Build 730 (creator): - Left pill while recording: solid #2ECC71 dot pulsing 1.0 <-> 0.2 on the existing one-second timer, and "REC: hh:mm:ss" in Consolas so the digits cannot jitter the pill's width. Idle face unchanged. - Right pill while recording: a stop square and "Stop Recording"; clicking it runs the normal stop, which halts and resets the clock and returns the pair to "Local MP4" / "Start Recording". - The separate reality line (dot + REC/LIVE + elapsed + End) is now LIVE-ONLY: a recording would otherwise print the same three facts twice. RunningVisible is IsLive, and ShowOutputStage no longer hides itself for a recording (it must be on screen to carry the clock and the stop). Two rules collided and are now separated rather than merged: a creator's Stop on a recording restores the Local MP4 pair (build 730), while a pipeline FAILURE still clears both pills (the 2026-09-01 zombie rule). StopStream(afterFailure:) carries the difference, and the pump-failure path passes it. RealAppHost.Run was rethrowing with `throw failure;`, which destroyed the original stack — a failing assertion inside the action reported the wrapper's line instead of its own. ExceptionDispatchInfo now preserves it; that is how the two failures above were located in one pass instead of five. 680 pass (one audio-pipe instance flake under full-suite load, passes alone), 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the creator's replacement art, still awaiting their visual check.
134 lines
5.3 KiB
C#
134 lines
5.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using Xunit;
|
|
using ytLive.ViewModels;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>
|
|
/// The top bar's one decision: <c>[ Build Scenes ][ Record / Go Live ]</c>. Build Scenes ships
|
|
/// highlighted, and it <b>is</b> edit mode — the old EDIT toggle is gone, so "can I edit?" and "which
|
|
/// world am I in?" cannot disagree.
|
|
/// </summary>
|
|
[Collection("MainViewModelLayoutPath")]
|
|
public class TopBarWorldTests
|
|
{
|
|
[Fact]
|
|
public void The_output_stage_belongs_to_the_output_world_and_holds_one_output_at_a_time()
|
|
{
|
|
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-outputstage-{Guid.NewGuid():N}.db");
|
|
MainViewModel.LayoutPathOverride = db;
|
|
try
|
|
{
|
|
var vm = new MainViewModel();
|
|
|
|
// Nothing to arm while building scenes.
|
|
Assert.False(vm.ShowOutputStage);
|
|
|
|
vm.EnterOutputWorldCommand.Execute(null);
|
|
Assert.True(vm.IsOutputWorld);
|
|
Assert.True(vm.IsOffline);
|
|
Assert.False(vm.IsRecording);
|
|
Assert.True(vm.ShowOutputStage);
|
|
|
|
// Entering the output world starts with NOTHING chosen (creator, build 729); the pair is
|
|
// one axis, so arming either drops the other.
|
|
Assert.False(vm.RecordPillOn);
|
|
Assert.False(vm.OnAirPillOn);
|
|
vm.OnAirPillOn = true;
|
|
Assert.True(vm.OnAirPillOn);
|
|
Assert.False(vm.RecordPillOn);
|
|
vm.RecordPillOn = true;
|
|
Assert.False(vm.OnAirPillOn);
|
|
vm.RecordPillOn = false; // back to "nothing chosen" for the state checks below
|
|
|
|
// Nothing chosen yet: both output chips, no action.
|
|
Assert.True(vm.ShowOutputChoice);
|
|
Assert.False(vm.ShowYouTubeSelected);
|
|
Assert.False(vm.ShowLocalMp4Selected);
|
|
Assert.True(vm.YouTubeChipVisible);
|
|
Assert.True(vm.LocalMp4ChipVisible);
|
|
|
|
// YouTube chosen: its chip stays (red), two actions appear, MP4's chip goes away.
|
|
vm.SelectYouTubeCommand.Execute(null);
|
|
Assert.True(vm.ShowYouTubeSelected);
|
|
Assert.False(vm.ShowOutputChoice);
|
|
Assert.True(vm.YouTubeChipVisible);
|
|
Assert.False(vm.LocalMp4ChipVisible);
|
|
|
|
// Local MP4 chosen: it takes the left slot, one action, and Start/End swap on recording.
|
|
vm.SelectLocalMp4Command.Execute(null);
|
|
Assert.True(vm.ShowLocalMp4Selected);
|
|
Assert.False(vm.YouTubeChipVisible);
|
|
Assert.True(vm.LocalMp4ChipVisible);
|
|
Assert.True(vm.ShowStartRecordingAction);
|
|
Assert.False(vm.ShowEndRecordingAction);
|
|
Assert.Equal("Local MP4", vm.RecordChipText);
|
|
|
|
// Recording: the pill carries REC + the clock, the stage stays up, and the live reality
|
|
// line does not (its dot/elapsed/stop would be a second set of the same facts).
|
|
vm.IsRecording = true;
|
|
Assert.True(vm.ShowOutputStage);
|
|
Assert.False(vm.RunningVisible);
|
|
Assert.False(vm.ShowStartRecordingAction);
|
|
Assert.True(vm.ShowEndRecordingAction);
|
|
Assert.StartsWith("REC: ", vm.RecordChipText);
|
|
|
|
vm.IsRecording = false;
|
|
Assert.Equal("Local MP4", vm.RecordChipText);
|
|
|
|
// Neither action exists while building scenes.
|
|
vm.EnterBuildWorldCommand.Execute(null);
|
|
Assert.False(vm.ShowStartRecordingAction);
|
|
Assert.False(vm.ShowEndRecordingAction);
|
|
Assert.False(vm.YouTubeChipVisible);
|
|
Assert.False(vm.LocalMp4ChipVisible);
|
|
}
|
|
finally
|
|
{
|
|
MainViewModel.LayoutPathOverride = null;
|
|
try { File.Delete(db); } catch (IOException) { }
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_scenes_is_the_default_world_and_is_the_only_place_editing_lives()
|
|
{
|
|
var db = Path.Combine(Path.GetTempPath(), $"llamacasty-world-{Guid.NewGuid():N}.db");
|
|
MainViewModel.LayoutPathOverride = db;
|
|
try
|
|
{
|
|
var vm = new MainViewModel();
|
|
|
|
// Launch state: Build Scenes highlighted, editing on.
|
|
Assert.Equal(BarWorld.Build, vm.BarWorld);
|
|
Assert.True(vm.IsBuildWorld);
|
|
Assert.False(vm.IsOutputWorld);
|
|
Assert.True(vm.IsEditMode);
|
|
|
|
// Record / Go Live leaves edit mode — the output surface has nothing to drag.
|
|
vm.EnterOutputWorldCommand.Execute(null);
|
|
Assert.Equal(BarWorld.Output, vm.BarWorld);
|
|
Assert.True(vm.IsOutputWorld);
|
|
Assert.False(vm.IsBuildWorld);
|
|
Assert.False(vm.IsEditMode);
|
|
|
|
// Clicking the highlighted half is not a no-op: it re-enters that world, which is how a
|
|
// creator takes editing back during a session.
|
|
vm.EnterBuildWorldCommand.Execute(null);
|
|
Assert.True(vm.IsBuildWorld);
|
|
Assert.True(vm.IsEditMode);
|
|
|
|
// …and re-entering the world already in force still re-asserts its edit state.
|
|
vm.EditingActive = false;
|
|
vm.EnterBuildWorldCommand.Execute(null);
|
|
Assert.True(vm.IsEditMode);
|
|
}
|
|
finally
|
|
{
|
|
MainViewModel.LayoutPathOverride = null;
|
|
try { File.Delete(db); } catch (IOException) { }
|
|
}
|
|
}
|
|
}
|