Skip to content

Commit 4f78926

Browse files
Fast fail feature - Stops verification process as soon as one failure is observed - Use case : CI speed up (#3879)
Resolves #3458 Added the option `--fail-fast`, which would stop the verification process whenever any of the harnesses fails, and returns the failed harness as an error. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Zyad Hassan <[email protected]>
1 parent 759e2c5 commit 4f78926

File tree

6 files changed

+129
-6
lines changed

6 files changed

+129
-6
lines changed

kani-driver/src/args/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ pub struct VerificationArgs {
334334
#[arg(long)]
335335
pub harness_timeout: Option<Timeout>,
336336

337+
/// Stop the verification process as soon as one of the harnesses fails.
338+
#[arg(long)]
339+
pub fail_fast: bool,
340+
337341
/// Arguments to pass down to Cargo
338342
#[command(flatten)]
339343
pub cargo: CargoCommonArgs,

kani-driver/src/harness_runner.rs

+40-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
use anyhow::{Result, bail};
4+
use anyhow::{Error, Result, bail};
55
use kani_metadata::{ArtifactType, HarnessMetadata};
66
use rayon::prelude::*;
77
use std::fs::File;
@@ -34,6 +34,20 @@ pub(crate) struct HarnessResult<'pr> {
3434
pub result: VerificationResult,
3535
}
3636

37+
#[derive(Debug)]
38+
struct FailFastHarnessInfo {
39+
pub index_to_failing_harness: usize,
40+
pub result: VerificationResult,
41+
}
42+
43+
impl std::error::Error for FailFastHarnessInfo {}
44+
45+
impl std::fmt::Display for FailFastHarnessInfo {
46+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47+
write!(f, "harness failed")
48+
}
49+
}
50+
3751
impl<'pr> HarnessRunner<'_, 'pr> {
3852
/// Given a [`HarnessRunner`] (to abstract over how these harnesses were generated), this runs
3953
/// the proof-checking process for each harness in `harnesses`.
@@ -55,7 +69,8 @@ impl<'pr> HarnessRunner<'_, 'pr> {
5569
let results = pool.install(|| -> Result<Vec<HarnessResult<'pr>>> {
5670
sorted_harnesses
5771
.par_iter()
58-
.map(|harness| -> Result<HarnessResult<'pr>> {
72+
.enumerate()
73+
.map(|(idx, harness)| -> Result<HarnessResult<'pr>> {
5974
let goto_file =
6075
self.project.get_harness_artifact(&harness, ArtifactType::Goto).unwrap();
6176

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

6883
let result = self.sess.check_harness(goto_file, harness)?;
69-
Ok(HarnessResult { harness, result })
84+
if self.sess.args.fail_fast && result.status == VerificationStatus::Failure {
85+
Err(Error::new(FailFastHarnessInfo {
86+
index_to_failing_harness: idx,
87+
result,
88+
}))
89+
} else {
90+
Ok(HarnessResult { harness, result })
91+
}
7092
})
7193
.collect::<Result<Vec<_>>>()
72-
})?;
73-
74-
Ok(results)
94+
});
95+
match results {
96+
Ok(results) => Ok(results),
97+
Err(err) => {
98+
if err.is::<FailFastHarnessInfo>() {
99+
let failed = err.downcast::<FailFastHarnessInfo>().unwrap();
100+
Ok(vec![HarnessResult {
101+
harness: sorted_harnesses[failed.index_to_failing_harness],
102+
result: failed.result,
103+
}])
104+
} else {
105+
Err(err)
106+
}
107+
}
108+
}
75109
}
76110

77111
/// Return an error if the user is trying to verify a harness with stubs without enabling the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Complete - 0 successfully verified harnesses, 1 failures, 1 total.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --fail-fast
4+
//! Ensure that the verification process stops as soon as one of the harnesses fails.
5+
6+
mod tests {
7+
#[kani::proof]
8+
fn test_01_fail() {
9+
assert!(false, "First failure");
10+
}
11+
12+
#[kani::proof]
13+
fn test_02_fail() {
14+
assert!(false, "Second failure");
15+
}
16+
17+
#[kani::proof]
18+
fn test_03_fail() {
19+
assert!(false, "Third failure");
20+
}
21+
22+
#[kani::proof]
23+
fn test_04_fail() {
24+
assert!(false, "Fourth failure");
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Complete - 0 successfully verified harnesses, 1 failures, 1 total.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// kani-flags: --fail-fast -Z unstable-options --jobs 4 --output-format=terse
4+
//! Ensure that the verification process stops as soon as one of the harnesses fails.
5+
//! This test runs on 4 parallel threads. Stops verification as soon as a harness on any of the threads fails.
6+
7+
mod tests {
8+
#[kani::proof]
9+
fn test_01_fail() {
10+
assert!(false, "First failure");
11+
}
12+
13+
#[kani::proof]
14+
fn test_02_fail() {
15+
assert!(false, "Second failure");
16+
}
17+
18+
#[kani::proof]
19+
fn test_03_fail() {
20+
assert!(false, "Third failure");
21+
}
22+
23+
#[kani::proof]
24+
fn test_04_fail() {
25+
assert!(false, "Fourth failure");
26+
}
27+
28+
#[kani::proof]
29+
fn test_05_fail() {
30+
assert!(false, "Fifth failure");
31+
}
32+
33+
#[kani::proof]
34+
fn test_06_fail() {
35+
assert!(false, "Sixth failure");
36+
}
37+
38+
#[kani::proof]
39+
fn test_07_fail() {
40+
assert!(false, "Seventh failure");
41+
}
42+
43+
#[kani::proof]
44+
fn test_08_fail() {
45+
assert!(false, "Eighth failure");
46+
}
47+
48+
#[kani::proof]
49+
fn test_09_fail() {
50+
assert!(false, "Ninth failure");
51+
}
52+
53+
#[kani::proof]
54+
fn test_10_fail() {
55+
assert!(false, "Tenth failure");
56+
}
57+
}

0 commit comments

Comments
 (0)