| /* | |
| * 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. | |
| */ | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Test; | |
| import org.testng.SkipException; | |
| import java.io.CharArrayWriter; | |
| import java.io.IOException; | |
| import java.io.UncheckedIOException; | |
| import java.nio.CharBuffer; | |
| import java.util.Arrays; | |
| import java.util.HexFormat; | |
| import java.util.Locale; | |
| import static org.testng.Assert.assertEquals; | |
| import static org.testng.Assert.assertFalse; | |
| import static org.testng.Assert.assertSame; | |
| import static org.testng.Assert.assertThrows; | |
| import static org.testng.Assert.assertTrue; | |
| import static org.testng.Assert.expectThrows; | |
| /* | |
| * @test | |
| * @summary Check HexFormat formatting and parsing | |
| * @run testng/othervm HexFormatTest | |
| */ | |
| @Test | |
| public class HexFormatTest { | |
| static final Class<NullPointerException> NPE = NullPointerException.class; | |
| @DataProvider(name = "HexFormattersParsers") | |
| Object[][] hexFormattersParsers() { | |
| return new Object[][]{ | |
| {"", "", "", true, | |
| HexFormat.of().withUpperCase()}, | |
| {", ", "#", "L", false, | |
| HexFormat.ofDelimiter(", ").withPrefix("#").withSuffix("L")}, | |
| {"", "", "", false, | |
| HexFormat.of().withPrefix("").withSuffix("")}, | |
| {".", "", "", false, | |
| HexFormat.ofDelimiter(".").withPrefix("").withSuffix("")}, | |
| {", ", "0x", "", true, | |
| HexFormat.ofDelimiter(", ").withUpperCase().withPrefix("0x")}, | |
| {"\u0202", "\u0203", "\u0204", false, | |
| HexFormat.ofDelimiter("\u0202").withPrefix("\u0203").withSuffix("\u0204")}, | |
| {"\u0202", "", "", false, | |
| HexFormat.ofDelimiter("\u0202")}, | |
| }; | |
| } | |
| @DataProvider(name = "HexStringsThrowing") | |
| Object[][] HexStringsThrowing() { | |
| return new Object[][]{ | |
| {"0", ":", "", ""}, // wrong string length | |
| {"01:", ":", "", ""}, // wrong string length | |
| {"01:0", ":", "", ""}, // wrong string length | |
| {"0", ",", "", ""}, // wrong length and separator | |
| {"01:", ",", "", ""}, // wrong length and separator | |
| {"01:0", ",", "", ""}, // wrong length and separator | |
| {"01:00", ",", "", ""}, // wrong separator | |
| {"00]", ",", "[", "]"}, // missing prefix | |
| {"[00", ",", "[", "]"}, // missing suffix | |
| {"]", ",", "[", "]"}, // missing prefix | |
| {"[", ",", "[", "]"}, // missing suffix | |
| {"00", ",", "abc", ""}, // Prefix longer than string | |
| {"01", ",", "", "def"}, // Suffix longer than string | |
| {"abc00,", ",", "abc", ""}, // Prefix and delim but not another value | |
| {"01def,", ",", "", "def"}, // Suffix and delim but not another value | |
| }; | |
| } | |
| @DataProvider(name = "BadBytesThrowing") | |
| Object[][] badBytesThrowing() { | |
| return new Object[][]{ | |
| {new byte[1], 0, 2}, // bad toIndex | |
| {new byte[1], 1, 2}, // bad fromIndex + toIndex | |
| {new byte[1], -1, 2}, // bad fromIndex | |
| {new byte[1], -1, 1}, // bad fromIndex | |
| {new byte[1], 0, -1}, // bad toIndex | |
| {new byte[1], 1, -1}, // bad toIndex | |
| }; | |
| } | |
| @DataProvider(name = "BadParseHexThrowing") | |
| Object[][] badParseHexThrowing() { | |
| return new Object[][]{ | |
| {"a", 0, 2, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"b", 1, 2, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"a", -1, 2, IndexOutOfBoundsException.class}, // bad fromIndex | |
| {"b", -1, 1, IndexOutOfBoundsException.class}, // bad fromIndex | |
| {"a", 0, -1, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"b", 1, -1, IndexOutOfBoundsException.class}, // bad fromIndex + toIndex | |
| {"76543210", 0, 7, IllegalArgumentException.class}, // odd number of digits | |
| {"zz00", 0, 4, IllegalArgumentException.class}, // non-hex digits | |
| {"00zz", 0, 4, IllegalArgumentException.class}, // non-hex digits | |
| }; | |
| } | |
| @DataProvider(name = "BadFromHexDigitsThrowing") | |
| Object[][] badHexDigitsThrowing() { | |
| return new Object[][]{ | |
| {"a", 0, 2, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"b", 1, 2, IndexOutOfBoundsException.class}, // bad fromIndex + toIndex | |
| {"a", -1, 2, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"b", -1, 1, IndexOutOfBoundsException.class}, // bad fromIndex + toIndex | |
| {"a", 0, -1, IndexOutOfBoundsException.class}, // bad toIndex | |
| {"b", 1, -1, IndexOutOfBoundsException.class}, // bad fromIndex + toIndex | |
| }; | |
| } | |
| static byte[] genBytes(int origin, int len) { | |
| byte[] bytes = new byte[len]; | |
| for (int i = 0; i < len; i++) | |
| bytes[i] = (byte) (origin + i); | |
| return bytes; | |
| } | |
| @Test | |
| static void testToHex() { | |
| HexFormat hex = HexFormat.of(); | |
| for (int i = 0; i < 32; i++) { | |
| char c = hex.toLowHexDigit((byte)i); | |
| String expected = Integer.toHexString(i & 0xf); | |
| assertEquals(c, expected.charAt(0), "toHex formatting"); | |
| } | |
| } | |
| @Test | |
| static void testToHexDigits() { | |
| HexFormat hex = HexFormat.of(); | |
| for (int i = 0; i < 256; i++) { | |
| String actual = hex.toHexDigits((byte)i); | |
| int expected = HexFormat.fromHexDigits(actual); | |
| assertEquals(expected, i, "fromHexDigits"); | |
| assertEquals(actual.charAt(0), hex.toHighHexDigit((byte)i), | |
| "first char mismatch"); | |
| assertEquals(actual.charAt(1), hex.toLowHexDigit((byte)i), | |
| "second char mismatch"); | |
| } | |
| } | |
| @Test | |
| static void testIsHexDigit() { | |
| for (int i = 0; i < 0x3ff; i++) { | |
| boolean actual = HexFormat.isHexDigit(i); | |
| boolean expected = Character.digit(i, 16) >= 0; | |
| assertEquals(actual, expected, "isHexDigit: " + i); | |
| } | |
| } | |
| @Test | |
| static void testFromHexDigit() { | |
| String chars = "0123456789ABCDEF0123456789abcdef"; | |
| for (int i = 0; i < chars.length(); i++) { | |
| int v = HexFormat.fromHexDigit(chars.charAt(i)); | |
| assertEquals(v, i & 0xf, "fromHex decode"); | |
| } | |
| } | |
| @Test | |
| static void testFromHexInvalid() { | |
| for (int i = 0; i < 65536; i++) { | |
| char ch = (char)i; | |
| if (ch > 0xff || Character.digit(ch, 16) < 0) { | |
| assertFalse(HexFormat.isHexDigit(ch), "isHexDigit incorrect for '" + ch + "' = " + i); | |
| expectThrows(NumberFormatException.class, | |
| () -> HexFormat.fromHexDigit(ch)); | |
| } | |
| } | |
| } | |
| @Test | |
| static void testAppendHexByteWithStringBuilder() { | |
| HexFormat hex = HexFormat.of(); | |
| StringBuilder sb = new StringBuilder(); | |
| for (int i = 0; i < 256; i++) { | |
| sb.setLength(0); | |
| StringBuilder sb1 = hex.toHexDigits(sb, (byte)i); | |
| assertSame(sb1, sb, "toHexDigits returned different StringBuilder"); | |
| assertEquals(sb.length(), 2, "wrong length after append: " + i); | |
| assertEquals(sb.charAt(0), hex.toHighHexDigit((byte)i), "MSB converted wrong"); | |
| assertEquals(sb.charAt(1), hex.toLowHexDigit((byte)i), "LSB converted wrong"); | |
| assertEquals(HexFormat.fromHexDigits(sb), i, "hex.format(sb, byte) wrong"); | |
| } | |
| } | |
| @Test | |
| static void testAppendHexByteWithCharBuffer() { | |
| HexFormat hex = HexFormat.of(); | |
| CharBuffer cb = CharBuffer.allocate(256); | |
| for (int i = 1; i <= 128; i++) { | |
| CharBuffer cb1 = hex.toHexDigits(cb, (byte)i); | |
| assertTrue(cb1 == cb); | |
| assertEquals(cb.position(), i * 2); | |
| } | |
| assertEquals(cb.remaining(), 0); | |
| } | |
| @Test | |
| static void testAppendHexByteWithCharArrayWriter() { | |
| HexFormat hex = HexFormat.of(); | |
| CharArrayWriter caw = new CharArrayWriter(); | |
| for (int i = 1; i <= 128; i++) { | |
| CharArrayWriter caw1 = hex.toHexDigits(caw, (byte)i); | |
| assertTrue(caw1 == caw); | |
| assertEquals(caw.size(), i * 2); | |
| } | |
| } | |
| @Test | |
| static void testFromHexPairInvalid() { | |
| HexFormat hex = HexFormat.of(); | |
| // An assortment of invalid characters | |
| String chars = "-0--0-"; | |
| for (int i = 0; i < chars.length(); i += 2) { | |
| final int ndx = i; | |
| Throwable ex = expectThrows(NumberFormatException.class, | |
| () -> HexFormat.fromHexDigits(chars.subSequence(ndx, ndx+2))); | |
| System.out.println(ex); | |
| } | |
| } | |
| @Test(dataProvider = "HexStringsThrowing") | |
| static void testToBytesThrowing(String value, String sep, String prefix, String suffix) { | |
| HexFormat hex = HexFormat.ofDelimiter(sep).withPrefix(prefix).withSuffix(suffix); | |
| Throwable ex = expectThrows(IllegalArgumentException.class, | |
| () -> { | |
| byte[] v = hex.parseHex(value); | |
| System.out.println("str: " + value + ", actual: " + v + ", bytes: " + | |
| Arrays.toString(v)); | |
| }); | |
| System.out.println("ex: " + ex); | |
| } | |
| @Test | |
| static void testFactoryNPE() { | |
| assertThrows(NPE, () -> HexFormat.ofDelimiter(null)); | |
| assertThrows(NPE, () -> HexFormat.of().withDelimiter(null)); | |
| assertThrows(NPE, () -> HexFormat.of().withPrefix(null)); | |
| assertThrows(NPE, () -> HexFormat.of().withSuffix(null)); | |
| } | |
| @Test | |
| static void testFormatHexNPE() { | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(null)); | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(null, 0, 1)); | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(null, null)); | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(null, null, 0, 0)); | |
| StringBuilder sb = new StringBuilder(); | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(sb, null)); | |
| assertThrows(NPE, () -> HexFormat.of().formatHex(sb, null, 0, 1)); | |
| } | |
| @Test | |
| static void testParseHexNPE() { | |
| assertThrows(NPE, () -> HexFormat.of().parseHex(null)); | |
| assertThrows(NPE, () -> HexFormat.of().parseHex((String)null, 0, 0)); | |
| assertThrows(NPE, () -> HexFormat.of().parseHex((char[])null, 0, 0)); | |
| } | |
| @Test | |
| static void testFromHexNPE() { | |
| assertThrows(NPE, () -> HexFormat.fromHexDigits(null)); | |
| assertThrows(NPE, () -> HexFormat.fromHexDigits(null, 0, 0)); | |
| assertThrows(NPE, () -> HexFormat.fromHexDigitsToLong(null)); | |
| assertThrows(NPE, () -> HexFormat.fromHexDigitsToLong(null, 0, 0)); | |
| } | |
| @Test | |
| static void testToHexDigitsNPE() { | |
| assertThrows(NPE, () -> HexFormat.of().toHexDigits(null, (byte)0)); | |
| } | |
| @Test(dataProvider = "BadParseHexThrowing") | |
| static void badParseHex(String string, int offset, int length, | |
| Class<? extends Throwable> exClass) { | |
| assertThrows(exClass, | |
| () -> HexFormat.of().parseHex(string, offset, length)); | |
| char[] chars = string.toCharArray(); | |
| assertThrows(exClass, | |
| () -> HexFormat.of().parseHex(chars, offset, length)); | |
| } | |
| @Test(dataProvider = "BadFromHexDigitsThrowing") | |
| static void badFromHexDigits(String string, int fromIndex, int toIndex, | |
| Class<? extends Throwable> exClass) { | |
| assertThrows(exClass, | |
| () -> HexFormat.fromHexDigits(string, fromIndex, toIndex)); | |
| assertThrows(exClass, | |
| () -> HexFormat.fromHexDigitsToLong(string, fromIndex, toIndex)); | |
| } | |
| // Verify IAE for strings that are too long for the target primitive type | |
| // or the number of requested digits is too large. | |
| @Test | |
| static void wrongNumberDigits() { | |
| assertThrows(IllegalArgumentException.class, | |
| () -> HexFormat.fromHexDigits("9876543210")); | |
| assertThrows(IllegalArgumentException.class, | |
| () -> HexFormat.fromHexDigits("9876543210", 0, 9)); | |
| assertThrows(IllegalArgumentException.class, | |
| () -> HexFormat.fromHexDigitsToLong("98765432109876543210")); | |
| assertThrows(IllegalArgumentException.class, | |
| () -> HexFormat.fromHexDigitsToLong("98765432109876543210", 0, 17)); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatter(String delimiter, String prefix, String suffix, | |
| boolean uppercase, | |
| HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| String res = hex.formatHex(expected); | |
| assertTrue(res.startsWith(prefix), "Prefix not found"); | |
| assertTrue(res.endsWith(suffix), "Suffix not found"); | |
| int expectedLen = expected.length * (2 + prefix.length() + | |
| delimiter.length() + suffix.length()) - delimiter.length(); | |
| assertEquals(res.length(), expectedLen, "String length"); | |
| if (expected.length > 1) { | |
| // check prefix and suffix is present for each hex pair | |
| for (int i = 0; i < expected.length; i++) { | |
| int valueChars = prefix.length() + 2 + suffix.length(); | |
| int offset = i * (valueChars + delimiter.length()); | |
| String value = res.substring(offset, offset + valueChars); | |
| assertTrue(value.startsWith(prefix), "wrong prefix"); | |
| assertTrue(value.endsWith(suffix), "wrong suffix"); | |
| // Check case of digits | |
| String cc = value.substring(prefix.length(), prefix.length() + 2); | |
| assertEquals(cc, | |
| (uppercase) ? cc.toUpperCase(Locale.ROOT) : cc.toLowerCase(Locale.ROOT), | |
| "Case mismatch"); | |
| if (i < expected.length - 1 && !delimiter.isEmpty()) { | |
| // Check the delimiter is present for each pair except the last | |
| assertEquals(res.substring(offset + valueChars, | |
| offset + valueChars + delimiter.length()), delimiter); | |
| } | |
| } | |
| } | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexString(String unused1, String unused2, String unused3, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| String s = hex.formatHex(expected); | |
| System.out.println(" formatted: " + s); | |
| byte[] actual = hex.parseHex(s); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| int mismatch = Arrays.mismatch(expected, actual); | |
| assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testParseHexStringRange(String delimiter, String prefix, String suffix, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| String s = hex.formatHex(expected); | |
| // Parse values 2, 3, 4 from the generated string | |
| int low = 2; | |
| int high = 5; | |
| int stride = prefix.length() + 2 + suffix.length() + delimiter.length(); | |
| System.out.println(" formatted subrange: " + | |
| s.substring(low * stride, high * stride - delimiter.length())); | |
| byte[] actual = hex.parseHex(s, low * stride, | |
| high * stride - delimiter.length()); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| assertEquals(actual.length, (high - low), "array length"); | |
| int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low); | |
| assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testParseHexEmptyString(String delimiter, String prefix, String suffix, | |
| boolean unused4, HexFormat hex) { | |
| byte[] actual = hex.parseHex(""); | |
| assertEquals(actual.length, 0, "empty string parse"); | |
| actual = hex.parseHex("abc", 0, 0); | |
| assertEquals(actual.length, 0, "empty string range parse"); | |
| actual = hex.parseHex(new char[1], 0, 0); | |
| assertEquals(actual.length, 0, "empty char array subrange empty parse"); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexRangeString(String unused1, String unused2, String unused3, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| int low = 1; | |
| int high = expected.length - 2; | |
| String s = hex.formatHex(expected, low, high); | |
| System.out.println(" formatted: " + s); | |
| byte[] actual = hex.parseHex(s); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low); | |
| assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexAppendable(String unused1, String unused2, String unused3, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| StringBuilder sb = new StringBuilder(); | |
| StringBuilder s = hex.formatHex(sb, expected); | |
| assertEquals(s, sb, "formatHex returned unknown StringBuilder"); | |
| System.out.println(" formatted: " + s); | |
| byte[] actual = hex.parseHex(s.toString()); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| int mismatch = Arrays.mismatch(expected, actual); | |
| assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexRangeAppendable(String unused1, String unused2, String unused3, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| int low = 1; | |
| int high = expected.length - 2; | |
| StringBuilder sb = new StringBuilder(); | |
| StringBuilder s = hex.formatHex(sb, expected, low, high); | |
| assertEquals(s, sb, "formatHex returned unknown StringBuilder"); | |
| System.out.println(" formatted: " + s); | |
| byte[] actual = hex.parseHex(s.toString()); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| byte[] sub = Arrays.copyOfRange(expected, low, high); | |
| System.out.println("actual: " + Arrays.toString(actual)); | |
| System.out.println("sub : " + Arrays.toString(sub)); | |
| int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low); | |
| assertEquals(actual, sub, "format/parse cycle failed, mismatch: " + mismatch); | |
| assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexCharArray(String unused1, String unused2, String unused3, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| String s = hex.formatHex(expected); | |
| System.out.println(" formatted: " + s); | |
| char[] chars = s.toCharArray(); | |
| byte[] actual = hex.parseHex(chars, 0, chars.length); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| int mismatch = Arrays.mismatch(expected, actual); | |
| assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatHexCharArrayIndexed(String delimiter, String prefix, String suffix, | |
| boolean unused4, HexFormat hex) { | |
| byte[] expected = genBytes('A', 15); | |
| String s = hex.formatHex(expected); | |
| System.out.println(" formatted: " + s); | |
| // Parse values 2, 3, 4 from the generated string | |
| int low = 2; | |
| int high = 5; | |
| int stride = prefix.length() + 2 + suffix.length() + delimiter.length(); | |
| System.out.println(" formatted subrange: " + | |
| s.substring(low * stride, high * stride - delimiter.length())); | |
| char[] chars = s.toCharArray(); | |
| byte[] actual = hex.parseHex(chars, low * stride, | |
| high * stride - delimiter.length()); | |
| System.out.println(" parsed as: " + Arrays.toString(actual)); | |
| assertEquals(actual.length, (high - low), "array length"); | |
| int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low); | |
| assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatterToString(String delimiter, String prefix, String suffix, | |
| boolean uppercase, | |
| HexFormat hex) { | |
| String actual = String.format( | |
| "uppercase: %s, delimiter: \"%s\", prefix: \"%s\", suffix: \"%s\"", | |
| uppercase, escapeNL(delimiter), escapeNL(prefix), escapeNL(suffix)); | |
| System.out.println(" hex: " + actual); | |
| assertEquals(actual, hex.toString(), "Formatter toString mismatch"); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatterParameterMethods(String delimiter, String prefix, String suffix, | |
| boolean uppercase, | |
| HexFormat hex) { | |
| assertEquals(hex.delimiter(), delimiter); | |
| assertEquals(hex.prefix(), prefix); | |
| assertEquals(hex.suffix(), suffix); | |
| assertEquals(hex.isUpperCase(), uppercase); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testFormatterTestEquals(String delimiter, String prefix, String suffix, | |
| boolean uppercase, | |
| HexFormat expected) { | |
| HexFormat actual = HexFormat.of() | |
| .withDelimiter(delimiter) | |
| .withPrefix(prefix) | |
| .withSuffix(suffix); | |
| actual = uppercase ? actual.withUpperCase() : actual.withLowerCase(); | |
| assertEquals(actual.delimiter(), delimiter, "delimiter"); | |
| assertEquals(actual.prefix(), prefix, "prefix"); | |
| assertEquals(actual.suffix(), suffix, "suffix"); | |
| assertEquals(actual.isUpperCase(), uppercase, "uppercase"); | |
| assertTrue(actual.equals(expected), "equals method"); | |
| assertEquals(actual.hashCode(), expected.hashCode(), "hashCode"); | |
| assertTrue(actual.equals(actual)); // equals self | |
| assertFalse(actual.equals(null)); // never equals null | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testZeroLength(String delimiter, String prefix, String suffix, boolean uppercase, | |
| HexFormat hex) { | |
| // Test formatting of zero length byte arrays, should produce no output | |
| StringBuilder sb = new StringBuilder(); | |
| assertEquals(hex.formatHex(new byte[0]), "", "Zero length"); | |
| assertEquals(hex.formatHex(new byte[0], 0, 0), "", "Zero length"); | |
| hex.formatHex(sb, new byte[0]); | |
| assertEquals(sb.length(), 0, "length should not change"); | |
| hex.formatHex(sb, new byte[0], 0, 0); | |
| assertEquals(sb.length(), 0, "length should not change"); | |
| } | |
| private static String escapeNL(String string) { | |
| return string.replace("\n", "\\n") | |
| .replace("\r", "\\r"); | |
| } | |
| @Test | |
| static void testfromHexDigitsToInt() { | |
| HexFormat hex = HexFormat.of(); | |
| String allHex = "76543210"; | |
| final int orig = 0x76543210; | |
| for (int digits = 0; digits <= 8; digits++) { | |
| String s = hex.toHexDigits(orig, digits); | |
| long actual = HexFormat.fromHexDigits(s, 0, digits); | |
| System.out.printf(" digits: %2d, formatted: \"%s\", parsed as: 0x%08x%n", | |
| digits, s, actual); | |
| assertEquals(s, allHex.substring(8 - digits, 8)); | |
| long expected = (digits < 8) ? orig & ~(0xffffffff << (4 * digits)) : orig; | |
| assertEquals(actual, expected); | |
| } | |
| } | |
| @Test | |
| static void testfromHexDigitsToLong() { | |
| HexFormat hex = HexFormat.of(); | |
| String allHex = "fedcba9876543210"; | |
| final long orig = 0xfedcba9876543210L; | |
| for (int digits = 0; digits <= 16; digits++) { | |
| String s = hex.toHexDigits(orig, digits); | |
| long actual = HexFormat.fromHexDigitsToLong(s, 0, digits); | |
| System.out.printf(" digits: %2d, formatted: \"%s\", parsed as: 0x%016xL%n", | |
| digits, s, actual); | |
| assertEquals(s, allHex.substring(16 - digits, 16)); | |
| long expected = (digits < 16) ? orig & ~(0xffffffffffffffffL << (4 * digits)) : orig; | |
| assertEquals(actual, expected); | |
| } | |
| } | |
| @Test | |
| static void testToHexDigitsLong() { | |
| HexFormat hex = HexFormat.of(); | |
| String allHex = "fedcba9876543210"; | |
| final long expected = 0xfedcba9876543210L; | |
| String s = hex.toHexDigits(expected); | |
| long actual = HexFormat.fromHexDigitsToLong(s); | |
| System.out.printf(" formatted: \"%s\", parsed as: 0x%016xL%n", s, actual); | |
| assertEquals(s, allHex); | |
| assertEquals(actual, expected); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testIOException(String delimiter, String prefix, String suffix, boolean uppercase, | |
| HexFormat hex) { | |
| Appendable throwingAppendable = new ThrowingAppendable(); | |
| assertThrows(UncheckedIOException.class, | |
| () -> hex.formatHex(throwingAppendable, new byte[1])); | |
| assertThrows(UncheckedIOException.class, | |
| () -> hex.formatHex(throwingAppendable, new byte[1], 0, 1)); | |
| assertThrows(UncheckedIOException.class, | |
| () -> hex.toHexDigits(throwingAppendable, (byte)1)); | |
| } | |
| @Test(dataProvider="HexFormattersParsers") | |
| static void testOOME(String delimiter, String prefix, String suffix, boolean uppercase, | |
| HexFormat hex) { | |
| // compute the size of byte array that will exceed the buffer | |
| long valueChars = prefix.length() + 2 + suffix.length(); | |
| long stride = valueChars + delimiter.length(); | |
| long max = Integer.MAX_VALUE & 0xFFFFFFFFL; | |
| long len = max / stride; | |
| long remainder = max - ((len - 1) * stride); | |
| if (remainder > valueChars) { | |
| len++; | |
| remainder -= valueChars; | |
| } | |
| try { | |
| byte[] bytes = new byte[(int) len]; | |
| Throwable ex = expectThrows(OutOfMemoryError.class, | |
| () -> hex.formatHex(bytes)); | |
| System.out.println("ex: " + ex); | |
| } catch (OutOfMemoryError oome) { | |
| System.out.printf("OOME: total mem: %08x, free mem: %08x, max mem: %08x%n", | |
| Runtime.getRuntime().totalMemory(), | |
| Runtime.getRuntime().freeMemory(), | |
| Runtime.getRuntime().maxMemory()); | |
| throw new SkipException("Insufficient Memory to test OOME"); | |
| } | |
| } | |
| /** | |
| * Example code from the HexFormat javadoc. | |
| * Showing simple usage of the API using "assert" to express the correct results | |
| * when shown in the javadoc. | |
| * The additional TestNG asserts verify the correctness of the same code. | |
| */ | |
| @Test | |
| private static void samples() { | |
| { | |
| // Primitive formatting and parsing. | |
| HexFormat hex = HexFormat.of(); | |
| byte b = 127; | |
| String byteStr = hex.toHexDigits(b); | |
| System.out.println(" " + byteStr); | |
| byte byteVal = (byte) HexFormat.fromHexDigits(byteStr); | |
| assert(byteStr.equals("7f")); | |
| assert(b == byteVal); | |
| assertTrue(byteStr.equals("7f")); | |
| assertTrue(b == byteVal); | |
| char c = 'A'; | |
| String charStr = hex.toHexDigits(c); | |
| System.out.println(" " + charStr); | |
| int charVal = HexFormat.fromHexDigits(charStr); | |
| assert(c == charVal); | |
| assertTrue(c == charVal); | |
| int i = 12345; | |
| String intStr = hex.toHexDigits(i); | |
| System.out.println(" " + intStr); | |
| int intVal = HexFormat.fromHexDigits(intStr); | |
| assert(i == intVal); | |
| assertTrue(i == intVal); | |
| long l = Long.MAX_VALUE; | |
| String longStr = hex.toHexDigits(l, 16); | |
| long longVal = HexFormat.fromHexDigitsToLong(longStr, 0, 16); | |
| System.out.println(" " + longStr + ", " + longVal); | |
| assert(l == longVal); | |
| assertTrue(l == longVal); | |
| } | |
| { | |
| // RFC 4752 Fingerprint | |
| HexFormat formatFingerprint = HexFormat.ofDelimiter(":").withUpperCase(); | |
| byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127}; | |
| String str = formatFingerprint.formatHex(bytes); | |
| System.out.println(" Formatted: " + str); | |
| byte[] parsed = formatFingerprint.parseHex(str); | |
| System.out.println(" Parsed: " + Arrays.toString(parsed)); | |
| assert(Arrays.equals(bytes, parsed)); | |
| assertTrue(Arrays.equals(bytes, parsed)); | |
| } | |
| { | |
| // Comma separated formatting | |
| HexFormat commaFormat = HexFormat.ofDelimiter(","); | |
| byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127}; | |
| String str = commaFormat.formatHex(bytes); | |
| System.out.println(" Formatted: " + str); | |
| byte[] parsed = commaFormat.parseHex(str); | |
| System.out.println(" Parsed: " + Arrays.toString(parsed)); | |
| assert(Arrays.equals(bytes, parsed)); | |
| assertTrue(Arrays.equals(bytes, parsed)); | |
| } | |
| { | |
| // Text formatting | |
| HexFormat commaFormat = HexFormat.ofDelimiter(", ").withPrefix("#"); | |
| byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127}; | |
| String str = commaFormat.formatHex(bytes); | |
| System.out.println(" Formatted: " + str); | |
| byte[] parsed = commaFormat.parseHex(str); | |
| System.out.println(" Parsed: " + Arrays.toString(parsed)); | |
| assert(Arrays.equals(bytes, parsed)); | |
| assertTrue(Arrays.equals(bytes, parsed)); | |
| } | |
| } | |
| /** | |
| * A test implementation of Appendable that throws IOException on all methods. | |
| */ | |
| static class ThrowingAppendable implements Appendable { | |
| @Override | |
| public Appendable append(CharSequence csq) throws IOException { | |
| throw new IOException(".append(CharSequence) always throws"); | |
| } | |
| @Override | |
| public Appendable append(CharSequence csq, int start, int end) throws IOException { | |
| throw new IOException(".append(CharSequence, start, end) always throws"); | |
| } | |
| @Override | |
| public Appendable append(char c) throws IOException { | |
| throw new IOException(".append(char) always throws"); | |
| } | |
| } | |
| } |