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);