mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Migrate 210 tests to UnitTests.Parallelizable, add CreateFakeDriver helper, prove View.Draw() works in parallel tests, and provide comprehensive performance analysis (#4297)
* Initial plan * Migrate Category A test files to UnitTests.Parallelizable (135 tests) Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add 11 ButtonTests to Parallelizable, remove from UnitTests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add comprehensive test migration report Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add comprehensive performance analysis of UnitTests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Migrate 2 Autocomplete tests and add Text tests analysis Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add proof-of-concept: TextFormatter.Draw works in parallel tests with local driver Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add CreateFakeDriver helper to ParallelizableBase and migrate 4 TextFormatterTests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Remove proof-of-concept test from AutocompleteTests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Move Scheme-accessing tests back to UnitTests to fix intermittent failures Co-authored-by: tig <585482+tig@users.noreply.github.com> * Update parallel tests README to document ConfigurationManager/SchemeManager restrictions Co-authored-by: tig <585482+tig@users.noreply.github.com> * Document static member restriction in parallel tests README Co-authored-by: tig <585482+tig@users.noreply.github.com> * Restore accidentally deleted ButtonTests.Accept_Cancel_Event_OnAccept_Returns_True test Co-authored-by: tig <585482+tig@users.noreply.github.com> * Migrate Accept_Cancel_Event_OnAccept_Returns_True test to Parallelizable Co-authored-by: tig <585482+tig@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
@@ -1,195 +0,0 @@
|
||||
namespace Terminal.Gui.ApplicationTests;
|
||||
|
||||
public class StackExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Stack_Toplevels_Contains ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
|
||||
Assert.True (Toplevels.Contains (new Window { Id = "w2" }, comparer));
|
||||
Assert.False (Toplevels.Contains (new Toplevel { Id = "top2" }, comparer));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_CreateToplevels ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
int index = Toplevels.Count - 1;
|
||||
|
||||
foreach (Toplevel top in Toplevels)
|
||||
{
|
||||
if (top.GetType () == typeof (Toplevel))
|
||||
{
|
||||
Assert.Equal ("Top", top.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal ($"w{index}", top.Id);
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("w4", tops [0].Id);
|
||||
Assert.Equal ("w3", tops [1].Id);
|
||||
Assert.Equal ("w2", tops [2].Id);
|
||||
Assert.Equal ("w1", tops [3].Id);
|
||||
Assert.Equal ("Top", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_FindDuplicates ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
|
||||
Toplevels.Push (new Toplevel { Id = "w4" });
|
||||
Toplevels.Push (new Toplevel { Id = "w1" });
|
||||
|
||||
Toplevel [] dup = Toplevels.FindDuplicates (comparer).ToArray ();
|
||||
|
||||
Assert.Equal ("w4", dup [0].Id);
|
||||
Assert.Equal ("w1", dup [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_MoveNext ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
Toplevels.MoveNext ();
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("w3", tops [0].Id);
|
||||
Assert.Equal ("w2", tops [1].Id);
|
||||
Assert.Equal ("w1", tops [2].Id);
|
||||
Assert.Equal ("Top", tops [3].Id);
|
||||
Assert.Equal ("w4", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_MovePrevious ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
Toplevels.MovePrevious ();
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("Top", tops [0].Id);
|
||||
Assert.Equal ("w4", tops [1].Id);
|
||||
Assert.Equal ("w3", tops [2].Id);
|
||||
Assert.Equal ("w2", tops [3].Id);
|
||||
Assert.Equal ("w1", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_MoveTo ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
var valueToMove = new Window { Id = "w1" };
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
|
||||
Toplevels.MoveTo (valueToMove, 1, comparer);
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("w4", tops [0].Id);
|
||||
Assert.Equal ("w1", tops [1].Id);
|
||||
Assert.Equal ("w3", tops [2].Id);
|
||||
Assert.Equal ("w2", tops [3].Id);
|
||||
Assert.Equal ("Top", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_MoveTo_From_Last_To_Top ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
var valueToMove = new Window { Id = "Top" };
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
|
||||
Toplevels.MoveTo (valueToMove, 0, comparer);
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("Top", tops [0].Id);
|
||||
Assert.Equal ("w4", tops [1].Id);
|
||||
Assert.Equal ("w3", tops [2].Id);
|
||||
Assert.Equal ("w2", tops [3].Id);
|
||||
Assert.Equal ("w1", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_Replace ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
var valueToReplace = new Window { Id = "w1" };
|
||||
var valueToReplaceWith = new Window { Id = "new" };
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
|
||||
Toplevels.Replace (valueToReplace, valueToReplaceWith, comparer);
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("w4", tops [0].Id);
|
||||
Assert.Equal ("w3", tops [1].Id);
|
||||
Assert.Equal ("w2", tops [2].Id);
|
||||
Assert.Equal ("new", tops [3].Id);
|
||||
Assert.Equal ("Top", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Stack_Toplevels_Swap ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
var valueToSwapFrom = new Window { Id = "w3" };
|
||||
var valueToSwapTo = new Window { Id = "w1" };
|
||||
var comparer = new ToplevelEqualityComparer ();
|
||||
Toplevels.Swap (valueToSwapFrom, valueToSwapTo, comparer);
|
||||
|
||||
Toplevel [] tops = Toplevels.ToArray ();
|
||||
|
||||
Assert.Equal ("w4", tops [0].Id);
|
||||
Assert.Equal ("w1", tops [1].Id);
|
||||
Assert.Equal ("w2", tops [2].Id);
|
||||
Assert.Equal ("w3", tops [3].Id);
|
||||
Assert.Equal ("Top", tops [^1].Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToplevelEqualityComparer_GetHashCode ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = CreateToplevels ();
|
||||
|
||||
// Only allows unique keys
|
||||
HashSet<int> hCodes = new ();
|
||||
|
||||
foreach (Toplevel top in Toplevels)
|
||||
{
|
||||
Assert.True (hCodes.Add (top.GetHashCode ()));
|
||||
}
|
||||
}
|
||||
|
||||
private Stack<Toplevel> CreateToplevels ()
|
||||
{
|
||||
Stack<Toplevel> Toplevels = new ();
|
||||
|
||||
Toplevels.Push (new Toplevel { Id = "Top" });
|
||||
Toplevels.Push (new Window { Id = "w1" });
|
||||
Toplevels.Push (new Window { Id = "w2" });
|
||||
Toplevels.Push (new Window { Id = "w3" });
|
||||
Toplevels.Push (new Window { Id = "w4" });
|
||||
|
||||
return Toplevels;
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Configuration.ConfigurationManager;
|
||||
|
||||
namespace Terminal.Gui.ConfigurationTests;
|
||||
|
||||
public class ThemeManagerTests (ITestOutputHelper output)
|
||||
{
|
||||
[Fact]
|
||||
public void ResetToCurrentValues_Adds_Default_Theme ()
|
||||
{
|
||||
try
|
||||
{
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
Assert.NotEmpty (ThemeManager.Themes!);
|
||||
|
||||
ThemeManager.UpdateToCurrentValues ();
|
||||
|
||||
Assert.NotEmpty (ThemeManager.Themes!);
|
||||
|
||||
// Default theme exists
|
||||
Assert.NotNull (ThemeManager.Themes? [ThemeManager.DEFAULT_THEME_NAME]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
}
|
||||
|
||||
// ResetToCurrentValues
|
||||
|
||||
// OnThemeChanged
|
||||
|
||||
#region Tests Settings["Theme"] and ThemeManager.Theme
|
||||
|
||||
[Fact]
|
||||
public void Theme_Settings_Theme_Equals_ThemeManager_Theme ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
|
||||
Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Theme_Enabled_Settings_Theme_Equals_ThemeManager_Theme ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
|
||||
Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Theme_Set_Sets ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
|
||||
|
||||
ThemeManager.Theme = "Test";
|
||||
Assert.Equal ("Test", ThemeManager.Theme);
|
||||
Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
|
||||
Assert.Equal ("Test", Settings! ["Theme"].PropertyValue);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Theme_ResetToHardCodedDefaults_Sets_To_Default ()
|
||||
{
|
||||
try
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme);
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
Assert.Equal ("Default", ThemeManager.Theme);
|
||||
|
||||
ThemeManager.Theme = "Test";
|
||||
Assert.Equal ("Test", ThemeManager.Theme);
|
||||
Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme);
|
||||
Assert.Equal ("Test", Settings! ["Theme"].PropertyValue);
|
||||
|
||||
ResetToHardCodedDefaults ();
|
||||
Assert.Equal ("Default", ThemeManager.Theme);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Disable(true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Tests Settings["Theme"] and ThemeManager.Theme
|
||||
|
||||
#region Tests Settings["Themes"] and ThemeManager.Themes
|
||||
|
||||
[Fact]
|
||||
public void Themes_Set_Throws_If_Not_Enabled ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
Assert.Throws<InvalidOperationException> (() => ThemeManager.Themes = new ());
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Themes_Set_Sets_If_Enabled ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
|
||||
// Use ConcurrentDictionary instead of a regular dictionary
|
||||
ThemeManager.Themes = new ConcurrentDictionary<string, ThemeScope> (
|
||||
new Dictionary<string, ThemeScope>
|
||||
{
|
||||
{ "Default", new ThemeScope() },
|
||||
{ "test", new ThemeScope() }
|
||||
},
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
);
|
||||
|
||||
Assert.Contains ("test", ThemeManager.Themes!);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Themes_Set_Throws_If_No_Default_Theme_In_Dictionary ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
|
||||
Assert.Throws<InvalidOperationException> (
|
||||
() => ThemeManager.Themes = new ConcurrentDictionary<string, ThemeScope> (
|
||||
new Dictionary<string, ThemeScope>
|
||||
{
|
||||
{ "test", new ThemeScope() }
|
||||
},
|
||||
StringComparer.InvariantCultureIgnoreCase
|
||||
));
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Themes_Get () { }
|
||||
|
||||
#endregion Tests Settings["Themes"] and ThemeManager.Themes
|
||||
|
||||
[Fact]
|
||||
public void Themes_TryAdd_Adds ()
|
||||
{
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
// Verify that the Themes dictionary contains only the Default theme
|
||||
Assert.Single (ThemeManager.Themes!);
|
||||
Assert.Contains (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Themes!);
|
||||
|
||||
var theme = new ThemeScope ();
|
||||
theme.LoadHardCodedDefaults ();
|
||||
Assert.NotEmpty (theme);
|
||||
|
||||
Assert.True (ThemeManager.Themes!.TryAdd ("testTheme", theme));
|
||||
Assert.Equal (2, ThemeManager.Themes.Count);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Apply_Applies ()
|
||||
{
|
||||
Assert.False (IsEnabled);
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
var theme = new ThemeScope ();
|
||||
theme.LoadHardCodedDefaults ();
|
||||
Assert.NotEmpty (theme);
|
||||
|
||||
Assert.True (ThemeManager.Themes!.TryAdd ("testTheme", theme));
|
||||
Assert.Equal (2, ThemeManager.Themes.Count);
|
||||
|
||||
Assert.Equal (LineStyle.Rounded, FrameView.DefaultBorderStyle);
|
||||
theme ["FrameView.DefaultBorderStyle"].PropertyValue = LineStyle.Double; // default is Single
|
||||
|
||||
ThemeManager.Theme = "testTheme";
|
||||
ThemeManager.Themes! [ThemeManager.Theme]!.Apply ();
|
||||
|
||||
Assert.Equal (LineStyle.Double, FrameView.DefaultBorderStyle);
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Theme_Reload_Consistency ()
|
||||
{
|
||||
try
|
||||
{
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
// BUGBUG: Setting Schemes to empty array is not valid!
|
||||
// Create a test theme
|
||||
RuntimeConfig = """
|
||||
{
|
||||
"Theme": "TestTheme",
|
||||
"Themes": [
|
||||
{
|
||||
"TestTheme": {
|
||||
"Schemes": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
// Load the test theme
|
||||
ThrowOnJsonErrors = true;
|
||||
Load (ConfigLocations.Runtime);
|
||||
Assert.Equal ("TestTheme", ThemeManager.Theme);
|
||||
|
||||
// Now reset everything and reload
|
||||
ResetToHardCodedDefaults ();
|
||||
|
||||
// Verify we're back to default
|
||||
Assert.Equal ("Default", ThemeManager.Theme);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void In_Memory_Themes_Size_Is_Reasonable ()
|
||||
{
|
||||
output.WriteLine ($"Start: Color size: {(MemorySizeEstimator.EstimateSize (Color.Red))} b");
|
||||
|
||||
output.WriteLine ($"Start: Attribute size: {(MemorySizeEstimator.EstimateSize (Attribute.Default))} b");
|
||||
|
||||
output.WriteLine ($"Start: Base Scheme size: {(MemorySizeEstimator.EstimateSize (Scheme.GetHardCodedSchemes ()))} b");
|
||||
|
||||
output.WriteLine ($"Start: PropertyInfo size: {(MemorySizeEstimator.EstimateSize (ConfigurationManager.Settings! ["Application.QuitKey"]))} b");
|
||||
|
||||
ThemeScope themeScope = new ThemeScope ();
|
||||
output.WriteLine ($"Start: ThemeScope ({themeScope.Count}) size: {(MemorySizeEstimator.EstimateSize (themeScope))} b");
|
||||
|
||||
themeScope.AddValue ("Schemes", Scheme.GetHardCodedSchemes ());
|
||||
output.WriteLine ($"Start: ThemeScope ({themeScope.Count}) size: {(MemorySizeEstimator.EstimateSize (themeScope))} b");
|
||||
|
||||
output.WriteLine ($"Start: HardCoded Schemes ({SchemeManager.Schemes!.Count}) size: {(MemorySizeEstimator.EstimateSize (SchemeManager.Schemes!))} b");
|
||||
|
||||
output.WriteLine ($"Start: Themes dictionary ({ThemeManager.Themes!.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb");
|
||||
|
||||
Enable (ConfigLocations.HardCoded);
|
||||
|
||||
output.WriteLine ($"Enabled: Themes dictionary ({ThemeManager.Themes.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb");
|
||||
|
||||
Load (ConfigLocations.LibraryResources);
|
||||
output.WriteLine ($"After Load: Themes dictionary ({ThemeManager.Themes!.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb");
|
||||
|
||||
output.WriteLine ($"Total Settings Size: {(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb");
|
||||
|
||||
string json = ConfigurationManager.SourcesManager?.ToJson (Settings)!;
|
||||
|
||||
// In memory size should be less than the size of the json
|
||||
output.WriteLine ($"JSON size: {json.Length / 1024} Kb");
|
||||
|
||||
Assert.True (70000 > MemorySizeEstimator.EstimateSize (ThemeManager.Themes!), $"In memory size ({(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb) is > json size ({json.Length / 1024} Kb)");
|
||||
|
||||
Disable (resetToHardCodedDefaults: true);
|
||||
}
|
||||
}
|
||||
@@ -1,819 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Terminal.Gui.DriverTests;
|
||||
|
||||
public class AnsiResponseParserTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly AnsiResponseParser<int> _parser1 = new ();
|
||||
private readonly AnsiResponseParser _parser2 = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Used for the T value in batches that are passed to the AnsiResponseParser<int> (parser1)
|
||||
/// </summary>
|
||||
private int _tIndex;
|
||||
|
||||
[Fact]
|
||||
public void TestInputProcessing ()
|
||||
{
|
||||
string ansiStream = "\u001b[<0;10;20M"
|
||||
+ // ANSI escape for mouse move at (10, 20)
|
||||
"Hello"
|
||||
+ // User types "Hello"
|
||||
"\u001b[0c"; // Device Attributes response (e.g., terminal identification i.e. DAR)
|
||||
|
||||
string? response1 = null;
|
||||
string? response2 = null;
|
||||
|
||||
var i = 0;
|
||||
|
||||
// Imagine that we are expecting a DAR
|
||||
_parser1.ExpectResponse ("c", s => response1 = s, null, false);
|
||||
_parser2.ExpectResponse ("c", s => response2 = s, null, false);
|
||||
|
||||
// First char is Escape which we must consume incase what follows is the DAR
|
||||
AssertConsumed (ansiStream, ref i); // Esc
|
||||
|
||||
for (var c = 0; c < "[<0;10;20".Length; c++)
|
||||
{
|
||||
AssertConsumed (ansiStream, ref i);
|
||||
}
|
||||
|
||||
// We see the M terminator
|
||||
AssertReleased (ansiStream, ref i, "\u001b[<0;10;20M");
|
||||
|
||||
// Regular user typing
|
||||
for (var c = 0; c < "Hello".Length; c++)
|
||||
{
|
||||
AssertIgnored (ansiStream, "Hello" [c], ref i);
|
||||
}
|
||||
|
||||
// Now we have entered the actual DAR we should be consuming these
|
||||
for (var c = 0; c < "\u001b[0".Length; c++)
|
||||
{
|
||||
AssertConsumed (ansiStream, ref i);
|
||||
}
|
||||
|
||||
// Consume the terminator 'c' and expect this to call the above event
|
||||
Assert.Null (response1);
|
||||
Assert.Null (response1);
|
||||
AssertConsumed (ansiStream, ref i);
|
||||
Assert.NotNull (response2);
|
||||
Assert.Equal ("\u001b[0c", response2);
|
||||
Assert.NotNull (response2);
|
||||
Assert.Equal ("\u001b[0c", response2);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("\u001b[<0;10;20MHi\u001b[0c", "c", "\u001b[0c", "\u001b[<0;10;20MHi")]
|
||||
[InlineData ("\u001b[<1;15;25MYou\u001b[1c", "c", "\u001b[1c", "\u001b[<1;15;25MYou")]
|
||||
[InlineData ("\u001b[0cHi\u001b[0c", "c", "\u001b[0c", "Hi\u001b[0c")]
|
||||
[InlineData ("\u001b[<0;0;0MHe\u001b[3c", "c", "\u001b[3c", "\u001b[<0;0;0MHe")]
|
||||
[InlineData ("\u001b[<0;1;2Da\u001b[0c\u001b[1c", "c", "\u001b[0c", "\u001b[<0;1;2Da\u001b[1c")]
|
||||
[InlineData ("\u001b[1;1M\u001b[3cAn", "c", "\u001b[3c", "\u001b[1;1MAn")]
|
||||
[InlineData ("hi\u001b[2c\u001b[<5;5;5m", "c", "\u001b[2c", "hi\u001b[<5;5;5m")]
|
||||
[InlineData ("\u001b[3c\u001b[4c\u001b[<0;0;0MIn", "c", "\u001b[3c", "\u001b[4c\u001b[<0;0;0MIn")]
|
||||
[InlineData ("\u001b[<1;2;3M\u001b[0c\u001b[<1;2;3M\u001b[2c", "c", "\u001b[0c", "\u001b[<1;2;3M\u001b[<1;2;3M\u001b[2c")]
|
||||
[InlineData ("\u001b[<0;1;1MHi\u001b[6c\u001b[2c\u001b[<1;0;0MT", "c", "\u001b[6c", "\u001b[<0;1;1MHi\u001b[2c\u001b[<1;0;0MT")]
|
||||
[InlineData ("Te\u001b[<2;2;2M\u001b[7c", "c", "\u001b[7c", "Te\u001b[<2;2;2M")]
|
||||
[InlineData ("\u001b[0c\u001b[<0;0;0M\u001b[3c\u001b[0c\u001b[1;0MT", "c", "\u001b[0c", "\u001b[<0;0;0M\u001b[3c\u001b[0c\u001b[1;0MT")]
|
||||
[InlineData ("\u001b[0;0M\u001b[<0;0;0M\u001b[3cT\u001b[1c", "c", "\u001b[3c", "\u001b[0;0M\u001b[<0;0;0MT\u001b[1c")]
|
||||
[InlineData ("\u001b[3c\u001b[<0;0;0M\u001b[0c\u001b[<1;1;1MIn\u001b[1c", "c", "\u001b[3c", "\u001b[<0;0;0M\u001b[0c\u001b[<1;1;1MIn\u001b[1c")]
|
||||
[InlineData ("\u001b[<5;5;5M\u001b[7cEx\u001b[8c", "c", "\u001b[7c", "\u001b[<5;5;5MEx\u001b[8c")]
|
||||
|
||||
// Random characters and mixed inputs
|
||||
[InlineData ("\u001b[<1;1;1MJJ\u001b[9c", "c", "\u001b[9c", "\u001b[<1;1;1MJJ")] // Mixed text
|
||||
[InlineData ("Be\u001b[0cAf", "c", "\u001b[0c", "BeAf")] // Escape in the middle of the string
|
||||
[InlineData ("\u001b[<0;0;0M\u001b[2cNot e", "c", "\u001b[2c", "\u001b[<0;0;0MNot e")] // Unexpected sequence followed by text
|
||||
[InlineData (
|
||||
"Just te\u001b[<0;0;0M\u001b[3c\u001b[2c\u001b[4c",
|
||||
"c",
|
||||
"\u001b[3c",
|
||||
"Just te\u001b[<0;0;0M\u001b[2c\u001b[4c")] // Multiple unexpected responses
|
||||
[InlineData (
|
||||
"\u001b[1;2;3M\u001b[0c\u001b[2;2M\u001b[0;0;0MTe",
|
||||
"c",
|
||||
"\u001b[0c",
|
||||
"\u001b[1;2;3M\u001b[2;2M\u001b[0;0;0MTe")] // Multiple commands with responses
|
||||
[InlineData ("\u001b[<3;3;3Mabc\u001b[4cde", "c", "\u001b[4c", "\u001b[<3;3;3Mabcde")] // Escape sequences mixed with regular text
|
||||
|
||||
// Edge cases
|
||||
[InlineData ("\u001b[0c\u001b[0c\u001b[0c", "c", "\u001b[0c", "\u001b[0c\u001b[0c")] // Multiple identical responses
|
||||
[InlineData ("", "c", "", "")] // Empty input
|
||||
[InlineData ("Normal", "c", "", "Normal")] // No escape sequences
|
||||
[InlineData ("\u001b[<0;0;0M", "c", "", "\u001b[<0;0;0M")] // Escape sequence only
|
||||
[InlineData ("\u001b[1;2;3M\u001b[0c", "c", "\u001b[0c", "\u001b[1;2;3M")] // Last response consumed
|
||||
[InlineData ("Inpu\u001b[0c\u001b[1;0;0M", "c", "\u001b[0c", "Inpu\u001b[1;0;0M")] // Single input followed by escape
|
||||
[InlineData ("\u001b[2c\u001b[<5;6;7MDa", "c", "\u001b[2c", "\u001b[<5;6;7MDa")] // Multiple escape sequences followed by text
|
||||
[InlineData ("\u001b[0cHi\u001b[1cGo", "c", "\u001b[0c", "Hi\u001b[1cGo")] // Normal text with multiple escape sequences
|
||||
[InlineData ("\u001b[<1;1;1MTe", "c", "", "\u001b[<1;1;1MTe")]
|
||||
|
||||
// Add more test cases here...
|
||||
public void TestInputSequences (string ansiStream, string? expectedTerminator, string expectedResponse, string expectedOutput)
|
||||
{
|
||||
var swGenBatches = Stopwatch.StartNew ();
|
||||
var tests = 0;
|
||||
|
||||
string [] [] permutations = GetBatchPermutations (ansiStream, 5).ToArray ();
|
||||
|
||||
swGenBatches.Stop ();
|
||||
var swRunTest = Stopwatch.StartNew ();
|
||||
|
||||
foreach (string [] batchSet in permutations)
|
||||
{
|
||||
_tIndex = 0;
|
||||
var response1 = string.Empty;
|
||||
var response2 = string.Empty;
|
||||
|
||||
// Register the expected response with the given terminator
|
||||
_parser1.ExpectResponse (expectedTerminator, s => response1 = s, null, false);
|
||||
_parser2.ExpectResponse (expectedTerminator, s => response2 = s, null, false);
|
||||
|
||||
// Process the input
|
||||
var actualOutput1 = new StringBuilder ();
|
||||
var actualOutput2 = new StringBuilder ();
|
||||
|
||||
foreach (string batch in batchSet)
|
||||
{
|
||||
IEnumerable<Tuple<char, int>> output1 = _parser1.ProcessInput (StringToBatch (batch));
|
||||
actualOutput1.Append (BatchToString (output1));
|
||||
|
||||
string output2 = _parser2.ProcessInput (batch);
|
||||
actualOutput2.Append (output2);
|
||||
}
|
||||
|
||||
// Assert the final output minus the expected response
|
||||
Assert.Equal (expectedOutput, actualOutput1.ToString ());
|
||||
Assert.Equal (expectedResponse, response1);
|
||||
Assert.Equal (expectedOutput, actualOutput2.ToString ());
|
||||
Assert.Equal (expectedResponse, response2);
|
||||
tests++;
|
||||
}
|
||||
|
||||
output.WriteLine ($"Tested {tests} in {swRunTest.ElapsedMilliseconds} ms (gen batches took {swGenBatches.ElapsedMilliseconds} ms)");
|
||||
}
|
||||
|
||||
public static IEnumerable<object? []> TestInputSequencesExact_Cases ()
|
||||
{
|
||||
yield return
|
||||
[
|
||||
"Esc Only",
|
||||
null,
|
||||
new []
|
||||
{
|
||||
new StepExpectation ('\u001b', AnsiResponseParserState.ExpectingEscapeSequence, string.Empty)
|
||||
}
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"Esc Hi with intermediate",
|
||||
'c',
|
||||
new []
|
||||
{
|
||||
new StepExpectation ('\u001b', AnsiResponseParserState.ExpectingEscapeSequence, string.Empty),
|
||||
new StepExpectation (
|
||||
'H',
|
||||
AnsiResponseParserState.InResponse,
|
||||
string.Empty), // H is known terminator and not expected one so here we release both chars
|
||||
new StepExpectation ('\u001b', AnsiResponseParserState.ExpectingEscapeSequence, "\u001bH"),
|
||||
new StepExpectation ('[', AnsiResponseParserState.InResponse, string.Empty),
|
||||
new StepExpectation ('0', AnsiResponseParserState.InResponse, string.Empty),
|
||||
new StepExpectation (
|
||||
'c',
|
||||
AnsiResponseParserState.Normal,
|
||||
string.Empty,
|
||||
"\u001b[0c"), // c is expected terminator so here we swallow input and populate expected response
|
||||
new StepExpectation ('\u001b', AnsiResponseParserState.ExpectingEscapeSequence, string.Empty)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
public class StepExpectation ()
|
||||
{
|
||||
/// <summary>
|
||||
/// The input character to feed into the parser at this step of the test
|
||||
/// </summary>
|
||||
public char Input { get; }
|
||||
|
||||
/// <summary>
|
||||
/// What should the state of the parser be after the <see cref="Input"/>
|
||||
/// is fed in.
|
||||
/// </summary>
|
||||
public AnsiResponseParserState ExpectedStateAfterOperation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If this step should release one or more characters, put them here.
|
||||
/// </summary>
|
||||
public string ExpectedRelease { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// If this step should result in a completing of detection of ANSI response
|
||||
/// then put the expected full response sequence here.
|
||||
/// </summary>
|
||||
public string ExpectedAnsiResponse { get; } = string.Empty;
|
||||
|
||||
public StepExpectation (
|
||||
char input,
|
||||
AnsiResponseParserState expectedStateAfterOperation,
|
||||
string expectedRelease = "",
|
||||
string expectedAnsiResponse = ""
|
||||
) : this ()
|
||||
{
|
||||
Input = input;
|
||||
ExpectedStateAfterOperation = expectedStateAfterOperation;
|
||||
ExpectedRelease = expectedRelease;
|
||||
ExpectedAnsiResponse = expectedAnsiResponse;
|
||||
}
|
||||
}
|
||||
|
||||
[MemberData (nameof (TestInputSequencesExact_Cases))]
|
||||
[Theory]
|
||||
public void TestInputSequencesExact (string caseName, char? terminator, IEnumerable<StepExpectation> expectedStates)
|
||||
{
|
||||
output.WriteLine ("Running test case:" + caseName);
|
||||
|
||||
var parser = new AnsiResponseParser ();
|
||||
string? response = null;
|
||||
|
||||
if (terminator.HasValue)
|
||||
{
|
||||
parser.ExpectResponse (terminator.Value.ToString (), s => response = s, null, false);
|
||||
}
|
||||
|
||||
var step = 0;
|
||||
|
||||
foreach (StepExpectation state in expectedStates)
|
||||
{
|
||||
step++;
|
||||
|
||||
// If we expect the response to be detected at this step
|
||||
if (!string.IsNullOrWhiteSpace (state.ExpectedAnsiResponse))
|
||||
{
|
||||
// Then before passing input it should be null
|
||||
Assert.Null (response);
|
||||
}
|
||||
|
||||
string actual = parser.ProcessInput (state.Input.ToString ());
|
||||
|
||||
Assert.Equal (state.ExpectedRelease, actual);
|
||||
Assert.Equal (state.ExpectedStateAfterOperation, parser.State);
|
||||
|
||||
// If we expect the response to be detected at this step
|
||||
if (!string.IsNullOrWhiteSpace (state.ExpectedAnsiResponse))
|
||||
{
|
||||
// And after passing input it shuld be the expected value
|
||||
Assert.Equal (state.ExpectedAnsiResponse, response);
|
||||
}
|
||||
|
||||
output.WriteLine ($"Step {step} passed");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReleasesEscapeAfterTimeout ()
|
||||
{
|
||||
var input = "\u001b";
|
||||
var i = 0;
|
||||
|
||||
// Esc on its own looks like it might be an esc sequence so should be consumed
|
||||
AssertConsumed (input, ref i);
|
||||
|
||||
// We should know when the state changed
|
||||
Assert.Equal (AnsiResponseParserState.ExpectingEscapeSequence, _parser1.State);
|
||||
Assert.Equal (AnsiResponseParserState.ExpectingEscapeSequence, _parser2.State);
|
||||
|
||||
Assert.Equal (DateTime.Now.Date, _parser1.StateChangedAt.Date);
|
||||
Assert.Equal (DateTime.Now.Date, _parser2.StateChangedAt.Date);
|
||||
|
||||
AssertManualReleaseIs (input);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TwoEscapesInARow ()
|
||||
{
|
||||
// Example user presses Esc key then a DAR comes in
|
||||
var input = "\u001b\u001b";
|
||||
var i = 0;
|
||||
|
||||
// First Esc gets grabbed
|
||||
AssertConsumed (input, ref i);
|
||||
|
||||
// Upon getting the second Esc we should release the first
|
||||
AssertReleased (input, ref i, "\u001b", 0);
|
||||
|
||||
// Assume 50ms or something has passed, lets force release as no new content
|
||||
|
||||
// It should be the second escape that gets released (i.e. index 1)
|
||||
AssertManualReleaseIs ("\u001b", 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestLateResponses ()
|
||||
{
|
||||
var p = new AnsiResponseParser ();
|
||||
|
||||
string? responseA = null;
|
||||
string? responseB = null;
|
||||
|
||||
p.ExpectResponse ("z", r => responseA = r, null, false);
|
||||
|
||||
// Some time goes by without us seeing a response
|
||||
p.StopExpecting ("z", false);
|
||||
|
||||
// Send our new request
|
||||
p.ExpectResponse ("z", r => responseB = r, null, false);
|
||||
|
||||
// Because we gave up on getting A, we should expect the response to be to our new request
|
||||
Assert.Empty (p.ProcessInput ("\u001b[<1;2z"));
|
||||
Assert.Null (responseA);
|
||||
Assert.Equal ("\u001b[<1;2z", responseB);
|
||||
|
||||
// Oh looks like we got one late after all - swallow it
|
||||
Assert.Empty (p.ProcessInput ("\u001b[0000z"));
|
||||
|
||||
// Do not expect late responses to be populated back to your variable
|
||||
Assert.Null (responseA);
|
||||
Assert.Equal ("\u001b[<1;2z", responseB);
|
||||
|
||||
// We now have no outstanding requests (late or otherwise) so new ansi codes should just fall through
|
||||
Assert.Equal ("\u001b[111z", p.ProcessInput ("\u001b[111z"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestPersistentResponses ()
|
||||
{
|
||||
var p = new AnsiResponseParser ();
|
||||
|
||||
var m = 0;
|
||||
var M = 1;
|
||||
|
||||
p.ExpectResponse ("m", _ => m++, null, true);
|
||||
p.ExpectResponse ("M", _ => M++, null, true);
|
||||
|
||||
// Act - Feed input strings containing ANSI sequences
|
||||
p.ProcessInput ("\u001b[<0;10;10m"); // Should match and increment `m`
|
||||
p.ProcessInput ("\u001b[<0;20;20m"); // Should match and increment `m`
|
||||
p.ProcessInput ("\u001b[<0;30;30M"); // Should match and increment `M`
|
||||
p.ProcessInput ("\u001b[<0;40;40M"); // Should match and increment `M`
|
||||
p.ProcessInput ("\u001b[<0;50;50M"); // Should match and increment `M`
|
||||
|
||||
// Assert - Verify that counters reflect the expected counts of each terminator
|
||||
Assert.Equal (2, m); // Expected two `m` responses
|
||||
Assert.Equal (4, M); // Expected three `M` responses plus the initial value of 1
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestPersistentResponses_WithMetadata ()
|
||||
{
|
||||
AnsiResponseParser<int> p = new ();
|
||||
|
||||
// ReSharper disable once NotAccessedVariable
|
||||
var m = 0;
|
||||
|
||||
List<Tuple<char, int>> result = new ();
|
||||
|
||||
p.ExpectResponseT (
|
||||
"m",
|
||||
r =>
|
||||
{
|
||||
result = r.ToList ();
|
||||
m++;
|
||||
},
|
||||
null,
|
||||
true);
|
||||
|
||||
// Act - Feed input strings containing ANSI sequences
|
||||
p.ProcessInput (StringToBatch ("\u001b[<0;10;10m")); // Should match and increment `m`
|
||||
|
||||
// Prepare expected result:
|
||||
List<Tuple<char, int>> expected = new()
|
||||
{
|
||||
Tuple.Create ('\u001b', 0), // Escape character
|
||||
Tuple.Create ('[', 1),
|
||||
Tuple.Create ('<', 2),
|
||||
Tuple.Create ('0', 3),
|
||||
Tuple.Create (';', 4),
|
||||
Tuple.Create ('1', 5),
|
||||
Tuple.Create ('0', 6),
|
||||
Tuple.Create (';', 7),
|
||||
Tuple.Create ('1', 8),
|
||||
Tuple.Create ('0', 9),
|
||||
Tuple.Create ('m', 10)
|
||||
};
|
||||
|
||||
Assert.Equal (expected.Count, result.Count); // Ensure the count is as expected
|
||||
Assert.True (expected.SequenceEqual (result), "The result does not match the expected output."); // Check the actual content
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldSwallowUnknownResponses_WhenDelegateSaysSo ()
|
||||
{
|
||||
// Swallow all unknown escape codes
|
||||
_parser1.UnexpectedResponseHandler = _ => true;
|
||||
_parser2.UnknownResponseHandler = _ => true;
|
||||
|
||||
AssertReleased (
|
||||
"Just te\u001b[<0;0;0M\u001b[3c\u001b[2c\u001b[4cst",
|
||||
"Just test",
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
28,
|
||||
29);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UnknownResponses_ParameterShouldMatch ()
|
||||
{
|
||||
// Track unknown responses passed to the UnexpectedResponseHandler
|
||||
List<string> unknownResponses = new ();
|
||||
|
||||
// Set up the UnexpectedResponseHandler to log each unknown response
|
||||
_parser1.UnexpectedResponseHandler = r1 =>
|
||||
{
|
||||
unknownResponses.Add (BatchToString (r1));
|
||||
|
||||
return true; // Return true to swallow unknown responses
|
||||
};
|
||||
|
||||
_parser2.UnknownResponseHandler = r2 =>
|
||||
{
|
||||
// parsers should be agreeing on what these responses are!
|
||||
Assert.Equal (unknownResponses.Last (), r2);
|
||||
|
||||
return true; // Return true to swallow unknown responses
|
||||
};
|
||||
|
||||
// Input with known and unknown responses
|
||||
AssertReleased (
|
||||
"Just te\u001b[<0;0;0M\u001b[3c\u001b[2c\u001b[4cst",
|
||||
"Just test");
|
||||
|
||||
// Expected unknown responses (ANSI sequences that are unknown)
|
||||
List<string> expectedUnknownResponses = new()
|
||||
{
|
||||
"\u001b[<0;0;0M",
|
||||
"\u001b[3c",
|
||||
"\u001b[2c",
|
||||
"\u001b[4c"
|
||||
};
|
||||
|
||||
// Assert that the UnexpectedResponseHandler was called with the correct unknown responses
|
||||
Assert.Equal (expectedUnknownResponses.Count, unknownResponses.Count);
|
||||
Assert.Equal (expectedUnknownResponses, unknownResponses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParserDetectsMouse ()
|
||||
{
|
||||
// ANSI escape sequence for mouse down (using a generic format example)
|
||||
const string MOUSE_DOWN = "\u001B[<0;12;32M";
|
||||
|
||||
// ANSI escape sequence for Device Attribute Response (e.g., Terminal identifying itself)
|
||||
const string DEVICE_ATTRIBUTE_RESPONSE = "\u001B[?1;2c";
|
||||
|
||||
// ANSI escape sequence for mouse up (using a generic format example)
|
||||
const string MOUSE_UP = "\u001B[<0;25;50m";
|
||||
|
||||
var parser = new AnsiResponseParser ();
|
||||
|
||||
parser.HandleMouse = true;
|
||||
string? foundDar = null;
|
||||
List<MouseEventArgs> mouseEventArgs = new ();
|
||||
|
||||
parser.Mouse += (s, e) => mouseEventArgs.Add (e);
|
||||
parser.ExpectResponse ("c", dar => foundDar = dar, null, false);
|
||||
string released = parser.ProcessInput ("a" + MOUSE_DOWN + "asdf" + DEVICE_ATTRIBUTE_RESPONSE + "bbcc" + MOUSE_UP + "sss");
|
||||
|
||||
Assert.Equal ("aasdfbbccsss", released);
|
||||
|
||||
Assert.Equal (2, mouseEventArgs.Count);
|
||||
|
||||
Assert.NotNull (foundDar);
|
||||
Assert.Equal (DEVICE_ATTRIBUTE_RESPONSE, foundDar);
|
||||
|
||||
Assert.True (mouseEventArgs [0].IsPressed);
|
||||
|
||||
// Mouse positions in ANSI are 1 based so actual Terminal.Gui Screen positions are x-1,y-1
|
||||
Assert.Equal (11, mouseEventArgs [0].Position.X);
|
||||
Assert.Equal (31, mouseEventArgs [0].Position.Y);
|
||||
|
||||
Assert.True (mouseEventArgs [1].IsReleased);
|
||||
Assert.Equal (24, mouseEventArgs [1].Position.X);
|
||||
Assert.Equal (49, mouseEventArgs [1].Position.Y);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParserDetectsKeyboard ()
|
||||
{
|
||||
// ANSI escape sequence for cursor left
|
||||
const string LEFT = "\u001b[D";
|
||||
|
||||
// ANSI escape sequence for Device Attribute Response (e.g., Terminal identifying itself)
|
||||
const string DEVICE_ATTRIBUTE_RESPONSE = "\u001B[?1;2c";
|
||||
|
||||
// ANSI escape sequence for cursor up (while shift held down)
|
||||
const string SHIFT_UP = "\u001b[1;2A";
|
||||
|
||||
var parser = new AnsiResponseParser ();
|
||||
|
||||
parser.HandleKeyboard = true;
|
||||
string? foundDar = null;
|
||||
List<Key> keys = new ();
|
||||
|
||||
parser.Keyboard += (s, e) => keys.Add (e);
|
||||
parser.ExpectResponse ("c", dar => foundDar = dar, null, false);
|
||||
string released = parser.ProcessInput ("a" + LEFT + "asdf" + DEVICE_ATTRIBUTE_RESPONSE + "bbcc" + SHIFT_UP + "sss");
|
||||
|
||||
Assert.Equal ("aasdfbbccsss", released);
|
||||
|
||||
Assert.Equal (2, keys.Count);
|
||||
|
||||
Assert.NotNull (foundDar);
|
||||
Assert.Equal (DEVICE_ATTRIBUTE_RESPONSE, foundDar);
|
||||
|
||||
Assert.Equal (Key.CursorLeft, keys [0]);
|
||||
Assert.Equal (Key.CursorUp.WithShift, keys [1]);
|
||||
}
|
||||
|
||||
public static IEnumerable<object []> ParserDetects_FunctionKeys_Cases ()
|
||||
{
|
||||
// These are VT100 escape codes for F1-4
|
||||
yield return
|
||||
[
|
||||
"\u001bOP",
|
||||
Key.F1
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001bOQ",
|
||||
Key.F2
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001bOR",
|
||||
Key.F3
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001bOS",
|
||||
Key.F4
|
||||
];
|
||||
|
||||
// These are also F keys
|
||||
yield return
|
||||
[
|
||||
"\u001b[11~",
|
||||
Key.F1
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[12~",
|
||||
Key.F2
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[13~",
|
||||
Key.F3
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[14~",
|
||||
Key.F4
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[15~",
|
||||
Key.F5
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[17~",
|
||||
Key.F6
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[18~",
|
||||
Key.F7
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[19~",
|
||||
Key.F8
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[20~",
|
||||
Key.F9
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[21~",
|
||||
Key.F10
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[23~",
|
||||
Key.F11
|
||||
];
|
||||
|
||||
yield return
|
||||
[
|
||||
"\u001b[24~",
|
||||
Key.F12
|
||||
];
|
||||
}
|
||||
|
||||
[MemberData (nameof (ParserDetects_FunctionKeys_Cases))]
|
||||
[Theory]
|
||||
public void ParserDetects_FunctionKeys (string input, Key expectedKey)
|
||||
{
|
||||
var parser = new AnsiResponseParser ();
|
||||
|
||||
parser.HandleKeyboard = true;
|
||||
List<Key> keys = new ();
|
||||
|
||||
parser.Keyboard += (s, e) => keys.Add (e);
|
||||
|
||||
foreach (char ch in input)
|
||||
{
|
||||
parser.ProcessInput (new (ch, 1));
|
||||
}
|
||||
|
||||
Key k = Assert.Single (keys);
|
||||
|
||||
Assert.Equal (k, expectedKey);
|
||||
}
|
||||
|
||||
private Tuple<char, int> [] StringToBatch (string batch) { return batch.Select (k => Tuple.Create (k, _tIndex++)).ToArray (); }
|
||||
|
||||
public static IEnumerable<string []> GetBatchPermutations (string input, int maxDepth = 3)
|
||||
{
|
||||
// Call the recursive method to generate batches with an initial depth of 0
|
||||
return GenerateBatches (input, 0, maxDepth, 0);
|
||||
}
|
||||
|
||||
private static IEnumerable<string []> GenerateBatches (string input, int start, int maxDepth, int currentDepth)
|
||||
{
|
||||
// If we have reached the maximum recursion depth, return no results
|
||||
if (currentDepth >= maxDepth)
|
||||
{
|
||||
yield break; // No more batches can be generated at this depth
|
||||
}
|
||||
|
||||
// If we have reached the end of the string, return an empty list
|
||||
if (start >= input.Length)
|
||||
{
|
||||
yield return new string [0];
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Iterate over the input string to create batches
|
||||
for (int i = start + 1; i <= input.Length; i++)
|
||||
{
|
||||
// Take a batch from 'start' to 'i'
|
||||
string batch = input.Substring (start, i - start);
|
||||
|
||||
// Recursively get batches from the remaining substring, increasing the depth
|
||||
foreach (string [] remainingBatches in GenerateBatches (input, i, maxDepth, currentDepth + 1))
|
||||
{
|
||||
// Combine the current batch with the remaining batches
|
||||
var result = new string [1 + remainingBatches.Length];
|
||||
result [0] = batch;
|
||||
Array.Copy (remainingBatches, 0, result, 1, remainingBatches.Length);
|
||||
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertIgnored (string ansiStream, char expected, ref int i)
|
||||
{
|
||||
char c2 = ansiStream [i];
|
||||
Tuple<char, int> [] c1 = NextChar (ansiStream, ref i);
|
||||
|
||||
// Parser does not grab this key (i.e. driver can continue with regular operations)
|
||||
Assert.Equal (c1, _parser1.ProcessInput (c1));
|
||||
Assert.Equal (expected, c1.Single ().Item1);
|
||||
|
||||
Assert.Equal (c2, _parser2.ProcessInput (c2.ToString ()).Single ());
|
||||
Assert.Equal (expected, c2);
|
||||
}
|
||||
|
||||
private void AssertConsumed (string ansiStream, ref int i)
|
||||
{
|
||||
// Parser grabs this key
|
||||
char c2 = ansiStream [i];
|
||||
Tuple<char, int> [] c1 = NextChar (ansiStream, ref i);
|
||||
|
||||
Assert.Empty (_parser1.ProcessInput (c1));
|
||||
Assert.Empty (_parser2.ProcessInput (c2.ToString ()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overload that fully exhausts <paramref name="ansiStream"/> and asserts
|
||||
/// that the final released content across whole processing is <paramref name="expectedRelease"/>
|
||||
/// </summary>
|
||||
/// <param name="ansiStream"></param>
|
||||
/// <param name="expectedRelease"></param>
|
||||
/// <param name="expectedTValues"></param>
|
||||
private void AssertReleased (string ansiStream, string expectedRelease, params int [] expectedTValues)
|
||||
{
|
||||
var sb = new StringBuilder ();
|
||||
List<int> tValues = new ();
|
||||
|
||||
var i = 0;
|
||||
|
||||
while (i < ansiStream.Length)
|
||||
{
|
||||
char c2 = ansiStream [i];
|
||||
Tuple<char, int> [] c1 = NextChar (ansiStream, ref i);
|
||||
|
||||
Tuple<char, int> [] released1 = _parser1.ProcessInput (c1).ToArray ();
|
||||
tValues.AddRange (released1.Select (kv => kv.Item2));
|
||||
|
||||
string released2 = _parser2.ProcessInput (c2.ToString ());
|
||||
|
||||
// Both parsers should have same chars so release chars consistently with each other
|
||||
Assert.Equal (BatchToString (released1), released2);
|
||||
|
||||
sb.Append (released2);
|
||||
}
|
||||
|
||||
Assert.Equal (expectedRelease, sb.ToString ());
|
||||
|
||||
if (expectedTValues.Length > 0)
|
||||
{
|
||||
Assert.True (expectedTValues.SequenceEqual (tValues));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that <paramref name="i"/> index of <see cref="ansiStream"/> when consumed will release
|
||||
/// <paramref name="expectedRelease"/>. Results in implicit increment of <paramref name="i"/>.
|
||||
/// <remarks>Note that this does NOT iteratively consume all the stream, only 1 char at <paramref name="i"/></remarks>
|
||||
/// </summary>
|
||||
/// <param name="ansiStream"></param>
|
||||
/// <param name="i"></param>
|
||||
/// <param name="expectedRelease"></param>
|
||||
/// <param name="expectedTValues"></param>
|
||||
private void AssertReleased (string ansiStream, ref int i, string expectedRelease, params int [] expectedTValues)
|
||||
{
|
||||
char c2 = ansiStream [i];
|
||||
Tuple<char, int> [] c1 = NextChar (ansiStream, ref i);
|
||||
|
||||
// Parser realizes it has grabbed content that does not belong to an outstanding request
|
||||
// Parser returns false to indicate to continue
|
||||
Tuple<char, int> [] released1 = _parser1.ProcessInput (c1).ToArray ();
|
||||
Assert.Equal (expectedRelease, BatchToString (released1));
|
||||
|
||||
if (expectedTValues.Length > 0)
|
||||
{
|
||||
Assert.True (expectedTValues.SequenceEqual (released1.Select (kv => kv.Item2)));
|
||||
}
|
||||
|
||||
Assert.Equal (expectedRelease, _parser2.ProcessInput (c2.ToString ()));
|
||||
}
|
||||
|
||||
private string BatchToString (IEnumerable<Tuple<char, int>> processInput) { return new (processInput.Select (a => a.Item1).ToArray ()); }
|
||||
|
||||
private Tuple<char, int> [] NextChar (string ansiStream, ref int i) { return StringToBatch (ansiStream [i++].ToString ()); }
|
||||
|
||||
private void AssertManualReleaseIs (string expectedRelease, params int [] expectedTValues)
|
||||
{
|
||||
// Consumer is responsible for determining this based on e.g. after 50ms
|
||||
Tuple<char, int> [] released1 = _parser1.Release ().ToArray ();
|
||||
Assert.Equal (expectedRelease, BatchToString (released1));
|
||||
|
||||
if (expectedTValues.Length > 0)
|
||||
{
|
||||
Assert.True (expectedTValues.SequenceEqual (released1.Select (kv => kv.Item2)));
|
||||
}
|
||||
|
||||
Assert.Equal (expectedRelease, _parser2.Release ());
|
||||
|
||||
Assert.Equal (AnsiResponseParserState.Normal, _parser1.State);
|
||||
Assert.Equal (AnsiResponseParserState.Normal, _parser2.State);
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
// Alias Console to MockConsole so we don't accidentally use Console
|
||||
|
||||
namespace Terminal.Gui.DriverTests;
|
||||
|
||||
public class MainLoopDriverTests
|
||||
{
|
||||
public MainLoopDriverTests (ITestOutputHelper output) { ConsoleDriver.RunningUnitTests = true; }
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_AddTimeout_ValidIdleHandler_ReturnsToken (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
var idleHandlerInvoked = false;
|
||||
|
||||
bool IdleHandler ()
|
||||
{
|
||||
idleHandlerInvoked = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var token = mainLoop.TimedEvents.Add(TimeSpan.Zero, IdleHandler);
|
||||
|
||||
Assert.NotNull (token);
|
||||
Assert.False (idleHandlerInvoked); // Idle handler should not be invoked immediately
|
||||
mainLoop.RunIteration (); // Run an iteration to process the idle handler
|
||||
Assert.True (idleHandlerInvoked); // Idle handler should be invoked after processing
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_AddTimeout_ValidParameters_ReturnsToken (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
var callbackInvoked = false;
|
||||
|
||||
object token = mainLoop.TimedEvents.Add (
|
||||
TimeSpan.FromMilliseconds (100),
|
||||
() =>
|
||||
{
|
||||
callbackInvoked = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
Assert.NotNull (token);
|
||||
mainLoop.RunIteration (); // Run an iteration to process the timeout
|
||||
Assert.False (callbackInvoked); // Callback should not be invoked immediately
|
||||
Thread.Sleep (200); // Wait for the timeout
|
||||
mainLoop.RunIteration (); // Run an iteration to process the timeout
|
||||
Assert.True (callbackInvoked); // Callback should be invoked after the timeout
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_CheckTimersAndIdleHandlers_IdleHandlersActive_ReturnsTrue (
|
||||
Type driverType,
|
||||
Type mainLoopDriverType
|
||||
)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
mainLoop.TimedEvents.Add (TimeSpan.Zero, () => false);
|
||||
bool result = mainLoop.TimedEvents.CheckTimers (out int waitTimeout);
|
||||
|
||||
Assert.True (result);
|
||||
Assert.Equal (0, waitTimeout);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_CheckTimers_NoTimersOrIdleHandlers_ReturnsFalse (
|
||||
Type driverType,
|
||||
Type mainLoopDriverType
|
||||
)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
bool result = mainLoop.TimedEvents.CheckTimers (out int waitTimeout);
|
||||
|
||||
Assert.False (result);
|
||||
Assert.Equal (-1, waitTimeout);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_CheckTimersAndIdleHandlers_TimersActive_ReturnsTrue (
|
||||
Type driverType,
|
||||
Type mainLoopDriverType
|
||||
)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
mainLoop.TimedEvents.Add (TimeSpan.FromMilliseconds (100), () => false);
|
||||
bool result = mainLoop.TimedEvents.CheckTimers(out int waitTimeout);
|
||||
|
||||
Assert.True (result);
|
||||
Assert.True (waitTimeout >= 0);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_Constructs_Disposes (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
// Check default values
|
||||
Assert.NotNull (mainLoop);
|
||||
Assert.Equal (mainLoopDriver, mainLoop.MainLoopDriver);
|
||||
Assert.Empty (mainLoop.TimedEvents.Timeouts);
|
||||
Assert.False (mainLoop.Running);
|
||||
|
||||
// Clean up
|
||||
mainLoop.Dispose ();
|
||||
|
||||
// TODO: It'd be nice if we could really verify IMainLoopDriver.TearDown was called
|
||||
// and that it was actually cleaned up.
|
||||
Assert.Null (mainLoop.MainLoopDriver);
|
||||
Assert.Empty (mainLoop.TimedEvents.Timeouts);
|
||||
Assert.False (mainLoop.Running);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_RemoveIdle_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
bool result = mainLoop.TimedEvents.Remove("flibble");
|
||||
|
||||
Assert.False (result);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_RemoveIdle_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
bool IdleHandler () { return false; }
|
||||
|
||||
|
||||
var token = mainLoop.TimedEvents.Add (TimeSpan.Zero, IdleHandler);
|
||||
bool result = mainLoop.TimedEvents.Remove (token);
|
||||
|
||||
Assert.True (result);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_RemoveTimeout_InvalidToken_ReturnsFalse (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
bool result = mainLoop.TimedEvents.Remove (new object ());
|
||||
|
||||
Assert.False (result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_RemoveTimeout_ValidToken_ReturnsTrue (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
|
||||
object token = mainLoop.TimedEvents.Add (TimeSpan.FromMilliseconds (100), () => false);
|
||||
bool result = mainLoop.TimedEvents.Remove (token);
|
||||
|
||||
Assert.True (result);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
//[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
//[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
//[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
|
||||
//[InlineData (typeof (ANSIDriver), typeof (AnsiMainLoopDriver))]
|
||||
public void MainLoop_RunIteration_ValidIdleHandler_CallsIdleHandler (Type driverType, Type mainLoopDriverType)
|
||||
{
|
||||
var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, driver);
|
||||
var mainLoop = new MainLoop (mainLoopDriver);
|
||||
var idleHandlerInvoked = false;
|
||||
|
||||
Func<bool> idleHandler = () =>
|
||||
{
|
||||
idleHandlerInvoked = true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
mainLoop.TimedEvents.Add (TimeSpan.Zero, idleHandler);
|
||||
mainLoop.RunIteration (); // Run an iteration to process the idle handler
|
||||
|
||||
Assert.True (idleHandlerInvoked);
|
||||
mainLoop.Dispose ();
|
||||
}
|
||||
|
||||
//[Theory]
|
||||
//[InlineData (typeof (FakeDriver), typeof (FakeMainLoop))]
|
||||
////[InlineData (typeof (DotNetDriver), typeof (NetMainLoop))]
|
||||
////[InlineData (typeof (UnixDriver), typeof (UnixMainLoop))]
|
||||
////[InlineData (typeof (WindowsDriver), typeof (WindowsMainLoop))]
|
||||
//public void MainLoop_Invoke_ValidAction_RunsAction (Type driverType, Type mainLoopDriverType)
|
||||
//{
|
||||
// var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
|
||||
// var mainLoopDriver = (IMainLoopDriver)Activator.CreateInstance (mainLoopDriverType, new object [] { driver });
|
||||
// var mainLoop = new MainLoop (mainLoopDriver);
|
||||
// var actionInvoked = false;
|
||||
|
||||
// mainLoop.Invoke (() => { actionInvoked = true; });
|
||||
// mainLoop.RunIteration (); // Run an iteration to process the action.
|
||||
|
||||
// Assert.True (actionInvoked);
|
||||
// mainLoop.Dispose ();
|
||||
//}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
namespace Terminal.Gui.DriverTests;
|
||||
|
||||
public class EscSeqRequestsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Add_Tests ()
|
||||
{
|
||||
EscSeqRequests.Add ("t");
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Add ("t", 2);
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (3, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (3, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Clear ();
|
||||
EscSeqRequests.Add ("t", 2);
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (2, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (2, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Add ("t", 3);
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (5, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (5, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Clear ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Defaults ()
|
||||
{
|
||||
Assert.NotNull (EscSeqRequests.Statuses);
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Remove_Tests ()
|
||||
{
|
||||
EscSeqRequests.Add ("t");
|
||||
EscSeqRequests.Remove ("t");
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
|
||||
EscSeqRequests.Add ("t", 2);
|
||||
EscSeqRequests.Remove ("t");
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (2, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Remove ("t");
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
|
||||
EscSeqRequests.Clear ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasResponse_Tests ()
|
||||
{
|
||||
Assert.False (EscSeqRequests.HasResponse ("t"));
|
||||
|
||||
EscSeqRequests.Add ("t");
|
||||
Assert.False (EscSeqRequests.HasResponse ("r"));
|
||||
Assert.True (EscSeqRequests.HasResponse ("t"));
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqRequests.Remove ("t");
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (null)]
|
||||
[InlineData ("")]
|
||||
public void Add_Null_Or_Empty_Terminator_Throws (string terminator)
|
||||
{
|
||||
if (terminator is null)
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => EscSeqRequests.Add (terminator));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<ArgumentException> (() => EscSeqRequests.Add (terminator));
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (null)]
|
||||
[InlineData ("")]
|
||||
public void HasResponse_Null_Or_Empty_Terminator_Does_Not_Throws (string terminator)
|
||||
{
|
||||
EscSeqRequests.Add ("t");
|
||||
|
||||
Assert.False (EscSeqRequests.HasResponse (terminator));
|
||||
|
||||
EscSeqRequests.Clear ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (null)]
|
||||
[InlineData ("")]
|
||||
public void Remove_Null_Or_Empty_Terminator_Throws (string terminator)
|
||||
{
|
||||
EscSeqRequests.Add ("t");
|
||||
|
||||
if (terminator is null)
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => EscSeqRequests.Remove (terminator));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<ArgumentException> (() => EscSeqRequests.Remove (terminator));
|
||||
}
|
||||
|
||||
EscSeqRequests.Clear ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Requests_Responses_Tests ()
|
||||
{
|
||||
// This is simulated response from a CSI_ReportTerminalSizeInChars
|
||||
ConsoleKeyInfo [] cki =
|
||||
[
|
||||
new ('\u001b', 0, false, false, false),
|
||||
new ('[', 0, false, false, false),
|
||||
new ('8', 0, false, false, false),
|
||||
new (';', 0, false, false, false),
|
||||
new ('1', 0, false, false, false),
|
||||
new ('0', 0, false, false, false),
|
||||
new (';', 0, false, false, false),
|
||||
new ('2', 0, false, false, false),
|
||||
new ('0', 0, false, false, false),
|
||||
new ('t', 0, false, false, false)
|
||||
];
|
||||
ConsoleKeyInfo newConsoleKeyInfo = default;
|
||||
ConsoleKey key = default;
|
||||
ConsoleModifiers mod = default;
|
||||
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
|
||||
EscSeqRequests.Add ("t");
|
||||
Assert.Single (EscSeqRequests.Statuses);
|
||||
Assert.Equal ("t", EscSeqRequests.Statuses [^1].Terminator);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumRequests);
|
||||
Assert.Equal (1, EscSeqRequests.Statuses [^1].NumOutstanding);
|
||||
|
||||
EscSeqUtils.DecodeEscSeq (
|
||||
ref newConsoleKeyInfo,
|
||||
ref key,
|
||||
cki,
|
||||
ref mod,
|
||||
out string c1Control,
|
||||
out string code,
|
||||
out string [] values,
|
||||
out string terminating,
|
||||
out bool isKeyMouse,
|
||||
out List<MouseFlags> mouseFlags,
|
||||
out Point pos,
|
||||
out bool isResponse,
|
||||
null
|
||||
);
|
||||
|
||||
Assert.Empty (EscSeqRequests.Statuses);
|
||||
Assert.Equal (default, newConsoleKeyInfo);
|
||||
Assert.Equal (default, key);
|
||||
Assert.Equal (10, cki.Length);
|
||||
Assert.Equal (default, mod);
|
||||
Assert.Equal ("CSI", c1Control);
|
||||
Assert.Null (code);
|
||||
// ReSharper disable once HeuristicUnreachableCode
|
||||
Assert.Equal (3, values.Length);
|
||||
Assert.Equal ("8", values [0]);
|
||||
Assert.Equal ("t", terminating);
|
||||
Assert.False (isKeyMouse);
|
||||
Assert.Single (mouseFlags);
|
||||
Assert.Equal (default, mouseFlags [^1]);
|
||||
Assert.Equal (Point.Empty, pos);
|
||||
Assert.True (isResponse);
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Terminal.Gui.ResourcesTests;
|
||||
|
||||
public class ResourceManagerTests
|
||||
{
|
||||
private const string EXISTENT_CULTURE = "pt-PT";
|
||||
private const string NO_EXISTENT_CULTURE = "de-DE";
|
||||
private const string NO_EXISTENT_KEY = "blabla";
|
||||
private const string NO_TRANSLATED_KEY = "fdDeleteTitle";
|
||||
private const string NO_TRANSLATED_VALUE = "Delete {0}";
|
||||
private const string TRANSLATED_KEY = "ctxSelectAll";
|
||||
private const string TRANSLATED_VALUE = "_Selecionar Tudo";
|
||||
|
||||
|
||||
[ModuleInitializer]
|
||||
internal static void SaveOriginalCultureInfo ()
|
||||
{
|
||||
_savedCulture = CultureInfo.CurrentCulture;
|
||||
_savedUICulture = CultureInfo.CurrentUICulture;
|
||||
}
|
||||
|
||||
private static CultureInfo? _savedCulture;
|
||||
private static CultureInfo? _savedUICulture;
|
||||
private static string? _stringsNoTranslatedKey;
|
||||
// ReSharper disable once NotAccessedField.Local
|
||||
private static string? _stringsTranslatedKey;
|
||||
|
||||
[Fact]
|
||||
public void GetObject_Does_Not_Overflows_If_Key_Does_Not_Exist () { Assert.Null (GlobalResources.GetObject (NO_EXISTENT_KEY, CultureInfo.CurrentCulture)); }
|
||||
|
||||
[Fact]
|
||||
public void GetObject_FallBack_To_Default_For_No_Existent_Culture_File ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = new (NO_EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (NO_EXISTENT_CULTURE);
|
||||
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, GlobalResources.GetObject (NO_TRANSLATED_KEY, CultureInfo.CurrentCulture));
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetObject_FallBack_To_Default_For_Not_Translated_Existent_Culture_File ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = new (NO_EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (NO_EXISTENT_CULTURE);
|
||||
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, GlobalResources.GetObject (NO_TRANSLATED_KEY, CultureInfo.CurrentCulture));
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetResourceSet_With_Filter_Does_Not_Overflows_If_Key_Does_Not_Exist ()
|
||||
{
|
||||
ResourceSet value = GlobalResources.GetResourceSet (CultureInfo.CurrentCulture, true, true, d => (string)d.Key == NO_EXISTENT_KEY)!;
|
||||
Assert.NotNull (value);
|
||||
Assert.Empty (value.Cast<DictionaryEntry> ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetResourceSet_Without_Filter_Does_Not_Overflows_If_Key_Does_Not_Exist ()
|
||||
{
|
||||
ResourceSet value = GlobalResources.GetResourceSet (CultureInfo.CurrentCulture, true, true)!;
|
||||
Assert.NotNull (value);
|
||||
Assert.NotEmpty (value.Cast<DictionaryEntry> ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetString_Does_Not_Overflows_If_Key_Does_Not_Exist ()
|
||||
{
|
||||
Assert.Null (GlobalResources.GetString (NO_EXISTENT_KEY, CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetString_FallBack_To_Default_For_No_Existent_Culture_File ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = new (NO_EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (NO_EXISTENT_CULTURE);
|
||||
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, GlobalResources.GetString (NO_TRANSLATED_KEY, CultureInfo.CurrentCulture));
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetString_FallBack_To_Default_For_Not_Translated_Existent_Culture_File ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = new (EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (EXISTENT_CULTURE);
|
||||
|
||||
// This is really already translated
|
||||
Assert.Equal (TRANSLATED_VALUE, GlobalResources.GetString (TRANSLATED_KEY, CultureInfo.CurrentCulture));
|
||||
|
||||
// These aren't already translated
|
||||
// Calling Strings.fdDeleteBody return always the invariant culture
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, GlobalResources.GetString (NO_TRANSLATED_KEY, CultureInfo.CurrentCulture));
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Strings_Always_FallBack_To_Default_For_No_Existent_Culture_File ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = new (NO_EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (NO_EXISTENT_CULTURE);
|
||||
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, _stringsNoTranslatedKey);
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Strings_Always_FallBack_To_Default_For_Not_Translated_Existent_Culture_File ()
|
||||
{
|
||||
ResetCultureInfo ();
|
||||
|
||||
CultureInfo.CurrentCulture = new (EXISTENT_CULTURE);
|
||||
CultureInfo.CurrentUICulture = new (EXISTENT_CULTURE);
|
||||
|
||||
// This is really already translated
|
||||
Assert.Equal (TRANSLATED_VALUE, Strings.ctxSelectAll);
|
||||
|
||||
// This isn't already translated
|
||||
Assert.Equal (NO_TRANSLATED_VALUE, _stringsNoTranslatedKey);
|
||||
|
||||
ResetCultureInfo ();
|
||||
}
|
||||
|
||||
private void ResetCultureInfo ()
|
||||
{
|
||||
CultureInfo.CurrentCulture = _savedCulture!;
|
||||
CultureInfo.CurrentUICulture = _savedUICulture!;
|
||||
|
||||
_stringsNoTranslatedKey = Strings.fdDeleteTitle;
|
||||
_stringsTranslatedKey = Strings.ctxSelectAll;
|
||||
}
|
||||
}
|
||||
@@ -255,31 +255,7 @@ This an long line and against TextView.",
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Test_GenerateSuggestions_Simple ()
|
||||
{
|
||||
var ac = new TextViewAutocomplete ();
|
||||
|
||||
((SingleWordSuggestionGenerator)ac.SuggestionGenerator).AllSuggestions =
|
||||
new () { "fish", "const", "Cobble" };
|
||||
|
||||
var tv = new TextView ();
|
||||
tv.InsertText ("co");
|
||||
|
||||
ac.HostControl = tv;
|
||||
|
||||
ac.GenerateSuggestions (
|
||||
new (
|
||||
Cell.ToCellList (tv.Text),
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
Assert.Equal (2, ac.Suggestions.Count);
|
||||
Assert.Equal ("const", ac.Suggestions [0].Title);
|
||||
Assert.Equal ("Cobble", ac.Suggestions [1].Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void TestSettingSchemeOnAutocomplete ()
|
||||
{
|
||||
var tv = new TextView ();
|
||||
@@ -303,4 +279,6 @@ This an long line and against TextView.",
|
||||
Assert.Equal (new (Color.Black), tv.Autocomplete.Scheme.Focus.Foreground);
|
||||
Assert.Equal (new (Color.Cyan), tv.Autocomplete.Scheme.Focus.Background);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,113 +15,6 @@ public class TextFormatterTests
|
||||
public static IEnumerable<object []> CMGlyphs =>
|
||||
new List<object []> { new object [] { $"{Glyphs.LeftBracket} Say Hello 你 {Glyphs.RightBracket}", 16, 15 } };
|
||||
|
||||
[SetupFakeDriver]
|
||||
[Theory]
|
||||
[InlineData ("A", 0, "")]
|
||||
[InlineData ("A", 1, "A")]
|
||||
[InlineData ("A", 2, "A")]
|
||||
[InlineData ("A", 3, " A")]
|
||||
[InlineData ("AB", 1, "A")]
|
||||
[InlineData ("AB", 2, "AB")]
|
||||
[InlineData ("ABC", 3, "ABC")]
|
||||
[InlineData ("ABC", 4, "ABC")]
|
||||
[InlineData ("ABC", 5, " ABC")]
|
||||
[InlineData ("ABC", 6, " ABC")]
|
||||
[InlineData ("ABC", 9, " ABC")]
|
||||
public void Draw_Horizontal_Centered (string text, int width, string expectedText)
|
||||
{
|
||||
TextFormatter tf = new ()
|
||||
{
|
||||
Text = text,
|
||||
Alignment = Alignment.Center
|
||||
};
|
||||
|
||||
tf.ConstrainToWidth = width;
|
||||
tf.ConstrainToHeight = 1;
|
||||
tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedText, _output);
|
||||
}
|
||||
|
||||
[SetupFakeDriver]
|
||||
[Theory]
|
||||
[InlineData ("A", 0, "")]
|
||||
[InlineData ("A", 1, "A")]
|
||||
[InlineData ("A", 2, "A")]
|
||||
[InlineData ("A B", 3, "A B")]
|
||||
[InlineData ("A B", 1, "A")]
|
||||
[InlineData ("A B", 2, "A")]
|
||||
[InlineData ("A B", 4, "A B")]
|
||||
[InlineData ("A B", 5, "A B")]
|
||||
[InlineData ("A B", 6, "A B")]
|
||||
[InlineData ("A B", 10, "A B")]
|
||||
[InlineData ("ABC ABC", 10, "ABC ABC")]
|
||||
public void Draw_Horizontal_Justified (string text, int width, string expectedText)
|
||||
{
|
||||
TextFormatter tf = new ()
|
||||
{
|
||||
Text = text,
|
||||
Alignment = Alignment.Fill
|
||||
};
|
||||
|
||||
tf.ConstrainToWidth = width;
|
||||
tf.ConstrainToHeight = 1;
|
||||
tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedText, _output);
|
||||
}
|
||||
|
||||
[SetupFakeDriver]
|
||||
[Theory]
|
||||
[InlineData ("A", 0, "")]
|
||||
[InlineData ("A", 1, "A")]
|
||||
[InlineData ("A", 2, "A")]
|
||||
[InlineData ("AB", 1, "A")]
|
||||
[InlineData ("AB", 2, "AB")]
|
||||
[InlineData ("ABC", 3, "ABC")]
|
||||
[InlineData ("ABC", 4, "ABC")]
|
||||
[InlineData ("ABC", 6, "ABC")]
|
||||
public void Draw_Horizontal_Left (string text, int width, string expectedText)
|
||||
|
||||
{
|
||||
TextFormatter tf = new ()
|
||||
{
|
||||
Text = text,
|
||||
Alignment = Alignment.Start
|
||||
};
|
||||
|
||||
tf.ConstrainToWidth = width;
|
||||
tf.ConstrainToHeight = 1;
|
||||
tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedText, _output);
|
||||
}
|
||||
|
||||
[SetupFakeDriver]
|
||||
[Theory]
|
||||
[InlineData ("A", 0, "")]
|
||||
[InlineData ("A", 1, "A")]
|
||||
[InlineData ("A", 2, " A")]
|
||||
[InlineData ("AB", 1, "B")]
|
||||
[InlineData ("AB", 2, "AB")]
|
||||
[InlineData ("ABC", 3, "ABC")]
|
||||
[InlineData ("ABC", 4, " ABC")]
|
||||
[InlineData ("ABC", 6, " ABC")]
|
||||
public void Draw_Horizontal_Right (string text, int width, string expectedText)
|
||||
{
|
||||
TextFormatter tf = new ()
|
||||
{
|
||||
Text = text,
|
||||
Alignment = Alignment.End
|
||||
};
|
||||
|
||||
tf.ConstrainToWidth = width;
|
||||
tf.ConstrainToHeight = 1;
|
||||
|
||||
tf.Draw (new (Point.Empty, new (width, 1)), Attribute.Default, Attribute.Default);
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedText, _output);
|
||||
}
|
||||
|
||||
[SetupFakeDriver]
|
||||
[Theory]
|
||||
[InlineData ("A", 1, 0, "")]
|
||||
|
||||
22
Tests/UnitTests/View/SchemeTests.cs
Normal file
22
Tests/UnitTests/View/SchemeTests.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Xunit;
|
||||
|
||||
namespace Terminal.Gui.ViewTests;
|
||||
|
||||
[Trait ("Category", "View.Scheme")]
|
||||
public class SchemeTests
|
||||
{
|
||||
[Fact]
|
||||
[UnitTests.AutoInitShutdown]
|
||||
public void View_Resolves_Attributes_From_Scheme ()
|
||||
{
|
||||
View view = new Label { SchemeName = "Base" };
|
||||
|
||||
foreach (VisualRole role in Enum.GetValues<VisualRole> ())
|
||||
{
|
||||
Attribute attr = view.GetAttributeForRole (role);
|
||||
Assert.NotEqual (default, attr.Foreground); // Defensive: avoid all-defaults
|
||||
}
|
||||
|
||||
view.Dispose ();
|
||||
}
|
||||
}
|
||||
@@ -104,114 +104,6 @@ public class ButtonTests (ITestOutputHelper output)
|
||||
btn.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HotKeyChange_Works ()
|
||||
{
|
||||
var clicked = false;
|
||||
var btn = new Button { Text = "_Test" };
|
||||
btn.Accepting += (s, e) => clicked = true;
|
||||
|
||||
Assert.Equal (KeyCode.T, btn.HotKey);
|
||||
Assert.False (btn.NewKeyDownEvent (Key.T)); // Button processes, but does not handle
|
||||
Assert.True (clicked);
|
||||
|
||||
clicked = false;
|
||||
Assert.False (btn.NewKeyDownEvent (Key.T.WithAlt)); // Button processes, but does not handle
|
||||
Assert.True (clicked);
|
||||
|
||||
clicked = false;
|
||||
btn.HotKey = KeyCode.E;
|
||||
Assert.False (btn.NewKeyDownEvent (Key.E.WithAlt)); // Button processes, but does not handle
|
||||
Assert.True (clicked);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, 0)]
|
||||
[InlineData (true, 1)]
|
||||
public void Space_Fires_Accept (bool focused, int expected)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
Button button = new ();
|
||||
|
||||
button.CanFocus = focused;
|
||||
|
||||
var acceptInvoked = 0;
|
||||
button.Accepting += (s, e) => acceptInvoked++;
|
||||
|
||||
superView.Add (button);
|
||||
button.SetFocus ();
|
||||
Assert.Equal (focused, button.HasFocus);
|
||||
|
||||
superView.NewKeyDownEvent (Key.Space);
|
||||
|
||||
Assert.Equal (expected, acceptInvoked);
|
||||
|
||||
superView.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, 0)]
|
||||
[InlineData (true, 1)]
|
||||
public void Enter_Fires_Accept (bool focused, int expected)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
Button button = new ();
|
||||
|
||||
button.CanFocus = focused;
|
||||
|
||||
var acceptInvoked = 0;
|
||||
button.Accepting += (s, e) => acceptInvoked++;
|
||||
|
||||
superView.Add (button);
|
||||
button.SetFocus ();
|
||||
Assert.Equal (focused, button.HasFocus);
|
||||
|
||||
superView.NewKeyDownEvent (Key.Enter);
|
||||
|
||||
Assert.Equal (expected, acceptInvoked);
|
||||
|
||||
superView.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (false, 1)]
|
||||
[InlineData (true, 1)]
|
||||
public void HotKey_Fires_Accept (bool focused, int expected)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
Button button = new ()
|
||||
{
|
||||
HotKey = Key.A
|
||||
};
|
||||
|
||||
button.CanFocus = focused;
|
||||
|
||||
var acceptInvoked = 0;
|
||||
button.Accepting += (s, e) => acceptInvoked++;
|
||||
|
||||
superView.Add (button);
|
||||
button.SetFocus ();
|
||||
Assert.Equal (focused, button.HasFocus);
|
||||
|
||||
superView.NewKeyDownEvent (Key.A);
|
||||
|
||||
Assert.Equal (expected, acceptInvoked);
|
||||
|
||||
superView.Dispose ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test demonstrates how to change the activation key for Button as described in the README.md keyboard
|
||||
/// handling section
|
||||
@@ -337,86 +229,6 @@ public class ButtonTests (ITestOutputHelper output)
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HotKey_Command_Accepts ()
|
||||
{
|
||||
var button = new Button ();
|
||||
var accepted = false;
|
||||
|
||||
button.Accepting += ButtonOnAccept;
|
||||
button.InvokeCommand (Command.HotKey);
|
||||
|
||||
Assert.True (accepted);
|
||||
button.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void ButtonOnAccept (object sender, CommandEventArgs e) { accepted = true; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_Cancel_Event_OnAccept_Returns_True ()
|
||||
{
|
||||
var button = new Button ();
|
||||
var acceptInvoked = false;
|
||||
|
||||
button.Accepting += ButtonAccept;
|
||||
|
||||
bool? ret = button.InvokeCommand (Command.Accept);
|
||||
Assert.True (ret);
|
||||
Assert.True (acceptInvoked);
|
||||
|
||||
button.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void ButtonAccept (object sender, CommandEventArgs e)
|
||||
{
|
||||
acceptInvoked = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Setting_Empty_Text_Sets_HoKey_To_KeyNull ()
|
||||
{
|
||||
var super = new View ();
|
||||
var btn = new Button { Text = "_Test" };
|
||||
super.Add (btn);
|
||||
super.BeginInit ();
|
||||
super.EndInit ();
|
||||
|
||||
Assert.Equal ("_Test", btn.Text);
|
||||
Assert.Equal (KeyCode.T, btn.HotKey);
|
||||
|
||||
btn.Text = string.Empty;
|
||||
Assert.Equal ("", btn.Text);
|
||||
Assert.Equal (KeyCode.Null, btn.HotKey);
|
||||
btn.Text = string.Empty;
|
||||
Assert.Equal ("", btn.Text);
|
||||
Assert.Equal (KeyCode.Null, btn.HotKey);
|
||||
|
||||
btn.Text = "Te_st";
|
||||
Assert.Equal ("Te_st", btn.Text);
|
||||
Assert.Equal (KeyCode.S, btn.HotKey);
|
||||
super.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestAssignTextToButton ()
|
||||
{
|
||||
View b = new Button { Text = "heya" };
|
||||
Assert.Equal ("heya", b.Text);
|
||||
Assert.Contains ("heya", b.TextFormatter.Text);
|
||||
b.Text = "heyb";
|
||||
Assert.Equal ("heyb", b.Text);
|
||||
Assert.Contains ("heyb", b.TextFormatter.Text);
|
||||
|
||||
// with cast
|
||||
Assert.Equal ("heyb", ((Button)b).Text);
|
||||
b.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[AutoInitShutdown]
|
||||
public void Update_Parameterless_Only_On_Or_After_Initialize ()
|
||||
@@ -451,7 +263,6 @@ public class ButtonTests (ITestOutputHelper output)
|
||||
Assert.Equal (new (0, 0, 30, 5), pos);
|
||||
top.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed, MouseFlags.Button1Released, MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Pressed, MouseFlags.Button2Released, MouseFlags.Button2Clicked)]
|
||||
|
||||
@@ -1,597 +0,0 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Terminal.Gui.ViewsTests;
|
||||
|
||||
public class SliderOptionTests
|
||||
{
|
||||
[Fact]
|
||||
public void OnChanged_Should_Raise_ChangedEvent ()
|
||||
{
|
||||
// Arrange
|
||||
SliderOption<int> sliderOption = new ();
|
||||
var eventRaised = false;
|
||||
sliderOption.Changed += (sender, args) => eventRaised = true;
|
||||
|
||||
// Act
|
||||
sliderOption.OnChanged (true);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnSet_Should_Raise_SetEvent ()
|
||||
{
|
||||
// Arrange
|
||||
SliderOption<int> sliderOption = new ();
|
||||
var eventRaised = false;
|
||||
sliderOption.Set += (sender, args) => eventRaised = true;
|
||||
|
||||
// Act
|
||||
sliderOption.OnSet ();
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnUnSet_Should_Raise_UnSetEvent ()
|
||||
{
|
||||
// Arrange
|
||||
SliderOption<int> sliderOption = new ();
|
||||
var eventRaised = false;
|
||||
sliderOption.UnSet += (sender, args) => eventRaised = true;
|
||||
|
||||
// Act
|
||||
sliderOption.OnUnSet ();
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Option_Default_Constructor ()
|
||||
{
|
||||
SliderOption<int> o = new ();
|
||||
Assert.Null (o.Legend);
|
||||
Assert.Equal (default (Rune), o.LegendAbbr);
|
||||
Assert.Equal (default (int), o.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Slider_Option_Values_Constructor ()
|
||||
{
|
||||
SliderOption<int> o = new ("1 thousand", new ('y'), 1000);
|
||||
Assert.Equal ("1 thousand", o.Legend);
|
||||
Assert.Equal (new ('y'), o.LegendAbbr);
|
||||
Assert.Equal (1000, o.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SliderOption_ToString_WhenEmpty ()
|
||||
{
|
||||
SliderOption<object> sliderOption = new ();
|
||||
Assert.Equal ("{Legend=, LegendAbbr=\0, Data=}", sliderOption.ToString ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SliderOption_ToString_WhenPopulated_WithInt ()
|
||||
{
|
||||
SliderOption<int> sliderOption = new () { Legend = "Lord flibble", LegendAbbr = new ('l'), Data = 1 };
|
||||
|
||||
Assert.Equal ("{Legend=Lord flibble, LegendAbbr=l, Data=1}", sliderOption.ToString ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SliderOption_ToString_WhenPopulated_WithSizeF ()
|
||||
{
|
||||
SliderOption<SizeF> sliderOption = new ()
|
||||
{
|
||||
Legend = "Lord flibble", LegendAbbr = new ('l'), Data = new (32, 11)
|
||||
};
|
||||
|
||||
Assert.Equal ("{Legend=Lord flibble, LegendAbbr=l, Data={Width=32, Height=11}}", sliderOption.ToString ());
|
||||
}
|
||||
}
|
||||
|
||||
public class SliderEventArgsTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_Sets_Cancel_Default_To_False ()
|
||||
{
|
||||
// Arrange
|
||||
Dictionary<int, SliderOption<int>> options = new ();
|
||||
var focused = 42;
|
||||
|
||||
// Act
|
||||
SliderEventArgs<int> sliderEventArgs = new (options, focused);
|
||||
|
||||
// Assert
|
||||
Assert.False (sliderEventArgs.Cancel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Sets_Focused ()
|
||||
{
|
||||
// Arrange
|
||||
Dictionary<int, SliderOption<int>> options = new ();
|
||||
var focused = 42;
|
||||
|
||||
// Act
|
||||
SliderEventArgs<int> sliderEventArgs = new (options, focused);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (focused, sliderEventArgs.Focused);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_Sets_Options ()
|
||||
{
|
||||
// Arrange
|
||||
Dictionary<int, SliderOption<int>> options = new ();
|
||||
|
||||
// Act
|
||||
SliderEventArgs<int> sliderEventArgs = new (options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (options, sliderEventArgs.Options);
|
||||
}
|
||||
}
|
||||
|
||||
public class SliderTests
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_Default ()
|
||||
{
|
||||
// Arrange & Act
|
||||
Slider<int> slider = new ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (slider);
|
||||
Assert.NotNull (slider.Options);
|
||||
Assert.Empty (slider.Options);
|
||||
Assert.Equal (Orientation.Horizontal, slider.Orientation);
|
||||
Assert.False (slider.AllowEmpty);
|
||||
Assert.True (slider.ShowLegends);
|
||||
Assert.False (slider.ShowEndSpacing);
|
||||
Assert.Equal (SliderType.Single, slider.Type);
|
||||
Assert.Equal (1, slider.MinimumInnerSpacing);
|
||||
Assert.True (slider.Width is DimAuto);
|
||||
Assert.True (slider.Height is DimAuto);
|
||||
Assert.Equal (0, slider.FocusedOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_With_Options ()
|
||||
{
|
||||
// Arrange
|
||||
List<int> options = new () { 1, 2, 3 };
|
||||
|
||||
// Act
|
||||
Slider<int> slider = new (options);
|
||||
slider.SetRelativeLayout (new (100, 100));
|
||||
|
||||
// Assert
|
||||
// 0123456789
|
||||
// 1 2 3
|
||||
Assert.Equal (1, slider.MinimumInnerSpacing);
|
||||
Assert.Equal (new Size (5, 2), slider.GetContentSize ());
|
||||
Assert.Equal (new Size (5, 2), slider.Frame.Size);
|
||||
Assert.NotNull (slider);
|
||||
Assert.NotNull (slider.Options);
|
||||
Assert.Equal (options.Count, slider.Options.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MovePlus_Should_MoveFocusRight_When_OptionIsAvailable ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
|
||||
// Act
|
||||
bool result = slider.MovePlus ();
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (1, slider.FocusedOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MovePlus_Should_NotMoveFocusRight_When_AtEnd ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
|
||||
slider.FocusedOption = 3;
|
||||
|
||||
// Act
|
||||
bool result = slider.MovePlus ();
|
||||
|
||||
// Assert
|
||||
Assert.False (result);
|
||||
Assert.Equal (3, slider.FocusedOption);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnOptionFocused_Event_Cancelled ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3 });
|
||||
var eventRaised = false;
|
||||
var cancel = false;
|
||||
slider.OptionFocused += (sender, args) => eventRaised = true;
|
||||
var newFocusedOption = 1;
|
||||
|
||||
// Create args with cancel set to false
|
||||
cancel = false;
|
||||
|
||||
SliderEventArgs<int> args =
|
||||
new (new (), newFocusedOption) { Cancel = cancel };
|
||||
Assert.Equal (0, slider.FocusedOption);
|
||||
|
||||
// Act
|
||||
slider.OnOptionFocused (newFocusedOption, args);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised); // Event should be raised
|
||||
Assert.Equal (newFocusedOption, slider.FocusedOption); // Focused option should change
|
||||
|
||||
// Create args with cancel set to true
|
||||
cancel = true;
|
||||
|
||||
args = new (new (), newFocusedOption)
|
||||
{
|
||||
Cancel = cancel
|
||||
};
|
||||
|
||||
// Act
|
||||
slider.OnOptionFocused (2, args);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised); // Event should be raised
|
||||
Assert.Equal (newFocusedOption, slider.FocusedOption); // Focused option should not change
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnOptionFocused_Event_Raised ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3 });
|
||||
var eventRaised = false;
|
||||
slider.OptionFocused += (sender, args) => eventRaised = true;
|
||||
var newFocusedOption = 1;
|
||||
SliderEventArgs<int> args = new (new (), newFocusedOption);
|
||||
|
||||
// Act
|
||||
slider.OnOptionFocused (newFocusedOption, args);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnOptionsChanged_Event_Raised ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new ();
|
||||
var eventRaised = false;
|
||||
slider.OptionsChanged += (sender, args) => eventRaised = true;
|
||||
|
||||
// Act
|
||||
slider.OnOptionsChanged ();
|
||||
|
||||
// Assert
|
||||
Assert.True (eventRaised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Should_Not_UnSetFocusedOption_When_EmptyNotAllowed ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 }) { AllowEmpty = false };
|
||||
|
||||
Assert.NotEmpty (slider.GetSetOptions ());
|
||||
|
||||
// Act
|
||||
bool result = slider.UnSetOption (slider.FocusedOption);
|
||||
|
||||
// Assert
|
||||
Assert.False (result);
|
||||
Assert.NotEmpty (slider.GetSetOptions ());
|
||||
}
|
||||
|
||||
// Add similar tests for other methods like MoveMinus, MoveStart, MoveEnd, Set, etc.
|
||||
|
||||
[Fact]
|
||||
public void Set_Should_SetFocusedOption ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
|
||||
// Act
|
||||
slider.FocusedOption = 2;
|
||||
bool result = slider.Select ();
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (2, slider.FocusedOption);
|
||||
Assert.Single (slider.GetSetOptions ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetOptionByPosition_InvalidPosition_Failure ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3 });
|
||||
var x = 10;
|
||||
var y = 10;
|
||||
var threshold = 2;
|
||||
int expectedOption = -1;
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetOptionByPosition (x, y, threshold, out int option);
|
||||
|
||||
// Assert
|
||||
Assert.False (result);
|
||||
Assert.Equal (expectedOption, option);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 1)]
|
||||
[InlineData (3, 0, 0, 2)]
|
||||
[InlineData (9, 0, 0, 4)]
|
||||
[InlineData (0, 0, 1, 1)]
|
||||
[InlineData (3, 0, 1, 2)]
|
||||
[InlineData (9, 0, 1, 4)]
|
||||
public void TryGetOptionByPosition_ValidPositionHorizontal_Success (int x, int y, int threshold, int expectedData)
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
// 0123456789
|
||||
// 1234
|
||||
|
||||
slider.MinimumInnerSpacing = 2;
|
||||
|
||||
// 0123456789
|
||||
// 1--2--3--4
|
||||
|
||||
// Arrange
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetOptionByPosition (x, y, threshold, out int option);
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (expectedData, slider.Options [option].Data);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0, 1)]
|
||||
[InlineData (0, 3, 0, 2)]
|
||||
[InlineData (0, 9, 0, 4)]
|
||||
[InlineData (0, 0, 1, 1)]
|
||||
[InlineData (0, 3, 1, 2)]
|
||||
[InlineData (0, 9, 1, 4)]
|
||||
public void TryGetOptionByPosition_ValidPositionVertical_Success (int x, int y, int threshold, int expectedData)
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
slider.Orientation = Orientation.Vertical;
|
||||
|
||||
// Set auto size to true to enable testing
|
||||
slider.MinimumInnerSpacing = 2;
|
||||
|
||||
// 0 1
|
||||
// 1 |
|
||||
// 2 |
|
||||
// 3 2
|
||||
// 4 |
|
||||
// 5 |
|
||||
// 6 3
|
||||
// 7 |
|
||||
// 8 |
|
||||
// 9 4
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetOptionByPosition (x, y, threshold, out int option);
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (expectedData, slider.Options [option].Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetPositionByOption_InvalidOption_Failure ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3 });
|
||||
int option = -1;
|
||||
(int, int) expectedPosition = (-1, -1);
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetPositionByOption (option, out (int x, int y) position);
|
||||
|
||||
// Assert
|
||||
Assert.False (result);
|
||||
Assert.Equal (expectedPosition, position);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0)]
|
||||
[InlineData (1, 3, 0)]
|
||||
[InlineData (3, 9, 0)]
|
||||
public void TryGetPositionByOption_ValidOptionHorizontal_Success (int option, int expectedX, int expectedY)
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
|
||||
// Set auto size to true to enable testing
|
||||
slider.MinimumInnerSpacing = 2;
|
||||
|
||||
// 0123456789
|
||||
// 1--2--3--4
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetPositionByOption (option, out (int x, int y) position);
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (expectedX, position.x);
|
||||
Assert.Equal (expectedY, position.y);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0, 0)]
|
||||
[InlineData (1, 0, 3)]
|
||||
[InlineData (3, 0, 9)]
|
||||
public void TryGetPositionByOption_ValidOptionVertical_Success (int option, int expectedX, int expectedY)
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new (new () { 1, 2, 3, 4 });
|
||||
slider.Orientation = Orientation.Vertical;
|
||||
|
||||
// Set auto size to true to enable testing
|
||||
slider.MinimumInnerSpacing = 2;
|
||||
|
||||
// Act
|
||||
bool result = slider.TryGetPositionByOption (option, out (int x, int y) position);
|
||||
|
||||
// Assert
|
||||
Assert.True (result);
|
||||
Assert.Equal (expectedX, position.x);
|
||||
Assert.Equal (expectedY, position.y);
|
||||
}
|
||||
|
||||
// https://github.com/gui-cs/Terminal.Gui/issues/3099
|
||||
[Fact]
|
||||
private void One_Option_Does_Not_Throw ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new ();
|
||||
slider.BeginInit ();
|
||||
slider.EndInit ();
|
||||
|
||||
// Act/Assert
|
||||
slider.Options = new () { new () };
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void Set_Options_No_Legend_Throws ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new ();
|
||||
|
||||
// Act/Assert
|
||||
Assert.Throws<ArgumentNullException> (() => slider.Options = null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void Set_Options_Throws_If_Null ()
|
||||
{
|
||||
// Arrange
|
||||
Slider<int> slider = new ();
|
||||
|
||||
// Act/Assert
|
||||
Assert.Throws<ArgumentNullException> (() => slider.Options = null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void DimAuto_Both_Respects_SuperView_ContentSize ()
|
||||
{
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
|
||||
List<object> options = new () { "01234", "01234" };
|
||||
|
||||
Slider slider = new (options)
|
||||
{
|
||||
Orientation = Orientation.Vertical,
|
||||
Type = SliderType.Multiple,
|
||||
};
|
||||
view.Add (slider);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
|
||||
Size expectedSize = slider.Frame.Size;
|
||||
|
||||
Assert.Equal (new (6, 3), expectedSize);
|
||||
|
||||
view.SetContentSize (new (1, 1));
|
||||
|
||||
view.LayoutSubViews ();
|
||||
slider.SetRelativeLayout (view.Viewport.Size);
|
||||
|
||||
Assert.Equal (expectedSize, slider.Frame.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void DimAuto_Width_Respects_SuperView_ContentSize ()
|
||||
{
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = 10
|
||||
};
|
||||
|
||||
List<object> options = new () { "01234", "01234" };
|
||||
|
||||
Slider slider = new (options)
|
||||
{
|
||||
Orientation = Orientation.Vertical,
|
||||
Type = SliderType.Multiple,
|
||||
Height = 10
|
||||
};
|
||||
view.Add (slider);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
|
||||
Size expectedSize = slider.Frame.Size;
|
||||
|
||||
Assert.Equal (new (6, 10), expectedSize);
|
||||
|
||||
view.SetContentSize (new (1, 1));
|
||||
|
||||
view.LayoutSubViews ();
|
||||
slider.SetRelativeLayout (view.Viewport.Size);
|
||||
|
||||
Assert.Equal (expectedSize, slider.Frame.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
private void DimAuto_Height_Respects_SuperView_ContentSize ()
|
||||
{
|
||||
View view = new ()
|
||||
{
|
||||
Width = 10,
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
|
||||
List<object> options = new () { "01234", "01234" };
|
||||
|
||||
Slider slider = new (options)
|
||||
{
|
||||
Orientation = Orientation.Vertical,
|
||||
Type = SliderType.Multiple,
|
||||
Width = 10,
|
||||
};
|
||||
view.Add (slider);
|
||||
view.BeginInit ();
|
||||
view.EndInit ();
|
||||
|
||||
Size expectedSize = slider.Frame.Size;
|
||||
|
||||
Assert.Equal (new (10, 3), expectedSize);
|
||||
|
||||
view.SetContentSize (new (1, 1));
|
||||
|
||||
view.LayoutSubViews ();
|
||||
slider.SetRelativeLayout (view.Viewport.Size);
|
||||
|
||||
Assert.Equal (expectedSize, slider.Frame.Size);
|
||||
}
|
||||
|
||||
// Add more tests for different scenarios and edge cases.
|
||||
}
|
||||
@@ -1,656 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Terminal.Gui.ViewsTests;
|
||||
|
||||
public class TextValidateField_NET_Provider_Tests
|
||||
{
|
||||
[Fact]
|
||||
public void Backspace_Key_Deletes_Previous_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--") { Text = "1234" }
|
||||
};
|
||||
|
||||
// Go to the end.
|
||||
field.NewKeyDownEvent (Key.End);
|
||||
|
||||
field.NewKeyDownEvent (Key.Backspace);
|
||||
Assert.Equal ("--(12_4)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.Backspace);
|
||||
Assert.Equal ("--(1__4)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.Backspace);
|
||||
Assert.Equal ("--(___4)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
// One more
|
||||
field.NewKeyDownEvent (Key.Backspace);
|
||||
Assert.Equal ("--(___4)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Changing_The_Mask_Tries_To_Keep_The_Previous_Text ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Start,
|
||||
Width = 30,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.Text = "1234";
|
||||
Assert.Equal ("--(1234)--", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
var provider = field.Provider as NetMaskedTextProvider;
|
||||
provider.Mask = "--------(00000000)--------";
|
||||
Assert.Equal ("--------(1234____)--------", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Default_Width_Is_Always_Equal_To_The_Provider_DisplayText_Length ()
|
||||
{
|
||||
// 9-Digit or space, optional. 0-Digit, required. L-Letter, required.
|
||||
// > Shift up. Converts all characters that follow to uppercase.
|
||||
// | Disable a previous shift up or shift down.
|
||||
// A-Alphanumeric, required. a-Alphanumeric, optional.
|
||||
var field = new TextValidateField { Provider = new NetMaskedTextProvider ("999 000 LLL >LLL |AAA aaa") };
|
||||
field.Layout ();
|
||||
Assert.Equal (field.Viewport.Width, field.Provider.DisplayText.Length);
|
||||
Assert.NotEqual (field.Provider.DisplayText.Length, field.Provider.Text.Length);
|
||||
Assert.Equal (new string (' ', field.Text.Length), field.Provider.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Delete_Key_Doesnt_Move_Cursor ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--") { Text = "1234" }
|
||||
};
|
||||
|
||||
Assert.Equal ("--(1234)--", field.Provider.DisplayText);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
|
||||
Assert.Equal ("--(_234)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
|
||||
Assert.Equal ("--(_2_4)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void End_Key_Last_Editable_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.End);
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(___1)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--( 1)--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Home_Key_First_Editable_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
field.NewKeyDownEvent (Key.Home);
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1___)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--(1 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Initial_Value_Bigger_Than_Mask_Discarded ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--") { Text = "12345" }
|
||||
};
|
||||
|
||||
Assert.Equal ("--(____)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--( )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Initial_Value_Exact_Valid ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--") { Text = "1234" }
|
||||
};
|
||||
|
||||
Assert.Equal ("--(1234)--", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Initial_Value_Smaller_Than_Mask_Accepted ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--") { Text = "123" }
|
||||
};
|
||||
|
||||
Assert.Equal ("--(123_)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--(123 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Initialized_With_Cursor_On_First_Editable_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1___)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--(1 )--", field.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Input_Ilegal_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.A);
|
||||
|
||||
Assert.Equal ("--( )--", field.Text);
|
||||
Assert.Equal ("--(____)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Insert_Skips_Non_Editable_Characters ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ** **
|
||||
// 01234567890
|
||||
Provider = new NetMaskedTextProvider ("--(00-00)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
Assert.Equal ("--(1_-__)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D2);
|
||||
Assert.Equal ("--(12-__)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D3);
|
||||
Assert.Equal ("--(12-3_)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D4);
|
||||
Assert.Equal ("--(12-34)--", field.Provider.DisplayText);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Left_Key_Stops_In_First_Editable_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.CursorLeft);
|
||||
}
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1___)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--(1 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MouseClick_Right_X_Greater_Than_Text_Width_Goes_To_Last_Editable_Position ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Start,
|
||||
Width = 30,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1___)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
Assert.Equal ("--(1 )--", field.Provider.Text);
|
||||
|
||||
field.NewMouseEvent (new MouseEventArgs { Position = new (25, 0), Flags = MouseFlags.Button1Pressed });
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1__1)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
Assert.Equal ("--(1 1)--", field.Provider.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnTextChanged_TextChanged_Event ()
|
||||
{
|
||||
var wasTextChanged = false;
|
||||
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Start, Width = 30, Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.Provider.TextChanged += (sender, e) => wasTextChanged = true;
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(1___)--", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
Assert.Equal ("--(1 )--", field.Provider.Text);
|
||||
Assert.True (wasTextChanged);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Right_Key_Stops_In_Last_Editable_Character ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// *
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
}
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("--(___1)--", field.Provider.DisplayText);
|
||||
Assert.Equal ("--( 1)--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_Text_After_Initialization ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Start,
|
||||
Width = 30,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.Text = "1234";
|
||||
|
||||
Assert.Equal ("--(1234)--", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void When_Valid_Is_Valid_True ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
|
||||
// ****
|
||||
// 0123456789
|
||||
Provider = new NetMaskedTextProvider ("--(0000)--")
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
Assert.Equal ("--(1 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D2);
|
||||
Assert.Equal ("--(12 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D3);
|
||||
Assert.Equal ("--(123 )--", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D4);
|
||||
Assert.Equal ("--(1234)--", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
}
|
||||
|
||||
public class TextValidateField_Regex_Provider_Tests
|
||||
{
|
||||
[Fact]
|
||||
public void End_Key_End_Of_Input ()
|
||||
{
|
||||
// Exactly 5 numbers
|
||||
var field = new TextValidateField
|
||||
{
|
||||
Width = 20, Provider = new TextRegexProvider ("^[0-9]{5}$") { ValidateOnInput = false }
|
||||
};
|
||||
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.D0);
|
||||
}
|
||||
|
||||
Assert.Equal ("0000", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
// HOME KEY
|
||||
field.NewKeyDownEvent (Key.Home);
|
||||
|
||||
// END KEY
|
||||
field.NewKeyDownEvent (Key.End);
|
||||
|
||||
// Insert 9
|
||||
field.NewKeyDownEvent (Key.D9);
|
||||
|
||||
Assert.Equal ("00009", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
// Insert 9
|
||||
field.NewKeyDownEvent (Key.D9);
|
||||
|
||||
Assert.Equal ("000099", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Home_Key_First_Editable_Character ()
|
||||
{
|
||||
// Range 0 to 1000
|
||||
// Accepts 001 too.
|
||||
var field = new TextValidateField { Width = 20, Provider = new TextRegexProvider ("^[0-9]?[0-9]?[0-9]|1000$") };
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
field.NewKeyDownEvent (Key.D0);
|
||||
field.NewKeyDownEvent (Key.D0);
|
||||
field.NewKeyDownEvent (Key.D0);
|
||||
|
||||
Assert.Equal ("1000", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
// HOME KEY
|
||||
field.NewKeyDownEvent (Key.Home);
|
||||
|
||||
// DELETE
|
||||
field.NewKeyDownEvent (Key.Delete);
|
||||
|
||||
Assert.Equal ("000", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Input_With_Validate_On_Input_Set_Text ()
|
||||
{
|
||||
var field = new TextValidateField { Width = 20, Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") };
|
||||
|
||||
// Input dosen't validates the pattern.
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
Assert.Equal ("", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
// Dosen't match
|
||||
field.Text = "12356";
|
||||
Assert.Equal ("", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
// Yes.
|
||||
field.Text = "123";
|
||||
Assert.Equal ("123", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Input_Without_Validate_On_Input ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
Width = 20, Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") { ValidateOnInput = false }
|
||||
};
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
Assert.Equal ("1", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D2);
|
||||
Assert.Equal ("12", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D3);
|
||||
Assert.Equal ("123", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
field.NewKeyDownEvent (Key.D4);
|
||||
Assert.Equal ("1234", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Left_Key_Stops_At_Start_And_Insert ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") { ValidateOnInput = false }
|
||||
};
|
||||
|
||||
field.Text = "123";
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.CursorLeft);
|
||||
}
|
||||
|
||||
Assert.Equal ("123", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
// Insert 4
|
||||
field.NewKeyDownEvent (Key.D4);
|
||||
|
||||
Assert.Equal ("4123", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mask_With_Invalid_Pattern_Exception ()
|
||||
{
|
||||
// Regex Exception
|
||||
// Maybe it's not the right behaviour.
|
||||
|
||||
var mask = "";
|
||||
|
||||
for (var i = 0; i < 255; i++)
|
||||
{
|
||||
mask += (char)i;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var field = new TextValidateField { Width = 20, Provider = new TextRegexProvider (mask) };
|
||||
}
|
||||
catch (RegexParseException ex)
|
||||
{
|
||||
Assert.True (true, ex.Message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.True (false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnTextChanged_TextChanged_Event ()
|
||||
{
|
||||
var wasTextChanged = false;
|
||||
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") { ValidateOnInput = false }
|
||||
};
|
||||
|
||||
field.Provider.TextChanged += (sender, e) => wasTextChanged = true;
|
||||
|
||||
field.NewKeyDownEvent (Key.D1);
|
||||
|
||||
Assert.Equal ("1", field.Provider.DisplayText);
|
||||
Assert.False (field.IsValid);
|
||||
Assert.Equal ("1", field.Provider.Text);
|
||||
Assert.True (wasTextChanged);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Right_Key_Stops_At_End_And_Insert ()
|
||||
{
|
||||
var field = new TextValidateField
|
||||
{
|
||||
TextAlignment = Alignment.Center,
|
||||
Width = 20,
|
||||
Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") { ValidateOnInput = false }
|
||||
};
|
||||
|
||||
field.Text = "123";
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
field.NewKeyDownEvent (Key.CursorRight);
|
||||
}
|
||||
|
||||
Assert.Equal ("123", field.Text);
|
||||
Assert.True (field.IsValid);
|
||||
|
||||
// Insert 4
|
||||
field.NewKeyDownEvent (Key.D4);
|
||||
|
||||
Assert.Equal ("1234", field.Text);
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Text_With_All_Charset ()
|
||||
{
|
||||
var field = new TextValidateField { Width = 20, Provider = new TextRegexProvider ("^[0-9][0-9][0-9]$") };
|
||||
|
||||
var text = "";
|
||||
|
||||
for (var i = 0; i < 255; i++)
|
||||
{
|
||||
text += (char)i;
|
||||
}
|
||||
|
||||
field.Text = text;
|
||||
|
||||
Assert.False (field.IsValid);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user