fixt/lib.rs
1#![allow(clippy::assign_op_pattern)]
2
3pub mod bool;
4pub mod bytes;
5pub mod number;
6pub mod prelude;
7#[deny(missing_docs)]
8mod rng;
9pub mod serialized_bytes;
10pub mod string;
11pub mod unit;
12pub use paste;
13
14pub use rng::rng;
15
16/// the Fixturator is the struct that we wrap in our FooFixturator newtypes to impl Iterator over
17/// each combination of Item and Curve needs its own Iterator implementation for Fixturator
18/// Item is the Foo type of FooFixturator, i.e. the type of thing we are generating examples of
19/// Curve represents some algorithm capable of generating fixtures
20/// the Item is PhantomData because it simply represents a type to output
21/// the Curve must be provided when the Fixturator is constructed to allow for paramaterized curves
22/// this is most easily handled in most cases with the fixturator! and newtype_fixturator! macros
23///
24/// The inner index is always a single usize.
25/// It can be ignored, e.g. in the case of Unpredictable implementations based on `rand::random()`.
26/// If it is used it should be incremented by 1 and/or wrapped back to 0 to derive returned values.
27/// Ideally the Curve should allow for efficient calculation of a fixture from any given index,
28/// e.g. a fibbonacci curve would be a bad idea as it requires sequential/recursive calculations to
29/// reach any specific index, c.f. the direct multiplication in the step function above.
30/// Following this standard allows for wrapper structs to delegate their curves to the curves of
31/// their inner types by constructing an inner Fixturator directly with the outer index passed in.
32/// If we can always assume the inner fixturators can be efficiently constructed at any index this
33/// allows us to efficiently compose fixturators.
34/// See [ `newtype_fixturator!` ](newtype_fixturator) macro defined below for an example of this.
35///
36/// Fixturator implements Clone for convenience but note that this will clone the current index.
37///
38/// Fixturators are lazy and infinite, they must never fail to iterate
39/// That is to say, calling fixturator.next().unwrap() must be safe to do and never panic
40/// This makes the external interface as easy to compose as possible when building up Fixturators
41/// over complex data types that include different curves with various periods.
42/// For example, the Predictable bool sequence cycles between true/false with period of 2 while the
43/// Predictable string sequence has 10 sample strings that it iterates over. We want to be able to
44/// easily support Fixturators over structs containing both string and bool fields, so we wrap the
45/// inner Fixturator sequences to keep producing bools and Strings for as needed (rather than
46/// forcing the outer struct to stop after 2 bools or manually implement ad-hoc wrapping).
47/// Wrapping logic may be subtle, e.g. mapping between a usize index and a u8 Item where the max
48/// values do not align, so it is best to centralise the wrapping behaviour inside the Iterator
49/// implementations for each <Item, Curve> combination.
50/// If you are implementing an iteration over some finite sequence then wrap the iteration back to
51/// the start of the sequence once the index exceeds the sequence's bounds or reset the index to 0
52/// after seq.len() iterations.
53/// essentially, the iteration of a fixturator should work like some_iter.cycle()
54pub struct Fixturator<Item, Curve> {
55    item: std::marker::PhantomData<Item>,
56    pub curve: Curve,
57    pub index: usize,
58}
59
60impl<Curve, Item> Fixturator<Item, Curve> {
61    /// constructs a Fixturator of type <Item, Curve> from a Curve and starting index
62    /// raw calls are a little verbose, e.g. `Fixturator::<u32, Predictable>::new(Predictable, 0)`
63    /// the starting index is exposed to facilitate wrapper structs to delegate their indexes to
64    /// internal Fixturators
65    /// See [`newtype_fixturator!`](newtype_fixturator) macro below for an example of this
66    pub fn new(curve: Curve, start: usize) -> Self {
67        Fixturator::<Item, Curve> {
68            curve,
69            index: start,
70            item: std::marker::PhantomData,
71        }
72    }
73}
74
75// /// set of basic tests that can be used to test any FooFixturator implementation
76// /// usage:
77// /// - type: the Foo of FooFixturator to be tested
78// /// - empty_expected: vector of any length of empties that we predict from Empty
79// /// - predictable_expected: vector of any length (can wrap) that we predict from Predictable
80// /// - test_unpredictable (optional): whether to try and test the unpredictable case
81// /// See the tests in modules in this crate
82#[macro_export]
83macro_rules! basic_test {
84    ( $type:ty, $empty_expected:expr, $predictable_expected:expr ) => {
85        basic_test!($type, $empty_expected, $predictable_expected, true);
86    };
87    ( $type:ty, $empty_expected:expr, $predictable_expected:expr, $test_unpredictable:literal ) => {
88        $crate::prelude::paste! {
89            #[test]
90            #[cfg(test)]
91            fn [<$type:lower _empty>] () {
92                let empties = [<$type:camel Fixturator>]::new(Empty);
93                // we can make many empties from the Empty curve
94                assert_eq!(
95                    $empty_expected,
96                    empties.take($empty_expected.len()).collect::<Vec<$type>>(),
97                );
98            }
99        }
100
101        $crate::prelude::paste! {
102            #[test]
103            #[cfg(test)]
104            fn [<$type:lower _predictable>] () {
105                let predictables = [<$type:camel Fixturator>]::new($crate::prelude::Predictable);
106                // we can predict some vector of values from the Predictable curve
107                assert_eq!(
108                    $predictable_expected,
109                    predictables.take($predictable_expected.len()).collect::<Vec<$type>>(),
110                );
111            }
112        }
113
114        $crate::prelude::paste! {
115            #[test]
116            #[cfg(test)]
117            fn [<$type:lower _unpredictable>] () {
118                if $test_unpredictable {
119                    let empties = [<$type:camel Fixturator>]::new(Empty);
120                    let unpredictables = [<$type:camel Fixturator>]::new($crate::prelude::Unpredictable);
121
122                    // the Unpredictable curve is not Empty
123                    assert_ne!(
124                        empties.take(100).collect::<Vec<$type>>(),
125                        unpredictables.take(100).collect::<Vec<$type>>(),
126                    );
127
128                    let predictables = [<$type:camel Fixturator>]::new($crate::prelude::Predictable);
129                    let unpredictables = [<$type:camel Fixturator>]::new($crate::prelude::Unpredictable);
130
131                    // the Unpredictable curve is not Predictable
132                    assert_ne!(
133                        predictables.take(100).collect::<Vec<$type>>(),
134                        unpredictables.take(100).collect::<Vec<$type>>(),
135                    );
136                }
137            }
138        }
139    };
140}
141
142/// implements a FooFixturator for any type Foo
143/// this simply wraps `Fixturator<Foo, Curve>` up as `FooFixturator<Curve>`
144///
145/// this macro serves a few purposes:
146/// - we avoid the orphan rule that would prevent us implementing Iterator on Fixturator directly
147/// - we avoid the verbosity of type and impl juggling around every new FooFixturator
148/// - we create a FooFixturator implementation that is compatible with basic_test! macro
149/// - we cover all three basic curves
150/// - we standardiize the new() and new_indexed() methods without relying on traits
151///
152/// the expressions passed into the macro are the body of the next calls for Empty, Unpredictable
153/// and Predictable, in order
154#[macro_export]
155macro_rules! fixturator {
156    (
157        with_vec $min:literal $max:literal;
158        $type:tt;
159        $($munch:tt)*
160    ) => {
161        $crate::prelude::paste! {
162            pub type [<$type:camel Vec>] = Vec<$type>;
163            fixturator!(
164                [<$type:camel Vec>];
165                curve Empty vec![];
166                curve Unpredictable {
167                    let mut index = get_fixt_index!();
168                    let mut rng = $crate::rng();
169                    let len = rng.gen_range($min..$max);
170                    let mut fixturator = [<$type:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, index);
171                    let mut v = vec![];
172                    for _ in 0..len {
173                        v.push(fixturator.next().unwrap());
174                    }
175                    index += 1;
176                    set_fixt_index!(index);
177                    v
178                };
179                curve Predictable {
180                    let mut index = get_fixt_index!();
181                    let mut fixturator = [<$type:camel Fixturator>]::new_indexed($crate::prelude::Predictable, index);
182                    let mut v = vec![];
183                    let min = $min;
184                    let max = (index % ($max - min)) + min;
185                    for _ in min..max {
186                        v.push(fixturator.next().unwrap());
187                    }
188                    index += 1;
189                    set_fixt_index!(index);
190                    v
191                };
192            );
193        }
194        fixturator!($type; $($munch)*);
195    };
196
197    // for an enum Foo with variants with a single inner type
198    //
199    // fixturator!(Foo; variants [ A(String) B(bool) ];);
200    //
201    // implements all basic curves using fixturators for the variant inner types
202    (
203        $type:tt;
204        variants [ $( $variant:tt($variant_inner:ty) )* ];
205        $($munch:tt)*
206    ) => {
207
208        fixturator!(
209            $type;
210            enum [ $( $variant )* ];
211
212            curve Empty $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
213                $(
214                    [<$type:camel Variant>]::$variant => $type::$variant(
215                        [<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap().into()
216                    ),
217                )*
218            }};
219
220            curve Unpredictable $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
221                $(
222                    [<$type:camel Variant>]::$variant => $type::$variant(
223                        [<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap().into()
224                    ),
225                )*
226            }};
227
228            curve Predictable $crate::prelude::paste! { match [<$type:camel Variant>]::nth(get_fixt_index!()) {
229                $(
230                    [<$type:camel Variant>]::$variant => $type::$variant(
231                        [<$variant_inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap().into()
232                    ),
233                )*
234            }};
235
236            $($munch)*
237        );
238    };
239
240    // for an enum Foo with unit variants with no inner types
241    //
242    // fixturator!(Foo; unit variants [ A B ] empty B;);
243    //
244    // implements all basic curves returning the empty curve passed to the macro, or a random
245    // variant or an iterating variant from the index
246    (
247        $type:tt;
248        unit variants [ $( $variant:tt )* ] empty $empty:tt;
249        $($munch:tt)*
250    ) => {
251        fixturator!(
252            $type;
253            enum [ $( $variant )* ];
254            curve Empty {
255                $crate::prelude::paste! { $type::$empty }
256            };
257            curve Unpredictable $crate::prelude::paste! { match [<$type:camel Variant>]::random() {
258                $(
259                        [<$type:camel Variant>]::$variant => $type::$variant,
260                )*
261            }};
262            curve Predictable $crate::prelude::paste! {{
263                match [<$type:camel Variant>]::nth(get_fixt_index!()) {
264                $(
265                    [<$type:camel Variant>]::$variant => $type::$variant,
266                )*
267            }}};
268            $($munch)*
269        );
270    };
271
272    // for any complex enum
273    //
274    // fixturator!(Foo; enum [ A B ]; curve ...; curve ...; curve ...;);
275    //
276    // implements an enum with variants matching Foo as FooVariant
277    // this enum can be iterated over as per the strum crate EnumIter
278    //
279    // it also has convenience methods to match against:
280    //
281    // - FooVariant::random() for a random variant of Foo
282    // - FooVariant::nth(n) for an indexed variant of Foo
283    //
284    // See the tests in this file for examples.
285        (
286            $type:tt;
287            enum [ $( $variant:tt )* ];
288            $($munch:tt)*
289        ) => {
290            $crate::prelude::paste! {
291                #[derive($crate::prelude::strum_macros::EnumIter)]
292                enum [<$type:camel Variant>] {
293                    $( $variant ),*
294                }
295
296                impl [<$type:camel Variant>] {
297                    fn random() -> Self {
298                        [<$type:camel Variant>]::iter().choose(&mut $crate::rng()).unwrap()
299                    }
300                    fn nth(index: usize) -> Self {
301                        $crate::prelude::paste! {
302                            [<$type:camel Variant>]::iter().cycle().nth(index).unwrap()
303                        }
304                    }
305                }
306            }
307
308            fixturator!($type; $($munch)* );
309    };
310
311    // for any Foo that impl From<Bar>
312    //
313    // fixturator!(Foo; from Bar;);
314    //
315    // implements all the curves by building Foo from a BarFixturator
316    ( $type:ident; from $from:ty; $($munch:tt)* ) => {
317        fixturator!(
318            $type;
319
320            curve Empty {
321                $type::from(
322                    $crate::prelude::paste! {
323                        [< $from:camel Fixturator >]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap()
324                    }
325                )
326            };
327            curve Unpredictable {
328                $type::from(
329                    $crate::prelude::paste! {
330                        [< $from:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap()
331                    }
332                )
333            };
334            curve Predictable {
335                $type::from(
336                    $crate::prelude::paste! {
337                        [< $from:camel Fixturator >]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap()
338                    }
339                )
340            };
341        );
342    };
343
344    // for any Foo that has a constructor function like Foo::new( ... )
345    //
346    // fixturator!(Foo; constructor fn new(String, String, bool););
347    //
348    // implements all curves by building all the arguments to the named constructor function from
349    // the fixturators of the types specified to the macro
350    ( $type:ident; constructor fn $fn:tt( $( $newtype:ty ),* ); $($munch:tt)* ) => {
351        fixturator!(
352            $type;
353
354            curve Empty {
355                let index = get_fixt_index!();
356                $type::$fn(
357                    $(
358                        $crate::prelude::paste! {
359                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Empty, index).next().unwrap().into()
360                        }
361                    ),*
362                )
363            };
364
365            curve Unpredictable {
366                let index = get_fixt_index!();
367                $type::$fn(
368                    $(
369                        $crate::prelude::paste! {
370                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, index).next().unwrap().into()
371                        }
372                    ),*
373                )
374            };
375            curve Predictable {
376                let index = get_fixt_index!();
377                $type::$fn(
378                    $(
379                        $crate::prelude::paste! {
380                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Predictable, index).next().unwrap().into()
381                        }
382                    ),*
383                )
384            };
385
386            $($munch)*
387        );
388    };
389
390    // for any Foo that has a vanilla function like fn make_foo( ... ) -> Foo
391    //
392    // fixturator!(Foo; vanilla fn make_foo(String, String, bool););
393    //
394    // implements all curves by building all the arguments to the named vanilla function from
395    // the fixturators of the types specified to the macro
396    ( $type:ident; vanilla fn $fn:tt( $( $newtype:ty ),* ); $($munch:tt)* ) => {
397        fixturator!(
398            $type;
399
400            curve Empty {
401                $fn(
402                    $(
403                        $crate::prelude::paste! {
404                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Empty, get_fixt_index!()).next().unwrap().into()
405                        }
406                    ),*
407                )
408            };
409
410            curve Unpredictable {
411                $fn(
412                    $(
413                        $crate::prelude::paste! {
414                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()).next().unwrap().into()
415                        }
416                    ),*
417                )
418            };
419            curve Predictable {
420                $fn(
421                    $(
422                        $crate::prelude::paste! {
423                            [< $newtype:camel Fixturator >]::new_indexed($crate::prelude::Predictable, get_fixt_index!()).next().unwrap().into()
424                        }
425                    ),*
426                )
427            };
428
429            $($munch)*
430        );
431    };
432
433    // implement a single curve for Foo
434    //
435    // fixturator!(Foo; curve MyCurve { ... };);
436    //
437    // uses TT munching for multiple curves
438    // used internally by this macro for all baseline curves
439    // See https://danielkeep.github.io/tlborm/book/pat-incremental-tt-munchers.html
440    ( $type:ident; curve $curve:ident $e:expr; $($munch:tt)* ) => {
441        curve!( $type, $curve, $e);
442
443        fixturator!( $type; $($munch)* );
444    };
445
446    // create a FooFixturator for Foo
447    //
448    // fixturator!(Foo;);
449    //
450    // simply creates a newtype around the standard Fixturator struct and implements two methods:
451    // - FooFixturator::new(curve) to construct a FooFixturator with curve at index 0
452    // - FooFixturator::new(curve, index) to construct a FooFixturator with curve at index
453    //
454    // intended to be the TT munch endpoint for all patterns in this macro
455    // See https://danielkeep.github.io/tlborm/book/pat-incremental-tt-munchers.html
456    ( $type:ident; $($munch:tt)* ) => {
457        $crate::prelude::paste! {
458            #[allow(missing_docs)]
459            pub struct [<$type:camel Fixturator>]<Curve>(Fixturator<$type, Curve>);
460
461            #[allow(missing_docs)]
462            impl <Curve>[<$type:camel Fixturator>]<Curve> {
463                pub fn new(curve: Curve) -> [<$type:camel Fixturator>]<Curve> {
464                    Self::new_indexed(curve, 0)
465                }
466                pub fn new_indexed(curve: Curve, start: usize) -> [<$type:camel Fixturator>]<Curve> {
467                    [<$type:camel Fixturator>](Fixturator::<$type, Curve>::new(curve, start))
468                }
469            }
470        }
471    };
472
473    // legacy syntax
474    //
475    // fixturator!(Foo, { /* empty */ }, { /* unpredictable */ }, { /* predictable */ });
476    //
477    // implements both FooFixturator and all the curves from raw expressions passed to the macro
478    //
479    // this syntax has several limitations:
480    // - positional curve definitions are easy to accidentally mix up
481    // - couples all curve definitions together and to FooFixturator creation
482    // - undifferentiated logic forces much boilerplate because the macro knows nothing about Foo
483    // - forces devs to define curves that might not be needed or make sense yet
484    ( $type:ident, $empty:expr, $unpredictable:expr, $predictable:expr ) => {
485        fixturator!(
486            $type;
487            curve Empty $empty;
488            curve Unpredictable $unpredictable;
489            curve Predictable $predictable;
490        );
491    };
492}
493
494#[macro_export]
495macro_rules! get_fixt_index {
496    () => {{
497        let mut index = 0;
498        FIXT_INDEX.with(|f| index = *f.borrow());
499        index
500    }};
501}
502
503#[macro_export]
504macro_rules! set_fixt_index {
505    ($index:expr) => {{
506        FIXT_INDEX.with(|f| *f.borrow_mut() = $index);
507    }};
508}
509
510#[macro_export]
511macro_rules! get_fixt_curve {
512    () => {{
513        let mut curve = None;
514        FIXT_CURVE.with(|f| curve = f.borrow().clone());
515        curve.unwrap()
516    }};
517}
518
519#[macro_export]
520/// implement Iterator for a FooFixturator for a given curve
521///
522/// curve!(Foo, Unpredictable, /* make an Unpredictable Foo here */ );
523///
524/// simple wrapper around the standard Iterator trait from rust
525/// the expression in the third parameter to curve! is just the body of .next() without the need or
526/// ability to return an Option - i.e. return a value of type Foo _not_ `Option<Foo>`
527/// if the body of the expression changes the index it will be respected, if not then it will be
528/// incremented by 1 automatically by the macro
529macro_rules! curve {
530    ( $type:ident, $curve:ident, $e:expr ) => {
531        $crate::prelude::paste! {
532            #[allow(missing_docs)]
533            impl Iterator for [< $type:camel Fixturator >]<$curve> {
534                type Item = $type;
535
536                fn next(&mut self) -> Option<Self::Item> {
537                    thread_local!(static FIXT_INDEX: std::cell::RefCell<usize> = std::cell::RefCell::new(0));
538                    thread_local!(static FIXT_CURVE: std::cell::RefCell<Option<$curve>> = std::cell::RefCell::new(None));
539                    FIXT_INDEX.with(|f| *f.borrow_mut() = self.0.index);
540                    FIXT_CURVE.with(|f| *f.borrow_mut() = Some(self.0.curve.clone()));
541                    let original_index = self.0.index;
542                    let ret = $e;
543                    FIXT_INDEX.with(|f| self.0.index = *f.borrow());
544                    if original_index == self.0.index {
545                        self.0.index += 1;
546                    }
547                    Some(ret)
548                }
549            }
550        }
551    };
552}
553
554#[macro_export]
555/// tiny convenience macro to make it easy to get the first Foo from its fixturator without using
556/// the iterator interface to save a little typing
557/// c.f. fixt!(Foo) vs. FooFixturator::new(Unpredictable).next().unwrap();
558macro_rules! fixt {
559    ( $name:tt ) => {
560        $crate::fixt!($name, $crate::prelude::Unpredictable)
561    };
562    ( $name:tt, $curve:expr ) => {
563        $crate::fixt!($name, $curve, 0)
564    };
565    ( $name:tt, $curve:expr, $index:expr ) => {
566        $crate::prelude::paste! { [< $name:camel Fixturator>]::new_indexed($curve, $index).next().unwrap() }
567    }
568}
569
570/// represents an unpredictable curve
571///
572/// unpredictable curves seek to:
573/// - disrupt 'just so' implementations of algorithms that lean too heavily on fragile assumptions
574/// - have a high probability of generating common edge cases that developers fail to cover
575///
576/// A classic example is broken/forgotten NaN handling in code that uses floats for calculations
577///
578/// in general this is what we want from our tests, to remind us of where we are _wrong_ about our
579/// assumptions in our code.
580/// it is likely that you want to use the Unpredictable curve as the defacto choice for testing.
581///
582/// however, note that unpredictable curves are NOT intended:
583/// - to comprehensively cover any particular value space
584/// - to replace property/fuzz testing
585/// - to algorithmically explore edge-cases in an automated fashion
586/// - to assert any particular security or correctness concern
587///
588/// unpredictable curves are a great way to knock off some low hanging fruit, especially around
589/// numeric calculations and utf-8 handling, but are no replacement for stringent approaches.
590#[derive(Clone, Copy)]
591pub struct Unpredictable;
592
593/// represents a predictable curve
594///
595/// a predictable curve simply iterates over some known progression of values in the same way every
596/// test run.
597///
598/// predictable curves can be convenient, or even necessary, if an unpredictable curve breaks our
599/// ability to make specific assertions about our code.
600///
601/// for example, we may want to demonstrate that additon works.
602/// with an unpredictable curve we can assert things like the arguments being commutative,
603/// associative, additive, etc. but then we quickly end up doing a bad version of property testing.
604/// better to assert known expected results of addition from various values from a predictable
605/// curve and then subject the addition function to real property testing with a dedicated tool.
606///
607/// this curve is provided as a standard option because there is a real, common tradeoff between
608/// test fragility (accuracy) and specificity (precision).
609#[derive(Clone, Copy)]
610pub struct Predictable;
611
612/// represents a curve over the empty value(s)
613/// the concept of "empty" is as slippery as it is of dubious value
614/// how many countless hours and bugs have we lost over deciding what "is" and what "isn't"?
615/// i'm looking at you, JS and PHP -_-
616///
617/// regardless, collections with no items, numbers with no magnitude, strings with no chars are all
618/// common sources of bugs, so feel free to manifest as much emptiness as you like from this curve.
619#[derive(Clone, Copy)]
620pub struct Empty;
621
622#[macro_export]
623/// a direct delegation of fixtures to the inner type for new types
624macro_rules! newtype_fixturator {
625    ( $outer:ident<Vec<$inner:ty>> ) => {
626        fixturator!(
627            $outer,
628            $outer(vec![]),
629            {
630                let mut rng = $crate::rng();
631                let vec_len = rng.gen_range(0..5);
632                let mut ret = vec![];
633                let mut inner_fixturator =
634                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()) };
635                for _ in 0..vec_len {
636                    ret.push(inner_fixturator.next().unwrap());
637                }
638                set_fixt_index!(get_fixt_index!() + 1);
639                $outer(ret)
640            },
641            {
642                let mut rng = $crate::rng();
643                let vec_len = rng.gen_range(0..5);
644                let mut ret = vec![];
645                let mut inner_fixturator =
646                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()) };
647                for _ in 0..vec_len {
648                    ret.push(inner_fixturator.next().unwrap());
649                }
650                set_fixt_index!(get_fixt_index!() + 1);
651                $outer(ret)
652            }
653        );
654    };
655    ( $outer:ident<$inner:ty> ) => {
656        fixturator!(
657            $outer,
658            {
659                let mut index = get_fixt_index!();
660                let mut fixturator =
661                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, index) };
662                index += 1;
663                set_fixt_index!(index);
664                $outer(fixturator.next().unwrap())
665            },
666            {
667                let mut index = get_fixt_index!();
668                let mut fixturator =
669                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, index) };
670                index += 1;
671                set_fixt_index!(index);
672                $outer(fixturator.next().unwrap())
673            },
674            {
675                let mut index = get_fixt_index!();
676                let mut fixturator =
677                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, index) };
678                index += 1;
679                set_fixt_index!(index);
680                $outer(fixturator.next().unwrap())
681            }
682        );
683    };
684}
685
686#[macro_export]
687/// a direct delegation of fixtures to the inner type for wasm io types
688/// See zome types crate
689macro_rules! wasm_io_fixturator {
690    ( $outer:ident<$inner:ty> ) => {
691        fixturator!(
692            $outer,
693            {
694                let mut fixturator =
695                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Empty, get_fixt_index!()) };
696                set_fixt_index!(get_fixt_index!() + 1);
697                $outer::new(fixturator.next().unwrap())
698            },
699            {
700                let mut fixturator =
701                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Unpredictable, get_fixt_index!()) };
702                set_fixt_index!(get_fixt_index!() + 1);
703                $outer::new(fixturator.next().unwrap())
704            },
705            {
706                let mut fixturator =
707                    $crate::prelude::paste! { [<$inner:camel Fixturator>]::new_indexed($crate::prelude::Predictable, get_fixt_index!()) };
708                set_fixt_index!(get_fixt_index!() + 1);
709                $outer::new(fixturator.next().unwrap())
710            }
711        );
712    };
713}
714
715#[macro_export]
716/// Creates a simple way to generate enums that use the strum way of iterating
717/// <https://docs.rs/strum/0.18.0/strum/>
718/// iterates over all the variants (Predictable) or selects random variants (Unpredictable)
719/// You do still need to BYO "empty" variant as the macro doesn't know what to use there
720macro_rules! enum_fixturator {
721    ( $enum:ident, $empty:expr ) => {
722        use rand::seq::IteratorRandom;
723        use $crate::prelude::IntoEnumIterator;
724        fixturator!(
725            $enum,
726            $empty,
727            { $enum::iter().choose(&mut $crate::rng()).unwrap() },
728            {
729                let ret = $enum::iter().cycle().nth(self.0.index).unwrap();
730                set_fixt_index!(get_fixt_index!() + 1);
731                ret
732            }
733        );
734    };
735}
736
737#[cfg(test)]
738mod tests {
739    use crate::prelude::*;
740    use crate::string::PREDICTABLE_STRS;
741
742    // in general enums can have a mix of whatever in their variants
743    #[derive(PartialEq, Debug)]
744    pub enum Foo {
745        A,
746        B(String),
747    }
748
749    fixturator!(
750        Foo;
751        enum [ A B ];
752        curve Empty Foo::A;
753        curve Unpredictable match FooVariant::random() {
754            FooVariant::A => Foo::A,
755            FooVariant::B => Foo::B(fixt!(String)),
756        };
757        curve Predictable match FooVariant::nth(get_fixt_index!()) {
758            FooVariant::A => Foo::A,
759            FooVariant::B => Foo::B(StringFixturator::new_indexed(Predictable, get_fixt_index!()).next().unwrap()),
760        };
761    );
762
763    #[test]
764    fn enum_test() {
765        assert_eq!(FooFixturator::new(Predictable).next().unwrap(), Foo::A,);
766
767        FooFixturator::new(Unpredictable).next().unwrap();
768
769        assert_eq!(FooFixturator::new(Empty).next().unwrap(), Foo::A,);
770
771        let mut fixt_iter = FooFixturator::new(Predictable);
772        assert_eq!(fixt_iter.next().unwrap(), Foo::A);
773        let string = StringFixturator::new_indexed(Predictable, 1)
774            .next()
775            .unwrap();
776        assert_eq!(fixt_iter.next().unwrap(), Foo::B(string));
777    }
778
779    #[derive(PartialEq, Debug)]
780    pub enum UnitFoo {
781        A,
782        B,
783        C,
784    }
785
786    fixturator!(
787        UnitFoo;
788        unit variants [ A B C ] empty B;
789    );
790
791    #[test]
792    fn unit_variants_test() {
793        assert_eq!(
794            UnitFooFixturator::new(Predictable).next().unwrap(),
795            UnitFoo::A,
796        );
797
798        // smoke test Unpredictable
799        UnitFooFixturator::new(Unpredictable).next().unwrap();
800
801        assert_eq!(UnitFooFixturator::new(Empty).next().unwrap(), UnitFoo::B,);
802    }
803
804    #[derive(PartialEq, Debug, Clone)]
805    pub enum VariantFoo {
806        A(String),
807        B(usize),
808        C(bool),
809    }
810
811    fixturator!(
812        VariantFoo;
813        variants [ A(String) B(usize) C(bool) ];
814    );
815
816    #[test]
817    fn variant_variants_test() {
818        let mut predictable_fixturator = VariantFooFixturator::new(Predictable);
819        for expected in [
820            VariantFoo::A("💯".into()),
821            VariantFoo::B(1),
822            VariantFoo::C(true),
823            VariantFoo::A(".".into()),
824            VariantFoo::B(4),
825            VariantFoo::C(false),
826        ]
827        .iter()
828        {
829            assert_eq!(expected.to_owned(), predictable_fixturator.next().unwrap(),);
830        }
831
832        let mut unpredictable_fixturator = VariantFooFixturator::new(Unpredictable);
833        for _ in 0..10 {
834            // smoke test
835            unpredictable_fixturator.next().unwrap();
836        }
837
838        let mut empty_fixturator = VariantFooFixturator::new(Empty);
839        for _ in 0..10 {
840            match empty_fixturator.next().unwrap() {
841                VariantFoo::A(s) => assert_eq!(s, ""),
842                VariantFoo::B(n) => assert_eq!(n, 0),
843                VariantFoo::C(b) => assert!(!b),
844            }
845        }
846    }
847
848    #[derive(Debug, PartialEq)]
849    pub struct StringFoo(String);
850
851    impl From<String> for StringFoo {
852        fn from(s: String) -> Self {
853            Self(s)
854        }
855    }
856
857    fixturator!(StringFoo; from String;);
858
859    #[test]
860    fn from_test() {
861        let mut predictable_fixturator = StringFooFixturator::new(Predictable);
862        for expected in PREDICTABLE_STRS.iter() {
863            assert_eq!(
864                StringFoo::from(expected.to_string()),
865                predictable_fixturator.next().unwrap()
866            );
867        }
868
869        let mut unpredictable_fixturator = StringFooFixturator::new(Unpredictable);
870        for _ in 0..10 {
871            // smoke test
872            unpredictable_fixturator.next().unwrap();
873        }
874
875        let mut empty_fixturator = StringFooFixturator::new(Empty);
876        for _ in 0..10 {
877            assert_eq!(
878                StringFoo::from("".to_string()),
879                empty_fixturator.next().unwrap(),
880            );
881        }
882    }
883
884    #[derive(Debug, PartialEq)]
885    pub struct ConstructedFoo {
886        bar: bool,
887    }
888
889    impl ConstructedFoo {
890        fn from_bar(bar: bool) -> Self {
891            Self { bar }
892        }
893    }
894
895    fixturator!(
896        ConstructedFoo;
897        constructor fn from_bar(bool);
898    );
899
900    #[test]
901    fn constructor_test() {
902        let mut predictable_fixturator = ConstructedFooFixturator::new(Predictable);
903        for expected in [true, false].iter().cycle().take(5) {
904            assert_eq!(
905                ConstructedFoo::from_bar(*expected),
906                predictable_fixturator.next().unwrap(),
907            );
908        }
909
910        let mut unpredictable_fixturator = ConstructedFooFixturator::new(Unpredictable);
911        for _ in 0..10 {
912            // smoke test
913            unpredictable_fixturator.next().unwrap();
914        }
915
916        let mut empty_fixturator = ConstructedFooFixturator::new(Empty);
917        for _ in 0..10 {
918            assert_eq!(
919                ConstructedFoo::from_bar(false),
920                empty_fixturator.next().unwrap(),
921            );
922        }
923    }
924}