1use 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 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#[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}