From ed213112335aecdfd0fe50edb63a2d4153c9b5c7 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Tue, 8 Sep 2020 11:58:10 +0200 Subject: [PATCH 01/14] 8229186: Improve error messages for TestStringIntrinsics failures --- .../string/TestStringIntrinsics.java | 54 ++- .../jdk/test/lib/format/ArrayDiffTest.java | 296 ++++++++++++++++ test/lib/jdk/test/lib/format/ArrayCodec.java | 324 ++++++++++++++++++ test/lib/jdk/test/lib/format/ArrayDiff.java | 239 +++++++++++++ test/lib/jdk/test/lib/format/Diff.java | 49 +++ test/lib/jdk/test/lib/format/Format.java | 127 +++++++ 6 files changed, 1077 insertions(+), 12 deletions(-) create mode 100644 test/lib-test/jdk/test/lib/format/ArrayDiffTest.java create mode 100644 test/lib/jdk/test/lib/format/ArrayCodec.java create mode 100644 test/lib/jdk/test/lib/format/ArrayDiff.java create mode 100644 test/lib/jdk/test/lib/format/Diff.java create mode 100644 test/lib/jdk/test/lib/format/Format.java diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java index 36cf6827b9b16..93968de39d737 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,16 @@ * @test * @bug 8054307 * @summary Tests correctness of string related intrinsics and C2 optimizations. + * @library /test/lib * * @run main/timeout=240 compiler.intrinsics.string.TestStringIntrinsics */ package compiler.intrinsics.string; +import jdk.test.lib.format.Format; +import jdk.test.lib.format.ArrayCodec; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -77,8 +81,8 @@ public void run() throws Exception { for (Method m : TestStringIntrinsics.class.getMethods()) { if (m.isAnnotationPresent(Test.class)) { System.out.print("Checking " + m.getName() + "... "); - Operation op = m.getAnnotation(Test.class).op(); Test antn = m.getAnnotation(Test.class); + Operation op = antn.op(); if (isStringConcatTest(op)) { checkStringConcat(op, m, antn); } else { @@ -121,13 +125,13 @@ private void checkIntrinsics(Operation op, Method m, String latin1, String utf16 switch (op) { case ARR_EQUALS_B: - invokeAndCheck(m, (incL == 0), latin1.getBytes("ISO-8859-1"), latin1Copy.getBytes("ISO-8859-1")); - invokeAndCheck(m, true, new byte[] {1, 2, 3}, new byte[] {1, 2, 3}); - invokeAndCheck(m, true, new byte[] {1}, new byte[] {1}); - invokeAndCheck(m, true, new byte[] {}, new byte[] {}); + invokeAndCompareArrays(m, (incL == 0), latin1.getBytes("ISO-8859-1"), latin1Copy.getBytes("ISO-8859-1")); + invokeAndCompareArrays(m, true, new byte[] {1, 2, 3}, new byte[] {1, 2, 3}); + invokeAndCompareArrays(m, true, new byte[] {1}, new byte[] {1}); + invokeAndCompareArrays(m, true, new byte[] {}, new byte[] {}); break; case ARR_EQUALS_C: - invokeAndCheck(m, (incU == 0), utf16.toCharArray(), arrU); + invokeAndCompareArrays(m, (incU == 0), utf16.toCharArray(), arrU); break; case EQUALS: invokeAndCheck(m, (incL == 0), latin1, latin1Copy); @@ -240,6 +244,29 @@ private void checkStringConcat(Operation op, Method m, Test antn) throws Excepti } } + /** + * Invokes method 'm' by passing arguments the two 'args' (which are supposed to be arrays) + * checks if the returned value. In case of error and arrays being not equal, prints their difference. + */ + private void invokeAndCompareArrays(Method m, boolean expectedResult, Object arg0, Object arg1) throws Exception { + boolean result = (Boolean)m.invoke(null, arg0, arg1); + if (expectedResult == result) + return; + + String cause = String.format("Result: (%b) of '%s' is not equal to expected (%b)", + result, m.getName(), expectedResult); + + if (expectedResult == true) { + System.err.println(cause); + System.err.println(Format.arrayDiff(arg0, arg1)); + } else { + System.err.println(cause); + System.err.printf("First array argument: %n %s%n", ArrayCodec.format(arg0)); + } + + throw new RuntimeException(cause); + } + /** * Invokes method 'm' by passing arguments 'args' and checks if the * returned value equals 'expectedResult'. @@ -247,11 +274,14 @@ private void checkStringConcat(Operation op, Method m, Test antn) throws Excepti private void invokeAndCheck(Method m, Object expectedResult, Object... args) throws Exception { Object result = m.invoke(null, args); if (!result.equals(expectedResult)) { -// System.out.println("Expected:"); -// System.out.println(expectedResult); -// System.out.println("Returned:"); -// System.out.println(result); - throw new RuntimeException("Result of '" + m.getName() + "' not equal to expected value."); + String message = "Result of '" + m.getName() + "' not equal to expected value."; + System.err.println(message); + System.err.println("Expected: " + Format.asLiteral(expectedResult)); + System.err.println("Result: " + Format.asLiteral(result)); + for (int i = 0; i < args.length; i++) { + System.err.println("Arg" + i + ": " + Format.asLiteral(args[i])); + } + throw new RuntimeException(message); } } diff --git a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java new file mode 100644 index 0000000000000..dc14e62d77123 --- /dev/null +++ b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertEquals; + +/* + * @test + * @summary Check ArrayDiff formatting + * @library /test/lib + * @run testng jdk.test.lib.format.ArrayDiffTest + */ +public class ArrayDiffTest { + + @Test + public void testEqualArrays() { + char[] first = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}; + char[] second = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}; + + assertTrue(ArrayDiff.of(first, second).areEqual()); + } + + @Test + public void testOutputFitsWidth() { + byte[] first = new byte[] {7, 8, 9, 10, 11, 12, 13}; + byte[] second = new byte[] {7, 8, 9, 10, 125, 12, 13}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 4]:%n" + + "[7, 8, 9, 10, 11, 12, 13]%n" + + "[7, 8, 9, 10, 125, 12, 13]%n" + + " ^^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testIntegers() { + int[] first = new int[] {7, 8, 10, 11, 12}; + int[] second = new int[] {7, 8, 9, 10, 11, 12, 13}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 2]:%n" + + "[7, 8, 10, 11, 12]%n" + + "[7, 8, 9, 10, 11, 12, 13]%n" + + " ^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testLongs() { + long[] first = new long[] {1, 2, 3, 4}; + long[] second = new long[] {1, 2, 3, 10}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 3]:%n" + + "[1, 2, 3, 4]%n" + + "[1, 2, 3, 10]%n" + + " ^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testFirstElementIsWrong() { + byte[] first = new byte[] {122}; + byte[] second = new byte[] {7, 8, 9, 10, 125, 12, 13}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 0]:%n" + + "[122]%n" + + "[ 7, 8, 9, 10, 125, 12, 13]%n" + + " ^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testOneElementIsEmpty() { + byte[] first = new byte[] {7, 8, 9, 10, 125, 12, 13}; + byte[] second = new byte[] {}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 0]:%n" + + "[7, 8, 9, 10, 125, 12, 13]%n" + + "[]%n" + + " ^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testOutputDoesntFitWidth() { + char[] first = new char[] {'1', '2', '3', '4', '5', '6', '7'}; + char[] second = new char[] {'1', 'F', '3', '4', '5', '6', '7'}; + + ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); + String expected = String.format( + "Arrays differ starting from [index: 1]:%n" + + "[1, 2, 3, 4, 5, ...%n" + + "[1, F, 3, 4, 5, ...%n" + + " ^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testVariableElementWidthOutputDoesntFitWidth() { + byte[] first = new byte[] {1, 2, 3, 4, 5, 6, 7}; + byte[] second = new byte[] {1, 112, 3, 4, 5, 6, 7}; + + ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); + String expected = String.format( + "Arrays differ starting from [index: 1]:%n" + + "[1, 2, 3, 4, 5, ...%n" + + "[1, 112, 3, 4, 5, ...%n" + + " ^^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testContextBefore() { + char[] first = new char[] {'1', '2', '3', '4', '5', '6', '7'}; + char[] second = new char[] {'1', '2', '3', '4', 'F', '6', '7'}; + + ArrayDiff diff = ArrayDiff.of(first, second, 20, 2); + String expected = String.format( + "Arrays differ starting from [index: 4]:%n" + + "... 3, 4, 5, 6, 7]%n" + + "... 3, 4, F, 6, 7]%n" + + " ^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testBoundedBytesWithDifferentWidth() { + byte[] first = new byte[] {0, 1, 2, 3, 125, 5, 6, 7}; + byte[] second = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; + + ArrayDiff diff = ArrayDiff.of(first, second, 24, 2); + String expected = String.format( + "Arrays differ starting from [index: 4]:%n" + + "... 2, 3, 125, 5, 6, 7]%n" + + "... 2, 3, 4, 5, 6, 7]%n" + + " ^^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testBoundedFirstElementIsWrong() { + byte[] first = new byte[] {101, 102, 103, 104, 105, 110}; + byte[] second = new byte[] {2}; + + ArrayDiff diff = ArrayDiff.of(first, second, 25, 2); + String expected = String.format( + "Arrays differ starting from [index: 0]:%n" + + "[101, 102, 103, 104, ...%n" + + "[ 2]%n" + + " ^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testBoundedOneArchiveIsEmpty() { + char[] first = new char[] {'a', 'b', 'c', 'd', 'e'}; + char[] second = new char[] {}; + + ArrayDiff diff = ArrayDiff.of(first, second, 10, 2); + String expected = String.format( + "Arrays differ starting from [index: 0]:%n" + + "[a, b, ...%n" + + "[]%n" + + " ^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testUnboundedOneArchiveIsEmpty() { + char[] first = new char[] {'a', 'b', 'c', 'd', 'e'}; + char[] second = new char[] {}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 0]:%n" + + "[a, b, c, d, e]%n" + + "[]%n" + + " ^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testUnprintableCharFormatting() { + char[] first = new char[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + char[] second = new char[] {0, 1, 2, 3, 4, 5, 6, 125, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + + ArrayDiff diff = ArrayDiff.of(first, second); + + // Lines in the code look like aren't aligned due to slashes taking more space than spaces. + var nl = System.lineSeparator(); + String expected = "Arrays differ starting from [index: 7]:" + nl + + "... \\u0005, \\u0006, \\u0007, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ..." + nl + + "... \\u0005, \\u0006, }, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ..." + nl + + " ^^^^^^^"; + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testStringElements() { + String[] first = new String[] {"first", "second", "third", "u\nprintable"}; + String[] second = new String[] {"first", "second", "incorrect", "u\nprintable"}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 2]:%n" + + "[\"first\", \"second\", \"third\", \"u\\nprintable\"]%n" + + "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]%n" + + " ^^^^^^^^^^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + @Test + public void testToStringableObjects() { + class StrObj { + private final String value; + public boolean equals(Object another) { return ((StrObj)another).value.equals(value); } + public StrObj(String value) { this.value = value; } + public String toString() { return value; } + } + + StrObj[] first = new StrObj[] {new StrObj("1"), new StrObj("Unp\rintable"), new StrObj("5")}; + StrObj[] second = new StrObj[] {new StrObj("1"), new StrObj("2"), new StrObj("5")}; + + ArrayDiff diff = ArrayDiff.of(first, second); + String expected = String.format( + "Arrays differ starting from [index: 1]:%n" + + "[1, Unp\\rintable, 5]%n" + + "[1, 2, 5]%n" + + " ^^^^^^^^^^^^^"); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + +} diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java new file mode 100644 index 0000000000000..089e2c5094212 --- /dev/null +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * A codec helping representing arrays in a string form. + * + * Encoding can be done in a controllable fashion (allowing the user to encode two + * or more arrays in a time) or as a single operation. + */ +public class ArrayCodec { + private static final String ELLIPSIS = "..."; + + private boolean exhausted; + private StringBuilder encoded; + + private List source; + private String element; + + private boolean bounded = false; + private int maxWidth; + private int idx; + + private ArrayCodec(List source) { + this.source = source; + } + + /** + * Creates a codec for a char array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(char[] array) { + var source = new ArrayList(array.length); + for (char value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a byte array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(byte[] array) { + var source = new ArrayList(array.length); + for (byte value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for an int array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(int[] array) { + var source = new ArrayList(array.length); + for (int value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a long array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(long[] array) { + var source = new ArrayList(array.length); + for (long value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a String array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(String[] array) { + var source = new ArrayList(array.length); + for (String value: array) { + source.add(value); + } + return new ArrayCodec<>(source); + } + + /** + * Creates a codec for a generic Object array + * + * @param array source array + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(Object[] array) { + var source = new ArrayList(array.length); + for (Object value: array) { + source.add(value); + } + return new ArrayCodec(source); + } + + /** + * Creates a codec for a generic array, trying to recognize its component type + * + * @param array source array + * @throws IllegalArgumentException if {@code array}'s component type is not supported + * @return an ArrayCodec for the provided array + */ + public static ArrayCodec of(Object array) { + var type = array.getClass().getComponentType(); + if (type == byte.class) { + return ArrayCodec.of((byte[])array); + } else if (type == int.class) { + return ArrayCodec.of((int[])array); + } else if (type == long.class) { + return ArrayCodec.of((long[])array); + } else if (type == char.class) { + return ArrayCodec.of((char[])array); + } else if (type == String.class) { + return ArrayCodec.of((String[])array); + } else if (!type.isPrimitive() && !type.isArray()) { + return ArrayCodec.of((String[])array); + } + + throw new IllegalArgumentException("Unsupported array component type: " + type); + } + + /** + * Formats an array at-once. + * The array is enclosed in brackets, its elements are separated with + * commas. String elements are additionally surrounded by double quotes. + * Unprintable symbols are C-stye escaped. + * + *

Sample outputs: + * + *

+     *   [0, 1, 2, 3, 4]
+     *   ["one", "first", "tree"]
+     *   [object1, object2, object3]
+     *   [a, b, \n, \u0002/, c]
+     * 
+ * + * @throws IllegalArgumentException if {@code array}'s component type is not supported + * @return an ArrayCodec for the provided array + */ + public static String format(Object array) { + var codec = ArrayCodec.of(array); + codec.startFormatting(0, -1); + while (!codec.isExhausted()) { + codec.formatNext(); + codec.appendFormatted(); + } + return codec.getEncoded(); + } + + /** + * Starts formatting with the given parameters. + * + * @param startIdx first element's index to start formattig with + * @param maxWidth maximum allowed formatting width (in characters). + * @return an ArrayCodec for the provided array + */ + public void startFormatting(int startIdx, int maxWidth) { + encoded = new StringBuilder(startIdx == 0 ? "[" : ELLIPSIS); + exhausted = false; + this.maxWidth = maxWidth; + bounded = (maxWidth > 0); + idx = startIdx; + } + + /** + * Format next element, store it in the internal element storage. + */ + public void formatNext() { + int limit = source.size(); + + String prefix = idx == 0 || idx >= limit ? "" : " "; + String suffix = (idx + 1 == limit) || (source.isEmpty() && idx == 0) + ? "]" + : idx >= limit ? "" : ","; + element = prefix + + (idx >= limit ? "" : Format.asLiteral(source.get(idx))) + + suffix; + } + + /** + * Append formatted element to internal StringBuilder. + * + * The formatted-so-far string can be accessed via {@link #getEncoded} + * no elements in array left the method silently does nothing. + */ + public void appendFormatted() { + if (exhausted) { + return; + } + + boolean isLast = idx == source.size() - 1; + if (isLast || source.isEmpty()) { + exhausted = true; + } + + if (bounded && encoded.length() + element.length() > maxWidth - ELLIPSIS.length()) { + encoded.append(isLast ? element : " " + ELLIPSIS); + exhausted = true; + } else { + encoded.append(element); + } + idx++; + } + + /** + * Aligns the element by another codec. + * + * If another codec's last encoded element string is longer than this + * codec's, widens this codec's encoded element with spaces so the + * two strings have the same length; + * + * @param another Another codec to compare encoded element width with + */ + public void alignBy(ArrayCodec another) { + if (!element.equals("") && !element.equals("]")) { + int delta = another.element.length() - element.length(); + if (delta > 0) { + element = Format.paddingForWidth(delta) + element; + } + } + } + + /** + * Indicates if there are no elements left in the source array + * + * @return {@code true} if there are no elements left, {@code false} otherwise + */ + public boolean isExhausted() { + return exhausted; + } + + /** + * Returns the string encoded-so-far + * + * @return the string encoded-so-far + */ + public String getEncoded() { + return encoded.toString(); + } + + /** + * Returns the length of the string encoded-so-far + * + * @return the length of the string encoded-so-far + */ + public int getEncodedLength() { + return encoded.length(); + } + + /** + * Returns the length of the last encoded element + * + * @return the length of the last encoded element + */ + public int getElementLength() { + return element.length(); + } + + /** + * Finds and returns the first mismatch index in another codec + * + * @param another a codec mismatch with whom is to be found + * @return the first mismatched element's index + */ + public int findMismatchIndex(ArrayCodec another) { + int result = 0; + while ((source.size() > result) && + (another.source.size() > result) && + (source.get(result).equals(another.source.get(result)))) { + result += 1; + } + return result; + } + + /** + * Indicates whether source array for another codec is equal to this codec's array + * + * @return {@code true} if source arrays are equal, {@code false} otherwise + */ + public boolean equals(ArrayCodec another) { + return source.equals(another.source); + } +} diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java new file mode 100644 index 0000000000000..667a44f6bc8e3 --- /dev/null +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * A difference between two arrays, which can be pretty formatted. + * For the calculated difference, user can request if the two arrays + * are equal (in terms of {@link Object#equals Object.equals()} for their + * elements). For the arrays that differ, a human-readable difference can + * be provided. + * + *

The difference is represented as a four-line text block, comprising of the + * first different element index, arrays printouts in the difference area, + * and a difference mark. For Primitive and Object elements in the source + * arrays their C-style escaped {@link String#valueOf String.valueOf()} are + * printed, element in String[] arrays are additionally surrounded with quotes. + * Additional formatting parameters, like maximum allowed width and number of + * elements printed before difference, can be specified. + * + *

Output examples: + * + *

two int arrays:

+ *
+ * Arrays differ starting from [index: 4]:
+ *         ... 3, 4,   5, 6, 7]
+ *         ... 3, 4, 225, 6, 7]
+ *                  ^^^^
+ * 
+ *

two String arrays:

+ *
+ * Arrays differ starting from [index: 2]:
+ *         ["first", "second",     "third", "u\nprintable"]
+ *         ["first", "second", "incorrect", "u\nprintable"]
+ *                            ^^^^^^^^^^^^
+ * 
+ *

two char arrays arrays:

+ *
+ * Arrays differ starting from [index: 7]:
+ *         ... \u0001, \u0002, \u0007, a, b, \n, ...
+ *         ... \u0001, \u0002,      }, a, b, \n, ...
+ *                            ^^^^^^^
+ * 
+ */ +public class ArrayDiff implements Diff { + + private int failureIdx; + private final int maxWidth; + private final int contextBefore; + + private final ArrayCodec first; + private final ArrayCodec second; + + /** + * Default limits for the formatter + */ + public static class Defaults { + final static int WIDTH = 80; + final static int CONTEXT_BEFORE = 2; + } + + private ArrayDiff(ArrayCodec first, ArrayCodec second, + int width, int getContextBefore) { + this.first = first; + this.second = second; + this.maxWidth = width; + this.contextBefore = getContextBefore; + failureIdx = first.findMismatchIndex(second); + } + + /** + * Creates an ArrayDiff fom two arrays and default limits. The given arguments must be of the same + * component type. + * + * @param first the first array + * @param second the second array + * @return an ArrayDiff instance for the two arrays + */ + public static ArrayDiff of(Object first, Object second) { + return ArrayDiff.of(first, second, Defaults.WIDTH, Defaults.CONTEXT_BEFORE); + } + + /** + * Creates an ArrayDiff fom two arrays with the given limits. The given arguments must be of the same + * component type. + * + * @param first the first array + * @param second the second array + * @param width the maximum allowed width in characters for the formatting + * @param contextBefore maximum number of elements to print before those that differ + * @throws IllegalArgumentException if component types of arrays is not supported or are not the same + * @return an ArrayDiff instance for the two arrays and formatting parameters provided + */ + public static ArrayDiff of(Object first, Object second, int width, int contextBefore) { + boolean bothAreArrays = first.getClass().isArray() && second.getClass().isArray(); + boolean componentTypesAreSame = + first.getClass().getComponentType() == second.getClass().getComponentType(); + + if (!bothAreArrays || !componentTypesAreSame) { + throw new IllegalArgumentException("Both arguments should be arrays of the same type"); + } + + var type = first.getClass().getComponentType(); + if (type == byte.class) { + return new ArrayDiff<>( + ArrayCodec.of((byte[])first), + ArrayCodec.of((byte[])second), + width, contextBefore); + } else if (type == int.class) { + return new ArrayDiff<>( + ArrayCodec.of((int[])first), + ArrayCodec.of((int[])second), + width, contextBefore); + } else if (type == long.class) { + return new ArrayDiff<>( + ArrayCodec.of((long[])first), + ArrayCodec.of((long[])second), + width, contextBefore); + } else if (type == char.class) { + return new ArrayDiff<>( + ArrayCodec.of((char[])first), + ArrayCodec.of((char[])second), + width, contextBefore); + } else if (type == String.class) { + return new ArrayDiff<>( + ArrayCodec.of((String[])first), + ArrayCodec.of((String[])second), + width, contextBefore); + } else if (!type.isPrimitive() && !type.isArray()) { + return new ArrayDiff<>( + ArrayCodec.of((Object[])first), + ArrayCodec.of((Object[])second), + width, contextBefore); + } + + throw new IllegalArgumentException("Unsupported array component type: " + type); + } + + /** + * Formats the given diff. + * + * @return formatted difference representation. + */ + @Override + public String format() { + if (areEqual()) { + return ""; + } + + return format(false) + .orElseGet(() -> format(true).get()); + } + + /** + * Indicates whether the two source arrays are equal + * + * @return {@code true} if the arrays are different, {@code false} otherwise + */ + @Override + public boolean areEqual() { + return first.equals(second); + } + + private void extractAndAlignElements() { + first.formatNext(); + second.formatNext(); + + first.alignBy(second); + second.alignBy(first); + } + + private static String failureMarkForWidth(int width) { + return new String("^").repeat(width); + } + + private Optional format(boolean bounded) { + int idx = bounded ? Math.max(0, failureIdx - contextBefore) : 0; + + first.startFormatting(idx, bounded ? maxWidth : -1); + second.startFormatting(idx, bounded ? maxWidth : -1); + StringBuilder failureMark = new StringBuilder( + Format.paddingForWidth(first.getEncodedLength())); + + for (; !(first.isExhausted() && second.isExhausted()); idx++) { + extractAndAlignElements(); + + first.appendFormatted(); + second.appendFormatted(); + + { // Process failure mark + if (idx < failureIdx) { + failureMark.append(Format.paddingForWidth(first.getElementLength())); + } else if (idx == failureIdx) { + failureMark.append(failureMarkForWidth(first.getElementLength()-1)); + } + } + + final int maxEncodedLength = Math.max( + first.getEncodedLength(), + second.getEncodedLength()); + if (!bounded && maxEncodedLength > maxWidth) { + return Optional.empty(); + } + } + + return Optional.of(String.format( + "Arrays differ starting from [index: %d]:%n%s%n%s%n%s", + failureIdx, + first.getEncoded(), + second.getEncoded(), + failureMark.toString())); + } + +} + diff --git a/test/lib/jdk/test/lib/format/Diff.java b/test/lib/jdk/test/lib/format/Diff.java new file mode 100644 index 0000000000000..eefa3a3cfd05b --- /dev/null +++ b/test/lib/jdk/test/lib/format/Diff.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Represents a possible difference between two objects. + */ +package jdk.test.lib.format; + +/** + * An abstraction representing formattabe difference between two or more objects + */ +public interface Diff { + + /** + * Formats the given diff. Different implementations can provide different + * result and formatting style. + * + * @return formatted difference representation. + */ + String format(); + + /** + * Indicates whether the two source arrays are equal. Different + * implementations can treat this notion differently. + * + * @return {@code true} if the source objects are different, {@code false} otherwise + */ + boolean areEqual(); +} diff --git a/test/lib/jdk/test/lib/format/Format.java b/test/lib/jdk/test/lib/format/Format.java new file mode 100644 index 0000000000000..22825274c85e0 --- /dev/null +++ b/test/lib/jdk/test/lib/format/Format.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.test.lib.format; + +/** + * A collection of formatting utilities + */ +public class Format { + + /** + * Formats character as literal, using C-style escaping for unprintable symbols + * + * @param c character to format + * @return formatted string representation of the character + */ + public static String asLiteral(char c) { + StringBuilder sb = new StringBuilder(); + appendCharToSb(c, sb); + return sb.toString(); + } + + /** + * Escapes String in C-style + * + * @param src source string + * @return C-style escaped source string + */ + public static String escapeString(String src) { + StringBuilder sb = new StringBuilder(); + src.chars().forEachOrdered( + (c) -> appendCharToSb((char) c, sb)); + return sb.toString(); + } + + /** + * Formats Object as literal, using its String representation C-style escaped. + * + * @param o object to format + * @return C-style escaped String representation of the object + */ + public static String asLiteral(Object o) { + if (o instanceof String) { + return '"' + escapeString((String)o) + '"'; + } else if (o instanceof Character) { + return asLiteral((char) o); + } else if (o instanceof Byte) { + return String.valueOf(o); + } else { + return escapeString(String.valueOf(o)); + } + } + + /** + * Formats a difference between two arrays with index of the first mismatch element, + * and slices of arrays necessary to understand the problem, along with a failure mark. + * + * @param first first array to compare + * @param second second array to compare + * @return the difference, generated by the {@link ArrayDiff ArrayDiff} + */ + public static String arrayDiff(Object first, Object second) { + return ArrayDiff.of(first, second).format(); + } + + /** + * Formats a difference between two arrays with index of the first mismatch element, + * and slices of arrays necessary to understand the problem, along with a failure mark. + * Takes into account maximum allowed width and context (in elements) before the mismatch. + * + * @param first first array to compare + * @param second second array to compare + * @param width the maximum allowed width in characters for the formatting + * @param contextBefore maximum number of elements to print before those that differ + * @return the difference, generated by the {@link ArrayDiff ArrayDiff} + */ + public static String arrayDiff(Object first, Object second, int width, int contextBefore) { + return ArrayDiff.of(first, second, width, contextBefore).format(); + } + + + /** + * Returns a string of spaces with length specified. + * + * @param width number of spaces in the resulting string + * @return Padding string of spaces + */ + public static String paddingForWidth(int width) { + return new String(" ").repeat(width); + } + + private static void appendCharToSb(char c, StringBuilder sb) { + if (c == 10) { + sb.append("\\n"); + } else if (c == 13) { + sb.append("\\r"); + } else if (c == 92) { + sb.append("\\\\"); + } else if (c == 34) { + sb.append("\\\""); + } else if (c < 32 || c > 126) { + sb.append("\\u" + String.format("%04X", (int) c)); + } else { + sb.append(c); + } + } +} From 7e6c0a699dd8351b0aa4fdc79672e478bdb3845c Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 13:47:59 +0200 Subject: [PATCH 02/14] Fix indent in ArrayCodec --- test/lib/jdk/test/lib/format/ArrayCodec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java index 089e2c5094212..cf64be02efe21 100644 --- a/test/lib/jdk/test/lib/format/ArrayCodec.java +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -257,7 +257,7 @@ public void alignBy(ArrayCodec another) { int delta = another.element.length() - element.length(); if (delta > 0) { element = Format.paddingForWidth(delta) + element; - } + } } } From 402fb529253a08ce22553bb66a44320640c79d5f Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 13:52:30 +0200 Subject: [PATCH 03/14] Replace new space string with constant --- test/lib/jdk/test/lib/format/Format.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/jdk/test/lib/format/Format.java b/test/lib/jdk/test/lib/format/Format.java index 22825274c85e0..9cdd9f0d1845d 100644 --- a/test/lib/jdk/test/lib/format/Format.java +++ b/test/lib/jdk/test/lib/format/Format.java @@ -106,7 +106,7 @@ public static String arrayDiff(Object first, Object second, int width, int conte * @return Padding string of spaces */ public static String paddingForWidth(int width) { - return new String(" ").repeat(width); + return " ".repeat(width); } private static void appendCharToSb(char c, StringBuilder sb) { From 0aa6e2b3747bccf181872bbc7ded24a6e2aa2728 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 13:54:09 +0200 Subject: [PATCH 04/14] Make ArrayDif defaults really public --- test/lib/jdk/test/lib/format/ArrayDiff.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java index 667a44f6bc8e3..f4cfad083ca47 100644 --- a/test/lib/jdk/test/lib/format/ArrayDiff.java +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -79,8 +79,8 @@ public class ArrayDiff implements Diff { * Default limits for the formatter */ public static class Defaults { - final static int WIDTH = 80; - final static int CONTEXT_BEFORE = 2; + public final static int WIDTH = 80; + public final static int CONTEXT_BEFORE = 2; } private ArrayDiff(ArrayCodec first, ArrayCodec second, From c51a2b76e71cdd324d6646335a5d2fad53e8e296 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 15:29:11 +0200 Subject: [PATCH 05/14] Align strings in ArrayDiffTest --- .../jdk/test/lib/format/ArrayDiffTest.java | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java index dc14e62d77123..99b8e6ea0d13b 100644 --- a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java +++ b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java @@ -53,9 +53,9 @@ public void testOutputFitsWidth() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 4]:%n" + - "[7, 8, 9, 10, 11, 12, 13]%n" + - "[7, 8, 9, 10, 125, 12, 13]%n" + - " ^^^^"); + "[7, 8, 9, 10, 11, 12, 13]%n" + + "[7, 8, 9, 10, 125, 12, 13]%n" + + " ^^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -69,9 +69,9 @@ public void testIntegers() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 2]:%n" + - "[7, 8, 10, 11, 12]%n" + - "[7, 8, 9, 10, 11, 12, 13]%n" + - " ^^^"); + "[7, 8, 10, 11, 12]%n" + + "[7, 8, 9, 10, 11, 12, 13]%n" + + " ^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -85,9 +85,9 @@ public void testLongs() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 3]:%n" + - "[1, 2, 3, 4]%n" + - "[1, 2, 3, 10]%n" + - " ^^^"); + "[1, 2, 3, 4]%n" + + "[1, 2, 3, 10]%n" + + " ^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -101,9 +101,9 @@ public void testFirstElementIsWrong() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 0]:%n" + - "[122]%n" + - "[ 7, 8, 9, 10, 125, 12, 13]%n" + - " ^^^"); + "[122]%n" + + "[ 7, 8, 9, 10, 125, 12, 13]%n" + + " ^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -117,9 +117,9 @@ public void testOneElementIsEmpty() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 0]:%n" + - "[7, 8, 9, 10, 125, 12, 13]%n" + - "[]%n" + - " ^"); + "[7, 8, 9, 10, 125, 12, 13]%n" + + "[]%n" + + " ^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -133,9 +133,9 @@ public void testOutputDoesntFitWidth() { ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); String expected = String.format( "Arrays differ starting from [index: 1]:%n" + - "[1, 2, 3, 4, 5, ...%n" + - "[1, F, 3, 4, 5, ...%n" + - " ^^"); + "[1, 2, 3, 4, 5, ...%n" + + "[1, F, 3, 4, 5, ...%n" + + " ^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -149,9 +149,9 @@ public void testVariableElementWidthOutputDoesntFitWidth() { ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); String expected = String.format( "Arrays differ starting from [index: 1]:%n" + - "[1, 2, 3, 4, 5, ...%n" + - "[1, 112, 3, 4, 5, ...%n" + - " ^^^^"); + "[1, 2, 3, 4, 5, ...%n" + + "[1, 112, 3, 4, 5, ...%n" + + " ^^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -165,9 +165,9 @@ public void testContextBefore() { ArrayDiff diff = ArrayDiff.of(first, second, 20, 2); String expected = String.format( "Arrays differ starting from [index: 4]:%n" + - "... 3, 4, 5, 6, 7]%n" + - "... 3, 4, F, 6, 7]%n" + - " ^^"); + "... 3, 4, 5, 6, 7]%n" + + "... 3, 4, F, 6, 7]%n" + + " ^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -181,9 +181,9 @@ public void testBoundedBytesWithDifferentWidth() { ArrayDiff diff = ArrayDiff.of(first, second, 24, 2); String expected = String.format( "Arrays differ starting from [index: 4]:%n" + - "... 2, 3, 125, 5, 6, 7]%n" + - "... 2, 3, 4, 5, 6, 7]%n" + - " ^^^^"); + "... 2, 3, 125, 5, 6, 7]%n" + + "... 2, 3, 4, 5, 6, 7]%n" + + " ^^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -197,9 +197,9 @@ public void testBoundedFirstElementIsWrong() { ArrayDiff diff = ArrayDiff.of(first, second, 25, 2); String expected = String.format( "Arrays differ starting from [index: 0]:%n" + - "[101, 102, 103, 104, ...%n" + - "[ 2]%n" + - " ^^^"); + "[101, 102, 103, 104, ...%n" + + "[ 2]%n" + + " ^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -213,9 +213,9 @@ public void testBoundedOneArchiveIsEmpty() { ArrayDiff diff = ArrayDiff.of(first, second, 10, 2); String expected = String.format( "Arrays differ starting from [index: 0]:%n" + - "[a, b, ...%n" + - "[]%n" + - " ^"); + "[a, b, ...%n" + + "[]%n" + + " ^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -229,9 +229,9 @@ public void testUnboundedOneArchiveIsEmpty() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 0]:%n" + - "[a, b, c, d, e]%n" + - "[]%n" + - " ^"); + "[a, b, c, d, e]%n" + + "[]%n" + + " ^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -262,9 +262,9 @@ public void testStringElements() { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 2]:%n" + - "[\"first\", \"second\", \"third\", \"u\\nprintable\"]%n" + - "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]%n" + - " ^^^^^^^^^^^^"); + "[\"first\", \"second\", \"third\", \"u\\nprintable\"]%n" + + "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]%n" + + " ^^^^^^^^^^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); @@ -285,9 +285,9 @@ class StrObj { ArrayDiff diff = ArrayDiff.of(first, second); String expected = String.format( "Arrays differ starting from [index: 1]:%n" + - "[1, Unp\\rintable, 5]%n" + - "[1, 2, 5]%n" + - " ^^^^^^^^^^^^^"); + "[1, Unp\\rintable, 5]%n" + + "[1, 2, 5]%n" + + " ^^^^^^^^^^^^^"); assertFalse(diff.areEqual()); assertEquals(diff.format(), expected); From 803cd35564ca0eca694b2bfaf79e45d023997c58 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 21:43:37 +0200 Subject: [PATCH 06/14] Fix Object component type for ArrayCodec.of --- test/lib/jdk/test/lib/format/ArrayCodec.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java index cf64be02efe21..47b393ccfb80f 100644 --- a/test/lib/jdk/test/lib/format/ArrayCodec.java +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -154,7 +154,7 @@ public static ArrayCodec of(Object array) { } else if (type == String.class) { return ArrayCodec.of((String[])array); } else if (!type.isPrimitive() && !type.isArray()) { - return ArrayCodec.of((String[])array); + return ArrayCodec.of((Object[])array); } throw new IllegalArgumentException("Unsupported array component type: " + type); From e6fb6d04caddc4b410a594574f570cfcb9445e4a Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Wed, 30 Sep 2020 22:18:17 +0200 Subject: [PATCH 07/14] Print all arguments in the invokeAndCheck's exception --- .../string/TestStringIntrinsics.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java index 93968de39d737..2b0d2ad92b5f5 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java @@ -272,15 +272,19 @@ private void invokeAndCompareArrays(Method m, boolean expectedResult, Object arg * returned value equals 'expectedResult'. */ private void invokeAndCheck(Method m, Object expectedResult, Object... args) throws Exception { - Object result = m.invoke(null, args); - if (!result.equals(expectedResult)) { - String message = "Result of '" + m.getName() + "' not equal to expected value."; - System.err.println(message); - System.err.println("Expected: " + Format.asLiteral(expectedResult)); - System.err.println("Result: " + Format.asLiteral(result)); + Object actualResult = m.invoke(null, args); + if (!actualResult.equals(expectedResult)) { + var nl = System.lineSeparator(); + String message = + "Actual result of '" + m.getName() + "' is not equal to expected value." + nl + + "Expected: " + Format.asLiteral(expectedResult) + nl + + "Actual: " + Format.asLiteral(actualResult); + for (int i = 0; i < args.length; i++) { - System.err.println("Arg" + i + ": " + Format.asLiteral(args[i])); + message += nl + " Arg" + i + ": " + Format.asLiteral(args[i]); } + + System.err.println(message); throw new RuntimeException(message); } } From f0b1df8974a25132b5932439445bd14059add318 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Thu, 1 Oct 2020 06:51:55 +0200 Subject: [PATCH 08/14] Use SB to build TestStringIntrinsics message, avoid type guessing in ArrayDiff --- .../string/TestStringIntrinsics.java | 11 +++--- test/lib/jdk/test/lib/format/ArrayDiff.java | 38 ++----------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java index 2b0d2ad92b5f5..659840293975b 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java +++ b/test/hotspot/jtreg/compiler/intrinsics/string/TestStringIntrinsics.java @@ -275,15 +275,16 @@ private void invokeAndCheck(Method m, Object expectedResult, Object... args) thr Object actualResult = m.invoke(null, args); if (!actualResult.equals(expectedResult)) { var nl = System.lineSeparator(); - String message = - "Actual result of '" + m.getName() + "' is not equal to expected value." + nl + - "Expected: " + Format.asLiteral(expectedResult) + nl + - "Actual: " + Format.asLiteral(actualResult); + StringBuilder msgBuilder = new StringBuilder(); + msgBuilder.append("Actual result of '" + m.getName() + "' is not equal to expected value." + nl); + msgBuilder.append("Expected: " + Format.asLiteral(expectedResult) + nl); + msgBuilder.append("Actual: " + Format.asLiteral(actualResult)); for (int i = 0; i < args.length; i++) { - message += nl + " Arg" + i + ": " + Format.asLiteral(args[i]); + msgBuilder.append(nl + " Arg" + i + ": " + Format.asLiteral(args[i])); } + final String message = msgBuilder.toString(); System.err.println(message); throw new RuntimeException(message); } diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java index f4cfad083ca47..1259d124033d2 100644 --- a/test/lib/jdk/test/lib/format/ArrayDiff.java +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -124,40 +124,10 @@ public static ArrayDiff of(Object first, Object second, int width, int contextBe throw new IllegalArgumentException("Both arguments should be arrays of the same type"); } - var type = first.getClass().getComponentType(); - if (type == byte.class) { - return new ArrayDiff<>( - ArrayCodec.of((byte[])first), - ArrayCodec.of((byte[])second), - width, contextBefore); - } else if (type == int.class) { - return new ArrayDiff<>( - ArrayCodec.of((int[])first), - ArrayCodec.of((int[])second), - width, contextBefore); - } else if (type == long.class) { - return new ArrayDiff<>( - ArrayCodec.of((long[])first), - ArrayCodec.of((long[])second), - width, contextBefore); - } else if (type == char.class) { - return new ArrayDiff<>( - ArrayCodec.of((char[])first), - ArrayCodec.of((char[])second), - width, contextBefore); - } else if (type == String.class) { - return new ArrayDiff<>( - ArrayCodec.of((String[])first), - ArrayCodec.of((String[])second), - width, contextBefore); - } else if (!type.isPrimitive() && !type.isArray()) { - return new ArrayDiff<>( - ArrayCodec.of((Object[])first), - ArrayCodec.of((Object[])second), - width, contextBefore); - } - - throw new IllegalArgumentException("Unsupported array component type: " + type); + return new ArrayDiff( + ArrayCodec.of(first), + ArrayCodec.of(second), + width, contextBefore); } /** From d792e6d0ab910c826d6c5d34868de1a382a5d98d Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Fri, 2 Oct 2020 10:37:37 +0200 Subject: [PATCH 09/14] Fix typos and comments --- test/lib/jdk/test/lib/format/Diff.java | 2 +- test/lib/jdk/test/lib/format/Format.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lib/jdk/test/lib/format/Diff.java b/test/lib/jdk/test/lib/format/Diff.java index eefa3a3cfd05b..ac73a75d7ecf7 100644 --- a/test/lib/jdk/test/lib/format/Diff.java +++ b/test/lib/jdk/test/lib/format/Diff.java @@ -27,7 +27,7 @@ package jdk.test.lib.format; /** - * An abstraction representing formattabe difference between two or more objects + * An abstraction representing formattable difference between two or more objects */ public interface Diff { diff --git a/test/lib/jdk/test/lib/format/Format.java b/test/lib/jdk/test/lib/format/Format.java index 9cdd9f0d1845d..9770e33c232d2 100644 --- a/test/lib/jdk/test/lib/format/Format.java +++ b/test/lib/jdk/test/lib/format/Format.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it From 81f7a9d9b8847c8862e1edc7955a575641051339 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Fri, 2 Oct 2020 10:44:15 +0200 Subject: [PATCH 10/14] Make ArrayDiff.Defaults a generic Diff.Defaults --- test/lib/jdk/test/lib/format/ArrayDiff.java | 10 +--------- test/lib/jdk/test/lib/format/Diff.java | 8 ++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java index 1259d124033d2..26fd165abf3e9 100644 --- a/test/lib/jdk/test/lib/format/ArrayDiff.java +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -75,14 +75,6 @@ public class ArrayDiff implements Diff { private final ArrayCodec first; private final ArrayCodec second; - /** - * Default limits for the formatter - */ - public static class Defaults { - public final static int WIDTH = 80; - public final static int CONTEXT_BEFORE = 2; - } - private ArrayDiff(ArrayCodec first, ArrayCodec second, int width, int getContextBefore) { this.first = first; @@ -101,7 +93,7 @@ private ArrayDiff(ArrayCodec first, ArrayCodec second, * @return an ArrayDiff instance for the two arrays */ public static ArrayDiff of(Object first, Object second) { - return ArrayDiff.of(first, second, Defaults.WIDTH, Defaults.CONTEXT_BEFORE); + return ArrayDiff.of(first, second, Diff.Defaults.WIDTH, Diff.Defaults.CONTEXT_BEFORE); } /** diff --git a/test/lib/jdk/test/lib/format/Diff.java b/test/lib/jdk/test/lib/format/Diff.java index ac73a75d7ecf7..85ad8839be984 100644 --- a/test/lib/jdk/test/lib/format/Diff.java +++ b/test/lib/jdk/test/lib/format/Diff.java @@ -31,6 +31,14 @@ */ public interface Diff { + /** + * Default limits for formatters + */ + public static class Defaults { + public final static int WIDTH = 80; + public final static int CONTEXT_BEFORE = 2; + } + /** * Formats the given diff. Different implementations can provide different * result and formatting style. From 39992fcba979a2f6bf7ea73aff92461b13220ee2 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Sat, 3 Oct 2020 13:44:24 +0200 Subject: [PATCH 11/14] Add null values support and two-way testing --- .../jdk/test/lib/format/ArrayDiffTest.java | 471 +++++++++++------- test/lib/jdk/test/lib/format/ArrayCodec.java | 24 +- test/lib/jdk/test/lib/format/ArrayDiff.java | 11 +- 3 files changed, 320 insertions(+), 186 deletions(-) diff --git a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java index 99b8e6ea0d13b..18bf3adc6002a 100644 --- a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java +++ b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java @@ -47,227 +47,226 @@ public void testEqualArrays() { @Test public void testOutputFitsWidth() { - byte[] first = new byte[] {7, 8, 9, 10, 11, 12, 13}; - byte[] second = new byte[] {7, 8, 9, 10, 125, 12, 13}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 4]:%n" + - "[7, 8, 9, 10, 11, 12, 13]%n" + - "[7, 8, 9, 10, 125, 12, 13]%n" + - " ^^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {7, 8, 9, 10, 11, 12, 13}, + new byte[] {7, 8, 9, 10, 125, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "[7, 8, 9, 10, 11, 12, 13]", + "[7, 8, 9, 10, 125, 12, 13]", + " ^^^^") + .assertTwoWay(); } @Test public void testIntegers() { - int[] first = new int[] {7, 8, 10, 11, 12}; - int[] second = new int[] {7, 8, 9, 10, 11, 12, 13}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 2]:%n" + - "[7, 8, 10, 11, 12]%n" + - "[7, 8, 9, 10, 11, 12, 13]%n" + - " ^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new int[] {7, 8, 10, 11, 12}, + new int[] {7, 8, 9, 10, 11, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 2, + "[7, 8, 10, 11, 12]", + "[7, 8, 9, 10, 11, 12, 13]", + " ^^^") + .assertTwoWay(); } @Test public void testLongs() { - long[] first = new long[] {1, 2, 3, 4}; - long[] second = new long[] {1, 2, 3, 10}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 3]:%n" + - "[1, 2, 3, 4]%n" + - "[1, 2, 3, 10]%n" + - " ^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new long[] {1, 2, 3, 4}, + new long[] {1, 2, 3, 10}) + .thatResultIs(false) + .thatFormattedValuesAre( + 3, + "[1, 2, 3, 4]", + "[1, 2, 3, 10]", + " ^^^") + .assertTwoWay(); } @Test public void testFirstElementIsWrong() { - byte[] first = new byte[] {122}; - byte[] second = new byte[] {7, 8, 9, 10, 125, 12, 13}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 0]:%n" + - "[122]%n" + - "[ 7, 8, 9, 10, 125, 12, 13]%n" + - " ^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {122}, + new byte[] {7, 8, 9, 10, 125, 12, 13}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[122]", + "[ 7, 8, 9, 10, 125, 12, 13]", + " ^^^") + .assertTwoWay(); } @Test public void testOneElementIsEmpty() { - byte[] first = new byte[] {7, 8, 9, 10, 125, 12, 13}; - byte[] second = new byte[] {}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 0]:%n" + - "[7, 8, 9, 10, 125, 12, 13]%n" + - "[]%n" + - " ^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new byte[] {7, 8, 9, 10, 125, 12, 13}, + new byte[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[7, 8, 9, 10, 125, 12, 13]", + "[]", + " ^") + .assertTwoWay(); } @Test public void testOutputDoesntFitWidth() { - char[] first = new char[] {'1', '2', '3', '4', '5', '6', '7'}; - char[] second = new char[] {'1', 'F', '3', '4', '5', '6', '7'}; - - ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); - String expected = String.format( - "Arrays differ starting from [index: 1]:%n" + - "[1, 2, 3, 4, 5, ...%n" + - "[1, F, 3, 4, 5, ...%n" + - " ^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(20, Integer.MAX_VALUE) + .withArrays( + new char[] {'1', '2', '3', '4', '5', '6', '7'}, + new char[] {'1', 'F', '3', '4', '5', '6', '7'}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, 2, 3, 4, 5, ...", + "[1, F, 3, 4, 5, ...", + " ^^") + .assertTwoWay(); } @Test public void testVariableElementWidthOutputDoesntFitWidth() { - byte[] first = new byte[] {1, 2, 3, 4, 5, 6, 7}; - byte[] second = new byte[] {1, 112, 3, 4, 5, 6, 7}; - - ArrayDiff diff = ArrayDiff.of(first, second, 20, Integer.MAX_VALUE); - String expected = String.format( - "Arrays differ starting from [index: 1]:%n" + - "[1, 2, 3, 4, 5, ...%n" + - "[1, 112, 3, 4, 5, ...%n" + - " ^^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(20, Integer.MAX_VALUE) + .withArrays( + new byte[] {1, 2, 3, 4, 5, 6, 7}, + new byte[] {1, 112, 3, 4, 5, 6, 7}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, 2, 3, 4, 5, ...", + "[1, 112, 3, 4, 5, ...", + " ^^^^") + .assertTwoWay(); } @Test public void testContextBefore() { - char[] first = new char[] {'1', '2', '3', '4', '5', '6', '7'}; - char[] second = new char[] {'1', '2', '3', '4', 'F', '6', '7'}; - - ArrayDiff diff = ArrayDiff.of(first, second, 20, 2); - String expected = String.format( - "Arrays differ starting from [index: 4]:%n" + - "... 3, 4, 5, 6, 7]%n" + - "... 3, 4, F, 6, 7]%n" + - " ^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(20, 2) + .withArrays( + new char[] {'1', '2', '3', '4', '5', '6', '7'}, + new char[] {'1', '2', '3', '4', 'F', '6', '7'}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "... 3, 4, 5, 6, 7]", + "... 3, 4, F, 6, 7]", + " ^^") + .assertTwoWay(); } @Test public void testBoundedBytesWithDifferentWidth() { - byte[] first = new byte[] {0, 1, 2, 3, 125, 5, 6, 7}; - byte[] second = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; - - ArrayDiff diff = ArrayDiff.of(first, second, 24, 2); - String expected = String.format( - "Arrays differ starting from [index: 4]:%n" + - "... 2, 3, 125, 5, 6, 7]%n" + - "... 2, 3, 4, 5, 6, 7]%n" + - " ^^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(24, 2) + .withArrays( + new byte[] {0, 1, 2, 3, 125, 5, 6, 7}, + new byte[] {0, 1, 2, 3, 4, 5, 6, 7}) + .thatResultIs(false) + .thatFormattedValuesAre( + 4, + "... 2, 3, 125, 5, 6, 7]", + "... 2, 3, 4, 5, 6, 7]", + " ^^^^") + .assertTwoWay(); } @Test public void testBoundedFirstElementIsWrong() { - byte[] first = new byte[] {101, 102, 103, 104, 105, 110}; - byte[] second = new byte[] {2}; - - ArrayDiff diff = ArrayDiff.of(first, second, 25, 2); - String expected = String.format( - "Arrays differ starting from [index: 0]:%n" + - "[101, 102, 103, 104, ...%n" + - "[ 2]%n" + - " ^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(25, 2) + .withArrays( + new byte[] {101, 102, 103, 104, 105, 110}, + new byte[] {2}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[101, 102, 103, 104, ...", + "[ 2]", + " ^^^") + .assertTwoWay(); } @Test public void testBoundedOneArchiveIsEmpty() { - char[] first = new char[] {'a', 'b', 'c', 'd', 'e'}; - char[] second = new char[] {}; - - ArrayDiff diff = ArrayDiff.of(first, second, 10, 2); - String expected = String.format( - "Arrays differ starting from [index: 0]:%n" + - "[a, b, ...%n" + - "[]%n" + - " ^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withParams(10, 2) + .withArrays( + new char[] {'a', 'b', 'c', 'd', 'e'}, + new char[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[a, b, ...", + "[]", + " ^") + .assertTwoWay(); } @Test public void testUnboundedOneArchiveIsEmpty() { - char[] first = new char[] {'a', 'b', 'c', 'd', 'e'}; - char[] second = new char[] {}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 0]:%n" + - "[a, b, c, d, e]%n" + - "[]%n" + - " ^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new char[] {'a', 'b', 'c', 'd', 'e'}, + new char[] {}) + .thatResultIs(false) + .thatFormattedValuesAre( + 0, + "[a, b, c, d, e]", + "[]", + " ^") + .assertTwoWay(); } @Test public void testUnprintableCharFormatting() { - char[] first = new char[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - char[] second = new char[] {0, 1, 2, 3, 4, 5, 6, 125, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - - ArrayDiff diff = ArrayDiff.of(first, second); - - // Lines in the code look like aren't aligned due to slashes taking more space than spaces. - var nl = System.lineSeparator(); - String expected = "Arrays differ starting from [index: 7]:" + nl + - "... \\u0005, \\u0006, \\u0007, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ..." + nl + - "... \\u0005, \\u0006, }, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ..." + nl + - " ^^^^^^^"; - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new char[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, + new char[] {0, 1, 2, 3, 4, 5, 6, 125, 8, 9, 10, 11, 12, 13, 14, 15, 16}) + .thatResultIs(false) + .thatFormattedValuesAre( + 7, + "... \\u0005, \\u0006, \\u0007, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ...", + "... \\u0005, \\u0006, }, \\u0008, \\u0009, \\n, \\u000B, \\u000C, \\r, \\u000E, ...", + " ^^^^^^^") + .assertTwoWay(); } @Test public void testStringElements() { - String[] first = new String[] {"first", "second", "third", "u\nprintable"}; - String[] second = new String[] {"first", "second", "incorrect", "u\nprintable"}; - - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 2]:%n" + - "[\"first\", \"second\", \"third\", \"u\\nprintable\"]%n" + - "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]%n" + - " ^^^^^^^^^^^^"); - - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); + new AssertBuilder() + .withDefaultParams() + .withArrays( + new String[] {"first", "second", "third", "u\nprintable"}, + new String[] {"first", "second", "incorrect", "u\nprintable"}) + .thatResultIs(false) + .thatFormattedValuesAre( + 2, + "[\"first\", \"second\", \"third\", \"u\\nprintable\"]", + "[\"first\", \"second\", \"incorrect\", \"u\\nprintable\"]", + " ^^^^^^^^^^^^") + .assertTwoWay(); } @Test @@ -279,18 +278,132 @@ class StrObj { public String toString() { return value; } } - StrObj[] first = new StrObj[] {new StrObj("1"), new StrObj("Unp\rintable"), new StrObj("5")}; - StrObj[] second = new StrObj[] {new StrObj("1"), new StrObj("2"), new StrObj("5")}; + new AssertBuilder() + .withDefaultParams() + .withArrays( + new StrObj[] {new StrObj("1"), new StrObj("Unp\rintable"), new StrObj("5")}, + new StrObj[] {new StrObj("1"), new StrObj("2"), new StrObj("5")}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[1, Unp\\rintable, 5]", + "[1, 2, 5]", + " ^^^^^^^^^^^^^") + .assertTwoWay(); + } + + @Test + public void testNullElements() { + new AssertBuilder() + .withDefaultParams() + .withArrays( + new String[] {"Anna", null, "Bill", "Julia"}, + new String[] {"Anna", "null", "William", "Julia"}) + .thatResultIs(false) + .thatFormattedValuesAre( + 1, + "[\"Anna\", null, \"Bill\", \"Julia\"]", + "[\"Anna\", \"null\", \"William\", \"Julia\"]", + " ^^^^^^^") + .assertTwoWay(); + } + + @Test (expectedExceptions = IllegalArgumentException.class) + public void testFirstArrayIsNull() { + var diff = ArrayDiff.of(null, new String[] {"a", "b"}); + } + + @Test (expectedExceptions = IllegalArgumentException.class) + public void testSecondArrayIsNull() { + var diff = ArrayDiff.of(null, new String[] {"a", "b"}); + } + + class AssertBuilder { + private boolean defaultParameters; + private int width; + private int contextBefore; + private Object firstArray; + private Object secondArray; + private boolean expectedResult; + private int expectedIndex; + private String firstFormattedArray; + private String secondFormattedArray; + private String failureMark; + + public AssertBuilder withDefaultParams() { + defaultParameters = true; + return this; + } - ArrayDiff diff = ArrayDiff.of(first, second); - String expected = String.format( - "Arrays differ starting from [index: 1]:%n" + - "[1, Unp\\rintable, 5]%n" + - "[1, 2, 5]%n" + - " ^^^^^^^^^^^^^"); + public AssertBuilder withParams(int width, int contextBefore) { + defaultParameters = false; + this.width = width; + this.contextBefore = contextBefore; + return this; + } + + public AssertBuilder withArrays(Object first, Object second) { + firstArray = first; + secondArray = second; + return this; + } + + public AssertBuilder thatResultIs(boolean result) { + expectedResult = result; + return this; + } + + public AssertBuilder thatFormattedValuesAre( + int idx, String first, String second, String mark) { + expectedIndex = idx; + firstFormattedArray = first; + secondFormattedArray = second; + failureMark = mark; + return this; + } + + public void assertTwoWay() { + ArrayDiff diff; + + // Direct + if (defaultParameters) { + diff = ArrayDiff.of(firstArray, secondArray); + } else { + diff = ArrayDiff.of(firstArray, secondArray, width, contextBefore); + } + + if (expectedResult == true) { + assertTrue(diff.areEqual()); + } else { + String expected = String.format( + "Arrays differ starting from [index: %d]:%n" + + "%s%n" + "%s%n" + "%s", + expectedIndex, firstFormattedArray, secondFormattedArray, failureMark); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + + // Reversed + if (defaultParameters) { + diff = ArrayDiff.of(secondArray, firstArray); + } else { + diff = ArrayDiff.of(secondArray, firstArray, width, contextBefore); + } + + if (expectedResult == true) { + assertTrue(diff.areEqual()); + } else { + String expected = String.format( + "Arrays differ starting from [index: %d]:%n" + + "%s%n" + "%s%n" + "%s", + expectedIndex, secondFormattedArray, firstFormattedArray, failureMark); + + assertFalse(diff.areEqual()); + assertEquals(diff.format(), expected); + } + } - assertFalse(diff.areEqual()); - assertEquals(diff.format(), expected); } } diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java index 47b393ccfb80f..b23ba9403908c 100644 --- a/test/lib/jdk/test/lib/format/ArrayCodec.java +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -304,13 +304,25 @@ public int getElementLength() { * @return the first mismatched element's index */ public int findMismatchIndex(ArrayCodec another) { - int result = 0; - while ((source.size() > result) && - (another.source.size() > result) && - (source.get(result).equals(another.source.get(result)))) { - result += 1; + for (int result = 0; (source.size() > result) && (another.source.size() > result); result++) { + Object first = source.get(result); + Object second = another.source.get(result); + + if (first == null || second == null) { + if (first == null && second == null) { + continue; // Both elements are null (i.e. equal) + } else { + return result; // Only one element is null, here's the failure index + } + } + + if (!first.equals(second)) { + return result; + } + } - return result; + + return 0; } /** diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java index 26fd165abf3e9..a0975c7d4c84b 100644 --- a/test/lib/jdk/test/lib/format/ArrayDiff.java +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -108,6 +108,14 @@ public static ArrayDiff of(Object first, Object second) { * @return an ArrayDiff instance for the two arrays and formatting parameters provided */ public static ArrayDiff of(Object first, Object second, int width, int contextBefore) { + if (first == null) { + throw new IllegalArgumentException("First array argument for ArrayDiff is null"); + } + + if (second == null) { + throw new IllegalArgumentException("Second array argument for ArrayDiff is null"); + } + boolean bothAreArrays = first.getClass().isArray() && second.getClass().isArray(); boolean componentTypesAreSame = first.getClass().getComponentType() == second.getClass().getComponentType(); @@ -177,7 +185,8 @@ private Optional format(boolean bounded) { if (idx < failureIdx) { failureMark.append(Format.paddingForWidth(first.getElementLength())); } else if (idx == failureIdx) { - failureMark.append(failureMarkForWidth(first.getElementLength()-1)); + int markLength = Math.max(first.getElementLength(), second.getElementLength()) - 1; + failureMark.append(failureMarkForWidth(markLength)); } } From ba6e5411b4de5cf8b9230b7cdb7a518900eadc4b Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Fri, 9 Oct 2020 09:06:42 +0200 Subject: [PATCH 12/14] Throw NPE instead of IAE when a null source array is provided --- test/lib-test/jdk/test/lib/format/ArrayDiffTest.java | 4 ++-- test/lib/jdk/test/lib/format/ArrayDiff.java | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java index 18bf3adc6002a..7b7dfc086f18b 100644 --- a/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java +++ b/test/lib-test/jdk/test/lib/format/ArrayDiffTest.java @@ -308,12 +308,12 @@ public void testNullElements() { .assertTwoWay(); } - @Test (expectedExceptions = IllegalArgumentException.class) + @Test (expectedExceptions = NullPointerException.class) public void testFirstArrayIsNull() { var diff = ArrayDiff.of(null, new String[] {"a", "b"}); } - @Test (expectedExceptions = IllegalArgumentException.class) + @Test (expectedExceptions = NullPointerException.class) public void testSecondArrayIsNull() { var diff = ArrayDiff.of(null, new String[] {"a", "b"}); } diff --git a/test/lib/jdk/test/lib/format/ArrayDiff.java b/test/lib/jdk/test/lib/format/ArrayDiff.java index a0975c7d4c84b..6755a693b7c83 100644 --- a/test/lib/jdk/test/lib/format/ArrayDiff.java +++ b/test/lib/jdk/test/lib/format/ArrayDiff.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; /** @@ -105,16 +106,12 @@ public static ArrayDiff of(Object first, Object second) { * @param width the maximum allowed width in characters for the formatting * @param contextBefore maximum number of elements to print before those that differ * @throws IllegalArgumentException if component types of arrays is not supported or are not the same + * @throws NullPointerException if at least one of the arrays is null * @return an ArrayDiff instance for the two arrays and formatting parameters provided */ public static ArrayDiff of(Object first, Object second, int width, int contextBefore) { - if (first == null) { - throw new IllegalArgumentException("First array argument for ArrayDiff is null"); - } - - if (second == null) { - throw new IllegalArgumentException("Second array argument for ArrayDiff is null"); - } + Objects.requireNonNull(first); + Objects.requireNonNull(second); boolean bothAreArrays = first.getClass().isArray() && second.getClass().isArray(); boolean componentTypesAreSame = From 78ed79f38262fec1df3ffb7ba407dc792666d1fd Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Fri, 9 Oct 2020 09:19:41 +0200 Subject: [PATCH 13/14] Disable instantiation of format.Diff.Defaults --- test/lib/jdk/test/lib/format/Diff.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/lib/jdk/test/lib/format/Diff.java b/test/lib/jdk/test/lib/format/Diff.java index 85ad8839be984..02ed3430e5d4c 100644 --- a/test/lib/jdk/test/lib/format/Diff.java +++ b/test/lib/jdk/test/lib/format/Diff.java @@ -35,6 +35,7 @@ public interface Diff { * Default limits for formatters */ public static class Defaults { + private Defaults() { } // This class should not be instantiated public final static int WIDTH = 80; public final static int CONTEXT_BEFORE = 2; } From 174148ab41e1c03d46dbde8862865dbaf3138843 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Fri, 9 Oct 2020 09:43:27 +0200 Subject: [PATCH 14/14] Fix ArrayCodec's behaviour for preffix and identical arrays --- test/lib/jdk/test/lib/format/ArrayCodec.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/lib/jdk/test/lib/format/ArrayCodec.java b/test/lib/jdk/test/lib/format/ArrayCodec.java index b23ba9403908c..0e34e7cd81122 100644 --- a/test/lib/jdk/test/lib/format/ArrayCodec.java +++ b/test/lib/jdk/test/lib/format/ArrayCodec.java @@ -301,10 +301,11 @@ public int getElementLength() { * Finds and returns the first mismatch index in another codec * * @param another a codec mismatch with whom is to be found - * @return the first mismatched element's index + * @return the first mismatched element's index or -1 if arrays are identical */ public int findMismatchIndex(ArrayCodec another) { - for (int result = 0; (source.size() > result) && (another.source.size() > result); result++) { + int result = 0; + while ((source.size() > result) && (another.source.size() > result)) { Object first = source.get(result); Object second = another.source.get(result); @@ -320,9 +321,12 @@ public int findMismatchIndex(ArrayCodec another) { return result; } + result++; } - return 0; + return source.size() != another.source.size() + ? result // Lengths are different, but the shorter arrays is a preffix to the longer array. + : -1; // Arrays are identical, there's no mismatch index } /**