diff --git a/src/libs/sqlite/sqlitedatabase.h b/src/libs/sqlite/sqlitedatabase.h index 3c3b8acb423..a84baafcc15 100644 --- a/src/libs/sqlite/sqlitedatabase.h +++ b/src/libs/sqlite/sqlitedatabase.h @@ -90,15 +90,12 @@ public: return m_databaseBackend.lastInsertedRowId(); } - void setLastInsertedRowId(int64_t rowId) - { - m_databaseBackend.setLastInsertedRowId(rowId); - } + void setLastInsertedRowId(int64_t rowId) { m_databaseBackend.setLastInsertedRowId(rowId); } - int changesCount() - { - return m_databaseBackend.changesCount(); - } + int version() const { return m_databaseBackend.version(); } + void setVersion(int version) { m_databaseBackend.setVersion(version); } + + int changesCount() { return m_databaseBackend.changesCount(); } int totalChangesCount() { return m_databaseBackend.totalChangesCount(); } diff --git a/src/libs/sqlite/sqlitedatabasebackend.cpp b/src/libs/sqlite/sqlitedatabasebackend.cpp index a18c90f527b..2861c45561b 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.cpp +++ b/src/libs/sqlite/sqlitedatabasebackend.cpp @@ -168,6 +168,19 @@ LockingMode DatabaseBackend::lockingMode() const return pragmaToLockingMode(pragmaValue("main.locking_mode")); } +int DatabaseBackend::version() const +{ + return toValue("PRAGMA main.user_version"); +} + +void DatabaseBackend::setVersion(int version) +{ + ReadWriteStatement<>{Utils::SmallString{"PRAGMA main.user_version = "} + + Utils::SmallString::number(version), + m_database} + .execute(); +} + int DatabaseBackend::changesCount() const { return sqlite3_changes(sqliteDatabaseHandle()); diff --git a/src/libs/sqlite/sqlitedatabasebackend.h b/src/libs/sqlite/sqlitedatabasebackend.h index 67556cc77e7..e9c24bd7a0c 100644 --- a/src/libs/sqlite/sqlitedatabasebackend.h +++ b/src/libs/sqlite/sqlitedatabasebackend.h @@ -50,6 +50,9 @@ public: Utils::SmallStringVector columnNames(Utils::SmallStringView tableName); + int version() const; + void setVersion(int version); + int changesCount() const; int totalChangesCount() const; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 129e62b33f8..a58841b1204 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -911,6 +911,10 @@ extend_qtc_plugin(QmlDesigner SOURCES explicitimagecacheimageprovider.cpp explicitimagecacheimageprovider.h + imagecacheimageresponse.cpp + imagecacheimageresponse.h + midsizeimagecacheprovider.cpp + midsizeimagecacheprovider.h smallimagecacheprovider.cpp smallimagecacheprovider.h ) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp index 40aa25db827..423366d2df8 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp @@ -3,6 +3,8 @@ #include "itemlibraryiconimageprovider.h" +#include + #include #include @@ -11,42 +13,14 @@ namespace QmlDesigner { -class ImageRespose : public QQuickImageResponse -{ -public: - QQuickTextureFactory *textureFactory() const override - { - return QQuickTextureFactory::textureFactoryForImage(m_image); - } - - void setImage(const QImage &image) - { - m_image = image; - - emit finished(); - } - - void abort() - { - m_image = QImage{ - Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")}; - - emit finished(); - } - -private: - QImage m_image; -}; - - QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QString &id, const QSize &) { - auto response = std::make_unique(); + auto response = std::make_unique(); m_cache.requestSmallImage( id, - [response = QPointer(response.get())](const QImage &image) { + [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, [response, image] { @@ -55,7 +29,8 @@ QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QS }, Qt::QueuedConnection); }, - [response = QPointer(response.get())](ImageCache::AbortReason abortReason) { + [response = QPointer(response.get())]( + ImageCache::AbortReason abortReason) { QMetaObject::invokeMethod( response, [response, abortReason] { diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp index fb17f6dee82..caa5348bc42 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorimageprovider.cpp @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "propertyeditorimageprovider.h" -#include "asset.h" + +#include +#include #include #include @@ -25,11 +27,11 @@ QQuickImageResponse *PropertyEditorImageProvider::requestImageResponse(const QSt return m_smallImageCacheProvider.requestImageResponse("#" + id.split('.').first(), requestedSize); - auto response = std::make_unique(m_smallImageCacheProvider.defaultImage()); + auto response = std::make_unique(m_smallImageCacheProvider.defaultImage()); QMetaObject::invokeMethod( response.get(), - [response = QPointer(response.get()), asset, requestedSize] { + [response = QPointer(response.get()), asset, requestedSize] { if (asset.isImage()) { QImage image = QImage(Utils::StyleHelper::dpiSpecificImageFile(asset.id())); if (!image.isNull()) { diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp index 2c3446bbe3f..3d53e894519 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousexplicitimagecache.cpp @@ -44,9 +44,21 @@ void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, const auto id = extraId.empty() ? Utils::PathString{name} : Utils::PathString::join({name, "+", extraId}); - const auto entry = requestType == RequestType::Image - ? storage.fetchImage(id, Sqlite::TimeStamp{}) - : storage.fetchSmallImage(id, Sqlite::TimeStamp{}); + auto requestImageFromStorage = [&](RequestType requestType) { + switch (requestType) { + case RequestType::Image: + return storage.fetchImage(id, Sqlite::TimeStamp{}); + case RequestType::MidSizeImage: + return storage.fetchMidSizeImage(id, Sqlite::TimeStamp{}); + case RequestType::SmallImage: + return storage.fetchSmallImage(id, Sqlite::TimeStamp{}); + default: + break; + } + + return storage.fetchImage(id, Sqlite::TimeStamp{}); + }; + const auto entry = requestImageFromStorage(requestType); if (entry && !entry->isNull()) captureCallback(*entry); @@ -75,6 +87,19 @@ void AsynchronousExplicitImageCache::requestImage(Utils::PathString name, m_condition.notify_all(); } +void AsynchronousExplicitImageCache::requestMidSizeImage(Utils::PathString name, + ImageCache::CaptureImageCallback captureCallback, + ImageCache::AbortCallback abortCallback, + Utils::SmallString extraId) +{ + addEntry(std::move(name), + std::move(extraId), + std::move(captureCallback), + std::move(abortCallback), + RequestType::MidSizeImage); + m_condition.notify_all(); +} + void AsynchronousExplicitImageCache::requestSmallImage(Utils::PathString name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp index 6d08443e9e7..e63576d80be 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagecache.cpp @@ -57,8 +57,21 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, : Utils::PathString::join({name, "+", extraId}); const auto timeStamp = timeStampProvider.timeStamp(name); - const auto entry = requestType == RequestType::Image ? storage.fetchImage(id, timeStamp) - : storage.fetchSmallImage(id, timeStamp); + auto requestImageFromStorage = [&](RequestType requestType) { + switch (requestType) { + case RequestType::Image: + return storage.fetchImage(id, timeStamp); + case RequestType::MidSizeImage: + return storage.fetchMidSizeImage(id, timeStamp); + case RequestType::SmallImage: + return storage.fetchSmallImage(id, timeStamp); + default: + break; + } + + return storage.fetchImage(id, timeStamp); + }; + const auto entry = requestImageFromStorage(requestType); if (entry) { if (entry->isNull()) @@ -66,10 +79,28 @@ void AsynchronousImageCache::request(Utils::SmallStringView name, else captureCallback(*entry); } else { - auto callback = [captureCallback = std::move(captureCallback), - requestType](const QImage &image, const QImage &smallImage) { - captureCallback(requestType == RequestType::Image ? image : smallImage); - }; + auto callback = + [captureCallback = std::move(captureCallback), + requestType](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + auto selectImage = [](RequestType requestType, + const QImage &image, + const QImage &midSizeImage, + const QImage &smallImage) { + switch (requestType) { + case RequestType::Image: + return image; + case RequestType::MidSizeImage: + return midSizeImage; + case RequestType::SmallImage: + return smallImage; + default: + break; + } + + return image; + }; + captureCallback(selectImage(requestType, image, midSizeImage, smallImage)); + }; generator.generateImage(name, extraId, timeStamp, @@ -102,6 +133,21 @@ void AsynchronousImageCache::requestImage(Utils::PathString name, m_condition.notify_all(); } +void AsynchronousImageCache::requestMidSizeImage(Utils::PathString name, + ImageCache::CaptureImageCallback captureCallback, + ImageCache::AbortCallback abortCallback, + Utils::SmallString extraId, + ImageCache::AuxiliaryData auxiliaryData) +{ + addEntry(std::move(name), + std::move(extraId), + std::move(captureCallback), + std::move(abortCallback), + std::move(auxiliaryData), + RequestType::MidSizeImage); + m_condition.notify_all(); +} + void AsynchronousImageCache::requestSmallImage(Utils::PathString name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, diff --git a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp index de738862a3a..a165172ecb4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/asynchronousimagefactory.cpp @@ -99,8 +99,8 @@ void AsynchronousImageFactory::request(Utils::SmallStringView name, if (currentModifiedTime < (storageModifiedTime + pause)) return; - auto capture = [=](const QImage &image, const QImage &smallImage) { - m_storage.storeImage(id, currentModifiedTime, image, smallImage); + auto capture = [=](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + m_storage.storeImage(id, currentModifiedTime, image, midSizeImage, smallImage); }; collector.start(name, diff --git a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp index 7023d94cba5..97dda3f8bf4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/explicitimagecacheimageprovider.cpp @@ -2,51 +2,23 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "explicitimagecacheimageprovider.h" +#include "imagecacheimageresponse.h" #include #include #include -namespace { - -class ImageResponse : public QQuickImageResponse -{ -public: - ImageResponse(const QImage &defaultImage) - : m_image(defaultImage) - {} - - QQuickTextureFactory *textureFactory() const override - { - return QQuickTextureFactory::textureFactoryForImage(m_image); - } - - void setImage(const QImage &image) - { - m_image = image; - - emit finished(); - } - - void abort() { emit finished(); } - -private: - QImage m_image; -}; - -} // namespace - namespace QmlDesigner { QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const QString &id, const QSize &) { - auto response = std::make_unique<::ImageResponse>(m_defaultImage); + auto response = std::make_unique(m_defaultImage); m_cache.requestImage( id, - [response = QPointer<::ImageResponse>(response.get())](const QImage &image) { + [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, [response, image] { @@ -55,7 +27,8 @@ QQuickImageResponse *ExplicitImageCacheImageProvider::requestImageResponse(const }, Qt::QueuedConnection); }, - [response = QPointer<::ImageResponse>(response.get())](ImageCache::AbortReason abortReason) { + [response = QPointer(response.get())]( + ImageCache::AbortReason abortReason) { QMetaObject::invokeMethod( response, [response, abortReason] { diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index d762299f1cb..63ee2ce6128 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -53,6 +53,22 @@ ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connection ImageCacheCollector::~ImageCacheCollector() = default; +namespace { +QImage scaleImage(const QImage &image, QSize targetSize) +{ + if (image.isNull()) + return {}; + + const qreal ratio = qGuiApp->devicePixelRatio(); + if (ratio > 1.0) + targetSize *= qRound(ratio); + QSize scaledImageSize = image.size().scaled(targetSize.boundedTo(image.size()), + Qt::KeepAspectRatio); + return image.scaled(scaledImageSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + +} +} // namespace + void ImageCacheCollector::start(Utils::SmallStringView name, Utils::SmallStringView state, const ImageCache::AuxiliaryData &auxiliaryData, @@ -104,17 +120,9 @@ void ImageCacheCollector::start(Utils::SmallStringView name, auto callback = [=, captureCallback = std::move(captureCallback)](const QImage &image) { if (nullImageHandling == ImageCacheCollectorNullImageHandling::CaptureNullImage || !image.isNull()) { - QSize targetSize {96, 96}; - const qreal ratio = qGuiApp->devicePixelRatio(); - if (ratio > 1.0) - targetSize *= qRound(ratio); - QSize smallImageSize = image.size().scaled(targetSize.boundedTo(image.size()), - Qt::KeepAspectRatio); - QImage smallImage = image.isNull() ? QImage{} - : image.scaled(smallImageSize, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - captureCallback(image, smallImage); + QImage midSizeImage = scaleImage(image, QSize{300, 300}); + QImage smallImage = scaleImage(midSizeImage, QSize{96, 96}); + captureCallback(image, midSizeImage, smallImage); } }; @@ -138,9 +146,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, abortCallback(ImageCache::AbortReason::Failed); } -std::pair ImageCacheCollector::createImage(Utils::SmallStringView, - Utils::SmallStringView, - const ImageCache::AuxiliaryData &) +ImageCacheCollectorInterface::ImageTuple ImageCacheCollector::createImage( + Utils::SmallStringView, Utils::SmallStringView, const ImageCache::AuxiliaryData &) { return {}; } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index a8c4298d9b8..d43cdbd5fb3 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -43,9 +43,9 @@ public: CaptureCallback captureCallback, AbortCallback abortCallback) override; - std::pair createImage(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData) override; + ImageTuple createImage(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData) override; QIcon createIcon(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h index 20548db554d..ef745f5ba9b 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h @@ -14,9 +14,9 @@ namespace QmlDesigner { class ImageCacheCollectorInterface { public: - using CaptureCallback = ImageCache::CaptureImageWithSmallImageCallback; + using CaptureCallback = ImageCache::CaptureImageWithScaledImagesCallback; using AbortCallback = ImageCache::AbortCallback; - using ImagePair = std::pair; + using ImageTuple = std::tuple; virtual void start(Utils::SmallStringView filePath, Utils::SmallStringView extraId, @@ -25,9 +25,9 @@ public: AbortCallback abortCallback) = 0; - virtual ImagePair createImage(Utils::SmallStringView filePath, - Utils::SmallStringView extraId, - const ImageCache::AuxiliaryData &auxiliaryData) + virtual ImageTuple createImage(Utils::SmallStringView filePath, + Utils::SmallStringView extraId, + const ImageCache::AuxiliaryData &auxiliaryData) = 0; virtual QIcon createIcon(Utils::SmallStringView filePath, Utils::SmallStringView extraId, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h index 94a38918483..49678ec77fc 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachedispatchcollector.h @@ -32,9 +32,9 @@ public: m_collectors); } - std::pair createImage(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData) override + ImageTuple createImage(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData) override { return std::apply( [&](const auto &...entries) { @@ -113,11 +113,11 @@ private: } template - std::pair dispatchCreateImage(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData, - const Collector &collector, - const Collectors &...collectors) + ImageTuple dispatchCreateImage(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData, + const Collector &collector, + const Collectors &...collectors) { if (collector.first(filePath, state, auxiliaryData)) { return collector.second->createImage(filePath, state, auxiliaryData); @@ -126,9 +126,9 @@ private: return dispatchCreateImage(filePath, state, auxiliaryData, collectors...); } - std::pair dispatchCreateImage(Utils::SmallStringView, - Utils::SmallStringView, - const ImageCache::AuxiliaryData &) + ImageTuple dispatchCreateImage(Utils::SmallStringView, + Utils::SmallStringView, + const ImageCache::AuxiliaryData &) { qWarning() << "ImageCacheDispatchCollector: cannot handle file type."; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp index 101e1227a01..a8694607a69 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp @@ -106,14 +106,14 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name, QImage image = createFontImage(text, textColor, font, size); if (!image.isNull()) { - captureCallback(std::move(image), {}); + captureCallback(std::move(image), {}, {}); return; } } abortCallback(ImageCache::AbortReason::Failed); } -std::pair ImageCacheFontCollector::createImage( +ImageCacheCollectorInterface::ImageTuple ImageCacheFontCollector::createImage( Utils::SmallStringView name, Utils::SmallStringView, const ImageCache::AuxiliaryData &auxiliaryDataValue) @@ -128,7 +128,7 @@ std::pair ImageCacheFontCollector::createImage( QImage image = createFontImage(text, textColor, font, size); if (!image.isNull()) - return {image, {}}; + return {image, {}, {}}; } return {}; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h index aee1c93c2bd..d427727e6b4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.h @@ -20,9 +20,9 @@ public: CaptureCallback captureCallback, AbortCallback abortCallback) override; - std::pair createImage(Utils::SmallStringView filePath, - Utils::SmallStringView extraId, - const ImageCache::AuxiliaryData &auxiliaryData) override; + ImageTuple createImage(Utils::SmallStringView filePath, + Utils::SmallStringView extraId, + const ImageCache::AuxiliaryData &auxiliaryData) override; QIcon createIcon(Utils::SmallStringView filePath, Utils::SmallStringView extraId, diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp index 02262702c8b..124ea30c194 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp @@ -28,7 +28,7 @@ ImageCacheGenerator::~ImageCacheGenerator() void ImageCacheGenerator::generateImage(Utils::SmallStringView name, Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, - ImageCache::CaptureImageWithSmallImageCallback &&captureCallback, + ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData) { @@ -113,21 +113,26 @@ void ImageCacheGenerator::startGeneration() task.filePath, task.extraId, std::move(task.auxiliaryData), - [this, task](const QImage &image, const QImage &smallImage) { - if (image.isNull() && smallImage.isNull()) + [this, task](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { + if (image.isNull() && midSizeImage.isNull() && smallImage.isNull()) callCallbacks(task.abortCallbacks, ImageCache::AbortReason::Failed); else - callCallbacks(task.captureCallbacks, image, smallImage); + callCallbacks(task.captureCallbacks, image, midSizeImage, smallImage); m_storage.storeImage(createId(task.filePath, task.extraId), task.timeStamp, image, + midSizeImage, smallImage); }, [this, task](ImageCache::AbortReason abortReason) { callCallbacks(task.abortCallbacks, abortReason); if (abortReason != ImageCache::AbortReason::Abort) - m_storage.storeImage(createId(task.filePath, task.extraId), task.timeStamp, {}, {}); + m_storage.storeImage(createId(task.filePath, task.extraId), + task.timeStamp, + {}, + {}, + {}); }); std::lock_guard lock{m_mutex}; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h index 7d017df10b2..6ae9ccc21d1 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h @@ -33,7 +33,7 @@ public: void generateImage(Utils::SmallStringView filePath, Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, - ImageCache::CaptureImageWithSmallImageCallback &&captureCallback, + ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData) override; void clean() override; @@ -48,7 +48,7 @@ private: Utils::SmallStringView extraId, ImageCache::AuxiliaryData &&auxiliaryData, Sqlite::TimeStamp timeStamp, - ImageCache::CaptureImageWithSmallImageCallback &&captureCallback, + ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback) : filePath(filePath) , extraId(std::move(extraId)) @@ -61,7 +61,7 @@ private: Utils::PathString filePath; Utils::SmallString extraId; ImageCache::AuxiliaryData auxiliaryData; - std::vector captureCallbacks; + std::vector captureCallbacks; std::vector abortCallbacks; Sqlite::TimeStamp timeStamp; }; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h index f2f3fa7bcfc..4c95c07df60 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h @@ -17,7 +17,7 @@ public: virtual void generateImage(Utils::SmallStringView name, Utils::SmallStringView extraId, Sqlite::TimeStamp timeStamp, - ImageCache::CaptureImageWithSmallImageCallback &&captureCallback, + ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, ImageCache::AbortCallback &&abortCallback, ImageCache::AuxiliaryData &&auxiliaryData) = 0; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp new file mode 100644 index 00000000000..38fc9491d22 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "imagecacheimageresponse.h" + +namespace QmlDesigner { + +QQuickTextureFactory *ImageCacheImageResponse::textureFactory() const +{ + return QQuickTextureFactory::textureFactoryForImage(m_image); +} + +void ImageCacheImageResponse::setImage(const QImage &image) +{ + m_image = image; + + emit finished(); +} + +void ImageCacheImageResponse::abort() +{ + emit finished(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h new file mode 100644 index 00000000000..1a4400e9893 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheimageresponse.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +class AsynchronousImageCache; + +class ImageCacheImageResponse : public QQuickImageResponse +{ +public: + ImageCacheImageResponse(const QImage &defaultImage = {}) + : m_image(defaultImage) + {} + + QQuickTextureFactory *textureFactory() const override; + + void setImage(const QImage &image); + QImage image() const { return m_image; } + + void abort(); + +private: + QImage m_image; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h index 4c568d268a5..c6d66bb3089 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h @@ -51,6 +51,26 @@ public: } } + ImageEntry fetchMidSizeImage(Utils::SmallStringView name, + Sqlite::TimeStamp minimumTimeStamp) const override + { + try { + Sqlite::DeferredTransaction transaction{database}; + + auto optionalBlob = selectMidSizeImageStatement.template optionalValue( + name, minimumTimeStamp.value); + + transaction.commit(); + + if (optionalBlob) + return {readImage(optionalBlob->byteArray)}; + + return {}; + } catch (const Sqlite::StatementIsBusy &) { + return fetchMidSizeImage(name, minimumTimeStamp); + } + } + ImageEntry fetchSmallImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override { @@ -95,22 +115,25 @@ public: void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image, + const QImage &midSizeImage, const QImage &smallImage) override { try { Sqlite::ImmediateTransaction transaction{database}; auto imageBuffer = createBuffer(image); + auto midSizeImageBuffer = createBuffer(midSizeImage); auto smallImageBuffer = createBuffer(smallImage); upsertImageStatement.write(name, newTimeStamp.value, createBlobView(imageBuffer.get()), + createBlobView(midSizeImageBuffer.get()), createBlobView(smallImageBuffer.get())); transaction.commit(); } catch (const Sqlite::StatementIsBusy &) { - return storeImage(name, newTimeStamp, image, smallImage); + return storeImage(name, newTimeStamp, image, midSizeImage, smallImage); } } @@ -158,12 +181,15 @@ private: Sqlite::ExclusiveTransaction transaction{database}; createImagesTable(database); + database.setVersion(1); transaction.commit(); database.setIsInitialized(true); database.walCheckpointFull(); + } else if (database.version() < 1) { + updateTableToVersion1(database); } } @@ -179,6 +205,7 @@ private: imageTable.addColumn("mtime", Sqlite::ColumnType::Integer); imageTable.addColumn("image", Sqlite::ColumnType::Blob); imageTable.addColumn("smallImage", Sqlite::ColumnType::Blob); + imageTable.addColumn("midSizeImage", Sqlite::ColumnType::Blob); imageTable.initialize(database); @@ -194,6 +221,17 @@ private: iconTable.initialize(database); } + + void updateTableToVersion1(DatabaseType &database) + { + Sqlite::ExclusiveTransaction transaction{database}; + + database.execute("DELETE FROM images"); + database.execute("ALTER TABLE images ADD COLUMN midSizeImage"); + database.setVersion(1); + + transaction.commit(); + } }; Sqlite::BlobView createBlobView(QBuffer *buffer) @@ -264,14 +302,17 @@ public: Sqlite::ImmediateNonThrowingDestructorTransaction transaction{database}; mutable ReadStatement<1, 2> selectImageStatement{ "SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database}; + mutable ReadStatement<1, 2> selectMidSizeImageStatement{ + "SELECT midSizeImage FROM images WHERE name=?1 AND mtime >= ?2", database}; mutable ReadStatement<1, 2> selectSmallImageStatement{ "SELECT smallImage FROM images WHERE name=?1 AND mtime >= ?2", database}; mutable ReadStatement<1, 2> selectIconStatement{ "SELECT icon FROM icons WHERE name=?1 AND mtime >= ?2", database}; - WriteStatement<4> upsertImageStatement{ - "INSERT INTO images(name, mtime, image, smallImage) VALUES (?1, ?2, ?3, ?4) ON " - "CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, " - "smallImage=excluded.smallImage", + WriteStatement<5> upsertImageStatement{ + "INSERT INTO images(name, mtime, image, midSizeImage, smallImage) " + "VALUES (?1, ?2, ?3, ?4, ?5) " + "ON CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, " + "midSizeImage=excluded.midSizeImage, smallImage=excluded.smallImage", database}; WriteStatement<3> upsertIconStatement{ "INSERT INTO icons(name, mtime, icon) VALUES (?1, ?2, ?3) ON " diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h index 00f4ce4ecb5..f02ebf037cf 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h @@ -21,6 +21,8 @@ public: virtual ImageEntry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0; + virtual ImageEntry fetchMidSizeImage(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, @@ -28,6 +30,7 @@ public: virtual void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image, + const QImage &midSizeImage, const QImage &smallImage) = 0; virtual void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) = 0; diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp index bae2e5703af..4341609cc10 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp @@ -66,9 +66,8 @@ void MeshImageCacheCollector::start(Utils::SmallStringView name, m_imageCacheCollector.start(path, state, auxiliaryData, captureCallback, abortCallback); } -std::pair MeshImageCacheCollector::createImage(Utils::SmallStringView, - Utils::SmallStringView, - const ImageCache::AuxiliaryData &) +ImageCacheCollectorInterface::ImageTuple MeshImageCacheCollector::createImage( + Utils::SmallStringView, Utils::SmallStringView, const ImageCache::AuxiliaryData &) { return {}; } diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h index c7f4e3df1f5..34830e01a9d 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.h @@ -31,9 +31,9 @@ public: CaptureCallback captureCallback, AbortCallback abortCallback) override; - std::pair createImage(Utils::SmallStringView filePath, - Utils::SmallStringView state, - const ImageCache::AuxiliaryData &auxiliaryData) override; + ImageTuple createImage(Utils::SmallStringView filePath, + Utils::SmallStringView state, + const ImageCache::AuxiliaryData &auxiliaryData) override; QIcon createIcon(Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp new file mode 100644 index 00000000000..2266e37bb92 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.cpp @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "midsizeimagecacheprovider.h" +#include "imagecacheimageresponse.h" + +#include + +#include + +namespace QmlDesigner { + +QQuickImageResponse *MidSizeImageCacheProvider::requestImageResponse(const QString &id, const QSize &) +{ + auto response = std::make_unique(m_defaultImage); + + m_cache.requestMidSizeImage( + id, + [response = QPointer(response.get())](const QImage &image) { + QMetaObject::invokeMethod( + response, + [response, image] { + if (response) + response->setImage(image); + }, + Qt::QueuedConnection); + }, + [response = QPointer(response.get())]( + ImageCache::AbortReason abortReason) { + QMetaObject::invokeMethod( + response, + [response, abortReason] { + switch (abortReason) { + case ImageCache::AbortReason::Failed: + if (response) + response->abort(); + break; + case ImageCache::AbortReason::Abort: + response->cancel(); + break; + } + }, + Qt::QueuedConnection); + }); + + return response.release(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h new file mode 100644 index 00000000000..b3a84f0abd2 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +class AsynchronousImageCache; + +class MidSizeImageCacheProvider : public QQuickAsyncImageProvider +{ +public: + MidSizeImageCacheProvider(AsynchronousImageCache &imageCache, const QImage &defaultImage = {}) + : m_cache{imageCache} + , m_defaultImage(defaultImage) + {} + + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; + QImage defaultImage() const { return m_defaultImage; } + +private: + AsynchronousImageCache &m_cache; + QImage m_defaultImage; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp index 09cdcbbf779..927a9415834 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.cpp @@ -3,36 +3,21 @@ #include "smallimagecacheprovider.h" +#include "imagecacheimageresponse.h" + #include #include namespace QmlDesigner { -QQuickTextureFactory *ImageResponse::textureFactory() const -{ - return QQuickTextureFactory::textureFactoryForImage(m_image); -} - -void ImageResponse::setImage(const QImage &image) -{ - m_image = image; - - emit finished(); -} - -void ImageResponse::abort() -{ - emit finished(); -} - QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString &id, const QSize &) { - auto response = std::make_unique(m_defaultImage); + auto response = std::make_unique(m_defaultImage); m_cache.requestSmallImage( id, - [response = QPointer(response.get())](const QImage &image) { + [response = QPointer(response.get())](const QImage &image) { QMetaObject::invokeMethod( response, [response, image] { @@ -41,7 +26,7 @@ QQuickImageResponse *SmallImageCacheProvider::requestImageResponse(const QString }, Qt::QueuedConnection); }, - [response = QPointer(response.get())]( + [response = QPointer(response.get())]( ImageCache::AbortReason abortReason) { QMetaObject::invokeMethod( response, diff --git a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h index 54b3b63f1d5..96834fba4b4 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h +++ b/src/plugins/qmldesigner/designercore/imagecache/smallimagecacheprovider.h @@ -4,30 +4,11 @@ #pragma once #include -#include namespace QmlDesigner { class AsynchronousImageCache; -class ImageResponse : public QQuickImageResponse -{ -public: - ImageResponse(const QImage &defaultImage) - : m_image(defaultImage) - {} - - QQuickTextureFactory *textureFactory() const override; - - void setImage(const QImage &image); - QImage image() const { return m_image; } - - void abort(); - -private: - QImage m_image; -}; - class SmallImageCacheProvider : public QQuickAsyncImageProvider { public: diff --git a/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp index 72fd8e937a4..4c7c527d96c 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/synchronousimagecache.cpp @@ -32,13 +32,36 @@ QImage SynchronousImageCache::image(Utils::PathString filePath, if (entry) return *entry; - const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData); + const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath, + extraId, + auxiliaryData); - m_storage.storeImage(id, timeStamp, image, smallImage); + m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage); return image; } +QImage SynchronousImageCache::midSizeImage(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.fetchMidSizeImage(id, timeStamp); + + if (entry) + return *entry; + + const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath, + extraId, + auxiliaryData); + + m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage); + + return midSizeImage; +} + QImage SynchronousImageCache::smallImage(Utils::PathString filePath, Utils::SmallString extraId, const ImageCache::AuxiliaryData &auxiliaryData) @@ -51,9 +74,11 @@ QImage SynchronousImageCache::smallImage(Utils::PathString filePath, if (entry) return *entry; - const auto &[image, smallImage] = m_collector.createImage(filePath, extraId, auxiliaryData); + const auto &[image, midSizeImage, smallImage] = m_collector.createImage(filePath, + extraId, + auxiliaryData); - m_storage.storeImage(id, timeStamp, image, smallImage); + m_storage.storeImage(id, timeStamp, image, midSizeImage, smallImage); return smallImage; } diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h index 9ac6d7a9330..ca6048fe58a 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousexplicitimagecache.h @@ -27,6 +27,10 @@ public: ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, Utils::SmallString extraId = {}); + void requestMidSizeImage(Utils::PathString name, + ImageCache::CaptureImageCallback captureCallback, + ImageCache::AbortCallback abortCallback, + Utils::SmallString extraId = {}); void requestSmallImage(Utils::PathString name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, @@ -35,7 +39,7 @@ public: void clean(); private: - enum class RequestType { Image, SmallImage, Icon }; + enum class RequestType { Image, MidSizeImage, SmallImage, Icon }; struct RequestEntry { RequestEntry() = default; diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h index d547a0979e3..0533a4521e0 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecache.h @@ -33,6 +33,11 @@ public: ImageCache::AbortCallback abortCallback, Utils::SmallString extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) override; + void requestMidSizeImage(Utils::PathString name, + ImageCache::CaptureImageCallback captureCallback, + ImageCache::AbortCallback abortCallback, + Utils::SmallString extraId = {}, + ImageCache::AuxiliaryData auxiliaryData = {}) override; void requestSmallImage(Utils::PathString name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, @@ -42,7 +47,7 @@ public: void clean(); private: - enum class RequestType { Image, SmallImage, Icon }; + enum class RequestType { Image, MidSizeImage, SmallImage, Icon }; struct Entry { Entry() = default; diff --git a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h index 743231a26dc..9b685793f48 100644 --- a/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h +++ b/src/plugins/qmldesigner/designercore/include/asynchronousimagecacheinterface.h @@ -20,6 +20,12 @@ public: Utils::SmallString extraId = {}, ImageCache::AuxiliaryData auxiliaryData = {}) = 0; + virtual void requestMidSizeImage(Utils::PathString name, + ImageCache::CaptureImageCallback captureCallback, + ImageCache::AbortCallback abortCallback, + Utils::SmallString extraId = {}, + ImageCache::AuxiliaryData auxiliaryData = {}) + = 0; virtual void requestSmallImage(Utils::PathString name, ImageCache::CaptureImageCallback captureCallback, ImageCache::AbortCallback abortCallback, diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index 0941b5e4072..4ff8cf0b2c1 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -46,7 +46,8 @@ using AuxiliaryData = std::variant; -using CaptureImageWithSmallImageCallback = std::function; +using CaptureImageWithScaledImagesCallback = std::function< + void(const QImage &image, const QImage &midSizeImage, const QImage &smallImage)>; using AbortCallback = std::function; } // namespace ImageCache diff --git a/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h b/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h index ca8a6595a53..757432ddba6 100644 --- a/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h +++ b/src/plugins/qmldesigner/designercore/include/synchronousimagecache.h @@ -39,6 +39,9 @@ public: QImage image(Utils::PathString filePath, Utils::SmallString extraId = {}, const ImageCache::AuxiliaryData &auxiliaryData = {}); + QImage midSizeImage(Utils::PathString filePath, + Utils::SmallString extraId = {}, + const ImageCache::AuxiliaryData &auxiliaryData = {}); QImage smallImage(Utils::PathString filePath, Utils::SmallString extraId = {}, const ImageCache::AuxiliaryData &auxiliaryData = {}); diff --git a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp index 68cc17c7b04..0502138c33f 100644 --- a/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp +++ b/tests/unit/unittest/asynchronousexplicitimagecache-test.cpp @@ -22,6 +22,7 @@ protected: NiceMock mockStorage; QmlDesigner::AsynchronousExplicitImageCache cache{mockStorage}; QImage image1{10, 10, QImage::Format_ARGB32}; + QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; }; @@ -96,6 +97,63 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutImag notification.wait(); } +TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorage) +{ + EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{}))) + .WillRepeatedly([&](Utils::SmallStringView, auto) { + notification.notify(); + return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromStorage) +{ + ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage1})); + + EXPECT_CALL(mockCaptureCallback, Call(Eq(midSizeImage1))).WillRepeatedly([&](const QImage &) { + notification.notify(); + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsAbortCallbackWithoutEntry) +{ + ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{})); + + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + .WillRepeatedly([&](auto) { notification.notify(); }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageCallsAbortCallbackWithoutMidSizeImage) +{ + ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}})); + + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + .WillRepeatedly([&](auto) { notification.notify(); }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + TEST_F(AsynchronousExplicitImageCache, RequestSmallImageFetchesSmallImageFromStorage) { EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{}))) @@ -223,6 +281,21 @@ TEST_F(AsynchronousExplicitImageCache, RequestImageWithExtraIdFetchesImageFromSt notification.wait(); } +TEST_F(AsynchronousExplicitImageCache, RequestMidSizeImageWithExtraIdFetchesImageFromStorage) +{ + EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), _)) + .WillRepeatedly([&](Utils::SmallStringView, auto) { + notification.notify(); + return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction(), + "extraId1"); + notification.wait(); +} + TEST_F(AsynchronousExplicitImageCache, RequestSmallImageWithExtraIdFetchesImageFromStorage) { EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), _)) diff --git a/tests/unit/unittest/asynchronousimagecache-test.cpp b/tests/unit/unittest/asynchronousimagecache-test.cpp index 3d381ea4b12..a98f67c1f56 100644 --- a/tests/unit/unittest/asynchronousimagecache-test.cpp +++ b/tests/unit/unittest/asynchronousimagecache-test.cpp @@ -24,6 +24,7 @@ protected: NiceMock mockTimeStampProvider; QmlDesigner::AsynchronousImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider}; QImage image1{10, 10, QImage::Format_ARGB32}; + QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; }; @@ -105,7 +106,7 @@ TEST_F(AsynchronousImageCache, RequestImageCallsCaptureCallbackWithImageFromGene { ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { - callback(QImage{image1}, QImage{smallImage1}); + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); notification.notify(); }); @@ -133,6 +134,113 @@ TEST_F(AsynchronousImageCache, RequestImageCallsAbortCallbackFromGenerator) notification.wait(); } +TEST_F(AsynchronousImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorage) +{ + EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillRepeatedly([&](Utils::SmallStringView, auto) { + notification.notify(); + return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageFetchesMidSizeImageFromStorageWithTimeStamp) +{ + EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) + .WillRepeatedly(Return(Sqlite::TimeStamp{123})); + EXPECT_CALL(mockStorage, + fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}))) + .WillRepeatedly([&](Utils::SmallStringView, auto) { + notification.notify(); + return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromStorage) +{ + ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1})); + + EXPECT_CALL(mockCaptureCallback, Call(Eq(smallImage1))).WillRepeatedly([&](const QImage &) { + notification.notify(); + }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsAbortCallbackWithoutMidSizeImage) +{ + ON_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml"), _)) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}})); + + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) + .WillRepeatedly([&](auto) { notification.notify(); }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageRequestImageFromGenerator) +{ + 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, auto, auto) { notification.notify(); }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsCaptureCallbackWithImageFromGenerator) +{ + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); + notification.notify(); + }); + + EXPECT_CALL(mockCaptureCallback, Call(Eq(midSizeImage1))); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + +TEST_F(AsynchronousImageCache, RequestMidSizeImageCallsAbortCallbackFromGenerator) +{ + ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto &&, auto &&abortCallback, auto) { + abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); + notification.notify(); + }); + + EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction()); + notification.wait(); +} + TEST_F(AsynchronousImageCache, RequestSmallImageFetchesSmallImageFromStorage) { EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) @@ -211,7 +319,7 @@ TEST_F(AsynchronousImageCache, RequestSmallImageCallsCaptureCallbackWithImageFro { ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto &&callback, auto, auto) { - callback(QImage{image1}, QImage{smallImage1}); + callback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); notification.notify(); }); @@ -243,7 +351,7 @@ TEST_F(AsynchronousImageCache, CleanRemovesEntries) { EXPECT_CALL(mockGenerator, generateImage(_, _, _, _, _, _)) .WillRepeatedly([&](auto, auto, auto, auto &&captureCallback, auto &&, auto) { - captureCallback(QImage{}, QImage{}); + captureCallback(QImage{}, QImage{}, QImage{}); waitInThread.wait(); }); cache.requestSmallImage("/path/to/Component1.qml", @@ -315,6 +423,21 @@ TEST_F(AsynchronousImageCache, RequestImageWithExtraIdFetchesImageFromStorage) notification.wait(); } +TEST_F(AsynchronousImageCache, RequestMidSizeImageWithExtraIdFetchesImageFromStorage) +{ + EXPECT_CALL(mockStorage, fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), _)) + .WillRepeatedly([&](Utils::SmallStringView, auto) { + notification.notify(); + return QmlDesigner::ImageCacheStorageInterface::ImageEntry{}; + }); + + cache.requestMidSizeImage("/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"), _)) @@ -347,6 +470,23 @@ TEST_F(AsynchronousImageCache, RequestImageWithExtraIdRequestImageFromGenerator) notification.wait(); } +TEST_F(AsynchronousImageCache, RequestMidSizeImageWithExtraIdRequestImageFromGenerator) +{ + 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.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction(), + "extraId1"); + notification.wait(); +} + TEST_F(AsynchronousImageCache, RequestSmallImageWithExtraIdRequestImageFromGenerator) { ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) @@ -392,6 +532,34 @@ TEST_F(AsynchronousImageCache, RequestImageWithAuxiliaryDataRequestImageFromGene notification.wait(); } +TEST_F(AsynchronousImageCache, RequestMidSizeImageWithAuxiliaryDataRequestImageFromGenerator) +{ + using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData; + std::vector 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( + AllOf(Field(&FontCollectorSizesAuxiliaryData::sizes, + ElementsAre(QSize{20, 11})), + Field(&FontCollectorSizesAuxiliaryData::colorName, + Eq(u"color")))))) + .WillRepeatedly([&](auto, auto, auto, auto &&, auto, auto) { notification.notify(); }); + + cache.requestMidSizeImage("/path/to/Component.qml", + mockCaptureCallback.AsStdFunction(), + mockAbortCallback.AsStdFunction(), + "extraId1", + FontCollectorSizesAuxiliaryData{sizes, "color", "text"}); + notification.wait(); +} + TEST_F(AsynchronousImageCache, RequestSmallImageWithAuxiliaryDataRequestImageFromGenerator) { using QmlDesigner::ImageCache::FontCollectorSizesAuxiliaryData; diff --git a/tests/unit/unittest/asynchronousimagefactory-test.cpp b/tests/unit/unittest/asynchronousimagefactory-test.cpp index 125a5cec0eb..0d207162785 100644 --- a/tests/unit/unittest/asynchronousimagefactory-test.cpp +++ b/tests/unit/unittest/asynchronousimagefactory-test.cpp @@ -32,6 +32,7 @@ protected: NiceMock timeStampProviderMock; QmlDesigner::AsynchronousImageFactory factory{storageMock, timeStampProviderMock, collectorMock}; QImage image1{10, 10, QImage::Format_ARGB32}; + QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; }; @@ -162,14 +163,17 @@ TEST_F(AsynchronousImageFactory, CaptureImageCallbackStoresImage) VariantWith(std::monostate{}), _, _)) - .WillByDefault([&](auto, auto, auto, auto capture, auto) { capture(image1, smallImage1); }); + .WillByDefault([&](auto, auto, auto, auto capture, auto) { + capture(image1, midSizeImage1, smallImage1); + }); EXPECT_CALL(storageMock, storeImage(Eq("/path/to/Component.qml+id"), Sqlite::TimeStamp{125}, Eq(image1), + Eq(midSizeImage1), Eq(smallImage1))) - .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); + .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); factory.generate("/path/to/Component.qml", "id"); notification.wait(); diff --git a/tests/unit/unittest/imagecachecollectormock.h b/tests/unit/unittest/imagecachecollectormock.h index 1dd3c69f568..6f5461e4ef5 100644 --- a/tests/unit/unittest/imagecachecollectormock.h +++ b/tests/unit/unittest/imagecachecollectormock.h @@ -19,7 +19,7 @@ public: ImageCacheCollectorInterface::AbortCallback abortCallback), (override)); - MOCK_METHOD(ImagePair, + MOCK_METHOD(ImageTuple, createImage, (Utils::SmallStringView filePath, Utils::SmallStringView state, diff --git a/tests/unit/unittest/imagecachedispatchcollector-test.cpp b/tests/unit/unittest/imagecachedispatchcollector-test.cpp index 7029554d5cc..3a63271d015 100644 --- a/tests/unit/unittest/imagecachedispatchcollector-test.cpp +++ b/tests/unit/unittest/imagecachedispatchcollector-test.cpp @@ -19,14 +19,16 @@ MATCHER_P(IsIcon, icon, std::string(negation ? "isn't " : "is ") + PrintToString return icon.availableSizes() == other.availableSizes(); } -MATCHER_P2(IsImage, +MATCHER_P3(IsImage, image, + midSizeImage, smallImage, - std::string(negation ? "aren't " : "are ") + PrintToString(image) + " and " - + PrintToString(smallImage)) + std::string(negation ? "aren't " : "are ") + PrintToString(image) + ", " + + PrintToString(midSizeImage) + " and " + PrintToString(smallImage)) { - const std::pair &other = arg; - return other.first == image && other.second == smallImage; + const std::tuple &other = arg; + return std::get<0>(other) == image && std::get<1>(other) == midSizeImage + && std::get<2>(other) == smallImage; } class ImageCacheDispatchCollector : public ::testing::Test @@ -38,21 +40,23 @@ protected: ON_CALL(collectorMock2, createIcon(_, _, _)).WillByDefault(Return(icon2)); ON_CALL(collectorMock1, createImage(_, _, _)) - .WillByDefault(Return(std::pair{image1, smallImage1})); + .WillByDefault(Return(std::tuple{image1, midSizeImage1, smallImage1})); ON_CALL(collectorMock2, createImage(_, _, _)) - .WillByDefault(Return(std::pair{image2, smallImage2})); + .WillByDefault(Return(std::tuple{image2, midSizeImage2, smallImage2})); } protected: std::vector sizes{{20, 11}}; - NiceMock> captureCallbackMock; + NiceMock> captureCallbackMock; NiceMock> abortCallbackMock; NiceMock collectorMock1; NiceMock collectorMock2; QImage image1{1, 1, QImage::Format_ARGB32}; - QImage image2{2, 2, QImage::Format_ARGB32}; - QImage smallImage1{1, 1, QImage::Format_ARGB32}; - QImage smallImage2{2, 1, QImage::Format_ARGB32}; + QImage image2{1, 2, QImage::Format_ARGB32}; + QImage midSizeImage1{2, 1, QImage::Format_ARGB32}; + QImage midSizeImage2{2, 2, QImage::Format_ARGB32}; + QImage smallImage1{3, 1, QImage::Format_ARGB32}; + QImage smallImage2{3, 2, QImage::Format_ARGB32}; QIcon icon1{QPixmap::fromImage(image1)}; QIcon icon2{QPixmap::fromImage(image2)}; }; @@ -79,7 +83,7 @@ TEST_F(ImageCacheDispatchCollector, CallQmlCollectorStart) }, &collectorMock2))}; - EXPECT_CALL(captureCallbackMock, Call(_, _)); + EXPECT_CALL(captureCallbackMock, Call(_, _, _)); EXPECT_CALL(abortCallbackMock, Call(_)); EXPECT_CALL(collectorMock2, start(Eq("foo.qml"), @@ -91,7 +95,7 @@ TEST_F(ImageCacheDispatchCollector, CallQmlCollectorStart) _, _)) .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) { - captureCallback(QImage{}, QImage{}); + captureCallback({}, {}, {}); abortCallback(QmlDesigner::ImageCache::AbortReason::Abort); }); EXPECT_CALL(collectorMock1, start(_, _, _, _, _)).Times(0); @@ -115,7 +119,7 @@ TEST_F(ImageCacheDispatchCollector, CallUiFileCollectorStart) const QmlDesigner::ImageCache::AuxiliaryData &) { return true; }, &collectorMock2))}; - EXPECT_CALL(captureCallbackMock, Call(_, _)); + EXPECT_CALL(captureCallbackMock, Call(_, _, _)); EXPECT_CALL(abortCallbackMock, Call(_)); EXPECT_CALL(collectorMock1, start(Eq("foo.ui.qml"), @@ -127,7 +131,7 @@ TEST_F(ImageCacheDispatchCollector, CallUiFileCollectorStart) _, _)) .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto abortCallback) { - captureCallback(QImage{}, QImage{}); + captureCallback({}, {}, {}); abortCallback(QmlDesigner::ImageCache::AbortReason::Abort); }); EXPECT_CALL(collectorMock2, start(_, _, _, _, _)).Times(0); @@ -288,7 +292,7 @@ TEST_F(ImageCacheDispatchCollector, CallFirstCollectorCreateImage) "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}); - ASSERT_THAT(image, IsImage(image1, smallImage1)); + ASSERT_THAT(image, IsImage(image1, midSizeImage1, smallImage1)); } TEST_F(ImageCacheDispatchCollector, FirstCollectorCreateImageCalls) @@ -334,7 +338,7 @@ TEST_F(ImageCacheDispatchCollector, CallSecondCollectorCreateImage) "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}); - ASSERT_THAT(image, IsImage(image2, smallImage2)); + ASSERT_THAT(image, IsImage(image2, midSizeImage2, smallImage2)); } TEST_F(ImageCacheDispatchCollector, SecondCollectorCreateImageCalls) @@ -380,7 +384,8 @@ TEST_F(ImageCacheDispatchCollector, DontCallCollectorCreateImageForUnknownFile) "state", FontCollectorSizesAuxiliaryData{sizes, "color", "text"}); - ASSERT_TRUE(image.first.isNull() && image.second.isNull()); + ASSERT_TRUE(std::get<0>(image).isNull() && std::get<1>(image).isNull() + && std::get<2>(image).isNull()); } } // namespace diff --git a/tests/unit/unittest/imagecachegenerator-test.cpp b/tests/unit/unittest/imagecachegenerator-test.cpp index 4e93e997db5..e105c2f3686 100644 --- a/tests/unit/unittest/imagecachegenerator-test.cpp +++ b/tests/unit/unittest/imagecachegenerator-test.cpp @@ -32,8 +32,9 @@ protected: Notification waitInThread; Notification notification; QImage image1{10, 10, QImage::Format_ARGB32}; + QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; QImage smallImage1{1, 1, QImage::Format_ARGB32}; - NiceMock> imageCallbackMock; + NiceMock> imageCallbackMock; NiceMock> abortCallbackMock; NiceMock collectorMock; NiceMock storageMock; @@ -44,11 +45,11 @@ TEST_F(ImageCacheGenerator, CallsCollectorWithCaptureCallback) { EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillRepeatedly([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); - EXPECT_CALL(imageCallbackMock, Call(_, _)).WillRepeatedly([&](const QImage &, const QImage &) { - notification.notify(); - }); + EXPECT_CALL(imageCallbackMock, Call(_, _, _)) + .WillRepeatedly( + [&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); notification.wait(); @@ -66,17 +67,16 @@ TEST_F(ImageCacheGenerator, CallsCollectorOnlyIfNotProcessing) TEST_F(ImageCacheGenerator, ProcessTaskAfterFirstFinished) { - ON_CALL(imageCallbackMock, Call(_, _)).WillByDefault([&](const QImage &, const QImage &) { - notification.notify(); - }); + ON_CALL(imageCallbackMock, Call(_, _, _)) + .WillByDefault([&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); EXPECT_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); EXPECT_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); @@ -88,7 +88,7 @@ TEST_F(ImageCacheGenerator, DontCrashAtDestructingGenerator) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); generator.generateImage( @@ -105,12 +105,16 @@ TEST_F(ImageCacheGenerator, StoreImage) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); EXPECT_CALL(storageMock, - storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1))) - .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); }); + storeImage(Eq("name"), + Eq(Sqlite::TimeStamp{11}), + Eq(image1), + Eq(midSizeImage1), + Eq(smallImage1))) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", {}, {11}, imageCallbackMock.AsStdFunction(), {}, {}); notification.wait(); @@ -120,12 +124,16 @@ TEST_F(ImageCacheGenerator, StoreImageWithExtraId) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); EXPECT_CALL(storageMock, - storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(image1), Eq(smallImage1))) - .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); }); + storeImage(Eq("name+extraId"), + Eq(Sqlite::TimeStamp{11}), + Eq(image1), + Eq(midSizeImage1), + Eq(smallImage1))) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", "extraId", {11}, imageCallbackMock.AsStdFunction(), {}, {}); notification.wait(); @@ -135,12 +143,13 @@ TEST_F(ImageCacheGenerator, StoreNullImage) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}); + captureCallback(QImage{}, QImage{}, QImage{}); }); - EXPECT_CALL(storageMock, - storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}))) - .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL( + storageMock, + storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}), Eq(QImage{}))) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -151,12 +160,16 @@ TEST_F(ImageCacheGenerator, StoreNullImageWithExtraId) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}); + captureCallback(QImage{}, QImage{}, QImage{}); }); EXPECT_CALL(storageMock, - storeImage(Eq("name+extraId"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}))) - .WillRepeatedly([&](auto, auto, auto, auto) { notification.notify(); }); + storeImage(Eq("name+extraId"), + Eq(Sqlite::TimeStamp{11}), + Eq(QImage{}), + Eq(QImage{}), + Eq(QImage{}))) + .WillRepeatedly([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage("name", "extraId", @@ -171,16 +184,15 @@ TEST_F(ImageCacheGenerator, AbortCallback) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto, auto abortCallback) { abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); }); - EXPECT_CALL(imageCallbackMock, Call(_, _)).WillOnce([&](const QImage &, const QImage &) { - notification.notify(); - }); + EXPECT_CALL(imageCallbackMock, Call(_, _, _)) + .WillOnce([&](const QImage &, const QImage &, const QImage &) { notification.notify(); }); EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) .WillOnce([&](auto) { notification.notify(); }); @@ -200,7 +212,7 @@ TEST_F(ImageCacheGenerator, StoreNullImageForAbortCallbackAbort) ON_CALL(collectorMock, start(Eq("dummyNotify"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); - EXPECT_CALL(storageMock, storeImage(Eq("name"), _, _, _)).Times(0); + EXPECT_CALL(storageMock, storeImage(Eq("name"), _, _, _, _)).Times(0); generator.generateImage( "name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -215,9 +227,10 @@ TEST_F(ImageCacheGenerator, DontStoreNullImageForAbortCallbackFailed) abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); }); - EXPECT_CALL(storageMock, - storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}))) - .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL( + storageMock, + storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{}), Eq(QImage{}), Eq(QImage{}))) + .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -228,7 +241,7 @@ TEST_F(ImageCacheGenerator, AbortForNullImage) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}); + captureCallback(QImage{}, QImage{}, QImage{}); }); EXPECT_CALL(abortCallbackMock, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) @@ -243,12 +256,11 @@ TEST_F(ImageCacheGenerator, CallImageCallbackIfSmallImageIsNotNull) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, smallImage1); + captureCallback({}, {}, smallImage1); }); - EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(smallImage1))).WillOnce([&](auto, auto) { - notification.notify(); - }); + EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(QImage()), Eq(smallImage1))) + .WillOnce([&](auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -259,11 +271,41 @@ TEST_F(ImageCacheGenerator, StoreImageIfSmallImageIsNotNull) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, smallImage1); + captureCallback({}, {}, smallImage1); }); - EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(smallImage1))) - .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(QImage()), Eq(smallImage1))) + .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); + + generator.generateImage( + "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); + notification.wait(); +} + +TEST_F(ImageCacheGenerator, CallImageCallbackIfMidSizeImageIsNotNull) +{ + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { + captureCallback({}, midSizeImage1, {}); + }); + + EXPECT_CALL(imageCallbackMock, Call(Eq(QImage()), Eq(midSizeImage1), Eq(QImage{}))) + .WillOnce([&](auto, auto, auto) { notification.notify(); }); + + generator.generateImage( + "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); + notification.wait(); +} + +TEST_F(ImageCacheGenerator, StoreImageIfMidSizeImageIsNotNull) +{ + ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) + .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { + captureCallback({}, midSizeImage1, {}); + }); + + EXPECT_CALL(storageMock, storeImage(_, _, Eq(QImage()), Eq(midSizeImage1), Eq(QImage()))) + .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -273,13 +315,11 @@ TEST_F(ImageCacheGenerator, StoreImageIfSmallImageIsNotNull) TEST_F(ImageCacheGenerator, CallImageCallbackIfImageIsNotNull) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(image1, QImage{}); - }); + .WillByDefault( + [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); }); - EXPECT_CALL(imageCallbackMock, Call(Eq(image1), Eq(QImage{}))).WillOnce([&](auto, auto) { - notification.notify(); - }); + EXPECT_CALL(imageCallbackMock, Call(Eq(image1), Eq(QImage{}), Eq(QImage{}))) + .WillOnce([&](auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -289,12 +329,11 @@ TEST_F(ImageCacheGenerator, CallImageCallbackIfImageIsNotNull) TEST_F(ImageCacheGenerator, StoreImageIfImageIsNotNull) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(image1, QImage{}); - }); + .WillByDefault( + [&](auto, auto, auto, auto captureCallback, auto) { captureCallback(image1, {}, {}); }); - EXPECT_CALL(storageMock, storeImage(_, _, Eq(image1), Eq(QImage{}))) - .WillOnce([&](auto, auto, auto, auto) { notification.notify(); }); + EXPECT_CALL(storageMock, storeImage(_, _, Eq(image1), Eq(QImage{}), Eq(QImage{}))) + .WillOnce([&](auto, auto, auto, auto, auto) { notification.notify(); }); generator.generateImage( "name", {}, {}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); @@ -304,7 +343,8 @@ TEST_F(ImageCacheGenerator, StoreImageIfImageIsNotNull) TEST_F(ImageCacheGenerator, CallWalCheckpointFullIfQueueIsEmpty) { ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) - .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}); }); + .WillByDefault( + [&](auto, auto, auto, auto captureCallback, auto) { captureCallback({}, {}, {}); }); EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); }); @@ -339,11 +379,11 @@ TEST_F(ImageCacheGenerator, WaitForFinished) ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { waitInThread.wait(); - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); ON_CALL(collectorMock, start(Eq("name2"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{image1}, QImage{smallImage1}); + captureCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); generator.generateImage( @@ -351,7 +391,7 @@ TEST_F(ImageCacheGenerator, WaitForFinished) generator.generateImage( "name2", {}, {11}, imageCallbackMock.AsStdFunction(), abortCallbackMock.AsStdFunction(), {}); - EXPECT_CALL(imageCallbackMock, Call(_, _)).Times(2); + EXPECT_CALL(imageCallbackMock, Call(_, _, _)).Times(2); waitInThread.notify(); generator.waitForFinished(); @@ -469,7 +509,7 @@ TEST_F(ImageCacheGenerator, UseLastTimeStampIfTasksAreMerged) abortCallback(QmlDesigner::ImageCache::AbortReason::Failed); }); - EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{4}), _, _)); + EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{4}), _, _, _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {3}, {}, abortCallbackMock.AsStdFunction(), {}); @@ -481,18 +521,18 @@ TEST_F(ImageCacheGenerator, UseLastTimeStampIfTasksAreMerged) TEST_F(ImageCacheGenerator, MergeCaptureCallbackIfTasksAreMerged) { - NiceMock> newerImageCallbackMock; + NiceMock> newerImageCallbackMock; ON_CALL(collectorMock, start(Eq("waitDummy"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto, auto) { waitInThread.wait(); }); ON_CALL(collectorMock, start(Eq("notificationDummy"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto, auto) { notification.notify(); }); ON_CALL(collectorMock, start(Eq("name"), _, _, _, _)) .WillByDefault([&](auto, auto, auto, auto imageCallback, auto) { - imageCallback(QImage{image1}, QImage{smallImage1}); + imageCallback(QImage{image1}, QImage{midSizeImage1}, QImage{smallImage1}); }); - EXPECT_CALL(imageCallbackMock, Call(_, _)); - EXPECT_CALL(newerImageCallbackMock, Call(_, _)); + EXPECT_CALL(imageCallbackMock, Call(_, _, _)); + EXPECT_CALL(newerImageCallbackMock, Call(_, _, _)); generator.generateImage("waitDummy", {}, {}, {}, {}, {}); generator.generateImage("name", {}, {}, imageCallbackMock.AsStdFunction(), {}, {}); @@ -529,7 +569,7 @@ TEST_F(ImageCacheGenerator, DontCallNullImageCallback) { EXPECT_CALL(collectorMock, start(_, _, _, _, _)) .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(image1, smallImage1); + captureCallback(image1, midSizeImage1, smallImage1); notification.notify(); }); @@ -541,7 +581,7 @@ TEST_F(ImageCacheGenerator, DontCallNullAbortCallbackForNullImage) { EXPECT_CALL(collectorMock, start(_, _, _, _, _)) .WillOnce([&](auto, auto, auto, auto captureCallback, auto) { - captureCallback(QImage{}, QImage{}); + captureCallback(QImage{}, QImage{}, QImage{}); notification.notify(); }); diff --git a/tests/unit/unittest/imagecachestorage-test.cpp b/tests/unit/unittest/imagecachestorage-test.cpp index 1e892062477..1e2566c0e8e 100644 --- a/tests/unit/unittest/imagecachestorage-test.cpp +++ b/tests/unit/unittest/imagecachestorage-test.cpp @@ -14,6 +14,83 @@ MATCHER_P(IsIcon, icon, std::string(negation ? "is't" : "is") + PrintToString(ic { return arg.availableSizes() == icon.availableSizes(); } + +TEST(ImageCacheStorageUpdateTest, CheckVersionIfDatabaseIsAlreadyInitialized) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true)); + + EXPECT_CALL(databaseMock, version()); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, AddColumnMidSizeIfVersionIsZero) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true)); + EXPECT_CALL(databaseMock, execute(_)).Times(AnyNumber()); + + EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage"))); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, DeleteAllRowsBeforeAddingMidSizeColumn) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true)); + InSequence s; + + EXPECT_CALL(databaseMock, execute(Eq("DELETE FROM images"))); + EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage"))); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, DontCallAddColumnMidSizeIfDatabaseWasNotAlreadyInitialized) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(false)); + EXPECT_CALL(databaseMock, execute(_)).Times(2); + + EXPECT_CALL(databaseMock, execute(Eq("ALTER TABLE images ADD COLUMN midSizeImage"))).Times(0); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, SetVersionToOneIfVersionIsZero) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true)); + + EXPECT_CALL(databaseMock, setVersion(Eq(1))); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, DontSetVersionIfVersionIsOne) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(true)); + ON_CALL(databaseMock, version()).WillByDefault(Return(1)); + + EXPECT_CALL(databaseMock, setVersion(_)).Times(0); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + +TEST(ImageCacheStorageUpdateTest, SetVersionToOneForInitialization) +{ + NiceMock databaseMock; + ON_CALL(databaseMock, isInitialized()).WillByDefault(Return(false)); + ON_CALL(databaseMock, version()).WillByDefault(Return(1)); + + EXPECT_CALL(databaseMock, setVersion(Eq(1))); + + QmlDesigner::ImageCacheStorage storage{databaseMock}; +} + class ImageCacheStorageTest : public testing::Test { protected: @@ -26,12 +103,14 @@ protected: NiceMock databaseMock; QmlDesigner::ImageCacheStorage storage{databaseMock}; ReadStatement<1, 2> &selectImageStatement = storage.selectImageStatement; + ReadStatement<1, 2> &selectMidSizeImageStatement = storage.selectMidSizeImageStatement; ReadStatement<1, 2> &selectSmallImageStatement = storage.selectSmallImageStatement; ReadStatement<1, 2> &selectIconStatement = storage.selectIconStatement; - WriteStatement<4> &upsertImageStatement = storage.upsertImageStatement; + WriteStatement<5> &upsertImageStatement = storage.upsertImageStatement; WriteStatement<3> &upsertIconStatement = storage.upsertIconStatement; QImage image1{10, 10, QImage::Format_ARGB32}; - QImage smallImage1{10, 10, QImage::Format_ARGB32}; + QImage midSizeImage1{5, 5, QImage::Format_ARGB32}; + QImage smallImage1{1, 1, QImage::Format_ARGB32}; QIcon icon1{QPixmap::fromImage(image1)}; }; @@ -67,6 +146,38 @@ TEST_F(ImageCacheStorageTest, FetchImageCallsIsBusy) storage.fetchImage("/path/to/component", {123}); } +TEST_F(ImageCacheStorageTest, FetchMidSizeImageCalls) +{ + InSequence s; + + EXPECT_CALL(databaseMock, deferredBegin()); + EXPECT_CALL(selectMidSizeImageStatement, + valueReturnBlob(TypedEq("/path/to/component"), + TypedEq(123))); + EXPECT_CALL(databaseMock, commit()); + + storage.fetchMidSizeImage("/path/to/component", {123}); +} + +TEST_F(ImageCacheStorageTest, FetchMidSizeImageCallsIsBusy) +{ + InSequence s; + + EXPECT_CALL(databaseMock, deferredBegin()); + EXPECT_CALL(selectMidSizeImageStatement, + valueReturnBlob(TypedEq("/path/to/component"), + TypedEq(123))) + .WillOnce(Throw(Sqlite::StatementIsBusy("busy"))); + EXPECT_CALL(databaseMock, rollback()); + EXPECT_CALL(databaseMock, deferredBegin()); + EXPECT_CALL(selectMidSizeImageStatement, + valueReturnBlob(TypedEq("/path/to/component"), + TypedEq(123))); + EXPECT_CALL(databaseMock, commit()); + + storage.fetchMidSizeImage("/path/to/component", {123}); +} + TEST_F(ImageCacheStorageTest, FetchSmallImageCalls) { InSequence s; @@ -140,10 +251,11 @@ TEST_F(ImageCacheStorageTest, StoreImageCalls) write(TypedEq("/path/to/component"), TypedEq(123), Not(IsEmpty()), + Not(IsEmpty()), Not(IsEmpty()))); EXPECT_CALL(databaseMock, commit()); - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); } TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls) @@ -155,10 +267,11 @@ TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls) write(TypedEq("/path/to/component"), TypedEq(123), IsEmpty(), + IsEmpty(), IsEmpty())); EXPECT_CALL(databaseMock, commit()); - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{}); } TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy) @@ -171,10 +284,11 @@ TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy) write(TypedEq("/path/to/component"), TypedEq(123), IsEmpty(), + IsEmpty(), IsEmpty())); EXPECT_CALL(databaseMock, commit()); - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{}); } TEST_F(ImageCacheStorageTest, StoreIconCalls) @@ -253,29 +367,62 @@ protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; QmlDesigner::ImageCacheStorage storage{database}; QImage image1{createImage()}; - QImage smallImage1{image1.scaled(96, 96)}; + QImage midSizeImage1{image1.scaled(96, 96)}; + QImage smallImage1{image1.scaled(48, 48)}; QIcon icon1{QPixmap::fromImage(image1)}; }; TEST_F(ImageCacheStorageSlowTest, StoreImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, QImage{}, QImage{}); ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(image1)); } TEST_F(ImageCacheStorageSlowTest, StoreEmptyImageAfterEntry) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, midSizeImage1, smallImage1); ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(QImage{})); } +TEST_F(ImageCacheStorageSlowTest, StoreMidSizeImage) +{ + storage.storeImage("/path/to/component", {123}, QImage{}, midSizeImage1, QImage{}); + + ASSERT_THAT(storage.fetchMidSizeImage("/path/to/component", {123}), Optional(midSizeImage1)); +} + +TEST_F(ImageCacheStorageSlowTest, StoreEmptyMidSizeImageAfterEntry) +{ + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); + + storage.storeImage("/path/to/component", {123}, image1, QImage{}, smallImage1); + + ASSERT_THAT(storage.fetchMidSizeImage("/path/to/component", {123}), Optional(QImage{})); +} + +TEST_F(ImageCacheStorageSlowTest, StoreSmallImage) +{ + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, smallImage1); + + ASSERT_THAT(storage.fetchSmallImage("/path/to/component", {123}), Optional(smallImage1)); +} + +TEST_F(ImageCacheStorageSlowTest, StoreEmptySmallImageAfterEntry) +{ + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); + + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, QImage{}); + + ASSERT_THAT(storage.fetchSmallImage("/path/to/component", {123}), Optional(QImage{})); +} + TEST_F(ImageCacheStorageSlowTest, StoreEmptyEntry) { - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{}); ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), Optional(QImage{})); } @@ -289,7 +436,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNonExistingImageIsEmpty) TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchImage("/path/to/component", {123}); @@ -298,7 +445,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage) TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchImage("/path/to/component", {124}); @@ -307,13 +454,47 @@ TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage) TEST_F(ImageCacheStorageSlowTest, FetchNewerImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchImage("/path/to/component", {122}); ASSERT_THAT(image, Optional(image1)); } +TEST_F(ImageCacheStorageSlowTest, FetchNonExistingMidSizeImageIsEmpty) +{ + auto image = storage.fetchMidSizeImage("/path/to/component", {123}); + + ASSERT_THAT(image, Eq(std::nullopt)); +} + +TEST_F(ImageCacheStorageSlowTest, FetchSameTimeMidSizeImage) +{ + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); + + auto image = storage.fetchMidSizeImage("/path/to/component", {123}); + + ASSERT_THAT(image, Optional(midSizeImage1)); +} + +TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderMidSizeImage) +{ + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); + + auto image = storage.fetchMidSizeImage("/path/to/component", {124}); + + ASSERT_THAT(image, Eq(std::nullopt)); +} + +TEST_F(ImageCacheStorageSlowTest, FetchNewerMidSizeImage) +{ + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); + + auto image = storage.fetchMidSizeImage("/path/to/component", {122}); + + ASSERT_THAT(image, Optional(midSizeImage1)); +} + TEST_F(ImageCacheStorageSlowTest, FetchNonExistingSmallImageIsEmpty) { auto image = storage.fetchSmallImage("/path/to/component", {123}); @@ -323,7 +504,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNonExistingSmallImageIsEmpty) TEST_F(ImageCacheStorageSlowTest, FetchSameTimeSmallImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchSmallImage("/path/to/component", {123}); @@ -332,7 +513,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchSameTimeSmallImage) TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderSmallImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchSmallImage("/path/to/component", {124}); @@ -341,7 +522,7 @@ TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderSmallImage) TEST_F(ImageCacheStorageSlowTest, FetchNewerSmallImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto image = storage.fetchSmallImage("/path/to/component", {122}); @@ -407,7 +588,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchNewerIcon) TEST_F(ImageCacheStorageSlowTest, FetchModifiedImageTime) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto timeStamp = storage.fetchModifiedImageTime("/path/to/component"); @@ -416,7 +597,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchModifiedImageTime) TEST_F(ImageCacheStorageSlowTest, FetchInvalidModifiedImageTimeForNoEntry) { - storage.storeImage("/path/to/component2", {123}, image1, smallImage1); + storage.storeImage("/path/to/component2", {123}, image1, midSizeImage1, smallImage1); auto timeStamp = storage.fetchModifiedImageTime("/path/to/component"); @@ -425,7 +606,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchInvalidModifiedImageTimeForNoEntry) TEST_F(ImageCacheStorageSlowTest, FetchHasImage) { - storage.storeImage("/path/to/component", {123}, image1, smallImage1); + storage.storeImage("/path/to/component", {123}, image1, midSizeImage1, smallImage1); auto hasImage = storage.fetchHasImage("/path/to/component"); @@ -434,7 +615,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchHasImage) TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNullImage) { - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{}); auto hasImage = storage.fetchHasImage("/path/to/component"); @@ -443,7 +624,7 @@ TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNullImage) TEST_F(ImageCacheStorageSlowTest, FetchHasImageForNoEntry) { - storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}); + storage.storeImage("/path/to/component", {123}, QImage{}, QImage{}, QImage{}); auto hasImage = storage.fetchHasImage("/path/to/component"); diff --git a/tests/unit/unittest/mockimagecachegenerator.h b/tests/unit/unittest/mockimagecachegenerator.h index c5a09423ca4..4b3bc7d62ee 100644 --- a/tests/unit/unittest/mockimagecachegenerator.h +++ b/tests/unit/unittest/mockimagecachegenerator.h @@ -15,7 +15,7 @@ public: (Utils::SmallStringView name, Utils::SmallStringView state, Sqlite::TimeStamp timeStamp, - QmlDesigner::ImageCache::CaptureImageWithSmallImageCallback &&captureCallback, + QmlDesigner::ImageCache::CaptureImageWithScaledImagesCallback &&captureCallback, QmlDesigner::ImageCache::AbortCallback &&abortCallback, QmlDesigner::ImageCache::AuxiliaryData &&auxiliaryData), (override)); diff --git a/tests/unit/unittest/mockimagecachestorage.h b/tests/unit/unittest/mockimagecachestorage.h index 710109e1ead..88bd6b789db 100644 --- a/tests/unit/unittest/mockimagecachestorage.h +++ b/tests/unit/unittest/mockimagecachestorage.h @@ -15,6 +15,11 @@ public: (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp), (const, override)); + MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry, + fetchMidSizeImage, + (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp), + (const, override)); + MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::ImageEntry, fetchSmallImage, (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp), @@ -30,6 +35,7 @@ public: (Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image, + const QImage &midSizeImage, const QImage &smallImage), (override)); diff --git a/tests/unit/unittest/sqlitedatabasemock.h b/tests/unit/unittest/sqlitedatabasemock.h index 9eaee4d3570..b892815a1ee 100644 --- a/tests/unit/unittest/sqlitedatabasemock.h +++ b/tests/unit/unittest/sqlitedatabasemock.h @@ -32,7 +32,10 @@ public: MOCK_METHOD(int64_t, lastInsertedRowId, (), (const)); - MOCK_METHOD(void, setLastInsertedRowId, (int64_t), (const)); + MOCK_METHOD(void, setLastInsertedRowId, (int64_t), ()); + + MOCK_METHOD(int, version, (), (const)); + MOCK_METHOD(void, setVersion, (int), ()); MOCK_METHOD(bool, isInitialized, (), (const)); diff --git a/tests/unit/unittest/sqlitewritestatementmock.h b/tests/unit/unittest/sqlitewritestatementmock.h index ac912d63191..2000e9c14bc 100644 --- a/tests/unit/unittest/sqlitewritestatementmock.h +++ b/tests/unit/unittest/sqlitewritestatementmock.h @@ -38,6 +38,10 @@ public: 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::BlobView, Sqlite::BlobView, Sqlite::BlobView), + ()); MOCK_METHOD(void, write, (Utils::SmallStringView, diff --git a/tests/unit/unittest/synchronousimagecache-test.cpp b/tests/unit/unittest/synchronousimagecache-test.cpp index e7fe5feeb10..cc0681baade 100644 --- a/tests/unit/unittest/synchronousimagecache-test.cpp +++ b/tests/unit/unittest/synchronousimagecache-test.cpp @@ -29,8 +29,14 @@ protected: ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123}))) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image2})); + ON_CALL(mockStorage, + fetchMidSizeImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}))) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage1})); ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}))) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1})); + ON_CALL(mockStorage, + fetchMidSizeImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123}))) + .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{midSizeImage2})); ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123}))) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage2})); @@ -40,7 +46,7 @@ protected: fetchIcon(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{123}))) .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::IconEntry{icon2})); ON_CALL(mockCollector, createImage(Eq("/path/to/Component.qml"), Eq("extraId1"), _)) - .WillByDefault(Return(std::make_pair(image3, smallImage3))); + .WillByDefault(Return(std::make_tuple(image3, midSizeImage3, smallImage3))); ON_CALL(mockCollector, createIcon(Eq("/path/to/Component.qml"), Eq("extraId1"), _)) .WillByDefault(Return(icon3)); } @@ -52,10 +58,13 @@ protected: NiceMock 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 image2{1, 2, QImage::Format_ARGB32}; + QImage image3{1, 3, QImage::Format_ARGB32}; + QImage midSizeImage1{2, 1, QImage::Format_ARGB32}; + QImage midSizeImage2{2, 2, QImage::Format_ARGB32}; + QImage midSizeImage3{2, 3, QImage::Format_ARGB32}; + QImage smallImage1{3, 1, QImage::Format_ARGB32}; + QImage smallImage2{3, 1, QImage::Format_ARGB32}; QImage smallImage3{3, 1, QImage::Format_ARGB32}; QIcon icon1{QPixmap::fromImage(image1)}; QIcon icon2{QPixmap::fromImage(image2)}; @@ -95,11 +104,51 @@ TEST_F(SynchronousImageCache, GetImageWithOutdatedTimeStampStored) storeImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{124}), Eq(image3), + Eq(midSizeImage3), Eq(smallImage3))); auto image = cache.image("/path/to/Component.qml", "extraId1"); } +TEST_F(SynchronousImageCache, GetMidSizeImageFromStorage) +{ + auto image = cache.midSizeImage("/path/to/Component.qml"); + + ASSERT_THAT(image, midSizeImage1); +} + +TEST_F(SynchronousImageCache, GetMidSizeImageWithExtraIdFromStorage) +{ + auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1"); + + ASSERT_THAT(image, midSizeImage2); +} + +TEST_F(SynchronousImageCache, GetMidSizeImageWithOutdatedTimeStampFromCollector) +{ + ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml"))) + .WillByDefault(Return(Sqlite::TimeStamp{124})); + + auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1"); + + ASSERT_THAT(image, midSizeImage3); +} + +TEST_F(SynchronousImageCache, GetMidSizeImageWithOutdatedTimeStampStored) +{ + 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(midSizeImage3), + Eq(smallImage3))); + + auto image = cache.midSizeImage("/path/to/Component.qml", "extraId1"); +} + TEST_F(SynchronousImageCache, GetSmallImageFromStorage) { auto image = cache.smallImage("/path/to/Component.qml"); @@ -133,6 +182,7 @@ TEST_F(SynchronousImageCache, GetSmallImageWithOutdatedTimeStampStored) storeImage(Eq("/path/to/Component.qml+extraId1"), Eq(Sqlite::TimeStamp{124}), Eq(image3), + Eq(midSizeImage3), Eq(smallImage3))); auto image = cache.smallImage("/path/to/Component.qml", "extraId1");