Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 286 additions & 0 deletions TUnit.Assertions.UnitTests/StringRegexAssertionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
using System.Text.RegularExpressions;
using TUnit.Assertions.Extensions;

namespace TUnit.Assertions.UnitTests;

public partial class StringRegexAssertionTests
{

#region Matches Succeeds
[Test]
public async Task Matches_WithValidPattern_StringPattern_Succeeds()
{
var text = "Hello123World";
var pattern = @"\w+\d+\w+";

await TUnitAssert.That(text).Matches(pattern);
}

[Test]
public async Task Matches_WithValidPattern_RegexPattern_Succeeds()
{
var text = "Hello123World";
var pattern = new Regex(@"\w+\d+\w+");

await TUnitAssert.That(text).Matches(pattern);
}

#if NET // Needed because NetFramework doesn't support partial methods
[GeneratedRegex(@"\w+\d+\w+")]
private static partial Regex FindHello123WorldRegex();

[Test]
public async Task Matches_WithValidPattern_GeneratedRegexPattern_Succeeds()
{
var text = "Hello123World";
Regex regex = FindHello123WorldRegex();

await TUnitAssert.That(text).Matches(regex);
}
#endif
#endregion

#region Matches Throws
[Test]
#if NET
[TestCase(typeof(RegexParseException), @"[", null!)] // invalid regex
#endif
[TestCase(typeof(ArgumentNullException), @"^\d+$", null)]
[TestCase(typeof(TUnitAssertionException), @"^\d+$", "Hello123World")]
public void Matches_WithInvalidPattern_StringPattern_Throws(Type exceptionType, string pattern, string? text)
{
AsyncTestDelegate action = async () => await TUnitAssert.That(text).Matches(pattern);

Exception? exception = NUnitAssert.ThrowsAsync(exceptionType,action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text match pattern

but The regex "^\d+$" does not match with "{text}"

at Assert.That(text).Matches(pattern)
"""
));
}

[Test]
[TestCase(typeof(ArgumentNullException), null)]
[TestCase(typeof(TUnitAssertionException), "Hello123World")]
public void Matches_WithInvalidPattern_RegexPattern_Throws(Type exceptionType, string? text)
{
var pattern = new Regex(@"^\d+$");

AsyncTestDelegate action = async () => await TUnitAssert.That(text).Matches(pattern);

var exception = NUnitAssert.ThrowsAsync(exceptionType, action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text match pattern

but The regex "^\d+$" does not match with "{text}"

at Assert.That(text).Matches(pattern)
"""
));
}

#if NET // Needed because NetFramework doesn't support partial methods
[GeneratedRegex(@"^\d+$")]
private static partial Regex Matches_FindNumberRegex();

[Test]
[TestCase(typeof(ArgumentNullException), null)]
[TestCase(typeof(TUnitAssertionException), "Hello123World")]
public void Matches_WithInvalidPattern_GeneratedRegexPattern_Throws(Type exceptionType, string? text)
{
Regex regex = Matches_FindNumberRegex();

AsyncTestDelegate action = async () => await TUnitAssert.That(text).Matches(regex);

Exception? exception = NUnitAssert.ThrowsAsync(exceptionType, action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text match regex

but The regex "^\d+$" does not match with "Hello123World"

at Assert.That(text).Matches(regex)
"""
));
}
#endif

[Test]
[TestCase(typeof(RegexMatchTimeoutException), "(a+)+$", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")]
[TestCase(typeof(RegexMatchTimeoutException), @"^(([a-z])+.)+[A-Z]([a-z])+$", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")]
public void Matches_WithTimeoutPattern_Throws(Type exceptionType, string pattern, string text)
{
// Create regex with a short timeout
#if NET8_0_OR_GREATER
var timeout = TimeSpan.FromMicroseconds(1);
#else
var timeout = TimeSpan.FromTicks(1);
#endif
var regex = new Regex(pattern, RegexOptions.None, timeout);

AsyncTestDelegate action = async () => await TUnitAssert.That(text).Matches(regex);

var exception = NUnitAssert.ThrowsAsync<RegexMatchTimeoutException>(action);
NUnitAssert.That(exception!.Pattern, Is.EqualTo(pattern));
}
#endregion

#region DoesNotMatch Succeeds
[Test]
public async Task DoesNotMatch_WithValidPattern_StringPattern_Succeeds()
{
var text = "Hello123World";
var pattern = @"^\d+$";

await TUnitAssert.That(text).DoesNotMatch(pattern);
}

[Test]
public async Task DoesNotMatch_WithValidPattern_RegexPattern_Succeeds()
{
var text = "Hello123World";
var pattern = new Regex(@"^\d+$");

await TUnitAssert.That(text).DoesNotMatch(pattern);
}

#if NET // Needed because NetFramework doesn't support partial methods
[GeneratedRegex(@"^\d+$")]
private static partial Regex DoesNotMatch_FindNumberOnlyRegex();

[Test]
public async Task DoesNotMatch_WithValidPattern_GeneratedRegexPattern_Succeeds()
{
var text = "Hello123World";
Regex regex = DoesNotMatch_FindNumberOnlyRegex();

await TUnitAssert.That(text).DoesNotMatch(regex);
}
#endif
#endregion

#region DoesNotMatch Throws
[Test]
#if NET
[TestCase(typeof(RegexParseException), @"[", null!)] // invalid regex
#endif
[TestCase(typeof(ArgumentNullException), @"^\d+$", null)]
[TestCase(typeof(TUnitAssertionException), @"^\d+$", "123")]
public void DoesNotMatch_WithInvalidPattern_StringPattern_Throws(Type exceptionType, string pattern, string? text)
{
AsyncTestDelegate action = async () => await TUnitAssert.That(text).DoesNotMatch(pattern);

Exception? exception = NUnitAssert.ThrowsAsync(exceptionType, action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text to not match with pattern

but The regex "^\d+$" matches with "{text}"

at Assert.That(text).DoesNotMatch(pattern)
"""
));
}

[Test]
[TestCase(typeof(ArgumentNullException), null)]
[TestCase(typeof(TUnitAssertionException), "123")]
public void DoesNotMatch_WithInvalidPattern_RegexPattern_Throws(Type exceptionType, string? text)
{
var pattern = new Regex(@"^\d+$");

AsyncTestDelegate action = async () => await TUnitAssert.That(text).DoesNotMatch(pattern);

Exception? exception = NUnitAssert.ThrowsAsync(exceptionType, action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text to not match with pattern

but The regex "^\d+$" matches with "{text}"

at Assert.That(text).DoesNotMatch(pattern)
"""
));
}

#if NET // Needed because NetFramework doesn't support partial methods
[GeneratedRegex(@"^\d+$")]
private static partial Regex FindNumberRegex();

[Test]
[TestCase(typeof(ArgumentNullException), null)]
[TestCase(typeof(TUnitAssertionException), "123")]
public void DoesNotMatch_WithInvalidPattern_GeneratedRegexPattern_Throws(Type exceptionType, string? text)
{
Regex regex = FindNumberRegex();

AsyncTestDelegate action = async () => await TUnitAssert.That(text).DoesNotMatch(regex);

Exception? exception = NUnitAssert.ThrowsAsync(exceptionType, action);
if (exceptionType != typeof(TUnitAssertionException))
{
return;
}

NUnitAssert.That(exception!.Message, Is.EqualTo(
$"""
Expected text to not match with regex

but The regex "^\d+$" matches with "{text}"

at Assert.That(text).DoesNotMatch(regex)
"""
));
}
#endif

[Test]
[TestCase(typeof(RegexMatchTimeoutException), "(a+)+$", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")]
[TestCase(typeof(RegexMatchTimeoutException), @"^(([a-z])+.)+[A-Z]([a-z])+$", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!")]
public void DoesNotMatch_WithTimeoutPattern_Throws(Type exceptionType, string pattern, string text)
{
// Create regex with a short timeout
#if NET8_0_OR_GREATER
var timeout = TimeSpan.FromMicroseconds(1);
#else
var timeout = TimeSpan.FromTicks(1);
#endif
var regex = new Regex(pattern, RegexOptions.None, timeout);

AsyncTestDelegate action = async () => await TUnitAssert.That(text).DoesNotMatch(regex);

var exception = NUnitAssert.ThrowsAsync<RegexMatchTimeoutException>(action);
NUnitAssert.That(exception!.Pattern, Is.EqualTo(pattern));
}
#endregion
}
19 changes: 19 additions & 0 deletions TUnit.Assertions/Assertions/Strings/DoesNotExtensions_String.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable disable

using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using TUnit.Assertions.AssertConditions;
using TUnit.Assertions.AssertConditions.Interfaces;
using TUnit.Assertions.AssertConditions.String;
Expand Down Expand Up @@ -57,4 +58,22 @@ public static InvokableValueAssertionBuilder<string> DoesNotEndWith(this IValueS
$"not end with {expected}")
, [doNotPopulateThisValue1, doNotPopulateThisValue2]);
}

public static InvokableValueAssertionBuilder<string> DoesNotMatch(this IValueSource<string> valueSource, string regex, [CallerArgumentExpression(nameof(regex))] string expression = "")
{
return DoesNotMatch(valueSource, new Regex(regex), expression);
}

public static InvokableValueAssertionBuilder<string> DoesNotMatch(this IValueSource<string> valueSource, Regex regex, [CallerArgumentExpression(nameof(regex))] string expression = "")
{
return valueSource.RegisterAssertion(new FuncValueAssertCondition<string, Regex>(regex,
(actual, _, _) =>
{
Verify.ArgNotNull(actual);
return !regex.IsMatch(actual);
},
(actual, _, _) => $"The regex \"{regex}\" matches with \"{actual}\"",
$"to not match with {expression}")
, [expression]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,8 @@ namespace TUnit.Assertions.Extensions
where TDictionary : System.Collections.IDictionary { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, System.Text.RegularExpressions.Regex regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,8 @@ namespace TUnit.Assertions.Extensions
where TDictionary : System.Collections.IDictionary { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, System.Text.RegularExpressions.Regex regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,8 @@ namespace TUnit.Assertions.Extensions
where TDictionary : System.Collections.IDictionary { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotEndWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, System.Text.RegularExpressions.Regex regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotMatch(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string regex, [System.Runtime.CompilerServices.CallerArgumentExpression("regex")] string expression = "") { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = null) { }
public static TUnit.Assertions.AssertionBuilders.InvokableValueAssertionBuilder<string> DoesNotStartWith(this TUnit.Assertions.AssertConditions.Interfaces.IValueSource<string> valueSource, string expected, System.StringComparison stringComparison, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue1 = null, [System.Runtime.CompilerServices.CallerArgumentExpression("stringComparison")] string doNotPopulateThisValue2 = null) { }
}
Expand Down
Loading