From 61ffbff8ab9fcdf8bbc73ee00fb9db7f40e0182b Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 26 Jun 2013 11:17:23 +0200 Subject: improve copy operation - now the destination can be a directory and it will internally be completed to filepath destination - added a unit test Task-number: QTIFW-274 Change-Id: I7741497d571cde5d5d4b374784d785358e9bb233 Reviewed-by: Niels Weber --- src/libs/kdtools/kdupdaterupdateoperations.cpp | 75 ++++++--- src/libs/kdtools/kdupdaterupdateoperations.h | 3 + .../copyoperationtest/copyoperationtest.pro | 6 + .../copyoperationtest/tst_copyoperationtest.cpp | 168 +++++++++++++++++++++ tests/auto/installer/installer.pro | 3 +- 5 files changed, 231 insertions(+), 24 deletions(-) create mode 100644 tests/auto/installer/copyoperationtest/copyoperationtest.pro create mode 100644 tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp diff --git a/src/libs/kdtools/kdupdaterupdateoperations.cpp b/src/libs/kdtools/kdupdaterupdateoperations.cpp index 6acdc33ed..c6a48bedb 100644 --- a/src/libs/kdtools/kdupdaterupdateoperations.cpp +++ b/src/libs/kdtools/kdupdaterupdateoperations.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -120,62 +121,90 @@ CopyOperation::~CopyOperation() deleteFileNowOrLater(value(QLatin1String("backupOfExistingDestination")).toString()); } +QString CopyOperation::sourcePath() +{ + return arguments().first(); +} + +QString CopyOperation::destinationPath() +{ + QString destination = arguments().last(); + + // if the target is a directory use the source filename to complete the destination path + if (QFileInfo(destination).isDir()) + destination = QDir(destination).filePath(QFileInfo(sourcePath()).fileName()); + return destination; +} + + void CopyOperation::backup() { - const QString dest = arguments().last(); - if (!QFile::exists(dest)) { + QString destination = destinationPath(); + + if (!QFile::exists(destination)) { clearValue(QLatin1String("backupOfExistingDestination")); return; } - setValue(QLatin1String("backupOfExistingDestination"), backupFileName(dest)); + setValue(QLatin1String("backupOfExistingDestination"), backupFileName(destination)); // race condition: The backup file could get created by another process right now. But this is the same // in QFile::copy... - if (!QFile::rename(dest, value(QLatin1String("backupOfExistingDestination")).toString())) - setError(UserDefinedError, tr("Could not backup file %1.").arg(dest)); + if (!QFile::rename(destination, value(QLatin1String("backupOfExistingDestination")).toString())) + setError(UserDefinedError, tr("Could not backup file %1.").arg(destination)); } bool CopyOperation::performOperation() { // We need two args to complete the copy operation. First arg provides the complete file name of source // Second arg provides the complete file name of dest - const QStringList args = this->arguments(); - if (args.count() != 2) { + if (arguments().count() != 2) { setError(InvalidArguments); - setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(args.count())); + setErrorString(tr("Invalid arguments: %1 arguments given, 2 expected.").arg(arguments().count())); return false; } - const QString dest = args.last(); + QString source = sourcePath(); + QString destination = destinationPath(); + + QFile sourceFile(source); + if (!sourceFile.exists()) { + setError(UserDefinedError); + setErrorString(tr("Could not copy a none existing file: %1").arg(source)); + return false; + } // If destination file exists, we cannot use QFile::copy() because it does not overwrite an existing // file. So we remove the destination file. - if (QFile::exists(dest)) { - QFile file(dest); - if (!file.remove()) { + QFile destinationFile(destination); + if (destinationFile.exists()) { + if (!destinationFile.remove()) { setError(UserDefinedError); - setErrorString(tr("Could not remove destination file %1: %2").arg(dest, file.errorString())); + setErrorString(tr("Could not remove destination file %1: %2").arg(destination, destinationFile.errorString())); return false; } } - QFile file(args.first()); - const bool copied = file.copy(dest); + const bool copied = sourceFile.copy(destination); if (!copied) { setError(UserDefinedError); - setErrorString(tr("Could not copy %1 to %2: %3").arg(file.fileName(), dest, file.errorString())); + setErrorString(tr("Could not copy %1 to %2: %3").arg(source, destination, sourceFile.errorString())); } return copied; } bool CopyOperation::undoOperation() { - const QString dest = arguments().last(); + QString source = sourcePath(); + QString destination = destinationPath(); - QFile destF(dest); + // if the target is a directory use the source filename to complete the destination path + if (QFileInfo(destination).isDir()) + destination = destination + QDir::separator() + QFileInfo(source).fileName(); + + QFile destFile(destination); // first remove the dest - if (!destF.remove()) { - setError(UserDefinedError, tr("Could not delete file %1: %2").arg(dest, destF.errorString())); + if (!destFile.remove()) { + setError(UserDefinedError, tr("Could not delete file %1: %2").arg(destination, destFile.errorString())); return false; } @@ -184,11 +213,11 @@ bool CopyOperation::undoOperation() if (!hasValue(QLatin1String("backupOfExistingDestination"))) return true; - QFile backupF(value(QLatin1String("backupOfExistingDestination")).toString()); + QFile backupFile(value(QLatin1String("backupOfExistingDestination")).toString()); // otherwise we have to copy the backup back: - const bool success = backupF.rename(dest); + const bool success = backupFile.rename(destination); if (!success) - setError(UserDefinedError, tr("Could not restore backup file into %1: %2").arg(dest, backupF.errorString())); + setError(UserDefinedError, tr("Could not restore backup file into %1: %2").arg(destination, backupFile.errorString())); return success; } diff --git a/src/libs/kdtools/kdupdaterupdateoperations.h b/src/libs/kdtools/kdupdaterupdateoperations.h index f8f5f1ddc..43a0ff9d2 100644 --- a/src/libs/kdtools/kdupdaterupdateoperations.h +++ b/src/libs/kdtools/kdupdaterupdateoperations.h @@ -59,6 +59,9 @@ public: CopyOperation *clone() const; QDomDocument toXml() const; +private: + QString sourcePath(); + QString destinationPath(); }; class KDTOOLS_EXPORT MoveOperation : public UpdateOperation diff --git a/tests/auto/installer/copyoperationtest/copyoperationtest.pro b/tests/auto/installer/copyoperationtest/copyoperationtest.pro new file mode 100644 index 000000000..ce4ad42ec --- /dev/null +++ b/tests/auto/installer/copyoperationtest/copyoperationtest.pro @@ -0,0 +1,6 @@ +include(../../qttest.pri) + +QT -= gui +QT += testlib + +SOURCES = tst_copyoperationtest.cpp diff --git a/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp b/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp new file mode 100644 index 000000000..61d24a2ad --- /dev/null +++ b/tests/auto/installer/copyoperationtest/tst_copyoperationtest.cpp @@ -0,0 +1,168 @@ +/************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "init.h" +#include "kdupdaterupdateoperations.h" +#include "utils.h" + +#include +#include +#include +#include +#include + +using namespace KDUpdater; +using namespace QInstaller; + +class tst_copyoperationtest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase() + { + //QInstaller::init(); + m_testDestinationPath = qApp->applicationDirPath() + QDir::toNativeSeparators("/test"); + m_testDestinationFilePath = QDir(m_testDestinationPath).absoluteFilePath(QFileInfo( + qApp->applicationFilePath()).fileName()); + if (QDir(m_testDestinationPath).exists()) { + QFAIL("Remove test folder first!"); + } + } + + void testMissingArguments() + { + CopyOperation op; + + QVERIFY(op.testOperation()); + QVERIFY(!op.performOperation()); + + QCOMPARE(UpdateOperation::Error(op.error()), UpdateOperation::InvalidArguments); + QCOMPARE(op.errorString(), QString("Invalid arguments: 0 arguments given, 2 expected.")); + + } + + void testCopySomething_data() + { + QTest::addColumn("source"); + QTest::addColumn("destination"); + QTest::newRow("full path syntax") << qApp->applicationFilePath() << m_testDestinationFilePath; + QTest::newRow("short destination syntax") << qApp->applicationFilePath() << m_testDestinationPath; + QTest::newRow("short destination syntax with ending separator") << qApp->applicationFilePath() + << m_testDestinationPath + QDir::separator(); + } + + void testCopySomething() + { + QFETCH(QString, source); + QFETCH(QString, destination); + + QVERIFY2(QFileInfo(source).exists(), QString("Source '%1' does not exist.").arg(source).toLatin1()); + CopyOperation op; + op.setArguments(QStringList() << source << destination); + op.backup(); + QVERIFY2(op.performOperation(), op.errorString().toLatin1()); + + QVERIFY2(QFileInfo(m_testDestinationFilePath).exists(), QString("Copying from '%1' to '%2' was " + "not working: '%3' does not exist").arg(source, destination, m_testDestinationFilePath).toLatin1()); + QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); + QVERIFY2(!QFileInfo(m_testDestinationFilePath).exists(), QString("Undo of copying from '%1' to " + "'%2' was not working.").toLatin1()); + } + + void testCopyIfDestinationExist_data() + { + testCopySomething_data(); + } + + void testCopyIfDestinationExist() + { + QFETCH(QString, source); + QFETCH(QString, destination); + + QByteArray testString("This file is generated by QTest\n"); + QFile testFile(m_testDestinationFilePath); + testFile.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&testFile); + out << testString; + testFile.close(); + + QByteArray testFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); + + QVERIFY2(QFileInfo(source).exists(), QString("Source '%1' does not exist.").arg(source).toLatin1()); + CopyOperation op; + op.setArguments(QStringList() << source << destination); + op.backup(); + QVERIFY2(!op.value("backupOfExistingDestination").toString().isEmpty(), "The CopyOperation didn't saved any backup."); + QVERIFY2(op.performOperation(), op.errorString().toLatin1()); + + // checking that perform did something + QByteArray currentFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); + QVERIFY(testFileHash != currentFileHash); + + QVERIFY2(QFileInfo(m_testDestinationFilePath).exists(), QString("Copying from '%1' to '%2' was " + "not working: '%3' does not exist").arg(source, destination, m_testDestinationFilePath).toLatin1()); + + // undo should replace the new one with the old backuped one + QVERIFY2(op.undoOperation(), op.errorString().toLatin1()); + currentFileHash = QInstaller::calculateHash(m_testDestinationFilePath, QCryptographicHash::Sha1); + QVERIFY(testFileHash == currentFileHash); + } + void init() + { + QVERIFY2(!QFileInfo(m_testDestinationFilePath).exists(), QString("Destination '%1' should not exist " + "to test the copy operation.").arg(m_testDestinationFilePath).toLatin1()); + QDir().mkpath(m_testDestinationPath); + } + + void cleanup() + { + QFile(m_testDestinationFilePath).remove(); + QDir().rmpath(m_testDestinationPath); + } +private: + QString m_testDestinationPath; + QString m_testDestinationFilePath; +}; + +QTEST_MAIN(tst_copyoperationtest) + +#include "tst_copyoperationtest.moc" diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro index fea58129d..f96e5f420 100644 --- a/tests/auto/installer/installer.pro +++ b/tests/auto/installer/installer.pro @@ -10,4 +10,5 @@ SUBDIRS += \ lib7zfacade \ scriptengine \ consumeoutputoperationtest \ - mkdiroperationtest + mkdiroperationtest \ + copyoperationtest \ No newline at end of file -- cgit v1.2.3