diff --git a/src/qc.rs b/src/qc.rs deleted file mode 100644 index 675bcc6..0000000 --- a/src/qc.rs +++ /dev/null @@ -1,311 +0,0 @@ -// When testing functions, QuickCheck (QC) uses small values for integer (`u*`/`i*`) arguments -// (~ `[-100, 100]`), but these values don't stress all the code paths in our intrinsics. Here we -// create newtypes over the primitive integer types with the goal of having full control over the -// random values that will be used to test our intrinsics. - -use std::boxed::Box; -use std::fmt; -use core::{f32, f64}; - -use quickcheck::{Arbitrary, Gen}; - -use int::LargeInt; -use float::Float; - -// Generates values in the full range of the integer type -macro_rules! arbitrary { - ($TY:ident : $ty:ident) => { - #[derive(Clone, Copy, PartialEq)] - pub struct $TY(pub $ty); - - impl Arbitrary for $TY { - fn arbitrary(g: &mut G) -> $TY - where G: Gen - { - // NOTE Generate edge cases with a 10% chance - let t = if g.gen_weighted_bool(10) { - *g.choose(&[ - $ty::min_value(), - 0, - $ty::max_value(), - ]).unwrap() - } else { - g.gen() - }; - - $TY(t) - } - - fn shrink(&self) -> Box> { - struct Shrinker { - x: $ty, - } - - impl Iterator for Shrinker { - type Item = $TY; - - fn next(&mut self) -> Option<$TY> { - self.x /= 2; - if self.x == 0 { - None - } else { - Some($TY(self.x)) - } - } - } - - if self.0 == 0 { - ::quickcheck::empty_shrinker() - } else { - Box::new(Shrinker { x: self.0 }) - } - } - } - - impl fmt::Debug for $TY { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } - } - } -} - -arbitrary!(I32: i32); -arbitrary!(U32: u32); - -// These integers are "too large". If we generate e.g. `u64` values in the full range then there's -// only `1 / 2^32` chance of seeing a value smaller than `2^32` (i.e. whose higher "word" (32-bits) -// is `0`)! But this is an important group of values to tests because we have special code paths for -// them. Instead we'll generate e.g. `u64` integers this way: uniformly pick between (a) setting the -// low word to 0 and generating a random high word, (b) vice versa: high word to 0 and random low -// word or (c) generate both words randomly. This let's cover better the code paths in our -// intrinsics. -macro_rules! arbitrary_large { - ($TY:ident : $ty:ident) => { - #[derive(Clone, Copy, PartialEq)] - pub struct $TY(pub $ty); - - impl Arbitrary for $TY { - fn arbitrary(g: &mut G) -> $TY - where G: Gen - { - // NOTE Generate edge cases with a 10% chance - let t = if g.gen_weighted_bool(10) { - *g.choose(&[ - $ty::min_value(), - 0, - $ty::max_value(), - ]).unwrap() - } else { - match g.gen_range(0, 3) { - 0 => $ty::from_parts(g.gen(), g.gen()), - 1 => $ty::from_parts(0, g.gen()), - 2 => $ty::from_parts(g.gen(), 0), - _ => unreachable!(), - } - }; - - $TY(t) - } - - fn shrink(&self) -> Box> { - struct Shrinker { - x: $ty, - } - - impl Iterator for Shrinker { - type Item = $TY; - - fn next(&mut self) -> Option<$TY> { - self.x /= 2; - if self.x == 0 { - None - } else { - Some($TY(self.x)) - } - } - } - - if self.0 == 0 { - ::quickcheck::empty_shrinker() - } else { - Box::new(Shrinker { x: self.0 }) - } - } - } - - impl fmt::Debug for $TY { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } - } - } -} - -arbitrary_large!(I64: i64); -arbitrary_large!(U64: u64); -arbitrary_large!(I128: i128); -arbitrary_large!(U128: u128); - -macro_rules! arbitrary_float { - ($TY:ident : $ty:ident) => { - #[derive(Clone, Copy)] - pub struct $TY(pub $ty); - - impl Arbitrary for $TY { - fn arbitrary(g: &mut G) -> $TY - where G: Gen - { - let special = [ - -0.0, 0.0, $ty::NAN, $ty::INFINITY, -$ty::INFINITY - ]; - - if g.gen_weighted_bool(10) { // Random special case - $TY(*g.choose(&special).unwrap()) - } else if g.gen_weighted_bool(10) { // NaN variants - let sign: bool = g.gen(); - let exponent: <$ty as Float>::Int = g.gen(); - let significand: <$ty as Float>::Int = 0; - $TY($ty::from_parts(sign, exponent, significand)) - } else if g.gen() { // Denormalized - let sign: bool = g.gen(); - let exponent: <$ty as Float>::Int = 0; - let significand: <$ty as Float>::Int = g.gen(); - $TY($ty::from_parts(sign, exponent, significand)) - } else { // Random anything - let sign: bool = g.gen(); - let exponent: <$ty as Float>::Int = g.gen(); - let significand: <$ty as Float>::Int = g.gen(); - $TY($ty::from_parts(sign, exponent, significand)) - } - } - - fn shrink(&self) -> Box> { - ::quickcheck::empty_shrinker() - } - } - - impl fmt::Debug for $TY { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } - } - - impl PartialEq for $TY { - fn eq(&self, other: &$TY) -> bool { - self.0.eq_repr(other.0) - } - } - } -} - -arbitrary_float!(F32: f32); -arbitrary_float!(F64: f64); - -// Convenience macro to test intrinsics against their reference implementations. -// -// Each intrinsic is tested against both the `gcc_s` library as well as -// `compiler-rt`. These libraries are defined in the `gcc_s` crate as well as -// the `compiler-rt` crate in this repository. Both load a dynamic library and -// lookup symbols through that dynamic library to ensure that we're using the -// right intrinsic. -// -// This macro hopefully allows you to define a bare minimum of how to test an -// intrinsic without worrying about these implementation details. A sample -// invocation looks like: -// -// -// check! { -// // First argument is the function we're testing (either from this lib -// // or a dynamically loaded one. Further arguments are all generated by -// // quickcheck. -// fn __my_intrinsic(f: extern fn(i32) -> i32, -// a: I32) -// -> Option<(i32, i64)> { -// -// // Discard tests by returning Some -// if a.0 == 0 { -// return None -// } -// -// // Return the result via `Some` if the test can run -// let mut other_result = 0; -// let result = f(a.0, &mut other_result); -// Some((result, other_result)) -// } -// } -// -// If anything returns `None` then the test is discarded, otherwise the two -// results are compared for equality and the test fails if this equality check -// fails. -macro_rules! check { - ($( - $(#[$cfg:meta])* - fn $name:ident($f:ident: extern $abi:tt fn($($farg:ty),*) -> $fret:ty, - $($arg:ident: $t:ty),*) - -> Option<$ret:ty> - { - $($code:tt)* - } - )*) => ( - $( - $(#[$cfg])* - fn $name($f: extern $abi fn($($farg),*) -> $fret, - $($arg: $t),*) -> Option<$ret> { - $($code)* - } - )* - - mod _test { - use qc::*; - use std::mem; - use quickcheck::TestResult; - - $( - $(#[$cfg])* - #[test] - fn $name() { - fn my_check($($arg:$t),*) -> TestResult { - let my_answer = super::$name(super::super::$name, - $($arg),*); - let compiler_rt_fn = ::compiler_rt::get(stringify!($name)); - let compiler_rt_answer = unsafe { - super::$name(mem::transmute(compiler_rt_fn), - $($arg),*) - }; - let gcc_s_answer = - match ::gcc_s::get(stringify!($name)) { - Some(f) => unsafe { - Some(super::$name(mem::transmute(f), - $($arg),*)) - }, - None => None, - }; - - let print_values = || { - print!("{} - Args: ", stringify!($name)); - $(print!("{:?} ", $arg);)* - print!("\n"); - println!(" compiler-builtins: {:?}", my_answer); - println!(" compiler_rt: {:?}", compiler_rt_answer); - println!(" gcc_s: {:?}", gcc_s_answer); - }; - - if my_answer != compiler_rt_answer { - print_values(); - TestResult::from_bool(false) - } else if gcc_s_answer.is_some() && - my_answer != gcc_s_answer.unwrap() { - print_values(); - TestResult::from_bool(false) - } else { - TestResult::from_bool(true) - } - } - - ::quickcheck::quickcheck(my_check as fn($($t),*) -> TestResult) - } - )* - } - ) -}