Merge lp:~raof/mir/manual-connection-dispatch into lp:mir
- manual-connection-dispatch
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~raof/mir/manual-connection-dispatch |
Merge into: | lp:mir |
Diff against target: |
1750 lines (+912/-252) 22 files modified
src/client/mir_connection.cpp (+18/-2) src/client/mir_connection.h (+12/-0) src/client/rpc/CMakeLists.txt (+1/-0) src/client/rpc/dispatchable.h (+55/-0) src/client/rpc/mir_protobuf_rpc_channel.cpp (+10/-0) src/client/rpc/mir_protobuf_rpc_channel.h (+8/-1) src/client/rpc/simple_rpc_thread.cpp (+103/-0) src/client/rpc/simple_rpc_thread.h (+50/-0) src/client/rpc/stream_socket_transport.cpp (+47/-116) src/client/rpc/stream_socket_transport.h (+4/-4) src/client/rpc/stream_transport.h (+4/-5) tests/include/mir_test/fd_utils.h (+69/-0) tests/include/mir_test/test_protobuf_client.h (+8/-0) tests/integration-tests/client/test_screencast.cpp (+6/-0) tests/mir_test/CMakeLists.txt (+1/-0) tests/mir_test/fd_utils.cpp (+40/-0) tests/mir_test_doubles/test_protobuf_client.cpp (+5/-1) tests/unit-tests/client/CMakeLists.txt (+1/-0) tests/unit-tests/client/test_mir_connection.cpp (+107/-4) tests/unit-tests/client/test_protobuf_rpc_channel.cpp (+2/-0) tests/unit-tests/client/test_simple_rpc_thread.cpp (+105/-0) tests/unit-tests/client/test_stream_transport.cpp (+256/-119) |
To merge this branch: | bzr merge lp:~raof/mir/manual-connection-dispatch |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir development team | Pending | ||
Review via email: mp+243613@code.launchpad.net |
Commit message
Description of the change
First stage of client-eventloop driven client library dispatch.
Unmerged revisions
- 1746. By Chris Halse Rogers
-
Test that MirConnection:
:watch_ fd() returns a pollable fd in manual dispatch mode - 1745. By Chris Halse Rogers
-
Move mt::fd_utils implementation out of header file
- 1744. By Chris Halse Rogers
-
Test that MirConnection:
:watch_ fd() returns an invalid fd when in automatic dispatch mode - 1743. By Chris Halse Rogers
-
Merge trunk
- 1742. By Chris Halse Rogers
-
Add an option to MirConnection selecting between automatic and manual dispatch.
- 1741. By Chris Halse Rogers
-
Add a simple eventloop thread implementation
- 1740. By Chris Halse Rogers
-
Fix more docs; Observers are now called from the dispatch() thread
- 1739. By Chris Halse Rogers
-
Test that we dispatch a signle event for a single ::dispatch()
- 1738. By Chris Halse Rogers
-
Simplify StreamSocketTra
nsport: :dispatch. We don't actually need to do all that exception-catching, particularly since we don't do
anything but eat the exceptions anyway. - 1737. By Chris Halse Rogers
-
Move dispatch tests to the top of the file.
These are more primitive than the observer & data read/write ones
Preview Diff
1 | === modified file 'src/client/mir_connection.cpp' | |||
2 | --- src/client/mir_connection.cpp 2014-11-27 09:00:52 +0000 | |||
3 | +++ src/client/mir_connection.cpp 2014-12-04 01:50:56 +0000 | |||
4 | @@ -22,6 +22,8 @@ | |||
5 | 22 | #include "client_platform.h" | 22 | #include "client_platform.h" |
6 | 23 | #include "client_platform_factory.h" | 23 | #include "client_platform_factory.h" |
7 | 24 | #include "rpc/mir_basic_rpc_channel.h" | 24 | #include "rpc/mir_basic_rpc_channel.h" |
8 | 25 | #include "rpc/dispatchable.h" | ||
9 | 26 | #include "rpc/simple_rpc_thread.h" | ||
10 | 25 | #include "connection_configuration.h" | 27 | #include "connection_configuration.h" |
11 | 26 | #include "display_configuration.h" | 28 | #include "display_configuration.h" |
12 | 27 | #include "connection_surface_map.h" | 29 | #include "connection_surface_map.h" |
13 | @@ -92,8 +94,13 @@ | |||
14 | 92 | { | 94 | { |
15 | 93 | } | 95 | } |
16 | 94 | 96 | ||
17 | 97 | MirConnection::MirConnection(mir::client::ConnectionConfiguration& conf) : | ||
18 | 98 | MirConnection(conf, DispatchType::automatic) | ||
19 | 99 | { | ||
20 | 100 | } | ||
21 | 101 | |||
22 | 95 | MirConnection::MirConnection( | 102 | MirConnection::MirConnection( |
24 | 96 | mir::client::ConnectionConfiguration& conf) : | 103 | mir::client::ConnectionConfiguration& conf, DispatchType dispatch) : |
25 | 97 | deregisterer{this}, | 104 | deregisterer{this}, |
26 | 98 | platform_library{conf.the_platform_library()}, | 105 | platform_library{conf.the_platform_library()}, |
27 | 99 | channel(conf.the_rpc_channel()), | 106 | channel(conf.the_rpc_channel()), |
28 | @@ -105,7 +112,11 @@ | |||
29 | 105 | display_configuration(conf.the_display_configuration()), | 112 | display_configuration(conf.the_display_configuration()), |
30 | 106 | lifecycle_control(conf.the_lifecycle_control()), | 113 | lifecycle_control(conf.the_lifecycle_control()), |
31 | 107 | surface_map(conf.the_surface_map()), | 114 | surface_map(conf.the_surface_map()), |
33 | 108 | event_handler_register(conf.the_event_handler_register()) | 115 | event_handler_register(conf.the_event_handler_register()), |
34 | 116 | eventloop{dispatch == DispatchType::automatic ? | ||
35 | 117 | new mcl::rpc::SimpleRpcThread{std::dynamic_pointer_cast<mcl::rpc::Dispatchable>(channel)} : | ||
36 | 118 | nullptr | ||
37 | 119 | } | ||
38 | 109 | { | 120 | { |
39 | 110 | connect_result.set_error("connect not called"); | 121 | connect_result.set_error("connect not called"); |
40 | 111 | { | 122 | { |
41 | @@ -333,6 +344,11 @@ | |||
42 | 333 | drm_auth_magic_wait_handle.result_received(); | 344 | drm_auth_magic_wait_handle.result_received(); |
43 | 334 | } | 345 | } |
44 | 335 | 346 | ||
45 | 347 | int MirConnection::watch_fd() const | ||
46 | 348 | { | ||
47 | 349 | return eventloop ? -1 : std::dynamic_pointer_cast<mcl::rpc::Dispatchable>(channel)->watch_fd(); | ||
48 | 350 | } | ||
49 | 351 | |||
50 | 336 | MirWaitHandle* MirConnection::drm_auth_magic(unsigned int magic, | 352 | MirWaitHandle* MirConnection::drm_auth_magic(unsigned int magic, |
51 | 337 | mir_drm_auth_magic_callback callback, | 353 | mir_drm_auth_magic_callback callback, |
52 | 338 | void* context) | 354 | void* context) |
53 | 339 | 355 | ||
54 | === modified file 'src/client/mir_connection.h' | |||
55 | --- src/client/mir_connection.h 2014-11-27 09:00:52 +0000 | |||
56 | +++ src/client/mir_connection.h 2014-12-04 01:50:56 +0000 | |||
57 | @@ -52,6 +52,7 @@ | |||
58 | 52 | namespace rpc | 52 | namespace rpc |
59 | 53 | { | 53 | { |
60 | 54 | class MirBasicRpcChannel; | 54 | class MirBasicRpcChannel; |
61 | 55 | class SimpleRpcThread; | ||
62 | 55 | } | 56 | } |
63 | 56 | } | 57 | } |
64 | 57 | 58 | ||
65 | @@ -69,12 +70,19 @@ | |||
66 | 69 | } | 70 | } |
67 | 70 | } | 71 | } |
68 | 71 | 72 | ||
69 | 73 | enum class DispatchType | ||
70 | 74 | { | ||
71 | 75 | automatic, | ||
72 | 76 | manual | ||
73 | 77 | }; | ||
74 | 78 | |||
75 | 72 | struct MirConnection : mir::client::ClientContext | 79 | struct MirConnection : mir::client::ClientContext |
76 | 73 | { | 80 | { |
77 | 74 | public: | 81 | public: |
78 | 75 | MirConnection(std::string const& error_message); | 82 | MirConnection(std::string const& error_message); |
79 | 76 | 83 | ||
80 | 77 | MirConnection(mir::client::ConnectionConfiguration& conf); | 84 | MirConnection(mir::client::ConnectionConfiguration& conf); |
81 | 85 | MirConnection(mir::client::ConnectionConfiguration &conf, DispatchType dispatch); | ||
82 | 78 | ~MirConnection() noexcept; | 86 | ~MirConnection() noexcept; |
83 | 79 | 87 | ||
84 | 80 | MirConnection(MirConnection const &) = delete; | 88 | MirConnection(MirConnection const &) = delete; |
85 | @@ -100,6 +108,8 @@ | |||
86 | 100 | 108 | ||
87 | 101 | MirWaitHandle* disconnect(); | 109 | MirWaitHandle* disconnect(); |
88 | 102 | 110 | ||
89 | 111 | int watch_fd() const; | ||
90 | 112 | |||
91 | 103 | MirWaitHandle* drm_auth_magic(unsigned int magic, | 113 | MirWaitHandle* drm_auth_magic(unsigned int magic, |
92 | 104 | mir_drm_auth_magic_callback callback, | 114 | mir_drm_auth_magic_callback callback, |
93 | 105 | void* context); | 115 | void* context); |
94 | @@ -183,6 +193,8 @@ | |||
95 | 183 | 193 | ||
96 | 184 | std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register; | 194 | std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register; |
97 | 185 | 195 | ||
98 | 196 | std::unique_ptr<mir::client::rpc::SimpleRpcThread> const eventloop; | ||
99 | 197 | |||
100 | 186 | std::vector<int> extra_platform_data; | 198 | std::vector<int> extra_platform_data; |
101 | 187 | 199 | ||
102 | 188 | struct SurfaceRelease; | 200 | struct SurfaceRelease; |
103 | 189 | 201 | ||
104 | === modified file 'src/client/rpc/CMakeLists.txt' | |||
105 | --- src/client/rpc/CMakeLists.txt 2014-11-27 09:00:52 +0000 | |||
106 | +++ src/client/rpc/CMakeLists.txt 2014-12-04 01:50:56 +0000 | |||
107 | @@ -5,4 +5,5 @@ | |||
108 | 5 | mir_protobuf_rpc_channel.cpp | 5 | mir_protobuf_rpc_channel.cpp |
109 | 6 | make_socket_rpc_channel.cpp | 6 | make_socket_rpc_channel.cpp |
110 | 7 | stream_socket_transport.cpp | 7 | stream_socket_transport.cpp |
111 | 8 | simple_rpc_thread.cpp | ||
112 | 8 | ) | 9 | ) |
113 | 9 | 10 | ||
114 | === added file 'src/client/rpc/dispatchable.h' | |||
115 | --- src/client/rpc/dispatchable.h 1970-01-01 00:00:00 +0000 | |||
116 | +++ src/client/rpc/dispatchable.h 2014-12-04 01:50:56 +0000 | |||
117 | @@ -0,0 +1,55 @@ | |||
118 | 1 | /* | ||
119 | 2 | * Copyright © 2014 Canonical Ltd. | ||
120 | 3 | * | ||
121 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
122 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
123 | 6 | * as published by the Free Software Foundation. | ||
124 | 7 | * | ||
125 | 8 | * This program is distributed in the hope that it will be useful, | ||
126 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
127 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
128 | 11 | * GNU Lesser General Public License for more details. | ||
129 | 12 | * | ||
130 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
131 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
132 | 15 | * | ||
133 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
134 | 17 | */ | ||
135 | 18 | |||
136 | 19 | #ifndef MIR_CLIENT_RPC_DISPATCHABLE_H_ | ||
137 | 20 | #define MIR_CLIENT_RPC_DISPATCHABLE_H_ | ||
138 | 21 | |||
139 | 22 | #include "mir/fd.h" | ||
140 | 23 | |||
141 | 24 | namespace mir | ||
142 | 25 | { | ||
143 | 26 | namespace client | ||
144 | 27 | { | ||
145 | 28 | namespace rpc | ||
146 | 29 | { | ||
147 | 30 | class Dispatchable | ||
148 | 31 | { | ||
149 | 32 | public: | ||
150 | 33 | Dispatchable() = default; | ||
151 | 34 | virtual ~Dispatchable() = default; | ||
152 | 35 | |||
153 | 36 | Dispatchable& operator=(Dispatchable const&) = delete; | ||
154 | 37 | Dispatchable(Dispatchable const&) = delete; | ||
155 | 38 | |||
156 | 39 | /** | ||
157 | 40 | * \brief Get a poll()able file descriptor | ||
158 | 41 | * \return A file descriptor usable with poll() or equivalent function calls that | ||
159 | 42 | * becomes readable when there are dispatchable events | ||
160 | 43 | */ | ||
161 | 44 | virtual Fd watch_fd() const = 0; | ||
162 | 45 | |||
163 | 46 | /** | ||
164 | 47 | * \brief Dispatch one pending event | ||
165 | 48 | */ | ||
166 | 49 | virtual void dispatch() = 0; | ||
167 | 50 | }; | ||
168 | 51 | } | ||
169 | 52 | } | ||
170 | 53 | } | ||
171 | 54 | |||
172 | 55 | #endif // MIR_CLIENT_RPC_DISPATCHABLE_H_ | ||
173 | 0 | 56 | ||
174 | === modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp' | |||
175 | --- src/client/rpc/mir_protobuf_rpc_channel.cpp 2014-11-27 09:00:52 +0000 | |||
176 | +++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2014-12-04 01:50:56 +0000 | |||
177 | @@ -338,3 +338,13 @@ | |||
178 | 338 | { | 338 | { |
179 | 339 | notify_disconnected(); | 339 | notify_disconnected(); |
180 | 340 | } | 340 | } |
181 | 341 | |||
182 | 342 | mir::Fd mir::client::rpc::MirProtobufRpcChannel::watch_fd() const | ||
183 | 343 | { | ||
184 | 344 | return transport->watch_fd(); | ||
185 | 345 | } | ||
186 | 346 | |||
187 | 347 | void mir::client::rpc::MirProtobufRpcChannel::dispatch() | ||
188 | 348 | { | ||
189 | 349 | transport->dispatch(); | ||
190 | 350 | } | ||
191 | 341 | 351 | ||
192 | === modified file 'src/client/rpc/mir_protobuf_rpc_channel.h' | |||
193 | --- src/client/rpc/mir_protobuf_rpc_channel.h 2014-11-27 09:00:52 +0000 | |||
194 | +++ src/client/rpc/mir_protobuf_rpc_channel.h 2014-12-04 01:50:56 +0000 | |||
195 | @@ -21,6 +21,7 @@ | |||
196 | 21 | 21 | ||
197 | 22 | #include "mir_basic_rpc_channel.h" | 22 | #include "mir_basic_rpc_channel.h" |
198 | 23 | #include "stream_transport.h" | 23 | #include "stream_transport.h" |
199 | 24 | #include "dispatchable.h" | ||
200 | 24 | 25 | ||
201 | 25 | #include <google/protobuf/service.h> | 26 | #include <google/protobuf/service.h> |
202 | 26 | #include <google/protobuf/descriptor.h> | 27 | #include <google/protobuf/descriptor.h> |
203 | @@ -52,7 +53,8 @@ | |||
204 | 52 | 53 | ||
205 | 53 | class MirProtobufRpcChannel : | 54 | class MirProtobufRpcChannel : |
206 | 54 | public MirBasicRpcChannel, | 55 | public MirBasicRpcChannel, |
208 | 55 | public StreamTransport::Observer | 56 | public StreamTransport::Observer, |
209 | 57 | public Dispatchable | ||
210 | 56 | { | 58 | { |
211 | 57 | public: | 59 | public: |
212 | 58 | MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport, | 60 | MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport, |
213 | @@ -64,8 +66,13 @@ | |||
214 | 64 | 66 | ||
215 | 65 | ~MirProtobufRpcChannel() = default; | 67 | ~MirProtobufRpcChannel() = default; |
216 | 66 | 68 | ||
217 | 69 | // StreamTransport::Observer | ||
218 | 67 | void on_data_available() override; | 70 | void on_data_available() override; |
219 | 68 | void on_disconnected() override; | 71 | void on_disconnected() override; |
220 | 72 | |||
221 | 73 | // Dispatchable | ||
222 | 74 | Fd watch_fd() const override; | ||
223 | 75 | void dispatch() override; | ||
224 | 69 | private: | 76 | private: |
225 | 70 | virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*, | 77 | virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*, |
226 | 71 | const google::protobuf::Message* parameters, google::protobuf::Message* response, | 78 | const google::protobuf::Message* parameters, google::protobuf::Message* response, |
227 | 72 | 79 | ||
228 | === added file 'src/client/rpc/simple_rpc_thread.cpp' | |||
229 | --- src/client/rpc/simple_rpc_thread.cpp 1970-01-01 00:00:00 +0000 | |||
230 | +++ src/client/rpc/simple_rpc_thread.cpp 2014-12-04 01:50:56 +0000 | |||
231 | @@ -0,0 +1,103 @@ | |||
232 | 1 | /* | ||
233 | 2 | * Copyright © 2014 Canonical Ltd. | ||
234 | 3 | * | ||
235 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
236 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
237 | 6 | * as published by the Free Software Foundation. | ||
238 | 7 | * | ||
239 | 8 | * This program is distributed in the hope that it will be useful, | ||
240 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
241 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
242 | 11 | * GNU Lesser General Public License for more details. | ||
243 | 12 | * | ||
244 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
245 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
246 | 15 | * | ||
247 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
248 | 17 | */ | ||
249 | 18 | |||
250 | 19 | #include "simple_rpc_thread.h" | ||
251 | 20 | #include "dispatchable.h" | ||
252 | 21 | |||
253 | 22 | #include <sys/epoll.h> | ||
254 | 23 | #include <unistd.h> | ||
255 | 24 | #include <system_error> | ||
256 | 25 | #include <signal.h> | ||
257 | 26 | #include <boost/exception/all.hpp> | ||
258 | 27 | |||
259 | 28 | namespace mclr = mir::client::rpc; | ||
260 | 29 | |||
261 | 30 | namespace | ||
262 | 31 | { | ||
263 | 32 | void wait_for_events_forever(std::shared_ptr<mclr::Dispatchable> const& dispatchee, mir::Fd shutdown_fd) | ||
264 | 33 | { | ||
265 | 34 | auto epoll_fd = mir::Fd{epoll_create1(0)}; | ||
266 | 35 | if (epoll_fd == mir::Fd::invalid) | ||
267 | 36 | { | ||
268 | 37 | BOOST_THROW_EXCEPTION((std::system_error{errno, | ||
269 | 38 | std::system_category(), | ||
270 | 39 | "Failed to create epoll IO monitor"})); | ||
271 | 40 | } | ||
272 | 41 | epoll_event event; | ||
273 | 42 | memset(&event, 0, sizeof(event)); | ||
274 | 43 | |||
275 | 44 | // We only care when the shutdown pipe has been closed | ||
276 | 45 | event.events = EPOLLRDHUP; | ||
277 | 46 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event); | ||
278 | 47 | |||
279 | 48 | // We want readability or closure for the dispatchee. | ||
280 | 49 | event.events = EPOLLIN | EPOLLRDHUP; | ||
281 | 50 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dispatchee->watch_fd(), &event); | ||
282 | 51 | |||
283 | 52 | for (;;) | ||
284 | 53 | { | ||
285 | 54 | epoll_wait(epoll_fd, &event, 1, -1); | ||
286 | 55 | if (event.events & EPOLLIN) | ||
287 | 56 | { | ||
288 | 57 | dispatchee->dispatch(); | ||
289 | 58 | } | ||
290 | 59 | else if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) | ||
291 | 60 | { | ||
292 | 61 | // On hangup we go away. | ||
293 | 62 | return; | ||
294 | 63 | } | ||
295 | 64 | } | ||
296 | 65 | } | ||
297 | 66 | |||
298 | 67 | } | ||
299 | 68 | |||
300 | 69 | mclr::SimpleRpcThread::SimpleRpcThread(std::shared_ptr<mclr::Dispatchable> const& dispatchee) | ||
301 | 70 | { | ||
302 | 71 | int pipefds[2]; | ||
303 | 72 | if (pipe(pipefds) < 0) | ||
304 | 73 | { | ||
305 | 74 | BOOST_THROW_EXCEPTION((std::system_error{errno, | ||
306 | 75 | std::system_category(), | ||
307 | 76 | "Failed to create shutdown pipe for IO thread"})); | ||
308 | 77 | } | ||
309 | 78 | shutdown_fd = mir::Fd{pipefds[1]}; | ||
310 | 79 | mir::Fd const terminate_fd = mir::Fd{pipefds[0]}; | ||
311 | 80 | eventloop = std::thread{[dispatchee, terminate_fd]() | ||
312 | 81 | { | ||
313 | 82 | // Our IO threads must not receive any signals | ||
314 | 83 | sigset_t all_signals; | ||
315 | 84 | sigfillset(&all_signals); | ||
316 | 85 | |||
317 | 86 | if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL)) | ||
318 | 87 | BOOST_THROW_EXCEPTION(( | ||
319 | 88 | std::system_error{error, | ||
320 | 89 | std::system_category(), | ||
321 | 90 | "Failed to block signals on IO thread"})); | ||
322 | 91 | |||
323 | 92 | wait_for_events_forever(dispatchee, terminate_fd); | ||
324 | 93 | }}; | ||
325 | 94 | } | ||
326 | 95 | |||
327 | 96 | mclr::SimpleRpcThread::~SimpleRpcThread() noexcept | ||
328 | 97 | { | ||
329 | 98 | ::close(shutdown_fd); | ||
330 | 99 | if (eventloop.joinable()) | ||
331 | 100 | { | ||
332 | 101 | eventloop.join(); | ||
333 | 102 | } | ||
334 | 103 | } | ||
335 | 0 | 104 | ||
336 | === added file 'src/client/rpc/simple_rpc_thread.h' | |||
337 | --- src/client/rpc/simple_rpc_thread.h 1970-01-01 00:00:00 +0000 | |||
338 | +++ src/client/rpc/simple_rpc_thread.h 2014-12-04 01:50:56 +0000 | |||
339 | @@ -0,0 +1,50 @@ | |||
340 | 1 | /* | ||
341 | 2 | * Copyright © 2014 Canonical Ltd. | ||
342 | 3 | * | ||
343 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
344 | 5 | * under the terms of the GNU Lesser General Public License version 3, | ||
345 | 6 | * as published by the Free Software Foundation. | ||
346 | 7 | * | ||
347 | 8 | * This program is distributed in the hope that it will be useful, | ||
348 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
349 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
350 | 11 | * GNU Lesser General Public License for more details. | ||
351 | 12 | * | ||
352 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
353 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
354 | 15 | * | ||
355 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
356 | 17 | */ | ||
357 | 18 | |||
358 | 19 | #ifndef MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_ | ||
359 | 20 | #define MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_ | ||
360 | 21 | |||
361 | 22 | #include <memory> | ||
362 | 23 | #include <thread> | ||
363 | 24 | #include "mir/fd.h" | ||
364 | 25 | |||
365 | 26 | namespace mir | ||
366 | 27 | { | ||
367 | 28 | namespace client | ||
368 | 29 | { | ||
369 | 30 | namespace rpc | ||
370 | 31 | { | ||
371 | 32 | class Dispatchable; | ||
372 | 33 | |||
373 | 34 | class SimpleRpcThread | ||
374 | 35 | { | ||
375 | 36 | public: | ||
376 | 37 | SimpleRpcThread(std::shared_ptr<Dispatchable> const& dispatchee); | ||
377 | 38 | ~SimpleRpcThread() noexcept; | ||
378 | 39 | |||
379 | 40 | private: | ||
380 | 41 | Fd shutdown_fd; | ||
381 | 42 | std::thread eventloop; | ||
382 | 43 | }; | ||
383 | 44 | |||
384 | 45 | } | ||
385 | 46 | } | ||
386 | 47 | } | ||
387 | 48 | |||
388 | 49 | |||
389 | 50 | #endif // MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_ | ||
390 | 0 | 51 | ||
391 | === modified file 'src/client/rpc/stream_socket_transport.cpp' | |||
392 | --- src/client/rpc/stream_socket_transport.cpp 2014-11-27 09:00:52 +0000 | |||
393 | +++ src/client/rpc/stream_socket_transport.cpp 2014-12-04 01:50:56 +0000 | |||
394 | @@ -23,7 +23,6 @@ | |||
395 | 23 | 23 | ||
396 | 24 | #include <system_error> | 24 | #include <system_error> |
397 | 25 | 25 | ||
398 | 26 | #include <signal.h> | ||
399 | 27 | #include <errno.h> | 26 | #include <errno.h> |
400 | 28 | #include <sys/epoll.h> | 27 | #include <sys/epoll.h> |
401 | 29 | #include <sys/types.h> | 28 | #include <sys/types.h> |
402 | @@ -35,10 +34,23 @@ | |||
403 | 35 | 34 | ||
404 | 36 | namespace mclr = mir::client::rpc; | 35 | namespace mclr = mir::client::rpc; |
405 | 37 | 36 | ||
408 | 38 | mclr::StreamSocketTransport::StreamSocketTransport(mir::Fd const& fd) | 37 | mclr::StreamSocketTransport::StreamSocketTransport(Fd const& fd) |
409 | 39 | : socket_fd{fd} | 38 | : socket_fd{fd}, |
410 | 39 | epoll_fd{epoll_create1(EPOLL_CLOEXEC)} | ||
411 | 40 | { | 40 | { |
413 | 41 | init(); | 41 | if (epoll_fd < 0) |
414 | 42 | { | ||
415 | 43 | BOOST_THROW_EXCEPTION((std::system_error{errno, | ||
416 | 44 | std::system_category(), | ||
417 | 45 | "Failed to create epoll monitor for IO"})); | ||
418 | 46 | } | ||
419 | 47 | epoll_event event; | ||
420 | 48 | // Make valgrind happy, harder | ||
421 | 49 | memset(&event, 0, sizeof(event)); | ||
422 | 50 | |||
423 | 51 | event.events = EPOLLIN | EPOLLRDHUP; | ||
424 | 52 | event.data.fd = socket_fd; | ||
425 | 53 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event); | ||
426 | 42 | } | 54 | } |
427 | 43 | 55 | ||
428 | 44 | mclr::StreamSocketTransport::StreamSocketTransport(std::string const& socket_path) | 56 | mclr::StreamSocketTransport::StreamSocketTransport(std::string const& socket_path) |
429 | @@ -46,17 +58,6 @@ | |||
430 | 46 | { | 58 | { |
431 | 47 | } | 59 | } |
432 | 48 | 60 | ||
433 | 49 | mclr::StreamSocketTransport::~StreamSocketTransport() | ||
434 | 50 | { | ||
435 | 51 | int dummy{0}; | ||
436 | 52 | send(shutdown_fd, &dummy, sizeof(dummy), MSG_NOSIGNAL); | ||
437 | 53 | if (io_service_thread.joinable()) | ||
438 | 54 | { | ||
439 | 55 | io_service_thread.join(); | ||
440 | 56 | } | ||
441 | 57 | close(shutdown_fd); | ||
442 | 58 | } | ||
443 | 59 | |||
444 | 60 | void mclr::StreamSocketTransport::register_observer(std::shared_ptr<Observer> const& observer) | 61 | void mclr::StreamSocketTransport::register_observer(std::shared_ptr<Observer> const& observer) |
445 | 61 | { | 62 | { |
446 | 62 | std::lock_guard<decltype(observer_mutex)> lock(observer_mutex); | 63 | std::lock_guard<decltype(observer_mutex)> lock(observer_mutex); |
447 | @@ -176,110 +177,40 @@ | |||
448 | 176 | mir::send_fds(socket_fd, fds); | 177 | mir::send_fds(socket_fd, fds); |
449 | 177 | } | 178 | } |
450 | 178 | 179 | ||
452 | 179 | void mclr::StreamSocketTransport::init() | 180 | mir::Fd mclr::StreamSocketTransport::watch_fd() const |
453 | 180 | { | 181 | { |
459 | 181 | // We use sockets rather than a pipe so that we can control | 182 | return epoll_fd; |
460 | 182 | // EPIPE behaviour; we don't want SIGPIPE when the IO loop terminates. | 183 | } |
456 | 183 | int socket_fds[2]; | ||
457 | 184 | socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds); | ||
458 | 185 | this->shutdown_fd = mir::Fd{socket_fds[1]}; | ||
461 | 186 | 184 | ||
464 | 187 | auto shutdown_fd = mir::Fd{socket_fds[0]}; | 185 | void mclr::StreamSocketTransport::dispatch() |
465 | 188 | io_service_thread = std::thread([this, shutdown_fd] | 186 | { |
466 | 187 | epoll_event event; | ||
467 | 188 | epoll_wait(epoll_fd, &event, 1, 0); | ||
468 | 189 | if (event.data.fd == socket_fd) | ||
469 | 189 | { | 190 | { |
563 | 190 | // Our IO threads must not receive any signals | 191 | if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) |
564 | 191 | sigset_t all_signals; | 192 | { |
565 | 192 | sigfillset(&all_signals); | 193 | if (event.events & EPOLLIN) |
566 | 193 | 194 | { | |
567 | 194 | if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL)) | 195 | // If the remote end shut down cleanly it's possible there's some more |
568 | 195 | BOOST_THROW_EXCEPTION( | 196 | // data left to read, or that reads will now return 0 (EOF) |
569 | 196 | boost::enable_error_info( | 197 | // |
570 | 197 | std::runtime_error("Failed to block signals on IO thread")) << boost::errinfo_errno(error)); | 198 | // If there's more data left to read, notify of this before disconnect. |
571 | 198 | 199 | int dummy; | |
572 | 199 | mir::set_thread_name("Client IO loop"); | 200 | if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0) |
573 | 200 | 201 | { | |
574 | 201 | int epoll_fd = epoll_create1(0); | 202 | notify_data_available(); |
575 | 202 | 203 | return; | |
576 | 203 | epoll_event event; | 204 | } |
577 | 204 | // Make valgrind happy, harder | 205 | } |
578 | 205 | memset(&event, 0, sizeof(event)); | 206 | notify_disconnected(); |
579 | 206 | 207 | epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, nullptr); | |
580 | 207 | event.events = EPOLLIN | EPOLLRDHUP; | 208 | } |
581 | 208 | event.data.fd = socket_fd; | 209 | else if (event.events & EPOLLIN) |
582 | 209 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event); | 210 | { |
583 | 210 | 211 | notify_data_available(); | |
584 | 211 | event.events = EPOLLIN | EPOLLRDHUP; | 212 | } |
585 | 212 | event.data.fd = shutdown_fd; | 213 | } |
493 | 213 | epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event); | ||
494 | 214 | |||
495 | 215 | bool shutdown_requested{false}; | ||
496 | 216 | while (!shutdown_requested) | ||
497 | 217 | { | ||
498 | 218 | epoll_event event; | ||
499 | 219 | epoll_wait(epoll_fd, &event, 1, -1); | ||
500 | 220 | if (event.data.fd == socket_fd) | ||
501 | 221 | { | ||
502 | 222 | if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) | ||
503 | 223 | { | ||
504 | 224 | if (event.events & EPOLLIN) | ||
505 | 225 | { | ||
506 | 226 | // If the remote end shut down cleanly it's possible there's some more | ||
507 | 227 | // data left to read, or that reads will now return 0 (EOF) | ||
508 | 228 | // | ||
509 | 229 | // If there's more data left to read, notify of this before disconnect. | ||
510 | 230 | int dummy; | ||
511 | 231 | if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0) | ||
512 | 232 | { | ||
513 | 233 | try | ||
514 | 234 | { | ||
515 | 235 | notify_data_available(); | ||
516 | 236 | } | ||
517 | 237 | catch(...) | ||
518 | 238 | { | ||
519 | 239 | //It's quite likely that notify_data_available() will lead to | ||
520 | 240 | //an exception being thrown; after all, the remote has closed | ||
521 | 241 | //the connection. | ||
522 | 242 | // | ||
523 | 243 | //This doesn't matter; we're already shutting down. | ||
524 | 244 | } | ||
525 | 245 | } | ||
526 | 246 | } | ||
527 | 247 | notify_disconnected(); | ||
528 | 248 | shutdown_requested = true; | ||
529 | 249 | } | ||
530 | 250 | else if (event.events & EPOLLIN) | ||
531 | 251 | { | ||
532 | 252 | try | ||
533 | 253 | { | ||
534 | 254 | notify_data_available(); | ||
535 | 255 | } | ||
536 | 256 | catch (socket_disconnected_error &err) | ||
537 | 257 | { | ||
538 | 258 | // We've already notified of disconnection. | ||
539 | 259 | shutdown_requested = true; | ||
540 | 260 | } | ||
541 | 261 | // These need not be fatal. | ||
542 | 262 | catch (fd_reception_error &err) | ||
543 | 263 | { | ||
544 | 264 | } | ||
545 | 265 | catch (socket_error &err) | ||
546 | 266 | { | ||
547 | 267 | } | ||
548 | 268 | catch (...) | ||
549 | 269 | { | ||
550 | 270 | // We've no idea what the problem is, so clean up as best we can. | ||
551 | 271 | notify_disconnected(); | ||
552 | 272 | shutdown_requested = true; | ||
553 | 273 | } | ||
554 | 274 | } | ||
555 | 275 | } | ||
556 | 276 | if (event.data.fd == shutdown_fd) | ||
557 | 277 | { | ||
558 | 278 | shutdown_requested = true; | ||
559 | 279 | } | ||
560 | 280 | } | ||
561 | 281 | ::close(epoll_fd); | ||
562 | 282 | }); | ||
586 | 283 | } | 214 | } |
587 | 284 | 215 | ||
588 | 285 | mir::Fd mclr::StreamSocketTransport::open_socket(std::string const& path) | 216 | mir::Fd mclr::StreamSocketTransport::open_socket(std::string const& path) |
589 | 286 | 217 | ||
590 | === modified file 'src/client/rpc/stream_socket_transport.h' | |||
591 | --- src/client/rpc/stream_socket_transport.h 2014-11-27 09:00:52 +0000 | |||
592 | +++ src/client/rpc/stream_socket_transport.h 2014-12-04 01:50:56 +0000 | |||
593 | @@ -38,22 +38,22 @@ | |||
594 | 38 | public: | 38 | public: |
595 | 39 | StreamSocketTransport(Fd const& fd); | 39 | StreamSocketTransport(Fd const& fd); |
596 | 40 | StreamSocketTransport(std::string const& socket_path); | 40 | StreamSocketTransport(std::string const& socket_path); |
597 | 41 | ~StreamSocketTransport() override; | ||
598 | 42 | 41 | ||
599 | 43 | void register_observer(std::shared_ptr<Observer> const& observer) override; | 42 | void register_observer(std::shared_ptr<Observer> const& observer) override; |
600 | 44 | void receive_data(void* buffer, size_t bytes_requested) override; | 43 | void receive_data(void* buffer, size_t bytes_requested) override; |
601 | 45 | void receive_data(void* buffer, size_t bytes_requested, std::vector<Fd>& fds) override; | 44 | void receive_data(void* buffer, size_t bytes_requested, std::vector<Fd>& fds) override; |
602 | 46 | void send_message(std::vector<uint8_t> const& buffer, std::vector<mir::Fd> const& fds) override; | 45 | void send_message(std::vector<uint8_t> const& buffer, std::vector<mir::Fd> const& fds) override; |
603 | 47 | 46 | ||
604 | 47 | Fd watch_fd() const override; | ||
605 | 48 | void dispatch() override; | ||
606 | 49 | |||
607 | 48 | private: | 50 | private: |
608 | 49 | void init(); | ||
609 | 50 | Fd open_socket(std::string const& path); | 51 | Fd open_socket(std::string const& path); |
610 | 51 | void notify_data_available(); | 52 | void notify_data_available(); |
611 | 52 | void notify_disconnected(); | 53 | void notify_disconnected(); |
612 | 53 | 54 | ||
613 | 54 | std::thread io_service_thread; | ||
614 | 55 | Fd const socket_fd; | 55 | Fd const socket_fd; |
616 | 56 | Fd shutdown_fd; | 56 | Fd const epoll_fd; |
617 | 57 | 57 | ||
618 | 58 | std::mutex observer_mutex; | 58 | std::mutex observer_mutex; |
619 | 59 | std::vector<std::shared_ptr<Observer>> observers; | 59 | std::vector<std::shared_ptr<Observer>> observers; |
620 | 60 | 60 | ||
621 | === modified file 'src/client/rpc/stream_transport.h' | |||
622 | --- src/client/rpc/stream_transport.h 2014-11-27 09:00:52 +0000 | |||
623 | +++ src/client/rpc/stream_transport.h 2014-12-04 01:50:56 +0000 | |||
624 | @@ -25,6 +25,7 @@ | |||
625 | 25 | #include <stdint.h> | 25 | #include <stdint.h> |
626 | 26 | 26 | ||
627 | 27 | #include "mir/fd.h" | 27 | #include "mir/fd.h" |
628 | 28 | #include "dispatchable.h" | ||
629 | 28 | 29 | ||
630 | 29 | namespace mir | 30 | namespace mir |
631 | 30 | { | 31 | { |
632 | @@ -64,7 +65,7 @@ | |||
633 | 64 | * from different threads. Multiple threads calling the same | 65 | * from different threads. Multiple threads calling the same |
634 | 65 | * function need synchronisation. | 66 | * function need synchronisation. |
635 | 66 | */ | 67 | */ |
637 | 67 | class StreamTransport | 68 | class StreamTransport : public Dispatchable |
638 | 68 | { | 69 | { |
639 | 69 | public: | 70 | public: |
640 | 70 | /** | 71 | /** |
641 | @@ -80,8 +81,8 @@ | |||
642 | 80 | 81 | ||
643 | 81 | /** | 82 | /** |
644 | 82 | * \brief Observer of IO status | 83 | * \brief Observer of IO status |
647 | 83 | * \note The Transport may call Observer members from arbitrary threads. | 84 | * \note The Transport will only call Observers in response to dispatch(), |
648 | 84 | * The Observer implementation is responsible for any synchronisation. | 85 | * and on the thread calling dispatch(). |
649 | 85 | */ | 86 | */ |
650 | 86 | class Observer | 87 | class Observer |
651 | 87 | { | 88 | { |
652 | @@ -106,8 +107,6 @@ | |||
653 | 106 | /** | 107 | /** |
654 | 107 | * \brief Register an IO observer | 108 | * \brief Register an IO observer |
655 | 108 | * \param [in] observer | 109 | * \param [in] observer |
656 | 109 | * \note There is no guarantee which thread will call into the observer. | ||
657 | 110 | * Synchronisation is the responsibility of the caller. | ||
658 | 111 | */ | 110 | */ |
659 | 112 | virtual void register_observer(std::shared_ptr<Observer> const& observer) = 0; | 111 | virtual void register_observer(std::shared_ptr<Observer> const& observer) = 0; |
660 | 113 | 112 | ||
661 | 114 | 113 | ||
662 | === added file 'tests/include/mir_test/fd_utils.h' | |||
663 | --- tests/include/mir_test/fd_utils.h 1970-01-01 00:00:00 +0000 | |||
664 | +++ tests/include/mir_test/fd_utils.h 2014-12-04 01:50:56 +0000 | |||
665 | @@ -0,0 +1,69 @@ | |||
666 | 1 | /* | ||
667 | 2 | * Copyright © 2014 Canonical Ltd. | ||
668 | 3 | * | ||
669 | 4 | * This program is free software: you can redistribute it and/or modify | ||
670 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
671 | 6 | * published by the Free Software Foundation. | ||
672 | 7 | * | ||
673 | 8 | * This program is distributed in the hope that it will be useful, | ||
674 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
675 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
676 | 11 | * GNU General Public License for more details. | ||
677 | 12 | * | ||
678 | 13 | * You should have received a copy of the GNU General Public License | ||
679 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
680 | 15 | * | ||
681 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
682 | 17 | */ | ||
683 | 18 | |||
684 | 19 | #ifndef MIR_TEST_FD_UTILS_H_ | ||
685 | 20 | #define MIR_TEST_FD_UTILS_H_ | ||
686 | 21 | |||
687 | 22 | #include "mir/fd.h" | ||
688 | 23 | #include <chrono> | ||
689 | 24 | |||
690 | 25 | #include <poll.h> | ||
691 | 26 | |||
692 | 27 | #include <gtest/gtest.h> | ||
693 | 28 | |||
694 | 29 | namespace mir | ||
695 | 30 | { | ||
696 | 31 | namespace test | ||
697 | 32 | { | ||
698 | 33 | ::testing::AssertionResult std_call_succeeded(int retval); | ||
699 | 34 | |||
700 | 35 | ::testing::AssertionResult fd_is_readable(mir::Fd const& fd); | ||
701 | 36 | |||
702 | 37 | template<typename Period, typename Rep> | ||
703 | 38 | ::testing::AssertionResult fd_becomes_readable(mir::Fd const& fd, | ||
704 | 39 | std::chrono::duration<Period, Rep> timeout) | ||
705 | 40 | { | ||
706 | 41 | int timeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count(); | ||
707 | 42 | |||
708 | 43 | pollfd readable; | ||
709 | 44 | readable.events = POLLIN; | ||
710 | 45 | readable.fd = fd; | ||
711 | 46 | |||
712 | 47 | auto result = std_call_succeeded(poll(&readable, 1, timeout_ms)); | ||
713 | 48 | if (result) | ||
714 | 49 | { | ||
715 | 50 | if (readable.revents & POLLERR) | ||
716 | 51 | { | ||
717 | 52 | return ::testing::AssertionFailure() << "error condition on fd"; | ||
718 | 53 | } | ||
719 | 54 | if (readable.revents & POLLNVAL) | ||
720 | 55 | { | ||
721 | 56 | return ::testing::AssertionFailure() << "fd is invalid"; | ||
722 | 57 | } | ||
723 | 58 | if (!(readable.revents & POLLIN)) | ||
724 | 59 | { | ||
725 | 60 | return ::testing::AssertionFailure() << "fd is not readable"; | ||
726 | 61 | } | ||
727 | 62 | return ::testing::AssertionSuccess(); | ||
728 | 63 | } | ||
729 | 64 | return result; | ||
730 | 65 | } | ||
731 | 66 | } | ||
732 | 67 | } | ||
733 | 68 | |||
734 | 69 | #endif // MIR_TEST_FD_UTILS_H_ | ||
735 | 0 | 70 | ||
736 | === modified file 'tests/include/mir_test/test_protobuf_client.h' | |||
737 | --- tests/include/mir_test/test_protobuf_client.h 2014-11-27 09:00:52 +0000 | |||
738 | +++ tests/include/mir_test/test_protobuf_client.h 2014-12-04 01:50:56 +0000 | |||
739 | @@ -30,6 +30,13 @@ | |||
740 | 30 | 30 | ||
741 | 31 | namespace mir | 31 | namespace mir |
742 | 32 | { | 32 | { |
743 | 33 | namespace client | ||
744 | 34 | { | ||
745 | 35 | namespace rpc | ||
746 | 36 | { | ||
747 | 37 | class SimpleRpcThread; | ||
748 | 38 | } | ||
749 | 39 | } | ||
750 | 33 | namespace test | 40 | namespace test |
751 | 34 | { | 41 | { |
752 | 35 | namespace doubles | 42 | namespace doubles |
753 | @@ -42,6 +49,7 @@ | |||
754 | 42 | 49 | ||
755 | 43 | std::shared_ptr<doubles::MockRpcReport> rpc_report; | 50 | std::shared_ptr<doubles::MockRpcReport> rpc_report; |
756 | 44 | std::shared_ptr<google::protobuf::RpcChannel> channel; | 51 | std::shared_ptr<google::protobuf::RpcChannel> channel; |
757 | 52 | std::shared_ptr<client::rpc::SimpleRpcThread> eventloop; | ||
758 | 45 | mir::protobuf::DisplayServer::Stub display_server; | 53 | mir::protobuf::DisplayServer::Stub display_server; |
759 | 46 | mir::protobuf::ConnectParameters connect_parameters; | 54 | mir::protobuf::ConnectParameters connect_parameters; |
760 | 47 | mir::protobuf::SurfaceParameters surface_parameters; | 55 | mir::protobuf::SurfaceParameters surface_parameters; |
761 | 48 | 56 | ||
762 | === modified file 'tests/integration-tests/client/test_screencast.cpp' | |||
763 | --- tests/integration-tests/client/test_screencast.cpp 2014-11-27 09:00:52 +0000 | |||
764 | +++ tests/integration-tests/client/test_screencast.cpp 2014-12-04 01:50:56 +0000 | |||
765 | @@ -18,6 +18,8 @@ | |||
766 | 18 | 18 | ||
767 | 19 | #include "mir_protobuf.pb.h" | 19 | #include "mir_protobuf.pb.h" |
768 | 20 | #include "src/client/default_connection_configuration.h" | 20 | #include "src/client/default_connection_configuration.h" |
769 | 21 | #include "src/client/rpc/simple_rpc_thread.h" | ||
770 | 22 | #include "src/client/rpc/dispatchable.h" | ||
771 | 21 | 23 | ||
772 | 22 | #include "mir/frontend/connector.h" | 24 | #include "mir/frontend/connector.h" |
773 | 23 | #include "mir_test/test_protobuf_server.h" | 25 | #include "mir_test/test_protobuf_server.h" |
774 | @@ -74,6 +76,9 @@ | |||
775 | 74 | mcl::DefaultConnectionConfiguration{test_socket}.the_rpc_channel(); | 76 | mcl::DefaultConnectionConfiguration{test_socket}.the_rpc_channel(); |
776 | 75 | protobuf_server = | 77 | protobuf_server = |
777 | 76 | std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get()); | 78 | std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get()); |
778 | 79 | eventloop = | ||
779 | 80 | std::make_shared<mir::client::rpc::SimpleRpcThread>( | ||
780 | 81 | std::dynamic_pointer_cast<mir::client::rpc::Dispatchable>(rpc_channel)); | ||
781 | 77 | } | 82 | } |
782 | 78 | 83 | ||
783 | 79 | char const* const test_socket = "./test_socket_screencast"; | 84 | char const* const test_socket = "./test_socket_screencast"; |
784 | @@ -81,6 +86,7 @@ | |||
785 | 81 | std::shared_ptr<mt::TestProtobufServer> test_server; | 86 | std::shared_ptr<mt::TestProtobufServer> test_server; |
786 | 82 | std::shared_ptr<google::protobuf::RpcChannel> rpc_channel; | 87 | std::shared_ptr<google::protobuf::RpcChannel> rpc_channel; |
787 | 83 | std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server; | 88 | std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server; |
788 | 89 | std::shared_ptr<mir::client::rpc::SimpleRpcThread> eventloop; | ||
789 | 84 | }; | 90 | }; |
790 | 85 | 91 | ||
791 | 86 | } | 92 | } |
792 | 87 | 93 | ||
793 | === modified file 'tests/mir_test/CMakeLists.txt' | |||
794 | --- tests/mir_test/CMakeLists.txt 2014-11-27 09:00:52 +0000 | |||
795 | +++ tests/mir_test/CMakeLists.txt 2014-12-04 01:50:56 +0000 | |||
796 | @@ -11,6 +11,7 @@ | |||
797 | 11 | wait_object.cpp | 11 | wait_object.cpp |
798 | 12 | current_thread_name.cpp | 12 | current_thread_name.cpp |
799 | 13 | validity_matchers.cpp | 13 | validity_matchers.cpp |
800 | 14 | fd_utils.cpp | ||
801 | 14 | ) | 15 | ) |
802 | 15 | 16 | ||
803 | 16 | target_link_libraries(mir-test mirprotobuf) | 17 | target_link_libraries(mir-test mirprotobuf) |
804 | 17 | 18 | ||
805 | === added file 'tests/mir_test/fd_utils.cpp' | |||
806 | --- tests/mir_test/fd_utils.cpp 1970-01-01 00:00:00 +0000 | |||
807 | +++ tests/mir_test/fd_utils.cpp 2014-12-04 01:50:56 +0000 | |||
808 | @@ -0,0 +1,40 @@ | |||
809 | 1 | /* | ||
810 | 2 | * Copyright © 2014 Canonical Ltd. | ||
811 | 3 | * | ||
812 | 4 | * This program is free software: you can redistribute it and/or modify | ||
813 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
814 | 6 | * published by the Free Software Foundation. | ||
815 | 7 | * | ||
816 | 8 | * This program is distributed in the hope that it will be useful, | ||
817 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
818 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
819 | 11 | * GNU General Public License for more details. | ||
820 | 12 | * | ||
821 | 13 | * You should have received a copy of the GNU General Public License | ||
822 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
823 | 15 | * | ||
824 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
825 | 17 | */ | ||
826 | 18 | |||
827 | 19 | #include "mir_test/fd_utils.h" | ||
828 | 20 | |||
829 | 21 | ::testing::AssertionResult mir::test::std_call_succeeded(int retval) | ||
830 | 22 | { | ||
831 | 23 | if (retval >= 0) | ||
832 | 24 | { | ||
833 | 25 | return ::testing::AssertionSuccess(); | ||
834 | 26 | } | ||
835 | 27 | else | ||
836 | 28 | { | ||
837 | 29 | return ::testing::AssertionFailure() << "errno: " | ||
838 | 30 | << errno | ||
839 | 31 | << " [" | ||
840 | 32 | << strerror(errno) | ||
841 | 33 | << "]"; | ||
842 | 34 | } | ||
843 | 35 | } | ||
844 | 36 | |||
845 | 37 | ::testing::AssertionResult mir::test::fd_is_readable(mir::Fd const& fd) | ||
846 | 38 | { | ||
847 | 39 | return fd_becomes_readable(fd, std::chrono::seconds{0}); | ||
848 | 40 | } | ||
849 | 0 | 41 | ||
850 | === modified file 'tests/mir_test_doubles/test_protobuf_client.cpp' | |||
851 | --- tests/mir_test_doubles/test_protobuf_client.cpp 2014-11-27 09:00:52 +0000 | |||
852 | +++ tests/mir_test_doubles/test_protobuf_client.cpp 2014-12-04 01:50:56 +0000 | |||
853 | @@ -25,22 +25,26 @@ | |||
854 | 25 | #include "src/client/lifecycle_control.h" | 25 | #include "src/client/lifecycle_control.h" |
855 | 26 | #include "src/client/rpc/make_rpc_channel.h" | 26 | #include "src/client/rpc/make_rpc_channel.h" |
856 | 27 | #include "src/client/rpc/mir_basic_rpc_channel.h" | 27 | #include "src/client/rpc/mir_basic_rpc_channel.h" |
857 | 28 | #include "src/client/rpc/dispatchable.h" | ||
858 | 29 | #include "src/client/rpc/simple_rpc_thread.h" | ||
859 | 28 | 30 | ||
860 | 29 | #include <thread> | 31 | #include <thread> |
861 | 30 | 32 | ||
862 | 31 | namespace mtd = mir::test::doubles; | 33 | namespace mtd = mir::test::doubles; |
863 | 34 | namespace mclr = mir::client::rpc; | ||
864 | 32 | 35 | ||
865 | 33 | mir::test::TestProtobufClient::TestProtobufClient( | 36 | mir::test::TestProtobufClient::TestProtobufClient( |
866 | 34 | std::string socket_file, | 37 | std::string socket_file, |
867 | 35 | int timeout_ms) : | 38 | int timeout_ms) : |
868 | 36 | rpc_report(std::make_shared<testing::NiceMock<doubles::MockRpcReport>>()), | 39 | rpc_report(std::make_shared<testing::NiceMock<doubles::MockRpcReport>>()), |
870 | 37 | channel(mir::client::rpc::make_rpc_channel( | 40 | channel(mclr::make_rpc_channel( |
871 | 38 | socket_file, | 41 | socket_file, |
872 | 39 | std::make_shared<mir::client::ConnectionSurfaceMap>(), | 42 | std::make_shared<mir::client::ConnectionSurfaceMap>(), |
873 | 40 | std::make_shared<mir::client::DisplayConfiguration>(), | 43 | std::make_shared<mir::client::DisplayConfiguration>(), |
874 | 41 | rpc_report, | 44 | rpc_report, |
875 | 42 | std::make_shared<mir::client::LifecycleControl>(), | 45 | std::make_shared<mir::client::LifecycleControl>(), |
876 | 43 | std::make_shared<mtd::NullClientEventSink>())), | 46 | std::make_shared<mtd::NullClientEventSink>())), |
877 | 47 | eventloop{std::make_shared<mclr::SimpleRpcThread>(std::dynamic_pointer_cast<mclr::Dispatchable>(channel))}, | ||
878 | 44 | display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL), | 48 | display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL), |
879 | 45 | maxwait(timeout_ms), | 49 | maxwait(timeout_ms), |
880 | 46 | connect_done_called(false), | 50 | connect_done_called(false), |
881 | 47 | 51 | ||
882 | === modified file 'tests/unit-tests/client/CMakeLists.txt' | |||
883 | --- tests/unit-tests/client/CMakeLists.txt 2014-11-27 09:00:52 +0000 | |||
884 | +++ tests/unit-tests/client/CMakeLists.txt 2014-12-04 01:50:56 +0000 | |||
885 | @@ -13,6 +13,7 @@ | |||
886 | 13 | ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_prompt_session.cpp | 13 | ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_prompt_session.cpp |
887 | 14 | ${CMAKE_CURRENT_SOURCE_DIR}/test_event_distributor.cpp | 14 | ${CMAKE_CURRENT_SOURCE_DIR}/test_event_distributor.cpp |
888 | 15 | ${CMAKE_CURRENT_SOURCE_DIR}/test_periodic_perf_report.cpp | 15 | ${CMAKE_CURRENT_SOURCE_DIR}/test_periodic_perf_report.cpp |
889 | 16 | ${CMAKE_CURRENT_SOURCE_DIR}/test_simple_rpc_thread.cpp | ||
890 | 16 | ) | 17 | ) |
891 | 17 | 18 | ||
892 | 18 | if(MIR_TEST_PLATFORM STREQUAL "android") | 19 | if(MIR_TEST_PLATFORM STREQUAL "android") |
893 | 19 | 20 | ||
894 | === modified file 'tests/unit-tests/client/test_mir_connection.cpp' | |||
895 | --- tests/unit-tests/client/test_mir_connection.cpp 2014-11-27 09:00:52 +0000 | |||
896 | +++ tests/unit-tests/client/test_mir_connection.cpp 2014-12-04 01:50:56 +0000 | |||
897 | @@ -25,10 +25,14 @@ | |||
898 | 25 | #include "src/client/display_configuration.h" | 25 | #include "src/client/display_configuration.h" |
899 | 26 | #include "src/client/mir_surface.h" | 26 | #include "src/client/mir_surface.h" |
900 | 27 | #include "src/client/client_buffer_factory.h" | 27 | #include "src/client/client_buffer_factory.h" |
901 | 28 | #include "src/client/rpc/dispatchable.h" | ||
902 | 28 | 29 | ||
903 | 29 | #include "src/server/frontend/resource_cache.h" /* needed by test_server.h */ | 30 | #include "src/server/frontend/resource_cache.h" /* needed by test_server.h */ |
904 | 30 | #include "mir_test/test_protobuf_server.h" | 31 | #include "mir_test/test_protobuf_server.h" |
905 | 31 | #include "mir_test/stub_server_tool.h" | 32 | #include "mir_test/stub_server_tool.h" |
906 | 33 | #include "mir_test/pipe.h" | ||
907 | 34 | #include "mir_test/signal.h" | ||
908 | 35 | #include "mir_test/fd_utils.h" | ||
909 | 32 | #include "mir_test_doubles/stub_client_buffer_factory.h" | 36 | #include "mir_test_doubles/stub_client_buffer_factory.h" |
910 | 33 | 37 | ||
911 | 34 | #include "mir_protobuf.pb.h" | 38 | #include "mir_protobuf.pb.h" |
912 | @@ -46,8 +50,14 @@ | |||
913 | 46 | namespace | 50 | namespace |
914 | 47 | { | 51 | { |
915 | 48 | 52 | ||
917 | 49 | struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel | 53 | struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel, |
918 | 54 | public mir::client::rpc::Dispatchable | ||
919 | 50 | { | 55 | { |
920 | 56 | MockRpcChannel() | ||
921 | 57 | { | ||
922 | 58 | ON_CALL(*this, watch_fd()).WillByDefault(testing::Return(mir::Fd{})); | ||
923 | 59 | } | ||
924 | 60 | |||
925 | 51 | void CallMethod(const google::protobuf::MethodDescriptor* method, | 61 | void CallMethod(const google::protobuf::MethodDescriptor* method, |
926 | 52 | google::protobuf::RpcController*, | 62 | google::protobuf::RpcController*, |
927 | 53 | const google::protobuf::Message* parameters, | 63 | const google::protobuf::Message* parameters, |
928 | @@ -75,6 +85,9 @@ | |||
929 | 75 | MOCK_METHOD1(drm_auth_magic, void(const mp::DRMMagic*)); | 85 | MOCK_METHOD1(drm_auth_magic, void(const mp::DRMMagic*)); |
930 | 76 | MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*)); | 86 | MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*)); |
931 | 77 | MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*)); | 87 | MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*)); |
932 | 88 | |||
933 | 89 | MOCK_CONST_METHOD0(watch_fd, mir::Fd()); | ||
934 | 90 | MOCK_METHOD0(dispatch, void()); | ||
935 | 78 | }; | 91 | }; |
936 | 79 | 92 | ||
937 | 80 | struct MockClientPlatform : public mcl::ClientPlatform | 93 | struct MockClientPlatform : public mcl::ClientPlatform |
938 | @@ -166,21 +179,21 @@ | |||
939 | 166 | MirConnectionTest() | 179 | MirConnectionTest() |
940 | 167 | : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()}, | 180 | : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()}, |
941 | 168 | mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()}, | 181 | mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()}, |
944 | 169 | conf{mock_platform, mock_channel}, | 182 | conf{mock_platform, mock_channel} |
943 | 170 | connection{std::make_shared<MirConnection>(conf)} | ||
945 | 171 | { | 183 | { |
946 | 172 | } | 184 | } |
947 | 173 | 185 | ||
948 | 174 | std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform; | 186 | std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform; |
949 | 175 | std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel; | 187 | std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel; |
950 | 176 | TestConnectionConfiguration conf; | 188 | TestConnectionConfiguration conf; |
951 | 177 | std::shared_ptr<MirConnection> const connection; | ||
952 | 178 | }; | 189 | }; |
953 | 179 | 190 | ||
954 | 180 | TEST_F(MirConnectionTest, returns_correct_egl_native_display) | 191 | TEST_F(MirConnectionTest, returns_correct_egl_native_display) |
955 | 181 | { | 192 | { |
956 | 182 | using namespace testing; | 193 | using namespace testing; |
957 | 183 | 194 | ||
958 | 195 | auto connection = std::make_shared<MirConnection>(conf); | ||
959 | 196 | |||
960 | 184 | EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef); | 197 | EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef); |
961 | 185 | auto native_display = std::make_shared<EGLNativeDisplayType>(); | 198 | auto native_display = std::make_shared<EGLNativeDisplayType>(); |
962 | 186 | *native_display = native_display_raw; | 199 | *native_display = native_display_raw; |
963 | @@ -206,6 +219,8 @@ | |||
964 | 206 | { | 219 | { |
965 | 207 | using namespace testing; | 220 | using namespace testing; |
966 | 208 | 221 | ||
967 | 222 | auto connection = std::make_shared<MirConnection>(conf); | ||
968 | 223 | |||
969 | 209 | unsigned int const drm_magic{0x10111213}; | 224 | unsigned int const drm_magic{0x10111213}; |
970 | 210 | 225 | ||
971 | 211 | EXPECT_CALL(*mock_channel, drm_auth_magic(has_drm_magic(drm_magic))) | 226 | EXPECT_CALL(*mock_channel, drm_auth_magic(has_drm_magic(drm_magic))) |
972 | @@ -276,6 +291,8 @@ | |||
973 | 276 | { | 291 | { |
974 | 277 | using namespace testing; | 292 | using namespace testing; |
975 | 278 | 293 | ||
976 | 294 | auto connection = std::make_shared<MirConnection>(conf); | ||
977 | 295 | |||
978 | 279 | EXPECT_CALL(*mock_channel, connect(_,_)) | 296 | EXPECT_CALL(*mock_channel, connect(_,_)) |
979 | 280 | .WillOnce(Invoke(fill_display_configuration)); | 297 | .WillOnce(Invoke(fill_display_configuration)); |
980 | 281 | 298 | ||
981 | @@ -315,6 +332,8 @@ | |||
982 | 315 | { | 332 | { |
983 | 316 | using namespace testing; | 333 | using namespace testing; |
984 | 317 | 334 | ||
985 | 335 | auto connection = std::make_shared<MirConnection>(conf); | ||
986 | 336 | |||
987 | 318 | EXPECT_CALL(*mock_channel, connect(_,_)) | 337 | EXPECT_CALL(*mock_channel, connect(_,_)) |
988 | 319 | .WillOnce(Invoke(fill_display_configuration)); | 338 | .WillOnce(Invoke(fill_display_configuration)); |
989 | 320 | 339 | ||
990 | @@ -360,6 +379,8 @@ | |||
991 | 360 | { | 379 | { |
992 | 361 | using namespace testing; | 380 | using namespace testing; |
993 | 362 | 381 | ||
994 | 382 | auto connection = std::make_shared<MirConnection>(conf); | ||
995 | 383 | |||
996 | 363 | EXPECT_CALL(*mock_channel, connect(_,_)) | 384 | EXPECT_CALL(*mock_channel, connect(_,_)) |
997 | 364 | .WillOnce(Invoke(fill_display_configuration)); | 385 | .WillOnce(Invoke(fill_display_configuration)); |
998 | 365 | 386 | ||
999 | @@ -381,6 +402,8 @@ | |||
1000 | 381 | { | 402 | { |
1001 | 382 | using namespace testing; | 403 | using namespace testing; |
1002 | 383 | 404 | ||
1003 | 405 | auto connection = std::make_shared<MirConnection>(conf); | ||
1004 | 406 | |||
1005 | 384 | EXPECT_CALL(*mock_channel, connect(_,_)) | 407 | EXPECT_CALL(*mock_channel, connect(_,_)) |
1006 | 385 | .WillOnce(Invoke(fill_display_configuration)); | 408 | .WillOnce(Invoke(fill_display_configuration)); |
1007 | 386 | 409 | ||
1008 | @@ -418,6 +441,8 @@ | |||
1009 | 418 | { | 441 | { |
1010 | 419 | using namespace testing; | 442 | using namespace testing; |
1011 | 420 | 443 | ||
1012 | 444 | auto connection = std::make_shared<MirConnection>(conf); | ||
1013 | 445 | |||
1014 | 421 | EXPECT_CALL(*mock_channel, connect(_,_)) | 446 | EXPECT_CALL(*mock_channel, connect(_,_)) |
1015 | 422 | .WillOnce(Invoke(fill_surface_pixel_formats)); | 447 | .WillOnce(Invoke(fill_surface_pixel_formats)); |
1016 | 423 | MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", | 448 | MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest", |
1017 | @@ -441,6 +466,8 @@ | |||
1018 | 441 | { | 466 | { |
1019 | 442 | using namespace testing; | 467 | using namespace testing; |
1020 | 443 | 468 | ||
1021 | 469 | auto connection = std::make_shared<MirConnection>(conf); | ||
1022 | 470 | |||
1023 | 444 | EXPECT_CALL(*mock_channel, connect(_,_)) | 471 | EXPECT_CALL(*mock_channel, connect(_,_)) |
1024 | 445 | .WillOnce(Invoke(fill_display_configuration)); | 472 | .WillOnce(Invoke(fill_display_configuration)); |
1025 | 446 | 473 | ||
1026 | @@ -504,6 +531,8 @@ | |||
1027 | 504 | { | 531 | { |
1028 | 505 | using namespace testing; | 532 | using namespace testing; |
1029 | 506 | 533 | ||
1030 | 534 | auto connection = std::make_shared<MirConnection>(conf); | ||
1031 | 535 | |||
1032 | 507 | MirSurfaceParameters params; | 536 | MirSurfaceParameters params; |
1033 | 508 | params.name = __PRETTY_FUNCTION__; | 537 | params.name = __PRETTY_FUNCTION__; |
1034 | 509 | 538 | ||
1035 | @@ -542,6 +571,8 @@ | |||
1036 | 542 | { | 571 | { |
1037 | 543 | using namespace testing; | 572 | using namespace testing; |
1038 | 544 | 573 | ||
1039 | 574 | auto connection = std::make_shared<MirConnection>(conf); | ||
1040 | 575 | |||
1041 | 545 | MirSurfaceParameters params; | 576 | MirSurfaceParameters params; |
1042 | 546 | params.name = __PRETTY_FUNCTION__; | 577 | params.name = __PRETTY_FUNCTION__; |
1043 | 547 | 578 | ||
1044 | @@ -593,6 +624,8 @@ | |||
1045 | 593 | std::vector<int> const initial_data{0x66, 0x67, 0x68}; | 624 | std::vector<int> const initial_data{0x66, 0x67, 0x68}; |
1046 | 594 | std::vector<int> const extra_data{0x11, 0x12, 0x13}; | 625 | std::vector<int> const extra_data{0x11, 0x12, 0x13}; |
1047 | 595 | 626 | ||
1048 | 627 | auto connection = std::make_shared<MirConnection>(conf); | ||
1049 | 628 | |||
1050 | 596 | EXPECT_CALL(*mock_channel, connect(_,_)) | 629 | EXPECT_CALL(*mock_channel, connect(_,_)) |
1051 | 597 | .WillOnce(FillPlatformDataWith(initial_data)); | 630 | .WillOnce(FillPlatformDataWith(initial_data)); |
1052 | 598 | 631 | ||
1053 | @@ -622,3 +655,73 @@ | |||
1054 | 622 | for (size_t i = 0; i < extra_data.size(); i++) | 655 | for (size_t i = 0; i < extra_data.size(); i++) |
1055 | 623 | EXPECT_EQ(extra_data[i], pkg.data[i + initial_data.size()]) << " i=" << i; | 656 | EXPECT_EQ(extra_data[i], pkg.data[i + initial_data.size()]) << " i=" << i; |
1056 | 624 | } | 657 | } |
1057 | 658 | |||
1058 | 659 | TEST_F(MirConnectionTest, dispatch_works_with_automatic_dispatch) | ||
1059 | 660 | { | ||
1060 | 661 | using namespace testing; | ||
1061 | 662 | |||
1062 | 663 | auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel()); | ||
1063 | 664 | mir::test::Pipe mock_epoll; | ||
1064 | 665 | auto dispatched = std::make_shared<mir::test::Signal>(); | ||
1065 | 666 | |||
1066 | 667 | ON_CALL(*channel, watch_fd()).WillByDefault(Return(mir::Fd{mock_epoll.read_fd()})); | ||
1067 | 668 | ON_CALL(*channel, dispatch()) | ||
1068 | 669 | .WillByDefault(Invoke([dispatched]() { dispatched->raise(); })); | ||
1069 | 670 | |||
1070 | 671 | auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic); | ||
1071 | 672 | |||
1072 | 673 | int dummy{0}; | ||
1073 | 674 | EXPECT_EQ(sizeof(dummy), write(mock_epoll.write_fd(), &dummy, sizeof(dummy))); | ||
1074 | 675 | |||
1075 | 676 | EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1})); | ||
1076 | 677 | } | ||
1077 | 678 | |||
1078 | 679 | TEST_F(MirConnectionTest, manual_dispatch_is_not_automatically_dispatched) | ||
1079 | 680 | { | ||
1080 | 681 | using namespace testing; | ||
1081 | 682 | |||
1082 | 683 | auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel()); | ||
1083 | 684 | |||
1084 | 685 | mir::test::Pipe mock_epoll; | ||
1085 | 686 | int dummy{0}; | ||
1086 | 687 | EXPECT_EQ(sizeof(dummy), write(mock_epoll.write_fd(), &dummy, sizeof(dummy))); | ||
1087 | 688 | |||
1088 | 689 | auto dispatched = std::make_shared<mir::test::Signal>(); | ||
1089 | 690 | |||
1090 | 691 | EXPECT_CALL(*channel, watch_fd()).Times(0); | ||
1091 | 692 | ON_CALL(*channel, dispatch()) | ||
1092 | 693 | .WillByDefault(Invoke([dispatched]() { dispatched->raise(); })); | ||
1093 | 694 | |||
1094 | 695 | auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual); | ||
1095 | 696 | |||
1096 | 697 | EXPECT_FALSE(dispatched->wait_for(std::chrono::seconds{1})); | ||
1097 | 698 | } | ||
1098 | 699 | |||
1099 | 700 | TEST_F(MirConnectionTest, returns_invalid_watch_fd_when_using_automatic_dispatch) | ||
1100 | 701 | { | ||
1101 | 702 | using namespace testing; | ||
1102 | 703 | |||
1103 | 704 | auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic); | ||
1104 | 705 | |||
1105 | 706 | EXPECT_THAT(connection->watch_fd(), Lt(0)); | ||
1106 | 707 | } | ||
1107 | 708 | |||
1108 | 709 | TEST_F(MirConnectionTest, returns_pollable_watch_fd_when_using_manual_dispatch) | ||
1109 | 710 | { | ||
1110 | 711 | using namespace testing; | ||
1111 | 712 | |||
1112 | 713 | // MirConnection will need a valid fd | ||
1113 | 714 | auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel()); | ||
1114 | 715 | |||
1115 | 716 | mir::test::Pipe mock_epoll; | ||
1116 | 717 | ON_CALL(*channel, watch_fd()).WillByDefault(Return(mir::Fd{mock_epoll.read_fd()})); | ||
1117 | 718 | |||
1118 | 719 | auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual); | ||
1119 | 720 | |||
1120 | 721 | EXPECT_THAT(connection->watch_fd(), Gt(0)); | ||
1121 | 722 | |||
1122 | 723 | pollfd fd_readable; | ||
1123 | 724 | fd_readable.events = POLLIN; | ||
1124 | 725 | fd_readable.fd = connection->watch_fd(); | ||
1125 | 726 | EXPECT_TRUE(mir::test::std_call_succeeded(poll(&fd_readable, 1, 0))); | ||
1126 | 727 | } | ||
1127 | 625 | 728 | ||
1128 | === modified file 'tests/unit-tests/client/test_protobuf_rpc_channel.cpp' | |||
1129 | --- tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2014-11-27 09:00:52 +0000 | |||
1130 | +++ tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2014-12-04 01:50:56 +0000 | |||
1131 | @@ -108,6 +108,8 @@ | |||
1132 | 108 | MOCK_METHOD2(receive_data, void(void*, size_t)); | 108 | MOCK_METHOD2(receive_data, void(void*, size_t)); |
1133 | 109 | MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<mir::Fd>&)); | 109 | MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<mir::Fd>&)); |
1134 | 110 | MOCK_METHOD2(send_message, void(std::vector<uint8_t> const&, std::vector<mir::Fd> const&)); | 110 | MOCK_METHOD2(send_message, void(std::vector<uint8_t> const&, std::vector<mir::Fd> const&)); |
1135 | 111 | MOCK_CONST_METHOD0(watch_fd, mir::Fd()); | ||
1136 | 112 | MOCK_METHOD0(dispatch, void()); | ||
1137 | 111 | 113 | ||
1138 | 112 | // Transport interface | 114 | // Transport interface |
1139 | 113 | void register_observer_default(std::shared_ptr<Observer> const& observer) | 115 | void register_observer_default(std::shared_ptr<Observer> const& observer) |
1140 | 114 | 116 | ||
1141 | === added file 'tests/unit-tests/client/test_simple_rpc_thread.cpp' | |||
1142 | --- tests/unit-tests/client/test_simple_rpc_thread.cpp 1970-01-01 00:00:00 +0000 | |||
1143 | +++ tests/unit-tests/client/test_simple_rpc_thread.cpp 2014-12-04 01:50:56 +0000 | |||
1144 | @@ -0,0 +1,105 @@ | |||
1145 | 1 | /* | ||
1146 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1147 | 3 | * | ||
1148 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1149 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1150 | 6 | * published by the Free Software Foundation. | ||
1151 | 7 | * | ||
1152 | 8 | * This program is distributed in the hope that it will be useful, | ||
1153 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1154 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1155 | 11 | * GNU General Public License for more details. | ||
1156 | 12 | * | ||
1157 | 13 | * You should have received a copy of the GNU General Public License | ||
1158 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1159 | 15 | * | ||
1160 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
1161 | 17 | */ | ||
1162 | 18 | |||
1163 | 19 | #include "src/client/rpc/simple_rpc_thread.h" | ||
1164 | 20 | #include "src/client/rpc/dispatchable.h" | ||
1165 | 21 | #include "mir/fd.h" | ||
1166 | 22 | #include "mir_test/pipe.h" | ||
1167 | 23 | #include "mir_test/signal.h" | ||
1168 | 24 | |||
1169 | 25 | #include <fcntl.h> | ||
1170 | 26 | |||
1171 | 27 | #include <atomic> | ||
1172 | 28 | |||
1173 | 29 | #include <gtest/gtest.h> | ||
1174 | 30 | #include <gmock/gmock.h> | ||
1175 | 31 | |||
1176 | 32 | namespace mclr = mir::client::rpc; | ||
1177 | 33 | namespace mt = mir::test; | ||
1178 | 34 | |||
1179 | 35 | namespace | ||
1180 | 36 | { | ||
1181 | 37 | class SimpleRPCThreadTest : public ::testing::Test | ||
1182 | 38 | { | ||
1183 | 39 | public: | ||
1184 | 40 | SimpleRPCThreadTest() | ||
1185 | 41 | { | ||
1186 | 42 | watch_fd = mir::Fd{pipe.read_fd()}; | ||
1187 | 43 | test_fd = mir::Fd{pipe.write_fd()}; | ||
1188 | 44 | fcntl(watch_fd, F_SETFL, O_NONBLOCK); | ||
1189 | 45 | } | ||
1190 | 46 | |||
1191 | 47 | mir::Fd watch_fd; | ||
1192 | 48 | mir::Fd test_fd; | ||
1193 | 49 | private: | ||
1194 | 50 | mt::Pipe pipe; | ||
1195 | 51 | }; | ||
1196 | 52 | |||
1197 | 53 | class MockDispatchable : public mclr::Dispatchable | ||
1198 | 54 | { | ||
1199 | 55 | public: | ||
1200 | 56 | MOCK_CONST_METHOD0(watch_fd, mir::Fd()); | ||
1201 | 57 | MOCK_METHOD0(dispatch, void()); | ||
1202 | 58 | }; | ||
1203 | 59 | |||
1204 | 60 | } | ||
1205 | 61 | |||
1206 | 62 | TEST_F(SimpleRPCThreadTest, CallsDispatchWhenFdIsReadable) | ||
1207 | 63 | { | ||
1208 | 64 | using namespace testing; | ||
1209 | 65 | |||
1210 | 66 | auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>(); | ||
1211 | 67 | ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(watch_fd)); | ||
1212 | 68 | |||
1213 | 69 | auto dispatched = std::make_shared<mt::Signal>(); | ||
1214 | 70 | ON_CALL(*dispatchable, dispatch()).WillByDefault(Invoke([dispatched]() { dispatched->raise(); })); | ||
1215 | 71 | |||
1216 | 72 | mclr::SimpleRpcThread dispatcher{dispatchable}; | ||
1217 | 73 | |||
1218 | 74 | uint64_t dummy{0xdeadbeef}; | ||
1219 | 75 | EXPECT_EQ(sizeof(dummy), write(test_fd, &dummy, sizeof(dummy))); | ||
1220 | 76 | |||
1221 | 77 | EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1})); | ||
1222 | 78 | } | ||
1223 | 79 | |||
1224 | 80 | TEST_F(SimpleRPCThreadTest, StopsCallingDispatchOnceFdIsNotReadable) | ||
1225 | 81 | { | ||
1226 | 82 | using namespace testing; | ||
1227 | 83 | |||
1228 | 84 | uint64_t dummy{0xdeadbeef}; | ||
1229 | 85 | std::atomic<int> dispatch_count{0}; | ||
1230 | 86 | |||
1231 | 87 | auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>(); | ||
1232 | 88 | ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(watch_fd)); | ||
1233 | 89 | |||
1234 | 90 | auto dispatched = std::make_shared<mt::Signal>(); | ||
1235 | 91 | ON_CALL(*dispatchable, dispatch()).WillByDefault(Invoke([this, &dispatch_count]() | ||
1236 | 92 | { | ||
1237 | 93 | decltype(dummy) buffer; | ||
1238 | 94 | dispatch_count++; | ||
1239 | 95 | read(this->watch_fd, &buffer, sizeof(buffer)); | ||
1240 | 96 | })); | ||
1241 | 97 | |||
1242 | 98 | mclr::SimpleRpcThread dispatcher{dispatchable}; | ||
1243 | 99 | |||
1244 | 100 | EXPECT_EQ(sizeof(dummy), write(test_fd, &dummy, sizeof(dummy))); | ||
1245 | 101 | |||
1246 | 102 | std::this_thread::sleep_for(std::chrono::seconds{1}); | ||
1247 | 103 | |||
1248 | 104 | EXPECT_EQ(1, dispatch_count); | ||
1249 | 105 | } | ||
1250 | 0 | 106 | ||
1251 | === modified file 'tests/unit-tests/client/test_stream_transport.cpp' | |||
1252 | --- tests/unit-tests/client/test_stream_transport.cpp 2014-11-27 09:00:52 +0000 | |||
1253 | +++ tests/unit-tests/client/test_stream_transport.cpp 2014-12-04 01:50:56 +0000 | |||
1254 | @@ -22,6 +22,7 @@ | |||
1255 | 22 | 22 | ||
1256 | 23 | #include "mir_test/auto_unblock_thread.h" | 23 | #include "mir_test/auto_unblock_thread.h" |
1257 | 24 | #include "mir_test/signal.h" | 24 | #include "mir_test/signal.h" |
1258 | 25 | #include "mir_test/fd_utils.h" | ||
1259 | 25 | #include "mir/raii.h" | 26 | #include "mir/raii.h" |
1260 | 26 | 27 | ||
1261 | 27 | #include <sys/socket.h> | 28 | #include <sys/socket.h> |
1262 | @@ -43,6 +44,7 @@ | |||
1263 | 43 | #include <gmock/gmock.h> | 44 | #include <gmock/gmock.h> |
1264 | 44 | 45 | ||
1265 | 45 | namespace mclr = mir::client::rpc; | 46 | namespace mclr = mir::client::rpc; |
1266 | 47 | namespace mt = mir::test; | ||
1267 | 46 | 48 | ||
1268 | 47 | namespace | 49 | namespace |
1269 | 48 | { | 50 | { |
1270 | @@ -71,12 +73,6 @@ | |||
1271 | 71 | transport = std::make_shared<TransportMechanism>(transport_fd); | 73 | transport = std::make_shared<TransportMechanism>(transport_fd); |
1272 | 72 | } | 74 | } |
1273 | 73 | 75 | ||
1274 | 74 | virtual ~StreamTransportTest() | ||
1275 | 75 | { | ||
1276 | 76 | // We don't care about errors, so unconditionally close the test fd. | ||
1277 | 77 | close(test_fd); | ||
1278 | 78 | } | ||
1279 | 79 | |||
1280 | 80 | mir::Fd transport_fd; | 76 | mir::Fd transport_fd; |
1281 | 81 | mir::Fd test_fd; | 77 | mir::Fd test_fd; |
1282 | 82 | std::shared_ptr<TransportMechanism> transport; | 78 | std::shared_ptr<TransportMechanism> transport; |
1283 | @@ -85,51 +81,201 @@ | |||
1284 | 85 | typedef ::testing::Types<mclr::StreamSocketTransport> Transports; | 81 | typedef ::testing::Types<mclr::StreamSocketTransport> Transports; |
1285 | 86 | TYPED_TEST_CASE(StreamTransportTest, Transports); | 82 | TYPED_TEST_CASE(StreamTransportTest, Transports); |
1286 | 87 | 83 | ||
1287 | 84 | TYPED_TEST(StreamTransportTest, ReturnsValidWatchFd) | ||
1288 | 85 | { | ||
1289 | 86 | // A valid fd is >= 0, and we know that stdin, stdout, and stderr aren't correct. | ||
1290 | 87 | EXPECT_GE(this->transport->watch_fd(), 3); | ||
1291 | 88 | } | ||
1292 | 89 | |||
1293 | 90 | TYPED_TEST(StreamTransportTest, WatchFdIsPollable) | ||
1294 | 91 | { | ||
1295 | 92 | pollfd socket_readable; | ||
1296 | 93 | socket_readable.events = POLLIN; | ||
1297 | 94 | socket_readable.fd = this->transport->watch_fd(); | ||
1298 | 95 | |||
1299 | 96 | ASSERT_TRUE(mt::std_call_succeeded(poll(&socket_readable, 1, 0))); | ||
1300 | 97 | |||
1301 | 98 | EXPECT_FALSE(socket_readable.revents & POLLERR); | ||
1302 | 99 | EXPECT_FALSE(socket_readable.revents & POLLNVAL); | ||
1303 | 100 | } | ||
1304 | 101 | |||
1305 | 102 | TYPED_TEST(StreamTransportTest, WatchFdNotifiesReadableWhenDataPending) | ||
1306 | 103 | { | ||
1307 | 104 | uint64_t dummy{0xdeadbeef}; | ||
1308 | 105 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1309 | 106 | |||
1310 | 107 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1311 | 108 | } | ||
1312 | 109 | |||
1313 | 110 | TYPED_TEST(StreamTransportTest, WatchFdRemainsUnreadableUntilEventPending) | ||
1314 | 111 | { | ||
1315 | 112 | EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1316 | 113 | |||
1317 | 114 | uint64_t dummy{0xdeadbeef}; | ||
1318 | 115 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1319 | 116 | |||
1320 | 117 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1321 | 118 | } | ||
1322 | 119 | |||
1323 | 120 | TYPED_TEST(StreamTransportTest, WatchFdIsNoLongerReadableAfterEventProcessing) | ||
1324 | 121 | { | ||
1325 | 122 | using namespace testing; | ||
1326 | 123 | |||
1327 | 124 | uint64_t dummy{0xdeadbeef}; | ||
1328 | 125 | |||
1329 | 126 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | ||
1330 | 127 | |||
1331 | 128 | ON_CALL(*observer, on_data_available()) | ||
1332 | 129 | .WillByDefault(Invoke([dummy, this]() | ||
1333 | 130 | { | ||
1334 | 131 | decltype(dummy) buffer; | ||
1335 | 132 | this->transport->receive_data(&buffer, sizeof(dummy)); | ||
1336 | 133 | })); | ||
1337 | 134 | |||
1338 | 135 | this->transport->register_observer(observer); | ||
1339 | 136 | |||
1340 | 137 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1341 | 138 | |||
1342 | 139 | ASSERT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1343 | 140 | |||
1344 | 141 | this->transport->dispatch(); | ||
1345 | 142 | |||
1346 | 143 | EXPECT_FALSE(mt::fd_is_readable(this->test_fd)); | ||
1347 | 144 | } | ||
1348 | 145 | |||
1349 | 146 | TYPED_TEST(StreamTransportTest, NoEventsDispatchedUntilDispatchCalled) | ||
1350 | 147 | { | ||
1351 | 148 | using namespace testing; | ||
1352 | 149 | |||
1353 | 150 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | ||
1354 | 151 | bool data_available{false}; | ||
1355 | 152 | bool disconnected{false}; | ||
1356 | 153 | |||
1357 | 154 | uint64_t dummy{0xdeadbeef}; | ||
1358 | 155 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1359 | 156 | ::close(this->test_fd); | ||
1360 | 157 | |||
1361 | 158 | ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]() | ||
1362 | 159 | { | ||
1363 | 160 | decltype(dummy) buffer; | ||
1364 | 161 | this->transport->receive_data(&buffer, sizeof(buffer)); | ||
1365 | 162 | data_available = true; | ||
1366 | 163 | })); | ||
1367 | 164 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() | ||
1368 | 165 | { disconnected = true; })); | ||
1369 | 166 | |||
1370 | 167 | this->transport->register_observer(observer); | ||
1371 | 168 | |||
1372 | 169 | std::this_thread::sleep_for(std::chrono::seconds{1}); | ||
1373 | 170 | EXPECT_FALSE(data_available); | ||
1374 | 171 | EXPECT_FALSE(disconnected); | ||
1375 | 172 | |||
1376 | 173 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1377 | 174 | while (mt::fd_is_readable(this->transport->watch_fd())) | ||
1378 | 175 | { | ||
1379 | 176 | this->transport->dispatch(); | ||
1380 | 177 | } | ||
1381 | 178 | |||
1382 | 179 | EXPECT_TRUE(data_available); | ||
1383 | 180 | EXPECT_TRUE(disconnected); | ||
1384 | 181 | } | ||
1385 | 182 | |||
1386 | 183 | TYPED_TEST(StreamTransportTest, DispatchesSingleEventAtATime) | ||
1387 | 184 | { | ||
1388 | 185 | using namespace testing; | ||
1389 | 186 | |||
1390 | 187 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | ||
1391 | 188 | bool data_available{false}; | ||
1392 | 189 | bool disconnected{false}; | ||
1393 | 190 | |||
1394 | 191 | uint64_t dummy{0xdeadbeef}; | ||
1395 | 192 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1396 | 193 | ::close(this->test_fd); | ||
1397 | 194 | |||
1398 | 195 | ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]() | ||
1399 | 196 | { | ||
1400 | 197 | decltype(dummy) buffer; | ||
1401 | 198 | this->transport->receive_data(&buffer, sizeof(buffer)); | ||
1402 | 199 | data_available = true; | ||
1403 | 200 | })); | ||
1404 | 201 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() | ||
1405 | 202 | { disconnected = true; })); | ||
1406 | 203 | |||
1407 | 204 | this->transport->register_observer(observer); | ||
1408 | 205 | |||
1409 | 206 | EXPECT_FALSE(data_available); | ||
1410 | 207 | EXPECT_FALSE(disconnected); | ||
1411 | 208 | |||
1412 | 209 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1413 | 210 | |||
1414 | 211 | this->transport->dispatch(); | ||
1415 | 212 | |||
1416 | 213 | EXPECT_TRUE(data_available xor disconnected); | ||
1417 | 214 | |||
1418 | 215 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1419 | 216 | |||
1420 | 217 | this->transport->dispatch(); | ||
1421 | 218 | |||
1422 | 219 | EXPECT_TRUE(data_available); | ||
1423 | 220 | EXPECT_TRUE(disconnected); | ||
1424 | 221 | } | ||
1425 | 222 | |||
1426 | 88 | TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnect) | 223 | TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnect) |
1427 | 89 | { | 224 | { |
1428 | 90 | using namespace testing; | 225 | using namespace testing; |
1429 | 91 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 226 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1431 | 92 | auto done = std::make_shared<mir::test::Signal>(); | 227 | bool disconnected{false}; |
1432 | 93 | 228 | ||
1435 | 94 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([done]() | 229 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() |
1436 | 95 | { done->raise(); })); | 230 | { disconnected = true; })); |
1437 | 96 | 231 | ||
1438 | 97 | this->transport->register_observer(observer); | 232 | this->transport->register_observer(observer); |
1439 | 98 | 233 | ||
1440 | 99 | close(this->test_fd); | 234 | close(this->test_fd); |
1441 | 100 | 235 | ||
1443 | 101 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{1})); | 236 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1444 | 237 | while (mt::fd_is_readable(this->transport->watch_fd())) | ||
1445 | 238 | { | ||
1446 | 239 | this->transport->dispatch(); | ||
1447 | 240 | } | ||
1448 | 241 | |||
1449 | 242 | EXPECT_TRUE(disconnected); | ||
1450 | 102 | } | 243 | } |
1451 | 103 | 244 | ||
1453 | 104 | TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnectWhileReadingInIOLoop) | 245 | TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnectWhileReading) |
1454 | 105 | { | 246 | { |
1455 | 106 | using namespace testing; | 247 | using namespace testing; |
1456 | 107 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 248 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1472 | 108 | auto done = std::make_shared<mir::test::Signal>(); | 249 | bool disconnected{false}; |
1473 | 109 | bool data_notified{false}; | 250 | bool receive_error_detected{false}; |
1474 | 110 | bool finished_read{false}; | 251 | |
1475 | 111 | 252 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() | |
1476 | 112 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([done]() | 253 | { disconnected = true; })); |
1462 | 113 | { done->raise(); })); | ||
1463 | 114 | ON_CALL(*observer, on_data_available()) | ||
1464 | 115 | .WillByDefault(Invoke([this, &data_notified, &finished_read]() | ||
1465 | 116 | { | ||
1466 | 117 | data_notified = true; | ||
1467 | 118 | char buffer[8]; | ||
1468 | 119 | this->transport->receive_data(buffer, sizeof(buffer)); | ||
1469 | 120 | finished_read = true; | ||
1470 | 121 | })); | ||
1471 | 122 | |||
1477 | 123 | this->transport->register_observer(observer); | 254 | this->transport->register_observer(observer); |
1478 | 124 | 255 | ||
1487 | 125 | uint32_t dummy{0xdeadbeef}; | 256 | mir::test::AutoJoinThread closer{[this]() |
1488 | 126 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | 257 | { |
1489 | 127 | 258 | std::this_thread::sleep_for(std::chrono::seconds{1}); | |
1490 | 128 | close(this->test_fd); | 259 | ::close(this->test_fd); |
1491 | 129 | 260 | }}; | |
1492 | 130 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{1})); | 261 | |
1493 | 131 | EXPECT_TRUE(data_notified); | 262 | try |
1494 | 132 | EXPECT_FALSE(finished_read); | 263 | { |
1495 | 264 | char buffer[8]; | ||
1496 | 265 | this->transport->receive_data(buffer, sizeof(buffer)); | ||
1497 | 266 | } | ||
1498 | 267 | catch (std::runtime_error) | ||
1499 | 268 | { | ||
1500 | 269 | receive_error_detected = true; | ||
1501 | 270 | } | ||
1502 | 271 | |||
1503 | 272 | // There should now be a disconnect event pending... | ||
1504 | 273 | EXPECT_TRUE(mt::fd_is_readable(this->transport->watch_fd())); | ||
1505 | 274 | |||
1506 | 275 | this->transport->dispatch(); | ||
1507 | 276 | |||
1508 | 277 | EXPECT_TRUE(disconnected); | ||
1509 | 278 | EXPECT_TRUE(receive_error_detected); | ||
1510 | 133 | } | 279 | } |
1511 | 134 | 280 | ||
1512 | 135 | TYPED_TEST(StreamTransportTest, NotifiesOnDataAvailable) | 281 | TYPED_TEST(StreamTransportTest, NotifiesOnDataAvailable) |
1513 | @@ -137,39 +283,21 @@ | |||
1514 | 137 | using namespace testing; | 283 | using namespace testing; |
1515 | 138 | 284 | ||
1516 | 139 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 285 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1550 | 140 | auto done = std::make_shared<mir::test::Signal>(); | 286 | bool notified_data_available{false}; |
1551 | 141 | 287 | ||
1552 | 142 | ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([done]() | 288 | ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([¬ified_data_available]() |
1553 | 143 | { done->raise(); })); | 289 | { notified_data_available = true; })); |
1554 | 144 | 290 | ||
1555 | 145 | this->transport->register_observer(observer); | 291 | this->transport->register_observer(observer); |
1556 | 146 | 292 | ||
1557 | 147 | uint64_t dummy{0xdeadbeef}; | 293 | uint64_t dummy{0xdeadbeef}; |
1558 | 148 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | 294 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); |
1559 | 149 | 295 | ||
1560 | 150 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{1})); | 296 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1561 | 151 | } | 297 | |
1562 | 152 | 298 | this->transport->dispatch(); | |
1563 | 153 | TYPED_TEST(StreamTransportTest, DoesntNotifyUntilDataAvailable) | 299 | |
1564 | 154 | { | 300 | EXPECT_TRUE(notified_data_available); |
1532 | 155 | using namespace testing; | ||
1533 | 156 | |||
1534 | 157 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | ||
1535 | 158 | auto done = std::make_shared<mir::test::Signal>(); | ||
1536 | 159 | |||
1537 | 160 | ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([done]() | ||
1538 | 161 | { done->raise(); })); | ||
1539 | 162 | |||
1540 | 163 | this->transport->register_observer(observer); | ||
1541 | 164 | |||
1542 | 165 | std::this_thread::sleep_for(std::chrono::seconds{1}); | ||
1543 | 166 | |||
1544 | 167 | EXPECT_FALSE(done->raised()); | ||
1545 | 168 | |||
1546 | 169 | uint64_t dummy{0xdeadbeef}; | ||
1547 | 170 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1548 | 171 | |||
1549 | 172 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{1})); | ||
1565 | 173 | } | 301 | } |
1566 | 174 | 302 | ||
1567 | 175 | TYPED_TEST(StreamTransportTest, KeepsNotifyingOfAvailableDataUntilAllIsRead) | 303 | TYPED_TEST(StreamTransportTest, KeepsNotifyingOfAvailableDataUntilAllIsRead) |
1568 | @@ -177,98 +305,104 @@ | |||
1569 | 177 | using namespace testing; | 305 | using namespace testing; |
1570 | 178 | 306 | ||
1571 | 179 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 307 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1572 | 180 | auto done = std::make_shared<mir::test::Signal>(); | ||
1573 | 181 | 308 | ||
1574 | 182 | std::array<uint8_t, sizeof(int) * 256> data; | 309 | std::array<uint8_t, sizeof(int) * 256> data; |
1575 | 183 | data.fill(0); | 310 | data.fill(0); |
1577 | 184 | std::atomic<size_t> bytes_left{data.size()}; | 311 | size_t bytes_left{data.size()}; |
1578 | 185 | 312 | ||
1579 | 186 | ON_CALL(*observer, on_data_available()) | 313 | ON_CALL(*observer, on_data_available()) |
1581 | 187 | .WillByDefault(Invoke([done, &bytes_left, this]() | 314 | .WillByDefault(Invoke([&bytes_left, this]() |
1582 | 188 | { | 315 | { |
1583 | 189 | int dummy; | 316 | int dummy; |
1584 | 190 | this->transport->receive_data(&dummy, sizeof(dummy)); | 317 | this->transport->receive_data(&dummy, sizeof(dummy)); |
1590 | 191 | bytes_left.fetch_sub(sizeof(dummy)); | 318 | bytes_left -= sizeof(dummy); |
1586 | 192 | if (bytes_left.load() == 0) | ||
1587 | 193 | { | ||
1588 | 194 | done->raise(); | ||
1589 | 195 | } | ||
1591 | 196 | })); | 319 | })); |
1592 | 197 | 320 | ||
1593 | 198 | this->transport->register_observer(observer); | 321 | this->transport->register_observer(observer); |
1594 | 199 | 322 | ||
1595 | 200 | EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size())); | 323 | EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size())); |
1596 | 201 | 324 | ||
1599 | 202 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{5})); | 325 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1600 | 203 | EXPECT_EQ(0, bytes_left.load()); | 326 | while (mt::fd_is_readable(this->transport->watch_fd())) |
1601 | 327 | { | ||
1602 | 328 | this->transport->dispatch(); | ||
1603 | 329 | } | ||
1604 | 330 | |||
1605 | 331 | EXPECT_EQ(0, bytes_left); | ||
1606 | 204 | } | 332 | } |
1607 | 205 | 333 | ||
1608 | 206 | TYPED_TEST(StreamTransportTest, StopsNotifyingOnceAllDataIsRead) | 334 | TYPED_TEST(StreamTransportTest, StopsNotifyingOnceAllDataIsRead) |
1609 | 207 | { | 335 | { |
1610 | 208 | using namespace testing; | 336 | using namespace testing; |
1611 | 209 | int const buffer_size{256}; | ||
1612 | 210 | 337 | ||
1613 | 211 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 338 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1615 | 212 | auto done = std::make_shared<mir::test::Signal>(); | 339 | |
1616 | 340 | std::array<uint8_t, sizeof(int) * 256> data; | ||
1617 | 341 | data.fill(0); | ||
1618 | 342 | size_t bytes_left{data.size()}; | ||
1619 | 213 | 343 | ||
1620 | 214 | ON_CALL(*observer, on_data_available()) | 344 | ON_CALL(*observer, on_data_available()) |
1622 | 215 | .WillByDefault(Invoke([this, done]() | 345 | .WillByDefault(Invoke([&bytes_left, this]() |
1623 | 216 | { | 346 | { |
1631 | 217 | if (done->raised()) | 347 | int dummy; |
1632 | 218 | { | 348 | this->transport->receive_data(&dummy, sizeof(dummy)); |
1633 | 219 | FAIL() << "on_data_available called without new data available"; | 349 | bytes_left -= sizeof(dummy); |
1627 | 220 | } | ||
1628 | 221 | uint8_t dummy_buffer[buffer_size]; | ||
1629 | 222 | this->transport->receive_data(dummy_buffer, sizeof(dummy_buffer)); | ||
1630 | 223 | done->raise(); | ||
1634 | 224 | })); | 350 | })); |
1635 | 351 | |||
1636 | 225 | this->transport->register_observer(observer); | 352 | this->transport->register_observer(observer); |
1637 | 226 | 353 | ||
1646 | 227 | EXPECT_FALSE(done->raised()); | 354 | EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size())); |
1647 | 228 | uint8_t dummy_buffer[buffer_size]; | 355 | |
1648 | 229 | memset(dummy_buffer, 0xab, sizeof(dummy_buffer)); | 356 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1649 | 230 | EXPECT_EQ(sizeof(dummy_buffer), write(this->test_fd, dummy_buffer, sizeof(dummy_buffer))); | 357 | while (bytes_left > 0) |
1650 | 231 | 358 | { | |
1651 | 232 | EXPECT_TRUE(done->wait_for(std::chrono::seconds{1})); | 359 | this->transport->dispatch(); |
1652 | 233 | 360 | } | |
1653 | 234 | std::this_thread::sleep_for(std::chrono::seconds{1}); | 361 | |
1654 | 362 | EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1655 | 235 | } | 363 | } |
1656 | 236 | 364 | ||
1657 | 237 | TYPED_TEST(StreamTransportTest, DoesntSendDataAvailableNotificationOnDisconnect) | 365 | TYPED_TEST(StreamTransportTest, DoesntSendDataAvailableNotificationOnDisconnect) |
1658 | 238 | { | 366 | { |
1659 | 239 | using namespace testing; | 367 | using namespace testing; |
1660 | 240 | int const buffer_size{256}; | ||
1661 | 241 | 368 | ||
1662 | 242 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 369 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1667 | 243 | auto read_done = std::make_shared<mir::test::Signal>(); | 370 | int notify_count{0}; |
1668 | 244 | auto disconnect_done = std::make_shared<mir::test::Signal>(); | 371 | bool disconnected{false}; |
1669 | 245 | std::atomic<int> notify_count{0}; | 372 | |
1670 | 246 | 373 | uint64_t dummy{0xdeedfaac}; | |
1671 | 374 | EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy))); | ||
1672 | 375 | |||
1673 | 376 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]() | ||
1674 | 377 | { disconnected = true; })); | ||
1675 | 247 | ON_CALL(*observer, on_data_available()) | 378 | ON_CALL(*observer, on_data_available()) |
1677 | 248 | .WillByDefault(Invoke([this, read_done, ¬ify_count]() | 379 | .WillByDefault(Invoke([dummy, ¬ify_count, this]() |
1678 | 249 | { | 380 | { |
1679 | 250 | notify_count++; | 381 | notify_count++; |
1683 | 251 | uint8_t dummy_buffer[buffer_size]; | 382 | |
1684 | 252 | this->transport->receive_data(dummy_buffer, sizeof(dummy_buffer)); | 383 | decltype(dummy) buffer; |
1685 | 253 | read_done->raise(); | 384 | this->transport->receive_data(&buffer, sizeof(buffer)); |
1686 | 254 | })); | 385 | })); |
1687 | 255 | ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([this, disconnect_done]() | ||
1688 | 256 | { disconnect_done->raise(); })); | ||
1689 | 257 | 386 | ||
1690 | 258 | this->transport->register_observer(observer); | 387 | this->transport->register_observer(observer); |
1691 | 259 | 388 | ||
1699 | 260 | EXPECT_FALSE(read_done->raised()); | 389 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1700 | 261 | uint8_t dummy_buffer[buffer_size]; | 390 | while (mt::fd_is_readable(this->transport->watch_fd())) |
1701 | 262 | memset(dummy_buffer, 0xab, sizeof(dummy_buffer)); | 391 | { |
1702 | 263 | EXPECT_EQ(sizeof(dummy_buffer), write(this->test_fd, dummy_buffer, sizeof(dummy_buffer))); | 392 | this->transport->dispatch(); |
1703 | 264 | 393 | } | |
1697 | 265 | EXPECT_TRUE(read_done->wait_for(std::chrono::seconds{1})); | ||
1698 | 266 | EXPECT_EQ(1, notify_count); | ||
1704 | 267 | 394 | ||
1705 | 268 | ::close(this->test_fd); | 395 | ::close(this->test_fd); |
1708 | 269 | EXPECT_TRUE(disconnect_done->wait_for(std::chrono::seconds{1})); | 396 | |
1709 | 270 | 397 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | |
1710 | 398 | while (mt::fd_is_readable(this->transport->watch_fd())) | ||
1711 | 399 | { | ||
1712 | 400 | this->transport->dispatch(); | ||
1713 | 401 | } | ||
1714 | 402 | |||
1715 | 403 | EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); | ||
1716 | 271 | EXPECT_EQ(1, notify_count); | 404 | EXPECT_EQ(1, notify_count); |
1717 | 405 | EXPECT_TRUE(disconnected); | ||
1718 | 272 | } | 406 | } |
1719 | 273 | 407 | ||
1720 | 274 | TYPED_TEST(StreamTransportTest, ReadsCorrectData) | 408 | TYPED_TEST(StreamTransportTest, ReadsCorrectData) |
1721 | @@ -276,23 +410,26 @@ | |||
1722 | 276 | using namespace testing; | 410 | using namespace testing; |
1723 | 277 | 411 | ||
1724 | 278 | auto observer = std::make_shared<NiceMock<MockObserver>>(); | 412 | auto observer = std::make_shared<NiceMock<MockObserver>>(); |
1725 | 279 | auto done = std::make_shared<mir::test::Signal>(); | ||
1726 | 280 | 413 | ||
1727 | 281 | std::string expected{"I am the very model of a modern major general"}; | 414 | std::string expected{"I am the very model of a modern major general"}; |
1728 | 282 | std::vector<char> received(expected.size()); | 415 | std::vector<char> received(expected.size()); |
1729 | 283 | 416 | ||
1730 | 284 | ON_CALL(*observer, on_data_available()) | 417 | ON_CALL(*observer, on_data_available()) |
1732 | 285 | .WillByDefault(Invoke([done, &received, this]() | 418 | .WillByDefault(Invoke([&received, this]() |
1733 | 286 | { | 419 | { |
1734 | 287 | this->transport->receive_data(received.data(), received.size()); | 420 | this->transport->receive_data(received.data(), received.size()); |
1735 | 288 | done->raise(); | ||
1736 | 289 | })); | 421 | })); |
1737 | 290 | 422 | ||
1738 | 291 | this->transport->register_observer(observer); | 423 | this->transport->register_observer(observer); |
1739 | 292 | 424 | ||
1740 | 293 | EXPECT_EQ(expected.size(), write(this->test_fd, expected.data(), expected.size())); | 425 | EXPECT_EQ(expected.size(), write(this->test_fd, expected.data(), expected.size())); |
1741 | 294 | 426 | ||
1743 | 295 | ASSERT_TRUE(done->wait_for(std::chrono::seconds{1})); | 427 | EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1})); |
1744 | 428 | while (mt::fd_is_readable(this->transport->watch_fd())) | ||
1745 | 429 | { | ||
1746 | 430 | this->transport->dispatch(); | ||
1747 | 431 | } | ||
1748 | 432 | |||
1749 | 296 | EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size())); | 433 | EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size())); |
1750 | 297 | } | 434 | } |
1751 | 298 | 435 |