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