| 1 | use std::hash::{DefaultHasher, Hasher}; |
| 2 | use std::time::{SystemTime, UNIX_EPOCH}; |
| 3 | |
| 4 | use crate::cli::TestOpts; |
| 5 | use crate::types::{TestDescAndFn, TestId, TestName}; |
| 6 | |
| 7 | pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option<u64> { |
| 8 | opts.shuffle_seed.or_else(|| { |
| 9 | if opts.shuffle { |
| 10 | Some( |
| 11 | SystemTimeDuration::now() |
| 12 | .duration_since(UNIX_EPOCH) |
| 13 | .expect(msg:"Failed to get system time" ) |
| 14 | .as_nanos() as u64, |
| 15 | ) |
| 16 | } else { |
| 17 | None |
| 18 | } |
| 19 | }) |
| 20 | } |
| 21 | |
| 22 | pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { |
| 23 | let test_names: Vec<&TestName> = tests.iter().map(|test: &(TestId, TestDescAndFn)| &test.1.desc.name).collect(); |
| 24 | let test_names_hash: u64 = calculate_hash(&test_names); |
| 25 | let mut rng: Rng = Rng::new(shuffle_seed, extra:test_names_hash); |
| 26 | shuffle(&mut rng, slice:tests); |
| 27 | } |
| 28 | |
| 29 | // `shuffle` is from `rust-analyzer/src/cli/analysis_stats.rs`. |
| 30 | fn shuffle<T>(rng: &mut Rng, slice: &mut [T]) { |
| 31 | for i: usize in 0..slice.len() { |
| 32 | randomize_first(rng, &mut slice[i..]); |
| 33 | } |
| 34 | |
| 35 | fn randomize_first<T>(rng: &mut Rng, slice: &mut [T]) { |
| 36 | assert!(!slice.is_empty()); |
| 37 | let idx: usize = rng.rand_range(0..slice.len() as u64) as usize; |
| 38 | slice.swap(a:0, b:idx); |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | struct Rng { |
| 43 | state: u64, |
| 44 | extra: u64, |
| 45 | } |
| 46 | |
| 47 | impl Rng { |
| 48 | fn new(seed: u64, extra: u64) -> Self { |
| 49 | Self { state: seed, extra } |
| 50 | } |
| 51 | |
| 52 | fn rand_range(&mut self, range: core::ops::Range<u64>) -> u64 { |
| 53 | self.rand_u64() % (range.end - range.start) + range.start |
| 54 | } |
| 55 | |
| 56 | fn rand_u64(&mut self) -> u64 { |
| 57 | self.state = calculate_hash(&(self.state, self.extra)); |
| 58 | self.state |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // `calculate_hash` is from `core/src/hash/mod.rs`. |
| 63 | fn calculate_hash<T: core::hash::Hash>(t: &T) -> u64 { |
| 64 | let mut s: DefaultHasher = DefaultHasher::new(); |
| 65 | t.hash(&mut s); |
| 66 | s.finish() |
| 67 | } |
| 68 | |