File indexing completed on 2024-04-21 03:52:28
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2006, 2010 David Faure <faure@kde.org> 0003 SPDX-FileCopyrightText: 2012 Mario Bensi <mbensi@ipsquad.net> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "karchivetest.h" 0009 #include <k7zip.h> 0010 #include <kar.h> 0011 #include <krcc.h> 0012 #include <ktar.h> 0013 #include <kzip.h> 0014 0015 #include <QDebug> 0016 #include <QFileInfo> 0017 #include <QRegularExpression> 0018 #include <QSaveFile> 0019 #include <QStandardPaths> 0020 #include <QTemporaryDir> 0021 #include <QTest> 0022 #include <kcompressiondevice.h> 0023 0024 #ifndef Q_OS_WIN 0025 #include <cerrno> 0026 #include <unistd.h> // symlink 0027 #endif 0028 0029 #ifdef Q_OS_WIN 0030 #include <QScopedValueRollback> 0031 #include <Windows.h> 0032 #else 0033 #include <grp.h> 0034 #include <pwd.h> 0035 #endif 0036 0037 QTEST_MAIN(KArchiveTest) 0038 0039 static const int SIZE1 = 100; 0040 0041 /** 0042 * Writes test fileset specified archive 0043 * @param archive archive 0044 */ 0045 static void writeTestFilesToArchive(KArchive *archive) 0046 { 0047 QVERIFY(archive->writeFile("empty", "", 0100644, "weis", "users")); 0048 QVERIFY(archive->writeFile("test1", QByteArray("Hallo"), 0100440, QString("weis"), QString("users"))); 0049 // Now let's try with the prepareWriting/writeData/finishWriting API 0050 QVERIFY(archive->prepareWriting("test2", "weis", "users", 8)); 0051 QVERIFY(archive->writeData("Hallo ", 6)); 0052 QVERIFY(archive->writeData("Du", 2)); 0053 QVERIFY(archive->finishWriting(8)); 0054 // Add local file 0055 QFile localFile(QStringLiteral("test3")); 0056 QVERIFY(localFile.open(QIODevice::WriteOnly)); 0057 QVERIFY(localFile.write("Noch so einer", 13) == 13); 0058 localFile.close(); 0059 0060 QVERIFY(archive->addLocalFile("test3", "z/test3")); 0061 0062 // writeFile API 0063 QVERIFY(archive->writeFile("my/dir/test3", "I do not speak German\nDavid.", 0100644, "dfaure", "hackers")); 0064 0065 // Now a medium file : 100 null bytes 0066 char medium[SIZE1] = {0}; 0067 QVERIFY(archive->writeFile("mediumfile", QByteArray(medium, SIZE1))); 0068 // Another one, with an absolute path 0069 QVERIFY(archive->writeFile("/dir/subdir/mediumfile2", QByteArray(medium, SIZE1))); 0070 0071 // Now a huge file : 20000 null bytes 0072 int n = 20000; 0073 char *huge = new char[n]; 0074 memset(huge, 0, n); 0075 QVERIFY(archive->writeFile("hugefile", QByteArray(huge, n))); 0076 delete[] huge; 0077 0078 // Now an empty directory 0079 QVERIFY(archive->writeDir("aaaemptydir")); 0080 0081 #ifndef Q_OS_WIN 0082 // Add local symlink 0083 QVERIFY(archive->addLocalFile("test3_symlink", "z/test3_symlink")); 0084 #endif 0085 0086 // Add executable 0087 QVERIFY(archive->writeFile("executableAll", "#!/bin/sh\necho hi", 0100755)); 0088 } 0089 0090 static QString getCurrentUserName() 0091 { 0092 #if defined(Q_OS_UNIX) 0093 struct passwd *pw = getpwuid(getuid()); 0094 return pw ? QFile::decodeName(pw->pw_name) : QString::number(getuid()); 0095 #elif defined(Q_OS_WIN) 0096 wchar_t buffer[255]; 0097 DWORD size = 255; 0098 bool ok = GetUserNameW(buffer, &size); 0099 if (!ok) { 0100 return QString(); 0101 } 0102 return QString::fromWCharArray(buffer); 0103 #else 0104 return QString(); 0105 #endif 0106 } 0107 0108 static QString getCurrentGroupName() 0109 { 0110 #if defined(Q_OS_UNIX) 0111 struct group *grp = getgrgid(getgid()); 0112 return grp ? QFile::decodeName(grp->gr_name) : QString::number(getgid()); 0113 #elif defined(Q_OS_WIN) 0114 return QString(); 0115 #else 0116 return QString(); 0117 #endif 0118 } 0119 0120 enum ListingFlags { 0121 WithUserGroup = 1, 0122 WithTime = 0x02, 0123 }; // ListingFlags 0124 0125 static QStringList recursiveListEntries(const KArchiveDirectory *dir, const QString &path, int listingFlags) 0126 { 0127 QStringList ret; 0128 QStringList l = dir->entries(); 0129 l.sort(); 0130 for (const QString &it : std::as_const(l)) { 0131 const KArchiveEntry *entry = dir->entry(it); 0132 0133 QString descr; 0134 descr += QStringLiteral("mode=") + QString::number(entry->permissions(), 8) + ' '; 0135 if (listingFlags & WithUserGroup) { 0136 descr += QStringLiteral("user=") + entry->user() + ' '; 0137 descr += QStringLiteral("group=") + entry->group() + ' '; 0138 } 0139 descr += QStringLiteral("path=") + path + (it) + ' '; 0140 descr += QStringLiteral("type=") + (entry->isDirectory() ? "dir" : "file"); 0141 if (entry->isFile()) { 0142 descr += QStringLiteral(" size=") + QString::number(static_cast<const KArchiveFile *>(entry)->size()); 0143 } 0144 if (!entry->symLinkTarget().isEmpty()) { 0145 descr += QStringLiteral(" symlink=") + entry->symLinkTarget(); 0146 } 0147 0148 if (listingFlags & WithTime) { 0149 descr += QStringLiteral(" time=") + entry->date().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss")); 0150 } 0151 0152 // qDebug() << descr; 0153 ret.append(descr); 0154 0155 if (entry->isDirectory()) { 0156 ret += recursiveListEntries((KArchiveDirectory *)entry, path + it + '/', listingFlags); 0157 } 0158 } 0159 return ret; 0160 } 0161 0162 /** 0163 * Verifies contents of specified archive against test fileset 0164 * @param archive archive 0165 */ 0166 static void testFileData(KArchive *archive) 0167 { 0168 const KArchiveDirectory *dir = archive->directory(); 0169 0170 const KArchiveFile *f = dir->file(QStringLiteral("z/test3")); 0171 QVERIFY(f); 0172 0173 QByteArray arr(f->data()); 0174 QCOMPARE(arr.size(), 13); 0175 QCOMPARE(arr, QByteArray("Noch so einer")); 0176 0177 // Now test using createDevice() 0178 QIODevice *dev = f->createDevice(); 0179 QByteArray contents = dev->readAll(); 0180 QCOMPARE(contents, arr); 0181 delete dev; 0182 0183 dev = f->createDevice(); 0184 contents = dev->read(5); // test reading in two chunks 0185 QCOMPARE(contents.size(), 5); 0186 contents += dev->read(50); 0187 QCOMPARE(contents.size(), 13); 0188 QCOMPARE(QString::fromLatin1(contents.constData()), QString::fromLatin1(arr.constData())); 0189 delete dev; 0190 0191 // test read/seek/peek work fine 0192 f = dir->file(QStringLiteral("test1")); 0193 dev = f->createDevice(); 0194 contents = dev->peek(4); 0195 QCOMPARE(contents, QByteArray("Hall")); 0196 contents = dev->peek(2); 0197 QCOMPARE(contents, QByteArray("Ha")); 0198 dev->seek(2); 0199 contents = dev->peek(2); 0200 QCOMPARE(contents, QByteArray("ll")); 0201 dev->seek(0); 0202 contents = dev->read(2); 0203 QCOMPARE(contents, QByteArray("Ha")); 0204 contents = dev->peek(2); 0205 QCOMPARE(contents, QByteArray("ll")); 0206 dev->seek(1); 0207 contents = dev->read(1); 0208 QCOMPARE(contents, QByteArray("a")); 0209 dev->seek(4); 0210 contents = dev->read(1); 0211 QCOMPARE(contents, QByteArray("o")); 0212 delete dev; 0213 0214 const KArchiveEntry *e = dir->entry(QStringLiteral("mediumfile")); 0215 QVERIFY(e && e->isFile()); 0216 f = (KArchiveFile *)e; 0217 QCOMPARE(f->data().size(), SIZE1); 0218 0219 f = dir->file(QStringLiteral("hugefile")); 0220 QCOMPARE(f->data().size(), 20000); 0221 0222 e = dir->entry(QStringLiteral("aaaemptydir")); 0223 QVERIFY(e && e->isDirectory()); 0224 QVERIFY(!dir->file("aaaemptydir")); 0225 0226 e = dir->entry(QStringLiteral("my/dir/test3")); 0227 QVERIFY(e && e->isFile()); 0228 f = (KArchiveFile *)e; 0229 dev = f->createDevice(); 0230 QByteArray firstLine = dev->readLine(); 0231 QCOMPARE(QString::fromLatin1(firstLine.constData()), QString::fromLatin1("I do not speak German\n")); 0232 QByteArray secondLine = dev->read(100); 0233 QCOMPARE(QString::fromLatin1(secondLine.constData()), QString::fromLatin1("David.")); 0234 delete dev; 0235 #ifndef Q_OS_WIN 0236 e = dir->entry(QStringLiteral("z/test3_symlink")); 0237 QVERIFY(e); 0238 QVERIFY(e->isFile()); 0239 QCOMPARE(e->symLinkTarget(), QString("test3")); 0240 #endif 0241 0242 // Test "./" prefix for KOffice (xlink:href="./ObjectReplacements/Object 1") 0243 e = dir->entry(QStringLiteral("./hugefile")); 0244 QVERIFY(e && e->isFile()); 0245 e = dir->entry(QStringLiteral("./my/dir/test3")); 0246 QVERIFY(e && e->isFile()); 0247 0248 // Test directory entries 0249 e = dir->entry(QStringLiteral("my")); 0250 QVERIFY(e && e->isDirectory()); 0251 e = dir->entry(QStringLiteral("my/")); 0252 QVERIFY(e && e->isDirectory()); 0253 e = dir->entry(QStringLiteral("./my/")); 0254 QVERIFY(e && e->isDirectory()); 0255 } 0256 0257 static void testReadWrite(KArchive *archive) 0258 { 0259 QVERIFY(archive->writeFile("newfile", "New File", 0100440, "dfaure", "users")); 0260 } 0261 0262 #ifdef Q_OS_WIN 0263 extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; 0264 #endif 0265 0266 static void testCopyTo(KArchive *archive) 0267 { 0268 const KArchiveDirectory *dir = archive->directory(); 0269 QTemporaryDir tmpDir; 0270 const QString dirName = tmpDir.path() + '/'; 0271 0272 QVERIFY(dir->copyTo(dirName)); 0273 0274 QVERIFY(QFile::exists(dirName + "dir")); 0275 QVERIFY(QFileInfo(dirName + "dir").isDir()); 0276 0277 QFileInfo fileInfo1(dirName + "dir/subdir/mediumfile2"); 0278 QVERIFY(fileInfo1.exists()); 0279 QVERIFY(fileInfo1.isFile()); 0280 QCOMPARE(fileInfo1.size(), Q_INT64_C(100)); 0281 0282 QFileInfo fileInfo2(dirName + "hugefile"); 0283 QVERIFY(fileInfo2.exists()); 0284 QVERIFY(fileInfo2.isFile()); 0285 QCOMPARE(fileInfo2.size(), Q_INT64_C(20000)); 0286 0287 QFileInfo fileInfo3(dirName + "mediumfile"); 0288 QVERIFY(fileInfo3.exists()); 0289 QVERIFY(fileInfo3.isFile()); 0290 QCOMPARE(fileInfo3.size(), Q_INT64_C(100)); 0291 0292 QFileInfo fileInfo4(dirName + "my/dir/test3"); 0293 QVERIFY(fileInfo4.exists()); 0294 QVERIFY(fileInfo4.isFile()); 0295 QCOMPARE(fileInfo4.size(), Q_INT64_C(28)); 0296 0297 #ifndef Q_OS_WIN 0298 const QString fileName = dirName + "z/test3_symlink"; 0299 const QFileInfo fileInfo5(fileName); 0300 QVERIFY(fileInfo5.exists()); 0301 QVERIFY(fileInfo5.isFile()); 0302 // Do not use fileInfo.symLinkTarget() for unix symlinks 0303 // It returns the -full- path to the target, while we want the target string "as is". 0304 QString symLinkTarget; 0305 const QByteArray encodedFileName = QFile::encodeName(fileName); 0306 QByteArray s; 0307 #if defined(PATH_MAX) 0308 s.resize(PATH_MAX + 1); 0309 #else 0310 int path_max = pathconf(encodedFileName.data(), _PC_PATH_MAX); 0311 if (path_max <= 0) { 0312 path_max = 4096; 0313 } 0314 s.resize(path_max); 0315 #endif 0316 int len = readlink(encodedFileName.data(), s.data(), s.size() - 1); 0317 if (len >= 0) { 0318 s[len] = '\0'; 0319 symLinkTarget = QFile::decodeName(s.constData()); 0320 } 0321 QCOMPARE(symLinkTarget, QString("test3")); 0322 #endif 0323 0324 #ifdef Q_OS_WIN 0325 QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup); 0326 qt_ntfs_permission_lookup++; 0327 #endif 0328 QVERIFY(QFileInfo(dirName + "executableAll").permissions() & (QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther)); 0329 } 0330 0331 /** 0332 * Prepares dataset for archive filter tests 0333 */ 0334 void KArchiveTest::setupData() 0335 { 0336 QTest::addColumn<QString>("fileName"); 0337 QTest::addColumn<QString>("mimeType"); 0338 0339 QTest::newRow(".tar.gz") << "karchivetest.tar.gz" 0340 << "application/gzip"; 0341 #if HAVE_BZIP2_SUPPORT 0342 QTest::newRow(".tar.bz2") << "karchivetest.tar.bz2" 0343 << "application/x-bzip"; 0344 #endif 0345 #if HAVE_XZ_SUPPORT 0346 QTest::newRow(".tar.lzma") << "karchivetest.tar.lzma" 0347 << "application/x-lzma"; 0348 QTest::newRow(".tar.xz") << "karchivetest.tar.xz" 0349 << "application/x-xz"; 0350 #endif 0351 #if HAVE_ZSTD_SUPPORT 0352 QTest::newRow(".tar.zst") << "karchivetest.tar.zst" 0353 << "application/zstd"; 0354 #endif 0355 } 0356 0357 /** 0358 * @see QTest::initTestCase() 0359 */ 0360 void KArchiveTest::initTestCase() 0361 { 0362 #ifndef Q_OS_WIN 0363 // Prepare local symlink 0364 QFile::remove(QStringLiteral("test3_symlink")); 0365 if (::symlink("test3", "test3_symlink") != 0) { 0366 qDebug() << errno; 0367 QVERIFY(false); 0368 } 0369 #endif 0370 // avoid interference from kdebugsettings on qCWarning 0371 QStandardPaths::setTestModeEnabled(true); 0372 } 0373 0374 void KArchiveTest::testEmptyFilename() 0375 { 0376 QTest::ignoreMessage(QtWarningMsg, "KArchive: No file name specified"); 0377 KTar tar(QLatin1String("")); 0378 QVERIFY(!tar.open(QIODevice::ReadOnly)); 0379 QCOMPARE(tar.errorString(), tr("No filename or device was specified")); 0380 } 0381 0382 void KArchiveTest::testNullDevice() 0383 { 0384 QIODevice *nil = nullptr; 0385 QTest::ignoreMessage(QtWarningMsg, "KArchive: Null device specified"); 0386 KTar tar(nil); 0387 QVERIFY(!tar.open(QIODevice::ReadOnly)); 0388 QCOMPARE(tar.errorString(), tr("No filename or device was specified")); 0389 } 0390 0391 void KArchiveTest::testNonExistentFile() 0392 { 0393 KTar tar(QStringLiteral("nonexistent.tar.gz")); 0394 QVERIFY(!tar.open(QIODevice::ReadOnly)); 0395 QCOMPARE(tar.errorString(), tr("File %1 does not exist").arg("nonexistent.tar.gz")); 0396 } 0397 0398 void KArchiveTest::testCreateTar_data() 0399 { 0400 QTest::addColumn<QString>("fileName"); 0401 QTest::newRow(".tar") << "karchivetest.tar"; 0402 } 0403 0404 /** 0405 * @dataProvider testCreateTar_data 0406 */ 0407 void KArchiveTest::testCreateTar() 0408 { 0409 QFETCH(QString, fileName); 0410 0411 // With tempfile: 0.7-0.8 ms, 994236 instr. loads 0412 // Without tempfile: 0.81 ms, 992541 instr. loads 0413 // Note: use ./karchivetest 2>&1 | grep ms 0414 // to avoid being slowed down by the kDebugs. 0415 QBENCHMARK { 0416 KTar tar(fileName); 0417 QVERIFY(tar.open(QIODevice::WriteOnly)); 0418 0419 writeTestFilesToArchive(&tar); 0420 0421 QVERIFY(tar.close()); 0422 0423 QFileInfo fileInfo(QFile::encodeName(fileName)); 0424 QVERIFY(fileInfo.exists()); 0425 // We can't check for an exact size because of the addLocalFile, whose data is system-dependent 0426 QVERIFY(fileInfo.size() > 450); 0427 } 0428 0429 // NOTE The only .tar test, cleanup here 0430 // QFile::remove(fileName); 0431 } 0432 0433 /** 0434 * @dataProvider setupData 0435 */ 0436 void KArchiveTest::testCreateTarXXX() 0437 { 0438 QFETCH(QString, fileName); 0439 0440 // With tempfile: 1.3-1.7 ms, 2555089 instr. loads 0441 // Without tempfile: 0.87 ms, 987915 instr. loads 0442 QBENCHMARK { 0443 KTar tar(fileName); 0444 QVERIFY(tar.open(QIODevice::WriteOnly)); 0445 0446 writeTestFilesToArchive(&tar); 0447 0448 QVERIFY(tar.close()); 0449 0450 QFileInfo fileInfo(QFile::encodeName(fileName)); 0451 QVERIFY(fileInfo.exists()); 0452 // We can't check for an exact size because of the addLocalFile, whose data is system-dependent 0453 QVERIFY(fileInfo.size() > 350); 0454 } 0455 } 0456 0457 // static void compareEntryWithTimestamp(const QString &dateString, const QString &expectedDateString, const QDateTime &expectedDateTime) 0458 // Made it a macro so that line numbers are meaningful on failure 0459 0460 #define compareEntryWithTimestamp(dateString, expectedDateString, expectedDateTime) \ 0461 { \ 0462 /* Take the time from the listing and chop it off */ \ 0463 const QDateTime dt = QDateTime::fromString(dateString.right(19), "dd.MM.yyyy hh:mm:ss"); \ 0464 QString _str(dateString); \ 0465 _str.chop(25); \ 0466 QCOMPARE(_str, expectedDateString); \ 0467 \ 0468 /* Compare the times separately with allowed 2 sec diversion */ \ 0469 if (dt.secsTo(expectedDateTime) > 2) { \ 0470 qWarning() << dt << "is too different from" << expectedDateTime; \ 0471 } \ 0472 QVERIFY(dt.secsTo(expectedDateTime) <= 2); \ 0473 } 0474 0475 /** 0476 * @dataProvider setupData 0477 */ 0478 void KArchiveTest::testReadTar() // testCreateTarGz must have been run first. 0479 { 0480 QFETCH(QString, fileName); 0481 0482 QFileInfo localFileData(QStringLiteral("test3")); 0483 0484 const QString systemUserName = getCurrentUserName(); 0485 const QString systemGroupName = getCurrentGroupName(); 0486 const QString owner = localFileData.owner(); 0487 const QString group = localFileData.group(); 0488 const QString emptyTime = QDateTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss")); 0489 const QDateTime creationTime = QFileInfo(fileName).birthTime(); 0490 0491 // 1.6-1.7 ms per interaction, 2908428 instruction loads 0492 // After the "no tempfile when writing fix" this went down 0493 // to 0.9-1.0 ms, 1689059 instruction loads. 0494 // I guess it finds the data in the kernel cache now that no KTempFile is 0495 // used when writing. 0496 QBENCHMARK { 0497 KTar tar(fileName); 0498 0499 QVERIFY(tar.open(QIODevice::ReadOnly)); 0500 0501 const KArchiveDirectory *dir = tar.directory(); 0502 QVERIFY(dir != nullptr); 0503 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup | WithTime); 0504 0505 #ifndef Q_OS_WIN 0506 const int expectedCount = 16; 0507 #else 0508 const int expectedCount = 15; 0509 #endif 0510 if (listing.count() != expectedCount) { 0511 qWarning() << listing; 0512 } 0513 QCOMPARE(listing.count(), expectedCount); 0514 compareEntryWithTimestamp(listing[0], QString("mode=40755 user= group= path=aaaemptydir type=dir"), creationTime); 0515 0516 QCOMPARE(listing[1], QString("mode=40777 user=%1 group=%2 path=dir type=dir time=%3").arg(systemUserName).arg(systemGroupName).arg(emptyTime)); 0517 QCOMPARE(listing[2], QString("mode=40777 user=%1 group=%2 path=dir/subdir type=dir time=%3").arg(systemUserName).arg(systemGroupName).arg(emptyTime)); 0518 compareEntryWithTimestamp(listing[3], QString("mode=100644 user= group= path=dir/subdir/mediumfile2 type=file size=100"), creationTime); 0519 compareEntryWithTimestamp(listing[4], QString("mode=100644 user=weis group=users path=empty type=file size=0"), creationTime); 0520 compareEntryWithTimestamp(listing[5], QString("mode=100755 user= group= path=executableAll type=file size=17"), creationTime); 0521 compareEntryWithTimestamp(listing[6], QString("mode=100644 user= group= path=hugefile type=file size=20000"), creationTime); 0522 compareEntryWithTimestamp(listing[7], QString("mode=100644 user= group= path=mediumfile type=file size=100"), creationTime); 0523 QCOMPARE(listing[8], QString("mode=40777 user=%1 group=%2 path=my type=dir time=").arg(systemUserName).arg(systemGroupName)); 0524 QCOMPARE(listing[9], QString("mode=40777 user=%1 group=%2 path=my/dir type=dir time=").arg(systemUserName).arg(systemGroupName)); 0525 compareEntryWithTimestamp(listing[10], QString("mode=100644 user=dfaure group=hackers path=my/dir/test3 type=file size=28"), creationTime); 0526 compareEntryWithTimestamp(listing[11], QString("mode=100440 user=weis group=users path=test1 type=file size=5"), creationTime); 0527 compareEntryWithTimestamp(listing[12], QString("mode=100644 user=weis group=users path=test2 type=file size=8"), creationTime); 0528 QCOMPARE(listing[13], QString("mode=40777 user=%1 group=%2 path=z type=dir time=").arg(systemUserName).arg(systemGroupName)); 0529 0530 // This one was added with addLocalFile, so ignore mode. 0531 QString str = listing[14]; 0532 str.replace(QRegularExpression(QStringLiteral("mode.*user=")), QStringLiteral("user=")); 0533 0534 compareEntryWithTimestamp(str, QString("user=%1 group=%2 path=z/test3 type=file size=13").arg(owner).arg(group), creationTime); 0535 0536 #ifndef Q_OS_WIN 0537 str = listing[15]; 0538 str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path=")); 0539 0540 compareEntryWithTimestamp(str, QString("path=z/test3_symlink type=file size=0 symlink=test3"), creationTime); 0541 #endif 0542 0543 QVERIFY(tar.close()); 0544 } 0545 } 0546 0547 /** 0548 * This tests the decompression using kfilterdev, basically. 0549 * To debug KTarPrivate::fillTempFile(). 0550 * 0551 * @dataProvider setupData 0552 */ 0553 void KArchiveTest::testUncompress() 0554 { 0555 QFETCH(QString, fileName); 0556 QFETCH(QString, mimeType); 0557 0558 // testCreateTar must have been run first. 0559 QVERIFY(QFile::exists(fileName)); 0560 KCompressionDevice filterDev(fileName); 0561 QByteArray buffer; 0562 buffer.resize(8 * 1024); 0563 // qDebug() << "buffer.size()=" << buffer.size(); 0564 QVERIFY(filterDev.open(QIODevice::ReadOnly)); 0565 0566 qint64 totalSize = 0; 0567 qint64 len = -1; 0568 while (!filterDev.atEnd() && len != 0) { 0569 len = filterDev.read(buffer.data(), buffer.size()); 0570 QVERIFY(len >= 0); 0571 totalSize += len; 0572 // qDebug() << "read len=" << len << " totalSize=" << totalSize; 0573 } 0574 filterDev.close(); 0575 // qDebug() << "totalSize=" << totalSize; 0576 QVERIFY(totalSize > 26000); // 27648 here when using gunzip 0577 } 0578 0579 /** 0580 * @dataProvider setupData 0581 */ 0582 void KArchiveTest::testTarFileData() 0583 { 0584 QFETCH(QString, fileName); 0585 0586 // testCreateTar must have been run first. 0587 KTar tar(fileName); 0588 QVERIFY(tar.open(QIODevice::ReadOnly)); 0589 0590 testFileData(&tar); 0591 0592 QVERIFY(tar.close()); 0593 } 0594 0595 /** 0596 * @dataProvider setupData 0597 */ 0598 void KArchiveTest::testTarCopyTo() 0599 { 0600 QFETCH(QString, fileName); 0601 0602 // testCreateTar must have been run first. 0603 KTar tar(fileName); 0604 QVERIFY(tar.open(QIODevice::ReadOnly)); 0605 0606 testCopyTo(&tar); 0607 0608 QVERIFY(tar.close()); 0609 } 0610 0611 /** 0612 * @dataProvider setupData 0613 */ 0614 void KArchiveTest::testTarReadWrite() 0615 { 0616 QFETCH(QString, fileName); 0617 0618 // testCreateTar must have been run first. 0619 KTar tar(fileName); 0620 QVERIFY(tar.open(QIODevice::ReadWrite)); 0621 0622 testReadWrite(&tar); 0623 testFileData(&tar); 0624 0625 QVERIFY(tar.close()); 0626 0627 // Reopen it and check it 0628 { 0629 KTar tar(fileName); 0630 QVERIFY(tar.open(QIODevice::ReadOnly)); 0631 testFileData(&tar); 0632 const KArchiveDirectory *dir = tar.directory(); 0633 const KArchiveEntry *e = dir->entry(QStringLiteral("newfile")); 0634 QVERIFY(e && e->isFile()); 0635 const KArchiveFile *f = (KArchiveFile *)e; 0636 QCOMPARE(f->data().size(), 8); 0637 } 0638 0639 // NOTE This is the last test for this dataset. so cleanup here 0640 QFile::remove(fileName); 0641 } 0642 0643 void KArchiveTest::testTarMaxLength_data() 0644 { 0645 QTest::addColumn<QString>("fileName"); 0646 QTest::newRow("maxlength.tar.gz") << "karchivetest-maxlength.tar.gz"; 0647 } 0648 0649 /** 0650 * @dataProvider testTarMaxLength_data 0651 */ 0652 void KArchiveTest::testTarMaxLength() 0653 { 0654 QFETCH(QString, fileName); 0655 0656 KTar tar(fileName); 0657 0658 QVERIFY(tar.open(QIODevice::WriteOnly)); 0659 0660 // Generate long filenames of each possible length bigger than 98... 0661 // Also exceed 512 byte block size limit to see how well the ././@LongLink 0662 // implementation fares 0663 for (int i = 98; i < 514; i++) { 0664 QString str; 0665 QString num; 0666 str.fill('a', i - 10); 0667 num.setNum(i); 0668 num = num.rightJustified(10, '0'); 0669 tar.writeFile(str + num, "hum"); 0670 } 0671 // Result of this test : works perfectly now (failed at 482 formerly and 0672 // before that at 154). 0673 QVERIFY(tar.close()); 0674 0675 QVERIFY(tar.open(QIODevice::ReadOnly)); 0676 0677 const KArchiveDirectory *dir = tar.directory(); 0678 QVERIFY(dir != nullptr); 0679 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0680 0681 QCOMPARE(listing[0], 0682 QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 " 0683 "type=file size=3")); 0684 QCOMPARE(listing[3], 0685 QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 " 0686 "type=file size=3")); 0687 QCOMPARE(listing[4], 0688 QString("mode=100644 user= group= path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 " 0689 "type=file size=3")); 0690 0691 QCOMPARE(listing.count(), 416); 0692 0693 QVERIFY(tar.close()); 0694 0695 // NOTE Cleanup here 0696 QFile::remove(fileName); 0697 } 0698 0699 void KArchiveTest::testTarGlobalHeader() 0700 { 0701 KTar tar(QFINDTESTDATA(QLatin1String("global_header_test.tar.gz"))); 0702 QVERIFY2(tar.open(QIODevice::ReadOnly), "global_header_test.tar.gz"); 0703 0704 const KArchiveDirectory *dir = tar.directory(); 0705 QVERIFY(dir != nullptr); 0706 0707 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0708 0709 QCOMPARE(listing.count(), 2); 0710 0711 QCOMPARE(listing[0], QString("mode=40775 user=root group=root path=Test type=dir")); 0712 QCOMPARE(listing[1], QString("mode=664 user=root group=root path=Test/test.txt type=file size=0")); 0713 0714 QVERIFY(tar.close()); 0715 } 0716 0717 void KArchiveTest::testTarPrefix() 0718 { 0719 KTar tar(QFINDTESTDATA(QLatin1String("tar_prefix_test.tar.gz"))); 0720 QVERIFY2(tar.open(QIODevice::ReadOnly), "tar_prefix_test.tar.gz"); 0721 0722 const KArchiveDirectory *dir = tar.directory(); 0723 QVERIFY(dir != nullptr); 0724 0725 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0726 0727 QCOMPARE(listing[0], QString("mode=40775 user=root group=root path=Test type=dir")); 0728 QCOMPARE(listing[1], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7 type=dir")); 0729 QCOMPARE(listing[2], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples type=dir")); 0730 QCOMPARE(listing[3], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator type=dir")); 0731 QCOMPARE(listing[4], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original type=dir")); 0732 QCOMPARE(listing[5], QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java type=dir")); 0733 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")); 0734 QCOMPARE(listing[7], 0735 QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech type=dir")); 0736 QCOMPARE( 0737 listing[8], 0738 QString("mode=40775 user=root group=root path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples type=dir")); 0739 QCOMPARE( 0740 listing[9], 0741 QString("mode=664 user=root group=root " 0742 "path=Test/qt-jambi-qtjambi-4_7/examples/generator/trolltech_original/java/com/trolltech/examples/GeneratorExample.html type=file size=43086")); 0743 0744 QCOMPARE(listing.count(), 10); 0745 0746 QVERIFY(tar.close()); 0747 } 0748 0749 void KArchiveTest::testTarDirectoryForgotten() 0750 { 0751 KTar tar(QFINDTESTDATA(QLatin1String("tar_directory_forgotten.tar.gz"))); 0752 QVERIFY2(tar.open(QIODevice::ReadOnly), "tar_directory_forgotten.tar.gz"); 0753 0754 const KArchiveDirectory *dir = tar.directory(); 0755 QVERIFY(dir != nullptr); 0756 0757 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0758 0759 QVERIFY(listing[9].contains("trolltech/examples/generator")); 0760 QVERIFY(listing[10].contains("trolltech/examples/generator/GeneratorExample.html")); 0761 0762 QCOMPARE(listing.count(), 11); 0763 0764 QVERIFY(tar.close()); 0765 } 0766 0767 void KArchiveTest::testTarEmptyFileMissingDir() 0768 { 0769 KTar tar(QFINDTESTDATA(QLatin1String("tar_emptyfile_missingdir.tar.gz"))); 0770 QVERIFY(tar.open(QIODevice::ReadOnly)); 0771 0772 const KArchiveDirectory *dir = tar.directory(); 0773 QVERIFY(dir != nullptr); 0774 0775 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 0776 0777 QCOMPARE(listing[0], QString("mode=40777 path=dir type=dir")); 0778 QCOMPARE(listing[1], QString("mode=40777 path=dir/foo type=dir")); 0779 QCOMPARE(listing[2], QString("mode=644 path=dir/foo/file type=file size=0")); 0780 QCOMPARE(listing.count(), 3); 0781 0782 QVERIFY(tar.close()); 0783 } 0784 0785 void KArchiveTest::testTarRootDir() // bug 309463 0786 { 0787 KTar tar(QFINDTESTDATA(QLatin1String("tar_rootdir.tar.gz"))); 0788 QVERIFY2(tar.open(QIODevice::ReadOnly), qPrintable(tar.fileName())); 0789 0790 const KArchiveDirectory *dir = tar.directory(); 0791 QVERIFY(dir != nullptr); 0792 0793 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0794 // qDebug() << listing.join("\n"); 0795 0796 QVERIFY(listing[0].contains("%{APPNAME}.cpp")); 0797 QVERIFY(listing[1].contains("%{APPNAME}.h")); 0798 QVERIFY(listing[5].contains("main.cpp")); 0799 0800 QCOMPARE(listing.count(), 10); 0801 } 0802 0803 void KArchiveTest::testTarLongNonASCIINames() // bug 266141 0804 { 0805 const QString tarName = QString("karchive-long-non-ascii-names.tar"); 0806 const QString longName = QString::fromUtf8("раз-два-три-четыре-пять-вышел-зайчик-погулять-вдруг-охотник-выбегает-прямо-в-зайчика.txt"); 0807 0808 { 0809 KTar tar(tarName); 0810 QVERIFY(tar.open(QIODevice::WriteOnly)); 0811 QVERIFY(tar.writeFile(longName, "", 0644, "user", "users")); 0812 QVERIFY(tar.close()); 0813 } 0814 0815 { 0816 KTar tar(tarName); 0817 0818 QVERIFY(tar.open(QIODevice::ReadOnly)); 0819 const KArchiveDirectory *dir = tar.directory(); 0820 QVERIFY(dir != nullptr); 0821 0822 const QStringList listing = recursiveListEntries(dir, QString(""), 0); 0823 0824 const QString expectedListingEntry = QString("mode=644 path=") + longName + QString(" type=file size=0"); 0825 0826 QCOMPARE(listing.count(), 1); 0827 0828 QCOMPARE(listing[0], expectedListingEntry); 0829 QVERIFY(tar.close()); 0830 } 0831 } 0832 0833 void KArchiveTest::testTarShortNonASCIINames() // bug 266141 0834 { 0835 KTar tar(QFINDTESTDATA(QString("tar_non_ascii_file_name.tar.gz"))); 0836 0837 QVERIFY(tar.open(QIODevice::ReadOnly)); 0838 const KArchiveDirectory *dir = tar.directory(); 0839 QVERIFY(dir != nullptr); 0840 0841 const QStringList listing = recursiveListEntries(dir, QString(""), 0); 0842 0843 QCOMPARE(listing.count(), 1); 0844 QCOMPARE(listing[0], 0845 QString("mode=644 path=абвгдеёжзийклмнопрстуфхцчшщъыьэюя.txt" 0846 " type=file size=0")); 0847 QVERIFY(tar.close()); 0848 } 0849 0850 void KArchiveTest::testTarDirectoryTwice() // bug 206994 0851 { 0852 KTar tar(QFINDTESTDATA(QLatin1String("tar_directory_twice.tar.gz"))); 0853 QVERIFY(tar.open(QIODevice::ReadOnly)); 0854 0855 const KArchiveDirectory *dir = tar.directory(); 0856 QVERIFY(dir != nullptr); 0857 0858 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), WithUserGroup); 0859 // qDebug() << listing.join("\n"); 0860 0861 QVERIFY(listing[0].contains("path=d")); 0862 QVERIFY(listing[1].contains("path=d/f1.txt")); 0863 QVERIFY(listing[2].contains("path=d/f2.txt")); 0864 0865 QCOMPARE(listing.count(), 3); 0866 } 0867 0868 void KArchiveTest::testTarIgnoreRelativePathOutsideArchive() 0869 { 0870 #if HAVE_BZIP2_SUPPORT 0871 // This test extracts a Tar archive that contains a relative path "../foo" pointing 0872 // outside of the archive directory. For security reasons extractions should only 0873 // be allowed within the extracted directory as long as not specifically asked. 0874 0875 KTar tar(QFINDTESTDATA(QLatin1String("tar_relative_path_outside_archive.tar.bz2"))); 0876 QVERIFY(tar.open(QIODevice::ReadOnly)); 0877 0878 const KArchiveDirectory *dir = tar.directory(); 0879 QTemporaryDir tmpDir; 0880 const QString dirName = tmpDir.path() + "/subdir"; // use a subdir so /tmp/foo doesn't break the test :) 0881 QDir().mkdir(dirName); 0882 0883 QVERIFY(dir->copyTo(dirName)); 0884 QVERIFY(!QFile::exists(dirName + "../foo")); 0885 QVERIFY(QFile::exists(dirName + "/foo")); 0886 #else 0887 QSKIP("Test data is in bz2 format and karchive is built without bzip2 format"); 0888 #endif 0889 } 0890 /// 0891 0892 static const char s_zipFileName[] = "karchivetest.zip"; 0893 static const char s_zipMaxLengthFileName[] = "karchivetest-maxlength.zip"; 0894 static const char s_zipLocaleFileName[] = "karchivetest-locale.zip"; 0895 static const char s_zipMimeType[] = "application/vnd.oasis.opendocument.text"; 0896 0897 void KArchiveTest::testCreateZip() 0898 { 0899 KZip zip(s_zipFileName); 0900 0901 QVERIFY(zip.open(QIODevice::WriteOnly)); 0902 0903 zip.setExtraField(KZip::NoExtraField); 0904 0905 zip.setCompression(KZip::NoCompression); 0906 QByteArray zipMimeType(s_zipMimeType); 0907 zip.writeFile(QStringLiteral("mimetype"), zipMimeType); 0908 zip.setCompression(KZip::DeflateCompression); 0909 0910 writeTestFilesToArchive(&zip); 0911 0912 QVERIFY(zip.close()); 0913 0914 QFile zipFile(QFile::encodeName(s_zipFileName)); 0915 QFileInfo fileInfo(zipFile); 0916 QVERIFY(fileInfo.exists()); 0917 QVERIFY(fileInfo.size() > 300); 0918 0919 // Check that the header with no-compression and no-extrafield worked. 0920 // (This is for the "magic" for koffice documents) 0921 QVERIFY(zipFile.open(QIODevice::ReadOnly)); 0922 QByteArray arr = zipFile.read(4); 0923 QCOMPARE(arr, QByteArray("PK\003\004")); 0924 QVERIFY(zipFile.seek(30)); 0925 arr = zipFile.read(8); 0926 QCOMPARE(arr, QByteArray("mimetype")); 0927 arr = zipFile.read(zipMimeType.size()); 0928 QCOMPARE(arr, zipMimeType); 0929 } 0930 0931 void KArchiveTest::testCreateZipError() 0932 { 0933 // Giving a directory name to kzip must give an error case in close(), see #136630. 0934 // Otherwise we just lose data. 0935 KZip zip(QDir::currentPath()); 0936 0937 QVERIFY(!zip.open(QIODevice::WriteOnly)); 0938 QCOMPARE(zip.errorString(), tr("QSaveFile creation for %1 failed: Filename refers to a directory").arg(QDir::currentPath())); 0939 } 0940 0941 void KArchiveTest::testReadZipError() 0942 { 0943 QFile brokenZip(QStringLiteral("broken.zip")); 0944 QVERIFY(brokenZip.open(QIODevice::WriteOnly)); 0945 0946 // incomplete magic 0947 brokenZip.write(QByteArray("PK\003")); 0948 0949 brokenZip.close(); 0950 { 0951 KZip zip(QStringLiteral("broken.zip")); 0952 0953 QVERIFY(!zip.open(QIODevice::ReadOnly)); 0954 QCOMPARE(zip.errorString(), tr("Invalid ZIP file. Unexpected end of file. (Error code: %1)").arg(1)); 0955 0956 QVERIFY(brokenZip.open(QIODevice::WriteOnly | QIODevice::Append)); 0957 0958 // add rest of magic, but still incomplete header 0959 brokenZip.write(QByteArray("\004\000\000\000\000")); 0960 0961 brokenZip.close(); 0962 0963 QVERIFY(!zip.open(QIODevice::ReadOnly)); 0964 QCOMPARE(zip.errorString(), tr("Invalid ZIP file. Unexpected end of file. (Error code: %1)").arg(4)); 0965 } 0966 0967 QVERIFY(brokenZip.remove()); 0968 } 0969 0970 void KArchiveTest::testReadZip() 0971 { 0972 // testCreateZip must have been run first. 0973 KZip zip(s_zipFileName); 0974 0975 QVERIFY(zip.open(QIODevice::ReadOnly)); 0976 0977 const KArchiveDirectory *dir = zip.directory(); 0978 QVERIFY(dir != nullptr); 0979 0980 // ZIP has no support for per-file user/group, so omit them from the listing 0981 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 0982 0983 #ifndef Q_OS_WIN 0984 QCOMPARE(listing.count(), 17); 0985 #else 0986 QCOMPARE(listing.count(), 16); 0987 #endif 0988 QCOMPARE(listing[0], QString("mode=40755 path=aaaemptydir type=dir")); 0989 QCOMPARE(listing[1], QString("mode=40777 path=dir type=dir")); 0990 QCOMPARE(listing[2], QString("mode=40777 path=dir/subdir type=dir")); 0991 QCOMPARE(listing[3], QString("mode=100644 path=dir/subdir/mediumfile2 type=file size=100")); 0992 QCOMPARE(listing[4], QString("mode=100644 path=empty type=file size=0")); 0993 QCOMPARE(listing[5], QString("mode=100755 path=executableAll type=file size=17")); 0994 QCOMPARE(listing[6], QString("mode=100644 path=hugefile type=file size=20000")); 0995 QCOMPARE(listing[7], QString("mode=100644 path=mediumfile type=file size=100")); 0996 QCOMPARE(listing[8], QString("mode=100644 path=mimetype type=file size=%1").arg(strlen(s_zipMimeType))); 0997 QCOMPARE(listing[9], QString("mode=40777 path=my type=dir")); 0998 QCOMPARE(listing[10], QString("mode=40777 path=my/dir type=dir")); 0999 QCOMPARE(listing[11], QString("mode=100644 path=my/dir/test3 type=file size=28")); 1000 QCOMPARE(listing[12], QString("mode=100440 path=test1 type=file size=5")); 1001 QCOMPARE(listing[13], QString("mode=100644 path=test2 type=file size=8")); 1002 QCOMPARE(listing[14], QString("mode=40777 path=z type=dir")); 1003 // This one was added with addLocalFile, so ignore mode 1004 QString str = listing[15]; 1005 str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path=")); 1006 QCOMPARE(str, QString("path=z/test3 type=file size=13")); 1007 #ifndef Q_OS_WIN 1008 str = listing[16]; 1009 str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path=")); 1010 QCOMPARE(str, QString("path=z/test3_symlink type=file size=5 symlink=test3")); 1011 #endif 1012 1013 QVERIFY(zip.close()); 1014 } 1015 1016 void KArchiveTest::testZipFileData() 1017 { 1018 // testCreateZip must have been run first. 1019 KZip zip(s_zipFileName); 1020 QVERIFY(zip.open(QIODevice::ReadOnly)); 1021 1022 testFileData(&zip); 1023 1024 QVERIFY(zip.close()); 1025 } 1026 1027 void KArchiveTest::testZipCopyTo() 1028 { 1029 // testCreateZip must have been run first. 1030 KZip zip(s_zipFileName); 1031 QVERIFY(zip.open(QIODevice::ReadOnly)); 1032 1033 testCopyTo(&zip); 1034 1035 QVERIFY(zip.close()); 1036 } 1037 1038 void KArchiveTest::testZipMaxLength() 1039 { 1040 KZip zip(s_zipMaxLengthFileName); 1041 1042 QVERIFY(zip.open(QIODevice::WriteOnly)); 1043 1044 // Similar to testTarMaxLength just to make sure, but of course zip doesn't have 1045 // those limitations in the first place. 1046 for (int i = 98; i < 514; i++) { 1047 QString str; 1048 QString num; 1049 str.fill('a', i - 10); 1050 num.setNum(i); 1051 num = num.rightJustified(10, '0'); 1052 zip.writeFile(str + num, "hum"); 1053 } 1054 QVERIFY(zip.close()); 1055 1056 QVERIFY(zip.open(QIODevice::ReadOnly)); 1057 1058 const KArchiveDirectory *dir = zip.directory(); 1059 QVERIFY(dir != nullptr); 1060 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1061 1062 QCOMPARE(listing[0], 1063 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3")); 1064 QCOMPARE( 1065 listing[3], 1066 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3")); 1067 QCOMPARE( 1068 listing[4], 1069 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3")); 1070 1071 QCOMPARE(listing.count(), 514 - 98); 1072 1073 QVERIFY(zip.close()); 1074 } 1075 1076 void KArchiveTest::testZipWithNonLatinFileNames() 1077 { 1078 KZip zip(s_zipLocaleFileName); 1079 1080 QVERIFY(zip.open(QIODevice::WriteOnly)); 1081 1082 const QByteArray fileData("Test of data with a russian file name"); 1083 const QString fileName = QString::fromUtf8("Архитектура.okular"); 1084 const QString recodedFileName = QFile::decodeName(QFile::encodeName(fileName)); 1085 QVERIFY(zip.writeFile(fileName, fileData)); 1086 1087 QVERIFY(zip.close()); 1088 1089 QVERIFY(zip.open(QIODevice::ReadOnly)); 1090 1091 const KArchiveDirectory *dir = zip.directory(); 1092 QVERIFY(dir != nullptr); 1093 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1094 1095 QCOMPARE(listing.count(), 1); 1096 QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(recodedFileName).arg(fileData.size())); 1097 1098 const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0])); 1099 QCOMPARE(fileEntry->data(), fileData); 1100 } 1101 1102 void KArchiveTest::testZipWithOverwrittenFileName() 1103 { 1104 KZip zip(s_zipFileName); 1105 1106 QVERIFY(zip.open(QIODevice::WriteOnly)); 1107 1108 const QByteArray fileData1("There could be a fire, if there is smoke."); 1109 const QString fileName = QStringLiteral("wisdom"); 1110 QVERIFY(zip.writeFile(fileName, fileData1, 0100644, "konqi", "dragons")); 1111 1112 // now overwrite it 1113 const QByteArray fileData2("If there is smoke, there could be a fire."); 1114 QVERIFY(zip.writeFile(fileName, fileData2, 0100644, "konqi", "dragons")); 1115 1116 QVERIFY(zip.close()); 1117 1118 QVERIFY(zip.open(QIODevice::ReadOnly)); 1119 1120 const KArchiveDirectory *dir = zip.directory(); 1121 QVERIFY(dir != nullptr); 1122 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1123 1124 QCOMPARE(listing.count(), 1); 1125 QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=%1 type=file size=%2").arg(fileName).arg(fileData2.size())); 1126 1127 const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0])); 1128 QCOMPARE(fileEntry->data(), fileData2); 1129 } 1130 1131 static bool writeFile(const QString &dirName, const QString &fileName, const QByteArray &data) 1132 { 1133 Q_ASSERT(dirName.endsWith('/')); 1134 QFile file(dirName + fileName); 1135 if (!file.open(QIODevice::WriteOnly)) { 1136 return false; 1137 } 1138 file.write(data); 1139 return true; 1140 } 1141 1142 void KArchiveTest::testZipAddLocalDirectory() 1143 { 1144 // Prepare local dir 1145 QTemporaryDir tmpDir; 1146 const QString dirName = tmpDir.path() + '/'; 1147 1148 const QByteArray file1Data = "Hello Shantanu"; 1149 const QString file1 = QStringLiteral("file1"); 1150 QVERIFY(writeFile(dirName, file1, file1Data)); 1151 1152 const QString emptyDir = QStringLiteral("emptyDir"); 1153 QDir(dirName).mkdir(emptyDir); 1154 1155 { 1156 KZip zip(s_zipFileName); 1157 1158 QVERIFY(zip.open(QIODevice::WriteOnly)); 1159 QVERIFY(zip.addLocalDirectory(dirName, ".")); 1160 QVERIFY(zip.close()); 1161 } 1162 { 1163 KZip zip(s_zipFileName); 1164 1165 QVERIFY(zip.open(QIODevice::ReadOnly)); 1166 1167 const KArchiveDirectory *dir = zip.directory(); 1168 QVERIFY(dir != nullptr); 1169 1170 const KArchiveEntry *e = dir->entry(file1); 1171 QVERIFY(e && e->isFile()); 1172 const KArchiveFile *f = static_cast<const KArchiveFile *>(e); 1173 QCOMPARE(f->data(), file1Data); 1174 1175 const KArchiveEntry *empty = dir->entry(emptyDir); 1176 QVERIFY(empty && empty->isDirectory()); 1177 } 1178 } 1179 1180 void KArchiveTest::testZipReadRedundantDataDescriptor_data() 1181 { 1182 QTest::addColumn<QString>("fileName"); 1183 QTest::newRow("noSignature") << "data/redundantDataDescriptorsNoSignature.zip"; 1184 QTest::newRow("withSignature") << "data/redundantDataDescriptorsWithSignature.zip"; 1185 } 1186 1187 /** 1188 * @dataProvider testZipReadRedundantDataDescriptor_data 1189 */ 1190 void KArchiveTest::testZipReadRedundantDataDescriptor() 1191 { 1192 QFETCH(QString, fileName); 1193 1194 const QString redundantDataDescriptorZipFileName = QFINDTESTDATA(fileName); 1195 QVERIFY(!redundantDataDescriptorZipFileName.isEmpty()); 1196 1197 KZip zip(redundantDataDescriptorZipFileName); 1198 1199 QVERIFY(zip.open(QIODevice::ReadOnly)); 1200 1201 const KArchiveDirectory *dir = zip.directory(); 1202 QVERIFY(dir != nullptr); 1203 1204 const QByteArray fileData("aaaaaaaaaaaaaaa"); 1205 1206 // ZIP has no support for per-file user/group, so omit them from the listing 1207 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1208 1209 QCOMPARE(listing.count(), 2); 1210 QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=compressed type=file size=%2").arg(fileData.size())); 1211 QCOMPARE(listing[1], QString::fromUtf8("mode=100644 path=uncompressed type=file size=%2").arg(fileData.size())); 1212 1213 const KArchiveFile *fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[0])); 1214 QCOMPARE(fileEntry->data(), fileData); 1215 fileEntry = static_cast<const KArchiveFile *>(dir->entry(dir->entries()[1])); 1216 QCOMPARE(fileEntry->data(), fileData); 1217 } 1218 1219 void KArchiveTest::testZipDirectoryPermissions() 1220 { 1221 QString fileName = QFINDTESTDATA("data/dirpermissions.zip"); 1222 QVERIFY(!fileName.isEmpty()); 1223 1224 KZip zip(fileName); 1225 1226 QVERIFY(zip.open(QIODevice::ReadOnly)); 1227 const KArchiveDirectory *dir = zip.directory(); 1228 const QStringList listing = recursiveListEntries(dir, QString(), 0); 1229 QCOMPARE(listing.join(' '), QString::fromUtf8("mode=40700 path=700 type=dir mode=40750 path=750 type=dir mode=40755 path=755 type=dir")); 1230 } 1231 1232 void KArchiveTest::testZipWithinZip() 1233 { 1234 QString fileName = QFINDTESTDATA("data/zip_within_zip.zip"); 1235 QVERIFY(!fileName.isEmpty()); 1236 1237 KZip zip(fileName); 1238 1239 QVERIFY(zip.open(QIODevice::ReadOnly)); 1240 const KArchiveDirectory *dir = zip.directory(); 1241 const QStringList listing = recursiveListEntries(dir, QString(), 0); 1242 QCOMPARE(listing.count(), 2); 1243 QCOMPARE(listing[0], QString::fromUtf8("mode=100644 path=foo type=file size=7")); 1244 QCOMPARE(listing[1], QString::fromUtf8("mode=100644 path=test.epub type=file size=525154")); 1245 } 1246 1247 void KArchiveTest::testZipUnusualButValid() 1248 { 1249 QString fileName = QFINDTESTDATA("data/unusual_but_valid_364071.zip"); 1250 QVERIFY(!fileName.isEmpty()); 1251 1252 KZip zip(fileName); 1253 1254 QVERIFY(zip.open(QIODevice::ReadOnly)); 1255 const KArchiveDirectory *dir = zip.directory(); 1256 const QStringList listing = recursiveListEntries(dir, QString(), 0); 1257 QCOMPARE(listing.join(' '), QLatin1String("mode=40744 path=test type=dir mode=744 path=test/os-release type=file size=199")); 1258 } 1259 1260 void KArchiveTest::testZipDuplicateNames() 1261 { 1262 QString fileName = QFINDTESTDATA("data/out.epub"); 1263 QVERIFY(!fileName.isEmpty()); 1264 1265 KZip zip(fileName); 1266 1267 QVERIFY(zip.open(QIODevice::ReadOnly)); 1268 1269 int metaInfCount = 0; 1270 for (const QString &entryName : zip.directory()->entries()) { 1271 if (entryName.startsWith("META-INF")) { 1272 metaInfCount++; 1273 } 1274 } 1275 1276 QVERIFY2(metaInfCount == 1, "Archive root directory contains duplicates"); 1277 } 1278 1279 void KArchiveTest::testZip64() 1280 { 1281 QString fileName = QFINDTESTDATA("data/zip64.pkpass"); 1282 QVERIFY(!fileName.isEmpty()); 1283 1284 KZip zip(fileName); 1285 QVERIFY(zip.open(QIODevice::ReadOnly)); 1286 1287 // NOTE the actual content of this file has been wiped, we can only verify the file meta data here 1288 QCOMPARE(zip.directory()->entries().size(), 7); 1289 auto entry = static_cast<const KZipFileEntry *>(zip.directory()->entry(QStringLiteral("manifest.json"))); 1290 QVERIFY(entry); 1291 QCOMPARE(entry->size(), 305); 1292 QCOMPARE(entry->compressedSize(), 194); 1293 QCOMPARE(entry->position(), 21417); 1294 entry = static_cast<const KZipFileEntry *>(zip.directory()->entry(QStringLiteral("logo.png"))); 1295 QVERIFY(entry); 1296 QCOMPARE(entry->size(), 3589); 1297 QCOMPARE(entry->compressedSize(), 3594); 1298 QCOMPARE(entry->position(), 8943); 1299 } 1300 1301 void KArchiveTest::testRcc() 1302 { 1303 const QString rccFile = QFINDTESTDATA("runtime_resource.rcc"); // was copied from qtbase/tests/auto/corelib/io/qresourceengine 1304 QVERIFY(!rccFile.isEmpty()); 1305 KRcc rcc(rccFile); 1306 QVERIFY(rcc.open(QIODevice::ReadOnly)); 1307 const KArchiveDirectory *rootDir = rcc.directory(); 1308 QVERIFY(rootDir != nullptr); 1309 const KArchiveEntry *rrEntry = rootDir->entry(QStringLiteral("runtime_resource")); 1310 QVERIFY(rrEntry && rrEntry->isDirectory()); 1311 const KArchiveDirectory *rrDir = static_cast<const KArchiveDirectory *>(rrEntry); 1312 const KArchiveEntry *fileEntry = rrDir->entry(QStringLiteral("search_file.txt")); 1313 QVERIFY(fileEntry && fileEntry->isFile()); 1314 const KArchiveFile *searchFile = static_cast<const KArchiveFile *>(fileEntry); 1315 const QByteArray fileData = searchFile->data(); 1316 QCOMPARE(QString::fromLatin1(fileData), QString::fromLatin1("root\n")); 1317 } 1318 1319 void KArchiveTest::testAr() 1320 { 1321 const QString artestFile = QFINDTESTDATA("data/artest.a"); 1322 KAr ar(artestFile); 1323 1324 QVERIFY(ar.open(QIODevice::ReadOnly)); 1325 1326 const KArchiveDirectory *dir = ar.directory(); 1327 QVERIFY(dir != nullptr); 1328 1329 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1330 1331 const QStringList expected = {"mode=100644 path=at_quick_exit.oS type=file size=3456", 1332 "mode=100644 path=atexit.oS type=file size=3436", 1333 "mode=100644 path=elf-init.oS type=file size=3288", 1334 "mode=100644 path=fstat.oS type=file size=3296", 1335 "mode=100644 path=fstat64.oS type=file size=3304", 1336 "mode=100644 path=fstatat.oS type=file size=3352", 1337 "mode=100644 path=fstatat64.oS type=file size=3380", 1338 "mode=100644 path=lstat.oS type=file size=3320", 1339 "mode=100644 path=lstat64.oS type=file size=3332", 1340 "mode=100644 path=mknod.oS type=file size=2840", 1341 "mode=100644 path=mknodat.oS type=file size=2848", 1342 "mode=100644 path=stack_chk_fail_local.oS type=file size=2288", 1343 "mode=100644 path=stat.oS type=file size=3316", 1344 "mode=100644 path=stat64.oS type=file size=3312", 1345 "mode=100644 path=warning-nop.oS type=file size=1976"}; 1346 1347 QCOMPARE(listing.count(), expected.count()); 1348 QCOMPARE(listing, expected); 1349 1350 QVERIFY(ar.close()); 1351 } 1352 1353 /** 1354 * @see QTest::cleanupTestCase() 1355 */ 1356 void KArchiveTest::cleanupTestCase() 1357 { 1358 QFile::remove(s_zipMaxLengthFileName); 1359 QFile::remove(s_zipFileName); 1360 QFile::remove(s_zipLocaleFileName); 1361 #ifndef Q_OS_WIN 1362 QFile::remove(QStringLiteral("test3_symlink")); 1363 #endif 1364 } 1365 1366 /// 1367 1368 #if HAVE_XZ_SUPPORT 1369 1370 /** 1371 * Prepares dataset for archive filter tests 1372 */ 1373 void KArchiveTest::setup7ZipData() 1374 { 1375 QTest::addColumn<QString>("fileName"); 1376 QTest::newRow(".7z") << "karchivetest.7z"; 1377 } 1378 1379 /** 1380 * @dataProvider testCreate7Zip_data 1381 */ 1382 void KArchiveTest::testCreate7Zip() 1383 { 1384 QFETCH(QString, fileName); 1385 1386 QBENCHMARK { 1387 K7Zip k7zip(fileName); 1388 QVERIFY(k7zip.open(QIODevice::WriteOnly)); 1389 1390 writeTestFilesToArchive(&k7zip); 1391 1392 QVERIFY(k7zip.close()); 1393 1394 QFileInfo fileInfo(QFile::encodeName(fileName)); 1395 QVERIFY(fileInfo.exists()); 1396 // qDebug() << "fileInfo.size()" << fileInfo.size(); 1397 // We can't check for an exact size because of the addLocalFile, whose data is system-dependent 1398 QVERIFY(fileInfo.size() > 390); 1399 } 1400 } 1401 1402 /** 1403 * @dataProvider setupData 1404 */ 1405 void KArchiveTest::testRead7Zip() // testCreate7Zip must have been run first. 1406 { 1407 QFETCH(QString, fileName); 1408 1409 QBENCHMARK { 1410 K7Zip k7zip(fileName); 1411 1412 QVERIFY(k7zip.open(QIODevice::ReadOnly)); 1413 1414 const KArchiveDirectory *dir = k7zip.directory(); 1415 QVERIFY(dir != nullptr); 1416 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1417 1418 #ifndef Q_OS_WIN 1419 QCOMPARE(listing.count(), 16); 1420 #else 1421 QCOMPARE(listing.count(), 15); 1422 #endif 1423 QCOMPARE(listing[0], QString("mode=40755 path=aaaemptydir type=dir")); 1424 QCOMPARE(listing[1], QString("mode=40777 path=dir type=dir")); 1425 QCOMPARE(listing[2], QString("mode=40777 path=dir/subdir type=dir")); 1426 QCOMPARE(listing[3], QString("mode=100644 path=dir/subdir/mediumfile2 type=file size=100")); 1427 QCOMPARE(listing[4], QString("mode=100644 path=empty type=file size=0")); 1428 QCOMPARE(listing[5], QString("mode=100755 path=executableAll type=file size=17")); 1429 QCOMPARE(listing[6], QString("mode=100644 path=hugefile type=file size=20000")); 1430 QCOMPARE(listing[7], QString("mode=100644 path=mediumfile type=file size=100")); 1431 QCOMPARE(listing[8], QString("mode=40777 path=my type=dir")); 1432 QCOMPARE(listing[9], QString("mode=40777 path=my/dir type=dir")); 1433 QCOMPARE(listing[10], QString("mode=100644 path=my/dir/test3 type=file size=28")); 1434 QCOMPARE(listing[11], QString("mode=100440 path=test1 type=file size=5")); 1435 QCOMPARE(listing[12], QString("mode=100644 path=test2 type=file size=8")); 1436 QCOMPARE(listing[13], QString("mode=40777 path=z type=dir")); 1437 // This one was added with addLocalFile, so ignore mode/user/group. 1438 QString str = listing[14]; 1439 str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path=")); 1440 QCOMPARE(str, QString("path=z/test3 type=file size=13")); 1441 #ifndef Q_OS_WIN 1442 str = listing[15]; 1443 str.replace(QRegularExpression(QStringLiteral("mode.*path=")), QStringLiteral("path=")); 1444 QCOMPARE(str, QString("path=z/test3_symlink type=file size=0 symlink=test3")); 1445 #endif 1446 1447 QVERIFY(k7zip.close()); 1448 } 1449 } 1450 1451 /** 1452 * @dataProvider setupData 1453 */ 1454 void KArchiveTest::test7ZipFileData() 1455 { 1456 QFETCH(QString, fileName); 1457 1458 // testCreate7Zip must have been run first. 1459 K7Zip k7zip(fileName); 1460 QVERIFY(k7zip.open(QIODevice::ReadOnly)); 1461 1462 testFileData(&k7zip); 1463 1464 QVERIFY(k7zip.close()); 1465 } 1466 1467 /** 1468 * @dataProvider setupData 1469 */ 1470 void KArchiveTest::test7ZipCopyTo() 1471 { 1472 QFETCH(QString, fileName); 1473 1474 // testCreateTar must have been run first. 1475 K7Zip k7zip(fileName); 1476 QVERIFY(k7zip.open(QIODevice::ReadOnly)); 1477 1478 testCopyTo(&k7zip); 1479 1480 QVERIFY(k7zip.close()); 1481 } 1482 1483 /** 1484 * @dataProvider setupData 1485 */ 1486 void KArchiveTest::test7ZipReadWrite() 1487 { 1488 QFETCH(QString, fileName); 1489 1490 // testCreate7zip must have been run first. 1491 K7Zip k7zip(fileName); 1492 QVERIFY(k7zip.open(QIODevice::ReadWrite)); 1493 1494 testReadWrite(&k7zip); 1495 testFileData(&k7zip); 1496 1497 QVERIFY(k7zip.close()); 1498 1499 // Reopen it and check it 1500 { 1501 K7Zip k7zip(fileName); 1502 QVERIFY(k7zip.open(QIODevice::ReadOnly)); 1503 testFileData(&k7zip); 1504 const KArchiveDirectory *dir = k7zip.directory(); 1505 const KArchiveEntry *e = dir->entry(QStringLiteral("newfile")); 1506 QVERIFY(e && e->isFile()); 1507 const KArchiveFile *f = (KArchiveFile *)e; 1508 QCOMPARE(f->data().size(), 8); 1509 } 1510 1511 // NOTE This is the last test for this dataset. so cleanup here 1512 QFile::remove(fileName); 1513 } 1514 1515 /** 1516 * @dataProvider test7ZipMaxLength_data 1517 */ 1518 void KArchiveTest::test7ZipMaxLength() 1519 { 1520 QFETCH(QString, fileName); 1521 1522 K7Zip k7zip(fileName); 1523 1524 QVERIFY(k7zip.open(QIODevice::WriteOnly)); 1525 1526 // Generate long filenames of each possible length bigger than 98... 1527 for (int i = 98; i < 514; i++) { 1528 QString str; 1529 QString num; 1530 str.fill('a', i - 10); 1531 num.setNum(i); 1532 num = num.rightJustified(10, '0'); 1533 k7zip.writeFile(str + num, "hum"); 1534 } 1535 QVERIFY(k7zip.close()); 1536 1537 QVERIFY(k7zip.open(QIODevice::ReadOnly)); 1538 1539 const KArchiveDirectory *dir = k7zip.directory(); 1540 QVERIFY(dir != nullptr); 1541 const QStringList listing = recursiveListEntries(dir, QLatin1String(""), 0); 1542 1543 QCOMPARE(listing[0], 1544 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3")); 1545 QCOMPARE( 1546 listing[3], 1547 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3")); 1548 QCOMPARE( 1549 listing[4], 1550 QString("mode=100644 path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3")); 1551 1552 QCOMPARE(listing.count(), 416); 1553 1554 QVERIFY(k7zip.close()); 1555 1556 // NOTE Cleanup here 1557 QFile::remove(fileName); 1558 } 1559 #endif 1560 1561 #include "moc_karchivetest.cpp"