Merge lp:~alecu/unity-scope-click/uninstall-scopes into lp:unity-scope-click/devel

Proposed by Alejandro J. Cura
Status: Superseded
Proposed branch: lp:~alecu/unity-scope-click/uninstall-scopes
Merge into: lp:unity-scope-click/devel
Diff against target: 1816 lines (+1007/-192)
28 files modified
CMakeLists.txt (+10/-5)
data/CMakeLists.txt (+41/-19)
data/clickscope.ini.in.in (+8/-0)
data/com.canonical.scopes.clickstore.ini.in.in (+3/-3)
libclickscope/click/interface.cpp (+55/-21)
libclickscope/click/interface.h (+7/-4)
libclickscope/click/preview.cpp (+32/-4)
libclickscope/click/preview.h (+7/-0)
libclickscope/click/scope_activation.cpp (+31/-0)
libclickscope/click/scope_activation.h (+13/-0)
libclickscope/tests/test_interface.cpp (+80/-18)
po/POTFILES.in (+5/-3)
scope/CMakeLists.txt (+3/-2)
scope/clickapps/CMakeLists.txt (+37/-0)
scope/clickapps/apps-query.cpp (+218/-0)
scope/clickapps/apps-query.h (+82/-0)
scope/clickapps/apps-scope.cpp (+158/-0)
scope/clickapps/apps-scope.h (+71/-0)
scope/clickstore/CMakeLists.txt (+8/-7)
scope/clickstore/store-query.cpp (+30/-56)
scope/clickstore/store-query.h (+7/-8)
scope/clickstore/store-scope.cpp (+2/-2)
scope/tests/CMakeLists.txt (+1/-1)
scope/tests/click_interface_tool/CMakeLists.txt (+1/-1)
scope/tests/click_interface_tool/click_interface_tool.cpp (+2/-2)
scope/tests/download_manager_tool/CMakeLists.txt (+1/-1)
scope/tests/integration/CMakeLists.txt (+1/-1)
scope/tests/test_query.cpp (+93/-34)
To merge this branch: bzr merge lp:~alecu/unity-scope-click/uninstall-scopes
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Unity Team Pending
Review via email: mp+221806@code.launchpad.net

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

Commit message

Allow installed packages in the Available category

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
282. By Alejandro J. Cura

Include subtitle with price or Installed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
283. By Alejandro J. Cura

Use the proper list of installed packages

284. By Alejandro J. Cura

Merged with lp:~stolowski/unity-scope-click/two-scopes

285. By Alejandro J. Cura

Fix broken tests after the merge

286. By Alejandro J. Cura

Removing fit, changing aspect-ratio to 1.13

287. By Alejandro J. Cura

Merged from lp:~stolowski/unity-scope-click/two-scopes

288. By Alejandro J. Cura

Test for get_installed_packages async->sync method

289. By Alejandro J. Cura

Use a typedef for PackageNames

290. By Alejandro J. Cura

After talking with pawel, realized keeping this was a mixup from the merge

291. By Alejandro J. Cura

Make new strings translatable

292. By Alejandro J. Cura

Better reporting of errors when parsing the output of 'click list'

293. By Alejandro J. Cura

Simplified parsing of click list lines

294. By Alejandro J. Cura

Use a set of Packages instead of the Packages' names

295. By Alejandro J. Cura

Store the installed version in the scope result

296. By Alejandro J. Cura

Allow searching installed scopes

297. By Alejandro J. Cura

Use the package name for the scope id

298. By Alejandro J. Cura

merged with devel

Unmerged revisions

298. By Alejandro J. Cura

merged with devel

297. By Alejandro J. Cura

Use the package name for the scope id

296. By Alejandro J. Cura

Allow searching installed scopes

295. By Alejandro J. Cura

Store the installed version in the scope result

294. By Alejandro J. Cura

Use a set of Packages instead of the Packages' names

293. By Alejandro J. Cura

Simplified parsing of click list lines

292. By Alejandro J. Cura

Better reporting of errors when parsing the output of 'click list'

291. By Alejandro J. Cura

Make new strings translatable

290. By Alejandro J. Cura

After talking with pawel, realized keeping this was a mixup from the merge

289. By Alejandro J. Cura

Use a typedef for PackageNames

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-05-23 18:47:34 +0000
3+++ CMakeLists.txt 2014-06-06 00:43:07 +0000
4@@ -14,8 +14,10 @@
5
6 include(GNUInstallDirs)
7
8-set(SCOPE_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickscope/)
9-set(SCOPE_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickscope/)
10+set(STORE_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickstore/)
11+set(STORE_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickstore/)
12+set(APPS_LIB_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/unity-scopes/clickapps/)
13+set(APPS_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickapps/)
14
15 include(FindPkgConfig)
16
17@@ -32,8 +34,11 @@
18 SET (SCOPE_LIB_VERSION 0.2.0)
19 SET (SCOPE_LIB_SOVERSION 0)
20 SET (SCOPE_LIB_API_VERSION 2.0)
21-SET (SCOPE_LIB_UNVERSIONED clickscope)
22-SET (SCOPE_LIB_NAME ${SCOPE_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION})
23+SET (STORE_LIB_UNVERSIONED com.canonical.scopes.clickstore)
24+SET (SCOPE_LIB_NAME clickscope)
25+SET (STORE_LIB_NAME ${STORE_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION})
26+SET (APPS_LIB_UNVERSIONED scope)
27+SET (APPS_LIB_NAME ${APPS_LIB_UNVERSIONED}-${SCOPE_LIB_API_VERSION})
28
29 # Build with system gmock and embedded gtest
30 set (GMOCK_INCLUDE_DIR "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
31@@ -108,4 +113,4 @@
32
33 add_custom_target (check-leaks
34 DEPENDS test-leaks
35-)
36\ No newline at end of file
37+)
38
39=== modified file 'data/CMakeLists.txt'
40--- data/CMakeLists.txt 2014-04-29 18:42:40 +0000
41+++ data/CMakeLists.txt 2014-06-06 00:43:07 +0000
42@@ -1,21 +1,43 @@
43 find_program(INTLTOOL_MERGE intltool-merge)
44-set(SCOPE_INI_TARGET clickscope.ini)
45-
46-configure_file(
47- ${SCOPE_INI_TARGET}.in.in
48- ${SCOPE_INI_TARGET}.in
49-)
50-
51-add_custom_target(${SCOPE_INI_TARGET} ALL
52- COMMENT "Merging translations into ${SCOPE_INI_TARGET}"
53- COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${SCOPE_INI_TARGET}.in ${SCOPE_INI_TARGET} >/dev/null
54-)
55-
56-install(
57- FILES clickscope-screenshot.jpg apps-scope.svg
58- DESTINATION "${SCOPE_DATA_DIR}"
59-)
60-install(
61- FILES "${CMAKE_CURRENT_BINARY_DIR}/${SCOPE_INI_TARGET}"
62- DESTINATION "${SCOPE_LIB_DIR}"
63+set(STORE_INI_TARGET com.canonical.scopes.clickstore.ini)
64+set(APPS_INI_TARGET clickscope.ini)
65+
66+configure_file(
67+ ${STORE_INI_TARGET}.in.in
68+ ${STORE_INI_TARGET}.in
69+)
70+
71+configure_file(
72+ ${APPS_INI_TARGET}.in.in
73+ ${APPS_INI_TARGET}.in
74+)
75+
76+add_custom_target(${STORE_INI_TARGET} ALL
77+ COMMENT "Merging translations into ${STORE_INI_TARGET}"
78+ COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${STORE_INI_TARGET}.in ${STORE_INI_TARGET} >/dev/null
79+)
80+
81+add_custom_target(${APPS_INI_TARGET} ALL
82+ COMMENT "Merging translations into ${APPS_INI_TARGET}"
83+ COMMAND LC_ALL=C ${INTLTOOL_MERGE} -d -u ${CMAKE_SOURCE_DIR}/po ${APPS_INI_TARGET}.in ${APPS_INI_TARGET} >/dev/null
84+)
85+
86+install(
87+ FILES clickscope-screenshot.jpg apps-scope.svg
88+ DESTINATION "${STORE_DATA_DIR}"
89+)
90+
91+install(
92+ FILES clickscope-screenshot.jpg apps-scope.svg
93+ DESTINATION "${APPS_DATA_DIR}"
94+)
95+
96+install(
97+ FILES "${CMAKE_CURRENT_BINARY_DIR}/${STORE_INI_TARGET}"
98+ DESTINATION "${STORE_LIB_DIR}"
99+)
100+
101+install(
102+ FILES "${CMAKE_CURRENT_BINARY_DIR}/${APPS_INI_TARGET}"
103+ DESTINATION "${APPS_LIB_DIR}"
104 )
105
106=== added file 'data/clickscope.ini.in.in'
107--- data/clickscope.ini.in.in 1970-01-01 00:00:00 +0000
108+++ data/clickscope.ini.in.in 2014-06-06 00:43:07 +0000
109@@ -0,0 +1,8 @@
110+[ScopeConfig]
111+_DisplayName=Apps
112+_Description=Scope for searching the installed click apps
113+Author=Canonical Ltd.
114+Art=@APPS_DATA_DIR@/clickscope-screenshot.jpg
115+Icon=@APPS_DATA_DIR@/apps-scope.svg
116+SearchHint=clickscope.SearchHint
117+HotKey=clickscope.HotKey
118
119=== renamed file 'data/clickscope.ini.in.in' => 'data/com.canonical.scopes.clickstore.ini.in.in'
120--- data/clickscope.ini.in.in 2014-04-29 18:42:40 +0000
121+++ data/com.canonical.scopes.clickstore.ini.in.in 2014-06-06 00:43:07 +0000
122@@ -1,8 +1,8 @@
123 [ScopeConfig]
124-_DisplayName=Apps
125+_DisplayName=Ubuntu Store
126 _Description=Scope for searching the click app store
127 Author=Canonical Ltd.
128-Art=@SCOPE_DATA_DIR@/clickscope-screenshot.jpg
129-Icon=@SCOPE_DATA_DIR@/apps-scope.svg
130+Art=@STORE_DATA_DIR@/clickscope-screenshot.jpg
131+Icon=@STORE_DATA_DIR@/apps-scope.svg
132 SearchHint=clickscope.SearchHint
133 HotKey=clickscope.HotKey
134
135=== modified file 'libclickscope/click/interface.cpp'
136--- libclickscope/click/interface.cpp 2014-05-26 14:02:45 +0000
137+++ libclickscope/click/interface.cpp 2014-06-06 00:43:07 +0000
138@@ -217,11 +217,6 @@
139 };
140
141 keyFileLocator->enumerateKeyFilesForInstalledApplications(enumerator);
142- // Sort applications so that newest come first.
143- std::sort(result.begin(), result.end(), [](const Application& a,
144- const Application& b) {
145- return a.installed_time > b.installed_time;
146- });
147 return result;
148 }
149
150@@ -321,7 +316,7 @@
151 return manifest;
152 }
153
154-void Interface::get_manifests(std::function<void(ManifestList, ManifestError)> callback)
155+void Interface::get_manifests(std::function<void(ManifestList, InterfaceError)> callback)
156 {
157 std::string command = "click list --manifest";
158 qDebug() << "Running command:" << command.c_str();
159@@ -329,18 +324,57 @@
160 if (code == 0) {
161 try {
162 ManifestList manifests = manifest_list_from_json(stdout_data);
163- callback(manifests, ManifestError::NoError);
164- } catch (...) {
165- callback(ManifestList(), ManifestError::ParseError);
166- }
167- } else {
168- callback(ManifestList(), ManifestError::CallError);
169+ callback(manifests, InterfaceError::NoError);
170+ } catch (...) {
171+ callback(ManifestList(), InterfaceError::ParseError);
172+ }
173+ } else {
174+ callback(ManifestList(), InterfaceError::CallError);
175+ }
176+ });
177+}
178+
179+PackageNames package_names_from_stdout(std::string stdout_data)
180+{
181+ PackageNames package_names;
182+ const char newline = '\n';
183+ const char tab = '\t';
184+
185+ size_t linestart = 0, tabpos;
186+ size_t eof = stdout_data.length();
187+
188+ while (linestart < eof) {
189+ tabpos = stdout_data.find_first_of(tab, linestart);
190+ if (tabpos == std::string::npos) {
191+ throw std::runtime_error("No tab in click list line");
192+ }
193+ package_names.insert(stdout_data.substr(linestart, tabpos-linestart));
194+ linestart = stdout_data.find_first_of(newline, tabpos) + 1;
195+ }
196+
197+ return package_names;
198+}
199+
200+void Interface::get_installed_packagenames(std::function<void(PackageNames, InterfaceError)> callback)
201+{
202+ std::string command = "click list";
203+ qDebug() << "Running command:" << command.c_str();
204+ run_process(command, [callback](int code, const std::string& stdout_data, const std::string&) {
205+ if (code == 0) {
206+ try {
207+ PackageNames package_names = package_names_from_stdout(stdout_data);
208+ callback(package_names, InterfaceError::NoError);
209+ } catch (...) {
210+ callback({}, InterfaceError::ParseError);
211+ }
212+ } else {
213+ callback({}, InterfaceError::CallError);
214 }
215 });
216 }
217
218 void Interface::get_manifest_for_app(const std::string &app_id,
219- std::function<void(Manifest, ManifestError)> callback)
220+ std::function<void(Manifest, InterfaceError)> callback)
221 {
222 std::string command = "click info " + app_id;
223 qDebug() << "Running command:" << command.c_str();
224@@ -348,23 +382,23 @@
225 if (code == 0) {
226 try {
227 Manifest manifest = manifest_from_json(stdout_data);
228- callback(manifest, ManifestError::NoError);
229+ callback(manifest, InterfaceError::NoError);
230 } catch (...) {
231- callback(Manifest(), ManifestError::ParseError);
232+ callback(Manifest(), InterfaceError::ParseError);
233 }
234 } else {
235- callback(Manifest(), ManifestError::CallError);
236+ callback(Manifest(), InterfaceError::CallError);
237 }
238 });
239 }
240
241 void Interface::get_dotdesktop_filename(const std::string &app_id,
242- std::function<void(std::string, ManifestError)> callback)
243+ std::function<void(std::string, InterfaceError)> callback)
244 {
245- get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, ManifestError error) {
246+ get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, InterfaceError error) {
247 qDebug() << "in get_dotdesktop_filename callback";
248
249- if (error != ManifestError::NoError){
250+ if (error != InterfaceError::NoError){
251 callback(std::string("Internal Error"), error);
252 return;
253 }
254@@ -372,10 +406,10 @@
255
256 if (!manifest.name.empty()) {
257 std::string ddstr = manifest.name + "_" + manifest.first_app_name + "_" + manifest.version + ".desktop";
258- callback(ddstr, ManifestError::NoError);
259+ callback(ddstr, InterfaceError::NoError);
260 } else {
261 qCritical() << "Warning: no manifest found for " << app_id.c_str();
262- callback(std::string("Not found"), ManifestError::CallError);
263+ callback(std::string("Not found"), InterfaceError::CallError);
264 }
265 });
266 }
267
268=== modified file 'libclickscope/click/interface.h'
269--- libclickscope/click/interface.h 2014-05-26 14:02:45 +0000
270+++ libclickscope/click/interface.h 2014-06-06 00:43:07 +0000
271@@ -62,12 +62,14 @@
272 bool removable = false;
273 };
274
275-enum class ManifestError {NoError, CallError, ParseError};
276+enum class InterfaceError {NoError, CallError, ParseError};
277 typedef std::list<Manifest> ManifestList;
278
279 ManifestList manifest_list_from_json(const std::string& json);
280 Manifest manifest_from_json(const std::string& json);
281
282+typedef std::unordered_set<std::string> PackageNames;
283+
284 class Interface
285 {
286 public:
287@@ -87,10 +89,11 @@
288
289 static bool is_icon_identifier(const std::string &icon_id);
290 static std::string add_theme_scheme(const std::string &filename);
291- virtual void get_manifests(std::function<void(ManifestList, ManifestError)> callback);
292- virtual void get_manifest_for_app(const std::string &app_id, std::function<void(Manifest, ManifestError)> callback);
293+ virtual void get_manifests(std::function<void(ManifestList, InterfaceError)> callback);
294+ virtual void get_installed_packagenames(std::function<void(PackageNames, InterfaceError)> callback);
295+ virtual void get_manifest_for_app(const std::string &app_id, std::function<void(Manifest, InterfaceError)> callback);
296 virtual void get_dotdesktop_filename(const std::string &app_id,
297- std::function<void(std::string filename, ManifestError)> callback);
298+ std::function<void(std::string filename, InterfaceError)> callback);
299 constexpr static const char* ENV_SHOW_DESKTOP_APPS {"CLICK_SCOPE_SHOW_DESKTOP_APPS"};
300 virtual bool is_visible_app(const unity::util::IniParser& keyFile);
301 virtual bool show_desktop_apps();
302
303=== modified file 'libclickscope/click/preview.cpp'
304--- libclickscope/click/preview.cpp 2014-05-26 14:02:45 +0000
305+++ libclickscope/click/preview.cpp 2014-06-06 00:43:07 +0000
306@@ -100,6 +100,10 @@
307 }
308 } else {
309 // metadata.scope_data() is Null, so we return an appropriate "default" preview:
310+ if (result.uri().find("scope://") == 0)
311+ {
312+ return new InstalledScopePreview(result);
313+ }
314 if (result["installed"].get_bool() == true) {
315 return new InstalledPreview(result, metadata, client);
316 } else {
317@@ -435,7 +439,7 @@
318 if (!app_name.empty()) {
319 qt::core::world::enter_with_task([&]() {
320 click::Interface().get_manifest_for_app(app_name,
321- [&](Manifest manifest, ManifestError error) {
322+ [&](Manifest manifest, InterfaceError error) {
323 qDebug() << "Got manifest for:" << app_name.c_str();
324 removable = manifest.removable;
325
326@@ -443,7 +447,7 @@
327 review.package_name = manifest.name;
328 review.package_version = manifest.version;
329
330- if (error != click::ManifestError::NoError) {
331+ if (error != click::InterfaceError::NoError) {
332 qDebug() << "There was an error getting the manifest for:" << app_name.c_str();
333 }
334 manifest_promise.set_value(true);
335@@ -532,9 +536,9 @@
336 auto ft = qt::core::world::enter_with_task([this, name, callback] ()
337 {
338 click::Interface().get_dotdesktop_filename(name,
339- [callback] (std::string val, click::ManifestError error) {
340+ [callback] (std::string val, click::InterfaceError error) {
341 std::string uri;
342- if (error == click::ManifestError::NoError) {
343+ if (error == click::InterfaceError::NoError) {
344 uri = "application:///" + val;
345 }
346 callback(uri);
347@@ -547,6 +551,30 @@
348 }
349 }
350
351+// class InstalledScopePreview
352+// this is a temporary fallback preview to get into the Store scope, the proper
353+// requires 'store' category to be treated special (like 'local') in unity8 shell.
354+
355+InstalledScopePreview::InstalledScopePreview(const unity::scopes::Result& result)
356+ : PreviewStrategy(result)
357+{
358+}
359+
360+void InstalledScopePreview::run(unity::scopes::PreviewReplyProxy const& reply)
361+{
362+ scopes::PreviewWidget actions("actions", "actions");
363+ {
364+ scopes::VariantBuilder builder;
365+ builder.add_tuple({
366+ {"id", scopes::Variant("search")},
367+ {"uri", scopes::Variant(result.uri())},
368+ {"label", scopes::Variant(_("Search"))}
369+ });
370+ actions.add_attribute_value("actions", builder.end());
371+ }
372+
373+ reply->push({actions});
374+}
375
376 // class PurchasingPreview
377
378
379=== modified file 'libclickscope/click/preview.h'
380--- libclickscope/click/preview.h 2014-05-26 14:02:45 +0000
381+++ libclickscope/click/preview.h 2014-06-06 00:43:07 +0000
382@@ -172,6 +172,13 @@
383 scopes::ActionMetadata metadata;
384 };
385
386+class InstalledScopePreview : public PreviewStrategy
387+{
388+public:
389+ InstalledScopePreview(const unity::scopes::Result& result);
390+ void run(unity::scopes::PreviewReplyProxy const& reply) override;
391+};
392+
393 class PurchasingPreview : public PreviewStrategy
394 {
395 public:
396
397=== modified file 'libclickscope/click/scope_activation.cpp'
398--- libclickscope/click/scope_activation.cpp 2014-05-26 14:02:45 +0000
399+++ libclickscope/click/scope_activation.cpp 2014-06-06 00:43:07 +0000
400@@ -28,6 +28,9 @@
401 */
402
403 #include "scope_activation.h"
404+#include <click/package.h>
405+#include <click/interface.h>
406+#include <click/qtbridge.h>
407 #include <unity/scopes/ActivationResponse.h>
408
409 unity::scopes::ActivationResponse click::ScopeActivation::activate()
410@@ -46,3 +49,31 @@
411 {
412 hints_[key] = value;
413 }
414+
415+click::PerformUninstallAction::PerformUninstallAction(const unity::scopes::Result& result, const unity::scopes::ActivationResponse& response)
416+ : result(result),
417+ response(response)
418+{
419+}
420+
421+unity::scopes::ActivationResponse click::PerformUninstallAction::activate()
422+{
423+ click::Package package;
424+ package.title = result.title();
425+ package.name = result["name"].get_string();
426+ package.version = result["version"].get_string();
427+ qt::core::world::enter_with_task([this, package] ()
428+ {
429+ click::PackageManager manager;
430+ manager.uninstall(package, [&](int code, std::string stderr_content) {
431+ if (code != 0) {
432+ qDebug() << "Error removing package:" << stderr_content.c_str();
433+ } else {
434+ qDebug() << "successfully removed package";
435+
436+ }
437+ } );
438+ });
439+
440+ return response;
441+}
442
443=== modified file 'libclickscope/click/scope_activation.h'
444--- libclickscope/click/scope_activation.h 2014-05-26 14:02:45 +0000
445+++ libclickscope/click/scope_activation.h 2014-06-06 00:43:07 +0000
446@@ -31,10 +31,23 @@
447 #define CLICK_SCOPE_ACTIVATION_H
448
449 #include <unity/scopes/ActivationQueryBase.h>
450+#include <unity/scopes/ActivationResponse.h>
451+#include <unity/scopes/Result.h>
452
453 namespace click
454 {
455
456+class PerformUninstallAction: public unity::scopes::ActivationQueryBase
457+{
458+public:
459+ PerformUninstallAction(const unity::scopes::Result& result, const unity::scopes::ActivationResponse& response);
460+ unity::scopes::ActivationResponse activate() override;
461+
462+private:
463+ unity::scopes::Result result;
464+ unity::scopes::ActivationResponse response;
465+};
466+
467 class ScopeActivation : public unity::scopes::ActivationQueryBase
468 {
469 unity::scopes::ActivationResponse activate() override;
470
471=== modified file 'libclickscope/tests/test_interface.cpp'
472--- libclickscope/tests/test_interface.cpp 2014-05-26 14:27:31 +0000
473+++ libclickscope/tests/test_interface.cpp 2014-06-06 00:43:07 +0000
474@@ -118,8 +118,9 @@
475
476 class ClickInterfaceTest : public ::testing::Test {
477 public:
478- MOCK_METHOD2(manifest_callback, void(Manifest, ManifestError));
479- MOCK_METHOD2(manifests_callback, void(ManifestList, ManifestError));
480+ MOCK_METHOD2(manifest_callback, void(Manifest, InterfaceError));
481+ MOCK_METHOD2(manifests_callback, void(ManifestList, InterfaceError));
482+ MOCK_METHOD2(installed_callback, void(PackageNames, InterfaceError));
483 };
484
485 }
486@@ -326,7 +327,7 @@
487 std::string command = "click info " + FAKE_PACKAGENAME;
488 EXPECT_CALL(iface, run_process(command, _)).
489 Times(1);
490- iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest, ManifestError){});
491+ iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest, InterfaceError){});
492 }
493
494 TEST_F(ClickInterfaceTest, testGetManifestForAppParseError)
495@@ -339,9 +340,9 @@
496 const std::string&)> callback){
497 callback(0, "INVALID JSON", "");
498 }));
499- EXPECT_CALL(*this, manifest_callback(_, ManifestError::ParseError));
500+ EXPECT_CALL(*this, manifest_callback(_, InterfaceError::ParseError));
501 iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest,
502- ManifestError error){
503+ InterfaceError error){
504 manifest_callback(manifest, error);
505 });
506 }
507@@ -356,9 +357,9 @@
508 const std::string&)> callback){
509 callback(-1, "", "CRITICAL: FAIL");
510 }));
511- EXPECT_CALL(*this, manifest_callback(_, ManifestError::CallError));
512+ EXPECT_CALL(*this, manifest_callback(_, InterfaceError::CallError));
513 iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest,
514- ManifestError error){
515+ InterfaceError error){
516 manifest_callback(manifest, error);
517 });
518 }
519@@ -374,8 +375,8 @@
520 callback(0, FAKE_JSON_MANIFEST_REMOVABLE, "");
521 }));
522 iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest,
523- ManifestError error){
524- ASSERT_TRUE(error == ManifestError::NoError);
525+ InterfaceError error){
526+ ASSERT_TRUE(error == InterfaceError::NoError);
527 ASSERT_TRUE(manifest.removable);
528 });
529 }
530@@ -391,8 +392,8 @@
531 callback(0, FAKE_JSON_MANIFEST_NONREMOVABLE, "");
532 }));
533 iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest,
534- ManifestError error){
535- ASSERT_TRUE(error == ManifestError::NoError);
536+ InterfaceError error){
537+ ASSERT_TRUE(error == InterfaceError::NoError);
538 ASSERT_FALSE(manifest.removable);
539 });
540 }
541@@ -403,7 +404,7 @@
542 std::string command = "click list --manifest";
543 EXPECT_CALL(iface, run_process(command, _)).
544 Times(1);
545- iface.get_manifests([](ManifestList, ManifestError){});
546+ iface.get_manifests([](ManifestList, InterfaceError){});
547 }
548
549 TEST_F(ClickInterfaceTest, testGetManifestsParseError)
550@@ -416,8 +417,8 @@
551 const std::string&)> callback){
552 callback(0, "INVALID JSON", "");
553 }));
554- EXPECT_CALL(*this, manifests_callback(_, ManifestError::ParseError));
555- iface.get_manifests([this](ManifestList manifests, ManifestError error){
556+ EXPECT_CALL(*this, manifests_callback(_, InterfaceError::ParseError));
557+ iface.get_manifests([this](ManifestList manifests, InterfaceError error){
558 manifests_callback(manifests, error);
559 });
560 }
561@@ -432,8 +433,8 @@
562 const std::string&)> callback){
563 callback(-1, "", "CRITICAL: FAIL");
564 }));
565- EXPECT_CALL(*this, manifests_callback(_, ManifestError::CallError));
566- iface.get_manifests([this](ManifestList manifests, ManifestError error){
567+ EXPECT_CALL(*this, manifests_callback(_, InterfaceError::CallError));
568+ iface.get_manifests([this](ManifestList manifests, InterfaceError error){
569 manifests_callback(manifests, error);
570 });
571 }
572@@ -452,8 +453,69 @@
573 const std::string&)> callback){
574 callback(0, expected_str, "");
575 }));
576- iface.get_manifests([expected](ManifestList manifests, ManifestError error){
577- ASSERT_TRUE(error == ManifestError::NoError);
578+ iface.get_manifests([expected](ManifestList manifests, InterfaceError error){
579+ ASSERT_TRUE(error == InterfaceError::NoError);
580 ASSERT_TRUE(manifests.size() == expected.size());
581 });
582 }
583+
584+TEST(ClickInterface, testGetInstalledPackagesCorrectCommand)
585+{
586+ FakeClickInterface iface;
587+ std::string command = "click list";
588+ EXPECT_CALL(iface, run_process(command, _)).
589+ Times(1);
590+ iface.get_installed_packagenames([](PackageNames, InterfaceError){});
591+}
592+
593+TEST_F(ClickInterfaceTest, testGetInstalledPackagesParseError)
594+{
595+ FakeClickInterface iface;
596+ EXPECT_CALL(iface, run_process(_, _)).
597+ Times(1).
598+ WillOnce(Invoke([&](const std::string&,
599+ std::function<void(int, const std::string&,
600+ const std::string&)> callback){
601+ callback(0, "valid\t\nINVALID LINE\n", "");
602+ }));
603+ EXPECT_CALL(*this, installed_callback(_, InterfaceError::ParseError));
604+ iface.get_installed_packagenames([this](PackageNames package_names, InterfaceError error){
605+ installed_callback(package_names, error);
606+ });
607+}
608+
609+TEST_F(ClickInterfaceTest, testGetInstalledPackagesCommandFailed)
610+{
611+ FakeClickInterface iface;
612+ EXPECT_CALL(iface, run_process(_, _)).
613+ Times(1).
614+ WillOnce(Invoke([&](const std::string&,
615+ std::function<void(int, const std::string&,
616+ const std::string&)> callback){
617+ callback(-1, "", "CRITICAL: FAIL");
618+ }));
619+ EXPECT_CALL(*this, installed_callback(_, InterfaceError::CallError));
620+ iface.get_installed_packagenames([this](PackageNames package_names, InterfaceError error){
621+ installed_callback(package_names, error);
622+ });
623+}
624+
625+TEST_F(ClickInterfaceTest, testGetInstalledPackagesParsed)
626+{
627+ FakeClickInterface iface;
628+ std::string sample_stdout = "ABC\t0.1\nDEF\t0.2\n";
629+ PackageNames expected{"ABC", "DEF"};
630+
631+ EXPECT_CALL(iface, run_process(_, _)).
632+ Times(1).
633+ WillOnce(Invoke([&](const std::string&,
634+ std::function<void(int, const std::string&,
635+ const std::string&)> callback){
636+ callback(0, sample_stdout, "");
637+ }));
638+ iface.get_installed_packagenames([expected](PackageNames package_names, InterfaceError error){
639+ ASSERT_EQ(error, InterfaceError::NoError);
640+ ASSERT_EQ(package_names, expected);
641+ });
642+}
643+
644
645=== modified file 'po/POTFILES.in'
646--- po/POTFILES.in 2014-04-29 18:42:40 +0000
647+++ po/POTFILES.in 2014-06-06 00:43:07 +0000
648@@ -1,3 +1,5 @@
649-[type: gettext/ini] data/clickscope.ini.in.in
650-scope/click/preview.cpp
651-scope/click/query.cpp
652+[type: gettext/ini] data/com.canonical.scopes.clickstore.ini.in.in
653+data/clickscope.ini.in.in
654+libclickscope/click/preview.cpp
655+scope/clickapps/apps-query.cpp
656+scope/clickstore/store-query.cpp
657
658=== modified file 'scope/CMakeLists.txt'
659--- scope/CMakeLists.txt 2014-01-28 08:49:16 +0000
660+++ scope/CMakeLists.txt 2014-06-06 00:43:07 +0000
661@@ -1,2 +1,3 @@
662-add_subdirectory(click)
663-add_subdirectory(tests)
664\ No newline at end of file
665+add_subdirectory(clickstore)
666+add_subdirectory(clickapps)
667+add_subdirectory(tests)
668
669=== added directory 'scope/clickapps'
670=== added file 'scope/clickapps/CMakeLists.txt'
671--- scope/clickapps/CMakeLists.txt 1970-01-01 00:00:00 +0000
672+++ scope/clickapps/CMakeLists.txt 2014-06-06 00:43:07 +0000
673@@ -0,0 +1,37 @@
674+SET (CMAKE_INCLUDE_CURRENT_DIR ON)
675+SET (CMAKE_AUTOMOC ON)
676+find_package (Qt5Core REQUIRED)
677+pkg_check_modules(JSON_CPP REQUIRED jsoncpp)
678+
679+add_definitions(
680+ -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\"
681+ -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\"
682+ -DSTORE_DATA_DIR="${STORE_DATA_DIR}"
683+)
684+
685+add_library(${APPS_LIB_UNVERSIONED} SHARED
686+ apps-query.cpp
687+ apps-scope.cpp
688+)
689+set_target_properties(${APPS_LIB_UNVERSIONED} PROPERTIES PREFIX "")
690+
691+include_directories(
692+ ${CMAKE_SOURCE_DIR}/libclickscope
693+ ${JSON_CPP_INCLUDE_DIRS}
694+)
695+
696+qt5_use_modules (${APPS_LIB_UNVERSIONED} Network)
697+
698+target_link_libraries (${APPS_LIB_UNVERSIONED}
699+ ${SCOPE_LIB_NAME}
700+ ${JSON_CPP_LDFLAGS}
701+ ${UNITY_SCOPES_LDFLAGS}
702+ ${UBUNTUONE_LDFLAGS}
703+ ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS}
704+ ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS}
705+)
706+
707+install(
708+ TARGETS ${APPS_LIB_UNVERSIONED}
709+ LIBRARY DESTINATION "${APPS_LIB_DIR}"
710+ )
711
712=== added file 'scope/clickapps/apps-query.cpp'
713--- scope/clickapps/apps-query.cpp 1970-01-01 00:00:00 +0000
714+++ scope/clickapps/apps-query.cpp 2014-06-06 00:43:07 +0000
715@@ -0,0 +1,218 @@
716+/*
717+ * Copyright (C) 2014 Canonical Ltd.
718+ *
719+ * This program is free software: you can redistribute it and/or modify it
720+ * under the terms of the GNU General Public License version 3, as published
721+ * by the Free Software Foundation.
722+ *
723+ * This program is distributed in the hope that it will be useful, but
724+ * WITHOUT ANY WARRANTY; without even the implied warranties of
725+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
726+ * PURPOSE. See the GNU General Public License for more details.
727+ *
728+ * You should have received a copy of the GNU General Public License along
729+ * with this program. If not, see <http://www.gnu.org/licenses/>.
730+ *
731+ * In addition, as a special exception, the copyright holders give
732+ * permission to link the code of portions of this program with the
733+ * OpenSSL library under certain conditions as described in each
734+ * individual source file, and distribute linked combinations
735+ * including the two.
736+ * You must obey the GNU General Public License in all respects
737+ * for all of the code used other than OpenSSL. If you modify
738+ * file(s) with this exception, you may extend this exception to your
739+ * version of the file(s), but you are not obligated to do so. If you
740+ * do not wish to do so, delete this exception statement from your
741+ * version. If you delete this exception statement from all source
742+ * files in the program, then also delete it here.
743+ */
744+
745+#include <click/application.h>
746+#include <click/interface.h>
747+
748+#include <click/key_file_locator.h>
749+
750+#include <unity/scopes/CategoryRenderer.h>
751+#include <unity/scopes/CategorisedResult.h>
752+#include <unity/scopes/CannedQuery.h>
753+#include <unity/scopes/SearchReply.h>
754+#include <unity/scopes/SearchMetadata.h>
755+
756+#include <vector>
757+
758+#include <click/click-i18n.h>
759+#include "apps-query.h"
760+
761+namespace
762+{
763+
764+std::string CATEGORY_APPS_DISPLAY = R"(
765+ {
766+ "schema-version" : 1,
767+ "template" : {
768+ "category-layout" : "grid",
769+ "card-size": "small"
770+ },
771+ "components" : {
772+ "title" : "title",
773+ "art" : {
774+ "field": "art",
775+ "aspect-ratio": 1.6,
776+ "fill-mode": "fit"
777+ }
778+ }
779+ }
780+)";
781+
782+std::string CATEGORY_APPS_SEARCH = R"(
783+ {
784+ "schema-version" : 1,
785+ "template" : {
786+ "category-layout" : "grid",
787+ "card-layout" : "horizontal",
788+ "card-size": "large"
789+ },
790+ "components" : {
791+ "title" : "title",
792+ "mascot" : {
793+ "field": "art"
794+ },
795+ "subtitle": "publisher"
796+ }
797+ }
798+)";
799+
800+static const char CATEGORY_STORE[] = R"(
801+{
802+ "schema-version": 1,
803+ "template": {
804+ "category-layout": "grid",
805+ "card-size": "medium",
806+ "card-background": "color:///#E9E9E9"
807+ },
808+ "components": {
809+ "title": "title",
810+ "subtitle": "author",
811+ "mascot": {
812+ "field": "art"
813+ },
814+ "background": "background"
815+ }
816+}
817+)";
818+
819+
820+}
821+
822+void click::Query::push_local_results(scopes::SearchReplyProxy const &replyProxy,
823+ std::vector<click::Application> const &apps,
824+ std::string &categoryTemplate)
825+{
826+ scopes::CategoryRenderer rdr(categoryTemplate);
827+ auto cat = replyProxy->register_category("local", "", "", rdr);
828+
829+ for(const auto & a: apps)
830+ {
831+ scopes::CategorisedResult res(cat);
832+ res.set_title(a.title);
833+ res.set_art(a.icon_url);
834+ res.set_uri(a.url);
835+ res[click::Query::ResultKeys::NAME] = a.name;
836+ res[click::Query::ResultKeys::DESCRIPTION] = a.description;
837+ res[click::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot;
838+ res[click::Query::ResultKeys::INSTALLED] = true;
839+ res[click::Query::ResultKeys::VERSION] = a.version;
840+ replyProxy->push(res);
841+ }
842+}
843+
844+struct click::Query::Private
845+{
846+ Private(const unity::scopes::CannedQuery& query, click::Index& index, const scopes::SearchMetadata& metadata)
847+ : query(query),
848+ index(index),
849+ meta(metadata)
850+ {
851+ }
852+ unity::scopes::CannedQuery query;
853+ click::Index& index;
854+ scopes::SearchMetadata meta;
855+};
856+
857+click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata)
858+ : impl(new Private(query, index, metadata))
859+{
860+}
861+
862+void click::Query::cancelled()
863+{
864+ qDebug() << "cancelling search of" << QString::fromStdString(impl->query.query_string());
865+}
866+
867+click::Query::~Query()
868+{
869+ qDebug() << "destroying search";
870+}
871+
872+namespace
873+{
874+click::Interface& clickInterfaceInstance()
875+{
876+ static QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
877+ static click::Interface iface(keyFileLocator);
878+
879+ return iface;
880+}
881+
882+}
883+
884+void click::Query::add_fake_store_app(scopes::SearchReplyProxy const& searchReply)
885+{
886+ static const std::string title = _("Get more apps in Ubuntu store");
887+ auto name = title;
888+
889+ std::string query = impl->query.query_string();
890+ std::transform(query.begin(), query.end(), query.begin(), ::tolower);
891+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
892+ if (query.empty() || name.find(query) != std::string::npos)
893+ {
894+ scopes::CategoryRenderer rdr(CATEGORY_STORE);
895+ auto cat = searchReply->register_category("store", "", "", rdr);
896+
897+ static const unity::scopes::CannedQuery store_scope("com.canonical.scopes.clickstore");
898+
899+ scopes::CategorisedResult res(cat);
900+ res.set_title(title);
901+ res.set_art(STORE_DATA_DIR "/apps-scope.svg");
902+ res.set_uri(store_scope.to_uri());
903+ res[click::Query::ResultKeys::NAME] = title;
904+ res[click::Query::ResultKeys::DESCRIPTION] = "";
905+ res[click::Query::ResultKeys::MAIN_SCREENSHOT] = "";
906+ res[click::Query::ResultKeys::INSTALLED] = true;
907+ res[click::Query::ResultKeys::VERSION] = "";
908+ searchReply->push(res);
909+ }
910+}
911+
912+void click::Query::run(scopes::SearchReplyProxy const& searchReply)
913+{
914+ auto query = impl->query.query_string();
915+ std::string categoryTemplate = CATEGORY_APPS_SEARCH;
916+ if (query.empty()) {
917+ categoryTemplate = CATEGORY_APPS_DISPLAY;
918+ }
919+ auto localResults = clickInterfaceInstance().find_installed_apps(
920+ query);
921+
922+ // Sort applications so that newest come first.
923+ std::sort(localResults.begin(), localResults.end(), [](const Application& a, const Application& b) {
924+ return a.installed_time > b.installed_time;
925+ });
926+
927+ push_local_results(
928+ searchReply,
929+ localResults,
930+ categoryTemplate);
931+
932+ add_fake_store_app(searchReply);
933+}
934
935=== added file 'scope/clickapps/apps-query.h'
936--- scope/clickapps/apps-query.h 1970-01-01 00:00:00 +0000
937+++ scope/clickapps/apps-query.h 2014-06-06 00:43:07 +0000
938@@ -0,0 +1,82 @@
939+/*
940+ * Copyright (C) 2014 Canonical Ltd.
941+ *
942+ * This program is free software: you can redistribute it and/or modify it
943+ * under the terms of the GNU General Public License version 3, as published
944+ * by the Free Software Foundation.
945+ *
946+ * This program is distributed in the hope that it will be useful, but
947+ * WITHOUT ANY WARRANTY; without even the implied warranties of
948+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
949+ * PURPOSE. See the GNU General Public License for more details.
950+ *
951+ * You should have received a copy of the GNU General Public License along
952+ * with this program. If not, see <http://www.gnu.org/licenses/>.
953+ *
954+ * In addition, as a special exception, the copyright holders give
955+ * permission to link the code of portions of this program with the
956+ * OpenSSL library under certain conditions as described in each
957+ * individual source file, and distribute linked combinations
958+ * including the two.
959+ * You must obey the GNU General Public License in all respects
960+ * for all of the code used other than OpenSSL. If you modify
961+ * file(s) with this exception, you may extend this exception to your
962+ * version of the file(s), but you are not obligated to do so. If you
963+ * do not wish to do so, delete this exception statement from your
964+ * version. If you delete this exception statement from all source
965+ * files in the program, then also delete it here.
966+ */
967+
968+#ifndef APPS_QUERY_H
969+#define APPS_QUERY_H
970+
971+
972+#include <unity/scopes/SearchQueryBase.h>
973+
974+namespace scopes = unity::scopes;
975+
976+#include <QSharedPointer>
977+#include <set>
978+
979+
980+namespace click
981+{
982+
983+class Application;
984+class Index;
985+
986+class Query : public scopes::SearchQueryBase
987+{
988+public:
989+ struct ResultKeys
990+ {
991+ ResultKeys() = delete;
992+
993+ constexpr static const char* NAME{"name"};
994+ constexpr static const char* DESCRIPTION{"description"};
995+ constexpr static const char* MAIN_SCREENSHOT{"main_screenshot"};
996+ constexpr static const char* INSTALLED{"installed"};
997+ constexpr static const char* DOWNLOAD_URL{"download_url"};
998+ constexpr static const char* VERSION{"version"};
999+ };
1000+
1001+ Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata);
1002+ virtual ~Query();
1003+
1004+ virtual void cancelled() override;
1005+
1006+ virtual void run(scopes::SearchReplyProxy const& reply) override;
1007+
1008+protected:
1009+ virtual void add_fake_store_app(scopes::SearchReplyProxy const &replyProxy);
1010+ virtual void push_local_results(scopes::SearchReplyProxy const &replyProxy,
1011+ std::vector<click::Application> const &apps,
1012+ std::string& categoryTemplate);
1013+
1014+private:
1015+ struct Private;
1016+ QSharedPointer<Private> impl;
1017+};
1018+}
1019+
1020+#endif // CLICK_QUERY_H
1021
1022=== added file 'scope/clickapps/apps-scope.cpp'
1023--- scope/clickapps/apps-scope.cpp 1970-01-01 00:00:00 +0000
1024+++ scope/clickapps/apps-scope.cpp 2014-06-06 00:43:07 +0000
1025@@ -0,0 +1,158 @@
1026+/*
1027+ * Copyright (C) 2014 Canonical Ltd.
1028+ *
1029+ * This program is free software: you can redistribute it and/or modify it
1030+ * under the terms of the GNU General Public License version 3, as published
1031+ * by the Free Software Foundation.
1032+ *
1033+ * This program is distributed in the hope that it will be useful, but
1034+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1035+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1036+ * PURPOSE. See the GNU General Public License for more details.
1037+ *
1038+ * You should have received a copy of the GNU General Public License along
1039+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1040+ *
1041+ * In addition, as a special exception, the copyright holders give
1042+ * permission to link the code of portions of this program with the
1043+ * OpenSSL library under certain conditions as described in each
1044+ * individual source file, and distribute linked combinations
1045+ * including the two.
1046+ * You must obey the GNU General Public License in all respects
1047+ * for all of the code used other than OpenSSL. If you modify
1048+ * file(s) with this exception, you may extend this exception to your
1049+ * version of the file(s), but you are not obligated to do so. If you
1050+ * do not wish to do so, delete this exception statement from your
1051+ * version. If you delete this exception statement from all source
1052+ * files in the program, then also delete it here.
1053+ */
1054+
1055+#include <click/qtbridge.h>
1056+#include <click/preview.h>
1057+#include <click/interface.h>
1058+#include <click/scope_activation.h>
1059+
1060+#include <QSharedPointer>
1061+
1062+#include <click/key_file_locator.h>
1063+#include <click/network_access_manager.h>
1064+#include <click/click-i18n.h>
1065+#include <unity/scopes/CannedQuery.h>
1066+
1067+#include "apps-scope.h"
1068+#include "apps-query.h"
1069+
1070+namespace
1071+{
1072+click::Interface& clickInterfaceInstance()
1073+{
1074+ static QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
1075+ static click::Interface iface(keyFileLocator);
1076+ return iface;
1077+}
1078+}
1079+
1080+click::Scope::Scope()
1081+{
1082+ nam.reset(new click::network::AccessManager());
1083+ client.reset(new click::web::Client(nam));
1084+ index.reset(new click::Index(client));
1085+}
1086+
1087+click::Scope::~Scope()
1088+{
1089+}
1090+
1091+int click::Scope::start(std::string const&, scopes::RegistryProxy const&)
1092+{
1093+ setlocale(LC_ALL, "");
1094+ bindtextdomain(GETTEXT_PACKAGE, GETTEXT_LOCALEDIR);
1095+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1096+
1097+ return VERSION;
1098+}
1099+
1100+void click::Scope::run()
1101+{
1102+ static const int zero = 0;
1103+ auto emptyCb = [this]()
1104+ {
1105+ };
1106+
1107+ qt::core::world::build_and_run(zero, nullptr, emptyCb);
1108+}
1109+
1110+void click::Scope::stop()
1111+{
1112+ qt::core::world::destroy();
1113+}
1114+
1115+scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata)
1116+{
1117+ return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, metadata));
1118+}
1119+
1120+
1121+unity::scopes::PreviewQueryBase::UPtr click::Scope::preview(const unity::scopes::Result& result,
1122+ const unity::scopes::ActionMetadata& metadata) {
1123+ qDebug() << "Scope::preview() called.";
1124+ return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam)};
1125+}
1126+
1127+
1128+unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& /* widget_id */, std::string const& action_id)
1129+{
1130+ if (action_id == click::Preview::Actions::CONFIRM_UNINSTALL) {
1131+ const unity::scopes::CannedQuery cquery("clickscope");
1132+ return scopes::ActivationQueryBase::UPtr(new PerformUninstallAction(result, unity::scopes::ActivationResponse(cquery)));
1133+ }
1134+
1135+ auto activation = new ScopeActivation();
1136+ qDebug() << "perform_action called with action_id" << QString().fromStdString(action_id);
1137+
1138+ if (action_id == click::Preview::Actions::UNINSTALL_CLICK) {
1139+ activation->setHint(click::Preview::Actions::UNINSTALL_CLICK, unity::scopes::Variant(true));
1140+ activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview);
1141+ } else if (action_id == click::Preview::Actions::CLOSE_PREVIEW) {
1142+ activation->setHint(click::Preview::Actions::CLOSE_PREVIEW, unity::scopes::Variant(true));
1143+ activation->setStatus(unity::scopes::ActivationResponse::Status::ShowPreview);
1144+ } else if (action_id == click::Preview::Actions::RATED) {
1145+ scopes::VariantMap rating_info = metadata.scope_data().get_dict();
1146+ // Cast to int because widget gives us double, which is wrong.
1147+ int rating = ((int)rating_info["rating"].get_double());
1148+ std::string review_text = rating_info["review"].get_string();
1149+
1150+ // We have to get the values and then set them as hints here, to be
1151+ // able to pass them on to the Preview, which actually makes the
1152+ // call to submit.
1153+ activation->setHint("rating", scopes::Variant(rating));
1154+ activation->setHint("review", scopes::Variant(review_text));
1155+ activation->setHint(click::Preview::Actions::RATED,
1156+ scopes::Variant(true));
1157+ activation->setStatus(scopes::ActivationResponse::Status::ShowPreview);
1158+ }
1159+ return scopes::ActivationQueryBase::UPtr(activation);
1160+}
1161+
1162+#define EXPORT __attribute__ ((visibility ("default")))
1163+
1164+extern "C"
1165+{
1166+
1167+ EXPORT
1168+ unity::scopes::ScopeBase*
1169+ // cppcheck-suppress unusedFunction
1170+ UNITY_SCOPE_CREATE_FUNCTION()
1171+ {
1172+ return new click::Scope();
1173+ }
1174+
1175+ EXPORT
1176+ void
1177+ // cppcheck-suppress unusedFunction
1178+ UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
1179+ {
1180+ delete scope_base;
1181+ }
1182+
1183+}
1184
1185=== added file 'scope/clickapps/apps-scope.h'
1186--- scope/clickapps/apps-scope.h 1970-01-01 00:00:00 +0000
1187+++ scope/clickapps/apps-scope.h 2014-06-06 00:43:07 +0000
1188@@ -0,0 +1,71 @@
1189+/*
1190+ * Copyright (C) 2014 Canonical Ltd.
1191+ *
1192+ * This program is free software: you can redistribute it and/or modify it
1193+ * under the terms of the GNU General Public License version 3, as published
1194+ * by the Free Software Foundation.
1195+ *
1196+ * This program is distributed in the hope that it will be useful, but
1197+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1198+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1199+ * PURPOSE. See the GNU General Public License for more details.
1200+ *
1201+ * You should have received a copy of the GNU General Public License along
1202+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1203+ *
1204+ * In addition, as a special exception, the copyright holders give
1205+ * permission to link the code of portions of this program with the
1206+ * OpenSSL library under certain conditions as described in each
1207+ * individual source file, and distribute linked combinations
1208+ * including the two.
1209+ * You must obey the GNU General Public License in all respects
1210+ * for all of the code used other than OpenSSL. If you modify
1211+ * file(s) with this exception, you may extend this exception to your
1212+ * version of the file(s), but you are not obligated to do so. If you
1213+ * do not wish to do so, delete this exception statement from your
1214+ * version. If you delete this exception statement from all source
1215+ * files in the program, then also delete it here.
1216+ */
1217+
1218+#ifndef APPS_SCOPE_H
1219+#define APPS_SCOPE_H
1220+
1221+#include <click/network_access_manager.h>
1222+#include <click/webclient.h>
1223+
1224+#include <unity/scopes/ScopeBase.h>
1225+#include <unity/scopes/QueryBase.h>
1226+#include <unity/scopes/ActivationQueryBase.h>
1227+
1228+#include <click/index.h>
1229+
1230+namespace scopes = unity::scopes;
1231+
1232+namespace click
1233+{
1234+class Scope : public scopes::ScopeBase
1235+{
1236+public:
1237+ Scope();
1238+ ~Scope();
1239+
1240+ virtual int start(std::string const&, scopes::RegistryProxy const&) override;
1241+
1242+ virtual void run() override;
1243+ virtual void stop() override;
1244+
1245+ virtual scopes::SearchQueryBase::UPtr search(scopes::CannedQuery const& q, scopes::SearchMetadata const&) override;
1246+ unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&,
1247+ const unity::scopes::ActionMetadata&) override;
1248+
1249+ virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) override;
1250+
1251+private:
1252+ QSharedPointer<click::network::AccessManager> nam;
1253+ QSharedPointer<click::web::Client> client;
1254+ QSharedPointer<click::Index> index;
1255+
1256+ std::string installApplication(unity::scopes::Result const& result);
1257+};
1258+}
1259+#endif // CLICK_SCOPE_H
1260
1261=== renamed directory 'scope/click' => 'scope/clickstore'
1262=== modified file 'scope/clickstore/CMakeLists.txt'
1263--- scope/click/CMakeLists.txt 2014-05-26 14:02:45 +0000
1264+++ scope/clickstore/CMakeLists.txt 2014-06-06 00:43:07 +0000
1265@@ -8,19 +8,20 @@
1266 -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\"
1267 )
1268
1269-add_library(${SCOPE_LIB_UNVERSIONED} SHARED
1270- query.cpp
1271- scope.cpp
1272+add_library(${STORE_LIB_UNVERSIONED} SHARED
1273+ store-query.cpp
1274+ store-scope.cpp
1275 )
1276+set_target_properties(${STORE_LIB_UNVERSIONED} PROPERTIES PREFIX "")
1277
1278 include_directories(
1279 ${CMAKE_SOURCE_DIR}/libclickscope
1280 ${JSON_CPP_INCLUDE_DIRS}
1281 )
1282
1283-qt5_use_modules (${SCOPE_LIB_UNVERSIONED} Network)
1284+qt5_use_modules (${STORE_LIB_UNVERSIONED} Network)
1285
1286-target_link_libraries (${SCOPE_LIB_UNVERSIONED}
1287+target_link_libraries (${STORE_LIB_UNVERSIONED}
1288 ${SCOPE_LIB_NAME}
1289 ${JSON_CPP_LDFLAGS}
1290 ${UNITY_SCOPES_LDFLAGS}
1291@@ -30,6 +31,6 @@
1292 )
1293
1294 install(
1295- TARGETS ${SCOPE_LIB_UNVERSIONED}
1296- LIBRARY DESTINATION "${SCOPE_LIB_DIR}"
1297+ TARGETS ${STORE_LIB_UNVERSIONED}
1298+ LIBRARY DESTINATION "${STORE_LIB_DIR}"
1299 )
1300
1301=== renamed file 'scope/click/query.cpp' => 'scope/clickstore/store-query.cpp'
1302--- scope/click/query.cpp 2014-05-26 14:02:45 +0000
1303+++ scope/clickstore/store-query.cpp 2014-06-06 00:43:07 +0000
1304@@ -28,9 +28,9 @@
1305 */
1306
1307 #include <click/application.h>
1308-#include "query.h"
1309+#include <click/interface.h>
1310+#include "store-query.h"
1311 #include <click/qtbridge.h>
1312-#include <click/interface.h>
1313
1314 #include <click/key_file_locator.h>
1315
1316@@ -48,6 +48,8 @@
1317
1318 #include <click/click-i18n.h>
1319
1320+using namespace click;
1321+
1322 namespace
1323 {
1324
1325@@ -60,10 +62,10 @@
1326 },
1327 "components" : {
1328 "title" : "title",
1329+ "subtitle": "subtitle",
1330 "art" : {
1331 "field": "art",
1332- "aspect-ratio": 1.6,
1333- "fill-mode": "fit"
1334+ "aspect-ratio": 1.13
1335 }
1336 }
1337 }
1338@@ -89,32 +91,6 @@
1339
1340 }
1341
1342-void click::Query::push_local_results(scopes::SearchReplyProxy const &replyProxy,
1343- std::vector<click::Application> const &apps,
1344- std::string &categoryTemplate)
1345-{
1346- scopes::CategoryRenderer rdr(categoryTemplate);
1347- auto cat = replyProxy->register_category("local", _("My apps"), "", rdr);
1348-
1349- // cat might be null when the underlying query got cancelled.
1350- if (!cat)
1351- return;
1352-
1353- for(const auto & a: apps)
1354- {
1355- scopes::CategorisedResult res(cat);
1356- res.set_title(a.title);
1357- res.set_art(a.icon_url);
1358- res.set_uri(a.url);
1359- res[click::Query::ResultKeys::NAME] = a.name;
1360- res[click::Query::ResultKeys::DESCRIPTION] = a.description;
1361- res[click::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot;
1362- res[click::Query::ResultKeys::INSTALLED] = true;
1363- res[click::Query::ResultKeys::VERSION] = a.version;
1364- replyProxy->push(res);
1365- }
1366-}
1367-
1368 struct click::Query::Private
1369 {
1370 Private(const unity::scopes::CannedQuery& query, click::Index& index, const scopes::SearchMetadata& metadata)
1371@@ -145,9 +121,7 @@
1372 impl->search_operation.cancel();
1373 }
1374
1375-namespace
1376-{
1377-click::Interface& clickInterfaceInstance()
1378+click::Interface& click::Query::clickInterfaceInstance()
1379 {
1380 static QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
1381 static click::Interface iface(keyFileLocator);
1382@@ -155,8 +129,6 @@
1383 return iface;
1384 }
1385
1386-}
1387-
1388 bool click::Query::push_result(scopes::SearchReplyProxy const& searchReply, const scopes::CategorisedResult &res)
1389 {
1390 return searchReply->push(res);
1391@@ -184,7 +156,7 @@
1392 }
1393
1394 void click::Query::add_available_apps(scopes::SearchReplyProxy const& searchReply,
1395- const std::set<std::string>& locallyInstalledApps,
1396+ const PackageNames& installedPackages,
1397 const std::string& categoryTemplate)
1398 {
1399 scopes::CategoryRenderer categoryRenderer(categoryTemplate);
1400@@ -197,7 +169,7 @@
1401
1402 run_under_qt([=]()
1403 {
1404- auto search_cb = [this, searchReply, category, locallyInstalledApps](PackageList packages) {
1405+ auto search_cb = [this, searchReply, category, installedPackages](PackageList packages) {
1406 qDebug("search callback");
1407
1408 // handle packages data
1409@@ -205,15 +177,13 @@
1410 qDebug() << "pushing result" << QString::fromStdString(p.name);
1411 try {
1412 scopes::CategorisedResult res(category);
1413- if (locallyInstalledApps.count(p.name) > 0) {
1414- qDebug() << "already installed" << QString::fromStdString(p.name);
1415- continue;
1416- }
1417 res.set_title(p.title);
1418 res.set_art(p.icon_url);
1419 res.set_uri(p.url);
1420 res[click::Query::ResultKeys::NAME] = p.name;
1421- res[click::Query::ResultKeys::INSTALLED] = false;
1422+ bool installed = (installedPackages.count(p.name) > 0);
1423+ res[click::Query::ResultKeys::INSTALLED] = installed;
1424+ res["subtitle"] = installed ? "✔ Installed" : "FREE";
1425
1426 this->push_result(searchReply, res);
1427 } catch(const std::exception& e){
1428@@ -231,6 +201,23 @@
1429 });
1430 }
1431
1432+PackageNames click::Query::get_installed_packages()
1433+{
1434+ std::promise<PackageNames> installed_promise;
1435+ std::future<PackageNames> installed_future = installed_promise.get_future();
1436+
1437+ run_under_qt([&]()
1438+ {
1439+ clickInterfaceInstance().get_installed_packagenames(
1440+ [&installed_promise](PackageNames installedPackages, InterfaceError){
1441+ installed_promise.set_value(installedPackages);
1442+ });
1443+ });
1444+
1445+ return installed_future.get();
1446+}
1447+
1448+
1449 void click::Query::run(scopes::SearchReplyProxy const& searchReply)
1450 {
1451 auto query = impl->query.query_string();
1452@@ -238,18 +225,6 @@
1453 if (query.empty()) {
1454 categoryTemplate = CATEGORY_APPS_DISPLAY;
1455 }
1456- auto localResults = clickInterfaceInstance().find_installed_apps(
1457- query);
1458-
1459- push_local_results(
1460- searchReply,
1461- localResults,
1462- categoryTemplate);
1463-
1464- std::set<std::string> locallyInstalledApps;
1465- for(const auto& app : localResults) {
1466- locallyInstalledApps.insert(app.name);
1467- }
1468
1469 static const std::string no_net_hint("no-internet");
1470 if (impl->meta.contains_hint(no_net_hint))
1471@@ -259,8 +234,7 @@
1472 {
1473 return;
1474 }
1475-
1476 }
1477
1478- add_available_apps(searchReply, locallyInstalledApps, categoryTemplate);
1479+ add_available_apps(searchReply, get_installed_packages(), categoryTemplate);
1480 }
1481
1482=== renamed file 'scope/click/query.h' => 'scope/clickstore/store-query.h'
1483--- scope/click/query.h 2014-05-14 18:34:20 +0000
1484+++ scope/clickstore/store-query.h 2014-06-06 00:43:07 +0000
1485@@ -27,8 +27,8 @@
1486 * files in the program, then also delete it here.
1487 */
1488
1489-#ifndef CLICK_QUERY_H
1490-#define CLICK_QUERY_H
1491+#ifndef STORE_QUERY_H
1492+#define STORE_QUERY_H
1493
1494
1495 #include <unity/scopes/SearchQueryBase.h>
1496@@ -36,8 +36,8 @@
1497 namespace scopes = unity::scopes;
1498
1499 #include <QSharedPointer>
1500-#include <set>
1501-
1502+#include <unordered_set>
1503+#include <click/interface.h>
1504
1505 namespace click
1506 {
1507@@ -78,12 +78,11 @@
1508 virtual void run(scopes::SearchReplyProxy const& reply) override;
1509
1510 protected:
1511- virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const std::set<std::string> &locallyInstalledApps, const std::string &category);
1512+ virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const PackageNames &installedPackages, const std::string &category);
1513+ virtual click::Interface& clickInterfaceInstance();
1514+ virtual PackageNames get_installed_packages();
1515 virtual bool push_result(const scopes::SearchReplyProxy &searchReply, scopes::CategorisedResult const& res);
1516 virtual void finished(const scopes::SearchReplyProxy &searchReply);
1517- virtual void push_local_results(scopes::SearchReplyProxy const &replyProxy,
1518- std::vector<click::Application> const &apps,
1519- std::string& categoryTemplate);
1520 virtual scopes::Category::SCPtr register_category(scopes::SearchReplyProxy const& searchReply,
1521 std::string const& id,
1522 std::string const& title,
1523
1524=== renamed file 'scope/click/scope.cpp' => 'scope/clickstore/store-scope.cpp'
1525--- scope/click/scope.cpp 2014-05-27 08:30:21 +0000
1526+++ scope/clickstore/store-scope.cpp 2014-06-06 00:43:07 +0000
1527@@ -28,8 +28,8 @@
1528 */
1529
1530 #include <click/qtbridge.h>
1531-#include "scope.h"
1532-#include "query.h"
1533+#include "store-scope.h"
1534+#include "store-query.h"
1535 #include <click/preview.h>
1536 #include <click/interface.h>
1537 #include <click/scope_activation.h>
1538
1539=== renamed file 'scope/click/scope.h' => 'scope/clickstore/store-scope.h'
1540=== modified file 'scope/tests/CMakeLists.txt'
1541--- scope/tests/CMakeLists.txt 2014-05-26 14:27:31 +0000
1542+++ scope/tests/CMakeLists.txt 2014-06-06 00:43:07 +0000
1543@@ -22,7 +22,7 @@
1544 qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test)
1545
1546 target_link_libraries(${CLICKSCOPE_TESTS_TARGET}
1547- ${SCOPE_LIB_UNVERSIONED}
1548+ ${STORE_LIB_UNVERSIONED}
1549 ${SCOPE_LIB_NAME}
1550
1551 ${UNITY_SCOPES_LDFLAGS}
1552
1553=== modified file 'scope/tests/click_interface_tool/CMakeLists.txt'
1554--- scope/tests/click_interface_tool/CMakeLists.txt 2014-05-13 19:32:29 +0000
1555+++ scope/tests/click_interface_tool/CMakeLists.txt 2014-06-06 00:43:07 +0000
1556@@ -10,5 +10,5 @@
1557 )
1558
1559 target_link_libraries (${CLICK_INTERFACE_TOOL_TARGET}
1560- ${SCOPE_LIB_UNVERSIONED}
1561+ ${STORE_LIB_UNVERSIONED}
1562 )
1563
1564=== modified file 'scope/tests/click_interface_tool/click_interface_tool.cpp'
1565--- scope/tests/click_interface_tool/click_interface_tool.cpp 2014-05-13 19:32:29 +0000
1566+++ scope/tests/click_interface_tool/click_interface_tool.cpp 2014-06-06 00:43:07 +0000
1567@@ -51,8 +51,8 @@
1568
1569 QObject::connect(&timer, &QTimer::timeout, [&]() {
1570 ci.get_dotdesktop_filename(std::string(argv[1]),
1571- [&a] (std::string val, click::ManifestError error){
1572- if (error == click::ManifestError::NoError) {
1573+ [&a] (std::string val, click::InterfaceError error){
1574+ if (error == click::InterfaceError::NoError) {
1575 std::cout << " Success, got dotdesktop:" << val << std::endl;
1576 } else {
1577 std::cout << " Error:" << val << std::endl;
1578
1579=== modified file 'scope/tests/download_manager_tool/CMakeLists.txt'
1580--- scope/tests/download_manager_tool/CMakeLists.txt 2014-02-27 17:24:07 +0000
1581+++ scope/tests/download_manager_tool/CMakeLists.txt 2014-06-06 00:43:07 +0000
1582@@ -10,5 +10,5 @@
1583 )
1584
1585 target_link_libraries (${DOWNLOAD_MANAGER_TOOL_TARGET}
1586- ${SCOPE_LIB_UNVERSIONED}
1587+ ${STORE_LIB_UNVERSIONED}
1588 )
1589
1590=== modified file 'scope/tests/integration/CMakeLists.txt'
1591--- scope/tests/integration/CMakeLists.txt 2014-02-27 17:24:07 +0000
1592+++ scope/tests/integration/CMakeLists.txt 2014-06-06 00:43:07 +0000
1593@@ -17,7 +17,7 @@
1594 qt5_use_modules(${INTEGRATION_TARGET} Core DBus Network Test)
1595
1596 target_link_libraries (${INTEGRATION_TARGET}
1597- ${SCOPE_LIB_UNVERSIONED}
1598+ ${STORE_LIB_UNVERSIONED}
1599
1600 gmock
1601 gmock_main
1602
1603=== modified file 'scope/tests/test_query.cpp'
1604--- scope/tests/test_query.cpp 2014-05-21 13:42:45 +0000
1605+++ scope/tests/test_query.cpp 2014-06-06 00:43:07 +0000
1606@@ -34,7 +34,7 @@
1607 #include <gmock/gmock.h>
1608
1609 #include "click/qtbridge.h"
1610-#include "click/query.h"
1611+#include "clickstore/store-query.h"
1612 #include "click/index.h"
1613 #include "click/application.h"
1614
1615@@ -47,6 +47,7 @@
1616 #include <unity/scopes/SearchReply.h>
1617
1618 using namespace ::testing;
1619+using namespace click;
1620
1621 namespace
1622 {
1623@@ -98,18 +99,20 @@
1624
1625 }
1626 void wrap_add_available_apps(const scopes::SearchReplyProxy &searchReply,
1627- const std::set<std::string> &locallyInstalledApps,
1628+ const PackageNames &installedPackages,
1629 const std::string& categoryTemplate)
1630 {
1631- add_available_apps(searchReply, locallyInstalledApps, categoryTemplate);
1632+ add_available_apps(searchReply, installedPackages, categoryTemplate);
1633 }
1634 MOCK_METHOD2(push_result, bool(scopes::SearchReplyProxy const&, scopes::CategorisedResult const&));
1635+ MOCK_METHOD0(clickInterfaceInstance, click::Interface&());
1636 MOCK_METHOD1(finished, void(scopes::SearchReplyProxy const&));
1637 MOCK_METHOD5(register_category, scopes::Category::SCPtr(const scopes::SearchReplyProxy &searchReply,
1638 const std::string &id,
1639 const std::string &title,
1640 const std::string &icon,
1641 const scopes::CategoryRenderer &renderer_template));
1642+ using click::Query::get_installed_packages; // allow tests to access protected method
1643 };
1644
1645 class MockQueryRun : public MockQueryBase {
1646@@ -121,11 +124,12 @@
1647 }
1648 MOCK_METHOD3(add_available_apps,
1649 void(scopes::SearchReplyProxy const&searchReply,
1650- const std::set<std::string> &locallyInstalledApps,
1651+ const PackageNames &locallyInstalledApps,
1652 const std::string& categoryTemplate));
1653 MOCK_METHOD3(push_local_results, void(scopes::SearchReplyProxy const &replyProxy,
1654 std::vector<click::Application> const &apps,
1655 std::string& categoryTemplate));
1656+ MOCK_METHOD0(get_installed_packages, PackageNames());
1657 };
1658
1659 class FakeCategory : public scopes::Category
1660@@ -144,7 +148,7 @@
1661 {
1662 MockIndex mock_index;
1663 scopes::SearchMetadata metadata("en_EN", "phone");
1664- std::set<std::string> no_installed_packages;
1665+ PackageNames no_installed_packages;
1666 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1667 MockQuery q(query, mock_index, metadata);
1668 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)).Times(1);
1669@@ -163,7 +167,7 @@
1670 };
1671 MockIndex mock_index(packages);
1672 scopes::SearchMetadata metadata("en_EN", "phone");
1673- std::set<std::string> no_installed_packages;
1674+ PackageNames no_installed_packages;
1675 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1676 MockQuery q(query, mock_index, metadata);
1677 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1678@@ -185,7 +189,7 @@
1679 };
1680 MockIndex mock_index(packages);
1681 scopes::SearchMetadata metadata("en_EN", "phone");
1682- std::set<std::string> no_installed_packages;
1683+ PackageNames no_installed_packages;
1684 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1685 MockQuery q(query, mock_index, metadata);
1686 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1687@@ -206,7 +210,7 @@
1688 };
1689 MockIndex mock_index(packages);
1690 scopes::SearchMetadata metadata("en_EN", "phone");
1691- std::set<std::string> no_installed_packages;
1692+ PackageNames no_installed_packages;
1693 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1694 MockQuery q(query, mock_index, metadata);
1695 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)).Times(0);
1696@@ -225,39 +229,94 @@
1697 };
1698 MockIndex mock_index(packages);
1699 scopes::SearchMetadata metadata("en_EN", "phone");
1700- std::set<std::string> no_installed_packages;
1701+ PackageNames no_installed_packages;
1702 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1703 MockQueryRun q(query, mock_index, metadata);
1704 auto reply = scopes::SearchReplyProxy();
1705- EXPECT_CALL(q, push_local_results(_, _, _));
1706+ EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages));
1707 EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _));
1708
1709 q.run(reply);
1710 }
1711
1712 MATCHER_P(HasPackageName, n, "") { return arg[click::Query::ResultKeys::NAME].get_string() == n; }
1713-
1714-TEST(QueryTest, testDuplicatesFilteredOnPackageName)
1715-{
1716- click::PackageList packages {
1717- {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1718- {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1719- };
1720- MockIndex mock_index(packages);
1721- scopes::SearchMetadata metadata("en_EN", "phone");
1722- std::set<std::string> one_installed_package {
1723- "org.example.app2"
1724- };
1725- const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1726- MockQuery q(query, mock_index, metadata);
1727- EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1728-
1729- scopes::CategoryRenderer renderer("{}");
1730- auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1731- EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1732-
1733- scopes::SearchReplyProxy reply;
1734- auto expected_name = packages.front().name;
1735- EXPECT_CALL(q, push_result(_, HasPackageName(expected_name)));
1736- q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1737+MATCHER_P(IsInstalled, b, "") { return arg[click::Query::ResultKeys::INSTALLED].get_bool() == b; }
1738+
1739+TEST(QueryTest, testDuplicatesNotFilteredAnymore)
1740+{
1741+ click::PackageList packages {
1742+ {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1743+ {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1744+ };
1745+ MockIndex mock_index(packages);
1746+ scopes::SearchMetadata metadata("en_EN", "phone");
1747+ PackageNames one_installed_package {
1748+ "org.example.app2"
1749+ };
1750+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1751+ MockQuery q(query, mock_index, metadata);
1752+ EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1753+
1754+ scopes::CategoryRenderer renderer("{}");
1755+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1756+ EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1757+
1758+ scopes::SearchReplyProxy reply;
1759+ auto expected_name1 = packages.front().name;
1760+ EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1)));
1761+ auto expected_name2 = packages.back().name;
1762+ EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2)));
1763+ q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1764+}
1765+
1766+TEST(QueryTest, testInstalledPackagesFlaggedAsSuch)
1767+{
1768+ click::PackageList packages {
1769+ {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1770+ {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1771+ };
1772+ MockIndex mock_index(packages);
1773+ scopes::SearchMetadata metadata("en_EN", "phone");
1774+ PackageNames one_installed_package {
1775+ "org.example.app2"
1776+ };
1777+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1778+ MockQuery q(query, mock_index, metadata);
1779+ EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1780+
1781+ scopes::CategoryRenderer renderer("{}");
1782+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1783+ EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1784+
1785+ scopes::SearchReplyProxy reply;
1786+ EXPECT_CALL(q, push_result(_, IsInstalled(true)));
1787+ EXPECT_CALL(q, push_result(_, IsInstalled(false)));
1788+ q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1789+}
1790+
1791+class FakeInterface : public click::Interface
1792+{
1793+public:
1794+ MOCK_METHOD1(get_installed_packagenames, void(std::function<void(PackageNames, click::InterfaceError)> callback));
1795+};
1796+
1797+TEST(QueryTest, testGetInstalledPackages)
1798+{
1799+ click::PackageList uninstalled_packages {
1800+ {"name", "title", 0.0, "icon", "uri"}
1801+ };
1802+ MockIndex mock_index(uninstalled_packages);
1803+ scopes::SearchMetadata metadata("en_EN", "phone");
1804+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1805+ MockQuery q(query, mock_index, metadata);
1806+ PackageNames installed_packages{"package_1"};
1807+
1808+ FakeInterface fake_interface;
1809+ EXPECT_CALL(q, clickInterfaceInstance()).WillOnce(ReturnRef(fake_interface));
1810+ EXPECT_CALL(fake_interface, get_installed_packagenames(_)).WillOnce(Invoke(
1811+ [&](std::function<void(PackageNames, click::InterfaceError)> callback){
1812+ callback(installed_packages, click::InterfaceError::NoError);
1813+ }));
1814+
1815+ ASSERT_EQ(q.get_installed_packages(), installed_packages);
1816 }

Subscribers

People subscribed via source and target branches

to all changes: