1 //! Tests for custom cargo commands and other global command features.
4 use std
::fs
::{self, File}
;
6 use std
::path
::{Path, PathBuf}
;
7 use std
::process
::Stdio
;
10 use cargo_test_support
::cargo_process
;
11 use cargo_test_support
::paths
::{self, CargoPathExt}
;
12 use cargo_test_support
::registry
::Package
;
13 use cargo_test_support
::{basic_bin_manifest, basic_manifest, cargo_exe, project, Project}
;
15 #[cfg_attr(windows, allow(dead_code))]
18 Symlink { target: &'a Path }
,
21 /// Adds an empty file with executable flags (and platform-dependent suffix).
23 // TODO: move this to `Project` if other cases using this emerge.
24 fn fake_file(proj
: Project
, dir
: &Path
, name
: &str, kind
: &FakeKind
<'_
>) -> Project
{
28 .join(&format
!("{}{}", name
, env
::consts
::EXE_SUFFIX
));
29 path
.parent().unwrap().mkdir_p();
31 FakeKind
::Executable
=> {
32 File
::create(&path
).unwrap();
33 make_executable(&path
);
35 FakeKind
::Symlink { target }
=> {
36 make_symlink(&path
, target
);
42 fn make_executable(p
: &Path
) {
43 use std
::os
::unix
::prelude
::*;
45 let mut perms
= fs
::metadata(p
).unwrap().permissions();
46 let mode
= perms
.mode();
47 perms
.set_mode(mode
| 0o111);
48 fs
::set_permissions(p
, perms
).unwrap();
51 fn make_executable(_
: &Path
) {}
53 fn make_symlink(p
: &Path
, t
: &Path
) {
54 ::std
::os
::unix
::fs
::symlink(t
, p
).expect("Failed to create symlink");
57 fn make_symlink(_
: &Path
, _
: &Path
) {
58 panic
!("Not supported")
62 fn path() -> Vec
<PathBuf
> {
63 env
::split_paths(&env
::var_os("PATH").unwrap_or_default()).collect()
67 fn list_commands_with_descriptions() {
68 let p
= project().build();
70 .with_stdout_contains(
71 " build Compile a local package and all of its dependencies",
73 // Assert that `read-manifest` prints the right one-line description followed by another
75 .with_stdout_contains(
76 " read-manifest Print a JSON representation of a Cargo.toml manifest.",
82 fn list_aliases_with_descriptions() {
83 let p
= project().build();
85 .with_stdout_contains(" b alias: build")
86 .with_stdout_contains(" c alias: check")
87 .with_stdout_contains(" r alias: run")
88 .with_stdout_contains(" t alias: test")
93 fn list_command_looks_at_path() {
94 let proj
= project().build();
97 Path
::new("path-test"),
99 &FakeKind
::Executable
,
102 let mut path
= path();
103 path
.push(proj
.root().join("path-test"));
104 let path
= env
::join_paths(path
.iter()).unwrap();
105 let output
= cargo_process("-v --list")
109 let output
= str::from_utf8(&output
.stdout
).unwrap();
111 output
.contains("\n 1 "),
117 // Windows and symlinks don't currently mix well.
120 fn list_command_resolves_symlinks() {
121 let proj
= project().build();
122 let proj
= fake_file(
124 Path
::new("path-test"),
127 target
: &cargo_exe(),
131 let mut path
= path();
132 path
.push(proj
.root().join("path-test"));
133 let path
= env
::join_paths(path
.iter()).unwrap();
134 let output
= cargo_process("-v --list")
138 let output
= str::from_utf8(&output
.stdout
).unwrap();
140 output
.contains("\n 2 "),
147 fn find_closest_biuld_to_build() {
148 cargo_process("biuld")
150 .with_stderr_contains(
152 error: no such subcommand: `biuld`
154 <tab>Did you mean `build`?
159 // But, if we actually have `biuld`, it must work!
160 // https://github.com/rust-lang/cargo/issues/5201
161 Package
::new("cargo-biuld", "1.0.0")
166 println!("Similar, but not identical to, build");
172 cargo_process("install cargo-biuld").run();
173 cargo_process("biuld")
174 .with_stdout("Similar, but not identical to, build\n")
176 cargo_process("--list")
177 .with_stdout_contains(
178 " build Compile a local package and all of its dependencies\n",
180 .with_stdout_contains(" biuld\n")
185 fn find_closest_alias() {
186 let root
= paths
::root();
187 let my_home
= root
.join("my_home");
188 fs
::create_dir(&my_home
).unwrap();
190 &my_home
.join("config"),
198 cargo_process("myalais")
199 .env("CARGO_HOME", &my_home
)
201 .with_stderr_contains(
203 error: no such subcommand: `myalais`
205 <tab>Did you mean `myalias`?
210 // But, if no alias is defined, it must not suggest one!
211 cargo_process("myalais")
213 .with_stderr_contains(
215 error: no such subcommand: `myalais`
218 .with_stderr_does_not_contain(
220 <tab>Did you mean `myalias`?
226 // If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
228 fn find_closest_dont_correct_nonsense() {
229 cargo_process("there-is-no-way-that-there-is-a-command-close-to-this")
233 "[ERROR] no such subcommand: \
234 `there-is-no-way-that-there-is-a-command-close-to-this`
241 fn displays_subcommand_on_error() {
242 cargo_process("invalid-command")
244 .with_stderr("[ERROR] no such subcommand: `invalid-command`\n")
249 fn override_cargo_home() {
250 let root
= paths
::root();
251 let my_home
= root
.join("my_home");
252 fs
::create_dir(&my_home
).unwrap();
254 &my_home
.join("config"),
264 cargo_process("new foo")
266 .env("CARGO_HOME", &my_home
)
269 let toml
= paths
::root().join("foo/Cargo.toml");
270 let contents
= fs
::read_to_string(&toml
).unwrap();
271 assert
!(contents
.contains(r
#"authors = ["foo <bar>"]"#));
275 fn cargo_subcommand_env() {
281 println!("{{}}", env::var("{}").unwrap());
289 .file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
290 .file("src/main.rs", &src
)
293 let target_dir
= p
.target_debug_dir();
295 p
.cargo("build").run();
296 assert
!(p
.bin("cargo-envtest").is_file());
298 let cargo
= cargo_exe().canonicalize().unwrap();
299 let mut path
= path();
300 path
.push(target_dir
);
301 let path
= env
::join_paths(path
.iter()).unwrap();
303 cargo_process("envtest")
305 .with_stdout(cargo
.to_str().unwrap())
310 fn cargo_subcommand_args() {
313 .file("Cargo.toml", &basic_manifest("cargo-foo", "0.0.1"))
318 let args: Vec<_> = ::std::env::args().collect();
319 println!("{:?}", args);
325 p
.cargo("build").run();
326 let cargo_foo_bin
= p
.bin("cargo-foo");
327 assert
!(cargo_foo_bin
.is_file());
329 let mut path
= path();
330 path
.push(p
.target_debug_dir());
331 let path
= env
::join_paths(path
.iter()).unwrap();
333 cargo_process("foo bar -v --help")
336 r
#"["[CWD]/cargo-foo/target/debug/cargo-foo[EXE]", "foo", "bar", "-v", "--help"]"#,
343 cargo_process("--explain E0001")
344 .with_stdout_contains(
345 "This error suggests that the expression arm corresponding to the noted pattern",
351 fn closed_output_ok() {
352 // Checks that closed output doesn't cause an error.
353 let mut p
= cargo_process("--list").build_command();
354 p
.stdout(Stdio
::piped()).stderr(Stdio
::piped());
355 let mut child
= p
.spawn().unwrap();
357 drop(child
.stdout
.take());
359 let mut s
= String
::new();
364 .read_to_string(&mut s
)
366 let status
= child
.wait().unwrap();
367 assert
!(status
.success());
368 assert
!(s
.is_empty(), "{}", s
);