From 25fdeef61a3bf3f901c1cb2b8556b84d22ed095a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:49:46 +0000 Subject: [PATCH] Fix fence logic to work with parallel tests Co-authored-by: tig <585482+tig@users.noreply.github.com> --- Terminal.Gui/App/Application.Lifecycle.cs | 7 ++----- Terminal.Gui/App/ApplicationImpl.Lifecycle.cs | 21 +++++++++++++++---- Terminal.Gui/App/ApplicationImpl.cs | 19 +++++++++++++++-- .../ApplicationModelFencingTests.cs | 18 +++++++++------- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Terminal.Gui/App/Application.Lifecycle.cs b/Terminal.Gui/App/Application.Lifecycle.cs index 2d1465fb0..ef16e1769 100644 --- a/Terminal.Gui/App/Application.Lifecycle.cs +++ b/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); } } diff --git a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs b/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs index 18db0fa6d..3edd303cc 100644 --- a/Terminal.Gui/App/ApplicationImpl.Lifecycle.cs +++ b/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 (); } /// diff --git a/Terminal.Gui/App/ApplicationImpl.cs b/Terminal.Gui/App/ApplicationImpl.cs index 9277a266d..aadbf6831 100644 --- a/Terminal.Gui/App/ApplicationImpl.cs +++ b/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 /// 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; } + /// + /// INTERNAL: Resets state without going through the fence-checked Instance property. + /// Used by Application.ResetState() to allow cleanup regardless of which model was used. + /// + 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 /// diff --git a/Tests/UnitTestsParallelizable/Application/ApplicationModelFencingTests.cs b/Tests/UnitTestsParallelizable/Application/ApplicationModelFencingTests.cs index b9728c151..e2bd4ce25 100644 --- a/Tests/UnitTestsParallelizable/Application/ApplicationModelFencingTests.cs +++ b/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 (() => { - 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 (() => { - 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 (() => { - IApplication _ = ApplicationImpl.Instance; + ApplicationImpl.Instance.Init ("fake"); }); Assert.Contains ("Cannot use legacy static Application model", ex.Message);