proptest/test_runner/
runner.rs

1//-
2// Copyright 2017, 2018, 2019, 2024 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, BTreeMap, Box, String, Vec};
11use core::sync::atomic::AtomicUsize;
12use core::sync::atomic::Ordering::SeqCst;
13use core::{fmt, iter};
14#[cfg(feature = "std")]
15use std::panic::{self, AssertUnwindSafe};
16
17#[cfg(feature = "fork")]
18use rusty_fork;
19#[cfg(feature = "fork")]
20use std::cell::{Cell, RefCell};
21#[cfg(feature = "fork")]
22use std::env;
23#[cfg(feature = "fork")]
24use std::fs;
25#[cfg(feature = "fork")]
26use tempfile;
27
28use crate::strategy::*;
29use crate::test_runner::config::*;
30use crate::test_runner::errors::*;
31use crate::test_runner::failure_persistence::PersistedSeed;
32use crate::test_runner::reason::*;
33#[cfg(feature = "fork")]
34use crate::test_runner::replay;
35use crate::test_runner::result_cache::*;
36use crate::test_runner::rng::TestRng;
37
38#[cfg(feature = "fork")]
39const ENV_FORK_FILE: &'static str = "_PROPTEST_FORKFILE";
40
41const ALWAYS: u32 = 0;
42/// Verbose level 1 to show failures. In state machine tests this level is used
43/// to print transitions.
44pub const INFO_LOG: u32 = 1;
45const TRACE: u32 = 2;
46
47#[cfg(feature = "std")]
48macro_rules! verbose_message {
49    ($runner:expr, $level:expr, $fmt:tt $($arg:tt)*) => { {
50        #[allow(unused_comparisons)]
51        {
52            if $runner.config.verbose >= $level {
53                eprintln!(concat!("proptest: ", $fmt) $($arg)*);
54            }
55        };
56        ()
57    } }
58}
59
60#[cfg(not(feature = "std"))]
61macro_rules! verbose_message {
62    ($runner:expr, $level:expr, $fmt:tt $($arg:tt)*) => {
63        let _ = $level;
64    };
65}
66
67type RejectionDetail = BTreeMap<Reason, u32>;
68
69/// State used when running a proptest test.
70#[derive(Clone)]
71pub struct TestRunner {
72    config: Config,
73    successes: u32,
74    local_rejects: u32,
75    global_rejects: u32,
76    rng: TestRng,
77    flat_map_regens: Arc<AtomicUsize>,
78
79    local_reject_detail: RejectionDetail,
80    global_reject_detail: RejectionDetail,
81}
82
83impl fmt::Debug for TestRunner {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        f.debug_struct("TestRunner")
86            .field("config", &self.config)
87            .field("successes", &self.successes)
88            .field("local_rejects", &self.local_rejects)
89            .field("global_rejects", &self.global_rejects)
90            .field("rng", &"<TestRng>")
91            .field("flat_map_regens", &self.flat_map_regens)
92            .field("local_reject_detail", &self.local_reject_detail)
93            .field("global_reject_detail", &self.global_reject_detail)
94            .finish()
95    }
96}
97
98impl fmt::Display for TestRunner {
99    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100        write!(
101            f,
102            "\tsuccesses: {}\n\
103             \tlocal rejects: {}\n",
104            self.successes, self.local_rejects
105        )?;
106        for (whence, count) in &self.local_reject_detail {
107            writeln!(f, "\t\t{} times at {}", count, whence)?;
108        }
109        writeln!(f, "\tglobal rejects: {}", self.global_rejects)?;
110        for (whence, count) in &self.global_reject_detail {
111            writeln!(f, "\t\t{} times at {}", count, whence)?;
112        }
113
114        Ok(())
115    }
116}
117
118/// Equivalent to: `TestRunner::new(Config::default())`.
119impl Default for TestRunner {
120    fn default() -> Self {
121        Self::new(Config::default())
122    }
123}
124
125#[cfg(feature = "fork")]
126#[derive(Debug)]
127struct ForkOutput {
128    file: Option<fs::File>,
129}
130
131#[cfg(feature = "fork")]
132impl ForkOutput {
133    fn append(&mut self, result: &TestCaseResult) {
134        if let Some(ref mut file) = self.file {
135            replay::append(file, result)
136                .expect("Failed to append to replay file");
137        }
138    }
139
140    fn ping(&mut self) {
141        if let Some(ref mut file) = self.file {
142            replay::ping(file).expect("Failed to append to replay file");
143        }
144    }
145
146    fn terminate(&mut self) {
147        if let Some(ref mut file) = self.file {
148            replay::terminate(file).expect("Failed to append to replay file");
149        }
150    }
151
152    fn empty() -> Self {
153        ForkOutput { file: None }
154    }
155
156    fn is_in_fork(&self) -> bool {
157        self.file.is_some()
158    }
159}
160
161#[cfg(not(feature = "fork"))]
162#[derive(Debug)]
163struct ForkOutput;
164
165#[cfg(not(feature = "fork"))]
166impl ForkOutput {
167    fn append(&mut self, _result: &TestCaseResult) {}
168    fn ping(&mut self) {}
169    fn terminate(&mut self) {}
170    fn empty() -> Self {
171        ForkOutput
172    }
173    fn is_in_fork(&self) -> bool {
174        false
175    }
176}
177
178#[cfg(not(feature = "std"))]
179fn call_test<V, F, R>(
180    _runner: &mut TestRunner,
181    case: V,
182    test: &F,
183    replay_from_fork: &mut R,
184    result_cache: &mut dyn ResultCache,
185    _: &mut ForkOutput,
186    is_from_persisted_seed: bool,
187) -> TestCaseResultV2
188where
189    V: fmt::Debug,
190    F: Fn(V) -> TestCaseResult,
191    R: Iterator<Item = TestCaseResult>,
192{
193    if let Some(result) = replay_from_fork.next() {
194        return result.map(|_| TestCaseOk::ReplayFromForkSuccess);
195    }
196
197    let cache_key = result_cache.key(&ResultCacheKey::new(&case));
198    if let Some(result) = result_cache.get(cache_key) {
199        return result.clone().map(|_| TestCaseOk::CacheHitSuccess);
200    }
201
202    let result = test(case);
203    result_cache.put(cache_key, &result);
204    result.map(|_| {
205        if is_from_persisted_seed {
206            TestCaseOk::PersistedCaseSuccess
207        } else {
208            TestCaseOk::NewCaseSuccess
209        }
210    })
211}
212
213#[cfg(feature = "std")]
214fn call_test<V, F, R>(
215    runner: &mut TestRunner,
216    case: V,
217    test: &F,
218    replay_from_fork: &mut R,
219    result_cache: &mut dyn ResultCache,
220    fork_output: &mut ForkOutput,
221    is_from_persisted_seed: bool,
222) -> TestCaseResultV2
223where
224    V: fmt::Debug,
225    F: Fn(V) -> TestCaseResult,
226    R: Iterator<Item = TestCaseResult>,
227{
228    #[cfg(feature = "timeout")]
229    let timeout = runner.config.timeout();
230
231    if let Some(result) = replay_from_fork.next() {
232        return result.map(|_| TestCaseOk::ReplayFromForkSuccess);
233    }
234
235    // Now that we're about to start a new test (as far as the replay system is
236    // concerned), ping the replay file so the parent process can determine
237    // that we made it this far.
238    fork_output.ping();
239
240    verbose_message!(runner, TRACE, "Next test input: {:?}", case);
241
242    let cache_key = result_cache.key(&ResultCacheKey::new(&case));
243    if let Some(result) = result_cache.get(cache_key) {
244        verbose_message!(
245            runner,
246            TRACE,
247            "Test input hit cache, skipping execution"
248        );
249        return result.clone().map(|_| TestCaseOk::CacheHitSuccess);
250    }
251
252    #[cfg(feature = "timeout")]
253    let time_start = std::time::Instant::now();
254
255    let mut result = unwrap_or!(
256        super::scoped_panic_hook::with_hook(
257            |_| { /* Silence out panic backtrace */ },
258            || panic::catch_unwind(AssertUnwindSafe(|| test(case)))
259        ),
260        what => Err(TestCaseError::Fail(
261            what.downcast::<&'static str>().map(|s| (*s).into())
262                .or_else(|what| what.downcast::<String>().map(|b| (*b).into()))
263                .or_else(|what| what.downcast::<Box<str>>().map(|b| (*b).into()))
264                .unwrap_or_else(|_| "<unknown panic value>".into()))));
265
266    // If there is a timeout and we exceeded it, fail the test here so we get
267    // consistent behaviour. (The parent process cannot precisely time the test
268    // cases itself.)
269    #[cfg(feature = "timeout")]
270    if timeout > 0 && result.is_ok() {
271        let elapsed = time_start.elapsed();
272        let elapsed_millis = elapsed.as_secs() as u32 * 1000
273            + elapsed.subsec_nanos() / 1_000_000;
274
275        if elapsed_millis > timeout {
276            result = Err(TestCaseError::fail(format!(
277                "Timeout of {} ms exceeded: test took {} ms",
278                timeout, elapsed_millis
279            )));
280        }
281    }
282
283    result_cache.put(cache_key, &result);
284    fork_output.append(&result);
285
286    match result {
287        Ok(()) => verbose_message!(runner, TRACE, "Test case passed"),
288        Err(TestCaseError::Reject(ref reason)) => {
289            verbose_message!(runner, INFO_LOG, "Test case rejected: {}", reason)
290        }
291        Err(TestCaseError::Fail(ref reason)) => {
292            verbose_message!(runner, INFO_LOG, "Test case failed: {}", reason)
293        }
294    }
295
296    result.map(|_| {
297        if is_from_persisted_seed {
298            TestCaseOk::PersistedCaseSuccess
299        } else {
300            TestCaseOk::NewCaseSuccess
301        }
302    })
303}
304
305type TestRunResult<S> = Result<(), TestError<<S as Strategy>::Value>>;
306
307impl TestRunner {
308    /// Create a fresh `TestRunner` with the given configuration.
309    ///
310    /// The runner will use an RNG with a generated seed and the default
311    /// algorithm.
312    ///
313    /// In `no_std` environments, every `TestRunner` will use the same
314    /// hard-coded seed. This seed is not contractually guaranteed and may be
315    /// changed between releases without notice.
316    pub fn new(config: Config) -> Self {
317        let seed = config.rng_seed;
318        let algorithm = config.rng_algorithm;
319        TestRunner::new_with_rng(config, TestRng::default_rng(seed, algorithm))
320    }
321
322    /// Create a fresh `TestRunner` with the standard deterministic RNG.
323    ///
324    /// This is sugar for the following:
325    ///
326    /// ```rust
327    /// # use proptest::test_runner::*;
328    /// let config = Config::default();
329    /// let algorithm = config.rng_algorithm;
330    /// TestRunner::new_with_rng(
331    ///     config,
332    ///     TestRng::deterministic_rng(algorithm));
333    /// ```
334    ///
335    /// Refer to `TestRng::deterministic_rng()` for more information on the
336    /// properties of the RNG used here.
337    pub fn deterministic() -> Self {
338        let config = Config::default();
339        let algorithm = config.rng_algorithm;
340        TestRunner::new_with_rng(config, TestRng::deterministic_rng(algorithm))
341    }
342
343    /// Create a fresh `TestRunner` with the given configuration and RNG.
344    pub fn new_with_rng(config: Config, rng: TestRng) -> Self {
345        TestRunner {
346            config: config,
347            successes: 0,
348            local_rejects: 0,
349            global_rejects: 0,
350            rng: rng,
351            flat_map_regens: Arc::new(AtomicUsize::new(0)),
352            local_reject_detail: BTreeMap::new(),
353            global_reject_detail: BTreeMap::new(),
354        }
355    }
356
357    /// Create a fresh `TestRunner` with the same config and global counters as
358    /// this one, but with local state reset and an independent `Rng` (but
359    /// deterministic).
360    pub(crate) fn partial_clone(&mut self) -> Self {
361        TestRunner {
362            config: self.config.clone(),
363            successes: 0,
364            local_rejects: 0,
365            global_rejects: 0,
366            rng: self.new_rng(),
367            flat_map_regens: Arc::clone(&self.flat_map_regens),
368            local_reject_detail: BTreeMap::new(),
369            global_reject_detail: BTreeMap::new(),
370        }
371    }
372
373    /// Returns the RNG for this test run.
374    pub fn rng(&mut self) -> &mut TestRng {
375        &mut self.rng
376    }
377
378    /// Create a new, independent but deterministic RNG from the RNG in this
379    /// runner.
380    pub fn new_rng(&mut self) -> TestRng {
381        self.rng.gen_rng()
382    }
383
384    /// Returns the configuration of this runner.
385    pub fn config(&self) -> &Config {
386        &self.config
387    }
388
389    /// Dumps the bytes obtained from the RNG so far (only works if the RNG is
390    /// set to `Recorder`).
391    ///
392    /// ## Panics
393    ///
394    /// Panics if the RNG does not capture generated data.
395    pub fn bytes_used(&self) -> Vec<u8> {
396        self.rng.bytes_used()
397    }
398
399    /// Run test cases against `f`, choosing inputs via `strategy`.
400    ///
401    /// If any failure cases occur, try to find a minimal failure case and
402    /// report that. If invoking `f` panics, the panic is turned into a
403    /// `TestCaseError::Fail`.
404    ///
405    /// If failure persistence is enabled, all persisted failing cases are
406    /// tested first. If a later non-persisted case fails, its seed is
407    /// persisted before returning failure.
408    ///
409    /// Returns success or failure indicating why the test as a whole failed.
410    pub fn run<S: Strategy>(
411        &mut self,
412        strategy: &S,
413        test: impl Fn(S::Value) -> TestCaseResult,
414    ) -> TestRunResult<S> {
415        if self.config.fork() {
416            self.run_in_fork(strategy, test)
417        } else {
418            self.run_in_process(strategy, test)
419        }
420    }
421
422    #[cfg(not(feature = "fork"))]
423    fn run_in_fork<S: Strategy>(
424        &mut self,
425        _: &S,
426        _: impl Fn(S::Value) -> TestCaseResult,
427    ) -> TestRunResult<S> {
428        unreachable!()
429    }
430
431    #[cfg(feature = "fork")]
432    fn run_in_fork<S: Strategy>(
433        &mut self,
434        strategy: &S,
435        test: impl Fn(S::Value) -> TestCaseResult,
436    ) -> TestRunResult<S> {
437        let mut test = Some(test);
438
439        let test_name = rusty_fork::fork_test::fix_module_path(
440            self.config
441                .test_name
442                .expect("Must supply test_name when forking enabled"),
443        );
444        let forkfile: RefCell<Option<tempfile::NamedTempFile>> =
445            RefCell::new(None);
446        let init_forkfile_size = Cell::new(0u64);
447        let seed = self.rng.new_rng_seed();
448        let mut replay = replay::Replay {
449            seed,
450            steps: vec![],
451        };
452        let mut child_count = 0;
453        let timeout = self.config.timeout();
454
455        fn forkfile_size(forkfile: &Option<tempfile::NamedTempFile>) -> u64 {
456            forkfile.as_ref().map_or(0, |ff| {
457                ff.as_file().metadata().map(|md| md.len()).unwrap_or(0)
458            })
459        }
460
461        loop {
462            let (child_error, last_fork_file_len) = rusty_fork::fork(
463                test_name,
464                rusty_fork_id!(),
465                |cmd| {
466                    let mut forkfile = forkfile.borrow_mut();
467                    if forkfile.is_none() {
468                        *forkfile =
469                            Some(tempfile::NamedTempFile::new().expect(
470                                "Failed to create temporary file for fork",
471                            ));
472                        replay.init_file(forkfile.as_mut().unwrap()).expect(
473                            "Failed to initialise temporary file for fork",
474                        );
475                    }
476
477                    init_forkfile_size.set(forkfile_size(&forkfile));
478
479                    cmd.env(ENV_FORK_FILE, forkfile.as_ref().unwrap().path());
480                },
481                |child, _| {
482                    await_child(
483                        child,
484                        &mut forkfile.borrow_mut().as_mut().unwrap(),
485                        timeout,
486                    )
487                },
488                || match self.run_in_process(strategy, test.take().unwrap()) {
489                    Ok(_) => (),
490                    Err(e) => panic!(
491                        "Test failed normally in child process.\n{}\n{}",
492                        e, self
493                    ),
494                },
495            )
496            .expect("Fork failed");
497
498            let parsed = replay::Replay::parse_from(
499                &mut forkfile.borrow_mut().as_mut().unwrap(),
500            )
501            .expect("Failed to re-read fork file");
502            match parsed {
503                replay::ReplayFileStatus::InProgress(new_replay) => {
504                    replay = new_replay
505                }
506                replay::ReplayFileStatus::Terminated(new_replay) => {
507                    replay = new_replay;
508                    break;
509                }
510                replay::ReplayFileStatus::Corrupt => {
511                    panic!("Child process corrupted replay file")
512                }
513            }
514
515            let curr_forkfile_size = forkfile_size(&forkfile.borrow());
516
517            // If the child failed to append *anything* to the forkfile, it
518            // crashed or timed out before starting even one test case, so
519            // bail.
520            if curr_forkfile_size == init_forkfile_size.get() {
521                return Err(TestError::Abort(
522                    "Child process crashed or timed out before the first test \
523                     started running; giving up."
524                        .into(),
525                ));
526            }
527
528            // The child only terminates early if it outright crashes or we
529            // kill it due to timeout, so add a synthetic failure to the
530            // output. But only do this if the length of the fork file is the
531            // same as when we last saw it, or if the child was not killed due
532            // to timeout. (This is because the child could have appended
533            // something to the file after we gave up waiting for it but before
534            // we were able to kill it).
535            if last_fork_file_len.map_or(true, |last_fork_file_len| {
536                last_fork_file_len == curr_forkfile_size
537            }) {
538                let error = Err(child_error.unwrap_or(TestCaseError::fail(
539                    "Child process was terminated abruptly \
540                     but with successful status",
541                )));
542                replay::append(forkfile.borrow_mut().as_mut().unwrap(), &error)
543                    .expect("Failed to append to replay file");
544                replay.steps.push(error);
545            }
546
547            // Bail if we've gone through too many processes in case the
548            // shrinking process itself is crashing.
549            child_count += 1;
550            if child_count >= 10000 {
551                return Err(TestError::Abort(
552                    "Giving up after 10000 child processes crashed".into(),
553                ));
554            }
555        }
556
557        // Run through the steps in-process (without ever running the actual
558        // tests) to produce the shrunken value and update the persistence
559        // file.
560        self.rng.set_seed(replay.seed);
561        self.run_in_process_with_replay(
562            strategy,
563            |_| panic!("Ran past the end of the replay"),
564            replay.steps.into_iter(),
565            ForkOutput::empty(),
566        )
567    }
568
569    fn run_in_process<S: Strategy>(
570        &mut self,
571        strategy: &S,
572        test: impl Fn(S::Value) -> TestCaseResult,
573    ) -> TestRunResult<S> {
574        let (replay_steps, fork_output) = init_replay(&mut self.rng);
575        self.run_in_process_with_replay(
576            strategy,
577            test,
578            replay_steps.into_iter(),
579            fork_output,
580        )
581    }
582
583    fn run_in_process_with_replay<S: Strategy>(
584        &mut self,
585        strategy: &S,
586        test: impl Fn(S::Value) -> TestCaseResult,
587        mut replay_from_fork: impl Iterator<Item = TestCaseResult>,
588        mut fork_output: ForkOutput,
589    ) -> TestRunResult<S> {
590        let old_rng = self.rng.clone();
591
592        let persisted_failure_seeds: Vec<PersistedSeed> = self
593            .config
594            .failure_persistence
595            .as_ref()
596            .map(|f| f.load_persisted_failures2(self.config.source_file))
597            .unwrap_or_default();
598
599        let mut result_cache = self.new_cache();
600
601        for PersistedSeed(persisted_seed) in
602            persisted_failure_seeds.into_iter().rev()
603        {
604            self.rng.set_seed(persisted_seed);
605            self.gen_and_run_case(
606                strategy,
607                &test,
608                &mut replay_from_fork,
609                &mut *result_cache,
610                &mut fork_output,
611                true,
612            )?;
613        }
614        self.rng = old_rng;
615
616        while self.successes < self.config.cases {
617            // Generate a new seed and make an RNG from that so that we know
618            // what seed to persist if this case fails.
619            let seed = self.rng.gen_get_seed();
620            let result = self.gen_and_run_case(
621                strategy,
622                &test,
623                &mut replay_from_fork,
624                &mut *result_cache,
625                &mut fork_output,
626                false,
627            );
628            if let Err(TestError::Fail(_, ref value)) = result {
629                if let Some(ref mut failure_persistence) =
630                    self.config.failure_persistence
631                {
632                    let source_file = &self.config.source_file;
633
634                    // Don't update the persistence file if we're a child
635                    // process. The parent relies on it remaining consistent
636                    // and will take care of updating it itself.
637                    if !fork_output.is_in_fork() {
638                        failure_persistence.save_persisted_failure2(
639                            *source_file,
640                            PersistedSeed(seed),
641                            value,
642                        );
643                    }
644                }
645            }
646
647            if let Err(e) = result {
648                fork_output.terminate();
649                return Err(e.into());
650            }
651        }
652
653        fork_output.terminate();
654        Ok(())
655    }
656
657    fn gen_and_run_case<S: Strategy>(
658        &mut self,
659        strategy: &S,
660        f: &impl Fn(S::Value) -> TestCaseResult,
661        replay_from_fork: &mut impl Iterator<Item = TestCaseResult>,
662        result_cache: &mut dyn ResultCache,
663        fork_output: &mut ForkOutput,
664        is_from_persisted_seed: bool,
665    ) -> TestRunResult<S> {
666        let case = unwrap_or!(strategy.new_tree(self), msg =>
667                return Err(TestError::Abort(msg)));
668
669        // We only count new cases to our set of successful runs against
670        // `PROPTEST_CASES` config.
671        let ok_type = self.run_one_with_replay(
672            case,
673            f,
674            replay_from_fork,
675            result_cache,
676            fork_output,
677            is_from_persisted_seed,
678        )?;
679        match ok_type {
680            TestCaseOk::NewCaseSuccess | TestCaseOk::ReplayFromForkSuccess => {
681                self.successes += 1
682            }
683            TestCaseOk::PersistedCaseSuccess
684            | TestCaseOk::CacheHitSuccess
685            | TestCaseOk::Reject => (),
686        }
687
688        Ok(())
689    }
690
691    /// Run one specific test case against this runner.
692    ///
693    /// If the test fails, finds the minimal failing test case. If the test
694    /// does not fail, returns whether it succeeded or was filtered out.
695    ///
696    /// This does not honour the `fork` config, and will not be able to
697    /// terminate the run if it runs for longer than `timeout`. However, if the
698    /// test function returns but took longer than `timeout`, the test case
699    /// will fail.
700    pub fn run_one<V: ValueTree>(
701        &mut self,
702        case: V,
703        test: impl Fn(V::Value) -> TestCaseResult,
704    ) -> Result<bool, TestError<V::Value>> {
705        let mut result_cache = self.new_cache();
706        self.run_one_with_replay(
707            case,
708            test,
709            &mut iter::empty::<TestCaseResult>().fuse(),
710            &mut *result_cache,
711            &mut ForkOutput::empty(),
712            false,
713        )
714        .map(|ok_type| match ok_type {
715            TestCaseOk::Reject => false,
716            _ => true,
717        })
718    }
719
720    fn run_one_with_replay<V: ValueTree>(
721        &mut self,
722        mut case: V,
723        test: impl Fn(V::Value) -> TestCaseResult,
724        replay_from_fork: &mut impl Iterator<Item = TestCaseResult>,
725        result_cache: &mut dyn ResultCache,
726        fork_output: &mut ForkOutput,
727        is_from_persisted_seed: bool,
728    ) -> Result<TestCaseOk, TestError<V::Value>> {
729        let result = call_test(
730            self,
731            case.current(),
732            &test,
733            replay_from_fork,
734            result_cache,
735            fork_output,
736            is_from_persisted_seed,
737        );
738
739        match result {
740            Ok(success_type) => Ok(success_type),
741            Err(TestCaseError::Fail(why)) => {
742                let why = self
743                    .shrink(
744                        &mut case,
745                        test,
746                        replay_from_fork,
747                        result_cache,
748                        fork_output,
749                        is_from_persisted_seed,
750                    )
751                    .unwrap_or(why);
752                Err(TestError::Fail(why, case.current()))
753            }
754            Err(TestCaseError::Reject(whence)) => {
755                self.reject_global(whence)?;
756                Ok(TestCaseOk::Reject)
757            }
758        }
759    }
760
761    fn shrink<V: ValueTree>(
762        &mut self,
763        case: &mut V,
764        test: impl Fn(V::Value) -> TestCaseResult,
765        replay_from_fork: &mut impl Iterator<Item = TestCaseResult>,
766        result_cache: &mut dyn ResultCache,
767        fork_output: &mut ForkOutput,
768        is_from_persisted_seed: bool,
769    ) -> Option<Reason> {
770        // exit early if shrink disabled
771        if self.config.max_shrink_iters == 0 {
772            verbose_message!(
773                self,
774                INFO_LOG,
775                "Shrinking disabled by configuration"
776            );
777            return None
778        }
779
780        #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
781        let start_time = std::time::Instant::now();
782        let mut last_failure = None;
783        let mut iterations = 0;
784
785        verbose_message!(self, TRACE, "Starting shrinking");
786
787        if case.simplify() {
788            loop {
789                let mut timed_out: Option<u64> = None;
790                #[cfg(all(feature = "std", not(target_arch = "wasm32")))]
791                if self.config.max_shrink_time > 0 {
792                    let elapsed = start_time.elapsed();
793                    let elapsed_ms = elapsed
794                        .as_secs()
795                        .saturating_mul(1000)
796                        .saturating_add(elapsed.subsec_millis().into());
797                    if elapsed_ms > self.config.max_shrink_time as u64 {
798                        timed_out = Some(elapsed_ms);
799                    }
800                }
801
802                let bail = if iterations >= self.config.max_shrink_iters() {
803                    #[cfg(feature = "std")]
804                    const CONTROLLER: &str =
805                        "the PROPTEST_MAX_SHRINK_ITERS environment \
806                         variable or ProptestConfig.max_shrink_iters";
807                    #[cfg(not(feature = "std"))]
808                    const CONTROLLER: &str = "ProptestConfig.max_shrink_iters";
809                    verbose_message!(
810                        self,
811                        ALWAYS,
812                        "Aborting shrinking after {} iterations (set {} \
813                         to a large(r) value to shrink more; current \
814                         configuration: {} iterations)",
815                        CONTROLLER,
816                        self.config.max_shrink_iters(),
817                        iterations
818                    );
819                    true
820                } else if let Some(ms) = timed_out {
821                    #[cfg(feature = "std")]
822                    const CONTROLLER: &str =
823                        "the PROPTEST_MAX_SHRINK_TIME environment \
824                         variable or ProptestConfig.max_shrink_time";
825                    #[cfg(feature = "std")]
826                    let current = self.config.max_shrink_time;
827                    #[cfg(not(feature = "std"))]
828                    const CONTROLLER: &str = "(not configurable in no_std)";
829                    #[cfg(not(feature = "std"))]
830                    let current = 0;
831                    verbose_message!(
832                        self,
833                        ALWAYS,
834                        "Aborting shrinking after taking too long: {} ms \
835                         (set {} to a large(r) value to shrink more; current \
836                         configuration: {} ms)",
837                        ms,
838                        CONTROLLER,
839                        current
840                    );
841                    true
842                } else {
843                    false
844                };
845
846                if bail {
847                    // Move back to the most recent failing case
848                    while case.complicate() {
849                        fork_output.append(&Ok(()));
850                    }
851                    break;
852                }
853
854                iterations += 1;
855
856                let result = call_test(
857                    self,
858                    case.current(),
859                    &test,
860                    replay_from_fork,
861                    result_cache,
862                    fork_output,
863                    is_from_persisted_seed,
864                );
865
866                match result {
867                    // Rejections are effectively a pass here,
868                    // since they indicate that any behaviour of
869                    // the function under test is acceptable.
870                    Ok(_) | Err(TestCaseError::Reject(..)) => {
871                        if !case.complicate() {
872                            verbose_message!(
873                                self,
874                                TRACE,
875                                "Cannot complicate further"
876                            );
877
878                            break;
879                        }
880                    }
881                    Err(TestCaseError::Fail(why)) => {
882                        last_failure = Some(why);
883                        if !case.simplify() {
884                            verbose_message!(
885                                self,
886                                TRACE,
887                                "Cannot simplify further"
888                            );
889
890                            break;
891                        }
892                    }
893                }
894            }
895        }
896
897        last_failure
898    }
899
900    /// Update the state to account for a local rejection from `whence`, and
901    /// return `Ok` if the caller should keep going or `Err` to abort.
902    pub fn reject_local(
903        &mut self,
904        whence: impl Into<Reason>,
905    ) -> Result<(), Reason> {
906        if self.local_rejects >= self.config.max_local_rejects {
907            Err("Too many local rejects".into())
908        } else {
909            self.local_rejects += 1;
910            Self::insert_or_increment(
911                &mut self.local_reject_detail,
912                whence.into(),
913            );
914            Ok(())
915        }
916    }
917
918    /// Update the state to account for a global rejection from `whence`, and
919    /// return `Ok` if the caller should keep going or `Err` to abort.
920    fn reject_global<T>(&mut self, whence: Reason) -> Result<(), TestError<T>> {
921        if self.global_rejects >= self.config.max_global_rejects {
922            Err(TestError::Abort("Too many global rejects".into()))
923        } else {
924            self.global_rejects += 1;
925            Self::insert_or_increment(&mut self.global_reject_detail, whence);
926            Ok(())
927        }
928    }
929
930    /// Insert 1 or increment the rejection detail at key for whence.
931    fn insert_or_increment(into: &mut RejectionDetail, whence: Reason) {
932        into.entry(whence)
933            .and_modify(|count| *count += 1)
934            .or_insert(1);
935    }
936
937    /// Increment the counter of flat map regenerations and return whether it
938    /// is still under the configured limit.
939    pub fn flat_map_regen(&self) -> bool {
940        self.flat_map_regens.fetch_add(1, SeqCst)
941            < self.config.max_flat_map_regens as usize
942    }
943
944    fn new_cache(&self) -> Box<dyn ResultCache> {
945        (self.config.result_cache)()
946    }
947}
948
949#[cfg(feature = "fork")]
950fn init_replay(rng: &mut TestRng) -> (Vec<TestCaseResult>, ForkOutput) {
951    use crate::test_runner::replay::{open_file, Replay, ReplayFileStatus::*};
952
953    if let Some(path) = env::var_os(ENV_FORK_FILE) {
954        let mut file = open_file(&path).expect("Failed to open replay file");
955        let loaded =
956            Replay::parse_from(&mut file).expect("Failed to read replay file");
957        match loaded {
958            InProgress(replay) => {
959                rng.set_seed(replay.seed);
960                (replay.steps, ForkOutput { file: Some(file) })
961            }
962
963            Terminated(_) => {
964                panic!("Replay file for child process is terminated?")
965            }
966
967            Corrupt => panic!("Replay file for child process is corrupt"),
968        }
969    } else {
970        (vec![], ForkOutput::empty())
971    }
972}
973
974#[cfg(not(feature = "fork"))]
975fn init_replay(
976    _rng: &mut TestRng,
977) -> (iter::Empty<TestCaseResult>, ForkOutput) {
978    (iter::empty(), ForkOutput::empty())
979}
980
981#[cfg(feature = "fork")]
982fn await_child_without_timeout(
983    child: &mut rusty_fork::ChildWrapper,
984) -> (Option<TestCaseError>, Option<u64>) {
985    let status = child.wait().expect("Failed to wait for child process");
986
987    if status.success() {
988        (None, None)
989    } else {
990        (
991            Some(TestCaseError::fail(format!(
992                "Child process exited with {}",
993                status
994            ))),
995            None,
996        )
997    }
998}
999
1000#[cfg(all(feature = "fork", not(feature = "timeout")))]
1001fn await_child(
1002    child: &mut rusty_fork::ChildWrapper,
1003    _: &mut tempfile::NamedTempFile,
1004    _timeout: u32,
1005) -> (Option<TestCaseError>, Option<u64>) {
1006    await_child_without_timeout(child)
1007}
1008
1009#[cfg(all(feature = "fork", feature = "timeout"))]
1010fn await_child(
1011    child: &mut rusty_fork::ChildWrapper,
1012    forkfile: &mut tempfile::NamedTempFile,
1013    timeout: u32,
1014) -> (Option<TestCaseError>, Option<u64>) {
1015    use std::time::Duration;
1016
1017    if 0 == timeout {
1018        return await_child_without_timeout(child);
1019    }
1020
1021    // The child can run for longer than the timeout since it may run
1022    // multiple tests. Each time the timeout expires, we check whether the
1023    // file has grown larger. If it has, we allow the child to keep running
1024    // until the next timeout.
1025    let mut last_forkfile_len = forkfile
1026        .as_file()
1027        .metadata()
1028        .map(|md| md.len())
1029        .unwrap_or(0);
1030
1031    loop {
1032        if let Some(status) = child
1033            .wait_timeout(Duration::from_millis(timeout.into()))
1034            .expect("Failed to wait for child process")
1035        {
1036            if status.success() {
1037                return (None, None);
1038            } else {
1039                return (
1040                    Some(TestCaseError::fail(format!(
1041                        "Child process exited with {}",
1042                        status
1043                    ))),
1044                    None,
1045                );
1046            }
1047        }
1048
1049        let current_len = forkfile
1050            .as_file()
1051            .metadata()
1052            .map(|md| md.len())
1053            .unwrap_or(0);
1054        // If we've gone a full timeout period without the file growing,
1055        // fail the test and kill the child.
1056        if current_len <= last_forkfile_len {
1057            return (
1058                Some(TestCaseError::fail(format!(
1059                    "Timed out waiting for child process"
1060                ))),
1061                Some(current_len),
1062            );
1063        } else {
1064            last_forkfile_len = current_len;
1065        }
1066    }
1067}
1068
1069#[cfg(test)]
1070mod test {
1071    use std::cell::Cell;
1072    use std::fs;
1073
1074    use super::*;
1075    use crate::strategy::Strategy;
1076    use crate::test_runner::{FileFailurePersistence, RngAlgorithm, TestRng};
1077
1078    #[test]
1079    fn gives_up_after_too_many_rejections() {
1080        let config = Config::default();
1081        let mut runner = TestRunner::new(config.clone());
1082        let runs = Cell::new(0);
1083        let result = runner.run(&(0u32..), |_| {
1084            runs.set(runs.get() + 1);
1085            Err(TestCaseError::reject("reject"))
1086        });
1087        match result {
1088            Err(TestError::Abort(_)) => (),
1089            e => panic!("Unexpected result: {:?}", e),
1090        }
1091        assert_eq!(config.max_global_rejects + 1, runs.get());
1092    }
1093
1094    #[test]
1095    fn test_pass() {
1096        let mut runner = TestRunner::default();
1097        let result = runner.run(&(1u32..), |v| {
1098            assert!(v > 0);
1099            Ok(())
1100        });
1101        assert_eq!(Ok(()), result);
1102    }
1103
1104    #[test]
1105    fn test_fail_via_result() {
1106        let mut runner = TestRunner::new(Config {
1107            failure_persistence: None,
1108            ..Config::default()
1109        });
1110        let result = runner.run(&(0u32..10u32), |v| {
1111            if v < 5 {
1112                Ok(())
1113            } else {
1114                Err(TestCaseError::fail("not less than 5"))
1115            }
1116        });
1117
1118        assert_eq!(Err(TestError::Fail("not less than 5".into(), 5)), result);
1119    }
1120
1121    #[test]
1122    fn test_fail_via_panic() {
1123        let mut runner = TestRunner::new(Config {
1124            failure_persistence: None,
1125            ..Config::default()
1126        });
1127        let result = runner.run(&(0u32..10u32), |v| {
1128            assert!(v < 5, "not less than 5");
1129            Ok(())
1130        });
1131        assert_eq!(Err(TestError::Fail("not less than 5".into(), 5)), result);
1132    }
1133
1134    #[test]
1135    fn persisted_cases_do_not_count_towards_total_cases() {
1136        const FILE: &'static str = "persistence-test.txt";
1137        let _ = fs::remove_file(FILE);
1138
1139        let config = Config {
1140            failure_persistence: Some(Box::new(
1141                FileFailurePersistence::Direct(FILE),
1142            )),
1143            cases: 1,
1144            ..Config::default()
1145        };
1146
1147        let max = 10_000_000i32;
1148        {
1149            TestRunner::new(config.clone())
1150                .run(&(0i32..max), |_v| {
1151                    Err(TestCaseError::Fail("persist a failure".into()))
1152                })
1153                .expect_err("didn't fail?");
1154        }
1155
1156        let run_count = RefCell::new(0);
1157        TestRunner::new(config.clone())
1158            .run(&(0i32..max), |_v| {
1159                *run_count.borrow_mut() += 1;
1160                Ok(())
1161            })
1162            .expect("should succeed");
1163
1164        // Persisted ran, and a new case ran, and only new case counts
1165        // against `cases: 1`.
1166        assert_eq!(run_count.into_inner(), 2);
1167    }
1168
1169    #[derive(Clone, Copy, PartialEq)]
1170    struct PoorlyBehavedDebug(i32);
1171    impl fmt::Debug for PoorlyBehavedDebug {
1172        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1173            write!(f, "\r\n{:?}\r\n", self.0)
1174        }
1175    }
1176
1177    #[test]
1178    fn failing_cases_persisted_and_reloaded() {
1179        const FILE: &'static str = "persistence-test.txt";
1180        let _ = fs::remove_file(FILE);
1181
1182        let max = 10_000_000i32;
1183        let input = (0i32..max).prop_map(PoorlyBehavedDebug);
1184        let config = Config {
1185            failure_persistence: Some(Box::new(
1186                FileFailurePersistence::Direct(FILE),
1187            )),
1188            ..Config::default()
1189        };
1190
1191        // First test with cases that fail above half max, and then below half
1192        // max, to ensure we can correctly parse both lines of the persistence
1193        // file.
1194        let first_sub_failure = {
1195            TestRunner::new(config.clone())
1196                .run(&input, |v| {
1197                    if v.0 < max / 2 {
1198                        Ok(())
1199                    } else {
1200                        Err(TestCaseError::Fail("too big".into()))
1201                    }
1202                })
1203                .expect_err("didn't fail?")
1204        };
1205        let first_super_failure = {
1206            TestRunner::new(config.clone())
1207                .run(&input, |v| {
1208                    if v.0 >= max / 2 {
1209                        Ok(())
1210                    } else {
1211                        Err(TestCaseError::Fail("too small".into()))
1212                    }
1213                })
1214                .expect_err("didn't fail?")
1215        };
1216        let second_sub_failure = {
1217            TestRunner::new(config.clone())
1218                .run(&input, |v| {
1219                    if v.0 < max / 2 {
1220                        Ok(())
1221                    } else {
1222                        Err(TestCaseError::Fail("too big".into()))
1223                    }
1224                })
1225                .expect_err("didn't fail?")
1226        };
1227        let second_super_failure = {
1228            TestRunner::new(config.clone())
1229                .run(&input, |v| {
1230                    if v.0 >= max / 2 {
1231                        Ok(())
1232                    } else {
1233                        Err(TestCaseError::Fail("too small".into()))
1234                    }
1235                })
1236                .expect_err("didn't fail?")
1237        };
1238
1239        assert_eq!(first_sub_failure, second_sub_failure);
1240        assert_eq!(first_super_failure, second_super_failure);
1241    }
1242
1243    #[test]
1244    fn new_rng_makes_separate_rng() {
1245        use rand::Rng;
1246        let mut runner = TestRunner::default();
1247        let from_1 = runner.new_rng().random::<[u8; 16]>();
1248        let from_2 = runner.rng().random::<[u8; 16]>();
1249        assert_ne!(from_1, from_2);
1250    }
1251
1252    #[test]
1253    fn record_rng_use() {
1254        use rand::Rng;
1255
1256        // create value with recorder rng
1257        let default_config = Config::default();
1258        let recorder_rng = TestRng::default_rng(RngSeed::Random, RngAlgorithm::Recorder);
1259        let mut runner =
1260            TestRunner::new_with_rng(default_config.clone(), recorder_rng);
1261        let random_byte_array1 = runner.rng().random::<[u8; 16]>();
1262        let bytes_used = runner.bytes_used();
1263        assert!(bytes_used.len() >= 16); // could use more bytes for some reason
1264
1265        // re-create value with pass-through rng
1266        let passthrough_rng =
1267            TestRng::from_seed(RngAlgorithm::PassThrough, &bytes_used);
1268        let mut runner =
1269            TestRunner::new_with_rng(default_config, passthrough_rng);
1270        let random_byte_array2 = runner.rng().random::<[u8; 16]>();
1271
1272        // make sure the same value was created
1273        assert_eq!(random_byte_array1, random_byte_array2);
1274    }
1275
1276    #[cfg(feature = "fork")]
1277    #[test]
1278    fn run_successful_test_in_fork() {
1279        let mut runner = TestRunner::new(Config {
1280            fork: true,
1281            test_name: Some(concat!(
1282                module_path!(),
1283                "::run_successful_test_in_fork"
1284            )),
1285            ..Config::default()
1286        });
1287
1288        assert!(runner.run(&(0u32..1000), |_| Ok(())).is_ok());
1289    }
1290
1291    #[cfg(feature = "fork")]
1292    #[test]
1293    fn normal_failure_in_fork_results_in_correct_failure() {
1294        let mut runner = TestRunner::new(Config {
1295            fork: true,
1296            test_name: Some(concat!(
1297                module_path!(),
1298                "::normal_failure_in_fork_results_in_correct_failure"
1299            )),
1300            ..Config::default()
1301        });
1302
1303        let failure = runner
1304            .run(&(0u32..1000), |v| {
1305                prop_assert!(v < 500);
1306                Ok(())
1307            })
1308            .err()
1309            .unwrap();
1310
1311        match failure {
1312            TestError::Fail(_, value) => assert_eq!(500, value),
1313            failure => panic!("Unexpected failure: {:?}", failure),
1314        }
1315    }
1316
1317    #[cfg(feature = "fork")]
1318    #[test]
1319    fn nonsuccessful_exit_finds_correct_failure() {
1320        let mut runner = TestRunner::new(Config {
1321            fork: true,
1322            test_name: Some(concat!(
1323                module_path!(),
1324                "::nonsuccessful_exit_finds_correct_failure"
1325            )),
1326            ..Config::default()
1327        });
1328
1329        let failure = runner
1330            .run(&(0u32..1000), |v| {
1331                if v >= 500 {
1332                    ::std::process::exit(1);
1333                }
1334                Ok(())
1335            })
1336            .err()
1337            .unwrap();
1338
1339        match failure {
1340            TestError::Fail(_, value) => assert_eq!(500, value),
1341            failure => panic!("Unexpected failure: {:?}", failure),
1342        }
1343    }
1344
1345    #[cfg(feature = "fork")]
1346    #[test]
1347    fn spurious_exit_finds_correct_failure() {
1348        let mut runner = TestRunner::new(Config {
1349            fork: true,
1350            test_name: Some(concat!(
1351                module_path!(),
1352                "::spurious_exit_finds_correct_failure"
1353            )),
1354            ..Config::default()
1355        });
1356
1357        let failure = runner
1358            .run(&(0u32..1000), |v| {
1359                if v >= 500 {
1360                    ::std::process::exit(0);
1361                }
1362                Ok(())
1363            })
1364            .err()
1365            .unwrap();
1366
1367        match failure {
1368            TestError::Fail(_, value) => assert_eq!(500, value),
1369            failure => panic!("Unexpected failure: {:?}", failure),
1370        }
1371    }
1372
1373    #[cfg(feature = "timeout")]
1374    #[test]
1375    fn long_sleep_timeout_finds_correct_failure() {
1376        let mut runner = TestRunner::new(Config {
1377            fork: true,
1378            timeout: 500,
1379            test_name: Some(concat!(
1380                module_path!(),
1381                "::long_sleep_timeout_finds_correct_failure"
1382            )),
1383            ..Config::default()
1384        });
1385
1386        let failure = runner
1387            .run(&(0u32..1000), |v| {
1388                if v >= 500 {
1389                    ::std::thread::sleep(::std::time::Duration::from_millis(
1390                        10_000,
1391                    ));
1392                }
1393                Ok(())
1394            })
1395            .err()
1396            .unwrap();
1397
1398        match failure {
1399            TestError::Fail(_, value) => assert_eq!(500, value),
1400            failure => panic!("Unexpected failure: {:?}", failure),
1401        }
1402    }
1403
1404    #[cfg(feature = "timeout")]
1405    #[test]
1406    fn mid_sleep_timeout_finds_correct_failure() {
1407        let mut runner = TestRunner::new(Config {
1408            fork: true,
1409            timeout: 500,
1410            test_name: Some(concat!(
1411                module_path!(),
1412                "::mid_sleep_timeout_finds_correct_failure"
1413            )),
1414            ..Config::default()
1415        });
1416
1417        let failure = runner
1418            .run(&(0u32..1000), |v| {
1419                if v >= 500 {
1420                    // Sleep a little longer than the timeout. This means that
1421                    // sometimes the test case itself will return before the parent
1422                    // process has noticed the child is timing out, so it's up to
1423                    // the child to mark it as a failure.
1424                    ::std::thread::sleep(::std::time::Duration::from_millis(
1425                        600,
1426                    ));
1427                } else {
1428                    // Sleep a bit so that the parent and child timing don't stay
1429                    // in sync.
1430                    ::std::thread::sleep(::std::time::Duration::from_millis(
1431                        100,
1432                    ))
1433                }
1434                Ok(())
1435            })
1436            .err()
1437            .unwrap();
1438
1439        match failure {
1440            TestError::Fail(_, value) => assert_eq!(500, value),
1441            failure => panic!("Unexpected failure: {:?}", failure),
1442        }
1443    }
1444
1445    #[cfg(feature = "std")]
1446    #[test]
1447    fn duplicate_tests_not_run_with_basic_result_cache() {
1448        use std::cell::{Cell, RefCell};
1449        use std::collections::HashSet;
1450        use std::rc::Rc;
1451
1452        for _ in 0..256 {
1453            let mut runner = TestRunner::new(Config {
1454                failure_persistence: None,
1455                result_cache:
1456                    crate::test_runner::result_cache::basic_result_cache,
1457                ..Config::default()
1458            });
1459            let pass = Rc::new(Cell::new(true));
1460            let seen = Rc::new(RefCell::new(HashSet::new()));
1461            let result =
1462                runner.run(&(0u32..65536u32).prop_map(|v| v % 10), |val| {
1463                    if !seen.borrow_mut().insert(val) {
1464                        println!("Value {} seen more than once", val);
1465                        pass.set(false);
1466                    }
1467
1468                    prop_assert!(val <= 5);
1469                    Ok(())
1470                });
1471
1472            assert!(pass.get());
1473            if let Err(TestError::Fail(_, val)) = result {
1474                assert_eq!(6, val);
1475            } else {
1476                panic!("Incorrect result: {:?}", result);
1477            }
1478        }
1479    }
1480}
1481
1482#[cfg(all(feature = "fork", feature = "timeout", test))]
1483mod timeout_tests {
1484    use core::u32;
1485    use std::thread;
1486    use std::time::Duration;
1487
1488    use super::*;
1489
1490    rusty_fork_test! {
1491        #![rusty_fork(timeout_ms = 4_000)]
1492
1493        #[test]
1494        fn max_shrink_iters_works() {
1495            test_shrink_bail(Config {
1496                max_shrink_iters: 5,
1497                .. Config::default()
1498            });
1499        }
1500
1501        #[test]
1502        fn max_shrink_time_works() {
1503            test_shrink_bail(Config {
1504                max_shrink_time: 1000,
1505                .. Config::default()
1506            });
1507        }
1508
1509        #[test]
1510        fn max_shrink_iters_works_with_forking() {
1511            test_shrink_bail(Config {
1512                fork: true,
1513                test_name: Some(
1514                    concat!(module_path!(),
1515                            "::max_shrink_iters_works_with_forking")),
1516                max_shrink_time: 1000,
1517                .. Config::default()
1518            });
1519        }
1520
1521        #[test]
1522        fn detects_child_failure_to_start() {
1523            let mut runner = TestRunner::new(Config {
1524                timeout: 100,
1525                test_name: Some(
1526                    concat!(module_path!(),
1527                            "::detects_child_failure_to_start")),
1528                .. Config::default()
1529            });
1530            let result = runner.run(&Just(()).prop_map(|()| {
1531                thread::sleep(Duration::from_millis(200))
1532            }), Ok);
1533
1534            if let Err(TestError::Abort(_)) = result {
1535                // OK
1536            } else {
1537                panic!("Unexpected result: {:?}", result);
1538            }
1539        }
1540    }
1541
1542    fn test_shrink_bail(config: Config) {
1543        let mut runner = TestRunner::new(config);
1544        let result = runner.run(&crate::num::u64::ANY, |v| {
1545            thread::sleep(Duration::from_millis(250));
1546            prop_assert!(v <= u32::MAX as u64);
1547            Ok(())
1548        });
1549
1550        if let Err(TestError::Fail(_, value)) = result {
1551            // Ensure the final value was in fact a failing case.
1552            assert!(value > u32::MAX as u64);
1553        } else {
1554            panic!("Unexpected result: {:?}", result);
1555        }
1556    }
1557}