proptest/test_runner/
rng.rs

1//-
2// Copyright 2017, 2018, 2019, 2020 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::std_facade::{Arc, String, ToOwned, Vec};
11use core::result::Result;
12use core::{fmt, str, u8, convert::TryInto};
13use crate::test_runner::{config, RngSeed};
14use rand::{self, Rng, RngCore, SeedableRng};
15use rand_chacha::ChaChaRng;
16use rand_xorshift::XorShiftRng;
17
18/// Identifies a particular RNG algorithm supported by proptest.
19///
20/// Proptest supports dynamic configuration of algorithms to allow it to
21/// continue operating with persisted regression files and to allow the
22/// configuration to be expressed in the `Config` struct.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum RngAlgorithm {
25    /// The [XorShift](https://rust-random.github.io/rand/rand_xorshift/struct.XorShiftRng.html)
26    /// algorithm. This was the default up through and including Proptest 0.9.0.
27    ///
28    /// It is faster than ChaCha but produces lower quality randomness and has
29    /// some pathological cases where it may fail to produce outputs that are
30    /// random even to casual observation.
31    ///
32    /// The seed must be exactly 16 bytes.
33    XorShift,
34    /// The [ChaCha](https://rust-random.github.io/rand/rand_chacha/struct.ChaChaRng.html)
35    /// algorithm. This became the default with Proptest 0.9.1.
36    ///
37    /// The seed must be exactly 32 bytes.
38    ChaCha,
39    /// This is not an actual RNG algorithm, but instead returns data directly
40    /// from its "seed".
41    ///
42    /// This is useful when Proptest is being driven from some other entropy
43    /// source, such as a fuzzer.
44    ///
45    /// If the seed is depleted, the RNG will return 0s forever.
46    ///
47    /// Note that in cases where a new RNG is to be derived from an existing
48    /// one, *the data is split evenly between them*, regardless of how much
49    /// entropy is actually needed. This means that combinators like
50    /// `prop_perturb` and `prop_flat_map` can require extremely large inputs.
51    PassThrough,
52    /// This is equivalent to the `ChaCha` RNG, with the addition that it
53    /// records the bytes used to create a value.
54    ///
55    /// This is useful when Proptest is used for fuzzing, and a corpus of
56    /// initial inputs need to be created. Note that in these cases, you need
57    /// to use the `TestRunner` API directly yourself instead of using the
58    /// `proptest!` macro, as otherwise there is no way to obtain the bytes
59    /// this captures.
60    Recorder,
61    #[allow(missing_docs)]
62    #[doc(hidden)]
63    _NonExhaustive,
64}
65
66impl Default for RngAlgorithm {
67    fn default() -> Self {
68        RngAlgorithm::ChaCha
69    }
70}
71
72impl RngAlgorithm {
73    pub(crate) fn persistence_key(self) -> &'static str {
74        match self {
75            RngAlgorithm::XorShift => "xs",
76            RngAlgorithm::ChaCha => "cc",
77            RngAlgorithm::PassThrough => "pt",
78            RngAlgorithm::Recorder => "rc",
79            RngAlgorithm::_NonExhaustive => unreachable!(),
80        }
81    }
82
83    pub(crate) fn from_persistence_key(k: &str) -> Option<Self> {
84        match k {
85            "xs" => Some(RngAlgorithm::XorShift),
86            "cc" => Some(RngAlgorithm::ChaCha),
87            "pt" => Some(RngAlgorithm::PassThrough),
88            "rc" => Some(RngAlgorithm::Recorder),
89            _ => None,
90        }
91    }
92}
93
94// These two are only used for parsing the environment variable
95// PROPTEST_RNG_ALGORITHM.
96impl str::FromStr for RngAlgorithm {
97    type Err = ();
98    fn from_str(s: &str) -> Result<Self, ()> {
99        RngAlgorithm::from_persistence_key(s).ok_or(())
100    }
101}
102impl fmt::Display for RngAlgorithm {
103    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104        write!(f, "{}", self.persistence_key())
105    }
106}
107
108/// Proptest's random number generator.
109#[derive(Clone, Debug)]
110pub struct TestRng {
111    rng: TestRngImpl,
112}
113
114#[derive(Clone, Debug)]
115enum TestRngImpl {
116    XorShift(XorShiftRng),
117    ChaCha(ChaChaRng),
118    PassThrough {
119        off: usize,
120        end: usize,
121        data: Arc<[u8]>,
122    },
123    Recorder {
124        rng: ChaChaRng,
125        record: Vec<u8>,
126    },
127}
128
129impl RngCore for TestRng {
130    fn next_u32(&mut self) -> u32 {
131        match &mut self.rng {
132            TestRngImpl::XorShift(rng) => rng.next_u32(),
133            TestRngImpl::ChaCha(rng) => rng.next_u32(),
134            TestRngImpl::PassThrough { .. } => {
135                let mut buf = [0; 4];
136                self.fill_bytes(&mut buf[..]);
137                u32::from_le_bytes(buf)
138            }
139            TestRngImpl::Recorder { rng, record } => {
140                let read = rng.next_u32();
141                record.extend_from_slice(&read.to_le_bytes());
142                read
143            }
144        }
145    }
146
147    fn next_u64(&mut self) -> u64 {
148        match &mut self.rng {
149            TestRngImpl::XorShift(rng) => rng.next_u64(),
150            TestRngImpl::ChaCha(rng) => rng.next_u64(),
151            TestRngImpl::PassThrough { .. } => {
152                let mut buf = [0; 8];
153                self.fill_bytes(&mut buf[..]);
154                u64::from_le_bytes(buf)
155            }
156            TestRngImpl::Recorder { rng, record } => {
157                let read = rng.next_u64();
158                record.extend_from_slice(&read.to_le_bytes());
159                read
160            }
161        }
162    }
163
164    fn fill_bytes(&mut self, dest: &mut [u8]) {
165        match &mut self.rng {
166            TestRngImpl::XorShift(rng) => rng.fill_bytes(dest),
167            TestRngImpl::ChaCha(rng) => rng.fill_bytes(dest),
168            TestRngImpl::PassThrough { off, end, data } => {
169                let bytes_to_copy = dest.len().min(*end - *off);
170                dest[.. bytes_to_copy].copy_from_slice(&data[*off .. *off + bytes_to_copy]);
171                *off += bytes_to_copy;
172                for i in bytes_to_copy .. dest.len() {
173                    dest[i] = 0;
174                }
175            }
176            TestRngImpl::Recorder { rng, record } => {
177                rng.fill_bytes(dest);
178                record.extend_from_slice(dest);
179            }
180        }
181    }
182}
183
184#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
185pub(crate) enum Seed {
186    XorShift([u8; 16]),
187    ChaCha([u8; 32]),
188    PassThrough(Option<(usize, usize)>, Arc<[u8]>),
189    Recorder([u8; 32]),
190}
191
192impl Seed {
193    pub(crate) fn from_bytes(algorithm: RngAlgorithm, seed: &[u8]) -> Self {
194        match algorithm {
195            RngAlgorithm::XorShift => {
196                assert_eq!(16, seed.len(), "XorShift requires a 16-byte seed");
197                let mut buf = [0; 16];
198                buf.copy_from_slice(seed);
199                Seed::XorShift(buf)
200            }
201
202            RngAlgorithm::ChaCha => {
203                assert_eq!(32, seed.len(), "ChaCha requires a 32-byte seed");
204                let mut buf = [0; 32];
205                buf.copy_from_slice(seed);
206                Seed::ChaCha(buf)
207            }
208
209            RngAlgorithm::PassThrough => Seed::PassThrough(None, seed.into()),
210
211            RngAlgorithm::Recorder => {
212                assert_eq!(32, seed.len(), "Recorder requires a 32-byte seed");
213                let mut buf = [0; 32];
214                buf.copy_from_slice(seed);
215                Seed::Recorder(buf)
216            }
217
218            RngAlgorithm::_NonExhaustive => unreachable!(),
219        }
220    }
221
222    pub(crate) fn from_persistence(string: &str) -> Option<Seed> {
223        fn from_base16(dst: &mut [u8], src: &str) -> Option<()> {
224            if dst.len() * 2 != src.len() {
225                return None;
226            }
227
228            for (dst_byte, src_pair) in
229                dst.into_iter().zip(src.as_bytes().chunks(2))
230            {
231                *dst_byte =
232                    u8::from_str_radix(str::from_utf8(src_pair).ok()?, 16)
233                        .ok()?;
234            }
235
236            Some(())
237        }
238
239        let parts =
240            string.trim().split(char::is_whitespace).collect::<Vec<_>>();
241        RngAlgorithm::from_persistence_key(&parts[0]).and_then(
242            |alg| match alg {
243                RngAlgorithm::XorShift => {
244                    if 5 != parts.len() {
245                        return None;
246                    }
247
248                    let mut dwords = [0u32; 4];
249                    for (dword, part) in
250                        (&mut dwords[..]).into_iter().zip(&parts[1..])
251                    {
252                        *dword = part.parse().ok()?;
253                    }
254
255                    let mut seed = [0u8; 16];
256                    for (chunk, dword) in seed.chunks_mut(4).zip(dwords) {
257                        chunk.copy_from_slice(&dword.to_le_bytes());
258                    }
259                    Some(Seed::XorShift(seed))
260                }
261
262                RngAlgorithm::ChaCha => {
263                    if 2 != parts.len() {
264                        return None;
265                    }
266
267                    let mut seed = [0u8; 32];
268                    from_base16(&mut seed, &parts[1])?;
269                    Some(Seed::ChaCha(seed))
270                }
271
272                RngAlgorithm::PassThrough => {
273                    if 1 == parts.len() {
274                        return Some(Seed::PassThrough(None, vec![].into()));
275                    }
276
277                    if 2 != parts.len() {
278                        return None;
279                    }
280
281                    let mut seed = vec![0u8; parts[1].len() / 2];
282                    from_base16(&mut seed, &parts[1])?;
283                    Some(Seed::PassThrough(None, seed.into()))
284                }
285
286                RngAlgorithm::Recorder => {
287                    if 2 != parts.len() {
288                        return None;
289                    }
290
291                    let mut seed = [0u8; 32];
292                    from_base16(&mut seed, &parts[1])?;
293                    Some(Seed::Recorder(seed))
294                }
295
296                RngAlgorithm::_NonExhaustive => unreachable!(),
297            },
298        )
299    }
300
301    pub(crate) fn to_persistence(&self) -> String {
302        fn to_base16(dst: &mut String, src: &[u8]) {
303            for byte in src {
304                dst.push_str(&format!("{:02x}", byte));
305            }
306        }
307
308        match *self {
309            Seed::XorShift(ref seed) => {
310                let dwords = [
311                    u32::from_le_bytes(seed[0..4].try_into().unwrap()),
312                    u32::from_le_bytes(seed[4..8].try_into().unwrap()),
313                    u32::from_le_bytes(seed[8..12].try_into().unwrap()),
314                    u32::from_le_bytes(seed[12..16].try_into().unwrap()),
315                ];
316                format!(
317                    "{} {} {} {} {}",
318                    RngAlgorithm::XorShift.persistence_key(),
319                    dwords[0],
320                    dwords[1],
321                    dwords[2],
322                    dwords[3]
323                )
324            }
325
326            Seed::ChaCha(ref seed) => {
327                let mut string =
328                    RngAlgorithm::ChaCha.persistence_key().to_owned();
329                string.push(' ');
330                to_base16(&mut string, seed);
331                string
332            }
333
334            Seed::PassThrough(bounds, ref data) => {
335                let data =
336                    bounds.map_or(&data[..], |(start, end)| &data[start..end]);
337                let mut string =
338                    RngAlgorithm::PassThrough.persistence_key().to_owned();
339                string.push(' ');
340                to_base16(&mut string, data);
341                string
342            }
343
344            Seed::Recorder(ref seed) => {
345                let mut string =
346                    RngAlgorithm::Recorder.persistence_key().to_owned();
347                string.push(' ');
348                to_base16(&mut string, seed);
349                string
350            }
351        }
352    }
353}
354
355impl TestRng {
356    /// Create a new RNG with the given algorithm and seed.
357    ///
358    /// Any RNG created with the same algorithm-seed pair will produce the same
359    /// sequence of values on all systems and all supporting versions of
360    /// proptest.
361    ///
362    /// ## Panics
363    ///
364    /// Panics if `seed` is not an appropriate length for `algorithm`.
365    pub fn from_seed(algorithm: RngAlgorithm, seed: &[u8]) -> Self {
366        TestRng::from_seed_internal(Seed::from_bytes(algorithm, seed))
367    }
368
369    /// Dumps the bytes obtained from the RNG so far (only works if the RNG is
370    /// set to `Recorder`).
371    ///
372    /// ## Panics
373    ///
374    /// Panics if this RNG does not capture generated data.
375    pub fn bytes_used(&self) -> Vec<u8> {
376        match self.rng {
377            TestRngImpl::Recorder { ref record, .. } => record.clone(),
378            _ => panic!("bytes_used() called on non-Recorder RNG"),
379        }
380    }
381
382    /// Construct a default TestRng from entropy.
383    pub(crate) fn default_rng(seed: config::RngSeed, algorithm: RngAlgorithm) -> Self {
384        #[cfg(feature = "std")]
385        {
386            Self {
387                rng: match algorithm {
388                    RngAlgorithm::XorShift => {
389                        let rng = match seed {
390                            RngSeed::Random => XorShiftRng::from_os_rng(),
391                            RngSeed::Fixed(seed) => XorShiftRng::seed_from_u64(seed),
392                        };
393                        TestRngImpl::XorShift(rng)
394                    }
395                    RngAlgorithm::ChaCha => {
396                        let rng = match seed {
397                            RngSeed::Random => ChaChaRng::from_os_rng(),
398                            RngSeed::Fixed(seed) => ChaChaRng::seed_from_u64(seed),
399                        };
400                        TestRngImpl::ChaCha(rng)
401                    }
402                    RngAlgorithm::PassThrough => {
403                        panic!("cannot create default instance of PassThrough")
404                    }
405                    RngAlgorithm::Recorder => {
406                        let rng =  match seed {
407                            RngSeed::Random => ChaChaRng::from_os_rng(),
408                            RngSeed::Fixed(seed) => ChaChaRng::seed_from_u64(seed),
409                        };
410                        TestRngImpl::Recorder {rng, record: Vec::new()}
411                    },
412                    RngAlgorithm::_NonExhaustive => unreachable!(),
413                },
414            }
415        }
416        #[cfg(all(
417            not(feature = "std"),
418            any(target_arch = "x86", target_arch = "x86_64"),
419            feature = "hardware-rng"
420        ))]
421        {
422            return Self::hardware_rng(algorithm);
423        }
424        #[cfg(not(feature = "std"))]
425        {
426            return Self::deterministic_rng(algorithm);
427        }
428    }
429
430    const SEED_FOR_XOR_SHIFT: [u8; 16] = [
431        0xf4, 0x16, 0x16, 0x48, 0xc3, 0xac, 0x77, 0xac, 0x72, 0x20, 0x0b, 0xea,
432        0x99, 0x67, 0x2d, 0x6d,
433    ];
434
435    const SEED_FOR_CHA_CHA: [u8; 32] = [
436        0xf4, 0x16, 0x16, 0x48, 0xc3, 0xac, 0x77, 0xac, 0x72, 0x20, 0x0b, 0xea,
437        0x99, 0x67, 0x2d, 0x6d, 0xca, 0x9f, 0x76, 0xaf, 0x1b, 0x09, 0x73, 0xa0,
438        0x59, 0x22, 0x6d, 0xc5, 0x46, 0x39, 0x1c, 0x4a,
439    ];
440
441    /// Returns a `TestRng` with a seed generated with the
442    /// RdRand instruction on x86 machines.
443    ///
444    /// This is useful in `no_std` scenarios on x86 where we don't
445    /// have a random number infrastructure but the `rdrand` instruction is
446    /// available.
447    #[cfg(all(
448        not(feature = "std"),
449        any(target_arch = "x86", target_arch = "x86_64"),
450        feature = "hardware-rng"
451    ))]
452    pub fn hardware_rng(algorithm: RngAlgorithm) -> Self {
453        use x86::random::{rdrand_slice, RdRand};
454
455        Self::from_seed_internal(match algorithm {
456            RngAlgorithm::XorShift => {
457                // Initialize to a sane seed just in case
458                let mut seed: [u8; 16] = TestRng::SEED_FOR_XOR_SHIFT;
459                unsafe {
460                    let r = rdrand_slice(&mut seed);
461                    debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
462                }
463                Seed::XorShift(seed)
464            }
465            RngAlgorithm::ChaCha => {
466                // Initialize to a sane seed just in case
467                let mut seed: [u8; 32] = TestRng::SEED_FOR_CHA_CHA;
468                unsafe {
469                    let r = rdrand_slice(&mut seed);
470                    debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
471                }
472                Seed::ChaCha(seed)
473            }
474            RngAlgorithm::PassThrough => {
475                panic!("deterministic RNG not available for PassThrough")
476            }
477            RngAlgorithm::Recorder => {
478                // Initialize to a sane seed just in case
479                let mut seed: [u8; 32] = TestRng::SEED_FOR_CHA_CHA;
480                unsafe {
481                    let r = rdrand_slice(&mut seed);
482                    debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
483                }
484                Seed::Recorder(seed)
485            }
486            RngAlgorithm::_NonExhaustive => unreachable!(),
487        })
488    }
489
490    /// Returns a `TestRng` with a particular hard-coded seed.
491    ///
492    /// The seed value will always be the same for a particular version of
493    /// Proptest and algorithm, but may change across releases.
494    ///
495    /// This is useful for testing things like strategy implementations without
496    /// risking getting "unlucky" RNGs which deviate from average behaviour
497    /// enough to cause spurious failures. For example, a strategy for `bool`
498    /// which is supposed to produce `true` 50% of the time might have a test
499    /// which checks that the distribution is "close enough" to 50%. If every
500    /// test run starts with a different RNG, occasionally there will be
501    /// spurious test failures when the RNG happens to produce a very skewed
502    /// distribution. Using this or `TestRunner::deterministic()` avoids such
503    /// issues.
504    pub fn deterministic_rng(algorithm: RngAlgorithm) -> Self {
505        Self::from_seed_internal(match algorithm {
506            RngAlgorithm::XorShift => {
507                Seed::XorShift(TestRng::SEED_FOR_XOR_SHIFT)
508            }
509            RngAlgorithm::ChaCha => Seed::ChaCha(TestRng::SEED_FOR_CHA_CHA),
510            RngAlgorithm::PassThrough => {
511                panic!("deterministic RNG not available for PassThrough")
512            }
513            RngAlgorithm::Recorder => Seed::Recorder(TestRng::SEED_FOR_CHA_CHA),
514            RngAlgorithm::_NonExhaustive => unreachable!(),
515        })
516    }
517
518    /// Construct a TestRng by the perturbed randomized seed
519    /// from an existing TestRng.
520    pub(crate) fn gen_rng(&mut self) -> Self {
521        Self::from_seed_internal(self.new_rng_seed())
522    }
523
524    /// Overwrite the given TestRng with the provided seed.
525    pub(crate) fn set_seed(&mut self, seed: Seed) {
526        *self = Self::from_seed_internal(seed);
527    }
528
529    /// Generate a new randomized seed, set it to this TestRng,
530    /// and return the seed.
531    pub(crate) fn gen_get_seed(&mut self) -> Seed {
532        let seed = self.new_rng_seed();
533        self.set_seed(seed.clone());
534        seed
535    }
536
537    /// Randomize a perturbed randomized seed from the given TestRng.
538    pub(crate) fn new_rng_seed(&mut self) -> Seed {
539        match self.rng {
540            TestRngImpl::XorShift(ref mut rng) => {
541                let mut seed = rng.random::<[u8; 16]>();
542
543                // Directly using XorShiftRng::from_seed() at this point would
544                // result in rng and the returned value being exactly the same.
545                // Perturb the seed with some arbitrary values to prevent this.
546                for word in seed.chunks_mut(4) {
547                    word[3] ^= 0xde;
548                    word[2] ^= 0xad;
549                    word[1] ^= 0xbe;
550                    word[0] ^= 0xef;
551                }
552
553                Seed::XorShift(seed)
554            }
555
556            TestRngImpl::ChaCha(ref mut rng) => Seed::ChaCha(rng.random()),
557
558            TestRngImpl::PassThrough {
559                ref mut off,
560                ref mut end,
561                ref data,
562            } => {
563                let len = *end - *off;
564                let child_start = *off + len / 2;
565                let child_end = *off + len;
566                *end = child_start;
567                Seed::PassThrough(
568                    Some((child_start, child_end)),
569                    Arc::clone(data),
570                )
571            }
572
573            TestRngImpl::Recorder { ref mut rng, .. } => {
574                Seed::Recorder(rng.random())
575            }
576        }
577    }
578
579    /// Construct a TestRng from a given seed.
580    fn from_seed_internal(seed: Seed) -> Self {
581        Self {
582            rng: match seed {
583                Seed::XorShift(seed) => {
584                    TestRngImpl::XorShift(XorShiftRng::from_seed(seed))
585                }
586
587                Seed::ChaCha(seed) => {
588                    TestRngImpl::ChaCha(ChaChaRng::from_seed(seed))
589                }
590
591                Seed::PassThrough(bounds, data) => {
592                    let (start, end) = bounds.unwrap_or((0, data.len()));
593                    TestRngImpl::PassThrough {
594                        off: start,
595                        end,
596                        data,
597                    }
598                }
599
600                Seed::Recorder(seed) => TestRngImpl::Recorder {
601                    rng: ChaChaRng::from_seed(seed),
602                    record: Vec::new(),
603                },
604            },
605        }
606    }
607}
608
609#[cfg(test)]
610mod test {
611    use crate::std_facade::Vec;
612
613    use rand::{Rng, RngCore};
614
615    use super::{RngAlgorithm, Seed, TestRng};
616    use crate::arbitrary::any;
617    use crate::strategy::*;
618
619    proptest! {
620        #[test]
621        fn gen_parse_seeds(
622            seed in prop_oneof![
623                any::<[u8;16]>().prop_map(Seed::XorShift),
624                any::<[u8;32]>().prop_map(Seed::ChaCha),
625                any::<Vec<u8>>().prop_map(|data| Seed::PassThrough(None, data.into())),
626                any::<[u8;32]>().prop_map(Seed::Recorder),
627            ])
628        {
629            assert_eq!(seed, Seed::from_persistence(&seed.to_persistence()).unwrap());
630        }
631
632        #[test]
633        fn rngs_dont_clone_self_on_genrng(
634            seed in prop_oneof![
635                any::<[u8;16]>().prop_map(Seed::XorShift),
636                any::<[u8;32]>().prop_map(Seed::ChaCha),
637                Just(()).prop_perturb(|_, mut rng| {
638                    let mut buf = vec![0u8; 2048];
639                    rng.fill_bytes(&mut buf);
640                    Seed::PassThrough(None, buf.into())
641                }),
642                any::<[u8;32]>().prop_map(Seed::Recorder),
643            ])
644        {
645            type Value = [u8;32];
646            let orig = TestRng::from_seed_internal(seed);
647
648            {
649                let mut rng1 = orig.clone();
650                let mut rng2 = rng1.gen_rng();
651                assert_ne!(rng1.random::<Value>(), rng2.random::<Value>());
652            }
653
654            {
655                let mut rng1 = orig.clone();
656                let mut rng2 = rng1.gen_rng();
657                let mut rng3 = rng1.gen_rng();
658                let mut rng4 = rng2.gen_rng();
659                let a = rng1.random::<Value>();
660                let b = rng2.random::<Value>();
661                let c = rng3.random::<Value>();
662                let d = rng4.random::<Value>();
663                assert_ne!(a, b);
664                assert_ne!(a, c);
665                assert_ne!(a, d);
666                assert_ne!(b, c);
667                assert_ne!(b, d);
668                assert_ne!(c, d);
669            }
670        }
671    }
672
673    #[test]
674    fn passthrough_rng_behaves_properly() {
675        let mut rng = TestRng::from_seed(
676            RngAlgorithm::PassThrough,
677            &[
678                0xDE, 0xC0, 0x12, 0x34, 0x56, 0x78, 0xFE, 0xCA, 0xEF, 0xBE,
679                0xAD, 0xDE, 0x01, 0x02, 0x03,
680            ],
681        );
682
683        assert_eq!(0x3412C0DE, rng.next_u32());
684        assert_eq!(0xDEADBEEFCAFE7856, rng.next_u64());
685
686        let mut buf = [0u8; 4];
687        rng.fill_bytes(&mut buf[0..4]);
688        assert_eq!([1, 2, 3, 0], buf);
689        rng.fill_bytes(&mut buf[0..4]);
690        assert_eq!([0, 0, 0, 0], buf);
691    }
692}