use super::*;
#[test]
pub(crate) fn project_model_indexes_manifest_modules_items_and_calls_deterministically() {
let _guard = analysis_lock();
let dir = tempdir().expect("tempdir");
fs::create_dir_all(dir.path().join("src")).expect("src dir");
fs::write(dir.path().join("README.md"), "# Fixture\n").expect("readme write");
fs::write(
dir.path().join("Cargo.toml"),
r#"[package]
name = "project-model-fixture"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1"
regex = { version = "1", default-features = false }
[dev-dependencies]
tempfile = "3"
"#,
)
.expect("manifest write");
fs::write(
dir.path().join("Cargo.lock"),
r#"# This file is automatically @generated by Cargo.
version = 3
[[package]]
name = "project-model-fixture"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
"#,
)
.expect("lockfile write");
fs::write(
dir.path().join("src/lib.rs"),
r#"pub mod api {
pub struct Public;
fn helper() {}
pub fn call_helper() {
helper();
}
impl Public {
pub fn new() -> Self {
Public
}
}
}
mod external;
"#,
)
.expect("lib write");
fs::write(
dir.path().join("src/external.rs"),
"pub fn visible_external() {}\n",
)
.expect("external write");
let first = project_context_for_test(dir.path());
let second = project_context_for_test(dir.path());
assert_eq!(first.manifest, second.manifest);
assert_eq!(first.lockfile, second.lockfile);
assert_eq!(first.modules, second.modules);
assert_eq!(first.items, second.items);
assert_eq!(first.call_names, second.call_names);
assert!(first.diagnostics.is_empty(), "{:?}", first.diagnostics);
let manifest = first.manifest.as_ref().expect("manifest summary");
assert_eq!(manifest.file_path, "Cargo.toml");
assert_eq!(
manifest.package_name.as_deref(),
Some("project-model-fixture")
);
assert!(manifest.dependencies.iter().any(|dependency| {
dependency.section == "dependencies"
&& dependency.name == "serde"
&& dependency.requirement.as_deref() == Some("1")
}));
assert!(manifest.dependencies.iter().any(|dependency| {
dependency.section == "dev-dependencies"
&& dependency.name == "tempfile"
&& dependency.requirement.as_deref() == Some("3")
}));
let lockfile = first.lockfile.as_ref().expect("lockfile summary");
assert_eq!(lockfile.file_path, "Cargo.lock");
assert!(lockfile
.packages
.iter()
.any(|package| package.name == "serde" && package.version == "1.0.0"));
assert!(first.modules.iter().any(|module| {
module.file_path == "src/lib.rs"
&& module.module_path == "api"
&& module.public
&& module.inline
}));
assert!(first.modules.iter().any(|module| {
module.file_path == "src/lib.rs"
&& module.module_path == "external"
&& !module.public
&& !module.inline
}));
assert!(first.items.iter().any(|item| {
item.file_path == "src/lib.rs"
&& item.module_path == "api"
&& item.name == "Public"
&& item.kind == "struct"
&& item.public
}));
assert!(first.items.iter().any(|item| {
item.file_path == "src/lib.rs"
&& item.module_path == "api"
&& item.name == "helper"
&& item.kind == "function"
&& !item.public
}));
assert!(first
.call_names
.iter()
.any(|call| { call.file_path == "src/lib.rs" && call.name == "helper" && call.line == 7 }));
}
#[test]
pub(crate) fn project_model_handles_missing_and_invalid_cargo_metadata() {
let _guard = analysis_lock();
let missing_dir = tempdir().expect("tempdir");
fs::create_dir_all(missing_dir.path().join("src")).expect("src dir");
fs::write(missing_dir.path().join("README.md"), "# Fixture\n").expect("readme write");
fs::write(missing_dir.path().join("src/lib.rs"), "pub fn ready() {}\n").expect("lib write");
let missing = project_context_for_test(missing_dir.path());
assert!(missing.manifest.is_none());
assert!(missing.lockfile.is_none());
assert!(missing.diagnostics.is_empty(), "{:?}", missing.diagnostics);
let invalid_manifest_dir = tempdir().expect("tempdir");
fs::write(invalid_manifest_dir.path().join("README.md"), "# Fixture\n").expect("readme write");
fs::write(invalid_manifest_dir.path().join("Cargo.toml"), "[package\n")
.expect("manifest write");
let invalid_manifest = project_context_for_test(invalid_manifest_dir.path());
assert!(invalid_manifest.manifest.is_none());
assert_eq!(invalid_manifest.diagnostics.len(), 1);
assert_eq!(
invalid_manifest.diagnostics[0].diagnostic_type,
"manifest-parse-error"
);
assert_eq!(
invalid_manifest.diagnostics[0].file_path.as_deref(),
Some("Cargo.toml")
);
assert!(!invalid_manifest.diagnostics[0]
.message
.to_ascii_lowercase()
.contains("parser"));
let invalid_lock_dir = tempdir().expect("tempdir");
fs::write(invalid_lock_dir.path().join("README.md"), "# Fixture\n").expect("readme write");
fs::write(
invalid_lock_dir.path().join("Cargo.toml"),
r#"[package]
name = "invalid-lock-fixture"
version = "0.1.0"
"#,
)
.expect("manifest write");
fs::write(invalid_lock_dir.path().join("Cargo.lock"), "[package\n").expect("lockfile write");
let invalid_lock = project_context_for_test(invalid_lock_dir.path());
assert!(invalid_lock.manifest.is_some());
assert_eq!(invalid_lock.diagnostics.len(), 1);
assert_eq!(
invalid_lock.diagnostics[0].diagnostic_type,
"lockfile-parse-error"
);
assert_eq!(
invalid_lock.diagnostics[0].file_path.as_deref(),
Some("Cargo.lock")
);
}
#[test]
pub(crate) fn missing_readme_is_project_scoped() {
let _guard = analysis_lock();
let dir = tempdir().expect("tempdir");
fs::write(dir.path().join("lib.rs"), "pub fn documented() {}\n").expect("fixture write");
let missing = analyse_project_paths(dir.path(), vec![PathBuf::from("lib.rs")]);
fs::write(dir.path().join("README.md"), "# Temporary fixture\n").expect("readme write");
let present = analyse_project_paths(dir.path(), vec![PathBuf::from("lib.rs")]);
assert_has_rule(&missing, "docs.missing-readme");
assert_missing_rule(&present, "docs.missing-readme");
}