Stabilize control surface and external bridge v1
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"name": "staged pattern plus transition commit",
|
||||
"steps": [
|
||||
{
|
||||
"request": {
|
||||
"request_id": "style",
|
||||
"session_id": "desk-a",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_transition_style",
|
||||
"payload": {
|
||||
"style": "chase",
|
||||
"duration_ms": 480
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "transition style staged",
|
||||
"session": {
|
||||
"parameter_keys": [],
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 480
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "pattern",
|
||||
"session_id": "desk-a",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_pattern",
|
||||
"payload": {
|
||||
"pattern_id": "noise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "pattern staged: noise",
|
||||
"session": {
|
||||
"pattern_id": "noise",
|
||||
"parameter_keys": [],
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 480
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "trigger",
|
||||
"session_id": "desk-a",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "trigger_transition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "transition triggered: noise",
|
||||
"session": {
|
||||
"parameter_keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "state",
|
||||
"command": {
|
||||
"type": "get_state"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "state",
|
||||
"state": {
|
||||
"active_pattern_id": "noise",
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 480
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"name": "group update plus parameter change plus commit",
|
||||
"steps": [
|
||||
{
|
||||
"request": {
|
||||
"request_id": "group",
|
||||
"session_id": "desk-b",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "upsert_group",
|
||||
"payload": {
|
||||
"group_id": "focus_pair",
|
||||
"tags": ["runtime", "focus"],
|
||||
"members": [
|
||||
{ "node_id": "node-01", "panel_position": "top" },
|
||||
{ "node_id": "node-01", "panel_position": "middle" }
|
||||
],
|
||||
"overwrite": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "group updated: focus_pair"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "param",
|
||||
"session_id": "desk-b",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_group_parameter",
|
||||
"payload": {
|
||||
"group_id": "focus_pair",
|
||||
"key": "grain",
|
||||
"value": { "kind": "scalar", "value": 0.81 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "group parameter staged: grain",
|
||||
"session": {
|
||||
"has_group_target": true,
|
||||
"group_id": "focus_pair",
|
||||
"parameter_keys": ["grain"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "style",
|
||||
"session_id": "desk-b",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_transition_style",
|
||||
"payload": {
|
||||
"style": "chase",
|
||||
"duration_ms": 420
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "transition style staged",
|
||||
"session": {
|
||||
"has_group_target": true,
|
||||
"group_id": "focus_pair",
|
||||
"parameter_keys": ["grain"],
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 420
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "pattern",
|
||||
"session_id": "desk-b",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_pattern",
|
||||
"payload": {
|
||||
"pattern_id": "noise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "pattern staged: noise",
|
||||
"session": {
|
||||
"pattern_id": "noise",
|
||||
"has_group_target": true,
|
||||
"group_id": "focus_pair",
|
||||
"parameter_keys": ["grain"],
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 420
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "trigger",
|
||||
"session_id": "desk-b",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "trigger_transition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "transition triggered: noise on focus_pair",
|
||||
"session": {
|
||||
"parameter_keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "state",
|
||||
"command": {
|
||||
"type": "get_state"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "state",
|
||||
"state": {
|
||||
"selected_group": "focus_pair",
|
||||
"active_pattern_id": "noise",
|
||||
"active_scene_group": "focus_pair",
|
||||
"transition_style": "chase",
|
||||
"transition_duration_ms": 420,
|
||||
"scalar_parameters": [
|
||||
{ "key": "grain", "value": 0.81 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"name": "preset recall during running transition",
|
||||
"steps": [
|
||||
{
|
||||
"request": {
|
||||
"request_id": "style",
|
||||
"session_id": "desk-c",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_transition_style",
|
||||
"payload": {
|
||||
"style": "crossfade",
|
||||
"duration_ms": 1600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "transition style staged"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "pattern",
|
||||
"session_id": "desk-c",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_pattern",
|
||||
"payload": {
|
||||
"pattern_id": "noise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "pattern staged: noise"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "trigger",
|
||||
"session_id": "desk-c",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "trigger_transition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "transition triggered: noise"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "recall",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "recall_preset",
|
||||
"payload": {
|
||||
"preset_id": "ocean_gradient"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "preset recalled: ocean_gradient"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "state",
|
||||
"command": {
|
||||
"type": "get_state"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "state",
|
||||
"state": {
|
||||
"preset_id": "ocean_gradient",
|
||||
"active_pattern_id": "gradient",
|
||||
"active_transition_present": true,
|
||||
"event_message_contains": "preset recalled: ocean_gradient"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
{
|
||||
"name": "blackout during staged session",
|
||||
"steps": [
|
||||
{
|
||||
"request": {
|
||||
"request_id": "pattern",
|
||||
"session_id": "desk-d",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_pattern",
|
||||
"payload": {
|
||||
"pattern_id": "noise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "pattern staged: noise",
|
||||
"session": {
|
||||
"pattern_id": "noise",
|
||||
"parameter_keys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "param",
|
||||
"session_id": "desk-d",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_group_parameter",
|
||||
"payload": {
|
||||
"group_id": "bottom_panels",
|
||||
"key": "grain",
|
||||
"value": { "kind": "scalar", "value": 0.74 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "primitive_buffered",
|
||||
"summary_contains": "group parameter staged: grain",
|
||||
"session": {
|
||||
"pattern_id": "noise",
|
||||
"has_group_target": true,
|
||||
"group_id": "bottom_panels",
|
||||
"parameter_keys": ["grain"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "blackout",
|
||||
"session_id": "desk-d",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "blackout",
|
||||
"payload": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "blackout enabled",
|
||||
"session": {
|
||||
"pattern_id": "noise",
|
||||
"has_group_target": true,
|
||||
"group_id": "bottom_panels",
|
||||
"parameter_keys": ["grain"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "trigger",
|
||||
"session_id": "desk-d",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "trigger_transition"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "command_accepted",
|
||||
"summary_contains": "transition triggered: noise on bottom_panels"
|
||||
}
|
||||
},
|
||||
{
|
||||
"request": {
|
||||
"request_id": "state",
|
||||
"command": {
|
||||
"type": "get_state"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "state",
|
||||
"state": {
|
||||
"blackout": true,
|
||||
"active_pattern_id": "noise",
|
||||
"active_scene_group": "bottom_panels",
|
||||
"scalar_parameters": [
|
||||
{ "key": "grain", "value": 0.74 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "stateless bridge path rejects staged primitive",
|
||||
"steps": [
|
||||
{
|
||||
"request": {
|
||||
"request_id": "reject",
|
||||
"command": {
|
||||
"type": "execute_primitive",
|
||||
"payload": {
|
||||
"primitive": {
|
||||
"primitive": "set_pattern",
|
||||
"payload": {
|
||||
"pattern_id": "noise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"result_type": "error",
|
||||
"error_code": "show_control_session_required"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
307
crates/infinity_host/tests/show_control_v1_golden.rs
Normal file
307
crates/infinity_host/tests/show_control_v1_golden.rs
Normal file
@@ -0,0 +1,307 @@
|
||||
use infinity_config::ProjectConfig;
|
||||
use infinity_host::{
|
||||
ExternalBridgeRequest, ExternalBridgeResponse, ExternalBridgeResult, ExternalBridgeSessionView,
|
||||
ExternalBridgeStateView, ExternalControlBridge, HostApiPort, SceneParameterValue,
|
||||
SimulationHostService,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{fs, path::PathBuf, sync::Arc};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GoldenTrace {
|
||||
name: String,
|
||||
steps: Vec<GoldenStep>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GoldenStep {
|
||||
request: ExternalBridgeRequest,
|
||||
expect: GoldenExpectation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GoldenExpectation {
|
||||
result_type: String,
|
||||
#[serde(default)]
|
||||
summary_contains: Option<String>,
|
||||
#[serde(default)]
|
||||
error_code: Option<String>,
|
||||
#[serde(default)]
|
||||
session: Option<ExpectedSession>,
|
||||
#[serde(default)]
|
||||
state: Option<ExpectedState>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ExpectedSession {
|
||||
#[serde(default)]
|
||||
pattern_id: Option<String>,
|
||||
#[serde(default)]
|
||||
has_group_target: Option<bool>,
|
||||
#[serde(default)]
|
||||
group_id: Option<String>,
|
||||
#[serde(default)]
|
||||
parameter_keys: Vec<String>,
|
||||
#[serde(default)]
|
||||
transition_style: Option<String>,
|
||||
#[serde(default)]
|
||||
transition_duration_ms: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ExpectedState {
|
||||
#[serde(default)]
|
||||
blackout: Option<bool>,
|
||||
#[serde(default)]
|
||||
selected_group: Option<String>,
|
||||
#[serde(default)]
|
||||
active_pattern_id: Option<String>,
|
||||
#[serde(default)]
|
||||
preset_id: Option<String>,
|
||||
#[serde(default)]
|
||||
active_scene_group: Option<String>,
|
||||
#[serde(default)]
|
||||
transition_style: Option<String>,
|
||||
#[serde(default)]
|
||||
transition_duration_ms: Option<u32>,
|
||||
#[serde(default)]
|
||||
active_transition_present: Option<bool>,
|
||||
#[serde(default)]
|
||||
event_message_contains: Option<String>,
|
||||
#[serde(default)]
|
||||
scalar_parameters: Vec<ExpectedScalarParameter>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ExpectedScalarParameter {
|
||||
key: String,
|
||||
value: f32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_control_v1_golden_traces_replay_cleanly() {
|
||||
for path in golden_trace_paths() {
|
||||
let fixture = fs::read_to_string(&path).expect("golden trace fixture must be readable");
|
||||
let trace: GoldenTrace =
|
||||
serde_json::from_str(&fixture).expect("golden trace fixture must parse");
|
||||
run_trace(&trace, &path);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_trace(trace: &GoldenTrace, path: &PathBuf) {
|
||||
let service: Arc<dyn HostApiPort> = SimulationHostService::spawn_shared(sample_project());
|
||||
let mut bridge = ExternalControlBridge::new(service);
|
||||
|
||||
for (index, step) in trace.steps.iter().enumerate() {
|
||||
let response = bridge.handle_request(step.request.clone());
|
||||
assert_response(trace, path, index, &response, &step.expect);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_response(
|
||||
trace: &GoldenTrace,
|
||||
path: &PathBuf,
|
||||
step_index: usize,
|
||||
response: &ExternalBridgeResponse,
|
||||
expect: &GoldenExpectation,
|
||||
) {
|
||||
let context = format!(
|
||||
"{} step {} ({})",
|
||||
path.display(),
|
||||
step_index + 1,
|
||||
trace.name
|
||||
);
|
||||
|
||||
match expect.result_type.as_str() {
|
||||
"primitive_buffered" => match &response.result {
|
||||
ExternalBridgeResult::PrimitiveBuffered { summary } => {
|
||||
assert_summary_contains(summary, expect.summary_contains.as_deref(), &context);
|
||||
}
|
||||
other => panic!("{context}: expected primitive_buffered, got {other:?}"),
|
||||
},
|
||||
"command_accepted" => match &response.result {
|
||||
ExternalBridgeResult::CommandAccepted { summary, .. } => {
|
||||
assert_summary_contains(summary, expect.summary_contains.as_deref(), &context);
|
||||
}
|
||||
other => panic!("{context}: expected command_accepted, got {other:?}"),
|
||||
},
|
||||
"state" => match &response.result {
|
||||
ExternalBridgeResult::State { state } => {
|
||||
assert_state(state, expect.state.as_ref(), &context);
|
||||
}
|
||||
other => panic!("{context}: expected state, got {other:?}"),
|
||||
},
|
||||
"error" => match &response.result {
|
||||
ExternalBridgeResult::Error { error } => {
|
||||
let expected_code = expect
|
||||
.error_code
|
||||
.as_deref()
|
||||
.expect("error result requires error_code");
|
||||
assert_eq!(
|
||||
error.code, expected_code,
|
||||
"{context}: unexpected error code"
|
||||
);
|
||||
}
|
||||
other => panic!("{context}: expected error, got {other:?}"),
|
||||
},
|
||||
other => panic!("{context}: unsupported expected result type '{other}'"),
|
||||
}
|
||||
|
||||
assert_session(response.session.as_ref(), expect.session.as_ref(), &context);
|
||||
}
|
||||
|
||||
fn assert_summary_contains(summary: &str, expected: Option<&str>, context: &str) {
|
||||
if let Some(expected) = expected {
|
||||
assert!(
|
||||
summary.contains(expected),
|
||||
"{context}: summary '{summary}' does not contain '{expected}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_session(
|
||||
actual: Option<&ExternalBridgeSessionView>,
|
||||
expected: Option<&ExpectedSession>,
|
||||
context: &str,
|
||||
) {
|
||||
let Some(expected) = expected else {
|
||||
return;
|
||||
};
|
||||
let actual = actual.expect("expected session view");
|
||||
assert_eq!(
|
||||
actual.pending.pattern_id.as_deref(),
|
||||
expected.pattern_id.as_deref(),
|
||||
"{context}: unexpected pending pattern"
|
||||
);
|
||||
if let Some(has_group_target) = expected.has_group_target {
|
||||
assert_eq!(
|
||||
actual.pending.has_group_target, has_group_target,
|
||||
"{context}: unexpected pending group target flag"
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
actual.pending.group_id.as_deref(),
|
||||
expected.group_id.as_deref(),
|
||||
"{context}: unexpected pending group id"
|
||||
);
|
||||
if let Some(style) = expected.transition_style.as_deref() {
|
||||
assert_eq!(
|
||||
actual.pending.transition_style.map(|value| value.label()),
|
||||
Some(style),
|
||||
"{context}: unexpected pending transition style"
|
||||
);
|
||||
}
|
||||
if let Some(duration_ms) = expected.transition_duration_ms {
|
||||
assert_eq!(
|
||||
actual.pending.transition_duration_ms,
|
||||
Some(duration_ms),
|
||||
"{context}: unexpected pending transition duration"
|
||||
);
|
||||
}
|
||||
for key in &expected.parameter_keys {
|
||||
assert!(
|
||||
actual.pending.parameters.contains_key(key),
|
||||
"{context}: missing pending parameter '{key}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_state(actual: &ExternalBridgeStateView, expected: Option<&ExpectedState>, context: &str) {
|
||||
let Some(expected) = expected else {
|
||||
return;
|
||||
};
|
||||
if let Some(blackout) = expected.blackout {
|
||||
assert_eq!(
|
||||
actual.global.blackout, blackout,
|
||||
"{context}: unexpected blackout state"
|
||||
);
|
||||
}
|
||||
if let Some(selected_group) = expected.selected_group.as_deref() {
|
||||
assert_eq!(
|
||||
actual.global.selected_group.as_deref(),
|
||||
Some(selected_group),
|
||||
"{context}: unexpected selected group"
|
||||
);
|
||||
}
|
||||
if let Some(active_pattern_id) = expected.active_pattern_id.as_deref() {
|
||||
assert_eq!(
|
||||
actual.active_scene.pattern_id, active_pattern_id,
|
||||
"{context}: unexpected active pattern"
|
||||
);
|
||||
}
|
||||
if let Some(preset_id) = expected.preset_id.as_deref() {
|
||||
assert_eq!(
|
||||
actual.active_scene.preset_id.as_deref(),
|
||||
Some(preset_id),
|
||||
"{context}: unexpected preset id"
|
||||
);
|
||||
}
|
||||
if let Some(active_scene_group) = expected.active_scene_group.as_deref() {
|
||||
assert_eq!(
|
||||
actual.active_scene.target_group.as_deref(),
|
||||
Some(active_scene_group),
|
||||
"{context}: unexpected active scene group"
|
||||
);
|
||||
}
|
||||
if let Some(transition_style) = expected.transition_style.as_deref() {
|
||||
assert_eq!(
|
||||
actual.global.transition_style.label(),
|
||||
transition_style,
|
||||
"{context}: unexpected transition style"
|
||||
);
|
||||
}
|
||||
if let Some(transition_duration_ms) = expected.transition_duration_ms {
|
||||
assert_eq!(
|
||||
actual.global.transition_duration_ms, transition_duration_ms,
|
||||
"{context}: unexpected transition duration"
|
||||
);
|
||||
}
|
||||
if let Some(active_transition_present) = expected.active_transition_present {
|
||||
assert_eq!(
|
||||
actual.engine.active_transition.is_some(),
|
||||
active_transition_present,
|
||||
"{context}: unexpected active transition presence"
|
||||
);
|
||||
}
|
||||
if let Some(message_fragment) = expected.event_message_contains.as_deref() {
|
||||
assert!(
|
||||
actual
|
||||
.recent_events
|
||||
.iter()
|
||||
.any(|event| event.message.contains(message_fragment)),
|
||||
"{context}: missing event containing '{message_fragment}'"
|
||||
);
|
||||
}
|
||||
for expected_parameter in &expected.scalar_parameters {
|
||||
assert!(
|
||||
actual.active_scene.parameters.iter().any(|parameter| {
|
||||
parameter.key == expected_parameter.key
|
||||
&& parameter.value == SceneParameterValue::Scalar(expected_parameter.value)
|
||||
}),
|
||||
"{context}: missing scalar parameter '{}'={}",
|
||||
expected_parameter.key,
|
||||
expected_parameter.value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn golden_trace_paths() -> Vec<PathBuf> {
|
||||
let mut paths = fs::read_dir(golden_trace_dir())
|
||||
.expect("golden trace directory must exist")
|
||||
.map(|entry| entry.expect("golden trace entry").path())
|
||||
.filter(|path| path.extension().and_then(|value| value.to_str()) == Some("json"))
|
||||
.collect::<Vec<_>>();
|
||||
paths.sort();
|
||||
paths
|
||||
}
|
||||
|
||||
fn golden_trace_dir() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests")
|
||||
.join("golden_traces")
|
||||
}
|
||||
|
||||
fn sample_project() -> ProjectConfig {
|
||||
ProjectConfig::from_toml_str(include_str!("../../../config/project.example.toml"))
|
||||
.expect("project config must parse")
|
||||
}
|
||||
Reference in New Issue
Block a user