{ system ? builtins.currentSystem, config ? { }, pkgs ? import ../.. { inherit system config; }, }: with import ../lib/testing-python.nix { inherit system pkgs; }; let user = "alice"; client = { pkgs, ... }: { imports = [ ./common/user-account.nix ./common/x11.nix ]; hardware.graphics.enable = true; virtualisation.memorySize = 384; environment = { systemPackages = [ pkgs.armagetronad ]; variables.XAUTHORITY = "/home/${user}/.Xauthority"; }; test-support.displayManager.auto.user = user; }; in makeTest { name = "armagetronad"; meta = with pkgs.lib.maintainers; { maintainers = [ numinit ]; }; enableOCR = true; nodes = { server = { services.armagetronad.servers = { high-rubber = { enable = true; name = "Smoke Test High Rubber Server"; port = 4534; settings = { SERVER_OPTIONS = "High Rubber server made to run smoke tests."; CYCLE_RUBBER = 40; SIZE_FACTOR = 0.5; }; roundSettings = { SAY = [ "NixOS Smoke Test Server" "https://nixos.org" ]; }; }; sty = { enable = true; name = "Smoke Test sty+ct+ap Server"; package = pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated; port = 4535; settings = { SERVER_OPTIONS = "sty+ct+ap server made to run smoke tests."; CYCLE_RUBBER = 20; SIZE_FACTOR = 0.5; }; roundSettings = { SAY = [ "NixOS Smoke Test sty+ct+ap Server" "https://nixos.org" ]; }; }; trunk = { enable = true; name = "Smoke Test trunk Server"; package = pkgs.armagetronad."0.4".dedicated; port = 4536; settings = { SERVER_OPTIONS = "0.4 server made to run smoke tests."; CYCLE_RUBBER = 20; SIZE_FACTOR = 0.5; }; roundSettings = { SAY = [ "NixOS Smoke Test 0.4 Server" "https://nixos.org" ]; }; }; }; }; client1 = client; client2 = client; }; testScript = let xdo = name: text: let xdoScript = pkgs.writeText "${name}.xdo" text; in "${pkgs.xdotool}/bin/xdotool ${xdoScript}"; in '' import shlex import threading from collections import namedtuple class Client(namedtuple('Client', ('node', 'name'))): def send(self, *keys): for key in keys: self.node.send_key(key) def send_on(self, text, *keys): self.node.wait_for_text(text) self.send(*keys) Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'attacker', 'victim', 'coredump_delay')) # Clients and their in-game names clients = ( Client(client1, 'Arduino'), Client(client2, 'SmOoThIcE') ) # Server configs. servers = ( Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino', 8), Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE', 8), Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE', 8) ) """ Runs a command as the client user. """ def run(cmd): return "su - ${user} -c " + shlex.quote(cmd) screenshot_idx = 1 """ Takes screenshots on all clients. """ def take_screenshots(screenshot_idx): for client in clients: client.node.screenshot(f"screen_{client.name}_{screenshot_idx}") return screenshot_idx + 1 # Wait for the servers to come up. start_all() for srv in servers: srv.node.wait_for_unit(f"armagetronad-{srv.name}") srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}") # Make sure console commands work through the named pipe we created. for srv in servers: srv.node.succeed( f"echo 'say Testing!' >> /var/lib/armagetronad/{srv.name}/input" ) srv.node.succeed( f"echo 'say Testing again!' >> /var/lib/armagetronad/{srv.name}/input" ) srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing!'" ) srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'" ) """ Sets up a client, waiting for the given barrier on completion. """ def client_setup(client, servers, barrier): client.node.wait_for_x() # Configure Armagetron. client.node.succeed( run("mkdir -p ~/.armagetronad/var"), run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg") ) for idx, srv in enumerate(servers): client.node.succeed( run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"), run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"), run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg") ) # Start Armagetron. client.node.succeed(run("ulimit -c unlimited; armagetronad >&2 & disown")) client.node.wait_until_succeeds( run( "${xdo "create_new_win-select_main_window" '' search --onlyvisible --name "Armagetron Advanced" windowfocus --sync windowactivate --sync ''}" ) ) # Get through the tutorial. client.send_on('Language Settings', 'ret') client.send_on('First Setup', 'ret') client.send_on('Welcome to Armagetron Advanced', 'ret') client.send_on('round 1', 'esc') client.send_on('Menu', 'up', 'up', 'ret') client.send_on('We hope you', 'ret') client.send_on('Armagetron Advanced', 'ret') client.send_on('Play Game', 'ret') # Online > LAN > Network Setup > Mates > Server Bookmarks client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret') barrier.wait() # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously. barrier = threading.Barrier(len(clients) + 1, timeout=240) for client in clients: threading.Thread(target=client_setup, args=(client, servers, barrier)).start() barrier.wait() # Main testing loop. Iterates through each server bookmark and connects to them in sequence. # Assumes that the game is currently on the Server Bookmarks screen. for srv in servers: screenshot_idx = take_screenshots(screenshot_idx) # Connect both clients at once, one second apart. for client in clients: client.send('ret') client.node.sleep(1) # Wait for clients to connect for client in clients: srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*entered the game'" ) # Wait for the match to start srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: {srv.welcome}'" ) srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: https://nixos.org'" ) srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'" ) # Wait a bit srv.node.sleep(srv.coredump_delay) # Turn the attacker player's lightcycle left attacker = next(client for client in clients if client.name == srv.attacker) victim = next(client for client in clients if client.name == srv.victim) attacker.send('left') screenshot_idx = take_screenshots(screenshot_idx) # Wait for coredump. srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q '{attacker.name} core dumped {victim.name}'" ) screenshot_idx = take_screenshots(screenshot_idx) # Disconnect both clients from the server for client in clients: client.send('esc') client.send_on('Menu', 'up', 'up', 'ret') srv.node.wait_until_succeeds( f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*left the game'" ) # Next server. for client in clients: client.send_on('Server Bookmarks', 'down') # Stop the servers for srv in servers: srv.node.succeed( f"systemctl stop armagetronad-{srv.name}" ) srv.node.wait_until_fails(f"ss --numeric --udp --listening | grep -q {srv.port}") ''; }