Skip to main content

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