2
* Copyright © 2014 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3,
6
* as published by the Free Software Foundation.
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.
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/>.
16
* Authored by: Alan Griffiths <alan@octopull.co.uk>
19
#include "mir_toolkit/mir_client_library.h"
21
#include "src/server/frontend/session_mediator.h"
23
#include "mir/frontend/connection_context.h"
24
#include "mir/scene/session.h"
25
#include "mir/graphics/graphic_buffer_allocator.h"
27
#include "mir_test_framework/stubbed_server_configuration.h"
28
#include "mir_test_framework/basic_client_server_fixture.h"
29
#include "mir_test_doubles/fake_ipc_factory.h"
30
#include "mir_test/popen.h"
32
#include <gtest/gtest.h>
33
#include <gmock/gmock.h>
36
#include <condition_variable>
39
using namespace testing;
43
struct TrustedHelperSessionMediator : mir::frontend::SessionMediator
45
using mir::frontend::SessionMediator::SessionMediator;
47
std::function<void(std::shared_ptr<mir::frontend::Session> const&)> trusted_connect_handler() const override
49
return [this](std::shared_ptr<mir::frontend::Session> const& session)
51
auto const ss = std::dynamic_pointer_cast<mir::scene::Session>(session);
52
client_pid = ss->process_id();
56
pid_t mutable client_pid = 0;
59
std::shared_ptr<TrustedHelperSessionMediator> trusted_helper_mediator;
61
struct FakeIpcFactory : mir::test::doubles::FakeIpcFactory
63
using mir::test::doubles::FakeIpcFactory::FakeIpcFactory;
65
std::shared_ptr<mir::frontend::detail::DisplayServer> make_helper_mediator(
66
std::shared_ptr<mir::frontend::Shell> const& shell,
67
std::shared_ptr<mir::graphics::Platform> const& graphics_platform,
68
std::shared_ptr<mir::frontend::DisplayChanger> const& changer,
69
std::shared_ptr<mir::graphics::GraphicBufferAllocator> const& buffer_allocator,
70
std::shared_ptr<mir::frontend::SessionMediatorReport> const& sm_report,
71
std::shared_ptr<mir::frontend::EventSink> const& sink,
72
std::shared_ptr<mir::frontend::Screencast> const& effective_screencast,
73
mir::frontend::ConnectionContext const& connection_context)
75
trusted_helper_mediator = std::make_shared<TrustedHelperSessionMediator>(
79
buffer_allocator->supported_pixel_formats(),
86
return trusted_helper_mediator;
90
struct MyServerConfiguration : mir_test_framework::StubbedServerConfiguration
92
std::shared_ptr<FakeIpcFactory> mediator_factory;
95
std::shared_ptr<mir::frontend::Shell> const& shell,
96
std::shared_ptr<mir::graphics::GraphicBufferAllocator> const& allocator)
97
-> std::shared_ptr<mir::frontend::ProtobufIpcFactory> override
102
mediator_factory = std::make_shared<FakeIpcFactory>(
104
the_session_mediator_report(),
105
the_graphics_platform(),
106
the_frontend_display_changer(),
109
the_session_authorizer());
111
EXPECT_CALL(*mediator_factory,
112
make_mediator(_, _, _, _, _, _, _, _)).Times(AnyNumber())
113
.WillOnce(Invoke(mediator_factory.get(), &FakeIpcFactory::make_helper_mediator))
114
.WillRepeatedly(Invoke(mediator_factory.get(), &FakeIpcFactory::make_default_mediator));
116
return mediator_factory;
121
using MyBasicClientServerFixture = mir_test_framework::BasicClientServerFixture<MyServerConfiguration>;
123
struct TrustSessionHelper : MyBasicClientServerFixture
128
trusted_helper_mediator.reset();
129
MyBasicClientServerFixture::TearDown();
132
static std::size_t const arbritary_fd_request_count = 3;
135
std::size_t actual_fd_count = 0;
136
int actual_fds[arbritary_fd_request_count] = {};
137
std::condition_variable cv;
138
bool called_back = false;
140
bool wait_for_callback(std::chrono::milliseconds timeout)
142
std::unique_lock<decltype(mutex)> lock(mutex);
143
return cv.wait_for(lock, timeout, [this]{ return called_back; });
146
char client_connect_string[128] = {0};
148
char const* fd_connect_string(int fd)
150
sprintf(client_connect_string, "fd://%d", fd);
151
return client_connect_string;
154
MOCK_METHOD1(process_line, void(std::string const&));
157
void client_fd_callback(MirConnection*, size_t count, int const* fds, void* context)
159
auto const self = static_cast<TrustSessionHelper*>(context);
161
std::unique_lock<decltype(self->mutex)> lock(self->mutex);
162
self->actual_fd_count = count;
164
std::copy(fds, fds+count, self->actual_fds);
165
self->called_back = true;
166
self->cv.notify_one();
170
TEST_F(TrustSessionHelper, can_get_fds_for_trusted_clients)
172
mir_connection_new_fds_for_trusted_clients(connection, arbritary_fd_request_count, &client_fd_callback, this);
173
EXPECT_TRUE(wait_for_callback(std::chrono::milliseconds(500)));
175
EXPECT_THAT(actual_fd_count, Eq(arbritary_fd_request_count));
178
TEST_F(TrustSessionHelper, gets_pid_when_trusted_client_connects_over_fd)
180
mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this);
181
wait_for_callback(std::chrono::milliseconds(500));
183
auto const expected_pid = getpid();
185
EXPECT_THAT(trusted_helper_mediator->client_pid, Eq(0)); // Before the client connects we don't have a pid
186
auto client_connection = mir_connect_sync(fd_connect_string(actual_fds[0]), __PRETTY_FUNCTION__);
187
EXPECT_THAT(trusted_helper_mediator->client_pid, Eq(expected_pid)); // After the client connects we do have a pid
189
mir_connection_release(client_connection);
192
// TODO we need a nice way to run this (and similar tests that require a separate client process) in CI
193
// Disabled as we can't be sure the mir_demo_client_basic is about
194
TEST_F(TrustSessionHelper, DISABLED_client_pid_is_associated_with_session)
196
auto const server_pid = getpid();
198
mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this);
199
wait_for_callback(std::chrono::milliseconds(500));
202
EXPECT_CALL(*this, process_line(StrEq("Starting")));
203
EXPECT_CALL(*this, process_line(StrEq("Connected")));
204
EXPECT_CALL(*this, process_line(StrEq("Surface created")));
205
EXPECT_CALL(*this, process_line(StrEq("Surface released")));
206
EXPECT_CALL(*this, process_line(StrEq("Connection released")));
208
mir::test::Popen output(std::string("bin/mir_demo_client_basic -m ") + fd_connect_string(actual_fds[0]));
211
while (output.get_line(line)) process_line(line);
213
EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(0));
214
EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(server_pid));