Both were mine, from the scoreboard units: - ScoreboardLayout.FitLine null-forgave tileWidths on one expression and then iterated the field anyway; it now binds a non-null local once and uses that throughout. - the output resolver dereferenced _scoreboardPresenter, which is assigned in InitScoreboard — it is nullable by construction, so the resolver reads '?.' exactly like the other optional services. Clean build, 0 warnings, scoreboard tests green.
33 lines
1.4 KiB
C#
33 lines
1.4 KiB
C#
using System.Linq;
|
|
using Xunit;
|
|
using ytLive.Models;
|
|
using ytLive.ViewModels;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>The scoreboard is a LAYER, not a special widget: it must appear as a source type, carry
|
|
/// the full-width strip geometry, be resolvable on the output path like a chat box, and hook the
|
|
/// session lifecycle so its numbers are session-scoped.</summary>
|
|
public class ScoreboardLayerTests
|
|
{
|
|
[Fact]
|
|
public void The_scoreboard_is_a_real_source_with_strip_geometry_and_a_resolver()
|
|
{
|
|
Assert.Contains("Scoreboard", System.Enum.GetNames<SourceType>());
|
|
|
|
// Geometry: full width, a strip's height, bottom by default.
|
|
var sources = SourceAssert.Text("ViewModels", "MainViewModel.Sources.cs");
|
|
Assert.Contains("SourceType.Scoreboard ? 1920", sources);
|
|
Assert.Contains("SourceType.Scoreboard ? 56", sources);
|
|
Assert.Contains("SourceType.Scoreboard ? 1024", sources);
|
|
|
|
// The output resolver knows how to produce its frame (same shape as the chat box/alert box).
|
|
var resolver = SourceAssert.Text("ViewModels", "MainViewModel.cs");
|
|
Assert.Contains("Source { Type: SourceType.Scoreboard } => _scoreboardPresenter?.Frame", resolver);
|
|
|
|
// Session lifecycle: every start clears the tallies.
|
|
var ops = SourceAssert.Text("ViewModels", "MainViewModel.Streaming.Operations.cs");
|
|
Assert.Contains("StartScoreboardSession();", ops);
|
|
}
|
|
}
|