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.simple.internal;
18  
19  import org.junit.jupiter.api.Assertions;
20  import org.junit.jupiter.api.Assumptions;
21  import org.junit.jupiter.params.ParameterizedTest;
22  import org.junit.jupiter.params.provider.EnumSource;
23  import org.junit.jupiter.api.Test;
24  
25  import java.lang.reflect.Array;
26  import java.util.Arrays;
27  import java.util.Set;
28  import java.util.function.Supplier;
29  import java.util.stream.Collectors;
30  
31  /**
32   * Tests for the {@link NativeSeedType} seed conversions. This test
33   * ensures that a seed can be created or converted from any supported input seed to each
34   * supported native seed type.
35   */
36  class NativeSeedTypeParametricTest {
37      /** This is a list of the class types that are supported native seeds. */
38      private static final Object[] SUPPORTED_NATIVE_TYPES = {
39          Integer.class,
40          Long.class,
41          int[].class,
42          long[].class
43      };
44      /** Example supported seeds for conversion to a native seed type. */
45      private static final Object[] SUPPORTED_SEEDS = {
46          Integer.valueOf(1),
47          Long.valueOf(2),
48          new int[] {3, 4, 5},
49          new long[] {6, 7, 8},
50          new byte[] {9, 10, 11},
51      };
52      /** Example unsupported seeds for conversion to a native seed type. */
53      private static final Object[] UNSUPPORTED_SEEDS = {
54          null,
55          Double.valueOf(Math.PI),
56      };
57  
58      /**
59       * Check that there are enum values for all supported types.
60       * This ensures the test is maintained to correspond to the enum.
61       */
62      @Test
63      void testNativeSeedTypeEnum() {
64          Set<Class<?>> supported = Arrays.stream(SUPPORTED_NATIVE_TYPES)
65              .map(o -> (Class<?>) o)
66              .collect(Collectors.toSet());
67          Assertions.assertEquals(SUPPORTED_NATIVE_TYPES.length, supported.size(),
68              "Class type of supported seeds should be unique");
69  
70          final NativeSeedType[] values = NativeSeedType.values();
71          Assertions.assertEquals(SUPPORTED_NATIVE_TYPES.length, values.length,
72              "Incorrect number of enum values for the supported native types");
73  
74          // Remove each
75          Arrays.stream(values).map(NativeSeedType::getType).forEach(supported::remove);
76          Assertions.assertEquals(0, supported.size());
77      }
78  
79      /**
80       * Test the seed can be created as the correct type.
81       *
82       * @param nativeSeedType Native seed type.
83       */
84      @ParameterizedTest
85      @EnumSource
86      void testCreateSeed(NativeSeedType nativeSeedType) {
87          final int size = 3;
88          final Object seed = nativeSeedType.createSeed(size);
89          Assertions.assertNotNull(seed);
90          final Class<?> type = nativeSeedType.getType();
91          Assertions.assertEquals(type, seed.getClass(), "Seed was not the correct class");
92          if (type.isArray()) {
93              Assertions.assertEquals(size, Array.getLength(seed), "Seed was not created the correct length");
94          }
95      }
96  
97      /**
98       * Test the seed can be checked as non-zero in a sub-range. This uses a bad range to
99       * generate an expected exception. The non-zero requirement is not tested as random
100      * seeding is very unlikely to create even a single 32-bit integer that is zero and
101      * requires correction. This test at least verifies the (from, to) sub-range arguments
102      * are used.
103      *
104      * @param nativeSeedType Native seed type.
105      */
106     @ParameterizedTest
107     @EnumSource
108     void testCreateArraySeedWithNonZeroRangeThrows(NativeSeedType nativeSeedType) {
109         Assumptions.assumeTrue(nativeSeedType.getType().isArray(), "Not an array type");
110         // Arguments: (size, from, to)
111         // Since the seed is created randomly it is very likely non-zero.
112         // Use a sub-range that will throw on the first array access; otherwise
113         // if the array location is valid it will likely be non-zero and no further
114         // checks are made.
115         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> nativeSeedType.createSeed(0, 0, 1));
116         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> nativeSeedType.createSeed(1, -1, 1));
117         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> nativeSeedType.createSeed(1, 1, 2));
118         Assertions.assertThrows(IndexOutOfBoundsException.class, () -> nativeSeedType.createSeed(1, 2, 3));
119     }
120 
121     /**
122      * Test the seed can be created, converted to a byte[] and then back to the native type.
123      *
124      * @param nativeSeedType Native seed type.
125      */
126     @ParameterizedTest
127     @EnumSource
128     void testConvertSeedToBytes(NativeSeedType nativeSeedType) {
129         final int size = 3;
130         final Object seed = nativeSeedType.createSeed(size);
131         Assertions.assertNotNull(seed, "Null seed");
132 
133         final byte[] bytes = NativeSeedType.convertSeedToBytes(seed);
134         Assertions.assertNotNull(bytes, "Null byte[] seed");
135 
136         final Object seed2 = nativeSeedType.convertSeed(bytes, size);
137         if (nativeSeedType.getType().isArray()) {
138             // This handles nested primitive arrays
139             Assertions.assertArrayEquals(new Object[] {seed}, new Object[] {seed2},
140                 "byte[] seed was not converted back");
141         } else {
142             Assertions.assertEquals(seed, seed2, "byte[] seed was not converted back");
143         }
144     }
145 
146     /**
147      * Test the seed can be converted to the correct type from any of the supported input types.
148      *
149      * @param nativeSeedType The native seed type enum instance.
150      */
151     @ParameterizedTest
152     @EnumSource
153     void testConvertSupportedSeed(NativeSeedType nativeSeedType) {
154         // Size can be ignored during conversion and so it not asserted
155         final int size = 3;
156         for (final Object input : SUPPORTED_SEEDS) {
157             final Object seed = nativeSeedType.convertSeed(input, size);
158             final Supplier<String> msg = () -> input.getClass() + " input seed was not converted";
159             Assertions.assertNotNull(seed, msg);
160             Assertions.assertEquals(nativeSeedType.getType(), seed.getClass(), msg);
161         }
162     }
163 
164     /**
165      * Test unsupported input seed types are rejected.
166      *
167      * @param nativeSeedType The native seed type enum instance.
168      */
169     @ParameterizedTest
170     @EnumSource
171     void testCannotConvertUnsupportedSeed(NativeSeedType nativeSeedType) {
172         final int size = 3;
173         for (final Object input : UNSUPPORTED_SEEDS) {
174             Assertions.assertThrows(UnsupportedOperationException.class,
175                     () -> nativeSeedType.convertSeed(input, size));
176         }
177     }
178 }