Merge lp:~michihenning/unity-scopes-api/scope-exceptions into lp:unity-scopes-api
- scope-exceptions
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 285 |
Merged at revision: | 306 |
Proposed branch: | lp:~michihenning/unity-scopes-api/scope-exceptions |
Merge into: | lp:unity-scopes-api |
Diff against target: |
1442 lines (+1019/-126) 19 files modified
debian/libunity-scopes3.symbols (+1/-1) include/unity/scopes/internal/QueryObject.h (+1/-1) include/unity/scopes/internal/ReplyObject.h (+0/-1) include/unity/scopes/internal/ScopeObject.h (+1/-0) src/scopes/QueryBase.cpp (+1/-14) src/scopes/internal/ActivationQueryObject.cpp (+8/-6) src/scopes/internal/PreviewQueryObject.cpp (+26/-7) src/scopes/internal/QueryObject.cpp (+38/-12) src/scopes/internal/ReplyObject.cpp (+16/-14) src/scopes/internal/ScopeObject.cpp (+81/-70) test/gtest/scopes/CMakeLists.txt (+1/-0) test/gtest/scopes/ThrowingScope/CMakeLists.txt (+26/-0) test/gtest/scopes/ThrowingScope/Runtime.ini.in (+7/-0) test/gtest/scopes/ThrowingScope/TestRegistry.ini.in (+7/-0) test/gtest/scopes/ThrowingScope/ThrowingScope.cpp (+273/-0) test/gtest/scopes/ThrowingScope/ThrowingScope.h (+49/-0) test/gtest/scopes/ThrowingScope/ThrowingScope.ini.in (+4/-0) test/gtest/scopes/ThrowingScope/ThrowingScope_test.cpp (+477/-0) test/gtest/scopes/ThrowingScope/Zmq.ini.in (+2/-0) |
To merge this branch: | bzr merge lp:~michihenning/unity-scopes-api/scope-exceptions |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paweł Stołowski (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email:
|
Commit message
Fixed core dump in the run time when a scope threw an exception from PreviewQueryBas
Added tests for the various scope methods to make sure that they are exception-safe.
Improved logging and error reporting for throwing scopes.
Description of the change
This MR fixes a core dump in the run time when a scope threw an exception from PreviewQueryBas
Added tests for the various scope methods to make sure that they are exception-safe.
Improved logging and error reporting for throwing scopes.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:280
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:281
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:281
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:281
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:281
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:281
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:281
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:282
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paweł Stołowski (stolowski) wrote : | # |
Looks good, thanks!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Paweł Stołowski (stolowski) wrote : | # |
After resolving conflicts and attempting to build it with some other big branches (such as loop-prevention) we were seeing new kind of test failures in the silo, so I had to pull that branch off for the moment.
Here is the log:
https:/
In case the full log disappears, here is an excerpt of failures:
Start 68: ThrowingScope
68: Test command: /build/
68: Test timeout computed to be: 1500
68: [==========] Running 11 tests from 1 test case.
68: [----------] Global test environment set-up.
68: [----------] 11 tests from ThrowingScopeTest
68: [ RUN ] ThrowingScopeTe
68: scoperegistry [ThrowingScope_
68: scoperegistry [ThrowingScope_
68: scoperegistry [ThrowingScope_
68: scoperegistry [ThrowingScope_
68: scoperegistry [ThrowingScope_
68: [2015-01-27 09:06:51.739966] INFO: TestRegistry: ScopesWatcher: scope: "ThrowingScope" installed to: "/build/
68: [2015-01-27 09:06:51.772695] INFO: TestRegistry: RegistryObject:
68: [ OK ] ThrowingScopeTe
68: [ RUN ] ThrowingScopeTe
68: [2015-01-27 09:06:51.781175] ERROR: ThrowingScope: Scope "ThrowingScope" threw an exception from search()
68: [ OK ] ThrowingScopeTe
68: [ RUN ] ThrowingScopeTe
68: [2015-01-27 09:06:51.786344] ERROR: ThrowingScope: QueryBase::run(): unity::
68: [2015-01-27 09:06:51.787049] WARNING: c-4e02aa2d00000000: ObjectAdapter: no servant for oneway request (id: f1dd504500000000, adapter: c-4e02aa2d00000
68: [ OK ] ThrowingScopeTe
68: [ RUN ] ThrowingScopeTe
68: [2015-01-27 09:06:52.791321] ERROR: ThrowingScope: QueryBase:
68: [2015-01-27 09:06:52.791663] WARNING: ThrowingScope: ObjectAdapter: no servant for oneway request (id: f82c2eee00000004, adapter: ThrowingScope-c, op: destroy)
68: [2015-01-27 09:06:52.791880] WARNING: c-4ce6aad900000000: ObjectAdapter: no servant for oneway request (id: 7e69520600000000, adapter: c-4ce6aad900000
68: [2015-01-27 09:06:52.792170] WARNIN...
- 283. By Michi Henning
-
Merged trunk and resolved conflicts.
- 284. By Michi Henning
-
Removed trailing whitespace.
- 285. By Michi Henning
-
Fixed race in test that caused bogus test failure if exception
from cancelled wasn't processed by the time run() returned. Fixed
race in ReplyObject. (info_list_ was accessed whithout holding a lock.)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michi Henning (michihenning) wrote : | # |
The failure that Pawel saw was caused by the merge having omitted a change from my branch. While testing, I found a race in the test that caused a bogus failure, and also fixed a real race in ReplyObject. Should be good now.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:285
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'debian/libunity-scopes3.symbols' |
2 | --- debian/libunity-scopes3.symbols 2015-01-30 12:34:39 +0000 |
3 | +++ debian/libunity-scopes3.symbols 2015-02-04 05:47:34 +0000 |
4 | @@ -546,7 +546,7 @@ |
5 | (c++)"unity::scopes::internal::ScopeObject::debug_mode() const@Base" 0.6.2+rtm+rtm+rtm+14.09.20140818 |
6 | (c++)"unity::scopes::internal::ScopeObject::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 |
7 | (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 |
8 | - (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 |
9 | + (c++)"unity::scopes::internal::ScopeObject::query(std::shared_ptr<unity::scopes::internal::MWReply> const&, unity::scopes::internal::MiddlewareBase*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, 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" 0replaceme |
10 | (c++)"unity::scopes::internal::ScopeObject::~ScopeObject()@Base" 0.4.0+14.04.20140312.1 |
11 | (c++)"unity::scopes::internal::ScopeObject::ScopeObject(unity::scopes::ScopeBase*, bool)@Base" 0.6.12+15.04.20150127.2 |
12 | (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" 0.6.12+15.04.20150127.2 |
13 | |
14 | === modified file 'include/unity/scopes/internal/QueryObject.h' |
15 | --- include/unity/scopes/internal/QueryObject.h 2015-01-09 00:14:14 +0000 |
16 | +++ include/unity/scopes/internal/QueryObject.h 2015-02-04 05:47:34 +0000 |
17 | @@ -67,7 +67,7 @@ |
18 | |
19 | protected: |
20 | std::shared_ptr<QueryBase> query_base_; |
21 | - MWReplyProxy reply_; |
22 | + MWReplyProxy const reply_; |
23 | std::weak_ptr<unity::scopes::Reply> reply_proxy_; |
24 | MWQueryCtrlProxy const ctrl_; |
25 | bool pushable_; |
26 | |
27 | === modified file 'include/unity/scopes/internal/ReplyObject.h' |
28 | --- include/unity/scopes/internal/ReplyObject.h 2015-01-22 03:54:58 +0000 |
29 | +++ include/unity/scopes/internal/ReplyObject.h 2015-02-04 05:47:34 +0000 |
30 | @@ -69,7 +69,6 @@ |
31 | std::condition_variable idle_; |
32 | std::string origin_proxy_; |
33 | int num_push_; |
34 | - std::atomic_bool info_occurred_; |
35 | std::vector<OperationInfo> info_list_; |
36 | }; |
37 | |
38 | |
39 | === modified file 'include/unity/scopes/internal/ScopeObject.h' |
40 | --- include/unity/scopes/internal/ScopeObject.h 2015-01-26 08:20:45 +0000 |
41 | +++ include/unity/scopes/internal/ScopeObject.h 2015-02-04 05:47:34 +0000 |
42 | @@ -84,6 +84,7 @@ |
43 | |
44 | private: |
45 | MWQueryCtrlProxy query(MWReplyProxy const& reply, MiddlewareBase* mw_base, |
46 | + std::string const& method, |
47 | std::function<QueryBase::SPtr(void)> const& query_factory_fun, |
48 | std::function<QueryObjectBase::SPtr(QueryBase::SPtr, MWQueryCtrlProxy)> const& query_object_factory_fun); |
49 | ScopeBase* const scope_base_; |
50 | |
51 | === modified file 'src/scopes/QueryBase.cpp' |
52 | --- src/scopes/QueryBase.cpp 2014-08-05 06:42:04 +0000 |
53 | +++ src/scopes/QueryBase.cpp 2015-02-04 05:47:34 +0000 |
54 | @@ -43,20 +43,7 @@ |
55 | void QueryBase::cancel() |
56 | { |
57 | p->cancel(); // Forward cancel to subqueries |
58 | - try |
59 | - { |
60 | - cancelled(); // Inform this query that it was cancelled |
61 | - } |
62 | - catch (std::exception const& e) |
63 | - { |
64 | - cerr << "QueryBase::cancel(): exception from cancelled(): " << e.what() << endl; |
65 | - // TODO: log error |
66 | - } |
67 | - catch (...) |
68 | - { |
69 | - cerr << "QueryBase::cancel(): unknown exception from cancelled()" << endl; |
70 | - // TODO: log error |
71 | - } |
72 | + cancelled(); |
73 | } |
74 | /// @endcond |
75 | |
76 | |
77 | === modified file 'src/scopes/internal/ActivationQueryObject.cpp' |
78 | --- src/scopes/internal/ActivationQueryObject.cpp 2015-01-09 00:14:14 +0000 |
79 | +++ src/scopes/internal/ActivationQueryObject.cpp 2015-02-04 05:47:34 +0000 |
80 | @@ -55,8 +55,8 @@ |
81 | { |
82 | assert(self_); |
83 | |
84 | - // The reply proxy now holds our reference count high, so |
85 | - // we can drop our own smart pointer and disconnect from the middleware. |
86 | + // Disconnect from middleware. While this request is in progress, |
87 | + // this instance will not be deallocated. |
88 | self_ = nullptr; |
89 | disconnect(); |
90 | |
91 | @@ -71,13 +71,15 @@ |
92 | } |
93 | catch (std::exception const& e) |
94 | { |
95 | - BOOST_LOG(info.mw->runtime()->logger()) << "ActivationQueryObject::run(): " << e.what(); |
96 | - reply_->finished(CompletionDetails(CompletionDetails::Error, e.what())); // Oneway, can't block |
97 | + BOOST_LOG(info.mw->runtime()->logger()) << "ActivationQueryBase::activate(): " << e.what(); |
98 | + reply_->finished(CompletionDetails(CompletionDetails::Error, |
99 | + string("ActivationQueryBase::activate(): ") + e.what())); |
100 | } |
101 | catch (...) |
102 | { |
103 | - BOOST_LOG(info.mw->runtime()->logger()) << "ActivationQueryObject::run(): unknown exception"; |
104 | - reply_->finished(CompletionDetails(CompletionDetails::Error, "unknown exception")); // Oneway, can't block |
105 | + BOOST_LOG(info.mw->runtime()->logger()) << "ActivationQueryBase::activate(): unknown exception"; |
106 | + reply_->finished(CompletionDetails(CompletionDetails::Error, |
107 | + "ActivationQueryBase::activate(): unknown exception")); |
108 | } |
109 | } |
110 | |
111 | |
112 | === modified file 'src/scopes/internal/PreviewQueryObject.cpp' |
113 | --- src/scopes/internal/PreviewQueryObject.cpp 2015-01-09 03:16:51 +0000 |
114 | +++ src/scopes/internal/PreviewQueryObject.cpp 2015-02-04 05:47:34 +0000 |
115 | @@ -57,6 +57,16 @@ |
116 | |
117 | void PreviewQueryObject::run(MWReplyProxy const& reply, InvokeInfo const& info) noexcept |
118 | { |
119 | + unique_lock<mutex> lock(mutex_); |
120 | + |
121 | + // See comment in QueryObject::run() |
122 | + if (!pushable_) |
123 | + { |
124 | + self_ = nullptr; |
125 | + disconnect(); |
126 | + return; |
127 | + } |
128 | + |
129 | assert(self_); |
130 | |
131 | auto reply_proxy = make_shared<PreviewReplyImpl>(reply, self_); |
132 | @@ -68,24 +78,33 @@ |
133 | self_ = nullptr; |
134 | disconnect(); |
135 | |
136 | - // Synchronous call into scope implementation. |
137 | - // On return, replies for the query may still be outstanding. |
138 | try |
139 | { |
140 | + lock.unlock(); |
141 | + |
142 | + // Synchronous call into scope implementation. |
143 | + // On return, replies for the preview may still be outstanding. |
144 | auto preview_query = dynamic_pointer_cast<PreviewQueryBase>(query_base_); |
145 | assert(preview_query); |
146 | preview_query->run(reply_proxy); |
147 | } |
148 | catch (std::exception const& e) |
149 | { |
150 | - pushable_ = false; |
151 | - BOOST_LOG(info.mw->runtime()->logger()) << "PreviewQueryObject::run(): " << e.what(); |
152 | - reply_->finished(CompletionDetails(CompletionDetails::Error, e.what())); // Oneway, can't block |
153 | + { |
154 | + lock_guard<mutex> lock(mutex_); |
155 | + pushable_ = false; |
156 | + } |
157 | + BOOST_LOG(info.mw->runtime()->logger()) << "PreviewQueryBase::run(): " << e.what(); |
158 | + reply_->finished(CompletionDetails(CompletionDetails::Error, string("PreviewQueryBase::run(): ") + e.what())); |
159 | } |
160 | catch (...) |
161 | { |
162 | - BOOST_LOG(info.mw->runtime()->logger()) << "PreviewQueryObject::run(): unknown exception"; |
163 | - reply_->finished(CompletionDetails(CompletionDetails::Error, "unknown exception")); // Oneway, can't block |
164 | + { |
165 | + lock_guard<mutex> lock(mutex_); |
166 | + pushable_ = false; |
167 | + } |
168 | + BOOST_LOG(info.mw->runtime()->logger()) << "PreviewQueryBase::run(): unknown exception"; |
169 | + reply_->finished(CompletionDetails(CompletionDetails::Error, "PreviewQueryBase::run(): unknown exception")); |
170 | } |
171 | } |
172 | |
173 | |
174 | === modified file 'src/scopes/internal/QueryObject.cpp' |
175 | --- src/scopes/internal/QueryObject.cpp 2015-01-09 03:16:51 +0000 |
176 | +++ src/scopes/internal/QueryObject.cpp 2015-02-04 05:47:34 +0000 |
177 | @@ -123,19 +123,25 @@ |
178 | } |
179 | catch (std::exception const& e) |
180 | { |
181 | - pushable_ = false; |
182 | - BOOST_LOG(info.mw->runtime()->logger()) << "ScopeBase::run(): " << e.what(); |
183 | - reply_->finished(CompletionDetails(CompletionDetails::Error, e.what())); // Oneway, can't block |
184 | + { |
185 | + lock_guard<mutex> lock(mutex_); |
186 | + pushable_ = false; |
187 | + } |
188 | + BOOST_LOG(info.mw->runtime()->logger()) << "QueryBase::run(): " << e.what(); |
189 | + reply_->finished(CompletionDetails(CompletionDetails::Error, string("QueryBase::run(): ") + e.what())); |
190 | } |
191 | catch (...) |
192 | { |
193 | - pushable_ = false; |
194 | - BOOST_LOG(info.mw->runtime()->logger()) << "ScopeBase::run(): unknown exception"; |
195 | - reply_->finished(CompletionDetails(CompletionDetails::Error, "unknown exception")); // Oneway, can't block |
196 | + { |
197 | + lock_guard<mutex> lock(mutex_); |
198 | + pushable_ = false; |
199 | + } |
200 | + BOOST_LOG(info.mw->runtime()->logger()) << "QueryBase::run(): unknown exception"; |
201 | + reply_->finished(CompletionDetails(CompletionDetails::Error, "QueryBase::run(): unknown exception")); |
202 | } |
203 | } |
204 | |
205 | -void QueryObject::cancel(InvokeInfo const& /* info */) |
206 | +void QueryObject::cancel(InvokeInfo const& info) |
207 | { |
208 | { |
209 | lock_guard<mutex> lock(mutex_); |
210 | @@ -145,6 +151,13 @@ |
211 | return; |
212 | } |
213 | pushable_ = false; |
214 | + } // Release lock |
215 | + |
216 | + try |
217 | + { |
218 | + // Forward the cancellation to the query base (which in turn will forward it to any subqueries). |
219 | + // The query base also calls the cancelled() callback to inform the application code. |
220 | + query_base_->cancel(); |
221 | |
222 | auto rp = reply_proxy_.lock(); |
223 | if (rp) |
224 | @@ -154,11 +167,24 @@ |
225 | // a CompletionDetails::CompletionStatus (whereas the public ReplyProxy does not). |
226 | reply_->finished(CompletionDetails(CompletionDetails::Cancelled)); // Oneway, can't block |
227 | } |
228 | - } // Release lock |
229 | - |
230 | - // Forward the cancellation to the query base (which in turn will forward it to any subqueries). |
231 | - // The query base also calls the cancelled() callback to inform the application code. |
232 | - query_base_->cancel(); |
233 | + } |
234 | + catch (std::exception const& e) |
235 | + { |
236 | + BOOST_LOG(info.mw->runtime()->logger()) << "QueryBase::cancelled(): " << e.what(); |
237 | + // Deliberately no error completion here. On the client side, we short-cut the cancelled() |
238 | + // callback locally, which unregisters the servant for the cancel message and assumes that |
239 | + // cancellation will work, passing a Cancelled status, even if cancellation throws in the scope. |
240 | + // But, removing the servant may be slow, allowing the finished message below to occasionally |
241 | + // reach the client. If it does, we want the same completion status that we get if the |
242 | + // local short-cut call hasn't done the job yet. |
243 | + reply_->finished(CompletionDetails(CompletionDetails::Cancelled, "")); |
244 | + } |
245 | + catch (...) |
246 | + { |
247 | + BOOST_LOG(info.mw->runtime()->logger()) << "QueryBase::cancelled(): unknown exception"; |
248 | + // Same caveat as for std::exception here. |
249 | + reply_->finished(CompletionDetails(CompletionDetails::Cancelled, "")); |
250 | + } |
251 | } |
252 | |
253 | bool QueryObject::pushable(InvokeInfo const& /* info */) const noexcept |
254 | |
255 | === modified file 'src/scopes/internal/ReplyObject.cpp' |
256 | --- src/scopes/internal/ReplyObject.cpp 2014-12-10 02:05:26 +0000 |
257 | +++ src/scopes/internal/ReplyObject.cpp 2015-02-04 05:47:34 +0000 |
258 | @@ -40,19 +40,20 @@ |
259 | namespace internal |
260 | { |
261 | |
262 | -ReplyObject::ReplyObject(ListenerBase::SPtr const& receiver_base, RuntimeImpl const* runtime, |
263 | - std::string const& scope_proxy, bool dont_reap) : |
264 | - runtime_(runtime), |
265 | - listener_base_(receiver_base), |
266 | - finished_(false), |
267 | - origin_proxy_(scope_proxy), |
268 | - num_push_(0), |
269 | - info_occurred_(false) |
270 | +ReplyObject::ReplyObject(ListenerBase::SPtr const& receiver_base, |
271 | + RuntimeImpl const* runtime, |
272 | + std::string const& scope_proxy, |
273 | + bool dont_reap) |
274 | + : runtime_(runtime) |
275 | + , listener_base_(receiver_base) |
276 | + , finished_(false) |
277 | + , origin_proxy_(scope_proxy) |
278 | + , num_push_(0) |
279 | { |
280 | assert(receiver_base); |
281 | assert(runtime); |
282 | |
283 | - if (dont_reap == false) |
284 | + if (!dont_reap) |
285 | { |
286 | unique_lock<mutex> lock(mutex_); // Make sure that when lambda fires, it sees current values. |
287 | reap_item_ = runtime->reply_reaper()->add([this] { |
288 | @@ -134,12 +135,11 @@ |
289 | // Safe to call finished now if something went wrong or cardinality was exceeded. |
290 | if (!error.empty()) |
291 | { |
292 | - // TODO: log error |
293 | + BOOST_LOG(runtime_->logger()) << "ReplyObject::push(): " << error; |
294 | finished(CompletionDetails(CompletionDetails::Error, error)); |
295 | } |
296 | else if (stop) |
297 | { |
298 | - // TODO: log error |
299 | finished(CompletionDetails(CompletionDetails::OK)); |
300 | } |
301 | } |
302 | @@ -171,7 +171,6 @@ |
303 | unique_lock<mutex> lock(mutex_); |
304 | assert(num_push_ >= 0); |
305 | idle_.wait(lock, [this] { return num_push_ == 0; }); |
306 | - lock.unlock(); // Inform the application code that the query is complete outside synchronization. |
307 | try |
308 | { |
309 | CompletionDetails details_with_info(details); |
310 | @@ -179,6 +178,7 @@ |
311 | { |
312 | details_with_info.add_info(info); |
313 | } |
314 | + lock.unlock(); // Inform the application code that the query is complete outside synchronization. |
315 | listener_base_->finished(details_with_info); |
316 | } |
317 | catch (std::exception const& e) |
318 | @@ -208,11 +208,13 @@ |
319 | { |
320 | reap_item_->refresh(); |
321 | } |
322 | - info_occurred_.exchange(true); |
323 | |
324 | try |
325 | { |
326 | - info_list_.push_back(op_info); |
327 | + { |
328 | + unique_lock<mutex> lock(mutex_); |
329 | + info_list_.push_back(op_info); |
330 | + } |
331 | listener_base_->info(op_info); |
332 | } |
333 | catch (std::exception const& e) |
334 | |
335 | === modified file 'src/scopes/internal/ScopeObject.cpp' |
336 | --- src/scopes/internal/ScopeObject.cpp 2015-01-26 08:20:45 +0000 |
337 | +++ src/scopes/internal/ScopeObject.cpp 2015-02-04 05:47:34 +0000 |
338 | @@ -59,7 +59,9 @@ |
339 | { |
340 | } |
341 | |
342 | -MWQueryCtrlProxy ScopeObject::query(MWReplyProxy const& reply, MiddlewareBase* mw_base, |
343 | +MWQueryCtrlProxy ScopeObject::query(MWReplyProxy const& reply, |
344 | + MiddlewareBase* mw_base, |
345 | + std::string const& method, |
346 | std::function<QueryBase::SPtr()> const& query_factory_fun, |
347 | std::function<QueryObjectBase::SPtr(QueryBase::SPtr, MWQueryCtrlProxy)> const& query_object_factory_fun) |
348 | { |
349 | @@ -70,7 +72,8 @@ |
350 | // to be safe, we don't assert, in case someone is running a broken client. |
351 | |
352 | // TODO: log error about incoming request containing an invalid reply proxy. |
353 | - throw LogicException("Scope \"" + mw_base->runtime()->scope_id() + "\": query() called with null reply proxy"); |
354 | + throw LogicException("Scope \"" + mw_base->runtime()->scope_id() + "\": " |
355 | + + method + " called with null reply proxy"); |
356 | } |
357 | |
358 | // Ask scope to instantiate a new query. |
359 | @@ -80,7 +83,7 @@ |
360 | query_base = query_factory_fun(); |
361 | if (!query_base) |
362 | { |
363 | - string msg = "Scope \"" + mw_base->runtime()->scope_id() + "\" returned nullptr from query_factory_fun()"; |
364 | + string msg = "Scope \"" + mw_base->runtime()->scope_id() + "\" returned nullptr from " + method + "()"; |
365 | BOOST_LOG(mw_base->runtime()->logger()) << msg; |
366 | throw ResourceException(msg); |
367 | } |
368 | @@ -88,7 +91,7 @@ |
369 | } |
370 | catch (...) |
371 | { |
372 | - string msg = "Scope \"" + mw_base->runtime()->scope_id() + "\" threw an exception from query_factory_fun()"; |
373 | + string msg = "Scope \"" + mw_base->runtime()->scope_id() + "\" threw an exception from " + method + "()"; |
374 | BOOST_LOG(mw_base->runtime()->logger()) << msg; |
375 | throw ResourceException(msg); |
376 | } |
377 | @@ -151,45 +154,47 @@ |
378 | MWReplyProxy const& reply, |
379 | InvokeInfo const& info) |
380 | { |
381 | - return query(reply, info.mw, |
382 | - [&q, &hints, &context, this]() -> SearchQueryBase::UPtr { |
383 | - auto search_query = this->scope_base_->search(q, hints); |
384 | - search_query->set_department_id(q.department_id()); |
385 | - |
386 | - auto sqb = dynamic_cast<SearchQueryBaseImpl*>(search_query->fwd()); |
387 | - assert(sqb); |
388 | - |
389 | - // Set client ID and history that we received in the SearchQueryBase |
390 | - // for loop detection. |
391 | - auto const c_it = context.find("client_id"); |
392 | - if (c_it != context.end()) |
393 | - { |
394 | - string client_id; |
395 | - client_id = c_it->second.get_string(); |
396 | - sqb->set_client_id(client_id); |
397 | - } |
398 | - |
399 | - auto const h_it = context.find("history"); |
400 | - if (h_it != context.end()) |
401 | - { |
402 | - auto const hlist = h_it->second.get_array(); |
403 | - SearchQueryBaseImpl::History history; |
404 | - for (auto const& t : hlist) |
405 | - { |
406 | - string client_id = t.get_dict()["c"].get_string(); |
407 | - string agg = t.get_dict()["a"].get_string(); |
408 | - string recv = t.get_dict()["r"].get_string(); |
409 | - SearchQueryBaseImpl::HistoryData hd = make_tuple(client_id, agg, recv); |
410 | - history.push_back(hd); |
411 | - } |
412 | - sqb->set_history(history); |
413 | - } |
414 | - |
415 | - return search_query; |
416 | - }, |
417 | - [&reply, &hints, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
418 | - return make_shared<QueryObject>(query_base, hints.cardinality(), reply, ctrl_proxy); |
419 | - } |
420 | + return query(reply, |
421 | + info.mw, |
422 | + "search", |
423 | + [&q, &hints, &context, this]() -> SearchQueryBase::UPtr { |
424 | + auto search_query = this->scope_base_->search(q, hints); |
425 | + search_query->set_department_id(q.department_id()); |
426 | + |
427 | + auto sqb = dynamic_cast<SearchQueryBaseImpl*>(search_query->fwd()); |
428 | + assert(sqb); |
429 | + |
430 | + // Set client ID and history that we received in the SearchQueryBase |
431 | + // for loop detection. |
432 | + auto const c_it = context.find("client_id"); |
433 | + if (c_it != context.end()) |
434 | + { |
435 | + string client_id; |
436 | + client_id = c_it->second.get_string(); |
437 | + sqb->set_client_id(client_id); |
438 | + } |
439 | + |
440 | + auto const h_it = context.find("history"); |
441 | + if (h_it != context.end()) |
442 | + { |
443 | + auto const hlist = h_it->second.get_array(); |
444 | + SearchQueryBaseImpl::History history; |
445 | + for (auto const& t : hlist) |
446 | + { |
447 | + string client_id = t.get_dict()["c"].get_string(); |
448 | + string agg = t.get_dict()["a"].get_string(); |
449 | + string recv = t.get_dict()["r"].get_string(); |
450 | + SearchQueryBaseImpl::HistoryData hd = make_tuple(client_id, agg, recv); |
451 | + history.push_back(hd); |
452 | + } |
453 | + sqb->set_history(history); |
454 | + } |
455 | + |
456 | + return search_query; |
457 | + }, |
458 | + [&reply, &hints, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
459 | + return make_shared<QueryObject>(query_base, hints.cardinality(), reply, ctrl_proxy); |
460 | + } |
461 | ); |
462 | } |
463 | |
464 | @@ -198,15 +203,17 @@ |
465 | MWReplyProxy const& reply, |
466 | InvokeInfo const& info) |
467 | { |
468 | - return query(reply, info.mw, |
469 | - [&result, &hints, this]() -> QueryBase::SPtr { |
470 | - return this->scope_base_->activate(result, hints); |
471 | - }, |
472 | - [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
473 | - auto activation_base = dynamic_pointer_cast<ActivationQueryBase>(query_base); |
474 | - assert(activation_base); |
475 | - return make_shared<ActivationQueryObject>(activation_base, reply, ctrl_proxy); |
476 | - } |
477 | + return query(reply, |
478 | + info.mw, |
479 | + "activate", |
480 | + [&result, &hints, this]() -> QueryBase::SPtr { |
481 | + return this->scope_base_->activate(result, hints); |
482 | + }, |
483 | + [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
484 | + auto activation_base = dynamic_pointer_cast<ActivationQueryBase>(query_base); |
485 | + assert(activation_base); |
486 | + return make_shared<ActivationQueryObject>(activation_base, reply, ctrl_proxy); |
487 | + } |
488 | ); |
489 | } |
490 | |
491 | @@ -217,15 +224,17 @@ |
492 | MWReplyProxy const &reply, |
493 | InvokeInfo const& info) |
494 | { |
495 | - return query(reply, info.mw, |
496 | - [&result, &hints, &widget_id, &action_id, this]() -> QueryBase::SPtr { |
497 | - return this->scope_base_->perform_action(result, hints, widget_id, action_id); |
498 | - }, |
499 | - [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
500 | - auto activation_base = dynamic_pointer_cast<ActivationQueryBase>(query_base); |
501 | - assert(activation_base); |
502 | - return make_shared<ActivationQueryObject>(activation_base, reply, ctrl_proxy); |
503 | - } |
504 | + return query(reply, |
505 | + info.mw, |
506 | + "perform_action", |
507 | + [&result, &hints, &widget_id, &action_id, this]() -> QueryBase::SPtr { |
508 | + return this->scope_base_->perform_action(result, hints, widget_id, action_id); |
509 | + }, |
510 | + [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
511 | + auto activation_base = dynamic_pointer_cast<ActivationQueryBase>(query_base); |
512 | + assert(activation_base); |
513 | + return make_shared<ActivationQueryObject>(activation_base, reply, ctrl_proxy); |
514 | + } |
515 | ); |
516 | } |
517 | |
518 | @@ -234,15 +243,17 @@ |
519 | MWReplyProxy const& reply, |
520 | InvokeInfo const& info) |
521 | { |
522 | - return query(reply, info.mw, |
523 | - [&result, &hints, this]() -> QueryBase::SPtr { |
524 | - return this->scope_base_->preview(result, hints); |
525 | - }, |
526 | - [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
527 | - auto preview_query = dynamic_pointer_cast<PreviewQueryBase>(query_base); |
528 | - assert(preview_query); |
529 | - return make_shared<PreviewQueryObject>(preview_query, reply, ctrl_proxy); |
530 | - } |
531 | + return query(reply, |
532 | + info.mw, |
533 | + "preview", |
534 | + [&result, &hints, this]() -> QueryBase::SPtr { |
535 | + return this->scope_base_->preview(result, hints); |
536 | + }, |
537 | + [&reply, this](QueryBase::SPtr query_base, MWQueryCtrlProxy ctrl_proxy) -> QueryObjectBase::SPtr { |
538 | + auto preview_query = dynamic_pointer_cast<PreviewQueryBase>(query_base); |
539 | + assert(preview_query); |
540 | + return make_shared<PreviewQueryObject>(preview_query, reply, ctrl_proxy); |
541 | + } |
542 | ); |
543 | } |
544 | |
545 | |
546 | === modified file 'test/gtest/scopes/CMakeLists.txt' |
547 | --- test/gtest/scopes/CMakeLists.txt 2015-01-26 08:20:45 +0000 |
548 | +++ test/gtest/scopes/CMakeLists.txt 2015-02-04 05:47:34 +0000 |
549 | @@ -35,6 +35,7 @@ |
550 | add_subdirectory(Runtime) |
551 | add_subdirectory(ScopeBase) |
552 | add_subdirectory(ScopeExceptions) |
553 | +add_subdirectory(ThrowingScope) |
554 | add_subdirectory(Variant) |
555 | add_subdirectory(VariantBuilder) |
556 | add_subdirectory(Version) |
557 | |
558 | === added directory 'test/gtest/scopes/ThrowingScope' |
559 | === added file 'test/gtest/scopes/ThrowingScope/CMakeLists.txt' |
560 | --- test/gtest/scopes/ThrowingScope/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
561 | +++ test/gtest/scopes/ThrowingScope/CMakeLists.txt 2015-02-04 05:47:34 +0000 |
562 | @@ -0,0 +1,26 @@ |
563 | +configure_file(TestRegistry.ini.in ${CMAKE_CURRENT_BINARY_DIR}/TestRegistry.ini) |
564 | +configure_file(Runtime.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini) |
565 | +configure_file(Zmq.ini.in ${CMAKE_CURRENT_BINARY_DIR}/Zmq.ini) |
566 | + |
567 | +add_definitions(-DTEST_RUNTIME_PATH="${CMAKE_CURRENT_BINARY_DIR}") |
568 | +add_definitions(-DTEST_RUNTIME_FILE="${CMAKE_CURRENT_BINARY_DIR}/Runtime.ini") |
569 | +add_definitions(-DTEST_REGISTRY_PATH="${PROJECT_BINARY_DIR}/scoperegistry") |
570 | + |
571 | +add_executable(ThrowingScope_test ThrowingScope_test.cpp ThrowingScope.cpp) |
572 | +target_link_libraries(ThrowingScope_test ${TESTLIBS}) |
573 | + |
574 | +add_dependencies(ThrowingScope_test scoperegistry scoperunner) |
575 | + |
576 | +add_test(ThrowingScope ThrowingScope_test) |
577 | + |
578 | +set(SCOPE_DIR "${CMAKE_CURRENT_BINARY_DIR}/scopes") |
579 | + |
580 | +foreach (scope ThrowingScope) |
581 | + file(MAKE_DIRECTORY "${SCOPE_DIR}/${scope}") |
582 | + configure_file(ThrowingScope.ini.in ${SCOPE_DIR}/${scope}/${scope}.ini) |
583 | + add_library(${scope} MODULE ThrowingScope.cpp) |
584 | + set_target_properties(${scope} |
585 | + PROPERTIES |
586 | + LIBRARY_OUTPUT_DIRECTORY "${SCOPE_DIR}/${scope}/" |
587 | + ) |
588 | +endforeach() |
589 | |
590 | === added file 'test/gtest/scopes/ThrowingScope/Runtime.ini.in' |
591 | --- test/gtest/scopes/ThrowingScope/Runtime.ini.in 1970-01-01 00:00:00 +0000 |
592 | +++ test/gtest/scopes/ThrowingScope/Runtime.ini.in 2015-02-04 05:47:34 +0000 |
593 | @@ -0,0 +1,7 @@ |
594 | +[Runtime] |
595 | +Registry.Identity = TestRegistry |
596 | +Registry.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/TestRegistry.ini |
597 | +Default.Middleware = Zmq |
598 | +Zmq.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/Zmq.ini |
599 | +Smartscopes.Registry.Identity = |
600 | +LogDir= |
601 | |
602 | === added file 'test/gtest/scopes/ThrowingScope/TestRegistry.ini.in' |
603 | --- test/gtest/scopes/ThrowingScope/TestRegistry.ini.in 1970-01-01 00:00:00 +0000 |
604 | +++ test/gtest/scopes/ThrowingScope/TestRegistry.ini.in 2015-02-04 05:47:34 +0000 |
605 | @@ -0,0 +1,7 @@ |
606 | +[Registry] |
607 | +Middleware = Zmq |
608 | +Zmq.ConfigFile = @CMAKE_CURRENT_BINARY_DIR@/Zmq.ini |
609 | +Scope.InstallDir = @CMAKE_CURRENT_BINARY_DIR@/scopes |
610 | +OEM.InstallDir = /unused |
611 | +Click.InstallDir = @CMAKE_CURRENT_BINARY_DIR@/click |
612 | +Scoperunner.Path = @PROJECT_BINARY_DIR@/scoperunner/scoperunner |
613 | |
614 | === added file 'test/gtest/scopes/ThrowingScope/ThrowingScope.cpp' |
615 | --- test/gtest/scopes/ThrowingScope/ThrowingScope.cpp 1970-01-01 00:00:00 +0000 |
616 | +++ test/gtest/scopes/ThrowingScope/ThrowingScope.cpp 2015-02-04 05:47:34 +0000 |
617 | @@ -0,0 +1,273 @@ |
618 | +/* |
619 | + * Copyright (C) 2015 Canonical Ltd |
620 | + * |
621 | + * This program is free software: you can redistribute it and/or modify |
622 | + * it under the terms of the GNU Lesser General Public License version 3 as |
623 | + * published by the Free Software Foundation. |
624 | + * |
625 | + * This program is distributed in the hope that it will be useful, |
626 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
627 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
628 | + * GNU Lesser General Public License for more details. |
629 | + * |
630 | + * You should have received a copy of the GNU Lesser General Public License |
631 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
632 | + * |
633 | + * Authored by: Michi Henning <michi.henning@canonical.com> |
634 | + */ |
635 | + |
636 | +#include "ThrowingScope.h" |
637 | + |
638 | +#include <unity/scopes/ActionMetadata.h> |
639 | +#include <unity/scopes/CategorisedResult.h> |
640 | +#include <unity/scopes/ScopeBase.h> |
641 | +#include <unity/scopes/SearchReply.h> |
642 | + |
643 | +#include <unity/UnityExceptions.h> |
644 | +#include <unity/util/FileIO.h> |
645 | + |
646 | +#include <condition_variable> |
647 | +#include <mutex> |
648 | +#include <thread> |
649 | + |
650 | +using namespace std; |
651 | +using namespace unity; |
652 | +using namespace unity::scopes; |
653 | + |
654 | +namespace |
655 | +{ |
656 | + |
657 | +class TestQuery : public SearchQueryBase |
658 | +{ |
659 | +public: |
660 | + TestQuery(CannedQuery const& query, |
661 | + SearchMetadata const& metadata, |
662 | + string const& id) |
663 | + : SearchQueryBase(query, metadata) |
664 | + , id_(id) |
665 | + , query_cancelled_(false) |
666 | + { |
667 | + } |
668 | + |
669 | + virtual void cancelled() override |
670 | + { |
671 | + if (query().query_string() == "throw from cancelled") |
672 | + { |
673 | + lock_guard<mutex> lock(mutex_); |
674 | + query_cancelled_ = true; |
675 | + cond_.notify_all(); |
676 | + throw ResourceException("exception from cancelled"); |
677 | + } |
678 | + } |
679 | + |
680 | + virtual void run(SearchReplyProxy const& reply) override |
681 | + { |
682 | + string query_string = query().query_string(); |
683 | + if (query_string == "throw from run") |
684 | + { |
685 | + throw ResourceException("exception from run"); |
686 | + } |
687 | + else if (query_string == "throw from cancelled") |
688 | + { |
689 | + unique_lock<mutex> lock(mutex_); |
690 | + cond_.wait(lock, [this] { return this->query_cancelled_; }); |
691 | + return; |
692 | + } |
693 | + auto cat = reply->register_category(id_, "", ""); |
694 | + CategorisedResult res(cat); |
695 | + res.set_uri("uri"); |
696 | + res.set_title(query_string); |
697 | + res.set_intercept_activation(); |
698 | + if (valid()) |
699 | + { |
700 | + reply->push(res); |
701 | + } |
702 | + // Need to wait a while here, to make sure that the exception thrown from cancelled() |
703 | + // is picked up by the runtime and the cancelled message is sent back to the client. |
704 | + // If we don't do this, a successful completion message will be sent as soon as |
705 | + // we return from here, and that might happen before the runtime has managed to send the |
706 | + // cancelled message, causing the test to fail because then we see successful |
707 | + // completion instead of cancelled completion. |
708 | + this_thread::sleep_for(chrono::milliseconds(200)); |
709 | + } |
710 | + |
711 | +private: |
712 | + string id_; |
713 | + bool query_cancelled_; |
714 | + mutex mutex_; |
715 | + condition_variable cond_; |
716 | +}; |
717 | + |
718 | +class TestPreview : public PreviewQueryBase |
719 | +{ |
720 | +public: |
721 | + TestPreview(Result const& result, ActionMetadata const& metadata) |
722 | + : PreviewQueryBase(result, metadata) |
723 | + , result_(result) |
724 | + , preview_cancelled_(false) |
725 | + { |
726 | + } |
727 | + |
728 | + virtual void cancelled() override |
729 | + { |
730 | + if (result_.title() == "throw from preview cancelled") |
731 | + { |
732 | + lock_guard<mutex> lock(mutex_); |
733 | + preview_cancelled_ = true; |
734 | + cond_.notify_all(); |
735 | + throw ResourceException("exception from preview cancelled"); |
736 | + } |
737 | + } |
738 | + |
739 | + virtual void run(PreviewReplyProxy const&) override |
740 | + { |
741 | + if (result_.title() == "throw from preview run") |
742 | + { |
743 | + throw ResourceException(result_.title()); |
744 | + } |
745 | + else if (result_.title() == "throw from preview cancelled") |
746 | + { |
747 | + unique_lock<mutex> lock(mutex_); |
748 | + cond_.wait(lock, [this] { return this->preview_cancelled_; }); |
749 | + // Need to wait a while here, to make sure that the exception thrown from cancelled() |
750 | + // is picked up by the runtime and the cancelled message is sent back to the client. |
751 | + // If we don't do this, a successful completion message will be sent as soon as |
752 | + // we return from here, and that might happen before the runtime has managed to send the |
753 | + // cancelled message, causing the test to fail because then we see successful |
754 | + // completion instead of cancelled completion. |
755 | + this_thread::sleep_for(chrono::milliseconds(200)); |
756 | + } |
757 | + } |
758 | + |
759 | +private: |
760 | + Result result_; |
761 | + bool preview_cancelled_; |
762 | + mutex mutex_; |
763 | + condition_variable cond_; |
764 | +}; |
765 | + |
766 | +class TestActivation : public ActivationQueryBase |
767 | +{ |
768 | +public: |
769 | + TestActivation(Result const& result, ActionMetadata const& metadata) |
770 | + : ActivationQueryBase(result, metadata) |
771 | + , result_(result) |
772 | + , activate_cancelled_(false) |
773 | + { |
774 | + } |
775 | + |
776 | + virtual void cancelled() override |
777 | + { |
778 | + if (result_.title() == "throw from activation cancelled") |
779 | + { |
780 | + lock_guard<mutex> lock(mutex_); |
781 | + activate_cancelled_ = true; |
782 | + cond_.notify_all(); |
783 | + throw ResourceException("exception from activation cancelled"); |
784 | + } |
785 | + } |
786 | + |
787 | + virtual ActivationResponse activate() override |
788 | + { |
789 | + if (result_.title() == "throw from activation activate") |
790 | + { |
791 | + throw ResourceException(result_.title()); |
792 | + } |
793 | + else if (result_.title() == "throw from activation cancelled") |
794 | + { |
795 | + unique_lock<mutex> lock(mutex_); |
796 | + cond_.wait(lock, [this] { return this->activate_cancelled_; }); |
797 | + // Need to wait a while here, to make sure that the exception thrown from cancelled() |
798 | + // is picked up by the runtime and the cancelled message is sent back to the client. |
799 | + // If we don't do this, a successful completion message will be sent as soon as |
800 | + // we return from here, and that might happen before the runtime has managed to send the |
801 | + // cancelled message, causing the test to fail because then we see successful |
802 | + // completion instead of cancelled completion. |
803 | + this_thread::sleep_for(chrono::milliseconds(200)); |
804 | + } |
805 | + return ActivationResponse(ActivationResponse::NotHandled); |
806 | + } |
807 | + |
808 | +private: |
809 | + Result result_; |
810 | + bool activate_cancelled_; |
811 | + mutex mutex_; |
812 | + condition_variable cond_; |
813 | +}; |
814 | + |
815 | +} // namespace |
816 | + |
817 | +void ThrowingScope::start(string const& scope_id) |
818 | +{ |
819 | + lock_guard<mutex> lock(mutex_); |
820 | + id_ = scope_id; |
821 | +} |
822 | + |
823 | +void ThrowingScope::stop() |
824 | +{ |
825 | +} |
826 | + |
827 | +void ThrowingScope::run() |
828 | +{ |
829 | +} |
830 | + |
831 | +SearchQueryBase::UPtr ThrowingScope::search(CannedQuery const& query, SearchMetadata const& metadata) |
832 | +{ |
833 | + if (query.query_string() == "throw from search") |
834 | + { |
835 | + throw ResourceException("exception from search"); |
836 | + } |
837 | + lock_guard<mutex> lock(mutex_); |
838 | + return SearchQueryBase::UPtr(new TestQuery(query, metadata, id_)); |
839 | +} |
840 | + |
841 | +PreviewQueryBase::UPtr ThrowingScope::preview(Result const& result, ActionMetadata const& metadata) |
842 | +{ |
843 | + if (result.title() == "throw from preview") |
844 | + { |
845 | + throw ResourceException("exception from preview"); |
846 | + } |
847 | + lock_guard<mutex> lock(mutex_); |
848 | + return PreviewQueryBase::UPtr(new TestPreview(result, metadata)); |
849 | +} |
850 | + |
851 | +ActivationQueryBase::UPtr ThrowingScope::activate(Result const& result, ActionMetadata const& metadata) |
852 | +{ |
853 | + if (result.title() == "throw from activate") |
854 | + { |
855 | + throw ResourceException("exception from activate"); |
856 | + } |
857 | + lock_guard<mutex> lock(mutex_); |
858 | + return ActivationQueryBase::UPtr(new TestActivation(result, metadata)); |
859 | +} |
860 | + |
861 | +ActivationQueryBase::UPtr ThrowingScope::perform_action(Result const& result, |
862 | + ActionMetadata const& metadata, |
863 | + string const& /* widget_id */, |
864 | + string const& /* action_id */) |
865 | +{ |
866 | + if (result.title() == "throw from perform_action") |
867 | + { |
868 | + throw ResourceException("exception from perform_action"); |
869 | + } |
870 | + lock_guard<mutex> lock(mutex_); |
871 | + return ActivationQueryBase::UPtr(new TestActivation(result, metadata)); |
872 | +} |
873 | + |
874 | +extern "C" |
875 | +{ |
876 | + |
877 | + unity::scopes::ScopeBase* |
878 | + // cppcheck-suppress unusedFunction |
879 | + UNITY_SCOPE_CREATE_FUNCTION() |
880 | + { |
881 | + return new ThrowingScope; |
882 | + } |
883 | + |
884 | + void |
885 | + // cppcheck-suppress unusedFunction |
886 | + UNITY_SCOPE_DESTROY_FUNCTION(unity::scopes::ScopeBase* scope_base) |
887 | + { |
888 | + delete scope_base; |
889 | + } |
890 | +} |
891 | |
892 | === added file 'test/gtest/scopes/ThrowingScope/ThrowingScope.h' |
893 | --- test/gtest/scopes/ThrowingScope/ThrowingScope.h 1970-01-01 00:00:00 +0000 |
894 | +++ test/gtest/scopes/ThrowingScope/ThrowingScope.h 2015-02-04 05:47:34 +0000 |
895 | @@ -0,0 +1,49 @@ |
896 | +/* |
897 | + * Copyright (C) 2015 Canonical Ltd |
898 | + * |
899 | + * This program is free software: you can redistribute it and/or modify |
900 | + * it under the terms of the GNU Lesser General Public License version 3 as |
901 | + * published by the Free Software Foundation. |
902 | + * |
903 | + * This program is distributed in the hope that it will be useful, |
904 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
905 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
906 | + * GNU Lesser General Public License for more details. |
907 | + * |
908 | + * You should have received a copy of the GNU Lesser General Public License |
909 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
910 | + * |
911 | + * Authored by: Michi Henning <michi.henning@canonical.com> |
912 | + */ |
913 | + |
914 | +#pragma once |
915 | + |
916 | +#include <unity/scopes/ScopeBase.h> |
917 | + |
918 | +class ThrowingScope : public unity::scopes::ScopeBase |
919 | +{ |
920 | +public: |
921 | + virtual void start(std::string const&) override; |
922 | + |
923 | + virtual void stop() override; |
924 | + |
925 | + virtual void run() override; |
926 | + |
927 | + virtual unity::scopes::SearchQueryBase::UPtr search(unity::scopes::CannedQuery const &, |
928 | + unity::scopes::SearchMetadata const &) override; |
929 | + |
930 | + virtual unity::scopes::PreviewQueryBase::UPtr preview(unity::scopes::Result const&, |
931 | + unity::scopes::ActionMetadata const &) override; |
932 | + |
933 | + virtual unity::scopes::ActivationQueryBase::UPtr activate(unity::scopes::Result const&, |
934 | + unity::scopes::ActionMetadata const &) override; |
935 | + |
936 | + virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const&, |
937 | + unity::scopes::ActionMetadata const &, |
938 | + std::string const &, |
939 | + std::string const&) override; |
940 | + |
941 | +private: |
942 | + std::string id_; |
943 | + std::mutex mutex_; |
944 | +}; |
945 | |
946 | === added file 'test/gtest/scopes/ThrowingScope/ThrowingScope.ini.in' |
947 | --- test/gtest/scopes/ThrowingScope/ThrowingScope.ini.in 1970-01-01 00:00:00 +0000 |
948 | +++ test/gtest/scopes/ThrowingScope/ThrowingScope.ini.in 2015-02-04 05:47:34 +0000 |
949 | @@ -0,0 +1,4 @@ |
950 | +[ScopeConfig] |
951 | +DisplayName = LoopScope |
952 | +Description = Aggregator scope that makes various loops |
953 | +Author = Michi |
954 | |
955 | === added file 'test/gtest/scopes/ThrowingScope/ThrowingScope_test.cpp' |
956 | --- test/gtest/scopes/ThrowingScope/ThrowingScope_test.cpp 1970-01-01 00:00:00 +0000 |
957 | +++ test/gtest/scopes/ThrowingScope/ThrowingScope_test.cpp 2015-02-04 05:47:34 +0000 |
958 | @@ -0,0 +1,477 @@ |
959 | +/* |
960 | + * Copyright (C) 2015 Canonical Ltd |
961 | + * |
962 | + * This program is free software: you can redistribute it and/or modify |
963 | + * it under the terms of the GNU Lesser General Public License version 3 as |
964 | + * published by the Free Software Foundation. |
965 | + * |
966 | + * This program is distributed in the hope that it will be useful, |
967 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
968 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
969 | + * GNU Lesser General Public License for more details. |
970 | + * |
971 | + * You should have received a copy of the GNU Lesser General Public License |
972 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
973 | + * |
974 | + * Authored by: Michi Henning <michi.henning@canonical.com> |
975 | + */ |
976 | + |
977 | +#include <unity/scopes/CategorisedResult.h> |
978 | +#include <unity/scopes/internal/RegistryObject.h> |
979 | +#include <unity/scopes/internal/RuntimeImpl.h> |
980 | +#include <unity/scopes/internal/ScopeImpl.h> |
981 | +#include <unity/scopes/QueryCtrl.h> |
982 | + |
983 | +#include <gtest/gtest.h> |
984 | + |
985 | +#include "ThrowingScope.h" |
986 | + |
987 | +using namespace std; |
988 | +using namespace unity::scopes; |
989 | +using namespace unity::scopes::internal; |
990 | + |
991 | +class Receiver : public SearchListenerBase |
992 | +{ |
993 | +public: |
994 | + Receiver() |
995 | + : query_complete_(false) |
996 | + { |
997 | + } |
998 | + |
999 | + virtual void push(CategorisedResult result) override |
1000 | + { |
1001 | + lock_guard<mutex> lock(mutex_); |
1002 | + |
1003 | + results_.push_back(move(result)); |
1004 | + } |
1005 | + |
1006 | + virtual void finished(CompletionDetails const& details) override |
1007 | + { |
1008 | + lock_guard<mutex> lock(mutex_); |
1009 | + |
1010 | + completion_status_ = details.status(); |
1011 | + completion_msg_ = details.message(); |
1012 | + query_complete_ = true; |
1013 | + cond_.notify_one(); |
1014 | + } |
1015 | + |
1016 | + void wait_until_finished() |
1017 | + { |
1018 | + unique_lock<mutex> lock(mutex_); |
1019 | + |
1020 | + cond_.wait(lock, [this] { return this->query_complete_; }); |
1021 | + query_complete_ = false; |
1022 | + } |
1023 | + |
1024 | + CompletionDetails::CompletionStatus completion_status() |
1025 | + { |
1026 | + lock_guard<mutex> lock(mutex_); |
1027 | + |
1028 | + return completion_status_; |
1029 | + } |
1030 | + |
1031 | + string completion_msg() |
1032 | + { |
1033 | + lock_guard<mutex> lock(mutex_); |
1034 | + |
1035 | + return completion_msg_; |
1036 | + } |
1037 | + |
1038 | + vector<CategorisedResult> results() |
1039 | + { |
1040 | + lock_guard<mutex> lock(mutex_); |
1041 | + |
1042 | + return results_; |
1043 | + } |
1044 | + |
1045 | +private: |
1046 | + vector<CategorisedResult> results_; |
1047 | + bool query_complete_; |
1048 | + string completion_msg_; |
1049 | + CompletionDetails::CompletionStatus completion_status_; |
1050 | + mutex mutex_; |
1051 | + condition_variable cond_; |
1052 | +}; |
1053 | + |
1054 | +class PreviewListener : public PreviewListenerBase |
1055 | +{ |
1056 | +public: |
1057 | + PreviewListener() |
1058 | + : preview_complete_(false) |
1059 | + { |
1060 | + } |
1061 | + |
1062 | + void finished(const unity::scopes::CompletionDetails& details) override |
1063 | + { |
1064 | + lock_guard<mutex> lock(mutex_); |
1065 | + |
1066 | + completion_status_ = details.status(); |
1067 | + completion_msg_ = details.message(); |
1068 | + preview_complete_ = true; |
1069 | + cond_.notify_one(); |
1070 | + } |
1071 | + |
1072 | + void wait_until_finished() |
1073 | + { |
1074 | + unique_lock<mutex> lock(mutex_); |
1075 | + |
1076 | + cond_.wait(lock, [this] { return this->preview_complete_; }); |
1077 | + preview_complete_ = false; |
1078 | + } |
1079 | + |
1080 | + CompletionDetails::CompletionStatus completion_status() |
1081 | + { |
1082 | + lock_guard<mutex> lock(mutex_); |
1083 | + |
1084 | + return completion_status_; |
1085 | + } |
1086 | + |
1087 | + string completion_msg() |
1088 | + { |
1089 | + lock_guard<mutex> lock(mutex_); |
1090 | + |
1091 | + return completion_msg_; |
1092 | + } |
1093 | + |
1094 | + void push(ColumnLayoutList const&) override |
1095 | + { |
1096 | + } |
1097 | + |
1098 | + void push(PreviewWidgetList const&) override |
1099 | + { |
1100 | + } |
1101 | + |
1102 | + void push(std::string const&, Variant const&) override |
1103 | + { |
1104 | + } |
1105 | + |
1106 | +private: |
1107 | + bool preview_complete_; |
1108 | + string completion_msg_; |
1109 | + CompletionDetails::CompletionStatus completion_status_; |
1110 | + mutex mutex_; |
1111 | + condition_variable cond_; |
1112 | +}; |
1113 | + |
1114 | +class ActivationListener : public ActivationListenerBase |
1115 | +{ |
1116 | +public: |
1117 | + ActivationListener() |
1118 | + : activate_complete_(false) |
1119 | + { |
1120 | + } |
1121 | + |
1122 | + void activated(ActivationResponse const& response) override |
1123 | + { |
1124 | + lock_guard<mutex> lock(mutex_); |
1125 | + |
1126 | + act_status_ = response.status(); |
1127 | + } |
1128 | + |
1129 | + void finished(const unity::scopes::CompletionDetails& details) override |
1130 | + { |
1131 | + lock_guard<mutex> lock(mutex_); |
1132 | + |
1133 | + completion_status_ = details.status(); |
1134 | + completion_msg_ = details.message(); |
1135 | + activate_complete_ = true; |
1136 | + cond_.notify_one(); |
1137 | + } |
1138 | + |
1139 | + void wait_until_finished() |
1140 | + { |
1141 | + unique_lock<mutex> lock(mutex_); |
1142 | + |
1143 | + cond_.wait(lock, [this] { return this->activate_complete_; }); |
1144 | + activate_complete_ = false; |
1145 | + } |
1146 | + |
1147 | + CompletionDetails::CompletionStatus completion_status() |
1148 | + { |
1149 | + lock_guard<mutex> lock(mutex_); |
1150 | + |
1151 | + return completion_status_; |
1152 | + } |
1153 | + |
1154 | + string completion_msg() |
1155 | + { |
1156 | + lock_guard<mutex> lock(mutex_); |
1157 | + |
1158 | + return completion_msg_; |
1159 | + } |
1160 | + |
1161 | +private: |
1162 | + ActivationResponse::Status act_status_; |
1163 | + bool activate_complete_; |
1164 | + string completion_msg_; |
1165 | + CompletionDetails::CompletionStatus completion_status_; |
1166 | + mutex mutex_; |
1167 | + condition_variable cond_; |
1168 | +}; |
1169 | + |
1170 | +class ThrowingScopeTest : public ::testing::Test |
1171 | +{ |
1172 | +public: |
1173 | + ThrowingScopeTest() |
1174 | + { |
1175 | + runtime_ = Runtime::create(TEST_RUNTIME_FILE); |
1176 | + auto reg = runtime_->registry(); |
1177 | + auto meta = reg->get_metadata("ThrowingScope"); |
1178 | + scope_ = meta.proxy(); |
1179 | + } |
1180 | + |
1181 | + ScopeProxy scope() const |
1182 | + { |
1183 | + return scope_; |
1184 | + } |
1185 | + |
1186 | +private: |
1187 | + Runtime::UPtr runtime_; |
1188 | + ScopeProxy scope_; |
1189 | +}; |
1190 | + |
1191 | +// The command file for each scope is called A.cmd, B.cmd, etc. It contains scope IDs, one per |
1192 | +// line. For each scope ID in its command file, the scope sends a subsearch to that scope. |
1193 | +// Once a scope's subsearch completes, it pushes a single result with the scope's ID as the |
1194 | +// category ID. This allows us to set up various callgraphs by writing to the various command files. |
1195 | + |
1196 | +TEST_F(ThrowingScopeTest, no_error) |
1197 | +{ |
1198 | + auto receiver = make_shared<Receiver>(); |
1199 | + scope()->search("success", SearchMetadata("unused", "unused"), receiver); |
1200 | + receiver->wait_until_finished(); |
1201 | + |
1202 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1203 | + EXPECT_EQ("", receiver->completion_msg()); |
1204 | + auto r = receiver->results(); |
1205 | + EXPECT_EQ(1, r.size()); |
1206 | +} |
1207 | + |
1208 | +TEST_F(ThrowingScopeTest, throw_from_search) |
1209 | +{ |
1210 | + auto receiver = make_shared<Receiver>(); |
1211 | + scope()->search("throw from search", SearchMetadata("unused", "unused"), receiver); |
1212 | + receiver->wait_until_finished(); |
1213 | + |
1214 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, receiver->completion_status()); |
1215 | + EXPECT_EQ("unity::scopes::MiddlewareException: unity::ResourceException: Scope \"ThrowingScope\" " |
1216 | + "threw an exception from search():\n" |
1217 | + " unity::ResourceException: exception from search", |
1218 | + receiver->completion_msg()); |
1219 | + auto r = receiver->results(); |
1220 | + EXPECT_EQ(0, r.size()); |
1221 | +} |
1222 | + |
1223 | +TEST_F(ThrowingScopeTest, throw_from_run) |
1224 | +{ |
1225 | + auto receiver = make_shared<Receiver>(); |
1226 | + scope()->search("throw from run", SearchMetadata("unused", "unused"), receiver); |
1227 | + receiver->wait_until_finished(); |
1228 | + |
1229 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, receiver->completion_status()); |
1230 | + EXPECT_EQ("QueryBase::run(): unity::ResourceException: exception from run", receiver->completion_msg()); |
1231 | + auto r = receiver->results(); |
1232 | + EXPECT_EQ(0, r.size()); |
1233 | +} |
1234 | + |
1235 | +TEST_F(ThrowingScopeTest, throw_from_cancelled) |
1236 | +{ |
1237 | + auto receiver = make_shared<Receiver>(); |
1238 | + auto ctrl = scope()->search("throw from cancelled", SearchMetadata("unused", "unused"), receiver); |
1239 | + // Make sure that the cancel goes to the scope instead of being cached locally. |
1240 | + this_thread::sleep_for(chrono::seconds(1)); |
1241 | + ctrl->cancel(); |
1242 | + receiver->wait_until_finished(); |
1243 | + |
1244 | + auto r = receiver->results(); |
1245 | + EXPECT_EQ(0, r.size()); |
1246 | + |
1247 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Cancelled, receiver->completion_status()); |
1248 | + // No exception string here because we short-cut the cancellation on the client side. |
1249 | + // The exception is logged though. |
1250 | + EXPECT_EQ("", receiver->completion_msg()); |
1251 | +} |
1252 | + |
1253 | +TEST_F(ThrowingScopeTest, throw_from_preview) |
1254 | +{ |
1255 | + auto receiver = make_shared<Receiver>(); |
1256 | + scope()->search("throw from preview", SearchMetadata("unused", "unused"), receiver); |
1257 | + receiver->wait_until_finished(); |
1258 | + |
1259 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1260 | + EXPECT_EQ("", receiver->completion_msg()); |
1261 | + auto r = receiver->results(); |
1262 | + EXPECT_EQ(1, r.size()); |
1263 | + |
1264 | + auto plistener = make_shared<PreviewListener>(); |
1265 | + scope()->preview(r[0], ActionMetadata("unused", "unused"), plistener); |
1266 | + plistener->wait_until_finished(); |
1267 | + |
1268 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, plistener->completion_status()); |
1269 | + EXPECT_EQ("unity::scopes::MiddlewareException: unity::ResourceException: Scope \"ThrowingScope\" " |
1270 | + "threw an exception from preview():\n" |
1271 | + " unity::ResourceException: exception from preview", |
1272 | + plistener->completion_msg()); |
1273 | +} |
1274 | + |
1275 | +TEST_F(ThrowingScopeTest, throw_from_preview_run) |
1276 | +{ |
1277 | + auto receiver = make_shared<Receiver>(); |
1278 | + scope()->search("throw from preview run", SearchMetadata("unused", "unused"), receiver); |
1279 | + receiver->wait_until_finished(); |
1280 | + |
1281 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1282 | + EXPECT_EQ("", receiver->completion_msg()); |
1283 | + auto r = receiver->results(); |
1284 | + EXPECT_EQ(1, r.size()); |
1285 | + |
1286 | + auto plistener = make_shared<PreviewListener>(); |
1287 | + scope()->preview(r[0], ActionMetadata("unused", "unused"), plistener); |
1288 | + plistener->wait_until_finished(); |
1289 | + |
1290 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, plistener->completion_status()); |
1291 | + EXPECT_EQ("PreviewQueryBase::run(): unity::ResourceException: throw from preview run", plistener->completion_msg()); |
1292 | +} |
1293 | + |
1294 | +TEST_F(ThrowingScopeTest, throw_from_preview_cancelled) |
1295 | +{ |
1296 | + auto receiver = make_shared<Receiver>(); |
1297 | + scope()->search("throw from preview cancelled", SearchMetadata("unused", "unused"), receiver); |
1298 | + receiver->wait_until_finished(); |
1299 | + |
1300 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1301 | + EXPECT_EQ("", receiver->completion_msg()); |
1302 | + auto r = receiver->results(); |
1303 | + EXPECT_EQ(1, r.size()); |
1304 | + |
1305 | + auto plistener = make_shared<PreviewListener>(); |
1306 | + auto ctrl = scope()->preview(r[0], ActionMetadata("unused", "unused"), plistener); |
1307 | + this_thread::sleep_for(chrono::seconds(1)); |
1308 | + ctrl->cancel(); |
1309 | + plistener->wait_until_finished(); |
1310 | + |
1311 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Cancelled, plistener->completion_status()); |
1312 | + // No exception string here because we short-cut the cancellation on the client side. |
1313 | + // The exception is logged though. |
1314 | + EXPECT_EQ("", plistener->completion_msg()); |
1315 | +} |
1316 | + |
1317 | +TEST_F(ThrowingScopeTest, throw_from_activate) |
1318 | +{ |
1319 | + auto receiver = make_shared<Receiver>(); |
1320 | + scope()->search("throw from activate", SearchMetadata("unused", "unused"), receiver); |
1321 | + receiver->wait_until_finished(); |
1322 | + |
1323 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1324 | + EXPECT_EQ("", receiver->completion_msg()); |
1325 | + auto r = receiver->results(); |
1326 | + EXPECT_EQ(1, r.size()); |
1327 | + |
1328 | + auto alistener = make_shared<ActivationListener>(); |
1329 | + scope()->activate(r[0], ActionMetadata("unused", "unused"), alistener); |
1330 | + alistener->wait_until_finished(); |
1331 | + |
1332 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, alistener->completion_status()); |
1333 | + EXPECT_EQ("unity::scopes::MiddlewareException: unity::ResourceException: Scope \"ThrowingScope\" " |
1334 | + "threw an exception from activate():\n" |
1335 | + " unity::ResourceException: exception from activate", |
1336 | + alistener->completion_msg()); |
1337 | +} |
1338 | + |
1339 | +TEST_F(ThrowingScopeTest, throw_from_activation_activate) |
1340 | +{ |
1341 | + auto receiver = make_shared<Receiver>(); |
1342 | + scope()->search("throw from activation activate", SearchMetadata("unused", "unused"), receiver); |
1343 | + receiver->wait_until_finished(); |
1344 | + |
1345 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1346 | + EXPECT_EQ("", receiver->completion_msg()); |
1347 | + auto r = receiver->results(); |
1348 | + EXPECT_EQ(1, r.size()); |
1349 | + |
1350 | + auto alistener = make_shared<ActivationListener>(); |
1351 | + scope()->activate(r[0], ActionMetadata("unused", "unused"), alistener); |
1352 | + alistener->wait_until_finished(); |
1353 | + |
1354 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, alistener->completion_status()); |
1355 | + EXPECT_EQ("ActivationQueryBase::activate(): unity::ResourceException: throw from activation activate", |
1356 | + alistener->completion_msg()); |
1357 | +} |
1358 | + |
1359 | +TEST_F(ThrowingScopeTest, throw_from_activation_cancelled) |
1360 | +{ |
1361 | + auto receiver = make_shared<Receiver>(); |
1362 | + scope()->search("throw from activation cancelled", SearchMetadata("unused", "unused"), receiver); |
1363 | + receiver->wait_until_finished(); |
1364 | + |
1365 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1366 | + EXPECT_EQ("", receiver->completion_msg()); |
1367 | + auto r = receiver->results(); |
1368 | + EXPECT_EQ(1, r.size()); |
1369 | + |
1370 | + auto alistener = make_shared<ActivationListener>(); |
1371 | + auto ctrl = scope()->activate(r[0], ActionMetadata("unused", "unused"), alistener); |
1372 | + this_thread::sleep_for(chrono::seconds(1)); |
1373 | + ctrl->cancel(); |
1374 | + alistener->wait_until_finished(); |
1375 | + |
1376 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Cancelled, alistener->completion_status()); |
1377 | + // No exception string here because we short-cut the cancellation on the client side. |
1378 | + // The exception is logged though. |
1379 | + EXPECT_EQ("", alistener->completion_msg()); |
1380 | +} |
1381 | + |
1382 | +TEST_F(ThrowingScopeTest, throw_from_perform_action) |
1383 | +{ |
1384 | + auto receiver = make_shared<Receiver>(); |
1385 | + scope()->search("throw from perform_action", SearchMetadata("unused", "unused"), receiver); |
1386 | + receiver->wait_until_finished(); |
1387 | + |
1388 | + EXPECT_EQ(CompletionDetails::CompletionStatus::OK, receiver->completion_status()); |
1389 | + EXPECT_EQ("", receiver->completion_msg()); |
1390 | + auto r = receiver->results(); |
1391 | + EXPECT_EQ(1, r.size()); |
1392 | + |
1393 | + auto alistener = make_shared<ActivationListener>(); |
1394 | + scope()->perform_action(r[0], ActionMetadata("unused", "unused"), "", "", alistener); |
1395 | + alistener->wait_until_finished(); |
1396 | + |
1397 | + EXPECT_EQ(CompletionDetails::CompletionStatus::Error, alistener->completion_status()); |
1398 | + EXPECT_EQ("unity::scopes::MiddlewareException: unity::ResourceException: Scope \"ThrowingScope\" " |
1399 | + "threw an exception from perform_action():\n" |
1400 | + " unity::ResourceException: exception from perform_action", |
1401 | + alistener->completion_msg()); |
1402 | +} |
1403 | + |
1404 | +int main(int argc, char **argv) |
1405 | +{ |
1406 | + ::testing::InitGoogleTest(&argc, argv); |
1407 | + |
1408 | + int rc = 0; |
1409 | + |
1410 | + // Set the "TEST_DESKTOP_FILES_DIR" env var before forking as not to create desktop files in ~/.local |
1411 | + putenv(const_cast<char*>("TEST_DESKTOP_FILES_DIR=" TEST_RUNTIME_PATH)); |
1412 | + |
1413 | + auto rpid = fork(); |
1414 | + if (rpid == 0) |
1415 | + { |
1416 | + const char* const args[] = {"scoperegistry [ThrowingScope_test]", TEST_RUNTIME_FILE, nullptr}; |
1417 | + if (execv(TEST_REGISTRY_PATH "/scoperegistry", const_cast<char* const*>(args)) < 0) |
1418 | + { |
1419 | + perror("Error starting scoperegistry:"); |
1420 | + } |
1421 | + return 1; |
1422 | + } |
1423 | + else if (rpid > 0) |
1424 | + { |
1425 | + rc = RUN_ALL_TESTS(); |
1426 | + kill(rpid, SIGTERM); |
1427 | + waitpid(rpid, nullptr, 0); |
1428 | + } |
1429 | + else |
1430 | + { |
1431 | + perror("Failed to fork:"); |
1432 | + } |
1433 | + |
1434 | + return rc; |
1435 | +} |
1436 | |
1437 | === added file 'test/gtest/scopes/ThrowingScope/Zmq.ini.in' |
1438 | --- test/gtest/scopes/ThrowingScope/Zmq.ini.in 1970-01-01 00:00:00 +0000 |
1439 | +++ test/gtest/scopes/ThrowingScope/Zmq.ini.in 2015-02-04 05:47:34 +0000 |
1440 | @@ -0,0 +1,2 @@ |
1441 | +[Zmq] |
1442 | +EndpointDir = /tmp |
FAILED: Continuous integration, rev:279 jenkins. qa.ubuntu. com/job/ unity-scopes- api-ci/ 527/ jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- amd64-ci/ 52/console jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- armhf-ci/ 52/console jenkins. qa.ubuntu. com/job/ unity-scopes- api-vivid- i386-ci/ 52/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scopes- api-ci/ 527/rebuild
http://