Skip to content

Commit 3e1e088

Browse files
committed
Add tests
1 parent e3d7ad7 commit 3e1e088

File tree

2 files changed

+90
-70
lines changed

2 files changed

+90
-70
lines changed

Tests/RegexTests/CompileTests.swift

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ extension RegexTests {
147147
for regex: String,
148148
syntax: SyntaxOptions = .traditional,
149149
semanticLevel: RegexSemanticLevel? = nil,
150-
contains targets: Set<Instruction.OpCode>,
150+
contains targets: Set<Instruction.OpCode> = [],
151+
doesNotContain invalid: Set<Instruction.OpCode> = [],
151152
file: StaticString = #file,
152153
line: UInt = #line
153154
) {
@@ -158,41 +159,22 @@ extension RegexTests {
158159
if targets.contains(inst.opcode) {
159160
found.insert(inst.opcode)
160161
}
161-
}
162162

163-
if !found.isSuperset(of: targets) {
164-
XCTFail(
165-
"Compiled regex '\(regex)' did not contain desired opcodes. Wanted: \(targets), found: \(found)",
166-
file: file,
167-
line: line)
168-
}
169-
} catch {
170-
XCTFail(
171-
"Failed to compile regex '\(regex)': \(error)",
172-
file: file,
173-
line: line)
174-
}
175-
}
176-
177-
private func expectProgram(
178-
for regex: String,
179-
syntax: SyntaxOptions = .traditional,
180-
semanticLevel: RegexSemanticLevel? = nil,
181-
doesNotContain targets: Set<Instruction.OpCode>,
182-
file: StaticString = #file,
183-
line: UInt = #line
184-
) {
185-
do {
186-
let prog = try _compileRegex(regex, syntax, semanticLevel)
187-
for inst in prog.engine.instructions {
188-
if targets.contains(inst.opcode) {
163+
if invalid.contains(inst.opcode) {
189164
XCTFail(
190165
"Compiled regex '\(regex)' contains incorrect opcode \(inst.opcode)",
191166
file: file,
192167
line: line)
193168
return
194169
}
195170
}
171+
172+
if !found.isSuperset(of: targets) {
173+
XCTFail(
174+
"Compiled regex '\(regex)' did not contain desired opcodes. Wanted: \(targets), found: \(found)",
175+
file: file,
176+
line: line)
177+
}
196178
} catch {
197179
XCTFail(
198180
"Failed to compile regex '\(regex)': \(error)",
@@ -202,10 +184,45 @@ extension RegexTests {
202184
}
203185

204186
func testBitsetCompile() {
205-
expectProgram(for: "[abc]", contains: [.matchBitset])
206-
expectProgram(for: "[abc]", doesNotContain: [.consumeBy])
187+
expectProgram(
188+
for: "[abc]",
189+
contains: [.matchBitset],
190+
doesNotContain: [.consumeBy, .matchBitsetScalar])
191+
expectProgram(
192+
for: "[abc]",
193+
semanticLevel: .unicodeScalar,
194+
contains: [.matchBitsetScalar],
195+
doesNotContain: [.matchBitset, .consumeBy])
196+
}
207197

208-
expectProgram(for: "[abc]", semanticLevel: .unicodeScalar, doesNotContain: [.matchBitset])
209-
expectProgram(for: "[abc]", semanticLevel: .unicodeScalar, contains: [.matchBitsetScalar])
198+
func testScalarOptimizeCompilation() {
199+
expectProgram(
200+
for: "abcd",
201+
contains: [.matchScalar, .matchScalarUnchecked],
202+
doesNotContain: [.matchSequence, .consumeBy])
203+
expectProgram(
204+
for: "a",
205+
contains: [.matchScalar],
206+
doesNotContain: [.matchSequence, .consumeBy, .matchScalarUnchecked])
207+
expectProgram(
208+
for: "aaa\u{301}",
209+
contains: [.matchSequence],
210+
doesNotContain: [.matchScalar, .consumeBy, .matchScalarUnchecked])
211+
212+
expectProgram(
213+
for: "abcd",
214+
semanticLevel: .unicodeScalar,
215+
contains: [.matchScalarUnchecked],
216+
doesNotContain: [.matchSequence, .consumeBy, .matchScalar])
217+
expectProgram(
218+
for: "a",
219+
semanticLevel: .unicodeScalar,
220+
contains: [.matchScalarUnchecked],
221+
doesNotContain: [.matchSequence, .consumeBy, .matchScalar])
222+
expectProgram(
223+
for: "aaa\u{301}",
224+
semanticLevel: .unicodeScalar,
225+
contains: [.matchScalarUnchecked],
226+
doesNotContain: [.matchSequence, .consumeBy, .matchScalar])
210227
}
211228
}

Tests/RegexTests/MatchTests.swift

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ func _firstMatch(
2424
_ regexStr: String,
2525
input: String,
2626
validateOptimizations: Bool,
27-
syntax: SyntaxOptions = .traditional
27+
semanticLevel: RegexSemanticLevel = .graphemeCluster,
28+
syntax: SyntaxOptions = .traditional,
29+
file: StaticString,
30+
line: UInt
2831
) throws -> (String, [String?]) {
29-
var regex = try Regex(regexStr, syntax: syntax)
32+
var regex = try Regex(regexStr, syntax: syntax).matchingSemantics(semanticLevel)
3033
guard let result = try regex.firstMatch(in: input) else {
3134
throw MatchError("match not found for \(regexStr) in \(input)")
3235
}
@@ -35,13 +38,15 @@ func _firstMatch(
3538
if validateOptimizations {
3639
regex._setCompilerOptionsForTesting(.disableOptimizations)
3740
guard let unoptResult = try regex.firstMatch(in: input) else {
38-
XCTFail("Optimized regex for \(regexStr) matched on \(input) when unoptimized regex did not")
41+
XCTFail("Optimized regex for \(regexStr) matched on \(input) when unoptimized regex did not", file: file, line: line)
3942
throw MatchError("match not found for unoptimized \(regexStr) in \(input)")
4043
}
4144
XCTAssertEqual(
4245
String(input[result.range]),
4346
String(input[unoptResult.range]),
44-
"Unoptimized regex returned a different result")
47+
"Unoptimized regex returned a different result",
48+
file: file,
49+
line: line)
4550
}
4651
return (String(input[result.range]), caps.map { $0.map(String.init) })
4752
}
@@ -55,6 +60,7 @@ func flatCaptureTest(
5560
dumpAST: Bool = false,
5661
xfail: Bool = false,
5762
validateOptimizations: Bool = true,
63+
semanticLevel: RegexSemanticLevel = .graphemeCluster,
5864
file: StaticString = #file,
5965
line: UInt = #line
6066
) {
@@ -64,7 +70,10 @@ func flatCaptureTest(
6470
regex,
6571
input: test,
6672
validateOptimizations: validateOptimizations,
67-
syntax: syntax
73+
semanticLevel: semanticLevel,
74+
syntax: syntax,
75+
file: file,
76+
line: line
6877
) else {
6978
if expect == nil {
7079
continue
@@ -114,6 +123,7 @@ func matchTest(
114123
dumpAST: Bool = false,
115124
xfail: Bool = false,
116125
validateOptimizations: Bool = true,
126+
semanticLevel: RegexSemanticLevel = .graphemeCluster,
117127
file: StaticString = #file,
118128
line: UInt = #line
119129
) {
@@ -127,6 +137,7 @@ func matchTest(
127137
dumpAST: dumpAST,
128138
xfail: xfail,
129139
validateOptimizations: validateOptimizations,
140+
semanticLevel: semanticLevel,
130141
file: file,
131142
line: line)
132143
}
@@ -144,6 +155,7 @@ func firstMatchTest(
144155
dumpAST: Bool = false,
145156
xfail: Bool = false,
146157
validateOptimizations: Bool = true,
158+
semanticLevel: RegexSemanticLevel = .graphemeCluster,
147159
file: StaticString = #filePath,
148160
line: UInt = #line
149161
) {
@@ -152,12 +164,15 @@ func firstMatchTest(
152164
regex,
153165
input: input,
154166
validateOptimizations: validateOptimizations,
155-
syntax: syntax)
167+
semanticLevel: semanticLevel,
168+
syntax: syntax,
169+
file: file,
170+
line: line)
156171

157172
if xfail {
158173
XCTAssertNotEqual(found, match, file: file, line: line)
159174
} else {
160-
XCTAssertEqual(found, match, file: file, line: line)
175+
XCTAssertEqual(found, match, "Incorrect match", file: file, line: line)
161176
}
162177
} catch {
163178
// FIXME: This allows non-matches to succeed even when xfail'd
@@ -618,14 +633,12 @@ extension RegexTests {
618633
// interpreted as matching the scalars "\r" or "\n".
619634
// It does not fully match the character "\r\n" because the character class
620635
// in scalar mode will only match one scalar
621-
do {
622-
let regex = try Regex("[\r\n]").matchingSemantics(.unicodeScalar)
623-
XCTAssertEqual("\r", try regex.wholeMatch(in: "\r")?.0)
624-
XCTAssertEqual("\n", try regex.wholeMatch(in: "\n")?.0)
625-
XCTAssertEqual(nil, try regex.wholeMatch(in: "\r\n")?.0)
626-
} catch {
627-
XCTFail("\(error)", file: #filePath, line: #line)
628-
}
636+
matchTest(
637+
"^[\r\n]$",
638+
("\r", true),
639+
("\n", true),
640+
("\r\n", false),
641+
semanticLevel: .unicodeScalar)
629642

630643
matchTest("[^\r\n]",
631644
("\r\n", false),
@@ -635,26 +648,15 @@ extension RegexTests {
635648
("\n", true),
636649
("\r", true),
637650
("\r\n", false))
638-
639-
do {
640-
let r = #"[a]\u0301"#
641-
var regex = try Regex(r).matchingSemantics(.unicodeScalar)
642-
let input: String = "a\u{301}"
643-
// Should match in unicode semantic mode because the character class
644-
// should consume the a and then matchScalar should match the \u{301}
645-
regex._debug()
646-
XCTAssertEqual("a\u{301}", try regex.wholeMatch(in: input)?.0)
647-
// validate this is the same in unoptimized mode
648-
regex._setCompilerOptionsForTesting(.disableOptimizations)
649-
XCTAssertEqual("a\u{301}", try regex.wholeMatch(in: input)?.0)
650-
651-
// Should not match in grapheme semantic mode because a\u{301} is
652-
// a single character
653-
matchTest(r,
654-
(input, false))
655-
} catch {
656-
XCTFail("\(error)", file: #filePath, line: #line)
657-
}
651+
652+
matchTest(
653+
#"[a]\u0301"#,
654+
("a\u{301}", false),
655+
semanticLevel: .graphemeCluster)
656+
matchTest(
657+
#"[a]\u0301"#,
658+
("a\u{301}", true),
659+
semanticLevel: .unicodeScalar)
658660

659661
firstMatchTest("[-]", input: "123-abcxyz", match: "-")
660662

@@ -1853,13 +1855,14 @@ extension RegexTests {
18531855

18541856
// TODO: Add test for grapheme boundaries at start/end of match
18551857

1858+
// Testing the matchScalar optimization for ascii quoted literals and characters
18561859
func testScalarOptimization() throws {
18571860
// check that we are correctly doing the boundary check after matchScalar
18581861
firstMatchTest("a", input: "a\u{301}", match: nil)
18591862
firstMatchTest("aa", input: "aa\u{301}", match: nil)
1860-
// let regex = "aa"
1861-
// let input = "aa\u{301}"
1862-
// XCTAssertEqual(regex.firstMatch(of: input), nil)
1863+
1864+
firstMatchTest("a", input: "a\u{301}", match: "a", semanticLevel: .unicodeScalar)
1865+
firstMatchTest("aa", input: "aa\u{301}", match: "aa", semanticLevel: .unicodeScalar)
18631866
}
18641867

18651868
func testCase() {

0 commit comments

Comments
 (0)