#include "testquazipfileinfo.h" #include "qztest.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < 0x050000 Q_DECLARE_METATYPE(QList); Q_DECLARE_METATYPE(QuaExtraFieldHash); #endif TestQuaZipFileInfo::TestQuaZipFileInfo(QObject *parent) : QObject(parent) { } void TestQuaZipFileInfo::getNTFSTime() { QFETCH(QString, zipName); QFETCH(bool, useMTime); QFETCH(bool, useATime); QFETCH(bool, useCTime); QStringList testFiles; testFiles << "test.txt"; QDir curDir; if (curDir.exists(zipName)) { if (!curDir.remove(zipName)) QFAIL("Can't remove zip file"); } if (!createTestFiles(testFiles)) { QFAIL("Can't create test file"); } QDateTime base(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC); quint64 mTicks, aTicks, cTicks; QFileInfo fileInfo("tmp/test.txt"); { // create QuaZip zip(zipName); QVERIFY(zip.open(QuaZip::mdCreate)); QuaZipFile zipFile(&zip); QDateTime lm = fileInfo.lastModified().toUTC(); QDateTime lr = fileInfo.lastRead().toUTC(); QDateTime cr = quazip_ctime(fileInfo).toUTC(); mTicks = (static_cast(base.date().daysTo(lm.date())) * Q_UINT64_C(86400000) + static_cast(base.time().msecsTo(lm.time()))) * Q_UINT64_C(10000); aTicks = (static_cast(base.date().daysTo(lr.date())) * Q_UINT64_C(86400000) + static_cast(base.time().msecsTo(lr.time()))) * Q_UINT64_C(10000); cTicks = (static_cast(base.date().daysTo(cr.date())) * Q_UINT64_C(86400000) + static_cast(base.time().msecsTo(cr.time()))) * Q_UINT64_C(10000); QuaZipNewInfo newInfo("test.txt", "tmp/test.txt"); QByteArray extra(36, 0); extra[0] = 0x0A; // magic extra[1] = 0x00; extra[2] = 32; // size extra[3] = 0; extra[4] = extra[5] = extra[6] = extra[7] = 0; // reserved extra[8] = 0x01; // time tag extra[9] = 0x00; extra[10] = 24; // time tag size extra[11] = 0; for (int i = 12; i < 36; i += 8) { quint64 ticks; if (i == 12) { ticks = useMTime ? mTicks : 0; } else if (i == 20) { ticks = useATime ? aTicks : 0; } else if (i == 28) { ticks = useCTime ? cTicks : 0; } else { QFAIL("Stupid programming bug here"); } extra[i] = static_cast(ticks); extra[i + 1] = static_cast(ticks >> 8); extra[i + 2] = static_cast(ticks >> 16); extra[i + 3] = static_cast(ticks >> 24); extra[i + 4] = static_cast(ticks >> 32); extra[i + 5] = static_cast(ticks >> 40); extra[i + 6] = static_cast(ticks >> 48); extra[i + 7] = static_cast(ticks >> 56); } newInfo.extraLocal = extra; newInfo.extraGlobal = extra; QVERIFY(zipFile.open(QIODevice::WriteOnly, newInfo)); zipFile.close(); zip.close(); } { // check QuaZip zip(zipName); QVERIFY(zip.open(QuaZip::mdUnzip)); zip.goToFirstFile(); QuaZipFileInfo64 zipFileInfo; QVERIFY(zip.getCurrentFileInfo(&zipFileInfo)); zip.close(); QCOMPARE(zipFileInfo.getNTFSmTime(), useMTime ? fileInfo.lastModified() : QDateTime()); QCOMPARE(zipFileInfo.getNTFSaTime(), useATime ? fileInfo.lastRead() : QDateTime()); QCOMPARE(zipFileInfo.getNTFScTime(), useCTime ? quazip_ctime(fileInfo) : QDateTime()); } removeTestFiles(testFiles); curDir.remove(zipName); } void TestQuaZipFileInfo::getNTFSTime_data() { QTest::addColumn("zipName"); QTest::addColumn("useMTime"); QTest::addColumn("useATime"); QTest::addColumn("useCTime"); QTest::newRow("no times") << QString::fromUtf8("noTimes") << false << false << false; QTest::newRow("all times") << QString::fromUtf8("allTimes") << true << true << true; QTest::newRow("no CTime") << QString::fromUtf8("noCTime") << true << true << false; } void TestQuaZipFileInfo::getExtTime_data() { QTest::addColumn("zipName"); QTest::addColumn("flags"); QTest::addColumn("sizeLocal"); QTest::addColumn< QList >("timesLocal"); QTest::addColumn("sizeGlobal"); QTest::addColumn< QList >("timesGlobal"); QTest::addColumn("expectedModTime"); QTest::addColumn("expectedAcTime"); QTest::addColumn("expectedCrTime"); QTest::newRow("no times") << QString::fromUtf8("noTimes") << quint8(0) << quint16(1) << QList() << quint16(1) << QList() << QDateTime() << QDateTime() << QDateTime(); QTest::newRow("all times") << QString::fromUtf8("allTimes") << quint8(7) << quint16(13) << (QList() << 1 << 2 << 3) << quint16(5) << (QList() << 1) << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 1), Qt::UTC) << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 2), Qt::UTC) << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 3), Qt::UTC); QTest::newRow("no ac time") << QString::fromUtf8("noAcTime") << quint8(5) << quint16(9) << (QList() << 1 << 3) << quint16(5) << (QList() << 1) << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 1), Qt::UTC) << QDateTime() << QDateTime(QDate(1970, 1, 1), QTime(0, 0, 3), Qt::UTC); QTest::newRow("negativeTime") << QString::fromUtf8("negativeTime") << quint8(1) << quint16(5) << (QList() << -1) << quint16(5) << (QList() << -1) << QDateTime(QDate(1969, 12, 31), QTime(23, 59, 59), Qt::UTC) << QDateTime() << QDateTime(); } static QByteArray makeExtTimeField(quint16 size, quint8 flags, const QList ×) { if (size == 0) return QByteArray(); const quint16 EXT_TIME_MAGIC = 0x5455; QBuffer buffer; buffer.open(QBuffer::WriteOnly); QDataStream localStream(&buffer); localStream.setByteOrder(QDataStream::LittleEndian); localStream << EXT_TIME_MAGIC; localStream << size; localStream << flags; Q_FOREACH(qint32 time, times) { localStream << time; } return buffer.buffer(); } void TestQuaZipFileInfo::getExtTime() { QFETCH(QString, zipName); QFETCH(quint8, flags); QFETCH(quint16, sizeLocal); QFETCH(QList, timesLocal); QFETCH(quint16, sizeGlobal); QFETCH(QList, timesGlobal); QFETCH(QDateTime, expectedModTime); QFETCH(QDateTime, expectedAcTime); QFETCH(QDateTime, expectedCrTime); QStringList fileNames; QString fileName = QString::fromLatin1("aFile.txt"); fileNames << fileName; { createTestFiles(fileNames); QuaZip testZip("tmp/" + zipName); testZip.open(QuaZip::mdCreate); QuaZipFile file(&testZip); QuaZipNewInfo newInfo(fileName, "tmp/" + fileName); newInfo.extraLocal = makeExtTimeField(sizeLocal, flags, timesLocal); newInfo.extraGlobal = makeExtTimeField(sizeGlobal, flags, timesGlobal); file.open(QIODevice::WriteOnly, newInfo); file.close(); testZip.close(); } removeTestFiles(fileNames); QuaZip zip("tmp/" + zipName); QVERIFY(zip.open(QuaZip::mdUnzip)); QVERIFY(zip.goToFirstFile()); QuaZipFileInfo64 fileInfo; QVERIFY(zip.getCurrentFileInfo(&fileInfo)); QuaZipFile zipFile(&zip); QVERIFY(zipFile.open(QIODevice::ReadOnly)); QDateTime actualGlobalModTime = fileInfo.getExtModTime(); QDateTime actualLocalModTime = zipFile.getExtModTime(); QDateTime actualLocalAcTime = zipFile.getExtAcTime(); QDateTime actualLocalCrTime = zipFile.getExtCrTime(); zipFile.close(); QCOMPARE(actualGlobalModTime, expectedModTime); QCOMPARE(actualLocalModTime, expectedModTime); QCOMPARE(actualLocalAcTime, expectedAcTime); QCOMPARE(actualLocalCrTime, expectedCrTime); zip.close(); QDir("tmp").remove(zipName); } void TestQuaZipFileInfo::getExtTime_issue43() { // Test original GitHub issue, just in case. // (The test above relies on manual ZIP generation, which isn't perfect.) QuaZip zip(":/test_files/issue43_cant_get_dates.zip"); QVERIFY(zip.open(QuaZip::mdUnzip)); zip.goToFirstFile(); QuaZipFileInfo64 zipFileInfo; QVERIFY(zip.getCurrentFileInfo(&zipFileInfo)); zip.goToFirstFile(); QuaZipFile zipFile(&zip); QVERIFY(zipFile.open(QIODevice::ReadOnly)); QDateTime actualGlobalModTime = zipFileInfo.getExtModTime(); QDateTime actualLocalModTime = zipFile.getExtModTime(); QDateTime actualLocalAcTime = zipFile.getExtAcTime(); QDateTime actualLocalCrTime = zipFile.getExtCrTime(); zip.close(); QDateTime extModTime(QDate(2019, 7, 2), QTime(15, 43, 47), Qt::UTC); QDateTime extAcTime = extModTime; QDateTime extCrTime = extModTime; QCOMPARE(actualGlobalModTime, extModTime); QCOMPARE(actualLocalModTime, extModTime); QCOMPARE(actualLocalAcTime, extAcTime); QCOMPARE(actualLocalCrTime, extCrTime); } void TestQuaZipFileInfo::parseExtraField_data() { QTest::addColumn("extraField"); QTest::addColumn("expected"); QTest::newRow("empty") << QByteArray() << QuaExtraFieldHash(); { QuaExtraFieldHash oneZeroIdWithNoData; oneZeroIdWithNoData[0] = QList() << QByteArray(); QTest::newRow("one zero ID with no data") << QByteArray("\x00\x00\x00\x00", 4) << oneZeroIdWithNoData; } { QuaExtraFieldHash oneZeroIdWithData; oneZeroIdWithData[0] = QList() << QByteArray("DATA", 4); QTest::newRow("one zero ID with data") << QByteArray("\x00\x00\x04\x00" "DATA", 8) << oneZeroIdWithData; } { QuaExtraFieldHash oneNonZeroIdWithData; oneNonZeroIdWithData[1] = QList() << QByteArray("DATA", 4); QTest::newRow("one non zero ID with data") << QByteArray("\x01\x00\x04\x00" "DATA", 8) << oneNonZeroIdWithData; } { QuaExtraFieldHash severalDifferentIDs; severalDifferentIDs[0] = QList() << QByteArray("DATA0", 5); severalDifferentIDs[1] = QList() << QByteArray("DATA1", 5); QTest::newRow("two IDs") << QByteArray("\x00\x00\x05\x00" "DATA0" "\x01\x00\x05\x00" "DATA1", 18) << severalDifferentIDs; } { QuaExtraFieldHash repeatedID; repeatedID[0] = QList() << QByteArray("DATA0", 5) << QByteArray("DATA1", 5); QTest::newRow("repeated ID") << QByteArray("\x00\x00\x05\x00" "DATA0" "\x00\x00\x05\x00" "DATA1", 18) << repeatedID; } { QuaExtraFieldHash largeID; largeID[0x0102] = QList() << QByteArray("DATA", 4); QTest::newRow("large ID") << QByteArray("\x02\x01\x04\x00" "DATA", 8) << largeID; } { QuaExtraFieldHash largeData; largeData[0] = QList() << QByteArray(65535, 'x'); QTest::newRow("large ID") << (QByteArray("\x00\x00\xff\xff", 4) + QByteArray(65535, 'x')) << largeData; } QTest::newRow("only 1 byte") << QByteArray("\x00", 1) << QuaExtraFieldHash(); QTest::newRow("only 2 bytes") << QByteArray("\x00\x00", 2) << QuaExtraFieldHash(); QTest::newRow("only 3 bytes") << QByteArray("\x00\x00\x00", 3) << QuaExtraFieldHash(); QTest::newRow("truncated data") << QByteArray("\x00\x00\x02\x00\x00", 5) << QuaExtraFieldHash(); } void TestQuaZipFileInfo::parseExtraField() { QFETCH(QByteArray, extraField); QFETCH(QuaExtraFieldHash, expected); QuaExtraFieldHash actual = QuaZipFileInfo64::parseExtraField(extraField); QCOMPARE(actual, expected); }