summaryrefslogtreecommitdiffstats
path: root/vendor/proptest/src/test_runner/rng.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proptest/src/test_runner/rng.rs')
-rw-r--r--vendor/proptest/src/test_runner/rng.rs724
1 files changed, 724 insertions, 0 deletions
diff --git a/vendor/proptest/src/test_runner/rng.rs b/vendor/proptest/src/test_runner/rng.rs
new file mode 100644
index 000000000..78c8d7a75
--- /dev/null
+++ b/vendor/proptest/src/test_runner/rng.rs
@@ -0,0 +1,724 @@
+//-
+// Copyright 2017, 2018, 2019, 2020 The proptest developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::std_facade::{Arc, String, ToOwned, Vec};
+use core::result::Result;
+use core::{fmt, str, u8};
+
+use byteorder::{ByteOrder, LittleEndian};
+use rand::{self, Rng, RngCore, SeedableRng};
+use rand_chacha::ChaChaRng;
+use rand_xorshift::XorShiftRng;
+
+/// Identifies a particular RNG algorithm supported by proptest.
+///
+/// Proptest supports dynamic configuration of algorithms to allow it to
+/// continue operating with persisted regression files and to allow the
+/// configuration to be expressed in the `Config` struct.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum RngAlgorithm {
+ /// The [XorShift](https://rust-random.github.io/rand/rand_xorshift/struct.XorShiftRng.html)
+ /// algorithm. This was the default up through and including Proptest 0.9.0.
+ ///
+ /// It is faster than ChaCha but produces lower quality randomness and has
+ /// some pathological cases where it may fail to produce outputs that are
+ /// random even to casual observation.
+ ///
+ /// The seed must be exactly 16 bytes.
+ XorShift,
+ /// The [ChaCha](https://rust-random.github.io/rand/rand_chacha/struct.ChaChaRng.html)
+ /// algorithm. This became the default with Proptest 0.9.1.
+ ///
+ /// The seed must be exactly 32 bytes.
+ ChaCha,
+ /// This is not an actual RNG algorithm, but instead returns data directly
+ /// from its "seed".
+ ///
+ /// This is useful when Proptest is being driven from some other entropy
+ /// source, such as a fuzzer.
+ ///
+ /// If the seed is depleted, the RNG will return 0s forever.
+ ///
+ /// Note that in cases where a new RNG is to be derived from an existing
+ /// one, *the data is split evenly between them*, regardless of how much
+ /// entropy is actually needed. This means that combinators like
+ /// `prop_perturb` and `prop_flat_map` can require extremely large inputs.
+ PassThrough,
+ /// This is equivalent to the `ChaCha` RNG, with the addition that it
+ /// records the bytes used to create a value.
+ ///
+ /// This is useful when Proptest is used for fuzzing, and a corpus of
+ /// initial inputs need to be created. Note that in these cases, you need
+ /// to use the `TestRunner` API directly yourself instead of using the
+ /// `proptest!` macro, as otherwise there is no way to obtain the bytes
+ /// this captures.
+ Recorder,
+ #[allow(missing_docs)]
+ #[doc(hidden)]
+ _NonExhaustive,
+}
+
+impl Default for RngAlgorithm {
+ fn default() -> Self {
+ RngAlgorithm::ChaCha
+ }
+}
+
+impl RngAlgorithm {
+ pub(crate) fn persistence_key(self) -> &'static str {
+ match self {
+ RngAlgorithm::XorShift => "xs",
+ RngAlgorithm::ChaCha => "cc",
+ RngAlgorithm::PassThrough => "pt",
+ RngAlgorithm::Recorder => "rc",
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ }
+ }
+
+ pub(crate) fn from_persistence_key(k: &str) -> Option<Self> {
+ match k {
+ "xs" => Some(RngAlgorithm::XorShift),
+ "cc" => Some(RngAlgorithm::ChaCha),
+ "pt" => Some(RngAlgorithm::PassThrough),
+ "rc" => Some(RngAlgorithm::Recorder),
+ _ => None,
+ }
+ }
+}
+
+// These two are only used for parsing the environment variable
+// PROPTEST_RNG_ALGORITHM.
+impl str::FromStr for RngAlgorithm {
+ type Err = ();
+ fn from_str(s: &str) -> Result<Self, ()> {
+ RngAlgorithm::from_persistence_key(s).ok_or(())
+ }
+}
+impl fmt::Display for RngAlgorithm {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.persistence_key())
+ }
+}
+
+/// Proptest's random number generator.
+#[derive(Clone, Debug)]
+pub struct TestRng {
+ rng: TestRngImpl,
+}
+
+#[derive(Clone, Debug)]
+enum TestRngImpl {
+ XorShift(XorShiftRng),
+ ChaCha(ChaChaRng),
+ PassThrough {
+ off: usize,
+ end: usize,
+ data: Arc<[u8]>,
+ },
+ Recorder {
+ rng: ChaChaRng,
+ record: Vec<u8>,
+ },
+}
+
+impl RngCore for TestRng {
+ fn next_u32(&mut self) -> u32 {
+ match &mut self.rng {
+ &mut TestRngImpl::XorShift(ref mut rng) => rng.next_u32(),
+
+ &mut TestRngImpl::ChaCha(ref mut rng) => rng.next_u32(),
+
+ &mut TestRngImpl::PassThrough { .. } => {
+ let mut buf = [0; 4];
+ self.fill_bytes(&mut buf[..]);
+ LittleEndian::read_u32(&buf[..])
+ }
+
+ &mut TestRngImpl::Recorder {
+ ref mut rng,
+ ref mut record,
+ } => {
+ let read = rng.next_u32();
+ record.extend_from_slice(&read.to_le_bytes());
+ read
+ }
+ }
+ }
+
+ fn next_u64(&mut self) -> u64 {
+ match &mut self.rng {
+ &mut TestRngImpl::XorShift(ref mut rng) => rng.next_u64(),
+
+ &mut TestRngImpl::ChaCha(ref mut rng) => rng.next_u64(),
+
+ &mut TestRngImpl::PassThrough { .. } => {
+ let mut buf = [0; 8];
+ self.fill_bytes(&mut buf[..]);
+ LittleEndian::read_u64(&buf[..])
+ }
+
+ &mut TestRngImpl::Recorder {
+ ref mut rng,
+ ref mut record,
+ } => {
+ let read = rng.next_u64();
+ record.extend_from_slice(&read.to_le_bytes());
+ read
+ }
+ }
+ }
+
+ fn fill_bytes(&mut self, dest: &mut [u8]) {
+ match &mut self.rng {
+ &mut TestRngImpl::XorShift(ref mut rng) => rng.fill_bytes(dest),
+
+ &mut TestRngImpl::ChaCha(ref mut rng) => rng.fill_bytes(dest),
+
+ &mut TestRngImpl::PassThrough {
+ ref mut off,
+ end,
+ ref data,
+ } => {
+ let bytes_to_copy = dest.len().min(end - *off);
+ dest[..bytes_to_copy]
+ .copy_from_slice(&data[*off..*off + bytes_to_copy]);
+ *off += bytes_to_copy;
+ for i in bytes_to_copy..dest.len() {
+ dest[i] = 0;
+ }
+ }
+
+ &mut TestRngImpl::Recorder {
+ ref mut rng,
+ ref mut record,
+ } => {
+ let res = rng.fill_bytes(dest);
+ record.extend_from_slice(&dest);
+ res
+ }
+ }
+ }
+
+ fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
+ match self.rng {
+ TestRngImpl::XorShift(ref mut rng) => rng.try_fill_bytes(dest),
+
+ TestRngImpl::ChaCha(ref mut rng) => rng.try_fill_bytes(dest),
+
+ TestRngImpl::PassThrough { .. } => {
+ self.fill_bytes(dest);
+ Ok(())
+ }
+
+ TestRngImpl::Recorder {
+ ref mut rng,
+ ref mut record,
+ } => {
+ let res = rng.try_fill_bytes(dest);
+ if res.is_ok() {
+ record.extend_from_slice(&dest);
+ }
+ res
+ }
+ }
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) enum Seed {
+ XorShift([u8; 16]),
+ ChaCha([u8; 32]),
+ PassThrough(Option<(usize, usize)>, Arc<[u8]>),
+ Recorder([u8; 32]),
+}
+
+impl Seed {
+ pub(crate) fn from_bytes(algorithm: RngAlgorithm, seed: &[u8]) -> Self {
+ match algorithm {
+ RngAlgorithm::XorShift => {
+ assert_eq!(16, seed.len(), "XorShift requires a 16-byte seed");
+ let mut buf = [0; 16];
+ buf.copy_from_slice(seed);
+ Seed::XorShift(buf)
+ }
+
+ RngAlgorithm::ChaCha => {
+ assert_eq!(32, seed.len(), "ChaCha requires a 32-byte seed");
+ let mut buf = [0; 32];
+ buf.copy_from_slice(seed);
+ Seed::ChaCha(buf)
+ }
+
+ RngAlgorithm::PassThrough => Seed::PassThrough(None, seed.into()),
+
+ RngAlgorithm::Recorder => {
+ assert_eq!(32, seed.len(), "Recorder requires a 32-byte seed");
+ let mut buf = [0; 32];
+ buf.copy_from_slice(seed);
+ Seed::Recorder(buf)
+ }
+
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ }
+ }
+
+ pub(crate) fn from_persistence(string: &str) -> Option<Seed> {
+ fn from_base16(dst: &mut [u8], src: &str) -> Option<()> {
+ if dst.len() * 2 != src.len() {
+ return None;
+ }
+
+ for (dst_byte, src_pair) in
+ dst.into_iter().zip(src.as_bytes().chunks(2))
+ {
+ *dst_byte =
+ u8::from_str_radix(str::from_utf8(src_pair).ok()?, 16)
+ .ok()?;
+ }
+
+ Some(())
+ }
+
+ let parts =
+ string.trim().split(char::is_whitespace).collect::<Vec<_>>();
+ RngAlgorithm::from_persistence_key(&parts[0]).and_then(
+ |alg| match alg {
+ RngAlgorithm::XorShift => {
+ if 5 != parts.len() {
+ return None;
+ }
+
+ let mut dwords = [0u32; 4];
+ for (dword, part) in
+ (&mut dwords[..]).into_iter().zip(&parts[1..])
+ {
+ *dword = part.parse().ok()?;
+ }
+
+ let mut seed = [0u8; 16];
+ LittleEndian::write_u32_into(&dwords[..], &mut seed[..]);
+ Some(Seed::XorShift(seed))
+ }
+
+ RngAlgorithm::ChaCha => {
+ if 2 != parts.len() {
+ return None;
+ }
+
+ let mut seed = [0u8; 32];
+ from_base16(&mut seed, &parts[1])?;
+ Some(Seed::ChaCha(seed))
+ }
+
+ RngAlgorithm::PassThrough => {
+ if 1 == parts.len() {
+ return Some(Seed::PassThrough(None, vec![].into()));
+ }
+
+ if 2 != parts.len() {
+ return None;
+ }
+
+ let mut seed = vec![0u8; parts[1].len() / 2];
+ from_base16(&mut seed, &parts[1])?;
+ Some(Seed::PassThrough(None, seed.into()))
+ }
+
+ RngAlgorithm::Recorder => {
+ if 2 != parts.len() {
+ return None;
+ }
+
+ let mut seed = [0u8; 32];
+ from_base16(&mut seed, &parts[1])?;
+ Some(Seed::Recorder(seed))
+ }
+
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ },
+ )
+ }
+
+ pub(crate) fn to_persistence(&self) -> String {
+ fn to_base16(dst: &mut String, src: &[u8]) {
+ for byte in src {
+ dst.push_str(&format!("{:02x}", byte));
+ }
+ }
+
+ match *self {
+ Seed::XorShift(ref seed) => {
+ let mut dwords = [0u32; 4];
+ LittleEndian::read_u32_into(seed, &mut dwords[..]);
+ format!(
+ "{} {} {} {} {}",
+ RngAlgorithm::XorShift.persistence_key(),
+ dwords[0],
+ dwords[1],
+ dwords[2],
+ dwords[3]
+ )
+ }
+
+ Seed::ChaCha(ref seed) => {
+ let mut string =
+ RngAlgorithm::ChaCha.persistence_key().to_owned();
+ string.push(' ');
+ to_base16(&mut string, seed);
+ string
+ }
+
+ Seed::PassThrough(bounds, ref data) => {
+ let data =
+ bounds.map_or(&data[..], |(start, end)| &data[start..end]);
+ let mut string =
+ RngAlgorithm::PassThrough.persistence_key().to_owned();
+ string.push(' ');
+ to_base16(&mut string, data);
+ string
+ }
+
+ Seed::Recorder(ref seed) => {
+ let mut string =
+ RngAlgorithm::Recorder.persistence_key().to_owned();
+ string.push(' ');
+ to_base16(&mut string, seed);
+ string
+ }
+ }
+ }
+}
+
+impl TestRng {
+ /// Create a new RNG with the given algorithm and seed.
+ ///
+ /// Any RNG created with the same algorithm-seed pair will produce the same
+ /// sequence of values on all systems and all supporting versions of
+ /// proptest.
+ ///
+ /// ## Panics
+ ///
+ /// Panics if `seed` is not an appropriate length for `algorithm`.
+ pub fn from_seed(algorithm: RngAlgorithm, seed: &[u8]) -> Self {
+ TestRng::from_seed_internal(Seed::from_bytes(algorithm, seed))
+ }
+
+ /// Dumps the bytes obtained from the RNG so far (only works if the RNG is
+ /// set to `Recorder`).
+ ///
+ /// ## Panics
+ ///
+ /// Panics if this RNG does not capture generated data.
+ pub fn bytes_used(&self) -> Vec<u8> {
+ match self.rng {
+ TestRngImpl::Recorder { ref record, .. } => record.clone(),
+ _ => panic!("bytes_used() called on non-Recorder RNG"),
+ }
+ }
+
+ /// Construct a default TestRng from entropy.
+ pub(crate) fn default_rng(algorithm: RngAlgorithm) -> Self {
+ #[cfg(feature = "std")]
+ {
+ Self {
+ rng: match algorithm {
+ RngAlgorithm::XorShift => {
+ TestRngImpl::XorShift(XorShiftRng::from_entropy())
+ }
+ RngAlgorithm::ChaCha => {
+ TestRngImpl::ChaCha(ChaChaRng::from_entropy())
+ }
+ RngAlgorithm::PassThrough => {
+ panic!("cannot create default instance of PassThrough")
+ }
+ RngAlgorithm::Recorder => TestRngImpl::Recorder {
+ rng: ChaChaRng::from_entropy(),
+ record: Vec::new(),
+ },
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ },
+ }
+ }
+ #[cfg(all(
+ not(feature = "std"),
+ any(target_arch = "x86", target_arch = "x86_64"),
+ feature = "hardware-rng"
+ ))]
+ {
+ return Self::hardware_rng(algorithm);
+ }
+ #[cfg(not(feature = "std"))]
+ {
+ return Self::deterministic_rng(algorithm);
+ }
+ }
+
+ const SEED_FOR_XOR_SHIFT: [u8; 16] = [
+ 0xf4, 0x16, 0x16, 0x48, 0xc3, 0xac, 0x77, 0xac, 0x72, 0x20, 0x0b, 0xea,
+ 0x99, 0x67, 0x2d, 0x6d,
+ ];
+
+ const SEED_FOR_CHA_CHA: [u8; 32] = [
+ 0xf4, 0x16, 0x16, 0x48, 0xc3, 0xac, 0x77, 0xac, 0x72, 0x20, 0x0b, 0xea,
+ 0x99, 0x67, 0x2d, 0x6d, 0xca, 0x9f, 0x76, 0xaf, 0x1b, 0x09, 0x73, 0xa0,
+ 0x59, 0x22, 0x6d, 0xc5, 0x46, 0x39, 0x1c, 0x4a,
+ ];
+
+ /// Returns a `TestRng` with a seed generated with the
+ /// RdRand instruction on x86 machines.
+ ///
+ /// This is useful in `no_std` scenarios on x86 where we don't
+ /// have a random number infrastructure but the `rdrand` instruction is
+ /// available.
+ #[cfg(all(
+ not(feature = "std"),
+ any(target_arch = "x86", target_arch = "x86_64"),
+ feature = "hardware-rng"
+ ))]
+ pub fn hardware_rng(algorithm: RngAlgorithm) -> Self {
+ use x86::random::{rdrand_slice, RdRand};
+
+ Self::from_seed_internal(match algorithm {
+ RngAlgorithm::XorShift => {
+ // Initialize to a sane seed just in case
+ let mut seed: [u8; 16] = TestRng::SEED_FOR_XOR_SHIFT;
+ unsafe {
+ let r = rdrand_slice(&mut seed);
+ debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
+ }
+ Seed::XorShift(seed)
+ }
+ RngAlgorithm::ChaCha => {
+ // Initialize to a sane seed just in case
+ let mut seed: [u8; 32] = TestRng::SEED_FOR_CHA_CHA;
+ unsafe {
+ let r = rdrand_slice(&mut seed);
+ debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
+ }
+ Seed::ChaCha(seed)
+ }
+ RngAlgorithm::PassThrough => {
+ panic!("deterministic RNG not available for PassThrough")
+ }
+ RngAlgorithm::Recorder => {
+ // Initialize to a sane seed just in case
+ let mut seed: [u8; 32] = TestRng::SEED_FOR_CHA_CHA;
+ unsafe {
+ let r = rdrand_slice(&mut seed);
+ debug_assert!(r, "hardware_rng should only be called on machines with support for rdrand");
+ }
+ Seed::Recorder(seed)
+ }
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ })
+ }
+
+ /// Returns a `TestRng` with a particular hard-coded seed.
+ ///
+ /// The seed value will always be the same for a particular version of
+ /// Proptest and algorithm, but may change across releases.
+ ///
+ /// This is useful for testing things like strategy implementations without
+ /// risking getting "unlucky" RNGs which deviate from average behaviour
+ /// enough to cause spurious failures. For example, a strategy for `bool`
+ /// which is supposed to produce `true` 50% of the time might have a test
+ /// which checks that the distribution is "close enough" to 50%. If every
+ /// test run starts with a different RNG, occasionally there will be
+ /// spurious test failures when the RNG happens to produce a very skewed
+ /// distribution. Using this or `TestRunner::deterministic()` avoids such
+ /// issues.
+ pub fn deterministic_rng(algorithm: RngAlgorithm) -> Self {
+ Self::from_seed_internal(match algorithm {
+ RngAlgorithm::XorShift => {
+ Seed::XorShift(TestRng::SEED_FOR_XOR_SHIFT)
+ }
+ RngAlgorithm::ChaCha => Seed::ChaCha(TestRng::SEED_FOR_CHA_CHA),
+ RngAlgorithm::PassThrough => {
+ panic!("deterministic RNG not available for PassThrough")
+ }
+ RngAlgorithm::Recorder => Seed::Recorder(TestRng::SEED_FOR_CHA_CHA),
+ RngAlgorithm::_NonExhaustive => unreachable!(),
+ })
+ }
+
+ /// Construct a TestRng by the perturbed randomized seed
+ /// from an existing TestRng.
+ pub(crate) fn gen_rng(&mut self) -> Self {
+ Self::from_seed_internal(self.new_rng_seed())
+ }
+
+ /// Overwrite the given TestRng with the provided seed.
+ pub(crate) fn set_seed(&mut self, seed: Seed) {
+ *self = Self::from_seed_internal(seed);
+ }
+
+ /// Generate a new randomized seed, set it to this TestRng,
+ /// and return the seed.
+ pub(crate) fn gen_get_seed(&mut self) -> Seed {
+ let seed = self.new_rng_seed();
+ self.set_seed(seed.clone());
+ seed
+ }
+
+ /// Randomize a perturbed randomized seed from the given TestRng.
+ pub(crate) fn new_rng_seed(&mut self) -> Seed {
+ match self.rng {
+ TestRngImpl::XorShift(ref mut rng) => {
+ let mut seed = rng.gen::<[u8; 16]>();
+
+ // Directly using XorShiftRng::from_seed() at this point would
+ // result in rng and the returned value being exactly the same.
+ // Perturb the seed with some arbitrary values to prevent this.
+ for word in seed.chunks_mut(4) {
+ word[3] ^= 0xde;
+ word[2] ^= 0xad;
+ word[1] ^= 0xbe;
+ word[0] ^= 0xef;
+ }
+
+ Seed::XorShift(seed)
+ }
+
+ TestRngImpl::ChaCha(ref mut rng) => Seed::ChaCha(rng.gen()),
+
+ TestRngImpl::PassThrough {
+ ref mut off,
+ ref mut end,
+ ref data,
+ } => {
+ let len = *end - *off;
+ let child_start = *off + len / 2;
+ let child_end = *off + len;
+ *end = child_start;
+ Seed::PassThrough(
+ Some((child_start, child_end)),
+ Arc::clone(data),
+ )
+ }
+
+ TestRngImpl::Recorder { ref mut rng, .. } => {
+ Seed::Recorder(rng.gen())
+ }
+ }
+ }
+
+ /// Construct a TestRng from a given seed.
+ fn from_seed_internal(seed: Seed) -> Self {
+ Self {
+ rng: match seed {
+ Seed::XorShift(seed) => {
+ TestRngImpl::XorShift(XorShiftRng::from_seed(seed))
+ }
+
+ Seed::ChaCha(seed) => {
+ TestRngImpl::ChaCha(ChaChaRng::from_seed(seed))
+ }
+
+ Seed::PassThrough(bounds, data) => {
+ let (start, end) = bounds.unwrap_or((0, data.len()));
+ TestRngImpl::PassThrough {
+ off: start,
+ end,
+ data,
+ }
+ }
+
+ Seed::Recorder(seed) => TestRngImpl::Recorder {
+ rng: ChaChaRng::from_seed(seed),
+ record: Vec::new(),
+ },
+ },
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::std_facade::Vec;
+
+ use rand::{Rng, RngCore};
+
+ use super::{RngAlgorithm, Seed, TestRng};
+ use crate::arbitrary::any;
+ use crate::strategy::*;
+
+ proptest! {
+ #[test]
+ fn gen_parse_seeds(
+ seed in prop_oneof![
+ any::<[u8;16]>().prop_map(Seed::XorShift),
+ any::<[u8;32]>().prop_map(Seed::ChaCha),
+ any::<Vec<u8>>().prop_map(|data| Seed::PassThrough(None, data.into())),
+ any::<[u8;32]>().prop_map(Seed::Recorder),
+ ])
+ {
+ assert_eq!(seed, Seed::from_persistence(&seed.to_persistence()).unwrap());
+ }
+
+ #[test]
+ fn rngs_dont_clone_self_on_genrng(
+ seed in prop_oneof![
+ any::<[u8;16]>().prop_map(Seed::XorShift),
+ any::<[u8;32]>().prop_map(Seed::ChaCha),
+ Just(()).prop_perturb(|_, mut rng| {
+ let mut buf = vec![0u8; 2048];
+ rng.fill_bytes(&mut buf);
+ Seed::PassThrough(None, buf.into())
+ }),
+ any::<[u8;32]>().prop_map(Seed::Recorder),
+ ])
+ {
+ type Value = [u8;32];
+ let orig = TestRng::from_seed_internal(seed);
+
+ {
+ let mut rng1 = orig.clone();
+ let mut rng2 = rng1.gen_rng();
+ assert_ne!(rng1.gen::<Value>(), rng2.gen::<Value>());
+ }
+
+ {
+ let mut rng1 = orig.clone();
+ let mut rng2 = rng1.gen_rng();
+ let mut rng3 = rng1.gen_rng();
+ let mut rng4 = rng2.gen_rng();
+ let a = rng1.gen::<Value>();
+ let b = rng2.gen::<Value>();
+ let c = rng3.gen::<Value>();
+ let d = rng4.gen::<Value>();
+ assert_ne!(a, b);
+ assert_ne!(a, c);
+ assert_ne!(a, d);
+ assert_ne!(b, c);
+ assert_ne!(b, d);
+ assert_ne!(c, d);
+ }
+ }
+ }
+
+ #[test]
+ fn passthrough_rng_behaves_properly() {
+ let mut rng = TestRng::from_seed(
+ RngAlgorithm::PassThrough,
+ &[
+ 0xDE, 0xC0, 0x12, 0x34, 0x56, 0x78, 0xFE, 0xCA, 0xEF, 0xBE,
+ 0xAD, 0xDE, 0x01, 0x02, 0x03,
+ ],
+ );
+
+ assert_eq!(0x3412C0DE, rng.next_u32());
+ assert_eq!(0xDEADBEEFCAFE7856, rng.next_u64());
+
+ let mut buf = [0u8; 4];
+ rng.try_fill_bytes(&mut buf[0..4]).unwrap();
+ assert_eq!([1, 2, 3, 0], buf);
+ rng.try_fill_bytes(&mut buf[0..4]).unwrap();
+ assert_eq!([0, 0, 0, 0], buf);
+ }
+}