/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** 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 https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "pixmapcachemodel_test.h" #include namespace QmlProfiler { namespace Internal { PixmapCacheModelTest::PixmapCacheModelTest(QObject *parent) : QObject(parent), manager(nullptr), model(&manager) { } void PixmapCacheModelTest::initTestCase() { manager.startAcquiring(); manager.traceTime()->setTime(1, 300); for (int i = 0; i < MaximumPixmapEventType; ++i) { eventTypeIndices[i] = manager.qmlModel()->addEventType( QmlEventType(PixmapCacheEvent, MaximumRangeType, i, QmlEventLocation("dings.png", 0, 0))); } // random data, should still result in consistent model. for (int i = 0; i < 100; ++i) { QmlEvent event; event.setTypeIndex(eventTypeIndices[(i * 13) % MaximumPixmapEventType]); event.setTimestamp(i); event.setNumbers({i + 1, i - 1, i * 2}); manager.qmlModel()->addEvent(event); } for (int i = 0; i < MaximumPixmapEventType; ++i) { eventTypeIndices[i + MaximumPixmapEventType] = manager.qmlModel()->addEventType( QmlEventType(PixmapCacheEvent, MaximumRangeType, i, QmlEventLocation("blah.png", 0, 0))); } // Explicitly test some "interesting" things int nextItem = model.count(); QmlEvent event; event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingStarted]); event.setTimestamp(101); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); event.setNumbers({0, 0, 200}); // cache count increase event.setTimestamp(102); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::ToBeCached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingError]); event.setTimestamp(103); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Corrupt); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); event.setNumbers({0, 0, 199}); // cache count decrease event.setTimestamp(104); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); ++nextItem; QCOMPARE(model.count(), nextItem); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingStarted]); event.setTimestamp(105); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingError]); event.setTimestamp(106); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); // We still cache the previous item, even though it's uncacheable. // This way we get a corrupt cache entry ... event.setNumbers({0, 0, 200}); event.setTimestamp(107); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem - 1), PixmapCacheModel::Corrupt); QCOMPARE(model.loadState(nextItem - 1), PixmapCacheModel::Error); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); // ... which is immediately removed by another cache count change event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); event.setNumbers({0, 0, 199}); // cache count decrease, removes the corrupt entry event.setTimestamp(108); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem - 1), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem - 1), PixmapCacheModel::Error); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); ++nextItem; QCOMPARE(model.count(), nextItem); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingStarted]); event.setTimestamp(109); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); event.setNumbers({0, 0, 200}); // cache count increase event.setTimestamp(110); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::ToBeCached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapSizeKnown]); event.setNumbers({50, 50}); event.setTimestamp(111); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Cached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapCacheCountChanged]); event.setNumbers({0, 0, 199}); // cache count decrease event.setTimestamp(112); manager.qmlModel()->addEvent(event); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncached); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Loading); // one pixmap item, and two cache count changes with known size QCOMPARE(model.count(), nextItem + 3); event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapSizeKnown]); event.setNumbers({20, 30}); event.setTimestamp(113); manager.qmlModel()->addEvent(event); // Results in Uncached, with valid size QCOMPARE(model.count(), nextItem + 3); // no item added here; we just store the size event.setTypeIndex(eventTypeIndices[MaximumPixmapEventType + PixmapLoadingError]); event.setTimestamp(114); // terminates the still loading item, adding another cache count change manager.qmlModel()->addEvent(event); QCOMPARE(model.count(), nextItem + 4); QCOMPARE(model.cacheState(nextItem), PixmapCacheModel::Uncacheable); QCOMPARE(model.loadState(nextItem), PixmapCacheModel::Error); // some more random data for (int i = MaximumPixmapEventType; i < 2 * MaximumPixmapEventType; ++i) { for (int j = 0; j < i; ++j) { QmlEvent event; event.setTypeIndex(eventTypeIndices[i]); event.setTimestamp(i + j + 200); event.setNumbers({i + 1, i - 1, i - j}); manager.qmlModel()->addEvent(event); } } manager.acquiringDone(); QCOMPARE(manager.state(), QmlProfilerModelManager::Done); } void PixmapCacheModelTest::testConsistency() { // one unique URL QCOMPARE(model.expandedRowCount(), 4); QVERIFY(model.collapsedRowCount() >= model.expandedRowCount()); float prevHeight = 0; qint64 currentTime = -1; qint64 currentEnd = -1; QHash collapsedEnds; for (int i = 0; i < model.count(); ++i) { int *typesEnd = eventTypeIndices + 2 * MaximumPixmapEventType; QVERIFY(std::find(eventTypeIndices, typesEnd, model.typeId(i)) != typesEnd); QVERIFY(model.startTime(i) >= currentTime); currentTime = model.startTime(i); QVERIFY(currentTime >= manager.traceTime()->startTime()); currentEnd = model.endTime(i); QVERIFY(currentEnd <= manager.traceTime()->endTime()); const QVariantMap details = model.details(i); int collapsedRow = model.collapsedRow(i); int expandedRow = model.expandedRow(i); switch (collapsedRow) { case 0: QFAIL("There should be no events in row 0"); break; case 1: QVERIFY(model.relativeHeight(i) != prevHeight); prevHeight = model.relativeHeight(i); QCOMPARE(expandedRow, 1); break; default: QVERIFY(expandedRow > 1); QCOMPARE(model.relativeHeight(i), 1.0f); QVERIFY(collapsedRow < model.collapsedRowCount()); if (collapsedEnds.contains(collapsedRow)) QVERIFY(collapsedEnds[collapsedRow] <= currentTime); collapsedEnds[collapsedRow] = model.endTime(i); } switch (expandedRow) { case 0: QFAIL("There should be no events in row 0"); break; case 1: QCOMPARE(collapsedRow, 1); QVERIFY(details[QLatin1String("displayName")].toString() == model.tr("Image Cached")); break; default: QVERIFY(collapsedRow > 1); QCOMPARE(model.relativeHeight(i), 1.0f); QVERIFY(expandedRow < model.expandedRowCount()); QVERIFY(details[QLatin1String("displayName")].toString() == model.tr("Image Loaded")); QCOMPARE(details[model.tr("Duration")].toString(), QmlProfilerDataModel::formatTime(model.duration(i))); // In expanded view pixmaps of the same URL but different sizes are allowed to overlap. // It looks bad, but that should be a rare thing. break; } QVERIFY(details.contains(model.tr("Cache Size"))); QString filename = details[model.tr("File")].toString(); QVERIFY(filename == QString("dings.png") || filename == QString("blah.png")); QVERIFY(details.contains(model.tr("Width"))); QVERIFY(details.contains(model.tr("Height"))); } } void PixmapCacheModelTest::testRowMaxValue() { QCOMPARE(model.rowMaxValue(2), 0); QVERIFY(model.rowMaxValue(1) > 0); } void PixmapCacheModelTest::testColor() { QColor row1Color; QColor dingsColor; QColor blahColor; for (int i = 0; i < model.count(); ++i) { if (model.collapsedRow(i) == 1) { if (!row1Color.isValid()) row1Color = model.color(i); else QCOMPARE(model.color(i), row1Color); } else { const QmlEventType &type = manager.qmlModel()->eventTypes()[model.typeId(i)]; QColor &pixmapColor = (type.location().filename() == QString("blah.png")) ? blahColor : dingsColor; if (!pixmapColor.isValid()) pixmapColor = model.color(i); else QCOMPARE(model.color(i), pixmapColor); } } } void PixmapCacheModelTest::testLabels() { const QVariantList labels = model.labels(); QCOMPARE(labels.length(), 3); const QVariantMap countRow = labels[0].toMap(); QCOMPARE(countRow[QString("description")].toString(), model.tr("Cache Size")); QCOMPARE(countRow[QString("id")].toInt(), 0); const QVariantMap dingsRow = labels[1].toMap(); QCOMPARE(dingsRow[QString("description")].toString(), QString("dings.png")); QCOMPARE(dingsRow[QString("id")].toInt(), 1); // urlIndex + 1 const QVariantMap blahRow = labels[2].toMap(); QCOMPARE(blahRow[QString("description")].toString(), QString("blah.png")); QCOMPARE(blahRow[QString("id")].toInt(), 2); // urlIndex + 1 } void PixmapCacheModelTest::cleanupTestCase() { manager.clear(); QCOMPARE(model.count(), 0); } } // namespace Internal } // namespace QmlProfiler