diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2018-05-16 17:38:37 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-05-17 12:39:51 +0000 |
commit | 38dc69ae01d2ba733fb35f7c8d26500062858798 (patch) | |
tree | 523ed25bb2fc513f675da97679d8d50b5d24a91e /tests/auto/quick/qquickimageprovider | |
parent | 7cb6dce1f3e140ea68d6b05281950f212fc99d38 (diff) |
Fix race condition in async image response handling
It may happen that the finished() signal is emitted from a super quick
image loading thread in the implementation before we had a chance to
connect to the signal in Qt Quick. In that case, record the emission
through a private connection established in the constructor.
The unit test simulates this scenario by emitting the signal before
returning the object, but in a real world scenario it may happen
that the response object is used in a separate thread that ends up
emitting the signal. That may also happen before the thread that will
try to connect to the signal runs.
Task-number: QTBUG-59601
Change-Id: I14de9719db47f4bca2e8a2c6659343cfb4deb526
Reviewed-by: Albert Astals Cid <albert.astals.cid@kdab.com>
Diffstat (limited to 'tests/auto/quick/qquickimageprovider')
-rw-r--r-- | tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp index d8464ebc74..4b75a7e008 100644 --- a/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp +++ b/tests/auto/quick/qquickimageprovider/tst_qquickimageprovider.cpp @@ -65,6 +65,7 @@ private slots: void threadTest(); void asyncTextureTest(); + void instantAsyncTextureTest(); private: QString newImageFileName() const; @@ -550,6 +551,70 @@ void tst_qquickimageprovider::asyncTextureTest() } } +class InstantAsyncImageResponse : public QQuickImageResponse +{ + public: + InstantAsyncImageResponse(const QString &id, const QSize &requestedSize) + { + QImage image(50, 50, QImage::Format_RGB32); + image.fill(QColor(id).rgb()); + if (requestedSize.isValid()) + image = image.scaled(requestedSize); + m_texture = QQuickTextureFactory::textureFactoryForImage(image); + emit finished(); + } + + QQuickTextureFactory *textureFactory() const + { + return m_texture; + } + + QQuickTextureFactory *m_texture; +}; + +class InstancAsyncProvider : public QQuickAsyncImageProvider +{ + public: + InstancAsyncProvider() + { + } + + ~InstancAsyncProvider() {} + + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) + { + return new InstantAsyncImageResponse(id, requestedSize); + } +}; + +void tst_qquickimageprovider::instantAsyncTextureTest() +{ + QQmlEngine engine; + + InstancAsyncProvider *provider = new InstancAsyncProvider; + + engine.addImageProvider("test_instantasync", provider); + QVERIFY(engine.imageProvider("test_instantasync") != nullptr); + + QString componentStr = "import QtQuick 2.0\nItem { \n" + "Image { source: \"image://test_instantasync/blue\"; }\n" + "Image { source: \"image://test_instantasync/red\"; }\n" + "Image { source: \"image://test_instantasync/green\"; }\n" + "Image { source: \"image://test_instantasync/yellow\"; }\n" + " }"; + QQmlComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QScopedPointer<QObject> obj(component.create()); + + QVERIFY(!obj.isNull()); + const QList<QQuickImage *> images = obj->findChildren<QQuickImage *>(); + QCOMPARE(images.count(), 4); + + for (QQuickImage *img: images) { + QTRY_COMPARE(img->status(), QQuickImage::Ready); + } +} + QTEST_MAIN(tst_qquickimageprovider) |