Files
LlamaCasty/ytLive.Tests/DesktopLockTests.cs
T

152 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using Microsoft.Data.Sqlite;
using Xunit;
using ytLive.Services;
using ytLive.ViewModels;
namespace ytLive.Tests;
/// <summary>
/// The desktop-"lock" bug (2026-10-10): at launch the main window ACTIVES
/// (WPF default <c>ShowActivated = true</c>), and when the creator is already
/// playing an exclusive-fullscreen game, activating LlamaCasty makes Windows
/// drop that game to the taskbar; the app's 1920×1040 window then sits over the
/// whole work area, so the desktop looks locked and the game can't be reached
/// with the mouse. Fix: the window opens WITHOUT activating when a full-screen
/// game owns the foreground at launch — <c>ShowActivated = !FullscreenGameRunningNow</c>.
/// These tests pin that rule using the <c>FullScreenDetectorOverride</c> seam,
/// so no real Win32 foreground state is touched.
/// </summary>
[Collection("RealApp")]
public sealed class DesktopLockTests
{
private readonly RealAppHost _app;
public DesktopLockTests(RealAppHost app) => _app = app;
[Fact]
public void MainWindow_Does_Not_Steal_Focus_When_A_Fullscreen_Game_Is_Foreground()
{
_app.Run(() =>
{
using var _ = new TempLayout();
MainViewModel.FullScreenDetectorOverride = new StubFullScreenDetector(foregroundMonitor: 0);
try
{
var window = new MainWindow();
try
{
// The regression: a full-screen game already owns the screen, so
// the window must open WITHOUT activating to keep the game live.
Assert.False(window.ShowActivated,
"launching while a full-screen game owns the foreground must not steal focus");
}
finally
{
window.Close();
}
}
finally
{
MainViewModel.FullScreenDetectorOverride = null;
}
});
}
[Fact]
public void MainWindow_Normal_Launch_Still_Activates()
{
_app.Run(() =>
{
using var _ = new TempLayout();
MainViewModel.FullScreenDetectorOverride = new StubFullScreenDetector(foregroundMonitor: null);
try
{
var window = new MainWindow();
try
{
// No game running: behaviour is exactly as before the fix.
Assert.True(window.ShowActivated,
"a normal launch (no full-screen game) must keep the default activate-on-show");
}
finally
{
window.Close();
}
}
finally
{
MainViewModel.FullScreenDetectorOverride = null;
}
});
}
[Fact]
public void FullscreenGameRunningNow_Reflects_The_Foreground_Monitor()
{
_app.Run(() =>
{
MainViewModel.FullScreenDetectorOverride = new StubFullScreenDetector(foregroundMonitor: 0);
try
{
var gameVm = new MainViewModel();
Assert.True(gameVm.FullscreenGameRunningNow);
}
finally
{
MainViewModel.FullScreenDetectorOverride = null;
}
MainViewModel.FullScreenDetectorOverride = new StubFullScreenDetector(foregroundMonitor: null);
try
{
var idleVm = new MainViewModel();
Assert.False(idleVm.FullscreenGameRunningNow);
}
finally
{
MainViewModel.FullScreenDetectorOverride = null;
}
});
}
private sealed class TempLayout : IDisposable
{
private readonly string _path;
public TempLayout()
{
_path = Path.Combine(Path.GetTempPath(), $"ytLive-desktoplock-{Guid.NewGuid():N}.db");
using (var schema = new LayoutStore(_path)) { }
SqliteConnection.ClearAllPools();
MainViewModel.LayoutPathOverride = _path;
}
public void Dispose()
{
MainViewModel.LayoutPathOverride = null;
SqliteConnection.ClearAllPools();
try { File.Delete(_path); } catch { /* best-effort cleanup */ }
}
}
/// <summary>A deterministic stand-in for <see cref="Win32FullScreenDetector"/>:
/// claims a full-screen window owns the given foreground monitor (or none).</summary>
#pragma warning disable CS0067 // fullscreen-changed wiring is not part of this seam
private sealed class StubFullScreenDetector : IFullScreenDetector
{
private readonly int? _foregroundMonitor;
public StubFullScreenDetector(int? foregroundMonitor) => _foregroundMonitor = foregroundMonitor;
public int? GetForegroundFullScreenMonitorIndex() => _foregroundMonitor;
public int PrimaryMonitorIndex() => 0;
public IReadOnlyList<DisplayInfo> GetDisplays() => Array.Empty<DisplayInfo>();
public event Action<int?>? FullscreenMonitorChanged;
public void StartWatching() { }
public void StopWatching() { }
}
}