mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
* Fixes #4391. Weird situation where ForceDriver with args doesn't persists on open scenario
* Prevents change ForceDriver if app it's already initialized and allowing also initialize with driver instead of only by driver name.
* Add dispose into FakeDriverBase and reset ForceDriver
* Moving test to Application folder
* Fix unit test
* Remove unnecessary GlobalTestSetup
* Add GC.SuppressFinalize
* Revert "Add GC.SuppressFinalize"
This reverts commit 2bd7cd7791.
* Reset MouseGrabView
* Avoid CI warnings
* Add GlobalTestSetup in all test that use Application
* Trying to fix unit test
* Reverting scope changes
* Remove UICatalog testing Run
* Force re-run CI test
* Fix merge errors
* Fix ansi for the red background color code
* Fix more ANSI color code unit tests
---------
Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
@@ -55,7 +55,9 @@ namespace UICatalog;
|
||||
/// </remarks>
|
||||
public class UICatalog
|
||||
{
|
||||
private static string? _forceDriver = null;
|
||||
private static string? _forceDriver;
|
||||
private static string? _uiCatalogDriver;
|
||||
private static string? _scenarioDriver;
|
||||
|
||||
public static string LogFilePath { get; set; } = string.Empty;
|
||||
public static LoggingLevelSwitch LogLevelSwitch { get; } = new ();
|
||||
@@ -194,6 +196,8 @@ public class UICatalog
|
||||
|
||||
UICatalogMain (Options);
|
||||
|
||||
Debug.Assert (Application.ForceDriver == string.Empty);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -255,7 +259,9 @@ public class UICatalog
|
||||
|
||||
Application.Init (driverName: _forceDriver);
|
||||
|
||||
var top = Application.Run<UICatalogTop> ();
|
||||
_uiCatalogDriver = Application.Driver!.GetName ();
|
||||
|
||||
Toplevel top = Application.Run<UICatalogTop> ();
|
||||
top.Dispose ();
|
||||
Application.Shutdown ();
|
||||
VerifyObjectsWereDisposed ();
|
||||
@@ -421,6 +427,8 @@ public class UICatalog
|
||||
Application.InitializedChanged += ApplicationOnInitializedChanged;
|
||||
#endif
|
||||
|
||||
Application.ForceDriver = _forceDriver;
|
||||
|
||||
scenario.Main ();
|
||||
scenario.Dispose ();
|
||||
|
||||
@@ -439,6 +447,8 @@ public class UICatalog
|
||||
if (e.Value)
|
||||
{
|
||||
sw.Start ();
|
||||
_scenarioDriver = Application.Driver!.GetName ();
|
||||
Debug.Assert (_scenarioDriver == _uiCatalogDriver);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -28,7 +28,21 @@ public static partial class Application // Driver abstractions
|
||||
public static string ForceDriver
|
||||
{
|
||||
get => ApplicationImpl.Instance.ForceDriver;
|
||||
set => ApplicationImpl.Instance.ForceDriver = value;
|
||||
set
|
||||
{
|
||||
if (!string.IsNullOrEmpty (ApplicationImpl.Instance.ForceDriver) && value != Driver?.GetName ())
|
||||
{
|
||||
// ForceDriver cannot be changed if it has a valid value
|
||||
return;
|
||||
}
|
||||
|
||||
if (ApplicationImpl.Instance.Initialized && value != Driver?.GetName ())
|
||||
{
|
||||
throw new InvalidOperationException ($"The {nameof (ForceDriver)} can only be set before initialized.");
|
||||
}
|
||||
|
||||
ApplicationImpl.Instance.ForceDriver = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IApplication.Sixel"/>
|
||||
|
||||
@@ -34,7 +34,7 @@ public partial class ApplicationImpl
|
||||
bool factoryIsFake = _componentFactory is IComponentFactory<ConsoleKeyInfo>;
|
||||
|
||||
// Then check driverName
|
||||
bool nameIsWindows = driverName?.Contains ("win", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
bool nameIsWindows = driverName?.Contains ("windows", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
bool nameIsDotNet = driverName?.Contains ("dotnet", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
bool nameIsUnix = driverName?.Contains ("unix", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
bool nameIsFake = driverName?.Contains ("fake", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
|
||||
@@ -33,8 +33,8 @@ public partial class ApplicationImpl
|
||||
_driverName = ForceDriver;
|
||||
}
|
||||
|
||||
// Debug.Assert (Navigation is null);
|
||||
// Navigation = new ();
|
||||
// Debug.Assert (Navigation is null);
|
||||
// Navigation = new ();
|
||||
|
||||
//Debug.Assert (Popover is null);
|
||||
//Popover = new ();
|
||||
@@ -62,7 +62,7 @@ public partial class ApplicationImpl
|
||||
_keyboard.PrevTabGroupKey = existingPrevTabGroupKey;
|
||||
}
|
||||
|
||||
CreateDriver (driverName ?? _driverName);
|
||||
CreateDriver (_driverName);
|
||||
Screen = Driver!.Screen;
|
||||
Initialized = true;
|
||||
|
||||
@@ -145,9 +145,13 @@ public partial class ApplicationImpl
|
||||
}
|
||||
#endif
|
||||
|
||||
private bool _isResetingState;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ResetState (bool ignoreDisposed = false)
|
||||
{
|
||||
_isResetingState = true;
|
||||
|
||||
// Shutdown is the bookend for Init. As such it needs to clean up all resources
|
||||
// Init created. Apps that do any threading will need to code defensively for this.
|
||||
// e.g. see Issue #537
|
||||
@@ -231,7 +235,14 @@ public partial class ApplicationImpl
|
||||
// === 9. Clear graphics ===
|
||||
Sixel.Clear ();
|
||||
|
||||
// === 10. Reset synchronization context ===
|
||||
// === 10. Reset ForceDriver ===
|
||||
// Note: ForceDriver and Force16Colors are reset
|
||||
// If they need to persist across Init/Shutdown cycles
|
||||
// then the user of the library should manage that state
|
||||
Force16Colors = false;
|
||||
ForceDriver = string.Empty;
|
||||
|
||||
// === 11. Reset synchronization context ===
|
||||
// IMPORTANT: Always reset sync context, even if not initialized
|
||||
// This ensures cleanup works correctly even if Shutdown is called without Init
|
||||
// Reset synchronization context to allow the user to run async/await,
|
||||
@@ -240,8 +251,7 @@ public partial class ApplicationImpl
|
||||
// (https://github.com/gui-cs/Terminal.Gui/issues/1084).
|
||||
SynchronizationContext.SetSynchronizationContext (null);
|
||||
|
||||
// Note: ForceDriver and Force16Colors are NOT reset;
|
||||
// they need to persist across Init/Shutdown cycles
|
||||
_isResetingState = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -254,6 +254,7 @@ internal class MouseImpl : IMouse
|
||||
// Do not clear LastMousePosition; Popover's require it to stay set with last mouse pos.
|
||||
CachedViewsUnderMouse.Clear ();
|
||||
MouseEvent = null;
|
||||
MouseGrabView = null;
|
||||
}
|
||||
|
||||
// Mouse grab functionality merged from MouseGrabHandler
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace UnitTests;
|
||||
/// Enables tests to create a FakeDriver for testing purposes.
|
||||
/// </summary>
|
||||
[Collection ("Global Test Setup")]
|
||||
public abstract class FakeDriverBase
|
||||
public abstract class FakeDriverBase : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new FakeDriver instance with the specified buffer size.
|
||||
@@ -19,14 +19,20 @@ public abstract class FakeDriverBase
|
||||
var output = new FakeOutput ();
|
||||
|
||||
DriverImpl driver = new (
|
||||
new FakeInputProcessor (null),
|
||||
new OutputBufferImpl (),
|
||||
output,
|
||||
new AnsiRequestScheduler (new AnsiResponseParser ()),
|
||||
new SizeMonitorImpl (output));
|
||||
new FakeInputProcessor (null),
|
||||
new OutputBufferImpl (),
|
||||
output,
|
||||
new AnsiRequestScheduler (new AnsiResponseParser ()),
|
||||
new SizeMonitorImpl (output));
|
||||
|
||||
driver.SetScreenSize (width, height);
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose ()
|
||||
{
|
||||
Application.ResetState (true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Reflection;
|
||||
using System.Drawing;
|
||||
|
||||
namespace UnitTests;
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using UnitTests;
|
||||
|
||||
namespace UnitTests_Parallelizable.ApplicationTests;
|
||||
|
||||
public class ApplicationForceDriverTests : FakeDriverBase
|
||||
{
|
||||
[Fact]
|
||||
public void ForceDriver_Does_Not_Changes_If_It_Has_Valid_Value ()
|
||||
{
|
||||
Assert.False (Application.Initialized);
|
||||
Assert.Null (Application.Driver);
|
||||
Assert.Equal (string.Empty, Application.ForceDriver);
|
||||
|
||||
Application.ForceDriver = "fake";
|
||||
Assert.Equal ("fake", Application.ForceDriver);
|
||||
|
||||
Application.ForceDriver = "dotnet";
|
||||
Assert.Equal ("fake", Application.ForceDriver);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForceDriver_Throws_If_Initialized_Changed_To_Another_Value ()
|
||||
{
|
||||
IDriver driver = CreateFakeDriver ();
|
||||
|
||||
Assert.False (Application.Initialized);
|
||||
Assert.Null (Application.Driver);
|
||||
Assert.Equal (string.Empty, Application.ForceDriver);
|
||||
|
||||
Application.Init (driverName: "fake");
|
||||
Assert.True (Application.Initialized);
|
||||
Assert.NotNull (Application.Driver);
|
||||
Assert.Equal ("fake", Application.Driver.GetName ());
|
||||
Assert.Equal (string.Empty, Application.ForceDriver);
|
||||
|
||||
Assert.Throws<InvalidOperationException> (() => Application.ForceDriver = "dotnet");
|
||||
|
||||
Application.ForceDriver = "fake";
|
||||
Assert.Equal ("fake", Application.ForceDriver);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using Moq;
|
||||
using Terminal.Gui.App;
|
||||
|
||||
|
||||
@@ -40,10 +40,13 @@ public class ToAnsiTests : FakeDriverBase
|
||||
Assert.Equal (3, lines.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToAnsi_With_Colors ()
|
||||
[Theory]
|
||||
[InlineData (true, "\u001b[31m", "\u001b[34m")]
|
||||
[InlineData (false, "\u001b[38;2;255;0;0m", "\u001b[38;2;0;0;255")]
|
||||
public void ToAnsi_With_Colors (bool force16Colors, string expectedRed, string expectedBue)
|
||||
{
|
||||
IDriver driver = CreateFakeDriver (10, 2);
|
||||
driver.Force16Colors = force16Colors;
|
||||
|
||||
// Set red foreground
|
||||
driver.CurrentAttribute = new Attribute (Color.Red, Color.Black);
|
||||
@@ -56,26 +59,42 @@ public class ToAnsiTests : FakeDriverBase
|
||||
|
||||
string ansi = driver.ToAnsi ();
|
||||
|
||||
Assert.True (driver.Force16Colors == force16Colors);
|
||||
// Should contain ANSI color codes
|
||||
Assert.Contains ("\u001b[31m", ansi); // Red foreground
|
||||
Assert.Contains ("\u001b[34m", ansi); // Blue foreground
|
||||
Assert.Contains (expectedRed, ansi); // Red foreground
|
||||
Assert.Contains (expectedBue, ansi); // Blue foreground
|
||||
Assert.Contains ("Red", ansi);
|
||||
Assert.Contains ("Blue", ansi);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToAnsi_With_Background_Colors ()
|
||||
[Theory]
|
||||
[InlineData (false, "\u001b[48;2;")]
|
||||
[InlineData (true, "\u001b[41m")]
|
||||
public void ToAnsi_With_Background_Colors (bool force16Colors, string expected)
|
||||
{
|
||||
IDriver driver = CreateFakeDriver (10, 2);
|
||||
Application.Force16Colors = force16Colors;
|
||||
|
||||
// Set background color
|
||||
driver.CurrentAttribute = new Attribute (Color.White, Color.Red);
|
||||
driver.CurrentAttribute = new (Color.White, Color.Red);
|
||||
driver.AddStr ("WhiteOnRed");
|
||||
|
||||
string ansi = driver.ToAnsi ();
|
||||
|
||||
/*
|
||||
The ANSI escape sequence for red background (8-color) is ESC[41m <20> where ESC is \x1b (or \u001b).
|
||||
Examples:
|
||||
<20> C# string: "\u001b[41m" or "\x1b[41m"
|
||||
<20> Reset (clear attributes): "\u001b[0m"
|
||||
Notes:
|
||||
<20> Bright/red background (16-color bright variant) uses ESC[101m ("\u001b[101m").
|
||||
<20> For 24-bit RGB background use ESC[48;2;<r>;<g>;<b>m, e.g. "\u001b[48;2;255;0;0m" for pure red.
|
||||
*/
|
||||
|
||||
Assert.True (driver.Force16Colors == force16Colors);
|
||||
|
||||
// Should contain ANSI background color code
|
||||
Assert.Contains ("\u001b[41m", ansi); // Red background
|
||||
Assert.Contains (expected, ansi); // Red background
|
||||
Assert.Contains ("WhiteOnRed", ansi);
|
||||
}
|
||||
|
||||
@@ -138,10 +157,13 @@ public class ToAnsiTests : FakeDriverBase
|
||||
Assert.Contains ("???", ansi);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToAnsi_Attribute_Changes_Within_Line ()
|
||||
[Theory]
|
||||
[InlineData (true, "\u001b[31m", "\u001b[34m")]
|
||||
[InlineData (false, "\u001b[38;2;", "\u001b[48;2;")]
|
||||
public void ToAnsi_Attribute_Changes_Within_Line (bool force16Colors, string expectedRed, string expectedBlue)
|
||||
{
|
||||
IDriver driver = CreateFakeDriver (20, 1);
|
||||
driver.Force16Colors = force16Colors;
|
||||
|
||||
driver.AddStr ("Normal");
|
||||
driver.CurrentAttribute = new Attribute (Color.Red, Color.Black);
|
||||
@@ -151,10 +173,11 @@ public class ToAnsiTests : FakeDriverBase
|
||||
|
||||
string ansi = driver.ToAnsi ();
|
||||
|
||||
Assert.True (driver.Force16Colors == force16Colors);
|
||||
// Should contain color changes within the line
|
||||
Assert.Contains ("Normal", ansi);
|
||||
Assert.Contains ("\u001b[31m", ansi); // Red
|
||||
Assert.Contains ("\u001b[34m", ansi); // Blue
|
||||
Assert.Contains (expectedRed, ansi); // Red
|
||||
Assert.Contains (expectedBlue, ansi); // Blue
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -223,40 +246,52 @@ public class ToAnsiTests : FakeDriverBase
|
||||
Assert.DoesNotContain ("\u001b[38;2;", ansi); // No RGB codes
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToAnsi_Multiple_Attributes_Per_Line ()
|
||||
[Theory]
|
||||
[InlineData (true, "\u001b[31m", "\u001b[32m", "\u001b[34m", "\u001b[33m", "\u001b[35m", "\u001b[36m")]
|
||||
[InlineData (false, "\u001b[38;2;255;0;0m", "\u001b[38;2;0;128;0m", "\u001b[38;2;0;0;255", "\u001b[38;2;255;255;0m", "\u001b[38;2;255;0;255m", "\u001b[38;2;0;255;255m")]
|
||||
public void ToAnsi_Multiple_Attributes_Per_Line (
|
||||
bool force16Colors,
|
||||
string expectedRed,
|
||||
string expectedGreen,
|
||||
string expectedBlue,
|
||||
string expectedYellow,
|
||||
string expectedMagenta,
|
||||
string expectedCyan
|
||||
)
|
||||
{
|
||||
IDriver driver = CreateFakeDriver (50, 1);
|
||||
driver.Force16Colors = force16Colors;
|
||||
|
||||
// Create a line with many attribute changes
|
||||
string[] colors = { "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan" };
|
||||
string [] colors = { "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan" };
|
||||
|
||||
foreach (string colorName in colors)
|
||||
{
|
||||
Color fg = colorName switch
|
||||
{
|
||||
"Red" => Color.Red,
|
||||
"Green" => Color.Green,
|
||||
"Blue" => Color.Blue,
|
||||
"Yellow" => Color.Yellow,
|
||||
"Magenta" => Color.Magenta,
|
||||
"Cyan" => Color.Cyan,
|
||||
_ => Color.White
|
||||
};
|
||||
{
|
||||
"Red" => Color.Red,
|
||||
"Green" => Color.Green,
|
||||
"Blue" => Color.Blue,
|
||||
"Yellow" => Color.Yellow,
|
||||
"Magenta" => Color.Magenta,
|
||||
"Cyan" => Color.Cyan,
|
||||
_ => Color.White
|
||||
};
|
||||
|
||||
driver.CurrentAttribute = new Attribute (fg, Color.Black);
|
||||
driver.CurrentAttribute = new (fg, Color.Black);
|
||||
driver.AddStr (colorName);
|
||||
}
|
||||
|
||||
string ansi = driver.ToAnsi ();
|
||||
|
||||
Assert.True (driver.Force16Colors == force16Colors);
|
||||
// Should contain multiple color codes
|
||||
Assert.Contains ("\u001b[31m", ansi); // Red
|
||||
Assert.Contains ("\u001b[32m", ansi); // Green
|
||||
Assert.Contains ("\u001b[34m", ansi); // Blue
|
||||
Assert.Contains ("\u001b[33m", ansi); // Yellow
|
||||
Assert.Contains ("\u001b[35m", ansi); // Magenta
|
||||
Assert.Contains ("\u001b[36m", ansi); // Cyan
|
||||
Assert.Contains (expectedRed, ansi); // Red
|
||||
Assert.Contains (expectedGreen, ansi); // Green
|
||||
Assert.Contains (expectedBlue, ansi); // Blue
|
||||
Assert.Contains (expectedYellow, ansi); // Yellow
|
||||
Assert.Contains (expectedMagenta, ansi); // Magenta
|
||||
Assert.Contains (expectedCyan, ansi); // Cyan
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -24,8 +24,8 @@ public class GlobalTestSetup : IDisposable
|
||||
// Reset application state just in case a test changed something.
|
||||
// TODO: Add an Assert to ensure none of the state of Application changed.
|
||||
// TODO: Add an Assert to ensure none of the state of ConfigurationManager changed.
|
||||
CheckDefaultState ();
|
||||
Application.ResetState (true);
|
||||
CheckDefaultState ();
|
||||
}
|
||||
|
||||
// IMPORTANT: Ensure this matches the code in Init_ResetState_Resets_Properties
|
||||
@@ -43,7 +43,7 @@ public class GlobalTestSetup : IDisposable
|
||||
Assert.Null (Application.Mouse.MouseGrabView);
|
||||
|
||||
// Don't check Application.ForceDriver
|
||||
// Assert.Empty (Application.ForceDriver);
|
||||
Assert.Empty (Application.ForceDriver);
|
||||
// Don't check Application.Force16Colors
|
||||
//Assert.False (Application.Force16Colors);
|
||||
Assert.Null (Application.Driver);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#nullable enable
|
||||
using System.Text;
|
||||
using UICatalog;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
namespace UnitTests.TextTests;
|
||||
namespace UnitTests_Parallelizable.TextTests;
|
||||
|
||||
public class TextFormatterDrawTests (ITestOutputHelper output) : FakeDriverBase
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using Xunit.Abstractions;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
namespace UnitTests.TextTests;
|
||||
namespace UnitTests_Parallelizable.TextTests;
|
||||
|
||||
public class TextFormatterJustificationTests (ITestOutputHelper output) : FakeDriverBase
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace UnitTests_Parallelizable.LayoutTests;
|
||||
using UnitTests.Parallelizable;
|
||||
|
||||
namespace UnitTests_Parallelizable.LayoutTests;
|
||||
|
||||
public partial class DimAutoTests
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Text;
|
||||
using UnitTests;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.ViewBase.Dim;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace UnitTests_Parallelizable.LayoutTests;
|
||||
|
||||
public class LayoutTests : GlobalTestSetup
|
||||
public class LayoutTests
|
||||
{
|
||||
#region Constructor Tests
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ using UnitTests.Parallelizable;
|
||||
|
||||
namespace UnitTests_Parallelizable.ViewLayoutEventTests;
|
||||
|
||||
public class ViewLayoutEventTests : GlobalTestSetup
|
||||
public class ViewLayoutEventTests
|
||||
{
|
||||
[Fact]
|
||||
public void View_WidthChanging_Event_Fires ()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using JetBrains.Annotations;
|
||||
using UnitTests.Parallelizable;
|
||||
|
||||
namespace UnitTests_Parallelizable.ViewsTests;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user