netem_trace/model/delay_per_packet.rs
1//! This module contains some predefined delay per-packet trace models.
2//!
3//! Enabled with feature `delay-per-packet-model` or `model`.
4//!
5//! ## Predefined models
6//!
7//! - [`StaticDelayPerPacket`]: A trace model with static delay.
8//! - [`RepeatedDelayPerPacketPattern`]: A trace model with a repeated delay pattern.
9//! - [`NormalizedDelayPerPacket`]: A trace model whose delay subjects to a normal distribution (can set upper and lower bounds, and can configure it to be truncated with `truncated-normal` feature enabled).
10//!
11//! ## Examples
12//!
13//! An example to build model from configuration:
14//!
15//! ```
16//! # use netem_trace::model::StaticDelayPerPacketConfig;
17//! # use netem_trace::{Delay, DelayPerPacketTrace};
18//! let mut static_delay = StaticDelayPerPacketConfig::new()
19//! .delay(Delay::from_millis(10))
20//! .count(2)
21//! .build();
22//! assert_eq!(static_delay.next_delay(), Some(Delay::from_millis(10)));
23//! assert_eq!(static_delay.next_delay(), Some(Delay::from_millis(10)));
24//! assert_eq!(static_delay.next_delay(), None);
25//! ```
26//!
27//! A more common use case is to build model from a configuration file (e.g. json file):
28//!
29//! ```
30//! # use netem_trace::model::DelayPerPacketTraceConfig;
31//! # use netem_trace::{Delay, DelayPerPacketTrace};
32//! # #[cfg(feature = "human")]
33//! # let config_file_content = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":\"10ms\",\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":\"20ms\",\"count\":1}}],\"count\":2}}";
34//! // The content would be "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}"
35//! // if the `human` feature is not enabled.
36//! # #[cfg(not(feature = "human"))]
37//! let config_file_content = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}";
38//! let des: Box<dyn DelayPerPacketTraceConfig> = serde_json::from_str(config_file_content).unwrap();
39//! let mut model = des.into_model();
40//! assert_eq!(
41//! model.next_delay(),
42//! Some(Delay::from_millis(10))
43//! );
44//! assert_eq!(
45//! model.next_delay(),
46//! Some(Delay::from_millis(20))
47//! );
48//! assert_eq!(
49//! model.next_delay(),
50//! Some(Delay::from_millis(10))
51//! );
52//! assert_eq!(
53//! model.next_delay(),
54//! Some(Delay::from_millis(20))
55//! );
56//! assert_eq!(model.next_delay(), None);
57//! ```
58use crate::{Delay, DelayPerPacketTrace};
59use dyn_clone::DynClone;
60
61const DEFAULT_RNG_SEED: u64 = 42; // Some documentation will need corrections if this changes
62
63/// This trait is used to convert a per-packet delay trace configuration into a per-packet delay trace model.
64///
65/// Since trace model is often configured with files and often has inner states which
66/// is not suitable to be serialized/deserialized, this trait makes it possible to
67/// separate the configuration part into a simple struct for serialization/deserialization, and
68/// construct the model from the configuration.
69#[cfg_attr(feature = "serde", typetag::serde)]
70pub trait DelayPerPacketTraceConfig: DynClone + Send + std::fmt::Debug {
71 fn into_model(self: Box<Self>) -> Box<dyn DelayPerPacketTrace>;
72}
73
74dyn_clone::clone_trait_object!(DelayPerPacketTraceConfig);
75
76use rand::{rngs::StdRng, RngCore, SeedableRng};
77use rand_distr::{Distribution, LogNormal, Normal, Uniform};
78#[cfg(feature = "serde")]
79use serde::{Deserialize, Serialize};
80
81#[cfg(feature = "truncated-normal")]
82use super::solve_truncate::solve;
83
84/// The model of a static per-packet delay trace.
85///
86/// This model always returns the same delay for the given amount of packets.
87///
88/// If the `count` is 0, the delay will be repeated forever.
89///
90/// ## Examples
91///
92/// ```
93/// # use netem_trace::model::StaticDelayPerPacketConfig;
94/// # use netem_trace::{Delay, DelayPerPacketTrace};
95/// let mut static_delay = StaticDelayPerPacketConfig::new()
96/// .delay(Delay::from_millis(10))
97/// .count(2)
98/// .build();
99/// assert_eq!(static_delay.next_delay(), Some(Delay::from_millis(10)));
100/// assert_eq!(static_delay.next_delay(), Some(Delay::from_millis(10)));
101/// assert_eq!(static_delay.next_delay(), None);
102/// ```
103#[derive(Default, Debug, Clone, Copy, PartialEq)]
104pub struct StaticDelayPerPacket {
105 pub delay: Delay,
106 pub count: usize,
107 current_count: usize,
108}
109
110/// The configuration struct for [`StaticDelayPerPacket`].
111///
112/// See [`StaticDelayPerPacket`] for more details.
113#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
114#[derive(Default, Debug, Clone, Copy, PartialEq)]
115pub struct StaticDelayPerPacketConfig {
116 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
117 #[cfg_attr(
118 all(feature = "serde", feature = "human"),
119 serde(with = "humantime_serde")
120 )]
121 pub delay: Option<Delay>,
122 pub count: usize,
123}
124
125/// The model contains an array of per-packet delay trace models.
126///
127/// Combine multiple per-packet delay trace models into one per-packet delay pattern,
128/// and repeat the pattern for `count` times.
129///
130/// If `count` is 0, the pattern will be repeated forever.
131///
132/// ## Examples
133///
134/// The most common use case is to read from a configuration file and
135/// deserialize it into a [`RepeatedDelayPerPacketPatternConfig`]:
136///
137/// ```
138/// use netem_trace::model::{StaticDelayPerPacketConfig, DelayPerPacketTraceConfig};
139/// use netem_trace::{Delay, DelayPerPacketTrace};
140/// #[cfg(feature = "human")]
141/// let config_file_content = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":\"10ms\",\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":\"20ms\",\"count\":1}}],\"count\":2}}";
142/// // The content would be "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}"
143/// // if the `human` feature is not enabled.
144/// #[cfg(not(feature = "human"))]
145/// let config_file_content = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}";
146/// let des: Box<dyn DelayPerPacketTraceConfig> = serde_json::from_str(config_file_content).unwrap();
147/// let mut model = des.into_model();
148/// assert_eq!(
149/// model.next_delay(),
150/// Some(Delay::from_millis(10))
151/// );
152/// assert_eq!(
153/// model.next_delay(),
154/// Some(Delay::from_millis(20))
155/// );
156/// assert_eq!(
157/// model.next_delay(),
158/// Some(Delay::from_millis(10))
159/// );
160/// assert_eq!(
161/// model.next_delay(),
162/// Some(Delay::from_millis(20))
163/// );
164/// assert_eq!(model.next_delay(), None);
165/// ```
166///
167/// You can also build manually:
168///
169/// ```
170/// # use netem_trace::model::{StaticDelayPerPacketConfig, DelayPerPacketTraceConfig, RepeatedDelayPerPacketPatternConfig};
171/// # use netem_trace::{Delay, DelayPerPacketTrace};
172/// let pat = vec![
173/// Box::new(
174/// StaticDelayPerPacketConfig::new()
175/// .delay(Delay::from_millis(10))
176/// .count(1),
177/// ) as Box<dyn DelayPerPacketTraceConfig>,
178/// Box::new(
179/// StaticDelayPerPacketConfig::new()
180/// .delay(Delay::from_millis(20))
181/// .count(1),
182/// ) as Box<dyn DelayPerPacketTraceConfig>,
183/// ];
184/// let ser = Box::new(RepeatedDelayPerPacketPatternConfig::new().pattern(pat).count(2)) as Box<dyn DelayPerPacketTraceConfig>;
185/// let ser_str = serde_json::to_string(&ser).unwrap();
186/// #[cfg(feature = "human")]
187/// let json_str = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":\"10ms\",\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":\"20ms\",\"count\":1}}],\"count\":2}}";
188/// // The content would be "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}"
189/// // if the `human` feature is not enabled.
190/// #[cfg(not(feature = "human"))]
191/// let json_str = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}";
192/// assert_eq!(ser_str, json_str);
193/// ```
194#[derive(Default)]
195pub struct RepeatedDelayPerPacketPattern {
196 pub pattern: Vec<Box<dyn DelayPerPacketTraceConfig>>,
197 pub count: usize,
198 current_model: Option<Box<dyn DelayPerPacketTrace>>,
199 current_cycle: usize,
200 current_pattern: usize,
201}
202
203/// The configuration struct for [`RepeatedDelayPerPacketPattern`].
204///
205/// See [`RepeatedDelayPerPacketPattern`] for more details.
206#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
207#[derive(Default, Debug, Clone)]
208pub struct RepeatedDelayPerPacketPatternConfig {
209 pub pattern: Vec<Box<dyn DelayPerPacketTraceConfig>>,
210 pub count: usize,
211}
212
213/// The model of a per-packet delay trace subjects to a uniform distribution.
214///
215/// The delay will subject to a uniform distribution in [lower_bound, upper_bound]
216///
217/// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
218///
219/// ## Examples
220///
221/// Default values for lower_bound and upper_bound are 0ms and 10ms, respectively.
222///
223/// ```
224/// # use netem_trace::model::UniformDelayPerPacketConfig;
225/// # use netem_trace::{Delay, DelayPerPacketTrace};
226/// let mut uniform_delay = UniformDelayPerPacketConfig::new()
227/// .upper_bound(Delay::from_micros(12100))
228/// .lower_bound(Delay::from_micros(11900))
229/// .count(2)
230/// .seed(42)
231/// .build();
232/// assert_eq!(uniform_delay.next_delay(), Some(Delay::from_nanos(12005311)));
233/// assert_eq!(uniform_delay.next_delay(), Some(Delay::from_nanos(12008545)));
234/// assert_eq!(uniform_delay.next_delay(), None);
235/// ```
236#[derive(Debug, Clone, Copy, PartialEq)]
237pub struct UniformDelayPerPacket<Rng = StdRng>
238where
239 Rng: RngCore,
240{
241 pub upper_bound: Delay,
242 pub lower_bound: Delay,
243 pub seed: u64,
244 pub count: usize,
245 current_count: usize,
246 rng: Rng,
247 uniform: Uniform<f64>,
248}
249
250/// The configuration struct for [`UniformDelayPerPacket`].
251///
252/// See [`UniformDelayPerPacket`] for more details.
253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
254#[derive(Default, Debug, Clone, Copy, PartialEq)]
255pub struct UniformDelayPerPacketConfig {
256 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
257 #[cfg_attr(
258 all(feature = "serde", feature = "human"),
259 serde(with = "humantime_serde")
260 )]
261 pub upper_bound: Option<Delay>,
262 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
263 #[cfg_attr(
264 all(feature = "serde", feature = "human"),
265 serde(with = "humantime_serde")
266 )]
267 pub lower_bound: Option<Delay>,
268 #[cfg_attr(feature = "serde", serde(default))]
269 pub count: usize,
270 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
271 pub seed: Option<u64>,
272}
273
274/// The model of a per-packet delay trace subjects to a normal distribution.
275///
276/// The delay will subject to N(mean, std_dev²), but bounded within [lower_bound, upper_bound] (optional)
277///
278/// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
279///
280/// ## Examples
281///
282/// A simple example without any bound on delay:
283///
284/// ```
285/// # use netem_trace::model::NormalizedDelayPerPacketConfig;
286/// # use netem_trace::{Delay, DelayPerPacketTrace};
287/// let mut normal_delay = NormalizedDelayPerPacketConfig::new()
288/// .mean(Delay::from_millis(12))
289/// .std_dev(Delay::from_millis(1))
290/// .count(2)
291/// .seed(42)
292/// .build();
293/// assert_eq!(normal_delay.next_delay(), Some(Delay::from_nanos(12069428)));
294/// assert_eq!(normal_delay.next_delay(), Some(Delay::from_nanos(12132938)));
295/// assert_eq!(normal_delay.next_delay(), None);
296/// ```
297///
298/// A more complex example with bounds on delay:
299///
300/// ```
301/// # use netem_trace::model::NormalizedDelayPerPacketConfig;
302/// # use netem_trace::{Delay, DelayPerPacketTrace};
303/// let mut normal_delay = NormalizedDelayPerPacketConfig::new()
304/// .mean(Delay::from_millis(12))
305/// .std_dev(Delay::from_millis(1))
306/// .count(2)
307/// .seed(42)
308/// .upper_bound(Delay::from_micros(12100))
309/// .lower_bound(Delay::from_micros(11900))
310/// .build();
311/// assert_eq!(normal_delay.next_delay(), Some(Delay::from_nanos(12069428)));
312/// assert_eq!(normal_delay.next_delay(), Some(Delay::from_nanos(12100000)));
313/// assert_eq!(normal_delay.next_delay(), None);
314/// ```
315#[derive(Debug, Clone, Copy, PartialEq)]
316pub struct NormalizedDelayPerPacket<Rng = StdRng>
317where
318 Rng: RngCore,
319{
320 pub mean: Delay,
321 pub std_dev: Delay,
322 pub upper_bound: Option<Delay>,
323 pub lower_bound: Delay,
324 pub seed: u64,
325 pub count: usize,
326 current_count: usize,
327 rng: Rng,
328 normal: Normal<f64>,
329}
330
331/// The configuration struct for [`NormalizedDelayPerPacket`].
332///
333/// See [`NormalizedDelayPerPacket`] for more details.
334#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
335#[derive(Default, Debug, Clone, Copy, PartialEq)]
336pub struct NormalizedDelayPerPacketConfig {
337 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
338 #[cfg_attr(
339 all(feature = "serde", feature = "human"),
340 serde(with = "humantime_serde")
341 )]
342 pub mean: Option<Delay>,
343 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
344 #[cfg_attr(
345 all(feature = "serde", feature = "human"),
346 serde(with = "humantime_serde")
347 )]
348 pub std_dev: Option<Delay>,
349 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
350 #[cfg_attr(
351 all(feature = "serde", feature = "human"),
352 serde(with = "humantime_serde")
353 )]
354 pub upper_bound: Option<Delay>,
355 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
356 #[cfg_attr(
357 all(feature = "serde", feature = "human"),
358 serde(with = "humantime_serde")
359 )]
360 pub lower_bound: Option<Delay>,
361 #[cfg_attr(feature = "serde", serde(default))]
362 pub count: usize,
363 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
364 pub seed: Option<u64>,
365}
366/// The model of a per-packet delay trace subjects to a log-normal distribution.
367///
368/// The delay will subject to Lognormal(μ, σ²), but bounded within [lower_bound, upper_bound] (optional)
369/// The provided mean and std_dev are the one of the log-normal law and not the one of the underlying normal law.
370///
371/// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
372///
373/// ## Examples
374///
375/// A simple example without any bound on delay:
376///
377/// ```
378/// # use netem_trace::model::LogNormalizedDelayPerPacketConfig;
379/// # use netem_trace::{Delay, DelayPerPacketTrace};
380/// let mut log_normal_delay = LogNormalizedDelayPerPacketConfig::new()
381/// .mean(Delay::from_millis(12))
382/// .std_dev(Delay::from_millis(1))
383/// .count(2)
384/// .seed(42)
385/// .build();
386/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12027817)));
387/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12091533)));
388/// assert_eq!(log_normal_delay.next_delay(), None);
389/// ```
390///
391/// A more complex example with bounds on delay:
392///
393/// ```
394/// # use netem_trace::model::LogNormalizedDelayPerPacketConfig;
395/// # use netem_trace::{Delay, DelayPerPacketTrace};
396/// let mut log_normal_delay = LogNormalizedDelayPerPacketConfig::new()
397/// .mean(Delay::from_millis(12))
398/// .std_dev(Delay::from_millis(1))
399/// .count(3)
400/// .seed(42)
401/// .upper_bound(Delay::from_micros(12100))
402/// .lower_bound(Delay::from_micros(11900))
403/// .build();
404/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12027817)));
405/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12091533)));
406/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12100000)));
407/// assert_eq!(log_normal_delay.next_delay(), None);
408/// ```
409#[derive(Debug, Clone, Copy, PartialEq)]
410pub struct LogNormalizedDelayPerPacket<Rng = StdRng>
411where
412 Rng: RngCore,
413{
414 pub mean: Delay,
415 pub std_dev: Delay,
416 pub upper_bound: Option<Delay>,
417 pub lower_bound: Delay,
418 pub seed: u64,
419 pub count: usize,
420 current_count: usize,
421 rng: Rng,
422 log_normal: LogNormal<f64>,
423}
424
425/// The configuration struct for [`LogNormalizedDelayPerPacket`].
426///
427/// See [`LogNormalizedDelayPerPacket`] for more details.
428#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
429#[derive(Default, Debug, Clone, Copy, PartialEq)]
430pub struct LogNormalizedDelayPerPacketConfig {
431 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
432 #[cfg_attr(
433 all(feature = "serde", feature = "human"),
434 serde(with = "humantime_serde")
435 )]
436 pub mean: Option<Delay>,
437 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
438 #[cfg_attr(
439 all(feature = "serde", feature = "human"),
440 serde(with = "humantime_serde")
441 )]
442 pub std_dev: Option<Delay>,
443 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
444 #[cfg_attr(
445 all(feature = "serde", feature = "human"),
446 serde(with = "humantime_serde")
447 )]
448 pub upper_bound: Option<Delay>,
449 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
450 #[cfg_attr(
451 all(feature = "serde", feature = "human"),
452 serde(with = "humantime_serde")
453 )]
454 pub lower_bound: Option<Delay>,
455 #[cfg_attr(feature = "serde", serde(default))]
456 pub count: usize,
457 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
458 pub seed: Option<u64>,
459}
460
461impl DelayPerPacketTrace for StaticDelayPerPacket {
462 fn next_delay(&mut self) -> Option<Delay> {
463 if self.count != 0 && self.count == self.current_count {
464 None
465 } else {
466 self.current_count += 1;
467 Some(self.delay)
468 }
469 }
470}
471
472impl DelayPerPacketTrace for RepeatedDelayPerPacketPattern {
473 fn next_delay(&mut self) -> Option<Delay> {
474 if self.pattern.is_empty() || (self.count != 0 && self.current_cycle >= self.count) {
475 None
476 } else {
477 if self.current_model.is_none() {
478 self.current_model = Some(self.pattern[self.current_pattern].clone().into_model());
479 }
480 match self.current_model.as_mut().unwrap().next_delay() {
481 Some(delay) => Some(delay),
482 None => {
483 self.current_model = None;
484 self.current_pattern += 1;
485 if self.current_pattern >= self.pattern.len() {
486 self.current_pattern = 0;
487 self.current_cycle += 1;
488 if self.count != 0 && self.current_cycle >= self.count {
489 return None;
490 }
491 }
492 self.next_delay()
493 }
494 }
495 }
496 }
497}
498
499impl<Rng: RngCore + Send> DelayPerPacketTrace for UniformDelayPerPacket<Rng> {
500 fn next_delay(&mut self) -> Option<Delay> {
501 if self.count != 0 && self.count == self.current_count {
502 None
503 } else {
504 self.current_count += 1;
505 let delay = self.uniform.sample(&mut self.rng).max(0.0);
506 Some(Delay::from_secs_f64(delay))
507 }
508 }
509}
510
511impl<Rng: RngCore + Send> DelayPerPacketTrace for NormalizedDelayPerPacket<Rng> {
512 fn next_delay(&mut self) -> Option<Delay> {
513 if self.count != 0 && self.count == self.current_count {
514 None
515 } else {
516 self.current_count += 1;
517 let delay = self.normal.sample(&mut self.rng).max(0.0);
518 let mut delay = Delay::from_secs_f64(delay);
519 delay = delay.max(self.lower_bound);
520 if let Some(upper_bound) = self.upper_bound {
521 delay = delay.min(upper_bound);
522 }
523 Some(delay)
524 }
525 }
526}
527
528impl<Rng: RngCore + Send> DelayPerPacketTrace for LogNormalizedDelayPerPacket<Rng> {
529 fn next_delay(&mut self) -> Option<Delay> {
530 if self.count != 0 && self.count == self.current_count {
531 None
532 } else {
533 self.current_count += 1;
534 let delay = self.log_normal.sample(&mut self.rng).max(0.0);
535 let mut delay = Delay::from_secs_f64(delay);
536 delay = delay.max(self.lower_bound);
537 if let Some(upper_bound) = self.upper_bound {
538 delay = delay.min(upper_bound);
539 }
540 Some(delay)
541 }
542 }
543}
544
545impl StaticDelayPerPacketConfig {
546 pub fn new() -> Self {
547 Self {
548 delay: None,
549 count: 0,
550 }
551 }
552
553 pub fn delay(mut self, delay: Delay) -> Self {
554 self.delay = Some(delay);
555 self
556 }
557
558 pub fn count(mut self, count: usize) -> Self {
559 self.count = count;
560 self
561 }
562
563 pub fn build(self) -> StaticDelayPerPacket {
564 StaticDelayPerPacket {
565 delay: self.delay.unwrap_or_else(|| Delay::from_millis(10)),
566 count: self.count,
567 current_count: 0,
568 }
569 }
570}
571
572impl RepeatedDelayPerPacketPatternConfig {
573 pub fn new() -> Self {
574 Self {
575 pattern: vec![],
576 count: 0,
577 }
578 }
579
580 pub fn pattern(mut self, pattern: Vec<Box<dyn DelayPerPacketTraceConfig>>) -> Self {
581 self.pattern = pattern;
582 self
583 }
584
585 pub fn count(mut self, count: usize) -> Self {
586 self.count = count;
587 self
588 }
589
590 pub fn build(self) -> RepeatedDelayPerPacketPattern {
591 RepeatedDelayPerPacketPattern {
592 pattern: self.pattern,
593 count: self.count,
594 current_model: None,
595 current_cycle: 0,
596 current_pattern: 0,
597 }
598 }
599}
600
601impl UniformDelayPerPacketConfig {
602 /// Creates an uninitialized config
603 pub fn new() -> Self {
604 Self {
605 upper_bound: None,
606 lower_bound: None,
607 count: 0,
608 seed: None,
609 }
610 }
611
612 /// Sets the upper bound
613 ///
614 /// If the upper bound is not set, the upper bound will be 10ms.
615 pub fn upper_bound(mut self, upper_bound: Delay) -> Self {
616 self.upper_bound = Some(upper_bound);
617 self
618 }
619
620 /// Sets the lower bound
621 ///
622 /// If the lower bound is not set, 0ms will be used.
623 pub fn lower_bound(mut self, lower_bound: Delay) -> Self {
624 self.lower_bound = Some(lower_bound);
625 self
626 }
627
628 /// Sets the number of packets to repeat
629 ///
630 /// If the count is not set, it will be set to 0 (ie, infinite repeat).
631 pub fn count(mut self, count: usize) -> Self {
632 self.count = count;
633 self
634 }
635
636 /// Set the seed for a random generator
637 ///
638 /// If the seed is not set, `42` will be used.
639 pub fn seed(mut self, seed: u64) -> Self {
640 self.seed = Some(seed);
641 self
642 }
643
644 /// Allows to use a randomly generated seed
645 ///
646 /// This is equivalent to: `self.seed(rand::random())`
647 pub fn random_seed(mut self) -> Self {
648 self.seed = Some(rand::random());
649 self
650 }
651
652 /// Creates a new [`UniformDelayPerPacket`] corresponding to this config.
653 ///
654 /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
655 /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
656 /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
657 /// to this end one can use [`build_with_rng`](Self::build_with_rng).
658 pub fn build(self) -> UniformDelayPerPacket {
659 self.build_with_rng()
660 }
661
662 /// Creates a new [`UniformDelayPerPacket`] corresponding to this config.
663 ///
664 /// Unlike [`build`](Self::build), this method let you choose the random generator.
665 ///
666 /// # Example
667 /// ```rust
668 /// # use netem_trace::model::UniformDelayPerPacketConfig;
669 /// # use netem_trace::{Delay, DelayPerPacketTrace};
670 /// # use rand::rngs::StdRng;
671 /// # use rand_chacha::ChaCha20Rng;
672 ///
673 /// let uniform_delay = UniformDelayPerPacketConfig::new()
674 /// .upper_bound(Delay::from_micros(12100))
675 /// .lower_bound(Delay::from_micros(11900))
676 /// .count(3)
677 /// .seed(42);
678 ///
679 /// let mut default_build = uniform_delay.clone().build();
680 /// let mut std_build = uniform_delay.clone().build_with_rng::<StdRng>();
681 /// // ChaCha is deterministic and portable, unlike StdRng
682 /// let mut chacha_build = uniform_delay.clone().build_with_rng::<ChaCha20Rng>();
683 ///
684 /// for cha in [12002810, 11982040, 11919563] {
685 /// let default = default_build.next_delay();
686 /// let std = std_build.next_delay();
687 /// let chacha = chacha_build.next_delay();
688 ///
689 /// assert!(default.is_some());
690 /// assert_eq!(default, std);
691 /// assert_ne!(default, chacha);
692 /// assert_eq!(chacha, Some(Delay::from_nanos(cha)));
693 /// }
694 ///
695 /// assert_eq!(default_build.next_delay(), None);
696 /// assert_eq!(std_build.next_delay(), None);
697 /// assert_eq!(chacha_build.next_delay(), None);
698 /// ```
699 pub fn build_with_rng<Rng: RngCore + SeedableRng>(self) -> UniformDelayPerPacket<Rng> {
700 let upper_bound = self.upper_bound.unwrap_or(Delay::from_millis(10));
701 let lower_bound = self.lower_bound.unwrap_or(Delay::ZERO);
702 let count = self.count;
703 let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
704 let rng = Rng::seed_from_u64(seed);
705 let uniform: Uniform<f64> =
706 Uniform::new(lower_bound.as_secs_f64(), upper_bound.as_secs_f64()).unwrap();
707 UniformDelayPerPacket {
708 upper_bound,
709 lower_bound,
710 count,
711 current_count: 0,
712 seed,
713 rng,
714 uniform,
715 }
716 }
717}
718
719impl NormalizedDelayPerPacketConfig {
720 /// Creates an uninitialized config
721 pub fn new() -> Self {
722 Self {
723 mean: None,
724 std_dev: None,
725 upper_bound: None,
726 lower_bound: None,
727 count: 0,
728 seed: None,
729 }
730 }
731
732 /// Sets the mean
733 ///
734 /// If the mean is not set, 10ms will be used.
735 pub fn mean(mut self, mean: Delay) -> Self {
736 self.mean = Some(mean);
737 self
738 }
739
740 /// Sets the standard deviation
741 ///
742 /// If the standard deviation is not set, 0ms will be used.
743 pub fn std_dev(mut self, std_dev: Delay) -> Self {
744 self.std_dev = Some(std_dev);
745 self
746 }
747
748 /// Sets the upper bound
749 ///
750 /// If the upper bound is not set, the upper bound will be the one of [`Delay`].
751 pub fn upper_bound(mut self, upper_bound: Delay) -> Self {
752 self.upper_bound = Some(upper_bound);
753 self
754 }
755
756 /// Sets the lower bound
757 ///
758 /// If the lower bound is not set, 0ms will be used.
759 pub fn lower_bound(mut self, lower_bound: Delay) -> Self {
760 self.lower_bound = Some(lower_bound);
761 self
762 }
763
764 /// Sets the number of packets to repeat
765 ///
766 /// If the count is not set, it will be set to 0 (ie, infinite repeat).
767 pub fn count(mut self, count: usize) -> Self {
768 self.count = count;
769 self
770 }
771
772 /// Set the seed for a random generator
773 ///
774 /// If the seed is not set, `42` will be used.
775 pub fn seed(mut self, seed: u64) -> Self {
776 self.seed = Some(seed);
777 self
778 }
779
780 /// Allows to use a randomly generated seed
781 ///
782 /// This is equivalent to: `self.seed(rand::random())`
783 pub fn random_seed(mut self) -> Self {
784 self.seed = Some(rand::random());
785 self
786 }
787
788 /// Creates a new [`NormalizedDelayPerPacket`] corresponding to this config.
789 ///
790 /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
791 /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
792 /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
793 /// to this end one can use [`build_with_rng`](Self::build_with_rng).
794 pub fn build(self) -> NormalizedDelayPerPacket {
795 self.build_with_rng()
796 }
797
798 /// Creates a new [`NormalizedDelayPerPacket`] corresponding to this config.
799 ///
800 /// Unlike [`build`](Self::build), this method let you choose the random generator.
801 ///
802 /// # Example
803 /// ```rust
804 /// # use netem_trace::model::NormalizedDelayPerPacketConfig;
805 /// # use netem_trace::{Delay, DelayPerPacketTrace};
806 /// # use rand::rngs::StdRng;
807 /// # use rand_chacha::ChaCha20Rng;
808 ///
809 /// let normal_delay = NormalizedDelayPerPacketConfig::new()
810 /// .mean(Delay::from_millis(12))
811 /// .std_dev(Delay::from_millis(1))
812 /// .count(3)
813 /// .seed(42);
814 ///
815 /// let mut default_build = normal_delay.clone().build();
816 /// let mut std_build = normal_delay.clone().build_with_rng::<StdRng>();
817 /// // ChaCha is deterministic and portable, unlike StdRng
818 /// let mut chacha_build = normal_delay.clone().build_with_rng::<ChaCha20Rng>();
819 ///
820 /// for cha in [12044676, 11754367, 11253775] {
821 /// let default = default_build.next_delay();
822 /// let std = std_build.next_delay();
823 /// let chacha = chacha_build.next_delay();
824 ///
825 /// assert!(default.is_some());
826 /// assert_eq!(default, std);
827 /// assert_ne!(default, chacha);
828 /// assert_eq!(chacha, Some(Delay::from_nanos(cha)));
829 /// }
830 ///
831 /// assert_eq!(default_build.next_delay(), None);
832 /// assert_eq!(std_build.next_delay(), None);
833 /// assert_eq!(chacha_build.next_delay(), None);
834 /// ```
835 pub fn build_with_rng<Rng: RngCore + SeedableRng>(self) -> NormalizedDelayPerPacket<Rng> {
836 let mean = self.mean.unwrap_or_else(|| Delay::from_millis(10));
837 let std_dev = self.std_dev.unwrap_or(Delay::ZERO);
838 let upper_bound = self.upper_bound;
839 let lower_bound = self.lower_bound.unwrap_or(Delay::ZERO);
840 let count = self.count;
841 let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
842 let rng = Rng::seed_from_u64(seed);
843 let delay_mean = mean.as_secs_f64();
844 let delay_std_dev = std_dev.as_secs_f64();
845 let normal: Normal<f64> = Normal::new(delay_mean, delay_std_dev).unwrap();
846 NormalizedDelayPerPacket {
847 mean,
848 std_dev,
849 upper_bound,
850 lower_bound,
851 count,
852 current_count: 0,
853 seed,
854 rng,
855 normal,
856 }
857 }
858}
859
860#[cfg(feature = "truncated-normal")]
861impl NormalizedDelayPerPacketConfig {
862 /// This is another implementation for converting NormalizedPerPacketConfig into NormalizedDelayPerPacket, where the impact
863 /// of truncation (`lower_bound` and `upper_bound` field) on the mathematical expectation of the distribution
864 /// is taking account by modifying the center of the distribution.
865 ///
866 /// ## Examples
867 ///
868 /// ```
869 ///
870 /// # use netem_trace::model::NormalizedDelayPerPacketConfig;
871 /// # use netem_trace::{Delay, DelayPerPacketTrace};
872 ///
873 /// let normal_delay = NormalizedDelayPerPacketConfig::new()
874 /// .mean(Delay::from_millis(12))
875 /// .std_dev(Delay::from_millis(12))
876 /// .count(1_000_000)
877 /// .seed(42);
878 ///
879 /// let mut default_build = normal_delay.clone().build();
880 /// let mut truncate_build = normal_delay.clone().build_truncated();
881 ///
882 /// fn avg_delay(mut model: impl DelayPerPacketTrace) -> Delay {
883 /// let mut count = 0;
884 /// std::iter::from_fn( move ||{
885 /// model.next_delay()
886 /// }).inspect(|_| count += 1).sum::<Delay>() / count
887 /// }
888 ///
889 /// assert_eq!(avg_delay(default_build), Delay::from_nanos(12998335)); // significantly higher than the expected mean
890 /// assert_eq!(avg_delay(truncate_build), Delay::from_nanos(11998818));
891 ///
892 /// let normal_delay = NormalizedDelayPerPacketConfig::new()
893 /// .mean(Delay::from_millis(12))
894 /// .std_dev(Delay::from_millis(12))
895 /// .lower_bound(Delay::from_millis(8))
896 /// .upper_bound(Delay::from_millis(20))
897 /// .count(1_000_000)
898 /// .seed(42);
899 ///
900 /// let mut default_build = normal_delay.clone().build();
901 /// let mut truncate_build = normal_delay.clone().build_truncated();
902 ///
903 /// assert_eq!(avg_delay(default_build), Delay::from_nanos(13234261)); // significantly higher than the expected mean
904 /// assert_eq!(avg_delay(truncate_build), Delay::from_nanos(11999151));
905 ///
906 /// ```
907 pub fn build_truncated(self) -> NormalizedDelayPerPacket {
908 self.build_truncated_with_rng()
909 }
910
911 /// Similar to [`build_truncated`](Self::build_truncated) but let you choose the random generator.
912 ///
913 /// See [`build`](Self::build) for details about the reason for using another random number generator than [`StdRng`].
914 pub fn build_truncated_with_rng<Rng: SeedableRng + RngCore>(
915 mut self,
916 ) -> NormalizedDelayPerPacket<Rng> {
917 let mean = self
918 .mean
919 .unwrap_or_else(|| Delay::from_millis(12))
920 .as_secs_f64();
921 let sigma = self.std_dev.unwrap_or(Delay::ZERO).as_secs_f64() / mean;
922 let lower = self.lower_bound.unwrap_or(Delay::ZERO).as_secs_f64() / mean;
923 let upper = self.upper_bound.map(|upper| upper.as_secs_f64() / mean);
924 let new_mean = mean * solve(1f64, sigma, Some(lower), upper).unwrap_or(1f64);
925 self.mean = Some(Delay::from_secs_f64(new_mean));
926 self.build_with_rng()
927 }
928}
929
930impl LogNormalizedDelayPerPacketConfig {
931 /// Creates an uninitialized config
932 pub fn new() -> Self {
933 Self {
934 mean: None,
935 std_dev: None,
936 upper_bound: None,
937 lower_bound: None,
938 count: 0,
939 seed: None,
940 }
941 }
942
943 /// Sets the mean
944 ///
945 /// If the mean is not set, 10ms will be used.
946 pub fn mean(mut self, mean: Delay) -> Self {
947 self.mean = Some(mean);
948 self
949 }
950
951 /// Sets the standard deviation
952 ///
953 /// If the standard deviation is not set, 0ms will be used.
954 pub fn std_dev(mut self, std_dev: Delay) -> Self {
955 self.std_dev = Some(std_dev);
956 self
957 }
958
959 /// Sets the upper bound
960 ///
961 /// If the upper bound is not set, the upper bound will be the one of [`Delay`].
962 pub fn upper_bound(mut self, upper_bound: Delay) -> Self {
963 self.upper_bound = Some(upper_bound);
964 self
965 }
966
967 /// Sets the lower bound
968 ///
969 /// If the lower bound is not set, 0ms will be used.
970 pub fn lower_bound(mut self, lower_bound: Delay) -> Self {
971 self.lower_bound = Some(lower_bound);
972 self
973 }
974
975 /// Sets the number of packets to repeat
976 ///
977 /// If the count is not set, it will be set to 0 (ie, infinite repeat).
978 pub fn count(mut self, count: usize) -> Self {
979 self.count = count;
980 self
981 }
982
983 /// Set the seed for a random generator
984 ///
985 /// If the seed is not set, `42` will be used.
986 pub fn seed(mut self, seed: u64) -> Self {
987 self.seed = Some(seed);
988 self
989 }
990
991 /// Allows to use a randomly generated seed
992 ///
993 /// This is equivalent to: `self.seed(rand::random())`
994 pub fn random_seed(mut self) -> Self {
995 self.seed = Some(rand::random());
996 self
997 }
998
999 /// Creates a new [`LogNormalizedDelayPerPacket`] corresponding to this config.
1000 ///
1001 /// The created model will use [`StdRng`] as source of randomness (the call is equivalent to `self.build_with_rng::<StdRng>()`).
1002 /// It should be sufficient for most cases, but [`StdRng`] is not a portable random number generator,
1003 /// so one may want to use a portable random number generator like [`ChaCha`](https://crates.io/crates/rand_chacha),
1004 /// to this end one can use [`build_with_rng`](Self::build_with_rng).
1005 pub fn build(self) -> LogNormalizedDelayPerPacket {
1006 self.build_with_rng()
1007 }
1008
1009 /// Creates a new [`LogNormalizedDelayPerPacket`] corresponding to this config.
1010 ///
1011 /// Unlike [`build`](Self::build), this method let you choose the random generator.
1012 ///
1013 /// # Example
1014 /// ```rust
1015 /// # use netem_trace::model::LogNormalizedDelayPerPacketConfig;
1016 /// # use netem_trace::{Delay, DelayPerPacketTrace};
1017 /// # use rand::rngs::StdRng;
1018 /// # use rand_chacha::ChaCha20Rng;
1019 ///
1020 /// let log_normal_delay = LogNormalizedDelayPerPacketConfig::new()
1021 /// .mean(Delay::from_millis(12))
1022 /// .std_dev(Delay::from_millis(1))
1023 /// .count(3)
1024 /// .seed(42);
1025 ///
1026 /// let mut default_build = log_normal_delay.clone().build();
1027 /// let mut std_build = log_normal_delay.clone().build_with_rng::<StdRng>();
1028 /// // ChaCha is deterministic and portable, unlike StdRng
1029 /// let mut chacha_build = log_normal_delay.clone().build_with_rng::<ChaCha20Rng>();
1030 ///
1031 /// for cha in [12003077, 11716668, 11238761] {
1032 /// let default = default_build.next_delay();
1033 /// let std = std_build.next_delay();
1034 /// let chacha = chacha_build.next_delay();
1035 ///
1036 /// assert!(default.is_some());
1037 /// assert_eq!(default, std);
1038 /// assert_ne!(default, chacha);
1039 /// assert_eq!(chacha, Some(Delay::from_nanos(cha)));
1040 /// }
1041 ///
1042 /// assert_eq!(default_build.next_delay(), None);
1043 /// assert_eq!(std_build.next_delay(), None);
1044 /// assert_eq!(chacha_build.next_delay(), None);
1045 /// ```
1046 pub fn build_with_rng<Rng: SeedableRng + RngCore>(self) -> LogNormalizedDelayPerPacket<Rng> {
1047 let mean = self.mean.unwrap_or_else(|| Delay::from_millis(10));
1048 let std_dev = self.std_dev.unwrap_or(Delay::ZERO);
1049 let upper_bound = self.upper_bound;
1050 let lower_bound = self.lower_bound.unwrap_or(Delay::ZERO);
1051 let count = self.count;
1052 let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
1053 let rng = Rng::seed_from_u64(seed);
1054 let delay_mean = mean.as_secs_f64();
1055 let delay_std_dev = std_dev.as_secs_f64();
1056
1057 // Computing the mean and standard deviation of underlying normal Law
1058 // Because Lognormal(μ , σ²) has a mean of exp(μ + σ²/2) and a standard deviation of sqrt((exp(σ²) - 1) exp(2μ + σ²))
1059 // So we need to comput μ and σ, given the mean and standard deviation of the log-normal law
1060 let normal_std_dev = f64::sqrt(f64::ln(
1061 1.0 + (delay_std_dev.powi(2)) / (delay_mean.powi(2)),
1062 ));
1063 let normal_mean = f64::ln(delay_mean) - normal_std_dev.powi(2) / 2.;
1064 let log_normal: LogNormal<f64> = LogNormal::new(normal_mean, normal_std_dev).unwrap();
1065
1066 LogNormalizedDelayPerPacket {
1067 mean,
1068 std_dev,
1069 upper_bound,
1070 lower_bound,
1071 count,
1072 current_count: 0,
1073 seed,
1074 rng,
1075 log_normal,
1076 }
1077 }
1078}
1079
1080macro_rules! impl_delay_per_packet_trace_config {
1081 ($name:ident) => {
1082 #[cfg_attr(feature = "serde", typetag::serde)]
1083 impl DelayPerPacketTraceConfig for $name {
1084 fn into_model(self: Box<$name>) -> Box<dyn DelayPerPacketTrace> {
1085 Box::new(self.build())
1086 }
1087 }
1088 };
1089}
1090
1091impl_delay_per_packet_trace_config!(StaticDelayPerPacketConfig);
1092impl_delay_per_packet_trace_config!(UniformDelayPerPacketConfig);
1093impl_delay_per_packet_trace_config!(NormalizedDelayPerPacketConfig);
1094impl_delay_per_packet_trace_config!(LogNormalizedDelayPerPacketConfig);
1095impl_delay_per_packet_trace_config!(RepeatedDelayPerPacketPatternConfig);
1096
1097impl<Model: DelayPerPacketTrace + 'static> From<Model> for Box<dyn DelayPerPacketTrace> {
1098 fn from(model: Model) -> Self {
1099 Box::new(model)
1100 }
1101}
1102
1103/// Turn a [`DelayPerPacketTraceConfig`] into a forever repeated [`RepeatedDelayPerPacketPatternConfig`].
1104pub trait Forever: DelayPerPacketTraceConfig {
1105 fn forever(self) -> RepeatedDelayPerPacketPatternConfig;
1106}
1107
1108/// Implement the [`Forever`] trait for the per-packet delay trace model config (any struct implements [`DelayPerPacketTraceConfig`]).
1109#[macro_export]
1110macro_rules! impl_forever_delay_per_packet {
1111 ($name:ident) => {
1112 impl Forever for $name {
1113 fn forever(self) -> RepeatedDelayPerPacketPatternConfig {
1114 RepeatedDelayPerPacketPatternConfig::new()
1115 .pattern(vec![Box::new(self)])
1116 .count(0)
1117 }
1118 }
1119 };
1120}
1121
1122impl_forever_delay_per_packet!(StaticDelayPerPacketConfig);
1123impl_forever_delay_per_packet!(NormalizedDelayPerPacketConfig);
1124
1125impl Forever for RepeatedDelayPerPacketPatternConfig {
1126 fn forever(self) -> RepeatedDelayPerPacketPatternConfig {
1127 self.count(0)
1128 }
1129}
1130
1131#[cfg(test)]
1132mod test {
1133 use super::*;
1134 use crate::model::StaticDelayPerPacketConfig;
1135 use crate::DelayPerPacketTrace;
1136
1137 #[test]
1138 fn test_static_delay_model() {
1139 let mut static_delay = StaticDelayPerPacketConfig::new()
1140 .delay(Delay::from_millis(10))
1141 .count(1)
1142 .build();
1143 assert_eq!(static_delay.next_delay(), Some(Delay::from_millis(10)));
1144 assert_eq!(static_delay.next_delay(), None);
1145 }
1146
1147 #[test]
1148 #[cfg(feature = "serde")]
1149 fn test_serde() {
1150 let a = vec![
1151 Box::new(
1152 StaticDelayPerPacketConfig::new()
1153 .delay(Delay::from_millis(10))
1154 .count(1),
1155 ) as Box<dyn DelayPerPacketTraceConfig>,
1156 Box::new(
1157 StaticDelayPerPacketConfig::new()
1158 .delay(Delay::from_millis(20))
1159 .count(1),
1160 ) as Box<dyn DelayPerPacketTraceConfig>,
1161 ];
1162 let ser = Box::new(
1163 RepeatedDelayPerPacketPatternConfig::new()
1164 .pattern(a)
1165 .count(2),
1166 ) as Box<dyn DelayPerPacketTraceConfig>;
1167 let ser_str = serde_json::to_string(&ser).unwrap();
1168 #[cfg(feature = "human")]
1169 let des_str = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":\"10ms\",\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":\"20ms\",\"count\":1}}],\"count\":2}}";
1170 #[cfg(not(feature = "human"))]
1171 let des_str = "{\"RepeatedDelayPerPacketPatternConfig\":{\"pattern\":[{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"count\":1}},{\"StaticDelayPerPacketConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"count\":1}}],\"count\":2}}";
1172 assert_eq!(ser_str, des_str);
1173 let des: Box<dyn DelayPerPacketTraceConfig> = serde_json::from_str(des_str).unwrap();
1174 let mut model = des.into_model();
1175 assert_eq!(model.next_delay(), Some(Delay::from_millis(10)));
1176 }
1177}