use super::common::*;
#[cfg(test)]
use crate::lighting::effects::*;
use crate::lighting::engine::EffectEngine;
use std::collections::HashMap;
use std::time::Duration;
#[test]
fn test_channel_state_blending() {
let base_state = ChannelState::new(0.8, EffectLayer::Background, BlendMode::Replace);
let overlay_state = ChannelState::new(0.5, EffectLayer::Foreground, BlendMode::Multiply);
let blended = base_state.blend_with(overlay_state);
assert!((blended.value - 0.4).abs() < 0.01);
assert_eq!(blended.layer, EffectLayer::Foreground); assert_eq!(blended.blend_mode, BlendMode::Multiply); }
#[test]
fn test_fixture_state_blending() {
let mut fixture1 = FixtureState::new();
fixture1.set_channel(
"red".to_string(),
ChannelState::new(1.0, EffectLayer::Background, BlendMode::Replace),
);
fixture1.set_channel(
"green".to_string(),
ChannelState::new(0.5, EffectLayer::Background, BlendMode::Replace),
);
let mut fixture2 = FixtureState::new();
fixture2.set_channel(
"green".to_string(),
ChannelState::new(0.8, EffectLayer::Foreground, BlendMode::Multiply),
);
fixture2.set_channel(
"blue".to_string(),
ChannelState::new(0.3, EffectLayer::Foreground, BlendMode::Replace),
);
fixture1.blend_with(&fixture2);
let green_state = fixture1.channels.get("green").unwrap();
assert!((green_state.value - 0.4).abs() < 0.01);
let blue_state = fixture1.channels.get("blue").unwrap();
assert!((blue_state.value - 0.3).abs() < 0.01);
let red_state = fixture1.channels.get("red").unwrap();
assert!((red_state.value - 1.0).abs() < 0.01);
}
#[test]
fn test_blend_mode_loss_debug() {
use super::super::effects::*;
use super::super::engine::EffectEngine;
use super::super::parser::parse_light_shows;
use std::collections::HashMap;
let dsl_with_multiply = r#"show "Blend Mode Loss Test" {
@00:00.000
front_wash: static color: "blue", duration: 10s, layer: background, blend_mode: replace
@00:02.000
front_wash: dimmer start_level: 1.0, end_level: 0.5, duration: 5s, layer: midground, blend_mode: multiply
}"#;
let result = parse_light_shows(dsl_with_multiply);
assert!(
result.is_ok(),
"DSL should parse successfully: {:?}",
result
);
let shows = result.unwrap();
let show = shows.get("Blend Mode Loss Test").unwrap();
let dimmer_cue = &show.cues[1];
let dimmer_effect = &dimmer_cue.effects[0];
assert_eq!(dimmer_effect.blend_mode, Some(BlendMode::Multiply));
println!(
"✅ DSL parsing: dimmer effect has blend_mode = {:?}",
dimmer_effect.blend_mode
);
let mut engine = EffectEngine::new();
let mut channels = HashMap::new();
channels.insert("red".to_string(), 1);
channels.insert("green".to_string(), 2);
channels.insert("blue".to_string(), 3);
channels.insert("strobe".to_string(), 4);
let fixture = FixtureInfo::new(
"front_wash".to_string(),
1,
1,
"Astera-PixelBrick".to_string(),
channels,
Some(20.0),
);
engine.register_fixture(fixture);
let static_effect = create_effect_with_layering(
"static_blue".to_string(),
EffectType::Static {
parameters: {
let mut params = HashMap::new();
params.insert("red".to_string(), 0.0);
params.insert("green".to_string(), 0.0);
params.insert("blue".to_string(), 1.0);
params
},
duration: Duration::from_secs(10),
},
vec!["front_wash".to_string()],
EffectLayer::Background,
BlendMode::Replace,
);
let dimmer_effect = create_effect_with_layering(
"dimmer_multiply".to_string(),
EffectType::Dimmer {
start_level: 1.0,
end_level: 0.5,
duration: Duration::from_secs(5),
curve: DimmerCurve::Linear,
},
vec!["front_wash".to_string()],
EffectLayer::Midground,
BlendMode::Multiply,
);
println!("✅ Created effects:");
println!(
" Static effect: blend_mode = {:?}",
static_effect.blend_mode
);
println!(
" Dimmer effect: blend_mode = {:?}",
dimmer_effect.blend_mode
);
engine.start_effect(static_effect).unwrap();
let _commands = engine.update(Duration::from_secs(0), None).unwrap();
println!("✅ Applied static effect");
engine.start_effect(dimmer_effect).unwrap();
let _commands = engine.update(Duration::from_secs(2), None).unwrap();
println!("✅ Applied dimmer effect");
}
#[test]
fn test_timeline_blend_mode_loss() {
use super::super::effects::*;
use super::super::engine::EffectEngine;
use super::super::parser::parse_light_shows;
use super::super::timeline::LightingTimeline;
use std::collections::HashMap;
let dsl_with_multiply = r#"show "Timeline Blend Mode Test" {
@00:00.000
front_wash: static color: "blue", duration: 10s, layer: background, blend_mode: replace
@00:02.000
front_wash: dimmer start_level: 1.0, end_level: 0.5, duration: 5s, layer: midground, blend_mode: multiply
}"#;
let result = parse_light_shows(dsl_with_multiply);
assert!(
result.is_ok(),
"DSL should parse successfully: {:?}",
result
);
let shows = result.unwrap();
let show = shows.get("Timeline Blend Mode Test").unwrap();
let dimmer_cue = &show.cues[1];
let dimmer_effect = &dimmer_cue.effects[0];
assert_eq!(dimmer_effect.blend_mode, Some(BlendMode::Multiply));
println!(
"✅ DSL parsing: dimmer effect has blend_mode = {:?}",
dimmer_effect.blend_mode
);
let mut timeline = LightingTimeline::new_with_cues(show.cues.clone());
println!("✅ Created timeline with {} cues", show.cues.len());
let mut engine = EffectEngine::new();
let mut channels = HashMap::new();
channels.insert("red".to_string(), 1);
channels.insert("green".to_string(), 2);
channels.insert("blue".to_string(), 3);
channels.insert("strobe".to_string(), 4);
let fixture = FixtureInfo::new(
"front_wash".to_string(),
1,
1,
"Astera-PixelBrick".to_string(),
channels,
Some(20.0),
);
engine.register_fixture(fixture);
timeline.start();
println!("✅ Started timeline");
let result_at_0s = timeline.update(Duration::from_secs(0));
println!("✅ Timeline at 0s: {} effects", result_at_0s.effects.len());
for effect in &result_at_0s.effects {
println!(
" Effect: {} blend_mode = {:?}",
effect.id, effect.blend_mode
);
}
let result_at_2s = timeline.update(Duration::from_secs(2));
println!("✅ Timeline at 2s: {} effects", result_at_2s.effects.len());
for effect in &result_at_2s.effects {
println!(
" Effect: {} blend_mode = {:?}",
effect.id, effect.blend_mode
);
}
for effect in result_at_0s.effects {
engine.start_effect(effect).unwrap();
}
let _commands = engine.update(Duration::from_secs(0), None).unwrap();
println!("✅ Applied static effect from timeline");
for effect in result_at_2s.effects {
engine.start_effect(effect).unwrap();
}
let _commands = engine.update(Duration::from_secs(2), None).unwrap();
println!("✅ Applied dimmer effect from timeline");
}
#[test]
fn test_blend_mode_compatibility_matrix() {
let mut engine = EffectEngine::new();
let mut channels = HashMap::new();
channels.insert("red".to_string(), 1);
channels.insert("green".to_string(), 2);
channels.insert("blue".to_string(), 3);
let fixture = FixtureInfo::new(
"test_fixture".to_string(),
1,
1,
"RGB_Par".to_string(),
channels,
Some(20.0),
);
engine.register_fixture(fixture);
let replace_effect = create_effect_with_layering(
"replace_effect".to_string(),
EffectType::Static {
parameters: {
let mut params = HashMap::new();
params.insert("red".to_string(), 1.0);
params
},
duration: Duration::from_secs(10),
},
vec!["test_fixture".to_string()],
EffectLayer::Background,
BlendMode::Replace,
);
let multiply_effect = create_effect_with_layering(
"multiply_effect".to_string(),
EffectType::Static {
parameters: {
let mut params = HashMap::new();
params.insert("blue".to_string(), 1.0);
params
},
duration: Duration::from_secs(10),
},
vec!["test_fixture".to_string()],
EffectLayer::Background, BlendMode::Multiply,
);
engine.start_effect(replace_effect).unwrap();
engine.start_effect(multiply_effect).unwrap();
assert_eq!(engine.active_effects_count(), 2);
assert!(engine.has_effect("replace_effect"));
assert!(engine.has_effect("multiply_effect"));
let add_effect = create_effect_with_layering(
"add_effect".to_string(),
EffectType::Dimmer {
start_level: 1.0,
end_level: 0.5,
duration: Duration::from_secs(1),
curve: DimmerCurve::Linear,
},
vec!["test_fixture".to_string()],
EffectLayer::Background,
BlendMode::Add,
);
let overlay_effect = create_effect_with_layering(
"overlay_effect".to_string(),
EffectType::Pulse {
base_level: 0.5,
pulse_amplitude: 0.3,
frequency: TempoAwareFrequency::Fixed(2.0),
duration: Duration::from_secs(10),
},
vec!["test_fixture".to_string()],
EffectLayer::Background, BlendMode::Overlay,
);
engine.start_effect(add_effect).unwrap();
engine.start_effect(overlay_effect).unwrap();
assert_eq!(engine.active_effects_count(), 4); assert!(engine.has_effect("add_effect"));
assert!(engine.has_effect("overlay_effect"));
}