Skip to content

Commit a161f34

Browse files
authored
A few more regex test (#58154)
* Rust parser tests * Update msg * comment * Work around #56945 * Add license notice * Update url * feedback
1 parent d495e30 commit a161f34

File tree

5 files changed

+154
-66
lines changed

5 files changed

+154
-66
lines changed

src/libraries/System.IO.FileSystem/tests/File/EncryptDecrypt.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ public static void EncryptDecrypt_Read()
4343
{
4444
File.Encrypt(tmpFileName);
4545
}
46-
catch (IOException e) when (e.HResult == unchecked((int)0x80070490))
46+
catch (IOException e) when (e.HResult == unchecked((int)0x80070490) ||
47+
(e.HResult == unchecked((int)0x80071776)))
4748
{
4849
// Ignore ERROR_NOT_FOUND 1168 (0x490). It is reported when EFS is disabled by domain policy.
50+
// Ignore ERROR_NO_USER_KEYS (0x1776). This occurs when no user key exists to encrypt with.
4951
return;
5052
}
5153

src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.cs

Lines changed: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -565,21 +565,6 @@ public partial class RegexParserTests
565565
[InlineData(@"\bgr[ae]y\b", RegexOptions.None, null)]
566566
[InlineData(@"\b((?# case sensitive comparison)D\w+)\s(?ixn)((?#case insensitive comparison)d\w+)\b", RegexOptions.None, null)]
567567
[InlineData(@"\{\d+(,-*\d+)*(\:\w{1,4}?)*\}(?x) # Looks for a composite format item.", RegexOptions.None, null)]
568-
public void Parse(string pattern, RegexOptions options, object errorObj, int offset = -1)
569-
{
570-
RegexParseError? error = (RegexParseError?)errorObj;
571-
572-
Assert.True(error == null || offset > 0, "All tests must be given positive offsets, or null if no offset should be tested.");
573-
574-
// Parse the main tree and if parsing fails check if the supplied error matches.
575-
ParseTree(pattern, options, error, offset);
576-
577-
// Assert that only ArgumentException might be thrown during parsing.
578-
ParseSubTrees(pattern, options);
579-
}
580-
581-
[Theory]
582-
// Negative tests
583568
[InlineData(@"cat([a-\d]*)dog", RegexOptions.None, RegexParseError.ShorthandClassInCharacterRange, 9)]
584569
[InlineData(@"\k<1", RegexOptions.None, RegexParseError.UnrecognizedEscape, 2)]
585570
[InlineData(@"(?')", RegexOptions.None, RegexParseError.CaptureGroupNameInvalid, 3)]
@@ -716,67 +701,67 @@ public void Parse(string pattern, RegexOptions options, object errorObj, int off
716701
[InlineData("[a-z-[b][", RegexOptions.None, RegexParseError.UnterminatedBracket, 9)]
717702
[InlineData("(?()|||||)", RegexOptions.None, RegexParseError.AlternationHasTooManyConditions, 10)]
718703
[InlineData("[^]", RegexOptions.None, RegexParseError.UnterminatedBracket, 3)]
719-
[InlineData("\\", RegexOptions.None, RegexParseError.UnescapedEndingBackslash, 1)]
720704
[InlineData("??", RegexOptions.None, RegexParseError.QuantifierAfterNothing, 1)]
721705
[InlineData("(?=*)", RegexOptions.None, RegexParseError.QuantifierAfterNothing, 4)]
722706
[InlineData("((((((*))))))", RegexOptions.None, RegexParseError.QuantifierAfterNothing, 7)]
723-
public void ParseCheckOffset(string pattern, RegexOptions options, object errorObj, int offset)
707+
public void ParseCheckOffset(string pattern, RegexOptions options, RegexParseError? error, int offset = -1)
724708
{
725-
RegexParseError? error = (RegexParseError?)errorObj;
726-
727-
// Parse the main tree and if parsing fails check if the supplied error matches.
728-
ParseTree(pattern, options, error, offset);
729-
730-
// Assert that only ArgumentException might be thrown during parsing.
731-
ParseSubTrees(pattern, options);
732-
}
733-
734-
735-
private static void ParseSubTrees(string pattern, RegexOptions options)
736-
{
737-
// Trim the input from the right and make sure tree invariants hold
738-
for (int i = pattern.Length - 1; i > 0; i--)
739-
{
740-
ParseSubTree(pattern.Substring(0, i), options);
741-
}
742-
743-
// Trim the input from the left and make sure tree invariants hold
744-
for (int i = 1; i < pattern.Length; i++)
745-
{
746-
ParseSubTree(pattern.Substring(i), options);
747-
}
748-
749-
// Skip middles
750-
for (int i = 1; i < pattern.Length; i++)
751-
{
752-
ParseSubTree(pattern.Substring(0, i) + pattern.Substring(i + 1), options);
753-
}
709+
Parse(pattern, options, error, offset);
754710
}
755711

756-
static void ParseTree(string pattern, RegexOptions options, RegexParseError? error, int offset)
712+
private static void Parse(string pattern, RegexOptions options, RegexParseError? error, int offset = -1)
757713
{
758714
if (error != null)
759715
{
716+
Assert.InRange(offset, 0, int.MaxValue);
760717
Throws(error.Value, offset, () => new Regex(pattern, options));
761718
return;
762719
}
763720

721+
Assert.Equal(-1, offset);
722+
764723
// Nothing to assert here without having access to internals.
765-
new Regex(pattern, options);
724+
new Regex(pattern, options); // Does not throw
725+
726+
ParsePatternFragments(pattern, options);
766727
}
767728

768-
private static void ParseSubTree(string pattern, RegexOptions options)
729+
private static void ParsePatternFragments(string pattern, RegexOptions options)
769730
{
770-
try
731+
// Trim the input in various places and parse.
732+
// Verify that if it throws, it's the correct exception type
733+
for (int i = pattern.Length - 1; i > 0; i--)
734+
{
735+
string str = pattern.Substring(0, i);
736+
MayThrow(() => new Regex(str, options));
737+
}
738+
739+
for (int i = 1; i < pattern.Length; i++)
771740
{
772-
new Regex(pattern, options);
741+
string str = pattern.Substring(i);
742+
MayThrow(() => new Regex(str, options));
773743
}
774-
catch (ArgumentException)
744+
745+
for (int i = 1; i < pattern.Length; i++)
775746
{
776-
// We are fine with ArgumentExceptions being thrown during sub expression parsing.
747+
string str = pattern.Substring(0, i) + pattern.Substring(i + 1);
748+
MayThrow(() => new Regex(str, options));
777749
}
778750
}
779751

752+
/// <summary>
753+
/// Checks that action throws either a RegexParseException or an ArgumentException depending on the
754+
/// environment and the supplied error.
755+
/// </summary>
756+
/// <param name="error">The expected parse error</param>
757+
/// <param name="action">The action to invoke.</param>
780758
static partial void Throws(RegexParseError error, int offset, Action action);
759+
760+
/// <summary>
761+
/// Checks that action succeeds or throws either a RegexParseException or an ArgumentException depending on the
762+
// environment and the action.
763+
/// </summary>
764+
/// <param name="action">The action to invoke.</param>
765+
static partial void MayThrow(Action action);
781766
}
782767
}

src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netcoreapp.cs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,11 @@ namespace System.Text.RegularExpressions.Tests
1111
public partial class RegexParserTests
1212
{
1313
[Theory]
14-
[InlineData(@"[a-\-]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
15-
[InlineData(@"[a-\-b]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
16-
[InlineData(@"[a-\-\-b]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
17-
[InlineData(@"[a-\-\D]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
18-
[InlineData(@"[a-\-\-\D]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
19-
[InlineData(@"[a -\-\b]", RegexOptions.None, null, 5)]
20-
// OutOfMemoryException
14+
15+
// Avoid OutOfMemoryException
2116
[InlineData("a{2147483647}", RegexOptions.None, null)]
2217
[InlineData("a{2147483647,}", RegexOptions.None, null)]
18+
2319
[InlineData(@"(?(?N))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
2420
[InlineData(@"(?(?i))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
2521
[InlineData(@"(?(?I))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
@@ -30,7 +26,6 @@ public partial class RegexParserTests
3026
[InlineData(@"(?(?X))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
3127
[InlineData(@"(?(?n))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
3228
[InlineData(@"(?(?m))", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 5)]
33-
// IndexOutOfRangeException
3429
[InlineData("(?<-", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
3530
[InlineData("(?<-", RegexOptions.IgnorePatternWhitespace, RegexParseError.InvalidGroupingConstruct, 3)]
3631
[InlineData(@"^[^<>]*(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))$", RegexOptions.None, null)]
@@ -90,8 +85,50 @@ public partial class RegexParserTests
9085
[InlineData("a{0,2147483648}", RegexOptions.None, RegexParseError.QuantifierOrCaptureGroupOutOfRange, 14)]
9186
// Surrogate pair which is parsed as [char,char-char,char] as we operate on UTF-16 code units.
9287
[InlineData("[\uD82F\uDCA0-\uD82F\uDCA3]", RegexOptions.IgnoreCase, RegexParseError.ReversedCharacterRange, 5)]
88+
89+
// Following are borrowed from Rust regex tests ============
90+
// https://github.com/rust-lang/regex/blob/master/tests/noparse.rs
91+
[InlineData(@"*", RegexOptions.None, RegexParseError.QuantifierAfterNothing, 1)]
92+
[InlineData(@"[A-", RegexOptions.None, RegexParseError.UnterminatedBracket, 3)]
93+
[InlineData(@"[A", RegexOptions.None, RegexParseError.UnterminatedBracket, 2)]
94+
[InlineData(@"[\A]", RegexOptions.None, RegexParseError.UnrecognizedEscape, 3)]
95+
[InlineData(@"[\z]", RegexOptions.None, RegexParseError.UnrecognizedEscape, 3)]
96+
[InlineData(@"(", RegexOptions.None, RegexParseError.InsufficientClosingParentheses, 1)]
97+
[InlineData(@")", RegexOptions.None, RegexParseError.InsufficientOpeningParentheses, 1)]
98+
[InlineData(@"[a-Z]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 4)]
99+
[InlineData(@"(?P<>a)", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
100+
[InlineData(@"(?P<na-me>)", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
101+
[InlineData(@"(?a)a", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
102+
[InlineData(@"a{2,1}", RegexOptions.None, RegexParseError.ReversedQuantifierRange, 6)]
103+
[InlineData(@"(?", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 2)]
104+
[InlineData(@"\8", RegexOptions.None, RegexParseError.UndefinedNumberedReference, 2)]
105+
[InlineData(@"\xG0", RegexOptions.None, RegexParseError.InsufficientOrInvalidHexDigits, 3)]
106+
[InlineData(@"\xF", RegexOptions.None, RegexParseError.InsufficientOrInvalidHexDigits, 2)]
107+
[InlineData(@"\x{fffg}", RegexOptions.None, RegexParseError.InsufficientOrInvalidHexDigits, 3)]
108+
[InlineData(@"(?a)", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
109+
[InlineData(@"(?)", RegexOptions.None, RegexParseError.QuantifierAfterNothing, 2)]
110+
[InlineData(@"(?P<a>.)(?P<a>.)", RegexOptions.None, RegexParseError.InvalidGroupingConstruct, 3)]
111+
[InlineData(@"[a-\A]", RegexOptions.None, RegexParseError.UnrecognizedEscape, 5)]
112+
[InlineData(@"[a-\z]", RegexOptions.None, RegexParseError.UnrecognizedEscape, 5)]
113+
[InlineData(@"[a-\b]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
114+
[InlineData(@"[a-\-]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
115+
[InlineData(@"[a-\-b]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
116+
[InlineData(@"[a-\-\-b]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
117+
[InlineData(@"[a-\-\D]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
118+
[InlineData(@"[a-\-\-\D]", RegexOptions.None, RegexParseError.ReversedCharacterRange, 5)]
119+
[InlineData(@"[a -\-\b]", RegexOptions.None, null)]
120+
[InlineData(@"[\b]", RegexOptions.None, null)] // errors in rust: class_no_boundary
121+
[InlineData(@"a{10000000}", RegexOptions.None, null)] // errors in rust: too_big
122+
[InlineData(@"a{1001", RegexOptions.None, null)] // errors in rust: counted_no_close
123+
[InlineData(@"a{-1,1}", RegexOptions.None, null)] // errors in rust: counted_nonnegative
124+
[InlineData(@"\\", RegexOptions.None, null)] // errors in rust: unfinished_escape
125+
[InlineData(@"(?-i-i)", RegexOptions.None, null)] // errors in rust: double_neg
126+
[InlineData(@"(?i-)", RegexOptions.None, null)] // errors in rust: neg_empty
127+
[InlineData(@"[a-[:lower:]]", RegexOptions.None, null)] // errors in rust: range_end_no_class
128+
// End of Rust parser tests ==============
129+
93130
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
94-
public void Parse_Netcoreapp(string pattern, RegexOptions options, object error, int offset = -1)
131+
public void Parse_Netcoreapp(string pattern, RegexOptions options, RegexParseError? error, int offset = -1)
95132
{
96133
Parse(pattern, options, error, offset);
97134
}
@@ -137,7 +174,7 @@ static partial void Throws(RegexParseError error, int offset, Action action)
137174
return;
138175
}
139176

140-
throw new XunitException($"Expected RegexParseException with error: ({error}) -> Actual error: {regexParseError})");
177+
throw new XunitException($"Expected RegexParseException with error {error} offset {offset} -> Actual error: {regexParseError} offset {e.Offset})");
141178
}
142179
catch (Exception e)
143180
{
@@ -146,5 +183,18 @@ static partial void Throws(RegexParseError error, int offset, Action action)
146183

147184
throw new XunitException($"Expected RegexParseException with error: ({error}) -> Actual: No exception thrown");
148185
}
186+
187+
/// <summary>
188+
/// Checks that action succeeds or throws either a RegexParseException or an ArgumentException depending on the
189+
// environment and the action.
190+
/// </summary>
191+
/// <param name="action">The action to invoke.</param>
192+
static partial void MayThrow(Action action)
193+
{
194+
if (Record.Exception(action) is Exception e && e is not RegexParseException)
195+
{
196+
throw new XunitException($"Expected RegexParseException or no exception -> Actual: ({e})");
197+
}
198+
}
149199
}
150200
}

src/libraries/System.Text.RegularExpressions/tests/RegexParserTests.netfx.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace System.Text.RegularExpressions.Tests
1111
public partial class RegexParserTests
1212
{
1313
/// <summary>
14-
/// Checks if action throws either a RegexParseException or an ArgumentException depending on the
14+
/// Checks that action throws either a RegexParseException or an ArgumentException depending on the
1515
/// environment and the supplied error.
1616
/// </summary>
1717
/// <param name="error">The expected parse error</param>
@@ -28,11 +28,24 @@ static partial void Throws(RegexParseError error, int offset, Action action)
2828
return;
2929
}
3030
catch (Exception e)
31-
{
31+
{
3232
throw new XunitException($"Expected ArgumentException -> Actual: {e}");
3333
}
3434

3535
throw new XunitException($"Expected ArgumentException with error: ({error}) -> Actual: No exception thrown");
3636
}
37+
38+
/// <summary>
39+
/// Checks that action succeeds or throws either a RegexParseException or an ArgumentException depending on the
40+
// environment and the action.
41+
/// </summary>
42+
/// <param name="action">The action to invoke.</param>
43+
static partial void MayThrow(Action action)
44+
{
45+
if (Record.Exception(action) is Exception e && e is not ArgumentException)
46+
{
47+
throw new XunitException($"Expected ArgumentException or no exception -> Actual: ({e})");
48+
}
49+
}
3750
}
3851
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.NET Runtime uses third-party libraries or other resources that may be
2+
distributed under licenses different than the .NET Runtime software.
3+
4+
In the event that we accidentally failed to list a required notice, please
5+
bring it to our attention. Post an issue or email us:
6+
7+
dotnet@microsoft.com
8+
9+
The attached notices are provided for information only.
10+
11+
License notice for https://github.com/rust-lang/regex
12+
-------------------------------
13+
14+
Copyright (c) 2014 The Rust Project Developers
15+
16+
Permission is hereby granted, free of charge, to any
17+
person obtaining a copy of this software and associated
18+
documentation files (the "Software"), to deal in the
19+
Software without restriction, including without
20+
limitation the rights to use, copy, modify, merge,
21+
publish, distribute, sublicense, and/or sell copies of
22+
the Software, and to permit persons to whom the Software
23+
is furnished to do so, subject to the following
24+
conditions:
25+
26+
The above copyright notice and this permission notice
27+
shall be included in all copies or substantial portions
28+
of the Software.
29+
30+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
31+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
32+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
33+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
34+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
35+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
37+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
38+
DEALINGS IN THE SOFTWARE.

0 commit comments

Comments
 (0)