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
40Q_DECLARE_METATYPE(QRegularExpression::PatternOptions)
41Q_DECLARE_METATYPE(QRegularExpression::MatchType)
42Q_DECLARE_METATYPE(QRegularExpression::MatchOptions)
43
44class tst_QRegularExpression : public QObject
45{
46 Q_OBJECT
47
48private 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
89private:
90 void provideRegularExpressions();
91};
92
93struct 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};
115QT_BEGIN_NAMESPACE
116Q_DECLARE_TYPEINFO(Match, Q_MOVABLE_TYPE);
117QT_END_NAMESPACE
118
119Q_DECLARE_METATYPE(Match)
120
121bool 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
156bool operator==(const Match &m, const QRegularExpressionMatch &rem)
157{
158 return operator==(rem, m);
159}
160
161bool operator!=(const QRegularExpressionMatch &rem, const Match &m)
162{
163 return !operator==(rem, m);
164}
165
166bool operator!=(const Match &m, const QRegularExpressionMatch &rem)
167{
168 return !operator==(m, rem);
169}
170
171
172bool 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
191bool operator==(const QVector<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator)
192{
193 return operator==(iterator, expectedMatchList);
194}
195
196bool operator!=(const QRegularExpressionMatchIterator &iterator, const QVector<Match> &expectedMatchList)
197{
198 return !operator==(iterator, expectedMatchList);
199}
200
201bool operator!=(const QVector<Match> &expectedMatchList, const QRegularExpressionMatchIterator &iterator)
202{
203 return !operator==(expectedMatchList, iterator);
204}
205
206void 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
252void 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
291template<typename Result>
292static void prepareResultForNoMatchType(Result *r, const Result &orig)
293{
294 Q_UNUSED(r);
295 Q_UNUSED(orig);
296}
297
298static void prepareResultForNoMatchType(Match *m, const Match &orig)
299{
300 m->isValid = orig.isValid;
301}
302
303template<typename QREMatch, typename QREMatchFunc, typename Subject, typename Result>
304static void testMatchImpl(const QRegularExpression &regexp,
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
336template<typename QREMatch, typename QREMatchFuncForString, typename QREMatchFuncForStringRef, typename Result>
337static void testMatch(const QRegularExpression &regexp,
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
359typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringPMF)(const QString &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
360typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringRefPMF)(const QStringRef &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
361typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringPMF)(const QString &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
362typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringRefPMF)(const QStringRef &, int, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const;
363
364void 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
415void 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
440void tst_QRegularExpression::gettersSetters_data()
441{
442 provideRegularExpressions();
443}
444
445void tst_QRegularExpression::gettersSetters()
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
473void 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
502void 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
511void 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
533void 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
551void 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
618void 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
629void 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
861void 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
879void 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
1132void 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
1151void 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
1409void 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
1428void tst_QRegularExpression::serialize_data()
1429{
1430 provideRegularExpressions();
1431}
1432
1433void 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
1452static 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
1488void tst_QRegularExpression::operatoreq_data()
1489{
1490 provideRegularExpressions();
1491}
1492
1493void 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
1517void 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
1537void 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...
1548typedef QMultiHash<QString, int> StringToIntMap;
1549Q_DECLARE_METATYPE(StringToIntMap)
1550
1551void 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
1603void 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
1628void 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
1658void 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
1668void 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
1686void 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
1695void 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
1712void 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
1730void 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
1744void 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
2101class MatcherThread : public QThread
2102{
2103public:
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
2111private:
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
2125void 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
2165void 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
2192void 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
2217void 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
2273void 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
2285void 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
2300void 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
2309QTEST_APPLESS_MAIN(tst_QRegularExpression)
2310
2311#include "tst_qregularexpression.moc"
2312

source code of qtbase/tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp