Fix fence logic to work with parallel tests

Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-22 18:49:46 +00:00
parent 071444ee59
commit 25fdeef61a
4 changed files with 46 additions and 19 deletions

View File

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

View File

@@ -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>

View File

@@ -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>

View File

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