View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.rng.examples.stress;
18  
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.Test;
21  
22  import java.util.Arrays;
23  import java.util.Locale;
24  import java.util.concurrent.ThreadLocalRandom;
25  
26  /**
27   * Tests for {@link Hex}.
28   */
29  class HexTest {
30      /** Upper-case hex digits. */
31      private static final char[] DIGITS = {
32          '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
33      };
34  
35      /**
36       * Test a round-trip hex encode and decode of a byte[] seed.
37       *
38       * <p>This test ensures that a generated byte[] seed recorded by the stress command
39       * in the results file can be used to create the same seed.
40       */
41      @Test
42      void testHexEncodeAndDecode() {
43          // Empty bytes
44          for (int size = 0; size < 10; size++) {
45              assertHexEncodeAndDecode(new byte[size]);
46          }
47          // Random bytes
48          for (int size : new int[] {3, 4, 5, 8, 16, 31}) {
49              final byte[] bytes = new byte[size];
50              for (int i = 0; i < 5; i++) {
51                  ThreadLocalRandom.current().nextBytes(bytes);
52                  assertHexEncodeAndDecode(bytes);
53              }
54          }
55      }
56  
57      /**
58       * Assert the bytes can be encoded and decoded.
59       *
60       * @param bytes the bytes
61       */
62      private static void assertHexEncodeAndDecode(byte[] bytes) {
63          // Use a StringBuilder to append the chars as this is the method used in the stress command.
64          final StringBuilder sb = new StringBuilder();
65          sb.append(Hex.encodeHex(bytes));
66          final byte[] decoded = Hex.decodeHex(sb);
67          Assertions.assertArrayEquals(bytes, decoded);
68      }
69  
70      /**
71       * Test a round-trip hex decode and encode of a char[] seed.
72       *
73       * <p>This test ensures that a user input random hex seed passed to the stress command
74       * can be converted to bytes and then recorded in the results file.
75       */
76      @Test
77      void testHexDecodeAndEncode() {
78          // Note: char[] must be an even length.
79          // Empty chars.
80          for (int size = 0; size < 10; size++) {
81              final char[] chars = new char[size * 2];
82              Arrays.fill(chars, '0');
83              assertHexDecodeAndEncode(chars);
84          }
85          // Random bytes
86          for (int size : new int[] {3, 4, 5, 8, 16, 31}) {
87              final char[] chars = new char[size * 2];
88              for (int i = 0; i < 5; i++) {
89                  // Encode upper case
90                  for (int j = 0; j < chars.length; j++) {
91                      chars[j] = DIGITS[ThreadLocalRandom.current().nextInt(16)];
92                  }
93                  assertHexDecodeAndEncode(chars);
94              }
95          }
96      }
97  
98      /**
99       * Assert the characters can be decoded and encoded.
100      *
101      * @param chars the chars
102      */
103     private static void assertHexDecodeAndEncode(char[] chars) {
104         final String text = String.valueOf(chars);
105         final byte[] decoded = Hex.decodeHex(text);
106         // Test the encoding is lower case
107         Assertions.assertArrayEquals(text.toLowerCase(Locale.US).toCharArray(), Hex.encodeHex(decoded));
108     }
109 
110     @Test
111     void testHexThrowsWithOddNumberOfCharacters() {
112         Assertions.assertThrows(IllegalArgumentException.class, () -> Hex.decodeHex("0"));
113     }
114 
115     @Test
116     void testHexThrowsWithIllegalHexCharacters() {
117         Assertions.assertThrows(IllegalArgumentException.class, () -> Hex.decodeHex("0g"));
118     }
119 }