summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArttu Tarkiainen <arttu.tarkiainen@qt.io>2019-06-14 16:15:16 +0300
committerArttu Tarkiainen <arttu.tarkiainen@qt.io>2019-08-16 08:33:43 +0000
commit3b310a3ec13a743ae2258bc43d92a5df42c6ad37 (patch)
tree83583b810174313f0f093711e8fb70f40a37afc7
parent28f92d37842ef087ab4f35ae7018b6366d0044e9 (diff)
Add regular expression support to replace operation
Introduce search with regular expressions support to replace operation. Add unit tests and make associated documentation changes. Task-number: QTIFW-889 Change-Id: I82e30056030ebc900be49046fda1903b27a2824d Reviewed-by: Katja Marttila <katja.marttila@qt.io>
-rw-r--r--doc/operations.qdoc12
-rw-r--r--src/libs/installer/replaceoperation.cpp38
-rw-r--r--tests/auto/installer/installer.pro3
-rw-r--r--tests/auto/installer/replaceoperation/replaceoperation.pro6
-rw-r--r--tests/auto/installer/replaceoperation/tst_replaceoperation.cpp184
5 files changed, 238 insertions, 5 deletions
diff --git a/doc/operations.qdoc b/doc/operations.qdoc
index 85a17a581..904421beb 100644
--- a/doc/operations.qdoc
+++ b/doc/operations.qdoc
@@ -90,8 +90,16 @@
is treated as ASCII text.
\row
\li Replace
- \li "Replace" \c file \c search \ replace
- \li Opens \c file to find \c search string and replaces that with the \c replace string.
+ \li "Replace" \c file \c search \c replace \c mode
+ \li Opens \c file to find \c search string and replaces that with the \c replace string,
+ using \c mode that can be either \c string or \c regex. For regular expressions
+ containing capturing groups, occurrences of \c \1, \c \2, ..., in \c replace are
+ replaced with the string captured by the corresponding capturing group.
+
+ Note that testing the operation using \c devtool does not support commas inside the
+ \c search pattern as they are used as argument separators. When using \c regex mode you
+ should also ensure the \c search pattern adheres to \c QRegularExpression class
+ documentation, particularly to the escaping rules for characters.
\row
\li LineReplace
\li "LineReplace" \c file \c search \c replace
diff --git a/src/libs/installer/replaceoperation.cpp b/src/libs/installer/replaceoperation.cpp
index 8cc1e0315..692bba715 100644
--- a/src/libs/installer/replaceoperation.cpp
+++ b/src/libs/installer/replaceoperation.cpp
@@ -31,6 +31,7 @@
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
+#include <QtCore/QRegularExpression>
using namespace QInstaller;
@@ -46,17 +47,45 @@ void ReplaceOperation::backup()
bool ReplaceOperation::performOperation()
{
+ static const QLatin1String stringMode("string");
+ static const QLatin1String regexMode("regex");
+
// Arguments:
// 1. filename
// 2. Source-String
// 3. Replace-String
- if (!checkArgumentCount(3))
+ // 4. mode=string|regex
+
+ // Calling with three arguments defaults to string search,
+ // this provides backward compatibility with the old syntax.
+ if (!checkArgumentCount(3, 4))
return false;
const QStringList args = arguments();
const QString fileName = args.at(0);
const QString before = args.at(1);
const QString after = args.at(2);
+ QString mode = args.value(3);
+
+ if (mode.isEmpty())
+ mode = stringMode;
+
+ if (before.isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Current search argument calling \"%1\" with "
+ "empty search argument is not supported.").arg(name()));
+
+ return false;
+ }
+
+ if (!(mode == stringMode || mode == regexMode)) {
+ setError(InvalidArguments);
+ setErrorString(tr("Current mode argument calling \"%1\" with "
+ "arguments \"%2\" is not supported. Please use string or regex.")
+ .arg(name(), arguments().join(QLatin1String("; "))));
+
+ return false;
+ }
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
@@ -78,7 +107,12 @@ bool ReplaceOperation::performOperation()
}
stream.setDevice(&file);
- stream << replacedFileContent.replace(before, after);
+ if (mode == regexMode) {
+ QRegularExpression regex(before);
+ stream << replacedFileContent.replace(regex, after);
+ } else if (mode == stringMode) {
+ stream << replacedFileContent.replace(before, after);
+ }
file.close();
return true;
diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro
index db94a23cb..7081a6316 100644
--- a/tests/auto/installer/installer.pro
+++ b/tests/auto/installer/installer.pro
@@ -21,7 +21,8 @@ SUBDIRS += \
settingsoperation \
task \
clientserver \
- factory
+ factory \
+ replaceoperation
win32 {
SUBDIRS += registerfiletypeoperation
diff --git a/tests/auto/installer/replaceoperation/replaceoperation.pro b/tests/auto/installer/replaceoperation/replaceoperation.pro
new file mode 100644
index 000000000..d2756d153
--- /dev/null
+++ b/tests/auto/installer/replaceoperation/replaceoperation.pro
@@ -0,0 +1,6 @@
+include(../../qttest.pri)
+
+QT -= gui
+QT += testlib
+
+SOURCES += tst_replaceoperation.cpp
diff --git a/tests/auto/installer/replaceoperation/tst_replaceoperation.cpp b/tests/auto/installer/replaceoperation/tst_replaceoperation.cpp
new file mode 100644
index 000000000..9afd91875
--- /dev/null
+++ b/tests/auto/installer/replaceoperation/tst_replaceoperation.cpp
@@ -0,0 +1,184 @@
+/**************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $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 <fileutils.h>
+#include <replaceoperation.h>
+#include <qinstallerglobal.h>
+
+#include <QObject>
+#include <QDir>
+#include <QFile>
+#include <QTest>
+
+using namespace KDUpdater;
+using namespace QInstaller;
+
+class tst_replaceoperation : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ m_testDirectory = QInstaller::generateTemporaryFileName();
+ m_testFilePath = m_testDirectory + "/test." + QString::number(qrand() % 1000);
+ }
+
+ void testWrongArguments()
+ {
+ ReplaceOperation missingArgumentsOperation(nullptr);
+ missingArgumentsOperation.setArguments(QStringList() << "testFile" << "testSearch");
+
+ // should do nothing if there are missing arguments
+ QVERIFY(missingArgumentsOperation.testOperation());
+ QCOMPARE(missingArgumentsOperation.performOperation(), false);
+
+ ReplaceOperation invalidModeArgumentOperation(nullptr);
+ invalidModeArgumentOperation.setArguments(QStringList() << "testFile"
+ << "testSearch" << "testReplace" << "invalid");
+
+ // should do nothing if there is an invalid mode argument
+ QVERIFY(invalidModeArgumentOperation.testOperation());
+ QCOMPARE(invalidModeArgumentOperation.performOperation(), false);
+
+ QCOMPARE(UpdateOperation::Error(invalidModeArgumentOperation.error()),
+ UpdateOperation::InvalidArguments);
+
+ QString compareString = "Current mode argument calling \"Replace\" with "
+ "arguments \"testFile; testSearch; testReplace; invalid\" is not supported. "
+ "Please use string or regex.";
+
+ QCOMPARE(invalidModeArgumentOperation.errorString(), compareString);
+
+ ReplaceOperation emptySearchArgumentOperation(nullptr);
+ emptySearchArgumentOperation.setArguments(QStringList() << "testFile"
+ << "" << "testReplace" << "regex");
+
+ // should do nothing if there is an empty search argument
+ QVERIFY(emptySearchArgumentOperation.testOperation());
+ QCOMPARE(emptySearchArgumentOperation.performOperation(), false);
+
+ QCOMPARE(UpdateOperation::Error(emptySearchArgumentOperation.error()),
+ UpdateOperation::InvalidArguments);
+
+ compareString = "Current search argument calling \"Replace\" with "
+ "empty search argument is not supported.";
+
+ QCOMPARE(emptySearchArgumentOperation.errorString(), compareString);
+ }
+
+ void testStringSearchReplace()
+ {
+ QVERIFY(QDir().mkpath(m_testDirectory));
+ QVERIFY(QDir(m_testDirectory).exists());
+
+ QFile file(m_testFilePath);
+ QVERIFY(file.open(QIODevice::WriteOnly));
+
+ QTextStream stream(&file);
+ stream << "Lorem ipsum dolore sit amet, consectetur adipiscing elit, sed do eiusmod "
+ "tempor incididunt ut labore et dolore magna aliqua." << endl;
+ file.close();
+
+ ReplaceOperation searchReplaceOperation(nullptr);
+ searchReplaceOperation.setArguments(QStringList() << m_testFilePath
+ << "dolore" << "test");
+
+ // should succeed
+ QVERIFY(searchReplaceOperation.testOperation());
+ QCOMPARE(searchReplaceOperation.performOperation(), true);
+
+ QVERIFY(file.open(QIODevice::ReadOnly));
+
+ QString fileContent = stream.readAll();
+ QCOMPARE(fileContent, QLatin1String(
+ "Lorem ipsum test sit amet, consectetur adipiscing elit, sed do eiusmod "
+ "tempor incididunt ut labore et test magna aliqua.\n"));
+
+ file.close();
+ QVERIFY(file.remove());
+ QVERIFY(QDir().rmdir(m_testDirectory));
+ }
+
+ void testRegexSearchReplace()
+ {
+ // Test with three different regexes, one containing
+ // a capturing group
+
+ QVERIFY(QDir().mkpath(m_testDirectory));
+ QVERIFY(QDir(m_testDirectory).exists());
+
+ QFile file(m_testFilePath);
+ QVERIFY(file.open(QIODevice::WriteOnly));
+
+ QTextStream stream(&file);
+ stream << "one | 10/10/2010 | three | 1.2345 | 0.00001 "
+ "| 7 | A <i>bon mot</i>." << endl;
+ file.close();
+
+ ReplaceOperation searchReplaceOperation(nullptr);
+ searchReplaceOperation.setArguments(QStringList() << m_testFilePath
+ << "\\d{1,2}/\\d{1,2}/\\d{4}" << "date-match" << "regex");
+
+ // should succeed
+ QVERIFY(searchReplaceOperation.testOperation());
+ QCOMPARE(searchReplaceOperation.performOperation(), true);
+
+ searchReplaceOperation.setArguments(QStringList() << m_testFilePath
+ << "[-+]?[0-9]*\\.?[0-9]+" << "number-match" << "regex");
+
+ // should succeed
+ QVERIFY(searchReplaceOperation.testOperation());
+ QCOMPARE(searchReplaceOperation.performOperation(), true);
+
+ searchReplaceOperation.setArguments(QStringList() << m_testFilePath
+ << "<i>([^<]*)</i>" << "\\emph{\\1}" << "regex");
+
+ // should succeed
+ QVERIFY(searchReplaceOperation.testOperation());
+ QCOMPARE(searchReplaceOperation.performOperation(), true);
+
+ QVERIFY(file.open(QIODevice::ReadOnly));
+
+ QString fileContent = stream.readAll();
+ QCOMPARE(fileContent, QLatin1String("one | date-match | three "
+ "| number-match | number-match | number-match | A \\emph{bon mot}.\n"));
+
+ file.close();
+ QVERIFY(file.remove());
+ QVERIFY(QDir().rmdir(m_testDirectory));
+ }
+
+private:
+ QString m_testDirectory;
+ QString m_testFilePath;
+};
+
+QTEST_MAIN(tst_replaceoperation)
+
+#include "tst_replaceoperation.moc"