Skip to content

Commit 84f0a31

Browse files
committed
Implemented the remaining UString methods and added tests for them. Updated to version 1.0.0 (finally!).
1 parent f541d09 commit 84f0a31

6 files changed

Lines changed: 485 additions & 48 deletions

File tree

Package/Package README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
# UnicodeHelper
22

3-
*Note: This is a work-in-progress. In particular, some methods on UString have not been implemented yet.*
4-
53
UnicodeHelper is a .Net library to get Unicode property information about codepoints, and to better handle the
64
upper planes (1-16) of Unicode where having to deal with surrogate pairs can get messy using normal .Net strings.
75
Current version supoorts **Unicode 17.0.0**.

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# UnicodeHelper
22
[![Test status](https://github.com/FoolRunning/UnicodeHelper/actions/workflows/dotnet.yml/badge.svg)](https://github.com/FoolRunning/UnicodeHelper/actions/workflows/dotnet.yml)
33

4-
*Note: This is a work-in-progress. In particular, some methods on UString have not been implemented yet.*
5-
64
UnicodeHelper is a .Net library to get Unicode property information about codepoints, and to better handle the
75
upper planes (1-16) of Unicode where having to deal with surrogate pairs can get messy using normal .Net strings.
86
Current version supoorts **Unicode 17.0.0**.

UnicodeHelper.Tests/UStringTests.cs

Lines changed: 292 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Text;
22
using UnicodeHelper.TestData;
33

4+
// ReSharper disable ObjectCreationAsStatement
45
namespace UnicodeHelper
56
{
67
[TestClass]
@@ -32,8 +33,74 @@ public void Constructor(string testString, params string[] expectedCodepoints)
3233
"Characters differ at index " + i);
3334
}
3435
}
36+
37+
private static IEnumerable<object[]> ConstructorCodepointListExceptionTestData =>
38+
[
39+
[new List<UCodepoint>(), -1, 0, typeof(ArgumentOutOfRangeException)],
40+
[new List<UCodepoint>(), 0, -1, typeof(ArgumentOutOfRangeException)],
41+
[new List<UCodepoint>(), 0, 1, typeof(ArgumentException)],
42+
[new List<UCodepoint> { 'a', 'b' }, 1, 2, typeof(ArgumentException)],
43+
];
44+
45+
[TestMethod]
46+
[DynamicData(nameof(ConstructorCodepointListExceptionTestData))]
47+
public void Constructor_CodepointList_InvalidParameters(List<UCodepoint> codepoints,
48+
int startIndex, int count, Type expectedExceptionType)
49+
{
50+
Assert.That.ThrowsException(expectedExceptionType,
51+
() => new UString(codepoints, startIndex, count));
52+
}
53+
54+
[TestMethod]
55+
public void Constructor_CodepointList_NullThrows()
56+
{
57+
Assert.That.ThrowsException(typeof(ArgumentNullException),
58+
() => new UString((IReadOnlyList<UCodepoint>?)null, 0, 0));
59+
}
60+
61+
private static IEnumerable<object[]> ConstructorCodepointListTestData =>
62+
[
63+
[""],
64+
["This is\r\na test!", "T", "h", "i", "s", " ", "i", "s", "\r", "\n", "a", " ", "t", "e", "s", "t", "!"],
65+
["العربية", "ا", "ل", "ع", "ر", "ب", "ي", "ة"],
66+
["😁🤔😮", "😁", "🤔", "😮"]
67+
];
68+
69+
[TestMethod]
70+
[DynamicData(nameof(ConstructorCodepointListTestData))]
71+
public void Constructor_CodepointList(string testString, params string[] expectedCodepoints)
72+
{
73+
UString temp = new(testString);
74+
List<UCodepoint> codepoints = temp.ToList();
75+
76+
UString us = new(codepoints);
77+
Assert.AreEqual(testString, us.ToString());
78+
Assert.AreEqual(expectedCodepoints.Length, us.Length);
79+
for (int i = 0; i < expectedCodepoints.Length; i++)
80+
Assert.AreEqual(UCodepoint.ReadFromStr(expectedCodepoints[i], 0), us[i], "Characters differ at index " + i);
81+
}
82+
83+
private static IEnumerable<object[]> ConstructorCodepointListWithRangeTestData =>
84+
[
85+
["This is a test!", 5, 2, "is"],
86+
["العربية", 2, 3, "عرب"],
87+
["😁🤔😮", 1, 2, "🤔😮"],
88+
["Hello", 0, 5, "Hello"],
89+
["Hello", 2, 0, ""],
90+
];
91+
92+
[TestMethod]
93+
[DynamicData(nameof(ConstructorCodepointListWithRangeTestData))]
94+
public void Constructor_CodepointList_WithRange(string testString, int startIndex, int count, string expectedResult)
95+
{
96+
UString temp = new(testString);
97+
List<UCodepoint> codepoints = temp.ToList();
98+
99+
UString us = new(codepoints, startIndex, count);
100+
Assert.AreEqual(new UString(expectedResult), us);
101+
}
35102
#endregion
36-
103+
37104
#region CharLength tests
38105
private static IEnumerable<object[]> CharLengthTestData =>
39106
[
@@ -306,6 +373,54 @@ public void IndexOf_UCodepoint(string testString, UCodepoint codePoint, int star
306373
}
307374
#endregion
308375

376+
#region IndexOf_UString tests
377+
private static IEnumerable<object[]> UStringIndexOfExceptionTestData =>
378+
[
379+
["", "a", -1, 0, typeof(ArgumentOutOfRangeException)],
380+
["", "a", 0, 1, typeof(ArgumentException)],
381+
["This", "a", 0, -1, typeof(ArgumentOutOfRangeException)],
382+
["This", "a", -1, 2, typeof(ArgumentOutOfRangeException)],
383+
["This", "a", 4, 1, typeof(ArgumentException)],
384+
["This", "a", 0, 5, typeof(ArgumentException)],
385+
];
386+
387+
[TestMethod]
388+
[DynamicData(nameof(UStringIndexOfExceptionTestData))]
389+
public void IndexOf_UString_InvalidParameters(string testString, string value, int startIndex, int count,
390+
Type expectedExceptionType)
391+
{
392+
UString us = new(testString);
393+
UString searchValue = new(value);
394+
Assert.That.ThrowsException(expectedExceptionType, () => us.IndexOf(searchValue, startIndex, count));
395+
}
396+
397+
private static IEnumerable<object[]> UStringIndexOfTestData =>
398+
[
399+
["This is a test!", "is", 0, 15, 2],
400+
["This is a test!", "is", 3, 12, 5],
401+
["This is a test!", "test", 0, 15, 10],
402+
["This is a test!", "xyz", 0, 15, -1],
403+
["This is a test!", "This is a test!", 0, 15, 0],
404+
["This is a test!", "is", 6, 9, -1],
405+
["العربية", "رب", 0, 7, 3],
406+
["😁🤔😮", "🤔😮", 0, 3, 1],
407+
["😁🤔😮", "😁", 1, 2, -1],
408+
];
409+
410+
[TestMethod]
411+
[DynamicData(nameof(UStringIndexOfTestData))]
412+
public void IndexOf_UString(string testString, string value, int startIndex, int count, int expectedResult)
413+
{
414+
UString us = new(testString);
415+
UString searchValue = new(value);
416+
Assert.AreEqual(expectedResult, us.IndexOf(searchValue, startIndex, count));
417+
418+
// Test making sure that a substring results in the correct result
419+
us = CreateTestSubstring(testString);
420+
Assert.AreEqual(expectedResult, us.IndexOf(searchValue, startIndex, count));
421+
}
422+
#endregion
423+
309424
#region LastIndexOf_UCodepoint tests
310425
private static IEnumerable<object[]> UCodepointLastIndexOfExceptionTestData =>
311426
[
@@ -357,7 +472,52 @@ public void LastIndexOf_UCodepoint(string testString, UCodepoint codePoint,
357472
}
358473
#endregion
359474

360-
#region StartsWith_UCodepoint tests
475+
#region LastIndexOf_UString tests
476+
private static IEnumerable<object[]> UStringLastIndexOfExceptionTestData =>
477+
[
478+
["", "a", 0, 0, typeof(ArgumentOutOfRangeException)],
479+
["This", "a", -1, 2, typeof(ArgumentOutOfRangeException)],
480+
["This", "a", 4, 1, typeof(ArgumentOutOfRangeException)],
481+
["This", "a", 3, -1, typeof(ArgumentOutOfRangeException)],
482+
["This", "a", 3, 5, typeof(ArgumentException)],
483+
];
484+
485+
[TestMethod]
486+
[DynamicData(nameof(UStringLastIndexOfExceptionTestData))]
487+
public void LastIndexOf_UString_InvalidParameters(string testString, string value, int startIndex, int count,
488+
Type expectedExceptionType)
489+
{
490+
UString us = new(testString);
491+
UString searchValue = new(value);
492+
Assert.That.ThrowsException(expectedExceptionType, () => us.LastIndexOf(searchValue, startIndex, count));
493+
}
494+
495+
private static IEnumerable<object[]> UStringLastIndexOfTestData =>
496+
[
497+
["This is a test!", "is", 14, 15, 5],
498+
["This is a test!", "is", 4, 5, 2],
499+
["This is a test!", "xyz", 14, 15, -1],
500+
["This is a test!", "This", 14, 15, 0],
501+
["العربية", "رب", 6, 7, 3],
502+
["😁🤔😮", "🤔", 2, 3, 1],
503+
["😁🤔😮", "😮", 1, 2, -1],
504+
];
505+
506+
[TestMethod]
507+
[DynamicData(nameof(UStringLastIndexOfTestData))]
508+
public void LastIndexOf_UString(string testString, string value, int startIndex, int count, int expectedResult)
509+
{
510+
UString us = new(testString);
511+
UString searchValue = new(value);
512+
Assert.AreEqual(expectedResult, us.LastIndexOf(searchValue, startIndex, count));
513+
514+
// Test making sure that a substring results in the correct result
515+
us = CreateTestSubstring(testString);
516+
Assert.AreEqual(expectedResult, us.LastIndexOf(searchValue, startIndex, count));
517+
}
518+
#endregion
519+
520+
#region StartsWith_UCodepoint tests
361521
private static IEnumerable<object[]> UCodepointStartsWithTestData =>
362522
[
363523
["", (UCodepoint)'A', true, false],
@@ -387,6 +547,39 @@ public void StartsWith_UCodepoint(string testString, UCodepoint codePoint, bool
387547
}
388548
#endregion
389549

550+
#region StartsWith_UString tests
551+
private static IEnumerable<object[]> UStringStartsWithTestData =>
552+
[
553+
["", "a", false, false],
554+
["", "", false, true],
555+
["This is a test!", "This", false, true],
556+
["This is a test!", "this", false, false],
557+
["This is a test!", "this", true, true],
558+
["This is a test!", "test!", false, false],
559+
["This is a test!", "This is a test!", false, true],
560+
["العربية", "العر", false, true],
561+
["العربية", "ية", false, false],
562+
["😁🤔😮", "😁🤔", false, true],
563+
["😁🤔😮", "🤔😮", false, false],
564+
["\U00010570\U00010597", "\U00010570", false, true], // VITHKUQI letters
565+
["\U00010570\U00010597", "\U00010597", false, false], // VITHKUQI letters
566+
["\U00010570\U00010597", "\U00010597", true, true], // VITHKUQI case-insensitive
567+
];
568+
569+
[TestMethod]
570+
[DynamicData(nameof(UStringStartsWithTestData))]
571+
public void StartsWith_UString(string testString, string value, bool ignoreCase, bool expectedResult)
572+
{
573+
UString us = new(testString);
574+
UString searchValue = new(value);
575+
Assert.AreEqual(expectedResult, us.StartsWith(searchValue, ignoreCase));
576+
577+
// Test making sure that a substring results in the correct result
578+
us = CreateTestSubstring(testString);
579+
Assert.AreEqual(expectedResult, us.StartsWith(searchValue, ignoreCase));
580+
}
581+
#endregion
582+
390583
#region EndsWith_UCodepoint tests
391584
private static IEnumerable<object[]> UCodepointEndsWithTestData =>
392585
[
@@ -417,6 +610,103 @@ public void EndsWith_UCodepoint(string testString, UCodepoint codePoint, bool ig
417610
}
418611
#endregion
419612

613+
#region EndsWith_UString tests
614+
private static IEnumerable<object[]> UStringEndsWithTestData =>
615+
[
616+
["", "a", false, false],
617+
["", "", false, true],
618+
["This is a test!", "test!", false, true],
619+
["This is a test!", "TEST!", false, false],
620+
["This is a test!", "TEST!", true, true],
621+
["This is a test!", "This", false, false],
622+
["This is a test!", "This is a test!", false, true],
623+
["العربية", "بية", false, true],
624+
["العربية", "العر", false, false],
625+
["😁🤔😮", "🤔😮", false, true],
626+
["😁🤔😮", "😁🤔", false, false],
627+
["\U00010570\U00010597", "\U00010597", false, true], // VITHKUQI letters
628+
["\U00010570\U00010597", "\U00010570", false, false], // VITHKUQI letters
629+
["\U00010570\U00010597", "\U00010570", true, true], // VITHKUQI case-insensitive
630+
];
631+
632+
[TestMethod]
633+
[DynamicData(nameof(UStringEndsWithTestData))]
634+
public void EndsWith_UString(string testString, string value, bool ignoreCase, bool expectedResult)
635+
{
636+
UString us = new(testString);
637+
UString searchValue = new(value);
638+
Assert.AreEqual(expectedResult, us.EndsWith(searchValue, ignoreCase));
639+
640+
// Test making sure that a substring results in the correct result
641+
us = CreateTestSubstring(testString);
642+
Assert.AreEqual(expectedResult, us.EndsWith(searchValue, ignoreCase));
643+
}
644+
#endregion
645+
646+
#region Contains_UCodepoint tests
647+
private static IEnumerable<object[]> UCodepointContainsTestData =>
648+
[
649+
["", (UCodepoint)'a', false, false],
650+
["This is a test!", (UCodepoint)'i', false, true],
651+
["This is a test!", (UCodepoint)'z', false, false],
652+
["This is a test!", (UCodepoint)'T', false, true],
653+
["this is a test!", (UCodepoint)'T', false, false],
654+
["this is a test!", (UCodepoint)'T', true, true],
655+
["This is a test!", (UCodepoint)'t', true, true],
656+
["العربية", (UCodepoint)'ر', false, true],
657+
["العربية", (UCodepoint)'ص', false, false],
658+
["😁🤔😮", UCodepoint.ReadFromStr("🤔", 0), false, true],
659+
["😁🤔😮", UCodepoint.ReadFromStr("🎉", 0), false, false],
660+
["\U00010570\U00010597", UCodepoint.ReadFromStr("\U00010570", 0), false, true], // VITHKUQI letters
661+
["\U00010570\U00010597", UCodepoint.ReadFromStr("\U00010597", 0), true, true], // VITHKUQI case-insensitive
662+
];
663+
664+
[TestMethod]
665+
[DynamicData(nameof(UCodepointContainsTestData))]
666+
public void Contains_UCodepoint(string testString, UCodepoint codePoint, bool ignoreCase, bool expectedResult)
667+
{
668+
UString us = new(testString);
669+
Assert.AreEqual(expectedResult, us.Contains(codePoint, ignoreCase));
670+
671+
// Test making sure that a substring results in the correct result
672+
us = CreateTestSubstring(testString);
673+
Assert.AreEqual(expectedResult, us.Contains(codePoint, ignoreCase));
674+
}
675+
#endregion
676+
677+
#region Contains_UString tests
678+
private static IEnumerable<object[]> UStringContainsTestData =>
679+
[
680+
["", "a", false, false],
681+
["", "", false, true],
682+
["This is a test!", "is", false, true],
683+
["This is a test!", "xyz", false, false],
684+
["This is a test!", "IS", false, false],
685+
["This is a test!", "IS", true, true],
686+
["This is a test!", "This is a test!", false, true],
687+
["العربية", "رب", false, true],
688+
["العربية", "صف", false, false],
689+
["😁🤔😮", "🤔😮", false, true],
690+
["😁🤔😮", "😮😁", false, false],
691+
["\U00010570\U00010597", "\U00010597", false, true], // VITHKUQI letters
692+
["\U00010570\U00010597", "\U00010570\U00010570", false, false], // VITHKUQI letters
693+
["\U00010570\U00010597", "\U00010570\U00010570", true, true], // VITHKUQI case-insensitive
694+
];
695+
696+
[TestMethod]
697+
[DynamicData(nameof(UStringContainsTestData))]
698+
public void Contains_UString(string testString, string value, bool ignoreCase, bool expectedResult)
699+
{
700+
UString us = new(testString);
701+
UString searchValue = new(value);
702+
Assert.AreEqual(expectedResult, us.Contains(searchValue, ignoreCase));
703+
704+
// Test making sure that a substring results in the correct result
705+
us = CreateTestSubstring(testString);
706+
Assert.AreEqual(expectedResult, us.Contains(searchValue, ignoreCase));
707+
}
708+
#endregion
709+
420710
#region IsNullOrEmpty tests
421711
private static IEnumerable<object?[]> IsNullOrEmptyTestData =>
422712
[

UnicodeHelper.Tests/UnicodeHelper.Tests.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
</ItemGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
22+
<PackageReference Include="JetBrains.Annotations" Version="2025.2.4" />
2323
<PackageReference Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
24-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
25-
<PackageReference Include="MSTest.TestAdapter" Version="3.11.0" />
26-
<PackageReference Include="MSTest.TestFramework" Version="3.11.0" />
24+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
25+
<PackageReference Include="MSTest.TestAdapter" Version="4.1.0" />
26+
<PackageReference Include="MSTest.TestFramework" Version="4.1.0" />
2727
</ItemGroup>
2828

2929
<ItemGroup>

0 commit comments

Comments
 (0)