Mir

Merge lp:~raof/mir/manual-connection-dispatch into lp:mir

Proposed by Chris Halse Rogers
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
Reviewer Review Type Date Requested Status
Mir development team Pending
Review via email: mp+243613@code.launchpad.net

Description of the change

First stage of client-eventloop driven client library dispatch.

To post a comment you must log in.

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 StreamSocketTransport::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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/client/mir_connection.cpp'
--- src/client/mir_connection.cpp 2014-11-27 09:00:52 +0000
+++ src/client/mir_connection.cpp 2014-12-04 01:50:56 +0000
@@ -22,6 +22,8 @@
22#include "client_platform.h"22#include "client_platform.h"
23#include "client_platform_factory.h"23#include "client_platform_factory.h"
24#include "rpc/mir_basic_rpc_channel.h"24#include "rpc/mir_basic_rpc_channel.h"
25#include "rpc/dispatchable.h"
26#include "rpc/simple_rpc_thread.h"
25#include "connection_configuration.h"27#include "connection_configuration.h"
26#include "display_configuration.h"28#include "display_configuration.h"
27#include "connection_surface_map.h"29#include "connection_surface_map.h"
@@ -92,8 +94,13 @@
92{94{
93}95}
9496
97MirConnection::MirConnection(mir::client::ConnectionConfiguration& conf) :
98 MirConnection(conf, DispatchType::automatic)
99{
100}
101
95MirConnection::MirConnection(102MirConnection::MirConnection(
96 mir::client::ConnectionConfiguration& conf) :103 mir::client::ConnectionConfiguration& conf, DispatchType dispatch) :
97 deregisterer{this},104 deregisterer{this},
98 platform_library{conf.the_platform_library()},105 platform_library{conf.the_platform_library()},
99 channel(conf.the_rpc_channel()),106 channel(conf.the_rpc_channel()),
@@ -105,7 +112,11 @@
105 display_configuration(conf.the_display_configuration()),112 display_configuration(conf.the_display_configuration()),
106 lifecycle_control(conf.the_lifecycle_control()),113 lifecycle_control(conf.the_lifecycle_control()),
107 surface_map(conf.the_surface_map()),114 surface_map(conf.the_surface_map()),
108 event_handler_register(conf.the_event_handler_register())115 event_handler_register(conf.the_event_handler_register()),
116 eventloop{dispatch == DispatchType::automatic ?
117 new mcl::rpc::SimpleRpcThread{std::dynamic_pointer_cast<mcl::rpc::Dispatchable>(channel)} :
118 nullptr
119 }
109{120{
110 connect_result.set_error("connect not called");121 connect_result.set_error("connect not called");
111 {122 {
@@ -333,6 +344,11 @@
333 drm_auth_magic_wait_handle.result_received();344 drm_auth_magic_wait_handle.result_received();
334}345}
335346
347int MirConnection::watch_fd() const
348{
349 return eventloop ? -1 : std::dynamic_pointer_cast<mcl::rpc::Dispatchable>(channel)->watch_fd();
350}
351
336MirWaitHandle* MirConnection::drm_auth_magic(unsigned int magic,352MirWaitHandle* MirConnection::drm_auth_magic(unsigned int magic,
337 mir_drm_auth_magic_callback callback,353 mir_drm_auth_magic_callback callback,
338 void* context)354 void* context)
339355
=== modified file 'src/client/mir_connection.h'
--- src/client/mir_connection.h 2014-11-27 09:00:52 +0000
+++ src/client/mir_connection.h 2014-12-04 01:50:56 +0000
@@ -52,6 +52,7 @@
52namespace rpc52namespace rpc
53{53{
54class MirBasicRpcChannel;54class MirBasicRpcChannel;
55class SimpleRpcThread;
55}56}
56}57}
5758
@@ -69,12 +70,19 @@
69}70}
70}71}
7172
73enum class DispatchType
74{
75 automatic,
76 manual
77};
78
72struct MirConnection : mir::client::ClientContext79struct MirConnection : mir::client::ClientContext
73{80{
74public:81public:
75 MirConnection(std::string const& error_message);82 MirConnection(std::string const& error_message);
7683
77 MirConnection(mir::client::ConnectionConfiguration& conf);84 MirConnection(mir::client::ConnectionConfiguration& conf);
85 MirConnection(mir::client::ConnectionConfiguration &conf, DispatchType dispatch);
78 ~MirConnection() noexcept;86 ~MirConnection() noexcept;
7987
80 MirConnection(MirConnection const &) = delete;88 MirConnection(MirConnection const &) = delete;
@@ -100,6 +108,8 @@
100108
101 MirWaitHandle* disconnect();109 MirWaitHandle* disconnect();
102110
111 int watch_fd() const;
112
103 MirWaitHandle* drm_auth_magic(unsigned int magic,113 MirWaitHandle* drm_auth_magic(unsigned int magic,
104 mir_drm_auth_magic_callback callback,114 mir_drm_auth_magic_callback callback,
105 void* context);115 void* context);
@@ -183,6 +193,8 @@
183193
184 std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;194 std::shared_ptr<mir::client::EventHandlerRegister> const event_handler_register;
185195
196 std::unique_ptr<mir::client::rpc::SimpleRpcThread> const eventloop;
197
186 std::vector<int> extra_platform_data;198 std::vector<int> extra_platform_data;
187199
188 struct SurfaceRelease;200 struct SurfaceRelease;
189201
=== modified file 'src/client/rpc/CMakeLists.txt'
--- src/client/rpc/CMakeLists.txt 2014-11-27 09:00:52 +0000
+++ src/client/rpc/CMakeLists.txt 2014-12-04 01:50:56 +0000
@@ -5,4 +5,5 @@
5 mir_protobuf_rpc_channel.cpp5 mir_protobuf_rpc_channel.cpp
6 make_socket_rpc_channel.cpp6 make_socket_rpc_channel.cpp
7 stream_socket_transport.cpp7 stream_socket_transport.cpp
8 simple_rpc_thread.cpp
8)9)
910
=== added file 'src/client/rpc/dispatchable.h'
--- src/client/rpc/dispatchable.h 1970-01-01 00:00:00 +0000
+++ src/client/rpc/dispatchable.h 2014-12-04 01:50:56 +0000
@@ -0,0 +1,55 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_CLIENT_RPC_DISPATCHABLE_H_
20#define MIR_CLIENT_RPC_DISPATCHABLE_H_
21
22#include "mir/fd.h"
23
24namespace mir
25{
26namespace client
27{
28namespace rpc
29{
30class Dispatchable
31{
32public:
33 Dispatchable() = default;
34 virtual ~Dispatchable() = default;
35
36 Dispatchable& operator=(Dispatchable const&) = delete;
37 Dispatchable(Dispatchable const&) = delete;
38
39 /**
40 * \brief Get a poll()able file descriptor
41 * \return A file descriptor usable with poll() or equivalent function calls that
42 * becomes readable when there are dispatchable events
43 */
44 virtual Fd watch_fd() const = 0;
45
46 /**
47 * \brief Dispatch one pending event
48 */
49 virtual void dispatch() = 0;
50};
51}
52}
53}
54
55#endif // MIR_CLIENT_RPC_DISPATCHABLE_H_
056
=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.cpp'
--- src/client/rpc/mir_protobuf_rpc_channel.cpp 2014-11-27 09:00:52 +0000
+++ src/client/rpc/mir_protobuf_rpc_channel.cpp 2014-12-04 01:50:56 +0000
@@ -338,3 +338,13 @@
338{338{
339 notify_disconnected();339 notify_disconnected();
340}340}
341
342mir::Fd mir::client::rpc::MirProtobufRpcChannel::watch_fd() const
343{
344 return transport->watch_fd();
345}
346
347void mir::client::rpc::MirProtobufRpcChannel::dispatch()
348{
349 transport->dispatch();
350}
341351
=== modified file 'src/client/rpc/mir_protobuf_rpc_channel.h'
--- src/client/rpc/mir_protobuf_rpc_channel.h 2014-11-27 09:00:52 +0000
+++ src/client/rpc/mir_protobuf_rpc_channel.h 2014-12-04 01:50:56 +0000
@@ -21,6 +21,7 @@
2121
22#include "mir_basic_rpc_channel.h"22#include "mir_basic_rpc_channel.h"
23#include "stream_transport.h"23#include "stream_transport.h"
24#include "dispatchable.h"
2425
25#include <google/protobuf/service.h>26#include <google/protobuf/service.h>
26#include <google/protobuf/descriptor.h>27#include <google/protobuf/descriptor.h>
@@ -52,7 +53,8 @@
5253
53class MirProtobufRpcChannel :54class MirProtobufRpcChannel :
54 public MirBasicRpcChannel,55 public MirBasicRpcChannel,
55 public StreamTransport::Observer56 public StreamTransport::Observer,
57 public Dispatchable
56{58{
57public:59public:
58 MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport,60 MirProtobufRpcChannel(std::unique_ptr<StreamTransport> transport,
@@ -64,8 +66,13 @@
6466
65 ~MirProtobufRpcChannel() = default;67 ~MirProtobufRpcChannel() = default;
6668
69 // StreamTransport::Observer
67 void on_data_available() override;70 void on_data_available() override;
68 void on_disconnected() override;71 void on_disconnected() override;
72
73 // Dispatchable
74 Fd watch_fd() const override;
75 void dispatch() override;
69private:76private:
70 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,77 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,
71 const google::protobuf::Message* parameters, google::protobuf::Message* response,78 const google::protobuf::Message* parameters, google::protobuf::Message* response,
7279
=== added file 'src/client/rpc/simple_rpc_thread.cpp'
--- src/client/rpc/simple_rpc_thread.cpp 1970-01-01 00:00:00 +0000
+++ src/client/rpc/simple_rpc_thread.cpp 2014-12-04 01:50:56 +0000
@@ -0,0 +1,103 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "simple_rpc_thread.h"
20#include "dispatchable.h"
21
22#include <sys/epoll.h>
23#include <unistd.h>
24#include <system_error>
25#include <signal.h>
26#include <boost/exception/all.hpp>
27
28namespace mclr = mir::client::rpc;
29
30namespace
31{
32void wait_for_events_forever(std::shared_ptr<mclr::Dispatchable> const& dispatchee, mir::Fd shutdown_fd)
33{
34 auto epoll_fd = mir::Fd{epoll_create1(0)};
35 if (epoll_fd == mir::Fd::invalid)
36 {
37 BOOST_THROW_EXCEPTION((std::system_error{errno,
38 std::system_category(),
39 "Failed to create epoll IO monitor"}));
40 }
41 epoll_event event;
42 memset(&event, 0, sizeof(event));
43
44 // We only care when the shutdown pipe has been closed
45 event.events = EPOLLRDHUP;
46 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event);
47
48 // We want readability or closure for the dispatchee.
49 event.events = EPOLLIN | EPOLLRDHUP;
50 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dispatchee->watch_fd(), &event);
51
52 for (;;)
53 {
54 epoll_wait(epoll_fd, &event, 1, -1);
55 if (event.events & EPOLLIN)
56 {
57 dispatchee->dispatch();
58 }
59 else if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
60 {
61 // On hangup we go away.
62 return;
63 }
64 }
65}
66
67}
68
69mclr::SimpleRpcThread::SimpleRpcThread(std::shared_ptr<mclr::Dispatchable> const& dispatchee)
70{
71 int pipefds[2];
72 if (pipe(pipefds) < 0)
73 {
74 BOOST_THROW_EXCEPTION((std::system_error{errno,
75 std::system_category(),
76 "Failed to create shutdown pipe for IO thread"}));
77 }
78 shutdown_fd = mir::Fd{pipefds[1]};
79 mir::Fd const terminate_fd = mir::Fd{pipefds[0]};
80 eventloop = std::thread{[dispatchee, terminate_fd]()
81 {
82 // Our IO threads must not receive any signals
83 sigset_t all_signals;
84 sigfillset(&all_signals);
85
86 if (auto error = pthread_sigmask(SIG_BLOCK, &all_signals, NULL))
87 BOOST_THROW_EXCEPTION((
88 std::system_error{error,
89 std::system_category(),
90 "Failed to block signals on IO thread"}));
91
92 wait_for_events_forever(dispatchee, terminate_fd);
93 }};
94}
95
96mclr::SimpleRpcThread::~SimpleRpcThread() noexcept
97{
98 ::close(shutdown_fd);
99 if (eventloop.joinable())
100 {
101 eventloop.join();
102 }
103}
0104
=== added file 'src/client/rpc/simple_rpc_thread.h'
--- src/client/rpc/simple_rpc_thread.h 1970-01-01 00:00:00 +0000
+++ src/client/rpc/simple_rpc_thread.h 2014-12-04 01:50:56 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_
20#define MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_
21
22#include <memory>
23#include <thread>
24#include "mir/fd.h"
25
26namespace mir
27{
28namespace client
29{
30namespace rpc
31{
32class Dispatchable;
33
34class SimpleRpcThread
35{
36public:
37 SimpleRpcThread(std::shared_ptr<Dispatchable> const& dispatchee);
38 ~SimpleRpcThread() noexcept;
39
40private:
41 Fd shutdown_fd;
42 std::thread eventloop;
43};
44
45}
46}
47}
48
49
50#endif // MIR_CLIENT_RPC_SIMPLE_RPC_THREAD_H_
051
=== modified file 'src/client/rpc/stream_socket_transport.cpp'
--- src/client/rpc/stream_socket_transport.cpp 2014-11-27 09:00:52 +0000
+++ src/client/rpc/stream_socket_transport.cpp 2014-12-04 01:50:56 +0000
@@ -23,7 +23,6 @@
2323
24#include <system_error>24#include <system_error>
2525
26#include <signal.h>
27#include <errno.h>26#include <errno.h>
28#include <sys/epoll.h>27#include <sys/epoll.h>
29#include <sys/types.h>28#include <sys/types.h>
@@ -35,10 +34,23 @@
3534
36namespace mclr = mir::client::rpc;35namespace mclr = mir::client::rpc;
3736
38mclr::StreamSocketTransport::StreamSocketTransport(mir::Fd const& fd)37mclr::StreamSocketTransport::StreamSocketTransport(Fd const& fd)
39 : socket_fd{fd}38 : socket_fd{fd},
39 epoll_fd{epoll_create1(EPOLL_CLOEXEC)}
40{40{
41 init();41 if (epoll_fd < 0)
42 {
43 BOOST_THROW_EXCEPTION((std::system_error{errno,
44 std::system_category(),
45 "Failed to create epoll monitor for IO"}));
46 }
47 epoll_event event;
48 // Make valgrind happy, harder
49 memset(&event, 0, sizeof(event));
50
51 event.events = EPOLLIN | EPOLLRDHUP;
52 event.data.fd = socket_fd;
53 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);
42}54}
4355
44mclr::StreamSocketTransport::StreamSocketTransport(std::string const& socket_path)56mclr::StreamSocketTransport::StreamSocketTransport(std::string const& socket_path)
@@ -46,17 +58,6 @@
46{58{
47}59}
4860
49mclr::StreamSocketTransport::~StreamSocketTransport()
50{
51 int dummy{0};
52 send(shutdown_fd, &dummy, sizeof(dummy), MSG_NOSIGNAL);
53 if (io_service_thread.joinable())
54 {
55 io_service_thread.join();
56 }
57 close(shutdown_fd);
58}
59
60void mclr::StreamSocketTransport::register_observer(std::shared_ptr<Observer> const& observer)61void mclr::StreamSocketTransport::register_observer(std::shared_ptr<Observer> const& observer)
61{62{
62 std::lock_guard<decltype(observer_mutex)> lock(observer_mutex);63 std::lock_guard<decltype(observer_mutex)> lock(observer_mutex);
@@ -176,110 +177,40 @@
176 mir::send_fds(socket_fd, fds);177 mir::send_fds(socket_fd, fds);
177}178}
178179
179void mclr::StreamSocketTransport::init()180mir::Fd mclr::StreamSocketTransport::watch_fd() const
180{181{
181 // We use sockets rather than a pipe so that we can control182 return epoll_fd;
182 // EPIPE behaviour; we don't want SIGPIPE when the IO loop terminates.183}
183 int socket_fds[2];
184 socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
185 this->shutdown_fd = mir::Fd{socket_fds[1]};
186184
187 auto shutdown_fd = mir::Fd{socket_fds[0]};185void mclr::StreamSocketTransport::dispatch()
188 io_service_thread = std::thread([this, shutdown_fd]186{
187 epoll_event event;
188 epoll_wait(epoll_fd, &event, 1, 0);
189 if (event.data.fd == socket_fd)
189 {190 {
190 // Our IO threads must not receive any signals191 if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
191 sigset_t all_signals;192 {
192 sigfillset(&all_signals);193 if (event.events & EPOLLIN)
193194 {
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
195 BOOST_THROW_EXCEPTION(196 // data left to read, or that reads will now return 0 (EOF)
196 boost::enable_error_info(197 //
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.
198199 int dummy;
199 mir::set_thread_name("Client IO loop");200 if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0)
200201 {
201 int epoll_fd = epoll_create1(0);202 notify_data_available();
202203 return;
203 epoll_event event;204 }
204 // Make valgrind happy, harder205 }
205 memset(&event, 0, sizeof(event));206 notify_disconnected();
206207 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, nullptr);
207 event.events = EPOLLIN | EPOLLRDHUP;208 }
208 event.data.fd = socket_fd;209 else if (event.events & EPOLLIN)
209 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event);210 {
210211 notify_data_available();
211 event.events = EPOLLIN | EPOLLRDHUP;212 }
212 event.data.fd = shutdown_fd;213 }
213 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, shutdown_fd, &event);
214
215 bool shutdown_requested{false};
216 while (!shutdown_requested)
217 {
218 epoll_event event;
219 epoll_wait(epoll_fd, &event, 1, -1);
220 if (event.data.fd == socket_fd)
221 {
222 if (event.events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))
223 {
224 if (event.events & EPOLLIN)
225 {
226 // If the remote end shut down cleanly it's possible there's some more
227 // data left to read, or that reads will now return 0 (EOF)
228 //
229 // If there's more data left to read, notify of this before disconnect.
230 int dummy;
231 if (recv(socket_fd, &dummy, sizeof(dummy), MSG_PEEK | MSG_NOSIGNAL) > 0)
232 {
233 try
234 {
235 notify_data_available();
236 }
237 catch(...)
238 {
239 //It's quite likely that notify_data_available() will lead to
240 //an exception being thrown; after all, the remote has closed
241 //the connection.
242 //
243 //This doesn't matter; we're already shutting down.
244 }
245 }
246 }
247 notify_disconnected();
248 shutdown_requested = true;
249 }
250 else if (event.events & EPOLLIN)
251 {
252 try
253 {
254 notify_data_available();
255 }
256 catch (socket_disconnected_error &err)
257 {
258 // We've already notified of disconnection.
259 shutdown_requested = true;
260 }
261 // These need not be fatal.
262 catch (fd_reception_error &err)
263 {
264 }
265 catch (socket_error &err)
266 {
267 }
268 catch (...)
269 {
270 // We've no idea what the problem is, so clean up as best we can.
271 notify_disconnected();
272 shutdown_requested = true;
273 }
274 }
275 }
276 if (event.data.fd == shutdown_fd)
277 {
278 shutdown_requested = true;
279 }
280 }
281 ::close(epoll_fd);
282 });
283}214}
284215
285mir::Fd mclr::StreamSocketTransport::open_socket(std::string const& path)216mir::Fd mclr::StreamSocketTransport::open_socket(std::string const& path)
286217
=== modified file 'src/client/rpc/stream_socket_transport.h'
--- src/client/rpc/stream_socket_transport.h 2014-11-27 09:00:52 +0000
+++ src/client/rpc/stream_socket_transport.h 2014-12-04 01:50:56 +0000
@@ -38,22 +38,22 @@
38public:38public:
39 StreamSocketTransport(Fd const& fd);39 StreamSocketTransport(Fd const& fd);
40 StreamSocketTransport(std::string const& socket_path);40 StreamSocketTransport(std::string const& socket_path);
41 ~StreamSocketTransport() override;
4241
43 void register_observer(std::shared_ptr<Observer> const& observer) override;42 void register_observer(std::shared_ptr<Observer> const& observer) override;
44 void receive_data(void* buffer, size_t bytes_requested) override;43 void receive_data(void* buffer, size_t bytes_requested) override;
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;
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;
4746
47 Fd watch_fd() const override;
48 void dispatch() override;
49
48private:50private:
49 void init();
50 Fd open_socket(std::string const& path);51 Fd open_socket(std::string const& path);
51 void notify_data_available();52 void notify_data_available();
52 void notify_disconnected();53 void notify_disconnected();
5354
54 std::thread io_service_thread;
55 Fd const socket_fd;55 Fd const socket_fd;
56 Fd shutdown_fd;56 Fd const epoll_fd;
5757
58 std::mutex observer_mutex;58 std::mutex observer_mutex;
59 std::vector<std::shared_ptr<Observer>> observers;59 std::vector<std::shared_ptr<Observer>> observers;
6060
=== modified file 'src/client/rpc/stream_transport.h'
--- src/client/rpc/stream_transport.h 2014-11-27 09:00:52 +0000
+++ src/client/rpc/stream_transport.h 2014-12-04 01:50:56 +0000
@@ -25,6 +25,7 @@
25#include <stdint.h>25#include <stdint.h>
2626
27#include "mir/fd.h"27#include "mir/fd.h"
28#include "dispatchable.h"
2829
29namespace mir30namespace mir
30{31{
@@ -64,7 +65,7 @@
64 * from different threads. Multiple threads calling the same65 * from different threads. Multiple threads calling the same
65 * function need synchronisation.66 * function need synchronisation.
66 */67 */
67class StreamTransport68class StreamTransport : public Dispatchable
68{69{
69public:70public:
70 /**71 /**
@@ -80,8 +81,8 @@
8081
81 /**82 /**
82 * \brief Observer of IO status83 * \brief Observer of IO status
83 * \note The Transport may call Observer members from arbitrary threads.84 * \note The Transport will only call Observers in response to dispatch(),
84 * The Observer implementation is responsible for any synchronisation.85 * and on the thread calling dispatch().
85 */86 */
86 class Observer87 class Observer
87 {88 {
@@ -106,8 +107,6 @@
106 /**107 /**
107 * \brief Register an IO observer108 * \brief Register an IO observer
108 * \param [in] observer109 * \param [in] observer
109 * \note There is no guarantee which thread will call into the observer.
110 * Synchronisation is the responsibility of the caller.
111 */110 */
112 virtual void register_observer(std::shared_ptr<Observer> const& observer) = 0;111 virtual void register_observer(std::shared_ptr<Observer> const& observer) = 0;
113112
114113
=== added file 'tests/include/mir_test/fd_utils.h'
--- tests/include/mir_test/fd_utils.h 1970-01-01 00:00:00 +0000
+++ tests/include/mir_test/fd_utils.h 2014-12-04 01:50:56 +0000
@@ -0,0 +1,69 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_TEST_FD_UTILS_H_
20#define MIR_TEST_FD_UTILS_H_
21
22#include "mir/fd.h"
23#include <chrono>
24
25#include <poll.h>
26
27#include <gtest/gtest.h>
28
29namespace mir
30{
31namespace test
32{
33::testing::AssertionResult std_call_succeeded(int retval);
34
35::testing::AssertionResult fd_is_readable(mir::Fd const& fd);
36
37template<typename Period, typename Rep>
38::testing::AssertionResult fd_becomes_readable(mir::Fd const& fd,
39 std::chrono::duration<Period, Rep> timeout)
40{
41 int timeout_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count();
42
43 pollfd readable;
44 readable.events = POLLIN;
45 readable.fd = fd;
46
47 auto result = std_call_succeeded(poll(&readable, 1, timeout_ms));
48 if (result)
49 {
50 if (readable.revents & POLLERR)
51 {
52 return ::testing::AssertionFailure() << "error condition on fd";
53 }
54 if (readable.revents & POLLNVAL)
55 {
56 return ::testing::AssertionFailure() << "fd is invalid";
57 }
58 if (!(readable.revents & POLLIN))
59 {
60 return ::testing::AssertionFailure() << "fd is not readable";
61 }
62 return ::testing::AssertionSuccess();
63 }
64 return result;
65}
66}
67}
68
69#endif // MIR_TEST_FD_UTILS_H_
070
=== modified file 'tests/include/mir_test/test_protobuf_client.h'
--- tests/include/mir_test/test_protobuf_client.h 2014-11-27 09:00:52 +0000
+++ tests/include/mir_test/test_protobuf_client.h 2014-12-04 01:50:56 +0000
@@ -30,6 +30,13 @@
3030
31namespace mir31namespace mir
32{32{
33namespace client
34{
35namespace rpc
36{
37class SimpleRpcThread;
38}
39}
33namespace test40namespace test
34{41{
35namespace doubles42namespace doubles
@@ -42,6 +49,7 @@
4249
43 std::shared_ptr<doubles::MockRpcReport> rpc_report;50 std::shared_ptr<doubles::MockRpcReport> rpc_report;
44 std::shared_ptr<google::protobuf::RpcChannel> channel;51 std::shared_ptr<google::protobuf::RpcChannel> channel;
52 std::shared_ptr<client::rpc::SimpleRpcThread> eventloop;
45 mir::protobuf::DisplayServer::Stub display_server;53 mir::protobuf::DisplayServer::Stub display_server;
46 mir::protobuf::ConnectParameters connect_parameters;54 mir::protobuf::ConnectParameters connect_parameters;
47 mir::protobuf::SurfaceParameters surface_parameters;55 mir::protobuf::SurfaceParameters surface_parameters;
4856
=== modified file 'tests/integration-tests/client/test_screencast.cpp'
--- tests/integration-tests/client/test_screencast.cpp 2014-11-27 09:00:52 +0000
+++ tests/integration-tests/client/test_screencast.cpp 2014-12-04 01:50:56 +0000
@@ -18,6 +18,8 @@
1818
19#include "mir_protobuf.pb.h"19#include "mir_protobuf.pb.h"
20#include "src/client/default_connection_configuration.h"20#include "src/client/default_connection_configuration.h"
21#include "src/client/rpc/simple_rpc_thread.h"
22#include "src/client/rpc/dispatchable.h"
2123
22#include "mir/frontend/connector.h"24#include "mir/frontend/connector.h"
23#include "mir_test/test_protobuf_server.h"25#include "mir_test/test_protobuf_server.h"
@@ -74,6 +76,9 @@
74 mcl::DefaultConnectionConfiguration{test_socket}.the_rpc_channel();76 mcl::DefaultConnectionConfiguration{test_socket}.the_rpc_channel();
75 protobuf_server =77 protobuf_server =
76 std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get());78 std::make_shared<mir::protobuf::DisplayServer::Stub>(rpc_channel.get());
79 eventloop =
80 std::make_shared<mir::client::rpc::SimpleRpcThread>(
81 std::dynamic_pointer_cast<mir::client::rpc::Dispatchable>(rpc_channel));
77 }82 }
7883
79 char const* const test_socket = "./test_socket_screencast";84 char const* const test_socket = "./test_socket_screencast";
@@ -81,6 +86,7 @@
81 std::shared_ptr<mt::TestProtobufServer> test_server;86 std::shared_ptr<mt::TestProtobufServer> test_server;
82 std::shared_ptr<google::protobuf::RpcChannel> rpc_channel;87 std::shared_ptr<google::protobuf::RpcChannel> rpc_channel;
83 std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server;88 std::shared_ptr<mir::protobuf::DisplayServer> protobuf_server;
89 std::shared_ptr<mir::client::rpc::SimpleRpcThread> eventloop;
84};90};
8591
86}92}
8793
=== modified file 'tests/mir_test/CMakeLists.txt'
--- tests/mir_test/CMakeLists.txt 2014-11-27 09:00:52 +0000
+++ tests/mir_test/CMakeLists.txt 2014-12-04 01:50:56 +0000
@@ -11,6 +11,7 @@
11 wait_object.cpp11 wait_object.cpp
12 current_thread_name.cpp12 current_thread_name.cpp
13 validity_matchers.cpp13 validity_matchers.cpp
14 fd_utils.cpp
14)15)
1516
16target_link_libraries(mir-test mirprotobuf)17target_link_libraries(mir-test mirprotobuf)
1718
=== added file 'tests/mir_test/fd_utils.cpp'
--- tests/mir_test/fd_utils.cpp 1970-01-01 00:00:00 +0000
+++ tests/mir_test/fd_utils.cpp 2014-12-04 01:50:56 +0000
@@ -0,0 +1,40 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir_test/fd_utils.h"
20
21::testing::AssertionResult mir::test::std_call_succeeded(int retval)
22{
23 if (retval >= 0)
24 {
25 return ::testing::AssertionSuccess();
26 }
27 else
28 {
29 return ::testing::AssertionFailure() << "errno: "
30 << errno
31 << " ["
32 << strerror(errno)
33 << "]";
34 }
35}
36
37::testing::AssertionResult mir::test::fd_is_readable(mir::Fd const& fd)
38{
39 return fd_becomes_readable(fd, std::chrono::seconds{0});
40}
041
=== modified file 'tests/mir_test_doubles/test_protobuf_client.cpp'
--- tests/mir_test_doubles/test_protobuf_client.cpp 2014-11-27 09:00:52 +0000
+++ tests/mir_test_doubles/test_protobuf_client.cpp 2014-12-04 01:50:56 +0000
@@ -25,22 +25,26 @@
25#include "src/client/lifecycle_control.h"25#include "src/client/lifecycle_control.h"
26#include "src/client/rpc/make_rpc_channel.h"26#include "src/client/rpc/make_rpc_channel.h"
27#include "src/client/rpc/mir_basic_rpc_channel.h"27#include "src/client/rpc/mir_basic_rpc_channel.h"
28#include "src/client/rpc/dispatchable.h"
29#include "src/client/rpc/simple_rpc_thread.h"
2830
29#include <thread>31#include <thread>
3032
31namespace mtd = mir::test::doubles;33namespace mtd = mir::test::doubles;
34namespace mclr = mir::client::rpc;
3235
33mir::test::TestProtobufClient::TestProtobufClient(36mir::test::TestProtobufClient::TestProtobufClient(
34 std::string socket_file,37 std::string socket_file,
35 int timeout_ms) :38 int timeout_ms) :
36 rpc_report(std::make_shared<testing::NiceMock<doubles::MockRpcReport>>()),39 rpc_report(std::make_shared<testing::NiceMock<doubles::MockRpcReport>>()),
37 channel(mir::client::rpc::make_rpc_channel(40 channel(mclr::make_rpc_channel(
38 socket_file,41 socket_file,
39 std::make_shared<mir::client::ConnectionSurfaceMap>(),42 std::make_shared<mir::client::ConnectionSurfaceMap>(),
40 std::make_shared<mir::client::DisplayConfiguration>(),43 std::make_shared<mir::client::DisplayConfiguration>(),
41 rpc_report,44 rpc_report,
42 std::make_shared<mir::client::LifecycleControl>(),45 std::make_shared<mir::client::LifecycleControl>(),
43 std::make_shared<mtd::NullClientEventSink>())),46 std::make_shared<mtd::NullClientEventSink>())),
47 eventloop{std::make_shared<mclr::SimpleRpcThread>(std::dynamic_pointer_cast<mclr::Dispatchable>(channel))},
44 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),48 display_server(channel.get(), ::google::protobuf::Service::STUB_DOESNT_OWN_CHANNEL),
45 maxwait(timeout_ms),49 maxwait(timeout_ms),
46 connect_done_called(false),50 connect_done_called(false),
4751
=== modified file 'tests/unit-tests/client/CMakeLists.txt'
--- tests/unit-tests/client/CMakeLists.txt 2014-11-27 09:00:52 +0000
+++ tests/unit-tests/client/CMakeLists.txt 2014-12-04 01:50:56 +0000
@@ -13,6 +13,7 @@
13 ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_prompt_session.cpp13 ${CMAKE_CURRENT_SOURCE_DIR}/test_mir_prompt_session.cpp
14 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_distributor.cpp14 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_distributor.cpp
15 ${CMAKE_CURRENT_SOURCE_DIR}/test_periodic_perf_report.cpp15 ${CMAKE_CURRENT_SOURCE_DIR}/test_periodic_perf_report.cpp
16 ${CMAKE_CURRENT_SOURCE_DIR}/test_simple_rpc_thread.cpp
16)17)
1718
18if(MIR_TEST_PLATFORM STREQUAL "android")19if(MIR_TEST_PLATFORM STREQUAL "android")
1920
=== modified file 'tests/unit-tests/client/test_mir_connection.cpp'
--- tests/unit-tests/client/test_mir_connection.cpp 2014-11-27 09:00:52 +0000
+++ tests/unit-tests/client/test_mir_connection.cpp 2014-12-04 01:50:56 +0000
@@ -25,10 +25,14 @@
25#include "src/client/display_configuration.h"25#include "src/client/display_configuration.h"
26#include "src/client/mir_surface.h"26#include "src/client/mir_surface.h"
27#include "src/client/client_buffer_factory.h"27#include "src/client/client_buffer_factory.h"
28#include "src/client/rpc/dispatchable.h"
2829
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 */
30#include "mir_test/test_protobuf_server.h"31#include "mir_test/test_protobuf_server.h"
31#include "mir_test/stub_server_tool.h"32#include "mir_test/stub_server_tool.h"
33#include "mir_test/pipe.h"
34#include "mir_test/signal.h"
35#include "mir_test/fd_utils.h"
32#include "mir_test_doubles/stub_client_buffer_factory.h"36#include "mir_test_doubles/stub_client_buffer_factory.h"
3337
34#include "mir_protobuf.pb.h"38#include "mir_protobuf.pb.h"
@@ -46,8 +50,14 @@
46namespace50namespace
47{51{
4852
49struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel53struct MockRpcChannel : public mir::client::rpc::MirBasicRpcChannel,
54 public mir::client::rpc::Dispatchable
50{55{
56 MockRpcChannel()
57 {
58 ON_CALL(*this, watch_fd()).WillByDefault(testing::Return(mir::Fd{}));
59 }
60
51 void CallMethod(const google::protobuf::MethodDescriptor* method,61 void CallMethod(const google::protobuf::MethodDescriptor* method,
52 google::protobuf::RpcController*,62 google::protobuf::RpcController*,
53 const google::protobuf::Message* parameters,63 const google::protobuf::Message* parameters,
@@ -75,6 +85,9 @@
75 MOCK_METHOD1(drm_auth_magic, void(const mp::DRMMagic*));85 MOCK_METHOD1(drm_auth_magic, void(const mp::DRMMagic*));
76 MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*));86 MOCK_METHOD2(connect, void(mp::ConnectParameters const*,mp::Connection*));
77 MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*));87 MOCK_METHOD1(configure_display_sent, void(mp::DisplayConfiguration const*));
88
89 MOCK_CONST_METHOD0(watch_fd, mir::Fd());
90 MOCK_METHOD0(dispatch, void());
78};91};
7992
80struct MockClientPlatform : public mcl::ClientPlatform93struct MockClientPlatform : public mcl::ClientPlatform
@@ -166,21 +179,21 @@
166 MirConnectionTest()179 MirConnectionTest()
167 : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()},180 : mock_platform{std::make_shared<testing::NiceMock<MockClientPlatform>>()},
168 mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()},181 mock_channel{std::make_shared<testing::NiceMock<MockRpcChannel>>()},
169 conf{mock_platform, mock_channel},182 conf{mock_platform, mock_channel}
170 connection{std::make_shared<MirConnection>(conf)}
171 {183 {
172 }184 }
173185
174 std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform;186 std::shared_ptr<testing::NiceMock<MockClientPlatform>> const mock_platform;
175 std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel;187 std::shared_ptr<testing::NiceMock<MockRpcChannel>> const mock_channel;
176 TestConnectionConfiguration conf;188 TestConnectionConfiguration conf;
177 std::shared_ptr<MirConnection> const connection;
178};189};
179190
180TEST_F(MirConnectionTest, returns_correct_egl_native_display)191TEST_F(MirConnectionTest, returns_correct_egl_native_display)
181{192{
182 using namespace testing;193 using namespace testing;
183194
195 auto connection = std::make_shared<MirConnection>(conf);
196
184 EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef);197 EGLNativeDisplayType native_display_raw = reinterpret_cast<EGLNativeDisplayType>(0xabcdef);
185 auto native_display = std::make_shared<EGLNativeDisplayType>();198 auto native_display = std::make_shared<EGLNativeDisplayType>();
186 *native_display = native_display_raw;199 *native_display = native_display_raw;
@@ -206,6 +219,8 @@
206{219{
207 using namespace testing;220 using namespace testing;
208221
222 auto connection = std::make_shared<MirConnection>(conf);
223
209 unsigned int const drm_magic{0x10111213};224 unsigned int const drm_magic{0x10111213};
210225
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)))
@@ -276,6 +291,8 @@
276{291{
277 using namespace testing;292 using namespace testing;
278293
294 auto connection = std::make_shared<MirConnection>(conf);
295
279 EXPECT_CALL(*mock_channel, connect(_,_))296 EXPECT_CALL(*mock_channel, connect(_,_))
280 .WillOnce(Invoke(fill_display_configuration));297 .WillOnce(Invoke(fill_display_configuration));
281298
@@ -315,6 +332,8 @@
315{332{
316 using namespace testing;333 using namespace testing;
317334
335 auto connection = std::make_shared<MirConnection>(conf);
336
318 EXPECT_CALL(*mock_channel, connect(_,_))337 EXPECT_CALL(*mock_channel, connect(_,_))
319 .WillOnce(Invoke(fill_display_configuration));338 .WillOnce(Invoke(fill_display_configuration));
320339
@@ -360,6 +379,8 @@
360{379{
361 using namespace testing;380 using namespace testing;
362381
382 auto connection = std::make_shared<MirConnection>(conf);
383
363 EXPECT_CALL(*mock_channel, connect(_,_))384 EXPECT_CALL(*mock_channel, connect(_,_))
364 .WillOnce(Invoke(fill_display_configuration));385 .WillOnce(Invoke(fill_display_configuration));
365386
@@ -381,6 +402,8 @@
381{402{
382 using namespace testing;403 using namespace testing;
383404
405 auto connection = std::make_shared<MirConnection>(conf);
406
384 EXPECT_CALL(*mock_channel, connect(_,_))407 EXPECT_CALL(*mock_channel, connect(_,_))
385 .WillOnce(Invoke(fill_display_configuration));408 .WillOnce(Invoke(fill_display_configuration));
386409
@@ -418,6 +441,8 @@
418{441{
419 using namespace testing;442 using namespace testing;
420443
444 auto connection = std::make_shared<MirConnection>(conf);
445
421 EXPECT_CALL(*mock_channel, connect(_,_))446 EXPECT_CALL(*mock_channel, connect(_,_))
422 .WillOnce(Invoke(fill_surface_pixel_formats));447 .WillOnce(Invoke(fill_surface_pixel_formats));
423 MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest",448 MirWaitHandle* wait_handle = connection->connect("MirClientSurfaceTest",
@@ -441,6 +466,8 @@
441{466{
442 using namespace testing;467 using namespace testing;
443468
469 auto connection = std::make_shared<MirConnection>(conf);
470
444 EXPECT_CALL(*mock_channel, connect(_,_))471 EXPECT_CALL(*mock_channel, connect(_,_))
445 .WillOnce(Invoke(fill_display_configuration));472 .WillOnce(Invoke(fill_display_configuration));
446473
@@ -504,6 +531,8 @@
504{531{
505 using namespace testing;532 using namespace testing;
506533
534 auto connection = std::make_shared<MirConnection>(conf);
535
507 MirSurfaceParameters params;536 MirSurfaceParameters params;
508 params.name = __PRETTY_FUNCTION__;537 params.name = __PRETTY_FUNCTION__;
509538
@@ -542,6 +571,8 @@
542{571{
543 using namespace testing;572 using namespace testing;
544573
574 auto connection = std::make_shared<MirConnection>(conf);
575
545 MirSurfaceParameters params;576 MirSurfaceParameters params;
546 params.name = __PRETTY_FUNCTION__;577 params.name = __PRETTY_FUNCTION__;
547578
@@ -593,6 +624,8 @@
593 std::vector<int> const initial_data{0x66, 0x67, 0x68};624 std::vector<int> const initial_data{0x66, 0x67, 0x68};
594 std::vector<int> const extra_data{0x11, 0x12, 0x13};625 std::vector<int> const extra_data{0x11, 0x12, 0x13};
595626
627 auto connection = std::make_shared<MirConnection>(conf);
628
596 EXPECT_CALL(*mock_channel, connect(_,_))629 EXPECT_CALL(*mock_channel, connect(_,_))
597 .WillOnce(FillPlatformDataWith(initial_data));630 .WillOnce(FillPlatformDataWith(initial_data));
598631
@@ -622,3 +655,73 @@
622 for (size_t i = 0; i < extra_data.size(); i++)655 for (size_t i = 0; i < extra_data.size(); i++)
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;
624}657}
658
659TEST_F(MirConnectionTest, dispatch_works_with_automatic_dispatch)
660{
661 using namespace testing;
662
663 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
664 mir::test::Pipe mock_epoll;
665 auto dispatched = std::make_shared<mir::test::Signal>();
666
667 ON_CALL(*channel, watch_fd()).WillByDefault(Return(mir::Fd{mock_epoll.read_fd()}));
668 ON_CALL(*channel, dispatch())
669 .WillByDefault(Invoke([dispatched]() { dispatched->raise(); }));
670
671 auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
672
673 int dummy{0};
674 EXPECT_EQ(sizeof(dummy), write(mock_epoll.write_fd(), &dummy, sizeof(dummy)));
675
676 EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
677}
678
679TEST_F(MirConnectionTest, manual_dispatch_is_not_automatically_dispatched)
680{
681 using namespace testing;
682
683 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
684
685 mir::test::Pipe mock_epoll;
686 int dummy{0};
687 EXPECT_EQ(sizeof(dummy), write(mock_epoll.write_fd(), &dummy, sizeof(dummy)));
688
689 auto dispatched = std::make_shared<mir::test::Signal>();
690
691 EXPECT_CALL(*channel, watch_fd()).Times(0);
692 ON_CALL(*channel, dispatch())
693 .WillByDefault(Invoke([dispatched]() { dispatched->raise(); }));
694
695 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
696
697 EXPECT_FALSE(dispatched->wait_for(std::chrono::seconds{1}));
698}
699
700TEST_F(MirConnectionTest, returns_invalid_watch_fd_when_using_automatic_dispatch)
701{
702 using namespace testing;
703
704 auto connection = std::make_shared<MirConnection>(conf, DispatchType::automatic);
705
706 EXPECT_THAT(connection->watch_fd(), Lt(0));
707}
708
709TEST_F(MirConnectionTest, returns_pollable_watch_fd_when_using_manual_dispatch)
710{
711 using namespace testing;
712
713 // MirConnection will need a valid fd
714 auto channel = std::dynamic_pointer_cast<MockRpcChannel>(conf.the_rpc_channel());
715
716 mir::test::Pipe mock_epoll;
717 ON_CALL(*channel, watch_fd()).WillByDefault(Return(mir::Fd{mock_epoll.read_fd()}));
718
719 auto connection = std::make_shared<MirConnection>(conf, DispatchType::manual);
720
721 EXPECT_THAT(connection->watch_fd(), Gt(0));
722
723 pollfd fd_readable;
724 fd_readable.events = POLLIN;
725 fd_readable.fd = connection->watch_fd();
726 EXPECT_TRUE(mir::test::std_call_succeeded(poll(&fd_readable, 1, 0)));
727}
625728
=== modified file 'tests/unit-tests/client/test_protobuf_rpc_channel.cpp'
--- tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2014-11-27 09:00:52 +0000
+++ tests/unit-tests/client/test_protobuf_rpc_channel.cpp 2014-12-04 01:50:56 +0000
@@ -108,6 +108,8 @@
108 MOCK_METHOD2(receive_data, void(void*, size_t));108 MOCK_METHOD2(receive_data, void(void*, size_t));
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>&));
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&));
111 MOCK_CONST_METHOD0(watch_fd, mir::Fd());
112 MOCK_METHOD0(dispatch, void());
111113
112 // Transport interface114 // Transport interface
113 void register_observer_default(std::shared_ptr<Observer> const& observer)115 void register_observer_default(std::shared_ptr<Observer> const& observer)
114116
=== added file 'tests/unit-tests/client/test_simple_rpc_thread.cpp'
--- tests/unit-tests/client/test_simple_rpc_thread.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/client/test_simple_rpc_thread.cpp 2014-12-04 01:50:56 +0000
@@ -0,0 +1,105 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "src/client/rpc/simple_rpc_thread.h"
20#include "src/client/rpc/dispatchable.h"
21#include "mir/fd.h"
22#include "mir_test/pipe.h"
23#include "mir_test/signal.h"
24
25#include <fcntl.h>
26
27#include <atomic>
28
29#include <gtest/gtest.h>
30#include <gmock/gmock.h>
31
32namespace mclr = mir::client::rpc;
33namespace mt = mir::test;
34
35namespace
36{
37class SimpleRPCThreadTest : public ::testing::Test
38{
39public:
40 SimpleRPCThreadTest()
41 {
42 watch_fd = mir::Fd{pipe.read_fd()};
43 test_fd = mir::Fd{pipe.write_fd()};
44 fcntl(watch_fd, F_SETFL, O_NONBLOCK);
45 }
46
47 mir::Fd watch_fd;
48 mir::Fd test_fd;
49private:
50 mt::Pipe pipe;
51};
52
53class MockDispatchable : public mclr::Dispatchable
54{
55public:
56 MOCK_CONST_METHOD0(watch_fd, mir::Fd());
57 MOCK_METHOD0(dispatch, void());
58};
59
60}
61
62TEST_F(SimpleRPCThreadTest, CallsDispatchWhenFdIsReadable)
63{
64 using namespace testing;
65
66 auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>();
67 ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(watch_fd));
68
69 auto dispatched = std::make_shared<mt::Signal>();
70 ON_CALL(*dispatchable, dispatch()).WillByDefault(Invoke([dispatched]() { dispatched->raise(); }));
71
72 mclr::SimpleRpcThread dispatcher{dispatchable};
73
74 uint64_t dummy{0xdeadbeef};
75 EXPECT_EQ(sizeof(dummy), write(test_fd, &dummy, sizeof(dummy)));
76
77 EXPECT_TRUE(dispatched->wait_for(std::chrono::seconds{1}));
78}
79
80TEST_F(SimpleRPCThreadTest, StopsCallingDispatchOnceFdIsNotReadable)
81{
82 using namespace testing;
83
84 uint64_t dummy{0xdeadbeef};
85 std::atomic<int> dispatch_count{0};
86
87 auto dispatchable = std::make_shared<NiceMock<MockDispatchable>>();
88 ON_CALL(*dispatchable, watch_fd()).WillByDefault(Return(watch_fd));
89
90 auto dispatched = std::make_shared<mt::Signal>();
91 ON_CALL(*dispatchable, dispatch()).WillByDefault(Invoke([this, &dispatch_count]()
92 {
93 decltype(dummy) buffer;
94 dispatch_count++;
95 read(this->watch_fd, &buffer, sizeof(buffer));
96 }));
97
98 mclr::SimpleRpcThread dispatcher{dispatchable};
99
100 EXPECT_EQ(sizeof(dummy), write(test_fd, &dummy, sizeof(dummy)));
101
102 std::this_thread::sleep_for(std::chrono::seconds{1});
103
104 EXPECT_EQ(1, dispatch_count);
105}
0106
=== modified file 'tests/unit-tests/client/test_stream_transport.cpp'
--- tests/unit-tests/client/test_stream_transport.cpp 2014-11-27 09:00:52 +0000
+++ tests/unit-tests/client/test_stream_transport.cpp 2014-12-04 01:50:56 +0000
@@ -22,6 +22,7 @@
2222
23#include "mir_test/auto_unblock_thread.h"23#include "mir_test/auto_unblock_thread.h"
24#include "mir_test/signal.h"24#include "mir_test/signal.h"
25#include "mir_test/fd_utils.h"
25#include "mir/raii.h"26#include "mir/raii.h"
2627
27#include <sys/socket.h>28#include <sys/socket.h>
@@ -43,6 +44,7 @@
43#include <gmock/gmock.h>44#include <gmock/gmock.h>
4445
45namespace mclr = mir::client::rpc;46namespace mclr = mir::client::rpc;
47namespace mt = mir::test;
4648
47namespace49namespace
48{50{
@@ -71,12 +73,6 @@
71 transport = std::make_shared<TransportMechanism>(transport_fd);73 transport = std::make_shared<TransportMechanism>(transport_fd);
72 }74 }
7375
74 virtual ~StreamTransportTest()
75 {
76 // We don't care about errors, so unconditionally close the test fd.
77 close(test_fd);
78 }
79
80 mir::Fd transport_fd;76 mir::Fd transport_fd;
81 mir::Fd test_fd;77 mir::Fd test_fd;
82 std::shared_ptr<TransportMechanism> transport;78 std::shared_ptr<TransportMechanism> transport;
@@ -85,51 +81,201 @@
85typedef ::testing::Types<mclr::StreamSocketTransport> Transports;81typedef ::testing::Types<mclr::StreamSocketTransport> Transports;
86TYPED_TEST_CASE(StreamTransportTest, Transports);82TYPED_TEST_CASE(StreamTransportTest, Transports);
8783
84TYPED_TEST(StreamTransportTest, ReturnsValidWatchFd)
85{
86 // A valid fd is >= 0, and we know that stdin, stdout, and stderr aren't correct.
87 EXPECT_GE(this->transport->watch_fd(), 3);
88}
89
90TYPED_TEST(StreamTransportTest, WatchFdIsPollable)
91{
92 pollfd socket_readable;
93 socket_readable.events = POLLIN;
94 socket_readable.fd = this->transport->watch_fd();
95
96 ASSERT_TRUE(mt::std_call_succeeded(poll(&socket_readable, 1, 0)));
97
98 EXPECT_FALSE(socket_readable.revents & POLLERR);
99 EXPECT_FALSE(socket_readable.revents & POLLNVAL);
100}
101
102TYPED_TEST(StreamTransportTest, WatchFdNotifiesReadableWhenDataPending)
103{
104 uint64_t dummy{0xdeadbeef};
105 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
106
107 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
108}
109
110TYPED_TEST(StreamTransportTest, WatchFdRemainsUnreadableUntilEventPending)
111{
112 EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
113
114 uint64_t dummy{0xdeadbeef};
115 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
116
117 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
118}
119
120TYPED_TEST(StreamTransportTest, WatchFdIsNoLongerReadableAfterEventProcessing)
121{
122 using namespace testing;
123
124 uint64_t dummy{0xdeadbeef};
125
126 auto observer = std::make_shared<NiceMock<MockObserver>>();
127
128 ON_CALL(*observer, on_data_available())
129 .WillByDefault(Invoke([dummy, this]()
130 {
131 decltype(dummy) buffer;
132 this->transport->receive_data(&buffer, sizeof(dummy));
133 }));
134
135 this->transport->register_observer(observer);
136
137 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
138
139 ASSERT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
140
141 this->transport->dispatch();
142
143 EXPECT_FALSE(mt::fd_is_readable(this->test_fd));
144}
145
146TYPED_TEST(StreamTransportTest, NoEventsDispatchedUntilDispatchCalled)
147{
148 using namespace testing;
149
150 auto observer = std::make_shared<NiceMock<MockObserver>>();
151 bool data_available{false};
152 bool disconnected{false};
153
154 uint64_t dummy{0xdeadbeef};
155 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
156 ::close(this->test_fd);
157
158 ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]()
159 {
160 decltype(dummy) buffer;
161 this->transport->receive_data(&buffer, sizeof(buffer));
162 data_available = true;
163 }));
164 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]()
165 { disconnected = true; }));
166
167 this->transport->register_observer(observer);
168
169 std::this_thread::sleep_for(std::chrono::seconds{1});
170 EXPECT_FALSE(data_available);
171 EXPECT_FALSE(disconnected);
172
173 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
174 while (mt::fd_is_readable(this->transport->watch_fd()))
175 {
176 this->transport->dispatch();
177 }
178
179 EXPECT_TRUE(data_available);
180 EXPECT_TRUE(disconnected);
181}
182
183TYPED_TEST(StreamTransportTest, DispatchesSingleEventAtATime)
184{
185 using namespace testing;
186
187 auto observer = std::make_shared<NiceMock<MockObserver>>();
188 bool data_available{false};
189 bool disconnected{false};
190
191 uint64_t dummy{0xdeadbeef};
192 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
193 ::close(this->test_fd);
194
195 ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([this, dummy, &data_available]()
196 {
197 decltype(dummy) buffer;
198 this->transport->receive_data(&buffer, sizeof(buffer));
199 data_available = true;
200 }));
201 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]()
202 { disconnected = true; }));
203
204 this->transport->register_observer(observer);
205
206 EXPECT_FALSE(data_available);
207 EXPECT_FALSE(disconnected);
208
209 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
210
211 this->transport->dispatch();
212
213 EXPECT_TRUE(data_available xor disconnected);
214
215 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
216
217 this->transport->dispatch();
218
219 EXPECT_TRUE(data_available);
220 EXPECT_TRUE(disconnected);
221}
222
88TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnect)223TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnect)
89{224{
90 using namespace testing;225 using namespace testing;
91 auto observer = std::make_shared<NiceMock<MockObserver>>();226 auto observer = std::make_shared<NiceMock<MockObserver>>();
92 auto done = std::make_shared<mir::test::Signal>();227 bool disconnected{false};
93228
94 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([done]()229 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]()
95 { done->raise(); }));230 { disconnected = true; }));
96231
97 this->transport->register_observer(observer);232 this->transport->register_observer(observer);
98233
99 close(this->test_fd);234 close(this->test_fd);
100235
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}));
237 while (mt::fd_is_readable(this->transport->watch_fd()))
238 {
239 this->transport->dispatch();
240 }
241
242 EXPECT_TRUE(disconnected);
102}243}
103244
104TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnectWhileReadingInIOLoop)245TYPED_TEST(StreamTransportTest, NoticesRemoteDisconnectWhileReading)
105{246{
106 using namespace testing;247 using namespace testing;
107 auto observer = std::make_shared<NiceMock<MockObserver>>();248 auto observer = std::make_shared<NiceMock<MockObserver>>();
108 auto done = std::make_shared<mir::test::Signal>();249 bool disconnected{false};
109 bool data_notified{false};250 bool receive_error_detected{false};
110 bool finished_read{false};251
111252 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]()
112 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([done]()253 { disconnected = true; }));
113 { done->raise(); }));
114 ON_CALL(*observer, on_data_available())
115 .WillByDefault(Invoke([this, &data_notified, &finished_read]()
116 {
117 data_notified = true;
118 char buffer[8];
119 this->transport->receive_data(buffer, sizeof(buffer));
120 finished_read = true;
121 }));
122
123 this->transport->register_observer(observer);254 this->transport->register_observer(observer);
124255
125 uint32_t dummy{0xdeadbeef};256 mir::test::AutoJoinThread closer{[this]()
126 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));257 {
127258 std::this_thread::sleep_for(std::chrono::seconds{1});
128 close(this->test_fd);259 ::close(this->test_fd);
129260 }};
130 EXPECT_TRUE(done->wait_for(std::chrono::seconds{1}));261
131 EXPECT_TRUE(data_notified);262 try
132 EXPECT_FALSE(finished_read);263 {
264 char buffer[8];
265 this->transport->receive_data(buffer, sizeof(buffer));
266 }
267 catch (std::runtime_error)
268 {
269 receive_error_detected = true;
270 }
271
272 // There should now be a disconnect event pending...
273 EXPECT_TRUE(mt::fd_is_readable(this->transport->watch_fd()));
274
275 this->transport->dispatch();
276
277 EXPECT_TRUE(disconnected);
278 EXPECT_TRUE(receive_error_detected);
133}279}
134280
135TYPED_TEST(StreamTransportTest, NotifiesOnDataAvailable)281TYPED_TEST(StreamTransportTest, NotifiesOnDataAvailable)
@@ -137,39 +283,21 @@
137 using namespace testing;283 using namespace testing;
138284
139 auto observer = std::make_shared<NiceMock<MockObserver>>();285 auto observer = std::make_shared<NiceMock<MockObserver>>();
140 auto done = std::make_shared<mir::test::Signal>();286 bool notified_data_available{false};
141287
142 ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([done]()288 ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([&notified_data_available]()
143 { done->raise(); }));289 { notified_data_available = true; }));
144290
145 this->transport->register_observer(observer);291 this->transport->register_observer(observer);
146292
147 uint64_t dummy{0xdeadbeef};293 uint64_t dummy{0xdeadbeef};
148 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));294 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
149295
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}));
151}297
152298 this->transport->dispatch();
153TYPED_TEST(StreamTransportTest, DoesntNotifyUntilDataAvailable)299
154{300 EXPECT_TRUE(notified_data_available);
155 using namespace testing;
156
157 auto observer = std::make_shared<NiceMock<MockObserver>>();
158 auto done = std::make_shared<mir::test::Signal>();
159
160 ON_CALL(*observer, on_data_available()).WillByDefault(Invoke([done]()
161 { done->raise(); }));
162
163 this->transport->register_observer(observer);
164
165 std::this_thread::sleep_for(std::chrono::seconds{1});
166
167 EXPECT_FALSE(done->raised());
168
169 uint64_t dummy{0xdeadbeef};
170 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
171
172 EXPECT_TRUE(done->wait_for(std::chrono::seconds{1}));
173}301}
174302
175TYPED_TEST(StreamTransportTest, KeepsNotifyingOfAvailableDataUntilAllIsRead)303TYPED_TEST(StreamTransportTest, KeepsNotifyingOfAvailableDataUntilAllIsRead)
@@ -177,98 +305,104 @@
177 using namespace testing;305 using namespace testing;
178306
179 auto observer = std::make_shared<NiceMock<MockObserver>>();307 auto observer = std::make_shared<NiceMock<MockObserver>>();
180 auto done = std::make_shared<mir::test::Signal>();
181308
182 std::array<uint8_t, sizeof(int) * 256> data;309 std::array<uint8_t, sizeof(int) * 256> data;
183 data.fill(0);310 data.fill(0);
184 std::atomic<size_t> bytes_left{data.size()};311 size_t bytes_left{data.size()};
185312
186 ON_CALL(*observer, on_data_available())313 ON_CALL(*observer, on_data_available())
187 .WillByDefault(Invoke([done, &bytes_left, this]()314 .WillByDefault(Invoke([&bytes_left, this]()
188 {315 {
189 int dummy;316 int dummy;
190 this->transport->receive_data(&dummy, sizeof(dummy));317 this->transport->receive_data(&dummy, sizeof(dummy));
191 bytes_left.fetch_sub(sizeof(dummy));318 bytes_left -= sizeof(dummy);
192 if (bytes_left.load() == 0)
193 {
194 done->raise();
195 }
196 }));319 }));
197320
198 this->transport->register_observer(observer);321 this->transport->register_observer(observer);
199322
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()));
201324
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}));
203 EXPECT_EQ(0, bytes_left.load());326 while (mt::fd_is_readable(this->transport->watch_fd()))
327 {
328 this->transport->dispatch();
329 }
330
331 EXPECT_EQ(0, bytes_left);
204}332}
205333
206TYPED_TEST(StreamTransportTest, StopsNotifyingOnceAllDataIsRead)334TYPED_TEST(StreamTransportTest, StopsNotifyingOnceAllDataIsRead)
207{335{
208 using namespace testing;336 using namespace testing;
209 int const buffer_size{256};
210337
211 auto observer = std::make_shared<NiceMock<MockObserver>>();338 auto observer = std::make_shared<NiceMock<MockObserver>>();
212 auto done = std::make_shared<mir::test::Signal>();339
340 std::array<uint8_t, sizeof(int) * 256> data;
341 data.fill(0);
342 size_t bytes_left{data.size()};
213343
214 ON_CALL(*observer, on_data_available())344 ON_CALL(*observer, on_data_available())
215 .WillByDefault(Invoke([this, done]()345 .WillByDefault(Invoke([&bytes_left, this]()
216 {346 {
217 if (done->raised())347 int dummy;
218 {348 this->transport->receive_data(&dummy, sizeof(dummy));
219 FAIL() << "on_data_available called without new data available";349 bytes_left -= sizeof(dummy);
220 }
221 uint8_t dummy_buffer[buffer_size];
222 this->transport->receive_data(dummy_buffer, sizeof(dummy_buffer));
223 done->raise();
224 }));350 }));
351
225 this->transport->register_observer(observer);352 this->transport->register_observer(observer);
226353
227 EXPECT_FALSE(done->raised());354 EXPECT_EQ(data.size(), write(this->test_fd, data.data(), data.size()));
228 uint8_t dummy_buffer[buffer_size];355
229 memset(dummy_buffer, 0xab, sizeof(dummy_buffer));356 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
230 EXPECT_EQ(sizeof(dummy_buffer), write(this->test_fd, dummy_buffer, sizeof(dummy_buffer)));357 while (bytes_left > 0)
231358 {
232 EXPECT_TRUE(done->wait_for(std::chrono::seconds{1}));359 this->transport->dispatch();
233360 }
234 std::this_thread::sleep_for(std::chrono::seconds{1});361
362 EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
235}363}
236364
237TYPED_TEST(StreamTransportTest, DoesntSendDataAvailableNotificationOnDisconnect)365TYPED_TEST(StreamTransportTest, DoesntSendDataAvailableNotificationOnDisconnect)
238{366{
239 using namespace testing;367 using namespace testing;
240 int const buffer_size{256};
241368
242 auto observer = std::make_shared<NiceMock<MockObserver>>();369 auto observer = std::make_shared<NiceMock<MockObserver>>();
243 auto read_done = std::make_shared<mir::test::Signal>();370 int notify_count{0};
244 auto disconnect_done = std::make_shared<mir::test::Signal>();371 bool disconnected{false};
245 std::atomic<int> notify_count{0};372
246373 uint64_t dummy{0xdeedfaac};
374 EXPECT_EQ(sizeof(dummy), write(this->test_fd, &dummy, sizeof(dummy)));
375
376 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([&disconnected]()
377 { disconnected = true; }));
247 ON_CALL(*observer, on_data_available())378 ON_CALL(*observer, on_data_available())
248 .WillByDefault(Invoke([this, read_done, &notify_count]()379 .WillByDefault(Invoke([dummy, &notify_count, this]()
249 {380 {
250 notify_count++;381 notify_count++;
251 uint8_t dummy_buffer[buffer_size];382
252 this->transport->receive_data(dummy_buffer, sizeof(dummy_buffer));383 decltype(dummy) buffer;
253 read_done->raise();384 this->transport->receive_data(&buffer, sizeof(buffer));
254 }));385 }));
255 ON_CALL(*observer, on_disconnected()).WillByDefault(Invoke([this, disconnect_done]()
256 { disconnect_done->raise(); }));
257386
258 this->transport->register_observer(observer);387 this->transport->register_observer(observer);
259388
260 EXPECT_FALSE(read_done->raised());389 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
261 uint8_t dummy_buffer[buffer_size];390 while (mt::fd_is_readable(this->transport->watch_fd()))
262 memset(dummy_buffer, 0xab, sizeof(dummy_buffer));391 {
263 EXPECT_EQ(sizeof(dummy_buffer), write(this->test_fd, dummy_buffer, sizeof(dummy_buffer)));392 this->transport->dispatch();
264393 }
265 EXPECT_TRUE(read_done->wait_for(std::chrono::seconds{1}));
266 EXPECT_EQ(1, notify_count);
267394
268 ::close(this->test_fd);395 ::close(this->test_fd);
269 EXPECT_TRUE(disconnect_done->wait_for(std::chrono::seconds{1}));396
270397 EXPECT_TRUE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
398 while (mt::fd_is_readable(this->transport->watch_fd()))
399 {
400 this->transport->dispatch();
401 }
402
403 EXPECT_FALSE(mt::fd_becomes_readable(this->transport->watch_fd(), std::chrono::seconds{1}));
271 EXPECT_EQ(1, notify_count);404 EXPECT_EQ(1, notify_count);
405 EXPECT_TRUE(disconnected);
272}406}
273407
274TYPED_TEST(StreamTransportTest, ReadsCorrectData)408TYPED_TEST(StreamTransportTest, ReadsCorrectData)
@@ -276,23 +410,26 @@
276 using namespace testing;410 using namespace testing;
277411
278 auto observer = std::make_shared<NiceMock<MockObserver>>();412 auto observer = std::make_shared<NiceMock<MockObserver>>();
279 auto done = std::make_shared<mir::test::Signal>();
280413
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"};
282 std::vector<char> received(expected.size());415 std::vector<char> received(expected.size());
283416
284 ON_CALL(*observer, on_data_available())417 ON_CALL(*observer, on_data_available())
285 .WillByDefault(Invoke([done, &received, this]()418 .WillByDefault(Invoke([&received, this]()
286 {419 {
287 this->transport->receive_data(received.data(), received.size());420 this->transport->receive_data(received.data(), received.size());
288 done->raise();
289 }));421 }));
290422
291 this->transport->register_observer(observer);423 this->transport->register_observer(observer);
292424
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()));
294426
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}));
428 while (mt::fd_is_readable(this->transport->watch_fd()))
429 {
430 this->transport->dispatch();
431 }
432
296 EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size()));433 EXPECT_EQ(0, memcmp(expected.data(), received.data(), expected.size()));
297}434}
298435

Subscribers

People subscribed via source and target branches