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