]>
Commit | Line | Data |
---|---|---|
ee5e24ff | 1 | use std::env; |
a6dad622 AC |
2 | use std::fs::{self, File}; |
3 | use std::io::prelude::*; | |
4 | use std::path::{Path, PathBuf}; | |
8cce8996 | 5 | use std::str; |
8cce8996 | 6 | |
98f17bcc | 7 | use cargo; |
43b42d6f | 8 | use support::cargo_process; |
85984a87 | 9 | use support::hamcrest::{assert_that, existing_file}; |
43b42d6f DW |
10 | use support::paths::{self, CargoPathExt}; |
11 | use support::registry::Package; | |
85984a87 | 12 | use support::{basic_bin_manifest, basic_manifest, cargo_exe, project, Project}; |
a3f6a404 | 13 | |
1e682848 | 14 | #[cfg_attr(windows, allow(dead_code))] |
974d5834 JB |
15 | enum FakeKind<'a> { |
16 | Executable, | |
1e682848 | 17 | Symlink { target: &'a Path }, |
974d5834 JB |
18 | } |
19 | ||
a3f6a404 | 20 | /// Add an empty file with executable flags (and platform-dependent suffix). |
d43ee1dd NK |
21 | /// TODO: move this to `Project` if other cases using this emerge. |
22 | fn fake_file(proj: Project, dir: &Path, name: &str, kind: &FakeKind) -> Project { | |
85984a87 DW |
23 | let path = proj |
24 | .root() | |
1e682848 AC |
25 | .join(dir) |
26 | .join(&format!("{}{}", name, env::consts::EXE_SUFFIX)); | |
763ba535 | 27 | path.parent().unwrap().mkdir_p(); |
23591fe5 | 28 | match *kind { |
974d5834 JB |
29 | FakeKind::Executable => { |
30 | File::create(&path).unwrap(); | |
31 | make_executable(&path); | |
1e682848 AC |
32 | } |
33 | FakeKind::Symlink { target } => { | |
34 | make_symlink(&path, target); | |
974d5834 JB |
35 | } |
36 | } | |
a6dad622 AC |
37 | return proj; |
38 | ||
39 | #[cfg(unix)] | |
40 | fn make_executable(p: &Path) { | |
41 | use std::os::unix::prelude::*; | |
42 | ||
53cc3ce8 | 43 | let mut perms = fs::metadata(p).unwrap().permissions(); |
a6dad622 AC |
44 | let mode = perms.mode(); |
45 | perms.set_mode(mode | 0o111); | |
46 | fs::set_permissions(p, perms).unwrap(); | |
47 | } | |
48 | #[cfg(windows)] | |
49 | fn make_executable(_: &Path) {} | |
974d5834 JB |
50 | #[cfg(unix)] |
51 | fn make_symlink(p: &Path, t: &Path) { | |
1e682848 | 52 | ::std::os::unix::fs::symlink(t, p).expect("Failed to create symlink"); |
974d5834 JB |
53 | } |
54 | #[cfg(windows)] | |
55 | fn make_symlink(_: &Path, _: &Path) { | |
56 | panic!("Not supported") | |
57 | } | |
a3f6a404 | 58 | } |
59 | ||
a6dad622 | 60 | fn path() -> Vec<PathBuf> { |
23591fe5 | 61 | env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect() |
db3823a8 | 62 | } |
ee5e24ff | 63 | |
af2c3555 DW |
64 | #[test] |
65 | fn list_commands_with_descriptions() { | |
66 | let p = project().build(); | |
67 | let output = p.cargo("--list").exec_with_output().unwrap(); | |
68 | let output = str::from_utf8(&output.stdout).unwrap(); | |
69 | assert!( | |
85984a87 DW |
70 | output.contains( |
71 | "\n build Compile a local package and all of its dependencies" | |
72 | ), | |
af2c3555 DW |
73 | "missing build, with description: {}", |
74 | output | |
75 | ); | |
a838f8fa DW |
76 | // assert read-manifest prints the right one-line description followed by another command, indented. |
77 | assert!( | |
78 | output.contains("\n read-manifest Print a JSON representation of a Cargo.toml manifest.\n "), | |
79 | "missing build, with description: {}", | |
80 | output | |
81 | ); | |
af2c3555 DW |
82 | } |
83 | ||
6950bbb0 AC |
84 | #[test] |
85 | fn list_command_looks_at_path() { | |
f8c9928c | 86 | let proj = project().build(); |
1e682848 AC |
87 | let proj = fake_file( |
88 | proj, | |
89 | Path::new("path-test"), | |
90 | "cargo-1", | |
91 | &FakeKind::Executable, | |
92 | ); | |
a3f6a404 | 93 | |
5d0cb3f2 | 94 | let mut path = path(); |
db3823a8 | 95 | path.push(proj.root().join("path-test")); |
ee5e24ff | 96 | let path = env::join_paths(path.iter()).unwrap(); |
8940d306 DW |
97 | let mut p = cargo_process("-v --list"); |
98 | let output = p.env("PATH", &path); | |
9ed3a6ea | 99 | let output = output.exec_with_output().unwrap(); |
a6dad622 | 100 | let output = str::from_utf8(&output.stdout).unwrap(); |
1e682848 AC |
101 | assert!( |
102 | output.contains("\n 1 "), | |
103 | "missing 1: {}", | |
104 | output | |
105 | ); | |
6950bbb0 | 106 | } |
12f5de8e | 107 | |
974d5834 JB |
108 | // windows and symlinks don't currently agree that well |
109 | #[cfg(unix)] | |
6950bbb0 AC |
110 | #[test] |
111 | fn list_command_resolves_symlinks() { | |
f8c9928c | 112 | let proj = project().build(); |
1e682848 AC |
113 | let proj = fake_file( |
114 | proj, | |
115 | Path::new("path-test"), | |
116 | "cargo-2", | |
117 | &FakeKind::Symlink { | |
118 | target: &cargo_exe(), | |
119 | }, | |
120 | ); | |
974d5834 JB |
121 | |
122 | let mut path = path(); | |
123 | path.push(proj.root().join("path-test")); | |
124 | let path = env::join_paths(path.iter()).unwrap(); | |
8940d306 DW |
125 | let mut p = cargo_process("-v --list"); |
126 | let output = p.env("PATH", &path); | |
974d5834 JB |
127 | let output = output.exec_with_output().unwrap(); |
128 | let output = str::from_utf8(&output.stdout).unwrap(); | |
1e682848 AC |
129 | assert!( |
130 | output.contains("\n 2 "), | |
131 | "missing 2: {}", | |
132 | output | |
133 | ); | |
6950bbb0 | 134 | } |
974d5834 | 135 | |
6950bbb0 AC |
136 | #[test] |
137 | fn find_closest_biuld_to_build() { | |
85984a87 DW |
138 | cargo_process("biuld") |
139 | .with_status(101) | |
140 | .with_stderr_contains( | |
1e682848 | 141 | "\ |
a1735c7a AK |
142 | error: no such subcommand: `biuld` |
143 | ||
144 | <tab>Did you mean `build`? | |
1e682848 | 145 | ", |
85984a87 | 146 | ).run(); |
a1735c7a AK |
147 | |
148 | // But, if we actually have `biuld`, it must work! | |
149 | // https://github.com/rust-lang/cargo/issues/5201 | |
150 | Package::new("cargo-biuld", "1.0.0") | |
151 | .file( | |
152 | "src/main.rs", | |
153 | r#" | |
154 | fn main() { | |
155 | println!("Similar, but not identical to, build"); | |
156 | } | |
157 | "#, | |
85984a87 DW |
158 | ).publish(); |
159 | ||
160 | cargo_process("install cargo-biuld").run(); | |
161 | cargo_process("biuld") | |
162 | .with_stdout("Similar, but not identical to, build\n") | |
163 | .run(); | |
164 | cargo_process("--list") | |
165 | .with_stdout_contains( | |
166 | " build Compile a local package and all of its dependencies\n", | |
167 | ).with_stdout_contains(" biuld\n") | |
168 | .run(); | |
6950bbb0 | 169 | } |
12f5de8e PW |
170 | |
171 | // if a subcommand is more than 3 edit distance away, we don't make a suggestion | |
6950bbb0 AC |
172 | #[test] |
173 | fn find_closest_dont_correct_nonsense() { | |
85984a87 DW |
174 | cargo_process("there-is-no-way-that-there-is-a-command-close-to-this") |
175 | .cwd(&paths::root()) | |
176 | .with_status(101) | |
177 | .with_stderr( | |
1e682848 | 178 | "[ERROR] no such subcommand: \ |
1671630b | 179 | `there-is-no-way-that-there-is-a-command-close-to-this` |
1e682848 | 180 | ", |
85984a87 | 181 | ).run(); |
79858995 KA |
182 | } |
183 | ||
184 | #[test] | |
185 | fn displays_subcommand_on_error() { | |
85984a87 DW |
186 | cargo_process("invalid-command") |
187 | .with_status(101) | |
188 | .with_stderr("[ERROR] no such subcommand: `invalid-command`\n") | |
189 | .run(); | |
6950bbb0 | 190 | } |
2badab8c | 191 | |
6950bbb0 AC |
192 | #[test] |
193 | fn override_cargo_home() { | |
2badab8c BA |
194 | let root = paths::root(); |
195 | let my_home = root.join("my_home"); | |
a6dad622 | 196 | fs::create_dir(&my_home).unwrap(); |
1e682848 AC |
197 | File::create(&my_home.join("config")) |
198 | .unwrap() | |
199 | .write_all( | |
200 | br#" | |
2badab8c BA |
201 | [cargo-new] |
202 | name = "foo" | |
203 | email = "bar" | |
204 | git = false | |
1e682848 | 205 | "#, |
85984a87 | 206 | ).unwrap(); |
2badab8c | 207 | |
85984a87 DW |
208 | cargo_process("new foo") |
209 | .env("USER", "foo") | |
210 | .env("CARGO_HOME", &my_home) | |
211 | .run(); | |
2badab8c BA |
212 | |
213 | let toml = paths::root().join("foo/Cargo.toml"); | |
a6dad622 | 214 | let mut contents = String::new(); |
1e682848 AC |
215 | File::open(&toml) |
216 | .unwrap() | |
217 | .read_to_string(&mut contents) | |
218 | .unwrap(); | |
a6dad622 | 219 | assert!(contents.contains(r#"authors = ["foo <bar>"]"#)); |
6950bbb0 | 220 | } |
2ff5f53b | 221 | |
015a08a0 VK |
222 | #[test] |
223 | fn cargo_subcommand_env() { | |
1e682848 AC |
224 | let src = format!( |
225 | r#" | |
015a08a0 VK |
226 | use std::env; |
227 | ||
228 | fn main() {{ | |
229 | println!("{{}}", env::var("{}").unwrap()); | |
230 | }} | |
1e682848 AC |
231 | "#, |
232 | cargo::CARGO_ENV | |
233 | ); | |
015a08a0 | 234 | |
85984a87 DW |
235 | let p = project() |
236 | .at("cargo-envtest") | |
015a08a0 | 237 | .file("Cargo.toml", &basic_bin_manifest("cargo-envtest")) |
d43ee1dd NK |
238 | .file("src/main.rs", &src) |
239 | .build(); | |
015a08a0 VK |
240 | |
241 | let target_dir = p.target_debug_dir(); | |
242 | ||
85984a87 | 243 | p.cargo("build").run(); |
015a08a0 VK |
244 | assert_that(&p.bin("cargo-envtest"), existing_file()); |
245 | ||
015a08a0 VK |
246 | let cargo = cargo_exe().canonicalize().unwrap(); |
247 | let mut path = path(); | |
248 | path.push(target_dir); | |
249 | let path = env::join_paths(path.iter()).unwrap(); | |
250 | ||
85984a87 DW |
251 | cargo_process("envtest") |
252 | .env("PATH", &path) | |
253 | .with_stdout(cargo.to_str().unwrap()) | |
254 | .run(); | |
015a08a0 VK |
255 | } |
256 | ||
deb1c1e1 AK |
257 | #[test] |
258 | fn cargo_subcommand_args() { | |
85984a87 DW |
259 | let p = project() |
260 | .at("cargo-foo") | |
ab19c483 | 261 | .file("Cargo.toml", &basic_manifest("cargo-foo", "0.0.1")) |
deb1c1e1 AK |
262 | .file( |
263 | "src/main.rs", | |
264 | r#" | |
265 | fn main() { | |
266 | let args: Vec<_> = ::std::env::args().collect(); | |
267 | println!("{:?}", args); | |
268 | } | |
269 | "#, | |
85984a87 | 270 | ).build(); |
deb1c1e1 | 271 | |
85984a87 | 272 | p.cargo("build").run(); |
deb1c1e1 AK |
273 | let cargo_foo_bin = p.bin("cargo-foo"); |
274 | assert_that(&cargo_foo_bin, existing_file()); | |
275 | ||
276 | let mut path = path(); | |
277 | path.push(p.target_debug_dir()); | |
278 | let path = env::join_paths(path.iter()).unwrap(); | |
279 | ||
85984a87 DW |
280 | cargo_process("foo bar -v --help") |
281 | .env("PATH", &path) | |
282 | .with_stdout(format!( | |
deb1c1e1 AK |
283 | r#"[{:?}, "foo", "bar", "-v", "--help"]"#, |
284 | cargo_foo_bin | |
85984a87 | 285 | )).run(); |
deb1c1e1 AK |
286 | } |
287 | ||
6950bbb0 AC |
288 | #[test] |
289 | fn cargo_help() { | |
85984a87 DW |
290 | cargo_process("").run(); |
291 | cargo_process("help").run(); | |
292 | cargo_process("-h").run(); | |
293 | cargo_process("help build").run(); | |
294 | cargo_process("build -h").run(); | |
295 | cargo_process("help help").run(); | |
6950bbb0 | 296 | } |
8dad57e8 | 297 | |
9e41e383 AR |
298 | #[test] |
299 | fn cargo_help_external_subcommand() { | |
300 | Package::new("cargo-fake-help", "1.0.0") | |
301 | .file( | |
302 | "src/main.rs", | |
303 | r#" | |
304 | fn main() { | |
305 | if ::std::env::args().nth(2) == Some(String::from("--help")) { | |
306 | println!("fancy help output"); | |
307 | } | |
308 | }"#, | |
85984a87 DW |
309 | ).publish(); |
310 | cargo_process("install cargo-fake-help").run(); | |
311 | cargo_process("help fake-help") | |
312 | .with_stdout("fancy help output\n") | |
313 | .run(); | |
9e41e383 AR |
314 | } |
315 | ||
6950bbb0 AC |
316 | #[test] |
317 | fn explain() { | |
85984a87 DW |
318 | cargo_process("--explain E0001") |
319 | .with_stdout_contains( | |
b0c181d9 | 320 | "This error suggests that the expression arm corresponding to the noted pattern", |
85984a87 | 321 | ).run(); |
6950bbb0 | 322 | } |
a4104914 KP |
323 | |
324 | // Test that the output of 'cargo -Z help' shows a different help screen with | |
325 | // all the -Z flags. | |
326 | #[test] | |
327 | fn z_flags_help() { | |
85984a87 DW |
328 | cargo_process("-Z help") |
329 | .with_stdout_contains( | |
9af095ad | 330 | " -Z unstable-options -- Allow the usage of unstable options such as --registry", |
85984a87 | 331 | ).run(); |
a4104914 | 332 | } |