1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. |
4 | ** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the test suite of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <qstring.h> |
32 | #include <qlist.h> |
33 | #include <qstringlist.h> |
34 | #include <qhash.h> |
35 | |
36 | #include <qobject.h> |
37 | #include <qregularexpression.h> |
38 | #include <qthread.h> |
39 | |
40 | Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) |
41 | Q_DECLARE_METATYPE(QRegularExpression::MatchType) |
42 | Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) |
43 | |
44 | class tst_QRegularExpression : public QObject |
45 | { |
46 | Q_OBJECT |
47 | |
48 | private slots: |
49 | void defaultConstructors(); |
50 | void gettersSetters_data(); |
51 | void gettersSetters(); |
52 | void escape_data(); |
53 | void escape(); |
54 | void validity_data(); |
55 | void validity(); |
56 | void patternOptions_data(); |
57 | void patternOptions(); |
58 | void normalMatch_data(); |
59 | void normalMatch(); |
60 | void partialMatch_data(); |
61 | void partialMatch(); |
62 | void globalMatch_data(); |
63 | void globalMatch(); |
64 | void serialize_data(); |
65 | void serialize(); |
66 | void operatoreq_data(); |
67 | void operatoreq(); |
68 | void captureCount_data(); |
69 | void captureCount(); |
70 | void captureNames_data(); |
71 | void captureNames(); |
72 | void captureNamesNul(); |
73 | void pcreJitStackUsage_data(); |
74 | void pcreJitStackUsage(); |
75 | void regularExpressionMatch_data(); |
76 | void regularExpressionMatch(); |
77 | void JOptionUsage_data(); |
78 | void JOptionUsage(); |
79 | void QStringAndQStringRefEquivalence(); |
80 | void threadSafety_data(); |
81 | void threadSafety(); |
82 | |
83 | void returnsViewsIntoOriginalString(); |
84 | void wildcard_data(); |
85 | void wildcard(); |
86 | void testInvalidWildcard_data(); |
87 | void testInvalidWildcard(); |
88 | |
89 | private: |
90 | void provideRegularExpressions(); |
91 | }; |
92 | |
93 | struct Match |
94 | { |
95 | Match() |
96 | { |
97 | clear(); |
98 | } |
99 | |
100 | void clear() |
101 | { |
102 | isValid = false; |
103 | hasMatch = false; |
104 | hasPartialMatch = false; |
105 | captured.clear(); |
106 | namedCaptured.clear(); |
107 | } |
108 | |
109 | bool isValid; |
110 | bool hasMatch; |
111 | bool hasPartialMatch; |
112 | QStringList captured; |
113 | QHash<QString, QString> namedCaptured; |
114 | }; |
115 | QT_BEGIN_NAMESPACE |
116 | Q_DECLARE_TYPEINFO(Match, Q_MOVABLE_TYPE); |
117 | QT_END_NAMESPACE |
118 | |
119 | Q_DECLARE_METATYPE(Match) |
120 | |
121 | bool operator==(const QRegularExpressionMatch &rem, const Match &m) |
122 | { |
123 | if (rem.isValid() != m.isValid) |
124 | return false; |
125 | if (!rem.isValid()) |
126 | return true; |
127 | if ((rem.hasMatch() != m.hasMatch) || (rem.hasPartialMatch() != m.hasPartialMatch)) |
128 | return false; |
129 | if (rem.hasMatch() || rem.hasPartialMatch()) { |
130 | if (rem.lastCapturedIndex() != (m.captured.size() - 1)) |
131 | return false; |
132 | for (int i = 0; i <= rem.lastCapturedIndex(); ++i) { |
133 | QString remCaptured = rem.captured(nth: i); |
134 | QString mCaptured = m.captured.at(i); |
135 | if (remCaptured != mCaptured |
136 | || remCaptured.isNull() != mCaptured.isNull() |
137 | || remCaptured.isEmpty() != mCaptured.isEmpty()) { |
138 | return false; |
139 | } |
140 | } |
141 | |
142 | for (auto it = m.namedCaptured.begin(), end = m.namedCaptured.end(); it != end; ++it) { |
143 | const QString remCaptured = rem.captured(name: it.key()); |
144 | const QString mCaptured = it.value(); |
145 | if (remCaptured != mCaptured |
146 | || remCaptured.isNull() != mCaptured.isNull() |
147 | || remCaptured.isEmpty() != mCaptured.isEmpty()) { |
148 | return false; |
149 | } |
150 | } |
151 | } |
152 | |
153 | return true; |
154 | } |
155 | |
156 | bool operator==(const Match &m, const QRegularExpressionMatch &rem) |
157 | { |
158 | return operator==(rem, m); |
159 | } |
160 | |
161 | bool operator!=(const QRegularExpressionMatch &rem, const Match &m) |
162 | { |
163 | return !operator==(rem, m); |
164 | } |
165 | |
166 | bool operator!=(const Match &m, const QRegularExpressionMatch &rem) |
167 | { |
168 | return !operator==(m, rem); |
169 | } |
170 | |
171 | |
172 | bool operator==(const QRegularExpressionMatchIterator &iterator, const QVector<Match> &expectedMatchList) |
173 | { |
174 | QRegularExpressionMatchIterator i = iterator; |
175 | |
176 | for (const Match &expectedMatch : expectedMatchList) { |
177 | if (!i.hasNext()) |
178 | return false; |
179 | |
180 | QRegularExpressionMatch match = i.next(); |
181 | if (match != expectedMatch) |
182 | return false; |
183 | } |
184 | |
185 | if (i.hasNext()) |
186 | return false; |
187 | |
188 | return true; |
189 | } |
190 | |
191 | bool operator==(const QVector<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator) |
192 | { |
193 | return operator==(iterator, expectedMatchList); |
194 | } |
195 | |
196 | bool operator!=(const QRegularExpressionMatchIterator &iterator, const QVector<Match> &expectedMatchList) |
197 | { |
198 | return !operator==(iterator, expectedMatchList); |
199 | } |
200 | |
201 | bool operator!=(const QVector<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator) |
202 | { |
203 | return !operator==(expectedMatchList, iterator); |
204 | } |
205 | |
206 | void consistencyCheck(const QRegularExpressionMatch &match) |
207 | { |
208 | if (match.isValid()) { |
209 | QVERIFY(match.regularExpression().isValid()); |
210 | QVERIFY(!(match.hasMatch() && match.hasPartialMatch())); |
211 | |
212 | if (match.hasMatch() || match.hasPartialMatch()) { |
213 | QVERIFY(match.lastCapturedIndex() >= 0); |
214 | if (match.hasPartialMatch()) |
215 | QVERIFY(match.lastCapturedIndex() == 0); |
216 | |
217 | for (int i = 0; i <= match.lastCapturedIndex(); ++i) { |
218 | int startPos = match.capturedStart(nth: i); |
219 | int endPos = match.capturedEnd(nth: i); |
220 | int length = match.capturedLength(nth: i); |
221 | QString captured = match.captured(nth: i); |
222 | QStringRef capturedRef = match.capturedRef(nth: i); |
223 | QStringView capturedView = match.capturedView(nth: i); |
224 | |
225 | if (!captured.isNull()) { |
226 | QVERIFY(startPos >= 0); |
227 | QVERIFY(endPos >= 0); |
228 | QVERIFY(length >= 0); |
229 | QVERIFY(endPos >= startPos); |
230 | QVERIFY((endPos - startPos) == length); |
231 | QVERIFY(captured == capturedRef); |
232 | QVERIFY(captured == capturedView); |
233 | } else { |
234 | QVERIFY(startPos == -1); |
235 | QVERIFY(endPos == -1); |
236 | QVERIFY((endPos - startPos) == length); |
237 | QVERIFY(capturedRef.isNull()); |
238 | QVERIFY(capturedView.isNull()); |
239 | } |
240 | } |
241 | } |
242 | } else { |
243 | QVERIFY(!match.hasMatch()); |
244 | QVERIFY(!match.hasPartialMatch()); |
245 | QVERIFY(match.captured(0).isNull()); |
246 | QVERIFY(match.capturedStart(0) == -1); |
247 | QVERIFY(match.capturedEnd(0) == -1); |
248 | QVERIFY(match.capturedLength(0) == 0); |
249 | } |
250 | } |
251 | |
252 | void consistencyCheck(const QRegularExpressionMatchIterator &iterator) |
253 | { |
254 | QRegularExpressionMatchIterator i(iterator); // make a copy, we modify it |
255 | if (i.isValid()) { |
256 | while (i.hasNext()) { |
257 | QRegularExpressionMatch peeked = i.peekNext(); |
258 | QRegularExpressionMatch match = i.next(); |
259 | consistencyCheck(match: peeked); |
260 | consistencyCheck(match); |
261 | QVERIFY(match.isValid()); |
262 | QVERIFY(match.hasMatch() || match.hasPartialMatch()); |
263 | QCOMPARE(i.regularExpression(), match.regularExpression()); |
264 | QCOMPARE(i.matchOptions(), match.matchOptions()); |
265 | QCOMPARE(i.matchType(), match.matchType()); |
266 | |
267 | QVERIFY(peeked.isValid() == match.isValid()); |
268 | QVERIFY(peeked.hasMatch() == match.hasMatch()); |
269 | QVERIFY(peeked.hasPartialMatch() == match.hasPartialMatch()); |
270 | QVERIFY(peeked.lastCapturedIndex() == match.lastCapturedIndex()); |
271 | for (int i = 0; i <= peeked.lastCapturedIndex(); ++i) { |
272 | QVERIFY(peeked.captured(i) == match.captured(i)); |
273 | QVERIFY(peeked.capturedStart(i) == match.capturedStart(i)); |
274 | QVERIFY(peeked.capturedEnd(i) == match.capturedEnd(i)); |
275 | } |
276 | } |
277 | } else { |
278 | QVERIFY(!i.hasNext()); |
279 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionMatchIterator::peekNext() called on an iterator already at end" ); |
280 | QRegularExpressionMatch peeked = i.peekNext(); |
281 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionMatchIterator::next() called on an iterator already at end" ); |
282 | QRegularExpressionMatch match = i.next(); |
283 | consistencyCheck(match: peeked); |
284 | consistencyCheck(match); |
285 | QVERIFY(!match.isValid()); |
286 | QVERIFY(!peeked.isValid()); |
287 | } |
288 | |
289 | } |
290 | |
291 | template<typename Result> |
292 | static void prepareResultForNoMatchType(Result *r, const Result &orig) |
293 | { |
294 | Q_UNUSED(r); |
295 | Q_UNUSED(orig); |
296 | } |
297 | |
298 | static void prepareResultForNoMatchType(Match *m, const Match &orig) |
299 | { |
300 | m->isValid = orig.isValid; |
301 | } |
302 | |
303 | template<typename QREMatch, typename QREMatchFunc, typename Subject, typename Result> |
304 | static void testMatchImpl(const QRegularExpression ®exp, |
305 | QREMatchFunc matchingMethod, |
306 | const Subject &subject, |
307 | int offset, |
308 | QRegularExpression::MatchType matchType, |
309 | QRegularExpression::MatchOptions matchOptions, |
310 | const Result &result) |
311 | { |
312 | { |
313 | const QREMatch m = (regexp.*matchingMethod)(subject, offset, matchType, matchOptions); |
314 | consistencyCheck(m); |
315 | QVERIFY(m == result); |
316 | QCOMPARE(m.regularExpression(), regexp); |
317 | QCOMPARE(m.matchType(), matchType); |
318 | QCOMPARE(m.matchOptions(), matchOptions); |
319 | } |
320 | { |
321 | // ignore the expected results provided by the match object -- |
322 | // we'll never get any result when testing the NoMatch type. |
323 | // Just check the validity of the match here. |
324 | Result realMatch; |
325 | prepareResultForNoMatchType(&realMatch, result); |
326 | |
327 | const QREMatch m = (regexp.*matchingMethod)(subject, offset, QRegularExpression::NoMatch, matchOptions); |
328 | consistencyCheck(m); |
329 | QVERIFY(m == realMatch); |
330 | QCOMPARE(m.regularExpression(), regexp); |
331 | QCOMPARE(m.matchType(), QRegularExpression::NoMatch); |
332 | QCOMPARE(m.matchOptions(), matchOptions); |
333 | } |
334 | } |
335 | |
336 | template<typename QREMatch, typename QREMatchFuncForString, typename QREMatchFuncForStringRef, typename Result> |
337 | static void testMatch(const QRegularExpression ®exp, |
338 | QREMatchFuncForString matchingMethodForString, |
339 | QREMatchFuncForStringRef matchingMethodForStringRef, |
340 | const QString &subject, |
341 | int offset, |
342 | QRegularExpression::MatchType matchType, |
343 | QRegularExpression::MatchOptions matchOptions, |
344 | const Result &result) |
345 | { |
346 | // test with QString as subject type |
347 | testMatchImpl<QREMatch>(regexp, matchingMethodForString, subject, offset, matchType, matchOptions, result); |
348 | |
349 | // test with QStringRef as subject type |
350 | testMatchImpl<QREMatch>(regexp, |
351 | matchingMethodForStringRef, |
352 | QStringRef(&subject, 0, subject.length()), |
353 | offset, |
354 | matchType, |
355 | matchOptions, |
356 | result); |
357 | } |
358 | |
359 | typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringPMF)(const QString &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; |
360 | typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringRefPMF)(const QStringRef &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; |
361 | typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringPMF)(const QString &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; |
362 | typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringRefPMF)(const QStringRef &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; |
363 | |
364 | void tst_QRegularExpression::provideRegularExpressions() |
365 | { |
366 | QTest::addColumn<QString>(name: "pattern" ); |
367 | QTest::addColumn<QRegularExpression::PatternOptions>(name: "patternOptions" ); |
368 | |
369 | QTest::newRow(dataTag: "emptynull01" ) << QString() |
370 | << QRegularExpression::PatternOptions{}; |
371 | QTest::newRow(dataTag: "emptynull02" ) << QString() |
372 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption |
373 | | QRegularExpression::DotMatchesEverythingOption |
374 | | QRegularExpression::MultilineOption); |
375 | QTest::newRow(dataTag: "emptynull03" ) << "" |
376 | << QRegularExpression::PatternOptions{}; |
377 | QTest::newRow(dataTag: "emptynull04" ) << "" |
378 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption |
379 | | QRegularExpression::DotMatchesEverythingOption |
380 | | QRegularExpression::MultilineOption); |
381 | |
382 | QTest::newRow(dataTag: "regexp01" ) << "a pattern" |
383 | << QRegularExpression::PatternOptions{}; |
384 | QTest::newRow(dataTag: "regexp02" ) << "^a (.*) more complicated(?<P>pattern)$" |
385 | << QRegularExpression::PatternOptions{}; |
386 | QTest::newRow(dataTag: "regexp03" ) << "(?:a) pAttErN" |
387 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption); |
388 | QTest::newRow(dataTag: "regexp04" ) << "a\nmultiline\npattern" |
389 | << QRegularExpression::PatternOptions(QRegularExpression::MultilineOption); |
390 | QTest::newRow(dataTag: "regexp05" ) << "an extended # IGNOREME\npattern" |
391 | << QRegularExpression::PatternOptions(QRegularExpression::ExtendedPatternSyntaxOption); |
392 | QTest::newRow(dataTag: "regexp06" ) << "a [sS]ingleline .* match" |
393 | << QRegularExpression::PatternOptions(QRegularExpression::DotMatchesEverythingOption); |
394 | QTest::newRow(dataTag: "regexp07" ) << "multiple.*options" |
395 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption |
396 | | QRegularExpression::DotMatchesEverythingOption |
397 | | QRegularExpression::MultilineOption |
398 | | QRegularExpression::DontCaptureOption |
399 | | QRegularExpression::InvertedGreedinessOption); |
400 | |
401 | QTest::newRow(dataTag: "unicode01" ) << QString::fromUtf8(str: "^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$" ) |
402 | << QRegularExpression::PatternOptions{}; |
403 | QTest::newRow(dataTag: "unicode02" ) << QString::fromUtf8(str: "^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$" ) |
404 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption |
405 | | QRegularExpression::DotMatchesEverythingOption |
406 | | QRegularExpression::InvertedGreedinessOption); |
407 | QTest::newRow(dataTag: "unicode03" ) << QString::fromUtf8(str: "Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f" ) |
408 | << QRegularExpression::PatternOptions{}; |
409 | QTest::newRow(dataTag: "unicode04" ) << QString::fromUtf8(str: "Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f" ) |
410 | << QRegularExpression::PatternOptions(QRegularExpression::CaseInsensitiveOption |
411 | | QRegularExpression::DotMatchesEverythingOption |
412 | | QRegularExpression::InvertedGreedinessOption); |
413 | } |
414 | |
415 | void tst_QRegularExpression::defaultConstructors() |
416 | { |
417 | QRegularExpression re; |
418 | QCOMPARE(re.pattern(), QString()); |
419 | QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); |
420 | |
421 | QRegularExpressionMatch match; |
422 | QCOMPARE(match.regularExpression(), QRegularExpression()); |
423 | QCOMPARE(match.regularExpression(), re); |
424 | QCOMPARE(match.matchType(), QRegularExpression::NoMatch); |
425 | QCOMPARE(match.matchOptions(), QRegularExpression::NoMatchOption); |
426 | QCOMPARE(match.hasMatch(), false); |
427 | QCOMPARE(match.hasPartialMatch(), false); |
428 | QCOMPARE(match.isValid(), true); |
429 | QCOMPARE(match.lastCapturedIndex(), -1); |
430 | |
431 | QRegularExpressionMatchIterator iterator; |
432 | QCOMPARE(iterator.regularExpression(), QRegularExpression()); |
433 | QCOMPARE(iterator.regularExpression(), re); |
434 | QCOMPARE(iterator.matchType(), QRegularExpression::NoMatch); |
435 | QCOMPARE(iterator.matchOptions(), QRegularExpression::NoMatchOption); |
436 | QCOMPARE(iterator.isValid(), true); |
437 | QCOMPARE(iterator.hasNext(), false); |
438 | } |
439 | |
440 | void tst_QRegularExpression::() |
441 | { |
442 | provideRegularExpressions(); |
443 | } |
444 | |
445 | void tst_QRegularExpression::() |
446 | { |
447 | QFETCH(QString, pattern); |
448 | QFETCH(QRegularExpression::PatternOptions, patternOptions); |
449 | { |
450 | QRegularExpression re; |
451 | re.setPattern(pattern); |
452 | QCOMPARE(re.pattern(), pattern); |
453 | QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); |
454 | } |
455 | { |
456 | QRegularExpression re; |
457 | re.setPatternOptions(patternOptions); |
458 | QCOMPARE(re.pattern(), QString()); |
459 | QCOMPARE(re.patternOptions(), patternOptions); |
460 | } |
461 | { |
462 | QRegularExpression re(pattern); |
463 | QCOMPARE(re.pattern(), pattern); |
464 | QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); |
465 | } |
466 | { |
467 | QRegularExpression re(pattern, patternOptions); |
468 | QCOMPARE(re.pattern(), pattern); |
469 | QCOMPARE(re.patternOptions(), patternOptions); |
470 | } |
471 | } |
472 | |
473 | void tst_QRegularExpression::escape_data() |
474 | { |
475 | QTest::addColumn<QString>(name: "string" ); |
476 | QTest::addColumn<QString>(name: "escaped" ); |
477 | QTest::newRow(dataTag: "escape01" ) << "a normal pattern" |
478 | << "a\\ normal\\ pattern" ; |
479 | |
480 | QTest::newRow(dataTag: "escape02" ) << "abcdefghijklmnopqrstuvzABCDEFGHIJKLMNOPQRSTUVZ1234567890_" |
481 | << "abcdefghijklmnopqrstuvzABCDEFGHIJKLMNOPQRSTUVZ1234567890_" ; |
482 | |
483 | QTest::newRow(dataTag: "escape03" ) << "^\\ba\\b.*(?<NAME>reg|exp)$" |
484 | << "\\^\\\\ba\\\\b\\.\\*\\(\\?\\<NAME\\>reg\\|exp\\)\\$" ; |
485 | |
486 | QString nulString("abcXabcXXabc" ); |
487 | nulString[3] = nulString[7] = nulString[8] = QChar(0, 0); |
488 | QTest::newRow(dataTag: "NUL" ) << nulString |
489 | << "abc\\0abc\\0\\0abc" ; |
490 | |
491 | QTest::newRow(dataTag: "unicode01" ) << QString::fromUtf8(str: "^s[ome] latin-1 \xc3\x80\xc3\x88\xc3\x8c\xc3\x92\xc3\x99 chars$" ) |
492 | << QString::fromUtf8(str: "\\^s\\[ome\\]\\ latin\\-1\\ \\\xc3\x80\\\xc3\x88\\\xc3\x8c\\\xc3\x92\\\xc3\x99\\ chars\\$" ); |
493 | QTest::newRow(dataTag: "unicode02" ) << QString::fromUtf8(str: "Unicode \xf0\x9d\x85\x9d \xf0\x9d\x85\x9e\xf0\x9d\x85\x9f" ) |
494 | << QString::fromUtf8(str: "Unicode\\ \\\xf0\x9d\x85\x9d\\ \\\xf0\x9d\x85\x9e\\\xf0\x9d\x85\x9f" ); |
495 | |
496 | QString unicodeAndNulString = QString::fromUtf8(str: "^\xc3\x80\xc3\x88\xc3\x8cN\xc3\x92NN\xc3\x99 chars$" ); |
497 | unicodeAndNulString[4] = unicodeAndNulString[6] = unicodeAndNulString[7] = QChar(0, 0); |
498 | QTest::newRow(dataTag: "unicode03" ) << unicodeAndNulString |
499 | << QString::fromUtf8(str: "\\^\\\xc3\x80\\\xc3\x88\\\xc3\x8c\\0\\\xc3\x92\\0\\0\\\xc3\x99\\ chars\\$" ); |
500 | } |
501 | |
502 | void tst_QRegularExpression::escape() |
503 | { |
504 | QFETCH(QString, string); |
505 | QFETCH(QString, escaped); |
506 | QCOMPARE(QRegularExpression::escape(string), escaped); |
507 | QRegularExpression re(escaped); |
508 | QCOMPARE(re.isValid(), true); |
509 | } |
510 | |
511 | void tst_QRegularExpression::validity_data() |
512 | { |
513 | QTest::addColumn<QString>(name: "pattern" ); |
514 | QTest::addColumn<bool>(name: "validity" ); |
515 | |
516 | QTest::newRow(dataTag: "valid01" ) << "a pattern" << true; |
517 | QTest::newRow(dataTag: "valid02" ) << "(a|pattern)" << true; |
518 | QTest::newRow(dataTag: "valid03" ) << "a [pP]attern" << true; |
519 | QTest::newRow(dataTag: "valid04" ) << "^(?<article>a).*(?<noun>pattern)$" << true; |
520 | QTest::newRow(dataTag: "valid05" ) << "a \\P{Ll}attern" << true; |
521 | |
522 | QTest::newRow(dataTag: "invalid01" ) << "a pattern\\" << false; |
523 | QTest::newRow(dataTag: "invalid02" ) << "(a|pattern" << false; |
524 | QTest::newRow(dataTag: "invalid03" ) << "a \\P{BLAH}attern" << false; |
525 | |
526 | QString pattern; |
527 | // 0xD800 (high surrogate) not followed by a low surrogate |
528 | pattern = "abcdef" ; |
529 | pattern[3] = QChar(0x00, 0xD8); |
530 | QTest::newRow(dataTag: "invalidUnicode01" ) << pattern << false; |
531 | } |
532 | |
533 | void tst_QRegularExpression::validity() |
534 | { |
535 | QFETCH(QString, pattern); |
536 | QFETCH(bool, validity); |
537 | QRegularExpression re(pattern); |
538 | QCOMPARE(re.isValid(), validity); |
539 | if (!validity) |
540 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object" ); |
541 | QRegularExpressionMatch match = re.match(subject: "a pattern" ); |
542 | QCOMPARE(match.isValid(), validity); |
543 | consistencyCheck(match); |
544 | |
545 | if (!validity) |
546 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object" ); |
547 | QRegularExpressionMatchIterator iterator = re.globalMatch(subject: "a pattern" ); |
548 | QCOMPARE(iterator.isValid(), validity); |
549 | } |
550 | |
551 | void tst_QRegularExpression::patternOptions_data() |
552 | { |
553 | QTest::addColumn<QRegularExpression>(name: "regexp" ); |
554 | QTest::addColumn<QString>(name: "subject" ); |
555 | QTest::addColumn<Match>(name: "match" ); |
556 | |
557 | // none of these would successfully match if the respective |
558 | // pattern option is not set |
559 | |
560 | Match m; |
561 | |
562 | m.clear(); |
563 | m.isValid = true; m.hasMatch = true; |
564 | m.captured << QString::fromUtf8(str: "AbC\xc3\xa0" ); |
565 | QTest::newRow(dataTag: "/i" ) << QRegularExpression(QString::fromUtf8(str: "abc\xc3\x80" ), QRegularExpression::CaseInsensitiveOption) |
566 | << QString::fromUtf8(str: "AbC\xc3\xa0" ) |
567 | << m; |
568 | |
569 | m.clear(); |
570 | m.isValid = true; m.hasMatch = true; |
571 | m.captured << "abc123\n678def" ; |
572 | QTest::newRow(dataTag: "/s" ) << QRegularExpression("\\Aabc.*def\\z" , QRegularExpression::DotMatchesEverythingOption) |
573 | << "abc123\n678def" |
574 | << m; |
575 | |
576 | m.clear(); |
577 | m.isValid = true; m.hasMatch = true; |
578 | m.captured << "jumped over" ; |
579 | QTest::newRow(dataTag: "/m" ) << QRegularExpression("^\\w+ \\w+$" , QRegularExpression::MultilineOption) |
580 | << "the quick fox\njumped over\nthe lazy\ndog" |
581 | << m; |
582 | |
583 | m.clear(); |
584 | m.isValid = true; m.hasMatch = true; |
585 | m.captured << "abc 123456" ; |
586 | QTest::newRow(dataTag: "/x" ) << QRegularExpression("\\w+ # a word\n" |
587 | "\\ # a space\n" |
588 | "\\w+ # another word" , |
589 | QRegularExpression::ExtendedPatternSyntaxOption) |
590 | << "abc 123456 def" |
591 | << m; |
592 | |
593 | m.clear(); |
594 | m.isValid = true; m.hasMatch = true; |
595 | m.captured << "the quick fox" << "the" << "quick fox" ; |
596 | QTest::newRow(dataTag: "/U" ) << QRegularExpression("(.+) (.+?)" , QRegularExpression::InvertedGreedinessOption) |
597 | << "the quick fox" |
598 | << m; |
599 | |
600 | m.clear(); |
601 | m.isValid = true; m.hasMatch = true; |
602 | m.captured << "the quick fox" << "quick" ; |
603 | m.namedCaptured["named" ] = "quick" ; |
604 | QTest::newRow(dataTag: "no cap" ) << QRegularExpression("(\\w+) (?<named>\\w+) (\\w+)" , QRegularExpression::DontCaptureOption) |
605 | << "the quick fox" |
606 | << m; |
607 | |
608 | m.clear(); |
609 | m.isValid = true; m.hasMatch = true; |
610 | m.captured << QString::fromUtf8(str: "abc\xc3\x80\xc3\xa0 12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98" ) |
611 | << QString::fromUtf8(str: "abc\xc3\x80\xc3\xa0" ) |
612 | << QString::fromUtf8(str: "12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98" ); |
613 | QTest::newRow(dataTag: "unicode properties" ) << QRegularExpression("(\\w+) (\\d+)" , QRegularExpression::UseUnicodePropertiesOption) |
614 | << QString::fromUtf8(str: "abc\xc3\x80\xc3\xa0 12\xdb\xb1\xdb\xb2\xf0\x9d\x9f\x98" ) |
615 | << m; |
616 | } |
617 | |
618 | void tst_QRegularExpression::patternOptions() |
619 | { |
620 | QFETCH(QRegularExpression, regexp); |
621 | QFETCH(QString, subject); |
622 | QFETCH(Match, match); |
623 | |
624 | QRegularExpressionMatch m = regexp.match(subject); |
625 | consistencyCheck(match: m); |
626 | QVERIFY(m == match); |
627 | } |
628 | |
629 | void tst_QRegularExpression::normalMatch_data() |
630 | { |
631 | QTest::addColumn<QRegularExpression>(name: "regexp" ); |
632 | QTest::addColumn<QString>(name: "subject" ); |
633 | QTest::addColumn<int>(name: "offset" ); |
634 | QTest::addColumn<QRegularExpression::MatchOptions>(name: "matchOptions" ); |
635 | QTest::addColumn<Match>(name: "match" ); |
636 | |
637 | Match m; |
638 | int offset = 0; |
639 | |
640 | m.clear(); |
641 | m.isValid = true; m.hasMatch = true; |
642 | m.captured << "string" << "string" ; |
643 | QTest::newRow(dataTag: "match01" ) << QRegularExpression("(\\bstring\\b)" ) |
644 | << "a string" |
645 | << 0 |
646 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
647 | << m; |
648 | |
649 | m.clear(); |
650 | m.isValid = true; m.hasMatch = true; |
651 | m.captured << "a string" << "a" << "string" ; |
652 | QTest::newRow(dataTag: "match02" ) << QRegularExpression("(\\w+) (\\w+)" ) |
653 | << "a string" |
654 | << 0 |
655 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
656 | << m; |
657 | |
658 | m.clear(); |
659 | m.isValid = true; m.hasMatch = true; |
660 | m.captured << "a string" << "a" << "string" ; |
661 | m.namedCaptured["article" ] = "a" ; |
662 | m.namedCaptured["noun" ] = "string" ; |
663 | QTest::newRow(dataTag: "match03" ) << QRegularExpression("(?<article>\\w+) (?<noun>\\w+)" ) |
664 | << "a string" |
665 | << 0 |
666 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
667 | << m; |
668 | |
669 | m.clear(); |
670 | m.isValid = true; m.hasMatch = true; |
671 | m.captured << " string" << QString() << "string" ; |
672 | QTest::newRow(dataTag: "match04" ) << QRegularExpression("(\\w+)? (\\w+)" ) |
673 | << " string" |
674 | << 0 |
675 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
676 | << m; |
677 | |
678 | m.clear(); |
679 | m.isValid = true; m.hasMatch = true; |
680 | m.captured << " string" << QString("" ) << "string" ; |
681 | QTest::newRow(dataTag: "match05" ) << QRegularExpression("(\\w*) (\\w+)" ) |
682 | << " string" |
683 | << 0 |
684 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
685 | << m; |
686 | |
687 | m.clear(); |
688 | m.isValid = true; m.hasMatch = true; |
689 | m.captured << "c123def" << "c12" << "3" << "def" ; |
690 | offset = 2; |
691 | for (int i = 0; i <= offset; ++i) { |
692 | QTest::newRow(dataTag: ("match06-offset" + QByteArray::number(i)).constData()) |
693 | << QRegularExpression("(\\w*)(\\d+)(\\w*)" ) |
694 | << QStringLiteral("abc123def" ).mid(position: offset - i) |
695 | << i |
696 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
697 | << m; |
698 | } |
699 | |
700 | m.clear(); |
701 | m.isValid = true; m.hasMatch = true; |
702 | m.captured << QString("" ); |
703 | offset = 9; |
704 | for (int i = 0; i <= offset; ++i) { |
705 | QTest::newRow(dataTag: ("match07-offset" + QByteArray::number(i)).constData()) |
706 | << QRegularExpression("\\w*" ) |
707 | << QStringLiteral("abc123def" ).mid(position: offset - i) |
708 | << i |
709 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
710 | << m; |
711 | } |
712 | |
713 | m.clear(); |
714 | m.isValid = true; m.hasMatch = true; |
715 | m.captured << QString("a string" ) << QString("a string" ) << QString("" ); |
716 | QTest::newRow(dataTag: "match08" ) << QRegularExpression("(.*)(.*)" ) |
717 | << "a string" |
718 | << 0 |
719 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
720 | << m; |
721 | |
722 | m.clear(); |
723 | m.isValid = true; m.hasMatch = true; |
724 | m.captured << QString("a string" ) << QString("" ) << QString("a string" ); |
725 | QTest::newRow(dataTag: "match09" ) << QRegularExpression("(.*?)(.*)" ) |
726 | << "a string" |
727 | << 0 |
728 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
729 | << m; |
730 | |
731 | // non existing names for capturing groups |
732 | m.clear(); |
733 | m.isValid = true; m.hasMatch = true; |
734 | m.captured << "a string" << "a" << "string" ; |
735 | m.namedCaptured["article" ] = "a" ; |
736 | m.namedCaptured["noun" ] = "string" ; |
737 | m.namedCaptured["nonexisting1" ] = QString(); |
738 | m.namedCaptured["nonexisting2" ] = QString(); |
739 | m.namedCaptured["nonexisting3" ] = QString(); |
740 | QTest::newRow(dataTag: "match10" ) << QRegularExpression("(?<article>\\w+) (?<noun>\\w+)" ) |
741 | << "a string" |
742 | << 0 |
743 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
744 | << m; |
745 | |
746 | m.clear(); |
747 | m.isValid = true; m.hasMatch = true; |
748 | m.captured << "" << "" ; |
749 | m.namedCaptured["digits" ] = "" ; // empty VS null |
750 | m.namedCaptured["nonexisting" ] = QString(); |
751 | QTest::newRow(dataTag: "match11" ) << QRegularExpression("(?<digits>\\d*)" ) |
752 | << "abcde" |
753 | << 0 |
754 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
755 | << m; |
756 | |
757 | // *** |
758 | |
759 | m.clear(); |
760 | m.isValid = true; m.hasMatch = true; |
761 | m.captured << "bcd" ; |
762 | QTest::newRow(dataTag: "match12" ) |
763 | << QRegularExpression("\\Bbcd\\B" ) |
764 | << "abcde" |
765 | << 1 |
766 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
767 | << m; |
768 | |
769 | // *** |
770 | |
771 | m.clear(); |
772 | m.isValid = true; |
773 | QTest::newRow(dataTag: "nomatch01" ) << QRegularExpression("\\d+" ) |
774 | << "a string" |
775 | << 0 |
776 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
777 | << m; |
778 | |
779 | m.clear(); |
780 | m.isValid = true; |
781 | offset = 1; |
782 | for (int i = 0; i <= offset; ++i) { |
783 | QTest::newRow(dataTag: ("nomatch02-offset" + QByteArray::number(i)).constData()) |
784 | << QRegularExpression("(\\w+) (\\w+)" ) |
785 | << QStringLiteral("a string" ).mid(position: offset - i) |
786 | << i |
787 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
788 | << m; |
789 | } |
790 | |
791 | m.clear(); |
792 | m.isValid = true; |
793 | offset = 9; |
794 | for (int i = 0; i <= offset; ++i) { |
795 | QTest::newRow(dataTag: ("nomatch03-offset" + QByteArray::number(i)).constData()) |
796 | << QRegularExpression("\\w+" ) |
797 | << QStringLiteral("abc123def" ).mid(position: offset - i) |
798 | << i |
799 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
800 | << m; |
801 | } |
802 | |
803 | // *** |
804 | |
805 | m.clear(); |
806 | m.isValid = true; |
807 | QTest::newRow(dataTag: "anchoredmatch01" ) << QRegularExpression("\\d+" ) |
808 | << "abc123def" |
809 | << 0 |
810 | << QRegularExpression::MatchOptions(QRegularExpression::AnchoredMatchOption) |
811 | << m; |
812 | |
813 | // *** |
814 | |
815 | m.clear(); |
816 | m.isValid = true; m.hasMatch = true; |
817 | m.captured << "678" ; |
818 | QTest::newRow(dataTag: "negativeoffset01" ) << QRegularExpression("\\d+" ) |
819 | << "abc123def678ghi" |
820 | << -6 |
821 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
822 | << m; |
823 | |
824 | m.clear(); |
825 | m.isValid = true; m.hasMatch = true; |
826 | m.captured << "678" ; |
827 | QTest::newRow(dataTag: "negativeoffset02" ) << QRegularExpression("\\d+" ) |
828 | << "abc123def678ghi" |
829 | << -8 |
830 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
831 | << m; |
832 | |
833 | m.clear(); |
834 | m.isValid = true; m.hasMatch = true; |
835 | m.captured << "678ghi" << "678" << "ghi" ; |
836 | QTest::newRow(dataTag: "negativeoffset03" ) << QRegularExpression("(\\d+)(\\w+)" ) |
837 | << "abc123def678ghi" |
838 | << -8 |
839 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
840 | << m; |
841 | |
842 | m.clear(); |
843 | m.isValid = true; |
844 | QTest::newRow(dataTag: "negativeoffset04" ) << QRegularExpression("\\d+" ) |
845 | << "abc123def678ghi" |
846 | << -3 |
847 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
848 | << m; |
849 | |
850 | m.clear(); |
851 | m.isValid = true; m.hasMatch = true; |
852 | m.captured << "678" ; |
853 | QTest::newRow(dataTag: "negativeoffset05" ) << QRegularExpression("^\\d+" , QRegularExpression::MultilineOption) |
854 | << "a\nbc123\ndef\n678gh\ni" |
855 | << -10 |
856 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
857 | << m; |
858 | } |
859 | |
860 | |
861 | void tst_QRegularExpression::normalMatch() |
862 | { |
863 | QFETCH(QRegularExpression, regexp); |
864 | QFETCH(QString, subject); |
865 | QFETCH(int, offset); |
866 | QFETCH(QRegularExpression::MatchOptions, matchOptions); |
867 | QFETCH(Match, match); |
868 | |
869 | testMatch<QRegularExpressionMatch>(regexp, |
870 | matchingMethodForString: static_cast<QREMatchStringPMF>(&QRegularExpression::match), |
871 | matchingMethodForStringRef: static_cast<QREMatchStringRefPMF>(&QRegularExpression::match), |
872 | subject, |
873 | offset, |
874 | matchType: QRegularExpression::NormalMatch, |
875 | matchOptions, |
876 | result: match); |
877 | } |
878 | |
879 | void tst_QRegularExpression::partialMatch_data() |
880 | { |
881 | QTest::addColumn<QRegularExpression>(name: "regexp" ); |
882 | QTest::addColumn<QString>(name: "subject" ); |
883 | QTest::addColumn<int>(name: "offset" ); |
884 | QTest::addColumn<QRegularExpression::MatchType>(name: "matchType" ); |
885 | QTest::addColumn<QRegularExpression::MatchOptions>(name: "matchOptions" ); |
886 | QTest::addColumn<Match>(name: "match" ); |
887 | |
888 | Match m; |
889 | int offset = 0; |
890 | |
891 | m.clear(); |
892 | m.isValid = true; m.hasPartialMatch = true; |
893 | m.captured << "str" ; |
894 | QTest::newRow(dataTag: "softmatch01" ) << QRegularExpression("string" ) |
895 | << "a str" |
896 | << 0 |
897 | << QRegularExpression::PartialPreferCompleteMatch |
898 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
899 | << m; |
900 | |
901 | m.clear(); |
902 | m.isValid = true; m.hasPartialMatch = true; |
903 | m.captured << " str" ; |
904 | QTest::newRow(dataTag: "softmatch02" ) << QRegularExpression("\\bstring\\b" ) |
905 | << "a str" |
906 | << 0 |
907 | << QRegularExpression::PartialPreferCompleteMatch |
908 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
909 | << m; |
910 | |
911 | m.clear(); |
912 | m.isValid = true; m.hasPartialMatch = true; |
913 | m.captured << " str" ; |
914 | QTest::newRow(dataTag: "softmatch03" ) << QRegularExpression("(\\bstring\\b)" ) |
915 | << "a str" |
916 | << 0 |
917 | << QRegularExpression::PartialPreferCompleteMatch |
918 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
919 | << m; |
920 | |
921 | m.clear(); |
922 | m.isValid = true; m.hasPartialMatch = true; |
923 | m.captured << "8 Dec 19" ; |
924 | QTest::newRow(dataTag: "softmatch04" ) << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$" ) |
925 | << "8 Dec 19" |
926 | << 0 |
927 | << QRegularExpression::PartialPreferCompleteMatch |
928 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
929 | << m; |
930 | |
931 | m.clear(); |
932 | m.isValid = true; m.hasMatch = true; |
933 | m.captured << "8 Dec 1985" << "8" << "Dec" << "1985" ; |
934 | QTest::newRow(dataTag: "softmatch05" ) << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$" ) |
935 | << "8 Dec 1985" |
936 | << 0 |
937 | << QRegularExpression::PartialPreferCompleteMatch |
938 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
939 | << m; |
940 | |
941 | m.clear(); |
942 | m.isValid = true; m.hasMatch = true; |
943 | m.captured << "def" ; |
944 | QTest::newRow(dataTag: "softmatch06" ) << QRegularExpression("abc\\w+X|def" ) |
945 | << "abcdef" |
946 | << 0 |
947 | << QRegularExpression::PartialPreferCompleteMatch |
948 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
949 | << m; |
950 | |
951 | m.clear(); |
952 | m.isValid = true; m.hasPartialMatch = true; |
953 | m.captured << "abcdef" ; |
954 | QTest::newRow(dataTag: "softmatch07" ) << QRegularExpression("abc\\w+X|defY" ) |
955 | << "abcdef" |
956 | << 0 |
957 | << QRegularExpression::PartialPreferCompleteMatch |
958 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
959 | << m; |
960 | |
961 | m.clear(); |
962 | m.isValid = true; m.hasPartialMatch = true; |
963 | m.captured << "def" ; |
964 | offset = 1; |
965 | for (int i = 0; i <= offset; ++i) { |
966 | QTest::newRow(dataTag: ("softmatch08-offset" + QByteArray::number(i)).constData()) |
967 | << QRegularExpression("abc\\w+X|defY" ) |
968 | << QStringLiteral("abcdef" ).mid(position: offset - i) |
969 | << i |
970 | << QRegularExpression::PartialPreferCompleteMatch |
971 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
972 | << m; |
973 | } |
974 | |
975 | // *** |
976 | |
977 | m.clear(); |
978 | m.isValid = true; m.hasPartialMatch = true; |
979 | m.captured << "str" ; |
980 | QTest::newRow(dataTag: "hardmatch01" ) << QRegularExpression("string" ) |
981 | << "a str" |
982 | << 0 |
983 | << QRegularExpression::PartialPreferFirstMatch |
984 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
985 | << m; |
986 | |
987 | m.clear(); |
988 | m.isValid = true; m.hasPartialMatch = true; |
989 | m.captured << " str" ; |
990 | QTest::newRow(dataTag: "hardmatch02" ) << QRegularExpression("\\bstring\\b" ) |
991 | << "a str" |
992 | << 0 |
993 | << QRegularExpression::PartialPreferFirstMatch |
994 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
995 | << m; |
996 | |
997 | m.clear(); |
998 | m.isValid = true; m.hasPartialMatch = true; |
999 | m.captured << " str" ; |
1000 | QTest::newRow(dataTag: "hardmatch03" ) << QRegularExpression("(\\bstring\\b)" ) |
1001 | << "a str" |
1002 | << 0 |
1003 | << QRegularExpression::PartialPreferFirstMatch |
1004 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1005 | << m; |
1006 | |
1007 | m.clear(); |
1008 | m.isValid = true; m.hasPartialMatch = true; |
1009 | m.captured << "8 Dec 19" ; |
1010 | QTest::newRow(dataTag: "hardmatch04" ) << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$" ) |
1011 | << "8 Dec 19" |
1012 | << 0 |
1013 | << QRegularExpression::PartialPreferFirstMatch |
1014 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1015 | << m; |
1016 | |
1017 | m.clear(); |
1018 | m.isValid = true; m.hasPartialMatch = true; |
1019 | m.captured << "8 Dec 1985" ; |
1020 | QTest::newRow(dataTag: "hardmatch05" ) << QRegularExpression("^(\\d{1,2}) (\\w{3}) (\\d{4})$" ) |
1021 | << "8 Dec 1985" |
1022 | << 0 |
1023 | << QRegularExpression::PartialPreferFirstMatch |
1024 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1025 | << m; |
1026 | |
1027 | m.clear(); |
1028 | m.isValid = true; m.hasPartialMatch = true; |
1029 | m.captured << "abcdef" ; |
1030 | QTest::newRow(dataTag: "hardmatch06" ) << QRegularExpression("abc\\w+X|def" ) |
1031 | << "abcdef" |
1032 | << 0 |
1033 | << QRegularExpression::PartialPreferFirstMatch |
1034 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1035 | << m; |
1036 | |
1037 | m.clear(); |
1038 | m.isValid = true; m.hasPartialMatch = true; |
1039 | m.captured << "abcdef" ; |
1040 | QTest::newRow(dataTag: "hardmatch07" ) << QRegularExpression("abc\\w+X|defY" ) |
1041 | << "abcdef" |
1042 | << 0 |
1043 | << QRegularExpression::PartialPreferFirstMatch |
1044 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1045 | << m; |
1046 | |
1047 | m.clear(); |
1048 | m.isValid = true; m.hasPartialMatch = true; |
1049 | m.captured << "def" ; |
1050 | offset = 1; |
1051 | for (int i = 0; i <= offset; ++i) { |
1052 | QTest::newRow(dataTag: ("hardmatch08-offset" + QByteArray::number(i)).constData()) |
1053 | << QRegularExpression("abc\\w+X|defY" ) |
1054 | << QStringLiteral("abcdef" ).mid(position: offset - i) |
1055 | << i |
1056 | << QRegularExpression::PartialPreferFirstMatch |
1057 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1058 | << m; |
1059 | } |
1060 | |
1061 | m.clear(); |
1062 | m.isValid = true; m.hasPartialMatch = true; |
1063 | m.captured << "ab" ; |
1064 | QTest::newRow(dataTag: "hardmatch09" ) << QRegularExpression("abc|ab" ) |
1065 | << "ab" |
1066 | << 0 |
1067 | << QRegularExpression::PartialPreferFirstMatch |
1068 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1069 | << m; |
1070 | |
1071 | m.clear(); |
1072 | m.isValid = true; m.hasPartialMatch = true; |
1073 | m.captured << "abc" ; |
1074 | QTest::newRow(dataTag: "hardmatch10" ) << QRegularExpression("abc(def)?" ) |
1075 | << "abc" |
1076 | << 0 |
1077 | << QRegularExpression::PartialPreferFirstMatch |
1078 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1079 | << m; |
1080 | |
1081 | m.clear(); |
1082 | m.isValid = true; m.hasPartialMatch = true; |
1083 | m.captured << "abc" ; |
1084 | QTest::newRow(dataTag: "hardmatch11" ) << QRegularExpression("(abc)*" ) |
1085 | << "abc" |
1086 | << 0 |
1087 | << QRegularExpression::PartialPreferFirstMatch |
1088 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1089 | << m; |
1090 | |
1091 | |
1092 | // *** |
1093 | |
1094 | m.clear(); |
1095 | m.isValid = true; |
1096 | QTest::newRow(dataTag: "nomatch01" ) << QRegularExpression("abc\\w+X|defY" ) |
1097 | << "123456" |
1098 | << 0 |
1099 | << QRegularExpression::PartialPreferCompleteMatch |
1100 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1101 | << m; |
1102 | |
1103 | m.clear(); |
1104 | m.isValid = true; |
1105 | QTest::newRow(dataTag: "nomatch02" ) << QRegularExpression("abc\\w+X|defY" ) |
1106 | << "123456" |
1107 | << 0 |
1108 | << QRegularExpression::PartialPreferFirstMatch |
1109 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1110 | << m; |
1111 | |
1112 | m.clear(); |
1113 | m.isValid = true; |
1114 | QTest::newRow(dataTag: "nomatch03" ) << QRegularExpression("abc\\w+X|defY" ) |
1115 | << "ab123" |
1116 | << 0 |
1117 | << QRegularExpression::PartialPreferCompleteMatch |
1118 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1119 | << m; |
1120 | |
1121 | m.clear(); |
1122 | m.isValid = true; |
1123 | QTest::newRow(dataTag: "nomatch04" ) << QRegularExpression("abc\\w+X|defY" ) |
1124 | << "ab123" |
1125 | << 0 |
1126 | << QRegularExpression::PartialPreferFirstMatch |
1127 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1128 | << m; |
1129 | |
1130 | } |
1131 | |
1132 | void tst_QRegularExpression::partialMatch() |
1133 | { |
1134 | QFETCH(QRegularExpression, regexp); |
1135 | QFETCH(QString, subject); |
1136 | QFETCH(int, offset); |
1137 | QFETCH(QRegularExpression::MatchType, matchType); |
1138 | QFETCH(QRegularExpression::MatchOptions, matchOptions); |
1139 | QFETCH(Match, match); |
1140 | |
1141 | testMatch<QRegularExpressionMatch>(regexp, |
1142 | matchingMethodForString: static_cast<QREMatchStringPMF>(&QRegularExpression::match), |
1143 | matchingMethodForStringRef: static_cast<QREMatchStringRefPMF>(&QRegularExpression::match), |
1144 | subject, |
1145 | offset, |
1146 | matchType, |
1147 | matchOptions, |
1148 | result: match); |
1149 | } |
1150 | |
1151 | void tst_QRegularExpression::globalMatch_data() |
1152 | { |
1153 | QTest::addColumn<QRegularExpression>(name: "regexp" ); |
1154 | QTest::addColumn<QString>(name: "subject" ); |
1155 | QTest::addColumn<int>(name: "offset" ); |
1156 | QTest::addColumn<QRegularExpression::MatchType>(name: "matchType" ); |
1157 | QTest::addColumn<QRegularExpression::MatchOptions>(name: "matchOptions" ); |
1158 | QTest::addColumn<QVector<Match> >(name: "matchList" ); |
1159 | |
1160 | QVector<Match> matchList; |
1161 | Match m; |
1162 | |
1163 | matchList.clear(); |
1164 | m.clear(); |
1165 | m.isValid = true; m.hasMatch = true; |
1166 | m.captured = QStringList() << "the" ; |
1167 | matchList << m; |
1168 | m.captured = QStringList() << "quick" ; |
1169 | matchList << m; |
1170 | m.captured = QStringList() << "fox" ; |
1171 | matchList << m; |
1172 | QTest::newRow(dataTag: "globalmatch01" ) << QRegularExpression("\\w+" ) |
1173 | << "the quick fox" |
1174 | << 0 |
1175 | << QRegularExpression::NormalMatch |
1176 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1177 | << matchList; |
1178 | |
1179 | matchList.clear(); |
1180 | m.clear(); |
1181 | m.isValid = true; m.hasMatch = true; |
1182 | m.captured = QStringList() << "the" << "t" << "he" ; |
1183 | matchList << m; |
1184 | m.captured = QStringList() << "quick" << "q" << "uick" ; |
1185 | matchList << m; |
1186 | m.captured = QStringList() << "fox" << "f" << "ox" ; |
1187 | matchList << m; |
1188 | QTest::newRow(dataTag: "globalmatch02" ) << QRegularExpression("(\\w+?)(\\w+)" ) |
1189 | << "the quick fox" |
1190 | << 0 |
1191 | << QRegularExpression::NormalMatch |
1192 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1193 | << matchList; |
1194 | |
1195 | matchList.clear(); |
1196 | m.clear(); |
1197 | m.isValid = true; m.hasMatch = true; |
1198 | m.captured = QStringList() << "ACA" "GTG" "CGA" "AAA" ; |
1199 | matchList << m; |
1200 | m.captured = QStringList() << "AAA" ; |
1201 | matchList << m; |
1202 | m.captured = QStringList() << "AAG" "GAA" "AAG" "AAA" ; |
1203 | matchList << m; |
1204 | m.captured = QStringList() << "AAA" ; |
1205 | matchList << m; |
1206 | QTest::newRow(dataTag: "globalmatch03" ) << QRegularExpression("\\G(?:\\w\\w\\w)*?AAA" ) |
1207 | << "ACA" "GTG" "CGA" "AAA" "AAA" "AAG" "GAA" "AAG" "AAA" "AAA" |
1208 | << 0 |
1209 | << QRegularExpression::NormalMatch |
1210 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1211 | << matchList; |
1212 | |
1213 | QTest::newRow(dataTag: "globalmatch04" ) << QRegularExpression("(?:\\w\\w\\w)*?AAA" ) |
1214 | << "ACA" "GTG" "CGA" "AAA" "AAA" "AAG" "GAA" "AAG" "AAA" "AAA" |
1215 | << 0 |
1216 | << QRegularExpression::NormalMatch |
1217 | << QRegularExpression::MatchOptions(QRegularExpression::AnchoredMatchOption) |
1218 | << matchList; |
1219 | |
1220 | matchList.clear(); |
1221 | m.clear(); |
1222 | m.isValid = true; m.hasMatch = true; |
1223 | m.captured = QStringList() << "" ; |
1224 | matchList << m; |
1225 | m.captured = QStringList() << "c" ; |
1226 | matchList << m; |
1227 | m.captured = QStringList() << "" ; |
1228 | matchList << m; |
1229 | m.captured = QStringList() << "c" ; |
1230 | matchList << m; |
1231 | m.captured = QStringList() << "aabb" ; |
1232 | matchList << m; |
1233 | m.captured = QStringList() << "" ; |
1234 | matchList << m; |
1235 | m.captured = QStringList() << "" ; |
1236 | matchList << m; |
1237 | |
1238 | QTest::newRow(dataTag: "globalmatch_emptycaptures01" ) << QRegularExpression("a*b*|c" ) |
1239 | << "ccaabbd" |
1240 | << 0 |
1241 | << QRegularExpression::NormalMatch |
1242 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1243 | << matchList; |
1244 | |
1245 | matchList.clear(); |
1246 | m.clear(); |
1247 | m.isValid = true; m.hasMatch = true; |
1248 | m.captured = QStringList() << "the" ; |
1249 | matchList << m; |
1250 | m.captured = QStringList() << "" ; |
1251 | matchList << m; |
1252 | m.captured = QStringList() << "quick" ; |
1253 | matchList << m; |
1254 | m.captured = QStringList() << "" ; |
1255 | matchList << m; |
1256 | m.captured = QStringList() << "fox" ; |
1257 | matchList << m; |
1258 | m.captured = QStringList() << "" ; |
1259 | matchList << m; |
1260 | |
1261 | QTest::newRow(dataTag: "globalmatch_emptycaptures02" ) << QRegularExpression(".*" ) |
1262 | << "the\nquick\nfox" |
1263 | << 0 |
1264 | << QRegularExpression::NormalMatch |
1265 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1266 | << matchList; |
1267 | |
1268 | matchList.clear(); |
1269 | m.clear(); |
1270 | m.isValid = true; m.hasMatch = true; |
1271 | m.captured = QStringList() << "the" ; |
1272 | matchList << m; |
1273 | m.captured = QStringList() << "" ; |
1274 | matchList << m; |
1275 | m.captured = QStringList() << "quick" ; |
1276 | matchList << m; |
1277 | m.captured = QStringList() << "" ; |
1278 | matchList << m; |
1279 | m.captured = QStringList() << "fox" ; |
1280 | matchList << m; |
1281 | m.captured = QStringList() << "" ; |
1282 | matchList << m; |
1283 | m.captured = QStringList() << "" ; |
1284 | matchList << m; |
1285 | |
1286 | QTest::newRow(dataTag: "globalmatch_emptycaptures03" ) << QRegularExpression(".*" ) |
1287 | << "the\nquick\nfox\n" |
1288 | << 0 |
1289 | << QRegularExpression::NormalMatch |
1290 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1291 | << matchList; |
1292 | |
1293 | matchList.clear(); |
1294 | m.clear(); |
1295 | m.isValid = true; m.hasMatch = true; |
1296 | m.captured = QStringList() << "the" ; |
1297 | matchList << m; |
1298 | m.captured = QStringList() << "" ; |
1299 | matchList << m; |
1300 | m.captured = QStringList() << "quick" ; |
1301 | matchList << m; |
1302 | m.captured = QStringList() << "" ; |
1303 | matchList << m; |
1304 | m.captured = QStringList() << "fox" ; |
1305 | matchList << m; |
1306 | m.captured = QStringList() << "" ; |
1307 | matchList << m; |
1308 | |
1309 | QTest::newRow(dataTag: "globalmatch_emptycaptures04" ) << QRegularExpression("(*CRLF).*" ) |
1310 | << "the\r\nquick\r\nfox" |
1311 | << 0 |
1312 | << QRegularExpression::NormalMatch |
1313 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1314 | << matchList; |
1315 | |
1316 | matchList.clear(); |
1317 | m.clear(); |
1318 | m.isValid = true; m.hasMatch = true; |
1319 | m.captured = QStringList() << "the" ; |
1320 | matchList << m; |
1321 | m.captured = QStringList() << "" ; |
1322 | matchList << m; |
1323 | m.captured = QStringList() << "quick" ; |
1324 | matchList << m; |
1325 | m.captured = QStringList() << "" ; |
1326 | matchList << m; |
1327 | m.captured = QStringList() << "fox" ; |
1328 | matchList << m; |
1329 | m.captured = QStringList() << "" ; |
1330 | matchList << m; |
1331 | m.captured = QStringList() << "" ; |
1332 | matchList << m; |
1333 | |
1334 | QTest::newRow(dataTag: "globalmatch_emptycaptures05" ) << QRegularExpression("(*CRLF).*" ) |
1335 | << "the\r\nquick\r\nfox\r\n" |
1336 | << 0 |
1337 | << QRegularExpression::NormalMatch |
1338 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1339 | << matchList; |
1340 | |
1341 | matchList.clear(); |
1342 | m.clear(); |
1343 | m.isValid = true; m.hasMatch = true; |
1344 | m.captured = QStringList() << "the" ; |
1345 | matchList << m; |
1346 | m.captured = QStringList() << "" ; |
1347 | matchList << m; |
1348 | m.captured = QStringList() << "quick" ; |
1349 | matchList << m; |
1350 | m.captured = QStringList() << "" ; |
1351 | matchList << m; |
1352 | m.captured = QStringList() << "fox" ; |
1353 | matchList << m; |
1354 | m.captured = QStringList() << "" ; |
1355 | matchList << m; |
1356 | m.captured = QStringList() << "jumped" ; |
1357 | matchList << m; |
1358 | m.captured = QStringList() << "" ; |
1359 | matchList << m; |
1360 | |
1361 | QTest::newRow(dataTag: "globalmatch_emptycaptures06" ) << QRegularExpression("(*ANYCRLF).*" ) |
1362 | << "the\r\nquick\nfox\rjumped" |
1363 | << 0 |
1364 | << QRegularExpression::NormalMatch |
1365 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1366 | << matchList; |
1367 | |
1368 | matchList.clear(); |
1369 | m.clear(); |
1370 | m.isValid = true; m.hasMatch = true; |
1371 | m.captured = QStringList() << "ABC" ; |
1372 | matchList << m; |
1373 | m.captured = QStringList() << "" ; |
1374 | matchList << m; |
1375 | m.captured = QStringList() << "DEF" ; |
1376 | matchList << m; |
1377 | m.captured = QStringList() << "" ; |
1378 | matchList << m; |
1379 | m.captured = QStringList() << "GHI" ; |
1380 | matchList << m; |
1381 | m.captured = QStringList() << "" ; |
1382 | matchList << m; |
1383 | QTest::newRow(dataTag: "globalmatch_emptycaptures07" ) << QRegularExpression("[\\x{0000}-\\x{FFFF}]*" ) |
1384 | << QString::fromUtf8(str: "ABC" "\xf0\x9d\x85\x9d" "DEF" "\xf0\x9d\x85\x9e" "GHI" ) |
1385 | << 0 |
1386 | << QRegularExpression::NormalMatch |
1387 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1388 | << matchList; |
1389 | |
1390 | matchList.clear(); |
1391 | m.clear(); |
1392 | m.isValid = true; m.hasMatch = true; |
1393 | m.captured = QStringList() << QString::fromUtf8(str: "ABC" "\xc3\x80" ); |
1394 | matchList << m; |
1395 | m.captured = QStringList() << "" ; |
1396 | matchList << m; |
1397 | m.captured = QStringList() << QString::fromUtf8(str: "\xc3\x80" "DEF" "\xc3\x80" ); |
1398 | matchList << m; |
1399 | m.captured = QStringList() << "" ; |
1400 | matchList << m; |
1401 | QTest::newRow(dataTag: "globalmatch_emptycaptures08" ) << QRegularExpression("[\\x{0000}-\\x{FFFF}]*" ) |
1402 | << QString::fromUtf8(str: "ABC" "\xc3\x80" "\xf0\x9d\x85\x9d" "\xc3\x80" "DEF" "\xc3\x80" ) |
1403 | << 0 |
1404 | << QRegularExpression::NormalMatch |
1405 | << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) |
1406 | << matchList; |
1407 | } |
1408 | |
1409 | void tst_QRegularExpression::globalMatch() |
1410 | { |
1411 | QFETCH(QRegularExpression, regexp); |
1412 | QFETCH(QString, subject); |
1413 | QFETCH(int, offset); |
1414 | QFETCH(QRegularExpression::MatchType, matchType); |
1415 | QFETCH(QRegularExpression::MatchOptions, matchOptions); |
1416 | QFETCH(QVector<Match>, matchList); |
1417 | |
1418 | testMatch<QRegularExpressionMatchIterator>(regexp, |
1419 | matchingMethodForString: static_cast<QREGlobalMatchStringPMF>(&QRegularExpression::globalMatch), |
1420 | matchingMethodForStringRef: static_cast<QREGlobalMatchStringRefPMF>(&QRegularExpression::globalMatch), |
1421 | subject, |
1422 | offset, |
1423 | matchType, |
1424 | matchOptions, |
1425 | result: matchList); |
1426 | } |
1427 | |
1428 | void tst_QRegularExpression::serialize_data() |
1429 | { |
1430 | provideRegularExpressions(); |
1431 | } |
1432 | |
1433 | void tst_QRegularExpression::serialize() |
1434 | { |
1435 | QFETCH(QString, pattern); |
1436 | QFETCH(QRegularExpression::PatternOptions, patternOptions); |
1437 | QRegularExpression outRe(pattern, patternOptions); |
1438 | |
1439 | QByteArray buffer; |
1440 | { |
1441 | QDataStream out(&buffer, QIODevice::WriteOnly); |
1442 | out << outRe; |
1443 | } |
1444 | QRegularExpression inRe; |
1445 | { |
1446 | QDataStream in(&buffer, QIODevice::ReadOnly); |
1447 | in >> inRe; |
1448 | } |
1449 | QCOMPARE(inRe, outRe); |
1450 | } |
1451 | |
1452 | static void verifyEquality(const QRegularExpression &re1, const QRegularExpression &re2) |
1453 | { |
1454 | QVERIFY(re1 == re2); |
1455 | QVERIFY(re2 == re1); |
1456 | QCOMPARE(qHash(re1), qHash(re2)); |
1457 | QVERIFY(!(re1 != re2)); |
1458 | QVERIFY(!(re2 != re1)); |
1459 | |
1460 | QRegularExpression re3(re1); |
1461 | |
1462 | QVERIFY(re1 == re3); |
1463 | QVERIFY(re3 == re1); |
1464 | QCOMPARE(qHash(re1), qHash(re3)); |
1465 | QVERIFY(!(re1 != re3)); |
1466 | QVERIFY(!(re3 != re1)); |
1467 | |
1468 | QVERIFY(re2 == re3); |
1469 | QVERIFY(re3 == re2); |
1470 | QCOMPARE(qHash(re2), qHash(re3)); |
1471 | QVERIFY(!(re2 != re3)); |
1472 | QVERIFY(!(re3 != re2)); |
1473 | |
1474 | re3 = re2; |
1475 | QVERIFY(re1 == re3); |
1476 | QVERIFY(re3 == re1); |
1477 | QCOMPARE(qHash(re1), qHash(re3)); |
1478 | QVERIFY(!(re1 != re3)); |
1479 | QVERIFY(!(re3 != re1)); |
1480 | |
1481 | QVERIFY(re2 == re3); |
1482 | QVERIFY(re3 == re2); |
1483 | QCOMPARE(qHash(re2), qHash(re3)); |
1484 | QVERIFY(!(re2 != re3)); |
1485 | QVERIFY(!(re3 != re2)); |
1486 | } |
1487 | |
1488 | void tst_QRegularExpression::operatoreq_data() |
1489 | { |
1490 | provideRegularExpressions(); |
1491 | } |
1492 | |
1493 | void tst_QRegularExpression::operatoreq() |
1494 | { |
1495 | QFETCH(QString, pattern); |
1496 | QFETCH(QRegularExpression::PatternOptions, patternOptions); |
1497 | { |
1498 | QRegularExpression re1(pattern); |
1499 | QRegularExpression re2(pattern); |
1500 | |
1501 | verifyEquality(re1, re2); |
1502 | } |
1503 | { |
1504 | QRegularExpression re1(QString(), patternOptions); |
1505 | QRegularExpression re2(QString(), patternOptions); |
1506 | |
1507 | verifyEquality(re1, re2); |
1508 | } |
1509 | { |
1510 | QRegularExpression re1(pattern, patternOptions); |
1511 | QRegularExpression re2(pattern, patternOptions); |
1512 | |
1513 | verifyEquality(re1, re2); |
1514 | } |
1515 | } |
1516 | |
1517 | void tst_QRegularExpression::captureCount_data() |
1518 | { |
1519 | QTest::addColumn<QString>(name: "pattern" ); |
1520 | QTest::addColumn<int>(name: "captureCount" ); |
1521 | QTest::newRow(dataTag: "captureCount01" ) << "a pattern" << 0; |
1522 | QTest::newRow(dataTag: "captureCount02" ) << "a.*pattern" << 0; |
1523 | QTest::newRow(dataTag: "captureCount03" ) << "(a) pattern" << 1; |
1524 | QTest::newRow(dataTag: "captureCount04" ) << "(a).*(pattern)" << 2; |
1525 | QTest::newRow(dataTag: "captureCount05" ) << "^(?<article>\\w+) (?<noun>\\w+)$" << 2; |
1526 | QTest::newRow(dataTag: "captureCount06" ) << "^(\\w+) (?<word>\\w+) (.)$" << 3; |
1527 | QTest::newRow(dataTag: "captureCount07" ) << "(?:non capturing) (capturing) (?<n>named) (?:non (capturing))" << 3; |
1528 | QTest::newRow(dataTag: "captureCount08" ) << "(?|(a)(b)|(c)(d))" << 2; |
1529 | QTest::newRow(dataTag: "captureCount09" ) << "(?|(a)(b)|(c)(d)(?:e))" << 2; |
1530 | QTest::newRow(dataTag: "captureCount10" ) << "(?|(a)(b)|(c)(d)(e)) (f)(g)" << 5; |
1531 | QTest::newRow(dataTag: "captureCount11" ) << "(?|(a)(b)|(c)(d)(e)) (f)(?:g)" << 4; |
1532 | QTest::newRow(dataTag: "captureCount_invalid01" ) << "(.*" << -1; |
1533 | QTest::newRow(dataTag: "captureCount_invalid02" ) << "\\" << -1; |
1534 | QTest::newRow(dataTag: "captureCount_invalid03" ) << "(?<noun)" << -1; |
1535 | } |
1536 | |
1537 | void tst_QRegularExpression::captureCount() |
1538 | { |
1539 | QFETCH(QString, pattern); |
1540 | QRegularExpression re(pattern); |
1541 | |
1542 | QTEST(re.captureCount(), "captureCount" ); |
1543 | if (!re.isValid()) |
1544 | QCOMPARE(re.captureCount(), -1); |
1545 | } |
1546 | |
1547 | // the comma in the template breaks QFETCH... |
1548 | typedef QMultiHash<QString, int> StringToIntMap; |
1549 | Q_DECLARE_METATYPE(StringToIntMap) |
1550 | |
1551 | void tst_QRegularExpression::captureNames_data() |
1552 | { |
1553 | QTest::addColumn<QString>(name: "pattern" ); |
1554 | QTest::addColumn<StringToIntMap>(name: "namedCapturesIndexMap" ); |
1555 | StringToIntMap map; |
1556 | |
1557 | QTest::newRow(dataTag: "captureNames01" ) << "a pattern" << map; |
1558 | QTest::newRow(dataTag: "captureNames02" ) << "a.*pattern" << map; |
1559 | QTest::newRow(dataTag: "captureNames03" ) << "(a) pattern" << map; |
1560 | QTest::newRow(dataTag: "captureNames04" ) << "(a).*(pattern)" << map; |
1561 | |
1562 | map.clear(); |
1563 | map.replace(key: "named" , value: 1); |
1564 | QTest::newRow(dataTag: "captureNames05" ) << "a.*(?<named>pattern)" << map; |
1565 | |
1566 | map.clear(); |
1567 | map.replace(key: "named" , value: 2); |
1568 | QTest::newRow(dataTag: "captureNames06" ) << "(a).*(?<named>pattern)" << map; |
1569 | |
1570 | map.clear(); |
1571 | map.replace(key: "name1" , value: 1); |
1572 | map.replace(key: "name2" , value: 2); |
1573 | QTest::newRow(dataTag: "captureNames07" ) << "(?<name1>a).*(?<name2>pattern)" << map; |
1574 | |
1575 | map.clear(); |
1576 | map.replace(key: "name1" , value: 2); |
1577 | map.replace(key: "name2" , value: 1); |
1578 | QTest::newRow(dataTag: "captureNames08" ) << "(?<name2>a).*(?<name1>pattern)" << map; |
1579 | |
1580 | map.clear(); |
1581 | map.replace(key: "date" , value: 1); |
1582 | map.replace(key: "month" , value: 2); |
1583 | map.replace(key: "year" , value: 3); |
1584 | QTest::newRow(dataTag: "captureNames09" ) << "^(?<date>\\d\\d)/(?<month>\\d\\d)/(?<year>\\d\\d\\d\\d)$" << map; |
1585 | |
1586 | map.clear(); |
1587 | map.replace(key: "date" , value: 2); |
1588 | map.replace(key: "month" , value: 1); |
1589 | map.replace(key: "year" , value: 3); |
1590 | QTest::newRow(dataTag: "captureNames10" ) << "^(?<month>\\d\\d)/(?<date>\\d\\d)/(?<year>\\d\\d\\d\\d)$" << map; |
1591 | |
1592 | map.clear(); |
1593 | map.replace(key: "noun" , value: 2); |
1594 | QTest::newRow(dataTag: "captureNames11" ) << "(a)(?|(?<noun>b)|(?<noun>c))(d)" << map; |
1595 | |
1596 | map.clear(); |
1597 | QTest::newRow(dataTag: "captureNames_invalid01" ) << "(.*" << map; |
1598 | QTest::newRow(dataTag: "captureNames_invalid02" ) << "\\" << map; |
1599 | QTest::newRow(dataTag: "captureNames_invalid03" ) << "(?<noun)" << map; |
1600 | QTest::newRow(dataTag: "captureNames_invalid04" ) << "(?|(?<noun1>a)|(?<noun2>b))" << map; |
1601 | } |
1602 | |
1603 | void tst_QRegularExpression::captureNames() |
1604 | { |
1605 | QFETCH(QString, pattern); |
1606 | QFETCH(StringToIntMap, namedCapturesIndexMap); |
1607 | |
1608 | QRegularExpression re(pattern); |
1609 | |
1610 | QStringList namedCaptureGroups = re.namedCaptureGroups(); |
1611 | int namedCaptureGroupsCount = namedCaptureGroups.size(); |
1612 | |
1613 | QCOMPARE(namedCaptureGroupsCount, re.captureCount() + 1); |
1614 | |
1615 | for (int i = 0; i < namedCaptureGroupsCount; ++i) { |
1616 | const QString &name = namedCaptureGroups.at(i); |
1617 | |
1618 | if (name.isEmpty()) { |
1619 | QVERIFY(!namedCapturesIndexMap.contains(name)); |
1620 | } else { |
1621 | QVERIFY(namedCapturesIndexMap.contains(name)); |
1622 | QCOMPARE(i, namedCapturesIndexMap.value(name)); |
1623 | } |
1624 | } |
1625 | |
1626 | } |
1627 | |
1628 | void tst_QRegularExpression::captureNamesNul() |
1629 | { |
1630 | QRegularExpression re("a(\\d+)b(?<name>\\d+)c(?<anotherName>\\d+)d(\\d+)e$" ); |
1631 | QVERIFY(re.isValid()); |
1632 | |
1633 | QCOMPARE(re.captureCount(), 4); |
1634 | |
1635 | QStringList namedCaptureGroups = re.namedCaptureGroups(); |
1636 | QCOMPARE(namedCaptureGroups[0], QString()); |
1637 | QCOMPARE(namedCaptureGroups[1], QString()); |
1638 | QCOMPARE(namedCaptureGroups[2], "name" ); |
1639 | QCOMPARE(namedCaptureGroups[3], "anotherName" ); |
1640 | QCOMPARE(namedCaptureGroups[4], QString()); |
1641 | |
1642 | QRegularExpressionMatch m = re.match(subject: "a12b456c789d0e" ); |
1643 | QVERIFY(m.hasMatch()); |
1644 | |
1645 | QString captureName("name" ); |
1646 | QCOMPARE(m.captured(captureName), "456" ); |
1647 | QCOMPARE(m.captured(QStringView(captureName)), "456" ); |
1648 | QCOMPARE(m.captured(qToStringViewIgnoringNull(captureName)), "456" ); |
1649 | QCOMPARE(m.captured(u"name" ), "456" ); |
1650 | |
1651 | captureName = "anotherName" ; |
1652 | QCOMPARE(m.captured(captureName), "789" ); |
1653 | QCOMPARE(m.captured(QStringView(captureName)), "789" ); |
1654 | QCOMPARE(m.captured(qToStringViewIgnoringNull(captureName)), "789" ); |
1655 | QCOMPARE(m.captured(u"anotherName" ), "789" ); |
1656 | } |
1657 | |
1658 | void tst_QRegularExpression::pcreJitStackUsage_data() |
1659 | { |
1660 | QTest::addColumn<QString>(name: "pattern" ); |
1661 | QTest::addColumn<QString>(name: "subject" ); |
1662 | // these patterns cause enough backtrack (or even infinite recursion) |
1663 | // in the regexp engine, so that JIT requests more memory. |
1664 | QTest::newRow(dataTag: "jitstack01" ) << "(?(R)a*(?1)|((?R))b)" << "aaaabcde" ; |
1665 | QTest::newRow(dataTag: "jitstack02" ) << "(?(R)a*(?1)|((?R))b)" << "aaaaaaabcde" ; |
1666 | } |
1667 | |
1668 | void tst_QRegularExpression::pcreJitStackUsage() |
1669 | { |
1670 | QFETCH(QString, pattern); |
1671 | QFETCH(QString, subject); |
1672 | |
1673 | QRegularExpression re(pattern); |
1674 | |
1675 | QVERIFY(re.isValid()); |
1676 | QRegularExpressionMatch match = re.match(subject); |
1677 | consistencyCheck(match); |
1678 | QRegularExpressionMatchIterator iterator = re.globalMatch(subject); |
1679 | consistencyCheck(iterator); |
1680 | while (iterator.hasNext()) { |
1681 | match = iterator.next(); |
1682 | consistencyCheck(match); |
1683 | } |
1684 | } |
1685 | |
1686 | void tst_QRegularExpression::regularExpressionMatch_data() |
1687 | { |
1688 | QTest::addColumn<QString>(name: "pattern" ); |
1689 | QTest::addColumn<QString>(name: "subject" ); |
1690 | |
1691 | QTest::newRow(dataTag: "validity01" ) << "(?<digits>\\d+)" << "1234 abcd" ; |
1692 | QTest::newRow(dataTag: "validity02" ) << "(?<digits>\\d+) (?<alpha>\\w+)" << "1234 abcd" ; |
1693 | } |
1694 | |
1695 | void tst_QRegularExpression::regularExpressionMatch() |
1696 | { |
1697 | QFETCH(QString, pattern); |
1698 | QFETCH(QString, subject); |
1699 | |
1700 | QRegularExpression re(pattern); |
1701 | |
1702 | QVERIFY(re.isValid()); |
1703 | QRegularExpressionMatch match = re.match(subject); |
1704 | consistencyCheck(match); |
1705 | QCOMPARE(match.captured("non-existing" ).isNull(), true); |
1706 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionMatch::captured: empty capturing group name passed" ); |
1707 | QCOMPARE(match.captured("" ).isNull(), true); |
1708 | QTest::ignoreMessage(type: QtWarningMsg, message: "QRegularExpressionMatch::captured: empty capturing group name passed" ); |
1709 | QCOMPARE(match.captured(QString()).isNull(), true); |
1710 | } |
1711 | |
1712 | void tst_QRegularExpression::JOptionUsage_data() |
1713 | { |
1714 | QTest::addColumn<QString>(name: "pattern" ); |
1715 | QTest::addColumn<bool>(name: "isValid" ); |
1716 | QTest::addColumn<bool>(name: "JOptionUsed" ); |
1717 | |
1718 | QTest::newRow(dataTag: "joption-notused-01" ) << "a.*b" << true << false; |
1719 | QTest::newRow(dataTag: "joption-notused-02" ) << "^a(b)(c)$" << true << false; |
1720 | QTest::newRow(dataTag: "joption-notused-03" ) << "a(b)(?<c>d)|e" << true << false; |
1721 | QTest::newRow(dataTag: "joption-notused-04" ) << "(?<a>.)(?<a>.)" << false << false; |
1722 | |
1723 | QTest::newRow(dataTag: "joption-used-01" ) << "(?J)a.*b" << true << true; |
1724 | QTest::newRow(dataTag: "joption-used-02" ) << "(?-J)a.*b" << true << true; |
1725 | QTest::newRow(dataTag: "joption-used-03" ) << "(?J)(?<a>.)(?<a>.)" << true << true; |
1726 | QTest::newRow(dataTag: "joption-used-04" ) << "(?-J)(?<a>.)(?<a>.)" << false << true; |
1727 | |
1728 | } |
1729 | |
1730 | void tst_QRegularExpression::JOptionUsage() |
1731 | { |
1732 | QFETCH(QString, pattern); |
1733 | QFETCH(bool, isValid); |
1734 | QFETCH(bool, JOptionUsed); |
1735 | |
1736 | const QString warningMessage = QStringLiteral("QRegularExpressionPrivate::getPatternInfo(): the pattern '%1'\n is using the (?J) option; duplicate capturing group names are not supported by Qt" ); |
1737 | |
1738 | QRegularExpression re(pattern); |
1739 | if (isValid && JOptionUsed) |
1740 | QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warningMessage.arg(pattern))); |
1741 | QCOMPARE(re.isValid(), isValid); |
1742 | } |
1743 | |
1744 | void tst_QRegularExpression::QStringAndQStringRefEquivalence() |
1745 | { |
1746 | const QString subject = QStringLiteral("Mississippi" ); |
1747 | { |
1748 | const QRegularExpression re("\\Biss\\B" ); |
1749 | QVERIFY(re.isValid()); |
1750 | { |
1751 | const QRegularExpressionMatch match = re.match(subject); |
1752 | consistencyCheck(match); |
1753 | QVERIFY(match.isValid()); |
1754 | QVERIFY(match.hasMatch()); |
1755 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1756 | QCOMPARE(match.capturedStart(), 1); |
1757 | QCOMPARE(match.capturedEnd(), 4); |
1758 | } |
1759 | { |
1760 | const QRegularExpressionMatch match = re.match(subjectRef: QStringRef(&subject)); |
1761 | consistencyCheck(match); |
1762 | QVERIFY(match.isValid()); |
1763 | QVERIFY(match.hasMatch()); |
1764 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1765 | QCOMPARE(match.capturedStart(), 1); |
1766 | QCOMPARE(match.capturedEnd(), 4); |
1767 | } |
1768 | { |
1769 | const QRegularExpressionMatch match = re.match(subject, offset: 1); |
1770 | consistencyCheck(match); |
1771 | QVERIFY(match.isValid()); |
1772 | QVERIFY(match.hasMatch()); |
1773 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1774 | QCOMPARE(match.capturedStart(), 1); |
1775 | QCOMPARE(match.capturedEnd(), 4); |
1776 | } |
1777 | { |
1778 | const QRegularExpressionMatch match = re.match(subjectRef: QStringRef(&subject), offset: 1); |
1779 | consistencyCheck(match); |
1780 | QVERIFY(match.isValid()); |
1781 | QVERIFY(match.hasMatch()); |
1782 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1783 | QCOMPARE(match.capturedStart(), 1); |
1784 | QCOMPARE(match.capturedEnd(), 4); |
1785 | } |
1786 | { |
1787 | const QRegularExpressionMatch match = re.match(subject: subject.mid(position: 1)); |
1788 | consistencyCheck(match); |
1789 | QVERIFY(match.isValid()); |
1790 | QVERIFY(match.hasMatch()); |
1791 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1792 | QCOMPARE(match.capturedStart(), 3); |
1793 | QCOMPARE(match.capturedEnd(), 6); |
1794 | } |
1795 | { |
1796 | const QRegularExpressionMatch match = re.match(subjectRef: subject.midRef(position: 1)); |
1797 | consistencyCheck(match); |
1798 | QVERIFY(match.isValid()); |
1799 | QVERIFY(match.hasMatch()); |
1800 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1801 | QCOMPARE(match.capturedStart(), 3); |
1802 | QCOMPARE(match.capturedEnd(), 6); |
1803 | } |
1804 | { |
1805 | const QRegularExpressionMatch match = re.match(subject: subject.mid(position: 1), offset: 1); |
1806 | consistencyCheck(match); |
1807 | QVERIFY(match.isValid()); |
1808 | QVERIFY(match.hasMatch()); |
1809 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1810 | QCOMPARE(match.capturedStart(), 3); |
1811 | QCOMPARE(match.capturedEnd(), 6); |
1812 | } |
1813 | { |
1814 | const QRegularExpressionMatch match = re.match(subjectRef: subject.midRef(position: 1), offset: 1); |
1815 | consistencyCheck(match); |
1816 | QVERIFY(match.isValid()); |
1817 | QVERIFY(match.hasMatch()); |
1818 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1819 | QCOMPARE(match.capturedStart(), 3); |
1820 | QCOMPARE(match.capturedEnd(), 6); |
1821 | } |
1822 | { |
1823 | const QRegularExpressionMatch match = re.match(subject, offset: 4); |
1824 | consistencyCheck(match); |
1825 | QVERIFY(match.isValid()); |
1826 | QVERIFY(match.hasMatch()); |
1827 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1828 | QCOMPARE(match.capturedStart(), 4); |
1829 | QCOMPARE(match.capturedEnd(), 7); |
1830 | } |
1831 | { |
1832 | const QRegularExpressionMatch match = re.match(subjectRef: QStringRef(&subject), offset: 4); |
1833 | consistencyCheck(match); |
1834 | QVERIFY(match.isValid()); |
1835 | QVERIFY(match.hasMatch()); |
1836 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1837 | QCOMPARE(match.capturedStart(), 4); |
1838 | QCOMPARE(match.capturedEnd(), 7); |
1839 | } |
1840 | { |
1841 | const QRegularExpressionMatch match = re.match(subject: subject.mid(position: 4)); |
1842 | consistencyCheck(match); |
1843 | QVERIFY(match.isValid()); |
1844 | QVERIFY(!match.hasMatch()); |
1845 | } |
1846 | { |
1847 | const QRegularExpressionMatch match = re.match(subjectRef: subject.midRef(position: 4)); |
1848 | consistencyCheck(match); |
1849 | QVERIFY(match.isValid()); |
1850 | QVERIFY(!match.hasMatch()); |
1851 | } |
1852 | |
1853 | { |
1854 | QRegularExpressionMatchIterator i = re.globalMatch(subject); |
1855 | QVERIFY(i.isValid()); |
1856 | |
1857 | consistencyCheck(iterator: i); |
1858 | QVERIFY(i.hasNext()); |
1859 | const QRegularExpressionMatch match1 = i.next(); |
1860 | consistencyCheck(match: match1); |
1861 | QVERIFY(match1.isValid()); |
1862 | QVERIFY(match1.hasMatch()); |
1863 | QCOMPARE(match1.captured(), QStringLiteral("iss" )); |
1864 | QCOMPARE(match1.capturedStart(), 1); |
1865 | QCOMPARE(match1.capturedEnd(), 4); |
1866 | |
1867 | consistencyCheck(iterator: i); |
1868 | QVERIFY(i.hasNext()); |
1869 | const QRegularExpressionMatch match2 = i.next(); |
1870 | consistencyCheck(match: match2); |
1871 | QVERIFY(match2.isValid()); |
1872 | QVERIFY(match2.hasMatch()); |
1873 | QCOMPARE(match2.captured(), QStringLiteral("iss" )); |
1874 | QCOMPARE(match2.capturedStart(), 4); |
1875 | QCOMPARE(match2.capturedEnd(), 7); |
1876 | |
1877 | QVERIFY(!i.hasNext()); |
1878 | } |
1879 | { |
1880 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: QStringRef(&subject)); |
1881 | QVERIFY(i.isValid()); |
1882 | |
1883 | consistencyCheck(iterator: i); |
1884 | QVERIFY(i.hasNext()); |
1885 | const QRegularExpressionMatch match1 = i.next(); |
1886 | consistencyCheck(match: match1); |
1887 | QVERIFY(match1.isValid()); |
1888 | QVERIFY(match1.hasMatch()); |
1889 | QCOMPARE(match1.captured(), QStringLiteral("iss" )); |
1890 | QCOMPARE(match1.capturedStart(), 1); |
1891 | QCOMPARE(match1.capturedEnd(), 4); |
1892 | |
1893 | consistencyCheck(iterator: i); |
1894 | QVERIFY(i.hasNext()); |
1895 | const QRegularExpressionMatch match2 = i.next(); |
1896 | consistencyCheck(match: match2); |
1897 | QVERIFY(match2.isValid()); |
1898 | QVERIFY(match2.hasMatch()); |
1899 | QCOMPARE(match2.captured(), QStringLiteral("iss" )); |
1900 | QCOMPARE(match2.capturedStart(), 4); |
1901 | QCOMPARE(match2.capturedEnd(), 7); |
1902 | |
1903 | QVERIFY(!i.hasNext()); |
1904 | } |
1905 | { |
1906 | QRegularExpressionMatchIterator i = re.globalMatch(subject, offset: 1); |
1907 | QVERIFY(i.isValid()); |
1908 | |
1909 | consistencyCheck(iterator: i); |
1910 | QVERIFY(i.hasNext()); |
1911 | const QRegularExpressionMatch match1 = i.next(); |
1912 | consistencyCheck(match: match1); |
1913 | QVERIFY(match1.isValid()); |
1914 | QVERIFY(match1.hasMatch()); |
1915 | QCOMPARE(match1.captured(), QStringLiteral("iss" )); |
1916 | QCOMPARE(match1.capturedStart(), 1); |
1917 | QCOMPARE(match1.capturedEnd(), 4); |
1918 | |
1919 | consistencyCheck(iterator: i); |
1920 | QVERIFY(i.hasNext()); |
1921 | const QRegularExpressionMatch match2 = i.next(); |
1922 | consistencyCheck(match: match2); |
1923 | QVERIFY(match2.isValid()); |
1924 | QVERIFY(match2.hasMatch()); |
1925 | QCOMPARE(match2.captured(), QStringLiteral("iss" )); |
1926 | QCOMPARE(match2.capturedStart(), 4); |
1927 | QCOMPARE(match2.capturedEnd(), 7); |
1928 | |
1929 | QVERIFY(!i.hasNext()); |
1930 | } |
1931 | { |
1932 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: QStringRef(&subject), offset: 1); |
1933 | QVERIFY(i.isValid()); |
1934 | |
1935 | consistencyCheck(iterator: i); |
1936 | QVERIFY(i.hasNext()); |
1937 | const QRegularExpressionMatch match1 = i.next(); |
1938 | consistencyCheck(match: match1); |
1939 | QVERIFY(match1.isValid()); |
1940 | QVERIFY(match1.hasMatch()); |
1941 | QCOMPARE(match1.captured(), QStringLiteral("iss" )); |
1942 | QCOMPARE(match1.capturedStart(), 1); |
1943 | QCOMPARE(match1.capturedEnd(), 4); |
1944 | |
1945 | consistencyCheck(iterator: i); |
1946 | QVERIFY(i.hasNext()); |
1947 | const QRegularExpressionMatch match2 = i.next(); |
1948 | consistencyCheck(match: match2); |
1949 | QVERIFY(match2.isValid()); |
1950 | QVERIFY(match2.hasMatch()); |
1951 | QCOMPARE(match2.captured(), QStringLiteral("iss" )); |
1952 | QCOMPARE(match2.capturedStart(), 4); |
1953 | QCOMPARE(match2.capturedEnd(), 7); |
1954 | |
1955 | QVERIFY(!i.hasNext()); |
1956 | } |
1957 | { |
1958 | QRegularExpressionMatchIterator i = re.globalMatch(subject: subject.mid(position: 1)); |
1959 | QVERIFY(i.isValid()); |
1960 | |
1961 | consistencyCheck(iterator: i); |
1962 | QVERIFY(i.hasNext()); |
1963 | const QRegularExpressionMatch match = i.next(); |
1964 | consistencyCheck(match); |
1965 | QVERIFY(match.isValid()); |
1966 | QVERIFY(match.hasMatch()); |
1967 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1968 | QCOMPARE(match.capturedStart(), 3); |
1969 | QCOMPARE(match.capturedEnd(), 6); |
1970 | |
1971 | QVERIFY(!i.hasNext()); |
1972 | } |
1973 | { |
1974 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: subject.midRef(position: 1)); |
1975 | QVERIFY(i.isValid()); |
1976 | |
1977 | consistencyCheck(iterator: i); |
1978 | QVERIFY(i.hasNext()); |
1979 | const QRegularExpressionMatch match = i.next(); |
1980 | consistencyCheck(match); |
1981 | QVERIFY(match.isValid()); |
1982 | QVERIFY(match.hasMatch()); |
1983 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
1984 | QCOMPARE(match.capturedStart(), 3); |
1985 | QCOMPARE(match.capturedEnd(), 6); |
1986 | |
1987 | QVERIFY(!i.hasNext()); |
1988 | } |
1989 | { |
1990 | QRegularExpressionMatchIterator i = re.globalMatch(subject: subject.mid(position: 1), offset: 1); |
1991 | QVERIFY(i.isValid()); |
1992 | |
1993 | consistencyCheck(iterator: i); |
1994 | QVERIFY(i.hasNext()); |
1995 | const QRegularExpressionMatch match = i.next(); |
1996 | consistencyCheck(match); |
1997 | QVERIFY(match.isValid()); |
1998 | QVERIFY(match.hasMatch()); |
1999 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2000 | QCOMPARE(match.capturedStart(), 3); |
2001 | QCOMPARE(match.capturedEnd(), 6); |
2002 | |
2003 | QVERIFY(!i.hasNext()); |
2004 | } |
2005 | { |
2006 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: subject.midRef(position: 1), offset: 1); |
2007 | QVERIFY(i.isValid()); |
2008 | |
2009 | consistencyCheck(iterator: i); |
2010 | QVERIFY(i.hasNext()); |
2011 | const QRegularExpressionMatch match = i.next(); |
2012 | consistencyCheck(match); |
2013 | QVERIFY(match.isValid()); |
2014 | QVERIFY(match.hasMatch()); |
2015 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2016 | QCOMPARE(match.capturedStart(), 3); |
2017 | QCOMPARE(match.capturedEnd(), 6); |
2018 | |
2019 | QVERIFY(!i.hasNext()); |
2020 | } |
2021 | { |
2022 | QRegularExpressionMatchIterator i = re.globalMatch(subject: subject.mid(position: 1), offset: 1); |
2023 | QVERIFY(i.isValid()); |
2024 | |
2025 | consistencyCheck(iterator: i); |
2026 | QVERIFY(i.hasNext()); |
2027 | const QRegularExpressionMatch match = i.next(); |
2028 | consistencyCheck(match); |
2029 | QVERIFY(match.isValid()); |
2030 | QVERIFY(match.hasMatch()); |
2031 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2032 | QCOMPARE(match.capturedStart(), 3); |
2033 | QCOMPARE(match.capturedEnd(), 6); |
2034 | |
2035 | QVERIFY(!i.hasNext()); |
2036 | } |
2037 | { |
2038 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: subject.midRef(position: 1), offset: 1); |
2039 | QVERIFY(i.isValid()); |
2040 | |
2041 | consistencyCheck(iterator: i); |
2042 | QVERIFY(i.hasNext()); |
2043 | const QRegularExpressionMatch match = i.next(); |
2044 | consistencyCheck(match); |
2045 | QVERIFY(match.isValid()); |
2046 | QVERIFY(match.hasMatch()); |
2047 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2048 | QCOMPARE(match.capturedStart(), 3); |
2049 | QCOMPARE(match.capturedEnd(), 6); |
2050 | |
2051 | QVERIFY(!i.hasNext()); |
2052 | } |
2053 | |
2054 | { |
2055 | QRegularExpressionMatchIterator i = re.globalMatch(subject, offset: 4); |
2056 | QVERIFY(i.isValid()); |
2057 | |
2058 | consistencyCheck(iterator: i); |
2059 | QVERIFY(i.hasNext()); |
2060 | const QRegularExpressionMatch match = i.next(); |
2061 | consistencyCheck(match); |
2062 | QVERIFY(match.isValid()); |
2063 | QVERIFY(match.hasMatch()); |
2064 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2065 | QCOMPARE(match.capturedStart(), 4); |
2066 | QCOMPARE(match.capturedEnd(), 7); |
2067 | |
2068 | QVERIFY(!i.hasNext()); |
2069 | } |
2070 | { |
2071 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: QStringRef(&subject), offset: 4); |
2072 | QVERIFY(i.isValid()); |
2073 | |
2074 | consistencyCheck(iterator: i); |
2075 | QVERIFY(i.hasNext()); |
2076 | const QRegularExpressionMatch match = i.next(); |
2077 | consistencyCheck(match); |
2078 | QVERIFY(match.isValid()); |
2079 | QVERIFY(match.hasMatch()); |
2080 | QCOMPARE(match.captured(), QStringLiteral("iss" )); |
2081 | QCOMPARE(match.capturedStart(), 4); |
2082 | QCOMPARE(match.capturedEnd(), 7); |
2083 | |
2084 | QVERIFY(!i.hasNext()); |
2085 | } |
2086 | { |
2087 | QRegularExpressionMatchIterator i = re.globalMatch(subject: subject.mid(position: 4)); |
2088 | consistencyCheck(iterator: i); |
2089 | QVERIFY(i.isValid()); |
2090 | QVERIFY(!i.hasNext()); |
2091 | } |
2092 | { |
2093 | QRegularExpressionMatchIterator i = re.globalMatch(subjectRef: subject.midRef(position: 4)); |
2094 | consistencyCheck(iterator: i); |
2095 | QVERIFY(i.isValid()); |
2096 | QVERIFY(!i.hasNext()); |
2097 | } |
2098 | } |
2099 | } |
2100 | |
2101 | class MatcherThread : public QThread |
2102 | { |
2103 | public: |
2104 | explicit MatcherThread(const QRegularExpression &re, const QString &subject, QObject *parent = nullptr) |
2105 | : QThread(parent), |
2106 | m_re(re), |
2107 | m_subject(subject) |
2108 | { |
2109 | } |
2110 | |
2111 | private: |
2112 | static const int MATCH_ITERATIONS = 50; |
2113 | |
2114 | void run() override |
2115 | { |
2116 | yieldCurrentThread(); |
2117 | for (int i = 0; i < MATCH_ITERATIONS; ++i) |
2118 | m_re.match(subject: m_subject); |
2119 | } |
2120 | |
2121 | const QRegularExpression &m_re; |
2122 | const QString &m_subject; |
2123 | }; |
2124 | |
2125 | void tst_QRegularExpression::threadSafety_data() |
2126 | { |
2127 | QTest::addColumn<QString>(name: "pattern" ); |
2128 | QTest::addColumn<QString>(name: "subject" ); |
2129 | |
2130 | int i = 0; |
2131 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abcd" ; |
2132 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abd" ; |
2133 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abbbbcccd" ; |
2134 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abababcd" ; |
2135 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abcabcd" ; |
2136 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << "abccccccababd" ; |
2137 | |
2138 | { |
2139 | QString subject(512*1024, QLatin1Char('x')); |
2140 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << subject; |
2141 | } |
2142 | |
2143 | // pcre2 does not support JIT for winrt. As this test row takes a long time without JIT we skip |
2144 | // it for winrt as it might time out in COIN. |
2145 | #ifndef Q_OS_WINRT |
2146 | { |
2147 | QString subject = "ab" ; |
2148 | subject.append(s: QString(512*1024, QLatin1Char('x'))); |
2149 | subject.append(s: "c" ); |
2150 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << subject; |
2151 | } |
2152 | #endif // Q_OS_WINRT |
2153 | |
2154 | { |
2155 | QString subject = "ab" ; |
2156 | subject.append(s: QString(512*1024, QLatin1Char('x'))); |
2157 | subject.append(s: "cd" ); |
2158 | QTest::addRow(format: "pattern%d" , ++i) << "ab.*cd" << subject; |
2159 | } |
2160 | |
2161 | QTest::addRow(format: "pattern%d" , ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaabcde" ; |
2162 | QTest::addRow(format: "pattern%d" , ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaaaaabcde" ; |
2163 | } |
2164 | |
2165 | void tst_QRegularExpression::threadSafety() |
2166 | { |
2167 | QFETCH(QString, pattern); |
2168 | QFETCH(QString, subject); |
2169 | |
2170 | QElapsedTimer time; |
2171 | time.start(); |
2172 | static const int THREAD_SAFETY_ITERATIONS = 50; |
2173 | const int threadCount = qMax(a: QThread::idealThreadCount(), b: 4); |
2174 | |
2175 | for (int threadSafetyIteration = 0; threadSafetyIteration < THREAD_SAFETY_ITERATIONS && time.elapsed() < 2000; ++threadSafetyIteration) { |
2176 | QRegularExpression re(pattern); |
2177 | |
2178 | QVector<MatcherThread *> threads; |
2179 | for (int i = 0; i < threadCount; ++i) { |
2180 | MatcherThread *thread = new MatcherThread(re, subject); |
2181 | thread->start(); |
2182 | threads.push_back(t: thread); |
2183 | } |
2184 | |
2185 | for (int i = 0; i < threadCount; ++i) |
2186 | threads[i]->wait(); |
2187 | |
2188 | qDeleteAll(c: threads); |
2189 | } |
2190 | } |
2191 | |
2192 | void tst_QRegularExpression::returnsViewsIntoOriginalString() |
2193 | { |
2194 | // https://bugreports.qt.io/browse/QTBUG-98653 |
2195 | |
2196 | auto to_void = [](const QChar *p) -> const void* { return p; }; |
2197 | |
2198 | // GIVEN |
2199 | // a QString with dynamically-allocated data: |
2200 | const QString string = QLatin1String("A\nA\nB\nB\n\nC\nC" ); // NOT QStringLiteral! |
2201 | const auto stringDataAddress = to_void(string.data()); |
2202 | |
2203 | // and a view over said QString: |
2204 | QStringView view(string); |
2205 | const auto viewDataAddress = to_void(view.data()); |
2206 | QCOMPARE(stringDataAddress, viewDataAddress); |
2207 | |
2208 | // WHEN |
2209 | // we call view.split() with a temporary QRegularExpression object |
2210 | const auto split = view.split(sep: QRegularExpression( "(\r\n|\n|\r)" ), behavior: Qt::KeepEmptyParts); |
2211 | |
2212 | // THEN |
2213 | // the returned views should point into the underlying string: |
2214 | QCOMPARE(to_void(split.front().data()), stringDataAddress); |
2215 | } |
2216 | |
2217 | void tst_QRegularExpression::wildcard_data() |
2218 | { |
2219 | QTest::addColumn<QString>(name: "pattern" ); |
2220 | QTest::addColumn<QString>(name: "string" ); |
2221 | QTest::addColumn<int>(name: "foundIndex" ); |
2222 | |
2223 | auto addRow = [](const char *pattern, const char *string, int foundIndex) { |
2224 | QTest::newRow(dataTag: pattern) << pattern << string << foundIndex; |
2225 | }; |
2226 | |
2227 | addRow("*.html" , "test.html" , 0); |
2228 | addRow("*.html" , "test.htm" , -1); |
2229 | addRow("*bar*" , "foobarbaz" , 0); |
2230 | addRow("*" , "Qt Rocks!" , 0); |
2231 | addRow("*.html" , "test.html" , 0); |
2232 | addRow("*.h" , "test.cpp" , -1); |
2233 | addRow("*.???l" , "test.html" , 0); |
2234 | addRow("*?" , "test.html" , 0); |
2235 | addRow("*?ml" , "test.html" , 0); |
2236 | addRow("*[*]" , "test.html" , -1); |
2237 | addRow("*[?]" ,"test.html" , -1); |
2238 | addRow("*[?]ml" ,"test.h?ml" , 0); |
2239 | addRow("*[[]ml" ,"test.h[ml" , 0); |
2240 | addRow("*[]]ml" ,"test.h]ml" , 0); |
2241 | addRow("*.h[a-z]ml" , "test.html" , 0); |
2242 | addRow("*.h[A-Z]ml" , "test.html" , -1); |
2243 | addRow("*.h[A-Z]ml" , "test.hTml" , 0); |
2244 | addRow("*.h[!A-Z]ml" , "test.hTml" , -1); |
2245 | addRow("*.h[!A-Z]ml" , "test.html" , 0); |
2246 | addRow("*.h[!T]ml" , "test.hTml" , -1); |
2247 | addRow("*.h[!T]ml" , "test.html" , 0); |
2248 | addRow("*.h[!T]m[!L]" , "test.htmL" , -1); |
2249 | addRow("*.h[!T]m[!L]" , "test.html" , 0); |
2250 | addRow("*.h[][!]ml" , "test.h]ml" , 0); |
2251 | addRow("*.h[][!]ml" , "test.h[ml" , 0); |
2252 | addRow("*.h[][!]ml" , "test.h!ml" , 0); |
2253 | |
2254 | addRow("foo/*/bar" , "foo/baz/bar" , 0); |
2255 | addRow("foo/(*)/bar" , "foo/baz/bar" , -1); |
2256 | addRow("foo/(*)/bar" , "foo/(baz)/bar" , 0); |
2257 | addRow("foo/?/bar" , "foo/Q/bar" , 0); |
2258 | addRow("foo/?/bar" , "foo/Qt/bar" , -1); |
2259 | addRow("foo/(?)/bar" , "foo/Q/bar" , -1); |
2260 | addRow("foo/(?)/bar" , "foo/(Q)/bar" , 0); |
2261 | |
2262 | #ifdef Q_OS_WIN |
2263 | addRow("foo\\*\\bar" , "foo\\baz\\bar" , 0); |
2264 | addRow("foo\\(*)\\bar" , "foo\\baz\\bar" , -1); |
2265 | addRow("foo\\(*)\\bar" , "foo\\(baz)\\bar" , 0); |
2266 | addRow("foo\\?\\bar" , "foo\\Q\\bar" , 0); |
2267 | addRow("foo\\?\\bar" , "foo\\Qt\\bar" , -1); |
2268 | addRow("foo\\(?)\\bar" , "foo\\Q\\bar" , -1); |
2269 | addRow("foo\\(?)\\bar" , "foo\\(Q)\\bar" , 0); |
2270 | #endif |
2271 | } |
2272 | |
2273 | void tst_QRegularExpression::wildcard() |
2274 | { |
2275 | QFETCH(QString, pattern); |
2276 | QFETCH(QString, string); |
2277 | QFETCH(int, foundIndex); |
2278 | |
2279 | QRegularExpression re(QRegularExpression::wildcardToRegularExpression(str: pattern)); |
2280 | QRegularExpressionMatch match = re.match(subject: string); |
2281 | |
2282 | QCOMPARE(match.capturedStart(), foundIndex); |
2283 | } |
2284 | |
2285 | void tst_QRegularExpression::testInvalidWildcard_data() |
2286 | { |
2287 | QTest::addColumn<QString>(name: "pattern" ); |
2288 | QTest::addColumn<bool>(name: "isValid" ); |
2289 | |
2290 | QTest::newRow(dataTag: "valid []" ) << "[abc]" << true; |
2291 | QTest::newRow(dataTag: "valid ending ]" ) << "abc]" << true; |
2292 | QTest::newRow(dataTag: "invalid [" ) << "[abc" << false; |
2293 | QTest::newRow(dataTag: "ending [" ) << "abc[" << false; |
2294 | QTest::newRow(dataTag: "ending [^" ) << "abc[^" << false; |
2295 | QTest::newRow(dataTag: "ending [\\" ) << "abc[\\" << false; |
2296 | QTest::newRow(dataTag: "ending []" ) << "abc[]" << false; |
2297 | QTest::newRow(dataTag: "ending [[" ) << "abc[[" << false; |
2298 | } |
2299 | |
2300 | void tst_QRegularExpression::testInvalidWildcard() |
2301 | { |
2302 | QFETCH(QString, pattern); |
2303 | QFETCH(bool, isValid); |
2304 | |
2305 | QRegularExpression re(QRegularExpression::wildcardToRegularExpression(str: pattern)); |
2306 | QCOMPARE(re.isValid(), isValid); |
2307 | } |
2308 | |
2309 | QTEST_APPLESS_MAIN(tst_QRegularExpression) |
2310 | |
2311 | #include "tst_qregularexpression.moc" |
2312 | |