Skip to content

Commit eefda78

Browse files
committed
Convert diagnostic tests into parse tests
Match against the parser output rather than unit testing the lexer, as it is more convenient and allows us to test a better range of error cases.
1 parent 3902577 commit eefda78

File tree

2 files changed

+77
-83
lines changed

2 files changed

+77
-83
lines changed

Tests/RegexTests/LexTests.swift

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ func diagnose(
4040
}
4141
}
4242

43-
extension Source {
44-
@discardableResult
45-
fileprivate mutating func lexBasicAtom() throws -> AST.Atom? {
46-
try lexAtom(isInCustomCharacterClass: false, priorGroupCount: 0)
47-
}
48-
}
49-
5043
extension RegexTests {
5144
func testLexicalAnalysis() {
5245
diagnose("a", expecting: .expected("b")) { src in
@@ -101,73 +94,6 @@ extension RegexTests {
10194
diagnoseUniScalarOverflow("{123456789}", base: "u")
10295
diagnoseUniScalarOverflow("{123456789}", base: "x")
10396

104-
// Text segment options
105-
diagnose("(?-y{g})", expecting: .cannotRemoveTextSegmentOptions) {
106-
_ = try $0.lexGroupStart()
107-
}
108-
diagnose("(?-y{w})", expecting: .cannotRemoveTextSegmentOptions) {
109-
_ = try $0.lexGroupStart()
110-
}
111-
112-
// Test expected group.
113-
diagnose(#"(*"#, expecting: .misc("Quantifier '*' must follow operand")) {
114-
_ = try $0.lexGroupStart()
115-
}
116-
117-
// Test expected closing delimiters.
118-
diagnose(#"\u{5"#, expecting: .expected("}")) { try $0.lexBasicAtom() }
119-
diagnose(#"\x{5"#, expecting: .expected("}")) { try $0.lexBasicAtom() }
120-
diagnose(#"\N{A"#, expecting: .expected("}")) { try $0.lexBasicAtom() }
121-
diagnose(#"\N{U+A"#, expecting: .expected("}")) { try $0.lexBasicAtom() }
122-
diagnose(#"\p{a"#, expecting: .expected("}")) { try $0.lexBasicAtom() }
123-
diagnose(#"\p{a="#, expecting: .expected("}")) { try $0.lexBasicAtom() }
124-
diagnose(#"(?#"#, expecting: .expected(")")) { _ = try $0.lexComment() }
125-
diagnose(#"(?x"#, expecting: .expected(")")) { _ = try $0.lexGroupStart() }
126-
127-
diagnose(#"(?"#, expecting: .expectedGroupSpecifier) {
128-
_ = try $0.lexGroupStart()
129-
}
130-
131-
diagnose(#"(?^"#, expecting: .expected(")")) { _ = try $0.lexGroupStart() }
132-
diagnose(#"(?^i"#, expecting: .expected(")")) { _ = try $0.lexGroupStart() }
133-
134-
diagnose(#"(?^-"#, expecting: .cannotRemoveMatchingOptionsAfterCaret) {
135-
_ = try $0.lexGroupStart()
136-
}
137-
diagnose(#"(?^-)"#, expecting: .cannotRemoveMatchingOptionsAfterCaret) {
138-
_ = try $0.lexGroupStart()
139-
}
140-
diagnose(#"(?^i-"#, expecting: .cannotRemoveMatchingOptionsAfterCaret) {
141-
_ = try $0.lexGroupStart()
142-
}
143-
diagnose(#"(?^i-m)"#, expecting: .cannotRemoveMatchingOptionsAfterCaret) {
144-
_ = try $0.lexGroupStart()
145-
}
146-
147-
diagnose(#"(?y)"#, expecting: .expected("{")) { _ = try $0.lexGroupStart() }
148-
diagnose(#"(?y{)"#, expecting: .expected("g")) { _ = try $0.lexGroupStart() }
149-
diagnose(#"(?y{g)"#, expecting: .expected("}")) { _ = try $0.lexGroupStart() }
150-
diagnose(#"(?y{x})"#, expecting: .expected("g")) { _ = try $0.lexGroupStart() }
151-
152-
diagnose(#"(?k)"#, expecting: .unknownGroupKind("?k")) {
153-
_ = try $0.lexGroupStart()
154-
}
155-
diagnose(#"(?P#)"#, expecting: .invalidMatchingOption("#")) {
156-
_ = try $0.lexGroupStart()
157-
}
158-
diagnose(#"(?P"#, expecting: .expected(")")) { _ = try $0.lexGroupStart() }
159-
diagnose(#"(?R"#, expecting: .expected(")")) { _ = try $0.lexBasicAtom() }
160-
161-
diagnose(#"\Qab"#, expecting: .expected("\\E")) { _ = try $0.lexQuote() }
162-
diagnose(#"\Qab\"#, expecting: .expected("\\E")) { _ = try $0.lexQuote() }
163-
diagnose(#""ab"#, expecting: .expected("\""), .experimental) { _ = try $0.lexQuote() }
164-
diagnose(#""ab\""#, expecting: .expected("\""), .experimental) { _ = try $0.lexQuote() }
165-
diagnose(#""ab\"#, expecting: .unexpectedEndOfInput, .experimental) { _ = try $0.lexQuote() }
166-
167-
diagnose(#"\k''"#, expecting: .expectedNonEmptyContents) { _ = try $0.lexBasicAtom() }
168-
diagnose(#"(?&)"#, expecting: .expectedNonEmptyContents) { _ = try $0.lexBasicAtom() }
169-
diagnose(#"(?P>)"#, expecting: .expectedNonEmptyContents) { _ = try $0.lexBasicAtom() }
170-
17197
// TODO: want to dummy print out source ranges, etc, test that.
17298
}
17399

Tests/RegexTests/ParseTests.swift

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,32 @@ func rangeTest(
155155
}
156156
}
157157

158+
func diagnosticTest(
159+
_ input: String, _ expected: ParseError,
160+
syntax: SyntaxOptions = .traditional,
161+
file: StaticString = #file, line: UInt = #line
162+
) {
163+
do {
164+
let ast = try parse(input, syntax)
165+
XCTFail("""
166+
167+
Passed \(ast)
168+
But expected error: \(expected)
169+
""", file: file, line: line)
170+
} catch let e as Source.LocatedError<ParseError> {
171+
guard e.error == expected else {
172+
XCTFail("""
173+
174+
Expected: \(expected)
175+
Actual: \(e.error)
176+
""", file: file, line: line)
177+
return
178+
}
179+
} catch let e {
180+
XCTFail("Error without source location: \(e)", file: file, line: line)
181+
}
182+
}
183+
158184
extension RegexTests {
159185
func testParse() {
160186
parseTest(
@@ -435,7 +461,7 @@ extension RegexTests {
435461

436462
// Quotes in character classes.
437463
parseTest(#"[\Q-\E]"#, charClass(quote_m("-")))
438-
parseTest(#"[\Qa-b[[*+\\E]"#, charClass(quote_m(#"a-b[[*+\"#)))
464+
parseTest(#"[\Qa-b[[*+\\E]"#, charClass(quote_m("a-b[[*+\\")))
439465

440466
parseTest(#"["-"]"#, charClass(quote_m("-")), syntax: .experimental)
441467
parseTest(#"["a-b[[*+\""]"#, charClass(quote_m(#"a-b[[*+""#)),
@@ -1038,17 +1064,59 @@ extension RegexTests {
10381064
}
10391065

10401066
func testParseErrors() {
1067+
// MARK: Closing delimiters.
10411068

1042-
func performErrorTest(_ input: String, _ expecting: String) {
1043-
// // Quick pattern match against AST to extract error nodes
1044-
// let ast = parse2(input)
1045-
// print(ast)
1046-
}
1069+
diagnosticTest("(", .expected(")"))
10471070

1048-
performErrorTest("(", "")
1071+
diagnosticTest(#"\u{5"#, .expected("}"))
1072+
diagnosticTest(#"\x{5"#, .expected("}"))
1073+
diagnosticTest(#"\N{A"#, .expected("}"))
1074+
diagnosticTest(#"\N{U+A"#, .expected("}"))
1075+
diagnosticTest(#"\p{a"#, .expected("}"))
1076+
diagnosticTest(#"\p{a="#, .expected("}"))
1077+
diagnosticTest(#"(?#"#, .expected(")"))
1078+
diagnosticTest(#"(?x"#, .expected(")"))
10491079

1080+
diagnosticTest(#"(?"#, .expectedGroupSpecifier)
1081+
diagnosticTest(#"(?^"#, .expected(")"))
1082+
diagnosticTest(#"(?^i"#, .expected(")"))
10501083

1051-
}
1084+
diagnosticTest(#"(?y)"#, .expected("{"))
1085+
diagnosticTest(#"(?y{)"#, .expected("g"))
1086+
diagnosticTest(#"(?y{g)"#, .expected("}"))
1087+
diagnosticTest(#"(?y{x})"#, .expected("g"))
10521088

1053-
}
1089+
diagnosticTest(#"(?P"#, .expected(")"))
1090+
diagnosticTest(#"(?R"#, .expected(")"))
10541091

1092+
diagnosticTest(#"\Qab"#, .expected("\\E"))
1093+
diagnosticTest("\\Qab\\", .expected("\\E"))
1094+
diagnosticTest(#""ab"#, .expected("\""), syntax: .experimental)
1095+
diagnosticTest(#""ab\""#, .expected("\""), syntax: .experimental)
1096+
1097+
// MARK: Text Segment options
1098+
1099+
diagnosticTest("(?-y{g})", .cannotRemoveTextSegmentOptions)
1100+
diagnosticTest("(?-y{w})", .cannotRemoveTextSegmentOptions)
1101+
1102+
// MARK: Group specifiers
1103+
1104+
diagnosticTest(#"(*"#, .misc("Quantifier '*' must follow operand"))
1105+
1106+
diagnosticTest(#"(?k)"#, .unknownGroupKind("?k"))
1107+
diagnosticTest(#"(?P#)"#, .invalidMatchingOption("#"))
1108+
1109+
// MARK: Matching options
1110+
1111+
diagnosticTest(#"(?^-"#, .cannotRemoveMatchingOptionsAfterCaret)
1112+
diagnosticTest(#"(?^-)"#, .cannotRemoveMatchingOptionsAfterCaret)
1113+
diagnosticTest(#"(?^i-"#, .cannotRemoveMatchingOptionsAfterCaret)
1114+
diagnosticTest(#"(?^i-m)"#, .cannotRemoveMatchingOptionsAfterCaret)
1115+
1116+
// MARK: Quotes
1117+
1118+
diagnosticTest(#"\k''"#, .expectedNonEmptyContents)
1119+
diagnosticTest(#"(?&)"#, .expectedNonEmptyContents)
1120+
diagnosticTest(#"(?P>)"#, .expectedNonEmptyContents)
1121+
}
1122+
}

0 commit comments

Comments
 (0)