gwr_components/
test_helpers.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3use std::cmp::min;
4use std::collections::HashMap;
5use std::rc::Rc;
6
7use futures::FutureExt;
8use futures::channel::oneshot;
9use futures::channel::oneshot::{Receiver, Sender};
10use futures::future::select_all;
11use gwr_engine::engine::Engine;
12use gwr_engine::port::InPort;
13use gwr_engine::time::clock::Clock;
14use gwr_engine::traits::SimObject;
15use gwr_engine::types::SimResult;
16use gwr_track::entity::{Entity, GetEntity};
17#[doc(hidden)]
18pub use paste::paste;
19
20use crate::arbiter::Arbiter;
21use crate::arbiter::policy::{Priority, PriorityRoundRobin};
22use crate::flow_controls::limiter::Limiter;
23use crate::source::Source;
24use crate::store::Store;
25use crate::{connect_port, option_box_repeat, rc_limiter};
26
27#[derive(Clone)]
28pub struct ArbiterInputData {
29    pub val: usize,
30    pub count: usize,
31    pub weight: usize,
32    pub priority: Priority,
33}
34
35pub fn check_round_robin(inputs: &[ArbiterInputData], data: &[usize]) {
36    let total_count: usize = inputs.iter().map(|i| i.count).sum();
37    assert_eq!(data.len(), total_count);
38
39    let mut inputs = inputs.to_vec();
40    let mut offset = 0;
41    loop {
42        // Determine the count for each input value in the next window. Note that this
43        // copes with inputs producing the same value and inputs not producing
44        // their full weight in the window.
45        let mut expected_window_counts: HashMap<usize, usize> = HashMap::new();
46        let mut window_length = 0;
47        let max_priority = inputs
48            .iter()
49            .map(|i| {
50                if i.count > 0 {
51                    i.priority
52                } else {
53                    Priority::default()
54                }
55            })
56            .max()
57            .unwrap();
58        for input in &mut inputs {
59            let value_count = min(input.count, input.weight);
60            if input.priority == max_priority && value_count > 0 {
61                expected_window_counts
62                    .entry(input.val)
63                    .and_modify(|e| *e += value_count)
64                    .or_insert(value_count);
65
66                window_length += value_count;
67                input.count -= value_count;
68            }
69        }
70        if window_length == 0 {
71            return;
72        }
73
74        let mut window_counts = HashMap::new();
75        for value in data.iter().skip(offset).take(window_length) {
76            window_counts
77                .entry(*value)
78                .and_modify(|e| *e += 1)
79                .or_insert(1);
80        }
81        assert_eq!(window_counts, expected_window_counts);
82
83        offset += window_length;
84    }
85}
86
87pub fn priority_policy_test_core(engine: &mut Engine, inputs: &[ArbiterInputData]) {
88    let clock = engine.default_clock();
89    let num_inputs = inputs.len();
90    let total_count = inputs.iter().map(|e| e.count).sum();
91    let mut policy = PriorityRoundRobin::new(num_inputs);
92    for (i, input) in inputs.iter().enumerate() {
93        policy = policy.set_priority(i, input.priority);
94    }
95
96    let arbiter = Arbiter::new_and_register(
97        engine,
98        &clock,
99        engine.top(),
100        "arb",
101        num_inputs,
102        Box::new(policy),
103    );
104    let mut sources = Vec::new();
105    for (i, input) in inputs.iter().enumerate() {
106        sources.push(Source::new_and_register(
107            engine,
108            engine.top(),
109            &("source_".to_owned() + &i.to_string()),
110            option_box_repeat!(input.val; input.count),
111        ));
112    }
113
114    let write_limiter = rc_limiter!(&clock, 1);
115    let store_limiter =
116        Limiter::new_and_register(engine, &clock, engine.top(), "limit_wr", write_limiter);
117    let store =
118        Store::new_and_register(engine, &clock, engine.top(), "store", total_count).unwrap();
119    connect_port!(store_limiter, tx => store, rx).unwrap();
120
121    for (i, source) in sources.iter_mut().enumerate() {
122        connect_port!(source, tx => arbiter, rx, i).unwrap();
123    }
124    connect_port!(arbiter, tx => store_limiter, rx).unwrap();
125
126    let port = InPort::new(
127        engine,
128        &clock,
129        &Rc::new(Entity::new(engine.top(), "port")),
130        "test_rx",
131    );
132    store.connect_port_tx(port.state()).unwrap();
133
134    let check_inputs = inputs.to_owned();
135    engine.spawn(async move {
136        let mut store_get = vec![0; total_count];
137        for i in &mut store_get {
138            *i = port.get()?.await;
139        }
140
141        check_round_robin(&check_inputs, &store_get);
142        Ok(())
143    });
144}
145
146pub fn one_shot_channel<T>() -> (Sender<T>, Receiver<T>) {
147    oneshot::channel()
148}
149
150pub trait NoTrafficPort {
151    fn wait_for_traffic<'a>(
152        &'a self,
153        step_name: &'a str,
154        port_name: &'static str,
155    ) -> futures::future::LocalBoxFuture<'a, SimResult>;
156}
157
158impl<T> NoTrafficPort for InPort<T>
159where
160    T: SimObject,
161{
162    fn wait_for_traffic<'a>(
163        &'a self,
164        location: &'a str,
165        port_name: &'static str,
166    ) -> futures::future::LocalBoxFuture<'a, SimResult> {
167        async move {
168            let value = self.get()?.await;
169            panic!("{location}: unexpected {port_name} traffic: {value}");
170            #[allow(unreachable_code)]
171            Ok(())
172        }
173        .boxed_local()
174    }
175}
176
177pub async fn expect_no_traffic(
178    location: &str,
179    clock: &Clock,
180    ticks: u64,
181    receivers: Vec<(&'static str, &dyn NoTrafficPort)>,
182) -> SimResult {
183    if receivers.is_empty() {
184        clock.wait_ticks(ticks).await;
185        return Ok(());
186    }
187
188    let mut traffic = select_all(
189        receivers
190            .into_iter()
191            .map(|(port_name, receiver)| receiver.wait_for_traffic(location, port_name))
192            .collect::<Vec<_>>(),
193    )
194    .fuse();
195    let mut timeout = clock.wait_ticks(ticks).fuse();
196
197    futures::select! {
198        (result, _, _) = traffic => {
199            result?;
200            panic!("{location}: no-traffic check completed unexpectedly");
201        }
202        _ = timeout => {}
203    }
204
205    Ok(())
206}
207
208pub trait ValueCheck<T> {
209    fn assert_matches(&self, check_id: &str, actual: &T);
210}
211
212impl<T> ValueCheck<T> for T
213where
214    T: PartialEq + std::fmt::Debug,
215{
216    fn assert_matches(&self, check_id: &str, actual: &T) {
217        assert_eq!(actual, self, "{check_id}: value mismatch");
218    }
219}
220
221#[derive(Clone, Copy, Debug)]
222pub struct StepLocation {
223    pub file: &'static str,
224    pub line: u32,
225    pub column: u32,
226}
227
228impl StepLocation {
229    #[must_use]
230    pub const fn new(file: &'static str, line: u32, column: u32) -> Self {
231        Self { file, line, column }
232    }
233}
234
235impl std::fmt::Display for StepLocation {
236    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237        write!(f, "{}:{}:{}", self.file, self.line, self.column)
238    }
239}
240
241/// Build a simulation test harness around a component.
242///
243/// This macro generates the harness struct, local `Port`/`Step` enums, helper
244/// macros, fixed step execution, stateful step generators, and recursive
245/// sequence/parallel step driving for a component testbench.
246///
247/// See the crate-level Testing documentation for the intended usage pattern,
248/// generated API, and examples.
249#[macro_export]
250macro_rules! build_component_harness {
251    (
252        $(#[$meta:meta])*
253        $vis:vis harness $harness:ident <$item:ident> {
254            component: $component_field:ident : $component_ty:ty,
255            $($sections:tt)*
256        }
257    ) => {
258        $crate::build_component_harness! {
259            @normalize
260            [$(#[$meta])*]
261            [$vis]
262            [$harness]
263            [$vis struct $harness<$item> where $item: gwr_engine::traits::SimObject]
264            [impl<$item> $harness<$item> where $item: gwr_engine::traits::SimObject]
265            [<$item, Expected>]
266            [Expected]
267            [()]
268            [where $item: gwr_engine::traits::SimObject]
269            [$item]
270            [$component_field: $component_ty]
271            []
272            []
273            []
274            []
275            $($sections)*
276        }
277    };
278
279    (
280        @normalize
281        [$($meta:tt)*]
282        [$vis:vis]
283        [$harness:ident]
284        [$($struct_head:tt)+]
285        [$($impl_head:tt)+]
286        [$($step_generics_decl:tt)*]
287        [$expected_ident:ident]
288        [$expected_ty:ty]
289        [$($step_where:tt)*]
290        [$item_ty:ty]
291        [$component_field:ident : $component_ty:ty]
292        [$($rx_ports:tt)*]
293        [$($tx_ports:tt)*]
294        [$($rx_port_arrays:tt)*]
295        [$($tx_port_arrays:tt)*]
296        rx ports: { $($rx_section:tt)* }, $($rest:tt)*
297    ) => {
298        $crate::build_component_harness! {
299            @normalize
300            [$($meta)*]
301            [$vis]
302            [$harness]
303            [$($struct_head)*]
304            [$($impl_head)*]
305            [$($step_generics_decl)*]
306            [$expected_ident]
307            [$expected_ty]
308            [$($step_where)*]
309            [$item_ty]
310            [$component_field: $component_ty]
311            [$($rx_section)*]
312            [$($tx_ports)*]
313            [$($rx_port_arrays)*]
314            [$($tx_port_arrays)*]
315            $($rest)*
316        }
317    };
318
319    (
320        @normalize
321        [$($meta:tt)*]
322        [$vis:vis]
323        [$harness:ident]
324        [$($struct_head:tt)+]
325        [$($impl_head:tt)+]
326        [$($step_generics_decl:tt)*]
327        [$expected_ident:ident]
328        [$expected_ty:ty]
329        [$($step_where:tt)*]
330        [$item_ty:ty]
331        [$component_field:ident : $component_ty:ty]
332        [$($rx_ports:tt)*]
333        [$($tx_ports:tt)*]
334        [$($rx_port_arrays:tt)*]
335        [$($tx_port_arrays:tt)*]
336        rx ports: { $($rx_section:tt)* }
337    ) => {
338        $crate::build_component_harness! {
339            @normalize
340            [$($meta)*]
341            [$vis]
342            [$harness]
343            [$($struct_head)*]
344            [$($impl_head)*]
345            [$($step_generics_decl)*]
346            [$expected_ident]
347            [$expected_ty]
348            [$($step_where)*]
349            [$item_ty]
350            [$component_field: $component_ty]
351            [$($rx_section)*]
352            [$($tx_ports)*]
353            [$($rx_port_arrays)*]
354            [$($tx_port_arrays)*]
355        }
356    };
357
358    (
359        @normalize
360        [$($meta:tt)*]
361        [$vis:vis]
362        [$harness:ident]
363        [$($struct_head:tt)+]
364        [$($impl_head:tt)+]
365        [$($step_generics_decl:tt)*]
366        [$expected_ident:ident]
367        [$expected_ty:ty]
368        [$($step_where:tt)*]
369        [$item_ty:ty]
370        [$component_field:ident : $component_ty:ty]
371        [$($rx_ports:tt)*]
372        [$($tx_ports:tt)*]
373        [$($rx_port_arrays:tt)*]
374        [$($tx_port_arrays:tt)*]
375        tx ports: { $($tx_section:tt)* }, $($rest:tt)*
376    ) => {
377        $crate::build_component_harness! {
378            @normalize
379            [$($meta)*]
380            [$vis]
381            [$harness]
382            [$($struct_head)*]
383            [$($impl_head)*]
384            [$($step_generics_decl)*]
385            [$expected_ident]
386            [$expected_ty]
387            [$($step_where)*]
388            [$item_ty]
389            [$component_field: $component_ty]
390            [$($rx_ports)*]
391            [$($tx_section)*]
392            [$($rx_port_arrays)*]
393            [$($tx_port_arrays)*]
394            $($rest)*
395        }
396    };
397
398    (
399        @normalize
400        [$($meta:tt)*]
401        [$vis:vis]
402        [$harness:ident]
403        [$($struct_head:tt)+]
404        [$($impl_head:tt)+]
405        [$($step_generics_decl:tt)*]
406        [$expected_ident:ident]
407        [$expected_ty:ty]
408        [$($step_where:tt)*]
409        [$item_ty:ty]
410        [$component_field:ident : $component_ty:ty]
411        [$($rx_ports:tt)*]
412        [$($tx_ports:tt)*]
413        [$($rx_port_arrays:tt)*]
414        [$($tx_port_arrays:tt)*]
415        tx ports: { $($tx_section:tt)* }
416    ) => {
417        $crate::build_component_harness! {
418            @normalize
419            [$($meta)*]
420            [$vis]
421            [$harness]
422            [$($struct_head)*]
423            [$($impl_head)*]
424            [$($step_generics_decl)*]
425            [$expected_ident]
426            [$expected_ty]
427            [$($step_where)*]
428            [$item_ty]
429            [$component_field: $component_ty]
430            [$($rx_ports)*]
431            [$($tx_section)*]
432            [$($rx_port_arrays)*]
433            [$($tx_port_arrays)*]
434        }
435    };
436
437    (
438        @normalize
439        [$($meta:tt)*]
440        [$vis:vis]
441        [$harness:ident]
442        [$($struct_head:tt)+]
443        [$($impl_head:tt)+]
444        [$($step_generics_decl:tt)*]
445        [$expected_ident:ident]
446        [$expected_ty:ty]
447        [$($step_where:tt)*]
448        [$item_ty:ty]
449        [$component_field:ident : $component_ty:ty]
450        [$($rx_ports:tt)*]
451        [$($tx_ports:tt)*]
452        [$($rx_port_arrays:tt)*]
453        [$($tx_port_arrays:tt)*]
454        rx port arrays: { $($rx_array_section:tt)* }, $($rest:tt)*
455    ) => {
456        $crate::build_component_harness! {
457            @normalize
458            [$($meta)*]
459            [$vis]
460            [$harness]
461            [$($struct_head)*]
462            [$($impl_head)*]
463            [$($step_generics_decl)*]
464            [$expected_ident]
465            [$expected_ty]
466            [$($step_where)*]
467            [$item_ty]
468            [$component_field: $component_ty]
469            [$($rx_ports)*]
470            [$($tx_ports)*]
471            [$($rx_array_section)*]
472            [$($tx_port_arrays)*]
473            $($rest)*
474        }
475    };
476
477    (
478        @normalize
479        [$($meta:tt)*]
480        [$vis:vis]
481        [$harness:ident]
482        [$($struct_head:tt)+]
483        [$($impl_head:tt)+]
484        [$($step_generics_decl:tt)*]
485        [$expected_ident:ident]
486        [$expected_ty:ty]
487        [$($step_where:tt)*]
488        [$item_ty:ty]
489        [$component_field:ident : $component_ty:ty]
490        [$($rx_ports:tt)*]
491        [$($tx_ports:tt)*]
492        [$($rx_port_arrays:tt)*]
493        [$($tx_port_arrays:tt)*]
494        rx port arrays: { $($rx_array_section:tt)* }
495    ) => {
496        $crate::build_component_harness! {
497            @normalize
498            [$($meta)*]
499            [$vis]
500            [$harness]
501            [$($struct_head)*]
502            [$($impl_head)*]
503            [$($step_generics_decl)*]
504            [$expected_ident]
505            [$expected_ty]
506            [$($step_where)*]
507            [$item_ty]
508            [$component_field: $component_ty]
509            [$($rx_ports)*]
510            [$($tx_ports)*]
511            [$($rx_array_section)*]
512            [$($tx_port_arrays)*]
513        }
514    };
515
516    (
517        @normalize
518        [$($meta:tt)*]
519        [$vis:vis]
520        [$harness:ident]
521        [$($struct_head:tt)+]
522        [$($impl_head:tt)+]
523        [$($step_generics_decl:tt)*]
524        [$expected_ident:ident]
525        [$expected_ty:ty]
526        [$($step_where:tt)*]
527        [$item_ty:ty]
528        [$component_field:ident : $component_ty:ty]
529        [$($rx_ports:tt)*]
530        [$($tx_ports:tt)*]
531        [$($rx_port_arrays:tt)*]
532        [$($tx_port_arrays:tt)*]
533        tx port arrays: { $($tx_array_section:tt)* }, $($rest:tt)*
534    ) => {
535        $crate::build_component_harness! {
536            @normalize
537            [$($meta)*]
538            [$vis]
539            [$harness]
540            [$($struct_head)*]
541            [$($impl_head)*]
542            [$($step_generics_decl)*]
543            [$expected_ident]
544            [$expected_ty]
545            [$($step_where)*]
546            [$item_ty]
547            [$component_field: $component_ty]
548            [$($rx_ports)*]
549            [$($tx_ports)*]
550            [$($rx_port_arrays)*]
551            [$($tx_array_section)*]
552            $($rest)*
553        }
554    };
555
556    (
557        @normalize
558        [$($meta:tt)*]
559        [$vis:vis]
560        [$harness:ident]
561        [$($struct_head:tt)+]
562        [$($impl_head:tt)+]
563        [$($step_generics_decl:tt)*]
564        [$expected_ident:ident]
565        [$expected_ty:ty]
566        [$($step_where:tt)*]
567        [$item_ty:ty]
568        [$component_field:ident : $component_ty:ty]
569        [$($rx_ports:tt)*]
570        [$($tx_ports:tt)*]
571        [$($rx_port_arrays:tt)*]
572        [$($tx_port_arrays:tt)*]
573        tx port arrays: { $($tx_array_section:tt)* }
574    ) => {
575        $crate::build_component_harness! {
576            @normalize
577            [$($meta)*]
578            [$vis]
579            [$harness]
580            [$($struct_head)*]
581            [$($impl_head)*]
582            [$($step_generics_decl)*]
583            [$expected_ident]
584            [$expected_ty]
585            [$($step_where)*]
586            [$item_ty]
587            [$component_field: $component_ty]
588            [$($rx_ports)*]
589            [$($tx_ports)*]
590            [$($rx_port_arrays)*]
591            [$($tx_array_section)*]
592        }
593    };
594
595    (
596        @normalize
597        [$($meta:tt)*]
598        [$vis:vis]
599        [$harness:ident]
600        [$($struct_head:tt)+]
601        [$($impl_head:tt)+]
602        [$($step_generics_decl:tt)*]
603        [$expected_ident:ident]
604        [$expected_ty:ty]
605        [$($step_where:tt)*]
606        [$item_ty:ty]
607        [$component_field:ident : $component_ty:ty]
608        [$($rx_ports:tt)*]
609        [$($tx_ports:tt)*]
610        [$($rx_port_arrays:tt)*]
611        [$($tx_port_arrays:tt)*]
612    ) => {
613        $crate::build_component_harness! {
614            @impl_inferred
615            [$($meta)*]
616            [$vis]
617            [$harness]
618            [$($struct_head)*]
619            [$($impl_head)*]
620            [$($step_generics_decl)*]
621            [$expected_ident]
622            [$expected_ty]
623            [$($step_where)*]
624            [$item_ty]
625            [$component_field: $component_ty]
626            rx ports: { $($rx_ports)* },
627            tx ports: { $($tx_ports)* },
628            rx port arrays: { $($rx_port_arrays)* },
629            tx port arrays: { $($tx_port_arrays)* },
630        }
631    };
632
633    (
634        @impl_inferred
635        [$($meta:tt)*]
636        [$vis:vis]
637        [$harness:ident]
638        [$($struct_head:tt)+]
639        [$($impl_head:tt)+]
640        [$($step_generics_decl:tt)*]
641        [$expected_ident:ident]
642        [$expected_ty:ty]
643        [$($step_where:tt)*]
644        [$item_ty:ty]
645        [$component_field:ident : $component_ty:ty]
646        rx ports: {
647            $(
648                $rx_variant:ident <$rx_ty:ty> => $rx_field:ident
649            ),* $(,)?
650        },
651        tx ports: {
652            $(
653                $tx_variant:ident <$tx_ty:ty> => $tx_field:ident
654            ),* $(,)?
655        },
656        rx port arrays: {
657            $(
658                $rx_array_variant:ident <$rx_array_ty:ty> => $rx_array_field:ident {
659                    count: $rx_array_count:ident
660                }
661            ),* $(,)?
662        },
663        tx port arrays: {
664            $(
665                $tx_array_variant:ident <$tx_array_ty:ty> => $tx_array_field:ident {
666                    count: $tx_array_count:ident
667                }
668            ),* $(,)?
669        } $(,)?
670    ) => {
671        $crate::build_component_harness! {
672            @impl
673            [$($meta)*]
674            [$vis]
675            [$harness]
676            [$($struct_head)*]
677            [$($impl_head)*]
678            [$($step_generics_decl)*]
679            [$expected_ident]
680            [$expected_ty]
681            [$($step_where)*]
682            [$item_ty]
683            [$component_field: $component_ty]
684            rx ports: {
685                $(
686                    $rx_variant <$rx_ty> => $rx_field {
687                        port: { [<port_ $rx_field>] }
688                    }
689                ),*
690            },
691            tx ports: {
692                $(
693                    $tx_variant <$tx_ty, $tx_ty> => $tx_field {
694                        connect: { [<connect_port_ $tx_field>] }
695                    }
696                ),*
697            },
698            rx port arrays: {
699                $(
700                    $rx_array_variant <$rx_array_ty> => $rx_array_field {
701                        port: { [<port_ $rx_array_field _i>] },
702                        count: $rx_array_count
703                    }
704                ),*
705            },
706            tx port arrays: {
707                $(
708                    $tx_array_variant <$tx_array_ty, $tx_array_ty> => $tx_array_field {
709                        connect: { [<connect_port_ $tx_array_field _i>] },
710                        count: $tx_array_count
711                    }
712                ),*
713            }
714        }
715    };
716
717    (
718        @impl_model
719        [$(#[$meta:meta])*]
720        [$vis:vis]
721        [$harness:ident]
722        [$item:ident]
723        [$default_expected:ty]
724        [$access_memory:path]
725        [$component_field:ident : $component_ty:ty]
726        rx ports: { $($rx_variant:ident <$rx_ty:ty> => $rx_field:ident),* $(,)? },
727        tx ports: { $($tx_variant:ident <$tx_ty:ty> => $tx_field:ident),* $(,)? },
728        rx port arrays: {
729            $($rx_array_variant:ident <$rx_array_ty:ty> => $rx_array_field:ident {
730                count: $rx_array_count:ident
731            }),* $(,)?
732        },
733        tx port arrays: {
734            $($tx_array_variant:ident <$tx_array_ty:ty> => $tx_array_field:ident {
735                count: $tx_array_count:ident
736            }),* $(,)?
737        } $(,)?
738    ) => {
739        $crate::build_component_harness! {
740            @impl
741            [$(#[$meta])*]
742            [$vis]
743            [$harness]
744            [
745                $vis struct $harness<$item>
746                where
747                    $item: $access_memory
748                        + gwr_engine::traits::SimObject
749                        + Clone
750                        + std::fmt::Debug
751                        + 'static
752            ]
753            [
754                impl<$item> $harness<$item>
755                where
756                    $item: $access_memory
757                        + gwr_engine::traits::SimObject
758                        + Clone
759                        + std::fmt::Debug
760                        + 'static
761            ]
762            [<$item, Expected>]
763            [Expected]
764            [$default_expected]
765            [where
766                $item: $access_memory
767                    + gwr_engine::traits::SimObject
768                    + Clone
769                    + std::fmt::Debug
770                    + 'static
771            ]
772            [$item]
773            [$component_field: $component_ty]
774            rx ports: {
775                $(
776                    $rx_variant <$rx_ty> => $rx_field {
777                        port: { [<port_ $rx_field>] }
778                    }
779                ),*
780            },
781            tx ports: {
782                $(
783                    $tx_variant <$tx_ty, $default_expected> => $tx_field {
784                        connect: { [<connect_port_ $tx_field>] }
785                    }
786                ),*
787            },
788            rx port arrays: {
789                $(
790                    $rx_array_variant <$rx_array_ty> => $rx_array_field {
791                        port: { [<port_ $rx_array_field _i>] },
792                        count: $rx_array_count
793                    }
794                ),*
795            },
796            tx port arrays: {
797                $(
798                    $tx_array_variant <$tx_array_ty, $default_expected> => $tx_array_field {
799                        connect: { [<connect_port_ $tx_array_field _i>] },
800                        count: $tx_array_count
801                    }
802                ),*
803            },
804        }
805    };
806
807    (
808        @impl
809        [$(#[$meta:meta])*]
810        [$vis:vis]
811        [$harness:ident]
812        [$($struct_head:tt)+]
813        [$($impl_head:tt)+]
814        [$($step_generics_decl:tt)*]
815        [$expected_ident:ident]
816        [$expected_ty:ty]
817        [$($step_where:tt)*]
818        [$item_ty:ty]
819        [$component_field:ident : $component_ty:ty]
820        rx ports: {
821            $(
822                $rx_variant:ident <$rx_ty:ty> => $rx_field:ident {
823                    port: { $($rx_method:tt)+ }
824                }
825            ),* $(,)?
826        },
827        tx ports: {
828            $(
829                $tx_variant:ident <$tx_ty:ty, $tx_expected_ty:ty> => $tx_field:ident {
830                    connect: { $($tx_method:tt)+ }
831                }
832            ),* $(,)?
833        },
834        rx port arrays: {
835            $(
836                $rx_array_variant:ident <$rx_array_ty:ty> => $rx_array_field:ident {
837                    port: { $($rx_array_method:tt)+ },
838                    count: $rx_array_count:ident
839                }
840            ),* $(,)?
841        },
842        tx port arrays: {
843            $(
844                $tx_array_variant:ident <$tx_array_ty:ty, $tx_array_expected_ty:ty> => $tx_array_field:ident {
845                    connect: { $($tx_array_method:tt)+ },
846                    count: $tx_array_count:ident
847                }
848            ),* $(,)?
849        } $(,)?
850    ) => {
851        $crate::test_helpers::paste! {
852            #[derive(Clone, Copy, Debug, PartialEq, Eq, std::hash::Hash)]
853            $vis enum Port {
854                $($rx_variant,)*
855                $($tx_variant,)*
856                $($rx_array_variant(usize),)*
857                $($tx_array_variant(usize),)*
858            }
859
860            #[derive(Clone, Debug)]
861            $vis enum Step<$item_ty, $expected_ident = ()> {
862                Seq {
863                    location: $crate::test_helpers::StepLocation,
864                    steps: Vec<Step<$item_ty, $expected_ty>>,
865                },
866                Par {
867                    location: $crate::test_helpers::StepLocation,
868                    steps: Vec<Step<$item_ty, $expected_ty>>,
869                },
870                $([<Send $rx_variant>] {
871                    location: $crate::test_helpers::StepLocation,
872                    port: Port,
873                    value: $rx_ty,
874                },)*
875                $([<Expect $tx_variant>] {
876                    location: $crate::test_helpers::StepLocation,
877                    port: Port,
878                    value: $tx_expected_ty,
879                },)*
880                $([<Send $rx_array_variant>] {
881                    location: $crate::test_helpers::StepLocation,
882                    port: Port,
883                    value: $rx_array_ty,
884                },)*
885                $([<Expect $tx_array_variant>] {
886                    location: $crate::test_helpers::StepLocation,
887                    port: Port,
888                    value: $tx_array_expected_ty,
889                },)*
890                ExpectNoTraffic {
891                    location: $crate::test_helpers::StepLocation,
892                    ports: Vec<Port>,
893                    ticks: u64,
894                },
895                Delay {
896                    location: $crate::test_helpers::StepLocation,
897                    ports: Vec<Port>,
898                    ticks: u64,
899                },
900                #[doc(hidden)]
901                __Expected(std::marker::PhantomData<fn() -> $expected_ident>),
902            }
903
904            impl<$item_ty, $expected_ident> Step<$item_ty, $expected_ident> {
905                fn location(&self) -> $crate::test_helpers::StepLocation {
906                    match self {
907                        Step::Seq { location, .. }
908                        | Step::Par { location, .. }
909                        $(| Step::[<Send $rx_variant>] { location, .. })*
910                        $(| Step::[<Expect $tx_variant>] { location, .. })*
911                        $(| Step::[<Send $rx_array_variant>] { location, .. })*
912                        $(| Step::[<Expect $tx_array_variant>] { location, .. })*
913                        | Step::ExpectNoTraffic { location, .. }
914                        | Step::Delay { location, .. } => *location,
915                        Step::__Expected(_) => {
916                            unreachable!("marker variant is not a harness step");
917                        }
918                    }
919                }
920            }
921
922            struct [<$harness Ports>]<$item_ty> $($step_where)* {
923                $(
924                    [<$rx_field _driver>]: Option<gwr_engine::port::OutPort<$rx_ty>>,
925                )*
926                $(
927                    [<$tx_field _receiver>]: Option<gwr_engine::port::InPort<$tx_ty>>,
928                )*
929                $(
930                    [<$rx_array_field _drivers>]: Vec<Option<gwr_engine::port::OutPort<$rx_array_ty>>>,
931                )*
932                $(
933                    [<$tx_array_field _receivers>]: Vec<Option<gwr_engine::port::InPort<$tx_array_ty>>>,
934                )*
935                _item: std::marker::PhantomData<fn() -> $item_ty>,
936            }
937
938            impl<$item_ty> [<$harness Ports>]<$item_ty> $($step_where)* {
939                fn new_empty(&self) -> Self {
940                    Self {
941                        $(
942                            [<$rx_field _driver>]: None,
943                        )*
944                        $(
945                            [<$tx_field _receiver>]: None,
946                        )*
947                        $(
948                            [<$rx_array_field _drivers>]: std::iter::repeat_with(|| None)
949                                .take(self.[<$rx_array_field _drivers>].len())
950                                .collect(),
951                        )*
952                        $(
953                            [<$tx_array_field _receivers>]: std::iter::repeat_with(|| None)
954                                .take(self.[<$tx_array_field _receivers>].len())
955                                .collect(),
956                        )*
957                        _item: std::marker::PhantomData,
958                    }
959                }
960
961                fn take_selected(
962                    &mut self,
963                    selected: &std::collections::HashSet<Port>,
964                    location: &str,
965                ) -> Self {
966                    let mut port_collection = self.new_empty();
967                    for port in selected {
968                        match *port {
969                            $(
970                            Port::$rx_variant => {
971                                port_collection.[<$rx_field _driver>] = Some(
972                                    self.[<$rx_field _driver>]
973                                        .take()
974                                        .unwrap_or_else(|| panic!("{location}: {} driver already taken", stringify!($rx_field))),
975                                );
976                            }
977                            )*
978                            $(
979                            Port::$tx_variant => {
980                                port_collection.[<$tx_field _receiver>] = Some(
981                                    self.[<$tx_field _receiver>]
982                                        .take()
983                                        .unwrap_or_else(|| panic!("{location}: {} receiver already taken", stringify!($tx_field))),
984                                );
985                            }
986                            )*
987                            $(
988                            Port::$rx_array_variant(idx) => {
989                                port_collection.[<$rx_array_field _drivers>][idx] = Some(
990                                    self.[<$rx_array_field _drivers>]
991                                        .get_mut(idx)
992                                        .and_then(|driver| driver.take())
993                                        .unwrap_or_else(|| panic!("{location}: {} driver index {idx} out of range or already taken", stringify!($rx_array_field))),
994                                );
995                            }
996                            )*
997                            $(
998                            Port::$tx_array_variant(idx) => {
999                                port_collection.[<$tx_array_field _receivers>][idx] = Some(
1000                                    self.[<$tx_array_field _receivers>]
1001                                        .get_mut(idx)
1002                                        .and_then(|receiver| receiver.take())
1003                                        .unwrap_or_else(|| panic!("{location}: {} receiver index {idx} out of range or already taken", stringify!($tx_array_field))),
1004                                );
1005                            }
1006                            )*
1007                        }
1008                    }
1009                    port_collection
1010                }
1011
1012                fn return_ports(&mut self, mut port_collection: Self, location: &str) {
1013                    $(
1014                    if let Some(driver) = port_collection.[<$rx_field _driver>].take() {
1015                        if self.[<$rx_field _driver>].replace(driver).is_some() {
1016                            panic!("{location}: {} driver returned twice", stringify!($rx_field));
1017                        }
1018                    }
1019                    )*
1020                    $(
1021                    if let Some(receiver) = port_collection.[<$tx_field _receiver>].take() {
1022                        if self.[<$tx_field _receiver>].replace(receiver).is_some() {
1023                            panic!("{location}: {} receiver returned twice", stringify!($tx_field));
1024                        }
1025                    }
1026                    )*
1027                    $(
1028                    for (idx, driver) in port_collection.[<$rx_array_field _drivers>].into_iter().enumerate() {
1029                        if let Some(driver) = driver {
1030                            if self.[<$rx_array_field _drivers>][idx].replace(driver).is_some() {
1031                                panic!("{location}: {} driver index {idx} returned twice", stringify!($rx_array_field));
1032                            }
1033                        }
1034                    }
1035                    )*
1036                    $(
1037                    for (idx, receiver) in port_collection.[<$tx_array_field _receivers>].into_iter().enumerate() {
1038                        if let Some(receiver) = receiver {
1039                            if self.[<$tx_array_field _receivers>][idx].replace(receiver).is_some() {
1040                                panic!("{location}: {} receiver index {idx} returned twice", stringify!($tx_array_field));
1041                            }
1042                        }
1043                    }
1044                    )*
1045                }
1046
1047                fn collect_step_ports(
1048                    step: &Step<$item_ty, $expected_ty>,
1049                    ports: &mut std::collections::HashSet<Port>,
1050                ) {
1051                    match step {
1052                        Step::<$item_ty, $expected_ty>::Seq { steps, .. }
1053                        | Step::<$item_ty, $expected_ty>::Par { steps, .. } => {
1054                            for step in steps {
1055                                Self::collect_step_ports(step, ports);
1056                            }
1057                        }
1058                        $(
1059                        Step::<$item_ty, $expected_ty>::[<Send $rx_variant>] { port, .. } => {
1060                            ports.insert(*port);
1061                        }
1062                        )*
1063                        $(
1064                        Step::<$item_ty, $expected_ty>::[<Expect $tx_variant>] { port, .. } => {
1065                            ports.insert(*port);
1066                        }
1067                        )*
1068                        $(
1069                        Step::<$item_ty, $expected_ty>::[<Send $rx_array_variant>] { port, .. } => {
1070                            ports.insert(*port);
1071                        }
1072                        )*
1073                        $(
1074                        Step::<$item_ty, $expected_ty>::[<Expect $tx_array_variant>] { port, .. } => {
1075                            ports.insert(*port);
1076                        }
1077                        )*
1078                        Step::<$item_ty, $expected_ty>::ExpectNoTraffic { ports: step_ports, .. } => {
1079                            ports.extend(step_ports.iter().copied());
1080                        }
1081                        Step::<$item_ty, $expected_ty>::Delay { ports: step_ports, .. } => {
1082                            ports.extend(step_ports.iter().copied());
1083                        }
1084                        Step::<$item_ty, $expected_ty>::__Expected(_) => {
1085                            unreachable!("marker variant is not a harness step");
1086                        }
1087                    }
1088                }
1089
1090                fn run_steps(
1091                    mut self,
1092                    steps: Vec<Step<$item_ty, $expected_ty>>,
1093                    clock: gwr_engine::time::clock::Clock,
1094                    spawner: gwr_engine::executor::Spawner,
1095                ) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, gwr_engine::types::SimError>> + 'static>>
1096                where
1097                    $($rx_ty: Clone + 'static,)*
1098                    $($tx_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_ty> + 'static,)*
1099                    $($rx_array_ty: Clone + 'static,)*
1100                    $($tx_array_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_array_ty> + 'static,)*
1101                    $item_ty: 'static,
1102                    $expected_ty: 'static,
1103                {
1104                    Box::pin(async move {
1105                        for step in steps {
1106                            let location = step.location().to_string();
1107                            match step {
1108                                Step::<$item_ty, $expected_ty>::Seq { steps, .. } => {
1109                                    self = self.run_steps(steps, clock.clone(), spawner.clone()).await?;
1110                                }
1111                                Step::<$item_ty, $expected_ty>::Par { steps, .. } => {
1112                                    let mut completions = Vec::with_capacity(steps.len());
1113
1114                                    for step in steps {
1115                                        let branch_location = step.location().to_string();
1116                                        let mut branch_ports = std::collections::HashSet::new();
1117                                        Self::collect_step_ports(&step, &mut branch_ports);
1118                                        let branch_runner_ports = self.take_selected(&branch_ports, &branch_location);
1119                                        let branch_clock = clock.clone();
1120                                        let branch_spawner = spawner.clone();
1121                                        let (complete_tx, complete_rx) = $crate::test_helpers::one_shot_channel();
1122
1123                                        spawner.spawn(async move {
1124                                            let branch_steps = match step {
1125                                                Step::<$item_ty, $expected_ty>::Seq { steps, .. } => steps,
1126                                                step => vec![step],
1127                                            };
1128                                            let result = branch_runner_ports
1129                                                .run_steps(branch_steps, branch_clock, branch_spawner)
1130                                                .await;
1131                                            complete_tx
1132                                                .send((branch_location.clone(), result))
1133                                                .unwrap_or_else(|_| panic!("{branch_location}: parallel step receiver dropped"));
1134                                            Ok::<(), gwr_engine::types::SimError>(())
1135                                        });
1136                                        completions.push(complete_rx);
1137                                    }
1138
1139                                    for completion in completions {
1140                                        let (branch_location, result) = completion
1141                                            .await
1142                                            .unwrap_or_else(|_| panic!("{location}: parallel section dropped"));
1143                                        let returned = result?;
1144                                        self.return_ports(returned, &branch_location);
1145                                    }
1146                                }
1147                                $(
1148                                Step::<$item_ty, $expected_ty>::[<Send $rx_variant>] { port, value, .. } => {
1149                                    let Port::$rx_variant = port else {
1150                                        panic!("{location}: {port:?}: step is for {}", stringify!($rx_variant));
1151                                    };
1152                                    self.[<$rx_field _driver>]
1153                                        .as_ref()
1154                                        .expect(concat!(stringify!($rx_field), " driver already taken"))
1155                                        .put(value.clone())?
1156                                        .await;
1157                                }
1158                                )*
1159                                $(
1160                                Step::<$item_ty, $expected_ty>::[<Expect $tx_variant>] { port, value, .. } => {
1161                                    let Port::$tx_variant = port else {
1162                                        panic!("{location}: {port:?}: step is for {}", stringify!($tx_variant));
1163                                    };
1164                                    let actual = self.[<$tx_field _receiver>]
1165                                        .as_ref()
1166                                        .expect(concat!(stringify!($tx_field), " receiver already taken"))
1167                                        .get()?
1168                                        .await;
1169                                    $crate::test_helpers::ValueCheck::assert_matches(
1170                                        &value,
1171                                        &format!("{location} {port:?}"),
1172                                        &actual,
1173                                    );
1174                                }
1175                                )*
1176                                $(
1177                                Step::<$item_ty, $expected_ty>::[<Send $rx_array_variant>] { port, value, .. } => {
1178                                    let Port::$rx_array_variant(idx) = port else {
1179                                        panic!("{location}: {port:?}: step is for {}", stringify!($rx_array_variant));
1180                                    };
1181                                    self.[<$rx_array_field _drivers>]
1182                                        .get(idx)
1183                                        .and_then(|driver| driver.as_ref())
1184                                        .unwrap_or_else(|| panic!("{location}: {} driver index {idx} out of range or already taken", stringify!($rx_array_field)))
1185                                        .put(value.clone())?
1186                                        .await;
1187                                }
1188                                )*
1189                                $(
1190                                Step::<$item_ty, $expected_ty>::[<Expect $tx_array_variant>] { port, value, .. } => {
1191                                    let Port::$tx_array_variant(idx) = port else {
1192                                        panic!("{location}: {port:?}: step is for {}", stringify!($tx_array_variant));
1193                                    };
1194                                    let actual = self.[<$tx_array_field _receivers>]
1195                                        .get(idx)
1196                                        .and_then(|receiver| receiver.as_ref())
1197                                        .unwrap_or_else(|| panic!("{location}: {} receiver index {idx} out of range or already taken", stringify!($tx_array_field)))
1198                                        .get()?
1199                                        .await;
1200                                    $crate::test_helpers::ValueCheck::assert_matches(
1201                                        &value,
1202                                        &format!("{location} {port:?}"),
1203                                        &actual,
1204                                    );
1205                                }
1206                                )*
1207                                Step::<$item_ty, $expected_ty>::ExpectNoTraffic { ports, ticks, .. } => {
1208                                    let mut receivers = Vec::new();
1209                                    for port in &ports {
1210                                        match port {
1211                                            $(
1212                                            Port::$tx_variant => {
1213                                                let receiver = self.[<$tx_field _receiver>]
1214                                                    .as_ref()
1215                                                    .expect(concat!(stringify!($tx_field), " receiver already taken"));
1216                                                receivers.push((stringify!($tx_field), receiver as &dyn $crate::test_helpers::NoTrafficPort));
1217                                            }
1218                                            )*
1219                                            $(
1220                                            Port::$tx_array_variant(idx) => {
1221                                                let receiver = self.[<$tx_array_field _receivers>]
1222                                                    .get(*idx)
1223                                                    .and_then(|receiver| receiver.as_ref())
1224                                                    .unwrap_or_else(|| panic!("{location}: {} receiver index {idx} out of range or already taken", stringify!($tx_array_field)));
1225                                                receivers.push((stringify!($tx_array_field), receiver as &dyn $crate::test_helpers::NoTrafficPort));
1226                                            }
1227                                            )*
1228                                            _ => {
1229                                                panic!("{location}: {port:?}: expect no traffic requires tx ports");
1230                                            }
1231                                        }
1232                                    }
1233
1234                                    $crate::test_helpers::expect_no_traffic(
1235                                        &location,
1236                                        &clock,
1237                                        ticks,
1238                                        receivers,
1239                                    )
1240                                    .await?;
1241                                }
1242                                Step::<$item_ty, $expected_ty>::Delay { ports, ticks, .. } => {
1243                                    if !ports.is_empty() {
1244                                        panic!("{location}: delay does not take ports");
1245                                    }
1246                                    clock.wait_ticks(ticks).await;
1247                                }
1248                                Step::<$item_ty, $expected_ty>::__Expected(_) => {
1249                                    unreachable!("marker variant is not a harness step");
1250                                }
1251                            }
1252                        }
1253                        Ok(self)
1254                    })
1255                }
1256            }
1257
1258            $(#[$meta])*
1259            $($struct_head)* {
1260                pub engine: gwr_engine::engine::Engine,
1261                pub clock: gwr_engine::time::clock::Clock,
1262                pub $component_field: $component_ty,
1263                $(
1264                    [<$rx_field _driver>]: Option<gwr_engine::port::OutPort<$rx_ty>>,
1265                )*
1266                $(
1267                    [<$tx_field _receiver>]: Option<gwr_engine::port::InPort<$tx_ty>>,
1268                )*
1269                $(
1270                    [<$rx_array_field _drivers>]: Vec<gwr_engine::port::OutPort<$rx_array_ty>>,
1271                )*
1272                $(
1273                    [<$tx_array_field _receivers>]: Vec<gwr_engine::port::InPort<$tx_array_ty>>,
1274                )*
1275                _expected: std::marker::PhantomData<$expected_ty>,
1276            }
1277
1278            $($impl_head)* {
1279                pub fn new(
1280                    mut engine: gwr_engine::engine::Engine,
1281                    $component_field: $component_ty,
1282                    $($rx_array_count: usize,)*
1283                    $($tx_array_count: usize,)*
1284                ) -> Self {
1285                    let clock = engine.default_clock();
1286                    let top = engine.top();
1287
1288                    $(
1289                        let mut [<$rx_field _driver>] = gwr_engine::port::OutPort::new(
1290                            top,
1291                            concat!(stringify!($rx_field), "_driver"),
1292                        );
1293                        [<$rx_field _driver>]
1294                            .connect($component_field.$($rx_method)+())
1295                            .unwrap();
1296                    )*
1297
1298                    $(
1299                        let [<$tx_field _receiver>] = gwr_engine::port::InPort::new(
1300                            &engine,
1301                            &clock,
1302                            top,
1303                            concat!(stringify!($tx_field), "_receiver"),
1304                        );
1305                        $component_field
1306                            .$($tx_method)+([<$tx_field _receiver>].state())
1307                            .unwrap();
1308                    )*
1309
1310                    $(
1311                        let mut [<$rx_array_field _drivers>] = Vec::with_capacity($rx_array_count);
1312                        for idx in 0..$rx_array_count {
1313                            let mut driver = gwr_engine::port::OutPort::new(
1314                                top,
1315                                &format!("{}_{}_driver", stringify!($rx_array_field), idx),
1316                            );
1317                            driver.connect($component_field.$($rx_array_method)+(idx)).unwrap();
1318                            [<$rx_array_field _drivers>].push(driver);
1319                        }
1320                    )*
1321
1322                    $(
1323                        let mut [<$tx_array_field _receivers>] = Vec::with_capacity($tx_array_count);
1324                        for idx in 0..$tx_array_count {
1325                            let receiver = gwr_engine::port::InPort::new(
1326                                &engine,
1327                                &clock,
1328                                top,
1329                                &format!("{}_{}_receiver", stringify!($tx_array_field), idx),
1330                            );
1331                            $component_field
1332                                .$($tx_array_method)+(idx, receiver.state())
1333                                .unwrap();
1334                            [<$tx_array_field _receivers>].push(receiver);
1335                        }
1336                    )*
1337
1338                    Self {
1339                        engine,
1340                        clock,
1341                        $component_field,
1342                        $(
1343                            [<$rx_field _driver>]: Some([<$rx_field _driver>]),
1344                        )*
1345                        $(
1346                            [<$tx_field _receiver>]: Some([<$tx_field _receiver>]),
1347                        )*
1348                        $(
1349                            [<$rx_array_field _drivers>],
1350                        )*
1351                        $(
1352                            [<$tx_array_field _receivers>],
1353                        )*
1354                        _expected: std::marker::PhantomData,
1355                    }
1356                }
1357
1358                $(
1359                    pub fn [<take_ $rx_field _driver>](
1360                        &mut self,
1361                    ) -> gwr_engine::port::OutPort<$rx_ty> {
1362                        self.[<$rx_field _driver>]
1363                            .take()
1364                            .expect(concat!(stringify!($rx_field), " driver already taken"))
1365                    }
1366
1367                )*
1368
1369                $(
1370                    pub fn [<take_ $tx_field _receiver>](
1371                        &mut self,
1372                    ) -> gwr_engine::port::InPort<$tx_ty> {
1373                        self.[<$tx_field _receiver>]
1374                            .take()
1375                            .expect(concat!(stringify!($tx_field), " receiver already taken"))
1376                    }
1377
1378                    pub async fn [<expect_no_ $tx_field _traffic>](
1379                        &self,
1380                        ticks: u64,
1381                    ) -> gwr_engine::types::SimResult {
1382                        $crate::test_helpers::expect_no_traffic(
1383                            stringify!($tx_field),
1384                            &self.clock,
1385                            ticks,
1386                            vec![
1387                                (
1388                                    stringify!($tx_field),
1389                                    self.[<$tx_field _receiver>]
1390                                        .as_ref()
1391                                        .expect(concat!(stringify!($tx_field), " receiver already taken")),
1392                                ),
1393                            ],
1394                        )
1395                        .await
1396                    }
1397                )*
1398
1399                $(
1400                    pub fn [<take_ $rx_array_field _drivers>](
1401                        &mut self,
1402                    ) -> Vec<gwr_engine::port::OutPort<$rx_array_ty>> {
1403                        std::mem::take(&mut self.[<$rx_array_field _drivers>])
1404                    }
1405
1406                )*
1407
1408                $(
1409                    pub fn [<take_ $tx_array_field _receivers>](
1410                        &mut self,
1411                    ) -> Vec<gwr_engine::port::InPort<$tx_array_ty>> {
1412                        std::mem::take(&mut self.[<$tx_array_field _receivers>])
1413                    }
1414
1415                )*
1416
1417                #[allow(unreachable_code)]
1418                pub fn run_steps<Steps>(
1419                    &mut self,
1420                    steps: Steps,
1421                )
1422                where
1423                    Steps: IntoIterator<Item = Step<$item_ty, $expected_ty>>,
1424                    Steps::IntoIter: 'static,
1425                    $($rx_ty: Clone + 'static,)*
1426                    $($tx_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_ty> + 'static,)*
1427                    $($rx_array_ty: Clone + 'static,)*
1428                    $($tx_array_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_array_ty> + 'static,)*
1429                    $item_ty: 'static,
1430                    $expected_ty: 'static,
1431                {
1432                    self.run_step_generator(steps.into_iter());
1433                }
1434
1435                #[allow(unreachable_code)]
1436                pub fn run_step_generator<I>(
1437                    &mut self,
1438                    mut steps: I,
1439                )
1440                where
1441                    I: Iterator<Item = Step<$item_ty, $expected_ty>> + 'static,
1442                    $($rx_ty: Clone + 'static,)*
1443                    $($tx_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_ty> + 'static,)*
1444                    $($rx_array_ty: Clone + 'static,)*
1445                    $($tx_array_expected_ty: Clone + $crate::test_helpers::ValueCheck<$tx_array_ty> + 'static,)*
1446                    $item_ty: 'static,
1447                    $expected_ty: 'static,
1448                {
1449                    let harness_complete = gwr_engine::events::once::Once::default();
1450                    let notify_harness_complete = harness_complete.clone();
1451                    let harness_completed = std::rc::Rc::new(std::cell::RefCell::new(false));
1452                    let mark_harness_completed = harness_completed.clone();
1453                    let clock = self.clock.clone();
1454                    let spawner = self.engine.spawner();
1455                    let runner_ports = [<$harness Ports>]::<$item_ty> {
1456                        $(
1457                            [<$rx_field _driver>]: Some(self.[<take_ $rx_field _driver>]()),
1458                        )*
1459                        $(
1460                            [<$tx_field _receiver>]: Some(self.[<take_ $tx_field _receiver>]()),
1461                        )*
1462                        $(
1463                            [<$rx_array_field _drivers>]: self
1464                                .[<take_ $rx_array_field _drivers>]()
1465                                .into_iter()
1466                                .map(Some)
1467                                .collect(),
1468                        )*
1469                        $(
1470                            [<$tx_array_field _receivers>]: self
1471                                .[<take_ $tx_array_field _receivers>]()
1472                                .into_iter()
1473                                .map(Some)
1474                                .collect(),
1475                        )*
1476                        _item: std::marker::PhantomData,
1477                    };
1478
1479                    self.engine.spawn(async move {
1480                        let mut runner_ports = runner_ports;
1481                        for step in steps {
1482                            runner_ports = runner_ports
1483                                .run_steps(vec![step], clock.clone(), spawner.clone())
1484                                .await?;
1485                        }
1486                        *mark_harness_completed.borrow_mut() = true;
1487                        notify_harness_complete.notify()?;
1488                        Ok::<(), gwr_engine::types::SimError>(())
1489                    });
1490
1491                    let engine = &mut self.engine;
1492                    engine.run_until(Box::new(harness_complete)).unwrap();
1493                    if !*harness_completed.borrow() {
1494                        panic!("test harness did not complete");
1495                    }
1496                }
1497
1498            }
1499
1500            #[allow(unused_macros)]
1501            macro_rules! step_location {
1502                () => {
1503                    $crate::test_helpers::StepLocation::new(file!(), line!(), column!())
1504                };
1505            }
1506
1507            $(
1508                #[allow(unused_macros)]
1509                macro_rules! [<send_ $rx_field>] {
1510                    ($value:expr,) => {
1511                        [<send_ $rx_field>]!($value)
1512                    };
1513                    ($value:expr) => {
1514                        Step::[<Send $rx_variant>] {
1515                            location: step_location!(),
1516                            port: Port::$rx_variant,
1517                            value: $value,
1518                        }
1519                    };
1520                }
1521            )*
1522
1523            $(
1524                #[allow(unused_macros)]
1525                macro_rules! [<expect_ $tx_field>] {
1526                    ($value:expr,) => {
1527                        [<expect_ $tx_field>]!($value)
1528                    };
1529                    ($value:expr) => {
1530                        Step::[<Expect $tx_variant>] {
1531                            location: step_location!(),
1532                            port: Port::$tx_variant,
1533                            value: $value,
1534                        }
1535                    };
1536                }
1537            )*
1538
1539            $(
1540                #[allow(unused_macros)]
1541                macro_rules! [<send_ $rx_array_field>] {
1542                    ($idx:expr, $value:expr,) => {
1543                        [<send_ $rx_array_field>]!($idx, $value)
1544                    };
1545                    ($idx:expr, $value:expr) => {
1546                        Step::[<Send $rx_array_variant>] {
1547                            location: step_location!(),
1548                            port: Port::$rx_array_variant($idx),
1549                            value: $value,
1550                        }
1551                    };
1552                }
1553            )*
1554
1555            $(
1556                #[allow(unused_macros)]
1557                macro_rules! [<expect_ $tx_array_field>] {
1558                    ($idx:expr, $value:expr,) => {
1559                        [<expect_ $tx_array_field>]!($idx, $value)
1560                    };
1561                    ($idx:expr, $value:expr) => {
1562                        Step::[<Expect $tx_array_variant>] {
1563                            location: step_location!(),
1564                            port: Port::$tx_array_variant($idx),
1565                            value: $value,
1566                        }
1567                    };
1568                }
1569            )*
1570
1571            #[allow(unused_macros)]
1572            macro_rules! delay {
1573                ($ticks:expr,) => {
1574                    delay!($ticks)
1575                };
1576                ($ticks:expr) => {
1577                    Step::Delay {
1578                        location: step_location!(),
1579                        ports: Vec::new(),
1580                        ticks: $ticks,
1581                    }
1582                };
1583            }
1584
1585            #[allow(unused_macros)]
1586            macro_rules! expect_no_traffic {
1587                ($ports:expr, $ticks:expr,) => {
1588                    expect_no_traffic!($ports, $ticks)
1589                };
1590                ($ports:expr, $ticks:expr) => {
1591                    Step::ExpectNoTraffic {
1592                        location: step_location!(),
1593                        ports: $ports.to_vec(),
1594                        ticks: $ticks,
1595                    }
1596                };
1597            }
1598
1599            #[allow(unused_macros)]
1600            macro_rules! seq {
1601                ($steps:expr,) => {
1602                    seq!($steps)
1603                };
1604                ($steps:expr) => {
1605                    Step::Seq {
1606                        location: step_location!(),
1607                        steps: $steps.into_iter().collect(),
1608                    }
1609                };
1610            }
1611
1612            #[allow(unused_macros)]
1613            macro_rules! par {
1614                ($steps:expr,) => {
1615                    par!($steps)
1616                };
1617                ($steps:expr) => {
1618                    Step::Par {
1619                        location: step_location!(),
1620                        steps: $steps.into_iter().collect(),
1621                    }
1622                };
1623            }
1624        }
1625    };
1626}