Merge lp:~marcustomlinson/unity-scopes-api/loop-prevention into lp:unity-scopes-api
- loop-prevention
- Merge into trunk
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 |
Related bugs: |
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.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
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-
Other than that, looks good, tests pass for me here, and looking through the changes doesn't show anything unusual.
- Added missing tmp_dir_
- Removed redundant lock in QueryCtrlImpl constructor.
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_
- Removed redundant lock in QueryCtrlImpl constructor.
And, in CONFIGFILES, change "colon-seperate" to "colon-separated".
Apologies for the noise…
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_
> - Removed redundant lock in QueryCtrlImpl constructor.
>
> And, in CONFIGFILES, change "colon-seperate" to "colon-separated".
>
> Apologies for the noise…
done.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:290
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 290. By Marcus Tomlinson
-
Merged child_scopes_
ordered
Preview Diff
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*) {}); |
PASSED: Continuous integration, rev:289 jenkins. qa.ubuntu. com/job/ unity-scopes- api-ci/ 550/ jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- amd64-ci/ 77 jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- armhf-ci/ 75 jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- armhf-ci/ 75/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- i386-ci/ 76
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- api-ci/ 550/rebuild
http://