diff --git a/src/CuteUtils.Tests/CuteUtils.Tests.csproj b/src/CuteUtils.Tests/CuteUtils.Tests.csproj index e625376..aacb2fb 100644 --- a/src/CuteUtils.Tests/CuteUtils.Tests.csproj +++ b/src/CuteUtils.Tests/CuteUtils.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net10.0 enable enable @@ -14,9 +14,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/CuteUtils.Tests/Misc/StringExtTests.cs b/src/CuteUtils.Tests/Misc/StringExtTests.cs new file mode 100644 index 0000000..b901a2f --- /dev/null +++ b/src/CuteUtils.Tests/Misc/StringExtTests.cs @@ -0,0 +1,161 @@ +using CuteUtils.Misc; + +namespace CuteUtils.Tests.Misc; + +[TestClass] +public class StringExtTests_Coverage +{ + [TestMethod] + public void ToFileName_EmptyAndNull_ReturnsEmptyOrThrows() + { + Assert.AreEqual("", "".ToFileName()); + Assert.AreEqual("", "".ToFileName(allowSpaces: false)); + + string? nullStr = null; + _ = Assert.ThrowsExactly(() => nullStr!.ToFileName()); + } + + [TestMethod] + public void ToFileName_NormalizesUnicodeAccents() + { + string input = "Ångström Îñţérñåţîöñåļ"; + string output = input.ToFileName(); + + Assert.IsFalse(output.Any(c => c > 127)); // no extended unicode + Assert.Contains("An", output); // Å -> A + n (typical decompose) + } + + [TestMethod] + public void ToFileName_LongString_TrimsProperly() + { + string longInput = new('x', 500); + string result = longInput.ToFileName(); + + Assert.IsLessThanOrEqualTo(255, result.Length, "File name should be trimmed to FS-safe length"); + } + + [TestMethod] + public void ToPath_EmptyAndNull_ReturnsEmptyOrThrows() + { + Assert.AreEqual("", "".ToPath()); + + string? nullStr = null; + _ = Assert.ThrowsExactly(() => nullStr!.ToPath()); + } + + [TestMethod] + public void ToPath_RemovesBothFileAndPathInvalidChars() + { + string input = "t<>e\"s|t/dir\\name:*?"; + string output = input.ToPath(); + + foreach (char c in Path.GetInvalidPathChars()) + { + Assert.DoesNotContain(c, output); + } + } + + [TestMethod] + public void Truncate_HandlesNull_Throws() + { + string? nullStr = null; + _ = Assert.ThrowsExactly(() => nullStr!.Truncate(5)); + } + + [TestMethod] + public void Truncate_LongString_Performance() + { + string longStr = new('x', 10_000); + string result = longStr.Truncate(100); + + Assert.AreEqual(100, result.Length); + } + + [TestMethod] + public void CorrectNewLine_EmptyString_ReturnsEmpty() + { + Assert.AreEqual("", "".CorrectNewLine()); + } + + [TestMethod] + public void CorrectNewLine_Mixed_Newlines_Normalizes() + { + string mixed = "a\r\nb\nc\rd"; + + string result = mixed.CorrectNewLine(); + + if (Environment.OSVersion.Platform == PlatformID.Unix) + { + Assert.AreEqual("a\nb\nc\nd", result); + } + else + { + Assert.AreEqual("a\r\nb\r\nc\r\nd", result); + } + } + + [TestMethod] + public void RemoveWhitespaces_UnicodeSpaces() + { + string input = "a\u2003b\u3000c"; // em-space, ideographic space + Assert.AreEqual("abc", input.RemoveWhitespaces()); + } + + [TestMethod] + public void Reverse_EmptyAndSingleCharacter() + { + Assert.AreEqual("", "".Reverse()); + Assert.AreEqual("a", "a".Reverse()); + } + + [TestMethod] + public void Reverse_UnicodeCombiningCharacters() + { + string input = "e\u0301"; // é composed as e + diacritic + string result = input.Reverse(); + + Assert.AreEqual("\u0301e", result); + } + + [TestMethod] + public void ReplaceCaseInsensitive_UnicodeCase() + { + string input = "Straße"; // Eszett + string replaced = input.ReplaceCaseInsensitive("ß", "ss"); + + Assert.AreEqual("Strasse", replaced); + } + + [TestMethod] + public void ReplaceCaseInsensitive_ReplacesOverlappingCorrectly() + { + string input = "aaaa"; + string replaced = input.ReplaceCaseInsensitive("aa", "b"); + + Assert.AreEqual("bb", replaced); // correct non-overlapping behavior + } + + [TestMethod] + public void RemoveDiacritics_RemovesAccents_PreservesBaseCharacters() + { + // composed character + string caf = "café"; + Assert.AreEqual("cafe", caf.RemoveDiacritics()); + + // another composed example (Å -> A or Ang depending on normalization) + string ang = "Ångström"; + Assert.AreEqual("Angstrom", ang.RemoveDiacritics()); + + // decomposed form: e + combining acute accent + string decomposed = "e\u0301"; // e + ◌́ + Assert.AreEqual("e", decomposed.RemoveDiacritics()); + + // empty string stays empty + Assert.AreEqual(string.Empty, string.Empty.RemoveDiacritics()); + + // null behavior: current implementation will throw NullReferenceException + string? nullStr = null; + _ = Assert.ThrowsExactly(() => nullStr!.RemoveDiacritics()); + } + +} diff --git a/src/CuteUtils.Tests/Misc/WaitTests.cs b/src/CuteUtils.Tests/Misc/WaitTests.cs index 634a7ee..5bbc4ad 100644 --- a/src/CuteUtils.Tests/Misc/WaitTests.cs +++ b/src/CuteUtils.Tests/Misc/WaitTests.cs @@ -36,7 +36,7 @@ public class WaitTests await Task.Delay(200); flag = true; }); - Wait.Until(() => flag, TimeSpan.FromMilliseconds(50), TimeSpan.FromSeconds(2)); + Wait.Until(() => flag, TimeSpan.FromMilliseconds(50)); Assert.IsTrue(flag); } @@ -87,7 +87,7 @@ public class WaitTests handler(42); }); } - int result = await Wait.WaitForEventAsync((Action>)subscribe, TimeSpan.FromSeconds(2)); + int result = await Wait.ForEventAsync((Action>)subscribe, TimeSpan.FromSeconds(2)); Assert.AreEqual(42, result); } @@ -100,7 +100,7 @@ public class WaitTests } _ = await Assert.ThrowsExactlyAsync(async () => { - _ = await Wait.WaitForEventAsync((Action>)subscribe, TimeSpan.FromMilliseconds(200)); + _ = await Wait.ForEventAsync((Action>)subscribe, TimeSpan.FromMilliseconds(200)); }); } @@ -115,7 +115,7 @@ public class WaitTests handler(); }); } - Wait.WaitForEvent(subscribe, TimeSpan.FromSeconds(2)); + Wait.ForEvent(subscribe, TimeSpan.FromSeconds(2)); } [TestMethod] @@ -127,7 +127,7 @@ public class WaitTests } _ = Assert.ThrowsExactly(() => { - Wait.WaitForEvent(subscribe, TimeSpan.FromMilliseconds(200)); + Wait.ForEvent(subscribe, TimeSpan.FromMilliseconds(200)); }); } @@ -142,7 +142,7 @@ public class WaitTests handler("hello"); }); } - string result = Wait.WaitForEvent((Action>)subscribe, TimeSpan.FromSeconds(2)); + string result = Wait.ForEvent((Action>)subscribe, TimeSpan.FromSeconds(2)); Assert.AreEqual("hello", result); } @@ -155,7 +155,7 @@ public class WaitTests } _ = Assert.ThrowsExactly(() => { - _ = Wait.WaitForEvent((Action>)subscribe, TimeSpan.FromMilliseconds(200)); + _ = Wait.ForEvent((Action>)subscribe, TimeSpan.FromMilliseconds(200)); }); } } \ No newline at end of file diff --git a/src/CuteUtils.Tests/Misc/XmlHelperTests.cs b/src/CuteUtils.Tests/Misc/XmlHelperTests.cs new file mode 100644 index 0000000..0b018eb --- /dev/null +++ b/src/CuteUtils.Tests/Misc/XmlHelperTests.cs @@ -0,0 +1,69 @@ +using CuteUtils.Misc; + +namespace CuteUtils.Tests.Misc; + +[TestClass] +public class XmlHelperTests +{ + public class TestModel + { + public string? Name { get; set; } + public int Age { get; set; } + public NestedModel? Child { get; set; } + } + + public class NestedModel + { + public string? Value { get; set; } + } + + [TestMethod] + public void SerializeDeserialize_RoundTrip_RetainsData() + { + TestModel model = new TestModel + { + Name = "Alice", + Age = 30, + Child = new NestedModel { Value = "Inner" } + }; + + string xml = XmlHelper.Serialize(model); + Assert.IsFalse(string.IsNullOrWhiteSpace(xml), "Serialized XML should not be empty."); + Assert.Contains("Alice", xml, "XML should contain the Name value."); + Assert.Contains("30", xml, "XML should contain the Age value."); + Assert.Contains("Inner", xml, "XML should contain the Nested Value."); + + TestModel? deserialized = XmlHelper.Deserialize(xml); + Assert.IsNotNull(deserialized, "Deserialized object should not be null."); + Assert.AreEqual(model.Name, deserialized!.Name); + Assert.AreEqual(model.Age, deserialized.Age); + Assert.AreEqual(model.Child?.Value, deserialized.Child?.Value); + } + + [TestMethod] + public void Serialize_OmitsNamespaceDeclarations() + { + TestModel model = new TestModel { Name = "Bob", Age = 25 }; + string xml = XmlHelper.Serialize(model); + + // Expect no namespace attributes like xmlns or common schema prefixes + Assert.DoesNotContain("xmlns", xml, "XML should not contain namespace declarations."); + Assert.DoesNotContain("xsi:", xml, "XML should not contain xsi prefix."); + Assert.DoesNotContain("xsd:", xml, "XML should not contain xsd prefix."); + } + + [TestMethod] + public void Deserialize_NullXml_ThrowsArgumentNullException() + { + _ = Assert.ThrowsExactly(() => XmlHelper.Deserialize(null!)); + } + + [TestMethod] + public void GetXmlSchema_ReturnsSchemaContainingTypeName() + { + string schema = XmlHelper.GetXmlSchema(); + Assert.IsFalse(string.IsNullOrWhiteSpace(schema), "Schema should not be empty."); + Assert.IsGreaterThanOrEqualTo(0, schema.IndexOf("schema", StringComparison.OrdinalIgnoreCase), "Schema should contain 'schema' element."); + Assert.Contains("TestModel", schema, "Schema should reference the TestModel type."); + } +} \ No newline at end of file diff --git a/src/CuteUtils/CuteUtils.csproj b/src/CuteUtils/CuteUtils.csproj index 6411c64..cb504a6 100644 --- a/src/CuteUtils/CuteUtils.csproj +++ b/src/CuteUtils/CuteUtils.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 enable enable True @@ -29,7 +29,7 @@ - +