Merge lp:unity-scopes-api/staging into lp:unity-scopes-api

Proposed by Paweł Stołowski
Status: Merged
Approved by: Pete Woods
Approved revision: 284
Merged at revision: 326
Proposed branch: lp:unity-scopes-api/staging
Merge into: lp:unity-scopes-api
Diff against target: 1359 lines (+414/-183)
24 files modified
CMakeLists.txt (+1/-1)
RELEASE_NOTES.md (+4/-0)
debian/changelog (+10/-0)
debian/libunity-scopes3.symbols (+2/-0)
doc/tutorial.dox (+113/-9)
include/unity/scopes/Result.h (+6/-0)
include/unity/scopes/internal/RegistryObject.h (+7/-0)
include/unity/scopes/internal/Utils.h (+2/-0)
scoperegistry/scoperegistry.cpp (+8/-19)
src/scopes/Result.cpp (+5/-0)
src/scopes/internal/RegistryObject.cpp (+38/-5)
src/scopes/internal/RuntimeConfig.cpp (+2/-8)
src/scopes/internal/RuntimeImpl.cpp (+23/-30)
src/scopes/internal/SearchQueryBaseImpl.cpp (+12/-7)
src/scopes/internal/Utils.cpp (+63/-24)
src/scopes/internal/smartscopes/SmartScopesClient.cpp (+2/-2)
test/gtest/scopes/Aggregation/CMakeLists.txt (+0/-39)
test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp (+2/-0)
test/gtest/scopes/Registry/Registry_test.cpp (+1/-1)
test/gtest/scopes/internal/RuntimeConfig/CMakeLists.txt (+8/-1)
test/gtest/scopes/internal/RuntimeConfig/RuntimeConfig_test.cpp (+35/-35)
test/gtest/scopes/internal/Utils/CMakeLists.txt (+1/-0)
test/gtest/scopes/internal/Utils/Utils_test.cpp (+68/-2)
unity-scopes.map (+1/-0)
To merge this branch: bzr merge lp:unity-scopes-api/staging
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Pete Woods (community) Approve
Review via email: mp+257273@code.launchpad.net

Commit message

Fix LP: 1446499.
Added is_account_login() method to Result.
Documentation updates.

Description of the change

Merge devel.

To post a comment you must log in.
lp:unity-scopes-api/staging updated
284. By Paweł Stołowski

Updated changelog.

Revision history for this message
Pete Woods (pete-woods) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
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 2015-04-10 07:21:29 +0000
3+++ CMakeLists.txt 2015-04-23 15:43:14 +0000
4@@ -227,7 +227,7 @@
5 # API version
6 set(UNITY_SCOPES_MAJOR 0)
7 set(UNITY_SCOPES_MINOR 6)
8-set(UNITY_SCOPES_MICRO 16)
9+set(UNITY_SCOPES_MICRO 17)
10 set(UNITY_SCOPES_SOVERSION 3)
11
12 # Version for testing, with all symbols visible
13
14=== modified file 'RELEASE_NOTES.md'
15--- RELEASE_NOTES.md 2015-04-10 07:21:29 +0000
16+++ RELEASE_NOTES.md 2015-04-23 15:43:14 +0000
17@@ -1,6 +1,10 @@
18 Release notes
19 =============
20
21+Changes in version 0.6.17
22+=========================
23+ - Added is_account_login_result() method to Result class.
24+
25 Changes in version 0.6.16
26 =========================
27 - Added support for attaching arbitrary data to CannedQuery.
28
29=== modified file 'debian/changelog'
30--- debian/changelog 2015-04-10 18:55:46 +0000
31+++ debian/changelog 2015-04-23 15:43:14 +0000
32@@ -1,3 +1,13 @@
33+unity-scopes-api (0.6.17-0ubuntu1) UNRELEASED; urgency=medium
34+
35+ [ Marcus Tomlinson ]
36+ * Added is_account_login_result() method to Result class.
37+
38+ [ Pawel Stolowski ]
39+ * Fix LP: #1446499.
40+
41+ -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Wed, 15 Apr 2015 17:22:07 +0200
42+
43 unity-scopes-api (0.6.16+15.04.20150410.3-0ubuntu1) vivid; urgency=medium
44
45 [ Pawel Stolowski ]
46
47=== modified file 'debian/libunity-scopes3.symbols'
48--- debian/libunity-scopes3.symbols 2015-04-10 18:55:46 +0000
49+++ debian/libunity-scopes3.symbols 2015-04-23 15:43:14 +0000
50@@ -379,6 +379,7 @@
51 (c++)"unity::scopes::internal::JsonSettingsSchema::~JsonSettingsSchema()@Base" 0.5.2+14.10.20140709.2
52 (c++)"unity::scopes::internal::JsonSettingsSchema::JsonSettingsSchema()@Base" 0.6.2+rtm+rtm+rtm+14.09.20140818
53 (c++)"unity::scopes::internal::JsonSettingsSchema::JsonSettingsSchema(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.5.2+14.10.20140709.2
54+ (c++)"unity::scopes::internal::make_directories(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int)@Base" 0replaceme
55 (c++)"unity::scopes::internal::MiddlewareBase::~MiddlewareBase()@Base" 0.4.0+14.04.20140312.1
56 (c++)"unity::scopes::internal::MiddlewareBase::MiddlewareBase(unity::scopes::internal::RuntimeImpl*)@Base" 0.4.0+14.04.20140312.1
57 (c++)"unity::scopes::internal::MiddlewareBase::runtime() const@Base" 0.4.0+14.04.20140312.1
58@@ -780,6 +781,7 @@
59 (c++)"unity::scopes::Result::direct_activation() const@Base" 0.4.0+14.04.20140312.1
60 (c++)"unity::scopes::Result::dnd_uri() const@Base" 0.4.0+14.04.20140312.1
61 (c++)"unity::scopes::Result::has_stored_result() const@Base" 0.4.0+14.04.20140312.1
62+ (c++)"unity::scopes::Result::is_account_login_result() const@Base" 0replaceme
63 (c++)"unity::scopes::Result::operator[](std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.4.0+14.04.20140312.1
64 (c++)"unity::scopes::Result::operator[](std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const@Base" 0.4.0+14.04.20140312.1
65 (c++)"unity::scopes::Result::operator=(unity::scopes::Result&&)@Base" 0.4.0+14.04.20140312.1
66
67=== modified file 'doc/tutorial.dox'
68--- doc/tutorial.dox 2015-02-25 05:20:57 +0000
69+++ doc/tutorial.dox 2015-04-23 15:43:14 +0000
70@@ -217,6 +217,51 @@
71 do not return more results than indicated by the cardinality. If you more than the requested number of results, you are
72 wasting resources. (The scopes run time ignores the additional results.)
73
74+\paragraph handlingaggregation Handling aggregation
75+
76+As previously stated, SearchMetadata contains additional information about the search requests you receive, including
77+the methods:
78+
79+<ul>
80+<li>\link unity::scopes::SearchMetadata::is_aggregated is_aggregated()\endlink - true if the request was initiated by
81+ an aggregator,
82+<li>and \link unity::scopes::SearchMetadata::aggregated_keywords aggregated_keywords()\endlink - the list of keywords
83+ used by the aggregator to find your scope.
84+</ul>
85+
86+\note Please refer to the <a href="https://developer.ubuntu.com/en/scopes/tutorials/scope-keywords/">Scope Keywords</a>
87+tutorial document for more detail on using keywords in your scope.
88+
89+You can use the is_aggregated() method from within
90+\link unity::scopes::SearchQueryBase::run SearchQueryBase::run()\endlink in order to ensure that an appropriate set of
91+results are returned when queried by an aggregator:
92+
93+\code{.cpp}
94+void MyQuery::run(SearchReplyProxy const& reply)
95+{
96+ if (metadata_.is_aggregated())
97+ {
98+ auto category = reply->register_category("agg_cat",
99+ "MyScope Featured",
100+ agg_icon);
101+ do_aggregated_search(reply, category);
102+ }
103+ else
104+ {
105+ do_normal_search(reply);
106+ }
107+}
108+\endcode
109+
110+You may notice in the code snippet above that for each aggregated search we receive, we register a specific results
111+category. Although aggregators may be willing to accept more than one category from its child scopes, they are only
112+required to accept the first.
113+
114+Thereafter, an aggregator may choose to ignore any additional categories the child scope registers. It is therefore
115+recommended that scope authors follow the above method of handling aggregated searches. It is also recommended that
116+your scope provide a decent category title (e.g. "MyScope Featured"). An aggregator is likely to display this category
117+title as is within its result set, so try to keep it clean and descriptive.
118+
119 \paragraph surfacingmode Surfacing mode
120
121 The query string may be the empty string. If so, the UI is asking your scope to produce default results
122@@ -549,7 +594,7 @@
123 of this class to each sub-query; the scopes run time invokes callback methods on this class
124 to let you know when a new result or status update arrives, and when a query completes.
125
126-\paragraph childscopes Locating child scopes
127+\paragraph childscopes Finding child scopes
128
129 To send queries to its child scopes, your scope must obtain a proxy for each child scope. The scopes run time
130 runs a registry process. The job of the registry (among other things) is to provide information about available
131@@ -560,13 +605,55 @@
132 \link unity::scopes::ScopeMetadata ScopeMetadata\endlink that describes the scope and also provides access to the
133 proxy for the scope.
134
135+You can also aggregate scopes indirectly via keyword(s). Keywords describe the type of content a scope provides (e.g.
136+a scope with the keyword "music" will return music results, the "video" keyword indicates video content, and so on).
137+You can obtain child scopes via keywords by calling \link unity::scopes::Registry::list_if list_if()\endlink on the
138+registry, supplying a predicate function. The return value is a map containing only those scopes for which the predicate
139+returns true. Therefore, your predicate function should return true for all scopes matching the keyword(s) you wish to
140+aggregate.
141+
142+\note Please refer to the <a href="https://developer.ubuntu.com/en/scopes/tutorials/scope-keywords/">Scope Keywords</a>
143+tutorial document for a list of recommended keywords to use.
144+
145+As an aggregator scope author you must provide an implementation of the virtual
146+\link unity::scopes::ScopeBase::find_child_scopes ScopeBase::find_child_scopes()\endlink method. All logic for finding
147+your aggregator's child scopes should be implemented within this method. The return value is of type
148+\link unity::scopes::ChildScopeList ChildScopeList\endlink and must contain an instance of
149+\link unity::scopes::ChildScope ChildScope\endlink for each scope your aggregator may collect results from.
150+
151+Here is how you could implement find_child_scopes() to return all scopes in the registry that contain the keywords
152+"sports" and "news":
153+
154+\code{.cpp}
155+ChildScopeList MyScope::find_child_scopes() const override
156+{
157+ auto sportsnews_scopes = registry()->list_if([](ScopeMetadata const& item)
158+ {
159+ auto keywords = item.keywords();
160+ return (keywords.find("sports") != keywords.end()) &&
161+ (keywords.find("news") != keywords.end());
162+ });
163+
164+ ChildScopeList list;
165+ for (auto const& sportsnews_scope : sportsnews_scopes)
166+ {
167+ list.emplace_back(ChildScope{sportsnews_scope.first, // Child scope ID
168+ sportsnews_scope.second, // Child scope metadata
169+ true, // Default enabled state (when first discovered)
170+ {"sports", "news"}}); // Keywords used to aggregate this scope
171+ }
172+ return list;
173+}
174+\endcode
175+
176 \paragraph subquery Sub-queries
177
178 To send a query to another scope, use one of the `subsearch()` overloads of \link unity::scopes::SearchQueryBase\endlink
179 inside your implementation of
180 \link unity::scopes::SearchQueryBase::run SearchQueryBase::run()\endlink.
181-This method requires the proxy to the child scope to query, the query details (\link unity::scopes::CannedQuery CannedQuery\endlink), plus
182-an instance of your `SearchListenerBase` implementation that will receive the query results.
183+This method requires a handle to the child scope to query (either via proxy or ChildScope handle), the query details
184+(\link unity::scopes::CannedQuery CannedQuery\endlink), plus an instance of your `SearchListenerBase` implementation
185+that will receive the query results.
186
187 \note `subsearch()` is identical to
188 \link unity::scopes::ScopeBase::search search()\endlink but, for `subsearch()`, the scopes run time transparently
189@@ -575,11 +662,19 @@
190 (However, your query class still needs to react to cancellation and should terminate the current query
191 is quickly as possible in response to a cancelled message.)
192
193+You should always call \link unity::scopes::ScopeBase::child_scopes ScopeBase::child_scopes()\endlink from within your
194+aggregator's \link unity::scopes::ScopeBase::search search()\endlink method in order to retrieve the latest child
195+scopes list containing the most recent "enabled" states. You can then pass this list into your instantiation of
196+SearchQueryBase for later use.
197+
198+\note An aggregator must respect the "enabled" states of its child scopes, querying only the child scopes that are
199+enabled.
200+
201 Here is how you could implement an aggregating scope that passes a query to a single child scope "scope-A":
202
203 \code{.cpp}
204
205-void MyScope::start(std::string const& scope_id) override
206+ChildScopeList MyScope::find_child_scopes() const override
207 {
208 auto reg = registry(); // Up-call into base class
209 if (!reg)
210@@ -587,21 +682,23 @@
211 throw ConfigException(scope_id + ": No registry available, cannot locate child scopes");
212 }
213
214+ ChildScopeList list;
215 try
216 {
217 auto meta = reg->get_metadata("scope-A");
218- scope_to_query_ = meta.proxy(); // store the proxy for passing it further in search
219+ list.emplace_back(ChildScope{"scope-A", meta});
220 }
221 catch (NotFoundException const& e)
222 {
223 ...
224 }
225+ return list;
226 }
227
228 QueryBase::UPtr MyScope::search(CannedQuery const& query,
229 SearchMetadata const& metadata)
230 {
231- SearchQueryBase::UPtr q(new MyQuery(query, metadata, scope_to_query_));
232+ SearchQueryBase::UPtr q(new MyQuery(query, metadata, child_scopes()));
233 return q;
234 }
235
236@@ -609,9 +706,13 @@
237
238 void MyQuery::run(SearchReplyProxy const& upstream_reply)
239 {
240- auto category = reply->register_category("recommended", "Recommended", icon, "");
241- SearchListenerBase::SPtr reply(new MyReceiver(upstream_reply, category));
242- subsearch(scope_to_query_, query_, reply);
243+ // Continue only if our child scope is installed AND enabled
244+ if (!child_scopes_.empty() && child_scopes_.front().enabled)
245+ {
246+ auto category = reply->register_category("recommended", "Recommended", icon, "");
247+ SearchListenerBase::SPtr reply(new MyReceiver(upstream_reply, category));
248+ subsearch(child_scopes_.front(), query_, reply);
249+ }
250 }
251 \endcode
252
253@@ -1214,6 +1315,9 @@
254 similar type (E.g. The Music scope will aggregate scopes with the keyword "music", and so on). The value of `Keywords` should specify a
255 list of keywords your scope falls under. Like `ChildScopes`, this value must be a semicolon separated list (E.g. `Keywords = music;video`).
256
257+\note Please refer to the <a href="https://developer.ubuntu.com/en/scopes/tutorials/scope-keywords/">Scope Keywords</a>
258+tutorial document for more detail on using keywords in your scope.
259+
260 \subsection scopetool The scope tool
261
262 The Unity Scope Tool is a stand-alone redering tool that allows you
263
264=== modified file 'include/unity/scopes/Result.h'
265--- include/unity/scopes/Result.h 2015-02-25 03:59:07 +0000
266+++ include/unity/scopes/Result.h 2015-04-23 15:43:14 +0000
267@@ -229,6 +229,12 @@
268 */
269 VariantMap serialize() const;
270
271+ /**
272+ \brief Check if this result is an online account login result.
273+ \return True if this result is an online account login result.
274+ */
275+ bool is_account_login_result() const;
276+
277 /// @cond
278 protected:
279 explicit Result(const VariantMap &variant_map);
280
281=== modified file 'include/unity/scopes/internal/RegistryObject.h'
282--- include/unity/scopes/internal/RegistryObject.h 2014-11-26 02:54:41 +0000
283+++ include/unity/scopes/internal/RegistryObject.h 2015-04-23 15:43:14 +0000
284@@ -29,6 +29,7 @@
285 #include <condition_variable>
286 #include <mutex>
287 #include <thread>
288+#include <chrono>
289
290 namespace unity
291 {
292@@ -152,6 +153,12 @@
293 mutable std::mutex mutex_;
294
295 MWPublisher::SPtr publisher_;
296+ std::thread publisher_notify_thread_;
297+ std::condition_variable publisher_notify_cond_;
298+ std::chrono::system_clock::time_point publisher_notify_timepoint_;
299+ bool publisher_notify_reset_timer_;
300+ bool publisher_notify_exit_;
301+
302 MWSubscriber::SPtr ss_list_update_subscriber_;
303 std::shared_ptr<core::ScopedConnection> ss_list_update_connection_;
304 bool generate_desktop_files_;
305
306=== modified file 'include/unity/scopes/internal/Utils.h'
307--- include/unity/scopes/internal/Utils.h 2014-11-17 01:29:26 +0000
308+++ include/unity/scopes/internal/Utils.h 2015-04-23 15:43:14 +0000
309@@ -55,6 +55,8 @@
310
311 int safe_system_call(std::string const& command);
312
313+void make_directories(std::string const& path_name, mode_t mode);
314+
315 } // namespace internal
316
317 } // namespace scopes
318
319=== modified file 'scoperegistry/scoperegistry.cpp'
320--- scoperegistry/scoperegistry.cpp 2015-02-17 00:27:08 +0000
321+++ scoperegistry/scoperegistry.cpp 2015-04-23 15:43:14 +0000
322@@ -26,13 +26,13 @@
323 #include <unity/scopes/internal/ScopeConfig.h>
324 #include <unity/scopes/internal/ScopeImpl.h>
325 #include <unity/scopes/internal/ScopeMetadataImpl.h>
326+#include <unity/scopes/internal/Utils.h>
327 #include <unity/scopes/ScopeExceptions.h>
328 #include <unity/UnityExceptions.h>
329
330+#include <boost/filesystem.hpp>
331 #include <boost/algorithm/string.hpp>
332-#include <boost/filesystem/operations.hpp>
333
334-#include <sys/stat.h> // TODO: remove this once hack for creating data root dir is removed
335 #include <wordexp.h>
336
337 using namespace scoperegistry;
338@@ -535,28 +535,17 @@
339 identity = runtime->registry_identity();
340 ss_reg_id = runtime->ss_registry_identity();
341
342- // TODO: HACK: We create the root of the cache and app directories for
343- // confined scopes, in case the scope is confined and the dir doesn't
344- // exist yet. This really should be done by the click-installation but,
345- // prior to RTM, we don't rely on that.
346- boost::system::error_code ec;
347-
348+ // Make sure that the cache and app directories exist.
349 string cache_root = rt_config.cache_directory() + "/leaf-net";
350- !boost::filesystem::exists(cache_root, ec) && ::mkdir(cache_root.c_str(), 0700);
351+ make_directories(cache_root, 0700);
352
353 string app_root = rt_config.app_directory();
354- !boost::filesystem::exists(app_root, ec) && ::mkdir(app_root.c_str(), 0700);
355+ make_directories(app_root, 0700);
356 } // Release memory for config parser
357
358- // Make sure that the parent directories for confined scope tmp directory exist.
359- {
360- string dir = string("/run/user/") + std::to_string(geteuid());
361- dir += "/scopes";
362- boost::system::error_code ec;
363- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700 | S_ISVTX);
364- dir += "/leaf-net";
365- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700 | S_ISVTX);
366- }
367+ // Make sure that the confined scope tmp directory exists.
368+ string tmp_dir = string("/run/user/") + std::to_string(geteuid()) + "/scopes/leaf-net";
369+ make_directories(tmp_dir, 0700);
370
371 // Collect the registry config data.
372
373
374=== modified file 'src/scopes/Result.cpp'
375--- src/scopes/Result.cpp 2014-02-14 17:00:04 +0000
376+++ src/scopes/Result.cpp 2015-04-23 15:43:14 +0000
377@@ -154,6 +154,11 @@
378 return p->serialize();
379 }
380
381+bool Result::is_account_login_result() const
382+{
383+ return p->contains("online_account_details");
384+}
385+
386 //! @endcond
387
388 } // namespace scopes
389
390=== modified file 'src/scopes/internal/RegistryObject.cpp'
391--- src/scopes/internal/RegistryObject.cpp 2015-01-20 08:25:39 +0000
392+++ src/scopes/internal/RegistryObject.cpp 2015-04-23 15:43:14 +0000
393@@ -37,6 +37,7 @@
394
395 static const char* c_debug_dbus_started_cmd = "dbus-send --type=method_call --dest=com.ubuntu.SDKAppLaunch /ScopeRegistryCallback com.ubuntu.SDKAppLaunch.ScopeLoaded";
396 static const char* c_debug_dbus_stopped_cmd = "dbus-send --type=method_call --dest=com.ubuntu.SDKAppLaunch /ScopeRegistryCallback com.ubuntu.SDKAppLaunch.ScopeStopped";
397+static const std::chrono::seconds removal_notification_delay(5);
398
399 namespace unity
400 {
401@@ -73,6 +74,8 @@
402 })
403 },
404 executor_(executor),
405+ publisher_notify_reset_timer_(false),
406+ publisher_notify_exit_(false),
407 generate_desktop_files_(generate_desktop_files)
408 {
409 if (middleware)
410@@ -99,10 +102,14 @@
411
412 RegistryObject::~RegistryObject()
413 {
414+ if (publisher_notify_thread_.joinable())
415 {
416- // The destructor may be called from an arbitrary
417- // thread, so we need a full fence here.
418- lock_guard<decltype(mutex_)> lock(mutex_);
419+ {
420+ lock_guard<decltype(mutex_)> lock(mutex_);
421+ publisher_notify_exit_ = true;
422+ publisher_notify_cond_.notify_one();
423+ }
424+ publisher_notify_thread_.join();
425 }
426
427 // kill all scope processes
428@@ -322,8 +329,34 @@
429
430 if (publisher_ && erased)
431 {
432- // Send a blank message to subscribers to inform them that the registry has been updated
433- publisher_->send_message("");
434+ // Send a blank message to subscribers to inform them that the registry has been updated.
435+ // Delay notification so that scope is not seen as removed and then added when updated.
436+ lock_guard<decltype(mutex_)> lock(mutex_);
437+
438+ if (!publisher_notify_thread_.joinable())
439+ {
440+ publisher_notify_timepoint_ = chrono::system_clock::now() + removal_notification_delay;
441+ publisher_notify_thread_ = thread([this]
442+ {
443+ unique_lock<decltype(mutex_)> lock(mutex_);
444+ while (!publisher_notify_exit_)
445+ {
446+ auto pred = [this]
447+ {
448+ return publisher_notify_exit_ || publisher_notify_reset_timer_;
449+ };
450+ if (!publisher_notify_cond_.wait_until(lock, publisher_notify_timepoint_, pred)) // pred is false, but timeout reached
451+ {
452+ publisher_->send_message("");
453+ publisher_notify_timepoint_ = chrono::system_clock::time_point::max();
454+ }
455+ publisher_notify_reset_timer_ = false;
456+ }
457+ });
458+ }
459+ publisher_notify_reset_timer_ = true;
460+ publisher_notify_timepoint_ = chrono::system_clock::now() + removal_notification_delay;
461+ publisher_notify_cond_.notify_one();
462 }
463
464 if (ex)
465
466=== modified file 'src/scopes/internal/RuntimeConfig.cpp'
467--- src/scopes/internal/RuntimeConfig.cpp 2015-01-06 02:38:27 +0000
468+++ src/scopes/internal/RuntimeConfig.cpp 2015-04-23 15:43:14 +0000
469@@ -19,18 +19,14 @@
470 #include <unity/scopes/internal/RuntimeConfig.h>
471
472 #include <unity/scopes/internal/DfltConfig.h>
473+#include <unity/scopes/internal/Utils.h>
474 #include <unity/scopes/ScopeExceptions.h>
475
476 #include <boost/algorithm/string/classification.hpp>
477 #include <boost/algorithm/string/constants.hpp>
478 #include <boost/algorithm/string/split.hpp>
479-#include <boost/filesystem.hpp>
480 #include <unity/UnityExceptions.h>
481
482-#include <sys/stat.h>
483-
484-#include <stdlib.h>
485-
486 using namespace std;
487
488 namespace unity
489@@ -343,9 +339,7 @@
490 throw ResourceException("RuntimeConfig::default_log_directory(): $HOME not set");
491 }
492 string dir = string(home) + "/" + DFLT_HOME_LOG_SUBDIR;
493-
494- boost::system::error_code ec;
495- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
496+ make_directories(dir, 0700);
497
498 return dir;
499 }
500
501=== modified file 'src/scopes/internal/RuntimeImpl.cpp'
502--- src/scopes/internal/RuntimeImpl.cpp 2015-03-02 03:59:01 +0000
503+++ src/scopes/internal/RuntimeImpl.cpp 2015-04-23 15:43:14 +0000
504@@ -30,6 +30,7 @@
505 #include <unity/scopes/internal/ScopeObject.h>
506 #include <unity/scopes/internal/SettingsDB.h>
507 #include <unity/scopes/internal/UniqueID.h>
508+#include <unity/scopes/internal/Utils.h>
509 #include <unity/scopes/ScopeBase.h>
510 #include <unity/scopes/ScopeExceptions.h>
511
512@@ -42,7 +43,6 @@
513 #include <future>
514
515 #include <sys/apparmor.h>
516-#include <sys/stat.h>
517
518 using namespace std;
519 using namespace unity::scopes;
520@@ -592,43 +592,39 @@
521 string RuntimeImpl::find_cache_dir() const
522 {
523 // Create the cache_dir_/<confinement-type>/<id> directories if they don't exist.
524- boost::system::error_code ec;
525- !confined() && !boost::filesystem::exists(cache_dir_, ec) && ::mkdir(cache_dir_.c_str(), 0700);
526 string dir = cache_dir_ + "/" + confinement_type();
527- !confined() && !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
528-
529+ if (!confined()) // Avoid apparmor noise
530+ {
531+ string dir = cache_dir_ + "/" + confinement_type();
532+ make_directories(cache_dir_, 0700);
533+ }
534 // A confined scope is allowed to create this dir.
535 dir += "/" + demangled_id(scope_id_);
536- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
537-
538+ make_directories(dir, 0700);
539 return dir;
540 }
541
542 string RuntimeImpl::find_app_dir() const
543 {
544 // Create the app_dir_/<id> directories if they don't exist.
545- boost::system::error_code ec;
546- !confined() && !boost::filesystem::exists(app_dir_, ec) && ::mkdir(app_dir_.c_str(), 0700);
547 string dir = app_dir_ + "/" + demangled_id(scope_id_);
548- !confined() && !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
549-
550+ if (!confined()) // Avoid apparmor noise
551+ {
552+ make_directories(dir, 0700);
553+ }
554 return dir;
555 }
556
557 string RuntimeImpl::find_log_dir(string const& id) const
558 {
559- // Create the log_dir_/<confinement-type>/<id>/logs directories if they don't exist.
560- boost::system::error_code ec;
561- !confined() && !boost::filesystem::exists(log_dir_, ec) && ::mkdir(log_dir_.c_str(), 0700);
562 string dir = log_dir_ + "/" + confinement_type();
563- !confined() && !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
564-
565+ if (!confined()) // Avoid apparmore noise
566+ {
567+ make_directories(dir, 0700);
568+ }
569 // A confined scope is allowed to create this dir.
570- dir += "/" + demangled_id(id);
571- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
572- dir += "/logs";
573- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700);
574-
575+ dir += "/" + demangled_id(id) + "/logs";
576+ make_directories(dir, 0700);
577 return dir;
578 }
579
580@@ -638,17 +634,14 @@
581 // We need to create any directories under /run/user/<uid> because they might not
582 // exist. We set the sticky bit because, without this, things in
583 // /run/user may be deleted if not accessed for more than six hours.
584- string dir = string("/run/user/") + std::to_string(geteuid());
585- dir += "/scopes";
586- boost::system::error_code ec;
587- !confined() && !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700 | S_ISVTX);
588- dir += "/" + confinement_type();
589- !confined() && !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700 | S_ISVTX);
590-
591+ string dir = string("/run/user/") + std::to_string(geteuid()) + "/scopes/" + confinement_type();
592+ if (!confined()) // Avoid apparmor noise
593+ {
594+ make_directories(dir, 0700);
595+ }
596 // A confined scope is allowed to create this dir.
597 dir += "/" + scope_id_; // Not demangled, use the real scope ID.
598- !boost::filesystem::exists(dir, ec) && ::mkdir(dir.c_str(), 0700 | S_ISVTX);
599-
600+ make_directories(dir, 0700);
601 return dir;
602 }
603
604
605=== modified file 'src/scopes/internal/SearchQueryBaseImpl.cpp'
606--- src/scopes/internal/SearchQueryBaseImpl.cpp 2015-04-09 15:59:17 +0000
607+++ src/scopes/internal/SearchQueryBaseImpl.cpp 2015-04-23 15:43:14 +0000
608@@ -193,14 +193,19 @@
609 reply->finished(CompletionDetails(CompletionDetails::OK,
610 "empty result set due to aggregator loop or repeated query on aggregating scope "
611 + get<1>(tuple)));
612+ ctrl_proxy = make_shared<QueryCtrlImpl>(nullptr, nullptr); // Dummy proxy in already-cancelled state
613+
614 auto scope_impl = dynamic_pointer_cast<ScopeImpl>(scope);
615- auto logger = scope_impl->runtime()->logger();
616- ctrl_proxy = make_shared<QueryCtrlImpl>(nullptr, nullptr); // Dummy proxy in already-cancelled state
617- BOOST_LOG_SEV(logger, Logger::Warning)
618- << "query loop for query \"" << canned_query_.query_string()
619- << "\", client: " << get<0>(tuple)
620- << ", aggregator: " << get<1>(tuple)
621- << ", receiver: " << get<2>(tuple) << endl;
622+ // scope_impl can be nullptr if we use a mock scope: TypedScopeFixture<testing::Scope>
623+ if (scope_impl)
624+ {
625+ auto logger = scope_impl->runtime()->logger();
626+ BOOST_LOG_SEV(logger, Logger::Warning)
627+ << "query loop for query \"" << canned_query_.query_string()
628+ << "\", client: " << get<0>(tuple)
629+ << ", aggregator: " << get<1>(tuple)
630+ << ", receiver: " << get<2>(tuple) << endl;
631+ }
632 }
633 else
634 {
635
636=== modified file 'src/scopes/internal/Utils.cpp'
637--- src/scopes/internal/Utils.cpp 2014-11-17 01:29:26 +0000
638+++ src/scopes/internal/Utils.cpp 2015-04-23 15:43:14 +0000
639@@ -20,10 +20,16 @@
640 #include <unity/scopes/ScopeExceptions.h>
641 #include <unity/UnityExceptions.h>
642
643+#include <boost/filesystem.hpp>
644+
645 #include <iomanip>
646 #include <locale>
647 #include <mutex>
648
649+#include <sys/stat.h>
650+
651+using namespace std;
652+
653 namespace unity
654 {
655
656@@ -33,7 +39,7 @@
657 namespace internal
658 {
659
660-VariantMap::const_iterator find_or_throw(std::string const& context, VariantMap const& var, std::string const& key)
661+VariantMap::const_iterator find_or_throw(string const& context, VariantMap const& var, string const& key)
662 {
663 auto it = var.find(key);
664 if (it == var.end())
665@@ -43,14 +49,14 @@
666 return it;
667 }
668
669-std::string to_percent_encoding(std::string const& str)
670+string to_percent_encoding(string const& str)
671 {
672- std::ostringstream result;
673+ ostringstream result;
674 for (auto const& c: str)
675 {
676 if ((!isalnum(c)))
677 {
678- result << '%' << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << static_cast<int>(static_cast<unsigned char>(c)) << std::nouppercase;
679+ result << '%' << setw(2) << setfill('0') << hex << uppercase << static_cast<int>(static_cast<unsigned char>(c)) << nouppercase;
680 }
681 else
682 {
683@@ -60,9 +66,9 @@
684 return result.str();
685 }
686
687-std::string from_percent_encoding(std::string const& str)
688+string from_percent_encoding(string const& str)
689 {
690- std::ostringstream result;
691+ ostringstream result;
692 for (auto it = str.begin(); it != str.end(); it++)
693 {
694 auto c = *it;
695@@ -75,16 +81,16 @@
696 c = *it;
697 if (++it != str.end())
698 {
699- std::string const hexnum { c, *it };
700+ string const hexnum { c, *it };
701 try
702 {
703- auto k = std::stoi(hexnum, nullptr, 16);
704+ auto k = stoi(hexnum, nullptr, 16);
705 result << static_cast<char>(k);
706 valid = true;
707 }
708- catch (std::logic_error const& e) // covers both std::invalid_argument and std::out_of_range
709+ catch (logic_error const& e) // covers both invalid_argument and out_of_range
710 {
711- std::stringstream err;
712+ stringstream err;
713 err << "from_percent_encoding(): unsupported conversion of '" << hexnum << "'";
714 throw unity::InvalidArgumentException(err.str());
715 }
716@@ -103,33 +109,33 @@
717 return result.str();
718 }
719
720-std::string uncamelcase(std::string const& str)
721+string uncamelcase(string const& str)
722 {
723- const std::locale loc("");
724+ const locale loc("");
725 if (str.size() == 0)
726 {
727 return str;
728 }
729 auto it = str.begin();
730- int previous_is_lower = std::islower(*it);
731- std::stringstream res;
732- res << std::tolower(*it, loc);
733+ int previous_is_lower = islower(*it);
734+ stringstream res;
735+ res << tolower(*it, loc);
736 ++it;
737 while (it != str.end())
738 {
739- if (std::isupper(*it) && previous_is_lower)
740+ if (isupper(*it) && previous_is_lower)
741 {
742 res << "-";
743 }
744- previous_is_lower = std::islower(*it);
745- res << std::tolower(*it, loc);
746+ previous_is_lower = islower(*it);
747+ res << tolower(*it, loc);
748 ++it;
749 }
750 return res.str();
751 }
752
753 template<>
754-bool convert_to<bool>(std::string const& val, Variant& out)
755+bool convert_to<bool>(string const& val, Variant& out)
756 {
757 if (val == "true")
758 {
759@@ -144,11 +150,44 @@
760 return false;
761 }
762
763-int safe_system_call(std::string const& command)
764-{
765- static std::mutex system_mutex;
766- std::lock_guard<std::mutex> lock(system_mutex);
767- return std::system(command.c_str());
768+int safe_system_call(string const& command)
769+{
770+ static mutex system_mutex;
771+ lock_guard<mutex> lock(system_mutex);
772+ return system(command.c_str());
773+}
774+
775+// Recursively create the directories in path, setting permissions to the specified mode
776+// (regardless of the setting of umask). If one or more directories already exist, they
777+// are left unchanged (including their permissions). If a directory cannot be created,
778+// fail silently.
779+
780+void make_directories(string const& path_name, mode_t mode)
781+{
782+ using namespace boost::filesystem;
783+
784+ // We can't use boost::create_directories() here because that does not allow control over permissions.
785+ auto abs = absolute(path_name);
786+ path path_so_far = "";
787+ path::iterator it = abs.begin();
788+ ++it; // No point in trying to create /
789+ while (it != abs.end())
790+ {
791+ path_so_far += "/";
792+ path_so_far += *it++;
793+ string p = path_so_far.native();
794+ if (mkdir(p.c_str(), mode) != 0)
795+ {
796+ if (errno == EEXIST)
797+ {
798+ continue;
799+ }
800+ return; // No point in continuing, we'd fail on all subsequent iterations.
801+ }
802+ // We just created the dir, make sure it has the requested permissions,
803+ // not the permissions modified by umask.
804+ chmod(p.c_str(), mode);
805+ }
806 }
807
808 } // namespace internal
809
810=== modified file 'src/scopes/internal/smartscopes/SmartScopesClient.cpp'
811--- src/scopes/internal/smartscopes/SmartScopesClient.cpp 2015-01-28 09:03:59 +0000
812+++ src/scopes/internal/smartscopes/SmartScopesClient.cpp 2015-04-23 15:43:14 +0000
813@@ -21,6 +21,7 @@
814 #include <unity/scopes/internal/FilterStateImpl.h>
815 #include <unity/scopes/internal/RuntimeImpl.h>
816 #include <unity/scopes/internal/smartscopes/SmartScopesClient.h>
817+#include <unity/scopes/internal/Utils.h>
818
819 #include <unity/scopes/ScopeExceptions.h>
820 #include <unity/UnityExceptions.h>
821@@ -34,7 +35,6 @@
822 #include <utility>
823 #include <map>
824 #include <sstream>
825-#include <sys/stat.h>
826
827 static std::string homedir()
828 {
829@@ -860,7 +860,7 @@
830 void SmartScopesClient::write_cache(std::string const& scopes_json)
831 {
832 // make cache directory (fails silently if already exists)
833- mkdir(c_scopes_cache_dir.c_str(), 0755);
834+ make_directories(c_scopes_cache_dir, 0755);
835
836 // open cache for output
837 std::ofstream cache_file(c_scopes_cache_dir + c_scopes_cache_filename);
838
839=== added file 'test/gtest/scopes/Aggregation/CMakeLists.txt'
840--- test/gtest/scopes/Aggregation/CMakeLists.txt 1970-01-01 00:00:00 +0000
841+++ test/gtest/scopes/Aggregation/CMakeLists.txt 2015-04-23 15:43:14 +0000
842@@ -0,0 +1,39 @@
843+configure_file(TestRegistry.ini.in ${CMAKE_CURRENT_BINARY_DIR}/TestRegistry.ini)
844+configure_file(Runtime.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini)
845+configure_file(Zmq.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Zmq.ini)
846+
847+add_definitions(-DTEST_RUNTIME_PATH="${CMAKE_CURRENT_BINARY_DIR}")
848+add_definitions(-DTEST_RUNTIME_FILE="${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini")
849+add_definitions(-DTEST_REGISTRY_PATH="${PROJECT_BINARY_DIR}/scoperegistry")
850+
851+
852+add_executable(ChildScopes_test ChildScopes_test.cpp)
853+target_link_libraries(ChildScopes_test ${TESTLIBS})
854+
855+add_test(ChildScopes ChildScopes_test)
856+
857+
858+add_executable(Keywords_test Keywords_test.cpp)
859+target_link_libraries(Keywords_test ${TESTLIBS})
860+
861+add_test(Keywords Keywords_test)
862+
863+
864+add_executable(LoopDetection_test LoopDetection_test.cpp AggTestScope.cpp)
865+target_link_libraries(LoopDetection_test ${TESTLIBS})
866+
867+add_dependencies(LoopDetection_test scoperegistry scoperunner)
868+
869+add_test(LoopDetection LoopDetection_test)
870+
871+set(SCOPE_DIR "${CMAKE_CURRENT_BINARY_DIR}/scopes")
872+
873+foreach (scope A B C D)
874+ file(MAKE_DIRECTORY "${SCOPE_DIR}/${scope}")
875+ configure_file(AggTestScope.ini.in ${SCOPE_DIR}/${scope}/${scope}.ini)
876+ add_library(${scope} MODULE AggTestScope.cpp)
877+ set_target_properties(${scope}
878+ PROPERTIES
879+ LIBRARY_OUTPUT_DIRECTORY "${SCOPE_DIR}/${scope}/"
880+ )
881+endforeach()
882
883=== removed file 'test/gtest/scopes/Aggregation/CMakeLists.txt'
884--- test/gtest/scopes/Aggregation/CMakeLists.txt 2015-04-09 15:59:17 +0000
885+++ test/gtest/scopes/Aggregation/CMakeLists.txt 1970-01-01 00:00:00 +0000
886@@ -1,39 +0,0 @@
887-configure_file(TestRegistry.ini.in ${CMAKE_CURRENT_BINARY_DIR}/TestRegistry.ini)
888-configure_file(Runtime.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini)
889-configure_file(Zmq.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Zmq.ini)
890-
891-add_definitions(-DTEST_RUNTIME_PATH="${CMAKE_CURRENT_BINARY_DIR}")
892-add_definitions(-DTEST_RUNTIME_FILE="${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini")
893-add_definitions(-DTEST_REGISTRY_PATH="${PROJECT_BINARY_DIR}/scoperegistry")
894-
895-
896-add_executable(ChildScopes_test ChildScopes_test.cpp)
897-target_link_libraries(ChildScopes_test ${TESTLIBS})
898-
899-add_test(ChildScopes ChildScopes_test)
900-
901-
902-add_executable(Keywords_test Keywords_test.cpp)
903-target_link_libraries(Keywords_test ${TESTLIBS})
904-
905-add_test(Keywords Keywords_test)
906-
907-
908-add_executable(LoopDetection_test LoopDetection_test.cpp AggTestScope.cpp)
909-target_link_libraries(LoopDetection_test ${TESTLIBS})
910-
911-add_dependencies(LoopDetection_test scoperegistry scoperunner)
912-
913-add_test(LoopDetection LoopDetection_test)
914-
915-set(SCOPE_DIR "${CMAKE_CURRENT_BINARY_DIR}/scopes")
916-
917-foreach (scope A B C D)
918- file(MAKE_DIRECTORY "${SCOPE_DIR}/${scope}")
919- configure_file(AggTestScope.ini.in ${SCOPE_DIR}/${scope}/${scope}.ini)
920- add_library(${scope} MODULE AggTestScope.cpp)
921- set_target_properties(${scope}
922- PROPERTIES
923- LIBRARY_OUTPUT_DIRECTORY "${SCOPE_DIR}/${scope}/"
924- )
925-endforeach()
926
927=== modified file 'test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp'
928--- test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2015-01-26 06:02:10 +0000
929+++ test/gtest/scopes/OnlineAccountClient/OnlineAccountClient_test.cpp 2015-04-23 15:43:14 +0000
930@@ -307,7 +307,9 @@
931 auto cat = reg.register_category("1", "title", "icon", nullptr, CategoryRenderer());
932 CategorisedResult result(cat);
933
934+ EXPECT_FALSE(result.is_account_login_result());
935 oa_client.register_account_login_item(result, CannedQuery("test"), OnlineAccountClient::InvalidateResults, OnlineAccountClient::DoNothing);
936+ EXPECT_TRUE(result.is_account_login_result());
937
938 EXPECT_TRUE(result.contains("online_account_details"));
939
940
941=== modified file 'test/gtest/scopes/Registry/Registry_test.cpp'
942--- test/gtest/scopes/Registry/Registry_test.cpp 2015-01-27 12:46:11 +0000
943+++ test/gtest/scopes/Registry/Registry_test.cpp 2015-04-23 15:43:14 +0000
944@@ -153,7 +153,7 @@
945 EXPECT_FALSE(meta.is_aggregator());
946 }
947
948-auto const wait_for_update_time = std::chrono::milliseconds(5000);
949+auto const wait_for_update_time = std::chrono::milliseconds(10000);
950
951 TEST(Registry, scope_state_notify)
952 {
953
954=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/BadLogDirSize.ini' => 'test/gtest/scopes/internal/RuntimeConfig/BadLogDirSize.ini.in'
955=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/BadLogFileSize.ini' => 'test/gtest/scopes/internal/RuntimeConfig/BadLogFileSize.ini.in'
956=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/BadMW.ini' => 'test/gtest/scopes/internal/RuntimeConfig/BadMW.ini.in'
957=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/BadReapExpiry.ini' => 'test/gtest/scopes/internal/RuntimeConfig/BadReapExpiry.ini.in'
958=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/BadReapInterval.ini' => 'test/gtest/scopes/internal/RuntimeConfig/BadReapInterval.ini.in'
959=== modified file 'test/gtest/scopes/internal/RuntimeConfig/CMakeLists.txt'
960--- test/gtest/scopes/internal/RuntimeConfig/CMakeLists.txt 2014-06-23 06:53:45 +0000
961+++ test/gtest/scopes/internal/RuntimeConfig/CMakeLists.txt 2015-04-23 15:43:14 +0000
962@@ -1,5 +1,12 @@
963-add_definitions(-DTEST_SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
964+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_BINARY_DIR}")
965 add_executable(RuntimeConfig_test RuntimeConfig_test.cpp)
966 target_link_libraries(RuntimeConfig_test ${TESTLIBS})
967
968+file(GLOB ini_files "*.ini.in")
969+foreach(ini_in ${ini_files})
970+ get_filename_component(file ${ini_in} NAME)
971+ string(REGEX REPLACE "(.*)\\.in$" "\\1" ini ${file})
972+ configure_file(${file} ${CMAKE_CURRENT_BINARY_DIR}/${ini})
973+endforeach(ini_in)
974+
975 add_test(RuntimeConfig RuntimeConfig_test)
976
977=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/CacheDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/CacheDir.ini.in'
978=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/Complete.ini' => 'test/gtest/scopes/internal/RuntimeConfig/Complete.ini.in'
979=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/ConfigDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/ConfigDir.ini.in'
980=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/LogDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/LogDir.ini.in'
981=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/NoAppDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/NoAppDir.ini.in'
982=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/NoCacheDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/NoCacheDir.ini.in'
983=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/NoConfigDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/NoConfigDir.ini.in'
984=== renamed file 'test/gtest/scopes/internal/RuntimeConfig/NoLogDir.ini' => 'test/gtest/scopes/internal/RuntimeConfig/NoLogDir.ini.in'
985=== modified file 'test/gtest/scopes/internal/RuntimeConfig/RuntimeConfig_test.cpp'
986--- test/gtest/scopes/internal/RuntimeConfig/RuntimeConfig_test.cpp 2015-02-12 23:21:47 +0000
987+++ test/gtest/scopes/internal/RuntimeConfig/RuntimeConfig_test.cpp 2015-04-23 15:43:14 +0000
988@@ -32,7 +32,7 @@
989
990 TEST(RuntimeConfig, basic)
991 {
992- setenv("HOME", TEST_SRC_DIR, 1);
993+ setenv("HOME", TEST_DIR, 1);
994
995 RuntimeConfig c("");
996 EXPECT_EQ("Registry", c.registry_identity());
997@@ -50,7 +50,7 @@
998
999 TEST(RuntimeConfig, complete)
1000 {
1001- RuntimeConfig c(TEST_SRC_DIR "/Complete.ini");
1002+ RuntimeConfig c(TEST_DIR "/Complete.ini");
1003 EXPECT_EQ("R.Id", c.registry_identity());
1004 EXPECT_EQ("R.Config", c.registry_configfile());
1005 EXPECT_EQ("SS.R.Id", c.ss_registry_identity());
1006@@ -70,25 +70,25 @@
1007
1008 TEST(RuntimeConfig, _default_cache_dir)
1009 {
1010- setenv("HOME", TEST_SRC_DIR, 1);
1011+ setenv("HOME", TEST_DIR, 1);
1012
1013 RuntimeConfig c("");
1014- EXPECT_EQ(TEST_SRC_DIR "/.local/share/unity-scopes", c.cache_directory());
1015+ EXPECT_EQ(TEST_DIR "/.local/share/unity-scopes", c.cache_directory());
1016 }
1017
1018 TEST(RuntimeConfig, overridden_cache_dir)
1019 {
1020 unsetenv("HOME");
1021
1022- RuntimeConfig c(TEST_SRC_DIR "/CacheDir.ini");
1023+ RuntimeConfig c(TEST_DIR "/CacheDir.ini");
1024 EXPECT_EQ("cachedir", c.cache_directory());
1025 }
1026
1027 TEST(RuntimeConfig, overridden_cache_dir_with_home_dir)
1028 {
1029- setenv("HOME", TEST_SRC_DIR, 1);
1030+ setenv("HOME", TEST_DIR, 1);
1031
1032- RuntimeConfig c(TEST_SRC_DIR "/CacheDir.ini");
1033+ RuntimeConfig c(TEST_DIR "/CacheDir.ini");
1034 EXPECT_EQ("cachedir", c.cache_directory());
1035 }
1036
1037@@ -96,15 +96,15 @@
1038 {
1039 unsetenv("HOME");
1040
1041- RuntimeConfig c(TEST_SRC_DIR "/CacheDir.ini");
1042+ RuntimeConfig c(TEST_DIR "/CacheDir.ini");
1043 EXPECT_EQ("appdir", c.app_directory());
1044 }
1045
1046 TEST(RuntimeConfig, overridden_app_dir_with_home_dir)
1047 {
1048- setenv("HOME", TEST_SRC_DIR, 1);
1049+ setenv("HOME", TEST_DIR, 1);
1050
1051- RuntimeConfig c(TEST_SRC_DIR "/ConfigDir.ini");
1052+ RuntimeConfig c(TEST_DIR "/ConfigDir.ini");
1053 EXPECT_EQ("appdir", c.app_directory());
1054 }
1055
1056@@ -112,15 +112,15 @@
1057 {
1058 unsetenv("HOME");
1059
1060- RuntimeConfig c(TEST_SRC_DIR "/ConfigDir.ini");
1061+ RuntimeConfig c(TEST_DIR "/ConfigDir.ini");
1062 EXPECT_EQ("configdir", c.config_directory());
1063 }
1064
1065 TEST(RuntimeConfig, overridden_config_dir_with_home_dir)
1066 {
1067- setenv("HOME", TEST_SRC_DIR, 1);
1068+ setenv("HOME", TEST_DIR, 1);
1069
1070- RuntimeConfig c(TEST_SRC_DIR "/ConfigDir.ini");
1071+ RuntimeConfig c(TEST_DIR "/ConfigDir.ini");
1072 EXPECT_EQ("configdir", c.config_directory());
1073 }
1074
1075@@ -128,13 +128,13 @@
1076 {
1077 unsetenv("HOME");
1078
1079- RuntimeConfig c(TEST_SRC_DIR "/LogDir.ini");
1080+ RuntimeConfig c(TEST_DIR "/LogDir.ini");
1081 EXPECT_EQ("logdir", c.log_directory());
1082 }
1083
1084 TEST(RuntimeConfig, overridden_log_dir_with_home_dir)
1085 {
1086- RuntimeConfig c(TEST_SRC_DIR "/LogDir.ini");
1087+ RuntimeConfig c(TEST_DIR "/LogDir.ini");
1088 EXPECT_EQ("logdir", c.log_directory());
1089 }
1090
1091@@ -142,7 +142,7 @@
1092 {
1093 setenv("UNITY_SCOPES_LOGDIR", "otherdir", 1);
1094
1095- RuntimeConfig c(TEST_SRC_DIR "/LogDir.ini");
1096+ RuntimeConfig c(TEST_DIR "/LogDir.ini");
1097 EXPECT_EQ("otherdir", c.log_directory());
1098 }
1099
1100@@ -150,7 +150,7 @@
1101 {
1102 setenv("UNITY_SCOPES_LOG_TRACECHANNELS", "ABC;XYZ;;DEF", 1);
1103
1104- RuntimeConfig c(TEST_SRC_DIR "/Complete.ini");
1105+ RuntimeConfig c(TEST_DIR "/Complete.ini");
1106 EXPECT_EQ((vector<string>{ "ABC", "XYZ", "DEF"}), c.trace_channels());
1107 }
1108
1109@@ -158,36 +158,36 @@
1110 {
1111 try
1112 {
1113- RuntimeConfig c(TEST_SRC_DIR "/BadMW.ini");
1114+ RuntimeConfig c(TEST_DIR "/BadMW.ini");
1115 FAIL();
1116 }
1117 catch (ConfigException const& e)
1118 {
1119- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/BadMW.ini\": Illegal value for Default.Middleware: "
1120+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/BadMW.ini\": Illegal value for Default.Middleware: "
1121 "\"Foo\": legal values are \"Zmq\" and \"REST\"",
1122 e.what());
1123 }
1124
1125 try
1126 {
1127- RuntimeConfig c(TEST_SRC_DIR "/BadReapExpiry.ini");
1128+ RuntimeConfig c(TEST_DIR "/BadReapExpiry.ini");
1129 FAIL();
1130 }
1131 catch (ConfigException const& e)
1132 {
1133- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/BadReapExpiry.ini\": Illegal value (0) for "
1134+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/BadReapExpiry.ini\": Illegal value (0) for "
1135 "Reap.Expiry: value must be > 0",
1136 e.what());
1137 }
1138
1139 try
1140 {
1141- RuntimeConfig c(TEST_SRC_DIR "/BadReapInterval.ini");
1142+ RuntimeConfig c(TEST_DIR "/BadReapInterval.ini");
1143 FAIL();
1144 }
1145 catch (ConfigException const& e)
1146 {
1147- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/BadReapInterval.ini\": Illegal value (0) for "
1148+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/BadReapInterval.ini\": Illegal value (0) for "
1149 "Reap.Interval: value must be > 0",
1150 e.what());
1151 }
1152@@ -196,12 +196,12 @@
1153 {
1154 unsetenv("HOME");
1155
1156- RuntimeConfig c(TEST_SRC_DIR "/NoCacheDir.ini");
1157+ RuntimeConfig c(TEST_DIR "/NoCacheDir.ini");
1158 FAIL();
1159 }
1160 catch (ConfigException const& e)
1161 {
1162- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/NoCacheDir.ini\": No CacheDir configured and "
1163+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/NoCacheDir.ini\": No CacheDir configured and "
1164 "failed to get default:\n unity::ResourceException: RuntimeConfig::default_cache_directory(): "
1165 "$HOME not set",
1166 e.what());
1167@@ -211,12 +211,12 @@
1168 {
1169 unsetenv("HOME");
1170
1171- RuntimeConfig c(TEST_SRC_DIR "/NoConfigDir.ini");
1172+ RuntimeConfig c(TEST_DIR "/NoConfigDir.ini");
1173 FAIL();
1174 }
1175 catch (ConfigException const& e)
1176 {
1177- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/NoConfigDir.ini\": No ConfigDir configured and "
1178+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/NoConfigDir.ini\": No ConfigDir configured and "
1179 "failed to get default:\n unity::ResourceException: RuntimeConfig::default_config_directory(): "
1180 "$HOME not set",
1181 e.what());
1182@@ -226,13 +226,13 @@
1183 {
1184 unsetenv("HOME");
1185
1186- RuntimeConfig c(TEST_SRC_DIR "/NoLogDir.ini");
1187+ RuntimeConfig c(TEST_DIR "/NoLogDir.ini");
1188 FAIL();
1189 }
1190 catch (ConfigException const& e)
1191 {
1192 // Using regex here because error message returned by glib changed from Utopic to Vivid.
1193- // The final .* takes care of the difference. Note that, instead of using TEST_SRC_DIR, we
1194+ // The final .* takes care of the difference. Note that, instead of using TEST_DIR, we
1195 // use .+. That's because, when building with bzr bd, we end up with a '+' in the path,
1196 // and that is a regex metacharacter, causing the match to fail.
1197 boost::regex r("unity::scopes::ConfigException: \".+/NoLogDir.ini\": "
1198@@ -246,12 +246,12 @@
1199 {
1200 unsetenv("HOME");
1201
1202- RuntimeConfig c(TEST_SRC_DIR "/NoAppDir.ini");
1203+ RuntimeConfig c(TEST_DIR "/NoAppDir.ini");
1204 FAIL();
1205 }
1206 catch (ConfigException const& e)
1207 {
1208- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/NoAppDir.ini\": "
1209+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/NoAppDir.ini\": "
1210 "No AppDir configured and failed to get default:\n"
1211 " unity::ResourceException: RuntimeConfig::default_app_directory(): $HOME not set",
1212 e.what());
1213@@ -259,24 +259,24 @@
1214
1215 try
1216 {
1217- RuntimeConfig c(TEST_SRC_DIR "/BadLogFileSize.ini");
1218+ RuntimeConfig c(TEST_DIR "/BadLogFileSize.ini");
1219 FAIL();
1220 }
1221 catch (ConfigException const& e)
1222 {
1223- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/BadLogFileSize.ini\": "
1224+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/BadLogFileSize.ini\": "
1225 "Illegal value (999) for Log.MaxFileSize: value must be > 1024",
1226 e.what());
1227 }
1228
1229 try
1230 {
1231- RuntimeConfig c(TEST_SRC_DIR "/BadLogDirSize.ini");
1232+ RuntimeConfig c(TEST_DIR "/BadLogDirSize.ini");
1233 FAIL();
1234 }
1235 catch (ConfigException const& e)
1236 {
1237- EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_SRC_DIR "/BadLogDirSize.ini\": "
1238+ EXPECT_STREQ("unity::scopes::ConfigException: \"" TEST_DIR "/BadLogDirSize.ini\": "
1239 "Illegal value (1024) for Log.MaxDirSize: value must be > Log.MaxFileSize (2048)",
1240 e.what());
1241 }
1242
1243=== modified file 'test/gtest/scopes/internal/Utils/CMakeLists.txt'
1244--- test/gtest/scopes/internal/Utils/CMakeLists.txt 2014-06-25 14:38:30 +0000
1245+++ test/gtest/scopes/internal/Utils/CMakeLists.txt 2015-04-23 15:43:14 +0000
1246@@ -1,4 +1,5 @@
1247 add_executable(Utils_test Utils_test.cpp)
1248+add_definitions(-DTEST_DIR="${CMAKE_CURRENT_BINARY_DIR}")
1249 target_link_libraries(Utils_test ${TESTLIBS})
1250
1251 add_test(Utils Utils_test)
1252
1253=== modified file 'test/gtest/scopes/internal/Utils/Utils_test.cpp'
1254--- test/gtest/scopes/internal/Utils/Utils_test.cpp 2014-11-17 01:29:26 +0000
1255+++ test/gtest/scopes/internal/Utils/Utils_test.cpp 2015-04-23 15:43:14 +0000
1256@@ -17,11 +17,14 @@
1257 */
1258
1259 #include <unity/scopes/internal/Utils.h>
1260+
1261+#include <boost/filesystem.hpp>
1262+#include <gtest/gtest.h>
1263 #include <unity/UnityExceptions.h>
1264-#include <gtest/gtest.h>
1265
1266 using namespace unity::scopes;
1267 using namespace unity::scopes::internal;
1268+using namespace std;
1269
1270 TEST(Utils, uncamelcase)
1271 {
1272@@ -47,7 +50,7 @@
1273 }
1274 {
1275 Variant out;
1276- EXPECT_TRUE(convert_to<std::string>("foo", out));
1277+ EXPECT_TRUE(convert_to<string>("foo", out));
1278 EXPECT_EQ("foo", out.get_string());
1279 }
1280 {
1281@@ -66,3 +69,66 @@
1282 EXPECT_FALSE(out.get_bool());
1283 }
1284 }
1285+
1286+void empty_dir(string const& dir)
1287+{
1288+ namespace fs = boost::filesystem;
1289+ try
1290+ {
1291+ for (fs::directory_iterator end, it(dir); it != end; ++it)
1292+ {
1293+ remove_all(it->path());
1294+ }
1295+ }
1296+ catch (...)
1297+ {
1298+ }
1299+}
1300+
1301+int get_perms(string const& path)
1302+{
1303+ struct stat st;
1304+ if (stat(path.c_str(), &st) != 0)
1305+ {
1306+ string msg = "get_perms(): cannot stat " + path + ": errno = " + to_string(errno);
1307+ throw runtime_error(msg);
1308+ }
1309+ return st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
1310+}
1311+
1312+TEST(Utils, make_directories)
1313+{
1314+ using namespace std;
1315+
1316+ string test_root = TEST_DIR "/dir";
1317+
1318+ empty_dir(test_root);
1319+ rmdir(test_root.c_str()); // First time around, make sure that test_root doesn't exist.
1320+
1321+ make_directories(test_root, 0700);
1322+ EXPECT_EQ(0700, get_perms(test_root));
1323+
1324+ // Do it a second time, to test that it works whether test_root exists or not.
1325+ make_directories(test_root, 0700);
1326+ EXPECT_EQ(0700, get_perms(test_root));
1327+
1328+ // Change permissions on test_root and make a dir underneath it.
1329+ chmod(test_root.c_str(), 0777);
1330+ string child = test_root + "/child";
1331+ int old_umask = umask(0000);
1332+ make_directories(child, 0555);
1333+ umask(old_umask);
1334+
1335+ // test_root permissions must still be the same, child must have perm 0555.
1336+ EXPECT_EQ(0777, get_perms(test_root));
1337+ EXPECT_EQ(0555, get_perms(child));
1338+
1339+ // Remove permission from child dir.
1340+ chmod(child.c_str(), 0000);
1341+
1342+ // Check that we fail silently trying to create the grandchild.
1343+ string grandchild = child + "/grandchild";
1344+ make_directories(grandchild, 0555);
1345+
1346+ chmod(child.c_str(), 0777); // Don't leave the dir behind without permissions.
1347+}
1348
1349=== modified file 'unity-scopes.map'
1350--- unity-scopes.map 2014-12-01 14:12:24 +0000
1351+++ unity-scopes.map 2015-04-23 15:43:14 +0000
1352@@ -16,6 +16,7 @@
1353 unity::scopes::internal::Executor::*;
1354 unity::scopes::internal::IniSettingsSchema::*;
1355 unity::scopes::internal::JsonSettingsSchema::*;
1356+ unity::scopes::internal::make_directories*;
1357 unity::scopes::internal::MiddlewareBase::*;
1358 unity::scopes::internal::MiddlewareFactory::*;
1359 unity::scopes::internal::RegistryConfig::*;

Subscribers

People subscribed via source and target branches

to all changes: