From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- library/test/src/helpers/concurrency.rs | 14 +++++++ library/test/src/helpers/exit_code.rs | 20 ++++++++++ library/test/src/helpers/isatty.rs | 32 ++++++++++++++++ library/test/src/helpers/metrics.rs | 50 ++++++++++++++++++++++++ library/test/src/helpers/mod.rs | 8 ++++ library/test/src/helpers/shuffle.rs | 67 +++++++++++++++++++++++++++++++++ 6 files changed, 191 insertions(+) create mode 100644 library/test/src/helpers/concurrency.rs create mode 100644 library/test/src/helpers/exit_code.rs create mode 100644 library/test/src/helpers/isatty.rs create mode 100644 library/test/src/helpers/metrics.rs create mode 100644 library/test/src/helpers/mod.rs create mode 100644 library/test/src/helpers/shuffle.rs (limited to 'library/test/src/helpers') diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs new file mode 100644 index 000000000..eb2111573 --- /dev/null +++ b/library/test/src/helpers/concurrency.rs @@ -0,0 +1,14 @@ +//! Helper module which helps to determine amount of threads to be used +//! during tests execution. +use std::{env, num::NonZeroUsize, thread}; + +pub fn get_concurrency() -> usize { + if let Ok(value) = env::var("RUST_TEST_THREADS") { + match value.parse::().ok() { + Some(n) => n.get(), + _ => panic!("RUST_TEST_THREADS is `{value}`, should be a positive integer."), + } + } else { + thread::available_parallelism().map(|n| n.get()).unwrap_or(1) + } +} diff --git a/library/test/src/helpers/exit_code.rs b/library/test/src/helpers/exit_code.rs new file mode 100644 index 000000000..f762f8881 --- /dev/null +++ b/library/test/src/helpers/exit_code.rs @@ -0,0 +1,20 @@ +//! Helper module to detect subprocess exit code. + +use std::process::ExitStatus; + +#[cfg(not(unix))] +pub fn get_exit_code(status: ExitStatus) -> Result { + status.code().ok_or_else(|| "received no exit code from child process".into()) +} + +#[cfg(unix)] +pub fn get_exit_code(status: ExitStatus) -> Result { + use std::os::unix::process::ExitStatusExt; + match status.code() { + Some(code) => Ok(code), + None => match status.signal() { + Some(signal) => Err(format!("child process exited with signal {signal}")), + None => Err("child process exited with unknown signal".into()), + }, + } +} diff --git a/library/test/src/helpers/isatty.rs b/library/test/src/helpers/isatty.rs new file mode 100644 index 000000000..874ecc376 --- /dev/null +++ b/library/test/src/helpers/isatty.rs @@ -0,0 +1,32 @@ +//! Helper module which provides a function to test +//! if stdout is a tty. + +cfg_if::cfg_if! { + if #[cfg(unix)] { + pub fn stdout_isatty() -> bool { + unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 } + } + } else if #[cfg(windows)] { + pub fn stdout_isatty() -> bool { + type DWORD = u32; + type BOOL = i32; + type HANDLE = *mut u8; + type LPDWORD = *mut u32; + const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD; + extern "system" { + fn GetStdHandle(which: DWORD) -> HANDLE; + fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL; + } + unsafe { + let handle = GetStdHandle(STD_OUTPUT_HANDLE); + let mut out = 0; + GetConsoleMode(handle, &mut out) != 0 + } + } + } else { + // FIXME: Implement isatty on SGX + pub fn stdout_isatty() -> bool { + false + } + } +} diff --git a/library/test/src/helpers/metrics.rs b/library/test/src/helpers/metrics.rs new file mode 100644 index 000000000..f77a23e68 --- /dev/null +++ b/library/test/src/helpers/metrics.rs @@ -0,0 +1,50 @@ +//! Benchmark metrics. +use std::collections::BTreeMap; + +#[derive(Clone, PartialEq, Debug, Copy)] +pub struct Metric { + value: f64, + noise: f64, +} + +impl Metric { + pub fn new(value: f64, noise: f64) -> Metric { + Metric { value, noise } + } +} + +#[derive(Clone, PartialEq)] +pub struct MetricMap(BTreeMap); + +impl MetricMap { + pub fn new() -> MetricMap { + MetricMap(BTreeMap::new()) + } + + /// Insert a named `value` (+/- `noise`) metric into the map. The value + /// must be non-negative. The `noise` indicates the uncertainty of the + /// metric, which doubles as the "noise range" of acceptable + /// pairwise-regressions on this named value, when comparing from one + /// metric to the next using `compare_to_old`. + /// + /// If `noise` is positive, then it means this metric is of a value + /// you want to see grow smaller, so a change larger than `noise` in the + /// positive direction represents a regression. + /// + /// If `noise` is negative, then it means this metric is of a value + /// you want to see grow larger, so a change larger than `noise` in the + /// negative direction represents a regression. + pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) { + let m = Metric { value, noise }; + self.0.insert(name.to_owned(), m); + } + + pub fn fmt_metrics(&self) -> String { + let v = self + .0 + .iter() + .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise)) + .collect::>(); + v.join(", ") + } +} diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs new file mode 100644 index 000000000..049cadf86 --- /dev/null +++ b/library/test/src/helpers/mod.rs @@ -0,0 +1,8 @@ +//! Module with common helpers not directly related to tests +//! but used in `libtest`. + +pub mod concurrency; +pub mod exit_code; +pub mod isatty; +pub mod metrics; +pub mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs new file mode 100644 index 000000000..ca503106c --- /dev/null +++ b/library/test/src/helpers/shuffle.rs @@ -0,0 +1,67 @@ +use crate::cli::TestOpts; +use crate::types::{TestDescAndFn, TestId, TestName}; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hasher; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub fn get_shuffle_seed(opts: &TestOpts) -> Option { + opts.shuffle_seed.or_else(|| { + if opts.shuffle { + Some( + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Failed to get system time") + .as_nanos() as u64, + ) + } else { + None + } + }) +} + +pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { + let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); + let test_names_hash = calculate_hash(&test_names); + let mut rng = Rng::new(shuffle_seed, test_names_hash); + shuffle(&mut rng, tests); +} + +// `shuffle` is from `rust-analyzer/src/cli/analysis_stats.rs`. +fn shuffle(rng: &mut Rng, slice: &mut [T]) { + for i in 0..slice.len() { + randomize_first(rng, &mut slice[i..]); + } + + fn randomize_first(rng: &mut Rng, slice: &mut [T]) { + assert!(!slice.is_empty()); + let idx = rng.rand_range(0..slice.len() as u64) as usize; + slice.swap(0, idx); + } +} + +struct Rng { + state: u64, + extra: u64, +} + +impl Rng { + fn new(seed: u64, extra: u64) -> Self { + Self { state: seed, extra } + } + + fn rand_range(&mut self, range: core::ops::Range) -> u64 { + self.rand_u64() % (range.end - range.start) + range.start + } + + fn rand_u64(&mut self) -> u64 { + self.state = calculate_hash(&(self.state, self.extra)); + self.state + } +} + +// `calculate_hash` is from `core/src/hash/mod.rs`. +fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} -- cgit v1.2.3