blob: 51eefb249e423a651f3d203ddd8c820c7103e91e [file] [log] [blame]
extern crate cargo_metadata;
extern crate semver;
#[macro_use]
extern crate serde_json;
use cargo_metadata::{DependencyKind, Metadata, MetadataCommand};
use std::path::PathBuf;
#[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.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_eq!(dep.optional, false);
assert_eq!(dep.uses_default_features, true);
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, PathBuf::from("/foo/src/main.rs"));
assert_eq!(target.edition, "2015");
assert_eq!(target.doctest, true);
assert_eq!(pkg.features.len(), 0);
assert_eq!(pkg.manifest_path, PathBuf::from("/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.edition, "2015");
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, PathBuf::from("/foo"));
assert_eq!(meta.target_directory, PathBuf::from("/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 = Vec::new();
ver.build = Vec::new();
ver
}
#[test]
fn all_the_fields() {
// All the fields currently generated as of 1.41. This tries to exercise as
// much as possible.
let ver = cargo_version();
let minimum = semver::Version::parse("1.41.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
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(),
PathBuf::from("all")
);
assert_eq!(
meta.target_directory.file_name().unwrap(),
PathBuf::from("target")
);
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(PathBuf::from("LICENSE")));
assert_eq!(all.publish, Some(vec![]));
assert_eq!(all.links, Some("foo".to_string()));
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_eq!(bitflags.optional, true);
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());
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_eq!(featdep.uses_default_features, false);
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", "dylib", "rlib", "staticlib"]
);
assert_eq!(
sorted!(lib.crate_types),
vec!["cdylib", "dylib", "rlib", "staticlib"]
);
assert_eq!(lib.required_features.len(), 0);
assert_eq!(lib.edition, "2018");
assert_eq!(lib.doctest, true);
let main = get_file_name!("main.rs");
assert_eq!(main.crate_types, vec!["bin"]);
assert_eq!(main.kind, vec!["bin"]);
assert_eq!(main.doctest, false);
let otherbin = get_file_name!("otherbin.rs");
assert_eq!(otherbin.edition, "2015");
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"]);
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"]);
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(PathBuf::from("README.md")));
assert_eq!(
all.repository,
Some("https://github.com/oli-obk/cargo_metadata/".to_string())
);
assert_eq!(all.edition, "2018");
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"));
}