1use rand_core::RngCore;
5
6use crate::{Error, Machine, TriggerAction, TriggerEvent, action, constants, counter, event};
7
8use self::action::Action;
9use self::constants::{STATE_END, STATE_LIMIT_MAX, STATE_SIGNAL};
10use self::counter::Operation;
11use self::event::Event;
12use crate::time::Duration as _;
13
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
22pub struct MachineId(usize);
23
24impl MachineId {
25 pub fn from_raw(raw: usize) -> Self {
32 MachineId(raw)
33 }
34
35 pub fn into_raw(self) -> usize {
39 self.0
40 }
41}
42
43#[derive(Debug, Clone)]
44struct MachineRuntime<T: crate::time::Instant> {
45 current_state: usize,
46 state_limit: u64,
47 padding_sent: u64,
48 normal_sent: u64,
49 blocking_duration: T::Duration,
50 machine_start: T,
51 allowed_blocked_microsec: T::Duration,
52 counter_a: u64,
53 counter_b: u64,
54}
55
56#[derive(PartialEq)]
57enum StateChange {
58 Changed,
59 Unchanged,
60}
61
62#[derive(Clone, Debug)]
66enum SignalTarget {
67 All,
68 AllExcept(usize),
69}
70
71#[derive(Clone, Debug)]
79pub struct Framework<M, R, T = std::time::Instant>
80where
81 T: crate::time::Instant,
82{
83 pub(crate) current_time: T,
85 rng: R,
87 pub(crate) actions: Vec<Option<TriggerAction<T>>>,
90 machines: M,
93 runtime: Vec<MachineRuntime<T>>,
94 max_padding_frac: f64,
96 normal_sent_packets: u64,
97 padding_sent_packets: u64,
98 max_blocking_frac: f64,
100 blocking_duration: T::Duration,
101 blocking_started: T,
102 blocking_active: bool,
103 signal_pending: Option<SignalTarget>,
105 counter_zeroed_once: (bool, bool),
107 framework_start: T,
108}
109
110impl<M, R, T> Framework<M, R, T>
111where
112 M: AsRef<[Machine]>,
113 R: RngCore,
114 T: crate::time::Instant,
115{
116 pub fn new(
130 machines: M,
131 max_padding_frac: f64,
132 max_blocking_frac: f64,
133 current_time: T,
134 rng: R,
135 ) -> Result<Self, Error> {
136 if !(0.0..=1.0).contains(&max_padding_frac) {
137 Err(Error::PaddingLimit)?;
138 }
139 if !(0.0..=1.0).contains(&max_blocking_frac) {
140 Err(Error::BlockingLimit)?;
141 }
142
143 let mut runtime = Vec::with_capacity(machines.as_ref().len());
144 for m in machines.as_ref() {
145 m.validate()?;
146 runtime.push(MachineRuntime {
147 current_state: 0,
148 state_limit: 0,
149 padding_sent: 0,
150 normal_sent: 0,
151 blocking_duration: T::Duration::zero(),
152 machine_start: current_time,
153 allowed_blocked_microsec: T::Duration::from_micros(m.allowed_blocked_microsec),
154 counter_a: 0,
155 counter_b: 0,
156 });
157 }
158
159 let actions = vec![None; machines.as_ref().len()];
160
161 let mut s = Self {
163 actions,
164 machines,
165 runtime,
166 current_time,
167 rng,
168 max_blocking_frac,
169 max_padding_frac,
170 framework_start: current_time,
171 blocking_active: false,
172 blocking_started: current_time,
173 blocking_duration: T::Duration::zero(),
174 padding_sent_packets: 0,
175 normal_sent_packets: 0,
176 signal_pending: None,
177 counter_zeroed_once: (false, false),
178 };
179
180 for (runtime, machine) in s.runtime.iter_mut().zip(s.machines.as_ref().iter()) {
181 if let Some(action) = machine.states[0].action {
182 runtime.state_limit = action.sample_limit(&mut s.rng);
183 }
184 }
185
186 Ok(s)
187 }
188
189 pub fn num_machines(&self) -> usize {
191 self.machines.as_ref().len()
192 }
193
194 pub fn all_machines_ended(&self) -> bool {
198 self.runtime.iter().all(|r| r.current_state == STATE_END)
201 }
202
203 pub fn trigger_events<'a>(
222 &'a mut self,
223 events: &[TriggerEvent],
224 current_time: T,
225 ) -> impl Iterator<Item = &'a TriggerAction<T>> + use<'a, M, R, T> {
226 self.actions.fill(None);
228
229 self.counter_zeroed_once = (false, false);
231
232 self.current_time = current_time;
237 for e in events {
238 self.process_event(e);
239 }
240
241 if let Some(signal) = self.signal_pending.take() {
246 let excluded = match signal {
248 SignalTarget::All => None,
249 SignalTarget::AllExcept(excluded) => Some(excluded),
250 };
251
252 for mi in 0..self.runtime.len() {
254 if let Some(excluded) = excluded {
255 if excluded == mi {
256 continue;
257 }
258 }
259 self.transition(mi, Event::Signal);
260 }
261
262 if self.signal_pending.take().is_some() {
267 if let Some(excluded) = excluded {
268 self.transition(excluded, Event::Signal);
269 }
270 }
271 }
272
273 self.actions.iter().filter_map(|action| action.as_ref())
275 }
276
277 fn process_event(&mut self, e: &TriggerEvent) {
278 match e {
279 TriggerEvent::NormalRecv => {
280 for mi in 0..self.runtime.len() {
282 self.transition(mi, Event::NormalRecv);
283 }
284 }
285 TriggerEvent::PaddingRecv => {
286 for mi in 0..self.runtime.len() {
288 self.transition(mi, Event::PaddingRecv);
289 }
290 }
291 TriggerEvent::TunnelRecv => {
292 for mi in 0..self.runtime.len() {
294 self.transition(mi, Event::TunnelRecv);
295 }
296 }
297 TriggerEvent::NormalSent => {
298 self.normal_sent_packets = self.normal_sent_packets.saturating_add(1);
299
300 for mi in 0..self.runtime.len() {
301 self.runtime[mi].normal_sent = self.runtime[mi].normal_sent.saturating_add(1);
302
303 self.transition(mi, Event::NormalSent);
304 }
305 }
306 TriggerEvent::PaddingSent { machine } => {
307 self.padding_sent_packets = self.padding_sent_packets.saturating_add(1);
308
309 let mi = machine.into_raw();
310 if mi >= self.runtime.len() {
311 return;
312 }
313 self.runtime[mi].padding_sent = self.runtime[mi].padding_sent.saturating_add(1);
314 if self.transition(mi, Event::PaddingSent) == StateChange::Unchanged
315 && self.runtime[mi].current_state != STATE_END
316 {
317 self.decrement_limit(mi);
319 }
320 }
321 TriggerEvent::TunnelSent => {
322 for mi in 0..self.runtime.len() {
324 self.transition(mi, Event::TunnelSent);
325 }
326 }
327 TriggerEvent::BlockingBegin { machine } => {
328 if !self.blocking_active {
330 self.blocking_active = true;
331 self.blocking_started = self.current_time;
332 }
333
334 for mi in 0..self.runtime.len() {
336 if self.transition(mi, Event::BlockingBegin) == StateChange::Unchanged
337 && self.runtime[mi].current_state != STATE_END
338 && mi == machine.into_raw()
339 {
340 self.decrement_limit(mi);
343 }
344 }
345 }
346 TriggerEvent::BlockingEnd => {
347 let mut blocked = T::Duration::zero();
348 if self.blocking_active {
349 blocked = self
350 .current_time
351 .saturating_duration_since(self.blocking_started);
352 self.blocking_duration += blocked; self.blocking_active = false;
354 }
355
356 for mi in 0..self.runtime.len() {
357 if !blocked.is_zero() {
360 self.runtime[mi].blocking_duration += blocked; }
362 self.transition(mi, Event::BlockingEnd);
363 }
364 }
365 TriggerEvent::TimerBegin { machine } => {
366 let mi = machine.into_raw();
367 if mi >= self.runtime.len() {
368 return;
369 }
370 if self.transition(mi, Event::TimerBegin) == StateChange::Unchanged
371 && self.runtime[mi].current_state != STATE_END
372 {
373 self.decrement_limit(machine.into_raw());
375 }
376 }
377 TriggerEvent::TimerEnd { machine } => {
378 let mi = machine.into_raw();
379 if mi >= self.runtime.len() {
380 return;
381 }
382 self.transition(mi, Event::TimerEnd);
383 }
384 }
385 }
386
387 fn transition(&mut self, mi: usize, event: Event) -> StateChange {
388 if self.runtime[mi].current_state == STATE_END {
390 return StateChange::Unchanged;
391 }
392
393 let next_state = {
396 let machine = &self.machines.as_ref()[mi];
397 let state = &machine.states[self.runtime[mi].current_state];
398 state.sample_state(event, &mut self.rng)
399 };
400
401 let Some(next_state) = next_state else {
403 return StateChange::Unchanged;
404 };
405
406 match next_state {
408 STATE_END => {
409 self.runtime[mi].current_state = STATE_END;
413 StateChange::Changed
414 }
415 STATE_SIGNAL => {
416 self.signal_pending = match self.signal_pending {
418 None => Some(SignalTarget::AllExcept(mi)),
420 _ => Some(SignalTarget::All),
423 };
424 StateChange::Unchanged
425 }
426 _ => {
427 let curr_state = self.runtime[mi].current_state;
428
429 if curr_state != next_state {
431 self.runtime[mi].current_state = next_state;
432 self.runtime[mi].state_limit = if let Some(action) =
433 self.machines.as_ref()[mi].states[next_state].action
434 {
435 action.sample_limit(&mut self.rng)
436 } else {
437 STATE_LIMIT_MAX
438 };
439 }
440
441 let below_limits =
449 self.below_action_limits(&self.runtime[mi], &self.machines.as_ref()[mi]);
450 let (allow_schedule, state_changed) = self.update_counter(mi);
451
452 if allow_schedule && below_limits {
454 self.schedule_action(mi, next_state);
455 }
456
457 if curr_state == self.runtime[mi].current_state && !state_changed {
458 StateChange::Unchanged
459 } else {
460 StateChange::Changed
461 }
462 }
463 }
464 }
465
466 fn update_counter(&mut self, mi: usize) -> (bool, bool) {
467 let state = &self.machines.as_ref()[mi].states[self.runtime[mi].current_state];
468
469 let old_value_a = self.runtime[mi].counter_a;
470 let old_value_b = self.runtime[mi].counter_b;
471 let mut any_counter_zeroed = false;
472
473 if let Some(counter_a) = state.counter.0 {
475 let change = if counter_a.copy {
476 old_value_b
477 } else {
478 counter_a.sample_value(&mut self.rng)
479 };
480
481 let updated_value_a = &mut self.runtime[mi].counter_a;
482 match counter_a.operation {
483 Operation::Increment => {
484 *updated_value_a = updated_value_a.saturating_add(change);
485 }
486 Operation::Decrement => {
487 *updated_value_a = updated_value_a.saturating_sub(change);
488 }
489 Operation::Set => {
490 *updated_value_a = change;
491 }
492 }
493
494 if old_value_a != 0 && *updated_value_a == 0 && !self.counter_zeroed_once.0 {
495 any_counter_zeroed = true;
496 self.counter_zeroed_once.0 = true;
497 }
498 }
499
500 if let Some(counter_b) = state.counter.1 {
501 let change = if counter_b.copy {
502 old_value_a
503 } else {
504 counter_b.sample_value(&mut self.rng)
505 };
506
507 let updated_value_b = &mut self.runtime[mi].counter_b;
508 match counter_b.operation {
509 Operation::Increment => {
510 *updated_value_b = updated_value_b.saturating_add(change);
511 }
512 Operation::Decrement => {
513 *updated_value_b = updated_value_b.saturating_sub(change);
514 }
515 Operation::Set => {
516 *updated_value_b = change;
517 }
518 }
519
520 if old_value_b != 0 && *updated_value_b == 0 && !self.counter_zeroed_once.1 {
521 any_counter_zeroed = true;
522 self.counter_zeroed_once.1 = true;
523 }
524 }
525
526 if any_counter_zeroed {
527 let state_changed = self.transition(mi, Event::CounterZero);
528 return (
529 self.actions[mi].is_none(),
530 state_changed == StateChange::Changed,
531 );
532 }
533
534 (true, false)
536 }
537
538 fn schedule_action(&mut self, mi: usize, state: usize) {
539 let index = MachineId(mi);
540 let action = self.machines.as_ref()[mi].states[state].action;
541
542 self.actions[mi] = match action {
543 Some(action) => match action {
544 Action::Cancel { timer } => Some(TriggerAction::Cancel {
545 machine: index,
546 timer,
547 }),
548 Action::SendPadding {
549 bypass, replace, ..
550 } => Some(TriggerAction::SendPadding {
551 timeout: T::Duration::from_micros(action.sample_timeout(&mut self.rng)),
552 bypass,
553 replace,
554 machine: index,
555 }),
556 Action::BlockOutgoing {
557 bypass, replace, ..
558 } => Some(TriggerAction::BlockOutgoing {
559 timeout: T::Duration::from_micros(action.sample_timeout(&mut self.rng)),
560 duration: T::Duration::from_micros(action.sample_duration(&mut self.rng)),
561 bypass,
562 replace,
563 machine: index,
564 }),
565 Action::UpdateTimer { replace, .. } => Some(TriggerAction::UpdateTimer {
566 duration: T::Duration::from_micros(action.sample_duration(&mut self.rng)),
567 replace,
568 machine: index,
569 }),
570 },
571 None => None,
572 };
573 }
574
575 fn decrement_limit(&mut self, mi: usize) {
576 if self.runtime[mi].state_limit > 0 {
577 self.runtime[mi].state_limit -= 1;
578 }
579 let cs = self.runtime[mi].current_state;
580
581 if let Some(action) = self.machines.as_ref()[mi].states[cs].action {
582 if self.runtime[mi].state_limit == 0 && action.has_limit() {
583 self.actions[mi] = None;
585 self.transition(mi, Event::LimitReached);
587 }
588 }
589 }
590
591 fn below_action_limits(&self, runtime: &MachineRuntime<T>, machine: &Machine) -> bool {
592 let current = &machine.states[runtime.current_state];
593
594 let Some(action) = current.action else {
595 return false;
596 };
597
598 match action {
599 Action::BlockOutgoing { .. } => self.below_limit_blocking(runtime, machine),
600 Action::SendPadding { .. } => self.below_limit_padding(runtime, machine),
601 Action::UpdateTimer { .. } => runtime.state_limit > 0,
602 _ => true,
603 }
604 }
605
606 fn below_limit_blocking(&self, runtime: &MachineRuntime<T>, machine: &Machine) -> bool {
607 let current = &machine.states[runtime.current_state];
608 let replace = if let Some(Action::BlockOutgoing { replace, .. }) = current.action {
612 replace
613 } else {
614 false
615 };
616
617 if replace && self.blocking_active {
618 return runtime.state_limit > 0;
620 }
621
622 let mut m_block_dur = runtime.blocking_duration;
624 let mut g_block_dur = self.blocking_duration;
625 if self.blocking_active {
626 m_block_dur += self
628 .current_time
629 .saturating_duration_since(self.blocking_started);
630 g_block_dur += self
631 .current_time
632 .saturating_duration_since(self.blocking_started);
633 }
634
635 if m_block_dur < runtime.allowed_blocked_microsec {
638 return runtime.state_limit > 0;
640 }
641
642 if machine.max_blocking_frac > 0.0 {
644 let f: f64 = m_block_dur.div_duration_f64(
645 self.current_time
646 .saturating_duration_since(runtime.machine_start),
647 );
648 if f >= machine.max_blocking_frac {
649 return false;
650 }
651 }
652
653 if self.max_blocking_frac > 0.0 {
655 let f: f64 = g_block_dur.div_duration_f64(
656 self.current_time
657 .saturating_duration_since(self.framework_start),
658 );
659 if f >= self.max_blocking_frac {
660 return false;
661 }
662 }
663
664 runtime.state_limit > 0
666 }
667
668 fn below_limit_padding(&self, runtime: &MachineRuntime<T>, machine: &Machine) -> bool {
669 if runtime.padding_sent < machine.allowed_padding_packets {
671 return runtime.state_limit > 0;
672 }
673
674 if machine.max_padding_frac > 0.0 {
676 let total = runtime.normal_sent + runtime.padding_sent;
677 if total == 0 {
678 return true;
679 }
680 if runtime.padding_sent as f64 / total as f64 >= machine.max_padding_frac {
681 return false;
682 }
683 }
684
685 if self.max_padding_frac > 0.0 {
687 let total = self.padding_sent_packets + self.normal_sent_packets;
688 if total == 0 {
689 return true;
690 }
691 if self.padding_sent_packets as f64 / total as f64 >= self.max_padding_frac {
692 return false;
693 }
694 }
695
696 runtime.state_limit > 0
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use crate::counter::Counter;
704 use crate::dist::*;
705 use crate::framework::*;
706 use crate::state::*;
707 use enum_map::enum_map;
708 use std::ops::Add;
709 use std::time::Duration;
710 use std::time::Instant;
711
712 #[test]
713 fn no_machines() {
714 let machines = vec![];
715 let f = Framework::new(&machines, 0.0, 0.0, Instant::now(), rand::rng());
716 assert!(f.is_ok());
717 }
718
719 #[test]
720 fn reuse_machines() {
721 let machines = vec![];
722 let f1 = Framework::new(&machines, 0.0, 0.0, Instant::now(), rand::rng());
723 assert!(f1.is_ok());
724 let f2 = Framework::new(&machines, 0.0, 0.0, Instant::now(), rand::rng());
725 assert!(f2.is_ok());
726 }
727
728 #[test]
729 fn noop_machine() {
730 let s0 = State::new(enum_map! {
731 _ => vec![],
732 });
733 let m = Machine::new(0, 0.0, 0, 0.0, vec![s0]).unwrap();
734 assert_eq!(m.serialize(), "02eNpjYEAHjOgCAAA0AAI=");
735 }
736
737 #[test]
738 fn trigger_events_actions() {
739 let mut s0 = State::new(enum_map! {
744 Event::PaddingSent => vec![Trans(1, 1.0)],
745 _ => vec![],
746 });
747 s0.action = Some(Action::SendPadding {
748 bypass: false,
749 replace: false,
750 timeout: Dist {
751 dist: DistType::Uniform {
752 low: 10.0,
753 high: 10.0,
754 },
755 start: 0.0,
756 max: 0.0,
757 },
758 limit: None,
759 });
760
761 let mut s1 = State::new(enum_map! {
763 Event::PaddingRecv => vec![Trans(0, 1.0)],
764 _ => vec![],
765 });
766 s1.action = Some(Action::SendPadding {
767 bypass: false,
768 replace: false,
769 timeout: Dist {
770 dist: DistType::Uniform {
771 low: 1.0,
772 high: 1.0,
773 },
774
775 start: 0.0,
776 max: 0.0,
777 },
778 limit: None,
779 });
780
781 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1]).unwrap();
783
784 let mut current_time = Instant::now();
785 let machines = vec![m];
786 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
787
788 assert_eq!(f.actions.len(), 1);
789
790 _ = f.trigger_events(
792 &[TriggerEvent::BlockingBegin {
793 machine: MachineId(0),
794 }],
795 current_time,
796 );
797 assert_eq!(f.actions[0], None);
798
799 current_time = current_time.add(Duration::from_micros(20));
801 _ = f.trigger_events(
802 &[TriggerEvent::BlockingBegin {
803 machine: MachineId(0),
804 }],
805 current_time,
806 );
807 assert_eq!(f.actions[0], None);
808
809 _ = f.trigger_events(
811 &[TriggerEvent::PaddingSent {
812 machine: MachineId(0),
813 }],
814 current_time,
815 );
816 assert_eq!(
817 f.actions[0],
818 Some(TriggerAction::SendPadding {
819 timeout: Duration::from_micros(1),
820 bypass: false,
821 replace: false,
822 machine: MachineId(0),
823 })
824 );
825
826 current_time = current_time.add(Duration::from_micros(20));
828 _ = f.trigger_events(
829 &[TriggerEvent::PaddingSent {
830 machine: MachineId(0),
831 }],
832 current_time,
833 );
834 assert_eq!(f.actions[0], None);
835
836 _ = f.trigger_events(&[TriggerEvent::PaddingRecv], current_time);
838 assert_eq!(
839 f.actions[0],
840 Some(TriggerAction::SendPadding {
841 timeout: Duration::from_micros(10),
842 bypass: false,
843 replace: false,
844 machine: MachineId(0),
845 })
846 );
847
848 for _ in 0..10 {
850 _ = f.trigger_events(
851 &[
852 TriggerEvent::PaddingSent {
853 machine: MachineId(0),
854 },
855 TriggerEvent::PaddingRecv,
856 ],
857 current_time,
858 );
859 assert_eq!(
860 f.actions[0],
861 Some(TriggerAction::SendPadding {
862 timeout: Duration::from_micros(10),
863 bypass: false,
864 replace: false,
865 machine: MachineId(0),
866 })
867 );
868 }
869
870 for i in 0..10 {
872 if i % 2 == 0 {
873 _ = f.trigger_events(
874 &[
875 TriggerEvent::PaddingRecv,
876 TriggerEvent::PaddingSent {
877 machine: MachineId(0),
878 },
879 TriggerEvent::PaddingRecv,
880 ],
881 current_time,
882 );
883 assert_eq!(
884 f.actions[0],
885 Some(TriggerAction::SendPadding {
886 timeout: Duration::from_micros(10),
887 bypass: false,
888 replace: false,
889 machine: MachineId(0),
890 })
891 );
892 } else {
893 _ = f.trigger_events(
894 &[
895 TriggerEvent::PaddingSent {
896 machine: MachineId(0),
897 },
898 TriggerEvent::PaddingRecv,
899 TriggerEvent::PaddingSent {
900 machine: MachineId(0),
901 },
902 ],
903 current_time,
904 );
905 assert_eq!(
906 f.actions[0],
907 Some(TriggerAction::SendPadding {
908 timeout: Duration::from_micros(1),
909 bypass: false,
910 replace: false,
911 machine: MachineId(0),
912 })
913 );
914 }
915 }
916 }
917
918 #[test]
919 fn blocking_machine() {
920 let mut s0 = State::new(enum_map! {
924 Event::NormalSent => vec![Trans(0, 1.0)],
925 _ => vec![],
926 });
927 s0.action = Some(Action::BlockOutgoing {
928 bypass: false,
929 replace: false,
930 timeout: Dist {
931 dist: DistType::Uniform {
932 low: 1.0,
933 high: 1.0,
934 },
935 start: 0.0,
936 max: 0.0,
937 },
938 duration: Dist {
939 dist: DistType::Uniform {
940 low: 10.0,
941 high: 10.0,
942 },
943 start: 0.0,
944 max: 0.0,
945 },
946 limit: None,
947 });
948
949 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0]).unwrap();
951
952 let mut current_time = Instant::now();
953 let machines = vec![m];
954 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
955
956 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
957 assert_eq!(
958 f.actions[0],
959 Some(TriggerAction::BlockOutgoing {
960 timeout: Duration::from_micros(1),
961 duration: Duration::from_micros(10),
962 bypass: false,
963 replace: false,
964 machine: MachineId(0),
965 })
966 );
967
968 current_time = current_time.add(Duration::from_micros(20));
969 _ = f.trigger_events(
970 &[TriggerEvent::BlockingBegin {
971 machine: MachineId(0),
972 }],
973 current_time,
974 );
975 assert_eq!(f.actions[0], None);
976
977 for _ in 0..10 {
978 current_time = current_time.add(Duration::from_micros(1));
979 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
980 assert_eq!(
981 f.actions[0],
982 Some(TriggerAction::BlockOutgoing {
983 timeout: Duration::from_micros(1),
984 duration: Duration::from_micros(10),
985 bypass: false,
986 replace: false,
987 machine: MachineId(0),
988 })
989 );
990 }
991 }
992
993 #[test]
994 fn timer_machine() {
995 let mut s0 = State::new(enum_map! {
999 Event::PaddingSent => vec![Trans(1, 1.0)],
1000 _ => vec![],
1001 });
1002 s0.action = Some(Action::SendPadding {
1003 bypass: false,
1004 replace: false,
1005 timeout: Dist {
1006 dist: DistType::Uniform {
1007 low: 1.0,
1008 high: 1.0,
1009 },
1010
1011 start: 0.0,
1012 max: 0.0,
1013 },
1014 limit: None,
1015 });
1016
1017 let mut s1 = State::new(enum_map! {
1019 Event::TimerEnd => vec![Trans(0, 1.0)],
1020 _ => vec![],
1021 });
1022 s1.action = Some(Action::UpdateTimer {
1023 replace: false,
1024 duration: Dist {
1025 dist: DistType::Uniform {
1026 low: 1000.0,
1027 high: 1000.0,
1028 },
1029 start: 0.0,
1030 max: 0.0,
1031 },
1032 limit: None,
1033 });
1034
1035 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1]).unwrap();
1037
1038 let mut current_time = Instant::now();
1039 let machines = vec![m];
1040 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1041
1042 _ = f.trigger_events(
1043 &[TriggerEvent::PaddingSent {
1044 machine: MachineId(0),
1045 }],
1046 current_time,
1047 );
1048 assert_eq!(
1049 f.actions[0],
1050 Some(TriggerAction::UpdateTimer {
1051 duration: Duration::from_micros(1000),
1052 replace: false,
1053 machine: MachineId(0),
1054 })
1055 );
1056
1057 current_time = current_time.add(Duration::from_micros(20));
1058 _ = f.trigger_events(
1059 &[TriggerEvent::TimerEnd {
1060 machine: MachineId(0),
1061 }],
1062 current_time,
1063 );
1064 assert_eq!(
1065 f.actions[0],
1066 Some(TriggerAction::SendPadding {
1067 timeout: Duration::from_micros(1),
1068 bypass: false,
1069 replace: false,
1070 machine: MachineId(0),
1071 })
1072 );
1073 }
1074
1075 #[test]
1076 fn counter_machine() {
1077 let mut s0 = State::new(enum_map! {
1082 Event::PaddingSent => vec![Trans(1, 1.0)],
1083 Event::CounterZero => vec![Trans(2, 1.0)],
1084 _ => vec![],
1085 });
1086 s0.counter = (Some(Counter::new(Operation::Decrement)), None);
1087
1088 let mut s1 = State::new(enum_map! {
1090 Event::NormalSent => vec![Trans(0, 1.0)],
1091 _ => vec![],
1092 });
1093 s1.counter = (Some(Counter::new(Operation::Increment)), None);
1094
1095 let mut s2 = State::new(enum_map! {
1097 Event::NormalSent => vec![Trans(0, 1.0)],
1098 Event::PaddingSent => vec![Trans(1, 1.0)],
1099 _ => vec![],
1100 });
1101 s2.action = Some(Action::SendPadding {
1102 bypass: false,
1103 replace: false,
1104 timeout: Dist {
1105 dist: DistType::Uniform {
1106 low: 2.0,
1107 high: 2.0,
1108 },
1109 start: 0.0,
1110 max: 0.0,
1111 },
1112 limit: None,
1113 });
1114 s2.counter = (
1115 None,
1116 Some(Counter::new_dist(
1117 Operation::Increment,
1118 Dist {
1119 dist: DistType::Uniform {
1120 low: 4.0,
1121 high: 4.0,
1122 },
1123 start: 0.0,
1124 max: 0.0,
1125 },
1126 )),
1127 );
1128
1129 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1131
1132 let mut current_time = Instant::now();
1133 let machines = vec![m];
1134 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1135
1136 _ = f.trigger_events(
1137 &[TriggerEvent::PaddingSent {
1138 machine: MachineId(0),
1139 }],
1140 current_time,
1141 );
1142 assert_eq!(f.actions[0], None);
1143 assert_eq!(f.runtime[0].counter_a, 1);
1144
1145 current_time = current_time.add(Duration::from_micros(20));
1146 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1147 assert_eq!(
1148 f.actions[0],
1149 Some(TriggerAction::SendPadding {
1150 timeout: Duration::from_micros(2),
1151 bypass: false,
1152 replace: false,
1153 machine: MachineId(0),
1154 })
1155 );
1156 assert_eq!(f.runtime[0].counter_a, 0);
1157 assert_eq!(f.runtime[0].counter_b, 4);
1158 }
1159
1160 #[test]
1161 fn counter_underflow_machine() {
1162 let mut s0 = State::new(enum_map! {
1167 Event::NormalSent => vec![Trans(0, 1.0)],
1168 Event::NormalRecv => vec![Trans(1, 1.0)],
1169 Event::CounterZero => vec![Trans(2, 1.0)],
1170 _ => vec![],
1171 });
1172 s0.counter = (
1173 None,
1174 Some(Counter::new_dist(
1176 Operation::Decrement,
1177 Dist {
1178 dist: DistType::Uniform {
1179 low: 10.0,
1180 high: 10.0,
1181 },
1182 start: 0.0,
1183 max: 0.0,
1184 },
1185 )),
1186 );
1187
1188 let mut s1 = State::new(enum_map! {
1190 Event::NormalSent => vec![Trans(0, 1.0)],
1191 Event::NormalRecv => vec![Trans(1, 1.0)],
1192 Event::CounterZero => vec![Trans(2, 1.0)],
1193 _ => vec![],
1194 });
1195 s1.counter = (
1196 None,
1197 Some(Counter::new_dist(
1198 Operation::Set,
1199 Dist {
1200 dist: DistType::Uniform {
1201 low: 0.0, high: 0.0,
1203 },
1204 start: 0.0,
1205 max: 0.0,
1206 },
1207 )),
1208 );
1209
1210 let mut s2 = State::new(enum_map! {
1212 Event::NormalSent => vec![Trans(0, 1.0)],
1213 Event::NormalRecv => vec![Trans(1, 1.0)],
1214 _ => vec![],
1215 });
1216 s2.action = Some(Action::SendPadding {
1217 bypass: false,
1218 replace: false,
1219 timeout: Dist {
1220 dist: DistType::Uniform {
1221 low: 2.0,
1222 high: 2.0,
1223 },
1224 start: 0.0,
1225 max: 0.0,
1226 },
1227 limit: None,
1228 });
1229
1230 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1232
1233 let current_time = Instant::now();
1234 let machines = vec![m];
1235 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1236
1237 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1239 assert_eq!(f.actions[0], None);
1240 assert_eq!(f.runtime[0].counter_b, 0);
1241
1242 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
1244 assert_eq!(f.actions[0], None);
1245 assert_eq!(f.runtime[0].counter_b, 0);
1246 }
1247
1248 #[test]
1249 fn counter_overflow_machine() {
1250 let mut s0 = State::new(enum_map! {
1255 Event::NormalSent => vec![Trans(0, 1.0)],
1256 Event::NormalRecv => vec![Trans(1, 1.0)],
1257 _ => vec![],
1258 });
1259 s0.counter = (
1260 Some(Counter::new_dist(
1262 Operation::Increment,
1263 Dist {
1264 dist: DistType::Uniform {
1265 low: 1000.0,
1266 high: 1000.0,
1267 },
1268 start: 0.0,
1269 max: 0.0,
1270 },
1271 )),
1272 None,
1273 );
1274
1275 let mut s1 = State::new(enum_map! {
1277 Event::NormalSent => vec![Trans(0, 1.0)],
1278 Event::NormalRecv => vec![Trans(1, 1.0)],
1279 _ => vec![],
1280 });
1281 s1.counter = (
1282 Some(Counter::new_dist(
1283 Operation::Set,
1284 Dist {
1285 dist: DistType::Uniform {
1286 low: u64::MAX as f64, high: u64::MAX as f64,
1288 },
1289 start: 0.0,
1290 max: 0.0,
1291 },
1292 )),
1293 None,
1294 );
1295
1296 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1]).unwrap();
1298
1299 let current_time = Instant::now();
1300 let machines = vec![m];
1301 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1302
1303 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
1305 assert_eq!(f.runtime[0].counter_a, u64::MAX);
1306
1307 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1309 assert_eq!(f.runtime[0].counter_a, u64::MAX);
1310 }
1311
1312 #[test]
1313 fn counter_convergence_machine() {
1314 let mut s0 = State::new(enum_map! {
1319 Event::NormalSent => vec![Trans(0, 1.0)],
1320 Event::PaddingSent => vec![Trans(1, 1.0)],
1321 _ => vec![],
1322 });
1323 s0.counter = (
1324 Some(Counter::new_dist(
1325 Operation::Set,
1326 Dist {
1327 dist: DistType::Uniform {
1328 low: 44.0,
1329 high: 44.0,
1330 },
1331 start: 0.0,
1332 max: 0.0,
1333 },
1334 )),
1335 Some(Counter::new_dist(
1336 Operation::Set,
1337 Dist {
1338 dist: DistType::Uniform {
1339 low: 28.0,
1340 high: 28.0,
1341 },
1342 start: 0.0,
1343 max: 0.0,
1344 },
1345 )),
1346 );
1347
1348 let mut s1 = State::new(enum_map! {
1350 Event::NormalSent => vec![Trans(1, 1.0)],
1351 Event::CounterZero => vec![Trans(2, 1.0)],
1352 _ => vec![],
1353 });
1354 s1.counter = (
1355 Some(Counter::new_dist(
1356 Operation::Decrement,
1357 Dist {
1358 dist: DistType::Uniform {
1359 low: 32.0,
1360 high: 32.0,
1361 },
1362 start: 0.0,
1363 max: 0.0,
1364 },
1365 )),
1366 Some(Counter::new_dist(
1367 Operation::Decrement,
1368 Dist {
1369 dist: DistType::Uniform {
1370 low: 15.0,
1371 high: 15.0,
1372 },
1373 start: 0.0,
1374 max: 0.0,
1375 },
1376 )),
1377 );
1378
1379 let s2 = State::new(enum_map! {
1381 Event::CounterZero => vec![Trans(0, 1.0)],
1382 _ => vec![],
1383 });
1384
1385 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1387
1388 let mut current_time = Instant::now();
1389 let machines = vec![m];
1390 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1391
1392 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1393 assert_eq!(f.actions[0], None);
1394 assert_eq!(f.runtime[0].counter_a, 44);
1395 assert_eq!(f.runtime[0].counter_b, 28);
1396
1397 current_time = current_time.add(Duration::from_micros(20));
1398 _ = f.trigger_events(
1399 &[TriggerEvent::PaddingSent {
1400 machine: MachineId(0),
1401 }],
1402 current_time,
1403 );
1404 assert_eq!(f.actions[0], None);
1405 assert_eq!(f.runtime[0].counter_a, 12);
1406 assert_eq!(f.runtime[0].counter_b, 13);
1407
1408 current_time = current_time.add(Duration::from_micros(20));
1409 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1410 assert_eq!(f.actions[0], None);
1411 assert_eq!(f.runtime[0].counter_a, 0);
1412 assert_eq!(f.runtime[0].counter_b, 0);
1413 }
1414
1415 #[test]
1416 fn counter_divergence_machine() {
1417 let mut s0 = State::new(enum_map! {
1422 Event::NormalSent => vec![Trans(0, 1.0)],
1423 Event::PaddingSent => vec![Trans(1, 1.0)],
1424 _ => vec![],
1425 });
1426 s0.counter = (
1427 Some(Counter::new_dist(
1428 Operation::Set,
1429 Dist {
1430 dist: DistType::Uniform {
1431 low: 50.0,
1432 high: 50.0,
1433 },
1434 start: 0.0,
1435 max: 0.0,
1436 },
1437 )),
1438 Some(Counter::new_dist(
1439 Operation::Set,
1440 Dist {
1441 dist: DistType::Uniform {
1442 low: 50.0,
1443 high: 50.0,
1444 },
1445 start: 0.0,
1446 max: 0.0,
1447 },
1448 )),
1449 );
1450
1451 let mut s1 = State::new(enum_map! {
1453 Event::CounterZero => vec![Trans(2, 1.0)],
1454 _ => vec![],
1455 });
1456 s1.counter = (
1457 None,
1458 Some(Counter::new_dist(
1459 Operation::Decrement,
1460 Dist {
1461 dist: DistType::Uniform {
1462 low: 50.0,
1463 high: 50.0,
1464 },
1465 start: 0.0,
1466 max: 0.0,
1467 },
1468 )),
1469 );
1470
1471 let mut s2 = State::new(enum_map! {
1473 _ => vec![],
1474 });
1475 s2.counter = (
1476 Some(Counter::new_dist(
1477 Operation::Increment,
1478 Dist {
1479 dist: DistType::Uniform {
1480 low: 25.0,
1481 high: 25.0,
1482 },
1483 start: 0.0,
1484 max: 0.0,
1485 },
1486 )),
1487 None,
1488 );
1489
1490 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1492
1493 let mut current_time = Instant::now();
1494 let machines = vec![m];
1495 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1496
1497 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1498 assert_eq!(f.actions[0], None);
1499 assert_eq!(f.runtime[0].counter_a, 50);
1500 assert_eq!(f.runtime[0].counter_b, 50);
1501
1502 current_time = current_time.add(Duration::from_micros(20));
1503 _ = f.trigger_events(
1504 &[TriggerEvent::PaddingSent {
1505 machine: MachineId(0),
1506 }],
1507 current_time,
1508 );
1509 assert_eq!(f.actions[0], None);
1510 assert_eq!(f.runtime[0].counter_a, 75);
1511 assert_eq!(f.runtime[0].counter_b, 0);
1512 }
1513
1514 #[test]
1515 fn counter_chain_machine() {
1516 let mut s0 = State::new(enum_map! {
1522 Event::NormalSent => vec![Trans(0, 1.0)],
1523 Event::PaddingSent => vec![Trans(1, 1.0)],
1524 _ => vec![],
1525 });
1526 s0.counter = (
1527 Some(Counter::new(Operation::Set)), Some(Counter::new(Operation::Set)),
1529 );
1530
1531 let mut s1 = State::new(enum_map! {
1533 Event::CounterZero => vec![Trans(2, 1.0)], _ => vec![],
1535 });
1536 s1.action = Some(Action::SendPadding {
1537 bypass: false,
1538 replace: false,
1539 timeout: Dist {
1540 dist: DistType::Uniform {
1541 low: 2.0,
1542 high: 2.0,
1543 },
1544 start: 0.0,
1545 max: 0.0,
1546 },
1547 limit: None,
1548 });
1549 s1.counter = (
1550 Some(Counter::new(Operation::Decrement)),
1551 None, );
1553
1554 let mut s2 = State::new(enum_map! {
1556 Event::CounterZero => vec![Trans(3, 1.0)],
1557 _ => vec![],
1558 });
1559 s2.action = Some(Action::SendPadding {
1560 bypass: true,
1561 replace: false,
1562 timeout: Dist {
1563 dist: DistType::Uniform {
1564 low: 67.0,
1565 high: 67.0,
1566 },
1567 start: 0.0,
1568 max: 0.0,
1569 },
1570 limit: None,
1571 });
1572 s2.counter = (
1573 None,
1574 Some(Counter::new(Operation::Decrement)), );
1576
1577 let mut s3 = State::new(enum_map! {
1579 _ => vec![],
1580 });
1581 s3.counter = (
1582 Some(Counter::new_dist(
1583 Operation::Set,
1584 Dist {
1585 dist: DistType::Uniform {
1586 low: 13.0,
1587 high: 13.0,
1588 },
1589 start: 0.0,
1590 max: 0.0,
1591 },
1592 )),
1593 None,
1594 );
1595
1596 let m = Machine::new(0, 0.0, 0, 0.0, vec![s0, s1, s2, s3]).unwrap();
1598
1599 let mut current_time = Instant::now();
1600 let machines = vec![m];
1601 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1602
1603 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1604 assert_eq!(f.actions[0], None);
1605 assert_eq!(f.runtime[0].counter_a, 1);
1606 assert_eq!(f.runtime[0].counter_b, 1);
1607
1608 current_time = current_time.add(Duration::from_micros(20));
1609 _ = f.trigger_events(
1610 &[TriggerEvent::PaddingSent {
1611 machine: MachineId(0),
1612 }],
1613 current_time,
1614 );
1615 assert_eq!(
1616 f.actions[0],
1617 Some(TriggerAction::SendPadding {
1618 timeout: Duration::from_micros(67),
1619 bypass: true,
1620 replace: false,
1621 machine: MachineId(0),
1622 })
1623 );
1624 assert_eq!(f.runtime[0].counter_a, 13);
1625 assert_eq!(f.runtime[0].counter_b, 0);
1626 }
1627
1628 #[test]
1629 fn counter_copy_machine() {
1630 let mut s0 = State::new(enum_map! {
1634 Event::NormalSent => vec![Trans(0, 1.0)],
1635 Event::PaddingSent => vec![Trans(1, 1.0)],
1636 _ => vec![],
1637 });
1638 s0.counter = (
1639 Some(Counter::new_dist(
1640 Operation::Set,
1641 Dist {
1642 dist: DistType::Uniform {
1643 low: 4.0,
1644 high: 4.0,
1645 },
1646 start: 0.0,
1647 max: 0.0,
1648 },
1649 )),
1650 Some(Counter::new_dist(
1651 Operation::Set,
1652 Dist {
1653 dist: DistType::Uniform {
1654 low: 13.0,
1655 high: 13.0,
1656 },
1657 start: 0.0,
1658 max: 0.0,
1659 },
1660 )),
1661 );
1662
1663 let mut s1 = State::new(enum_map! {
1665 Event::PaddingSent => vec![Trans(2, 1.0)],
1666 _ => vec![],
1667 });
1668 s1.counter = (
1669 Some(Counter::new_copy(Operation::Set)),
1671 Some(Counter::new_copy(Operation::Decrement)),
1673 );
1674
1675 let mut s2 = State::new(enum_map! {
1677 _ => vec![],
1678 });
1679 s2.counter = (
1680 Some(Counter::new_copy(Operation::Increment)),
1682 None,
1684 );
1685
1686 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1688
1689 let mut current_time = Instant::now();
1690 let machines = vec![m];
1691 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1692
1693 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1694 assert_eq!(f.actions[0], None);
1695 assert_eq!(f.runtime[0].counter_a, 4);
1696 assert_eq!(f.runtime[0].counter_b, 13);
1697
1698 current_time = current_time.add(Duration::from_micros(20));
1699 _ = f.trigger_events(
1700 &[TriggerEvent::PaddingSent {
1701 machine: MachineId(0),
1702 }],
1703 current_time,
1704 );
1705 assert_eq!(f.actions[0], None);
1706 assert_eq!(f.runtime[0].counter_a, 13);
1707 assert_eq!(f.runtime[0].counter_b, 9);
1708
1709 current_time = current_time.add(Duration::from_micros(20));
1710 _ = f.trigger_events(
1711 &[TriggerEvent::PaddingSent {
1712 machine: MachineId(0),
1713 }],
1714 current_time,
1715 );
1716 assert_eq!(f.actions[0], None);
1717 assert_eq!(f.runtime[0].counter_a, 22);
1718 assert_eq!(f.runtime[0].counter_b, 9);
1719 }
1720
1721 #[test]
1722 fn counter_triggered_no_early_limit_decrement() {
1723 let mut s0 = State::new(enum_map! {
1728 Event::NormalSent => vec![Trans(0, 1.0)],
1729 Event::PaddingSent => vec![Trans(1, 1.0)],
1730 _ => vec![],
1731 });
1732 s0.counter = (
1733 Some(Counter::new_dist(
1734 Operation::Set,
1735 Dist {
1736 dist: DistType::Uniform {
1737 low: 2.0,
1738 high: 2.0,
1739 },
1740 start: 0.0,
1741 max: 0.0,
1742 },
1743 )),
1744 Some(Counter::new(Operation::Set)),
1745 );
1746
1747 let mut s1 = State::new(enum_map! {
1749 Event::CounterZero => vec![Trans(2, 1.0)],
1750 Event::PaddingSent => vec![Trans(1, 1.0)],
1751 _ => vec![],
1752 });
1753 s1.counter = (Some(Counter::new(Operation::Decrement)), None);
1754 s1.action = Some(Action::SendPadding {
1755 bypass: false,
1756 replace: false,
1757 timeout: Dist {
1758 dist: DistType::Uniform {
1759 low: 1.0,
1760 high: 1.0,
1761 },
1762 start: 0.0,
1763 max: 0.0,
1764 },
1765 limit: Some(Dist {
1766 dist: DistType::Uniform {
1767 low: 2.0,
1768 high: 2.0,
1769 },
1770 start: 0.0,
1771 max: 0.0,
1772 }),
1773 });
1774
1775 let mut s2 = State::new(enum_map! {
1777 Event::CounterZero => vec![Trans(1, 1.0)],
1778 _ => vec![],
1779 });
1780 s2.counter = (None, Some(Counter::new(Operation::Decrement)));
1781
1782 let m = Machine::new(1000, 1.0, 0, 0.0, vec![s0, s1, s2]).unwrap();
1783
1784 let current_time = Instant::now();
1785 let machines = vec![m];
1786 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1787
1788 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1789 assert_eq!(f.actions[0], None);
1790 assert_eq!(f.runtime[0].counter_a, 2);
1791 assert_eq!(f.runtime[0].counter_b, 1);
1792
1793 _ = f.trigger_events(
1794 &[TriggerEvent::PaddingSent {
1795 machine: MachineId(0),
1796 }],
1797 current_time,
1798 );
1799 assert!(f.actions[0].is_some());
1800 assert_eq!(f.runtime[0].counter_a, 1);
1801 assert_eq!(f.runtime[0].counter_b, 1);
1802 assert_eq!(f.runtime[0].state_limit, 2);
1803
1804 _ = f.trigger_events(
1805 &[TriggerEvent::PaddingSent {
1806 machine: MachineId(0),
1807 }],
1808 current_time,
1809 );
1810 assert!(f.actions[0].is_some());
1811 assert_eq!(f.runtime[0].counter_a, 0);
1812 assert_eq!(f.runtime[0].counter_b, 0);
1813 assert_eq!(f.runtime[0].state_limit, 2);
1816 }
1817
1818 #[test]
1819 fn test_infinite_loop_counter() {
1820 let s0 = State::new(enum_map! {
1822 Event::NormalSent => vec![Trans(1, 1.0)],
1823 _ => vec![],
1824 });
1825
1826 let mut init = State::new(enum_map! {
1828 Event::NormalSent => vec![Trans(2, 1.0)],
1829 _ => vec![],
1830 });
1831 init.counter = (Some(Counter::new(Operation::Set)), None);
1832
1833 let mut state_a = State::new(enum_map! {
1835 Event::CounterZero => vec![Trans(3, 1.0)],
1837 Event::NormalRecv => vec![Trans(4, 1.0)],
1839 _ => vec![],
1840 });
1841 state_a.counter = (
1842 Some(Counter::new(Operation::Decrement)),
1843 Some(Counter::new(Operation::Set)),
1844 );
1845
1846 let mut state_b = State::new(enum_map! {
1848 Event::CounterZero => vec![Trans(2, 1.0)],
1850 _ => vec![],
1851 });
1852 state_b.counter = (
1853 Some(Counter::new(Operation::Set)),
1854 Some(Counter::new(Operation::Decrement)),
1855 );
1856
1857 let mut state_pad = State::new(enum_map! {
1858 _ => vec![],
1859 });
1860 state_pad.action = Some(Action::SendPadding {
1861 bypass: false,
1862 replace: false,
1863 timeout: Dist {
1864 dist: DistType::Uniform {
1865 low: 1.0,
1866 high: 1.0,
1867 },
1868 start: 0.0,
1869 max: 0.0,
1870 },
1871 limit: None,
1872 });
1873
1874 let m = Machine::new(
1875 1000,
1876 1.0,
1877 0,
1878 0.0,
1879 vec![s0, init, state_a, state_b, state_pad],
1880 )
1881 .unwrap();
1882 let machines = vec![m];
1883 let current_time = Instant::now();
1884 let mut f = Framework::new(machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1885 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1887 assert_eq!(
1892 f.trigger_events(
1893 &[TriggerEvent::NormalSent, TriggerEvent::NormalRecv],
1894 current_time
1895 )
1896 .count(),
1897 1
1898 );
1899 }
1900
1901 #[test]
1902 fn signal_one_machine() {
1903 let s0_m0 = State::new(enum_map! {
1908 Event::NormalSent => vec![Trans(STATE_SIGNAL, 1.0)],
1909 Event::Signal => vec![Trans(1, 1.0)],
1910 _ => vec![],
1911 });
1912 let s0_m1 = State::new(enum_map! {
1913 Event::Signal => vec![Trans(1, 1.0)],
1914 _ => vec![],
1915 });
1916
1917 let mut s1 = State::new(enum_map! {
1919 _ => vec![],
1920 });
1921 s1.action = Some(Action::SendPadding {
1922 bypass: false,
1923 replace: false,
1924 timeout: Dist {
1925 dist: DistType::Uniform {
1926 low: 2.0,
1927 high: 2.0,
1928 },
1929 start: 0.0,
1930 max: 0.0,
1931 },
1932 limit: None,
1933 });
1934
1935 let m0 = Machine::new(1000, 1.0, 0, 0.0, vec![s0_m0, s1.clone()]).unwrap();
1937 let m1 = Machine::new(1000, 1.0, 0, 0.0, vec![s0_m1, s1.clone()]).unwrap();
1938
1939 let current_time = Instant::now();
1940 let machines = vec![m0, m1];
1941 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1942
1943 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1944 assert_eq!(f.actions[0], None);
1945 assert_eq!(
1946 f.actions[1],
1947 Some(TriggerAction::SendPadding {
1948 timeout: Duration::from_micros(2),
1949 bypass: false,
1950 replace: false,
1951 machine: MachineId(1),
1952 })
1953 );
1954 }
1955
1956 #[test]
1957 fn signal_two_machine() {
1958 let s0 = State::new(enum_map! {
1962 Event::NormalSent => vec![Trans(STATE_SIGNAL, 1.0)],
1963 Event::Signal => vec![Trans(1, 1.0)],
1964 _ => vec![],
1965 });
1966
1967 let mut s1 = State::new(enum_map! {
1969 _ => vec![],
1970 });
1971 s1.action = Some(Action::SendPadding {
1972 bypass: false,
1973 replace: false,
1974 timeout: Dist {
1975 dist: DistType::Uniform {
1976 low: 2.0,
1977 high: 2.0,
1978 },
1979 start: 0.0,
1980 max: 0.0,
1981 },
1982 limit: None,
1983 });
1984
1985 let m0 = Machine::new(1000, 1.0, 0, 0.0, vec![s0.clone(), s1.clone()]).unwrap();
1987 let m1 = Machine::new(1000, 1.0, 0, 0.0, vec![s0.clone(), s1.clone()]).unwrap();
1988
1989 let current_time = Instant::now();
1990 let machines = vec![m0, m1];
1991 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
1992
1993 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
1994 assert_eq!(
1995 f.actions[0],
1996 Some(TriggerAction::SendPadding {
1997 timeout: Duration::from_micros(2),
1998 bypass: false,
1999 replace: false,
2000 machine: MachineId(0),
2001 })
2002 );
2003 assert_eq!(
2004 f.actions[1],
2005 Some(TriggerAction::SendPadding {
2006 timeout: Duration::from_micros(2),
2007 bypass: false,
2008 replace: false,
2009 machine: MachineId(1),
2010 })
2011 );
2012 }
2013
2014 #[test]
2015 fn signal_response() {
2016 let s0_m0 = State::new(enum_map! {
2020 Event::NormalSent => vec![Trans(STATE_SIGNAL, 1.0)],
2021 Event::Signal => vec![Trans(1, 1.0)],
2022 _ => vec![],
2023 });
2024 let s0_m1 = State::new(enum_map! {
2025 Event::Signal => vec![Trans(STATE_SIGNAL, 1.0)],
2026 _ => vec![],
2027 });
2028
2029 let mut s1 = State::new(enum_map! {
2031 _ => vec![],
2032 });
2033 s1.action = Some(Action::SendPadding {
2034 bypass: false,
2035 replace: false,
2036 timeout: Dist {
2037 dist: DistType::Uniform {
2038 low: 2.0,
2039 high: 2.0,
2040 },
2041 start: 0.0,
2042 max: 0.0,
2043 },
2044 limit: None,
2045 });
2046
2047 let m0 = Machine::new(1000, 1.0, 0, 0.0, vec![s0_m0, s1.clone()]).unwrap();
2049 let m1 = Machine::new(1000, 1.0, 0, 0.0, vec![s0_m1, s1.clone()]).unwrap();
2050
2051 let current_time = Instant::now();
2052 let machines = vec![m0, m1];
2053 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
2054
2055 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2056 assert_eq!(
2057 f.actions[0],
2058 Some(TriggerAction::SendPadding {
2059 timeout: Duration::from_micros(2),
2060 bypass: false,
2061 replace: false,
2062 machine: MachineId(0),
2063 })
2064 );
2065 assert_eq!(f.actions[1], None);
2066 }
2067
2068 #[test]
2069 fn machine_max_padding_frac() {
2070 let mut s0 = State::new(enum_map! {
2078 Event::PaddingSent | Event::NormalSent | Event::NormalRecv => vec![Trans(0, 1.0)],
2081 _ => vec![],
2082 });
2083 s0.action = Some(Action::SendPadding {
2084 bypass: false,
2085 replace: false,
2086 timeout: Dist {
2087 dist: DistType::Uniform {
2088 low: 2.0,
2089 high: 2.0,
2090 },
2091 start: 0.0,
2092 max: 0.0,
2093 },
2094 limit: None,
2095 });
2096
2097 let m = Machine::new(100, 0.5, 0, 0.0, vec![s0]).unwrap();
2099
2100 let current_time = Instant::now();
2101 let machines = vec![m];
2102 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
2103
2104 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2106
2107 for _ in 0..100 {
2109 assert_eq!(
2110 f.actions[0],
2111 Some(TriggerAction::SendPadding {
2112 timeout: Duration::from_micros(2),
2113 bypass: false,
2114 replace: false,
2115 machine: MachineId(0),
2116 })
2117 );
2118
2119 _ = f.trigger_events(
2120 &[TriggerEvent::PaddingSent {
2121 machine: MachineId(0),
2122 }],
2123 current_time,
2124 );
2125 }
2126
2127 assert_eq!(f.actions[0], None);
2129
2130 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2132 assert_eq!(f.actions[0], None);
2133
2134 for _ in 0..100 {
2137 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2138 assert_eq!(f.actions[0], None);
2139 }
2140
2141 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2143
2144 assert_eq!(
2145 f.actions[0],
2146 Some(TriggerAction::SendPadding {
2147 timeout: Duration::from_micros(2),
2148 bypass: false,
2149 replace: false,
2150 machine: MachineId(0),
2151 })
2152 );
2153 }
2154
2155 #[test]
2156 fn framework_max_padding_frac() {
2157 let mut s0 = State::new(enum_map! {
2162 Event::PaddingSent | Event::NormalSent | Event::NormalRecv => vec![Trans(0, 1.0)],
2165 _ => vec![],
2166 });
2167 s0.action = Some(Action::SendPadding {
2168 bypass: false,
2169 replace: false,
2170 timeout: Dist {
2171 dist: DistType::Uniform {
2172 low: 2.0,
2173 high: 2.0,
2174 },
2175 start: 0.0,
2176 max: 0.0,
2177 },
2178 limit: None,
2179 });
2180
2181 let m1 = Machine::new(100, 0.0, 0, 0.0, vec![s0]).unwrap();
2183 let m2 = m1.clone();
2184
2185 let current_time = Instant::now();
2187 let machines = vec![m1, m2];
2188 let mut f = Framework::new(&machines, 0.5, 0.0, current_time, rand::rng()).unwrap();
2189
2190 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2194
2195 for _ in 0..100 {
2197 assert_eq!(
2198 f.actions[0],
2199 Some(TriggerAction::SendPadding {
2200 timeout: Duration::from_micros(2),
2201 bypass: false,
2202 replace: false,
2203 machine: MachineId(0),
2204 })
2205 );
2206 assert_eq!(
2207 f.actions[1],
2208 Some(TriggerAction::SendPadding {
2209 timeout: Duration::from_micros(2),
2210 bypass: false,
2211 replace: false,
2212 machine: MachineId(1),
2213 })
2214 );
2215 _ = f.trigger_events(
2216 &[
2217 TriggerEvent::PaddingSent {
2218 machine: MachineId(0),
2219 },
2220 TriggerEvent::PaddingSent {
2221 machine: MachineId(1),
2222 },
2223 TriggerEvent::TunnelSent,
2224 TriggerEvent::TunnelSent,
2225 ],
2226 current_time,
2227 );
2228 }
2229
2230 assert_eq!(f.actions[0], None);
2232 assert_eq!(f.actions[1], None);
2233 _ = f.trigger_events(
2234 &[TriggerEvent::NormalRecv, TriggerEvent::NormalRecv],
2235 current_time,
2236 );
2237 assert_eq!(f.actions[0], None);
2238 assert_eq!(f.actions[1], None);
2239
2240 assert_eq!(f.runtime[0].padding_sent, f.runtime[1].padding_sent);
2242 assert_eq!(f.runtime[0].padding_sent, 100);
2243
2244 for _ in 0..200 {
2248 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2249 assert_eq!(f.actions[0], None);
2250 assert_eq!(f.actions[1], None);
2251 }
2252
2253 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2255
2256 assert_eq!(
2257 f.actions[0],
2258 Some(TriggerAction::SendPadding {
2259 timeout: Duration::from_micros(2),
2260 bypass: false,
2261 replace: false,
2262 machine: MachineId(0),
2263 })
2264 );
2265 assert_eq!(
2266 f.actions[1],
2267 Some(TriggerAction::SendPadding {
2268 timeout: Duration::from_micros(2),
2269 bypass: false,
2270 replace: false,
2271 machine: MachineId(1),
2272 })
2273 );
2274 }
2275
2276 #[test]
2277 fn machine_max_blocking_frac() {
2278 let mut s0 = State::new(enum_map! {
2285 Event::BlockingBegin | Event::BlockingEnd | Event::NormalRecv => vec![Trans(0, 1.0)],
2286 _ => vec![],
2287 });
2288 s0.action = Some(Action::BlockOutgoing {
2290 bypass: false,
2291 replace: false,
2292 timeout: Dist {
2293 dist: DistType::Uniform {
2294 low: 2.0,
2295 high: 2.0,
2296 },
2297 start: 0.0,
2298 max: 0.0,
2299 },
2300 duration: Dist {
2301 dist: DistType::Uniform {
2302 low: 2.0,
2303 high: 2.0,
2304 },
2305 start: 0.0,
2306 max: 0.0,
2307 },
2308 limit: None,
2309 });
2310
2311 let m = Machine::new(0, 0.0, 10, 0.5, vec![s0]).unwrap();
2313
2314 let mut current_time = Instant::now();
2315 let machines = vec![m];
2316 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
2317
2318 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2320
2321 for _ in 0..5 {
2323 assert_eq!(
2324 f.actions[0],
2325 Some(TriggerAction::BlockOutgoing {
2326 timeout: Duration::from_micros(2),
2327 duration: Duration::from_micros(2),
2328 bypass: false,
2329 replace: false,
2330 machine: MachineId(0),
2331 })
2332 );
2333
2334 _ = f.trigger_events(
2335 &[TriggerEvent::BlockingBegin {
2336 machine: MachineId(0),
2337 }],
2338 current_time,
2339 );
2340 assert_eq!(
2341 f.actions[0],
2342 Some(TriggerAction::BlockOutgoing {
2343 timeout: Duration::from_micros(2),
2344 duration: Duration::from_micros(2),
2345 bypass: false,
2346 replace: false,
2347 machine: MachineId(0),
2348 })
2349 );
2350 current_time = current_time.add(Duration::from_micros(2));
2351 _ = f.trigger_events(&[TriggerEvent::BlockingEnd], current_time);
2352 }
2353 assert_eq!(f.actions[0], None);
2354 assert_eq!(f.runtime[0].blocking_duration, Duration::from_micros(10));
2355
2356 for _ in 0..5 {
2358 current_time = current_time.add(Duration::from_micros(2));
2359 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2360 assert_eq!(f.actions[0], None);
2361 }
2362 assert_eq!(f.runtime[0].blocking_duration, Duration::from_micros(10));
2363 assert_eq!(
2364 current_time.duration_since(f.runtime[0].machine_start),
2365 Duration::from_micros(20)
2366 );
2367
2368 current_time = current_time.add(Duration::from_micros(2));
2370 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2371 assert_eq!(
2372 f.actions[0],
2373 Some(TriggerAction::BlockOutgoing {
2374 timeout: Duration::from_micros(2),
2375 duration: Duration::from_micros(2),
2376 bypass: false,
2377 replace: false,
2378 machine: MachineId(0),
2379 })
2380 );
2381 }
2382
2383 #[test]
2384 fn framework_max_blocking_frac() {
2385 let mut s0 = State::new(enum_map! {
2392 Event::BlockingBegin | Event::BlockingEnd | Event::NormalRecv => vec![Trans(0, 1.0)],
2393 _ => vec![],
2394 });
2395 s0.action = Some(Action::BlockOutgoing {
2397 bypass: false,
2398 replace: false,
2399 timeout: Dist {
2400 dist: DistType::Uniform {
2401 low: 2.0,
2402 high: 2.0,
2403 },
2404
2405 start: 0.0,
2406 max: 0.0,
2407 },
2408 duration: Dist {
2409 dist: DistType::Uniform {
2410 low: 2.0,
2411 high: 2.0,
2412 },
2413
2414 start: 0.0,
2415 max: 0.0,
2416 },
2417 limit: None,
2418 });
2419
2420 let m = Machine::new(0, 0.0, 10, 0.0, vec![s0]).unwrap();
2422
2423 let mut current_time = Instant::now();
2424 let machines = vec![m];
2425 let mut f = Framework::new(&machines, 0.0, 0.5, current_time, rand::rng()).unwrap();
2426
2427 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2429
2430 for _ in 0..5 {
2432 assert_eq!(
2433 f.actions[0],
2434 Some(TriggerAction::BlockOutgoing {
2435 timeout: Duration::from_micros(2),
2436 duration: Duration::from_micros(2),
2437 bypass: false,
2438 replace: false,
2439 machine: MachineId(0),
2440 })
2441 );
2442
2443 _ = f.trigger_events(
2444 &[TriggerEvent::BlockingBegin {
2445 machine: MachineId(0),
2446 }],
2447 current_time,
2448 );
2449 assert_eq!(
2450 f.actions[0],
2451 Some(TriggerAction::BlockOutgoing {
2452 timeout: Duration::from_micros(2),
2453 duration: Duration::from_micros(2),
2454 bypass: false,
2455 replace: false,
2456 machine: MachineId(0),
2457 })
2458 );
2459 current_time = current_time.add(Duration::from_micros(2));
2460 _ = f.trigger_events(&[TriggerEvent::BlockingEnd], current_time);
2461 }
2462 assert_eq!(f.actions[0], None);
2463 assert_eq!(f.runtime[0].blocking_duration, Duration::from_micros(10));
2464
2465 for _ in 0..5 {
2467 current_time = current_time.add(Duration::from_micros(2));
2468 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2469 assert_eq!(f.actions[0], None);
2470 }
2471 assert_eq!(f.runtime[0].blocking_duration, Duration::from_micros(10));
2472 assert_eq!(
2473 current_time.duration_since(f.runtime[0].machine_start),
2474 Duration::from_micros(20)
2475 );
2476
2477 current_time = current_time.add(Duration::from_micros(2));
2479 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2480 assert_eq!(
2481 f.actions[0],
2482 Some(TriggerAction::BlockOutgoing {
2483 timeout: Duration::from_micros(2),
2484 duration: Duration::from_micros(2),
2485 bypass: false,
2486 replace: false,
2487 machine: MachineId(0),
2488 })
2489 );
2490 }
2491
2492 #[test]
2493 fn framework_replace_blocking() {
2494 let mut s0 = State::new(enum_map! {
2501 Event::NormalRecv => vec![Trans(0, 1.0)],
2502 _ => vec![],
2503 });
2504 s0.action = Some(Action::BlockOutgoing {
2506 bypass: false,
2507 replace: true, timeout: Dist {
2509 dist: DistType::Uniform {
2510 low: 2.0,
2511 high: 2.0,
2512 },
2513 start: 0.0,
2514 max: 0.0,
2515 },
2516 duration: Dist {
2517 dist: DistType::Uniform {
2518 low: 2.0,
2519 high: 2.0,
2520 },
2521 start: 0.0,
2522 max: 0.0,
2523 },
2524 limit: None,
2525 });
2526
2527 let m0 = Machine::new(0, 0.0, 2, 0.5, vec![s0]).unwrap();
2529
2530 let mut s0 = State::new(enum_map! {
2532 Event::NormalSent => vec![Trans(0, 1.0)],
2533 _ => vec![],
2534 });
2535 s0.action = Some(Action::BlockOutgoing {
2537 bypass: false,
2538 replace: false,
2539 timeout: Dist {
2540 dist: DistType::Uniform {
2541 low: 0.0,
2542 high: 0.0,
2543 },
2544
2545 start: 0.0,
2546 max: 0.0,
2547 },
2548 duration: Dist {
2549 dist: DistType::Uniform {
2550 low: 1000.0,
2551 high: 1000.0,
2552 },
2553 start: 0.0,
2554 max: 0.0,
2555 },
2556 limit: None,
2557 });
2558
2559 let m1 = Machine::new(0, 0.0, 0, 0.0, vec![s0]).unwrap();
2561
2562 let mut current_time = Instant::now();
2563 let machines = vec![m0, m1];
2564 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
2565
2566 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2568
2569 assert_eq!(
2571 f.actions[0],
2572 Some(TriggerAction::BlockOutgoing {
2573 timeout: Duration::from_micros(2),
2574 duration: Duration::from_micros(2),
2575 bypass: false,
2576 replace: true,
2577 machine: MachineId(0),
2578 })
2579 );
2580
2581 _ = f.trigger_events(
2582 &[TriggerEvent::BlockingBegin {
2583 machine: MachineId(0),
2584 }],
2585 current_time,
2586 );
2587
2588 current_time = current_time.add(Duration::from_micros(2));
2589 _ = f.trigger_events(&[TriggerEvent::BlockingEnd], current_time);
2590
2591 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2593
2594 assert_eq!(f.actions[0], None);
2595 assert_eq!(f.runtime[0].blocking_duration, Duration::from_micros(2));
2596
2597 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2599
2600 assert_eq!(
2602 f.actions[1],
2603 Some(TriggerAction::BlockOutgoing {
2604 timeout: Duration::from_micros(0),
2605 duration: Duration::from_micros(1000),
2606 bypass: false,
2607 replace: false,
2608 machine: MachineId(1),
2609 })
2610 );
2611
2612 _ = f.trigger_events(
2613 &[TriggerEvent::BlockingBegin {
2614 machine: MachineId(1),
2615 }],
2616 current_time,
2617 );
2618
2619 _ = f.trigger_events(&[TriggerEvent::NormalRecv], current_time);
2621
2622 assert_eq!(
2623 f.actions[0],
2624 Some(TriggerAction::BlockOutgoing {
2625 timeout: Duration::from_micros(2),
2626 duration: Duration::from_micros(2),
2627 bypass: false,
2628 replace: true,
2629 machine: MachineId(0),
2630 })
2631 );
2632 }
2633
2634 #[test]
2635 fn framework_machine_sampled_limit() {
2636 let s0 = State::new(enum_map! {
2642 Event::NormalSent => vec![Trans(1, 1.0)],
2643 _ => vec![],
2644 });
2645
2646 let mut s1 = State::new(enum_map! {
2648 Event::PaddingSent => vec![Trans(1, 1.0)],
2649 _ => vec![],
2650 });
2651 s1.action = Some(Action::SendPadding {
2652 bypass: false,
2653 replace: false,
2654 timeout: Dist {
2655 dist: DistType::Uniform {
2656 low: 1.0,
2657 high: 1.0,
2658 },
2659 start: 0.0,
2660 max: 0.0,
2661 },
2662 limit: Some(Dist {
2663 dist: DistType::Uniform {
2664 low: 4.0,
2665 high: 4.0,
2666 },
2667 start: 0.0,
2668 max: 0.0,
2669 }),
2670 });
2671
2672 let m = Machine::new(100000, 0.0, 0, 0.0, vec![s0, s1]).unwrap();
2674
2675 let mut current_time = Instant::now();
2676 let machines = vec![m];
2677 let mut f = Framework::new(&machines, 0.0, 0.0, current_time, rand::rng()).unwrap();
2678
2679 _ = f.trigger_events(&[TriggerEvent::NormalSent], current_time);
2681
2682 assert_eq!(f.runtime[0].state_limit, 4);
2683
2684 for _ in 0..4 {
2686 assert_eq!(
2687 f.actions[0],
2688 Some(TriggerAction::SendPadding {
2689 timeout: Duration::from_micros(1),
2690 bypass: false,
2691 replace: false,
2692 machine: MachineId(0),
2693 })
2694 );
2695 current_time = current_time.add(Duration::from_micros(1));
2696 _ = f.trigger_events(
2697 &[TriggerEvent::PaddingSent {
2698 machine: MachineId(0),
2699 }],
2700 current_time,
2701 );
2702 }
2703
2704 assert_eq!(f.runtime[0].padding_sent, 4);
2706 assert_eq!(f.runtime[0].normal_sent, 1);
2707
2708 assert_eq!(f.actions[0], None);
2710 assert_eq!(f.runtime[0].state_limit, 0);
2711 }
2712}