Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions kani-driver/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ pub struct VerificationArgs {
#[arg(long)]
pub harness_timeout: Option<Timeout>,

/// Stop the verification process as soon as one of the harnesses fails.
#[arg(long)]
pub fail_fast: bool,

/// Arguments to pass down to Cargo
#[command(flatten)]
pub cargo: CargoCommonArgs,
Expand Down
46 changes: 40 additions & 6 deletions kani-driver/src/harness_runner.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT

use anyhow::{Result, bail};
use anyhow::{Error, Result, bail};
use kani_metadata::{ArtifactType, HarnessMetadata};
use rayon::prelude::*;
use std::fs::File;
Expand Down Expand Up @@ -34,6 +34,20 @@ pub(crate) struct HarnessResult<'pr> {
pub result: VerificationResult,
}

#[derive(Debug)]
struct FailFastHarnessInfo {
pub index_to_failing_harness: usize,
pub result: VerificationResult,
}

impl std::error::Error for FailFastHarnessInfo {}

impl std::fmt::Display for FailFastHarnessInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "harness failed")
}
}

impl<'pr> HarnessRunner<'_, 'pr> {
/// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs
/// the proof-checking process for each harness in `harnesses`.
Expand All @@ -55,7 +69,8 @@ impl<'pr> HarnessRunner<'_, 'pr> {
let results = pool.install(|| -> Result<Vec<HarnessResult<'pr>>> {
sorted_harnesses
.par_iter()
.map(|harness| -> Result<HarnessResult<'pr>> {
.enumerate()
.map(|(idx, harness)| -> Result<HarnessResult<'pr>> {
let goto_file =
self.project.get_harness_artifact(&harness, ArtifactType::Goto).unwrap();

Expand All @@ -66,12 +81,31 @@ impl<'pr> HarnessRunner<'_, 'pr> {
}

let result = self.sess.check_harness(goto_file, harness)?;
Ok(HarnessResult { harness, result })
if self.sess.args.fail_fast && result.status == VerificationStatus::Failure {
Err(Error::new(FailFastHarnessInfo {
index_to_failing_harness: idx,
result,
}))
} else {
Ok(HarnessResult { harness, result })
}
})
.collect::<Result<Vec<_>>>()
})?;

Ok(results)
});
match results {
Ok(results) => Ok(results),
Err(err) => {
if err.is::<FailFastHarnessInfo>() {
let failed = err.downcast::<FailFastHarnessInfo>().unwrap();
Ok(vec![HarnessResult {
harness: sorted_harnesses[failed.index_to_failing_harness],
result: failed.result,
}])
} else {
Err(err)
}
}
}
}

/// Return an error if the user is trying to verify a harness with stubs without enabling the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Complete - 0 successfully verified harnesses, 1 failures, 1 total.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
// kani-flags: --fail-fast
//! Ensure that the verification process stops as soon as one of the harnesses fails.

mod tests {
#[kani::proof]
fn test_01_fail() {
assert!(false, "First failure");
}

#[kani::proof]
fn test_02_fail() {
assert!(false, "Second failure");
}

#[kani::proof]
fn test_03_fail() {
assert!(false, "Third failure");
}

#[kani::proof]
fn test_04_fail() {
assert!(false, "Fourth failure");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Complete - 0 successfully verified harnesses, 1 failures, 1 total.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
// kani-flags: --fail-fast -Z unstable-options --jobs 4 --output-format=terse
//! Ensure that the verification process stops as soon as one of the harnesses fails.
//! This test runs on 4 parallel threads. Stops verification as soon as a harness on any of the threads fails.

mod tests {
#[kani::proof]
fn test_01_fail() {
assert!(false, "First failure");
}

#[kani::proof]
fn test_02_fail() {
assert!(false, "Second failure");
}

#[kani::proof]
fn test_03_fail() {
assert!(false, "Third failure");
}

#[kani::proof]
fn test_04_fail() {
assert!(false, "Fourth failure");
}

#[kani::proof]
fn test_05_fail() {
assert!(false, "Fifth failure");
}

#[kani::proof]
fn test_06_fail() {
assert!(false, "Sixth failure");
}

#[kani::proof]
fn test_07_fail() {
assert!(false, "Seventh failure");
}

#[kani::proof]
fn test_08_fail() {
assert!(false, "Eighth failure");
}

#[kani::proof]
fn test_09_fail() {
assert!(false, "Ninth failure");
}

#[kani::proof]
fn test_10_fail() {
assert!(false, "Tenth failure");
}
}
Loading