Skip to main content

netem_trace/
lib.rs

1//! This crate provides a set of tools to generate traces for network emulation.
2//!
3//! ## Examples
4//!
5//! If you want to use the pre-defined models, please enable the `model` or `bw-model` feature.
6//!
7//! And if you want read configuration from file, `serde` feature should also be enabled.
8//! We else recommend you to enable `human` feature to make the configuration file more human-readable.
9//!
10//! An example to build model from configuration:
11//!
12//! ```
13//! # use netem_trace::model::StaticBwConfig;
14//! # use netem_trace::{Bandwidth, Duration, BwTrace};
15//! let mut static_bw = StaticBwConfig::new()
16//!     .bw(Bandwidth::from_mbps(24))
17//!     .duration(Duration::from_secs(1))
18//!     .build();
19//! assert_eq!(static_bw.next_bw(), Some((Bandwidth::from_mbps(24), Duration::from_secs(1))));
20//! assert_eq!(static_bw.next_bw(), None);
21//! ```
22//!
23//! A more common use case is to build model from a configuration file (e.g. json file):
24//!
25//! ```
26//! # use netem_trace::model::{StaticBwConfig, BwTraceConfig};
27//! # use netem_trace::{Bandwidth, Duration, BwTrace};
28//! # #[cfg(feature = "human")]
29//! # let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
30//! // The content would be "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
31//! // if the `human` feature is not enabled.
32//! # #[cfg(not(feature = "human"))]
33//! let config_file_content = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
34//! let des: Box<dyn BwTraceConfig> = serde_json::from_str(config_file_content).unwrap();
35//! let mut model = des.into_model();
36//! assert_eq!(
37//!     model.next_bw(),
38//!     Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
39//! );
40//! assert_eq!(
41//!     model.next_bw(),
42//!     Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
43//! );
44//! assert_eq!(
45//!     model.next_bw(),
46//!     Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
47//! );
48//! assert_eq!(
49//!     model.next_bw(),
50//!     Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
51//! );
52//! assert_eq!(model.next_bw(), None);
53//! ```
54//!
55//! ## Make your own model
56//!
57//! Here is an simple example of how to do this. For more complicated examples, please refer to our pre-defined models.
58//!
59//! ```
60//! use netem_trace::BwTrace;
61//! use netem_trace::{Bandwidth, Duration};
62//!
63//! struct MyStaticBw {
64//!    bw: Bandwidth,
65//!    duration: Option<Duration>,
66//! }
67//!
68//! impl BwTrace for MyStaticBw {
69//!     fn next_bw(&mut self) -> Option<(Bandwidth, Duration)> {
70//!         if let Some(duration) = self.duration.take() {
71//!             if duration.is_zero() {
72//!                 None
73//!             } else {
74//!                 Some((self.bw, duration))
75//!             }
76//!         } else {
77//!             None
78//!         }
79//!     }
80//! }
81//! ```
82//!
83//! This is almost the same as how this library implements the [`model::StaticBw`] model.
84//!
85//! ## Features
86//!
87//! ### Model Features
88//!
89//! - `model`: Enable this feature if you want to use all pre-defined models.
90//!     - `bw-model`: Enable this feature if you want to use the pre-defined [`BwTrace`] models.
91//!     - `truncated-normal`: Enable this feature if you want to use truncated normal distribution in [`model::NormalizedBw`] models.
92//!
93//! ### Trace Format Features
94//!
95//! - `mahimahi`: Enable this feature if you want to load or output traces in [mahimahi](https://github.com/ravinet/mahimahi) format.
96//!
97//! ### Trace Extension Features
98//!
99//! - `trace-ext`: Enable this feature to use the series expansion and export functionality for plotting and visualization. See [`series`] module for details.
100//!
101//! ### Other Features
102//!
103//! - `serde`: Enable this features if you want some structs to be serializable/deserializable. Often used with model features.
104//! - `human`: Enable this feature if you want to use human-readable format in configuration files. Often used with model features.
105
106#[cfg(feature = "mahimahi")]
107pub mod mahimahi;
108#[cfg(all(feature = "mahimahi", feature = "pdo-model"))]
109pub use mahimahi::{load_mahimahi_heavy_pdo_trace, MAHIMAHI_PDO_BYTES};
110#[cfg(feature = "mahimahi")]
111pub use mahimahi::{load_mahimahi_trace, Mahimahi, MahimahiExt};
112
113#[cfg(any(
114    feature = "bw-model",
115    feature = "delay-model",
116    feature = "loss-model",
117    feature = "duplicate-model",
118    feature = "rwnd-model",
119    feature = "pdo-model",
120    feature = "model",
121))]
122pub mod model;
123
124#[cfg(feature = "trace-ext")]
125pub mod series;
126
127pub mod peek;
128
129pub use bandwidth::Bandwidth;
130pub use std::time::Duration;
131
132/// The delay describes how long a packet is delayed when going through.
133pub type Delay = std::time::Duration;
134
135/// The loss_pattern describes how the packets are dropped when going through.
136///
137/// The loss_pattern is a sequence of conditional probabilities describing how packets are dropped.
138/// The probability is a f64 between 0 and 1.
139///
140/// The meaning of the loss_pattern sequence is as follows:
141///
142/// - The probability on index 0 describes how likely a packet will be dropped **if the previous packet was not lost**.
143/// - The probability on index 1 describes how likely a packet will be dropped **if the previous packet was lost**.
144/// - The probability on index 2 describes how likely a packet will be dropped **if the previous 2 packet was lost**.
145/// - ...
146///
147/// For example, if the loss_pattern is [0.1, 0.2], and packet 100 is not lost,
148/// then the probability of packet 101 being lost is 0.1.
149///
150/// If the packet 101 is lost, then the probability of packet 102 being lost is 0.2.
151/// If the packet 101 is not lost, then the probability of packet 102 being lost is still 0.1.
152pub type LossPattern = Vec<f64>;
153
154/// The duplicate_pattern describes how the packets are duplicated.
155///
156/// The duplicate_pattern is a sequence of conditional probabilities describing how packets are duplicated.
157/// The probability is a f64 between 0 and 1.
158///
159/// The meaning of the duplicate_pattern sequence is:
160///
161/// - The probability on index 0 describes how likely a packet will be duplicated
162///   **if the previous packet was transmitted normally**.
163/// - The probability on index 1 describes how likely a packet will be duplicated
164///   **if the previous packet was duplicated**.
165/// - ...
166///
167/// For example, if the duplicate_pattern is [0.8, 0.1], and packet 100 is not duplicated, then the
168/// probability of packet 101 being duplicated is 0.8.
169///
170/// If the packet 101 is duplicated, the the probability of packet 102 being duplicated is 0.1; if
171/// the packet 101 is not duplicated, then the probability of packet 102 being duplicated is still 0.8.
172///
173/// If both packet 101 and 102 were duplicated, then the probability of packet 103 being duplicated
174/// is still 0.1, and as long as the packets were duplicated, the probability of the next packet
175/// being duplicated is always the last element - in this case, 0.1.
176pub type DuplicatePattern = Vec<f64>;
177
178/// This is a trait that represents a trace of bandwidths.
179///
180/// The trace is a sequence of `(bandwidth, duration)` pairs.
181/// The bandwidth describes how many bits can be sent per second.
182/// The duration is the time that the bandwidth lasts.
183///
184/// For example, if the sequence is [(1Mbps, 1s), (2Mbps, 2s), (3Mbps, 3s)],
185/// then the bandwidth will be 1Mbps for 1s, then 2Mbps for 2s, then 3Mbps for 3s.
186///
187/// The next_bw function either returns **the next bandwidth and its duration**
188/// in the sequence, or **None** if the trace goes to end.
189pub trait BwTrace: Send {
190    fn next_bw(&mut self) -> Option<(Bandwidth, Duration)>;
191}
192
193/// This is a trait that represents a trace of delays.
194///
195/// The trace is a sequence of `(delay, duration)` pairs.
196/// The delay describes how long a packet is delayed when going through.
197/// The duration is the time that the delay lasts.
198///
199/// For example, if the sequence is [(10ms, 1s), (20ms, 2s), (30ms, 3s)],
200/// then the delay will be 10ms for 1s, then 20ms for 2s, then 30ms for 3s.
201///
202/// The next_delay function either returns **the next delay and its duration**
203/// in the sequence, or **None** if the trace goes to end.
204pub trait DelayTrace: Send {
205    fn next_delay(&mut self) -> Option<(Delay, Duration)>;
206}
207
208/// This is a trait that represents a trace of per-packet delays.
209///
210/// The trace is a sequence of `delay`.
211/// The delay describes how long the packet is delayed when going through.
212///
213/// For example, if the sequence is [10ms, 20ms, 30ms],
214/// then the delay will be 10ms for the first packet, then 20ms for second, then 30ms for third.
215///
216/// The next_delay function either returns **the next delay**
217/// in the sequence, or **None** if the trace goes to end.
218pub trait DelayPerPacketTrace: Send {
219    fn next_delay(&mut self) -> Option<Delay>;
220}
221
222/// This is a trait that represents a trace of loss patterns.
223///
224/// The trace is a sequence of `(loss_pattern, duration)` pairs.
225/// The loss_pattern describes how packets are dropped when going through.
226/// The duration is the time that the loss_pattern lasts.
227///
228/// The next_loss function either returns **the next loss_pattern and its duration**
229/// in the sequence, or **None** if the trace goes to end.
230pub trait LossTrace: Send {
231    fn next_loss(&mut self) -> Option<(LossPattern, Duration)>;
232}
233
234/// This is a trait that represents a trace of duplicate patterns.
235///
236/// The trace is a sequence of `(duplicate_pattern, duration)` pairs.
237/// The duplicate_pattern describes how packets are duplicated when going through.
238/// The duration is the time that the duplicate_pattern lasts.
239///
240/// The next_duplicate function either returns **the next duplicate_pattern and its duration** in
241/// the sequence, or **None** if the trace goes to end.
242pub trait DuplicateTrace: Send {
243    fn next_duplicate(&mut self) -> Option<(DuplicatePattern, Duration)>;
244}
245
246/// A single receive-side decision emitted by a [`RwndTrace`].
247///
248/// The two fields are independent, and between them they say what the receiver
249/// does for this step's duration.
250///
251/// `set_rcv_buf` sizes the receive buffer. On its own it also states that the
252/// application is keeping up with it: the buffer holds that many bytes, the
253/// advertised window is held at that value, and the application drains
254/// continuously so the window's right edge slides forward with the data
255/// received. That models a receiver whose buffer stopped growing -- in-flight
256/// ends up limited by the window, and the window itself stays put.
257///
258/// `app_read_bytes` states the opposite situation: over this step the
259/// application reads exactly that many bytes and then stops. The window is
260/// whatever is left of the buffer once the unread backlog is subtracted, so it
261/// shrinks as data arrives and reaches zero when the buffer fills. That models a
262/// receiver whose application is the bottleneck.
263///
264/// Because the fields are independent, all four combinations are meaningful and
265/// none is a special case:
266///
267/// | `set_rcv_buf` | `app_read_bytes` | meaning |
268/// |---|---|---|
269/// | `Some(n)` | `None` | buffer `n`, window pinned at `n`, application drains continuously |
270/// | `None` | `Some(m)` | read `m` bytes against the standing buffer; window is what is left |
271/// | `Some(n)` | `Some(m)` | resize the buffer to `n`, then read `m` bytes from it |
272/// | `None` | `None` | carry the previous configuration forward for this step |
273///
274/// # Both fields on one step
275///
276/// The two apply in order: the buffer is resized first, and the read is taken
277/// against the new size. Three consequences are worth stating outright, because
278/// they are what distinguishes this from a buffer-only step:
279///
280/// - **The window is not pinned.** The "application keeps up" half of
281///   `set_rcv_buf` belongs to a step that states no read. Once `app_read_bytes`
282///   is present it is the application's behaviour, so the window follows from
283///   `n - unread` and decays as data arrives, exactly as for a read-only step.
284///   A step of `set_rcv_buf: n` and `app_read_bytes: m` is therefore *not*
285///   equivalent to a buffer-only step of `n` followed by a read of `m`.
286/// - **The backlog survives the resize.** Only the capacity changes; bytes
287///   already received and not yet read stay unread. Resizing does not discard,
288///   deliver, or otherwise account for them.
289/// - **The window saturates at zero.** With `unread` bytes outstanding the
290///   window is `n.saturating_sub(unread)`, so resizing to a value at or below
291///   the current backlog advertises a zero window until the application reads
292///   its way back under the new size. This is the intended way to state a
293///   receiver that shrank its buffer while behind.
294#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
295pub struct RwndDecision {
296    /// If `Some`, size the receive buffer to this many bytes and hold the
297    /// advertised window there, with the application draining continuously.
298    pub set_rcv_buf: Option<u64>,
299    /// If `Some`, the application reads exactly this many bytes over this step
300    /// and then stops; the window follows from what is left unread.
301    pub app_read_bytes: Option<u64>,
302}
303
304/// This is a trait that represents a trace of receive-window decisions over time.
305///
306/// The trace is a sequence of `(rwnd_decision, duration)` pairs. The decision
307/// describes what the receiver does -- how large its buffer is and how its
308/// application reads from it -- and the duration is how long that lasts before
309/// the next step applies.
310///
311/// For example, if the sequence is
312/// `[(set_rcv_buf=64KB, 1s), (app_read_bytes=1KB, 2s)]`, then for 1s the
313/// receiver holds a 64KB window and drains it as fast as data arrives, and for
314/// the next 2s its application reads only 1KB, so the window decays from 64KB
315/// as the unread backlog grows.
316///
317/// Each `next_rwnd` call returns **the next decision and its duration** in the
318/// sequence, or **None** when the trace is exhausted. Mirrors the shape of
319/// [`BwTrace`], [`DelayTrace`], and [`LossTrace`].
320pub trait RwndTrace: Send {
321    fn next_rwnd(&mut self) -> Option<(RwndDecision, Duration)>;
322}
323
324/// This is a trait that represents a trace of heavy packet delivery opportunities (PDOs).
325///
326/// The trace is a sequence of `(interval, bytes)` pairs.
327/// The interval is the time since the previous PDO (or since the start of the trace for the first one).
328/// The bytes is how many bytes this PDO can deliver.
329///
330/// There is no concept of bandwidth here. For example, if the sequence is
331/// [(1ms, 1500), (0ms, 1500), (2ms, 3000)], then 3000 bytes can go out at 1ms
332/// and another 3000 bytes at 3ms.
333///
334/// A mahimahi trace (or the heavy PDO trace of CellReplay) can be loaded as a heavy PDO trace,
335/// see `load_mahimahi_heavy_pdo_trace` (needs the `mahimahi` feature).
336///
337/// The next_heavy_pdo function either returns **the next interval and its bytes**
338/// in the sequence, or **None** if the trace goes to end.
339pub trait HeavyPdoTrace: Send {
340    fn next_heavy_pdo(&mut self) -> Option<(Duration, u64)>;
341}
342
343/// A single light PDO record, one line of a CellReplay light PDO (packet train) trace.
344///
345/// - `timestamp` is when this record was measured, from the start of the trace.
346/// - `base_delay` is the delay of the first packet of an active period starting at `timestamp`.
347/// - `opportunities` are the times of the light PDOs, measured from the start of the active
348///   period. They are nondecreasing and usually start from 0.
349///
350/// How many bytes one light PDO can deliver is given by the trace, see [`LightPdoTrace::pdo_size`].
351#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
352#[derive(Debug, Clone, PartialEq, Eq, Default)]
353pub struct LightPdo {
354    #[cfg_attr(
355        all(feature = "serde", feature = "human"),
356        serde(with = "humantime_serde")
357    )]
358    pub timestamp: Duration,
359    #[cfg_attr(
360        all(feature = "serde", feature = "human"),
361        serde(with = "humantime_serde")
362    )]
363    pub base_delay: Delay,
364    #[cfg_attr(
365        all(feature = "serde", feature = "human"),
366        serde(with = "duration_vec_serde")
367    )]
368    pub opportunities: Vec<Duration>,
369}
370
371/// This is a trait that represents a trace of light PDO records.
372///
373/// The trace is a sequence of [`LightPdo`], with nondecreasing timestamps.
374/// Unlike other traces, the records are not consumed one after another in time:
375/// the user (e.g. a cell in an emulator) picks the record matching the moment
376/// the link goes from inactive to active.
377///
378/// The next_light_pdo function either returns **the next record**
379/// in the sequence, or **None** if the trace goes to end.
380///
381/// The pdo_size function returns how many bytes one light PDO can deliver.
382/// It is part of the trace, so the user doesn't need to know where the trace came from.
383pub trait LightPdoTrace: Send {
384    fn next_light_pdo(&mut self) -> Option<LightPdo>;
385    fn pdo_size(&self) -> u64;
386}
387
388#[cfg(all(feature = "serde", feature = "human"))]
389mod duration_vec_serde {
390    use serde::{Deserialize, Deserializer, Serialize, Serializer};
391    use std::time::Duration;
392
393    pub fn serialize<S: Serializer>(v: &[Duration], serializer: S) -> Result<S::Ok, S::Error> {
394        v.iter()
395            .map(humantime_serde::Serde::from)
396            .collect::<Vec<_>>()
397            .serialize(serializer)
398    }
399
400    pub fn deserialize<'de, D: Deserializer<'de>>(
401        deserializer: D,
402    ) -> Result<Vec<Duration>, D::Error> {
403        let v: Vec<humantime_serde::Serde<Duration>> = Vec::deserialize(deserializer)?;
404        Ok(v.into_iter().map(|d| d.into_inner()).collect())
405    }
406}
407
408#[cfg(test)]
409mod test {
410    use model::TraceBwConfig;
411
412    use self::model::bw::Forever;
413
414    use super::*;
415    #[cfg(feature = "serde")]
416    use crate::model::RepeatedBwPatternConfig;
417    use crate::model::{BwTraceConfig, NormalizedBwConfig, SawtoothBwConfig, StaticBwConfig};
418
419    #[test]
420    fn test_static_bw_model() {
421        let mut static_bw = StaticBwConfig::new()
422            .bw(Bandwidth::from_mbps(24))
423            .duration(Duration::from_secs(1))
424            .build();
425        assert_eq!(
426            static_bw.next_bw(),
427            Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
428        );
429    }
430
431    #[test]
432    fn test_normalized_bw_model() {
433        let mut normal_bw = NormalizedBwConfig::new()
434            .mean(Bandwidth::from_mbps(12))
435            .std_dev(Bandwidth::from_mbps(1))
436            .duration(Duration::from_secs(1))
437            .step(Duration::from_millis(100))
438            .seed(42)
439            .build();
440        assert_eq!(
441            normal_bw.next_bw(),
442            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
443        );
444        assert_eq!(
445            normal_bw.next_bw(),
446            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
447        );
448        let mut normal_bw = NormalizedBwConfig::new()
449            .mean(Bandwidth::from_mbps(12))
450            .std_dev(Bandwidth::from_mbps(1))
451            .duration(Duration::from_secs(1))
452            .step(Duration::from_millis(100))
453            .seed(42)
454            .upper_bound(Bandwidth::from_kbps(12100))
455            .lower_bound(Bandwidth::from_kbps(11900))
456            .build();
457        assert_eq!(
458            normal_bw.next_bw(),
459            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
460        );
461        assert_eq!(
462            normal_bw.next_bw(),
463            Some((Bandwidth::from_bps(12100000), Duration::from_millis(100)))
464        );
465    }
466
467    #[test]
468    fn test_sawtooth_bw_model() {
469        let mut sawtooth_bw = SawtoothBwConfig::new()
470            .bottom(Bandwidth::from_mbps(12))
471            .top(Bandwidth::from_mbps(16))
472            .duration(Duration::from_secs(1))
473            .step(Duration::from_millis(100))
474            .interval(Duration::from_millis(500))
475            .duty_ratio(0.8)
476            .build();
477        assert_eq!(
478            sawtooth_bw.next_bw(),
479            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
480        );
481        assert_eq!(
482            sawtooth_bw.next_bw(),
483            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
484        );
485        assert_eq!(
486            sawtooth_bw.next_bw(),
487            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
488        );
489        assert_eq!(
490            sawtooth_bw.next_bw(),
491            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
492        );
493        assert_eq!(
494            sawtooth_bw.next_bw(),
495            Some((Bandwidth::from_mbps(16), Duration::from_millis(100)))
496        );
497        assert_eq!(
498            sawtooth_bw.next_bw(),
499            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
500        );
501        assert_eq!(
502            sawtooth_bw.next_bw(),
503            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
504        );
505        assert_eq!(
506            sawtooth_bw.next_bw(),
507            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
508        );
509        assert_eq!(
510            sawtooth_bw.next_bw(),
511            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
512        );
513        let mut sawtooth_bw = SawtoothBwConfig::new()
514            .bottom(Bandwidth::from_mbps(12))
515            .top(Bandwidth::from_mbps(16))
516            .duration(Duration::from_secs(1))
517            .step(Duration::from_millis(100))
518            .interval(Duration::from_millis(500))
519            .duty_ratio(0.8)
520            .std_dev(Bandwidth::from_mbps(5))
521            .upper_noise_bound(Bandwidth::from_mbps(1))
522            .lower_noise_bound(Bandwidth::from_kbps(500))
523            .build();
524        assert_eq!(
525            sawtooth_bw.next_bw(),
526            Some((Bandwidth::from_bps(12347139), Duration::from_millis(100)))
527        );
528        assert_eq!(
529            sawtooth_bw.next_bw(),
530            Some((Bandwidth::from_bps(13664690), Duration::from_millis(100)))
531        );
532        assert_eq!(
533            sawtooth_bw.next_bw(),
534            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
535        );
536        assert_eq!(
537            sawtooth_bw.next_bw(),
538            Some((Bandwidth::from_bps(14500000), Duration::from_millis(100)))
539        );
540    }
541
542    #[test]
543    fn test_trace_bw() {
544        let mut trace_bw = TraceBwConfig::new()
545            .pattern(vec![
546                (
547                    Duration::from_millis(1),
548                    vec![
549                        Bandwidth::from_kbps(29123),
550                        Bandwidth::from_kbps(41242),
551                        Bandwidth::from_kbps(7395),
552                    ],
553                ),
554                (
555                    Duration::from_millis(2),
556                    vec![Bandwidth::from_mbps(1), Bandwidth::from_kbps(8542)],
557                ),
558            ])
559            .build();
560
561        assert_eq!(
562            trace_bw.next_bw(),
563            Some((Bandwidth::from_bps(29123000), Duration::from_millis(1)))
564        );
565        assert_eq!(
566            trace_bw.next_bw(),
567            Some((Bandwidth::from_bps(41242000), Duration::from_millis(1)))
568        );
569        assert_eq!(
570            trace_bw.next_bw(),
571            Some((Bandwidth::from_bps(7395000), Duration::from_millis(1)))
572        );
573        assert_eq!(
574            trace_bw.next_bw(),
575            Some((Bandwidth::from_bps(1000000), Duration::from_millis(2)))
576        );
577        assert_eq!(
578            trace_bw.next_bw(),
579            Some((Bandwidth::from_bps(8542000), Duration::from_millis(2)))
580        );
581        assert_eq!(trace_bw.next_bw(), None);
582    }
583
584    #[test]
585    #[cfg(feature = "serde")]
586    fn test_model_serde() {
587        let a = vec![
588            Box::new(
589                StaticBwConfig::new()
590                    .bw(Bandwidth::from_mbps(12))
591                    .duration(Duration::from_secs(1)),
592            ) as Box<dyn BwTraceConfig>,
593            Box::new(
594                StaticBwConfig::new()
595                    .bw(Bandwidth::from_mbps(24))
596                    .duration(Duration::from_secs(1)),
597            ) as Box<dyn BwTraceConfig>,
598        ];
599        let ser =
600            Box::new(RepeatedBwPatternConfig::new().pattern(a).count(2)) as Box<dyn BwTraceConfig>;
601        let ser_str = serde_json::to_string(&ser).unwrap();
602        #[cfg(not(feature = "human"))]
603        let des_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":12000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticBwConfig\":{\"bw\":{\"gbps\":0,\"bps\":24000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
604        #[cfg(feature = "human")]
605        let des_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
606        assert_eq!(ser_str, des_str);
607        let des: Box<dyn BwTraceConfig> = serde_json::from_str(des_str).unwrap();
608        let mut model = des.into_model();
609        assert_eq!(
610            model.next_bw(),
611            Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
612        );
613    }
614
615    #[test]
616    fn test_forever() {
617        let mut normal_bw = NormalizedBwConfig::new()
618            .mean(Bandwidth::from_mbps(12))
619            .std_dev(Bandwidth::from_mbps(1))
620            .duration(Duration::from_millis(200))
621            .step(Duration::from_millis(100))
622            .seed(42)
623            .build();
624        assert_eq!(
625            normal_bw.next_bw(),
626            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
627        );
628        assert_eq!(
629            normal_bw.next_bw(),
630            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
631        );
632        assert_eq!(normal_bw.next_bw(), None);
633        let normal_bw_config = NormalizedBwConfig::new()
634            .mean(Bandwidth::from_mbps(12))
635            .std_dev(Bandwidth::from_mbps(1))
636            .duration(Duration::from_millis(200))
637            .step(Duration::from_millis(100))
638            .seed(42);
639        let normal_bw_repeated = normal_bw_config.forever();
640        let mut model = Box::new(normal_bw_repeated).into_model();
641        assert_eq!(
642            model.next_bw(),
643            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
644        );
645        assert_eq!(
646            model.next_bw(),
647            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
648        );
649        assert_eq!(
650            model.next_bw(),
651            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
652        );
653        assert_eq!(
654            model.next_bw(),
655            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
656        );
657    }
658
659    #[test]
660    #[cfg(feature = "human")]
661    fn test_compatibility_with_figment() {
662        use figment::{
663            providers::{Format, Json},
664            Figment,
665        };
666
667        let config = r##"
668{
669   "RepeatedBwPatternConfig":{
670      "pattern":[
671         {
672            "TraceBwConfig":{
673               "pattern":[
674                  [
675                     "25ms",
676                     [
677                        "10Mbps",
678                        "20Mbps"
679                     ]
680                  ],
681                  [
682                     "2ms",
683                     [
684                        "11Mbps",
685                        "23Mbps"
686                     ]
687                  ]
688               ]
689            }
690         },
691         {
692            "SawtoothBwConfig":{
693               "bottom":"10Mbps",
694               "top":"20Mbps",
695               "step":"1ms",
696               "interval":"10ms",
697               "duty_ratio":0.5
698            }
699         }
700      ],
701      "count":0
702   }
703}"##;
704
705        let trace: Box<dyn BwTraceConfig> = Figment::new()
706            .merge(Json::string(config))
707            .extract()
708            .unwrap();
709
710        let mut model = trace.into_model();
711
712        assert_eq!(
713            model.next_bw(),
714            Some((Bandwidth::from_mbps(10), Duration::from_millis(25)))
715        );
716        assert_eq!(
717            model.next_bw(),
718            Some((Bandwidth::from_mbps(20), Duration::from_millis(25)))
719        );
720        assert_eq!(
721            model.next_bw(),
722            Some((Bandwidth::from_mbps(11), Duration::from_millis(2)))
723        );
724        assert_eq!(
725            model.next_bw(),
726            Some((Bandwidth::from_mbps(23), Duration::from_millis(2)))
727        );
728        assert_eq!(
729            model.next_bw(),
730            Some((Bandwidth::from_mbps(10), Duration::from_millis(1)))
731        );
732        assert_eq!(
733            model.next_bw(),
734            Some((Bandwidth::from_mbps(12), Duration::from_millis(1)))
735        );
736        assert_eq!(
737            model.next_bw(),
738            Some((Bandwidth::from_mbps(14), Duration::from_millis(1)))
739        );
740        assert_eq!(
741            model.next_bw(),
742            Some((Bandwidth::from_mbps(16), Duration::from_millis(1)))
743        );
744        assert_eq!(
745            model.next_bw(),
746            Some((Bandwidth::from_mbps(18), Duration::from_millis(1)))
747        );
748    }
749}