test_strategy/lib.rs
1// #![include_doc("../README.md", start("This crate provides two procedural macros, `#[derive(Arbitrary)]` and `#[proptest]`."))]
2//! This crate provides two procedural macros, `#[derive(Arbitrary)]` and `#[proptest]`.
3//!
4//! Each of these macros is an alternative to the following proptest's official macros.
5//!
6//! | [test-strategy][] | [proptest][] | [proptest-derive][] |
7//! | ------------------------------------------ | ------------------------------ | ------------------------------------ |
8//! | [`#[derive(Arbitrary)]`](#derivearbitrary) | | [`#[derive(Arbitrary)]`][official-a] |
9//! | [`#[proptest]`](#proptest) | [`proptest ! { }`][official-m] | |
10//!
11//! [test-strategy]: https://crates.io/crates/test-strategy
12//! [proptest]: https://crates.io/crates/proptest
13//! [proptest-derive]: https://crates.io/crates/proptest-derive
14//! [official-m]: https://altsysrq.github.io/rustdoc/proptest/latest/proptest/macro.proptest.html
15//! [official-a]: https://altsysrq.github.io/proptest-book/proptest-derive/modifiers.html
16//!
17//! The macros provided by this crate have the following advantages over the proptest's official macros.
18//!
19//! - Supports higher-order strategies. (`#[derive(Arbitrary)]` and `#[proptest]`)
20//! - Code formatting is not disabled. (`#[proptest]`)
21//!
22//! However, the syntax of this crate's macros are not compatible with the syntax of the official macros.
23//!
24//! ## Install
25//!
26//! Add this to your Cargo.toml:
27//!
28//! ```toml
29//! [dependencies]
30//! test-strategy = "0.4.3"
31//! proptest = "1.6.0"
32//! ```
33//!
34//! ## Example
35//!
36//! You can use `#[derive(Arbitrary)]` to automatically implement proptest's `Arbitrary` trait.
37//!
38//! ```rust
39//! use test_strategy::Arbitrary;
40//!
41//! #[derive(Arbitrary, Debug)]
42//! struct TestInputStruct {
43//! x: u32,
44//!
45//! #[strategy(1..10u32)]
46//! y: u32,
47//!
48//! #[strategy(0..#y)]
49//! z: u32,
50//! }
51//!
52//! #[derive(Arbitrary, Debug)]
53//! enum TestInputEnum {
54//! A,
55//! B,
56//! #[weight(3)]
57//! C,
58//! X(u32),
59//! Y(#[strategy(0..10u32)] u32),
60//! }
61//! ```
62//!
63//! You can define a property test by adding `#[proptest]` to the function.
64//!
65//! ```rust
66//! use test_strategy::proptest;
67//!
68//! #[proptest]
69//! fn my_test(_x: u32, #[strategy(1..10u32)] y: u32, #[strategy(0..#y)] z: u32) {
70//! assert!(1 <= y && y < 10);
71//! assert!(z <= y);
72//! }
73//! ```
74//!
75//! ## Attributes
76//!
77//! Attributes can be written in the following positions.
78//!
79//! | attribute | function | struct | enum | variant | field | function parameter |
80//! | --------------------------------------------------- | -------- | ------ | ---- | ------- | ----- | ------------------ |
81//! | [`#[strategy]`](#strategy) | | | | | ✔ | ✔ |
82//! | [`#[any]`](#any) | | | | | ✔ | ✔ |
83//! | [`#[weight]`](#weight) | | | | ✔ | | |
84//! | [`#[map]`](#map) | | | | | ✔ | ✔ |
85//! | [`#[filter]`](#filter) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
86//! | [`#[by_ref]`](#by_ref) | | | | | ✔ | ✔ |
87//! | [`#[arbitrary(args = T)]`](#arbitraryargs--t) | | ✔ | ✔ | | | |
88//! | [`#[arbitrary(bound(...))]`](#arbitraryboundt1-t2-) | | ✔ | ✔ | ✔ | ✔ | |
89//! | [`#[arbitrary(dump)]`](#arbitrarydump) | | ✔ | ✔ | | | |
90//! | [`#[proptest]`](#proptest) | ✔ | | | | | |
91//! | [`#[proptest(async = ...)]`](#proptestasync--) | ✔ | | | | | |
92//! | [`#[proptest(dump)]`](#proptestdump) | ✔ | | | | | |
93//!
94//! ## `#[derive(Arbitrary)]`
95//!
96//! You can implement `proptest::arbitrary::Arbitrary` automatically by adding `#[derive(Arbitrary)]` to struct or enum declaration.
97//!
98//! By default, all fields are set using the strategy obtained by `proptest::arbitrary::any()`.
99//!
100//! So the following two codes are equivalent.
101//!
102//! ```rust
103//! use test_strategy::Arbitrary;
104//!
105//! #[derive(Arbitrary, Debug)]
106//! struct TestInput {
107//! x: u32,
108//! y: u32,
109//! }
110//! ```
111//!
112//! ```rust
113//! use proptest::{
114//! arbitrary::{any, Arbitrary},
115//! strategy::{BoxedStrategy, Strategy},
116//! };
117//!
118//! #[derive(Debug)]
119//! struct TestInput {
120//! x: u32,
121//! y: u32,
122//! }
123//! impl Arbitrary for TestInput {
124//! type Parameters = ();
125//! type Strategy = BoxedStrategy<Self>;
126//!
127//! fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
128//! let x = any::<u32>();
129//! let y = any::<u32>();
130//! (x, y).prop_map(|(x, y)| Self { x, y }).boxed()
131//! }
132//! }
133//! ```
134//!
135//! ## `#[strategy]`
136//!
137//! You can specify a strategy to generate values for the field by adding `#[strategy(...)]` to the field.
138//!
139//! In the following example, the value of field `x` will be less than 20.
140//!
141//! ```rust
142//! use test_strategy::Arbitrary;
143//!
144//! #[derive(Arbitrary, Debug)]
145//! struct TestInput {
146//! #[strategy(0..20u32)]
147//! x: u32,
148//! }
149//! ```
150//!
151//! In `#[strategy]`, the values of other fields can be used by following `#` to the name of the field.
152//!
153//! In the following example, the value of `y` is less than or equal to `x`.
154//!
155//! ```rust
156//! use test_strategy::Arbitrary;
157//!
158//! #[derive(Arbitrary, Debug)]
159//! struct TestInput {
160//! x: u32,
161//! #[strategy(0..=#x)]
162//! y: u32,
163//! }
164//! ```
165//!
166//! ## `#[any]`
167//!
168//! Instead of writing `#[strategy(any_with::<Type>(expr))]`, you can write `#[any(expr)]`.
169//!
170//! ```rust
171//! use proptest::collection::size_range;
172//! use test_strategy::Arbitrary;
173//!
174//! #[derive(Arbitrary, Debug, PartialEq)]
175//! struct TestInput {
176//! #[any(size_range(0..16).lift())]
177//! x: Vec<u16>,
178//! }
179//! ```
180//!
181//! Instead of writing an expression to be passed to `any_with`, you can write only the value of the field to be changed from the default value.
182//!
183//! Therefore, the following `TestInputA`, `TestInputB` and `TestInputC` are equivalent.
184//!
185//! ```rust
186//! use test_strategy::Arbitrary;
187//!
188//! #[derive(Arbitrary, Debug)]
189//! struct TestInputA {
190//! #[any(InnerArgs { upper : 20, ..InnerArgs::default() })]
191//! a: Inner,
192//! }
193//! #[derive(Arbitrary, Debug)]
194//! struct TestInputB {
195//! #[any(InnerArgs::default(), upper = 20)]
196//! a: Inner,
197//! }
198//! #[derive(Arbitrary, Debug)]
199//! struct TestInputC {
200//! #[any(upper = 20)]
201//! a: Inner,
202//! }
203//!
204//! #[derive(Default)]
205//! struct InnerArgs {
206//! lower: i32,
207//! upper: i32,
208//! }
209//!
210//! #[derive(Arbitrary, Debug)]
211//! #[arbitrary(args = InnerArgs)]
212//! struct Inner {
213//! #[strategy(args.lower..args.upper)]
214//! x: i32,
215//! }
216//! ```
217//!
218//! ## `#[weight]`
219//!
220//! By default, all variants appear with equal probability.
221//!
222//! You can add `#[weight]` to the variant to change the probability of the variant appearing.
223//!
224//! In the following example, `TestInput::B` is twice as likely to appear as `TestInput::A`.
225//!
226//! ```rust
227//! use test_strategy::Arbitrary;
228//!
229//! #[derive(Arbitrary, Debug)]
230//! enum TestInput {
231//! A,
232//!
233//! #[weight(2)]
234//! B,
235//! }
236//! ```
237//!
238//! If you add `#[weight(0)]` to a variant, the variant does not appear, so you can use a type in that variant that cannot be used as `Arbitrary`.
239//!
240//! ```rust
241//! use test_strategy::Arbitrary;
242//!
243//! #[derive(Debug)]
244//! struct NotArbitrary;
245//!
246//! #[derive(Arbitrary, Debug)]
247//! enum TestInput {
248//! A,
249//!
250//! #[allow(dead_code)]
251//! #[weight(0)] // Removing this `#[weight(0)]` will cause a compile error.
252//! B(NotArbitrary),
253//! }
254//! ```
255//!
256//! ## `#[map]`
257//!
258//! Instead of using `prop_map` in `#[strategy(...)]`, `#[map(...)]` can be used.
259//!
260//! The following codes mean the same thing.
261//!
262//! ```rust
263//! use proptest::arbitrary::any;
264//! use proptest::strategy::Strategy;
265//! use test_strategy::Arbitrary;
266//!
267//! #[derive(Arbitrary, Debug)]
268//! struct TestInput1 {
269//! #[strategy(any::<u32>().prop_map(|x| x + 1))]
270//! x: u32,
271//! }
272//!
273//! #[derive(Arbitrary, Debug)]
274//! struct TestInput2 {
275//! #[strategy(any::<u32>())]
276//! #[map(|x| x + 1)]
277//! x: u32,
278//! }
279//!
280//! #[derive(Arbitrary, Debug)]
281//! struct TestInput3 {
282//! #[map(|x: u32| x + 1)]
283//! x: u32,
284//! }
285//! ```
286//!
287//! References to other fields in the function applied to `prop_map` or `#[map(...)]` will generate different strategies.
288//!
289//! Referencing another field in `#[strategy(...)]` will expand it to `prop_flat_map`, even if it is in `prop_map`.
290//!
291//! ```rust
292//! use proptest::arbitrary::any;
293//! use proptest::strategy::{Just, Strategy};
294//! use test_strategy::Arbitrary;
295//!
296//! #[derive(Arbitrary, Debug)]
297//! struct T1 {
298//! x: u32,
299//!
300//! #[strategy(any::<u32>().prop_map(move |y| #x + y))]
301//! y: u32,
302//! }
303//! // The code above generates the following strategy.
304//! let t1 = any::<u32>()
305//! .prop_flat_map(|x| (Just(x), any::<u32>().prop_map(move |y| x + y)))
306//! .prop_map(|(x, y)| T1 { x, y });
307//! ```
308//!
309//! On the other hand, if you refer to another field in `#[map]`, it will expand to `prop_map`.
310//!
311//! ```rust
312//! use proptest::arbitrary::any;
313//! use proptest::strategy::Strategy;
314//! use test_strategy::Arbitrary;
315//!
316//! #[derive(Arbitrary, Debug)]
317//! struct T2 {
318//! x: u32,
319//!
320//! #[map(|y: u32| #x + y)]
321//! y: u32,
322//! }
323//! // The code above generates the following strategy.
324//! let t2 = (any::<u32>(), any::<u32>()).prop_map(|(x, y)| T2 { x, y });
325//! ```
326//!
327//! If the input and output types of the function specified in `#[map]` are different, the value type of the strategy set in `#[strategy]` is the type of the function's input, not the type of the field.
328//!
329//! ```rust
330//! use proptest::arbitrary::any;
331//! use proptest::sample::Index;
332//! use test_strategy::Arbitrary;
333//!
334//! #[derive(Arbitrary, Debug)]
335//! struct T1 {
336//! #[strategy(any::<Index>())]
337//! #[map(|i: Index| i.index(10))]
338//! x: usize,
339//! }
340//!
341//! // `#[strategy(any::<Index>())]` can be omitted.
342//! #[derive(Arbitrary, Debug)]
343//! struct T2 {
344//! #[map(|i: Index| i.index(10))]
345//! x: usize,
346//! }
347//! ```
348//!
349//! ## `#[filter]`
350//!
351//! By adding `#[filter]` , you can limit the values generated.
352//!
353//! In the following examples, x is an even number.
354//!
355//! ```rust
356//! use test_strategy::Arbitrary;
357//!
358//! #[derive(Arbitrary, Debug)]
359//! struct TestInput {
360//! #[filter(#x % 2 == 0)]
361//! x: u32,
362//! }
363//! ```
364//!
365//! You can also use multiple variables in a predicate.
366//!
367//! ```rust
368//! use test_strategy::Arbitrary;
369//!
370//! #[derive(Arbitrary, Debug)]
371//! #[filter((#x + #y) % 2 == 0)]
372//! struct T1 {
373//! x: u32,
374//! y: u32,
375//! }
376//!
377//! #[derive(Arbitrary, Debug)]
378//! struct T2 {
379//! x: u32,
380//! #[filter((#x + #y) % 2 == 0)]
381//! y: u32,
382//! }
383//! ```
384//!
385//! You can use the value of a structure or enum in the filter by using `#self`.
386//!
387//! ```rust
388//! use test_strategy::Arbitrary;
389//!
390//! #[derive(Arbitrary, Debug)]
391//! #[filter((#self.x + #self.y) % 2 == 0)]
392//! struct TestInput {
393//! x: u32,
394//! y: u32,
395//! }
396//! ```
397//!
398//! If the expression specified for `#[filter]` does not contain a variable named by appending # to its own field name, the expression is treated as a predicate function, rather than an expression that returns a bool.
399//!
400//! ```rust
401//! use test_strategy::Arbitrary;
402//!
403//! #[derive(Arbitrary, Debug)]
404//! struct TestInput {
405//! #[filter(is_even)]
406//! x: u32,
407//! }
408//! fn is_even(x: &u32) -> bool {
409//! x % 2 == 0
410//! }
411//!
412//! #[derive(Arbitrary, Debug)]
413//! struct T2 {
414//! a: u32,
415//!
416//! // Since `#a` exists but `#b` does not, it is treated as a predicate function.
417//! #[filter(|&x| x > #a)]
418//! b: u32,
419//! }
420//! ```
421//!
422//! Similarly, an expression that does not contain `#self` in the `#[filter(...)]` that it attaches to a type is treated as a predicate function.
423//!
424//! ```rust
425//! use test_strategy::Arbitrary;
426//!
427//! #[derive(Arbitrary, Debug)]
428//! #[filter(is_even)]
429//! struct T {
430//! x: u32,
431//! }
432//! fn is_even(t: &T) -> bool {
433//! t.x % 2 == 0
434//! }
435//! ```
436//!
437//! You can specify a filter name by passing two arguments to `#[filter]`.
438//!
439//! ```rust
440//! use test_strategy::Arbitrary;
441//!
442//! #[derive(Arbitrary, Debug)]
443//! struct TestInput {
444//! #[filter("x is even", #x % 2 == 0)]
445//! x: u32,
446//! }
447//! ```
448//!
449//! ## `#[by_ref]`
450//!
451//! By default, if you use a variable with `#[strategy]`, `#[any]`, `#[map]` or `#[filter]` with `#` attached to it, the cloned value is set.
452//!
453//! Adding `#[by_ref]` to the field makes it use the reference instead of the cloned value.
454//!
455//! ```rust
456//! use test_strategy::Arbitrary;
457//!
458//! #[derive(Arbitrary, Debug)]
459//! struct TestInput {
460//! #[by_ref]
461//! #[strategy(1..10u32)]
462//! x: u32,
463//!
464//! #[strategy(0..*#x)]
465//! y: u32,
466//! }
467//! ```
468//!
469//! ## `#[arbitrary]`
470//!
471//! ### `#[arbitrary(args = T)]`
472//!
473//! Specifies the type of `Arbitrary::Parameters`.
474//!
475//! You can use the `Rc` value of this type in `#[strategy]`, `#[any]`, or `#[filter]` with the variable name `args`.
476//!
477//! ```rust
478//! use test_strategy::Arbitrary;
479//!
480//! #[derive(Debug, Default)]
481//! struct TestInputArgs {
482//! x_max: u32,
483//! }
484//!
485//! #[derive(Arbitrary, Debug)]
486//! #[arbitrary(args = TestInputArgs)]
487//! struct TestInput {
488//! #[strategy(0..=args.x_max)]
489//! x: u32,
490//! }
491//! ```
492//!
493//! ### `#[arbitrary(bound(T1, T2, ..))]`
494//!
495//! By default, if the type of field for which `#[strategy]` is not specified contains a generic parameter, that type is set to trait bounds.
496//!
497//! Therefore, the following `TestInputA` and `TestInputB` are equivalent.
498//!
499//! ```rust
500//! use proptest::{
501//! arbitrary::any, arbitrary::Arbitrary, strategy::BoxedStrategy, strategy::Strategy,
502//! };
503//! use test_strategy::Arbitrary;
504//!
505//! #[derive(Arbitrary, Debug)]
506//! struct TestInputA<T> {
507//! x: T,
508//! }
509//!
510//! #[derive(Debug)]
511//! struct TestInputB<T> {
512//! x: T,
513//! }
514//! impl<T: Arbitrary + 'static> Arbitrary for TestInputB<T> {
515//! type Parameters = ();
516//! type Strategy = BoxedStrategy<Self>;
517//!
518//! fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
519//! any::<T>().prop_map(|x| Self { x }).boxed()
520//! }
521//! }
522//! ```
523//!
524//! Types of fields with `#[strategy]` do not set trait bounds automatically, so you need to set trait bound manually with `#[arbitrary(bound(T))]`.
525//!
526//! ```rust
527//! use proptest::arbitrary::any_with;
528//! use test_strategy::Arbitrary;
529//!
530//! #[derive(Arbitrary, Debug, PartialEq)]
531//! #[arbitrary(bound(T))]
532//! struct TestInput<T> {
533//! #[strategy(any_with::<T>(Default::default()))]
534//! x: T,
535//! }
536//! ```
537//!
538//! You can also specify where predicate instead of type.
539//!
540//! ```rust
541//! use proptest::arbitrary::{any_with, Arbitrary};
542//! use test_strategy::Arbitrary;
543//!
544//! #[derive(Arbitrary, Debug, PartialEq)]
545//! #[arbitrary(bound(T : Arbitrary + 'static))]
546//! struct TestInput<T> {
547//! #[strategy(any_with::<T>(Default::default()))]
548//! x: T,
549//! }
550//! ```
551//!
552//! `..` means automatically generated trait bounds.
553//!
554//! The following example uses a manually specified trait bounds in addition to the automatically generated trait bounds.
555//!
556//! ```rust
557//! use proptest::arbitrary::any_with;
558//! use test_strategy::Arbitrary;
559//!
560//! #[derive(Arbitrary, Debug, PartialEq)]
561//! #[arbitrary(bound(T1, ..))]
562//! struct TestInput<T1, T2> {
563//! #[strategy(any_with::<T1>(Default::default()))]
564//! x: T1,
565//!
566//! y: T2,
567//! }
568//! ```
569//!
570//! ### `#[arbitrary(dump)]`
571//!
572//! Causes a compile error and outputs the code generated by `#[derive(Arbitrary)]` as an error message.
573//!
574//! ## `#[proptest]`
575//!
576//! `#[proptest]` is the attribute used instead of `#[test]` when defining a property test.
577//!
578//! The following example defines a test that takes a variety of integers as input.
579//!
580//! ```rust
581//! use test_strategy::proptest;
582//!
583//! #[proptest]
584//! fn my_test(_input: i32) {
585//! // ...
586//! }
587//! ```
588//!
589//! You can add `#[strategy]`, `#[any]`, `#[filter]`, `#[by_ref]` to the parameter of the function with `# [proptest]`.
590//!
591//! ```rust
592//! use test_strategy::proptest;
593//!
594//! #[proptest]
595//! fn my_test2(#[strategy(10..20)] _input: i32) {
596//! // ...
597//! }
598//! ```
599//!
600//! You can change the configuration of a property test by setting the argument of `#[proptest]` attribute to a value of [`proptest::prelude::ProptestConfig`](https://docs.rs/proptest/latest/proptest/prelude/index.html#reexport.ProptestConfig) type.
601//!
602//! ```rust
603//! use proptest::prelude::ProptestConfig;
604//! use test_strategy::proptest;
605//!
606//! #[proptest(ProptestConfig { cases : 1000, ..ProptestConfig::default() })]
607//! fn my_test_with_config(_input: i32) {
608//! // ...
609//! }
610//! ```
611//!
612//! As with `#[any]`, you can also set only the value of the field to be changed from the default value.
613//!
614//! The example below is equivalent to the one above.
615//!
616//! ```rust
617//! use proptest::prelude::ProptestConfig;
618//! use test_strategy::proptest;
619//!
620//! #[proptest(ProptestConfig::default(), cases = 1000)]
621//! fn my_test_with_config_2(_input: i32) {
622//! // ...
623//! }
624//!
625//! #[proptest(cases = 1000)]
626//! fn my_test_with_config_3(_input: i32) {
627//! // ...
628//! }
629//! ```
630//!
631//! ### `#[proptest(async = ...)]`
632//!
633//! Async functions can be tested by setting `async = ...` to the argument of `#[proptest]`.
634//!
635//! The following values are allowed after `async =`.
636//! The value specifies the asynchronous runtime used for the test.
637//!
638//! - "tokio"
639//!
640//! ```toml
641//! [dev-dependencies]
642//! test-strategy = "0.4.3"
643//! proptest = "1.6.0"
644//! tokio = { version = "1.38.0", features = ["rt-multi-thread"] }
645//! ```
646//!
647//! ```rust
648//! use test_strategy::proptest;
649//! use proptest::prop_assert;
650//!
651//! #[proptest(async = "tokio")]
652//! async fn my_test_async() {
653//! async { }.await;
654//! prop_assert!(true);
655//! }
656//! ```
657//!
658//! ### `#[proptest(dump)]`
659//!
660//! You can use `#[proptest(dump)]` and output the code generated by `#[proptest]` as an compile error message.
661//!
662//! ```compile_fail
663//! #[proptest(dump)]
664//! fn my_test(_input: i32) {
665//! // ...
666//! }
667//! ```
668// #![include_doc("../README.md", end("## License"))]
669
670extern crate proc_macro;
671
672#[macro_use]
673mod syn_utils;
674mod arbitrary;
675mod bound;
676mod proptest_fn;
677
678use syn::{parse_macro_input, DeriveInput, ItemFn};
679use syn_utils::into_macro_output;
680
681#[proc_macro_attribute]
682pub fn proptest(
683 attr: proc_macro::TokenStream,
684 item: proc_macro::TokenStream,
685) -> proc_macro::TokenStream {
686 let item_fn = parse_macro_input!(item as ItemFn);
687 into_macro_output(proptest_fn::build_proptest(attr.into(), item_fn))
688}
689
690#[proc_macro_derive(
691 Arbitrary,
692 attributes(arbitrary, strategy, any, map, filter, weight, by_ref)
693)]
694pub fn derive_arbitrary(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
695 let input = parse_macro_input!(input as DeriveInput);
696 into_macro_output(arbitrary::derive_arbitrary(input))
697}