Merge lp:~alan-griffiths/mir/spike-exposing-rpc-in-frontend into lp:mir
- spike-exposing-rpc-in-frontend
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alan Griffiths |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1344 |
Proposed branch: | lp:~alan-griffiths/mir/spike-exposing-rpc-in-frontend |
Merge into: | lp:mir |
Diff against target: |
918 lines (+355/-180) 20 files modified
include/server/mir/default_server_configuration.h (+3/-4) include/server/mir/frontend/message_processor.h (+10/-11) include/server/mir/frontend/protobuf_session_creator.h (+9/-1) include/server/mir/frontend/template_protobuf_message_processor.h (+15/-36) src/server/frontend/CMakeLists.txt (+0/-2) src/server/frontend/default_configuration.cpp (+1/-1) src/server/frontend/null_message_processor.cpp (+0/-26) src/server/frontend/protobuf_message_processor.cpp (+8/-2) src/server/frontend/protobuf_message_processor.h (+6/-3) src/server/frontend/protobuf_session_creator.cpp (+14/-2) src/server/frontend/published_socket_connector.cpp (+1/-1) src/server/frontend/socket_session.cpp (+9/-2) src/server/frontend/socket_session.h (+1/-1) src/server/frontend/template_protobuf_message_processor.cpp (+0/-44) src/shared/protobuf/mir_protobuf_wire.proto (+1/-1) tests/acceptance-tests/test_protobuf.cpp (+216/-7) tests/acceptance-tests/test_protobuf.proto (+1/-0) tests/mir_test_doubles/test_protobuf_socket_server.cpp (+1/-1) tests/unit-tests/frontend/test_published_socket_connector.cpp (+1/-1) tests/unit-tests/frontend/test_socket_session.cpp (+58/-34) |
To merge this branch: | bzr merge lp:~alan-griffiths/mir/spike-exposing-rpc-in-frontend |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexandros Frantzis (community) | Approve | ||
Kevin DuBois (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+202351@code.launchpad.net |
Commit message
frontend: exposing internals of the RPC mechanism to enable custom function calls to be added.
Description of the change
frontend: exposing internals of the RPC mechanism to enable custom function calls to be added. (As illustrated by DemoPrivateProt
PS Jenkins bot (ps-jenkins) wrote : | # |
Alexandros Frantzis (afrantzis) wrote : | # |
I like the new version better.
Looks OK, besides some stray tabs:
272 + sender(sender),
819 + std::function<
820 + boost::
821 + size_t size)
822 + {
823 + read_size = size;
824 + pstream = &stream;
825 + callback_function = callback;831
831 + ASSERT_
839 + callback_
866 + int const header_size = 2;
Alan Griffiths (alan-griffiths) wrote : | # |
> I like the new version better.
>
> Looks OK, besides some stray tabs:
>
Weird, I've checked that the editor is set to insert spaces, but tabs appear in the text.
Fixed anyway.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1347
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1350
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1354
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Kevin DuBois (kdub) wrote : | # |
nit:
l913, 914, 867 seem like bit magic numbers.
other than that, looks okay though
Alexandros Frantzis (afrantzis) wrote : | # |
Looks good.
Preview Diff
1 | === modified file 'include/server/mir/default_server_configuration.h' |
2 | --- include/server/mir/default_server_configuration.h 2014-01-13 06:12:33 +0000 |
3 | +++ include/server/mir/default_server_configuration.h 2014-01-21 15:42:35 +0000 |
4 | @@ -239,6 +239,9 @@ |
5 | |
6 | virtual std::shared_ptr<input::InputChannelFactory> the_input_channel_factory(); |
7 | virtual std::shared_ptr<scene::MediatingDisplayChanger> the_mediating_display_changer(); |
8 | + virtual std::shared_ptr<frontend::ProtobufIpcFactory> the_ipc_factory( |
9 | + std::shared_ptr<frontend::Shell> const& shell, |
10 | + std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator); |
11 | |
12 | CachedPtr<frontend::Connector> connector; |
13 | |
14 | @@ -292,10 +295,6 @@ |
15 | |
16 | private: |
17 | std::shared_ptr<input::EventFilter> const default_filter; |
18 | - // the communications interface to use |
19 | - virtual std::shared_ptr<frontend::ProtobufIpcFactory> the_ipc_factory( |
20 | - std::shared_ptr<frontend::Shell> const& shell, |
21 | - std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator); |
22 | |
23 | virtual std::string the_socket_file() const; |
24 | |
25 | |
26 | === renamed file 'src/server/frontend/connected_sessions.h' => 'include/server/mir/frontend/connected_sessions.h' |
27 | === modified file 'include/server/mir/frontend/message_processor.h' |
28 | --- include/server/mir/frontend/message_processor.h 2014-01-16 14:39:11 +0000 |
29 | +++ include/server/mir/frontend/message_processor.h 2014-01-21 15:42:35 +0000 |
30 | @@ -1,5 +1,5 @@ |
31 | /* |
32 | - * Copyright © 2012 Canonical Ltd. |
33 | + * Copyright © 2012, 2014 Canonical Ltd. |
34 | * |
35 | * This program is free software: you can redistribute it and/or modify it |
36 | * under the terms of the GNU General Public License version 3, |
37 | @@ -19,10 +19,15 @@ |
38 | #ifndef MIR_FRONTEND_MESSAGE_PROCESSOR_H_ |
39 | #define MIR_FRONTEND_MESSAGE_PROCESSOR_H_ |
40 | |
41 | -#include <iosfwd> |
42 | - |
43 | namespace mir |
44 | { |
45 | +namespace protobuf |
46 | +{ |
47 | +namespace wire |
48 | +{ |
49 | +class Invocation; |
50 | +} |
51 | +} |
52 | namespace frontend |
53 | { |
54 | namespace detail |
55 | @@ -31,20 +36,14 @@ |
56 | class MessageProcessor |
57 | { |
58 | public: |
59 | - virtual bool process_message(std::istream& msg) = 0; |
60 | + virtual bool dispatch(mir::protobuf::wire::Invocation const& invocation) = 0; |
61 | + |
62 | protected: |
63 | MessageProcessor() = default; |
64 | virtual ~MessageProcessor() = default; |
65 | MessageProcessor(MessageProcessor const&) = delete; |
66 | MessageProcessor& operator=(MessageProcessor const&) = delete; |
67 | }; |
68 | - |
69 | -class NullMessageProcessor : MessageProcessor |
70 | -{ |
71 | -public: |
72 | - bool process_message(std::istream&); |
73 | -}; |
74 | - |
75 | } |
76 | } |
77 | } |
78 | |
79 | === renamed file 'src/server/frontend/protobuf_session_creator.h' => 'include/server/mir/frontend/protobuf_session_creator.h' |
80 | --- src/server/frontend/protobuf_session_creator.h 2014-01-15 12:26:01 +0000 |
81 | +++ include/server/mir/frontend/protobuf_session_creator.h 2014-01-21 15:42:35 +0000 |
82 | @@ -20,12 +20,13 @@ |
83 | #define MIR_FRONTEND_PROTOBUF_SESSION_CREATOR_H_ |
84 | |
85 | #include "mir/frontend/session_creator.h" |
86 | -#include "connected_sessions.h" |
87 | +#include "mir/frontend/connected_sessions.h" |
88 | |
89 | #include <atomic> |
90 | |
91 | namespace mir |
92 | { |
93 | +namespace protobuf { class DisplayServer; } |
94 | namespace frontend |
95 | { |
96 | class MessageProcessorReport; |
97 | @@ -35,6 +36,8 @@ |
98 | namespace detail |
99 | { |
100 | struct SocketSession; |
101 | +class MessageProcessor; |
102 | +class ProtobufMessageSender; |
103 | } |
104 | |
105 | class ProtobufSessionCreator : public SessionCreator |
106 | @@ -48,6 +51,11 @@ |
107 | |
108 | void create_session_for(std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket); |
109 | |
110 | + virtual std::shared_ptr<detail::MessageProcessor> create_processor( |
111 | + std::shared_ptr<detail::ProtobufMessageSender> const& sender, |
112 | + std::shared_ptr<protobuf::DisplayServer> const& display_server, |
113 | + std::shared_ptr<MessageProcessorReport> const& report) const; |
114 | + |
115 | private: |
116 | int next_id(); |
117 | |
118 | |
119 | === modified file 'include/server/mir/frontend/template_protobuf_message_processor.h' |
120 | --- include/server/mir/frontend/template_protobuf_message_processor.h 2014-01-17 10:55:55 +0000 |
121 | +++ include/server/mir/frontend/template_protobuf_message_processor.h 2014-01-21 15:42:35 +0000 |
122 | @@ -36,40 +36,15 @@ |
123 | { |
124 | namespace detail |
125 | { |
126 | -class ProtobufMessageSender; |
127 | - |
128 | -// This class is intended to make implementation of a protobuf based MessageProcessor simpler. |
129 | -// The template method process_message() calls dispatch after unpacking the received "invocation" |
130 | -// message. The related invoke<>() template handles further unpacking of the parameter |
131 | -// message and packing of the response and calls send_response. |
132 | -// Derived classes can overload send_response, but need to specialize result_ptr_t before instantiating |
133 | -// invoke<>() to ensure the correct overload is called. |
134 | -class TemplateProtobufMessageProcessor : public MessageProcessor |
135 | -{ |
136 | -public: |
137 | - TemplateProtobufMessageProcessor( |
138 | - std::shared_ptr<ProtobufMessageSender> const& sender); |
139 | - |
140 | - ~TemplateProtobufMessageProcessor() noexcept {} |
141 | - |
142 | - void send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response); |
143 | - |
144 | - std::shared_ptr<ProtobufMessageSender> const sender; |
145 | - |
146 | -private: |
147 | - virtual bool dispatch(mir::protobuf::wire::Invocation const& invocation) = 0; |
148 | - |
149 | - bool process_message(std::istream& msg) override final; |
150 | -}; |
151 | - |
152 | -// Utility function result_ptr() allows invoke() to pick the right send_response() overload |
153 | -// Client code may specialize result_ptr_t to get an overload of send_response called. |
154 | -template<typename ResultType> struct result_ptr_t { typedef ::google::protobuf::Message* type; }; |
155 | -template<typename ResultType> inline |
156 | -auto result_ptr(ResultType& result) -> typename result_ptr_t<ResultType>::type { return &result; } |
157 | +// Utility metafunction result_ptr_t<> allows invoke() to pick the right |
158 | +// send_response() overload. The base template resolves to the prototype |
159 | +// "send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response)" |
160 | +// Client code may specialize result_ptr_t to resolve to another overload. |
161 | +template<typename ResultType> struct result_ptr_t |
162 | +{ typedef ::google::protobuf::Message* type; }; |
163 | |
164 | // Boiler plate for unpacking a parameter message, invoking a server function, and |
165 | -// sending the result message. |
166 | +// sending the result message. Assumes the existence of Self::send_response(). |
167 | template<class Self, class Server, class ParameterMessage, class ResultMessage> |
168 | void invoke( |
169 | Self* self, |
170 | @@ -88,10 +63,14 @@ |
171 | try |
172 | { |
173 | std::unique_ptr<google::protobuf::Closure> callback( |
174 | - google::protobuf::NewPermanentCallback(self, |
175 | - &Self::send_response, |
176 | - invocation.id(), |
177 | - result_ptr(result_message))); |
178 | + google::protobuf::NewPermanentCallback< |
179 | + Self, |
180 | + ::google::protobuf::uint32, |
181 | + typename result_ptr_t<ResultMessage>::type>( |
182 | + self, |
183 | + &Self::send_response, |
184 | + invocation.id(), |
185 | + &result_message)); |
186 | |
187 | (server->*function)( |
188 | 0, |
189 | |
190 | === modified file 'src/server/frontend/CMakeLists.txt' |
191 | --- src/server/frontend/CMakeLists.txt 2014-01-20 07:11:48 +0000 |
192 | +++ src/server/frontend/CMakeLists.txt 2014-01-21 15:42:35 +0000 |
193 | @@ -8,11 +8,9 @@ |
194 | protobuf_message_processor.cpp |
195 | protobuf_responder.cpp |
196 | protobuf_buffer_packer.cpp |
197 | - null_message_processor.cpp |
198 | published_socket_connector.cpp |
199 | protobuf_session_creator.cpp |
200 | socket_session.cpp |
201 | - template_protobuf_message_processor.cpp |
202 | resource_cache.cpp |
203 | socket_messenger.cpp |
204 | event_sender.cpp |
205 | |
206 | === modified file 'src/server/frontend/default_configuration.cpp' |
207 | --- src/server/frontend/default_configuration.cpp 2014-01-20 07:11:48 +0000 |
208 | +++ src/server/frontend/default_configuration.cpp 2014-01-21 15:42:35 +0000 |
209 | @@ -17,10 +17,10 @@ |
210 | */ |
211 | |
212 | #include "mir/default_server_configuration.h" |
213 | +#include "mir/frontend/protobuf_session_creator.h" |
214 | |
215 | #include "resource_cache.h" |
216 | #include "protobuf_ipc_factory.h" |
217 | -#include "protobuf_session_creator.h" |
218 | #include "published_socket_connector.h" |
219 | #include "session_mediator.h" |
220 | #include "unauthorized_display_changer.h" |
221 | |
222 | === removed file 'src/server/frontend/null_message_processor.cpp' |
223 | --- src/server/frontend/null_message_processor.cpp 2014-01-16 14:39:11 +0000 |
224 | +++ src/server/frontend/null_message_processor.cpp 1970-01-01 00:00:00 +0000 |
225 | @@ -1,26 +0,0 @@ |
226 | -/* |
227 | - * Copyright © 2012 Canonical Ltd. |
228 | - * |
229 | - * This program is free software: you can redistribute it and/or modify it |
230 | - * under the terms of the GNU General Public License version 3, |
231 | - * as published by the Free Software Foundation. |
232 | - * |
233 | - * This program is distributed in the hope that it will be useful, |
234 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
235 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
236 | - * GNU General Public License for more details. |
237 | - * |
238 | - * You should have received a copy of the GNU General Public License |
239 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
240 | - * |
241 | - * Authored by: Alan Griffiths <alan@octopull.co.uk> |
242 | - */ |
243 | - |
244 | -#include "mir/frontend/message_processor.h" |
245 | - |
246 | -namespace mfd = mir::frontend::detail; |
247 | - |
248 | -bool mfd::NullMessageProcessor::process_message(std::istream& ) |
249 | -{ |
250 | - return false; |
251 | -} |
252 | |
253 | === modified file 'src/server/frontend/protobuf_message_processor.cpp' |
254 | --- src/server/frontend/protobuf_message_processor.cpp 2014-01-20 07:11:48 +0000 |
255 | +++ src/server/frontend/protobuf_message_processor.cpp 2014-01-21 15:42:35 +0000 |
256 | @@ -17,8 +17,9 @@ |
257 | */ |
258 | |
259 | #include "protobuf_message_processor.h" |
260 | +#include "mir/frontend/message_processor_report.h" |
261 | #include "mir/frontend/protobuf_message_sender.h" |
262 | -#include "mir/frontend/message_processor_report.h" |
263 | +#include "mir/frontend/template_protobuf_message_processor.h" |
264 | |
265 | namespace mfd = mir::frontend::detail; |
266 | |
267 | @@ -38,7 +39,7 @@ |
268 | std::shared_ptr<ProtobufMessageSender> const& sender, |
269 | std::shared_ptr<protobuf::DisplayServer> const& display_server, |
270 | std::shared_ptr<MessageProcessorReport> const& report) : |
271 | - TemplateProtobufMessageProcessor(sender), |
272 | + sender(sender), |
273 | display_server(display_server), |
274 | report(report) |
275 | { |
276 | @@ -121,6 +122,11 @@ |
277 | return result; |
278 | } |
279 | |
280 | +void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response) |
281 | +{ |
282 | + sender->send_response(id, response, {}); |
283 | +} |
284 | + |
285 | void mfd::ProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, mir::protobuf::Buffer* response) |
286 | { |
287 | const auto& fd = extract_fds_from(response); |
288 | |
289 | === modified file 'src/server/frontend/protobuf_message_processor.h' |
290 | --- src/server/frontend/protobuf_message_processor.h 2014-01-20 07:11:48 +0000 |
291 | +++ src/server/frontend/protobuf_message_processor.h 2014-01-21 15:42:35 +0000 |
292 | @@ -20,7 +20,7 @@ |
293 | #ifndef MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_ |
294 | #define MIR_FRONTEND_PROTOBUF_MESSAGE_PROCESSOR_H_ |
295 | |
296 | -#include "mir/frontend/template_protobuf_message_processor.h" |
297 | +#include "mir/frontend/message_processor.h" |
298 | |
299 | #include "mir_protobuf.pb.h" |
300 | |
301 | @@ -36,7 +36,9 @@ |
302 | |
303 | namespace detail |
304 | { |
305 | -class ProtobufMessageProcessor : public TemplateProtobufMessageProcessor |
306 | +class ProtobufMessageSender; |
307 | + |
308 | +class ProtobufMessageProcessor : public MessageProcessor |
309 | { |
310 | public: |
311 | ProtobufMessageProcessor( |
312 | @@ -46,7 +48,7 @@ |
313 | |
314 | ~ProtobufMessageProcessor() noexcept {} |
315 | |
316 | - using TemplateProtobufMessageProcessor::send_response; |
317 | + void send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response); |
318 | void send_response(::google::protobuf::uint32 id, protobuf::Buffer* response); |
319 | void send_response(::google::protobuf::uint32 id, protobuf::Connection* response); |
320 | void send_response(::google::protobuf::uint32 id, protobuf::Surface* response); |
321 | @@ -54,6 +56,7 @@ |
322 | private: |
323 | bool dispatch(mir::protobuf::wire::Invocation const& invocation); |
324 | |
325 | + std::shared_ptr<ProtobufMessageSender> const sender; |
326 | std::shared_ptr<protobuf::DisplayServer> const display_server; |
327 | std::shared_ptr<MessageProcessorReport> const report; |
328 | }; |
329 | |
330 | === modified file 'src/server/frontend/protobuf_session_creator.cpp' |
331 | --- src/server/frontend/protobuf_session_creator.cpp 2014-01-20 07:11:48 +0000 |
332 | +++ src/server/frontend/protobuf_session_creator.cpp 2014-01-21 15:42:35 +0000 |
333 | @@ -16,7 +16,7 @@ |
334 | * Authored by: Alan Griffiths <alan@octopull.co.uk> |
335 | */ |
336 | |
337 | -#include "protobuf_session_creator.h" |
338 | +#include "mir/frontend/protobuf_session_creator.h" |
339 | |
340 | #include "event_sender.h" |
341 | #include "protobuf_message_processor.h" |
342 | @@ -67,7 +67,7 @@ |
343 | ipc_factory->resource_cache()); |
344 | |
345 | auto const event_sink = std::make_shared<detail::EventSender>(messenger); |
346 | - auto const msg_processor = std::make_shared<detail::ProtobufMessageProcessor>( |
347 | + auto const msg_processor = create_processor( |
348 | message_sender, |
349 | ipc_factory->make_ipc_server(event_sink, authorized_to_resize_display), |
350 | report); |
351 | @@ -77,3 +77,15 @@ |
352 | session->read_next_message(); |
353 | } |
354 | } |
355 | + |
356 | +std::shared_ptr<mfd::MessageProcessor> |
357 | +mf::ProtobufSessionCreator::create_processor( |
358 | + std::shared_ptr<mfd::ProtobufMessageSender> const& sender, |
359 | + std::shared_ptr<protobuf::DisplayServer> const& display_server, |
360 | + std::shared_ptr<mf::MessageProcessorReport> const& report) const |
361 | +{ |
362 | + return std::make_shared<detail::ProtobufMessageProcessor>( |
363 | + sender, |
364 | + display_server, |
365 | + report); |
366 | +} |
367 | |
368 | === modified file 'src/server/frontend/published_socket_connector.cpp' |
369 | --- src/server/frontend/published_socket_connector.cpp 2014-01-13 06:12:33 +0000 |
370 | +++ src/server/frontend/published_socket_connector.cpp 2014-01-21 15:42:35 +0000 |
371 | @@ -17,7 +17,7 @@ |
372 | */ |
373 | |
374 | #include "published_socket_connector.h" |
375 | -#include "protobuf_session_creator.h" |
376 | +#include "mir/frontend/protobuf_session_creator.h" |
377 | |
378 | #include "mir/frontend/connector_report.h" |
379 | |
380 | |
381 | === modified file 'src/server/frontend/socket_session.cpp' |
382 | --- src/server/frontend/socket_session.cpp 2014-01-20 07:11:48 +0000 |
383 | +++ src/server/frontend/socket_session.cpp 2014-01-21 15:42:35 +0000 |
384 | @@ -21,6 +21,8 @@ |
385 | #include "message_receiver.h" |
386 | #include "mir/frontend/message_processor.h" |
387 | |
388 | +#include "mir_protobuf_wire.pb.h" |
389 | + |
390 | #include <boost/signals2.hpp> |
391 | #include <boost/throw_exception.hpp> |
392 | |
393 | @@ -85,8 +87,13 @@ |
394 | } |
395 | |
396 | std::istream msg(&message); |
397 | - |
398 | - if (processor->process_message(msg)) |
399 | + mir::protobuf::wire::Invocation invocation; |
400 | + invocation.ParseFromIstream(&msg); |
401 | + |
402 | + if (!invocation.has_protocol_version() || invocation.protocol_version() != 1) |
403 | + BOOST_THROW_EXCEPTION(std::runtime_error("Unsupported protocol version")); |
404 | + |
405 | + if (processor->dispatch(invocation)) |
406 | { |
407 | read_next_message(); |
408 | } |
409 | |
410 | === modified file 'src/server/frontend/socket_session.h' |
411 | --- src/server/frontend/socket_session.h 2014-01-16 14:39:11 +0000 |
412 | +++ src/server/frontend/socket_session.h 2014-01-21 15:42:35 +0000 |
413 | @@ -20,7 +20,7 @@ |
414 | #ifndef MIR_FRONTEND_DETAIL_SOCKET_SESSION_H_ |
415 | #define MIR_FRONTEND_DETAIL_SOCKET_SESSION_H_ |
416 | |
417 | -#include "connected_sessions.h" |
418 | +#include "mir/frontend/connected_sessions.h" |
419 | |
420 | #include <boost/asio.hpp> |
421 | |
422 | |
423 | === removed file 'src/server/frontend/template_protobuf_message_processor.cpp' |
424 | --- src/server/frontend/template_protobuf_message_processor.cpp 2014-01-16 14:39:11 +0000 |
425 | +++ src/server/frontend/template_protobuf_message_processor.cpp 1970-01-01 00:00:00 +0000 |
426 | @@ -1,44 +0,0 @@ |
427 | -/* |
428 | - * Copyright © 2014 Canonical Ltd. |
429 | - * |
430 | - * This program is free software: you can redistribute it and/or modify it |
431 | - * under the terms of the GNU General Public License version 3, |
432 | - * as published by the Free Software Foundation. |
433 | - * |
434 | - * This program is distributed in the hope that it will be useful, |
435 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
436 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
437 | - * GNU General Public License for more details. |
438 | - * |
439 | - * You should have received a copy of the GNU General Public License |
440 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
441 | - * |
442 | - * Authored by: Alan Griffiths <alan@octopull.co.uk> |
443 | - */ |
444 | - |
445 | -#include "mir/frontend/template_protobuf_message_processor.h" |
446 | -#include "mir/frontend/protobuf_message_sender.h" |
447 | - |
448 | -namespace mfd = mir::frontend::detail; |
449 | - |
450 | -mfd::TemplateProtobufMessageProcessor::TemplateProtobufMessageProcessor( |
451 | - std::shared_ptr<ProtobufMessageSender> const& sender) : |
452 | - sender(sender) |
453 | -{ |
454 | -} |
455 | - |
456 | -bool mfd::TemplateProtobufMessageProcessor::process_message(std::istream& msg) |
457 | -{ |
458 | - mir::protobuf::wire::Invocation invocation; |
459 | - invocation.ParseFromIstream(&msg); |
460 | - |
461 | - if (invocation.has_protocol_version() && invocation.protocol_version() != 1) |
462 | - BOOST_THROW_EXCEPTION(std::runtime_error("Unsupported protocol version")); |
463 | - |
464 | - return dispatch(invocation); |
465 | -} |
466 | - |
467 | -void mfd::TemplateProtobufMessageProcessor::send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response) |
468 | -{ |
469 | - sender->send_response(id, response, {}); |
470 | -} |
471 | |
472 | === modified file 'src/shared/protobuf/mir_protobuf_wire.proto' |
473 | --- src/shared/protobuf/mir_protobuf_wire.proto 2013-08-29 03:48:16 +0000 |
474 | +++ src/shared/protobuf/mir_protobuf_wire.proto 2014-01-21 15:42:35 +0000 |
475 | @@ -4,7 +4,7 @@ |
476 | required uint32 id = 1; |
477 | required string method_name = 2; |
478 | required bytes parameters = 3; |
479 | - optional uint32 protocol_version = 4; |
480 | + required uint32 protocol_version = 4; |
481 | } |
482 | |
483 | message Result { |
484 | |
485 | === modified file 'tests/acceptance-tests/test_protobuf.cpp' |
486 | --- tests/acceptance-tests/test_protobuf.cpp 2014-01-10 17:34:40 +0000 |
487 | +++ tests/acceptance-tests/test_protobuf.cpp 2014-01-21 15:42:35 +0000 |
488 | @@ -20,29 +20,169 @@ |
489 | |
490 | #include "mir_toolkit/mir_client_library.h" |
491 | #include "mir/client/private.h" |
492 | +#include "mir/frontend/protobuf_message_sender.h" |
493 | +#include "mir/frontend/protobuf_session_creator.h" |
494 | +#include "mir/frontend/template_protobuf_message_processor.h" |
495 | |
496 | #include "mir_test_framework/stubbed_server_configuration.h" |
497 | #include "mir_test_framework/in_process_server.h" |
498 | |
499 | #include <gtest/gtest.h> |
500 | +#include <gmock/gmock.h> |
501 | |
502 | #include <atomic> |
503 | |
504 | +namespace mf = mir::frontend; |
505 | +namespace mfd = mir::frontend::detail; |
506 | + |
507 | +/*************************************************************************/ |
508 | +/*************************************************************************/ |
509 | +/* Note that the functionality demonstrated here relies on "detail" and */ |
510 | +/* is not guaranteed to be supported in future. */ |
511 | +/*************************************************************************/ |
512 | +/*************************************************************************/ |
513 | namespace |
514 | { |
515 | -class DemoPrivateProtobuf : public mir_test_framework::InProcessServer |
516 | -{ |
517 | - mir::DefaultServerConfiguration& server_config() override { return server_config_; } |
518 | - |
519 | - mir_test_framework::StubbedServerConfiguration server_config_; |
520 | +struct DemoMirServer : mir::protobuf::MirServer |
521 | +{ |
522 | + MOCK_CONST_METHOD1(on_call, std::string(std::string)); |
523 | + |
524 | + DemoMirServer() |
525 | + { |
526 | + using namespace testing; |
527 | + ON_CALL(*this, on_call(_)).WillByDefault(Return("ok")); |
528 | + } |
529 | + |
530 | + void function( |
531 | + ::google::protobuf::RpcController* , |
532 | + ::mir::protobuf::Parameters const* parameters, |
533 | + ::mir::protobuf::Result* response, |
534 | + ::google::protobuf::Closure* done) |
535 | + { |
536 | + response->set_value(on_call(parameters->name())); |
537 | + done->Run(); |
538 | + } |
539 | +}; |
540 | + |
541 | +// using a global for easy access from tests and DemoMessageProcessor::dispatch() |
542 | +DemoMirServer* demo_mir_server; |
543 | + |
544 | +struct DemoMessageProcessor : mfd::MessageProcessor |
545 | +{ |
546 | + DemoMessageProcessor( |
547 | + std::shared_ptr<mfd::ProtobufMessageSender> const& sender, |
548 | + std::shared_ptr<mfd::MessageProcessor> const& wrapped) : |
549 | + sender(sender), |
550 | + wrapped(wrapped) {} |
551 | + |
552 | + bool dispatch(mir::protobuf::wire::Invocation const& invocation) |
553 | + { |
554 | + if ("function" == invocation.method_name()) |
555 | + { |
556 | + mfd::invoke( |
557 | + this, |
558 | + demo_mir_server, |
559 | + &DemoMirServer::function, |
560 | + invocation); |
561 | + return true; |
562 | + } |
563 | + |
564 | + return wrapped->dispatch(invocation); |
565 | + } |
566 | + |
567 | + void send_response(::google::protobuf::uint32 id, ::google::protobuf::Message* response) |
568 | + { |
569 | + sender->send_response(id, response, {}); |
570 | + } |
571 | + |
572 | + std::shared_ptr<mfd::ProtobufMessageSender> const sender; |
573 | + std::shared_ptr<mfd::MessageProcessor> const wrapped; |
574 | +}; |
575 | + |
576 | +struct DemoSessionCreator : mf::ProtobufSessionCreator |
577 | +{ |
578 | + using ProtobufSessionCreator::ProtobufSessionCreator; |
579 | + |
580 | + MOCK_CONST_METHOD3(create_processor, |
581 | + std::shared_ptr<mfd::MessageProcessor>( |
582 | + std::shared_ptr<mfd::ProtobufMessageSender> const& sender, |
583 | + std::shared_ptr<mir::protobuf::DisplayServer> const& display_server, |
584 | + std::shared_ptr<mf::MessageProcessorReport> const& report)); |
585 | + |
586 | + std::shared_ptr<mfd::MessageProcessor> create_wrapped_processor( |
587 | + std::shared_ptr<mfd::ProtobufMessageSender> const& sender, |
588 | + std::shared_ptr<mir::protobuf::DisplayServer> const& display_server, |
589 | + std::shared_ptr<mf::MessageProcessorReport> const& report) const |
590 | + { |
591 | + auto const wrapped = mf::ProtobufSessionCreator::create_processor( |
592 | + sender, |
593 | + display_server, |
594 | + report); |
595 | + |
596 | + return std::make_shared<DemoMessageProcessor>(sender, wrapped); |
597 | + } |
598 | + |
599 | + std::shared_ptr<mfd::MessageProcessor> create_unwrapped_processor( |
600 | + std::shared_ptr<mfd::ProtobufMessageSender> const& sender, |
601 | + std::shared_ptr<mir::protobuf::DisplayServer> const& display_server, |
602 | + std::shared_ptr<mf::MessageProcessorReport> const& report) const |
603 | + { |
604 | + return mf::ProtobufSessionCreator::create_processor( |
605 | + sender, |
606 | + display_server, |
607 | + report); |
608 | + } |
609 | +}; |
610 | + |
611 | +struct DemoServerConfiguration : mir_test_framework::StubbedServerConfiguration |
612 | +{ |
613 | + std::shared_ptr<mf::SessionCreator> the_session_creator() override |
614 | + { |
615 | + return session_creator([this] |
616 | + { |
617 | + return std::make_shared<DemoSessionCreator>( |
618 | + the_ipc_factory(the_frontend_shell(), the_buffer_allocator()), |
619 | + the_session_authorizer(), |
620 | + the_message_processor_report()); |
621 | + }); |
622 | + } |
623 | + |
624 | +}; |
625 | + |
626 | +struct DemoPrivateProtobuf : mir_test_framework::InProcessServer |
627 | +{ |
628 | + mir::DefaultServerConfiguration& server_config() override { return my_server_config; } |
629 | + |
630 | + DemoServerConfiguration my_server_config; |
631 | + |
632 | + std::shared_ptr<DemoSessionCreator> demo_session_creator; |
633 | + |
634 | + void SetUp() |
635 | + { |
636 | + ::demo_mir_server = &demo_mir_server; |
637 | + |
638 | + mir_test_framework::InProcessServer::SetUp(); |
639 | + demo_session_creator = std::dynamic_pointer_cast<DemoSessionCreator>(my_server_config.the_session_creator()); |
640 | + |
641 | + using namespace testing; |
642 | + ASSERT_THAT(demo_session_creator, NotNull()); |
643 | + |
644 | + ON_CALL(*demo_session_creator, create_processor(_, _, _)) |
645 | + .WillByDefault(Invoke(demo_session_creator.get(), &DemoSessionCreator::create_unwrapped_processor)); |
646 | + } |
647 | + |
648 | + testing::NiceMock<DemoMirServer> demo_mir_server; |
649 | }; |
650 | |
651 | void callback(std::atomic<bool>* called_back) { called_back->store(true); } |
652 | char const* const nothing_returned = "Nothing returned"; |
653 | } |
654 | |
655 | -TEST_F(DemoPrivateProtobuf, client_can_call_server) |
656 | +TEST_F(DemoPrivateProtobuf, client_calls_server) |
657 | { |
658 | + using namespace testing; |
659 | + EXPECT_CALL(*demo_session_creator, create_processor(_, _, _)); |
660 | + |
661 | auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
662 | ASSERT_TRUE(mir_connection_is_valid(connection)); |
663 | |
664 | @@ -72,5 +212,74 @@ |
665 | mir_connection_release(connection); |
666 | |
667 | EXPECT_TRUE(called_back); |
668 | - EXPECT_EQ(nothing_returned, result.error()); |
669 | + EXPECT_THAT(result.error(), Eq(nothing_returned)); |
670 | +} |
671 | + |
672 | +TEST_F(DemoPrivateProtobuf, wrapping_message_processor) |
673 | +{ |
674 | + using namespace testing; |
675 | + EXPECT_CALL(*demo_session_creator, create_processor(_, _, _)) |
676 | + .Times(1) |
677 | + .WillOnce(Invoke(demo_session_creator.get(), &DemoSessionCreator::create_wrapped_processor)); |
678 | + |
679 | + auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
680 | + |
681 | + mir_connection_release(connection); |
682 | +} |
683 | + |
684 | +TEST_F(DemoPrivateProtobuf, server_receives_function_call) |
685 | +{ |
686 | + using namespace testing; |
687 | + EXPECT_CALL(*demo_session_creator, create_processor(_, _, _)) |
688 | + .WillRepeatedly(Invoke(demo_session_creator.get(), &DemoSessionCreator::create_wrapped_processor)); |
689 | + |
690 | + auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
691 | + ASSERT_TRUE(mir_connection_is_valid(connection)); |
692 | + |
693 | + auto const rpc_channel = mir::client::the_rpc_channel(connection); |
694 | + |
695 | + using namespace mir::protobuf; |
696 | + using namespace google::protobuf; |
697 | + |
698 | + MirServer::Stub server(rpc_channel.get()); |
699 | + |
700 | + Parameters parameters; |
701 | + Result result; |
702 | + parameters.set_name(__PRETTY_FUNCTION__); |
703 | + |
704 | + EXPECT_CALL(demo_mir_server, on_call(__PRETTY_FUNCTION__)).Times(1); |
705 | + |
706 | + server.function(nullptr, ¶meters, &result, NewCallback([]{})); |
707 | + |
708 | + mir_connection_release(connection); |
709 | +} |
710 | + |
711 | + |
712 | +TEST_F(DemoPrivateProtobuf, client_receives_result) |
713 | +{ |
714 | + using namespace testing; |
715 | + EXPECT_CALL(*demo_session_creator, create_processor(_, _, _)) |
716 | + .WillRepeatedly(Invoke(demo_session_creator.get(), &DemoSessionCreator::create_wrapped_processor)); |
717 | + EXPECT_CALL(demo_mir_server, on_call(_)).WillRepeatedly(Return(__PRETTY_FUNCTION__)); |
718 | + |
719 | + auto const connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__); |
720 | + ASSERT_TRUE(mir_connection_is_valid(connection)); |
721 | + |
722 | + auto const rpc_channel = mir::client::the_rpc_channel(connection); |
723 | + |
724 | + using namespace mir::protobuf; |
725 | + using namespace google::protobuf; |
726 | + |
727 | + MirServer::Stub server(rpc_channel.get()); |
728 | + |
729 | + Parameters parameters; |
730 | + Result result; |
731 | + parameters.set_name(__PRETTY_FUNCTION__); |
732 | + |
733 | + server.function(nullptr, ¶meters, &result, NewCallback([]{})); |
734 | + |
735 | + mir_connection_release(connection); |
736 | + |
737 | + EXPECT_THAT(result.has_error(), Eq(false)); |
738 | + EXPECT_THAT(result.value(), Eq(__PRETTY_FUNCTION__)); |
739 | } |
740 | |
741 | === modified file 'tests/acceptance-tests/test_protobuf.proto' |
742 | --- tests/acceptance-tests/test_protobuf.proto 2014-01-10 16:51:32 +0000 |
743 | +++ tests/acceptance-tests/test_protobuf.proto 2014-01-21 15:42:35 +0000 |
744 | @@ -9,6 +9,7 @@ |
745 | |
746 | message Result { |
747 | optional string error = 127; |
748 | + optional string value = 1; |
749 | } |
750 | |
751 | service MirServer { |
752 | |
753 | === modified file 'tests/mir_test_doubles/test_protobuf_socket_server.cpp' |
754 | --- tests/mir_test_doubles/test_protobuf_socket_server.cpp 2014-01-15 12:26:01 +0000 |
755 | +++ tests/mir_test_doubles/test_protobuf_socket_server.cpp 2014-01-21 15:42:35 +0000 |
756 | @@ -21,8 +21,8 @@ |
757 | #include "mir_test_doubles/stub_session_authorizer.h" |
758 | #include "mir/frontend/connector_report.h" |
759 | #include "mir/frontend/null_message_processor_report.h" |
760 | +#include "mir/frontend/protobuf_session_creator.h" |
761 | #include "src/server/frontend/published_socket_connector.h" |
762 | -#include "src/server/frontend/protobuf_session_creator.h" |
763 | |
764 | namespace mt = mir::test; |
765 | namespace mtd = mir::test::doubles; |
766 | |
767 | === modified file 'tests/unit-tests/frontend/test_published_socket_connector.cpp' |
768 | --- tests/unit-tests/frontend/test_published_socket_connector.cpp 2014-01-20 07:11:48 +0000 |
769 | +++ tests/unit-tests/frontend/test_published_socket_connector.cpp 2014-01-21 15:42:35 +0000 |
770 | @@ -20,9 +20,9 @@ |
771 | #include "mir/frontend/connector.h" |
772 | #include "mir/frontend/connector_report.h" |
773 | #include "mir/frontend/null_message_processor_report.h" |
774 | +#include "mir/frontend/protobuf_session_creator.h" |
775 | #include "src/server/frontend/resource_cache.h" |
776 | #include "src/server/frontend/published_socket_connector.h" |
777 | -#include "src/server/frontend/protobuf_session_creator.h" |
778 | |
779 | #include "mir_protobuf.pb.h" |
780 | |
781 | |
782 | === modified file 'tests/unit-tests/frontend/test_socket_session.cpp' |
783 | --- tests/unit-tests/frontend/test_socket_session.cpp 2014-01-20 07:11:48 +0000 |
784 | +++ tests/unit-tests/frontend/test_socket_session.cpp 2014-01-21 15:42:35 +0000 |
785 | @@ -1,5 +1,5 @@ |
786 | /* |
787 | - * Copyright © 2013 Canonical Ltd. |
788 | + * Copyright © 2013, 2014 Canonical Ltd. |
789 | * |
790 | * This program is free software: you can redistribute it and/or modify |
791 | * it under the terms of the GNU General Public License version 3 as |
792 | @@ -14,6 +14,7 @@ |
793 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
794 | * |
795 | * Authored by: Kevin DuBois <kevin.dubois@canonical.com> |
796 | + * Alan Griffiths <alan@octopull.co.uk> |
797 | */ |
798 | |
799 | #include "src/server/frontend/socket_session.h" |
800 | @@ -22,6 +23,8 @@ |
801 | |
802 | #include "mir_test/fake_shared.h" |
803 | |
804 | +#include "mir_protobuf_wire.pb.h" |
805 | + |
806 | #include <gmock/gmock.h> |
807 | #include <gtest/gtest.h> |
808 | |
809 | @@ -31,55 +34,76 @@ |
810 | |
811 | namespace |
812 | { |
813 | -struct MockReceiver : public mfd::MessageReceiver |
814 | +struct StubReceiver : public mfd::MessageReceiver |
815 | { |
816 | - MOCK_METHOD3(async_receive_msg, void(std::function<void(boost::system::error_code const&, size_t)> const&, |
817 | - boost::asio::streambuf&, size_t)); |
818 | + void async_receive_msg( |
819 | + std::function<void(boost::system::error_code const&, size_t)> const& callback, |
820 | + boost::asio::streambuf& stream, |
821 | + size_t size) |
822 | + { |
823 | + read_size = size; |
824 | + pstream = &stream; |
825 | + callback_function = callback; |
826 | + } |
827 | + |
828 | + void fake_receive_msg(char* buffer, size_t size) |
829 | + { |
830 | + using namespace testing; |
831 | + ASSERT_NE(nullptr, callback_function); |
832 | + ASSERT_THAT(pstream, NotNull()); |
833 | + ASSERT_THAT(read_size, Eq(size)); |
834 | + |
835 | + pstream->sputn(buffer, size); |
836 | + pstream->commit(size); |
837 | + |
838 | + boost::system::error_code code; |
839 | + callback_function(code, size); |
840 | + } |
841 | + |
842 | +private: |
843 | + std::function<void(boost::system::error_code const&, size_t)> callback_function; |
844 | + boost::asio::streambuf* pstream = nullptr; |
845 | + size_t read_size = 0; |
846 | + |
847 | MOCK_METHOD0(client_pid, pid_t()); |
848 | }; |
849 | |
850 | struct MockProcessor : public mfd::MessageProcessor |
851 | { |
852 | - MOCK_METHOD1(process_message, bool(std::istream&)); |
853 | + MOCK_METHOD1(dispatch, bool(mir::protobuf::wire::Invocation const& invocation)); |
854 | }; |
855 | } |
856 | struct SocketSessionTest : public ::testing::Test |
857 | { |
858 | testing::NiceMock<MockProcessor> mock_processor; |
859 | - testing::NiceMock<MockReceiver> mock_receiver; |
860 | + StubReceiver stub_receiver; |
861 | }; |
862 | |
863 | -TEST_F(SocketSessionTest, basic_msg) |
864 | +TEST_F(SocketSessionTest, basic_msg_is_received_and_dispatched) |
865 | { |
866 | + int const header_size = 2; |
867 | + char buffer[512]; |
868 | + mir::protobuf::wire::Invocation invocation; |
869 | + invocation.set_id(1); |
870 | + invocation.set_method_name(""); |
871 | + invocation.set_parameters(buffer, 0); |
872 | + invocation.set_protocol_version(1); |
873 | + auto const body_size = invocation.ByteSize(); |
874 | + |
875 | using namespace testing; |
876 | |
877 | std::shared_ptr<mfd::ConnectedSessions<mfd::SocketSession>> null_sessions; |
878 | - std::function<void(boost::system::error_code const&, size_t)> header_read, body_read; |
879 | - |
880 | - size_t header_size = 2; |
881 | - EXPECT_CALL(mock_receiver, async_receive_msg(_,_, header_size)) |
882 | - .Times(1) |
883 | - .WillOnce(SaveArg<0>(&header_read)); |
884 | - |
885 | - mfd::SocketSession session(mt::fake_shared(mock_receiver), 0, null_sessions, mt::fake_shared(mock_processor)); |
886 | - |
887 | - //trigger wait for header |
888 | + |
889 | + mfd::SocketSession session(mt::fake_shared(stub_receiver), 0, null_sessions, mt::fake_shared(mock_processor)); |
890 | + |
891 | + EXPECT_CALL(mock_processor, dispatch(_)).Times(1).WillOnce(Return(true)); |
892 | + |
893 | session.read_next_message(); |
894 | - testing::Mock::VerifyAndClearExpectations(&mock_receiver); |
895 | - |
896 | - //trigger body read |
897 | - EXPECT_CALL(mock_receiver, async_receive_msg(_,_,_)) |
898 | - .Times(1) |
899 | - .WillOnce(SaveArg<0>(&body_read)); |
900 | - |
901 | - boost::system::error_code code; |
902 | - header_read(code, 2); |
903 | - |
904 | - testing::Mock::VerifyAndClearExpectations(&mock_receiver); |
905 | - |
906 | - //trigger message process |
907 | - EXPECT_CALL(mock_processor, process_message(_)) |
908 | - .Times(1) |
909 | - .WillOnce(Return(true)); |
910 | - body_read(code, 9); |
911 | + |
912 | + buffer[0] = body_size / 0x100; |
913 | + buffer[1] = body_size % 0x100; |
914 | + stub_receiver.fake_receive_msg(buffer, header_size); |
915 | + |
916 | + invocation.SerializeToArray(buffer, sizeof buffer); |
917 | + stub_receiver.fake_receive_msg(buffer, body_size); |
918 | } |
PASSED: Continuous integration, rev:1344 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/688/ jenkins. qa.ubuntu. com/job/ mir-android- trusty- i386-build/ 675 jenkins. qa.ubuntu. com/job/ mir-clang- trusty- amd64-build/ 671 jenkins. qa.ubuntu. com/job/ mir-mediumtests -trusty- touch/277 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- amd64-ci/ 418 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- amd64-ci/ 418/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 422 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 422/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- trusty- armhf/277 jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- trusty- armhf/277/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/293 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 3208
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/688/ rebuild
http://