]>
git.proxmox.com Git - cargo.git/blob - tests/testsuite/cargo_command.rs
1 //! Tests for custom cargo commands and other global command features.
6 use std
::path
::{Path, PathBuf}
;
7 use std
::process
::Stdio
;
10 use cargo_test_support
::basic_manifest
;
11 use cargo_test_support
::paths
::CargoPathExt
;
12 use cargo_test_support
::registry
::Package
;
13 use cargo_test_support
::tools
::echo_subcommand
;
14 use cargo_test_support
::{
15 basic_bin_manifest
, cargo_exe
, cargo_process
, paths
, project
, project_in_home
,
17 use cargo_util
::paths
::join_paths
;
19 fn path() -> Vec
<PathBuf
> {
20 env
::split_paths(&env
::var_os("PATH").unwrap_or_default()).collect()
24 fn list_commands_with_descriptions() {
25 let p
= project().build();
27 .with_stdout_contains(
28 " build Compile a local package and all of its dependencies",
30 // Assert that `read-manifest` prints the right one-line description followed by another
32 .with_stdout_contains(
33 " read-manifest Print a JSON representation of a Cargo.toml manifest.",
39 fn list_builtin_aliases_with_descriptions() {
40 let p
= project().build();
42 .with_stdout_contains(" b alias: build")
43 .with_stdout_contains(" c alias: check")
44 .with_stdout_contains(" r alias: run")
45 .with_stdout_contains(" t alias: test")
50 fn list_custom_aliases_with_descriptions() {
51 let p
= project_in_home("proj")
53 &paths
::home().join(".cargo").join("config"),
56 myaliasstr = "foo --bar"
57 myaliasvec = ["foo", "--bar"]
63 .with_stdout_contains(" myaliasstr alias: foo --bar")
64 .with_stdout_contains(" myaliasvec alias: foo --bar")
71 .executable(Path
::new("path-test-1").join("cargo-dupe"), "")
72 .executable(Path
::new("path-test-2").join("cargo-dupe"), "")
75 let mut path
= path();
76 path
.push(p
.root().join("path-test-1"));
77 path
.push(p
.root().join("path-test-2"));
78 let path
= env
::join_paths(path
.iter()).unwrap();
82 .with_stdout_contains_n(" dupe", 1)
87 fn list_command_looks_at_path() {
89 .executable(Path
::new("path-test").join("cargo-1"), "")
92 let mut path
= path();
93 path
.push(proj
.root().join("path-test"));
94 let path
= env
::join_paths(path
.iter()).unwrap();
95 let output
= cargo_process("-v --list")
99 let output
= str::from_utf8(&output
.stdout
).unwrap();
101 output
.contains("\n 1 "),
108 fn list_command_handles_known_external_commands() {
110 .executable(Path
::new("path-test").join("cargo-fmt"), "")
113 let fmt_desc
= " fmt Formats all bin and lib files of the current crate using rustfmt.";
115 // Without path - fmt isn't there
118 .with_stdout_does_not_contain(fmt_desc
)
121 // With path - fmt is there with known description
122 let mut path
= path();
123 path
.push(p
.root().join("path-test"));
124 let path
= env
::join_paths(path
.iter()).unwrap();
128 .with_stdout_contains(fmt_desc
)
133 fn list_command_resolves_symlinks() {
135 .symlink(cargo_exe(), Path
::new("path-test").join("cargo-2"))
138 let mut path
= path();
139 path
.push(proj
.root().join("path-test"));
140 let path
= env
::join_paths(path
.iter()).unwrap();
141 let output
= cargo_process("-v --list")
145 let output
= str::from_utf8(&output
.stdout
).unwrap();
147 output
.contains("\n 2 "),
154 fn find_closest_capital_c_to_c() {
157 .with_stderr_contains(
159 error: no such subcommand: `C`
161 <tab>Did you mean `c`?
168 fn find_closest_capital_b_to_b() {
171 .with_stderr_contains(
173 error: no such subcommand: `B`
175 <tab>Did you mean `b`?
182 fn find_closest_biuld_to_build() {
183 cargo_process("biuld")
185 .with_stderr_contains(
187 error: no such subcommand: `biuld`
189 <tab>Did you mean `build`?
194 // But, if we actually have `biuld`, it must work!
195 // https://github.com/rust-lang/cargo/issues/5201
196 Package
::new("cargo-biuld", "1.0.0")
201 println!("Similar, but not identical to, build");
207 cargo_process("install cargo-biuld").run();
208 cargo_process("biuld")
209 .with_stdout("Similar, but not identical to, build\n")
211 cargo_process("--list")
212 .with_stdout_contains(
213 " build Compile a local package and all of its dependencies\n",
215 .with_stdout_contains(" biuld\n")
220 fn find_closest_alias() {
221 let root
= paths
::root();
222 let my_home
= root
.join("my_home");
223 fs
::create_dir(&my_home
).unwrap();
225 &my_home
.join("config"),
233 cargo_process("myalais")
234 .env("CARGO_HOME", &my_home
)
236 .with_stderr_contains(
238 error: no such subcommand: `myalais`
240 <tab>Did you mean `myalias`?
245 // But, if no alias is defined, it must not suggest one!
246 cargo_process("myalais")
248 .with_stderr_contains(
250 error: no such subcommand: `myalais`
253 .with_stderr_does_not_contain(
255 <tab>Did you mean `myalias`?
261 // If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
263 fn find_closest_dont_correct_nonsense() {
264 cargo_process("there-is-no-way-that-there-is-a-command-close-to-this")
269 [ERROR] no such subcommand: `there-is-no-way-that-there-is-a-command-close-to-this`
271 <tab>View all installed commands with `cargo --list`",
277 fn displays_subcommand_on_error() {
278 cargo_process("invalid-command")
282 [ERROR] no such subcommand: `invalid-command`
284 <tab>View all installed commands with `cargo --list`",
290 fn override_cargo_home() {
291 let root
= paths
::root();
292 let my_home
= root
.join("my_home");
293 fs
::create_dir(&my_home
).unwrap();
295 &my_home
.join("config"),
303 cargo_process("new foo").env("CARGO_HOME", &my_home
).run();
305 assert
!(!paths
::root().join("foo/.git").is_dir());
307 cargo_process("new foo2").run();
309 assert
!(paths
::root().join("foo2/.git").is_dir());
313 fn cargo_subcommand_env() {
319 println!("{{}}", env::var("{}").unwrap());
327 .file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
328 .file("src/main.rs", &src
)
331 let target_dir
= p
.target_debug_dir();
333 p
.cargo("build").run();
334 assert
!(p
.bin("cargo-envtest").is_file());
336 let cargo
= cargo_exe().canonicalize().unwrap();
337 let mut path
= path();
338 path
.push(target_dir
);
339 let path
= env
::join_paths(path
.iter()).unwrap();
341 cargo_process("envtest")
343 .with_stdout(cargo
.to_str().unwrap())
348 fn cargo_cmd_bins_vs_explicit_path() {
349 // Set up `cargo-foo` binary in two places: inside `$HOME/.cargo/bin` and outside of it
351 // Return paths to both places
352 fn set_up_cargo_foo() -> (PathBuf
, PathBuf
) {
355 .file("Cargo.toml", &basic_manifest("cargo-foo", "1.0.0"))
357 "src/bin/cargo-foo.rs",
358 r
#"fn main() { println!("INSIDE"); }"#,
361 "src/bin/cargo-foo2.rs",
362 r
#"fn main() { println!("OUTSIDE"); }"#,
365 p
.cargo("build").run();
366 let cargo_bin_dir
= paths
::home().join(".cargo/bin");
367 cargo_bin_dir
.mkdir_p();
368 let root_bin_dir
= paths
::root().join("bin");
369 root_bin_dir
.mkdir_p();
370 let exe_name
= format
!("cargo-foo{}", env
::consts
::EXE_SUFFIX
);
371 fs
::rename(p
.bin("cargo-foo"), cargo_bin_dir
.join(&exe_name
)).unwrap();
372 fs
::rename(p
.bin("cargo-foo2"), root_bin_dir
.join(&exe_name
)).unwrap();
374 (root_bin_dir
, cargo_bin_dir
)
377 let (outside_dir
, inside_dir
) = set_up_cargo_foo();
379 // If `$CARGO_HOME/bin` is not in a path, prefer it over anything in `$PATH`.
381 // This is the historical behavior we don't want to break.
382 cargo_process("foo").with_stdout_contains("INSIDE").run();
384 // When `$CARGO_HOME/bin` is in the `$PATH`
385 // use only `$PATH` so the user-defined ordering is respected.
390 join_paths(&[&inside_dir
, &outside_dir
], "PATH").unwrap(),
392 .with_stdout_contains("INSIDE")
396 // Note: trailing slash
399 join_paths(&[inside_dir
.join(""), outside_dir
.join("")], "PATH").unwrap(),
401 .with_stdout_contains("INSIDE")
407 join_paths(&[&outside_dir
, &inside_dir
], "PATH").unwrap(),
409 .with_stdout_contains("OUTSIDE")
413 // Note: trailing slash
416 join_paths(&[outside_dir
.join(""), inside_dir
.join("")], "PATH").unwrap(),
418 .with_stdout_contains("OUTSIDE")
425 fn cargo_subcommand_args() {
426 let p
= echo_subcommand();
427 let cargo_foo_bin
= p
.bin("cargo-echo");
428 assert
!(cargo_foo_bin
.is_file());
430 let mut path
= path();
431 path
.push(p
.target_debug_dir());
432 let path
= env
::join_paths(path
.iter()).unwrap();
434 cargo_process("echo bar -v --help")
436 .with_stdout("echo bar -v --help")
442 cargo_process("--explain E0001")
443 .with_stdout_contains(
444 "This error suggests that the expression arm corresponding to the noted pattern",
450 fn closed_output_ok() {
451 // Checks that closed output doesn't cause an error.
452 let mut p
= cargo_process("--list").build_command();
453 p
.stdout(Stdio
::piped()).stderr(Stdio
::piped());
454 let mut child
= p
.spawn().unwrap();
456 drop(child
.stdout
.take());
458 let mut s
= String
::new();
463 .read_to_string(&mut s
)
465 let status
= child
.wait().unwrap();
466 assert
!(status
.success());
467 assert
!(s
.is_empty(), "{}", s
);
471 fn subcommand_leading_plus_output_contains() {
472 cargo_process("+nightly")
476 error: no such subcommand: `+nightly`
478 <tab>Cargo does not handle `+toolchain` directives.
479 <tab>Did you mean to invoke `cargo` through `rustup` instead?",
485 fn full_did_you_mean() {
486 cargo_process("bluid")
490 error: no such subcommand: `bluid`
492 <tab>Did you mean `build`?
494 <tab>View all installed commands with `cargo --list`",