@@ -151,3 +151,209 @@ final class QuickReverseASCIICharacterTests: XCTestCase {
151
151
XCTAssertTrue ( isCRLF)
152
152
}
153
153
}
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