@@ -23,8 +23,12 @@
2323
2424#include "launcheritem.h"
2525#include "launchermodel.h"
26+#include "dbusinterface.h"
2627
2728#include <QtTest>
29+#include <QDBusInterface>
30+#include <QDBusReply>
31+#include <QDomDocument>
2832
2933// This is a mock, specifically to test the LauncherModel
3034class MockApp: public unity::shell::application::ApplicationInfoInterface
@@ -111,186 +115,262 @@ private:
111115
112116private Q_SLOTS:
113117
114−// void initTestCase() {
115−// launcherModel = new LauncherModel(this);
116−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
117−
118−// appManager = new MockAppManager(this);
119−// launcherModel->setApplicationManager(appManager);
120−// }
121−
122−// // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
123−// void init() {
124−// appManager->addApplication(new MockApp("abs-icon"));
125−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
126−
127−// appManager->addApplication(new MockApp("no-icon"));
128−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
129−// }
130−
131−// // Removing apps from appmanager and launcher as pinned ones would stick
132−// void cleanup() {
133−// while (appManager->count() > 0) {
134−// appManager->removeApplication(0);
135−// }
136−// while (launcherModel->rowCount(QModelIndex()) > 0) {
137−// launcherModel->requestRemove(launcherModel->get(0)->appId());
138−// }
139−// }
140−
141−// void testMove() {
142−// QCOMPARE(launcherModel->get(0)->pinned(), false);
143−// QCOMPARE(launcherModel->get(1)->pinned(), false);
144−
145−// LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
146−// LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
147−// launcherModel->move(1, 0);
148−
149−// QCOMPARE(item0BeforeMove, launcherModel->get(1));
150−// QCOMPARE(item1BeforeMove, launcherModel->get(0));
151−
152−// // moved item must be pinned now
153−// QCOMPARE(item0BeforeMove->pinned(), false);
154−// QCOMPARE(item1BeforeMove->pinned(), true);
155−// }
156−
157−// void testPinning() {
158−// QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
159−// QCOMPARE(launcherModel->get(0)->pinned(), false);
160−// QCOMPARE(launcherModel->get(1)->pinned(), false);
161−// launcherModel->pin(launcherModel->get(0)->appId());
162−// QCOMPARE(launcherModel->get(0)->pinned(), true);
163−// QCOMPARE(launcherModel->get(1)->pinned(), false);
164−// QCOMPARE(spy.count(), 1);
165−// QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
166−
167−// launcherModel->requestRemove(launcherModel->get(0)->appId());
168−// QCOMPARE(launcherModel->get(0)->pinned(), false);
169−// QCOMPARE(launcherModel->get(1)->pinned(), false);
170−// QCOMPARE(spy.count(), 2);
171−// QCOMPARE(spy.at(1).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
172−// }
173−
174−// void testRemove_data() {
175−
176−// QTest::addColumn<bool>("pinned");
177−// QTest::addColumn<bool>("running");
178−
179−// QTest::newRow("non-pinned, running") << false << true;
180−// QTest::newRow("pinned, running") << true << false;
181−// QTest::newRow("pinned, non-running") << true << false;
182−// }
183−
184−// void testRemove() {
185−// QFETCH(bool, pinned);
186−// QFETCH(bool, running);
187−
188−// // In the beginning we always have two items
189−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
190−
191−// // pin one if required
192−// if (pinned) {
193−// launcherModel->pin(launcherModel->get(1)->appId());
194−// }
195−
196−// // stop it if required
197−// if (!running) {
198−// appManager->removeApplication(1);
199−// }
200−
201−// // Now remove it
202−// launcherModel->requestRemove(launcherModel->get(1)->appId());
203−
204−// if (running) {
205−// // both apps are running, both apps must still be here
206−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
207−
208−// // Item must be unpinned now
209−// QCOMPARE(launcherModel->get(1)->pinned(), false);
210−
211−// } else if (pinned) {
212−// // Item 1 must go away, item 0 is here to stay
213−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
214−// }
215−
216−// // done our checks. now stop the app if was still running
217−// if (running) {
218−// appManager->removeApplication(1);
219−// }
220−
221−// // It needs to go away in any case now
222−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
223−// }
224−
225−// void testQuickListPinningRemoving() {
226−// // we start with 2 unpinned items
227−// QCOMPARE(launcherModel->get(0)->pinned(), false);
228−// QCOMPARE(launcherModel->get(1)->pinned(), false);
229−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
230−
231−// // find the Pin item in the quicklist
232−// QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
233−// int pinActionIndex = -1;
234−// for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
235−// if (model->get(i).actionId() == "pin_item") {
236−// pinActionIndex = i;
237−// break;
238−// }
239−// }
240−// QVERIFY(pinActionIndex >= 0);
241−
242−// // trigger pin item quicklist action => Item must be pinned now.
243−// launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
244−// QCOMPARE(launcherModel->get(0)->pinned(), true);
245−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
246−
247−// // quicklist needs to transform to remove item. trigger it and check it item goes away
248−// launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
249−// QCOMPARE(launcherModel->get(0)->pinned(), false);
250−
251−// // still needs to be here as the app is still here
252−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
253−// // close the app
254−// appManager->removeApplication(0);
255−// // Now it needs to go away
256−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
257−// }
258−
259−// void testApplicationFocused() {
260−// // all apps unfocused at beginning...
261−// QCOMPARE(launcherModel->get(0)->focused(), false);
262−// QCOMPARE(launcherModel->get(1)->focused(), false);
263−
264−// appManager->focusApplication("abs-icon");
265−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
266−// QCOMPARE(launcherModel->get(0)->focused(), true);
267−// QCOMPARE(launcherModel->get(1)->focused(), false);
268−
269−// appManager->focusApplication("no-icon");
270−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
271−// QCOMPARE(launcherModel->get(0)->focused(), false);
272−// QCOMPARE(launcherModel->get(1)->focused(), true);
273−// }
274−
275−// void testClosingApps() {
276−// // At the start there are 2 items. Let's pin one.
277−// launcherModel->pin("abs-icon");
278−// while (appManager->count() > 0) {
279−// appManager->removeApplication(0);
280−// }
281−// // The pinned one needs to stay, the other needs to disappear
282−// QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
283−// QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
284−// }
285−
286−// void testGetUrlForAppId() {
287−// QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
288−// QCOMPARE(launcherModel->getUrlForAppId(""), QString());
289−// QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));
290−// QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("appid://com.test.good/first-listed-app/current-user-version"));
291−// QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));
292−// QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));
293−// }
118+ void initTestCase() {
119+ launcherModel = new LauncherModel(this);
120+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
121+
122+ appManager = new MockAppManager(this);
123+ launcherModel->setApplicationManager(appManager);
124+ }
125+
126+ // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
127+ void init() {
128+ appManager->addApplication(new MockApp("abs-icon"));
129+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
130+
131+ appManager->addApplication(new MockApp("no-icon"));
132+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
133+ }
134+
135+ // Removing apps from appmanager and launcher as pinned ones would stick
136+ void cleanup() {
137+ while (appManager->count() > 0) {
138+ appManager->removeApplication(0);
139+ }
140+ while (launcherModel->rowCount(QModelIndex()) > 0) {
141+ launcherModel->requestRemove(launcherModel->get(0)->appId());
142+ }
143+ }
144+
145+ void testMove() {
146+ QCOMPARE(launcherModel->get(0)->pinned(), false);
147+ QCOMPARE(launcherModel->get(1)->pinned(), false);
148+
149+ LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
150+ LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
151+ launcherModel->move(1, 0);
152+
153+ QCOMPARE(item0BeforeMove, launcherModel->get(1));
154+ QCOMPARE(item1BeforeMove, launcherModel->get(0));
155+
156+ // moved item must be pinned now
157+ QCOMPARE(item0BeforeMove->pinned(), false);
158+ QCOMPARE(item1BeforeMove->pinned(), true);
159+ }
160+
161+ void testPinning() {
162+ QSignalSpy spy(launcherModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)));
163+ QCOMPARE(launcherModel->get(0)->pinned(), false);
164+ QCOMPARE(launcherModel->get(1)->pinned(), false);
165+ launcherModel->pin(launcherModel->get(0)->appId());
166+ QCOMPARE(launcherModel->get(0)->pinned(), true);
167+ QCOMPARE(launcherModel->get(1)->pinned(), false);
168+ QCOMPARE(spy.count(), 1);
169+ QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
170+
171+ launcherModel->requestRemove(launcherModel->get(0)->appId());
172+ QCOMPARE(launcherModel->get(0)->pinned(), false);
173+ QCOMPARE(launcherModel->get(1)->pinned(), false);
174+ QCOMPARE(spy.count(), 2);
175+ QCOMPARE(spy.at(1).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
176+ }
177+
178+ void testRemove_data() {
179+
180+ QTest::addColumn<bool>("pinned");
181+ QTest::addColumn<bool>("running");
182+
183+ QTest::newRow("non-pinned, running") << false << true;
184+ QTest::newRow("pinned, running") << true << false;
185+ QTest::newRow("pinned, non-running") << true << false;
186+ }
187+
188+ void testRemove() {
189+ QFETCH(bool, pinned);
190+ QFETCH(bool, running);
191+
192+ // In the beginning we always have two items
193+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
194+
195+ // pin one if required
196+ if (pinned) {
197+ launcherModel->pin(launcherModel->get(1)->appId());
198+ }
199+
200+ // stop it if required
201+ if (!running) {
202+ appManager->removeApplication(1);
203+ }
204+
205+ // Now remove it
206+ launcherModel->requestRemove(launcherModel->get(1)->appId());
207+
208+ if (running) {
209+ // both apps are running, both apps must still be here
210+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
211+
212+ // Item must be unpinned now
213+ QCOMPARE(launcherModel->get(1)->pinned(), false);
214+
215+ } else if (pinned) {
216+ // Item 1 must go away, item 0 is here to stay
217+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
218+ }
219+
220+ // done our checks. now stop the app if was still running
221+ if (running) {
222+ appManager->removeApplication(1);
223+ }
224+
225+ // It needs to go away in any case now
226+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
227+ }
228+
229+ void testQuickListPinningRemoving() {
230+ // we start with 2 unpinned items
231+ QCOMPARE(launcherModel->get(0)->pinned(), false);
232+ QCOMPARE(launcherModel->get(1)->pinned(), false);
233+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
234+
235+ // find the Pin item in the quicklist
236+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
237+ int pinActionIndex = -1;
238+ for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
239+ if (model->get(i).actionId() == "pin_item") {
240+ pinActionIndex = i;
241+ break;
242+ }
243+ }
244+ QVERIFY(pinActionIndex >= 0);
245+
246+ // trigger pin item quicklist action => Item must be pinned now.
247+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
248+ QCOMPARE(launcherModel->get(0)->pinned(), true);
249+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
250+
251+ // quicklist needs to transform to remove item. trigger it and check it item goes away
252+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
253+ QCOMPARE(launcherModel->get(0)->pinned(), false);
254+
255+ // still needs to be here as the app is still here
256+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
257+ // close the app
258+ appManager->removeApplication(0);
259+ // Now it needs to go away
260+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
261+ }
262+
263+ void testApplicationFocused() {
264+ // all apps unfocused at beginning...
265+ QCOMPARE(launcherModel->get(0)->focused(), false);
266+ QCOMPARE(launcherModel->get(1)->focused(), false);
267+
268+ appManager->focusApplication("abs-icon");
269+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
270+ QCOMPARE(launcherModel->get(0)->focused(), true);
271+ QCOMPARE(launcherModel->get(1)->focused(), false);
272+
273+ appManager->focusApplication("no-icon");
274+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
275+ QCOMPARE(launcherModel->get(0)->focused(), false);
276+ QCOMPARE(launcherModel->get(1)->focused(), true);
277+ }
278+
279+ void testClosingApps() {
280+ // At the start there are 2 items. Let's pin one.
281+ launcherModel->pin("abs-icon");
282+ while (appManager->count() > 0) {
283+ appManager->removeApplication(0);
284+ }
285+ // The pinned one needs to stay, the other needs to disappear
286+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
287+ QCOMPARE(launcherModel->get(0)->appId(), QLatin1String("abs-icon"));
288+ }
289+
290+ void testGetUrlForAppId() {
291+ QCOMPARE(launcherModel->getUrlForAppId(QString()), QString());
292+ QCOMPARE(launcherModel->getUrlForAppId(""), QString());
293+ QCOMPARE(launcherModel->getUrlForAppId("no-name"), QString("application:///no-name.desktop"));
294+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good"), QString("application:///com.test.good.desktop"));
295+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application"), QString("appid://com.test.good/application/current-user-version"));
296+ QCOMPARE(launcherModel->getUrlForAppId("com.test.good_application_1.2.3"), QString("appid://com.test.good/application/current-user-version"));
297+ }
298+
299+ void testIntrospection() {
300+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "org.freedesktop.DBus.Introspectable");
301+ QDBusReply<QString> reply = interface.call("Introspect");
302+ QStringList nodes = extractNodes(reply.value());
303+ QCOMPARE(nodes.count(), launcherModel->rowCount());
304+
305+ appManager->addApplication(new MockApp("foobar"));
306+ reply = interface.call("Introspect");
307+ nodes = extractNodes(reply.value());
308+ QCOMPARE(nodes.contains("foobar"), true);
309+
310+ appManager->removeApplication(2);
311+ reply = interface.call("Introspect");
312+ nodes = extractNodes(reply.value());
313+ QCOMPARE(nodes.contains("foobar"), false);
314+ }
315+
316+ QStringList extractNodes(const QString &introspectionXml) {
317+ QXmlStreamReader introspectReply(introspectionXml);
318+
319+ QStringList ret;
320+ while (!introspectReply.atEnd() && !introspectReply.hasError()) {
321+ QXmlStreamReader::TokenType token = introspectReply.readNext();
322+
323+ if (token == QXmlStreamReader::StartElement) {
324+ if (introspectReply.name() == "node" && introspectReply.attributes().count() > 0) {
325+ ret << introspectReply.attributes().value("name").toString();
326+ }
327+ }
328+ }
329+ return ret;
330+ }
331+
332+ void testCountEmblems() {
333+ // Call GetAll on abs-icon
334+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher/abs_2Dicon", "org.freedesktop.DBus.Properties");
335+ QDBusReply<QVariantMap> reply = interface.call("GetAll");
336+ QVariantMap map = reply.value();
337+
338+ // Make sure GetAll returns a map with count and countVisible props
339+ QCOMPARE(map.contains("count"), true);
340+ QCOMPARE(map.contains("countVisible"), true);
341+
342+ // Make sure count is intitilized to 0 and non-visible
343+ QCOMPARE(map.value("count").toInt(), 0);
344+ QCOMPARE(map.value("countVisible").toBool(), false);
345+
346+ // Now make it visible and set it to 55 through D-Bus
347+ interface.call("Set", "com.canonical.Unity.Launcher.Item", "count", 55);
348+ interface.call("Set", "com.canonical.Unity.Launcher.Item", "countVisible", true);
349+
350+ // Fetch it again using GetAll
351+ reply = interface.call("GetAll");
352+ map = reply.value();
353+
354+ // Make sure values have changed on the D-Bus interface
355+ QCOMPARE(map.value("count").toInt(), 55);
356+ QCOMPARE(map.value("countVisible").toBool(), true);
357+
358+ // Now the item on the upper side of the API
359+ int index = launcherModel->findApplication("abs-icon");
360+ QCOMPARE(index >= 0, true);
361+
362+ // And make sure values have changed there as well
363+ QCOMPARE(launcherModel->get(index)->countVisible(), true);
364+ QCOMPARE(launcherModel->get(index)->count(), 55);
365+ }
366+
367+ void testRefresh() {
368+ QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher");
369+ QDBusReply<void> reply = interface.call("Refresh");
370+
371+ // Make sure the call to Refresh returned without error.
372+ QCOMPARE(reply.isValid(), true);
373+ }
294374};
295375
296376QTEST_GUILESS_MAIN(LauncherModelTest)