Commit 6de290a1 authored by Ulf Hermann's avatar Ulf Hermann
Browse files

QmlProfiler: Add tests for pixmap cache model



Change-Id: Ic3114f6cd205434e2a489112d070300a3c842352
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@theqtcompany.com>
parent a0f956f0
......@@ -400,6 +400,26 @@ void PixmapCacheModel::clear()
QmlProfilerTimelineModel::clear();
}
#ifdef WITH_TESTS
PixmapCacheModel::LoadState PixmapCacheModel::loadState(int index) const
{
const PixmapCacheItem &item = m_data[index];
if (item.urlIndex == -1 || item.sizeIndex == -1)
return MaximumLoadState;
return m_pixmaps[item.urlIndex].sizes[item.sizeIndex].loadState;
}
PixmapCacheModel::CacheState PixmapCacheModel::cacheState(int index) const
{
const PixmapCacheItem &item = m_data[index];
if (item.urlIndex == -1 || item.sizeIndex == -1)
return MaximumCacheState;
return m_pixmaps[item.urlIndex].sizes[item.sizeIndex].cacheState;
}
#endif // WITH_TESTS
void PixmapCacheModel::computeMaxCacheSize()
{
foreach (const PixmapCacheModel::PixmapCacheItem &event, m_data) {
......
......@@ -44,14 +44,18 @@ public:
ToBeCached, // After determining the pixmap is to be cached but before knowing its size
Cached, // After caching a pixmap or determining the size of a ToBeCached pixmap
Uncacheable, // If loading failed without ToBeCached or after a corrupt pixmap has been uncached
Corrupt // If after ToBeCached we learn that loading failed
Corrupt, // If after ToBeCached we learn that loading failed
MaximumCacheState
};
enum LoadState {
Initial,
Loading,
Finished,
Error
Error,
MaximumLoadState
};
struct PixmapState {
......@@ -105,11 +109,15 @@ public:
QVariantMap details(int index) const override;
protected:
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
#ifdef WITH_TESTS
LoadState loadState(int index) const;
CacheState cacheState(int index) const;
#endif
private:
void computeMaxCacheSize();
void resizeUnfinishedLoads();
......
......@@ -84,6 +84,7 @@ QtcPlugin {
"inputeventsmodel_test.cpp", "inputeventsmodel_test.h",
"localqmlprofilerrunner_test.cpp", "localqmlprofilerrunner_test.h",
"memoryusagemodel_test.cpp", "memoryusagemodel_test.h",
"pixmapcachemodel_test.cpp", "pixmapcachemodel_test.h",
]
}
}
......@@ -37,6 +37,7 @@
#include "tests/inputeventsmodel_test.h"
#include "tests/localqmlprofilerrunner_test.h"
#include "tests/memoryusagemodel_test.h"
#include "tests/pixmapcachemodel_test.h"
#endif
#include <extensionsystem/pluginmanager.h>
......@@ -91,6 +92,7 @@ QList<QObject *> QmlProfiler::Internal::QmlProfilerPlugin::createTestObjects() c
tests << new InputEventsModelTest;
tests << new LocalQmlProfilerRunnerTest;
tests << new MemoryUsageModelTest;
tests << new PixmapCacheModelTest;
#endif
return tests;
}
......
/****************************************************************************
**
** 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 <QtTest>
namespace QmlProfiler {
namespace Internal {
PixmapCacheModelTest::PixmapCacheModelTest(QObject *parent) : QObject(parent),
manager(nullptr), model(&manager)
{
}
void PixmapCacheModelTest::initTestCase()
{
manager.startAcquiring();
manager.traceTime()->setTime(1, 300);
QmlEventType type;
type.message = PixmapCacheEvent;
type.rangeType = MaximumRangeType;
type.location = QmlEventLocation("dings.png", -1, -1);
for (int i = 0; i < MaximumPixmapEventType; ++i) {
type.detailType = i;
eventTypeIndices[i] = manager.qmlModel()->addEventType(type);
}
// 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);
}
type.location = QmlEventLocation("blah.png", -1, -1);
for (int i = 0; i < MaximumPixmapEventType; ++i) {
type.detailType = i;
eventTypeIndices[i + MaximumPixmapEventType] = manager.qmlModel()->addEventType(type);
}
// 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<int, qint64> 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
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include <qmlprofiler/pixmapcachemodel.h>
#include <qmlprofiler/qmlprofilermodelmanager.h>
#include <QObject>
namespace QmlProfiler {
namespace Internal {
class PixmapCacheModelTest : public QObject
{
Q_OBJECT
public:
PixmapCacheModelTest(QObject *parent = 0);
private slots:
void initTestCase();
void testConsistency();
void testRowMaxValue();
void testColor();
void testLabels();
void cleanupTestCase();
private:
QmlProfilerModelManager manager;
PixmapCacheModel model;
int eventTypeIndices[2 * MaximumPixmapEventType];
};
} // namespace Internal
} // namespace QmlProfiler
......@@ -5,7 +5,8 @@ SOURCES += \
$$PWD/flamegraphview_test.cpp \
$$PWD/inputeventsmodel_test.cpp \
$$PWD/localqmlprofilerrunner_test.cpp \
$$PWD/memoryusagemodel_test.cpp
$$PWD/memoryusagemodel_test.cpp \
$$PWD/pixmapcachemodel_test.cpp
HEADERS += \
$$PWD/debugmessagesmodel_test.h \
......@@ -14,4 +15,5 @@ HEADERS += \
$$PWD/flamegraphview_test.h \
$$PWD/inputeventsmodel_test.h \
$$PWD/localqmlprofilerrunner_test.h \
$$PWD/memoryusagemodel_test.h
$$PWD/memoryusagemodel_test.h \
$$PWD/pixmapcachemodel_test.h
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment