//! Tests for the `cargo tree` command. use cargo_test_support::cross_compile::{self, alternate}; use cargo_test_support::prelude::*; use cargo_test_support::registry::{Dependency, Package}; use cargo_test_support::str; use cargo_test_support::{basic_manifest, git, project, rustc_host, Project}; use super::features2::switch_to_resolver_2; fn make_simple_proj() -> Project { Package::new("c", "1.0.0").publish(); Package::new("b", "1.0.0").dep("c", "1.0").publish(); Package::new("a", "1.0.0").dep("b", "1.0").publish(); Package::new("bdep", "1.0.0").dep("b", "1.0").publish(); Package::new("devdep", "1.0.0").dep("b", "1.0.0").publish(); project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" c = "1.0" [build-dependencies] bdep = "1.0" [dev-dependencies] devdep = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build() } #[cargo_test] fn simple() { // A simple test with a few different dependencies. let p = make_simple_proj(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); p.cargo("tree -p bdep") .with_stdout_data(str![[r#" bdep v1.0.0 └── b v1.0.0 └── c v1.0.0 "#]]) .run(); } #[cargo_test] fn virtual_workspace() { // Multiple packages in a virtual workspace. Package::new("somedep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "baz", "c"] "#, ) .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) .file("a/src/lib.rs", "") .file( "baz/Cargo.toml", r#" [package] name = "baz" version = "0.1.0" [dependencies] c = { path = "../c" } somedep = "1.0" "#, ) .file("baz/src/lib.rs", "") .file("c/Cargo.toml", &basic_manifest("c", "1.0.0")) .file("c/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) "#]]) .run(); p.cargo("tree") .cwd("baz") .with_stdout_data(str![[r#" baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 "#]]) .run(); // exclude baz p.cargo("tree --workspace --exclude baz") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); // exclude glob '*z' p.cargo("tree --workspace --exclude '*z'") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) c v1.0.0 ([ROOT]/foo/c) "#]]) .run(); // include glob '*z' p.cargo("tree -p '*z'") .with_stdout_data(str![[r#" baz v0.1.0 ([ROOT]/foo/baz) ├── c v1.0.0 ([ROOT]/foo/c) └── somedep v1.0.0 "#]]) .run(); } #[cargo_test] fn dedupe_edges() { // Works around https://github.com/rust-lang/cargo/issues/7985 Package::new("bitflags", "1.0.0").publish(); Package::new("manyfeat", "1.0.0") .feature("f1", &[]) .feature("f2", &[]) .feature("f3", &[]) .dep("bitflags", "1.0") .publish(); Package::new("a", "1.0.0") .feature_dep("manyfeat", "1.0", &["f1"]) .publish(); Package::new("b", "1.0.0") .feature_dep("manyfeat", "1.0", &["f2"]) .publish(); Package::new("c", "1.0.0") .feature_dep("manyfeat", "1.0", &["f3"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" b = "1.0" c = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── manyfeat v1.0.0 │ └── bitflags v1.0.0 ├── b v1.0.0 │ └── manyfeat v1.0.0 (*) └── c v1.0.0 └── manyfeat v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn renamed_deps() { // Handles renamed dependencies. Package::new("one", "1.0.0").publish(); Package::new("two", "1.0.0").publish(); Package::new("bar", "1.0.0").dep("one", "1.0").publish(); Package::new("bar", "2.0.0").dep("two", "1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dependencies] bar1 = {version = "1.0", package="bar"} bar2 = {version = "2.0", package="bar"} "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── bar v1.0.0 │ └── one v1.0.0 └── bar v2.0.0 └── two v1.0.0 "#]]) .run(); } #[cargo_test] fn source_kinds() { // Handles git and path sources. Package::new("regdep", "1.0.0").publish(); let git_project = git::new("gitdep", |p| { p.file("Cargo.toml", &basic_manifest("gitdep", "1.0.0")) .file("src/lib.rs", "") }); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [dependencies] regdep = "1.0" pathdep = {{ path = "pathdep" }} gitdep = {{ git = "{}" }} "#, git_project.url() ), ) .file("src/lib.rs", "") .file("pathdep/Cargo.toml", &basic_manifest("pathdep", "1.0.0")) .file("pathdep/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── gitdep v1.0.0 ([ROOTURL]/gitdep#[..]) ├── pathdep v1.0.0 ([ROOT]/foo/pathdep) └── regdep v1.0.0 "#]]) .run(); } #[cargo_test] fn features() { // Exercises a variety of feature behaviors. Package::new("optdep_default", "1.0.0").publish(); Package::new("optdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] optdep_default = { version = "1.0", optional = true } optdep = { version = "1.0", optional = true } [features] default = ["optdep_default"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) └── optdep_default v1.0.0 "#]]) .run(); p.cargo("tree --no-default-features") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree --all-features") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) ├── optdep v1.0.0 └── optdep_default v1.0.0 "#]]) .run(); p.cargo("tree --features optdep") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo) ├── optdep v1.0.0 └── optdep_default v1.0.0 "#]]) .run(); } #[cargo_test] fn filters_target() { // --target flag if cross_compile::disabled() { return; } Package::new("targetdep", "1.0.0").publish(); Package::new("hostdep", "1.0.0").publish(); Package::new("devdep", "1.0.0").publish(); Package::new("build_target_dep", "1.0.0").publish(); Package::new("build_host_dep", "1.0.0") .target_dep("targetdep", "1.0", alternate()) .target_dep("hostdep", "1.0", rustc_host()) .publish(); Package::new("pm_target", "1.0.0") .proc_macro(true) .publish(); Package::new("pm_host", "1.0.0").proc_macro(true).publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [target.'{alt}'.dependencies] targetdep = "1.0" pm_target = "1.0" [target.'{host}'.dependencies] hostdep = "1.0" pm_host = "1.0" [target.'{alt}'.dev-dependencies] devdep = "1.0" [target.'{alt}'.build-dependencies] build_target_dep = "1.0" [target.'{host}'.build-dependencies] build_host_dep = "1.0" "#, alt = alternate(), host = rustc_host() ), ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── pm_host v1.0.0 (proc-macro) [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 "#]]) .run(); p.cargo("tree --target") .arg(alternate()) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm_target v1.0.0 (proc-macro) └── targetdep v1.0.0 [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); p.cargo("tree --target") .arg(rustc_host()) .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── pm_host v1.0.0 (proc-macro) [build-dependencies] └── build_host_dep v1.0.0 └── hostdep v1.0.0 "#]]) .run(); p.cargo("tree --target=all") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 ├── pm_host v1.0.0 (proc-macro) ├── pm_target v1.0.0 (proc-macro) └── targetdep v1.0.0 [build-dependencies] ├── build_host_dep v1.0.0 │ ├── hostdep v1.0.0 │ └── targetdep v1.0.0 └── build_target_dep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); // no-proc-macro p.cargo("tree --target=all -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── hostdep v1.0.0 └── targetdep v1.0.0 [build-dependencies] ├── build_host_dep v1.0.0 │ ├── hostdep v1.0.0 │ └── targetdep v1.0.0 └── build_target_dep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); } #[cargo_test] fn no_selected_target_dependency() { // --target flag if cross_compile::disabled() { return; } Package::new("targetdep", "1.0.0").publish(); let p = project() .file( "Cargo.toml", &format!( r#" [package] name = "foo" version = "0.1.0" [target.'{alt}'.dependencies] targetdep = "1.0" "#, alt = alternate(), ), ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -i targetdep") .with_stderr_data(str![[r#" [WARNING] nothing to print. To find dependencies that require specific target platforms, try to use option `--target all` first, and then narrow your search scope accordingly. "#]]) .run(); p.cargo("tree -i targetdep --target all") .with_stdout_data(str![[r#" targetdep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn dep_kinds() { Package::new("inner-devdep", "1.0.0").publish(); Package::new("inner-builddep", "1.0.0").publish(); Package::new("inner-normal", "1.0.0").publish(); Package::new("inner-pm", "1.0.0").proc_macro(true).publish(); Package::new("inner-buildpm", "1.0.0") .proc_macro(true) .publish(); Package::new("normaldep", "1.0.0") .dep("inner-normal", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .publish(); Package::new("devdep", "1.0.0") .dep("inner-normal", "1.0") .dep("inner-pm", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .build_dep("inner-buildpm", "1.0") .publish(); Package::new("builddep", "1.0.0") .dep("inner-normal", "1.0") .dev_dep("inner-devdep", "1.0") .build_dep("inner-builddep", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] normaldep = "1.0" [dev-dependencies] devdep = "1.0" [build-dependencies] builddep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [build-dependencies] └── builddep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 ├── inner-normal v1.0.0 └── inner-pm v1.0.0 (proc-macro) [build-dependencies] ├── inner-builddep v1.0.0 └── inner-buildpm v1.0.0 (proc-macro) "#]]) .run(); p.cargo("tree -e no-dev") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [build-dependencies] └── builddep v1.0.0 └── inner-normal v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── normaldep v1.0.0 └── inner-normal v1.0.0 "#]]) .run(); p.cargo("tree -e dev,build") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── builddep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 [build-dependencies] ├── inner-builddep v1.0.0 └── inner-buildpm v1.0.0 (proc-macro) "#]]) .run(); p.cargo("tree -e dev,build,no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── builddep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 [dev-dependencies] └── devdep v1.0.0 [build-dependencies] └── inner-builddep v1.0.0 "#]]) .run(); } #[cargo_test] fn cyclic_dev_dep() { // Cyclical dev-dependency and inverse flag. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] dev-dep = { path = "dev-dep" } "#, ) .file("src/lib.rs", "") .file( "dev-dep/Cargo.toml", r#" [package] name = "dev-dep" version = "0.1.0" [dependencies] foo = { path=".." } "#, ) .file("dev-dep/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── dev-dep v0.1.0 ([ROOT]/foo/dev-dep) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree --invert foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── dev-dep v0.1.0 ([ROOT]/foo/dev-dep) [dev-dependencies] └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); } #[cargo_test] fn invert() { Package::new("b1", "1.0.0").dep("c", "1.0").publish(); Package::new("b2", "1.0.0").dep("d", "1.0").publish(); Package::new("c", "1.0.0").publish(); Package::new("d", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] b1 = "1.0" b2 = "1.0" c = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── b1 v1.0.0 │ └── c v1.0.0 ├── b2 v1.0.0 │ └── d v1.0.0 └── c v1.0.0 "#]]) .run(); p.cargo("tree --invert c") .with_stdout_data(str![[r#" c v1.0.0 ├── b1 v1.0.0 │ └── foo v0.1.0 ([ROOT]/foo) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn invert_with_build_dep() { // -i for a common dependency between normal and build deps. Package::new("common", "1.0.0").publish(); Package::new("bdep", "1.0.0").dep("common", "1.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] common = "1.0" [build-dependencies] bdep = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── common v1.0.0 [build-dependencies] └── bdep v1.0.0 └── common v1.0.0 "#]]) .run(); p.cargo("tree -i common") .with_stdout_data(str![[r#" common v1.0.0 ├── bdep v1.0.0 │ [build-dependencies] │ └── foo v0.1.0 ([ROOT]/foo) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn no_indent() { let p = make_simple_proj(); p.cargo("tree --prefix=none") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) a v1.0.0 b v1.0.0 c v1.0.0 c v1.0.0 bdep v1.0.0 b v1.0.0 (*) devdep v1.0.0 b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn prefix_depth() { let p = make_simple_proj(); p.cargo("tree --prefix=depth") .with_stdout_data(str![[r#" 0foo v0.1.0 ([ROOT]/foo) 1a v1.0.0 2b v1.0.0 3c v1.0.0 1c v1.0.0 1bdep v1.0.0 2b v1.0.0 (*) 1devdep v1.0.0 2b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn no_dedupe() { let p = make_simple_proj(); p.cargo("tree --no-dedupe") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 └── c v1.0.0 [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 └── c v1.0.0 "#]]) .run(); } #[cargo_test] fn no_dedupe_cycle() { // --no-dedupe with a dependency cycle let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] bar = {path = "bar"} "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "0.1.0" [dependencies] foo = {path=".."} "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── bar v0.1.0 ([ROOT]/foo/bar) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree --no-dedupe") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [dev-dependencies] └── bar v0.1.0 ([ROOT]/foo/bar) └── foo v0.1.0 ([ROOT]/foo) (*) "#]]) .run(); } #[cargo_test] fn duplicates() { Package::new("dog", "1.0.0").publish(); Package::new("dog", "2.0.0").publish(); Package::new("cat", "1.0.0").publish(); Package::new("cat", "2.0.0").publish(); Package::new("dep", "1.0.0") .dep("dog", "1.0") .dep("cat", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] dog1 = { version = "1.0", package = "dog" } dog2 = { version = "2.0", package = "dog" } "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [dependencies] dep = "1.0" cat = "2.0" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) ├── dog v1.0.0 └── dog v2.0.0 "#]]) .run(); p.cargo("tree -p b") .with_stdout_data(str![[r#" b v0.1.0 ([ROOT]/foo/b) ├── cat v2.0.0 └── dep v1.0.0 ├── cat v1.0.0 └── dog v1.0.0 "#]]) .run(); p.cargo("tree -p a -d") .with_stdout_data(str![[r#" dog v1.0.0 └── a v0.1.0 ([ROOT]/foo/a) dog v2.0.0 └── a v0.1.0 ([ROOT]/foo/a) "#]]) .run(); p.cargo("tree -p b -d") .with_stdout_data(str![[r#" cat v1.0.0 └── dep v1.0.0 └── b v0.1.0 ([ROOT]/foo/b) cat v2.0.0 └── b v0.1.0 ([ROOT]/foo/b) "#]]) .run(); } #[cargo_test] fn duplicates_with_target() { // --target flag if cross_compile::disabled() { return; } Package::new("a", "1.0.0").publish(); Package::new("dog", "1.0.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] a = "1.0" dog = "1.0" [build-dependencies] a = "1.0" dog = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); p.cargo("tree -d").with_stdout_data(str![""]).run(); p.cargo("tree -d --target") .arg(alternate()) .with_stdout_data(str![""]) .run(); p.cargo("tree -d --target") .arg(rustc_host()) .with_stdout_data(str![""]) .run(); p.cargo("tree -d --target=all") .with_stdout_data(str![""]) .run(); } #[cargo_test] fn duplicates_with_proc_macro() { Package::new("dupe-dep", "1.0.0").publish(); Package::new("dupe-dep", "2.0.0").publish(); Package::new("proc", "1.0.0") .proc_macro(true) .dep("dupe-dep", "1.0") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] proc = "1.0" dupe-dep = "2.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── dupe-dep v2.0.0 └── proc v1.0.0 (proc-macro) └── dupe-dep v1.0.0 "#]]) .run(); p.cargo("tree --duplicates") .with_stdout_data(str![[r#" dupe-dep v1.0.0 └── proc v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) dupe-dep v2.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree --duplicates --edges no-proc-macro") .with_stdout_data(str![""]) .run(); } #[cargo_test] fn charset() { let p = make_simple_proj(); p.cargo("tree --charset ascii") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) |-- a v1.0.0 | `-- b v1.0.0 | `-- c v1.0.0 `-- c v1.0.0 [build-dependencies] `-- bdep v1.0.0 `-- b v1.0.0 (*) [dev-dependencies] `-- devdep v1.0.0 `-- b v1.0.0 (*) "#]]) .run(); } #[cargo_test] fn format() { Package::new("dep", "1.0.0").publish(); Package::new("other-dep", "1.0.0").publish(); Package::new("dep_that_is_awesome", "1.0.0") .file( "Cargo.toml", r#" [package] name = "dep_that_is_awesome" version = "1.0.0" [lib] name = "awesome_dep" "#, ) .file("src/lib.rs", "pub struct Straw;") .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" license = "MIT" repository = "https://github.com/rust-lang/cargo" [dependencies] dep = {version="1.0", optional=true} other-dep = {version="1.0", optional=true} dep_that_is_awesome = {version="1.0", optional=true} [features] default = ["foo"] foo = ["bar"] bar = [] "#, ) .file("src/main.rs", "") .build(); p.cargo("tree --format <<<{p}>>>") .with_stdout_data(str![[r#" <<>> "#]]) .run(); p.cargo("tree --format {}") .with_stderr_data(str![[r#" [ERROR] tree format `{}` not valid Caused by: unsupported pattern `` "#]]) .with_status(101) .run(); p.cargo("tree --format {p}-{{hello}}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo)-{hello} "#]]) .run(); p.cargo("tree --format") .arg("{p} {l} {r}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) MIT https://github.com/rust-lang/cargo "#]]) .run(); p.cargo("tree --format") .arg("{p} {f}") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) bar,default,foo "#]]) .run(); p.cargo("tree --all-features --format") .arg("{p} [{f}]") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [bar,default,dep,dep_that_is_awesome,foo,other-dep] ├── dep v1.0.0 [] ├── dep_that_is_awesome v1.0.0 [] └── other-dep v1.0.0 [] "#]]) .run(); p.cargo("tree") .arg("--features=other-dep,dep_that_is_awesome") .arg("--format={lib}") .with_stdout_data(str![[r#" ├── awesome_dep └── other_dep "#]]) .run(); } #[cargo_test] fn dev_dep_feature() { // New feature resolver with optional dep Package::new("optdep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dev-dependencies] bar = { version = "1.0", features = ["optdep"] } [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Old behavior. p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [dev-dependencies] └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [dev-dependencies] └── bar v1.0.0 (*) "#]]) .run(); p.cargo("tree -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 "#]]) .run(); } #[cargo_test] fn host_dep_feature() { // New feature resolver with optional build dep Package::new("optdep", "1.0.0").publish(); Package::new("bar", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [build-dependencies] bar = { version = "1.0", features = ["optdep"] } [dependencies] bar = "1.0" "#, ) .file("src/lib.rs", "") .file("build.rs", "fn main() {}") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 └── optdep v1.0.0 [build-dependencies] └── bar v1.0.0 (*) "#]]) .run(); // -p p.cargo("tree -p bar") .with_stdout_data(str![[r#" bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); // invert p.cargo("tree -i optdep") .with_stdout_data(str![[r#" optdep v1.0.0 └── bar v1.0.0 └── foo v0.1.0 ([ROOT]/foo) [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── bar v1.0.0 [build-dependencies] └── bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -p bar") .with_stdout_data(str![[r#" bar v1.0.0 bar v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -i optdep") .with_stdout_data(str![[r#" optdep v1.0.0 └── bar v1.0.0 [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // Check that -d handles duplicates with features. p.cargo("tree -d") .with_stdout_data(str![[r#" bar v1.0.0 └── foo v0.1.0 ([ROOT]/foo) bar v1.0.0 [build-dependencies] └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn proc_macro_features() { // New feature resolver with a proc-macro Package::new("optdep", "1.0.0").publish(); Package::new("somedep", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); Package::new("pm", "1.0.0") .proc_macro(true) .feature_dep("somedep", "1.0", &["optdep"]) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] pm = "1.0" somedep = "1.0" "#, ) .file("src/lib.rs", "") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm v1.0.0 (proc-macro) │ └── somedep v1.0.0 │ └── optdep v1.0.0 └── somedep v1.0.0 (*) "#]]) .run(); // Old behavior + no-proc-macro p.cargo("tree -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // -p p.cargo("tree -p somedep") .with_stdout_data(str![[r#" somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // -p -e no-proc-macro p.cargo("tree -p somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); // invert p.cargo("tree -i somedep") .with_stdout_data(str![[r#" somedep v1.0.0 ├── foo v0.1.0 ([ROOT]/foo) └── pm v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // invert + no-proc-macro p.cargo("tree -i somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); // New behavior. switch_to_resolver_2(&p); // Note the missing (*) p.cargo("tree") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── pm v1.0.0 (proc-macro) │ └── somedep v1.0.0 │ └── optdep v1.0.0 └── somedep v1.0.0 "#]]) .run(); p.cargo("tree -e no-proc-macro") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── somedep v1.0.0 "#]]) .run(); p.cargo("tree -p somedep") .with_stdout_data(str![[r#" somedep v1.0.0 somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -i somedep") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) somedep v1.0.0 └── pm v1.0.0 (proc-macro) └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -i somedep -e no-proc-macro") .with_stdout_data(str![[r#" somedep v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn itarget_opt_dep() { // New feature resolver with optional target dep Package::new("optdep", "1.0.0").publish(); Package::new("common", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dependencies] common = "1.0" [target.'cfg(whatever)'.dependencies] common = { version = "1.0", features = ["optdep"] } "#, ) .file("src/lib.rs", "") .build(); // Old behavior p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── common v1.0.0 └── optdep v1.0.0 "#]]) .run(); // New behavior. switch_to_resolver_2(&p); p.cargo("tree") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) └── common v1.0.0 "#]]) .run(); } #[cargo_test] fn ambiguous_name() { // -p that is ambiguous. Package::new("dep", "1.0.0").publish(); Package::new("dep", "2.0.0").publish(); Package::new("bar", "1.0.0").dep("dep", "2.0").publish(); let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" [dependencies] dep = "1.0" bar = "1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -p dep") .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 4 packages to latest compatible versions [ADDING] dep v1.0.0 (latest: v2.0.0) [DOWNLOADING] crates ... [DOWNLOADED] dep v2.0.0 (registry `dummy-registry`) [DOWNLOADED] dep v1.0.0 (registry `dummy-registry`) [DOWNLOADED] bar v1.0.0 (registry `dummy-registry`) [ERROR] There are multiple `dep` packages in your project, and the specification `dep` is ambiguous. Please re-run this command with one of the following specifications: dep@1.0.0 dep@2.0.0 "#]]) .with_status(101) .run(); } #[cargo_test] fn workspace_features_are_local() { // The features for workspace packages should be the same as `cargo build` // (i.e., the features selected depend on the "current" package). Package::new("optdep", "1.0.0").publish(); Package::new("somedep", "1.0.0") .add_dep(Dependency::new("optdep", "1.0").optional(true)) .publish(); let p = project() .file( "Cargo.toml", r#" [workspace] members = ["a", "b"] "#, ) .file( "a/Cargo.toml", r#" [package] name = "a" version = "0.1.0" [dependencies] somedep = {version="1.0", features=["optdep"]} "#, ) .file("a/src/lib.rs", "") .file( "b/Cargo.toml", r#" [package] name = "b" version = "0.1.0" [dependencies] somedep = "1.0" "#, ) .file("b/src/lib.rs", "") .build(); p.cargo("tree") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) └── somedep v1.0.0 └── optdep v1.0.0 b v0.1.0 ([ROOT]/foo/b) └── somedep v1.0.0 (*) "#]]) .run(); p.cargo("tree -p a") .with_stdout_data(str![[r#" a v0.1.0 ([ROOT]/foo/a) └── somedep v1.0.0 └── optdep v1.0.0 "#]]) .run(); p.cargo("tree -p b") .with_stdout_data(str![[r#" b v0.1.0 ([ROOT]/foo/b) └── somedep v1.0.0 "#]]) .run(); } #[cargo_test] fn unknown_edge_kind() { let p = project() .file("Cargo.toml", "") .file("src/lib.rs", "") .build(); p.cargo("tree -e unknown") .with_stderr_data(str![[r#" [ERROR] unknown edge kind `unknown`, valid values are "normal", "build", "dev", "no-normal", "no-build", "no-dev", "no-proc-macro", "features", or "all" "#]]) .with_status(101) .run(); } #[cargo_test] fn mixed_no_edge_kinds() { let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.1.0" "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e no-build,normal") .with_stderr_data(str![[r#" [ERROR] `normal` dependency kind cannot be mixed with "no-normal", "no-build", or "no-dev" dependency kinds "#]]) .with_status(101) .run(); // `no-proc-macro` can be mixed with others p.cargo("tree -e no-proc-macro,normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn depth_limit() { let p = make_simple_proj(); p.cargo("tree --depth 0") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) [build-dependencies] [dev-dependencies] "#]]) .run(); p.cargo("tree --depth 1") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 [dev-dependencies] └── devdep v1.0.0 "#]]) .run(); p.cargo("tree --depth 2") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // specify a package p.cargo("tree -p bdep --depth 1") .with_stdout_data(str![[r#" bdep v1.0.0 └── b v1.0.0 "#]]) .run(); // different prefix p.cargo("tree --depth 1 --prefix depth") .with_stdout_data(str![[r#" 0foo v0.1.0 ([ROOT]/foo) 1a v1.0.0 1c v1.0.0 1bdep v1.0.0 1devdep v1.0.0 "#]]) .run(); // with edge-kinds p.cargo("tree --depth 1 -e no-dev") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 "#]]) .run(); // invert p.cargo("tree --depth 1 --invert c") .with_stdout_data(str![[r#" c v1.0.0 ├── b v1.0.0 └── foo v0.1.0 ([ROOT]/foo) "#]]) .run(); } #[cargo_test] fn prune() { let p = make_simple_proj(); p.cargo("tree --prune c") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // multiple prune p.cargo("tree --prune c --prune bdep") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 [build-dependencies] [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // with edge-kinds p.cargo("tree --prune c -e normal") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) └── a v1.0.0 └── b v1.0.0 "#]]) .run(); // pruning self does not works p.cargo("tree --prune foo") .with_stdout_data(str![[r#" foo v0.1.0 ([ROOT]/foo) ├── a v1.0.0 │ └── b v1.0.0 │ └── c v1.0.0 └── c v1.0.0 [build-dependencies] └── bdep v1.0.0 └── b v1.0.0 (*) [dev-dependencies] └── devdep v1.0.0 └── b v1.0.0 (*) "#]]) .run(); // dep not exist p.cargo("tree --prune no-dep") .with_stderr_data(str![[r#" [ERROR] package ID specification `no-dep` did not match any packages Did you mean `bdep`? "#]]) .with_status(101) .run(); } #[cargo_test] fn cyclic_features() { // Check for stack overflow with cyclic features (oops!). let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [features] a = ["b"] b = ["a"] default = ["a"] "#, ) .file("src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) "#]]) .run(); p.cargo("tree -e features -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ ├── foo feature "b" │ │ └── foo feature "a" (*) │ └── foo feature "default" (command-line) ├── foo feature "b" (*) └── foo feature "default" (command-line) "#]]) .run(); } #[cargo_test] fn dev_dep_cycle_with_feature() { // Cycle with features and a dev-dependency. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dev-dependencies] bar = { path = "bar" } [features] a = ["bar/feat1"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [dependencies] foo = { path = ".." } [features] feat1 = ["foo/a"] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -e features --features a") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) [dev-dependencies] └── bar feature "default" └── bar v1.0.0 ([ROOT]/foo/bar) └── foo feature "default" (command-line) └── foo v1.0.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree -e features --features a -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" (command-line) │ └── bar feature "feat1" │ └── foo feature "a" (command-line) (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); } #[cargo_test] fn dev_dep_cycle_with_feature_nested() { // Checks for an issue where a cyclic dev dependency tries to activate a // feature on its parent that tries to activate the feature back on the // dev-dependency. let p = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "1.0.0" [dev-dependencies] bar = { path = "bar" } [features] a = ["bar/feat1"] b = ["a"] "#, ) .file("src/lib.rs", "") .file( "bar/Cargo.toml", r#" [package] name = "bar" version = "1.0.0" [dependencies] foo = { path = ".." } [features] feat1 = ["foo/b"] "#, ) .file("bar/src/lib.rs", "") .build(); p.cargo("tree -e features") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) [dev-dependencies] └── bar feature "default" └── bar v1.0.0 ([ROOT]/foo/bar) └── foo feature "default" (command-line) └── foo v1.0.0 ([ROOT]/foo) (*) "#]]) .run(); p.cargo("tree -e features --features a -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" (command-line) │ └── foo feature "b" │ └── bar feature "feat1" │ └── foo feature "a" (command-line) (*) ├── foo feature "b" (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); p.cargo("tree -e features --features b -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ └── foo feature "b" (command-line) │ └── bar feature "feat1" │ └── foo feature "a" (*) ├── foo feature "b" (command-line) (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (*) "#]]) .run(); p.cargo("tree -e features --features bar/feat1 -i foo") .with_stdout_data(str![[r#" foo v1.0.0 ([ROOT]/foo) ├── foo feature "a" │ └── foo feature "b" │ └── bar feature "feat1" (command-line) │ └── foo feature "a" (*) ├── foo feature "b" (*) └── foo feature "default" (command-line) └── bar v1.0.0 ([ROOT]/foo/bar) ├── bar feature "default" │ [dev-dependencies] │ └── foo v1.0.0 ([ROOT]/foo) (*) └── bar feature "feat1" (command-line) (*) "#]]) .run(); }