on xenial help sign in
Make a few DBus calls asynchronous, for a smoother UX.

The calls I've changed here are neither hugely important nor frequent calls. But every little bit helps. - Made a few sync calls async. - Made a few sync calls more obviously sync. We had several instances of asyncCall()'s return value -- a QDBusPendingReply -- being assigned to a QDBusReply variable, which makes it wait for the call to finish. In cases where it didn't make sense to rewrite logic to be async, I've merely changed the method to be call() instead of asyncCall() for clarity. - I removed the API in our AccountsService plugin for even making sync calls. This won't stop future developers from from writing sync code, but might give them pause. - I removed some test mocks for our AccountsService plugin interface and changed those tests to use our actual AS plugin against a mock AS server. In truth, this change was because I had a devil of a time crafting a fake reply that included a custom complex type. But it's a good change anyway. Exercises more of our code in tests and reduces duplicated interfaces. Approved by: Albert Astals Cid

commit941e4b775e22c0bcc1ef0fd81eb18f5d255f9f0f authorMichael Terry <michael.terry@canonical.com> dateThu 12 Nov 2015, 20:39 (11 years ago) parent78bdee59ef54b3
19 files changed+237−314
plugins/AccountsService/AccountsService.cpp+3−3 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−22 plugins/AccountsService/AccountsServiceDBusAdaptor.h−12 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+19−4 plugins/Greeter/Unity/Launcher/launchermodelas.h+2 plugins/Unity/Session/dbusunitysessionservice.cpp+64−36 tests/plugins/AccountsService/AccountsServer.cpp+11−5 tests/plugins/AccountsService/AccountsServer.h+1−6 tests/plugins/AccountsService/PropertiesServer.cpp+9 tests/plugins/AccountsService/client.cpp+6−8 tests/plugins/AccountsService/server.cpp+1−7 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cpp−48 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.h−53 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+8−2 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+81−16 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cpp−42 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.h−47 tests/plugins/Unity/Launcher/CMakeLists.txt+8−2 tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−1
plugins/AccountsService/AccountsService.cpp+3−3
@@ -74,7 +74,7 @@ void AccountsService::setDemoEdges(bool demoEdges)
7474{
7575 if (m_demoEdges != demoEdges) {
7676 m_demoEdges = demoEdges;
77− m_service->setUserProperty(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
77+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
7878
7979 Q_EMIT demoEdgesChanged();
8080 }
@@ -114,7 +114,7 @@ void AccountsService::setHereEnabled(bool enabled)
114114{
115115 if (m_hereEnabled != enabled) {
116116 m_hereEnabled = enabled;
117− m_service->setUserProperty(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
117+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
118118
119119 Q_EMIT hereEnabledChanged();
120120 }
@@ -403,7 +403,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
403403{
404404 if (m_failedLogins != failedLogins) {
405405 m_failedLogins = failedLogins;
406− m_service->setUserProperty(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
406+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
407407
408408 Q_EMIT failedLoginsChanged();
409409 }
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−22
@@ -37,18 +37,6 @@ AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
3737 connection, this);
3838}
3939
40−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
41−{
42− QDBusInterface *iface = getUserInterface(user);
43− if (iface != nullptr && iface->isValid()) {
44− QDBusReply<QVariant> answer = iface->call(QStringLiteral("Get"), interface, property);
45− if (answer.isValid()) {
46− return answer;
47− }
48− }
49− return QVariant();
50−}
51−
5240QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
5341{
5442 QDBusInterface *iface = getUserInterface(user);
@@ -58,15 +46,6 @@ QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(con
5846 return QDBusPendingReply<QVariant>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
5947}
6048
61−void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
62−{
63− QDBusInterface *iface = getUserInterface(user);
64− if (iface != nullptr && iface->isValid()) {
65− // The value needs to be carefully wrapped
66− iface->call(QStringLiteral("Set"), interface, property, QVariant::fromValue(QDBusVariant(value)));
67− }
68−}
69−
7049QDBusPendingCall AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
7150{
7251 QDBusInterface *iface = getUserInterface(user);
@@ -116,7 +95,7 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
11695{
11796 QDBusInterface *iface = m_users.value(user);
11897 if (iface == nullptr && m_accountsManager->isValid()) {
119− QDBusReply<QDBusObjectPath> answer = m_accountsManager->asyncCall(QStringLiteral("FindUserByName"), user);
98+ QDBusReply<QDBusObjectPath> answer = m_accountsManager->call(QStringLiteral("FindUserByName"), user);
12099 if (answer.isValid()) {
121100 const QString path = answer.value().path();
122101
plugins/AccountsService/AccountsServiceDBusAdaptor.h−12
@@ -35,19 +35,7 @@ public:
3535 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
3636 ~AccountsServiceDBusAdaptor() = default;
3737
38− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
3938 Q_INVOKABLE QDBusPendingReply<QVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
40−
41− template <typename T>
42− inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
43− const QVariant variant = getUserProperty(user, interface, property);
44− if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
45− return qdbus_cast<T>(variant.value<QDBusArgument>());
46− }
47− return T();
48− }
49−
50− Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
5139 Q_INVOKABLE QDBusPendingCall setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
5240
5341Q_SIGNALS:
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+19−4
@@ -194,15 +194,30 @@ int LauncherModel::findApplication(const QString &appId)
194194 return -1;
195195}
196196
197−typedef QList<QVariantMap> VariantMapList;
198197void LauncherModel::refresh()
199198{
200− QList<QVariantMap> items;
199+ if (!m_accounts || m_user.isEmpty()) {
200+ refreshWithItems(QList<QVariantMap>());
201+ } else {
202+ QDBusPendingCall pendingCall = m_accounts->getUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("LauncherItems"));
203+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
204+ connect(watcher, &QDBusPendingCallWatcher::finished,
205+ this, [this](QDBusPendingCallWatcher* watcher) {
206+
207+ QDBusPendingReply<QVariant> reply = *watcher;
208+ watcher->deleteLater();
209+ if (reply.isError()) {
210+ qWarning() << "Failed to refresh LauncherItems" << reply.error().message();
211+ return;
212+ }
201213
202− if (m_accounts && !m_user.isEmpty()) {
203− items = m_accounts->getUserProperty<VariantMapList>(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("LauncherItems"));
214+ refreshWithItems(qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>()));
215+ });
204216 }
217+}
205218
219+void LauncherModel::refreshWithItems(const QList<QVariantMap> &items)
220+{
206221 // First walk through all the existing items and see if we need to remove something
207222 QList<LauncherItem*> toBeRemoved;
208223
plugins/Greeter/Unity/Launcher/launchermodelas.h+2
@@ -65,6 +65,8 @@ private Q_SLOTS:
6565 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
6666
6767private:
68+ void refreshWithItems(const QList<QVariantMap> &items);
69+
6870 QString m_user;
6971 QList<LauncherItem*> m_list;
7072 AccountsServiceDBusAdaptor *m_accounts;
plugins/Unity/Session/dbusunitysessionservice.cpp+64−36
@@ -61,7 +61,7 @@ public:
6161 QStringLiteral("GetSessionByPID"));
6262 msg << (quint32) getpid();
6363
64− QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
64+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
6565 if (reply.isValid()) {
6666 logindSessionPath = reply.value().path();
6767
@@ -92,20 +92,28 @@ public:
9292 msg << "Unity"; // who
9393 msg << "Unity8 handles power events"; // why
9494 msg << "block"; // mode
95− QDBusReply<QDBusUnixFileDescriptor> desc = QDBusConnection::systemBus().asyncCall(msg);
9695
97− if (desc.isValid()) {
98− m_systemdInhibitFd = desc.value();
99− } else {
100− qWarning() << "failed to inhibit systemd powersave handling";
101− }
96+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
97+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
98+ connect(watcher, &QDBusPendingCallWatcher::finished,
99+ this, [this](QDBusPendingCallWatcher* watcher) {
100+
101+ QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
102+ watcher->deleteLater();
103+ if (reply.isError()) {
104+ qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
105+ return;
106+ }
107+
108+ m_systemdInhibitFd = reply.value();
109+ });
102110 }
103111
104112
105113 bool checkLogin1Call(const QString &method) const
106114 {
107115 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
108− QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
116+ QDBusReply<QString> reply = QDBusConnection::systemBus().call(msg);
109117 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
110118 }
111119
@@ -119,6 +127,21 @@ public:
119127 QDBusConnection::systemBus().asyncCall(msg);
120128 }
121129
130+ void setActive(bool active)
131+ {
132+ isSessionActive = active;
133+
134+ Q_EMIT screensaverActiveChanged(!isSessionActive);
135+
136+ if (isSessionActive) {
137+ screensaverActiveTimer.invalidate();
138+ setIdleHint(false);
139+ } else {
140+ screensaverActiveTimer.start();
141+ setIdleHint(true);
142+ }
143+ }
144+
122145 void checkActive()
123146 {
124147 if (logindSessionPath.isEmpty()) {
@@ -133,12 +156,20 @@ public:
133156 msg << LOGIN1_SESSION_IFACE;
134157 msg << ACTIVE_KEY;
135158
136− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
137− if (reply.isValid()) {
138− isSessionActive = reply.value().toBool();
139− } else {
140− qWarning() << "Failed to get Active property" << reply.error().message();
141− }
159+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
160+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
161+ connect(watcher, &QDBusPendingCallWatcher::finished,
162+ this, [this](QDBusPendingCallWatcher* watcher) {
163+
164+ QDBusPendingReply<QVariant> reply = *watcher;
165+ watcher->deleteLater();
166+ if (reply.isError()) {
167+ qWarning() << "Failed to get Active property" << reply.error().message();
168+ return;
169+ }
170+
171+ setActive(reply.value().toBool());
172+ });
142173 }
143174
144175 quint32 screensaverActiveTime() const
@@ -159,7 +190,7 @@ public:
159190 msg << LOGIN1_SESSION_IFACE;
160191 msg << IDLE_SINCE_KEY;
161192
162− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
193+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
163194 if (reply.isValid()) {
164195 return reply.value().value<quint64>();
165196 } else {
@@ -184,22 +215,10 @@ private Q_SLOTS:
184215 {
185216 Q_UNUSED(iface)
186217
187− if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
188− if (changedProps.value(ACTIVE_KEY).isValid()) {
189− isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
190− } else {
191− checkActive();
192− }
193−
194− Q_EMIT screensaverActiveChanged(!isSessionActive);
195−
196− if (isSessionActive) {
197− screensaverActiveTimer.invalidate();
198− setIdleHint(false);
199− } else {
200− screensaverActiveTimer.start();
201− setIdleHint(true);
202− }
218+ if (changedProps.contains(ACTIVE_KEY)) {
219+ setActive(changedProps.value(ACTIVE_KEY).toBool());
220+ } else if (invalidatedProps.contains(ACTIVE_KEY)) {
221+ checkActive();
203222 }
204223 }
205224
@@ -325,13 +344,22 @@ void DBusUnitySessionService::Lock()
325344 sessionPath,
326345 QStringLiteral("org.freedesktop.DisplayManager.Session"),
327346 QStringLiteral("Lock"));
328− QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
329− if (!reply.isValid()) {
330− qWarning() << "Lock call failed" << reply.error().message();
331− } else {
347+
348+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
349+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
350+ connect(watcher, &QDBusPendingCallWatcher::finished,
351+ this, [this](QDBusPendingCallWatcher* watcher) {
352+
353+ QDBusPendingReply<QVariant> reply = *watcher;
354+ watcher->deleteLater();
355+ if (reply.isError()) {
356+ qWarning() << "Lock call failed" << reply.error().message();
357+ return;
358+ }
359+
332360 // emit Locked when the call succeeds
333361 Q_EMIT Locked();
334− }
362+ });
335363}
336364
337365bool DBusUnitySessionService::IsLocked() const
tests/plugins/AccountsService/AccountsServer.cpp+11−5
@@ -18,13 +18,14 @@
1818 */
1919
2020#include "AccountsServer.h"
21+#include "AccountsUserAdaptor.h"
22+#include "PropertiesAdaptor.h"
2123#include "PropertiesServer.h"
2224#include <QDBusConnection>
2325#include <QDebug>
2426
25−AccountsServer::AccountsServer(PropertiesServer* propServer, QObject *parent)
27+AccountsServer::AccountsServer(QObject *parent)
2628 : QObject(parent)
27− , m_propServer(propServer)
2829{
2930}
3031
@@ -36,16 +37,21 @@ QDBusObjectPath AccountsServer::FindUserByName(const QString &user)
3637bool AccountsServer::AddUser(const QString &user)
3738{
3839 QString path(QString("/%1").arg(user));
39− if (QDBusConnection::sessionBus().objectRegisteredAt(path) == m_propServer)
40+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) != nullptr)
4041 return true;
41− return QDBusConnection::sessionBus().registerObject(path, m_propServer);
42+
43+ auto props = new PropertiesServer(this);
44+ new PropertiesAdaptor(props);
45+ new AccountsUserAdaptor(props);
46+ return QDBusConnection::sessionBus().registerObject(path, props);
4247}
4348
4449bool AccountsServer::RemoveUser(const QString &user)
4550{
4651 QString path(QString("/%1").arg(user));
47− if (QDBusConnection::sessionBus().objectRegisteredAt(path) != m_propServer)
52+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) == nullptr)
4853 return false;
54+
4955 QDBusConnection::sessionBus().unregisterObject(path);
5056 return true;
5157}
tests/plugins/AccountsService/AccountsServer.h+1−6
@@ -25,14 +25,12 @@
2525#include <QObject>
2626#include <QString>
2727
28−class PropertiesServer;
29−
3028class AccountsServer: public QObject, protected QDBusContext
3129{
3230 Q_OBJECT
3331
3432public:
35− explicit AccountsServer(PropertiesServer *propServer, QObject *parent = 0);
33+ explicit AccountsServer(QObject *parent = 0);
3634
3735public Q_SLOTS:
3836
@@ -41,9 +39,6 @@ public Q_SLOTS:
4139 // mock only.
4240 bool AddUser(const QString &user);
4341 bool RemoveUser(const QString &user);
44−
45−private:
46− PropertiesServer* m_propServer;
4742};
4843
4944#endif
tests/plugins/AccountsService/PropertiesServer.cpp+9
@@ -23,10 +23,12 @@
2323// Qt
2424#include <QDebug>
2525#include <QDBusMessage>
26+#include <QDBusMetaType>
2627
2728PropertiesServer::PropertiesServer(QObject *parent)
2829 : QObject(parent)
2930{
31+ qDBusRegisterMetaType<QList<QVariantMap>>();
3032 Reset();
3133}
3234
@@ -47,6 +49,12 @@ void PropertiesServer::Set(const QString &interface, const QString &property, co
4749 if (m_properties[interface].contains(property)) {
4850 QVariant& oldValue = m_properties[interface][property];
4951 if (oldValue != newValue) {
52+ // complex types have an extra layer of wrapping via QDBusArgument
53+ if (interface == QStringLiteral("com.canonical.unity.AccountsService") &&
54+ property == QStringLiteral("LauncherItems")) {
55+ newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>()));
56+ }
57+
5058 oldValue = newValue;
5159
5260 // Special case for Background file.
@@ -71,6 +79,7 @@ void PropertiesServer::Set(const QString &interface, const QString &property, co
7179void PropertiesServer::Reset()
7280{
7381 m_properties["com.canonical.unity.AccountsService"]["demo-edges"] = false;
82+ m_properties["com.canonical.unity.AccountsService"]["LauncherItems"] = QVariant::fromValue(QList<QVariantMap>());
7483 m_properties["com.canonical.unity.AccountsService.Private"]["FailedLogins"] = 0;
7584 m_properties["com.ubuntu.touch.AccountsService.SecurityPrivacy"]["StatsWelcomeScreen"] = true;
7685 m_properties["com.ubuntu.AccountsService.SecurityPrivacy"]["EnableLauncherWhileLocked"] = true;
tests/plugins/AccountsService/client.cpp+6−8
@@ -57,8 +57,6 @@ private Q_SLOTS:
5757 m_userInterface = new QDBusInterface("org.freedesktop.Accounts",
5858 QString("/%1").arg(QTest::currentTestFunction()),
5959 "org.freedesktop.DBus.Properties", QDBusConnection::sessionBus(), this);
60− QDBusReply<void> resetReply = m_userInterface->call("Reset");
61− QVERIFY(resetReply.isValid());
6260
6361 QVERIFY(QObject::connect(m_userInterface, SIGNAL(PropertiesChanged(QString, QVariantMap, QStringList)),
6462 this, SIGNAL(propertiesChanged(QString, QVariantMap, QStringList))));
@@ -79,17 +77,17 @@ private Q_SLOTS:
7977 {
8078 // Test various invalid calls
8179 AccountsServiceDBusAdaptor session;
82− QCOMPARE(session.getUserProperty("NOPE", "com.canonical.unity.AccountsService", "demo-edges"), QVariant());
83− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE"), QVariant());
80+ QCOMPARE(session.getUserPropertyAsync("NOPE", "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant());
81+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE").value(), QVariant());
8482 }
8583
8684 void testGetSetServiceDBusAdaptor()
8785 {
8886 AccountsServiceDBusAdaptor session;
89− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true));
90− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(true));
91− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false));
92− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(false));
87+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true)).waitForFinished();
88+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(true));
89+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false)).waitForFinished();
90+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(false));
9391 }
9492
9593 void testGetSetService()
tests/plugins/AccountsService/server.cpp+1−7
@@ -19,11 +19,9 @@
1919
2020#include "AccountsAdaptor.h"
2121#include "AccountsServer.h"
22−#include "PropertiesAdaptor.h"
2322#include "PropertiesServer.h"
2423#include "SecurityPrivacyAdaptor.h"
2524#include "LocationAdaptor.h"
26−#include "AccountsUserAdaptor.h"
2725#include "AccountsPrivateAdaptor.h"
2826#include <QCoreApplication>
2927
@@ -31,11 +29,7 @@ int main(int argc, char *argv[])
3129{
3230 QCoreApplication a(argc, argv);
3331
34− auto props = new PropertiesServer(&a);
35− new PropertiesAdaptor(props);
36− new AccountsUserAdaptor(props);
37−
38− auto accounts = new AccountsServer(props, &a);
32+ auto accounts = new AccountsServer(&a);
3933 new AccountsAdaptor(accounts);
4034 new SecurityPrivacyAdaptor(accounts);
4135 new LocationAdaptor(accounts);
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cppdeleted−48
@@ -1,48 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "AccountsServiceDBusAdaptor.h"
18−#include <QDebug>
19−#include <QDBusArgument>
20−#include <QDBusMessage>
21−#include <QDBusMetaType>
22−
23−AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
24− : QObject(parent)
25−{
26− qDBusRegisterMetaType<QList<QVariantMap>>();
27−}
28−
29−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
30−{
31− Q_UNUSED(interface)
32− Q_UNUSED(property) // We only fake one property here (LauncherItems)
33−
34− QDBusMessage msg;
35− QVariant v = QVariant::fromValue(mockProperties.value(user));
36− QDBusVariant dv(v);
37− QVariant packed = QVariant::fromValue(dv);
38− msg << packed;
39−
40− return packed.value<QDBusArgument>().asVariant();
41−}
42−
43−void AccountsServiceDBusAdaptor::simulatePropertyChange(const QString &user, const QString &property, const QVariant &value)
44−{
45− Q_ASSERT(property == "LauncherItems");
46− mockProperties[user] = value.value<QList<QVariantMap>>();
47− Q_EMIT propertiesChanged(user, "com.canonical.unity.AccountsService", QStringList() << property);
48−}
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.hdeleted−53
@@ -1,53 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18−#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19−
20−#include <QMap>
21−#include <QObject>
22−#include <QString>
23−#include <QHash>
24−#include <QDBusArgument>
25−
26−extern QHash<QString, QList<QVariantMap>> mockProperties;
27−
28−class AccountsServiceDBusAdaptor: public QObject
29−{
30− Q_OBJECT
31−
32−public:
33− explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34−
35− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
36− template <typename T>
37− inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
38− Q_UNUSED(interface)
39− Q_ASSERT(property == "LauncherItems");
40− T ret = mockProperties[user];
41− return ret;
42− }
43−
44−Q_SIGNALS:
45− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
46−
47−private:
48− void simulatePropertyChange(const QString &user, const QString &property, const QVariant &value);
49−
50− friend class LauncherModelASTest;
51−};
52−
53−#endif
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+8−2
@@ -3,6 +3,7 @@ pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
55 ${CMAKE_CURRENT_BINARY_DIR}
6+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
67 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher
78 ${libunity8-private_SOURCE_DIR}
89 )
@@ -14,7 +15,7 @@ add_definitions(-DLAUNCHER_TESTING)
1415### LauncherModelASTest
1516add_executable(launchermodelastestExec
1617 launchermodelastest.cpp
17− AccountsServiceDBusAdaptor.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
1819 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launchermodelas.cpp
1920 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launcheritem.cpp
2021 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistmodel.cpp
@@ -29,10 +30,15 @@ target_link_libraries(launchermodelastestExec
2930 unity8-private
3031 ${GSETTINGS_QT_LDFLAGS}
3132 )
33+add_dependencies(launchermodelastestExec mock-server)
3234qt5_use_modules(launchermodelastestExec Test Core DBus Gui)
3335add_unity8_unittest(LauncherModelAS dbus-test-runner
3436 ARG_PREFIX "--parameter"
35− ARGS --task $<TARGET_FILE:launchermodelastestExec>
37+ ARGS
38+ --task $<TARGET_FILE:mock-server>
39+ --ignore-return
40+ --task $<TARGET_FILE:launchermodelastestExec>
41+ --wait-for org.freedesktop.Accounts
3642)
3743
3844# copy .desktop files into build directory for shadow builds
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+81−16
@@ -22,9 +22,12 @@
2222#include "launchermodelas.h"
2323#include "AccountsServiceDBusAdaptor.h"
2424
25+#include <QDBusInterface>
26+#include <QDBusMetaType>
27+#include <QDBusReply>
28+#include <QSignalSpy>
2529#include <QtTest>
2630
27−QHash<QString, QList<QVariantMap>> mockProperties;
2831
2932class LauncherModelASTest : public QObject
3033{
@@ -32,9 +35,15 @@ class LauncherModelASTest : public QObject
3235
3336private:
3437 LauncherModel *launcherModel;
38+ QStringList users;
39+ QHash<QString, QList<QVariantMap>> mockProperties;
3540
3641private Q_SLOTS:
3742
43+ void initTestCase() {
44+ qDBusRegisterMetaType<QList<QVariantMap>>();
45+ }
46+
3847 void init() {
3948 // Prepare some fake list
4049 QList<QVariantMap> list;
@@ -50,7 +59,63 @@ private Q_SLOTS:
5059 item["id"] = "appId2";
5160 item["name"] = "Item 2";
5261 list.append(item);
53− mockProperties["user1"] = list;
62+ setASLauncherItems("user1", list);
63+ }
64+
65+ void cleanup() {
66+ Q_FOREACH(QString user, users) {
67+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
68+ QStringLiteral("/org/freedesktop/Accounts"),
69+ QStringLiteral("org.freedesktop.Accounts"));
70+ QDBusReply<bool> removeReply = accountsInterface.call(QStringLiteral("RemoveUser"),
71+ user);
72+ QVERIFY(removeReply.isValid());
73+ QCOMPARE(removeReply.value(), true);
74+ }
75+
76+ users.clear();
77+ mockProperties.clear();
78+ }
79+
80+ void addASUser(const QString &user)
81+ {
82+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
83+ QStringLiteral("/org/freedesktop/Accounts"),
84+ QStringLiteral("org.freedesktop.Accounts"));
85+ QDBusReply<bool> addReply = accountsInterface.call(QStringLiteral("AddUser"),
86+ user);
87+ QVERIFY(addReply.isValid());
88+ QCOMPARE(addReply.value(), true);
89+
90+ users << user;
91+ mockProperties[user] = QList<QVariantMap>();
92+ }
93+
94+ void setASLauncherItems(const QString &user, const QList<QVariantMap> &items)
95+ {
96+ if (!users.contains(user)) {
97+ addASUser(user);
98+ }
99+
100+ QDBusInterface userInterface(QStringLiteral("org.freedesktop.Accounts"),
101+ QString("/%1").arg(user),
102+ QStringLiteral("org.freedesktop.DBus.Properties"));
103+
104+ QVariant wrapped = QVariant::fromValue(QDBusVariant(QVariant::fromValue(items)));
105+ QDBusReply<void> setReply = userInterface.call(QStringLiteral("Set"),
106+ QStringLiteral("com.canonical.unity.AccountsService"),
107+ QStringLiteral("LauncherItems"),
108+ wrapped);
109+ QVERIFY(setReply.isValid());
110+
111+ // Record what we set for comparison's sake
112+ mockProperties[user] = items;
113+ }
114+
115+ void setUser(LauncherModel *model, const QString &user) {
116+ QSignalSpy spy(model, &QAbstractItemModel::rowsInserted);
117+ model->setUser(user);
118+ QTRY_COMPARE(spy.count(), mockProperties[user].count());
54119 }
55120
56121 bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
@@ -81,7 +146,7 @@ private Q_SLOTS:
81146 // We didn't set a user yet. model should be empty
82147 QCOMPARE(model->rowCount(), 0);
83148
84− model->setUser("user1");
149+ setUser(model, "user1");
85150
86151 QCOMPARE(isInSync(model, mockProperties["user1"]), true);
87152 model->deleteLater();
@@ -102,7 +167,7 @@ private Q_SLOTS:
102167 QFETCH(bool, inSync);
103168
104169 LauncherModel* model = new LauncherModel(this);
105− model->setUser(modelUser);
170+ setUser(model, modelUser);
106171
107172 int oldCount = mockProperties[modelUser].count();
108173 QCOMPARE(model->rowCount(), oldCount);
@@ -117,17 +182,17 @@ private Q_SLOTS:
117182 newEntry.insert("countVisible", false);
118183 newEntry.insert("pinned", true);
119184 newList.append(newEntry);
120− model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
185+ setASLauncherItems(changedUser, newList);
121186
122− QCOMPARE(isInSync(model, mockProperties[modelUser]), true);
123− QCOMPARE(isInSync(model, mockProperties[changedUser]), inSync);
187+ QTRY_COMPARE(isInSync(model, mockProperties[modelUser]), true);
188+ QTRY_COMPARE(isInSync(model, mockProperties[changedUser]), inSync);
124189
125190 model->deleteLater();
126191 }
127192
128193 void testUpdateCount() {
129194 LauncherModel* model = new LauncherModel(this);
130− model->setUser("user1");
195+ setUser(model, "user1");
131196
132197 QCOMPARE(model->get(0)->countVisible(), false);
133198 QCOMPARE(model->get(0)->count(), 0);
@@ -137,34 +202,34 @@ private Q_SLOTS:
137202 entry["countVisible"] = true;
138203 entry["count"] = 55;
139204 newList[0] = entry;
140− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
205+ setASLauncherItems("user1", newList);
141206
142− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
207+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
143208
144209 model->deleteLater();
145210 }
146211
147212 void testOnlyPinned() {
148213 LauncherModel *model = new LauncherModel(this);
149− model->setUser("user1");
214+ setUser(model, "user1");
150215 model->setOnlyPinned(true);
151216
152− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
217+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
153218
154219 // Let's unpin one item
155220 QList<QVariantMap> newList = mockProperties["user1"];
156221 QVariantMap entry = newList.at(0);
157222 entry["pinned"] = false;
158223 newList[0] = entry;
159− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
160− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
224+ setASLauncherItems("user1", newList);
225+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
161226
162227 // Now toggle onlyPinned and make sure the model keeps up
163228 model->setOnlyPinned(false);
164− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
229+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
165230
166231 model->setOnlyPinned(true);
167− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
232+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
168233 }
169234};
170235
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cppdeleted−42
@@ -1,42 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "AccountsServiceDBusAdaptor.h"
18−#include <QDebug>
19−
20−AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
21− : QObject(parent)
22−{
23−}
24−
25−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
26−{
27− Q_UNUSED(user)
28− Q_UNUSED(interface)
29− return m_properties.value(property);
30−}
31−
32−void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
33−{
34− Q_UNUSED(user)
35− Q_UNUSED(interface)
36− m_properties[property] = value;
37−}
38−
39−void AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
40−{
41− setUserProperty(user, interface, property, value);
42−}
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.hdeleted−47
@@ -1,47 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18−#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19−
20−#include <QMap>
21−#include <QObject>
22−#include <QString>
23−#include <QHash>
24−
25−class AccountsServiceDBusAdaptor: public QObject
26−{
27− Q_OBJECT
28−
29−public:
30− explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
31−
32− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
33− Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
34− Q_INVOKABLE void setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
35−
36−Q_SIGNALS:
37− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
38− void maybeChanged(const QString &user); // Standard properties might have changed
39−
40−private:
41− void simulatePropertyChange(const QString &property, const QVariant &value);
42−
43−private:
44− QHash<QString, QVariant> m_properties;
45−};
46−
47−#endif
tests/plugins/Unity/Launcher/CMakeLists.txt+8−2
@@ -4,6 +4,7 @@ pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
66 ${CMAKE_CURRENT_BINARY_DIR}
7+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
78 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
89 ${GSETTINGS_QT_INCLUDE_DIRS}
910 ${libunity8-private_SOURCE_DIR}
@@ -17,7 +18,7 @@ add_definitions(-DLAUNCHER_TESTING)
1718add_executable(launchermodeltestExec
1819 launchermodeltest.cpp
1920 gsettings.cpp
20− AccountsServiceDBusAdaptor.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
2122 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
2223 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
2324 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
@@ -35,11 +36,16 @@ target_link_libraries(launchermodeltestExec
3536 unity8-private
3637 ${GSETTINGS_QT_LDFLAGS}
3738 )
39+add_dependencies(launchermodeltestExec mock-server)
3840qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
3941
4042add_unity8_unittest(LauncherModel dbus-test-runner
4143 ARG_PREFIX "--parameter"
42− ARGS --task $<TARGET_FILE:launchermodeltestExec>
44+ ARGS
45+ --task $<TARGET_FILE:mock-server>
46+ --ignore-return
47+ --task $<TARGET_FILE:launchermodeltestExec>
48+ --wait-for org.freedesktop.Accounts
4349)
4450
4551# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−1
@@ -31,6 +31,7 @@
3131#include <QtTest>
3232#include <QDBusInterface>
3333#include <QDBusReply>
34+#include <QDBusMetaType>
3435#include <QDomDocument>
3536
3637// This is a mock, specifically to test the LauncherModel
@@ -136,12 +137,17 @@ private:
136137
137138 QList<QVariantMap> getASConfig() {
138139 AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
139− return as->getUserProperty("", "", "LauncherItems").value<QList<QVariantMap>>();
140+ QDBusReply<QVariant> reply = as->getUserPropertyAsync(QString(qgetenv("USER")),
141+ "com.canonical.unity.AccountsService",
142+ "LauncherItems");
143+ return qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>());
140144 }
141145
142146private Q_SLOTS:
143147
144148 void initTestCase() {
149+ qDBusRegisterMetaType<QList<QVariantMap>>();
150+
145151 launcherModel = new LauncherModel(this);
146152 QCoreApplication::processEvents(); // to let the model register on DBus
147153 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
@@ -152,6 +158,14 @@ private Q_SLOTS:
152158
153159 // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
154160 void init() {
161+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
162+ QStringLiteral("/org/freedesktop/Accounts"),
163+ QStringLiteral("org.freedesktop.Accounts"));
164+ QDBusReply<bool> addReply = accountsInterface.call(QStringLiteral("AddUser"),
165+ QString(qgetenv("USER")));
166+ QVERIFY(addReply.isValid());
167+ QCOMPARE(addReply.value(), true);
168+
155169 appManager->addApplication(new MockApp("abs-icon"));
156170 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
157171
@@ -169,6 +183,14 @@ private Q_SLOTS:
169183 while (launcherModel->rowCount(QModelIndex()) > 0) {
170184 launcherModel->requestRemove(launcherModel->get(0)->appId());
171185 }
186+
187+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
188+ QStringLiteral("/org/freedesktop/Accounts"),
189+ QStringLiteral("org.freedesktop.Accounts"));
190+ QDBusReply<bool> removeReply = accountsInterface.call(QStringLiteral("RemoveUser"),
191+ QString(qgetenv("USER")));
192+ QVERIFY(removeReply.isValid());
193+ QCOMPARE(removeReply.value(), true);
172194 }
173195
174196 void testMove() {