Resolving merge conflicts

This commit is contained in:
BDisp
2025-11-12 15:25:29 +00:00
parent 012356eaeb
commit 87ea2af9bf
49 changed files with 2374 additions and 2030 deletions

View File

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

View File

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

View File

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

View File

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