]> git.proxmox.com Git - cargo.git/blame - tests/testsuite/cargo_command.rs
Auto merge of #11044 - Eh2406:file_hash, r=weihanglo
[cargo.git] / tests / testsuite / cargo_command.rs
CommitLineData
83571aee
EH
1//! Tests for custom cargo commands and other global command features.
2
ee5e24ff 3use std::env;
dde290e6 4use std::fs;
7274307a 5use std::io::Read;
a6dad622 6use std::path::{Path, PathBuf};
7274307a 7use std::process::Stdio;
8cce8996 8use std::str;
8cce8996 9
9115b2c3 10use cargo_test_support::registry::Package;
5a56cf2e
NK
11use cargo_test_support::tools::echo_subcommand;
12use cargo_test_support::{
13 basic_bin_manifest, cargo_exe, cargo_process, paths, project, project_in_home,
14};
a3f6a404 15
a6dad622 16fn path() -> Vec<PathBuf> {
23591fe5 17 env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
db3823a8 18}
ee5e24ff 19
0e0d9688 20#[cargo_test]
af2c3555
DW
21fn list_commands_with_descriptions() {
22 let p = project().build();
a173fc0a 23 p.cargo("--list")
fecb7246
AC
24 .with_stdout_contains(
25 " build Compile a local package and all of its dependencies",
26 )
f7c91ba6
AR
27 // Assert that `read-manifest` prints the right one-line description followed by another
28 // command, indented.
fecb7246
AC
29 .with_stdout_contains(
30 " read-manifest Print a JSON representation of a Cargo.toml manifest.",
31 )
a173fc0a 32 .run();
af2c3555
DW
33}
34
7b16c7c1 35#[cargo_test]
1c5e68b4 36fn list_builtin_aliases_with_descriptions() {
7b16c7c1
C
37 let p = project().build();
38 p.cargo("--list")
39 .with_stdout_contains(" b alias: build")
40 .with_stdout_contains(" c alias: check")
41 .with_stdout_contains(" r alias: run")
42 .with_stdout_contains(" t alias: test")
43 .run();
44}
45
1c5e68b4
DM
46#[cargo_test]
47fn list_custom_aliases_with_descriptions() {
48 let p = project_in_home("proj")
49 .file(
50 &paths::home().join(".cargo").join("config"),
51 r#"
52 [alias]
53 myaliasstr = "foo --bar"
54 myaliasvec = ["foo", "--bar"]
55 "#,
56 )
57 .build();
58
59 p.cargo("--list")
910569aa
EH
60 .with_stdout_contains(" myaliasstr alias: foo --bar")
61 .with_stdout_contains(" myaliasvec alias: foo --bar")
1c5e68b4
DM
62 .run();
63}
64
0ab79d7a
NK
65#[cargo_test]
66fn list_dedupe() {
67 let p = project()
68 .executable(Path::new("path-test-1").join("cargo-dupe"), "")
69 .executable(Path::new("path-test-2").join("cargo-dupe"), "")
70 .build();
71
72 let mut path = path();
73 path.push(p.root().join("path-test-1"));
74 path.push(p.root().join("path-test-2"));
75 let path = env::join_paths(path.iter()).unwrap();
76
77 p.cargo("--list")
78 .env("PATH", &path)
79 .with_stdout_contains_n(" dupe", 1)
80 .run();
81}
82
0e0d9688 83#[cargo_test]
6950bbb0 84fn list_command_looks_at_path() {
dde290e6
NK
85 let proj = project()
86 .executable(Path::new("path-test").join("cargo-1"), "")
87 .build();
a3f6a404 88
5d0cb3f2 89 let mut path = path();
db3823a8 90 path.push(proj.root().join("path-test"));
ee5e24ff 91 let path = env::join_paths(path.iter()).unwrap();
fecb7246
AC
92 let output = cargo_process("-v --list")
93 .env("PATH", &path)
94 .exec_with_output()
95 .unwrap();
63b34b64
DW
96 let output = str::from_utf8(&output.stdout).unwrap();
97 assert!(
98 output.contains("\n 1 "),
99 "missing 1: {}",
100 output
101 );
6950bbb0 102}
12f5de8e 103
1edd8630
NK
104#[cargo_test]
105fn list_command_handles_known_external_commands() {
106 let p = project()
107 .executable(Path::new("path-test").join("cargo-fmt"), "")
108 .build();
109
110 let fmt_desc = " fmt Formats all bin and lib files of the current crate using rustfmt.";
111
112 // Without path - fmt isn't there
113 p.cargo("--list")
114 .env("PATH", "")
115 .with_stdout_does_not_contain(fmt_desc)
116 .run();
117
118 // With path - fmt is there with known description
119 let mut path = path();
120 path.push(p.root().join("path-test"));
121 let path = env::join_paths(path.iter()).unwrap();
122
123 p.cargo("--list")
124 .env("PATH", &path)
125 .with_stdout_contains(fmt_desc)
126 .run();
127}
128
0e0d9688 129#[cargo_test]
6950bbb0 130fn list_command_resolves_symlinks() {
dde290e6
NK
131 let proj = project()
132 .symlink(cargo_exe(), Path::new("path-test").join("cargo-2"))
133 .build();
974d5834
JB
134
135 let mut path = path();
136 path.push(proj.root().join("path-test"));
137 let path = env::join_paths(path.iter()).unwrap();
fecb7246
AC
138 let output = cargo_process("-v --list")
139 .env("PATH", &path)
140 .exec_with_output()
141 .unwrap();
63b34b64
DW
142 let output = str::from_utf8(&output.stdout).unwrap();
143 assert!(
144 output.contains("\n 2 "),
145 "missing 2: {}",
146 output
147 );
6950bbb0 148}
974d5834 149
42528799
ML
150#[cargo_test]
151fn find_closest_capital_c_to_c() {
152 cargo_process("C")
153 .with_status(101)
154 .with_stderr_contains(
155 "\
156error: no such subcommand: `C`
157
158<tab>Did you mean `c`?
159",
160 )
161 .run();
162}
163
164#[cargo_test]
6f13c466 165fn find_closest_capital_b_to_b() {
42528799
ML
166 cargo_process("B")
167 .with_status(101)
168 .with_stderr_contains(
169 "\
170error: no such subcommand: `B`
171
172<tab>Did you mean `b`?
173",
174 )
175 .run();
176}
177
0e0d9688 178#[cargo_test]
6950bbb0 179fn find_closest_biuld_to_build() {
85984a87
DW
180 cargo_process("biuld")
181 .with_status(101)
182 .with_stderr_contains(
1e682848 183 "\
a1735c7a
AK
184error: no such subcommand: `biuld`
185
186<tab>Did you mean `build`?
1e682848 187",
fecb7246
AC
188 )
189 .run();
a1735c7a
AK
190
191 // But, if we actually have `biuld`, it must work!
192 // https://github.com/rust-lang/cargo/issues/5201
193 Package::new("cargo-biuld", "1.0.0")
194 .file(
195 "src/main.rs",
196 r#"
6f8c7d5a
EH
197 fn main() {
198 println!("Similar, but not identical to, build");
199 }
200 "#,
fecb7246
AC
201 )
202 .publish();
85984a87
DW
203
204 cargo_process("install cargo-biuld").run();
205 cargo_process("biuld")
206 .with_stdout("Similar, but not identical to, build\n")
207 .run();
208 cargo_process("--list")
209 .with_stdout_contains(
210 " build Compile a local package and all of its dependencies\n",
fecb7246
AC
211 )
212 .with_stdout_contains(" biuld\n")
85984a87 213 .run();
6950bbb0 214}
12f5de8e 215
ff3e880c
ZL
216#[cargo_test]
217fn find_closest_alias() {
218 let root = paths::root();
219 let my_home = root.join("my_home");
220 fs::create_dir(&my_home).unwrap();
4ae79d2f
EH
221 fs::write(
222 &my_home.join("config"),
223 r#"
224 [alias]
225 myalias = "build"
226 "#,
227 )
228 .unwrap();
ff3e880c
ZL
229
230 cargo_process("myalais")
231 .env("CARGO_HOME", &my_home)
232 .with_status(101)
233 .with_stderr_contains(
234 "\
235error: no such subcommand: `myalais`
236
237<tab>Did you mean `myalias`?
238",
239 )
240 .run();
241
242 // But, if no alias is defined, it must not suggest one!
243 cargo_process("myalais")
244 .with_status(101)
245 .with_stderr_contains(
246 "\
247error: no such subcommand: `myalais`
248",
249 )
250 .with_stderr_does_not_contain(
251 "\
252<tab>Did you mean `myalias`?
253",
254 )
255 .run();
256}
257
f7c91ba6 258// If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
0e0d9688 259#[cargo_test]
6950bbb0 260fn find_closest_dont_correct_nonsense() {
85984a87
DW
261 cargo_process("there-is-no-way-that-there-is-a-command-close-to-this")
262 .cwd(&paths::root())
263 .with_status(101)
264 .with_stderr(
5f3ded12 265 "\
266[ERROR] no such subcommand: `there-is-no-way-that-there-is-a-command-close-to-this`
267
42df8740 268<tab>View all installed commands with `cargo --list`",
fecb7246
AC
269 )
270 .run();
79858995
KA
271}
272
0e0d9688 273#[cargo_test]
79858995 274fn displays_subcommand_on_error() {
85984a87
DW
275 cargo_process("invalid-command")
276 .with_status(101)
b2f44de8 277 .with_stderr(
5f3ded12 278 "\
279[ERROR] no such subcommand: `invalid-command`
280
42df8740 281<tab>View all installed commands with `cargo --list`",
b2f44de8 282 )
85984a87 283 .run();
6950bbb0 284}
2badab8c 285
b0998864
J
286#[cargo_test]
287fn override_cargo_home() {
288 let root = paths::root();
289 let my_home = root.join("my_home");
290 fs::create_dir(&my_home).unwrap();
291 fs::write(
292 &my_home.join("config"),
293 r#"
294 [cargo-new]
295 vcs = "none"
296 "#,
297 )
298 .unwrap();
299
300 cargo_process("new foo").env("CARGO_HOME", &my_home).run();
301
302 assert!(!paths::root().join("foo/.git").is_dir());
303
304 cargo_process("new foo2").run();
305
306 assert!(paths::root().join("foo2/.git").is_dir());
307}
308
0e0d9688 309#[cargo_test]
015a08a0 310fn cargo_subcommand_env() {
1e682848
AC
311 let src = format!(
312 r#"
015a08a0
VK
313 use std::env;
314
315 fn main() {{
316 println!("{{}}", env::var("{}").unwrap());
317 }}
1e682848
AC
318 "#,
319 cargo::CARGO_ENV
320 );
015a08a0 321
85984a87
DW
322 let p = project()
323 .at("cargo-envtest")
015a08a0 324 .file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
d43ee1dd
NK
325 .file("src/main.rs", &src)
326 .build();
015a08a0
VK
327
328 let target_dir = p.target_debug_dir();
329
85984a87 330 p.cargo("build").run();
570fe892 331 assert!(p.bin("cargo-envtest").is_file());
015a08a0 332
015a08a0
VK
333 let cargo = cargo_exe().canonicalize().unwrap();
334 let mut path = path();
335 path.push(target_dir);
336 let path = env::join_paths(path.iter()).unwrap();
337
85984a87
DW
338 cargo_process("envtest")
339 .env("PATH", &path)
340 .with_stdout(cargo.to_str().unwrap())
341 .run();
015a08a0
VK
342}
343
0e0d9688 344#[cargo_test]
deb1c1e1 345fn cargo_subcommand_args() {
5a56cf2e
NK
346 let p = echo_subcommand();
347 let cargo_foo_bin = p.bin("cargo-echo");
570fe892 348 assert!(cargo_foo_bin.is_file());
deb1c1e1
AK
349
350 let mut path = path();
351 path.push(p.target_debug_dir());
352 let path = env::join_paths(path.iter()).unwrap();
353
5a56cf2e 354 cargo_process("echo bar -v --help")
85984a87 355 .env("PATH", &path)
5a56cf2e 356 .with_stdout("echo bar -v --help")
49f73b9c 357 .run();
deb1c1e1
AK
358}
359
0e0d9688 360#[cargo_test]
6950bbb0 361fn explain() {
85984a87
DW
362 cargo_process("--explain E0001")
363 .with_stdout_contains(
b0c181d9 364 "This error suggests that the expression arm corresponding to the noted pattern",
fecb7246
AC
365 )
366 .run();
6950bbb0 367}
a4104914 368
7274307a
EH
369#[cargo_test]
370fn closed_output_ok() {
371 // Checks that closed output doesn't cause an error.
372 let mut p = cargo_process("--list").build_command();
373 p.stdout(Stdio::piped()).stderr(Stdio::piped());
374 let mut child = p.spawn().unwrap();
375 // Close stdout
376 drop(child.stdout.take());
377 // Read stderr
378 let mut s = String::new();
379 child
380 .stderr
381 .as_mut()
382 .unwrap()
383 .read_to_string(&mut s)
384 .unwrap();
385 let status = child.wait().unwrap();
386 assert!(status.success());
f5a3d559 387 assert!(s.is_empty(), "{}", s);
7274307a 388}
8f5f2ed2 389
390#[cargo_test]
391fn subcommand_leading_plus_output_contains() {
392 cargo_process("+nightly")
393 .with_status(101)
222d90b7 394 .with_stderr(
8f5f2ed2 395 "\
222d90b7 396error: no such subcommand: `+nightly`
397
398<tab>Cargo does not handle `+toolchain` directives.
399<tab>Did you mean to invoke `cargo` through `rustup` instead?",
400 )
401 .run();
402}
403
404#[cargo_test]
405fn full_did_you_mean() {
406 cargo_process("bluid")
407 .with_status(101)
408 .with_stderr(
409 "\
410error: no such subcommand: `bluid`
411
412<tab>Did you mean `build`?
413
414<tab>View all installed commands with `cargo --list`",
8f5f2ed2 415 )
416 .run();
417}