diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2018-06-19 21:25:02 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2018-06-22 19:43:54 +0000 |
commit | 06af9a1e3891751d89a64e9013e204176c28ebdc (patch) | |
tree | 6043152468ef3a8319a4d15d5f5cb621481ad13c /tests/auto | |
parent | 305f57411d450c9b19330cd56f64702163bdcb34 (diff) |
QRegularExpression: refactor pattern optimization
After the move to PCRE2, optimizing patterns has been a thorn in the
side due to the fact that PCRE2's JIT compiler modifies the pattern
object itself (instead of returning a new set of data, like PCRE1
did). To make this fit with the existing behavior, a read/write
lock was introduced, with the read part locking when matching and
the write when compiling (or JIT-compiling) the pattern.
This locking strategy however introduced a performance issue,
as we needed:
* to acquire a write lock to compile/optimize the pattern (incl. the
common case where the pattern was already compiled, so bailing out
immediately);
* to acquire a read lock during the actual match, to prevent
some other thread from optimizing the pattern under our nose.
This was due to the "lazy" optimization policy of QRegularExpression
-- optimize a pattern after a certain number of usages. The
excessive amount of locking effectively limited scalability.
Simplify the code, and drop that policy altogether: since JIT
compiling in PCRE2 is faster and pretty much "always recommended",
just always do it for any pattern (unless it gets disabled via
env variables) when compiling it.
This allows to go back to a plain QMutex, and now the actual
matching doesn't require acquiring any locks any longer. Of course,
there is still a mutex acquired just before matching for checking
whether the pattern needs recompiling in the first place; this can
probably be further optimized via double-checked locking (using
atomics), but not doing it right now.
This shift makes a couple of pattern options controlling
optimization useless, and allows to centralize the 3
QRegularExpression tests (which were actually the very same test,
just setting slightly different optimizations strategies).
While at it, install a stress-test for threading, with the idea
of running it under TSAN or helgrind to catch bugs in
QRegularExpression's locking.
[ChangeLog][Important Behavior Changes][QRegularExpression] Regular
expressions are now automatically optimized (including JIT
compiling) on their first usage. The pattern options
OptimizeOnFirstUsageOption and DontAutomaticallyOptimizeOption no
longer have any effect, and will get removed in a future version of
Qt. QRegularExpression::optimize() can be still used to compile and
optimize the regular expression in advance (before any match), if
needed.
Task-number: QTBUG-66781
Change-Id: Ia0e97208ae78255fe811b78029ed01c204e47bd2
Reviewed-by: David Faure <david.faure@kdab.com>
Diffstat (limited to 'tests/auto')
9 files changed, 147 insertions, 291 deletions
diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro deleted file mode 100644 index a27286ff20..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/alwaysoptimize.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_alwaysoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_alwaysoptimize.cpp \ - ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp deleted file mode 100644 index 6d2ae48235..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/alwaysoptimize/tst_qregularexpression_alwaysoptimize.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.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> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_AlwaysOptimize : public tst_QRegularExpression -{ - Q_OBJECT - -private slots: - void initTestCase(); -}; - -QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count; // from qregularexpression.cpp -QT_END_NAMESPACE - -void tst_QRegularExpression_AlwaysOptimize::initTestCase() -{ - qt_qregularexpression_optimize_after_use_count = 1; -} - -QTEST_APPLESS_MAIN(tst_QRegularExpression_AlwaysOptimize) - -#include "tst_qregularexpression_alwaysoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro deleted file mode 100644 index 0b36c79c1b..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/defaultoptimize.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_defaultoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_defaultoptimize.cpp \ - ../tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp deleted file mode 100644 index a815c6cab9..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/defaultoptimize/tst_qregularexpression_defaultoptimize.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.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> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_DefaultOptimize : public tst_QRegularExpression -{ - Q_OBJECT -}; - -QTEST_APPLESS_MAIN(tst_QRegularExpression_DefaultOptimize) - -#include "tst_qregularexpression_defaultoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro b/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro deleted file mode 100644 index 1db77781dd..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/forceoptimize/forceoptimize.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_qregularexpression_forceoptimize -QT = core testlib -HEADERS = ../tst_qregularexpression.h -SOURCES = \ - tst_qregularexpression_forceoptimize.cpp \ - ../tst_qregularexpression.cpp -DEFINES += forceOptimize=true diff --git a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp b/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp deleted file mode 100644 index 38a3a64fa3..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/forceoptimize/tst_qregularexpression_forceoptimize.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 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> -#include "../tst_qregularexpression.h" - -class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression -{ - Q_OBJECT -}; - -QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize) - -#include "tst_qregularexpression_forceoptimize.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro index e1840808ff..ec8189717e 100644 --- a/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro +++ b/tests/auto/corelib/tools/qregularexpression/qregularexpression.pro @@ -1,3 +1,4 @@ -TEMPLATE = subdirs -SUBDIRS = defaultoptimize forceoptimize -qtConfig(private_tests): SUBDIRS += alwaysoptimize +CONFIG += testcase +TARGET = tst_qregularexpression +QT = core testlib +SOURCES = tst_qregularexpression.cpp diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp index ec495fd272..1a6fdaaa61 100644 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp +++ b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.cpp @@ -33,11 +33,60 @@ #include <qstringlist.h> #include <qhash.h> -#include "tst_qregularexpression.h" +#include <qobject.h> +#include <qregularexpression.h> +#include <qthread.h> -#ifndef forceOptimize -#define forceOptimize false -#endif +Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) +Q_DECLARE_METATYPE(QRegularExpression::MatchType) +Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) + +class tst_QRegularExpression : public QObject +{ + Q_OBJECT + +private slots: + void defaultConstructors(); + void gettersSetters_data(); + void gettersSetters(); + void escape_data(); + void escape(); + void validity_data(); + void validity(); + void patternOptions_data(); + void patternOptions(); + void normalMatch_data(); + void normalMatch(); + void partialMatch_data(); + void partialMatch(); + void globalMatch_data(); + void globalMatch(); + void serialize_data(); + void serialize(); + void operatoreq_data(); + void operatoreq(); + void captureCount_data(); + void captureCount(); + void captureNames_data(); + void captureNames(); + void pcreJitStackUsage_data(); + void pcreJitStackUsage(); + void regularExpressionMatch_data(); + void regularExpressionMatch(); + void JOptionUsage_data(); + void JOptionUsage(); + void QStringAndQStringRefEquivalence(); + void threadSafety_data(); + void threadSafety(); + + void wildcard_data(); + void wildcard(); + void testInvalidWildcard_data(); + void testInvalidWildcard(); + +private: + void provideRegularExpressions(); +}; struct Match { @@ -292,9 +341,6 @@ static void testMatch(const QRegularExpression ®exp, QRegularExpression::MatchOptions matchOptions, const Result &result) { - if (forceOptimize) - regexp.optimize(); - // test with QString as subject type testMatchImpl<QREMatch>(regexp, matchingMethodForString, subject, offset, matchType, matchOptions, result); @@ -401,30 +447,22 @@ void tst_QRegularExpression::gettersSetters() { QRegularExpression re; re.setPattern(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re; re.setPatternOptions(patternOptions); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), QString()); QCOMPARE(re.patternOptions(), patternOptions); } { QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption); } { QRegularExpression re(pattern, patternOptions); - if (forceOptimize) - re.optimize(); QCOMPARE(re.pattern(), pattern); QCOMPARE(re.patternOptions(), patternOptions); } @@ -465,8 +503,6 @@ void tst_QRegularExpression::escape() QFETCH(QString, escaped); QCOMPARE(QRegularExpression::escape(string), escaped); QRegularExpression re(escaped); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), true); } @@ -497,8 +533,6 @@ void tst_QRegularExpression::validity() QFETCH(QString, pattern); QFETCH(bool, validity); QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), validity); if (!validity) QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); @@ -585,9 +619,6 @@ void tst_QRegularExpression::patternOptions() QFETCH(QString, subject); QFETCH(Match, match); - if (forceOptimize) - regexp.optimize(); - QRegularExpressionMatch m = regexp.match(subject); consistencyCheck(m); QVERIFY(m == match); @@ -1403,9 +1434,6 @@ void tst_QRegularExpression::serialize() QFETCH(QRegularExpression::PatternOptions, patternOptions); QRegularExpression outRe(pattern, patternOptions); - if (forceOptimize) - outRe.optimize(); - QByteArray buffer; { QDataStream out(&buffer, QIODevice::WriteOnly); @@ -1468,33 +1496,18 @@ void tst_QRegularExpression::operatoreq() QRegularExpression re1(pattern); QRegularExpression re2(pattern); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } { QRegularExpression re1(QString(), patternOptions); QRegularExpression re2(QString(), patternOptions); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } { QRegularExpression re1(pattern, patternOptions); QRegularExpression re2(pattern, patternOptions); - if (forceOptimize) - re1.optimize(); - if (forceOptimize) - re2.optimize(); - verifyEquality(re1, re2); } } @@ -1524,9 +1537,6 @@ void tst_QRegularExpression::captureCount() QFETCH(QString, pattern); QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QTEST(re.captureCount(), "captureCount"); if (!re.isValid()) QCOMPARE(re.captureCount(), -1); @@ -1595,9 +1605,6 @@ void tst_QRegularExpression::captureNames() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QStringList namedCaptureGroups = re.namedCaptureGroups(); int namedCaptureGroupsCount = namedCaptureGroups.size(); @@ -1633,9 +1640,6 @@ void tst_QRegularExpression::pcreJitStackUsage() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1663,9 +1667,6 @@ void tst_QRegularExpression::regularExpressionMatch() QRegularExpression re(pattern); - if (forceOptimize) - re.optimize(); - QVERIFY(re.isValid()); QRegularExpressionMatch match = re.match(subject); consistencyCheck(match); @@ -1705,8 +1706,6 @@ void tst_QRegularExpression::JOptionUsage() QRegularExpression re(pattern); if (isValid && JOptionUsed) QTest::ignoreMessage(QtWarningMsg, qPrintable(warningMessage.arg(pattern))); - if (forceOptimize) - re.optimize(); QCOMPARE(re.isValid(), isValid); } @@ -2067,6 +2066,92 @@ void tst_QRegularExpression::QStringAndQStringRefEquivalence() } } +class MatcherThread : public QThread +{ +public: + explicit MatcherThread(const QRegularExpression &re, const QString &subject, QObject *parent = nullptr) + : QThread(parent), + m_re(re), + m_subject(subject) + { + } + +private: + static const int MATCH_ITERATIONS = 50; + + void run() override + { + yieldCurrentThread(); + for (int i = 0; i < MATCH_ITERATIONS; ++i) + m_re.match(m_subject); + } + + const QRegularExpression &m_re; + const QString &m_subject; +}; + +void tst_QRegularExpression::threadSafety_data() +{ + QTest::addColumn<QString>("pattern"); + QTest::addColumn<QString>("subject"); + + int i = 0; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abbbbcccd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abababcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcabcd"; + QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abccccccababd"; + + { + QString subject(512*1024, QLatin1Char('x')); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + { + QString subject = "ab"; + subject.append(QString(512*1024, QLatin1Char('x'))); + subject.append("c"); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + { + QString subject = "ab"; + subject.append(QString(512*1024, QLatin1Char('x'))); + subject.append("cd"); + QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject; + } + + QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaabcde"; + QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaaaaabcde"; +} + +void tst_QRegularExpression::threadSafety() +{ + QFETCH(QString, pattern); + QFETCH(QString, subject); + + static const int THREAD_SAFETY_ITERATIONS = 50; + + const int threadCount = qMax(QThread::idealThreadCount(), 4); + + for (int threadSafetyIteration = 0; threadSafetyIteration < THREAD_SAFETY_ITERATIONS; ++threadSafetyIteration) { + QRegularExpression re(pattern); + + QVector<MatcherThread *> threads; + for (int i = 0; i < threadCount; ++i) { + MatcherThread *thread = new MatcherThread(re, subject); + thread->start(); + threads.push_back(thread); + } + + for (int i = 0; i < threadCount; ++i) + threads[i]->wait(); + + qDeleteAll(threads); + } +} + void tst_QRegularExpression::wildcard_data() { QTest::addColumn<QString>("pattern"); @@ -2108,8 +2193,6 @@ void tst_QRegularExpression::wildcard() QRegularExpression re; re.setWildcardPattern(pattern); - if (forceOptimize) - re.optimize(); QRegularExpressionMatch match = re.match(string); @@ -2137,9 +2220,11 @@ void tst_QRegularExpression::testInvalidWildcard() QRegularExpression re; re.setWildcardPattern(pattern); - if (forceOptimize) - re.optimize(); QFETCH(bool, isValid); QCOMPARE(re.isValid(), isValid); } + +QTEST_APPLESS_MAIN(tst_QRegularExpression) + +#include "tst_qregularexpression.moc" diff --git a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h b/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h deleted file mode 100644 index db5b15be66..0000000000 --- a/tests/auto/corelib/tools/qregularexpression/tst_qregularexpression.h +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.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 <qobject.h> -#include <qregularexpression.h> - -Q_DECLARE_METATYPE(QRegularExpression::PatternOptions) -Q_DECLARE_METATYPE(QRegularExpression::MatchType) -Q_DECLARE_METATYPE(QRegularExpression::MatchOptions) - -class tst_QRegularExpression : public QObject -{ - Q_OBJECT - -private slots: - void defaultConstructors(); - void gettersSetters_data(); - void gettersSetters(); - void escape_data(); - void escape(); - void validity_data(); - void validity(); - void patternOptions_data(); - void patternOptions(); - void normalMatch_data(); - void normalMatch(); - void partialMatch_data(); - void partialMatch(); - void globalMatch_data(); - void globalMatch(); - void serialize_data(); - void serialize(); - void operatoreq_data(); - void operatoreq(); - void captureCount_data(); - void captureCount(); - void captureNames_data(); - void captureNames(); - void pcreJitStackUsage_data(); - void pcreJitStackUsage(); - void regularExpressionMatch_data(); - void regularExpressionMatch(); - void JOptionUsage_data(); - void JOptionUsage(); - void QStringAndQStringRefEquivalence(); - - void wildcard_data(); - void wildcard(); - void testInvalidWildcard_data(); - void testInvalidWildcard(); - -private: - void provideRegularExpressions(); -}; |