/* __ __ __ __ __ ___
* \ \ / / \ \ / / __/
* \ \/ / /\ \ \/ / /
* \____/__/ \__\____/__/.ɪᴏ
* ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ
*/
package io.vavr.test;
import io.vavr.Function1;
import io.vavr.Tuple;
import io.vavr.collection.Iterator;
import io.vavr.collection.List;
import io.vavr.collection.Stream;
import org.junit.Test;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Comparator;
import java.util.Random;
import static org.assertj.core.api.Assertions.assertThat;
public class ArbitraryTest {
// equally distributed random number generator
private static final Random RANDOM = new Random();
// predictable random number generator (seed = 1)
private Random predictableRandom = new Random(1L);
// -- apply
@Test
public void shouldApplyIntegerObject() {
final Gen<BinaryTree<Integer>> gen = new ArbitraryBinaryTree(0, 0).apply(0);
assertThat(gen).isNotNull();
}
// -- flatMap
@Test
public void shouldFlatMapArbitrary() {
final Arbitrary<Integer> arbitraryInt = size -> Gen.choose(-size, size);
final Arbitrary<BinaryTree<Integer>> arbitraryTree = arbitraryInt.flatMap(i -> new ArbitraryBinaryTree(-i, i));
assertThat(arbitraryTree.apply(0).apply(RANDOM)).isNotNull();
}
// -- map
@Test
public void shouldMapArbitrary() {
final Arbitrary<Integer> arbitraryInt = size -> Gen.choose(-size, size);
final Arbitrary<BinaryTree<Integer>> arbitraryTree = arbitraryInt.map(BinaryTree::leaf);
assertThat(arbitraryTree.apply(0).apply(RANDOM)).isNotNull();
}
// -- filter
@Test
public void shouldFilterArbitrary() {
final Arbitrary<Integer> ints = Arbitrary.integer();
final Arbitrary<Integer> evenInts = ints.filter(i -> i % 2 == 0);
assertThat(evenInts.apply(10).apply(RANDOM)).isNotNull();
}
// -- peek
@Test
public void shouldPeekArbitrary() {
final int[] actual = new int[] { Integer.MIN_VALUE };
final int expected = Arbitrary.integer().peek(i -> actual[0] = i).apply(10).apply(RANDOM);
assertThat(actual[0]).isEqualTo(expected);
}
// factory methods
@Test
public void shouldCreateArbitraryInteger() {
final Arbitrary<Integer> arbitrary = Arbitrary.integer();
final Integer actual = arbitrary.apply(10).apply(RANDOM);
assertThat(actual).isNotNull();
}
@Test
public void shouldCreateArbitraryString() {
final Arbitrary<String> arbitrary = Arbitrary.string(Gen.choose('a', 'z'));
final String actual = arbitrary.apply(10).apply(RANDOM);
assertThat(actual).isNotNull();
}
@Test
public void shouldCreateArbitraryList() {
final Arbitrary<List<Integer>> arbitrary = Arbitrary.list(Arbitrary.integer());
final List<Integer> actual = arbitrary.apply(10).apply(RANDOM);
assertThat(actual).isNotNull();
}
@Test
public void shouldCreateArbitraryStream() {
final Arbitrary<Stream<Integer>> arbitrary = Arbitrary.stream(Arbitrary.integer());
final Stream<Integer> actual = arbitrary.apply(10).apply(RANDOM);
assertThat(actual).isNotNull();
}
@Test
public void shouldCreateFixedContentArbitrary() {
final Gen<String> arbitrary = Arbitrary.of("test", "content").apply(10);
for (int i = 0; i < 100; i++) {
assertThat(arbitrary.apply(RANDOM)).isIn("test", "content");
}
}
@Test
public void shouldCreateNonDistinctArbitrary() {
final Gen<String> arbitrary = Arbitrary.string(Gen.choose('a', 'b')).apply(2);
List.range(0, 1000)
.map(i -> arbitrary.apply(RANDOM))
.groupBy(Function1.identity())
.forEach((key, value) -> assertThat(value.length())
.describedAs(key)
.isGreaterThan(1));
}
@Test
public void shouldCreateDistinctArbitrary() {
final Gen<String> distinctArbitrary = Arbitrary.string(Gen.choose('a', 'b')).distinct().apply(100);
List.range(0, 1000)
.map(i -> distinctArbitrary.apply(RANDOM))
.groupBy(Function1.identity())
.forEach((key, value) -> assertThat(value.length())
.describedAs(key)
.isEqualTo(1));
}
@Test
public void shouldCreateDistinctByArbitrary() {
final Gen<String> distinctByArbitrary = Arbitrary.string(Gen.choose('a', 'b'))
.distinctBy(Comparator.naturalOrder()).apply(100);
List.range(0, 10000)
.map(i -> distinctByArbitrary.apply(RANDOM))
.groupBy(Function1.identity())
.forEach((key, value) -> assertThat(value.length())
.describedAs(key)
.isEqualTo(1));
}
@Test
public void shouldCreateInterspersedFixedContentArbitrary() {
final Gen<String> arbitrary = Arbitrary.of("test")
.intersperse(Arbitrary.of("content"))
.apply(10);
for (int i = 0; i < 100; i++) {
assertThat(arbitrary.apply(RANDOM)).isIn("test", "content");
}
}
@Test
public void shouldCreateInterspersedFixedContentArbitraryWithConstantOrder() {
final Gen<String> arbitrary = Arbitrary.of("test")
.intersperse(Arbitrary.of("content"))
.apply(10);
final Iterator<Stream<String>> generatedStringPairs = Stream.range(0, 10)
.map(i -> arbitrary.apply(RANDOM))
.grouped(2);
for (Stream<String> stringPairs : generatedStringPairs) {
assertThat(stringPairs.mkString(",")).isEqualTo("test,content");
}
}
@Test
public void shouldCreateCharArrayArbitrary() {
final Gen<String> arbitrary = Arbitrary.string(Gen.choose("test".toCharArray()))
.filter(s -> !"".equals(s))
.apply(1);
for (int i = 0; i < 100; i++) {
assertThat(arbitrary.apply(RANDOM)).isIn("t", "e", "s");
}
}
@Test
public void shouldCreateArbitraryStreamAndEvaluateAllElements() {
final Arbitrary<Stream<Integer>> arbitrary = Arbitrary.stream(Arbitrary.integer());
final Stream<Integer> actual = arbitrary.apply(10).apply(new Random() {
private static final long serialVersionUID = 1L;
@Override
public int nextInt(int bound) {
return bound - 1;
}
});
assertThat(actual.length()).isEqualTo(10);
}
@Test
public void shouldCreateArbitraryLocalDateTime(){
final Arbitrary<LocalDateTime> date = Arbitrary.localDateTime();
assertThat(date).isNotNull();
}
@Test(expected = NullPointerException.class)
public void shouldNotAcceptNullMedianLocalDateTime(){
Arbitrary.localDateTime(null, ChronoUnit.DAYS);
}
@Test(expected = NullPointerException.class)
public void shouldNotAcceptNullChronoUnit(){
Arbitrary.localDateTime(LocalDateTime.now(), null);
}
@Test
public void shouldCreateArbitraryLocalDateTimeAdjustedWithGivenChronoUnit(){
final LocalDateTime median = LocalDateTime.of(2017, 2, 17, 3, 40);
final Arbitrary<LocalDateTime> arbitrary = Arbitrary.localDateTime(median, ChronoUnit.YEARS);
final LocalDateTime date = arbitrary.apply(100).apply(predictableRandom);
assertThat(date).isEqualTo("2063-04-22T01:46:10.312");
}
@Test
public void shouldCreateMedianLocalDateTimeIfSizeIsZero(){
final LocalDateTime median = LocalDateTime.now();
final Arbitrary<LocalDateTime> arbitrary = Arbitrary.localDateTime(median, ChronoUnit.DAYS);
final LocalDateTime date = arbitrary.apply(0).apply(RANDOM);
assertThat(date).isEqualTo(median);
}
@Test
public void shouldCreateDatesInInRangeOfSize(){
final LocalDateTime median = LocalDateTime.now();
final Arbitrary<LocalDateTime> arbitrary = Arbitrary.localDateTime(median, ChronoUnit.DAYS);
Property.def("With size of 100 days, dates should be in range of +/- 100 days")
.forAll(arbitrary)
.suchThat(d -> d.isAfter(median.minusDays(100)) && d.isBefore(median.plusDays(100)))
.check(100, 1000);
}
@Test
public void shouldIgnoreNegativeSignInRangeOfDates(){
final LocalDateTime median = LocalDateTime.now();
final Arbitrary<LocalDateTime> arbitrary = Arbitrary.localDateTime(median, ChronoUnit.DAYS);
Property.def("With negative size of -100 days, dates should be in range of +/- 100 days")
.forAll(arbitrary)
.suchThat(d -> d.isAfter(median.minusDays(100)) && d.isBefore(median.plusDays(100)))
.check(-100, 1000);
}
@Test
public void shouldGenerateTwoDifferentSuccessiveDates(){
final Arbitrary<LocalDateTime> dates = Arbitrary.localDateTime();
final LocalDateTime firstDate = dates.apply(100).apply(RANDOM);
final LocalDateTime secondDate = dates.apply(100).apply(RANDOM);
assertThat(firstDate).isNotEqualTo(secondDate);
}
// -- transform
@Test
public void shouldTransformArbitrary() {
final Arbitrary<Integer> arbitrary = ignored -> Gen.of(1);
final String s = arbitrary.transform(a -> a.apply(0).apply(RANDOM).toString());
assertThat(s).isEqualTo("1");
}
// helpers
/**
* Represents arbitrary binary trees of a certain depth n with values of type int.
*/
static class ArbitraryBinaryTree implements Arbitrary<BinaryTree<Integer>> {
final int minValue;
final int maxValue;
ArbitraryBinaryTree(int minValue, int maxValue) {
this.minValue = Math.min(minValue, maxValue);
this.maxValue = Math.max(minValue, maxValue);
}
@Override
public Gen<BinaryTree<Integer>> apply(int n) {
return random -> Gen.choose(minValue, maxValue).flatMap(value -> {
if (n == 0) {
return Gen.of(BinaryTree.leaf(value));
} else {
return Gen.frequency(
Tuple.of(1, Gen.of(BinaryTree.leaf(value))),
Tuple.of(4, Gen.of(BinaryTree.branch(apply(n / 2).apply(random), value, apply(n / 2).apply(random))))
);
}
}
).apply(random);
}
}
interface BinaryTree<T> {
static <T> Branch<T> branch(BinaryTree<T> left, T value, BinaryTree<T> right) {
return new Branch<>(left, value, right);
}
static <T> Branch<T> leaf(T value) {
return new Branch<>(empty(), value, empty());
}
static <T> Empty<T> empty() {
return Empty.instance();
}
class Branch<T> implements BinaryTree<T> {
final BinaryTree<T> left;
final T value;
final BinaryTree<T> right;
Branch(BinaryTree<T> left, T value, BinaryTree<T> right) {
this.left = left;
this.value = value;
this.right = right;
}
}
class Empty<T> implements BinaryTree<T> {
private static final Empty<?> INSTANCE = new Empty<>();
@SuppressWarnings("unchecked")
static <T> Empty<T> instance() {
return (Empty<T>) INSTANCE;
}
}
}
}