浏览代码

Fix fence logic to work with parallel tests

Co-authored-by: tig <[email protected]>
copilot-swe-agent[bot] 3 周之前
父节点
当前提交
25fdeef61a

+ 2 - 5
Terminal.Gui/App/Application.Lifecycle.cs

@@ -75,10 +75,7 @@ public static partial class Application // Lifecycle (Init/Shutdown)
     [Obsolete ("The legacy static Application object is going away.")]
     internal static void ResetState (bool ignoreDisposed = false)
     {
-        // Reset the model usage tracking first to allow access to Instance if needed
-        ApplicationImpl.ResetModelUsageTracking ();
-
-        // Now safe to access Instance for cleanup
-        ApplicationImpl.Instance?.ResetState (ignoreDisposed);
+        // Use the static reset method to bypass the fence check
+        ApplicationImpl.ResetStateStatic (ignoreDisposed);
     }
 }

+ 17 - 4
Terminal.Gui/App/ApplicationImpl.Lifecycle.cs

@@ -23,6 +23,23 @@ public partial class ApplicationImpl
             throw new InvalidOperationException ("Init called multiple times without Shutdown");
         }
 
+        // Check the fence: ensure we're not mixing application models
+        // If this is a legacy static instance and instance-based model was used, throw
+        if (this == _instance && _modelUsage == ApplicationModelUsage.InstanceBased)
+        {
+            throw new InvalidOperationException (
+                "Cannot use legacy static Application model (Application.Init/ApplicationImpl.Instance) after using modern instance-based model (Application.Create). " +
+                "Use only one model per process.");
+        }
+
+        // If this is an instance-based instance and legacy static model was used, throw
+        if (this != _instance && _modelUsage == ApplicationModelUsage.LegacyStatic)
+        {
+            throw new InvalidOperationException (
+                "Cannot use modern instance-based model (Application.Create) after using legacy static Application model (Application.Init/ApplicationImpl.Instance). " +
+                "Use only one model per process.");
+        }
+
         if (!string.IsNullOrWhiteSpace (driverName))
         {
             _driverName = driverName;
@@ -273,10 +290,6 @@ public partial class ApplicationImpl
         // gui.cs does no longer process any callbacks. See #1084 for more details:
         // (https://github.com/gui-cs/Terminal.Gui/issues/1084).
         SynchronizationContext.SetSynchronizationContext (null);
-
-        // === 12. Reset application model usage tracking ===
-        // Reset the model usage tracking to allow the process to use either model after shutdown
-        ResetModelUsageTracking ();
     }
 
     /// <summary>

+ 17 - 2
Terminal.Gui/App/ApplicationImpl.cs

@@ -49,7 +49,7 @@ public partial class ApplicationImpl : IApplication
                 return _instance;
             }
 
-            // Only check the fence when creating a new instance
+            // Check if the instance-based model has already been used
             if (_modelUsage == ApplicationModelUsage.InstanceBased)
             {
                 throw new InvalidOperationException (
@@ -57,6 +57,7 @@ public partial class ApplicationImpl : IApplication
                     "Use only one model per process.");
             }
 
+            // Mark the usage and create the instance
             _modelUsage = ApplicationModelUsage.LegacyStatic;
 
             return _instance = new ApplicationImpl ();
@@ -68,7 +69,8 @@ public partial class ApplicationImpl : IApplication
     /// </summary>
     internal static void MarkInstanceBasedModelUsed ()
     {
-        if (_modelUsage == ApplicationModelUsage.LegacyStatic)
+        // Check if the legacy static model has already been initialized
+        if (_modelUsage == ApplicationModelUsage.LegacyStatic && _instance?.Initialized == true)
         {
             throw new InvalidOperationException (
                 "Cannot use modern instance-based model (Application.Create) after using legacy static Application model (Application.Init/ApplicationImpl.Instance). " +
@@ -87,6 +89,19 @@ public partial class ApplicationImpl : IApplication
         _instance = null;
     }
 
+    /// <summary>
+    ///     INTERNAL: Resets state without going through the fence-checked Instance property.
+    ///     Used by Application.ResetState() to allow cleanup regardless of which model was used.
+    /// </summary>
+    internal static void ResetStateStatic (bool ignoreDisposed = false)
+    {
+        // If an instance exists, reset it
+        _instance?.ResetState (ignoreDisposed);
+
+        // Always reset the model tracking to allow tests to use either model after reset
+        ResetModelUsageTracking ();
+    }
+
     #endregion Singleton
 
     /// <summary>

+ 10 - 8
Tests/UnitTestsParallelizable/Application/ApplicationModelFencingTests.cs

@@ -18,11 +18,12 @@ public class ApplicationModelFencingTests
     {
         // Create a modern instance-based application
         IApplication app = Application.Create ();
+        app.Init ("fake");
 
-        // Attempting to access the legacy static instance should throw
+        // Attempting to initialize using the legacy static model should throw
         InvalidOperationException ex = Assert.Throws<InvalidOperationException> (() =>
         {
-            IApplication _ = ApplicationImpl.Instance;
+            ApplicationImpl.Instance.Init ("fake");
         });
 
         Assert.Contains ("Cannot use legacy static Application model", ex.Message);
@@ -35,13 +36,15 @@ public class ApplicationModelFencingTests
     [Fact]
     public void InstanceAccess_ThenCreate_ThrowsInvalidOperationException ()
     {
-        // Access the legacy static instance
+        // Initialize using the legacy static model
         IApplication staticInstance = ApplicationImpl.Instance;
+        staticInstance.Init ("fake");
 
-        // Attempting to create a modern instance-based application should throw
+        // Attempting to create and initialize with modern instance-based model should throw
         InvalidOperationException ex = Assert.Throws<InvalidOperationException> (() =>
         {
-            IApplication _ = Application.Create ();
+            IApplication app = Application.Create ();
+            app.Init ("fake");
         });
 
         Assert.Contains ("Cannot use modern instance-based model", ex.Message);
@@ -78,11 +81,10 @@ public class ApplicationModelFencingTests
         IApplication app = Application.Create ();
         app.Init ("fake");
 
-        // Attempting to access the legacy static instance should throw
-        // (Init calls ApplicationImpl.Instance internally)
+        // Attempting to initialize using the legacy static model should throw
         InvalidOperationException ex = Assert.Throws<InvalidOperationException> (() =>
         {
-            IApplication _ = ApplicationImpl.Instance;
+            ApplicationImpl.Instance.Init ("fake");
         });
 
         Assert.Contains ("Cannot use legacy static Application model", ex.Message);