using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Media;
using Xunit;
using ytLive.Services.Compositor;
namespace ytLive.Tests;
/// On-stream graphics cannot read application resources (the frame pump is not the UI
/// thread), so their colours live in . That duplication is safe only
/// if it is checked: this test proves every brand token there equals the brush in
/// Themes/Tokens.xaml, and that the renderers carry no colour literals of their own.
///
/// Without it, the broadcast picture would drift away from the UI one "quick fix" at a
/// time — the exact failure mode DESIGN.md exists to stop.
public class OutputPaletteTests
{
[Fact]
public void The_on_stream_palette_matches_the_theme_and_the_renderers_carry_no_literals()
{
var brushes = Tokens();
var mirrored = new (string Brush, Color Colour)[]
{
("Brush.Primary", OutputPalette.Live),
("Brush.PrimaryContainer", OutputPalette.LiveDark),
("Brush.Secondary", OutputPalette.Amber),
("Brush.Attention", OutputPalette.Attention),
("Brush.Positive", OutputPalette.Positive),
("Brush.Error", OutputPalette.Error),
("Brush.ErrorContainer", OutputPalette.ErrorSurface),
("Brush.Tertiary", OutputPalette.Tertiary),
("Brush.OnSurface", OutputPalette.TextPrimary),
("Brush.OnSurfaceVariant", OutputPalette.TextMuted),
("Brush.Background", OutputPalette.Background),
("Brush.SurfaceContainer", OutputPalette.Surface),
("Brush.SurfaceContainerHigh", OutputPalette.SurfaceHigh),
("Brush.Outline", OutputPalette.Outline),
};
foreach (var (brushKey, colour) in mirrored)
{
Assert.True(brushes.ContainsKey(brushKey), $"{brushKey} missing from Themes/Tokens.xaml");
Assert.True(brushes[brushKey] == colour,
$"{brushKey} is {brushes[brushKey]} in the theme but {colour} in OutputPalette — the " +
"broadcast picture and the UI have drifted apart");
}
// The renderers must not introduce colours of their own; the alert accents belong to
// OutputPalette too, so one file owns every on-stream value.
foreach (var file in new[]
{
"Services/AlertRenderer.cs", "Services/Compositor/AlertTickerRenderer.cs",
"Services/Compositor/BrandFlashPresenter.cs",
})
{
var text = SourceAssert.Text(file.Split('/'));
Assert.DoesNotContain("Color.FromRgb(0x", text);
Assert.DoesNotContain("\"Segoe UI\"", text);
}
}
private static Dictionary Tokens()
{
var text = SourceAssert.Text("Themes", "Tokens.xaml");
var result = new Dictionary(StringComparer.Ordinal);
foreach (Match m in Regex.Matches(text, "x:Key=\"(Brush\\.[^\"]+)\"\\s+Color=\"#([0-9a-fA-F]{6,8})\""))
{
var hex = m.Groups[2].Value;
if (hex.Length == 8) hex = hex[2..]; // drop the alpha the palette does not mirror
var r = int.Parse(hex[..2], NumberStyles.HexNumber, CultureInfo.InvariantCulture);
var g = int.Parse(hex[2..4], NumberStyles.HexNumber, CultureInfo.InvariantCulture);
var b = int.Parse(hex[4..6], NumberStyles.HexNumber, CultureInfo.InvariantCulture);
result[m.Groups[1].Value] = Color.FromRgb((byte)r, (byte)g, (byte)b);
}
return result;
}
}