Skip to content

Commit

Permalink
Simplified checking for shrunk values. One test still sporadically fa…
Browse files Browse the repository at this point in the history
…iling.
  • Loading branch information
jlink committed Jan 31, 2020
1 parent 1db8b0b commit 2d3893a
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,13 @@ private boolean shrinkPair(
private List<Tuple2<Shrinkable<T>, Shrinkable<T>>> suggestions(Shrinkable<T> first, Shrinkable<T> second) {
List<Shrinkable<T>> suggestionsFirst = first.shrinkingSuggestions();
List<Shrinkable<T>> suggestionsSecond = second.shrinkingSuggestions();
int length = Math.min(suggestionsFirst.size(), suggestionsSecond.size());

List<Tuple2<Shrinkable<T>, Shrinkable<T>>> suggestions = new ArrayList<>();

for (int i = 0; i < length; i++) {
Shrinkable<T> firstSuggestion = suggestionsFirst.get(i);
Shrinkable<T> secondSuggestion = suggestionsSecond.get(i);
if (areDuplicates(firstSuggestion, secondSuggestion)) {
suggestions.add(Tuple.of(firstSuggestion, secondSuggestion));
for (Shrinkable<T> firstSuggestion : suggestionsFirst) {
for (Shrinkable<T> secondSuggestion : suggestionsSecond) {
if (areDuplicates(firstSuggestion, secondSuggestion)) {
suggestions.add(Tuple.of(firstSuggestion, secondSuggestion));
}
}
}
return suggestions;
Expand Down
10 changes: 6 additions & 4 deletions engine/src/test/java/net/jqwik/engine/ExpectFailure.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.jqwik.engine;

import java.lang.annotation.*;
import java.util.function.*;

import net.jqwik.api.lifecycle.*;

Expand All @@ -14,15 +15,16 @@
@AddLifecycleHook(ExpectFailureHook.class)
public @interface ExpectFailure {

class None extends Throwable {
private None() {
class NullChecker implements Consumer<PropertyExecutionResult> {
@Override
public void accept(PropertyExecutionResult propertyExecutionResult) {
}
}

/**
* Optionally specify an expected Throwable subtype to show up as failure reason.
* Optionally specify a checker
*/
Class<? extends Throwable> throwable() default None.class;
Class<? extends Consumer<PropertyExecutionResult>> checkResult() default NullChecker.class;

String value() default "";
}
38 changes: 23 additions & 15 deletions engine/src/test/java/net/jqwik/engine/ExpectFailureHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,39 @@

import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

import org.junit.platform.commons.support.*;

import net.jqwik.api.lifecycle.*;
import net.jqwik.engine.hooks.*;
import net.jqwik.engine.support.*;

public class ExpectFailureHook implements AroundPropertyHook {

@Override
public PropertyExecutionResult aroundProperty(PropertyLifecycleContext context, PropertyExecutor property) throws Throwable {
PropertyExecutionResult testExecutionResult = property.execute();
Class<? extends Throwable> expectedFailureType = getExpectedFailureType(context.targetMethod());
Consumer<PropertyExecutionResult> resultChecker = getResultChecker(context.targetMethod(), context.testInstance());
String messageFromAnnotation = getMessage(context.targetMethod());

if (testExecutionResult.getStatus() == PropertyExecutionResult.Status.FAILED) {
if (expectedFailureType == null) {
return testExecutionResult.changeToSuccessful();
}
if (resultHasExpectedFailureType(testExecutionResult, expectedFailureType)) {
try {
if (testExecutionResult.getStatus() == PropertyExecutionResult.Status.FAILED) {
resultChecker.accept(testExecutionResult);
return testExecutionResult.changeToSuccessful();
}
} catch (AssertionError assertionError) {
return testExecutionResult.changeToFailed(assertionError);
}

String headerText = messageFromAnnotation == null ? "" : messageFromAnnotation + "\n\t";
String expectedFailureTypeText = expectedFailureType == null ? ""
: String.format(" with exception of type [%s]", expectedFailureType.getName());
String reason = testExecutionResult.getThrowable()
.map(throwable -> String.format("it failed with [%s]", throwable))
.orElse("it did not fail at all");
String message = String.format(
"%sProperty [%s] should have failed%s, but %s",
"%sProperty [%s] should have failed, but %s",
headerText,
context.label(),
expectedFailureTypeText,
reason
);
return testExecutionResult.changeToFailed(message);
Expand All @@ -58,17 +57,26 @@ private Boolean resultHasExpectedFailureType(
.orElse(false);
}

private Class<? extends Throwable> getExpectedFailureType(Method method) {
private Consumer<PropertyExecutionResult> getResultChecker(Method method, Object testInstance) {
Optional<ExpectFailure> annotation = AnnotationSupport.findAnnotation(method, ExpectFailure.class);
return annotation.map(expectFailure -> {
Class<? extends Throwable> expectedFailureType = expectFailure.throwable();
return expectedFailureType == ExpectFailure.None.class ? null : expectedFailureType;
}).orElse(null);
return annotation.map((ExpectFailure expectFailure) -> {
Class<? extends Consumer<PropertyExecutionResult>> checkResult = expectFailure.checkResult();
return (Consumer<PropertyExecutionResult>) JqwikReflectionSupport.newInstanceInTestContext(checkResult, testInstance);
})
.orElse(
JqwikReflectionSupport.newInstanceInTestContext(ExpectFailure.NullChecker.class, testInstance)
);
}

@Override
public int aroundPropertyProximity() {
return Hooks.AroundProperty.EXPECT_FAILURE_PROXIMITY;
}

static class NullChecker implements Consumer<PropertyExecutionResult> {
@Override
public void accept(PropertyExecutionResult propertyExecutionResult) {
}
}

}
22 changes: 22 additions & 0 deletions engine/src/test/java/net/jqwik/engine/ShrinkToChecker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.jqwik.engine;

import java.util.*;
import java.util.function.*;

import net.jqwik.api.lifecycle.*;

import static org.assertj.core.api.Assertions.*;

public abstract class ShrinkToChecker implements Consumer<PropertyExecutionResult> {
@Override
public void accept(PropertyExecutionResult propertyExecutionResult) {
Optional<List<Object>> falsifiedSample = propertyExecutionResult.getFalsifiedSample();
assertThat(falsifiedSample).isPresent();
assertThat(falsifiedSample.get()).containsExactlyElementsOf(shrunkValues());

}

public abstract Iterable<?> shrunkValues();
}


Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
import org.mockito.*;

import net.jqwik.api.*;
import net.jqwik.api.arbitraries.*;
import net.jqwik.api.constraints.*;
import net.jqwik.api.lifecycle.*;
import net.jqwik.engine.*;
import net.jqwik.engine.properties.shrinking.ShrinkableTypesForTest.*;

import static java.util.Arrays.*;
Expand Down Expand Up @@ -75,38 +74,34 @@ void shrinkAllParameters() {
}

@Property(tries = 10000)
@ExpectFailure(checkResult = ShrinkTo77.class)
boolean shrinkDuplicateParametersTogether(
@ForAll @Positive int int1,
@ForAll @Positive int int2
) {
PropertyLifecycle.after(((executionResult, context) -> {
if (executionResult.getStatus() == PropertyExecutionResult.Status.FAILED) {
Optional<List<Object>> falsifiedSample = executionResult.getFalsifiedSample();
assertThat(falsifiedSample).isPresent();
assertThat(falsifiedSample.get()).containsExactly(6, 6);
return executionResult.changeToSuccessful();
} else {
return executionResult.changeToFailed("Should have failed");
}
}));
return int1 < 6 || int1 != int2;
return int1 < 7 || int1 != int2;
}

private class ShrinkTo77 extends ShrinkToChecker {
@Override
public Iterable<?> shrunkValues() {
return Arrays.asList(7, 7);
}
}

@Property(tries = 10000, afterFailure = AfterFailureMode.RANDOM_SEED)
@ExpectFailure(checkResult = ShrunkToAA.class)
void shrinkBothParametersToStringAA(@ForAll("aString") String first, @ForAll("aString") String second) {
PropertyLifecycle.after(((executionResult, context) -> {
if (executionResult.getStatus() == PropertyExecutionResult.Status.FAILED) {
Optional<List<Object>> falsifiedSample = executionResult.getFalsifiedSample();
assertThat(falsifiedSample).isPresent();
assertThat(falsifiedSample.get()).containsExactly("aa", "aa");
return executionResult.changeToSuccessful();
} else {
return executionResult.changeToFailed("Should have failed");
}
}));
assertThat(first).isNotEqualTo(second);
}

private class ShrunkToAA extends ShrinkToChecker {
@Override
public Iterable<?> shrunkValues() {
return Arrays.asList("aa", "aa");
}
}

@Provide
Arbitrary<String> aString() {
return Arbitraries.strings().withCharRange('a', 'z').ofMinLength(2).ofMaxLength(5);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

import net.jqwik.api.*;
import net.jqwik.api.constraints.*;
import net.jqwik.api.lifecycle.*;
import net.jqwik.api.statistics.*;
import net.jqwik.api.statistics.Statistics;
import net.jqwik.engine.*;

import static org.assertj.core.api.Assertions.*;

import static net.jqwik.api.statistics.StatisticsReport.StatisticsReportMode.*;

@Group
Expand Down Expand Up @@ -78,7 +81,7 @@ void countCheckAssertion(@ForAll int anInt) {

Statistics.coverage(coverage -> {
coverage.check(true).count(c -> {
Assertions.assertThat(c).isEqualTo(countPositive);
assertThat(c).isEqualTo(countPositive);
});
});
}
Expand All @@ -89,7 +92,7 @@ void countCheckAssertionWithCountAll(@ForAll int anInt) {

Statistics.coverage(coverage -> {
coverage.check(true).count((c, a) -> {
Assertions.assertThat(c).isLessThanOrEqualTo(a);
assertThat(c).isLessThanOrEqualTo(a);
});
});
}
Expand Down Expand Up @@ -134,8 +137,8 @@ void percentageCheckAssertion(@ForAll int anInt) {

Statistics.coverage(coverage -> {
coverage.check(true).percentage(p -> {
Assertions.assertThat(p).isGreaterThan(0.0);
Assertions.assertThat(p).isLessThan(100.0);
assertThat(p).isGreaterThan(0.0);
assertThat(p).isLessThan(100.0);
});
});
}
Expand Down Expand Up @@ -163,7 +166,7 @@ void checkPercentageForQuery(@ForAll @IntRange(min = 1, max = 100) int anInt) {
Statistics.coverage(coverage -> {
Predicate<List<Integer>> query = params -> params.get(0) <= 50;
coverage.checkQuery(query).percentage(p -> {
Assertions.assertThat(p).isCloseTo(50.0, Offset.offset(0.1));
assertThat(p).isCloseTo(50.0, Offset.offset(0.1));
});
});
}
Expand All @@ -182,7 +185,7 @@ void checkCountsAreAddedUp(@ForAll @IntRange(min = 1, max = 100) int anInt) {

@Property(tries = 10)
@StatisticsReport(OFF)
@ExpectFailure(throwable = ClassCastException.class)
@ExpectFailure(checkResult = CheckClassCastException.class)
void queryWithWrongTypeFails(@ForAll int anInt) {
Statistics.collect(anInt);

Expand All @@ -192,5 +195,13 @@ void queryWithWrongTypeFails(@ForAll int anInt) {
});
}

private class CheckClassCastException implements Consumer<PropertyExecutionResult> {
@Override
public void accept(PropertyExecutionResult propertyExecutionResult) {
assertThat(propertyExecutionResult.getThrowable()).isPresent();
assertThat(propertyExecutionResult.getThrowable().get()).isInstanceOf(ClassCastException.class);
}
}

}
}

0 comments on commit 2d3893a

Please sign in to comment.