From 2ed589dc6bc8e0e1cc6f0345be023aae702239b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 25 Sep 2026 17:32:53 -0400 Subject: [PATCH 1/2] Serialize headless UI tests to avoid dispatcher races Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- tests/Devolutions.Terminal.UI.Tests/AssemblyInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/Devolutions.Terminal.UI.Tests/AssemblyInfo.cs diff --git a/tests/Devolutions.Terminal.UI.Tests/AssemblyInfo.cs b/tests/Devolutions.Terminal.UI.Tests/AssemblyInfo.cs new file mode 100644 index 0000000..44c147d --- /dev/null +++ b/tests/Devolutions.Terminal.UI.Tests/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using Xunit; + +// Headless Avalonia shares a dispatcher across test classes; concurrent sessions can initialize it on different threads. +[assembly: CollectionBehavior(DisableTestParallelization = true)] From 722715f43d187ea139713f9ad707c9fd0c8f92a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 25 Sep 2026 18:23:42 -0400 Subject: [PATCH 2/2] Avoid launching PTY sessions in window action UI tests Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../WindowActionRegistrationTests.cs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/Devolutions.Terminal.UI.Tests/WindowActionRegistrationTests.cs b/tests/Devolutions.Terminal.UI.Tests/WindowActionRegistrationTests.cs index d21c35c..c294134 100644 --- a/tests/Devolutions.Terminal.UI.Tests/WindowActionRegistrationTests.cs +++ b/tests/Devolutions.Terminal.UI.Tests/WindowActionRegistrationTests.cs @@ -11,10 +11,20 @@ namespace Devolutions.Terminal.UI.Tests; public sealed class WindowActionRegistrationTests { + private static readonly TerminalWindowActivation SettingsOnlyStartup = new( + null, + null, + null, + null, + TerminalWindowLaunchMode.Default, + [new ActionAndArgs( + ShortcutAction.OpenSettings, + new OpenSettingsArgs(SettingsTarget.SettingsUI))]); + [AvaloniaFact] public void NewTabDropdownUsesExistingProfileEntriesForCtrlClickLaunch() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); var menu = window.BuildNewTabMenu(); @@ -37,7 +47,7 @@ public void NewTabDropdownUsesExistingProfileEntriesForCtrlClickLaunch() [AvaloniaFact] public void RegistersMarkColorAndWindowDialogActions() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); var expected = new[] { @@ -69,7 +79,7 @@ public void RegistersMarkColorAndWindowDialogActions() [AvaloniaFact] public async Task OpenSettingsCreatesReusableSettingsTab() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); var first = await window.ActivateAsync(new TerminalWindowActivation( null, @@ -103,7 +113,7 @@ [new ActionAndArgs( [AvaloniaFact] public async Task ClosedSettingsTabIsNotRestoredAsATerminal() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); await window.ActivateAsync(new TerminalWindowActivation( null, @@ -143,7 +153,7 @@ [new ActionAndArgs( [AvaloniaFact] public async Task TerminalOnlyActionsAreUnavailableOnTheSettingsTab() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); await window.ActivateAsync(new TerminalWindowActivation( null, @@ -193,7 +203,7 @@ await window.ActivateAsync(new TerminalWindowActivation( [AvaloniaFact] public async Task ActivatingAfterTheLastTabClosesDoesNotThrow() { - var window = new MainWindow(); + var window = new MainWindow(0, string.Empty, SettingsOnlyStartup); await window.ActivateAsync(new TerminalWindowActivation( null, @@ -204,6 +214,7 @@ await window.ActivateAsync(new TerminalWindowActivation( [new ActionAndArgs( ShortcutAction.OpenSettings, new OpenSettingsArgs(SettingsTarget.SettingsUI))])); + await window.InitialActivation; Dispatcher.UIThread.RunJobs(); while (window.Tabs.Count > 0) @@ -239,7 +250,7 @@ public async Task RenameWindowUpdatesRoutingIdentityAndRejectsDuplicates() var window = new MainWindow( 7, "original", - null, + SettingsOnlyStartup, windowNameValidator: name => name != "duplicate"); var renamed = await window.ActivateAsync(new TerminalWindowActivation( @@ -272,7 +283,7 @@ public async Task RenameWindowRejectsPersistedWorkspaceCollision() using var temporary = new TemporaryDirectory(); var stateStore = new ApplicationStateStore(temporary.Path); stateStore.SaveWorkspace("existing", new WindowLayoutState()); - var window = new MainWindow(9, "original", null, stateStore: stateStore); + var window = new MainWindow(9, "original", SettingsOnlyStartup, stateStore: stateStore); var result = await window.ActivateAsync(new TerminalWindowActivation( null, @@ -320,7 +331,7 @@ public async Task WorkspaceActionsListAndRequestNamedWorkspace() var window = new MainWindow( 1, string.Empty, - null, + SettingsOnlyStartup, stateStore: store, workspaceRequested: name => requested = name);