Skip to main content

iddqd/id_ord_map/
proptest_impls.rs

1//! Proptest strategies for generating [`IdOrdMap`]s with random inputs.
2
3use crate::{IdOrdItem, id_ord_map::IdOrdMap};
4use core::fmt;
5use proptest::{
6    arbitrary::{Arbitrary, StrategyFor, any_with},
7    collection::{SizeRange, VecStrategy, VecValueTree},
8    strategy::{NewTree, Strategy, ValueTree},
9    test_runner::TestRunner,
10};
11
12/// Strategy to create [`IdOrdMap`]s with a length in a certain range.
13///
14/// Created by the [`prop_strategy()`] function.
15#[must_use = "strategies do nothing unless used"]
16#[derive(Clone)]
17pub struct IdOrdMapStrategy<T>
18where
19    T: Strategy,
20{
21    inner: VecStrategy<T>,
22}
23
24impl<T> fmt::Debug for IdOrdMapStrategy<T>
25where
26    T: Strategy,
27{
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.debug_struct("IdOrdMapStrategy")
30            .field("inner", &self.inner)
31            .finish_non_exhaustive()
32    }
33}
34
35/// Creates a strategy to generate [`IdOrdMap`]s containing items drawn from
36/// `element` and with a size within the given range.
37///
38/// # Examples
39///
40/// ```
41/// use iddqd::{IdOrdItem, IdOrdMap, id_ord_map, id_upcast};
42/// use proptest::{
43///     arbitrary::any, strategy::Strategy, test_runner::TestRunner,
44/// };
45///
46/// #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
47/// struct Person {
48///     id: u32,
49///     name: String,
50/// }
51///
52/// impl IdOrdItem for Person {
53///     type Key<'a> = u32;
54///
55///     fn key(&self) -> Self::Key<'_> {
56///         self.id
57///     }
58///     id_upcast!();
59/// }
60///
61/// // Create a strategy using a tuple and mapping it to Person.
62/// let strategy = id_ord_map::prop_strategy(
63///     (any::<u32>(), any::<String>())
64///         .prop_map(|(id, name)| Person { id, name }),
65///     0..=5,
66/// );
67///
68/// // The strategy can be used in proptest contexts.
69/// let mut runner = TestRunner::default();
70/// let _tree = strategy.new_tree(&mut runner).unwrap();
71/// ```
72pub fn prop_strategy<T: Strategy>(
73    element: T,
74    size: impl Into<SizeRange>,
75) -> IdOrdMapStrategy<T> {
76    IdOrdMapStrategy { inner: proptest::collection::vec(element, size) }
77}
78
79impl<'a, T> Strategy for IdOrdMapStrategy<T>
80where
81    T: Strategy,
82    T::Value: 'a + IdOrdItem,
83    <T::Value as IdOrdItem>::Key<'a>: fmt::Debug,
84{
85    type Tree = IdOrdMapValueTree<T::Tree>;
86    type Value = IdOrdMap<T::Value>;
87
88    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
89        let inner = self.inner.new_tree(runner)?;
90
91        Ok(IdOrdMapValueTree { inner })
92    }
93}
94
95/// `ValueTree` corresponding to [`IdOrdMapStrategy`].
96#[derive(Clone)]
97pub struct IdOrdMapValueTree<T>
98where
99    T: ValueTree,
100{
101    inner: VecValueTree<T>,
102}
103
104impl<T> fmt::Debug for IdOrdMapValueTree<T>
105where
106    T: ValueTree + fmt::Debug,
107{
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        f.debug_struct("IdOrdMapValueTree")
110            .field("inner", &self.inner)
111            .finish_non_exhaustive()
112    }
113}
114
115impl<'a, T> ValueTree for IdOrdMapValueTree<T>
116where
117    T: ValueTree,
118    T::Value: 'a + IdOrdItem,
119    <T::Value as IdOrdItem>::Key<'a>: fmt::Debug,
120{
121    type Value = IdOrdMap<T::Value>;
122
123    fn current(&self) -> Self::Value {
124        let items = self.inner.current();
125        let mut map = IdOrdMap::new();
126
127        for item in items {
128            // Use insert_overwrite to handle duplicate keys.
129            map.insert_overwrite(item);
130        }
131
132        map
133    }
134
135    fn simplify(&mut self) -> bool {
136        self.inner.simplify()
137    }
138
139    fn complicate(&mut self) -> bool {
140        self.inner.complicate()
141    }
142}
143
144impl<'a, T> Arbitrary for IdOrdMap<T>
145where
146    T: 'a + IdOrdItem + Arbitrary,
147    <T as IdOrdItem>::Key<'a>: fmt::Debug,
148{
149    type Parameters = (SizeRange, T::Parameters);
150    type Strategy = IdOrdMapStrategy<StrategyFor<T>>;
151
152    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
153        let (size, element_args) = args;
154        prop_strategy(any_with::<T>(element_args), size)
155    }
156}