cf12defd28
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1570 lines
54 KiB
C++
1570 lines
54 KiB
C++
/* This file is part of the KDE project
|
|
SPDX-FileCopyrightText: 2006, 2010 David Faure <faure@kde.org>
|
|
SPDX-FileCopyrightText: 2012 Mario Bensi <mbensi@ipsquad.net>
|
|
|
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
|
*/
|
|
|
|
#include "karchivetest.h"
|
|
#include <k7zip.h>
|
|
#include <kar.h>
|
|
#include <krcc.h>
|
|
#include <ktar.h>
|
|
#include <kzip.h>
|
|
|
|
#include <QDebug>
|
|
#include <QFileInfo>
|
|
#include <QRegularExpression>
|
|
#include <QSaveFile>
|
|
#include <QStandardPaths>
|
|
#include <QTemporaryDir>
|
|
#include <QTest>
|
|
#include <kcompressiondevice.h>
|
|
|
|
#ifndef Q_OS_WIN
|
|
#include <cerrno>
|
|
#include <unistd.h> // symlink
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include <QScopedValueRollback>
|
|
#include <Windows.h>
|
|
#else
|
|
#include <grp.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
QTEST_MAIN(KArchiveTest)
|
|
|
|
static const int SIZE1 = 100;
|
|
|
|
/**
|
|
* Writes test fileset specified archive
|
|
* @param archive archive
|
|
*/
|
|
static void writeTestFilesToArchive(KArchive *archive)
|
|
{
|
|
QVERIFY(archive->writeFile("empty", "", 0100644, "weis", "users"));
|
|
QVERIFY(archive->writeFile("test1", QByteArray("Hallo"), 0100440, QString("weis"), QString("users")));
|
|
// Now let's try with the prepareWriting/writeData/finishWriting API
|
|
QVERIFY(archive->prepareWriting("test2", "weis", "users", 8));
|
|
QVERIFY(archive->writeData("Hallo ", 6));
|
|
QVERIFY(archive->writeData("Du", 2));
|
|
QVERIFY(archive->finishWriting(8));
|
|
// Add local file
|
|
QFile localFile(QStringLiteral("test3"));
|
|
QVERIFY(localFile.open(QIODevice::WriteOnly));
|
|
QVERIFY(localFile.write("Noch so einer", 13) == 13);
|
|
localFile.close();
|
|
|
|
QVERIFY(archive->addLocalFile("test3", "z/test3"));
|
|
|
|
// writeFile API
|
|
QVERIFY(archive->writeFile("my/dir/test3", "I do not speak German\nDavid.", 0100644, "dfaure", "hackers"));
|
|
|
|
// Now a medium file : 100 null bytes
|
|
char medium[SIZE1] = {0};
|
|
QVERIFY(archive->writeFile("mediumfile", QByteArray(medium, SIZE1)));
|
|
// Another one, with an absolute path
|
|
QVERIFY(archive->writeFile("/dir/subdir/mediumfile2", QByteArray(medium, SIZE1)));
|
|
|
|
// Now a huge file : 20000 null bytes
|
|
int n = 20000;
|
|
char *huge = new char[n];
|
|
memset(huge, 0, n);
|
|
QVERIFY(archive->writeFile("hugefile", QByteArray(huge, n)));
|
|
delete[] huge;
|
|
|
|
// Now an empty directory
|
|
QVERIFY(archive->writeDir("aaaemptydir"));
|
|
|
|
#ifndef Q_OS_WIN
|
|
// Add local symlink
|
|
QVERIFY(archive->addLocalFile("test3_symlink", "z/test3_symlink"));
|
|
#endif
|
|
|
|
// Add executable
|
|
QVERIFY(archive->writeFile("executableAll", "#!/bin/sh\necho hi", 0100755));
|
|
}
|
|
|
|
static QString getCurrentUserName()
|
|
{
|
|
#if defined(Q_OS_UNIX)
|
|
struct passwd *pw = getpwuid(getuid());
|
|
return pw ? QFile::decodeName(pw->pw_name) : QString::number(getuid());
|
|
#elif defined(Q_OS_WIN)
|
|
wchar_t buffer[255];
|
|
DWORD size = 255;
|
|
bool ok = GetUserNameW(buffer, &size);
|
|
if (!ok) {
|
|
return QString();
|
|
}
|
|
return QString::fromWCharArray(buffer);
|
|
#else
|
|
return QString();
|
|
#endif
|
|
}
|
|
|
|
static QString getCurrentGroupName()
|
|
{
|
|
#if defined(Q_OS_UNIX)
|
|
struct group *grp = getgrgid(getgid());
|
|
return grp ? QFile::decodeName(grp->gr_name) : QString::number(getgid());
|
|
#elif defined(Q_OS_WIN)
|
|
return QString();
|
|
#else
|
|
return QString();
|
|
#endif
|
|
}
|
|
|
|
enum ListingFlags {
|
|
WithUserGroup = 1,
|
|
WithTime = 0x02,
|
|
}; // ListingFlags
|
|
|
|
static QStringList recursiveListEntries(const KArchiveDirectory *dir, const QString &path, int listingFlags)
|
|
{
|
|
QStringList ret;
|
|
QStringList l = dir->entries();
|
|
l.sort();
|
|
for (const QString &it : std::as_const(l)) {
|
|
const KArchiveEntry *entry = dir->entry(it);
|
|
|
|
QString descr;
|
|
descr += QStringLiteral("mode=") + QString::number(entry->permissions(), 8) + ' ';
|
|
if (listingFlags & WithUserGroup) {
|
|
descr += QStringLiteral("user=") + entry->user() + ' ';
|
|
descr += QStringLiteral("group=") + entry->group() + ' ';
|
|
}
|
|
descr += QStringLiteral("path=") + path + (it) + ' ';
|
|
descr += QStringLiteral("type=") + (entry->isDirectory() ? "dir" : "file");
|
|
if (entry->isFile()) {
|
|
descr += QStringLiteral(" size=") + QString::number(static_cast<const KArchiveFile *>(entry)->size());
|
|
}
|
|
if (!entry->symLinkTarget().isEmpty()) {
|
|
descr += QStringLiteral(" symlink=") + entry->symLinkTarget();
|
|
}
|
|
|
|
if (listingFlags & WithTime) {
|
|
descr += QStringLiteral(" time=") + entry->date().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"));
|
|
}
|
|
|
|
// qDebug() << descr;
|
|
ret.append(descr);
|
|
|
|
if (entry->isDirectory()) {
|
|
ret += recursiveListEntries((KArchiveDirectory *)entry, path + it + '/', listingFlags);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Verifies contents of specified archive against test fileset
|
|
* @param archive archive
|
|
*/
|
|
static void testFileData(KArchive *archive)
|
|
{
|
|
const KArchiveDirectory *dir = archive->directory();
|
|
|
|
const KArchiveFile *f = dir->file(QStringLiteral("z/test3"));
|
|
QVERIFY(f);
|
|
|
|
QByteArray arr(f->data());
|
|
QCOMPARE(arr.size(), 13);
|
|
QCOMPARE(arr, QByteArray("Noch so einer"));
|
|
|
|
// Now test using createDevice()
|
|
QIODevice *dev = f->createDevice();
|
|
QByteArray contents = dev->readAll();
|
|
QCOMPARE(contents, arr);
|
|
delete dev;
|
|
|
|
dev = f->createDevice();
|
|
contents = dev->read(5); // test reading in two chunks
|
|
QCOMPARE(contents.size(), 5);
|
|
contents += dev->read(50);
|
|
QCOMPARE(contents.size(), 13);
|
|
QCOMPARE(QString::fromLatin1(contents.constData()), QString::fromLatin1(arr.constData()));
|
|
delete dev;
|
|
|
|
// test read/seek/peek work fine
|
|
f = dir->file(QStringLiteral("test1"));
|
|
dev = f->createDevice();
|
|
contents = dev->peek(4);
|
|
QCOMPARE(contents, QByteArray("Hall"));
|
|
contents = dev->peek(2);
|
|
QCOMPARE(contents, QByteArray("Ha"));
|
|
dev->seek(2);
|
|
contents = dev->peek(2);
|
|
QCOMPARE(contents, QByteArray("ll"));
|
|
dev->seek(0);
|
|
contents = dev->read(2);
|
|
QCOMPARE(contents, QByteArray("Ha"));
|
|
contents = dev->peek(2);
|
|
QCOMPARE(contents, QByteArray("ll"));
|
|
dev->seek(1);
|
|
contents = dev->read(1);
|
|
QCOMPARE(contents, QByteArray("a"));
|
|
dev->seek(4);
|
|
contents = dev->read(1);
|
|
QCOMPARE(contents, QByteArray("o"));
|
|
delete dev;
|
|
|
|
const KArchiveEntry *e = dir->entry(QStringLiteral("mediumfile"));
|
|
QVERIFY(e && e->isFile());
|
|
f = (KArchiveFile *)e;
|
|
QCOMPARE(f->data().size(), SIZE1);
|
|
|
|
f = dir->file(QStringLiteral("hugefile"));
|
|
QCOMPARE(f->data().size(), 20000);
|
|
|
|
e = dir->entry(QStringLiteral("aaaemptydir"));
|
|
QVERIFY(e && e->isDirectory());
|
|
QVERIFY(!dir->file("aaaemptydir"));
|
|
|
|
e = dir->entry(QStringLiteral("my/dir/test3"));
|
|
QVERIFY(e && e->isFile());
|
|
f = (KArchiveFile *)e;
|
|
dev = f->createDevice();
|
|
QByteArray firstLine = dev->readLine();
|
|
QCOMPARE(QString::fromLatin1(firstLine.constData()), QString::fromLatin1("I do not speak German\n"));
|
|
QByteArray secondLine = dev->read(100);
|
|
QCOMPARE(QString::fromLatin1(secondLine.constData()), QString::fromLatin1("David."));
|
|
delete dev;
|
|
#ifndef Q_OS_WIN
|
|
e = dir->entry(QStringLiteral("z/test3_symlink"));
|
|
QVERIFY(e);
|
|
QVERIFY(e->isFile());
|
|
QCOMPARE(e->symLinkTarget(), QString("test3"));
|
|
#endif
|
|
|
|
// Test "./" prefix for KOffice (xlink:href="./ObjectReplacements/Object 1")
|
|
e = dir->entry(QStringLiteral("./hugefile"));
|
|
QVERIFY(e && e->isFile());
|
|
e = dir->entry(QStringLiteral("./my/dir/test3"));
|
|
QVERIFY(e && e->isFile());
|
|
|
|
// Test directory entries
|
|
e = dir->entry(QStringLiteral("my"));
|
|
QVERIFY(e && e->isDirectory());
|
|
e = dir->entry(QStringLiteral("my/"));
|
|
QVERIFY(e && e->isDirectory());
|
|
e = dir->entry(QStringLiteral("./my/"));
|
|
QVERIFY(e && e->isDirectory());
|
|
}
|
|
|
|
static void testReadWrite(KArchive *archive)
|
|
{
|
|
QVERIFY(archive->writeFile("newfile", "New File", 0100440, "dfaure", "users"));
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
|
|
#endif
|
|
|
|
static void testCopyTo(KArchive *archive)
|
|
{
|
|
const KArchiveDirectory *dir = archive->directory();
|
|
QTemporaryDir tmpDir;
|
|
const QString dirName = tmpDir.path() + '/';
|
|
|
|
QVERIFY(dir->copyTo(dirName));
|
|
|
|
QVERIFY(QFile::exists(dirName + "dir"));
|
|
QVERIFY(QFileInfo(dirName + "dir").isDir());
|
|
|
|
QFileInfo fileInfo1(dirName + "dir/subdir/mediumfile2");
|
|
QVERIFY(fileInfo1.exists());
|
|
QVERIFY(fileInfo1.isFile());
|
|
QCOMPARE(fileInfo1.size(), Q_INT64_C(100));
|
|
|
|
QFileInfo fileInfo2(dirName + "hugefile");
|
|
QVERIFY(fileInfo2.exists());
|
|
QVERIFY(fileInfo2.isFile());
|
|
QCOMPARE(fileInfo2.size(), Q_INT64_C(20000));
|
|
|
|
QFileInfo fileInfo3(dirName + "mediumfile");
|
|
QVERIFY(fileInfo3.exists());
|
|
QVERIFY(fileInfo3.isFile());
|
|
QCOMPARE(fileInfo3.size(), Q_INT64_C(100));
|
|
|
|
QFileInfo fileInfo4(dirName + "my/dir/test3");
|
|
QVERIFY(fileInfo4.exists());
|
|
QVERIFY(fileInfo4.isFile());
|
|
QCOMPARE(fileInfo4.size(), Q_INT64_C(28));
|
|
|
|
#ifndef Q_OS_WIN
|
|
const QString fileName = dirName + "z/test3_symlink";
|
|
const QFileInfo fileInfo5(fileName);
|
|
QVERIFY(fileInfo5.exists());
|
|
QVERIFY(fileInfo5.isFile());
|
|
// Do not use fileInfo.symLinkTarget() for unix symlinks
|
|
// It returns the -full- path to the target, while we want the target string "as is".
|
|
QString symLinkTarget;
|
|
const QByteArray encodedFileName = QFile::encodeName(fileName);
|
|
QByteArray s;
|
|
#if defined(PATH_MAX)
|
|
s.resize(PATH_MAX + 1);
|
|
#else
|
|
int path_max = pathconf(encodedFileName.data(), _PC_PATH_MAX);
|
|
if (path_max <= 0) {
|
|
path_max = 4096;
|
|
}
|
|
s.resize(path_max);
|
|
#endif
|
|
int len = readlink(encodedFileName.data(), s.data(), s.size() - 1);
|
|
if (len >= 0) {
|
|
s[len] = '\0';
|
|
symLinkTarget = QFile::decodeName(s.constData());
|
|
}
|
|
QCOMPARE(symLinkTarget, QString("test3"));
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup);
|
|
qt_ntfs_permission_lookup++;
|
|
#endif
|
|
QVERIFY(QFileInfo(dirName + "executableAll").permissions() & (QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther));
|
|
}
|
|
|
|
/**
|
|
* Prepares dataset for archive filter tests
|
|
*/
|
|
void KArchiveTest::setupData()
|
|
{
|
|
QTest::addColumn<QString>("fileName");
|
|
QTest::addColumn<QString>("mimeType");
|
|
|
|
QTest::newRow(".tar.gz") << "karchivetest.tar.gz"
|
|
<< "application/gzip";
|
|
#if HAVE_BZIP2_SUPPORT
|
|
QTest::newRow(".tar.bz2") << "karchivetest.tar.bz2"
|
|
<< "application/x-bzip";
|
|
#endif
|
|
#if HAVE_XZ_SUPPORT
|
|
QTest::newRow(".tar.lzma") << "karchivetest.tar.lzma"
|
|
<< "application/x-lzma";
|
|
QTest::newRow(".tar.xz") << "karchivetest.tar.xz"
|
|
<< "application/x-xz";
|
|
#endif
|
|
#if HAVE_ZSTD_SUPPORT
|
|
QTest::newRow(".tar.zst") << "karchivetest.tar.zst"
|
|
<< "application/zstd";
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @see QTest::initTestCase()
|
|
*/
|
|
void KArchiveTest::initTestCase()
|
|
{
|
|
#ifndef Q_OS_WIN
|
|
// Prepare local symlink
|
|
QFile::remove(QStringLiteral("test3_symlink"));
|
|
if (::symlink("test3", "test3_symlink") != 0) {
|
|
qDebug() << errno;
|
|
QVERIFY(false);
|
|
}
|
|
#endif
|
|
// avoid interference from kdebugsettings on qCWarning
|
|
QStandardPaths::setTestModeEnabled(true);
|
|
}
|
|
|
|
void KArchiveTest::testEmptyFilename()
|
|
{
|
|
QTest::ignoreMessage(QtWarningMsg, "KArchive: No file name specified");
|
|
KTar tar(QLatin1String(""));
|
|
QVERIFY(!tar.open(QIODevice::ReadOnly));
|
|
QCOMPARE(tar.errorString(), tr("No filename or device was specified"));
|
|
}
|
|
|
|
void KArchiveTest::testNullDevice()
|
|
{
|
|
QIODevice *nil = nullptr;
|
|
QTest::ignoreMessage(QtWarningMsg, "KArchive: Null device specified");
|
|
KTar tar(nil);
|
|
QVERIFY(!tar.open(QIODevice::ReadOnly));
|
|
QCOMPARE(tar.errorString(), tr("No filename or device was specified"));
|
|
}
|
|
|
|
void KArchiveTest::testNonExistentFile()
|
|
{
|
|
KTar tar(QStringLiteral("nonexistent.tar.gz"));
|
|
QVERIFY(!tar.open(QIODevice::ReadOnly));
|
|
QCOMPARE(tar.errorString(), tr("File %1 does not exist").arg("nonexistent.tar.gz"));
|
|
}
|
|
|
|
void KArchiveTest::testCreateTar_data()
|
|
{
|
|
QTest::addColumn<QString>("fileName");
|
|
QTest::newRow(".tar") << "karchivetest.tar";
|
|
}
|
|
|
|
/**
|
|
* @dataProvider testCreateTar_data
|
|
*/
|
|
void KArchiveTest::testCreateTar()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// With tempfile: 0.7-0.8 ms, 994236 instr. loads
|
|
// Without tempfile: 0.81 ms, 992541 instr. loads
|
|
// Note: use ./karchivetest 2>&1 | grep ms
|
|
// to avoid being slowed down by the kDebugs.
|
|
QBENCHMARK {
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::WriteOnly));
|
|
|
|
writeTestFilesToArchive(&tar);
|
|
|
|
QVERIFY(tar.close());
|
|
|
|
QFileInfo fileInfo(QFile::encodeName(fileName));
|
|
QVERIFY(fileInfo.exists());
|
|
// We can't check for an exact size because of the addLocalFile, whose data is system-dependent
|
|
QVERIFY(fileInfo.size() > 450);
|
|
}
|
|
|
|
// NOTE The only .tar test, cleanup here
|
|
// QFile::remove(fileName);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testCreateTarXXX()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// With tempfile: 1.3-1.7 ms, 2555089 instr. loads
|
|
// Without tempfile: 0.87 ms, 987915 instr. loads
|
|
QBENCHMARK {
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::WriteOnly));
|
|
|
|
writeTestFilesToArchive(&tar);
|
|
|
|
QVERIFY(tar.close());
|
|
|
|
QFileInfo fileInfo(QFile::encodeName(fileName));
|
|
QVERIFY(fileInfo.exists());
|
|
// We can't check for an exact size because of the addLocalFile, whose data is system-dependent
|
|
QVERIFY(fileInfo.size() > 350);
|
|
}
|
|
}
|
|
|
|
// static void compareEntryWithTimestamp(const QString &dateString, const QString &expectedDateString, const QDateTime &expectedDateTime)
|
|
// Made it a macro so that line numbers are meaningful on failure
|
|
|
|
#define compareEntryWithTimestamp(dateString, expectedDateString, expectedDateTime) \
|
|
{ \
|
|
/* Take the time from the listing and chop it off */ \
|
|
const QDateTime dt = QDateTime::fromString(dateString.right(19), "dd.MM.yyyy hh:mm:ss"); \
|
|
QString _str(dateString); \
|
|
_str.chop(25); \
|
|
QCOMPARE(_str, expectedDateString); \
|
|
\
|
|
/* Compare the times separately with allowed 2 sec diversion */ \
|
|
if (dt.secsTo(expectedDateTime) > 2) { \
|
|
qWarning() << dt << "is too different from" << expectedDateTime; \
|
|
} \
|
|
QVERIFY(dt.secsTo(expectedDateTime) <= 2); \
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testReadTar() // testCreateTarGz must have been run first.
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
QFileInfo localFileData(QStringLiteral("test3"));
|
|
|
|
const QString systemUserName = getCurrentUserName();
|
|
const QString systemGroupName = getCurrentGroupName();
|
|
const QString owner = localFileData.owner();
|
|
const QString group = localFileData.group();
|
|
const QString emptyTime = QDateTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"));
|
|
const QDateTime creationTime = QFileInfo(fileName).birthTime();
|
|
|
|
// 1.6-1.7 ms per interaction, 2908428 instruction loads
|
|
// After the "no tempfile when writing fix" this went down
|
|
// to 0.9-1.0 ms, 1689059 instruction loads.
|
|
// I guess it finds the data in the kernel cache now that no KTempFile is
|
|
// used when writing.
|
|
QBENCHMARK {
|
|
KTar tar(fileName);
|
|
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup | WithTime);
|
|
|
|
#ifndef Q_OS_WIN
|
|
const int expectedCount = 16;
|
|
#else
|
|
const int expectedCount = 15;
|
|
#endif
|
|
if (listing.count() != expectedCount) {
|
|
qWarning() << listing;
|
|
}
|
|
QCOMPARE(listing.count(), expectedCount);
|
|
compareEntryWithTimestamp(listing[0], QString("mode=40755 user= group= path=aaaemptydir type=dir"), creationTime);
|
|
|
|
QCOMPARE(listing[1], QString("mode=40777 user=%1 group=%2 path=dir type=dir time=%3").arg(systemUserName).arg(systemGroupName).arg(emptyTime));
|
|
QCOMPARE(listing[2], QString("mode=40777 user=%1 group=%2 path=dir/subdir type=dir time=%3").arg(systemUserName).arg(systemGroupName).arg(emptyTime));
|
|
compareEntryWithTimestamp(listing[3], QString("mode=100644 user= group= path=dir/subdir/mediumfile2 type=file size=100"), creationTime);
|
|
compareEntryWithTimestamp(listing[4], QString("mode=100644 user=weis group=users path=empty type=file size=0"), creationTime);
|
|
compareEntryWithTimestamp(listing[5], QString("mode=100755 user= group= path=executableAll type=file size=17"), creationTime);
|
|
compareEntryWithTimestamp(listing[6], QString("mode=100644 user= group= path=hugefile type=file size=20000"), creationTime);
|
|
compareEntryWithTimestamp(listing[7], QString("mode=100644 user= group= path=mediumfile type=file size=100"), creationTime);
|
|
QCOMPARE(listing[8], QString("mode=40777 user=%1 group=%2 path=my type=dir time=").arg(systemUserName).arg(systemGroupName));
|
|
QCOMPARE(listing[9], QString("mode=40777 user=%1 group=%2 path=my/dir type=dir time=").arg(systemUserName).arg(systemGroupName));
|
|
compareEntryWithTimestamp(listing[10], QString("mode=100644 user=dfaure group=hackers path=my/dir/test3 type=file size=28"), creationTime);
|
|
compareEntryWithTimestamp(listing[11], QString("mode=100440 user=weis group=users path=test1 type=file size=5"), creationTime);
|
|
compareEntryWithTimestamp(listing[12], QString("mode=100644 user=weis group=users path=test2 type=file size=8"), creationTime);
|
|
QCOMPARE(listing[13], QString("mode=40777 user=%1 group=%2 path=z type=dir time=").arg(systemUserName).arg(systemGroupName));
|
|
|
|
// This one was added with addLocalFile, so ignore mode.
|
|
QString str = listing[14];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*user=")), QStringLiteral("user="));
|
|
|
|
compareEntryWithTimestamp(str, QString("user=%1 group=%2 path=z/test3 type=file size=13").arg(owner).arg(group), creationTime);
|
|
|
|
#ifndef Q_OS_WIN
|
|
str = listing[15];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path="));
|
|
|
|
compareEntryWithTimestamp(str, QString("path=z/test3_symlink type=file size=0 symlink=test3"), creationTime);
|
|
#endif
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This tests the decompression using kfilterdev, basically.
|
|
* To debug KTarPrivate::fillTempFile().
|
|
*
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testUncompress()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
QFETCH(QString, mimeType);
|
|
|
|
// testCreateTar must have been run first.
|
|
QVERIFY(QFile::exists(fileName));
|
|
KCompressionDevice filterDev(fileName);
|
|
QByteArray buffer;
|
|
buffer.resize(8 * 1024);
|
|
// qDebug() << "buffer.size()=" << buffer.size();
|
|
QVERIFY(filterDev.open(QIODevice::ReadOnly));
|
|
|
|
qint64 totalSize = 0;
|
|
qint64 len = -1;
|
|
while (!filterDev.atEnd() && len != 0) {
|
|
len = filterDev.read(buffer.data(), buffer.size());
|
|
QVERIFY(len >= 0);
|
|
totalSize += len;
|
|
// qDebug() << "read len=" << len << " totalSize=" << totalSize;
|
|
}
|
|
filterDev.close();
|
|
// qDebug() << "totalSize=" << totalSize;
|
|
QVERIFY(totalSize > 26000); // 27648 here when using gunzip
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testTarFileData()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreateTar must have been run first.
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
testFileData(&tar);
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testTarCopyTo()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreateTar must have been run first.
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
testCopyTo(&tar);
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testTarReadWrite()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreateTar must have been run first.
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::ReadWrite));
|
|
|
|
testReadWrite(&tar);
|
|
testFileData(&tar);
|
|
|
|
QVERIFY(tar.close());
|
|
|
|
// Reopen it and check it
|
|
{
|
|
KTar tar(fileName);
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
testFileData(&tar);
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
const KArchiveEntry *e = dir->entry(QStringLiteral("newfile"));
|
|
QVERIFY(e && e->isFile());
|
|
const KArchiveFile *f = (KArchiveFile *)e;
|
|
QCOMPARE(f->data().size(), 8);
|
|
}
|
|
|
|
// NOTE This is the last test for this dataset. so cleanup here
|
|
QFile::remove(fileName);
|
|
}
|
|
|
|
void KArchiveTest::testTarMaxLength_data()
|
|
{
|
|
QTest::addColumn<QString>("fileName");
|
|
QTest::newRow("maxlength.tar.gz") << "karchivetest-maxlength.tar.gz";
|
|
}
|
|
|
|
/**
|
|
* @dataProvider testTarMaxLength_data
|
|
*/
|
|
void KArchiveTest::testTarMaxLength()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
KTar tar(fileName);
|
|
|
|
QVERIFY(tar.open(QIODevice::WriteOnly));
|
|
|
|
// Generate long filenames of each possible length bigger than 98...
|
|
// Also exceed 512 byte block size limit to see how well the ././@LongLink
|
|
// implementation fares
|
|
for (int i = 98; i < 514; i++) {
|
|
QString str;
|
|
QString num;
|
|
str.fill('a', i - 10);
|
|
num.setNum(i);
|
|
num = num.rightJustified(10, '0');
|
|
tar.writeFile(str + num, "hum");
|
|
}
|
|
// Result of this test : works perfectly now (failed at 482 formerly and
|
|
// before that at 154).
|
|
QVERIFY(tar.close());
|
|
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
|
|
QCOMPARE(listing[0],
|
|
QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 "
|
|
"type=file size=3"));
|
|
QCOMPARE(listing[3],
|
|
QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 "
|
|
"type=file size=3"));
|
|
QCOMPARE(listing[4],
|
|
QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 "
|
|
"type=file size=3"));
|
|
|
|
QCOMPARE(listing.count(), 416);
|
|
|
|
QVERIFY(tar.close());
|
|
|
|
// NOTE Cleanup here
|
|
QFile::remove(fileName);
|
|
}
|
|
|
|
void KArchiveTest::testTarGlobalHeader()
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("global_header_test.tar.gz")));
|
|
QVERIFY2(tar.open(QIODevice::ReadOnly), "global_header_test.tar.gz");
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
|
|
QCOMPARE(listing.count(), 2);
|
|
|
|
QCOMPARE(listing[0], QString("mode=40775 user=root group=root path=Test type=dir"));
|
|
QCOMPARE(listing[1], QString("mode=664 user=root group=root path=Test/test.txt type=file size=0"));
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
void KArchiveTest::testTarPrefix()
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_prefix_test.tar.gz")));
|
|
QVERIFY2(tar.open(QIODevice::ReadOnly), "tar_prefix_test.tar.gz");
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
|
|
QCOMPARE(listing[0], QString("mode=40775 user=root group=root path=Test type=dir"));
|
|
QCOMPARE(listing[1], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7 type=dir"));
|
|
QCOMPARE(listing[2], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples type=dir"));
|
|
QCOMPARE(listing[3], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator type=dir"));
|
|
QCOMPARE(listing[4], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original type=dir"));
|
|
QCOMPARE(listing[5], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java type=dir"));
|
|
QCOMPARE(listing[6], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com type=dir"));
|
|
QCOMPARE(listing[7],
|
|
QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech type=dir"));
|
|
QCOMPARE(
|
|
listing[8],
|
|
QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples type=dir"));
|
|
QCOMPARE(
|
|
listing[9],
|
|
QString("mode=664 user=root group=root "
|
|
"path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples/GeneratorExample.html type=file size=43086"));
|
|
|
|
QCOMPARE(listing.count(), 10);
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
void KArchiveTest::testTarDirectoryForgotten()
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_directory_forgotten.tar.gz")));
|
|
QVERIFY2(tar.open(QIODevice::ReadOnly), "tar_directory_forgotten.tar.gz");
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
|
|
QVERIFY(listing[9].contains("trolltech/examples/generator"));
|
|
QVERIFY(listing[10].contains("trolltech/examples/generator/GeneratorExample.html"));
|
|
|
|
QCOMPARE(listing.count(), 11);
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
void KArchiveTest::testTarEmptyFileMissingDir()
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_emptyfile_missingdir.tar.gz")));
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing[0], QString("mode=40777 path=dir type=dir"));
|
|
QCOMPARE(listing[1], QString("mode=40777 path=dir/foo type=dir"));
|
|
QCOMPARE(listing[2], QString("mode=644 path=dir/foo/file type=file size=0"));
|
|
QCOMPARE(listing.count(), 3);
|
|
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
void KArchiveTest::testTarRootDir() // bug 309463
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_rootdir.tar.gz")));
|
|
QVERIFY2(tar.open(QIODevice::ReadOnly), qPrintable(tar.fileName()));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
// qDebug() << listing.join("\n");
|
|
|
|
QVERIFY(listing[0].contains("%{APPNAME}.cpp"));
|
|
QVERIFY(listing[1].contains("%{APPNAME}.h"));
|
|
QVERIFY(listing[5].contains("main.cpp"));
|
|
|
|
QCOMPARE(listing.count(), 10);
|
|
}
|
|
|
|
void KArchiveTest::testTarLongNonASCIINames() // bug 266141
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
QSKIP("tar test file encoding not windows compatible");
|
|
#endif
|
|
|
|
const QString tarName = QString("karchive-long-non-ascii-names.tar");
|
|
const QString longName = QString::fromUtf8("раз-два-три-четыре-пять-вышел-зайчик-погулять-вдруг-охотник-выбегает-прямо-в-зайчика.txt");
|
|
|
|
{
|
|
KTar tar(tarName);
|
|
QVERIFY(tar.open(QIODevice::WriteOnly));
|
|
QVERIFY(tar.writeFile(longName, "", 0644, "user", "users"));
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
{
|
|
KTar tar(tarName);
|
|
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QString(""), 0);
|
|
|
|
const QString expectedListingEntry = QString("mode=644 path=") + longName + QString(" type=file size=0");
|
|
|
|
QCOMPARE(listing.count(), 1);
|
|
|
|
QCOMPARE(listing[0], expectedListingEntry);
|
|
QVERIFY(tar.close());
|
|
}
|
|
}
|
|
|
|
void KArchiveTest::testTarShortNonASCIINames() // bug 266141
|
|
{
|
|
#ifdef Q_OS_WIN
|
|
QSKIP("tar test file encoding not windows compatible");
|
|
#endif
|
|
|
|
KTar tar(QFINDTESTDATA(QString("tar_non_ascii_file_name.tar.gz")));
|
|
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QString(""), 0);
|
|
|
|
QCOMPARE(listing.count(), 1);
|
|
QCOMPARE(listing[0],
|
|
QString("mode=644 path=абвгдеёжзийклмнопрстуфхцчшщъыьэюя.txt"
|
|
" type=file size=0"));
|
|
QVERIFY(tar.close());
|
|
}
|
|
|
|
void KArchiveTest::testTarDirectoryTwice() // bug 206994
|
|
{
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_directory_twice.tar.gz")));
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup);
|
|
// qDebug() << listing.join("\n");
|
|
|
|
QVERIFY(listing[0].contains("path=d"));
|
|
QVERIFY(listing[1].contains("path=d/f1.txt"));
|
|
QVERIFY(listing[2].contains("path=d/f2.txt"));
|
|
|
|
QCOMPARE(listing.count(), 3);
|
|
}
|
|
|
|
void KArchiveTest::testTarIgnoreRelativePathOutsideArchive()
|
|
{
|
|
#if HAVE_BZIP2_SUPPORT
|
|
// This test extracts a Tar archive that contains a relative path "../foo" pointing
|
|
// outside of the archive directory. For security reasons extractions should only
|
|
// be allowed within the extracted directory as long as not specifically asked.
|
|
|
|
KTar tar(QFINDTESTDATA(QLatin1String("tar_relative_path_outside_archive.tar.bz2")));
|
|
QVERIFY(tar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = tar.directory();
|
|
QTemporaryDir tmpDir;
|
|
const QString dirName = tmpDir.path() + "/subdir"; // use a subdir so /tmp/foo doesn't break the test :)
|
|
QDir().mkdir(dirName);
|
|
|
|
QVERIFY(dir->copyTo(dirName));
|
|
QVERIFY(!QFile::exists(dirName + "../foo"));
|
|
QVERIFY(QFile::exists(dirName + "/foo"));
|
|
#else
|
|
QSKIP("Test data is in bz2 format and karchive is built without bzip2 format");
|
|
#endif
|
|
}
|
|
///
|
|
|
|
static const char s_zipFileName[] = "karchivetest.zip";
|
|
static const char s_zipMaxLengthFileName[] = "karchivetest-maxlength.zip";
|
|
static const char s_zipLocaleFileName[] = "karchivetest-locale.zip";
|
|
static const char s_zipMimeType[] = "application/vnd.oasis.opendocument.text";
|
|
|
|
void KArchiveTest::testCreateZip()
|
|
{
|
|
KZip zip(s_zipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::WriteOnly));
|
|
|
|
zip.setExtraField(KZip::NoExtraField);
|
|
|
|
zip.setCompression(KZip::NoCompression);
|
|
QByteArray zipMimeType(s_zipMimeType);
|
|
zip.writeFile(QStringLiteral("mimetype"), zipMimeType);
|
|
zip.setCompression(KZip::DeflateCompression);
|
|
|
|
writeTestFilesToArchive(&zip);
|
|
|
|
QVERIFY(zip.close());
|
|
|
|
QFile zipFile(QFile::encodeName(s_zipFileName));
|
|
QFileInfo fileInfo(zipFile);
|
|
QVERIFY(fileInfo.exists());
|
|
QVERIFY(fileInfo.size() > 300);
|
|
|
|
// Check that the header with no-compression and no-extrafield worked.
|
|
// (This is for the "magic" for koffice documents)
|
|
QVERIFY(zipFile.open(QIODevice::ReadOnly));
|
|
QByteArray arr = zipFile.read(4);
|
|
QCOMPARE(arr, QByteArray("PK\003\004"));
|
|
QVERIFY(zipFile.seek(30));
|
|
arr = zipFile.read(8);
|
|
QCOMPARE(arr, QByteArray("mimetype"));
|
|
arr = zipFile.read(zipMimeType.size());
|
|
QCOMPARE(arr, zipMimeType);
|
|
}
|
|
|
|
void KArchiveTest::testCreateZipError()
|
|
{
|
|
// Giving a directory name to kzip must give an error case in close(), see #136630.
|
|
// Otherwise we just lose data.
|
|
KZip zip(QDir::currentPath());
|
|
|
|
QVERIFY(!zip.open(QIODevice::WriteOnly));
|
|
QCOMPARE(zip.errorString(), tr("QSaveFile creation for %1 failed: Filename refers to a directory").arg(QDir::currentPath()));
|
|
}
|
|
|
|
void KArchiveTest::testReadZipError()
|
|
{
|
|
QFile brokenZip(QStringLiteral("broken.zip"));
|
|
QVERIFY(brokenZip.open(QIODevice::WriteOnly));
|
|
|
|
// incomplete magic
|
|
brokenZip.write(QByteArray("PK\003"));
|
|
|
|
brokenZip.close();
|
|
{
|
|
KZip zip(QStringLiteral("broken.zip"));
|
|
|
|
QVERIFY(!zip.open(QIODevice::ReadOnly));
|
|
QCOMPARE(zip.errorString(), tr("Invalid ZIP file. Unexpected end of file. (Error code: %1)").arg(1));
|
|
|
|
QVERIFY(brokenZip.open(QIODevice::WriteOnly | QIODevice::Append));
|
|
|
|
// add rest of magic, but still incomplete header
|
|
brokenZip.write(QByteArray("\004\000\000\000\000"));
|
|
|
|
brokenZip.close();
|
|
|
|
QVERIFY(!zip.open(QIODevice::ReadOnly));
|
|
QCOMPARE(zip.errorString(), tr("Invalid ZIP file. Unexpected end of file. (Error code: %1)").arg(4));
|
|
}
|
|
|
|
QVERIFY(brokenZip.remove());
|
|
}
|
|
|
|
void KArchiveTest::testReadZip()
|
|
{
|
|
// testCreateZip must have been run first.
|
|
KZip zip(s_zipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
// ZIP has no support for per-file user/group, so omit them from the listing
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
#ifndef Q_OS_WIN
|
|
QCOMPARE(listing.count(), 17);
|
|
#else
|
|
QCOMPARE(listing.count(), 16);
|
|
#endif
|
|
QCOMPARE(listing[0], QString("mode=40755 path=aaaemptydir type=dir"));
|
|
QCOMPARE(listing[1], QString("mode=40777 path=dir type=dir"));
|
|
QCOMPARE(listing[2], QString("mode=40777 path=dir/subdir type=dir"));
|
|
QCOMPARE(listing[3], QString("mode=100644 path=dir/subdir/mediumfile2 type=file size=100"));
|
|
QCOMPARE(listing[4], QString("mode=100644 path=empty type=file size=0"));
|
|
QCOMPARE(listing[5], QString("mode=100755 path=executableAll type=file size=17"));
|
|
QCOMPARE(listing[6], QString("mode=100644 path=hugefile type=file size=20000"));
|
|
QCOMPARE(listing[7], QString("mode=100644 path=mediumfile type=file size=100"));
|
|
QCOMPARE(listing[8], QString("mode=100644 path=mimetype type=file size=%1").arg(strlen(s_zipMimeType)));
|
|
QCOMPARE(listing[9], QString("mode=40777 path=my type=dir"));
|
|
QCOMPARE(listing[10], QString("mode=40777 path=my/dir type=dir"));
|
|
QCOMPARE(listing[11], QString("mode=100644 path=my/dir/test3 type=file size=28"));
|
|
QCOMPARE(listing[12], QString("mode=100440 path=test1 type=file size=5"));
|
|
QCOMPARE(listing[13], QString("mode=100644 path=test2 type=file size=8"));
|
|
QCOMPARE(listing[14], QString("mode=40777 path=z type=dir"));
|
|
// This one was added with addLocalFile, so ignore mode
|
|
QString str = listing[15];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path="));
|
|
QCOMPARE(str, QString("path=z/test3 type=file size=13"));
|
|
#ifndef Q_OS_WIN
|
|
str = listing[16];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path="));
|
|
QCOMPARE(str, QString("path=z/test3_symlink type=file size=5 symlink=test3"));
|
|
#endif
|
|
|
|
QVERIFY(zip.close());
|
|
}
|
|
|
|
void KArchiveTest::testZipFileData()
|
|
{
|
|
// testCreateZip must have been run first.
|
|
KZip zip(s_zipFileName);
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
testFileData(&zip);
|
|
|
|
QVERIFY(zip.close());
|
|
}
|
|
|
|
void KArchiveTest::testZipCopyTo()
|
|
{
|
|
// testCreateZip must have been run first.
|
|
KZip zip(s_zipFileName);
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
testCopyTo(&zip);
|
|
|
|
QVERIFY(zip.close());
|
|
}
|
|
|
|
void KArchiveTest::testZipMaxLength()
|
|
{
|
|
KZip zip(s_zipMaxLengthFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::WriteOnly));
|
|
|
|
// Similar to testTarMaxLength just to make sure, but of course zip doesn't have
|
|
// those limitations in the first place.
|
|
for (int i = 98; i < 514; i++) {
|
|
QString str;
|
|
QString num;
|
|
str.fill('a', i - 10);
|
|
num.setNum(i);
|
|
num = num.rightJustified(10, '0');
|
|
zip.writeFile(str + num, "hum");
|
|
}
|
|
QVERIFY(zip.close());
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing[0],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3"));
|
|
QCOMPARE(
|
|
listing[3],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3"));
|
|
QCOMPARE(
|
|
listing[4],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3"));
|
|
|
|
QCOMPARE(listing.count(), 514 - 98);
|
|
|
|
QVERIFY(zip.close());
|
|
}
|
|
|
|
void KArchiveTest::testZipWithNonLatinFileNames()
|
|
{
|
|
KZip zip(s_zipLocaleFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::WriteOnly));
|
|
|
|
const QByteArray fileData("Test of data with a russian file name");
|
|
const QString fileName = QString::fromUtf8("Архитектура.okular");
|
|
const QString recodedFileName = QFile::decodeName(QFile::encodeName(fileName));
|
|
QVERIFY(zip.writeFile(fileName, fileData));
|
|
|
|
QVERIFY(zip.close());
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing.count(), 1);
|
|
QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(recodedFileName).arg(fileData.size()));
|
|
|
|
const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0]));
|
|
QCOMPARE(fileEntry->data(), fileData);
|
|
}
|
|
|
|
void KArchiveTest::testZipWithOverwrittenFileName()
|
|
{
|
|
KZip zip(s_zipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::WriteOnly));
|
|
|
|
const QByteArray fileData1("There could be a fire, if there is smoke.");
|
|
const QString fileName = QStringLiteral("wisdom");
|
|
QVERIFY(zip.writeFile(fileName, fileData1, 0100644, "konqi", "dragons"));
|
|
|
|
// now overwrite it
|
|
const QByteArray fileData2("If there is smoke, there could be a fire.");
|
|
QVERIFY(zip.writeFile(fileName, fileData2, 0100644, "konqi", "dragons"));
|
|
|
|
QVERIFY(zip.close());
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing.count(), 1);
|
|
QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(fileName).arg(fileData2.size()));
|
|
|
|
const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0]));
|
|
QCOMPARE(fileEntry->data(), fileData2);
|
|
}
|
|
|
|
static bool writeFile(const QString &dirName, const QString &fileName, const QByteArray &data)
|
|
{
|
|
Q_ASSERT(dirName.endsWith('/'));
|
|
QFile file(dirName + fileName);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
return false;
|
|
}
|
|
file.write(data);
|
|
return true;
|
|
}
|
|
|
|
void KArchiveTest::testZipAddLocalDirectory()
|
|
{
|
|
// Prepare local dir
|
|
QTemporaryDir tmpDir;
|
|
const QString dirName = tmpDir.path() + '/';
|
|
|
|
const QByteArray file1Data = "Hello Shantanu";
|
|
const QString file1 = QStringLiteral("file1");
|
|
QVERIFY(writeFile(dirName, file1, file1Data));
|
|
|
|
const QString emptyDir = QStringLiteral("emptyDir");
|
|
QDir(dirName).mkdir(emptyDir);
|
|
|
|
{
|
|
KZip zip(s_zipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::WriteOnly));
|
|
QVERIFY(zip.addLocalDirectory(dirName, "."));
|
|
QVERIFY(zip.close());
|
|
}
|
|
{
|
|
KZip zip(s_zipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const KArchiveEntry *e = dir->entry(file1);
|
|
QVERIFY(e && e->isFile());
|
|
const KArchiveFile *f = static_cast<const KArchiveFile *>(e);
|
|
QCOMPARE(f->data(), file1Data);
|
|
|
|
const KArchiveEntry *empty = dir->entry(emptyDir);
|
|
QVERIFY(empty && empty->isDirectory());
|
|
}
|
|
}
|
|
|
|
void KArchiveTest::testZipReadRedundantDataDescriptor_data()
|
|
{
|
|
QTest::addColumn<QString>("fileName");
|
|
QTest::newRow("noSignature") << "data/redundantDataDescriptorsNoSignature.zip";
|
|
QTest::newRow("withSignature") << "data/redundantDataDescriptorsWithSignature.zip";
|
|
}
|
|
|
|
/**
|
|
* @dataProvider testZipReadRedundantDataDescriptor_data
|
|
*/
|
|
void KArchiveTest::testZipReadRedundantDataDescriptor()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
const QString redundantDataDescriptorZipFileName = QFINDTESTDATA(fileName);
|
|
QVERIFY(!redundantDataDescriptorZipFileName.isEmpty());
|
|
|
|
KZip zip(redundantDataDescriptorZipFileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QByteArray fileData("aaaaaaaaaaaaaaa");
|
|
|
|
// ZIP has no support for per-file user/group, so omit them from the listing
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing.count(), 2);
|
|
QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=compressed type=file size=%2").arg(fileData.size()));
|
|
QCOMPARE(listing[1], QString::fromUtf8("mode=100644 path=uncompressed type=file size=%2").arg(fileData.size()));
|
|
|
|
const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0]));
|
|
QCOMPARE(fileEntry->data(), fileData);
|
|
fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[1]));
|
|
QCOMPARE(fileEntry->data(), fileData);
|
|
}
|
|
|
|
void KArchiveTest::testZipDirectoryPermissions()
|
|
{
|
|
QString fileName = QFINDTESTDATA("data/dirpermissions.zip");
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
KZip zip(fileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
const QStringList listing = recursiveListEntries(dir, QString(), 0);
|
|
QCOMPARE(listing.join(' '), QString::fromUtf8("mode=40700 path=700 type=dir mode=40750 path=750 type=dir mode=40755 path=755 type=dir"));
|
|
}
|
|
|
|
void KArchiveTest::testZipWithinZip()
|
|
{
|
|
QString fileName = QFINDTESTDATA("data/zip_within_zip.zip");
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
KZip zip(fileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
const QStringList listing = recursiveListEntries(dir, QString(), 0);
|
|
QCOMPARE(listing.count(), 2);
|
|
QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=foo type=file size=7"));
|
|
QCOMPARE(listing[1], QString::fromUtf8("mode=100644 path=test.epub type=file size=525154"));
|
|
}
|
|
|
|
void KArchiveTest::testZipUnusualButValid()
|
|
{
|
|
QString fileName = QFINDTESTDATA("data/unusual_but_valid_364071.zip");
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
KZip zip(fileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *dir = zip.directory();
|
|
const QStringList listing = recursiveListEntries(dir, QString(), 0);
|
|
QCOMPARE(listing.join(' '), QLatin1String("mode=40744 path=test type=dir mode=744 path=test/os-release type=file size=199"));
|
|
}
|
|
|
|
void KArchiveTest::testZipDuplicateNames()
|
|
{
|
|
QString fileName = QFINDTESTDATA("data/out.epub");
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
KZip zip(fileName);
|
|
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
int metaInfCount = 0;
|
|
for (const QString &entryName : zip.directory()->entries()) {
|
|
if (entryName.startsWith("META-INF")) {
|
|
metaInfCount++;
|
|
}
|
|
}
|
|
|
|
QVERIFY2(metaInfCount == 1, "Archive root directory contains duplicates");
|
|
}
|
|
|
|
void KArchiveTest::testZip64()
|
|
{
|
|
QString fileName = QFINDTESTDATA("data/zip64.pkpass");
|
|
QVERIFY(!fileName.isEmpty());
|
|
|
|
KZip zip(fileName);
|
|
QVERIFY(zip.open(QIODevice::ReadOnly));
|
|
|
|
// NOTE the actual content of this file has been wiped, we can only verify the file meta data here
|
|
QCOMPARE(zip.directory()->entries().size(), 7);
|
|
auto entry = static_cast<const KZipFileEntry *>(zip.directory()->entry(QStringLiteral("manifest.json")));
|
|
QVERIFY(entry);
|
|
QCOMPARE(entry->size(), 305);
|
|
QCOMPARE(entry->compressedSize(), 194);
|
|
QCOMPARE(entry->position(), 21417);
|
|
entry = static_cast<const KZipFileEntry *>(zip.directory()->entry(QStringLiteral("logo.png")));
|
|
QVERIFY(entry);
|
|
QCOMPARE(entry->size(), 3589);
|
|
QCOMPARE(entry->compressedSize(), 3594);
|
|
QCOMPARE(entry->position(), 8943);
|
|
}
|
|
|
|
void KArchiveTest::testRcc()
|
|
{
|
|
const QString rccFile = QFINDTESTDATA("runtime_resource.rcc"); // was copied from qtbase/tests/auto/corelib/io/qresourceengine
|
|
QVERIFY(!rccFile.isEmpty());
|
|
KRcc rcc(rccFile);
|
|
QVERIFY(rcc.open(QIODevice::ReadOnly));
|
|
const KArchiveDirectory *rootDir = rcc.directory();
|
|
QVERIFY(rootDir != nullptr);
|
|
const KArchiveEntry *rrEntry = rootDir->entry(QStringLiteral("runtime_resource"));
|
|
QVERIFY(rrEntry && rrEntry->isDirectory());
|
|
const KArchiveDirectory *rrDir = static_cast<const KArchiveDirectory *>(rrEntry);
|
|
const KArchiveEntry *fileEntry = rrDir->entry(QStringLiteral("search_file.txt"));
|
|
QVERIFY(fileEntry && fileEntry->isFile());
|
|
const KArchiveFile *searchFile = static_cast<const KArchiveFile *>(fileEntry);
|
|
const QByteArray fileData = searchFile->data();
|
|
QCOMPARE(QString::fromLatin1(fileData), QString::fromLatin1("root\n"));
|
|
}
|
|
|
|
void KArchiveTest::testAr()
|
|
{
|
|
const QString artestFile = QFINDTESTDATA("data/artest.a");
|
|
KAr ar(artestFile);
|
|
|
|
QVERIFY(ar.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = ar.directory();
|
|
QVERIFY(dir != nullptr);
|
|
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
const QStringList expected = {"mode=100644 path=at_quick_exit.oS type=file size=3456",
|
|
"mode=100644 path=atexit.oS type=file size=3436",
|
|
"mode=100644 path=elf-init.oS type=file size=3288",
|
|
"mode=100644 path=fstat.oS type=file size=3296",
|
|
"mode=100644 path=fstat64.oS type=file size=3304",
|
|
"mode=100644 path=fstatat.oS type=file size=3352",
|
|
"mode=100644 path=fstatat64.oS type=file size=3380",
|
|
"mode=100644 path=lstat.oS type=file size=3320",
|
|
"mode=100644 path=lstat64.oS type=file size=3332",
|
|
"mode=100644 path=mknod.oS type=file size=2840",
|
|
"mode=100644 path=mknodat.oS type=file size=2848",
|
|
"mode=100644 path=stack_chk_fail_local.oS type=file size=2288",
|
|
"mode=100644 path=stat.oS type=file size=3316",
|
|
"mode=100644 path=stat64.oS type=file size=3312",
|
|
"mode=100644 path=warning-nop.oS type=file size=1976"};
|
|
|
|
QCOMPARE(listing.count(), expected.count());
|
|
QCOMPARE(listing, expected);
|
|
|
|
QVERIFY(ar.close());
|
|
}
|
|
|
|
/**
|
|
* @see QTest::cleanupTestCase()
|
|
*/
|
|
void KArchiveTest::cleanupTestCase()
|
|
{
|
|
QFile::remove(s_zipMaxLengthFileName);
|
|
QFile::remove(s_zipFileName);
|
|
QFile::remove(s_zipLocaleFileName);
|
|
#ifndef Q_OS_WIN
|
|
QFile::remove(QStringLiteral("test3_symlink"));
|
|
#endif
|
|
}
|
|
|
|
///
|
|
|
|
#if HAVE_XZ_SUPPORT
|
|
|
|
/**
|
|
* Prepares dataset for archive filter tests
|
|
*/
|
|
void KArchiveTest::setup7ZipData()
|
|
{
|
|
QTest::addColumn<QString>("fileName");
|
|
QTest::newRow(".7z") << "karchivetest.7z";
|
|
}
|
|
|
|
/**
|
|
* @dataProvider testCreate7Zip_data
|
|
*/
|
|
void KArchiveTest::testCreate7Zip()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
QBENCHMARK {
|
|
K7Zip k7zip(fileName);
|
|
QVERIFY(k7zip.open(QIODevice::WriteOnly));
|
|
|
|
writeTestFilesToArchive(&k7zip);
|
|
|
|
QVERIFY(k7zip.close());
|
|
|
|
QFileInfo fileInfo(QFile::encodeName(fileName));
|
|
QVERIFY(fileInfo.exists());
|
|
// qDebug() << "fileInfo.size()" << fileInfo.size();
|
|
// We can't check for an exact size because of the addLocalFile, whose data is system-dependent
|
|
QVERIFY(fileInfo.size() > 390);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::testRead7Zip() // testCreate7Zip must have been run first.
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
QBENCHMARK {
|
|
K7Zip k7zip(fileName);
|
|
|
|
QVERIFY(k7zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = k7zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
#ifndef Q_OS_WIN
|
|
QCOMPARE(listing.count(), 16);
|
|
#else
|
|
QCOMPARE(listing.count(), 15);
|
|
#endif
|
|
QCOMPARE(listing[0], QString("mode=40755 path=aaaemptydir type=dir"));
|
|
QCOMPARE(listing[1], QString("mode=40777 path=dir type=dir"));
|
|
QCOMPARE(listing[2], QString("mode=40777 path=dir/subdir type=dir"));
|
|
QCOMPARE(listing[3], QString("mode=100644 path=dir/subdir/mediumfile2 type=file size=100"));
|
|
QCOMPARE(listing[4], QString("mode=100644 path=empty type=file size=0"));
|
|
QCOMPARE(listing[5], QString("mode=100755 path=executableAll type=file size=17"));
|
|
QCOMPARE(listing[6], QString("mode=100644 path=hugefile type=file size=20000"));
|
|
QCOMPARE(listing[7], QString("mode=100644 path=mediumfile type=file size=100"));
|
|
QCOMPARE(listing[8], QString("mode=40777 path=my type=dir"));
|
|
QCOMPARE(listing[9], QString("mode=40777 path=my/dir type=dir"));
|
|
QCOMPARE(listing[10], QString("mode=100644 path=my/dir/test3 type=file size=28"));
|
|
QCOMPARE(listing[11], QString("mode=100440 path=test1 type=file size=5"));
|
|
QCOMPARE(listing[12], QString("mode=100644 path=test2 type=file size=8"));
|
|
QCOMPARE(listing[13], QString("mode=40777 path=z type=dir"));
|
|
// This one was added with addLocalFile, so ignore mode/user/group.
|
|
QString str = listing[14];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path="));
|
|
QCOMPARE(str, QString("path=z/test3 type=file size=13"));
|
|
#ifndef Q_OS_WIN
|
|
str = listing[15];
|
|
str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path="));
|
|
QCOMPARE(str, QString("path=z/test3_symlink type=file size=0 symlink=test3"));
|
|
#endif
|
|
|
|
QVERIFY(k7zip.close());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::test7ZipFileData()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreate7Zip must have been run first.
|
|
K7Zip k7zip(fileName);
|
|
QVERIFY(k7zip.open(QIODevice::ReadOnly));
|
|
|
|
testFileData(&k7zip);
|
|
|
|
QVERIFY(k7zip.close());
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::test7ZipCopyTo()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreateTar must have been run first.
|
|
K7Zip k7zip(fileName);
|
|
QVERIFY(k7zip.open(QIODevice::ReadOnly));
|
|
|
|
testCopyTo(&k7zip);
|
|
|
|
QVERIFY(k7zip.close());
|
|
}
|
|
|
|
/**
|
|
* @dataProvider setupData
|
|
*/
|
|
void KArchiveTest::test7ZipReadWrite()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
// testCreate7zip must have been run first.
|
|
K7Zip k7zip(fileName);
|
|
QVERIFY(k7zip.open(QIODevice::ReadWrite));
|
|
|
|
testReadWrite(&k7zip);
|
|
testFileData(&k7zip);
|
|
|
|
QVERIFY(k7zip.close());
|
|
|
|
// Reopen it and check it
|
|
{
|
|
K7Zip k7zip(fileName);
|
|
QVERIFY(k7zip.open(QIODevice::ReadOnly));
|
|
testFileData(&k7zip);
|
|
const KArchiveDirectory *dir = k7zip.directory();
|
|
const KArchiveEntry *e = dir->entry(QStringLiteral("newfile"));
|
|
QVERIFY(e && e->isFile());
|
|
const KArchiveFile *f = (KArchiveFile *)e;
|
|
QCOMPARE(f->data().size(), 8);
|
|
}
|
|
|
|
// NOTE This is the last test for this dataset. so cleanup here
|
|
QFile::remove(fileName);
|
|
}
|
|
|
|
/**
|
|
* @dataProvider test7ZipMaxLength_data
|
|
*/
|
|
void KArchiveTest::test7ZipMaxLength()
|
|
{
|
|
QFETCH(QString, fileName);
|
|
|
|
K7Zip k7zip(fileName);
|
|
|
|
QVERIFY(k7zip.open(QIODevice::WriteOnly));
|
|
|
|
// Generate long filenames of each possible length bigger than 98...
|
|
for (int i = 98; i < 514; i++) {
|
|
QString str;
|
|
QString num;
|
|
str.fill('a', i - 10);
|
|
num.setNum(i);
|
|
num = num.rightJustified(10, '0');
|
|
k7zip.writeFile(str + num, "hum");
|
|
}
|
|
QVERIFY(k7zip.close());
|
|
|
|
QVERIFY(k7zip.open(QIODevice::ReadOnly));
|
|
|
|
const KArchiveDirectory *dir = k7zip.directory();
|
|
QVERIFY(dir != nullptr);
|
|
const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0);
|
|
|
|
QCOMPARE(listing[0],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3"));
|
|
QCOMPARE(
|
|
listing[3],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3"));
|
|
QCOMPARE(
|
|
listing[4],
|
|
QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3"));
|
|
|
|
QCOMPARE(listing.count(), 416);
|
|
|
|
QVERIFY(k7zip.close());
|
|
|
|
// NOTE Cleanup here
|
|
QFile::remove(fileName);
|
|
}
|
|
#endif
|
|
|
|
#include "moc_karchivetest.cpp"
|