]> git.proxmox.com Git - cargo.git/blame - tests/testsuite/cargo_command.rs
Finish documentation changes
[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
0e0d9688 248#[cargo_test]
015a08a0 249fn cargo_subcommand_env() {
1e682848
AC
250 let src = format!(
251 r#"
015a08a0
VK
252 use std::env;
253
254 fn main() {{
255 println!("{{}}", env::var("{}").unwrap());
256 }}
1e682848
AC
257 "#,
258 cargo::CARGO_ENV
259 );
015a08a0 260
85984a87
DW
261 let p = project()
262 .at("cargo-envtest")
015a08a0 263 .file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
d43ee1dd
NK
264 .file("src/main.rs", &src)
265 .build();
015a08a0
VK
266
267 let target_dir = p.target_debug_dir();
268
85984a87 269 p.cargo("build").run();
570fe892 270 assert!(p.bin("cargo-envtest").is_file());
015a08a0 271
015a08a0
VK
272 let cargo = cargo_exe().canonicalize().unwrap();
273 let mut path = path();
274 path.push(target_dir);
275 let path = env::join_paths(path.iter()).unwrap();
276
85984a87
DW
277 cargo_process("envtest")
278 .env("PATH", &path)
279 .with_stdout(cargo.to_str().unwrap())
280 .run();
015a08a0
VK
281}
282
0e0d9688 283#[cargo_test]
deb1c1e1 284fn cargo_subcommand_args() {
85984a87
DW
285 let p = project()
286 .at("cargo-foo")
ab19c483 287 .file("Cargo.toml", &basic_manifest("cargo-foo", "0.0.1"))
deb1c1e1
AK
288 .file(
289 "src/main.rs",
290 r#"
6f8c7d5a
EH
291 fn main() {
292 let args: Vec<_> = ::std::env::args().collect();
293 println!("{:?}", args);
294 }
295 "#,
fecb7246
AC
296 )
297 .build();
deb1c1e1 298
85984a87 299 p.cargo("build").run();
deb1c1e1 300 let cargo_foo_bin = p.bin("cargo-foo");
570fe892 301 assert!(cargo_foo_bin.is_file());
deb1c1e1
AK
302
303 let mut path = path();
304 path.push(p.target_debug_dir());
305 let path = env::join_paths(path.iter()).unwrap();
306
85984a87
DW
307 cargo_process("foo bar -v --help")
308 .env("PATH", &path)
092f7bae
DW
309 .with_stdout(
310 r#"["[CWD]/cargo-foo/target/debug/cargo-foo[EXE]", "foo", "bar", "-v", "--help"]"#,
311 )
49f73b9c 312 .run();
deb1c1e1
AK
313}
314
0e0d9688 315#[cargo_test]
6950bbb0 316fn explain() {
85984a87
DW
317 cargo_process("--explain E0001")
318 .with_stdout_contains(
b0c181d9 319 "This error suggests that the expression arm corresponding to the noted pattern",
fecb7246
AC
320 )
321 .run();
6950bbb0 322}
a4104914 323
7274307a
EH
324#[cargo_test]
325fn closed_output_ok() {
326 // Checks that closed output doesn't cause an error.
327 let mut p = cargo_process("--list").build_command();
328 p.stdout(Stdio::piped()).stderr(Stdio::piped());
329 let mut child = p.spawn().unwrap();
330 // Close stdout
331 drop(child.stdout.take());
332 // Read stderr
333 let mut s = String::new();
334 child
335 .stderr
336 .as_mut()
337 .unwrap()
338 .read_to_string(&mut s)
339 .unwrap();
340 let status = child.wait().unwrap();
341 assert!(status.success());
f5a3d559 342 assert!(s.is_empty(), "{}", s);
7274307a 343}