Merge lp:~zsombi/ubuntu-ui-toolkit/asyncLoader into lp:ubuntu-ui-toolkit/staging

Proposed by Zsombor Egri
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1872
Merged at revision: 1876
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/asyncLoader
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 910 lines (+778/-4)
18 files modified
debian/control (+10/-0)
debian/libubuntutoolkit5-dev.install (+2/-0)
debian/libubuntutoolkit5-private-dev.install (+1/-0)
src/Ubuntu/Test/plugin/uctestcase.cpp (+13/-0)
src/Ubuntu/Test/plugin/uctestcase.h (+1/-0)
src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro (+5/-2)
src/Ubuntu/UbuntuToolkit/asyncloader.cpp (+222/-0)
src/Ubuntu/UbuntuToolkit/asyncloader.h (+63/-0)
src/Ubuntu/UbuntuToolkit/asyncloader_p.h (+54/-0)
tests/unit/plugin_dependency.pri (+1/-0)
tests/unit/test-include.pri (+1/-1)
tests/unit_x11/tst_asyncloader/Document.qml (+31/-0)
tests/unit_x11/tst_asyncloader/FaultyDocument.qml (+20/-0)
tests/unit_x11/tst_asyncloader/HeavyDocument.qml (+31/-0)
tests/unit_x11/tst_asyncloader/TestApp.qml (+22/-0)
tests/unit_x11/tst_asyncloader/tst_asyncloader.cpp (+288/-0)
tests/unit_x11/tst_asyncloader/tst_asyncloader.pro (+11/-0)
tests/unit_x11/unit_x11.pro (+2/-1)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/asyncLoader
Reviewer Review Type Date Requested Status
ubuntu-sdk-build-bot continuous-integration Approve
Benjamin Zeller Approve
Review via email: mp+286502@code.launchpad.net

Commit message

Asynchronous loader (AsyncLoader) using incubator, prerequisite for BottomEdge preloading its content.

To post a comment you must log in.
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Benjamin Zeller (zeller-benjamin) wrote :

A few inline comments, but looks good already.

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

Replied back, will provide the fixes next.

Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Benjamin Zeller (zeller-benjamin) wrote :

You need to add the -private-dev package for the new UbuntuToolkit:

=== modified file 'debian/control'
--- debian/control 2016-02-11 16:17:46 +0000
+++ debian/control 2016-02-25 15:50:04 +0000
@@ -139,6 +139,16 @@
  This package contains the development files for
  Ubuntu toolkit common library

+Package: libubuntutoolkit5-private-dev
+Architecture: any
+Multi-Arch: same
+Pre-Depends: dpkg (>= 1.15.6~), ${misc:Pre-Depends}
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
+Description: Ubuntu toolkit common library private development files
+ This package contains the private development files for
+ Ubuntu toolkit common library
+
 Package: ubuntu-ui-toolkit-theme
 Architecture: any
 Multi-Arch: foreign

=== added file 'debian/libubuntutoolkit5-private-dev.install'
--- debian/libubuntutoolkit5-private-dev.install 1970-01-01 00:00:00 +0000
+++ debian/libubuntutoolkit5-private-dev.install 2016-02-25 15:46:46 +0000
@@ -0,0 +1,1 @@
+usr/include/*/qt5/UbuntuToolkit/*/UbuntuToolkit/private/*_p.h

review: Needs Fixing
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Benjamin Zeller (zeller-benjamin) wrote :

Perfect, can go in now!

review: Approve
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Needs Fixing (continuous-integration)
1872. By Zsombor Egri

staging sync

Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2016-02-11 16:17:46 +0000
3+++ debian/control 2016-03-01 10:45:34 +0000
4@@ -139,6 +139,16 @@
5 This package contains the development files for
6 Ubuntu toolkit common library
7
8+Package: libubuntutoolkit5-private-dev
9+Architecture: any
10+Multi-Arch: same
11+Pre-Depends: dpkg (>= 1.15.6~), ${misc:Pre-Depends}
12+Depends: ${misc:Depends},
13+ ${shlibs:Depends},
14+Description: Ubuntu toolkit common library private development files
15+ This package contains the private development files for
16+ Ubuntu toolkit common library
17+
18 Package: ubuntu-ui-toolkit-theme
19 Architecture: any
20 Multi-Arch: foreign
21
22=== modified file 'debian/libubuntutoolkit5-dev.install'
23--- debian/libubuntutoolkit5-dev.install 2016-02-12 08:52:18 +0000
24+++ debian/libubuntutoolkit5-dev.install 2016-03-01 10:45:34 +0000
25@@ -1,3 +1,5 @@
26+usr/include/*/qt5/UbuntuToolkit/AsyncLoader
27+usr/include/*/qt5/UbuntuToolkit/asyncloader.h
28 usr/include/*/qt5/UbuntuToolkit/ColorUtils
29 usr/include/*/qt5/UbuntuToolkit/colorutils.h
30 usr/include/*/qt5/UbuntuToolkit/Tree
31
32=== added file 'debian/libubuntutoolkit5-private-dev.install'
33--- debian/libubuntutoolkit5-private-dev.install 1970-01-01 00:00:00 +0000
34+++ debian/libubuntutoolkit5-private-dev.install 2016-03-01 10:45:34 +0000
35@@ -0,0 +1,1 @@
36+usr/include/*/qt5/UbuntuToolkit/*/UbuntuToolkit/private/*_p.h
37
38=== modified file 'src/Ubuntu/Test/plugin/uctestcase.cpp'
39--- src/Ubuntu/Test/plugin/uctestcase.cpp 2016-01-25 17:12:56 +0000
40+++ src/Ubuntu/Test/plugin/uctestcase.cpp 2016-03-01 10:45:34 +0000
41@@ -76,3 +76,16 @@
42 QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
43 }
44 }
45+
46+/*!
47+ * Same as previous but without column number
48+ */
49+void UbuntuTestCase::ignoreWarning(const QString& fileName, uint line,
50+ const QString& message, uint occurences)
51+{
52+ for (uint i = 0; i < occurences; i++) {
53+ QString url(QUrl::fromLocalFile(QFileInfo(fileName).absoluteFilePath()).toEncoded());
54+ QString warning(QString("%1:%2 %3").arg(url).arg(line).arg(message));
55+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
56+ }
57+}
58
59=== modified file 'src/Ubuntu/Test/plugin/uctestcase.h'
60--- src/Ubuntu/Test/plugin/uctestcase.h 2016-01-25 17:12:56 +0000
61+++ src/Ubuntu/Test/plugin/uctestcase.h 2016-03-01 10:45:34 +0000
62@@ -54,6 +54,7 @@
63 }
64
65 static void ignoreWarning(const QString& fileName, uint line, uint column, const QString& message, uint occurences=1);
66+ static void ignoreWarning(const QString& fileName, uint line, const QString& message, uint occurences=1);
67
68 static inline void waitForSignal(QSignalSpy *spy, int timeout = 5000)
69 {
70
71=== modified file 'src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro'
72--- src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-02-11 17:19:48 +0000
73+++ src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-03-01 10:45:34 +0000
74@@ -15,8 +15,11 @@
75 HEADERS += \
76 colorutils.h \
77 ubuntutoolkitglobal.h \
78- tree.h
79+ tree.h \
80+ asyncloader.h \
81+ asyncloader_p.h
82
83 SOURCES += \
84 colorutils.cpp \
85- tree.cpp
86+ tree.cpp \
87+ asyncloader.cpp
88
89=== added file 'src/Ubuntu/UbuntuToolkit/asyncloader.cpp'
90--- src/Ubuntu/UbuntuToolkit/asyncloader.cpp 1970-01-01 00:00:00 +0000
91+++ src/Ubuntu/UbuntuToolkit/asyncloader.cpp 2016-03-01 10:45:34 +0000
92@@ -0,0 +1,222 @@
93+/*
94+ * Copyright 2016 Canonical Ltd.
95+ *
96+ * This program is free software; you can redistribute it and/or modify
97+ * it under the terms of the GNU Lesser General Public License as published by
98+ * the Free Software Foundation; version 3.
99+ *
100+ * This program is distributed in the hope that it will be useful,
101+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
102+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
103+ * GNU Lesser General Public License for more details.
104+ *
105+ * You should have received a copy of the GNU Lesser General Public License
106+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
107+ *
108+ * Author: Zsombor Egri <zsombor.egri@canonical.com>
109+ */
110+
111+#include "asyncloader_p.h"
112+
113+#include <QtQml/QQmlContext>
114+#include <QtQml/QQmlComponent>
115+#include <QtQuick/QQuickItem>
116+
117+namespace UbuntuToolkit {
118+
119+AsyncLoader::AsyncLoader(QObject *parent)
120+ : QObject(*(new AsyncLoaderPrivate), parent)
121+{
122+}
123+
124+AsyncLoader::~AsyncLoader()
125+{
126+ reset();
127+}
128+
129+// incubator methods
130+void AsyncLoaderPrivate::setInitialState(QObject *object)
131+{
132+ emitStatus(AsyncLoader::Initializing, object);
133+}
134+
135+AsyncLoader::LoadingStatus incubatorToLoadingStatus(QQmlIncubator::Status status)
136+{
137+ switch (status) {
138+ case QQmlIncubator::Null: return AsyncLoader::Null;
139+ case QQmlIncubator::Ready: return AsyncLoader::Ready;
140+ case QQmlIncubator::Loading: return AsyncLoader::Loading;
141+ case QQmlIncubator::Error: return AsyncLoader::Error;
142+ }
143+ // unlikely to be reached, but must satisfy compiler
144+ return AsyncLoader::Null;
145+}
146+
147+void AsyncLoaderPrivate::statusChanged(Status status)
148+{
149+ if (status == QQmlIncubator::Error) {
150+ QList<QQmlError> e = errors();
151+ for (int i = 0; i < e.size(); i++) {
152+ // remove quotes and any leading/trailing whitespace
153+ qWarning().noquote() << e[0].toString().trimmed();
154+ }
155+ }
156+ if (status != QQmlIncubator::Loading) {
157+ detachComponent();
158+ }
159+ // we should emit the status change only after we do the cleanup
160+ emitStatus(incubatorToLoadingStatus(status));
161+}
162+
163+// procected methods
164+void AsyncLoaderPrivate::emitStatus(AsyncLoader::LoadingStatus status, QObject *object)
165+{
166+ if (this->status == status) {
167+ return;
168+ }
169+
170+ this->status = status;
171+ if (!object) {
172+ object = this->object();
173+ }
174+ Q_EMIT q_func()->loadingStatus(this->status, object);
175+}
176+
177+void AsyncLoaderPrivate::detachComponent()
178+{
179+ if (!component) {
180+ return;
181+ }
182+
183+ Q_Q(AsyncLoader);
184+ if (componentHandler) {
185+ QObject::disconnect(*componentHandler);
186+ }
187+ componentHandler.reset();
188+ if (ownComponent) {
189+ component->deleteLater();
190+ }
191+ component = nullptr;
192+ ownComponent = false;
193+}
194+
195+void AsyncLoaderPrivate::onComponentStatusChanged(QQmlComponent::Status status)
196+{
197+ if (status == QQmlComponent::Error) {
198+ QString error = component->errorString();
199+ // remove quotes and any leading/trailing whitespace
200+ qWarning().noquote() << error.trimmed();
201+ detachComponent();
202+ emitStatus(AsyncLoader::Error);
203+ return;
204+ }
205+ if (status == QQmlComponent::Ready) {
206+ component->create(*this, context);
207+ }
208+}
209+
210+/*!
211+ * \brief AsyncLoader::load
212+ * \param url
213+ * \param context
214+ * \return bool
215+ * The method initiates the loading of a given \e url within a specific \e context.
216+ * Returns true on success.
217+ * \note If the loading is initiated while there is a previous loading in place,
218+ * you must make sure you delete the object from the previous loading before you
219+ * trigger the new load.
220+ */
221+bool AsyncLoader::load(const QUrl &url, QQmlContext *context)
222+{
223+ if (!reset() || !context) {
224+ return false;
225+ }
226+ Q_D(AsyncLoader);
227+ if (url.isEmpty() || !url.isValid()) {
228+ d->emitStatus(Ready);
229+ return false;
230+ }
231+ d->ownComponent = true;
232+ return load(new QQmlComponent(context->engine(), url, QQmlComponent::Asynchronous), context);
233+}
234+
235+/*!
236+ * \brief AsyncLoader::load
237+ * \param component
238+ * \param context
239+ * \return bool
240+ * The method initiates the loading of a \e component within the given \e context.
241+ * Returns true on success.
242+ * \note If the loading is initiated while there is a previous loading in place,
243+ * you must make sure you delete the object from the previous loading before you
244+ * trigger the new load.
245+ */
246+bool AsyncLoader::load(QQmlComponent *component, QQmlContext *context)
247+{
248+ if (!reset() || !context) {
249+ return false;
250+ }
251+ Q_D(AsyncLoader);
252+ if (!component) {
253+ d->emitStatus(Ready);
254+ return false;
255+ }
256+ d->component = component;
257+ d->context = context;
258+ if (d->component->isLoading()) {
259+ d->emitStatus(Compiling);
260+ auto callback = [d] (QQmlComponent::Status status) {
261+ d->onComponentStatusChanged(status);
262+ };
263+
264+ d->componentHandler.reset(new QMetaObject::Connection);
265+ *(d->componentHandler) = QObject::connect(d->component, &QQmlComponent::statusChanged, callback);
266+ } else {
267+ d->onComponentStatusChanged(d->component->status());
268+ }
269+ return true;
270+}
271+
272+/*!
273+ * \brief AsyncLoader::reset
274+ * \return bool
275+ * Clears the incubator and emits loadingStatus() signal with \c Reset status.
276+ * The loader can be reset only if the status passed \c Loading. Returns true
277+ * if the reset was successful, or when the loader status is \c Ready or
278+ * \c Error.
279+ */
280+bool AsyncLoader::reset()
281+{
282+ Q_D(AsyncLoader);
283+ if (d->status < Loading) {
284+ return false;
285+ }
286+ if (d->status >= Ready) {
287+ return true;
288+ }
289+ d->clear();
290+ // make sure the listeners are getting the reset so they can delete the object
291+ d->emitStatus(Reset);
292+ return true;
293+}
294+
295+/*!
296+ * \brief AsyncLoader::status
297+ * \return LoadingStatus
298+ * Returns the status of the loader.
299+ */
300+AsyncLoader::LoadingStatus AsyncLoader::status()
301+{
302+ return d_func()->status;
303+}
304+
305+/*!
306+ * \brief AsyncLoader::forceCompletion
307+ * Forces loading completion.
308+ */
309+void AsyncLoader::forceCompletion()
310+{
311+ d_func()->forceCompletion();
312+}
313+
314+} // namespace UbuntuToolkit
315
316=== added file 'src/Ubuntu/UbuntuToolkit/asyncloader.h'
317--- src/Ubuntu/UbuntuToolkit/asyncloader.h 1970-01-01 00:00:00 +0000
318+++ src/Ubuntu/UbuntuToolkit/asyncloader.h 2016-03-01 10:45:34 +0000
319@@ -0,0 +1,63 @@
320+/*
321+ * Copyright 2016 Canonical Ltd.
322+ *
323+ * This program is free software; you can redistribute it and/or modify
324+ * it under the terms of the GNU Lesser General Public License as published by
325+ * the Free Software Foundation; version 3.
326+ *
327+ * This program is distributed in the hope that it will be useful,
328+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
329+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
330+ * GNU Lesser General Public License for more details.
331+ *
332+ * You should have received a copy of the GNU Lesser General Public License
333+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
334+ *
335+ * Author: Zsombor Egri <zsombor.egri@canonical.com>
336+ */
337+
338+#ifndef ASYNCLOADER_H
339+#define ASYNCLOADER_H
340+
341+#include <QtQml/QQmlComponent>
342+#include <ubuntutoolkitglobal.h>
343+
344+class QQuickItem;
345+class QQmlContext;
346+
347+namespace UbuntuToolkit {
348+
349+class AsyncLoaderPrivate;
350+class UBUNTUTOOLKIT_EXPORT AsyncLoader : public QObject
351+{
352+ Q_OBJECT
353+public:
354+ enum LoadingStatus {
355+ Null,
356+ Compiling,
357+ Loading,
358+ Initializing,
359+ Ready,
360+ Error,
361+ Reset
362+ };
363+
364+ explicit AsyncLoader(QObject *parent = 0);
365+ ~AsyncLoader();
366+
367+ bool load(const QUrl &url, QQmlContext *context);
368+ bool load(QQmlComponent *component, QQmlContext *context);
369+ bool reset();
370+ LoadingStatus status();
371+ void forceCompletion();
372+
373+Q_SIGNALS:
374+ void loadingStatus(LoadingStatus status, QObject *object);
375+
376+protected:
377+ Q_DECLARE_PRIVATE(AsyncLoader)
378+};
379+
380+} // namespace UbuntuToolkit
381+
382+#endif // ASYNCLOADER_H
383
384=== added file 'src/Ubuntu/UbuntuToolkit/asyncloader_p.h'
385--- src/Ubuntu/UbuntuToolkit/asyncloader_p.h 1970-01-01 00:00:00 +0000
386+++ src/Ubuntu/UbuntuToolkit/asyncloader_p.h 2016-03-01 10:45:34 +0000
387@@ -0,0 +1,54 @@
388+/*
389+ * Copyright 2016 Canonical Ltd.
390+ *
391+ * This program is free software; you can redistribute it and/or modify
392+ * it under the terms of the GNU Lesser General Public License as published by
393+ * the Free Software Foundation; version 3.
394+ *
395+ * This program is distributed in the hope that it will be useful,
396+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
397+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
398+ * GNU Lesser General Public License for more details.
399+ *
400+ * You should have received a copy of the GNU Lesser General Public License
401+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
402+ *
403+ * Author: Zsombor Egri <zsombor.egri@canonical.com>
404+ */
405+
406+#ifndef ASYNCLOADER_P_H
407+#define ASYNCLOADER_P_H
408+
409+#include <QtCore/private/qobject_p.h>
410+#include <QtQml/QQmlIncubator>
411+#include <AsyncLoader>
412+
413+namespace UbuntuToolkit {
414+
415+class AsyncLoaderPrivate : public QObjectPrivate, public QQmlIncubator
416+{
417+ Q_DECLARE_PUBLIC(AsyncLoader)
418+public:
419+ AsyncLoaderPrivate()
420+ : QObjectPrivate()
421+ , QQmlIncubator(Asynchronous)
422+ {}
423+
424+ QSharedPointer<QMetaObject::Connection> componentHandler;
425+ QQmlComponent *component = nullptr;
426+ QQmlContext *context = nullptr;
427+ AsyncLoader::LoadingStatus status = AsyncLoader::Ready;
428+ bool ownComponent = false;
429+
430+ void setInitialState(QObject *object) override;
431+ void statusChanged(Status status) override;
432+
433+ void emitStatus(AsyncLoader::LoadingStatus status, QObject *object = 0);
434+ void onComponentStatusChanged(QQmlComponent::Status status);
435+ void detachComponent();
436+};
437+
438+} // namespace UbuntuToolkit
439+
440+#endif // ASYNCLOADER_P_H
441+
442
443=== modified file 'tests/unit/plugin_dependency.pri'
444--- tests/unit/plugin_dependency.pri 2015-12-21 12:18:50 +0000
445+++ tests/unit/plugin_dependency.pri 2016-03-01 10:45:34 +0000
446@@ -9,6 +9,7 @@
447 LIBS += -L$$PLUGIN_BLD/Components -lUbuntuComponents
448 LIBS += -L$$PLUGIN_BLD/Test -lUbuntuTest
449 LIBS += -L$${ROOT_BUILD_DIR}/lib -lUbuntuGestures
450+#LIBS += -L$${ROOT_BUILD_DIR}/lib -lUbuntuToolkit
451 DEFINES += QUICK_TEST_SOURCE_DIR=\"\\\"$$_PRO_FILE_PWD_\\\"\"
452 QMAKE_CXXFLAGS += -Werror
453
454
455=== modified file 'tests/unit/test-include.pri'
456--- tests/unit/test-include.pri 2016-02-25 11:56:23 +0000
457+++ tests/unit/test-include.pri 2016-03-01 10:45:34 +0000
458@@ -3,6 +3,6 @@
459
460 TEMPLATE = app
461 QT += testlib qml quick
462-CONFIG += no_keywords C++11
463+CONFIG += no_keywords c++11
464
465
466
467=== added directory 'tests/unit_x11/tst_asyncloader'
468=== added file 'tests/unit_x11/tst_asyncloader/Document.qml'
469--- tests/unit_x11/tst_asyncloader/Document.qml 1970-01-01 00:00:00 +0000
470+++ tests/unit_x11/tst_asyncloader/Document.qml 2016-03-01 10:45:34 +0000
471@@ -0,0 +1,31 @@
472+/*
473+ * Copyright 2016 Canonical Ltd.
474+ *
475+ * This program is free software; you can redistribute it and/or modify
476+ * it under the terms of the GNU Lesser General Public License as published by
477+ * the Free Software Foundation; version 3.
478+ *
479+ * This program is distributed in the hope that it will be useful,
480+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
481+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
482+ * GNU Lesser General Public License for more details.
483+ *
484+ * You should have received a copy of the GNU Lesser General Public License
485+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
486+ *
487+ */
488+
489+import QtQuick 2.4
490+import Ubuntu.Components 1.3
491+
492+Column {
493+ anchors.fill: parent
494+ spacing: 1
495+ Repeater {
496+ model: 500
497+ TextField {
498+ width: parent.width
499+ }
500+ }
501+}
502+
503
504=== added file 'tests/unit_x11/tst_asyncloader/FaultyDocument.qml'
505--- tests/unit_x11/tst_asyncloader/FaultyDocument.qml 1970-01-01 00:00:00 +0000
506+++ tests/unit_x11/tst_asyncloader/FaultyDocument.qml 2016-03-01 10:45:34 +0000
507@@ -0,0 +1,20 @@
508+/*
509+ * Copyright 2016 Canonical Ltd.
510+ *
511+ * This program is free software; you can redistribute it and/or modify
512+ * it under the terms of the GNU Lesser General Public License as published by
513+ * the Free Software Foundation; version 3.
514+ *
515+ * This program is distributed in the hope that it will be useful,
516+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
517+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
518+ * GNU Lesser General Public License for more details.
519+ *
520+ * You should have received a copy of the GNU Lesser General Public License
521+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
522+ *
523+ */
524+
525+import QtQuick 2.4
526+
527+Label {}
528
529=== added file 'tests/unit_x11/tst_asyncloader/HeavyDocument.qml'
530--- tests/unit_x11/tst_asyncloader/HeavyDocument.qml 1970-01-01 00:00:00 +0000
531+++ tests/unit_x11/tst_asyncloader/HeavyDocument.qml 2016-03-01 10:45:34 +0000
532@@ -0,0 +1,31 @@
533+/*
534+ * Copyright 2016 Canonical Ltd.
535+ *
536+ * This program is free software; you can redistribute it and/or modify
537+ * it under the terms of the GNU Lesser General Public License as published by
538+ * the Free Software Foundation; version 3.
539+ *
540+ * This program is distributed in the hope that it will be useful,
541+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
542+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
543+ * GNU Lesser General Public License for more details.
544+ *
545+ * You should have received a copy of the GNU Lesser General Public License
546+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
547+ *
548+ */
549+
550+import QtQuick 2.4
551+import Ubuntu.Components 1.3
552+
553+Column {
554+ anchors.fill: parent
555+ spacing: units.dp(1)
556+ Repeater {
557+ model: 500
558+ TextField {
559+ width: parent.width
560+ }
561+ }
562+}
563+
564
565=== added file 'tests/unit_x11/tst_asyncloader/TestApp.qml'
566--- tests/unit_x11/tst_asyncloader/TestApp.qml 1970-01-01 00:00:00 +0000
567+++ tests/unit_x11/tst_asyncloader/TestApp.qml 2016-03-01 10:45:34 +0000
568@@ -0,0 +1,22 @@
569+/*
570+ * Copyright 2016 Canonical Ltd.
571+ *
572+ * This program is free software; you can redistribute it and/or modify
573+ * it under the terms of the GNU Lesser General Public License as published by
574+ * the Free Software Foundation; version 3.
575+ *
576+ * This program is distributed in the hope that it will be useful,
577+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
578+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
579+ * GNU Lesser General Public License for more details.
580+ *
581+ * You should have received a copy of the GNU Lesser General Public License
582+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
583+ *
584+ */
585+
586+import QtQuick 2.4
587+
588+Item {
589+
590+}
591
592=== added file 'tests/unit_x11/tst_asyncloader/tst_asyncloader.cpp'
593--- tests/unit_x11/tst_asyncloader/tst_asyncloader.cpp 1970-01-01 00:00:00 +0000
594+++ tests/unit_x11/tst_asyncloader/tst_asyncloader.cpp 2016-03-01 10:45:34 +0000
595@@ -0,0 +1,288 @@
596+/*
597+ * Copyright 2016 Canonical Ltd.
598+ *
599+ * This program is free software; you can redistribute it and/or modify
600+ * it under the terms of the GNU Lesser General Public License as published by
601+ * the Free Software Foundation; version 3.
602+ *
603+ * This program is distributed in the hope that it will be useful,
604+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
605+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
606+ * GNU Lesser General Public License for more details.
607+ *
608+ * You should have received a copy of the GNU Lesser General Public License
609+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
610+ *
611+ */
612+
613+#include <QtTest/QtTest>
614+#include "uctestcase.h"
615+#include "uctestextras.h"
616+#include <AsyncLoader>
617+#include <functional>
618+#include <QtQml/QQmlEngine>
619+
620+class LoaderSpy : public QObject
621+{
622+ Q_OBJECT
623+public:
624+ UbuntuToolkit::AsyncLoader *m_loader;
625+ QScopedPointer<QObject> m_object;
626+ bool m_done = false;
627+ bool m_error = false;
628+ QList<UbuntuToolkit::AsyncLoader::LoadingStatus> m_statusList;
629+public:
630+ LoaderSpy(UbuntuToolkit::AsyncLoader *loader)
631+ : QObject(0)
632+ , m_loader(loader)
633+ {
634+ connect(loader, &UbuntuToolkit::AsyncLoader::loadingStatus,
635+ this, &LoaderSpy::onLoadingStatusChanged);
636+ }
637+
638+protected Q_SLOTS:
639+ virtual void onLoadingStatusChanged(UbuntuToolkit::AsyncLoader::LoadingStatus status, QObject *object)
640+ {
641+ m_statusList << status;
642+ if (status == UbuntuToolkit::AsyncLoader::Ready) {
643+ m_done = true;
644+ m_object.reset(object);
645+ }
646+ if (status == UbuntuToolkit::AsyncLoader::Error) {
647+ m_error = true;
648+ }
649+ }
650+};
651+
652+class ResetLoaderSpy : public LoaderSpy
653+{
654+ Q_OBJECT
655+public:
656+ bool m_reset = false;
657+public:
658+ ResetLoaderSpy(UbuntuToolkit::AsyncLoader *loader)
659+ : LoaderSpy(loader)
660+ {
661+ }
662+
663+ void onLoadingStatusChanged(UbuntuToolkit::AsyncLoader::LoadingStatus status, QObject *object) override
664+ {
665+ if (status == UbuntuToolkit::AsyncLoader::Loading) {
666+ m_loader->reset();
667+ m_reset = true;
668+ }
669+ LoaderSpy::onLoadingStatusChanged(status, object);
670+ }
671+};
672+
673+class SecondLoaderSpy : public LoaderSpy
674+{
675+ Q_OBJECT
676+public:
677+ bool m_loadResult = false;
678+public:
679+ SecondLoaderSpy(UbuntuToolkit::AsyncLoader *loader, UbuntuToolkit::AsyncLoader::LoadingStatus loadAt,
680+ const QUrl &url, QQmlContext *context)
681+ : LoaderSpy(loader)
682+ , m_loadAt(loadAt)
683+ , m_url(url)
684+ , m_context(context)
685+ {
686+ }
687+
688+ void onLoadingStatusChanged(UbuntuToolkit::AsyncLoader::LoadingStatus status, QObject *object) override
689+ {
690+ if (status == (UbuntuToolkit::AsyncLoader::LoadingStatus)m_loadAt) {
691+ m_loadAt = -1;
692+ delete object;
693+ object = nullptr;
694+ m_loadResult = m_loader->load(m_url, m_context);
695+ }
696+ LoaderSpy::onLoadingStatusChanged(status, object);
697+ }
698+
699+private:
700+ int m_loadAt;
701+ QUrl m_url;
702+ QQmlContext *m_context;
703+};
704+
705+/********************************************************************
706+ * Test
707+ ********************************************************************/
708+class tst_AsyncLoader : public QObject
709+{
710+ Q_OBJECT
711+private Q_SLOTS:
712+
713+ void test_load_data()
714+ {
715+ QTest::addColumn<bool>("loadAsDocument");
716+ QTest::addColumn<QString>("document");
717+ QTest::addColumn<int>("mode");
718+ QTest::addColumn< QList<int> >("statuses");
719+
720+ QTest::newRow("document") << true << "Document.qml" << (int)QQmlComponent::Asynchronous
721+ << (QList<int>()
722+ << UbuntuToolkit::AsyncLoader::Compiling
723+ << UbuntuToolkit::AsyncLoader::Loading
724+ << UbuntuToolkit::AsyncLoader::Initializing
725+ << UbuntuToolkit::AsyncLoader::Ready
726+ );
727+ QTest::newRow("component, asynchronous") << false << "Document.qml" << (int)QQmlComponent::Asynchronous
728+ << (QList<int>()
729+ << UbuntuToolkit::AsyncLoader::Compiling
730+ << UbuntuToolkit::AsyncLoader::Loading
731+ << UbuntuToolkit::AsyncLoader::Initializing
732+ << UbuntuToolkit::AsyncLoader::Ready
733+ );
734+ QTest::newRow("component, synchronous") << false << "Document.qml" << (int)QQmlComponent::PreferSynchronous
735+ << (QList<int>()
736+ << UbuntuToolkit::AsyncLoader::Loading
737+ << UbuntuToolkit::AsyncLoader::Initializing
738+ << UbuntuToolkit::AsyncLoader::Ready
739+ );
740+ }
741+ void test_load()
742+ {
743+ QFETCH(bool, loadAsDocument);
744+ QFETCH(QString, document);
745+ QFETCH(int, mode);
746+ QFETCH(QList<int>, statuses);
747+
748+ QScopedPointer<UbuntuTestCase> view(new UbuntuTestCase("TestApp.qml"));
749+ UbuntuToolkit::AsyncLoader loader;
750+ LoaderSpy spy(&loader);
751+ QScopedPointer<QQmlComponent> component;
752+
753+ if (loadAsDocument) {
754+ loader.load(QUrl::fromLocalFile(document), view->rootContext());
755+ } else {
756+ // create a component
757+ component.reset(new QQmlComponent(view->engine(), QUrl::fromLocalFile(document), (QQmlComponent::CompilationMode)mode));
758+ loader.load(component.data(), view->rootContext());
759+ }
760+ QTRY_VERIFY(spy.m_object != nullptr);
761+ // check the statuses
762+ QCOMPARE(spy.m_statusList.length(), statuses.length());
763+ for (int i = 0; i < spy.m_statusList.length(); i++) {
764+ QVERIFY2((int)spy.m_statusList[i] == statuses[i], (QString(" Status at index %1 differs").arg(i)).toLocal8Bit().constData());
765+ }
766+ }
767+
768+ void test_load_with_error()
769+ {
770+ QUrl document = QUrl::fromLocalFile("FaultyDocument.qml");
771+ UbuntuTestCase::ignoreWarning("FaultyDocument.qml", 20, "Label is not a type");
772+ QScopedPointer<UbuntuTestCase> view(new UbuntuTestCase("TestApp.qml"));
773+ UbuntuToolkit::AsyncLoader loader;
774+ LoaderSpy spy(&loader);
775+ loader.load(document, view->rootContext());
776+ QTRY_VERIFY(spy.m_error == true);
777+ }
778+
779+ void test_load_no_context()
780+ {
781+ UbuntuToolkit::AsyncLoader loader;
782+ QVERIFY(!loader.load(nullptr, nullptr));
783+ QVERIFY(!loader.load(QUrl(), nullptr));
784+ }
785+
786+ void test_load_invalid_url()
787+ {
788+ QQmlEngine engine;
789+ UbuntuToolkit::AsyncLoader loader;
790+ LoaderSpy spy(&loader);
791+ QVERIFY(!loader.load(QUrl(), engine.rootContext()));
792+ }
793+
794+ void test_load_null_component()
795+ {
796+ QQmlEngine engine;
797+ UbuntuToolkit::AsyncLoader loader;
798+ LoaderSpy spy(&loader);
799+ QVERIFY(!loader.load(nullptr, engine.rootContext()));
800+ }
801+
802+ void test_load_and_cancel_data()
803+ {
804+ QTest::addColumn<bool>("loadAsDocument");
805+ QTest::addColumn<QString>("document");
806+ QTest::addColumn<int>("mode");
807+
808+ QTest::newRow("document") << true << "HeavyDocument.qml" << (int)QQmlComponent::Asynchronous;
809+ QTest::newRow("component, asynchronous") << false << "HeavyDocument.qml" << (int)QQmlComponent::Asynchronous;
810+ QTest::newRow("component, synchronous") << false << "HeavyDocument.qml" << (int)QQmlComponent::PreferSynchronous;
811+ }
812+ void test_load_and_cancel()
813+ {
814+ QFETCH(bool, loadAsDocument);
815+ QFETCH(QString, document);
816+ QFETCH(int, mode);
817+
818+ QScopedPointer<UbuntuTestCase> view(new UbuntuTestCase("TestApp.qml"));
819+ UbuntuToolkit::AsyncLoader loader;
820+ ResetLoaderSpy spy(&loader);
821+ QScopedPointer<QQmlComponent> component;
822+
823+ if (loadAsDocument) {
824+ loader.load(QUrl::fromLocalFile(document), view->rootContext());
825+ } else {
826+ // create a component
827+ component.reset(new QQmlComponent(view->engine(), QUrl::fromLocalFile(document), (QQmlComponent::CompilationMode)mode));
828+ loader.load(component.data(), view->rootContext());
829+ }
830+ if (loader.status() < UbuntuToolkit::AsyncLoader::Loading) {
831+ // cannot reset yet!
832+ QVERIFY(!loader.reset());
833+ }
834+ QTRY_VERIFY(spy.m_reset);
835+ }
836+
837+ void test_second_load_scenarios_data()
838+ {
839+ QTest::addColumn<QString> ("doc1");
840+ QTest::addColumn<QString> ("doc2");
841+ QTest::addColumn<int> ("when");
842+ QTest::addColumn<bool> ("success");
843+
844+ QTest::newRow("status = Compiling")
845+ << "Document.qml" << "HeavyDocument.qml"
846+ << (int)UbuntuToolkit::AsyncLoader::Compiling << false;
847+ QTest::newRow("status = Loading")
848+ << "Document.qml" << "HeavyDocument.qml"
849+ << (int)UbuntuToolkit::AsyncLoader::Loading << true;
850+ QTest::newRow("status = Initializing")
851+ << "Document.qml" << "HeavyDocument.qml"
852+ << (int)UbuntuToolkit::AsyncLoader::Initializing << true;
853+ QTest::newRow("status = Ready")
854+ << "Document.qml" << "HeavyDocument.qml"
855+ << (int)UbuntuToolkit::AsyncLoader::Ready << true;
856+ QTest::newRow("status = Error")
857+ << "FaultyDocument.qml" << "HeavyDocument.qml"
858+ << (int)UbuntuToolkit::AsyncLoader::Error << true;
859+ }
860+ void test_second_load_scenarios()
861+ {
862+ QFETCH(QString, doc1);
863+ QFETCH(QString, doc2);
864+ QFETCH(int, when);
865+ QFETCH(bool, success);
866+
867+ QScopedPointer<UbuntuTestCase> view(new UbuntuTestCase("TestApp.qml"));
868+ UbuntuToolkit::AsyncLoader loader;
869+ if (when == (int)UbuntuToolkit::AsyncLoader::Error) {
870+ UbuntuTestCase::ignoreWarning("FaultyDocument.qml", 20, "Label is not a type");
871+ }
872+
873+ SecondLoaderSpy spy(&loader, (UbuntuToolkit::AsyncLoader::LoadingStatus)when, QUrl::fromLocalFile(doc2), view->rootContext());
874+ // load the first document
875+ QVERIFY(loader.load(QUrl::fromLocalFile(doc1), view->rootContext()));
876+ QTRY_VERIFY(spy.m_object != nullptr);
877+ QCOMPARE(spy.m_loadResult, success);
878+ }
879+};
880+
881+QTEST_MAIN(tst_AsyncLoader)
882+
883+#include "tst_asyncloader.moc"
884
885=== added file 'tests/unit_x11/tst_asyncloader/tst_asyncloader.pro'
886--- tests/unit_x11/tst_asyncloader/tst_asyncloader.pro 1970-01-01 00:00:00 +0000
887+++ tests/unit_x11/tst_asyncloader/tst_asyncloader.pro 2016-03-01 10:45:34 +0000
888@@ -0,0 +1,11 @@
889+include(../test-include.pri)
890+QT += core-private qml-private quick-private gui-private UbuntuToolkit
891+
892+SOURCES += \
893+ tst_asyncloader.cpp
894+
895+DISTFILES += \
896+ Document.qml \
897+ TestApp.qml \
898+ HeavyDocument.qml \
899+ FaultyDocument.qml
900
901=== modified file 'tests/unit_x11/unit_x11.pro'
902--- tests/unit_x11/unit_x11.pro 2015-11-23 16:12:16 +0000
903+++ tests/unit_x11/unit_x11.pro 2016-03-01 10:45:34 +0000
904@@ -17,4 +17,5 @@
905 tst_subtheming \
906 tst_swipearea \
907 tst_touchregistry \
908- tst_bottomedge
909+ tst_bottomedge \
910+ tst_asyncloader

Subscribers

People subscribed via source and target branches