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