Skip to main content

anvil_test/
datasets.rs

1//! Pest-style parameterized tests via the `dataset!` macro.
2//!
3//! Pest in Laravel-land lets you write:
4//!
5//! ```php
6//! it('squares numbers', function ($n, $sq) {
7//!     expect($n * $n)->toBe($sq);
8//! })->with([[1, 1], [2, 4], [3, 9]]);
9//! ```
10//!
11//! Rust's `#[test]` is rigid about test names and arguments, so we generate
12//! a separate `#[tokio::test]` per row. The `dataset!` macro takes a base test
13//! name, a list of named cases, the parameter list, and the test body.
14//!
15//! ```ignore
16//! use anvilforge::assay::*;
17//!
18//! dataset!(squares_numbers, [
19//!     one      => (1, 1),
20//!     two      => (2, 4),
21//!     three    => (3, 9),
22//!     negative => (-3, 9),
23//! ], |(n, sq): (i32, i32)| {
24//!     expect(n * n).to_be(sq);
25//! });
26//! ```
27//!
28//! The closure parameter is a single tuple pattern + tuple type. Rust's
29//! declarative macros can't cleanly mix the case-level repetition with a
30//! per-arg parameter list, so we destructure once per generated test.
31//!
32//! Each row becomes its own test:
33//!
34//!   - `squares_numbers__one`
35//!   - `squares_numbers__two`
36//!   - `squares_numbers__three`
37//!   - `squares_numbers__negative`
38//!
39//! Async variants are supported by using `dataset!(name, [...], async |...| { ... })`.
40
41/// Generate one `#[test]` per row. Each row is `case_name => (arg1, arg2, ...)`.
42/// The closure parameter is a single tuple pattern + tuple type:
43/// `|(n, sq): (i32, i32)|`. For one-arg cases use `|(n,): (i32,)|`.
44#[macro_export]
45macro_rules! dataset {
46    ($base:ident, [ $($case:ident => $args:tt),* $(,)? ], | $pat:tt : $ty:tt | $body:block) => {
47        $(
48            $crate::paste::paste! {
49                #[test]
50                fn [<$base __ $case>]() {
51                    let $pat: $ty = $args;
52                    $body
53                }
54            }
55        )*
56    };
57}
58
59/// Async variant — generates `#[tokio::test]` per row.
60#[macro_export]
61macro_rules! dataset_async {
62    ($base:ident, [ $($case:ident => $args:tt),* $(,)? ], async | $pat:tt : $ty:tt | $body:block) => {
63        $(
64            $crate::paste::paste! {
65                #[tokio::test]
66                async fn [<$base __ $case>]() {
67                    let $pat: $ty = $args;
68                    $body
69                }
70            }
71        )*
72    };
73}