works pretty well now

This commit is contained in:
Shautvast 2023-07-25 21:48:59 +02:00
parent b48a51fc74
commit 9807b14a77
14 changed files with 494 additions and 122 deletions

View file

@ -2,3 +2,5 @@
* universal compare tool
* compares any to any and shows the diff
* no reflection
* compiles to bytecode version jdk11
* but also handles records, if you run jdk16+

View file

@ -29,10 +29,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>20</release>
<source>20</source>
<target>20</target>
<compilerArgs>--enable-preview</compilerArgs>
<release>11</release>
</configuration>
</plugin>
</plugins>

View file

@ -5,12 +5,11 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.*;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.UUID;
import static org.objectweb.asm.Opcodes.*;
class AppleFactory extends ClassVisitor {
class AppleFactory extends ClassVisitor {
public static final String SUPER = javaName(BaseApple.class.getName());
@ -39,7 +38,7 @@ import static org.objectweb.asm.Opcodes.*;
this.classToMap = name;
classNode.name = "Apple" + UUID.randomUUID();
classNode.superName = SUPER;
classNode.version = V20;
classNode.version = V1_8;
classNode.access = ACC_PUBLIC;
MethodNode constructor = new MethodNode(ACC_PUBLIC, INIT, ZERO_ARGS_VOID, null, null);
constructor.instructions.add(new VarInsnNode(ALOAD, 0));
@ -50,30 +49,32 @@ import static org.objectweb.asm.Opcodes.*;
compareMethod = new MethodNode(ACC_PUBLIC,
"compare", "(Ljava/lang/Object;Ljava/lang/Object;)Lnl/sander/apples/Result;", null, null);
classNode.methods.add(compareMethod);
add(new VarInsnNode(ALOAD,0));
add(new VarInsnNode(ALOAD, 0));
}
public MethodVisitor visitMethod(int access, String methodname,
String desc, String signature, String[] exceptions) {
if (!hasArgs(desc) && access == Modifier.PUBLIC && isRecord ||
(methodname.startsWith("get") || (methodname.startsWith("is")) && desc.equals("()Z"))) {
int startIndex;
if (isRecord) {
startIndex = 0;
} else {
if (methodname.startsWith("is")) {
startIndex = 2;
} else {
startIndex = 3;
}
}
visitGetter(correctName(methodname, startIndex), getReturnType(desc));
visitGetter(methodname, asProperty(methodname, isRecord), getReturnType(desc));
}
return null;
}
private void visitGetter(String getterMethodName, String returnType) {
private String asProperty(String getter, boolean isRecord) {
if (isRecord){
return getter;
} else {
if (getter.startsWith("get")){
return getter.substring(3,4).toLowerCase()+getter.substring(4);
} else {
return getter.substring(2,3).toLowerCase()+getter.substring(3);
}
}
}
private void visitGetter(String getterMethodName, String propertyName, String returnType) {
add(new LdcInsnNode(propertyName));
add(new VarInsnNode(ALOAD, 1));
add(new TypeInsnNode(CHECKCAST, javaName(classToMap)));
add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
@ -82,13 +83,20 @@ import static org.objectweb.asm.Opcodes.*;
add(new TypeInsnNode(CHECKCAST, javaName(classToMap)));
add(new MethodInsnNode(INVOKEVIRTUAL, classToMap, getterMethodName, "()" + returnType));
add(new MethodInsnNode(INVOKESTATIC, "nl/sander/apples/Apples", "compare", "(Ljava/lang/Object;Ljava/lang/Object;)Lnl/sander/apples/Result;"));
add(new MethodInsnNode(INVOKESTATIC, "nl/sander/apples/Apples", "compare", "("
+ getSignature(returnType)
+ ")Lnl/sander/apples/Result;"));
add(new VarInsnNode(ASTORE, 3 + (localVarIndex++)));
}
private String correctName(String getterMethodName, int startIndex) {
String tmp = getterMethodName.substring(startIndex);
return tmp.substring(0, 1).toLowerCase() + tmp.substring(1);
private String getSignature(String returnType) {
String type;
if (returnType.startsWith("L")) {
type = "Ljava/lang/Object;";
} else {
type = returnType;
}
return "Ljava/lang/String;"+type + type;
}
@Override

View file

@ -3,21 +3,28 @@ package nl.sander.apples;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import java.io.FileOutputStream;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@SuppressWarnings({"unchecked", "rawtypes"})
public class Apples {
private final static Map<Character, String> CHAR_ESCAPES = Map.of('\t', "\\t", '\b', "\\b", '\n', "\\n", '\r', "\\r", '\f', "\\f", '\\', "\\\\");
private static final ByteClassLoader generatedClassesLoader = new ByteClassLoader();
public static Result compare(Object left, Object right) {
return compare("", left, right);
}
public static Result compare(String property, Object left, Object right) {
if (left == null) {
return Result.from(right == null, "null != " + asString(right));
return Result.from(property, right == null, "null != " + asString(right));
}
if (right == null) {
return Result.unequal(asString(left) + " != null");
return Result.unequal(property, asString(left) + " != null");
}
if (left == right) {
@ -25,24 +32,42 @@ public class Apples {
}
if (left.getClass() != right.getClass()) {
return Result.unequal(asString(left) + " != " + asString(right));
return Result.unequal(property, asString(left) + " != " + asString(right));
}
if (left instanceof String) {
return Result.from(left.equals(right), () -> asString(left) + " != " + asString(right));
return Result.from(property, left.equals(right), () -> asString(left) + " != " + asString(right));
}
if (left instanceof Number) {
return Result.from(property, left.equals(right), () -> left + " != " + right);
}
if (left instanceof Collection) {
return compareCollections(property, (Collection<?>) left, (Collection<?>) right);
}
if (left instanceof Map) {
return compareMaps(property, (Map<?, ?>) left, (Map<?, ?>) right);
}
if (left instanceof Comparable<?>) {
int comparison = ((Comparable) left).compareTo(right);
if (comparison == 0) {
return new Result(true, List.of());
} else {
return Result.from(property, false, left + " != " + right);
}
}
try {
ClassReader cr = new ClassReader(left.getClass().getName());
AppleFactory appleFactory = new AppleFactory();
cr.accept(appleFactory, ClassReader.SKIP_FRAMES);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES );
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
appleFactory.classNode.accept(classWriter);
byte[] byteArray = classWriter.toByteArray();
try (FileOutputStream f = new FileOutputStream("B.class")) {
f.write(byteArray);
}
generatedClassesLoader.addClass(appleFactory.classNode.name, byteArray);
BaseApple apple = (BaseApple) generatedClassesLoader.loadClass(appleFactory.classNode.name).getConstructor().newInstance();
return apple.compare(left, right);
@ -51,79 +76,210 @@ public class Apples {
}
}
public static Result compare(long left, long right) {
return Result.from(left == right, left + " != " + right);
private static Result compareCollections(String property, Collection<?> left, Collection<?> right) {
List<String> diffs =
zipAndEnumerate(left, right)
.map(t -> new Tuple4<Integer, Object, Object, Result>(t.e1, left, right, Apples.compare(property, t.e2, t.e3)))
.filter(t -> !t.e4.areEqual())
.map(t -> property + "[" + t.e1 + "]:" + t.e2 + " != " + t.e3)
.collect(Collectors.toList());
return new Result(!diffs.isEmpty(), diffs);
}
private static Result compareMaps(String property, Map<?, ?> left, Map<?, ?> right) {
List<String> diffs = new ArrayList<>();
for (Map.Entry<?, ?> leftEntry : left.entrySet()) {
Object leftValue = leftEntry.getValue();
Object rightValue = right.get(leftEntry.getKey());
Result result = Apples.compare(property + "[" + leftEntry.getKey() + "]", leftValue, rightValue);
if (!result.areEqual()) {
diffs.addAll(result.getDiffs());
}
}
return new Result(diffs.isEmpty(), diffs);
}
private static Stream<Tuple3<Integer, Object, Object>> zipAndEnumerate(Collection<?> left, Collection<?> right) {
Iterator<?> rightIt = right.iterator();
LongAdder adder = new LongAdder();
return left.stream()
.filter(__ -> rightIt.hasNext())
.map(o -> {
Tuple3<Integer, Object, Object> next = new Tuple3<>(adder.intValue(), o, rightIt.next());
adder.increment();
return next;
});
}
public static Result compare(long left, long right) {
return compare("", left, right);
}
public static Result compare(String property, long left, long right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(int left, int right) {
return compare("", left, right);
}
public static Result compare(String property, int left, int right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(short left, short right) {
return compare("", left, right);
}
public static Result compare(String property, short left, short right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(byte left, byte right) {
return compare("", left, right);
}
public static Result compare(String property, byte left, byte right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(float left, float right) {
return compare("", left, right);
}
public static Result compare(String property, float left, float right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(Object left, long right) {
if (left == null) {
return Result.unequal("null != " + right);
return compare("", left, right);
}
public static Result compare(String property, Object left, long right) {
if (left == null) {
return Result.unequal(property, "null != " + right);
}
if (left instanceof Long) {
return Result.from(property, (Long) left == right, asString(left) + " != " + right);
} else if (left instanceof Integer) {
return Result.from(property, (Integer) left == right, asString(left) + " != " + right);
} else if (left instanceof Short) {
return Result.from(property, (Short) left == right, asString(left) + " != " + right);
} else if (left instanceof Byte) {
return Result.from(property, (Byte) left == right, asString(left) + " != " + right);
} else {
return Result.unequal(property, asString(left) + " != " + right);
}
return switch (left) {
case Long l -> Result.from(l == right, asString(left) + " != " + right);
case Integer i -> Result.from(i == right, asString(left) + " != " + right);
case Short s -> Result.from(s == right, asString(left) + " != " + right);
case Byte b -> Result.from(b == right, asString(left) + " != " + right);
default -> Result.unequal(asString(left) + " != " + right);
};
}
public static Result compare(char left, char right) {
return Result.from(left == right, asString(left) + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, char left, char right) {
return Result.from(property, left == right, asString(left) + " != " + asString(right));
}
public static Result compare(Object left, char right) {
return Result.from(left instanceof Character && (Character) left == right, asString(left) + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, Object left, char right) {
return Result.from(property, left instanceof Character && (Character) left == right, asString(left) + " != " + asString(right));
}
public static Result compare(long left, Object right) {
if (right == null) {
return Result.unequal(left + " != null");
return compare("", left, right);
}
public static Result compare(String property, long left, Object right) {
if (right == null) {
return Result.unequal(property, left + " != null");
}
if (right instanceof Long) {
return Result.from(property, (Long) right == left, left + " != " + asString(right));
} else if (right instanceof Integer) {
return Result.from(property, (Integer) right == left, left + " != " + asString(right));
} else if (right instanceof Short) {
return Result.from(property, (Short) right == left, left + " != " + asString(right));
} else if (right instanceof Byte) {
return Result.from(property, (Byte) right == left, left + " != " + asString(right));
} else {
return Result.unequal(property, left + " != " + asString(right));
}
return switch (right) {
case Long l -> Result.from(l == left, left + " != " + asString(right));
case Integer i -> Result.from(i == left, left + " != " + asString(right));
case Short s -> Result.from(s == left, left + " != " + asString(right));
case Byte b -> Result.from(b == left, left + " != " + asString(right));
default -> Result.unequal(left + " != " + asString(right));
};
}
public static Result compare(char left, Object right) {
return Result.from(right instanceof Character && left == (Character) right, asString(left) + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, char left, Object right) {
return Result.from(property, right instanceof Character && left == (Character) right, asString(left) + " != " + asString(right));
}
public static Result compare(boolean left, boolean right) {
return Result.from(left == right, left + " != " + right);
return compare("", left, right);
}
public static Result compare(String property, boolean left, boolean right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(Object left, boolean right) {
return Result.from(left instanceof Boolean && (Boolean) left == right, asString(left) + " != " + right);
return compare("", left, right);
}
public static Result compare(String property, Object left, boolean right) {
return Result.from(property, left instanceof Boolean && (Boolean) left == right, asString(left) + " != " + right);
}
public static Result compare(boolean left, Object right) {
return Result.from(right instanceof Boolean && left == (Boolean) right, left + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, boolean left, Object right) {
return Result.from(property, right instanceof Boolean && left == (Boolean) right, left + " != " + asString(right));
}
public static Result compare(double left, double right) {
return Result.from(left == right, left + " != " + right);
return compare("", left, right);
}
public static Result compare(String property, double left, double right) {
return Result.from(property, left == right, left + " != " + right);
}
public static Result compare(double left, double right, int precision) {
return Result.from((int) (left * Math.pow(10, precision)) == (int) (right * Math.pow(10, precision)), left + " != " + right + ", using precision" + precision);
return compare("", left, right, precision);
}
public static Result compare(String property, double left, double right, int precision) {
return Result.from(property, (int) (left * Math.pow(10, precision)) == (int) (right * Math.pow(10, precision)), left + " != " + right + ", using precision" + precision);
}
public static Result compare(Object left, double right) {
return Result.from(left instanceof Double && (Double) left == right, asString(left) + " != " + right);
return compare("", left, right);
}
public static Result compare(String property, Object left, double right) {
return Result.from(property, left instanceof Double && (Double) left == right, asString(left) + " != " + right);
}
public static Result compare(double left, Object right) {
return Result.from(right instanceof Double && left == (Double) right, left + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, double left, Object right) {
return Result.from(property, right instanceof Double && left == (Double) right, left + " != " + asString(right));
}
public static Result compare(float left, Object right) {
return Result.from(right instanceof Float && left == (Float) right, left + " != " + asString(right));
return compare("", left, right);
}
public static Result compare(String property, float left, Object right) {
return Result.from(property, right instanceof Float && left == (Float) right, left + " != " + asString(right));
}
private static String asString(char value) {
@ -150,4 +306,30 @@ public class Apples {
return value.getClass().getName() + ": " + value;
}
}
static class Tuple3<E1, E2, E3> {
final E1 e1;
final E2 e2;
final E3 e3;
Tuple3(E1 e1, E2 e2, E3 e3) {
this.e1 = e1;
this.e2 = e2;
this.e3 = e3;
}
}
static class Tuple4<E1, E2, E3, E4> {
final E1 e1;
final E2 e2;
final E3 e3;
final E4 e4;
Tuple4(E1 e1, E2 e2, E3 e3, E4 e4) {
this.e1 = e1;
this.e2 = e2;
this.e3 = e3;
this.e4 = e4;
}
}
}

View file

@ -1,6 +1,6 @@
package nl.sander.apples;
abstract class BaseApple<T> {
public abstract class BaseApple<T> {
public abstract Result compare(T left, T right);
}

View file

@ -2,38 +2,89 @@ package nl.sander.apples;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public record Result(boolean areEqual, List<String> diffs) {
@SuppressWarnings("unused") // used by generated code
public class Result {
private final boolean areEqual;
private final List<String> diffs;
public Result(boolean areEqual, List<String> diffs) {
this.areEqual = areEqual;
this.diffs = diffs;
}
public static Result SAME = new Result(true, List.of());
public static Result from(boolean areEqual, String message) {
public static Result from(String property, boolean areEqual, String message) {
if (!areEqual) {
if (property.length() > 0) {
return new Result(areEqual, List.of("for " + property + ": " + message));
} else {
return new Result(areEqual, List.of(message));
}
} else {
return SAME;
}
}
public static Result from(boolean areEqual, Supplier<String> messageSupplier) {
public static Result from(String property, boolean areEqual, Supplier<String> messageSupplier) {
if (!areEqual) {
if (property.length() > 0) {
return new Result(areEqual, List.of("for " + property + ": " + messageSupplier.get()));
} else {
return new Result(areEqual, List.of(messageSupplier.get()));
}
} else {
return SAME;
}
}
public static Result unequal(String message) {
return from(false, message);
return from("", false, message);
}
public static Result unequal(String property, String message) {
return from(property, false, message);
}
public List<String> getDiffs() {
return diffs;
}
public boolean areEqual() {
return areEqual;
}
public static Result merge(Result... result) {
boolean areEqual = Arrays.stream(result).allMatch(r -> r.areEqual);
List<String> diffs = Arrays.stream(result)
.map(Result::diffs)
.map(Result::getDiffs)
.flatMap(List::stream)
.collect(Collectors.toList());
return new Result(areEqual, diffs);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Result result = (Result) o;
return areEqual == result.areEqual && Objects.equals(diffs, result.diffs);
}
@Override
public int hashCode() {
return Objects.hash(areEqual, diffs);
}
@Override
public String toString() {
return "Result{" +
"areEqual=" + areEqual +
", diffs=" + diffs +
'}';
}
}

View file

@ -0,0 +1,25 @@
package nl.sander.apples;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class BeansTest {
@Test
void testRecords() {
Result comparison = Apples.compare(new PlumBean("small", "red", true, 1, 1.0F, Storage.HIGH, (byte) 1, List.of(new Shop("tesco"))),
new PlumBean("large", "green", true, 1, 1.0F, Storage.LOW, (byte) 1, List.of(new Shop("asda"))));
assertFalse(comparison.areEqual());
assertFalse(comparison.getDiffs().isEmpty());
assertEquals(4, comparison.getDiffs().size());
assertEquals("for core: \"small\" != \"large\"", comparison.getDiffs().get(0));
assertEquals("for peel: \"red\" != \"green\"", comparison.getDiffs().get(1));
assertEquals("for storage: HIGH != LOW", comparison.getDiffs().get(2));
assertEquals("shops[0]:[Shop{name='tesco'}] != [Shop{name='asda'}]", comparison.getDiffs().get(3));
}
}

View file

@ -2,6 +2,9 @@ package nl.sander.apples;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
class ObjectsTest {
@ -22,7 +25,34 @@ class ObjectsTest {
}
@Test
void differentClass(){
void differentClass() {
assertEquals(Result.unequal("\"1\" != java.lang.Integer: 1"), Apples.compare("1", Integer.valueOf(1)));
}
@Test
void sameKeysAndValues() {
assertTrue(Apples.compare("map", Map.of("a", 1, "b", 2), Map.of("b", 2, "a", 1)).areEqual());
}
@Test
void differentKeysAndValues() {
Result result = Apples.compare("map", Map.of("a", 2, "b", 1), Map.of("b", 2, "a", 1));
assertFalse(result.areEqual());
assertTrue(result.getDiffs().contains("for map[b]: 1 != 2"));
assertTrue(result.getDiffs().contains("for map[a]: 2 != 1"));
}
@Test
void bigDecimals() {
Result result = Apples.compare(BigDecimal.valueOf(0), BigDecimal.valueOf(1));
assertFalse(result.areEqual());
assertEquals("0 != 1", result.getDiffs().get(0));
}
@Test
void enums() {
Result result = Apples.compare(Storage.HIGH, Storage.LOW);
assertFalse(result.areEqual());
assertEquals("HIGH != LOW", result.getDiffs().get(0));
}
}

View file

@ -1,4 +0,0 @@
package nl.sander.apples;
public record Plum(String core, String peel, boolean juicy, int number, float price, Storage storage) {
}

View file

@ -1,14 +0,0 @@
package nl.sander.apples;
public class PlumApple extends BaseApple<Plum> {
public Result compare(Plum left, Plum right) {
Result core = Apples.compare(left.core(), right.core());
Result peel = Apples.compare(left.peel(), right.peel());
Result juicy = Apples.compare(left.juicy(), right.juicy());
Result price = Apples.compare(left.price(), right.price());
Result number = Apples.compare(left.number(), right.number());
Result storage = Apples.compare(left.storage(), right.storage());
return Result.merge(core, peel, juicy, price, number, storage);
}
}

View file

@ -0,0 +1,58 @@
package nl.sander.apples;
import java.util.List;
public class PlumBean {
private final String core;
private final String peel;
private final boolean juicy;
private final int number;
private final float price;
private final Storage storage;
private final byte cores;
private final List<Shop> shops;
public PlumBean(String core, String peel, boolean juicy, int number, float price, Storage storage, byte cores, List<Shop> shops) {
this.core = core;
this.peel = peel;
this.juicy = juicy;
this.number = number;
this.price = price;
this.storage = storage;
this.cores = cores;
this.shops = shops;
}
public String getCore() {
return core;
}
public String getPeel() {
return peel;
}
public boolean isJuicy() {
return juicy;
}
public int getNumber() {
return number;
}
public float getPrice() {
return price;
}
public Storage getStorage() {
return storage;
}
public byte getCores() {
return cores;
}
public List<Shop> getShops() {
return shops;
}
}

View file

@ -0,0 +1,17 @@
/*
* uncomment when in jdk16+
*/
//package nl.sander.apples;
//
//import java.util.List;
//
//public record PlumRecord(String core,
// String peel,
// boolean juicy,
// int number,
// float price,
// Storage storage,
// byte cores,
// List<Shop> shops) {
//}

View file

@ -1,33 +1,33 @@
package nl.sander.apples;
/*
* uncomment when in jdk16+
*/
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
class RecordsTest {
@Test
void testExample() {
Result comparison = new PlumApple().compare(new Plum("small", "red",true, 1, 1.0F,Storage.HIGH),
new Plum("large", "green",true, 1, 1.0F,Storage.HIGH));
assertFalse(comparison.areEqual());
assertFalse(comparison.diffs().isEmpty());
assertEquals(2, comparison.diffs().size());
assertEquals("\"small\" != \"large\"", comparison.diffs().get(0));
assertEquals("\"red\" != \"green\"", comparison.diffs().get(1));
}
@Test
void testRecords() {
Result comparison = Apples.compare(new Plum("small", "red",true, 1, 1.0F,Storage.HIGH),
new Plum("large", "green",true, 1, 1.0F,Storage.HIGH));
assertFalse(comparison.areEqual());
assertFalse(comparison.diffs().isEmpty());
assertEquals(2, comparison.diffs().size());
assertEquals("\"small\" != \"large\"", comparison.diffs().get(0));
assertEquals("\"red\" != \"green\"", comparison.diffs().get(1));
}
}
//package nl.sander.apples;
//
//import org.junit.jupiter.api.Test;
//
//import java.util.List;
//
//import static org.junit.jupiter.api.Assertions.assertEquals;
//import static org.junit.jupiter.api.Assertions.assertFalse;
//
//class RecordsTest {
//
// @Test
// void testRecords() {
// Result comparison = Apples.compare(new PlumRecord("small", "red", true, 1, 1.0F, Storage.HIGH, (byte) 1, List.of(new Shop("tesco"))),
// new PlumRecord("large", "green", true, 1, 1.0F, Storage.LOW, (byte) 1, List.of(new Shop("asda"))));
//
// assertFalse(comparison.areEqual());
// assertFalse(comparison.getDiffs().isEmpty());
// assertEquals(4, comparison.getDiffs().size());
// assertEquals("for core: \"small\" != \"large\"", comparison.getDiffs().get(0));
// assertEquals("for peel: \"red\" != \"green\"", comparison.getDiffs().get(1));
// assertEquals("for storage: HIGH != LOW", comparison.getDiffs().get(2));
// assertEquals("shops[0]:[Shop{name='tesco'}] != [Shop{name='asda'}]", comparison.getDiffs().get(3));
// }
//
//
//}
//

View file

@ -0,0 +1,20 @@
package nl.sander.apples;
public class Shop {
private final String name;
public Shop(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Shop{" +
"name='" + name + '\'' +
'}';
}
}