on 16fa310 help sign in
unity8/tests/plugins/Unity/Launcher/launchermodeltest.cpp
740 lines 31 kB history raw
1/*
2 * Copyright 2013-2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17// unity-api
18#include <unity/shell/launcher/LauncherModelInterface.h>
19#include <unity/shell/application/ApplicationInfoInterface.h>
21#include "launcheritem.h"
22#include "launchermodel.h"
23#include "dbusinterface.h"
24#include "gsettings.h"
25#include "asadapter.h"
26#include "AccountsServiceDBusAdaptor.h"
28#include <QtTest>
29#include <QDBusInterface>
30#include <QDBusReply>
31#include <QDBusMetaType>
32#include <QDomDocument>
34#include <glib.h>
36namespace unityapi = unity::shell::application;
38// This is a mock, specifically to test the LauncherModel
39class MockApp: public unity::shell::application::ApplicationInfoInterface
41 Q_OBJECT
42public:
43 MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
45 RequestedState requestedState() const override { return RequestedRunning; }
46 void setRequestedState(RequestedState) override {}
47 QString appId() const override { return m_appId; }
48 QString name() const override { return "mock"; }
49 QString comment() const override { return "this is a mock"; }
50 QUrl icon() const override { return QUrl(); }
51 ApplicationInfoInterface::State state() const override { return ApplicationInfoInterface::Running; }
52 bool focused() const override { return m_focused; }
53 QString splashTitle() const override { return QString(); }
54 QUrl splashImage() const override { return QUrl(); }
55 bool splashShowHeader() const override { return true; }
56 QColor splashColor() const override { return QColor(0,0,0,0); }
57 QColor splashColorHeader() const override { return QColor(0,0,0,0); }
58 QColor splashColorFooter() const override { return QColor(0,0,0,0); }
59 Qt::ScreenOrientations supportedOrientations() const override { return Qt::PortraitOrientation; }
60 bool rotatesWindowContents() const override { return false; }
61 bool isTouchApp() const override { return true; }
62 bool exemptFromLifecycle() const override { return false; }
63 void setExemptFromLifecycle(bool) override {}
64 QSize initialSurfaceSize() const override { return QSize(); }
65 void setInitialSurfaceSize(const QSize &) override {}
66 MirSurfaceListInterface* surfaceList() const override { return nullptr; }
67 MirSurfaceListInterface* promptSurfaceList() const override { return nullptr; }
68 int surfaceCount() const override { return m_surfaceCount; }
69 void setSurfaceCount(int count) { m_surfaceCount = count; Q_EMIT surfaceCountChanged(count); }
70 void close() override {}
72 // Methods used for mocking (not in the interface)
73 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
74private:
75 QString m_appId;
76 bool m_focused;
77 int m_surfaceCount = 0;
78};
80// This is a mock, specifically to test the LauncherModel
81class MockAppManager: public unity::shell::application::ApplicationManagerInterface
83 Q_OBJECT
84public:
85 MockAppManager(QObject *parent = 0): ApplicationManagerInterface(parent) {}
86 ~MockAppManager() {}
87 int rowCount(const QModelIndex &) const override { return m_list.count(); }
88 QVariant data(const QModelIndex &, int ) const override { return QVariant(); }
89 QString focusedApplicationId() const override {
90 Q_FOREACH(MockApp *app, m_list) {
91 if (app->focused()) return app->appId();
92 }
93 return QString();
94 }
95 unity::shell::application::ApplicationInfoInterface *get(int index) const override { return m_list.at(index); }
96 unity::shell::application::ApplicationInfoInterface *findApplication(const QString &appId) const override {
97 Q_FOREACH(MockApp* app, m_list) {
98 if (app->appId() == appId) {
99 return app;
100 }
101 }
102 return nullptr;
103 }
104 unityapi::ApplicationInfoInterface *findApplicationWithSurface(unityapi::MirSurfaceInterface* surface) const override {
105 return nullptr;
106 }
107 unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) override { return nullptr; }
108 bool stopApplication(const QString &appId) override {
109 Q_FOREACH(MockApp* app, m_list) {
110 if (app->appId() == appId) {
111 removeApplication(m_list.indexOf(app));
112 return true;
113 }
114 }
115 return false;
116 }
117 bool focusApplication(const QString &appId) {
118 Q_FOREACH(MockApp* app, m_list) {
119 app->setFocused(app->appId() == appId);
120 }
121 Q_EMIT focusedApplicationIdChanged();
122 return true;
123 }
125 void addApplication(MockApp *app) {
126 beginInsertRows(QModelIndex(), count(), count());
127 m_list.append(app);
128 endInsertRows();
129 }
130 void removeApplication(int index) {
131 beginRemoveRows(QModelIndex(), index, index);
132 m_list.takeAt(index)->deleteLater();
133 endRemoveRows();
134 }
135 bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
137private:
138 QList<MockApp*> m_list;
139};
141class LauncherModelTest : public QObject
143 Q_OBJECT
145private:
146 LauncherModel *launcherModel;
147 MockAppManager *appManager;
149 QList<QVariantMap> getASConfig() {
150 AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
151 QDBusReply<QVariant> reply = as->getUserPropertyAsync(QString::fromUtf8(g_get_user_name()),
152 "com.canonical.unity.AccountsService",
153 "LauncherItems");
154 return qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>());
155 }
157private Q_SLOTS:
159 void initTestCase() {
160 qDBusRegisterMetaType<QList<QVariantMap>>();
162 launcherModel = new LauncherModel(this);
163 QCoreApplication::processEvents(); // to let the model register on DBus
164 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
166 appManager = new MockAppManager(this);
167 launcherModel->setApplicationManager(appManager);
168 }
170 // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
171 void init() {
172 QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
173 QStringLiteral("/org/freedesktop/Accounts"),
174 QStringLiteral("org.freedesktop.Accounts"));
175 QDBusReply<bool> addReply = accountsInterface.call(QStringLiteral("AddUser"),
176 QString::fromUtf8(g_get_user_name()));
177 QVERIFY(addReply.isValid());
178 QCOMPARE(addReply.value(), true);
180 appManager->addApplication(new MockApp("abs-icon"));
181 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
183 appManager->addApplication(new MockApp("rel-icon"));
184 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
186 launcherModel->m_settings->setStoredApplications(QStringList());
187 }
189 // Removing apps from appmanager and launcher as pinned ones would stick
190 void cleanup() {
191 while (appManager->count() > 0) {
192 appManager->removeApplication(0);
193 }
194 while (launcherModel->rowCount(QModelIndex()) > 0) {
195 launcherModel->requestRemove(launcherModel->get(0)->appId());
196 }
198 QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
199 QStringLiteral("/org/freedesktop/Accounts"),
200 QStringLiteral("org.freedesktop.Accounts"));
201 QDBusReply<bool> removeReply = accountsInterface.call(QStringLiteral("RemoveUser"),
202 QString::fromUtf8(g_get_user_name()));
203 QVERIFY(removeReply.isValid());
204 QCOMPARE(removeReply.value(), true);
206 // Some tests move the directory, so lets move it back if so.
207 // But this will usually fail.
208 QFile::rename("applications.old", "applications");
209 }
211 void testMove() {
212 QCOMPARE(launcherModel->get(0)->pinned(), false);
213 QCOMPARE(launcherModel->get(1)->pinned(), false);
215 LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
216 LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
217 launcherModel->move(1, 0);
219 QCOMPARE(item0BeforeMove, launcherModel->get(1));
220 QCOMPARE(item1BeforeMove, launcherModel->get(0));
222 // moved item must be pinned now
223 QCOMPARE(item0BeforeMove->pinned(), false);
224 QCOMPARE(item1BeforeMove->pinned(), true);
225 }
227 void testPinning() {
228 QSignalSpy spy(launcherModel, &LauncherModel::dataChanged);
229 QCOMPARE(launcherModel->get(0)->pinned(), false);
230 QCOMPARE(launcherModel->get(1)->pinned(), false);
231 launcherModel->pin(launcherModel->get(0)->appId());
232 QCOMPARE(launcherModel->get(0)->pinned(), true);
233 QCOMPARE(launcherModel->get(1)->pinned(), false);
234 QCOMPARE(spy.count() > 0, true);
235 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
237 // App should be pinned now
238 spy.clear();
239 launcherModel->requestRemove(launcherModel->get(0)->appId());
240 QCOMPARE(launcherModel->get(0)->pinned(), false);
241 QCOMPARE(launcherModel->get(1)->pinned(), false);
242 QCOMPARE(spy.count(), 1);
243 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
245 // Now that the app is unpinned, nothing should change and the signal should not be emitted
246 spy.clear();
247 launcherModel->requestRemove(launcherModel->get(0)->appId());
248 QCOMPARE(launcherModel->get(0)->pinned(), false);
249 QCOMPARE(launcherModel->get(1)->pinned(), false);
250 QCOMPARE(spy.count(), 0);
251 }
253 void testRemove_data() {
255 QTest::addColumn<bool>("pinned");
256 QTest::addColumn<bool>("running");
258 QTest::newRow("non-pinned, running") << false << true;
259 QTest::newRow("pinned, running") << true << true;
260 QTest::newRow("pinned, non-running") << true << false;
261 }
263 void testRemove() {
264 QFETCH(bool, pinned);
265 QFETCH(bool, running);
267 // In the beginning we always have two items
268 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
270 // pin one if required
271 if (pinned) {
272 launcherModel->pin("abs-icon");
273 }
275 // stop it if required
276 if (!running) {
277 appManager->stopApplication("abs-icon");
278 }
280 // Now remove it
281 launcherModel->requestRemove("abs-icon");
283 if (running) {
284 // both apps are running, both apps must still be here
285 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
287 // Item must be unpinned now
288 int index = launcherModel->findApplication("abs-icon");
289 QCOMPARE(launcherModel->get(index)->pinned(), false);
291 } else if (pinned) {
292 // Item 1 must go away, item 0 is here to stay
293 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
294 }
296 // done our checks. now stop the app if was still running
297 if (running) {
298 appManager->stopApplication("abs-icon");
299 }
301 // It needs to go away in any case now
302 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
303 }
305 void testQuickListPinningRemoving() {
306 // we start with 2 unpinned items
307 QCOMPARE(launcherModel->get(0)->pinned(), false);
308 QCOMPARE(launcherModel->get(1)->pinned(), false);
309 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
311 // find the Pin item in the quicklist
312 QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
313 int pinActionIndex = -1;
314 for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
315 if (model->get(i).actionId() == "pin_item") {
316 pinActionIndex = i;
317 break;
318 }
319 }
320 QVERIFY(pinActionIndex >= 0);
322 // trigger pin item quicklist action => Item must be pinned now.
323 launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
324 QCOMPARE(launcherModel->get(0)->pinned(), true);
325 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
327 // quicklist needs to transform to remove item. trigger it and check it item goes away
328 launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
329 QCOMPARE(launcherModel->get(0)->pinned(), false);
331 // still needs to be here as the app is still here
332 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
333 // close the app
334 appManager->removeApplication(0);
335 // Now it needs to go away
336 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
337 }
339 void testApplicationRunning() {
340 launcherModel->pin("abs-icon");
341 launcherModel->pin("rel-icon");
343 QCOMPARE(launcherModel->get(0)->running(), true);
344 QCOMPARE(launcherModel->get(1)->running(), true);
346 appManager->stopApplication("abs-icon");
347 QCOMPARE(launcherModel->get(0)->running(), false);
348 QCOMPARE(launcherModel->get(1)->running(), true);
350 appManager->stopApplication("rel-icon");
351 QCOMPARE(launcherModel->get(0)->running(), false);
352 QCOMPARE(launcherModel->get(1)->running(), false);
353 }
355 void testApplicationFocused() {
356 // all apps unfocused at beginning...
357 QCOMPARE(launcherModel->get(0)->focused(), false);
358 QCOMPARE(launcherModel->get(1)->focused(), false);
360 appManager->focusApplication("abs-icon");
361 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
362 QCOMPARE(launcherModel->get(0)->focused(), true);
363 QCOMPARE(launcherModel->get(1)->focused(), false);
365 appManager->focusApplication("rel-icon");
366 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
367 QCOMPARE(launcherModel->get(0)->focused(), false);
368 QCOMPARE(launcherModel->get(1)->focused(), true);
369 }
371 void testClosingApps() {
372 // At the start there are 2 items. Let's pin one.
373 launcherModel->pin("abs-icon");
374 while (appManager->count() > 0) {
375 appManager->removeApplication(0);
376 }
377 // The pinned one needs to stay, the other needs to disappear
378 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
379 QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
380 }
382 void testQuitMenuItem() {
383 // we have 2 apps running, both should have the Quit action in its quick list
384 QCOMPARE(launcherModel->rowCount(), 2);
386 // stop the second one keeping it pinned so that it doesn't go away
387 launcherModel->pin("rel-icon");
388 appManager->stopApplication("rel-icon");
390 // find the first Quit item, should be there
391 QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
392 int quitActionIndex = -1;
393 for (int i = 0; i < model->rowCount(); ++i) {
394 if (model->get(i).actionId() == "stop_item") {
395 quitActionIndex = i;
396 break;
397 }
398 }
399 QVERIFY(quitActionIndex >= 0);
401 // find the second Quit item, should NOT be there, the app is stopped
402 QuickListModel *model2 = qobject_cast<QuickListModel*>(launcherModel->get(1)->quickList());
403 int quitActionIndex2 = -1;
404 for (int i = 0; i < model2->rowCount(); ++i) {
405 if (model2->get(i).actionId() == "stop_item") {
406 quitActionIndex2 = i;
407 break;
408 }
409 }
410 QVERIFY(quitActionIndex2 == -1);
412 // trigger the first quit item quicklist action
413 launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), quitActionIndex);
414 // first app should be gone...
415 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
416 // ... the second app (now at index 0) should still be there, pinned and stopped
417 QCOMPARE(launcherModel->get(0)->appId(), QStringLiteral("rel-icon"));
418 QCOMPARE(launcherModel->get(0)->pinned(), true);
419 QCOMPARE(launcherModel->get(0)->running(), false);
420 }
422 void testGetUrlForAppId() {
423 QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
424 QCOMPARE(launcherModel->getUrlForAppId(""), QString());
425 QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));
426 QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("application:///com.test.good.desktop"));
427 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));
428 QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));
429 }
431 void testIntrospection() {
432 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "org.freedesktop.DBus.Introspectable");
433 QDBusReply<QString> reply = interface.call("Introspect");
434 QStringList nodes = extractNodes(reply.value());
435 QCOMPARE(nodes.count(), launcherModel->rowCount());
437 appManager->addApplication(new MockApp("foobar"));
438 reply = interface.call("Introspect");
439 nodes = extractNodes(reply.value());
440 QCOMPARE(nodes.contains("foobar"), true);
442 appManager->removeApplication(2);
443 reply = interface.call("Introspect");
444 nodes = extractNodes(reply.value());
445 QCOMPARE(nodes.contains("foobar"), false);
446 }
448 QStringList extractNodes(const QString &introspectionXml) {
449 QXmlStreamReader introspectReply(introspectionXml);
451 QStringList ret;
452 while (!introspectReply.atEnd() && !introspectReply.hasError()) {
453 QXmlStreamReader::TokenType token = introspectReply.readNext();
455 if (token == QXmlStreamReader::StartElement) {
456 if (introspectReply.name() == "node" && introspectReply.attributes().count() > 0) {
457 ret << introspectReply.attributes().value("name").toString();
458 }
459 }
460 }
461 return ret;
462 }
464 void testCountEmblems() {
465 QSignalSpy spy(launcherModel, &LauncherModel::dataChanged);
467 // Call GetAll on abs-icon
468 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
469 QDBusReply<QVariantMap> reply = interface.call("GetAll");
470 QVariantMap map = reply.value();
472 // Check that the alerting-status is still false, and the item on the upper side of the API
473 int index = launcherModel->findApplication("abs-icon");
474 QCOMPARE(index >= 0, true);
475 QVERIFY(launcherModel->get(index)->alerting() == false);
477 // Make sure GetAll returns a map with count and countVisible props
478 QCOMPARE(map.contains("count"), true);
479 QCOMPARE(map.contains("countVisible"), true);
481 // Make sure count is intitilized to 0 and non-visible
482 QCOMPARE(map.value("count").toInt(), 0);
483 QCOMPARE(map.value("countVisible").toBool(), false);
485 // Now make it visible and set it to 55 through D-Bus
486 interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", QVariant::fromValue(QDBusVariant(55)));
487 interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", QVariant::fromValue(QDBusVariant(true)));
489 // Fetch it again using GetAll
490 reply = interface.call("GetAll");
491 map = reply.value();
493 // Make sure values have changed on the D-Bus interface
494 QCOMPARE(map.value("count").toInt(), 55);
495 QCOMPARE(map.value("countVisible").toBool(), true);
497 // Finally check, that the change to "count" implicitly also set the alerting-state to true
498 QVERIFY(launcherModel->get(index)->alerting() == true);
500 // Check if the launcher emitted the changed signals
501 QCOMPARE(spy.count(), 2);
503 QVariantList countEmissionArgs = spy.takeFirst();
504 QCOMPARE(countEmissionArgs.at(0).value<QModelIndex>().row(), index);
505 QCOMPARE(countEmissionArgs.at(1).value<QModelIndex>().row(), index);
506 QVector<int> roles = countEmissionArgs.at(2).value<QVector<int> >();
507 QCOMPARE(roles.first(), (int)LauncherModel::RoleCount);
509 QVariantList countVisibleEmissionArgs = spy.takeFirst();
510 QCOMPARE(countVisibleEmissionArgs.at(0).value<QModelIndex>().row(), index);
511 QCOMPARE(countVisibleEmissionArgs.at(1).value<QModelIndex>().row(), index);
512 roles = countVisibleEmissionArgs.at(2).value<QVector<int> >();
513 QVERIFY(roles.contains(LauncherModel::RoleCountVisible));
514 QVERIFY(roles.contains(LauncherModel::RoleAlerting));
516 // Check if the values match
517 QCOMPARE(launcherModel->get(index)->countVisible(), true);
518 QCOMPARE(launcherModel->get(index)->count(), 55);
519 QCOMPARE(launcherModel->get(index)->alerting(), true);
521 // Focus the app, make sure the alert gets cleared
522 appManager->focusApplication("abs-icon");
523 QVERIFY(launcherModel->get(index)->alerting() == false);
524 }
526 void testCountEmblemAddsRemovesItem_data() {
527 QTest::addColumn<bool>("isPinned");
528 QTest::addColumn<bool>("isRunning");
529 QTest::addColumn<bool>("startWhenVisible");
530 QTest::newRow("not pinned, not running") << false << false << false;
531 QTest::newRow("pinned, not running") << true << false << false;
532 QTest::newRow("not pinned, not running, starting from notification") << false << false << true;
533 QTest::newRow("pinned, not running, starting from notification") << true << false << true;
534 QTest::newRow("not pinned, running") << false << true << false;
535 QTest::newRow("pinned, running") << true << true << false;
536 }
538 void testCountEmblemAddsRemovesItem() {
539 QFETCH(bool, isPinned);
540 QFETCH(bool, isRunning);
541 QFETCH(bool, startWhenVisible);
543 // Make sure item is here as expected after init() and that count is not visible
544 int index = launcherModel->findApplication("abs-icon");
545 QCOMPARE(index == -1, false);
546 QCOMPARE(launcherModel->get(index)->countVisible(), false);
548 // Pin if we need to
549 if (isPinned) {
550 launcherModel->pin("abs-icon");
551 }
552 QCOMPARE(launcherModel->get(0)->pinned(), isPinned);
554 // Stop it if we need to
555 if (!isRunning) {
556 appManager->stopApplication("abs-icon");
557 }
558 QCOMPARE(launcherModel->findApplication("abs-icon") >= 0, isRunning || isPinned);
561 // set the count emblem to visible
562 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
563 interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", QVariant::fromValue(QDBusVariant(55)));
564 interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", QVariant::fromValue(QDBusVariant(true)));
566 // Make sure item is here and that count is visible
567 index = launcherModel->findApplication("abs-icon");
568 QCOMPARE(index == -1, false);
569 QCOMPARE(launcherModel->get(index)->countVisible(), true);
571 if (!isRunning && startWhenVisible) {
572 appManager->addApplication(new MockApp("abs-icon"));
573 }
575 // Hide count emblem again
576 interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", QVariant::fromValue(QDBusVariant(false)));
578 // Make sure item is shown/hidden as expected
579 index = launcherModel->findApplication("abs-icon");
580 QCOMPARE(index == -1, !isRunning && !isPinned && !startWhenVisible);
581 }
583 void testAlert() {
584 // Check that the alerting-status is still false
585 int index = launcherModel->findApplication("abs-icon");
586 QCOMPARE(index >= 0, true);
587 QVERIFY(launcherModel->get(index)->alerting() == false);
589 // Call Alert() on "abs-icon"
590 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "com.canonical.Unity.Launcher.Item");
591 interface.call("Alert");
593 // Check that the alerting-status is now true
594 QVERIFY(launcherModel->get(index)->alerting() == true);
596 // Focus the app, make sure the alert gets cleared
597 appManager->focusApplication("abs-icon");
598 QVERIFY(launcherModel->get(index)->alerting() == false);
599 }
601 void testRefreshAfterDeletedDesktopFiles_data() {
602 QTest::addColumn<bool>("deleted");
603 QTest::newRow("have .desktop files") << false;
604 QTest::newRow("deleted .desktop files") << true;
605 }
607 void testRefreshAfterDeletedDesktopFiles() {
608 QFETCH(bool, deleted);
610 // pin both apps
611 launcherModel->pin("abs-icon");
612 launcherModel->pin("rel-icon");
613 // close both apps
614 appManager->removeApplication(0);
615 appManager->removeApplication(0);
617 // "delete" the .desktop files
618 if (deleted) {
619 // In testing mode, the launcher searches the current dir for the sample .desktop file
620 // We can make that fail by moving the applications dir
621 QFile::rename("applications", "applications.old");
622 }
624 // Call refresh
625 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher");
626 QDBusReply<void> reply = interface.call("Refresh");
628 // Make sure the call to Refresh returned without error.
629 QCOMPARE(reply.isValid(), true);
631 QCOMPARE(launcherModel->rowCount(), deleted ? 0 : 2);
632 }
634 void testSettings() {
635 GSettings *settings = launcherModel->m_settings;
636 QSignalSpy spy(launcherModel, &LauncherModel::hint);
638 // Nothing pinned at startup
639 QCOMPARE(settings->storedApplications().count(), 0);
641 // pin both apps
642 launcherModel->pin("abs-icon");
643 launcherModel->pin("rel-icon");
644 QCOMPARE(spy.count(), 0);
646 // Now settings should have 2 apps
647 QCOMPARE(settings->storedApplications().count(), 2);
649 // close both apps
650 appManager->removeApplication(0);
651 appManager->removeApplication(0);
653 // Now settings should have 2 apps
654 QCOMPARE(settings->storedApplications().count(), 2);
656 // Now remove 1 app through the backend, make sure one is still there
657 settings->simulateDConfChanged(QStringList() << "abs-icon");
658 QCOMPARE(settings->storedApplications().count(), 1);
659 QCOMPARE(spy.count(), 1);
661 // Check if it disappeared from the frontend too
662 QCOMPARE(launcherModel->rowCount(), 1);
664 // Add them back but in reverse order
665 settings->simulateDConfChanged(QStringList() << "rel-icon" << "abs-icon");
666 QCOMPARE(launcherModel->rowCount(), 2);
667 QCOMPARE(launcherModel->get(0)->appId(), QString("rel-icon"));
668 QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
669 QCOMPARE(spy.count(), 2);
670 }
672 void testAddSyncsToAS() {
673 // Make sure launcher and AS are in sync when we start the test
674 QCOMPARE(launcherModel->rowCount(), getASConfig().count());
676 int oldCount = launcherModel->rowCount();
677 appManager->addApplication(new MockApp("click-icon"));
678 QCOMPARE(launcherModel->rowCount(), oldCount + 1);
679 QCOMPARE(launcherModel->rowCount(), getASConfig().count());
680 }
682 void testRemoveSyncsToAS() {
683 // Make sure launcher and AS are in sync when we start the test
684 QCOMPARE(launcherModel->rowCount(), getASConfig().count());
686 int oldCount = launcherModel->rowCount();
687 appManager->stopApplication("abs-icon");
688 QCOMPARE(launcherModel->rowCount(), oldCount - 1);
689 QCOMPARE(launcherModel->rowCount(), getASConfig().count());
690 }
692 void testMoveSyncsToAS() {
693 // Make sure launcher and AS are in sync when we start the test
694 QCOMPARE(launcherModel->rowCount(), getASConfig().count());
696 for (int i = 0; i < launcherModel->rowCount(); i++) {
697 QString launcherAppId = launcherModel->get(i)->appId();
698 QString asAppId = getASConfig().at(i).value("id").toString();
699 QCOMPARE(launcherAppId, asAppId);
700 }
702 launcherModel->move(0, 1);
704 for (int i = 0; i < launcherModel->rowCount(); i++) {
705 QString launcherAppId = launcherModel->get(i)->appId();
706 QString asAppId = getASConfig().at(i).value("id").toString();
707 QCOMPARE(launcherAppId, asAppId);
708 }
709 }
711 void testCountChangeSyncsToAS() {
712 // Find the index of the abs-icon app
713 int index = launcherModel->findApplication("abs-icon");
715 // Make sure it's invisible and 0 at the beginning
716 QCOMPARE(getASConfig().at(index).value("countVisible").toBool(), false);
717 QCOMPARE(getASConfig().at(index).value("count").toInt(), 0);
719 // Change the count of the abs-icon app through D-Bus
720 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
721 interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", QVariant::fromValue(QDBusVariant(55)));
722 interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", QVariant::fromValue(QDBusVariant(true)));
724 // Make sure it changed to visible and 55
725 QCOMPARE(getASConfig().at(index).value("countVisible").toBool(), true);
726 QCOMPARE(getASConfig().at(index).value("count").toInt(), 55);
727 }
729 void testSurfaceCountUpdates() {
730 QString appId = launcherModel->get(0)->appId();
732 QCOMPARE(launcherModel->get(0)->surfaceCount(), 0);
733 MockApp *app = qobject_cast<MockApp*>(appManager->findApplication(appId));
734 app->setSurfaceCount(1);
735 QCOMPARE(launcherModel->get(0)->surfaceCount(), 1);
736 }
737};
739QTEST_GUILESS_MAIN(LauncherModelTest)
740#include "launchermodeltest.moc"