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;
18  
19  import java.util.Spliterator;
20  import java.util.concurrent.ThreadLocalRandom;
21  import java.util.stream.DoubleStream;
22  import java.util.stream.IntStream;
23  import java.util.stream.LongStream;
24  import java.util.stream.Stream;
25  import org.junit.jupiter.api.Assertions;
26  import org.junit.jupiter.api.Test;
27  import org.junit.jupiter.params.ParameterizedTest;
28  import org.junit.jupiter.params.provider.Arguments;
29  import org.junit.jupiter.params.provider.MethodSource;
30  import org.junit.jupiter.params.provider.ValueSource;
31  
32  /**
33   * Tests for default stream method implementations in {@link UniformRandomProvider} and derived
34   * interfaces.
35   *
36   * <p>This class exists to test that {@link UniformRandomProvider} and any derived interface that
37   * overloads the base implementation function identically for the stream based methods. Stream
38   * methods are asserted to call the corresponding single value generation method in the interface.
39   */
40  abstract class BaseRandomProviderStreamTest {
41      private static final long STREAM_SIZE_ONE = 1;
42  
43      static Stream<Arguments> invalidNextIntOriginBound() {
44          return UniformRandomProviderTest.invalidNextIntOriginBound();
45      }
46  
47      static Stream<Arguments> invalidNextLongOriginBound() {
48          return UniformRandomProviderTest.invalidNextLongOriginBound();
49      }
50  
51      static Stream<Arguments> invalidNextDoubleOriginBound() {
52          return UniformRandomProviderTest.invalidNextDoubleOriginBound();
53      }
54  
55      static long[] streamSizes() {
56          return new long[] {0, 1, 13};
57      }
58  
59      /**
60       * Creates the provider used to test the stream methods.
61       * The instance will be used to verify the following conditions:
62       * <ul>
63       * <li>Invalid stream sizes
64       * <li>Unspecified stream size has an iterator that initially reports Long.MAX_VALUE
65       * <li>Invalid bounds for the bounded stream methods
66       * </ul>
67       *
68       * @return the uniform random provider
69       */
70      abstract UniformRandomProvider create();
71  
72      /**
73       * Creates the provider using the specified {@code values} for the
74       * {@link UniformRandomProvider#nextInt()} method. All other primitive
75       * generation methods should raise an exception to ensure the
76       * {@link UniformRandomProvider#ints()} method calls the correct generation
77       * method.
78       *
79       * @param values Values to return from the generation method.
80       * @return the uniform random provider
81       */
82      abstract UniformRandomProvider createInts(int[] values);
83  
84      /**
85       * Creates the provider using the specified {@code values} for the
86       * {@link UniformRandomProvider#nextInt(int, int)} method. All other primitive
87       * generation methods should raise an exception to ensure the
88       * {@link UniformRandomProvider#ints(int, int)} method calls the correct
89       * generation method.
90       *
91       * @param values Values to return from the generation method.
92       * @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
93       * @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
94       * @return the uniform random provider
95       */
96      abstract UniformRandomProvider createInts(int[] values, int origin, int bound);
97  
98      /**
99       * Creates the provider using the specified {@code values} for the
100      * {@link UniformRandomProvider#nextLong()} method.
101      * All other primitive generation methods should raise an exception to
102      * ensure the {@link UniformRandomProvider#longs()} method calls the correct
103      * generation method.
104      *
105      * @param values Values to return from the generation method.
106      * @return the uniform random provider
107      */
108     abstract UniformRandomProvider createLongs(long[] values);
109 
110     /**
111      * Creates the provider using the specified {@code values} for the
112      * {@link UniformRandomProvider#nextLong(long, long)} method.
113      * All other primitive generation methods should raise an exception to
114      * ensure the {@link UniformRandomProvider#longs(long, long)} method calls the correct
115      * generation method.
116      *
117      * @param values Values to return from the generation method.
118      * @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
119      * @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
120      * @return the uniform random provider
121      */
122     abstract UniformRandomProvider createLongs(long[] values, long origin, long bound);
123 
124     /**
125      * Creates the provider using the specified {@code values} for the
126      * {@link UniformRandomProvider#nextDouble()} method.
127      * All other primitive generation methods should raise an exception to
128      * ensure the {@link UniformRandomProvider#doubles()} method calls the correct
129      * generation method.
130      *
131      * @param values Values to return from the generation method.
132      * @return the uniform random provider
133      */
134     abstract UniformRandomProvider createDoubles(double[] values);
135 
136     /**
137      * Creates the provider using the specified {@code values} for the
138      * {@link UniformRandomProvider#nextDouble(double, double)} method.
139      * All other primitive generation methods should raise an exception to
140      * ensure the {@link UniformRandomProvider#doubles(double, double)} method calls the correct
141      * generation method.
142      *
143      * @param values Values to return from the generation method.
144      * @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
145      * @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
146      * @return the uniform random provider
147      */
148     abstract UniformRandomProvider createDoubles(double[] values, double origin, double bound);
149 
150     /**
151      * Gets the expected stream characteristics for the initial stream created with unlimited size.
152      *
153      * @return the characteristics
154      */
155     abstract int getCharacteristics();
156 
157     @ParameterizedTest
158     @ValueSource(longs = {-1, -2, Long.MIN_VALUE})
159     void testInvalidStreamSizeThrows(long size) {
160         final UniformRandomProvider rng = create();
161         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(size), "ints()");
162         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(size, 1, 42), "ints(lower, upper)");
163         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(size), "longs()");
164         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(size, 3L, 33L), "longs(lower, upper)");
165         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(size), "doubles()");
166         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(size, 1.5, 2.75), "doubles(lower, upper)");
167     }
168 
169     @Test
170     void testUnlimitedStreamSize() {
171         final UniformRandomProvider rng = create();
172         assertUnlimitedSpliterator(rng.ints().spliterator(), "ints()");
173         assertUnlimitedSpliterator(rng.ints(1, 42).spliterator(), "ints(lower, upper)");
174         assertUnlimitedSpliterator(rng.longs().spliterator(), "longs()");
175         assertUnlimitedSpliterator(rng.longs(1627384682623L, 32676823622343L).spliterator(), "longs(lower, upper)");
176         assertUnlimitedSpliterator(rng.doubles().spliterator(), "doubles()");
177         assertUnlimitedSpliterator(rng.doubles(1.5, 2.75).spliterator(), "doubles(lower, upper)");
178     }
179 
180     /**
181      * Assert the spliterator has an unlimited expected size and the characteristics specified
182      * by {@link #getCharacteristics()}.
183      *
184      * @param spliterator Spliterator.
185      * @param msg Error message.
186      */
187     private void assertUnlimitedSpliterator(Spliterator<?> spliterator, String msg) {
188         assertSpliterator(spliterator, Long.MAX_VALUE, getCharacteristics(), msg);
189     }
190 
191     /**
192      * Assert the spliterator has the expected size and characteristics.
193      *
194      * @param spliterator Spliterator.
195      * @param expectedSize Expected size.
196      * @param characteristics Expected characteristics.
197      * @param msg Error message.
198      * @see Spliterator#hasCharacteristics(int)
199      */
200     static void assertSpliterator(Spliterator<?> spliterator, long expectedSize, int characteristics, String msg) {
201         Assertions.assertEquals(expectedSize, spliterator.estimateSize(), msg);
202         Assertions.assertTrue(spliterator.hasCharacteristics(characteristics),
203             () -> String.format("%s: characteristics = %s, expected %s", msg,
204                 Integer.toBinaryString(spliterator.characteristics()),
205                 Integer.toBinaryString(characteristics)
206             ));
207     }
208 
209     // Test stream methods throw immediately for invalid range arguments.
210 
211     @ParameterizedTest
212     @MethodSource(value = {"invalidNextIntOriginBound"})
213     void testIntsOriginBoundThrows(int origin, int bound) {
214         final UniformRandomProvider rng = create();
215         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(origin, bound));
216         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(STREAM_SIZE_ONE, origin, bound));
217     }
218 
219     @ParameterizedTest
220     @MethodSource(value = {"invalidNextLongOriginBound"})
221     void testLongsOriginBoundThrows(long origin, long bound) {
222         final UniformRandomProvider rng = create();
223         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(origin, bound));
224         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(STREAM_SIZE_ONE, origin, bound));
225     }
226 
227     @ParameterizedTest
228     @MethodSource(value = {"invalidNextDoubleOriginBound"})
229     void testDoublesOriginBoundThrows(double origin, double bound) {
230         final UniformRandomProvider rng = create();
231         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(origin, bound));
232         Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(STREAM_SIZE_ONE, origin, bound));
233     }
234 
235     // Test stream methods call the correct generation method in the UniformRandomProvider.
236     // If range arguments are supplied they are asserted to be passed through.
237     // Streams are asserted to be sequential.
238 
239     @ParameterizedTest
240     @MethodSource(value = {"streamSizes"})
241     void testInts(long streamSize) {
242         final int[] values = ThreadLocalRandom.current().ints(streamSize).toArray();
243         final UniformRandomProvider rng = createInts(values);
244         final IntStream stream = rng.ints();
245         Assertions.assertFalse(stream.isParallel());
246         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
247     }
248 
249     @ParameterizedTest
250     @MethodSource(value = {"streamSizes"})
251     void testIntsOriginBound(long streamSize) {
252         final int origin = 13;
253         final int bound = 42;
254         final int[] values = ThreadLocalRandom.current().ints(streamSize, origin, bound).toArray();
255         final UniformRandomProvider rng = createInts(values, origin, bound);
256         final IntStream stream = rng.ints(origin, bound);
257         Assertions.assertFalse(stream.isParallel());
258         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
259     }
260 
261     @ParameterizedTest
262     @MethodSource(value = {"streamSizes"})
263     void testIntsWithSize(long streamSize) {
264         final int[] values = ThreadLocalRandom.current().ints(streamSize).toArray();
265         final UniformRandomProvider rng = createInts(values);
266         final IntStream stream = rng.ints(streamSize);
267         Assertions.assertFalse(stream.isParallel());
268         Assertions.assertArrayEquals(values, stream.toArray());
269     }
270 
271     @ParameterizedTest
272     @MethodSource(value = {"streamSizes"})
273     void testIntsOriginBoundWithSize(long streamSize) {
274         final int origin = 13;
275         final int bound = 42;
276         final int[] values = ThreadLocalRandom.current().ints(streamSize, origin, bound).toArray();
277         final UniformRandomProvider rng = createInts(values, origin, bound);
278         final IntStream stream = rng.ints(streamSize, origin, bound);
279         Assertions.assertFalse(stream.isParallel());
280         Assertions.assertArrayEquals(values, stream.toArray());
281     }
282 
283     @ParameterizedTest
284     @MethodSource(value = {"streamSizes"})
285     void testLongs(long streamSize) {
286         final long[] values = ThreadLocalRandom.current().longs(streamSize).toArray();
287         final UniformRandomProvider rng = createLongs(values);
288         final LongStream stream = rng.longs();
289         Assertions.assertFalse(stream.isParallel());
290         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
291     }
292 
293     @ParameterizedTest
294     @MethodSource(value = {"streamSizes"})
295     void testLongsOriginBound(long streamSize) {
296         final long origin = 26278368423L;
297         final long bound = 422637723236L;
298         final long[] values = ThreadLocalRandom.current().longs(streamSize, origin, bound).toArray();
299         final UniformRandomProvider rng = createLongs(values, origin, bound);
300         final LongStream stream = rng.longs(origin, bound);
301         Assertions.assertFalse(stream.isParallel());
302         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
303     }
304 
305     @ParameterizedTest
306     @MethodSource(value = {"streamSizes"})
307     void testLongsWithSize(long streamSize) {
308         final long[] values = ThreadLocalRandom.current().longs(streamSize).toArray();
309         final UniformRandomProvider rng = createLongs(values);
310         final LongStream stream = rng.longs(streamSize);
311         Assertions.assertFalse(stream.isParallel());
312         Assertions.assertArrayEquals(values, stream.toArray());
313     }
314 
315     @ParameterizedTest
316     @MethodSource(value = {"streamSizes"})
317     void testLongsOriginBoundWithSize(long streamSize) {
318         final long origin = 26278368423L;
319         final long bound = 422637723236L;
320         final long[] values = ThreadLocalRandom.current().longs(streamSize, origin, bound).toArray();
321         final UniformRandomProvider rng = createLongs(values, origin, bound);
322         final LongStream stream = rng.longs(streamSize, origin, bound);
323         Assertions.assertFalse(stream.isParallel());
324         Assertions.assertArrayEquals(values, stream.toArray());
325     }
326 
327     @ParameterizedTest
328     @MethodSource(value = {"streamSizes"})
329     void testDoubles(long streamSize) {
330         final double[] values = ThreadLocalRandom.current().doubles(streamSize).toArray();
331         final UniformRandomProvider rng = createDoubles(values);
332         final DoubleStream stream = rng.doubles();
333         Assertions.assertFalse(stream.isParallel());
334         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
335     }
336 
337     @ParameterizedTest
338     @MethodSource(value = {"streamSizes"})
339     void testDoublesOriginBound(long streamSize) {
340         final double origin = 1.23;
341         final double bound = 4.56;
342         final double[] values = ThreadLocalRandom.current().doubles(streamSize, origin, bound).toArray();
343         final UniformRandomProvider rng = createDoubles(values, origin, bound);
344         final DoubleStream stream = rng.doubles(origin, bound);
345         Assertions.assertFalse(stream.isParallel());
346         Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
347     }
348 
349     @ParameterizedTest
350     @MethodSource(value = {"streamSizes"})
351     void testDoublesWithSize(long streamSize) {
352         final double[] values = ThreadLocalRandom.current().doubles(streamSize).toArray();
353         final UniformRandomProvider rng = createDoubles(values);
354         final DoubleStream stream = rng.doubles(streamSize);
355         Assertions.assertFalse(stream.isParallel());
356         Assertions.assertArrayEquals(values, stream.toArray());
357     }
358 
359     @ParameterizedTest
360     @MethodSource(value = {"streamSizes"})
361     void testDoublesOriginBoundWithSize(long streamSize) {
362         final double origin = 1.23;
363         final double bound = 4.56;
364         final double[] values = ThreadLocalRandom.current().doubles(streamSize, origin, bound).toArray();
365         final UniformRandomProvider rng = createDoubles(values, origin, bound);
366         final DoubleStream stream = rng.doubles(streamSize, origin, bound);
367         Assertions.assertFalse(stream.isParallel());
368         Assertions.assertArrayEquals(values, stream.toArray());
369     }
370 }