Merge lp:~marcustomlinson/unity-scopes-api/loop-prevention into lp:unity-scopes-api

Proposed by Marcus Tomlinson
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 290
Merged at revision: 291
Proposed branch: lp:~marcustomlinson/unity-scopes-api/loop-prevention
Merge into: lp:unity-scopes-api
Prerequisite: lp:~marcustomlinson/unity-scopes-api/child_scopes_ordered
Diff against target: 1572 lines (+813/-162)
39 files modified
CONFIGFILES (+1/-1)
RELEASE_NOTES.md (+2/-0)
STRUCTS (+4/-4)
debian/changelog (+12/-1)
debian/libunity-scopes3.symbols (+6/-4)
include/unity/scopes/QueryBase.h (+2/-2)
include/unity/scopes/QueryMetadata.h (+3/-0)
include/unity/scopes/SearchMetadata.h (+2/-1)
include/unity/scopes/SearchQueryBase.h (+4/-4)
include/unity/scopes/internal/MWScope.h (+20/-4)
include/unity/scopes/internal/ScopeImpl.h (+15/-4)
include/unity/scopes/internal/ScopeObject.h (+7/-6)
include/unity/scopes/internal/ScopeObjectBase.h (+1/-0)
include/unity/scopes/internal/SearchQueryBaseImpl.h (+16/-15)
include/unity/scopes/internal/smartscopes/SSScopeObject.h (+1/-0)
include/unity/scopes/internal/zmq_middleware/ZmqScope.h (+3/-2)
include/unity/scopes/utility/internal/BufferedResultForwarderImpl.h (+2/-2)
include/unity/scopes/utility/internal/BufferedSearchReplyImpl.h (+1/-1)
src/scopes/SearchQueryBase.cpp (+3/-3)
src/scopes/internal/QueryCtrlImpl.cpp (+8/-6)
src/scopes/internal/ScopeImpl.cpp (+45/-9)
src/scopes/internal/ScopeObject.cpp (+34/-1)
src/scopes/internal/SearchMetadataImpl.cpp (+1/-1)
src/scopes/internal/SearchQueryBaseImpl.cpp (+90/-73)
src/scopes/internal/smartscopes/SSScopeObject.cpp (+4/-3)
src/scopes/internal/zmq_middleware/ScopeI.cpp (+2/-0)
src/scopes/internal/zmq_middleware/ZmqScope.cpp (+6/-1)
src/scopes/internal/zmq_middleware/capnproto/Scope.capnp (+2/-12)
test/gtest/scopes/CMakeLists.txt (+1/-0)
test/gtest/scopes/LoopDetection/CMakeLists.txt (+26/-0)
test/gtest/scopes/LoopDetection/LoopDetection_test.cpp (+249/-0)
test/gtest/scopes/LoopDetection/LoopScope.cpp (+177/-0)
test/gtest/scopes/LoopDetection/LoopScope.h (+41/-0)
test/gtest/scopes/LoopDetection/LoopScope.ini.in (+4/-0)
test/gtest/scopes/LoopDetection/Runtime.ini.in (+6/-0)
test/gtest/scopes/LoopDetection/TestRegistry.ini.in (+7/-0)
test/gtest/scopes/LoopDetection/Zmq.ini.in (+2/-0)
test/gtest/scopes/internal/zmq_middleware/ZmqMiddleware/ZmqMiddleware_test.cpp (+1/-0)
test/gtest/scopes/testing/IsolatedScope/IsolatedScope_test.cpp (+2/-2)
To merge this branch: bzr merge lp:~marcustomlinson/unity-scopes-api/loop-prevention
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Michi Henning (community) Needs Fixing
Review via email: mp+247439@code.launchpad.net

Commit message

Added loop detection for subsearch(). If an aggregation loop is detected, the current subquery immediately finishes with a status of OK, but a message saying that a loop was found. This returns an empty results set for the subquery() that detected the loop to the client, while leaving other subqueries alone.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Minor glitches:

+ [ Michi Henning ]
+ * Added utility subdir to header tests.
+ * Bunch of miscellaneous fixes:
+
+ [ James Henstridge ]

Looks like some text has gone missing here. It should be:

* Bunch of miscellaneous fixes:

- Added warning for non-virtual destructors.
- Warnings will disappear once Marcus's child_scopes branch is merged.
- Tidied up the way compiler flags are set in CMakeLists.txt for clarity.
- Added missing dependencies to demos, so we can't run with an out-of-date registry or scoperunner.
- Fixed a number of warnings when building the developer doc.
- Minor doc fixes.
- Minor stylistic code fixes.

My fault, typo in CONFIGFILES:

The value must be a semicolon-separate list of channel names.

Should be "semicolon-separated". When you fix the changelog could you throw that in too for me please?

Other than that, looks good, tests pass for me here, and looking through the changes doesn't show anything unusual.

- Added missing tmp_dir_initialized_ initializer to ScopeBaseImpl.
- Removed redundant lock in QueryCtrlImpl constructor.

review: Needs Fixing
Revision history for this message
Michi Henning (michihenning) wrote :

Oops, made an editing mistake earlier. Here is the correct changelog text:

* Bunch of miscellaneous fixes:

- Added warning for non-virtual destructors.
- Warnings will disappear once Marcus's child_scopes branch is merged.
- Tidied up the way compiler flags are set in CMakeLists.txt for clarity.
- Added missing dependencies to demos, so we can't run with an out-of-date registry or scoperunner.
- Fixed a number of warnings when building the developer doc.
- Minor doc fixes.
- Minor stylistic code fixes.
- Added missing tmp_dir_initialized_ initializer to ScopeBaseImpl.
- Removed redundant lock in QueryCtrlImpl constructor.

And, in CONFIGFILES, change "colon-seperate" to "colon-separated".

Apologies for the noise…

Revision history for this message
Marcus Tomlinson (marcustomlinson) wrote :

> Oops, made an editing mistake earlier. Here is the correct changelog text:
>
> * Bunch of miscellaneous fixes:
>
> - Added warning for non-virtual destructors.
> - Warnings will disappear once Marcus's child_scopes branch is merged.
> - Tidied up the way compiler flags are set in CMakeLists.txt for clarity.
> - Added missing dependencies to demos, so we can't run with an out-of-date
> registry or scoperunner.
> - Fixed a number of warnings when building the developer doc.
> - Minor doc fixes.
> - Minor stylistic code fixes.
> - Added missing tmp_dir_initialized_ initializer to ScopeBaseImpl.
> - Removed redundant lock in QueryCtrlImpl constructor.
>
> And, in CONFIGFILES, change "colon-seperate" to "colon-separated".
>
> Apologies for the noise…

done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
290. By Marcus Tomlinson

Merged child_scopes_ordered

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CONFIGFILES'
2--- CONFIGFILES 2015-01-16 02:59:43 +0000
3+++ CONFIGFILES 2015-01-26 17:28:26 +0000
4@@ -167,7 +167,7 @@
5 Trace inter-process messages activity.
6
7 The environment variable UNITY_SCOPES_LOG_TRACECHANNELS overrides this key.
8- The value must be a semicolon-separate list of channel names.
9+ The value must be a semicolon-separated list of channel names.
10
11
12 Zmq.ini
13
14=== modified file 'RELEASE_NOTES.md'
15--- RELEASE_NOTES.md 2015-01-26 17:28:26 +0000
16+++ RELEASE_NOTES.md 2015-01-26 17:28:26 +0000
17@@ -7,6 +7,8 @@
18 list of child scopes at runtime.
19 - Added missing virtual destructor to AbstractScopeBase. (LP: #1360266)
20 - Removed deprecated Runtime::run_scope() method.
21+ - Prevent query from looping indefinitely if a query is forwarded
22+ among aggregators and loops back to an earlier aggregator.
23
24 Changes in version 0.6.11
25 =========================
26
27=== modified file 'STRUCTS'
28--- STRUCTS 2014-12-03 08:05:20 +0000
29+++ STRUCTS 2015-01-26 17:28:26 +0000
30@@ -161,10 +161,10 @@
31
32 QueryMetadata (returned by serialize())
33 =======================================
34- 'type' : string (metadata type)
35- 'locale' : string
36- 'form_factor' : string
37- 'hints' : dict
38+ 'type' : string (metadata type)
39+ 'locale' : string
40+ 'form_factor' : string
41+ 'hints' : dict
42 'internet_connectivity' : bool (optional)
43
44 SearchMetadata (returned by serialize())
45
46=== modified file 'debian/changelog'
47--- debian/changelog 2015-01-26 17:28:26 +0000
48+++ debian/changelog 2015-01-26 17:28:26 +0000
49@@ -4,8 +4,10 @@
50 list of child scopes at runtime.
51 * Added missing virtual destructor to AbstractScopeBase. (LP: #1360266)
52 * Removed deprecated Runtime::run_scope() method.
53+ * Prevent query from looping indefinitely if a query is forwarded
54+ among aggregators and loops back to an earlier aggregator.
55
56- -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Tue, 20 Jan 2015 07:52:23 +0200
57+ -- Marcus Tomlinson <marcus.tomlinson@canonical.com> Mon, 26 Jan 2015 10:25:49 +0200
58
59 unity-scopes-api (0.6.11+15.04.20150126-0ubuntu1) vivid; urgency=low
60
61@@ -32,6 +34,15 @@
62 [ Michi Henning ]
63 * Added utility subdir to header tests.
64 * Bunch of miscellaneous fixes:
65+ - Added warning for non-virtual destructors.
66+ - Warnings will disappear once Marcus's child_scopes branch is merged.
67+ - Tidied up the way compiler flags are set in CMakeLists.txt for clarity.
68+ - Added missing dependencies to demos, so we can't run with an out-of-date registry or scoperunner.
69+ - Fixed a number of warnings when building the developer doc.
70+ - Minor doc fixes.
71+ - Minor stylistic code fixes.
72+ - Added missing tmp_dir_initialized_ initializer to ScopeBaseImpl.
73+ - Removed redundant lock in QueryCtrlImpl constructor.
74
75 [ James Henstridge ]
76 * Expose the deserialize() static method on FilterBase and FilterState
77
78=== modified file 'debian/libunity-scopes3.symbols'
79--- debian/libunity-scopes3.symbols 2015-01-26 17:28:26 +0000
80+++ debian/libunity-scopes3.symbols 2015-01-26 17:28:26 +0000
81@@ -478,12 +478,14 @@
82 (c++)"unity::scopes::internal::ScopeImpl::fwd()@Base" 0.4.3+14.10.20140428
83 (c++)"unity::scopes::internal::ScopeImpl::perform_action(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::shared_ptr<unity::scopes::ActivationListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
84 (c++)"unity::scopes::internal::ScopeImpl::preview(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::shared_ptr<unity::scopes::PreviewListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
85+ (c++)"unity::scopes::internal::ScopeImpl::runtime() const@Base" 0replaceme
86 (c++)"unity::scopes::internal::ScopeImpl::~ScopeImpl()@Base" 0.4.0+14.04.20140312.1
87 (c++)"unity::scopes::internal::ScopeImpl::ScopeImpl(std::shared_ptr<unity::scopes::internal::MWScope> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.6.11+15.04.20150119
88 (c++)"unity::scopes::internal::ScopeImpl::search(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::FilterState const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
89+ (c++)"unity::scopes::internal::ScopeImpl::search(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::FilterState const&, unity::scopes::SearchMetadata const&, std::vector<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0replaceme
90 (c++)"unity::scopes::internal::ScopeImpl::search(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::FilterState const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
91 (c++)"unity::scopes::internal::ScopeImpl::search(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
92- (c++)"unity::scopes::internal::ScopeImpl::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0.4.0+14.04.20140312.1
93+ (c++)"unity::scopes::internal::ScopeImpl::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::vector<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > const&, std::shared_ptr<unity::scopes::SearchListenerBase> const&)@Base" 0replaceme
94 (c++)"unity::scopes::internal::ScopeImpl::set_child_scopes_ordered(std::list<unity::scopes::ChildScope, std::allocator<unity::scopes::ChildScope> > const&)@Base" 0replaceme
95 (c++)"unity::scopes::internal::ScopeLoader::libpath() const@Base" 0.4.0+14.04.20140312.1
96 (c++)"unity::scopes::internal::ScopeLoader::load(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.6.0+14.10.20140804.1
97@@ -546,8 +548,8 @@
98 (c++)"unity::scopes::internal::ScopeObject::preview(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
99 (c++)"unity::scopes::internal::ScopeObject::query(std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::MiddlewareBase*, std::function<std::shared_ptr<unity::scopes::QueryBase> ()> const&, std::function<std::shared_ptr<unity::scopes::internal::QueryObjectBase> (std::shared_ptr<unity::scopes::QueryBase>, std::shared_ptr<unity::scopes::internal::MWQueryCtrl>)> const&)@Base" 0.4.0+14.04.20140312.1
100 (c++)"unity::scopes::internal::ScopeObject::~ScopeObject()@Base" 0.4.0+14.04.20140312.1
101- (c++)"unity::scopes::internal::ScopeObject::ScopeObject(unity::scopes::ScopeBase*, bool)@Base" 0.6.11+15.04.20150119
102- (c++)"unity::scopes::internal::ScopeObject::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
103+ (c++)"unity::scopes::internal::ScopeObject::ScopeObject(unity::scopes::ScopeBase*, bool)@Base" 0replaceme
104+ (c++)"unity::scopes::internal::ScopeObject::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unity::scopes::Variant, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unity::scopes::Variant> > > const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0replaceme
105 (c++)"unity::scopes::internal::ScopeObject::set_child_scopes_ordered(std::list<unity::scopes::ChildScope, std::allocator<unity::scopes::ChildScope> > const&)@Base" 0replaceme
106 (c++)"unity::scopes::internal::smartscopes::SSConfig::http_reply_timeout() const@Base" 0.4.4+14.10.20140508
107 (c++)"unity::scopes::internal::smartscopes::SSConfig::reg_refresh_fail_timeout() const@Base" 0.4.4+14.10.20140508
108@@ -575,7 +577,7 @@
109 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::perform_action(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
110 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::preview(unity::scopes::Result const&, unity::scopes::ActionMetadata const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
111 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::query(unity::scopes::internal::InvokeInfo const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, std::function<std::shared_ptr<unity::scopes::QueryBase> ()> const&, std::function<void (std::shared_ptr<unity::scopes::QueryBase>)> const&)@Base" 0.4.0+14.04.20140312.1
112- (c++)"unity::scopes::internal::smartscopes::SSScopeObject::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0.4.0+14.04.20140312.1
113+ (c++)"unity::scopes::internal::smartscopes::SSScopeObject::search(unity::scopes::CannedQuery const&, unity::scopes::SearchMetadata const&, std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, unity::scopes::Variant, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, unity::scopes::Variant> > > const&, std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::InvokeInfo const&)@Base" 0replaceme
114 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::set_child_scopes_ordered(std::list<unity::scopes::ChildScope, std::allocator<unity::scopes::ChildScope> > const&)@Base" 0replaceme
115 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::~SSScopeObject()@Base" 0.4.0+14.04.20140312.1
116 (c++)"unity::scopes::internal::smartscopes::SSScopeObject::SSScopeObject(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::shared_ptr<unity::scopes::internal::MiddlewareBase>, std::shared_ptr<unity::scopes::internal::smartscopes::SSRegistryObject>)@Base" 0.4.0+14.04.20140312.1
117
118=== modified file 'include/unity/scopes/QueryBase.h'
119--- include/unity/scopes/QueryBase.h 2014-11-03 05:31:30 +0000
120+++ include/unity/scopes/QueryBase.h 2015-01-26 17:28:26 +0000
121@@ -123,8 +123,8 @@
122 private:
123 friend class internal::QueryObject; // So QueryObject can call cancel()
124 friend class internal::smartscopes::SSQueryObject; // So SSQueryObject can call cancel()
125- friend class internal::ScopeObject; // So ScopeObject can call set_metadata() and set_department_id()
126- friend class internal::smartscopes::SSScopeObject; // So SSQueryObject can call set_department_id()
127+ friend class internal::ScopeObject; // So ScopeObject can call set_department_id()
128+ friend class internal::smartscopes::SSScopeObject; // So SSScopeObject can call set_department_id()
129 };
130
131 } // namespace scopes
132
133=== modified file 'include/unity/scopes/QueryMetadata.h'
134--- include/unity/scopes/QueryMetadata.h 2014-11-03 05:31:30 +0000
135+++ include/unity/scopes/QueryMetadata.h 2015-01-26 17:28:26 +0000
136@@ -31,6 +31,7 @@
137 {
138
139 class QueryMetadataImpl;
140+class SearchQueryBaseImpl;
141
142 }
143
144@@ -96,6 +97,8 @@
145
146 std::unique_ptr<internal::QueryMetadataImpl> p;
147 QueryMetadata(internal::QueryMetadataImpl* impl);
148+
149+ friend class internal::SearchQueryBaseImpl; // Allow access for loop detection
150 /// @endcond
151 };
152
153
154=== modified file 'include/unity/scopes/SearchMetadata.h'
155--- include/unity/scopes/SearchMetadata.h 2014-11-03 05:31:30 +0000
156+++ include/unity/scopes/SearchMetadata.h 2015-01-26 17:28:26 +0000
157@@ -31,8 +31,9 @@
158
159 namespace internal
160 {
161+
162 class SearchMetadataImpl;
163-class ScopeObject;
164+
165 }
166
167 /**
168
169=== modified file 'include/unity/scopes/SearchQueryBase.h'
170--- include/unity/scopes/SearchQueryBase.h 2014-11-03 05:31:30 +0000
171+++ include/unity/scopes/SearchQueryBase.h 2015-01-26 17:28:26 +0000
172@@ -149,10 +149,10 @@
173 void set_department_id(std::string const& department_id);
174 std::string department_id() const;
175
176- friend class internal::QueryObject; // So QueryObject can call cancel() and set_department_id()
177- friend class internal::ScopeObject; // So ScopeObject can call set_metadata() and set_department_id()
178- friend class internal::smartscopes::SSQueryObject; // So SSQueryObject can call cancel()
179- friend class internal::smartscopes::SSScopeObject; // So SSQueryObject can call set_department_id()
180+ friend class internal::QueryObject; // So QueryObject can call cancel() and set_department_id()
181+ friend class internal::ScopeObject; // So ScopeObject can set department and query context
182+ friend class internal::smartscopes::SSQueryObject; // So SSQueryObject can call cancel()
183+ friend class internal::smartscopes::SSScopeObject; // So SSScopeObject can call set_department_id()
184 };
185
186 } // namespace scopes
187
188=== modified file 'include/unity/scopes/internal/MWScope.h'
189--- include/unity/scopes/internal/MWScope.h 2015-01-26 17:28:26 +0000
190+++ include/unity/scopes/internal/MWScope.h 2015-01-26 17:28:26 +0000
191@@ -40,12 +40,28 @@
192 public:
193 virtual ~MWScope();
194
195- virtual QueryCtrlProxy search(CannedQuery const& query, VariantMap const& hints, MWReplyProxy const& reply) = 0;
196- virtual QueryCtrlProxy activate(VariantMap const& result, VariantMap const& hints, MWReplyProxy const& reply) = 0;
197- virtual QueryCtrlProxy perform_action(VariantMap const& result, VariantMap const& hints, std::string const& widget_id, std::string const& action_id, MWReplyProxy const& reply) = 0;
198- virtual QueryCtrlProxy preview(VariantMap const& result, VariantMap const& hints, MWReplyProxy const& reply) = 0;
199+ virtual QueryCtrlProxy search(CannedQuery const& query,
200+ VariantMap const& hints,
201+ VariantMap const& details,
202+ MWReplyProxy const& reply) = 0;
203+
204+ virtual QueryCtrlProxy activate(VariantMap const& result,
205+ VariantMap const& hints,
206+ MWReplyProxy const& reply) = 0;
207+
208+ virtual QueryCtrlProxy perform_action(VariantMap const& result,
209+ VariantMap const& hints,
210+ std::string const& widget_id,
211+ std::string const& action_id,
212+ MWReplyProxy const& reply) = 0;
213+
214+ virtual QueryCtrlProxy preview(VariantMap const& result,
215+ VariantMap const& hints,
216+ MWReplyProxy const& reply) = 0;
217+
218 virtual ChildScopeList child_scopes_ordered() = 0;
219 virtual bool set_child_scopes_ordered(ChildScopeList const& child_scopes_ordered) = 0;
220+
221 virtual bool debug_mode() = 0;
222
223 protected:
224
225=== modified file 'include/unity/scopes/internal/ScopeImpl.h'
226--- include/unity/scopes/internal/ScopeImpl.h 2015-01-26 17:28:26 +0000
227+++ include/unity/scopes/internal/ScopeImpl.h 2015-01-26 17:28:26 +0000
228@@ -18,13 +18,14 @@
229
230 #pragma once
231
232+#include <unity/scopes/ActivationListenerBase.h>
233 #include <unity/scopes/internal/MWScopeProxyFwd.h>
234 #include <unity/scopes/internal/ObjectImpl.h>
235+#include <unity/scopes/internal/SearchQueryBaseImpl.h>
236+#include <unity/scopes/PreviewListenerBase.h>
237 #include <unity/scopes/QueryCtrlProxyFwd.h>
238+#include <unity/scopes/Scope.h>
239 #include <unity/scopes/SearchListenerBase.h>
240-#include <unity/scopes/ActivationListenerBase.h>
241-#include <unity/scopes/PreviewListenerBase.h>
242-#include <unity/scopes/Scope.h>
243
244 namespace unity
245 {
246@@ -48,6 +49,8 @@
247 ScopeImpl(MWScopeProxy const& mw_proxy, std::string const& scope_id);
248 virtual ~ScopeImpl();
249
250+ RuntimeImpl* runtime() const;
251+
252 virtual QueryCtrlProxy search(std::string const& q,
253 SearchMetadata const& metadata,
254 SearchListenerBase::SPtr const& reply) override;
255@@ -63,9 +66,17 @@
256 SearchMetadata const& metadata,
257 SearchListenerBase::SPtr const& reply) override;
258
259+ QueryCtrlProxy search(std::string const& query_string,
260+ std::string const& department_id,
261+ FilterState const& filter_state,
262+ SearchMetadata const& metadata,
263+ SearchQueryBaseImpl::History const& history,
264+ SearchListenerBase::SPtr const& reply); // Not remote, hence not override
265+
266 QueryCtrlProxy search(CannedQuery const& query,
267 SearchMetadata const& metadata,
268- SearchListenerBase::SPtr const& reply); // Not remote, hence not override
269+ SearchQueryBaseImpl::History const& history,
270+ SearchListenerBase::SPtr const& reply); // Not remote, hence not override
271
272 virtual QueryCtrlProxy activate(Result const& result,
273 ActionMetadata const& metadata,
274
275=== modified file 'include/unity/scopes/internal/ScopeObject.h'
276--- include/unity/scopes/internal/ScopeObject.h 2015-01-26 17:28:26 +0000
277+++ include/unity/scopes/internal/ScopeObject.h 2015-01-26 17:28:26 +0000
278@@ -55,14 +55,15 @@
279
280 // Remote operation implementations
281 virtual MWQueryCtrlProxy search(CannedQuery const& q,
282- SearchMetadata const& hints,
283- MWReplyProxy const& reply,
284- InvokeInfo const& info) override;
285+ SearchMetadata const& hints,
286+ VariantMap const& context,
287+ MWReplyProxy const& reply,
288+ InvokeInfo const& info) override;
289
290 virtual MWQueryCtrlProxy activate(Result const& result,
291- ActionMetadata const& hints,
292- MWReplyProxy const &reply,
293- InvokeInfo const& info) override;
294+ ActionMetadata const& hints,
295+ MWReplyProxy const &reply,
296+ InvokeInfo const& info) override;
297
298 virtual MWQueryCtrlProxy perform_action(Result const& result,
299 ActionMetadata const& hints,
300
301=== modified file 'include/unity/scopes/internal/ScopeObjectBase.h'
302--- include/unity/scopes/internal/ScopeObjectBase.h 2015-01-26 17:28:26 +0000
303+++ include/unity/scopes/internal/ScopeObjectBase.h 2015-01-26 17:28:26 +0000
304@@ -48,6 +48,7 @@
305
306 virtual MWQueryCtrlProxy search(CannedQuery const& query,
307 SearchMetadata const& hints,
308+ VariantMap const& context,
309 MWReplyProxy const& reply,
310 InvokeInfo const& info) = 0;
311
312
313=== modified file 'include/unity/scopes/internal/SearchQueryBaseImpl.h'
314--- include/unity/scopes/internal/SearchQueryBaseImpl.h 2014-11-03 05:31:30 +0000
315+++ include/unity/scopes/internal/SearchQueryBaseImpl.h 2015-01-26 17:28:26 +0000
316@@ -41,21 +41,17 @@
317 SearchQueryBaseImpl(CannedQuery const& query, SearchMetadata const& metadata);
318 CannedQuery query() const;
319 SearchMetadata search_metadata() const;
320+
321 void set_department_id(std::string const& department_id);
322 std::string department_id() const;
323
324- QueryCtrlProxy subsearch(ScopeProxy const& scope,
325- std::string const& query_string,
326- SearchListenerBase::SPtr const& reply);
327- QueryCtrlProxy subsearch(ScopeProxy const& scope,
328- std::string const& query_string,
329- FilterState const& filter_state,
330- SearchListenerBase::SPtr const& reply);
331- QueryCtrlProxy subsearch(ScopeProxy const& scope,
332- std::string const& query_string,
333- std::string const& department_id,
334- FilterState const& filter_state,
335- SearchListenerBase::SPtr const& reply);
336+ void set_client_id(const std::string& id);
337+
338+ typedef std::tuple<std::string, std::string, std::string> HistoryData;
339+ typedef std::vector<HistoryData> History;
340+
341+ void set_history(History const& h);
342+
343 QueryCtrlProxy subsearch(ScopeProxy const& scope,
344 std::string const& query_string,
345 std::string const& department_id,
346@@ -67,12 +63,17 @@
347 bool valid() const override;
348
349 private:
350+ CannedQuery const canned_query_;
351+ SearchMetadata const search_metadata_;
352+
353+ mutable std::mutex mutex_;
354 bool valid_;
355- mutable std::mutex mutex_;
356- const CannedQuery canned_query_;
357- const SearchMetadata search_metadata_;
358 std::string department_id_;
359+ std::string client_id_;
360+ History history_;
361 std::vector<QueryCtrlProxy> subqueries_;
362+
363+ QueryCtrlProxy check_for_query_loop(ScopeProxy const& scope, SearchListenerBase::SPtr const& reply);
364 };
365
366 } // namespace internal
367
368=== modified file 'include/unity/scopes/internal/smartscopes/SSScopeObject.h'
369--- include/unity/scopes/internal/smartscopes/SSScopeObject.h 2015-01-26 17:28:26 +0000
370+++ include/unity/scopes/internal/smartscopes/SSScopeObject.h 2015-01-26 17:28:26 +0000
371@@ -53,6 +53,7 @@
372 // Remote operation implementations
373 MWQueryCtrlProxy search(CannedQuery const& q,
374 SearchMetadata const& hints,
375+ VariantMap const& context,
376 MWReplyProxy const& reply,
377 InvokeInfo const& info) override;
378
379
380=== modified file 'include/unity/scopes/internal/zmq_middleware/ZmqScope.h'
381--- include/unity/scopes/internal/zmq_middleware/ZmqScope.h 2015-01-26 17:28:26 +0000
382+++ include/unity/scopes/internal/zmq_middleware/ZmqScope.h 2015-01-26 17:28:26 +0000
383@@ -45,8 +45,9 @@
384 virtual ~ZmqScope();
385
386 virtual QueryCtrlProxy search(CannedQuery const& query,
387- VariantMap const& hints,
388- MWReplyProxy const& reply) override;
389+ VariantMap const& hints,
390+ VariantMap const& context,
391+ MWReplyProxy const& reply) override;
392
393 virtual QueryCtrlProxy activate(VariantMap const& result,
394 VariantMap const& hints,
395
396=== modified file 'include/unity/scopes/utility/internal/BufferedResultForwarderImpl.h'
397--- include/unity/scopes/utility/internal/BufferedResultForwarderImpl.h 2014-12-01 09:47:04 +0000
398+++ include/unity/scopes/utility/internal/BufferedResultForwarderImpl.h 2015-01-26 17:28:26 +0000
399@@ -55,8 +55,8 @@
400 std::atomic<bool> ready_;
401 std::atomic<bool> has_previous_;
402 std::atomic<bool> previous_ready_;
403- unity::scopes::SearchReplyProxy const upstream_; // Immutable
404- BufferedResultForwarder::SPtr next_; // Immutable
405+ unity::scopes::SearchReplyProxy const upstream_;
406+ BufferedResultForwarder::SPtr const next_;
407 };
408
409 } // namespace internal
410
411=== modified file 'include/unity/scopes/utility/internal/BufferedSearchReplyImpl.h'
412--- include/unity/scopes/utility/internal/BufferedSearchReplyImpl.h 2014-12-01 01:51:38 +0000
413+++ include/unity/scopes/utility/internal/BufferedSearchReplyImpl.h 2015-01-26 17:28:26 +0000
414@@ -78,7 +78,7 @@
415
416 private:
417 std::mutex mutex_;
418- unity::scopes::SearchReplyProxy upstream_; // Immutable
419+ unity::scopes::SearchReplyProxy const upstream_;
420 std::atomic<bool> buffer_;
421 std::vector<CategorisedResult> results_;
422 };
423
424=== modified file 'src/scopes/SearchQueryBase.cpp'
425--- src/scopes/SearchQueryBase.cpp 2014-06-12 16:16:01 +0000
426+++ src/scopes/SearchQueryBase.cpp 2015-01-26 17:28:26 +0000
427@@ -61,7 +61,7 @@
428 string const& query_string,
429 SearchListenerBase::SPtr const& reply)
430 {
431- return fwd()->subsearch(scope, query_string, reply);
432+ return subsearch(scope, query_string, "", FilterState(), fwd()->search_metadata(), reply);
433 }
434
435 QueryCtrlProxy SearchQueryBase::subsearch(ScopeProxy const& scope,
436@@ -69,7 +69,7 @@
437 FilterState const& filter_state,
438 SearchListenerBase::SPtr const& reply)
439 {
440- return fwd()->subsearch(scope, query_string, filter_state, reply);
441+ return subsearch(scope, query_string, "", filter_state, fwd()->search_metadata(), reply);
442 }
443
444 QueryCtrlProxy SearchQueryBase::subsearch(ScopeProxy const& scope,
445@@ -78,7 +78,7 @@
446 FilterState const& filter_state,
447 SearchListenerBase::SPtr const& reply)
448 {
449- return fwd()->subsearch(scope, query_string, department_id, filter_state, reply);
450+ return subsearch(scope, query_string, department_id, filter_state, fwd()->search_metadata(), reply);
451 }
452
453 QueryCtrlProxy SearchQueryBase::subsearch(ScopeProxy const& scope,
454
455=== modified file 'src/scopes/internal/QueryCtrlImpl.cpp'
456--- src/scopes/internal/QueryCtrlImpl.cpp 2015-01-20 04:14:26 +0000
457+++ src/scopes/internal/QueryCtrlImpl.cpp 2015-01-26 17:28:26 +0000
458@@ -45,8 +45,6 @@
459 {
460 // We remember the reply proxy so, when the query is cancelled, we can
461 // inform the reply object belonging to this query that the query is finished.
462- assert(reply_proxy);
463-
464 ready_ = ctrl_proxy != nullptr;
465 cancelled_ = false;
466 }
467@@ -87,12 +85,16 @@
468 void QueryCtrlImpl::set_proxy(MWQueryCtrlProxy const& p)
469 {
470 assert(proxy() == nullptr);
471- ObjectImpl::set_proxy(p);
472-
473- bool need_cancel;
474-
475+
476+ bool need_cancel = false;
477 {
478 lock_guard<mutex> lock(mutex_);
479+
480+ if (!reply_proxy_)
481+ {
482+ return;
483+ }
484+ ObjectImpl::set_proxy(p);
485 ready_ = true;
486 need_cancel = cancelled_;
487 } // Unlock
488
489=== modified file 'src/scopes/internal/ScopeImpl.cpp'
490--- src/scopes/internal/ScopeImpl.cpp 2015-01-26 17:28:26 +0000
491+++ src/scopes/internal/ScopeImpl.cpp 2015-01-26 17:28:26 +0000
492@@ -55,6 +55,11 @@
493 {
494 }
495
496+RuntimeImpl* ScopeImpl::runtime() const
497+{
498+ return runtime_;
499+}
500+
501 QueryCtrlProxy ScopeImpl::search(std::string const& query_string,
502 std::string const& department_id,
503 FilterState const& filter_state,
504@@ -63,7 +68,7 @@
505 {
506 CannedQuery query(scope_id_, query_string, department_id);
507 query.set_filter_state(filter_state);
508- return search(query, metadata, reply);
509+ return search(query, metadata, SearchQueryBaseImpl::History(), reply);
510 }
511
512 QueryCtrlProxy ScopeImpl::search(std::string const& query_string,
513@@ -71,23 +76,34 @@
514 SearchMetadata const& metadata,
515 SearchListenerBase::SPtr const& reply)
516 {
517- CannedQuery query(scope_id_);
518- query.set_query_string(query_string);
519+ CannedQuery query(scope_id_, query_string, "");
520 query.set_filter_state(filter_state);
521- return search(query, metadata, reply);
522+ return search(query, metadata, SearchQueryBaseImpl::History(), reply);
523 }
524
525 QueryCtrlProxy ScopeImpl::search(string const& query_string,
526 SearchMetadata const& metadata,
527 SearchListenerBase::SPtr const& reply)
528 {
529- CannedQuery query(scope_id_);
530- query.set_query_string(query_string);
531- return search(query, metadata, reply);
532+ CannedQuery query(scope_id_, query_string, "");
533+ return search(query, metadata, SearchQueryBaseImpl::History(), reply);
534+}
535+
536+QueryCtrlProxy ScopeImpl::search(std::string const& query_string,
537+ std::string const& department_id,
538+ FilterState const& filter_state,
539+ SearchMetadata const& metadata,
540+ SearchQueryBaseImpl::History const& history,
541+ SearchListenerBase::SPtr const& reply)
542+{
543+ CannedQuery query(scope_id_, query_string, department_id);
544+ query.set_filter_state(filter_state);
545+ return search(query, metadata, history, reply);
546 }
547
548 QueryCtrlProxy ScopeImpl::search(CannedQuery const& query,
549 SearchMetadata const& metadata,
550+ SearchQueryBaseImpl::History const& history,
551 SearchListenerBase::SPtr const& reply)
552 {
553 if (reply == nullptr)
554@@ -107,12 +123,32 @@
555 // sent in the new thread, the lambda will call into this by-now-destroyed instance.
556 auto impl = dynamic_pointer_cast<ScopeImpl>(shared_from_this());
557
558- auto send_search = [impl, query, metadata, rp, ro, ctrl]() -> void
559+ string const my_id = runtime_->scope_id();
560+ auto send_search = [my_id, impl, query, metadata, history, rp, ro, ctrl]() -> void
561 {
562 try
563 {
564+ // Create query context with our own ID and the query history for loop detection.
565+ VariantMap context;
566+ context["client_id"] = my_id;
567+
568+ VariantArray hist;
569+ for (auto const& tuple : history)
570+ {
571+ VariantMap d;
572+ d["c"] = get<0>(tuple); // Client
573+ d["a"] = get<1>(tuple); // Aggregator
574+ d["r"] = get<2>(tuple); // Receiver
575+ hist.push_back(Variant(d));
576+ }
577+ context["history"] = Variant(hist);
578+
579 // Forward the (synchronous) search() method across the bus.
580- auto real_ctrl = dynamic_pointer_cast<QueryCtrlImpl>(impl->fwd()->search(query, metadata.serialize(), rp));
581+ auto real_ctrl = dynamic_pointer_cast<QueryCtrlImpl>(impl->fwd()->search(query,
582+ metadata.serialize(),
583+ context,
584+ rp));
585+
586 assert(real_ctrl);
587
588 // Call has completed now, so we update the MWQueryCtrlProxy for the fake proxy
589
590=== modified file 'src/scopes/internal/ScopeObject.cpp'
591--- src/scopes/internal/ScopeObject.cpp 2015-01-26 17:28:26 +0000
592+++ src/scopes/internal/ScopeObject.cpp 2015-01-26 17:28:26 +0000
593@@ -27,6 +27,8 @@
594 #include <unity/scopes/internal/QueryObject.h>
595 #include <unity/scopes/internal/RuntimeImpl.h>
596 #include <unity/scopes/internal/ScopeBaseImpl.h>
597+#include <unity/scopes/internal/SearchMetadataImpl.h>
598+#include <unity/scopes/internal/SearchQueryBaseImpl.h>
599 #include <unity/scopes/ScopeBase.h>
600 #include <unity/UnityExceptions.h>
601
602@@ -145,13 +147,44 @@
603
604 MWQueryCtrlProxy ScopeObject::search(CannedQuery const& q,
605 SearchMetadata const& hints,
606+ VariantMap const& context,
607 MWReplyProxy const& reply,
608 InvokeInfo const& info)
609 {
610 return query(reply, info.mw,
611- [&q, &hints, this]() -> SearchQueryBase::UPtr {
612+ [&q, &hints, &context, this]() -> SearchQueryBase::UPtr {
613 auto search_query = this->scope_base_->search(q, hints);
614 search_query->set_department_id(q.department_id());
615+
616+ auto sqb = dynamic_cast<SearchQueryBaseImpl*>(search_query->fwd());
617+ assert(sqb);
618+
619+ // Set client ID and history that we received in the SearchQueryBase
620+ // for loop detection.
621+ auto const c_it = context.find("client_id");
622+ if (c_it != context.end())
623+ {
624+ string client_id;
625+ client_id = c_it->second.get_string();
626+ sqb->set_client_id(client_id);
627+ }
628+
629+ auto const h_it = context.find("history");
630+ if (h_it != context.end())
631+ {
632+ auto const hlist = h_it->second.get_array();
633+ SearchQueryBaseImpl::History history;
634+ for (auto const& t : hlist)
635+ {
636+ string client_id = t.get_dict()["c"].get_string();
637+ string agg = t.get_dict()["a"].get_string();
638+ string recv = t.get_dict()["r"].get_string();
639+ SearchQueryBaseImpl::HistoryData hd = make_tuple(client_id, agg, recv);
640+ history.push_back(hd);
641+ }
642+ sqb->set_history(history);
643+ }
644+
645 return search_query;
646 },
647 [&reply, &hints, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr {
648
649=== modified file 'src/scopes/internal/SearchMetadataImpl.cpp'
650--- src/scopes/internal/SearchMetadataImpl.cpp 2015-01-14 02:19:17 +0000
651+++ src/scopes/internal/SearchMetadataImpl.cpp 2015-01-26 17:28:26 +0000
652@@ -50,6 +50,7 @@
653 auto it = find_or_throw("SearchMetadataImpl()", var, "cardinality");
654 cardinality_ = it->second.get_int();
655 check_cardinality("SearchMetadataImpl(VariantMap)", cardinality_);
656+
657 try
658 {
659 it = find_or_throw("SearchMetadataImpl()", var, "location");
660@@ -90,7 +91,6 @@
661 return location_;
662 }
663
664-
665 std::string SearchMetadataImpl::metadata_type() const
666 {
667 static const std::string t("search_metadata");
668
669=== modified file 'src/scopes/internal/SearchQueryBaseImpl.cpp'
670--- src/scopes/internal/SearchQueryBaseImpl.cpp 2014-08-05 06:42:04 +0000
671+++ src/scopes/internal/SearchQueryBaseImpl.cpp 2015-01-26 17:28:26 +0000
672@@ -18,9 +18,17 @@
673 */
674
675 #include <unity/scopes/internal/SearchQueryBaseImpl.h>
676+
677+#include <unity/scopes/internal/QueryCtrlImpl.h>
678+#include <unity/scopes/internal/RuntimeImpl.h>
679+#include <unity/scopes/internal/ScopeImpl.h>
680+#include <unity/scopes/internal/SearchMetadataImpl.h>
681+#include <unity/scopes/ScopeExceptions.h>
682+
683 #include <unity/UnityExceptions.h>
684-#include <unity/scopes/QueryCtrl.h>
685-#include <unity/scopes/Scope.h>
686+
687+#include <algorithm>
688+#include <cassert>
689
690 namespace unity
691 {
692@@ -35,9 +43,9 @@
693
694 SearchQueryBaseImpl::SearchQueryBaseImpl(CannedQuery const& query, SearchMetadata const& metadata)
695 : QueryBaseImpl(),
696- valid_(true),
697 canned_query_(query),
698- search_metadata_(metadata)
699+ search_metadata_(metadata),
700+ valid_(true)
701 {
702 }
703
704@@ -52,72 +60,6 @@
705 }
706
707 QueryCtrlProxy SearchQueryBaseImpl::subsearch(ScopeProxy const& scope,
708- string const& query_string,
709- SearchListenerBase::SPtr const& reply)
710-{
711- if (!scope)
712- {
713- throw InvalidArgumentException("QueryBase::subsearch(): scope cannot be nullptr");
714- }
715- if (!reply)
716- {
717- throw InvalidArgumentException("QueryBase::subsearch(): reply cannot be nullptr");
718- }
719-
720- // Forward the create request to the child scope and remember the control.
721- // This allows cancel() to forward incoming cancellations to subqueries
722- // without intervention from the scope application code.
723- QueryCtrlProxy qcp = scope->search(query_string, search_metadata_, reply);
724-
725- lock_guard<mutex> lock(mutex_);
726- subqueries_.push_back(qcp);
727- return qcp;
728-}
729-
730-QueryCtrlProxy SearchQueryBaseImpl::subsearch(ScopeProxy const& scope,
731- std::string const& query_string,
732- FilterState const& filter_state,
733- SearchListenerBase::SPtr const& reply)
734-{
735- if (!scope)
736- {
737- throw InvalidArgumentException("QueryBase::subsearch(): scope cannot be nullptr");
738- }
739- if (!reply)
740- {
741- throw InvalidArgumentException("QueryBase::subsearch(): reply cannot be nullptr");
742- }
743-
744- QueryCtrlProxy qcp = scope->search(query_string, filter_state, search_metadata_, reply);
745-
746- lock_guard<mutex> lock(mutex_);
747- subqueries_.push_back(qcp);
748- return qcp;
749-}
750-
751-QueryCtrlProxy SearchQueryBaseImpl::subsearch(ScopeProxy const& scope,
752- std::string const& query_string,
753- std::string const& department_id,
754- FilterState const& filter_state,
755- SearchListenerBase::SPtr const& reply)
756-{
757- if (!scope)
758- {
759- throw InvalidArgumentException("QueryBase::subsearch(): scope cannot be nullptr");
760- }
761- if (!reply)
762- {
763- throw InvalidArgumentException("QueryBase::subsearch(): reply cannot be nullptr");
764- }
765-
766- QueryCtrlProxy qcp = scope->search(query_string, department_id, filter_state, search_metadata_, reply);
767-
768- lock_guard<mutex> lock(mutex_);
769- subqueries_.push_back(qcp);
770- return qcp;
771-}
772-
773-QueryCtrlProxy SearchQueryBaseImpl::subsearch(ScopeProxy const& scope,
774 std::string const& query_string,
775 std::string const& department_id,
776 FilterState const& filter_state,
777@@ -133,11 +75,28 @@
778 throw InvalidArgumentException("QueryBase::subsearch(): reply cannot be nullptr");
779 }
780
781- QueryCtrlProxy qcp = scope->search(query_string, department_id, filter_state, metadata, reply);
782+ auto query_ctrl = check_for_query_loop(scope, reply);
783+ if (query_ctrl)
784+ {
785+ return query_ctrl; // Loop was detected, return dummy QueryCtrlProxy.
786+ }
787+
788+ // scope_impl can be nullptr if we use a mock scope: TypedScopeFixture<testing::Scope>
789+ // If so, we call the normal search without passing the history through because
790+ // we don't need loop detection for mock scopes.
791+ auto scope_impl = dynamic_pointer_cast<ScopeImpl>(scope);
792+ if (scope_impl)
793+ {
794+ query_ctrl = scope_impl->search(query_string, department_id, filter_state, metadata, history_, reply);
795+ }
796+ else
797+ {
798+ query_ctrl = scope->search(query_string, department_id, filter_state, metadata, reply);
799+ }
800
801 lock_guard<mutex> lock(mutex_);
802- subqueries_.push_back(qcp);
803- return qcp;
804+ subqueries_.push_back(query_ctrl); // Remember subsearch in case we get a cancel() later that we need to forward.
805+ return query_ctrl;
806 }
807
808 void SearchQueryBaseImpl::cancel()
809@@ -171,12 +130,70 @@
810 return department_id_;
811 }
812
813+void SearchQueryBaseImpl::set_client_id(std::string const& id)
814+{
815+ lock_guard<mutex> lock(mutex_);
816+ client_id_ = id;
817+}
818+
819+void SearchQueryBaseImpl::set_history(History const& h)
820+{
821+ lock_guard<mutex> lock(mutex_);
822+ history_ = h;
823+}
824+
825 bool SearchQueryBaseImpl::valid() const
826 {
827 lock_guard<mutex> lock(mutex_);
828 return valid_;
829 }
830
831+// Check if this query has been through this aggregator before by checking the
832+// history. The history is a list of <client, aggregator, receiver> scope ID tuples.
833+// If a query loops around to this aggregator, and has been sent to the same child
834+// previously on behalf of the same client, we immediately call finished to indicate
835+// an empty result set. If a loop is detected, we return a dummy QueryCtrl. Otherwise,
836+// we add the current <client, aggregator, receiver> tuple to the history before forwarding the search.
837+//
838+// Note that we do this unconditionally, whether the receiver is a leaf or an aggregator.
839+// This means that an aggregator can ask a *leaf scope* more than once for data.
840+// This works because normal searches do not check the query history (only
841+// subsearches do). But an aggregator cannot ask another *aggregator* more
842+// than once for data, even if that would not create a loop.
843+//
844+// It is possible for an aggregator to send a query to itself, but only once.
845+
846+QueryCtrlProxy SearchQueryBaseImpl::check_for_query_loop(ScopeProxy const& scope,
847+ SearchListenerBase::SPtr const& reply)
848+{
849+ shared_ptr<QueryCtrlImpl> ctrl_proxy;
850+
851+ lock_guard<mutex> lock(mutex_);
852+
853+ HistoryData tuple = make_tuple(client_id_, canned_query_.scope_id(), scope->identity());
854+ auto it = find(history_.begin(), history_.end(), tuple);
855+ if (it != history_.end())
856+ {
857+ // Query has been here before from the same client and with the same child scope as the target.
858+ reply->finished(CompletionDetails(CompletionDetails::OK,
859+ "empty result set due to aggregator loop or repeated query on aggregating scope "
860+ + get<1>(tuple)));
861+ auto scope_impl = dynamic_pointer_cast<ScopeImpl>(scope);
862+ auto logger = scope_impl->runtime()->logger();
863+ ctrl_proxy = make_shared<QueryCtrlImpl>(nullptr, nullptr); // Dummy proxy in already-cancelled state
864+ BOOST_LOG_SEV(logger, Logger::Warning)
865+ << "query loop for query \"" << canned_query_.query_string()
866+ << "\", client: " << get<0>(tuple)
867+ << ", aggregator: " << get<1>(tuple)
868+ << ", receiver: " << get<2>(tuple) << endl;
869+ }
870+ else
871+ {
872+ history_.push_back(tuple);
873+ }
874+
875+ return ctrl_proxy; // null proxy if there was no loop
876+}
877
878 } // namespace internal
879
880
881=== modified file 'src/scopes/internal/smartscopes/SSScopeObject.cpp'
882--- src/scopes/internal/smartscopes/SSScopeObject.cpp 2015-01-26 17:28:26 +0000
883+++ src/scopes/internal/smartscopes/SSScopeObject.cpp 2015-01-26 17:28:26 +0000
884@@ -64,9 +64,10 @@
885 }
886
887 MWQueryCtrlProxy SSScopeObject::search(CannedQuery const& q,
888- SearchMetadata const& hints,
889- MWReplyProxy const& reply,
890- InvokeInfo const& info)
891+ SearchMetadata const& hints,
892+ VariantMap const& /* context */,
893+ MWReplyProxy const& reply,
894+ InvokeInfo const& info)
895 {
896 return query(info,
897 reply,
898
899=== modified file 'src/scopes/internal/zmq_middleware/ScopeI.cpp'
900--- src/scopes/internal/zmq_middleware/ScopeI.cpp 2015-01-26 17:28:26 +0000
901+++ src/scopes/internal/zmq_middleware/ScopeI.cpp 2015-01-26 17:28:26 +0000
902@@ -98,10 +98,12 @@
903 proxy.getEndpoint().cStr(),
904 proxy.getIdentity().cStr(),
905 proxy.getCategory().cStr()));
906+ auto context = to_variant_map(req.getContext());
907 auto delegate = dynamic_pointer_cast<ScopeObjectBase>(del());
908 assert(delegate);
909 auto ctrl_proxy = dynamic_pointer_cast<ZmqQueryCtrl>(delegate->search(query,
910 metadata,
911+ context,
912 reply_proxy,
913 to_info(current)));
914 assert(ctrl_proxy);
915
916=== modified file 'src/scopes/internal/zmq_middleware/ZmqScope.cpp'
917--- src/scopes/internal/zmq_middleware/ZmqScope.cpp 2015-01-26 17:28:26 +0000
918+++ src/scopes/internal/zmq_middleware/ZmqScope.cpp 2015-01-26 17:28:26 +0000
919@@ -78,7 +78,10 @@
920 {
921 }
922
923-QueryCtrlProxy ZmqScope::search(CannedQuery const& query, VariantMap const& hints, MWReplyProxy const& reply)
924+QueryCtrlProxy ZmqScope::search(CannedQuery const& query,
925+ VariantMap const& hints,
926+ VariantMap const& context,
927+ MWReplyProxy const& reply)
928 {
929 capnp::MallocMessageBuilder request_builder;
930 auto reply_proxy = dynamic_pointer_cast<ZmqReply>(reply);
931@@ -93,6 +96,8 @@
932 p.setEndpoint(reply_proxy->endpoint().c_str());
933 p.setIdentity(reply_proxy->identity().c_str());
934 p.setCategory(reply_proxy->target_category().c_str());
935+ auto d = in_params.initContext();
936+ to_value_dict(context, d);
937 }
938
939 auto future = mw_base()->twoway_pool()->submit([&] { return this->invoke_scope_(request_builder); });
940
941=== modified file 'src/scopes/internal/zmq_middleware/capnproto/Scope.capnp'
942--- src/scopes/internal/zmq_middleware/capnproto/Scope.capnp 2015-01-26 17:28:26 +0000
943+++ src/scopes/internal/zmq_middleware/capnproto/Scope.capnp 2015-01-26 17:28:26 +0000
944@@ -25,27 +25,17 @@
945 using Proxy = import "Proxy.capnp";
946 using ValueDict = import "ValueDict.capnp";
947
948-# Factory for queries. The Scope object runs on the ctrl-endpoint.
949+# Factory for queries.
950 # createQuery() creates a Query object on the normal endpoint, and a QueryCtrl object
951 # on the ctrl-endpoint. It then calls run() on the Query object to set it executing in its own thread, before
952 # returning the QueryCtrl proxy that permits cancellation. This guarantees that createQuery() will not block.
953
954-# Scope interface
955-#
956-# Operations:
957-#
958-# Proxy createQuery(string query, ValueDict hints, Proxy reply_proxy);
959-
960-# The createQuery method instantiates a Query Object and its corresponding QueryCtrlObject.
961-# The return value is the proxy to the QueryCtrl object.
962-# The implementation of createQuery calls the Query's run() method to give a thread of control
963-# to the application code.
964-
965 struct CreateQueryRequest
966 {
967 query @0 : ValueDict.ValueDict;
968 hints @1 : ValueDict.ValueDict;
969 replyProxy @2 : Proxy.Proxy;
970+ context @3 : ValueDict.ValueDict; # Additional context for the request, such as client ID and history.
971 }
972
973 struct CreateQueryResponse
974
975=== modified file 'test/gtest/scopes/CMakeLists.txt'
976--- test/gtest/scopes/CMakeLists.txt 2015-01-26 17:28:26 +0000
977+++ test/gtest/scopes/CMakeLists.txt 2015-01-26 17:28:26 +0000
978@@ -20,6 +20,7 @@
979 add_subdirectory(IdleShutdown)
980 add_subdirectory(Invocation)
981 add_subdirectory(Location)
982+add_subdirectory(LoopDetection)
983 add_subdirectory(OnlineAccountClient)
984 add_subdirectory(OptionSelectorFilter)
985 add_subdirectory(RadioButtonsFilter)
986
987=== added directory 'test/gtest/scopes/LoopDetection'
988=== added file 'test/gtest/scopes/LoopDetection/CMakeLists.txt'
989--- test/gtest/scopes/LoopDetection/CMakeLists.txt 1970-01-01 00:00:00 +0000
990+++ test/gtest/scopes/LoopDetection/CMakeLists.txt 2015-01-26 17:28:26 +0000
991@@ -0,0 +1,26 @@
992+configure_file(TestRegistry.ini.in ${CMAKE_CURRENT_BINARY_DIR}/TestRegistry.ini)
993+configure_file(Runtime.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini)
994+configure_file(Zmq.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Zmq.ini)
995+
996+add_definitions(-DTEST_RUNTIME_PATH="${CMAKE_CURRENT_BINARY_DIR}")
997+add_definitions(-DTEST_RUNTIME_FILE="${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini")
998+add_definitions(-DTEST_REGISTRY_PATH="${PROJECT_BINARY_DIR}/scoperegistry")
999+
1000+add_executable(LoopDetection_test LoopDetection_test.cpp LoopScope.cpp)
1001+target_link_libraries(LoopDetection_test ${TESTLIBS})
1002+
1003+add_dependencies(LoopDetection_test scoperegistry scoperunner)
1004+
1005+add_test(LoopDetection LoopDetection_test)
1006+
1007+set(SCOPE_DIR "${CMAKE_CURRENT_BINARY_DIR}/scopes")
1008+
1009+foreach (scope A B C D)
1010+ file(MAKE_DIRECTORY "${SCOPE_DIR}/${scope}")
1011+ configure_file(LoopScope.ini.in ${SCOPE_DIR}/${scope}/${scope}.ini)
1012+ add_library(${scope} MODULE LoopScope.cpp)
1013+ set_target_properties(${scope}
1014+ PROPERTIES
1015+ LIBRARY_OUTPUT_DIRECTORY "${SCOPE_DIR}/${scope}/"
1016+ )
1017+endforeach()
1018
1019=== added file 'test/gtest/scopes/LoopDetection/LoopDetection_test.cpp'
1020--- test/gtest/scopes/LoopDetection/LoopDetection_test.cpp 1970-01-01 00:00:00 +0000
1021+++ test/gtest/scopes/LoopDetection/LoopDetection_test.cpp 2015-01-26 17:28:26 +0000
1022@@ -0,0 +1,249 @@
1023+/*
1024+ * Copyright (C) 2014 Canonical Ltd
1025+ *
1026+ * This program is free software: you can redistribute it and/or modify
1027+ * it under the terms of the GNU Lesser General Public License version 3 as
1028+ * published by the Free Software Foundation.
1029+ *
1030+ * This program is distributed in the hope that it will be useful,
1031+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1032+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1033+ * GNU Lesser General Public License for more details.
1034+ *
1035+ * You should have received a copy of the GNU Lesser General Public License
1036+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1037+ *
1038+ * Authored by: Michi Henning <michi.henning@canonical.com>
1039+ */
1040+
1041+#include <unity/scopes/CategorisedResult.h>
1042+#include <unity/scopes/internal/RegistryObject.h>
1043+#include <unity/scopes/internal/RuntimeImpl.h>
1044+#include <unity/scopes/internal/ScopeImpl.h>
1045+
1046+#include <gtest/gtest.h>
1047+
1048+#include "LoopScope.h"
1049+
1050+using namespace std;
1051+using namespace unity::scopes;
1052+using namespace unity::scopes::internal;
1053+
1054+class CountReceiver : public SearchListenerBase
1055+{
1056+public:
1057+ CountReceiver()
1058+ : query_complete_(false)
1059+ {
1060+ }
1061+
1062+ virtual void push(CategorisedResult result) override
1063+ {
1064+ string cat = result.category()->id();
1065+ string title = result.title();
1066+
1067+ lock_guard<mutex> lock(mutex_);
1068+ auto it = results_.find(cat);
1069+ if (it != results_.end())
1070+ {
1071+ it->second.push_back(title);
1072+ }
1073+ else
1074+ {
1075+ results_.insert(make_pair(cat, vector<string>{ title }));
1076+ }
1077+ cerr << "inserted " << cat << ", " << title << endl;
1078+ }
1079+
1080+ virtual void finished(CompletionDetails const& details) override
1081+ {
1082+ EXPECT_EQ(CompletionDetails::OK, details.status());
1083+ lock_guard<mutex> lock(mutex_);
1084+ query_complete_ = true;
1085+ cond_.notify_one();
1086+ }
1087+
1088+ void wait_until_finished()
1089+ {
1090+ unique_lock<mutex> lock(mutex_);
1091+ cond_.wait(lock, [this] { return this->query_complete_; });
1092+ query_complete_ = false;
1093+ }
1094+
1095+ map<string, vector<string>> results() const
1096+ {
1097+ lock_guard<mutex> lock(mutex_);
1098+
1099+ return results_;
1100+ }
1101+
1102+private:
1103+ map<string, vector<string>> results_;
1104+ bool query_complete_;
1105+ mutable mutex mutex_;
1106+ condition_variable cond_;
1107+};
1108+
1109+class LoopDetectionTest : public ::testing::Test
1110+{
1111+public:
1112+ LoopDetectionTest()
1113+ {
1114+ runtime_ = Runtime::create(TEST_RUNTIME_FILE);
1115+ auto reg = runtime_->registry();
1116+ auto meta = reg->get_metadata("A"); // First scope to receive a search is always A
1117+ scope_ = meta.proxy();
1118+ }
1119+
1120+ ScopeProxy scope() const
1121+ {
1122+ return scope_;
1123+ }
1124+
1125+private:
1126+ Runtime::UPtr runtime_;
1127+ ScopeProxy scope_;
1128+};
1129+
1130+// Stop warnings about unused return value from system()
1131+
1132+#pragma GCC diagnostic push
1133+#pragma GCC diagnostic ignored "-Wunused-result"
1134+
1135+// The command file for each scope is called A.cmd, B.cmd, etc. It contains scope IDs, one per
1136+// line. For each scope ID in its command file, the scope sends a subsearch to that scope.
1137+// Once a scope's subsearch completes, it pushes a single result with the scope's ID as the
1138+// category ID. This allows us to set up various callgraphs by writing to the various command files.
1139+
1140+TEST_F(LoopDetectionTest, no_error)
1141+{
1142+ system("cat >A.cmd << EOF\nB\nC\nEOF");
1143+ system(">B.cmd");
1144+ system(">C.cmd");
1145+ auto receiver = make_shared<CountReceiver>();
1146+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1147+ receiver->wait_until_finished();
1148+
1149+ auto r = receiver->results();
1150+ EXPECT_EQ(3, r.size());
1151+ EXPECT_TRUE(r.find("A") != r.end());
1152+ EXPECT_TRUE(r.find("B") != r.end());
1153+ EXPECT_TRUE(r.find("C") != r.end());
1154+}
1155+
1156+TEST_F(LoopDetectionTest, diamond)
1157+{
1158+ system("cat >A.cmd << EOF\nB\nC\nEOF");
1159+ system("cat >B.cmd << EOF\nD\nEOF");
1160+ system("cat >C.cmd << EOF\nD\nEOF");
1161+ system(">D.cmd");
1162+ auto receiver = make_shared<CountReceiver>();
1163+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1164+ receiver->wait_until_finished();
1165+
1166+ auto r = receiver->results();
1167+ EXPECT_EQ(4, r.size());
1168+ EXPECT_TRUE(r.find("A") != r.end());
1169+ EXPECT_TRUE(r.find("B") != r.end());
1170+ EXPECT_TRUE(r.find("C") != r.end());
1171+ EXPECT_TRUE(r.find("D") != r.end());
1172+ EXPECT_EQ(2, r.find("D")->second.size()); // B, and C each forwarded to D, so there must be 2 results.
1173+}
1174+
1175+TEST_F(LoopDetectionTest, immediate_loop)
1176+{
1177+ system("cat >A.cmd << EOF\nA\nEOF");
1178+ auto receiver = make_shared<CountReceiver>();
1179+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1180+ receiver->wait_until_finished();
1181+
1182+ auto r = receiver->results();
1183+ EXPECT_EQ(1, r.size());
1184+ EXPECT_EQ(3, r.find("A")->second.size()); // "A" has sent the query twice to itself, second attempt stops loop
1185+}
1186+
1187+TEST_F(LoopDetectionTest, intermediate_loop)
1188+{
1189+ system("cat >A.cmd << EOF\nB\nEOF");
1190+ system("cat >B.cmd << EOF\nC\nEOF");
1191+ system("cat >C.cmd << EOF\nA\nEOF");
1192+ auto receiver = make_shared<CountReceiver>();
1193+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1194+ receiver->wait_until_finished();
1195+
1196+ auto r = receiver->results();
1197+ EXPECT_EQ(3, r.size());
1198+ EXPECT_EQ(2, r.find("A")->second.size()); // "A" got results from B and C
1199+ EXPECT_EQ(2, r.find("B")->second.size()); // "B" got results from C and A
1200+ EXPECT_EQ(1, r.find("C")->second.size()); // "C" got result from A
1201+}
1202+
1203+TEST_F(LoopDetectionTest, repeated_search_on_leaf)
1204+{
1205+ system("cat >A.cmd << EOF\nB\nC\nD\nEOF");
1206+ system("cat >B.cmd << EOF\nD\nD\nEOF");
1207+ system("cat >C.cmd << EOF\nD\nEOF");
1208+ system(">D.cmd");
1209+ auto receiver = make_shared<CountReceiver>();
1210+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1211+ receiver->wait_until_finished();
1212+
1213+ auto r = receiver->results();
1214+ EXPECT_EQ(4, r.size());
1215+ EXPECT_EQ(1, r.find("A")->second.size());
1216+ EXPECT_EQ(1, r.find("B")->second.size());
1217+ EXPECT_EQ(1, r.find("C")->second.size());
1218+ EXPECT_EQ(3, r.find("D")->second.size());
1219+}
1220+
1221+TEST_F(LoopDetectionTest, repeated_search_on_aggregator)
1222+{
1223+ system("cat >A.cmd << EOF\nB\nEOF");
1224+ system("cat >B.cmd << EOF\nC\nC\nEOF");
1225+ system(">C.cmd << EOF\nD\nEOF");
1226+ system(">D.cmd");
1227+ auto receiver = make_shared<CountReceiver>();
1228+ scope()->search("A", SearchMetadata("unused", "unused"), receiver);
1229+ receiver->wait_until_finished();
1230+
1231+ auto r = receiver->results();
1232+ EXPECT_EQ(3, r.size());
1233+ EXPECT_EQ(1, r.find("A")->second.size());
1234+ EXPECT_EQ(1, r.find("B")->second.size());
1235+ EXPECT_EQ(1, r.find("C")->second.size());
1236+}
1237+
1238+#pragma GCC diagnostic pop
1239+
1240+int main(int argc, char **argv)
1241+{
1242+ ::testing::InitGoogleTest(&argc, argv);
1243+
1244+ int rc = 0;
1245+
1246+ // Set the "TEST_DESKTOP_FILES_DIR" env var before forking as not to create desktop files in ~/.local
1247+ putenv(const_cast<char*>("TEST_DESKTOP_FILES_DIR=" TEST_RUNTIME_PATH));
1248+
1249+ auto rpid = fork();
1250+ if (rpid == 0)
1251+ {
1252+ const char* const args[] = {"scoperegistry [LoopDetecttion_test]", TEST_RUNTIME_FILE, nullptr};
1253+ if (execv(TEST_REGISTRY_PATH "/scoperegistry", const_cast<char* const*>(args)) < 0)
1254+ {
1255+ perror("Error starting scoperegistry:");
1256+ }
1257+ return 1;
1258+ }
1259+ else if (rpid > 0)
1260+ {
1261+ rc = RUN_ALL_TESTS();
1262+ kill(rpid, SIGTERM);
1263+ waitpid(rpid, nullptr, 0);
1264+ }
1265+ else
1266+ {
1267+ perror("Failed to fork:");
1268+ }
1269+
1270+ return rc;
1271+}
1272
1273=== added file 'test/gtest/scopes/LoopDetection/LoopScope.cpp'
1274--- test/gtest/scopes/LoopDetection/LoopScope.cpp 1970-01-01 00:00:00 +0000
1275+++ test/gtest/scopes/LoopDetection/LoopScope.cpp 2015-01-26 17:28:26 +0000
1276@@ -0,0 +1,177 @@
1277+/*
1278+ * Copyright (C) 2014 Canonical Ltd
1279+ *
1280+ * This program is free software: you can redistribute it and/or modify
1281+ * it under the terms of the GNU Lesser General Public License version 3 as
1282+ * published by the Free Software Foundation.
1283+ *
1284+ * This program is distributed in the hope that it will be useful,
1285+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1286+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1287+ * GNU Lesser General Public License for more details.
1288+ *
1289+ * You should have received a copy of the GNU Lesser General Public License
1290+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1291+ *
1292+ * Authored by: Michi Henning <michi.henning@canonical.com>
1293+ */
1294+
1295+#include "LoopScope.h"
1296+
1297+#include <unity/scopes/CategorisedResult.h>
1298+#include <unity/scopes/ScopeBase.h>
1299+#include <unity/scopes/ScopeExceptions.h>
1300+#include <unity/scopes/SearchReply.h>
1301+
1302+#include <unity/UnityExceptions.h>
1303+#include <unity/util/FileIO.h>
1304+
1305+#include <mutex>
1306+#include <sstream>
1307+#include <thread>
1308+
1309+using namespace std;
1310+using namespace unity::scopes;
1311+
1312+namespace
1313+{
1314+
1315+class Receiver: public SearchListenerBase
1316+{
1317+public:
1318+
1319+ virtual void push(Category::SCPtr const& category) override
1320+ {
1321+ try
1322+ {
1323+ upstream_->register_category(category);
1324+ }
1325+ catch (unity::InvalidArgumentException const&)
1326+ {
1327+ }
1328+ }
1329+
1330+ virtual void push(CategorisedResult result) override
1331+ {
1332+ cerr << scope_id_ << ": received result: " << result.uri() << ", " << result.title() << endl;
1333+ try
1334+ {
1335+ upstream_->push(std::move(result));
1336+ }
1337+ catch (const unity::InvalidArgumentException &e)
1338+ {
1339+ cerr << scope_id_ << ": error pushing result: " << e.what() << endl;
1340+ }
1341+ }
1342+
1343+ virtual void finished(CompletionDetails const& details) override
1344+ {
1345+ cerr << scope_id_ << ": subquery complete, status: " << to_string(details.status())
1346+ << ", msg: " << details.message() << endl;
1347+ }
1348+
1349+ Receiver(string const& scope_id, SearchReplyProxy const& upstream) :
1350+ scope_id_(scope_id),
1351+ upstream_(upstream)
1352+ {
1353+ }
1354+
1355+private:
1356+ string scope_id_;
1357+ SearchReplyProxy upstream_;
1358+};
1359+
1360+class TestQuery : public SearchQueryBase
1361+{
1362+public:
1363+ TestQuery(CannedQuery const& query,
1364+ SearchMetadata const& metadata,
1365+ string const& id,
1366+ RegistryProxy const& reg)
1367+ : SearchQueryBase(query, metadata)
1368+ , id_(id)
1369+ , registry_(reg)
1370+ {
1371+ }
1372+
1373+ virtual void cancelled() override
1374+ {
1375+ }
1376+
1377+ virtual void run(SearchReplyProxy const& reply) override
1378+ {
1379+ auto cat = reply->register_category(id_, "", "");
1380+ SearchListenerBase::SPtr receiver(new Receiver(id_, reply));
1381+
1382+ // Open the command file and, for each scope ID (one per line),
1383+ // send a subquery to that scope.
1384+ istringstream cmds(unity::util::read_text_file(string(TEST_RUNTIME_PATH) + "/" + id_ + ".cmd"));
1385+ string child_id;
1386+ while (getline(cmds, child_id))
1387+ {
1388+ if (child_id.empty())
1389+ {
1390+ continue; // Ignore empty ID, which is what we get if we act as a leaf scope.
1391+ }
1392+ auto proxy = registry_->get_metadata(child_id).proxy();
1393+ cerr << id_ << ": sending subsearch to " << child_id << endl;
1394+ subsearch(proxy, id_ + "->" + child_id, receiver);
1395+ }
1396+ CategorisedResult res(cat);
1397+ res.set_uri("uri");
1398+ res.set_title(id_ + ": result for query \"" + query().query_string() + "\"");
1399+ if (valid())
1400+ {
1401+ reply->push(res);
1402+ }
1403+ }
1404+
1405+private:
1406+ string id_;
1407+ RegistryProxy registry_;
1408+};
1409+
1410+} // namespace
1411+
1412+void LoopScope::start(string const& scope_id)
1413+{
1414+ lock_guard<mutex> lock(mutex_);
1415+ id_ = scope_id;
1416+}
1417+
1418+void LoopScope::stop()
1419+{
1420+}
1421+
1422+void LoopScope::run()
1423+{
1424+}
1425+
1426+SearchQueryBase::UPtr LoopScope::search(CannedQuery const& query, SearchMetadata const& metadata)
1427+{
1428+ lock_guard<mutex> lock(mutex_);
1429+ return SearchQueryBase::UPtr(new TestQuery(query, metadata, id_, registry()));
1430+}
1431+
1432+PreviewQueryBase::UPtr LoopScope::preview(Result const&, ActionMetadata const &)
1433+{
1434+ return nullptr; // unused
1435+}
1436+
1437+extern "C"
1438+{
1439+
1440+ unity::scopes::ScopeBase*
1441+ // cppcheck-suppress unusedFunction
1442+ UNITY_SCOPE_CREATE_FUNCTION()
1443+ {
1444+ return new LoopScope;
1445+ }
1446+
1447+ void
1448+ // cppcheck-suppress unusedFunction
1449+ UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base)
1450+ {
1451+ delete scope_base;
1452+ }
1453+}
1454
1455=== added file 'test/gtest/scopes/LoopDetection/LoopScope.h'
1456--- test/gtest/scopes/LoopDetection/LoopScope.h 1970-01-01 00:00:00 +0000
1457+++ test/gtest/scopes/LoopDetection/LoopScope.h 2015-01-26 17:28:26 +0000
1458@@ -0,0 +1,41 @@
1459+/*
1460+ * Copyright (C) 2014 Canonical Ltd
1461+ *
1462+ * This program is free software: you can redistribute it and/or modify
1463+ * it under the terms of the GNU Lesser General Public License version 3 as
1464+ * published by the Free Software Foundation.
1465+ *
1466+ * This program is distributed in the hope that it will be useful,
1467+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1468+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1469+ * GNU Lesser General Public License for more details.
1470+ *
1471+ * You should have received a copy of the GNU Lesser General Public License
1472+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1473+ *
1474+ * Authored by: Michi Henning <michi.henning@canonical.com>
1475+ */
1476+
1477+#pragma once
1478+
1479+#include <unity/scopes/ScopeBase.h>
1480+
1481+class LoopScope : public unity::scopes::ScopeBase
1482+{
1483+public:
1484+ virtual void start(std::string const&) override;
1485+
1486+ virtual void stop() override;
1487+
1488+ virtual void run() override;
1489+
1490+ virtual unity::scopes::SearchQueryBase::UPtr search(unity::scopes::CannedQuery const &,
1491+ unity::scopes::SearchMetadata const &) override;
1492+
1493+ virtual unity::scopes::PreviewQueryBase::UPtr preview(unity::scopes::Result const&,
1494+ unity::scopes::ActionMetadata const &) override;
1495+
1496+private:
1497+ std::string id_;
1498+ std::mutex mutex_;
1499+};
1500
1501=== added file 'test/gtest/scopes/LoopDetection/LoopScope.ini.in'
1502--- test/gtest/scopes/LoopDetection/LoopScope.ini.in 1970-01-01 00:00:00 +0000
1503+++ test/gtest/scopes/LoopDetection/LoopScope.ini.in 2015-01-26 17:28:26 +0000
1504@@ -0,0 +1,4 @@
1505+[ScopeConfig]
1506+DisplayName = LoopScope
1507+Description = Aggregator scope that makes various loops
1508+Author = Michi
1509
1510=== added file 'test/gtest/scopes/LoopDetection/Runtime.ini.in'
1511--- test/gtest/scopes/LoopDetection/Runtime.ini.in 1970-01-01 00:00:00 +0000
1512+++ test/gtest/scopes/LoopDetection/Runtime.ini.in 2015-01-26 17:28:26 +0000
1513@@ -0,0 +1,6 @@
1514+[Runtime]
1515+Registry.Identity = TestRegistry
1516+Registry.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/TestRegistry.ini
1517+Default.Middleware = Zmq
1518+Zmq.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/Zmq.ini
1519+Smartscopes.Registry.Identity =
1520
1521=== added file 'test/gtest/scopes/LoopDetection/TestRegistry.ini.in'
1522--- test/gtest/scopes/LoopDetection/TestRegistry.ini.in 1970-01-01 00:00:00 +0000
1523+++ test/gtest/scopes/LoopDetection/TestRegistry.ini.in 2015-01-26 17:28:26 +0000
1524@@ -0,0 +1,7 @@
1525+[Registry]
1526+Middleware = Zmq
1527+Zmq.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/Zmq.ini
1528+Scope.InstallDir = @CMAKE_CURRENT_BINARY_DIR@/scopes
1529+OEM.InstallDir = /unused
1530+Click.InstallDir = @CMAKE_CURRENT_BINARY_DIR@/click
1531+Scoperunner.Path = @PROJECT_BINARY_DIR@/scoperunner/scoperunner
1532
1533=== added file 'test/gtest/scopes/LoopDetection/Zmq.ini.in'
1534--- test/gtest/scopes/LoopDetection/Zmq.ini.in 1970-01-01 00:00:00 +0000
1535+++ test/gtest/scopes/LoopDetection/Zmq.ini.in 2015-01-26 17:28:26 +0000
1536@@ -0,0 +1,2 @@
1537+[Zmq]
1538+EndpointDir = /tmp
1539
1540=== modified file 'test/gtest/scopes/internal/zmq_middleware/ZmqMiddleware/ZmqMiddleware_test.cpp'
1541--- test/gtest/scopes/internal/zmq_middleware/ZmqMiddleware/ZmqMiddleware_test.cpp 2015-01-26 17:28:26 +0000
1542+++ test/gtest/scopes/internal/zmq_middleware/ZmqMiddleware/ZmqMiddleware_test.cpp 2015-01-26 17:28:26 +0000
1543@@ -421,6 +421,7 @@
1544
1545 virtual MWQueryCtrlProxy search(CannedQuery const&,
1546 SearchMetadata const&,
1547+ VariantMap const&,
1548 MWReplyProxy const&,
1549 InvokeInfo const&) override
1550 {
1551
1552=== modified file 'test/gtest/scopes/testing/IsolatedScope/IsolatedScope_test.cpp'
1553--- test/gtest/scopes/testing/IsolatedScope/IsolatedScope_test.cpp 2014-12-03 10:01:38 +0000
1554+++ test/gtest/scopes/testing/IsolatedScope/IsolatedScope_test.cpp 2015-01-26 17:28:26 +0000
1555@@ -211,7 +211,7 @@
1556 &queryctrl, [](unity::scopes::QueryCtrl*) {});
1557
1558 auto push_childscope_results =
1559- [queryctrl_proxy](Unused, Unused, unity::scopes::SearchListenerBase::SPtr const& listener) -> unity::scopes::QueryCtrlProxy {
1560+ [queryctrl_proxy](Unused, Unused, Unused, Unused, unity::scopes::SearchListenerBase::SPtr const& listener) -> unity::scopes::QueryCtrlProxy {
1561 unity::scopes::Category::SCPtr cat(
1562 new unity::scopes::testing::Category(
1563 "id", "title", "icon", unity::scopes::CategoryRenderer()));
1564@@ -229,7 +229,7 @@
1565 };
1566
1567 NiceMock<unity::scopes::testing::MockScope> childscope;
1568- EXPECT_CALL(childscope, search(aggregator_query, _, _))
1569+ EXPECT_CALL(childscope, search(aggregator_query, _, _, _, _))
1570 .WillOnce(Invoke(push_childscope_results));
1571 unity::scopes::ScopeProxy childscope_proxy(
1572 &childscope, [](unity::scopes::Scope*) {});

Subscribers

People subscribed via source and target branches

to all changes: