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

Proposed by Zsombor Egri
Status: Merged
Approved by: Cris Dywan
Approved revision: 1354
Merged at revision: 1360
Proposed branch: lp:~zsombi/ubuntu-ui-toolkit/serviceproperties-component
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 1073 lines (+989/-5)
12 files modified
debian/control (+2/-0)
modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.cpp (+226/-0)
modules/Ubuntu/Components/plugin/adapters/dbuspropertywatcher_p.h (+55/-0)
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)
To merge this branch: bzr merge lp:~zsombi/ubuntu-ui-toolkit/serviceproperties-component
Reviewer Review Type Date Requested Status
Cris Dywan Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+243496@code.launchpad.net

Commit message

ServiceProperties component to Ubuntu.Components 1.1.

To post a comment you must log in.
Revision history for this message
Cris Dywan (kalikiana) wrote :

We need a test case for when accountservice is not available - if we don't want a hard dependency it can't be causing errors at runtime.

995 + QVERIFY(watcher->property("error").toString().isEmpty());
1009 + QVERIFY(watcher->property("error").toString().isEmpty());
1039 + QCOMPARE(errorSpy.count(), 0);

Please change all 3 cases where error is checked to a COMPARE so that the error becomes visible in the log.

785 - _CMD="./$_TARGET"
786 + _CMD="dbus-test-runner --task ./$_TARGET -n $_TESTFILE -m 30"

This must be 300 otherwise some tests time out.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1354. By Zsombor Egri

review comments applied

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Cris Dywan (kalikiana) wrote :

It passed!

review: Approve

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

Subscribers

People subscribed via source and target branches