summaryrefslogtreecommitdiffstats
path: root/tests/x11session.py
blob: 5678073054abccc295d0084a2f768cac6286c458 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import os
import sys
import subprocess
from dbusmock import DBusTestCase

# This file has been submitted for inclusion in python-dbusmock
#  https://github.com/martinpitt/python-dbusmock/pull/44

if sys.version_info[0] < 3:
    PIPE_DEVNULL = open(os.devnull, 'wb')

    def Popen(*args, **kwargs):
        if 'pass_fds' in kwargs:
            pass_fds = kwargs['pass_fds']
            del kwargs['pass_fds']
        else:
            pass_fds = []

        orig_preexec_fn = kwargs.get('preexec_fn', None)

        def _setup():
            for fd in range(3, subprocess.MAXFD):
                if fd in pass_fds:
                    continue

                try:
                    os.close(fd)
                except OSError:
                    pass

            if orig_preexec_fn:
                orig_preexec_fn()

        # Don't let subprocess close FDs for us
        kwargs['close_fds'] = False
        kwargs['preexec_fn'] = _setup

        return subprocess.Popen(*args, **kwargs)

else:
    PIPE_DEVNULL = subprocess.DEVNULL
    Popen = subprocess.Popen


class X11SessionTestCase(DBusTestCase):
    #: The display the X server is running on
    X_display = -1
    #: The X server to start
    Xserver = 'Xvfb'
    #: Further parameters for the X server
    Xserver_args = ['-screen', '0', '1280x1024x24', '+extension', 'GLX']
    #: Where to redirect the X stdout and stderr to. Set to None for debugging
    #: purposes if the X server is failing for some reason.
    Xserver_output = PIPE_DEVNULL

    @classmethod
    def setUpClass(klass):
        klass.start_xorg()
        klass.addClassCleanup(klass.stop_xorg)

        klass.start_system_bus()
        klass.start_session_bus()

    @classmethod
    def start_xorg(klass):
        r, w = os.pipe()

        klass.xorg = Popen([klass.Xserver, '-displayfd', "%d" % w, '-noreset'] + klass.Xserver_args,
                           pass_fds=(w,),
                           stdout=klass.Xserver_output,
                           stderr=subprocess.STDOUT)
        os.close(w)

        # The X server will write "%d\n", we need to make sure to read the "\n".
        # If the server dies we get zero bytes reads as EOF is reached.
        display = b''
        while True:
            tmp = os.read(r, 1024)
            display += tmp

            # Break out if the read was empty or the line feed was read
            if not tmp or tmp[-1] == b'\n':
                break

        os.close(r)

        try:
            display = int(display.strip())
        except ValueError:
            # This should never happen, the X server didn't return a proper integer.
            # Most likely it died for some odd reason.
            # Note: Set Xserver_output to None to debug Xvfb startup issues.
            klass.stop_xorg()
            raise AssertionError('X server reported back no or an invalid display number (%s)' % (display))

        klass.X_display = display
        # Export information into our environment for tests to use
        os.environ['DISPLAY'] = ":%d" % klass.X_display
        if 'GNOME_SETUP_DISPLAY' in os.environ:
            del os.environ['GNOME_SETUP_DISPLAY']
        if 'WAYLAND_DISPLAY' in os.environ:
            del os.environ['WAYLAND_DISPLAY']

        # Server should still be up and running at this point
        assert klass.xorg.poll() is None

        return klass.X_display

    @classmethod
    def stop_xorg(klass):
        if hasattr(klass, 'xorg'):
            klass.X_display = -1
            klass.xorg.terminate()
            try:
                klass.xorg.wait(1)
            except:
                klass.xorg.kill()
                klass.xorg.wait()
            del klass.xorg