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).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#[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}