summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cargo_metadata/tests
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cargo_metadata/tests')
-rw-r--r--third_party/rust/cargo_metadata/tests/selftest.rs163
-rw-r--r--third_party/rust/cargo_metadata/tests/test_samples.rs647
2 files changed, 810 insertions, 0 deletions
diff --git a/third_party/rust/cargo_metadata/tests/selftest.rs b/third_party/rust/cargo_metadata/tests/selftest.rs
new file mode 100644
index 0000000000..d6ab61832d
--- /dev/null
+++ b/third_party/rust/cargo_metadata/tests/selftest.rs
@@ -0,0 +1,163 @@
+use std::env::current_dir;
+use std::path::PathBuf;
+
+use semver::Version;
+
+use cargo_metadata::{CargoOpt, Error, MetadataCommand};
+use serde::Deserialize;
+
+#[derive(Debug, PartialEq, Eq, Deserialize)]
+struct TestPackageMetadata {
+ some_field: bool,
+ other_field: String,
+}
+
+#[test]
+fn metadata() {
+ let metadata = MetadataCommand::new().no_deps().exec().unwrap();
+
+ let this = &metadata.packages[0];
+ assert_eq!(this.name, "cargo_metadata");
+ assert_eq!(this.targets.len(), 3);
+
+ let lib = this
+ .targets
+ .iter()
+ .find(|t| t.name == "cargo_metadata")
+ .unwrap();
+ assert_eq!(lib.kind[0], "lib");
+ assert_eq!(lib.crate_types[0], "lib");
+
+ let selftest = this.targets.iter().find(|t| t.name == "selftest").unwrap();
+ assert_eq!(selftest.name, "selftest");
+ assert_eq!(selftest.kind[0], "test");
+ assert_eq!(selftest.crate_types[0], "bin");
+
+ let package_metadata = &metadata.packages[0]
+ .metadata
+ .as_object()
+ .expect("package.metadata must be a table.");
+ assert_eq!(package_metadata.len(), 1);
+
+ let value = package_metadata.get("cargo_metadata_test").unwrap();
+ let test_package_metadata: TestPackageMetadata = serde_json::from_value(value.clone()).unwrap();
+ assert_eq!(
+ test_package_metadata,
+ TestPackageMetadata {
+ some_field: true,
+ other_field: "foo".into(),
+ }
+ );
+}
+
+#[test]
+fn builder_interface() {
+ let _ = MetadataCommand::new()
+ .manifest_path("Cargo.toml")
+ .exec()
+ .unwrap();
+ let _ = MetadataCommand::new()
+ .manifest_path(String::from("Cargo.toml"))
+ .exec()
+ .unwrap();
+ let _ = MetadataCommand::new()
+ .manifest_path(PathBuf::from("Cargo.toml"))
+ .exec()
+ .unwrap();
+ let _ = MetadataCommand::new()
+ .manifest_path("Cargo.toml")
+ .no_deps()
+ .exec()
+ .unwrap();
+ let _ = MetadataCommand::new()
+ .manifest_path("Cargo.toml")
+ .features(CargoOpt::AllFeatures)
+ .exec()
+ .unwrap();
+ let _ = MetadataCommand::new()
+ .manifest_path("Cargo.toml")
+ .current_dir(current_dir().unwrap())
+ .exec()
+ .unwrap();
+}
+
+#[test]
+fn error1() {
+ match MetadataCommand::new().manifest_path("foo").exec() {
+ Err(Error::CargoMetadata { stderr }) => assert_eq!(
+ stderr.trim(),
+ "error: the manifest-path must be a path to a Cargo.toml file"
+ ),
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn error2() {
+ match MetadataCommand::new()
+ .manifest_path("foo/Cargo.toml")
+ .exec()
+ {
+ Err(Error::CargoMetadata { stderr }) => assert_eq!(
+ stderr.trim(),
+ "error: manifest path `foo/Cargo.toml` does not exist"
+ ),
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn cargo_path() {
+ match MetadataCommand::new()
+ .cargo_path("this does not exist")
+ .exec()
+ {
+ Err(Error::Io(e)) => assert_eq!(e.kind(), std::io::ErrorKind::NotFound),
+ _ => unreachable!(),
+ }
+}
+
+#[test]
+fn metadata_deps() {
+ std::env::set_var("CARGO_PROFILE", "3");
+ let metadata = MetadataCommand::new()
+ .manifest_path("Cargo.toml")
+ .exec()
+ .unwrap();
+ let this_id = metadata
+ .workspace_members
+ .first()
+ .expect("Did not find ourselves");
+ let this = &metadata[this_id];
+
+ assert_eq!(this.name, "cargo_metadata");
+
+ let workspace_packages = metadata.workspace_packages();
+ assert_eq!(workspace_packages.len(), 1);
+ assert_eq!(&workspace_packages[0].id, this_id);
+
+ let lib = this
+ .targets
+ .iter()
+ .find(|t| t.name == "cargo_metadata")
+ .unwrap();
+ assert_eq!(lib.kind[0], "lib");
+ assert_eq!(lib.crate_types[0], "lib");
+
+ let selftest = this.targets.iter().find(|t| t.name == "selftest").unwrap();
+ assert_eq!(selftest.name, "selftest");
+ assert_eq!(selftest.kind[0], "test");
+ assert_eq!(selftest.crate_types[0], "bin");
+
+ let dependencies = &this.dependencies;
+
+ let serde = dependencies
+ .iter()
+ .find(|dep| dep.name == "serde")
+ .expect("Did not find serde dependency");
+
+ assert_eq!(serde.kind, cargo_metadata::DependencyKind::Normal);
+ assert!(!serde.req.matches(&Version::parse("1.0.0").unwrap()));
+ assert!(serde.req.matches(&Version::parse("1.99.99").unwrap()));
+ assert!(!serde.req.matches(&Version::parse("2.0.0").unwrap()));
+}
diff --git a/third_party/rust/cargo_metadata/tests/test_samples.rs b/third_party/rust/cargo_metadata/tests/test_samples.rs
new file mode 100644
index 0000000000..3c747c595a
--- /dev/null
+++ b/third_party/rust/cargo_metadata/tests/test_samples.rs
@@ -0,0 +1,647 @@
+extern crate cargo_metadata;
+extern crate semver;
+#[macro_use]
+extern crate serde_json;
+
+use camino::Utf8PathBuf;
+use cargo_metadata::{CargoOpt, DependencyKind, Edition, Metadata, MetadataCommand};
+
+#[test]
+fn old_minimal() {
+ // Output from oldest supported version (1.24).
+ // This intentionally has as many null fields as possible.
+ // 1.8 is when metadata was introduced.
+ // Older versions not supported because the following are required:
+ // - `workspace_members` added in 1.13
+ // - `target_directory` added in 1.19
+ // - `workspace_root` added in 1.24
+ let json = r#"
+{
+ "packages": [
+ {
+ "name": "foo",
+ "version": "0.1.0",
+ "id": "foo 0.1.0 (path+file:///foo)",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": null,
+ "dependencies": [
+ {
+ "name": "somedep",
+ "source": null,
+ "req": "^1.0",
+ "kind": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "foo",
+ "src_path": "/foo/src/main.rs"
+ }
+ ],
+ "features": {},
+ "manifest_path": "/foo/Cargo.toml"
+ }
+ ],
+ "workspace_members": [
+ "foo 0.1.0 (path+file:///foo)"
+ ],
+ "resolve": null,
+ "target_directory": "/foo/target",
+ "version": 1,
+ "workspace_root": "/foo"
+}
+"#;
+ let meta: Metadata = serde_json::from_str(json).unwrap();
+ assert_eq!(meta.packages.len(), 1);
+ let pkg = &meta.packages[0];
+ assert_eq!(pkg.name, "foo");
+ assert_eq!(pkg.version, semver::Version::parse("0.1.0").unwrap());
+ assert_eq!(pkg.authors.len(), 0);
+ assert_eq!(pkg.id.to_string(), "foo 0.1.0 (path+file:///foo)");
+ assert_eq!(pkg.description, None);
+ assert_eq!(pkg.license, None);
+ assert_eq!(pkg.license_file, None);
+ assert_eq!(pkg.default_run, None);
+ assert_eq!(pkg.rust_version, None);
+ assert_eq!(pkg.dependencies.len(), 1);
+ let dep = &pkg.dependencies[0];
+ assert_eq!(dep.name, "somedep");
+ assert_eq!(dep.source, None);
+ assert_eq!(dep.req, semver::VersionReq::parse("^1.0").unwrap());
+ assert_eq!(dep.kind, DependencyKind::Normal);
+ assert!(!dep.optional);
+ assert!(dep.uses_default_features);
+ assert_eq!(dep.features.len(), 0);
+ assert!(dep.target.is_none());
+ assert_eq!(dep.rename, None);
+ assert_eq!(dep.registry, None);
+ assert_eq!(pkg.targets.len(), 1);
+ let target = &pkg.targets[0];
+ assert_eq!(target.name, "foo");
+ assert_eq!(target.kind, vec!["bin"]);
+ assert_eq!(target.crate_types, vec!["bin"]);
+ assert_eq!(target.required_features.len(), 0);
+ assert_eq!(target.src_path, "/foo/src/main.rs");
+ assert_eq!(target.edition, Edition::E2015);
+ assert!(target.doctest);
+ assert!(target.test);
+ assert!(target.doc);
+ assert_eq!(pkg.features.len(), 0);
+ assert_eq!(pkg.manifest_path, "/foo/Cargo.toml");
+ assert_eq!(pkg.categories.len(), 0);
+ assert_eq!(pkg.keywords.len(), 0);
+ assert_eq!(pkg.readme, None);
+ assert_eq!(pkg.repository, None);
+ assert_eq!(pkg.homepage, None);
+ assert_eq!(pkg.documentation, None);
+ assert_eq!(pkg.edition, Edition::E2015);
+ assert_eq!(pkg.metadata, serde_json::Value::Null);
+ assert_eq!(pkg.links, None);
+ assert_eq!(pkg.publish, None);
+ assert_eq!(meta.workspace_members.len(), 1);
+ assert_eq!(
+ meta.workspace_members[0].to_string(),
+ "foo 0.1.0 (path+file:///foo)"
+ );
+ assert!(meta.resolve.is_none());
+ assert_eq!(meta.workspace_root, "/foo");
+ assert_eq!(meta.workspace_metadata, serde_json::Value::Null);
+ assert_eq!(meta.target_directory, "/foo/target");
+}
+
+macro_rules! sorted {
+ ($e:expr) => {{
+ let mut v = $e.clone();
+ v.sort();
+ v
+ }};
+}
+
+fn cargo_version() -> semver::Version {
+ let output = std::process::Command::new("cargo")
+ .arg("-V")
+ .output()
+ .expect("Failed to exec cargo.");
+ let out = std::str::from_utf8(&output.stdout)
+ .expect("invalid utf8")
+ .trim();
+ let split: Vec<&str> = out.split_whitespace().collect();
+ assert!(split.len() >= 2, "cargo -V output is unexpected: {}", out);
+ let mut ver = semver::Version::parse(split[1]).expect("cargo -V semver could not be parsed");
+ // Don't care about metadata, it is awkward to compare.
+ ver.pre = semver::Prerelease::EMPTY;
+ ver.build = semver::BuildMetadata::EMPTY;
+ ver
+}
+
+#[derive(serde::Deserialize, PartialEq, Eq, Debug)]
+struct WorkspaceMetadata {
+ testobject: TestObject,
+}
+
+#[derive(serde::Deserialize, PartialEq, Eq, Debug)]
+struct TestObject {
+ myvalue: String,
+}
+
+#[test]
+fn all_the_fields() {
+ // All the fields currently generated as of 1.60. This tries to exercise as
+ // much as possible.
+ let ver = cargo_version();
+ let minimum = semver::Version::parse("1.56.0").unwrap();
+ if ver < minimum {
+ // edition added in 1.30
+ // rename added in 1.31
+ // links added in 1.33
+ // doctest added in 1.37
+ // publish added in 1.39
+ // dep_kinds added in 1.41
+ // test added in 1.47
+ // homepage added in 1.49
+ // documentation added in 1.49
+ // doc added in 1.50
+ // path added in 1.51
+ // default_run added in 1.55
+ // rust_version added in 1.58
+ eprintln!("Skipping all_the_fields test, cargo {} is too old.", ver);
+ return;
+ }
+ let meta = MetadataCommand::new()
+ .manifest_path("tests/all/Cargo.toml")
+ .exec()
+ .unwrap();
+ assert_eq!(meta.workspace_root.file_name().unwrap(), "all");
+ assert_eq!(
+ serde_json::from_value::<WorkspaceMetadata>(meta.workspace_metadata).unwrap(),
+ WorkspaceMetadata {
+ testobject: TestObject {
+ myvalue: "abc".to_string()
+ }
+ }
+ );
+ assert_eq!(meta.workspace_members.len(), 1);
+ assert!(meta.workspace_members[0].to_string().starts_with("all"));
+
+ assert_eq!(meta.packages.len(), 9);
+ let all = meta.packages.iter().find(|p| p.name == "all").unwrap();
+ assert_eq!(all.version, semver::Version::parse("0.1.0").unwrap());
+ assert_eq!(all.authors, vec!["Jane Doe <user@example.com>"]);
+ assert!(all.id.to_string().starts_with("all"));
+ assert_eq!(all.description, Some("Package description.".to_string()));
+ assert_eq!(all.license, Some("MIT/Apache-2.0".to_string()));
+ assert_eq!(all.license_file, Some(Utf8PathBuf::from("LICENSE")));
+ assert!(all.license_file().unwrap().ends_with("tests/all/LICENSE"));
+ assert_eq!(all.publish, Some(vec![]));
+ assert_eq!(all.links, Some("foo".to_string()));
+ assert_eq!(all.default_run, Some("otherbin".to_string()));
+ if ver >= semver::Version::parse("1.58.0").unwrap() {
+ assert_eq!(
+ all.rust_version,
+ Some(semver::VersionReq::parse("1.56").unwrap())
+ );
+ }
+
+ assert_eq!(all.dependencies.len(), 8);
+ let bitflags = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "bitflags")
+ .unwrap();
+ assert_eq!(
+ bitflags.source,
+ Some("registry+https://github.com/rust-lang/crates.io-index".to_string())
+ );
+ assert!(bitflags.optional);
+ assert_eq!(bitflags.req, semver::VersionReq::parse("^1.0").unwrap());
+
+ let path_dep = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "path-dep")
+ .unwrap();
+ assert_eq!(path_dep.source, None);
+ assert_eq!(path_dep.kind, DependencyKind::Normal);
+ assert_eq!(path_dep.req, semver::VersionReq::parse("*").unwrap());
+ assert_eq!(
+ path_dep.path.as_ref().map(|p| p.ends_with("path-dep")),
+ Some(true),
+ );
+
+ all.dependencies
+ .iter()
+ .find(|d| d.name == "namedep")
+ .unwrap();
+
+ let featdep = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "featdep")
+ .unwrap();
+ assert_eq!(featdep.features, vec!["i128"]);
+ assert!(!featdep.uses_default_features);
+
+ let renamed = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "oldname")
+ .unwrap();
+ assert_eq!(renamed.rename, Some("newname".to_string()));
+
+ let devdep = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "devdep")
+ .unwrap();
+ assert_eq!(devdep.kind, DependencyKind::Development);
+
+ let bdep = all.dependencies.iter().find(|d| d.name == "bdep").unwrap();
+ assert_eq!(bdep.kind, DependencyKind::Build);
+
+ let windep = all
+ .dependencies
+ .iter()
+ .find(|d| d.name == "windep")
+ .unwrap();
+ assert_eq!(
+ windep.target.as_ref().map(|x| x.to_string()),
+ Some("cfg(windows)".to_string())
+ );
+
+ macro_rules! get_file_name {
+ ($v:expr) => {
+ all.targets
+ .iter()
+ .find(|t| t.src_path.file_name().unwrap() == $v)
+ .unwrap()
+ };
+ }
+ assert_eq!(all.targets.len(), 8);
+ let lib = get_file_name!("lib.rs");
+ assert_eq!(lib.name, "all");
+ assert_eq!(sorted!(lib.kind), vec!["cdylib", "rlib", "staticlib"]);
+ assert_eq!(
+ sorted!(lib.crate_types),
+ vec!["cdylib", "rlib", "staticlib"]
+ );
+ assert_eq!(lib.required_features.len(), 0);
+ assert_eq!(lib.edition, Edition::E2018);
+ assert!(lib.doctest);
+ assert!(lib.test);
+ assert!(lib.doc);
+
+ let main = get_file_name!("main.rs");
+ assert_eq!(main.crate_types, vec!["bin"]);
+ assert_eq!(main.kind, vec!["bin"]);
+ assert!(!main.doctest);
+ assert!(main.test);
+ assert!(main.doc);
+
+ let otherbin = get_file_name!("otherbin.rs");
+ assert_eq!(otherbin.edition, Edition::E2015);
+ assert!(!otherbin.doc);
+
+ let reqfeat = get_file_name!("reqfeat.rs");
+ assert_eq!(reqfeat.required_features, vec!["feat2"]);
+
+ let ex1 = get_file_name!("ex1.rs");
+ assert_eq!(ex1.kind, vec!["example"]);
+ assert!(!ex1.test);
+
+ let t1 = get_file_name!("t1.rs");
+ assert_eq!(t1.kind, vec!["test"]);
+
+ let b1 = get_file_name!("b1.rs");
+ assert_eq!(b1.kind, vec!["bench"]);
+
+ let build = get_file_name!("build.rs");
+ assert_eq!(build.kind, vec!["custom-build"]);
+
+ if ver >= semver::Version::parse("1.60.0").unwrap() {
+ // 1.60 now reports optional dependencies within the features table
+ assert_eq!(all.features.len(), 4);
+ assert_eq!(all.features["bitflags"], vec!["dep:bitflags"]);
+ } else {
+ assert_eq!(all.features.len(), 3);
+ }
+ assert_eq!(all.features["feat1"].len(), 0);
+ assert_eq!(all.features["feat2"].len(), 0);
+ assert_eq!(sorted!(all.features["default"]), vec!["bitflags", "feat1"]);
+
+ assert!(all.manifest_path.ends_with("all/Cargo.toml"));
+ assert_eq!(all.categories, vec!["command-line-utilities"]);
+ assert_eq!(all.keywords, vec!["cli"]);
+ assert_eq!(all.readme, Some(Utf8PathBuf::from("README.md")));
+ assert!(all.readme().unwrap().ends_with("tests/all/README.md"));
+ assert_eq!(
+ all.repository,
+ Some("https://github.com/oli-obk/cargo_metadata/".to_string())
+ );
+ assert_eq!(
+ all.homepage,
+ Some("https://github.com/oli-obk/cargo_metadata/".to_string())
+ );
+ assert_eq!(
+ all.documentation,
+ Some("https://docs.rs/cargo_metadata/".to_string())
+ );
+ assert_eq!(all.edition, Edition::E2018);
+ assert_eq!(
+ all.metadata,
+ json!({
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "default-target": "x86_64-unknown-linux-gnu",
+ "rustc-args": ["--example-rustc-arg"]
+ }
+ }
+ })
+ );
+
+ let resolve = meta.resolve.as_ref().unwrap();
+ assert!(resolve
+ .root
+ .as_ref()
+ .unwrap()
+ .to_string()
+ .starts_with("all"));
+
+ assert_eq!(resolve.nodes.len(), 9);
+ let path_dep = resolve
+ .nodes
+ .iter()
+ .find(|n| n.id.to_string().starts_with("path-dep"))
+ .unwrap();
+ assert_eq!(path_dep.deps.len(), 0);
+ assert_eq!(path_dep.dependencies.len(), 0);
+ assert_eq!(path_dep.features.len(), 0);
+
+ let bitflags = resolve
+ .nodes
+ .iter()
+ .find(|n| n.id.to_string().starts_with("bitflags"))
+ .unwrap();
+ assert_eq!(bitflags.features, vec!["default"]);
+
+ let featdep = resolve
+ .nodes
+ .iter()
+ .find(|n| n.id.to_string().starts_with("featdep"))
+ .unwrap();
+ assert_eq!(featdep.features, vec!["i128"]);
+
+ let all = resolve
+ .nodes
+ .iter()
+ .find(|n| n.id.to_string().starts_with("all"))
+ .unwrap();
+ assert_eq!(all.dependencies.len(), 8);
+ assert_eq!(all.deps.len(), 8);
+ let newname = all.deps.iter().find(|d| d.name == "newname").unwrap();
+ assert!(newname.pkg.to_string().starts_with("oldname"));
+ // Note the underscore here.
+ let path_dep = all.deps.iter().find(|d| d.name == "path_dep").unwrap();
+ assert!(path_dep.pkg.to_string().starts_with("path-dep"));
+ assert_eq!(path_dep.dep_kinds.len(), 1);
+ let kind = &path_dep.dep_kinds[0];
+ assert_eq!(kind.kind, DependencyKind::Normal);
+ assert!(kind.target.is_none());
+
+ let namedep = all
+ .deps
+ .iter()
+ .find(|d| d.name == "different_name")
+ .unwrap();
+ assert!(namedep.pkg.to_string().starts_with("namedep"));
+ assert_eq!(sorted!(all.features), vec!["bitflags", "default", "feat1"]);
+
+ let bdep = all.deps.iter().find(|d| d.name == "bdep").unwrap();
+ assert_eq!(bdep.dep_kinds.len(), 1);
+ let kind = &bdep.dep_kinds[0];
+ assert_eq!(kind.kind, DependencyKind::Build);
+ assert!(kind.target.is_none());
+
+ let devdep = all.deps.iter().find(|d| d.name == "devdep").unwrap();
+ assert_eq!(devdep.dep_kinds.len(), 1);
+ let kind = &devdep.dep_kinds[0];
+ assert_eq!(kind.kind, DependencyKind::Development);
+ assert!(kind.target.is_none());
+
+ let windep = all.deps.iter().find(|d| d.name == "windep").unwrap();
+ assert_eq!(windep.dep_kinds.len(), 1);
+ let kind = &windep.dep_kinds[0];
+ assert_eq!(kind.kind, DependencyKind::Normal);
+ assert_eq!(
+ kind.target.as_ref().map(|x| x.to_string()),
+ Some("cfg(windows)".to_string())
+ );
+}
+
+#[test]
+fn alt_registry() {
+ // This is difficult to test (would need to set up a custom index).
+ // Just manually check the JSON is handled.
+ let json = r#"
+{
+ "packages": [
+ {
+ "name": "alt",
+ "version": "0.1.0",
+ "id": "alt 0.1.0 (path+file:///alt)",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "alt2",
+ "source": "registry+https://example.com",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": "https://example.com"
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "alt",
+ "src_path": "/alt/src/lib.rs",
+ "edition": "2018"
+ }
+ ],
+ "features": {},
+ "manifest_path": "/alt/Cargo.toml",
+ "metadata": null,
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "edition": "2018",
+ "links": null
+ }
+ ],
+ "workspace_members": [
+ "alt 0.1.0 (path+file:///alt)"
+ ],
+ "resolve": null,
+ "target_directory": "/alt/target",
+ "version": 1,
+ "workspace_root": "/alt"
+}
+"#;
+ let meta: Metadata = serde_json::from_str(json).unwrap();
+ assert_eq!(meta.packages.len(), 1);
+ let alt = &meta.packages[0];
+ let deps = &alt.dependencies;
+ assert_eq!(deps.len(), 1);
+ let dep = &deps[0];
+ assert_eq!(dep.registry, Some("https://example.com".to_string()));
+}
+
+#[test]
+fn current_dir() {
+ let meta = MetadataCommand::new()
+ .current_dir("tests/all/namedep")
+ .exec()
+ .unwrap();
+ let namedep = meta.packages.iter().find(|p| p.name == "namedep").unwrap();
+ assert!(namedep.name.starts_with("namedep"));
+}
+
+#[test]
+fn parse_stream_is_robust() {
+ // Proc macros can print stuff to stdout, which naturally breaks JSON messages.
+ // Let's check that we don't die horribly in this case, and report an error.
+ let json_output = r##"{"reason":"compiler-artifact","package_id":"chatty 0.1.0 (path+file:///chatty-macro/chatty)","manifest_path":"chatty-macro/Cargo.toml","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"chatty","src_path":"/chatty-macro/chatty/src/lib.rs","edition":"2018","doctest":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/chatty-macro/target/debug/deps/libchatty-f2adcff24cdf3bb2.so"],"executable":null,"fresh":false}
+Evil proc macro was here!
+{"reason":"compiler-artifact","package_id":"chatty-macro 0.1.0 (path+file:///chatty-macro)","manifest_path":"chatty-macro/Cargo.toml","target":{"kind":["lib"],"crate_types":["lib"],"name":"chatty-macro","src_path":"/chatty-macro/src/lib.rs","edition":"2018","doctest":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/chatty-macro/target/debug/libchatty_macro.rlib","/chatty-macro/target/debug/deps/libchatty_macro-cb5956ed52a11fb6.rmeta"],"executable":null,"fresh":false}
+"##;
+ let mut n_messages = 0;
+ let mut text = String::new();
+ for message in cargo_metadata::Message::parse_stream(json_output.as_bytes()) {
+ let message = message.unwrap();
+ match message {
+ cargo_metadata::Message::TextLine(line) => text = line,
+ _ => n_messages += 1,
+ }
+ }
+ assert_eq!(n_messages, 2);
+ assert_eq!(text, "Evil proc macro was here!");
+}
+
+#[test]
+fn advanced_feature_configuration() {
+ fn build_features<F: FnOnce(&mut MetadataCommand) -> &mut MetadataCommand>(
+ func: F,
+ ) -> Vec<String> {
+ let mut meta = MetadataCommand::new();
+ let meta = meta.manifest_path("tests/all/Cargo.toml");
+
+ let meta = func(meta);
+ let meta = meta.exec().unwrap();
+
+ let resolve = meta.resolve.as_ref().unwrap();
+
+ let all = resolve
+ .nodes
+ .iter()
+ .find(|n| n.id.to_string().starts_with("all"))
+ .unwrap();
+
+ all.features.clone()
+ }
+
+ // Default behavior; tested above
+ let default_features = build_features(|meta| meta);
+ assert_eq!(
+ sorted!(default_features),
+ vec!["bitflags", "default", "feat1"]
+ );
+
+ // Manually specify the same default features
+ let manual_features = build_features(|meta| {
+ meta.features(CargoOpt::NoDefaultFeatures)
+ .features(CargoOpt::SomeFeatures(vec![
+ "feat1".into(),
+ "bitflags".into(),
+ ]))
+ });
+ assert_eq!(sorted!(manual_features), vec!["bitflags", "feat1"]);
+
+ // Multiple SomeFeatures is same as one longer SomeFeatures
+ let manual_features = build_features(|meta| {
+ meta.features(CargoOpt::NoDefaultFeatures)
+ .features(CargoOpt::SomeFeatures(vec!["feat1".into()]))
+ .features(CargoOpt::SomeFeatures(vec!["feat2".into()]))
+ });
+ assert_eq!(sorted!(manual_features), vec!["feat1", "feat2"]);
+
+ // No features + All features == All features
+ let all_features = build_features(|meta| {
+ meta.features(CargoOpt::AllFeatures)
+ .features(CargoOpt::NoDefaultFeatures)
+ });
+ assert_eq!(
+ sorted!(all_features),
+ vec!["bitflags", "default", "feat1", "feat2"]
+ );
+
+ // The '--all-features' flag supersedes other feature flags
+ let all_flag_variants = build_features(|meta| {
+ meta.features(CargoOpt::SomeFeatures(vec!["feat2".into()]))
+ .features(CargoOpt::NoDefaultFeatures)
+ .features(CargoOpt::AllFeatures)
+ });
+ assert_eq!(sorted!(all_flag_variants), sorted!(all_features));
+}
+
+#[test]
+fn depkind_to_string() {
+ assert_eq!(DependencyKind::Normal.to_string(), "normal");
+ assert_eq!(DependencyKind::Development.to_string(), "dev");
+ assert_eq!(DependencyKind::Build.to_string(), "build");
+ assert_eq!(DependencyKind::Unknown.to_string(), "Unknown");
+}
+
+#[test]
+fn basic_workspace_root_package_exists() {
+ // First try with dependencies
+ let meta = MetadataCommand::new()
+ .manifest_path("tests/basic_workspace/Cargo.toml")
+ .exec()
+ .unwrap();
+ assert_eq!(meta.root_package().unwrap().name, "ex_bin");
+ // Now with no_deps, it should still work exactly the same
+ let meta = MetadataCommand::new()
+ .manifest_path("tests/basic_workspace/Cargo.toml")
+ .no_deps()
+ .exec()
+ .unwrap();
+ assert_eq!(
+ meta.root_package()
+ .expect("workspace root still exists when no_deps used")
+ .name,
+ "ex_bin"
+ );
+}