forked from qt-creator/qt-creator
QmlDesigner: Improve imagecache
Instead of coding some arguments to extraId(state) we provide now a std::variant there extra arguments can be saved. Because it's a std::variant it can be easlily extended by new structs. There is a new synchronous interface too. It has an extra method for QIcon which saves icons in an extra table. It would be even nicer if we would have a mipmap image too. So we could do it asynchonously too but so far it works only in the main thread. Task-number: QDS-3579 Fixes: QDS-3584 Change-Id: If368d84d82308a91a5f4f037021e749c9ef868ed Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -249,11 +249,18 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
|
||||
|
||||
void BaseStatement::bind(int index, BlobView blobView)
|
||||
{
|
||||
int resultCode = sqlite3_bind_blob64(m_compiledStatement.get(),
|
||||
int resultCode = SQLITE_OK;
|
||||
|
||||
if (blobView.empty()) {
|
||||
sqlite3_bind_null(m_compiledStatement.get(), index);
|
||||
} else {
|
||||
resultCode = sqlite3_bind_blob64(m_compiledStatement.get(),
|
||||
index,
|
||||
blobView.data(),
|
||||
blobView.size(),
|
||||
SQLITE_STATIC);
|
||||
}
|
||||
|
||||
if (resultCode != SQLITE_OK)
|
||||
checkForBindingError(resultCode);
|
||||
}
|
||||
@@ -713,6 +720,26 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Type BaseStatement::fetchType(int column) const
|
||||
{
|
||||
auto dataType = sqlite3_column_type(m_compiledStatement.get(), column);
|
||||
|
||||
switch (dataType) {
|
||||
case SQLITE_INTEGER:
|
||||
return Type::Integer;
|
||||
case SQLITE_FLOAT:
|
||||
return Type::Float;
|
||||
case SQLITE3_TEXT:
|
||||
return Type::Text;
|
||||
case SQLITE_BLOB:
|
||||
return Type::Blob;
|
||||
case SQLITE_NULL:
|
||||
return Type::Null;
|
||||
}
|
||||
|
||||
return Type::Invalid;
|
||||
}
|
||||
|
||||
int BaseStatement::fetchIntValue(int column) const
|
||||
{
|
||||
return sqlite3_column_int(m_compiledStatement.get(), column);
|
||||
|
@@ -51,6 +51,8 @@ namespace Sqlite {
|
||||
class Database;
|
||||
class DatabaseBackend;
|
||||
|
||||
enum class Type : char { Invalid, Integer, Float, Text, Blob, Null };
|
||||
|
||||
class SQLITE_EXPORT BaseStatement
|
||||
{
|
||||
public:
|
||||
@@ -65,6 +67,7 @@ public:
|
||||
void step() const;
|
||||
void reset() const;
|
||||
|
||||
Type fetchType(int column) const;
|
||||
int fetchIntValue(int column) const;
|
||||
long fetchLongValue(int column) const;
|
||||
long long fetchLongLongValue(int column) const;
|
||||
|
@@ -509,8 +509,10 @@ extend_qtc_plugin(QmlDesigner
|
||||
include/textmodifier.h
|
||||
include/variantproperty.h
|
||||
include/viewmanager.h
|
||||
include/imagecache.h
|
||||
include/imagecacheinterface.h
|
||||
include/asynchronousimagecache.h
|
||||
include/synchronousimagecache.h
|
||||
include/imagecacheauxiliarydata.h
|
||||
include/asynchronousimagecacheinterface.h
|
||||
)
|
||||
|
||||
extend_qtc_plugin(QmlDesigner
|
||||
@@ -601,7 +603,8 @@ extend_qtc_plugin(QmlDesigner
|
||||
imagecache/imagecachecollector.cpp
|
||||
imagecache/imagecachefontcollector.h
|
||||
imagecache/imagecachefontcollector.cpp
|
||||
imagecache/imagecache.cpp
|
||||
imagecache/asynchronousimagecache.cpp
|
||||
imagecache/synchronousimagecache.cpp
|
||||
imagecache/imagecachecollectorinterface.h
|
||||
imagecache/imagecacheconnectionmanager.cpp
|
||||
imagecache/imagecacheconnectionmanager.h
|
||||
|
@@ -25,8 +25,8 @@
|
||||
|
||||
#include "customfilesystemmodel.h"
|
||||
|
||||
#include <synchronousimagecache.h>
|
||||
#include <theme.h>
|
||||
#include <imagecache.h>
|
||||
|
||||
#include <utils/filesystemwatcher.h>
|
||||
|
||||
@@ -98,7 +98,7 @@ QString fontFamily(const QFileInfo &info)
|
||||
class ItemLibraryFileIconProvider : public QFileIconProvider
|
||||
{
|
||||
public:
|
||||
ItemLibraryFileIconProvider(ImageCache &fontImageCache)
|
||||
ItemLibraryFileIconProvider(SynchronousImageCache &fontImageCache)
|
||||
: QFileIconProvider()
|
||||
, m_fontImageCache(fontImageCache)
|
||||
{
|
||||
@@ -138,71 +138,29 @@ public:
|
||||
|
||||
QIcon generateFontIcons(const QString &filePath) const
|
||||
{
|
||||
QIcon icon;
|
||||
QString colorName = Theme::getColor(Theme::DStextColor).name();
|
||||
std::condition_variable condition;
|
||||
int count = iconSizes.size();
|
||||
std::mutex mutex;
|
||||
QList<QPair<QSize, QImage>> images;
|
||||
|
||||
for (auto iconSize : iconSizes) {
|
||||
m_fontImageCache.requestImage(
|
||||
filePath,
|
||||
[&images, &condition, &count, &mutex, iconSize](const QImage &image) {
|
||||
int currentCount;
|
||||
{
|
||||
std::unique_lock lock{mutex};
|
||||
currentCount = --count;
|
||||
images.append({iconSize, image});
|
||||
}
|
||||
if (currentCount <= 0)
|
||||
condition.notify_all();
|
||||
},
|
||||
[&images, &condition, &count, &mutex, iconSize] {
|
||||
int currentCount;
|
||||
{
|
||||
std::unique_lock lock{mutex};
|
||||
currentCount = --count;
|
||||
images.append({iconSize, {}});
|
||||
}
|
||||
if (currentCount <= 0)
|
||||
condition.notify_all();
|
||||
},
|
||||
QStringLiteral("%1@%2@Abc").arg(QString::number(iconSize.width()),
|
||||
colorName)
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
// Block main thread until icons are generated, as it has to be done synchronously
|
||||
std::unique_lock lock{mutex};
|
||||
if (count > 0)
|
||||
condition.wait(lock, [&]{ return count <= 0; });
|
||||
}
|
||||
|
||||
for (const auto &pair : qAsConst(images)) {
|
||||
QImage image = pair.second;
|
||||
if (image.isNull())
|
||||
icon.addPixmap(defaultPixmapForType("font", pair.first));
|
||||
else
|
||||
icon.addPixmap(QPixmap::fromImage(image));
|
||||
}
|
||||
|
||||
return icon;
|
||||
return m_fontImageCache.icon(
|
||||
filePath,
|
||||
{},
|
||||
ImageCache::FontCollectorSizesAuxiliaryData{Utils::span{iconSizes},
|
||||
Theme::getColor(Theme::DStextColor).name(),
|
||||
"Abc"});
|
||||
}
|
||||
|
||||
// Generated icon sizes should contain all ItemLibraryResourceView needed icon sizes, and their
|
||||
// x2 versions for HDPI sceens
|
||||
QList<QSize> iconSizes = {{384, 384}, {192, 192}, // Large
|
||||
{256, 256}, {128, 128}, // Drag
|
||||
{96, 96}, // Medium
|
||||
{48, 48}, // Small
|
||||
{64, 64}, {32, 32}}; // List
|
||||
std::vector<QSize> iconSizes = {{384, 384},
|
||||
{192, 192}, // Large
|
||||
{256, 256},
|
||||
{128, 128}, // Drag
|
||||
{96, 96}, // Medium
|
||||
{48, 48}, // Small
|
||||
{64, 64},
|
||||
{32, 32}}; // List
|
||||
|
||||
ImageCache &m_fontImageCache;
|
||||
SynchronousImageCache &m_fontImageCache;
|
||||
};
|
||||
|
||||
CustomFileSystemModel::CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent)
|
||||
CustomFileSystemModel::CustomFileSystemModel(SynchronousImageCache &fontImageCache, QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, m_fileSystemModel(new QFileSystemModel(this))
|
||||
, m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
|
||||
|
@@ -39,13 +39,14 @@ namespace Utils { class FileSystemWatcher; }
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ImageCache;
|
||||
class SynchronousImageCache;
|
||||
|
||||
class CustomFileSystemModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CustomFileSystemModel(ImageCache &fontImageCache, QObject *parent = nullptr);
|
||||
CustomFileSystemModel(QmlDesigner::SynchronousImageCache &fontImageCache,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
void setFilter(QDir::Filters filters);
|
||||
QString rootPath() const;
|
||||
|
@@ -66,7 +66,7 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS
|
||||
{
|
||||
auto response = std::make_unique<ImageRespose>();
|
||||
|
||||
m_cache.requestIcon(
|
||||
m_cache.requestSmallImage(
|
||||
id,
|
||||
[response = QPointer<ImageRespose>(response.get())](const QImage &image) {
|
||||
QMetaObject::invokeMethod(
|
||||
|
@@ -29,7 +29,7 @@
|
||||
#include <rewriterview.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <imagecache.h>
|
||||
#include <asynchronousimagecache.h>
|
||||
#include <imagecache/imagecachecollector.h>
|
||||
#include <imagecache/imagecacheconnectionmanager.h>
|
||||
#include <imagecache/imagecachegenerator.h>
|
||||
@@ -45,14 +45,14 @@ namespace QmlDesigner {
|
||||
class ItemLibraryIconImageProvider : public QQuickAsyncImageProvider
|
||||
{
|
||||
public:
|
||||
ItemLibraryIconImageProvider(ImageCache &imageCache)
|
||||
ItemLibraryIconImageProvider(AsynchronousImageCache &imageCache)
|
||||
: m_cache{imageCache}
|
||||
{}
|
||||
|
||||
QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
|
||||
|
||||
private:
|
||||
ImageCache &m_cache;
|
||||
AsynchronousImageCache &m_cache;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include "customfilesystemmodel.h"
|
||||
|
||||
#include <theme.h>
|
||||
#include <imagecache.h>
|
||||
#include <asynchronousimagecache.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
@@ -62,8 +62,9 @@ void ItemLibraryResourceView::addSizeAction(QActionGroup *group, const QString &
|
||||
});
|
||||
}
|
||||
|
||||
ItemLibraryResourceView::ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent) :
|
||||
QListView(parent)
|
||||
ItemLibraryResourceView::ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
|
||||
QWidget *parent)
|
||||
: QListView(parent)
|
||||
{
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
@@ -113,13 +114,12 @@ ItemLibraryResourceView::ItemLibraryResourceView(ImageCache &fontImageCache, QWi
|
||||
// a commonly used sentence to preview the font glyphs in latin fonts.
|
||||
// For fonts that do not have latin glyphs, the font family name will have to
|
||||
// suffice for preview. Font family name is inserted into %1 at render time.
|
||||
m_fontPreviewTooltipBackend->setState(QStringLiteral("%1@%2@%3")
|
||||
.arg(QString::number(300),
|
||||
Theme::getColor(Theme::DStextColor).name(),
|
||||
QStringLiteral("%1\n\n"
|
||||
"The quick brown fox jumps\n"
|
||||
"over the lazy dog\n"
|
||||
"1234567890")));
|
||||
m_fontPreviewTooltipBackend->setAuxiliaryData(
|
||||
ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300},
|
||||
Theme::getColor(Theme::DStextColor).name(),
|
||||
QStringLiteral("The quick brown fox jumps\n"
|
||||
"over the lazy dog\n"
|
||||
"1234567890")});
|
||||
}
|
||||
|
||||
void ItemLibraryResourceView::startDrag(Qt::DropActions /* supportedActions */)
|
||||
|
@@ -35,13 +35,14 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ImageCache;
|
||||
class AsynchronousImageCache;
|
||||
|
||||
class ItemLibraryResourceView : public QListView {
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ItemLibraryResourceView(ImageCache &fontImageCache, QWidget *parent = nullptr);
|
||||
explicit ItemLibraryResourceView(AsynchronousImageCache &fontImageCache,
|
||||
QWidget *parent = nullptr);
|
||||
|
||||
void startDrag(Qt::DropActions supportedActions) override;
|
||||
bool viewportEvent(QEvent *event) override;
|
||||
|
@@ -26,12 +26,12 @@
|
||||
#include "itemlibraryview.h"
|
||||
#include "itemlibrarywidget.h"
|
||||
#include "metainfo.h"
|
||||
#include <asynchronousimagecache.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <imagecache.h>
|
||||
#include <imagecache/imagecachecollector.h>
|
||||
#include <imagecache/imagecachefontcollector.h>
|
||||
#include <imagecache/imagecacheconnectionmanager.h>
|
||||
#include <imagecache/imagecachefontcollector.h>
|
||||
#include <imagecache/imagecachegenerator.h>
|
||||
#include <imagecache/imagecachestorage.h>
|
||||
#include <imagecache/timestampprovider.h>
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <projectexplorer/target.h>
|
||||
#include <rewriterview.h>
|
||||
#include <sqlitedatabase.h>
|
||||
#include <synchronousimagecache.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <qmlitemnode.h>
|
||||
@@ -52,7 +53,7 @@ class ImageCacheData
|
||||
{
|
||||
public:
|
||||
Sqlite::Database database{
|
||||
Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v1.db"}};
|
||||
Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v2.db"}};
|
||||
ImageCacheStorage<Sqlite::Database> storage{database};
|
||||
ImageCacheConnectionManager connectionManager;
|
||||
ImageCacheCollector collector{connectionManager};
|
||||
@@ -60,8 +61,9 @@ public:
|
||||
ImageCacheGenerator generator{collector, storage};
|
||||
ImageCacheGenerator fontGenerator{fontCollector, storage};
|
||||
TimeStampProvider timeStampProvider;
|
||||
ImageCache cache{storage, generator, timeStampProvider};
|
||||
ImageCache fontImageCache{storage, fontGenerator, timeStampProvider};
|
||||
AsynchronousImageCache cache{storage, generator, timeStampProvider};
|
||||
AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider};
|
||||
SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector};
|
||||
};
|
||||
|
||||
ItemLibraryView::ItemLibraryView(QObject* parent)
|
||||
@@ -82,7 +84,9 @@ bool ItemLibraryView::hasWidget() const
|
||||
WidgetInfo ItemLibraryView::widgetInfo()
|
||||
{
|
||||
if (m_widget.isNull()) {
|
||||
m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache};
|
||||
m_widget = new ItemLibraryWidget{m_imageCacheData->cache,
|
||||
m_imageCacheData->asynchronousFontImageCache,
|
||||
m_imageCacheData->synchronousFontImageCache};
|
||||
m_widget->setImportsWidget(m_importManagerView->widgetInfo().widget);
|
||||
}
|
||||
|
||||
@@ -159,12 +163,14 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL
|
||||
void ItemLibraryView::setResourcePath(const QString &resourcePath)
|
||||
{
|
||||
if (m_widget.isNull())
|
||||
m_widget = new ItemLibraryWidget{m_imageCacheData->cache, m_imageCacheData->fontImageCache};
|
||||
m_widget = new ItemLibraryWidget{m_imageCacheData->cache,
|
||||
m_imageCacheData->asynchronousFontImageCache,
|
||||
m_imageCacheData->synchronousFontImageCache};
|
||||
|
||||
m_widget->setResourcePath(resourcePath);
|
||||
}
|
||||
|
||||
ImageCache &ItemLibraryView::imageCache()
|
||||
AsynchronousImageCache &ItemLibraryView::imageCache()
|
||||
{
|
||||
return m_imageCacheData->cache;
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ namespace QmlDesigner {
|
||||
class ItemLibraryWidget;
|
||||
class ImportManagerView;
|
||||
class ImageCacheData;
|
||||
class ImageCache;
|
||||
class AsynchronousImageCache;
|
||||
|
||||
class ItemLibraryView : public AbstractView
|
||||
{
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
void setResourcePath(const QString &resourcePath);
|
||||
|
||||
ImageCache &imageCache();
|
||||
AsynchronousImageCache &imageCache();
|
||||
|
||||
protected:
|
||||
void updateImports();
|
||||
|
@@ -82,10 +82,12 @@ static QString propertyEditorResourcesPath() {
|
||||
return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources");
|
||||
}
|
||||
|
||||
ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache)
|
||||
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
|
||||
AsynchronousImageCache &asynchronousFontImageCache,
|
||||
SynchronousImageCache &synchronousFontImageCache)
|
||||
: m_itemIconSize(24, 24)
|
||||
, m_itemViewQuickWidget(new QQuickWidget(this))
|
||||
, m_resourcesView(new ItemLibraryResourceView(fontImageCache, this))
|
||||
, m_resourcesView(new ItemLibraryResourceView(asynchronousFontImageCache, this))
|
||||
, m_importTagsWidget(new QWidget(this))
|
||||
, m_addResourcesWidget(new QWidget(this))
|
||||
, m_imageCache{imageCache}
|
||||
@@ -118,7 +120,7 @@ ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontIma
|
||||
Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
|
||||
|
||||
/* create Resources view and its model */
|
||||
m_resourcesFileSystemModel = new CustomFileSystemModel(fontImageCache, this);
|
||||
m_resourcesFileSystemModel = new CustomFileSystemModel(synchronousFontImageCache, this);
|
||||
m_resourcesView->setModel(m_resourcesFileSystemModel.data());
|
||||
|
||||
/* create image provider for loading item icons */
|
||||
|
@@ -56,7 +56,8 @@ class CustomFileSystemModel;
|
||||
|
||||
class ItemLibraryModel;
|
||||
class ItemLibraryResourceView;
|
||||
class ImageCache;
|
||||
class SynchronousImageCache;
|
||||
class AsynchronousImageCache;
|
||||
class ImageCacheCollector;
|
||||
|
||||
class ItemLibraryWidget : public QFrame
|
||||
@@ -69,7 +70,9 @@ class ItemLibraryWidget : public QFrame
|
||||
};
|
||||
|
||||
public:
|
||||
ItemLibraryWidget(ImageCache &imageCache, ImageCache &fontImageCache);
|
||||
ItemLibraryWidget(AsynchronousImageCache &imageCache,
|
||||
AsynchronousImageCache &asynchronousFontImageCache,
|
||||
SynchronousImageCache &synchronousFontImageCache);
|
||||
~ItemLibraryWidget();
|
||||
|
||||
void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo);
|
||||
@@ -126,7 +129,7 @@ private:
|
||||
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
|
||||
|
||||
QShortcut *m_qmlSourceUpdateShortcut;
|
||||
ImageCache &m_imageCache;
|
||||
AsynchronousImageCache &m_imageCache;
|
||||
QPointer<Model> m_model;
|
||||
FilterChangeFlag m_filterFlag;
|
||||
ItemLibraryEntry m_currentitemLibraryEntry;
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include "previewimagetooltip.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <imagecache.h>
|
||||
#include <asynchronousimagecache.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMetaObject>
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
PreviewTooltipBackend::PreviewTooltipBackend(ImageCache &cache)
|
||||
PreviewTooltipBackend::PreviewTooltipBackend(AsynchronousImageCache &cache)
|
||||
: m_cache{cache}
|
||||
{}
|
||||
|
||||
@@ -64,8 +64,8 @@ void PreviewTooltipBackend::showTooltip()
|
||||
});
|
||||
},
|
||||
[] {},
|
||||
m_state
|
||||
);
|
||||
m_extraId,
|
||||
m_auxiliaryData);
|
||||
|
||||
reposition();
|
||||
}
|
||||
@@ -155,17 +155,17 @@ void PreviewTooltipBackend::setInfo(const QString &info)
|
||||
emit infoChanged();
|
||||
}
|
||||
|
||||
QString PreviewTooltipBackend::state() const
|
||||
QString PreviewTooltipBackend::extraId() const
|
||||
{
|
||||
return m_state;
|
||||
return m_extraId;
|
||||
}
|
||||
|
||||
// Sets the imageCache state hint. Valid content depends on image cache collector used.
|
||||
void PreviewTooltipBackend::setState(const QString &state)
|
||||
// Sets the imageCache extraId hint. Valid content depends on image cache collector used.
|
||||
void PreviewTooltipBackend::setExtraId(const QString &extraId)
|
||||
{
|
||||
m_state = state;
|
||||
if (m_state != state)
|
||||
emit stateChanged();
|
||||
m_extraId = extraId;
|
||||
if (m_extraId != extraId)
|
||||
emit extraIdChanged();
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imagecacheauxiliarydata.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
@@ -33,7 +35,7 @@
|
||||
namespace QmlDesigner {
|
||||
|
||||
class PreviewImageTooltip;
|
||||
class ImageCache;
|
||||
class AsynchronousImageCache;
|
||||
|
||||
class PreviewTooltipBackend : public QObject
|
||||
{
|
||||
@@ -42,10 +44,10 @@ class PreviewTooltipBackend : public QObject
|
||||
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
|
||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||
Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged)
|
||||
Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged)
|
||||
Q_PROPERTY(QString extraId READ extraId WRITE setExtraId NOTIFY extraIdChanged)
|
||||
|
||||
public:
|
||||
PreviewTooltipBackend(ImageCache &cache);
|
||||
PreviewTooltipBackend(AsynchronousImageCache &cache);
|
||||
~PreviewTooltipBackend();
|
||||
|
||||
Q_INVOKABLE void showTooltip();
|
||||
@@ -58,24 +60,30 @@ public:
|
||||
void setPath(const QString &path);
|
||||
QString info() const;
|
||||
void setInfo(const QString &info);
|
||||
QString state() const;
|
||||
void setState(const QString &state);
|
||||
QString extraId() const;
|
||||
void setExtraId(const QString &extraId);
|
||||
|
||||
bool isVisible() const;
|
||||
|
||||
void setAuxiliaryData(ImageCache::AuxiliaryData auxiliaryData)
|
||||
{
|
||||
m_auxiliaryData = std::move(auxiliaryData);
|
||||
}
|
||||
|
||||
signals:
|
||||
void nameChanged();
|
||||
void pathChanged();
|
||||
void infoChanged();
|
||||
void stateChanged();
|
||||
void extraIdChanged();
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_path;
|
||||
QString m_info;
|
||||
QString m_state;
|
||||
QString m_extraId;
|
||||
std::unique_ptr<PreviewImageTooltip> m_tooltip;
|
||||
ImageCache &m_cache;
|
||||
ImageCache::AuxiliaryData m_auxiliaryData;
|
||||
AsynchronousImageCache &m_cache;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -88,7 +88,8 @@ SOURCES += $$PWD/model/abstractview.cpp \
|
||||
$$PWD/model/qmltimelinekeyframegroup.cpp \
|
||||
$$PWD/model/annotation.cpp \
|
||||
$$PWD/model/stylesheetmerger.cpp \
|
||||
$$PWD/imagecache/imagecache.cpp \
|
||||
$$PWD/imagecache/asynchronousimagecache.cpp \
|
||||
$$PWD/imagecache/synchronousimagecache.cpp \
|
||||
$$PWD/imagecache/imagecacheconnectionmanager.cpp \
|
||||
$$PWD/imagecache/imagecachegenerator.cpp \
|
||||
$$PWD/imagecache/timestampprovider.cpp
|
||||
@@ -174,7 +175,9 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \
|
||||
$$PWD/include/qmltimelinekeyframegroup.h \
|
||||
$$PWD/include/annotation.h \
|
||||
$$PWD/include/stylesheetmerger.h \
|
||||
$$PWD/include/imagecache.h \
|
||||
$$PWD/include/asynchronousimagecache.h \
|
||||
$$PWD/include/synchronousimagecache.h \
|
||||
$$PWD/include/imagecacheauxiliarydata.h \
|
||||
$$PWD/imagecache/imagecachecollectorinterface.h \
|
||||
$$PWD/imagecache/imagecacheconnectionmanager.h \
|
||||
$$PWD/imagecache/imagecachegeneratorinterface.h \
|
||||
|
@@ -23,7 +23,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "imagecache.h"
|
||||
#include "asynchronousimagecache.h"
|
||||
|
||||
#include "imagecachegenerator.h"
|
||||
#include "imagecachestorage.h"
|
||||
@@ -33,9 +33,9 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
ImageCache::ImageCache(ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider)
|
||||
AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider)
|
||||
: m_storage(storage)
|
||||
, m_generator(generator)
|
||||
, m_timeStampProvider(timeStampProvider)
|
||||
@@ -44,10 +44,11 @@ ImageCache::ImageCache(ImageCacheStorageInterface &storage,
|
||||
while (isRunning()) {
|
||||
if (auto [hasEntry, entry] = getEntry(); hasEntry) {
|
||||
request(entry.name,
|
||||
entry.state,
|
||||
entry.extraId,
|
||||
entry.requestType,
|
||||
std::move(entry.captureCallback),
|
||||
std::move(entry.abortCallback),
|
||||
std::move(entry.auxiliaryData),
|
||||
m_storage,
|
||||
m_generator,
|
||||
m_timeStampProvider);
|
||||
@@ -58,26 +59,27 @@ ImageCache::ImageCache(ImageCacheStorageInterface &storage,
|
||||
}};
|
||||
}
|
||||
|
||||
ImageCache::~ImageCache()
|
||||
AsynchronousImageCache::~AsynchronousImageCache()
|
||||
{
|
||||
clean();
|
||||
wait();
|
||||
}
|
||||
|
||||
void ImageCache::request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
ImageCache::RequestType requestType,
|
||||
ImageCache::CaptureCallback captureCallback,
|
||||
ImageCache::AbortCallback abortCallback,
|
||||
ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider)
|
||||
void AsynchronousImageCache::request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
AsynchronousImageCache::RequestType requestType,
|
||||
AsynchronousImageCache::CaptureImageCallback captureCallback,
|
||||
AsynchronousImageCache::AbortCallback abortCallback,
|
||||
ImageCache::AuxiliaryData auxiliaryData,
|
||||
ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider)
|
||||
{
|
||||
const auto id = state.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", state};
|
||||
const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", extraId};
|
||||
|
||||
const auto timeStamp = timeStampProvider.timeStamp(name);
|
||||
const auto entry = requestType == RequestType::Image ? storage.fetchImage(id, timeStamp)
|
||||
: storage.fetchIcon(id, timeStamp);
|
||||
: storage.fetchSmallImage(id, timeStamp);
|
||||
|
||||
if (entry.hasEntry) {
|
||||
if (entry.image.isNull())
|
||||
@@ -85,15 +87,20 @@ void ImageCache::request(Utils::SmallStringView name,
|
||||
else
|
||||
captureCallback(entry.image);
|
||||
} else {
|
||||
auto callback = [captureCallback = std::move(captureCallback),
|
||||
requestType](const QImage &image, const QImage &smallImage) {
|
||||
captureCallback(requestType == RequestType::Image ? image : smallImage);
|
||||
};
|
||||
generator.generateImage(name,
|
||||
state,
|
||||
extraId,
|
||||
timeStamp,
|
||||
std::move(captureCallback),
|
||||
std::move(abortCallback));
|
||||
std::move(callback),
|
||||
std::move(abortCallback),
|
||||
std::move(auxiliaryData));
|
||||
}
|
||||
}
|
||||
|
||||
void ImageCache::wait()
|
||||
void AsynchronousImageCache::wait()
|
||||
{
|
||||
stopThread();
|
||||
m_condition.notify_all();
|
||||
@@ -101,46 +108,50 @@ void ImageCache::wait()
|
||||
m_backgroundThread.join();
|
||||
}
|
||||
|
||||
void ImageCache::requestImage(Utils::PathString name,
|
||||
ImageCache::CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString state)
|
||||
void AsynchronousImageCache::requestImage(Utils::PathString name,
|
||||
AsynchronousImageCache::CaptureImageCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString extraId,
|
||||
ImageCache::AuxiliaryData auxiliaryData)
|
||||
{
|
||||
addEntry(std::move(name),
|
||||
std::move(state),
|
||||
std::move(extraId),
|
||||
std::move(captureCallback),
|
||||
std::move(abortCallback),
|
||||
std::move(auxiliaryData),
|
||||
RequestType::Image);
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
void ImageCache::requestIcon(Utils::PathString name,
|
||||
ImageCache::CaptureCallback captureCallback,
|
||||
ImageCache::AbortCallback abortCallback,
|
||||
Utils::SmallString state)
|
||||
void AsynchronousImageCache::requestSmallImage(Utils::PathString name,
|
||||
AsynchronousImageCache::CaptureImageCallback captureCallback,
|
||||
AsynchronousImageCache::AbortCallback abortCallback,
|
||||
Utils::SmallString extraId,
|
||||
ImageCache::AuxiliaryData auxiliaryData)
|
||||
{
|
||||
addEntry(std::move(name),
|
||||
std::move(state),
|
||||
std::move(extraId),
|
||||
std::move(captureCallback),
|
||||
std::move(abortCallback),
|
||||
RequestType::Icon);
|
||||
std::move(auxiliaryData),
|
||||
RequestType::SmallImage);
|
||||
m_condition.notify_all();
|
||||
}
|
||||
|
||||
void ImageCache::clean()
|
||||
void AsynchronousImageCache::clean()
|
||||
{
|
||||
clearEntries();
|
||||
m_generator.clean();
|
||||
}
|
||||
|
||||
void ImageCache::waitForFinished()
|
||||
void AsynchronousImageCache::waitForFinished()
|
||||
{
|
||||
wait();
|
||||
|
||||
m_generator.waitForFinished();
|
||||
}
|
||||
|
||||
std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
|
||||
std::tuple<bool, AsynchronousImageCache::Entry> AsynchronousImageCache::getEntry()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
@@ -153,22 +164,24 @@ std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
|
||||
return {true, entry};
|
||||
}
|
||||
|
||||
void ImageCache::addEntry(Utils::PathString &&name,
|
||||
Utils::SmallString &&state,
|
||||
ImageCache::CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback,
|
||||
RequestType requestType)
|
||||
void AsynchronousImageCache::addEntry(Utils::PathString &&name,
|
||||
Utils::SmallString &&extraId,
|
||||
AsynchronousImageCache::CaptureImageCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData,
|
||||
RequestType requestType)
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
|
||||
m_entries.emplace_back(std::move(name),
|
||||
std::move(state),
|
||||
std::move(extraId),
|
||||
std::move(captureCallback),
|
||||
std::move(abortCallback),
|
||||
std::move(auxiliaryData),
|
||||
requestType);
|
||||
}
|
||||
|
||||
void ImageCache::clearEntries()
|
||||
void AsynchronousImageCache::clearEntries()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
for (Entry &entry : m_entries)
|
||||
@@ -176,20 +189,20 @@ void ImageCache::clearEntries()
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
void ImageCache::waitForEntries()
|
||||
void AsynchronousImageCache::waitForEntries()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
if (m_entries.empty())
|
||||
m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
|
||||
}
|
||||
|
||||
void ImageCache::stopThread()
|
||||
void AsynchronousImageCache::stopThread()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
m_finishing = true;
|
||||
}
|
||||
|
||||
bool ImageCache::isRunning()
|
||||
bool AsynchronousImageCache::isRunning()
|
||||
{
|
||||
std::unique_lock lock{m_mutex};
|
||||
return !m_finishing || m_entries.size();
|
@@ -68,6 +68,7 @@ ImageCacheCollector::~ImageCacheCollector() = default;
|
||||
|
||||
void ImageCacheCollector::start(Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback)
|
||||
{
|
||||
@@ -97,7 +98,15 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
|
||||
if (stateNode.isValid())
|
||||
rewriterView.setCurrentStateNode(stateNode);
|
||||
|
||||
m_connectionManager.setCallback(std::move(captureCallback));
|
||||
auto callback = [captureCallback = std::move(captureCallback)](QImage &&image) {
|
||||
QSize smallImageSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
|
||||
Qt::KeepAspectRatio);
|
||||
QImage smallImage = image.isNull() ? QImage{} : image.scaled(smallImageSize);
|
||||
|
||||
captureCallback(std::move(image), std::move(smallImage));
|
||||
};
|
||||
|
||||
m_connectionManager.setCallback(std::move(callback));
|
||||
|
||||
nodeInstanceView.setTarget(m_target.data());
|
||||
nodeInstanceView.setCrashCallback(abortCallback);
|
||||
@@ -115,6 +124,20 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
|
||||
abortCallback();
|
||||
}
|
||||
|
||||
std::pair<QImage, QImage> ImageCacheCollector::createImage(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
QIcon ImageCacheCollector::createIcon(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void ImageCacheCollector::setTarget(ProjectExplorer::Target *target)
|
||||
{
|
||||
m_target = target;
|
||||
|
@@ -54,9 +54,18 @@ public:
|
||||
|
||||
void start(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback) override;
|
||||
|
||||
std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData) override;
|
||||
|
||||
QIcon createIcon(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData) override;
|
||||
|
||||
void setTarget(ProjectExplorer::Target *target);
|
||||
|
||||
private:
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imagecacheauxiliarydata.h>
|
||||
#include <utils/smallstringview.h>
|
||||
|
||||
#include <QImage>
|
||||
@@ -34,15 +35,26 @@ namespace QmlDesigner {
|
||||
class ImageCacheCollectorInterface
|
||||
{
|
||||
public:
|
||||
using CaptureCallback = std::function<void(QImage &&image)>;
|
||||
using CaptureCallback = std::function<void(QImage &&image, QImage &&smallImage)>;
|
||||
using AbortCallback = std::function<void()>;
|
||||
using ImagePair = std::pair<QImage, QImage>;
|
||||
|
||||
virtual void start(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback)
|
||||
= 0;
|
||||
|
||||
virtual ImagePair createImage(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
= 0;
|
||||
virtual QIcon createIcon(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
= 0;
|
||||
|
||||
protected:
|
||||
~ImageCacheCollectorInterface() = default;
|
||||
};
|
||||
|
@@ -42,6 +42,8 @@ ImageCacheFontCollector::ImageCacheFontCollector() = default;
|
||||
|
||||
ImageCacheFontCollector::~ImageCacheFontCollector() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
QByteArray fileToByteArray(QString const &filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
@@ -53,29 +55,17 @@ QByteArray fileToByteArray(QString const &filename)
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ImageCacheFontCollector::start(Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView,
|
||||
const ImageCache::AuxiliaryData &auxiliaryDataValue,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback)
|
||||
{
|
||||
// State contains size, text color, and sample text
|
||||
QStringList hints = QString(state).split('@');
|
||||
int dim(300);
|
||||
if (hints.size() >= 1) {
|
||||
bool ok = false;
|
||||
int newDim = QString(hints[0]).toInt(&ok);
|
||||
if (ok)
|
||||
dim = newDim;
|
||||
}
|
||||
#ifndef QMLDESIGNER_TEST
|
||||
QColor textColor(Theme::getColor(Theme::DStextColor));
|
||||
#else
|
||||
QColor textColor;
|
||||
#endif
|
||||
if (hints.size() >= 2)
|
||||
textColor.setNamedColor(hints[1]);
|
||||
QString text = hints.size() >= 3 ? hints[2] : "Abc";
|
||||
QSize size(dim, dim);
|
||||
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizeAuxiliaryData>(auxiliaryDataValue);
|
||||
QColor textColor = auxiliaryData.colorName;
|
||||
QSize size = auxiliaryData.size;
|
||||
QRect rect({0, 0}, size);
|
||||
|
||||
QByteArray fontData(fileToByteArray(QString(name)));
|
||||
@@ -86,8 +76,7 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
|
||||
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
|
||||
if (!families.isEmpty()) {
|
||||
QString fontFamily = families.first();
|
||||
if (text.contains("%1"))
|
||||
text = text.arg(fontFamily);
|
||||
QString text = fontFamily + "\n\n" + auxiliaryData.text;
|
||||
QFont font(fontFamily);
|
||||
font.setStyle(rawFont.style());
|
||||
font.setStyleName(rawFont.styleName());
|
||||
@@ -120,7 +109,7 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
|
||||
painter.setFont(font);
|
||||
painter.drawText(rect, flags, text);
|
||||
|
||||
captureCallback(std::move(image));
|
||||
captureCallback(std::move(image), {});
|
||||
return;
|
||||
}
|
||||
QFontDatabase::removeApplicationFont(fontId);
|
||||
@@ -129,4 +118,133 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name,
|
||||
abortCallback();
|
||||
}
|
||||
|
||||
std::pair<QImage, QImage> ImageCacheFontCollector::createImage(
|
||||
Utils::SmallStringView name,
|
||||
Utils::SmallStringView,
|
||||
const ImageCache::AuxiliaryData &auxiliaryDataValue)
|
||||
{
|
||||
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizeAuxiliaryData>(auxiliaryDataValue);
|
||||
QColor textColor = auxiliaryData.colorName;
|
||||
QSize size = auxiliaryData.size;
|
||||
QRect rect({0, 0}, size);
|
||||
|
||||
QByteArray fontData(fileToByteArray(QString(name)));
|
||||
if (!fontData.isEmpty()) {
|
||||
int fontId = QFontDatabase::addApplicationFontFromData(fontData);
|
||||
if (fontId != -1) {
|
||||
QRawFont rawFont(fontData, 10.); // Pixel size is irrelevant, we only need style/weight
|
||||
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
|
||||
if (!families.isEmpty()) {
|
||||
QString fontFamily = families.first();
|
||||
QString text = fontFamily + "\n\n" + auxiliaryData.text;
|
||||
QFont font(fontFamily);
|
||||
font.setStyle(rawFont.style());
|
||||
font.setStyleName(rawFont.styleName());
|
||||
font.setWeight(rawFont.weight());
|
||||
QImage image(size, QImage::Format_ARGB32);
|
||||
image.fill(Qt::transparent);
|
||||
int pixelSize(200);
|
||||
int flags = Qt::AlignCenter;
|
||||
while (pixelSize >= 2) {
|
||||
font.setPixelSize(pixelSize);
|
||||
QFontMetrics fm(font, &image);
|
||||
QRect bounds = fm.boundingRect(rect, flags, text);
|
||||
if (bounds.width() < rect.width() && bounds.height() < rect.height()) {
|
||||
break;
|
||||
} else {
|
||||
int newPixelSize = pixelSize - 1;
|
||||
if (bounds.width() >= rect.width())
|
||||
newPixelSize = int(qreal(pixelSize) * qreal(rect.width())
|
||||
/ qreal(bounds.width()));
|
||||
else if (bounds.height() >= rect.height())
|
||||
newPixelSize = int(qreal(pixelSize) * qreal(rect.height())
|
||||
/ qreal(bounds.height()));
|
||||
if (newPixelSize < pixelSize)
|
||||
pixelSize = newPixelSize;
|
||||
else
|
||||
--pixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
QPainter painter(&image);
|
||||
painter.setPen(textColor);
|
||||
painter.setFont(font);
|
||||
painter.drawText(rect, flags, text);
|
||||
|
||||
return {image, {}};
|
||||
}
|
||||
QFontDatabase::removeApplicationFont(fontId);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
QIcon ImageCacheFontCollector::createIcon(Utils::SmallStringView name,
|
||||
Utils::SmallStringView,
|
||||
const ImageCache::AuxiliaryData &auxiliaryDataValue)
|
||||
{
|
||||
auto &&auxiliaryData = std::get<ImageCache::FontCollectorSizesAuxiliaryData>(auxiliaryDataValue);
|
||||
QColor textColor = auxiliaryData.colorName;
|
||||
auto sizes = auxiliaryData.sizes;
|
||||
|
||||
QIcon icon;
|
||||
|
||||
QByteArray fontData(fileToByteArray(QString(name)));
|
||||
if (!fontData.isEmpty()) {
|
||||
int fontId = QFontDatabase::addApplicationFontFromData(fontData);
|
||||
if (fontId != -1) {
|
||||
QRawFont rawFont(fontData, 10.); // Pixel size is irrelevant, we only need style/weight
|
||||
const QStringList families = QFontDatabase::applicationFontFamilies(fontId);
|
||||
if (!families.isEmpty()) {
|
||||
QString fontFamily = families.first();
|
||||
QString text = auxiliaryData.text;
|
||||
QFont font(fontFamily);
|
||||
font.setStyle(rawFont.style());
|
||||
font.setStyleName(rawFont.styleName());
|
||||
font.setWeight(rawFont.weight());
|
||||
for (QSize size : sizes) {
|
||||
QPixmap pixmap(size);
|
||||
pixmap.fill(Qt::transparent);
|
||||
int pixelSize(200);
|
||||
int flags = Qt::AlignCenter;
|
||||
QRect rect({0, 0}, size);
|
||||
while (pixelSize >= 2) {
|
||||
font.setPixelSize(pixelSize);
|
||||
QFontMetrics fm(font, &pixmap);
|
||||
QRect bounds = fm.boundingRect(rect, flags, text);
|
||||
if (bounds.width() < rect.width() && bounds.height() < rect.height()) {
|
||||
break;
|
||||
} else {
|
||||
int newPixelSize = pixelSize - 1;
|
||||
if (bounds.width() >= rect.width())
|
||||
newPixelSize = int(qreal(pixelSize) * qreal(rect.width())
|
||||
/ qreal(bounds.width()));
|
||||
else if (bounds.height() >= rect.height())
|
||||
newPixelSize = int(qreal(pixelSize) * qreal(rect.height())
|
||||
/ qreal(bounds.height()));
|
||||
if (newPixelSize < pixelSize)
|
||||
pixelSize = newPixelSize;
|
||||
else
|
||||
--pixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
QPainter painter(&pixmap);
|
||||
painter.setPen(textColor);
|
||||
painter.setFont(font);
|
||||
painter.drawText(rect, flags, text);
|
||||
|
||||
icon.addPixmap(pixmap);
|
||||
}
|
||||
|
||||
} else {
|
||||
QFontDatabase::removeApplicationFont(fontId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -37,9 +37,18 @@ public:
|
||||
~ImageCacheFontCollector();
|
||||
|
||||
void start(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback) override;
|
||||
|
||||
std::pair<QImage, QImage> createImage(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData) override;
|
||||
|
||||
QIcon createIcon(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData) override;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -47,17 +47,18 @@ ImageCacheGenerator::~ImageCacheGenerator()
|
||||
waitForFinished();
|
||||
}
|
||||
|
||||
void ImageCacheGenerator::generateImage(
|
||||
Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback)
|
||||
void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
|
||||
Utils::SmallStringView extraId,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock{m_mutex};
|
||||
m_tasks.emplace_back(name,
|
||||
state,
|
||||
extraId,
|
||||
std::move(auxiliaryData),
|
||||
timeStamp,
|
||||
std::move(captureCallback),
|
||||
std::move(abortCallback));
|
||||
@@ -82,6 +83,12 @@ void ImageCacheGenerator::waitForFinished()
|
||||
if (m_backgroundThread)
|
||||
m_backgroundThread->wait();
|
||||
}
|
||||
namespace {
|
||||
Utils::PathString createId(Utils::SmallStringView name, Utils::SmallStringView extraId)
|
||||
{
|
||||
return extraId.empty() ? Utils::PathString{name} : Utils::PathString{name, "+", extraId};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ImageCacheGenerator::startGeneration()
|
||||
{
|
||||
@@ -105,18 +112,22 @@ void ImageCacheGenerator::startGeneration()
|
||||
|
||||
m_collector.start(
|
||||
task.filePath,
|
||||
task.state,
|
||||
[this, task](QImage &&image) {
|
||||
task.extraId,
|
||||
std::move(task.auxiliaryData),
|
||||
[this, task](QImage &&image, QImage &&smallImage) {
|
||||
if (image.isNull())
|
||||
task.abortCallback();
|
||||
else
|
||||
task.captureCallback(image);
|
||||
task.captureCallback(image, smallImage);
|
||||
|
||||
m_storage.storeImage(std::move(task.filePath), task.timeStamp, image);
|
||||
m_storage.storeImage(createId(task.filePath, task.extraId),
|
||||
task.timeStamp,
|
||||
image,
|
||||
smallImage);
|
||||
},
|
||||
[this, task] {
|
||||
task.abortCallback();
|
||||
m_storage.storeImage(std::move(task.filePath), task.timeStamp, {});
|
||||
m_storage.storeImage(createId(task.filePath, task.extraId), task.timeStamp, {}, {});
|
||||
});
|
||||
|
||||
std::lock_guard lock{m_mutex};
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "imagecachegeneratorinterface.h"
|
||||
|
||||
#include <imagecacheauxiliarydata.h>
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
#include <QThread>
|
||||
@@ -51,10 +52,11 @@ public:
|
||||
~ImageCacheGenerator();
|
||||
|
||||
void generateImage(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView extraId,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback) override;
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData) override;
|
||||
void clean() override;
|
||||
|
||||
void waitForFinished() override;
|
||||
@@ -64,19 +66,22 @@ private:
|
||||
{
|
||||
Task() = default;
|
||||
Task(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView extraId,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback)
|
||||
: filePath(filePath)
|
||||
, state(std::move(state))
|
||||
, extraId(std::move(extraId))
|
||||
, auxiliaryData(std::move(auxiliaryData))
|
||||
, captureCallback(std::move(captureCallback))
|
||||
, abortCallback(std::move(abortCallback))
|
||||
, timeStamp(timeStamp)
|
||||
{}
|
||||
|
||||
Utils::PathString filePath;
|
||||
Utils::SmallString state;
|
||||
Utils::SmallString extraId;
|
||||
ImageCache::AuxiliaryData auxiliaryData;
|
||||
CaptureCallback captureCallback;
|
||||
AbortCallback abortCallback;
|
||||
Sqlite::TimeStamp timeStamp;
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <imagecacheauxiliarydata.h>
|
||||
#include <sqlitetimestamp.h>
|
||||
#include <utils/smallstringview.h>
|
||||
|
||||
@@ -35,14 +36,15 @@ namespace QmlDesigner {
|
||||
class ImageCacheGeneratorInterface
|
||||
{
|
||||
public:
|
||||
using CaptureCallback = std::function<void(const QImage &image)>;
|
||||
using CaptureCallback = std::function<void(const QImage &image, const QImage &smallImage)>;
|
||||
using AbortCallback = std::function<void()>;
|
||||
|
||||
virtual void generateImage(Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
Utils::SmallStringView extraId,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback)
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData)
|
||||
= 0;
|
||||
|
||||
virtual void clean() = 0;
|
||||
|
@@ -34,8 +34,8 @@
|
||||
#include <sqlitewritestatement.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QImageReader>
|
||||
#include <QImageWriter>
|
||||
#include <QIcon>
|
||||
#include <QImage>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
|
||||
ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
|
||||
{
|
||||
try {
|
||||
Sqlite::DeferredTransaction transaction{database};
|
||||
@@ -62,21 +62,37 @@ public:
|
||||
|
||||
transaction.commit();
|
||||
|
||||
if (optionalBlob) {
|
||||
QBuffer buffer{&optionalBlob->byteArray};
|
||||
QImageReader reader{&buffer, "PNG"};
|
||||
|
||||
return Entry{reader.read(), true};
|
||||
}
|
||||
if (optionalBlob)
|
||||
return {readImage(optionalBlob->byteArray), true};
|
||||
|
||||
return {};
|
||||
|
||||
} catch (const Sqlite::StatementIsBusy &) {
|
||||
return fetchImage(name, minimumTimeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
|
||||
ImageEntry fetchSmallImage(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp minimumTimeStamp) const override
|
||||
{
|
||||
try {
|
||||
Sqlite::DeferredTransaction transaction{database};
|
||||
|
||||
auto optionalBlob = selectSmallImageStatement.template value<Sqlite::ByteArrayBlob>(
|
||||
name, minimumTimeStamp.value);
|
||||
|
||||
transaction.commit();
|
||||
|
||||
if (optionalBlob)
|
||||
return ImageEntry{readImage(optionalBlob->byteArray), true};
|
||||
|
||||
return {};
|
||||
|
||||
} catch (const Sqlite::StatementIsBusy &) {
|
||||
return fetchSmallImage(name, minimumTimeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
IconEntry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
|
||||
{
|
||||
try {
|
||||
Sqlite::DeferredTransaction transaction{database};
|
||||
@@ -86,12 +102,8 @@ public:
|
||||
|
||||
transaction.commit();
|
||||
|
||||
if (optionalBlob) {
|
||||
QBuffer buffer{&optionalBlob->byteArray};
|
||||
QImageReader reader{&buffer, "PNG"};
|
||||
|
||||
return Entry{reader.read(), true};
|
||||
}
|
||||
if (optionalBlob)
|
||||
return {readIcon(optionalBlob->byteArray), true};
|
||||
|
||||
return {};
|
||||
|
||||
@@ -100,29 +112,40 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) override
|
||||
void storeImage(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp newTimeStamp,
|
||||
const QImage &image,
|
||||
const QImage &smallImage) override
|
||||
{
|
||||
try {
|
||||
Sqlite::ImmediateTransaction transaction{database};
|
||||
|
||||
if (image.isNull()) {
|
||||
upsertImageStatement.write(name,
|
||||
newTimeStamp.value,
|
||||
Sqlite::NullValue{},
|
||||
Sqlite::NullValue{});
|
||||
} else {
|
||||
QSize iconSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
|
||||
Qt::KeepAspectRatio);
|
||||
QImage icon = image.scaled(iconSize);
|
||||
upsertImageStatement.write(name,
|
||||
newTimeStamp.value,
|
||||
Sqlite::BlobView{createImageBuffer(image)->data()},
|
||||
Sqlite::BlobView{createImageBuffer(icon)->data()});
|
||||
}
|
||||
auto imageBuffer = createBuffer(image);
|
||||
auto smallImageBuffer = createBuffer(smallImage);
|
||||
upsertImageStatement.write(name,
|
||||
newTimeStamp.value,
|
||||
createBlobView(imageBuffer.get()),
|
||||
createBlobView(smallImageBuffer.get()));
|
||||
|
||||
transaction.commit();
|
||||
|
||||
} catch (const Sqlite::StatementIsBusy &) {
|
||||
return storeImage(name, newTimeStamp, image);
|
||||
return storeImage(name, newTimeStamp, image, smallImage);
|
||||
}
|
||||
}
|
||||
|
||||
void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon)
|
||||
{
|
||||
try {
|
||||
Sqlite::ImmediateTransaction transaction{database};
|
||||
|
||||
auto iconBuffer = createBuffer(icon);
|
||||
upsertIconStatement.write(name, newTimeStamp.value, createBlobView(iconBuffer.get()));
|
||||
|
||||
transaction.commit();
|
||||
|
||||
} catch (const Sqlite::StatementIsBusy &) {
|
||||
return storeIcon(name, newTimeStamp, icon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,41 +179,113 @@ private:
|
||||
|
||||
void createImagesTable(DatabaseType &database)
|
||||
{
|
||||
Sqlite::Table table;
|
||||
table.setUseIfNotExists(true);
|
||||
table.setName("images");
|
||||
table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
|
||||
table.addColumn("name", Sqlite::ColumnType::Text, {Sqlite::NotNull{}, Sqlite::Unique{}});
|
||||
table.addColumn("mtime", Sqlite::ColumnType::Integer);
|
||||
table.addColumn("image", Sqlite::ColumnType::Blob);
|
||||
table.addColumn("icon", Sqlite::ColumnType::Blob);
|
||||
Sqlite::Table imageTable;
|
||||
imageTable.setUseIfNotExists(true);
|
||||
imageTable.setName("images");
|
||||
imageTable.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
|
||||
imageTable.addColumn("name",
|
||||
Sqlite::ColumnType::Text,
|
||||
{Sqlite::NotNull{}, Sqlite::Unique{}});
|
||||
imageTable.addColumn("mtime", Sqlite::ColumnType::Integer);
|
||||
imageTable.addColumn("image", Sqlite::ColumnType::Blob);
|
||||
imageTable.addColumn("smallImage", Sqlite::ColumnType::Blob);
|
||||
|
||||
table.initialize(database);
|
||||
imageTable.initialize(database);
|
||||
|
||||
Sqlite::Table iconTable;
|
||||
iconTable.setUseIfNotExists(true);
|
||||
iconTable.setName("icons");
|
||||
iconTable.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
|
||||
iconTable.addColumn("name",
|
||||
Sqlite::ColumnType::Text,
|
||||
{Sqlite::NotNull{}, Sqlite::Unique{}});
|
||||
iconTable.addColumn("mtime", Sqlite::ColumnType::Integer);
|
||||
iconTable.addColumn("icon", Sqlite::ColumnType::Blob);
|
||||
|
||||
iconTable.initialize(database);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<QBuffer> createImageBuffer(const QImage &image)
|
||||
Sqlite::BlobView createBlobView(QBuffer *buffer)
|
||||
{
|
||||
if (buffer)
|
||||
return Sqlite::BlobView{buffer->data()};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static std::unique_ptr<QBuffer> createBuffer(const QImage &image)
|
||||
{
|
||||
if (image.isNull())
|
||||
return {};
|
||||
|
||||
auto buffer = std::make_unique<QBuffer>();
|
||||
buffer->open(QIODevice::WriteOnly);
|
||||
QImageWriter writer{buffer.get(), "PNG"};
|
||||
writer.write(image);
|
||||
QDataStream out{buffer.get()};
|
||||
|
||||
out << image;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static std::unique_ptr<QBuffer> createBuffer(const QIcon &icon)
|
||||
{
|
||||
if (icon.isNull())
|
||||
return {};
|
||||
|
||||
auto buffer = std::make_unique<QBuffer>();
|
||||
buffer->open(QIODevice::WriteOnly);
|
||||
QDataStream out{buffer.get()};
|
||||
|
||||
out << icon;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static QIcon readIcon(const QByteArray &byteArray)
|
||||
{
|
||||
QIcon icon;
|
||||
QBuffer buffer;
|
||||
buffer.setData(byteArray);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QDataStream in{&buffer};
|
||||
|
||||
in >> icon;
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
static QImage readImage(const QByteArray &byteArray)
|
||||
{
|
||||
QImage image;
|
||||
QBuffer buffer;
|
||||
buffer.setData(byteArray);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QDataStream in{&buffer};
|
||||
|
||||
in >> image;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
public:
|
||||
DatabaseType &database;
|
||||
Initializer initializer{database};
|
||||
Sqlite::ImmediateNonThrowingDestructorTransaction transaction{database};
|
||||
mutable ReadStatement selectImageStatement{
|
||||
"SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database};
|
||||
mutable ReadStatement selectSmallImageStatement{
|
||||
"SELECT smallImage FROM images WHERE name=?1 AND mtime >= ?2", database};
|
||||
mutable ReadStatement selectIconStatement{
|
||||
"SELECT icon FROM images WHERE name=?1 AND mtime >= ?2", database};
|
||||
"SELECT icon FROM icons WHERE name=?1 AND mtime >= ?2", database};
|
||||
WriteStatement upsertImageStatement{
|
||||
"INSERT INTO images(name, mtime, image, icon) VALUES (?1, ?2, ?3, ?4) ON "
|
||||
"INSERT INTO images(name, mtime, image, smallImage) VALUES (?1, ?2, ?3, ?4) ON "
|
||||
"CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
|
||||
"icon=excluded.icon",
|
||||
"smallImage=excluded.smallImage",
|
||||
database};
|
||||
WriteStatement upsertIconStatement{
|
||||
"INSERT INTO icons(name, mtime, icon) VALUES (?1, ?2, ?3) ON "
|
||||
"CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, icon=excluded.icon",
|
||||
database};
|
||||
};
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QIcon>
|
||||
#include <QImage>
|
||||
|
||||
#include <sqlitetimestamp.h>
|
||||
@@ -32,23 +33,40 @@
|
||||
|
||||
namespace QmlDesigner {
|
||||
namespace Internal {
|
||||
class ImageCacheStorageEntry
|
||||
class ImageCacheStorageImageEntry
|
||||
{
|
||||
public:
|
||||
public:
|
||||
QImage image;
|
||||
bool hasEntry = false;
|
||||
};
|
||||
|
||||
class ImageCacheStorageIconEntry
|
||||
{
|
||||
public:
|
||||
QIcon icon;
|
||||
bool hasEntry = false;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
class ImageCacheStorageInterface
|
||||
{
|
||||
public:
|
||||
using Entry = Internal::ImageCacheStorageEntry;
|
||||
using ImageEntry = Internal::ImageCacheStorageImageEntry;
|
||||
using IconEntry = Internal::ImageCacheStorageIconEntry;
|
||||
|
||||
virtual Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
|
||||
virtual Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
|
||||
virtual void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) = 0;
|
||||
virtual ImageEntry fetchImage(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp minimumTimeStamp) const = 0;
|
||||
virtual ImageEntry fetchSmallImage(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp minimumTimeStamp) const = 0;
|
||||
virtual IconEntry fetchIcon(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp minimumTimeStamp) const = 0;
|
||||
virtual void storeImage(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp newTimeStamp,
|
||||
const QImage &image,
|
||||
const QImage &smallImage)
|
||||
= 0;
|
||||
virtual void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) = 0;
|
||||
virtual void walCheckpointFull() = 0;
|
||||
|
||||
protected:
|
||||
|
@@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "synchronousimagecache.h"
|
||||
|
||||
#include "imagecachecollectorinterface.h"
|
||||
#include "imagecachestorage.h"
|
||||
#include "timestampprovider.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace {
|
||||
|
||||
Utils::PathString createId(Utils::PathString filePath, Utils::SmallString extraId)
|
||||
{
|
||||
return extraId.empty() ? Utils::PathString{filePath} : Utils::PathString{filePath, "+", extraId};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
QImage SynchronousImageCache::image(Utils::PathString filePath,
|
||||
Utils::SmallString extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
{
|
||||
const auto id = createId(filePath, extraId);
|
||||
|
||||
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
|
||||
const auto entry = m_storage.fetchImage(id, timeStamp);
|
||||
|
||||
if (entry.hasEntry)
|
||||
return entry.image;
|
||||
|
||||
const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
|
||||
|
||||
m_storage.storeImage(id, timeStamp, image, smallImage);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage SynchronousImageCache::smallImage(Utils::PathString filePath,
|
||||
Utils::SmallString extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
{
|
||||
const auto id = createId(filePath, extraId);
|
||||
|
||||
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
|
||||
const auto entry = m_storage.fetchSmallImage(id, timeStamp);
|
||||
|
||||
if (entry.hasEntry)
|
||||
return entry.image;
|
||||
|
||||
const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData);
|
||||
|
||||
m_storage.storeImage(id, timeStamp, image, smallImage);
|
||||
|
||||
return smallImage;
|
||||
}
|
||||
|
||||
QIcon SynchronousImageCache::icon(Utils::PathString filePath,
|
||||
Utils::SmallString extraId,
|
||||
const ImageCache::AuxiliaryData &auxiliaryData)
|
||||
{
|
||||
const auto id = createId(filePath, extraId);
|
||||
|
||||
const auto timeStamp = m_timeStampProvider.timeStamp(filePath);
|
||||
const auto entry = m_storage.fetchIcon(id, timeStamp);
|
||||
|
||||
if (entry.hasEntry)
|
||||
return entry.icon;
|
||||
|
||||
const auto icon = m_collector.createIcon(filePath, extraId, auxiliaryData);
|
||||
|
||||
m_storage.storeIcon(id, timeStamp, icon);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -25,7 +25,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagecacheinterface.h"
|
||||
#include "asynchronousimagecacheinterface.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
@@ -37,70 +37,78 @@ namespace QmlDesigner {
|
||||
class TimeStampProviderInterface;
|
||||
class ImageCacheStorageInterface;
|
||||
class ImageCacheGeneratorInterface;
|
||||
class ImageCacheCollectorInterface;
|
||||
|
||||
class ImageCache final : public ImageCacheInterface
|
||||
class AsynchronousImageCache final : public AsynchronousImageCacheInterface
|
||||
{
|
||||
public:
|
||||
using CaptureCallback = std::function<void(const QImage &)>;
|
||||
using CaptureImageCallback = std::function<void(const QImage &)>;
|
||||
using AbortCallback = std::function<void()>;
|
||||
|
||||
~ImageCache();
|
||||
~AsynchronousImageCache();
|
||||
|
||||
ImageCache(ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider);
|
||||
AsynchronousImageCache(ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider);
|
||||
|
||||
void requestImage(Utils::PathString name,
|
||||
CaptureCallback captureCallback,
|
||||
CaptureImageCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString state = {}) override;
|
||||
void requestIcon(Utils::PathString name,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString state = {}) override;
|
||||
Utils::SmallString extraId = {},
|
||||
ImageCache::AuxiliaryData auxiliaryData = {}) override;
|
||||
void requestSmallImage(Utils::PathString name,
|
||||
CaptureImageCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString extraId = {},
|
||||
ImageCache::AuxiliaryData auxiliaryData = {}) override;
|
||||
|
||||
void clean();
|
||||
void waitForFinished();
|
||||
|
||||
private:
|
||||
enum class RequestType { Image, Icon };
|
||||
enum class RequestType { Image, SmallImage, Icon };
|
||||
struct Entry
|
||||
{
|
||||
Entry() = default;
|
||||
Entry(Utils::PathString name,
|
||||
Utils::SmallString state,
|
||||
CaptureCallback &&captureCallback,
|
||||
Utils::SmallString extraId,
|
||||
CaptureImageCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData,
|
||||
RequestType requestType)
|
||||
: name{std::move(name)}
|
||||
, state{std::move(state)}
|
||||
, extraId{std::move(extraId)}
|
||||
, captureCallback{std::move(captureCallback)}
|
||||
, abortCallback{std::move(abortCallback)}
|
||||
, auxiliaryData{std::move(auxiliaryData)}
|
||||
, requestType{requestType}
|
||||
{}
|
||||
|
||||
Utils::PathString name;
|
||||
Utils::SmallString state;
|
||||
CaptureCallback captureCallback;
|
||||
Utils::SmallString extraId;
|
||||
CaptureImageCallback captureCallback;
|
||||
AbortCallback abortCallback;
|
||||
ImageCache::AuxiliaryData auxiliaryData;
|
||||
RequestType requestType = RequestType::Image;
|
||||
};
|
||||
|
||||
std::tuple<bool, Entry> getEntry();
|
||||
void addEntry(Utils::PathString &&name,
|
||||
Utils::SmallString &&state,
|
||||
CaptureCallback &&captureCallback,
|
||||
Utils::SmallString &&extraId,
|
||||
CaptureImageCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback,
|
||||
ImageCache::AuxiliaryData &&auxiliaryData,
|
||||
RequestType requestType);
|
||||
void clearEntries();
|
||||
void waitForEntries();
|
||||
void stopThread();
|
||||
bool isRunning();
|
||||
static void request(Utils::SmallStringView name,
|
||||
Utils::SmallStringView state,
|
||||
ImageCache::RequestType requestType,
|
||||
ImageCache::CaptureCallback captureCallback,
|
||||
ImageCache::AbortCallback abortCallback,
|
||||
Utils::SmallStringView extraId,
|
||||
AsynchronousImageCache::RequestType requestType,
|
||||
AsynchronousImageCache::CaptureImageCallback captureCallback,
|
||||
AsynchronousImageCache::AbortCallback abortCallback,
|
||||
ImageCache::AuxiliaryData auxiliaryData,
|
||||
ImageCacheStorageInterface &storage,
|
||||
ImageCacheGeneratorInterface &generator,
|
||||
TimeStampProviderInterface &timeStampProvider);
|
@@ -25,13 +25,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imagecacheauxiliarydata.h"
|
||||
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
#include <QImage>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class ImageCacheInterface
|
||||
class AsynchronousImageCacheInterface
|
||||
{
|
||||
public:
|
||||
using CaptureCallback = std::function<void(const QImage &)>;
|
||||
@@ -40,18 +42,20 @@ public:
|
||||
virtual void requestImage(Utils::PathString name,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString state = {})
|
||||
Utils::SmallString extraId = {},
|
||||
ImageCache::AuxiliaryData auxiliaryData = {})
|
||||
= 0;
|
||||
virtual void requestIcon(Utils::PathString name,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString state = {})
|
||||
virtual void requestSmallImage(Utils::PathString name,
|
||||
CaptureCallback captureCallback,
|
||||
AbortCallback abortCallback,
|
||||
Utils::SmallString extraId = {},
|
||||
ImageCache::AuxiliaryData auxiliaryData = {})
|
||||
= 0;
|
||||
|
||||
void clean();
|
||||
|
||||
protected:
|
||||
~ImageCacheInterface() = default;
|
||||
~AsynchronousImageCacheInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 <utils/span.h>
|
||||
#include <utils/variant.h>
|
||||
|
||||
#include <QSize>
|
||||
#include <QString>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
namespace ImageCache {
|
||||
|
||||
class NullAuxiliaryData
|
||||
{};
|
||||
|
||||
class FontCollectorSizeAuxiliaryData
|
||||
{
|
||||
public:
|
||||
QSize size;
|
||||
QString colorName;
|
||||
QString text;
|
||||
};
|
||||
|
||||
class FontCollectorSizesAuxiliaryData
|
||||
{
|
||||
public:
|
||||
Utils::span<const QSize> sizes;
|
||||
QString colorName;
|
||||
QString text;
|
||||
};
|
||||
|
||||
using AuxiliaryData = std::variant<NullAuxiliaryData, FontCollectorSizeAuxiliaryData, FontCollectorSizesAuxiliaryData>;
|
||||
|
||||
} // namespace ImageCache
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -0,0 +1,76 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "imagecacheauxiliarydata.h"
|
||||
|
||||
#include <utils/smallstring.h>
|
||||
|
||||
#include <QImage>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
class TimeStampProviderInterface;
|
||||
class ImageCacheStorageInterface;
|
||||
class ImageCacheGeneratorInterface;
|
||||
class ImageCacheCollectorInterface;
|
||||
|
||||
class SynchronousImageCache
|
||||
{
|
||||
public:
|
||||
using CaptureImageCallback = std::function<void(const QImage &)>;
|
||||
using AbortCallback = std::function<void()>;
|
||||
|
||||
SynchronousImageCache(ImageCacheStorageInterface &storage,
|
||||
TimeStampProviderInterface &timeStampProvider,
|
||||
ImageCacheCollectorInterface &collector)
|
||||
: m_storage(storage)
|
||||
, m_timeStampProvider(timeStampProvider)
|
||||
, m_collector(collector)
|
||||
{}
|
||||
|
||||
QImage image(Utils::PathString filePath,
|
||||
Utils::SmallString extraId = {},
|
||||
const ImageCache::AuxiliaryData &auxiliaryData = {});
|
||||
QImage smallImage(Utils::PathString filePath,
|
||||
Utils::SmallString extraId = {},
|
||||
const ImageCache::AuxiliaryData &auxiliaryData = {});
|
||||
QIcon icon(Utils::PathString filePath,
|
||||
Utils::SmallString extraId = {},
|
||||
const ImageCache::AuxiliaryData &auxiliaryData = {});
|
||||
|
||||
private:
|
||||
ImageCacheStorageInterface &m_storage;
|
||||
TimeStampProviderInterface &m_timeStampProvider;
|
||||
ImageCacheCollectorInterface &m_collector;
|
||||
};
|
||||
|
||||
} // namespace QmlDesigner
|
@@ -47,7 +47,7 @@ class Edit3DView;
|
||||
namespace Internal { class DesignModeWidget; }
|
||||
|
||||
class ViewManagerData;
|
||||
class ImageCache;
|
||||
class AsynchronousImageCache;
|
||||
|
||||
class QMLDESIGNERCORE_EXPORT ViewManager
|
||||
{
|
||||
@@ -104,7 +104,7 @@ public:
|
||||
void disableStandardViews();
|
||||
void enableStandardViews();
|
||||
|
||||
ImageCache &imageCache();
|
||||
AsynchronousImageCache &imageCache();
|
||||
|
||||
private: // functions
|
||||
Q_DISABLE_COPY(ViewManager)
|
||||
|
@@ -453,7 +453,7 @@ void ViewManager::enableStandardViews()
|
||||
attachViewsExceptRewriterAndComponetView();
|
||||
}
|
||||
|
||||
ImageCache &ViewManager::imageCache()
|
||||
AsynchronousImageCache &ViewManager::imageCache()
|
||||
{
|
||||
return d->itemLibraryView.imageCache();
|
||||
}
|
||||
|
@@ -567,7 +567,7 @@ void QmlDesignerPlugin::emitUsageStatisticsContextAction(const QString &identifi
|
||||
emitUsageStatistics(Constants::EVENT_ACTION_EXECUTED + identifier);
|
||||
}
|
||||
|
||||
ImageCache &QmlDesignerPlugin::imageCache()
|
||||
AsynchronousImageCache &QmlDesignerPlugin::imageCache()
|
||||
{
|
||||
return m_instance->d->viewManager.imageCache();
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ namespace Core {
|
||||
namespace QmlDesigner {
|
||||
|
||||
class QmlDesignerPluginPrivate;
|
||||
class ImageCache;
|
||||
class AsynchronousImageCache;
|
||||
|
||||
namespace Internal { class DesignModeWidget; }
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
static void emitUsageStatisticsContextAction(const QString &identifier);
|
||||
static void emitUsageStatisticsTime(const QString &identifier, int elapsed);
|
||||
|
||||
static ImageCache &imageCache();
|
||||
static AsynchronousImageCache &imageCache();
|
||||
|
||||
signals:
|
||||
void usageStatisticsNotifier(const QString &identifier);
|
||||
|
@@ -414,13 +414,15 @@ Project {
|
||||
"pluginmanager/widgetpluginmanager.h",
|
||||
"pluginmanager/widgetpluginpath.cpp",
|
||||
"pluginmanager/widgetpluginpath.h",
|
||||
"include/imagecache.h",
|
||||
"include/imagecacheinterface.h",
|
||||
"include/asynchronousimagecache.h",
|
||||
"include/synchronousimagecache.h",
|
||||
"include/imagecacheauxiliarydata.h",
|
||||
"include/asynchronousimagecacheinterface.h",
|
||||
"imagecache/imagecachecollector.cpp",
|
||||
"imagecache/imagecachecollector.h",
|
||||
"imagecache/imagecachefontcollector.cpp",
|
||||
"imagecache/imagecachefontcollector.h",
|
||||
"imagecache/imagecache.cpp",
|
||||
"imagecache/asynchronousimagecache.cpp",
|
||||
"imagecache/imagecachecollectorinterface.h",
|
||||
"imagecache/imagecacheconnectionmanager.cpp",
|
||||
"imagecache/imagecacheconnectionmanager.h",
|
||||
|
@@ -32,7 +32,8 @@ SOURCES += \
|
||||
$$PWD/designercore/model/annotation.cpp \
|
||||
$$PWD/designercore/rewritertransaction.cpp \
|
||||
$$PWD/components/listmodeleditor/listmodeleditormodel.cpp \
|
||||
$$PWD/designercore/imagecache/imagecache.cpp \
|
||||
$$PWD/designercore/imagecache/asynchronousimagecache.cpp \
|
||||
$$PWD/designercore/imagecache/synchronousimagecache.cpp \
|
||||
$$PWD/designercore/imagecache/imagecachegenerator.cpp
|
||||
|
||||
HEADERS += \
|
||||
@@ -42,8 +43,10 @@ HEADERS += \
|
||||
$$PWD/designercore/imagecache/imagecachestorageinterface.h \
|
||||
$$PWD/designercore/imagecache/imagecachegeneratorinterface.h \
|
||||
$$PWD/designercore/imagecache/timestampproviderinterface.h \
|
||||
$$PWD/designercore/include/imagecache.h \
|
||||
$$PWD/designercore/include/imagecacheinterface.h \
|
||||
$$PWD/designercore/include/asynchronousimagecache.h \
|
||||
$$PWD/designercore/include/synchronousimagecache.h \
|
||||
$$PWD/designercore/include/imagecacheauxiliarydata.h \
|
||||
$$PWD/designercore/include/asynchronousimagecacheinterface.h \
|
||||
$$PWD/designercore/include/modelnode.h \
|
||||
$$PWD/designercore/include/model.h \
|
||||
$$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \
|
||||
|
@@ -179,7 +179,8 @@ add_qtc_test(unittest GTEST
|
||||
sqlstatementbuilder-test.cpp
|
||||
createtablesqlstatementbuilder-test.cpp
|
||||
sqlitevalue-test.cpp
|
||||
imagecache-test.cpp
|
||||
asynchronousimagecache-test.cpp
|
||||
synchronousimagecache-test.cpp
|
||||
imagecachegenerator-test.cpp
|
||||
imagecachestorage-test.cpp
|
||||
sqlitedatabasemock.h
|
||||
@@ -405,8 +406,10 @@ extend_qtc_test(unittest
|
||||
model/signalhandlerproperty.cpp include/signalhandlerproperty.h
|
||||
model/variantproperty.cpp include/variantproperty.h
|
||||
rewritertransaction.cpp rewritertransaction.h
|
||||
imagecache/imagecache.cpp include/imagecache.h
|
||||
include/imagecacheinterface.h
|
||||
include/imagecacheauxiliarydata.h
|
||||
imagecache/synchronousimagecache.cpp include/synchronousimagecache.h
|
||||
imagecache/asynchronousimagecache.cpp include/asynchronousimagecache.h
|
||||
include/asynchronousimagecacheinterface.h
|
||||
imagecache/imagecachecollectorinterface.h
|
||||
imagecache/imagecachegenerator.cpp imagecache/imagecachegenerator.h
|
||||
imagecache/imagecachegeneratorinterface.h
|
||||
|
467
tests/unit/unittest/asynchronousimagecache-test.cpp
Normal file
467
tests/unit/unittest/asynchronousimagecache-test.cpp
Normal file
@@ -0,0 +1,467 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "googletest.h"
|
||||
|
||||
#include "mockimagecachegenerator.h"
|
||||
#include "mockimagecachestorage.h"
|
||||
#include "mocktimestampprovider.h"
|
||||
#include "notification.h"
|
||||
|
||||
#include <asynchronousimagecache.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class AsynchronousImageCache : public testing::Test
|
||||
{
|
||||
protected:
|
||||
Notification notification;
|
||||
Notification waitInThread;
|
||||
NiceMock<MockFunction<void()>> mockAbortCallback;
|
||||
NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback;
|
||||
NiceMock<MockImageCacheStorage> mockStorage;
|
||||
NiceMock<MockImageCacheGenerator> mockGenerator;
|
||||
NiceMock<MockTimeStampProvider> mockTimeStampProvider;
|
||||
QmlDesigner::AsynchronousImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider};
|
||||
QImage image1{10, 10, QImage::Format_ARGB32};
|
||||
QImage smallImage1{1, 1, QImage::Format_ARGB32};
|
||||
};
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageFetchesImageFromStorageWithTimeStamp)
|
||||
{
|
||||
EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillRepeatedly(Return(Sqlite::TimeStamp{123}));
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageCallsCaptureCallbackWithImageFromStorage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image1, true}));
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageCallsAbortCallbackWithoutImage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, true}));
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&callback, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageCallsCaptureCallbackWithImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) {
|
||||
callback(QImage{image1}, QImage{smallImage1});
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageCallsAbortCallbackFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) {
|
||||
abortCallback();
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call());
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageFetchesSmallImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageFetchesSmallImageFromStorageWithTimeStamp)
|
||||
{
|
||||
EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillRepeatedly(Return(Sqlite::TimeStamp{123}));
|
||||
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, false};
|
||||
});
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageCallsCaptureCallbackWithImageFromStorage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true}));
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(smallImage1))).WillRepeatedly([&](const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageCallsAbortCallbackWithoutSmallImage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, true}));
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&callback, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageCallsCaptureCallbackWithImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) {
|
||||
callback(QImage{image1}, QImage{smallImage1});
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(smallImage1)));
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageCallsAbortCallbackFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) {
|
||||
abortCallback();
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call());
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, CleanRemovesEntries)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&captureCallback, auto &&, auto) {
|
||||
captureCallback(QImage{}, QImage{});
|
||||
waitInThread.wait();
|
||||
});
|
||||
cache.requestSmallImage("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(_)).Times(AtMost(1));
|
||||
|
||||
cache.requestSmallImage("/path/to/Component3.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.clean();
|
||||
waitInThread.notify();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, CleanCallsAbort)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(_, _, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&mockCaptureCallback, auto &&, auto) {
|
||||
waitInThread.wait();
|
||||
});
|
||||
cache.requestSmallImage("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.requestSmallImage("/path/to/Component2.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).Times(AtLeast(2));
|
||||
|
||||
cache.requestSmallImage("/path/to/Component3.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.clean();
|
||||
waitInThread.notify();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, CleanCallsGeneratorClean)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, clean()).Times(AtLeast(1));
|
||||
|
||||
cache.clean();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, AfterCleanNewJobsWorks)
|
||||
{
|
||||
cache.clean();
|
||||
|
||||
EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto &&, auto) { notification.notify(); });
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, WaitForFinished)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(_, _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image1, true}));
|
||||
cache.requestImage("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.requestImage("/path/to/Component2.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(_)).Times(2);
|
||||
|
||||
cache.waitForFinished();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, WaitForFinishedInGenerator)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, waitForFinished());
|
||||
|
||||
cache.waitForFinished();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageWithExtraIdFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml+extraId1"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageWithExtraIdFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageWithExtraIdRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(
|
||||
Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageWithExtraIdRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(
|
||||
Eq("/path/to/Component.qml"), Eq("extraId1"), Eq(Sqlite::TimeStamp{123}), _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestImageWithAuxiliaryDataRequestImageFromGenerator)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"),
|
||||
Eq("extraId1"),
|
||||
Eq(Sqlite::TimeStamp{123}),
|
||||
_,
|
||||
_,
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName,
|
||||
Eq(u"color"))))))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(AsynchronousImageCache, RequestSmallImageWithAuxiliaryDataRequestImageFromGenerator)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"),
|
||||
Eq("extraId1"),
|
||||
Eq(Sqlite::TimeStamp{123}),
|
||||
_,
|
||||
_,
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName,
|
||||
Eq(u"color"))))))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); });
|
||||
|
||||
cache.requestSmallImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
} // namespace
|
@@ -1566,11 +1566,16 @@ std::ostream &operator<<(std::ostream &out, const VariantProperty &property)
|
||||
}
|
||||
|
||||
namespace Internal {
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry)
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageImageEntry &entry)
|
||||
{
|
||||
return out << "(" << entry.image << ", " << entry.hasEntry << ")";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageIconEntry &entry)
|
||||
{
|
||||
return out << "(" << entry.icon << ", " << entry.hasEntry << ")";
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -379,9 +379,11 @@ std::ostream &operator<<(std::ostream &out, const ModelNode &node);
|
||||
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
|
||||
|
||||
namespace Internal {
|
||||
class ImageCacheStorageEntry;
|
||||
class ImageCacheStorageImageEntry;
|
||||
class ImageCacheStorageIconEntry;
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry);
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageImageEntry &entry);
|
||||
std::ostream &operator<<(std::ostream &out, const ImageCacheStorageIconEntry &entry);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace QmlDesigner
|
||||
|
@@ -23,9 +23,10 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QTextCharFormat>
|
||||
#include <QDebug>
|
||||
|
||||
#include <gtest/gtest-printers.h>
|
||||
|
||||
@@ -90,6 +91,16 @@ std::ostream &operator<<(std::ostream &out, const QImage &image)
|
||||
return out << "(" << image.width() << ", " << image.height() << ", " << image.format() << ")";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const QIcon &icon)
|
||||
{
|
||||
return out << "(";
|
||||
|
||||
for (const QSize &size : icon.availableSizes())
|
||||
out << "(" << size.width() << ", " << size.height() << "), ";
|
||||
|
||||
out << icon.cacheKey() << ")";
|
||||
}
|
||||
|
||||
void PrintTo(const QString &text, std::ostream *os)
|
||||
{
|
||||
*os << text;
|
||||
|
@@ -35,12 +35,14 @@ class QVariant;
|
||||
class QString;
|
||||
class QTextCharFormat;
|
||||
class QImage;
|
||||
class QIcon;
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const QVariant &QVariant);
|
||||
std::ostream &operator<<(std::ostream &out, const QString &text);
|
||||
std::ostream &operator<<(std::ostream &out, const QByteArray &byteArray);
|
||||
std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format);
|
||||
std::ostream &operator<<(std::ostream &out, const QImage &image);
|
||||
std::ostream &operator<<(std::ostream &out, const QIcon &icon);
|
||||
|
||||
void PrintTo(const QString &text, std::ostream *os);
|
||||
void PrintTo(const QVariant &variant, std::ostream *os);
|
||||
|
@@ -1,391 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "googletest.h"
|
||||
|
||||
#include "mockimagecachegenerator.h"
|
||||
#include "mockimagecachestorage.h"
|
||||
#include "mocktimestampprovider.h"
|
||||
#include "notification.h"
|
||||
|
||||
#include <imagecache.h>
|
||||
|
||||
namespace {
|
||||
|
||||
class ImageCache : public testing::Test
|
||||
{
|
||||
protected:
|
||||
Notification notification;
|
||||
Notification waitInThread;
|
||||
NiceMock<MockFunction<void()>> mockAbortCallback;
|
||||
NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback;
|
||||
NiceMock<MockImageCacheStorage> mockStorage;
|
||||
NiceMock<MockImageCacheGenerator> mockGenerator;
|
||||
NiceMock<MockTimeStampProvider> mockTimeStampProvider;
|
||||
QmlDesigner::ImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider};
|
||||
QImage image1{10, 10, QImage::Format_ARGB32};
|
||||
};
|
||||
|
||||
TEST_F(ImageCache, RequestImageFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageFetchesImageFromStorageWithTimeStamp)
|
||||
{
|
||||
EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillRepeatedly(Return(Sqlite::TimeStamp{123}));
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromStorage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageCallsAbortCallbackWithoutImage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&callback, auto) { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&callback, auto) {
|
||||
callback(QImage{image1});
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageCallsAbortCallbackFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback) {
|
||||
abortCallback();
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call());
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconFetchesIconFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconFetchesIconFromStorageWithTimeStamp)
|
||||
{
|
||||
EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillRepeatedly(Return(Sqlite::TimeStamp{123}));
|
||||
EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
|
||||
});
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromStorage)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconCallsAbortCallbackWithoutIcon)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"), _, Eq(Sqlite::TimeStamp{123}), _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&callback, auto) { notification.notify(); });
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&callback, auto) {
|
||||
callback(QImage{image1});
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconCallsAbortCallbackFromGenerator)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback) {
|
||||
abortCallback();
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call());
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, CleanRemovesEntries)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&mockCaptureCallback, auto &&) {
|
||||
mockCaptureCallback(QImage{});
|
||||
waitInThread.wait();
|
||||
});
|
||||
cache.requestIcon("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(_)).Times(AtMost(1));
|
||||
|
||||
cache.requestIcon("/path/to/Component3.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.clean();
|
||||
waitInThread.notify();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, CleanCallsAbort)
|
||||
{
|
||||
ON_CALL(mockGenerator, generateImage(_, _, _, _, _))
|
||||
.WillByDefault(
|
||||
[&](auto, auto, auto, auto &&mockCaptureCallback, auto &&) { waitInThread.wait(); });
|
||||
cache.requestIcon("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.requestIcon("/path/to/Component2.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockAbortCallback, Call()).Times(AtLeast(2));
|
||||
|
||||
cache.requestIcon("/path/to/Component3.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.clean();
|
||||
waitInThread.notify();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, CleanCallsGeneratorClean)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, clean()).Times(AtLeast(1));
|
||||
|
||||
cache.clean();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, AfterCleanNewJobsWorks)
|
||||
{
|
||||
cache.clean();
|
||||
|
||||
EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&, auto &&) { notification.notify(); });
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, WaitForFinished)
|
||||
{
|
||||
ON_CALL(mockStorage, fetchImage(_, _))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
|
||||
cache.requestImage("/path/to/Component1.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
cache.requestImage("/path/to/Component2.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction());
|
||||
|
||||
EXPECT_CALL(mockCaptureCallback, Call(_)).Times(2);
|
||||
|
||||
cache.waitForFinished();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, WaitForFinishedInGenerator)
|
||||
{
|
||||
EXPECT_CALL(mockGenerator, waitForFinished());
|
||||
|
||||
cache.waitForFinished();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageWithStateFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml+state1"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"state1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestIconWithStateFetchesImageFromStorage)
|
||||
{
|
||||
EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml+state1"), _))
|
||||
.WillRepeatedly([&](Utils::SmallStringView, auto) {
|
||||
notification.notify();
|
||||
return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
|
||||
});
|
||||
|
||||
cache.requestIcon("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"state1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCache, RequestImageWithStateRequestImageFromGenerator)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
|
||||
EXPECT_CALL(mockGenerator,
|
||||
generateImage(Eq("/path/to/Component.qml"), Eq("state1"), Eq(Sqlite::TimeStamp{123}), _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto &&callback, auto) { notification.notify(); });
|
||||
|
||||
cache.requestImage("/path/to/Component.qml",
|
||||
mockCaptureCallback.AsStdFunction(),
|
||||
mockAbortCallback.AsStdFunction(),
|
||||
"state1");
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
} // namespace
|
@@ -36,7 +36,22 @@ public:
|
||||
start,
|
||||
(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData,
|
||||
ImageCacheCollectorInterface::CaptureCallback captureCallback,
|
||||
ImageCacheCollectorInterface::AbortCallback abortCallback),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(ImagePair,
|
||||
createImage,
|
||||
(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(QIcon,
|
||||
createIcon,
|
||||
(Utils::SmallStringView filePath,
|
||||
Utils::SmallStringView state,
|
||||
const QmlDesigner::ImageCache::AuxiliaryData &auxiliaryData),
|
||||
(override));
|
||||
};
|
||||
|
@@ -54,7 +54,8 @@ protected:
|
||||
Notification waitInThread;
|
||||
Notification notification;
|
||||
QImage image1{10, 10, QImage::Format_ARGB32};
|
||||
NiceMock<MockFunction<void(const QImage &)>> imageCallbackMock;
|
||||
QImage smallImage1{1, 1, QImage::Format_ARGB32};
|
||||
NiceMock<MockFunction<void(const QImage &, const QImage &)>> imageCallbackMock;
|
||||
NiceMock<MockFunction<void()>> abortCallbackMock;
|
||||
NiceMock<ImageCacheCollectorMock> collectorMock;
|
||||
NiceMock<MockImageCacheStorage> storageMock;
|
||||
@@ -63,191 +64,205 @@ protected:
|
||||
|
||||
TEST_F(ImageCacheGenerator, CallsCollectorWithCaptureCallback)
|
||||
{
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillRepeatedly(
|
||||
[&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
EXPECT_CALL(imageCallbackMock, Call(_)).WillRepeatedly([&](const QImage &) {
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
EXPECT_CALL(imageCallbackMock, Call(_, _)).WillRepeatedly([&](const QImage &, const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, CallsCollectorOnlyIfNotProcessing)
|
||||
{
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _)).WillRepeatedly([&](auto, auto, auto, auto) {
|
||||
notification.notify();
|
||||
});
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait(2);
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, ProcessTaskAfterFirstFinished)
|
||||
{
|
||||
ON_CALL(imageCallbackMock, Call(_)).WillByDefault([&](const QImage &) { notification.notify(); });
|
||||
ON_CALL(imageCallbackMock, Call(_, _)).WillByDefault([&](const QImage &, const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillOnce([&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _))
|
||||
.WillOnce([&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _))
|
||||
.WillOnce([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name2", {}, {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
generator.generateImage("name2", {}, {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait(2);
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, DontCrashAtDestructingGenerator)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault(
|
||||
[&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name2",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name3",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name4",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage(
|
||||
"name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name2", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name3", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name4", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, StoreImage)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault(
|
||||
[&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
|
||||
EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1)))
|
||||
.WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
|
||||
EXPECT_CALL(storageMock,
|
||||
storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1)))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name", {}, {11}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", {}, {11}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, StoreImageWithExtraId)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
|
||||
EXPECT_CALL(storageMock,
|
||||
storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1)))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name", "extraId", {11}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, StoreNullImage)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{}); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{}, QImage{});
|
||||
});
|
||||
|
||||
EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})))
|
||||
.WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
|
||||
EXPECT_CALL(storageMock,
|
||||
storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage(
|
||||
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, StoreNullImageWithExtraId)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{}, QImage{});
|
||||
});
|
||||
|
||||
EXPECT_CALL(storageMock,
|
||||
storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
"extraId",
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
abortCallbackMock.AsStdFunction(),
|
||||
{});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, AbortCallback)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault(
|
||||
[&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
|
||||
ON_CALL(collectorMock, start(Eq("name2"), _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto abortCallback) { abortCallback(); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { abortCallback(); });
|
||||
|
||||
EXPECT_CALL(imageCallbackMock, Call(_)).WillOnce([&](const QImage &) { notification.notify(); });
|
||||
EXPECT_CALL(imageCallbackMock, Call(_, _)).WillOnce([&](const QImage &, const QImage &) {
|
||||
notification.notify();
|
||||
});
|
||||
EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name2",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage(
|
||||
"name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name2", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
notification.wait(2);
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, StoreNullImageForAbortCallback)
|
||||
{
|
||||
ON_CALL(collectorMock, start(_, _, _, _)).WillByDefault([&](auto, auto, auto, auto abortCallback) {
|
||||
abortCallback();
|
||||
});
|
||||
ON_CALL(collectorMock, start(_, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { abortCallback(); });
|
||||
|
||||
EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})))
|
||||
.WillOnce([&](auto, auto, auto) { notification.notify(); });
|
||||
EXPECT_CALL(storageMock,
|
||||
storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{})))
|
||||
.WillOnce([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage(
|
||||
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, AbortForEmptyImage)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto captureCallback, auto) { captureCallback(QImage{}); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{}, QImage{});
|
||||
});
|
||||
|
||||
EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage(
|
||||
"name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, CallWalCheckpointFullIfQueueIsEmpty)
|
||||
{
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto captureCallback, auto) { captureCallback({}); });
|
||||
ON_CALL(collectorMock, start(Eq("name"), _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}); });
|
||||
|
||||
EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name2",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage(
|
||||
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, CleanIsCallingAbortCallback)
|
||||
{
|
||||
ON_CALL(collectorMock, start(_, _, _, _)).WillByDefault([&](auto, auto, auto captureCallback, auto) {
|
||||
captureCallback({});
|
||||
waitInThread.wait();
|
||||
});
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name2",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
ON_CALL(collectorMock, start(_, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback({}, {});
|
||||
waitInThread.wait();
|
||||
});
|
||||
generator.generateImage(
|
||||
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
|
||||
EXPECT_CALL(abortCallbackMock, Call()).Times(AtLeast(1));
|
||||
|
||||
@@ -257,31 +272,51 @@ TEST_F(ImageCacheGenerator, CleanIsCallingAbortCallback)
|
||||
|
||||
TEST_F(ImageCacheGenerator, WaitForFinished)
|
||||
{
|
||||
ON_CALL(collectorMock, start(_, _, _, _)).WillByDefault([&](auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1});
|
||||
});
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
generator.generateImage("name2",
|
||||
{},
|
||||
{11},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
abortCallbackMock.AsStdFunction());
|
||||
ON_CALL(collectorMock, start(_, _, _, _, _))
|
||||
.WillByDefault([&](auto, auto, auto, auto captureCallback, auto) {
|
||||
captureCallback(QImage{image1}, QImage{smallImage1});
|
||||
});
|
||||
generator.generateImage(
|
||||
"name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage(
|
||||
"name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {});
|
||||
|
||||
EXPECT_CALL(imageCallbackMock, Call(_)).Times(2);
|
||||
EXPECT_CALL(imageCallbackMock, Call(_, _)).Times(2);
|
||||
|
||||
generator.waitForFinished();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, CallsCollectorWithState)
|
||||
TEST_F(ImageCacheGenerator, CallsCollectorWithExtraId)
|
||||
{
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), Eq("state1"), _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); });
|
||||
EXPECT_CALL(collectorMock, start(Eq("name"), Eq("extraId1"), _, _, _))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name", "state1", {}, imageCallbackMock.AsStdFunction(), {});
|
||||
generator.generateImage("name", "extraId1", {}, imageCallbackMock.AsStdFunction(), {}, {});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheGenerator, CallsCollectorWithAuxiliaryData)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
|
||||
EXPECT_CALL(collectorMock,
|
||||
start(Eq("name"),
|
||||
_,
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName, Eq(u"color")))),
|
||||
_,
|
||||
_))
|
||||
.WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); });
|
||||
|
||||
generator.generateImage("name",
|
||||
{},
|
||||
{},
|
||||
imageCallbackMock.AsStdFunction(),
|
||||
{},
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
notification.wait();
|
||||
}
|
||||
|
||||
|
@@ -32,28 +32,28 @@
|
||||
|
||||
namespace {
|
||||
|
||||
MATCHER_P2(IsEntry,
|
||||
MATCHER_P2(IsImageEntry,
|
||||
image,
|
||||
hasEntry,
|
||||
std::string(negation ? "is't" : "is")
|
||||
+ PrintToString(QmlDesigner::ImageCacheStorageInterface::Entry{image, hasEntry}))
|
||||
+ PrintToString(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image, hasEntry}))
|
||||
{
|
||||
const QmlDesigner::ImageCacheStorageInterface::Entry &entry = arg;
|
||||
const QmlDesigner::ImageCacheStorageInterface::ImageEntry &entry = arg;
|
||||
return entry.image == image && entry.hasEntry == hasEntry;
|
||||
}
|
||||
|
||||
MATCHER_P2(IsIconEntry,
|
||||
icon,
|
||||
hasEntry,
|
||||
std::string(negation ? "is't" : "is")
|
||||
+ PrintToString(QmlDesigner::ImageCacheStorageInterface::IconEntry{icon, hasEntry}))
|
||||
{
|
||||
const QmlDesigner::ImageCacheStorageInterface::IconEntry &entry = arg;
|
||||
return entry.icon.availableSizes() == icon.availableSizes() && entry.hasEntry == hasEntry;
|
||||
}
|
||||
|
||||
class ImageCacheStorageTest : public testing::Test
|
||||
{
|
||||
protected:
|
||||
QImage createImage()
|
||||
{
|
||||
QImage image{150, 150, QImage::Format_ARGB32};
|
||||
image.fill(QColor{128, 64, 0, 11});
|
||||
image.setPixelColor(75, 75, QColor{1, 255, 33, 196});
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
protected:
|
||||
using ReadStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::ReadStatement;
|
||||
using WriteStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::WriteStatement;
|
||||
@@ -61,9 +61,13 @@ protected:
|
||||
NiceMock<SqliteDatabaseMock> databaseMock;
|
||||
QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
|
||||
ReadStatement &selectImageStatement = storage.selectImageStatement;
|
||||
ReadStatement &selectSmallImageStatement = storage.selectSmallImageStatement;
|
||||
ReadStatement &selectIconStatement = storage.selectIconStatement;
|
||||
WriteStatement &upsertImageStatement = storage.upsertImageStatement;
|
||||
QImage image1{createImage()};
|
||||
WriteStatement &upsertIconStatement = storage.upsertIconStatement;
|
||||
QImage image1{10, 10, QImage::Format_ARGB32};
|
||||
QImage smallImage1{10, 10, QImage::Format_ARGB32};
|
||||
QIcon icon1{QPixmap::fromImage(image1)};
|
||||
};
|
||||
|
||||
TEST_F(ImageCacheStorageTest, Initialize)
|
||||
@@ -73,12 +77,17 @@ TEST_F(ImageCacheStorageTest, Initialize)
|
||||
EXPECT_CALL(databaseMock, exclusiveBegin());
|
||||
EXPECT_CALL(databaseMock,
|
||||
execute(Eq("CREATE TABLE IF NOT EXISTS images(id INTEGER PRIMARY KEY, name TEXT "
|
||||
"NOT NULL UNIQUE, mtime INTEGER, image BLOB, icon BLOB)")));
|
||||
"NOT NULL UNIQUE, mtime INTEGER, image BLOB, smallImage BLOB)")));
|
||||
EXPECT_CALL(databaseMock,
|
||||
execute(Eq("CREATE TABLE IF NOT EXISTS icons(id INTEGER PRIMARY KEY, name TEXT "
|
||||
"NOT NULL UNIQUE, mtime INTEGER, icon BLOB)")));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
EXPECT_CALL(databaseMock, immediateBegin());
|
||||
EXPECT_CALL(databaseMock, prepare(Eq(selectImageStatement.sqlStatement)));
|
||||
EXPECT_CALL(databaseMock, prepare(Eq(selectSmallImageStatement.sqlStatement)));
|
||||
EXPECT_CALL(databaseMock, prepare(Eq(selectIconStatement.sqlStatement)));
|
||||
EXPECT_CALL(databaseMock, prepare(Eq(upsertImageStatement.sqlStatement)));
|
||||
EXPECT_CALL(databaseMock, prepare(Eq(upsertIconStatement.sqlStatement)));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
|
||||
@@ -116,6 +125,38 @@ TEST_F(ImageCacheStorageTest, FetchImageCallsIsBusy)
|
||||
storage.fetchImage("/path/to/component", {123});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, FetchSmallImageCalls)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, deferredBegin());
|
||||
EXPECT_CALL(selectSmallImageStatement,
|
||||
valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123)));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.fetchSmallImage("/path/to/component", {123});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, FetchSmallImageCallsIsBusy)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, deferredBegin());
|
||||
EXPECT_CALL(selectSmallImageStatement,
|
||||
valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123)))
|
||||
.WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
|
||||
EXPECT_CALL(databaseMock, rollback());
|
||||
EXPECT_CALL(databaseMock, deferredBegin());
|
||||
EXPECT_CALL(selectSmallImageStatement,
|
||||
valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123)));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.fetchSmallImage("/path/to/component", {123});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, FetchIconCalls)
|
||||
{
|
||||
InSequence s;
|
||||
@@ -156,11 +197,11 @@ TEST_F(ImageCacheStorageTest, StoreImageCalls)
|
||||
EXPECT_CALL(upsertImageStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
A<Sqlite::BlobView>(),
|
||||
A<Sqlite::BlobView>()));
|
||||
Not(IsEmpty()),
|
||||
Not(IsEmpty())));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
|
||||
@@ -171,11 +212,11 @@ TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
|
||||
EXPECT_CALL(upsertImageStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
A<Sqlite::NullValue>(),
|
||||
A<Sqlite::NullValue>()));
|
||||
IsEmpty(),
|
||||
IsEmpty()));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeImage("/path/to/component", {123}, QImage{});
|
||||
storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
|
||||
@@ -187,11 +228,54 @@ TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
|
||||
EXPECT_CALL(upsertImageStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
A<Sqlite::NullValue>(),
|
||||
A<Sqlite::NullValue>()));
|
||||
IsEmpty(),
|
||||
IsEmpty()));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeImage("/path/to/component", {123}, QImage{});
|
||||
storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, StoreIconCalls)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, immediateBegin());
|
||||
EXPECT_CALL(upsertIconStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
A<Sqlite::BlobView>()));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, StoreEmptyIconCalls)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, immediateBegin());
|
||||
EXPECT_CALL(upsertIconStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
IsEmpty()));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeIcon("/path/to/component", {123}, QIcon{});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, StoreIconCallsIsBusy)
|
||||
{
|
||||
InSequence s;
|
||||
|
||||
EXPECT_CALL(databaseMock, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
|
||||
EXPECT_CALL(databaseMock, immediateBegin());
|
||||
EXPECT_CALL(upsertIconStatement,
|
||||
write(TypedEq<Utils::SmallStringView>("/path/to/component"),
|
||||
TypedEq<long long>(123),
|
||||
IsEmpty()));
|
||||
EXPECT_CALL(databaseMock, commit());
|
||||
|
||||
storage.storeIcon("/path/to/component", {123}, QIcon{});
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageTest, CallWalCheckointFull)
|
||||
@@ -227,108 +311,155 @@ protected:
|
||||
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
|
||||
QmlDesigner::ImageCacheStorage<Sqlite::Database> storage{database};
|
||||
QImage image1{createImage()};
|
||||
QImage image2{10, 10, QImage::Format_ARGB32};
|
||||
QImage icon1{image1.scaled(96, 96)};
|
||||
QImage smallImage1{image1.scaled(96, 96)};
|
||||
QIcon icon1{QPixmap::fromImage(image1)};
|
||||
};
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(image1, true));
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsImageEntry(image1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreEmptyImageAfterEntry)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
storage.storeImage("/path/to/component", {123}, QImage{});
|
||||
storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
|
||||
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsImageEntry(QImage{}, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreEmptyEntry)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, QImage{});
|
||||
storage.storeImage("/path/to/component", {123}, QImage{}, QImage{});
|
||||
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
|
||||
ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsImageEntry(QImage{}, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNonExistingImageIsEmpty)
|
||||
{
|
||||
auto image = storage.fetchImage("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(QImage{}, false));
|
||||
ASSERT_THAT(image, IsImageEntry(QImage{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchImage("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(image1, true));
|
||||
ASSERT_THAT(image, IsImageEntry(image1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchImage("/path/to/component", {124});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(QImage{}, false));
|
||||
ASSERT_THAT(image, IsImageEntry(QImage{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNewerImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchImage("/path/to/component", {122});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(image1, true));
|
||||
ASSERT_THAT(image, IsImageEntry(image1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNonExistingSmallImageIsEmpty)
|
||||
{
|
||||
auto image = storage.fetchSmallImage("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsImageEntry(QImage{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchSameTimeSmallImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchSmallImage("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsImageEntry(smallImage1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderSmallImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchSmallImage("/path/to/component", {124});
|
||||
|
||||
ASSERT_THAT(image, IsImageEntry(QImage{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNewerSmallImage)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1, smallImage1);
|
||||
|
||||
auto image = storage.fetchSmallImage("/path/to/component", {122});
|
||||
|
||||
ASSERT_THAT(image, IsImageEntry(smallImage1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreIcon)
|
||||
{
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
|
||||
ASSERT_THAT(storage.fetchIcon("/path/to/component", {123}), IsIconEntry(icon1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreEmptyIconAfterEntry)
|
||||
{
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
|
||||
storage.storeIcon("/path/to/component", {123}, QIcon{});
|
||||
|
||||
ASSERT_THAT(storage.fetchIcon("/path/to/component", {123}), IsIconEntry(QIcon{}, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, StoreEmptyIconEntry)
|
||||
{
|
||||
storage.storeIcon("/path/to/component", {123}, QIcon{});
|
||||
|
||||
ASSERT_THAT(storage.fetchIcon("/path/to/component", {123}), IsIconEntry(QIcon{}, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNonExistingIconIsEmpty)
|
||||
{
|
||||
auto image = storage.fetchIcon("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(QImage{}, false));
|
||||
ASSERT_THAT(image, IsIconEntry(QIcon{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchSameTimeIcon)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
|
||||
auto image = storage.fetchIcon("/path/to/component", {123});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(icon1, true));
|
||||
ASSERT_THAT(image, IsIconEntry(icon1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderIcon)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
|
||||
auto image = storage.fetchIcon("/path/to/component", {124});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(QImage{}, false));
|
||||
ASSERT_THAT(image, IsIconEntry(QIcon{}, false));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, FetchNewerIcon)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image1);
|
||||
storage.storeIcon("/path/to/component", {123}, icon1);
|
||||
|
||||
auto image = storage.fetchIcon("/path/to/component", {122});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(icon1, true));
|
||||
ASSERT_THAT(image, IsIconEntry(icon1, true));
|
||||
}
|
||||
|
||||
TEST_F(ImageCacheStorageSlowTest, DontScaleSmallerIcon)
|
||||
{
|
||||
storage.storeImage("/path/to/component", {123}, image2);
|
||||
|
||||
auto image = storage.fetchImage("/path/to/component", {122});
|
||||
|
||||
ASSERT_THAT(image, IsEntry(image2, true));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@@ -38,7 +38,8 @@ public:
|
||||
Utils::SmallStringView state,
|
||||
Sqlite::TimeStamp timeStamp,
|
||||
CaptureCallback &&captureCallback,
|
||||
AbortCallback &&abortCallback),
|
||||
AbortCallback &&abortCallback,
|
||||
QmlDesigner::ImageCache::AuxiliaryData &&auxiliaryData),
|
||||
(override));
|
||||
MOCK_METHOD(void, clean, (), (override));
|
||||
MOCK_METHOD(void, waitForFinished, (), (override));
|
||||
|
@@ -32,19 +32,33 @@
|
||||
class MockImageCacheStorage : public QmlDesigner::ImageCacheStorageInterface
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
|
||||
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry,
|
||||
fetchImage,
|
||||
(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
|
||||
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry,
|
||||
fetchSmallImage,
|
||||
(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::IconEntry,
|
||||
fetchIcon,
|
||||
(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(void,
|
||||
storeImage,
|
||||
(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image),
|
||||
(Utils::SmallStringView name,
|
||||
Sqlite::TimeStamp newTimeStamp,
|
||||
const QImage &image,
|
||||
const QImage &smallImage),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void,
|
||||
storeIcon,
|
||||
(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, walCheckpointFull, (), (override));
|
||||
};
|
||||
|
@@ -390,7 +390,7 @@ TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundValu
|
||||
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundBlob)
|
||||
{
|
||||
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
|
||||
Sqlite::BlobView bytes;
|
||||
Sqlite::BlobView bytes{QByteArray{"XXX"}};
|
||||
|
||||
ASSERT_THROW(statement.bind(2, bytes), Sqlite::BindingIndexIsOutOfRange);
|
||||
}
|
||||
@@ -505,6 +505,18 @@ TEST_F(SqliteStatement, WriteEmptyBlobs)
|
||||
ASSERT_THAT(statement.fetchBlobValue(0), IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, EmptyBlobsAreNull)
|
||||
{
|
||||
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT ifnull(blob, 1) FROM T",
|
||||
database);
|
||||
|
||||
Sqlite::BlobView bytes;
|
||||
|
||||
statement.write(bytes);
|
||||
|
||||
ASSERT_THAT(statement.fetchType(0), Eq(Sqlite::Type::Null));
|
||||
}
|
||||
|
||||
TEST_F(SqliteStatement, WriteBlobs)
|
||||
{
|
||||
SqliteTestStatement statement("INSERT INTO test VALUES ('blob', 40, ?)", database);
|
||||
|
@@ -52,11 +52,8 @@ public:
|
||||
MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView, double), ());
|
||||
MOCK_METHOD(void, write, (long long, Utils::SmallStringView, Utils::SmallStringView), ());
|
||||
MOCK_METHOD(void, write, (long long, Utils::SmallStringView, const Sqlite::Value &), ());
|
||||
MOCK_METHOD(void, write, (Utils::SmallStringView, long long, Sqlite::BlobView), ());
|
||||
MOCK_METHOD(void, write, (Utils::SmallStringView, long long, Sqlite::BlobView, Sqlite::BlobView), ());
|
||||
MOCK_METHOD(void,
|
||||
write,
|
||||
(Utils::SmallStringView, long long, Sqlite::NullValue, Sqlite::NullValue),
|
||||
());
|
||||
MOCK_METHOD(void,
|
||||
write,
|
||||
(Utils::SmallStringView,
|
||||
|
269
tests/unit/unittest/synchronousimagecache-test.cpp
Normal file
269
tests/unit/unittest/synchronousimagecache-test.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2021 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 "googletest.h"
|
||||
|
||||
#include "imagecachecollectormock.h"
|
||||
#include "mockimagecachestorage.h"
|
||||
#include "mocktimestampprovider.h"
|
||||
|
||||
#include <synchronousimagecache.h>
|
||||
|
||||
namespace {
|
||||
|
||||
MATCHER_P(IsIcon, icon, std::string(negation ? "isn't " : "is ") + PrintToString(icon))
|
||||
{
|
||||
const QIcon &other = arg;
|
||||
return icon.availableSizes() == other.availableSizes();
|
||||
}
|
||||
|
||||
class SynchronousImageCache : public testing::Test
|
||||
{
|
||||
protected:
|
||||
SynchronousImageCache()
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{123}));
|
||||
ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image1, true}));
|
||||
ON_CALL(mockStorage,
|
||||
fetchImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image2, true}));
|
||||
ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(
|
||||
Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true}));
|
||||
ON_CALL(mockStorage,
|
||||
fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(
|
||||
Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage2, true}));
|
||||
ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::IconEntry{icon1, true}));
|
||||
ON_CALL(mockStorage,
|
||||
fetchIcon(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123})))
|
||||
.WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::IconEntry{icon2, true}));
|
||||
ON_CALL(mockCollector, createImage(Eq("/path/to/Component.qml"), Eq("extraId1"), _))
|
||||
.WillByDefault(Return(std::make_pair(image3, smallImage3)));
|
||||
ON_CALL(mockCollector, createIcon(Eq("/path/to/Component.qml"), Eq("extraId1"), _))
|
||||
.WillByDefault(Return(icon3));
|
||||
}
|
||||
|
||||
protected:
|
||||
NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback;
|
||||
NiceMock<MockImageCacheStorage> mockStorage;
|
||||
NiceMock<ImageCacheCollectorMock> mockCollector;
|
||||
NiceMock<MockTimeStampProvider> mockTimeStampProvider;
|
||||
QmlDesigner::SynchronousImageCache cache{mockStorage, mockTimeStampProvider, mockCollector};
|
||||
QImage image1{1, 1, QImage::Format_ARGB32};
|
||||
QImage image2{2, 2, QImage::Format_ARGB32};
|
||||
QImage image3{3, 3, QImage::Format_ARGB32};
|
||||
QImage smallImage1{1, 1, QImage::Format_ARGB32};
|
||||
QImage smallImage2{2, 1, QImage::Format_ARGB32};
|
||||
QImage smallImage3{3, 1, QImage::Format_ARGB32};
|
||||
QIcon icon1{QPixmap::fromImage(image1)};
|
||||
QIcon icon2{QPixmap::fromImage(image2)};
|
||||
QIcon icon3{QPixmap::fromImage(image3)};
|
||||
};
|
||||
|
||||
TEST_F(SynchronousImageCache, GetImageFromStorage)
|
||||
{
|
||||
auto image = cache.image("/path/to/Component.qml");
|
||||
|
||||
ASSERT_THAT(image, image1);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetImageWithExtraIdFromStorage)
|
||||
{
|
||||
auto image = cache.image("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(image, image2);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetImageWithOutdatedTimeStampFromCollector)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
auto image = cache.image("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(image, image3);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetImageWithOutdatedTimeStampStored)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockStorage,
|
||||
storeImage(Eq("/path/to/Component.qml+extraId1"),
|
||||
Eq(Sqlite::TimeStamp{124}),
|
||||
Eq(image3),
|
||||
Eq(smallImage3)));
|
||||
|
||||
auto image = cache.image("/path/to/Component.qml", "extraId1");
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetSmallImageFromStorage)
|
||||
{
|
||||
auto image = cache.smallImage("/path/to/Component.qml");
|
||||
|
||||
ASSERT_THAT(image, smallImage1);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetSmallImageWithExtraIdFromStorage)
|
||||
{
|
||||
auto image = cache.smallImage("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(image, smallImage2);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetSmallImageWithOutdatedTimeStampFromCollector)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
auto image = cache.smallImage("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(image, smallImage3);
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetSmallImageWithOutdatedTimeStampStored)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockStorage,
|
||||
storeImage(Eq("/path/to/Component.qml+extraId1"),
|
||||
Eq(Sqlite::TimeStamp{124}),
|
||||
Eq(image3),
|
||||
Eq(smallImage3)));
|
||||
|
||||
auto image = cache.smallImage("/path/to/Component.qml", "extraId1");
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetIconFromStorage)
|
||||
{
|
||||
auto icon = cache.icon("/path/to/Component.qml");
|
||||
|
||||
ASSERT_THAT(icon, IsIcon(icon1));
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetIconWithExtraIdFromStorage)
|
||||
{
|
||||
auto icon = cache.icon("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(icon, IsIcon(icon2));
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetIconWithOutdatedTimeStampFromCollector)
|
||||
{
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
auto icon = cache.icon("/path/to/Component.qml", "extraId1");
|
||||
|
||||
ASSERT_THAT(icon, IsIcon(icon3));
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, GetIconWithOutdatedTimeStampStored)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockStorage,
|
||||
storeIcon(Eq("/path/to/Component.qml+extraId1"),
|
||||
Eq(Sqlite::TimeStamp{124}),
|
||||
IsIcon(icon3)));
|
||||
|
||||
auto icon = cache.icon("/path/to/Component.qml",
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, IconCallsCollectorWithAuxiliaryData)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockCollector,
|
||||
createIcon(Eq("/path/to/Component.qml"),
|
||||
Eq("extraId1"),
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName,
|
||||
Eq(u"color"))))));
|
||||
|
||||
auto icon = cache.icon("/path/to/Component.qml",
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, ImageCallsCollectorWithAuxiliaryData)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockCollector,
|
||||
createImage(Eq("/path/to/Component.qml"),
|
||||
Eq("extraId1"),
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName,
|
||||
Eq(u"color"))))));
|
||||
|
||||
auto icon = cache.image("/path/to/Component.qml",
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
}
|
||||
|
||||
TEST_F(SynchronousImageCache, SmallImageCallsCollectorWithAuxiliaryData)
|
||||
{
|
||||
using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData;
|
||||
std::vector<QSize> sizes{{20, 11}};
|
||||
ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
|
||||
.WillByDefault(Return(Sqlite::TimeStamp{124}));
|
||||
|
||||
EXPECT_CALL(mockCollector,
|
||||
createImage(Eq("/path/to/Component.qml"),
|
||||
Eq("extraId1"),
|
||||
VariantWith<FontCollectorSizesAuxiliaryData>(
|
||||
AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes,
|
||||
ElementsAre(QSize{20, 11})),
|
||||
Field(&FontCollectorSizesAuxiliaryData::colorName,
|
||||
Eq(u"color"))))));
|
||||
|
||||
auto icon = cache.smallImage("/path/to/Component.qml",
|
||||
"extraId1",
|
||||
FontCollectorSizesAuxiliaryData{sizes, "color"});
|
||||
}
|
||||
|
||||
} // namespace
|
@@ -66,7 +66,8 @@ SOURCES += \
|
||||
filepathview-test.cpp \
|
||||
gtest-creator-printing.cpp \
|
||||
gtest-qt-printing.cpp \
|
||||
imagecache-test.cpp \
|
||||
asynchronousimagecache-test.cpp \
|
||||
synchronousimagecache-test.cpp \
|
||||
imagecachegenerator-test.cpp \
|
||||
imagecachestorage-test.cpp \
|
||||
lastchangedrowid-test.cpp \
|
||||
|
@@ -29,7 +29,7 @@
|
||||
#include <sqliteglobal.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#ifdef WITH_BENCHMARKS
|
||||
@@ -54,7 +54,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
Sqlite::Database::activateLogging();
|
||||
|
||||
QCoreApplication application(argc, argv);
|
||||
QGuiApplication application(argc, argv);
|
||||
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
#ifdef WITH_BENCHMARKS
|
||||
|
Reference in New Issue
Block a user