Repository: bitcoin/bitcoin
File: contrib/devtools/deterministic-fuzz-coverage/src/main.rs (Rust)

Code before:
        "Check each fuzz input individually ... ({} inputs with parallelism {par})",
        entries.len()
    );
    let check_individual = |entry: &DirEntry, thread_id: usize| -> AppResult {
        let entry = entry.path();
        if !entry.is_file() {
            Err(format!("{} should be a file", entry.display()))?;
        }
        let cov_txt_base = run_single(0, &entry, thread_id)?;
        let cov_txt_repeat = run_single(1, &entry, thread_id)?;
        check_diff(
            &cov_txt_base,
            &cov_txt_repeat,
            &format!("The fuzz target input was {}.", entry.display()),
        )?;
        Ok(())
    };
    thread::scope(|s| -> AppResult {
        let mut handles = VecDeque::with_capacity(par);
        let mut res = Ok(());
        let set_err = |res: &mut _, handle: ScopedJoinHandle<_>| {
            let r = match handle.join() {
                Err(_e) => Err("a scoped thread panicked".to_string()),
                Ok(r) => r,
            };
            if r.is_err() {
                *res = r;
            }
        };
        for (i, entry) in entries.iter().enumerate() {
            println!("[{i}/{}]", entries.len());
            handles.push_back(s.spawn(move || check_individual(entry, i % par)));
            if handles.len() >= par {
                let h = handles.pop_front().expect("unreachable: must not be empty");
                set_err(&mut res, h);
                if res.is_err() {
                    break;
                }
            }
        }
        for h in handles {
            set_err(&mut res, h);
        }
        res
    })?;
    // Finally, check that running over all fuzz inputs in one process is deterministic as well.
    // This can catch issues where mutable global state is leaked from one fuzz input execution to
    // the next.
    println!("Check all fuzz inputs in one go ...");
    {
        if !corpus_dir.is_dir() {

Review comment (bug):
Off-by-one:
```suggestion
            println!("[{}/{}]", i + 1, entries.len());
```

Code after:
    };
    // First, check that each fuzz input is deterministic running by itself in a process.
    //
    // This can catch issues and isolate where a single fuzz input triggers non-determinism, but
    // all other fuzz inputs are deterministic.
    //
    // Also, This can catch issues where several fuzz inputs are non-deterministic, but the sum of
    // their overall coverage trace remains the same across runs and thus remains undetected.
    println!(
        "Check each fuzz input individually ... ({} inputs with parallelism {par})",
        entries.len()
    );
    let check_individual = |entry: &DirEntry, thread_id: usize| -> AppResult {
        let entry = entry.path();
        if !entry.is_file() {
            Err(format!("{} should be a file", entry.display()))?;
        }
        let cov_txt_base = run_single('a', &entry, thread_id)?;
        let cov_txt_repeat = run_single('b', &entry, thread_id)?;
        check_diff(
            &cov_txt_base,
            &cov_txt_repeat,
            &format!("The fuzz target input was {}.", entry.display()),
        )?;
        Ok(())
    };
    thread::scope(|s| -> AppResult {
        let mut handles = VecDeque::with_capacity(par);
        let mut res = Ok(());
        for (i, entry) in entries.iter().enumerate() {
            println!("[{}/{}]", i + 1, entries.len());
            handles.push_back(s.spawn(move || check_individual(entry, i % par)));
            while handles.len() >= par || i == (entries.len() - 1) || res.is_err() {
                if let Some(th) = handles.pop_front() {
                    let thread_result = match th.join() {
                        Err(_e) => Err("A scoped thread panicked".to_string()),
                        Ok(r) => r,
                    };
                    if thread_result.is_err() {
                        res = thread_result;
                    }
                } else {
                    return res;
                }
            }
        }
        res
    })?;
    // Finally, check that running over all fuzz inputs in one process is deterministic as well.
    // This can catch issues where mutable global state is leaked from one fuzz input execution to
    // the next.