cargo/tests/testsuite/cargo_command.rs

370 lines
9.5 KiB
Rust
Raw Normal View History

2019-11-25 02:42:45 +00:00
//! Tests for custom cargo commands and other global command features.
2015-02-06 07:27:53 +00:00
use std::env;
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::str;
use cargo_test_support::cargo_process;
use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_bin_manifest, basic_manifest, cargo_exe, project, Project};
2018-03-14 15:17:44 +00:00
#[cfg_attr(windows, allow(dead_code))]
enum FakeKind<'a> {
Executable,
2018-03-14 15:17:44 +00:00
Symlink { target: &'a Path },
}
2019-02-03 04:01:23 +00:00
/// Adds an empty file with executable flags (and platform-dependent suffix).
//
// TODO: move this to `Project` if other cases using this emerge.
2018-12-06 19:21:24 +00:00
fn fake_file(proj: Project, dir: &Path, name: &str, kind: &FakeKind<'_>) -> Project {
let path = proj
.root()
2018-03-14 15:17:44 +00:00
.join(dir)
.join(&format!("{}{}", name, env::consts::EXE_SUFFIX));
path.parent().unwrap().mkdir_p();
2017-09-24 14:26:37 +00:00
match *kind {
FakeKind::Executable => {
File::create(&path).unwrap();
make_executable(&path);
2018-03-14 15:17:44 +00:00
}
FakeKind::Symlink { target } => {
make_symlink(&path, target);
}
}
return proj;
#[cfg(unix)]
fn make_executable(p: &Path) {
use std::os::unix::prelude::*;
2016-04-19 18:59:55 +00:00
let mut perms = fs::metadata(p).unwrap().permissions();
let mode = perms.mode();
perms.set_mode(mode | 0o111);
fs::set_permissions(p, perms).unwrap();
}
#[cfg(windows)]
fn make_executable(_: &Path) {}
#[cfg(unix)]
fn make_symlink(p: &Path, t: &Path) {
2018-03-14 15:17:44 +00:00
::std::os::unix::fs::symlink(t, p).expect("Failed to create symlink");
}
#[cfg(windows)]
fn make_symlink(_: &Path, _: &Path) {
panic!("Not supported")
}
}
fn path() -> Vec<PathBuf> {
2017-09-24 14:26:37 +00:00
env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect()
2014-08-12 04:22:29 +00:00
}
2015-02-06 07:27:53 +00:00
#[cargo_test]
fn list_commands_with_descriptions() {
let p = project().build();
p.cargo("--list")
2018-12-08 11:19:47 +00:00
.with_stdout_contains(
" build Compile a local package and all of its dependencies",
)
2019-02-03 04:01:23 +00:00
// Assert that `read-manifest` prints the right one-line description followed by another
// command, indented.
2018-12-08 11:19:47 +00:00
.with_stdout_contains(
" read-manifest Print a JSON representation of a Cargo.toml manifest.",
)
.run();
}
#[cargo_test]
fn list_aliases_with_descriptions() {
let p = project().build();
p.cargo("--list")
.with_stdout_contains(" b alias: build")
.with_stdout_contains(" c alias: check")
.with_stdout_contains(" r alias: run")
.with_stdout_contains(" t alias: test")
.run();
}
#[cargo_test]
fn list_command_looks_at_path() {
let proj = project().build();
2018-03-14 15:17:44 +00:00
let proj = fake_file(
proj,
Path::new("path-test"),
"cargo-1",
&FakeKind::Executable,
);
let mut path = path();
2014-08-12 04:22:29 +00:00
path.push(proj.root().join("path-test"));
2015-02-06 07:27:53 +00:00
let path = env::join_paths(path.iter()).unwrap();
2018-12-08 11:19:47 +00:00
let output = cargo_process("-v --list")
.env("PATH", &path)
.exec_with_output()
.unwrap();
let output = str::from_utf8(&output.stdout).unwrap();
assert!(
output.contains("\n 1 "),
"missing 1: {}",
output
);
}
2019-02-03 04:01:23 +00:00
// Windows and symlinks don't currently mix well.
#[cfg(unix)]
#[cargo_test]
fn list_command_resolves_symlinks() {
let proj = project().build();
2018-03-14 15:17:44 +00:00
let proj = fake_file(
proj,
Path::new("path-test"),
"cargo-2",
&FakeKind::Symlink {
target: &cargo_exe(),
},
);
let mut path = path();
path.push(proj.root().join("path-test"));
let path = env::join_paths(path.iter()).unwrap();
2018-12-08 11:19:47 +00:00
let output = cargo_process("-v --list")
.env("PATH", &path)
.exec_with_output()
.unwrap();
let output = str::from_utf8(&output.stdout).unwrap();
assert!(
output.contains("\n 2 "),
"missing 2: {}",
output
);
}
#[cargo_test]
fn find_closest_biuld_to_build() {
cargo_process("biuld")
.with_status(101)
.with_stderr_contains(
2018-03-14 15:17:44 +00:00
"\
error: no such subcommand: `biuld`
<tab>Did you mean `build`?
2018-03-14 15:17:44 +00:00
",
2018-12-08 11:19:47 +00:00
)
.run();
// But, if we actually have `biuld`, it must work!
// https://github.com/rust-lang/cargo/issues/5201
Package::new("cargo-biuld", "1.0.0")
.file(
"src/main.rs",
r#"
fn main() {
println!("Similar, but not identical to, build");
}
"#,
2018-12-08 11:19:47 +00:00
)
.publish();
cargo_process("install cargo-biuld").run();
cargo_process("biuld")
.with_stdout("Similar, but not identical to, build\n")
.run();
cargo_process("--list")
.with_stdout_contains(
" build Compile a local package and all of its dependencies\n",
2018-12-08 11:19:47 +00:00
)
.with_stdout_contains(" biuld\n")
.run();
}
#[cargo_test]
fn find_closest_alias() {
let root = paths::root();
let my_home = root.join("my_home");
fs::create_dir(&my_home).unwrap();
fs::write(
&my_home.join("config"),
r#"
[alias]
myalias = "build"
"#,
)
.unwrap();
cargo_process("myalais")
.env("CARGO_HOME", &my_home)
.with_status(101)
.with_stderr_contains(
"\
error: no such subcommand: `myalais`
<tab>Did you mean `myalias`?
",
)
.run();
// But, if no alias is defined, it must not suggest one!
cargo_process("myalais")
.with_status(101)
.with_stderr_contains(
"\
error: no such subcommand: `myalais`
",
)
.with_stderr_does_not_contain(
"\
<tab>Did you mean `myalias`?
",
)
.run();
}
2019-02-03 04:01:23 +00:00
// If a subcommand is more than an edit distance of 3 away, we don't make a suggestion.
#[cargo_test]
fn find_closest_dont_correct_nonsense() {
cargo_process("there-is-no-way-that-there-is-a-command-close-to-this")
.cwd(&paths::root())
.with_status(101)
.with_stderr(
2018-03-14 15:17:44 +00:00
"[ERROR] no such subcommand: \
`there-is-no-way-that-there-is-a-command-close-to-this`
2018-03-14 15:17:44 +00:00
",
2018-12-08 11:19:47 +00:00
)
.run();
}
#[cargo_test]
fn displays_subcommand_on_error() {
cargo_process("invalid-command")
.with_status(101)
.with_stderr("[ERROR] no such subcommand: `invalid-command`\n")
.run();
}
#[cargo_test]
fn override_cargo_home() {
let root = paths::root();
let my_home = root.join("my_home");
fs::create_dir(&my_home).unwrap();
fs::write(
&my_home.join("config"),
r#"
[cargo-new]
name = "foo"
email = "bar"
git = false
"#,
)
.unwrap();
cargo_process("new foo")
.env("USER", "foo")
.env("CARGO_HOME", &my_home)
.run();
let toml = paths::root().join("foo/Cargo.toml");
let contents = fs::read_to_string(&toml).unwrap();
assert!(contents.contains(r#"authors = ["foo <bar>"]"#));
}
#[cargo_test]
fn cargo_subcommand_env() {
2018-03-14 15:17:44 +00:00
let src = format!(
r#"
use std::env;
fn main() {{
println!("{{}}", env::var("{}").unwrap());
}}
2018-03-14 15:17:44 +00:00
"#,
cargo::CARGO_ENV
);
let p = project()
.at("cargo-envtest")
.file("Cargo.toml", &basic_bin_manifest("cargo-envtest"))
.file("src/main.rs", &src)
.build();
let target_dir = p.target_debug_dir();
p.cargo("build").run();
2018-08-29 06:11:10 +00:00
assert!(p.bin("cargo-envtest").is_file());
let cargo = cargo_exe().canonicalize().unwrap();
let mut path = path();
path.push(target_dir);
let path = env::join_paths(path.iter()).unwrap();
cargo_process("envtest")
.env("PATH", &path)
.with_stdout(cargo.to_str().unwrap())
.run();
}
#[cargo_test]
fn cargo_subcommand_args() {
let p = project()
.at("cargo-foo")
2018-07-24 22:35:01 +00:00
.file("Cargo.toml", &basic_manifest("cargo-foo", "0.0.1"))
.file(
"src/main.rs",
r#"
fn main() {
let args: Vec<_> = ::std::env::args().collect();
println!("{:?}", args);
}
"#,
2018-12-08 11:19:47 +00:00
)
.build();
p.cargo("build").run();
let cargo_foo_bin = p.bin("cargo-foo");
2018-08-29 06:11:10 +00:00
assert!(cargo_foo_bin.is_file());
let mut path = path();
path.push(p.target_debug_dir());
let path = env::join_paths(path.iter()).unwrap();
cargo_process("foo bar -v --help")
.env("PATH", &path)
.with_stdout(
r#"["[CWD]/cargo-foo/target/debug/cargo-foo[EXE]", "foo", "bar", "-v", "--help"]"#,
)
.run();
}
#[cargo_test]
fn explain() {
cargo_process("--explain E0001")
.with_stdout_contains(
2018-03-14 15:43:41 +00:00
"This error suggests that the expression arm corresponding to the noted pattern",
2018-12-08 11:19:47 +00:00
)
.run();
}
#[cargo_test]
fn closed_output_ok() {
// Checks that closed output doesn't cause an error.
let mut p = cargo_process("--list").build_command();
p.stdout(Stdio::piped()).stderr(Stdio::piped());
let mut child = p.spawn().unwrap();
// Close stdout
drop(child.stdout.take());
// Read stderr
let mut s = String::new();
child
.stderr
.as_mut()
.unwrap()
.read_to_string(&mut s)
.unwrap();
let status = child.wait().unwrap();
assert!(status.success());
assert!(s.is_empty(), s);
}