Skip to content

Commit

Permalink
Added Arbitrary.ignoreExceptions(maxThrows, exceptionTypes)
Browse files Browse the repository at this point in the history
  • Loading branch information
jlink committed Mar 16, 2023
1 parent 15f6067 commit ef6122a
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 38 deletions.
3 changes: 0 additions & 3 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# 1.7.3

- Add Arbitrary.ignoreException(maxMisses, ...)
See https://github.com/jqwik-team/jqwik/issues/462.

- Allow annotation @BeforeTry on member variables of tests to reinitialize them before each try.
- Alternative: New annotation @InitBeforeTry

Expand Down
44 changes: 42 additions & 2 deletions api/src/main/java/net/jqwik/api/Arbitrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ abstract class ArbitraryFacade {

public abstract <T, U> Arbitrary<U> flatMap(Arbitrary<T> self, Function<T, Arbitrary<U>> mapper);

public abstract <T> Arbitrary<T> ignoreExceptions(Arbitrary<T> self, Class<? extends Throwable>[] exceptionTypes);
public abstract <T> Arbitrary<T> ignoreExceptions(Arbitrary<T> self, int maxThrows, Class<? extends Throwable>[] exceptionTypes);

public abstract <T> Arbitrary<T> dontShrink(Arbitrary<T> self);

Expand Down Expand Up @@ -534,13 +534,31 @@ default Arbitrary<Tuple.Tuple5<T, T, T, T, T>> tuple5() {
*
* @param exceptionType The exception type to ignore
* @return a new arbitrary instance
* @throws TooManyFilterMissesException if more than 10000 exceptions are thrown in a row
*/
@SuppressWarnings("unchecked")
@API(status = MAINTAINED, since = "1.3.1")
default Arbitrary<T> ignoreException(Class<? extends Throwable> exceptionType) {
return ignoreExceptions(exceptionType);
}

/**
* Create a new arbitrary of type {@code T} that will use the underlying
* arbitrary to create the tuple values but will ignore any raised exception of
* type {@code exceptionType} during generation.
*
* @param maxThrows The maximum number of subsequent exception throws before generation
* is stopped.
* @param exceptionType The exception type to ignore
* @return a new arbitrary instance
* @throws TooManyFilterMissesException if more than {@code maxThrows} exceptions are thrown in a row
*/
@SuppressWarnings("unchecked")
@API(status = EXPERIMENTAL, since = "1.7.3")
default Arbitrary<T> ignoreException(int maxThrows, Class<? extends Throwable> exceptionType) {
return ignoreExceptions(maxThrows, exceptionType);
}

/**
* Create a new arbitrary of type {@code T} that will use the underlying
* arbitrary to create the tuple values but will ignore any raised exception in
Expand All @@ -552,11 +570,33 @@ default Arbitrary<T> ignoreException(Class<? extends Throwable> exceptionType) {
*
* @param exceptionTypes The exception types to ignore
* @return a new arbitrary instance
* @throws TooManyFilterMissesException if more than 10000 exceptions are thrown in a row
*/
@SuppressWarnings("unchecked")
@API(status = MAINTAINED, since = "1.7.2")
default Arbitrary<T> ignoreExceptions(Class<? extends Throwable>... exceptionTypes) {
return ArbitraryFacade.implementation.ignoreExceptions(Arbitrary.this, exceptionTypes);
return this.ignoreExceptions(10000, exceptionTypes);
}

/**
* Create a new arbitrary of type {@code T} that will use the underlying
* arbitrary to create the tuple values but will ignore any raised exception in
* {@code exceptionTypes} during generation.
*
* <p>
* If {@code exceptionTypes} is empty, the original arbitrary is returned.
* </p>
*
* @param maxThrows The maximum number of subsequent exception throws before generation
* is stopped.
* @param exceptionTypes The exception types to ignore
* @return a new arbitrary instance
* @throws TooManyFilterMissesException if more than {@code maxThrows} exceptions are thrown in a row
*/
@SuppressWarnings("unchecked")
@API(status = EXPERIMENTAL, since = "1.7.3")
default Arbitrary<T> ignoreExceptions(int maxThrows, Class<? extends Throwable>... exceptionTypes) {
return ArbitraryFacade.implementation.ignoreExceptions(Arbitrary.this, maxThrows, exceptionTypes);
}

/**
Expand Down
11 changes: 7 additions & 4 deletions api/src/main/java/net/jqwik/api/ExhaustiveGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ abstract class ExhaustiveGeneratorFacade {

public abstract <T> ExhaustiveGenerator<T> injectNull(ExhaustiveGenerator<T> self);

public abstract <T> ExhaustiveGenerator<T> ignoreExceptions(ExhaustiveGenerator<T> self, Class<? extends Throwable>[] exceptionTypes);
public abstract <T> ExhaustiveGenerator<T> ignoreExceptions(
ExhaustiveGenerator<T> self,
Class<? extends Throwable>[] exceptionTypes,
int maxThrows
);
}

/**
Expand All @@ -48,9 +52,8 @@ default ExhaustiveGenerator<T> injectNull() {
return ExhaustiveGeneratorFacade.implementation.injectNull(this);
}

default ExhaustiveGenerator<T> ignoreExceptions(Class<? extends Throwable>[] exceptionTypes) {
return ExhaustiveGeneratorFacade.implementation.ignoreExceptions(this, exceptionTypes);
default ExhaustiveGenerator<T> ignoreExceptions(int maxThrows, Class<? extends Throwable>[] exceptionTypes) {
return ExhaustiveGeneratorFacade.implementation.ignoreExceptions(this, exceptionTypes, maxThrows);
}


}
22 changes: 13 additions & 9 deletions api/src/main/java/net/jqwik/api/RandomGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ abstract class RandomGeneratorFacade {
public abstract <T, U> Shrinkable<U> flatMap(Shrinkable<T> self, Function<T, RandomGenerator<U>> mapper, long nextLong);

public abstract <T, U> Shrinkable<U> flatMap(
Shrinkable<T> wrappedShrinkable,
Function<T, Arbitrary<U>> mapper,
int genSize,
long nextLong,
boolean withEmbeddedEdgeCases
Shrinkable<T> wrappedShrinkable,
Function<T, Arbitrary<U>> mapper,
int genSize,
long nextLong,
boolean withEmbeddedEdgeCases
);

public abstract <T> RandomGenerator<T> filter(RandomGenerator<T> self, Predicate<T> filterPredicate, int maxMisses);
Expand All @@ -37,7 +37,11 @@ public abstract <T, U> Shrinkable<U> flatMap(

public abstract <T> RandomGenerator<T> injectDuplicates(RandomGenerator<T> self, double duplicateProbability);

public abstract <T> RandomGenerator<T> ignoreExceptions(RandomGenerator<T> self, Class<? extends Throwable>[] exceptionTypes);
public abstract <T> RandomGenerator<T> ignoreExceptions(
RandomGenerator<T> self,
Class<? extends Throwable>[] exceptionTypes,
int maxThrows
);
}

/**
Expand Down Expand Up @@ -72,7 +76,7 @@ default <U> RandomGenerator<U> flatMap(Function<T, Arbitrary<U>> mapper, int gen
return random -> {
Shrinkable<T> wrappedShrinkable = RandomGenerator.this.next(random);
return RandomGeneratorFacade.implementation
.flatMap(wrappedShrinkable, mapper, genSize, random.nextLong(), withEmbeddedEdgeCases);
.flatMap(wrappedShrinkable, mapper, genSize, random.nextLong(), withEmbeddedEdgeCases);
};
}

Expand Down Expand Up @@ -102,8 +106,8 @@ default RandomGenerator<T> injectDuplicates(double duplicateProbability) {
}

@API(status = INTERNAL)
default RandomGenerator<T> ignoreExceptions(Class<? extends Throwable>[] exceptionTypes) {
return RandomGeneratorFacade.implementation.ignoreExceptions(this, exceptionTypes);
default RandomGenerator<T> ignoreExceptions(int maxThrows, Class<? extends Throwable>[] exceptionTypes) {
return RandomGeneratorFacade.implementation.ignoreExceptions(this, exceptionTypes, maxThrows);
}

@API(status = INTERNAL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,25 +81,25 @@ public <T> Arbitrary<T> injectNull(Arbitrary<T> self, double nullProbability) {
}

@Override
public <T> Arbitrary<T> ignoreExceptions(Arbitrary<T> self, Class<? extends Throwable>[] exceptionTypes) {
public <T> Arbitrary<T> ignoreExceptions(Arbitrary<T> self, int maxThrows, Class<? extends Throwable>[] exceptionTypes) {
if (exceptionTypes.length == 0) {
return self;
}
return new ArbitraryDelegator<T>(self) {
@Override
public RandomGenerator<T> generator(int genSize) {
return super.generator(genSize).ignoreExceptions(exceptionTypes);
return super.generator(genSize).ignoreExceptions(maxThrows, exceptionTypes);
}

@Override
public RandomGenerator<T> generatorWithEmbeddedEdgeCases(int genSize) {
return super.generatorWithEmbeddedEdgeCases(genSize).ignoreExceptions(exceptionTypes);
return super.generatorWithEmbeddedEdgeCases(genSize).ignoreExceptions(maxThrows, exceptionTypes);
}

@Override
public Optional<ExhaustiveGenerator<T>> exhaustive(long maxNumberOfSamples) {
return super.exhaustive(maxNumberOfSamples)
.map(generator -> generator.ignoreExceptions(exceptionTypes));
.map(generator -> generator.ignoreExceptions(maxThrows, exceptionTypes));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ public <T> ExhaustiveGenerator<T> injectNull(ExhaustiveGenerator<T> self) {
}

@Override
public <T> ExhaustiveGenerator<T> ignoreExceptions(final ExhaustiveGenerator<T> self, final Class<? extends Throwable>[] exceptionTypes) {
return new IgnoreExceptionExhaustiveGenerator<>(self, exceptionTypes);
public <T> ExhaustiveGenerator<T> ignoreExceptions(
final ExhaustiveGenerator<T> self,
final Class<? extends Throwable>[] exceptionTypes,
int maxThrows
) {
return new IgnoreExceptionExhaustiveGenerator<>(self, exceptionTypes, maxThrows);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ public <T, U> Shrinkable<U> flatMap(Shrinkable<T> self, Function<T, RandomGenera

@Override
public <T, U> Shrinkable<U> flatMap(
Shrinkable<T> self,
Function<T, Arbitrary<U>> mapper,
int genSize,
long nextLong,
boolean withEmbeddedEdgeCases
Shrinkable<T> self,
Function<T, Arbitrary<U>> mapper,
int genSize,
long nextLong,
boolean withEmbeddedEdgeCases
) {
return new FlatMappedShrinkable<>(self, mapper, genSize, nextLong, withEmbeddedEdgeCases);
}
Expand All @@ -48,7 +48,7 @@ public <T> RandomGenerator<T> injectDuplicates(RandomGenerator<T> self, double d
}

@Override
public <T> RandomGenerator<T> ignoreExceptions(RandomGenerator<T> self, Class<? extends Throwable>[] exceptionTypes) {
return new IgnoreExceptionGenerator<>(self, exceptionTypes);
public <T> RandomGenerator<T> ignoreExceptions(RandomGenerator<T> self, Class<? extends Throwable>[] exceptionTypes, int maxThrows) {
return new IgnoreExceptionGenerator<>(self, exceptionTypes, maxThrows);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
public class IgnoreExceptionExhaustiveGenerator<T> implements ExhaustiveGenerator<T> {
private final ExhaustiveGenerator<T> toFilter;
private final Class<? extends Throwable>[] exceptionTypes;
private final int maxThrows;

public IgnoreExceptionExhaustiveGenerator(ExhaustiveGenerator<T> toFilter, Class<? extends Throwable>[] exceptionTypes) {
public IgnoreExceptionExhaustiveGenerator(ExhaustiveGenerator<T> toFilter, Class<? extends Throwable>[] exceptionTypes, int maxThrows) {
this.toFilter = toFilter;
this.exceptionTypes = exceptionTypes;
this.maxThrows = maxThrows;
}

@Override
Expand Down Expand Up @@ -43,7 +45,7 @@ public T next() {
}

private T findNext() {
for (int i = 0; i < 10000; i++) {
for (int i = 0; i < maxThrows; i++) {
if (!mappedIterator.hasNext()) {
return null;
}
Expand All @@ -56,7 +58,7 @@ private T findNext() {
throw throwable;
}
}
String message = String.format("Filter missed more than %s times.", 10000);
String message = String.format("Filter missed more than %s times.", maxThrows);
throw new TooManyFilterMissesException(message);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ public class IgnoreExceptionGenerator<T> implements RandomGenerator<T> {

private final RandomGenerator<T> base;
private final Class<? extends Throwable>[] exceptionTypes;
private final int maxThrows;

public IgnoreExceptionGenerator(RandomGenerator<T> base, Class<? extends Throwable>[] exceptionTypes) {
public IgnoreExceptionGenerator(RandomGenerator<T> base, Class<? extends Throwable>[] exceptionTypes, int maxThrows) {
this.base = base;
this.exceptionTypes = exceptionTypes;
this.maxThrows = maxThrows;
}

@Override
Expand All @@ -24,7 +26,7 @@ public Shrinkable<T> next(final Random random) {
}

private Shrinkable<T> nextUntilAccepted(Random random, Function<Random, Shrinkable<T>> fetchShrinkable) {
for (int i = 0; i < 10000; i++) {
for (int i = 0; i < maxThrows; i++) {
try {
Shrinkable<T> next = fetchShrinkable.apply(random);
// Enforce value generation for possible exception raising
Expand All @@ -37,7 +39,7 @@ private Shrinkable<T> nextUntilAccepted(Random random, Function<Random, Shrinkab
throw throwable;
}
}
String message = String.format("%s missed more than %s times.", this, 10000);
String message = String.format("%s missed more than %s times.", this, maxThrows);
throw new TooManyFilterMissesException(message);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.jqwik.api;

import java.util.*;
import java.util.concurrent.atomic.*;

import org.assertj.core.api.*;

Expand All @@ -20,7 +21,6 @@ private static class OtherException extends RuntimeException {
}
}


@Example
void ignoreIllegalArgumentException(@ForAll Random random) {
Arbitrary<Integer> arbitrary =
Expand Down Expand Up @@ -99,6 +99,26 @@ void failIfFilterWillDiscard10000ValuesInARow(@ForAll Random random) {
assertThatThrownBy(() -> generator.next(random).value()).isInstanceOf(JqwikException.class);
}

@Example
void maxThrowsIsConsideredForFailing(@ForAll Random random) {
AtomicInteger countMisses = new AtomicInteger();
Arbitrary<Integer> arbitrary =
Arbitraries.integers()
.map(anInt -> {
countMisses.incrementAndGet();
throw new IllegalArgumentException("Always fail");
});
Arbitrary<Integer> filtered = arbitrary.ignoreException(
100,
IllegalArgumentException.class
);
// Allowing edge cases will raise the number since edge case filtering cannot be cancelled
RandomGenerator<Integer> generator = filtered.generator(10, false);

assertThatThrownBy(() -> generator.next(random)).isInstanceOf(TooManyFilterMissesException.class);
assertThat(countMisses).hasValue(100);
}

@Example
void exhaustiveGeneration() {
Arbitrary<Integer> arbitrary =
Expand Down

0 comments on commit ef6122a

Please sign in to comment.