use crate::lighting::effects::*;
use crate::lighting::engine::tests::common::create_test_fixture;
use crate::lighting::engine::EffectEngine;
use std::collections::HashMap;
use std::time::Duration;
#[test]
fn test_stop_sequence_stops_all_effects_from_sequence() {
let mut engine = EffectEngine::new();
let fixture1 = create_test_fixture("test_fixture_1", 1, 1);
let fixture2 = create_test_fixture("test_fixture_2", 1, 10);
engine.register_fixture(fixture1);
engine.register_fixture(fixture2);
let mut seq1_effect1 = EffectInstance::new(
"seq_intro_effect_1".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.5);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture_1".to_string()],
None,
None,
None,
);
seq1_effect1.id = "seq_intro_effect_1".to_string();
seq1_effect1.layer = EffectLayer::Background;
let mut seq1_effect2 = EffectInstance::new(
"seq_intro_effect_2".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("red".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture_1".to_string()],
None,
None,
None,
);
seq1_effect2.id = "seq_intro_effect_2".to_string();
seq1_effect2.layer = EffectLayer::Midground;
let mut seq2_effect = EffectInstance::new(
"seq_outro_effect_1".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("green".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture_2".to_string()],
None,
None,
None,
);
seq2_effect.id = "seq_outro_effect_1".to_string();
seq2_effect.layer = EffectLayer::Background;
let mut regular_effect = EffectInstance::new(
"regular_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("blue".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture_2".to_string()],
None,
None,
None,
);
regular_effect.id = "regular_effect".to_string();
regular_effect.layer = EffectLayer::Foreground;
engine.start_effect(seq1_effect1).unwrap();
engine.start_effect(seq1_effect2).unwrap();
engine.start_effect(seq2_effect).unwrap();
engine.start_effect(regular_effect).unwrap();
assert_eq!(engine.active_effects_count(), 4);
engine.stop_sequence("intro");
assert_eq!(engine.active_effects_count(), 2);
assert!(!engine.has_effect("seq_intro_effect_1"));
assert!(!engine.has_effect("seq_intro_effect_2"));
assert!(engine.has_effect("seq_outro_effect_1"));
assert!(engine.has_effect("regular_effect"));
}
#[test]
fn test_stop_sequence_handles_releasing_effects() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut seq_effect = EffectInstance::new(
"seq_test_effect_1".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
seq_effect.id = "seq_test_effect_1".to_string();
seq_effect.layer = EffectLayer::Foreground;
engine.start_effect(seq_effect).unwrap();
assert_eq!(engine.active_effects_count(), 1);
engine.release_layer(EffectLayer::Foreground);
assert_eq!(engine.active_effects_count(), 1);
engine.update(Duration::from_millis(100), None).unwrap();
engine.stop_sequence("test");
assert_eq!(engine.active_effects_count(), 0);
assert!(!engine.has_effect("seq_test_effect_1"));
}
#[test]
fn test_stop_sequence_with_no_matching_effects() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let regular_effect = EffectInstance::new(
"regular_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.5);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
engine.start_effect(regular_effect).unwrap();
assert_eq!(engine.active_effects_count(), 1);
engine.stop_sequence("nonexistent");
assert_eq!(engine.active_effects_count(), 1);
assert!(engine.has_effect("regular_effect"));
}
#[test]
fn test_stop_sequence_with_empty_engine() {
let mut engine = EffectEngine::new();
engine.stop_sequence("any_sequence");
assert_eq!(engine.active_effects_count(), 0);
}
#[test]
fn test_clear_all_layers_stops_all_effects() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut bg_effect = EffectInstance::new(
"bg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.3);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
bg_effect.layer = EffectLayer::Background;
let mut mid_effect = EffectInstance::new(
"mid_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("red".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
mid_effect.layer = EffectLayer::Midground;
let mut fg_effect = EffectInstance::new(
"fg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("green".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
fg_effect.layer = EffectLayer::Foreground;
engine.start_effect(bg_effect).unwrap();
engine.start_effect(mid_effect).unwrap();
engine.start_effect(fg_effect).unwrap();
assert_eq!(engine.active_effects_count(), 3);
engine.clear_all_layers();
assert_eq!(engine.active_effects_count(), 0);
assert!(!engine.has_effect("bg_effect"));
assert!(!engine.has_effect("mid_effect"));
assert!(!engine.has_effect("fg_effect"));
}
#[test]
fn test_clear_all_layers_clears_frozen_layers() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut bg_effect = EffectInstance::new(
"bg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.5);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
bg_effect.layer = EffectLayer::Background;
engine.start_effect(bg_effect).unwrap();
engine.freeze_layer(EffectLayer::Background);
assert!(engine.is_layer_frozen(EffectLayer::Background));
assert_eq!(engine.active_effects_count(), 1);
engine.clear_all_layers();
assert!(!engine.is_layer_frozen(EffectLayer::Background));
assert_eq!(engine.active_effects_count(), 0);
}
#[test]
fn test_clear_all_layers_clears_releasing_effects() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut bg_effect = EffectInstance::new(
"bg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.5);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
bg_effect.layer = EffectLayer::Background;
let mut fg_effect = EffectInstance::new(
"fg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("red".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
fg_effect.layer = EffectLayer::Foreground;
engine.start_effect(bg_effect).unwrap();
engine.start_effect(fg_effect).unwrap();
engine.release_layer(EffectLayer::Foreground);
engine.update(Duration::from_millis(100), None).unwrap();
engine.clear_all_layers();
assert_eq!(engine.active_effects_count(), 0);
}
#[test]
fn test_clear_all_layers_clears_fixture_states_and_locks() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut fg_effect = EffectInstance::new(
"fg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("red".to_string(), 1.0);
p.insert("blue".to_string(), 1.0);
p
},
duration: Duration::from_secs(5), },
vec!["test_fixture".to_string()],
None,
None,
None,
);
fg_effect.layer = EffectLayer::Foreground;
fg_effect.blend_mode = BlendMode::Replace;
engine.start_effect(fg_effect).unwrap();
let commands_before = engine.update(Duration::from_millis(100), None).unwrap();
assert!(
!commands_before.is_empty(),
"Should have DMX commands after processing effect"
);
engine.clear_all_layers();
assert_eq!(engine.active_effects_count(), 0);
let mut bg_effect = EffectInstance::new(
"bg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("green".to_string(), 1.0); p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
bg_effect.layer = EffectLayer::Background;
bg_effect.blend_mode = BlendMode::Replace;
engine.start_effect(bg_effect).unwrap();
let commands_after = engine.update(Duration::from_millis(100), None).unwrap();
assert!(
!commands_after.is_empty(),
"Should be able to control channels after clear_all_layers"
);
}
#[test]
fn test_clear_all_layers_with_empty_engine() {
let mut engine = EffectEngine::new();
engine.clear_all_layers();
assert_eq!(engine.active_effects_count(), 0);
}
#[test]
fn test_freeze_unfreeze_multiple_effects_same_layer() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut channels = HashMap::new();
channels.insert("red".to_string(), 1);
channels.insert("green".to_string(), 2);
channels.insert("blue".to_string(), 3);
let rgb_fixture = FixtureInfo::new(
"rgb_fixture".to_string(),
1,
1,
"RGB".to_string(),
channels,
None,
);
engine.register_fixture(rgb_fixture);
let mut effect1 = EffectInstance::new(
"effect1".to_string(),
EffectType::Rainbow {
speed: TempoAwareSpeed::Fixed(1.0),
saturation: 1.0,
brightness: 1.0,
duration: Duration::from_secs(5),
},
vec!["rgb_fixture".to_string()],
None,
None,
None,
);
effect1.layer = EffectLayer::Background;
let mut effect2 = EffectInstance::new(
"effect2".to_string(),
EffectType::ColorCycle {
colors: vec![
Color::new(255, 0, 0),
Color::new(0, 255, 0),
Color::new(0, 0, 255),
],
speed: TempoAwareSpeed::Fixed(2.0),
direction: CycleDirection::Forward,
transition: CycleTransition::Snap,
duration: Duration::from_secs(5),
},
vec!["rgb_fixture".to_string()],
None,
None,
None,
);
effect2.layer = EffectLayer::Background;
engine.start_effect(effect1).unwrap();
engine.start_effect(effect2).unwrap();
let _commands1 = engine.update(Duration::from_millis(200), None).unwrap();
let commands_before = engine
.update(Duration::from_millis(10), None)
.unwrap()
.to_vec();
engine.freeze_layer(EffectLayer::Background);
assert!(engine.is_layer_frozen(EffectLayer::Background));
let commands_frozen1 = engine
.update(Duration::from_millis(500), None)
.unwrap()
.to_vec();
let commands_frozen2 = engine
.update(Duration::from_millis(500), None)
.unwrap()
.to_vec();
let mut before_sorted: Vec<_> = commands_before.iter().collect();
before_sorted.sort_by_key(|c| c.channel);
let mut frozen1_sorted: Vec<_> = commands_frozen1.iter().collect();
frozen1_sorted.sort_by_key(|c| c.channel);
let mut frozen2_sorted: Vec<_> = commands_frozen2.iter().collect();
frozen2_sorted.sort_by_key(|c| c.channel);
assert_eq!(frozen1_sorted.len(), frozen2_sorted.len());
for (f1, f2) in frozen1_sorted.iter().zip(frozen2_sorted.iter()) {
assert_eq!(f1.channel, f2.channel);
assert!(
(f1.value as i32 - f2.value as i32).abs() <= 1,
"Frozen values should match: {} vs {}",
f1.value,
f2.value
);
}
engine.unfreeze_layer(EffectLayer::Background);
assert!(!engine.is_layer_frozen(EffectLayer::Background));
let commands_after = engine.update(Duration::from_millis(200), None).unwrap();
assert!(!commands_after.is_empty());
}
#[test]
fn test_freeze_unfreeze_different_layers_independently() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut bg_effect = EffectInstance::new(
"bg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 0.5);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
bg_effect.layer = EffectLayer::Background;
let mut fg_effect = EffectInstance::new(
"fg_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("red".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
fg_effect.layer = EffectLayer::Foreground;
engine.start_effect(bg_effect).unwrap();
engine.start_effect(fg_effect).unwrap();
engine.freeze_layer(EffectLayer::Background);
assert!(engine.is_layer_frozen(EffectLayer::Background));
assert!(!engine.is_layer_frozen(EffectLayer::Foreground));
engine.unfreeze_layer(EffectLayer::Background);
engine.freeze_layer(EffectLayer::Foreground);
assert!(!engine.is_layer_frozen(EffectLayer::Background));
assert!(engine.is_layer_frozen(EffectLayer::Foreground));
assert_eq!(engine.active_effects_count(), 2);
}
#[test]
fn test_freeze_unfreeze_with_releasing_effects() {
let mut engine = EffectEngine::new();
let fixture = create_test_fixture("test_fixture", 1, 1);
engine.register_fixture(fixture);
let mut effect = EffectInstance::new(
"test_effect".to_string(),
EffectType::Static {
parameters: {
let mut p = HashMap::new();
p.insert("dimmer".to_string(), 1.0);
p
},
duration: Duration::from_secs(5),
},
vec!["test_fixture".to_string()],
None,
None,
None,
);
effect.layer = EffectLayer::Background;
engine.start_effect(effect).unwrap();
engine.freeze_layer(EffectLayer::Background);
assert!(engine.is_layer_frozen(EffectLayer::Background));
engine.release_layer(EffectLayer::Background);
assert!(!engine.is_layer_frozen(EffectLayer::Background));
}