use concinnity_core::ecs::asset_id::AssetId;
use concinnity_core::ecs::{FrameTime, World};
use concinnity_host::thread::asset_id;
use crate::editor::behavior::graph::CardKind;
use crate::editor::behavior::panel::BehaviorAction;
use crate::editor::behavior::path;
use crate::editor::hook::tests::fixtures::{behavior, hook, playing_hook};
use crate::editor::panels::registry::PanelKey;
use crate::editor::sim;
fn traced_world(id: AssetId, hit: bool) -> World {
use concinnity_core::ecs::{ExecutionTrace, TraceEvent, TracePaths, TraceStep, TraceVal};
let mut world = World::new();
let event = TraceEvent {
behavior: id,
node: 0,
};
world.insert_resource(TracePaths(vec![(
id,
vec![vec![TraceStep::Field("do"), TraceStep::Index(0)]],
)]));
world.insert_resource(ExecutionTrace {
frame: 1,
events: vec![event],
vars: vec![("n".to_string(), TraceVal::Int(3))],
locals: Vec::new(),
hit: hit.then_some(event),
});
world
}
#[test]
fn trace_events_become_pulses_and_live_values() {
asset_id::reset_interner();
let id = asset_id::intern("b");
let mut h = playing_hook(vec![behavior(
"b",
serde_json::json!({
"on": "start", "do": [{"save": {}}],
}),
)]);
h.open[PanelKey::Behavior] = true;
let mut world = traced_world(id, false);
h.drive_trace(&mut world);
assert_eq!(h.behavior.pulses.len(), 1);
assert_eq!(
h.behavior.pulses[0].path,
vec![path::field("do"), path::Step::Index(0)],
"the pulse addresses the node the way a checker fault would"
);
assert_eq!(
h.live_vars,
vec![("n".to_string(), "int".to_string(), "3".to_string())]
);
let data = h.behavior_data();
assert_eq!(
data.pulse_cards.len(),
1,
"the node's card carries the pulse"
);
assert_eq!(data.pulse_rows.len(), 1, "so does its outline row");
h.drive_trace(&mut world);
assert_eq!(h.behavior.pulses.len(), 1);
let vdata = h.variables_data();
assert!(vdata.live);
assert!(
vdata.rows.iter().any(|r| r.name == "n" && r.value == "3"),
"{:?}",
vdata.rows
);
}
#[test]
fn pulses_decay_by_frame_time() {
asset_id::reset_interner();
let id = asset_id::intern("b");
let mut h = playing_hook(vec![behavior(
"b",
serde_json::json!({
"on": "start", "do": [{"save": {}}],
}),
)]);
h.open[PanelKey::Behavior] = true;
let mut world = traced_world(id, false);
h.drive_trace(&mut world);
assert_eq!(h.behavior.pulses[0].age, 0.0, "a fresh firing");
let half = crate::editor::behavior::pulse::PULSE_SECS * 0.5;
world.insert_resource(FrameTime {
dt: half,
elapsed: 0.0,
});
h.drive_trace(&mut world);
assert_eq!(h.behavior.pulses.len(), 1, "half the window keeps it");
assert!((h.behavior.pulses[0].age - half).abs() < 1e-6);
h.drive_trace(&mut world);
assert!(h.behavior.pulses.is_empty(), "the full window drops it");
}
#[test]
fn a_breakpoint_hit_pauses_and_lands_on_the_node() {
asset_id::reset_interner();
let id = asset_id::intern("b");
let mut h = playing_hook(vec![behavior(
"b",
serde_json::json!({
"on": "start", "do": [{"save": {}}],
}),
)]);
h.open[PanelKey::Behavior] = true;
let mut world = traced_world(id, true);
h.drive_trace(&mut world);
assert_eq!(h.sim.state, sim::SimState::Paused, "the hit froze the run");
let row = h.behavior.row.expect("the panel landed on the node");
assert_eq!(
h.behavior_rows()[row].path,
vec![path::field("do"), path::Step::Index(0)]
);
}
#[test]
fn stopping_clears_the_live_state() {
asset_id::reset_interner();
let id = asset_id::intern("b");
let mut h = playing_hook(vec![behavior(
"b",
serde_json::json!({
"on": "start", "do": [{"save": {}}],
}),
)]);
h.open[PanelKey::Behavior] = true;
let mut world = traced_world(id, false);
h.drive_trace(&mut world);
assert!(!h.behavior.pulses.is_empty() && !h.live_vars.is_empty());
assert!(h.sim.stop());
h.drive_trace(&mut world);
assert!(h.behavior.pulses.is_empty(), "Stop shows authored data");
assert!(h.live_vars.is_empty());
assert!(!h.variables_data().live);
}
#[test]
fn ctrl_click_toggles_a_card_breakpoint() {
let mut h = hook(vec![behavior(
"b",
serde_json::json!({
"on": "start", "do": [{"save": {}}],
}),
)]);
let mut world = World::new();
h.open[PanelKey::Behavior] = true;
let data = h.behavior_data();
let card = data
.chart
.cards
.iter()
.position(|c| c.kind == CardKind::Node)
.expect("the body has a node card");
h.ctrl_held = true;
h.apply_behavior_action(BehaviorAction::SelectCard(card), &mut world, [0.0, 0.0]);
assert_eq!(h.behavior.breakpoints.len(), 1);
assert_eq!(h.behavior.breakpoints[0].0, "b", "held by behavior name");
assert_eq!(
h.behavior_data().break_cards,
vec![card],
"the card shows its marker"
);
h.apply_behavior_action(BehaviorAction::SelectCard(card), &mut world, [0.0, 0.0]);
assert!(
h.behavior.breakpoints.is_empty(),
"a second toggle removes it"
);
h.ctrl_held = false;
h.apply_behavior_action(BehaviorAction::SelectCard(card), &mut world, [0.0, 0.0]);
assert!(h.behavior.row.is_some());
}