first commit
This commit is contained in:
366
java/expression/generic/GenericTester.java
Normal file
366
java/expression/generic/GenericTester.java
Normal file
@@ -0,0 +1,366 @@
|
||||
package expression.generic;
|
||||
|
||||
import base.*;
|
||||
import expression.ToMiniString;
|
||||
import expression.common.*;
|
||||
import expression.parser.ParserTestSet;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
|
||||
*/
|
||||
public class GenericTester extends Tester {
|
||||
|
||||
private static final int SIZE = 10;
|
||||
private static final int MAX = Integer.MAX_VALUE - 1;
|
||||
private static final int MIN = Integer.MIN_VALUE;
|
||||
private static final List<Pair<String, F<?>>> VARIABLES = List.of(
|
||||
Pair.of("x", (x, y, z) -> x),
|
||||
Pair.of("y", (x, y, z) -> y),
|
||||
Pair.of("z", (x, y, z) -> z)
|
||||
);
|
||||
private static final ExpressionKind.Variables<F<?>> VARS = (c, r) ->
|
||||
VARIABLES;
|
||||
|
||||
protected final List<Named<IF<?>>> tests = new ArrayList<>();
|
||||
private final Tabulator tabulator = new GenericTabulator();
|
||||
private final Set<String> operations = new HashSet<>();
|
||||
private final List<NodeRenderer.Paren> parens = new ArrayList<>(
|
||||
List.of(NodeRenderer.paren("(", ")"))
|
||||
);
|
||||
|
||||
private final TestGeneratorBuilder<Integer> generator;
|
||||
private final List<Mode.Builder<?>> modes = new ArrayList<>();
|
||||
|
||||
public GenericTester(final TestCounter counter) {
|
||||
super(counter);
|
||||
generator = new TestGeneratorBuilder<>(
|
||||
random(),
|
||||
random()::nextInt,
|
||||
ParserTestSet.CONSTS,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
protected void test(
|
||||
final String expression,
|
||||
final String name,
|
||||
final IF<?> f
|
||||
) {
|
||||
tests.add(Named.of(name + ": " + expression, f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void test() {
|
||||
final List<Mode> modes = this.modes.stream()
|
||||
.map(mode -> mode.build(this))
|
||||
.toList();
|
||||
|
||||
for (final Named<IF<?>> test : tests) {
|
||||
final String[] parts = test.name().split(": ");
|
||||
testFull(parts[0], parts[1], test.value());
|
||||
}
|
||||
|
||||
final TestGenerator<Integer, F<?>> generator = this.generator.build(
|
||||
VARS
|
||||
);
|
||||
counter.scope("basic", () -> generator.testBasic(test(true, modes)));
|
||||
counter.scope("random", () ->
|
||||
generator.testRandom(
|
||||
counter,
|
||||
20 + (TestCounter.DENOMINATOR - 1) * 2,
|
||||
test(false, modes)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void testFull(
|
||||
final String mode,
|
||||
final String expression,
|
||||
final IF<?> f
|
||||
) {
|
||||
testShort(mode, expression, f);
|
||||
test(mode, expression, f, MAX, -1, MAX, 0);
|
||||
test(mode, expression, f, MIN, 0, MIN, 1);
|
||||
}
|
||||
|
||||
private void testShort(
|
||||
final String mode,
|
||||
final String expression,
|
||||
final IF<?> f
|
||||
) {
|
||||
test(mode, expression, f, 0, -1, 0, 1);
|
||||
}
|
||||
|
||||
private void test(
|
||||
final String mode,
|
||||
final String expression,
|
||||
final IF<?> f,
|
||||
final int min,
|
||||
final int dMin,
|
||||
final int max,
|
||||
final int dMax
|
||||
) {
|
||||
test(
|
||||
mode,
|
||||
expression,
|
||||
f,
|
||||
min + random().nextInt(SIZE) * dMin,
|
||||
max + random().nextInt(SIZE) * dMax,
|
||||
min + random().nextInt(SIZE) * dMin,
|
||||
max + random().nextInt(SIZE) * dMax,
|
||||
min + random().nextInt(SIZE) * dMin,
|
||||
max + random().nextInt(SIZE) * dMax
|
||||
);
|
||||
}
|
||||
|
||||
private Consumer<TestGenerator.Test<Integer, F<?>>> test(
|
||||
final boolean full,
|
||||
final List<Mode> modes
|
||||
) {
|
||||
final NodeRenderer.Settings settings = NodeRenderer.FULL.withParens(
|
||||
parens
|
||||
);
|
||||
return test ->
|
||||
modes.forEach(mode ->
|
||||
mode.test(test.expr, test.render(settings), full)
|
||||
);
|
||||
}
|
||||
|
||||
private <T> void test(
|
||||
final String mode,
|
||||
final String expression,
|
||||
final IF<T> f,
|
||||
final int x1,
|
||||
final int x2,
|
||||
final int y1,
|
||||
final int y2,
|
||||
final int z1,
|
||||
final int z2
|
||||
) {
|
||||
final String context = String.format(
|
||||
"mode=%s, x=[%d, %d] y=[%d, %d] z=[%d, %d], expression=%s%n",
|
||||
mode,
|
||||
x1,
|
||||
x2,
|
||||
y1,
|
||||
y2,
|
||||
z1,
|
||||
z2,
|
||||
expression
|
||||
);
|
||||
final Object[][][] result = counter.testV(() ->
|
||||
TestCounter.get(() ->
|
||||
tabulator.tabulate(mode, expression, x1, x2, y1, y2, z1, z2)
|
||||
).either(
|
||||
e -> counter.fail(e, "%s %s", "tabulate", context),
|
||||
Function.identity()
|
||||
)
|
||||
);
|
||||
IntStream.rangeClosed(x1, x2).forEach(x ->
|
||||
IntStream.rangeClosed(y1, y2).forEach(y ->
|
||||
IntStream.rangeClosed(z1, z2).forEach(z ->
|
||||
counter.test(() -> {
|
||||
final Object expected = TestCounter.get(() ->
|
||||
f.apply(x, y, z)
|
||||
).either(e -> null, Function.identity());
|
||||
final Object actual = result[x - x1][y - y1][z - z1];
|
||||
counter.checkTrue(
|
||||
Objects.equals(actual, expected),
|
||||
"table[%d][%d][%d](x=%d, y=%d, z=%d]) = %s (expected %s)%n%s",
|
||||
x - x1,
|
||||
y - y1,
|
||||
z - z1,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
actual,
|
||||
expected,
|
||||
context
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void binary(final String name, final int priority) {
|
||||
operations.add(name + ":2");
|
||||
generator.binary(name, priority);
|
||||
}
|
||||
|
||||
public void unary(final String name, final int priority) {
|
||||
operations.add(name + ":1");
|
||||
generator.unary(name, priority);
|
||||
}
|
||||
|
||||
public void parens(final String... parens) {
|
||||
assert parens.length % 2 == 0 : "Parens should come in pairs";
|
||||
for (int i = 0; i < parens.length; i += 2) {
|
||||
this.parens.add(NodeRenderer.paren(parens[i], parens[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
/* package-private */ interface Mode {
|
||||
static <T> Builder<T> builder(
|
||||
final String mode,
|
||||
final IntFunction<T> constant,
|
||||
final IntUnaryOperator fixer
|
||||
) {
|
||||
return new Builder<>(mode, constant, fixer);
|
||||
}
|
||||
|
||||
void test(
|
||||
final Expr<Integer, ? extends F<?>> expr,
|
||||
final String expression,
|
||||
final boolean full
|
||||
);
|
||||
|
||||
/* package-private */ final class Builder<
|
||||
T
|
||||
> implements Consumer<GenericTester> {
|
||||
|
||||
private final String mode;
|
||||
private final IntFunction<T> constant;
|
||||
private final IntUnaryOperator fixer;
|
||||
private final List<Named<UnaryOperator<GenericTester.F<T>>>> unary =
|
||||
new ArrayList<>();
|
||||
private final List<
|
||||
Named<BinaryOperator<GenericTester.F<T>>>
|
||||
> binary = new ArrayList<>();
|
||||
|
||||
private Builder(
|
||||
final String mode,
|
||||
final IntFunction<T> constant,
|
||||
final IntUnaryOperator fixer
|
||||
) {
|
||||
this.mode = mode;
|
||||
this.constant = constant;
|
||||
this.fixer = fixer;
|
||||
}
|
||||
|
||||
public Builder<T> unary(
|
||||
final String name,
|
||||
final UnaryOperator<T> op
|
||||
) {
|
||||
unary.add(
|
||||
Named.of(
|
||||
name,
|
||||
arg -> (x, y, z) -> op.apply(arg.apply(x, y, z))
|
||||
)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> binary(
|
||||
final String name,
|
||||
final BinaryOperator<T> op
|
||||
) {
|
||||
binary.add(
|
||||
Named.of(
|
||||
name,
|
||||
(a, b) ->
|
||||
(x, y, z) ->
|
||||
op.apply(a.apply(x, y, z), b.apply(x, y, z))
|
||||
)
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(final GenericTester tester) {
|
||||
tester.modes.add(this);
|
||||
}
|
||||
|
||||
private Mode build(final GenericTester tester) {
|
||||
final Set<String> ops = Stream.concat(
|
||||
unary.stream().map(op -> op.name() + ":1"),
|
||||
binary.stream().map(op -> op.name() + ":2")
|
||||
).collect(Collectors.toUnmodifiableSet());
|
||||
final List<String> diff = tester.operations
|
||||
.stream()
|
||||
.filter(Predicate.not(ops::contains))
|
||||
.toList();
|
||||
Asserts.assertTrue(
|
||||
String.format("Missing operations for %s: %s", mode, diff),
|
||||
diff.isEmpty()
|
||||
);
|
||||
|
||||
final Renderer.Builder<
|
||||
Integer,
|
||||
Unit,
|
||||
GenericTester.F<T>
|
||||
> builder = Renderer.builder(
|
||||
value -> (x, y, z) -> constant.apply(value)
|
||||
);
|
||||
unary.forEach(op ->
|
||||
builder.unary(op.name(), (unit, arg) ->
|
||||
op.value().apply(arg)
|
||||
)
|
||||
);
|
||||
binary.forEach(op ->
|
||||
builder.binary(op.name(), (unit, a, b) ->
|
||||
op.value().apply(a, b)
|
||||
)
|
||||
);
|
||||
final Renderer<Integer, Unit, F<T>> renderer = builder.build();
|
||||
final TestGenerator<Integer, F<?>> genRenderer =
|
||||
tester.generator.build(VARS);
|
||||
|
||||
return (expr, expression, full) -> {
|
||||
final String fixed =
|
||||
fixer == IntUnaryOperator.identity()
|
||||
? expression
|
||||
: genRenderer.render(cata(expr), NodeRenderer.FULL);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Expr<Integer, F<T>> converted = (Expr<
|
||||
Integer,
|
||||
F<T>
|
||||
>) expr;
|
||||
final F<T> expected = renderer.render(
|
||||
converted,
|
||||
Unit.INSTANCE
|
||||
);
|
||||
final IF<T> f = (x, y, z) ->
|
||||
expected.apply(
|
||||
constant.apply(x),
|
||||
constant.apply(y),
|
||||
constant.apply(z)
|
||||
);
|
||||
if (full) {
|
||||
tester.testFull(mode, fixed, f);
|
||||
} else {
|
||||
tester.testShort(mode, fixed, f);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Expr<Integer, ? extends F<?>> cata(
|
||||
final Expr<Integer, ? extends F<?>> expr
|
||||
) {
|
||||
return expr.node(node ->
|
||||
node.cata(
|
||||
c -> Node.constant(fixer.applyAsInt(c)),
|
||||
Node::op,
|
||||
Node::op,
|
||||
Node::op
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface IF<T> {
|
||||
T apply(int x, int y, int z);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface F<T> extends ToMiniString {
|
||||
T apply(T x, T y, T z);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user