Mir

Merge lp:~afrantzis/mir/better-client-api-override into lp:mir

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alexandros Frantzis
Approved revision: no longer in the source branch.
Merged at revision: 1834
Proposed branch: lp:~afrantzis/mir/better-client-api-override
Merge into: lp:mir
Diff against target: 447 lines (+180/-163)
6 files modified
include/test/mir_test_framework/using_stub_client_platform.h (+11/-3)
src/client/api_impl.h (+0/-27)
src/client/api_impl_types.h (+0/-32)
src/client/mir_connection_api.cpp (+73/-66)
src/client/mir_connection_api.h (+59/-0)
tests/mir_test_framework/using_stub_client_platform.cpp (+37/-35)
To merge this branch: bzr merge lp:~afrantzis/mir/better-client-api-override
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Chris Halse Rogers Approve
Kevin DuBois (community) Approve
Alan Griffiths Approve
Review via email: mp+229479@code.launchpad.net

Commit message

client: Rework mechanism to override Mir client functions

Description of the change

client: Rework mechanism to override Mir client functions

In addition to being more versatile and powerful in general, the proposed approach is needed so that we can fix the tests for [1] (which is currently WIP) in a more sane way. I have provided override points only for connect, release (which existed before) and a new one (the ConnectionConfiguration to use in the default connect implementation). We can add new override points as needed.

IMPORTANT UPDATE: it turns out that because of recent changes in how we build unit and integration tests, client global code is executed twice: once from the client code that was compiled in mir_unit_tests/mir_integration_tests and once because we indirectly load libmirclient.so. This means that constructors
and destructors of global objects are run twice, causing issues. I have worked around this issue in r1814, but this problem may come back to bite us at later time, so it's worth some further consideration (not as part of this MP please, unless the proposed workaround is not acceptable).

[1] https://code.launchpad.net/~afrantzis/mir/client-lifecycle-terminate-properly/+merge/229234

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: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I've no objection to this step (the direction is sane), but I've a feeling that this and the [Default]ConnectionConfiguration stuff all belongs in one customization point.

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

hah, I was just staring at this today when writing an integration test, and thought that this should be done. looks good to me.

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

Looks sensible to me.

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Oh, no...

Setting up libmirplatform1:armhf (0.6.0bzr1815pkg0utopic172+autopilot0) ...
dpkg: dependency problems prevent configuration of libmirplatform-dev:armhf:
 libmirplatform-dev:armhf depends on libboost-program-options-dev; however:
  Package libboost-program-options-dev is not installed.

dpkg: error processing package libmirplatform-dev:armhf (--install):
 dependency problems - leaving unconfigured
dpkg: libmirplatform:armhf: dependency problems, but configuring anyway as you requested:
 libmirplatform-dev:armhf (0.6.0bzr1815pkg0utopic172+autopilot0) breaks libmirplatform (<= 0.5.1+14.10.20140728-0ubuntu1) and is unpacked but not configured.
  Version of libmirplatform:armhf to be configured is 0.5.1+14.10.20140728-0ubuntu1.

Setting up libmirplatform:armhf (0.5.1+14.10.20140728-0ubuntu1) ...
Setting up libmirplatformgraphics-android:armhf (0.6.0bzr1815pkg0utopic172+autopilot0) ...
Setting up libmirplatformgraphics-mesa:armhf (0.6.0bzr1815pkg0utopic172+autopilot0) ...
dpkg: dependency problems prevent configuration of libmirserver-dev:armhf:
 libmirserver-dev:armhf depends on libmirplatform-dev (= 0.6.0bzr1815pkg0utopic172+autopilot0); however:
  Package libmirplatform-dev:armhf is not configured yet.

dpkg: error processing package libmirserver-dev:armhf (--install):
 dependency problems - leaving unconfigured

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
Alexandros Frantzis (afrantzis) wrote :

Started by upstream project "mir-mediumtests-utopic-touch" build number 1256
Building remotely on cyclops-node07 in workspace /home/ubuntu/jenkins/workspace/mir-mediumtests-builder-utopic-armhf
java.io.IOException: Failed to mkdirs: /home/ubuntu/jenkins/workspace/mir-mediumtests-builder-utopic-armhf
 at hudson.FilePath.mkdirs(FilePath.java:911)
 at hudson.model.AbstractProject.checkout(AbstractProject.java:1254)
 at hudson.model.AbstractBuild$AbstractBuildExecution.defaultCheckout(AbstractBuild.java:590)
 at jenkins.scm.SCMCheckoutStrategy.checkout(SCMCheckoutStrategy.java:88)
 at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:495)
 at hudson.model.Run.execute(Run.java:1502)
 at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:46)
 at hudson.model.ResourceController.execute(ResourceController.java:88)
 at hudson.model.Executor.run(Executor.java:237)
Archiving artifacts
Build was marked for publishing on https://jenkins.qa.ubuntu.com/
Finished: FAILURE

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/test/mir_test_framework/using_stub_client_platform.h'
2--- include/test/mir_test_framework/using_stub_client_platform.h 2014-01-29 13:06:25 +0000
3+++ include/test/mir_test_framework/using_stub_client_platform.h 2014-08-05 10:18:32 +0000
4@@ -19,7 +19,15 @@
5 #ifndef MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_
6 #define MIR_TEST_FRAMEWORK_USING_STUB_CLIENT_PLATFORM_H_
7
8-#include "src/client/api_impl_types.h"
9+#include <memory>
10+
11+namespace mir
12+{
13+namespace client
14+{
15+class MirConnectionAPI;
16+}
17+}
18
19 namespace mir_test_framework
20 {
21@@ -34,8 +42,8 @@
22 UsingStubClientPlatform(UsingStubClientPlatform const&) = delete;
23 UsingStubClientPlatform operator=(UsingStubClientPlatform const&) = delete;
24
25- mir_connect_impl_func const prev_mir_connect_impl;
26- mir_connection_release_impl_func const prev_mir_connection_release_impl;
27+ mir::client::MirConnectionAPI* prev_api;
28+ std::unique_ptr<mir::client::MirConnectionAPI> stub_api;
29 };
30
31 }
32
33=== removed file 'src/client/api_impl.h'
34--- src/client/api_impl.h 2014-01-28 18:21:14 +0000
35+++ src/client/api_impl.h 1970-01-01 00:00:00 +0000
36@@ -1,27 +0,0 @@
37-/*
38- * Copyright © 2014 Canonical Ltd.
39- *
40- * This program is free software: you can redistribute it and/or modify it
41- * under the terms of the GNU Lesser General Public License version 3,
42- * as published by the Free Software Foundation.
43- *
44- * This program is distributed in the hope that it will be useful,
45- * but WITHOUT ANY WARRANTY; without even the implied warranty of
46- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47- * GNU Lesser General Public License for more details.
48- *
49- * You should have received a copy of the GNU Lesser General Public License
50- * along with this program. If not, see <http://www.gnu.org/licenses/>.
51- *
52- * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
53- */
54-
55-#ifndef MIR_CLIENT_API_IMPL_H_
56-#define MIR_CLIENT_API_IMPL_H_
57-
58-#include "api_impl_types.h"
59-
60-extern mir_connect_impl_func mir_connect_impl;
61-extern mir_connection_release_impl_func mir_connection_release_impl;
62-
63-#endif /* MIR_CLIENT_API_IMPL_H_ */
64
65=== removed file 'src/client/api_impl_types.h'
66--- src/client/api_impl_types.h 2014-01-28 18:21:14 +0000
67+++ src/client/api_impl_types.h 1970-01-01 00:00:00 +0000
68@@ -1,32 +0,0 @@
69-/*
70- * Copyright © 2014 Canonical Ltd.
71- *
72- * This program is free software: you can redistribute it and/or modify it
73- * under the terms of the GNU Lesser General Public License version 3,
74- * as published by the Free Software Foundation.
75- *
76- * This program is distributed in the hope that it will be useful,
77- * but WITHOUT ANY WARRANTY; without even the implied warranty of
78- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
79- * GNU Lesser General Public License for more details.
80- *
81- * You should have received a copy of the GNU Lesser General Public License
82- * along with this program. If not, see <http://www.gnu.org/licenses/>.
83- *
84- * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
85- */
86-
87-#ifndef MIR_CLIENT_API_IMPL_TYPES_H_
88-#define MIR_CLIENT_API_IMPL_TYPES_H_
89-
90-#include "mir_toolkit/client_types.h"
91-
92-typedef MirWaitHandle* (*mir_connect_impl_func)(
93- char const *server,
94- char const *app_name,
95- mir_connected_callback callback,
96- void *context);
97-
98-typedef void (*mir_connection_release_impl_func)(MirConnection *connection);
99-
100-#endif /* MIR_CLIENT_API_IMPL_TYPES_H_ */
101
102=== modified file 'src/client/mir_connection_api.cpp'
103--- src/client/mir_connection_api.cpp 2014-06-09 13:48:18 +0000
104+++ src/client/mir_connection_api.cpp 2014-08-05 10:18:32 +0000
105@@ -16,6 +16,7 @@
106 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
107 */
108
109+#include "mir_connection_api.h"
110 #include "mir_toolkit/mir_connection.h"
111 #include "mir_toolkit/mir_client_library_drm.h"
112 #include "mir/default_configuration.h"
113@@ -25,7 +26,6 @@
114 //#include "egl_native_display_container.h"
115 #include "default_connection_configuration.h"
116 #include "display_configuration.h"
117-#include "api_impl_types.h"
118 #include "error_connections.h"
119
120
121@@ -49,78 +49,85 @@
122 return ((a - 1) / b) + 1;
123 }
124
125-
126-MirWaitHandle* mir_default_connect(
127- char const* socket_file,
128- char const* name,
129- mir_connected_callback callback,
130- void* context)
131+class DefaultMirConnectionAPI : public mcl::MirConnectionAPI
132 {
133-
134- try
135- {
136- std::string sock;
137- if (socket_file)
138- sock = socket_file;
139+public:
140+ MirWaitHandle* connect(
141+ char const* socket_file,
142+ char const* name,
143+ mir_connected_callback callback,
144+ void* context) override
145+ {
146+ try
147+ {
148+ std::string sock;
149+ if (socket_file)
150+ sock = socket_file;
151+ else
152+ {
153+ auto socket_env = getenv("MIR_SOCKET");
154+ if (socket_env)
155+ sock = socket_env;
156+ else
157+ sock = mir::default_server_socket;
158+ }
159+
160+ auto const conf = configuration(sock);
161+
162+ std::unique_ptr<MirConnection> connection{new MirConnection(*conf)};
163+ auto const result = connection->connect(name, callback, context);
164+ connection.release();
165+ return result;
166+ }
167+ catch (std::exception const& x)
168+ {
169+ MirConnection* error_connection = new MirConnection(x.what());
170+ mcl::ErrorConnections::instance().insert(error_connection);
171+ callback(error_connection, context);
172+ return nullptr;
173+ }
174+ }
175+
176+ void release(MirConnection* connection) override
177+ {
178+ if (!mcl::ErrorConnections::instance().contains(connection))
179+ {
180+ try
181+ {
182+ auto wait_handle = connection->disconnect();
183+ wait_handle->wait_for_all();
184+ }
185+ catch (std::exception const&)
186+ {
187+ // We're implementing a C API so no exceptions are to be
188+ // propagated. And that's OK because if disconnect() fails,
189+ // we don't care why. We're finished with the connection anyway.
190+ }
191+ }
192 else
193 {
194- auto socket_env = getenv("MIR_SOCKET");
195- if (socket_env)
196- sock = socket_env;
197- else
198- sock = mir::default_server_socket;
199+ mcl::ErrorConnections::instance().remove(connection);
200 }
201
202- mcl::DefaultConnectionConfiguration conf{sock};
203-
204- std::unique_ptr<MirConnection> connection{new MirConnection(conf)};
205- auto const result = connection->connect(name, callback, context);
206- connection.release();
207- return result;
208+ delete connection;
209 }
210- catch (std::exception const& x)
211+
212+ std::unique_ptr<mcl::ConnectionConfiguration> configuration(std::string const& socket) override
213 {
214- MirConnection* error_connection = new MirConnection(x.what());
215- mcl::ErrorConnections::instance().insert(error_connection);
216- callback(error_connection, context);
217- return nullptr;
218+ return std::unique_ptr<mcl::ConnectionConfiguration>{
219+ new mcl::DefaultConnectionConfiguration{socket}};
220 }
221-}
222-
223-
224-void mir_default_connection_release(MirConnection* connection)
225+};
226+
227+mcl::MirConnectionAPI* mir_connection_api_impl_default()
228 {
229- if (!mcl::ErrorConnections::instance().contains(connection))
230- {
231- try
232- {
233- auto wait_handle = connection->disconnect();
234- wait_handle->wait_for_all();
235- }
236- catch (std::exception const&)
237- {
238- // We're implementing a C API so no exceptions are to be
239- // propagated. And that's OK because if disconnect() fails,
240- // we don't care why. We're finished with the connection anyway.
241- }
242- }
243- else
244- {
245- mcl::ErrorConnections::instance().remove(connection);
246- }
247-
248- delete connection;
249-}
250-
251-}
252-
253-//mir_connect and mir_connection_release can be overridden by test code that sets these function
254-//pointers to do things like stub out the graphics drivers or change the connection configuration.
255-
256-//TODO: we could have a more comprehensive solution that allows us to substitute any of the functions
257-//for test purposes, not just the connect functions
258-mir_connect_impl_func mir_connect_impl = mir_default_connect;
259-mir_connection_release_impl_func mir_connection_release_impl = mir_default_connection_release;
260+ static DefaultMirConnectionAPI default_api;
261+ return &default_api;
262+}
263+
264+}
265+
266+mcl::MirConnectionAPI* mir_connection_api_impl{mir_connection_api_impl_default()};
267
268 MirWaitHandle* mir_connect(
269 char const* socket_file,
270@@ -130,7 +137,7 @@
271 {
272 try
273 {
274- return mir_connect_impl(socket_file, name, callback, context);
275+ return mir_connection_api_impl->connect(socket_file, name, callback, context);
276 }
277 catch (std::exception const&)
278 {
279@@ -164,7 +171,7 @@
280 {
281 try
282 {
283- return mir_connection_release_impl(connection);
284+ return mir_connection_api_impl->release(connection);
285 }
286 catch (std::exception const&)
287 {
288
289=== added file 'src/client/mir_connection_api.h'
290--- src/client/mir_connection_api.h 1970-01-01 00:00:00 +0000
291+++ src/client/mir_connection_api.h 2014-08-05 10:18:32 +0000
292@@ -0,0 +1,59 @@
293+/*
294+ * Copyright © 2014 Canonical Ltd.
295+ *
296+ * This program is free software: you can redistribute it and/or modify it
297+ * under the terms of the GNU Lesser General Public License version 3,
298+ * as published by the Free Software Foundation.
299+ *
300+ * This program is distributed in the hope that it will be useful,
301+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
302+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303+ * GNU Lesser General Public License for more details.
304+ *
305+ * You should have received a copy of the GNU Lesser General Public License
306+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
307+ *
308+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
309+ */
310+
311+#ifndef MIR_CLIENT_MIR_CONNECTION_API_H_
312+#define MIR_CLIENT_MIR_CONNECTION_API_H_
313+
314+#include "mir_toolkit/mir_wait.h"
315+
316+#include <string>
317+#include <memory>
318+
319+namespace mir
320+{
321+namespace client
322+{
323+class ConnectionConfiguration;
324+
325+class MirConnectionAPI
326+{
327+public:
328+ virtual ~MirConnectionAPI() = default;
329+
330+ virtual MirWaitHandle* connect(
331+ char const* socket_file,
332+ char const* name,
333+ mir_connected_callback callback,
334+ void* context) = 0;
335+
336+ virtual void release(MirConnection* connection) = 0;
337+
338+ virtual std::unique_ptr<ConnectionConfiguration> configuration(std::string const& socket) = 0;
339+
340+protected:
341+ MirConnectionAPI() = default;
342+ MirConnectionAPI(MirConnectionAPI const&) = default;
343+ MirConnectionAPI& operator=(MirConnectionAPI const&) = default;
344+};
345+
346+}
347+}
348+
349+extern "C" mir::client::MirConnectionAPI* mir_connection_api_impl;
350+
351+#endif
352
353=== modified file 'tests/mir_test_framework/using_stub_client_platform.cpp'
354--- tests/mir_test_framework/using_stub_client_platform.cpp 2014-07-29 10:25:43 +0000
355+++ tests/mir_test_framework/using_stub_client_platform.cpp 2014-08-05 10:18:32 +0000
356@@ -18,54 +18,56 @@
357
358 #include "mir_test_framework/using_stub_client_platform.h"
359 #include "mir_test_framework/stub_client_connection_configuration.h"
360-#include "src/client/mir_wait_handle.h"
361-#include "src/client/mir_connection.h"
362-#include "src/client/api_impl.h"
363+#include "src/client/mir_connection_api.h"
364
365 namespace mtf = mir_test_framework;
366+namespace mcl = mir::client;
367
368 namespace
369 {
370
371-MirWaitHandle* mir_connect_override(
372- char const *socket_file,
373- char const *app_name,
374- mir_connected_callback callback,
375- void *context)
376-{
377- mtf::StubConnectionConfiguration conf(socket_file);
378- auto connection = new MirConnection(conf);
379- return connection->connect(app_name, callback, context);
380-}
381-
382-void mir_connection_release_override(MirConnection *connection)
383-{
384- try
385- {
386- auto wait_handle = connection->disconnect();
387- mir_wait_for(wait_handle);
388- }
389- catch (std::exception const&)
390- {
391- // Really, we want try/finally, but that's not C++11
392- delete connection;
393- throw;
394- }
395- delete connection;
396-}
397+class StubMirConnectionAPI : public mcl::MirConnectionAPI
398+{
399+public:
400+ StubMirConnectionAPI(mcl::MirConnectionAPI* prev_api)
401+ : prev_api{prev_api}
402+ {
403+ }
404+
405+ MirWaitHandle* connect(
406+ char const* socket_file,
407+ char const* name,
408+ mir_connected_callback callback,
409+ void* context) override
410+ {
411+ return prev_api->connect(socket_file, name, callback, context);
412+ }
413+
414+ void release(MirConnection* connection) override
415+ {
416+ return prev_api->release(connection);
417+ }
418+
419+ std::unique_ptr<mcl::ConnectionConfiguration> configuration(std::string const& socket) override
420+ {
421+ return std::unique_ptr<mcl::ConnectionConfiguration>{
422+ new mtf::StubConnectionConfiguration{socket}};
423+ }
424+
425+private:
426+ mcl::MirConnectionAPI* const prev_api;
427+};
428
429 }
430
431 mtf::UsingStubClientPlatform::UsingStubClientPlatform()
432- : prev_mir_connect_impl{mir_connect_impl},
433- prev_mir_connection_release_impl{mir_connection_release_impl}
434+ : prev_api{mir_connection_api_impl},
435+ stub_api{new StubMirConnectionAPI{prev_api}}
436 {
437- mir_connect_impl = mir_connect_override;
438- mir_connection_release_impl = mir_connection_release_override;
439+ mir_connection_api_impl = stub_api.get();
440 }
441
442 mtf::UsingStubClientPlatform::~UsingStubClientPlatform()
443 {
444- mir_connect_impl = prev_mir_connect_impl;
445- mir_connection_release_impl = prev_mir_connection_release_impl;
446+ mir_connection_api_impl = prev_api;
447 }

Subscribers

People subscribed via source and target branches