mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 17:57:57 +01:00
Resolving merge conflicts
This commit is contained in:
@@ -88,7 +88,7 @@ public class RuneTests
|
||||
1
|
||||
)] // the letters 법 join to form the Korean word for "rice:" U+BC95 법 (read from top left to bottom right)
|
||||
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467", "👨👩👧", 8, 2, 8)] // Man, Woman and Girl emoji.
|
||||
[InlineData ("\u0915\u093f", "कि", 2, 1, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
|
||||
[InlineData ("\u0915\u093f", "कि", 2, 2, 2)] // Hindi कि with DEVANAGARI LETTER KA and DEVANAGARI VOWEL SIGN I
|
||||
[InlineData (
|
||||
"\u0e4d\u0e32",
|
||||
"ํา",
|
||||
@@ -365,6 +365,42 @@ public class RuneTests
|
||||
[InlineData ('\ud801')]
|
||||
public void Rune_Exceptions_Integers (int code) { Assert.Throws<ArgumentOutOfRangeException> (() => new Rune (code)); }
|
||||
|
||||
[Theory]
|
||||
// Control characters (should be mapped to Control Pictures)
|
||||
[InlineData ('\u0000', 0x2400)] // NULL → ␀
|
||||
[InlineData ('\u0009', 0x2409)] // TAB → ␉
|
||||
[InlineData ('\u000A', 0x240A)] // LF → ␊
|
||||
[InlineData ('\u000D', 0x240D)] // CR → ␍
|
||||
|
||||
// Printable characters (should remain unchanged)
|
||||
[InlineData ('A', 'A')]
|
||||
[InlineData (' ', ' ')]
|
||||
[InlineData ('~', '~')]
|
||||
public void MakePrintable_ReturnsExpected (char inputChar, int expectedCodePoint)
|
||||
{
|
||||
// Arrange
|
||||
Rune input = new Rune (inputChar);
|
||||
|
||||
// Act
|
||||
Rune result = input.MakePrintable ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedCodePoint, result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MakePrintable_SupplementaryRune_RemainsUnchanged ()
|
||||
{
|
||||
// Arrange: supplementary character outside BMP (not a control)
|
||||
Rune input = new Rune (0x1F600); // 😀 grinning face emoji
|
||||
|
||||
// Act
|
||||
Rune result = input.MakePrintable ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (input.Value, result.Value);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (new [] { '\ud799', '\udc21' })]
|
||||
public void Rune_Exceptions_Utf16_Encode (char [] code)
|
||||
@@ -954,11 +990,9 @@ public class RuneTests
|
||||
Assert.Equal (runeCount, us.GetRuneCount ());
|
||||
Assert.Equal (stringCount, s.Length);
|
||||
|
||||
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator (s);
|
||||
|
||||
var textElementCount = 0;
|
||||
|
||||
while (enumerator.MoveNext ())
|
||||
foreach (string _ in GraphemeHelper.GetGraphemes (s))
|
||||
{
|
||||
textElementCount++; // For versions prior to Net5.0 the StringInfo class might handle some grapheme clusters incorrectly.
|
||||
}
|
||||
@@ -1064,4 +1098,95 @@ public class RuneTests
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0x0041, new byte [] { 0x41 })] // 'A', ASCII
|
||||
[InlineData (0x00E9, new byte [] { 0xC3, 0xA9 })] // 'é', 2-byte UTF-8
|
||||
[InlineData (0x20AC, new byte [] { 0xE2, 0x82, 0xAC })] // '€', 3-byte UTF-8
|
||||
[InlineData (0x1F600, new byte [] { 0xF0, 0x9F, 0x98, 0x80 })] // 😀 emoji, 4-byte UTF-8
|
||||
public void Encode_WritesExpectedBytes (int codePoint, byte [] expectedBytes)
|
||||
{
|
||||
// Arrange
|
||||
Rune rune = new Rune (codePoint);
|
||||
byte [] buffer = new byte [10]; // extra space
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer [i] = 0xFF;
|
||||
}
|
||||
|
||||
// Act
|
||||
int written = rune.Encode (buffer);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedBytes.Length, written);
|
||||
for (int i = 0; i < written; i++)
|
||||
{
|
||||
Assert.Equal (expectedBytes [i], buffer [i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encode_WithStartAndCount_WritesPartialBytes ()
|
||||
{
|
||||
// Arrange: U+1F600 😀 (4 bytes)
|
||||
Rune rune = new Rune (0x1F600);
|
||||
byte [] buffer = new byte [10];
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer [i] = 0xFF;
|
||||
}
|
||||
|
||||
// Act: write starting at index 2, limit count to 2 bytes
|
||||
int written = rune.Encode (buffer, start: 2, count: 2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (2, written);
|
||||
// Original UTF-8 bytes: F0 9F 98 80
|
||||
Assert.Equal (0xF0, buffer [2]);
|
||||
Assert.Equal (0x9F, buffer [3]);
|
||||
// Remaining buffer untouched
|
||||
Assert.Equal (0xFF, buffer [0]);
|
||||
Assert.Equal (0xFF, buffer [1]);
|
||||
Assert.Equal (0xFF, buffer [4]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encode_WithCountGreaterThanRuneBytes_WritesAllBytes ()
|
||||
{
|
||||
// Arrange: é → C3 A9
|
||||
Rune rune = new Rune ('é');
|
||||
byte [] buffer = new byte [10];
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer [i] = 0xFF;
|
||||
}
|
||||
|
||||
// Act: count larger than needed
|
||||
int written = rune.Encode (buffer, start: 1, count: 10);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (2, written);
|
||||
Assert.Equal (0xC3, buffer [1]);
|
||||
Assert.Equal (0xA9, buffer [2]);
|
||||
Assert.Equal (0xFF, buffer [3]); // next byte untouched
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Encode_ZeroCount_WritesNothing ()
|
||||
{
|
||||
Rune rune = new Rune ('A');
|
||||
byte [] buffer = new byte [5];
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
buffer [i] = 0xFF;
|
||||
}
|
||||
|
||||
int written = rune.Encode (buffer, start: 0, count: 0);
|
||||
|
||||
Assert.Equal (0, written);
|
||||
foreach (var b in buffer)
|
||||
{
|
||||
Assert.Equal (0xFF, b); // buffer untouched
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +30,24 @@ public class StringTests
|
||||
|
||||
// Test known wide codepoints
|
||||
[Theory]
|
||||
[InlineData ("🙂", 2)]
|
||||
[InlineData ("a🙂", 3)]
|
||||
[InlineData ("🙂a", 3)]
|
||||
[InlineData ("👨👩👦👦", 2)]
|
||||
[InlineData ("👨👩👦👦🙂", 4)]
|
||||
[InlineData ("👨👩👦👦🙂a", 5)]
|
||||
[InlineData ("👨👩👦👦a🙂", 5)]
|
||||
[InlineData ("👨👩👦👦👨👩👦👦", 4)]
|
||||
[InlineData ("山", 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
|
||||
[InlineData ("山🙂", 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
|
||||
[InlineData ("🙂", 1, 2)]
|
||||
[InlineData ("a🙂", 2, 3)]
|
||||
[InlineData ("🙂a", 2, 3)]
|
||||
[InlineData ("👨👩👦👦", 1, 2)]
|
||||
[InlineData ("👨👩👦👦🙂", 2, 4)]
|
||||
[InlineData ("👨👩👦👦🙂a", 3, 5)]
|
||||
[InlineData ("👨👩👦👦a🙂", 3, 5)]
|
||||
[InlineData ("👨👩👦👦👨👩👦👦", 2, 4)]
|
||||
[InlineData ("าำ", 1, 2)] // า U+0E32 - THAI CHARACTER SARA AA with ำ U+0E33 - THAI CHARACTER SARA AM
|
||||
[InlineData ("山", 1, 2)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
|
||||
[InlineData ("山🙂", 2, 4)] // The character for "mountain" in Chinese/Japanese/Korean (山), Unicode U+5C71
|
||||
//[InlineData ("\ufe20\ufe21", 2)] // Combining Ligature Left Half ︠ - U+fe20 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
|
||||
// // Combining Ligature Right Half - U+fe21 -https://github.com/microsoft/terminal/blob/main/src/types/unicode_width_overrides.xml
|
||||
public void TestGetColumns_MultiRune_WideBMP (string str, int expected) { Assert.Equal (expected, str.GetColumns ()); }
|
||||
public void TestGetColumns_MultiRune_WideBMP_Graphemes (string str, int graphemesCount, int expected)
|
||||
{
|
||||
Assert.Equal (graphemesCount, GraphemeHelper.GetGraphemes (str).ToArray ().Length);
|
||||
Assert.Equal (expected, str.GetColumns ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGetColumns_Null ()
|
||||
@@ -64,4 +69,121 @@ public class StringTests
|
||||
var str = "\u200D";
|
||||
Assert.Equal (0, str.GetColumns ());
|
||||
}
|
||||
|
||||
public class ReadOnlySpanExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData ("12345", true)] // all ASCII digits
|
||||
[InlineData ("0", true)] // single ASCII digit
|
||||
[InlineData ("", false)] // empty span
|
||||
[InlineData ("12a45", false)] // contains a letter
|
||||
[InlineData ("123", false)] // full-width Unicode digits (not ASCII)
|
||||
[InlineData ("12 34", false)] // contains space
|
||||
[InlineData ("١٢٣", false)] // Arabic-Indic digits
|
||||
public void IsAllAsciiDigits_WorksAsExpected (string input, bool expected)
|
||||
{
|
||||
// Arrange
|
||||
ReadOnlySpan<char> span = input.AsSpan ();
|
||||
|
||||
// Act
|
||||
bool result = span.IsAllAsciiDigits ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("0", true)]
|
||||
[InlineData ("9", true)]
|
||||
[InlineData ("A", true)]
|
||||
[InlineData ("F", true)]
|
||||
[InlineData ("a", true)]
|
||||
[InlineData ("f", true)]
|
||||
[InlineData ("123ABC", true)]
|
||||
[InlineData ("abcdef", true)]
|
||||
[InlineData ("G", false)] // 'G' not hex
|
||||
[InlineData ("Z9", false)] // 'Z' not hex
|
||||
[InlineData ("12 34", false)] // space not hex
|
||||
[InlineData ("", false)] // empty string
|
||||
[InlineData ("123", false)] // full-width digits, not ASCII
|
||||
[InlineData ("0xFF", false)] // includes 'x'
|
||||
public void IsAllAsciiHexDigits_ReturnsExpected (string input, bool expected)
|
||||
{
|
||||
// Arrange
|
||||
ReadOnlySpan<char> span = input.AsSpan ();
|
||||
|
||||
// Act
|
||||
bool result = span.IsAllAsciiHexDigits ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData (nameof (GetStringConcatCases))]
|
||||
public void ToString_ReturnsExpected (IEnumerable<string> input, string expected)
|
||||
{
|
||||
// Act
|
||||
string result = StringExtensions.ToString (input);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
|
||||
public static IEnumerable<object []> GetStringConcatCases ()
|
||||
{
|
||||
yield return [new string [] { }, string.Empty]; // Empty sequence
|
||||
yield return [new [] { "" }, string.Empty]; // Single empty string
|
||||
yield return [new [] { "A" }, "A"]; // Single element
|
||||
yield return [new [] { "A", "B" }, "AB"]; // Simple concatenation
|
||||
yield return [new [] { "Hello", " ", "World" }, "Hello World"]; // Multiple parts
|
||||
yield return [new [] { "123", "456", "789" }, "123456789"]; // Numeric strings
|
||||
yield return [new [] { "👩", "🧒" }, "👩🧒"]; // Grapheme sequence
|
||||
yield return [new [] { "α", "β", "γ" }, "αβγ"]; // Unicode letters
|
||||
yield return [new [] { "A", null, "B" }, "AB"]; // Null ignored by string.Concat
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("", false)] // Empty string
|
||||
[InlineData ("A", false)] // Single BMP character
|
||||
[InlineData ("AB", false)] // Two BMP chars, not a surrogate pair
|
||||
[InlineData ("👩", true)] // Single emoji surrogate pair (U+1F469)
|
||||
[InlineData ("🧒", true)] // Another emoji surrogate pair (U+1F9D2)
|
||||
[InlineData ("𐍈", true)] // Gothic letter hwair (U+10348)
|
||||
[InlineData ("A👩", false)] // One BMP + one surrogate half
|
||||
[InlineData ("👩", false)] // Surrogate pair + ZWJ (length != 2)
|
||||
public void IsSurrogatePair_ReturnsExpected (string input, bool expected)
|
||||
{
|
||||
// Act
|
||||
bool result = input.IsSurrogatePair ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Control characters (should be replaced with the "Control Pictures" block)
|
||||
[InlineData ("\u0000", "\u2400")] // NULL → ␀
|
||||
[InlineData ("\u0009", "\u2409")] // TAB → ␉
|
||||
[InlineData ("\u000A", "\u240A")] // LF → ␊
|
||||
[InlineData ("\u000D", "\u240D")] // CR → ␍
|
||||
|
||||
// Printable characters (should remain unchanged)
|
||||
[InlineData ("A", "A")]
|
||||
[InlineData (" ", " ")]
|
||||
[InlineData ("~", "~")]
|
||||
|
||||
// Multi-character string (should return unchanged)
|
||||
[InlineData ("AB", "AB")]
|
||||
[InlineData ("Hello", "Hello")]
|
||||
[InlineData ("\u0009A", "\u0009A")] // includes a control char, but length > 1
|
||||
public void MakePrintable_ReturnsExpected (string input, string expected)
|
||||
{
|
||||
// Act
|
||||
string result = input.MakePrintable ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,5 +665,30 @@ Nice Work")]
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output, driver);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.LeftRight_TopBottom, "👨👩👧👦")]
|
||||
[InlineData ("\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 2, 1, TextDirection.TopBottom_LeftRight, "👨👩👧👦")]
|
||||
public void Draw_Emojis_With_Zero_Width_Joiner (
|
||||
string text,
|
||||
int width,
|
||||
int height,
|
||||
TextDirection direction,
|
||||
string expectedDraw
|
||||
)
|
||||
{
|
||||
TextFormatter tf = new ()
|
||||
{
|
||||
Direction = direction,
|
||||
ConstrainToSize = new (width, height),
|
||||
Text = text,
|
||||
WordWrap = false
|
||||
};
|
||||
Assert.Equal (width, text.GetColumns ());
|
||||
|
||||
tf.Draw (new (0, 0, width, height), Attribute.Default, Attribute.Default);
|
||||
|
||||
DriverAssert.AssertDriverContentsWithFrameAre (expectedDraw, output);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -792,19 +792,16 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
[MemberData (nameof (CMGlyphs))]
|
||||
public void GetLengthThatFits_List_Simple_And_Wide_Runes (string text, int columns, int expectedLength)
|
||||
{
|
||||
List<Rune> runes = text.ToRuneList ();
|
||||
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
|
||||
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData ("test", 3, 3)]
|
||||
[InlineData ("test", 4, 4)]
|
||||
[InlineData ("test", 10, 4)]
|
||||
public void GetLengthThatFits_Runelist (string text, int columns, int expectedLength)
|
||||
public void GetLengthThatFits_For_String (string text, int columns, int expectedLength)
|
||||
{
|
||||
List<Rune> runes = text.ToRuneList ();
|
||||
|
||||
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (runes, columns));
|
||||
Assert.Equal (expectedLength, TextFormatter.GetLengthThatFits (text, columns));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -833,7 +830,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
public void GetLengthThatFits_With_Combining_Runes ()
|
||||
{
|
||||
var text = "Les Mise\u0328\u0301rables";
|
||||
Assert.Equal (16, TextFormatter.GetLengthThatFits (text, 14));
|
||||
Assert.Equal (14, TextFormatter.GetLengthThatFits (text, 14));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -2761,8 +2758,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
"ฮ",
|
||||
"ฯ",
|
||||
"ะั",
|
||||
"า",
|
||||
"ำ"
|
||||
"าำ"
|
||||
}
|
||||
)]
|
||||
public void WordWrap_Unicode_SingleWordLine (
|
||||
@@ -2797,7 +2793,17 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
Assert.True (
|
||||
expectedClippedWidth >= (wrappedLines.Count > 0 ? wrappedLines.Max (l => l.GetColumns ()) : 0)
|
||||
);
|
||||
Assert.Equal (resultLines, wrappedLines);
|
||||
|
||||
if (maxWidth == 1)
|
||||
{
|
||||
List<string> newResultLines = resultLines.ToList ();
|
||||
newResultLines [^1] = "";
|
||||
Assert.Equal (newResultLines, wrappedLines);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal (resultLines, wrappedLines);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>WordWrap strips CRLF</summary>
|
||||
@@ -3074,8 +3080,8 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
}
|
||||
|
||||
[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 (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,
|
||||
@@ -3084,7 +3090,7 @@ public class TextFormatterTests (ITestOutputHelper output) : FakeDriverBase
|
||||
LMre
|
||||
eias
|
||||
ssb
|
||||
ęl "
|
||||
ę́l "
|
||||
)]
|
||||
public void Draw_With_Combining_Runes (int width, int height, TextDirection textDirection, string expected)
|
||||
{
|
||||
@@ -3111,7 +3117,6 @@ ssb
|
||||
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")]
|
||||
@@ -3189,7 +3194,6 @@ ssb
|
||||
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")]
|
||||
@@ -3227,5 +3231,4 @@ ssb
|
||||
|
||||
driver.End ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user