~ubuntu-branches/ubuntu/vivid/mir/vivid

« back to all changes in this revision

Viewing changes to tests/unit-tests/client/test_protobuf_rpc_channel.cpp

  • Committer: Package Import Robot
  • Author(s): Ubuntu daily release
  • Date: 2014-08-11 19:52:06 UTC
  • mto: This revision was merged to the branch mainline in revision 77.
  • Revision ID: package-import@ubuntu.com-20140811195206-05ee991qbzvdbmel
Tags: upstream-0.6.0+14.10.20140811
ImportĀ upstreamĀ versionĀ 0.6.0+14.10.20140811

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/mir_protobuf_rpc_channel.h"
 
20
#include "src/client/rpc/stream_transport.h"
 
21
#include "src/client/surface_map.h"
 
22
#include "src/client/display_configuration.h"
 
23
#include "src/client/rpc/null_rpc_report.h"
 
24
#include "src/client/lifecycle_control.h"
 
25
 
 
26
#include "mir_protobuf.pb.h"
 
27
#include "mir_protobuf_wire.pb.h"
 
28
 
 
29
#include "mir_test_doubles/null_client_event_sink.h"
 
30
 
 
31
#include <list>
 
32
#include <endian.h>
 
33
 
 
34
#include <google/protobuf/descriptor.h>
 
35
 
 
36
#include <gtest/gtest.h>
 
37
#include <gmock/gmock.h>
 
38
 
 
39
namespace mcl = mir::client;
 
40
namespace mclr = mir::client::rpc;
 
41
namespace mtd = mir::test::doubles;
 
42
 
 
43
namespace
 
44
{
 
45
 
 
46
class StubSurfaceMap : public mcl::SurfaceMap
 
47
{
 
48
public:
 
49
    void with_surface_do(int /*surface_id*/, std::function<void(MirSurface*)> /*exec*/) const
 
50
    {
 
51
    }
 
52
};
 
53
 
 
54
class MockStreamTransport : public mclr::StreamTransport
 
55
{
 
56
public:
 
57
    MockStreamTransport()
 
58
    {
 
59
        using namespace testing;
 
60
        ON_CALL(*this, register_observer(_))
 
61
            .WillByDefault(Invoke(std::bind(&MockStreamTransport::register_observer_default,
 
62
                                            this, std::placeholders::_1)));
 
63
        ON_CALL(*this, receive_data(_,_))
 
64
            .WillByDefault(Invoke([this](void* buffer, size_t message_size)
 
65
        {
 
66
            receive_data_default(buffer, message_size);
 
67
        }));
 
68
 
 
69
        ON_CALL(*this, receive_data(_,_,_))
 
70
            .WillByDefault(Invoke([this](void* buffer, size_t message_size, std::vector<int>& fds)
 
71
        {
 
72
            receive_data_default(buffer, message_size, fds);
 
73
        }));
 
74
 
 
75
        ON_CALL(*this, send_data(_))
 
76
            .WillByDefault(Invoke(std::bind(&MockStreamTransport::send_data_default,
 
77
                                            this, std::placeholders::_1)));
 
78
    }
 
79
 
 
80
    void add_server_message(std::vector<uint8_t> const& message)
 
81
    {
 
82
        received_data.insert(received_data.end(), message.begin(), message.end());
 
83
    }
 
84
    void add_server_message(std::vector<uint8_t> const& message, std::initializer_list<int> fds)
 
85
    {
 
86
        add_server_message(message);
 
87
        received_fds.insert(received_fds.end(), fds);
 
88
    }
 
89
 
 
90
    bool all_data_consumed() const
 
91
    {
 
92
        return received_data.empty() && received_fds.empty();
 
93
    }
 
94
 
 
95
    void notify_data_received()
 
96
    {
 
97
        do
 
98
        {
 
99
            for(auto& observer : observers)
 
100
                observer->on_data_available();
 
101
        }
 
102
        while (!all_data_consumed());
 
103
    }
 
104
 
 
105
    MOCK_METHOD1(register_observer, void(std::shared_ptr<Observer> const&));
 
106
    MOCK_METHOD2(receive_data, void(void*, size_t));
 
107
    MOCK_METHOD3(receive_data, void(void*, size_t, std::vector<int>&));
 
108
    MOCK_METHOD1(send_data, void(std::vector<uint8_t> const&));
 
109
 
 
110
    // Transport interface
 
111
    void register_observer_default(std::shared_ptr<Observer> const& observer)
 
112
    {
 
113
        observers.push_back(observer);
 
114
    }
 
115
 
 
116
    void receive_data_default(void* buffer, size_t read_bytes)
 
117
    {
 
118
        static std::vector<int> dummy;
 
119
        receive_data_default(buffer, read_bytes, dummy);
 
120
    }
 
121
 
 
122
    void receive_data_default(void* buffer, size_t read_bytes, std::vector<int>& fds)
 
123
    {
 
124
        auto num_fds = fds.size();
 
125
        if (read_bytes > received_data.size())
 
126
        {
 
127
            throw std::runtime_error("Attempt to read more data than is available");
 
128
        }
 
129
        if (num_fds > received_fds.size())
 
130
        {
 
131
            throw std::runtime_error("Attempt to receive more fds than are available");
 
132
        }
 
133
 
 
134
        memcpy(buffer, received_data.data(), read_bytes);
 
135
        fds.assign(received_fds.begin(), received_fds.begin() + num_fds);
 
136
 
 
137
        received_data.erase(received_data.begin(), received_data.begin() + read_bytes);
 
138
        received_fds.erase(received_fds.begin(), received_fds.begin() + num_fds);
 
139
    }
 
140
 
 
141
    void send_data_default(std::vector<uint8_t> const& buffer)
 
142
    {
 
143
        sent_messages.push_back(buffer);
 
144
    }
 
145
 
 
146
    std::list<std::shared_ptr<Observer>> observers;
 
147
 
 
148
    size_t read_offset{0};
 
149
    std::vector<uint8_t> received_data;
 
150
    std::vector<int> received_fds;
 
151
    std::list<std::vector<uint8_t>> sent_messages;
 
152
};
 
153
 
 
154
class MirProtobufRpcChannelTest : public testing::Test
 
155
{
 
156
public:
 
157
    MirProtobufRpcChannelTest()
 
158
        : transport{new testing::NiceMock<MockStreamTransport>},
 
159
          lifecycle{std::make_shared<mcl::LifecycleControl>()},
 
160
          channel{new mclr::MirProtobufRpcChannel{
 
161
                  std::unique_ptr<MockStreamTransport>{transport},
 
162
                  std::make_shared<StubSurfaceMap>(),
 
163
                  std::make_shared<mcl::DisplayConfiguration>(),
 
164
                  std::make_shared<mclr::NullRpcReport>(),
 
165
                  lifecycle,
 
166
                  std::make_shared<mtd::NullClientEventSink>()}}
 
167
    {
 
168
    }
 
169
 
 
170
    MockStreamTransport* transport;
 
171
    std::shared_ptr<mcl::LifecycleControl> lifecycle;
 
172
    std::shared_ptr<::google::protobuf::RpcChannel> channel;
 
173
};
 
174
 
 
175
}
 
176
 
 
177
TEST_F(MirProtobufRpcChannelTest, ReadsFullMessages)
 
178
{
 
179
    std::vector<uint8_t> empty_message(sizeof(uint16_t));
 
180
    std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
 
181
    std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
 
182
 
 
183
    *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
 
184
    *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
 
185
    *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
 
186
 
 
187
    transport->add_server_message(empty_message);
 
188
    transport->notify_data_received();
 
189
    EXPECT_TRUE(transport->all_data_consumed());
 
190
 
 
191
    transport->add_server_message(small_message);
 
192
    transport->notify_data_received();
 
193
    EXPECT_TRUE(transport->all_data_consumed());
 
194
 
 
195
    transport->add_server_message(large_message);
 
196
    transport->notify_data_received();
 
197
    EXPECT_TRUE(transport->all_data_consumed());
 
198
}
 
199
 
 
200
TEST_F(MirProtobufRpcChannelTest, ReadsAllQueuedMessages)
 
201
{
 
202
    std::vector<uint8_t> empty_message(sizeof(uint16_t));
 
203
    std::vector<uint8_t> small_message(sizeof(uint16_t) + 8);
 
204
    std::vector<uint8_t> large_message(sizeof(uint16_t) + 4096);
 
205
 
 
206
    *reinterpret_cast<uint16_t*>(empty_message.data()) = htobe16(0);
 
207
    *reinterpret_cast<uint16_t*>(small_message.data()) = htobe16(8);
 
208
    *reinterpret_cast<uint16_t*>(large_message.data()) = htobe16(4096);
 
209
 
 
210
    transport->add_server_message(empty_message);
 
211
    transport->add_server_message(small_message);
 
212
    transport->add_server_message(large_message);
 
213
 
 
214
    transport->notify_data_received();
 
215
    EXPECT_TRUE(transport->all_data_consumed());
 
216
}
 
217
 
 
218
TEST_F(MirProtobufRpcChannelTest, SendsMessagesAtomically)
 
219
{
 
220
    mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
 
221
    mir::protobuf::ConnectParameters message;
 
222
    message.set_application_name("I'm a little teapot!");
 
223
 
 
224
    channel_user.connect(nullptr, &message, nullptr, nullptr);
 
225
 
 
226
    EXPECT_EQ(transport->sent_messages.size(), 1);
 
227
}
 
228
 
 
229
TEST_F(MirProtobufRpcChannelTest, SetsCorrectSizeWhenSendingMessage)
 
230
{
 
231
    mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
 
232
    mir::protobuf::ConnectParameters message;
 
233
    message.set_application_name("I'm a little teapot!");
 
234
 
 
235
    channel_user.connect(nullptr, &message, nullptr, nullptr);
 
236
 
 
237
    uint16_t message_header = *reinterpret_cast<uint16_t*>(transport->sent_messages.front().data());
 
238
    message_header = be16toh(message_header);
 
239
    EXPECT_EQ(transport->sent_messages.front().size() - sizeof(uint16_t), message_header);
 
240
}
 
241
 
 
242
TEST_F(MirProtobufRpcChannelTest, ReadsFds)
 
243
{
 
244
    mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
 
245
    mir::protobuf::Buffer reply;
 
246
    mir::protobuf::SurfaceId request;
 
247
 
 
248
    channel_user.next_buffer(nullptr, &request, &reply, google::protobuf::NewCallback([](){}));
 
249
 
 
250
    std::initializer_list<int> fds = {2, 3, 5};
 
251
 
 
252
    ASSERT_EQ(transport->sent_messages.size(), 1);
 
253
    {
 
254
        mir::protobuf::Buffer reply_message;
 
255
 
 
256
        for (auto fd : fds)
 
257
            reply_message.add_fd(fd);
 
258
        reply_message.set_fds_on_side_channel(fds.size());
 
259
 
 
260
        mir::protobuf::wire::Invocation request;
 
261
        mir::protobuf::wire::Result reply;
 
262
 
 
263
        request.ParseFromArray(transport->sent_messages.front().data() + sizeof(uint16_t),
 
264
                               transport->sent_messages.front().size() - sizeof(uint16_t));
 
265
 
 
266
        reply.set_id(request.id());
 
267
        reply.set_response(reply_message.SerializeAsString());
 
268
 
 
269
        ASSERT_TRUE(reply.has_id());
 
270
        ASSERT_TRUE(reply.has_response());
 
271
 
 
272
        std::vector<uint8_t> buffer(reply.ByteSize() + sizeof(uint16_t));
 
273
        *reinterpret_cast<uint16_t*>(buffer.data()) = htobe16(reply.ByteSize());
 
274
        ASSERT_TRUE(reply.SerializeToArray(buffer.data() + sizeof(uint16_t), buffer.size() - sizeof(uint16_t)));
 
275
 
 
276
        transport->add_server_message(buffer);
 
277
 
 
278
        // Because our protocol is a bit silly...
 
279
        std::vector<uint8_t> dummy = {1};
 
280
        transport->add_server_message(dummy, fds);
 
281
 
 
282
        transport->notify_data_received();
 
283
    }
 
284
 
 
285
    ASSERT_EQ(reply.fd_size(), fds.size());
 
286
    int i = 0;
 
287
    for (auto fd : fds)
 
288
    {
 
289
        EXPECT_EQ(reply.fd(i), fd);
 
290
        ++i;
 
291
    }
 
292
}
 
293
 
 
294
TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnWriteError)
 
295
{
 
296
    using namespace ::testing;
 
297
 
 
298
    bool disconnected{false};
 
299
 
 
300
    lifecycle->set_lifecycle_event_handler([&disconnected](MirLifecycleState state)
 
301
    {
 
302
        if (state == mir_lifecycle_connection_lost)
 
303
        {
 
304
            disconnected = true;
 
305
        }
 
306
    });
 
307
 
 
308
    EXPECT_CALL(*transport, send_data(_))
 
309
        .WillOnce(Throw(std::runtime_error("Eaten by giant space goat")));
 
310
 
 
311
    mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
 
312
    mir::protobuf::Buffer reply;
 
313
    mir::protobuf::SurfaceId request;
 
314
 
 
315
    EXPECT_THROW(
 
316
        channel_user.next_buffer(nullptr, &request, &reply, google::protobuf::NewCallback([](){})),
 
317
        std::runtime_error);
 
318
 
 
319
    EXPECT_TRUE(disconnected);
 
320
}
 
321
 
 
322
TEST_F(MirProtobufRpcChannelTest, ForwardsDisconnectNotification)
 
323
{
 
324
    using namespace ::testing;
 
325
 
 
326
    bool disconnected{false};
 
327
 
 
328
    lifecycle->set_lifecycle_event_handler([&disconnected](MirLifecycleState state)
 
329
    {
 
330
        if (state == mir_lifecycle_connection_lost)
 
331
        {
 
332
            disconnected = true;
 
333
        }
 
334
    });
 
335
 
 
336
    for(auto& observer : transport->observers)
 
337
    {
 
338
        observer->on_disconnected();
 
339
    }
 
340
 
 
341
    EXPECT_TRUE(disconnected);
 
342
}
 
343
 
 
344
TEST_F(MirProtobufRpcChannelTest, NotifiesOfDisconnectOnlyOnce)
 
345
{
 
346
    using namespace ::testing;
 
347
 
 
348
    bool disconnected{false};
 
349
 
 
350
    lifecycle->set_lifecycle_event_handler([&disconnected](MirLifecycleState state)
 
351
    {
 
352
        if (state == mir_lifecycle_connection_lost)
 
353
        {
 
354
            if (disconnected)
 
355
            {
 
356
                FAIL()<<"Received disconnected message twice";
 
357
            }
 
358
            disconnected = true;
 
359
        }
 
360
    });
 
361
 
 
362
    EXPECT_CALL(*transport, send_data(_))
 
363
        .WillOnce(DoAll(Throw(std::runtime_error("Eaten by giant space goat")),
 
364
                        InvokeWithoutArgs([this]()
 
365
    {
 
366
        for(auto& observer : transport->observers)
 
367
        {
 
368
            observer->on_disconnected();
 
369
        }
 
370
    })));
 
371
 
 
372
    mir::protobuf::DisplayServer::Stub channel_user{channel.get(), mir::protobuf::DisplayServer::STUB_DOESNT_OWN_CHANNEL};
 
373
    mir::protobuf::Buffer reply;
 
374
    mir::protobuf::SurfaceId request;
 
375
 
 
376
    EXPECT_THROW(
 
377
        channel_user.next_buffer(nullptr, &request, &reply, google::protobuf::NewCallback([](){})),
 
378
        std::runtime_error);
 
379
 
 
380
    EXPECT_TRUE(disconnected);
 
381
}