Merge lp:~stolowski/unity-scope-click/header-apps into lp:unity-scope-click/devel

Proposed by Paweł Stołowski
Status: Merged
Approved by: dobey
Approved revision: 320
Merged at revision: 317
Proposed branch: lp:~stolowski/unity-scope-click/header-apps
Merge into: lp:unity-scope-click/devel
Diff against target: 981 lines (+509/-126)
17 files modified
CMakeLists.txt (+4/-3)
cmake/UseGSettings.cmake (+42/-0)
data/CMakeLists.txt (+2/-0)
data/com.canonical.unity.clickscope.gschema.xml (+10/-0)
debian/control (+1/-0)
debian/unity-scope-click.install (+1/-0)
libclickscope/click/CMakeLists.txt (+4/-0)
libclickscope/click/configuration.cpp (+37/-0)
libclickscope/click/configuration.h (+16/-0)
libclickscope/tests/test_configuration.cpp (+27/-0)
scope/clickapps/apps-query.cpp (+132/-61)
scope/clickapps/apps-query.h (+29/-6)
scope/clickapps/apps-scope.cpp (+2/-1)
scope/tests/CMakeLists.txt (+44/-0)
scope/tests/test_apps_query.cpp (+84/-0)
scope/tests/test_helpers.h (+71/-0)
scope/tests/test_query.cpp (+3/-55)
To merge this branch: bzr merge lp:~stolowski/unity-scope-click/header-apps
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alejandro J. Cura (community) Approve
Review via email: mp+225844@code.launchpad.net

Commit message

Display preloaded core apps at the top in a dedicated headerless category. The list of apps to be displayed can be overriden with a dconf key.

Description of the change

Display preloaded core apps at the top in a dedicated headerless category. The list of apps to be displayed can be overriden with a dconf key.

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

Code looks good; seems to be missing libgsettings-qt-dev in debian/control
Will test it on device after jenkins approves.

review: Needs Fixing
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, looks wonderful.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-06-23 15:00:28 +0000
3+++ CMakeLists.txt 2014-07-08 07:23:41 +0000
4@@ -20,6 +20,7 @@
5 set(APPS_DATA_DIR ${CMAKE_INSTALL_FULL_DATADIR}/unity/scopes/clickapps/)
6
7 include(FindPkgConfig)
8+include(UseGSettings)
9
10 pkg_check_modules(UNITY_SCOPES REQUIRED libunity-scopes>=0.5.0 libunity-api>=0.1.3)
11 add_definitions(${UNITY_SCOPES_CFLAGS} ${UNITY_SCOPES_CFLAGS_OTHER})
12@@ -75,7 +76,7 @@
13
14 # Custom targets for the tests
15 add_custom_target (test
16- DEPENDS test-click-scope test-libclickscope
17+ DEPENDS test-click-scope test-apps-scope test-libclickscope
18 )
19
20 add_custom_target (test-disabled
21@@ -90,13 +91,13 @@
22
23 # Add a custom target for running the tests under valgrind.
24 add_custom_target (test-valgrind
25- DEPENDS test-click-scope-valgrind test-libclickscope-valgrind
26+ DEPENDS test-click-scope-valgrind test-apps-scope-valgrind test-libclickscope-valgrind
27 )
28
29 # Add a custom target for running the tests under valgrind with the
30 # full leak checks enabled.
31 add_custom_target (test-leaks
32- DEPENDS test-click-scope-leaks test-libclickscope-leaks
33+ DEPENDS test-click-scope-leaks test-apps-scope-leaks test-libclickscope-leaks
34 )
35
36 # Also let "make check" and partners work.
37
38=== added file 'cmake/UseGSettings.cmake'
39--- cmake/UseGSettings.cmake 1970-01-01 00:00:00 +0000
40+++ cmake/UseGSettings.cmake 2014-07-08 07:23:41 +0000
41@@ -0,0 +1,42 @@
42+# GSettings.cmake, CMake macros written for Marlin, feel free to re-use them.
43+
44+option (GSETTINGS_LOCALINSTALL "Install GSettings Schemas locally instead of to the GLib prefix" ${LOCAL_INSTALL})
45+
46+option (GSETTINGS_COMPILE "Compile GSettings Schemas after installation" ${GSETTINGS_LOCALINSTALL})
47+
48+if(GSETTINGS_LOCALINSTALL)
49+ message(STATUS "GSettings schemas will be installed locally.")
50+endif()
51+
52+if(GSETTINGS_COMPILE)
53+ message(STATUS "GSettings shemas will be compiled.")
54+endif()
55+
56+macro(add_schema SCHEMA_NAME)
57+
58+ set(PKG_CONFIG_EXECUTABLE pkg-config)
59+ # Have an option to not install the schema into where GLib is
60+ if (GSETTINGS_LOCALINSTALL)
61+ SET (GSETTINGS_DIR "${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas/")
62+ else (GSETTINGS_LOCALINSTALL)
63+ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} glib-2.0 --variable prefix OUTPUT_VARIABLE _glib_prefix OUTPUT_STRIP_TRAILING_WHITESPACE)
64+ SET (GSETTINGS_DIR "${_glib_prefix}/share/glib-2.0/schemas/")
65+ endif (GSETTINGS_LOCALINSTALL)
66+
67+ # Run the validator and error if it fails
68+ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_comple_schemas OUTPUT_STRIP_TRAILING_WHITESPACE)
69+ execute_process (COMMAND ${_glib_comple_schemas} --dry-run --schema-file=${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE)
70+
71+ if (_schemas_invalid)
72+ message (SEND_ERROR "Schema validation error: ${_schemas_invalid}")
73+ endif (_schemas_invalid)
74+
75+ # Actually install and recomple schemas
76+ message (STATUS "GSettings schemas will be installed into ${GSETTINGS_DIR}")
77+ install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL)
78+
79+ if (GSETTINGS_COMPILE)
80+ install (CODE "message (STATUS \"Compiling GSettings schemas\")")
81+ install (CODE "execute_process (COMMAND ${_glib_comple_schemas} ${GSETTINGS_DIR})")
82+ endif ()
83+endmacro()
84
85=== modified file 'data/CMakeLists.txt'
86--- data/CMakeLists.txt 2014-06-30 21:50:00 +0000
87+++ data/CMakeLists.txt 2014-07-08 07:23:41 +0000
88@@ -2,6 +2,8 @@
89 set(STORE_INI_TARGET com.canonical.scopes.clickstore.ini)
90 set(APPS_INI_TARGET clickscope.ini)
91
92+add_schema(com.canonical.unity.clickscope.gschema.xml)
93+
94 configure_file(
95 ${STORE_INI_TARGET}.in.in
96 ${STORE_INI_TARGET}.in
97
98=== added file 'data/com.canonical.unity.clickscope.gschema.xml'
99--- data/com.canonical.unity.clickscope.gschema.xml 1970-01-01 00:00:00 +0000
100+++ data/com.canonical.unity.clickscope.gschema.xml 2014-07-08 07:23:41 +0000
101@@ -0,0 +1,10 @@
102+<?xml version="1.0" encoding="UTF-8"?>
103+<schemalist>
104+ <schema path="/com/canonical/unity/clickscope/" id="com.canonical.Unity.ClickScope" gettext-domain="unity-scope-click">
105+ <key type="as" name="core-apps">
106+ <default>[]</default>
107+ <summary>Applications to display in the top category of the Apps scope</summary>
108+ <description>List of application IDs that will be displayed in the upper area of the Applications scope for quick access.</description>
109+ </key>
110+ </schema>
111+</schemalist>
112
113=== modified file 'debian/control'
114--- debian/control 2014-06-23 15:00:28 +0000
115+++ debian/control 2014-07-08 07:23:41 +0000
116@@ -14,6 +14,7 @@
117 libubuntuoneauth-2.0-dev,
118 libunity-api-dev (>= 7.80.7),
119 libunity-scopes-dev (>= 0.5.0),
120+ libgsettings-qt-dev,
121 pkg-config,
122 python3-all,
123 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
124
125=== modified file 'debian/unity-scope-click.install'
126--- debian/unity-scope-click.install 2014-06-20 20:00:53 +0000
127+++ debian/unity-scope-click.install 2014-07-08 07:23:41 +0000
128@@ -1,4 +1,5 @@
129 usr/lib/*/unity-scopes/*
130 usr/lib/unity-scope-click/*
131 usr/share/unity/scopes/*
132+usr/share/glib-2.0/schemas/*
133 usr/share/locale/*/LC_MESSAGES/unity-scope-click.mo
134
135=== modified file 'libclickscope/click/CMakeLists.txt'
136--- libclickscope/click/CMakeLists.txt 2014-06-23 15:00:28 +0000
137+++ libclickscope/click/CMakeLists.txt 2014-07-08 07:23:41 +0000
138@@ -2,11 +2,13 @@
139 SET (CMAKE_AUTOMOC ON)
140 find_package (Qt5Core REQUIRED)
141 pkg_check_modules(JSON_CPP REQUIRED jsoncpp)
142+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
143
144 add_definitions(
145 -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\"
146 -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\"
147 -DCLICK_INSTALL_HELPER=\"${CMAKE_INSTALL_PREFIX}/lib/unity-scope-click/install-helper\"
148+ ${GSETTINGS_QT_CFLAGS} ${GSETTINGS_QT_OTHER}
149 )
150
151 add_library(${SCOPE_LIB_NAME} STATIC
152@@ -32,6 +34,7 @@
153
154 include_directories(
155 ${JSON_CPP_INCLUDE_DIRS}
156+ ${GSETTINGS_QT_INCLUDE_DIRS}
157 ${CMAKE_SOURCE_DIR}/libclickscope
158 )
159
160@@ -41,5 +44,6 @@
161 ${UBUNTUONE_LDFLAGS}
162 ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS}
163 ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS}
164+ ${GSETTINGS_QT_LIBRARIES}
165 -lboost_locale
166 )
167
168=== modified file 'libclickscope/click/configuration.cpp'
169--- libclickscope/click/configuration.cpp 2014-06-25 21:33:59 +0000
170+++ libclickscope/click/configuration.cpp 2014-07-08 07:23:41 +0000
171@@ -32,6 +32,11 @@
172
173 #include <QDir>
174 #include <QProcess>
175+#include <QStringList>
176+#include <QVariant>
177+#include <QDebug>
178+
179+#include <qgsettings.h>
180
181 #include <boost/algorithm/string.hpp>
182 #include <boost/algorithm/string/replace.hpp>
183@@ -141,4 +146,36 @@
184 != FULL_LANG_CODES.end();
185 }
186
187+const std::vector<std::string> Configuration::get_dconf_strings(const std::string& schema, const std::string& key) const
188+{
189+ if (!QGSettings::isSchemaInstalled(schema.c_str()))
190+ {
191+ qWarning() << "Schema" << QString::fromStdString(schema) << "is missing";
192+ return std::vector<std::string>();
193+ }
194+ QGSettings qgs(schema.c_str());
195+ std::vector<std::string> v;
196+ if (qgs.keys().contains(QString::fromStdString(key)))
197+ {
198+ auto locations = qgs.get(QString::fromStdString(key)).toStringList();
199+ for(const auto& l : locations) {
200+ v.push_back(l.toStdString());
201+ }
202+ }
203+ else
204+ {
205+ qWarning() << "No" << QString::fromStdString(key) << " key in schema" << QString::fromStdString(schema);
206+ }
207+ return v;
208+}
209+
210+const std::vector<std::string> Configuration::get_core_apps() const
211+{
212+ auto apps = get_dconf_strings(Configuration::COREAPPS_SCHEMA, Configuration::COREAPPS_KEY);
213+ if (apps.empty()) {
214+ apps = get_default_core_apps();
215+ }
216+ return apps;
217+}
218+
219 } // namespace click
220
221=== modified file 'libclickscope/click/configuration.h'
222--- libclickscope/click/configuration.h 2014-06-25 21:33:59 +0000
223+++ libclickscope/click/configuration.h 2014-07-08 07:23:41 +0000
224@@ -54,10 +54,26 @@
225 virtual std::string get_accept_languages();
226 static bool is_full_lang_code(const std::string& language);
227
228+ constexpr static const char* COREAPPS_SCHEMA {"com.canonical.Unity.ClickScope"};
229+ constexpr static const char* COREAPPS_KEY {"coreApps"};
230+
231+ virtual const std::vector<std::string> get_core_apps() const;
232 virtual ~Configuration() {}
233 protected:
234 virtual std::vector<std::string> list_folder(const std::string &folder, const std::string &pattern);
235 virtual std::string architectureFromDpkg();
236+ virtual const std::vector<std::string> get_dconf_strings(const std::string& schema, const std::string& key) const;
237+ static const std::vector<std::string>& get_default_core_apps() {
238+ static std::vector<std::string> default_apps {
239+ "dialer-app",
240+ "messaging-app",
241+ "com.ubuntu.calculator",
242+ "com.ubuntu.clock",
243+ "com.ubuntu.camera",
244+ "com.ubuntu.calendar"
245+ };
246+ return default_apps;
247+ }
248 };
249
250 } // namespace click
251
252=== modified file 'libclickscope/tests/test_configuration.cpp'
253--- libclickscope/tests/test_configuration.cpp 2014-06-25 21:33:59 +0000
254+++ libclickscope/tests/test_configuration.cpp 2014-07-08 07:23:41 +0000
255@@ -27,6 +27,8 @@
256 * files in the program, then also delete it here.
257 */
258
259+#include <QStringList>
260+
261 #include <gmock/gmock.h>
262 #include <gtest/gtest.h>
263
264@@ -42,10 +44,35 @@
265 public:
266 MOCK_METHOD2(list_folder, std::vector<std::string>(
267 const std::string& folder, const std::string& pattern));
268+ MOCK_CONST_METHOD2(get_dconf_strings, const std::vector<std::string>(const std::string& schema, const std::string& key));
269+ using Configuration::get_default_core_apps;
270 };
271
272 }
273
274+TEST(Configuration, getCoreAppsFound)
275+{
276+ using namespace ::testing;
277+ FakeConfiguration c;
278+ EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA,
279+ Configuration::COREAPPS_KEY))
280+ .WillOnce(Return(std::vector<std::string>{"package1", "package2"}));
281+ auto found_apps = c.get_core_apps();
282+ auto expected_apps = std::vector<std::string>{"package1", "package2"};
283+ ASSERT_EQ(found_apps, expected_apps);
284+}
285+
286+TEST(Configuration, getCoreAppsEmpty)
287+{
288+ using namespace ::testing;
289+ FakeConfiguration c;
290+ EXPECT_CALL(c, get_dconf_strings(Configuration::COREAPPS_SCHEMA,
291+ Configuration::COREAPPS_KEY))
292+ .WillOnce(Return(std::vector<std::string>{}));
293+ auto found_apps = c.get_core_apps();
294+ auto expected_apps = c.get_default_core_apps();
295+ ASSERT_EQ(found_apps, expected_apps);
296+}
297
298 TEST(Configuration, getAvailableFrameworksUsesRightFolder)
299 {
300
301=== modified file 'scope/clickapps/apps-query.cpp'
302--- scope/clickapps/apps-query.cpp 2014-06-30 20:06:33 +0000
303+++ scope/clickapps/apps-query.cpp 2014-07-08 07:23:41 +0000
304@@ -42,6 +42,7 @@
305
306 #include <click/click-i18n.h>
307 #include "apps-query.h"
308+#include <QDebug>
309
310 namespace
311 {
312@@ -64,24 +65,6 @@
313 }
314 )";
315
316-std::string CATEGORY_APPS_SEARCH = R"(
317- {
318- "schema-version" : 1,
319- "template" : {
320- "category-layout" : "grid",
321- "card-layout" : "horizontal",
322- "card-size": "large"
323- },
324- "components" : {
325- "title" : "title",
326- "mascot" : {
327- "field": "art"
328- },
329- "subtitle": "publisher"
330- }
331- }
332-)";
333-
334 static const char CATEGORY_STORE[] = R"(
335 {
336 "template": {
337@@ -104,51 +87,138 @@
338
339 }
340
341-void click::Query::push_local_results(scopes::SearchReplyProxy const &replyProxy,
342- std::vector<click::Application> const &apps,
343- std::string &categoryTemplate)
344-{
345- scopes::CategoryRenderer rdr(categoryTemplate);
346+click::apps::ResultPusher::ResultPusher(const scopes::SearchReplyProxy &replyProxy, const std::vector<std::string>& core_apps)
347+ : replyProxy(replyProxy),
348+ core_apps(core_apps),
349+ top_apps_lookup(core_apps.begin(), core_apps.end())
350+{
351+}
352+
353+void click::apps::ResultPusher::push_result(scopes::Category::SCPtr& cat, const click::Application& a)
354+{
355+ scopes::CategorisedResult res(cat);
356+ res.set_title(a.title);
357+ res.set_art(a.icon_url);
358+ res.set_uri(a.url);
359+ res[click::apps::Query::ResultKeys::NAME] = a.name;
360+ res[click::apps::Query::ResultKeys::DESCRIPTION] = a.description;
361+ res[click::apps::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot;
362+ res[click::apps::Query::ResultKeys::INSTALLED] = true;
363+ res[click::apps::Query::ResultKeys::VERSION] = a.version;
364+ replyProxy->push(res);
365+}
366+
367+//
368+// Return an application identifier used to match applications against core-apps dconf key;
369+// For click apps, it just returns application name (e.g. com.canonical.calculator).
370+// For non-click apps, it return the desktop file name (without extension), taken from app uri.
371+std::string click::apps::ResultPusher::get_app_identifier(const click::Application& app)
372+{
373+ static const std::string app_prefix("application:///");
374+ if (!app.name.empty())
375+ {
376+ return app.name;
377+ }
378+ if (app.url.size() > app_prefix.size())
379+ {
380+ auto i = app.url.rfind('.');
381+ if (i != std::string::npos)
382+ {
383+ return app.url.substr(app_prefix.size(), i - app_prefix.size());
384+ }
385+ }
386+ throw std::runtime_error("Cannot determine application identifier for" + app.url);
387+}
388+
389+void click::apps::ResultPusher::push_local_results(
390+ const std::vector<click::Application> &apps,
391+ const std::string &categoryTemplate)
392+{
393+ const scopes::CategoryRenderer rdr(categoryTemplate);
394 auto cat = replyProxy->register_category("local", _("My apps"), "", rdr);
395
396 for(const auto & a: apps)
397 {
398- scopes::CategorisedResult res(cat);
399- res.set_title(a.title);
400- res.set_art(a.icon_url);
401- res.set_uri(a.url);
402- res[click::Query::ResultKeys::NAME] = a.name;
403- res[click::Query::ResultKeys::DESCRIPTION] = a.description;
404- res[click::Query::ResultKeys::MAIN_SCREENSHOT] = a.main_screenshot;
405- res[click::Query::ResultKeys::INSTALLED] = true;
406- res[click::Query::ResultKeys::VERSION] = a.version;
407- replyProxy->push(res);
408- }
409-}
410-
411-struct click::Query::Private
412-{
413- Private(click::Index& index, const scopes::SearchMetadata& metadata)
414- : index(index),
415- meta(metadata)
416- {
417- }
418- click::Index& index;
419+ try
420+ {
421+ if (top_apps_lookup.size() == 0 || top_apps_lookup.find(get_app_identifier(a)) == top_apps_lookup.end())
422+ {
423+ push_result(cat, a);
424+ }
425+ }
426+ catch (const std::runtime_error &e)
427+ {
428+ qWarning() << QString::fromStdString(e.what());
429+ }
430+ }
431+}
432+
433+void click::apps::ResultPusher::push_top_results(
434+ const std::vector<click::Application>& apps,
435+ const std::string& categoryTemplate)
436+{
437+ const scopes::CategoryRenderer rdr(categoryTemplate);
438+ auto cat = replyProxy->register_category("predefined", "", "", rdr);
439+
440+ //
441+ // iterate over all apps, insert those matching core apps into top_apps_to_push
442+ std::map<std::string, click::Application> top_apps_to_push;
443+ for (const auto& a: apps)
444+ {
445+ try
446+ {
447+ const auto id = get_app_identifier(a);
448+ if (top_apps_lookup.find(id) != top_apps_lookup.end())
449+ {
450+ top_apps_to_push[id] = a;
451+ if (core_apps.size() == top_apps_to_push.size())
452+ {
453+ // no need to iterate over remaining apps
454+ break;
455+ }
456+ }
457+ }
458+ catch (const std::runtime_error &e)
459+ {
460+ qWarning() << QString::fromStdString(e.what());
461+ }
462+ }
463+
464+ //
465+ // iterate over core apps and insert them based on top_apps_to_push;
466+ // this way the order of core apps is preserved.
467+ for (const auto &a: core_apps)
468+ {
469+ auto const it = top_apps_to_push.find(a);
470+ if (it != top_apps_to_push.end())
471+ {
472+ push_result(cat, it->second);
473+ }
474+ }
475+}
476+
477+struct click::apps::Query::Private
478+{
479+ Private(const scopes::SearchMetadata& metadata)
480+ : meta(metadata)
481+ {
482+ }
483 scopes::SearchMetadata meta;
484+ click::Configuration configuration;
485 };
486
487-click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata)
488+click::apps::Query::Query(unity::scopes::CannedQuery const& query, scopes::SearchMetadata const& metadata)
489 : unity::scopes::SearchQueryBase(query, metadata),
490- impl(new Private(index, metadata))
491+ impl(new Private(metadata))
492 {
493 }
494
495-void click::Query::cancelled()
496+void click::apps::Query::cancelled()
497 {
498 qDebug() << "cancelling search of" << QString::fromStdString(query().query_string());
499 }
500
501-click::Query::~Query()
502+click::apps::Query::~Query()
503 {
504 qDebug() << "destroying search";
505 }
506@@ -165,7 +235,7 @@
507
508 }
509
510-void click::Query::add_fake_store_app(scopes::SearchReplyProxy const& searchReply)
511+void click::apps::Query::add_fake_store_app(scopes::SearchReplyProxy const& searchReply)
512 {
513 static const std::string title = _("Ubuntu Store");
514 static const std::string cat_title = _("Get more apps from the store");
515@@ -185,27 +255,28 @@
516 res.set_title(title);
517 res.set_art(STORE_DATA_DIR "/store-scope-icon.svg");
518 res.set_uri(store_scope.to_uri());
519- res[click::Query::ResultKeys::NAME] = title;
520- res[click::Query::ResultKeys::DESCRIPTION] = "";
521- res[click::Query::ResultKeys::MAIN_SCREENSHOT] = "";
522- res[click::Query::ResultKeys::INSTALLED] = true;
523- res[click::Query::ResultKeys::VERSION] = "";
524+ res[click::apps::Query::ResultKeys::NAME] = title;
525+ res[click::apps::Query::ResultKeys::DESCRIPTION] = "";
526+ res[click::apps::Query::ResultKeys::MAIN_SCREENSHOT] = "";
527+ res[click::apps::Query::ResultKeys::INSTALLED] = true;
528+ res[click::apps::Query::ResultKeys::VERSION] = "";
529 searchReply->push(res);
530 }
531 }
532
533-void click::Query::run(scopes::SearchReplyProxy const& searchReply)
534+void click::apps::Query::run(scopes::SearchReplyProxy const& searchReply)
535 {
536+ const std::string categoryTemplate = CATEGORY_APPS_DISPLAY;
537 auto querystr = query().query_string();
538- std::string categoryTemplate = CATEGORY_APPS_SEARCH;
539+
540+ ResultPusher pusher(searchReply, querystr.empty() ? impl->configuration.get_core_apps() : std::vector<std::string>());
541+ auto localResults = clickInterfaceInstance().find_installed_apps(querystr);
542+
543 if (querystr.empty()) {
544- categoryTemplate = CATEGORY_APPS_DISPLAY;
545+ pusher.push_top_results(localResults, categoryTemplate);
546 }
547- auto localResults = clickInterfaceInstance().find_installed_apps(
548- querystr);
549
550- push_local_results(
551- searchReply,
552+ pusher.push_local_results(
553 localResults,
554 categoryTemplate);
555
556
557=== modified file 'scope/clickapps/apps-query.h'
558--- scope/clickapps/apps-query.h 2014-05-28 14:27:28 +0000
559+++ scope/clickapps/apps-query.h 2014-07-08 07:23:41 +0000
560@@ -37,14 +37,19 @@
561
562 #include <QSharedPointer>
563 #include <set>
564+#include <unordered_set>
565
566
567 namespace click
568 {
569
570 class Application;
571+class Configuration;
572 class Index;
573
574+namespace apps
575+{
576+
577 class Query : public scopes::SearchQueryBase
578 {
579 public:
580@@ -60,7 +65,7 @@
581 constexpr static const char* VERSION{"version"};
582 };
583
584- Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata);
585+ Query(unity::scopes::CannedQuery const& query, scopes::SearchMetadata const& metadata);
586 virtual ~Query();
587
588 virtual void cancelled() override;
589@@ -69,14 +74,32 @@
590
591 protected:
592 virtual void add_fake_store_app(scopes::SearchReplyProxy const &replyProxy);
593- virtual void push_local_results(scopes::SearchReplyProxy const &replyProxy,
594- std::vector<click::Application> const &apps,
595- std::string& categoryTemplate);
596-
597 private:
598 struct Private;
599 QSharedPointer<Private> impl;
600 };
601-}
602+
603+class ResultPusher
604+{
605+ const scopes::SearchReplyProxy &replyProxy;
606+ std::vector<std::string> core_apps;
607+ std::unordered_set<std::string> top_apps_lookup;
608+
609+public:
610+ ResultPusher(const scopes::SearchReplyProxy &replyProxy, const std::vector<std::string>& core_apps);
611+ virtual ~ResultPusher() = default;
612+
613+ virtual void push_local_results(const std::vector<click::Application> &apps,
614+ const std::string& categoryTemplate);
615+
616+ virtual void push_top_results(
617+ const std::vector<click::Application>& apps,
618+ const std::string& categoryTemplate);
619+protected:
620+ virtual void push_result(scopes::Category::SCPtr& cat, const click::Application& a);
621+ static std::string get_app_identifier(const click::Application& app);
622+};
623+} // namespace apps
624+} // namespace query
625
626 #endif // CLICK_QUERY_H
627
628=== modified file 'scope/clickapps/apps-scope.cpp'
629--- scope/clickapps/apps-scope.cpp 2014-06-18 16:23:50 +0000
630+++ scope/clickapps/apps-scope.cpp 2014-07-08 07:23:41 +0000
631@@ -42,6 +42,7 @@
632 #include "apps-scope.h"
633 #include "apps-query.h"
634
635+using namespace click;
636
637 click::Scope::Scope()
638 {
639@@ -78,7 +79,7 @@
640
641 scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata)
642 {
643- return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, metadata));
644+ return scopes::SearchQueryBase::UPtr(new click::apps::Query(q, metadata));
645 }
646
647
648
649=== modified file 'scope/tests/CMakeLists.txt'
650--- scope/tests/CMakeLists.txt 2014-06-23 15:00:28 +0000
651+++ scope/tests/CMakeLists.txt 2014-07-08 07:23:41 +0000
652@@ -1,4 +1,5 @@
653 set (CLICKSCOPE_TESTS_TARGET click-scope-tests)
654+set (APPS_SCOPE_TESTS_TARGET apps-scope-tests)
655 find_package(Threads)
656
657 # Qt5 bits
658@@ -19,7 +20,12 @@
659 test_query.cpp
660 )
661
662+add_executable (${APPS_SCOPE_TESTS_TARGET}
663+ test_apps_query.cpp
664+)
665+
666 qt5_use_modules(${CLICKSCOPE_TESTS_TARGET} Core DBus Network Test)
667+qt5_use_modules(${APPS_SCOPE_TESTS_TARGET} Core DBus Network Test)
668
669 target_link_libraries(${CLICKSCOPE_TESTS_TARGET}
670 ${STORE_LIB_UNVERSIONED}
671@@ -37,6 +43,22 @@
672 ${CMAKE_THREAD_LIBS_INIT}
673 )
674
675+target_link_libraries(${APPS_SCOPE_TESTS_TARGET}
676+ ${APPS_LIB_UNVERSIONED}
677+ ${SCOPE_LIB_NAME}
678+
679+ ${UNITY_SCOPES_LDFLAGS}
680+ ${UBUNTUONE_LDFLAGS}
681+ ${UBUNTU_DOWNLOAD_MANAGER_CLIENT_LDFLAGS}
682+ ${UBUNTU_DOWNLOAD_MANAGER_COMMON_LDFLAGS}
683+ ${JSON_CPP_LDFLAGS}
684+
685+ gmock
686+ gmock_main
687+
688+ ${CMAKE_THREAD_LIBS_INIT}
689+)
690+
691 add_custom_target (test-click-scope
692 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${CLICKSCOPE_TESTS_TARGET}
693 DEPENDS ${CLICKSCOPE_TESTS_TARGET}
694@@ -56,6 +78,28 @@
695 DEPENDS ${CLICKSCOPE_TESTS_TARGET}
696 )
697
698+# ---
699+
700+add_custom_target (test-apps-scope
701+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET}
702+ DEPENDS ${APPS_SCOPE_TESTS_TARGET}
703+)
704+
705+add_custom_target(test-apps-scope-valgrind
706+ COMMAND valgrind --tool=memcheck ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET}
707+ DEPENDS ${APPS_SCOPE_TESTS_TARGET}
708+)
709+
710+add_custom_target(test-apps-scope-leaks
711+ COMMAND valgrind --tool=memcheck --track-origins=yes --num-callers=40 --leak-resolution=high --leak-check=full ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET}
712+ DEPENDS ${APPS_SCOPE_TESTS_TARGET}
713+)
714+add_custom_target (test-apps-scope-disabled
715+ COMMAND GTEST_ALSO_RUN_DISABLED_TESTS=1 ${CMAKE_CURRENT_BINARY_DIR}/${APPS_SCOPE_TESTS_TARGET}
716+ DEPENDS ${APPS_SCOPE_TESTS_TARGET}
717+)
718+
719+
720 add_subdirectory(integration)
721 add_subdirectory(download_manager_tool)
722 add_subdirectory(click_interface_tool)
723
724=== added file 'scope/tests/test_apps_query.cpp'
725--- scope/tests/test_apps_query.cpp 1970-01-01 00:00:00 +0000
726+++ scope/tests/test_apps_query.cpp 2014-07-08 07:23:41 +0000
727@@ -0,0 +1,84 @@
728+/*
729+ * Copyright (C) 2014 Canonical Ltd.
730+ *
731+ * This program is free software: you can redistribute it and/or modify it
732+ * under the terms of the GNU General Public License version 3, as published
733+ * by the Free Software Foundation.
734+ *
735+ * This program is distributed in the hope that it will be useful, but
736+ * WITHOUT ANY WARRANTY; without even the implied warranties of
737+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
738+ * PURPOSE. See the GNU General Public License for more details.
739+ *
740+ * You should have received a copy of the GNU General Public License along
741+ * with this program. If not, see <http://www.gnu.org/licenses/>.
742+ *
743+ * In addition, as a special exception, the copyright holders give
744+ * permission to link the code of portions of this program with the
745+ * OpenSSL library under certain conditions as described in each
746+ * individual source file, and distribute linked combinations
747+ * including the two.
748+ * You must obey the GNU General Public License in all respects
749+ * for all of the code used other than OpenSSL. If you modify
750+ * file(s) with this exception, you may extend this exception to your
751+ * version of the file(s), but you are not obligated to do so. If you
752+ * do not wish to do so, delete this exception statement from your
753+ * version. If you delete this exception statement from all source
754+ * files in the program, then also delete it here.
755+ */
756+
757+#include <string>
758+#include <memory>
759+
760+#include <gtest/gtest.h>
761+#include <gmock/gmock.h>
762+
763+#include <clickapps/apps-query.h>
764+
765+#include <unity/scopes/SearchReply.h>
766+#include <unity/scopes/testing/MockSearchReply.h>
767+
768+#include "test_helpers.h"
769+
770+using namespace click::test::helpers;
771+using namespace ::testing;
772+
773+class ResultPusherTest : public ::testing::Test
774+{
775+protected:
776+ scopes::SearchReplyProxy reply;
777+public:
778+ ResultPusherTest()
779+ {
780+ reply.reset(new scopes::testing::MockSearchReply());
781+ }
782+};
783+
784+MATCHER_P(HasApplicationTitle, n, "") { return arg["title"].get_string() == n; }
785+
786+TEST_F(ResultPusherTest, testPushTopAndLocalResults)
787+{
788+ std::string categoryTemplate("{}");
789+ std::vector<click::Application> apps {
790+ {"app1", "App1", 0.0f, "icon", "url", "", "sshot"},
791+ {"app2", "App2", 0.0f, "icon", "url", "", "sshot"},
792+ {"app3", "App3", 0.0f, "icon", "url", "", "sshot"},
793+ {"", "App4", 0.0f, "icon", "application:///app4.desktop", "", "sshot"} // a non-click app
794+ };
795+
796+ click::apps::ResultPusher pusher(reply, {"app2", "app4"});
797+ auto mockreply = (scopes::testing::MockSearchReply*)reply.get();
798+
799+ scopes::CategoryRenderer renderer("{}");
800+ auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
801+
802+ EXPECT_CALL(*mockreply, register_category(_, _, _, _)).WillRepeatedly(Return(ptrCat));
803+ EXPECT_CALL(*mockreply, push(Matcher<unity::scopes::CategorisedResult const&>(HasApplicationTitle(std::string("App2")))));
804+ EXPECT_CALL(*mockreply, push(Matcher<unity::scopes::CategorisedResult const&>(HasApplicationTitle(std::string("App4")))));
805+
806+ EXPECT_CALL(*mockreply, push(Matcher<unity::scopes::CategorisedResult const&>(HasApplicationTitle(std::string("App1")))));
807+ EXPECT_CALL(*mockreply, push(Matcher<unity::scopes::CategorisedResult const&>(HasApplicationTitle(std::string("App3")))));
808+ pusher.push_top_results(apps, categoryTemplate);
809+ pusher.push_local_results(apps, categoryTemplate);
810+}
811+
812
813=== added file 'scope/tests/test_helpers.h'
814--- scope/tests/test_helpers.h 1970-01-01 00:00:00 +0000
815+++ scope/tests/test_helpers.h 2014-07-08 07:23:41 +0000
816@@ -0,0 +1,71 @@
817+#ifndef TEST_HELPERS_H
818+#define TEST_HELPERS_H
819+
820+#include <click/department-lookup.h>
821+#include <click/highlights.h>
822+#include "click/index.h"
823+#include <click/interface.h>
824+#include <click/package.h>
825+
826+namespace click
827+{
828+namespace test
829+{
830+namespace helpers
831+{
832+static const std::string FAKE_QUERY {"FAKE_QUERY"};
833+static const std::string FAKE_CATEGORY_TEMPLATE {"{}"};
834+
835+
836+class MockIndex : public click::Index {
837+ click::Packages packages;
838+ click::Packages recommends;
839+ click::DepartmentList departments;
840+ click::DepartmentList bootstrap_departments;
841+ click::HighlightList bootstrap_highlights;
842+public:
843+ MockIndex(click::Packages packages = click::Packages(),
844+ click::DepartmentList departments = click::DepartmentList(),
845+ click::DepartmentList boot_departments = click::DepartmentList())
846+ : Index(QSharedPointer<click::web::Client>()),
847+ packages(packages),
848+ departments(departments),
849+ bootstrap_departments(boot_departments)
850+ {
851+
852+ }
853+
854+ click::web::Cancellable search(const std::string &query, std::function<void (click::Packages, click::Packages)> callback) override
855+ {
856+ do_search(query, callback);
857+ callback(packages, recommends);
858+ return click::web::Cancellable();
859+ }
860+
861+ click::web::Cancellable bootstrap(std::function<void(const click::DepartmentList&, const click::HighlightList&, Error, int)> callback) override
862+ {
863+ callback(bootstrap_departments, bootstrap_highlights, click::Index::Error::NoError, 0);
864+ return click::web::Cancellable();
865+ }
866+
867+ MOCK_METHOD2(do_search,
868+ void(const std::string&,
869+ std::function<void(click::Packages, click::Packages)>));
870+};
871+
872+
873+class FakeCategory : public scopes::Category
874+{
875+public:
876+ FakeCategory(std::string const& id, std::string const& title,
877+ std::string const& icon, scopes::CategoryRenderer const& renderer) :
878+ scopes::Category(id, title, icon, renderer)
879+ {
880+ }
881+
882+};
883+} // namespace helpers
884+} // namespace test
885+} // namespace click
886+
887+#endif // TEST_HELPERS_H
888
889=== modified file 'scope/tests/test_query.cpp'
890--- scope/tests/test_query.cpp 2014-06-24 17:18:53 +0000
891+++ scope/tests/test_query.cpp 2014-07-08 07:23:41 +0000
892@@ -35,8 +35,8 @@
893
894 #include "click/qtbridge.h"
895 #include "clickstore/store-query.h"
896-#include "click/index.h"
897 #include "click/application.h"
898+#include "test_helpers.h"
899
900 #include <tests/mock_network_access_manager.h>
901
902@@ -49,51 +49,10 @@
903
904 using namespace ::testing;
905 using namespace click;
906+using namespace click::test::helpers;
907
908 namespace
909 {
910-static const std::string FAKE_QUERY {"FAKE_QUERY"};
911-static const std::string FAKE_CATEGORY_TEMPLATE {"{}"};
912-
913-
914-class MockIndex : public click::Index {
915- click::Packages packages;
916- click::Packages recommends;
917- click::DepartmentList departments;
918- click::DepartmentList bootstrap_departments;
919- click::HighlightList bootstrap_highlights;
920-
921-public:
922- MockIndex(click::Packages packages = click::Packages(),
923- click::DepartmentList departments = click::DepartmentList(),
924- click::DepartmentList boot_departments = click::DepartmentList())
925- : Index(QSharedPointer<click::web::Client>()),
926- packages(packages),
927- departments(departments),
928- bootstrap_departments(boot_departments)
929- {
930-
931- }
932-
933- click::web::Cancellable search(const std::string &query, std::function<void (click::Packages, click::Packages)> callback) override
934- {
935- do_search(query, callback);
936- callback(packages, recommends);
937- return click::web::Cancellable();
938- }
939-
940-
941- click::web::Cancellable bootstrap(std::function<void(const click::DepartmentList&, const click::HighlightList&, Error, int)> callback) override
942- {
943- callback(bootstrap_departments, bootstrap_highlights, click::Index::Error::NoError, 0);
944- return click::web::Cancellable();
945- }
946-
947- MOCK_METHOD2(do_search,
948- void(const std::string&,
949- std::function<void(click::Packages, click::Packages)>));
950-};
951-
952 class MockQueryBase : public click::Query {
953 public:
954 MockQueryBase(const unity::scopes::CannedQuery& query, click::Index& index,
955@@ -140,7 +99,7 @@
956 public:
957 MockQueryRun(const unity::scopes::CannedQuery& query, click::Index& index,
958 click::DepartmentLookup& depts,
959- click::HighlightList& highlights,
960+ click::HighlightList& highlights,
961 scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, highlights, metadata)
962 {
963
964@@ -154,17 +113,6 @@
965 std::string& categoryTemplate));
966 MOCK_METHOD0(get_installed_packages, PackageSet());
967 };
968-
969-class FakeCategory : public scopes::Category
970-{
971-public:
972- FakeCategory(std::string const& id, std::string const& title,
973- std::string const& icon, scopes::CategoryRenderer const& renderer) :
974- scopes::Category(id, title, icon, renderer)
975- {
976- }
977-
978-};
979 } // namespace
980
981 TEST(QueryTest, testAddAvailableAppsCallsClickIndex)

Subscribers

People subscribed via source and target branches

to all changes: