Merge lp:~mhr3/unity-scopes-shell/department-support into lp:unity-scopes-shell

Proposed by Michal Hruby
Status: Superseded
Proposed branch: lp:~mhr3/unity-scopes-shell/department-support
Merge into: lp:unity-scopes-shell
Diff against target: 1806 lines (+1206/-70)
23 files modified
debian/changelog (+6/-0)
debian/control (+2/-2)
src/Unity/CMakeLists.txt (+4/-1)
src/Unity/collectors.cpp (+24/-10)
src/Unity/collectors.h (+4/-3)
src/Unity/department.cpp (+167/-0)
src/Unity/department.h (+87/-0)
src/Unity/departmentnode.cpp (+126/-0)
src/Unity/departmentnode.h (+71/-0)
src/Unity/plugin.cpp (+2/-0)
src/Unity/scope.cpp (+162/-29)
src/Unity/scope.h (+71/-4)
src/Unity/scopes.cpp (+1/-1)
tests/CMakeLists.txt (+1/-0)
tests/data/CMakeLists.txt (+1/-0)
tests/data/mock-scope-departments/CMakeLists.txt (+16/-0)
tests/data/mock-scope-departments/mock-scope-departments.cpp (+201/-0)
tests/data/mock-scope-departments/mock-scope-departments.ini.in (+8/-0)
tests/data/mock-scope/CMakeLists.txt (+1/-1)
tests/data/mock-scope/mock-scope.cpp (+4/-3)
tests/departmentstest.cpp (+236/-0)
tests/previewtest.cpp (+3/-2)
tests/resultstest.cpp (+8/-14)
To merge this branch: bzr merge lp:~mhr3/unity-scopes-shell/department-support
Reviewer Review Type Date Requested Status
Albert Astals Cid (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Needs Fixing
Paweł Stołowski (community) Needs Fixing
Review via email: mp+221883@code.launchpad.net

This proposal has been superseded by a proposal from 2014-06-06.

Commit message

Added support for departments.

Description of the change

Added support for departments. Note that this requires changes to lp:unity-api (https://code.launchpad.net/~unity-team/unity-api/departments/+merge/221241) plus changes to libunity-scopes departments API (present in lp:unity-scopes-api/devel).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) :
review: Needs Fixing
124. By Michal Hruby

Let's not forget to commit this

Revision history for this message
Albert Astals Cid (aacid) wrote :

Some small comments over a quick code review.

Will check tests tomorrow.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Albert Astals Cid (aacid) wrote :

My small comments were indeed a "Needs fixing"

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) :
Revision history for this message
Albert Astals Cid (aacid) wrote :
Revision history for this message
Michal Hruby (mhr3) :
Revision history for this message
Michal Hruby (mhr3) wrote :

Should be all addressed now.

Revision history for this message
Michal Hruby (mhr3) wrote :

Eh, in the superseding branch...

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-05-22 09:46:47 +0000
3+++ debian/changelog 2014-06-03 15:51:24 +0000
4@@ -1,3 +1,9 @@
5+unity-scopes-shell (0.4.8-0ubuntu1) UNRELEASED; urgency=medium
6+
7+ * Added support for departments
8+
9+ -- Michal Hruby <michal.hruby@canonical.com> Tue, 27 May 2014 14:20:15 +0200
10+
11 unity-scopes-shell (0.4.0+14.10.20140522-0ubuntu1) utopic; urgency=low
12
13 [ Albert Astals ]
14
15=== modified file 'debian/control'
16--- debian/control 2014-05-22 08:03:59 +0000
17+++ debian/control 2014-06-03 15:51:24 +0000
18@@ -3,8 +3,8 @@
19 Section: libs
20 Build-Depends: cmake,
21 debhelper (>= 9),
22- libunity-api-dev (>= 7.81),
23- libunity-scopes-dev (>= 0.4.6~),
24+ libunity-api-dev (>= 7.82),
25+ libunity-scopes-dev (>= 0.4.8~),
26 libgsettings-qt-dev (>= 0.1),
27 pkg-config,
28 qt5-default,
29
30=== modified file 'src/Unity/CMakeLists.txt'
31--- src/Unity/CMakeLists.txt 2014-05-22 09:18:45 +0000
32+++ src/Unity/CMakeLists.txt 2014-06-03 15:51:24 +0000
33@@ -4,7 +4,7 @@
34 # Dependencies
35 include(FindPkgConfig)
36 pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=1)
37-pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0)
38+pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8)
39 pkg_check_modules(GSETTINGSQT REQUIRED gsettings-qt)
40
41 include_directories(
42@@ -17,6 +17,8 @@
43 set(QMLPLUGIN_SRC
44 categories.cpp
45 collectors.cpp
46+ department.cpp
47+ departmentnode.cpp
48 previewmodel.cpp
49 previewstack.cpp
50 previewwidgetmodel.cpp
51@@ -29,6 +31,7 @@
52 # We need these headers here so moc runs and we get the moc-stuff
53 # compiled in, otherwise we miss some symbols
54 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/CategoriesInterface.h
55+ ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/DepartmentInterface.h
56 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewModelInterface.h
57 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewStackInterface.h
58 ${SCOPES_API_INCLUDEDIR}/unity/shell/scopes/PreviewWidgetModelInterface.h
59
60=== modified file 'src/Unity/collectors.cpp'
61--- src/Unity/collectors.cpp 2014-03-25 16:34:26 +0000
62+++ src/Unity/collectors.cpp 2014-06-03 15:51:24 +0000
63@@ -71,15 +71,15 @@
64 return m_timer.elapsed();
65 }
66
67-class ResultCollector: public CollectorBase
68+class SearchDataCollector: public CollectorBase
69 {
70 public:
71- ResultCollector(): CollectorBase()
72+ SearchDataCollector(): CollectorBase()
73 {
74 }
75
76 // Returns bool indicating whether this resultset was already posted
77- bool addResult(std::shared_ptr<scopes::CategorisedResult> const& result)
78+ bool addResult(scopes::CategorisedResult::SPtr const& result)
79 {
80 QMutexLocker locker(&m_mutex);
81 m_results.append(result);
82@@ -87,7 +87,13 @@
83 return m_posted;
84 }
85
86- Status collect(QList<std::shared_ptr<scopes::CategorisedResult>>& out_results)
87+ void setDepartment(scopes::Department::SCPtr const& department)
88+ {
89+ QMutexLocker locker(&m_mutex);
90+ m_rootDepartment = department;
91+ }
92+
93+ Status collect(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SPtr& rootDepartment)
94 {
95 Status status;
96
97@@ -98,12 +104,14 @@
98 }
99 status = m_status;
100 m_results.swap(out_results);
101+ rootDepartment = std::const_pointer_cast<scopes::Department>(m_rootDepartment);
102
103 return status;
104 }
105
106 private:
107- QList<std::shared_ptr<scopes::CategorisedResult>> m_results;
108+ QList<scopes::CategorisedResult::SPtr> m_results;
109+ scopes::Department::SCPtr m_rootDepartment;
110 };
111
112 class PreviewDataCollector: public CollectorBase
113@@ -214,10 +222,10 @@
114 return m_collector->msecsSinceStart();
115 }
116
117-CollectorBase::Status PushEvent::collectSearchResults(QList<std::shared_ptr<scopes::CategorisedResult>>& out_results)
118+CollectorBase::Status PushEvent::collectSearchResults(QList<scopes::CategorisedResult::SPtr>& out_results, scopes::Department::SPtr& rootDepartment)
119 {
120- auto collector = std::dynamic_pointer_cast<ResultCollector>(m_collector);
121- return collector->collect(out_results);
122+ auto collector = std::dynamic_pointer_cast<SearchDataCollector>(m_collector);
123+ return collector->collect(out_results, rootDepartment);
124 }
125
126 CollectorBase::Status PushEvent::collectPreviewData(scopes::ColumnLayoutList& out_columns, scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data)
127@@ -257,9 +265,9 @@
128 }
129
130 SearchResultReceiver::SearchResultReceiver(QObject* receiver):
131- ScopeDataReceiverBase(receiver, PushEvent::SEARCH, std::shared_ptr<CollectorBase>(new ResultCollector))
132+ ScopeDataReceiverBase(receiver, PushEvent::SEARCH, std::shared_ptr<CollectorBase>(new SearchDataCollector))
133 {
134- m_collector = collectorAs<ResultCollector>();
135+ m_collector = collectorAs<SearchDataCollector>();
136 }
137
138 // this will be called from non-main thread, (might even be multiple different threads)
139@@ -273,6 +281,12 @@
140 }
141 }
142
143+// this will be called from non-main thread, (might even be multiple different threads)
144+void SearchResultReceiver::push(scopes::Department::SCPtr const& department)
145+{
146+ m_collector->setDepartment(department);
147+}
148+
149 // this might be called from any thread (might be main, might be any other thread)
150 void SearchResultReceiver::finished(scopes::ListenerBase::Reason reason, std::string const& error_msg)
151 {
152
153=== modified file 'src/Unity/collectors.h'
154--- src/Unity/collectors.h 2014-03-25 16:34:26 +0000
155+++ src/Unity/collectors.h 2014-06-03 15:51:24 +0000
156@@ -37,7 +37,7 @@
157 namespace scopes_ng
158 {
159
160-class ResultCollector;
161+class SearchDataCollector;
162 class PreviewDataCollector;
163 class ActivationCollector;
164
165@@ -73,7 +73,7 @@
166 PushEvent(Type event_type, std::shared_ptr<CollectorBase> collector);
167 Type type();
168
169- CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results);
170+ CollectorBase::Status collectSearchResults(QList<std::shared_ptr<unity::scopes::CategorisedResult>>& out_results, unity::scopes::Department::SPtr& rootDepartment);
171 CollectorBase::Status collectPreviewData(unity::scopes::ColumnLayoutList& out_columns, unity::scopes::PreviewWidgetList& out_widgets, QHash<QString, QVariant>& out_data);
172 CollectorBase::Status collectActivationResponse(std::shared_ptr<unity::scopes::ActivationResponse>& out_response, std::shared_ptr<unity::scopes::Result>& out_result);
173
174@@ -104,12 +104,13 @@
175 {
176 public:
177 virtual void push(unity::scopes::CategorisedResult result) override;
178+ virtual void push(unity::scopes::Department::SCPtr const& department) override;
179 virtual void finished(unity::scopes::ListenerBase::Reason reason, std::string const& error_msg) override;
180
181 SearchResultReceiver(QObject* receiver);
182
183 private:
184- std::shared_ptr<ResultCollector> m_collector;
185+ std::shared_ptr<SearchDataCollector> m_collector;
186 };
187
188 class PreviewDataReceiver: public unity::scopes::PreviewListenerBase, public ScopeDataReceiverBase
189
190=== added file 'src/Unity/department.cpp'
191--- src/Unity/department.cpp 1970-01-01 00:00:00 +0000
192+++ src/Unity/department.cpp 2014-06-03 15:51:24 +0000
193@@ -0,0 +1,167 @@
194+/*
195+ * Copyright (C) 2014 Canonical, Ltd.
196+ *
197+ * Authors:
198+ * Michal Hruby <michal.hruby@canonical.com>
199+ *
200+ * This program is free software; you can redistribute it and/or modify
201+ * it under the terms of the GNU General Public License as published by
202+ * the Free Software Foundation; version 3.
203+ *
204+ * This program is distributed in the hope that it will be useful,
205+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
206+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
207+ * GNU General Public License for more details.
208+ *
209+ * You should have received a copy of the GNU General Public License
210+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
211+ */
212+
213+#include "department.h"
214+
215+namespace scopes_ng
216+{
217+
218+using namespace unity;
219+
220+Department::Department(QObject* parent) :
221+ unity::shell::scopes::DepartmentInterface(parent),
222+ m_loaded(false),
223+ m_isRoot(false)
224+{
225+}
226+
227+void Department::loadFromDepartmentNode(DepartmentNode* treeNode)
228+{
229+ if (treeNode == nullptr) {
230+ qWarning("Tried to set null DepartmentNode!");
231+ return;
232+ }
233+ m_departmentId = treeNode->id();
234+ m_label = treeNode->label();
235+ m_allLabel = treeNode->allLabel();
236+ m_loaded = !treeNode->isLeaf() && treeNode->childCount() > 0;
237+ m_isRoot = treeNode->isRoot();
238+
239+ DepartmentNode* parentNode = treeNode->parent();
240+ m_parentDepartmentId = parentNode ? parentNode->id() : "";
241+ m_parentLabel = parentNode ? parentNode->label() : "";
242+
243+ beginResetModel();
244+
245+ m_subdepartments.clear();
246+ Q_FOREACH (DepartmentNode* node, treeNode->childNodes()) {
247+ QSharedPointer<SubdepartmentData> subdept(new SubdepartmentData);
248+ subdept->id = node->id();
249+ subdept->label = node->label();
250+ subdept->hasChildren = node->hasSubdepartments();
251+ subdept->isActive = false;
252+ m_subdepartments.append(subdept);
253+ }
254+
255+ endResetModel();
256+
257+ Q_EMIT departmentIdChanged();
258+ Q_EMIT labelChanged();
259+ Q_EMIT allLabelChanged();
260+ Q_EMIT parentDepartmentIdChanged();
261+ Q_EMIT parentLabelChanged();
262+ Q_EMIT loadedChanged();
263+ Q_EMIT countChanged();
264+ Q_EMIT isRootChanged();
265+}
266+
267+void Department::markSubdepartmentActive(QString const& subdepartmentId)
268+{
269+ int idx = -1;
270+ bool isActiveReset = false;
271+ for (int i = 0; i < m_subdepartments.count(); i++) {
272+ if (m_subdepartments[i]->id == subdepartmentId) {
273+ m_subdepartments[i]->isActive = true;
274+ idx = i;
275+ } else if (m_subdepartments[i]->isActive) {
276+ // only one department can be active
277+ m_subdepartments[i]->isActive = false;
278+ isActiveReset = true;
279+ }
280+ }
281+
282+ if (idx < 0) return;
283+
284+ QVector<int> roles;
285+ roles.append(Roles::RoleIsActive);
286+
287+ QModelIndex startIndex(index(isActiveReset ? 0 : idx));
288+ QModelIndex endIndex(index(isActiveReset ? m_subdepartments.count() - 1 : idx));
289+ dataChanged(startIndex, endIndex, roles);
290+}
291+
292+QVariant Department::data(const QModelIndex& index, int role) const
293+{
294+ SubdepartmentData* data = m_subdepartments[index.row()].data();
295+ switch (role) {
296+ case RoleDepartmentId: return data->id;
297+ case RoleLabel: return data->label;
298+ case RoleHasChildren: return data->hasChildren;
299+ case RoleIsActive: return data->isActive;
300+ default: return QVariant();
301+ }
302+}
303+
304+int Department::rowCount(const QModelIndex& parent) const
305+{
306+ Q_UNUSED(parent);
307+ return m_subdepartments.size();
308+}
309+
310+QHash<int, QByteArray> Department::roleNames() const
311+{
312+ QHash<int, QByteArray> roles;
313+ roles[RoleDepartmentId] = "departmentId";
314+ roles[RoleLabel] = "label";
315+ roles[RoleHasChildren] = "hasChildren";
316+ roles[RoleIsActive] = "isActive";
317+ return roles;
318+}
319+
320+QString Department::departmentId() const
321+{
322+ return m_departmentId;
323+}
324+
325+QString Department::label() const
326+{
327+ return m_label;
328+}
329+
330+QString Department::allLabel() const
331+{
332+ return m_allLabel;
333+}
334+
335+QString Department::parentDepartmentId() const
336+{
337+ return m_parentDepartmentId;
338+}
339+
340+QString Department::parentLabel() const
341+{
342+ return m_parentLabel;
343+}
344+
345+bool Department::loaded() const
346+{
347+ return m_loaded;
348+}
349+
350+bool Department::isRoot() const
351+{
352+ return m_isRoot;
353+}
354+
355+int Department::count() const
356+{
357+ return rowCount();
358+}
359+
360+} // namespace scopes_ng
361
362=== added file 'src/Unity/department.h'
363--- src/Unity/department.h 1970-01-01 00:00:00 +0000
364+++ src/Unity/department.h 2014-06-03 15:51:24 +0000
365@@ -0,0 +1,87 @@
366+/*
367+ * Copyright (C) 2014 Canonical, Ltd.
368+ *
369+ * Authors:
370+ * Michal Hruby <michal.hruby@canonical.com>
371+ *
372+ * This program is free software; you can redistribute it and/or modify
373+ * it under the terms of the GNU General Public License as published by
374+ * the Free Software Foundation; version 3.
375+ *
376+ * This program is distributed in the hope that it will be useful,
377+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
378+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
379+ * GNU General Public License for more details.
380+ *
381+ * You should have received a copy of the GNU General Public License
382+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
383+ */
384+
385+
386+#ifndef NG_DEPARTMENT_H
387+#define NG_DEPARTMENT_H
388+
389+#include <unity/shell/scopes/DepartmentInterface.h>
390+
391+#include <QString>
392+#include <QSharedPointer>
393+#include <QPointer>
394+#include <QAbstractListModel>
395+
396+#include <unity/scopes/Result.h>
397+#include <unity/scopes/Department.h>
398+
399+#include "departmentnode.h"
400+
401+namespace scopes_ng
402+{
403+
404+struct SubdepartmentData
405+{
406+ QString id;
407+ QString label;
408+ bool hasChildren;
409+ bool isActive;
410+};
411+
412+class Q_DECL_EXPORT Department : public unity::shell::scopes::DepartmentInterface
413+{
414+ Q_OBJECT
415+
416+public:
417+ explicit Department(QObject* parent = 0);
418+ void loadFromDepartmentNode(DepartmentNode* treeNode);
419+ void markSubdepartmentActive(QString const& subdepartmentId);
420+
421+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
422+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
423+ QHash<int, QByteArray> roleNames() const override;
424+
425+ QString departmentId() const override;
426+ QString label() const override;
427+ QString allLabel() const override;
428+ QString parentDepartmentId() const override;
429+ QString parentLabel() const override;
430+ bool loaded() const override;
431+ bool isRoot() const override;
432+ int count() const override;
433+
434+Q_SIGNALS:
435+
436+private:
437+ QString m_departmentId;
438+ QString m_label;
439+ QString m_allLabel;
440+ QString m_parentDepartmentId;
441+ QString m_parentLabel;
442+ bool m_loaded;
443+ bool m_isRoot;
444+
445+ QList<QSharedPointer<SubdepartmentData>> m_subdepartments;
446+};
447+
448+} // namespace scopes_ng
449+
450+Q_DECLARE_METATYPE(scopes_ng::Department*)
451+
452+#endif // NG_DEPARTMENT_H
453
454=== added file 'src/Unity/departmentnode.cpp'
455--- src/Unity/departmentnode.cpp 1970-01-01 00:00:00 +0000
456+++ src/Unity/departmentnode.cpp 2014-06-03 15:51:24 +0000
457@@ -0,0 +1,126 @@
458+/*
459+ * Copyright (C) 2014 Canonical, Ltd.
460+ *
461+ * Authors:
462+ * Michal Hruby <michal.hruby@canonical.com>
463+ *
464+ * This program is free software; you can redistribute it and/or modify
465+ * it under the terms of the GNU General Public License as published by
466+ * the Free Software Foundation; version 3.
467+ *
468+ * This program is distributed in the hope that it will be useful,
469+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
470+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
471+ * GNU General Public License for more details.
472+ *
473+ * You should have received a copy of the GNU General Public License
474+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
475+ */
476+
477+#include "departmentnode.h"
478+
479+namespace scopes_ng
480+{
481+
482+using namespace unity;
483+
484+DepartmentNode::DepartmentNode(DepartmentNode* parent) : m_parent(parent), m_isRoot(false)
485+{
486+}
487+
488+DepartmentNode::~DepartmentNode()
489+{
490+ clearChildren();
491+}
492+
493+void DepartmentNode::initializeForDepartment(scopes::Department::SCPtr const& dep)
494+{
495+ m_id = QString::fromStdString(dep->id());
496+ m_label = QString::fromStdString(dep->label());
497+ m_allLabel = QString::fromStdString(dep->alternate_label());
498+ m_hasSubdepartments = dep->has_subdepartments();
499+
500+ clearChildren();
501+
502+ auto subdeps = dep->subdepartments();
503+ for (auto it = subdeps.begin(); it != subdeps.end(); ++it) {
504+ DepartmentNode* subdep = new DepartmentNode(this);
505+ subdep->initializeForDepartment(*it);
506+ this->appendChild(subdep);
507+ }
508+}
509+
510+void DepartmentNode::setIsRoot(bool isRoot)
511+{
512+ m_isRoot = isRoot;
513+}
514+
515+bool DepartmentNode::isRoot() const
516+{
517+ return m_isRoot;
518+}
519+
520+DepartmentNode* DepartmentNode::findNodeById(QString const& id)
521+{
522+ if (id == m_id) return this;
523+
524+ Q_FOREACH(DepartmentNode* child, m_children) {
525+ DepartmentNode* node = child->findNodeById(id);
526+ if (node) return node;
527+ }
528+
529+ return nullptr;
530+}
531+
532+QString DepartmentNode::id() const
533+{
534+ return m_id;
535+}
536+
537+QString DepartmentNode::label() const
538+{
539+ return m_label;
540+}
541+
542+QString DepartmentNode::allLabel() const
543+{
544+ return m_allLabel;
545+}
546+
547+bool DepartmentNode::hasSubdepartments() const
548+{
549+ return m_hasSubdepartments;
550+}
551+
552+void DepartmentNode::appendChild(DepartmentNode* child)
553+{
554+ m_children.append(child);
555+}
556+
557+int DepartmentNode::childCount() const
558+{
559+ return m_children.count();
560+}
561+
562+QList<DepartmentNode*> DepartmentNode::childNodes() const
563+{
564+ return m_children;
565+}
566+
567+DepartmentNode* DepartmentNode::parent() const
568+{
569+ return m_parent;
570+}
571+
572+bool DepartmentNode::isLeaf() const
573+{
574+ return m_children.count() == 0 && !m_hasSubdepartments;
575+}
576+
577+void DepartmentNode::clearChildren()
578+{
579+ qDeleteAll(m_children);
580+ m_children.clear();
581+}
582+
583+} // namespace scopes_ng
584
585=== added file 'src/Unity/departmentnode.h'
586--- src/Unity/departmentnode.h 1970-01-01 00:00:00 +0000
587+++ src/Unity/departmentnode.h 2014-06-03 15:51:24 +0000
588@@ -0,0 +1,71 @@
589+/*
590+ * Copyright (C) 2014 Canonical, Ltd.
591+ *
592+ * Authors:
593+ * Michal Hruby <michal.hruby@canonical.com>
594+ *
595+ * This program is free software; you can redistribute it and/or modify
596+ * it under the terms of the GNU General Public License as published by
597+ * the Free Software Foundation; version 3.
598+ *
599+ * This program is distributed in the hope that it will be useful,
600+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
601+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
602+ * GNU General Public License for more details.
603+ *
604+ * You should have received a copy of the GNU General Public License
605+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
606+ */
607+
608+
609+#ifndef NG_DEPARTMENT_NODE_H
610+#define NG_DEPARTMENT_NODE_H
611+
612+#include <QSharedPointer>
613+#include <QMultiMap>
614+#include <QStringList>
615+#include <QPointer>
616+
617+#include <unity-scopes.h>
618+
619+namespace scopes_ng
620+{
621+
622+class Q_DECL_EXPORT DepartmentNode
623+{
624+public:
625+ DepartmentNode(DepartmentNode* parent = nullptr);
626+ ~DepartmentNode();
627+
628+ void initializeForDepartment(unity::scopes::Department::SCPtr const& dep);
629+ DepartmentNode* findNodeById(QString const& id);
630+
631+ QString id() const;
632+ QString label() const;
633+ QString allLabel() const;
634+ bool hasSubdepartments() const;
635+ bool isRoot() const;
636+
637+ void setIsRoot(bool isRoot);
638+
639+ void appendChild(DepartmentNode* child);
640+ int childCount() const;
641+ bool isLeaf() const;
642+ QList<DepartmentNode*> childNodes() const;
643+ DepartmentNode* parent() const;
644+
645+private:
646+ void clearChildren();
647+
648+ DepartmentNode* m_parent;
649+ QList<DepartmentNode*> m_children;
650+ QString m_id;
651+ QString m_label;
652+ QString m_allLabel;
653+ bool m_hasSubdepartments;
654+ bool m_isRoot;
655+};
656+
657+} // namespace scopes_ng
658+
659+#endif // NG_DEPARTMENT_NODE_H
660
661=== modified file 'src/Unity/plugin.cpp'
662--- src/Unity/plugin.cpp 2014-05-19 11:10:04 +0000
663+++ src/Unity/plugin.cpp 2014-06-03 15:51:24 +0000
664@@ -27,6 +27,7 @@
665 #include "scopes.h"
666 #include "scope.h"
667 #include "categories.h"
668+#include "department.h"
669 #include "resultsmodel.h"
670 #include "previewstack.h"
671 #include "previewmodel.h"
672@@ -39,6 +40,7 @@
673 // new Scopes classes
674 qmlRegisterType<scopes_ng::Scopes>(uri, 0, 2, "Scopes");
675 qmlRegisterUncreatableType<unity::shell::scopes::ScopeInterface>(uri, 0, 2, "Scope", "Can't create Scope object in QML. Get them from Scopes instance.");
676+ qmlRegisterUncreatableType<unity::shell::scopes::DepartmentInterface>(uri, 0, 2, "Department", "Can't create Department object in QML. Get them from Scope instance.");
677 qmlRegisterUncreatableType<unity::shell::scopes::CategoriesInterface>(uri, 0, 2, "Categories", "Can't create Categories object in QML. Get them from Scope instance.");
678 qmlRegisterUncreatableType<scopes_ng::ResultsModel>(uri, 0, 2, "ResultsModel", "Can't create new ResultsModel in QML. Get them from Categories instance.");
679 qmlRegisterUncreatableType<unity::shell::scopes::PreviewModelInterface>(uri, 0, 2, "PreviewModel", "Can't create new PreviewModel in QML. Get them from PreviewStack instance.");
680
681=== modified file 'src/Unity/scope.cpp'
682--- src/Unity/scope.cpp 2014-05-22 08:03:59 +0000
683+++ src/Unity/scope.cpp 2014-06-03 15:51:24 +0000
684@@ -69,6 +69,9 @@
685 , m_searchInProgress(false)
686 , m_resultsDirty(false)
687 , m_delayedClear(false)
688+ , m_hasDepartments(false)
689+ , m_searchController(new CollectionController)
690+ , m_activationController(new CollectionController)
691 {
692 m_categories = new Categories(this);
693
694@@ -86,24 +89,21 @@
695
696 Scope::~Scope()
697 {
698- if (m_lastSearch) {
699- std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastSearch)->invalidate();
700- }
701- if (m_lastActivation) {
702- std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastActivation)->invalidate();
703- }
704 }
705
706 void Scope::processSearchChunk(PushEvent* pushEvent)
707 {
708 CollectorBase::Status status;
709 QList<std::shared_ptr<scopes::CategorisedResult>> results;
710+ scopes::Department::SPtr rootDepartment;
711
712- status = pushEvent->collectSearchResults(results);
713+ status = pushEvent->collectSearchResults(results, rootDepartment);
714 if (status == CollectorBase::Status::CANCELLED) {
715 return;
716 }
717
718+ m_rootDepartment = rootDepartment;
719+
720 if (m_cachedResults.empty()) {
721 m_cachedResults.swap(results);
722 } else {
723@@ -213,10 +213,12 @@
724
725 QString scopeId(QString::fromStdString(query.scope_id()));
726 QString searchString(QString::fromStdString(query.query_string()));
727+ QString departmentId(QString::fromStdString(query.department_id()));
728 // figure out if this scope is already favourited
729 Scope* scope = scopes->getScopeById(scopeId);
730 if (scope != nullptr) {
731- // TODO: change department, filters?
732+ // TODO: change filters?
733+ scope->setCurrentDepartmentId(departmentId);
734 scope->setSearchQuery(searchString);
735 Q_EMIT gotoScope(scopeId);
736 } else {
737@@ -225,6 +227,7 @@
738 if (meta_sptr) {
739 scope = new scopes_ng::Scope(this);
740 scope->setScopeData(*meta_sptr);
741+ scope->setCurrentDepartmentId(departmentId);
742 scope->setSearchQuery(searchString);
743 m_tempScopes.insert(scope);
744 Q_EMIT openScope(scope);
745@@ -252,6 +255,85 @@
746 }
747
748 processResultSet(m_cachedResults); // clears the result list
749+
750+ // process departments
751+ if (m_rootDepartment != m_lastRootDepartment) {
752+ // build / append to the tree
753+ DepartmentNode* node = nullptr;
754+ if (m_departmentTree) {
755+ QString departmentId(QString::fromStdString(m_rootDepartment->id()));
756+ node = m_departmentTree->findNodeById(departmentId);
757+ if (node == nullptr) {
758+ node = m_departmentTree.data();
759+ }
760+ auto depNode = findDepartmentById(m_rootDepartment, m_currentDepartmentId.toStdString());
761+ if (depNode && depNode->has_subdepartments()) {
762+ node->initializeForDepartment(m_rootDepartment);
763+ }
764+ // as far as we know, this is the root, re-initializing might have unset the flag
765+ m_departmentTree->setIsRoot(true);
766+
767+ // update corresponding models
768+ QString activeDepartment(m_currentDepartmentId);
769+ node = m_departmentTree->findNodeById(activeDepartment);
770+ DepartmentNode* parentNode = nullptr;
771+ if (node != nullptr) {
772+ auto it = m_departmentModels.find(activeDepartment);
773+ while (it != m_departmentModels.end() && it.key() == activeDepartment) {
774+ it.value()->loadFromDepartmentNode(node);
775+ ++it;
776+ }
777+ // if this node is a leaf, we need to update models for the parent
778+ parentNode = node->isLeaf() ? node->parent() : nullptr;
779+ }
780+ if (parentNode != nullptr) {
781+ auto it = m_departmentModels.find(parentNode->id());
782+ while (it != m_departmentModels.end() && it.key() == parentNode->id()) {
783+ it.value()->markSubdepartmentActive(activeDepartment);
784+ ++it;
785+ }
786+ }
787+ } else {
788+ m_departmentTree.reset(new DepartmentNode);
789+ m_departmentTree->initializeForDepartment(m_rootDepartment);
790+ // as far as we know, this is the root, changing our mind later
791+ // is better than pretending it isn't
792+ m_departmentTree->setIsRoot(true);
793+ }
794+
795+ m_lastRootDepartment = m_rootDepartment;
796+ }
797+
798+ bool containsDepartments = m_rootDepartment.get() != nullptr;
799+ // design decision - no departments when doing searches
800+ containsDepartments &= m_searchQuery.isEmpty();
801+
802+ if (containsDepartments != m_hasDepartments) {
803+ m_hasDepartments = containsDepartments;
804+ Q_EMIT hasDepartmentsChanged();
805+ }
806+
807+ if (!containsDepartments && !m_currentDepartmentId.isEmpty()) {
808+ m_currentDepartmentId = "";
809+ Q_EMIT currentDepartmentIdChanged();
810+ }
811+}
812+
813+scopes::Department::SCPtr Scope::findDepartmentById(scopes::Department::SCPtr const& root, std::string const& id)
814+{
815+ if (root->id() == id) return root;
816+
817+ auto sub_deps = root->subdepartments();
818+ for (auto it = sub_deps.begin(); it != sub_deps.end(); ++it) {
819+ if ((*it)->id() == id) {
820+ return *it;
821+ } else {
822+ auto node = findDepartmentById(*it, id);
823+ if (node) return node;
824+ }
825+ }
826+
827+ return nullptr;
828 }
829
830 void Scope::processResultSet(QList<std::shared_ptr<scopes::CategorisedResult>>& result_set)
831@@ -289,14 +371,7 @@
832
833 void Scope::invalidateLastSearch()
834 {
835- if (m_lastSearch) {
836- std::dynamic_pointer_cast<SearchResultReceiver>(m_lastSearch)->invalidate();
837- m_lastSearch.reset();
838- }
839- if (m_lastSearchQuery) {
840- m_lastSearchQuery->cancel();
841- m_lastSearchQuery.reset();
842- }
843+ m_searchController->invalidate();
844 if (m_aggregatorTimer.isActive()) {
845 m_aggregatorTimer.stop();
846 }
847@@ -338,6 +413,14 @@
848 }
849 }
850
851+void Scope::setCurrentDepartmentId(QString const& id)
852+{
853+ if (m_currentDepartmentId != id) {
854+ m_currentDepartmentId = id;
855+ Q_EMIT currentDepartmentIdChanged();
856+ }
857+}
858+
859 void Scope::dispatchSearch()
860 {
861 invalidateLastSearch();
862@@ -364,7 +447,7 @@
863 if (m_resultsDirty)
864 {
865 m_resultsDirty = false;
866- resultsDirtyChanged(false);
867+ resultsDirtyChanged();
868 }
869
870 setSearchInProgress(true);
871@@ -377,9 +460,11 @@
872 meta["no-internet"] = true;
873 }
874 }
875- m_lastSearch.reset(new SearchResultReceiver(this));
876+ scopes::SearchListenerBase::SPtr listener(new SearchResultReceiver(this));
877+ m_searchController->setListener(listener);
878 try {
879- m_lastSearchQuery = m_proxy->search(m_searchQuery.toStdString(), meta, m_lastSearch);
880+ scopes::QueryCtrlProxy controller = m_proxy->search(m_searchQuery.toStdString(), m_currentDepartmentId.toStdString(), scopes::FilterState(), meta, listener);
881+ m_searchController->setController(controller);
882 } catch (std::exception& e) {
883 qWarning("Caught an error from create_query(): %s", e.what());
884 } catch (...) {
885@@ -387,7 +472,7 @@
886 }
887 }
888
889- if (!m_lastSearchQuery) {
890+ if (!m_searchController->isValid()) {
891 // something went wrong, reset search state
892 setSearchInProgress(false);
893 }
894@@ -470,6 +555,44 @@
895 }
896 */
897
898+unity::shell::scopes::DepartmentInterface* Scope::getDepartment(QString const& departmentId)
899+{
900+ if (!m_departmentTree) return nullptr;
901+
902+ DepartmentNode* node = m_departmentTree->findNodeById(departmentId);
903+ if (!node) return nullptr;
904+
905+ Department* departmentModel = new Department;
906+ departmentModel->loadFromDepartmentNode(node);
907+
908+ m_departmentModels.insert(departmentId, departmentModel);
909+ m_inverseDepartments.insert(departmentModel, departmentId);
910+ QObject::connect(departmentModel, &QObject::destroyed, this, &Scope::departmentModelDestroyed);
911+
912+ return departmentModel;
913+}
914+
915+void Scope::departmentModelDestroyed(QObject* obj)
916+{
917+ scopes_ng::Department* department = reinterpret_cast<scopes_ng::Department*>(obj);
918+
919+ auto it = m_inverseDepartments.find(department);
920+ if (it == m_inverseDepartments.end()) return;
921+
922+ m_departmentModels.remove(it.value(), department);
923+ m_inverseDepartments.remove(department);
924+}
925+
926+void Scope::loadDepartment(QString const& departmentId)
927+{
928+ if (departmentId != m_currentDepartmentId) {
929+ m_currentDepartmentId = departmentId;
930+ Q_EMIT currentDepartmentIdChanged();
931+
932+ dispatchSearch();
933+ }
934+}
935+
936 QString Scope::searchQuery() const
937 {
938 return m_searchQuery;
939@@ -490,6 +613,16 @@
940 return m_isActive;
941 }
942
943+QString Scope::currentDepartmentId() const
944+{
945+ return m_currentDepartmentId;
946+}
947+
948+bool Scope::hasDepartments() const
949+{
950+ return m_hasDepartments;
951+}
952+
953 void Scope::setSearchQuery(const QString& search_query)
954 {
955 /* Checking for m_searchQuery.isNull() which returns true only when the string
956@@ -526,7 +659,7 @@
957 void Scope::setActive(const bool active) {
958 if (active != m_isActive) {
959 m_isActive = active;
960- Q_EMIT isActiveChanged(m_isActive);
961+ Q_EMIT isActiveChanged();
962
963 if (active && m_resultsDirty) {
964 dispatchSearch();
965@@ -551,11 +684,14 @@
966 activateUri(QString::fromStdString(result->uri()));
967 } else {
968 try {
969+ cancelActivation();
970+ scopes::ActivationListenerBase::SPtr listener(new ActivationReceiver(this, result));
971+ m_activationController->setListener(listener);
972+
973 auto proxy = result->target_scope_proxy();
974- // FIXME: don't block
975 unity::scopes::ActionMetadata metadata(QLocale::system().name().toStdString(), m_formFactor.toStdString());
976- m_lastActivation.reset(new ActivationReceiver(this, result));
977- proxy->activate(*(result.get()), metadata, m_lastActivation);
978+ scopes::QueryCtrlProxy controller = proxy->activate(*(result.get()), metadata, listener);
979+ m_activationController->setController(controller);
980 } catch (std::exception& e) {
981 qWarning("Caught an error from activate(): %s", e.what());
982 } catch (...) {
983@@ -585,10 +721,7 @@
984
985 void Scope::cancelActivation()
986 {
987- if (m_lastActivation) {
988- std::dynamic_pointer_cast<ScopeDataReceiverBase>(m_lastActivation)->invalidate();
989- m_lastActivation.reset();
990- }
991+ m_activationController->invalidate();
992 }
993
994 void Scope::invalidateResults()
995@@ -600,7 +733,7 @@
996 if (!m_resultsDirty)
997 {
998 m_resultsDirty = true;
999- resultsDirtyChanged(true);
1000+ resultsDirtyChanged();
1001 }
1002 }
1003 }
1004
1005=== modified file 'src/Unity/scope.h'
1006--- src/Unity/scope.h 2014-05-22 08:03:59 +0000
1007+++ src/Unity/scope.h 2014-06-03 15:51:24 +0000
1008@@ -26,6 +26,7 @@
1009 #include <QTimer>
1010 #include <QMetaType>
1011 #include <QPointer>
1012+#include <QMultiMap>
1013 #include <QSet>
1014 #include <QGSettings>
1015
1016@@ -36,6 +37,10 @@
1017 #include <unity/scopes/ScopeMetadata.h>
1018 #include <unity/shell/scopes/ScopeInterface.h>
1019
1020+#include "collectors.h"
1021+#include "departmentnode.h"
1022+#include "department.h"
1023+
1024 namespace scopes_ng
1025 {
1026
1027@@ -43,6 +48,54 @@
1028 class PushEvent;
1029 class PreviewStack;
1030
1031+class CollectionController
1032+{
1033+public:
1034+ CollectionController() {}
1035+ ~CollectionController()
1036+ {
1037+ if (m_receiver) {
1038+ m_receiver->invalidate();
1039+ }
1040+ // shouldn't call QueryCtrlProxy->cancel() cause the Runtime might be
1041+ // in the process of being destroyed
1042+ }
1043+
1044+ bool isValid()
1045+ {
1046+ return m_listener && m_controller;
1047+ }
1048+
1049+ void invalidate()
1050+ {
1051+ if (m_receiver) {
1052+ m_receiver->invalidate();
1053+ m_receiver.reset();
1054+ }
1055+ m_listener.reset();
1056+ if (m_controller) {
1057+ m_controller->cancel();
1058+ m_controller.reset();
1059+ }
1060+ }
1061+
1062+ void setListener(unity::scopes::ListenerBase::SPtr const& listener)
1063+ {
1064+ m_listener = listener;
1065+ m_receiver = std::dynamic_pointer_cast<ScopeDataReceiverBase>(listener);
1066+ }
1067+
1068+ void setController(unity::scopes::QueryCtrlProxy const& controller)
1069+ {
1070+ m_controller = controller;
1071+ }
1072+
1073+private:
1074+ unity::scopes::ListenerBase::SPtr m_listener;
1075+ std::shared_ptr<ScopeDataReceiverBase> m_receiver;
1076+ unity::scopes::QueryCtrlProxy m_controller;
1077+};
1078+
1079 class Q_DECL_EXPORT Scope : public unity::shell::scopes::ScopeInterface
1080 {
1081 Q_OBJECT
1082@@ -67,6 +120,8 @@
1083 QString noResultsHint() const override;
1084 QString formFactor() const override;
1085 bool isActive() const override;
1086+ QString currentDepartmentId() const override;
1087+ bool hasDepartments() const override;
1088
1089 /* setters */
1090 void setSearchQuery(const QString& search_query) override;
1091@@ -78,6 +133,8 @@
1092 Q_INVOKABLE unity::shell::scopes::PreviewStackInterface* preview(QVariant const& result) override;
1093 Q_INVOKABLE void cancelActivation() override;
1094 Q_INVOKABLE void closeScope(unity::shell::scopes::ScopeInterface* scope) override;
1095+ Q_INVOKABLE unity::shell::scopes::DepartmentInterface* getDepartment(QString const& id) override;
1096+ Q_INVOKABLE void loadDepartment(QString const& id) override;
1097
1098 void setScopeData(unity::scopes::ScopeMetadata const& data);
1099 void handleActivation(std::shared_ptr<unity::scopes::ActivationResponse> const&, unity::scopes::Result::SPtr const&);
1100@@ -89,16 +146,18 @@
1101 void invalidateResults();
1102
1103 Q_SIGNALS:
1104- void resultsDirtyChanged(bool resultsDirty);
1105+ void resultsDirtyChanged();
1106
1107 private Q_SLOTS:
1108 void flushUpdates();
1109 void metadataRefreshed();
1110 void internetFlagChanged(QString const& key);
1111+ void departmentModelDestroyed(QObject* obj);
1112
1113 private:
1114 void startTtlTimer();
1115 void setSearchInProgress(bool searchInProgress);
1116+ void setCurrentDepartmentId(QString const& id);
1117 void processSearchChunk(PushEvent* pushEvent);
1118 void executeCannedQuery(unity::scopes::CannedQuery const& query, bool allowDelayedActivation);
1119
1120@@ -106,27 +165,35 @@
1121 void dispatchSearch();
1122 void invalidateLastSearch();
1123
1124+ static unity::scopes::Department::SCPtr findDepartmentById(unity::scopes::Department::SCPtr const& root, std::string const& id);
1125+
1126 QString m_searchQuery;
1127 QString m_noResultsHint;
1128 QString m_formFactor;
1129+ QString m_currentDepartmentId;
1130 bool m_isActive;
1131 bool m_searchInProgress;
1132 bool m_resultsDirty;
1133 bool m_delayedClear;
1134+ bool m_hasDepartments;
1135
1136+ std::unique_ptr<CollectionController> m_searchController;
1137+ std::unique_ptr<CollectionController> m_activationController;
1138 unity::scopes::ScopeProxy m_proxy;
1139 unity::scopes::ScopeMetadata::SPtr m_scopeMetadata;
1140- unity::scopes::SearchListenerBase::SPtr m_lastSearch;
1141- unity::scopes::QueryCtrlProxy m_lastSearchQuery;
1142- unity::scopes::ActivationListenerBase::SPtr m_lastActivation;
1143 std::shared_ptr<unity::scopes::ActivationResponse> m_delayedActivation;
1144+ unity::scopes::Department::SPtr m_rootDepartment;
1145+ unity::scopes::Department::SPtr m_lastRootDepartment;
1146 QGSettings* m_settings;
1147 Categories* m_categories;
1148+ QSharedPointer<DepartmentNode> m_departmentTree;
1149 QTimer m_aggregatorTimer;
1150 QTimer m_clearTimer;
1151 QTimer m_invalidateTimer;
1152 QList<std::shared_ptr<unity::scopes::CategorisedResult>> m_cachedResults;
1153 QSet<unity::shell::scopes::ScopeInterface*> m_tempScopes;
1154+ QMultiMap<QString, Department*> m_departmentModels;
1155+ QMap<Department*, QString> m_inverseDepartments;
1156 };
1157
1158 } // namespace scopes_ng
1159
1160=== modified file 'src/Unity/scopes.cpp'
1161--- src/Unity/scopes.cpp 2014-05-19 09:58:14 +0000
1162+++ src/Unity/scopes.cpp 2014-06-03 15:51:24 +0000
1163@@ -162,7 +162,7 @@
1164 endResetModel();
1165
1166 m_loaded = true;
1167- Q_EMIT loadedChanged(m_loaded);
1168+ Q_EMIT loadedChanged();
1169 Q_EMIT metadataRefreshed();
1170
1171 m_listThread = nullptr;
1172
1173=== modified file 'tests/CMakeLists.txt'
1174--- tests/CMakeLists.txt 2014-05-22 08:03:59 +0000
1175+++ tests/CMakeLists.txt 2014-06-03 15:51:24 +0000
1176@@ -48,6 +48,7 @@
1177 endmacro(run_tests)
1178
1179 run_tests(
1180+ departmentstest
1181 resultstest
1182 previewtest
1183 utilstest
1184
1185=== modified file 'tests/data/CMakeLists.txt'
1186--- tests/data/CMakeLists.txt 2014-05-02 15:26:40 +0000
1187+++ tests/data/CMakeLists.txt 2014-06-03 15:51:24 +0000
1188@@ -1,4 +1,5 @@
1189 add_subdirectory(mock-scope)
1190+add_subdirectory(mock-scope-departments)
1191 add_subdirectory(mock-scope-ttl)
1192
1193 configure_file(Runtime.ini.in Runtime.ini @ONLY)
1194
1195=== added directory 'tests/data/mock-scope-departments'
1196=== added file 'tests/data/mock-scope-departments/CMakeLists.txt'
1197--- tests/data/mock-scope-departments/CMakeLists.txt 1970-01-01 00:00:00 +0000
1198+++ tests/data/mock-scope-departments/CMakeLists.txt 2014-06-03 15:51:24 +0000
1199@@ -0,0 +1,16 @@
1200+include(FindPkgConfig)
1201+pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0)
1202+
1203+set(SCOPES_BIN_DIR ${SCOPESLIB_LIBDIR})
1204+
1205+include_directories(${SCOPESLIB_INCLUDE_DIRS})
1206+include_directories(${CMAKE_CURRENT_BINARY_DIR})
1207+
1208+set(SCOPE_SOURCES
1209+ mock-scope-departments.cpp
1210+ )
1211+
1212+add_library(mock-scope-departments MODULE ${SCOPE_SOURCES})
1213+target_link_libraries(mock-scope-departments ${SCOPESLIB_LDFLAGS})
1214+
1215+configure_file(mock-scope-departments.ini.in mock-scope-departments.ini)
1216
1217=== added file 'tests/data/mock-scope-departments/mock-scope-departments.cpp'
1218--- tests/data/mock-scope-departments/mock-scope-departments.cpp 1970-01-01 00:00:00 +0000
1219+++ tests/data/mock-scope-departments/mock-scope-departments.cpp 2014-06-03 15:51:24 +0000
1220@@ -0,0 +1,201 @@
1221+/*
1222+ * Copyright (C) 2014 Canonical Ltd
1223+ *
1224+ * This program is free software: you can redistribute it and/or modify
1225+ * it under the terms of the GNU Lesser General Public License version 3 as
1226+ * published by the Free Software Foundation.
1227+ *
1228+ * This program is distributed in the hope that it will be useful,
1229+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1230+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1231+ * GNU Lesser General Public License for more details.
1232+ *
1233+ * You should have received a copy of the GNU Lesser General Public License
1234+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1235+ *
1236+ * Authored by: Pete Woods <pete.woods@canonical.com>
1237+ */
1238+
1239+#include <unity/scopes/CategorisedResult.h>
1240+#include <unity/scopes/ScopeBase.h>
1241+#include <unity/scopes/SearchReply.h>
1242+
1243+#include <iostream>
1244+#include <thread>
1245+#include <atomic>
1246+#include <sstream>
1247+
1248+#define EXPORT __attribute__ ((visibility ("default")))
1249+
1250+using namespace std;
1251+using namespace unity::scopes;
1252+
1253+class MyQuery : public SearchQueryBase
1254+{
1255+public:
1256+ MyQuery(CannedQuery const& query) :
1257+ query_(query), department_id_(query.department_id())
1258+ {
1259+ }
1260+
1261+ ~MyQuery()
1262+ {
1263+ }
1264+
1265+ virtual void cancelled() override
1266+ {
1267+ }
1268+
1269+ static Department::SPtr create_root_dep(CannedQuery const& query)
1270+ {
1271+ Department::SPtr child_dep;
1272+ Department::SPtr root_dep;
1273+ root_dep = Department::create("", query, "All departments");
1274+
1275+ child_dep = Department::create("books", query, "Books");
1276+ child_dep->set_has_subdepartments();
1277+ root_dep->add_subdepartment(child_dep);
1278+
1279+ child_dep = Department::create("movies", query, "Movies, TV, Music");
1280+ child_dep->set_has_subdepartments();
1281+ root_dep->add_subdepartment(child_dep);
1282+
1283+ child_dep = Department::create("electronics", query, "Electronics");
1284+ child_dep->set_has_subdepartments();
1285+ root_dep->add_subdepartment(child_dep);
1286+
1287+ child_dep = Department::create("home", query, "Home, Garden & DIY");
1288+ child_dep->set_has_subdepartments();
1289+ root_dep->add_subdepartment(child_dep);
1290+
1291+ child_dep = Department::create("toys", query, "Toys, Children & Baby");
1292+ child_dep->set_has_subdepartments();
1293+ root_dep->add_subdepartment(child_dep);
1294+
1295+ return root_dep;
1296+ }
1297+
1298+ static Department::SPtr get_department_by_id(Department::SPtr root_dep, std::string const& dep_id)
1299+ {
1300+ auto children = root_dep->subdepartments();
1301+ for (auto it = children.begin(); it != children.end(); ++it)
1302+ {
1303+ if ((*it)->id() == dep_id) return const_pointer_cast<Department>(*it);
1304+ }
1305+ return Department::SPtr();
1306+ }
1307+
1308+ virtual void run(SearchReplyProxy const& reply) override
1309+ {
1310+ Department::SPtr child_dep;
1311+ Department::SPtr root_dep;
1312+ Department::SPtr active_dep;
1313+
1314+ root_dep = create_root_dep(query_);
1315+
1316+ if (department_id_.compare(0, 5, "books") == 0)
1317+ {
1318+ active_dep = get_department_by_id(root_dep, "books");
1319+ child_dep = Department::create("books-kindle", query_, "Kindle Books");
1320+ active_dep->add_subdepartment(child_dep);
1321+
1322+ child_dep = Department::create("books-study", query_, "Books for Study");
1323+ active_dep->add_subdepartment(child_dep);
1324+
1325+ child_dep = Department::create("books-audio", query_, "Audiobooks");
1326+ active_dep->add_subdepartment(child_dep);
1327+ }
1328+
1329+ if (department_id_.compare(0, 4, "home") == 0)
1330+ {
1331+ active_dep = get_department_by_id(root_dep, "home");
1332+ child_dep = Department::create("home-garden", query_, "Garden & Outdoors");
1333+ active_dep->add_subdepartment(child_dep);
1334+
1335+ child_dep = Department::create("home-furniture", query_, "Homeware & Furniture");
1336+ active_dep->add_subdepartment(child_dep);
1337+
1338+ child_dep = Department::create("home-kitchen", query_, "Kitchen & Dining");
1339+ active_dep->add_subdepartment(child_dep);
1340+ }
1341+
1342+ if (department_id_.compare(0, 4, "toys") == 0)
1343+ {
1344+ active_dep = get_department_by_id(root_dep, "toys");
1345+ child_dep = Department::create("toys-games", query_, "Toys & Games");
1346+ active_dep->add_subdepartment(child_dep);
1347+
1348+ child_dep = Department::create("toys-baby", query_, "Baby");
1349+ active_dep->add_subdepartment(child_dep);
1350+ }
1351+
1352+ // provide only partial tree for this leaf
1353+ if (department_id_ == "toys-games")
1354+ {
1355+ root_dep = Department::create("", query_, "All departments");
1356+ child_dep = Department::create("toys", query_, "Toys, Children & Baby");
1357+ root_dep->add_subdepartment(child_dep);
1358+ active_dep = Department::create("toys-games", query_, "Toys & Games");
1359+ child_dep->add_subdepartment(active_dep);
1360+ }
1361+
1362+ reply->register_departments(root_dep);
1363+
1364+ auto cat1 = reply->register_category("cat1", "Category 1", "");
1365+ CategorisedResult res1(cat1);
1366+ res1.set_uri("test:uri");
1367+ res1.set_title("result for: \"" + query_.query_string() + "\"");
1368+ reply->push(res1);
1369+ }
1370+
1371+protected:
1372+ CannedQuery query_;
1373+ string department_id_;
1374+};
1375+
1376+class MyScope : public ScopeBase
1377+{
1378+public:
1379+ MyScope()
1380+ {
1381+ }
1382+
1383+ virtual int start(string const&, RegistryProxy const&) override
1384+ {
1385+ return VERSION;
1386+ }
1387+
1388+ virtual void stop() override {
1389+ }
1390+
1391+ virtual SearchQueryBase::UPtr search(CannedQuery const& q, SearchMetadata const&) override
1392+ {
1393+ return SearchQueryBase::UPtr(new MyQuery(q));
1394+ }
1395+
1396+ virtual PreviewQueryBase::UPtr preview(Result const&, ActionMetadata const&) override
1397+ {
1398+ return nullptr;
1399+ }
1400+};
1401+
1402+extern "C"
1403+{
1404+
1405+ EXPORT
1406+ unity::scopes::ScopeBase*
1407+ // cppcheck-suppress unusedFunction
1408+ UNITY_SCOPE_CREATE_FUNCTION()
1409+ {
1410+ return new MyScope;
1411+ }
1412+
1413+ EXPORT
1414+ void
1415+ // cppcheck-suppress unusedFunction
1416+ UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
1417+ {
1418+ delete scope_base;
1419+ }
1420+
1421+}
1422
1423=== added file 'tests/data/mock-scope-departments/mock-scope-departments.ini.in'
1424--- tests/data/mock-scope-departments/mock-scope-departments.ini.in 1970-01-01 00:00:00 +0000
1425+++ tests/data/mock-scope-departments/mock-scope-departments.ini.in 2014-06-03 15:51:24 +0000
1426@@ -0,0 +1,8 @@
1427+[ScopeConfig]
1428+DisplayName = mock-departments.DisplayName
1429+Description = mock-departments.Description
1430+Art = /mock-departments.Art
1431+Icon = /mock-departments.Icon
1432+SearchHint = mock-departments.SearchHint
1433+HotKey = mock-departments.HotKey
1434+Author = mock-departments.Author
1435
1436=== modified file 'tests/data/mock-scope/CMakeLists.txt'
1437--- tests/data/mock-scope/CMakeLists.txt 2014-05-12 13:13:59 +0000
1438+++ tests/data/mock-scope/CMakeLists.txt 2014-06-03 15:51:24 +0000
1439@@ -1,5 +1,5 @@
1440 include(FindPkgConfig)
1441-pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.0)
1442+pkg_check_modules(SCOPESLIB REQUIRED libunity-scopes>=0.4.8)
1443
1444 include_directories(${SCOPESLIB_INCLUDE_DIRS})
1445
1446
1447=== modified file 'tests/data/mock-scope/mock-scope.cpp'
1448--- tests/data/mock-scope/mock-scope.cpp 2014-04-30 17:22:57 +0000
1449+++ tests/data/mock-scope/mock-scope.cpp 2014-06-03 15:51:24 +0000
1450@@ -31,8 +31,8 @@
1451 class MyQuery : public SearchQueryBase
1452 {
1453 public:
1454- MyQuery(string const& query) :
1455- query_(query)
1456+ MyQuery(string const& query, string const& department_id) :
1457+ query_(query), department_id_(department_id)
1458 {
1459 }
1460
1461@@ -217,6 +217,7 @@
1462
1463 private:
1464 string query_;
1465+ string department_id_;
1466 };
1467
1468 class MyPreview : public PreviewQueryBase
1469@@ -351,7 +352,7 @@
1470
1471 virtual SearchQueryBase::UPtr search(CannedQuery const& q, SearchMetadata const&) override
1472 {
1473- SearchQueryBase::UPtr query(new MyQuery(q.query_string()));
1474+ SearchQueryBase::UPtr query(new MyQuery(q.query_string(), q.department_id()));
1475 cout << "scope-A: created query: \"" << q.query_string() << "\"" << endl;
1476 return query;
1477 }
1478
1479=== added file 'tests/departmentstest.cpp'
1480--- tests/departmentstest.cpp 1970-01-01 00:00:00 +0000
1481+++ tests/departmentstest.cpp 2014-06-03 15:51:24 +0000
1482@@ -0,0 +1,236 @@
1483+/*
1484+ * Copyright (C) 2013-2014 Canonical, Ltd.
1485+ *
1486+ * This program is free software; you can redistribute it and/or modify
1487+ * it under the terms of the GNU General Public License as published by
1488+ * the Free Software Foundation; version 3.
1489+ *
1490+ * This program is distributed in the hope that it will be useful,
1491+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1492+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1493+ * GNU General Public License for more details.
1494+ *
1495+ * You should have received a copy of the GNU General Public License
1496+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1497+ *
1498+ * Authors:
1499+ * Michal Hruby <michal.hruby@canonical.com>
1500+ */
1501+
1502+#include <QObject>
1503+#include <QTest>
1504+#include <QJsonValue>
1505+#include <QJsonObject>
1506+#include <QThread>
1507+#include <QScopedPointer>
1508+#include <QSignalSpy>
1509+#include <QVariantList>
1510+#include <QDBusConnection>
1511+
1512+#include <scopes.h>
1513+#include <scope.h>
1514+#include <categories.h>
1515+#include <resultsmodel.h>
1516+#include <previewmodel.h>
1517+#include <previewstack.h>
1518+#include <previewwidgetmodel.h>
1519+#include <department.h>
1520+
1521+#include "registry-spawner.h"
1522+#include "test-utils.h"
1523+
1524+using namespace scopes_ng;
1525+using namespace unity::shell::scopes;
1526+
1527+class DepartmentsTest : public QObject
1528+{
1529+ Q_OBJECT
1530+private:
1531+ QScopedPointer<Scopes> m_scopes;
1532+ Scope* m_scope;
1533+ QScopedPointer<RegistrySpawner> m_registry;
1534+
1535+private Q_SLOTS:
1536+ void initTestCase()
1537+ {
1538+ m_registry.reset(new RegistrySpawner);
1539+ }
1540+
1541+ void cleanupTestCase()
1542+ {
1543+ m_registry.reset();
1544+ }
1545+
1546+ void init()
1547+ {
1548+ m_scopes.reset(new Scopes(nullptr));
1549+ // no scopes on startup
1550+ QCOMPARE(m_scopes->rowCount(), 0);
1551+ QCOMPARE(m_scopes->loaded(), false);
1552+ QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged()));
1553+ // wait till the registry spawns
1554+ QVERIFY(spy.wait());
1555+ QCOMPARE(m_scopes->loaded(), true);
1556+ // should have one scope now
1557+ QVERIFY(m_scopes->rowCount() > 1);
1558+
1559+ // get scope proxy
1560+ m_scope = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-departments")));
1561+ QVERIFY(m_scope != nullptr);
1562+ }
1563+
1564+ void cleanup()
1565+ {
1566+ m_scopes.reset();
1567+ m_scope = nullptr;
1568+ }
1569+
1570+ void testNoDepartments()
1571+ {
1572+ performSearch(m_scope, QString("foo"));
1573+
1574+ QCOMPARE(m_scope->hasDepartments(), false);
1575+ }
1576+
1577+ void testRootDepartment()
1578+ {
1579+ performSearch(m_scope, QString(""));
1580+
1581+ QCOMPARE(m_scope->hasDepartments(), true);
1582+ QCOMPARE(m_scope->currentDepartmentId(), QString(""));
1583+ QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(m_scope->currentDepartmentId()));
1584+ QVERIFY(departmentModel != nullptr);
1585+
1586+ QVERIFY(departmentModel->departmentId().isEmpty());
1587+ QCOMPARE(departmentModel->label(), QString("All departments"));
1588+ QCOMPARE(departmentModel->allLabel(), QString(""));
1589+ QCOMPARE(departmentModel->parentDepartmentId(), QString());
1590+ QCOMPARE(departmentModel->parentLabel(), QString());
1591+ QCOMPARE(departmentModel->loaded(), true);
1592+ QCOMPARE(departmentModel->isRoot(), true);
1593+
1594+ QCOMPARE(departmentModel->rowCount(), 5);
1595+ QModelIndex idx;
1596+
1597+ idx = departmentModel->index(0);
1598+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleDepartmentId), QVariant(QString("books")));
1599+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleLabel), QVariant(QString("Books")));
1600+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleHasChildren), QVariant(true));
1601+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive), QVariant(false));
1602+
1603+ idx = departmentModel->index(4);
1604+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleDepartmentId), QVariant(QString("toys")));
1605+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleLabel), QVariant(QString("Toys, Children & Baby")));
1606+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleHasChildren), QVariant(true));
1607+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive), QVariant(false));
1608+ }
1609+
1610+ void testChildDepartmentModel()
1611+ {
1612+ performSearch(m_scope, QString(""));
1613+
1614+ QCOMPARE(m_scope->currentDepartmentId(), QString(""));
1615+ QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("toys")));
1616+ QVERIFY(departmentModel != nullptr);
1617+
1618+ QSignalSpy spy(departmentModel.data(), SIGNAL(loadedChanged()));
1619+
1620+ QCOMPARE(departmentModel->departmentId(), QString("toys"));
1621+ QCOMPARE(departmentModel->label(), QString("Toys, Children & Baby"));
1622+ QCOMPARE(departmentModel->allLabel(), QString(""));
1623+ QCOMPARE(departmentModel->parentDepartmentId(), QString(""));
1624+ QCOMPARE(departmentModel->parentLabel(), QString("All departments"));
1625+ QCOMPARE(departmentModel->loaded(), false);
1626+ QCOMPARE(departmentModel->isRoot(), false);
1627+
1628+ QCOMPARE(departmentModel->rowCount(), 0);
1629+
1630+ m_scope->loadDepartment(QString("toys"));
1631+ QVERIFY(spy.wait());
1632+
1633+ QCOMPARE(departmentModel->rowCount(), 2);
1634+ QCOMPARE(departmentModel->loaded(), true);
1635+ QCOMPARE(departmentModel->isRoot(), false);
1636+ }
1637+
1638+ void testLeafActivationUpdatesModel()
1639+ {
1640+ performSearch(m_scope, QString(""));
1641+
1642+ QCOMPARE(m_scope->currentDepartmentId(), QString(""));
1643+ QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged()));
1644+ m_scope->loadDepartment(QString("books"));
1645+ QVERIFY(spy.wait());
1646+ QCOMPARE(m_scope->searchInProgress(), false);
1647+ QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("books")));
1648+ QCOMPARE(departmentModel->isRoot(), false);
1649+
1650+ // this is a leaf department, so activating it should update the parent model
1651+ m_scope->loadDepartment(QString("books-audio"));
1652+ QVERIFY(spy.wait());
1653+ QCOMPARE(m_scope->searchInProgress(), false);
1654+ QCOMPARE(departmentModel->isRoot(), false);
1655+
1656+ bool foundAudiobooks = false;
1657+ for (int i = 0; i < departmentModel->rowCount(); i++) {
1658+ QModelIndex idx(departmentModel->index(i));
1659+ QVariant data = departmentModel->data(idx, Department::Roles::RoleDepartmentId);
1660+ if (data.toString() == QString("books-audio")) {
1661+ QCOMPARE(departmentModel->data(idx, Department::Roles::RoleIsActive).toBool(), true);
1662+ foundAudiobooks = true;
1663+ }
1664+ }
1665+ QCOMPARE(foundAudiobooks, true);
1666+ }
1667+
1668+ void testGoingBack()
1669+ {
1670+ performSearch(m_scope, QString(""));
1671+
1672+ QCOMPARE(m_scope->currentDepartmentId(), QString(""));
1673+ QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged()));
1674+ m_scope->loadDepartment(QString("books"));
1675+ QVERIFY(spy.wait());
1676+ QCOMPARE(m_scope->searchInProgress(), false);
1677+ QScopedPointer<DepartmentInterface> departmentModel(m_scope->getDepartment(QString("books")));
1678+ QCOMPARE(departmentModel->isRoot(), false);
1679+
1680+ // get the root again without actually loading the department
1681+ departmentModel.reset(m_scope->getDepartment(departmentModel->parentDepartmentId()));
1682+ QCOMPARE(departmentModel->isRoot(), true);
1683+ QEXPECT_FAIL("", "We have the department in cache, to it kind of is loaded", Continue);
1684+ QCOMPARE(departmentModel->loaded(), false);
1685+ }
1686+
1687+ void testIncompleteTreeOnLeaf()
1688+ {
1689+ QScopedPointer<DepartmentInterface> departmentModel;
1690+ performSearch(m_scope, QString(""));
1691+
1692+ QCOMPARE(m_scope->currentDepartmentId(), QString(""));
1693+ QCOMPARE(m_scope->hasDepartments(), true);
1694+
1695+ QSignalSpy spy(m_scope, SIGNAL(searchInProgressChanged()));
1696+ m_scope->loadDepartment(QString("toys"));
1697+ QVERIFY(spy.wait());
1698+ QCOMPARE(m_scope->searchInProgress(), false);
1699+
1700+ departmentModel.reset(m_scope->getDepartment(QString("toys")));
1701+ QCOMPARE(departmentModel->isRoot(), false);
1702+ QCOMPARE(departmentModel->rowCount(), 2);
1703+
1704+ m_scope->loadDepartment(QString("toys-games"));
1705+ QVERIFY(spy.wait());
1706+ QCOMPARE(m_scope->searchInProgress(), false);
1707+
1708+ // after getting the parent department model, it should still have
1709+ // all the leaves, even though the leaf served just itself
1710+ departmentModel.reset(m_scope->getDepartment(QString("toys")));
1711+ QCOMPARE(departmentModel->isRoot(), false);
1712+ QCOMPARE(departmentModel->rowCount(), 2);
1713+ }
1714+
1715+};
1716+
1717+QTEST_GUILESS_MAIN(DepartmentsTest)
1718+#include <departmentstest.moc>
1719
1720=== modified file 'tests/previewtest.cpp'
1721--- tests/previewtest.cpp 2014-05-22 08:03:59 +0000
1722+++ tests/previewtest.cpp 2014-06-03 15:51:24 +0000
1723@@ -62,18 +62,19 @@
1724 // no scopes on startup
1725 QCOMPARE(m_scopes->rowCount(), 0);
1726 QCOMPARE(m_scopes->loaded(), false);
1727- QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged(bool)));
1728+ QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged()));
1729 // wait till the registry spawns
1730 QVERIFY(spy.wait());
1731 QCOMPARE(m_scopes->loaded(), true);
1732 // should have one scope now
1733- QCOMPARE(m_scopes->rowCount(), 2);
1734+ QCOMPARE(m_scopes->rowCount(), 3);
1735
1736 QVariant scope_var = m_scopes->data(m_scopes->index(0), Scopes::Roles::RoleScope);
1737 QVERIFY(scope_var.canConvert<Scope*>());
1738
1739 // get scope proxy
1740 m_scope = scope_var.value<Scope*>();
1741+ QCOMPARE(m_scope->id(), QString("mock-scope"));
1742 }
1743
1744 void cleanup()
1745
1746=== modified file 'tests/resultstest.cpp'
1747--- tests/resultstest.cpp 2014-05-22 08:03:59 +0000
1748+++ tests/resultstest.cpp 2014-06-03 15:51:24 +0000
1749@@ -107,22 +107,20 @@
1750 // no scopes on startup
1751 QCOMPARE(m_scopes->rowCount(), 0);
1752 QCOMPARE(m_scopes->loaded(), false);
1753- QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged(bool)));
1754+ QSignalSpy spy(m_scopes.data(), SIGNAL(loadedChanged()));
1755 // wait till the registry spawns
1756 QVERIFY(spy.wait());
1757 QCOMPARE(m_scopes->loaded(), true);
1758 // should have one scope now
1759- QCOMPARE(m_scopes->rowCount(), 2);
1760+ QCOMPARE(m_scopes->rowCount(), 3);
1761
1762 // get scope proxy
1763- QVariant scope_var = m_scopes->data(m_scopes->index(0), Scopes::Roles::RoleScope);
1764- QVERIFY(scope_var.canConvert<Scope*>());
1765- m_scope = scope_var.value<Scope*>();
1766+ m_scope = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope")));
1767+ QVERIFY(m_scope != nullptr);
1768
1769 // get scope proxy for TTL scope
1770- scope_var = m_scopes->data(m_scopes->index(1), Scopes::Roles::RoleScope);
1771- QVERIFY(scope_var.canConvert<Scope*>());
1772- m_scope_ttl = scope_var.value<Scope*>();
1773+ m_scope_ttl = qobject_cast<scopes_ng::Scope*>(m_scopes->getScope(QString("mock-scope-ttl")));
1774+ QVERIFY(m_scope != nullptr);
1775 }
1776
1777 void cleanup()
1778@@ -307,7 +305,7 @@
1779
1780 void testInactiveTtlScope()
1781 {
1782- QSignalSpy dirtySpy(m_scope_ttl, SIGNAL(resultsDirtyChanged(bool)));
1783+ QSignalSpy dirtySpy(m_scope_ttl, SIGNAL(resultsDirtyChanged()));
1784
1785 m_scope_ttl->setActive(false);
1786 performSearch(m_scope_ttl, "banana");
1787@@ -318,10 +316,6 @@
1788 }
1789
1790 // Model should go dirty
1791- QList<QVariantList> expected;
1792- expected << (QVariantList() << true);
1793- QVERIFY(dirtySpy == expected);
1794-
1795 QVERIFY(m_scope_ttl->resultsDirty());
1796 }
1797
1798@@ -623,7 +617,7 @@
1799
1800 QSignalSpy spy(m_scopes.data(), SIGNAL(metadataRefreshed()));
1801 QSignalSpy spy2(m_scope, SIGNAL(gotoScope(QString)));
1802- QSignalSpy spy3(m_scope, SIGNAL(openScope(scopes_ng::Scope*)));
1803+ QSignalSpy spy3(m_scope, SIGNAL(openScope(unity::shell::scopes::ScopeInterface*)));
1804 // this tries to activate non-existing scope
1805 m_scope->activate(QVariant::fromValue(result));
1806 QVERIFY(spy.wait());

Subscribers

People subscribed via source and target branches

to all changes: