...
 
Commits (2)
add_subdirectory(database)
\ No newline at end of file
find_package(KF5Archive)
set(CMAKE_AUTOMOC ON)
add_executable(archiving_benchmark archiving.cpp)
target_link_libraries(archiving_benchmark akabeicore KF5::Archive Qt5::Test)
add_subdirectory(database)
// This is a benchmark to compare the performance of our libarchive-based
// implementation to handle archives and KArchive.
#include <QChar>
#include <QString>
#include <QTemporaryFile>
#include <QTest>
#include <KF5/KArchive/KTar>
#include <libarchive++/archivehandler.h>
class ArchivingBenchmark : public QObject
{
Q_OBJECT
private:
QMap<QString, QStringList> fileSets;
const QString libarchive = QStringLiteral("libarchive");
const QString KArchive = QStringLiteral("KArchive");
private Q_SLOTS:
void initTestCase() {
qDebug() << "Start preparing files for the benchmark";
qsrand(1);
QList<QPair<int, int>> pairs{{1, 5000000}, {10000, 10}};
for (auto pair : pairs) {
int count(pair.first);
int size(pair.second);
QString label(QStringLiteral("%1 files of %2 bytes each").arg(
count).arg(size));
qDebug() << "Generating" << label;
QStringList paths;
for (int j = 0; j < count; ++j) {
QTemporaryFile file;
file.setAutoRemove(false);
file.open();
for (int k = 0; k < size; ++k) {
file.write(QString(QChar(32 + qrand() % 95)).toLatin1());
}
file.close();
paths.append(file.fileName());
}
fileSets.insert(label, paths);
}
qDebug() << "Finished preparing files for the benchmark";
}
void tets_data()
{
QTest::addColumn<QString>("backend");
QTest::addColumn<QString>("fileSet");
for (auto backend : {libarchive, KArchive}) {
for (auto fileSet : fileSets.keys()) {
QTest::newRow(QStringLiteral(
"Archive and extract %1 using %2").arg(
fileSet).arg(backend).toStdString().c_str())
<< backend << fileSet;
}
}
}
void tets() {
QFETCH(QString, backend);
QFETCH(QString, fileSet);
QStringList files = fileSets[fileSet];
QTemporaryFile archiveFile;
archiveFile.open();
archiveFile.close();
QString archiveFilePath(archiveFile.fileName());
QTemporaryDir extractedFolder;
QVERIFY(extractedFolder.isValid());
QString extractedFolderPath(extractedFolder.path());
QHash<QString, QString> entries;
int length(files.length());
for (int i = 0; i < length; ++i) {
QString file(files.at(i));
QString relativeFile(file.remove(QRegExp("^(\\w+:)?[/\\]")));
entries.insert(relativeFile, file);
}
if (backend == libarchive) {
QBENCHMARK_ONCE {
ArchiveHandler::write(archiveFilePath, entries);
ArchiveHandler handler(archiveFilePath);
handler.extractAll(extractedFolderPath, true);
}
} else { // backend == KArchive
QBENCHMARK_ONCE {
KTar tarWriter(archiveFilePath,
QStringLiteral("application/x-xz"));
tarWriter.open(QIODevice::WriteOnly);
for (auto relativeFile : entries.keys()) {
tarWriter.addLocalFile(entries[relativeFile], relativeFile);
}
tarWriter.close();
}
QBENCHMARK_ONCE {
// Use a separate instance for archiving and extracting, not to
// cheat.
KTar tarReader(archiveFilePath,
QStringLiteral("application/x-xz"));
tarReader.open(QIODevice::ReadOnly);
tarReader.directory()->copyTo(extractedFolderPath);
tarReader.close();
}
}
};
void cleanupTestCase() {
for (auto paths : fileSets.values()) {
for (auto path : paths) {
QFile file (path);
file.remove();
}
}
}
};
QTEST_MAIN(ArchivingBenchmark)
#include "archiving.moc"
......@@ -2,6 +2,7 @@
#include <QCoreApplication>
#include <QString>
#include <QTemporaryFile>
#include <QTest>
QString dbSchemaPath() {
......@@ -9,6 +10,16 @@ QString dbSchemaPath() {
QStringLiteral("/../../etc/dbschema.sql");
}
void initializeTemporaryFile(
QTemporaryFile* file, QByteArray content = QByteArray())
{
file->open();
if (!content.isEmpty()) {
file->write(content);
}
file->close();
}
// Based on QVERIFY_EXCEPTION_THROWN macro from Qt 5.3, implemented as an
// inline function and including an optional substring check for the exception
// message.
......
......@@ -2,4 +2,5 @@ function(_add_unittest name)
_add_test(unit_${name} ${name}_unittest ${name} ${ARGN})
endfunction(_add_unittest)
_add_unittest(archivehandler)
_add_unittest(sqliteresource)
/* This file is part of the Chakra project
Copyright (C) 2016 Adrián Chaves (Gallaecio)
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.
*/
#include <QDebug>
#include <QTemporaryDir>
#include <libarchive++/archivehandler.h>
#include "../common.h"
class ArchiveHandlerTest : public QObject
{
Q_OBJECT
public:
ArchiveHandlerTest() : QObject(), m_exampleFileContent("File content") {};
private:
QByteArray m_exampleFileContent;
private Q_SLOTS:
void cannotInstantiateWithNonExistentPath() {
verifyExceptionThrown<ArchiveException>([]() {
ArchiveHandler handler(QStringLiteral("Non-existent path"));
});
}
void emptyFoldersAreSupported() {
QString entryName("Entry name");
QTemporaryFile archive;
initializeTemporaryFile(&archive);
QTemporaryDir inputFolder;
QVERIFY(inputFolder.isValid());
QVERIFY(QDir(inputFolder.path()).exists());
QVERIFY(ArchiveHandler::write(
archive.fileName(), {{entryName, inputFolder.path()}}));
ArchiveHandler handler(archive.fileName());
QCOMPARE(handler.getEntries(), QStringList{entryName});
QTemporaryDir outputParentFolder;
QVERIFY(outputParentFolder.isValid());
handler.extractAll(outputParentFolder.path(), true);
QDir outputFolder(QStringLiteral("%1/%2").arg(
outputParentFolder.path()).arg(entryName));
qDebug() << outputFolder.path();
QFileInfo fileInfo(outputFolder.path());
qDebug() << (fileInfo.isFile() ?
QStringLiteral(
"The expected output folder is actually a file!") :
QStringLiteral("All is well!"));
QVERIFY(outputFolder.exists());
QCOMPARE(
outputFolder.entryInfoList(
QDir::NoDotAndDotDot|QDir::AllEntries).count(),
0);
}
void entryNamesMayBeRelative()
{
QString entryName("relative/path");
QTemporaryFile archive;
initializeTemporaryFile(&archive);
QTemporaryFile inputFile;
initializeTemporaryFile(&inputFile, m_exampleFileContent);
QVERIFY(ArchiveHandler::write(
archive.fileName(), {{entryName, inputFile.fileName()}}));
}
void entryNamesMayNotBeAbsolute()
{
QString entryName("/relative/path");
QTemporaryFile archive;
QByteArray archiveContent(
"Content that should not be overwritten if write() fails.");
initializeTemporaryFile(&archive, archiveContent);
QTemporaryFile inputFile;
initializeTemporaryFile(&inputFile, m_exampleFileContent);
QVERIFY(!ArchiveHandler::write(
archive.fileName(), {{entryName, inputFile.fileName()}}));
QFile archiveReader(archive.fileName());
QVERIFY(archiveReader.open(QIODevice::ReadOnly));
QCOMPARE(archiveReader.readAll(), archiveContent);
}
void entryNamesMayNotCdUp()
{
QString entryName("relative/../../path");
QTemporaryFile archive;
QByteArray archiveContent(
"Content that should not be overwritten if write() fails.");
initializeTemporaryFile(&archive, archiveContent);
QTemporaryFile inputFile;
initializeTemporaryFile(&inputFile, m_exampleFileContent);
QVERIFY(!ArchiveHandler::write(
archive.fileName(), {{entryName, inputFile.fileName()}}));
QFile archiveReader(archive.fileName());
QVERIFY(archiveReader.open(QIODevice::ReadOnly));
QCOMPARE(archiveReader.readAll(), archiveContent);
}
void entryNamesMayNotIncludeNullCharacter()
{
QString entryName("relative/\0path");
QTemporaryFile archive;
QByteArray archiveContent(
"Content that should not be overwritten if write() fails.");
initializeTemporaryFile(&archive, archiveContent);
QTemporaryFile inputFile;
initializeTemporaryFile(&inputFile, m_exampleFileContent);
QVERIFY(!ArchiveHandler::write(
archive.fileName(), {{entryName, inputFile.fileName()}}));
QFile archiveReader(archive.fileName());
QVERIFY(archiveReader.open(QIODevice::ReadOnly));
QCOMPARE(archiveReader.readAll(), archiveContent);
}
void writenFileEqualsExtractedFile() {
QString entryName("Entry name");
QTemporaryFile archive;
initializeTemporaryFile(&archive);
QTemporaryFile inputFile;
initializeTemporaryFile(&inputFile, m_exampleFileContent);
QVERIFY(ArchiveHandler::write(
archive.fileName(), {{entryName, inputFile.fileName()}}));
ArchiveHandler handler(archive.fileName());
QCOMPARE(handler.getEntries(), QStringList{entryName});
QTemporaryFile outputFile;
initializeTemporaryFile(&outputFile);
handler.extract(entryName, outputFile.fileName(), true);
QFile outputFileReader(outputFile.fileName());
QVERIFY(outputFileReader.open(QIODevice::ReadOnly));
QCOMPARE(outputFileReader.readAll(), m_exampleFileContent);
}
// TODO:
// - Subfolders in the entry name.
// - False is returned if the specified archive path cannot be
// written.
// - If any of the specified files to add to the archive does not
// exist, false is returned and the archive is not created.
// - If any of the specified files to add to the archive is not
// readable, false is returned and the archive is not created.
// - Repeated entries.
/*
static bool write(const QString& filename, const QHash<QString, QString>& files);
ArchiveHandler(const QString &);
virtual ~ArchiveHandler();
QString filename() const;
QStringList getEntries() const;
size_t getEntrySize(QString const& fileName) const;
size_t totalEntrySize() const;
QString readTextFile(const QString&);
void extract(const QString& source, const QString& destName, bool overwrite);
void extract(const ArchiveEntry &entry, const QString& destName, bool overwrite);
void extractAll(const QString&, bool overwrite);
void handleEntries(Akabei::EntryHandlerFunctor& handler);//FIXME: EntryHandlerFunctor should move into this file and be exported?
QList<QByteArray> md5(const QStringList&);
QByteArray md5(const QString &);
bool checkPackageValidity();
Akabei::Error::List errors();
struct archive *getArchive();
*/
// …
};
QTEST_MAIN(ArchiveHandlerTest)
#include "archivehandler.moc"