1import ./make-test-python.nix (
2 { pkgs, lib, ... }:
3 {
4 name = "swayfx";
5 meta = {
6 maintainers = with lib.maintainers; [ ];
7 };
8
9 # testScriptWithTypes:49: error: Cannot call function of unknown type
10 # (machine.succeed if succeed else machine.execute)(
11 # ^
12 # Found 1 error in 1 file (checked 1 source file)
13 skipTypeCheck = true;
14
15 nodes.machine = {
16 # Automatically login on tty1 as a normal user:
17 imports = [ ./common/user-account.nix ];
18 services.getty.autologinUser = "alice";
19
20 environment = {
21 # For glinfo and wayland-info:
22 systemPackages = with pkgs; [
23 mesa-demos
24 wayland-utils
25 alacritty
26 ];
27 # Use a fixed SWAYSOCK path (for swaymsg):
28 variables = {
29 "SWAYSOCK" = "/tmp/sway-ipc.sock";
30 # TODO: Investigate if we can get hardware acceleration to work (via
31 # virtio-gpu and Virgil). We currently have to use the Pixman software
32 # renderer since the GLES2 renderer doesn't work inside the VM (even
33 # with WLR_RENDERER_ALLOW_SOFTWARE):
34 # "WLR_RENDERER_ALLOW_SOFTWARE" = "1";
35 "WLR_RENDERER" = "pixman";
36 };
37 # For convenience:
38 shellAliases = {
39 test-x11 = "glinfo | tee /tmp/test-x11.out && touch /tmp/test-x11-exit-ok";
40 test-wayland = "wayland-info | tee /tmp/test-wayland.out && touch /tmp/test-wayland-exit-ok";
41 };
42
43 # To help with OCR:
44 etc."xdg/foot/foot.ini".text = lib.generators.toINI { } {
45 main = {
46 font = "inconsolata:size=14";
47 };
48 colors = rec {
49 foreground = "000000";
50 background = "ffffff";
51 regular2 = foreground;
52 };
53 };
54
55 etc."gpg-agent.conf".text = ''
56 pinentry-timeout 86400
57 '';
58 };
59
60 fonts.packages = [ pkgs.inconsolata ];
61
62 # Automatically configure and start Sway when logging in on tty1:
63 programs.bash.loginShellInit = ''
64 if [ "$(tty)" = "/dev/tty1" ]; then
65 set -e
66
67 mkdir -p ~/.config/sway
68 sed s/Mod4/Mod1/ /etc/sway/config > ~/.config/sway/config
69
70 sway --validate
71 sway && touch /tmp/sway-exit-ok
72 fi
73 '';
74
75 programs.sway = {
76 enable = true;
77 package = pkgs.swayfx.override { isNixOS = true; };
78 };
79
80 # To test pinentry via gpg-agent:
81 programs.gnupg.agent.enable = true;
82
83 # Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
84 virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
85 };
86
87 testScript =
88 { nodes, ... }:
89 ''
90 import shlex
91 import json
92
93 q = shlex.quote
94 NODE_GROUPS = ["nodes", "floating_nodes"]
95
96
97 def swaymsg(command: str = "", succeed=True, type="command"):
98 assert command != "" or type != "command", "Must specify command or type"
99 shell = q(f"swaymsg -t {q(type)} -- {q(command)}")
100 with machine.nested(
101 f"sending swaymsg {shell!r}" + " (allowed to fail)" * (not succeed)
102 ):
103 ret = (machine.succeed if succeed else machine.execute)(
104 f"su - alice -c {shell}"
105 )
106
107 # execute also returns a status code, but disregard.
108 if not succeed:
109 _, ret = ret
110
111 if not succeed and not ret:
112 return None
113
114 parsed = json.loads(ret)
115 return parsed
116
117
118 def walk(tree):
119 yield tree
120 for group in NODE_GROUPS:
121 for node in tree.get(group, []):
122 yield from walk(node)
123
124
125 def wait_for_window(pattern):
126 def func(last_chance):
127 nodes = (node["name"] for node in walk(swaymsg(type="get_tree")))
128
129 if last_chance:
130 nodes = list(nodes)
131 machine.log(f"Last call! Current list of windows: {nodes}")
132
133 return any(pattern in name for name in nodes)
134
135 retry(func)
136
137 start_all()
138 machine.wait_for_unit("multi-user.target")
139
140 # To check the version:
141 print(machine.succeed("sway --version"))
142
143 # Wait for Sway to complete startup:
144 machine.wait_for_file("/run/user/1000/wayland-1")
145 machine.wait_for_file("/tmp/sway-ipc.sock")
146
147 # Test XWayland (foot does not support X):
148 swaymsg("exec WINIT_UNIX_BACKEND=x11 WAYLAND_DISPLAY= alacritty")
149 wait_for_window("alice@machine")
150 machine.send_chars("test-x11\n")
151 machine.wait_for_file("/tmp/test-x11-exit-ok")
152 print(machine.succeed("cat /tmp/test-x11.out"))
153 machine.copy_from_vm("/tmp/test-x11.out")
154 machine.screenshot("alacritty_glinfo")
155 machine.succeed("pkill alacritty")
156
157 # Start a terminal (foot) on workspace 3:
158 machine.send_key("alt-3")
159 machine.sleep(3)
160 machine.send_key("alt-ret")
161 wait_for_window("alice@machine")
162 machine.send_chars("test-wayland\n")
163 machine.wait_for_file("/tmp/test-wayland-exit-ok")
164 print(machine.succeed("cat /tmp/test-wayland.out"))
165 machine.copy_from_vm("/tmp/test-wayland.out")
166 machine.screenshot("foot_wayland_info")
167 machine.send_key("alt-shift-q")
168 machine.wait_until_fails("pgrep foot")
169
170 # Test gpg-agent starting pinentry-gnome3 via D-Bus (tests if
171 # $WAYLAND_DISPLAY is correctly imported into the D-Bus user env):
172 swaymsg("exec mkdir -p ~/.gnupg")
173 swaymsg("exec cp /etc/gpg-agent.conf ~/.gnupg")
174
175 swaymsg("exec DISPLAY=INVALID gpg --no-tty --yes --quick-generate-key test", succeed=False)
176 machine.wait_until_succeeds("pgrep --exact gpg")
177 wait_for_window("gpg")
178 machine.succeed("pgrep --exact gpg")
179 machine.screenshot("gpg_pinentry")
180 machine.send_key("alt-shift-q")
181 machine.wait_until_fails("pgrep --exact gpg")
182
183 # Test swaynag:
184 def get_height():
185 return [node['rect']['height'] for node in walk(swaymsg(type="get_tree")) if node['focused']][0]
186
187 before = get_height()
188 machine.send_key("alt-shift-e")
189 retry(lambda _: get_height() < before)
190 machine.screenshot("sway_exit")
191
192 swaymsg("exec swaylock")
193 machine.wait_until_succeeds("pgrep -xf swaylock")
194 machine.sleep(3)
195 machine.send_chars("${nodes.machine.users.users.alice.password}")
196 machine.send_key("ret")
197 machine.wait_until_fails("pgrep -xf swaylock")
198
199 # Exit Sway and verify process exit status 0:
200 swaymsg("exit", succeed=False)
201 machine.wait_until_fails("pgrep -xf sway")
202 machine.wait_for_file("/tmp/sway-exit-ok")
203 '';
204 }
205)