diff --git a/Examples/UICatalog/Scenarios/Arrangement.cs b/Examples/UICatalog/Scenarios/Arrangement.cs index 867e36f1d..7b261db6b 100644 --- a/Examples/UICatalog/Scenarios/Arrangement.cs +++ b/Examples/UICatalog/Scenarios/Arrangement.cs @@ -99,7 +99,6 @@ public class Arrangement : Scenario progressBar.Fraction += 0.01f; - Application.Wakeup (); progressBar.SetNeedsDraw (); }; diff --git a/Examples/UICatalog/Scenarios/Clipping.cs b/Examples/UICatalog/Scenarios/Clipping.cs index 986a466f2..2cebee301 100644 --- a/Examples/UICatalog/Scenarios/Clipping.cs +++ b/Examples/UICatalog/Scenarios/Clipping.cs @@ -100,7 +100,6 @@ public class Clipping : Scenario { tiledProgressBar1.Pulse (); tiledProgressBar2.Pulse (); - Application.Wakeup (); }; progressTimer.Start (); diff --git a/Examples/UICatalog/Scenarios/ProgressBarStyles.cs b/Examples/UICatalog/Scenarios/ProgressBarStyles.cs index 638303466..a31969c87 100644 --- a/Examples/UICatalog/Scenarios/ProgressBarStyles.cs +++ b/Examples/UICatalog/Scenarios/ProgressBarStyles.cs @@ -198,7 +198,6 @@ public class ProgressBarStyles : Scenario button.Enabled = true; } - Application.Wakeup (); }, null, 0, @@ -282,7 +281,6 @@ public class ProgressBarStyles : Scenario marqueesBlocksPB.Text = marqueesContinuousPB.Text = DateTime.Now.TimeOfDay.ToString (); marqueesBlocksPB.Pulse (); marqueesContinuousPB.Pulse (); - Application.Wakeup (); }, null, 0, diff --git a/Examples/UICatalog/Scenarios/Shortcuts.cs b/Examples/UICatalog/Scenarios/Shortcuts.cs index c96dccd7c..fc9843b3c 100644 --- a/Examples/UICatalog/Scenarios/Shortcuts.cs +++ b/Examples/UICatalog/Scenarios/Shortcuts.cs @@ -384,7 +384,6 @@ public class Shortcuts : Scenario pb.Fraction += 0.01f; - Application.Wakeup (); pb.SetNeedsDraw (); } diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs index c69ad06f6..4aa915d0f 100644 --- a/Terminal.Gui/App/Application.Lifecycle.cs +++ b/Terminal.Gui/App/Application.Lifecycle.cs @@ -131,7 +131,7 @@ public static partial class Application // Lifecycle (Init/Shutdown) try { - MainLoop = Driver!.Init (); + Driver!.Init (); SubscribeDriverEvents (); } catch (InvalidOperationException ex) diff --git a/Terminal.Gui/App/Application.Run.cs b/Terminal.Gui/App/Application.Run.cs index e136602e6..fc6920ba9 100644 --- a/Terminal.Gui/App/Application.Run.cs +++ b/Terminal.Gui/App/Application.Run.cs @@ -372,12 +372,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E /// the action to be invoked on the main processing thread. public static void Invoke (Action action) { ApplicationImpl.Instance.Invoke (action); } - // TODO: Determine if this is really needed. The only code that calls WakeUp I can find - // is ProgressBarStyles, and it's not clear it needs to. - - /// Wakes up the running application that might be waiting on input. - public static void Wakeup () { MainLoop?.Wakeup (); } - /// /// Causes any Toplevels that need layout to be laid out. Then draws any Toplevels that need display. Only Views that /// need to be laid out (see ) will be laid out. @@ -396,10 +390,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E /// See also public static event EventHandler? Iteration; - /// The driver for the application - /// The main loop. - internal static MainLoop? MainLoop { get; set; } - /// /// Set to true to cause to be called after the first iteration. Set to false (the default) to /// cause the application to continue running until Application.RequestStop () is called. @@ -417,11 +407,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E for (state.Toplevel.Running = true; state.Toplevel?.Running == true;) { - if (MainLoop is { }) - { - MainLoop.Running = true; - } - if (EndAfterFirstIteration && !firstIteration) { return; @@ -430,11 +415,6 @@ public static partial class Application // Run (Begin -> Run -> Layout/Draw -> E firstIteration = RunIteration (ref state, firstIteration); } - if (MainLoop is { }) - { - MainLoop.Running = false; - } - // Run one last iteration to consume any outstanding input events from Driver // This is important for remaining OnKeyUp events. RunIteration (ref state, firstIteration); diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs index 718c24772..944c07918 100644 --- a/Terminal.Gui/App/Application.cs +++ b/Terminal.Gui/App/Application.cs @@ -216,9 +216,6 @@ public static partial class Application Top = null; _cachedRunStateToplevel = null; - // MainLoop stuff - MainLoop?.Dispose (); - MainLoop = null; MainThreadId = -1; Iteration = null; EndAfterFirstIteration = false; diff --git a/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs b/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs deleted file mode 100644 index 197535654..000000000 --- a/Terminal.Gui/App/MainLoop/IMainLoopDriver.cs +++ /dev/null @@ -1,24 +0,0 @@ -#nullable enable -namespace Terminal.Gui.App; - -/// Interface to create a platform specific driver. -internal interface IMainLoopDriver -{ - /// Must report whether there are any events pending, or even block waiting for events. - /// , if there were pending events, otherwise. - bool EventsPending (); - - /// The iteration function. - void Iteration (); - - /// Initializes the , gets the calling main loop for the initialization. - /// Call to release resources. - /// Main loop. - void Setup (MainLoop mainLoop); - - /// Tears down the driver. Releases resources created in . - void TearDown (); - - /// Wakes up the that might be waiting on input, must be thread safe. - void Wakeup (); -} diff --git a/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs b/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs deleted file mode 100644 index 995baa3b7..000000000 --- a/Terminal.Gui/App/MainLoop/LegacyMainLoopDriver.cs +++ /dev/null @@ -1,122 +0,0 @@ -#nullable enable -// -// LegacyMainLoopDriver.cs: IMainLoopDriver and MainLoop for legacy v1 driver based applications -// -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// - -using System.Collections.ObjectModel; - -namespace Terminal.Gui.App; - -/// -/// The main event loop of legacy v1 driver based applications. -/// -/// -/// -/// This class is provided for backward compatibility with the legacy FakeDriver implementation. -/// New code should use the modern architecture instead. -/// -/// -/// Monitoring of file descriptors is only available on Unix, there does not seem to be a way of supporting this -/// on Windows. -/// -/// -[Obsolete ("This class is for legacy FakeDriver compatibility only. Use ApplicationMainLoop for new code.")] -public class MainLoop : IDisposable -{ - /// - /// Gets the class responsible for handling timeouts - /// - public ITimedEvents TimedEvents { get; } = new TimedEvents(); - - /// Creates a new MainLoop. - /// Use to release resources. - /// - /// The instance (one of the implementations FakeMainLoop, UnixMainLoop, - /// NetMainLoop or WindowsMainLoop). - /// - internal MainLoop (IMainLoopDriver driver) - { - MainLoopDriver = driver; - driver.Setup (this); - } - - - /// The current in use. - /// The main loop driver. - internal IMainLoopDriver? MainLoopDriver { get; private set; } - - /// Used for unit tests. - internal bool Running { get; set; } - - - /// - public void Dispose () - { - GC.SuppressFinalize (this); - Stop (); - Running = false; - MainLoopDriver?.TearDown (); - MainLoopDriver = null; - } - - - /// Determines whether there are pending events to be processed. - /// - /// You can use this method if you want to probe if events are pending. Typically used if you need to flush the - /// input queue while still running some of your own code in your main thread. - /// - internal bool EventsPending () { return MainLoopDriver!.EventsPending (); } - - - /// Runs the . Used only for unit tests. - internal void Run () - { - bool prev = Running; - Running = true; - - while (Running) - { - EventsPending (); - RunIteration (); - } - - Running = prev; - } - - /// Runs one iteration of timers and file watches - /// - /// Use this to process all pending events (timers handlers and file watches). - /// - /// while (main.EventsPending ()) RunIteration (); - /// - /// - internal void RunIteration () - { - RunAnsiScheduler (); - - MainLoopDriver?.Iteration (); - - TimedEvents.RunTimers (); - } - - private void RunAnsiScheduler () - { - Application.Driver?.GetRequestScheduler ().RunSchedule (); - } - - /// Stops the main loop driver and calls . Used only for unit tests. - internal void Stop () - { - Running = false; - Wakeup (); - } - - - /// Wakes up the that might be waiting on input. - internal void Wakeup () { MainLoopDriver?.Wakeup (); } - - -} diff --git a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs index 2f188b5e0..f67f5ec81 100644 --- a/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs +++ b/Terminal.Gui/App/MainLoop/MainLoopSyncContext.cs @@ -10,23 +10,8 @@ internal sealed class MainLoopSyncContext : SynchronizationContext public override void Post (SendOrPostCallback d, object state) { - // Queue the task - if (ApplicationImpl.Instance.IsLegacy) - { - Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero, - () => - { - d (state); - - return false; - } - ); - Application.MainLoop?.Wakeup (); - } - else - { - ApplicationImpl.Instance.Invoke (() => { d (state); }); - } + // Queue the task using the modern architecture + ApplicationImpl.Instance.Invoke (() => { d (state); }); } //_mainLoop.Driver.Wakeup (); diff --git a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs index 27697bb4b..97dbc933e 100644 --- a/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs +++ b/Terminal.Gui/Drivers/AnsiHandling/EscSeqUtils/EscSeqUtils.cs @@ -931,7 +931,7 @@ public static class EscSeqUtils _isButtonClicked = false; _isButtonDoubleClicked = true; - Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero, + ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero, () => { Task.Run (async () => await ProcessButtonDoubleClickedAsync ()); @@ -970,7 +970,7 @@ public static class EscSeqUtils mouseFlags.Add (GetButtonClicked (buttonState)); _isButtonClicked = true; - Application.MainLoop?.TimedEvents.Add (TimeSpan.Zero, + ApplicationImpl.Instance.TimedEvents?.Add (TimeSpan.Zero, () => { Task.Run (async () => await ProcessButtonClickedAsync ()); diff --git a/Terminal.Gui/Drivers/ConsoleDriver.cs b/Terminal.Gui/Drivers/ConsoleDriver.cs index 939000ecc..534adefb7 100644 --- a/Terminal.Gui/Drivers/ConsoleDriver.cs +++ b/Terminal.Gui/Drivers/ConsoleDriver.cs @@ -550,8 +550,7 @@ public abstract class ConsoleDriver : IConsoleDriver #region Setup & Teardown /// Initializes the driver - /// Returns an instance of using the for the driver. - public abstract MainLoop Init (); + public abstract void Init (); /// Ends the execution of the console driver. public abstract void End (); diff --git a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs index d306b06d7..ef12ec04e 100644 --- a/Terminal.Gui/Drivers/ConsoleDriverFacade.cs +++ b/Terminal.Gui/Drivers/ConsoleDriverFacade.cs @@ -365,7 +365,7 @@ internal class ConsoleDriverFacade : IConsoleDriver, IConsoleDriverFacade /// Initializes the driver /// Returns an instance of using the for the driver. - public MainLoop Init () { throw new NotSupportedException (); } + public void Init () { throw new NotSupportedException (); } /// Ends the execution of the console driver. public void End () diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs index 5a4e13f50..916760c97 100644 --- a/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/Drivers/FakeDriver/FakeDriver.cs @@ -91,9 +91,7 @@ public class FakeDriver : ConsoleDriver FakeConsole.Clear (); } - private FakeMainLoop? _mainLoopDriver; - - public override MainLoop Init () + public override void Init () { FakeConsole.MockKeyPresses.Clear (); @@ -102,12 +100,6 @@ public class FakeDriver : ConsoleDriver FakeConsole.Clear (); ResizeScreen (); CurrentAttribute = new Attribute (Color.White, Color.Black); - //ClearContents (); - - _mainLoopDriver = new FakeMainLoop (this); - _mainLoopDriver.MockKeyPressed = MockKeyPressedHandler; - - return new MainLoop (_mainLoopDriver); } public override bool UpdateScreen () @@ -346,24 +338,6 @@ public class FakeDriver : ConsoleDriver private CursorVisibility _savedCursorVisibility; - private void MockKeyPressedHandler (ConsoleKeyInfo consoleKeyInfo) - { - if (consoleKeyInfo.Key == ConsoleKey.Packet) - { - consoleKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo); - } - - KeyCode map = MapKey (consoleKeyInfo); - - if (IsValidInput (map, out map)) - { - OnKeyDown (new (map)); - OnKeyUp (new (map)); - } - - //OnKeyPressed (new KeyEventArgs (map)); - } - /// public override bool GetCursorVisibility (out CursorVisibility visibility) { diff --git a/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs deleted file mode 100644 index d9d918031..000000000 --- a/Terminal.Gui/Drivers/FakeDriver/FakeMainLoop.cs +++ /dev/null @@ -1,38 +0,0 @@ - -namespace Terminal.Gui.Drivers; - -internal class FakeMainLoop : IMainLoopDriver -{ - public Action MockKeyPressed; - - public FakeMainLoop (IConsoleDriver consoleDriver = null) - { - // No implementation needed for FakeMainLoop - } - - public void Setup (MainLoop mainLoop) - { - // No implementation needed for FakeMainLoop - } - - public void Wakeup () - { - // No implementation needed for FakeMainLoop - } - - public bool EventsPending () - { - // Always return true for FakeMainLoop - return true; - } - - public void Iteration () - { - if (FakeConsole.MockKeyPresses.Count > 0) - { - MockKeyPressed?.Invoke (FakeConsole.MockKeyPresses.Pop ()); - } - } - - public void TearDown () { } -} diff --git a/Terminal.Gui/Drivers/IConsoleDriver.cs b/Terminal.Gui/Drivers/IConsoleDriver.cs index 1c42be49b..a28671a4e 100644 --- a/Terminal.Gui/Drivers/IConsoleDriver.cs +++ b/Terminal.Gui/Drivers/IConsoleDriver.cs @@ -214,8 +214,7 @@ public interface IConsoleDriver void UpdateCursor (); /// Initializes the driver - /// Returns an instance of using the for the driver. - MainLoop Init (); + void Init (); /// Ends the execution of the console driver. void End (); diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs index 3816db679..8de8b43b5 100644 --- a/Tests/UnitTests/Application/ApplicationTests.cs +++ b/Tests/UnitTests/Application/ApplicationTests.cs @@ -159,7 +159,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -316,7 +315,6 @@ public class ApplicationTests // Don't check Application.Force16Colors //Assert.False (Application.Force16Colors); Assert.Null (Application.Driver); - Assert.Null (Application.MainLoop); Assert.False (Application.EndAfterFirstIteration); // Commented out because if CM changed the defaults, those changes should @@ -472,7 +470,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -486,7 +483,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -522,14 +518,12 @@ public class ApplicationTests Application.End (runstate); Assert.NotNull (Application.Top); - Assert.NotNull (Application.MainLoop); Assert.NotNull (Application.Driver); topLevel.Dispose (); Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -676,7 +670,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -700,7 +693,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -734,7 +726,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -752,7 +743,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -774,7 +764,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); a.After (null); @@ -793,7 +782,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -812,7 +800,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -829,7 +816,6 @@ public class ApplicationTests Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -849,7 +835,6 @@ public class ApplicationTests top.Dispose (); Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -872,7 +857,6 @@ public class ApplicationTests top.Dispose (); Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } @@ -892,7 +876,6 @@ public class ApplicationTests top.Dispose (); Application.Shutdown (); Assert.Null (Application.Top); - Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } diff --git a/Tests/UnitTests/Application/MainLoopTests.cs b/Tests/UnitTests/Application/MainLoopTests.cs deleted file mode 100644 index 0a04f3c4f..000000000 --- a/Tests/UnitTests/Application/MainLoopTests.cs +++ /dev/null @@ -1,940 +0,0 @@ -using System.Diagnostics; -using Xunit.Abstractions; - -// Alias Console to MockConsole so we don't accidentally use Console - -namespace UnitTests.ApplicationTests; - -/// Tests MainLoop using the FakeMainLoop. -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; - private static int four; - private static int one; - private static string pewPew; - private static bool taskCompleted; - - // TODO: EventsPending tests - // - wait = true - // - wait = false - - // TODO: Add IMainLoop tests - private static int three; - private static int total; - private static int two; - private static int zero; - - // See Also ConsoleDRivers/MainLoopDriverTests.cs for tests of the MainLoopDriver - - // Idle Handler tests - [Fact] - public void AddTimeout_Adds_And_Removes () - { - var ml = new MainLoop (new FakeMainLoop ()); - - Func fnTrue = () => true; - Func fnFalse = () => false; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fnTrue); - var b = ml.TimedEvents.Add (TimeSpan.Zero, fnFalse); - - Assert.Equal (2, ml.TimedEvents.Timeouts.Count); - Assert.Equal (fnTrue, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback); - Assert.NotEqual (fnFalse, ml.TimedEvents.Timeouts.ElementAt (0).Value.Callback); - - Assert.True (ml.TimedEvents.Remove (a)); - Assert.Single (ml.TimedEvents.Timeouts); - - // BUGBUG: This doesn't throw or indicate an error. Ideally RemoveIdle would either - // throw an exception in this case, or return an error. - // No. Only need to return a boolean. - Assert.False (ml.TimedEvents.Remove (a)); - - Assert.True (ml.TimedEvents.Remove (b)); - - // BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either - // throw an exception in this case, or return an error. - // No. Only need to return a boolean. - Assert.False (ml.TimedEvents.Remove(b)); - } - - [Fact] - public void AddTimeout_Function_GetsCalled_OnIteration () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn = () => - { - functionCalled++; - - return true; - }; - - ml.TimedEvents.Add (TimeSpan.Zero, fn); - ml.RunIteration (); - Assert.Equal (1, functionCalled); - } - - [Fact] - public void AddTimeout_Twice_Returns_False_Called_Twice () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn1 = () => - { - functionCalled++; - - return false; - }; - - // Force stop if 10 iterations - var stopCount = 0; - - Func fnStop = () => - { - stopCount++; - - if (stopCount == 10) - { - ml.Stop (); - } - - return true; - }; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop); - var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1); - ml.Run (); - - Assert.True (ml.TimedEvents.Remove(a)); - Assert.False (ml.TimedEvents.Remove (a)); - - // Cannot remove b because it returned false i.e. auto removes itself - Assert.False (ml.TimedEvents.Remove (b)); - - Assert.Equal (1, functionCalled); - } - - [Fact] - public void AddTimeoutTwice_Function_CalledTwice () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn = () => - { - functionCalled++; - - return true; - }; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fn); - var b = ml.TimedEvents.Add (TimeSpan.Zero, fn); - ml.RunIteration (); - Assert.Equal (2, functionCalled); - Assert.Equal (2, ml.TimedEvents.Timeouts.Count); - - functionCalled = 0; - Assert.True (ml.TimedEvents.Remove (a)); - Assert.Single (ml.TimedEvents.Timeouts); - ml.RunIteration (); - Assert.Equal (1, functionCalled); - - functionCalled = 0; - Assert.True (ml.TimedEvents.Remove (b)); - Assert.Empty (ml.TimedEvents.Timeouts); - ml.RunIteration (); - Assert.Equal (0, functionCalled); - Assert.False (ml.TimedEvents.Remove (b)); - } - - [Fact] - public void AddThenRemoveIdle_Function_NotCalled () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn = () => - { - functionCalled++; - - return true; - }; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fn); - Assert.True (ml.TimedEvents.Remove (a)); - ml.RunIteration (); - Assert.Equal (0, functionCalled); - } - - // Timeout Handler Tests - [Fact] - public void AddTimer_Adds_Removes_NoFaults () - { - var ml = new MainLoop (new FakeMainLoop ()); - var ms = 100; - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - return true; - }; - - object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback); - - Assert.True (ml.TimedEvents.Remove (token)); - - // BUGBUG: This should probably fault? - // Must return a boolean. - Assert.False (ml.TimedEvents.Remove (token)); - } - - [Fact] - public async Task AddTimer_Duplicate_Keys_Not_Allowed () - { - var ml = new MainLoop (new FakeMainLoop ()); - const int ms = 100; - object token1 = null, token2 = null; - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - if (callbackCount == 2) - { - ml.Stop (); - } - - return true; - }; - - var task1 = new Task (() => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback)); - var task2 = new Task (() => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback)); - Assert.Null (token1); - Assert.Null (token2); - task1.Start (); - task2.Start (); - ml.Run (); - Assert.NotNull (token1); - Assert.NotNull (token2); - await Task.WhenAll (task1, task2); - Assert.True (ml.TimedEvents.Remove (token1)); - Assert.True (ml.TimedEvents.Remove (token2)); - - Assert.Equal (2, callbackCount); - } - - // Timeout Handler Tests - [Fact] - public void AddTimer_EventFired () - { - var ml = new MainLoop (new FakeMainLoop ()); - var ms = 100; - - // Use Stopwatch ticks since TimedEvents now uses Stopwatch.GetTimestamp internally - long originTicks = Stopwatch.GetTimestamp () * TimeSpan.TicksPerSecond / Stopwatch.Frequency; - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - return true; - }; - - object sender = null; - TimeoutEventArgs args = null; - - ml.TimedEvents.Added += (s, e) => - { - sender = s; - args = e; - }; - - object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback); - - Assert.Same (ml.TimedEvents, sender); - Assert.NotNull (args.Timeout); - Assert.True (args.Ticks - originTicks >= 100 * TimeSpan.TicksPerMillisecond); - } - - [Fact] - public void AddTimer_In_Parallel_Wont_Throw () - { - var ml = new MainLoop (new FakeMainLoop ()); - const int ms = 100; - object token1 = null, token2 = null; - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - if (callbackCount == 2) - { - ml.Stop (); - } - - return true; - }; - - Parallel.Invoke ( - () => token1 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback), - () => token2 = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback) - ); - ml.Run (); - Assert.NotNull (token1); - Assert.NotNull (token2); - Assert.True (ml.TimedEvents.Remove (token1)); - Assert.True (ml.TimedEvents.Remove (token2)); - - Assert.Equal (2, callbackCount); - } - - [Fact] - public void AddTimer_Remove_NotCalled () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - - // Force stop if 10 iterations - var stopCount = 0; - - Func fnStop = () => - { - stopCount++; - - if (stopCount == 10) - { - ml.Stop (); - } - - return true; - }; - ml.TimedEvents.Add (TimeSpan.Zero, fnStop); - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - return true; - }; - - object token = ml.TimedEvents.Add (ms, callback); - Assert.True (ml.TimedEvents.Remove (token)); - ml.Run (); - Assert.Equal (0, callbackCount); - } - - [Fact] - public void AddTimer_ReturnFalse_StopsBeingCalled () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - - // Force stop if 10 iterations - var stopCount = 0; - - Func fnStop = () => - { - Thread.Sleep (10); // Sleep to enable timer to fire - stopCount++; - - if (stopCount == 10) - { - ml.Stop (); - } - - return true; - }; - ml.TimedEvents.Add (TimeSpan.Zero, fnStop); - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - return false; - }; - - object token = ml.TimedEvents.Add (ms, callback); - ml.Run (); - Assert.Equal (1, callbackCount); - Assert.Equal (10, stopCount); - Assert.False (ml.TimedEvents.Remove (token)); - } - - [Fact] - public void AddTimer_Run_Called () - { - var ml = new MainLoop (new FakeMainLoop ()); - var ms = 100; - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - ml.Stop (); - - return true; - }; - - object token = ml.TimedEvents.Add (TimeSpan.FromMilliseconds (ms), callback); - ml.Run (); - Assert.True (ml.TimedEvents.Remove (token)); - - Assert.Equal (1, callbackCount); - } - - [Fact] - public void AddTimer_Run_CalledAtApproximatelyRightTime () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - var watch = new Stopwatch (); - - var callbackCount = 0; - - Func callback = () => - { - watch.Stop (); - callbackCount++; - ml.Stop (); - - return true; - }; - - object token = ml.TimedEvents.Add (ms, callback); - watch.Start (); - ml.Run (); - - // +/- 100ms should be good enuf - // https://github.com/xunit/assert.xunit/pull/25 - Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100)); - - Assert.True (ml.TimedEvents.Remove (token)); - Assert.Equal (1, callbackCount); - } - - [Fact] - public void AddTimer_Run_CalledTwiceApproximatelyRightTime () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - var watch = new Stopwatch (); - - var callbackCount = 0; - - Func callback = () => - { - callbackCount++; - - if (callbackCount == 2) - { - watch.Stop (); - ml.Stop (); - } - - return true; - }; - - object token = ml.TimedEvents.Add (ms, callback); - watch.Start (); - ml.Run (); - - // +/- 100ms should be good enuf - // https://github.com/xunit/assert.xunit/pull/25 - Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100)); - - Assert.True (ml.TimedEvents.Remove (token)); - Assert.Equal (2, callbackCount); - } - - [Fact] - public void CheckTimersAndIdleHandlers_NoTimers_Returns_False () - { - var ml = new MainLoop (new FakeMainLoop ()); - bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut); - Assert.False (retVal); - Assert.Equal (-1, waitTimeOut); - } - - [Fact] - public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True () - { - var ml = new MainLoop (new FakeMainLoop ()); - Func fnTrue = () => true; - - ml.TimedEvents.Add (TimeSpan.Zero, fnTrue); - bool retVal = ml.TimedEvents.CheckTimers(out int waitTimeOut); - Assert.True (retVal); - Assert.Equal (0, waitTimeOut); - } - - [Fact] - public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - - static bool Callback () { return false; } - - _ = ml.TimedEvents.Add (ms, Callback); - bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut); - - Assert.True (retVal); - - // It should take < 10ms to execute to here - Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10); - } - - [Fact] - public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer () - { - var ml = new MainLoop (new FakeMainLoop ()); - TimeSpan ms = TimeSpan.FromMilliseconds (50); - - static bool Callback () { return false; } - - _ = ml.TimedEvents.Add (ms, Callback); - _ = ml.TimedEvents.Add (ms, Callback); - bool retVal = ml.TimedEvents.CheckTimers (out int waitTimeOut); - - Assert.True (retVal); - - // It should take < 10ms to execute to here - Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10); - } - - [Fact] - public void False_Idle_Stops_It_Being_Called_Again () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn1 = () => - { - functionCalled++; - - if (functionCalled == 10) - { - return false; - } - - return true; - }; - - // Force stop if 20 iterations - var stopCount = 0; - - Func fnStop = () => - { - stopCount++; - - if (stopCount == 20) - { - ml.Stop (); - } - - return true; - }; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fnStop); - var b = ml.TimedEvents.Add (TimeSpan.Zero, fn1); - ml.Run (); - Assert.True (ml.TimedEvents.Remove (a)); - Assert.False (ml.TimedEvents.Remove (a)); - - Assert.Equal (10, functionCalled); - Assert.Equal (20, stopCount); - } - - [Fact] - public void Internal_Tests () - { - var testMainloop = new TestMainloop (); - var mainloop = new MainLoop (testMainloop); - Assert.Empty (mainloop.TimedEvents.Timeouts); - - Assert.NotNull ( - new Terminal.Gui.App.Timeout { Span = new (), Callback = () => true } - ); - } - - [Theory] - [MemberData (nameof (TestAddTimeout))] - public void Mainloop_Invoke_Or_AddTimeout_Can_Be_Used_For_Events_Or_Actions ( - Action action, - string pclickMe, - string pcancel, - string ppewPew, - int pzero, - int pone, - int ptwo, - int pthree, - int pfour - ) - { - // TODO: Expand this test to test all drivers - Application.Init (null, "fakedriver"); - - total = 0; - btn = null; - clickMe = pclickMe; - cancel = pcancel; - pewPew = ppewPew; - zero = pzero; - one = pone; - two = ptwo; - three = pthree; - four = pfour; - taskCompleted = false; - - var btnLaunch = new Button { Text = "Open Window" }; - - btnLaunch.Accepting += (s, e) => action (); - - var top = new Toplevel (); - top.Add (btnLaunch); - - int iterations = -1; - - Application.Iteration += (s, a) => - { - iterations++; - - if (iterations == 0) - { - Assert.Null (btn); - Assert.Equal (zero, total); - Assert.False (btnLaunch.NewKeyDownEvent (Key.Space)); - - if (btn == null) - { - Assert.Null (btn); - Assert.Equal (zero, total); - } - else - { - Assert.Equal (clickMe, btn.Text); - Assert.Equal (four, total); - } - } - else if (iterations == 1) - { - Assert.Equal (clickMe, btn.Text); - Assert.Equal (zero, total); - Assert.False (btn.NewKeyDownEvent (Key.Space)); - Assert.Equal (cancel, btn.Text); - Assert.Equal (one, total); - } - else if (taskCompleted) - { - Application.RequestStop (); - } - }; - - Application.Run (top); - top.Dispose (); - - Assert.True (taskCompleted); - Assert.Equal (clickMe, btn.Text); - Assert.Equal (four, total); - - Application.Shutdown (); - } - - [Fact] - public void RemoveIdle_Function_NotCalled () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn = () => - { - functionCalled++; - - return true; - }; - - Assert.False (ml.TimedEvents.Remove ("flibble")); - ml.RunIteration (); - Assert.Equal (0, functionCalled); - } - - [Fact] - public void Run_Runs_Idle_Stop_Stops_Idle () - { - var ml = new MainLoop (new FakeMainLoop ()); - - var functionCalled = 0; - - Func fn = () => - { - functionCalled++; - - if (functionCalled == 10) - { - ml.Stop (); - } - - return true; - }; - - var a = ml.TimedEvents.Add (TimeSpan.Zero, fn); - ml.Run (); - Assert.True (ml.TimedEvents.Remove (a)); - - 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 TestAddTimeout - { - get - { - // Goes fine - Action a1 = StartWindow; - - yield return new object [] { a1, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 }; - - // Also goes fine - Action a2 = () => Application.Invoke (StartWindow); - - yield return new object [] { a2, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 }; - } - } - - private static async void RunAsyncTest (object sender, EventArgs e) - { - Assert.Equal (clickMe, btn.Text); - Assert.Equal (zero, total); - - btn.Text = "Cancel"; - Interlocked.Increment (ref total); - btn.SetNeedsDraw (); - - await Task.Run ( - () => - { - try - { - Assert.Equal (cancel, btn.Text); - Assert.Equal (one, total); - - RunSql (); - } - finally - { - SetReadyToRun (); - } - } - ) - .ContinueWith ( - async (s, e) => - { - await Task.Delay (1000); - Assert.Equal (clickMe, btn.Text); - Assert.Equal (three, total); - - Interlocked.Increment (ref total); - - Assert.Equal (clickMe, btn.Text); - Assert.Equal (four, total); - - taskCompleted = true; - }, - TaskScheduler.FromCurrentSynchronizationContext () - ); - } - - private static void RunSql () - { - Thread.Sleep (100); - Assert.Equal (cancel, btn.Text); - Assert.Equal (one, total); - - Application.Invoke ( - () => - { - btn.Text = "Pew Pew"; - Interlocked.Increment (ref total); - btn.SetNeedsDraw (); - } - ); - } - - private static void SetReadyToRun () - { - Thread.Sleep (100); - Assert.Equal (pewPew, btn.Text); - Assert.Equal (two, total); - - Application.Invoke ( - () => - { - btn.Text = "Click Me"; - Interlocked.Increment (ref total); - btn.SetNeedsDraw (); - } - ); - } - - private static void StartWindow () - { - var startWindow = new Window { Modal = true }; - - btn = new() { Text = "Click Me" }; - - btn.Accepting += RunAsyncTest; - - var totalbtn = new Button { X = Pos.Right (btn), Text = "total" }; - - totalbtn.Accepting += (s, e) => { MessageBox.Query ("Count", $"Count is {total}", "Ok"); }; - - startWindow.Add (btn); - startWindow.Add (totalbtn); - - Application.Run (startWindow); - - Assert.Equal (clickMe, btn.Text); - Assert.Equal (four, total); - - Application.RequestStop (); - } - - private class MillisecondTolerance : IEqualityComparer - { - public MillisecondTolerance (int tolerance) { _tolerance = tolerance; } - private readonly int _tolerance; - public bool Equals (TimeSpan x, TimeSpan y) { return Math.Abs (x.Milliseconds - y.Milliseconds) <= _tolerance; } - public int GetHashCode (TimeSpan obj) { return obj.GetHashCode (); } - } - - private class TestMainloop : IMainLoopDriver - { - private MainLoop mainLoop; - public bool EventsPending () { throw new NotImplementedException (); } - public void Iteration () { throw new NotImplementedException (); } - public void TearDown () { throw new NotImplementedException (); } - public void Setup (MainLoop mainLoop) { this.mainLoop = mainLoop; } - public void Wakeup () { throw new NotImplementedException (); } - } -} diff --git a/Tests/UnitTests/Application/RunStateTests.cs b/Tests/UnitTests/Application/RunStateTests.cs index e00860020..cf5033085 100644 --- a/Tests/UnitTests/Application/RunStateTests.cs +++ b/Tests/UnitTests/Application/RunStateTests.cs @@ -45,7 +45,6 @@ public class RunStateTests #endif Assert.Null (Application.Top); - // Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); } diff --git a/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs b/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs index 069cd12ef..b77a1905a 100644 --- a/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs +++ b/Tests/UnitTests/ConsoleDrivers/ConsoleDriverTests.cs @@ -50,8 +50,8 @@ public class ConsoleDriverTests public void Init_Inits (Type driverType) { var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - MainLoop ml = driver.Init (); - Assert.NotNull (ml); + driver.Init (); + // Note: MainLoop is no longer returned from Init() as part of legacy MainLoop removal Assert.NotNull (driver.Clipboard); Console.ForegroundColor = ConsoleColor.Red; Assert.Equal (ConsoleColor.Red, Console.ForegroundColor); diff --git a/Tests/UnitTests/View/Draw/AllViewsDrawTests.cs b/Tests/UnitTests/View/Draw/AllViewsDrawTests.cs index f9a45d5c9..c5a957921 100644 --- a/Tests/UnitTests/View/Draw/AllViewsDrawTests.cs +++ b/Tests/UnitTests/View/Draw/AllViewsDrawTests.cs @@ -12,7 +12,6 @@ public class AllViewsDrawTests (ITestOutputHelper output) : TestsAllViews { Application.ResetState (true); // Required for spinner view that wants to register timeouts - Application.MainLoop = new MainLoop (new FakeMainLoop (Application.Driver)); var view = (View)CreateInstanceIfNotGeneric (viewType); diff --git a/Tests/UnitTests/View/Layout/LayoutTests.cs b/Tests/UnitTests/View/Layout/LayoutTests.cs index 53b40ceed..abe4442ea 100644 --- a/Tests/UnitTests/View/Layout/LayoutTests.cs +++ b/Tests/UnitTests/View/Layout/LayoutTests.cs @@ -12,7 +12,6 @@ public class LayoutTests (ITestOutputHelper output) : TestsAllViews { // Required for spinner view that wants to register timeouts - Application.MainLoop = new MainLoop (new FakeMainLoop (Application.Driver)); var view = (View)CreateInstanceIfNotGeneric (viewType); diff --git a/Tests/UnitTests/Views/AllViewsTests.cs b/Tests/UnitTests/Views/AllViewsTests.cs index bba6ffc48..cd08c2f7e 100644 --- a/Tests/UnitTests/Views/AllViewsTests.cs +++ b/Tests/UnitTests/Views/AllViewsTests.cs @@ -14,7 +14,6 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews public void AllViews_Center_Properly (Type viewType) { // Required for spinner view that wants to register timeouts - Application.MainLoop = new (new FakeMainLoop (Application.Driver)); var view = CreateInstanceIfNotGeneric (viewType); diff --git a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs index 6b7b69afc..e81512510 100644 --- a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs +++ b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs @@ -623,7 +623,6 @@ public class MenuBarv1Tests (ITestOutputHelper output) Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked }); // Need to fool MainLoop into thinking it's running - Application.MainLoop.Running = true; AutoInitShutdownAttribute.RunIteration (); Assert.Equal (items [0], menu.Menus [0].Title); @@ -815,7 +814,6 @@ public class MenuBarv1Tests (ITestOutputHelper output) Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked }); // Need to fool MainLoop into thinking it's running - Application.MainLoop.Running = true; AutoInitShutdownAttribute.RunIteration (); Assert.Equal (items [0], menu.Menus [0].Title); diff --git a/Tests/UnitTestsParallelizable/ConsoleDrivers/MainLoopDriverTests.cs b/Tests/UnitTestsParallelizable/ConsoleDrivers/MainLoopDriverTests.cs deleted file mode 100644 index ddc851187..000000000 --- a/Tests/UnitTestsParallelizable/ConsoleDrivers/MainLoopDriverTests.cs +++ /dev/null @@ -1,300 +0,0 @@ -using Xunit.Abstractions; - -// Alias Console to MockConsole so we don't accidentally use Console - -namespace UnitTests_Parallelizable.DriverTests; - -public class MainLoopDriverTests : UnitTests.Parallelizable.ParallelizableBase -{ - public MainLoopDriverTests (ITestOutputHelper output) { ConsoleDriver.RunningUnitTests = true; } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_AddTimeout_ValidIdleHandler_ReturnsToken (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - var idleHandlerInvoked = false; - - bool IdleHandler () - { - idleHandlerInvoked = true; - - return false; - } - - var token = mainLoop.TimedEvents.Add(TimeSpan.Zero, IdleHandler); - - Assert.NotNull (token); - Assert.False (idleHandlerInvoked); // Idle handler should not be invoked immediately - mainLoop.RunIteration (); // Run an iteration to process the idle handler - Assert.True (idleHandlerInvoked); // Idle handler should be invoked after processing - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_AddTimeout_ValidParameters_ReturnsToken (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - var callbackInvoked = false; - - object token = mainLoop.TimedEvents.Add ( - TimeSpan.FromMilliseconds (100), - () => - { - callbackInvoked = true; - - return false; - } - ); - - Assert.NotNull (token); - mainLoop.RunIteration (); // Run an iteration to process the timeout - Assert.False (callbackInvoked); // Callback should not be invoked immediately - Thread.Sleep (200); // Wait for the timeout - mainLoop.RunIteration (); // Run an iteration to process the timeout - Assert.True (callbackInvoked); // Callback should be invoked after the timeout - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_CheckTimersAndIdleHandlers_IdleHandlersActive_ReturnsTrue ( - Type driverType, - Type mainLoopDriverType - ) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - mainLoop.TimedEvents.Add (TimeSpan.Zero, () => false); - bool result = mainLoop.TimedEvents.CheckTimers (out int waitTimeout); - - Assert.True (result); - Assert.Equal (0, waitTimeout); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_CheckTimers_NoTimersOrIdleHandlers_ReturnsFalse ( - Type driverType, - Type mainLoopDriverType - ) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - bool result = mainLoop.TimedEvents.CheckTimers (out int waitTimeout); - - Assert.False (result); - Assert.Equal (-1, waitTimeout); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_CheckTimersAndIdleHandlers_TimersActive_ReturnsTrue ( - Type driverType, - Type mainLoopDriverType - ) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - mainLoop.TimedEvents.Add (TimeSpan.FromMilliseconds (100), () => false); - bool result = mainLoop.TimedEvents.CheckTimers(out int waitTimeout); - - Assert.True (result); - Assert.True (waitTimeout >= 0); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_Constructs_Disposes (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - // Check default values - Assert.NotNull (mainLoop); - Assert.Equal (mainLoopDriver, mainLoop.MainLoopDriver); - Assert.Empty (mainLoop.TimedEvents.Timeouts); - Assert.False (mainLoop.Running); - - // Clean up - mainLoop.Dispose (); - - // TODO: It'd be nice if we could really verify IMainLoopDriver.TearDown was called - // and that it was actually cleaned up. - Assert.Null (mainLoop.MainLoopDriver); - Assert.Empty (mainLoop.TimedEvents.Timeouts); - Assert.False (mainLoop.Running); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_RemoveIdle_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - bool result = mainLoop.TimedEvents.Remove("flibble"); - - Assert.False (result); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_RemoveIdle_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - bool IdleHandler () { return false; } - - - var token = mainLoop.TimedEvents.Add (TimeSpan.Zero, IdleHandler); - bool result = mainLoop.TimedEvents.Remove (token); - - Assert.True (result); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_RemoveTimeout_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - bool result = mainLoop.TimedEvents.Remove (new object ()); - - Assert.False (result); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_RemoveTimeout_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - - object token = mainLoop.TimedEvents.Add (TimeSpan.FromMilliseconds (100), () => false); - bool result = mainLoop.TimedEvents.Remove (token); - - Assert.True (result); - mainLoop.Dispose (); - } - - [Theory] - [InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - //[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - //[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - //[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - - //[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))] - public void MainLoop_RunIteration_ValidIdleHandler_CallsIdleHandler (Type driverType, Type mainLoopDriverType) - { - var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver); - var mainLoop = new MainLoop (mainLoopDriver); - var idleHandlerInvoked = false; - - Func idleHandler = () => - { - idleHandlerInvoked = true; - - return false; - }; - - mainLoop.TimedEvents.Add (TimeSpan.Zero, idleHandler); - mainLoop.RunIteration (); // Run an iteration to process the idle handler - - Assert.True (idleHandlerInvoked); - mainLoop.Dispose (); - } - - //[Theory] - //[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))] - ////[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))] - ////[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))] - ////[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))] - //public void MainLoop_Invoke_ValidAction_RunsAction (Type driverType, Type mainLoopDriverType) - //{ - // var driver = (IConsoleDriver)Activator.CreateInstance (driverType); - // var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver }); - // var mainLoop = new MainLoop (mainLoopDriver); - // var actionInvoked = false; - - // mainLoop.Invoke (() => { actionInvoked = true; }); - // mainLoop.RunIteration (); // Run an iteration to process the action. - - // Assert.True (actionInvoked); - // mainLoop.Dispose (); - //} -} diff --git a/Tests/UnitTestsParallelizable/MockConsoleDriver.cs b/Tests/UnitTestsParallelizable/MockConsoleDriver.cs index 1eb4699e1..50f025834 100644 --- a/Tests/UnitTestsParallelizable/MockConsoleDriver.cs +++ b/Tests/UnitTestsParallelizable/MockConsoleDriver.cs @@ -155,7 +155,7 @@ internal class MockConsoleDriver : IConsoleDriver public void UpdateCursor () {} /// - public MainLoop Init () { return null!; } + public void Init () { } /// public void End () { } diff --git a/Tests/UnitTestsParallelizable/TestSetup.cs b/Tests/UnitTestsParallelizable/TestSetup.cs index 897548638..78eee0aec 100644 --- a/Tests/UnitTestsParallelizable/TestSetup.cs +++ b/Tests/UnitTestsParallelizable/TestSetup.cs @@ -47,7 +47,6 @@ public class GlobalTestSetup : IDisposable // Don't check Application.Force16Colors //Assert.False (Application.Force16Colors); Assert.Null (Application.Driver); - Assert.Null (Application.MainLoop); Assert.False (Application.EndAfterFirstIteration); Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey); Assert.Equal (Key.Tab, Application.NextTabKey); diff --git a/docfx/docs/drivers.md b/docfx/docs/drivers.md index 3a78d597a..e8322ebf2 100644 --- a/docfx/docs/drivers.md +++ b/docfx/docs/drivers.md @@ -291,6 +291,8 @@ Application.Init(); Terminal.Gui v1 drivers that implement `IConsoleDriver` but not `IConsoleDriverFacade` are still supported through a legacy compatibility layer. However, they do not benefit from the v2 architecture improvements (multi-threading, component separation, etc.). +**Note**: The legacy `MainLoop` infrastructure (including the `MainLoop` class, `IMainLoopDriver` interface, and `FakeMainLoop`) has been removed in favor of the modern architecture. All drivers now use the `MainLoopCoordinator` and `ApplicationMainLoop` system exclusively. + ## See Also - @Terminal.Gui.Drivers - API Reference diff --git a/docfx/docs/migratingfromv1.md b/docfx/docs/migratingfromv1.md index 292368be2..fbf121c2f 100644 --- a/docfx/docs/migratingfromv1.md +++ b/docfx/docs/migratingfromv1.md @@ -456,17 +456,26 @@ Additionally, the `Toggle` event was renamed `CheckStateChanging` and made cance +cb.AdvanceCheckState (); ``` -## `MainLoop` is no longer accessible from `Application` +## `MainLoop` has been removed from `Application` -In v1, you could add timeouts via `Application.MainLoop.AddTimeout` among other things. In v2, the `MainLoop` object is internal to `Application` and methods previously accessed via `MainLoop` can now be accessed directly via `Application` +In v1, you could add timeouts via `Application.MainLoop.AddTimeout` and access the `MainLoop` object directly. In v2, the legacy `MainLoop` class has been completely removed as part of the architectural modernization. Timeout functionality and other features previously accessed via `MainLoop` are now available directly through `Application` or `ApplicationImpl`. ### How to Fix +Replace any `Application.MainLoop` references: + ```diff - Application.MainLoop.AddTimeout (TimeSpan time, Func callback) + Application.AddTimeout (TimeSpan time, Func callback) ``` +```diff +- Application.MainLoop.Wakeup () ++ // No replacement needed - wakeup is handled automatically by the modern architecture +``` + +**Note**: The legacy `MainLoop` infrastructure (including `IMainLoopDriver` and `FakeMainLoop`) has been removed. The modern v2 architecture uses `ApplicationImpl`, `MainLoopCoordinator`, and `ApplicationMainLoop` instead. + ## `SendSubViewXXX` renamed and corrected In v1, the `View` methods to move SubViews within the SubViews list were poorly named and actually operated in reverse of what their names suggested. diff --git a/local_packages/Terminal.Gui.2.0.0.nupkg b/local_packages/Terminal.Gui.2.0.0.nupkg index b93b3273b..c8a1980a4 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.nupkg and b/local_packages/Terminal.Gui.2.0.0.nupkg differ diff --git a/local_packages/Terminal.Gui.2.0.0.snupkg b/local_packages/Terminal.Gui.2.0.0.snupkg index 390f9f036..aa76f8d2a 100644 Binary files a/local_packages/Terminal.Gui.2.0.0.snupkg and b/local_packages/Terminal.Gui.2.0.0.snupkg differ