2
* Copyright Ā© 2014 Canonical Ltd.
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.
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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
19
#include "mir_toolkit/mir_client_library.h"
20
#include "mir_toolkit/debug/surface.h"
22
#include "src/include/client/mir/client_platform_factory.h"
23
#include "src/include/client/mir/client_platform.h"
24
#include "src/include/client/mir/client_buffer_factory.h"
26
#include "mir/test/validity_matchers.h"
28
#include "mir_test_framework/headless_in_process_server.h"
29
#include "mir_test_framework/using_stub_client_platform.h"
30
#include "mir_test_framework/stub_client_connection_configuration.h"
31
#include "mir_test_framework/any_surface.h"
32
#include "mir/test/doubles/stub_client_buffer_factory.h"
34
#include <gtest/gtest.h>
35
#include <gmock/gmock.h>
38
#include <boost/throw_exception.hpp>
41
namespace mcl = mir::client;
42
namespace mtf = mir_test_framework;
43
namespace mtd = mir::test::doubles;
47
enum Method : uint64_t
50
the_client_platform_factory = 1<<0,
51
create_client_platform = 1<<1,
52
create_egl_native_window = 1<<2,
53
create_buffer_factory = 1<<3
56
std::string const exception_text{"Ducks!"};
58
template<Method name, Method failure_set>
61
return (name & failure_set);
64
template<Method failure_set>
65
class ConfigurableFailurePlatform : public mir::client::ClientPlatform
67
std::shared_ptr<EGLNativeWindowType> create_egl_native_window(mir::client::EGLNativeSurface *)
69
if (should_fail<Method::create_egl_native_window, failure_set>())
71
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
73
return std::shared_ptr<EGLNativeWindowType>{};
76
void populate(MirPlatformPackage&) const override
80
MirPlatformMessage* platform_operation(MirPlatformMessage const*) override
85
MirPlatformType platform_type() const
87
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
88
return MirPlatformType::mir_platform_type_gbm;
90
std::shared_ptr<mir::client::ClientBufferFactory> create_buffer_factory()
92
if (should_fail<Method::create_buffer_factory, failure_set>())
94
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
96
return std::make_shared<mtd::StubClientBufferFactory>();
98
std::shared_ptr<EGLNativeDisplayType> create_egl_native_display()
100
return std::shared_ptr<EGLNativeDisplayType>{};
102
MirNativeBuffer *convert_native_buffer(mir::graphics::NativeBuffer*) const
104
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
107
MirPixelFormat get_egl_pixel_format(EGLDisplay, EGLConfig) const override
109
return mir_pixel_format_invalid;
113
template<Method failure_set>
114
class ConfigurableFailureFactory: public mir::client::ClientPlatformFactory
116
std::shared_ptr<mir::client::ClientPlatform>
117
create_client_platform(mir::client::ClientContext* /*context*/) override
119
if (should_fail<Method::create_client_platform, failure_set>())
121
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
123
return std::make_shared<ConfigurableFailurePlatform<failure_set>>();
127
template<Method failure_set>
128
class ConfigurableFailureConfiguration : public mtf::StubConnectionConfiguration
130
using mtf::StubConnectionConfiguration::StubConnectionConfiguration;
132
std::shared_ptr<mir::client::ClientPlatformFactory> the_client_platform_factory() override
134
if (should_fail<Method::the_client_platform_factory, failure_set>())
136
BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
138
return std::make_shared<ConfigurableFailureFactory<failure_set>>();
142
struct ClientLibraryErrors : mtf::HeadlessInProcessServer
144
ClientLibraryErrors() { add_to_environment("MIR_SERVER_ENABLE_INPUT","off"); }
149
TEST_F(ClientLibraryErrors, exception_in_client_configuration_constructor_generates_error)
151
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::the_client_platform_factory>> stubby;
153
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
155
EXPECT_FALSE(mir_connection_is_valid(connection));
156
EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text));
157
mir_connection_release(connection);
160
TEST_F(ClientLibraryErrors, exception_in_platform_construction_generates_error)
162
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_client_platform>> stubby;
164
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
166
EXPECT_FALSE(mir_connection_is_valid(connection));
167
EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text));
168
mir_connection_release(connection);
171
TEST_F(ClientLibraryErrors, connecting_to_garbage_socket_returns_appropriate_error)
173
using namespace testing;
174
mtf::UsingStubClientPlatform stubby;
176
auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__);
177
ASSERT_THAT(connection, NotNull());
179
char const* error = mir_connection_get_error_message(connection);
181
if (std::strcmp("connect: No such file or directory", error) &&
182
std::strcmp("Can't find MIR server", error) &&
183
!std::strstr(error, "Failed to connect to server socket"))
187
mir_connection_release(connection);
190
TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure)
192
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_buffer_factory>> stubby;
194
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
196
ASSERT_THAT(connection, IsValid());
198
auto surface = mtf::make_any_surface(connection);
199
ASSERT_NE(surface, nullptr);
200
EXPECT_FALSE(mir_surface_is_valid(surface));
201
EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
203
mir_surface_release_sync(surface);
204
mir_connection_release(connection);
209
void recording_surface_callback(MirSurface*, void* ctx)
211
auto called = static_cast<bool*>(ctx);
216
TEST_F(ClientLibraryErrors, surface_release_on_error_object_still_calls_callback)
218
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_buffer_factory>> stubby;
220
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
222
ASSERT_THAT(connection, IsValid());
224
auto surface = mtf::make_any_surface(connection);
226
ASSERT_NE(surface, nullptr);
227
EXPECT_FALSE(mir_surface_is_valid(surface));
228
EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
230
bool callback_called{false};
231
mir_surface_release(surface, &recording_surface_callback, &callback_called);
232
EXPECT_TRUE(callback_called);
233
mir_connection_release(connection);
237
TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure_in_reply_processing)
239
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_egl_native_window>> stubby;
241
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
243
ASSERT_THAT(connection, IsValid());
245
auto surface = mtf::make_any_surface(connection);
246
ASSERT_NE(surface, nullptr);
247
EXPECT_FALSE(mir_surface_is_valid(surface));
248
EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
250
mir_surface_release_sync(surface);
251
mir_connection_release(connection);
254
TEST_F(ClientLibraryErrors, passing_invalid_parent_id_to_surface_create)
256
using namespace testing;
258
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
260
ASSERT_THAT(connection, IsValid());
262
// An ID that parses as valid, but doesn't correspond to any
263
auto invalid_id = mir_persistent_id_from_string("05f223a2-39e5-48b9-9416-b0ce837351b6");
265
auto spec = mir_connection_create_spec_for_input_method(connection,
267
mir_pixel_format_argb_8888);
274
mir_surface_spec_attach_to_foreign_parent(spec, invalid_id, &rect, mir_edge_attachment_any);
276
auto surface = mir_surface_create_sync(spec);
277
EXPECT_THAT(surface, Not(IsValid()));
278
EXPECT_THAT(mir_surface_get_error_message(surface), MatchesRegex(".*Lookup.*failed.*"));
280
mir_persistent_id_release(invalid_id);
281
mir_surface_spec_release(spec);
282
mir_surface_release_sync(surface);
283
mir_connection_release(connection);
286
using ClientLibraryErrorsDeathTest = ClientLibraryErrors;
289
TEST_F(ClientLibraryErrorsDeathTest, creating_surface_on_garbage_connection_is_fatal)
291
mtf::UsingStubClientPlatform stubby;
293
auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__);
295
ASSERT_FALSE(mir_connection_is_valid(connection));
297
mtf::make_any_surface(connection), "");
299
mir_connection_release(connection);
303
TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_malconstructed_connection_is_fatal)
305
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::the_client_platform_factory>> stubby;
307
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
309
ASSERT_FALSE(mir_connection_is_valid(connection));
310
EXPECT_DEATH(mtf::make_any_surface(connection), "");
312
mir_connection_release(connection);
315
TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_invalid_connection_is_fatal)
317
mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_client_platform>> stubby;
319
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
321
ASSERT_FALSE(mir_connection_is_valid(connection));
322
EXPECT_DEATH(mtf::make_any_surface(connection), "");
324
mir_connection_release(connection);
327
TEST_F(ClientLibraryErrorsDeathTest, surface_spec_attaching_invalid_parent_id)
329
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
331
auto spec = mir_connection_create_spec_for_input_method(connection, 100, 100, mir_pixel_format_argb_8888);
339
EXPECT_DEATH(mir_surface_spec_attach_to_foreign_parent(spec, nullptr, &rect, mir_edge_attachment_any), "");
341
mir_connection_release(connection);
344
TEST_F(ClientLibraryErrorsDeathTest, surface_spec_attaching_invalid_rectangle)
346
auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
348
auto spec = mir_connection_create_spec_for_input_method(connection, 100, 100, mir_pixel_format_argb_8888);
350
auto id = mir_persistent_id_from_string("fa69b2e9-d507-4005-be61-5068f40a5aec");
352
EXPECT_DEATH(mir_surface_spec_attach_to_foreign_parent(spec, id, nullptr, mir_edge_attachment_any), "");
354
mir_persistent_id_release(id);
355
mir_connection_release(connection);