#![allow(clippy::assign_op_pattern)]
pub mod bool;
pub mod bytes;
pub mod number;
pub mod prelude;
#[deny(missing_docs)]
mod rng;
pub mod serialized_bytes;
pub mod string;
pub mod unit;
pub use paste;
pub use rng::rng;
pub struct Fixturator<Item, Curve> {
item: std::marker::PhantomData<Item>,
pub curve: Curve,
pub index: usize,
}
impl<Curve, Item> Fixturator<Item, Curve> {
pub fn new(curve: Curve, start: usize) -> Self {
Fixturator::<Item, Curve> {
curve,
index: start,
item: std::marker::PhantomData,
}
}
}
#[macro_export]
macro_rules! basic_test {
( $type:ty, $empty_expected:expr, $predictable_expected:expr ) => {
basic_test!($type, $empty_expected, $predictable_expected, true);
};
( $type:ty, $empty_expected:expr, $predictable_expected:expr, $test_unpredictable:literal ) => {
$crate::prelude::paste! {
#[test]
#[cfg(test)]
fn [<$type:lower _empty>] () {
let empties = [<$type:camel Fixturator>]::new(Empty);
assert_eq!(
$empty_expected,
empties.take($empty_expected.len()).collect::<Vec<$type>>(),
);
}
}
$crate::prelude::paste! {
#[test]
#[cfg(test)]
fn [<$type:lower _predictable>] () {
let predictables = [<$type:camel Fixturator>]::new($crate::prelude::Predictable);
assert_eq!(
$predictable_expected,
predictables.take($predictable_expected.len()).collect::<Vec<$type>>(),
);
}
}
$crate::prelude::paste! {
#[test]
#[cfg(test)]
fn [<$type:lower _unpredictable>] () {
if $test_unpredictable {
let empties = [<$type:camel Fixturator>]::new(Empty);
let unpredictables = [<$type:camel Fixturator>]::new($crate::prelude::Unpredictable);
assert_ne!(
empties.take(100).collect::<Vec<$type>>(),
unpredictables.take(100).collect::<Vec<$type>>(),
);
let predictables = [<$type:camel Fixturator>]::new($crate::prelude::Predictable);
let unpredictables = [<$type:camel Fixturator>]::new($crate::prelude::Unpredictable);
assert_ne!(
predictables.take(100).collect::<Vec<$type>>(),
unpredictables.take(100).collect::<Vec<$type>>(),
);
}
}
}
};
}
#[macro_export]
macro_rules! fixturator {
(
with_vec $min:literal $max:literal;
$type:tt;
$($munch:tt)*
) => {
$crate::prelude::paste! {
pub type [<$type:camel Vec>] = Vec<$type>;
fixturator!(
[<$type:camel Vec>];
curve Empty vec![];
curve Unpredictable {
let mut index = get_fixt_index!();
let mut rng = $crate::rng();
let len = rng.random_range($min..$max);
let mut fixturator = [<$type:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, index);
let mut v = vec![];
for _ in 0..len {
v.push(fixturator.next().unwrap());
}
index += 1;
set_fixt_index!(index);
v
};
curve Predictable {
let mut index = get_fixt_index!();
let mut fixturator = [<$type:camel Fixturator>]::new_indexed($crate::prelude::Predictable, index);
let mut v = vec![];
let min = $min;
let max = (index % ($max - min)) + min;
for _ in min..max {
v.push(fixturator.next().unwrap());
}
index += 1;
set_fixt_index!(index);
v
};
);
}
fixturator!($type; $($munch)*);
};
(
$type:tt;
variants [ $( $variant:tt($variant_inner:ty) )* ];
$($munch:tt)*
) => {
fixturator!(
$type;
enum [ $( $variant )* ];
curve Empty $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
$(
[<$type:camel Variant>]::$variant => $type::$variant(
[<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap().into()
),
)*
}};
curve Unpredictable $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
$(
[<$type:camel Variant>]::$variant => $type::$variant(
[<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap().into()
),
)*
}};
curve Predictable $crate::prelude::paste! { match [<$type:camel Variant>]::nth(get_fixt_index!()) {
$(
[<$type:camel Variant>]::$variant => $type::$variant(
[<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap().into()
),
)*
}};
$($munch)*
);
};
(
$type:tt;
unit variants [ $( $variant:tt )* ] empty $empty:tt;
$($munch:tt)*
) => {
fixturator!(
$type;
enum [ $( $variant )* ];
curve Empty {
$crate::prelude::paste! { $type::$empty }
};
curve Unpredictable $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
$(
[<$type:camel Variant>]::$variant => $type::$variant,
)*
}};
curve Predictable $crate::prelude::paste! {{
match [<$type:camel Variant>]::nth(get_fixt_index!()) {
$(
[<$type:camel Variant>]::$variant => $type::$variant,
)*
}}};
$($munch)*
);
};
(
$type:tt;
enum [ $( $variant:tt )* ];
$($munch:tt)*
) => {
$crate::prelude::paste! {
#[derive($crate::prelude::strum_macros::EnumIter)]
enum [<$type:camel Variant>] {
$( $variant ),*
}
impl [<$type:camel Variant>] {
fn random() -> Self {
[<$type:camel Variant>]::iter().choose(&mut $crate::rng()).unwrap()
}
fn nth(index: usize) -> Self {
$crate::prelude::paste! {
[<$type:camel Variant>]::iter().cycle().nth(index).unwrap()
}
}
}
}
fixturator!($type; $($munch)* );
};
( $type:ident; from $from:ty; $($munch:tt)* ) => {
fixturator!(
$type;
curve Empty {
$type::from(
$crate::prelude::paste! {
[< $from:camel Fixturator >]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap()
}
)
};
curve Unpredictable {
$type::from(
$crate::prelude::paste! {
[< $from:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap()
}
)
};
curve Predictable {
$type::from(
$crate::prelude::paste! {
[< $from:camel Fixturator >]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap()
}
)
};
);
};
( $type:ident; constructor fn $fn:tt( $( $newtype:ty ),* ); $($munch:tt)* ) => {
fixturator!(
$type;
curve Empty {
let index = get_fixt_index!();
$type::$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Empty, index).next().unwrap().into()
}
),*
)
};
curve Unpredictable {
let index = get_fixt_index!();
$type::$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, index).next().unwrap().into()
}
),*
)
};
curve Predictable {
let index = get_fixt_index!();
$type::$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Predictable, index).next().unwrap().into()
}
),*
)
};
$($munch)*
);
};
( $type:ident; vanilla fn $fn:tt( $( $newtype:ty ),* ); $($munch:tt)* ) => {
fixturator!(
$type;
curve Empty {
$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap().into()
}
),*
)
};
curve Unpredictable {
$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap().into()
}
),*
)
};
curve Predictable {
$fn(
$(
$crate::prelude::paste! {
[< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap().into()
}
),*
)
};
$($munch)*
);
};
( $type:ident; curve $curve:ident $e:expr; $($munch:tt)* ) => {
curve!( $type, $curve, $e);
fixturator!( $type; $($munch)* );
};
( $type:ident; $($munch:tt)* ) => {
$crate::prelude::paste! {
#[allow(missing_docs)]
pub struct [<$type:camel Fixturator>]<Curve>(Fixturator<$type, Curve>);
#[allow(missing_docs)]
impl <Curve>[<$type:camel Fixturator>]<Curve> {
pub fn new(curve: Curve) -> [<$type:camel Fixturator>]<Curve> {
Self::new_indexed(curve, 0)
}
pub fn new_indexed(curve: Curve, start: usize) -> [<$type:camel Fixturator>]<Curve> {
[<$type:camel Fixturator>](Fixturator::<$type, Curve>::new(curve, start))
}
}
}
};
( $type:ident, $empty:expr, $unpredictable:expr, $predictable:expr ) => {
fixturator!(
$type;
curve Empty $empty;
curve Unpredictable $unpredictable;
curve Predictable $predictable;
);
};
}
#[macro_export]
macro_rules! get_fixt_index {
() => {{
let mut index = 0;
FIXT_INDEX.with(|f| index = *f.borrow());
index
}};
}
#[macro_export]
macro_rules! set_fixt_index {
($index:expr) => {{
FIXT_INDEX.with(|f| *f.borrow_mut() = $index);
}};
}
#[macro_export]
macro_rules! get_fixt_curve {
() => {{
let mut curve = None;
FIXT_CURVE.with(|f| curve = f.borrow().clone());
curve.unwrap()
}};
}
#[macro_export]
macro_rules! curve {
( $type:ident, $curve:ident, $e:expr ) => {
$crate::prelude::paste! {
#[allow(missing_docs)]
impl Iterator for [< $type:camel Fixturator >]<$curve> {
type Item = $type;
fn next(&mut self) -> Option<Self::Item> {
thread_local!(static FIXT_INDEX: std::cell::RefCell<usize> = std::cell::RefCell::new(0));
thread_local!(static FIXT_CURVE: std::cell::RefCell<Option<$curve>> = std::cell::RefCell::new(None));
FIXT_INDEX.with(|f| *f.borrow_mut() = self.0.index);
FIXT_CURVE.with(|f| *f.borrow_mut() = Some(self.0.curve.clone()));
let original_index = self.0.index;
let ret = $e;
FIXT_INDEX.with(|f| self.0.index = *f.borrow());
if original_index == self.0.index {
self.0.index += 1;
}
Some(ret)
}
}
}
};
}
#[macro_export]
macro_rules! fixt {
( $name:tt ) => {
$crate::fixt!($name, $crate::prelude::Unpredictable)
};
( $name:tt, $curve:expr ) => {
$crate::fixt!($name, $curve, 0)
};
( $name:tt, $curve:expr, $index:expr ) => {
$crate::prelude::paste! { [< $name:camel Fixturator>]::new_indexed($curve, $index).next().unwrap() }
}
}
#[derive(Clone, Copy)]
pub struct Unpredictable;
#[derive(Clone, Copy)]
pub struct Predictable;
#[derive(Clone, Copy)]
pub struct Empty;
#[macro_export]
macro_rules! newtype_fixturator {
( $outer:ident<Vec<$inner:ty>> ) => {
fixturator!(
$outer,
$outer(vec![]),
{
let mut rng = $crate::rng();
let vec_len = rng.random_range(0..5);
let mut ret = vec![];
let mut inner_fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()) };
for _ in 0..vec_len {
ret.push(inner_fixturator.next().unwrap());
}
set_fixt_index!(get_fixt_index!() + 1);
$outer(ret)
},
{
let mut rng = $crate::rng();
let vec_len = rng.random_range(0..5);
let mut ret = vec![];
let mut inner_fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()) };
for _ in 0..vec_len {
ret.push(inner_fixturator.next().unwrap());
}
set_fixt_index!(get_fixt_index!() + 1);
$outer(ret)
}
);
};
( $outer:ident<$inner:ty> ) => {
fixturator!(
$outer,
{
let mut index = get_fixt_index!();
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, index) };
index += 1;
set_fixt_index!(index);
$outer(fixturator.next().unwrap())
},
{
let mut index = get_fixt_index!();
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, index) };
index += 1;
set_fixt_index!(index);
$outer(fixturator.next().unwrap())
},
{
let mut index = get_fixt_index!();
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, index) };
index += 1;
set_fixt_index!(index);
$outer(fixturator.next().unwrap())
}
);
};
}
#[macro_export]
macro_rules! wasm_io_fixturator {
( $outer:ident<$inner:ty> ) => {
fixturator!(
$outer,
{
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, get_fixt_index!()) };
set_fixt_index!(get_fixt_index!() + 1);
$outer::new(fixturator.next().unwrap())
},
{
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()) };
set_fixt_index!(get_fixt_index!() + 1);
$outer::new(fixturator.next().unwrap())
},
{
let mut fixturator =
$crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()) };
set_fixt_index!(get_fixt_index!() + 1);
$outer::new(fixturator.next().unwrap())
}
);
};
}
#[macro_export]
macro_rules! enum_fixturator {
( $enum:ident, $empty:expr ) => {
use rand::seq::IteratorRandom;
use $crate::prelude::IntoEnumIterator;
fixturator!(
$enum,
$empty,
{ $enum::iter().choose(&mut $crate::rng()).unwrap() },
{
let ret = $enum::iter().cycle().nth(self.0.index).unwrap();
set_fixt_index!(get_fixt_index!() + 1);
ret
}
);
};
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::string::PREDICTABLE_STRS;
#[derive(PartialEq, Debug)]
pub enum Foo {
A,
B(String),
}
fixturator!(
Foo;
enum [ A B ];
curve Empty Foo::A;
curve Unpredictable match FooVariant::random() {
FooVariant::A => Foo::A,
FooVariant::B => Foo::B(fixt!(String)),
};
curve Predictable match FooVariant::nth(get_fixt_index!()) {
FooVariant::A => Foo::A,
FooVariant::B => Foo::B(StringFixturator::new_indexed(Predictable, get_fixt_index!()).next().unwrap()),
};
);
#[test]
fn enum_test() {
assert_eq!(FooFixturator::new(Predictable).next().unwrap(), Foo::A,);
FooFixturator::new(Unpredictable).next().unwrap();
assert_eq!(FooFixturator::new(Empty).next().unwrap(), Foo::A,);
let mut fixt_iter = FooFixturator::new(Predictable);
assert_eq!(fixt_iter.next().unwrap(), Foo::A);
let string = StringFixturator::new_indexed(Predictable, 1)
.next()
.unwrap();
assert_eq!(fixt_iter.next().unwrap(), Foo::B(string));
}
#[derive(PartialEq, Debug)]
pub enum UnitFoo {
A,
B,
C,
}
fixturator!(
UnitFoo;
unit variants [ A B C ] empty B;
);
#[test]
fn unit_variants_test() {
assert_eq!(
UnitFooFixturator::new(Predictable).next().unwrap(),
UnitFoo::A,
);
UnitFooFixturator::new(Unpredictable).next().unwrap();
assert_eq!(UnitFooFixturator::new(Empty).next().unwrap(), UnitFoo::B,);
}
#[derive(PartialEq, Debug, Clone)]
pub enum VariantFoo {
A(String),
B(u64),
C(bool),
}
fixturator!(
VariantFoo;
variants [ A(String) B(u64) C(bool) ];
);
#[test]
fn variant_variants_test() {
let mut predictable_fixturator = VariantFooFixturator::new(Predictable);
for expected in [
VariantFoo::A("💯".into()),
VariantFoo::B(1),
VariantFoo::C(true),
VariantFoo::A(".".into()),
VariantFoo::B(4),
VariantFoo::C(false),
]
.iter()
{
assert_eq!(expected.to_owned(), predictable_fixturator.next().unwrap(),);
}
let mut unpredictable_fixturator = VariantFooFixturator::new(Unpredictable);
for _ in 0..10 {
unpredictable_fixturator.next().unwrap();
}
let mut empty_fixturator = VariantFooFixturator::new(Empty);
for _ in 0..10 {
match empty_fixturator.next().unwrap() {
VariantFoo::A(s) => assert_eq!(s, ""),
VariantFoo::B(n) => assert_eq!(n, 0),
VariantFoo::C(b) => assert!(!b),
}
}
}
#[derive(Debug, PartialEq)]
pub struct StringFoo(String);
impl From<String> for StringFoo {
fn from(s: String) -> Self {
Self(s)
}
}
fixturator!(StringFoo; from String;);
#[test]
fn from_test() {
let mut predictable_fixturator = StringFooFixturator::new(Predictable);
for expected in PREDICTABLE_STRS.iter() {
assert_eq!(
StringFoo::from(expected.to_string()),
predictable_fixturator.next().unwrap()
);
}
let mut unpredictable_fixturator = StringFooFixturator::new(Unpredictable);
for _ in 0..10 {
unpredictable_fixturator.next().unwrap();
}
let mut empty_fixturator = StringFooFixturator::new(Empty);
for _ in 0..10 {
assert_eq!(
StringFoo::from("".to_string()),
empty_fixturator.next().unwrap(),
);
}
}
#[derive(Debug, PartialEq)]
pub struct ConstructedFoo {
bar: bool,
}
impl ConstructedFoo {
fn from_bar(bar: bool) -> Self {
Self { bar }
}
}
fixturator!(
ConstructedFoo;
constructor fn from_bar(bool);
);
#[test]
fn constructor_test() {
let mut predictable_fixturator = ConstructedFooFixturator::new(Predictable);
for expected in [true, false].iter().cycle().take(5) {
assert_eq!(
ConstructedFoo::from_bar(*expected),
predictable_fixturator.next().unwrap(),
);
}
let mut unpredictable_fixturator = ConstructedFooFixturator::new(Unpredictable);
for _ in 0..10 {
unpredictable_fixturator.next().unwrap();
}
let mut empty_fixturator = ConstructedFooFixturator::new(Empty);
for _ in 0..10 {
assert_eq!(
ConstructedFoo::from_bar(false),
empty_fixturator.next().unwrap(),
);
}
}
}