compiler-builtins-zynq/src/qc.rs

124 lines
3.8 KiB
Rust

// 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 quickcheck::{Arbitrary, Gen};
use int::LargeInt;
// Generates values in the full range of the integer type
macro_rules! arbitrary {
($TY:ident : $ty:ident) => {
#[derive(Clone, Copy)]
pub struct $TY(pub $ty);
impl Arbitrary for $TY {
fn arbitrary<G>(g: &mut G) -> $TY
where G: Gen
{
$TY(g.gen())
}
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
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)]
pub struct $TY(pub $ty);
impl Arbitrary for $TY {
fn arbitrary<G>(g: &mut G) -> $TY
where G: Gen
{
if g.gen() {
$TY($ty::from_parts(g.gen(), g.gen()))
} else if g.gen() {
$TY($ty::from_parts(0, g.gen()))
} else {
$TY($ty::from_parts(g.gen(), 0))
}
}
fn shrink(&self) -> Box<Iterator<Item=$TY>> {
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);