Skip to content

Commit 3de9ca9

Browse files
Vladimir Sitnikovjlink
authored andcommitted
Add mix64 to XORShiftRandom constructor to make newRandom(random.nextLong()) better
See #423
1 parent e671d55 commit 3de9ca9

File tree

2 files changed

+50
-9
lines changed

2 files changed

+50
-9
lines changed

engine/src/main/java/net/jqwik/engine/SourceOfRandomness.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,20 @@ private XORShiftRandom() {
6262
}
6363

6464
private XORShiftRandom(long seed) {
65-
if (seed == 0l) {
66-
throw new IllegalArgumentException("0L is not an allowed seed value");
65+
this.seed = mix64(seed);
66+
if (this.seed == 0) {
67+
// 0 is invalid for XorShift seed, so we set it to a non-zero value
68+
this.seed = 0xbf58476d1ce4e5b9L;
6769
}
68-
this.seed = seed;
70+
}
71+
72+
/**
73+
* See <a href="http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html">Better Bit Mixing - Improving on MurmurHash3's 64-bit Finalizer</a>
74+
*/
75+
private static long mix64(long z) {
76+
z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L;
77+
z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL;
78+
return z ^ (z >>> 31);
6979
}
7080

7181
@Override

engine/src/test/java/net/jqwik/engine/properties/CheckedPropertyTests.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.*;
44
import java.util.function.*;
55

6+
import org.assertj.core.api.*;
67
import org.junit.platform.engine.reporting.*;
78
import org.opentest4j.*;
89

@@ -16,6 +17,7 @@
1617
import net.jqwik.testing.*;
1718

1819
import static org.assertj.core.api.Assertions.*;
20+
import static org.assertj.core.api.SoftAssertions.*;
1921

2022
import static net.jqwik.api.GenerationMode.*;
2123
import static net.jqwik.engine.TestHelper.*;
@@ -263,25 +265,54 @@ void usePreviouslyFailedGeneration() {
263265
Arbitrary<Integer> integers = Arbitraries.integers().between(1, 99);
264266
GenerationInfo previousGenerationInfo = new GenerationInfo("41", 13);
265267
// This is what's being generated from integers in the 13th attempt
266-
List<Integer> previousSample = Arrays.asList(99, 97);
268+
List<Integer> expectedParameterValues = Arrays.asList(65, 77);
267269

268-
CheckedFunction checkSample = params -> params.equals(previousSample);
270+
CheckedFunction checkSample = params -> {
271+
Assertions.assertThat(params)
272+
.describedAs("sampleProperty initial params should reuse GenerationInfo supplied in the config. " +
273+
"If you see failure here, then it looks like the random generation strategy has changed. " +
274+
"You might need to adjust expectedParameterValues = ... in usePreviouslyFailedGeneration() property test.")
275+
.isEqualTo(expectedParameterValues);
276+
return true;
277+
};
269278

270279
CheckedProperty checkedProperty = createCheckedProperty(
271280
"sampleProperty", checkSample, getParametersForMethod("sampleProperty"),
272281
p -> Collections.singleton(integers),
273282
Optional.empty(),
274283
aConfig()
275284
.withPreviousFailureGeneration(previousGenerationInfo)
285+
// Disable shrinking, so checkSample fails on the first attempt, and we can see the first failure,
286+
// and not the result of the shrinking which will be always [1, 1]
287+
.withShrinking(ShrinkingMode.OFF)
276288
.withAfterFailure(AfterFailureMode.SAMPLE_ONLY).build(),
277289
lifecycleContextForMethod("sampleProperty", int.class, int.class)
278290
);
279291

280292
PropertyCheckResult check = checkedProperty.check(new Reporting[0]);
281-
assertThat(check.countTries()).isEqualTo(1);
282-
assertThat(check.seed()).isEqualTo(Optional.of("41"));
283-
assertThat(check.checkStatus()).isEqualTo(SUCCESSFUL);
284-
assertThat(check.falsifiedParameters()).isEmpty();
293+
assertSoftly(
294+
softly -> {
295+
// Rethrow the error, so the stacktrace is meaningful, and the assert message in checkSample is printed
296+
check.throwable().ifPresent(
297+
e -> softly.assertThat(e)
298+
// softly.fail("...", e) would not print stacktrace for some reason
299+
.describedAs("checkSample property failed")
300+
.doesNotThrowAnyException()
301+
);
302+
softly.assertThat(check.countTries())
303+
.describedAs("check.countTries()")
304+
.isEqualTo(1);
305+
softly.assertThat(check.seed())
306+
.describedAs("check.seed()")
307+
.contains("41");
308+
softly.assertThat(check.checkStatus())
309+
.describedAs("check.checkStatus()")
310+
.isEqualTo(SUCCESSFUL);
311+
softly.assertThat(check.falsifiedParameters())
312+
.describedAs("check.falsifiedParameters()")
313+
.isEmpty();
314+
}
315+
);
285316
}
286317

287318
@SuppressWarnings("unchecked")

0 commit comments

Comments
 (0)