mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Fixes #4325. ApplicationImpl.Invoke is sometimes running on UI thread when Application.Top is null (#4339)
This commit is contained in:
@@ -17,9 +17,7 @@ public class ApplicationStressTests : TestsAllViews
|
||||
|
||||
private const int NUM_PASSES = 50;
|
||||
private const int NUM_INCREMENTS = 500;
|
||||
|
||||
// Use longer timeout when running under debugger to account for slower iterations
|
||||
private static readonly int POLL_MS = System.Diagnostics.Debugger.IsAttached ? 500 : 100;
|
||||
private const int POLL_MS = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Stress test for Application.Invoke to verify that invocations from background threads
|
||||
@@ -79,6 +77,13 @@ public class ApplicationStressTests : TestsAllViews
|
||||
while (_tbCounter != (j + 1) * numIncrements) // Wait for tbCounter to reach expected value
|
||||
{
|
||||
int tbNow = _tbCounter;
|
||||
|
||||
// Wait for Application.Top to be running to ensure timed events can be processed
|
||||
while (Application.Top is null || Application.Top is { Running: false })
|
||||
{
|
||||
Thread.Sleep (1);
|
||||
}
|
||||
|
||||
_wakeUp.Wait (pollMs);
|
||||
|
||||
if (_tbCounter != tbNow)
|
||||
|
||||
@@ -586,11 +586,10 @@ public class ApplicationTests
|
||||
{
|
||||
var top = new Toplevel ();
|
||||
RunState rs = Application.Begin (top);
|
||||
var firstIteration = false;
|
||||
|
||||
var actionCalled = 0;
|
||||
Application.Invoke (() => { actionCalled++; });
|
||||
Application.RunIteration (ref rs, firstIteration);
|
||||
ApplicationImpl.Instance.TimedEvents!.RunTimers ();
|
||||
Assert.Equal (1, actionCalled);
|
||||
top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
@@ -7,6 +8,14 @@ namespace UnitTests.ApplicationTests;
|
||||
/// <summary>Tests MainLoop using the FakeMainLoop.</summary>
|
||||
public class MainLoopTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public MainLoopTests (ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
ConsoleDriver.RunningUnitTests = true;
|
||||
}
|
||||
|
||||
private static Button btn;
|
||||
private static string cancel;
|
||||
private static string clickMe;
|
||||
@@ -708,6 +717,95 @@ public class MainLoopTests
|
||||
Assert.Equal (10, functionCalled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("fake")]
|
||||
[InlineData ("windows")]
|
||||
[InlineData ("dotnet")]
|
||||
[InlineData ("unix")]
|
||||
public void Application_Invoke_Run_TimedEvents (string driverName)
|
||||
{
|
||||
// Arrange
|
||||
Application.Init (driverName: driverName);
|
||||
var functionCalled = 0;
|
||||
var stopwatch = new Stopwatch ();
|
||||
|
||||
// Act
|
||||
Application.Invoke (() =>
|
||||
{
|
||||
// Stop the stopwatch *after* the function is called.
|
||||
functionCalled++;
|
||||
stopwatch.Stop ();
|
||||
Application.RequestStop ();
|
||||
});
|
||||
|
||||
// Start timing just before running the application loop.
|
||||
stopwatch.Start ();
|
||||
Application.Run ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (Application.Top);
|
||||
Application.Top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
Assert.Equal (1, functionCalled);
|
||||
|
||||
// Output the elapsed time for this test case.
|
||||
// ReSharper disable once Xunit.XunitTestWithConsoleOutput
|
||||
// ReSharper disable once LocalizableElement
|
||||
Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
|
||||
|
||||
// Output elapsed duration to xUnit's test output
|
||||
_output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("fake")]
|
||||
[InlineData ("windows")]
|
||||
[InlineData ("dotnet")]
|
||||
[InlineData ("unix")]
|
||||
public void Application_AddTimeout_Run_TimedEvents (string driverName)
|
||||
{
|
||||
// Arrange
|
||||
Application.Init (driverName: driverName);
|
||||
var functionCalled = 0;
|
||||
var stopwatch = new Stopwatch ();
|
||||
|
||||
// Act
|
||||
bool Function ()
|
||||
{
|
||||
functionCalled++;
|
||||
|
||||
if (functionCalled == 10 && Application.Top is { Running: true })
|
||||
{
|
||||
stopwatch.Stop ();
|
||||
Application.RequestStop ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Application.AddTimeout (TimeSpan.FromMilliseconds (1), Function);
|
||||
|
||||
// Start timing just before running the application loop.
|
||||
stopwatch.Start ();
|
||||
Application.Run ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (Application.Top);
|
||||
Application.Top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
Assert.Equal (10, functionCalled);
|
||||
|
||||
// Output the elapsed time for this test case.
|
||||
// ReSharper disable once Xunit.XunitTestWithConsoleOutput
|
||||
// ReSharper disable once LocalizableElement
|
||||
Console.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
|
||||
|
||||
// Output elapsed duration to xUnit's test output
|
||||
_output.WriteLine ($"[{driverName}] Duration: {stopwatch.Elapsed.TotalMilliseconds:F2} ms");
|
||||
}
|
||||
|
||||
public static IEnumerable<object []> TestAddTimeout
|
||||
{
|
||||
get
|
||||
|
||||
Reference in New Issue
Block a user