From b267e1698e86a707600a7484608042c950e0e5e7 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 19 Aug 2024 10:09:15 -0600 Subject: [PATCH] V2 fix warnings (#3671) * Fixed NumericUpDown warning * Fixed Aot Warning * Fixed warnings in Application.cs * Fixed more NumericUpDown warning * Fixed CommandImpl warning * Fixed Thickness warnings * Fixed Label warning * Fixed warning * Fixed menubar test warning * Fixed warnings * Fixed warnings * Removed dead code * Fixed warnings --- NativeAot/Program.cs | 4 +- .../Application/Application.Keyboard.cs | 33 +- Terminal.Gui/Application/Application.cs | 12 +- Terminal.Gui/Drawing/Thickness.cs | 8 +- Terminal.Gui/View/View.Keyboard.cs | 1 - Terminal.Gui/Views/Label.cs | 4 +- Terminal.Gui/Views/NumericUpDown.cs | 13 +- UICatalog/Scenarios/ListViewWithSelection.cs | 4 +- UICatalog/UICatalog.cs | 2 +- .../Configuration/ConfigurationMangerTests.cs | 2 + UnitTests/Text/TextFormatterTests.cs | 6407 ++++++++--------- UnitTests/View/TitleTests.cs | 2 +- UnitTests/Views/ListViewTests.cs | 2 + UnitTests/Views/MenuBarTests.cs | 2 +- UnitTests/Views/NumericUpDownTests.cs | 2 +- UnitTests/Views/StatusBarTests.cs | 2 +- UnitTests/Views/TableViewTests.cs | 8 + 17 files changed, 3241 insertions(+), 3267 deletions(-) diff --git a/NativeAot/Program.cs b/NativeAot/Program.cs index 5ef38db05..4aae3c099 100644 --- a/NativeAot/Program.cs +++ b/NativeAot/Program.cs @@ -17,14 +17,14 @@ public static class Program #region The code in this region is not intended for use in a native Aot self-contained. It's just here to make sure there is no functionality break with localization in Terminal.Gui using self-contained - if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0) + if (Equals(Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures!.Count == 0) { // Only happens if the project has true Debug.Assert (Application.SupportedCultures.Count == 0); } else { - Debug.Assert (Application.SupportedCultures.Count > 0); + Debug.Assert (Application.SupportedCultures!.Count > 0); Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture)); } diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs index 21e3df5ab..e60ed61d2 100644 --- a/Terminal.Gui/Application/Application.Keyboard.cs +++ b/Terminal.Gui/Application/Application.Keyboard.cs @@ -190,7 +190,7 @@ public static partial class Application // Keyboard handling foreach (Command command in appBinding.Commands) { - if (!CommandImplementations.ContainsKey (command)) + if (!CommandImplementations!.ContainsKey (command)) { throw new NotSupportedException ( @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application." @@ -274,7 +274,7 @@ public static partial class Application // Keyboard handling /// /// Commands for Application. /// - private static Dictionary> CommandImplementations { get; set; } + private static Dictionary>? CommandImplementations { get; set; } /// /// @@ -292,7 +292,7 @@ public static partial class Application // Keyboard handling /// /// The command. /// The function. - private static void AddCommand (Command command, Func f) { CommandImplementations [command] = ctx => f (); } + private static void AddCommand (Command command, Func f) { CommandImplementations! [command] = ctx => f (); } static Application () { AddApplicationKeyBindings (); } @@ -432,31 +432,4 @@ public static partial class Application // Keyboard handling .Distinct () .ToList (); } - - ///// - ///// Gets the list of Views that have key bindings for the specified key. - ///// - ///// - ///// This is an internal method used by the class to add Application key bindings. - ///// - ///// The key to check. - ///// Outputs the list of views bound to - ///// if successful. - //internal static bool TryGetKeyBindings (Key key, out List views) { return _keyBindings.TryGetValue (key, out views); } - - /// - /// Removes all scoped key bindings for the specified view. - /// - /// - /// This is an internal method used by the class to remove Application key bindings. - /// - /// The view that is bound to the key. - internal static void RemoveKeyBindings (View view) - { - List list = KeyBindings.Bindings - .Where (kv => kv.Value.Scope != KeyBindingScope.Application) - .Select (kv => kv.Value) - .Distinct () - .ToList (); - } } diff --git a/Terminal.Gui/Application/Application.cs b/Terminal.Gui/Application/Application.cs index fe1a5cd55..bcb8d5b3b 100644 --- a/Terminal.Gui/Application/Application.cs +++ b/Terminal.Gui/Application/Application.cs @@ -32,7 +32,7 @@ public static partial class Application /// A string representation of the Application public new static string ToString () { - ConsoleDriver driver = Driver; + ConsoleDriver? driver = Driver; if (driver is null) { @@ -47,13 +47,17 @@ public static partial class Application /// /// The driver to use to render the contents. /// A string representation of the Application - public static string ToString (ConsoleDriver driver) + public static string ToString (ConsoleDriver? driver) { + if (driver is null) + { + return string.Empty; + } var sb = new StringBuilder (); - Cell [,] contents = driver.Contents; + Cell [,] contents = driver?.Contents!; - for (var r = 0; r < driver.Rows; r++) + for (var r = 0; r < driver!.Rows; r++) { for (var c = 0; c < driver.Cols; c++) { diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs index 8585ccb38..d96ab73ea 100644 --- a/Terminal.Gui/Drawing/Thickness.cs +++ b/Terminal.Gui/Drawing/Thickness.cs @@ -61,7 +61,7 @@ public record struct Thickness [JsonInclude] public int Bottom { - get => (int)_sides.W; + readonly get => (int)_sides.W; set => _sides.W = value; } @@ -249,7 +249,7 @@ public record struct Thickness [JsonInclude] public int Left { - get => (int)_sides.X; + readonly get => (int)_sides.X; set => _sides.X = value; } @@ -265,7 +265,7 @@ public record struct Thickness [JsonInclude] public int Right { - get => (int)_sides.Z; + readonly get => (int)_sides.Z; set => _sides.Z = value; } @@ -273,7 +273,7 @@ public record struct Thickness [JsonInclude] public int Top { - get => (int)_sides.Y; + readonly get => (int)_sides.Y; set => _sides.Y = value; } diff --git a/Terminal.Gui/View/View.Keyboard.cs b/Terminal.Gui/View/View.Keyboard.cs index 73625417a..7a7fd35c5 100644 --- a/Terminal.Gui/View/View.Keyboard.cs +++ b/Terminal.Gui/View/View.Keyboard.cs @@ -27,7 +27,6 @@ public partial class View // Keyboard APIs private void DisposeKeyboard () { TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged; - Application.RemoveKeyBindings (this); } #region HotKey Support diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs index b05593fda..55f174c34 100644 --- a/Terminal.Gui/Views/Label.cs +++ b/Terminal.Gui/Views/Label.cs @@ -51,9 +51,9 @@ public class Label : View set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value; } - private new bool? FocusNext () + private bool? FocusNext () { - var me = SuperView?.Subviews.IndexOf (this) ?? -1; + int me = SuperView?.Subviews.IndexOf (this) ?? -1; if (me != -1 && me < SuperView?.Subviews.Count - 1) { SuperView?.Subviews [me + 1].SetFocus (); diff --git a/Terminal.Gui/Views/NumericUpDown.cs b/Terminal.Gui/Views/NumericUpDown.cs index 279cfd6cf..7ca7d463a 100644 --- a/Terminal.Gui/Views/NumericUpDown.cs +++ b/Terminal.Gui/Views/NumericUpDown.cs @@ -100,7 +100,7 @@ public class NumericUpDown : View where T : notnull return false; } - if (Value is { }) + if (Value is { } && Increment is { }) { Value = (dynamic)Value + (dynamic)Increment; } @@ -117,11 +117,12 @@ public class NumericUpDown : View where T : notnull return false; } - if (Value is { }) + if (Value is { } && Increment is { }) { Value = (dynamic)Value - (dynamic)Increment; } + return true; }); @@ -218,23 +219,23 @@ public class NumericUpDown : View where T : notnull Text = _number.Text; } - private T _increment; + private T? _increment; /// /// - public T Increment + public T? Increment { get => _increment; set { - if ((dynamic)_increment == (dynamic)value) + if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value) { return; } _increment = value; - IncrementChanged?.Invoke (this, new (value)); + IncrementChanged?.Invoke (this, new (value!)); } } diff --git a/UICatalog/Scenarios/ListViewWithSelection.cs b/UICatalog/Scenarios/ListViewWithSelection.cs index f9bb7ea03..e22b2e2ec 100644 --- a/UICatalog/Scenarios/ListViewWithSelection.cs +++ b/UICatalog/Scenarios/ListViewWithSelection.cs @@ -202,9 +202,11 @@ public class ListViewWithSelection : Scenario return false; } - +#pragma warning disable CS0067 /// public event NotifyCollectionChangedEventHandler CollectionChanged; +#pragma warning restore CS0067 + public int Count => Scenarios?.Count ?? 0; public int Length { get; private set; } public bool SuspendCollectionChangedEvent { get => throw new System.NotImplementedException (); set => throw new System.NotImplementedException (); } diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index aff6dd975..14b06f6e5 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -643,7 +643,7 @@ public class UICatalogApp Add (CategoryList); Add (ScenarioList); - Add (MenuBar); + Add (MenuBar!); if (StatusBar is { }) { diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index 44b681d28..fbca323b5 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -389,6 +389,7 @@ public class ConfigurationManagerTests ) ); +#pragma warning disable xUnit2029 Assert.Empty ( Settings.Where ( cp => cp.Value.PropertyInfo!.GetCustomAttribute ( @@ -397,6 +398,7 @@ public class ConfigurationManagerTests == null ) ); +#pragma warning restore xUnit2029 // Application is a static class PropertyInfo pi = typeof (Application).GetProperty ("QuitKey"); diff --git a/UnitTests/Text/TextFormatterTests.cs b/UnitTests/Text/TextFormatterTests.cs index 31d2f59f0..109065302 100644 --- a/UnitTests/Text/TextFormatterTests.cs +++ b/UnitTests/Text/TextFormatterTests.cs @@ -1,4 +1,5 @@ using System.Text; +using UICatalog; using Xunit.Abstractions; // Alias Console to MockConsole so we don't accidentally use Console @@ -7,37 +8,8 @@ namespace Terminal.Gui.TextTests; public class TextFormatterTests { - private readonly ITestOutputHelper _output; public TextFormatterTests (ITestOutputHelper output) { _output = output; } - - public static IEnumerable CMGlyphs => - new List { new object [] { $"{CM.Glyphs.LeftBracket} Say Hello 你 {CM.Glyphs.RightBracket}", 16, 15 } }; - - public static IEnumerable FormatEnvironmentNewLine => - new List - { - new object [] - { - $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}", - 60, - new [] { "Line1", "Line2", "Line3" } - } - }; - - public static IEnumerable SplitEnvironmentNewLine => - new List - { - new object [] - { - $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界", - new [] { "First Line 界", "Second Line 界", "Third Line 界" } - }, - new object [] - { - $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界{Environment.NewLine}", - new [] { "First Line 界", "Second Line 界", "Third Line 界", "" } - } - }; + private readonly ITestOutputHelper _output; [Theory] [InlineData ("")] @@ -262,2760 +234,8 @@ public class TextFormatterTests ); } - [Theory] - [InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")] - [InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")] - [InlineData ( - 4, - 4, - TextDirection.TopBottom_LeftRight, - @" -LMre -eias -ssb - ęl " - )] - public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected) - { - var driver = new FakeDriver (); - driver.Init (); - - var text = "Les Mise\u0328\u0301rables"; - - var tf = new TextFormatter (); - tf.Direction = textDirection; - tf.Text = text; - - Assert.True (tf.WordWrap); - - tf.ConstrainToSize = new (width, height); - - tf.Draw ( - new (0, 0, width, height), - new (ColorName.White, ColorName.Black), - new (ColorName.Blue, ColorName.Black), - default (Rectangle), - driver - ); - TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); - - driver.End (); - } - - [Fact] - [SetupFakeDriver] - public void FillRemaining_True_False () - { - ((FakeDriver)Application.Driver!).SetBufferSize (22, 5); - - Attribute [] attrs = - { - Attribute.Default, new (ColorName.Green, ColorName.BrightMagenta), - new (ColorName.Blue, ColorName.Cyan) - }; - var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true }; - - tf.Draw ( - new (1, 1, 19, 3), - attrs [1], - attrs [2]); - - Assert.False (tf.FillRemaining); - - TestHelpers.AssertDriverContentsWithFrameAre ( - @" - Test - Test long - Test long long", - _output); - - TestHelpers.AssertDriverAttributesAre ( - @" -000000000000000000000 -011110000000000000000 -011111111100000000000 -011111111111111000000 -000000000000000000000", - null, - attrs); - - tf.FillRemaining = true; - - tf.Draw ( - new (1, 1, 19, 3), - attrs [1], - attrs [2]); - - TestHelpers.AssertDriverAttributesAre ( - @" -000000000000000000000 -011111111111111111110 -011111111111111111110 -011111111111111111110 -000000000000000000000", - null, - attrs); - } - - [Theory] - [InlineData ("_k Before", true, 0, (KeyCode)'K')] // lower case should return uppercase Hotkey - [InlineData ("a_k Second", true, 1, (KeyCode)'K')] - [InlineData ("Last _k", true, 5, (KeyCode)'K')] - [InlineData ("After k_", false, -1, KeyCode.Null)] - [InlineData ("Multiple _k and _R", true, 9, (KeyCode)'K')] - [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к')] // Lower case Cryllic K (к) - [InlineData ("_k Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results - [InlineData ("a_k Second", true, 1, (KeyCode)'K', true)] - [InlineData ("Last _k", true, 5, (KeyCode)'K', true)] - [InlineData ("After k_", false, -1, KeyCode.Null, true)] - [InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)] - [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К) - public void FindHotKey_AlphaLowerCase_Succeeds ( - string text, - bool expectedResult, - int expectedHotPos, - KeyCode expectedKey, - bool supportFirstUpperCase = false - ) - { - var hotKeySpecifier = (Rune)'_'; - - bool result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out int hotPos, - out Key hotKey, - supportFirstUpperCase - ); - - if (expectedResult) - { - Assert.True (result); - } - else - { - Assert.False (result); - } - - Assert.Equal (expectedResult, result); - Assert.Equal (expectedHotPos, hotPos); - Assert.Equal (expectedKey, hotKey); - } - - [Theory] - [InlineData ("_K Before", true, 0, (KeyCode)'K')] - [InlineData ("a_K Second", true, 1, (KeyCode)'K')] - [InlineData ("Last _K", true, 5, (KeyCode)'K')] - [InlineData ("After K_", false, -1, KeyCode.Null)] - [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K')] - [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К) - [InlineData ("_K Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results - [InlineData ("a_K Second", true, 1, (KeyCode)'K', true)] - [InlineData ("Last _K", true, 5, (KeyCode)'K', true)] - [InlineData ("After K_", false, -1, KeyCode.Null, true)] - [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)] - [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К) - public void FindHotKey_AlphaUpperCase_Succeeds ( - string text, - bool expectedResult, - int expectedHotPos, - KeyCode expectedKey, - bool supportFirstUpperCase = false - ) - { - var hotKeySpecifier = (Rune)'_'; - - bool result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out int hotPos, - out Key hotKey, - supportFirstUpperCase - ); - - if (expectedResult) - { - Assert.True (result); - } - else - { - Assert.False (result); - } - - Assert.Equal (expectedResult, result); - Assert.Equal (expectedHotPos, hotPos); - Assert.Equal (expectedKey, hotKey); - } - - [Theory] - [InlineData (null)] - [InlineData ("")] - [InlineData ("no hotkey")] - [InlineData ("No hotkey, Upper Case")] - [InlineData ("Non-english: Сохранить")] - public void FindHotKey_Invalid_ReturnsFalse (string text) - { - var hotKeySpecifier = (Rune)'_'; - var supportFirstUpperCase = false; - var hotPos = 0; - Key hotKey = KeyCode.Null; - var result = false; - - result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out hotPos, - out hotKey, - supportFirstUpperCase - ); - Assert.False (result); - Assert.Equal (-1, hotPos); - Assert.Equal (KeyCode.Null, hotKey); - } - - [Theory] - [InlineData ("\"k before")] - [InlineData ("ak second")] - [InlineData ("last k")] - [InlineData ("multiple k and r")] - [InlineData ("12345")] - [InlineData ("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")] // punctuation - [InlineData (" ~  s  gui.cs   master ↑10")] // ~IsLetterOrDigit + Unicode - [InlineData ("non-english: кдать")] // Lower case Cryllic K (к) - public void FindHotKey_Legacy_FirstUpperCase_NotFound_Returns_False (string text) - { - var supportFirstUpperCase = true; - - var hotKeySpecifier = (Rune)0; - - bool result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out int hotPos, - out Key hotKey, - supportFirstUpperCase - ); - Assert.False (result); - Assert.Equal (-1, hotPos); - Assert.Equal (KeyCode.Null, hotKey); - } - - [Theory] - [InlineData ("K Before", true, 0, (KeyCode)'K')] - [InlineData ("aK Second", true, 1, (KeyCode)'K')] - [InlineData ("last K", true, 5, (KeyCode)'K')] - [InlineData ("multiple K and R", true, 9, (KeyCode)'K')] - [InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К) - public void FindHotKey_Legacy_FirstUpperCase_Succeeds ( - string text, - bool expectedResult, - int expectedHotPos, - KeyCode expectedKey - ) - { - var supportFirstUpperCase = true; - - var hotKeySpecifier = (Rune)0; - - bool result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out int hotPos, - out Key hotKey, - supportFirstUpperCase - ); - - if (expectedResult) - { - Assert.True (result); - } - else - { - Assert.False (result); - } - - Assert.Equal (expectedResult, result); - Assert.Equal (expectedHotPos, hotPos); - Assert.Equal (expectedKey, hotKey); - } - - [Theory] - [InlineData ("_1 Before", true, 0, (KeyCode)'1')] // Digits - [InlineData ("a_1 Second", true, 1, (KeyCode)'1')] - [InlineData ("Last _1", true, 5, (KeyCode)'1')] - [InlineData ("After 1_", false, -1, KeyCode.Null)] - [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1')] - [InlineData ("_1 Before", true, 0, (KeyCode)'1', true)] // Turn on FirstUpperCase and verify same results - [InlineData ("a_1 Second", true, 1, (KeyCode)'1', true)] - [InlineData ("Last _1", true, 5, (KeyCode)'1', true)] - [InlineData ("After 1_", false, -1, KeyCode.Null, true)] - [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)] - public void FindHotKey_Numeric_Succeeds ( - string text, - bool expectedResult, - int expectedHotPos, - KeyCode expectedKey, - bool supportFirstUpperCase = false - ) - { - var hotKeySpecifier = (Rune)'_'; - - bool result = TextFormatter.FindHotKey ( - text, - hotKeySpecifier, - out int hotPos, - out Key hotKey, - supportFirstUpperCase - ); - - if (expectedResult) - { - Assert.True (result); - } - else - { - Assert.False (result); - } - - Assert.Equal (expectedResult, result); - Assert.Equal (expectedHotPos, hotPos); - Assert.Equal (expectedKey, hotKey); - } - - [Theory] - [InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char - [InlineData ("\"_k before", true, KeyCode.K)] - [InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')] - [InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')] - [InlineData ( - "`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?", - true, - (KeyCode)'=' - )] // BUGBUG: Not sure why this fails. Ignore the first and consider the second - [InlineData ("_ ~  s  gui.cs   master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode - [InlineData (" ~  s  gui.cs  _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode - [InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к) - public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected) - { - var hotKeySpecifier = (Rune)'_'; - - bool result = TextFormatter.FindHotKey (text, hotKeySpecifier, out int _, out Key hotKey); - Assert.Equal (found, result); - Assert.Equal (expected, hotKey); - } - - [Fact] - public void Format_Dont_Throw_ArgumentException_With_WordWrap_As_False_And_Keep_End_Spaces_As_True () - { - Exception exception = Record.Exception ( - () => - TextFormatter.Format ( - "Some text", - 4, - Alignment.Start, - false, - true - ) - ); - Assert.Null (exception); - } - - [Theory] - [InlineData ( - "Hello world, how are you today? Pretty neat!", - 44, - 80, - "Hello world, how are you today? Pretty neat!" - )] - public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Horizontal ( - string text, - int runeCount, - int maxWidth, - string justifiedText - ) - { - Assert.Equal (runeCount, text.GetRuneCount ()); - - var fmtText = string.Empty; - - for (int i = text.GetRuneCount (); i < maxWidth; i++) - { - fmtText = TextFormatter.Format (text, i, Alignment.Fill, true) [0]; - Assert.Equal (i, fmtText.GetRuneCount ()); - char c = fmtText [^1]; - Assert.True (text.EndsWith (c)); - } - - Assert.Equal (justifiedText, fmtText); - } - - [Theory] - [InlineData ( - "Hello world, how are you today? Pretty neat!", - 44, - 80, - "Hello world, how are you today? Pretty neat!" - )] - public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Vertical ( - string text, - int runeCount, - int maxWidth, - string justifiedText - ) - { - Assert.Equal (runeCount, text.GetRuneCount ()); - - var fmtText = string.Empty; - - for (int i = text.GetRuneCount (); i < maxWidth; i++) - { - fmtText = TextFormatter.Format ( - text, - i, - Alignment.Fill, - false, - true, - 0, - TextDirection.TopBottom_LeftRight - ) [0]; - Assert.Equal (i, fmtText.GetRuneCount ()); - char c = fmtText [^1]; - Assert.True (text.EndsWith (c)); - } - - Assert.Equal (justifiedText, fmtText); - } - - [Theory] - [InlineData ("Truncate", 3, "Tru")] - [InlineData ("デモエムポンズ", 3, "デ")] - public void Format_Truncate_Simple_And_Wide_Runes (string text, int width, string expected) - { - List list = TextFormatter.Format (text, width, false, false); - Assert.Equal (expected, list [^1]); - } - - [Theory] - [MemberData (nameof (FormatEnvironmentNewLine))] - public void Format_With_PreserveTrailingSpaces_And_Without_PreserveTrailingSpaces ( - string text, - int width, - IEnumerable expected - ) - { - var preserveTrailingSpaces = false; - List formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces); - Assert.Equal (expected, formated); - - preserveTrailingSpaces = true; - formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces); - Assert.Equal (expected, formated); - } - - [Theory] - [InlineData ( - " A sentence has words. \n This is the second Line - 2. ", - 4, - -50, - Alignment.Start, - true, - false, - new [] { " A", "sent", "ence", "has", "word", "s. ", " Thi", "s is", "the", "seco", "nd", "Line", "- 2." }, - " Asentencehaswords. This isthesecondLine- 2." - )] - [InlineData ( - " A sentence has words. \n This is the second Line - 2. ", - 4, - -50, - Alignment.Start, - true, - true, - new [] - { - " A ", - "sent", - "ence", - " ", - "has ", - "word", - "s. ", - " ", - "This", - " is ", - "the ", - "seco", - "nd ", - "Line", - " - ", - "2. " - }, - " A sentence has words. This is the second Line - 2. " - )] - public void Format_WordWrap_PreserveTrailingSpaces ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - bool preserveTrailingSpaces, - IEnumerable resultLines, - string expectedWrappedText - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); - Assert.Equal (list.Count, resultLines.Count ()); - Assert.Equal (resultLines, list); - var wrappedText = string.Empty; - - foreach (string txt in list) - { - wrappedText += txt; - } - - Assert.Equal (expectedWrappedText, wrappedText); - } - - [Theory] - [InlineData ("Hello World", 11)] - [InlineData ("こんにちは世界", 14)] - public void GetColumns_Simple_And_Wide_Runes (string text, int width) { Assert.Equal (width, text.GetColumns ()); } - - [Theory] - [InlineData ("Hello World", 6, 6)] - [InlineData ("こんにちは 世界", 6, 3)] - [MemberData (nameof (CMGlyphs))] - public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength) - { - List runes = text.ToRuneList (); - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); - } - - [Theory] - [InlineData ("test", 3, 3)] - [InlineData ("test", 4, 4)] - [InlineData ("test", 10, 4)] - public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength) - { - List runes = text.ToRuneList (); - - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); - } - - [Theory] - [InlineData ("Hello World", 6, 6)] - [InlineData ("こんにちは 世界", 6, 3)] - public void GetLengthThatFits_Simple_And_Wide_Runes (string text, int columns, int expectedLength) - { - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); - } - - [Theory] - [InlineData ("test", 3, 3)] - [InlineData ("test", 4, 4)] - [InlineData ("test", 10, 4)] - [InlineData ("test", 1, 1)] - [InlineData ("test", 0, 0)] - [InlineData ("test", -1, 0)] - [InlineData (null, -1, 0)] - [InlineData ("", -1, 0)] - public void GetLengthThatFits_String (string text, int columns, int expectedLength) - { - Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); - } - - [Fact] - public void GetLengthThatFits_With_Combining_Runes () - { - var text = "Les Mise\u0328\u0301rables"; - Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14)); - } - - [Fact] - public void GetMaxColsForWidth_With_Combining_Runes () - { - List text = new () { "Les Mis", "e\u0328\u0301", "rables" }; - Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1)); - } - - [Theory] - [InlineData (new [] { "0123456789" }, 1)] - [InlineData (new [] { "Hello World" }, 1)] - [InlineData (new [] { "Hello", "World" }, 2)] - [InlineData (new [] { "こんにちは", "世界" }, 4)] - public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable text, int expectedWidth) - { - Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ())); - } - - [Theory] - [InlineData (new [] { "Hello World" }, 1, 0, 1, 1)] - [InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)] - [InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)] - public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes ( - IEnumerable text, - int expectedWidth, - int index, - int length, - int expectedIndexWidth - ) - { - Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ())); - Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length)); - } - - [Fact] - public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes () - { - List text = new () { "Les Mis", "e\u0328\u0301", "rables" }; - Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1)); - } - - //[Fact] - //public void GetWidestLineLength_With_Combining_Runes () - //{ - // var text = "Les Mise\u0328\u0301rables"; - // Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1)); - //} - - [Fact] - public void Internal_Tests () - { - var tf = new TextFormatter (); - Assert.Equal (KeyCode.Null, tf.HotKey); - tf.HotKey = KeyCode.CtrlMask | KeyCode.Q; - Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, tf.HotKey); - } - - [Theory] - [InlineData ("")] - [InlineData (null)] - [InlineData ("test")] - public void Justify_Invalid (string text) - { - Assert.Equal (text, TextFormatter.Justify (text, 0)); - Assert.Equal (text, TextFormatter.Justify (text, 0)); - Assert.Throws (() => TextFormatter.Justify (text, -1)); - } - - [Theory] - - // Even # of spaces - // 0123456789 - [InlineData ("012 456 89", "012 456 89", 10, 0, "+", true)] - [InlineData ("012 456 89", "012++456+89", 11, 1)] - [InlineData ("012 456 89", "012 456 89", 12, 2, "++", true)] - [InlineData ("012 456 89", "012+++456++89", 13, 3)] - [InlineData ("012 456 89", "012 456 89", 14, 4, "+++", true)] - [InlineData ("012 456 89", "012++++456+++89", 15, 5)] - [InlineData ("012 456 89", "012 456 89", 16, 6, "++++", true)] - [InlineData ("012 456 89", "012 456 89", 30, 20, "+++++++++++", true)] - [InlineData ("012 456 89", "012+++++++++++++456++++++++++++89", 33, 23)] - - // Odd # of spaces - // 01234567890123 - [InlineData ("012 456 89 end", "012 456 89 end", 14, 0, "+", true)] - [InlineData ("012 456 89 end", "012++456+89+end", 15, 1)] - [InlineData ("012 456 89 end", "012++456++89+end", 16, 2)] - [InlineData ("012 456 89 end", "012 456 89 end", 17, 3, "++", true)] - [InlineData ("012 456 89 end", "012+++456++89++end", 18, 4)] - [InlineData ("012 456 89 end", "012+++456+++89++end", 19, 5)] - [InlineData ("012 456 89 end", "012 456 89 end", 20, 6, "+++", true)] - [InlineData ("012 456 89 end", "012++++++++456++++++++89+++++++end", 34, 20)] - [InlineData ("012 456 89 end", "012+++++++++456+++++++++89++++++++end", 37, 23)] - - // Unicode - // Even # of chars - // 0123456789 - [InlineData ("пÑРвРÑ", "пÑРвРÑ", 10, 0, "+", true)] - [InlineData ("пÑРвРÑ", "пÑÐ++вÐ+Ñ", 11, 1)] - [InlineData ("пÑРвРÑ", "пÑРвРÑ", 12, 2, "++", true)] - [InlineData ("пÑРвРÑ", "пÑÐ+++вÐ++Ñ", 13, 3)] - [InlineData ("пÑРвРÑ", "пÑРвРÑ", 14, 4, "+++", true)] - [InlineData ("пÑРвРÑ", "пÑÐ++++вÐ+++Ñ", 15, 5)] - [InlineData ("пÑРвРÑ", "пÑРвРÑ", 16, 6, "++++", true)] - [InlineData ("пÑРвРÑ", "пÑРвРÑ", 30, 20, "+++++++++++", true)] - [InlineData ("пÑРвРÑ", "пÑÐ+++++++++++++вÐ++++++++++++Ñ", 33, 23)] - - // Unicode - // Odd # of chars - // 0123456789 - [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 10, 0, "+", true)] - [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ+вÐ+Ñ", 11, 1)] - [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ++вÐ+Ñ", 12, 2)] - [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 13, 3, "++", true)] - [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ++вÐ++Ñ", 14, 4)] - [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ+++вÐ++Ñ", 15, 5)] - [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 16, 6, "+++", true)] - [InlineData ("Ð ÑРвРÑ", "Ð++++++++ÑÐ++++++++вÐ+++++++Ñ", 30, 20)] - [InlineData ("Ð ÑРвРÑ", "Ð+++++++++ÑÐ+++++++++вÐ++++++++Ñ", 33, 23)] - public void Justify_Sentence ( - string text, - string justifiedText, - int forceToWidth, - int widthOffset, - string replaceWith = null, - bool replace = false - ) - { - var fillChar = '+'; - - Assert.Equal (forceToWidth, text.GetRuneCount () + widthOffset); - - if (replace) - { - justifiedText = text.Replace (" ", replaceWith); - } - - Assert.Equal (justifiedText, TextFormatter.Justify (text, forceToWidth, fillChar)); - Assert.True (Math.Abs (forceToWidth - justifiedText.GetRuneCount ()) < text.Count (s => s == ' ')); - Assert.True (Math.Abs (forceToWidth - justifiedText.GetColumns ()) < text.Count (s => s == ' ')); - } - - [Theory] - [InlineData ("word")] // Even # of chars - [InlineData ("word.")] // Odd # of chars - [InlineData ("пÑивеÑ")] // Unicode (even #) - [InlineData ("пÑивеÑ.")] // Unicode (odd # of chars) - public void Justify_SingleWord (string text) - { - string justifiedText = text; - var fillChar = '+'; - - int width = text.GetRuneCount (); - Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); - width = text.GetRuneCount () + 1; - Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); - width = text.GetRuneCount () + 2; - Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); - width = text.GetRuneCount () + 10; - Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); - width = text.GetRuneCount () + 11; - Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); - } - - [Theory] - [InlineData ("Single Line 界", 14)] - [InlineData ("First Line 界\nSecond Line 界\nThird Line 界\n", 14)] - public void MaxWidthLine_With_And_Without_Newlines (string text, int expected) { Assert.Equal (expected, TextFormatter.GetWidestLineLength (text)); } - - [Theory] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 0, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 1, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 1, - 0, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 0, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 1, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 1, - 0, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 6, - 5, - false, - new [] { "First " } - )] - [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3 " })] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 6, - 5, - true, - new [] { "First ", "Second", "Third ", "Forty ", "Fiftee" } - )] - [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一" })] - [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一", "第二", "第三", "四十", "第十" })] - public void MultiLine_WordWrap_False_Horizontal_Direction ( - string text, - int maxWidth, - int maxHeight, - bool multiLine, - IEnumerable resultLines - ) - { - var tf = new TextFormatter - { - Text = text, ConstrainToSize = new (maxWidth, maxHeight), WordWrap = false, MultiLine = multiLine - }; - - Assert.False (tf.WordWrap); - Assert.True (tf.MultiLine == multiLine); - Assert.Equal (TextDirection.LeftRight_TopBottom, tf.Direction); - - List splitLines = tf.GetLines (); - Assert.Equal (splitLines.Count, resultLines.Count ()); - Assert.Equal (splitLines, resultLines); - } - - [Theory] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 0, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 1, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 1, - 0, - false, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 0, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 0, - 1, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 1, - 0, - true, - new [] { "" } - )] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 6, - 5, - false, - new [] { "First" } - )] - [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3" })] - [InlineData ( - "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", - 6, - 5, - true, - new [] { "First", "Secon", "Third", "Forty", "Fifte", "Seven" } - )] - [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一行 第" })] - [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一行", "第二行" })] - public void MultiLine_WordWrap_False_Vertical_Direction ( - string text, - int maxWidth, - int maxHeight, - bool multiLine, - IEnumerable resultLines - ) - { - var tf = new TextFormatter - { - Text = text, - ConstrainToSize = new (maxWidth, maxHeight), - WordWrap = false, - MultiLine = multiLine, - Direction = TextDirection.TopBottom_LeftRight - }; - - Assert.False (tf.WordWrap); - Assert.True (tf.MultiLine == multiLine); - Assert.Equal (TextDirection.TopBottom_LeftRight, tf.Direction); - - List splitLines = tf.GetLines (); - Assert.Equal (splitLines.Count, resultLines.Count ()); - Assert.Equal (splitLines, resultLines); - } - - [Fact] - public void NeedsFormat_Sets () - { - var testText = "test"; - var testBounds = new Rectangle (0, 0, 100, 1); - var tf = new TextFormatter (); - - tf.Text = "test"; - Assert.True (tf.NeedsFormat); // get_Lines causes a Format - Assert.NotEmpty (tf.GetLines ()); - Assert.False (tf.NeedsFormat); // get_Lines causes a Format - Assert.Equal (testText, tf.Text); - tf.Draw (testBounds, new (), new ()); - Assert.False (tf.NeedsFormat); - - tf.ConstrainToSize = new (1, 1); - Assert.True (tf.NeedsFormat); - Assert.NotEmpty (tf.GetLines ()); - Assert.False (tf.NeedsFormat); // get_Lines causes a Format - - tf.Alignment = Alignment.Center; - Assert.True (tf.NeedsFormat); - Assert.NotEmpty (tf.GetLines ()); - Assert.False (tf.NeedsFormat); // get_Lines causes a Format - } - - [Theory] - [InlineData ("", -1, Alignment.Start, false, 0)] - [InlineData (null, 0, Alignment.Start, false, 1)] - [InlineData (null, 0, Alignment.Start, true, 1)] - [InlineData ("", 0, Alignment.Start, false, 1)] - [InlineData ("", 0, Alignment.Start, true, 1)] - public void Reformat_Invalid (string text, int maxWidth, Alignment alignment, bool wrap, int linesCount) - { - if (maxWidth < 0) - { - Assert.Throws ( - () => - TextFormatter.Format (text, maxWidth, alignment, wrap) - ); - } - else - { - List list = TextFormatter.Format (text, maxWidth, alignment, wrap); - Assert.NotEmpty (list); - Assert.True (list.Count == linesCount); - Assert.Equal (string.Empty, list [0]); - } - } - - [Theory] - [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true)] - [InlineData ("A sentence has words.\nLine 2.", 1, -28, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\nLine 2.", 5, -24, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\nLine 2.", 28, -1, Alignment.Start, false, 1, false)] - - // no clip - [InlineData ("A sentence has words.\nLine 2.", 29, 0, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\nLine 2.", 30, 1, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true)] - [InlineData ("A sentence has words.\r\nLine 2.", 1, -29, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\r\nLine 2.", 5, -25, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\r\nLine 2.", 29, -1, Alignment.Start, false, 1, false, 1)] - [InlineData ("A sentence has words.\r\nLine 2.", 30, 0, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.\r\nLine 2.", 31, 1, Alignment.Start, false, 1, false)] - public void Reformat_NoWordrap_NewLines_MultiLine_False ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - int linesCount, - bool stringEmpty, - int clipWidthOffset = 0 - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset; - List list = TextFormatter.Format (text, maxWidth, alignment, wrap); - Assert.NotEmpty (list); - Assert.True (list.Count == linesCount); - - if (stringEmpty) - { - Assert.Equal (string.Empty, list [0]); - } - else - { - Assert.NotEqual (string.Empty, list [0]); - } - - if (text.Contains ("\r\n") && maxWidth > 0) - { - Assert.Equal ( - StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]) - .Replace ("\r\n", " "), - list [0] - ); - } - else if (text.Contains ('\n') && maxWidth > 0) - { - Assert.Equal ( - StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]) - .Replace ("\n", " "), - list [0] - ); - } - else - { - Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]); - } - } - - [Theory] - [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })] - [InlineData ( - "A sentence has words.\nLine 2.", - 1, - -28, - Alignment.Start, - false, - 2, - false, - new [] { "A", "L" } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 5, - -24, - Alignment.Start, - false, - 2, - false, - new [] { "A sen", "Line " } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 28, - -1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - //// no clip - [InlineData ( - "A sentence has words.\nLine 2.", - 29, - 0, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 30, - 1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 1, - -29, - Alignment.Start, - false, - 2, - false, - new [] { "A", "L" } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 5, - -25, - Alignment.Start, - false, - 2, - false, - new [] { "A sen", "Line " } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 29, - -1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 30, - 0, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 31, - 1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - public void Reformat_NoWordrap_NewLines_MultiLine_True ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - int linesCount, - bool stringEmpty, - IEnumerable resultLines - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - - List list = TextFormatter.Format ( - text, - maxWidth, - alignment, - wrap, - false, - 0, - TextDirection.LeftRight_TopBottom, - true - ); - Assert.NotEmpty (list); - Assert.True (list.Count == linesCount); - - if (stringEmpty) - { - Assert.Equal (string.Empty, list [0]); - } - else - { - Assert.NotEqual (string.Empty, list [0]); - } - - Assert.Equal (list, resultLines); - } - - [Theory] - [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })] - [InlineData ( - "A sentence has words.\nLine 2.", - 1, - -28, - Alignment.Start, - false, - 2, - false, - new [] { "A", "L" } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 5, - -24, - Alignment.Start, - false, - 2, - false, - new [] { "A sen", "Line " } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 28, - -1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - //// no clip - [InlineData ( - "A sentence has words.\nLine 2.", - 29, - 0, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\nLine 2.", - 30, - 1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 1, - -29, - Alignment.Start, - false, - 2, - false, - new [] { "A", "L" } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 5, - -25, - Alignment.Start, - false, - 2, - false, - new [] { "A sen", "Line " } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 29, - -1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 30, - 0, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - [InlineData ( - "A sentence has words.\r\nLine 2.", - 31, - 1, - Alignment.Start, - false, - 2, - false, - new [] { "A sentence has words.", "Line 2." } - )] - public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - int linesCount, - bool stringEmpty, - IEnumerable resultLines - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - - List list = TextFormatter.Format ( - text, - maxWidth, - alignment, - wrap, - false, - 0, - TextDirection.TopBottom_LeftRight, - true - ); - Assert.NotEmpty (list); - Assert.True (list.Count == linesCount); - - if (stringEmpty) - { - Assert.Equal (string.Empty, list [0]); - } - else - { - Assert.NotEqual (string.Empty, list [0]); - } - - Assert.Equal (list, resultLines); - } - - [Theory] - [InlineData ("", 0, 0, Alignment.Start, false, 1, true)] - [InlineData ("", 1, 1, Alignment.Start, false, 1, true)] - [InlineData ("A sentence has words.", 0, -21, Alignment.Start, false, 1, true)] - [InlineData ("A sentence has words.", 1, -20, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.", 5, -16, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.", 20, -1, Alignment.Start, false, 1, false)] - - // no clip - [InlineData ("A sentence has words.", 21, 0, Alignment.Start, false, 1, false)] - [InlineData ("A sentence has words.", 22, 1, Alignment.Start, false, 1, false)] - public void Reformat_NoWordrap_SingleLine ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - int linesCount, - bool stringEmpty - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - List list = TextFormatter.Format (text, maxWidth, alignment, wrap); - Assert.NotEmpty (list); - Assert.True (list.Count == linesCount); - - if (stringEmpty) - { - Assert.Equal (string.Empty, list [0]); - } - else - { - Assert.NotEqual (string.Empty, list [0]); - } - - Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]); - } - - [Theory] - - // Unicode - [InlineData ( - "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", - 8, - -1, - Alignment.Start, - true, - false, - new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } - )] - - // no clip - [InlineData ( - "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", - 9, - 0, - Alignment.Start, - true, - false, - new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } - )] - [InlineData ( - "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", - 10, - 1, - Alignment.Start, - true, - false, - new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } - )] - public void Reformat_Unicode_Wrap_Spaces_NewLines ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - bool preserveTrailingSpaces, - IEnumerable resultLines - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); - Assert.Equal (list.Count, resultLines.Count ()); - Assert.Equal (resultLines, list); - } - - [Theory] - - // Unicode - // Even # of chars - // 0123456789 - [InlineData ("\u2660пÑРвРÑ", 10, -1, Alignment.Start, true, false, new [] { "\u2660пÑРвÐ", "Ñ" })] - - // no clip - [InlineData ("\u2660пÑРвРÑ", 11, 0, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })] - [InlineData ("\u2660пÑРвРÑ", 12, 1, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })] - - // Unicode - // Odd # of chars - // 0123456789 - [InlineData ("\u2660 ÑРвРÑ", 9, -1, Alignment.Start, true, false, new [] { "\u2660 ÑРвÐ", "Ñ" })] - - // no clip - [InlineData ("\u2660 ÑРвРÑ", 10, 0, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })] - [InlineData ("\u2660 ÑРвРÑ", 11, 1, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })] - public void Reformat_Unicode_Wrap_Spaces_No_NewLines ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - bool preserveTrailingSpaces, - IEnumerable resultLines - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); - Assert.Equal (list.Count, resultLines.Count ()); - Assert.Equal (resultLines, list); - } - - [Theory] - - // Even # of spaces - // 0123456789 - [InlineData ("012 456 89", 0, -10, Alignment.Start, true, true, true, new [] { "" })] - [InlineData ( - "012 456 89", - 1, - -9, - Alignment.Start, - true, - true, - false, - new [] { "0", "1", "2", " ", "4", "5", "6", " ", "8", "9" }, - "01245689" - )] - [InlineData ("012 456 89", 5, -5, Alignment.Start, true, true, false, new [] { "012 ", "456 ", "89" })] - [InlineData ("012 456 89", 9, -1, Alignment.Start, true, true, false, new [] { "012 456 ", "89" })] - - // no clip - [InlineData ("012 456 89", 10, 0, Alignment.Start, true, true, false, new [] { "012 456 89" })] - [InlineData ("012 456 89", 11, 1, Alignment.Start, true, true, false, new [] { "012 456 89" })] - - // Odd # of spaces - // 01234567890123 - [InlineData ("012 456 89 end", 13, -1, Alignment.Start, true, true, false, new [] { "012 456 89 ", "end" })] - - // no clip - [InlineData ("012 456 89 end", 14, 0, Alignment.Start, true, true, false, new [] { "012 456 89 end" })] - [InlineData ("012 456 89 end", 15, 1, Alignment.Start, true, true, false, new [] { "012 456 89 end" })] - public void Reformat_Wrap_Spaces_No_NewLines ( - string text, - int maxWidth, - int widthOffset, - Alignment alignment, - bool wrap, - bool preserveTrailingSpaces, - bool stringEmpty, - IEnumerable resultLines, - string noSpaceText = "" - ) - { - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); - Assert.NotEmpty (list); - Assert.True (list.Count == resultLines.Count ()); - - if (stringEmpty) - { - Assert.Equal (string.Empty, list [0]); - } - else - { - Assert.NotEqual (string.Empty, list [0]); - } - - Assert.Equal (resultLines, list); - - if (maxWidth > 0) - { - // remove whitespace chars - if (maxWidth < 5) - { - expectedClippedWidth = text.GetRuneCount () - text.Sum (r => r == ' ' ? 1 : 0); - } - else - { - expectedClippedWidth = Math.Min ( - text.GetRuneCount (), - maxWidth - text.Sum (r => r == ' ' ? 1 : 0) - ); - } - - list = TextFormatter.Format (text, maxWidth, Alignment.Start, wrap); - - if (maxWidth == 1) - { - Assert.Equal (expectedClippedWidth, list.Count); - Assert.Equal (noSpaceText, string.Concat (list.ToArray ())); - } - - if (maxWidth > 1 && maxWidth < 10) - { - Assert.Equal ( - StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), - list [0] - ); - } - } - } - - [Theory] - [InlineData (null)] - [InlineData ("")] - [InlineData ("a")] - public void RemoveHotKeySpecifier_InValid_ReturnsOriginal (string text) - { - var hotKeySpecifier = (Rune)'_'; - - if (text == null) - { - Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier)); - Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier)); - Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier)); - } - else - { - Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier)); - Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier)); - Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier)); - } - } - - [Theory] - [InlineData ("all lower case", 0)] - [InlineData ("K Before", 0)] - [InlineData ("aK Second", 1)] - [InlineData ("Last K", 5)] - [InlineData ("fter K", 7)] - [InlineData ("Multiple K and R", 9)] - [InlineData ("Non-english: Кдать", 13)] - public void RemoveHotKeySpecifier_Valid_Legacy_ReturnsOriginal (string text, int hotPos) - { - var hotKeySpecifier = (Rune)'_'; - - Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier)); - } - - [Theory] - [InlineData ("_K Before", 0, "K Before")] - [InlineData ("a_K Second", 1, "aK Second")] - [InlineData ("Last _K", 5, "Last K")] - [InlineData ("After K_", 7, "After K")] - [InlineData ("Multiple _K and _R", 9, "Multiple K and _R")] - [InlineData ("Non-english: _Кдать", 13, "Non-english: Кдать")] - public void RemoveHotKeySpecifier_Valid_ReturnsStripped (string text, int hotPos, string expectedText) - { - var hotKeySpecifier = (Rune)'_'; - - Assert.Equal (expectedText, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier)); - } - - [Theory] - [InlineData ("test", 0, 't', "test")] - [InlineData ("test", 1, 'e', "test")] - [InlineData ("Ok", 0, 'O', "Ok")] - [InlineData ("[◦ Ok ◦]", 3, 'O', "[◦ Ok ◦]")] - [InlineData ("^k", 0, '^', "^k")] - public void ReplaceHotKeyWithTag (string text, int hotPos, uint tag, string expected) - { - var tf = new TextFormatter (); - List runes = text.ToRuneList (); - Rune rune; - - if (Rune.TryGetRuneAt (text, hotPos, out rune)) - { - Assert.Equal (rune, (Rune)tag); - } - - string result = TextFormatter.ReplaceHotKeyWithTag (text, hotPos); - Assert.Equal (result, expected); - Assert.Equal ((Rune)tag, result.ToRunes () [hotPos]); - Assert.Equal (text.GetRuneCount (), runes.Count); - Assert.Equal (text, StringExtensions.ToString (runes)); - } - - [Theory] - [MemberData (nameof (SplitEnvironmentNewLine))] - public void SplitNewLine_Ending__With_Or_Without_NewLine_Probably_CRLF ( - string text, - IEnumerable expected - ) - { - List splited = TextFormatter.SplitNewLine (text); - Assert.Equal (expected, splited); - } - - [Theory] - [InlineData ( - "First Line 界\nSecond Line 界\nThird Line 界\n", - new [] { "First Line 界", "Second Line 界", "Third Line 界", "" } - )] - public void SplitNewLine_Ending_With_NewLine_Only_LF (string text, IEnumerable expected) - { - List splited = TextFormatter.SplitNewLine (text); - Assert.Equal (expected, splited); - } - - [Theory] - [InlineData ( - "First Line 界\nSecond Line 界\nThird Line 界", - new [] { "First Line 界", "Second Line 界", "Third Line 界" } - )] - public void SplitNewLine_Ending_Without_NewLine_Only_LF (string text, IEnumerable expected) - { - List splited = TextFormatter.SplitNewLine (text); - Assert.Equal (expected, splited); - } - - [Theory] - [InlineData ("New Test 你", 10, 10, 20320, 20320, 9, "你")] - [InlineData ("New Test \U0001d539", 10, 11, 120121, 55349, 9, "𝔹")] - public void String_Array_Is_Not_Always_Equal_ToRunes_Array ( - string text, - int runesLength, - int stringLength, - int runeValue, - int stringValue, - int index, - string expected - ) - { - Rune [] usToRunes = text.ToRunes (); - Assert.Equal (runesLength, usToRunes.Length); - Assert.Equal (stringLength, text.Length); - Assert.Equal (runeValue, usToRunes [index].Value); - Assert.Equal (stringValue, text [index]); - Assert.Equal (expected, usToRunes [index].ToString ()); - - if (char.IsHighSurrogate (text [index])) - { - // Rune array length isn't equal to string array - Assert.Equal (expected, new (new [] { text [index], text [index + 1] })); - } - else - { - // Rune array length is equal to string array - Assert.Equal (expected, text [index].ToString ()); - } - } - - [Theory] - [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] - [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] - [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] - [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] - public void TabWith_PreserveTrailingSpaces_False ( - int width, - int height, - TextDirection textDirection, - int tabWidth, - string expected - ) - { - var driver = new FakeDriver (); - driver.Init (); - - var text = "This is a \tTab"; - var tf = new TextFormatter (); - tf.Direction = textDirection; - tf.TabWidth = tabWidth; - tf.Text = text; - tf.ConstrainToWidth = 20; - tf.ConstrainToHeight = 20; - - Assert.True (tf.WordWrap); - Assert.False (tf.PreserveTrailingSpaces); - - tf.Draw ( - new (0, 0, width, height), - new (ColorName.White, ColorName.Black), - new (ColorName.Blue, ColorName.Black), - default (Rectangle), - driver - ); - TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); - - driver.End (); - } - - [Theory] - [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] - [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] - [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] - [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] - public void TabWith_PreserveTrailingSpaces_True ( - int width, - int height, - TextDirection textDirection, - int tabWidth, - string expected - ) - { - var driver = new FakeDriver (); - driver.Init (); - - var text = "This is a \tTab"; - var tf = new TextFormatter (); - - tf.Direction = textDirection; - tf.TabWidth = tabWidth; - tf.PreserveTrailingSpaces = true; - tf.Text = text; - tf.ConstrainToWidth = 20; - tf.ConstrainToHeight = 20; - - Assert.True (tf.WordWrap); - - tf.Draw ( - new (0, 0, width, height), - new (ColorName.White, ColorName.Black), - new (ColorName.Blue, ColorName.Black), - default (Rectangle), - driver - ); - TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); - - driver.End (); - } - - [Theory] - [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] - [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] - [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] - [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] - public void TabWith_WordWrap_True ( - int width, - int height, - TextDirection textDirection, - int tabWidth, - string expected - ) - { - var driver = new FakeDriver (); - driver.Init (); - - var text = "This is a \tTab"; - var tf = new TextFormatter (); - - tf.Direction = textDirection; - tf.TabWidth = tabWidth; - tf.WordWrap = true; - tf.Text = text; - tf.ConstrainToWidth = 20; - tf.ConstrainToHeight = 20; - - Assert.False (tf.PreserveTrailingSpaces); - - tf.Draw ( - new (0, 0, width, height), - new (ColorName.White, ColorName.Black), - new (ColorName.Blue, ColorName.Black), - default (Rectangle), - driver - ); - TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); - - driver.End (); - } - - [Theory] - [InlineData ("123456789", 3, "123")] - [InlineData ("Hello World", 8, "Hello Wo")] - public void TestClipOrPad_LongWord (string text, int fillPad, string expectedText) - { - // word is long but we want it to fill # space only - Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad)); - } - - [Theory] - [InlineData ("fff", 6, "fff ")] - [InlineData ("Hello World", 16, "Hello World ")] - public void TestClipOrPad_ShortWord (string text, int fillPad, string expectedText) - { - // word is short but we want it to fill # so it should be padded - Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad)); - } - - [Theory] - [InlineData ("你", TextDirection.LeftRight_TopBottom, 2, 1)] - [InlineData ("你", TextDirection.TopBottom_LeftRight, 2, 1)] - [InlineData ("你你", TextDirection.LeftRight_TopBottom, 4, 1)] - [InlineData ("你你", TextDirection.TopBottom_LeftRight, 2, 2)] - public void Text_Set_SizeIsCorrect (string text, TextDirection textDirection, int expectedWidth, int expectedHeight) - { - var tf = new TextFormatter { Direction = textDirection, Text = text }; - tf.ConstrainToWidth = 10; - tf.ConstrainToHeight = 10; - - Assert.Equal (new (expectedWidth, expectedHeight), tf.FormatAndGetSize ()); - } - - [Fact] - public void WordWrap_BigWidth () - { - List wrappedLines; - - var text = "Constantinople"; - wrappedLines = TextFormatter.WordWrapText (text, 100); - Assert.True (wrappedLines.Count == 1); - Assert.Equal ("Constantinople", wrappedLines [0]); - } - - [Fact] - public void WordWrap_Invalid () - { - var text = string.Empty; - var width = 0; - - Assert.Empty (TextFormatter.WordWrapText (null, width)); - Assert.Empty (TextFormatter.WordWrapText (text, width)); - Assert.Throws (() => TextFormatter.WordWrapText (text, -1)); - } - - [Theory] - [InlineData ("A sentence has words.", 3, -18, new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." })] - [InlineData ( - "A sentence has words.", - 2, - -19, - new [] { "A", "se", "nt", "en", "ce", "ha", "s", "wo", "rd", "s." } - )] - [InlineData ( - "A sentence has words.", - 1, - -20, - new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." } - )] - public void WordWrap_Narrow_Default ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("A sentence has words.", 21, 0, new [] { "A sentence has words." })] - [InlineData ("A sentence has words.", 20, -1, new [] { "A sentence has", "words." })] - [InlineData ("A sentence has words.", 15, -6, new [] { "A sentence has", "words." })] - [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence has", "words." })] - [InlineData ("A sentence has words.", 13, -8, new [] { "A sentence", "has words." })] - - // Unicode - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 42, - 0, - new [] { "A Unicode sentence (пÑивеÑ) has words." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 41, - -1, - new [] { "A Unicode sentence (пÑивеÑ) has", "words." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 36, - -6, - new [] { "A Unicode sentence (пÑивеÑ) has", "words." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 35, - -7, - new [] { "A Unicode sentence (пÑивеÑ) has", "words." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 34, - -8, - new [] { "A Unicode sentence (пÑивеÑ)", "has words." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.", - 25, - -17, - new [] { "A Unicode sentence", "(пÑивеÑ) has words." } - )] - public void WordWrap_NoNewLines_Default ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("これが最初の行です。 こんにちは世界。 これが2行目です。", 29, 0, new [] { "これが最初の行です。", "こんにちは世界。", "これが2行目です。" })] - public void WordWrap_PreserveTrailingSpaces_False_Unicode_Wide_Runes ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉", "があり ます。" })] - [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })] - [InlineData ("文に は言葉 があり ます。", 2, -12, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })] - [InlineData ( - "文に は言葉 があり ます。", - 1, - -13, - new [] { " ", " ", " " } - )] // Just Spaces; should result in a single space for each line - public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData (null, 1, new string [] { })] // null input - [InlineData ("", 1, new string [] { })] // Empty input - [InlineData ("1 34", 1, new [] { "1", "3", "4" })] // Single Spaces - [InlineData ("1", 1, new [] { "1" })] // Short input - [InlineData ("12", 1, new [] { "1", "2" })] - [InlineData ("123", 1, new [] { "1", "2", "3" })] - [InlineData ("123456", 1, new [] { "1", "2", "3", "4", "5", "6" })] // No spaces - [InlineData (" ", 1, new [] { " " })] // Just Spaces; should result in a single space - [InlineData (" ", 1, new [] { " " })] - [InlineData (" ", 1, new [] { " ", " " })] - [InlineData (" ", 1, new [] { " ", " " })] - [InlineData ("12 456", 1, new [] { "1", "2", "4", "5", "6" })] // Single Spaces - [InlineData (" 2 456", 1, new [] { " ", "2", "4", "5", "6" })] // Leading spaces should be preserved. - [InlineData (" 2 456 8", 1, new [] { " ", "2", "4", "5", "6", "8" })] - [InlineData ( - "A sentence has words. ", - 1, - new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." } - )] // Complex example - [InlineData ("12 567", 1, new [] { "1", "2", " ", "5", "6", "7" })] // Double Spaces - [InlineData (" 3 567", 1, new [] { " ", "3", "5", "6", "7" })] // Double Leading spaces should be preserved. - [InlineData (" 3 678 1", 1, new [] { " ", "3", " ", "6", "7", "8", " ", "1" })] - [InlineData ("1 456", 1, new [] { "1", " ", "4", "5", "6" })] - [InlineData ( - "A sentence has words. ", - 1, - new [] - { - "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", "w", "o", "r", "d", "s", ".", " " - } - )] // Double space Complex example - public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_1 ( - string text, - int width, - IEnumerable resultLines - ) - { - List wrappedLines = TextFormatter.WordWrapText (text, width); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - Assert.Equal (resultLines, wrappedLines); - var breakLines = ""; - - foreach (string line in wrappedLines) - { - breakLines += $"{line}{Environment.NewLine}"; - } - - var expected = string.Empty; - - foreach (string line in resultLines) - { - expected += $"{line}{Environment.NewLine}"; - } - - Assert.Equal (expected, breakLines); - } - - [Theory] - [InlineData (null, 3, new string [] { })] // null input - [InlineData ("", 3, new string [] { })] // Empty input - [InlineData ("1", 3, new [] { "1" })] // Short input - [InlineData ("12", 3, new [] { "12" })] - [InlineData ("123", 3, new [] { "123" })] - [InlineData ("123456", 3, new [] { "123", "456" })] // No spaces - [InlineData ("1234567", 3, new [] { "123", "456", "7" })] // No spaces - [InlineData (" ", 3, new [] { " " })] // Just Spaces; should result in a single space - [InlineData (" ", 3, new [] { " " })] - [InlineData (" ", 3, new [] { " " })] - [InlineData (" ", 3, new [] { " " })] - [InlineData ("12 456", 3, new [] { "12", "456" })] // Single Spaces - [InlineData (" 2 456", 3, new [] { " 2", "456" })] // Leading spaces should be preserved. - [InlineData (" 2 456 8", 3, new [] { " 2", "456", "8" })] - [InlineData ( - "A sentence has words. ", - 3, - new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." } - )] // Complex example - [InlineData ("12 567", 3, new [] { "12 ", "567" })] // Double Spaces - [InlineData (" 3 567", 3, new [] { " 3", "567" })] // Double Leading spaces should be preserved. - [InlineData (" 3 678 1", 3, new [] { " 3", " 67", "8 ", "1" })] - [InlineData ("1 456", 3, new [] { "1 ", "456" })] - [InlineData ( - "A sentence has words. ", - 3, - new [] { "A ", "sen", "ten", "ce ", " ", "has", "wor", "ds.", " " } - )] // Double space Complex example - public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_3 ( - string text, - int width, - IEnumerable resultLines - ) - { - List wrappedLines = TextFormatter.WordWrapText (text, width); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - Assert.Equal (resultLines, wrappedLines); - var breakLines = ""; - - foreach (string line in wrappedLines) - { - breakLines += $"{line}{Environment.NewLine}"; - } - - var expected = string.Empty; - - foreach (string line in resultLines) - { - expected += $"{line}{Environment.NewLine}"; - } - - Assert.Equal (expected, breakLines); - } - - [Theory] - [InlineData (null, 50, new string [] { })] // null input - [InlineData ("", 50, new string [] { })] // Empty input - [InlineData ("1", 50, new [] { "1" })] // Short input - [InlineData ("12", 50, new [] { "12" })] - [InlineData ("123", 50, new [] { "123" })] - [InlineData ("123456", 50, new [] { "123456" })] // No spaces - [InlineData ("1234567", 50, new [] { "1234567" })] // No spaces - [InlineData (" ", 50, new [] { " " })] // Just Spaces; should result in a single space - [InlineData (" ", 50, new [] { " " })] - [InlineData (" ", 50, new [] { " " })] - [InlineData ("12 456", 50, new [] { "12 456" })] // Single Spaces - [InlineData (" 2 456", 50, new [] { " 2 456" })] // Leading spaces should be preserved. - [InlineData (" 2 456 8", 50, new [] { " 2 456 8" })] - [InlineData ("A sentence has words. ", 50, new [] { "A sentence has words. " })] // Complex example - [InlineData ("12 567", 50, new [] { "12 567" })] // Double Spaces - [InlineData (" 3 567", 50, new [] { " 3 567" })] // Double Leading spaces should be preserved. - [InlineData (" 3 678 1", 50, new [] { " 3 678 1" })] - [InlineData ("1 456", 50, new [] { "1 456" })] - [InlineData ( - "A sentence has words. ", - 50, - new [] { "A sentence has words. " } - )] // Double space Complex example - public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_50 ( - string text, - int width, - IEnumerable resultLines - ) - { - List wrappedLines = TextFormatter.WordWrapText (text, width); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - Assert.Equal (resultLines, wrappedLines); - var breakLines = ""; - - foreach (string line in wrappedLines) - { - breakLines += $"{line}{Environment.NewLine}"; - } - - var expected = string.Empty; - - foreach (string line in resultLines) - { - expected += $"{line}{Environment.NewLine}"; - } - - Assert.Equal (expected, breakLines); - } - - [Theory] - [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence ", "has words." })] - [InlineData ("A sentence has words.", 8, -13, new [] { "A ", "sentence", " has ", "words." })] - [InlineData ("A sentence has words.", 6, -15, new [] { "A ", "senten", "ce ", "has ", "words." })] - [InlineData ("A sentence has words.", 3, -18, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds." })] - [InlineData ( - "A sentence has words.", - 2, - -19, - new [] { "A ", "se", "nt", "en", "ce", " ", "ha", "s ", "wo", "rd", "s." } - )] - [InlineData ( - "A sentence has words.", - 1, - -20, - new [] - { - "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", " ", "w", "o", "r", "d", "s", "." - } - )] - public void WordWrap_PreserveTrailingSpaces_True ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉 ", "があり ます。" })] - [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に ", "は", "言", "葉 ", "が", "あ", "り ", "ま", "す", "。" })] - [InlineData ( - "文に は言葉 があり ます。", - 2, - -12, - new [] { "文", "に", " ", "は", "言", "葉", " ", "が", "あ", "り", " ", "ま", "す", "。" } - )] - [InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })] - public void WordWrap_PreserveTrailingSpaces_True_Wide_Runes ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("A sentence has words. ", 3, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds.", " " })] - [InlineData ( - "A sentence has words. ", - 3, - new [] { "A ", " ", "sen", "ten", "ce ", " ", " ", " ", "has", " ", "wor", "ds.", " " } - )] - public void WordWrap_PreserveTrailingSpaces_True_With_Simple_Runes_Width_3 ( - string text, - int width, - IEnumerable resultLines - ) - { - List wrappedLines = TextFormatter.WordWrapText (text, width, true); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - Assert.Equal (resultLines, wrappedLines); - var breakLines = ""; - - foreach (string line in wrappedLines) - { - breakLines += $"{line}{Environment.NewLine}"; - } - - var expected = string.Empty; - - foreach (string line in resultLines) - { - expected += $"{line}{Environment.NewLine}"; - } - - Assert.Equal (expected, breakLines); - - // Double space Complex example - this is how VS 2022 does it - //text = "A sentence has words. "; - //breakLines = ""; - //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true); - //foreach (var line in wrappedLines) { - // breakLines += $"{line}{Environment.NewLine}"; - //} - //expected = "A " + Environment.NewLine + - // " se" + Environment.NewLine + - // " nt" + Environment.NewLine + - // " en" + Environment.NewLine + - // " ce" + Environment.NewLine + - // " " + Environment.NewLine + - // " " + Environment.NewLine + - // " " + Environment.NewLine + - // " ha" + Environment.NewLine + - // " s " + Environment.NewLine + - // " wo" + Environment.NewLine + - // " rd" + Environment.NewLine + - // " s." + Environment.NewLine; - //Assert.Equal (expected, breakLines); - } - - [Theory] - [InlineData ("A sentence\t\t\t has words.", 14, -10, new [] { "A sentence\t", "\t\t has ", "words." })] - [InlineData ( - "A sentence\t\t\t has words.", - 8, - -16, - new [] { "A ", "sentence", "\t\t", "\t ", "has ", "words." } - )] - [InlineData ( - "A sentence\t\t\t has words.", - 3, - -21, - new [] { "A ", "sen", "ten", "ce", "\t", "\t", "\t", " ", "has", " ", "wor", "ds." } - )] - [InlineData ( - "A sentence\t\t\t has words.", - 2, - -22, - new [] { "A ", "se", "nt", "en", "ce", "\t", "\t", "\t", " ", "ha", "s ", "wo", "rd", "s." } - )] - [InlineData ( - "A sentence\t\t\t has words.", - 1, - -23, - new [] - { - "A", - " ", - "s", - "e", - "n", - "t", - "e", - "n", - "c", - "e", - "\t", - "\t", - "\t", - " ", - "h", - "a", - "s", - " ", - "w", - "o", - "r", - "d", - "s", - "." - } - )] - public void WordWrap_PreserveTrailingSpaces_True_With_Tab ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines, - int tabWidth = 4 - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true, tabWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("Constantinople", 14, 0, new [] { "Constantinople" })] - [InlineData ("Constantinople", 12, -2, new [] { "Constantinop", "le" })] - [InlineData ("Constantinople", 9, -5, new [] { "Constanti", "nople" })] - [InlineData ("Constantinople", 7, -7, new [] { "Constan", "tinople" })] - [InlineData ("Constantinople", 5, -9, new [] { "Const", "antin", "ople" })] - [InlineData ("Constantinople", 4, -10, new [] { "Cons", "tant", "inop", "le" })] - [InlineData ( - "Constantinople", - 1, - -13, - new [] { "C", "o", "n", "s", "t", "a", "n", "t", "i", "n", "o", "p", "l", "e" } - )] - public void WordWrap_SingleWordLine ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 20, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] - [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 19, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] - [InlineData ( - "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.", - 19, - 0, - new [] { "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence." } - )] - public void WordWrap_Unicode_2LinesWithNonBreakingSpace ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 19, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] - [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 18, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence", "." })] - [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 17, -2, new [] { "This\u00A0is\u00A0a\u00A0sentenc", "e." })] - [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 14, -5, new [] { "This\u00A0is\u00A0a\u00A0sent", "ence." })] - [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 10, -9, new [] { "This\u00A0is\u00A0a\u00A0", "sentence." })] - [InlineData ( - "This\u00A0is\u00A0a\u00A0sentence.", - 7, - -12, - new [] { "This\u00A0is", "\u00A0a\u00A0sent", "ence." } - )] - [InlineData ( - "This\u00A0is\u00A0a\u00A0sentence.", - 5, - -14, - new [] { "This\u00A0", "is\u00A0a\u00A0", "sente", "nce." } - )] - [InlineData ( - "This\u00A0is\u00A0a\u00A0sentence.", - 1, - -18, - new [] - { - "T", "h", "i", "s", "\u00A0", "i", "s", "\u00A0", "a", "\u00A0", "s", "e", "n", "t", "e", "n", "c", "e", "." - } - )] - public void WordWrap_Unicode_LineWithNonBreakingSpace ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [Theory] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 51, - 0, - new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 50, - -1, - new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 46, - -5, - new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 26, - -25, - new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 17, - -34, - new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 13, - -38, - new [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" } - )] - [InlineData ( - "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", - 1, - -50, - new [] - { - "ก", - "ข", - "ฃ", - "ค", - "ฅ", - "ฆ", - "ง", - "จ", - "ฉ", - "ช", - "ซ", - "ฌ", - "ญ", - "ฎ", - "ฏ", - "ฐ", - "ฑ", - "ฒ", - "ณ", - "ด", - "ต", - "ถ", - "ท", - "ธ", - "น", - "บ", - "ป", - "ผ", - "ฝ", - "พ", - "ฟ", - "ภ", - "ม", - "ย", - "ร", - "ฤ", - "ล", - "ฦ", - "ว", - "ศ", - "ษ", - "ส", - "ห", - "ฬ", - "อ", - "ฮ", - "ฯ", - "ะั", - "า", - "ำ" - } - )] - public void WordWrap_Unicode_SingleWordLine ( - string text, - int maxWidth, - int widthOffset, - IEnumerable resultLines - ) - { - List wrappedLines; - - IEnumerable zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0); - Assert.Single (zeroWidth); - Assert.Equal ('ั', zeroWidth.ElementAt (0).Value); - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth - >= (wrappedLines.Count > 0 - ? wrappedLines.Max ( - l => l.GetRuneCount () - + zeroWidth.Count () - - 1 - + widthOffset - ) - : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - /// WordWrap strips CRLF - [Theory] - [InlineData ( - "A sentence has words.\nA paragraph has lines.", - 44, - 0, - new [] { "A sentence has words.A paragraph has lines." } - )] - [InlineData ( - "A sentence has words.\nA paragraph has lines.", - 43, - -1, - new [] { "A sentence has words.A paragraph has lines." } - )] - [InlineData ( - "A sentence has words.\nA paragraph has lines.", - 38, - -6, - new [] { "A sentence has words.A paragraph has", "lines." } - )] - [InlineData ( - "A sentence has words.\nA paragraph has lines.", - 34, - -10, - new [] { "A sentence has words.A paragraph", "has lines." } - )] - [InlineData ( - "A sentence has words.\nA paragraph has lines.", - 27, - -17, - new [] { "A sentence has words.A", "paragraph has lines." } - )] - - // Unicode - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", - 69, - 0, - new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", - 68, - -1, - new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", - 63, - -6, - new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", "Линии." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", - 59, - -10, - new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", "has Линии." } - )] - [InlineData ( - "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", - 52, - -17, - new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode", "Пункт has Линии." } - )] - public void WordWrap_WithNewLines (string text, int maxWidth, int widthOffset, IEnumerable resultLines) - { - List wrappedLines; - - Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); - int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); - wrappedLines = TextFormatter.WordWrapText (text, maxWidth); - Assert.Equal (wrappedLines.Count, resultLines.Count ()); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) - ); - - Assert.True ( - expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) - ); - Assert.Equal (resultLines, wrappedLines); - } - - [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); - - TestHelpers.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); - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - } + public static IEnumerable CMGlyphs => + new List { new object [] { $"{CM.Glyphs.LeftBracket} Say Hello 你 {CM.Glyphs.RightBracket}", 16, 15 } }; [SetupFakeDriver] [Theory] @@ -3075,340 +295,52 @@ ssb [SetupFakeDriver] [Theory] - [InlineData ("A", 5, 5, "A")] - [InlineData ( - "AB12", - 5, - 5, - @" -A -B -1 -2")] - [InlineData ( - "AB\n12", - 5, - 5, - @" -A1 -B2")] - [InlineData ("", 5, 1, "")] - [InlineData ( - "Hello Worlds", - 1, - 12, - @" -H -e -l -l -o - -W -o -r -l -d -s")] - [InlineData ( - "Hello Worlds", - 1, - 12, - @" -H -e -l -l -o - -W -o -r -l -d -s")] - [InlineData ("Hello Worlds", 12, 1, @"HelloWorlds")] - public void Draw_Vertical_TopBottom_LeftRight (string text, int width, int height, string expectedText) + [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, - Direction = TextDirection.TopBottom_LeftRight + Alignment = Alignment.Start }; tf.ConstrainToWidth = width; - tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 20, 20), Attribute.Default, Attribute.Default); + tf.ConstrainToHeight = 1; + tf.Draw (new (0, 0, width, 1), Attribute.Default, Attribute.Default); TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); } [SetupFakeDriver] [Theory] - [InlineData ("Hello World", 15, 1, "Hello World")] - [InlineData ( - "Well Done\nNice Work", - 15, - 2, - @" -Well Done -Nice Work")] - [InlineData ("你好 世界", 15, 1, "你好 世界")] - [InlineData ( - "做 得好\n幹 得好", - 15, - 2, - @" -做 得好 -幹 得好")] - public void Justify_Horizontal (string text, int width, int height, string expectedText) + [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.Fill, - ConstrainToSize = new Size (width, height), - MultiLine = true - }; - - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); - - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - } - - [SetupFakeDriver] - [Theory] - [InlineData ("Hello World", 1, 15, "H\ne\nl\nl\no\n \n \n \n \n \nW\no\nr\nl\nd")] - [InlineData ( - "Well Done\nNice Work", - 2, - 15, - @" -WN -ei -lc -le - - - - - - - -DW -oo -nr -ek")] - [InlineData ("你好 世界", 2, 15, "你\n好\n \n \n \n \n \n \n \n \n \n \n \n世\n界")] - [InlineData ( - "做 得好\n幹 得好", - 4, - 15, - @" -做幹 - - - - - - - - - - - - -得得 -好好")] - public void Justify_Vertical (string text, int width, int height, string expectedText) - { - TextFormatter tf = new () - { - Text = text, - Direction = TextDirection.TopBottom_LeftRight, - VerticalAlignment = Alignment.Fill, - ConstrainToSize = new Size (width, height), - MultiLine = true - }; - - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); - - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - } - - [SetupFakeDriver] - [Theory] - [InlineData ("A", 0, 1, "", 0)] - [InlineData ("A", 1, 1, "A", 0)] - [InlineData ("A", 2, 2, " A", 1)] - [InlineData ("AB", 1, 1, "B", 0)] - [InlineData ("AB", 2, 2, " A\n B", 0)] - [InlineData ("ABC", 3, 2, " B\n C", 0)] - [InlineData ("ABC", 4, 2, " B\n C", 0)] - [InlineData ("ABC", 6, 2, " B\n C", 0)] - [InlineData ("こんにちは", 0, 1, "", 0)] - [InlineData ("こんにちは", 1, 0, "", 0)] - [InlineData ("こんにちは", 1, 1, "", 0)] - [InlineData ("こんにちは", 2, 1, "は", 0)] - [InlineData ("こんにちは", 2, 2, "ち\nは", 0)] - [InlineData ("こんにちは", 2, 3, "に\nち\nは", 0)] - [InlineData ("こんにちは", 2, 4, "ん\nに\nち\nは", 0)] - [InlineData ("こんにちは", 2, 5, "こ\nん\nに\nち\nは", 0)] - [InlineData ("こんにちは", 2, 6, "こ\nん\nに\nち\nは", 1)] - [InlineData ("ABCD\nこんにちは", 4, 7, " こ\n Aん\n Bに\n Cち\n Dは", 2)] - [InlineData ("こんにちは\nABCD", 3, 7, "こ \nんA\nにB\nちC\nはD", 2)] - public void Draw_Vertical_Bottom_Horizontal_Right (string text, int width, int height, string expectedText, int expectedY) - { - TextFormatter tf = new () - { - Text = text, - Alignment = Alignment.End, - Direction = TextDirection.TopBottom_LeftRight, - VerticalAlignment = Alignment.End + Alignment = Alignment.End }; tf.ConstrainToWidth = width; - tf.ConstrainToHeight = height; - - tf.Draw (new (Point.Empty, new (width, height)), Attribute.Default, Attribute.Default); - Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - Assert.Equal (expectedY, rect.Y); - } - - [SetupFakeDriver] - [Theory] - [InlineData ("A", 5, "A")] - [InlineData ( - "AB12", - 5, - @" -A -B -1 -2")] - [InlineData ( - "AB\n12", - 5, - @" -A1 -B2")] - [InlineData ("", 1, "")] - [InlineData ( - "AB1 2", - 2, - @" -A12 -B ")] - [InlineData ( - "こんにちは", - 1, - @" -こん")] - [InlineData ( - "こんにちは", - 2, - @" -こに -んち")] - [InlineData ( - "こんにちは", - 5, - @" -こ -ん -に -ち -は")] - public void Draw_Vertical_TopBottom_LeftRight_Top (string text, int height, string expectedText) - { - TextFormatter tf = new () - { - Text = text, - Direction = TextDirection.TopBottom_LeftRight - }; - - tf.ConstrainToWidth = 5; - tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default); - - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - } - - [SetupFakeDriver] - [Theory] - - // The expectedY param is to probe that the expectedText param start at that Y coordinate - [InlineData ("A", 0, "", 0)] - [InlineData ("A", 1, "A", 0)] - [InlineData ("A", 2, "A", 0)] - [InlineData ("A", 3, "A", 1)] - [InlineData ("AB", 1, "A", 0)] - [InlineData ("AB", 2, "A\nB", 0)] - [InlineData ("ABC", 2, "A\nB", 0)] - [InlineData ("ABC", 3, "A\nB\nC", 0)] - [InlineData ("ABC", 4, "A\nB\nC", 0)] - [InlineData ("ABC", 5, "A\nB\nC", 1)] - [InlineData ("ABC", 6, "A\nB\nC", 1)] - [InlineData ("ABC", 9, "A\nB\nC", 3)] - [InlineData ("ABCD", 2, "B\nC", 0)] - [InlineData ("こんにちは", 0, "", 0)] - [InlineData ("こんにちは", 1, "に", 0)] - [InlineData ("こんにちは", 2, "ん\nに", 0)] - [InlineData ("こんにちは", 3, "ん\nに\nち", 0)] - [InlineData ("こんにちは", 4, "こ\nん\nに\nち", 0)] - [InlineData ("こんにちは", 5, "こ\nん\nに\nち\nは", 0)] - [InlineData ("こんにちは", 6, "こ\nん\nに\nち\nは", 0)] - [InlineData ("ABCD\nこんにちは", 7, "Aこ\nBん\nCに\nDち\n は", 1)] - [InlineData ("こんにちは\nABCD", 7, "こA\nんB\nにC\nちD\nは ", 1)] - public void Draw_Vertical_TopBottom_LeftRight_Middle (string text, int height, string expectedText, int expectedY) - { - TextFormatter tf = new () - { - Text = text, - Direction = TextDirection.TopBottom_LeftRight, - VerticalAlignment = Alignment.Center - }; - - int width = text.ToRunes ().Max (r => r.GetColumns ()); - - if (text.Contains ("\n")) - { - width++; - } - - tf.ConstrainToWidth = width; - tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default); - - Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - Assert.Equal (expectedY, rect.Y); - } - - [SetupFakeDriver] - [Theory] - [InlineData ("A", 1, 0, "")] - [InlineData ("A", 0, 1, "")] - [InlineData ("AB1 2", 2, 1, "2")] - [InlineData ("AB12", 5, 1, "21BA")] - [InlineData ("AB\n12", 5, 2, "BA\n21")] - [InlineData ("ABC 123 456", 7, 2, "654 321\nCBA ")] - [InlineData ("こんにちは", 1, 1, "")] - [InlineData ("こんにちは", 2, 1, "は")] - [InlineData ("こんにちは", 5, 1, "はち")] - [InlineData ("こんにちは", 10, 1, "はちにんこ")] - [InlineData ("こんにちは\nAB\n12", 10, 3, "はちにんこ\nBA \n21 ")] - public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int height, string expectedText) - { - TextFormatter tf = new () - { - Text = text, - Direction = TextDirection.RightLeft_TopBottom - }; - - tf.ConstrainToWidth = width; - tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + tf.ConstrainToHeight = 1; + tf.Draw (new (Point.Empty, new (width, 1)), Attribute.Default, Attribute.Default); TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); } @@ -3444,21 +376,21 @@ B ")] [Theory] [InlineData ("A", 1, 0, "")] [InlineData ("A", 0, 1, "")] - [InlineData ("AB1 2", 1, 2, "2")] - [InlineData ("AB12", 1, 5, "2\n1\nB\nA")] - [InlineData ("AB\n12", 2, 5, "B2\nA1")] - [InlineData ("ABC 123 456", 2, 7, "6C\n5B\n4A\n \n3 \n2 \n1 ")] + [InlineData ("AB1 2", 2, 1, "2")] + [InlineData ("AB12", 5, 1, "21BA")] + [InlineData ("AB\n12", 5, 2, "BA\n21")] + [InlineData ("ABC 123 456", 7, 2, "654 321\nCBA ")] [InlineData ("こんにちは", 1, 1, "")] [InlineData ("こんにちは", 2, 1, "は")] - [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")] - [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")] - [InlineData ("こんにちは\nAB\n12", 4, 10, "はB2\nちA1\nに \nん \nこ ")] - public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int height, string expectedText) + [InlineData ("こんにちは", 5, 1, "はち")] + [InlineData ("こんにちは", 10, 1, "はちにんこ")] + [InlineData ("こんにちは\nAB\n12", 10, 3, "はちにんこ\nBA \n21 ")] + public void Draw_Horizontal_RightLeft_TopBottom (string text, int width, int height, string expectedText) { TextFormatter tf = new () { Text = text, - Direction = TextDirection.BottomTop_LeftRight + Direction = TextDirection.RightLeft_TopBottom }; tf.ConstrainToWidth = width; @@ -3468,70 +400,6 @@ B ")] TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); } - [SetupFakeDriver] - [Theory] - [InlineData ("A", 1, 0, "")] - [InlineData ("A", 0, 1, "")] - [InlineData ("AB1 2", 1, 2, "2")] - [InlineData ("AB12", 1, 5, "2\n1\nB\nA")] - [InlineData ("AB\n12", 2, 5, "2B\n1A")] - [InlineData ("ABC 123 456", 2, 7, "C6\nB5\nA4\n \n 3\n 2\n 1")] - [InlineData ("こんにちは", 1, 1, "")] - [InlineData ("こんにちは", 2, 1, "は")] - [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")] - [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")] - [InlineData ("こんにちは\nAB\n12", 4, 10, "2Bは\n1Aち\n に\n ん\n こ")] - public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int height, string expectedText) - { - TextFormatter tf = new () - { - Text = text, - Direction = TextDirection.BottomTop_RightLeft - }; - - tf.ConstrainToWidth = width; - tf.ConstrainToHeight = height; - tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); - - TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); - } - - // Draw tests - Note that these depend on View - - [Fact] - [TestRespondersDisposed] - public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds () - { - Application.Init (new FakeDriver ()); - - Toplevel top = new (); - - var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" }; - top.Add (view); - - Application.Iteration += (s, a) => - { - Assert.Equal (-2, view.Y); - - Application.RequestStop (); - }; - - try - { - Application.Run (top); - } - catch (IndexOutOfRangeException ex) - { - // After the fix this exception will not be caught. - Assert.IsType (ex); - } - - top.Dispose (); - - // Shutdown must be called to safely clean up Application if Init has been called - Application.Shutdown (); - } - [SetupFakeDriver] [Theory] @@ -6950,6 +3818,1334 @@ B ")] TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); } + [SetupFakeDriver] + [Theory] + [InlineData ("A", 0, 1, "", 0)] + [InlineData ("A", 1, 1, "A", 0)] + [InlineData ("A", 2, 2, " A", 1)] + [InlineData ("AB", 1, 1, "B", 0)] + [InlineData ("AB", 2, 2, " A\n B", 0)] + [InlineData ("ABC", 3, 2, " B\n C", 0)] + [InlineData ("ABC", 4, 2, " B\n C", 0)] + [InlineData ("ABC", 6, 2, " B\n C", 0)] + [InlineData ("こんにちは", 0, 1, "", 0)] + [InlineData ("こんにちは", 1, 0, "", 0)] + [InlineData ("こんにちは", 1, 1, "", 0)] + [InlineData ("こんにちは", 2, 1, "は", 0)] + [InlineData ("こんにちは", 2, 2, "ち\nは", 0)] + [InlineData ("こんにちは", 2, 3, "に\nち\nは", 0)] + [InlineData ("こんにちは", 2, 4, "ん\nに\nち\nは", 0)] + [InlineData ("こんにちは", 2, 5, "こ\nん\nに\nち\nは", 0)] + [InlineData ("こんにちは", 2, 6, "こ\nん\nに\nち\nは", 1)] + [InlineData ("ABCD\nこんにちは", 4, 7, " こ\n Aん\n Bに\n Cち\n Dは", 2)] + [InlineData ("こんにちは\nABCD", 3, 7, "こ \nんA\nにB\nちC\nはD", 2)] + public void Draw_Vertical_Bottom_Horizontal_Right (string text, int width, int height, string expectedText, int expectedY) + { + TextFormatter tf = new () + { + Text = text, + Alignment = Alignment.End, + Direction = TextDirection.TopBottom_LeftRight, + VerticalAlignment = Alignment.End + }; + + tf.ConstrainToWidth = width; + tf.ConstrainToHeight = height; + + tf.Draw (new (Point.Empty, new (width, height)), Attribute.Default, Attribute.Default); + Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + Assert.Equal (expectedY, rect.Y); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("A", 1, 0, "")] + [InlineData ("A", 0, 1, "")] + [InlineData ("AB1 2", 1, 2, "2")] + [InlineData ("AB12", 1, 5, "2\n1\nB\nA")] + [InlineData ("AB\n12", 2, 5, "B2\nA1")] + [InlineData ("ABC 123 456", 2, 7, "6C\n5B\n4A\n \n3 \n2 \n1 ")] + [InlineData ("こんにちは", 1, 1, "")] + [InlineData ("こんにちは", 2, 1, "は")] + [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")] + [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")] + [InlineData ("こんにちは\nAB\n12", 4, 10, "はB2\nちA1\nに \nん \nこ ")] + public void Draw_Vertical_BottomTop_LeftRight (string text, int width, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.BottomTop_LeftRight + }; + + tf.ConstrainToWidth = width; + tf.ConstrainToHeight = height; + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("A", 1, 0, "")] + [InlineData ("A", 0, 1, "")] + [InlineData ("AB1 2", 1, 2, "2")] + [InlineData ("AB12", 1, 5, "2\n1\nB\nA")] + [InlineData ("AB\n12", 2, 5, "2B\n1A")] + [InlineData ("ABC 123 456", 2, 7, "C6\nB5\nA4\n \n 3\n 2\n 1")] + [InlineData ("こんにちは", 1, 1, "")] + [InlineData ("こんにちは", 2, 1, "は")] + [InlineData ("こんにちは", 2, 5, "は\nち\nに\nん\nこ")] + [InlineData ("こんにちは", 2, 10, "は\nち\nに\nん\nこ")] + [InlineData ("こんにちは\nAB\n12", 4, 10, "2Bは\n1Aち\n に\n ん\n こ")] + public void Draw_Vertical_BottomTop_RightLeft (string text, int width, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.BottomTop_RightLeft + }; + + tf.ConstrainToWidth = width; + tf.ConstrainToHeight = height; + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + // Draw tests - Note that these depend on View + + [Fact] + [TestRespondersDisposed] + public void Draw_Vertical_Throws_IndexOutOfRangeException_With_Negative_Bounds () + { + Application.Init (new FakeDriver ()); + + Toplevel top = new (); + + var view = new View { Y = -2, Height = 10, TextDirection = TextDirection.TopBottom_LeftRight, Text = "view" }; + top.Add (view); + + Application.Iteration += (s, a) => + { + Assert.Equal (-2, view.Y); + + Application.RequestStop (); + }; + + try + { + Application.Run (top); + } + catch (IndexOutOfRangeException ex) + { + // After the fix this exception will not be caught. + Assert.IsType (ex); + } + + top.Dispose (); + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("A", 5, 5, "A")] + [InlineData ( + "AB12", + 5, + 5, + @" +A +B +1 +2")] + [InlineData ( + "AB\n12", + 5, + 5, + @" +A1 +B2")] + [InlineData ("", 5, 1, "")] + [InlineData ( + "Hello Worlds", + 1, + 12, + @" +H +e +l +l +o + +W +o +r +l +d +s")] + [InlineData ("Hello Worlds", 12, 1, @"HelloWorlds")] + public void Draw_Vertical_TopBottom_LeftRight (string text, int width, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.TopBottom_LeftRight + }; + + tf.ConstrainToWidth = width; + tf.ConstrainToHeight = height; + tf.Draw (new (0, 0, 20, 20), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + [SetupFakeDriver] + [Theory] + + // The expectedY param is to probe that the expectedText param start at that Y coordinate + [InlineData ("A", 0, "", 0)] + [InlineData ("A", 1, "A", 0)] + [InlineData ("A", 2, "A", 0)] + [InlineData ("A", 3, "A", 1)] + [InlineData ("AB", 1, "A", 0)] + [InlineData ("AB", 2, "A\nB", 0)] + [InlineData ("ABC", 2, "A\nB", 0)] + [InlineData ("ABC", 3, "A\nB\nC", 0)] + [InlineData ("ABC", 4, "A\nB\nC", 0)] + [InlineData ("ABC", 5, "A\nB\nC", 1)] + [InlineData ("ABC", 6, "A\nB\nC", 1)] + [InlineData ("ABC", 9, "A\nB\nC", 3)] + [InlineData ("ABCD", 2, "B\nC", 0)] + [InlineData ("こんにちは", 0, "", 0)] + [InlineData ("こんにちは", 1, "に", 0)] + [InlineData ("こんにちは", 2, "ん\nに", 0)] + [InlineData ("こんにちは", 3, "ん\nに\nち", 0)] + [InlineData ("こんにちは", 4, "こ\nん\nに\nち", 0)] + [InlineData ("こんにちは", 5, "こ\nん\nに\nち\nは", 0)] + [InlineData ("こんにちは", 6, "こ\nん\nに\nち\nは", 0)] + [InlineData ("ABCD\nこんにちは", 7, "Aこ\nBん\nCに\nDち\n は", 1)] + [InlineData ("こんにちは\nABCD", 7, "こA\nんB\nにC\nちD\nは ", 1)] + public void Draw_Vertical_TopBottom_LeftRight_Middle (string text, int height, string expectedText, int expectedY) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.TopBottom_LeftRight, + VerticalAlignment = Alignment.Center + }; + + int width = text.ToRunes ().Max (r => r.GetColumns ()); + + if (text.Contains ("\n")) + { + width++; + } + + tf.ConstrainToWidth = width; + tf.ConstrainToHeight = height; + tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default); + + Rectangle rect = TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + Assert.Equal (expectedY, rect.Y); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("A", 5, "A")] + [InlineData ( + "AB12", + 5, + @" +A +B +1 +2")] + [InlineData ( + "AB\n12", + 5, + @" +A1 +B2")] + [InlineData ("", 1, "")] + [InlineData ( + "AB1 2", + 2, + @" +A12 +B ")] + [InlineData ( + "こんにちは", + 1, + @" +こん")] + [InlineData ( + "こんにちは", + 2, + @" +こに +んち")] + [InlineData ( + "こんにちは", + 5, + @" +こ +ん +に +ち +は")] + public void Draw_Vertical_TopBottom_LeftRight_Top (string text, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.TopBottom_LeftRight + }; + + tf.ConstrainToWidth = 5; + tf.ConstrainToHeight = height; + tf.Draw (new (0, 0, 5, height), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + [Theory] + [InlineData (14, 1, TextDirection.LeftRight_TopBottom, "Les Misęrables")] + [InlineData (1, 14, TextDirection.TopBottom_LeftRight, "L\ne\ns\n \nM\ni\ns\nę\nr\na\nb\nl\ne\ns")] + [InlineData ( + 4, + 4, + TextDirection.TopBottom_LeftRight, + @" +LMre +eias +ssb + ęl " + )] + public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected) + { + var driver = new FakeDriver (); + driver.Init (); + + var text = "Les Mise\u0328\u0301rables"; + + var tf = new TextFormatter (); + tf.Direction = textDirection; + tf.Text = text; + + Assert.True (tf.WordWrap); + + tf.ConstrainToSize = new (width, height); + + tf.Draw ( + new (0, 0, width, height), + new (ColorName.White, ColorName.Black), + new (ColorName.Blue, ColorName.Black), + default (Rectangle), + driver + ); + TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); + + driver.End (); + } + + [Fact] + [SetupFakeDriver] + public void FillRemaining_True_False () + { + ((FakeDriver)Application.Driver!).SetBufferSize (22, 5); + + Attribute [] attrs = + { + Attribute.Default, new (ColorName.Green, ColorName.BrightMagenta), + new (ColorName.Blue, ColorName.Cyan) + }; + var tf = new TextFormatter { ConstrainToSize = new (14, 3), Text = "Test\nTest long\nTest long long\n", MultiLine = true }; + + tf.Draw ( + new (1, 1, 19, 3), + attrs [1], + attrs [2]); + + Assert.False (tf.FillRemaining); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" + Test + Test long + Test long long", + _output); + + TestHelpers.AssertDriverAttributesAre ( + @" +000000000000000000000 +011110000000000000000 +011111111100000000000 +011111111111111000000 +000000000000000000000", + null, + attrs); + + tf.FillRemaining = true; + + tf.Draw ( + new (1, 1, 19, 3), + attrs [1], + attrs [2]); + + TestHelpers.AssertDriverAttributesAre ( + @" +000000000000000000000 +011111111111111111110 +011111111111111111110 +011111111111111111110 +000000000000000000000", + null, + attrs); + } + + [Theory] + [InlineData ("_k Before", true, 0, (KeyCode)'K')] // lower case should return uppercase Hotkey + [InlineData ("a_k Second", true, 1, (KeyCode)'K')] + [InlineData ("Last _k", true, 5, (KeyCode)'K')] + [InlineData ("After k_", false, -1, KeyCode.Null)] + [InlineData ("Multiple _k and _R", true, 9, (KeyCode)'K')] + [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к')] // Lower case Cryllic K (к) + [InlineData ("_k Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results + [InlineData ("a_k Second", true, 1, (KeyCode)'K', true)] + [InlineData ("Last _k", true, 5, (KeyCode)'K', true)] + [InlineData ("After k_", false, -1, KeyCode.Null, true)] + [InlineData ("Multiple _k and _r", true, 9, (KeyCode)'K', true)] + [InlineData ("Non-english: _кдать", true, 13, (KeyCode)'к', true)] // Cryllic K (К) + public void FindHotKey_AlphaLowerCase_Succeeds ( + string text, + bool expectedResult, + int expectedHotPos, + KeyCode expectedKey, + bool supportFirstUpperCase = false + ) + { + var hotKeySpecifier = (Rune)'_'; + + bool result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out int hotPos, + out Key hotKey, + supportFirstUpperCase + ); + + if (expectedResult) + { + Assert.True (result); + } + else + { + Assert.False (result); + } + + Assert.Equal (expectedResult, result); + Assert.Equal (expectedHotPos, hotPos); + Assert.Equal (expectedKey, hotKey); + } + + [Theory] + [InlineData ("_K Before", true, 0, (KeyCode)'K')] + [InlineData ("a_K Second", true, 1, (KeyCode)'K')] + [InlineData ("Last _K", true, 5, (KeyCode)'K')] + [InlineData ("After K_", false, -1, KeyCode.Null)] + [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K')] + [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К) + [InlineData ("_K Before", true, 0, (KeyCode)'K', true)] // Turn on FirstUpperCase and verify same results + [InlineData ("a_K Second", true, 1, (KeyCode)'K', true)] + [InlineData ("Last _K", true, 5, (KeyCode)'K', true)] + [InlineData ("After K_", false, -1, KeyCode.Null, true)] + [InlineData ("Multiple _K and _R", true, 9, (KeyCode)'K', true)] + [InlineData ("Non-english: _Кдать", true, 13, (KeyCode)'К', true)] // Cryllic K (К) + public void FindHotKey_AlphaUpperCase_Succeeds ( + string text, + bool expectedResult, + int expectedHotPos, + KeyCode expectedKey, + bool supportFirstUpperCase = false + ) + { + var hotKeySpecifier = (Rune)'_'; + + bool result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out int hotPos, + out Key hotKey, + supportFirstUpperCase + ); + + if (expectedResult) + { + Assert.True (result); + } + else + { + Assert.False (result); + } + + Assert.Equal (expectedResult, result); + Assert.Equal (expectedHotPos, hotPos); + Assert.Equal (expectedKey, hotKey); + } + + [Theory] + [InlineData (null)] + [InlineData ("")] + [InlineData ("no hotkey")] + [InlineData ("No hotkey, Upper Case")] + [InlineData ("Non-english: Сохранить")] + public void FindHotKey_Invalid_ReturnsFalse (string text) + { + var hotKeySpecifier = (Rune)'_'; + var supportFirstUpperCase = false; + var hotPos = 0; + Key hotKey = KeyCode.Null; + var result = false; + + result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out hotPos, + out hotKey, + supportFirstUpperCase + ); + Assert.False (result); + Assert.Equal (-1, hotPos); + Assert.Equal (KeyCode.Null, hotKey); + } + + [Theory] + [InlineData ("\"k before")] + [InlineData ("ak second")] + [InlineData ("last k")] + [InlineData ("multiple k and r")] + [InlineData ("12345")] + [InlineData ("`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?")] // punctuation + [InlineData (" ~  s  gui.cs   master ↑10")] // ~IsLetterOrDigit + Unicode + [InlineData ("non-english: кдать")] // Lower case Cryllic K (к) + public void FindHotKey_Legacy_FirstUpperCase_NotFound_Returns_False (string text) + { + var supportFirstUpperCase = true; + + var hotKeySpecifier = (Rune)0; + + bool result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out int hotPos, + out Key hotKey, + supportFirstUpperCase + ); + Assert.False (result); + Assert.Equal (-1, hotPos); + Assert.Equal (KeyCode.Null, hotKey); + } + + [Theory] + [InlineData ("K Before", true, 0, (KeyCode)'K')] + [InlineData ("aK Second", true, 1, (KeyCode)'K')] + [InlineData ("last K", true, 5, (KeyCode)'K')] + [InlineData ("multiple K and R", true, 9, (KeyCode)'K')] + [InlineData ("non-english: Кдать", true, 13, (KeyCode)'К')] // Cryllic K (К) + public void FindHotKey_Legacy_FirstUpperCase_Succeeds ( + string text, + bool expectedResult, + int expectedHotPos, + KeyCode expectedKey + ) + { + var supportFirstUpperCase = true; + + var hotKeySpecifier = (Rune)0; + + bool result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out int hotPos, + out Key hotKey, + supportFirstUpperCase + ); + + if (expectedResult) + { + Assert.True (result); + } + else + { + Assert.False (result); + } + + Assert.Equal (expectedResult, result); + Assert.Equal (expectedHotPos, hotPos); + Assert.Equal (expectedKey, hotKey); + } + + [Theory] + [InlineData ("_1 Before", true, 0, (KeyCode)'1')] // Digits + [InlineData ("a_1 Second", true, 1, (KeyCode)'1')] + [InlineData ("Last _1", true, 5, (KeyCode)'1')] + [InlineData ("After 1_", false, -1, KeyCode.Null)] + [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1')] + [InlineData ("_1 Before", true, 0, (KeyCode)'1', true)] // Turn on FirstUpperCase and verify same results + [InlineData ("a_1 Second", true, 1, (KeyCode)'1', true)] + [InlineData ("Last _1", true, 5, (KeyCode)'1', true)] + [InlineData ("After 1_", false, -1, KeyCode.Null, true)] + [InlineData ("Multiple _1 and _2", true, 9, (KeyCode)'1', true)] + public void FindHotKey_Numeric_Succeeds ( + string text, + bool expectedResult, + int expectedHotPos, + KeyCode expectedKey, + bool supportFirstUpperCase = false + ) + { + var hotKeySpecifier = (Rune)'_'; + + bool result = TextFormatter.FindHotKey ( + text, + hotKeySpecifier, + out int hotPos, + out Key hotKey, + supportFirstUpperCase + ); + + if (expectedResult) + { + Assert.True (result); + } + else + { + Assert.False (result); + } + + Assert.Equal (expectedResult, result); + Assert.Equal (expectedHotPos, hotPos); + Assert.Equal (expectedKey, hotKey); + } + + [Theory] + [InlineData ("_\"k before", true, (KeyCode)'"')] // BUGBUG: Not sure why this fails. " is a normal char + [InlineData ("\"_k before", true, KeyCode.K)] + [InlineData ("_`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'`')] + [InlineData ("`_~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?", true, (KeyCode)'~')] + [InlineData ( + "`~!@#$%^&*()-__=+[{]}\\|;:'\",<.>/?", + true, + (KeyCode)'=' + )] // BUGBUG: Not sure why this fails. Ignore the first and consider the second + [InlineData ("_ ~  s  gui.cs   master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode + [InlineData (" ~  s  gui.cs  _ master ↑10", true, (KeyCode)'')] // ~IsLetterOrDigit + Unicode + [InlineData ("non-english: _кдать", true, (KeyCode)'к')] // Lower case Cryllic K (к) + public void FindHotKey_Symbols_Returns_Symbol (string text, bool found, KeyCode expected) + { + var hotKeySpecifier = (Rune)'_'; + + bool result = TextFormatter.FindHotKey (text, hotKeySpecifier, out int _, out Key hotKey); + Assert.Equal (found, result); + Assert.Equal (expected, hotKey); + } + + [Fact] + public void Format_Dont_Throw_ArgumentException_With_WordWrap_As_False_And_Keep_End_Spaces_As_True () + { + Exception exception = Record.Exception ( + () => + TextFormatter.Format ( + "Some text", + 4, + Alignment.Start, + false, + true + ) + ); + Assert.Null (exception); + } + + [Theory] + [InlineData ( + "Hello world, how are you today? Pretty neat!", + 44, + 80, + "Hello world, how are you today? Pretty neat!" + )] + public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Horizontal ( + string text, + int runeCount, + int maxWidth, + string justifiedText + ) + { + Assert.Equal (runeCount, text.GetRuneCount ()); + + var fmtText = string.Empty; + + for (int i = text.GetRuneCount (); i < maxWidth; i++) + { + fmtText = TextFormatter.Format (text, i, Alignment.Fill, true) [0]; + Assert.Equal (i, fmtText.GetRuneCount ()); + char c = fmtText [^1]; + Assert.True (text.EndsWith (c)); + } + + Assert.Equal (justifiedText, fmtText); + } + + [Theory] + [InlineData ( + "Hello world, how are you today? Pretty neat!", + 44, + 80, + "Hello world, how are you today? Pretty neat!" + )] + public void Format_Justified_Always_Returns_Text_Width_Equal_To_Passed_Width_Vertical ( + string text, + int runeCount, + int maxWidth, + string justifiedText + ) + { + Assert.Equal (runeCount, text.GetRuneCount ()); + + var fmtText = string.Empty; + + for (int i = text.GetRuneCount (); i < maxWidth; i++) + { + fmtText = TextFormatter.Format ( + text, + i, + Alignment.Fill, + false, + true, + 0, + TextDirection.TopBottom_LeftRight + ) [0]; + Assert.Equal (i, fmtText.GetRuneCount ()); + char c = fmtText [^1]; + Assert.True (text.EndsWith (c)); + } + + Assert.Equal (justifiedText, fmtText); + } + + [Theory] + [InlineData ("Truncate", 3, "Tru")] + [InlineData ("デモエムポンズ", 3, "デ")] + public void Format_Truncate_Simple_And_Wide_Runes (string text, int width, string expected) + { + List list = TextFormatter.Format (text, width, false, false); + Assert.Equal (expected, list [^1]); + } + + [Theory] + [MemberData (nameof (FormatEnvironmentNewLine))] + public void Format_With_PreserveTrailingSpaces_And_Without_PreserveTrailingSpaces ( + string text, + int width, + IEnumerable expected + ) + { + var preserveTrailingSpaces = false; + List formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces); + Assert.Equal (expected, formated); + + preserveTrailingSpaces = true; + formated = TextFormatter.Format (text, width, false, true, preserveTrailingSpaces); + Assert.Equal (expected, formated); + } + + [Theory] + [InlineData ( + " A sentence has words. \n This is the second Line - 2. ", + 4, + -50, + Alignment.Start, + true, + false, + new [] { " A", "sent", "ence", "has", "word", "s. ", " Thi", "s is", "the", "seco", "nd", "Line", "- 2." }, + " Asentencehaswords. This isthesecondLine- 2." + )] + [InlineData ( + " A sentence has words. \n This is the second Line - 2. ", + 4, + -50, + Alignment.Start, + true, + true, + new [] + { + " A ", + "sent", + "ence", + " ", + "has ", + "word", + "s. ", + " ", + "This", + " is ", + "the ", + "seco", + "nd ", + "Line", + " - ", + "2. " + }, + " A sentence has words. This is the second Line - 2. " + )] + public void Format_WordWrap_PreserveTrailingSpaces ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + bool preserveTrailingSpaces, + IEnumerable resultLines, + string expectedWrappedText + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); + Assert.Equal (list.Count, resultLines.Count ()); + Assert.Equal (resultLines, list); + var wrappedText = string.Empty; + + foreach (string txt in list) + { + wrappedText += txt; + } + + Assert.Equal (expectedWrappedText, wrappedText); + } + + public static IEnumerable FormatEnvironmentNewLine => + new List + { + new object [] + { + $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3{Environment.NewLine}", + 60, + new [] { "Line1", "Line2", "Line3" } + } + }; + + [Theory] + [InlineData ("Hello World", 11)] + [InlineData ("こんにちは世界", 14)] + public void GetColumns_Simple_And_Wide_Runes (string text, int width) { Assert.Equal (width, text.GetColumns ()); } + + [Theory] + [InlineData (new [] { "0123456789" }, 1)] + [InlineData (new [] { "Hello World" }, 1)] + [InlineData (new [] { "Hello", "World" }, 2)] + [InlineData (new [] { "こんにちは", "世界" }, 4)] + public void GetColumnsRequiredForVerticalText_List_GetsWidth (IEnumerable text, int expectedWidth) + { + Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ())); + } + + [Theory] + [InlineData (new [] { "Hello World" }, 1, 0, 1, 1)] + [InlineData (new [] { "Hello", "World" }, 2, 1, 1, 1)] + [InlineData (new [] { "こんにちは", "世界" }, 4, 1, 1, 2)] + public void GetColumnsRequiredForVerticalText_List_Simple_And_Wide_Runes ( + IEnumerable text, + int expectedWidth, + int index, + int length, + int expectedIndexWidth + ) + { + Assert.Equal (expectedWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList ())); + Assert.Equal (expectedIndexWidth, TextFormatter.GetColumnsRequiredForVerticalText (text.ToList (), index, length)); + } + + [Fact] + public void GetColumnsRequiredForVerticalText_List_With_Combining_Runes () + { + List text = new () { "Les Mis", "e\u0328\u0301", "rables" }; + Assert.Equal (1, TextFormatter.GetColumnsRequiredForVerticalText (text, 1, 1)); + } + + [Theory] + [InlineData ("Hello World", 6, 6)] + [InlineData ("こんにちは 世界", 6, 3)] + [MemberData (nameof (CMGlyphs))] + public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength) + { + List runes = text.ToRuneList (); + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); + } + + [Theory] + [InlineData ("test", 3, 3)] + [InlineData ("test", 4, 4)] + [InlineData ("test", 10, 4)] + public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength) + { + List runes = text.ToRuneList (); + + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns)); + } + + [Theory] + [InlineData ("Hello World", 6, 6)] + [InlineData ("こんにちは 世界", 6, 3)] + public void GetLengthThatFits_Simple_And_Wide_Runes (string text, int columns, int expectedLength) + { + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); + } + + [Theory] + [InlineData ("test", 3, 3)] + [InlineData ("test", 4, 4)] + [InlineData ("test", 10, 4)] + [InlineData ("test", 1, 1)] + [InlineData ("test", 0, 0)] + [InlineData ("test", -1, 0)] + [InlineData (null, -1, 0)] + [InlineData ("", -1, 0)] + public void GetLengthThatFits_String (string text, int columns, int expectedLength) + { + Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns)); + } + + [Fact] + public void GetLengthThatFits_With_Combining_Runes () + { + var text = "Les Mise\u0328\u0301rables"; + Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14)); + } + + [Fact] + public void GetMaxColsForWidth_With_Combining_Runes () + { + List text = new () { "Les Mis", "e\u0328\u0301", "rables" }; + Assert.Equal (1, TextFormatter.GetMaxColsForWidth (text, 1)); + } + + //[Fact] + //public void GetWidestLineLength_With_Combining_Runes () + //{ + // var text = "Les Mise\u0328\u0301rables"; + // Assert.Equal (1, TextFormatter.GetWidestLineLength (text, 1, 1)); + //} + + [Fact] + public void Internal_Tests () + { + var tf = new TextFormatter (); + Assert.Equal (KeyCode.Null, tf.HotKey); + tf.HotKey = KeyCode.CtrlMask | KeyCode.Q; + Assert.Equal (KeyCode.CtrlMask | KeyCode.Q, tf.HotKey); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("Hello World", 15, 1, "Hello World")] + [InlineData ( + "Well Done\nNice Work", + 15, + 2, + @" +Well Done +Nice Work")] + [InlineData ("你好 世界", 15, 1, "你好 世界")] + [InlineData ( + "做 得好\n幹 得好", + 15, + 2, + @" +做 得好 +幹 得好")] + public void Justify_Horizontal (string text, int width, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Alignment = Alignment.Fill, + ConstrainToSize = new Size (width, height), + MultiLine = true + }; + + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + [Theory] + [InlineData ("")] + [InlineData (null)] + [InlineData ("test")] + public void Justify_Invalid (string text) + { + Assert.Equal (text, TextFormatter.Justify (text, 0)); + Assert.Equal (text, TextFormatter.Justify (text, 0)); + Assert.Throws (() => TextFormatter.Justify (text, -1)); + } + + [Theory] + + // Even # of spaces + // 0123456789 + [InlineData ("012 456 89", "012 456 89", 10, 0, "+", true)] + [InlineData ("012 456 89", "012++456+89", 11, 1)] + [InlineData ("012 456 89", "012 456 89", 12, 2, "++", true)] + [InlineData ("012 456 89", "012+++456++89", 13, 3)] + [InlineData ("012 456 89", "012 456 89", 14, 4, "+++", true)] + [InlineData ("012 456 89", "012++++456+++89", 15, 5)] + [InlineData ("012 456 89", "012 456 89", 16, 6, "++++", true)] + [InlineData ("012 456 89", "012 456 89", 30, 20, "+++++++++++", true)] + [InlineData ("012 456 89", "012+++++++++++++456++++++++++++89", 33, 23)] + + // Odd # of spaces + // 01234567890123 + [InlineData ("012 456 89 end", "012 456 89 end", 14, 0, "+", true)] + [InlineData ("012 456 89 end", "012++456+89+end", 15, 1)] + [InlineData ("012 456 89 end", "012++456++89+end", 16, 2)] + [InlineData ("012 456 89 end", "012 456 89 end", 17, 3, "++", true)] + [InlineData ("012 456 89 end", "012+++456++89++end", 18, 4)] + [InlineData ("012 456 89 end", "012+++456+++89++end", 19, 5)] + [InlineData ("012 456 89 end", "012 456 89 end", 20, 6, "+++", true)] + [InlineData ("012 456 89 end", "012++++++++456++++++++89+++++++end", 34, 20)] + [InlineData ("012 456 89 end", "012+++++++++456+++++++++89++++++++end", 37, 23)] + + // Unicode + // Even # of chars + // 0123456789 + [InlineData ("пÑРвРÑ", "пÑРвРÑ", 10, 0, "+", true)] + [InlineData ("пÑРвРÑ", "пÑÐ++вÐ+Ñ", 11, 1)] + [InlineData ("пÑРвРÑ", "пÑРвРÑ", 12, 2, "++", true)] + [InlineData ("пÑРвРÑ", "пÑÐ+++вÐ++Ñ", 13, 3)] + [InlineData ("пÑРвРÑ", "пÑРвРÑ", 14, 4, "+++", true)] + [InlineData ("пÑРвРÑ", "пÑÐ++++вÐ+++Ñ", 15, 5)] + [InlineData ("пÑРвРÑ", "пÑРвРÑ", 16, 6, "++++", true)] + [InlineData ("пÑРвРÑ", "пÑРвРÑ", 30, 20, "+++++++++++", true)] + [InlineData ("пÑРвРÑ", "пÑÐ+++++++++++++вÐ++++++++++++Ñ", 33, 23)] + + // Unicode + // Odd # of chars + // 0123456789 + [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 10, 0, "+", true)] + [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ+вÐ+Ñ", 11, 1)] + [InlineData ("Ð ÑРвРÑ", "Ð++ÑÐ++вÐ+Ñ", 12, 2)] + [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 13, 3, "++", true)] + [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ++вÐ++Ñ", 14, 4)] + [InlineData ("Ð ÑРвРÑ", "Ð+++ÑÐ+++вÐ++Ñ", 15, 5)] + [InlineData ("Ð ÑРвРÑ", "Ð ÑРвРÑ", 16, 6, "+++", true)] + [InlineData ("Ð ÑРвРÑ", "Ð++++++++ÑÐ++++++++вÐ+++++++Ñ", 30, 20)] + [InlineData ("Ð ÑРвРÑ", "Ð+++++++++ÑÐ+++++++++вÐ++++++++Ñ", 33, 23)] + public void Justify_Sentence ( + string text, + string justifiedText, + int forceToWidth, + int widthOffset, + string replaceWith = null, + bool replace = false + ) + { + var fillChar = '+'; + + Assert.Equal (forceToWidth, text.GetRuneCount () + widthOffset); + + if (replace) + { + justifiedText = text.Replace (" ", replaceWith); + } + + Assert.Equal (justifiedText, TextFormatter.Justify (text, forceToWidth, fillChar)); + Assert.True (Math.Abs (forceToWidth - justifiedText.GetRuneCount ()) < text.Count (s => s == ' ')); + Assert.True (Math.Abs (forceToWidth - justifiedText.GetColumns ()) < text.Count (s => s == ' ')); + } + + [Theory] + [InlineData ("word")] // Even # of chars + [InlineData ("word.")] // Odd # of chars + [InlineData ("пÑивеÑ")] // Unicode (even #) + [InlineData ("пÑивеÑ.")] // Unicode (odd # of chars) + public void Justify_SingleWord (string text) + { + string justifiedText = text; + var fillChar = '+'; + + int width = text.GetRuneCount (); + Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); + width = text.GetRuneCount () + 1; + Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); + width = text.GetRuneCount () + 2; + Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); + width = text.GetRuneCount () + 10; + Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); + width = text.GetRuneCount () + 11; + Assert.Equal (justifiedText, TextFormatter.Justify (text, width, fillChar)); + } + + [SetupFakeDriver] + [Theory] + [InlineData ("Hello World", 1, 15, "H\ne\nl\nl\no\n \n \n \n \n \nW\no\nr\nl\nd")] + [InlineData ( + "Well Done\nNice Work", + 2, + 15, + @" +WN +ei +lc +le + + + + + + + +DW +oo +nr +ek")] + [InlineData ("你好 世界", 2, 15, "你\n好\n \n \n \n \n \n \n \n \n \n \n \n世\n界")] + [InlineData ( + "做 得好\n幹 得好", + 4, + 15, + @" +做幹 + + + + + + + + + + + + +得得 +好好")] + public void Justify_Vertical (string text, int width, int height, string expectedText) + { + TextFormatter tf = new () + { + Text = text, + Direction = TextDirection.TopBottom_LeftRight, + VerticalAlignment = Alignment.Fill, + ConstrainToSize = new Size (width, height), + MultiLine = true + }; + + tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default); + + TestHelpers.AssertDriverContentsWithFrameAre (expectedText, _output); + } + + [Theory] + [InlineData ("Single Line 界", 14)] + [InlineData ("First Line 界\nSecond Line 界\nThird Line 界\n", 14)] + public void MaxWidthLine_With_And_Without_Newlines (string text, int expected) { Assert.Equal (expected, TextFormatter.GetWidestLineLength (text)); } + + [Theory] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 0, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 1, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 1, + 0, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 0, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 1, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 1, + 0, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 6, + 5, + false, + new [] { "First " } + )] + [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3 " })] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 6, + 5, + true, + new [] { "First ", "Second", "Third ", "Forty ", "Fiftee" } + )] + [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一" })] + [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一", "第二", "第三", "四十", "第十" })] + public void MultiLine_WordWrap_False_Horizontal_Direction ( + string text, + int maxWidth, + int maxHeight, + bool multiLine, + IEnumerable resultLines + ) + { + var tf = new TextFormatter + { + Text = text, ConstrainToSize = new (maxWidth, maxHeight), WordWrap = false, MultiLine = multiLine + }; + + Assert.False (tf.WordWrap); + Assert.True (tf.MultiLine == multiLine); + Assert.Equal (TextDirection.LeftRight_TopBottom, tf.Direction); + + List splitLines = tf.GetLines (); + Assert.Equal (splitLines.Count, resultLines.Count ()); + Assert.Equal (splitLines, resultLines); + } + + [Theory] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 0, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 1, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 1, + 0, + false, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 0, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 0, + 1, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 1, + 0, + true, + new [] { "" } + )] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 6, + 5, + false, + new [] { "First" } + )] + [InlineData ("1\n2\n3\n4\n5\n6", 6, 5, false, new [] { "1 2 3" })] + [InlineData ( + "First Line\nSecond Line\nThird Line\nForty Line\nFifteenth Line\nSeventy Line", + 6, + 5, + true, + new [] { "First", "Secon", "Third", "Forty", "Fifte", "Seven" } + )] + [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, false, new [] { "第一行 第" })] + [InlineData ("第一行\n第二行\n第三行\n四十行\n第十五行\n七十行", 5, 5, true, new [] { "第一行", "第二行" })] + public void MultiLine_WordWrap_False_Vertical_Direction ( + string text, + int maxWidth, + int maxHeight, + bool multiLine, + IEnumerable resultLines + ) + { + var tf = new TextFormatter + { + Text = text, + ConstrainToSize = new (maxWidth, maxHeight), + WordWrap = false, + MultiLine = multiLine, + Direction = TextDirection.TopBottom_LeftRight + }; + + Assert.False (tf.WordWrap); + Assert.True (tf.MultiLine == multiLine); + Assert.Equal (TextDirection.TopBottom_LeftRight, tf.Direction); + + List splitLines = tf.GetLines (); + Assert.Equal (splitLines.Count, resultLines.Count ()); + Assert.Equal (splitLines, resultLines); + } + + [Fact] + public void NeedsFormat_Sets () + { + var testText = "test"; + var testBounds = new Rectangle (0, 0, 100, 1); + var tf = new TextFormatter (); + + tf.Text = "test"; + Assert.True (tf.NeedsFormat); // get_Lines causes a Format + Assert.NotEmpty (tf.GetLines ()); + Assert.False (tf.NeedsFormat); // get_Lines causes a Format + Assert.Equal (testText, tf.Text); + tf.Draw (testBounds, new (), new ()); + Assert.False (tf.NeedsFormat); + + tf.ConstrainToSize = new (1, 1); + Assert.True (tf.NeedsFormat); + Assert.NotEmpty (tf.GetLines ()); + Assert.False (tf.NeedsFormat); // get_Lines causes a Format + + tf.Alignment = Alignment.Center; + Assert.True (tf.NeedsFormat); + Assert.NotEmpty (tf.GetLines ()); + Assert.False (tf.NeedsFormat); // get_Lines causes a Format + } + // Test that changing TextFormatter does not impact View dimensions if Dim.Auto is not in play [Fact] public void Not_Used_TextFormatter_Does_Not_Change_View_Size () @@ -6998,6 +5194,1835 @@ B ")] Assert.Equal (Size.Empty, view.Frame.Size); } + [Theory] + [InlineData ("", -1, Alignment.Start, false, 0)] + [InlineData (null, 0, Alignment.Start, false, 1)] + [InlineData (null, 0, Alignment.Start, true, 1)] + [InlineData ("", 0, Alignment.Start, false, 1)] + [InlineData ("", 0, Alignment.Start, true, 1)] + public void Reformat_Invalid (string text, int maxWidth, Alignment alignment, bool wrap, int linesCount) + { + if (maxWidth < 0) + { + Assert.Throws ( + () => + TextFormatter.Format (text, maxWidth, alignment, wrap) + ); + } + else + { + List list = TextFormatter.Format (text, maxWidth, alignment, wrap); + Assert.NotEmpty (list); + Assert.True (list.Count == linesCount); + Assert.Equal (string.Empty, list [0]); + } + } + + [Theory] + [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true)] + [InlineData ("A sentence has words.\nLine 2.", 1, -28, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\nLine 2.", 5, -24, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\nLine 2.", 28, -1, Alignment.Start, false, 1, false)] + + // no clip + [InlineData ("A sentence has words.\nLine 2.", 29, 0, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\nLine 2.", 30, 1, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true)] + [InlineData ("A sentence has words.\r\nLine 2.", 1, -29, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\r\nLine 2.", 5, -25, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\r\nLine 2.", 29, -1, Alignment.Start, false, 1, false, 1)] + [InlineData ("A sentence has words.\r\nLine 2.", 30, 0, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.\r\nLine 2.", 31, 1, Alignment.Start, false, 1, false)] + public void Reformat_NoWordrap_NewLines_MultiLine_False ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + int linesCount, + bool stringEmpty, + int clipWidthOffset = 0 + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth) + clipWidthOffset; + List list = TextFormatter.Format (text, maxWidth, alignment, wrap); + Assert.NotEmpty (list); + Assert.True (list.Count == linesCount); + + if (stringEmpty) + { + Assert.Equal (string.Empty, list [0]); + } + else + { + Assert.NotEqual (string.Empty, list [0]); + } + + if (text.Contains ("\r\n") && maxWidth > 0) + { + Assert.Equal ( + StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]) + .Replace ("\r\n", " "), + list [0] + ); + } + else if (text.Contains ('\n') && maxWidth > 0) + { + Assert.Equal ( + StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]) + .Replace ("\n", " "), + list [0] + ); + } + else + { + Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]); + } + } + + [Theory] + [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })] + [InlineData ( + "A sentence has words.\nLine 2.", + 1, + -28, + Alignment.Start, + false, + 2, + false, + new [] { "A", "L" } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 5, + -24, + Alignment.Start, + false, + 2, + false, + new [] { "A sen", "Line " } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 28, + -1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + //// no clip + [InlineData ( + "A sentence has words.\nLine 2.", + 29, + 0, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 30, + 1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 1, + -29, + Alignment.Start, + false, + 2, + false, + new [] { "A", "L" } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 5, + -25, + Alignment.Start, + false, + 2, + false, + new [] { "A sen", "Line " } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 29, + -1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 30, + 0, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 31, + 1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + public void Reformat_NoWordrap_NewLines_MultiLine_True ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + int linesCount, + bool stringEmpty, + IEnumerable resultLines + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + + List list = TextFormatter.Format ( + text, + maxWidth, + alignment, + wrap, + false, + 0, + TextDirection.LeftRight_TopBottom, + true + ); + Assert.NotEmpty (list); + Assert.True (list.Count == linesCount); + + if (stringEmpty) + { + Assert.Equal (string.Empty, list [0]); + } + else + { + Assert.NotEqual (string.Empty, list [0]); + } + + Assert.Equal (list, resultLines); + } + + [Theory] + [InlineData ("A sentence has words.\nLine 2.", 0, -29, Alignment.Start, false, 1, true, new [] { "" })] + [InlineData ( + "A sentence has words.\nLine 2.", + 1, + -28, + Alignment.Start, + false, + 2, + false, + new [] { "A", "L" } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 5, + -24, + Alignment.Start, + false, + 2, + false, + new [] { "A sen", "Line " } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 28, + -1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + //// no clip + [InlineData ( + "A sentence has words.\nLine 2.", + 29, + 0, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\nLine 2.", + 30, + 1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ("A sentence has words.\r\nLine 2.", 0, -30, Alignment.Start, false, 1, true, new [] { "" })] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 1, + -29, + Alignment.Start, + false, + 2, + false, + new [] { "A", "L" } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 5, + -25, + Alignment.Start, + false, + 2, + false, + new [] { "A sen", "Line " } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 29, + -1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 30, + 0, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + [InlineData ( + "A sentence has words.\r\nLine 2.", + 31, + 1, + Alignment.Start, + false, + 2, + false, + new [] { "A sentence has words.", "Line 2." } + )] + public void Reformat_NoWordrap_NewLines_MultiLine_True_Vertical ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + int linesCount, + bool stringEmpty, + IEnumerable resultLines + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + + List list = TextFormatter.Format ( + text, + maxWidth, + alignment, + wrap, + false, + 0, + TextDirection.TopBottom_LeftRight, + true + ); + Assert.NotEmpty (list); + Assert.True (list.Count == linesCount); + + if (stringEmpty) + { + Assert.Equal (string.Empty, list [0]); + } + else + { + Assert.NotEqual (string.Empty, list [0]); + } + + Assert.Equal (list, resultLines); + } + + [Theory] + [InlineData ("", 0, 0, Alignment.Start, false, 1, true)] + [InlineData ("", 1, 1, Alignment.Start, false, 1, true)] + [InlineData ("A sentence has words.", 0, -21, Alignment.Start, false, 1, true)] + [InlineData ("A sentence has words.", 1, -20, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.", 5, -16, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.", 20, -1, Alignment.Start, false, 1, false)] + + // no clip + [InlineData ("A sentence has words.", 21, 0, Alignment.Start, false, 1, false)] + [InlineData ("A sentence has words.", 22, 1, Alignment.Start, false, 1, false)] + public void Reformat_NoWordrap_SingleLine ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + int linesCount, + bool stringEmpty + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + List list = TextFormatter.Format (text, maxWidth, alignment, wrap); + Assert.NotEmpty (list); + Assert.True (list.Count == linesCount); + + if (stringEmpty) + { + Assert.Equal (string.Empty, list [0]); + } + else + { + Assert.NotEqual (string.Empty, list [0]); + } + + Assert.Equal (StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), list [0]); + } + + [Theory] + + // Unicode + [InlineData ( + "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", + 8, + -1, + Alignment.Start, + true, + false, + new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } + )] + + // no clip + [InlineData ( + "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", + 9, + 0, + Alignment.Start, + true, + false, + new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } + )] + [InlineData ( + "\u2460\u2461\u2462\n\u2460\u2461\u2462\u2463\u2464", + 10, + 1, + Alignment.Start, + true, + false, + new [] { "\u2460\u2461\u2462", "\u2460\u2461\u2462\u2463\u2464" } + )] + public void Reformat_Unicode_Wrap_Spaces_NewLines ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + bool preserveTrailingSpaces, + IEnumerable resultLines + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); + Assert.Equal (list.Count, resultLines.Count ()); + Assert.Equal (resultLines, list); + } + + [Theory] + + // Unicode + // Even # of chars + // 0123456789 + [InlineData ("\u2660пÑРвРÑ", 10, -1, Alignment.Start, true, false, new [] { "\u2660пÑРвÐ", "Ñ" })] + + // no clip + [InlineData ("\u2660пÑРвРÑ", 11, 0, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })] + [InlineData ("\u2660пÑРвРÑ", 12, 1, Alignment.Start, true, false, new [] { "\u2660пÑРвРÑ" })] + + // Unicode + // Odd # of chars + // 0123456789 + [InlineData ("\u2660 ÑРвРÑ", 9, -1, Alignment.Start, true, false, new [] { "\u2660 ÑРвÐ", "Ñ" })] + + // no clip + [InlineData ("\u2660 ÑРвРÑ", 10, 0, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })] + [InlineData ("\u2660 ÑРвРÑ", 11, 1, Alignment.Start, true, false, new [] { "\u2660 ÑРвРÑ" })] + public void Reformat_Unicode_Wrap_Spaces_No_NewLines ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + bool preserveTrailingSpaces, + IEnumerable resultLines + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); + Assert.Equal (list.Count, resultLines.Count ()); + Assert.Equal (resultLines, list); + } + + [Theory] + + // Even # of spaces + // 0123456789 + [InlineData ("012 456 89", 0, -10, Alignment.Start, true, true, true, new [] { "" })] + [InlineData ( + "012 456 89", + 1, + -9, + Alignment.Start, + true, + true, + false, + new [] { "0", "1", "2", " ", "4", "5", "6", " ", "8", "9" }, + "01245689" + )] + [InlineData ("012 456 89", 5, -5, Alignment.Start, true, true, false, new [] { "012 ", "456 ", "89" })] + [InlineData ("012 456 89", 9, -1, Alignment.Start, true, true, false, new [] { "012 456 ", "89" })] + + // no clip + [InlineData ("012 456 89", 10, 0, Alignment.Start, true, true, false, new [] { "012 456 89" })] + [InlineData ("012 456 89", 11, 1, Alignment.Start, true, true, false, new [] { "012 456 89" })] + + // Odd # of spaces + // 01234567890123 + [InlineData ("012 456 89 end", 13, -1, Alignment.Start, true, true, false, new [] { "012 456 89 ", "end" })] + + // no clip + [InlineData ("012 456 89 end", 14, 0, Alignment.Start, true, true, false, new [] { "012 456 89 end" })] + [InlineData ("012 456 89 end", 15, 1, Alignment.Start, true, true, false, new [] { "012 456 89 end" })] + public void Reformat_Wrap_Spaces_No_NewLines ( + string text, + int maxWidth, + int widthOffset, + Alignment alignment, + bool wrap, + bool preserveTrailingSpaces, + bool stringEmpty, + IEnumerable resultLines, + string noSpaceText = "" + ) + { + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + List list = TextFormatter.Format (text, maxWidth, alignment, wrap, preserveTrailingSpaces); + Assert.NotEmpty (list); + Assert.True (list.Count == resultLines.Count ()); + + if (stringEmpty) + { + Assert.Equal (string.Empty, list [0]); + } + else + { + Assert.NotEqual (string.Empty, list [0]); + } + + Assert.Equal (resultLines, list); + + if (maxWidth > 0) + { + // remove whitespace chars + if (maxWidth < 5) + { + expectedClippedWidth = text.GetRuneCount () - text.Sum (r => r == ' ' ? 1 : 0); + } + else + { + expectedClippedWidth = Math.Min ( + text.GetRuneCount (), + maxWidth - text.Sum (r => r == ' ' ? 1 : 0) + ); + } + + list = TextFormatter.Format (text, maxWidth, Alignment.Start, wrap); + + if (maxWidth == 1) + { + Assert.Equal (expectedClippedWidth, list.Count); + Assert.Equal (noSpaceText, string.Concat (list.ToArray ())); + } + + if (maxWidth > 1 && maxWidth < 10) + { + Assert.Equal ( + StringExtensions.ToString (text.ToRunes () [..expectedClippedWidth]), + list [0] + ); + } + } + } + + [Theory] + [InlineData (null)] + [InlineData ("")] + [InlineData ("a")] + public void RemoveHotKeySpecifier_InValid_ReturnsOriginal (string text) + { + var hotKeySpecifier = (Rune)'_'; + + if (text == null) + { + Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier)); + Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier)); + Assert.Null (TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier)); + } + else + { + Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 0, hotKeySpecifier)); + Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, -1, hotKeySpecifier)); + Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, 100, hotKeySpecifier)); + } + } + + [Theory] + [InlineData ("all lower case", 0)] + [InlineData ("K Before", 0)] + [InlineData ("aK Second", 1)] + [InlineData ("Last K", 5)] + [InlineData ("fter K", 7)] + [InlineData ("Multiple K and R", 9)] + [InlineData ("Non-english: Кдать", 13)] + public void RemoveHotKeySpecifier_Valid_Legacy_ReturnsOriginal (string text, int hotPos) + { + var hotKeySpecifier = (Rune)'_'; + + Assert.Equal (text, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier)); + } + + [Theory] + [InlineData ("_K Before", 0, "K Before")] + [InlineData ("a_K Second", 1, "aK Second")] + [InlineData ("Last _K", 5, "Last K")] + [InlineData ("After K_", 7, "After K")] + [InlineData ("Multiple _K and _R", 9, "Multiple K and _R")] + [InlineData ("Non-english: _Кдать", 13, "Non-english: Кдать")] + public void RemoveHotKeySpecifier_Valid_ReturnsStripped (string text, int hotPos, string expectedText) + { + var hotKeySpecifier = (Rune)'_'; + + Assert.Equal (expectedText, TextFormatter.RemoveHotKeySpecifier (text, hotPos, hotKeySpecifier)); + } + + [Theory] + [InlineData ("test", 0, 't', "test")] + [InlineData ("test", 1, 'e', "test")] + [InlineData ("Ok", 0, 'O', "Ok")] + [InlineData ("[◦ Ok ◦]", 3, 'O', "[◦ Ok ◦]")] + [InlineData ("^k", 0, '^', "^k")] + public void ReplaceHotKeyWithTag (string text, int hotPos, uint tag, string expected) + { + var tf = new TextFormatter (); + List runes = text.ToRuneList (); + Rune rune; + + if (Rune.TryGetRuneAt (text, hotPos, out rune)) + { + Assert.Equal (rune, (Rune)tag); + } + + string result = TextFormatter.ReplaceHotKeyWithTag (text, hotPos); + Assert.Equal (result, expected); + Assert.Equal ((Rune)tag, result.ToRunes () [hotPos]); + Assert.Equal (text.GetRuneCount (), runes.Count); + Assert.Equal (text, StringExtensions.ToString (runes)); + } + + public static IEnumerable SplitEnvironmentNewLine => + new List + { + new object [] + { + $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界", + new [] { "First Line 界", "Second Line 界", "Third Line 界" } + }, + new object [] + { + $"First Line 界{Environment.NewLine}Second Line 界{Environment.NewLine}Third Line 界{Environment.NewLine}", + new [] { "First Line 界", "Second Line 界", "Third Line 界", "" } + } + }; + + [Theory] + [MemberData (nameof (SplitEnvironmentNewLine))] + public void SplitNewLine_Ending__With_Or_Without_NewLine_Probably_CRLF ( + string text, + IEnumerable expected + ) + { + List splited = TextFormatter.SplitNewLine (text); + Assert.Equal (expected, splited); + } + + [Theory] + [InlineData ( + "First Line 界\nSecond Line 界\nThird Line 界\n", + new [] { "First Line 界", "Second Line 界", "Third Line 界", "" } + )] + public void SplitNewLine_Ending_With_NewLine_Only_LF (string text, IEnumerable expected) + { + List splited = TextFormatter.SplitNewLine (text); + Assert.Equal (expected, splited); + } + + [Theory] + [InlineData ( + "First Line 界\nSecond Line 界\nThird Line 界", + new [] { "First Line 界", "Second Line 界", "Third Line 界" } + )] + public void SplitNewLine_Ending_Without_NewLine_Only_LF (string text, IEnumerable expected) + { + List splited = TextFormatter.SplitNewLine (text); + Assert.Equal (expected, splited); + } + + [Theory] + [InlineData ("New Test 你", 10, 10, 20320, 20320, 9, "你")] + [InlineData ("New Test \U0001d539", 10, 11, 120121, 55349, 9, "𝔹")] + public void String_Array_Is_Not_Always_Equal_ToRunes_Array ( + string text, + int runesLength, + int stringLength, + int runeValue, + int stringValue, + int index, + string expected + ) + { + Rune [] usToRunes = text.ToRunes (); + Assert.Equal (runesLength, usToRunes.Length); + Assert.Equal (stringLength, text.Length); + Assert.Equal (runeValue, usToRunes [index].Value); + Assert.Equal (stringValue, text [index]); + Assert.Equal (expected, usToRunes [index].ToString ()); + + if (char.IsHighSurrogate (text [index])) + { + // Rune array length isn't equal to string array + Assert.Equal (expected, new (new [] { text [index], text [index + 1] })); + } + else + { + // Rune array length is equal to string array + Assert.Equal (expected, text [index].ToString ()); + } + } + + [Theory] + [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] + [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] + [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] + [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] + public void TabWith_PreserveTrailingSpaces_False ( + int width, + int height, + TextDirection textDirection, + int tabWidth, + string expected + ) + { + var driver = new FakeDriver (); + driver.Init (); + + var text = "This is a \tTab"; + var tf = new TextFormatter (); + tf.Direction = textDirection; + tf.TabWidth = tabWidth; + tf.Text = text; + tf.ConstrainToWidth = 20; + tf.ConstrainToHeight = 20; + + Assert.True (tf.WordWrap); + Assert.False (tf.PreserveTrailingSpaces); + + tf.Draw ( + new (0, 0, width, height), + new (ColorName.White, ColorName.Black), + new (ColorName.Blue, ColorName.Black), + default (Rectangle), + driver + ); + TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); + + driver.End (); + } + + [Theory] + [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] + [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] + [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] + [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] + public void TabWith_PreserveTrailingSpaces_True ( + int width, + int height, + TextDirection textDirection, + int tabWidth, + string expected + ) + { + var driver = new FakeDriver (); + driver.Init (); + + var text = "This is a \tTab"; + var tf = new TextFormatter (); + + tf.Direction = textDirection; + tf.TabWidth = tabWidth; + tf.PreserveTrailingSpaces = true; + tf.Text = text; + tf.ConstrainToWidth = 20; + tf.ConstrainToHeight = 20; + + Assert.True (tf.WordWrap); + + tf.Draw ( + new (0, 0, width, height), + new (ColorName.White, ColorName.Black), + new (ColorName.Blue, ColorName.Black), + default (Rectangle), + driver + ); + TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); + + driver.End (); + } + + [Theory] + [InlineData (17, 1, TextDirection.LeftRight_TopBottom, 4, "This is a Tab")] + [InlineData (1, 17, TextDirection.TopBottom_LeftRight, 4, "T\nh\ni\ns\n \ni\ns\n \na\n \n \n \n \n \nT\na\nb")] + [InlineData (13, 1, TextDirection.LeftRight_TopBottom, 0, "This is a Tab")] + [InlineData (1, 13, TextDirection.TopBottom_LeftRight, 0, "T\nh\ni\ns\n \ni\ns\n \na\n \nT\na\nb")] + public void TabWith_WordWrap_True ( + int width, + int height, + TextDirection textDirection, + int tabWidth, + string expected + ) + { + var driver = new FakeDriver (); + driver.Init (); + + var text = "This is a \tTab"; + var tf = new TextFormatter (); + + tf.Direction = textDirection; + tf.TabWidth = tabWidth; + tf.WordWrap = true; + tf.Text = text; + tf.ConstrainToWidth = 20; + tf.ConstrainToHeight = 20; + + Assert.False (tf.PreserveTrailingSpaces); + + tf.Draw ( + new (0, 0, width, height), + new (ColorName.White, ColorName.Black), + new (ColorName.Blue, ColorName.Black), + default (Rectangle), + driver + ); + TestHelpers.AssertDriverContentsWithFrameAre (expected, _output, driver); + + driver.End (); + } + + [Theory] + [InlineData ("123456789", 3, "123")] + [InlineData ("Hello World", 8, "Hello Wo")] + public void TestClipOrPad_LongWord (string text, int fillPad, string expectedText) + { + // word is long but we want it to fill # space only + Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad)); + } + + [Theory] + [InlineData ("fff", 6, "fff ")] + [InlineData ("Hello World", 16, "Hello World ")] + public void TestClipOrPad_ShortWord (string text, int fillPad, string expectedText) + { + // word is short but we want it to fill # so it should be padded + Assert.Equal (expectedText, TextFormatter.ClipOrPad (text, fillPad)); + } + + [Theory] + [InlineData ("你", TextDirection.LeftRight_TopBottom, 2, 1)] + [InlineData ("你", TextDirection.TopBottom_LeftRight, 2, 1)] + [InlineData ("你你", TextDirection.LeftRight_TopBottom, 4, 1)] + [InlineData ("你你", TextDirection.TopBottom_LeftRight, 2, 2)] + public void Text_Set_SizeIsCorrect (string text, TextDirection textDirection, int expectedWidth, int expectedHeight) + { + var tf = new TextFormatter { Direction = textDirection, Text = text }; + tf.ConstrainToWidth = 10; + tf.ConstrainToHeight = 10; + + Assert.Equal (new (expectedWidth, expectedHeight), tf.FormatAndGetSize ()); + } + + [Fact] + [SetupFakeDriver] + public void UICatalog_AboutBox_Text () + { + TextFormatter tf = new () + { + Text = UICatalogApp.GetAboutBoxMessage (), + Alignment = Alignment.Center, + VerticalAlignment = Alignment.Start, + WordWrap = false, + MultiLine = true, + HotKeySpecifier = (Rune)0xFFFF + }; + + Size tfSize = tf.FormatAndGetSize (); + Assert.Equal (new (58, 13), tfSize); + + ((FakeDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height); + + Application.Driver.FillRect (Application.Screen, (Rune)'*'); + tf.Draw (Application.Screen, Attribute.Default, Attribute.Default); + + var expectedText = """ + ******UI Catalog: A comprehensive sample library for****** + ********************************************************** + _______ _ _ _____ _ + |__ __| (_) | | / ____| (_) + | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _ + | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | + | | __/ | | | | | | | | | | | (_| | || |__| | |_| | | + |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| + ********************************************************** + **********************v2 - Pre-Alpha********************** + ********************************************************** + **********https://github.com/gui-cs/Terminal.Gui********** + ********************************************************** + """; + + TestHelpers.AssertDriverContentsAre (expectedText.ReplaceLineEndings (), _output); + } + + [Fact] + public void WordWrap_BigWidth () + { + List wrappedLines; + + var text = "Constantinople"; + wrappedLines = TextFormatter.WordWrapText (text, 100); + Assert.True (wrappedLines.Count == 1); + Assert.Equal ("Constantinople", wrappedLines [0]); + } + + [Fact] + public void WordWrap_Invalid () + { + var text = string.Empty; + var width = 0; + + Assert.Empty (TextFormatter.WordWrapText (null, width)); + Assert.Empty (TextFormatter.WordWrapText (text, width)); + Assert.Throws (() => TextFormatter.WordWrapText (text, -1)); + } + + [Theory] + [InlineData ("A sentence has words.", 3, -18, new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." })] + [InlineData ( + "A sentence has words.", + 2, + -19, + new [] { "A", "se", "nt", "en", "ce", "ha", "s", "wo", "rd", "s." } + )] + [InlineData ( + "A sentence has words.", + 1, + -20, + new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." } + )] + public void WordWrap_Narrow_Default ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("A sentence has words.", 21, 0, new [] { "A sentence has words." })] + [InlineData ("A sentence has words.", 20, -1, new [] { "A sentence has", "words." })] + [InlineData ("A sentence has words.", 15, -6, new [] { "A sentence has", "words." })] + [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence has", "words." })] + [InlineData ("A sentence has words.", 13, -8, new [] { "A sentence", "has words." })] + + // Unicode + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 42, + 0, + new [] { "A Unicode sentence (пÑивеÑ) has words." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 41, + -1, + new [] { "A Unicode sentence (пÑивеÑ) has", "words." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 36, + -6, + new [] { "A Unicode sentence (пÑивеÑ) has", "words." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 35, + -7, + new [] { "A Unicode sentence (пÑивеÑ) has", "words." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 34, + -8, + new [] { "A Unicode sentence (пÑивеÑ)", "has words." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.", + 25, + -17, + new [] { "A Unicode sentence", "(пÑивеÑ) has words." } + )] + public void WordWrap_NoNewLines_Default ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + // Calls WordWrapText (text, width) and thus preserveTrailingSpaces defaults to false + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("これが最初の行です。 こんにちは世界。 これが2行目です。", 29, 0, new [] { "これが最初の行です。", "こんにちは世界。", "これが2行目です。" })] + public void WordWrap_PreserveTrailingSpaces_False_Unicode_Wide_Runes ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉", "があり ます。" })] + [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })] + [InlineData ("文に は言葉 があり ます。", 2, -12, new [] { "文", "に", "は", "言", "葉", "が", "あ", "り", "ま", "す", "。" })] + [InlineData ( + "文に は言葉 があり ます。", + 1, + -13, + new [] { " ", " ", " " } + )] // Just Spaces; should result in a single space for each line + public void WordWrap_PreserveTrailingSpaces_False_Wide_Runes ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData (null, 1, new string [] { })] // null input + [InlineData ("", 1, new string [] { })] // Empty input + [InlineData ("1 34", 1, new [] { "1", "3", "4" })] // Single Spaces + [InlineData ("1", 1, new [] { "1" })] // Short input + [InlineData ("12", 1, new [] { "1", "2" })] + [InlineData ("123", 1, new [] { "1", "2", "3" })] + [InlineData ("123456", 1, new [] { "1", "2", "3", "4", "5", "6" })] // No spaces + [InlineData (" ", 1, new [] { " " })] // Just Spaces; should result in a single space + [InlineData (" ", 1, new [] { " " })] + [InlineData (" ", 1, new [] { " ", " " })] + [InlineData (" ", 1, new [] { " ", " " })] + [InlineData ("12 456", 1, new [] { "1", "2", "4", "5", "6" })] // Single Spaces + [InlineData (" 2 456", 1, new [] { " ", "2", "4", "5", "6" })] // Leading spaces should be preserved. + [InlineData (" 2 456 8", 1, new [] { " ", "2", "4", "5", "6", "8" })] + [InlineData ( + "A sentence has words. ", + 1, + new [] { "A", "s", "e", "n", "t", "e", "n", "c", "e", "h", "a", "s", "w", "o", "r", "d", "s", "." } + )] // Complex example + [InlineData ("12 567", 1, new [] { "1", "2", " ", "5", "6", "7" })] // Double Spaces + [InlineData (" 3 567", 1, new [] { " ", "3", "5", "6", "7" })] // Double Leading spaces should be preserved. + [InlineData (" 3 678 1", 1, new [] { " ", "3", " ", "6", "7", "8", " ", "1" })] + [InlineData ("1 456", 1, new [] { "1", " ", "4", "5", "6" })] + [InlineData ( + "A sentence has words. ", + 1, + new [] + { + "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", "w", "o", "r", "d", "s", ".", " " + } + )] // Double space Complex example + public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_1 ( + string text, + int width, + IEnumerable resultLines + ) + { + List wrappedLines = TextFormatter.WordWrapText (text, width); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + Assert.Equal (resultLines, wrappedLines); + var breakLines = ""; + + foreach (string line in wrappedLines) + { + breakLines += $"{line}{Environment.NewLine}"; + } + + var expected = string.Empty; + + foreach (string line in resultLines) + { + expected += $"{line}{Environment.NewLine}"; + } + + Assert.Equal (expected, breakLines); + } + + [Theory] + [InlineData (null, 3, new string [] { })] // null input + [InlineData ("", 3, new string [] { })] // Empty input + [InlineData ("1", 3, new [] { "1" })] // Short input + [InlineData ("12", 3, new [] { "12" })] + [InlineData ("123", 3, new [] { "123" })] + [InlineData ("123456", 3, new [] { "123", "456" })] // No spaces + [InlineData ("1234567", 3, new [] { "123", "456", "7" })] // No spaces + [InlineData (" ", 3, new [] { " " })] // Just Spaces; should result in a single space + [InlineData (" ", 3, new [] { " " })] + [InlineData (" ", 3, new [] { " " })] + [InlineData (" ", 3, new [] { " " })] + [InlineData ("12 456", 3, new [] { "12", "456" })] // Single Spaces + [InlineData (" 2 456", 3, new [] { " 2", "456" })] // Leading spaces should be preserved. + [InlineData (" 2 456 8", 3, new [] { " 2", "456", "8" })] + [InlineData ( + "A sentence has words. ", + 3, + new [] { "A", "sen", "ten", "ce", "has", "wor", "ds." } + )] // Complex example + [InlineData ("12 567", 3, new [] { "12 ", "567" })] // Double Spaces + [InlineData (" 3 567", 3, new [] { " 3", "567" })] // Double Leading spaces should be preserved. + [InlineData (" 3 678 1", 3, new [] { " 3", " 67", "8 ", "1" })] + [InlineData ("1 456", 3, new [] { "1 ", "456" })] + [InlineData ( + "A sentence has words. ", + 3, + new [] { "A ", "sen", "ten", "ce ", " ", "has", "wor", "ds.", " " } + )] // Double space Complex example + public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_3 ( + string text, + int width, + IEnumerable resultLines + ) + { + List wrappedLines = TextFormatter.WordWrapText (text, width); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + Assert.Equal (resultLines, wrappedLines); + var breakLines = ""; + + foreach (string line in wrappedLines) + { + breakLines += $"{line}{Environment.NewLine}"; + } + + var expected = string.Empty; + + foreach (string line in resultLines) + { + expected += $"{line}{Environment.NewLine}"; + } + + Assert.Equal (expected, breakLines); + } + + [Theory] + [InlineData (null, 50, new string [] { })] // null input + [InlineData ("", 50, new string [] { })] // Empty input + [InlineData ("1", 50, new [] { "1" })] // Short input + [InlineData ("12", 50, new [] { "12" })] + [InlineData ("123", 50, new [] { "123" })] + [InlineData ("123456", 50, new [] { "123456" })] // No spaces + [InlineData ("1234567", 50, new [] { "1234567" })] // No spaces + [InlineData (" ", 50, new [] { " " })] // Just Spaces; should result in a single space + [InlineData (" ", 50, new [] { " " })] + [InlineData (" ", 50, new [] { " " })] + [InlineData ("12 456", 50, new [] { "12 456" })] // Single Spaces + [InlineData (" 2 456", 50, new [] { " 2 456" })] // Leading spaces should be preserved. + [InlineData (" 2 456 8", 50, new [] { " 2 456 8" })] + [InlineData ("A sentence has words. ", 50, new [] { "A sentence has words. " })] // Complex example + [InlineData ("12 567", 50, new [] { "12 567" })] // Double Spaces + [InlineData (" 3 567", 50, new [] { " 3 567" })] // Double Leading spaces should be preserved. + [InlineData (" 3 678 1", 50, new [] { " 3 678 1" })] + [InlineData ("1 456", 50, new [] { "1 456" })] + [InlineData ( + "A sentence has words. ", + 50, + new [] { "A sentence has words. " } + )] // Double space Complex example + public void WordWrap_PreserveTrailingSpaces_False_With_Simple_Runes_Width_50 ( + string text, + int width, + IEnumerable resultLines + ) + { + List wrappedLines = TextFormatter.WordWrapText (text, width); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + Assert.Equal (resultLines, wrappedLines); + var breakLines = ""; + + foreach (string line in wrappedLines) + { + breakLines += $"{line}{Environment.NewLine}"; + } + + var expected = string.Empty; + + foreach (string line in resultLines) + { + expected += $"{line}{Environment.NewLine}"; + } + + Assert.Equal (expected, breakLines); + } + + [Theory] + [InlineData ("A sentence has words.", 14, -7, new [] { "A sentence ", "has words." })] + [InlineData ("A sentence has words.", 8, -13, new [] { "A ", "sentence", " has ", "words." })] + [InlineData ("A sentence has words.", 6, -15, new [] { "A ", "senten", "ce ", "has ", "words." })] + [InlineData ("A sentence has words.", 3, -18, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds." })] + [InlineData ( + "A sentence has words.", + 2, + -19, + new [] { "A ", "se", "nt", "en", "ce", " ", "ha", "s ", "wo", "rd", "s." } + )] + [InlineData ( + "A sentence has words.", + 1, + -20, + new [] + { + "A", " ", "s", "e", "n", "t", "e", "n", "c", "e", " ", "h", "a", "s", " ", "w", "o", "r", "d", "s", "." + } + )] + public void WordWrap_PreserveTrailingSpaces_True ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("文に は言葉 があり ます。", 14, 0, new [] { "文に は言葉 ", "があり ます。" })] + [InlineData ("文に は言葉 があり ます。", 3, -11, new [] { "文", "に ", "は", "言", "葉 ", "が", "あ", "り ", "ま", "す", "。" })] + [InlineData ( + "文に は言葉 があり ます。", + 2, + -12, + new [] { "文", "に", " ", "は", "言", "葉", " ", "が", "あ", "り", " ", "ま", "す", "。" } + )] + [InlineData ("文に は言葉 があり ます。", 1, -13, new string [] { })] + public void WordWrap_PreserveTrailingSpaces_True_Wide_Runes ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("A sentence has words. ", 3, new [] { "A ", "sen", "ten", "ce ", "has", " ", "wor", "ds.", " " })] + [InlineData ( + "A sentence has words. ", + 3, + new [] { "A ", " ", "sen", "ten", "ce ", " ", " ", " ", "has", " ", "wor", "ds.", " " } + )] + public void WordWrap_PreserveTrailingSpaces_True_With_Simple_Runes_Width_3 ( + string text, + int width, + IEnumerable resultLines + ) + { + List wrappedLines = TextFormatter.WordWrapText (text, width, true); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + Assert.Equal (resultLines, wrappedLines); + var breakLines = ""; + + foreach (string line in wrappedLines) + { + breakLines += $"{line}{Environment.NewLine}"; + } + + var expected = string.Empty; + + foreach (string line in resultLines) + { + expected += $"{line}{Environment.NewLine}"; + } + + Assert.Equal (expected, breakLines); + + // Double space Complex example - this is how VS 2022 does it + //text = "A sentence has words. "; + //breakLines = ""; + //wrappedLines = TextFormatter.WordWrapText (text, width, preserveTrailingSpaces: true); + //foreach (var line in wrappedLines) { + // breakLines += $"{line}{Environment.NewLine}"; + //} + //expected = "A " + Environment.NewLine + + // " se" + Environment.NewLine + + // " nt" + Environment.NewLine + + // " en" + Environment.NewLine + + // " ce" + Environment.NewLine + + // " " + Environment.NewLine + + // " " + Environment.NewLine + + // " " + Environment.NewLine + + // " ha" + Environment.NewLine + + // " s " + Environment.NewLine + + // " wo" + Environment.NewLine + + // " rd" + Environment.NewLine + + // " s." + Environment.NewLine; + //Assert.Equal (expected, breakLines); + } + + [Theory] + [InlineData ("A sentence\t\t\t has words.", 14, -10, new [] { "A sentence\t", "\t\t has ", "words." })] + [InlineData ( + "A sentence\t\t\t has words.", + 8, + -16, + new [] { "A ", "sentence", "\t\t", "\t ", "has ", "words." } + )] + [InlineData ( + "A sentence\t\t\t has words.", + 3, + -21, + new [] { "A ", "sen", "ten", "ce", "\t", "\t", "\t", " ", "has", " ", "wor", "ds." } + )] + [InlineData ( + "A sentence\t\t\t has words.", + 2, + -22, + new [] { "A ", "se", "nt", "en", "ce", "\t", "\t", "\t", " ", "ha", "s ", "wo", "rd", "s." } + )] + [InlineData ( + "A sentence\t\t\t has words.", + 1, + -23, + new [] + { + "A", + " ", + "s", + "e", + "n", + "t", + "e", + "n", + "c", + "e", + "\t", + "\t", + "\t", + " ", + "h", + "a", + "s", + " ", + "w", + "o", + "r", + "d", + "s", + "." + } + )] + public void WordWrap_PreserveTrailingSpaces_True_With_Tab ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines, + int tabWidth = 4 + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth, true, tabWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("Constantinople", 14, 0, new [] { "Constantinople" })] + [InlineData ("Constantinople", 12, -2, new [] { "Constantinop", "le" })] + [InlineData ("Constantinople", 9, -5, new [] { "Constanti", "nople" })] + [InlineData ("Constantinople", 7, -7, new [] { "Constan", "tinople" })] + [InlineData ("Constantinople", 5, -9, new [] { "Const", "antin", "ople" })] + [InlineData ("Constantinople", 4, -10, new [] { "Cons", "tant", "inop", "le" })] + [InlineData ( + "Constantinople", + 1, + -13, + new [] { "C", "o", "n", "s", "t", "a", "n", "t", "i", "n", "o", "p", "l", "e" } + )] + public void WordWrap_SingleWordLine ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 20, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] + [InlineData ("This\u00A0is\n\u00A0a\u00A0sentence.", 19, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] + [InlineData ( + "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence.", + 19, + 0, + new [] { "\u00A0\u00A0\u00A0\u00A0\u00A0test\u00A0sentence." } + )] + public void WordWrap_Unicode_2LinesWithNonBreakingSpace ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 19, 0, new [] { "This\u00A0is\u00A0a\u00A0sentence." })] + [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 18, -1, new [] { "This\u00A0is\u00A0a\u00A0sentence", "." })] + [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 17, -2, new [] { "This\u00A0is\u00A0a\u00A0sentenc", "e." })] + [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 14, -5, new [] { "This\u00A0is\u00A0a\u00A0sent", "ence." })] + [InlineData ("This\u00A0is\u00A0a\u00A0sentence.", 10, -9, new [] { "This\u00A0is\u00A0a\u00A0", "sentence." })] + [InlineData ( + "This\u00A0is\u00A0a\u00A0sentence.", + 7, + -12, + new [] { "This\u00A0is", "\u00A0a\u00A0sent", "ence." } + )] + [InlineData ( + "This\u00A0is\u00A0a\u00A0sentence.", + 5, + -14, + new [] { "This\u00A0", "is\u00A0a\u00A0", "sente", "nce." } + )] + [InlineData ( + "This\u00A0is\u00A0a\u00A0sentence.", + 1, + -18, + new [] + { + "T", "h", "i", "s", "\u00A0", "i", "s", "\u00A0", "a", "\u00A0", "s", "e", "n", "t", "e", "n", "c", "e", "." + } + )] + public void WordWrap_Unicode_LineWithNonBreakingSpace ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + [Theory] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 51, + 0, + new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 50, + -1, + new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 46, + -5, + new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮ", "ฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 26, + -25, + new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 17, + -34, + new [] { "กขฃคฅฆงจฉชซฌญฎฏฐฑ", "ฒณดตถทธนบปผฝพฟภมย", "รฤลฦวศษสหฬอฮฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 13, + -38, + new [] { "กขฃคฅฆงจฉชซฌญ", "ฎฏฐฑฒณดตถทธนบ", "ปผฝพฟภมยรฤลฦว", "ศษสหฬอฮฯะัาำ" } + )] + [InlineData ( + "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำ", + 1, + -50, + new [] + { + "ก", + "ข", + "ฃ", + "ค", + "ฅ", + "ฆ", + "ง", + "จ", + "ฉ", + "ช", + "ซ", + "ฌ", + "ญ", + "ฎ", + "ฏ", + "ฐ", + "ฑ", + "ฒ", + "ณ", + "ด", + "ต", + "ถ", + "ท", + "ธ", + "น", + "บ", + "ป", + "ผ", + "ฝ", + "พ", + "ฟ", + "ภ", + "ม", + "ย", + "ร", + "ฤ", + "ล", + "ฦ", + "ว", + "ศ", + "ษ", + "ส", + "ห", + "ฬ", + "อ", + "ฮ", + "ฯ", + "ะั", + "า", + "ำ" + } + )] + public void WordWrap_Unicode_SingleWordLine ( + string text, + int maxWidth, + int widthOffset, + IEnumerable resultLines + ) + { + List wrappedLines; + + IEnumerable zeroWidth = text.EnumerateRunes ().Where (r => r.GetColumns () == 0); + Assert.Single (zeroWidth); + Assert.Equal ('ั', zeroWidth.ElementAt (0).Value); + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth + >= (wrappedLines.Count > 0 + ? wrappedLines.Max ( + l => l.GetRuneCount () + + zeroWidth.Count () + - 1 + + widthOffset + ) + : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + + /// WordWrap strips CRLF + [Theory] + [InlineData ( + "A sentence has words.\nA paragraph has lines.", + 44, + 0, + new [] { "A sentence has words.A paragraph has lines." } + )] + [InlineData ( + "A sentence has words.\nA paragraph has lines.", + 43, + -1, + new [] { "A sentence has words.A paragraph has lines." } + )] + [InlineData ( + "A sentence has words.\nA paragraph has lines.", + 38, + -6, + new [] { "A sentence has words.A paragraph has", "lines." } + )] + [InlineData ( + "A sentence has words.\nA paragraph has lines.", + 34, + -10, + new [] { "A sentence has words.A paragraph", "has lines." } + )] + [InlineData ( + "A sentence has words.\nA paragraph has lines.", + 27, + -17, + new [] { "A sentence has words.A", "paragraph has lines." } + )] + + // Unicode + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", + 69, + 0, + new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", + 68, + -1, + new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has Линии." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", + 63, + -6, + new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт has", "Линии." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", + 59, + -10, + new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode Пункт", "has Линии." } + )] + [InlineData ( + "A Unicode sentence (пÑивеÑ) has words.\nA Unicode Пункт has Линии.", + 52, + -17, + new [] { "A Unicode sentence (пÑивеÑ) has words.A Unicode", "Пункт has Линии." } + )] + public void WordWrap_WithNewLines (string text, int maxWidth, int widthOffset, IEnumerable resultLines) + { + List wrappedLines; + + Assert.Equal (maxWidth, text.GetRuneCount () + widthOffset); + int expectedClippedWidth = Math.Min (text.GetRuneCount (), maxWidth); + wrappedLines = TextFormatter.WordWrapText (text, maxWidth); + Assert.Equal (wrappedLines.Count, resultLines.Count ()); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetRuneCount ()) : 0) + ); + + Assert.True ( + expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0) + ); + Assert.Equal (resultLines, wrappedLines); + } + #region FormatAndGetSizeTests // TODO: Add multi-line examples @@ -7193,46 +7218,4 @@ B ")] } #endregion - - [Fact] - [SetupFakeDriver] - public void UICatalog_AboutBox_Text () - { - TextFormatter tf = new () - { - Text = UICatalog.UICatalogApp.GetAboutBoxMessage (), - Alignment = Alignment.Center, - VerticalAlignment = Alignment.Start, - WordWrap = false, - MultiLine = true, - HotKeySpecifier = (Rune)0xFFFF - }; - - Size tfSize = tf.FormatAndGetSize (); - Assert.Equal (new Size (58, 13), tfSize); - - ((FakeDriver)Application.Driver).SetBufferSize (tfSize.Width, tfSize.Height); - - Application.Driver.FillRect (Application.Screen, (Rune)'*'); - tf.Draw (Application.Screen, Attribute.Default, Attribute.Default); - - string expectedText = """ - ******UI Catalog: A comprehensive sample library for****** - ********************************************************** - _______ _ _ _____ _ - |__ __| (_) | | / ____| (_) - | | ___ _ __ _ __ ___ _ _ __ __ _| || | __ _ _ _ - | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | || | |_ | | | | | - | | __/ | | | | | | | | | | | (_| | || |__| | |_| | | - |_|\___|_| |_| |_| |_|_|_| |_|\__,_|_(_)_____|\__,_|_| - ********************************************************** - **********************v2 - Pre-Alpha********************** - ********************************************************** - **********https://github.com/gui-cs/Terminal.Gui********** - ********************************************************** - """; - - TestHelpers.AssertDriverContentsAre (expectedText.ReplaceLineEndings (), _output); - - } } diff --git a/UnitTests/View/TitleTests.cs b/UnitTests/View/TitleTests.cs index 0c2a6a5b7..c808e66e4 100644 --- a/UnitTests/View/TitleTests.cs +++ b/UnitTests/View/TitleTests.cs @@ -3,7 +3,7 @@ using Xunit.Abstractions; namespace Terminal.Gui.ViewTests; -public class TitleTests (ITestOutputHelper output) +public class TitleTests { // Unit tests that verify look & feel of title are in BorderTests.cs diff --git a/UnitTests/Views/ListViewTests.cs b/UnitTests/Views/ListViewTests.cs index 049c5f7e1..6d2e5decc 100644 --- a/UnitTests/Views/ListViewTests.cs +++ b/UnitTests/Views/ListViewTests.cs @@ -674,8 +674,10 @@ Item 6", private class NewListDataSource : IListDataSource { +#pragma warning disable CS0067 /// public event NotifyCollectionChangedEventHandler CollectionChanged; +#pragma warning restore CS0067 public int Count => 0; public int Length => 0; diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index ac8de4db0..9b1067290 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/UnitTests/Views/MenuBarTests.cs @@ -3800,7 +3800,7 @@ Edit menuItem.RemoveMenuItem (); Assert.Single (menuBar.Menus); - Assert.Equal (null, menuBar.Menus [0].Children); + Assert.Null (menuBar.Menus [0].Children); Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings); Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings); diff --git a/UnitTests/Views/NumericUpDownTests.cs b/UnitTests/Views/NumericUpDownTests.cs index 9743de019..571a9fab4 100644 --- a/UnitTests/Views/NumericUpDownTests.cs +++ b/UnitTests/Views/NumericUpDownTests.cs @@ -3,7 +3,7 @@ using Xunit.Abstractions; namespace Terminal.Gui.ViewsTests; -public class NumericUpDownTests (ITestOutputHelper _output) +public class NumericUpDownTests { [Fact] public void WhenCreated_ShouldHaveDefaultValues_int () diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs index 6f6f93061..14866a154 100644 --- a/UnitTests/Views/StatusBarTests.cs +++ b/UnitTests/Views/StatusBarTests.cs @@ -1,7 +1,7 @@ using Xunit.Abstractions; namespace Terminal.Gui.ViewsTests; -public class StatusBarTests (ITestOutputHelper output) +public class StatusBarTests { [Fact] public void AddItemAt_RemoveItem_Replacing () diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs index 2a3397491..61deab69c 100644 --- a/UnitTests/Views/TableViewTests.cs +++ b/UnitTests/Views/TableViewTests.cs @@ -2582,7 +2582,9 @@ A B C TestHelpers.AssertDriverContentsAre (expected, output); +#pragma warning disable xUnit2029 Assert.Empty (pets.Where (p => p.IsPicked)); +#pragma warning restore xUnit2029 tv.NewKeyDownEvent (Key.Space); @@ -2795,7 +2797,9 @@ A B C tv.NewKeyDownEvent (Key.Space); +#pragma warning disable xUnit2029 Assert.Empty (pets.Where (p => p.IsPicked)); +#pragma warning restore xUnit2029 tv.Draw (); @@ -2924,7 +2928,9 @@ A B C TestHelpers.AssertDriverContentsAre (expected, output); +#pragma warning disable xUnit2029 Assert.Empty (pets.Where (p => p.IsPicked)); +#pragma warning restore xUnit2029 tv.NewKeyDownEvent (Key.Space); @@ -3089,7 +3095,9 @@ A B C // Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.Space }); +#pragma warning disable xUnit2029 Assert.Empty (tableView.MultiSelectedRegions.Where (r => r.IsToggled)); +#pragma warning restore xUnit2029 } [Fact]