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(feature = "mahimahi")]
109pub use mahimahi::{load_mahimahi_trace, Mahimahi, MahimahiExt};
110
111#[cfg(any(
112    feature = "bw-model",
113    feature = "delay-model",
114    feature = "loss-model",
115    feature = "duplicate-model",
116    feature = "rwnd-model",
117    feature = "model",
118))]
119pub mod model;
120
121#[cfg(feature = "trace-ext")]
122pub mod series;
123
124pub use bandwidth::Bandwidth;
125pub use std::time::Duration;
126
127/// The delay describes how long a packet is delayed when going through.
128pub type Delay = std::time::Duration;
129
130/// The loss_pattern describes how the packets are dropped when going through.
131///
132/// The loss_pattern is a sequence of conditional probabilities describing how packets are dropped.
133/// The probability is a f64 between 0 and 1.
134///
135/// The meaning of the loss_pattern sequence is as follows:
136///
137/// - The probability on index 0 describes how likely a packet will be dropped **if the previous packet was not lost**.
138/// - The probability on index 1 describes how likely a packet will be dropped **if the previous packet was lost**.
139/// - The probability on index 2 describes how likely a packet will be dropped **if the previous 2 packet was lost**.
140/// - ...
141///
142/// For example, if the loss_pattern is [0.1, 0.2], and packet 100 is not lost,
143/// then the probability of packet 101 being lost is 0.1.
144///
145/// If the packet 101 is lost, then the probability of packet 102 being lost is 0.2.
146/// If the packet 101 is not lost, then the probability of packet 102 being lost is still 0.1.
147pub type LossPattern = Vec<f64>;
148
149/// The duplicate_pattern describes how the packets are duplicated.
150///
151/// The duplicate_pattern is a sequence of conditional probabilities describing how packets are duplicated.
152/// The probability is a f64 between 0 and 1.
153///
154/// The meaning of the duplicate_pattern sequence is:
155///
156/// - The probability on index 0 describes how likely a packet will be duplicated
157///   **if the previous packet was transmitted normally**.
158/// - The probability on index 1 describes how likely a packet will be duplicated
159///   **if the previous packet was duplicated**.
160/// - ...
161///
162/// For example, if the duplicate_pattern is [0.8, 0.1], and packet 100 is not duplicated, then the
163/// probability of packet 101 being duplicated is 0.8.
164///
165/// If the packet 101 is duplicated, the the probability of packet 102 being duplicated is 0.1; if
166/// the packet 101 is not duplicated, then the probability of packet 102 being duplicated is still 0.8.
167///
168/// If both packet 101 and 102 were duplicated, then the probability of packet 103 being duplicated
169/// is still 0.1, and as long as the packets were duplicated, the probability of the next packet
170/// being duplicated is always the last element - in this case, 0.1.
171pub type DuplicatePattern = Vec<f64>;
172
173/// This is a trait that represents a trace of bandwidths.
174///
175/// The trace is a sequence of `(bandwidth, duration)` pairs.
176/// The bandwidth describes how many bits can be sent per second.
177/// The duration is the time that the bandwidth lasts.
178///
179/// For example, if the sequence is [(1Mbps, 1s), (2Mbps, 2s), (3Mbps, 3s)],
180/// then the bandwidth will be 1Mbps for 1s, then 2Mbps for 2s, then 3Mbps for 3s.
181///
182/// The next_bw function either returns **the next bandwidth and its duration**
183/// in the sequence, or **None** if the trace goes to end.
184pub trait BwTrace: Send {
185    fn next_bw(&mut self) -> Option<(Bandwidth, Duration)>;
186}
187
188/// This is a trait that represents a trace of delays.
189///
190/// The trace is a sequence of `(delay, duration)` pairs.
191/// The delay describes how long a packet is delayed when going through.
192/// The duration is the time that the delay lasts.
193///
194/// For example, if the sequence is [(10ms, 1s), (20ms, 2s), (30ms, 3s)],
195/// then the delay will be 10ms for 1s, then 20ms for 2s, then 30ms for 3s.
196///
197/// The next_delay function either returns **the next delay and its duration**
198/// in the sequence, or **None** if the trace goes to end.
199pub trait DelayTrace: Send {
200    fn next_delay(&mut self) -> Option<(Delay, Duration)>;
201}
202
203/// This is a trait that represents a trace of per-packet delays.
204///
205/// The trace is a sequence of `delay`.
206/// The delay describes how long the packet is delayed when going through.
207///
208/// For example, if the sequence is [10ms, 20ms, 30ms],
209/// then the delay will be 10ms for the first packet, then 20ms for second, then 30ms for third.
210///
211/// The next_delay function either returns **the next delay**
212/// in the sequence, or **None** if the trace goes to end.
213pub trait DelayPerPacketTrace: Send {
214    fn next_delay(&mut self) -> Option<Delay>;
215}
216
217/// This is a trait that represents a trace of loss patterns.
218///
219/// The trace is a sequence of `(loss_pattern, duration)` pairs.
220/// The loss_pattern describes how packets are dropped when going through.
221/// The duration is the time that the loss_pattern lasts.
222///
223/// The next_loss function either returns **the next loss_pattern and its duration**
224/// in the sequence, or **None** if the trace goes to end.
225pub trait LossTrace: Send {
226    fn next_loss(&mut self) -> Option<(LossPattern, Duration)>;
227}
228
229/// This is a trait that represents a trace of duplicate patterns.
230///
231/// The trace is a sequence of `(duplicate_pattern, duration)` pairs.
232/// The duplicate_pattern describes how packets are duplicated when going through.
233/// The duration is the time that the duplicate_pattern lasts.
234///
235/// The next_duplicate function either returns **the next duplicate_pattern and its duration** in
236/// the sequence, or **None** if the trace goes to end.
237pub trait DuplicateTrace: Send {
238    fn next_duplicate(&mut self) -> Option<(DuplicatePattern, Duration)>;
239}
240
241/// A single receive-side decision emitted by a [`RwndTrace`].
242///
243/// The two fields are independent, and between them they say what the receiver
244/// does for this step's duration.
245///
246/// `set_rcv_buf` sizes the receive buffer. On its own it also states that the
247/// application is keeping up with it: the buffer holds that many bytes, the
248/// advertised window is held at that value, and the application drains
249/// continuously so the window's right edge slides forward with the data
250/// received. That models a receiver whose buffer stopped growing -- in-flight
251/// ends up limited by the window, and the window itself stays put.
252///
253/// `app_read_bytes` states the opposite situation: over this step the
254/// application reads exactly that many bytes and then stops. The window is
255/// whatever is left of the buffer once the unread backlog is subtracted, so it
256/// shrinks as data arrives and reaches zero when the buffer fills. That models a
257/// receiver whose application is the bottleneck.
258///
259/// Because the fields are independent, all four combinations are meaningful and
260/// none is a special case:
261///
262/// | `set_rcv_buf` | `app_read_bytes` | meaning |
263/// |---|---|---|
264/// | `Some(n)` | `None` | buffer `n`, window pinned at `n`, application drains continuously |
265/// | `None` | `Some(m)` | read `m` bytes against the standing buffer; window is what is left |
266/// | `Some(n)` | `Some(m)` | resize the buffer to `n`, then read `m` bytes from it |
267/// | `None` | `None` | carry the previous configuration forward for this step |
268///
269/// # Both fields on one step
270///
271/// The two apply in order: the buffer is resized first, and the read is taken
272/// against the new size. Three consequences are worth stating outright, because
273/// they are what distinguishes this from a buffer-only step:
274///
275/// - **The window is not pinned.** The "application keeps up" half of
276///   `set_rcv_buf` belongs to a step that states no read. Once `app_read_bytes`
277///   is present it is the application's behaviour, so the window follows from
278///   `n - unread` and decays as data arrives, exactly as for a read-only step.
279///   A step of `set_rcv_buf: n` and `app_read_bytes: m` is therefore *not*
280///   equivalent to a buffer-only step of `n` followed by a read of `m`.
281/// - **The backlog survives the resize.** Only the capacity changes; bytes
282///   already received and not yet read stay unread. Resizing does not discard,
283///   deliver, or otherwise account for them.
284/// - **The window saturates at zero.** With `unread` bytes outstanding the
285///   window is `n.saturating_sub(unread)`, so resizing to a value at or below
286///   the current backlog advertises a zero window until the application reads
287///   its way back under the new size. This is the intended way to state a
288///   receiver that shrank its buffer while behind.
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
290pub struct RwndDecision {
291    /// If `Some`, size the receive buffer to this many bytes and hold the
292    /// advertised window there, with the application draining continuously.
293    pub set_rcv_buf: Option<u64>,
294    /// If `Some`, the application reads exactly this many bytes over this step
295    /// and then stops; the window follows from what is left unread.
296    pub app_read_bytes: Option<u64>,
297}
298
299/// This is a trait that represents a trace of receive-window decisions over time.
300///
301/// The trace is a sequence of `(rwnd_decision, duration)` pairs. The decision
302/// describes what the receiver does -- how large its buffer is and how its
303/// application reads from it -- and the duration is how long that lasts before
304/// the next step applies.
305///
306/// For example, if the sequence is
307/// `[(set_rcv_buf=64KB, 1s), (app_read_bytes=1KB, 2s)]`, then for 1s the
308/// receiver holds a 64KB window and drains it as fast as data arrives, and for
309/// the next 2s its application reads only 1KB, so the window decays from 64KB
310/// as the unread backlog grows.
311///
312/// Each `next_rwnd` call returns **the next decision and its duration** in the
313/// sequence, or **None** when the trace is exhausted. Mirrors the shape of
314/// [`BwTrace`], [`DelayTrace`], and [`LossTrace`].
315pub trait RwndTrace: Send {
316    fn next_rwnd(&mut self) -> Option<(RwndDecision, Duration)>;
317}
318
319#[cfg(test)]
320mod test {
321    use model::TraceBwConfig;
322
323    use self::model::bw::Forever;
324
325    use super::*;
326    #[cfg(feature = "serde")]
327    use crate::model::RepeatedBwPatternConfig;
328    use crate::model::{BwTraceConfig, NormalizedBwConfig, SawtoothBwConfig, StaticBwConfig};
329
330    #[test]
331    fn test_static_bw_model() {
332        let mut static_bw = StaticBwConfig::new()
333            .bw(Bandwidth::from_mbps(24))
334            .duration(Duration::from_secs(1))
335            .build();
336        assert_eq!(
337            static_bw.next_bw(),
338            Some((Bandwidth::from_mbps(24), Duration::from_secs(1)))
339        );
340    }
341
342    #[test]
343    fn test_normalized_bw_model() {
344        let mut normal_bw = NormalizedBwConfig::new()
345            .mean(Bandwidth::from_mbps(12))
346            .std_dev(Bandwidth::from_mbps(1))
347            .duration(Duration::from_secs(1))
348            .step(Duration::from_millis(100))
349            .seed(42)
350            .build();
351        assert_eq!(
352            normal_bw.next_bw(),
353            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
354        );
355        assert_eq!(
356            normal_bw.next_bw(),
357            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
358        );
359        let mut normal_bw = NormalizedBwConfig::new()
360            .mean(Bandwidth::from_mbps(12))
361            .std_dev(Bandwidth::from_mbps(1))
362            .duration(Duration::from_secs(1))
363            .step(Duration::from_millis(100))
364            .seed(42)
365            .upper_bound(Bandwidth::from_kbps(12100))
366            .lower_bound(Bandwidth::from_kbps(11900))
367            .build();
368        assert_eq!(
369            normal_bw.next_bw(),
370            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
371        );
372        assert_eq!(
373            normal_bw.next_bw(),
374            Some((Bandwidth::from_bps(12100000), Duration::from_millis(100)))
375        );
376    }
377
378    #[test]
379    fn test_sawtooth_bw_model() {
380        let mut sawtooth_bw = SawtoothBwConfig::new()
381            .bottom(Bandwidth::from_mbps(12))
382            .top(Bandwidth::from_mbps(16))
383            .duration(Duration::from_secs(1))
384            .step(Duration::from_millis(100))
385            .interval(Duration::from_millis(500))
386            .duty_ratio(0.8)
387            .build();
388        assert_eq!(
389            sawtooth_bw.next_bw(),
390            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
391        );
392        assert_eq!(
393            sawtooth_bw.next_bw(),
394            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
395        );
396        assert_eq!(
397            sawtooth_bw.next_bw(),
398            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
399        );
400        assert_eq!(
401            sawtooth_bw.next_bw(),
402            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
403        );
404        assert_eq!(
405            sawtooth_bw.next_bw(),
406            Some((Bandwidth::from_mbps(16), Duration::from_millis(100)))
407        );
408        assert_eq!(
409            sawtooth_bw.next_bw(),
410            Some((Bandwidth::from_mbps(12), Duration::from_millis(100)))
411        );
412        assert_eq!(
413            sawtooth_bw.next_bw(),
414            Some((Bandwidth::from_mbps(13), Duration::from_millis(100)))
415        );
416        assert_eq!(
417            sawtooth_bw.next_bw(),
418            Some((Bandwidth::from_mbps(14), Duration::from_millis(100)))
419        );
420        assert_eq!(
421            sawtooth_bw.next_bw(),
422            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
423        );
424        let mut sawtooth_bw = SawtoothBwConfig::new()
425            .bottom(Bandwidth::from_mbps(12))
426            .top(Bandwidth::from_mbps(16))
427            .duration(Duration::from_secs(1))
428            .step(Duration::from_millis(100))
429            .interval(Duration::from_millis(500))
430            .duty_ratio(0.8)
431            .std_dev(Bandwidth::from_mbps(5))
432            .upper_noise_bound(Bandwidth::from_mbps(1))
433            .lower_noise_bound(Bandwidth::from_kbps(500))
434            .build();
435        assert_eq!(
436            sawtooth_bw.next_bw(),
437            Some((Bandwidth::from_bps(12347139), Duration::from_millis(100)))
438        );
439        assert_eq!(
440            sawtooth_bw.next_bw(),
441            Some((Bandwidth::from_bps(13664690), Duration::from_millis(100)))
442        );
443        assert_eq!(
444            sawtooth_bw.next_bw(),
445            Some((Bandwidth::from_mbps(15), Duration::from_millis(100)))
446        );
447        assert_eq!(
448            sawtooth_bw.next_bw(),
449            Some((Bandwidth::from_bps(14500000), Duration::from_millis(100)))
450        );
451    }
452
453    #[test]
454    fn test_trace_bw() {
455        let mut trace_bw = TraceBwConfig::new()
456            .pattern(vec![
457                (
458                    Duration::from_millis(1),
459                    vec![
460                        Bandwidth::from_kbps(29123),
461                        Bandwidth::from_kbps(41242),
462                        Bandwidth::from_kbps(7395),
463                    ],
464                ),
465                (
466                    Duration::from_millis(2),
467                    vec![Bandwidth::from_mbps(1), Bandwidth::from_kbps(8542)],
468                ),
469            ])
470            .build();
471
472        assert_eq!(
473            trace_bw.next_bw(),
474            Some((Bandwidth::from_bps(29123000), Duration::from_millis(1)))
475        );
476        assert_eq!(
477            trace_bw.next_bw(),
478            Some((Bandwidth::from_bps(41242000), Duration::from_millis(1)))
479        );
480        assert_eq!(
481            trace_bw.next_bw(),
482            Some((Bandwidth::from_bps(7395000), Duration::from_millis(1)))
483        );
484        assert_eq!(
485            trace_bw.next_bw(),
486            Some((Bandwidth::from_bps(1000000), Duration::from_millis(2)))
487        );
488        assert_eq!(
489            trace_bw.next_bw(),
490            Some((Bandwidth::from_bps(8542000), Duration::from_millis(2)))
491        );
492        assert_eq!(trace_bw.next_bw(), None);
493    }
494
495    #[test]
496    #[cfg(feature = "serde")]
497    fn test_model_serde() {
498        let a = vec![
499            Box::new(
500                StaticBwConfig::new()
501                    .bw(Bandwidth::from_mbps(12))
502                    .duration(Duration::from_secs(1)),
503            ) as Box<dyn BwTraceConfig>,
504            Box::new(
505                StaticBwConfig::new()
506                    .bw(Bandwidth::from_mbps(24))
507                    .duration(Duration::from_secs(1)),
508            ) as Box<dyn BwTraceConfig>,
509        ];
510        let ser =
511            Box::new(RepeatedBwPatternConfig::new().pattern(a).count(2)) as Box<dyn BwTraceConfig>;
512        let ser_str = serde_json::to_string(&ser).unwrap();
513        #[cfg(not(feature = "human"))]
514        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}}";
515        #[cfg(feature = "human")]
516        let des_str = "{\"RepeatedBwPatternConfig\":{\"pattern\":[{\"StaticBwConfig\":{\"bw\":\"12Mbps\",\"duration\":\"1s\"}},{\"StaticBwConfig\":{\"bw\":\"24Mbps\",\"duration\":\"1s\"}}],\"count\":2}}";
517        assert_eq!(ser_str, des_str);
518        let des: Box<dyn BwTraceConfig> = serde_json::from_str(des_str).unwrap();
519        let mut model = des.into_model();
520        assert_eq!(
521            model.next_bw(),
522            Some((Bandwidth::from_mbps(12), Duration::from_secs(1)))
523        );
524    }
525
526    #[test]
527    fn test_forever() {
528        let mut normal_bw = NormalizedBwConfig::new()
529            .mean(Bandwidth::from_mbps(12))
530            .std_dev(Bandwidth::from_mbps(1))
531            .duration(Duration::from_millis(200))
532            .step(Duration::from_millis(100))
533            .seed(42)
534            .build();
535        assert_eq!(
536            normal_bw.next_bw(),
537            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
538        );
539        assert_eq!(
540            normal_bw.next_bw(),
541            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
542        );
543        assert_eq!(normal_bw.next_bw(), None);
544        let normal_bw_config = NormalizedBwConfig::new()
545            .mean(Bandwidth::from_mbps(12))
546            .std_dev(Bandwidth::from_mbps(1))
547            .duration(Duration::from_millis(200))
548            .step(Duration::from_millis(100))
549            .seed(42);
550        let normal_bw_repeated = normal_bw_config.forever();
551        let mut model = Box::new(normal_bw_repeated).into_model();
552        assert_eq!(
553            model.next_bw(),
554            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
555        );
556        assert_eq!(
557            model.next_bw(),
558            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
559        );
560        assert_eq!(
561            model.next_bw(),
562            Some((Bandwidth::from_bps(12069427), Duration::from_millis(100)))
563        );
564        assert_eq!(
565            model.next_bw(),
566            Some((Bandwidth::from_bps(12132938), Duration::from_millis(100)))
567        );
568    }
569
570    #[test]
571    #[cfg(feature = "human")]
572    fn test_compatibility_with_figment() {
573        use figment::{
574            providers::{Format, Json},
575            Figment,
576        };
577
578        let config = r##"
579{
580   "RepeatedBwPatternConfig":{
581      "pattern":[
582         {
583            "TraceBwConfig":{
584               "pattern":[
585                  [
586                     "25ms",
587                     [
588                        "10Mbps",
589                        "20Mbps"
590                     ]
591                  ],
592                  [
593                     "2ms",
594                     [
595                        "11Mbps",
596                        "23Mbps"
597                     ]
598                  ]
599               ]
600            }
601         },
602         {
603            "SawtoothBwConfig":{
604               "bottom":"10Mbps",
605               "top":"20Mbps",
606               "step":"1ms",
607               "interval":"10ms",
608               "duty_ratio":0.5
609            }
610         }
611      ],
612      "count":0
613   }
614}"##;
615
616        let trace: Box<dyn BwTraceConfig> = Figment::new()
617            .merge(Json::string(config))
618            .extract()
619            .unwrap();
620
621        let mut model = trace.into_model();
622
623        assert_eq!(
624            model.next_bw(),
625            Some((Bandwidth::from_mbps(10), Duration::from_millis(25)))
626        );
627        assert_eq!(
628            model.next_bw(),
629            Some((Bandwidth::from_mbps(20), Duration::from_millis(25)))
630        );
631        assert_eq!(
632            model.next_bw(),
633            Some((Bandwidth::from_mbps(11), Duration::from_millis(2)))
634        );
635        assert_eq!(
636            model.next_bw(),
637            Some((Bandwidth::from_mbps(23), Duration::from_millis(2)))
638        );
639        assert_eq!(
640            model.next_bw(),
641            Some((Bandwidth::from_mbps(10), Duration::from_millis(1)))
642        );
643        assert_eq!(
644            model.next_bw(),
645            Some((Bandwidth::from_mbps(12), Duration::from_millis(1)))
646        );
647        assert_eq!(
648            model.next_bw(),
649            Some((Bandwidth::from_mbps(14), Duration::from_millis(1)))
650        );
651        assert_eq!(
652            model.next_bw(),
653            Some((Bandwidth::from_mbps(16), Duration::from_millis(1)))
654        );
655        assert_eq!(
656            model.next_bw(),
657            Some((Bandwidth::from_mbps(18), Duration::from_millis(1)))
658        );
659    }
660}