Merge lp:~alecu/unity-scope-click/uninstall-scopes into lp:unity-scope-click/devel
- uninstall-scopes
- Merge into devel
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 |
Related bugs: |
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.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:282
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:289
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:291
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
24 + } catch (...) {
25 + callback(
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?
Paweł Stołowski (stolowski) wrote : | # |
+PackageNames package_
Could you please make it const std::string& ?
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.
Paweł Stołowski (stolowski) wrote : | # |
I'm getting the following exception in scoperegistry.log when trying to uninstall Soundcloud scope with this branch:
PreviewQueryObj
boost::bad_get: failed value get using boost::get
Paweł Stołowski (stolowski) wrote : | # |
> I'm getting the following exception in scoperegistry.log when trying to
> uninstall Soundcloud scope with this branch:
>
> PreviewQueryObj
> 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.
Alejandro J. Cura (alecu) wrote : | # |
> 24 + } catch (...) {
> 25 + callback(
> 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.
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.
Alejandro J. Cura (alecu) wrote : | # |
> +PackageNames package_
>
> 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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:293
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
Alejandro J. Cura (alecu) wrote : | # |
> > I'm getting the following exception in scoperegistry.log when trying to
> > uninstall Soundcloud scope with this branch:
> >
> > PreviewQueryObj
> > 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:295
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
Alejandro J. Cura (alecu) wrote : | # |
Here's the bug for the missing refresh of results after install/uninstall: bug #1328102
Paweł Stołowski (stolowski) wrote : | # |
Thanks, Uninstall for scopes works indeed. However, I just noticed that 'Open' button is completely broken for them.
- 296. By Alejandro J. Cura
-
Allow searching installed scopes
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:296
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 297. By Alejandro J. Cura
-
Use the package name for the scope id
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:297
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 298. By Alejandro J. Cura
-
merged with devel
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:298
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alejandro J. Cura (alecu) wrote : | # |
After dobey's suggestion, I've split this MP in two branches, so it can be more easily reviewed:
https:/
https:/
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
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 | } |
PASSED: Continuous integration, rev:281 jenkins. qa.ubuntu. com/job/ unity-team- unity-scope- click-devel- ci/92/ jenkins. qa.ubuntu. com/job/ unity-team- unity-scope- click-devel- utopic- amd64-ci/ 67 jenkins. qa.ubuntu. com/job/ unity-team- unity-scope- click-devel- utopic- armhf-ci/ 66 jenkins. qa.ubuntu. com/job/ unity-team- unity-scope- click-devel- utopic- armhf-ci/ 66/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-team- unity-scope- click-devel- utopic- i386-ci/ 66
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- team-unity- scope-click- devel-ci/ 92/rebuild
http://