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

Proposed by Alejandro J. Cura
Status: Rejected
Rejected by: Alejandro J. Cura
Proposed branch: lp:~alecu/unity-scope-click/uninstall-scopes
Merge into: lp:unity-scope-click/devel
Prerequisite: lp:~stolowski/unity-scope-click/two-scopes
Diff against target: 1327 lines (+446/-175)
16 files modified
libclickscope/click/index.cpp (+3/-3)
libclickscope/click/index.h (+1/-1)
libclickscope/click/interface.cpp (+73/-22)
libclickscope/click/interface.h (+13/-4)
libclickscope/click/package.cpp (+4/-7)
libclickscope/click/package.h (+24/-4)
libclickscope/click/preview.cpp (+43/-32)
libclickscope/click/preview.h (+3/-2)
libclickscope/tests/fake_json.h (+28/-0)
libclickscope/tests/test_index.cpp (+12/-12)
libclickscope/tests/test_interface.cpp (+96/-18)
scope/clickstore/store-query.cpp (+37/-25)
scope/clickstore/store-query.h (+6/-3)
scope/tests/click_interface_tool/click_interface_tool.cpp (+2/-2)
scope/tests/integration/webclient_integration.cpp (+2/-2)
scope/tests/test_query.cpp (+99/-38)
To merge this branch: bzr merge lp:~alecu/unity-scope-click/uninstall-scopes
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Disapprove
PS Jenkins bot (community) continuous-integration Approve
Paweł Stołowski (community) Needs Fixing
Review via email: mp+222273@code.launchpad.net

This proposal supersedes a proposal from 2014-06-02.

Commit message

Allow installed packages in the Available category and show the installed status on results.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
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

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

24 + } catch (...) {
25 + callback(ManifestList(), InterfaceError::ParseError);
26 + }

Can we get a specific exception here to print any useful info? In any case, how about printing a warning with as much info as possible?

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

+PackageNames package_names_from_stdout(std::string stdout_data)

Could you please make it const std::string& ?

review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

42 + while (linestart < eof) {

I think you could greatly simplify this loop with a loop around std::getline() with '\t' as a delimiter. But that's just suggestion, I'm not going to block on this.

Revision history for this message
Paweł Stołowski (stolowski) wrote :

I'm getting the following exception in scoperegistry.log when trying to uninstall Soundcloud scope with this branch:

PreviewQueryObject::run(): unity::LogicException: Variant does not contain a string value:
    boost::bad_get: failed value get using boost::get

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

> I'm getting the following exception in scoperegistry.log when trying to
> uninstall Soundcloud scope with this branch:
>
> PreviewQueryObject::run(): unity::LogicException: Variant does not contain a
> string value:
> boost::bad_get: failed value get using boost::get

Same happens when trying to uninstall regular apps via Store (ones that are installed). I'm only able to uninstall apps from the Apps scope.

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

> 24 + } catch (...) {
> 25 + callback(ManifestList(), InterfaceError::ParseError);
> 26 + }
>
> Can we get a specific exception here to print any useful info? In any case,
> how about printing a warning with as much info as possible?

I'm now printing the error codes and the stdout/stderr as appropriate.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

> 42 + while (linestart < eof) {
>
> I think you could greatly simplify this loop with a loop around std::getline()
> with '\t' as a delimiter. But that's just suggestion, I'm not going to block
> on this.

I've simplified the code as suggested.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

> +PackageNames package_names_from_stdout(std::string stdout_data)
>
> Could you please make it const std::string& ?

fixed.

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

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
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

Revision history for this message
Alejandro J. Cura (alecu) wrote :

> > I'm getting the following exception in scoperegistry.log when trying to
> > uninstall Soundcloud scope with this branch:
> >
> > PreviewQueryObject::run(): unity::LogicException: Variant does not contain a
> > string value:
> > boost::bad_get: failed value get using boost::get
>
> Same happens when trying to uninstall regular apps via Store (ones that are
> installed). I'm only able to uninstall apps from the Apps scope.

Should be fixed now. Will test on the device when jenkins builds the .debs.

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

Tested on mako, seems to be working ok.
Found a bug where after uninstall the scope is not refreshed, and will open a new bug for that.

Revision history for this message
Alejandro J. Cura (alecu) wrote :

Here's the bug for the missing refresh of results after install/uninstall: bug #1328102

Revision history for this message
Paweł Stołowski (stolowski) wrote :

Thanks, Uninstall for scopes works indeed. However, I just noticed that 'Open' button is completely broken for them.

review: Needs Fixing
296. By Alejandro J. Cura

Allow searching installed scopes

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

Use the package name for the scope id

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

merged with devel

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

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 'libclickscope/click/index.cpp'
2--- libclickscope/click/index.cpp 2014-05-26 14:02:45 +0000
3+++ libclickscope/click/index.cpp 2014-06-12 16:58:45 +0000
4@@ -110,7 +110,7 @@
5 };
6 }
7
8-click::web::Cancellable Index::search (const std::string& query, std::function<void(click::PackageList)> callback)
9+click::web::Cancellable Index::search (const std::string& query, std::function<void(click::Packages)> callback)
10 {
11 click::web::CallParams params;
12 const std::string built_query(build_index_query(query));
13@@ -122,7 +122,7 @@
14 Json::Reader reader;
15 Json::Value root;
16
17- click::PackageList pl;
18+ click::Packages pl;
19 if (reader.parse(reply.toUtf8().constData(), root)) {
20 pl = click::package_list_from_json_node(root);
21 qDebug() << "found packages:" << pl.size();
22@@ -131,7 +131,7 @@
23 });
24 QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/) {
25 qDebug() << "No packages found due to network error";
26- click::PackageList pl;
27+ click::Packages pl;
28 qDebug() << "calling callback";
29 callback(pl);
30 qDebug() << " ...Done!";
31
32=== modified file 'libclickscope/click/index.h'
33--- libclickscope/click/index.h 2014-05-26 14:02:45 +0000
34+++ libclickscope/click/index.h 2014-06-12 16:58:45 +0000
35@@ -71,7 +71,7 @@
36 enum class Error {NoError, CredentialsError, NetworkError};
37 Index(const QSharedPointer<click::web::Client>& client,
38 const QSharedPointer<Configuration> configuration=QSharedPointer<Configuration>(new Configuration()));
39- virtual click::web::Cancellable search (const std::string& query, std::function<void(PackageList)> callback);
40+ virtual click::web::Cancellable search (const std::string& query, std::function<void(Packages)> callback);
41 virtual click::web::Cancellable get_details(const std::string& package_name, std::function<void(PackageDetails, Error)> callback);
42 virtual ~Index();
43
44
45=== modified file 'libclickscope/click/interface.cpp'
46--- libclickscope/click/interface.cpp 2014-06-09 16:49:10 +0000
47+++ libclickscope/click/interface.cpp 2014-06-12 16:58:45 +0000
48@@ -33,9 +33,12 @@
49 #include <QStandardPaths>
50 #include <QTimer>
51
52+#include <cstdio>
53 #include <list>
54 #include <sys/stat.h>
55 #include <map>
56+#include <sstream>
57+#include <streambuf>
58
59 #include <boost/property_tree/ptree.hpp>
60 #include <boost/property_tree/json_parser.hpp>
61@@ -44,7 +47,6 @@
62
63 #include <unity/UnityExceptions.h>
64 #include <unity/util/IniParser.h>
65-#include <sstream>
66
67 #include "interface.h"
68 #include <click/key_file_locator.h>
69@@ -333,36 +335,85 @@
70
71 BOOST_FOREACH(ptree::value_type &sv, pt.get_child("hooks"))
72 {
73- // FIXME: "primary app" for a package is not defined, we just
74- // use first one here:
75- manifest.first_app_name = sv.first;
76- break;
77+ // FIXME: "primary app or scope" for a package is not defined,
78+ // we just use first one here:
79+ auto app_name = sv.second.get("desktop", "");
80+ if (app_name != "") {
81+ manifest.first_app_name = sv.first;
82+ break;
83+ }
84+ auto scope_id = sv.second.get("scope", "");
85+ if (scope_id != "") {
86+ manifest.first_scope_id = manifest.name; // need to change this for more than one scope per click
87+ break;
88+ }
89 }
90 qDebug() << "adding manifest: " << manifest.name.c_str() << manifest.version.c_str() << manifest.first_app_name.c_str();
91
92 return manifest;
93 }
94
95-void Interface::get_manifests(std::function<void(ManifestList, ManifestError)> callback)
96+void Interface::get_manifests(std::function<void(ManifestList, InterfaceError)> callback)
97 {
98 std::string command = "click list --manifest";
99 qDebug() << "Running command:" << command.c_str();
100- run_process(command, [callback](int code, const std::string& stdout_data, const std::string&) {
101+ run_process(command, [callback](int code, const std::string& stdout_data, const std::string& stderr_data) {
102 if (code == 0) {
103 try {
104 ManifestList manifests = manifest_list_from_json(stdout_data);
105- callback(manifests, ManifestError::NoError);
106- } catch (...) {
107- callback(ManifestList(), ManifestError::ParseError);
108- }
109- } else {
110- callback(ManifestList(), ManifestError::CallError);
111+ callback(manifests, InterfaceError::NoError);
112+ } catch (...) {
113+ qWarning() << "Can't parse 'click list --manifest' output: " << QString::fromStdString(stdout_data);
114+ callback(ManifestList(), InterfaceError::ParseError);
115+ }
116+ } else {
117+ qWarning() << "Error" << code << "running 'click list --manifest': " << QString::fromStdString(stderr_data);
118+ callback(ManifestList(), InterfaceError::CallError);
119+ }
120+ });
121+}
122+
123+PackageSet package_names_from_stdout(const std::string& stdout_data)
124+{
125+ const char TAB='\t', NEWLINE='\n';
126+ std::istringstream iss(stdout_data);
127+ PackageSet installed_packages;
128+
129+ while (iss.peek() != EOF) {
130+ Package p;
131+ std::getline(iss, p.name, TAB);
132+ std::getline(iss, p.version, NEWLINE);
133+ if (iss.eof() || p.name.empty() || p.version.empty()) {
134+ throw std::runtime_error("Error encountered parsing 'click list' output");
135+ }
136+ installed_packages.insert(p);
137+ }
138+
139+ return installed_packages;
140+}
141+
142+void Interface::get_installed_packages(std::function<void(PackageSet, InterfaceError)> callback)
143+{
144+ std::string command = "click list";
145+ qDebug() << "Running command:" << command.c_str();
146+ run_process(command, [callback](int code, const std::string& stdout_data, const std::string& stderr_data) {
147+ if (code == 0) {
148+ try {
149+ PackageSet package_names = package_names_from_stdout(stdout_data);
150+ callback(package_names, InterfaceError::NoError);
151+ } catch (...) {
152+ qWarning() << "Can't parse 'click list' output: " << QString::fromStdString(stdout_data);
153+ callback({}, InterfaceError::ParseError);
154+ }
155+ } else {
156+ qWarning() << "Error" << code << "running 'click list': " << QString::fromStdString(stderr_data);
157+ callback({}, InterfaceError::CallError);
158 }
159 });
160 }
161
162 void Interface::get_manifest_for_app(const std::string &app_id,
163- std::function<void(Manifest, ManifestError)> callback)
164+ std::function<void(Manifest, InterfaceError)> callback)
165 {
166 std::string command = "click info " + app_id;
167 qDebug() << "Running command:" << command.c_str();
168@@ -370,23 +421,23 @@
169 if (code == 0) {
170 try {
171 Manifest manifest = manifest_from_json(stdout_data);
172- callback(manifest, ManifestError::NoError);
173+ callback(manifest, InterfaceError::NoError);
174 } catch (...) {
175- callback(Manifest(), ManifestError::ParseError);
176+ callback(Manifest(), InterfaceError::ParseError);
177 }
178 } else {
179- callback(Manifest(), ManifestError::CallError);
180+ callback(Manifest(), InterfaceError::CallError);
181 }
182 });
183 }
184
185 void Interface::get_dotdesktop_filename(const std::string &app_id,
186- std::function<void(std::string, ManifestError)> callback)
187+ std::function<void(std::string, InterfaceError)> callback)
188 {
189- get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, ManifestError error) {
190+ get_manifest_for_app(app_id, [app_id, callback] (Manifest manifest, InterfaceError error) {
191 qDebug() << "in get_dotdesktop_filename callback";
192
193- if (error != ManifestError::NoError){
194+ if (error != InterfaceError::NoError){
195 callback(std::string("Internal Error"), error);
196 return;
197 }
198@@ -394,10 +445,10 @@
199
200 if (!manifest.name.empty()) {
201 std::string ddstr = manifest.name + "_" + manifest.first_app_name + "_" + manifest.version + ".desktop";
202- callback(ddstr, ManifestError::NoError);
203+ callback(ddstr, InterfaceError::NoError);
204 } else {
205 qCritical() << "Warning: no manifest found for " << app_id.c_str();
206- callback(std::string("Not found"), ManifestError::CallError);
207+ callback(std::string("Not found"), InterfaceError::CallError);
208 }
209 });
210 }
211
212=== modified file 'libclickscope/click/interface.h'
213--- libclickscope/click/interface.h 2014-05-26 14:02:45 +0000
214+++ libclickscope/click/interface.h 2014-06-12 16:58:45 +0000
215@@ -38,6 +38,7 @@
216 #include <unordered_set>
217
218 #include "application.h"
219+#include "package.h"
220
221 namespace click
222 {
223@@ -59,10 +60,17 @@
224 std::string name;
225 std::string version;
226 std::string first_app_name;
227+ std::string first_scope_id;
228 bool removable = false;
229+ bool has_any_apps() const {
230+ return !first_app_name.empty();
231+ }
232+ bool has_any_scopes() const {
233+ return !first_scope_id.empty();
234+ }
235 };
236
237-enum class ManifestError {NoError, CallError, ParseError};
238+enum class InterfaceError {NoError, CallError, ParseError};
239 typedef std::list<Manifest> ManifestList;
240
241 ManifestList manifest_list_from_json(const std::string& json);
242@@ -87,10 +95,11 @@
243
244 static bool is_icon_identifier(const std::string &icon_id);
245 static std::string add_theme_scheme(const std::string &filename);
246- virtual void get_manifests(std::function<void(ManifestList, ManifestError)> callback);
247- virtual void get_manifest_for_app(const std::string &app_id, std::function<void(Manifest, ManifestError)> callback);
248+ virtual void get_manifests(std::function<void(ManifestList, InterfaceError)> callback);
249+ virtual void get_installed_packages(std::function<void(PackageSet, InterfaceError)> callback);
250+ virtual void get_manifest_for_app(const std::string &app_id, std::function<void(Manifest, InterfaceError)> callback);
251 virtual void get_dotdesktop_filename(const std::string &app_id,
252- std::function<void(std::string filename, ManifestError)> callback);
253+ std::function<void(std::string filename, InterfaceError)> callback);
254 constexpr static const char* ENV_SHOW_DESKTOP_APPS {"CLICK_SCOPE_SHOW_DESKTOP_APPS"};
255 virtual bool is_visible_app(const unity::util::IniParser& keyFile);
256 virtual bool show_desktop_apps();
257
258=== modified file 'libclickscope/click/package.cpp'
259--- libclickscope/click/package.cpp 2014-05-26 14:02:45 +0000
260+++ libclickscope/click/package.cpp 2014-06-12 16:58:45 +0000
261@@ -41,10 +41,7 @@
262 }
263
264 bool operator==(const Package& lhs, const Package& rhs) {
265- return lhs.name == rhs.name &&
266- lhs.title == rhs.title &&
267- lhs.price == rhs.price &&
268- lhs.icon_url == rhs.icon_url;
269+ return lhs.name == rhs.name;
270 }
271
272 bool operator==(const PackageDetails& lhs, const PackageDetails& rhs) {
273@@ -80,9 +77,9 @@
274 return p;
275 }
276
277-PackageList package_list_from_json_node(const Json::Value& root)
278+Packages package_list_from_json_node(const Json::Value& root)
279 {
280- PackageList pl;
281+ Packages pl;
282 if (root.isObject() && root.isMember(Package::JsonKeys::embedded))
283 {
284 auto const emb = root[Package::JsonKeys::embedded];
285@@ -114,7 +111,7 @@
286 return pl;
287 }
288
289-PackageList package_list_from_json(const std::string& json)
290+Packages package_list_from_json(const std::string& json)
291 {
292 std::istringstream is(json);
293
294
295=== modified file 'libclickscope/click/package.h'
296--- libclickscope/click/package.h 2014-05-26 14:02:45 +0000
297+++ libclickscope/click/package.h 2014-06-12 16:58:45 +0000
298@@ -30,8 +30,10 @@
299 #ifndef CLICK_PACKAGE_H
300 #define CLICK_PACKAGE_H
301
302+#include <list>
303 #include <string>
304-#include <list>
305+#include <unordered_set>
306+#include <vector>
307
308 #include <json/json.h>
309
310@@ -76,6 +78,11 @@
311 version(version)
312 {
313 }
314+ Package(std::string name, std::string version) :
315+ name(name),
316+ version(version)
317+ {
318+ }
319 virtual ~Package() = default;
320
321 std::string name; // formerly app_id
322@@ -87,11 +94,12 @@
323 void matches (std::string query, std::function<bool> callback);
324 };
325
326-typedef std::list<Package> PackageList;
327+typedef std::vector<Package> Packages;
328+typedef std::unordered_set<Package> PackageSet;
329
330 Package package_from_json_node(const Json::Value& item);
331-PackageList package_list_from_json(const std::string& json);
332-PackageList package_list_from_json_node(const Json::Value& root);
333+Packages package_list_from_json(const std::string& json);
334+Packages package_list_from_json_node(const Json::Value& root);
335
336 struct PackageDetails
337 {
338@@ -141,4 +149,16 @@
339
340 } // namespace click
341
342+using namespace std;
343+namespace std {
344+ template <>
345+ class hash<click::Package>{
346+ public :
347+ size_t operator()(const click::Package &package ) const
348+ {
349+ return hash<string>()(package.name);
350+ }
351+ };
352+} // namespace std
353+
354 #endif // CLICK_PACKAGE_H
355
356=== modified file 'libclickscope/click/preview.cpp'
357--- libclickscope/click/preview.cpp 2014-06-03 16:36:44 +0000
358+++ libclickscope/click/preview.cpp 2014-06-12 16:58:45 +0000
359@@ -36,6 +36,7 @@
360 #include <boost/algorithm/string/replace.hpp>
361
362 #include <unity/UnityExceptions.h>
363+#include <unity/scopes/CannedQuery.h>
364 #include <unity/scopes/PreviewReply.h>
365 #include <unity/scopes/Variant.h>
366 #include <unity/scopes/VariantBuilder.h>
367@@ -431,29 +432,28 @@
368 // Do nothing as we are not submitting a review.
369 }
370
371- // Get the removable flag from the click manifest.
372- bool removable = false;
373- std::promise<bool> manifest_promise;
374- std::future<bool> manifest_future = manifest_promise.get_future();
375+ // Get the click manifest.
376+ Manifest manifest;
377+ std::promise<Manifest> manifest_promise;
378+ std::future<Manifest> manifest_future = manifest_promise.get_future();
379 std::string app_name = result["name"].get_string();
380 if (!app_name.empty()) {
381 qt::core::world::enter_with_task([&]() {
382 click::Interface().get_manifest_for_app(app_name,
383- [&](Manifest manifest, ManifestError error) {
384+ [&](Manifest found_manifest, InterfaceError error) {
385 qDebug() << "Got manifest for:" << app_name.c_str();
386- removable = manifest.removable;
387
388 // Fill in required data about the package being reviewed.
389- review.package_name = manifest.name;
390- review.package_version = manifest.version;
391+ review.package_name = found_manifest.name;
392+ review.package_version = found_manifest.version;
393
394- if (error != click::ManifestError::NoError) {
395+ if (error != click::InterfaceError::NoError) {
396 qDebug() << "There was an error getting the manifest for:" << app_name.c_str();
397 }
398- manifest_promise.set_value(true);
399+ manifest_promise.set_value(found_manifest);
400 });
401 });
402- manifest_future.get();
403+ manifest = manifest_future.get();
404 if (review.rating > 0) {
405 std::promise<bool> submit_promise;
406 std::future<bool> submit_future = submit_promise.get_future();
407@@ -469,13 +469,13 @@
408 submit_future.get();
409 }
410 }
411- getApplicationUri([this, reply, removable, app_name, &review](const std::string& uri) {
412- populateDetails([this, reply, uri, removable, app_name, &review](const PackageDetails &details){
413+ getApplicationUri(manifest, [this, reply, manifest, app_name, &review](const std::string& uri) {
414+ populateDetails([this, reply, uri, manifest, app_name, &review](const PackageDetails &details){
415 reply->push(headerWidgets(details));
416- reply->push(createButtons(uri, removable));
417+ reply->push(createButtons(uri, manifest));
418 reply->push(descriptionWidgets(details));
419
420- if (review.rating == 0 && removable) {
421+ if (review.rating == 0 && manifest.removable) {
422 scopes::PreviewWidgetList review_input;
423 scopes::PreviewWidget rating("rating", "rating-input");
424 rating.add_attribute_value("required", scopes::Variant("rating"));
425@@ -496,35 +496,38 @@
426 }
427
428 scopes::PreviewWidgetList InstalledPreview::createButtons(const std::string& uri,
429- bool removable)
430+ const Manifest& manifest)
431 {
432 scopes::PreviewWidgetList widgets;
433 scopes::PreviewWidget buttons("buttons", "actions");
434 scopes::VariantBuilder builder;
435+ bool no_apps_but_scope = !manifest.has_any_apps() && manifest.has_any_scopes();
436+ std::string open_label = no_apps_but_scope ? _("Search") : _("Open");
437+
438 if (!uri.empty())
439 {
440 builder.add_tuple(
441 {
442 {"id", scopes::Variant(click::Preview::Actions::OPEN_CLICK)},
443- {"label", scopes::Variant(_("Open"))},
444+ {"label", scopes::Variant(open_label)},
445 {"uri", scopes::Variant(uri)}
446 });
447 }
448- if (removable)
449+ if (manifest.removable)
450 {
451 builder.add_tuple({
452 {"id", scopes::Variant(click::Preview::Actions::UNINSTALL_CLICK)},
453 {"label", scopes::Variant(_("Uninstall"))}
454 });
455 }
456- if (!uri.empty() || removable) {
457+ if (!uri.empty() || manifest.removable) {
458 buttons.add_attribute_value("actions", builder.end());
459 widgets.push_back(buttons);
460 }
461 return widgets;
462 }
463
464-void InstalledPreview::getApplicationUri(std::function<void(const std::string&)> callback)
465+void InstalledPreview::getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback)
466 {
467 std::string uri;
468 QString app_url = QString::fromStdString(result.uri());
469@@ -533,18 +536,26 @@
470 // this can happen if the app was just installed and we have its http uri from the Result.
471 if (!app_url.startsWith("application:///")) {
472 const std::string name = result["name"].get_string();
473- auto ft = qt::core::world::enter_with_task([this, name, callback] ()
474- {
475- click::Interface().get_dotdesktop_filename(name,
476- [callback] (std::string val, click::ManifestError error) {
477- std::string uri;
478- if (error == click::ManifestError::NoError) {
479- uri = "application:///" + val;
480- }
481- callback(uri);
482- }
483- );
484- });
485+
486+ if (manifest.has_any_apps()) {
487+ qt::core::world::enter_with_task([this, name, callback] ()
488+ {
489+ click::Interface().get_dotdesktop_filename(name,
490+ [callback] (std::string val, click::InterfaceError error) {
491+ std::string uri;
492+ if (error == click::InterfaceError::NoError) {
493+ uri = "application:///" + val;
494+ }
495+ callback(uri);
496+ }
497+ );
498+ });
499+ } else {
500+ if (manifest.has_any_scopes()) {
501+ unity::scopes::CannedQuery cq(manifest.first_scope_id);
502+ callback(cq.to_uri());
503+ }
504+ }
505 } else {
506 uri = app_url.toStdString();
507 callback(uri);
508
509=== modified file 'libclickscope/click/preview.h'
510--- libclickscope/click/preview.h 2014-06-03 16:21:22 +0000
511+++ libclickscope/click/preview.h 2014-06-12 16:58:45 +0000
512@@ -47,6 +47,7 @@
513
514 namespace click {
515
516+class Manifest;
517 class PreviewStrategy;
518
519 class Preview : public unity::scopes::PreviewQueryBase
520@@ -164,11 +165,11 @@
521 void run(unity::scopes::PreviewReplyProxy const& reply) override;
522
523 protected:
524- void getApplicationUri(std::function<void(const std::string&)> callback);
525+ void getApplicationUri(const Manifest& manifest, std::function<void(const std::string&)> callback);
526
527 private:
528 static scopes::PreviewWidgetList createButtons(const std::string& uri,
529- bool removable);
530+ const click::Manifest& manifest);
531 scopes::ActionMetadata metadata;
532 };
533
534
535=== modified file 'libclickscope/tests/fake_json.h'
536--- libclickscope/tests/fake_json.h 2014-05-26 14:27:31 +0000
537+++ libclickscope/tests/fake_json.h 2014-06-12 16:58:45 +0000
538@@ -176,4 +176,32 @@
539 }
540 )foo";
541
542+const std::string FAKE_JSON_MANIFEST_ONE_APP = R"foo(
543+ {
544+ "_removable": 1,
545+ "name": "com.example.fake-app",
546+ "version": "0.1",
547+ "hooks": {
548+ "fake-app": {
549+ "apparmor": "fake-app.json",
550+ "desktop": "fake-app.desktop"
551+ }
552+ }
553+ }
554+)foo";
555+
556+const std::string FAKE_JSON_MANIFEST_ONE_SCOPE = R"foo(
557+ {
558+ "_removable": 1,
559+ "name": "com.example.fake-scope",
560+ "version": "0.1",
561+ "hooks": {
562+ "fake-scope": {
563+ "apparmor": "scope-security.json",
564+ "scope": "fake-scope"
565+ }
566+ }
567+ }
568+)foo";
569+
570 #endif // FAKE_JSON_H
571
572=== modified file 'libclickscope/tests/test_index.cpp'
573--- libclickscope/tests/test_index.cpp 2014-05-26 14:27:31 +0000
574+++ libclickscope/tests/test_index.cpp 2014-06-12 16:58:45 +0000
575@@ -79,7 +79,7 @@
576 DefaultValue<std::map<std::string, std::string>>::Set(std::map<std::string, std::string>());
577 }
578 public:
579- MOCK_METHOD1(search_callback, void(click::PackageList));
580+ MOCK_METHOD1(search_callback, void(click::Packages));
581 MOCK_METHOD2(details_callback, void(click::PackageDetails, click::Index::Error));
582 };
583
584@@ -100,7 +100,7 @@
585 .Times(1)
586 .WillOnce(Return(response));
587
588- indexPtr->search("", [](click::PackageList) {});
589+ indexPtr->search("", [](click::Packages) {});
590 }
591
592 TEST_F(IndexTest, testSearchSendsBuiltQueryAsParam)
593@@ -120,7 +120,7 @@
594 .Times(1)
595 .WillOnce(Return(FAKE_BUILT_QUERY));
596
597- indexPtr->search(FAKE_QUERY, [](click::PackageList) {});
598+ indexPtr->search(FAKE_QUERY, [](click::Packages) {});
599 }
600
601 TEST_F(IndexTest, testSearchSendsRightPath)
602@@ -133,7 +133,7 @@
603 .Times(1)
604 .WillOnce(Return(response));
605
606- indexPtr->search("", [](click::PackageList) {});
607+ indexPtr->search("", [](click::Packages) {});
608 }
609
610 TEST_F(IndexTest, testSearchCallbackIsCalled)
611@@ -150,7 +150,7 @@
612 .WillOnce(Return(response));
613 EXPECT_CALL(*this, search_callback(_)).Times(1);
614
615- indexPtr->search("", [this](click::PackageList packages){
616+ indexPtr->search("", [this](click::Packages packages){
617 search_callback(packages);
618 });
619 response->replyFinished();
620@@ -168,10 +168,10 @@
621 EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _))
622 .Times(1)
623 .WillOnce(Return(response));
624- click::PackageList empty_package_list;
625+ click::Packages empty_package_list;
626 EXPECT_CALL(*this, search_callback(empty_package_list)).Times(1);
627
628- indexPtr->search("", [this](click::PackageList packages){
629+ indexPtr->search("", [this](click::Packages packages){
630 search_callback(packages);
631 });
632 response->replyFinished();
633@@ -189,7 +189,7 @@
634 EXPECT_CALL(*clientPtr, callImpl(_, _, _, _, _, _))
635 .Times(1)
636 .WillOnce(Return(response));
637- click::PackageList single_package_list =
638+ click::Packages single_package_list =
639 {
640 click::Package
641 {
642@@ -200,7 +200,7 @@
643 };
644 EXPECT_CALL(*this, search_callback(single_package_list)).Times(1);
645
646- indexPtr->search("", [this](click::PackageList packages){
647+ indexPtr->search("", [this](click::Packages packages){
648 search_callback(packages);
649 });
650 response->replyFinished();
651@@ -215,7 +215,7 @@
652 .Times(1)
653 .WillOnce(Return(response));
654
655- auto search_operation = indexPtr->search("", [](click::PackageList) {});
656+ auto search_operation = indexPtr->search("", [](click::Packages) {});
657 EXPECT_CALL(reply.instance, abort()).Times(1);
658 search_operation.cancel();
659 }
660@@ -234,11 +234,11 @@
661 .Times(1)
662 .WillOnce(Return(response));
663 EXPECT_CALL(reply.instance, errorString()).Times(1).WillOnce(Return("fake error"));
664- indexPtr->search("", [this](click::PackageList packages){
665+ indexPtr->search("", [this](click::Packages packages){
666 search_callback(packages);
667 });
668
669- click::PackageList empty_package_list;
670+ click::Packages empty_package_list;
671 EXPECT_CALL(*this, search_callback(empty_package_list)).Times(1);
672
673 emit reply.instance.error(QNetworkReply::UnknownNetworkError);
674
675=== modified file 'libclickscope/tests/test_interface.cpp'
676--- libclickscope/tests/test_interface.cpp 2014-06-09 16:42:23 +0000
677+++ libclickscope/tests/test_interface.cpp 2014-06-12 16:58:45 +0000
678@@ -118,8 +118,9 @@
679
680 class ClickInterfaceTest : public ::testing::Test {
681 public:
682- MOCK_METHOD2(manifest_callback, void(Manifest, ManifestError));
683- MOCK_METHOD2(manifests_callback, void(ManifestList, ManifestError));
684+ MOCK_METHOD2(manifest_callback, void(Manifest, InterfaceError));
685+ MOCK_METHOD2(manifests_callback, void(ManifestList, InterfaceError));
686+ MOCK_METHOD2(installed_callback, void(PackageSet, InterfaceError));
687 };
688
689 }
690@@ -348,13 +349,29 @@
691 EXPECT_FALSE(iface.show_desktop_apps());
692 }
693
694+TEST(ClickInterface, testManifestFromJsonOneApp)
695+{
696+ Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_ONE_APP);
697+ ASSERT_EQ(m.first_app_name, "fake-app");
698+ ASSERT_TRUE(m.has_any_apps());
699+ ASSERT_FALSE(m.has_any_scopes());
700+}
701+
702+TEST(ClickInterface, testManifestFromJsonOneScope)
703+{
704+ Manifest m = manifest_from_json(FAKE_JSON_MANIFEST_ONE_SCOPE);
705+ ASSERT_EQ(m.first_scope_id, "com.example.fake-scope");
706+ ASSERT_FALSE(m.has_any_apps());
707+ ASSERT_TRUE(m.has_any_scopes());
708+}
709+
710 TEST(ClickInterface, testGetManifestForAppCorrectCommand)
711 {
712 FakeClickInterface iface;
713 std::string command = "click info " + FAKE_PACKAGENAME;
714 EXPECT_CALL(iface, run_process(command, _)).
715 Times(1);
716- iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest, ManifestError){});
717+ iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest, InterfaceError){});
718 }
719
720 TEST_F(ClickInterfaceTest, testGetManifestForAppParseError)
721@@ -367,9 +384,9 @@
722 const std::string&)> callback){
723 callback(0, "INVALID JSON", "");
724 }));
725- EXPECT_CALL(*this, manifest_callback(_, ManifestError::ParseError));
726+ EXPECT_CALL(*this, manifest_callback(_, InterfaceError::ParseError));
727 iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest,
728- ManifestError error){
729+ InterfaceError error){
730 manifest_callback(manifest, error);
731 });
732 }
733@@ -384,9 +401,9 @@
734 const std::string&)> callback){
735 callback(-1, "", "CRITICAL: FAIL");
736 }));
737- EXPECT_CALL(*this, manifest_callback(_, ManifestError::CallError));
738+ EXPECT_CALL(*this, manifest_callback(_, InterfaceError::CallError));
739 iface.get_manifest_for_app(FAKE_PACKAGENAME, [this](Manifest manifest,
740- ManifestError error){
741+ InterfaceError error){
742 manifest_callback(manifest, error);
743 });
744 }
745@@ -402,8 +419,8 @@
746 callback(0, FAKE_JSON_MANIFEST_REMOVABLE, "");
747 }));
748 iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest,
749- ManifestError error){
750- ASSERT_TRUE(error == ManifestError::NoError);
751+ InterfaceError error){
752+ ASSERT_TRUE(error == InterfaceError::NoError);
753 ASSERT_TRUE(manifest.removable);
754 });
755 }
756@@ -419,8 +436,8 @@
757 callback(0, FAKE_JSON_MANIFEST_NONREMOVABLE, "");
758 }));
759 iface.get_manifest_for_app(FAKE_PACKAGENAME, [](Manifest manifest,
760- ManifestError error){
761- ASSERT_TRUE(error == ManifestError::NoError);
762+ InterfaceError error){
763+ ASSERT_TRUE(error == InterfaceError::NoError);
764 ASSERT_FALSE(manifest.removable);
765 });
766 }
767@@ -431,7 +448,7 @@
768 std::string command = "click list --manifest";
769 EXPECT_CALL(iface, run_process(command, _)).
770 Times(1);
771- iface.get_manifests([](ManifestList, ManifestError){});
772+ iface.get_manifests([](ManifestList, InterfaceError){});
773 }
774
775 TEST_F(ClickInterfaceTest, testGetManifestsParseError)
776@@ -444,8 +461,8 @@
777 const std::string&)> callback){
778 callback(0, "INVALID JSON", "");
779 }));
780- EXPECT_CALL(*this, manifests_callback(_, ManifestError::ParseError));
781- iface.get_manifests([this](ManifestList manifests, ManifestError error){
782+ EXPECT_CALL(*this, manifests_callback(_, InterfaceError::ParseError));
783+ iface.get_manifests([this](ManifestList manifests, InterfaceError error){
784 manifests_callback(manifests, error);
785 });
786 }
787@@ -460,8 +477,8 @@
788 const std::string&)> callback){
789 callback(-1, "", "CRITICAL: FAIL");
790 }));
791- EXPECT_CALL(*this, manifests_callback(_, ManifestError::CallError));
792- iface.get_manifests([this](ManifestList manifests, ManifestError error){
793+ EXPECT_CALL(*this, manifests_callback(_, InterfaceError::CallError));
794+ iface.get_manifests([this](ManifestList manifests, InterfaceError error){
795 manifests_callback(manifests, error);
796 });
797 }
798@@ -480,8 +497,69 @@
799 const std::string&)> callback){
800 callback(0, expected_str, "");
801 }));
802- iface.get_manifests([expected](ManifestList manifests, ManifestError error){
803- ASSERT_TRUE(error == ManifestError::NoError);
804+ iface.get_manifests([expected](ManifestList manifests, InterfaceError error){
805+ ASSERT_TRUE(error == InterfaceError::NoError);
806 ASSERT_TRUE(manifests.size() == expected.size());
807 });
808 }
809+
810+TEST(ClickInterface, testGetInstalledPackagesCorrectCommand)
811+{
812+ FakeClickInterface iface;
813+ std::string command = "click list";
814+ EXPECT_CALL(iface, run_process(command, _)).
815+ Times(1);
816+ iface.get_installed_packages([](PackageSet, InterfaceError){});
817+}
818+
819+TEST_F(ClickInterfaceTest, testGetInstalledPackagesParseError)
820+{
821+ FakeClickInterface iface;
822+ EXPECT_CALL(iface, run_process(_, _)).
823+ Times(1).
824+ WillOnce(Invoke([&](const std::string&,
825+ std::function<void(int, const std::string&,
826+ const std::string&)> callback){
827+ callback(0, "valid.package\t1.0\nINVALID LINE", "");
828+ }));
829+ EXPECT_CALL(*this, installed_callback(_, InterfaceError::ParseError));
830+ iface.get_installed_packages([this](PackageSet package_names, InterfaceError error){
831+ installed_callback(package_names, error);
832+ });
833+}
834+
835+TEST_F(ClickInterfaceTest, testGetInstalledPackagesCommandFailed)
836+{
837+ FakeClickInterface iface;
838+ EXPECT_CALL(iface, run_process(_, _)).
839+ Times(1).
840+ WillOnce(Invoke([&](const std::string&,
841+ std::function<void(int, const std::string&,
842+ const std::string&)> callback){
843+ callback(-1, "", "CRITICAL: FAIL");
844+ }));
845+ EXPECT_CALL(*this, installed_callback(_, InterfaceError::CallError));
846+ iface.get_installed_packages([this](PackageSet package_names, InterfaceError error){
847+ installed_callback(package_names, error);
848+ });
849+}
850+
851+TEST_F(ClickInterfaceTest, testGetInstalledPackagesParsed)
852+{
853+ FakeClickInterface iface;
854+ std::string sample_stdout = "ABC\t0.1\nDEF\t0.2\n";
855+ PackageSet expected{{"ABC", "0.1"}, {"DEF", "0.2"}};
856+
857+ EXPECT_CALL(iface, run_process(_, _)).
858+ Times(1).
859+ WillOnce(Invoke([&](const std::string&,
860+ std::function<void(int, const std::string&,
861+ const std::string&)> callback){
862+ callback(0, sample_stdout, "");
863+ }));
864+ iface.get_installed_packages([expected](PackageSet package_names, InterfaceError error){
865+ ASSERT_EQ(error, InterfaceError::NoError);
866+ ASSERT_EQ(package_names, expected);
867+ });
868+}
869+
870
871=== modified file 'scope/clickstore/store-query.cpp'
872--- scope/clickstore/store-query.cpp 2014-05-27 09:37:28 +0000
873+++ scope/clickstore/store-query.cpp 2014-06-12 16:58:45 +0000
874@@ -28,9 +28,9 @@
875 */
876
877 #include <click/application.h>
878+#include <click/interface.h>
879 #include "store-query.h"
880 #include <click/qtbridge.h>
881-#include <click/interface.h>
882
883 #include <click/key_file_locator.h>
884
885@@ -48,6 +48,8 @@
886
887 #include <click/click-i18n.h>
888
889+using namespace click;
890+
891 namespace
892 {
893
894@@ -60,10 +62,10 @@
895 },
896 "components" : {
897 "title" : "title",
898+ "subtitle": "subtitle",
899 "art" : {
900 "field": "art",
901- "aspect-ratio": 1.6,
902- "fill-mode": "fit"
903+ "aspect-ratio": 1.13
904 }
905 }
906 }
907@@ -82,7 +84,7 @@
908 "mascot" : {
909 "field": "art"
910 },
911- "subtitle": "publisher"
912+ "subtitle": "subtitle"
913 }
914 }
915 )";
916@@ -119,9 +121,7 @@
917 impl->search_operation.cancel();
918 }
919
920-namespace
921-{
922-click::Interface& clickInterfaceInstance()
923+click::Interface& click::Query::clickInterfaceInstance()
924 {
925 static QSharedPointer<click::KeyFileLocator> keyFileLocator(new click::KeyFileLocator());
926 static click::Interface iface(keyFileLocator);
927@@ -129,8 +129,6 @@
928 return iface;
929 }
930
931-}
932-
933 bool click::Query::push_result(scopes::SearchReplyProxy const& searchReply, const scopes::CategorisedResult &res)
934 {
935 return searchReply->push(res);
936@@ -158,7 +156,7 @@
937 }
938
939 void click::Query::add_available_apps(scopes::SearchReplyProxy const& searchReply,
940- const std::set<std::string>& locallyInstalledApps,
941+ const PackageSet& installedPackages,
942 const std::string& categoryTemplate)
943 {
944 scopes::CategoryRenderer categoryRenderer(categoryTemplate);
945@@ -166,7 +164,7 @@
946
947 run_under_qt([=]()
948 {
949- auto search_cb = [this, searchReply, category, locallyInstalledApps](PackageList packages) {
950+ auto search_cb = [this, searchReply, category, installedPackages](Packages packages) {
951 qDebug("search callback");
952
953 // handle packages data
954@@ -174,16 +172,20 @@
955 qDebug() << "pushing result" << QString::fromStdString(p.name);
956 try {
957 scopes::CategorisedResult res(category);
958- // TODO: mark as installed
959- if (locallyInstalledApps.count(p.name) > 0) {
960- qDebug() << "already installed" << QString::fromStdString(p.name);
961- continue;
962- }
963 res.set_title(p.title);
964 res.set_art(p.icon_url);
965 res.set_uri(p.url);
966 res[click::Query::ResultKeys::NAME] = p.name;
967- res[click::Query::ResultKeys::INSTALLED] = false;
968+ auto installed = installedPackages.find(p);
969+ if (installed != installedPackages.end()) {
970+ res[click::Query::ResultKeys::INSTALLED] = true;
971+ res["subtitle"] = _("✔ Installed");
972+ res[click::Query::ResultKeys::VERSION] = installed->version;
973+ } else {
974+ res[click::Query::ResultKeys::INSTALLED] = false;
975+ // TODO: get the real price from the webservice (upcoming branch)
976+ res["subtitle"] = _("FREE");
977+ }
978
979 this->push_result(searchReply, res);
980 } catch(const std::exception& e){
981@@ -201,6 +203,23 @@
982 });
983 }
984
985+PackageSet click::Query::get_installed_packages()
986+{
987+ std::promise<PackageSet> installed_promise;
988+ std::future<PackageSet> installed_future = installed_promise.get_future();
989+
990+ run_under_qt([&]()
991+ {
992+ clickInterfaceInstance().get_installed_packages(
993+ [&installed_promise](PackageSet installedPackages, InterfaceError){
994+ installed_promise.set_value(installedPackages);
995+ });
996+ });
997+
998+ return installed_future.get();
999+}
1000+
1001+
1002 void click::Query::run(scopes::SearchReplyProxy const& searchReply)
1003 {
1004 auto query = impl->query.query_string();
1005@@ -208,12 +227,6 @@
1006 if (query.empty()) {
1007 categoryTemplate = CATEGORY_APPS_DISPLAY;
1008 }
1009- auto localResults = clickInterfaceInstance().find_installed_apps(query);
1010-
1011- std::set<std::string> locallyInstalledApps;
1012- for(const auto& app : localResults) {
1013- locallyInstalledApps.insert(app.name);
1014- }
1015
1016 static const std::string no_net_hint("no-internet");
1017 if (impl->meta.contains_hint(no_net_hint))
1018@@ -223,8 +236,7 @@
1019 {
1020 return;
1021 }
1022-
1023 }
1024
1025- add_available_apps(searchReply, locallyInstalledApps, categoryTemplate);
1026+ add_available_apps(searchReply, get_installed_packages(), categoryTemplate);
1027 }
1028
1029=== modified file 'scope/clickstore/store-query.h'
1030--- scope/clickstore/store-query.h 2014-05-27 06:57:52 +0000
1031+++ scope/clickstore/store-query.h 2014-06-12 16:58:45 +0000
1032@@ -36,8 +36,8 @@
1033 namespace scopes = unity::scopes;
1034
1035 #include <QSharedPointer>
1036-#include <set>
1037-
1038+#include <unordered_set>
1039+#include <click/interface.h>
1040
1041 namespace click
1042 {
1043@@ -78,7 +78,10 @@
1044 virtual void run(scopes::SearchReplyProxy const& reply) override;
1045
1046 protected:
1047- virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const std::set<std::string> &locallyInstalledApps, const std::string &category);
1048+ virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply,
1049+ const PackageSet &installedPackages, const std::string &category);
1050+ virtual click::Interface& clickInterfaceInstance();
1051+ virtual PackageSet get_installed_packages();
1052 virtual bool push_result(const scopes::SearchReplyProxy &searchReply, scopes::CategorisedResult const& res);
1053 virtual void finished(const scopes::SearchReplyProxy &searchReply);
1054 virtual scopes::Category::SCPtr register_category(scopes::SearchReplyProxy const& searchReply,
1055
1056=== modified file 'scope/tests/click_interface_tool/click_interface_tool.cpp'
1057--- scope/tests/click_interface_tool/click_interface_tool.cpp 2014-05-13 19:32:29 +0000
1058+++ scope/tests/click_interface_tool/click_interface_tool.cpp 2014-06-12 16:58:45 +0000
1059@@ -51,8 +51,8 @@
1060
1061 QObject::connect(&timer, &QTimer::timeout, [&]() {
1062 ci.get_dotdesktop_filename(std::string(argv[1]),
1063- [&a] (std::string val, click::ManifestError error){
1064- if (error == click::ManifestError::NoError) {
1065+ [&a] (std::string val, click::InterfaceError error){
1066+ if (error == click::InterfaceError::NoError) {
1067 std::cout << " Success, got dotdesktop:" << val << std::endl;
1068 } else {
1069 std::cout << " Error:" << val << std::endl;
1070
1071=== modified file 'scope/tests/integration/webclient_integration.cpp'
1072--- scope/tests/integration/webclient_integration.cpp 2014-04-29 03:17:38 +0000
1073+++ scope/tests/integration/webclient_integration.cpp 2014-06-12 16:58:45 +0000
1074@@ -101,8 +101,8 @@
1075 QSharedPointer<click::web::Client> clientPtr(
1076 new click::web::Client(namPtr));
1077 click::Index index(clientPtr);
1078- click::PackageList packages;
1079- index.search("qr,architecture:armhf", [&, this](click::PackageList found_packages){
1080+ click::Packages packages;
1081+ index.search("qr,architecture:armhf", [&, this](click::Packages found_packages){
1082 packages = found_packages;
1083 Quit();
1084 });
1085
1086=== modified file 'scope/tests/test_query.cpp'
1087--- scope/tests/test_query.cpp 2014-06-09 16:49:10 +0000
1088+++ scope/tests/test_query.cpp 2014-06-12 16:58:45 +0000
1089@@ -47,6 +47,7 @@
1090 #include <unity/scopes/SearchReply.h>
1091
1092 using namespace ::testing;
1093+using namespace click;
1094
1095 namespace
1096 {
1097@@ -55,16 +56,16 @@
1098
1099
1100 class MockIndex : public click::Index {
1101- click::PackageList packages;
1102+ click::Packages packages;
1103 public:
1104- MockIndex(click::PackageList packages = click::PackageList())
1105+ MockIndex(click::Packages packages = click::Packages())
1106 : Index(QSharedPointer<click::web::Client>()),
1107 packages(packages)
1108 {
1109
1110 }
1111
1112- click::web::Cancellable search(const std::string &query, std::function<void (click::PackageList)> callback) override
1113+ click::web::Cancellable search(const std::string &query, std::function<void (click::Packages)> callback) override
1114 {
1115 do_search(query, callback);
1116 callback(packages);
1117@@ -73,7 +74,7 @@
1118
1119 MOCK_METHOD2(do_search,
1120 void(const std::string&,
1121- std::function<void(click::PackageList)>));
1122+ std::function<void(click::Packages)>));
1123 };
1124
1125 class MockQueryBase : public click::Query {
1126@@ -98,18 +99,20 @@
1127
1128 }
1129 void wrap_add_available_apps(const scopes::SearchReplyProxy &searchReply,
1130- const std::set<std::string> &locallyInstalledApps,
1131+ const PackageSet &installedPackages,
1132 const std::string& categoryTemplate)
1133 {
1134- add_available_apps(searchReply, locallyInstalledApps, categoryTemplate);
1135+ add_available_apps(searchReply, installedPackages, categoryTemplate);
1136 }
1137 MOCK_METHOD2(push_result, bool(scopes::SearchReplyProxy const&, scopes::CategorisedResult const&));
1138+ MOCK_METHOD0(clickInterfaceInstance, click::Interface&());
1139 MOCK_METHOD1(finished, void(scopes::SearchReplyProxy const&));
1140 MOCK_METHOD5(register_category, scopes::Category::SCPtr(const scopes::SearchReplyProxy &searchReply,
1141 const std::string &id,
1142 const std::string &title,
1143 const std::string &icon,
1144 const scopes::CategoryRenderer &renderer_template));
1145+ using click::Query::get_installed_packages; // allow tests to access protected method
1146 };
1147
1148 class MockQueryRun : public MockQueryBase {
1149@@ -121,11 +124,12 @@
1150 }
1151 MOCK_METHOD3(add_available_apps,
1152 void(scopes::SearchReplyProxy const&searchReply,
1153- const std::set<std::string> &locallyInstalledApps,
1154+ const PackageSet &locallyInstalledApps,
1155 const std::string& categoryTemplate));
1156 MOCK_METHOD3(push_local_results, void(scopes::SearchReplyProxy const &replyProxy,
1157 std::vector<click::Application> const &apps,
1158 std::string& categoryTemplate));
1159+ MOCK_METHOD0(get_installed_packages, PackageSet());
1160 };
1161
1162 class FakeCategory : public scopes::Category
1163@@ -144,7 +148,7 @@
1164 {
1165 MockIndex mock_index;
1166 scopes::SearchMetadata metadata("en_EN", "phone");
1167- std::set<std::string> no_installed_packages;
1168+ PackageSet no_installed_packages;
1169 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1170 MockQuery q(query, mock_index, metadata);
1171 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)).Times(1);
1172@@ -158,12 +162,12 @@
1173
1174 TEST(QueryTest, testAddAvailableAppsPushesResults)
1175 {
1176- click::PackageList packages {
1177+ click::Packages packages {
1178 {"name", "title", 0.0, "icon", "uri"}
1179 };
1180 MockIndex mock_index(packages);
1181 scopes::SearchMetadata metadata("en_EN", "phone");
1182- std::set<std::string> no_installed_packages;
1183+ PackageSet no_installed_packages;
1184 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1185 MockQuery q(query, mock_index, metadata);
1186 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1187@@ -180,12 +184,12 @@
1188
1189 TEST(QueryTest, testAddAvailableAppsCallsFinished)
1190 {
1191- click::PackageList packages {
1192+ click::Packages packages {
1193 {"name", "title", 0.0, "icon", "uri"}
1194 };
1195 MockIndex mock_index(packages);
1196 scopes::SearchMetadata metadata("en_EN", "phone");
1197- std::set<std::string> no_installed_packages;
1198+ PackageSet no_installed_packages;
1199 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1200 MockQuery q(query, mock_index, metadata);
1201 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1202@@ -201,42 +205,99 @@
1203
1204 TEST(QueryTest, testQueryRunCallsAddAvailableApps)
1205 {
1206- click::PackageList packages {
1207+ click::Packages packages {
1208 {"name", "title", 0.0, "icon", "uri"}
1209 };
1210 MockIndex mock_index(packages);
1211 scopes::SearchMetadata metadata("en_EN", "phone");
1212+ PackageSet no_installed_packages;
1213 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1214 MockQueryRun q(query, mock_index, metadata);
1215 auto reply = scopes::SearchReplyProxy();
1216- EXPECT_CALL(q, add_available_apps(reply, _, _));
1217+ EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages));
1218+ EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _));
1219
1220 q.run(reply);
1221 }
1222
1223 MATCHER_P(HasPackageName, n, "") { return arg[click::Query::ResultKeys::NAME].get_string() == n; }
1224-
1225-TEST(QueryTest, testDuplicatesFilteredOnPackageName)
1226-{
1227- click::PackageList packages {
1228- {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1229- {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1230- };
1231- MockIndex mock_index(packages);
1232- scopes::SearchMetadata metadata("en_EN", "phone");
1233- std::set<std::string> one_installed_package {
1234- "org.example.app2"
1235- };
1236- const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1237- MockQuery q(query, mock_index, metadata);
1238- EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1239-
1240- scopes::CategoryRenderer renderer("{}");
1241- auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1242- EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1243-
1244- scopes::SearchReplyProxy reply;
1245- auto expected_name = packages.front().name;
1246- EXPECT_CALL(q, push_result(_, HasPackageName(expected_name)));
1247- q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1248+MATCHER_P(IsInstalled, b, "") { return arg[click::Query::ResultKeys::INSTALLED].get_bool() == b; }
1249+
1250+TEST(QueryTest, testDuplicatesNotFilteredAnymore)
1251+{
1252+ click::Packages packages {
1253+ {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1254+ {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1255+ };
1256+ MockIndex mock_index(packages);
1257+ scopes::SearchMetadata metadata("en_EN", "phone");
1258+ PackageSet one_installed_package {
1259+ {"org.example.app2", "0.2"}
1260+ };
1261+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1262+ MockQuery q(query, mock_index, metadata);
1263+ EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1264+
1265+ scopes::CategoryRenderer renderer("{}");
1266+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1267+ EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1268+
1269+ scopes::SearchReplyProxy reply;
1270+ auto expected_name1 = packages.front().name;
1271+ EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1)));
1272+ auto expected_name2 = packages.back().name;
1273+ EXPECT_CALL(q, push_result(_, HasPackageName(expected_name2)));
1274+ q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1275+}
1276+
1277+TEST(QueryTest, testInstalledPackagesFlaggedAsSuch)
1278+{
1279+ click::Packages packages {
1280+ {"org.example.app1", "app title1", 0.0, "icon", "uri"},
1281+ {"org.example.app2", "app title2", 0.0, "icon", "uri"}
1282+ };
1283+ MockIndex mock_index(packages);
1284+ scopes::SearchMetadata metadata("en_EN", "phone");
1285+ PackageSet one_installed_package {
1286+ {"org.example.app2", "0.2"}
1287+ };
1288+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1289+ MockQuery q(query, mock_index, metadata);
1290+ EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1291+
1292+ scopes::CategoryRenderer renderer("{}");
1293+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1294+ EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1295+
1296+ scopes::SearchReplyProxy reply;
1297+ EXPECT_CALL(q, push_result(_, IsInstalled(true)));
1298+ EXPECT_CALL(q, push_result(_, IsInstalled(false)));
1299+ q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
1300+}
1301+
1302+class FakeInterface : public click::Interface
1303+{
1304+public:
1305+ MOCK_METHOD1(get_installed_packages, void(std::function<void(PackageSet, click::InterfaceError)> callback));
1306+};
1307+
1308+TEST(QueryTest, testGetInstalledPackages)
1309+{
1310+ click::Packages uninstalled_packages {
1311+ {"name", "title", 0.0, "icon", "uri"}
1312+ };
1313+ MockIndex mock_index(uninstalled_packages);
1314+ scopes::SearchMetadata metadata("en_EN", "phone");
1315+ const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1316+ MockQuery q(query, mock_index, metadata);
1317+ PackageSet installed_packages{{"package_1", "0.1"}};
1318+
1319+ FakeInterface fake_interface;
1320+ EXPECT_CALL(q, clickInterfaceInstance()).WillOnce(ReturnRef(fake_interface));
1321+ EXPECT_CALL(fake_interface, get_installed_packages(_)).WillOnce(Invoke(
1322+ [&](std::function<void(PackageSet, click::InterfaceError)> callback){
1323+ callback(installed_packages, click::InterfaceError::NoError);
1324+ }));
1325+
1326+ ASSERT_EQ(q.get_installed_packages(), installed_packages);
1327 }

Subscribers

People subscribed via source and target branches

to all changes: