]> git.proxmox.com Git - cargo.git/blame - tests/testsuite/cargo_command.rs
Auto merge of #9762 - erickt:bump, r=ehuss
[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;
a6dad622 4use std::fs::{self, File};
7274307a 5use std::io::Read;
a6dad622 6use std::path::{Path, PathBuf};
7274307a 7use std::process::Stdio;
8cce8996 8use std::str;
8cce8996 9
9115b2c3
AC
10use cargo_test_support::cargo_process;
11use cargo_test_support::paths::{self, CargoPathExt};
12use cargo_test_support::registry::Package;
13use cargo_test_support::{basic_bin_manifest, basic_manifest, cargo_exe, project, Project};
a3f6a404 14
1e682848 15#[cfg_attr(windows, allow(dead_code))]
974d5834
JB
16enum FakeKind<'a> {
17 Executable,
1e682848 18 Symlink { target: &'a Path },
974d5834
JB
19}
20
f7c91ba6
AR
21/// Adds an empty file with executable flags (and platform-dependent suffix).
22//
23// TODO: move this to `Project` if other cases using this emerge.
6d1d3a68 24fn fake_file(proj: Project, dir: &Path, name: &str, kind: &FakeKind<'_>) -> Project {
85984a87
DW
25 let path = proj
26 .root()
1e682848
AC
27 .join(dir)
28 .join(&format!("{}{}", name, env::consts::EXE_SUFFIX));
763ba535 29 path.parent().unwrap().mkdir_p();
23591fe5 30 match *kind {
974d5834
JB
31 FakeKind::Executable => {
32 File::create(&path).unwrap();
33 make_executable(&path);
1e682848
AC
34 }
35 FakeKind::Symlink { target } => {
36 make_symlink(&path, target);
974d5834
JB
37 }
38 }
a6dad622
AC
39 return proj;
40
41 #[cfg(unix)]
42 fn make_executable(p: &Path) {
43 use std::os::unix::prelude::*;
44
53cc3ce8 45 let mut perms = fs::metadata(p).unwrap().permissions();
a6dad622
AC
46 let mode = perms.mode();
47 perms.set_mode(mode | 0o111);
48 fs::set_permissions(p, perms).unwrap();
49 }
50 #[cfg(windows)]
51 fn make_executable(_: &Path) {}
974d5834
JB
52 #[cfg(unix)]
53 fn make_symlink(p: &Path, t: &Path) {
1e682848 54 ::std::os::unix::fs::symlink(t, p).expect("Failed to create symlink");
974d5834
JB
55 }
56 #[cfg(windows)]
57 fn make_symlink(_: &Path, _: &Path) {
58 panic!("Not supported")
59 }
a3f6a404 60}
61
a6dad622 62fn path() -> Vec<PathBuf> {
23591fe5 63 env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
db3823a8 64}
ee5e24ff 65
0e0d9688 66#[cargo_test]
af2c3555
DW
67fn list_commands_with_descriptions() {
68 let p = project().build();
a173fc0a 69 p.cargo("--list")
fecb7246
AC
70 .with_stdout_contains(
71 " build Compile a local package and all of its dependencies",
72 )
f7c91ba6
AR
73 // Assert that `read-manifest` prints the right one-line description followed by another
74 // command, indented.
fecb7246
AC
75 .with_stdout_contains(
76 " read-manifest Print a JSON representation of a Cargo.toml manifest.",
77 )
a173fc0a 78 .run();
af2c3555
DW
79}
80
7b16c7c1
C
81#[cargo_test]
82fn list_aliases_with_descriptions() {
83 let p = project().build();
84 p.cargo("--list")
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")
89 .run();
90}
91
0e0d9688 92#[cargo_test]
6950bbb0 93fn list_command_looks_at_path() {
f8c9928c 94 let proj = project().build();
1e682848
AC
95 let proj = fake_file(
96 proj,
97 Path::new("path-test"),
98 "cargo-1",
99 &FakeKind::Executable,
100 );
a3f6a404 101
5d0cb3f2 102 let mut path = path();
db3823a8 103 path.push(proj.root().join("path-test"));
ee5e24ff 104 let path = env::join_paths(path.iter()).unwrap();
fecb7246
AC
105 let output = cargo_process("-v --list")
106 .env("PATH", &path)
107 .exec_with_output()
108 .unwrap();
63b34b64
DW
109 let output = str::from_utf8(&output.stdout).unwrap();
110 assert!(
111 output.contains("\n 1 "),
112 "missing 1: {}",
113 output
114 );
6950bbb0 115}
12f5de8e 116
f7c91ba6 117// Windows and symlinks don't currently mix well.
974d5834 118#[cfg(unix)]
0e0d9688 119#[cargo_test]
6950bbb0 120fn list_command_resolves_symlinks() {
f8c9928c 121 let proj = project().build();
1e682848
AC
122 let proj = fake_file(
123 proj,
124 Path::new("path-test"),
125 "cargo-2",
126 &FakeKind::Symlink {
127 target: &cargo_exe(),
128 },
129 );
974d5834
JB
130
131 let mut path = path();
132 path.push(proj.root().join("path-test"));
133 let path = env::join_paths(path.iter()).unwrap();
fecb7246
AC
134 let output = cargo_process("-v --list")
135 .env("PATH", &path)
136 .exec_with_output()
137 .unwrap();
63b34b64
DW
138 let output = str::from_utf8(&output.stdout).unwrap();
139 assert!(
140 output.contains("\n 2 "),
141 "missing 2: {}",
142 output
143 );
6950bbb0 144}
974d5834 145
0e0d9688 146#[cargo_test]
6950bbb0 147fn find_closest_biuld_to_build() {
85984a87
DW
148 cargo_process("biuld")
149 .with_status(101)
150 .with_stderr_contains(
1e682848 151 "\
a1735c7a
AK
152error: no such subcommand: `biuld`
153
154<tab>Did you mean `build`?
1e682848 155",
fecb7246
AC
156 )
157 .run();
a1735c7a
AK
158
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")
162 .file(
163 "src/main.rs",
164 r#"
6f8c7d5a
EH
165 fn main() {
166 println!("Similar, but not identical to, build");
167 }
168 "#,
fecb7246
AC
169 )
170 .publish();
85984a87
DW
171
172 cargo_process("install cargo-biuld").run();
173 cargo_process("biuld")
174 .with_stdout("Similar, but not identical to, build\n")
175 .run();
176 cargo_process("--list")
177 .with_stdout_contains(
178 " build Compile a local package and all of its dependencies\n",
fecb7246
AC
179 )
180 .with_stdout_contains(" biuld\n")
85984a87 181 .run();
6950bbb0 182}
12f5de8e 183
ff3e880c
ZL
184#[cargo_test]
185fn find_closest_alias() {
186 let root = paths::root();
187 let my_home = root.join("my_home");
188 fs::create_dir(&my_home).unwrap();
4ae79d2f
EH
189 fs::write(
190 &my_home.join("config"),
191 r#"
192 [alias]
193 myalias = "build"
194 "#,
195 )
196 .unwrap();
ff3e880c
ZL
197
198 cargo_process("myalais")
199 .env("CARGO_HOME", &my_home)
200 .with_status(101)
201 .with_stderr_contains(
202 "\
203error: no such subcommand: `myalais`
204
205<tab>Did you mean `myalias`?
206",
207 )
208 .run();
209
210 // But, if no alias is defined, it must not suggest one!
211 cargo_process("myalais")
212 .with_status(101)
213 .with_stderr_contains(
214 "\
215error: no such subcommand: `myalais`
216",
217 )
218 .with_stderr_does_not_contain(
219 "\
220<tab>Did you mean `myalias`?
221",
222 )
223 .run();
224}
225
f7c91ba6 226// If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
0e0d9688 227#[cargo_test]
6950bbb0 228fn find_closest_dont_correct_nonsense() {
85984a87
DW
229 cargo_process("there-is-no-way-that-there-is-a-command-close-to-this")
230 .cwd(&paths::root())
231 .with_status(101)
232 .with_stderr(
1e682848 233 "[ERROR] no such subcommand: \
1671630b 234 `there-is-no-way-that-there-is-a-command-close-to-this`
1e682848 235",
fecb7246
AC
236 )
237 .run();
79858995
KA
238}
239
0e0d9688 240#[cargo_test]
79858995 241fn displays_subcommand_on_error() {
85984a87
DW
242 cargo_process("invalid-command")
243 .with_status(101)
244 .with_stderr("[ERROR] no such subcommand: `invalid-command`\n")
245 .run();
6950bbb0 246}
2badab8c 247
b0998864
J
248#[cargo_test]
249fn override_cargo_home() {
250 let root = paths::root();
251 let my_home = root.join("my_home");
252 fs::create_dir(&my_home).unwrap();
253 fs::write(
254 &my_home.join("config"),
255 r#"
256 [cargo-new]
257 vcs = "none"
258 "#,
259 )
260 .unwrap();
261
262 cargo_process("new foo").env("CARGO_HOME", &my_home).run();
263
264 assert!(!paths::root().join("foo/.git").is_dir());
265
266 cargo_process("new foo2").run();
267
268 assert!(paths::root().join("foo2/.git").is_dir());
269}
270
0e0d9688 271#[cargo_test]
015a08a0 272fn cargo_subcommand_env() {
1e682848
AC
273 let src = format!(
274 r#"
015a08a0
VK
275 use std::env;
276
277 fn main() {{
278 println!("{{}}", env::var("{}").unwrap());
279 }}
1e682848
AC
280 "#,
281 cargo::CARGO_ENV
282 );
015a08a0 283
85984a87
DW
284 let p = project()
285 .at("cargo-envtest")
015a08a0 286 .file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
d43ee1dd
NK
287 .file("src/main.rs", &src)
288 .build();
015a08a0
VK
289
290 let target_dir = p.target_debug_dir();
291
85984a87 292 p.cargo("build").run();
570fe892 293 assert!(p.bin("cargo-envtest").is_file());
015a08a0 294
015a08a0
VK
295 let cargo = cargo_exe().canonicalize().unwrap();
296 let mut path = path();
297 path.push(target_dir);
298 let path = env::join_paths(path.iter()).unwrap();
299
85984a87
DW
300 cargo_process("envtest")
301 .env("PATH", &path)
302 .with_stdout(cargo.to_str().unwrap())
303 .run();
015a08a0
VK
304}
305
0e0d9688 306#[cargo_test]
deb1c1e1 307fn cargo_subcommand_args() {
85984a87
DW
308 let p = project()
309 .at("cargo-foo")
ab19c483 310 .file("Cargo.toml", &basic_manifest("cargo-foo", "0.0.1"))
deb1c1e1
AK
311 .file(
312 "src/main.rs",
313 r#"
6f8c7d5a
EH
314 fn main() {
315 let args: Vec<_> = ::std::env::args().collect();
aea5ca3c 316 println!("{}", args.join(" "));
6f8c7d5a
EH
317 }
318 "#,
fecb7246
AC
319 )
320 .build();
deb1c1e1 321
85984a87 322 p.cargo("build").run();
deb1c1e1 323 let cargo_foo_bin = p.bin("cargo-foo");
570fe892 324 assert!(cargo_foo_bin.is_file());
deb1c1e1
AK
325
326 let mut path = path();
327 path.push(p.target_debug_dir());
328 let path = env::join_paths(path.iter()).unwrap();
329
85984a87
DW
330 cargo_process("foo bar -v --help")
331 .env("PATH", &path)
aea5ca3c 332 .with_stdout("[CWD]/cargo-foo/target/debug/cargo-foo[EXE] foo bar -v --help")
49f73b9c 333 .run();
deb1c1e1
AK
334}
335
0e0d9688 336#[cargo_test]
6950bbb0 337fn explain() {
85984a87
DW
338 cargo_process("--explain E0001")
339 .with_stdout_contains(
b0c181d9 340 "This error suggests that the expression arm corresponding to the noted pattern",
fecb7246
AC
341 )
342 .run();
6950bbb0 343}
a4104914 344
7274307a
EH
345#[cargo_test]
346fn closed_output_ok() {
347 // Checks that closed output doesn't cause an error.
348 let mut p = cargo_process("--list").build_command();
349 p.stdout(Stdio::piped()).stderr(Stdio::piped());
350 let mut child = p.spawn().unwrap();
351 // Close stdout
352 drop(child.stdout.take());
353 // Read stderr
354 let mut s = String::new();
355 child
356 .stderr
357 .as_mut()
358 .unwrap()
359 .read_to_string(&mut s)
360 .unwrap();
361 let status = child.wait().unwrap();
362 assert!(status.success());
f5a3d559 363 assert!(s.is_empty(), "{}", s);
7274307a 364}