use std::{collections::BTreeSet, path::PathBuf};
use cargo_metadata::Metadata;
use super::common::{package, test_workspace};
use crate::support::git::{
IncrementalPackageSelection, selection::select_incremental_packages_for_paths,
top_level_affected_workspace_packages,
};
#[test]
fn changed_top_level_crate_affected_set_is_only_itself() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("crates/gamma/src/lib.rs")],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: vec!["gamma".into()],
affected: vec!["gamma".into()],
}
);
}
#[test]
fn changed_crate_selects_reverse_dependencies() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("crates/alpha/src/lib.rs")],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: vec!["alpha".into()],
affected: vec!["alpha".into(), "beta".into(), "gamma".into()],
}
);
}
#[test]
fn changed_middle_crate_selects_itself_and_dependents() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("crates/beta/src/lib.rs")],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: vec!["beta".into()],
affected: vec!["beta".into(), "gamma".into()],
}
);
}
#[test]
fn top_level_frontier_covers_a_dependency_cycle_at_the_top() {
let root = tempfile::tempdir().unwrap();
let ru = root.path().display().to_string();
let a = format!("a 0.1.0 (path+file://{ru}/crates/a)");
let b = format!("b 0.1.0 (path+file://{ru}/crates/b)");
let leaf = format!("leaf 0.1.0 (path+file://{ru}/crates/leaf)");
let dep = |name: &str, pkg: &str| {
serde_json::json!({
"name": name,
"pkg": pkg,
"dep_kinds": [{ "kind": null, "target": null }]
})
};
let value = serde_json::json!({
"packages": [
package(root.path(), "a", &["b", "leaf"]),
package(root.path(), "b", &["a", "leaf"]),
package(root.path(), "leaf", &[]),
],
"workspace_members": [a, b, leaf],
"workspace_default_members": [a, b, leaf],
"resolve": {
"nodes": [
{ "id": a, "dependencies": [b, leaf], "deps": [dep("b", &b), dep("leaf", &leaf)], "features": [] },
{ "id": b, "dependencies": [a, leaf], "deps": [dep("a", &a), dep("leaf", &leaf)], "features": [] },
{ "id": leaf, "dependencies": [], "deps": [], "features": [] },
],
"root": null
},
"target_directory": root.path().join("target"),
"version": 1,
"workspace_root": root.path(),
"metadata": null,
});
let metadata: Metadata = serde_json::from_value(value).unwrap();
let packages = metadata.packages.clone();
let affected = BTreeSet::from(["a".to_string(), "b".to_string(), "leaf".to_string()]);
let frontier = top_level_affected_workspace_packages(&metadata, &packages, &affected);
assert_eq!(frontier, vec!["a".to_string()]);
}
#[test]
fn no_changes_selects_no_packages() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
Vec::<PathBuf>::new(),
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: Vec::new(),
affected: Vec::new(),
}
);
}
#[test]
fn lockfile_only_change_falls_back_to_full() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("Cargo.lock")],
)
.unwrap();
assert!(matches!(
selected,
IncrementalPackageSelection::Full { reason } if reason.contains("Cargo.lock")
));
}
#[test]
fn lockfile_change_keeps_incremental_selection_when_packages_changed() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[
PathBuf::from("Cargo.lock"),
PathBuf::from("crates/beta/Cargo.toml"),
],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: vec!["beta".into()],
affected: vec!["beta".into(), "gamma".into()],
}
);
}
#[test]
fn root_cargo_toml_only_falls_back_to_full() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("Cargo.toml")],
)
.unwrap();
assert!(matches!(
selected,
IncrementalPackageSelection::Full { reason } if reason.contains("Cargo.toml")
));
}
#[test]
fn root_cargo_toml_with_package_change_still_falls_back_to_full() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[
PathBuf::from("Cargo.toml"),
PathBuf::from("crates/alpha/src/lib.rs"),
],
)
.unwrap();
assert!(matches!(
selected,
IncrementalPackageSelection::Full { reason } if reason.contains("Cargo.toml")
));
}
#[test]
fn global_config_file_falls_back_to_full_run() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from(".cargo/config.toml")],
)
.unwrap();
assert!(matches!(
selected,
IncrementalPackageSelection::Full { reason } if reason.contains(".cargo")
));
}
#[test]
fn unrelated_outside_package_file_selects_no_packages() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[PathBuf::from("docs/guide.md")],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: Vec::new(),
affected: Vec::new(),
}
);
}
#[test]
fn unrelated_outside_package_file_does_not_hide_package_changes() {
let (root, metadata, workspace_packages) = test_workspace();
let selected = select_incremental_packages_for_paths(
root.path(),
&metadata,
&workspace_packages,
[
PathBuf::from(".github/workflows/review.yml"),
PathBuf::from("crates/beta/src/lib.rs"),
],
)
.unwrap();
assert_eq!(
selected,
IncrementalPackageSelection::Packages {
changed: vec!["beta".into()],
affected: vec!["beta".into(), "gamma".into()],
}
);
}