Skip to content

Commit d5538e2

Browse files
committed
Add utility methods to RandomizedTest to simpify backporting.
1 parent 6575d29 commit d5538e2

8 files changed

Lines changed: 510 additions & 3 deletions

File tree

etc/junit4-missing-features.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
2. RandomizedTest base class and RandomizedContext
1010
- Extend RandomizedTest for convenient access to a per-test Random instance
1111
- Access the context directly via RandomizedContext.current()
12+
- Utility methods on RandomizedTest: randomInt(), randomIntInRange(),
1213

1314
3. Randomized input generation
14-
- Utility methods on RandomizedTest: randomInt(), randomIntInRange(),
1515
randomBoolean(), randomFloat(), etc.
1616
- Encourages testing over a broad input domain rather than fixed values
1717

Lines changed: 312 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,320 @@
11
package com.carrotsearch.randomizedtesting.jupiter;
22

3+
import com.carrotsearch.randomizedtesting.jupiter.generators.RandomBytes;
4+
import com.carrotsearch.randomizedtesting.jupiter.generators.RandomNumbers;
5+
import com.carrotsearch.randomizedtesting.jupiter.generators.RandomPicks;
6+
import com.carrotsearch.randomizedtesting.jupiter.generators.RandomStrings;
7+
import java.util.Arrays;
8+
import java.util.Comparator;
9+
import java.util.List;
10+
import java.util.Locale;
11+
import java.util.Random;
12+
import java.util.TimeZone;
13+
import org.junit.jupiter.api.extension.AfterAllCallback;
14+
import org.junit.jupiter.api.extension.BeforeAllCallback;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.junit.jupiter.api.extension.Extension;
17+
import org.junit.jupiter.api.extension.ExtensionContext;
318
import org.junit.jupiter.api.parallel.Execution;
419
import org.junit.jupiter.api.parallel.ExecutionMode;
520

621
@Randomized
722
@DetectThreadLeaks(scope = DetectThreadLeaks.Scope.TEST)
823
@Execution(value = ExecutionMode.SAME_THREAD, reason = "Backward compatibility.")
9-
public abstract class RandomizedTest {}
24+
@ExtendWith(RandomizedTest.SetMultiplier.class)
25+
public abstract class RandomizedTest {
26+
private static Double multiplier;
27+
28+
static final class SetMultiplier implements Extension, BeforeAllCallback, AfterAllCallback {
29+
@Override
30+
public void beforeAll(ExtensionContext extensionContext) throws Exception {
31+
multiplier =
32+
extensionContext
33+
.getConfigurationParameter(SysProps.TESTS_RANDOM_FACTORY.propertyKey)
34+
.map(Double::parseDouble)
35+
.orElse(1.);
36+
}
37+
38+
@Override
39+
public void afterAll(ExtensionContext extensionContext) throws Exception {
40+
multiplier = null;
41+
}
42+
}
43+
44+
// The following utility methods are declared for convenience and ease
45+
// of porting from the previous version of the framework.
46+
47+
public static byte[] randomBytesOfLength(Random rnd, int length) {
48+
return RandomBytes.randomBytesOfLength(new Random(rnd.nextLong()), length);
49+
}
50+
51+
public static byte[] randomBytesOfLength(Random rnd, int minLength, int maxLength) {
52+
return RandomBytes.randomBytesOfLengthBetween(new Random(rnd.nextLong()), minLength, maxLength);
53+
}
54+
55+
/** Use {@link #randomIntInRange(Random, int, int)}. */
56+
@Deprecated(forRemoval = true)
57+
public static int randomIntBetween(Random rnd, int min, int max) {
58+
return randomIntInRange(rnd, min, max);
59+
}
60+
61+
public static int randomIntInRange(Random rnd, int min, int max) {
62+
return RandomNumbers.randomIntInRange(rnd, min, max);
63+
}
64+
65+
/** Use {@link #randomLongInRange(Random, long, long)}. */
66+
@Deprecated(forRemoval = true)
67+
public static long randomLongBetween(Random rnd, long min, long max) {
68+
return randomLongInRange(rnd, min, max);
69+
}
70+
71+
public static long randomLongInRange(Random rnd, long min, long max) {
72+
return RandomNumbers.randomLongInRange(rnd, min, max);
73+
}
74+
75+
/**
76+
* Returns a random value greater or equal to <code>min</code>. The value picked is affected
77+
* {@link #multiplier()}.
78+
*
79+
* @see #scaledRandomIntBetween(Random, int, int)
80+
*/
81+
public static int atLeast(Random rnd, int min) {
82+
if (min < 0)
83+
throw new IllegalArgumentException("atLeast requires non-negative argument: " + min);
84+
return scaledRandomIntBetween(rnd, min, Integer.MAX_VALUE);
85+
}
86+
87+
/**
88+
* Returns a non-negative random value smaller or equal <code>max</code>. The value picked is
89+
* affected by {@link #multiplier()}.
90+
*
91+
* <p>This method is effectively an alias to:
92+
*
93+
* <pre>
94+
* scaledRandomIntBetween(0, max)
95+
* </pre>
96+
*
97+
* @see #scaledRandomIntBetween(Random, int, int)
98+
*/
99+
public static int atMost(Random rnd, int max) {
100+
if (max < 0)
101+
throw new IllegalArgumentException("atMost requires non-negative argument: " + max);
102+
return scaledRandomIntBetween(rnd, 0, max);
103+
}
104+
105+
/** Rarely returns <code>true</code> in about 10% of all calls. */
106+
public static boolean rarely(Random rnd) {
107+
return rnd.nextInt(100) >= 90;
108+
}
109+
110+
/** The exact opposite of {@link #rarely(Random)}. */
111+
public static boolean frequently(Random rnd) {
112+
return !rarely(rnd);
113+
}
114+
115+
public static <T> T randomFrom(Random rnd, T[] array) {
116+
return RandomPicks.randomFrom(rnd, array);
117+
}
118+
119+
public static <T> T randomFrom(Random rnd, List<T> list) {
120+
return RandomPicks.randomFrom(rnd, list);
121+
}
122+
123+
public static byte randomFrom(Random rnd, byte[] array) {
124+
return RandomPicks.randomFrom(rnd, array);
125+
}
126+
127+
public static short randomFrom(Random rnd, short[] array) {
128+
return RandomPicks.randomFrom(rnd, array);
129+
}
130+
131+
public static int randomFrom(Random rnd, int[] array) {
132+
return RandomPicks.randomFrom(rnd, array);
133+
}
134+
135+
public static char randomFrom(Random rnd, char[] array) {
136+
return RandomPicks.randomFrom(rnd, array);
137+
}
138+
139+
public static float randomFrom(Random rnd, float[] array) {
140+
return RandomPicks.randomFrom(rnd, array);
141+
}
142+
143+
public static long randomFrom(Random rnd, long[] array) {
144+
return RandomPicks.randomFrom(rnd, array);
145+
}
146+
147+
public static double randomFrom(Random rnd, double[] array) {
148+
return RandomPicks.randomFrom(rnd, array);
149+
}
150+
151+
//
152+
// "multiplied" or scaled value pickers. These will be affected by global multiplier.
153+
//
154+
155+
/**
156+
* A multiplier can be used to linearly scale certain values. It can be used to make data or
157+
* iterations of certain tests "heavier" for nightly runs, for example.
158+
*
159+
* <p>The default multiplier value is 1.
160+
*
161+
* @see SysProps#TESTS_MULTIPLIER
162+
*/
163+
public static double multiplier() {
164+
if (multiplier == null) {
165+
throw new RuntimeException("Multiplier not set?");
166+
}
167+
return multiplier;
168+
}
169+
170+
/**
171+
* Returns the "scaled" number of iterations for loops which can have a variable iteration count.
172+
* This method is effectively an alias to {@link #scaledRandomIntBetween(Random, int, int)}.
173+
*
174+
* @param min minimum number of iterations (inclusive).
175+
* @param max maximum number of iterations (inclusive).
176+
*/
177+
public static int iterations(Random rnd, int min, int max) {
178+
return scaledRandomIntBetween(rnd, min, max);
179+
}
180+
181+
/**
182+
* Returns a "scaled" random number between min and max (inclusive). The number of iterations will
183+
* fall between [min, max], but the selection will also try to achieve the points below:
184+
*
185+
* <ul>
186+
* <li>the multiplier can be used to move the number of iterations closer to min (if it is
187+
* smaller than 1) or closer to max (if it is larger than 1). Setting the multiplier to 0
188+
* will always result in picking min.
189+
* <li>on normal runs, the number will be closer to min than to max.
190+
* <li>on nightly runs, the number will be closer to max than to min.
191+
* </ul>
192+
*
193+
* @see #multiplier()
194+
* @param min Minimum (inclusive).
195+
* @param max Maximum (inclusive).
196+
* @return Returns a random number between min and max.
197+
*/
198+
public static int scaledRandomIntBetween(Random rnd, int min, int max) {
199+
if (min < 0) throw new IllegalArgumentException("min must be >= 0: " + min);
200+
if (min > max) throw new IllegalArgumentException("max must be >= min: " + min + ", " + max);
201+
202+
double point = Math.min(1, Math.abs(rnd.nextGaussian()) * 0.3) * multiplier();
203+
double range = max - min;
204+
int scaled = (int) Math.round(Math.min(point * range, range));
205+
return min + scaled;
206+
}
207+
208+
public static boolean randomBoolean(Random rnd) {
209+
return rnd.nextBoolean();
210+
}
211+
212+
public static byte randomByte(Random rnd) {
213+
return (byte) rnd.nextInt();
214+
}
215+
216+
public static short randomShort(Random rnd) {
217+
return (short) rnd.nextInt();
218+
}
219+
220+
public static int randomInt(Random rnd) {
221+
return rnd.nextInt();
222+
}
223+
224+
public static float randomFloat(Random rnd) {
225+
return rnd.nextFloat();
226+
}
227+
228+
public static double randomDouble(Random rnd) {
229+
return rnd.nextDouble();
230+
}
231+
232+
public static long randomLong(Random rnd) {
233+
return rnd.nextLong();
234+
}
235+
236+
public static double randomGaussian(Random rnd) {
237+
return rnd.nextGaussian();
238+
}
239+
240+
/**
241+
* Return a random Locale from the available locales on the system.
242+
*
243+
* <p>Warning: This test assumes the returned array of locales is repeatable from jvm execution to
244+
* jvm execution. It _may_ be different from jvm to jvm and as such, it can render tests execute
245+
* in a different way.
246+
*/
247+
public static Locale randomLocale(Random rnd) {
248+
Locale[] availableLocales = Locale.getAvailableLocales();
249+
Arrays.sort(availableLocales, Comparator.comparing(Locale::toString));
250+
return randomFrom(rnd, availableLocales);
251+
}
252+
253+
/**
254+
* Return a random TimeZone from the available timezones on the system.
255+
*
256+
* <p>Warning: This test assumes the returned array of time zones is repeatable from jvm execution
257+
* to jvm execution. It _may_ be different from jvm to jvm and as such, it can render tests
258+
* execute in a different way.
259+
*/
260+
public static TimeZone randomTimeZone(Random rnd) {
261+
final String[] availableIDs = TimeZone.getAvailableIDs();
262+
Arrays.sort(availableIDs);
263+
return TimeZone.getTimeZone(randomFrom(rnd, availableIDs));
264+
}
265+
266+
public static String randomAsciiLettersOfLengthBetween(
267+
Random rnd, int minLetters, int maxLetters) {
268+
return RandomStrings.randomAsciiLettersOfLengthBetween(rnd, minLetters, maxLetters);
269+
}
270+
271+
public static String randomAsciiLettersOfLength(Random rnd, int codeUnits) {
272+
return RandomStrings.randomAsciiLettersOfLength(rnd, codeUnits);
273+
}
274+
275+
public static String randomAsciiAlphanumOfLengthBetween(
276+
Random rnd, int minCodeUnits, int maxCodeUnits) {
277+
return RandomStrings.randomAsciiAlphanumOfLengthBetween(rnd, minCodeUnits, maxCodeUnits);
278+
}
279+
280+
public static String randomAsciiAlphanumOfLength(Random rnd, int codeUnits) {
281+
return RandomStrings.randomAsciiAlphanumOfLength(rnd, codeUnits);
282+
}
283+
284+
public static String randomUnicodeOfLengthBetween(
285+
Random rnd, int minCodeUnits, int maxCodeUnits) {
286+
return RandomStrings.randomUnicodeOfLengthBetween(rnd, minCodeUnits, maxCodeUnits);
287+
}
288+
289+
public static String randomUnicodeOfLength(Random rnd, int codeUnits) {
290+
return RandomStrings.randomUnicodeOfLength(rnd, codeUnits);
291+
}
292+
293+
public static String randomUnicodeOfCodepointLengthBetween(
294+
Random rnd, int minCodePoints, int maxCodePoints) {
295+
return RandomStrings.randomUnicodeOfCodepointLengthBetween(rnd, minCodePoints, maxCodePoints);
296+
}
297+
298+
public static String randomUnicodeOfCodepointLength(Random rnd, int codePoints) {
299+
return RandomStrings.randomUnicodeOfCodepointLength(rnd, codePoints);
300+
}
301+
302+
public static String randomRealisticUnicodeOfLengthBetween(
303+
Random rnd, int minCodeUnits, int maxCodeUnits) {
304+
return RandomStrings.randomRealisticUnicodeOfLengthBetween(rnd, minCodeUnits, maxCodeUnits);
305+
}
306+
307+
public static String randomRealisticUnicodeOfLength(Random rnd, int codeUnits) {
308+
return RandomStrings.randomRealisticUnicodeOfLength(rnd, codeUnits);
309+
}
310+
311+
public static String randomRealisticUnicodeOfCodepointLengthBetween(
312+
Random rnd, int minCodePoints, int maxCodePoints) {
313+
return RandomStrings.randomRealisticUnicodeOfCodepointLengthBetween(
314+
rnd, minCodePoints, maxCodePoints);
315+
}
316+
317+
public static String randomRealisticUnicodeOfCodepointLength(Random rnd, int codePoints) {
318+
return RandomStrings.randomRealisticUnicodeOfCodepointLength(rnd, codePoints);
319+
}
320+
}

randomizedtesting-jupiter/src/main/java/com/carrotsearch/randomizedtesting/jupiter/SysProps.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@ public enum SysProps {
1919
* A boolean property that enables stricter sanity assertions (including forbidding thread-shared
2020
* access to the returned {@link Random} instances, which makes tests more predictable).
2121
*/
22-
TESTS_RANDOM_ASSERTING("tests.random.asserting");
22+
TESTS_RANDOM_ASSERTING("tests.random.asserting"),
23+
24+
/**
25+
* A "multiplier" for certain methods that return random values in {@link RandomizedTest}.
26+
*
27+
* @see RandomizedTest#multiplier()
28+
*/
29+
TESTS_MULTIPLIER("tests.multiplier");
2330

2431
public final String propertyKey;
2532

randomizedtesting-jupiter/src/main/java/com/carrotsearch/randomizedtesting/jupiter/generators/RandomNumbers.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
* @see BiasedNumbers
1010
*/
1111
public final class RandomNumbers {
12+
/**
13+
* @deprecated Will be removed. Use {@link #randomIntInRange(Random, int, int) }.
14+
*/
15+
@Deprecated(forRemoval = true)
16+
public static int randomIntBetween(Random r, int min, int max) {
17+
return randomIntInRange(r, min, max);
18+
}
19+
1220
/** A random integer between <code>min</code> (inclusive) and <code>max</code> (inclusive). */
1321
public static int randomIntInRange(Random r, int min, int max) {
1422
assert max >= min : "max must be >= min: " + min + ", " + max;
@@ -20,6 +28,14 @@ public static int randomIntInRange(Random r, int min, int max) {
2028
}
2129
}
2230

31+
/**
32+
* @deprecated Will be removed. Use {@link #randomLongInRange(Random, long, long)}.
33+
*/
34+
@Deprecated(forRemoval = true)
35+
public static long randomLongBetween(Random r, long min, long max) {
36+
return randomLongInRange(r, min, max);
37+
}
38+
2339
/** A random long between <code>min</code> (inclusive) and <code>max</code> (inclusive). */
2440
public static long randomLongInRange(Random r, long min, long max) {
2541
assert max >= min : "max must be >= min: " + min + ", " + max;

randomizedtesting-jupiter/src/main/java/module-info.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
exports com.carrotsearch.randomizedtesting.jupiter.generators;
1010

1111
opens com.carrotsearch.randomizedtesting.jupiter.internals;
12+
opens com.carrotsearch.randomizedtesting.jupiter to
13+
org.junit.platform.commons;
1214

1315
provides org.junit.jupiter.api.extension.Extension with
1416
RandomizedContextExtension;

0 commit comments

Comments
 (0)