vuls: init at 0.27.0
[NixPkgs.git] / nixos / tests / armagetronad.nix
blobb657893fc9eef316702249452e8d9371ecf63a14
1 { system ? builtins.currentSystem,
2   config ? {},
3   pkgs ? import ../.. { inherit system config; }
4 }:
6 with import ../lib/testing-python.nix { inherit system pkgs; };
8 let
9   user = "alice";
11   client =
12     { pkgs, ... }:
14     { imports = [ ./common/user-account.nix ./common/x11.nix ];
15       hardware.graphics.enable = true;
16       virtualisation.memorySize = 384;
17       environment = {
18         systemPackages = [ pkgs.armagetronad ];
19         variables.XAUTHORITY = "/home/${user}/.Xauthority";
20       };
21       test-support.displayManager.auto.user = user;
22     };
25 makeTest {
26   name = "armagetronad";
27   meta = with pkgs.lib.maintainers; {
28     maintainers = [ numinit ];
29   };
31   enableOCR = true;
33   nodes =
34     {
35       server = {
36         services.armagetronad.servers = {
37           high-rubber = {
38             enable = true;
39             name = "Smoke Test High Rubber Server";
40             port = 4534;
41             settings = {
42               SERVER_OPTIONS = "High Rubber server made to run smoke tests.";
43               CYCLE_RUBBER = 40;
44               SIZE_FACTOR = 0.5;
45             };
46             roundSettings = {
47               SAY = [
48                 "NixOS Smoke Test Server"
49                 "https://nixos.org"
50               ];
51             };
52           };
53           sty = {
54             enable = true;
55             name = "Smoke Test sty+ct+ap Server";
56             package = pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated;
57             port = 4535;
58             settings = {
59               SERVER_OPTIONS = "sty+ct+ap server made to run smoke tests.";
60               CYCLE_RUBBER = 20;
61               SIZE_FACTOR = 0.5;
62             };
63             roundSettings = {
64               SAY = [
65                 "NixOS Smoke Test sty+ct+ap Server"
66                 "https://nixos.org"
67               ];
68             };
69           };
70           trunk = {
71             enable = true;
72             name = "Smoke Test trunk Server";
73             package = pkgs.armagetronad."0.4".dedicated;
74             port = 4536;
75             settings = {
76               SERVER_OPTIONS = "0.4 server made to run smoke tests.";
77               CYCLE_RUBBER = 20;
78               SIZE_FACTOR = 0.5;
79             };
80             roundSettings = {
81               SAY = [
82                 "NixOS Smoke Test 0.4 Server"
83                 "https://nixos.org"
84               ];
85             };
86           };
87         };
88       };
90       client1 = client;
91       client2 = client;
92     };
94   testScript = let
95     xdo = name: text: let
96       xdoScript = pkgs.writeText "${name}.xdo" text;
97     in "${pkgs.xdotool}/bin/xdotool ${xdoScript}";
98   in
99     ''
100       import shlex
101       import threading
102       from collections import namedtuple
104       class Client(namedtuple('Client', ('node', 'name'))):
105         def send(self, *keys):
106           for key in keys:
107             self.node.send_key(key)
109         def send_on(self, text, *keys):
110           self.node.wait_for_text(text)
111           self.send(*keys)
113       Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'attacker', 'victim', 'coredump_delay'))
115       # Clients and their in-game names
116       clients = (
117         Client(client1, 'Arduino'),
118         Client(client2, 'SmOoThIcE')
119       )
121       # Server configs.
122       servers = (
123         Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino', 8),
124         Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE', 8),
125         Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE', 8)
126       )
128       """
129       Runs a command as the client user.
130       """
131       def run(cmd):
132         return "su - ${user} -c " + shlex.quote(cmd)
134       screenshot_idx = 1
136       """
137       Takes screenshots on all clients.
138       """
139       def take_screenshots(screenshot_idx):
140         for client in clients:
141           client.node.screenshot(f"screen_{client.name}_{screenshot_idx}")
142         return screenshot_idx + 1
144       # Wait for the servers to come up.
145       start_all()
146       for srv in servers:
147         srv.node.wait_for_unit(f"armagetronad-{srv.name}")
148         srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}")
150       # Make sure console commands work through the named pipe we created.
151       for srv in servers:
152         srv.node.succeed(
153           f"echo 'say Testing!' >> /var/lib/armagetronad/{srv.name}/input"
154         )
155         srv.node.succeed(
156           f"echo 'say Testing again!' >> /var/lib/armagetronad/{srv.name}/input"
157         )
158         srv.node.wait_until_succeeds(
159           f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing!'"
160         )
161         srv.node.wait_until_succeeds(
162           f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'"
163         )
165       """
166       Sets up a client, waiting for the given barrier on completion.
167       """
168       def client_setup(client, servers, barrier):
169         client.node.wait_for_x()
171         # Configure Armagetron.
172         client.node.succeed(
173           run("mkdir -p ~/.armagetronad/var"),
174           run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg")
175         )
176         for idx, srv in enumerate(servers):
177           client.node.succeed(
178             run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"),
179             run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"),
180             run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg")
181           )
183         # Start Armagetron.
184         client.node.succeed(run("ulimit -c unlimited; armagetronad >&2 & disown"))
185         client.node.wait_until_succeeds(
186           run(
187             "${xdo "create_new_win-select_main_window" ''
188               search --onlyvisible --name "Armagetron Advanced"
189               windowfocus --sync
190               windowactivate --sync
191             ''}"
192           )
193         )
195         # Get through the tutorial.
196         client.send_on('Language Settings', 'ret')
197         client.send_on('First Setup', 'ret')
198         client.send_on('Welcome to Armagetron Advanced', 'ret')
199         client.send_on('round 1', 'esc')
200         client.send_on('Menu', 'up', 'up', 'ret')
201         client.send_on('We hope you', 'ret')
202         client.send_on('Armagetron Advanced', 'ret')
203         client.send_on('Play Game', 'ret')
205         # Online > LAN > Network Setup > Mates > Server Bookmarks
206         client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret')
208         barrier.wait()
210       # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously.
211       barrier = threading.Barrier(len(clients) + 1, timeout=240)
212       for client in clients:
213         threading.Thread(target=client_setup, args=(client, servers, barrier)).start()
214       barrier.wait()
216       # Main testing loop. Iterates through each server bookmark and connects to them in sequence.
217       # Assumes that the game is currently on the Server Bookmarks screen.
218       for srv in servers:
219         screenshot_idx = take_screenshots(screenshot_idx)
221         # Connect both clients at once, one second apart.
222         for client in clients:
223           client.send('ret')
224           client.node.sleep(1)
226         # Wait for clients to connect
227         for client in clients:
228           srv.node.wait_until_succeeds(
229             f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*entered the game'"
230           )
232         # Wait for the match to start
233         srv.node.wait_until_succeeds(
234           f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: {srv.welcome}'"
235         )
236         srv.node.wait_until_succeeds(
237           f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: https://nixos.org'"
238         )
239         srv.node.wait_until_succeeds(
240           f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'"
241         )
243         # Wait a bit
244         srv.node.sleep(srv.coredump_delay)
246         # Turn the attacker player's lightcycle left
247         attacker = next(client for client in clients if client.name == srv.attacker)
248         victim = next(client for client in clients if client.name == srv.victim)
249         attacker.send('left')
250         screenshot_idx = take_screenshots(screenshot_idx)
252         # Wait for coredump.
253         srv.node.wait_until_succeeds(
254           f"journalctl -u armagetronad-{srv.name} -e | grep -q '{attacker.name} core dumped {victim.name}'"
255         )
256         screenshot_idx = take_screenshots(screenshot_idx)
258         # Disconnect both clients from the server
259         for client in clients:
260           client.send('esc')
261           client.send_on('Menu', 'up', 'up', 'ret')
262           srv.node.wait_until_succeeds(
263             f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*left the game'"
264           )
266         # Next server.
267         for client in clients:
268           client.send_on('Server Bookmarks', 'down')
270       # Stop the servers
271       for srv in servers:
272         srv.node.succeed(
273           f"systemctl stop armagetronad-{srv.name}"
274         )
275         srv.node.wait_until_fails(f"ss --numeric --udp --listening | grep -q {srv.port}")
276     '';