use std::collections::BTreeMap;
use std::path::PathBuf;
use drove::dsl::compile;
use drove::ir::Ir;
use drove::model::Tab;
fn example_path(relative: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join(relative)
}
fn fixture_path(relative: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(relative)
}
fn pane_content_digests(drovefile: &std::path::Path) -> BTreeMap<String, String> {
let compiled = compile(drovefile).expect("compile");
let ir = compiled
.config
.profile("default")
.expect("default profile")
.to_ir();
ir.resources
.iter()
.filter(|resource| resource.kind == "pane")
.map(|resource| (resource.name.clone(), resource.digest.clone()))
.collect()
}
const BASIC_V2_PANE_DIGESTS: &[(&str, &str)] = &[
(
"editor",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"728c981e3aad62fd5b893e8a2ab14c0d7dde019d6dcef6d0d71248d5a3225ceb\"}",
),
(
"tests",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"728c981e3aad62fd5b893e8a2ab14c0d7dde019d6dcef6d0d71248d5a3225ceb\"}",
),
];
const LOG_DRIVEN_V2_PANE_DIGESTS: &[(&str, &str)] = &[
(
"controller",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"b306b3084d85d40c8bcc555474e499eebe504f74fcbad8dfed59b9400ba374e4\",\"serve\":\"728c981e3aad62fd5b893e8a2ab14c0d7dde019d6dcef6d0d71248d5a3225ceb\"}",
),
(
"eventlog",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"751666fb7db0bebbc2ec905bf493260c99ab3742562075b1a5507c06e79d1378\"}",
),
(
"agentmon",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"3c81c9927eae8c04a29cc1ae76f52f508b97ef2369be4e7db9cc6b7dcfa93272\"}",
),
(
"spiceedit",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"3a5e63d8178dbc33b2d979bee8fa8ddae86993ab81eea796758c91f75ec5256c\"}",
),
(
"commit-reactor",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"ed157801ab1bd3ac9b981a99e8dcd02ac45c7d21724d95c18b1fdd1c9023cc27\",\"other\":\"631493e03b384122dce9880bbd98ff13009ec3a82930fa9d2141844c5250d9e2\",\"serve\":\"7a305d7cbbe31204a9ad54a1600cdcec53e59ae635cc053e7f0bc7b2596a0205\"}",
),
(
"gitlog",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"0a3aba4b6279d8dd2f5186753b99d6f38925ba859d64cf683f7f435a312cbbc0\",\"other\":\"2d008dd52867a5dd6c3c6cf09c7fa56b722e448094db894417f2cd3a5ce689c2\",\"serve\":\"3849dacfee5ac53976eb31d0d65015c1171fae907d9a344b7c97e787129077e5\"}",
),
(
"doc-sync-reactor",
"{\"cwd\":\"59911c573c6d7f60fac16b5a657027e3355ba9758f0e74f12bff2ea7e5601b99\",\"env\":\"460e3a1e3343b944d6c45008cf5c70047a11ce4396ff1f399d426ba392fcf442\",\"label\":\"f86921faad5a509c7866edc54ace4be6e5e54d9f36d0af208204a81dc4ddf962\",\"on_start\":\"7cec09ef17a109f71b9955dce4b8747dfd5fc19ab8f3b26b03a69c1ad43b1258\",\"other\":\"4a0b8a4fa07e674dfff30da3fe3dd2b7a3005133e6336a84ddc4a994f6ed61de\",\"serve\":\"42a5a79093e145dbf08596abc41cf6adc431dea488213af3d3caa57201b5bcda\"}",
),
];
fn pane_digest(ir: &Ir, name: &str) -> String {
ir.resources
.iter()
.find(|resource| resource.kind == "pane" && resource.name == name)
.unwrap_or_else(|| panic!("pane resource `{name}`"))
.digest
.clone()
}
fn example_ir(relative: &str) -> Ir {
let compiled = compile(&example_path(relative)).expect("compile example");
compiled
.config
.profile("default")
.expect("default profile")
.to_ir()
}
#[test]
fn basic_example_compiles() {
let compiled = compile(&example_path("basic/Drovefile")).expect("compile basic example");
let profile = compiled.config.profile("default").expect("default profile");
assert_eq!(profile.workspaces.len(), 1);
}
#[test]
fn log_driven_example_compiles_with_reactors_and_extends() {
let compiled =
compile(&example_path("log-driven/Drovefile")).expect("compile log-driven example");
let default_profile = compiled.config.profile("default").expect("default profile");
assert_eq!(default_profile.workspaces.len(), 3);
assert_eq!(default_profile.tasks.len(), 2);
let maintenance = default_profile
.workspaces
.iter()
.find(|workspace| workspace.name == "maintenance")
.expect("maintenance workspace");
assert_eq!(maintenance.tabs.len(), 3);
let commit_reactor_pane = maintenance
.tabs
.iter()
.flat_map(|tab| &tab.panes)
.find(|pane| pane.name == "commit-reactor")
.expect("commit-reactor pane");
assert!(commit_reactor_pane.on_start.is_some());
let core_profile = compiled.config.profile("core").expect("core profile");
assert_eq!(core_profile.workspaces.len(), 2);
assert!(
core_profile
.workspaces
.iter()
.all(|workspace| workspace.name != "files"),
"core profile should drop the `files` workspace via `without`"
);
let ir = default_profile.to_ir();
assert!(
ir.resources
.iter()
.all(|resource| !resource.digest.is_empty())
);
}
#[test]
fn example_pane_content_digests_equal_the_v2_values() {
let basic = example_ir("basic/Drovefile");
for (name, expected) in BASIC_V2_PANE_DIGESTS {
assert_eq!(
&pane_digest(&basic, name),
expected,
"basic pane `{name}` content digest drifted from its v2 value"
);
}
let log_driven = example_ir("log-driven/Drovefile");
for (name, expected) in LOG_DRIVEN_V2_PANE_DIGESTS {
assert_eq!(
&pane_digest(&log_driven, name),
expected,
"log-driven pane `{name}` content digest drifted from its v2 value"
);
}
}
#[test]
fn migrated_examples_are_in_v3_form_with_no_warnings() {
for example in ["basic/Drovefile", "log-driven/Drovefile"] {
let compiled = compile(&example_path(example)).expect("compile example");
assert!(
compiled.warnings.is_empty(),
"{example} still uses a v2 form: {:?}",
compiled.warnings
);
}
}
#[test]
fn v2_fixtures_still_compile_but_warn() {
for fixture in ["v2/basic/Drovefile", "v2/log-driven/Drovefile"] {
let compiled = compile(&fixture_path(fixture)).expect("compile v2 fixture");
assert!(
!compiled.warnings.is_empty(),
"{fixture} is a v2 form but raised no warning"
);
}
}
#[test]
fn v2_and_v3_forms_share_every_pane_content_digest() {
for (v2, v3) in [
("v2/basic/Drovefile", "basic/Drovefile"),
("v2/log-driven/Drovefile", "log-driven/Drovefile"),
] {
let v2_digests = pane_content_digests(&fixture_path(v2));
let v3_digests = pane_content_digests(&example_path(v3));
assert_eq!(
v2_digests, v3_digests,
"pane content digests drifted between the v2 form `{v2}` and the v3 form `{v3}`"
);
}
}
#[test]
fn moving_a_pane_between_tabs_keeps_content_but_changes_topology() {
let compiled = compile(&example_path("basic/Drovefile")).expect("compile basic example");
let mut profile = compiled
.config
.profile("default")
.expect("default profile")
.clone();
let before = profile.to_ir();
let before_editor = pane_digest(&before, "editor");
let before_tests = pane_digest(&before, "tests");
let before_main = before
.placements
.iter()
.find(|group| group.name == "main")
.expect("main group")
.topology_digest
.clone();
let workspace = &mut profile.workspaces[0];
workspace.tabs.push(Tab {
name: "side".into(),
label: None,
split: Default::default(),
ratios: vec![],
panes: vec![],
});
let moved = workspace.tabs[0].panes.remove(1);
workspace.tabs[1].panes.push(moved);
let after = profile.to_ir();
assert_eq!(
before_editor,
pane_digest(&after, "editor"),
"the pane left in place must keep its content digest"
);
assert_eq!(
before_tests,
pane_digest(&after, "tests"),
"a moved pane must keep its content digest"
);
let after_main = after
.placements
.iter()
.find(|group| group.name == "main")
.expect("main group")
.topology_digest
.clone();
assert_ne!(
before_main, after_main,
"the source group's topology digest must change when a pane leaves it"
);
let after_side = after
.placements
.iter()
.find(|group| group.name == "side")
.expect("side group");
assert_eq!(
after_side.panes,
["tests"],
"the moved pane lands in `side`"
);
}