use scxml::model::StateKind;
use scxml::{parse_xml, validate};
#[test]
fn w3c_144_atomic_state() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<transition event="done" target="pass"/>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
assert_eq!(chart.states[0].kind, StateKind::Atomic);
}
#[test]
fn w3c_147_compound_state() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1" initial="s1_1">
<state id="s1_1">
<transition event="done" target="pass"/>
</state>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
assert_eq!(chart.states[0].kind, StateKind::Compound);
assert_eq!(chart.states[0].children.len(), 1);
}
#[test]
fn w3c_155_parallel_state() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="p">
<parallel id="p">
<state id="r1" initial="r1_a">
<state id="r1_a">
<transition event="done" target="r1_b"/>
</state>
<final id="r1_b"/>
</state>
<state id="r2" initial="r2_a">
<state id="r2_a">
<transition event="done" target="r2_b"/>
</state>
<final id="r2_b"/>
</state>
</parallel>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
assert_eq!(chart.states[0].kind, StateKind::Parallel);
assert_eq!(chart.states[0].children.len(), 2);
}
#[test]
fn w3c_351_eventless_transition() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<transition target="pass"/>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
let t = &chart.states[0].transitions[0];
assert!(t.event.is_none());
assert_eq!(t.targets[0].as_str(), "pass");
}
#[test]
fn w3c_352_guarded_transition() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<transition event="e" cond="true" target="pass"/>
<transition event="e" target="fail"/>
</state>
<final id="pass"/>
<final id="fail"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
assert_eq!(
chart.states[0].transitions[0].guard.as_deref(),
Some("true")
);
assert!(chart.states[0].transitions[1].guard.is_none());
}
#[test]
fn w3c_355_multiple_targets() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="p">
<parallel id="p">
<state id="r1" initial="r1_a">
<state id="r1_a"/>
<state id="r1_b"/>
</state>
<state id="r2" initial="r2_a">
<state id="r2_a">
<transition event="e" target="r1_b r2_b"/>
</state>
<state id="r2_b"/>
</state>
</parallel>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
let t = &chart.states[0].children[1].children[0].transitions[0];
assert_eq!(t.targets.len(), 2);
assert_eq!(t.targets[0].as_str(), "r1_b");
assert_eq!(t.targets[1].as_str(), "r2_b");
}
#[test]
fn w3c_403_internal_transition() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1" initial="s1_1">
<transition event="e" type="internal" target="s1_1"/>
<state id="s1_1">
<transition event="done" target="pass"/>
</state>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
let t = &chart.states[0].transitions[0];
assert_eq!(t.transition_type, scxml::model::TransitionType::Internal);
}
#[test]
fn w3c_372_final_state() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<transition event="done" target="pass"/>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
assert_eq!(chart.states[1].kind, StateKind::Final);
}
#[test]
fn w3c_387_shallow_history() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1" initial="s1_1">
<history id="h" type="shallow">
<transition target="s1_1"/>
</history>
<state id="s1_1">
<transition event="next" target="s1_2"/>
</state>
<state id="s1_2">
<transition event="done" target="pass"/>
</state>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
validate(&chart).unwrap();
let hist = &chart.states[0].children[0];
assert_eq!(
hist.kind,
StateKind::History(scxml::model::HistoryKind::Shallow)
);
assert_eq!(hist.transitions.len(), 1);
}
#[test]
fn w3c_388_deep_history() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1" initial="s1_1">
<history id="h" type="deep">
<transition target="s1_1"/>
</history>
<state id="s1_1">
<transition event="done" target="pass"/>
</state>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
let hist = &chart.states[0].children[0];
assert_eq!(
hist.kind,
StateKind::History(scxml::model::HistoryKind::Deep)
);
}
#[test]
fn w3c_487_datamodel() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1"
datamodel="null">
<datamodel>
<data id="x" expr="1"/>
<data id="y"/>
</datamodel>
<state id="s1">
<transition event="done" target="pass"/>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
assert_eq!(chart.datamodel.items.len(), 2);
assert_eq!(chart.datamodel.items[0].id.as_str(), "x");
assert_eq!(chart.datamodel.items[0].expr.as_deref(), Some("1"));
assert!(chart.datamodel.items[1].expr.is_none());
}
#[test]
fn w3c_550_raise_action() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<onentry>
<raise event="internal_event"/>
</onentry>
<transition event="done" target="pass"/>
</state>
<final id="pass"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
assert_eq!(chart.states[0].on_entry.len(), 1);
match &chart.states[0].on_entry[0].kind {
scxml::model::ActionKind::Raise { event } => {
assert_eq!(event.as_str(), "internal_event");
}
other => panic!("expected Raise, got {other:?}"),
}
}
#[test]
fn w3c_initial_attribute_must_exist() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="nonexistent">
<state id="s1"/>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
let result = validate(&chart);
assert!(result.is_err());
}
#[test]
fn w3c_script_as_descriptor() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<onentry>
<script>var x = 1;</script>
</onentry>
</state>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
assert!(matches!(
&chart.states[0].on_entry[0].kind,
scxml::ActionKind::Script { content } if content == "var x = 1;"
));
}
#[test]
fn w3c_invoke_as_descriptor() {
let xml = r#"
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="s1">
<state id="s1">
<invoke type="scxml" src="child.scxml"/>
</state>
</scxml>
"#;
let chart = parse_xml(xml).unwrap();
assert!(matches!(
&chart.states[0].on_entry[0].kind,
scxml::ActionKind::Invoke { invoke_type: Some(t), src: Some(s), .. }
if t == "scxml" && s == "child.scxml"
));
}