Merge pull request rust-lang/libm#378 from tgross35/run-cfg
Use `CheckCtx` in more places
This commit is contained in:
commit
3166dcd51d
7 changed files with 72 additions and 52 deletions
|
|
@ -14,7 +14,7 @@ use std::{env, fs};
|
|||
|
||||
use libm_test::domain::HasDomain;
|
||||
use libm_test::gen::{domain_logspace, edge_cases};
|
||||
use libm_test::{MathOp, op};
|
||||
use libm_test::{CheckBasis, CheckCtx, MathOp, op};
|
||||
|
||||
const JL_PLOT: &str = "examples/plot_file.jl";
|
||||
|
||||
|
|
@ -54,30 +54,32 @@ fn plot_one_operator<Op>(out_dir: &Path, config: &mut String)
|
|||
where
|
||||
Op: MathOp<FTy = f32> + HasDomain<f32>,
|
||||
{
|
||||
let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr);
|
||||
plot_one_generator(
|
||||
out_dir,
|
||||
Op::BASE_NAME.as_str(),
|
||||
&ctx,
|
||||
"logspace",
|
||||
config,
|
||||
domain_logspace::get_test_cases::<Op>(),
|
||||
domain_logspace::get_test_cases::<Op>(&ctx),
|
||||
);
|
||||
plot_one_generator(
|
||||
out_dir,
|
||||
Op::BASE_NAME.as_str(),
|
||||
&ctx,
|
||||
"edge_cases",
|
||||
config,
|
||||
edge_cases::get_test_cases::<Op, _>(),
|
||||
edge_cases::get_test_cases::<Op, _>(&ctx),
|
||||
);
|
||||
}
|
||||
|
||||
/// Plot the output of a single generator.
|
||||
fn plot_one_generator(
|
||||
out_dir: &Path,
|
||||
fn_name: &str,
|
||||
ctx: &CheckCtx,
|
||||
gen_name: &str,
|
||||
config: &mut String,
|
||||
gen: impl Iterator<Item = (f32,)>,
|
||||
) {
|
||||
let fn_name = ctx.base_name_str;
|
||||
let text_file = out_dir.join(format!("input-{fn_name}-{gen_name}.txt"));
|
||||
|
||||
let f = fs::File::create(&text_file).unwrap();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use libm::support::{IntTy, MinInt};
|
|||
|
||||
use crate::domain::HasDomain;
|
||||
use crate::op::OpITy;
|
||||
use crate::{MathOp, logspace};
|
||||
use crate::{CheckCtx, MathOp, logspace};
|
||||
|
||||
/// Number of tests to run.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
|
|
@ -30,7 +30,7 @@ const NTESTS: usize = {
|
|||
///
|
||||
/// This allows us to get reasonably thorough coverage without wasting time on values that are
|
||||
/// NaN or out of range. Random tests will still cover values that are excluded here.
|
||||
pub fn get_test_cases<Op>() -> impl Iterator<Item = (Op::FTy,)>
|
||||
pub fn get_test_cases<Op>(_ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
|
||||
where
|
||||
Op: MathOp + HasDomain<Op::FTy>,
|
||||
IntTy<Op::FTy>: TryFrom<usize>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use libm::support::Float;
|
||||
|
||||
use crate::domain::HasDomain;
|
||||
use crate::{FloatExt, MathOp};
|
||||
use crate::{CheckCtx, FloatExt, MathOp};
|
||||
|
||||
/// Number of values near an interesting point to check.
|
||||
// FIXME(ntests): replace this with a more logical algorithm
|
||||
|
|
@ -14,7 +14,7 @@ const AROUND: usize = 100;
|
|||
const MAX_CHECK_POINTS: usize = 10;
|
||||
|
||||
/// Create a list of values around interesting points (infinities, zeroes, NaNs).
|
||||
pub fn get_test_cases<Op, F>() -> impl Iterator<Item = (F,)>
|
||||
pub fn get_test_cases<Op, F>(_ctx: &CheckCtx) -> impl Iterator<Item = (F,)>
|
||||
where
|
||||
Op: MathOp<FTy = F> + HasDomain<F>,
|
||||
F: Float,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub mod mpfloat;
|
|||
mod num;
|
||||
pub mod op;
|
||||
mod precision;
|
||||
mod run_cfg;
|
||||
mod test_traits;
|
||||
|
||||
pub use f8_impl::f8;
|
||||
|
|
@ -15,7 +16,8 @@ pub use libm::support::{Float, Int, IntTy, MinInt};
|
|||
pub use num::{FloatExt, logspace};
|
||||
pub use op::{BaseName, FloatTy, Identifier, MathOp, OpCFn, OpFTy, OpRustFn, OpRustRet, Ty};
|
||||
pub use precision::{MaybeOverride, SpecialCase, default_ulp};
|
||||
pub use test_traits::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, Hex, TupleCall};
|
||||
pub use run_cfg::{CheckBasis, CheckCtx};
|
||||
pub use test_traits::{CheckOutput, GenerateInput, Hex, TupleCall};
|
||||
|
||||
/// Result type for tests is usually from `anyhow`. Most times there is no success value to
|
||||
/// propagate.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
//! Configuration for how tests get run.
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::{BaseName, FloatTy, Identifier, op};
|
||||
|
||||
pub const EXTENSIVE_ENV: &str = "LIBM_EXTENSIVE_TESTS";
|
||||
|
||||
/// Context passed to [`CheckOutput`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CheckCtx {
|
||||
/// Allowed ULP deviation
|
||||
pub ulp: u32,
|
||||
pub fn_ident: Identifier,
|
||||
pub base_name: BaseName,
|
||||
/// Function name.
|
||||
pub fn_name: &'static str,
|
||||
/// Return the unsuffixed version of the function name.
|
||||
pub base_name_str: &'static str,
|
||||
/// Source of truth for tests.
|
||||
pub basis: CheckBasis,
|
||||
}
|
||||
|
||||
impl CheckCtx {
|
||||
/// Create a new check context, using the default ULP for the function.
|
||||
pub fn new(fn_ident: Identifier, basis: CheckBasis) -> Self {
|
||||
let mut ret = Self {
|
||||
ulp: 0,
|
||||
fn_ident,
|
||||
fn_name: fn_ident.as_str(),
|
||||
base_name: fn_ident.base_name(),
|
||||
base_name_str: fn_ident.base_name().as_str(),
|
||||
basis,
|
||||
};
|
||||
ret.ulp = crate::default_ulp(&ret);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible items to test against
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CheckBasis {
|
||||
/// Check against Musl's math sources.
|
||||
Musl,
|
||||
/// Check against infinite precision (MPFR).
|
||||
Mpfr,
|
||||
}
|
||||
|
|
@ -11,44 +11,7 @@ use std::fmt;
|
|||
|
||||
use anyhow::{Context, bail, ensure};
|
||||
|
||||
use crate::{BaseName, Float, Identifier, Int, MaybeOverride, SpecialCase, TestResult};
|
||||
|
||||
/// Context passed to [`CheckOutput`].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CheckCtx {
|
||||
/// Allowed ULP deviation
|
||||
pub ulp: u32,
|
||||
pub fn_ident: Identifier,
|
||||
pub base_name: BaseName,
|
||||
/// Function name.
|
||||
pub fn_name: &'static str,
|
||||
/// Source of truth for tests.
|
||||
pub basis: CheckBasis,
|
||||
}
|
||||
|
||||
impl CheckCtx {
|
||||
/// Create a new check context, using the default ULP for the function.
|
||||
pub fn new(fn_ident: Identifier, basis: CheckBasis) -> Self {
|
||||
let mut ret = Self {
|
||||
ulp: 0,
|
||||
fn_ident,
|
||||
fn_name: fn_ident.as_str(),
|
||||
base_name: fn_ident.base_name(),
|
||||
basis,
|
||||
};
|
||||
ret.ulp = crate::default_ulp(&ret);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible items to test against
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum CheckBasis {
|
||||
/// Check against Musl's math sources.
|
||||
Musl,
|
||||
/// Check against infinite precision (MPFR).
|
||||
Mpfr,
|
||||
}
|
||||
use crate::{CheckCtx, Float, Int, MaybeOverride, SpecialCase, TestResult};
|
||||
|
||||
/// Implement this on types that can generate a sequence of tuples for test input.
|
||||
pub trait GenerateInput<TupleArgs> {
|
||||
|
|
|
|||
|
|
@ -83,21 +83,21 @@ macro_rules! mp_domain_tests {
|
|||
$(#[$meta])*
|
||||
fn [< mp_edge_case_ $fn_name >]() {
|
||||
type Op = libm_test::op::$fn_name::Routine;
|
||||
domain_test_runner::<Op>(edge_cases::get_test_cases::<Op, _>());
|
||||
domain_test_runner::<Op, _>(edge_cases::get_test_cases::<Op, _>);
|
||||
}
|
||||
|
||||
#[test]
|
||||
$(#[$meta])*
|
||||
fn [< mp_logspace_ $fn_name >]() {
|
||||
type Op = libm_test::op::$fn_name::Routine;
|
||||
domain_test_runner::<Op>(domain_logspace::get_test_cases::<Op>());
|
||||
domain_test_runner::<Op, _>(domain_logspace::get_test_cases::<Op>);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Test a single routine against domaine-aware inputs.
|
||||
fn domain_test_runner<Op>(cases: impl Iterator<Item = (Op::FTy,)>)
|
||||
fn domain_test_runner<Op, I>(gen: impl FnOnce(&CheckCtx) -> I)
|
||||
where
|
||||
// Complicated generics...
|
||||
// The operation must take a single float argument (unary only)
|
||||
|
|
@ -108,9 +108,11 @@ where
|
|||
Op: HasDomain<Op::FTy>,
|
||||
// The single float argument tuple must be able to call the `RustFn` and return `RustRet`
|
||||
(OpFTy<Op>,): TupleCall<OpRustFn<Op>, Output = OpRustRet<Op>>,
|
||||
I: Iterator<Item = (Op::FTy,)>,
|
||||
{
|
||||
let mut mp_vals = Op::new_mp();
|
||||
let ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr);
|
||||
let cases = gen(&ctx);
|
||||
|
||||
for input in cases {
|
||||
let mp_res = Op::run(&mut mp_vals, input);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue