Merge remote-tracking branch 'origin/checksum_1.8' into 1.8

This commit is contained in:
Olivier Goffart 2015-05-22 10:32:37 +02:00
commit 6b9e123816
26 changed files with 728 additions and 19 deletions

View file

@ -165,13 +165,13 @@ endif()
find_package(Sphinx)
find_package(PdfLatex)
find_package(SQLite3 3.8.0 REQUIRED)
# On some OS, we want to use our own, not the system sqlite
if (USE_OUR_OWN_SQLITE3)
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
endif()
find_package(ZLIB)
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

View file

@ -1,5 +1,8 @@
ChangeLog
=========
version 1.8.2 (release 2015-06-xx)
* HTTP: Add the branding name to the UserAgent string.
version 1.8.1 (release 2015-05-07)
* Make "operation canceled" error a soft error
* Do not throw an error for files that are scheduled to be removed,

View file

@ -17,6 +17,7 @@ if( Qt5Core_FOUND )
message(STATUS "Found Qt5 core, checking for further dependencies...")
find_package(Qt5Network REQUIRED)
find_package(Qt5Xml REQUIRED)
find_package(Qt5Concurrent REQUIRED)
if(NOT TOKEN_AUTH_ONLY)
find_package(Qt5WebKitWidgets REQUIRED)
find_package(Qt5WebKit REQUIRED)

View file

@ -19,6 +19,8 @@
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
#cmakedefine APPLICATION_UPDATE_URL "@APPLICATION_UPDATE_URL@"
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
#cmakedefine DATADIR "@DATADIR@"

View file

@ -61,6 +61,7 @@ set(libsync_SRCS
theme.cpp
utility.cpp
ownsql.cpp
transmissionchecksumvalidator.cpp
creds/dummycredentials.cpp
creds/abstractcredentials.cpp
creds/credentialsfactory.cpp
@ -141,6 +142,11 @@ if(NEON_FOUND)
endif()
endif()
if(ZLIB_FOUND)
list(APPEND libsync_LINK_TARGETS ${ZLIB_LIBRARIES})
include_directories(${ZLIB_INCLUDE_DIRS})
endif(ZLIB_FOUND)
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
GENERATE_EXPORT_HEADER( ${synclib_NAME}
BASE_NAME ${synclib_NAME}
@ -151,9 +157,9 @@ GENERATE_EXPORT_HEADER( ${synclib_NAME}
if(TOKEN_AUTH_ONLY)
qt5_use_modules(${synclib_NAME} Network)
qt5_use_modules(${synclib_NAME} Network Concurrent)
else()
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets)
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets Concurrent)
endif()
set_target_properties( ${synclib_NAME} PROPERTIES

View file

@ -49,6 +49,7 @@ static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications"
static const char skipUpdateCheckC[] = "skipUpdateCheck";
static const char geometryC[] = "geometry";
static const char timeoutC[] = "timeout";
static const char transmissionChecksumC[] = "transmissionChecksum";
static const char proxyHostC[] = "Proxy/host";
static const char proxyTypeC[] = "Proxy/type";
@ -118,6 +119,20 @@ int ConfigFile::timeout() const
return settings.value(QLatin1String(timeoutC), 300).toInt(); // default to 5 min
}
QString ConfigFile::transmissionChecksum() const
{
QSettings settings(configFile(), QSettings::IniFormat);
QString checksum = settings.value(QLatin1String(transmissionChecksumC), QString()).toString();
if( checksum.isEmpty() ) {
// if the config file setting is empty, maybe the Branding requires it.
checksum = Theme::instance()->transmissionChecksum();
}
return checksum;
}
void ConfigFile::setOptionalDesktopNotifications(bool show)
{
QSettings settings(configFile(), QSettings::IniFormat);

View file

@ -103,6 +103,12 @@ public:
int timeout() const;
// send a checksum as a header along with the transmission or not.
// possible values:
// empty: no checksum calculated or expected.
// or "Adler32", "MD5", "SHA1"
QString transmissionChecksum() const;
void saveGeometry(QWidget *w);
void restoreGeometry(QWidget *w);

View file

@ -18,6 +18,11 @@
#include <QFileInfo>
#include <QCoreApplication>
#include <QDebug>
#include <QCryptographicHash>
#ifdef ZLIB_FOUND
#include <zlib.h>
#endif
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <qabstractfileengine.h>
@ -394,4 +399,58 @@ QString FileSystem::fileSystemForPath(const QString & path)
}
#endif
#define BUFSIZE 1024*1024*10
static QByteArray readToCrypto( const QString& filename, QCryptographicHash::Algorithm algo )
{
const qint64 bufSize = BUFSIZE;
QByteArray buf(bufSize,0);
QByteArray arr;
QCryptographicHash crypto( algo );
QFile file(filename);
if (file.open(QIODevice::ReadOnly)) {
qint64 size;
while (!file.atEnd()) {
size = file.read( buf.data(), bufSize );
if( size > 0 ) {
crypto.addData(buf.data(), size);
}
}
arr = crypto.result().toHex();
}
return arr;
}
QByteArray FileSystem::calcMd5( const QString& filename )
{
return readToCrypto( filename, QCryptographicHash::Md5 );
}
QByteArray FileSystem::calcSha1( const QString& filename )
{
return readToCrypto( filename, QCryptographicHash::Sha1 );
}
#ifdef ZLIB_FOUND
QByteArray FileSystem::calcAdler32( const QString& filename )
{
unsigned int adler = adler32(0L, Z_NULL, 0);
const qint64 bufSize = BUFSIZE;
QByteArray buf(bufSize, 0);
QFile file(filename);
if (file.open(QIODevice::ReadOnly)) {
qint64 size;
while (!file.atEnd()) {
size = file.read(buf.data(), bufSize);
if( size > 0 )
adler = adler32(adler, (const Bytef*) buf.data(), size);
}
}
return QByteArray::number( adler, 16 );
}
#endif
} // namespace OCC

View file

@ -13,8 +13,11 @@
#pragma once
#include "config.h"
#include <QString>
#include <ctime>
#include <QCryptographicHash>
#include <owncloudlib.h>
@ -121,4 +124,10 @@ bool openAndSeekFileSharedRead(QFile* file, QString* error, qint64 seek);
QString fileSystemForPath(const QString & path);
#endif
QByteArray calcMd5( const QString& fileName );
QByteArray calcSha1( const QString& fileName );
#ifdef ZLIB_FOUND
QByteArray calcAdler32( const QString& fileName );
#endif
}}

View file

@ -43,7 +43,6 @@ namespace OCC {
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
: QObject(parent)
, _duration(0)
, _timedout(false)
, _followRedirects(false)
, _ignoreCredentialFailure(false)

View file

@ -259,6 +259,21 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
{
Q_ASSERT(std::is_sorted(items.begin(), items.end()));
/* Check and log the transmission checksum type */
ConfigFile cfg;
const QString checksumType = cfg.transmissionChecksum().toUpper();
/* if the checksum type is empty, it is not send. No error */
if( !checksumType.isEmpty() ) {
if( checksumType == checkSumAdlerUpperC ||
checksumType == checkSumMD5C ||
checksumType == checkSumSHA1C ) {
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
} else {
qWarning() << "Unknown transmission checksum type from config" << checksumType;
}
}
/* This builds all the job needed for the propagation.
* Each directories is a PropagateDirectory job, which contains the files in it.
* In order to do that we loop over the items. (which are sorted by destination)

View file

@ -12,6 +12,7 @@
* for more details.
*/
#include "config.h"
#include "owncloudpropagator_p.h"
#include "propagatedownload.h"
#include "networkjobs.h"
@ -21,6 +22,8 @@
#include "utility.h"
#include "filesystem.h"
#include "propagatorjobs.h"
#include "transmissionchecksumvalidator.h"
#include <json.h>
#include <QNetworkAccessManager>
#include <QFileInfo>
@ -483,7 +486,21 @@ void PropagateDownloadFileQNAM::slotGetFinished()
return;
}
downloadFinished();
// Do checksum validation for the download. If there is no checksum header, the validator
// will also emit the validated() signal to continue the flow in slot downloadFinished()
// as this is (still) also correct.
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(_tmpFile.fileName(), this);
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(downloadFinished()));
connect(validator, SIGNAL(validationFailed(QString)), this, SLOT(slotChecksumFail(QString)));
validator->downloadValidation(job->reply()->rawHeader(checkSumHeaderC));
}
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
{
_tmpFile.remove();
_propagator->_anotherSyncNeeded = true;
done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded."));
}
QString makeConflictFileName(const QString &fn, const QDateTime &dt)

View file

@ -101,19 +101,21 @@ private slots:
class PropagateDownloadFileQNAM : public PropagateItemJob {
Q_OBJECT
QPointer<GETFileJob> _job;
// QFile *_file;
QFile _tmpFile;
public:
PropagateDownloadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
: PropagateItemJob(propagator, item) {}
void start() Q_DECL_OVERRIDE;
private slots:
void slotGetFinished();
void abort() Q_DECL_OVERRIDE;
void downloadFinished();
void slotDownloadProgress(qint64,qint64);
void slotChecksumFail( const QString& errMsg );
private:
QPointer<GETFileJob> _job;
QFile _tmpFile;
};
}

View file

@ -12,6 +12,7 @@
* for more details.
*/
#include "config.h"
#include "propagateupload.h"
#include "owncloudpropagator_p.h"
#include "networkjobs.h"
@ -21,6 +22,8 @@
#include "utility.h"
#include "filesystem.h"
#include "propagatorjobs.h"
#include "transmissionchecksumvalidator.h"
#include <json.h>
#include <QNetworkAccessManager>
#include <QFileInfo>
@ -190,22 +193,51 @@ bool PollJob::finished()
return true;
}
void PropagateUploadFileQNAM::start()
{
if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
return;
}
const QString filePath = _propagator->getFilePath(_item._file);
// remember the modtime before checksumming to be able to detect a file
// change during the checksum calculation
_item._modtime = FileSystem::getModTime(filePath);
_stopWatch.start();
// do whatever is needed to add a checksum to the http upload request.
// in any case, the validator will emit signal startUpload to let the flow
// continue in slotStartUpload here.
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(filePath, this);
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(slotStartUpload(QByteArray)));
validator->uploadValidation();
}
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksum)
{
const QString fullFilePath(_propagator->getFilePath(_item._file));
_item._checksum = checksum;
if (!FileSystem::fileExists(fullFilePath)) {
done(SyncFileItem::SoftError, tr("File Removed"));
return;
}
_stopWatch.addLapTime(QLatin1String("Checksum"));
time_t prevModtime = _item._modtime; // the _item value was set in PropagateUploadFileQNAM::start()
// but a potential checksum calculation could have taken some time during which the file could
// have been changed again, so better check again here.
// Update the mtime and size, it might have changed since discovery.
_item._modtime = FileSystem::getModTime(fullFilePath);
if( prevModtime != _item._modtime ) {
_propagator->_anotherSyncNeeded = true;
done(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
return;
}
quint64 fileSize = FileSystem::getSize(fullFilePath);
_item._size = fileSize;
@ -432,6 +464,14 @@ void PropagateUploadFileQNAM::startNextChunk()
if( currentChunkSize == 0 ) { // if the last chunk pretents to be 0, its actually the full chunk size.
currentChunkSize = chunkSize();
}
if( !_item._checksum.isEmpty() ) {
headers[checkSumHeaderC] = _item._checksum;
}
}
} else {
// checksum if its only one chunk
if( !_item._checksum.isEmpty() ) {
headers[checkSumHeaderC] = _item._checksum;
}
}
@ -652,6 +692,11 @@ void PropagateUploadFileQNAM::slotPutFinished()
// Well, the mtime was not set
#endif
}
// performance logging
_item._requestDuration = _stopWatch.stop();
qDebug() << "*==* duration UPLOAD" << _item._size << _stopWatch.durationOfLap(QLatin1String("Checksum")) << _item._requestDuration;
finalize(_item);
}

View file

@ -20,6 +20,7 @@
#include <QFile>
#include <QDebug>
namespace OCC {
class BandwidthManager;
@ -75,6 +76,8 @@ protected slots:
class PUTFileJob : public AbstractNetworkJob {
Q_OBJECT
private:
QScopedPointer<QIODevice> _device;
QMap<QByteArray, QByteArray> _headers;
QString _errorString;
@ -146,6 +149,7 @@ signals:
class PropagateUploadFileQNAM : public PropagateItemJob {
Q_OBJECT
private:
/**
* That's the start chunk that was stored in the database for resuming.
* In the non-resuming case it is 0.
@ -163,6 +167,10 @@ class PropagateUploadFileQNAM : public PropagateItemJob {
QElapsedTimer _duration;
QVector<PUTFileJob*> _jobs; /// network jobs that are currently in transit
bool _finished; // Tells that all the jobs have been finished
// measure the performance of checksum calc and upload
Utility::StopWatch _stopWatch;
public:
PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
: PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {}
@ -175,6 +183,8 @@ private slots:
void startNextChunk();
void finalize(const SyncFileItem&);
void slotJobDestroyed(QObject *job);
void slotStartUpload(const QByteArray &checksum);
private:
void startPollJob(const QString& path);
void abortWithError(SyncFileItem::Status status, const QString &error);

View file

@ -21,6 +21,22 @@
namespace OCC {
/**
* Tags for checksum headers.
* They are here for being shared between Upload- and Download Job
*/
// the header itself
static const char checkSumHeaderC[] = "OC-Checksum";
// ...and it's values
static const char checkSumMD5C[] = "MD5";
static const char checkSumSHA1C[] = "SHA1";
static const char checkSumAdlerC[] = "Adler32";
static const char checkSumAdlerUpperC[] = "ADLER32";
/**
* Declaration of the other propagation jobs
*/
class PropagateLocalRemove : public PropagateItemJob {
Q_OBJECT
public:

View file

@ -150,6 +150,7 @@ public:
quint64 _inode;
QByteArray _fileId;
QByteArray _remotePerm;
QByteArray _checksum;
QString _directDownloadUrl;
QString _directDownloadCookies;

View file

@ -241,12 +241,17 @@ QString Theme::updateCheckUrl() const
return QLatin1String("https://updates.owncloud.com/client/");
}
QString Theme::transmissionChecksum() const
{
return QString::null; // No transmission by default.
}
QString Theme::gitSHA1() const
{
QString devString;
#ifdef GIT_SHA1
const QString githubPrefix(QLatin1String(
"https://github.com/owncloud/mirall/commit/"));
"https://github.com/owncloud/client/commit/"));
const QString gitSha1(QLatin1String(GIT_SHA1));
devString = QCoreApplication::translate("ownCloudTheme::about()",
"<p><small>Built from Git revision <a href=\"%1\">%2</a>"
@ -389,5 +394,5 @@ bool Theme::wizardSelectiveSyncDefaultNothing() const
}
} // end namespace mirall
} // end namespace client

View file

@ -189,12 +189,19 @@ public:
*/
virtual QString updateCheckUrl() const;
/**
* When true, the setup wizard will show the selective sync dialog by default and default
* to nothing selected
*/
virtual bool wizardSelectiveSyncDefaultNothing() const;
/**
* @brief Add an additional checksum header to PUT requests and compare them
* if they come with GET requests.
* This value sets the checksum type (SHA1, MD5 or Adler32) or is left empty
* if no checksumming is wanted. In that case it can still be overwritten in
* the client config file.
*/
virtual QString transmissionChecksum() const;
protected:
#ifndef TOKEN_AUTH_ONLY

View file

@ -0,0 +1,151 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "config.h"
#include "filesystem.h"
#include "transmissionchecksumvalidator.h"
#include "syncfileitem.h"
#include "propagatorjobs.h"
#include "configfile.h"
#include <QtConcurrent>
namespace OCC {
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
:QObject(parent),
_filePath(filePath)
{
}
void TransmissionChecksumValidator::setChecksumType( const QByteArray& type )
{
_checksumType = type;
}
QString TransmissionChecksumValidator::checksumType() const
{
QString checksumType = _checksumType;
if( checksumType.isEmpty() ) {
ConfigFile cfg;
checksumType = cfg.transmissionChecksum();
}
return checksumType;
}
void TransmissionChecksumValidator::uploadValidation()
{
const QString csType = checksumType();
if( csType.isEmpty() ) {
// if there is no checksum defined, continue to upload
emit validated(QByteArray());
} else {
// Calculate the checksum in a different thread first.
connect( &_watcher, SIGNAL(finished()),
this, SLOT(slotUploadChecksumCalculated()));
if( csType == checkSumMD5C ) {
_checksumHeader = checkSumMD5C;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
} else if( csType == checkSumSHA1C ) {
_checksumHeader = checkSumSHA1C;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, _filePath));
}
#ifdef ZLIB_FOUND
else if( csType == checkSumAdlerC) {
_checksumHeader = checkSumAdlerC;
_checksumHeader += ":";
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
}
#endif
else {
// for an unknown checksum, continue to upload
emit validated(QByteArray());
}
}
}
void TransmissionChecksumValidator::slotUploadChecksumCalculated( )
{
QByteArray checksum = _watcher.future().result();
if( !checksum.isEmpty() ) {
checksum.prepend( _checksumHeader );
}
emit validated(checksum);
}
void TransmissionChecksumValidator::downloadValidation( const QByteArray& checksumHeader )
{
// if the incoming header is empty, there was no checksum header, and
// no validation can happen. Just continue.
const QString csType = checksumType();
// for empty checksum type, everything is valid.
if( csType.isEmpty() ) {
emit validated(QByteArray());
return;
}
int indx = checksumHeader.indexOf(':');
if( indx < 0 ) {
qDebug() << "Checksum header malformed:" << checksumHeader;
emit validationFailed(tr("The checksum header is malformed.")); // show must go on - even not validated.
return;
}
const QByteArray type = checksumHeader.left(indx).toUpper();
_expectedHash = checksumHeader.mid(indx+1);
connect( &_watcher, SIGNAL(finished()), this, SLOT(slotDownloadChecksumCalculated()) );
// start the calculation in different thread
if( type == checkSumMD5C ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
} else if( type == checkSumSHA1C ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcSha1, _filePath));
}
#ifdef ZLIB_FOUND
else if( type == checkSumAdlerUpperC ) {
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
}
#endif
else {
qDebug() << "Unknown checksum type" << type;
emit validationFailed(tr("The checksum header is malformed."));
return;
}
}
void TransmissionChecksumValidator::slotDownloadChecksumCalculated()
{
const QByteArray hash = _watcher.future().result();
if( hash != _expectedHash ) {
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
} else {
// qDebug() << "Checksum checked and matching: " << _expectedHash;
emit validated(hash);
}
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QObject>
#include <QByteArray>
#include <QFutureWatcher>
namespace OCC {
class TransmissionChecksumValidator : public QObject
{
Q_OBJECT
public:
explicit TransmissionChecksumValidator(const QString& filePath, QObject *parent = 0);
/**
* method to prepare a checksum for transmission and save it to the _checksum
* member of the SyncFileItem *item.
* The kind of requested checksum is taken from config. No need to set from outside.
*
* In any case of processing (checksum set, no checksum required and also unusual error)
* the object will emit the signal validated(). The item->_checksum is than either
* set to a proper value or empty.
*/
void uploadValidation();
/**
* method to verify the checksum coming with requests in a checksum header. The required
* checksum method is read from config.
*
* If no checksum is there, or if a correct checksum is there, the signal validated()
* will be emitted. In case of any kind of error, the signal validationFailed() will
* be emitted.
*/
void downloadValidation( const QByteArray& checksumHeader );
// This is only used in test cases (by now). This class reads the required
// test case from the config file.
void setChecksumType(const QByteArray &type );
QString checksumType() const;
signals:
void validated(const QByteArray& checksum);
void validationFailed( const QString& errMsg );
private slots:
void slotUploadChecksumCalculated();
void slotDownloadChecksumCalculated();
private:
QByteArray _checksumType;
QByteArray _expectedHash;
QByteArray _checksumHeader;
QString _filePath;
// watcher for the checksum calculation thread
QFutureWatcher<QByteArray> _watcher;
};
}

View file

@ -15,6 +15,7 @@
#include "utility.h"
#include "version.h"
#include "theme.h"
// Note: This file must compile without QtGui
#include <QCoreApplication>
@ -154,10 +155,19 @@ QString Utility::platform()
QByteArray Utility::userAgentString()
{
return QString::fromLatin1("Mozilla/5.0 (%1) mirall/%2")
QString re = QString::fromLatin1("Mozilla/5.0 (%1) mirall/%2")
.arg(Utility::platform())
.arg(QLatin1String(MIRALL_STRINGIFY(MIRALL_VERSION)))
.toLatin1();
.arg(QLatin1String(MIRALL_STRINGIFY(MIRALL_VERSION)));
const QString appName = Theme::instance()->appName();
// this constant "ownCloud" is defined in the default OEM theming
// that is used for the standard client. If it is changed there,
// it needs to be adjusted here.
if( appName != QLatin1String("ownCloud") ) {
re += QString(" (%1)").arg(appName);
}
return re.toLatin1();
}
bool Utility::hasLaunchOnStartup(const QString &appName)
@ -401,10 +411,12 @@ void Utility::StopWatch::start()
_timer.start();
}
void Utility::StopWatch::stop()
quint64 Utility::StopWatch::stop()
{
addLapTime(QLatin1String(STOPWATCH_END_TAG));
quint64 duration = _timer.elapsed();
_timer.invalidate();
return duration;
}
void Utility::StopWatch::reset()

View file

@ -105,7 +105,7 @@ namespace Utility
QElapsedTimer _timer;
public:
void start();
void stop();
quint64 stop();
quint64 addLapTime( const QString& lapName );
void reset();

View file

@ -33,4 +33,6 @@ owncloud_add_test(SyncFileItem "")
owncloud_add_test(ConcatUrl "")
owncloud_add_test(XmlParse "")
owncloud_add_test(FileSystem "")
owncloud_add_test(TransChecksumValidator "")

93
test/testfilesystem.h Normal file
View file

@ -0,0 +1,93 @@
/*
This software is in the public domain, furnished "as is", without technical
support, and with no warranty, express or implied, as to its usefulness for
any purpose.
*/
#ifndef MIRALL_TESTFILESYSTEM_H
#define MIRALL_TESTFILESYSTEM_H
#include <QtTest>
#include <QDebug>
#include "filesystem.h"
#include "utility.h"
using namespace OCC::Utility;
using namespace OCC::FileSystem;
class TestFileSystem : public QObject
{
Q_OBJECT
QString _root;
QByteArray shellSum( const QByteArray& cmd, const QString& file )
{
QProcess md5;
QStringList args;
args.append(file);
md5.start(cmd, args);
QByteArray sumShell;
qDebug() << "File: "<< file;
if( md5.waitForFinished() ) {
sumShell = md5.readAll();
sumShell = sumShell.left( sumShell.indexOf(' '));
}
return sumShell;
}
private slots:
void initTestCase() {
qsrand(QTime::currentTime().msec());
QString subdir("test_"+QString::number(qrand()));
_root = QDir::tempPath() + "/" + subdir;
QDir dir("/tmp");
dir.mkdir(subdir);
qDebug() << "creating test directory " << _root;
}
void cleanupTestCase()
{
if( !_root.isEmpty() )
system(QString("rm -rf "+_root).toUtf8());
}
void testMd5Calc()
{
QString file( _root+"/file_a.bin");
writeRandomFile(file);
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcMd5(file);
QByteArray sSum = shellSum("/usr/bin/md5sum", file);
qDebug() << "calulated" << sum << "versus md5sum:"<< sSum;
QVERIFY(!sSum.isEmpty());
QVERIFY(!sum.isEmpty());
QVERIFY(sSum == sum );
}
void testSha1Calc()
{
QString file( _root+"/file_b.bin");
writeRandomFile(file);
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcSha1(file);
QByteArray sSum = shellSum("/usr/bin/sha1sum", file);
qDebug() << "calulated" << sum << "versus sha1sum:"<< sSum;
QVERIFY(!sSum.isEmpty());
QVERIFY(!sum.isEmpty());
QVERIFY(sSum == sum );
}
};
#endif

View file

@ -0,0 +1,159 @@
/*
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
*
*/
#pragma once
#include <QtTest>
#include <QDir>
#include <QString>
#include "transmissionchecksumvalidator.h"
#include "networkjobs.h"
#include "utility.h"
#include "filesystem.h"
#include "propagatorjobs.h"
using namespace OCC;
class TestTransChecksumValidator : public QObject
{
Q_OBJECT
private:
QString _root;
QString _testfile;
QString _expectedError;
QEventLoop _loop;
QByteArray _expected;
bool _successDown;
bool _errorSeen;
void processAndWait() {
_loop.processEvents();
Utility::usleep(200000);
_loop.processEvents();
}
public slots:
void slotUpValidated(const QByteArray& checksum) {
qDebug() << "Checksum: " << checksum;
QVERIFY(_expected == checksum );
}
void slotDownValidated() {
_successDown = true;
}
void slotDownError( const QString& errMsg ) {
QVERIFY(_expectedError == errMsg );
_errorSeen = true;
}
private slots:
void initTestCase() {
qDebug() << Q_FUNC_INFO;
_root = QDir::tempPath() + "/" + "test_" + QString::number(qrand());
QDir rootDir(_root);
rootDir.mkpath(_root );
_testfile = _root+"/csFile";
Utility::writeRandomFile( _testfile);
}
void testUploadChecksummingAdler() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType("Adler32");
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
QString testfile = _testfile;
_expected = "Adler32:"+FileSystem::calcAdler32( testfile );
qDebug() << "XX Expected Checksum: " << _expected;
vali->uploadValidation();
usleep(5000);
_loop.processEvents();
delete vali;
}
void testUploadChecksummingMd5() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType( OCC::checkSumMD5C );
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
_expected = checkSumMD5C;
_expected.append(":"+FileSystem::calcMd5( _testfile ));
vali->uploadValidation();
usleep(2000);
_loop.processEvents();
delete vali;
}
void testUploadChecksummingSha1() {
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType( OCC::checkSumSHA1C );
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotUpValidated(QByteArray)));
_expected = checkSumSHA1C;
_expected.append(":"+FileSystem::calcSha1( _testfile ));
vali->uploadValidation();
usleep(2000);
_loop.processEvents();
delete vali;
}
void testDownloadChecksummingAdler() {
QByteArray adler = checkSumAdlerC;
adler.append(":");
adler.append(FileSystem::calcAdler32( _testfile ));
_successDown = false;
TransmissionChecksumValidator *vali = new TransmissionChecksumValidator(_testfile, this);
vali->setChecksumType("Adler32");
connect(vali, SIGNAL(validated(QByteArray)), this, SLOT(slotDownValidated()));
connect(vali, SIGNAL(validationFailed(QString)), this, SLOT(slotDownError(QString)));
vali->downloadValidation(adler);
usleep(2000);
_loop.processEvents();
QVERIFY(_successDown);
_expectedError = QLatin1String("The downloaded file does not match the checksum, it will be resumed.");
_errorSeen = false;
vali->downloadValidation("Adler32:543345");
usleep(2000);
_loop.processEvents();
QVERIFY(_errorSeen);
_expectedError = QLatin1String("The checksum header is malformed.");
_errorSeen = false;
vali->downloadValidation("Klaas32:543345");
usleep(2000);
_loop.processEvents();
QVERIFY(_errorSeen);
delete vali;
}
void cleanupTestCase() {
}
};