Skip to content

Commit e4f989b

Browse files
authored
Polish cargo test arguments + add test (rust-lang#2492)
This is a continuation from rust-lang#2491. For completeness, I'm adding --bin option. I've also added a test for these options. Finally, I moved cargo specific arguments to its own module.
1 parent 87047cf commit e4f989b

File tree

11 files changed

+290
-108
lines changed

11 files changed

+290
-108
lines changed

kani-driver/src/args/cargo.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//! Module that define parsers that mimic Cargo options.
4+
5+
use crate::args::ValidateArgs;
6+
use clap::error::Error;
7+
use std::ffi::OsString;
8+
use std::path::PathBuf;
9+
10+
/// Arguments that Kani pass down into Cargo essentially uninterpreted.
11+
/// These generally have to do with selection of packages or activation of features.
12+
/// These do not (currently) include cargo args that kani pays special attention to:
13+
/// for instance, we keep `--tests` and `--target-dir` elsewhere.
14+
#[derive(Debug, Default, clap::Args)]
15+
pub struct CargoCommonArgs {
16+
/// Activate all package features
17+
#[arg(long)]
18+
pub all_features: bool,
19+
/// Do not activate the `default` feature
20+
#[arg(long)]
21+
pub no_default_features: bool,
22+
23+
// This tolerates spaces too, but we say "comma" only because this is the least error-prone approach...
24+
/// Comma separated list of package features to activate
25+
#[arg(short = 'F', long)]
26+
features: Vec<String>,
27+
28+
/// Path to Cargo.toml
29+
#[arg(long, name = "PATH")]
30+
pub manifest_path: Option<PathBuf>,
31+
32+
/// Build all packages in the workspace
33+
#[arg(long)]
34+
pub workspace: bool,
35+
36+
/// Run Kani on the specified packages.
37+
#[arg(long, short, conflicts_with("workspace"), num_args(1..))]
38+
pub package: Vec<String>,
39+
40+
/// Exclude the specified packages
41+
#[arg(long, short, requires("workspace"), conflicts_with("package"), num_args(1..))]
42+
pub exclude: Vec<String>,
43+
}
44+
45+
impl CargoCommonArgs {
46+
/// Parse the string we're given into a list of feature names
47+
///
48+
/// clap can't do this for us because it accepts multiple different delimeters
49+
pub fn features(&self) -> Vec<String> {
50+
let mut result = Vec::new();
51+
52+
for s in &self.features {
53+
for piece in s.split(&[' ', ',']) {
54+
result.push(piece.to_owned());
55+
}
56+
}
57+
result
58+
}
59+
60+
/// Convert the arguments back to a format that cargo can understand.
61+
/// Note that the `exclude` option requires special processing and it's not included here.
62+
pub fn to_cargo_args(&self) -> Vec<OsString> {
63+
let mut cargo_args: Vec<OsString> = vec![];
64+
if self.all_features {
65+
cargo_args.push("--all-features".into());
66+
}
67+
68+
if self.no_default_features {
69+
cargo_args.push("--no-default-features".into());
70+
}
71+
72+
let features = self.features();
73+
if !features.is_empty() {
74+
cargo_args.push(format!("--features={}", features.join(",")).into());
75+
}
76+
77+
if let Some(path) = &self.manifest_path {
78+
cargo_args.push("--manifest-path".into());
79+
cargo_args.push(path.into());
80+
}
81+
if self.workspace {
82+
cargo_args.push("--workspace".into())
83+
}
84+
85+
cargo_args.extend(self.package.iter().map(|pkg| format!("-p={pkg}").into()));
86+
cargo_args
87+
}
88+
}
89+
90+
/// Leave it for Cargo to validate these for now.
91+
impl ValidateArgs for CargoCommonArgs {
92+
fn validate(&self) -> Result<(), Error> {
93+
Ok(())
94+
}
95+
}
96+
97+
/// Arguments that Kani pass down into Cargo test essentially uninterpreted.
98+
#[derive(Debug, Default, clap::Args)]
99+
pub struct CargoTestArgs {
100+
/// Test only the specified binary target.
101+
#[arg(long)]
102+
pub bin: Vec<String>,
103+
104+
/// Test all binaries.
105+
#[arg(long)]
106+
pub bins: bool,
107+
108+
/// Test only the package's library unit tests.
109+
#[arg(long)]
110+
pub lib: bool,
111+
112+
/// Arguments to pass down to Cargo
113+
#[command(flatten)]
114+
pub common: CargoCommonArgs,
115+
}
116+
117+
impl CargoTestArgs {
118+
/// Convert the arguments back to a format that cargo can understand.
119+
pub fn to_cargo_args(&self) -> Vec<OsString> {
120+
let mut cargo_args = self.common.to_cargo_args();
121+
122+
if self.bins {
123+
cargo_args.push("--bins".into());
124+
}
125+
126+
if self.lib {
127+
cargo_args.push("--lib".into());
128+
}
129+
130+
cargo_args.extend(self.bin.iter().map(|binary| format!("--bin={binary}").into()));
131+
cargo_args
132+
}
133+
}
134+
135+
/// Leave it for Cargo to validate these for now.
136+
impl ValidateArgs for CargoTestArgs {
137+
fn validate(&self) -> Result<(), Error> {
138+
self.common.validate()
139+
}
140+
}

kani-driver/src/args/mod.rs

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
//! Module that define Kani's command line interface. This includes all subcommands.
44
55
pub mod assess_args;
6+
pub mod cargo;
67
pub mod common;
78
pub mod playback_args;
89

910
pub use assess_args::*;
1011

1112
use self::common::*;
1213
use crate::util::warning;
14+
use cargo::CargoCommonArgs;
1315
use clap::builder::{PossibleValue, TypedValueParser};
1416
use clap::{error::ContextKind, error::ContextValue, error::Error, error::ErrorKind, ValueEnum};
1517
use kani_metadata::CbmcSolver;
@@ -295,7 +297,7 @@ pub struct VerificationArgs {
295297

296298
/// Arguments to pass down to Cargo
297299
#[command(flatten)]
298-
pub cargo: CargoArgs,
300+
pub cargo: CargoCommonArgs,
299301

300302
#[command(flatten)]
301303
pub common_args: CommonArgs,
@@ -331,93 +333,6 @@ impl VerificationArgs {
331333
}
332334
}
333335

334-
/// Arguments that Kani pass down into Cargo essentially uninterpreted.
335-
/// These generally have to do with selection of packages or activation of features.
336-
/// These do not (currently) include cargo args that kani pays special attention to:
337-
/// for instance, we keep `--tests` and `--target-dir` elsewhere.
338-
#[derive(Debug, Default, clap::Args)]
339-
pub struct CargoArgs {
340-
/// Activate all package features
341-
#[arg(long)]
342-
pub all_features: bool,
343-
/// Do not activate the `default` feature
344-
#[arg(long)]
345-
pub no_default_features: bool,
346-
347-
// This tolerates spaces too, but we say "comma" only because this is the least error-prone approach...
348-
/// Comma separated list of package features to activate
349-
#[arg(short = 'F', long)]
350-
features: Vec<String>,
351-
352-
/// Path to Cargo.toml
353-
#[arg(long, name = "PATH")]
354-
pub manifest_path: Option<PathBuf>,
355-
356-
/// Build all packages in the workspace
357-
#[arg(long)]
358-
pub workspace: bool,
359-
360-
/// Run Kani on the specified packages.
361-
#[arg(long, short, conflicts_with("workspace"), num_args(1..))]
362-
pub package: Vec<String>,
363-
364-
/// Exclude the specified packages
365-
#[arg(long, short, requires("workspace"), conflicts_with("package"), num_args(1..))]
366-
pub exclude: Vec<String>,
367-
}
368-
369-
impl CargoArgs {
370-
/// Parse the string we're given into a list of feature names
371-
///
372-
/// clap can't do this for us because it accepts multiple different delimeters
373-
pub fn features(&self) -> Vec<String> {
374-
let mut result = Vec::new();
375-
376-
for s in &self.features {
377-
for piece in s.split(&[' ', ',']) {
378-
result.push(piece.to_owned());
379-
}
380-
}
381-
result
382-
}
383-
384-
/// Convert the arguments back to a format that cargo can understand.
385-
/// Note that the `exclude` option requires special processing and it's not included here.
386-
pub fn to_cargo_args(&self) -> Vec<OsString> {
387-
let mut cargo_args: Vec<OsString> = vec![];
388-
if self.all_features {
389-
cargo_args.push("--all-features".into());
390-
}
391-
392-
if self.no_default_features {
393-
cargo_args.push("--no-default-features".into());
394-
}
395-
396-
let features = self.features();
397-
if !features.is_empty() {
398-
cargo_args.push(format!("--features={}", features.join(",")).into());
399-
}
400-
401-
if let Some(path) = &self.manifest_path {
402-
cargo_args.push("--manifest-path".into());
403-
cargo_args.push(path.into());
404-
}
405-
if self.workspace {
406-
cargo_args.push("--workspace".into())
407-
}
408-
409-
cargo_args.extend(self.package.iter().map(|pkg| format!("-p={pkg}").into()));
410-
cargo_args
411-
}
412-
}
413-
414-
/// Leave it for Cargo to validate these for now.
415-
impl ValidateArgs for CargoArgs {
416-
fn validate(&self) -> Result<(), Error> {
417-
Ok(())
418-
}
419-
}
420-
421336
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
422337
pub enum ConcretePlaybackMode {
423338
Print,

kani-driver/src/args/playback_args.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
//! Implements the subcommand handling of the playback subcommand
44
5+
use crate::args::cargo::CargoTestArgs;
56
use crate::args::common::UnstableFeatures;
6-
use crate::args::{CargoArgs, CommonArgs, ValidateArgs};
7+
use crate::args::{CommonArgs, ValidateArgs};
78
use clap::error::ErrorKind;
89
use clap::{Error, Parser, ValueEnum};
910
use std::path::PathBuf;
@@ -14,17 +15,9 @@ pub struct CargoPlaybackArgs {
1415
#[command(flatten)]
1516
pub playback: PlaybackArgs,
1617

17-
/// Test all binaries.
18-
#[arg(long)]
19-
pub bins: bool,
20-
21-
/// Test only the package's library unit tests.
22-
#[arg(long)]
23-
pub lib: bool,
24-
25-
/// Arguments to pass down to Cargo
18+
/// Arguments to pass down to Cargo that are specific to tests.
2619
#[command(flatten)]
27-
pub cargo: CargoArgs,
20+
pub cargo: CargoTestArgs,
2821
}
2922

3023
/// Execute concrete playback testcases of a local crate.
@@ -126,7 +119,7 @@ mod tests {
126119
let input = "playback -Z concrete-playback -p PKG_NAME".split_whitespace();
127120
let args = CargoPlaybackArgs::try_parse_from(input).unwrap();
128121
args.validate().unwrap();
129-
assert_eq!(&args.cargo.package, &["PKG_NAME"])
122+
assert_eq!(&args.cargo.common.package, &["PKG_NAME"])
130123
}
131124

132125
#[test]

kani-driver/src/concrete_playback/playback.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,6 @@ fn cargo_test(install: &InstallType, args: CargoPlaybackArgs) -> Result<()> {
112112
cargo_args.push("--no-run".into());
113113
}
114114

115-
if args.bins {
116-
cargo_args.push("--bins".into());
117-
}
118-
119-
if args.lib {
120-
cargo_args.push("--lib".into());
121-
}
122-
123115
cargo_args.append(&mut args.cargo.to_cargo_args());
124116
cargo_args.push("--target".into());
125117
cargo_args.push(env!("TARGET").into());
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
script: playback_target.sh
4+
expected: playback_target.expected
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[TEST] Run all...\
2+
test verify::kani_concrete_playback_cover_bar_1234\
3+
test verify::kani_concrete_playback_cover_foo_1234\
4+
test verify::kani_concrete_playback_cover_lib_1234
5+
6+
[TEST] Run lib...\
7+
test verify::kani_concrete_playback_cover_lib_1234
8+
9+
[TEST] Run bins...\
10+
test verify::kani_concrete_playback_cover_bar_1234\
11+
test verify::kani_concrete_playback_cover_foo_1234
12+
13+
[TEST] Only foo tests...\
14+
test verify::kani_concrete_playback_cover_foo_1234
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env bash
2+
# Copyright Kani Contributors
3+
# SPDX-License-Identifier: Apache-2.0 OR MIT
4+
5+
set +e
6+
7+
function check_playback {
8+
local OUTPUT=output.log
9+
cargo kani playback "${@}" >& $OUTPUT
10+
# Sort output so we can rely on the order.
11+
echo "$(grep "test verify::.* ok" $OUTPUT | sort)"
12+
echo
13+
echo "======= Raw Output ======="
14+
cat $OUTPUT
15+
echo "=========================="
16+
echo
17+
rm $OUTPUT
18+
}
19+
20+
pushd sample_crate > /dev/null
21+
cargo clean
22+
23+
echo "[TEST] Run all..."
24+
check_playback -Z concrete-playback
25+
26+
echo "[TEST] Run lib..."
27+
check_playback -Z concrete-playback --lib
28+
29+
echo "[TEST] Run bins..."
30+
check_playback -Z concrete-playback --bins
31+
32+
echo "[TEST] Only foo tests..."
33+
check_playback -Z concrete-playback --bin foo
34+
35+
cargo clean
36+
popd > /dev/null
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
[package]
4+
name = "sample_crate"
5+
version = "0.1.0"
6+
edition = "2021"
7+
doctest = false
8+
9+
[[bin]]
10+
name = "foo"
11+
doctest = false
12+
13+
14+
[[bin]]
15+
name = "bar"
16+
doctest = false
17+

0 commit comments

Comments
 (0)