works pretty well now
This commit is contained in:
parent
b48a51fc74
commit
9807b14a77
14 changed files with 494 additions and 122 deletions
|
|
@ -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+
|
||||
|
|
|
|||
5
pom.xml
5
pom.xml
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
return compare("", left, right);
|
||||
}
|
||||
|
||||
public static Result compare(String property, Object left, long right) {
|
||||
if (left == null) {
|
||||
return Result.unequal("null != " + right);
|
||||
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) {
|
||||
return compare("", left, right);
|
||||
}
|
||||
|
||||
public static Result compare(String property, long left, Object right) {
|
||||
if (right == null) {
|
||||
return Result.unequal(left + " != 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
return new Result(areEqual, List.of(message));
|
||||
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) {
|
||||
return new Result(areEqual, List.of(messageSupplier.get()));
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
src/test/java/nl/sander/apples/BeansTest.java
Normal file
25
src/test/java/nl/sander/apples/BeansTest.java
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
package nl.sander.apples;
|
||||
|
||||
public record Plum(String core, String peel, boolean juicy, int number, float price, Storage storage) {
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
58
src/test/java/nl/sander/apples/PlumBean.java
Normal file
58
src/test/java/nl/sander/apples/PlumBean.java
Normal 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;
|
||||
}
|
||||
}
|
||||
17
src/test/java/nl/sander/apples/PlumRecord.java
Normal file
17
src/test/java/nl/sander/apples/PlumRecord.java
Normal 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) {
|
||||
//}
|
||||
|
|
@ -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));
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
//
|
||||
|
|
|
|||
20
src/test/java/nl/sander/apples/Shop.java
Normal file
20
src/test/java/nl/sander/apples/Shop.java
Normal 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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue