dwarves-gui/tests/robot_build_scene_features.rs
//! **ROBOT-UI: a cargo feature picked in the Build Thing tab reaches the build.**
//!
//! Owner report 2026-10-16 (loki, dwarves 0aa9c50): an open cargo feature's node
//! said "öppen cargo-feature: `dwarves buildthing` har ingen flagga för den, och ⚒
//! skickar den inte till bygget — klicket n…" (clipped). Through the REAL widgets:
//! tick the feature, press BYGG, and a real cargo build of a crate whose
//! non-default feature changes its output runs WITH it — the outcome says which
//! features built it (the build's own record), and no such notice is on screen.
use facett_core::Facet;
use nornir_robotui::egui_kittest::Harness;
use nornir_robotui::{Observable, RobotSession};
use dwarves_gui::build_forge::DeclaredForge;
use dwarves_gui::build_scene::BuildScene;
struct SceneApp(BuildScene);
impl Observable for SceneApp {
fn state_json(&self) -> serde_json::Value {
Facet::state_json(&self.0)
}
}
fn staged() -> tempfile::TempDir {
let dir = tempfile::tempdir().unwrap();
let root = dir.path().join("featured");
std::fs::create_dir_all(root.join("src")).unwrap();
std::fs::create_dir_all(root.join(".nornir")).unwrap();
std::fs::write(
root.join("Cargo.toml"),
"[package]\nname = \"featured\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[[bin]]\nname = \"featured\"\npath = \"src/main.rs\"\n\n[features]\ndefault = []\nextra = []\n\n[workspace]\n",
)
.unwrap();
std::fs::write(root.join("Cargo.lock"), "version = 4\n\n[[package]]\nname = \"featured\"\nversion = \"0.1.0\"\n").unwrap();
std::fs::write(
root.join("src/main.rs"),
"fn main() {\n #[cfg(feature = \"extra\")]\n println!(\"extra\");\n}\n",
)
.unwrap();
std::fs::write(
root.join(".nornir/build-variants.toml"),
"[build]\nbase_suite = \"ORIGINAL\"\n\n[[build.variant]]\nname = \"featured\"\nrole = \"server\"\ndefault = true\nsuites = [\"ORIGINAL\"]\npackage = \"featured\"\nbin = \"featured\"\nkind = \"exe\"\nopen = [\"extra\"]\n",
)
.unwrap();
dir
}
#[test]
fn ticking_a_cargo_feature_and_pressing_bygg_builds_with_it() {
let dir = staged();
let scene = BuildScene::new(Box::new(DeclaredForge::at(dir.path().to_path_buf())));
let harness = Harness::builder()
.with_size(nornir_robotui::egui::Vec2::new(1400.0, 1200.0))
.build_ui_state(|ui, app: &mut SceneApp| Facet::ui(&mut app.0, ui), SceneApp(scene));
let mut robot = RobotSession::new(harness, "dwarves-gui");
robot.run(2);
robot.type_into("artefakt", "featured").expect("the artefakt field");
robot.click_label("sök").expect("the sök button");
robot.run(2);
robot.click_label("featured").expect("the row");
robot.run(2);
robot.click_label("exe").expect("the exe column");
robot.run(3);
let state = robot.state();
assert!(
!state.to_string().contains("ingen flagga"),
"the 'no flag' notice is gone — the feature has one now: {state}"
);
robot.click_label("extra").expect("the open feature's checkbox");
robot.run(3);
assert!(
robot.state().to_string().contains("--features extra"),
"ticked, the node shows the flag the build gets"
);
robot.click_label("BYGG").expect("BYGG is armed");
// a real cargo build: pump frames with wall clock until it is terminal
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(300);
let outcome = loop {
robot.run(1);
if let Some(o) = robot.state_at("/outcome").filter(|o| !o.is_null()) {
break o;
}
assert!(std::time::Instant::now() < deadline, "no outcome: {}", robot.state());
std::thread::sleep(std::time::Duration::from_millis(200));
};
assert_eq!(outcome["ok"], true, "{outcome}");
let features = outcome["features"].as_str().unwrap_or_default();
assert!(features.contains("extra"), "the outcome says the build used the feature: {outcome}");
let artifact = outcome["artifact"].as_str().unwrap_or_default();
let printed = std::process::Command::new(artifact).output().map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).unwrap_or_default();
assert_eq!(printed, "extra", "the artifact was built WITH the feature ({artifact})");
}