-
Notifications
You must be signed in to change notification settings - Fork 0
Add string extension methods #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
3741a6c
Add string extension methods
drebrez 32abc70
Merge branch 'main' into feature/string-extensions
drebrez 44024e4
move to correct location
drebrez ce568d6
Merge branch 'main' into feature/string-extensions
drebrez 8c476d9
Fix
drebrez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 131 additions & 0 deletions
131
Neolution.Utilities.UnitTests/Extensions/StringExtensionsTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| namespace Neolution.Utilities.UnitTests.Extensions; | ||
|
|
||
| using Neolution.Utilities.Extensions; | ||
| using Xunit; | ||
|
|
||
| /// <summary> | ||
| /// Unit tests for <see cref="StringExtensions"/>. | ||
| /// </summary> | ||
| public class StringExtensionsTests | ||
| { | ||
| /// <summary> | ||
| /// Truncate returns the expected result. | ||
| /// </summary> | ||
| /// <param name="value">The value.</param> | ||
| /// <param name="maxLength">The maximum length.</param> | ||
| /// <param name="expected">The expected.</param> | ||
| [Theory] | ||
| [InlineData(null, 5, null)] | ||
| [InlineData("", 5, "")] | ||
| [InlineData("abc", 5, "abc")] | ||
| [InlineData("abcdef", 3, "abc")] | ||
| [InlineData("abcdef", 6, "abcdef")] | ||
| public void Truncate_ReturnsExpectedResult(string? value, int maxLength, string? expected) | ||
| { | ||
| // Act | ||
| var result = value.Truncate(maxLength); | ||
|
|
||
| // Assert | ||
| Assert.Equal(expected, result); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncate with suffix returns the expected result when suffix length does not exceed maxLength. | ||
| /// maxLength represents the total length of the resulting string. | ||
| /// </summary> | ||
| /// <param name="value">The value.</param> | ||
| /// <param name="maxLength">The maximum (final) length.</param> | ||
| /// <param name="suffix">The suffix.</param> | ||
| /// <param name="expected">The expected.</param> | ||
| [Theory] | ||
| [InlineData(null, 5, "...", null)] | ||
| [InlineData("", 5, "...", "")] | ||
| [InlineData("abc", 5, "...", "abc")] // no truncation because length <= maxLength | ||
| [InlineData("abcdef", 6, "...", "abcdef")] // no truncation | ||
| [InlineData("abcdef", 3, "...", "...")] // suffix length == maxLength => only suffix | ||
| [InlineData("abcdef", 4, "-", "abc-")] // keep 3 chars + '-' | ||
| [InlineData("abcdef", 4, "", "abcd")] // empty suffix behaves like plain truncate | ||
| [InlineData("abcdef", 0, "", "")] // zero length with empty suffix | ||
| public void Truncate_WithSuffix_ReturnsExpectedResult(string? value, int maxLength, string suffix, string? expected) | ||
| { | ||
| // Act | ||
| var result = value.Truncate(maxLength, suffix); | ||
|
|
||
| // Assert | ||
| Assert.Equal(expected, result); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncating with a negative maxLength throws an <see cref="ArgumentOutOfRangeException"/>. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_NegativeMaxLength_Throws() | ||
| { | ||
| Assert.Throws<ArgumentOutOfRangeException>(() => "abc".Truncate(-1)); | ||
| Assert.Throws<ArgumentOutOfRangeException>(() => "abc".Truncate(-5, "..")); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncating with a null suffix throws an <see cref="ArgumentNullException"/>. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_NullSuffix_Throws() | ||
| { | ||
| Assert.Throws<ArgumentNullException>(() => "abc".Truncate(2, null!)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncating with a suffix longer than maxLength throws an <see cref="ArgumentException"/>. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_SuffixLongerThanMaxLength_Throws() | ||
| { | ||
| Assert.Throws<ArgumentException>(() => "abcdef".Truncate(3, "....")); | ||
| Assert.Throws<ArgumentException>(() => "abcdef".Truncate(0, ".")); | ||
| Assert.Throws<ArgumentException>(() => "abcdef".Truncate(2, "...")); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// When the value length equals maxLength and a suffix is provided (shorter than maxLength), the value is returned unchanged. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_ValueLengthEqualsMaxLength_WithSuffix_ReturnsUnchanged() | ||
| { | ||
| var value = "12345"; | ||
| var result = value.Truncate(5, "..."); | ||
| Assert.Equal("12345", result); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Result equals the suffix when suffix length == maxLength. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_SuffixLengthEqualsMaxLength_ReturnsOnlySuffix() | ||
| { | ||
| var result = "abcdef".Truncate(3, "..."); | ||
| Assert.Equal("...", result); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncation works with Unicode surrogate pairs (keeping whole emoji, not splitting) when maxLength accommodates suffix + complete emoji. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_UnicodeCharacters() | ||
| { | ||
| var value = "😀😃😄😁"; // 4 emoji (each surrogate pair) | ||
| var result = value.Truncate(5, "..."); // keep one emoji (2 code units) + '...' | ||
| Assert.Equal("😀...", result); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncation of a long string returns the expected prefix plus suffix with total length == maxLength. | ||
| /// </summary> | ||
| [Fact] | ||
| public void Truncate_LongString() | ||
| { | ||
| var value = new string('x', 1000); | ||
| var result = value.Truncate(10, "..."); // keep 7 + '...' | ||
| Assert.Equal(new string('x', 7) + "...", result); | ||
| Assert.Equal(10, result!.Length); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| namespace Neolution.Utilities.Extensions; | ||
|
|
||
| /// <summary> | ||
| /// String extensions. | ||
| /// </summary> | ||
| public static class StringExtensions | ||
| { | ||
| /// <summary> | ||
| /// Truncates the string to the specified maximum length. | ||
| /// </summary> | ||
| /// <param name="value">The value.</param> | ||
| /// <param name="maxLength">The maximum length.</param> | ||
| /// <returns>The truncated string</returns> | ||
| public static string? Truncate(this string? value, int maxLength) | ||
| { | ||
| return value.Truncate(maxLength, string.Empty); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Truncates the string to the specified maximum length, appending a suffix if necessary. | ||
| /// </summary> | ||
| /// <param name="value">The value.</param> | ||
| /// <param name="maxLength">The maximum length.</param> | ||
| /// <param name="suffix">The suffix to append if the string is truncated. The suffix length must not exceed <paramref name="maxLength"/>.</param> | ||
| /// <returns>The truncated string.</returns> | ||
| /// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="maxLength"/> is negative.</exception> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="suffix"/> is <c>null</c>.</exception> | ||
| /// <exception cref="ArgumentException">Thrown when <paramref name="suffix"/> is longer than <paramref name="maxLength"/>.</exception> | ||
| public static string? Truncate(this string? value, int maxLength, string suffix) | ||
| { | ||
| ArgumentOutOfRangeException.ThrowIfNegative(maxLength); | ||
| ArgumentNullException.ThrowIfNull(suffix); | ||
|
|
||
| if (suffix.Length > maxLength) | ||
| { | ||
| throw new ArgumentException("Suffix length must not exceed maxLength.", nameof(suffix)); | ||
| } | ||
|
|
||
| if (string.IsNullOrEmpty(value) || value.Length <= maxLength) | ||
| { | ||
| return value; | ||
| } | ||
|
|
||
| return $"{value[..(maxLength - suffix.Length)]}{suffix}"; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.