diff --git a/.gitignore b/.gitignore index ea0701b..d777e3b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ /packages/ riderModule.iml /_ReSharper.Caches/ -.idea \ No newline at end of file +.idea +# BenchmarkDotNet output +BenchmarkDotNet.Artifacts/ diff --git a/Benchmarks/Benchmark.cs b/Benchmarks/Benchmark.cs index 7db10cd..d970a85 100644 --- a/Benchmarks/Benchmark.cs +++ b/Benchmarks/Benchmark.cs @@ -7,15 +7,14 @@ BenchmarkRunner.Run( ManualConfig.Create(DefaultConfig.Instance.WithOptions(ConfigOptions.DisableOptimizationsValidator))); - -namespace Benchmark { - +namespace Benchmark +{ /// /// Contains benchmarks for the StringExtension methods. /// [MemoryDiagnoser(false)] - public class StringExtensionBenchmark { - + public class StringExtensionBenchmark + { private readonly string input = "hello world!"; private readonly char[] charactersToRemove = { 'l', 'o' }; private readonly string substring = "l"; @@ -26,7 +25,8 @@ public class StringExtensionBenchmark { /// Benchmark for the RemoveCharacters method. /// [Benchmark] - public string RemoveCharacters() { + public string RemoveCharacters() + { return input.RemoveCharacters(charactersToRemove); } @@ -34,7 +34,8 @@ public string RemoveCharacters() { /// Benchmark for the IsValidEmail method. /// [Benchmark] - public bool IsValidEmail() { + public bool IsValidEmail() + { return email.IsValidEmail(); } @@ -42,7 +43,8 @@ public bool IsValidEmail() { /// Benchmark for the IsValidPhoneNumber method. /// [Benchmark] - public bool IsValidPhoneNumber() { + public bool IsValidPhoneNumber() + { return phoneNumber.IsValidPhoneNumber(); } @@ -50,7 +52,8 @@ public bool IsValidPhoneNumber() { /// Benchmark for the CountSubstring method. /// [Benchmark] - public int CountSubstring() { + public int CountSubstring() + { return input.CountSubstring(substring); } @@ -58,7 +61,8 @@ public int CountSubstring() { /// Benchmark for the ReverseWords method. /// [Benchmark] - public string ReverseWords() { + public string ReverseWords() + { return input.ReverseWords(); } @@ -66,7 +70,8 @@ public string ReverseWords() { /// Benchmark for the IsPalindrome method. /// [Benchmark] - public bool IsPalindrome() { + public bool IsPalindrome() + { return input.IsPalindrome(); } @@ -74,7 +79,8 @@ public bool IsPalindrome() { /// Benchmark for the CountLetters method. /// [Benchmark] - public int CountLetters() { + public int CountLetters() + { return input.CountLetters(); } @@ -82,7 +88,8 @@ public int CountLetters() { /// Benchmark for the RemoveDuplicateCharacters method. /// [Benchmark] - public string RemoveDuplicateCharacters() { + public string RemoveDuplicateCharacters() + { return input.RemoveDuplicateCharacters(); } @@ -90,8 +97,9 @@ public string RemoveDuplicateCharacters() { /// Benchmark for the ConvertToCamelCase method. /// [Benchmark] - public string ConvertToCamelCase() { + public string ConvertToCamelCase() + { return input.ToCamelCase(); } } -} +} \ No newline at end of file diff --git a/README.md b/README.md index bb8f075..04e40da 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,11 @@ Install [StringExtension](https://www.nuget.org/packages/String-Extension/) usin ## Running Tests - - The unit tests are [here](https://github.com/Antyss77/StringExtension/blob/master/UnitTests/UnitTest.cs) + - The unit tests are [here](https://github.com/Antyss77/StringExtension/blob/master/UnitTests/StringExtensionTests.cs) - The performance tests are [here](https://github.com/Antyss77/StringExtension/blob/master/Benchmarks/Benchmark.cs) ## Licence Distributed under the MIT License. See [LICENCE](https://github.com/Antyss77/StringExtension/blob/master/LICENSE) for more information. ## Contributing -You can contribute to the project in several ways: by proposing new ideas, reporting bugs you encounter, or improving existing code. If you would like to submit a change, you can open a Pull Request so that we can review your changes and incorporate them into the project if necessary. We welcome your participation and look forward to moving this string library project forward. - - +You can contribute to the project in several ways: by proposing new ideas, reporting bugs you encounter, or improving existing code. If you would like to submit a change, you can open a Pull Request so that we can review your changes and incorporate them into the project if necessary. We welcome your participation and look forward to moving this string library project forward. \ No newline at end of file diff --git a/StringExtension.sln b/StringExtension.sln deleted file mode 100644 index 4cf0126..0000000 --- a/StringExtension.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringExtension", "StringExtension\StringExtension.csproj", "{6368280F-4A55-46C1-90AD-76D2991256F9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{CC98F382-B0C8-4D9E-B301-4ACFE17C9BBE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{F36DB991-3DEC-4C29-BE9F-2C1E122608AD}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6368280F-4A55-46C1-90AD-76D2991256F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6368280F-4A55-46C1-90AD-76D2991256F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6368280F-4A55-46C1-90AD-76D2991256F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6368280F-4A55-46C1-90AD-76D2991256F9}.Release|Any CPU.Build.0 = Release|Any CPU - {CC98F382-B0C8-4D9E-B301-4ACFE17C9BBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CC98F382-B0C8-4D9E-B301-4ACFE17C9BBE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CC98F382-B0C8-4D9E-B301-4ACFE17C9BBE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CC98F382-B0C8-4D9E-B301-4ACFE17C9BBE}.Release|Any CPU.Build.0 = Release|Any CPU - {F36DB991-3DEC-4C29-BE9F-2C1E122608AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F36DB991-3DEC-4C29-BE9F-2C1E122608AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F36DB991-3DEC-4C29-BE9F-2C1E122608AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F36DB991-3DEC-4C29-BE9F-2C1E122608AD}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/StringExtension.slnx b/StringExtension.slnx new file mode 100644 index 0000000..0625b15 --- /dev/null +++ b/StringExtension.slnx @@ -0,0 +1,5 @@ + + + + + diff --git a/StringExtension/StringExtension.cs b/StringExtension/StringExtension.cs index 1ef7da7..a5cb5d0 100644 --- a/StringExtension/StringExtension.cs +++ b/StringExtension/StringExtension.cs @@ -1,206 +1,357 @@ -using System.Text; -using System.Text.RegularExpressions; - -namespace StringExtension { - /// - /// Provides extension methods for string manipulation. - /// - public static partial class StringExtension { - - /// - /// Represents a regular expression that can be used to validate an email address. - /// - /// A regular expression that can be used to validate an email address. - [GeneratedRegex(@"[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+")] - - private static partial Regex MailAddressRegex(); - - /// - /// Represents a regular expression that can be used to validate a phone number. - /// - /// A regular expression that can be used to validate a phone number. - [GeneratedRegex(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")] - private static partial Regex PhoneNumberRegex(); - - /// - /// Removes specified characters from the given string. - /// - /// The input string. - /// An array of characters to remove. - /// A new string with specified characters removed. - public static string RemoveCharacters(this string input, char[] charactersToRemove) { - if (string.IsNullOrEmpty(input) || charactersToRemove is null || charactersToRemove.Length == 0) - { - return input; - } +using System.Text.RegularExpressions; - return new string(input.Where(c => !charactersToRemove.Contains(c)).ToArray()); - } +namespace StringExtension; - /// - /// Validates the given email address. - /// - /// The email address to validate. - /// true if the given email address is valid; otherwise, false. - public static bool IsValidEmail(this string email) { - return !string.IsNullOrEmpty(email) && MailAddressRegex().IsMatch(email); +/// +/// Provides extension methods for string manipulation. +/// +public static partial class StringExtension +{ + /// + /// Represents a regular expression that can be used to validate an email address. + /// + /// A regular expression that can be used to validate an email address. + [GeneratedRegex(@"[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+")] + private static partial Regex MailAddressRegex(); + + /// + /// Represents a regular expression that can be used to validate a phone number. + /// + /// A regular expression that can be used to validate a phone number. + [GeneratedRegex(@"^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")] + private static partial Regex PhoneNumberRegex(); + + /// + /// Removes specified characters from the given string. + /// + /// The input string. + /// An array of characters to remove. + /// A new string with specified characters removed. + public static string RemoveCharacters(this string input, char[] charactersToRemove) + { + if (string.IsNullOrEmpty(input) || charactersToRemove is null || charactersToRemove.Length == 0) + { + return input; } - /// - /// Validates the given phone number. - /// - /// The phone number to validate. - /// true if the given phone number is valid; otherwise, false. - public static bool IsValidPhoneNumber(this string phoneNumber) { - return !string.IsNullOrEmpty(phoneNumber) && PhoneNumberRegex().IsMatch(phoneNumber); + return RemoveCharacters(input.AsSpan(), charactersToRemove); + } + + /// + /// Removes specified characters from the given span of characters. + /// + /// The input characters. + /// The characters to remove. + /// A new string with specified characters removed. + public static string RemoveCharacters(this ReadOnlySpan input, ReadOnlySpan charactersToRemove) + { + if (input.IsEmpty || charactersToRemove.IsEmpty) + { + return input.ToString(); } - /// - /// Counts the number of occurrences of a substring in the given string. - /// - /// The input string. - /// The substring to count. - /// The number of occurrences of the substring in the input string. - public static int CountSubstring(this string input, string substring) { - if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(substring)) + Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length]; + var count = 0; + + foreach (var c in input) + { + if (charactersToRemove.IndexOf(c) < 0) { - return 0; + buffer[count++] = c; } + } - return (input.Length - input.Replace(substring, "").Length) / substring.Length; + return new string(buffer[..count]); + } + + /// + /// Validates the given email address. + /// + /// The email address to validate. + /// true if the given email address is valid; otherwise, false. + public static bool IsValidEmail(this string email) + { + return !string.IsNullOrEmpty(email) && MailAddressRegex().IsMatch(email); + } + + /// + /// Validates the given email address. + /// + /// The email address to validate. + /// true if the given email address is valid; otherwise, false. + public static bool IsValidEmail(this ReadOnlySpan email) + { + return !email.IsEmpty && MailAddressRegex().IsMatch(email); + } + + /// + /// Validates the given phone number. + /// + /// The phone number to validate. + /// true if the given phone number is valid; otherwise, false. + public static bool IsValidPhoneNumber(this string phoneNumber) + { + return !string.IsNullOrEmpty(phoneNumber) && PhoneNumberRegex().IsMatch(phoneNumber); + } + + /// + /// Validates the given phone number. + /// + /// The phone number to validate. + /// true if the given phone number is valid; otherwise, false. + public static bool IsValidPhoneNumber(this ReadOnlySpan phoneNumber) + { + return !phoneNumber.IsEmpty && PhoneNumberRegex().IsMatch(phoneNumber); + } + + /// + /// Counts the number of occurrences of a substring in the given string. + /// + /// The input string. + /// The substring to count. + /// The number of occurrences of the substring in the input string. + public static int CountSubstring(this string input, string substring) + { + if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(substring)) + { + return 0; } - /// - /// Reverses the order of words in the given string. - /// - /// The input to reverse words. - /// The input string with the order of words reversed. - public static string ReverseWords(this string input) { - if (string.IsNullOrEmpty(input)) - { - return input; - } + return CountSubstring(input.AsSpan(), substring.AsSpan()); + } - var span = input.AsSpan(); + /// + /// Counts the number of occurrences of a substring in the given span of characters. + /// Zero-allocation: delegates directly to . + /// + /// The input characters. + /// The characters to count. + /// The number of occurrences of the substring in the input. + public static int CountSubstring(this ReadOnlySpan input, ReadOnlySpan substring) + { + if (input.IsEmpty || substring.IsEmpty) + { + return 0; + } - var builder = new StringBuilder(); + return input.Count(substring); + } - var start = span.Length; + /// + /// Reverses the order of words in the given string. + /// + /// The input to reverse words. + /// The input string with the order of words reversed. + public static string ReverseWords(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } - for (var i = span.Length - 1; i >= 0; i--) + // string.Create needs the length up front, and it is preserved by a + // simple word swap around single-space separators. + return string.Create(input.Length, input, static (span, source) => + { + foreach (Range word in source.AsSpan().Split(' ')) { - if (span[i] is not ' ') - continue; + ReadOnlySpan wordSpan = source.AsSpan()[word]; + var length = wordSpan.Length; - builder - .Append(span.Slice(i + 1, start - i - 1)) - .Append(' '); + // Copy the word to the end of the remaining span. + wordSpan.TryCopyTo(span[^length..]); + span = span[..^length]; - start = i; + // Insert a separating space, unless this was the last word. + if (!span.IsEmpty) + { + span[^1] = ' '; + span = span[..^1]; + } } + }); + } - builder.Append(span[..start]); + /// + /// Determines if the given string is a palindrome. + /// + /// The input string. + /// true if the string is a palindrome; otherwise, false. + public static bool IsPalindrome(this string input) + { + return !string.IsNullOrEmpty(input) && IsPalindrome(input.AsSpan()); + } - return builder.ToString(); + /// + /// Determines if the given span of characters is a palindrome. + /// Zero-allocation: compares from both ends inward, skipping non-letters. + /// + /// The input characters. + /// true if the characters form a palindrome; otherwise, false. + public static bool IsPalindrome(this ReadOnlySpan input) + { + if (input.IsEmpty) + { + return false; } - /// - /// Determines if the given string is a palindrome. - /// - /// The input string. - /// true if the string is a palindrome; otherwise, false. - public static bool IsPalindrome(this string input) { - if (string.IsNullOrEmpty(input)) + var left = 0; + var right = input.Length - 1; + + while (left < right) + { + if (!char.IsLetter(input[left])) + { + left++; + continue; + } + + if (!char.IsLetter(input[right])) + { + right--; + continue; + } + + if (char.ToLowerInvariant(input[left]) != char.ToLowerInvariant(input[right])) { return false; } - // Remove all non-letter characters and convert to lowercase - string cleanString = new string(input.Where(char.IsLetter).Select(char.ToLowerInvariant).ToArray()); + left++; + right--; + } - // Check if the string is equal to its reverse - return cleanString == new string(cleanString.Reverse().ToArray()); + return true; + } + + /// + /// Counts the number of letters in the given string. + /// + /// The input string. + /// The number of letters in the input string. + public static int CountLetters(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return 0; } - /// - /// Counts the number of letters in the given string. - /// - /// The input string. - /// The number of letters in the input string. - public static int CountLetters(this string input) { - if (string.IsNullOrEmpty(input)) + return CountLetters(input.AsSpan()); + } + + /// + /// Counts the number of letters in the given span of characters. + /// + /// The input characters. + /// The number of letters in the input. + public static int CountLetters(this ReadOnlySpan input) + { + var count = 0; + foreach (var c in input) + { + if (char.IsLetter(c)) { - return 0; + count++; } + } + + return count; + } + + /// + /// Removes duplicate characters from the given string. + /// + /// The input string. + /// A new string with duplicate characters removed. + public static string RemoveDuplicateCharacters(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } + + return RemoveDuplicateCharacters(input.AsSpan()); + } - // Remove all non-letter characters and count the length of the resulting string - return input.Count(char.IsLetter); + /// + /// Removes duplicate characters from the given span of characters, preserving + /// the order of first occurrence. + /// + /// The input characters. + /// A new string with duplicate characters removed. + public static string RemoveDuplicateCharacters(this ReadOnlySpan input) + { + if (input.IsEmpty) + { + return string.Empty; } - /// - /// Removes duplicate characters from the given string. - /// - /// The input string. - /// A new string with duplicate characters removed. - public static string RemoveDuplicateCharacters(this string input) { - if (string.IsNullOrEmpty(input)) + Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length]; + var seen = new HashSet(input.Length); + var count = 0; + + foreach (var c in input) + { + if (seen.Add(c)) { - return input; + buffer[count++] = c; } + } - // Convert input to char array and remove duplicates - var chars = input.ToCharArray().Distinct().ToArray(); + return new string(buffer[..count]); + } - // Convert char array back to string - return new string(chars); + /// + /// Converts the given string to camel case. + /// + /// The input string. + /// The input string converted to camel case. + public static string ToCamelCase(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; } - /// - /// Converts the given string to camel case. - /// - /// The input string. - /// The input string converted to camel case. - public static string ToCamelCase(this string input) { - if (string.IsNullOrEmpty(input)) - { - return string.Empty; - } + return ToCamelCase(input.AsSpan()); + } - var span = input.AsSpan(); + /// + /// Converts the given span of characters to camel case. + /// + /// The input characters. + /// The input converted to camel case. + public static string ToCamelCase(this ReadOnlySpan input) + { + if (input.IsEmpty) + { + return string.Empty; + } - Span output = stackalloc char[input.Length]; + Span buffer = input.Length <= 256 ? stackalloc char[input.Length] : new char[input.Length]; - var outputIndex = 0; - var shouldCapitalize = false; + var count = 0; + var shouldCapitalize = false; - foreach (var c in span) + foreach (var c in input) + { + if (char.IsWhiteSpace(c) || c == '_') { - if (char.IsWhiteSpace(c) || c == '_') - { - shouldCapitalize = true; - } - else - { - if (shouldCapitalize) - { - output[outputIndex++] = char.ToUpperInvariant(c); - shouldCapitalize = false; - } - else - { - output[outputIndex++] = char.ToLowerInvariant(c); - } - } + shouldCapitalize = true; + continue; } - // Ensure the very first character is always lowercase, even if the - // input starts with a separator (e.g. "_hello" should give "hello", not "Hello"). - if (outputIndex > 0) - { - output[0] = char.ToLowerInvariant(output[0]); - } + buffer[count++] = shouldCapitalize ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c); + shouldCapitalize = false; + } - return new string(output[..outputIndex]); + // The very first character is always lowercase, even if the input + // started with a separator (e.g. "_hello" gives "hello", not "Hello"). + if (count > 0) + { + buffer[0] = char.ToLowerInvariant(buffer[0]); } + + return new string(buffer[..count]); } } \ No newline at end of file diff --git a/UnitTests/StringExtensionTests.cs b/UnitTests/StringExtensionTests.cs new file mode 100644 index 0000000..e26bcd1 --- /dev/null +++ b/UnitTests/StringExtensionTests.cs @@ -0,0 +1,187 @@ +using StringExtension; + +namespace UnitTests; + +/// +/// Contains unit tests for the StringExtension methods. +/// +public class StringExtensionTests +{ + /// + /// Tests the RemoveCharacters method. + /// + [Test] + public void TestRemoveCharacters() + { + string input = "hello world!"; + char[] charactersToRemove = { 'l', 'o' }; + string expected = "he wrd!"; + string result = input.RemoveCharacters(charactersToRemove); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests that RemoveCharacters handles a null input gracefully. + /// + [Test] + public void TestRemoveCharacters_NullInput() + { + string input = null!; + char[] charactersToRemove = { 'l', 'o' }; + string result = input.RemoveCharacters(charactersToRemove); + Assert.That(result, Is.Null); + } + + /// + /// Tests the ReadOnlySpan<char> overload of RemoveCharacters. + /// + [Test] + public void TestRemoveCharacters_Span() + { + ReadOnlySpan input = "hello world!"; + ReadOnlySpan charactersToRemove = "lo"; + string expected = "he wrd!"; + string result = input.RemoveCharacters(charactersToRemove); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests the IsValidEmail method. + /// + [Test] + public void TestIsValidEmail() + { + string email = "john.doe@example.com"; + bool result = email.IsValidEmail(); + Assert.That(result, Is.True); + } + + /// + /// Tests the IsValidPhoneNumber method. + /// + [Test] + public void TestIsValidPhoneNumber() + { + string phoneNumber = "555-555-5555"; + bool result = phoneNumber.IsValidPhoneNumber(); + Assert.That(result, Is.True); + } + + /// + /// Tests the CountSubstring method. + /// + [Test] + public void TestCountSubstring() + { + string input = "hello world!"; + string substring = "l"; + int expected = 3; + int result = input.CountSubstring(substring); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests the ReverseWords method. + /// + [Test] + public void TestReverseWords() + { + string input = "hello world!"; + string expected = "world! hello"; + string result = input.ReverseWords(); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests that ReverseWords handles a null input gracefully. + /// + [Test] + public void TestReverseWords_NullInput() + { + string input = null!; + string result = input.ReverseWords(); + Assert.That(result, Is.Null); + } + + /// + /// Tests the IsPalindrome method. + /// + [Test] + public void TestIsPalindrome() + { + string input = "racecar"; + bool result = input.IsPalindrome(); + Assert.That(result, Is.True); + } + + /// + /// Tests that IsPalindrome handles a null input gracefully. + /// + [Test] + public void TestIsPalindrome_NullInput() + { + string input = null!; + bool result = input.IsPalindrome(); + Assert.That(result, Is.False); + } + + /// + /// Tests the CountLetters method. + /// + [Test] + public void TestCountLetters() + { + string input = "hello world!"; + int expected = 10; + int result = input.CountLetters(); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests that CountLetters handles a null input gracefully. + /// + [Test] + public void TestCountLetters_NullInput() + { + string input = null!; + int result = input.CountLetters(); + Assert.That(result, Is.EqualTo(0)); + } + + /// + /// Tests the RemoveDuplicateCharacters method. + /// + [Test] + public void TestRemoveDuplicateCharacters() + { + string input = "hello world!"; + string expected = "helo wrd!"; + string result = input.RemoveDuplicateCharacters(); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests the ToCamelCase method. + /// + [Test] + public void TestConvertToCamelCase() + { + string input = "hello_world"; + string expected = "helloWorld"; + string result = input.ToCamelCase(); + Assert.That(result, Is.EqualTo(expected)); + } + + /// + /// Tests that ToCamelCase correctly lowercases the first character + /// even when the input starts with a separator. + /// + [Test] + public void TestConvertToCamelCase_LeadingSeparator() + { + string input = "_hello_world"; + string expected = "helloWorld"; + string result = input.ToCamelCase(); + Assert.That(result, Is.EqualTo(expected)); + } +} \ No newline at end of file diff --git a/UnitTests/UnitTest.cs b/UnitTests/UnitTest.cs deleted file mode 100644 index a8fce4b..0000000 --- a/UnitTests/UnitTest.cs +++ /dev/null @@ -1,174 +0,0 @@ -using StringExtension; - -namespace UnitTests; - -/// -/// Contains unit tests for the StringExtension methods. -/// -public class Tests { - [SetUp] - public void Setup() { - } - - public class UnitTest { - /// - /// Tests the RemoveCharacters method. - /// - [Test] - public void TestRemoveCharacters() { - string input = "hello world!"; - char[] charactersToRemove = {'l', 'o'}; - string expected = "he wrd!"; - string result = input.RemoveCharacters(charactersToRemove); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests that RemoveCharacters handles a null input gracefully. - /// - [Test] - public void TestRemoveCharacters_NullInput() { - string input = null; - char[] charactersToRemove = {'l', 'o'}; - string result = input.RemoveCharacters(charactersToRemove); - Assert.That(result, Is.Null); - } - - /// - /// Tests the IsValidEmail method. - /// - [Test] - public void TestIsValidEmail() { - string email = "john.doe@example.com"; - bool result = email.IsValidEmail(); - Assert.IsTrue(result); - // Test code and assertions explained in code. - } - - /// - /// Tests the IsValidPhoneNumber method. - /// - [Test] - public void TestIsValidPhoneNumber() { - string phoneNumber = "555-555-5555"; - bool result = phoneNumber.IsValidPhoneNumber(); - Assert.IsTrue(result); - // Test code and assertions explained in code. - } - - /// - /// Tests the CountSubstring method. - /// - [Test] - public void TestCountSubstring() { - string input = "hello world!"; - string substring = "l"; - int expected = 3; - int result = input.CountSubstring(substring); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests the ReverseWords method. - /// - [Test] - public void TestReverseWords() { - string input = "hello world!"; - string expected = "world! hello"; - string result = input.ReverseWords(); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests that ReverseWords handles a null input gracefully. - /// - [Test] - public void TestReverseWords_NullInput() { - string input = null; - string result = input.ReverseWords(); - Assert.That(result, Is.Null); - } - - /// - /// Tests the IsPalindrome method. - /// - [Test] - public void TestIsPalindrome() { - string input = "racecar"; - bool result = input.IsPalindrome(); - Assert.IsTrue(result); - // Test code and assertions explained in code. - } - - /// - /// Tests that IsPalindrome handles a null input gracefully. - /// - [Test] - public void TestIsPalindrome_NullInput() { - string input = null; - bool result = input.IsPalindrome(); - Assert.IsFalse(result); - } - - /// - /// Tests the CountLetters method. - /// - [Test] - public void TestCountLetters() { - string input = "hello world!"; - int expected = 10; - int result = input.CountLetters(); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests that CountLetters handles a null input gracefully. - /// - [Test] - public void TestCountLetters_NullInput() { - string input = null; - int result = input.CountLetters(); - Assert.That(result, Is.EqualTo(0)); - } - - /// - /// Tests the RemoveDuplicateCharacters method. - /// - [Test] - public void TestRemoveDuplicateCharacters() { - string input = "hello world!"; - string expected = "helo wrd!"; - string result = input.RemoveDuplicateCharacters(); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests the ToCamelCase method. - /// - [Test] - public void TestConvertToCamelCase() { - string input = "hello_world"; - string expected = "helloWorld"; - string result = input.ToCamelCase(); - Assert.That(result, Is.EqualTo(expected)); - // Test code and assertions explained in code. - } - - /// - /// Tests that ToCamelCase correctly lowercases the first character - /// even when the input starts with a separator. - /// - [Test] - public void TestConvertToCamelCase_LeadingSeparator() { - string input = "_hello_world"; - string expected = "helloWorld"; - string result = input.ToCamelCase(); - Assert.That(result, Is.EqualTo(expected)); - } - } -} \ No newline at end of file