blob: f243476bd325d62ee5fa2d1c3b88281cea2b9e7d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.rng;
import java.util.Spliterator;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
/**
* Tests for default stream method implementations in {@link UniformRandomProvider} and derived
* interfaces.
*
* <p>This class exists to test that {@link UniformRandomProvider} and any derived interface that
* overloads the base implementation function identically for the stream based methods. Stream
* methods are asserted to call the corresponding single value generation method in the interface.
*/
abstract class BaseRandomProviderStreamTest {
private static final long STREAM_SIZE_ONE = 1;
static Stream<Arguments> invalidNextIntOriginBound() {
return UniformRandomProviderTest.invalidNextIntOriginBound();
}
static Stream<Arguments> invalidNextLongOriginBound() {
return UniformRandomProviderTest.invalidNextLongOriginBound();
}
static Stream<Arguments> invalidNextDoubleOriginBound() {
return UniformRandomProviderTest.invalidNextDoubleOriginBound();
}
static long[] streamSizes() {
return new long[] {0, 1, 13};
}
/**
* Creates the provider used to test the stream methods.
* The instance will be used to verify the following conditions:
* <ul>
* <li>Invalid stream sizes
* <li>Unspecified stream size has an iterator that initially reports Long.MAX_VALUE
* <li>Invalid bounds for the bounded stream methods
* </ul>
*
* @return the uniform random provider
*/
abstract UniformRandomProvider create();
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextInt()} method. All other primitive
* generation methods should raise an exception to ensure the
* {@link UniformRandomProvider#ints()} method calls the correct generation
* method.
*
* @param values Values to return from the generation method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createInts(int[] values);
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextInt(int, int)} method. All other primitive
* generation methods should raise an exception to ensure the
* {@link UniformRandomProvider#ints(int, int)} method calls the correct
* generation method.
*
* @param values Values to return from the generation method.
* @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
* @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createInts(int[] values, int origin, int bound);
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextLong()} method.
* All other primitive generation methods should raise an exception to
* ensure the {@link UniformRandomProvider#longs()} method calls the correct
* generation method.
*
* @param values Values to return from the generation method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createLongs(long[] values);
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextLong(long, long)} method.
* All other primitive generation methods should raise an exception to
* ensure the {@link UniformRandomProvider#longs(long, long)} method calls the correct
* generation method.
*
* @param values Values to return from the generation method.
* @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
* @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createLongs(long[] values, long origin, long bound);
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextDouble()} method.
* All other primitive generation methods should raise an exception to
* ensure the {@link UniformRandomProvider#doubles()} method calls the correct
* generation method.
*
* @param values Values to return from the generation method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createDoubles(double[] values);
/**
* Creates the provider using the specified {@code values} for the
* {@link UniformRandomProvider#nextDouble(double, double)} method.
* All other primitive generation methods should raise an exception to
* ensure the {@link UniformRandomProvider#doubles(double, double)} method calls the correct
* generation method.
*
* @param values Values to return from the generation method.
* @param origin Origin for the generation method. Can be asserted to match the argument passed to the method.
* @param bound Bound for the generation method. Can be asserted to match the argument passed to the method.
* @return the uniform random provider
*/
abstract UniformRandomProvider createDoubles(double[] values, double origin, double bound);
/**
* Gets the expected stream characteristics for the initial stream created with unlimited size.
*
* @return the characteristics
*/
abstract int getCharacteristics();
@ParameterizedTest
@ValueSource(longs = {-1, -2, Long.MIN_VALUE})
void testInvalidStreamSizeThrows(long size) {
final UniformRandomProvider rng = create();
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(size), "ints()");
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(size, 1, 42), "ints(lower, upper)");
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(size), "longs()");
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(size, 3L, 33L), "longs(lower, upper)");
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(size), "doubles()");
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(size, 1.5, 2.75), "doubles(lower, upper)");
}
@Test
void testUnlimitedStreamSize() {
final UniformRandomProvider rng = create();
assertUnlimitedSpliterator(rng.ints().spliterator(), "ints()");
assertUnlimitedSpliterator(rng.ints(1, 42).spliterator(), "ints(lower, upper)");
assertUnlimitedSpliterator(rng.longs().spliterator(), "longs()");
assertUnlimitedSpliterator(rng.longs(1627384682623L, 32676823622343L).spliterator(), "longs(lower, upper)");
assertUnlimitedSpliterator(rng.doubles().spliterator(), "doubles()");
assertUnlimitedSpliterator(rng.doubles(1.5, 2.75).spliterator(), "doubles(lower, upper)");
}
/**
* Assert the spliterator has an unlimited expected size and the characteristics specified
* by {@link #getCharacteristics()}.
*
* @param spliterator Spliterator.
* @param msg Error message.
*/
private void assertUnlimitedSpliterator(Spliterator<?> spliterator, String msg) {
assertSpliterator(spliterator, Long.MAX_VALUE, getCharacteristics(), msg);
}
/**
* Assert the spliterator has the expected size and characteristics.
*
* @param spliterator Spliterator.
* @param expectedSize Expected size.
* @param characteristics Expected characteristics.
* @param msg Error message.
* @see Spliterator#hasCharacteristics(int)
*/
static void assertSpliterator(Spliterator<?> spliterator, long expectedSize, int characteristics, String msg) {
Assertions.assertEquals(expectedSize, spliterator.estimateSize(), msg);
Assertions.assertTrue(spliterator.hasCharacteristics(characteristics),
() -> String.format("%s: characteristics = %s, expected %s", msg,
Integer.toBinaryString(spliterator.characteristics()),
Integer.toBinaryString(characteristics)
));
}
// Test stream methods throw immediately for invalid range arguments.
@ParameterizedTest
@MethodSource(value = {"invalidNextIntOriginBound"})
void testIntsOriginBoundThrows(int origin, int bound) {
final UniformRandomProvider rng = create();
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(origin, bound));
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.ints(STREAM_SIZE_ONE, origin, bound));
}
@ParameterizedTest
@MethodSource(value = {"invalidNextLongOriginBound"})
void testLongsOriginBoundThrows(long origin, long bound) {
final UniformRandomProvider rng = create();
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(origin, bound));
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.longs(STREAM_SIZE_ONE, origin, bound));
}
@ParameterizedTest
@MethodSource(value = {"invalidNextDoubleOriginBound"})
void testDoublesOriginBoundThrows(double origin, double bound) {
final UniformRandomProvider rng = create();
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(origin, bound));
Assertions.assertThrows(IllegalArgumentException.class, () -> rng.doubles(STREAM_SIZE_ONE, origin, bound));
}
// Test stream methods call the correct generation method in the UniformRandomProvider.
// If range arguments are supplied they are asserted to be passed through.
// Streams are asserted to be sequential.
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testInts(long streamSize) {
final int[] values = ThreadLocalRandom.current().ints(streamSize).toArray();
final UniformRandomProvider rng = createInts(values);
final IntStream stream = rng.ints();
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testIntsOriginBound(long streamSize) {
final int origin = 13;
final int bound = 42;
final int[] values = ThreadLocalRandom.current().ints(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createInts(values, origin, bound);
final IntStream stream = rng.ints(origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testIntsWithSize(long streamSize) {
final int[] values = ThreadLocalRandom.current().ints(streamSize).toArray();
final UniformRandomProvider rng = createInts(values);
final IntStream stream = rng.ints(streamSize);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testIntsOriginBoundWithSize(long streamSize) {
final int origin = 13;
final int bound = 42;
final int[] values = ThreadLocalRandom.current().ints(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createInts(values, origin, bound);
final IntStream stream = rng.ints(streamSize, origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testLongs(long streamSize) {
final long[] values = ThreadLocalRandom.current().longs(streamSize).toArray();
final UniformRandomProvider rng = createLongs(values);
final LongStream stream = rng.longs();
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testLongsOriginBound(long streamSize) {
final long origin = 26278368423L;
final long bound = 422637723236L;
final long[] values = ThreadLocalRandom.current().longs(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createLongs(values, origin, bound);
final LongStream stream = rng.longs(origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testLongsWithSize(long streamSize) {
final long[] values = ThreadLocalRandom.current().longs(streamSize).toArray();
final UniformRandomProvider rng = createLongs(values);
final LongStream stream = rng.longs(streamSize);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testLongsOriginBoundWithSize(long streamSize) {
final long origin = 26278368423L;
final long bound = 422637723236L;
final long[] values = ThreadLocalRandom.current().longs(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createLongs(values, origin, bound);
final LongStream stream = rng.longs(streamSize, origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testDoubles(long streamSize) {
final double[] values = ThreadLocalRandom.current().doubles(streamSize).toArray();
final UniformRandomProvider rng = createDoubles(values);
final DoubleStream stream = rng.doubles();
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testDoublesOriginBound(long streamSize) {
final double origin = 1.23;
final double bound = 4.56;
final double[] values = ThreadLocalRandom.current().doubles(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createDoubles(values, origin, bound);
final DoubleStream stream = rng.doubles(origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.limit(streamSize).toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testDoublesWithSize(long streamSize) {
final double[] values = ThreadLocalRandom.current().doubles(streamSize).toArray();
final UniformRandomProvider rng = createDoubles(values);
final DoubleStream stream = rng.doubles(streamSize);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
@ParameterizedTest
@MethodSource(value = {"streamSizes"})
void testDoublesOriginBoundWithSize(long streamSize) {
final double origin = 1.23;
final double bound = 4.56;
final double[] values = ThreadLocalRandom.current().doubles(streamSize, origin, bound).toArray();
final UniformRandomProvider rng = createDoubles(values, origin, bound);
final DoubleStream stream = rng.doubles(streamSize, origin, bound);
Assertions.assertFalse(stream.isParallel());
Assertions.assertArrayEquals(values, stream.toArray());
}
}