diff --git a/src/main/java/net/amygdalum/testrecorder/TestGenerator.java b/src/main/java/net/amygdalum/testrecorder/TestGenerator.java index 820a5380..58c68262 100644 --- a/src/main/java/net/amygdalum/testrecorder/TestGenerator.java +++ b/src/main/java/net/amygdalum/testrecorder/TestGenerator.java @@ -64,10 +64,12 @@ import net.amygdalum.testrecorder.deserializers.matcher.MatcherGenerators; import net.amygdalum.testrecorder.util.ExpectedOutput; import net.amygdalum.testrecorder.util.IORecorder; +import net.amygdalum.testrecorder.util.Pair; import net.amygdalum.testrecorder.util.RecordInput; import net.amygdalum.testrecorder.util.RecordOutput; import net.amygdalum.testrecorder.util.SetupInput; import net.amygdalum.testrecorder.util.Throwables; +import net.amygdalum.testrecorder.util.Triple; import net.amygdalum.testrecorder.values.SerializedField; import net.amygdalum.testrecorder.values.SerializedInput; import net.amygdalum.testrecorder.values.SerializedOutput; @@ -436,11 +438,12 @@ public MethodGenerator generateArrange() { this.base = setupThis.isStored() ? setupThis.getValue() : assign(snapshot.getSetupThis().getType(), setupThis.getValue()); - this.args = IntStream.range(0, setupArgs.size()) - .mapToObj(i -> setupArgs.get(i).isStored() - ? setupArgs.get(i).getValue() - : assign(snapshotSetupArgs[i].value.getResultType(), setupArgs.get(i).getValue())) - .collect(toList()); + Pair[] arguments = Pair.zip(setupArgs.toArray(new Computation[0]), snapshotSetupArgs); + this.args = Stream.of(arguments) + .map(arg -> arg.getElement1().isStored() + ? arg.getElement1().getValue() + : assign(arg.getElement2().value.getResultType(), arg.getElement1().getValue())) + .collect(toList()); return this; } @@ -515,12 +518,15 @@ public MethodGenerator generateAssert() { SerializedValue[] snapshotSetupArgs = snapshot.getSetupArgs(); AnnotatedValue[] snapshotExpectArgs = snapshot.getAnnotatedExpectArgs(); - List expectArgs = IntStream.range(0, snapshotExpectArgs.length) - .filter(i -> !snapshotExpectArgs[i].value.equals(snapshotSetupArgs[i])) - .mapToObj(i -> createAssertion(snapshotExpectArgs[i].value.accept(matcher.create(locals, types), newContext(snapshotExpectArgs[i].annotations)), args.get(i))) + Triple[] arguments = Triple.zip(snapshotExpectArgs, snapshotSetupArgs, args.toArray(new String[0])); + List expectArgs = Stream.of(arguments) + .filter(arg -> !arg.getElement1().value.equals(arg.getElement2())) + .map(arg -> new Pair(arg.getElement1().value.accept(matcher.create(locals, types), newContext(arg.getElement1().annotations)), arg.getElement3())) + .filter(arg -> arg.getElement1() != null) + .map(arg -> createAssertion(arg.getElement1(), arg.getElement2())) .flatMap(statements -> statements.stream()) .collect(toList()); - + statements.addAll(expectArgs); SerializedField[] serializedGlobals = snapshot.getExpectGlobals(); @@ -542,7 +548,6 @@ public MethodGenerator generateAssert() { } private List createAssertion(Computation matcher, String exp) { - List statements = new ArrayList<>(); statements.addAll(matcher.getStatements()); diff --git a/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/DefaultObjectAdaptor.java b/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/DefaultObjectAdaptor.java index 23cd3880..cd9a2ab7 100644 --- a/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/DefaultObjectAdaptor.java +++ b/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/DefaultObjectAdaptor.java @@ -9,6 +9,7 @@ import java.lang.reflect.Type; import java.util.List; +import java.util.Objects; import org.hamcrest.Matcher; @@ -33,6 +34,7 @@ public Computation tryDeserialize(SerializedObject value, MatcherGenerators gene List fields = value.getFields().stream() .sorted() .map(field -> field.accept(generator)) + .filter(Objects::nonNull) .collect(toList()); List fieldComputations = fields.stream() diff --git a/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/MatcherGenerators.java b/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/MatcherGenerators.java index f21ddf4c..453e9ef8 100644 --- a/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/MatcherGenerators.java +++ b/src/main/java/net/amygdalum/testrecorder/deserializers/matcher/MatcherGenerators.java @@ -27,6 +27,7 @@ import net.amygdalum.testrecorder.deserializers.DeserializerFactory; import net.amygdalum.testrecorder.deserializers.LocalVariableNameGenerator; import net.amygdalum.testrecorder.deserializers.TypeManager; +import net.amygdalum.testrecorder.hints.SkipChecks; import net.amygdalum.testrecorder.util.GenericMatcher; import net.amygdalum.testrecorder.values.SerializedField; import net.amygdalum.testrecorder.values.SerializedLiteral; @@ -34,139 +35,148 @@ public class MatcherGenerators implements Deserializer { - public static final Adaptors DEFAULT = new Adaptors() - .load(MatcherGenerator.class); + public static final Adaptors DEFAULT = new Adaptors() + .load(MatcherGenerator.class); - private LocalVariableNameGenerator locals; - private TypeManager types; - private Adaptors adaptors; + private LocalVariableNameGenerator locals; + private TypeManager types; + private Adaptors adaptors; - private Set computed; + private Set computed; + public MatcherGenerators(Class clazz) { + this(new LocalVariableNameGenerator(), new TypeManager(clazz.getPackage().getName()), DEFAULT); + } - public MatcherGenerators(Class clazz) { - this(new LocalVariableNameGenerator(), new TypeManager(clazz.getPackage().getName()), DEFAULT); - } + public MatcherGenerators(LocalVariableNameGenerator locals, TypeManager types) { + this(locals, types, DEFAULT); + } - public MatcherGenerators(LocalVariableNameGenerator locals, TypeManager types) { - this(locals, types, DEFAULT); - } + public MatcherGenerators(LocalVariableNameGenerator locals, TypeManager types, Adaptors adaptors) { + this.locals = locals; + this.types = types; + this.adaptors = adaptors; + this.computed = new HashSet<>(); + } - public MatcherGenerators(LocalVariableNameGenerator locals, TypeManager types, Adaptors adaptors) { - this.locals = locals; - this.types = types; - this.adaptors = adaptors; - this.computed = new HashSet<>(); - } - - public LocalVariableNameGenerator getLocals() { + public LocalVariableNameGenerator getLocals() { return locals; } - public TypeManager getTypes() { - return types; - } + public TypeManager getTypes() { + return types; + } - public boolean isSimpleValue(SerializedValue element) { - return element instanceof SerializedNull - || element instanceof SerializedLiteral; + public boolean isSimpleValue(SerializedValue element) { + return element instanceof SerializedNull + || element instanceof SerializedLiteral; } public Computation simpleMatcher(SerializedValue element) { return simpleMatcher(element, DeserializerContext.NULL); - } - - public Computation simpleMatcher(SerializedValue element, DeserializerContext context) { - if (element instanceof SerializedNull) { - types.staticImport(Matchers.class, "nullValue"); - return new Computation(nullMatcher(""), element.getResultType()); - } else if (element instanceof SerializedLiteral) { - return new Computation(asLiteral(((SerializedLiteral) element).getValue()), element.getResultType()); - } else { - return element.accept(this, context); - } + } + + public Computation simpleMatcher(SerializedValue element, DeserializerContext context) { + if (element instanceof SerializedNull) { + types.staticImport(Matchers.class, "nullValue"); + return new Computation(nullMatcher(""), element.getResultType()); + } else if (element instanceof SerializedLiteral) { + return new Computation(asLiteral(((SerializedLiteral) element).getValue()), element.getResultType()); + } else { + return element.accept(this, context); + } } public Computation simpleValue(SerializedValue element) { return simpleValue(element, DeserializerContext.NULL); - } - - public Computation simpleValue(SerializedValue element, DeserializerContext context) { - if (element instanceof SerializedNull) { - return new Computation("null", element.getResultType()); - } else if (element instanceof SerializedLiteral) { - return new Computation(asLiteral(((SerializedLiteral) element).getValue()), element.getResultType()); - } else { - return element.accept(this, context); - } - } - - @Override - public Computation visitField(SerializedField field, DeserializerContext context) { - SerializedValue fieldValue = field.getValue(); - DeserializerContext fieldContext = newContext(field.getAnnotations()); - if (types.isHidden(field.getType())) { - types.registerImport(Matcher.class); - Computation value = fieldValue.accept(this, fieldContext); - - String genericType = types.getRelaxedName(value.getType()); - - String assignField = assignLocalVariableStatement(genericType, field.getName(), value.getValue()); - return new Computation(assignField, null, value.getStatements()); - } else if (isSimpleValue(fieldValue)) { - types.registerImport(baseType(field.getType())); - Computation value = simpleValue(fieldValue, fieldContext); - - String assignField = assignLocalVariableStatement(types.getRawName(field.getType()), field.getName(), value.getValue()); - return new Computation(assignField, null, value.getStatements()); - } else { - types.registerImport(Matcher.class); - Computation value = fieldValue.accept(this, fieldContext); - - String genericType = types.getRelaxedName(value.getType()); - - String assignField = assignLocalVariableStatement(genericType, field.getName(), value.getValue()); - return new Computation(assignField, null, value.getStatements()); - } - } - - @Override - public Computation visitReferenceType(SerializedReferenceType value, DeserializerContext context) { - if (!computed.add(value)) { - types.staticImport(GenericMatcher.class, "recursive"); - Type resultType = value.getResultType().equals(value.getType()) ? parameterized(Matcher.class, null, value.getResultType()) : parameterized(Matcher.class, null, wildcard()); - if (!types.isHidden(value.getType())) { - return new Computation(recursiveMatcher(types.getRawTypeName(value.getType())), resultType); - } else if (!types.isHidden(value.getResultType())) { - return new Computation(recursiveMatcher(types.getRawTypeName(value.getResultType())), resultType); - } else { - return new Computation(recursiveMatcher(types.getRawTypeName(Object.class)), parameterized(Matcher.class, null, wildcard())); - } - } - return adaptors.tryDeserialize(value, types, this, context); - } - - @Override - public Computation visitImmutableType(SerializedImmutableType value, DeserializerContext context) { - return adaptors.tryDeserialize(value, types, this, context); - } - - @Override - public Computation visitValueType(SerializedValueType value, DeserializerContext context) { - return adaptors.tryDeserialize(value, types, this, context); - } - - public static class Factory implements DeserializerFactory { - - @Override - public Deserializer create(LocalVariableNameGenerator locals, TypeManager types) { - return new MatcherGenerators(locals, types); - } - - @Override - public Type resultType(Type type) { - return parameterized(Matcher.class, null, type); - } - } + } + + public Computation simpleValue(SerializedValue element, DeserializerContext context) { + if (element instanceof SerializedNull) { + return new Computation("null", element.getResultType()); + } else if (element instanceof SerializedLiteral) { + return new Computation(asLiteral(((SerializedLiteral) element).getValue()), element.getResultType()); + } else { + return element.accept(this, context); + } + } + + @Override + public Computation visitField(SerializedField field, DeserializerContext context) { + SerializedValue fieldValue = field.getValue(); + DeserializerContext fieldContext = newContext(field.getAnnotations()); + if (fieldContext.getHint(SkipChecks.class).isPresent()) { + return null; + } else if (types.isHidden(field.getType())) { + types.registerImport(Matcher.class); + Computation value = fieldValue.accept(this, fieldContext); + + String genericType = types.getRelaxedName(value.getType()); + + String assignField = assignLocalVariableStatement(genericType, field.getName(), value.getValue()); + return new Computation(assignField, null, value.getStatements()); + } else if (isSimpleValue(fieldValue)) { + types.registerImport(baseType(field.getType())); + Computation value = simpleValue(fieldValue, fieldContext); + + String assignField = assignLocalVariableStatement(types.getRawName(field.getType()), field.getName(), value.getValue()); + return new Computation(assignField, null, value.getStatements()); + } else { + types.registerImport(Matcher.class); + Computation value = fieldValue.accept(this, fieldContext); + + String genericType = types.getRelaxedName(value.getType()); + + String assignField = assignLocalVariableStatement(genericType, field.getName(), value.getValue()); + return new Computation(assignField, null, value.getStatements()); + } + } + + @Override + public Computation visitReferenceType(SerializedReferenceType value, DeserializerContext context) { + if (context.getHint(SkipChecks.class).isPresent()) { + return null; + } else if (!computed.add(value)) { + types.staticImport(GenericMatcher.class, "recursive"); + Type resultType = value.getResultType().equals(value.getType()) ? parameterized(Matcher.class, null, value.getResultType()) : parameterized(Matcher.class, null, wildcard()); + if (!types.isHidden(value.getType())) { + return new Computation(recursiveMatcher(types.getRawTypeName(value.getType())), resultType); + } else if (!types.isHidden(value.getResultType())) { + return new Computation(recursiveMatcher(types.getRawTypeName(value.getResultType())), resultType); + } else { + return new Computation(recursiveMatcher(types.getRawTypeName(Object.class)), parameterized(Matcher.class, null, wildcard())); + } + } + return adaptors.tryDeserialize(value, types, this, context); + } + + @Override + public Computation visitImmutableType(SerializedImmutableType value, DeserializerContext context) { + if (context.getHint(SkipChecks.class).isPresent()) { + return null; + } + return adaptors.tryDeserialize(value, types, this, context); + } + + @Override + public Computation visitValueType(SerializedValueType value, DeserializerContext context) { + if (context.getHint(SkipChecks.class).isPresent()) { + return null; + } + return adaptors.tryDeserialize(value, types, this, context); + } + + public static class Factory implements DeserializerFactory { + + @Override + public Deserializer create(LocalVariableNameGenerator locals, TypeManager types) { + return new MatcherGenerators(locals, types); + } + + @Override + public Type resultType(Type type) { + return parameterized(Matcher.class, null, type); + } + } } diff --git a/src/main/java/net/amygdalum/testrecorder/util/Pair.java b/src/main/java/net/amygdalum/testrecorder/util/Pair.java index bd2821a5..b54b416a 100644 --- a/src/main/java/net/amygdalum/testrecorder/util/Pair.java +++ b/src/main/java/net/amygdalum/testrecorder/util/Pair.java @@ -12,6 +12,18 @@ public Pair(T1 element1, T2 element2) { this.element2 = element2; } + @SuppressWarnings("unchecked") + public static Pair[] zip(T1[] e1, T2[] e2) { + if (e1.length != e2.length) { + throw new IllegalArgumentException(); + } + Pair[] pairs = new Pair[e1.length]; + for (int i = 0; i < pairs.length; i++) { + pairs[i] = new Pair<>(e1[i], e2[i]); + } + return pairs; + } + public T1 getElement1() { return element1; } diff --git a/src/main/java/net/amygdalum/testrecorder/util/Triple.java b/src/main/java/net/amygdalum/testrecorder/util/Triple.java new file mode 100644 index 00000000..d0174e53 --- /dev/null +++ b/src/main/java/net/amygdalum/testrecorder/util/Triple.java @@ -0,0 +1,64 @@ +package net.amygdalum.testrecorder.util; + +import java.util.Objects; + +public class Triple { + + private T1 element1; + private T2 element2; + private T3 element3; + + public Triple(T1 element1, T2 element2, T3 element3) { + this.element1 = element1; + this.element2 = element2; + this.element3 = element3; + } + + @SuppressWarnings("unchecked") + public static Triple[] zip(T1[] e1, T2[] e2, T3[] e3) { + if (e1.length != e2.length || e2.length != e3.length) { + throw new IllegalArgumentException(); + } + Triple[] triples = new Triple[e1.length]; + for (int i = 0; i < triples.length; i++) { + triples[i] = new Triple<>(e1[i], e2[i], e3[i]); + } + return triples; + } + + public T1 getElement1() { + return element1; + } + + public T2 getElement2() { + return element2; + } + + public T3 getElement3() { + return element3; + } + + @Override + public int hashCode() { + return Objects.hash(element1, element2, element3) + 17; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Triple that = (Triple) obj; + return Objects.equals(this.element1, that.element1) + && Objects.equals(this.element2, that.element2) + && Objects.equals(this.element3, that.element3); + } + +} diff --git a/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromChecking.java b/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromChecking.java index 51629489..ad3a56dc 100644 --- a/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromChecking.java +++ b/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromChecking.java @@ -6,37 +6,37 @@ public class ExcludedFromChecking { @SkipChecks - private long excluded; + private long longVar; - private int notExcluded; + private int intVar; public ExcludedFromChecking(int init) { - this.excluded = init; - this.notExcluded = init * init; + this.longVar = init; + this.intVar = init * init; } @Recorded @SkipChecks - public int getNotExcluded() { - return notExcluded; + public int getIntVar() { + return intVar; } @Recorded public void reinit(@SkipChecks int... factors) { for (int i = 0; i < factors.length; i++) { - excluded *= factors[i]; + longVar *= factors[i]; } - notExcluded = (int) excluded; + intVar = (int) longVar; } @Recorded public long next() { - int temp = (int) excluded; - excluded *= 2; - notExcluded = temp; - return excluded; + int temp = (int) longVar; + longVar *= 2; + intVar = temp; + return longVar; } } diff --git a/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromCheckingTest.java b/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromCheckingTest.java index 63bab271..e960fd46 100644 --- a/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromCheckingTest.java +++ b/src/test/java/net/amygdalum/testrecorder/scenarios/ExcludedFromCheckingTest.java @@ -38,15 +38,15 @@ public void testFieldsExcludedInTestCompilable() throws Exception { assertThat(testGenerator.renderTest(ExcludedFromChecking.class), testsRun(LargeIntArrays.class)); assertThat(testGenerator.renderTest(ExcludedFromChecking.class), allOf( containsPattern("assertThat(long*, equalTo(84l))"), - containsString("int notExcluded = 42;"), - not(containsString("long excluded = 84l;")))); + containsString("int intVar = 42;"), + not(containsString("long longVar = 84l;")))); } @Test public void testResultsExcludedInTestCompilable() throws Exception { ExcludedFromChecking arrays = new ExcludedFromChecking(42); - arrays.getNotExcluded(); + arrays.getIntVar(); TestGenerator testGenerator = TestGenerator.fromRecorded(); assertThat(testGenerator.testsFor(ExcludedFromChecking.class), hasSize(1));