Last inconsistency from the sweep: several VM-side state colours are still literals, and after the palette moved they disagreed with everything around them — the top bar's LIVE state would have painted the retired pink-red while the tile beside it used the standard red. Values aligned (tokenisation of these sites is its own unit — they are strings bound to Background/Foreground, so the fix is a VM->Brush refactor, not a find/replace): - live / on-air / recorder dot: #e94560 -> #ff5451 (the standard red) - error banner: #8f1f1f -> #93000a (error-container) - audio meter bands follow the doc's stated thresholds: nominal cyan #38bdf8, caution amber #ffb95f, peak #ff5451 — they were green/yellow/red, which contradicted DESIGN.md's own table GameMeterHonestyTests pinned the old band colours; its intent (the meter scales by volume) is unchanged, so the two colour expectations now name the new bands.
73 lines
2.5 KiB
C#
73 lines
2.5 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using Microsoft.Data.Sqlite;
|
||
using Xunit;
|
||
using ytLive.Services;
|
||
using ytLive.ViewModels;
|
||
|
||
namespace ytLive.Tests;
|
||
|
||
/// <summary>
|
||
/// Game-bar meter honesty (creator bug 2026-09-01): the WASAPI loopback tap is
|
||
/// PRE-endpoint-volume, so lowering the desktop slider left the meter pegged
|
||
/// yellow/red at 20% — the mic formula scales level × volume, the game clone
|
||
/// dropped the factor. The meter, the stream gain, and the headphones must all
|
||
/// follow the one knob.
|
||
/// </summary>
|
||
[Collection("RealApp")]
|
||
public sealed class GameMeterHonestyTests
|
||
{
|
||
private readonly RealAppHost _app;
|
||
|
||
public GameMeterHonestyTests(RealAppHost app) => _app = app;
|
||
|
||
[Fact]
|
||
public void Game_Meter_Scales_By_Volume()
|
||
{
|
||
_app.Run(Run);
|
||
}
|
||
|
||
private void Run()
|
||
{
|
||
var tempDb = Path.Combine(Path.GetTempPath(), $"ytLlive-gamemeter-{Guid.NewGuid():N}.db");
|
||
MainViewModel.LayoutPathOverride = tempDb;
|
||
var pushed = new List<double>();
|
||
MainViewModel.VolumePushOverride = v => pushed.Add(v);
|
||
var window = default(MainWindow);
|
||
try
|
||
{
|
||
using (var schema = new LayoutStore(tempDb)) { }
|
||
SqliteConnection.ClearAllPools();
|
||
|
||
window = new MainWindow();
|
||
var vm = (MainViewModel)window.DataContext;
|
||
window.Show();
|
||
|
||
// Hot desktop bed pegged at full linear level (ToDisplay ~= 1.0).
|
||
vm.GameAudioVolume = 1.0;
|
||
vm.GameAudioLevel = 1.0;
|
||
Assert.True(vm.GameMeterFillWidth > 230, "unity volume must keep the meter hot");
|
||
Assert.Equal("#ff5451", vm.GameMeterBrush); // DESIGN.md: peak band
|
||
|
||
// Drop the knob to 20% — the captured level does NOT change (the OS
|
||
// tap is pre-endpoint-volume); the meter must scale it anyway.
|
||
vm.GameAudioVolume = 0.2;
|
||
vm.GameAudioLevel = 1.0;
|
||
Assert.InRange(vm.GameMeterFillWidth, 40, 80);
|
||
Assert.Equal("#38bdf8", vm.GameMeterBrush); // DESIGN.md: nominal band
|
||
|
||
// And the push really reached the (faked) endpoint — one knob, honest.
|
||
Assert.Contains(0.2, pushed);
|
||
}
|
||
finally
|
||
{
|
||
MainViewModel.VolumePushOverride = null;
|
||
if (window != null) window.Close();
|
||
MainViewModel.LayoutPathOverride = null;
|
||
SqliteConnection.ClearAllPools();
|
||
try { File.Delete(tempDb); } catch { /* best-effort */ }
|
||
}
|
||
}
|
||
}
|