• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 package test.java.util.HexFormat;
24 
25 import org.testng.annotations.DataProvider;
26 import org.testng.annotations.Test;
27 import org.testng.SkipException;
28 
29 import java.io.CharArrayWriter;
30 import java.io.IOException;
31 import java.io.UncheckedIOException;
32 import java.nio.CharBuffer;
33 import java.util.Arrays;
34 import java.util.HexFormat;
35 import java.util.Locale;
36 
37 import static org.testng.Assert.assertEquals;
38 import static org.testng.Assert.assertFalse;
39 import static org.testng.Assert.assertSame;
40 import static org.testng.Assert.assertThrows;
41 import static org.testng.Assert.assertTrue;
42 import static org.testng.Assert.expectThrows;
43 
44 /*
45  * @test
46  * @summary Check HexFormat formatting and parsing
47  * @run testng/othervm HexFormatTest
48  */
49 
50 @Test
51 public class HexFormatTest {
52     static final Class<NullPointerException> NPE = NullPointerException.class;
53 
54     @DataProvider(name = "HexFormattersParsers")
hexFormattersParsers()55     Object[][] hexFormattersParsers() {
56         return new Object[][]{
57                 {"", "", "", true,
58                         HexFormat.of().withUpperCase()},
59                 {", ", "#", "L", false,
60                         HexFormat.ofDelimiter(", ").withPrefix("#").withSuffix("L")},
61                 {"", "", "", false,
62                         HexFormat.of().withPrefix("").withSuffix("")},
63                 {".", "", "", false,
64                         HexFormat.ofDelimiter(".").withPrefix("").withSuffix("")},
65                 {", ", "0x", "", true,
66                         HexFormat.ofDelimiter(", ").withUpperCase().withPrefix("0x")},
67                 {"\u0202", "\u0203", "\u0204", false,
68                         HexFormat.ofDelimiter("\u0202").withPrefix("\u0203").withSuffix("\u0204")},
69                 {"\u0202", "", "", false,
70                         HexFormat.ofDelimiter("\u0202")},
71 
72         };
73     }
74 
75     @DataProvider(name = "HexStringsThrowing")
HexStringsThrowing()76     Object[][] HexStringsThrowing() {
77         return new Object[][]{
78                 {"0", ":", "", ""},         // wrong string length
79                 {"01:", ":", "", ""},       // wrong string length
80                 {"01:0", ":", "", ""},      // wrong string length
81                 {"0", ",", "", ""},         // wrong length and separator
82                 {"01:", ",", "", ""},       // wrong length and separator
83                 {"01:0", ",", "", ""},      // wrong length and separator
84                 {"01:00", ",", "", ""},     // wrong separator
85                 {"00]", ",", "[", "]"},     // missing prefix
86                 {"[00", ",", "[", "]"},     // missing suffix
87                 {"]", ",", "[", "]"},       // missing prefix
88                 {"[", ",", "[", "]"},       // missing suffix
89                 {"00", ",", "abc", ""},     // Prefix longer than string
90                 {"01", ",", "", "def"},     // Suffix longer than string
91                 {"abc00,", ",", "abc", ""},     // Prefix and delim but not another value
92                 {"01def,", ",", "", "def"},     // Suffix and delim but not another value
93         };
94     }
95 
96     @DataProvider(name = "BadBytesThrowing")
badBytesThrowing()97     Object[][] badBytesThrowing() {
98         return new Object[][]{
99                 {new byte[1], 0, 2},        // bad toIndex
100                 {new byte[1], 1, 2},        // bad fromIndex + toIndex
101                 {new byte[1], -1, 2},       // bad fromIndex
102                 {new byte[1], -1, 1},       // bad fromIndex
103                 {new byte[1], 0, -1},       // bad toIndex
104                 {new byte[1], 1, -1},       // bad toIndex
105         };
106     }
107 
108     @DataProvider(name = "BadParseHexThrowing")
badParseHexThrowing()109     Object[][] badParseHexThrowing() {
110         return new Object[][]{
111                 {"a", 0, 2, IndexOutOfBoundsException.class},        // bad toIndex
112                 {"b", 1, 2, IndexOutOfBoundsException.class},        // bad toIndex
113                 {"a", -1, 2, IndexOutOfBoundsException.class},       // bad fromIndex
114                 {"b", -1, 1, IndexOutOfBoundsException.class},       // bad fromIndex
115                 {"a", 0, -1, IndexOutOfBoundsException.class},       // bad toIndex
116                 {"b", 1, -1, IndexOutOfBoundsException.class},       // bad fromIndex + toIndex
117                 {"76543210", 0, 7, IllegalArgumentException.class},  // odd number of digits
118                 {"zz00", 0, 4, IllegalArgumentException.class},      // non-hex digits
119                 {"00zz", 0, 4, IllegalArgumentException.class},      // non-hex digits
120         };
121     }
122 
123     @DataProvider(name = "BadFromHexDigitsThrowing")
badHexDigitsThrowing()124     Object[][] badHexDigitsThrowing() {
125         return new Object[][]{
126                 {"a", 0, 2, IndexOutOfBoundsException.class},        // bad toIndex
127                 {"b", 1, 2, IndexOutOfBoundsException.class},        // bad fromIndex + toIndex
128                 {"a", -1, 2, IndexOutOfBoundsException.class},       // bad toIndex
129                 {"b", -1, 1, IndexOutOfBoundsException.class},       // bad fromIndex + toIndex
130                 {"a", 0, -1, IndexOutOfBoundsException.class},       // bad toIndex
131                 {"b", 1, -1, IndexOutOfBoundsException.class},       // bad fromIndex + toIndex
132         };
133     }
134 
genBytes(int origin, int len)135     static byte[] genBytes(int origin, int len) {
136         byte[] bytes = new byte[len];
137         for (int i = 0; i < len; i++)
138             bytes[i] = (byte) (origin + i);
139         return bytes;
140     }
141 
142     @Test
testToHex()143     static void testToHex() {
144         HexFormat hex = HexFormat.of();
145         for (int i = 0; i < 32; i++) {
146             char c = hex.toLowHexDigit((byte)i);
147             String expected = Integer.toHexString(i & 0xf);
148             assertEquals(c, expected.charAt(0), "toHex formatting");
149         }
150     }
151 
152     @Test
testToHexDigits()153     static void testToHexDigits() {
154         HexFormat hex = HexFormat.of();
155         for (int i = 0; i < 256; i++) {
156             String actual = hex.toHexDigits((byte)i);
157             int expected = HexFormat.fromHexDigits(actual);
158             assertEquals(expected, i, "fromHexDigits");
159             assertEquals(actual.charAt(0), hex.toHighHexDigit((byte)i),
160                     "first char mismatch");
161             assertEquals(actual.charAt(1), hex.toLowHexDigit((byte)i),
162                     "second char mismatch");
163         }
164     }
165 
166     @Test
testIsHexDigit()167     static void testIsHexDigit() {
168         for (int i = 0; i < 0x3ff; i++) {
169             boolean actual = HexFormat.isHexDigit(i);
170             boolean expected = Character.digit(i, 16) >= 0;
171             assertEquals(actual, expected, "isHexDigit: " + i);
172         }
173     }
174 
175     @Test
testFromHexDigit()176     static void testFromHexDigit() {
177         String chars = "0123456789ABCDEF0123456789abcdef";
178         for (int i = 0; i < chars.length(); i++) {
179             int v = HexFormat.fromHexDigit(chars.charAt(i));
180             assertEquals(v, i & 0xf, "fromHex decode");
181         }
182     }
183 
184     @Test
testFromHexInvalid()185     static void testFromHexInvalid() {
186         for (int i = 0; i < 65536; i++) {
187             char ch = (char)i;
188             if (ch > 0xff || Character.digit(ch, 16) < 0) {
189                 assertFalse(HexFormat.isHexDigit(ch), "isHexDigit incorrect for '" + ch + "'  = " + i);
190                 expectThrows(NumberFormatException.class,
191                         () -> HexFormat.fromHexDigit(ch));
192 
193             }
194         }
195     }
196 
197     @Test
testAppendHexByteWithStringBuilder()198     static void testAppendHexByteWithStringBuilder() {
199         HexFormat hex = HexFormat.of();
200         StringBuilder sb = new StringBuilder();
201         for (int i = 0; i < 256; i++) {
202             sb.setLength(0);
203             StringBuilder sb1 = hex.toHexDigits(sb, (byte)i);
204             assertSame(sb1, sb, "toHexDigits returned different StringBuilder");
205             assertEquals(sb.length(), 2, "wrong length after append: " + i);
206             assertEquals(sb.charAt(0), hex.toHighHexDigit((byte)i), "MSB converted wrong");
207             assertEquals(sb.charAt(1), hex.toLowHexDigit((byte)i), "LSB converted wrong");
208 
209             assertEquals(HexFormat.fromHexDigits(sb), i, "hex.format(sb, byte) wrong");
210         }
211     }
212 
213     @Test
testAppendHexByteWithCharBuffer()214     static void testAppendHexByteWithCharBuffer() {
215         HexFormat hex = HexFormat.of();
216         CharBuffer cb = CharBuffer.allocate(256);
217         for (int i = 1; i <= 128; i++) {
218             CharBuffer cb1 = hex.toHexDigits(cb, (byte)i);
219             assertTrue(cb1 == cb);
220             assertEquals(cb.position(), i * 2);
221         }
222         assertEquals(cb.remaining(), 0);
223     }
224 
225     @Test
testAppendHexByteWithCharArrayWriter()226     static void testAppendHexByteWithCharArrayWriter() {
227         HexFormat hex = HexFormat.of();
228         CharArrayWriter caw = new CharArrayWriter();
229         for (int i = 1; i <= 128; i++) {
230             CharArrayWriter caw1 = hex.toHexDigits(caw, (byte)i);
231             assertTrue(caw1 == caw);
232             assertEquals(caw.size(), i * 2);
233         }
234     }
235 
236     @Test
testFromHexPairInvalid()237     static void testFromHexPairInvalid() {
238         HexFormat hex = HexFormat.of();
239 
240         // An assortment of invalid characters
241         String chars = "-0--0-";
242         for (int i = 0; i < chars.length(); i += 2) {
243             final int ndx = i;
244             Throwable ex = expectThrows(NumberFormatException.class,
245                     () -> HexFormat.fromHexDigits(chars.subSequence(ndx, ndx+2)));
246             System.out.println(ex);
247         }
248     }
249 
250     @Test(dataProvider = "HexStringsThrowing")
testToBytesThrowing(String value, String sep, String prefix, String suffix)251     static void testToBytesThrowing(String value, String sep, String prefix, String suffix) {
252         HexFormat hex = HexFormat.ofDelimiter(sep).withPrefix(prefix).withSuffix(suffix);
253         Throwable ex = expectThrows(IllegalArgumentException.class,
254                 () -> {
255                     byte[] v = hex.parseHex(value);
256                     System.out.println("str: " + value + ", actual: " + v + ", bytes: " +
257                                     Arrays.toString(v));
258                 });
259         System.out.println("ex: " + ex);
260     }
261 
262     @Test
testFactoryNPE()263     static void testFactoryNPE() {
264         assertThrows(NPE, () -> HexFormat.ofDelimiter(null));
265         assertThrows(NPE, () -> HexFormat.of().withDelimiter(null));
266         assertThrows(NPE, () -> HexFormat.of().withPrefix(null));
267         assertThrows(NPE, () -> HexFormat.of().withSuffix(null));
268     }
269 
270     @Test
testFormatHexNPE()271     static void testFormatHexNPE() {
272         assertThrows(NPE, () -> HexFormat.of().formatHex(null));
273         assertThrows(NPE, () -> HexFormat.of().formatHex(null, 0, 1));
274         assertThrows(NPE, () -> HexFormat.of().formatHex(null, null));
275         assertThrows(NPE,  () -> HexFormat.of().formatHex(null, null, 0, 0));
276         StringBuilder sb = new StringBuilder();
277         assertThrows(NPE, () -> HexFormat.of().formatHex(sb, null));
278         assertThrows(NPE, () -> HexFormat.of().formatHex(sb, null, 0, 1));
279     }
280 
281     @Test
testParseHexNPE()282     static void testParseHexNPE() {
283         assertThrows(NPE, () -> HexFormat.of().parseHex(null));
284         assertThrows(NPE, () -> HexFormat.of().parseHex((String)null, 0, 0));
285         assertThrows(NPE, () -> HexFormat.of().parseHex((char[])null, 0, 0));
286     }
287 
288     @Test
testFromHexNPE()289     static void testFromHexNPE() {
290         assertThrows(NPE, () -> HexFormat.fromHexDigits(null));
291         assertThrows(NPE, () -> HexFormat.fromHexDigits(null, 0, 0));
292         assertThrows(NPE, () -> HexFormat.fromHexDigitsToLong(null));
293         assertThrows(NPE, () -> HexFormat.fromHexDigitsToLong(null, 0, 0));
294     }
295 
296     @Test
testToHexDigitsNPE()297     static void testToHexDigitsNPE() {
298         assertThrows(NPE, () -> HexFormat.of().toHexDigits(null, (byte)0));
299     }
300 
301     @Test(dataProvider = "BadParseHexThrowing")
badParseHex(String string, int offset, int length, Class<? extends Throwable> exClass)302     static void badParseHex(String string, int offset, int length,
303                             Class<? extends Throwable> exClass) {
304         assertThrows(exClass,
305                 () -> HexFormat.of().parseHex(string, offset, length));
306         char[] chars = string.toCharArray();
307         assertThrows(exClass,
308                 () -> HexFormat.of().parseHex(chars, offset, length));
309     }
310 
311     @Test(dataProvider = "BadFromHexDigitsThrowing")
badFromHexDigits(String string, int fromIndex, int toIndex, Class<? extends Throwable> exClass)312     static void badFromHexDigits(String string, int fromIndex, int toIndex,
313                            Class<? extends Throwable> exClass) {
314         assertThrows(exClass,
315                 () -> HexFormat.fromHexDigits(string, fromIndex, toIndex));
316         assertThrows(exClass,
317                 () -> HexFormat.fromHexDigitsToLong(string, fromIndex, toIndex));
318     }
319 
320     // Verify IAE for strings that are too long for the target primitive type
321     // or the number of requested digits is too large.
322     @Test
wrongNumberDigits()323     static void wrongNumberDigits() {
324         assertThrows(IllegalArgumentException.class,
325                 () -> HexFormat.fromHexDigits("9876543210"));
326         assertThrows(IllegalArgumentException.class,
327                 () -> HexFormat.fromHexDigits("9876543210", 0, 9));
328         assertThrows(IllegalArgumentException.class,
329                 () -> HexFormat.fromHexDigitsToLong("98765432109876543210"));
330         assertThrows(IllegalArgumentException.class,
331                 () -> HexFormat.fromHexDigitsToLong("98765432109876543210", 0, 17));
332     }
333 
334     @Test(dataProvider="HexFormattersParsers")
testFormatter(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)335     static void testFormatter(String delimiter, String prefix, String suffix,
336                                    boolean uppercase,
337                                    HexFormat hex) {
338         byte[] expected = genBytes('A', 15);
339         String res = hex.formatHex(expected);
340         assertTrue(res.startsWith(prefix), "Prefix not found");
341         assertTrue(res.endsWith(suffix), "Suffix not found");
342         int expectedLen = expected.length * (2 + prefix.length() +
343                 delimiter.length() + suffix.length()) - delimiter.length();
344         assertEquals(res.length(), expectedLen, "String length");
345 
346         if (expected.length > 1) {
347             // check prefix and suffix is present for each hex pair
348             for (int i = 0; i < expected.length; i++) {
349                 int valueChars = prefix.length() + 2 + suffix.length();
350                 int offset = i * (valueChars + delimiter.length());
351                 String value = res.substring(offset, offset + valueChars);
352                 assertTrue(value.startsWith(prefix), "wrong prefix");
353                 assertTrue(value.endsWith(suffix), "wrong suffix");
354 
355                 // Check case of digits
356                 String cc = value.substring(prefix.length(), prefix.length() + 2);
357                 assertEquals(cc,
358                         (uppercase) ? cc.toUpperCase(Locale.ROOT) : cc.toLowerCase(Locale.ROOT),
359                         "Case mismatch");
360                 if (i < expected.length - 1 && !delimiter.isEmpty()) {
361                     // Check the delimiter is present for each pair except the last
362                     assertEquals(res.substring(offset + valueChars,
363                             offset + valueChars + delimiter.length()), delimiter);
364                 }
365             }
366         }
367     }
368 
369     @Test(dataProvider="HexFormattersParsers")
testFormatHexString(String unused1, String unused2, String unused3, boolean unused4, HexFormat hex)370     static void testFormatHexString(String unused1, String unused2, String unused3,
371                                    boolean unused4, HexFormat hex) {
372         byte[] expected = genBytes('A', 15);
373         String s = hex.formatHex(expected);
374         System.out.println("    formatted: " + s);
375 
376         byte[] actual = hex.parseHex(s);
377         System.out.println("    parsed as: " + Arrays.toString(actual));
378         int mismatch = Arrays.mismatch(expected, actual);
379         assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch);
380     }
381 
382     @Test(dataProvider="HexFormattersParsers")
testParseHexStringRange(String delimiter, String prefix, String suffix, boolean unused4, HexFormat hex)383     static void testParseHexStringRange(String delimiter, String prefix, String suffix,
384                                    boolean unused4, HexFormat hex) {
385         byte[] expected = genBytes('A', 15);
386         String s = hex.formatHex(expected);
387 
388         // Parse values 2, 3, 4 from the generated string
389         int low = 2;
390         int high = 5;
391         int stride = prefix.length() + 2 + suffix.length() + delimiter.length();
392         System.out.println("    formatted subrange: " +
393                 s.substring(low * stride, high * stride - delimiter.length()));
394         byte[] actual = hex.parseHex(s, low * stride,
395                 high * stride - delimiter.length());
396         System.out.println("    parsed as: " + Arrays.toString(actual));
397 
398         assertEquals(actual.length, (high - low), "array length");
399         int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low);
400         assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch);
401     }
402 
403     @Test(dataProvider="HexFormattersParsers")
testParseHexEmptyString(String delimiter, String prefix, String suffix, boolean unused4, HexFormat hex)404     static void testParseHexEmptyString(String delimiter, String prefix, String suffix,
405                                         boolean unused4, HexFormat hex) {
406         byte[] actual = hex.parseHex("");
407         assertEquals(actual.length, 0, "empty string parse");
408         actual = hex.parseHex("abc", 0, 0);
409         assertEquals(actual.length, 0, "empty string range parse");
410         actual = hex.parseHex(new char[1], 0, 0);
411         assertEquals(actual.length, 0, "empty char array subrange empty parse");
412     }
413 
414         @Test(dataProvider="HexFormattersParsers")
testFormatHexRangeString(String unused1, String unused2, String unused3, boolean unused4, HexFormat hex)415     static void testFormatHexRangeString(String unused1, String unused2, String unused3,
416                                    boolean unused4, HexFormat hex) {
417         byte[] expected = genBytes('A', 15);
418         int low = 1;
419         int high = expected.length - 2;
420         String s = hex.formatHex(expected, low, high);
421         System.out.println("    formatted: " + s);
422 
423         byte[] actual = hex.parseHex(s);
424         System.out.println("    parsed as: " + Arrays.toString(actual));
425         int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low);
426         assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch);
427     }
428 
429     @Test(dataProvider="HexFormattersParsers")
testFormatHexAppendable(String unused1, String unused2, String unused3, boolean unused4, HexFormat hex)430     static void testFormatHexAppendable(String unused1, String unused2, String unused3,
431                                      boolean unused4, HexFormat hex) {
432         byte[] expected = genBytes('A', 15);
433         StringBuilder sb = new StringBuilder();
434         StringBuilder s = hex.formatHex(sb, expected);
435         assertEquals(s, sb, "formatHex returned unknown StringBuilder");
436         System.out.println("    formatted: " + s);
437 
438         byte[] actual = hex.parseHex(s.toString());
439         System.out.println("    parsed as: " + Arrays.toString(actual));
440         int mismatch = Arrays.mismatch(expected, actual);
441         assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch);
442     }
443 
444     @Test(dataProvider="HexFormattersParsers")
testFormatHexRangeAppendable(String unused1, String unused2, String unused3, boolean unused4, HexFormat hex)445     static void testFormatHexRangeAppendable(String unused1, String unused2, String unused3,
446                                      boolean unused4, HexFormat hex) {
447         byte[] expected = genBytes('A', 15);
448         int low = 1;
449         int high = expected.length - 2;
450         StringBuilder sb = new StringBuilder();
451         StringBuilder s = hex.formatHex(sb, expected, low, high);
452         assertEquals(s, sb, "formatHex returned unknown StringBuilder");
453         System.out.println("    formatted: " + s);
454 
455         byte[] actual = hex.parseHex(s.toString());
456         System.out.println("    parsed as: " + Arrays.toString(actual));
457         byte[] sub = Arrays.copyOfRange(expected, low, high);
458         System.out.println("actual: " + Arrays.toString(actual));
459         System.out.println("sub   : " + Arrays.toString(sub));
460         int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low);
461 
462         assertEquals(actual, sub, "format/parse cycle failed, mismatch: " + mismatch);
463         assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch);
464     }
465 
466     @Test(dataProvider="HexFormattersParsers")
testFormatHexCharArray(String unused1, String unused2, String unused3, boolean unused4, HexFormat hex)467     static void testFormatHexCharArray(String unused1, String unused2, String unused3,
468                                      boolean unused4, HexFormat hex) {
469         byte[] expected = genBytes('A', 15);
470         String s = hex.formatHex(expected);
471         System.out.println("    formatted: " + s);
472 
473         char[] chars = s.toCharArray();
474         byte[] actual = hex.parseHex(chars, 0, chars.length);
475         System.out.println("    parsed as: " + Arrays.toString(actual));
476         int mismatch = Arrays.mismatch(expected, actual);
477         assertEquals(actual, expected, "format/parse cycle failed, mismatch: " + mismatch);
478     }
479 
480     @Test(dataProvider="HexFormattersParsers")
testFormatHexCharArrayIndexed(String delimiter, String prefix, String suffix, boolean unused4, HexFormat hex)481     static void testFormatHexCharArrayIndexed(String delimiter, String prefix, String suffix,
482                                               boolean unused4, HexFormat hex) {
483         byte[] expected = genBytes('A', 15);
484         String s = hex.formatHex(expected);
485         System.out.println("    formatted: " + s);
486 
487 
488         // Parse values 2, 3, 4 from the generated string
489         int low = 2;
490         int high = 5;
491         int stride = prefix.length() + 2 + suffix.length() + delimiter.length();
492         System.out.println("    formatted subrange: " +
493                 s.substring(low * stride, high * stride - delimiter.length()));
494         char[] chars = s.toCharArray();
495         byte[] actual = hex.parseHex(chars, low * stride,
496                 high * stride - delimiter.length());
497         System.out.println("    parsed as: " + Arrays.toString(actual));
498 
499         assertEquals(actual.length, (high - low), "array length");
500         int mismatch = Arrays.mismatch(expected, low, high, actual, 0, high - low);
501         assertEquals(mismatch, -1, "format/parse cycle failed, mismatch: " + mismatch);
502     }
503 
504     @Test(dataProvider="HexFormattersParsers")
testFormatterToString(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)505     static void testFormatterToString(String delimiter, String prefix, String suffix,
506                                     boolean uppercase,
507                                     HexFormat hex) {
508         String actual = String.format(
509                 "uppercase: %s, delimiter: \"%s\", prefix: \"%s\", suffix: \"%s\"",
510                 uppercase, escapeNL(delimiter), escapeNL(prefix), escapeNL(suffix));
511         System.out.println("    hex: " + actual);
512         assertEquals(actual, hex.toString(), "Formatter toString mismatch");
513     }
514 
515     @Test(dataProvider="HexFormattersParsers")
testFormatterParameterMethods(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)516     static void testFormatterParameterMethods(String delimiter, String prefix, String suffix,
517                                     boolean uppercase,
518                                     HexFormat hex) {
519         assertEquals(hex.delimiter(), delimiter);
520         assertEquals(hex.prefix(), prefix);
521         assertEquals(hex.suffix(), suffix);
522         assertEquals(hex.isUpperCase(), uppercase);
523     }
524 
525     @Test(dataProvider="HexFormattersParsers")
testFormatterTestEquals(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat expected)526     static void testFormatterTestEquals(String delimiter, String prefix, String suffix,
527                                     boolean uppercase,
528                                     HexFormat expected) {
529         HexFormat actual = HexFormat.of()
530                 .withDelimiter(delimiter)
531                 .withPrefix(prefix)
532                 .withSuffix(suffix);
533         actual = uppercase ? actual.withUpperCase() : actual.withLowerCase();
534 
535         assertEquals(actual.delimiter(), delimiter, "delimiter");
536         assertEquals(actual.prefix(), prefix, "prefix");
537         assertEquals(actual.suffix(), suffix, "suffix");
538         assertEquals(actual.isUpperCase(), uppercase, "uppercase");
539         assertTrue(actual.equals(expected), "equals method");
540         assertEquals(actual.hashCode(), expected.hashCode(), "hashCode");
541 
542         assertTrue(actual.equals(actual));   // equals self
543         assertFalse(actual.equals(null));    // never equals null
544     }
545 
546     @Test(dataProvider="HexFormattersParsers")
testZeroLength(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)547     static void testZeroLength(String delimiter, String prefix, String suffix, boolean uppercase,
548                                 HexFormat hex) {
549         // Test formatting of zero length byte arrays, should produce no output
550         StringBuilder sb = new StringBuilder();
551         assertEquals(hex.formatHex(new byte[0]), "", "Zero length");
552         assertEquals(hex.formatHex(new byte[0], 0, 0), "", "Zero length");
553 
554         hex.formatHex(sb, new byte[0]);
555         assertEquals(sb.length(), 0, "length should not change");
556         hex.formatHex(sb, new byte[0], 0, 0);
557         assertEquals(sb.length(), 0, "length should not change");
558 
559     }
escapeNL(String string)560     private static String escapeNL(String string) {
561         return string.replace("\n", "\\n")
562                 .replace("\r", "\\r");
563     }
564 
565     @Test
testfromHexDigitsToInt()566     static void testfromHexDigitsToInt() {
567         HexFormat hex = HexFormat.of();
568 
569         String allHex = "76543210";
570         final int orig = 0x76543210;
571         for (int digits = 0; digits <= 8; digits++) {
572             String s = hex.toHexDigits(orig, digits);
573             long actual = HexFormat.fromHexDigits(s, 0, digits);
574             System.out.printf("    digits: %2d, formatted: \"%s\", parsed as: 0x%08x%n",
575                     digits, s, actual);
576             assertEquals(s, allHex.substring(8 - digits, 8));
577             long expected = (digits < 8) ? orig & ~(0xffffffff << (4 * digits)) : orig;
578             assertEquals(actual, expected);
579         }
580     }
581 
582     @Test
testfromHexDigitsToLong()583     static void testfromHexDigitsToLong() {
584         HexFormat hex = HexFormat.of();
585 
586         String allHex = "fedcba9876543210";
587         final long orig = 0xfedcba9876543210L;
588         for (int digits = 0; digits <= 16; digits++) {
589             String s = hex.toHexDigits(orig, digits);
590             long actual = HexFormat.fromHexDigitsToLong(s, 0, digits);
591             System.out.printf("    digits: %2d, formatted: \"%s\", parsed as: 0x%016xL%n",
592                     digits, s, actual);
593             assertEquals(s, allHex.substring(16 - digits, 16));
594             long expected = (digits < 16) ? orig & ~(0xffffffffffffffffL << (4 * digits)) : orig;
595             assertEquals(actual, expected);
596         }
597     }
598 
599     @Test
testToHexDigitsLong()600     static void testToHexDigitsLong() {
601         HexFormat hex = HexFormat.of();
602 
603         String allHex = "fedcba9876543210";
604         final long expected = 0xfedcba9876543210L;
605         String s = hex.toHexDigits(expected);
606         long actual = HexFormat.fromHexDigitsToLong(s);
607         System.out.printf("    formatted: \"%s\", parsed as: 0x%016xL%n", s, actual);
608         assertEquals(s, allHex);
609         assertEquals(actual, expected);
610     }
611 
612     @Test(dataProvider="HexFormattersParsers")
testIOException(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)613     static void testIOException(String delimiter, String prefix, String suffix, boolean uppercase,
614                                HexFormat hex) {
615         Appendable throwingAppendable = new ThrowingAppendable();
616         assertThrows(UncheckedIOException.class,
617                 () -> hex.formatHex(throwingAppendable, new byte[1]));
618         assertThrows(UncheckedIOException.class,
619                 () -> hex.formatHex(throwingAppendable, new byte[1], 0, 1));
620         assertThrows(UncheckedIOException.class,
621                 () -> hex.toHexDigits(throwingAppendable, (byte)1));
622     }
623 
624     @Test(dataProvider="HexFormattersParsers")
testOOME(String delimiter, String prefix, String suffix, boolean uppercase, HexFormat hex)625     static void testOOME(String delimiter, String prefix, String suffix, boolean uppercase,
626                          HexFormat hex) {
627         // compute the size of byte array that will exceed the buffer
628         long valueChars = prefix.length() + 2 + suffix.length();
629         long stride = valueChars + delimiter.length();
630         long max = Integer.MAX_VALUE & 0xFFFFFFFFL;
631         long len = max / stride;
632         long remainder = max - ((len - 1) * stride);
633         if (remainder > valueChars) {
634             len++;
635             remainder -= valueChars;
636         }
637         try {
638             byte[] bytes = new byte[(int) len];
639             Throwable ex = expectThrows(OutOfMemoryError.class,
640                     () -> hex.formatHex(bytes));
641             System.out.println("ex: " + ex);
642         } catch (OutOfMemoryError oome) {
643             System.out.printf("OOME: total mem: %08x, free mem: %08x, max mem: %08x%n",
644                     Runtime.getRuntime().totalMemory(),
645                     Runtime.getRuntime().freeMemory(),
646                     Runtime.getRuntime().maxMemory());
647             throw new SkipException("Insufficient Memory to test OOME");
648         }
649 
650     }
651 
652     /**
653      * Example code from the HexFormat javadoc.
654      * Showing simple usage of the API using "assert" to express the correct results
655      * when shown in the javadoc.
656      * The additional TestNG asserts verify the correctness of the same code.
657      */
658     @Test
samples()659     private static void samples() {
660         {
661             // Primitive formatting and parsing.
662             HexFormat hex = HexFormat.of();
663 
664             byte b = 127;
665             String byteStr = hex.toHexDigits(b);
666             System.out.println("    " + byteStr);
667 
668             byte byteVal = (byte) HexFormat.fromHexDigits(byteStr);
669             assert(byteStr.equals("7f"));
670             assert(b == byteVal);
671             assertTrue(byteStr.equals("7f"));
672             assertTrue(b == byteVal);
673 
674 
675             char c = 'A';
676             String charStr = hex.toHexDigits(c);
677             System.out.println("    " + charStr);
678             int charVal = HexFormat.fromHexDigits(charStr);
679             assert(c == charVal);
680             assertTrue(c == charVal);
681 
682             int i = 12345;
683             String intStr = hex.toHexDigits(i);
684             System.out.println("    " + intStr);
685             int intVal = HexFormat.fromHexDigits(intStr);
686             assert(i == intVal);
687             assertTrue(i == intVal);
688 
689             long l = Long.MAX_VALUE;
690             String longStr = hex.toHexDigits(l, 16);
691             long longVal = HexFormat.fromHexDigitsToLong(longStr, 0, 16);
692             System.out.println("    " + longStr + ", " + longVal);
693             assert(l == longVal);
694             assertTrue(l == longVal);
695         }
696 
697         {
698             // RFC 4752 Fingerprint
699             HexFormat formatFingerprint = HexFormat.ofDelimiter(":").withUpperCase();
700             byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127};
701             String str = formatFingerprint.formatHex(bytes);
702             System.out.println("    Formatted: " + str);
703 
704             byte[] parsed = formatFingerprint.parseHex(str);
705             System.out.println("    Parsed: " + Arrays.toString(parsed));
706             assert(Arrays.equals(bytes, parsed));
707             assertTrue(Arrays.equals(bytes, parsed));
708         }
709 
710         {
711             // Comma separated formatting
712             HexFormat commaFormat = HexFormat.ofDelimiter(",");
713             byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127};
714             String str = commaFormat.formatHex(bytes);
715             System.out.println("    Formatted: " + str);
716 
717             byte[] parsed = commaFormat.parseHex(str);
718             System.out.println("    Parsed: " + Arrays.toString(parsed));
719             assert(Arrays.equals(bytes, parsed));
720             assertTrue(Arrays.equals(bytes, parsed));
721         }
722         {
723             // Text formatting
724             HexFormat commaFormat = HexFormat.ofDelimiter(", ").withPrefix("#");
725             byte[] bytes = {0, 1, 2, 3, 124, 125, 126, 127};
726             String str = commaFormat.formatHex(bytes);
727             System.out.println("    Formatted: " + str);
728 
729             byte[] parsed = commaFormat.parseHex(str);
730             System.out.println("    Parsed:    " + Arrays.toString(parsed));
731             assert(Arrays.equals(bytes, parsed));
732             assertTrue(Arrays.equals(bytes, parsed));
733         }
734     }
735 
736     /**
737      * A test implementation of Appendable that throws IOException on all methods.
738      */
739     static class ThrowingAppendable implements Appendable {
740         @Override
append(CharSequence csq)741         public Appendable append(CharSequence csq) throws IOException {
742             throw new IOException(".append(CharSequence) always throws");
743         }
744 
745         @Override
append(CharSequence csq, int start, int end)746         public Appendable append(CharSequence csq, int start, int end) throws IOException {
747             throw new IOException(".append(CharSequence, start, end) always throws");
748         }
749 
750         @Override
append(char c)751         public Appendable append(char c) throws IOException {
752             throw new IOException(".append(char) always throws");
753         }
754     }
755 }
756