Mir

Merge lp:~raof/mir/xserver-spawner into lp:mir

Proposed by Chris Halse Rogers
Status: Work in progress
Proposed branch: lp:~raof/mir/xserver-spawner
Merge into: lp:mir
Prerequisite: lp:~raof/mir/process-wrapper
Diff against target: 725 lines (+566/-2)
16 files modified
.clang-format (+1/-1)
include/server/mir/default_server_configuration.h (+11/-0)
include/server/mir/xserver/null_server_spawner.h (+25/-0)
include/server/mir/xserver/xserver_launcher.h (+59/-0)
src/server/CMakeLists.txt (+3/-1)
src/server/default_server_configuration.cpp (+6/-0)
src/server/xserver/CMakeLists.txt (+12/-0)
src/server/xserver/global_socket_listening_server_spawner.cpp (+71/-0)
src/server/xserver/global_socket_listening_server_spawner.h (+52/-0)
src/server/xserver/null_server_spawner.cpp (+34/-0)
tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/CMakeLists.txt (+2/-0)
tests/acceptance-tests/test_xserver_spawner.cpp (+73/-0)
tests/unit-tests/CMakeLists.txt (+2/-0)
tests/unit-tests/xserver/CMakeLists.txt (+8/-0)
tests/unit-tests/xserver/test_xserver_spawner.cpp (+206/-0)
To merge this branch: bzr merge lp:~raof/mir/xserver-spawner
Reviewer Review Type Date Requested Status
Andreas Pokorny (community) Needs Fixing
Daniel van Vugt Needs Fixing
Alan Griffiths Needs Information
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+203475@code.launchpad.net

This proposal supersedes a proposal from 2014-01-28.

Description of the change

A first cut at the infrastructure required for seamless nesting of X11 clients.

In order to do this we need to be able to spawn an Xorg server, wait until it's
ready to accept connections, connect a Mir X11 client to act as a bridging WM,
and *then* offer the X11 socket to clients.

This branch accomplishes the first two requirements.

I'm proposing it now because it's a reasonable checkpoint and I need to go and work on something else.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Please rethink class names ending in "er". That's almost always a bad decision, and an indication that a different cleaner design is possible...

http://www.benhallbenhall.com/2013/01/naming-objects-er-object-names/

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) :
review: Needs Fixing
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 :

How much should Mir know about launching xservers?

Is it essential to all the uses we envisage: unity8? system compositor? Other (hypothetical at this time) shells?

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

could
+const char* mir::X::NullServerContext::client_connection_string()
return a string instead of a const char*?

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

> How much should Mir know about launching xservers?
>
> Is it essential to all the uses we envisage: unity8? system compositor? Other
> (hypothetical at this time) shells?

Last I heard from the architectural-level view is that lightdm was going to negotiate starting mir and friends and hooking up the FD's between them appropriately. That was something I heard a (relatively) long time ago though.

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

@Alan:
I think most non-trivial shells are going to want to support running X11 apps. It's not interesting for system-compositor usage, true.

As to why Mir should know about launching xservers - the process of tying together foreign X11 windows needs Mir support. Mir will need to include an X11 window manager in order to proxy between X11 EWMH window management and Mir - this could be in U8, but any shell that wants to support X11 apps will need identical code.

Ideally you want to start the X11 WM before any app connects to the server to avoid unexpected behaviours; the easiest way to do this is to start the X server, wait for it to come up, connect the WM proxy, and only then consider the server to be started (that's the next part of X11 integration).

I (obviously) think it makes sense to have Mir know how to start the X server.
@kdub:
This is somewhat of a circular dependency - the X server needs Mir running in order to be started (it needs the mir socket), and Mir's X11 WM needs the X server to be running. You can't easily start both from LightDM, as it would need to start Mir, it to be ready to accept connections, then start the X server.

Also, U8 wants to (a) start an X server only when an app needs it, and (b) start one-Xserver-per-app.

re: client_connection_string - Yeah, it could return a std::string (and did, at one point). I switched to char const* because I only ever wanted to consume it from things that took a char const*, but can easily switch it back.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) "er" issues as described above.

(2) I'm concerned about mentioning "X"-anything in the Mir source too. It feels like we're preventing Mir from being a success in its own right if it even has to mention X in its source. Even if the coupling is weak, we should strive for a better answer. Generalize, move it out-of-project, etc. We almost certainly don't want an "X" namespace. That's a reasonable indication that we're going in the wrong direction.

(3) You've introduced a build-dep on libx11-dev:
  +#include <X11/Xlib.h>
Definitely don't do that, at least :)

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

On Wed, 2014-01-29 at 03:09 +0000, Daniel van Vugt wrote:
> Review: Needs Fixing
>
> (1) "er" issues as described above.

Would you prefer XServerFactory?

> (2) I'm concerned about mentioning "X"-anything in the Mir source too. It feels like we're preventing Mir from being a success in its own right if it even has to mention X in its source. Even if the coupling is weak, we should strive for a better answer. Generalize, move it out-of-project, etc. We almost certainly don't want an "X" namespace. That's a reasonable indication that we're going in the wrong direction.

We're going to have to support running X11 apps for the foreseeable
future. Certainly for the life of 14.04, probably for the life of 16.04.

I don't think it's unreasonable for the Mir codebase to reflect the
importance of legacy X11 support.

Indeed, the actual implementations should be in a separate opt-in
libmirserverX11integration.so library, and will be once they start to
depend on X11 libraries.

>
> (3) You've introduced a build-dep on libx11-dev:
> +#include <X11/Xlib.h>
> Definitely don't do that, at least :)
>

Ah, yeah. This is currently only for the (disabled) acceptance test.
Once there's mirserver code that depends on X11 libs it'll be optional
and in a separate library.

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

@er:
I still dont get the argument about "er" - If RAOF renames "XServerSpawner" into "XServerFactory" and people are satisfied then there is something wrong with that rule (of thumb?).
We then switched from a noun that was derived from a verb that meant something like: "creates and launches a process or activity" to something that only means: "creates something". So from a clearer specific term to a generic one. A specific term that describes what should be in this class and what not - A term that suggest that this is actually a very specific functor that launches an XServer.

So I would prefer XServerSpawner::create_server over XServerFactory::create_server. Something like is XServerFactory::spawn_server also a good name - I just dont get the reasoning with the "no-er"-rule.

@library: I do believe that this functionality is something a current linux compositor framework needs to provide, but probably as a separate library. -> Needs Fixing

@clang-format: thanks for fixing!

review: Needs Fixing

Unmerged revisions

1349. By Chris Halse Rogers

clang-format: We always split constructor initialisers on new lines

1348. By Chris Halse Rogers

Disable X11ClientConnects acceptance test.

This needs a bit more fiddling - it all roughly works, but in the acceptance
test framework we don't have a real graphics card, so XMir currently fails.

Needs at least an xorg.conf specifying the dummy graphics driver

1347. By Chris Halse Rogers

Get the Xserver to connect to a Mir socket

1346. By Chris Halse Rogers

Update for process::Spawner API change

1345. By Chris Halse Rogers

Merged subprocess-wrapper into xserver-spawner.

1344. By Chris Halse Rogers

X::ServerSpawner::create_server must take a shared_ptr<process::Spawner>

create_server does things asynchronously, so it has to take shared ownership
of the process::Spawner it's using

1343. By Chris Halse Rogers

Rejigger X::ServerSpawner API.

spawn_server() now returns a future<ServerHandle> that will become a present ServerHandle once
the server is ready, rather than returning a ServerHandle now that has methods that will only
work in the future.

1342. By Chris Halse Rogers

Implement -displayfd handling for GlobalSocketListeningServerSpawner.

client_connection_string now returns a string that should be associated with an Xorg server that's ready to accept connections

1341. By Chris Halse Rogers

Merge process::Spawner work required to do Xserver startup properly

1340. By Chris Halse Rogers

More basic unittests for GlobalSocketListeningServerSpawner

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.clang-format'
--- .clang-format 2014-01-10 15:48:08 +0000
+++ .clang-format 2014-01-28 06:35:36 +0000
@@ -14,7 +14,7 @@
14BreakConstructorInitializersBeforeComma: false14BreakConstructorInitializersBeforeComma: false
15BinPackParameters: true15BinPackParameters: true
16ColumnLimit: 12016ColumnLimit: 120
17ConstructorInitializerAllOnOneLineOrOnePerLine: false17ConstructorInitializerAllOnOneLineOrOnePerLine: true
18DerivePointerBinding: true18DerivePointerBinding: true
19ExperimentalAutoDetectBinPacking: true19ExperimentalAutoDetectBinPacking: true
20IndentCaseLabels: false20IndentCaseLabels: false
2121
=== modified file 'include/server/mir/default_server_configuration.h'
--- include/server/mir/default_server_configuration.h 2014-01-21 15:29:52 +0000
+++ include/server/mir/default_server_configuration.h 2014-01-28 06:35:36 +0000
@@ -113,6 +113,11 @@
113class Logger;113class Logger;
114}114}
115115
116namespace X
117{
118class ServerSpawner;
119}
120
116class DefaultServerConfiguration : public virtual ServerConfiguration, DefaultConfigurationOptions121class DefaultServerConfiguration : public virtual ServerConfiguration, DefaultConfigurationOptions
117{122{
118public:123public:
@@ -232,6 +237,12 @@
232237
233 virtual std::shared_ptr<time::Clock> the_clock();238 virtual std::shared_ptr<time::Clock> the_clock();
234239
240 /** @name X11 server integration - customization
241 * configurable interfaces for integration with legacy X11 apps
242 * @{ */
243 virtual std::shared_ptr<X::ServerSpawner> the_xserver_spawner();
244 /** @} */
245
235protected:246protected:
236 using DefaultConfigurationOptions::the_options;247 using DefaultConfigurationOptions::the_options;
237 using DefaultConfigurationOptions::add_options;248 using DefaultConfigurationOptions::add_options;
238249
=== added directory 'include/server/mir/xserver'
=== added file 'include/server/mir/xserver/null_server_spawner.h'
--- include/server/mir/xserver/null_server_spawner.h 1970-01-01 00:00:00 +0000
+++ include/server/mir/xserver/null_server_spawner.h 2014-01-28 06:35:36 +0000
@@ -0,0 +1,25 @@
1#ifndef MIR_X_NULL_SERVER_SPAWNER_H_
2#define MIR_X_NULL_SERVER_SPAWNER_H_
3
4#include <mir/xserver/xserver_launcher.h>
5
6namespace mir
7{
8namespace X
9{
10class NullServerContext : public ServerContext
11{
12public:
13 char const* client_connection_string() override;
14};
15
16class NullServerSpawner : public ServerSpawner
17{
18public:
19 std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector) override;
20};
21
22}
23}
24
25#endif // MIR_X_NULL_SERVER_SPAWNER_H_
026
=== added file 'include/server/mir/xserver/xserver_launcher.h'
--- include/server/mir/xserver/xserver_launcher.h 1970-01-01 00:00:00 +0000
+++ include/server/mir/xserver/xserver_launcher.h 2014-01-28 06:35:36 +0000
@@ -0,0 +1,59 @@
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#ifndef MIR_X_XSERVER_LAUNCHER_H_
20#define MIR_X_XSERVER_LAUNCHER_H_
21
22#include <future>
23#include <memory>
24
25#include "mir/process/spawner.h"
26#include "mir/frontend/connector.h"
27
28namespace mir
29{
30namespace X
31{
32
33class ServerContext
34{
35public:
36 virtual ~ServerContext() = default;
37
38 /**
39 * \brief Get the XLib connection string to connect to this server
40 *
41 * This string can be passed by the client to XOpenDisplay to connect
42 * to this server instance, or set in the DISPLAY environment variable
43 * to be used as the default display.
44 */
45 virtual char const* client_connection_string() = 0;
46};
47
48class ServerSpawner
49{
50public:
51 virtual ~ServerSpawner() = default;
52
53 virtual std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<mir::process::Spawner> const& spawner,
54 std::shared_ptr<mir::frontend::Connector> const& connector) = 0;
55};
56}
57}
58
59#endif // MIR_X_XSERVER_LAUNCHER_H_
060
=== modified file 'src/server/CMakeLists.txt'
--- src/server/CMakeLists.txt 2014-01-28 06:35:36 +0000
+++ src/server/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -13,6 +13,7 @@
13add_subdirectory(shell/)13add_subdirectory(shell/)
14add_subdirectory(time/)14add_subdirectory(time/)
15add_subdirectory(process/)15add_subdirectory(process/)
16add_subdirectory(xserver/)
1617
17set(PREFIX "${CMAKE_INSTALL_PREFIX}")18set(PREFIX "${CMAKE_INSTALL_PREFIX}")
18set(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}")19set(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}")
@@ -57,8 +58,9 @@
57 mirlttng58 mirlttng
58 mirnestedgraphics59 mirnestedgraphics
59 miroffscreengraphics60 miroffscreengraphics
61 mirsharedpipe
60 mirprocess62 mirprocess
61 mirsharedpipe63 mirxserverintegration
62)64)
6365
64list(APPEND MIRSERVER_LINKS66list(APPEND MIRSERVER_LINKS
6567
=== modified file 'src/server/default_server_configuration.cpp'
--- src/server/default_server_configuration.cpp 2014-01-13 06:12:33 +0000
+++ src/server/default_server_configuration.cpp 2014-01-28 06:35:36 +0000
@@ -42,6 +42,7 @@
42#include "mir/time/high_resolution_clock.h"42#include "mir/time/high_resolution_clock.h"
43#include "mir/geometry/rectangles.h"43#include "mir/geometry/rectangles.h"
44#include "mir/default_configuration.h"44#include "mir/default_configuration.h"
45#include "mir/xserver/null_server_spawner.h"
4546
46#include <map>47#include <map>
4748
@@ -249,3 +250,8 @@
249 return std::make_shared<mir::DefaultServerStatusListener>();250 return std::make_shared<mir::DefaultServerStatusListener>();
250 });251 });
251}252}
253
254std::shared_ptr<mir::X::ServerSpawner> mir::DefaultServerConfiguration::the_xserver_spawner()
255{
256 return std::make_shared<mir::X::NullServerSpawner>();
257}
252258
=== added directory 'src/server/xserver'
=== added file 'src/server/xserver/CMakeLists.txt'
--- src/server/xserver/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/server/xserver/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -0,0 +1,12 @@
1set(
2 MIR_XSERVER_SRCS
3
4 global_socket_listening_server_spawner.cpp
5 null_server_spawner.cpp
6)
7
8ADD_LIBRARY(
9 mirxserverintegration STATIC
10
11 ${MIR_XSERVER_SRCS}
12)
013
=== added file 'src/server/xserver/global_socket_listening_server_spawner.cpp'
--- src/server/xserver/global_socket_listening_server_spawner.cpp 1970-01-01 00:00:00 +0000
+++ src/server/xserver/global_socket_listening_server_spawner.cpp 2014-01-28 06:35:36 +0000
@@ -0,0 +1,71 @@
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 <unistd.h>
20#include <errno.h>
21
22#include <boost/throw_exception.hpp>
23#include <boost/exception/errinfo_errno.hpp>
24
25#include "global_socket_listening_server_spawner.h"
26#include "mir/process/handle.h"
27
28namespace mx = mir::X;
29
30mx::GlobalSocketListeningServerContext::GlobalSocketListeningServerContext(std::unique_ptr<mir::process::Handle> server_handle, std::string connection_string)
31 : server_handle(std::move(server_handle)),
32 connection_string(connection_string)
33{
34}
35
36char const* mx::GlobalSocketListeningServerContext::client_connection_string()
37{
38 return connection_string.c_str();
39}
40
41std::future<std::unique_ptr<mx::ServerContext>> mx::GlobalSocketListeningServerSpawner::create_server(std::shared_ptr<mir::process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector)
42{
43 return std::async(std::launch::async, [spawner, connector]()
44 {
45 mir::pipe::Pipe displayfd_pipe;
46 auto displayfd = std::to_string(displayfd_pipe.write_fd());
47 int mir_fd = connector->client_socket_fd();
48 auto mir_fd_arg = std::string("fd://") + std::to_string(mir_fd);
49
50 auto future_handle = spawner->run_from_path("Xorg",
51 {"-displayfd", displayfd.c_str(),
52 "-mir", "xserver",
53 "-mirSocket", mir_fd_arg.c_str()},
54 {displayfd_pipe.write_fd(), mir_fd});
55
56 char display_number[10];
57 errno = 0;
58 int bytes_read = read(displayfd_pipe.read_fd(), display_number, sizeof display_number);
59
60 while (bytes_read == -1 && errno == EINTR)
61 bytes_read = read(displayfd_pipe.read_fd(), display_number, sizeof display_number);;
62
63 if (errno != 0)
64 BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failed to receive display number from Xserver"))
65 << boost::errinfo_errno(errno));
66
67 display_number[bytes_read] = '\0';
68
69 return std::unique_ptr<mx::ServerContext>(new mx::GlobalSocketListeningServerContext(future_handle.get(), std::string(":") + display_number));
70 });
71}
072
=== added file 'src/server/xserver/global_socket_listening_server_spawner.h'
--- src/server/xserver/global_socket_listening_server_spawner.h 1970-01-01 00:00:00 +0000
+++ src/server/xserver/global_socket_listening_server_spawner.h 2014-01-28 06:35:36 +0000
@@ -0,0 +1,52 @@
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#ifndef MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_
20#define MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_
21
22#include "mir/xserver/xserver_launcher.h"
23#include "mir/process/spawner.h"
24#include "mir/pipe.h"
25
26#include <memory>
27
28namespace mir
29{
30namespace X
31{
32class GlobalSocketListeningServerContext : public ServerContext
33{
34public:
35 GlobalSocketListeningServerContext(std::unique_ptr<mir::process::Handle> server_handle, std::string connection_string);
36 char const* client_connection_string() override;
37
38private:
39 std::unique_ptr<mir::process::Handle> server_handle;
40 std::string connection_string;
41};
42
43class GlobalSocketListeningServerSpawner : public ServerSpawner
44{
45public:
46 std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<mir::process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector) override;
47};
48
49}
50}
51
52#endif // MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_
053
=== added file 'src/server/xserver/null_server_spawner.cpp'
--- src/server/xserver/null_server_spawner.cpp 1970-01-01 00:00:00 +0000
+++ src/server/xserver/null_server_spawner.cpp 2014-01-28 06:35:36 +0000
@@ -0,0 +1,34 @@
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 "mir/xserver/null_server_spawner.h"
20
21const char* mir::X::NullServerContext::client_connection_string()
22{
23 return "";
24}
25
26std::future<std::unique_ptr<mir::X::ServerContext>> mir::X::NullServerSpawner::create_server(
27 std::shared_ptr<mir::process::Spawner> const& unused, std::shared_ptr<mir::frontend::Connector> const& unuseder)
28{
29 static_cast<void>(unused);
30 static_cast<void>(unuseder);
31 std::promise<std::unique_ptr<mir::X::ServerContext>> boring_promise;
32 boring_promise.set_value(std::unique_ptr<mir::X::ServerContext>(new mir::X::NullServerContext));
33 return boring_promise.get_future();
34}
035
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2014-01-23 08:06:05 +0000
+++ tests/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -36,6 +36,7 @@
3636
37if (MIR_BUILD_ACCEPTANCE_TESTS)37if (MIR_BUILD_ACCEPTANCE_TESTS)
38 add_subdirectory(acceptance-tests/)38 add_subdirectory(acceptance-tests/)
39 pkg_check_modules(X11 REQUIRED x11)
39endif (MIR_BUILD_ACCEPTANCE_TESTS)40endif (MIR_BUILD_ACCEPTANCE_TESTS)
4041
41if (MIR_BUILD_INTEGRATION_TESTS)42if (MIR_BUILD_INTEGRATION_TESTS)
4243
=== modified file 'tests/acceptance-tests/CMakeLists.txt'
--- tests/acceptance-tests/CMakeLists.txt 2014-01-28 06:35:36 +0000
+++ tests/acceptance-tests/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -28,6 +28,7 @@
28 test_client_library_drm.cpp28 test_client_library_drm.cpp
29 test_protobuf.cpp29 test_protobuf.cpp
30 test_subprocess.cpp30 test_subprocess.cpp
31 test_xserver_spawner.cpp
31 ${GENERATED_PROTOBUF_SRCS}32 ${GENERATED_PROTOBUF_SRCS}
32 ${GENERATED_PROTOBUF_HDRS}33 ${GENERATED_PROTOBUF_HDRS}
33)34)
@@ -66,6 +67,7 @@
66 ${GMOCK_LIBRARY}67 ${GMOCK_LIBRARY}
67 ${GMOCK_MAIN_LIBRARY}68 ${GMOCK_MAIN_LIBRARY}
68 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.69 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
70 ${X11_LDFLAGS}
69)71)
7072
71CMAKE_DEPENDENT_OPTION(73CMAKE_DEPENDENT_OPTION(
7274
=== added file 'tests/acceptance-tests/test_xserver_spawner.cpp'
--- tests/acceptance-tests/test_xserver_spawner.cpp 1970-01-01 00:00:00 +0000
+++ tests/acceptance-tests/test_xserver_spawner.cpp 2014-01-28 06:35:36 +0000
@@ -0,0 +1,73 @@
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/server/xserver/global_socket_listening_server_spawner.h"
20#include "mir_test_framework/testing_server_configuration.h"
21#include "mir_test_framework/in_process_server.h"
22#include "mir/xserver/xserver_launcher.h"
23#include "src/server/process/fork_spawner.h"
24
25#include <X11/Xlib.h>
26#include <stdlib.h>
27
28#include <gtest/gtest.h>
29
30namespace mtf = mir_test_framework;
31namespace mx = mir::X;
32
33namespace
34{
35struct XserverSpawningServer : public mtf::InProcessServer
36{
37public:
38 std::shared_ptr<mx::ServerSpawner> the_xserver_spawner()
39 {
40 return config.the_xserver_spawner();
41 }
42
43 mir::DefaultServerConfiguration& server_config() override
44 {
45 return config;
46 }
47
48 class SocketListeningXServerConfig : public mtf::StubbedServerConfiguration
49 {
50 public:
51 std::shared_ptr<mx::ServerSpawner> the_xserver_spawner() override
52 {
53 return std::make_shared<mx::GlobalSocketListeningServerSpawner> ();
54 }
55 } config;
56};
57}
58
59// This requires a bit more fiddling before it will work.
60// Particularly, it needs an xorg.conf that specifies dummy
61// devices, so the real devices don't fail to load.
62TEST_F(XserverSpawningServer, DISABLED_X11ClientConnects)
63{
64 // Ensure the surrounding environment doesn't mess with the test
65 unsetenv("DISPLAY");
66
67 auto xserver = the_xserver_spawner()->create_server(std::make_shared<mir::process::ForkSpawner>(), config.the_connector());
68 Display* disp = XOpenDisplay(xserver.get()->client_connection_string());
69
70 ASSERT_TRUE(disp != NULL);
71
72 XCloseDisplay(disp);
73}
074
=== modified file 'tests/unit-tests/CMakeLists.txt'
--- tests/unit-tests/CMakeLists.txt 2014-01-28 06:35:36 +0000
+++ tests/unit-tests/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -30,6 +30,7 @@
30add_subdirectory(android_input/)30add_subdirectory(android_input/)
31add_subdirectory(scene/)31add_subdirectory(scene/)
32add_subdirectory(draw/)32add_subdirectory(draw/)
33add_subdirectory(xserver/)
3334
34add_executable(mir_unit_tests ${UNIT_TEST_SOURCES})35add_executable(mir_unit_tests ${UNIT_TEST_SOURCES})
35uses_android_input(mir_unit_tests)36uses_android_input(mir_unit_tests)
@@ -43,6 +44,7 @@
43 mirdraw44 mirdraw
44 mirtestdraw45 mirtestdraw
45 mirlogging46 mirlogging
47 mirxserverintegration
4648
47 mir-test49 mir-test
48 mir-test-doubles50 mir-test-doubles
4951
=== added directory 'tests/unit-tests/xserver'
=== added file 'tests/unit-tests/xserver/CMakeLists.txt'
--- tests/unit-tests/xserver/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/xserver/CMakeLists.txt 2014-01-28 06:35:36 +0000
@@ -0,0 +1,8 @@
1list(APPEND UNIT_TEST_SOURCES
2 ${CMAKE_CURRENT_SOURCE_DIR}/test_xserver_spawner.cpp
3)
4
5set(
6 UNIT_TEST_SOURCES
7 ${UNIT_TEST_SOURCES}
8 PARENT_SCOPE)
09
=== added file 'tests/unit-tests/xserver/test_xserver_spawner.cpp'
--- tests/unit-tests/xserver/test_xserver_spawner.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/xserver/test_xserver_spawner.cpp 2014-01-28 06:35:36 +0000
@@ -0,0 +1,206 @@
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 <future>
20#include <vector>
21#include <thread>
22#include <condition_variable>
23
24#include <gtest/gtest.h>
25#include <gmock/gmock.h>
26
27#include "mir/process/spawner.h"
28#include "mir/process/handle.h"
29
30#include "src/server/xserver/global_socket_listening_server_spawner.h"
31
32using namespace ::testing;
33
34namespace
35{
36struct MockConnector : public mir::frontend::Connector
37{
38 MOCK_METHOD0(start, void(void));
39 MOCK_METHOD0(stop, void(void));
40 MOCK_CONST_METHOD0(client_socket_fd, int(void));
41 MOCK_CONST_METHOD0(remove_endpoint, void(void));
42};
43
44struct MockProcessSpawner : public mir::process::Spawner
45{
46 MOCK_CONST_METHOD3(run, mir::process::Handle*(std::string, std::vector<char const*>, std::vector<int>));
47
48 std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary) const override
49 {
50 std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise;
51 dummy_promise.set_value(std::unique_ptr<mir::process::Handle>(
52 run(binary, std::initializer_list<char const*>(), std::initializer_list<int>())));
53 return dummy_promise.get_future();
54 }
55 std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary,
56 std::initializer_list<char const*> args) const
57 override
58 {
59 std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise;
60 dummy_promise.set_value(std::unique_ptr<mir::process::Handle>(run(binary, args, std::initializer_list<int>())));
61 return dummy_promise.get_future();
62 }
63
64 std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary,
65 std::initializer_list<char const*> args,
66 std::initializer_list<int> fds) const override
67 {
68 std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise;
69 dummy_promise.set_value(std::unique_ptr<mir::process::Handle>(run(binary, args, fds)));
70 return dummy_promise.get_future();
71 }
72};
73
74struct SocketListeningServerTest : public testing::Test
75{
76 SocketListeningServerTest()
77 : default_server_number("100"),
78 spawner(std::make_shared<NiceMock<MockProcessSpawner>>()),
79 connector(std::make_shared<NiceMock<MockConnector>>())
80 {
81 ON_CALL(*spawner, run(_, _, _))
82 .WillByDefault(DoAll(SaveArg<0>(&binary),
83 SaveArg<1>(&args),
84 SaveArg<2>(&fds),
85 InvokeWithoutArgs([this]()
86 { write_server_string(default_server_number); }),
87 Return(nullptr)));
88 ON_CALL(*connector, client_socket_fd())
89 .WillByDefault(Return(22));
90 }
91
92 void write_server_string(std::string server_number)
93 {
94 auto location = std::find_if(args.begin(), args.end(), [](char const* a)
95 { return strcmp(a, "-displayfd") == 0; });
96 ASSERT_NE(location, args.end());
97 ASSERT_NE(++location, args.end());
98 int server_fd = atoi(*location);
99 write(server_fd, server_number.data(), server_number.length());
100 close(server_fd);
101 }
102
103 std::string default_server_number;
104 std::string binary;
105 std::vector<char const*> args;
106 std::vector<int> fds;
107 std::shared_ptr<NiceMock<MockProcessSpawner>> spawner;
108 std::shared_ptr<NiceMock<MockConnector>> connector;
109};
110}
111
112TEST_F(SocketListeningServerTest, CreateServerAlwaysValid)
113{
114 mir::X::GlobalSocketListeningServerSpawner factory;
115
116 auto server_context = factory.create_server(spawner, connector);
117 ASSERT_NE(server_context.get(), nullptr);
118}
119
120TEST_F(SocketListeningServerTest, SpawnsCorrectExecutable)
121{
122 mir::X::GlobalSocketListeningServerSpawner factory;
123
124 auto server_context = factory.create_server(spawner, connector);
125 server_context.get();
126
127 EXPECT_EQ(binary, "Xorg");
128}
129
130namespace
131{
132MATCHER_P(ContainsSubsequence, subsequence, "")
133{
134 auto location =
135 std::search(arg.begin(), arg.end(), subsequence.begin(), subsequence.end(), [](char const* a, std::string b)
136 { return strcmp(a, b.c_str()) == 0; });
137 return location != arg.end();
138}
139}
140
141TEST_F(SocketListeningServerTest, SpawnsWithDisplayFDSet)
142{
143 mir::X::GlobalSocketListeningServerSpawner factory;
144
145 auto server_context = factory.create_server(spawner, connector);
146 server_context.get();
147
148 ASSERT_THAT(args, Not(IsEmpty()));
149 ASSERT_THAT(fds, Not(IsEmpty()));
150
151 Matcher<std::vector<char const*>> fd_matcher = Not(_);
152 for (auto fd : fds)
153 {
154 fd_matcher = AnyOf(fd_matcher, ContainsSubsequence(std::vector<std::string>{"-displayfd", std::to_string(fd)}));
155 }
156 EXPECT_THAT(args, fd_matcher);
157}
158
159TEST_F(SocketListeningServerTest, ReturnsCorrectDisplayString)
160{
161 mir::X::GlobalSocketListeningServerSpawner factory;
162
163 default_server_number = "20";
164 auto server_context = factory.create_server(spawner, connector);
165
166 EXPECT_STREQ(":20", server_context.get()->client_connection_string());
167}
168
169TEST_F(SocketListeningServerTest, HandlesSpawnerLifecycleCorrectly)
170{
171 mir::X::GlobalSocketListeningServerSpawner factory;
172
173 std::future<std::unique_ptr<mir::X::ServerContext>> server_context;
174
175 {
176 auto tmp_spawner = std::make_shared<MockProcessSpawner>();
177 ON_CALL(*tmp_spawner, run(_, _, _))
178 .WillByDefault(DoAll(SaveArg<0>(&binary),
179 SaveArg<1>(&args),
180 SaveArg<2>(&fds),
181 InvokeWithoutArgs([this]()
182 { write_server_string(default_server_number); }),
183 Return(nullptr)));
184 EXPECT_CALL(*tmp_spawner, run(_, _, _));
185 server_context = factory.create_server(tmp_spawner, connector);
186 }
187
188 ASSERT_NE(server_context.get(), nullptr);
189}
190
191TEST_F(SocketListeningServerTest, PassesMirSocketCorrectly)
192{
193 mir::X::GlobalSocketListeningServerSpawner factory;
194
195 int const mir_fd{32};
196 std::string const mir_connect_str{std::string("fd://") + std::to_string(mir_fd)};
197 EXPECT_CALL(*connector, client_socket_fd())
198 .WillOnce(Return(mir_fd));
199
200 auto server_context = factory.create_server(spawner, connector);
201
202 server_context.get();
203
204 EXPECT_THAT(args, ContainsSubsequence(std::vector<std::string>{"-mirSocket", mir_connect_str}));
205 EXPECT_THAT(fds, Contains(Eq(mir_fd)));
206}

Subscribers

People subscribed via source and target branches