Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2d3c691

Browse files
committedJan 20, 2025
Add ASCII _quickMatch and _quickReverseMatch tests
1 parent cf53ddd commit 2d3c691

File tree

2 files changed

+206
-2
lines changed

2 files changed

+206
-2
lines changed
 

‎Sources/_StringProcessing/Unicode/ASCII.swift

-2
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,4 @@ extension String {
258258
return (previous, asciiValue._asciiIsWord)
259259
}
260260
}
261-
262261
}
263-

‎Tests/MatchingEngineTests/ASCIITests.swift

+206
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,209 @@ final class QuickReverseASCIICharacterTests: XCTestCase {
151151
XCTAssertTrue(isCRLF)
152152
}
153153
}
154+
155+
final class ASCIIQuickMatchTests: XCTestCase {
156+
func testAny() throws {
157+
try _test(matching: .any, against: "!")
158+
try _test(matching: .anyGrapheme, against: "!")
159+
}
160+
161+
func testDigit() throws {
162+
try _test(matching: .digit, against: "1")
163+
try _test(matching: .digit, against: "a", shouldMatch: false)
164+
}
165+
166+
func testHorizontalWhitespace() throws {
167+
try _test(matching: .horizontalWhitespace, against: " ")
168+
try _test(matching: .horizontalWhitespace, against: "\t")
169+
try _test(matching: .horizontalWhitespace, against: "\n", shouldMatch: false)
170+
}
171+
172+
func testVerticalWhitespace() throws {
173+
try _test(matching: .verticalWhitespace, against: "\n")
174+
try _test(matching: .verticalWhitespace, against: "\t", shouldMatch: false)
175+
try _test(matching: .newlineSequence, against: "\n")
176+
try _test(matching: .newlineSequence, against: "\t", shouldMatch: false)
177+
}
178+
179+
func testVerticalWhitespaceMatchesCRLF() throws {
180+
let crlf = "\r\n"
181+
182+
// When using scalar semantics:
183+
// The next index should be the index of the "\n" character
184+
try _test(
185+
matching: .verticalWhitespace,
186+
against: crlf,
187+
expectedNext: crlf.utf8.firstIndex(of: ._lineFeed)
188+
)
189+
190+
// When not using scalar semantics:
191+
// The next index should be the index after the whole \r\n sequence (the end index)
192+
try _test(
193+
matching: .verticalWhitespace,
194+
against: crlf,
195+
isScalarSemantics: false
196+
)
197+
}
198+
199+
func testWhitespace() throws {
200+
try _test(matching: .whitespace, against: " ")
201+
try _test(matching: .whitespace, against: "\t")
202+
try _test(matching: .whitespace, against: "\n")
203+
try _test(matching: .whitespace, against: "a", shouldMatch: false)
204+
}
205+
206+
func testWhitespaceCRLF() throws {
207+
// Given
208+
let crlf = "\r\n"
209+
210+
// When using scalar semantics:
211+
// The next index should be the index of the "\n" character
212+
try _test(
213+
matching: .whitespace,
214+
against: crlf,
215+
expectedNext: crlf.utf8.firstIndex(of: ._lineFeed)
216+
)
217+
218+
// When not using scalar semantics:
219+
// The next index should be the index after the whole \r\n sequence (the end index)
220+
try _test(
221+
matching: .whitespace,
222+
against: crlf,
223+
isScalarSemantics: false
224+
)
225+
}
226+
227+
func testWord() throws {
228+
// Given
229+
try _test(matching: .word, against: "a")
230+
try _test(matching: .word, against: "1")
231+
try _test(matching: .word, against: "_")
232+
try _test(matching: .word, against: "-", shouldMatch: false)
233+
}
234+
235+
private func _test(
236+
matching cc: _CharacterClassModel.Representation,
237+
against sut: String,
238+
isScalarSemantics: Bool = true,
239+
shouldMatch: Bool = true,
240+
expectedNext: String.Index? = nil
241+
) throws {
242+
// When
243+
let result = sut._quickMatch(
244+
cc,
245+
at: sut.startIndex,
246+
limitedBy: sut.endIndex,
247+
isScalarSemantics: isScalarSemantics
248+
)
249+
250+
// Then
251+
let (next, matched) = try XCTUnwrap(result)
252+
XCTAssertEqual(matched, shouldMatch)
253+
XCTAssertEqual(next, expectedNext ?? sut.endIndex)
254+
}
255+
}
256+
257+
final class ASCIIQuickReverseMatchTests: XCTestCase {
258+
func testAny() throws {
259+
try _test(matching: .any, against: "1!")
260+
try _test(matching: .anyGrapheme, against: "1!")
261+
}
262+
263+
func testDigit() throws {
264+
try _test(matching: .digit, against: "a1")
265+
try _test(matching: .digit, against: "1a", shouldMatch: false)
266+
}
267+
268+
func testHorizontalWhitespace() throws {
269+
try _test(matching: .horizontalWhitespace, against: "a ")
270+
try _test(matching: .horizontalWhitespace, against: "a\t")
271+
try _test(matching: .horizontalWhitespace, against: "a\n", shouldMatch: false)
272+
}
273+
274+
func testVerticalWhitespace() throws {
275+
try _test(matching: .verticalWhitespace, against: "a\n")
276+
try _test(matching: .verticalWhitespace, against: "a\t", shouldMatch: false)
277+
}
278+
279+
func testVerticalWhitespaceMatchesCRLF() throws {
280+
let sut = "a\r\n"
281+
282+
// When using scalar semantics:
283+
// The next index should be the index of the "\n" character
284+
try _test(
285+
matching: .verticalWhitespace,
286+
against: sut,
287+
at: sut.utf8.index(before: sut.utf8.endIndex),
288+
expectedPrevious: sut.utf8.firstIndex(of: ._carriageReturn)
289+
)
290+
291+
// When not using scalar semantics:
292+
// The next index should be the index after the whole \r\n sequence (the end index)
293+
try _test(
294+
matching: .verticalWhitespace,
295+
against: sut,
296+
isScalarSemantics: false
297+
)
298+
}
299+
300+
func testWhitespace() throws {
301+
try _test(matching: .whitespace, against: "a ")
302+
try _test(matching: .whitespace, against: "a\t")
303+
try _test(matching: .whitespace, against: "a\n")
304+
try _test(matching: .whitespace, against: " a", shouldMatch: false)
305+
}
306+
307+
func testWhitespaceCRLF() throws {
308+
// Given
309+
let sut = "a\r\n"
310+
311+
// When using scalar semantics:
312+
// The previous index should be the index of the "\r" character
313+
try _test(
314+
matching: .whitespace,
315+
against: sut,
316+
at: sut.utf8.index(before: sut.utf8.endIndex),
317+
expectedPrevious: sut.utf8.firstIndex(of: ._carriageReturn)
318+
)
319+
320+
// When not using scalar semantics:
321+
// The previous index should be the index before the whole \r\n sequence
322+
// (the start index)
323+
try _test(
324+
matching: .whitespace,
325+
against: sut,
326+
isScalarSemantics: false
327+
)
328+
}
329+
330+
func testWord() throws {
331+
// Given
332+
try _test(matching: .word, against: "!a")
333+
try _test(matching: .word, against: "!1")
334+
try _test(matching: .word, against: "!_")
335+
try _test(matching: .word, against: "a-", shouldMatch: false)
336+
}
337+
338+
private func _test(
339+
matching cc: _CharacterClassModel.Representation,
340+
against sut: String,
341+
at index: String.Index? = nil,
342+
isScalarSemantics: Bool = true,
343+
shouldMatch: Bool = true,
344+
expectedPrevious: String.Index? = nil
345+
) throws {
346+
// When
347+
let result = sut._quickReverseMatch(
348+
cc,
349+
at: index ?? sut.index(before: sut.endIndex),
350+
limitedBy: sut.startIndex,
351+
isScalarSemantics: isScalarSemantics
352+
)
353+
354+
// Then
355+
let (previous, matched) = try XCTUnwrap(result)
356+
XCTAssertEqual(matched, shouldMatch)
357+
XCTAssertEqual(previous, expectedPrevious ?? sut.startIndex)
358+
}
359+
}

0 commit comments

Comments
 (0)
Please sign in to comment.