Mir

Merge lp:~raof/mir/a-little-more-error-checkin into lp:mir

Proposed by Chris Halse Rogers
Status: Merged
Approved by: Alberto Aguirre
Approved revision: no longer in the source branch.
Merged at revision: 2039
Proposed branch: lp:~raof/mir/a-little-more-error-checkin
Merge into: lp:mir
Diff against target: 1196 lines (+659/-157)
18 files modified
src/client/mir_connection.cpp (+8/-0)
src/client/mir_connection_api.cpp (+12/-4)
src/client/mir_connection_api.h (+5/-1)
src/client/mir_surface.cpp (+45/-12)
src/client/mir_surface.h (+2/-0)
src/client/mir_surface_api.cpp (+7/-3)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_client_library.cpp (+0/-38)
tests/acceptance-tests/test_client_library_errors.cpp (+338/-0)
tests/acceptance-tests/test_server_disconnect.cpp (+4/-1)
tests/include/mir_test/stub_server_tool.h (+8/-1)
tests/include/mir_test/validity_matchers.h (+64/-0)
tests/include/mir_test_framework/using_stub_client_platform.h (+61/-17)
tests/integration-tests/client/test_client_render.cpp (+7/-1)
tests/integration-tests/test_error_reporting.cpp (+49/-34)
tests/mir_test/CMakeLists.txt (+1/-0)
tests/mir_test/validity_matchers.cpp (+32/-0)
tests/mir_test_framework/using_stub_client_platform.cpp (+15/-45)
To merge this branch: bzr merge lp:~raof/mir/a-little-more-error-checkin
Reviewer Review Type Date Requested Status
Alberto Aguirre (community) Approve
Alan Griffiths Approve
PS Jenkins bot (community) continuous-integration Approve
Robert Carr (community) Needs Information
Review via email: mp+232508@code.launchpad.net

Commit message

Improve error checking and reporting for the client library.

Description of the change

Some more miscellaneous error checking niceties that would have been great to have while developing privatise-all-the-things.

This starts fleshing out the error handling policy I think we settled on in London - *create calls always return a non-null object, but passing error objects to any method other than *_is_valid or *_release is fatal.

Oh, and it also fixes UsingStubClientPlatform so that it does something :).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :

661, 672. Maybe use the new validity matchers here?

review: Needs Information
Revision history for this message
Chris Halse Rogers (raof) wrote :

Move to WIP - I can do this better with a real stub platform.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

538 +TEST_F(ClientLibraryErrors, CreateSurfaceReturnsErrorObjectOnFailureInReplyProcessing)

this_is_how_we_name_test_cases_around_here

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

Man, I hope gtest never does anything would break test_names_with_underscores_in_them, which the documentation explicitly tells you not to use.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

I am confused as to why “make test” doesn't catch these failures locally. Anyway, let's try this again.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

67 + return mir_connection_api_impl->connect(mir_connection_api_impl->configuration_factory(),

Looks horrible. But I don't have a better suggestion in mind.

review: Approve
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/mir_connection.cpp'
2--- src/client/mir_connection.cpp 2014-11-03 19:06:48 +0000
3+++ src/client/mir_connection.cpp 2014-11-05 22:26:46 +0000
4@@ -34,6 +34,8 @@
5 #include <unistd.h>
6 #include <signal.h>
7
8+#include <boost/exception/diagnostic_information.hpp>
9+
10 namespace mcl = mir::client;
11 namespace mircv = mir::input::receiver;
12 namespace gp = google::protobuf;
13@@ -219,6 +221,7 @@
14 void MirConnection::connected(mir_connected_callback callback, void * context)
15 {
16 bool safe_to_callback = true;
17+ try
18 {
19 std::lock_guard<decltype(mutex)> lock(mutex);
20
21@@ -262,6 +265,11 @@
22 display_configuration->set_configuration(connect_result.display_configuration());
23 lifecycle_control->set_lifecycle_event_handler(default_lifecycle_event_handler);
24 }
25+ catch (std::exception const& e)
26+ {
27+ connect_result.set_error(std::string{"Failed to process connect response: "} +
28+ boost::diagnostic_information(e));
29+ }
30
31 if (safe_to_callback) callback(this, context);
32 connect_wait_handle.result_received();
33
34=== modified file 'src/client/mir_connection_api.cpp'
35--- src/client/mir_connection_api.cpp 2014-10-01 06:25:56 +0000
36+++ src/client/mir_connection_api.cpp 2014-11-05 22:26:46 +0000
37@@ -53,6 +53,7 @@
38 {
39 public:
40 MirWaitHandle* connect(
41+ mcl::ConfigurationFactory configuration,
42 char const* socket_file,
43 char const* name,
44 mir_connected_callback callback,
45@@ -112,10 +113,13 @@
46 delete connection;
47 }
48
49- std::unique_ptr<mcl::ConnectionConfiguration> configuration(std::string const& socket) override
50+ mcl::ConfigurationFactory configuration_factory() override
51 {
52- return std::unique_ptr<mcl::ConnectionConfiguration>{
53- new mcl::DefaultConnectionConfiguration{socket}};
54+ return [](std::string const& socket) {
55+ return std::unique_ptr<mcl::ConnectionConfiguration>{
56+ new mcl::DefaultConnectionConfiguration{socket}
57+ };
58+ };
59 }
60 };
61
62@@ -132,7 +136,11 @@
63 {
64 try
65 {
66- return mir_connection_api_impl->connect(socket_file, name, callback, context);
67+ return mir_connection_api_impl->connect(mir_connection_api_impl->configuration_factory(),
68+ socket_file,
69+ name,
70+ callback,
71+ context);
72 }
73 catch (std::exception const&)
74 {
75
76=== modified file 'src/client/mir_connection_api.h'
77--- src/client/mir_connection_api.h 2014-10-01 06:25:56 +0000
78+++ src/client/mir_connection_api.h 2014-11-05 22:26:46 +0000
79@@ -23,6 +23,7 @@
80
81 #include <string>
82 #include <memory>
83+#include <functional>
84
85 namespace mir
86 {
87@@ -30,12 +31,15 @@
88 {
89 class ConnectionConfiguration;
90
91+using ConfigurationFactory = std::function<std::unique_ptr<ConnectionConfiguration>(std::string const&)>;
92+
93 class MirConnectionAPI
94 {
95 public:
96 virtual ~MirConnectionAPI() = default;
97
98 virtual MirWaitHandle* connect(
99+ ConfigurationFactory configuration,
100 char const* socket_file,
101 char const* name,
102 mir_connected_callback callback,
103@@ -43,7 +47,7 @@
104
105 virtual void release(MirConnection* connection) = 0;
106
107- virtual std::unique_ptr<ConnectionConfiguration> configuration(std::string const& socket) = 0;
108+ virtual ConfigurationFactory configuration_factory() = 0;
109
110 protected:
111 MirConnectionAPI() = default;
112
113=== modified file 'src/client/mir_surface.cpp'
114--- src/client/mir_surface.cpp 2014-10-28 22:48:39 +0000
115+++ src/client/mir_surface.cpp 2014-11-05 22:26:46 +0000
116@@ -30,6 +30,8 @@
117 #include <cassert>
118 #include <unistd.h>
119
120+#include <boost/exception/diagnostic_information.hpp>
121+
122 namespace geom = mir::geometry;
123 namespace mcl = mir::client;
124 namespace mircv = mir::input::receiver;
125@@ -39,11 +41,22 @@
126 namespace
127 {
128 void null_callback(MirSurface*, void*) {}
129+mp::DisplayServer::Stub null_server{nullptr};
130
131 std::mutex handle_mutex;
132 std::unordered_set<MirSurface*> valid_surfaces;
133 }
134
135+MirSurface::MirSurface(std::string const& error)
136+ : server{null_server},
137+ connection{nullptr}
138+{
139+ surface.set_error(error);
140+
141+ std::lock_guard<decltype(handle_mutex)> lock(handle_mutex);
142+ valid_surfaces.insert(this);
143+}
144+
145 MirSurface::MirSurface(
146 MirConnection *allocating_connection,
147 mp::DisplayServer::Stub & server,
148@@ -258,20 +271,29 @@
149 {
150 auto platform = connection->get_client_platform();
151
152+ try
153 {
154- std::lock_guard<decltype(mutex)> lock(mutex);
155-
156- process_incoming_buffer();
157- accelerated_window = platform->create_egl_native_window(this);
158-
159- for(int i = 0; i < surface.attributes_size(); i++)
160 {
161- auto const& attrib = surface.attributes(i);
162- attrib_cache[attrib.attrib()] = attrib.ivalue();
163+ std::lock_guard<decltype(mutex)> lock(mutex);
164+
165+ process_incoming_buffer();
166+ accelerated_window = platform->create_egl_native_window(this);
167+
168+ for(int i = 0; i < surface.attributes_size(); i++)
169+ {
170+ auto const& attrib = surface.attributes(i);
171+ attrib_cache[attrib.attrib()] = attrib.ivalue();
172+ }
173 }
174- }
175-
176- connection->on_surface_created(id(), this);
177+
178+ connection->on_surface_created(id(), this);
179+ }
180+ catch (std::exception const& error)
181+ {
182+ surface.set_error(std::string{"Error processing Surface creating response:"} +
183+ boost::diagnostic_information(error));
184+ }
185+
186 callback(this, context);
187 create_wait_handle.result_received();
188 }
189@@ -296,7 +318,18 @@
190 valid_surfaces.erase(this);
191 }
192
193- return connection->release_surface(this, callback, context);
194+ MirWaitHandle* wait_handle{nullptr};
195+ if (connection)
196+ {
197+ wait_handle = connection->release_surface(this, callback, context);
198+ }
199+ else
200+ {
201+ callback(this, context);
202+ delete this;
203+ }
204+
205+ return wait_handle;
206 }
207
208 MirNativeBuffer* MirSurface::get_current_buffer_package()
209
210=== modified file 'src/client/mir_surface.h'
211--- src/client/mir_surface.h 2014-10-27 22:31:16 +0000
212+++ src/client/mir_surface.h 2014-11-05 22:26:46 +0000
213@@ -59,6 +59,8 @@
214 MirSurface(MirSurface const &) = delete;
215 MirSurface& operator=(MirSurface const &) = delete;
216
217+ MirSurface(std::string const& error);
218+
219 MirSurface(
220 MirConnection *allocating_connection,
221 mir::protobuf::DisplayServer::Stub & server,
222
223=== modified file 'src/client/mir_surface_api.cpp'
224--- src/client/mir_surface_api.cpp 2014-10-27 22:31:16 +0000
225+++ src/client/mir_surface_api.cpp 2014-11-05 22:26:46 +0000
226@@ -23,6 +23,8 @@
227 #include "mir_surface.h"
228 #include "error_connections.h"
229
230+#include <boost/exception/diagnostic_information.hpp>
231+
232 namespace mcl = mir::client;
233
234 namespace
235@@ -43,15 +45,17 @@
236 mir_surface_callback callback,
237 void* context)
238 {
239- if (mcl::ErrorConnections::instance().contains(connection)) return 0;
240+ if (!mir_connection_is_valid(connection)) abort();
241
242 try
243 {
244 return connection->create_surface(*params, callback, context);
245 }
246- catch (std::exception const&)
247+ catch (std::exception const& error)
248 {
249- // TODO callback with an error surface
250+ auto error_surf = new MirSurface{std::string{"Failed to create surface: "} +
251+ boost::diagnostic_information(error)};
252+ (*callback)(error_surf, context);
253 return nullptr;
254 }
255 }
256
257=== modified file 'tests/acceptance-tests/CMakeLists.txt'
258--- tests/acceptance-tests/CMakeLists.txt 2014-11-03 19:06:48 +0000
259+++ tests/acceptance-tests/CMakeLists.txt 2014-11-05 22:26:46 +0000
260@@ -17,6 +17,7 @@
261 server_signal_handling.cpp
262 clients.cpp
263 test_client_library.cpp
264+ test_client_library_errors.cpp
265 test_client_library_old.cpp
266 test_client_surface_events.cpp
267 test_client_surface_swap_buffers.cpp
268
269=== modified file 'tests/acceptance-tests/test_client_library.cpp'
270--- tests/acceptance-tests/test_client_library.cpp 2014-10-29 10:11:50 +0000
271+++ tests/acceptance-tests/test_client_library.cpp 2014-11-05 22:26:46 +0000
272@@ -53,8 +53,6 @@
273 {
274 struct ClientLibrary : mtf::HeadlessInProcessServer
275 {
276- mtf::UsingStubClientPlatform using_stub_client_platform;
277-
278 std::set<MirSurface*> surfaces;
279 MirConnection* connection = nullptr;
280 MirSurface* surface = nullptr;
281@@ -597,42 +595,6 @@
282 mir_connection_release(connection);
283 }
284
285-TEST_F(ClientLibrary, connect_errors_handled)
286-{
287- mir_wait_for(mir_connect("garbage", __PRETTY_FUNCTION__, connection_callback, this));
288- ASSERT_THAT(connection, NotNull());
289-
290- char const* error = mir_connection_get_error_message(connection);
291-
292- if (std::strcmp("connect: No such file or directory", error) &&
293- std::strcmp("Can't find MIR server", error) &&
294- !std::strstr(error, "Failed to connect to server socket"))
295- {
296- FAIL() << error;
297- }
298-}
299-
300-TEST_F(ClientLibrary, connect_errors_dont_blow_up)
301-{
302- mir_wait_for(mir_connect("garbage", __PRETTY_FUNCTION__, connection_callback, this));
303-
304- MirSurfaceParameters const request_params =
305- {
306- __PRETTY_FUNCTION__,
307- 640, 480,
308- mir_pixel_format_abgr_8888,
309- mir_buffer_usage_hardware,
310- mir_display_output_id_invalid
311- };
312-
313- mir_wait_for(mir_connection_create_surface(connection, &request_params, create_surface_callback, this));
314-// TODO surface_create needs to fail safe too. After that is done we should add the following:
315-// TODO mir_wait_for(mir_surface_swap_buffers(surface, next_buffer_callback, this));
316-// TODO mir_wait_for(mir_surface_release( surface, release_surface_callback, this));
317-
318- mir_connection_release(connection);
319-}
320-
321 TEST_F(ClientLibrary, MultiSurfaceClientTracksBufferFdsCorrectly)
322 {
323 mir_wait_for(mir_connect(new_connection().c_str(), __PRETTY_FUNCTION__, connection_callback, this));
324
325=== added file 'tests/acceptance-tests/test_client_library_errors.cpp'
326--- tests/acceptance-tests/test_client_library_errors.cpp 1970-01-01 00:00:00 +0000
327+++ tests/acceptance-tests/test_client_library_errors.cpp 2014-11-05 22:26:46 +0000
328@@ -0,0 +1,338 @@
329+/*
330+ * Copyright © 2014 Canonical Ltd.
331+ *
332+ * This program is free software: you can redistribute it and/or modify
333+ * it under the terms of the GNU General Public License version 3 as
334+ * published by the Free Software Foundation.
335+ *
336+ * This program is distributed in the hope that it will be useful,
337+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
338+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
339+ * GNU General Public License for more details.
340+ *
341+ * You should have received a copy of the GNU General Public License
342+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
343+ *
344+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
345+ */
346+
347+#include "mir_toolkit/mir_client_library.h"
348+#include "mir_toolkit/debug/surface.h"
349+
350+#include "src/client/client_platform_factory.h"
351+#include "src/client/client_platform.h"
352+#include "src/client/client_buffer_factory.h"
353+
354+#include "mir_test/validity_matchers.h"
355+
356+#include "mir_test_framework/in_process_server.h"
357+#include "mir_test_framework/stubbed_server_configuration.h"
358+#include "mir_test_framework/using_stub_client_platform.h"
359+#include "mir_test_framework/stub_client_connection_configuration.h"
360+
361+#include <gtest/gtest.h>
362+#include <gmock/gmock.h>
363+
364+#include <stdexcept>
365+#include <boost/throw_exception.hpp>
366+#include <cstring>
367+
368+namespace mcl = mir::client;
369+namespace mtf = mir_test_framework;
370+
371+namespace
372+{
373+enum Method : uint64_t
374+{
375+ none = 0,
376+ the_client_platform_factory = 1<<0,
377+ create_client_platform = 1<<1,
378+ create_egl_native_window = 1<<2,
379+ create_buffer_factory = 1<<3
380+};
381+
382+std::string const exception_text{"Ducks!"};
383+
384+template<Method name, Method failure_set>
385+bool should_fail()
386+{
387+ return (name & failure_set);
388+}
389+
390+class StubClientBufferFactory : public mir::client::ClientBufferFactory
391+{
392+ std::shared_ptr<mir::client::ClientBuffer> create_buffer(const std::shared_ptr<MirBufferPackage>&,
393+ mir::geometry::Size,
394+ MirPixelFormat)
395+ {
396+ return std::shared_ptr<mir::client::ClientBuffer>{};
397+ }
398+};
399+
400+template<Method failure_set>
401+class ConfigurableFailurePlatform : public mir::client::ClientPlatform
402+{
403+ std::shared_ptr<EGLNativeWindowType> create_egl_native_window(mir::client::ClientSurface *)
404+ {
405+ if (should_fail<Method::create_egl_native_window, failure_set>())
406+ {
407+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
408+ }
409+ return std::shared_ptr<EGLNativeWindowType>{};
410+ }
411+ MirPlatformType platform_type() const
412+ {
413+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
414+ return MirPlatformType::mir_platform_type_gbm;
415+ }
416+ std::shared_ptr<mir::client::ClientBufferFactory> create_buffer_factory()
417+ {
418+ if (should_fail<Method::create_buffer_factory, failure_set>())
419+ {
420+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
421+ }
422+ return std::make_shared<StubClientBufferFactory>();
423+ }
424+ std::shared_ptr<EGLNativeDisplayType> create_egl_native_display()
425+ {
426+ return std::shared_ptr<EGLNativeDisplayType>{};
427+ }
428+ MirNativeBuffer *convert_native_buffer(mir::graphics::NativeBuffer*) const
429+ {
430+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
431+ return nullptr;
432+ }
433+};
434+
435+template<Method failure_set>
436+class ConfigurableFailureFactory: public mir::client::ClientPlatformFactory
437+{
438+ std::shared_ptr<mir::client::ClientPlatform>
439+ create_client_platform(mir::client::ClientContext* /*context*/) override
440+ {
441+ if (should_fail<Method::create_client_platform, failure_set>())
442+ {
443+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
444+ }
445+ return std::make_shared<ConfigurableFailurePlatform<failure_set>>();
446+ }
447+};
448+
449+template<Method failure_set>
450+class ConfigurableFailureConfiguration : public mtf::StubConnectionConfiguration
451+{
452+ using mtf::StubConnectionConfiguration::StubConnectionConfiguration;
453+
454+ std::shared_ptr<mir::client::ClientPlatformFactory> the_client_platform_factory() override
455+ {
456+ if (should_fail<Method::the_client_platform_factory, failure_set>())
457+ {
458+ BOOST_THROW_EXCEPTION(std::runtime_error{exception_text});
459+ }
460+ return std::make_shared<ConfigurableFailureFactory<failure_set>>();
461+ }
462+};
463+
464+class ClientLibraryErrors : public mtf::InProcessServer
465+{
466+private:
467+ mtf::StubbedServerConfiguration config;
468+
469+ mir::DefaultServerConfiguration &server_config() override
470+ {
471+ return config;
472+ }
473+};
474+}
475+
476+TEST_F(ClientLibraryErrors, exception_in_client_configuration_constructor_generates_error)
477+{
478+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::the_client_platform_factory>> stubby;
479+
480+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
481+
482+ EXPECT_FALSE(mir_connection_is_valid(connection));
483+ EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text));
484+ mir_connection_release(connection);
485+}
486+
487+TEST_F(ClientLibraryErrors, exception_in_platform_construction_generates_error)
488+{
489+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_client_platform>> stubby;
490+
491+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
492+
493+ EXPECT_FALSE(mir_connection_is_valid(connection));
494+ EXPECT_THAT(mir_connection_get_error_message(connection), testing::HasSubstr(exception_text));
495+ mir_connection_release(connection);
496+}
497+
498+TEST_F(ClientLibraryErrors, connecting_to_garbage_socket_returns_appropriate_error)
499+{
500+ using namespace testing;
501+ mtf::UsingStubClientPlatform stubby;
502+
503+ auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__);
504+ ASSERT_THAT(connection, NotNull());
505+
506+ char const* error = mir_connection_get_error_message(connection);
507+
508+ if (std::strcmp("connect: No such file or directory", error) &&
509+ std::strcmp("Can't find MIR server", error) &&
510+ !std::strstr(error, "Failed to connect to server socket"))
511+ {
512+ FAIL() << error;
513+ }
514+ mir_connection_release(connection);
515+}
516+
517+TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure)
518+{
519+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_buffer_factory>> stubby;
520+
521+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
522+
523+ ASSERT_THAT(connection, IsValid());
524+
525+ MirSurfaceParameters const request_params =
526+ {
527+ __PRETTY_FUNCTION__,
528+ 640, 480,
529+ mir_pixel_format_abgr_8888,
530+ mir_buffer_usage_hardware,
531+ mir_display_output_id_invalid
532+ };
533+
534+ auto surface = mir_connection_create_surface_sync(connection, &request_params);
535+ ASSERT_NE(surface, nullptr);
536+ EXPECT_FALSE(mir_surface_is_valid(surface));
537+ EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
538+
539+ mir_surface_release_sync(surface);
540+ mir_connection_release(connection);
541+}
542+
543+namespace
544+{
545+void recording_surface_callback(MirSurface*, void* ctx)
546+{
547+ auto called = static_cast<bool*>(ctx);
548+ *called = true;
549+}
550+}
551+
552+TEST_F(ClientLibraryErrors, surface_release_on_error_object_still_calls_callback)
553+{
554+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_buffer_factory>> stubby;
555+
556+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
557+
558+ ASSERT_THAT(connection, IsValid());
559+
560+ MirSurfaceParameters const request_params =
561+ {
562+ __PRETTY_FUNCTION__,
563+ 640, 480,
564+ mir_pixel_format_abgr_8888,
565+ mir_buffer_usage_hardware,
566+ mir_display_output_id_invalid
567+ };
568+
569+ auto surface = mir_connection_create_surface_sync(connection, &request_params);
570+ ASSERT_NE(surface, nullptr);
571+ EXPECT_FALSE(mir_surface_is_valid(surface));
572+ EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
573+
574+ bool callback_called{false};
575+ mir_surface_release(surface, &recording_surface_callback, &callback_called);
576+ EXPECT_TRUE(callback_called);
577+ mir_connection_release(connection);
578+}
579+
580+
581+TEST_F(ClientLibraryErrors, create_surface_returns_error_object_on_failure_in_reply_processing)
582+{
583+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_egl_native_window>> stubby;
584+
585+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
586+
587+ ASSERT_THAT(connection, IsValid());
588+
589+ MirSurfaceParameters const request_params =
590+ {
591+ __PRETTY_FUNCTION__,
592+ 640, 480,
593+ mir_pixel_format_abgr_8888,
594+ mir_buffer_usage_hardware,
595+ mir_display_output_id_invalid
596+ };
597+
598+ auto surface = mir_connection_create_surface_sync(connection, &request_params);
599+ ASSERT_NE(surface, nullptr);
600+ EXPECT_FALSE(mir_surface_is_valid(surface));
601+ EXPECT_THAT(mir_surface_get_error_message(surface), testing::HasSubstr(exception_text));
602+
603+ mir_surface_release_sync(surface);
604+ mir_connection_release(connection);
605+}
606+
607+using ClientLibraryErrorsDeathTest = ClientLibraryErrors;
608+
609+
610+TEST_F(ClientLibraryErrorsDeathTest, createing_surface_on_garbage_connection_is_fatal)
611+{
612+ mtf::UsingStubClientPlatform stubby;
613+
614+ auto connection = mir_connect_sync("garbage", __PRETTY_FUNCTION__);
615+
616+ MirSurfaceParameters const request_params =
617+ {
618+ __PRETTY_FUNCTION__,
619+ 640, 480,
620+ mir_pixel_format_abgr_8888,
621+ mir_buffer_usage_hardware,
622+ mir_display_output_id_invalid
623+ };
624+
625+ ASSERT_FALSE(mir_connection_is_valid(connection));
626+ EXPECT_DEATH(mir_connection_create_surface_sync(connection, &request_params), "");
627+}
628+
629+
630+TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_malconstructed_connection_is_fatal)
631+{
632+ MirSurfaceParameters const request_params =
633+ {
634+ __PRETTY_FUNCTION__,
635+ 640, 480,
636+ mir_pixel_format_abgr_8888,
637+ mir_buffer_usage_hardware,
638+ mir_display_output_id_invalid
639+ };
640+
641+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::the_client_platform_factory>> stubby;
642+
643+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
644+
645+ ASSERT_FALSE(mir_connection_is_valid(connection));
646+ EXPECT_DEATH(mir_connection_create_surface_sync(connection, &request_params), "");
647+}
648+
649+TEST_F(ClientLibraryErrorsDeathTest, creating_surface_synchronosly_on_invalid_connection_is_fatal)
650+{
651+ MirSurfaceParameters const request_params =
652+ {
653+ __PRETTY_FUNCTION__,
654+ 640, 480,
655+ mir_pixel_format_abgr_8888,
656+ mir_buffer_usage_hardware,
657+ mir_display_output_id_invalid
658+ };
659+
660+ mtf::UsingClientPlatform<ConfigurableFailureConfiguration<Method::create_client_platform>> stubby;
661+
662+ auto connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
663+
664+ ASSERT_FALSE(mir_connection_is_valid(connection));
665+ EXPECT_DEATH(mir_connection_create_surface_sync(connection, &request_params), "");
666+}
667
668=== modified file 'tests/acceptance-tests/test_server_disconnect.cpp'
669--- tests/acceptance-tests/test_server_disconnect.cpp 2014-10-01 06:25:56 +0000
670+++ tests/acceptance-tests/test_server_disconnect.cpp 2014-11-05 22:26:46 +0000
671@@ -107,7 +107,7 @@
672 mir_buffer_usage_hardware,
673 mir_display_output_id_invalid
674 };
675- mir_connection_create_surface_sync(connection, &parameters);
676+ surf = mir_connection_create_surface_sync(connection, &parameters);
677 });
678
679 configure_display.exec([&] {
680@@ -117,6 +117,7 @@
681 });
682
683 disconnect.exec([&] {
684+ mir_surface_release_sync(surf);
685 mir_connection_release(connection);
686 });
687 }
688@@ -129,6 +130,8 @@
689 mt::CrossProcessAction create_surface;
690 mt::CrossProcessAction configure_display;
691 mt::CrossProcessAction disconnect;
692+private:
693+ MirSurface* surf;
694 };
695
696 /*
697
698=== modified file 'tests/include/mir_test/stub_server_tool.h'
699--- tests/include/mir_test/stub_server_tool.h 2014-10-01 06:25:56 +0000
700+++ tests/include/mir_test/stub_server_tool.h 2014-11-05 22:26:46 +0000
701@@ -86,7 +86,14 @@
702 ::google::protobuf::Closure* done) override
703 {
704 app_name = request->application_name();
705- connect_msg->set_error("");
706+ // If you check out MirConnection::connected either the platform and display_configuration
707+ // have to be set or the error has to be set, otherwise we die and fail to callback.
708+ //
709+ // Since setting the error to "" means that mir_connection_is_valid will return false
710+ // with a confusing non-error-message, instead clear the error and set the platform et al.
711+ connect_msg->clear_error();
712+ connect_msg->set_allocated_platform(new mir::protobuf::Platform{});
713+ connect_msg->set_allocated_display_configuration(new mir::protobuf::DisplayConfiguration{});
714 done->Run();
715 }
716
717
718=== added file 'tests/include/mir_test/validity_matchers.h'
719--- tests/include/mir_test/validity_matchers.h 1970-01-01 00:00:00 +0000
720+++ tests/include/mir_test/validity_matchers.h 2014-11-05 22:26:46 +0000
721@@ -0,0 +1,64 @@
722+/*
723+ * Copyright © 2014 Canonical Ltd.
724+ *
725+ * This program is free software: you can redistribute it and/or modify it
726+ * under the terms of the GNU General Public License version 3,
727+ * as published by the Free Software Foundation.
728+ *
729+ * This program is distributed in the hope that it will be useful,
730+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
731+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
732+ * GNU General Public License for more details.
733+ *
734+ * You should have received a copy of the GNU General Public License
735+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
736+ *
737+ * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
738+ */
739+
740+#ifndef MIR_TEST_VALIDITY_MATCHERS_H_
741+#define MIR_TEST_VALIDITY_MATCHERS_H_
742+
743+#include <gmock/gmock.h>
744+
745+#include "mir_toolkit/mir_client_library.h"
746+
747+using ::testing::MakePolymorphicMatcher;
748+using ::testing::MatchResultListener;
749+using ::testing::NotNull;
750+using ::testing::PolymorphicMatcher;
751+
752+class IsValidMatcher {
753+public:
754+ // To implement a polymorphic matcher, first define a COPYABLE class
755+ // that has three members MatchAndExplain(), DescribeTo(), and
756+ // DescribeNegationTo(), like the following.
757+
758+ // In this example, we want to use NotNull() with any pointer, so
759+ // MatchAndExplain() accepts a pointer of any type as its first argument.
760+ // In general, you can define MatchAndExplain() as an ordinary method or
761+ // a method template, or even overload it.
762+ template <typename T>
763+ bool MatchAndExplain(T* p, MatchResultListener* listener) const;
764+
765+ // Describes the property of a value matching this matcher.
766+ void DescribeTo(::std::ostream* os) const { *os << "is valid"; }
767+
768+ // Describes the property of a value NOT matching this matcher.
769+ void DescribeNegationTo(::std::ostream* os) const { *os << "is not valid"; }
770+};
771+
772+template<>
773+bool IsValidMatcher::MatchAndExplain(MirConnection* connection, MatchResultListener* listener) const;
774+
775+template<>
776+bool IsValidMatcher::MatchAndExplain(MirSurface* surface, MatchResultListener* listener) const;
777+
778+// To construct a polymorphic matcher, pass an instance of the class
779+// to MakePolymorphicMatcher(). Note the return type.
780+inline PolymorphicMatcher<IsValidMatcher> IsValid()
781+{
782+ return MakePolymorphicMatcher(IsValidMatcher{});
783+}
784+
785+#endif // MIR_TEST_VALIDITY_MATCHERS_H_
786
787=== modified file 'tests/include/mir_test_framework/using_stub_client_platform.h'
788--- tests/include/mir_test_framework/using_stub_client_platform.h 2014-10-01 06:25:56 +0000
789+++ tests/include/mir_test_framework/using_stub_client_platform.h 2014-11-05 22:26:46 +0000
790@@ -20,32 +20,76 @@
791 #define MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_
792
793 #include <memory>
794-
795-namespace mir
796-{
797-namespace client
798-{
799-class MirConnectionAPI;
800-}
801-}
802+#include <functional>
803+
804+#include "mir_test_framework/stub_client_connection_configuration.h"
805+#include "src/client/mir_connection_api.h"
806+
807+namespace mcl = mir::client;
808
809 namespace mir_test_framework
810 {
811
812-class UsingStubClientPlatform
813-{
814-public:
815- UsingStubClientPlatform();
816- ~UsingStubClientPlatform();
817-
818-private:
819- UsingStubClientPlatform(UsingStubClientPlatform const&) = delete;
820- UsingStubClientPlatform operator=(UsingStubClientPlatform const&) = delete;
821+class StubMirConnectionAPI : public mcl::MirConnectionAPI
822+{
823+public:
824+ StubMirConnectionAPI(mcl::MirConnectionAPI* prev_api,
825+ mcl::ConfigurationFactory factory)
826+ : prev_api{prev_api},
827+ factory{factory}
828+ {
829+ }
830+
831+ MirWaitHandle* connect(
832+ mcl::ConfigurationFactory configuration,
833+ char const* socket_file,
834+ char const* name,
835+ mir_connected_callback callback,
836+ void* context) override;
837+
838+ void release(MirConnection* connection) override;
839+
840+ mcl::ConfigurationFactory configuration_factory() override;
841+
842+private:
843+ mcl::MirConnectionAPI* const prev_api;
844+ mcl::ConfigurationFactory factory;
845+};
846+
847+template<class ClientConfig>
848+class UsingClientPlatform
849+{
850+public:
851+ UsingClientPlatform()
852+ : prev_api{mir_connection_api_impl},
853+ stub_api{new StubMirConnectionAPI{prev_api, make_configuration_factory()}}
854+ {
855+ mir_connection_api_impl = stub_api.get();
856+ }
857+
858+ ~UsingClientPlatform()
859+ {
860+ mir_connection_api_impl = prev_api;
861+ }
862+
863+private:
864+ UsingClientPlatform(UsingClientPlatform const&) = delete;
865+ UsingClientPlatform operator=(UsingClientPlatform const&) = delete;
866
867 mir::client::MirConnectionAPI* prev_api;
868 std::unique_ptr<mir::client::MirConnectionAPI> stub_api;
869+
870+ mcl::ConfigurationFactory make_configuration_factory()
871+ {
872+ return [](std::string const& socket) {
873+ return std::unique_ptr<mir::client::ConnectionConfiguration>{
874+ new ClientConfig{socket}
875+ };
876+ };
877+ }
878 };
879
880+using UsingStubClientPlatform = UsingClientPlatform<StubConnectionConfiguration>;
881 }
882
883 #endif /* MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_ */
884
885=== modified file 'tests/integration-tests/client/test_client_render.cpp'
886--- tests/integration-tests/client/test_client_render.cpp 2014-10-30 18:37:11 +0000
887+++ tests/integration-tests/client/test_client_render.cpp 2014-11-05 22:26:46 +0000
888@@ -28,6 +28,7 @@
889 #include "examples/testdraw/patterns.h"
890 #include "mir_test/stub_server_tool.h"
891 #include "mir_test/test_protobuf_server.h"
892+#include "mir_test/validity_matchers.h"
893
894 #include "mir/frontend/connector.h"
895
896@@ -92,7 +93,9 @@
897 mir_buffer_usage_software, mir_display_output_id_invalid
898 };
899 auto connection = mir_connect_sync(socket_file, "test_renderer");
900+ EXPECT_THAT(connection, IsValid());
901 auto surface = mir_connection_create_surface_sync(connection, &surface_parameters);
902+ EXPECT_THAT(surface, IsValid());
903 MirGraphicsRegion graphics_region;
904 for(int i=0u; i < num_frames; i++)
905 {
906@@ -119,6 +122,7 @@
907 process_sync.wait_for_signal_ready_for();
908
909 auto connection = mir_connect_sync(socket_file, "test_renderer");
910+ EXPECT_THAT(connection, IsValid());
911
912 /* set up egl context */
913 int major, minor, n;
914@@ -137,7 +141,9 @@
915 eglInitialize(egl_display, &major, &minor);
916 eglChooseConfig(egl_display, attribs, &egl_config, 1, &n);
917
918- auto mir_surface = create_mir_surface(connection, egl_display, egl_config);
919+ auto mir_surface = create_mir_surface(connection, egl_display, egl_config);
920+ EXPECT_THAT(mir_surface, IsValid());
921+
922 auto native_window = static_cast<EGLNativeWindowType>(
923 mir_surface_get_egl_native_window(mir_surface));
924
925
926=== modified file 'tests/integration-tests/test_error_reporting.cpp'
927--- tests/integration-tests/test_error_reporting.cpp 2014-10-01 06:25:56 +0000
928+++ tests/integration-tests/test_error_reporting.cpp 2014-11-05 22:26:46 +0000
929@@ -24,6 +24,7 @@
930 #include "mir/frontend/connector.h"
931
932 #include "src/server/frontend/display_server.h"
933+#include "src/server/scene/surface_allocator.h"
934
935 #include "mir_test_framework/display_server_test_fixture.h"
936 #include "mir_test_doubles/stub_ipc_factory.h"
937@@ -34,6 +35,7 @@
938 #include <gmock/gmock.h>
939 #include <gtest/gtest.h>
940 #include "mir_test/gmock_fixes.h"
941+#include "mir_test/validity_matchers.h"
942
943 namespace mc = mir::compositor;
944 namespace mf = mir::frontend;
945@@ -45,32 +47,13 @@
946 namespace
947 {
948 char const* const mir_test_socket = mtf::test_socket_file().c_str();
949-
950-struct ErrorServer : mf::detail::DisplayServer
951+std::string const test_exception_text{"test exception text"};
952+
953+
954+struct ConnectionErrorServer : mf::detail::DisplayServer
955 {
956 void client_pid(int /*pid*/) override {}
957
958- static std::string const test_exception_text;
959-
960- void create_surface(
961- google::protobuf::RpcController*,
962- const mir::protobuf::SurfaceParameters*,
963- mir::protobuf::Surface*,
964- google::protobuf::Closure*)
965- {
966- throw std::runtime_error(test_exception_text);
967- }
968-
969- void release_surface(
970- google::protobuf::RpcController*,
971- const mir::protobuf::SurfaceId*,
972- mir::protobuf::Void*,
973- google::protobuf::Closure*)
974- {
975- throw std::runtime_error(test_exception_text);
976- }
977-
978-
979 void connect(
980 ::google::protobuf::RpcController*,
981 const ::mir::protobuf::ConnectParameters*,
982@@ -90,8 +73,6 @@
983 }
984 };
985
986-std::string const ErrorServer::test_exception_text{"test exception text"};
987-
988 struct SurfaceSync
989 {
990 SurfaceSync() :
991@@ -193,7 +174,7 @@
992
993 using ErrorReporting = BespokeDisplayServerTestFixture;
994
995-TEST_F(ErrorReporting, c_api_returns_error)
996+TEST_F(ErrorReporting, c_api_returns_connection_error)
997 {
998
999 struct ServerConfig : TestingServerConfiguration
1000@@ -201,7 +182,7 @@
1001 std::shared_ptr<mf::ProtobufIpcFactory> new_ipc_factory(
1002 std::shared_ptr<mf::SessionAuthorizer> const&) override
1003 {
1004- static auto error_server = std::make_shared<ErrorServer>();
1005+ static auto error_server = std::make_shared<ConnectionErrorServer>();
1006 return std::make_shared<mtd::StubIpcFactory>(*error_server);
1007 }
1008 } server_config;
1009@@ -218,7 +199,46 @@
1010
1011 ASSERT_TRUE(connection != NULL);
1012 EXPECT_FALSE(mir_connection_is_valid(connection));
1013- EXPECT_TRUE(std::strstr(mir_connection_get_error_message(connection), ErrorServer::test_exception_text.c_str()));
1014+ EXPECT_TRUE(std::strstr(mir_connection_get_error_message(connection), test_exception_text.c_str()));
1015+
1016+ mir_connection_release(connection);
1017+ }
1018+ } client_config;
1019+
1020+ launch_client_process(client_config);
1021+}
1022+
1023+TEST_F(ErrorReporting, c_api_returns_surface_creation_error)
1024+{
1025+
1026+ struct ServerConfig : TestingServerConfiguration
1027+ {
1028+ class BoobytrappedSurfaceFactory : public mir::scene::SurfaceFactory
1029+ {
1030+ std::shared_ptr<mir::scene::Surface>
1031+ create_surface(mir::scene::SurfaceCreationParameters const&) override
1032+ {
1033+ throw std::runtime_error{test_exception_text};
1034+ }
1035+ };
1036+
1037+ std::shared_ptr<mir::scene::SurfaceFactory> the_surface_factory() override
1038+ {
1039+ return std::make_shared<BoobytrappedSurfaceFactory>();
1040+ }
1041+ } server_config;
1042+
1043+ launch_server_process(server_config);
1044+
1045+ struct ClientConfig : ClientConfigCommon
1046+ {
1047+ void exec()
1048+ {
1049+ mir_connect(mir_test_socket, __PRETTY_FUNCTION__, connection_callback, this);
1050+
1051+ wait_for_connect();
1052+
1053+ ASSERT_THAT(connection, IsValid());
1054
1055 MirSurfaceParameters const request_params =
1056 {
1057@@ -235,12 +255,7 @@
1058
1059 ASSERT_TRUE(ssync->surface != NULL);
1060 EXPECT_FALSE(mir_surface_is_valid(ssync->surface));
1061- EXPECT_TRUE(std::strstr(mir_surface_get_error_message(ssync->surface), ErrorServer::test_exception_text.c_str()));
1062-
1063- EXPECT_NO_THROW({
1064- MirSurfaceParameters response_params;
1065- mir_surface_get_parameters(ssync->surface, &response_params);
1066- });
1067+ EXPECT_TRUE(std::strstr(mir_surface_get_error_message(ssync->surface), test_exception_text.c_str()));
1068
1069 mir_surface_release(ssync->surface, release_surface_callback, ssync);
1070
1071
1072=== modified file 'tests/mir_test/CMakeLists.txt'
1073--- tests/mir_test/CMakeLists.txt 2014-11-03 04:01:25 +0000
1074+++ tests/mir_test/CMakeLists.txt 2014-11-05 22:26:46 +0000
1075@@ -10,6 +10,7 @@
1076 popen.cpp
1077 wait_object.cpp
1078 current_thread_name.cpp
1079+ validity_matchers.cpp
1080 )
1081
1082 target_link_libraries(mir-test mirprotobuf)
1083
1084=== added file 'tests/mir_test/validity_matchers.cpp'
1085--- tests/mir_test/validity_matchers.cpp 1970-01-01 00:00:00 +0000
1086+++ tests/mir_test/validity_matchers.cpp 2014-11-05 22:26:46 +0000
1087@@ -0,0 +1,32 @@
1088+#include "mir_test/validity_matchers.h"
1089+#include "mir_toolkit/mir_client_library.h"
1090+
1091+template<>
1092+bool IsValidMatcher::MatchAndExplain(MirConnection* connection, MatchResultListener* listener) const
1093+{
1094+ if (connection == nullptr)
1095+ {
1096+ return false;
1097+ }
1098+ if (!mir_connection_is_valid(connection))
1099+ {
1100+ *listener << "error is: " << mir_connection_get_error_message(connection);
1101+ return false;
1102+ }
1103+ return true;
1104+}
1105+
1106+template<>
1107+bool IsValidMatcher::MatchAndExplain(MirSurface* surface, MatchResultListener* listener) const
1108+{
1109+ if (surface == nullptr)
1110+ {
1111+ return false;
1112+ }
1113+ if (!mir_surface_is_valid(surface))
1114+ {
1115+ *listener << "error is: " << mir_surface_get_error_message(surface);
1116+ return false;
1117+ }
1118+ return true;
1119+}
1120
1121=== modified file 'tests/mir_test_framework/using_stub_client_platform.cpp'
1122--- tests/mir_test_framework/using_stub_client_platform.cpp 2014-10-27 15:22:31 +0000
1123+++ tests/mir_test_framework/using_stub_client_platform.cpp 2014-11-05 22:26:46 +0000
1124@@ -17,57 +17,27 @@
1125 */
1126
1127 #include "mir_test_framework/using_stub_client_platform.h"
1128-#include "mir_test_framework/stub_client_connection_configuration.h"
1129 #include "mir_toolkit/mir_client_library.h"
1130-#include "src/client/mir_connection_api.h"
1131
1132 namespace mtf = mir_test_framework;
1133 namespace mcl = mir::client;
1134
1135-namespace
1136-{
1137-class StubMirConnectionAPI : public mcl::MirConnectionAPI
1138-{
1139-public:
1140- StubMirConnectionAPI(mcl::MirConnectionAPI* prev_api)
1141- : prev_api{prev_api}
1142- {
1143- }
1144-
1145- MirWaitHandle* connect(
1146+MirWaitHandle* mtf::StubMirConnectionAPI::connect(
1147+ mcl::ConfigurationFactory /*configuration*/,
1148 char const* socket_file,
1149 char const* name,
1150 mir_connected_callback callback,
1151- void* context) override
1152- {
1153- return prev_api->connect(socket_file, name, callback, context);
1154- }
1155-
1156- void release(MirConnection* connection) override
1157- {
1158- return prev_api->release(connection);
1159- }
1160-
1161- std::unique_ptr<mcl::ConnectionConfiguration> configuration(std::string const& socket) override
1162- {
1163- return std::unique_ptr<mcl::ConnectionConfiguration>{
1164- new mtf::StubConnectionConfiguration{socket}};
1165- }
1166-
1167-private:
1168- mcl::MirConnectionAPI* const prev_api;
1169-};
1170-
1171-}
1172-
1173-mtf::UsingStubClientPlatform::UsingStubClientPlatform()
1174- : prev_api{mir_connection_api_impl},
1175- stub_api{new StubMirConnectionAPI{prev_api}}
1176-{
1177- mir_connection_api_impl = stub_api.get();
1178-}
1179-
1180-mtf::UsingStubClientPlatform::~UsingStubClientPlatform()
1181-{
1182- mir_connection_api_impl = prev_api;
1183+ void* context)
1184+{
1185+ return prev_api->connect(configuration_factory(), socket_file, name, callback, context);
1186+}
1187+
1188+void mtf::StubMirConnectionAPI::release(MirConnection* connection)
1189+{
1190+ return prev_api->release(connection);
1191+}
1192+
1193+mcl::ConfigurationFactory mtf::StubMirConnectionAPI::configuration_factory()
1194+{
1195+ return factory;
1196 }

Subscribers

People subscribed via source and target branches