@@ -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