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