mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-06 03:52:10 -04:00
TestAssemblySetup set the unhandled-exception mode and a ThreadException handler from a module initializer. WinForms stores both per thread (ThreadContext/NativeWindow thread statics), so they applied only to the runner thread that loaded the assembly, never to the STA threads the tests run on. There, an exception escaping a window message or a BeginInvoke/SynchronizationContext callback still opened a modal ThreadExceptionDialog in an interactive session, or was silently ignored otherwise; the handler would also have logged and swallowed it. Replace the fifteen copied RunSta helpers with StaTestThread.Run, which selects UnhandledExceptionMode.ThrowException on each STA thread before it creates a window, so the exception propagates to the test body and fails the test. Each class keeps its own timeout and message; all threads are now background threads. Remove TestAssemblySetup. StaTestThreadTests covers a throwing BeginInvoke callback through StaTestThread (fails with the original exception) and, as a control, the default mode on a plain STA thread (the exception goes to the thread's handler and never reaches the test body). Cross-compiled on Linux; Windows execution is the windows-desktop CI job.
151 lines
5.2 KiB
C#
151 lines
5.2 KiB
C#
using System.Reflection;
|
|
using System.Windows.Forms;
|
|
using OpenNest.Forms;
|
|
|
|
namespace OpenNest.WinForms.Tests.Forms;
|
|
|
|
[Collection("Fill operation lifetime")]
|
|
public class EditNestFocusTraversalTests
|
|
{
|
|
[Fact]
|
|
public void NestedActiveContainersReturnTheirLeaf() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
var outer = new UserControl();
|
|
var inner = new UserControl();
|
|
var leaf = new TextBox();
|
|
form.Controls.Add(outer);
|
|
outer.Controls.Add(inner);
|
|
inner.Controls.Add(leaf);
|
|
inner.ActiveControl = leaf;
|
|
outer.ActiveControl = inner;
|
|
form.ActiveControl = outer;
|
|
|
|
Assert.Same(outer, form.ActiveControl);
|
|
Assert.Same(inner, outer.ActiveControl);
|
|
Assert.Same(leaf, inner.ActiveControl);
|
|
Assert.Same(leaf, GetFocusedControl(form));
|
|
});
|
|
|
|
[Fact]
|
|
public void OrdinaryActiveControlIsReturnedUnchanged() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
var leaf = new TextBox();
|
|
form.Controls.Add(leaf);
|
|
form.ActiveControl = leaf;
|
|
|
|
Assert.Same(leaf, form.ActiveControl);
|
|
Assert.Same(leaf, GetFocusedControl(form));
|
|
});
|
|
|
|
[Fact]
|
|
public void NoActiveControlReturnsTheForm() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
form.ActiveControl = null;
|
|
|
|
Assert.Null(form.ActiveControl);
|
|
Assert.Same(form, GetFocusedControl(form));
|
|
});
|
|
|
|
[Fact]
|
|
public void NestedContainerWithoutActiveControlIsReturned() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
var outer = new UserControl();
|
|
var inner = new UserControl();
|
|
form.Controls.Add(outer);
|
|
outer.Controls.Add(inner);
|
|
inner.ActiveControl = null;
|
|
outer.ActiveControl = inner;
|
|
form.ActiveControl = outer;
|
|
|
|
Assert.Same(outer, form.ActiveControl);
|
|
Assert.Same(inner, outer.ActiveControl);
|
|
Assert.Null(inner.ActiveControl);
|
|
Assert.Same(inner, GetFocusedControl(form));
|
|
});
|
|
|
|
[Fact]
|
|
public void PlateListRefreshRestoresTheOriginalNestedLeaf() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
form.Width = 1100;
|
|
form.Height = 600;
|
|
form.Show();
|
|
var outer = new UserControl();
|
|
var inner = new UserControl { Dock = DockStyle.Fill };
|
|
var original = new TextBox { Top = 10 };
|
|
var replacement = new TextBox { Top = 50 };
|
|
inner.Controls.AddRange(new Control[] { original, replacement });
|
|
outer.Controls.Add(inner);
|
|
form.ShowSidePanel(outer, "Focus regression");
|
|
var plates = PlateList(form);
|
|
SelectPlatesTab(form, plates); // Drawings is the default tab; the list can't take focus while hidden.
|
|
Assert.True(original.Focus());
|
|
Assert.True(form.ContainsFocus);
|
|
Assert.Same(original, inner.ActiveControl);
|
|
|
|
// Force a different leaf to become active during the real refresh. Merely
|
|
// focusing the old outer container would now restore this replacement.
|
|
var focusWasDisplaced = false;
|
|
plates.SelectedIndexChanged += (_, _) =>
|
|
{
|
|
Assert.True(replacement.Focus());
|
|
focusWasDisplaced = true;
|
|
};
|
|
|
|
form.UpdatePlateList();
|
|
|
|
Assert.True(focusWasDisplaced);
|
|
Assert.True(original.Focused);
|
|
Assert.False(replacement.Focused);
|
|
Assert.Same(original, GetFocusedControl(form));
|
|
Assert.Equal(form.Nest.Plates.Count, plates.Items.Count);
|
|
Assert.Equal(form.PlateManager.CurrentIndex, Assert.Single(plates.SelectedIndices.Cast<int>()));
|
|
});
|
|
|
|
[Fact]
|
|
public void PlateListRefreshKeepsFocusOnThePlateList() => RunSta(() =>
|
|
{
|
|
using var form = CreateForm();
|
|
form.Show();
|
|
var plates = PlateList(form);
|
|
SelectPlatesTab(form, plates); // Drawings is the default tab; the list can't take focus while hidden.
|
|
Assert.True(plates.Focus());
|
|
|
|
form.UpdatePlateList();
|
|
|
|
Assert.True(plates.Focused);
|
|
Assert.Same(plates, GetFocusedControl(form));
|
|
Assert.Equal(form.Nest.Plates.Count, plates.Items.Count);
|
|
});
|
|
|
|
private static EditNestForm CreateForm()
|
|
{
|
|
var form = new EditNestForm(new Nest("focus traversal"));
|
|
form.PlateView.SetOverlapAutoCheck(null);
|
|
return form;
|
|
}
|
|
|
|
private static Control GetFocusedControl(EditNestForm form)
|
|
{
|
|
var method = typeof(EditNestForm).GetMethod("GetFocusedControl", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
Assert.NotNull(method);
|
|
return Assert.IsAssignableFrom<Control>(method.Invoke(form, null));
|
|
}
|
|
|
|
private static ListView PlateList(EditNestForm form) =>
|
|
Assert.IsType<ListView>(Assert.Single(form.Controls.Find("platesListView", true)));
|
|
|
|
private static void SelectPlatesTab(EditNestForm form, ListView plates)
|
|
{
|
|
var tabControl = Assert.IsType<TabControl>(Assert.Single(form.Controls.Find("tabControl1", true)));
|
|
tabControl.SelectedTab = Assert.IsType<TabPage>(plates.Parent);
|
|
}
|
|
|
|
private static void RunSta(System.Action testBody) =>
|
|
StaTestThread.Run(testBody, TimeSpan.FromSeconds(60), "The focus traversal STA test did not complete.");
|
|
}
|