]>
Commit | Line | Data |
---|---|---|
83571aee EH |
1 | //! Tests for custom cargo commands and other global command features. |
2 | ||
ee5e24ff | 3 | use std::env; |
dde290e6 | 4 | use std::fs; |
7274307a | 5 | use std::io::Read; |
a6dad622 | 6 | use std::path::{Path, PathBuf}; |
7274307a | 7 | use std::process::Stdio; |
8cce8996 | 8 | use std::str; |
8cce8996 | 9 | |
9115b2c3 | 10 | use cargo_test_support::registry::Package; |
5a56cf2e NK |
11 | use cargo_test_support::tools::echo_subcommand; |
12 | use cargo_test_support::{ | |
13 | basic_bin_manifest, cargo_exe, cargo_process, paths, project, project_in_home, | |
14 | }; | |
a3f6a404 | 15 | |
a6dad622 | 16 | fn 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 |
21 | fn 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 | 36 | fn 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] |
47 | fn 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] |
66 | fn 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 | 84 | fn 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] |
105 | fn 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 | 130 | fn 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] |
151 | fn find_closest_capital_c_to_c() { | |
152 | cargo_process("C") | |
153 | .with_status(101) | |
154 | .with_stderr_contains( | |
155 | "\ | |
156 | error: no such subcommand: `C` | |
157 | ||
158 | <tab>Did you mean `c`? | |
159 | ", | |
160 | ) | |
161 | .run(); | |
162 | } | |
163 | ||
164 | #[cargo_test] | |
6f13c466 | 165 | fn find_closest_capital_b_to_b() { |
42528799 ML |
166 | cargo_process("B") |
167 | .with_status(101) | |
168 | .with_stderr_contains( | |
169 | "\ | |
170 | error: no such subcommand: `B` | |
171 | ||
172 | <tab>Did you mean `b`? | |
173 | ", | |
174 | ) | |
175 | .run(); | |
176 | } | |
177 | ||
0e0d9688 | 178 | #[cargo_test] |
6950bbb0 | 179 | fn find_closest_biuld_to_build() { |
85984a87 DW |
180 | cargo_process("biuld") |
181 | .with_status(101) | |
182 | .with_stderr_contains( | |
1e682848 | 183 | "\ |
a1735c7a AK |
184 | error: 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] |
217 | fn 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 | "\ | |
235 | error: 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 | "\ | |
247 | error: 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 | 260 | fn 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 | 274 | fn 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] |
287 | fn 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 | 310 | fn 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 | 345 | fn 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 | 361 | fn 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] |
370 | fn 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] | |
391 | fn subcommand_leading_plus_output_contains() { | |
392 | cargo_process("+nightly") | |
393 | .with_status(101) | |
222d90b7 | 394 | .with_stderr( |
8f5f2ed2 | 395 | "\ |
222d90b7 | 396 | error: 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] | |
405 | fn full_did_you_mean() { | |
406 | cargo_process("bluid") | |
407 | .with_status(101) | |
408 | .with_stderr( | |
409 | "\ | |
410 | error: 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 | } |