internal: add simple smoke test for project model

Our project model code is rather complicated -- the logic for lowering
from `cargo metadata` to `CrateGraph` is fiddly and special-case. So
far, we survived without testing this at all, but this increasingly
seems like a poor option.

So this PR introduces a simple tests just to detect the most obvious
failures. The idea here is that, although we rely on external processes
(cargo & rustc), we are actually using their stable interfaces, so we
might just mock out the outputs.

Long term, I would like to try to virtualize IO here, so as to do such
mocking in a more principled way, but lets start simple.

Should we forgo the mocking and just call `cargo metadata` directly
perhaps? Touch question -- I personally feel that fast, in-process tests
are more important in this case than any extra assurance we get from
running the real thing.

Super-long term, we would probably want to extend our heavy tests to
cover more use-cases, but we should figure a way to do that without
slowing the tests down for everyone.

Perhaps we need two-tiered bors system, where we pull from `master` into
`release` branch only when an additional set of tests passes?
This commit is contained in:
Aleksey Kladov 2021-07-20 15:38:20 +03:00
parent 2211d2cece
commit b0c4b776b5
6 changed files with 782 additions and 1 deletions

1
Cargo.lock generated
View file

@ -1175,6 +1175,7 @@ dependencies = [
"base_db",
"cargo_metadata",
"cfg",
"expect-test",
"la-arena",
"log",
"paths",

View file

@ -23,11 +23,26 @@ pub use dnf::DnfExpr;
/// of key and value in `key_values`.
///
/// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options>
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Clone, PartialEq, Eq, Default)]
pub struct CfgOptions {
enabled: FxHashSet<CfgAtom>,
}
impl fmt::Debug for CfgOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut items = self
.enabled
.iter()
.map(|atom| match atom {
CfgAtom::Flag(it) => it.to_string(),
CfgAtom::KeyValue { key, value } => format!("{}={}", key, value),
})
.collect::<Vec<_>>();
items.sort();
f.debug_tuple("CfgOptions").field(&items).finish()
}
}
impl CfgOptions {
pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
cfg.fold(&|atom| self.enabled.contains(atom))

View file

@ -16,6 +16,7 @@ semver = "1"
serde = { version = "1.0.106", features = ["derive"] }
serde_json = "1.0.48"
anyhow = "1.0.26"
expect-test = "1"
la-arena = { version = "0.2.0", path = "../../lib/arena" }
cfg = { path = "../cfg", version = "0.0.0" }

View file

@ -24,6 +24,9 @@ mod workspace;
mod rustc_cfg;
mod build_scripts;
#[cfg(test)]
mod tests;
use std::{
convert::{TryFrom, TryInto},
fs::{self, read_dir, ReadDir},

View file

@ -0,0 +1,516 @@
use std::path::PathBuf;
use base_db::FileId;
use expect_test::{expect, Expect};
use crate::{CargoWorkspace, CfgOverrides, ProjectWorkspace, Sysroot, WorkspaceBuildScripts};
fn check(file: &str, expect: Expect) {
let meta = get_test_metadata(file);
let cargo_workspace = CargoWorkspace::new(meta);
let project_workspace = ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
sysroot: Sysroot::default(),
rustc: None,
rustc_cfg: Vec::new(),
cfg_overrides: CfgOverrides::default(),
};
let crate_graph = project_workspace.to_crate_graph(None, {
let mut counter = 0;
&mut move |_path| {
counter += 1;
Some(FileId(counter))
}
});
let mut crate_graph = format!("{:#?}", crate_graph);
replace_root(&mut crate_graph, false);
expect.assert_eq(&crate_graph);
}
fn get_test_metadata(file: &str) -> cargo_metadata::Metadata {
let mut json = get_test_data(file).parse::<serde_json::Value>().unwrap();
fixup_paths(&mut json);
return serde_json::from_value(json).unwrap();
fn fixup_paths(val: &mut serde_json::Value) -> () {
match val {
serde_json::Value::String(s) => replace_root(s, true),
serde_json::Value::Array(vals) => vals.iter_mut().for_each(fixup_paths),
serde_json::Value::Object(kvals) => kvals.values_mut().for_each(fixup_paths),
serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => {
()
}
}
}
}
fn replace_root(s: &mut String, direction: bool) {
if direction {
let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" };
*s = s.replace("$ROOT$", root)
} else {
let root = if cfg!(windows) { r#"C:\\\\ROOT\\"# } else { "/ROOT/" };
*s = s.replace(root, "$ROOT$")
}
}
fn get_test_data(file: &str) -> String {
let base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let file = base.join("test_data").join(file);
std::fs::read_to_string(file).unwrap()
}
#[test]
fn hello_world_project_model() {
check(
"hello-world-metadata.json",
expect![[r#"
CrateGraph {
arena: {
CrateId(
0,
): CrateData {
root_file_id: FileId(
1,
),
edition: Edition2018,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"hello_world",
),
canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "hello-world",
"CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [
Dependency {
crate_id: CrateId(
4,
),
name: CrateName(
"libc",
),
},
],
proc_macro: [],
},
CrateId(
5,
): CrateData {
root_file_id: FileId(
6,
),
edition: Edition2015,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"const_fn",
),
canonical_name: "const_fn",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"feature=default",
"feature=std",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"feature=align",
"feature=const-extern-fn",
"feature=default",
"feature=extra_traits",
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
"CARGO_PKG_VERSION": "0.2.98",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "libc",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "libc",
"CARGO_PKG_VERSION_PATCH": "98",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "2",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [
Dependency {
crate_id: CrateId(
4,
),
name: CrateName(
"libc",
),
},
],
proc_macro: [],
},
CrateId(
2,
): CrateData {
root_file_id: FileId(
3,
),
edition: Edition2018,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"an_example",
),
canonical_name: "an-example",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "hello-world",
"CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [
Dependency {
crate_id: CrateId(
0,
),
name: CrateName(
"hello_world",
),
},
Dependency {
crate_id: CrateId(
4,
),
name: CrateName(
"libc",
),
},
],
proc_macro: [],
},
CrateId(
4,
): CrateData {
root_file_id: FileId(
5,
),
edition: Edition2015,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"libc",
),
canonical_name: "libc",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"feature=default",
"feature=std",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"feature=align",
"feature=const-extern-fn",
"feature=default",
"feature=extra_traits",
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
"CARGO_PKG_VERSION": "0.2.98",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "libc",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "libc",
"CARGO_PKG_VERSION_PATCH": "98",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "2",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [],
proc_macro: [],
},
CrateId(
1,
): CrateData {
root_file_id: FileId(
2,
),
edition: Edition2018,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"hello_world",
),
canonical_name: "hello-world",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "hello-world",
"CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [
Dependency {
crate_id: CrateId(
0,
),
name: CrateName(
"hello_world",
),
},
Dependency {
crate_id: CrateId(
4,
),
name: CrateName(
"libc",
),
},
],
proc_macro: [],
},
CrateId(
6,
): CrateData {
root_file_id: FileId(
7,
),
edition: Edition2015,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"build_script_build",
),
canonical_name: "build-script-build",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"feature=default",
"feature=std",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"feature=align",
"feature=const-extern-fn",
"feature=default",
"feature=extra_traits",
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
"CARGO_PKG_VERSION": "0.2.98",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "libc",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "libc",
"CARGO_PKG_VERSION_PATCH": "98",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "2",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [],
proc_macro: [],
},
CrateId(
3,
): CrateData {
root_file_id: FileId(
4,
),
edition: Edition2018,
display_name: Some(
CrateDisplayName {
crate_name: CrateName(
"it",
),
canonical_name: "it",
},
),
cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
potential_cfg_options: CfgOptions(
[
"debug_assertions",
"test",
],
),
env: Env {
entries: {
"CARGO_PKG_LICENSE": "",
"CARGO_PKG_VERSION_MAJOR": "0",
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
"CARGO_CRATE_NAME": "hello_world",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
"CARGO_PKG_NAME": "hello-world",
"CARGO_PKG_VERSION_PATCH": "0",
"CARGO": "cargo",
"CARGO_PKG_REPOSITORY": "",
"CARGO_PKG_VERSION_MINOR": "1",
"CARGO_PKG_VERSION_PRE": "",
},
},
dependencies: [
Dependency {
crate_id: CrateId(
0,
),
name: CrateName(
"hello_world",
),
},
Dependency {
crate_id: CrateId(
4,
),
name: CrateName(
"libc",
),
},
],
proc_macro: [],
},
},
}"#]],
)
}

View file

@ -0,0 +1,245 @@
{
"packages": [
{
"name": "hello-world",
"version": "0.1.0",
"id": "hello-world 0.1.0 (path+file://$ROOT$hello-world)",
"license": null,
"license_file": null,
"description": null,
"source": null,
"dependencies": [
{
"name": "libc",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^0.2",
"kind": null,
"rename": null,
"optional": false,
"uses_default_features": true,
"features": [],
"target": null,
"registry": null
}
],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "hello-world",
"src_path": "$ROOT$hello-world/src/lib.rs",
"edition": "2018",
"doc": true,
"doctest": true,
"test": true
},
{
"kind": [
"bin"
],
"crate_types": [
"bin"
],
"name": "hello-world",
"src_path": "$ROOT$hello-world/src/main.rs",
"edition": "2018",
"doc": true,
"doctest": false,
"test": true
},
{
"kind": [
"example"
],
"crate_types": [
"bin"
],
"name": "an-example",
"src_path": "$ROOT$hello-world/examples/an-example.rs",
"edition": "2018",
"doc": false,
"doctest": false,
"test": false
},
{
"kind": [
"test"
],
"crate_types": [
"bin"
],
"name": "it",
"src_path": "$ROOT$hello-world/tests/it.rs",
"edition": "2018",
"doc": false,
"doctest": false,
"test": true
}
],
"features": {},
"manifest_path": "$ROOT$hello-world/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [],
"categories": [],
"keywords": [],
"readme": null,
"repository": null,
"homepage": null,
"documentation": null,
"edition": "2018",
"links": null
},
{
"name": "libc",
"version": "0.2.98",
"id": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)",
"license": "MIT OR Apache-2.0",
"license_file": null,
"description": "Raw FFI bindings to platform libraries like libc.\n",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"dependencies": [
{
"name": "rustc-std-workspace-core",
"source": "registry+https://github.com/rust-lang/crates.io-index",
"req": "^1.0.0",
"kind": null,
"rename": null,
"optional": true,
"uses_default_features": true,
"features": [],
"target": null,
"registry": null
}
],
"targets": [
{
"kind": [
"lib"
],
"crate_types": [
"lib"
],
"name": "libc",
"src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/src/lib.rs",
"edition": "2015",
"doc": true,
"doctest": true,
"test": true
},
{
"kind": [
"test"
],
"crate_types": [
"bin"
],
"name": "const_fn",
"src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/tests/const_fn.rs",
"edition": "2015",
"doc": false,
"doctest": false,
"test": true
},
{
"kind": [
"custom-build"
],
"crate_types": [
"bin"
],
"name": "build-script-build",
"src_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/build.rs",
"edition": "2015",
"doc": false,
"doctest": false,
"test": false
}
],
"features": {
"align": [],
"const-extern-fn": [],
"default": [
"std"
],
"extra_traits": [],
"rustc-dep-of-std": [
"align",
"rustc-std-workspace-core"
],
"std": [],
"use_std": [
"std"
]
},
"manifest_path": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98/Cargo.toml",
"metadata": null,
"publish": null,
"authors": [
"The Rust Project Developers"
],
"categories": [
"external-ffi-bindings",
"no-std",
"os"
],
"keywords": [
"libc",
"ffi",
"bindings",
"operating",
"system"
],
"readme": "README.md",
"repository": "https://github.com/rust-lang/libc",
"homepage": "https://github.com/rust-lang/libc",
"documentation": "https://docs.rs/libc/",
"edition": "2015",
"links": null
}
],
"workspace_members": [
"hello-world 0.1.0 (path+file://$ROOT$hello-world)"
],
"resolve": {
"nodes": [
{
"id": "hello-world 0.1.0 (path+file://$ROOT$hello-world)",
"dependencies": [
"libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)"
],
"deps": [
{
"name": "libc",
"pkg": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)",
"dep_kinds": [
{
"kind": null,
"target": null
}
]
}
],
"features": []
},
{
"id": "libc 0.2.98 (registry+https://github.com/rust-lang/crates.io-index)",
"dependencies": [],
"deps": [],
"features": [
"default",
"std"
]
}
],
"root": "hello-world 0.1.0 (path+file://$ROOT$hello-world)"
},
"target_directory": "$ROOT$hello-world/target",
"version": 1,
"workspace_root": "$ROOT$hello-world",
"metadata": null
}