瀏覽代碼

Merge pull request #3537 from BDisp/v2_3536-application-t-fix

Fixes #3536. Application.Run<T> is broken by not calling Init before initializing T.
Tig 1 年之前
父節點
當前提交
bd0045340e
共有 2 個文件被更改,包括 31 次插入16 次删除
  1. 15 10
      Terminal.Gui/Application/Application.cs
  2. 16 6
      UnitTests/Application/ApplicationTests.cs

+ 15 - 10
Terminal.Gui/Application/Application.cs

@@ -1,7 +1,6 @@
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Globalization;
 using System.Globalization;
 using System.Reflection;
 using System.Reflection;
-using System.Text.Json.Serialization;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -679,9 +678,15 @@ public static partial class Application
     public static T Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
     public static T Run<T> (Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
         where T : Toplevel, new ()
         where T : Toplevel, new ()
     {
     {
+        if (!_initialized)
+        {
+            // Init() has NOT been called.
+            InternalInit (driver, null, true);
+        }
+
         var top = new T ();
         var top = new T ();
 
 
-        Run (top, errorHandler, driver);
+        Run (top, errorHandler);
 
 
         return top;
         return top;
     }
     }
@@ -708,7 +713,10 @@ public static partial class Application
     ///         <see cref="RunLoop(RunState)"/> method will only process any pending events, timers, idle handlers and then
     ///         <see cref="RunLoop(RunState)"/> method will only process any pending events, timers, idle handlers and then
     ///         return control immediately.
     ///         return control immediately.
     ///     </para>
     ///     </para>
-    ///     <para>Calling <see cref="Init"/> first is not needed as this function will initialize the application.</para>
+    ///     <para>When using <see cref="Run{T}"/> or
+    ///         <see cref="Run(System.Func{System.Exception,bool},Terminal.Gui.ConsoleDriver)"/>
+    ///         <see cref="Init"/> will be called automatically.
+    ///     </para>
     ///     <para>
     ///     <para>
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         RELEASE builds only: When <paramref name="errorHandler"/> is <see langword="null"/> any exceptions will be
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
     ///         rethrown. Otherwise, if <paramref name="errorHandler"/> will be called. If <paramref name="errorHandler"/>
@@ -721,12 +729,7 @@ public static partial class Application
     ///     RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true,
     ///     RELEASE builds only: Handler for any unhandled exceptions (resumes when returns true,
     ///     rethrows when null).
     ///     rethrows when null).
     /// </param>
     /// </param>
-    /// <param name="driver">
-    ///     The <see cref="ConsoleDriver"/> to use. If not specified the default driver for the platform will
-    ///     be used ( <see cref="WindowsDriver"/>, <see cref="CursesDriver"/>, or <see cref="NetDriver"/>). Must be
-    ///     <see langword="null"/> if <see cref="Init"/> was called.
-    /// </param>
-    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null, ConsoleDriver driver = null)
+    public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null)
     {
     {
         ArgumentNullException.ThrowIfNull (view);
         ArgumentNullException.ThrowIfNull (view);
 
 
@@ -746,7 +749,9 @@ public static partial class Application
         else
         else
         {
         {
             // Init() has NOT been called.
             // Init() has NOT been called.
-            InternalInit (driver, null, true);
+            throw new InvalidOperationException (
+                                                 "Init() has not been called. Only Run() or Run<T>() can be used without calling Init()."
+                                                );
         }
         }
 
 
         var resume = true;
         var resume = true;

+ 16 - 6
UnitTests/Application/ApplicationTests.cs

@@ -863,7 +863,7 @@ public class ApplicationTests
 
 
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         Assert.False (w.WasDisposed);
         Assert.False (w.WasDisposed);
-        Exception exception = Record.Exception (() => Application.Shutdown ()); // Invalid - w has not been disposed.
+        Exception exception = Record.Exception (Application.Shutdown); // Invalid - w has not been disposed.
         Assert.NotNull (exception);
         Assert.NotNull (exception);
 
 
         w.Dispose ();
         w.Dispose ();
@@ -902,7 +902,7 @@ public class ApplicationTests
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         Assert.Equal (top, Application.Top);
         Assert.Equal (top, Application.Top);
         Assert.False (top.WasDisposed);
         Assert.False (top.WasDisposed);
-        Exception exception = Record.Exception (() => Application.Shutdown ());
+        Exception exception = Record.Exception (Application.Shutdown);
         Assert.NotNull (exception);
         Assert.NotNull (exception);
         Assert.False (top.WasDisposed);
         Assert.False (top.WasDisposed);
 #endif
 #endif
@@ -934,7 +934,7 @@ public class ApplicationTests
         Application.Run<Toplevel> (null, driver);
         Application.Run<Toplevel> (null, driver);
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         Assert.False (Application.Top.WasDisposed);
         Assert.False (Application.Top.WasDisposed);
-        Exception exception = Record.Exception (() => Application.Shutdown ());
+        Exception exception = Record.Exception (Application.Shutdown);
         Assert.NotNull (exception);
         Assert.NotNull (exception);
         Assert.False (Application.Top.WasDisposed);
         Assert.False (Application.Top.WasDisposed);
 
 
@@ -949,21 +949,31 @@ public class ApplicationTests
     }
     }
 
 
     [Fact]
     [Fact]
-    public void Run_t_Creates_Top_Without_Init ()
+    public void Run_t_Does_Not_Creates_Top_Without_Init ()
     {
     {
+        // When a Toplevel is created it must already have all the Application configuration loaded
+        // This is only possible by two ways:
+        // 1 - Using Application.Init first
+        // 2 - Using Application.Run() or Application.Run<T>()
+        // The Application.Run(new(Toplevel)) must always call Application.Init() first because
+        // the new(Toplevel) may be a derived class that is possible using Application static
+        // properties that is only available after the Application.Init was called
         var driver = new FakeDriver ();
         var driver = new FakeDriver ();
 
 
         Assert.Null (Application.Top);
         Assert.Null (Application.Top);
 
 
+        Assert.Throws<InvalidOperationException> (() => Application.Run (new Toplevel ()));
+
+        Application.Init (driver);
         Application.Iteration += (s, e) =>
         Application.Iteration += (s, e) =>
                                  {
                                  {
                                      Assert.NotNull (Application.Top);
                                      Assert.NotNull (Application.Top);
                                      Application.RequestStop ();
                                      Application.RequestStop ();
                                  };
                                  };
-        Application.Run (new (), null, driver);
+        Application.Run (new Toplevel ());
 #if DEBUG_IDISPOSABLE
 #if DEBUG_IDISPOSABLE
         Assert.False (Application.Top.WasDisposed);
         Assert.False (Application.Top.WasDisposed);
-        Exception exception = Record.Exception (() => Application.Shutdown ());
+        Exception exception = Record.Exception (Application.Shutdown);
         Assert.NotNull (exception);
         Assert.NotNull (exception);
         Assert.False (Application.Top.WasDisposed);
         Assert.False (Application.Top.WasDisposed);