Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Commit acde065

Browse files
committed
Replace NTESTS with better configuration
1 parent 70d9e16 commit acde065

File tree

3 files changed

+135
-24
lines changed

3 files changed

+135
-24
lines changed

crates/libm-test/src/gen/domain_logspace.rs

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,30 @@ use libm::support::{IntTy, MinInt};
44

55
use crate::domain::HasDomain;
66
use crate::op::OpITy;
7+
use crate::run_cfg::{GeneratorKind, TestAction};
78
use crate::{CheckCtx, MathOp, logspace};
89

9-
/// Number of tests to run.
10-
// FIXME(ntests): replace this with a more logical algorithm
11-
const NTESTS: usize = {
12-
if cfg!(optimizations_enabled) {
13-
if crate::emulated()
14-
|| !cfg!(target_pointer_width = "64")
15-
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"))
16-
{
17-
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run
18-
// in QEMU.
19-
100_000
20-
} else {
21-
5_000_000
22-
}
23-
} else {
24-
// Without optimizations just run a quick check
25-
800
26-
}
27-
};
28-
2910
/// Create a range of logarithmically spaced inputs within a function's domain.
3011
///
3112
/// This allows us to get reasonably thorough coverage without wasting time on values that are
3213
/// NaN or out of range. Random tests will still cover values that are excluded here.
33-
pub fn get_test_cases<Op>(_ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
14+
pub fn get_test_cases<Op>(ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
3415
where
3516
Op: MathOp + HasDomain<Op::FTy>,
36-
IntTy<Op::FTy>: TryFrom<usize>,
17+
IntTy<Op::FTy>: TryFrom<u64>,
3718
{
3819
let domain = Op::DOMAIN;
20+
let action = crate::run_cfg::get_iterations(ctx, GeneratorKind::Logspace, 0);
21+
let ntests = match action {
22+
TestAction::Iterations(n) => n,
23+
TestAction::Run => unimplemented!(),
24+
TestAction::Skip => unimplemented!(),
25+
};
26+
27+
// We generate logspaced inputs within a specific range, excluding values that are out of
28+
// range in order to make iterations useful (random tests still cover the full range).
3929
let start = domain.range_start();
4030
let end = domain.range_end();
41-
let steps = OpITy::<Op>::try_from(NTESTS).unwrap_or(OpITy::<Op>::MAX);
31+
let steps = OpITy::<Op>::try_from(ntests).unwrap_or(OpITy::<Op>::MAX);
4232
logspace(start, end, steps).map(|v| (v,))
4333
}

crates/libm-test/src/lib.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub use libm::support::{Float, Int, IntTy, MinInt};
1616
pub use num::{FloatExt, logspace};
1717
pub use op::{BaseName, FloatTy, Identifier, MathOp, OpCFn, OpFTy, OpRustFn, OpRustRet, Ty};
1818
pub use precision::{MaybeOverride, SpecialCase, default_ulp};
19-
pub use run_cfg::{CheckBasis, CheckCtx};
19+
pub use run_cfg::{CheckBasis, CheckCtx, EXTENSIVE_ENV, GeneratorKind, TestAction, get_iterations};
2020
pub use test_traits::{CheckOutput, GenerateInput, Hex, TupleCall};
2121

2222
/// Result type for tests is usually from `anyhow`. Most times there is no success value to
@@ -34,3 +34,12 @@ pub const fn emulated() -> bool {
3434
Some(_) => true,
3535
}
3636
}
37+
38+
/// True if `CI` is set and nonempty.
39+
pub const fn ci() -> bool {
40+
match option_env!("CI") {
41+
Some(s) if s.is_empty() => false,
42+
None => false,
43+
Some(_) => true,
44+
}
45+
}

crates/libm-test/src/run_cfg.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,115 @@ pub enum CheckBasis {
4949
/// Check against infinite precision (MPFR).
5050
Mpfr,
5151
}
52+
53+
/// The different kinds of tests that we run
54+
#[derive(Clone, Debug, PartialEq, Eq)]
55+
pub enum GeneratorKind {
56+
Extensive,
57+
Logspace,
58+
Random,
59+
EdgeCases,
60+
}
61+
62+
#[derive(Clone, Debug, PartialEq, Eq)]
63+
pub enum TestAction {
64+
Run,
65+
Iterations(u64),
66+
Skip,
67+
}
68+
69+
/// A list of all functions that should get extensive tests
70+
static EXTENSIVE: LazyLock<Vec<Identifier>> = LazyLock::new(|| {
71+
let var = env::var(EXTENSIVE_ENV).unwrap_or_default();
72+
let list = var.split(",").filter(|s| !s.is_empty()).collect::<Vec<_>>();
73+
let mut ret = Vec::new();
74+
75+
for item in list {
76+
match item {
77+
"all" => ret = Identifier::ALL.to_owned(),
78+
"all_f32" => ret.extend(
79+
Identifier::ALL
80+
.iter()
81+
.filter(|id| matches!(id.math_op().float_ty, FloatTy::F32))
82+
.copied(),
83+
),
84+
"all_f64" => ret.extend(
85+
Identifier::ALL
86+
.iter()
87+
.filter(|id| matches!(id.math_op().float_ty, FloatTy::F64))
88+
.copied(),
89+
),
90+
s => ret.push(
91+
Identifier::from_str(s).unwrap_or_else(|| panic!("unrecognized test name `{s}`")),
92+
),
93+
}
94+
}
95+
96+
ret
97+
});
98+
99+
pub fn get_iterations(ctx: &CheckCtx, test_ty: GeneratorKind, argnum: usize) -> TestAction {
100+
// TODO: use argnum to figure out that the second arg of `jn` should be reduced
101+
102+
let id = ctx.fn_ident;
103+
// Run more musl tests if we don't have mp
104+
let run_mp = cfg!(feature = "test-multiprecision");
105+
let run_musl = cfg!(feature = "build-musl");
106+
let run_extensive = EXTENSIVE.contains(&id);
107+
108+
// Extensive tests handle their own iterations
109+
if matches!(test_ty, GeneratorKind::Extensive) {
110+
return if run_extensive { TestAction::Run } else { TestAction::Skip };
111+
}
112+
113+
// Ideally run 5M tests
114+
let mut primary_test_iter = 5_000_000;
115+
116+
// Tests are pretty slow on non-64-bit targets, x86 MacOS, and targets that run in QEMU.
117+
let slow_on_ci = crate::emulated()
118+
|| !cfg!(target_pointer_width = "64")
119+
|| cfg!(all(target_arch = "x86_64", target_vendor = "apple"));
120+
let slow_platform = slow_on_ci && crate::ci();
121+
if slow_platform {
122+
primary_test_iter = 100_000;
123+
}
124+
125+
let op = id.math_op();
126+
127+
match op.float_ty {
128+
FloatTy::F16 | FloatTy::F32 => (),
129+
FloatTy::F64 | FloatTy::F128 => primary_test_iter *= 4,
130+
};
131+
132+
// Provide more space for functions with multiple arguments
133+
let arg_multiplier = 1 << (op.rust_sig.args.len() - 1);
134+
primary_test_iter *= arg_multiplier;
135+
136+
let mut secondary_test_iter = primary_test_iter / 100;
137+
138+
let mut primary_test = GeneratorKind::Logspace;
139+
140+
// TODO
141+
// Still run some random tests for NaN and similar, but most of the tests should be
142+
// logspace.
143+
let has_logspace_test = true;
144+
if !has_logspace_test {
145+
primary_test = GeneratorKind::Random;
146+
}
147+
148+
if run_extensive {
149+
primary_test_iter /= 100;
150+
secondary_test_iter /= 100;
151+
}
152+
153+
// Without optimizations just run a quick check
154+
if !cfg!(optimizations_enabled) {
155+
primary_test_iter = 800;
156+
secondary_test_iter = 800;
157+
}
158+
159+
let ntests = if test_ty == primary_test { primary_test_iter } else { secondary_test_iter };
160+
161+
eprintln!("running {ntests:?} tests for {test_ty:?} `{id}`");
162+
TestAction::Iterations(ntests)
163+
}

0 commit comments

Comments
 (0)