Merge lp:~pete-woods/qtcreator-plugin-ubuntu/new-scope-template into lp:qtcreator-plugin-ubuntu

Proposed by Pete Woods
Status: Merged
Approved by: Zoltan Balogh
Approved revision: 259
Merged at revision: 260
Proposed branch: lp:~pete-woods/qtcreator-plugin-ubuntu/new-scope-template
Merge into: lp:qtcreator-plugin-ubuntu
Diff against target: 1712 lines (+1234/-276)
32 files modified
share/qtcreator/templates/wizards/ubuntu/scope/CMakeLists.txt (+63/-17)
share/qtcreator/templates/wizards/ubuntu/scope/cmake/FindGMock.cmake (+12/-0)
share/qtcreator/templates/wizards/ubuntu/scope/data/CMakeLists.txt (+13/-8)
share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini (+9/-0)
share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini.in (+0/-8)
share/qtcreator/templates/wizards/ubuntu/scope/include/api/client.h (+121/-0)
share/qtcreator/templates/wizards/ubuntu/scope/include/api/config.h (+25/-0)
share/qtcreator/templates/wizards/ubuntu/scope/include/scope/preview.h (+37/-0)
share/qtcreator/templates/wizards/ubuntu/scope/include/scope/query.h (+36/-0)
share/qtcreator/templates/wizards/ubuntu/scope/include/scope/scope.h (+53/-0)
share/qtcreator/templates/wizards/ubuntu/scope/src/CMakeLists.txt (+50/-11)
share/qtcreator/templates/wizards/ubuntu/scope/src/api/client.cpp (+157/-0)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.cpp (+0/-49)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.h (+0/-18)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.cpp (+0/-54)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.h (+0/-17)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.cpp (+0/-49)
share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.h (+0/-24)
share/qtcreator/templates/wizards/ubuntu/scope/src/scope/preview.cpp (+60/-0)
share/qtcreator/templates/wizards/ubuntu/scope/src/scope/query.cpp (+177/-0)
share/qtcreator/templates/wizards/ubuntu/scope/src/scope/scope.cpp (+58/-0)
share/qtcreator/templates/wizards/ubuntu/scope/test/CMakeLists.txt (+0/-2)
share/qtcreator/templates/wizards/ubuntu/scope/test/simpletest.cpp (+0/-3)
share/qtcreator/templates/wizards/ubuntu/scope/tests/CMakeLists.txt (+25/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/London,uk.json (+1/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/Manchester,uk.json (+1/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/server/server.py (+51/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.json (+1/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.json (+1/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/CMakeLists.txt (+24/-0)
share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/scope/test-scope.cpp (+225/-0)
share/qtcreator/templates/wizards/ubuntu/scope/wizard.xml (+34/-16)
To merge this branch: bzr merge lp:~pete-woods/qtcreator-plugin-ubuntu/new-scope-template
Reviewer Review Type Date Requested Status
Benjamin Zeller Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+233218@code.launchpad.net

Commit message

New scope template that talks to openweather API

Description of the change

New scope template that talks to openweather API

To post a comment you must log in.
253. By Pete Woods

Process-cpp is used only for testing

254. By Pete Woods

Update images

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
255. By Pete Woods

Add documentation to the examples

256. By Pete Woods

Document the CMakeLists.txt code

257. By Pete Woods

It's a png

258. By Pete Woods

Typo

259. By Pete Woods

The scope is your own :)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Benjamin Zeller (zeller-benjamin) wrote :

Ok

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'share/qtcreator/templates/wizards/ubuntu/scope/CMakeLists.txt'
2--- share/qtcreator/templates/wizards/ubuntu/scope/CMakeLists.txt 2014-08-18 15:29:28 +0000
3+++ share/qtcreator/templates/wizards/ubuntu/scope/CMakeLists.txt 2014-09-03 15:09:42 +0000
4@@ -1,23 +1,69 @@
5 project(%ProjectName:l% CXX)
6 cmake_minimum_required(VERSION 2.8.10)
7-set(SCOPE_NAME %ClickHookName:l%)
8-set(SCOPE_LIB_TARGET_NAME %ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%)
9-
10-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pedantic -Wall -Wextra")
11+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")
12+
13+# We require g++ 4.9, to avoid ABI breakage with earlier version.
14+set(cxx_version_required 4.9)
15+if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
16+ if (NOT CMAKE_CXX_COMPILER_VERSION MATCHES "^${cxx_version_required}")
17+ message(FATAL_ERROR "g++ version must be ${cxx_version_required}!")
18+ endif()
19+endif()
20+
21+# Set strict and naggy C++ compiler flags, and enable C++11
22+add_definitions(
23+ -fno-permissive
24+ -std=c++11
25+ -pedantic
26+ -Wall
27+ -Wextra
28+ -fPIC
29+)
30+
31+include(GNUInstallDirs)
32+find_package(PkgConfig)
33+
34+# We depend on Boost for string trimming
35+find_package(
36+ Boost
37+ REQUIRED
38+)
39+
40+# Search for our dependencies
41+pkg_check_modules(
42+ SCOPE
43+ libunity-scopes>=0.6.0
44+ jsoncpp
45+ net-cpp>=1.1.0
46+ REQUIRED
47+)
48+
49+# Add our dependencies to the include paths
50+include_directories(
51+ "${CMAKE_SOURCE_DIR}/include"
52+ ${Boost_INCLUDE_DIRS}
53+ ${SCOPE_INCLUDE_DIRS}
54+)
55+
56 set(UBUNTU_PROJECT_TYPE "Scope" CACHE INTERNAL "Tells QtCreator this is a Scope project")
57-
58-set(INI_INSTALLDIR "/${SCOPE_NAME}")
59-set(SCOPE_INSTALLDIR "/${SCOPE_NAME}")
60-
61-include(FindPkgConfig)
62-pkg_check_modules(UNITY_SCOPES libunity-scopes>=0.5.0 REQUIRED)
63-
64+set(SCOPE_INSTALL_DIR "/%ClickHookName:l%")
65+
66+# If we need to refer to the scope's name or package in code, these definitions will help
67+add_definitions(-DPACKAGE_NAME="%ClickDomain:l%.%ProjectName:l%")
68+add_definitions(-DSCOPE_NAME="%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%")
69+
70+# Install the click manifest
71+install(FILES manifest.json DESTINATION "/")
72+install(FILES "%ClickHookName:l%.apparmor" DESTINATION "/")
73+
74+# Add our main directories
75 add_subdirectory(src)
76 add_subdirectory(data)
77+
78+# Set up the tests
79 enable_testing()
80-add_subdirectory(test)
81-
82-install(FILES manifest.json DESTINATION "/")
83-install(FILES "%ClickHookName:l%.apparmor" DESTINATION "/")
84-
85-add_custom_target("%ProjectName:l%_ClickFiles" ALL SOURCES "%ClickHookName:l%.apparmor" "manifest.json")
86+add_subdirectory(tests)
87+add_custom_target(
88+ check
89+ ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --output-on-failure
90+)
91
92=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/cmake'
93=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/cmake/FindGMock.cmake'
94--- share/qtcreator/templates/wizards/ubuntu/scope/cmake/FindGMock.cmake 1970-01-01 00:00:00 +0000
95+++ share/qtcreator/templates/wizards/ubuntu/scope/cmake/FindGMock.cmake 2014-09-03 15:09:42 +0000
96@@ -0,0 +1,12 @@
97+# Build with system gmock and embedded gtest
98+set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory")
99+set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory")
100+set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory")
101+
102+add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock")
103+
104+set(GTEST_LIBRARIES gtest)
105+set(GTEST_MAIN_LIBRARIES gtest_main)
106+set(GMOCK_LIBRARIES gmock gmock_main)
107+
108+set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
109
110=== modified file 'share/qtcreator/templates/wizards/ubuntu/scope/data/CMakeLists.txt'
111--- share/qtcreator/templates/wizards/ubuntu/scope/data/CMakeLists.txt 2014-08-04 07:25:56 +0000
112+++ share/qtcreator/templates/wizards/ubuntu/scope/data/CMakeLists.txt 2014-09-03 15:09:42 +0000
113@@ -1,11 +1,16 @@
114-# Put the ini file in the build directory next to the scope
115-# .so file so test tools can find both easily.
116-configure_file(
117- "%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini.in"
118- "${CMAKE_BINARY_DIR}/src/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"
119+
120+# Install the scope ini file
121+install(
122+ FILES "%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"
123+ DESTINATION ${SCOPE_INSTALL_DIR}
124 )
125
126-INSTALL(
127- FILES "${CMAKE_BINARY_DIR}/src/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"
128- DESTINATION "${INI_INSTALLDIR}"
129+# Install the scope images
130+install(
131+ FILES
132+ "icon.png"
133+ "logo.png"
134+ "screenshot.png"
135+ DESTINATION
136+ "${SCOPE_INSTALL_DIR}"
137 )
138
139=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini'
140--- share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini 1970-01-01 00:00:00 +0000
141+++ share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini 2014-09-03 15:09:42 +0000
142@@ -0,0 +1,9 @@
143+[ScopeConfig]
144+DisplayName = %ProjectName:c% Scope
145+Description = This is a %ProjectName:c% scope
146+Art = screenshot.png
147+Author = Firstname Lastname
148+Icon = icon.png
149+
150+[Appearance]
151+PageHeader.Logo = logo.png
152
153=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini.in'
154--- share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini.in 2014-08-04 08:14:53 +0000
155+++ share/qtcreator/templates/wizards/ubuntu/scope/data/displayName.ini.in 1970-01-01 00:00:00 +0000
156@@ -1,8 +0,0 @@
157-[ScopeConfig]
158-DisplayName = %ProjectName:c% Scope
159-Description = This is a %ProjectName:c% scope
160-Author = Firstname Lastname
161-Art = %ProjectName:l%.Art
162-Icon = %ProjectName:l%.Icon
163-SearchHint = %ProjectName:l%.SearchHint
164-HotKey = %ProjectName:l%.HotKey
165
166=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/data/icon.png'
167Binary files share/qtcreator/templates/wizards/ubuntu/scope/data/icon.png 1970-01-01 00:00:00 +0000 and share/qtcreator/templates/wizards/ubuntu/scope/data/icon.png 2014-09-03 15:09:42 +0000 differ
168=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/data/logo.png'
169Binary files share/qtcreator/templates/wizards/ubuntu/scope/data/logo.png 1970-01-01 00:00:00 +0000 and share/qtcreator/templates/wizards/ubuntu/scope/data/logo.png 2014-09-03 15:09:42 +0000 differ
170=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/data/screenshot.png'
171Binary files share/qtcreator/templates/wizards/ubuntu/scope/data/screenshot.png 1970-01-01 00:00:00 +0000 and share/qtcreator/templates/wizards/ubuntu/scope/data/screenshot.png 2014-09-03 15:09:42 +0000 differ
172=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/include'
173=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/include/api'
174=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/include/api/client.h'
175--- share/qtcreator/templates/wizards/ubuntu/scope/include/api/client.h 1970-01-01 00:00:00 +0000
176+++ share/qtcreator/templates/wizards/ubuntu/scope/include/api/client.h 2014-09-03 15:09:42 +0000
177@@ -0,0 +1,121 @@
178+#ifndef API_CLIENT_H_
179+#define API_CLIENT_H_
180+
181+#include <api/config.h>
182+
183+#include <atomic>
184+#include <deque>
185+#include <map>
186+#include <string>
187+#include <core/net/http/request.h>
188+#include <core/net/uri.h>
189+
190+namespace Json {
191+class Value;
192+}
193+
194+namespace api {
195+
196+/**
197+ * Provide a nice way to access the HTTP API.
198+ *
199+ * We don't want our scope's code to be mixed together with HTTP and JSON handling.
200+ */
201+class Client {
202+public:
203+ /**
204+ * Information about a City
205+ */
206+ struct City {
207+ unsigned int id;
208+ std::string name;
209+ std::string country;
210+ };
211+
212+ /**
213+ * Temperature information for a day.
214+ */
215+ struct Temp {
216+ double max;
217+ double min;
218+ double cur;
219+ };
220+
221+ /**
222+ * Weather information for a day.
223+ */
224+ struct Weather {
225+ unsigned int id;
226+ std::string main;
227+ std::string description;
228+ std::string icon;
229+ Temp temp;
230+ };
231+
232+ /**
233+ * A list of weather information
234+ */
235+ typedef std::deque<Weather> WeatherList;
236+
237+ /**
238+ * Weather information about the current day
239+ */
240+ struct Current {
241+ City city;
242+ Weather weather;
243+ };
244+
245+ /**
246+ * Forecast information about a city
247+ */
248+ struct Forecast {
249+ City city;
250+ WeatherList weather;
251+ };
252+
253+ Client(Config::Ptr config);
254+
255+ virtual ~Client() = default;
256+
257+ /**
258+ * Get the current weather for the specified location
259+ */
260+ virtual Current weather(const std::string &query);
261+
262+ /**
263+ * Get the weather forecast for the specified location and duration
264+ */
265+ virtual Forecast forecast_daily(const std::string &query, unsigned int days = 7);
266+
267+ /**
268+ * Cancel any pending queries (this method can be called from a different thread)
269+ */
270+ virtual void cancel();
271+
272+ virtual Config::Ptr config();
273+
274+protected:
275+ void get(const core::net::Uri::Path &path,
276+ const core::net::Uri::QueryParameters &parameters,
277+ Json::Value &root);
278+
279+ /**
280+ * Progress callback that allows the query to cancel pending HTTP requests.
281+ */
282+ core::net::http::Request::Progress::Next progress_report(
283+ const core::net::http::Request::Progress& progress);
284+
285+ /**
286+ * Hang onto the configuration information
287+ */
288+ Config::Ptr config_;
289+
290+ /**
291+ * Thread-safe cancelled flag
292+ */
293+ std::atomic<bool> cancelled_;
294+};
295+
296+}
297+
298+#endif // API_CLIENT_H_
299
300=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/include/api/config.h'
301--- share/qtcreator/templates/wizards/ubuntu/scope/include/api/config.h 1970-01-01 00:00:00 +0000
302+++ share/qtcreator/templates/wizards/ubuntu/scope/include/api/config.h 2014-09-03 15:09:42 +0000
303@@ -0,0 +1,25 @@
304+#ifndef API_CONFIG_H_
305+#define API_CONFIG_H_
306+
307+#include <memory>
308+#include <string>
309+
310+namespace api {
311+
312+struct Config {
313+ typedef std::shared_ptr<Config> Ptr;
314+
315+ /*
316+ * The root of all API request URLs
317+ */
318+ std::string apiroot { "http://api.openweathermap.org" };
319+
320+ /*
321+ * The custom HTTP user agent string for this library
322+ */
323+ std::string user_agent { "example-network-scope 0.1; (foo)" };
324+};
325+
326+}
327+
328+#endif /* API_CONFIG_H_ */
329
330=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/include/scope'
331=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/include/scope/preview.h'
332--- share/qtcreator/templates/wizards/ubuntu/scope/include/scope/preview.h 1970-01-01 00:00:00 +0000
333+++ share/qtcreator/templates/wizards/ubuntu/scope/include/scope/preview.h 2014-09-03 15:09:42 +0000
334@@ -0,0 +1,37 @@
335+#ifndef SCOPE_PREVIEW_H_
336+#define SCOPE_PREVIEW_H_
337+
338+#include <unity/scopes/PreviewQueryBase.h>
339+
340+namespace unity {
341+namespace scopes {
342+class Result;
343+}
344+}
345+
346+namespace scope {
347+
348+/**
349+ * Represents an individual preview request.
350+ *
351+ * Each time a result is previewed in the UI a new Preview
352+ * object is created.
353+ */
354+class Preview: public unity::scopes::PreviewQueryBase {
355+public:
356+ Preview(const unity::scopes::Result &result,
357+ const unity::scopes::ActionMetadata &metadata);
358+
359+ ~Preview() = default;
360+
361+ void cancelled() override;
362+
363+ /**
364+ * Populates the reply object with preview information.
365+ */
366+ void run(unity::scopes::PreviewReplyProxy const& reply) override;
367+};
368+
369+}
370+
371+#endif // SCOPE_PREVIEW_H_
372
373=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/include/scope/query.h'
374--- share/qtcreator/templates/wizards/ubuntu/scope/include/scope/query.h 1970-01-01 00:00:00 +0000
375+++ share/qtcreator/templates/wizards/ubuntu/scope/include/scope/query.h 2014-09-03 15:09:42 +0000
376@@ -0,0 +1,36 @@
377+#ifndef SCOPE_QUERY_H_
378+#define SCOPE_QUERY_H_
379+
380+#include <api/client.h>
381+
382+#include <unity/scopes/SearchQueryBase.h>
383+#include <unity/scopes/ReplyProxyFwd.h>
384+
385+namespace scope {
386+
387+/**
388+ * Represents an individual query.
389+ *
390+ * A new Query object will be constructed for each query. It is
391+ * given query information, metadata about the search, and
392+ * some scope-specific configuration.
393+ */
394+class Query: public unity::scopes::SearchQueryBase {
395+public:
396+ Query(const unity::scopes::CannedQuery &query,
397+ const unity::scopes::SearchMetadata &metadata, api::Config::Ptr config);
398+
399+ ~Query() = default;
400+
401+ void cancelled() override;
402+
403+ void run(const unity::scopes::SearchReplyProxy &reply) override;
404+
405+private:
406+ api::Client client_;
407+};
408+
409+}
410+
411+#endif // SCOPE_QUERY_H_
412+
413
414=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/include/scope/scope.h'
415--- share/qtcreator/templates/wizards/ubuntu/scope/include/scope/scope.h 1970-01-01 00:00:00 +0000
416+++ share/qtcreator/templates/wizards/ubuntu/scope/include/scope/scope.h 2014-09-03 15:09:42 +0000
417@@ -0,0 +1,53 @@
418+#ifndef SCOPE_SCOPE_H_
419+#define SCOPE_SCOPE_H_
420+
421+#include <api/config.h>
422+
423+#include <unity/scopes/ScopeBase.h>
424+#include <unity/scopes/QueryBase.h>
425+#include <unity/scopes/ReplyProxyFwd.h>
426+#include <unity/scopes/QueryBase.h>
427+#include <unity/scopes/PreviewQueryBase.h>
428+
429+namespace scope {
430+
431+/**
432+ * Defines the lifecycle of scope plugin, and acts as a factory
433+ * for Query and Preview objects.
434+ *
435+ * Note that the #preview and #search methods are each called on
436+ * different threads, so some form of interlocking is required
437+ * if shared data structures are used.
438+ */
439+class Scope: public unity::scopes::ScopeBase {
440+public:
441+ /**
442+ * Called once at startup
443+ */
444+ void start(std::string const&) override;
445+
446+ /**
447+ * Called at shutdown
448+ */
449+ void stop() override;
450+
451+ /**
452+ * Called each time a new preview is requested
453+ */
454+ unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result&,
455+ const unity::scopes::ActionMetadata&) override;
456+
457+ /**
458+ * Called each time a new query is requested
459+ */
460+ unity::scopes::SearchQueryBase::UPtr search(
461+ unity::scopes::CannedQuery const& q,
462+ unity::scopes::SearchMetadata const&) override;
463+
464+protected:
465+ api::Config::Ptr config_;
466+};
467+
468+}
469+
470+#endif // SCOPE_SCOPE_H_
471
472=== modified file 'share/qtcreator/templates/wizards/ubuntu/scope/src/CMakeLists.txt'
473--- share/qtcreator/templates/wizards/ubuntu/scope/src/CMakeLists.txt 2014-07-01 10:09:49 +0000
474+++ share/qtcreator/templates/wizards/ubuntu/scope/src/CMakeLists.txt 2014-09-03 15:09:42 +0000
475@@ -1,12 +1,51 @@
476-add_library(
477- ${SCOPE_LIB_TARGET_NAME} SHARED
478- %ClickHookName:l%-preview.cpp
479- %ClickHookName:l%-query.cpp
480- %ClickHookName:l%-scope.cpp
481-)
482-target_link_libraries(${SCOPE_LIB_TARGET_NAME} ${UNITY_SCOPES_LDFLAGS})
483-set_property(TARGET ${SCOPE_LIB_TARGET_NAME} PROPERTY COMPILE_FLAGS ${UNITY_SCOPES_CFLAGS})
484-
485-install(TARGETS ${SCOPE_LIB_TARGET_NAME}
486-LIBRARY DESTINATION "${SCOPE_INSTALLDIR}"
487+
488+# Put the ini file in the build directory next to the scope
489+# .so file so test tools can find both easily.
490+configure_file(
491+ "${CMAKE_SOURCE_DIR}/data/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"
492+ "${CMAKE_CURRENT_BINARY_DIR}/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"
493+ @ONLY
494+)
495+
496+# Build an object library for the scope code
497+add_library(
498+ scope-static OBJECT
499+ api/client.cpp
500+ scope/preview.cpp
501+ scope/query.cpp
502+ scope/scope.cpp
503+)
504+
505+# Ensure we export all the symbols
506+set_target_properties(
507+ scope-static
508+ PROPERTIES
509+ LINK_FLAGS "-Wl,--export-all-symbols"
510+)
511+
512+# Build a shared library containing our scope code.
513+# This will be the actual plugin that is loaded.
514+add_library(
515+ scope SHARED
516+ $<TARGET_OBJECTS:scope-static>
517+)
518+
519+# Link against the object library and our external library dependencies
520+target_link_libraries(
521+ scope
522+ ${SCOPE_LDFLAGS}
523+ ${Boost_LIBRARIES}
524+)
525+
526+# Set the correct library output name to conform to the securiry policy
527+set_target_properties(
528+ scope
529+ PROPERTIES
530+ OUTPUT_NAME "%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%"
531+)
532+
533+# Install the scope shared library
534+install(
535+ TARGETS scope
536+ LIBRARY DESTINATION ${SCOPE_INSTALL_DIR}
537 )
538
539=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/src/api'
540=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/src/api/client.cpp'
541--- share/qtcreator/templates/wizards/ubuntu/scope/src/api/client.cpp 1970-01-01 00:00:00 +0000
542+++ share/qtcreator/templates/wizards/ubuntu/scope/src/api/client.cpp 2014-09-03 15:09:42 +0000
543@@ -0,0 +1,157 @@
544+#include <api/client.h>
545+
546+#include <core/net/error.h>
547+#include <core/net/http/client.h>
548+#include <core/net/http/content_type.h>
549+#include <core/net/http/response.h>
550+#include <json/json.h>
551+
552+namespace http = core::net::http;
553+namespace json = Json;
554+namespace net = core::net;
555+
556+using namespace api;
557+using namespace std;
558+
559+Client::Client(Config::Ptr config) :
560+ config_(config), cancelled_(false) {
561+}
562+
563+void Client::get(const net::Uri::Path &path,
564+ const net::Uri::QueryParameters &parameters, json::Value &root) {
565+ // Create a new HTTP client
566+ auto client = http::make_client();
567+
568+ // Start building the request configuration
569+ http::Request::Configuration configuration;
570+
571+ // Build the URI from its components
572+ net::Uri uri = net::make_uri(config_->apiroot, path, parameters);
573+ configuration.uri = client->uri_to_string(uri);
574+
575+ // Give out a user agent string
576+ configuration.header.add("User-Agent", config_->user_agent);
577+
578+ // Build a HTTP request object from our configuration
579+ auto request = client->head(configuration);
580+
581+ try {
582+ // Synchronously make the HTTP request
583+ // We bind the cancellable callback to #progress_report
584+ auto response = request->execute(
585+ bind(&Client::progress_report, this, placeholders::_1));
586+
587+ // Check that we got a sensible HTTP status code
588+ if (response.status != http::Status::ok) {
589+ throw domain_error(root["error"].asString());
590+ }
591+
592+ // Parse the JSON from the response
593+ json::Reader reader;
594+ reader.parse(response.body, root);
595+
596+ // Open weather map API error code can either be a string or int
597+ json::Value cod = root["cod"];
598+ if ((cod.isString() && cod.asString() != "200")
599+ || (cod.isUInt() && cod.asUInt() != 200)) {
600+ throw domain_error(root["message"].asString());
601+ }
602+ } catch (net::Error &) {
603+ }
604+}
605+
606+Client::Current Client::weather(const string& query) {
607+ json::Value root;
608+
609+ // Build a URI and get the contents.
610+ // The fist parameter forms the path part of the URI.
611+ // The second parameter forms the CGI parameters.
612+ get( { "data", "2.5", "weather" },
613+ { { "q", query }, { "units", "metric" } }, root);
614+ // e.g. http://api.openweathermap.org/data/2.5/weather?q=QUERY&units=metric
615+
616+ Current result;
617+
618+ // Read out the city we found
619+ json::Value sys = root["sys"];
620+ result.city.id = sys["id"].asUInt();
621+ result.city.name = root["name"].asString();
622+ result.city.country = sys["country"].asString();
623+
624+ // Read the weather
625+ json::Value weather = root["weather"].get(json::ArrayIndex(0),
626+ json::Value());
627+ result.weather.id = weather["id"].asUInt();
628+ result.weather.main = weather["main"].asString();
629+ result.weather.description = weather["description"].asString();
630+ result.weather.icon = "http://openweathermap.org/img/w/"
631+ + weather["icon"].asString() + ".png";
632+
633+ // Read the temps
634+ json::Value main = root["main"];
635+ result.weather.temp.cur = main["temp"].asDouble();
636+ result.weather.temp.max = main["temp_max"].asDouble();
637+ result.weather.temp.min = main["temp_min"].asDouble();
638+
639+ return result;
640+}
641+
642+Client::Forecast Client::forecast_daily(const string& query, unsigned int cnt) {
643+ json::Value root;
644+
645+ // Build a URI and get the contents
646+ // The fist parameter forms the path part of the URI.
647+ // The second parameter forms the CGI parameters.
648+ get( { "data", "2.5", "forecast", "daily" }, { { "q", query }, { "units",
649+ "metric" }, { "cnt", to_string(cnt) } }, root);
650+ // e.g. http://api.openweathermap.org/data/2.5/forecast/daily/?q=QUERY&units=metric&cnt=7
651+
652+ Forecast result;
653+
654+ // Read out the city we found
655+ json::Value city = root["city"];
656+ result.city.id = city["id"].asUInt();
657+ result.city.name = city["name"].asString();
658+ result.city.country = city["country"].asString();
659+
660+ // Iterate through the weather data
661+ json::Value list = root["list"];
662+ for (json::ArrayIndex index = 0; index < list.size(); ++index) {
663+ json::Value item = list.get(index, json::Value());
664+
665+ // Extract the first weather item
666+ json::Value weather_list = item["weather"];
667+ json::Value weather = weather_list.get(json::ArrayIndex(0),
668+ json::Value());
669+
670+ // Extract the temperature data
671+ json::Value temp = item["temp"];
672+
673+ // Add a result to the weather list
674+ result.weather.emplace_back(
675+ Weather { weather["id"].asUInt(), weather["main"].asString(),
676+ weather["description"].asString(),
677+ "http://openweathermap.org/img/w/"
678+ + weather["icon"].asString() + ".png", Temp {
679+ temp["max"].asDouble(), temp["min"].asDouble(),
680+ 0.0 } });
681+ }
682+
683+ return result;
684+}
685+
686+http::Request::Progress::Next Client::progress_report(
687+ const http::Request::Progress&) {
688+
689+ return cancelled_ ?
690+ http::Request::Progress::Next::abort_operation :
691+ http::Request::Progress::Next::continue_operation;
692+}
693+
694+void Client::cancel() {
695+ cancelled_ = true;
696+}
697+
698+Config::Ptr Client::config() {
699+ return config_;
700+}
701
702=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.cpp'
703--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.cpp 2014-07-01 11:02:41 +0000
704+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.cpp 1970-01-01 00:00:00 +0000
705@@ -1,49 +0,0 @@
706-#include"%ClickHookName:l%-preview.h"
707-
708-#include<unity/scopes/PreviewWidget.h>
709-#include<unity/scopes/ColumnLayout.h>
710-#include<unity/scopes/PreviewReply.h>
711-
712-using namespace unity::scopes;
713-
714-%ClickHookName:s%Preview::%ClickHookName:s%Preview(Result const& result, ActionMetadata const& metadata) :
715- PreviewQueryBase(result, metadata)
716-{
717-}
718-
719-%ClickHookName:s%Preview::~%ClickHookName:s%Preview()
720-{
721-}
722-
723-void %ClickHookName:s%Preview::cancelled()
724-{
725-}
726-
727-void %ClickHookName:s%Preview::run(unity::scopes::PreviewReplyProxy const& reply)
728-{
729- PreviewWidgetList widgets;
730- widgets.emplace_back(PreviewWidget(R"({"id": "header", "type": "header", "components" : { "title": "title", "subtitle": "author" } })"));
731- widgets.emplace_back(PreviewWidget(R"({"id": "img", "type": "image", "components" : { "source": "screenshot-url" } })"));
732-
733- PreviewWidget w("img2", "image");
734- w.add_attribute_value("zoomable", Variant(false));
735- w.add_attribute_mapping("source", "screenshot-url");
736- widgets.emplace_back(w);
737-
738- ColumnLayout layout1col(1);
739- layout1col.add_column({"header", "img", "img2"});
740-
741- ColumnLayout layout2col(2);
742- layout2col.add_column({"header", "img"});
743- layout2col.add_column({"img2"});
744-
745- ColumnLayout layout3col(3);
746- layout3col.add_column({"header"});
747- layout3col.add_column({"img"});
748- layout3col.add_column({"img2"});
749-
750- reply->register_layout({layout1col, layout2col, layout3col});
751- reply->push(widgets);
752- reply->push("author", Variant("Foo"));
753- reply->push("screenshot-url", Variant("/path/to/image.png"));
754-}
755
756=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.h'
757--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.h 2014-07-01 11:02:41 +0000
758+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-preview.h 1970-01-01 00:00:00 +0000
759@@ -1,18 +0,0 @@
760-#ifndef DEMOPREVIEW_H
761-#define DEMOPREVIEW_H
762-
763-#include<unity/scopes/PreviewQueryBase.h>
764-#include<unity/scopes/Result.h>
765-#include<unity/scopes/ActionMetadata.h>
766-
767-class %ClickHookName:s%Preview : public unity::scopes::PreviewQueryBase
768-{
769-public:
770- %ClickHookName:s%Preview(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata);
771- ~%ClickHookName:s%Preview();
772-
773- virtual void cancelled() override;
774- virtual void run(unity::scopes::PreviewReplyProxy const& reply) override;
775-};
776-
777-#endif
778
779=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.cpp'
780--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.cpp 2014-07-01 15:07:36 +0000
781+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.cpp 1970-01-01 00:00:00 +0000
782@@ -1,54 +0,0 @@
783-#include "%ClickHookName:l%-query.h"
784-#include <unity/scopes/Annotation.h>
785-#include <unity/scopes/CategorisedResult.h>
786-#include <unity/scopes/CategoryRenderer.h>
787-#include <unity/scopes/QueryBase.h>
788-#include <unity/scopes/SearchReply.h>
789-
790-using namespace unity::scopes;
791-
792-namespace
793-{
794-const static std::string CATEGORY_TEMPLATE =
795- R"(
796-{
797- "schema-version": 1,
798- "template": {
799- "category-layout": "grid",
800- "card-size": "medium"
801- },
802- "components": {
803- "title": "title",
804- "art" : {
805- "field": "art"
806- },
807- "subtitle": "subtitle"
808- }
809-}
810-)";
811-}
812-
813-%ClickHookName:s%Query::%ClickHookName:s%Query(CannedQuery const& query, SearchMetadata const& metadata) :
814- SearchQueryBase(query, metadata)
815-{
816-}
817-
818-%ClickHookName:s%Query::~%ClickHookName:s%Query()
819-{
820-}
821-
822-void %ClickHookName:s%Query::cancelled()
823-{
824-}
825-
826-void %ClickHookName:s%Query::run(unity::scopes::SearchReplyProxy const& reply)
827-{
828- CategoryRenderer rdr(CATEGORY_TEMPLATE);
829- auto cat = reply->register_category("cat1", "Category 1", "", rdr);
830- CategorisedResult res(cat);
831- res.set_uri("uri");
832- res.set_title("scope-A: result 1 for query \"" + query().query_string() + "\"");
833- res.set_art("http://design.ubuntu.com/wp-content/uploads/ubuntu-logo32.png");
834- res.set_dnd_uri("dnd_uri");
835- reply->push(res);
836-}
837
838=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.h'
839--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.h 2014-07-01 11:02:41 +0000
840+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-query.h 1970-01-01 00:00:00 +0000
841@@ -1,17 +0,0 @@
842-#ifndef DEMOQUERY_H
843-#define DEMOQUERY_H
844-
845-#include <unity/scopes/SearchQueryBase.h>
846-#include <unity/scopes/ReplyProxyFwd.h>
847-
848-class %ClickHookName:s%Query : public unity::scopes::SearchQueryBase
849-{
850-public:
851- %ClickHookName:s%Query(unity::scopes::CannedQuery const& query, unity::scopes::SearchMetadata const& metadata);
852- ~%ClickHookName:s%Query();
853- virtual void cancelled() override;
854-
855- virtual void run(unity::scopes::SearchReplyProxy const& reply) override;
856-};
857-
858-#endif
859
860=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.cpp'
861--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.cpp 2014-08-05 11:35:58 +0000
862+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.cpp 1970-01-01 00:00:00 +0000
863@@ -1,49 +0,0 @@
864-#include "%ClickHookName:l%-scope.h"
865-#include "%ClickHookName:l%-query.h"
866-#include "%ClickHookName:l%-preview.h"
867-#include <unity-scopes.h>
868-
869-using namespace unity::scopes;
870-
871-void %ClickHookName:s%Scope::start(std::string const&)
872-{
873-}
874-
875-void %ClickHookName:s%Scope::stop()
876-{
877-}
878-
879-SearchQueryBase::UPtr %ClickHookName:s%Scope::search(CannedQuery const &q, SearchMetadata const& metadata)
880-{
881- SearchQueryBase::UPtr query(new %ClickHookName:s%Query(q, metadata));
882- return query;
883-}
884-
885-
886-PreviewQueryBase::UPtr %ClickHookName:s%Scope::preview(Result const& result, ActionMetadata const& metadata) {
887- PreviewQueryBase::UPtr preview(new %ClickHookName:s%Preview(result, metadata));
888- return preview;
889-}
890-
891-#define EXPORT __attribute__ ((visibility ("default")))
892-
893-extern "C"
894-{
895-
896- EXPORT
897- unity::scopes::ScopeBase*
898- // cppcheck-suppress unusedFunction
899- UNITY_SCOPE_CREATE_FUNCTION()
900- {
901- return new %ClickHookName:s%Scope();
902- }
903-
904- EXPORT
905- void
906- // cppcheck-suppress unusedFunction
907- UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
908- {
909- delete scope_base;
910- }
911-
912-}
913
914=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.h'
915--- share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.h 2014-08-05 11:35:58 +0000
916+++ share/qtcreator/templates/wizards/ubuntu/scope/src/displayName-scope.h 1970-01-01 00:00:00 +0000
917@@ -1,24 +0,0 @@
918-#ifndef DEMOSCOPE_H
919-#define DEMOSCOPE_H
920-
921-#include <unity/scopes/ScopeBase.h>
922-#include <unity/scopes/QueryBase.h>
923-#include <unity/scopes/ReplyProxyFwd.h>
924-#include <unity/scopes/QueryBase.h>
925-#include <unity/scopes/PreviewQueryBase.h>
926-
927-class %ClickHookName:s%Scope : public unity::scopes::ScopeBase
928-{
929-public:
930- virtual void start(std::string const&) override;
931-
932- virtual void stop() override;
933-
934- unity::scopes::PreviewQueryBase::UPtr preview(const unity::scopes::Result& result,
935- unity::scopes::ActionMetadata const& metadata) override;
936-
937- virtual unity::scopes::SearchQueryBase::UPtr search(unity::scopes::CannedQuery const& q,
938- unity::scopes::SearchMetadata const& metadata) override;
939-};
940-
941-#endif
942
943=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/src/scope'
944=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/src/scope/preview.cpp'
945--- share/qtcreator/templates/wizards/ubuntu/scope/src/scope/preview.cpp 1970-01-01 00:00:00 +0000
946+++ share/qtcreator/templates/wizards/ubuntu/scope/src/scope/preview.cpp 2014-09-03 15:09:42 +0000
947@@ -0,0 +1,60 @@
948+#include <scope/preview.h>
949+
950+#include <unity/scopes/ColumnLayout.h>
951+#include <unity/scopes/PreviewWidget.h>
952+#include <unity/scopes/PreviewReply.h>
953+#include <unity/scopes/Result.h>
954+#include <unity/scopes/VariantBuilder.h>
955+
956+#include <iostream>
957+
958+namespace sc = unity::scopes;
959+
960+using namespace std;
961+using namespace scope;
962+
963+Preview::Preview(const sc::Result &result, const sc::ActionMetadata &metadata) :
964+ sc::PreviewQueryBase(result, metadata) {
965+}
966+
967+void Preview::cancelled() {
968+}
969+
970+void Preview::run(sc::PreviewReplyProxy const& reply) {
971+ // Support three different column layouts
972+ sc::ColumnLayout layout1col(1), layout2col(2), layout3col(3);
973+
974+ // Single column layout
975+ layout1col.add_column( { "image", "header", "summary" });
976+
977+ // Two column layout
978+ layout2col.add_column( { "image" });
979+ layout2col.add_column( { "header", "summary" });
980+
981+ // Three cokumn layout
982+ layout3col.add_column( { "image" });
983+ layout3col.add_column( { "header", "summary" });
984+ layout3col.add_column( { });
985+
986+ // Register the layouts we just created
987+ reply->register_layout( { layout1col, layout2col, layout3col });
988+
989+ // Define the header section
990+ sc::PreviewWidget header("header", "header");
991+ // It has title and a subtitle properties
992+ header.add_attribute_mapping("title", "title");
993+ header.add_attribute_mapping("subtitle", "subtitle");
994+
995+ // Define the image section
996+ sc::PreviewWidget image("image", "image");
997+ // It has a single source property, mapped to the result's art property
998+ image.add_attribute_mapping("source", "art");
999+
1000+ // Define the summary section
1001+ sc::PreviewWidget description("summary", "text");
1002+ // It has a text property, mapped to the result's description property
1003+ description.add_attribute_mapping("text", "description");
1004+
1005+ // Push each of the sections
1006+ reply->push( { image, header, description });
1007+}
1008
1009=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/src/scope/query.cpp'
1010--- share/qtcreator/templates/wizards/ubuntu/scope/src/scope/query.cpp 1970-01-01 00:00:00 +0000
1011+++ share/qtcreator/templates/wizards/ubuntu/scope/src/scope/query.cpp 2014-09-03 15:09:42 +0000
1012@@ -0,0 +1,177 @@
1013+#include <boost/algorithm/string/trim.hpp>
1014+
1015+#include <scope/query.h>
1016+
1017+#include <unity/scopes/Annotation.h>
1018+#include <unity/scopes/CategorisedResult.h>
1019+#include <unity/scopes/CategoryRenderer.h>
1020+#include <unity/scopes/QueryBase.h>
1021+#include <unity/scopes/SearchReply.h>
1022+
1023+#include <iomanip>
1024+#include <sstream>
1025+
1026+namespace sc = unity::scopes;
1027+namespace alg = boost::algorithm;
1028+
1029+using namespace std;
1030+using namespace api;
1031+using namespace scope;
1032+
1033+/**
1034+ * Define the layout for the forecast results
1035+ *
1036+ * The icon size is small, and ask for the card layout
1037+ * itself to be horizontal. I.e. the text will be placed
1038+ * next to the image.
1039+ */
1040+const static string WEATHER_TEMPLATE =
1041+ R"(
1042+{
1043+ "schema-version": 1,
1044+ "template": {
1045+ "category-layout": "grid",
1046+ "card-layout": "horizontal",
1047+ "card-size": "small"
1048+ },
1049+ "components": {
1050+ "title": "title",
1051+ "art" : {
1052+ "field": "art"
1053+ },
1054+ "subtitle": "subtitle"
1055+ }
1056+}
1057+)";
1058+
1059+/**
1060+ * Define the larger "current weather" layout.
1061+ *
1062+ * The icons are larger.
1063+ */
1064+const static string CITY_TEMPLATE =
1065+ R"(
1066+{
1067+ "schema-version": 1,
1068+ "template": {
1069+ "category-layout": "grid",
1070+ "card-size": "medium"
1071+ },
1072+ "components": {
1073+ "title": "title",
1074+ "art" : {
1075+ "field": "art"
1076+ },
1077+ "subtitle": "subtitle"
1078+ }
1079+}
1080+)";
1081+
1082+Query::Query(const sc::CannedQuery &query, const sc::SearchMetadata &metadata,
1083+ Config::Ptr config) :
1084+ sc::SearchQueryBase(query, metadata), client_(config) {
1085+}
1086+
1087+void Query::cancelled() {
1088+ client_.cancel();
1089+}
1090+
1091+void Query::run(sc::SearchReplyProxy const& reply) {
1092+ try {
1093+ // Start by getting information about the query
1094+ const sc::CannedQuery &query(sc::SearchQueryBase::query());
1095+
1096+ // Trim the query string of whitespace
1097+ string query_string = alg::trim_copy(query.query_string());
1098+
1099+ Client::Current current;
1100+ if (query_string.empty()) {
1101+ // If the string is empty, get the current weather for London
1102+ current = client_.weather("London,uk");
1103+ } else {
1104+ // otherwise, get the current weather for the search string
1105+ current = client_.weather(query_string);
1106+ }
1107+
1108+ // Build up the description for the city
1109+ stringstream ss(stringstream::in | stringstream::out);
1110+ ss << current.city.name << ", " << current.city.country;
1111+
1112+ // Register a category for the current weather, with the title we just built
1113+ auto location_cat = reply->register_category("current", ss.str(), "",
1114+ sc::CategoryRenderer(CITY_TEMPLATE));
1115+
1116+ {
1117+ // Create a single result for the current weather category
1118+ sc::CategorisedResult res(location_cat);
1119+
1120+ // We must have a URI
1121+ res.set_uri(to_string(current.city.id));
1122+
1123+ // Build up the description for the current weather
1124+ stringstream ss(stringstream::in | stringstream::out);
1125+ ss << setprecision(3) << current.weather.temp.cur;
1126+ ss << "°C";
1127+ res.set_title(ss.str());
1128+
1129+ // Set the rest of the attributes, art, description, etc
1130+ res.set_art(current.weather.icon);
1131+ res["subtitle"] = current.weather.description;
1132+ res["description"] = "A description of the result";
1133+
1134+ // Push the result
1135+ if (!reply->push(res)) {
1136+ // If we fail to push, it means the query has been cancelled.
1137+ // So don't continue;
1138+ return;
1139+ }
1140+ }
1141+
1142+ Client::Forecast forecast;
1143+ if (query_string.empty()) {
1144+ // If there is no search string, get the forecast for London
1145+ forecast = client_.forecast_daily("London,uk");
1146+ } else {
1147+ // otherwise, get the forecast for the search string
1148+ forecast = client_.forecast_daily(query_string);
1149+ }
1150+
1151+ // Register a category for the forecast
1152+ auto forecast_cat = reply->register_category("forecast",
1153+ "7 day forecast", "", sc::CategoryRenderer(WEATHER_TEMPLATE));
1154+
1155+ // For each of the forecast days
1156+ for (const auto &weather : forecast.weather) {
1157+ // Create a result
1158+ sc::CategorisedResult res(forecast_cat);
1159+
1160+ // We must have a URI
1161+ res.set_uri(to_string(weather.id));
1162+
1163+ // Build the description for the result
1164+ stringstream ss(stringstream::in | stringstream::out);
1165+ ss << setprecision(3) << weather.temp.max;
1166+ ss << "°C to ";
1167+ ss << setprecision(3) << weather.temp.min;
1168+ ss << "°C";
1169+ res.set_title(ss.str());
1170+
1171+ // Set the rest of the attributes
1172+ res.set_art(weather.icon);
1173+ res["subtitle"] = weather.description;
1174+ res["description"] = "A description of the result";
1175+
1176+ // Push the result
1177+ if (!reply->push(res)) {
1178+ // If we fail to push, it means the query has been cancelled.
1179+ // So don't continue;
1180+ return;
1181+ }
1182+ }
1183+
1184+ } catch (domain_error &e) {
1185+ // Handle exceptions being thrown by the client API
1186+ cerr << e.what() << endl;
1187+ reply->error(current_exception());
1188+ }
1189+}
1190
1191=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/src/scope/scope.cpp'
1192--- share/qtcreator/templates/wizards/ubuntu/scope/src/scope/scope.cpp 1970-01-01 00:00:00 +0000
1193+++ share/qtcreator/templates/wizards/ubuntu/scope/src/scope/scope.cpp 2014-09-03 15:09:42 +0000
1194@@ -0,0 +1,58 @@
1195+#include <scope/scope.h>
1196+#include <scope/query.h>
1197+#include <scope/preview.h>
1198+
1199+#include <iostream>
1200+#include <sstream>
1201+#include <fstream>
1202+
1203+namespace sc = unity::scopes;
1204+using namespace std;
1205+using namespace api;
1206+using namespace scope;
1207+
1208+void Scope::start(string const&) {
1209+ config_ = make_shared<Config>();
1210+
1211+ // Under test we set a different API root
1212+ char *apiroot = getenv("NETWORK_SCOPE_APIROOT");
1213+ if (apiroot) {
1214+ config_->apiroot = apiroot;
1215+ }
1216+}
1217+
1218+void Scope::stop() {
1219+}
1220+
1221+sc::SearchQueryBase::UPtr Scope::search(const sc::CannedQuery &query,
1222+ const sc::SearchMetadata &metadata) {
1223+ // Boilerplate construction of Query
1224+ return sc::SearchQueryBase::UPtr(new Query(query, metadata, config_));
1225+}
1226+
1227+sc::PreviewQueryBase::UPtr Scope::preview(sc::Result const& result,
1228+ sc::ActionMetadata const& metadata) {
1229+ // Boilerplate construction of Preview
1230+ return sc::PreviewQueryBase::UPtr(new Preview(result, metadata));
1231+}
1232+
1233+#define EXPORT __attribute__ ((visibility ("default")))
1234+
1235+// These functions define the entry points for the scope plugin
1236+extern "C" {
1237+
1238+EXPORT
1239+unity::scopes::ScopeBase*
1240+// cppcheck-suppress unusedFunction
1241+UNITY_SCOPE_CREATE_FUNCTION() {
1242+ return new Scope();
1243+}
1244+
1245+EXPORT
1246+void
1247+// cppcheck-suppress unusedFunction
1248+UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) {
1249+ delete scope_base;
1250+}
1251+
1252+}
1253
1254=== removed directory 'share/qtcreator/templates/wizards/ubuntu/scope/test'
1255=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/test/CMakeLists.txt'
1256--- share/qtcreator/templates/wizards/ubuntu/scope/test/CMakeLists.txt 2014-05-15 10:14:07 +0000
1257+++ share/qtcreator/templates/wizards/ubuntu/scope/test/CMakeLists.txt 1970-01-01 00:00:00 +0000
1258@@ -1,2 +0,0 @@
1259-add_executable(simpletest simpletest.cpp)
1260-add_test(simpletest simpletest)
1261
1262=== removed file 'share/qtcreator/templates/wizards/ubuntu/scope/test/simpletest.cpp'
1263--- share/qtcreator/templates/wizards/ubuntu/scope/test/simpletest.cpp 2014-05-15 10:14:07 +0000
1264+++ share/qtcreator/templates/wizards/ubuntu/scope/test/simpletest.cpp 1970-01-01 00:00:00 +0000
1265@@ -1,3 +0,0 @@
1266-int main(int, char**) {
1267- return 0;
1268-}
1269
1270=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests'
1271=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/CMakeLists.txt'
1272--- share/qtcreator/templates/wizards/ubuntu/scope/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
1273+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/CMakeLists.txt 2014-09-03 15:09:42 +0000
1274@@ -0,0 +1,25 @@
1275+
1276+# Google Mock unfortunately has to be compiled from source
1277+include(FindGMock)
1278+
1279+# We need process-cpp to launch the python test server
1280+pkg_check_modules(
1281+ TEST
1282+ process-cpp
1283+ REQUIRED
1284+)
1285+
1286+# Include our test library headers
1287+include_directories(
1288+ ${GTEST_INCLUDE_DIRS}
1289+ ${GMOCK_INCLUDE_DIRS}
1290+ ${TEST_INCLUDE_DIRS}
1291+)
1292+
1293+# Where to find the test server binary
1294+add_definitions(
1295+ -DFAKE_SERVER="${CMAKE_CURRENT_SOURCE_DIR}/server/server.py"
1296+)
1297+
1298+# Add the unit tests
1299+add_subdirectory(unit)
1300
1301=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server'
1302=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast'
1303=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily'
1304=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/London,uk.json'
1305--- share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/London,uk.json 1970-01-01 00:00:00 +0000
1306+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/London,uk.json 2014-09-03 15:09:42 +0000
1307@@ -0,0 +1,1 @@
1308+{"cod":"200","message":0.1117,"city":{"id":2643743,"name":"London","coord":{"lon":-0.12574,"lat":51.50853},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":23.33,"min":18.84,"max":25.09,"night":18.84,"eve":24.17,"morn":20.76},"pressure":1018.23,"humidity":83,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.87,"deg":295,"clouds":12,"rain":0.5},{"dt":1407499200,"temp":{"day":19.44,"min":15.54,"max":20.86,"night":15.54,"eve":18.28,"morn":16.21},"pressure":1010.57,"humidity":97,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":4.01,"deg":143,"clouds":92,"rain":8},{"dt":1407585600,"temp":{"day":18.78,"min":13.19,"max":19.58,"night":13.19,"eve":18.95,"morn":14.03},"pressure":1010.78,"humidity":83,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":5.46,"deg":281,"clouds":32},{"dt":1407672000,"temp":{"day":18.09,"min":13.46,"max":18.09,"night":13.46,"eve":16.58,"morn":13.91},"pressure":1002.33,"humidity":67,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.68,"deg":162,"clouds":92,"rain":4},{"dt":1407758400,"temp":{"day":17.38,"min":15.39,"max":17.38,"night":16.3,"eve":17.03,"morn":15.39},"pressure":1008.2,"humidity":0,"weather":[{"id":502,"main":"Rain","description":"heavy intensity rain","icon":"10d"}],"speed":13.52,"deg":245,"clouds":74,"rain":12.63},{"dt":1407844800,"temp":{"day":18.44,"min":16.17,"max":18.44,"night":16.83,"eve":18.02,"morn":16.17},"pressure":1009.94,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.93,"deg":251,"clouds":60,"rain":5.25},{"dt":1407931200,"temp":{"day":18.8,"min":16.67,"max":19.02,"night":16.77,"eve":19.02,"morn":16.67},"pressure":1013.17,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.88,"deg":275,"clouds":85,"rain":1.35}]}
1309
1310=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/Manchester,uk.json'
1311--- share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/Manchester,uk.json 1970-01-01 00:00:00 +0000
1312+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/server/forecast/daily/Manchester,uk.json 2014-09-03 15:09:42 +0000
1313@@ -0,0 +1,1 @@
1314+{"cod":"200","message":0.0056,"city":{"id":2643123,"name":"Manchester","coord":{"lon":-2.23743,"lat":53.480949},"country":"GB","population":0,"sys":{"population":0}},"cnt":7,"list":[{"dt":1407412800,"temp":{"day":17.91,"min":11.96,"max":18.83,"night":11.96,"eve":18.01,"morn":16.13},"pressure":1014.82,"humidity":77,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":3.32,"deg":303,"clouds":0},{"dt":1407499200,"temp":{"day":18.59,"min":12.33,"max":18.59,"night":12.33,"eve":14.55,"morn":14.69},"pressure":1008.22,"humidity":79,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":3.46,"deg":163,"clouds":92,"rain":11},{"dt":1407585600,"temp":{"day":15.8,"min":10.79,"max":17.11,"night":10.79,"eve":16.89,"morn":13.19},"pressure":1005.76,"humidity":92,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"speed":5.61,"deg":279,"clouds":56},{"dt":1407672000,"temp":{"day":15.29,"min":12.21,"max":15.98,"night":12.41,"eve":15.98,"morn":12.21},"pressure":995.36,"humidity":95,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.75,"deg":137,"clouds":92,"rain":5},{"dt":1407758400,"temp":{"day":15.73,"min":12.99,"max":15.73,"night":13.8,"eve":15.23,"morn":12.99},"pressure":990.16,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":9.02,"deg":239,"clouds":99,"rain":1.15},{"dt":1407844800,"temp":{"day":17.64,"min":14.08,"max":17.64,"night":14.08,"eve":14.92,"morn":14.12},"pressure":992.52,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":10.36,"deg":240,"clouds":45,"rain":4.84},{"dt":1407931200,"temp":{"day":14.35,"min":13.37,"max":15.52,"night":13.77,"eve":15.52,"morn":13.37},"pressure":999.39,"humidity":0,"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"speed":7.79,"deg":285,"clouds":92,"rain":5.98}]}
1315
1316=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/server.py'
1317--- share/qtcreator/templates/wizards/ubuntu/scope/tests/server/server.py 1970-01-01 00:00:00 +0000
1318+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/server/server.py 2014-09-03 15:09:42 +0000
1319@@ -0,0 +1,51 @@
1320+#!/usr/bin/env python3
1321+
1322+import http.server
1323+import os
1324+import socketserver
1325+import sys
1326+from urllib.parse import urlparse,parse_qs
1327+
1328+def read_file(path):
1329+ file = os.path.join(os.path.dirname(__file__), path)
1330+ if os.path.isfile(file):
1331+ with open(file, 'r') as fp:
1332+ content = fp.read()
1333+ else:
1334+ content = ''
1335+
1336+ return content
1337+
1338+class MyRequestHandler(http.server.BaseHTTPRequestHandler):
1339+ def do_GET(self):
1340+ sys.stderr.write("GET: %s\n" % self.path)
1341+ sys.stderr.flush()
1342+
1343+ parse = urlparse(self.path)
1344+ path = parse.path
1345+ query = parse_qs(parse.query)
1346+
1347+ if path == '/data/2.5/weather':
1348+ self.send_response(200)
1349+ self.send_header("Content-type", "text/html")
1350+ self.end_headers()
1351+ self.wfile.write(bytes(read_file('weather/%s.json' % query['q'][0]), 'UTF-8'))
1352+ elif path == '/data/2.5/forecast/daily':
1353+ self.send_response(200)
1354+ self.send_header("Content-type", "text/html")
1355+ self.end_headers()
1356+ self.wfile.write(bytes(read_file('forecast/daily/%s.json' % query['q'][0]), 'UTF-8'))
1357+ else:
1358+ self.send_response(404)
1359+ self.send_header("Content-type", "text/html")
1360+ self.end_headers()
1361+ self.wfile.write(bytes('ERROR', 'UTF-8'))
1362+
1363+if __name__ == "__main__":
1364+ Handler = MyRequestHandler
1365+ httpd = socketserver.TCPServer(("127.0.0.1", 0), Handler)
1366+
1367+ sys.stdout.write('%d\n' % httpd.server_address[1])
1368+ sys.stdout.flush()
1369+
1370+ httpd.serve_forever()
1371
1372=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather'
1373=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.json'
1374--- share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.json 1970-01-01 00:00:00 +0000
1375+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/London,uk.json 2014-09-03 15:09:42 +0000
1376@@ -0,0 +1,1 @@
1377+{"coord":{"lon":-0.13,"lat":51.51},"sys":{"type":1,"id":5091,"message":0.29,"country":"GB","sunrise":1407386057,"sunset":1407440289},"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"base":"cmc stations","main":{"temp":21.83,"pressure":1014,"humidity":53,"temp_min":20,"temp_max":24},"wind":{"speed":1.5,"deg":0},"clouds":{"all":20},"dt":1407408276,"id":2643743,"name":"London","cod":200}
1378\ No newline at end of file
1379
1380=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.json'
1381--- share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.json 1970-01-01 00:00:00 +0000
1382+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/server/weather/Manchester,uk.json 2014-09-03 15:09:42 +0000
1383@@ -0,0 +1,1 @@
1384+{"coord":{"lon":-2.24,"lat":53.48},"sys":{"type":1,"id":5060,"message":0.2423,"country":"GB","sunrise":1407386141,"sunset":1407441219},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"base":"cmc stations","main":{"temp":17.35,"pressure":1016,"humidity":77,"temp_min":17,"temp_max":18},"wind":{"speed":2.6,"deg":20,"var_beg":330,"var_end":80},"clouds":{"all":40},"dt":1407408600,"id":2643123,"name":"Manchester","cod":200}
1385
1386=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/unit'
1387=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/CMakeLists.txt'
1388--- share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/CMakeLists.txt 1970-01-01 00:00:00 +0000
1389+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/CMakeLists.txt 2014-09-03 15:09:42 +0000
1390@@ -0,0 +1,24 @@
1391+
1392+# Our test executable.
1393+# It includes the object code from the scope
1394+add_executable(
1395+ scope-unit-tests
1396+ scope/test-scope.cpp
1397+ $<TARGET_OBJECTS:scope-static>
1398+)
1399+
1400+# Link against the scope, and all of our test lib dependencies
1401+target_link_libraries(
1402+ scope-unit-tests
1403+ ${GTEST_BOTH_LIBRARIES}
1404+ ${GMOCK_LIBRARIES}
1405+ ${SCOPE_LDFLAGS}
1406+ ${TEST_LDFLAGS}
1407+ ${Boost_LIBRARIES}
1408+)
1409+
1410+# Register the test with CTest
1411+add_test(
1412+ scope-unit-tests
1413+ scope-unit-tests
1414+)
1415
1416=== added directory 'share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/scope'
1417=== added file 'share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/scope/test-scope.cpp'
1418--- share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/scope/test-scope.cpp 1970-01-01 00:00:00 +0000
1419+++ share/qtcreator/templates/wizards/ubuntu/scope/tests/unit/scope/test-scope.cpp 2014-09-03 15:09:42 +0000
1420@@ -0,0 +1,225 @@
1421+#include <scope/scope.h>
1422+
1423+#include <core/posix/exec.h>
1424+#include <gtest/gtest.h>
1425+#include <gmock/gmock.h>
1426+#include <string>
1427+#include <unity/scopes/SearchReply.h>
1428+#include <unity/scopes/SearchReplyProxyFwd.h>
1429+#include <unity/scopes/Variant.h>
1430+#include <unity/scopes/testing/Category.h>
1431+#include <unity/scopes/testing/MockSearchReply.h>
1432+#include <unity/scopes/testing/TypedScopeFixture.h>
1433+
1434+using namespace std;
1435+using namespace testing;
1436+using namespace scope;
1437+
1438+namespace posix = core::posix;
1439+namespace sc = unity::scopes;
1440+namespace sct = unity::scopes::testing;
1441+
1442+/**
1443+ * Keep the tests in an anonymous namespace
1444+ */
1445+namespace {
1446+
1447+/**
1448+ * Custom matcher to check the properties of search results
1449+ */
1450+MATCHER_P2(ResultProp, prop, value, "") {
1451+ if (arg.contains(prop)) {
1452+ *result_listener << "result[" << prop << "] is " << arg[prop].serialize_json();
1453+ } else {
1454+ *result_listener << "result[" << prop << "] is not set";
1455+ }
1456+ return arg.contains(prop) && arg[prop] == sc::Variant(value);
1457+}
1458+
1459+/**
1460+ * Custom matcher to check the presence of departments
1461+ */
1462+MATCHER_P(IsDepartment, department, "") {
1463+ return arg->serialize() == department->serialize();
1464+}
1465+
1466+typedef sct::TypedScopeFixture<Scope> TypedScopeFixtureScope;
1467+
1468+class TestScope: public TypedScopeFixtureScope {
1469+protected:
1470+ void SetUp() override
1471+ {
1472+ // Start up Python-based fake OpenWeatherMap server
1473+ fake_server_ = posix::exec("/usr/bin/python3", { FAKE_SERVER }, { },
1474+ posix::StandardStream::stdout);
1475+
1476+ // Check it's running
1477+ ASSERT_GT(fake_server_.pid(), 0);
1478+ string port;
1479+ // The server will print out the random port it is using
1480+ fake_server_.cout() >> port;
1481+ // Check we have a port
1482+ ASSERT_FALSE(port.empty());
1483+
1484+ // Build up the API root
1485+ string apiroot = "http://127.0.0.1:" + port;
1486+ // Override the API root that the scope will use
1487+ setenv("NETWORK_SCOPE_APIROOT", apiroot.c_str(), true);
1488+
1489+ // Do the parent SetUp
1490+ TypedScopeFixtureScope::SetUp();
1491+ }
1492+
1493+ /**
1494+ * Start by assuming the server is invalid
1495+ */
1496+ posix::ChildProcess fake_server_ = posix::ChildProcess::invalid();
1497+};
1498+
1499+TEST_F(TestScope, empty_search_string) {
1500+ const sc::CategoryRenderer renderer;
1501+ NiceMock<sct::MockSearchReply> reply;
1502+
1503+ // Build a query with an empty search string
1504+ sc::CannedQuery query(SCOPE_NAME, "", "");
1505+
1506+ // Expect the current weather category
1507+ EXPECT_CALL(reply, register_category("current", "London, GB", "", _)).Times(1)
1508+ .WillOnce(Return(make_shared<sct::Category>("current", "London, GB", "", renderer)));
1509+
1510+ // With one result
1511+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1512+ ResultProp("title", "21.8°C"),
1513+ ResultProp("art", "http://openweathermap.org/img/w/02d.png"),
1514+ ResultProp("subtitle", "few clouds")
1515+ )))).WillOnce(
1516+ Return(true));
1517+
1518+ // Expect the forecast category
1519+ EXPECT_CALL(reply, register_category("forecast", "7 day forecast", "", _)).Times(1)
1520+ .WillOnce(Return(make_shared<sct::Category>("forecast", "7 day forecast", "", renderer)));
1521+
1522+ // With seven results
1523+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1524+ ResultProp("title", "25.1°C to 18.8°C"),
1525+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1526+ ResultProp("subtitle", "light rain")
1527+ )))).WillOnce(Return(true));
1528+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1529+ ResultProp("title", "20.9°C to 15.5°C"),
1530+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1531+ ResultProp("subtitle", "moderate rain")
1532+ )))).WillOnce(Return(true));
1533+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1534+ ResultProp("title", "19.6°C to 13.2°C"),
1535+ ResultProp("art", "http://openweathermap.org/img/w/03d.png"),
1536+ ResultProp("subtitle", "scattered clouds")
1537+ )))).WillOnce(Return(true));
1538+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1539+ ResultProp("title", "18.1°C to 13.5°C"),
1540+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1541+ ResultProp("subtitle", "moderate rain")
1542+ )))).WillOnce(Return(true));
1543+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1544+ ResultProp("title", "17.4°C to 15.4°C"),
1545+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1546+ ResultProp("subtitle", "heavy intensity rain")
1547+ )))).WillOnce(Return(true));
1548+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1549+ ResultProp("title", "18.4°C to 16.2°C"),
1550+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1551+ ResultProp("subtitle", "moderate rain")
1552+ )))).WillOnce(Return(true));
1553+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1554+ ResultProp("title", "19°C to 16.7°C"),
1555+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1556+ ResultProp("subtitle", "light rain")
1557+ )))).WillOnce(Return(true));
1558+
1559+ sc::SearchReplyProxy reply_proxy(&reply, [](sc::SearchReply*) {}); // note: this is a std::shared_ptr with empty deleter
1560+ sc::SearchMetadata meta_data("en_EN", "phone");
1561+
1562+ // Create a query object
1563+ auto search_query = scope->search(query, meta_data);
1564+ ASSERT_NE(nullptr, search_query);
1565+
1566+ // Run the search
1567+ search_query->run(reply_proxy);
1568+
1569+ // Google Mock will make assertions when the mocks are destructed.
1570+}
1571+
1572+TEST_F(TestScope, search) {
1573+ const sc::CategoryRenderer renderer;
1574+ NiceMock<sct::MockSearchReply> reply;
1575+
1576+ // Build a query with a non-empty search string
1577+ sc::CannedQuery query(SCOPE_NAME, "Manchester,uk", "");
1578+
1579+ // Expect the current weather category
1580+ EXPECT_CALL(reply, register_category("current", "Manchester, GB", "", _)).Times(1)
1581+ .WillOnce(Return(make_shared<sct::Category>("current", "Manchester, GB", "", renderer)));
1582+
1583+ // With one result
1584+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1585+ ResultProp("title", "17.4°C"),
1586+ ResultProp("art", "http://openweathermap.org/img/w/03d.png"),
1587+ ResultProp("subtitle", "scattered clouds")
1588+ )))).WillOnce(
1589+ Return(true));
1590+
1591+ // Expect the forecast category
1592+ EXPECT_CALL(reply, register_category("forecast", "7 day forecast", "", _)).Times(1)
1593+ .WillOnce(Return(make_shared<sct::Category>("forecast", "7 day forecast", "", renderer)));
1594+
1595+ // With seven results
1596+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1597+ ResultProp("title", "18.8°C to 12°C"),
1598+ ResultProp("art", "http://openweathermap.org/img/w/01d.png"),
1599+ ResultProp("subtitle", "sky is clear")
1600+ )))).WillOnce(Return(true));
1601+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1602+ ResultProp("title", "18.6°C to 12.3°C"),
1603+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1604+ ResultProp("subtitle", "moderate rain")
1605+ )))).WillOnce(Return(true));
1606+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1607+ ResultProp("title", "17.1°C to 10.8°C"),
1608+ ResultProp("art", "http://openweathermap.org/img/w/04d.png"),
1609+ ResultProp("subtitle", "broken clouds")
1610+ )))).WillOnce(Return(true));
1611+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1612+ ResultProp("title", "16°C to 12.2°C"),
1613+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1614+ ResultProp("subtitle", "moderate rain")
1615+ )))).WillOnce(Return(true));
1616+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1617+ ResultProp("title", "15.7°C to 13°C"),
1618+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1619+ ResultProp("subtitle", "light rain")
1620+ )))).WillOnce(Return(true));
1621+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1622+ ResultProp("title", "17.6°C to 14.1°C"),
1623+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1624+ ResultProp("subtitle", "moderate rain")
1625+ )))).WillOnce(Return(true));
1626+ EXPECT_CALL(reply, push(Matcher<sc::CategorisedResult const&>(AllOf(
1627+ ResultProp("title", "15.5°C to 13.4°C"),
1628+ ResultProp("art", "http://openweathermap.org/img/w/10d.png"),
1629+ ResultProp("subtitle", "moderate rain")
1630+ )))).WillOnce(Return(true));
1631+
1632+ sc::SearchReplyProxy reply_proxy(&reply, [](sc::SearchReply*) {}); // note: this is a std::shared_ptr with empty deleter
1633+ sc::SearchMetadata meta_data("en_EN", "phone");
1634+
1635+ // Create a query object
1636+ auto search_query = scope->search(query, meta_data);
1637+ ASSERT_NE(nullptr, search_query);
1638+
1639+ // Run the search
1640+ search_query->run(reply_proxy);
1641+
1642+ // Google Mock will make assertions when the mocks are destructed.
1643+}
1644+
1645+} // namespace
1646
1647=== modified file 'share/qtcreator/templates/wizards/ubuntu/scope/wizard.xml'
1648--- share/qtcreator/templates/wizards/ubuntu/scope/wizard.xml 2014-08-18 12:29:43 +0000
1649+++ share/qtcreator/templates/wizards/ubuntu/scope/wizard.xml 2014-09-03 15:09:42 +0000
1650@@ -2,28 +2,46 @@
1651 <wizard version="1" kind="project" firstpage="10" id="UbuntuUnityScope" category="A.UbuntuProject"
1652 platformIndependent="true"
1653 class="ubuntu-project-cmake"
1654- featuresRequired=""
1655+ featuresRequired=""
1656 >
1657 <icon>../share/ubuntu.png</icon>
1658 <description>A simple C++ based Unity Scope</description>
1659 <displayname>Unity Scope</displayname>
1660 <displaycategory>Ubuntu</displaycategory>
1661 <files>
1662- <file source="data/displayName.ini.in" target="data/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini.in" openeditor="false"/>
1663- <file source="data/CMakeLists.txt" target="data/CMakeLists.txt" openeditor="false"/>
1664- <file source="displayName.apparmor" target="%ClickHookName:l%.apparmor" openeditor="false"/>
1665- <file source="test/simpletest.cpp" target="test/simpletest.cpp" openeditor="false"/>
1666- <file source="test/CMakeLists.txt" target="test/CMakeLists.txt" openeditor="false"/>
1667- <file source="manifest.json" target="manifest.json" openeditor="true"/>
1668- <file source="src/displayName-preview.h" target="src/%ClickHookName:l%-preview.h" openeditor="false"/>
1669- <file source="src/displayName-query.h" target="src/%ClickHookName:l%-query.h" openeditor="false"/>
1670- <file source="src/displayName-preview.cpp" target="src/%ClickHookName:l%-preview.cpp" openeditor="false"/>
1671- <file source="src/displayName-query.cpp" target="src/%ClickHookName:l%-query.cpp" openeditor="false"/>
1672- <file source="src/CMakeLists.txt" target="src/CMakeLists.txt" openeditor="false"/>
1673- <file source="src/displayName-scope.h" target="src/%ClickHookName:l%-scope.h" openeditor="false"/>
1674- <file source="src/displayName-scope.cpp" target="src/%ClickHookName:l%-scope.cpp" openeditor="false"/>
1675- <file source="readme.txt" target="readme.txt" openeditor="true"/>
1676- <file source="CMakeLists.txt" target="CMakeLists.txt" openeditor="true" openproject="true"/>
1677+ <file source="cmake/FindGMock.cmake"/>
1678+
1679+ <file source="data/CMakeLists.txt"/>
1680+ <file source="data/displayName.ini" target="data/%ClickDomain:l%.%ProjectName:l%_%ClickHookName:l%.ini"/>
1681+ <file source="data/icon.png" binary="true"/>
1682+ <file source="data/logo.png" binary="true"/>
1683+ <file source="data/screenshot.png" binary="true"/>
1684+
1685+ <file source="include/api/config.h"/>
1686+ <file source="include/api/client.h"/>
1687+ <file source="include/scope/preview.h"/>
1688+ <file source="include/scope/query.h"/>
1689+ <file source="include/scope/scope.h"/>
1690+
1691+ <file source="src/CMakeLists.txt"/>
1692+ <file source="src/api/client.cpp"/>
1693+ <file source="src/scope/query.cpp"/>
1694+ <file source="src/scope/scope.cpp" openeditor="true"/>
1695+ <file source="src/scope/preview.cpp"/>
1696+
1697+ <file source="tests/server/forecast/daily/Manchester,uk.json"/>
1698+ <file source="tests/server/forecast/daily/London,uk.json"/>
1699+ <file source="tests/server/weather/Manchester,uk.json"/>
1700+ <file source="tests/server/weather/London,uk.json"/>
1701+ <file source="tests/server/server.py"/>
1702+ <file source="tests/CMakeLists.txt"/>
1703+ <file source="tests/unit/CMakeLists.txt"/>
1704+ <file source="tests/unit/scope/test-scope.cpp"/>
1705+
1706+ <file source="CMakeLists.txt" openproject="true"/>
1707+ <file source="displayName.apparmor" target="%ClickHookName:l%.apparmor"/>
1708+ <file source="manifest.json" openeditor="true"/>
1709+ <file source="readme.txt"/>
1710 </files>
1711
1712 <!-- Create a 2nd wizard page with click package parameters -->

Subscribers

People subscribed via source and target branches

to all changes: