diff options
Diffstat (limited to 'tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp')
-rw-r--r-- | tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp | 638 |
1 files changed, 459 insertions, 179 deletions
diff --git a/tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp index dd96b158f4..72c49d2a9c 100644 --- a/tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/text/qregularexpression/tst_qregularexpression.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. -** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. +// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> #include <qstring.h> #include <qlist.h> #include <qstringlist.h> @@ -37,6 +12,9 @@ #include <qregularexpression.h> #include <qthread.h> +#include <iostream> +#include <optional> + Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) Q_DECLARE_METATYPE(QRegularExpression::MatchType) Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) @@ -45,8 +23,14 @@ class tst_QRegularExpression : public QObject { Q_OBJECT +public: + static void initMain(); + private slots: void defaultConstructors(); + void moveSemantics(); + void moveSemanticsMatch(); + void moveSemanticsMatchIterator(); void gettersSetters_data(); void gettersSetters(); void escape_data(); @@ -80,6 +64,7 @@ private slots: void threadSafety_data(); void threadSafety(); + void returnsViewsIntoOriginalString(); void wildcard_data(); void wildcard(); void testInvalidWildcard_data(); @@ -89,6 +74,8 @@ private: void provideRegularExpressions(); }; +using CapturedList = QVector<std::optional<QString>>; + struct Match { Match() @@ -108,11 +95,11 @@ struct Match bool isValid; bool hasMatch; bool hasPartialMatch; - QStringList captured; - QHash<QString, QString> namedCaptured; + CapturedList captured; + QHash<QString, std::optional<QString>> namedCaptured; }; QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Match, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Match, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(Match) @@ -126,27 +113,53 @@ bool operator==(const QRegularExpressionMatch &rem, const Match &m) if ((rem.hasMatch() != m.hasMatch) || (rem.hasPartialMatch() != m.hasPartialMatch)) return false; if (rem.hasMatch() || rem.hasPartialMatch()) { + if (!rem.hasCaptured(0)) + return false; if (rem.lastCapturedIndex() != (m.captured.size() - 1)) return false; for (int i = 0; i <= rem.lastCapturedIndex(); ++i) { + auto mMaybeCaptured = m.captured.at(i); QString remCaptured = rem.captured(i); - QString mCaptured = m.captured.at(i); - if (remCaptured != mCaptured - || remCaptured.isNull() != mCaptured.isNull() - || remCaptured.isEmpty() != mCaptured.isEmpty()) { - return false; + if (!mMaybeCaptured) { + if (rem.hasCaptured(i)) + return false; + if (!remCaptured.isNull()) + return false; + } else { + if (!rem.hasCaptured(i)) + return false; + QString mCaptured = *mMaybeCaptured; + if (remCaptured != mCaptured + || remCaptured.isNull() != mCaptured.isNull() + || remCaptured.isEmpty() != mCaptured.isEmpty()) { + return false; + } } } for (auto it = m.namedCaptured.begin(), end = m.namedCaptured.end(); it != end; ++it) { - const QString remCaptured = rem.captured(it.key()); - const QString mCaptured = it.value(); - if (remCaptured != mCaptured - || remCaptured.isNull() != mCaptured.isNull() - || remCaptured.isEmpty() != mCaptured.isEmpty()) { - return false; + const QString capturedGroupName = it.key(); + const QString remCaptured = rem.captured(capturedGroupName); + const auto mMaybeCaptured = it.value(); + if (!mMaybeCaptured) { + if (rem.hasCaptured(capturedGroupName)) + return false; + if (!remCaptured.isNull()) + return false; + } else { + if (!rem.hasCaptured(capturedGroupName)) + return false; + const auto mCaptured = *mMaybeCaptured; + if (remCaptured != mCaptured + || remCaptured.isNull() != mCaptured.isNull() + || remCaptured.isEmpty() != mCaptured.isEmpty()) { + return false; + } } } + } else { + if (rem.hasCaptured(0)) + return false; } return true; @@ -249,8 +262,11 @@ void consistencyCheck(const QRegularExpressionMatch &match) QVERIFY((endPos - startPos) == length); QVERIFY(captured == capturedView); } else { - QVERIFY(startPos == -1); - QVERIFY(endPos == -1); + // A null capture can either mean no capture at all, + // or capture of length 0 over a null subject. + QVERIFY(startPos == endPos); + QVERIFY(((startPos == -1) && (endPos == -1)) // no capture + || ((startPos == 0) && (endPos == 0))); // null subject QVERIFY((endPos - startPos) == length); QVERIFY(capturedView.isNull()); } @@ -274,7 +290,11 @@ void consistencyCheck(const QRegularExpressionMatchIterator &iterator) QRegularExpressionMatch peeked = i.peekNext(); QRegularExpressionMatch match = i.next(); consistencyCheck(peeked); + if (QTest::currentTestFailed()) + return; consistencyCheck(match); + if (QTest::currentTestFailed()) + return; QVERIFY(match.isValid()); QVERIFY(match.hasMatch() || match.hasPartialMatch()); QCOMPARE(i.regularExpression(), match.regularExpression()); @@ -373,6 +393,7 @@ static void testMatch(const QRegularExpression ®exp, result); } +// ### Qt 7: there should no longer be the need for these typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringPMF)(const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; typedef QRegularExpressionMatch (QRegularExpression::*QREMatchStringViewPMF)(QStringView, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; typedef QRegularExpressionMatchIterator (QRegularExpression::*QREGlobalMatchStringPMF)(const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions) const; @@ -429,11 +450,25 @@ void tst_QRegularExpression::provideRegularExpressions() | QRegularExpression::InvertedGreedinessOption); } +static const char enableJitEnvironmentVariable[] = "QT_ENABLE_REGEXP_JIT"; + +void tst_QRegularExpression::initMain() +{ + if (!qEnvironmentVariableIsSet(enableJitEnvironmentVariable)) { + std::cerr << "Enabling QRegularExpression JIT for testing; set QT_ENABLE_REGEXP_JIT to 0 to disable it.\n"; + qputenv(enableJitEnvironmentVariable, "1"); + } +} + void tst_QRegularExpression::defaultConstructors() { QRegularExpression re; QCOMPARE(re.pattern(), QString()); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); + QCOMPARE(re.isValid(), true); + QCOMPARE(re.patternErrorOffset(), -1); + QCOMPARE(re.captureCount(), 0); + QCOMPARE(re.namedCaptureGroups(), QStringList { QString() }); QRegularExpressionMatch match; QCOMPARE(match.regularExpression(), QRegularExpression()); @@ -444,6 +479,15 @@ void tst_QRegularExpression::defaultConstructors() QCOMPARE(match.hasPartialMatch(), false); QCOMPARE(match.isValid(), true); QCOMPARE(match.lastCapturedIndex(), -1); + QCOMPARE(match.captured(), QString()); + QCOMPARE(match.captured("test"), QString()); + QCOMPARE(match.capturedTexts(), QStringList()); + QCOMPARE(match.capturedStart(), -1); + QCOMPARE(match.capturedEnd(), -1); + QCOMPARE(match.capturedLength(), 0); + QCOMPARE(match.capturedStart("test"), -1); + QCOMPARE(match.capturedEnd("test"), -1); + QCOMPARE(match.capturedLength("test"), 0); QRegularExpressionMatchIterator iterator; QCOMPARE(iterator.regularExpression(), QRegularExpression()); @@ -454,6 +498,101 @@ void tst_QRegularExpression::defaultConstructors() QCOMPARE(iterator.hasNext(), false); } +void tst_QRegularExpression::moveSemantics() +{ + const QString pattern = "pattern"; + const QRegularExpression::PatternOptions options = QRegularExpression::CaseInsensitiveOption; + QRegularExpression expr1(pattern, options); + QCOMPARE(expr1.pattern(), pattern); + QCOMPARE(expr1.patternOptions(), options); + + QRegularExpression expr2(std::move(expr1)); + QCOMPARE(expr2.pattern(), pattern); + QCOMPARE(expr2.patternOptions(), options); + + const QString pattern2 = "pattern2"; + QRegularExpression expr3(pattern2); + QCOMPARE(expr3.pattern(), pattern2); + QCOMPARE(expr3.patternOptions(), QRegularExpression::NoPatternOption); + + // check that (move)assigning to the moved-from object is ok + expr1 = std::move(expr3); + QCOMPARE(expr1.pattern(), pattern2); + QCOMPARE(expr1.patternOptions(), QRegularExpression::NoPatternOption); + + // here expr3 is in the moved-from state, so destructor call for moved-from + // object is also checked +} + +void tst_QRegularExpression::moveSemanticsMatch() +{ + QRegularExpression re("test"); + QRegularExpressionMatch match1 = re.match("abctestdef"); + QCOMPARE(match1.hasMatch(), true); + QCOMPARE(match1.capturedStart(), 3); + QCOMPARE(match1.capturedEnd(), 7); + + QRegularExpressionMatch match2(std::move(match1)); + QCOMPARE(match2.hasMatch(), true); + QCOMPARE(match2.capturedStart(), 3); + QCOMPARE(match2.capturedEnd(), 7); + consistencyCheck(match2); + if (QTest::currentTestFailed()) + return; + + QRegularExpressionMatch match3 = re.match("test1"); + QCOMPARE(match3.hasMatch(), true); + QCOMPARE(match3.capturedStart(), 0); + QCOMPARE(match3.capturedEnd(), 4); + + // check that (move)assigning to the moved-from object is ok + match1 = std::move(match3); + QCOMPARE(match1.hasMatch(), true); + QCOMPARE(match1.capturedStart(), 0); + QCOMPARE(match1.capturedEnd(), 4); + consistencyCheck(match1); + if (QTest::currentTestFailed()) + return; + + // here match3 is in the moved-from state, so destructor call for moved-from + // object is also checked +} + +void tst_QRegularExpression::moveSemanticsMatchIterator() +{ + QRegularExpression re("(\\w+)"); + QRegularExpressionMatchIterator it1 = re.globalMatch("some test"); + QVERIFY(it1.isValid()); + QVERIFY(it1.hasNext()); + QCOMPARE(it1.regularExpression(), re); + + QRegularExpressionMatchIterator it2(std::move(it1)); + QVERIFY(it2.isValid()); + QVERIFY(it2.hasNext()); + QCOMPARE(it2.regularExpression(), re); + consistencyCheck(it2); + if (QTest::currentTestFailed()) + return; + + QRegularExpression re2("test"); + QRegularExpressionMatchIterator it3 = re2.globalMatch("123test456"); + QVERIFY(it3.isValid()); + QVERIFY(it3.hasNext()); + QCOMPARE(it3.regularExpression(), re2); + + // check that (move)assigning to the moved-from object is ok + it1 = std::move(it3); + QVERIFY(it1.isValid()); + QVERIFY(it1.hasNext()); + QCOMPARE(it1.regularExpression(), re2); + consistencyCheck(it1); + if (QTest::currentTestFailed()) + return; + + // here it3 is in the moved-from state, so destructor call for moved-from + // object is also checked +} + void tst_QRegularExpression::gettersSetters_data() { provideRegularExpressions(); @@ -549,18 +688,23 @@ void tst_QRegularExpression::validity_data() void tst_QRegularExpression::validity() { + static const QRegularExpression ignoreMessagePattern( + "^" + QRegularExpression::escape("QRegularExpressionPrivate::doMatch(): " + "called on an invalid QRegularExpression object") + ); + QFETCH(QString, pattern); QFETCH(bool, validity); QRegularExpression re(pattern); QCOMPARE(re.isValid(), validity); if (!validity) - QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + QTest::ignoreMessage(QtWarningMsg, ignoreMessagePattern); QRegularExpressionMatch match = re.match("a pattern"); QCOMPARE(match.isValid(), validity); consistencyCheck(match); if (!validity) - QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + QTest::ignoreMessage(QtWarningMsg, ignoreMessagePattern); QRegularExpressionMatchIterator iterator = re.globalMatch("a pattern"); QCOMPARE(iterator.isValid(), validity); } @@ -685,7 +829,7 @@ void tst_QRegularExpression::normalMatch_data() m.clear(); m.isValid = true; m.hasMatch = true; - m.captured << " string" << QString() << "string"; + m.captured << " string" << std::nullopt << "string"; QTest::newRow("match04") << QRegularExpression("(\\w+)? (\\w+)") << " string" << qsizetype(0) @@ -745,15 +889,33 @@ void tst_QRegularExpression::normalMatch_data() << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) << m; + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString(); + QTest::newRow("empty-in-null-string") << QRegularExpression("") + << QString() + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString(""); + QTest::newRow("empty-in-empty-string") << QRegularExpression("") + << QString("") + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + // non existing names for capturing groups m.clear(); m.isValid = true; m.hasMatch = true; m.captured << "a string" << "a" << "string"; m.namedCaptured["article"] = "a"; m.namedCaptured["noun"] = "string"; - m.namedCaptured["nonexisting1"] = QString(); - m.namedCaptured["nonexisting2"] = QString(); - m.namedCaptured["nonexisting3"] = QString(); + m.namedCaptured["nonexisting1"] = std::nullopt; + m.namedCaptured["nonexisting2"] = std::nullopt; + m.namedCaptured["nonexisting3"] = std::nullopt; QTest::newRow("match10") << QRegularExpression("(?<article>\\w+) (?<noun>\\w+)") << "a string" << qsizetype(0) @@ -764,7 +926,7 @@ void tst_QRegularExpression::normalMatch_data() m.isValid = true; m.hasMatch = true; m.captured << "" << ""; m.namedCaptured["digits"] = ""; // empty VS null - m.namedCaptured["nonexisting"] = QString(); + m.namedCaptured["nonexisting"] = std::nullopt; QTest::newRow("match11") << QRegularExpression("(?<digits>\\d*)") << "abcde" << qsizetype(0) @@ -786,6 +948,56 @@ void tst_QRegularExpression::normalMatch_data() // *** m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString() << QString(); + QTest::newRow("capture-in-null-string") + << QRegularExpression("(a*)") + << QString() + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString() << QString() << QString(); + QTest::newRow("capture-in-null-string-2") + << QRegularExpression("(a*)(b*)") + << QString() + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << QString(); + QTest::newRow("no-capture-in-null-string") + << QRegularExpression("(a+)?") + << QString() + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "bb" << QString("") << "bb"; + QTest::newRow("empty-capture-in-non-null-string") + << QRegularExpression("(a*)(b*)") + << QString("bbc") + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); + m.isValid = true; m.hasMatch = true; + m.captured << "bb" << std::nullopt << "bb"; + QTest::newRow("no-capture-in-non-null-string") + << QRegularExpression("(a+)?(b+)?") + << QString("bbc") + << qsizetype(0) + << QRegularExpression::MatchOptions(QRegularExpression::NoMatchOption) + << m; + + m.clear(); m.isValid = true; QTest::newRow("nomatch01") << QRegularExpression("\\d+") << "a string" @@ -885,7 +1097,7 @@ void tst_QRegularExpression::normalMatch() testMatch<QRegularExpressionMatch>(regexp, static_cast<QREMatchStringPMF>(&QRegularExpression::match), - static_cast<QREMatchStringViewPMF>(&QRegularExpression::match), + static_cast<QREMatchStringViewPMF>(&QRegularExpression::matchView), subject, offset, QRegularExpression::NormalMatch, @@ -1157,7 +1369,7 @@ void tst_QRegularExpression::partialMatch() testMatch<QRegularExpressionMatch>(regexp, static_cast<QREMatchStringPMF>(&QRegularExpression::match), - static_cast<QREMatchStringViewPMF>(&QRegularExpression::match), + static_cast<QREMatchStringViewPMF>(&QRegularExpression::matchView), subject, offset, matchType, @@ -1180,11 +1392,11 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; QTest::newRow("globalmatch01") << QRegularExpression("\\w+") << "the quick fox" @@ -1196,11 +1408,11 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the" << "t" << "he"; + m.captured = CapturedList() << "the" << "t" << "he"; matchList << m; - m.captured = QStringList() << "quick" << "q" << "uick"; + m.captured = CapturedList() << "quick" << "q" << "uick"; matchList << m; - m.captured = QStringList() << "fox" << "f" << "ox"; + m.captured = CapturedList() << "fox" << "f" << "ox"; matchList << m; QTest::newRow("globalmatch02") << QRegularExpression("(\\w+?)(\\w+)") << "the quick fox" @@ -1212,13 +1424,13 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "ACA""GTG""CGA""AAA"; + m.captured = CapturedList() << "ACA""GTG""CGA""AAA"; matchList << m; - m.captured = QStringList() << "AAA"; + m.captured = CapturedList() << "AAA"; matchList << m; - m.captured = QStringList() << "AAG""GAA""AAG""AAA"; + m.captured = CapturedList() << "AAG""GAA""AAG""AAA"; matchList << m; - m.captured = QStringList() << "AAA"; + m.captured = CapturedList() << "AAA"; matchList << m; QTest::newRow("globalmatch03") << QRegularExpression("\\G(?:\\w\\w\\w)*?AAA") << "ACA""GTG""CGA""AAA""AAA""AAG""GAA""AAG""AAA""AAA" @@ -1237,19 +1449,19 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "c"; + m.captured = CapturedList() << "c"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "c"; + m.captured = CapturedList() << "c"; matchList << m; - m.captured = QStringList() << "aabb"; + m.captured = CapturedList() << "aabb"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures01") << QRegularExpression("a*b*|c") @@ -1262,17 +1474,17 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures02") << QRegularExpression(".*") @@ -1285,19 +1497,19 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures03") << QRegularExpression(".*") @@ -1310,17 +1522,17 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures04") << QRegularExpression("(*CRLF).*") @@ -1333,19 +1545,19 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures05") << QRegularExpression("(*CRLF).*") @@ -1358,21 +1570,21 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "the"; + m.captured = CapturedList() << "the"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "quick"; + m.captured = CapturedList() << "quick"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "fox"; + m.captured = CapturedList() << "fox"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "jumped"; + m.captured = CapturedList() << "jumped"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures06") << QRegularExpression("(*ANYCRLF).*") @@ -1385,17 +1597,17 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << "ABC"; + m.captured = CapturedList() << "ABC"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "DEF"; + m.captured = CapturedList() << "DEF"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << "GHI"; + m.captured = CapturedList() << "GHI"; matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures07") << QRegularExpression("[\\x{0000}-\\x{FFFF}]*") << QString::fromUtf8("ABC""\xf0\x9d\x85\x9d""DEF""\xf0\x9d\x85\x9e""GHI") @@ -1407,13 +1619,13 @@ void tst_QRegularExpression::globalMatch_data() matchList.clear(); m.clear(); m.isValid = true; m.hasMatch = true; - m.captured = QStringList() << QString::fromUtf8("ABC""\xc3\x80"); + m.captured = CapturedList() << QString::fromUtf8("ABC""\xc3\x80"); matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; - m.captured = QStringList() << QString::fromUtf8("\xc3\x80""DEF""\xc3\x80"); + m.captured = CapturedList() << QString::fromUtf8("\xc3\x80""DEF""\xc3\x80"); matchList << m; - m.captured = QStringList() << ""; + m.captured = CapturedList() << ""; matchList << m; QTest::newRow("globalmatch_emptycaptures08") << QRegularExpression("[\\x{0000}-\\x{FFFF}]*") << QString::fromUtf8("ABC""\xc3\x80""\xf0\x9d\x85\x9d""\xc3\x80""DEF""\xc3\x80") @@ -1434,7 +1646,7 @@ void tst_QRegularExpression::globalMatch() testMatch<QRegularExpressionMatchIterator>(regexp, static_cast<QREGlobalMatchStringPMF>(&QRegularExpression::globalMatch), - static_cast<QREGlobalMatchStringViewPMF>(&QRegularExpression::globalMatch), + static_cast<QREGlobalMatchStringViewPMF>(&QRegularExpression::globalMatchView), subject, offset, matchType, @@ -1662,12 +1874,14 @@ void tst_QRegularExpression::captureNamesNul() QString captureName("name"); QCOMPARE(m.captured(captureName), "456"); QCOMPARE(m.captured(QStringView(captureName)), "456"); + QCOMPARE(m.captured(QAnyStringView(captureName)), "456"); QCOMPARE(m.captured(qToStringViewIgnoringNull(captureName)), "456"); QCOMPARE(m.captured(u"name"), "456"); captureName = "anotherName"; QCOMPARE(m.captured(captureName), "789"); QCOMPARE(m.captured(QStringView(captureName)), "789"); + QCOMPARE(m.captured(QAnyStringView(captureName)), "789"); QCOMPARE(m.captured(qToStringViewIgnoringNull(captureName)), "789"); QCOMPARE(m.captured(u"anotherName"), "789"); } @@ -1774,7 +1988,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QCOMPARE(match.capturedEnd(), 4); } { - const QRegularExpressionMatch match = re.match(QStringView(subject)); + const QRegularExpressionMatch match = re.matchView(QStringView(subject)); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(match.hasMatch()); @@ -1792,7 +2006,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QCOMPARE(match.capturedEnd(), 4); } { - const QRegularExpressionMatch match = re.match(QStringView(subject), 1); + const QRegularExpressionMatch match = re.matchView(QStringView(subject), 1); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(match.hasMatch()); @@ -1810,7 +2024,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QCOMPARE(match.capturedEnd(), 6); } { - const QRegularExpressionMatch match = re.match(QStringView(subject).mid(1)); + const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(1)); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(match.hasMatch()); @@ -1828,7 +2042,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QCOMPARE(match.capturedEnd(), 6); } { - const QRegularExpressionMatch match = re.match(QStringView(subject).mid(1), 1); + const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(1), 1); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(match.hasMatch()); @@ -1846,7 +2060,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QCOMPARE(match.capturedEnd(), 7); } { - const QRegularExpressionMatch match = re.match(QStringView(subject), 4); + const QRegularExpressionMatch match = re.matchView(QStringView(subject), 4); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(match.hasMatch()); @@ -1861,7 +2075,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!match.hasMatch()); } { - const QRegularExpressionMatch match = re.match(QStringView(subject).mid(4)); + const QRegularExpressionMatch match = re.matchView(QStringView(subject).mid(4)); consistencyCheck(match); QVERIFY(match.isValid()); QVERIFY(!match.hasMatch()); @@ -1894,7 +2108,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject)); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject)); QVERIFY(i.isValid()); consistencyCheck(i); @@ -1946,7 +2160,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject), 1); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject), 1); QVERIFY(i.isValid()); consistencyCheck(i); @@ -1988,7 +2202,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1)); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1)); QVERIFY(i.isValid()); consistencyCheck(i); @@ -2020,7 +2234,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1), 1); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1), 1); QVERIFY(i.isValid()); consistencyCheck(i); @@ -2052,7 +2266,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(1), 1); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(1), 1); QVERIFY(i.isValid()); consistencyCheck(i); @@ -2085,7 +2299,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject), 4); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject), 4); QVERIFY(i.isValid()); consistencyCheck(i); @@ -2107,7 +2321,7 @@ void tst_QRegularExpression::QStringAndQStringViewEquivalence() QVERIFY(!i.hasNext()); } { - QRegularExpressionMatchIterator i = re.globalMatch(QStringView(subject).mid(4)); + QRegularExpressionMatchIterator i = re.globalMatchView(QStringView(subject).mid(4)); consistencyCheck(i); QVERIFY(i.isValid()); QVERIFY(!i.hasNext()); @@ -2132,7 +2346,7 @@ private: { yieldCurrentThread(); for (int i = 0; i < MATCH_ITERATIONS; ++i) - m_re.match(m_subject); + (void)m_re.match(m_subject); } const QRegularExpression &m_re; @@ -2177,6 +2391,10 @@ void tst_QRegularExpression::threadSafety_data() void tst_QRegularExpression::threadSafety() { +#if defined(Q_OS_WASM) + QSKIP("This test misbehaves on WASM. Investigation needed (QTBUG-110067)"); +#endif + QFETCH(QString, pattern); QFETCH(QString, subject); @@ -2202,59 +2420,111 @@ void tst_QRegularExpression::threadSafety() } } +void tst_QRegularExpression::returnsViewsIntoOriginalString() +{ + // https://bugreports.qt.io/browse/QTBUG-98653 + + auto to_void = [](const auto *p) -> const void* { return p; }; + + // GIVEN + // a QString with dynamically-allocated data: + const QString string = QLatin1String("A\nA\nB\nB\n\nC\nC"); // NOT QStringLiteral! + const auto stringDataAddress = to_void(string.data()); + + // and a view over said QString: + QStringView view(string); + const auto viewDataAddress = to_void(view.data()); + QCOMPARE(stringDataAddress, viewDataAddress); + + // WHEN + // we call view.split() with a temporary QRegularExpression object + const auto split = view.split(QRegularExpression( "(\r\n|\n|\r)" ), Qt::KeepEmptyParts); + + // THEN + // the returned views should point into the underlying string: +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QEXPECT_FAIL("", "QTBUG-98653", Continue); +#endif + QCOMPARE(to_void(split.front().data()), stringDataAddress); +} + void tst_QRegularExpression::wildcard_data() { QTest::addColumn<QString>("pattern"); QTest::addColumn<QString>("string"); - QTest::addColumn<qsizetype>("foundIndex"); + QTest::addColumn<bool>("matchesPathGlob"); + QTest::addColumn<bool>("matchesNonPathGlob"); + QTest::addColumn<bool>("anchored"); - auto addRow = [](const char *pattern, const char *string, qsizetype foundIndex) { - QTest::newRow(pattern) << pattern << string << foundIndex; + auto addRow = [](const char *pattern, const char *string, bool matchesPathGlob, bool matchesNonPathGlob, bool anchored = true) { + QTest::addRow("%s@%s", pattern, string) << pattern << string << matchesPathGlob << matchesNonPathGlob << anchored; }; - addRow("*.html", "test.html", 0); - addRow("*.html", "test.htm", -1); - addRow("*bar*", "foobarbaz", 0); - addRow("*", "Qt Rocks!", 0); - addRow("*.html", "test.html", 0); - addRow("*.h", "test.cpp", -1); - addRow("*.???l", "test.html", 0); - addRow("*?", "test.html", 0); - addRow("*?ml", "test.html", 0); - addRow("*[*]", "test.html", -1); - addRow("*[?]","test.html", -1); - addRow("*[?]ml","test.h?ml", 0); - addRow("*[[]ml","test.h[ml", 0); - addRow("*[]]ml","test.h]ml", 0); - addRow("*.h[a-z]ml", "test.html", 0); - addRow("*.h[A-Z]ml", "test.html", -1); - addRow("*.h[A-Z]ml", "test.hTml", 0); - addRow("*.h[!A-Z]ml", "test.hTml", -1); - addRow("*.h[!A-Z]ml", "test.html", 0); - addRow("*.h[!T]ml", "test.hTml", -1); - addRow("*.h[!T]ml", "test.html", 0); - addRow("*.h[!T]m[!L]", "test.htmL", -1); - addRow("*.h[!T]m[!L]", "test.html", 0); - addRow("*.h[][!]ml", "test.h]ml", 0); - addRow("*.h[][!]ml", "test.h[ml", 0); - addRow("*.h[][!]ml", "test.h!ml", 0); - - addRow("foo/*/bar", "foo/baz/bar", 0); - addRow("foo/(*)/bar", "foo/baz/bar", -1); - addRow("foo/(*)/bar", "foo/(baz)/bar", 0); - addRow("foo/?/bar", "foo/Q/bar", 0); - addRow("foo/?/bar", "foo/Qt/bar", -1); - addRow("foo/(?)/bar", "foo/Q/bar", -1); - addRow("foo/(?)/bar", "foo/(Q)/bar", 0); + addRow("*.html", "test.html", true, true); + addRow("*.html", "test.htm", false, false); + addRow("*bar*", "foobarbaz", true, true); + addRow("*", "Qt Rocks!", true, true); + addRow("*.h", "test.cpp", false, false); + addRow("*.???l", "test.html", true, true); + addRow("*?", "test.html", true, true); + addRow("*?ml", "test.html", true, true); + addRow("*[*]", "test.html", false, false); + addRow("*[?]","test.html", false, false); + addRow("*[?]ml","test.h?ml", true, true); + addRow("*[[]ml","test.h[ml", true, true); + addRow("*[]]ml","test.h]ml", true, true); + addRow("*.h[a-z]ml", "test.html", true, true); + addRow("*.h[A-Z]ml", "test.html", false, false); + addRow("*.h[A-Z]ml", "test.hTml", true, true); + addRow("*.h[!A-Z]ml", "test.hTml", false, false); + addRow("*.h[!A-Z]ml", "test.html", true, true); + addRow("*.h[!T]ml", "test.hTml", false, false); + addRow("*.h[!T]ml", "test.html", true, true); + addRow("*.h[!T]m[!L]", "test.htmL", false, false); + addRow("*.h[!T]m[!L]", "test.html", true, true); + addRow("*.h[][!]ml", "test.h]ml", true, true); + addRow("*.h[][!]ml", "test.h[ml", true, true); + addRow("*.h[][!]ml", "test.h!ml", true, true); + + addRow("foo/*/bar", "foo/baz/bar", true, true); + addRow("foo/*/bar", "foo/fie/baz/bar", false, true); + addRow("foo?bar", "foo/bar", false, true); + addRow("foo/(*)/bar", "foo/baz/bar", false, false); + addRow("foo/(*)/bar", "foo/(baz)/bar", true, true); + addRow("foo/?/bar", "foo/Q/bar", true, true); + addRow("foo/?/bar", "foo/Qt/bar", false, false); + addRow("foo/(?)/bar", "foo/Q/bar", false, false); + addRow("foo/(?)/bar", "foo/(Q)/bar", true, true); + + addRow("foo*bar", "foo/fie/baz/bar", false, true); + addRow("foo*bar", "foo bar", true, true); + addRow("foo*bar", "foo\tbar", true, true); + addRow("foo*bar", "foo\nbar", true, true); + addRow("foo*bar", "foo\r\nbar", true, true); + + // different anchor modes + addRow("foo", "afoob", false, false, true); + addRow("foo", "afoob", true, true, false); + + addRow("fie*bar", "foo/fie/baz/bar", false, false, true); + addRow("fie*bar", "foo/fie/baz/bar", false, true, false); #ifdef Q_OS_WIN - addRow("foo\\*\\bar", "foo\\baz\\bar", 0); - addRow("foo\\(*)\\bar", "foo\\baz\\bar", -1); - addRow("foo\\(*)\\bar", "foo\\(baz)\\bar", 0); - addRow("foo\\?\\bar", "foo\\Q\\bar", 0); - addRow("foo\\?\\bar", "foo\\Qt\\bar", -1); - addRow("foo\\(?)\\bar", "foo\\Q\\bar", -1); - addRow("foo\\(?)\\bar", "foo\\(Q)\\bar", 0); + addRow("foo\\*\\bar", "foo\\baz\\bar", true, true); + addRow("foo\\*\\bar", "foo/baz/bar", true, false); + addRow("foo\\*\\bar", "foo/baz\\bar", true, false); + addRow("foo\\*\\bar", "foo\\fie\\baz\\bar", false, true); + addRow("foo\\*\\bar", "foo/fie/baz/bar", false, false); + addRow("foo/*/bar", "foo\\baz\\bar", true, false); + addRow("foo/*/bar", "foo/baz/bar", true, true); + addRow("foo/*/bar", "foo\\fie\\baz\\bar", false, false); + addRow("foo/*/bar", "foo/fie/baz/bar", false, true); + addRow("foo\\(*)\\bar", "foo\\baz\\bar", false, false); + addRow("foo\\(*)\\bar", "foo\\(baz)\\bar", true, true); + addRow("foo\\?\\bar", "foo\\Q\\bar", true, true); + addRow("foo\\?\\bar", "foo\\Qt\\bar", false, false); + addRow("foo\\(?)\\bar", "foo\\Q\\bar", false, false); + addRow("foo\\(?)\\bar", "foo\\(Q)\\bar", true, true); #endif } @@ -2262,12 +2532,22 @@ void tst_QRegularExpression::wildcard() { QFETCH(QString, pattern); QFETCH(QString, string); - QFETCH(qsizetype, foundIndex); + QFETCH(bool, matchesPathGlob); + QFETCH(bool, matchesNonPathGlob); + QFETCH(bool, anchored); - QRegularExpression re(QRegularExpression::wildcardToRegularExpression(pattern)); - QRegularExpressionMatch match = re.match(string); + QRegularExpression::WildcardConversionOptions options = {}; + if (!anchored) + options |= QRegularExpression::UnanchoredWildcardConversion; - QCOMPARE(match.capturedStart(), foundIndex); + { + QRegularExpression re(QRegularExpression::wildcardToRegularExpression(pattern, options)); + QCOMPARE(string.contains(re), matchesPathGlob); + } + { + QRegularExpression re(QRegularExpression::wildcardToRegularExpression(pattern, options | QRegularExpression::NonPathWildcardConversion)); + QCOMPARE(string.contains(re), matchesNonPathGlob); + } } void tst_QRegularExpression::testInvalidWildcard_data() |