8229186: Improve error messages for TestStringIntrinsics failures
Reviewed-by: iignatyev, lmesnik
This commit is contained in:
parent
6d2c1a66bb
commit
52e45a3677
@ -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 class TestStringIntrinsics {
|
||||
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 @@ public class TestStringIntrinsics {
|
||||
|
||||
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,18 +244,49 @@ public class TestStringIntrinsics {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'.
|
||||
*/
|
||||
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.");
|
||||
Object actualResult = m.invoke(null, args);
|
||||
if (!actualResult.equals(expectedResult)) {
|
||||
var nl = System.lineSeparator();
|
||||
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++) {
|
||||
msgBuilder.append(nl + " Arg" + i + ": " + Format.asLiteral(args[i]));
|
||||
}
|
||||
|
||||
final String message = msgBuilder.toString();
|
||||
System.err.println(message);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
409
test/lib-test/jdk/test/lib/format/ArrayDiffTest.java
Normal file
409
test/lib-test/jdk/test/lib/format/ArrayDiffTest.java
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* 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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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() {
|
||||
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
|
||||
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; }
|
||||
}
|
||||
|
||||
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 = NullPointerException.class)
|
||||
public void testFirstArrayIsNull() {
|
||||
var diff = ArrayDiff.of(null, new String[] {"a", "b"});
|
||||
}
|
||||
|
||||
@Test (expectedExceptions = NullPointerException.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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
340
test/lib/jdk/test/lib/format/ArrayCodec.java
Normal file
340
test/lib/jdk/test/lib/format/ArrayCodec.java
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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<E> {
|
||||
private static final String ELLIPSIS = "...";
|
||||
|
||||
private boolean exhausted;
|
||||
private StringBuilder encoded;
|
||||
|
||||
private List<E> source;
|
||||
private String element;
|
||||
|
||||
private boolean bounded = false;
|
||||
private int maxWidth;
|
||||
private int idx;
|
||||
|
||||
private ArrayCodec(List<E> 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<Character> of(char[] array) {
|
||||
var source = new ArrayList<Character>(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<Byte> of(byte[] array) {
|
||||
var source = new ArrayList<Byte>(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<Integer> of(int[] array) {
|
||||
var source = new ArrayList<Integer>(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<Long> of(long[] array) {
|
||||
var source = new ArrayList<Long>(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<String> of(String[] array) {
|
||||
var source = new ArrayList<String>(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<Object> of(Object[] array) {
|
||||
var source = new ArrayList<Object>(array.length);
|
||||
for (Object value: array) {
|
||||
source.add(value);
|
||||
}
|
||||
return new ArrayCodec<Object>(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((Object[])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.
|
||||
*
|
||||
* <p>Sample outputs:
|
||||
*
|
||||
* <pre>
|
||||
* [0, 1, 2, 3, 4]
|
||||
* ["one", "first", "tree"]
|
||||
* [object1, object2, object3]
|
||||
* [a, b, \n, \u0002/, c]
|
||||
* </pre>
|
||||
*
|
||||
* @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<E> 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 or -1 if arrays are identical
|
||||
*/
|
||||
public int findMismatchIndex(ArrayCodec<E> another) {
|
||||
int result = 0;
|
||||
while ((source.size() > result) && (another.source.size() > 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;
|
||||
}
|
||||
|
||||
result++;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<E> another) {
|
||||
return source.equals(another.source);
|
||||
}
|
||||
}
|
207
test/lib/jdk/test/lib/format/ArrayDiff.java
Normal file
207
test/lib/jdk/test/lib/format/ArrayDiff.java
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.Objects;
|
||||
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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>Output examples:
|
||||
*
|
||||
* <p> two int arrays: </p>
|
||||
* <pre>
|
||||
* Arrays differ starting from [index: 4]:
|
||||
* ... 3, 4, 5, 6, 7]
|
||||
* ... 3, 4, 225, 6, 7]
|
||||
* ^^^^
|
||||
* </pre>
|
||||
* <p> two String arrays: </p>
|
||||
* <pre>
|
||||
* Arrays differ starting from [index: 2]:
|
||||
* ["first", "second", "third", "u\nprintable"]
|
||||
* ["first", "second", "incorrect", "u\nprintable"]
|
||||
* ^^^^^^^^^^^^
|
||||
* </pre>
|
||||
* <p> two char arrays arrays: </p>
|
||||
* <pre>
|
||||
* Arrays differ starting from [index: 7]:
|
||||
* ... \u0001, \u0002, \u0007, a, b, \n, ...
|
||||
* ... \u0001, \u0002, }, a, b, \n, ...
|
||||
* ^^^^^^^
|
||||
* </pre>
|
||||
*/
|
||||
public class ArrayDiff<E> implements Diff {
|
||||
|
||||
private int failureIdx;
|
||||
private final int maxWidth;
|
||||
private final int contextBefore;
|
||||
|
||||
private final ArrayCodec<E> first;
|
||||
private final ArrayCodec<E> second;
|
||||
|
||||
private ArrayDiff(ArrayCodec<E> first, ArrayCodec<E> 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, Diff.Defaults.WIDTH, Diff.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
|
||||
* @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) {
|
||||
Objects.requireNonNull(first);
|
||||
Objects.requireNonNull(second);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
return new ArrayDiff(
|
||||
ArrayCodec.of(first),
|
||||
ArrayCodec.of(second),
|
||||
width, contextBefore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> 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) {
|
||||
int markLength = Math.max(first.getElementLength(), second.getElementLength()) - 1;
|
||||
failureMark.append(failureMarkForWidth(markLength));
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
58
test/lib/jdk/test/lib/format/Diff.java
Normal file
58
test/lib/jdk/test/lib/format/Diff.java
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 formattable difference between two or more objects
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
127
test/lib/jdk/test/lib/format/Format.java
Normal file
127
test/lib/jdk/test/lib/format/Format.java
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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 " ".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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user