python310Packages.pydeconz: 104 -> 105
[NixPkgs.git] / nixos / tests / turbovnc-headless-server.nix
blob1dbf9297c813142aec53c056ff4da5573024ca52
1 import ./make-test-python.nix ({ pkgs, lib, ... }: {
2   name = "turbovnc-headless-server";
3   meta = {
4     maintainers = with lib.maintainers; [ nh2 ];
5   };
7   nodes.machine = { pkgs, ... }: {
9     environment.systemPackages = with pkgs; [
10       glxinfo
11       procps # for `pkill`, `pidof` in the test
12       scrot # for screenshotting Xorg
13       turbovnc
14     ];
16     programs.turbovnc.ensureHeadlessSoftwareOpenGL = true;
18     networking.firewall = {
19       # Reject instead of drop, for failures instead of hangs.
20       rejectPackets = true;
21       allowedTCPPorts = [
22         5900 # VNC :0, for seeing what's going on in the server
23       ];
24     };
26     # So that we can ssh into the VM, see e.g.
27     # http://blog.patapon.info/nixos-local-vm/#accessing-the-vm-with-ssh
28     services.openssh.enable = true;
29     services.openssh.permitRootLogin = "yes";
30     users.extraUsers.root.password = "";
31     users.mutableUsers = false;
32   };
34   testScript = ''
35     def wait_until_terminated_or_succeeds(
36         termination_check_shell_command,
37         success_check_shell_command,
38         get_detail_message_fn,
39         retries=60,
40         retry_sleep=0.5,
41     ):
42         def check_success():
43             command_exit_code, _output = machine.execute(success_check_shell_command)
44             return command_exit_code == 0
46         for _ in range(retries):
47             exit_check_exit_code, _output = machine.execute(termination_check_shell_command)
48             is_terminated = exit_check_exit_code != 0
49             if is_terminated:
50                 if check_success():
51                     return
52                 else:
53                     details = get_detail_message_fn()
54                     raise Exception(
55                         f"termination check ({termination_check_shell_command}) triggered without command succeeding ({success_check_shell_command}); details: {details}"
56                     )
57             else:
58                 if check_success():
59                     return
60             import time
61             time.sleep(retry_sleep)
63         if not check_success():
64             details = get_detail_message_fn()
65             raise Exception(
66                 f"action timed out ({success_check_shell_command}); details: {details}"
67             )
70     # Below we use the pattern:
71     #     (cmd | tee stdout.log) 3>&1 1>&2 2>&3 | tee stderr.log
72     # to capture both stderr and stdout while also teeing them, see:
73     # https://unix.stackexchange.com/questions/6430/how-to-redirect-stderr-and-stdout-to-different-files-and-also-display-in-termina/6431#6431
76     # Starts headless VNC server, backgrounding it.
77     def start_xvnc():
78         xvnc_command = " ".join(
79             [
80                 "Xvnc",
81                 ":0",
82                 "-iglx",
83                 "-auth /root/.Xauthority",
84                 "-geometry 1240x900",
85                 "-depth 24",
86                 "-rfbwait 5000",
87                 "-deferupdate 1",
88                 "-verbose",
89                 "-securitytypes none",
90                 # We don't enforce localhost listening such that we
91                 # can connect from outside the VM using
92                 #     env QEMU_NET_OPTS=hostfwd=tcp::5900-:5900 $(nix-build nixos/tests/turbovnc-headless-server.nix -A driver)/bin/nixos-test-driver
93                 # for testing purposes, and so that we can in the future
94                 # add another test case that connects the TurboVNC client.
95                 # "-localhost",
96             ]
97         )
98         machine.execute(
99             # Note trailing & for backgrounding.
100             f"({xvnc_command} | tee /tmp/Xvnc.stdout) 3>&1 1>&2 2>&3 | tee /tmp/Xvnc.stderr >&2 &",
101         )
104     # Waits until the server log message that tells us that GLX is ready
105     # (requires `-verbose` above), avoiding screenshoting racing below.
106     def wait_until_xvnc_glx_ready():
107         machine.wait_until_succeeds("test -f /tmp/Xvnc.stderr")
108         wait_until_terminated_or_succeeds(
109             termination_check_shell_command="pidof Xvnc",
110             success_check_shell_command="grep 'GLX: Initialized DRISWRAST' /tmp/Xvnc.stderr",
111             get_detail_message_fn=lambda: "Contents of /tmp/Xvnc.stderr:\n"
112             + machine.succeed("cat /tmp/Xvnc.stderr"),
113         )
116     # Checks that we detect glxgears failing when
117     # `LIBGL_DRIVERS_PATH=/nonexistent` is set
118     # (in which case software rendering should not work).
119     def test_glxgears_failing_with_bad_driver_path():
120         machine.execute(
121             # Note trailing & for backgrounding.
122             "(env DISPLAY=:0 LIBGL_DRIVERS_PATH=/nonexistent glxgears -info | tee /tmp/glxgears-should-fail.stdout) 3>&1 1>&2 2>&3 | tee /tmp/glxgears-should-fail.stderr >&2 &"
123         )
124         machine.wait_until_succeeds("test -f /tmp/glxgears-should-fail.stderr")
125         wait_until_terminated_or_succeeds(
126             termination_check_shell_command="pidof glxgears",
127             success_check_shell_command="grep 'libGL error: failed to load driver: swrast' /tmp/glxgears-should-fail.stderr",
128             get_detail_message_fn=lambda: "Contents of /tmp/glxgears-should-fail.stderr:\n"
129             + machine.succeed("cat /tmp/glxgears-should-fail.stderr"),
130         )
131         machine.wait_until_fails("pidof glxgears")
134     # Starts glxgears, backgrounding it. Waits until it prints the `GL_RENDERER`.
135     # Does not quit glxgears.
136     def test_glxgears_prints_renderer():
137         machine.execute(
138             # Note trailing & for backgrounding.
139             "(env DISPLAY=:0 glxgears -info | tee /tmp/glxgears.stdout) 3>&1 1>&2 2>&3 | tee /tmp/glxgears.stderr >&2 &"
140         )
141         machine.wait_until_succeeds("test -f /tmp/glxgears.stderr")
142         wait_until_terminated_or_succeeds(
143             termination_check_shell_command="pidof glxgears",
144             success_check_shell_command="grep 'GL_RENDERER' /tmp/glxgears.stdout",
145             get_detail_message_fn=lambda: "Contents of /tmp/glxgears.stderr:\n"
146             + machine.succeed("cat /tmp/glxgears.stderr"),
147         )
150     with subtest("Start Xvnc"):
151         start_xvnc()
152         wait_until_xvnc_glx_ready()
154     with subtest("Ensure bad driver path makes glxgears fail"):
155         test_glxgears_failing_with_bad_driver_path()
157     with subtest("Run 3D application (glxgears)"):
158         test_glxgears_prints_renderer()
160         # Take screenshot; should display the glxgears.
161         machine.succeed("scrot --display :0 /tmp/glxgears.png")
163     # Copy files down.
164     machine.copy_from_vm("/tmp/glxgears.png")
165     machine.copy_from_vm("/tmp/glxgears.stdout")
166     machine.copy_from_vm("/tmp/glxgears-should-fail.stdout")
167     machine.copy_from_vm("/tmp/glxgears-should-fail.stderr")
168     machine.copy_from_vm("/tmp/Xvnc.stdout")
169     machine.copy_from_vm("/tmp/Xvnc.stderr")
170   '';