I need to test that a regex replacement times out. I'm using this overload:
public static string Replace(
string input, string pattern, string replacement,
RegexOptions options,
TimeSpan matchTimeout
)
My code doesn't throw on timeout, so I decided to test the actual framework method:
[Fact]
public void framework_method_should_throw() {
var input = string.Join("", Enumerable.Range(0,100).Select(x => "01234567890123456789"));
var pattern = "3[abc4]5(?!xyz)[abc6]7[abc8]9";
var replacement = "bar";
var timeout = TimeSpan.FromMilliseconds(1); // <--- should time out immediately
Action action = () => Regex.Replace(input, pattern, replacement, RegexOptions.None, timeout);
action.Should().Throw<RegexMatchTimeoutException>();
}
I used a massive search string, and various difficult patterns (the one above is just an example), but it never throws.
Expected a <System.Text.RegularExpressions.RegexMatchTimeoutException> to be thrown, but no exception was thrown.
What am I doing wrong?
CodePudding user response:
As per the documentation (emphasis mine):
The
matchTimeout
parameter specifies how long a pattern matching method should try to find a match before it times out. Setting a time-out interval prevents regular expressions that rely on excessive backtracking from appearing to "stop responding when they process input that contains near matches. For more information, see Best Practices for Regular Expressions and Backtracking. If no match is found in that time interval, the method throws a RegexMatchTimeoutException exception. matchTimeout overrides any default time-out value defined for the application domain in which the method executes.
The easiest way to test this is to use a pattern that's vulnerable to Catastrophic Backtracking. Here's an example that will throw a RegexMatchTimeoutException
:
string input = "This is a test string that should not match.";
string pattern = @"^(\w \s?)*$";
string output = Regex.Replace(input, pattern, string.Empty,
RegexOptions.None, TimeSpan.FromSeconds(2));
CodePudding user response:
I increased the search space dramatically - by changing the above code to Enumerable.Range(0,10_000)
. Only then did it timeout.
I automatically assumed a bug in my code, but it turns out the .NET regex engine is EXTREMELY performant!
Another approach mentioned above by @ChristianK is to use TimeSpan.FromTicks(1)
instead. And, incredibly, perf will increase even more in .net7.