/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include enum Numbers { NumFakeCacheObjects = 200, //entries in pre-populated cache NumInsertions = 100, //insertions to be timed NumRemovals = 100, //removals to be timed NumReadContent = 100, //meta requests to be timed HugeCacheLimit = 50*1024*1024, // max size for a big cache TinyCacheLimit = 1*512*1024}; // max size for a tiny cache const QString fakeURLbase = "http://127.0.0.1/fake/"; //fake HTTP body aka payload const QByteArray payload("Qt rocks!"); class tst_qnetworkdiskcache : public QObject { Q_OBJECT private: void injectFakeData(); void insertOneItem(); bool isUrlCached(quint32 id); void cleanRecursive(QString &path); void cleanupCacheObject(); void initCacheObject(); QString cacheDir; QNetworkDiskCache *cache; public slots: void initTestCase(); void cleanupTestCase(); private slots: void timeInsertion_data(); void timeInsertion(); void timeRead_data(); void timeRead(); void timeRemoval_data(); void timeRemoval(); void timeExpiration_data(); void timeExpiration(); }; void tst_qnetworkdiskcache::initTestCase() { cache = 0; } void tst_qnetworkdiskcache::cleanupTestCase() { cleanupCacheObject(); cleanRecursive(cacheDir); } void tst_qnetworkdiskcache::timeInsertion_data() { QTest::addColumn("cacheRootDirectory"); QString cacheLoc = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QTest::newRow("QStandardPaths Cache Location") << cacheLoc; } //This functions times an insert() operation. //You can run it after populating the cache with //fake data so that more realistic performance //estimates are obtained. void tst_qnetworkdiskcache::timeInsertion() { QFETCH(QString, cacheRootDirectory); cacheDir = QString( cacheRootDirectory + QDir::separator() + "man_qndc"); QDir d; qDebug() << "Setting cache directory to = " << d.absoluteFilePath(cacheDir); //Housekeeping cleanRecursive(cacheDir); // slow op. initCacheObject(); cache->setCacheDirectory(cacheDir); cache->setMaximumCacheSize(qint64(HugeCacheLimit)); cache->clear(); //populate some fake data to simulate partially full cache injectFakeData(); // SLOW //Sanity-check that the first URL that we insert below isn't already in there. QVERIFY(isUrlCached(NumFakeCacheObjects) == false); // IMPORTANT: max cache size should be HugeCacheLimit, to avoid evictions below //time insertion of previously-uncached URLs. QBENCHMARK_ONCE { for (quint32 i = NumFakeCacheObjects; i < (NumFakeCacheObjects + NumInsertions); i++) { //prepare metata for url QNetworkCacheMetaData meta; QString fakeURL; QTextStream stream(&fakeURL); stream << fakeURLbase << i; QUrl url(fakeURL); meta.setUrl(url); meta.setSaveToDisk(true); //commit payload and metadata to disk QIODevice *device = cache->prepare(meta); device->write(payload); cache->insert(device); } } //SLOW cleanup cleanupCacheObject(); cleanRecursive(cacheDir); } void tst_qnetworkdiskcache::timeRead_data() { QTest::addColumn("cacheRootDirectory"); QString cacheLoc = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QTest::newRow("QStandardPaths Cache Location") << cacheLoc; } //Times metadata as well payload lookup // i.e metaData(), rawHeaders() and data() void tst_qnetworkdiskcache::timeRead() { QFETCH(QString, cacheRootDirectory); cacheDir = QString( cacheRootDirectory + QDir::separator() + "man_qndc"); QDir d; qDebug() << "Setting cache directory to = " << d.absoluteFilePath(cacheDir); //Housekeeping cleanRecursive(cacheDir); // slow op. initCacheObject(); cache->setCacheDirectory(cacheDir); cache->setMaximumCacheSize(qint64(HugeCacheLimit)); cache->clear(); //populate some fake data to simulate partially full cache injectFakeData(); //Entries in the cache should be > what we try to remove QVERIFY(NumFakeCacheObjects > NumReadContent); //time metadata lookup of previously inserted URL. QBENCHMARK_ONCE { for (quint32 i = 0; i < NumReadContent; i++) { QString fakeURL; QTextStream stream(&fakeURL); stream << fakeURLbase << i; QUrl url(fakeURL); QNetworkCacheMetaData qndc = cache->metaData(url); QVERIFY(qndc.isValid()); // we must have read the metadata QNetworkCacheMetaData::RawHeaderList raw(qndc.rawHeaders()); QVERIFY(raw.size()); // we must have parsed the headers from the meta QIODevice *iodevice(cache->data(url)); QVERIFY(iodevice); //must not be NULL iodevice->close(); delete iodevice; } } //Cleanup (slow) cleanupCacheObject(); cleanRecursive(cacheDir); } void tst_qnetworkdiskcache::timeRemoval_data() { QTest::addColumn("cacheRootDirectory"); QString cacheLoc = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QTest::newRow("QStandardPaths Cache Location") << cacheLoc; } void tst_qnetworkdiskcache::timeRemoval() { QFETCH(QString, cacheRootDirectory); cacheDir = QString( cacheRootDirectory + QDir::separator() + "man_qndc"); QDir d; qDebug() << "Setting cache directory to = " << d.absoluteFilePath(cacheDir); //Housekeeping initCacheObject(); cleanRecursive(cacheDir); // slow op. cache->setCacheDirectory(cacheDir); // Make max cache size HUGE, so that evictions don't happen below cache->setMaximumCacheSize(qint64(HugeCacheLimit)); cache->clear(); //populate some fake data to simulate partially full cache injectFakeData(); //Sanity-check that the URL is already in there somewhere QVERIFY(isUrlCached(NumRemovals-1) == true); //Entries in the cache should be > what we try to remove QVERIFY(NumFakeCacheObjects > NumRemovals); //time removal of previously-inserted URL. QBENCHMARK_ONCE { for (quint32 i = 0; i < NumRemovals; i++) { QString fakeURL; QTextStream stream(&fakeURL); stream << fakeURLbase << i; QUrl url(fakeURL); cache->remove(url); } } //Cleanup (slow) cleanupCacheObject(); cleanRecursive(cacheDir); } void tst_qnetworkdiskcache::timeExpiration_data() { QTest::addColumn("cacheRootDirectory"); QString cacheLoc = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QTest::newRow("QStandardPaths Cache Location") << cacheLoc; } void tst_qnetworkdiskcache::timeExpiration() { QFETCH(QString, cacheRootDirectory); cacheDir = QString( cacheRootDirectory + QDir::separator() + "man_qndc"); QDir d; qDebug() << "Setting cache directory to = " << d.absoluteFilePath(cacheDir); //Housekeeping initCacheObject(); cleanRecursive(cacheDir); // slow op. cache->setCacheDirectory(cacheDir); // Make max cache size HUGE, so that evictions don't happen below cache->setMaximumCacheSize(qint64(HugeCacheLimit)); cache->clear(); //populate some fake data to simulate partially full cache injectFakeData(); //Sanity-check that the URL is already in there somewhere QVERIFY(isUrlCached(NumRemovals-1) == true); //Entries in the cache should be > what we try to remove QVERIFY(NumFakeCacheObjects > NumRemovals); //Set cache limit lower, so this force 1 round of eviction cache->setMaximumCacheSize(qint64(TinyCacheLimit)); //time insertions of additional content, which is likely to internally cause evictions QBENCHMARK_ONCE { for (quint32 i = NumFakeCacheObjects; i < (NumFakeCacheObjects + NumInsertions); i++) { //prepare metata for url QNetworkCacheMetaData meta; QString fakeURL; QTextStream stream(&fakeURL); stream << fakeURLbase << i;//codescanner::leave QUrl url(fakeURL); meta.setUrl(url); meta.setSaveToDisk(true); //commit payload and metadata to disk QIODevice *device = cache->prepare(meta); device->write(payload); cache->insert(device); // this should trigger evictions, if TinyCacheLimit is small enough } } //Cleanup (slow) cleanupCacheObject(); cleanRecursive(cacheDir); } // This function simulates a partially or fully occupied disk cache // like a normal user of a cache might encounter is real-life browsing. // The point of this is to trigger degradation in file-system and media performance // that occur due to the quantity and layout of data. void tst_qnetworkdiskcache::injectFakeData() { QNetworkCacheMetaData::RawHeaderList headers; headers.append(qMakePair(QByteArray("X-TestHeader"),QByteArray("HeaderValue"))); //Prep cache dir with fake data using QNetworkDiskCache APIs for (quint32 i = 0; i < NumFakeCacheObjects; i++) { //prepare metata for url QNetworkCacheMetaData meta; QString fakeURL; QTextStream stream(&fakeURL); stream << fakeURLbase << i; QUrl url(fakeURL); meta.setUrl(url); meta.setRawHeaders(headers); meta.setSaveToDisk(true); //commit payload and metadata to disk QIODevice *device = cache->prepare(meta); device->write(payload); cache->insert(device); } } // Checks if the fake URL #id is already cached or not. bool tst_qnetworkdiskcache::isUrlCached(quint32 id) { QString str; QTextStream stream(&str); stream << fakeURLbase << id; QUrl url(str); QIODevice *iod = cache->data(url); return ((iod == 0) ? false : true) ; } // Utility function for recursive directory cleanup. void tst_qnetworkdiskcache::cleanRecursive(QString &path) { QDirIterator it(path, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { QFile f(it.next()); bool err = f.remove(); Q_UNUSED(err); } QDirIterator it2(path, QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it2.hasNext()) { QString s(it2.next()); QDir dir(s); dir.rmdir(s); } } void tst_qnetworkdiskcache::cleanupCacheObject() { delete cache; cache = 0; } void tst_qnetworkdiskcache::initCacheObject() { cache = new QNetworkDiskCache(); } QTEST_MAIN(tst_qnetworkdiskcache) #include "tst_qnetworkdiskcache.moc"