Add property-based testing for SideEffectSet

This commit is contained in:
Octavia Togami 2024-09-06 23:52:24 -07:00
parent e54a68325c
commit c61b273f8d
5 changed files with 56 additions and 95 deletions

1
.gitignore vendored
View File

@ -14,6 +14,7 @@ target
forge-download
out
run
.jqwik-database
/dependency-reduced-pom.xml
*-private.sh

View File

@ -30,7 +30,9 @@
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
useJUnitPlatform {
includeEngines("junit-jupiter", "jqwik")
}
}
dependencies {
@ -38,6 +40,7 @@
"testImplementation"(platform(stringyLibs.getLibrary("junit-bom")))
"testImplementation"(stringyLibs.getLibrary("junit-jupiter-api"))
"testImplementation"(stringyLibs.getLibrary("junit-jupiter-params"))
"testImplementation"(stringyLibs.getLibrary("jqwik"))
"testImplementation"(platform(stringyLibs.getLibrary("mockito-bom")))
"testImplementation"(stringyLibs.getLibrary("mockito-core"))
"testImplementation"(stringyLibs.getLibrary("mockito-junit-jupiter"))

View File

@ -66,6 +66,8 @@ junit-jupiter-api.module = "org.junit.jupiter:junit-jupiter-api"
junit-jupiter-params.module = "org.junit.jupiter:junit-jupiter-params"
junit-jupiter-engine.module = "org.junit.jupiter:junit-jupiter-engine"
jqwik = "net.jqwik:jqwik:1.9.0"
mockito-bom = "org.mockito:mockito-bom:5.11.0"
mockito-core.module = "org.mockito:mockito-core"
mockito-junit-jupiter.module = "org.mockito:mockito-junit-jupiter"

View File

@ -19,107 +19,60 @@
package com.sk89q.worldedit.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.junit.jupiter.api.Test;
import net.jqwik.api.ForAll;
import net.jqwik.api.Property;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class SideEffectSetTest {
private static void assertAppliesWithState(Map<SideEffect, SideEffect.State> expected, SideEffectSet set) {
Preconditions.checkArgument(
expected.keySet().containsAll(EnumSet.allOf(SideEffect.class)),
"Expected map must contain all side effects"
);
@Property
public boolean stateOrDefaultIsCorrect(
@ForAll Map<SideEffect, SideEffect.State> stateMap,
@ForAll SideEffect effectToTest
) {
SideEffectSet set = new SideEffectSet(stateMap);
return set.getState(effectToTest) == stateMap.getOrDefault(effectToTest, effectToTest.getDefaultValue());
}
Set<SideEffect> appliedSet = expected.entrySet().stream()
.filter(e -> e.getValue() != SideEffect.State.OFF)
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
assertEquals(appliedSet, set.getSideEffectsToApply());
assertEquals(!appliedSet.isEmpty(), set.doesApplyAny());
@Property
public boolean shouldApplyUnlessOff(
@ForAll Map<SideEffect, SideEffect.State> stateMap,
@ForAll SideEffect effectToTest
) {
SideEffectSet set = new SideEffectSet(stateMap);
return set.shouldApply(effectToTest)
== (stateMap.getOrDefault(effectToTest, effectToTest.getDefaultValue()) != SideEffect.State.OFF);
}
@Property
public boolean withChangesState(
@ForAll Map<SideEffect, SideEffect.State> stateMap,
@ForAll SideEffect effectToTest,
@ForAll SideEffect.State stateToSet
) {
SideEffectSet set = new SideEffectSet(stateMap).with(effectToTest, stateToSet);
return set.getState(effectToTest) == stateToSet;
}
@Property
public boolean anyShouldApplyEqualsDoesApplyAny(@ForAll Map<SideEffect, SideEffect.State> stateMap) {
SideEffectSet set = new SideEffectSet(stateMap);
boolean anyShouldApply = false;
for (SideEffect effect : SideEffect.values()) {
assertEquals(
appliedSet.contains(effect), set.shouldApply(effect), "Does not apply expected effect: " + effect
);
assertEquals(
expected.get(effect), set.getState(effect),
"Does not have expected state for effect: " + effect
);
}
}
private static Map<SideEffect, SideEffect.State> initStateMap(Function<SideEffect, SideEffect.State> stateFunction) {
return Arrays.stream(SideEffect.values()).collect(Collectors.toMap(Function.identity(), stateFunction));
}
@Test
public void defaults() {
assertAppliesWithState(
initStateMap(SideEffect::getDefaultValue),
SideEffectSet.defaults()
);
}
@Test
public void noneExposed() {
assertAppliesWithState(
initStateMap(effect -> {
if (effect.isExposed()) {
return SideEffect.State.OFF;
} else {
return effect.getDefaultValue();
}
}),
SideEffectSet.none()
);
}
@Test
public void allOn() {
Map<SideEffect, SideEffect.State> expected = initStateMap(effect -> SideEffect.State.ON);
assertAppliesWithState(
expected,
new SideEffectSet(expected)
);
}
@Test
public void allDelayed() {
Map<SideEffect, SideEffect.State> expected = initStateMap(effect -> SideEffect.State.DELAYED);
assertAppliesWithState(
expected,
new SideEffectSet(expected)
);
}
@Test
public void allOff() {
Map<SideEffect, SideEffect.State> expected = initStateMap(effect -> SideEffect.State.OFF);
assertAppliesWithState(
expected,
new SideEffectSet(expected)
);
}
@Test
public void with() {
Map<SideEffect, SideEffect.State> expected = initStateMap(SideEffect::getDefaultValue);
SideEffectSet set = SideEffectSet.defaults();
for (SideEffect effect : SideEffect.values()) {
for (SideEffect.State state : SideEffect.State.values()) {
expected = Maps.transformEntries(expected, (e, s) -> e == effect ? state : s);
set = set.with(effect, state);
assertAppliesWithState(expected, set);
if (set.shouldApply(effect)) {
anyShouldApply = true;
break;
}
}
return anyShouldApply == set.doesApplyAny();
}
@Property
public boolean shouldApplyEqualsApplySetContains(
@ForAll Map<SideEffect, SideEffect.State> stateMap,
@ForAll SideEffect effectToTest
) {
SideEffectSet set = new SideEffectSet(stateMap);
return set.shouldApply(effectToTest) == set.getSideEffectsToApply().contains(effectToTest);
}
}

View File

@ -3,3 +3,5 @@ junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=same_thread
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.config.dynamic.factor=4
jqwik.tries.default = 10000