using DeepDrftPublic.Client.Services; namespace DeepDrftTests; /// /// Unit tests for the Theater-Mode auto-exit invariant on /// (Phase 20 bug fix): when both subsystems are disabled, /// must force TheaterMode = false so observers never see a stranded-theater state. /// [TestFixture] public class WaveformVisualizerControlStateTests { private WaveformVisualizerControlState _state = null!; [SetUp] public void SetUp() => _state = new WaveformVisualizerControlState(); // ── CoerceTheaterMode guard ── // Both off + Theater on → coerce exits theater. [Test] public void CoerceTheaterMode_BothOff_TheaterBecomesFalse() { _state.TheaterMode = true; _state.LavaEnabled = false; _state.WaveformEnabled = false; _state.CoerceTheaterMode(); Assert.That(_state.TheaterMode, Is.False); } // Lava still on → theater is left alone even if waveform is off. [Test] public void CoerceTheaterMode_LavaOnWaveformOff_TheaterPreserved() { _state.TheaterMode = true; _state.LavaEnabled = true; _state.WaveformEnabled = false; _state.CoerceTheaterMode(); Assert.That(_state.TheaterMode, Is.True); } // Waveform still on → theater is left alone even if lava is off. [Test] public void CoerceTheaterMode_WaveformOnLavaOff_TheaterPreserved() { _state.TheaterMode = true; _state.LavaEnabled = false; _state.WaveformEnabled = true; _state.CoerceTheaterMode(); Assert.That(_state.TheaterMode, Is.True); } // Theater already false + both off → no change (no false-positive write). [Test] public void CoerceTheaterMode_TheaterAlreadyFalse_NoChange() { _state.TheaterMode = false; _state.LavaEnabled = false; _state.WaveformEnabled = false; _state.CoerceTheaterMode(); Assert.That(_state.TheaterMode, Is.False); } // ── Changed event fires once with coerced state visible ── // Verify that after coercion, the Changed notification carries the already-corrected TheaterMode // value — all observers see a consistent state in the single Changed cycle. [Test] public void NotifyChanged_AfterCoerce_ObserverSeesTheaterFalse() { _state.TheaterMode = true; _state.LavaEnabled = false; _state.WaveformEnabled = false; bool? observedTheaterMode = null; _state.Changed += () => observedTheaterMode = _state.TheaterMode; _state.CoerceTheaterMode(); _state.NotifyChanged(); Assert.That(observedTheaterMode, Is.False); } // ── ApplyCapabilityDefault: the one-time HW-accel default-set ── // No HW accel → lava defaults off, waveform stays on (the whole point of the feature). [Test] public void ApplyCapabilityDefault_NoHardwareAccel_LavaOffWaveformOn() { _state.ApplyCapabilityDefault(hardwareAccelerated: false); Assert.Multiple(() => { Assert.That(_state.LavaEnabled, Is.False); Assert.That(_state.WaveformEnabled, Is.True); }); } // HW accel present → no change; lava keeps its shipped on-default (the common case is untouched). [Test] public void ApplyCapabilityDefault_HardwareAccelerated_LavaStaysOn() { _state.ApplyCapabilityDefault(hardwareAccelerated: true); Assert.Multiple(() => { Assert.That(_state.LavaEnabled, Is.True); Assert.That(_state.WaveformEnabled, Is.True); }); } // No HW accel raises Changed once so the controls UI / bridge / theater observers all react. [Test] public void ApplyCapabilityDefault_NoHardwareAccel_RaisesChanged() { var raised = 0; _state.Changed += () => raised++; _state.ApplyCapabilityDefault(hardwareAccelerated: false); Assert.That(raised, Is.EqualTo(1)); } // HW accel present must not churn observers — no Changed when nothing changed. [Test] public void ApplyCapabilityDefault_HardwareAccelerated_DoesNotRaiseChanged() { var raised = 0; _state.Changed += () => raised++; _state.ApplyCapabilityDefault(hardwareAccelerated: true); Assert.That(raised, Is.Zero); } // Guarded to once per session: a second call cannot re-clobber, even with a different verdict. [Test] public void ApplyCapabilityDefault_SecondCall_IsNoOp() { _state.ApplyCapabilityDefault(hardwareAccelerated: true); // first call wins (accelerated → on) _state.ApplyCapabilityDefault(hardwareAccelerated: false); // must be ignored Assert.That(_state.LavaEnabled, Is.True); } // Must not override an explicit in-session toggle: once applied, a user re-enabling lava on a // software renderer sticks — a later (guarded) call never reverts it. [Test] public void ApplyCapabilityDefault_DoesNotOverrideExplicitUserToggle() { _state.ApplyCapabilityDefault(hardwareAccelerated: false); // default: lava off _state.LavaEnabled = true; // user re-enables at their own risk _state.ApplyCapabilityDefault(hardwareAccelerated: false); // guarded → no-op Assert.That(_state.LavaEnabled, Is.True); } // No HW accel while Theater Mode is on with only lava active → coercion exits theater in the same // cycle (lava off + waveform off would strand theater; here waveform stays on, but verify the // coercion path is exercised when it WOULD strand). [Test] public void ApplyCapabilityDefault_NoHardwareAccel_CoercesStrandedTheater() { _state.WaveformEnabled = false; // only lava active _state.TheaterMode = true; _state.ApplyCapabilityDefault(hardwareAccelerated: false); // lava off → both off → strand Assert.That(_state.TheaterMode, Is.False); } }