/* This file is part of the KDE project Copyright (C) 2006, 2010 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "karchivetest.h" #include #include "moc_karchivetest.cpp" #include #include #include #include #include #include #include #include // symlink #include QTEST_KDEMAIN_CORE( KArchiveTest ) static const int SIZE1 = 100; /** * Writes test fileset specified archive * @param archive archive */ static void writeTestFilesToArchive( KArchive* archive ) { QVERIFY( archive->writeFile( "empty", "weis", "users", "", 0 ) ); QVERIFY( archive->writeFile( "test1", "weis", "users", "Hallo", 5, 0100440 ) ); // 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( "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", "dfaure", "hackers", "I do not speak German\nDavid.", 29 ) ); // Now a medium file : 100 null bytes char medium[ SIZE1 ]; memset( medium, 0, SIZE1 ); QVERIFY( archive->writeFile( "mediumfile", "user", "group", medium, SIZE1 ) ); // Another one, with an absolute path QVERIFY( archive->writeFile( "/dir/subdir/mediumfile2", "user", "group", 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", "user", "group", huge, n ) ); delete [] huge; // Now an empty directory QVERIFY( archive->writeDir( "aaaemptydir", "user", "group" ) ); // Add local symlink QVERIFY( archive->addLocalFile( "test3_symlink", "z/test3_symlink") ); } enum { WithUserGroup = 1 }; // ListingFlags static QStringList recursiveListEntries( const KArchiveDirectory * dir, const QString & path, int listingFlags ) { QStringList ret; QStringList l = dir->entries(); l.sort(); Q_FOREACH(const QString& it, l) { const KArchiveEntry* entry = dir->entry(it); QString descr; descr += QString("mode=") + QString::number( entry->permissions(), 8 ) + ' '; if ( listingFlags & WithUserGroup ) { descr += QString("user=") + entry->user() + ' '; descr += QString("group=") + entry->group() + ' '; } descr += QString("path=") + path+(it) + ' '; descr += QString("type=") + ( entry->isDirectory() ? "dir" : "file" ); if ( entry->isFile() ) descr += QString(" size=") + QString::number( static_cast(entry)->size() ); if (!entry->symLinkTarget().isEmpty()) descr += QString(" symlink=") + entry->symLinkTarget(); // TODO add date and time //kDebug() << 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 KArchiveEntry* e = dir->entry( "z/test3" ); QVERIFY( e ); QVERIFY( e->isFile() ); const KArchiveFile* f = static_cast( e ); 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), QString::fromLatin1(arr) ); delete dev; e = dir->entry( "mediumfile" ); QVERIFY( e && e->isFile() ); f = (KArchiveFile*)e; QCOMPARE( f->data().size(), SIZE1 ); e = dir->entry( "hugefile" ); QVERIFY( e && e->isFile() ); f = (KArchiveFile*)e; QCOMPARE( f->data().size(), 20000 ); e = dir->entry( "aaaemptydir" ); QVERIFY( e && e->isDirectory() ); e = dir->entry( "my/dir/test3" ); QVERIFY( e && e->isFile() ); f = (KArchiveFile*)e; dev = f->createDevice(); QByteArray firstLine = dev->readLine(); QCOMPARE(QString::fromLatin1(firstLine), QString::fromLatin1("I do not speak German\n")); QByteArray secondLine = dev->read(100); QCOMPARE(QString::fromLatin1(secondLine), QString::fromLatin1("David.")); delete dev; e = dir->entry( "z/test3_symlink" ); QVERIFY(e); QVERIFY(e->isFile()); QCOMPARE(e->symLinkTarget(), QString("test3")); // Test "./" prefix for KOffice (xlink:href="./ObjectReplacements/Object 1") e = dir->entry( "./hugefile" ); QVERIFY( e && e->isFile() ); e = dir->entry( "./my/dir/test3" ); QVERIFY( e && e->isFile() ); // Test directory entries e = dir->entry( "my" ); QVERIFY(e && e->isDirectory()); e = dir->entry( "my/" ); QVERIFY(e && e->isDirectory()); e = dir->entry( "./my/" ); QVERIFY(e && e->isDirectory()); } static void testReadWrite( KArchive* archive ) { QVERIFY(archive->writeFile("newfile", "dfaure", "users", "New File", 8, 0100440)); } static void testCopyTo( KArchive* archive ) { const KArchiveDirectory* dir = archive->directory(); KTempDir tmpDir; const QString dirName = tmpDir.name(); 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(29)); const QString fileName = dirName+"z/test3_symlink"; const QFileInfo fileInfo5(fileName); QVERIFY(fileInfo5.exists()); QVERIFY(fileInfo5.isFile()); // Do not use fileInfo.readLink() 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); } QCOMPARE(symLinkTarget, QString("test3")); } /** * Prepares dataset for archive filter tests */ void KArchiveTest::setupData() { QTest::addColumn("fileName"); QTest::addColumn("mimeType"); QTest::newRow(".tar.gz") << "karchivetest.tar.gz" << "application/x-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 } /** * @see QTest::initTestCase() */ void KArchiveTest::initTestCase() { // Prepare local symlink QFile::remove("test3_symlink"); if (::symlink("test3", "test3_symlink") != 0) { qDebug() << errno; QVERIFY(false); } // For better benchmarks: initialize KMimeTypeFactory magic here KMimeType::findByContent(QByteArray("hello")); } void KArchiveTest::testCreateTar_data() { QTest::addColumn("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); } } /** * @dataProvider setupData */ void KArchiveTest::testReadTar() // testCreateTarGz must have been run first. { QFETCH( QString, fileName ); // 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 != 0 ); const QStringList listing = recursiveListEntries( dir, "", WithUserGroup ); QFileInfo localFileData("test3"); const QString owner = localFileData.owner(); const QString group = localFileData.group(); QCOMPARE( listing.count(), 15 ); QCOMPARE( listing[ 0], QString("mode=40755 user=user group=group path=aaaemptydir type=dir") ); QCOMPARE( listing[ 1], QString("mode=40777 user=%1 group=%2 path=dir type=dir").arg(owner, group) ); QCOMPARE( listing[ 2], QString("mode=40777 user=%1 group=%2 path=dir/subdir type=dir").arg(owner,group) ); QCOMPARE( listing[ 3], QString("mode=100644 user=user group=group path=dir/subdir/mediumfile2 type=file size=100") ); QCOMPARE( listing[ 4], QString("mode=100644 user=weis group=users path=empty type=file size=0") ); QCOMPARE( listing[ 5], QString("mode=100644 user=user group=group path=hugefile type=file size=20000") ); QCOMPARE( listing[ 6], QString("mode=100644 user=user group=group path=mediumfile type=file size=100") ); QCOMPARE( listing[ 7], QString("mode=40777 user=%1 group=%2 path=my type=dir").arg(owner, group) ); QCOMPARE( listing[ 8], QString("mode=40777 user=%1 group=%2 path=my/dir type=dir").arg(owner, group) ); QCOMPARE( listing[ 9], QString("mode=100644 user=dfaure group=hackers path=my/dir/test3 type=file size=29") ); QCOMPARE( listing[10], QString("mode=100440 user=weis group=users path=test1 type=file size=5") ); QCOMPARE( listing[11], QString("mode=100644 user=weis group=users path=test2 type=file size=8") ); QCOMPARE( listing[12], QString("mode=40777 user=%1 group=%2 path=z type=dir").arg(owner, group) ); // This one was added with addLocalFile, so ignore mode/user/group. QString str = listing[13]; str.replace(QRegExp("mode.*path"), "path" ); QCOMPARE( str, QString("path=z/test3 type=file size=13") ); str = listing[14]; str.replace(QRegExp("mode.*path"), "path" ); QCOMPARE( str, QString("path=z/test3_symlink type=file size=0 symlink=test3") ); 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)); QIODevice *filterDev = KFilterDev::deviceForFile(fileName, mimeType, true); QVERIFY(filterDev); QByteArray buffer; buffer.resize(8*1024); kDebug() << "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; // kDebug() << "read len=" << len << " totalSize=" << totalSize; } filterDev->close(); delete filterDev; // kDebug() << "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("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("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, num; str.fill( 'a', i-10 ); num.setNum( i ); num = num.rightJustified( 10, '0' ); tar.writeFile( str+num, "testu", "testg", "hum", 3 ); } // 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 != 0 ); const QStringList listing = recursiveListEntries( dir, "", WithUserGroup ); QCOMPARE( listing[ 0], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000098 type=file size=3") ); QCOMPARE( listing[ 3], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000101 type=file size=3") ); QCOMPARE( listing[ 4], QString("mode=100644 user=testu group=testg path=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000102 type=file size=3") ); QCOMPARE( listing.count(), 416 ); QVERIFY( tar.close() ); // NOTE Cleanup here QFile::remove( fileName ); } void KArchiveTest::testTarGlobalHeader() { KTar tar( QString::fromLatin1(KDESRCDIR) + QLatin1String( "global_header_test.tar.bz2" ) ); QVERIFY( tar.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = tar.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", WithUserGroup ); 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") ); QCOMPARE( listing.count(), 2 ); QVERIFY( tar.close() ); } void KArchiveTest::testTarPrefix() { KTar tar( QString::fromLatin1(KDESRCDIR) + QLatin1String( "tar_prefix_test.tar.bz2" ) ); QVERIFY( tar.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = tar.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", 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( QString::fromLatin1(KDESRCDIR) + QLatin1String( "tar_directory_forgotten.tar.bz2" ) ); QVERIFY( tar.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = tar.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", 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::testTarRootDir() // bug 309463 { KTar tar(QString::fromLatin1(KDESRCDIR) + QLatin1String("tar_rootdir.tar.bz2")); QVERIFY(tar.open(QIODevice::ReadOnly)); const KArchiveDirectory* dir = tar.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries(dir, "", 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::testTarDirectoryTwice() // bug 206994 { KTar tar(QString::fromLatin1(KDESRCDIR) + QLatin1String("tar_directory_twice.tar.bz2")); QVERIFY(tar.open(QIODevice::ReadOnly)); const KArchiveDirectory* dir = tar.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries(dir, "", 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); } /// 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( "mimetype", "", "", zipMimeType.data(), zipMimeType.size() ); 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)); writeTestFilesToArchive(&zip); // try to add something as a file that is no file QVERIFY( !zip.addLocalFile( QDir::currentPath(), "bogusdir" ) ); QVERIFY(!zip.close()); } void KArchiveTest::testReadZipError() { QFile brokenZip( "broken.zip" ); QVERIFY( brokenZip.open( QIODevice::WriteOnly ) ); // incomplete magic brokenZip.write( QByteArray( "PK\003" ) ); brokenZip.close(); KZip zip( "broken.zip" ); QVERIFY( !zip.open(QIODevice::ReadOnly) ); 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) ); 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 != 0 ); // ZIP has no support for per-file user/group, so omit them from the listing const QStringList listing = recursiveListEntries( dir, "", 0 ); QCOMPARE( listing.count(), 16 ); 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=100644 path=hugefile type=file size=20000") ); QCOMPARE( listing[ 6], QString("mode=100644 path=mediumfile type=file size=100") ); QCOMPARE( listing[ 7], QString("mode=100644 path=mimetype type=file size=%1").arg(strlen(s_zipMimeType)) ); 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=29") ); 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 QString str = listing[14]; str.replace(QRegExp("mode.*path"), "path" ); QCOMPARE( str, QString("path=z/test3 type=file size=13") ); str = listing[15]; str.replace(QRegExp("mode.*path"), "path" ); QCOMPARE( str, QString("path=z/test3_symlink type=file size=5 symlink=test3") ); 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, num; str.fill( 'a', i-10 ); num.setNum( i ); num = num.rightJustified( 10, '0' ); zip.writeFile( str+num, "testu", "testg", "hum", 3 ); } QVERIFY( zip.close() ); QVERIFY( zip.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = zip.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", 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, "pino", "users", fileData.constData(), fileData.size() ) ); QVERIFY( zip.close() ); QVERIFY( zip.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = zip.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", 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 = QLatin1String("wisdom"); QVERIFY( zip.writeFile( fileName, "konqi", "dragons", fileData1.constData(), fileData1.size() ) ); // now overwrite it const QByteArray fileData2("If there is smoke, there could be a fire."); QVERIFY( zip.writeFile( fileName, "konqi", "dragons", fileData2.constData(), fileData2.size() ) ); QVERIFY( zip.close() ); QVERIFY( zip.open( QIODevice::ReadOnly ) ); const KArchiveDirectory* dir = zip.directory(); QVERIFY( dir != 0 ); const QStringList listing = recursiveListEntries( dir, "", 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 KTempDir tmpDir; const QString dirName = tmpDir.name(); const QByteArray file1Data = "Hello Shantanu"; const QString file1 = QLatin1String("file1"); QVERIFY(writeFile(dirName, file1, file1Data)); { 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 != 0); const KArchiveEntry* e = dir->entry(file1); QVERIFY(e && e->isFile()); const KArchiveFile* f = (KArchiveFile*)e; QCOMPARE(f->data(), file1Data); } } /** * @see QTest::cleanupTestCase() */ void KArchiveTest::cleanupTestCase() { QFile::remove(s_zipMaxLengthFileName); QFile::remove(s_zipFileName); QFile::remove(s_zipLocaleFileName); QFile::remove("test3_symlink"); }