Skip to content

Commit 5939d5d

Browse files
committed
test-float-parse: Use an overrideable random seed
Currently `test-float-parse` is mostly random tests which take a fixed seed, meaning the same set of values get tested for each invocation. This isn't ideal because we don't get the true benefit of randomness, which is to have better coverage over time. Improve this by using a randomly generated seed, which can also be set via env. The seed is printed at the beginning of each run so it is easy to reproduce failures using the same test set, if needed. Typically it isn't great to have fuzzing randomness in tests that get run in CI because it can lead to spurious failures. However, this is a test for which failure should never happen becasue the algorithms are reasonably well proven, so if one does occur it represents a very unexpected bug that needs to be addressed. The error message is updated to strongly recommend reporting before retrying and to include details on how to reproduce.
1 parent fccd826 commit 5939d5d

6 files changed

Lines changed: 51 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ version = "0.21.7"
276276
source = "registry+https://github.com/rust-lang/crates.io-index"
277277
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
278278

279+
[[package]]
280+
name = "base64"
281+
version = "0.22.1"
282+
source = "registry+https://github.com/rust-lang/crates.io-index"
283+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
284+
279285
[[package]]
280286
name = "basic-toml"
281287
version = "0.1.10"
@@ -4887,7 +4893,7 @@ version = "0.0.0"
48874893
dependencies = [
48884894
"arrayvec",
48894895
"askama",
4890-
"base64",
4896+
"base64 0.21.7",
48914897
"expect-test",
48924898
"indexmap",
48934899
"itertools",
@@ -5542,6 +5548,7 @@ checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
55425548
name = "test-float-parse"
55435549
version = "0.1.0"
55445550
dependencies = [
5551+
"base64 0.22.1",
55455552
"indicatif",
55465553
"num",
55475554
"rand 0.10.0",

src/tools/test-float-parse/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2024"
55
publish = false
66

77
[dependencies]
8+
base64 = "0.22.1"
89
indicatif = { version = "0.17.11", default-features = false }
910
num = "0.4.3"
1011
rand = "0.10.0"

src/tools/test-float-parse/src/gen_/fuzz.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ where
6262
}
6363

6464
fn new() -> Self {
65-
let rng = ChaCha8Rng::from_seed(SEED);
65+
let rng = ChaCha8Rng::from_seed(SEED.0);
6666

6767
Self { iter: 0..Self::total_tests(), rng, marker: PhantomData }
6868
}

src/tools/test-float-parse/src/gen_/many_digits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl<F: Float> Generator<F> for RandDigits<F> {
3939
}
4040

4141
fn new() -> Self {
42-
let rng = ChaCha8Rng::from_seed(SEED);
42+
let rng = ChaCha8Rng::from_seed(SEED.0);
4343
let range = Uniform::try_from(0..10).unwrap();
4444

4545
Self { rng, iter: 0..ITERATIONS, uniform: range, marker: PhantomData }

src/tools/test-float-parse/src/lib.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ use std::any::type_name;
1010
use std::cmp::min;
1111
use std::ops::RangeInclusive;
1212
use std::process::ExitCode;
13-
use std::sync::OnceLock;
1413
use std::sync::atomic::{AtomicU64, Ordering};
15-
use std::{fmt, time};
14+
use std::sync::{LazyLock, OnceLock};
15+
use std::{env, fmt, time};
1616

17+
use base64::engine::Engine;
18+
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
19+
use rand::TryRng;
1720
use rand::distr::{Distribution, StandardUniform};
21+
use rand::rngs::SysRng;
1822
use rayon::prelude::*;
1923
use time::{Duration, Instant};
2024
use traits::{Float, Generator, Int};
@@ -44,8 +48,26 @@ const MAX_BITS_FOR_EXHAUUSTIVE: u32 = 32;
4448
/// `--skip-huge`.
4549
const HUGE_TEST_CUTOFF: u64 = 5_000_000;
4650

47-
/// Seed for tests that use a deterministic RNG.
48-
const SEED: [u8; 32] = *b"3.141592653589793238462643383279";
51+
const SEED_ENV: &str = "TEST_FLOAT_PARSE_SEED";
52+
53+
/// Seed for tests that use a deterministic RNG, and its b64 representation for printing. Taken
54+
/// from env if provided,
55+
static SEED: LazyLock<([u8; 32], Box<str>)> = LazyLock::new(|| {
56+
let seed = match env::var(SEED_ENV) {
57+
Ok(s) => URL_SAFE_NO_PAD
58+
.decode(s)
59+
.ok()
60+
.and_then(|decoded| <[u8; 32]>::try_from(decoded).ok())
61+
.unwrap_or_else(|| panic!("{SEED_ENV} must be 32 bytes, base64 encoded")),
62+
Err(_) => {
63+
let mut seed = [0u8; 32];
64+
SysRng.try_fill_bytes(&mut seed).unwrap();
65+
seed
66+
}
67+
};
68+
let encoded = URL_SAFE_NO_PAD.encode(&seed).into_boxed_str();
69+
(seed, encoded)
70+
});
4971

5072
/// Global configuration.
5173
#[derive(Debug)]
@@ -78,8 +100,10 @@ pub fn run(cfg: Config, include: &[String], exclude: &[String]) -> ExitCode {
78100
let threads = std::thread::available_parallelism().map(Into::into).unwrap_or(0) * 3 / 2;
79101
rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
80102

103+
println!("Starting test runner");
104+
println!("Using {SEED_ENV}={}`", SEED.1);
81105
let mut tests = register_tests(&cfg);
82-
println!("registered");
106+
println!("Tests registered");
83107
let initial_tests: Vec<_> = tests.iter().map(|t| t.name.clone()).collect();
84108

85109
let unmatched: Vec<_> = include

src/tools/test-float-parse/src/ui.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::time::Duration;
88

99
use indicatif::{ProgressBar, ProgressStyle};
1010

11-
use crate::{Completed, Config, EarlyExit, FinishedAll, TestInfo};
11+
use crate::{Completed, Config, EarlyExit, FinishedAll, SEED, SEED_ENV, TestInfo};
1212

1313
/// Templates for progress bars.
1414
const PB_TEMPLATE: &str = "[{elapsed:3} {percent:3}%] {bar:20.cyan/blue} NAME \
@@ -135,6 +135,16 @@ pub fn finish_all(tests: &[TestInfo], total_elapsed: Duration, cfg: &Config) ->
135135
);
136136

137137
if failed_generators > 0 || stopped_generators > 0 {
138+
println!();
139+
println!(
140+
"ERROR: Some float parsing/printing tests failed.\n\
141+
\n\
142+
If you ever encounter this failure when not expected, PLEASE OPEN AN ISSUE!! The \
143+
failure will likely go away on the next run, but may represent a real bug.\n\
144+
\n\
145+
To reproduce, rerun with the same seed: {}={}",
146+
SEED_ENV, SEED.1
147+
);
138148
ExitCode::FAILURE
139149
} else {
140150
ExitCode::SUCCESS

0 commit comments

Comments
 (0)