at master 8.9 kB view raw
1{ 2 lib, 3 pkgs, 4 ... 5}: 6let 7 user = "alice"; 8 9 client = 10 { pkgs, ... }: 11 12 { 13 imports = [ 14 ./common/user-account.nix 15 ./common/x11.nix 16 ]; 17 hardware.graphics.enable = true; 18 virtualisation.memorySize = 384; 19 environment = { 20 systemPackages = [ pkgs.armagetronad ]; 21 variables.XAUTHORITY = "/home/${user}/.Xauthority"; 22 }; 23 test-support.displayManager.auto.user = user; 24 }; 25 26in 27{ 28 name = "armagetronad"; 29 meta = with lib.maintainers; { 30 maintainers = [ numinit ]; 31 }; 32 33 enableOCR = true; 34 35 nodes = { 36 server = { 37 services.armagetronad.servers = { 38 high-rubber = { 39 enable = true; 40 name = "Smoke Test High Rubber Server"; 41 port = 4534; 42 settings = { 43 SERVER_OPTIONS = "High Rubber server made to run smoke tests."; 44 CYCLE_RUBBER = 40; 45 SIZE_FACTOR = 0.5; 46 }; 47 roundSettings = { 48 SAY = [ 49 "NixOS Smoke Test Server" 50 "https://nixos.org" 51 ]; 52 }; 53 }; 54 sty = { 55 enable = true; 56 name = "Smoke Test sty+ct+ap Server"; 57 package = pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated; 58 port = 4535; 59 settings = { 60 SERVER_OPTIONS = "sty+ct+ap server made to run smoke tests."; 61 CYCLE_RUBBER = 20; 62 SIZE_FACTOR = 0.5; 63 }; 64 roundSettings = { 65 SAY = [ 66 "NixOS Smoke Test sty+ct+ap Server" 67 "https://nixos.org" 68 ]; 69 }; 70 }; 71 trunk = { 72 enable = true; 73 name = "Smoke Test trunk Server"; 74 package = pkgs.armagetronad."0.4".dedicated; 75 port = 4536; 76 settings = { 77 SERVER_OPTIONS = "0.4 server made to run smoke tests."; 78 CYCLE_RUBBER = 20; 79 SIZE_FACTOR = 0.5; 80 }; 81 roundSettings = { 82 SAY = [ 83 "NixOS Smoke Test 0.4 Server" 84 "https://nixos.org" 85 ]; 86 }; 87 }; 88 }; 89 }; 90 91 client1 = client; 92 client2 = client; 93 }; 94 95 testScript = 96 let 97 xdo = 98 name: text: 99 let 100 xdoScript = pkgs.writeText "${name}.xdo" text; 101 in 102 "${pkgs.xdotool}/bin/xdotool ${xdoScript}"; 103 in 104 '' 105 import shlex 106 import threading 107 from collections import namedtuple 108 109 class Client(namedtuple('Client', ('node', 'name'))): 110 def send(self, *keys): 111 for key in keys: 112 self.node.send_key(key) 113 114 def send_on(self, text, *keys): 115 self.node.wait_for_text(text) 116 self.send(*keys) 117 118 Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'player1', 'player2')) 119 120 # Clients and their in-game names 121 clients = ( 122 Client(client1, 'Arduino'), 123 Client(client2, 'SmOoThIcE') 124 ) 125 126 # Server configs. 127 servers = ( 128 Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino'), 129 Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE'), 130 Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE') 131 ) 132 133 """ 134 Runs a command as the client user. 135 """ 136 def run(cmd): 137 return "su - ${user} -c " + shlex.quote(cmd) 138 139 screenshot_idx = 1 140 141 """ 142 Takes screenshots on all clients. 143 """ 144 def take_screenshots(screenshot_idx): 145 for client in clients: 146 client.node.screenshot(f"screen_{client.name}_{screenshot_idx}") 147 return screenshot_idx + 1 148 149 """ 150 Sets up a client, waiting for the given barrier on completion. 151 """ 152 def client_setup(client, servers, barrier): 153 client.node.wait_for_x() 154 155 # Configure Armagetron so we skip the tutorial. 156 client.node.succeed( 157 run("mkdir -p ~/.armagetronad/var"), 158 run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg"), 159 run("echo 'FIRST_USE 0' >> ~/.armagetronad/var/autoexec.cfg") 160 ) 161 for idx, srv in enumerate(servers): 162 client.node.succeed( 163 run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"), 164 run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"), 165 run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg") 166 ) 167 168 # Start Armagetron. Use the recording mode since it skips the splashscreen. 169 client.node.succeed(run("cd; ulimit -c unlimited; armagetronad --record test.aarec >&2 & disown")) 170 client.node.wait_until_succeeds( 171 run( 172 "${xdo "create_new_win-select_main_window" '' 173 search --onlyvisible --name "Armagetron Advanced" 174 windowfocus --sync 175 windowactivate --sync 176 ''}" 177 ) 178 ) 179 180 # Get into the multiplayer menu. 181 client.send_on('Armagetron Advanced', 'ret') 182 client.send_on('Play Game', 'ret') 183 184 # Online > LAN > Network Setup > Mates > Server Bookmarks 185 client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret') 186 187 barrier.wait() 188 189 # Start everything. 190 start_all() 191 192 # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously. 193 barrier = threading.Barrier(len(clients) + 1, timeout=600) 194 for client in clients: 195 threading.Thread(target=client_setup, args=(client, servers, barrier)).start() 196 197 # Wait for the servers to come up. 198 for srv in servers: 199 srv.node.wait_for_unit(f"armagetronad-{srv.name}") 200 srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}") 201 202 # Make sure console commands work through the named pipe we created. 203 for srv in servers: 204 srv.node.succeed( 205 f"echo 'say Testing!' >> /var/lib/armagetronad/{srv.name}/input" 206 ) 207 srv.node.succeed( 208 f"echo 'say Testing again!' >> /var/lib/armagetronad/{srv.name}/input" 209 ) 210 srv.node.wait_until_succeeds( 211 f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing!'" 212 ) 213 srv.node.wait_until_succeeds( 214 f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'" 215 ) 216 217 # Wait for the client setup to complete. 218 barrier.wait() 219 220 # Main testing loop. Iterates through each server bookmark and connects to them in sequence. 221 # Assumes that the game is currently on the Server Bookmarks screen. 222 for srv in servers: 223 screenshot_idx = take_screenshots(screenshot_idx) 224 225 # Connect both clients at once, one second apart. 226 for client in clients: 227 client.send('ret') 228 client.node.sleep(1) 229 230 # Wait for clients to connect 231 for client in clients: 232 srv.node.wait_until_succeeds( 233 f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*entered the game'" 234 ) 235 236 # Wait for the match to start 237 srv.node.wait_until_succeeds( 238 f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: {srv.welcome}'" 239 ) 240 srv.node.wait_until_succeeds( 241 f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: https://nixos.org'" 242 ) 243 srv.node.wait_until_succeeds( 244 f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'" 245 ) 246 247 # Wait for the players to die by running into the wall. 248 player1 = next(client for client in clients if client.name == srv.player1) 249 player2 = next(client for client in clients if client.name == srv.player2) 250 srv.node.wait_until_succeeds( 251 f"journalctl -u armagetronad-{srv.name} -e | grep -q '{player1.name}.*lost 4 points'" 252 ) 253 srv.node.wait_until_succeeds( 254 f"journalctl -u armagetronad-{srv.name} -e | grep -q '{player2.name}.*lost 4 points'" 255 ) 256 screenshot_idx = take_screenshots(screenshot_idx) 257 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 ) 265 266 # Next server. 267 for client in clients: 268 client.send_on('Server Bookmarks', 'down') 269 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 ''; 277 278}