using Lumi.Companion.Abstractions; using Lumi.Companion.Core; using Lumi.Companion.Overlay; using Lumi.Companion.SongOverlay; static void Assert(bool condition, string message) { if (!condition) throw new InvalidOperationException(message); } var operation = new SingleFlightOperation(); var calls = 0; await operation.RunAsync(_ => { calls += 1; return Task.CompletedTask; }); Assert(calls == 1 && !operation.IsRunning, "A successful update-style operation did not reset."); await operation.RunAsync(_ => { calls += 1; return Task.CompletedTask; }); Assert(calls == 2 && !operation.IsRunning, "A no-update-style repeat did not execute."); try { await operation.RunAsync(_ => throw new InvalidOperationException("expected")); } catch (InvalidOperationException error) when (error.Message == "expected") { } await operation.RunAsync(_ => { calls += 1; return Task.CompletedTask; }); Assert(calls == 3 && !operation.IsRunning, "A failed operation left stale single-flight state."); using (var cancelled = new CancellationTokenSource()) { cancelled.Cancel(); try { await operation.RunAsync(token => Task.Delay(1, token), cancelled.Token); } catch (OperationCanceledException) { } } await operation.RunAsync(_ => { calls += 1; return Task.CompletedTask; }); Assert(calls == 4 && !operation.IsRunning, "A cancelled operation left stale single-flight state."); var release = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var concurrentCalls = 0; var first = operation.RunAsync(async _ => { concurrentCalls += 1; await release.Task; }); var second = operation.RunAsync(async _ => { concurrentCalls += 1; await Task.Yield(); }); Assert(operation.IsRunning && concurrentCalls == 1, "Rapid update checks started more than one operation."); release.SetResult(); await Task.WhenAll(first, second); Assert(concurrentCalls == 1 && !operation.IsRunning, "Rapid update checks did not share and reset the active operation."); var sanitized = CompanionLogSanitizer.Sanitize( "LumiDevice device-id.private-device-secret Authorization=private-value https://lumi.test/?token=private-token Cookie: session=private-cookie" ); Assert(!sanitized.Contains("private-value"), "Companion logs retained an authorization value."); Assert(!sanitized.Contains("private-cookie"), "Companion logs retained a cookie."); Assert(!sanitized.Contains("private-token"), "Companion logs retained a query token."); Assert(!sanitized.Contains("private-device-secret"), "Companion logs retained a paired-device credential."); Assert(CompanionLogSanitizer.NormalizeEvent("Update Failed!") == "update_failed", "Companion event normalization drifted."); Assert(CompanionLogSanitizer.LevelForEvent("update_failed") == "error", "Companion failed events must use the error level."); var anchored = new OverlayContainerSettings { Anchor = OverlayAnchor.Top, PaddingTop = 20, PaddingLeft = 40, PaddingRight = 60, PaddingBottom = 999, Width = 5000, Height = 300 }; var rect = AnchorLayout.Calculate(anchored, 1920, 1080); Assert(anchored.PaddingBottom == 0, "Top-side anchors must zero disabled bottom padding."); Assert(rect.Width == 1820 && rect.X == 40 && rect.Y == 20, "Physical-pixel layout did not clamp and center symmetrically inside valid padding."); var now = DateTimeOffset.UnixEpoch; var queueSettings = new OverlayContainerSettings { QueueLimit = 2, EntryDurationMs = 100, LifetimeMs = 200, ExitDurationMs = 100 }; var queue = new OverlayLifecycleQueue(() => queueSettings, () => now); queue.Add("one", "chat", "one"); queue.Add("two", "chat", "two"); queue.Add("three", "chat", "three"); Assert(queue.Items[0].State == OverlayItemState.Exiting && queue.Items.Count == 3, "Overflow must animate the oldest item out while keeping it rendered."); now += TimeSpan.FromMilliseconds(100); queue.Advance(); Assert(queue.Items.Count == 2 && queue.Items.All(item => item.State == OverlayItemState.Visible), "Entry and exit lifecycle transitions did not advance deterministically."); now += TimeSpan.FromMilliseconds(201); queue.Advance(); Assert(queue.Items.All(item => item.State == OverlayItemState.Exiting), "Lifetime must begin only after entry completes."); for (var index = 0; index < 20; index++) queue.Add($"burst-{index}", "chat", index); Assert(queue.Items.Count <= queueSettings.QueueLimit * 2, "Burst handling must keep the rendered lifecycle queue strictly bounded."); var settingsPath = Path.Combine(Path.GetTempPath(), $"lumi-overlay-test-{Guid.NewGuid():N}", "settings.json"); var overlayStore = new OverlaySettingsStore(settingsPath); await overlayStore.LoadAsync(); overlayStore.Current.Sources.Add(new OverlaySourceSetting { Id = "twitch:renamed-channel", Platform = "twitch", Label = "Previously configured channel", Selected = true }); overlayStore.Current.Unified.Anchor = OverlayAnchor.BottomRight; overlayStore.Current.Unified.PaddingTop = 42; await overlayStore.SaveAsync(overlayStore.Current); var restoredStore = new OverlaySettingsStore(settingsPath); await restoredStore.LoadAsync(); Assert(restoredStore.Current.Sources.Single().Selected && restoredStore.Current.Sources.Single().Label == "Previously configured channel", "Unavailable source selections were not retained by atomic local settings persistence."); Assert(restoredStore.Current.Unified.PaddingTop == 0, "Settings normalization must clear padding that is invalid for the selected anchor."); var unsavedDraft = restoredStore.Current.CreateCopy(); unsavedDraft.ChatPercentage = 60; unsavedDraft.Sources[0].Selected = false; Assert(restoredStore.Current.ChatPercentage != unsavedDraft.ChatPercentage && restoredStore.Current.Sources[0].Selected, "Editable overlay drafts must not mutate or get replaced by the last saved settings."); Assert(!CompanionPluginNavigation.UsesNestedNavigation( [new CompanionPluginPage("SongOverlay", "Overview & settings")]) && CompanionPluginNavigation.UsesNestedNavigation( [new CompanionPluginPage("Transcription", "Capture"), new CompanionPluginPage("Test", "Test")]), "Single-page plugins must be direct navigation entries while multi-page plugins remain grouped."); var defaultMediaSources = MediaSourceCatalog.Defaults(); Assert(defaultMediaSources.SetEquals(["wmp", "youtube", "spotify"]), "Song discovery defaults must enable only Windows Media Player, YouTube, and Spotify."); Assert(MediaSourceCatalog.Detect("Spotify.exe", "Track", "Artist", "") == "spotify" && MediaSourceCatalog.Detect("chrome.exe", "Track ยท SoundCloud", "Artist", "") == "soundcloud" && MediaSourceCatalog.Detect("chrome.exe", "Unlabelled browser track", "Artist", "") == "youtube" && MediaSourceCatalog.Detect("vlc.exe", "Track", "", "") == "vlc", "Windows media-session discovery did not classify desktop and browser sources independently."); Assert(MediaSourceCatalog.DetectEnabled("chrome.exe", "Unlabelled track", "Artist", "", new HashSet(["soundcloud"], StringComparer.Ordinal)) == "soundcloud", "An independently enabled browser source must remain discoverable when Windows withholds the tab origin."); Assert(new CaptureExclusionStatus(CaptureExclusionState.Unsupported, "unsupported").State == CaptureExclusionState.Unsupported, "Capture-exclusion unsupported state must remain distinct from failure."); var visibilitySettings = new LumiOverlaySettings { Visibility = OverlayVisibilityMode.WhenLive, Sources = [new() { Id = "twitch:lumi", Platform = "twitch", Label = "Lumi", Selected = true }] }; var liveSources = new[] { new OverlaySource("twitch:lumi", "twitch", "Lumi", true, true, "live") }; Assert(OverlayVisibilityPolicy.Automatic(visibilitySettings, liveSources), "A selected live Twitch source must activate When live."); Assert(!OverlayVisibilityPolicy.Automatic(visibilitySettings, [new OverlaySource("discord:guild:channel", "discord", "Discord", true, true, "connected")]), "Discord must never activate When live by itself."); Assert(OverlayVisibilityPolicy.Effective(visibilitySettings, [], true, null), "Configuration preview must override live-state visibility."); visibilitySettings.Enabled = false; Assert(OverlayVisibilityPolicy.Effective(visibilitySettings, [], true, null), "Configuration preview must remain available while the runtime is disabled."); visibilitySettings.Enabled = true; Assert(!OverlayVisibilityPolicy.Effective(visibilitySettings, liveSources, false, false), "A manual hidden override must win until automatic visibility is restored."); var automaticPresentation = OverlayVisibilityPresenter.Describe(visibilitySettings, true, false, null); Assert(automaticPresentation.Visible && automaticPresentation.Action == "Hide temporarily" && automaticPresentation.State.Contains("automatic", StringComparison.Ordinal), "Visibility presentation must separate the current automatic state from the button action."); var overridePresentation = OverlayVisibilityPresenter.Describe(visibilitySettings, true, false, false); Assert(!overridePresentation.Visible && overridePresentation.Action == "Restore automatic visibility", "Visibility presentation must identify a temporary override and explain how to leave it."); var previewPresentation = OverlayVisibilityPresenter.Describe(visibilitySettings, false, true, null); Assert(previewPresentation.Visible && !previewPresentation.CanToggle && previewPresentation.Action.Contains("End preview", StringComparison.Ordinal), "Preview-controlled visibility must be explicit and must not offer an ineffective override action."); var monitorFallback = OverlayMonitorPolicy.Resolve("missing", ["primary", "second"], "primary"); Assert(monitorFallback.MonitorId == "primary" && monitorFallback.UsedFallback && monitorFallback.Warning is not null, "Monitor loss must fall back without erasing the saved monitor or hiding the warning."); Assert(CompanionLiveReconnectPolicy.Delay(1) == TimeSpan.FromSeconds(2) && CompanionLiveReconnectPolicy.Delay(99) == TimeSpan.FromSeconds(30), "Live feed reconnect backoff must be bounded and deterministic."); Directory.Delete(Path.GetDirectoryName(settingsPath)!, true); Console.WriteLine("Companion core, native overlay layout/lifecycle, settings persistence, and capture-status regression checks passed.");