Skip to main content

cargo_options/
test.rs

1use std::ops::{Deref, DerefMut};
2use std::path::PathBuf;
3use std::process::Command;
4
5use clap::{ArgAction, Parser};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10use crate::common::CommonOptions;
11use crate::heading;
12
13/// Execute all unit and integration tests and build examples of a local package
14#[derive(Clone, Debug, Default, Parser)]
15#[command(
16    display_order = 1,
17    after_help = "Run `cargo help test` for more detailed information.\nRun `cargo test -- --help` for test binary options."
18)]
19#[group(skip)]
20#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
21pub struct Test {
22    #[command(flatten)]
23    #[cfg_attr(feature = "serde", serde(flatten))]
24    pub common: CommonOptions,
25
26    /// Path to Cargo.toml
27    #[arg(long, value_name = "PATH", help_heading = heading::MANIFEST_OPTIONS)]
28    #[cfg_attr(feature = "serde", serde(default))]
29    pub manifest_path: Option<PathBuf>,
30
31    /// Build artifacts in release mode, with optimizations
32    #[arg(
33        short = 'r',
34        long,
35        conflicts_with = "profile",
36        help_heading = heading::COMPILATION_OPTIONS,
37    )]
38    #[cfg_attr(feature = "serde", serde(default))]
39    pub release: bool,
40
41    /// Ignore `rust-version` specification in packages
42    #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
43    #[cfg_attr(feature = "serde", serde(default))]
44    pub ignore_rust_version: bool,
45
46    /// Output build graph in JSON (unstable)
47    #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
48    #[cfg_attr(feature = "serde", serde(default))]
49    pub unit_graph: bool,
50
51    /// Package to run tests for
52    #[arg(
53        short = 'p',
54        long = "package",
55        value_name = "SPEC",
56        action = ArgAction::Append,
57        num_args=0..=1,
58        help_heading = heading::PACKAGE_SELECTION,
59    )]
60    #[cfg_attr(feature = "serde", serde(default))]
61    pub packages: Vec<String>,
62
63    /// Test all packages in the workspace
64    #[arg(long, help_heading = heading::PACKAGE_SELECTION)]
65    #[cfg_attr(feature = "serde", serde(default))]
66    pub workspace: bool,
67
68    /// Exclude packages from the test
69    #[arg(
70        long,
71        value_name = "SPEC",
72        action = ArgAction::Append,
73        help_heading = heading::PACKAGE_SELECTION,
74    )]
75    #[cfg_attr(feature = "serde", serde(default))]
76    pub exclude: Vec<String>,
77
78    /// Alias for workspace (deprecated)
79    #[arg(long, help_heading = heading::PACKAGE_SELECTION)]
80    #[cfg_attr(feature = "serde", serde(default))]
81    pub all: bool,
82
83    /// Test only this package's library
84    #[arg(long, help_heading = heading::TARGET_SELECTION)]
85    #[cfg_attr(feature = "serde", serde(default))]
86    pub lib: bool,
87
88    /// Test only the specified binary
89    #[arg(
90        long,
91        value_name = "NAME",
92        action = ArgAction::Append,
93        num_args=0..=1,
94        help_heading = heading::TARGET_SELECTION,
95    )]
96    #[cfg_attr(feature = "serde", serde(default))]
97    pub bin: Vec<String>,
98
99    /// Test all binaries
100    #[arg(long, help_heading = heading::TARGET_SELECTION)]
101    #[cfg_attr(feature = "serde", serde(default))]
102    pub bins: bool,
103
104    /// Test only the specified example
105    #[arg(
106        long,
107        value_name = "NAME",
108        action = ArgAction::Append,
109        num_args=0..=1,
110        help_heading = heading::TARGET_SELECTION,
111    )]
112    #[cfg_attr(feature = "serde", serde(default))]
113    pub example: Vec<String>,
114
115    /// Test all examples
116    #[arg(long, help_heading = heading::TARGET_SELECTION)]
117    #[cfg_attr(feature = "serde", serde(default))]
118    pub examples: bool,
119
120    /// Test only the specified test target
121    #[arg(
122        long,
123        value_name = "NAME",
124        action = ArgAction::Append,
125        help_heading = heading::TARGET_SELECTION,
126    )]
127    #[cfg_attr(feature = "serde", serde(default))]
128    pub test: Vec<String>,
129
130    /// Test all targets that have `test = true` set
131    #[arg(long, help_heading = heading::TARGET_SELECTION)]
132    #[cfg_attr(feature = "serde", serde(default))]
133    pub tests: bool,
134
135    /// Test only the specified bench target
136    #[arg(
137        long,
138        value_name = "NAME",
139        action = ArgAction::Append,
140        help_heading = heading::TARGET_SELECTION,
141    )]
142    #[cfg_attr(feature = "serde", serde(default))]
143    pub bench: Vec<String>,
144
145    /// Test all targets that have `bench = true` set
146    #[arg(long, help_heading = heading::TARGET_SELECTION)]
147    #[cfg_attr(feature = "serde", serde(default))]
148    pub benches: bool,
149
150    /// Test all targets (does not include doctests)
151    #[arg(long, help_heading = heading::TARGET_SELECTION)]
152    #[cfg_attr(feature = "serde", serde(default))]
153    pub all_targets: bool,
154
155    /// Test only this library's documentation
156    #[arg(long, help_heading = heading::TARGET_SELECTION)]
157    #[cfg_attr(feature = "serde", serde(default))]
158    pub doc: bool,
159
160    /// Compile, but don't run tests
161    #[arg(long)]
162    #[cfg_attr(feature = "serde", serde(default))]
163    pub no_run: bool,
164
165    /// Run all tests regardless of failure
166    #[arg(long)]
167    #[cfg_attr(feature = "serde", serde(default))]
168    pub no_fail_fast: bool,
169
170    /// Outputs a future incompatibility report at the end of the build
171    #[arg(long)]
172    #[cfg_attr(feature = "serde", serde(default))]
173    pub future_incompat_report: bool,
174
175    /// If specified, only run tests containing this string in their names
176    #[arg(value_name = "TESTNAME")]
177    #[cfg_attr(feature = "serde", serde(default))]
178    pub test_name: Option<String>,
179
180    /// Arguments for the test binary
181    #[arg(value_name = "ARGS", last = true, num_args = 0..)]
182    #[cfg_attr(feature = "serde", serde(default))]
183    pub args: Vec<String>,
184}
185
186impl Test {
187    /// Build a `cargo test` command
188    pub fn command(&self) -> Command {
189        let mut cmd = CommonOptions::cargo_command();
190        cmd.arg("test");
191
192        self.common.apply(&mut cmd);
193
194        if let Some(path) = self.manifest_path.as_ref() {
195            cmd.arg("--manifest-path").arg(path);
196        }
197        if self.release {
198            cmd.arg("--release");
199        }
200        if self.ignore_rust_version {
201            cmd.arg("--ignore-rust-version");
202        }
203        if self.unit_graph {
204            cmd.arg("--unit-graph");
205        }
206        for pkg in &self.packages {
207            cmd.arg("--package").arg(pkg);
208        }
209        if self.workspace {
210            cmd.arg("--workspace");
211        }
212        for item in &self.exclude {
213            cmd.arg("--exclude").arg(item);
214        }
215        if self.all {
216            cmd.arg("--all");
217        }
218        if self.lib {
219            cmd.arg("--lib");
220        }
221        for bin in &self.bin {
222            cmd.arg("--bin").arg(bin);
223        }
224        if self.bins {
225            cmd.arg("--bins");
226        }
227        for example in &self.example {
228            cmd.arg("--example").arg(example);
229        }
230        if self.examples {
231            cmd.arg("--examples");
232        }
233        for test in &self.test {
234            cmd.arg("--test").arg(test);
235        }
236        if self.tests {
237            cmd.arg("--tests");
238        }
239        for bench in &self.bench {
240            cmd.arg("--bench").arg(bench);
241        }
242        if self.benches {
243            cmd.arg("--benches");
244        }
245        if self.all_targets {
246            cmd.arg("--all-targets");
247        }
248        if self.doc {
249            cmd.arg("--doc");
250        }
251        if self.no_run {
252            cmd.arg("--no-run");
253        }
254        if self.no_fail_fast {
255            cmd.arg("--no-fail-fast");
256        }
257        if self.future_incompat_report {
258            cmd.arg("--future-incompat-report");
259        }
260        if self.test_name.is_some() || !self.args.is_empty() {
261            cmd.arg("--");
262            if let Some(test_name) = self.test_name.as_ref() {
263                cmd.arg(test_name);
264            }
265        }
266        cmd.args(&self.args);
267
268        cmd
269    }
270}
271
272impl Deref for Test {
273    type Target = CommonOptions;
274
275    fn deref(&self) -> &Self::Target {
276        &self.common
277    }
278}
279
280impl DerefMut for Test {
281    fn deref_mut(&mut self) -> &mut Self::Target {
282        &mut self.common
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::Test;
289    use clap::CommandFactory;
290
291    #[test]
292    fn verify_cli() {
293        <Test as CommandFactory>::command().debug_assert()
294    }
295}