forked from qt-creator/qt-creator
		
	QmlDesigner: Add AsynchronousExplicitImageCache
Task-number: QDS-5859 Change-Id: I962be68f22ba0c0118c14149a0d0ecf395934db5 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
		| @@ -0,0 +1,173 @@ | ||||
| /**************************************************************************** | ||||
| ** | ||||
| ** Copyright (C) 2020 The Qt Company Ltd. | ||||
| ** Contact: https://www.qt.io/licensing/ | ||||
| ** | ||||
| ** This file is part of Qt Creator. | ||||
| ** | ||||
| ** Commercial License Usage | ||||
| ** Licensees holding valid commercial Qt licenses may use this file in | ||||
| ** accordance with the commercial license agreement provided with the | ||||
| ** Software or, alternatively, in accordance with the terms contained in | ||||
| ** a written agreement between you and The Qt Company. For licensing terms | ||||
| ** and conditions see https://www.qt.io/terms-conditions. For further | ||||
| ** information use the contact form at https://www.qt.io/contact-us. | ||||
| ** | ||||
| ** GNU General Public License Usage | ||||
| ** Alternatively, this file may be used under the terms of the GNU | ||||
| ** General Public License version 3 as published by the Free Software | ||||
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | ||||
| ** included in the packaging of this file. Please review the following | ||||
| ** information to ensure the GNU General Public License requirements will | ||||
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | ||||
| ** | ||||
| ****************************************************************************/ | ||||
|  | ||||
| #include "asynchronousexplicitimagecache.h" | ||||
|  | ||||
| #include "imagecachestorage.h" | ||||
|  | ||||
| #include <thread> | ||||
|  | ||||
| namespace QmlDesigner { | ||||
|  | ||||
| AsynchronousExplicitImageCache::AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage) | ||||
|     : m_storage(storage) | ||||
| { | ||||
|     m_backgroundThread = std::thread{[this] { | ||||
|         while (isRunning()) { | ||||
|             if (auto [hasEntry, entry] = getEntry(); hasEntry) { | ||||
|                 request(entry.name, | ||||
|                         entry.extraId, | ||||
|                         entry.requestType, | ||||
|                         std::move(entry.captureCallback), | ||||
|                         std::move(entry.abortCallback), | ||||
|                         m_storage); | ||||
|             } | ||||
|  | ||||
|             waitForEntries(); | ||||
|         } | ||||
|     }}; | ||||
| } | ||||
|  | ||||
| AsynchronousExplicitImageCache::~AsynchronousExplicitImageCache() | ||||
| { | ||||
|     clean(); | ||||
|     wait(); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::request(Utils::SmallStringView name, | ||||
|                                              Utils::SmallStringView extraId, | ||||
|                                              AsynchronousExplicitImageCache::RequestType requestType, | ||||
|                                              ImageCache::CaptureImageCallback captureCallback, | ||||
|                                              ImageCache::AbortCallback abortCallback, | ||||
|                                              ImageCacheStorageInterface &storage) | ||||
| { | ||||
|     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{}); | ||||
|  | ||||
|     if (entry.hasEntry && !entry.image.isNull()) | ||||
|         captureCallback(entry.image); | ||||
|     else | ||||
|         abortCallback(ImageCache::AbortReason::Failed); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::wait() | ||||
| { | ||||
|     stopThread(); | ||||
|     m_condition.notify_all(); | ||||
|     if (m_backgroundThread.joinable()) | ||||
|         m_backgroundThread.join(); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::requestImage(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::Image); | ||||
|     m_condition.notify_all(); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::requestSmallImage(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::SmallImage); | ||||
|     m_condition.notify_all(); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::clean() | ||||
| { | ||||
|     clearEntries(); | ||||
| } | ||||
|  | ||||
| std::tuple<bool, AsynchronousExplicitImageCache::RequestEntry> AsynchronousExplicitImageCache::getEntry() | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|  | ||||
|     if (m_requestEntries.empty()) | ||||
|         return {false, RequestEntry{}}; | ||||
|  | ||||
|     RequestEntry entry = m_requestEntries.front(); | ||||
|     m_requestEntries.pop_front(); | ||||
|  | ||||
|     return {true, entry}; | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::addEntry(Utils::PathString &&name, | ||||
|                                               Utils::SmallString &&extraId, | ||||
|                                               ImageCache::CaptureImageCallback &&captureCallback, | ||||
|                                               ImageCache::AbortCallback &&abortCallback, | ||||
|                                               RequestType requestType) | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|  | ||||
|     m_requestEntries.emplace_back(std::move(name), | ||||
|                                   std::move(extraId), | ||||
|                                   std::move(captureCallback), | ||||
|                                   std::move(abortCallback), | ||||
|                                   requestType); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::clearEntries() | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|     for (RequestEntry &entry : m_requestEntries) | ||||
|         entry.abortCallback(ImageCache::AbortReason::Abort); | ||||
|     m_requestEntries.clear(); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::waitForEntries() | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|     if (m_requestEntries.empty()) | ||||
|         m_condition.wait(lock, [&] { return m_requestEntries.size() || m_finishing; }); | ||||
| } | ||||
|  | ||||
| void AsynchronousExplicitImageCache::stopThread() | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|     m_finishing = true; | ||||
| } | ||||
|  | ||||
| bool AsynchronousExplicitImageCache::isRunning() | ||||
| { | ||||
|     std::unique_lock lock{m_mutex}; | ||||
|     return !m_finishing || m_requestEntries.size(); | ||||
| } | ||||
|  | ||||
| } // namespace QmlDesigner | ||||
| @@ -0,0 +1,111 @@ | ||||
| /**************************************************************************** | ||||
| ** | ||||
| ** Copyright (C) 2020 The Qt Company Ltd. | ||||
| ** Contact: https://www.qt.io/licensing/ | ||||
| ** | ||||
| ** This file is part of Qt Creator. | ||||
| ** | ||||
| ** Commercial License Usage | ||||
| ** Licensees holding valid commercial Qt licenses may use this file in | ||||
| ** accordance with the commercial license agreement provided with the | ||||
| ** Software or, alternatively, in accordance with the terms contained in | ||||
| ** a written agreement between you and The Qt Company. For licensing terms | ||||
| ** and conditions see https://www.qt.io/terms-conditions. For further | ||||
| ** information use the contact form at https://www.qt.io/contact-us. | ||||
| ** | ||||
| ** GNU General Public License Usage | ||||
| ** Alternatively, this file may be used under the terms of the GNU | ||||
| ** General Public License version 3 as published by the Free Software | ||||
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | ||||
| ** included in the packaging of this file. Please review the following | ||||
| ** information to ensure the GNU General Public License requirements will | ||||
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | ||||
| ** | ||||
| ****************************************************************************/ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "asynchronousimagecacheinterface.h" | ||||
|  | ||||
| #include <condition_variable> | ||||
| #include <deque> | ||||
| #include <functional> | ||||
| #include <mutex> | ||||
| #include <thread> | ||||
|  | ||||
| namespace QmlDesigner { | ||||
|  | ||||
| class ImageCacheStorageInterface; | ||||
|  | ||||
| class AsynchronousExplicitImageCache | ||||
| { | ||||
| public: | ||||
|     ~AsynchronousExplicitImageCache(); | ||||
|  | ||||
|     AsynchronousExplicitImageCache(ImageCacheStorageInterface &storage); | ||||
|  | ||||
|     void requestImage(Utils::PathString name, | ||||
|                       ImageCache::CaptureImageCallback captureCallback, | ||||
|                       ImageCache::AbortCallback abortCallback, | ||||
|                       Utils::SmallString extraId = {}); | ||||
|     void requestSmallImage(Utils::PathString name, | ||||
|                            ImageCache::CaptureImageCallback captureCallback, | ||||
|                            ImageCache::AbortCallback abortCallback, | ||||
|                            Utils::SmallString extraId = {}); | ||||
|  | ||||
|     void clean(); | ||||
|  | ||||
| private: | ||||
|     enum class RequestType { Image, SmallImage, Icon }; | ||||
|     struct RequestEntry | ||||
|     { | ||||
|         RequestEntry() = default; | ||||
|         RequestEntry(Utils::PathString name, | ||||
|                      Utils::SmallString extraId, | ||||
|                      ImageCache::CaptureImageCallback &&captureCallback, | ||||
|                      ImageCache::AbortCallback &&abortCallback, | ||||
|                      RequestType requestType) | ||||
|             : name{std::move(name)} | ||||
|             , extraId{std::move(extraId)} | ||||
|             , captureCallback{std::move(captureCallback)} | ||||
|             , abortCallback{std::move(abortCallback)} | ||||
|             , requestType{requestType} | ||||
|         {} | ||||
|  | ||||
|         Utils::PathString name; | ||||
|         Utils::SmallString extraId; | ||||
|         ImageCache::CaptureImageCallback captureCallback; | ||||
|         ImageCache::AbortCallback abortCallback; | ||||
|         RequestType requestType = RequestType::Image; | ||||
|     }; | ||||
|  | ||||
|     std::tuple<bool, RequestEntry> getEntry(); | ||||
|     void addEntry(Utils::PathString &&name, | ||||
|                   Utils::SmallString &&extraId, | ||||
|                   ImageCache::CaptureImageCallback &&captureCallback, | ||||
|                   ImageCache::AbortCallback &&abortCallback, | ||||
|                   RequestType requestType); | ||||
|     void clearEntries(); | ||||
|     void waitForEntries(); | ||||
|     void stopThread(); | ||||
|     bool isRunning(); | ||||
|     static void request(Utils::SmallStringView name, | ||||
|                         Utils::SmallStringView extraId, | ||||
|                         AsynchronousExplicitImageCache::RequestType requestType, | ||||
|                         ImageCache::CaptureImageCallback captureCallback, | ||||
|                         ImageCache::AbortCallback abortCallback, | ||||
|                         ImageCacheStorageInterface &storage); | ||||
|  | ||||
| private: | ||||
|     void wait(); | ||||
|  | ||||
| private: | ||||
|     std::deque<RequestEntry> m_requestEntries; | ||||
|     mutable std::mutex m_mutex; | ||||
|     std::condition_variable m_condition; | ||||
|     std::thread m_backgroundThread; | ||||
|     ImageCacheStorageInterface &m_storage; | ||||
|     bool m_finishing{false}; | ||||
| }; | ||||
|  | ||||
| } // namespace QmlDesigner | ||||
| @@ -43,7 +43,6 @@ class ImageCacheCollectorInterface; | ||||
| class AsynchronousImageCache final : public AsynchronousImageCacheInterface | ||||
| { | ||||
| public: | ||||
|  | ||||
|     ~AsynchronousImageCache(); | ||||
|  | ||||
|     AsynchronousImageCache(ImageCacheStorageInterface &storage, | ||||
| @@ -62,7 +61,6 @@ public: | ||||
|                            ImageCache::AuxiliaryData auxiliaryData = {}) override; | ||||
|  | ||||
|     void clean(); | ||||
|     //   void waitForFinished(); | ||||
|  | ||||
| private: | ||||
|     enum class RequestType { Image, SmallImage, Icon }; | ||||
|   | ||||
| @@ -107,6 +107,7 @@ function(extend_with_qmldesigner_core target_name) | ||||
|       filemanager/removeuiobjectmembervisitor.cpp | ||||
|       filemanager/removeuiobjectmembervisitor.h | ||||
|  | ||||
|       imagecache/asynchronousexplicitimagecache.cpp | ||||
|       imagecache/asynchronousimagecache.cpp | ||||
|       imagecache/imagecachecollector.cpp | ||||
|       imagecache/imagecachecollector.h | ||||
| @@ -129,6 +130,7 @@ function(extend_with_qmldesigner_core target_name) | ||||
|       include/abstractview.h | ||||
|       include/anchorline.h | ||||
|       include/annotation.h | ||||
|       include/asynchronousexplicitimagecache.h | ||||
|       include/asynchronousimagecache.h | ||||
|       include/basetexteditmodifier.h | ||||
|       include/bindingproperty.h | ||||
|   | ||||
| @@ -114,6 +114,7 @@ add_qtc_test(unittest GTEST | ||||
|     imagecachecollectormock.h | ||||
|     mockimagecachegenerator.h | ||||
|     mockimagecachestorage.h | ||||
|     asynchronousexplicitimagecache-test.cpp | ||||
| ) | ||||
|  | ||||
| if (NOT TARGET unittest) | ||||
| @@ -281,6 +282,7 @@ extend_qtc_test(unittest | ||||
|     exceptions/notimplementedexception.cpp | ||||
|     exceptions/removebasestateexception.cpp | ||||
|     exceptions/rewritingexception.cpp | ||||
|     imagecache/asynchronousexplicitimagecache.cpp | ||||
|     imagecache/asynchronousimagecache.cpp | ||||
|     imagecache/imagecachecollectorinterface.h | ||||
|     imagecache/imagecachegenerator.cpp | ||||
| @@ -292,6 +294,7 @@ extend_qtc_test(unittest | ||||
|     imagecache/timestampproviderinterface.h | ||||
|     include/abstractproperty.h | ||||
|     include/abstractview.h | ||||
|     include/asynchronousexplicitimagecache.h | ||||
|     include/asynchronousimagecache.h | ||||
|     include/asynchronousimagecacheinterface.h | ||||
|     include/bindingproperty.h | ||||
|   | ||||
							
								
								
									
										265
									
								
								tests/unit/unittest/asynchronousexplicitimagecache-test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								tests/unit/unittest/asynchronousexplicitimagecache-test.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | ||||
| /**************************************************************************** | ||||
| ** | ||||
| ** Copyright (C) 2016 The Qt Company Ltd. | ||||
| ** Contact: https://www.qt.io/licensing/ | ||||
| ** | ||||
| ** This file is part of Qt Creator. | ||||
| ** | ||||
| ** Commercial License Usage | ||||
| ** Licensees holding valid commercial Qt licenses may use this file in | ||||
| ** accordance with the commercial license agreement provided with the | ||||
| ** Software or, alternatively, in accordance with the terms contained in | ||||
| ** a written agreement between you and The Qt Company. For licensing terms | ||||
| ** and conditions see https://www.qt.io/terms-conditions. For further | ||||
| ** information use the contact form at https://www.qt.io/contact-us. | ||||
| ** | ||||
| ** GNU General Public License Usage | ||||
| ** Alternatively, this file may be used under the terms of the GNU | ||||
| ** General Public License version 3 as published by the Free Software | ||||
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | ||||
| ** included in the packaging of this file. Please review the following | ||||
| ** information to ensure the GNU General Public License requirements will | ||||
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | ||||
| ** | ||||
| ****************************************************************************/ | ||||
|  | ||||
| #include "googletest.h" | ||||
|  | ||||
| #include "mockimagecachegenerator.h" | ||||
| #include "mockimagecachestorage.h" | ||||
| #include "mocktimestampprovider.h" | ||||
| #include "notification.h" | ||||
|  | ||||
| #include <asynchronousexplicitimagecache.h> | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| class AsynchronousExplicitImageCache : public testing::Test | ||||
| { | ||||
| protected: | ||||
|     Notification notification; | ||||
|     Notification waitInThread; | ||||
|     NiceMock<MockFunction<void(QmlDesigner::ImageCache::AbortReason)>> mockAbortCallback; | ||||
|     NiceMock<MockFunction<void(QmlDesigner::ImageCache::AbortReason)>> mockAbortCallback2; | ||||
|     NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback; | ||||
|     NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback2; | ||||
|     NiceMock<MockImageCacheStorage> mockStorage; | ||||
|     QmlDesigner::AsynchronousExplicitImageCache cache{mockStorage}; | ||||
|     QImage image1{10, 10, QImage::Format_ARGB32}; | ||||
|     QImage smallImage1{1, 1, QImage::Format_ARGB32}; | ||||
| }; | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageFetchesImageFromStorage) | ||||
| { | ||||
|     EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{}))) | ||||
|         .WillRepeatedly([&](Utils::SmallStringView, auto) { | ||||
|             notification.notify(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false}; | ||||
|         }); | ||||
|  | ||||
|     cache.requestImage("/path/to/Component.qml", | ||||
|                        mockCaptureCallback.AsStdFunction(), | ||||
|                        mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageFetchesImageFromStorageWithTimeStamp) | ||||
| { | ||||
|     EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{}))) | ||||
|         .WillRepeatedly([&](Utils::SmallStringView, auto) { | ||||
|             notification.notify(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, false}; | ||||
|         }); | ||||
|  | ||||
|     cache.requestImage("/path/to/Component.qml", | ||||
|                        mockCaptureCallback.AsStdFunction(), | ||||
|                        mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageCallsCaptureCallbackWithImageFromStorage) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image1, true})); | ||||
|  | ||||
|     EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) { | ||||
|         notification.notify(); | ||||
|     }); | ||||
|  | ||||
|     cache.requestImage("/path/to/Component.qml", | ||||
|                        mockCaptureCallback.AsStdFunction(), | ||||
|                        mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutEntry) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{image1, false})); | ||||
|  | ||||
|     EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) | ||||
|         .WillRepeatedly([&](auto) { notification.notify(); }); | ||||
|  | ||||
|     cache.requestImage("/path/to/Component.qml", | ||||
|                        mockCaptureCallback.AsStdFunction(), | ||||
|                        mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageCallsAbortCallbackWithoutImage) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, true})); | ||||
|  | ||||
|     EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) | ||||
|         .WillRepeatedly([&](auto) { notification.notify(); }); | ||||
|  | ||||
|     cache.requestImage("/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{}))) | ||||
|         .WillRepeatedly([&](Utils::SmallStringView, auto) { | ||||
|             notification.notify(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false}; | ||||
|         }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestSmallImageCallsCaptureCallbackWithImageFromStorage) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true})); | ||||
|  | ||||
|     EXPECT_CALL(mockCaptureCallback, Call(Eq(smallImage1))).WillRepeatedly([&](const QImage &) { | ||||
|         notification.notify(); | ||||
|     }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestSmallImageCallsAbortCallbackWithoutEntry) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, false})); | ||||
|  | ||||
|     EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) | ||||
|         .WillRepeatedly([&](auto) { notification.notify(); }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestSmallImageCallsAbortCallbackWithoutSmallImage) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), _)) | ||||
|         .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::ImageEntry{QImage{}, true})); | ||||
|  | ||||
|     EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Failed))) | ||||
|         .WillRepeatedly([&](auto) { notification.notify(); }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, CleanRemovesEntries) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchSmallImage(_, _)).WillByDefault([&](Utils::SmallStringView, auto) { | ||||
|         return QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true}; | ||||
|     }); | ||||
|     ON_CALL(mockCaptureCallback2, Call(_)).WillByDefault([&](auto) { waitInThread.wait(); }); | ||||
|     cache.requestSmallImage("/path/to/Component1.qml", | ||||
|                             mockCaptureCallback2.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|  | ||||
|     EXPECT_CALL(mockCaptureCallback, Call(_)).Times(0); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component3.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     cache.clean(); | ||||
|     waitInThread.notify(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, CleanCallsAbort) | ||||
| { | ||||
|     ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component1.qml"), _)) | ||||
|         .WillByDefault([&](Utils::SmallStringView, auto) { | ||||
|             waitInThread.wait(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true}; | ||||
|         }); | ||||
|     cache.requestSmallImage("/path/to/Component1.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback2.AsStdFunction()); | ||||
|     cache.requestSmallImage("/path/to/Component2.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|  | ||||
|     EXPECT_CALL(mockAbortCallback, Call(Eq(QmlDesigner::ImageCache::AbortReason::Abort))); | ||||
|  | ||||
|     cache.clean(); | ||||
|     waitInThread.notify(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, AfterCleanNewJobsWorks) | ||||
| { | ||||
|     cache.clean(); | ||||
|  | ||||
|     ON_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{}))) | ||||
|         .WillByDefault([&](Utils::SmallStringView, auto) { | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{smallImage1, true}; | ||||
|         }); | ||||
|     ON_CALL(mockCaptureCallback, Call(_)).WillByDefault([&](auto) { notification.notify(); }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction()); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestImageWithExtraIdFetchesImageFromStorage) | ||||
| { | ||||
|     EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml+extraId1"), _)) | ||||
|         .WillRepeatedly([&](Utils::SmallStringView, auto) { | ||||
|             notification.notify(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false}; | ||||
|         }); | ||||
|  | ||||
|     cache.requestImage("/path/to/Component.qml", | ||||
|                        mockCaptureCallback.AsStdFunction(), | ||||
|                        mockAbortCallback.AsStdFunction(), | ||||
|                        "extraId1"); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| TEST_F(AsynchronousExplicitImageCache, RequestSmallImageWithExtraIdFetchesImageFromStorage) | ||||
| { | ||||
|     EXPECT_CALL(mockStorage, fetchSmallImage(Eq("/path/to/Component.qml+extraId1"), _)) | ||||
|         .WillRepeatedly([&](Utils::SmallStringView, auto) { | ||||
|             notification.notify(); | ||||
|             return QmlDesigner::ImageCacheStorageInterface::ImageEntry{{}, false}; | ||||
|         }); | ||||
|  | ||||
|     cache.requestSmallImage("/path/to/Component.qml", | ||||
|                             mockCaptureCallback.AsStdFunction(), | ||||
|                             mockAbortCallback.AsStdFunction(), | ||||
|                             "extraId1"); | ||||
|     notification.wait(); | ||||
| } | ||||
|  | ||||
| } // namespace | ||||
		Reference in New Issue
	
	Block a user