Merge lp:~zsombi/ubuntu-ui-toolkit/other-vibrations into lp:ubuntu-ui-toolkit/rtm

Proposed by Zsombor Egri
Status: Merged
Approved by: Cris Dywan
Approved revision: 1143
Merged at revision: 1142
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/other-vibrations
Merge into: lp:ubuntu-ui-toolkit/rtm
Diff against target: 1168 lines (+1014/-9)
17 files modified
components.api (+12/-0)
debian/control (+3/-0)
modules/Ubuntu/Components/AbstractButton.qml (+1/-1)
modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp (+226/-0)
modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.h (+55/-0)
modules/Ubuntu/Components/plugin/plugin.cpp (+2/-0)
modules/Ubuntu/Components/plugin/plugin.pro (+7/-2)
modules/Ubuntu/Components/plugin/ucserviceproperties.cpp (+317/-0)
modules/Ubuntu/Components/plugin/ucserviceproperties.h (+83/-0)
modules/Ubuntu/Components/plugin/ucserviceproperties_p.h (+53/-0)
tests/unit/runtest.sh (+5/-5)
tests/unit_x11/tst_serviceproperties/IncomingCallVibrateWatcher.qml (+31/-0)
tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher.qml (+31/-0)
tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher2.qml (+32/-0)
tests/unit_x11/tst_serviceproperties/tst_serviceproperties.cpp (+146/-0)
tests/unit_x11/tst_serviceproperties/tst_serviceproperties.pro (+8/-0)
tests/unit_x11/unit_x11.pro (+2/-1)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/other-vibrations
Reviewer Review Type Date Requested Status
Cris Dywan Approve
Jonas G. Drange (community) Needs Information
Review via email: mp+242404@code.launchpad.net

Commit message

isHapticsFeedbackEnabled context property exposed to drive HapticsFeedback in AbstractButton.

To post a comment you must log in.
Revision history for this message
Zsombor Egri (zsombi) wrote :

DBusPropertyWatcher adaptation layer API added to support multiple DBus service watching. It has been added as adaptation layer API as prerequisite for the multi platform support (SDK must enable development on other platforms).

The property is a temporary workaround to support the feature requested. Future singleton API should be productised during the near future to replace this property.

Revision history for this message
Jonas G. Drange (jonas-drange) wrote :

Looks good, but I have added a couple of comments concerning names.

review: Needs Information
Revision history for this message
Cris Dywan (kalikiana) wrote :

Some comments with ideas to make the API simpler, and some concerns on the sync bits and eror handling. If we use this for more in the near future it's probably worth making it more robust even if it's for now internal.

84 +void DBusPropertyWatcher::syncProperties(const QString &serviceInterface)
100 +QVariant DBusPropertyWatcher::readProperty(const QString &interface, const QString &property)
117 +bool DBusPropertyWatcher::writeProperty(const QString &interface, const QString &property, const QVariant &value)

Sync isn't nice. By the looks of it, it's handy for testing - thing is, you're not actually testing the production code paths so you're not doing yourself a favor in the end.

What I'd like suggest is: a default value is given. the current property is read async after setup. the value is cached so read can be sync. write is async always. success can be seen in an error property & signal. the new value will be reflected by the propertiesChanged.
If we want multiple properties we can make that a list of names and defaults, and asking for something other than what you specified will be an error, giving you extra safety even if you're making a typo on the consumer side.

I'll be happy to look into the testing approach based on all-async.

This also automatically makes a QML-able API if we want it that looks and feels similar to "Binding".

163 +void DBusPropertyWatcher::setupInterface()

This needs to warn if the object name is invalid.
I presume we also need error handling for connect().

9 + if (isHapticsFeedbackEnabled) {

As an idea, by simplifying the API a bit we could use DBusWatcher in QML and avoid the context property. Or we could have a little internal "settings" object if we re-use it in the future.

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

> Looks good, but I have added a couple of comments concerning names.

Thanks for the comments, I tried to reply inline, hopefully it'll get in.

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

Jonas, looks like replying to an inline-comitter comment does not land with the reply to the inline comments...

Revision history for this message
Zsombor Egri (zsombi) wrote :
Download full text (3.9 KiB)

> Some comments with ideas to make the API simpler, and some concerns on the
> sync bits and eror handling. If we use this for more in the near future it's
> probably worth making it more robust even if it's for now internal.
>
> 84 +void DBusPropertyWatcher::syncProperties(const QString
> &serviceInterface)
> 100 +QVariant DBusPropertyWatcher::readProperty(const QString &interface,
> const QString &property)
> 117 +bool DBusPropertyWatcher::writeProperty(const QString &interface,
> const QString &property, const QVariant &value)
>
> Sync isn't nice. By the looks of it, it's handy for testing - thing is, you're
> not actually testing the production code paths so you're not doing yourself a
> favor in the end.

The first one (syncProperties) initiates reading of the watched properties. Yet synchronously. Calls the readProperty() for each watched property, so if we have async read, we will get this async as well. Anyway, the property values read are reported by propertyChanged() signal.

Even if we'd have read/write async, we may want also to keep the sync version of them. And yes, error codes/messages we receive from DBus should be exported, so that part we must also add.

>
> What I'd like suggest is: a default value is given. the current property is
> read async after setup. the value is cached so read can be sync. write is
> async always. success can be seen in an error property & signal. the new value
> will be reflected by the propertiesChanged.

Yep, I agree on this. So far the default is like that, just the current values are read synchronously. Yet we only use write in testing, and tbh the only reason I added the writeProperty() was to be able to test the watching functionality, as I wanted to have a module test which is not DBus-agnostic, and uses the same connection/interface instances the watcher uses. After all, a write method in a watcher doesn't really has its place. But if we have write, the "watcher" might not be the best name anymore, it's rather a DBusService.

> If we want multiple properties we can make that a list of names and defaults,
> and asking for something other than what you specified will be an error,
> giving you extra safety even if you're making a typo on the consumer side.

I'm not sure the intention of DBusWatcher was that. The SystemSettings otoh keeps the defaults and initiates the watcher(s) to get the correct settings - yet we have only one watcher, for the accounts, but we could have more. Each service watcher can watch for several properties on the same service interface, but all defaults are handled in the SystemSettings.
>
> I'll be happy to look into the testing approach based on all-async.

Thanks, but I'd not have any CPO yet for this, as it's more like an internal component.

>
> This also automatically makes a QML-able API if we want it that looks and
> feels similar to "Binding".

Aaah, now I get why you want to see a default in the watcher. In that case, yes, it would be beneficial to have a default on that level.

>
> 163 +void DBusPropertyWatcher::setupInterface()
>
> This needs to warn if the object name is invalid.
> I presume we also need error handling for connect...

Read more...

1142. By Zsombor Egri

API changed

Revision history for this message
Cris Dywan (kalikiana) wrote :

64 +#define DYNAMIC_PROPERTY "__q_proeprty"
81 + // crear all previous connections
914 + property bool thisIsAndInvalidPropertyToWatch: true

Typos.

Also, can we test with valid *and* invalid? To make sure the error won't be overridden by the valid one. From the code it doesn't seem to stop the loop in that case at least.

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

> 64 +#define DYNAMIC_PROPERTY "__q_proeprty"
> 81 + // crear all previous connections
> 914 + property bool thisIsAndInvalidPropertyToWatch: true
>
> Typos.

Ups... I should have an ibus filter which fixes proeprty, teh and si (is) typos :D

>
> Also, can we test with valid *and* invalid? To make sure the error won't be
> overridden by the valid one. From the code it doesn't seem to stop the loop in
> that case at least.

Yes, I will add one. The error will not be cleared ever if there was an error set. So a valid property won't clear the previous invalid property reported.

1143. By Zsombor Egri

review comments applied

Revision history for this message
Cris Dywan (kalikiana) wrote :

Thanks for taking my comments into account. Looking nice now!

review: Approve
1144. By Zsombor Egri

test and launch updates

1145. By Zsombor Egri

adding build dependencies

1146. By Zsombor Egri

rtm merge

1147. By Zsombor Egri

typo fixed

1148. By Zsombor Egri

one more typo

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2014-11-07 13:39:31 +0000
3+++ components.api 2015-02-04 14:00:19 +0000
4@@ -915,6 +915,18 @@
5 Signal {
6 name: "sourceChanged"
7 Parameter { type: "QUrl" }
8+ name: "UCServiceProperties"
9+ prototype: "QObject"
10+ exports: ["ServiceProperties 1.1"]
11+ name: "ServiceType"
12+ name: "Status"
13+ Property { name: "type"; revision: 1; type: "ServiceType" }
14+ Property { name: "service"; revision: 1; type: "string" }
15+ Property { name: "path"; revision: 1; type: "string" }
16+ Property { name: "serviceInterface"; revision: 1; type: "string" }
17+ Property { name: "adaptorInterface"; revision: 1; type: "string" }
18+ Property { name: "error"; revision: 1; type: "string"; isReadonly: true }
19+ Property { name: "status"; revision: 1; type: "Status"; isReadonly: true }
20 name: "UCStateSaver"
21 prototype: "QObject"
22 exports: ["StateSaver 0.1", "StateSaver 1.0"]
23
24=== modified file 'debian/control'
25--- debian/control 2014-10-16 11:21:39 +0000
26+++ debian/control 2015-02-04 14:00:19 +0000
27@@ -37,6 +37,9 @@
28 language-pack-en-base,
29 libdbus-1-dev,
30 libnih-dbus-dev,
31+ dbus,
32+ dbus-test-runner,
33+ accountsservice,
34 xvfb,
35 libgl1-mesa-dri,
36 locales,
37
38=== modified file 'modules/Ubuntu/Components/AbstractButton.qml'
39--- modules/Ubuntu/Components/AbstractButton.qml 2014-09-02 05:52:32 +0000
40+++ modules/Ubuntu/Components/AbstractButton.qml 2015-02-04 14:00:19 +0000
41@@ -104,7 +104,7 @@
42
43 onClicked: {
44 if (button.__acceptEvents) {
45- pressEffect.start()
46+ pressEffect.start();
47 button.clicked()
48 }
49 }
50
51=== added file 'modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp'
52--- modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp 1970-01-01 00:00:00 +0000
53+++ modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp 2015-02-04 14:00:19 +0000
54@@ -0,0 +1,226 @@
55+/*
56+ * Copyright 2014 Canonical Ltd.
57+ *
58+ * This program is free software; you can redistribute it and/or modify
59+ * it under the terms of the GNU Lesser General Public License as published by
60+ * the Free Software Foundation; version 3.
61+ *
62+ * This program is distributed in the hope that it will be useful,
63+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
64+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
65+ * GNU Lesser General Public License for more details.
66+ *
67+ * You should have received a copy of the GNU Lesser General Public License
68+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
69+ */
70+
71+#include "dbuspropertywatcher_p.h"
72+#include <QtDBus/QDBusReply>
73+#include <unistd.h>
74+#include <sys/types.h>
75+#include "i18n.h"
76+#include <QtQml/QQmlInfo>
77+
78+#define DYNAMIC_PROPERTY "__q_property"
79+
80+UCServicePropertiesPrivate *createServicePropertiesAdapter(UCServiceProperties *owner)
81+{
82+ return new DBusServiceProperties(owner);
83+}
84+
85+DBusServiceProperties::DBusServiceProperties(UCServiceProperties *qq)
86+ : UCServicePropertiesPrivate(qq)
87+ , connection("")
88+ , watcher(0)
89+ , iface(0)
90+{
91+}
92+
93+bool DBusServiceProperties::init()
94+{
95+ // crear previous connections
96+ setStatus(UCServiceProperties::Inactive);
97+ delete iface;
98+ iface = 0;
99+ delete watcher;
100+ watcher = 0;
101+ setError(QString());
102+
103+ if (service.isEmpty() || path.isEmpty()) {
104+ setStatus(UCServiceProperties::ConnectionError);
105+ setError(UbuntuI18n::instance().tr("No service/path specified"));
106+ return false;
107+ }
108+
109+ switch (type) {
110+ case UCServiceProperties::System:
111+ {
112+ connection = QDBusConnection::systemBus();
113+ break;
114+ }
115+ case UCServiceProperties::Session:
116+ {
117+ connection = QDBusConnection::sessionBus();
118+ break;
119+ }
120+ default:
121+ {
122+ setStatus(UCServiceProperties::ConnectionError);
123+ setError(UbuntuI18n::instance().tr("Invalid bus type: %1.").arg(type));
124+ return false;
125+ }
126+ }
127+
128+ Q_Q(UCServiceProperties);
129+ // connect dbus watcher to catch OwnerChanged
130+ watcher = new QDBusServiceWatcher(service, connection, QDBusServiceWatcher::WatchForOwnerChange, q);
131+ // connect interface
132+ iface = new QDBusInterface(service, path, interface, connection, q);
133+ if (!iface->isValid()) {
134+ setStatus(UCServiceProperties::ConnectionError);
135+ setError(iface->lastError().message());
136+ return false;
137+ }
138+ // connect watcher to get owner changes
139+ QObject::connect(watcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
140+ this, SLOT(changeServiceOwner(QString,QString,QString)));
141+ return setupInterface();
142+}
143+
144+/*
145+ * Connect dbus signal identified by (service, path, iface, name) quaduple to a
146+ * slot to receive property changes.
147+ */
148+bool DBusServiceProperties::setupInterface()
149+{
150+ QDBusReply<QDBusObjectPath> dbusObjectPath = iface->call("FindUserById", qlonglong(getuid()));
151+ if (dbusObjectPath.isValid()) {
152+ objectPath = dbusObjectPath.value().path();
153+ iface->connection().connect(
154+ service,
155+ objectPath,
156+ "org.freedesktop.DBus.Properties",
157+ "PropertiesChanged",
158+ this,
159+ SLOT(updateProperties(QString,QVariantMap,QStringList)));
160+ return true;
161+ }
162+
163+ setStatus(UCServiceProperties::ConnectionError);
164+ setError(dbusObjectPath.error().message());
165+ return false;
166+}
167+
168+bool DBusServiceProperties::fetchPropertyValues()
169+{
170+ scannedProperties = properties;
171+ Q_FOREACH(QString property, properties) {
172+ readProperty(property);
173+ }
174+ return true;
175+}
176+
177+/*
178+ * Reads a property value from the adaptorInterface asynchronously.
179+ */
180+bool DBusServiceProperties::readProperty(const QString &property)
181+{
182+ if ((status < UCServiceProperties::Synchronizing) || objectPath.isEmpty()) {
183+ return false;
184+ }
185+ Q_Q(UCServiceProperties);
186+ QDBusInterface readIFace(iface->interface(), objectPath, "org.freedesktop.DBus.Properties", connection);
187+ if (!readIFace.isValid()) {
188+ // report invalid interface only if the property's first letter was with capital one!
189+ if (property[0].isUpper()) {
190+ qmlInfo(q) << readIFace.lastError().message();
191+ }
192+ return false;
193+ }
194+ QDBusPendingCall pending = readIFace.asyncCall("Get", adaptor, property);
195+ if (pending.isError()) {
196+ qmlInfo(q) << pending.error().message();
197+ return false;
198+ }
199+ QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(pending, q);
200+ QObject::connect(callWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
201+ this, SLOT(readFinished(QDBusPendingCallWatcher*)));
202+
203+ // set a dynamic property so we know which property are we reading
204+ callWatcher->setProperty(DYNAMIC_PROPERTY, property);
205+ return true;
206+}
207+
208+/*
209+ * Writes a property value to theadaptorInterface synchronously. It is for pure testing purposes.
210+ */
211+bool DBusServiceProperties::testProperty(const QString &property, const QVariant &value)
212+{
213+ if (objectPath.isEmpty()) {
214+ return false;
215+ }
216+ QDBusInterface writeIFace(iface->interface(), objectPath, "org.freedesktop.DBus.Properties", connection);
217+ if (!writeIFace.isValid()) {
218+ // invalid interface
219+ return false;
220+ }
221+ QDBusMessage msg = writeIFace.call("Set", adaptor, property, QVariant::fromValue(QDBusVariant(value)));
222+ return msg.type() == QDBusMessage::ReplyMessage;
223+}
224+
225+/*
226+ * Slot called when the async read operation finishes.
227+ */
228+void DBusServiceProperties::readFinished(QDBusPendingCallWatcher *call)
229+{
230+ Q_Q(UCServiceProperties);
231+ QDBusPendingReply<QVariant> reply = *call;
232+ QString property = call->property(DYNAMIC_PROPERTY).toString();
233+ scannedProperties.removeAll(property);
234+ if (reply.isError()) {
235+ // remove the property from being watched, as it has no property like that
236+ properties.removeAll(property);
237+ if (property[0].isUpper()) {
238+ // report error!
239+ qmlInfo(q) << reply.error().message();
240+ }
241+ } else {
242+ // update watched property value
243+ // make sure we have lower case when the property value is updated
244+ property[0] = property[0].toLower();
245+ q->setProperty(property.toLocal8Bit().constData(), reply.value());
246+ }
247+
248+ if ((status == UCServiceProperties::Synchronizing) && scannedProperties.isEmpty()) {
249+ // set status to active
250+ setStatus(UCServiceProperties::Active);
251+ }
252+
253+ // delete watcher
254+ call->deleteLater();
255+}
256+
257+/*
258+ * Slot called when service owner is changed.
259+ */
260+void DBusServiceProperties::changeServiceOwner(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
261+{
262+ Q_UNUSED(oldOwner);
263+ Q_UNUSED(newOwner);
264+ if (serviceName != service) {
265+ return;
266+ }
267+ setupInterface();
268+}
269+
270+/*
271+ * Slot called when the properties are changed in the service.
272+ */
273+void DBusServiceProperties::updateProperties(const QString &onInterface, const QVariantMap &map, const QStringList &invalidated)
274+{
275+ Q_UNUSED(onInterface);
276+ Q_UNUSED(map);
277+ Q_FOREACH(const QString &property, invalidated) {
278+ readProperty(property);
279+ }
280+}
281
282=== added file 'modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.h'
283--- modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.h 1970-01-01 00:00:00 +0000
284+++ modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.h 2015-02-04 14:00:19 +0000
285@@ -0,0 +1,55 @@
286+/*
287+ * Copyright 2014 Canonical Ltd.
288+ *
289+ * This program is free software; you can redistribute it and/or modify
290+ * it under the terms of the GNU Lesser General Public License as published by
291+ * the Free Software Foundation; version 3.
292+ *
293+ * This program is distributed in the hope that it will be useful,
294+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
295+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
296+ * GNU Lesser General Public License for more details.
297+ *
298+ * You should have received a copy of the GNU Lesser General Public License
299+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
300+ */
301+
302+#ifndef DBUSPROPERTYWATCHER_P_H
303+#define DBUSPROPERTYWATCHER_P_H
304+
305+#include <QtCore/QObject>
306+#include <QtDBus/QDBusConnection>
307+#include <QtDBus/QDBusServiceWatcher>
308+#include <QtDBus/QDBusInterface>
309+
310+#include "ucserviceproperties_p.h"
311+
312+class QDBusPendingCallWatcher;
313+class DBusServiceProperties : public QObject, public UCServicePropertiesPrivate
314+{
315+ Q_OBJECT
316+ Q_DECLARE_PUBLIC(UCServiceProperties)
317+public:
318+ DBusServiceProperties(UCServiceProperties *qq);
319+
320+ bool init();
321+ bool fetchPropertyValues();
322+ bool readProperty(const QString &property);
323+ // for testing purposes only!!!
324+ bool testProperty(const QString &property, const QVariant &value);
325+
326+ QStringList scannedProperties;
327+ QDBusConnection connection;
328+ QDBusServiceWatcher *watcher;
329+ QDBusInterface *iface;
330+ QString objectPath;
331+
332+ bool setupInterface();
333+
334+public Q_SLOTS:
335+ void readFinished(QDBusPendingCallWatcher *watcher);
336+ void changeServiceOwner(const QString &serviceName, const QString &oldOwner, const QString &newOwner);
337+ void updateProperties(const QString &iface, const QVariantMap &map, const QStringList &invalidated);
338+};
339+
340+#endif // DBUSPROPERTYWATCHER_P_H
341
342=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
343--- modules/Ubuntu/Components/plugin/plugin.cpp 2014-10-07 14:37:16 +0000
344+++ modules/Ubuntu/Components/plugin/plugin.cpp 2015-02-04 14:00:19 +0000
345@@ -52,6 +52,7 @@
346 #include "ucaction.h"
347 #include "ucactioncontext.h"
348 #include "ucactionmanager.h"
349+#include "ucserviceproperties.h"
350
351 #include <sys/types.h>
352 #include <unistd.h>
353@@ -162,6 +163,7 @@
354 qmlRegisterType<QSortFilterProxyModelQML>(uri, 1, 1, "SortFilterModel");
355 qmlRegisterUncreatableType<FilterBehavior>(uri, 1, 1, "FilterBehavior", "Not instantiable");
356 qmlRegisterUncreatableType<SortBehavior>(uri, 1, 1, "SortBehavior", "Not instantiable");
357+ qmlRegisterType<UCServiceProperties, 1>(uri, 1, 1, "ServiceProperties");
358 }
359
360 void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
361
362=== modified file 'modules/Ubuntu/Components/plugin/plugin.pro'
363--- modules/Ubuntu/Components/plugin/plugin.pro 2014-10-07 14:37:16 +0000
364+++ modules/Ubuntu/Components/plugin/plugin.pro 2015-02-04 14:00:19 +0000
365@@ -68,7 +68,10 @@
366 ucaction.h \
367 ucactioncontext.h \
368 ucactionmanager.h \
369- adapters/actionsproxy_p.h
370+ adapters/actionsproxy_p.h \
371+ adapters/dbuspropertywatcher_p.h \
372+ ucserviceproperties.h \
373+ ucserviceproperties_p.h
374
375 SOURCES += plugin.cpp \
376 uctheme.cpp \
377@@ -105,7 +108,9 @@
378 ucaction.cpp \
379 ucactioncontext.cpp \
380 ucactionmanager.cpp \
381- adapters/actionsproxy_p.cpp
382+ adapters/actionsproxy_p.cpp \
383+ adapters/dbuspropertywatcher_p.cpp \
384+ ucserviceproperties.cpp
385
386 # adapters
387 SOURCES += adapters/alarmsadapter_organizer.cpp
388
389=== added file 'modules/Ubuntu/Components/plugin/ucserviceproperties.cpp'
390--- modules/Ubuntu/Components/plugin/ucserviceproperties.cpp 1970-01-01 00:00:00 +0000
391+++ modules/Ubuntu/Components/plugin/ucserviceproperties.cpp 2015-02-04 14:00:19 +0000
392@@ -0,0 +1,317 @@
393+/*
394+ * Copyright 2014 Canonical Ltd.
395+ *
396+ * This program is free software; you can redistribute it and/or modify
397+ * it under the terms of the GNU Lesser General Public License as published by
398+ * the Free Software Foundation; version 3.
399+ *
400+ * This program is distributed in the hope that it will be useful,
401+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
402+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
403+ * GNU Lesser General Public License for more details.
404+ *
405+ * You should have received a copy of the GNU Lesser General Public License
406+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
407+ */
408+
409+#include "ucserviceproperties.h"
410+#include "ucserviceproperties_p.h"
411+#include "i18n.h"
412+#include <QtQml/QQmlInfo>
413+#include <QtCore/QMetaProperty>
414+#include <QtQml/QQmlProperty>
415+#include <QtQml/private/qqmlproperty_p.h>
416+
417+UCServicePropertiesPrivate::UCServicePropertiesPrivate(UCServiceProperties *qq)
418+ : q_ptr(qq)
419+ , ready(false)
420+ , status(UCServiceProperties::Inactive)
421+ , type(UCServiceProperties::System)
422+{
423+}
424+
425+UCServicePropertiesPrivate::~UCServicePropertiesPrivate()
426+{
427+}
428+
429+UCServicePropertiesPrivate *UCServicePropertiesPrivate::get(UCServiceProperties *service)
430+{
431+ return service->d_func();
432+}
433+
434+void UCServicePropertiesPrivate::setError(const QString &msg)
435+{
436+ if (error == msg) {
437+ return;
438+ }
439+ error = msg;
440+ Q_EMIT q_ptr->errorChanged();
441+}
442+
443+void UCServicePropertiesPrivate::setStatus(UCServiceProperties::Status status)
444+{
445+ if (this->status == status) {
446+ return;
447+ }
448+ this->status = status;
449+ Q_EMIT q_ptr->statusChanged();
450+}
451+
452+void printLocked(UCServiceProperties *owner)
453+{
454+ qmlInfo(owner) << UbuntuI18n::instance().tr("Changing connection parameters forbidden.");
455+}
456+
457+/*!
458+ * \qmltype ServiceProperties
459+ * \instantiates UCServiceProperties
460+ * \inqmlmodule Ubuntu.Components 1.1
461+ * \since Ubuntu.Components 1.1
462+ * \ingroup ubuntu-services
463+ * \brief The component enables accessing service properties from QML.
464+ *
465+ * The services accessed by the component are ones providing their interfaces
466+ * through DBus. The component is specialized to read properties exposed by these
467+ * services, andf to keep these property values up to date. It is not meant to
468+ * access signals or slots exposed, nor to change the values of the properties
469+ * watched.
470+ *
471+ * Properties watched should be declared within the body of the component like
472+ * any other QML property, preferably defining a default value for them. The component
473+ * will enumerate these properties and will ask the service to provide values for
474+ * those. When enumerating properties, each property will be checked twice, with
475+ * the case specified as well as with the first letter capitalized.
476+ * \qml
477+ * import QtQuick 2.3
478+ * import Ubuntu.Components 1.1
479+ *
480+ * ServiceProperties {
481+ * service: "org.freenode.AccountsService"
482+ * path: "/org/freenode/AccountsService"
483+ * serviceInterface: "org.freenode.AccountsService"
484+ * adaptorInterface: "com.ubuntu.touch.Accounts.Sound"
485+ * // listing properties to watch
486+ * // each property name existence will be checked against the current case
487+ * // as well as with first character capitalized
488+ * property bool incomingCallVibrate: true
489+ * }
490+ * \endqml
491+ *
492+ * Note that there are few properties which must be set in order the component
493+ * to work. These are \l service, \l path and \l adaptorInterface. Also, once
494+ * specified, \l service, \l serviceInterface and \l adaptorInterface values
495+ * should not be changed as it cannot be guaranteed that properties watched will
496+ * be available on those service. Therefore any change on these properties after
497+ * the component completion will be ignored. Property bindings on properties
498+ * watched will be ignored as well, as service will report changes in these property
499+ * values.
500+ *
501+ * The service is connected once the component gets completed (Component.onCompleted).
502+ * The \l error property specifies any error occured during connection, and the
503+ * \l status property notifies whether the connection to the service is active or not.
504+ *
505+ * \note Pay attention when chosing the service watched, and set your application's
506+ * AppArmor rights to ensure a successful service connection.
507+ */
508+UCServiceProperties::UCServiceProperties(QObject *parent)
509+ : QObject(parent)
510+ , d_ptr(createServicePropertiesAdapter(this))
511+{
512+}
513+UCServiceProperties::~UCServiceProperties()
514+{
515+ delete d_ptr;
516+ d_ptr = 0;
517+}
518+
519+void UCServiceProperties::classBegin()
520+{
521+}
522+
523+void UCServiceProperties::componentComplete()
524+{
525+ Q_D(UCServiceProperties);
526+ d->ready = true;
527+ // enumerate properties
528+ const QMetaObject *mo = metaObject();
529+ for (int i = mo->propertyOffset(); i < mo->propertyCount(); i++) {
530+ const QMetaProperty prop = mo->property(i);
531+ QString property(prop.name());
532+
533+ // check the binding on the property and warn if there is one.
534+ QQmlProperty qmlProperty(this, property);
535+ if (QQmlPropertyPrivate::binding(qmlProperty)) {
536+ qmlInfo(this) << UbuntuI18n::instance().
537+ tr("Binding detected on property '%1' will be removed by the service updates.").
538+ arg(property);
539+ }
540+ // insert both the declared and capitalized first character properties
541+ d->properties << property;
542+ property[0] = property[0].toUpper();
543+ d->properties << property;
544+ }
545+ // initialize DBus
546+ if (d->init()) {
547+ d->setStatus(UCServiceProperties::Synchronizing);
548+ d->fetchPropertyValues();
549+ }
550+}
551+
552+/*!
553+ * \qmlproperty enum ServiceProperties::type
554+ * Specifies the DBus connection session type. It can get the following values:
555+ * \list
556+ * \li - \e ServiceProperties.System when system bus is used (default)
557+ * \li - \e ServiceProperties.Session when session bus is used
558+ * \endlist
559+ */
560+UCServiceProperties::ServiceType UCServiceProperties::type() const
561+{
562+ Q_D(const UCServiceProperties);
563+ return d->type;
564+}
565+void UCServiceProperties::setType(ServiceType type)
566+{
567+ Q_D(UCServiceProperties);
568+ if (d->type == type) {
569+ return;
570+ }
571+ if (d->ready) {
572+ printLocked(this);
573+ return;
574+ }
575+ d->type = type;
576+ Q_EMIT typeChanged();
577+}
578+
579+/*!
580+ * \qmlproperty string ServiceProperties::service
581+ * The proeprty specifies the DBus service URI. It is mandatory to be specified.
582+ */
583+QString UCServiceProperties::service() const
584+{
585+ Q_D(const UCServiceProperties);
586+ return d->service;
587+}
588+void UCServiceProperties::setService(const QString &value)
589+{
590+ Q_D(UCServiceProperties);
591+ if (d->service == value) {
592+ return;
593+ }
594+ if (d->ready) {
595+ printLocked(this);
596+ return;
597+ }
598+ d->service = value;
599+ Q_EMIT serviceChanged();
600+}
601+
602+/*!
603+ * \qmlproperty string ServiceProperties::path
604+ * The property specifies the DBus service connection path. It is mandatory to be
605+ * specified.
606+ */
607+QString UCServiceProperties::path() const
608+{
609+ Q_D(const UCServiceProperties);
610+ return d->path;
611+}
612+void UCServiceProperties::setPath(const QString &value)
613+{
614+ Q_D(UCServiceProperties);
615+ if (d->path == value) {
616+ return;
617+ }
618+ d->path = value;
619+ Q_EMIT pathChanged();
620+ if (d->ready) {
621+ // need to re-initialize connections
622+ d->init();
623+ }
624+}
625+
626+/*!
627+ * \qmlproperty string ServiceProperties::serviceInterface
628+ * The property specifies the service intertface. If it is an empty string, the
629+ * component will refer to the merging of all interfaces found in the service.
630+ */
631+QString UCServiceProperties::interface() const
632+{
633+ Q_D(const UCServiceProperties);
634+ return d->interface;
635+}
636+void UCServiceProperties::setInterface(const QString &value)
637+{
638+ Q_D(UCServiceProperties);
639+ if (d->interface == value) {
640+ return;
641+ }
642+ if (d->ready) {
643+ printLocked(this);
644+ return;
645+ }
646+ d->interface = value;
647+ Q_EMIT serviceInterfaceChanged();
648+}
649+/*!
650+ * \qmlproperty string ServiceProperties::adaptorInterface
651+ * The proeprty specifies the dbus adaptor interface which provides the properties
652+ * watched. This can be a different interface that the one specified in \l serviceInterface,
653+ * and in the same way, it can be empty, in which case all the properties from all
654+ * interfaces of the service will be watched.
655+ */
656+QString UCServiceProperties::adaptor() const
657+{
658+ Q_D(const UCServiceProperties);
659+ return d->adaptor;
660+}
661+void UCServiceProperties::setAdaptor(const QString &value)
662+{
663+ Q_D(UCServiceProperties);
664+ if (d->adaptor == value) {
665+ return;
666+ }
667+ if (d->ready) {
668+ printLocked(this);
669+ return;
670+ }
671+ d->adaptor = value;
672+ Q_EMIT adaptorInterfaceChanged();
673+}
674+
675+/*!
676+ * \qmlproperty string ServiceProperties::error
677+ * The property is set with a human readablestring each time an error occurrs
678+ * during the service connection. Empty string means no error.
679+ */
680+QString UCServiceProperties::error()
681+{
682+ Q_D(UCServiceProperties);
683+ return d->error;
684+}
685+
686+/*!
687+ * \qmlproperty enum ServiceProperties::status
688+ * The property presents the status of the component.
689+ * \list
690+ * \li - \e ServiceProperties.Inactive - the component is inactive, initial state
691+ * \li - \e ServiceProperties.ConnectionError - there was a connection error, the
692+ * \l error contains the error string.
693+ * \li - \e ServiceProperties.Synchronizing - the connection to the service succeeded,
694+ * and the properties are being synchronized;
695+ * \li - \e ServiceProperties.Active - the service watcher is active and initial
696+ * property synchronization completed.
697+ * \endlist
698+ * \note While the status is set to \e Synchronizing, the properties are checked
699+ * against their existence in the service. Each proeprty will be checked as declared
700+ * as well with capital first letter. If neither of these exists in the service,
701+ * it will be reported in the \l error property separately.
702+ */
703+UCServiceProperties::Status UCServiceProperties::status() const
704+{
705+ Q_D(const UCServiceProperties);
706+ return d->status;
707+}
708+
709+#include "moc_ucserviceproperties.cpp"
710
711=== added file 'modules/Ubuntu/Components/plugin/ucserviceproperties.h'
712--- modules/Ubuntu/Components/plugin/ucserviceproperties.h 1970-01-01 00:00:00 +0000
713+++ modules/Ubuntu/Components/plugin/ucserviceproperties.h 2015-02-04 14:00:19 +0000
714@@ -0,0 +1,83 @@
715+/*
716+ * Copyright 2014 Canonical Ltd.
717+ *
718+ * This program is free software; you can redistribute it and/or modify
719+ * it under the terms of the GNU Lesser General Public License as published by
720+ * the Free Software Foundation; version 3.
721+ *
722+ * This program is distributed in the hope that it will be useful,
723+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
724+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
725+ * GNU Lesser General Public License for more details.
726+ *
727+ * You should have received a copy of the GNU Lesser General Public License
728+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
729+ */
730+
731+#ifndef UCSERVICEPROPERTIES_H
732+#define UCSERVICEPROPERTIES_H
733+
734+#include <QtCore/QObject>
735+#include <QtQml/QQmlParserStatus>
736+
737+class UCServicePropertiesPrivate;
738+class UCServiceProperties : public QObject, public QQmlParserStatus
739+{
740+ Q_OBJECT
741+ Q_INTERFACES(QQmlParserStatus)
742+
743+ Q_PROPERTY(ServiceType type READ type WRITE setType NOTIFY typeChanged REVISION 1)
744+ Q_PROPERTY(QString service READ service WRITE setService NOTIFY serviceChanged REVISION 1)
745+ Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REVISION 1)
746+ Q_PROPERTY(QString serviceInterface READ interface WRITE setInterface NOTIFY serviceInterfaceChanged REVISION 1)
747+ Q_PROPERTY(QString adaptorInterface READ adaptor WRITE setAdaptor NOTIFY adaptorInterfaceChanged REVISION 1)
748+ Q_PROPERTY(QString error READ error NOTIFY errorChanged REVISION 1)
749+ Q_PROPERTY(Status status READ status NOTIFY statusChanged REVISION 1)
750+
751+ Q_ENUMS(ServiceType Status)
752+public:
753+ enum ServiceType {
754+ Undefined,
755+ System,
756+ Session
757+ };
758+ enum Status {
759+ Inactive,
760+ ConnectionError,
761+ Synchronizing,
762+ Active
763+ };
764+
765+ explicit UCServiceProperties(QObject *parent = 0);
766+ ~UCServiceProperties();
767+ void classBegin();
768+ void componentComplete();
769+
770+ ServiceType type() const;
771+ void setType(ServiceType type);
772+ QString service() const;
773+ void setService(const QString &value);
774+ QString path() const;
775+ void setPath(const QString &value);
776+ QString interface() const;
777+ void setInterface(const QString &value);
778+ QString adaptor() const;
779+ void setAdaptor(const QString &value);
780+ QString error();
781+ Status status() const;
782+
783+Q_SIGNALS:
784+ void typeChanged();
785+ void serviceChanged();
786+ void pathChanged();
787+ void serviceInterfaceChanged();
788+ void adaptorInterfaceChanged();
789+ void errorChanged();
790+ void statusChanged();
791+
792+private:
793+ UCServicePropertiesPrivate *d_ptr;
794+ Q_DECLARE_PRIVATE_D(d_ptr, UCServiceProperties)
795+};
796+
797+#endif // UCSERVICEPROPERTIES_H
798
799=== added file 'modules/Ubuntu/Components/plugin/ucserviceproperties_p.h'
800--- modules/Ubuntu/Components/plugin/ucserviceproperties_p.h 1970-01-01 00:00:00 +0000
801+++ modules/Ubuntu/Components/plugin/ucserviceproperties_p.h 2015-02-04 14:00:19 +0000
802@@ -0,0 +1,53 @@
803+/*
804+ * Copyright 2014 Canonical Ltd.
805+ *
806+ * This program is free software; you can redistribute it and/or modify
807+ * it under the terms of the GNU Lesser General Public License as published by
808+ * the Free Software Foundation; version 3.
809+ *
810+ * This program is distributed in the hope that it will be useful,
811+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
812+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
813+ * GNU Lesser General Public License for more details.
814+ *
815+ * You should have received a copy of the GNU Lesser General Public License
816+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
817+ */
818+
819+#ifndef UCSERVICEPROPERTIES_P_H
820+#define UCSERVICEPROPERTIES_P_H
821+
822+#include "ucserviceproperties.h"
823+#include <QtCore/QStringList>
824+
825+class UCServicePropertiesPrivate
826+{
827+public:
828+ UCServicePropertiesPrivate(UCServiceProperties *qq);
829+ virtual ~UCServicePropertiesPrivate();
830+
831+ static UCServicePropertiesPrivate *get(UCServiceProperties *service);
832+ void setError(const QString &msg);
833+ void setStatus(UCServiceProperties::Status status);
834+
835+ virtual bool init() = 0;
836+ virtual bool fetchPropertyValues() = 0;
837+ virtual bool readProperty(const QString &property) = 0;
838+ virtual bool testProperty(const QString &property, const QVariant &value) = 0;
839+
840+ // data
841+ UCServiceProperties *q_ptr;
842+ bool ready:1;
843+ UCServiceProperties::Status status;
844+ UCServiceProperties::ServiceType type;
845+ QString service;
846+ QString path;
847+ QString interface;
848+ QString adaptor;
849+ QString error;
850+ QStringList properties;
851+};
852+
853+UCServicePropertiesPrivate *createServicePropertiesAdapter(UCServiceProperties *owner);
854+
855+#endif // UCSERVICEPROPERTIES_P_H
856
857=== modified file 'tests/unit/runtest.sh'
858--- tests/unit/runtest.sh 2014-09-16 08:19:14 +0000
859+++ tests/unit/runtest.sh 2015-02-04 14:00:19 +0000
860@@ -22,18 +22,18 @@
861 _TESTFILE=$2
862 _MINIMAL=$3
863 _XML="../../test_$_TARGET_$_TESTFILE.xml"
864-_ARGS="-o $_XML,xunitxml -o -,txt"
865+_ARGS="-p -o -p $_XML,xunitxml -p -o -p -,txt"
866 set +e
867
868 function create_test_cmd {
869- _CMD="./$_TARGET"
870+ _CMD="dbus-test-runner --task ./$_TARGET -n $_TESTFILE -m 300"
871 if [ "$_MINIMAL" = "minimal" ]; then
872- _CMD="$_CMD -platform minimal"
873+ _CMD="$_CMD -p -platform -p minimal"
874 fi
875 if [ $_TARGET != $_TESTFILE ]; then
876- _CMD="$_CMD -input $_TESTFILE"
877+ _CMD="$_CMD -p -input -p $_TESTFILE"
878 fi
879- _CMD="$_CMD -maxwarnings 40"
880+ _CMD="$_CMD -p -maxwarnings -p 40"
881 }
882
883 function execute_test_cmd {
884
885=== added directory 'tests/unit_x11/tst_serviceproperties'
886=== added file 'tests/unit_x11/tst_serviceproperties/IncomingCallVibrateWatcher.qml'
887--- tests/unit_x11/tst_serviceproperties/IncomingCallVibrateWatcher.qml 1970-01-01 00:00:00 +0000
888+++ tests/unit_x11/tst_serviceproperties/IncomingCallVibrateWatcher.qml 2015-02-04 14:00:19 +0000
889@@ -0,0 +1,31 @@
890+/*
891+ * Copyright 2014 Canonical Ltd.
892+ *
893+ * This program is free software; you can redistribute it and/or modify
894+ * it under the terms of the GNU Lesser General Public License as published by
895+ * the Free Software Foundation; version 3.
896+ *
897+ * This program is distributed in the hope that it will be useful,
898+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
899+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
900+ * GNU Lesser General Public License for more details.
901+ *
902+ * You should have received a copy of the GNU Lesser General Public License
903+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
904+ */
905+
906+import QtQuick 2.3
907+import Ubuntu.Components 1.1
908+
909+Item {
910+ property alias service: service
911+ ServiceProperties {
912+ id: service
913+ service: "org.freedesktop.Accounts"
914+ serviceInterface: "org.freedesktop.Accounts"
915+ path: "/org/freedesktop/Accounts"
916+ adaptorInterface: "com.ubuntu.touch.AccountsService.Sound"
917+
918+ property bool incomingCallVibrate: true
919+ }
920+}
921
922=== added file 'tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher.qml'
923--- tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher.qml 1970-01-01 00:00:00 +0000
924+++ tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher.qml 2015-02-04 14:00:19 +0000
925@@ -0,0 +1,31 @@
926+/*
927+ * Copyright 2014 Canonical Ltd.
928+ *
929+ * This program is free software; you can redistribute it and/or modify
930+ * it under the terms of the GNU Lesser General Public License as published by
931+ * the Free Software Foundation; version 3.
932+ *
933+ * This program is distributed in the hope that it will be useful,
934+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
935+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
936+ * GNU Lesser General Public License for more details.
937+ *
938+ * You should have received a copy of the GNU Lesser General Public License
939+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
940+ */
941+
942+import QtQuick 2.3
943+import Ubuntu.Components 1.1
944+
945+Item {
946+ property alias service: service
947+ ServiceProperties {
948+ id: service
949+ service: "org.freedesktop.Accounts"
950+ serviceInterface: "org.freedesktop.Accounts"
951+ path: "/org/freedesktop/Accounts"
952+ adaptorInterface: "com.ubuntu.touch.AccountsService.Sound"
953+
954+ property bool thisIsAnInvalidPropertyToWatch: true
955+ }
956+}
957
958=== added file 'tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher2.qml'
959--- tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher2.qml 1970-01-01 00:00:00 +0000
960+++ tests/unit_x11/tst_serviceproperties/InvalidPropertyWatcher2.qml 2015-02-04 14:00:19 +0000
961@@ -0,0 +1,32 @@
962+/*
963+ * Copyright 2014 Canonical Ltd.
964+ *
965+ * This program is free software; you can redistribute it and/or modify
966+ * it under the terms of the GNU Lesser General Public License as published by
967+ * the Free Software Foundation; version 3.
968+ *
969+ * This program is distributed in the hope that it will be useful,
970+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
971+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
972+ * GNU Lesser General Public License for more details.
973+ *
974+ * You should have received a copy of the GNU Lesser General Public License
975+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
976+ */
977+
978+import QtQuick 2.3
979+import Ubuntu.Components 1.1
980+
981+Item {
982+ property alias service: service
983+ ServiceProperties {
984+ id: service
985+ service: "org.freedesktop.Accounts"
986+ serviceInterface: "org.freedesktop.Accounts"
987+ path: "/org/freedesktop/Accounts"
988+ adaptorInterface: "com.ubuntu.touch.AccountsService.Sound"
989+
990+ property bool thisIsAnInvalidPropertyToWatch: true
991+ property bool incomingCallVibrate: true
992+ }
993+}
994
995=== added file 'tests/unit_x11/tst_serviceproperties/tst_serviceproperties.cpp'
996--- tests/unit_x11/tst_serviceproperties/tst_serviceproperties.cpp 1970-01-01 00:00:00 +0000
997+++ tests/unit_x11/tst_serviceproperties/tst_serviceproperties.cpp 2015-02-04 14:00:19 +0000
998@@ -0,0 +1,146 @@
999+/*
1000+ * Copyright 2014 Canonical Ltd.
1001+ *
1002+ * This program is free software; you can redistribute it and/or modify
1003+ * it under the terms of the GNU Lesser General Public License as published by
1004+ * the Free Software Foundation; version 3.
1005+ *
1006+ * This program is distributed in the hope that it will be useful,
1007+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1008+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1009+ * GNU Lesser General Public License for more details.
1010+ *
1011+ * You should have received a copy of the GNU Lesser General Public License
1012+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1013+ */
1014+
1015+#include "uctestcase.h"
1016+#include "ucserviceproperties.h"
1017+#include "ucserviceproperties_p.h"
1018+#include <QtCore/QString>
1019+#include <QtCore/QDebug>
1020+#include <QtTest/QTest>
1021+#include <QtTest/QSignalSpy>
1022+
1023+class tst_ServiceProperties : public QObject
1024+{
1025+ Q_OBJECT
1026+
1027+public:
1028+ tst_ServiceProperties() {}
1029+
1030+private:
1031+
1032+ QString error;
1033+
1034+ // FIXME use UbuntuTestCase::ignoreWaring in Vivid
1035+ void ignoreWarning(const QString& fileName, uint line, uint column, const QString& message, uint occurences=1)
1036+ {
1037+ for (uint i = 0; i < occurences; i++) {
1038+ QString url(QUrl::fromLocalFile(QFileInfo(fileName).absoluteFilePath()).toEncoded());
1039+ QString warning(QString("%1:%2:%3: %4").arg(url).arg(line).arg(column).arg(message));
1040+ QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
1041+ }
1042+ }
1043+
1044+
1045+private Q_SLOTS:
1046+
1047+ void initTestCase()
1048+ {
1049+ // check if the connection is possible, otherwise we must skip all tests
1050+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("IncomingCallVibrateWatcher.qml"));
1051+ UCServiceProperties *watcher = static_cast<UCServiceProperties*>(test->rootObject()->property("service").value<QObject*>());
1052+ QVERIFY(watcher);
1053+ if (watcher->status() == UCServiceProperties::Synchronizing ||
1054+ watcher->status() == UCServiceProperties::Inactive) {
1055+ QSignalSpy wait(watcher, SIGNAL(statusChanged()));
1056+ wait.wait();
1057+ }
1058+ if (watcher->status() == UCServiceProperties::ConnectionError) {
1059+ error = "Skip test: " + watcher->error();
1060+ }
1061+ }
1062+
1063+ void test_change_property()
1064+ {
1065+ if (!error.isEmpty()) {
1066+ QSKIP(qPrintable(error));
1067+ }
1068+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("IncomingCallVibrateWatcher.qml"));
1069+ UCServiceProperties *watcher = static_cast<UCServiceProperties*>(test->rootObject()->property("service").value<QObject*>());
1070+ QVERIFY(watcher);
1071+
1072+ bool backup = watcher->property("incomingCallVibrate").toBool();
1073+ UCServicePropertiesPrivate *pWatcher = UCServicePropertiesPrivate::get(watcher);
1074+ QSignalSpy spy(watcher, SIGNAL(incomingCallVibrateChanged()));
1075+ pWatcher->testProperty("IncomingCallVibrate", !backup);
1076+ spy.wait(400);
1077+ QCOMPARE(spy.count(), 1);
1078+ QCOMPARE(watcher->property("incomingCallVibrate").toBool(), !backup);
1079+
1080+ // restore value
1081+ spy.clear();
1082+ pWatcher->testProperty("IncomingCallVibrate", backup);
1083+ spy.wait(400);
1084+ }
1085+
1086+ void test_invalid_property()
1087+ {
1088+ if (!error.isEmpty()) {
1089+ QSKIP(qPrintable(error));
1090+ }
1091+ ignoreWarning("InvalidPropertyWatcher.qml", 22, 5, "QML ServiceProperties: No such property 'ThisIsAnInvalidPropertyToWatch'");
1092+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("InvalidPropertyWatcher.qml"));
1093+ UCServiceProperties *watcher = static_cast<UCServiceProperties*>(test->rootObject()->property("service").value<QObject*>());
1094+ QVERIFY(watcher);
1095+ // error should not be set
1096+ QCOMPARE(watcher->property("error").toString(), QString());
1097+ }
1098+
1099+ void test_one_valid_one_invalid_property()
1100+ {
1101+ if (!error.isEmpty()) {
1102+ QSKIP(qPrintable(error));
1103+ }
1104+ ignoreWarning("InvalidPropertyWatcher2.qml", 22, 5, "QML ServiceProperties: No such property 'ThisIsAnInvalidPropertyToWatch'");
1105+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("InvalidPropertyWatcher2.qml"));
1106+ UCServiceProperties *watcher = static_cast<UCServiceProperties*>(test->rootObject()->property("service").value<QObject*>());
1107+ QVERIFY(watcher);
1108+ // error should not be set
1109+ QCOMPARE(watcher->property("error").toString(), QString());
1110+ }
1111+
1112+ void test_change_connection_props_data()
1113+ {
1114+ QTest::addColumn<QString>("property");
1115+ QTest::addColumn<QString>("value");
1116+
1117+ QTest::newRow("Changing servcie") << "service" << "anything.else";
1118+ QTest::newRow("Changing interface") << "serviceInterface" << "anything.else";
1119+ QTest::newRow("Changing adaptor") << "adaptorInterface" << "anything.else";
1120+ }
1121+ void test_change_connection_props()
1122+ {
1123+ QFETCH(QString, property);
1124+ QFETCH(QString, value);
1125+
1126+ if (!error.isEmpty()) {
1127+ QSKIP(qPrintable(error));
1128+ }
1129+ ignoreWarning("IncomingCallVibrateWatcher.qml", 22, 5, "QML ServiceProperties: Changing connection parameters forbidden.");
1130+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("IncomingCallVibrateWatcher.qml"));
1131+ UCServiceProperties *watcher = static_cast<UCServiceProperties*>(test->rootObject()->property("service").value<QObject*>());
1132+ QVERIFY(watcher);
1133+
1134+ // try to change the property
1135+ watcher->setProperty(property.toLocal8Bit().constData(), value);
1136+ // no error should be reported
1137+ QCOMPARE(watcher->property("error").toString(), QString());
1138+ }
1139+
1140+};
1141+
1142+QTEST_MAIN(tst_ServiceProperties)
1143+
1144+#include "tst_serviceproperties.moc"
1145
1146=== added file 'tests/unit_x11/tst_serviceproperties/tst_serviceproperties.pro'
1147--- tests/unit_x11/tst_serviceproperties/tst_serviceproperties.pro 1970-01-01 00:00:00 +0000
1148+++ tests/unit_x11/tst_serviceproperties/tst_serviceproperties.pro 2015-02-04 14:00:19 +0000
1149@@ -0,0 +1,8 @@
1150+include(../test-include.pri)
1151+SOURCES += \
1152+ tst_serviceproperties.cpp
1153+
1154+OTHER_FILES += \
1155+ IncomingCallVibrateWatcher.qml \
1156+ InvalidPropertyWatcher.qml \
1157+ InvalidPropertyWatcher2.qml
1158
1159=== modified file 'tests/unit_x11/unit_x11.pro'
1160--- tests/unit_x11/unit_x11.pro 2014-08-13 11:21:04 +0000
1161+++ tests/unit_x11/unit_x11.pro 2015-02-04 14:00:19 +0000
1162@@ -11,4 +11,5 @@
1163 tst_orientation \
1164 tst_layouts \
1165 tst_mousefilters \
1166- tst_animator
1167+ tst_animator \
1168+ tst_serviceproperties

Subscribers

People subscribed via source and target branches

to all changes: