/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $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"