1
use rand::Rng;
2
use rand::SeedableRng;
3
use rand::rngs::StdRng;
4

            
5
use crate::test_logger;
6

            
7
/// Returns the seed for a random test, honoring the `MERC_SEED` environment
8
/// variable when set so that failures can be reproduced, and otherwise picking a
9
/// fresh random seed. The chosen seed is printed for reproducibility.
10
3336
fn random_test_seed() -> u64 {
11
3336
    if let Ok(seed_str) = std::env::var("MERC_SEED") {
12
        let seed = seed_str.parse::<u64>().expect("MERC_SEED must be a valid u64");
13
        println!("seed: {seed} (set by MERC_SEED)");
14
        seed
15
    } else {
16
3336
        let seed: u64 = rand::random();
17
3336
        println!("random seed: {seed} (use MERC_SEED=<seed> to set fixed seed)");
18
3336
        seed
19
    }
20
3336
}
21

            
22
/// Constructs a random number generator that should be used in random tests. Prints its seed to the console for reproducibility.
23
110
pub fn random_test<F>(iterations: usize, mut test_function: F)
24
110
where
25
110
    F: FnMut(&mut StdRng),
26
{
27
110
    test_logger();
28

            
29
110
    let mut rng = StdRng::seed_from_u64(random_test_seed());
30
13228
    for _ in 0..iterations {
31
13228
        test_function(&mut rng);
32
13228
    }
33
110
}
34

            
35
6
pub fn random_test_threads<C, F, G>(iterations: usize, num_threads: usize, init_function: G, test_function: F)
36
6
where
37
6
    C: Send + 'static,
38
6
    F: Fn(&mut StdRng, &mut C) + Copy + Send + Sync + 'static,
39
6
    G: Fn() -> C,
40
{
41
6
    test_logger();
42

            
43
6
    let mut threads = vec![];
44

            
45
6
    let mut rng = StdRng::seed_from_u64(random_test_seed());
46

            
47
6
    for _ in 0..num_threads {
48
60
        let mut rng = StdRng::seed_from_u64(rng.next_u64());
49
60
        let mut init = init_function();
50
60
        threads.push(std::thread::spawn(move || {
51
143200
            for _ in 0..iterations {
52
143200
                test_function(&mut rng, &mut init);
53
143200
            }
54
60
        }));
55
    }
56

            
57
    // Propagate a worker panic so a failing assertion fails the test instead of
58
    // being silently swallowed.
59
60
    for thread in threads {
60
60
        thread.join().unwrap();
61
60
    }
62
6
}