diff options
Diffstat (limited to 'nixpkgs/nixos/tests')
-rw-r--r-- | nixpkgs/nixos/tests/all-tests.nix | 5 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/armagetronad.nix | 272 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/boot.nix | 82 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/common/ec2.nix | 2 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/consul.nix | 4 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/docker-tools.nix | 22 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/incus/container.nix | 13 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/installer.nix | 46 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/k3s/default.nix | 5 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/k3s/etcd.nix | 100 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/kernel-generic.nix | 1 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/lomiri-system-settings.nix | 99 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/mealie.nix | 24 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/monado.nix | 39 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/plasma6.nix | 64 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/searx.nix | 6 | ||||
-rw-r--r-- | nixpkgs/nixos/tests/systemd-boot.nix | 115 |
17 files changed, 835 insertions, 64 deletions
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix index 1144a5611dcf..231767ca2b97 100644 --- a/nixpkgs/nixos/tests/all-tests.nix +++ b/nixpkgs/nixos/tests/all-tests.nix @@ -128,6 +128,7 @@ in { appliance-repart-image = runTest ./appliance-repart-image.nix; apparmor = handleTest ./apparmor.nix {}; archi = handleTest ./archi.nix {}; + armagetronad = handleTest ./armagetronad.nix {}; atd = handleTest ./atd.nix {}; atop = handleTest ./atop.nix {}; atuin = handleTest ./atuin.nix {}; @@ -497,6 +498,7 @@ in { lxd = pkgs.recurseIntoAttrs (handleTest ./lxd { inherit handleTestOn; }); lxd-image-server = handleTest ./lxd-image-server.nix {}; #logstash = handleTest ./logstash.nix {}; + lomiri-system-settings = handleTest ./lomiri-system-settings.nix {}; lorri = handleTest ./lorri/default.nix {}; maddy = discoverTests (import ./maddy { inherit handleTest; }); maestral = handleTest ./maestral.nix {}; @@ -516,6 +518,7 @@ in { matrix-synapse = handleTest ./matrix/synapse.nix {}; matrix-synapse-workers = handleTest ./matrix/synapse-workers.nix {}; mattermost = handleTest ./mattermost.nix {}; + mealie = handleTest ./mealie.nix {}; mediamtx = handleTest ./mediamtx.nix {}; mediatomb = handleTest ./mediatomb.nix {}; mediawiki = handleTest ./mediawiki.nix {}; @@ -535,6 +538,7 @@ in { mobilizon = handleTest ./mobilizon.nix {}; mod_perl = handleTest ./mod_perl.nix {}; molly-brown = handleTest ./molly-brown.nix {}; + monado = handleTest ./monado.nix {}; monica = handleTest ./web-apps/monica.nix {}; mongodb = handleTest ./mongodb.nix {}; moodle = handleTest ./moodle.nix {}; @@ -692,6 +696,7 @@ in { plantuml-server = handleTest ./plantuml-server.nix {}; plasma-bigscreen = handleTest ./plasma-bigscreen.nix {}; plasma5 = handleTest ./plasma5.nix {}; + plasma6 = handleTest ./plasma6.nix {}; plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {}; plausible = handleTest ./plausible.nix {}; please = handleTest ./please.nix {}; diff --git a/nixpkgs/nixos/tests/armagetronad.nix b/nixpkgs/nixos/tests/armagetronad.nix new file mode 100644 index 000000000000..ff2841dedd21 --- /dev/null +++ b/nixpkgs/nixos/tests/armagetronad.nix @@ -0,0 +1,272 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +let + user = "alice"; + + client = + { pkgs, ... }: + + { imports = [ ./common/user-account.nix ./common/x11.nix ]; + hardware.opengl.driSupport = true; + virtualisation.memorySize = 256; + environment = { + systemPackages = [ pkgs.armagetronad ]; + variables.XAUTHORITY = "/home/${user}/.Xauthority"; + }; + test-support.displayManager.auto.user = user; + }; + +in { + 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(3, timeout=120) + 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}") + ''; + +}) diff --git a/nixpkgs/nixos/tests/boot.nix b/nixpkgs/nixos/tests/boot.nix index ec2a9f6527c9..56f72dddf526 100644 --- a/nixpkgs/nixos/tests/boot.nix +++ b/nixpkgs/nixos/tests/boot.nix @@ -4,10 +4,41 @@ }: with import ../lib/testing-python.nix { inherit system pkgs; }; -with pkgs.lib; let - qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + lib = pkgs.lib; + qemu-common = import ../lib/qemu-common.nix { inherit lib pkgs; }; + + mkStartCommand = { + memory ? 2048, + cdrom ? null, + usb ? null, + pxe ? null, + uboot ? false, + uefi ? false, + extraFlags ? [], + }: let + qemu = qemu-common.qemuBinary pkgs.qemu_test; + + flags = [ + "-m" (toString memory) + "-netdev" ("user,id=net0" + (lib.optionalString (pxe != null) ",tftp=${pxe},bootfile=netboot.ipxe")) + "-device" ("virtio-net-pci,netdev=net0" + (lib.optionalString (pxe != null && uefi) ",romfile=${pkgs.ipxe}/ipxe.efirom")) + ] ++ lib.optionals (cdrom != null) [ + "-cdrom" cdrom + ] ++ lib.optionals (usb != null) [ + "-device" "usb-ehci" + "-drive" "id=usbdisk,file=${usb},if=none,readonly" + "-device" "usb-storage,drive=usbdisk" + ] ++ lib.optionals (pxe != null) [ + "-boot" "order=n" + ] ++ lib.optionals uefi [ + "-drive" "if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}" + "-drive" "if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}" + ] ++ extraFlags; + + flagsStr = lib.concatStringsSep " " flags; + in "${qemu} ${flagsStr}"; iso = (import ../lib/eval-config.nix { @@ -28,21 +59,16 @@ let ]; }).config.system.build.sdImage; - pythonDict = params: "\n {\n ${concatStringsSep ",\n " (mapAttrsToList (name: param: "\"${name}\": \"${param}\"") params)},\n }\n"; - - makeBootTest = name: extraConfig: + makeBootTest = name: config: let - machineConfig = pythonDict ({ - qemuBinary = qemu-common.qemuBinary pkgs.qemu_test; - qemuFlags = "-m 768"; - } // extraConfig); + startCommand = mkStartCommand config; in makeTest { name = "boot-" + name; nodes = { }; testScript = '' - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system") @@ -73,43 +99,35 @@ let config.system.build.netbootIpxeScript ]; }; - machineConfig = pythonDict ({ - qemuBinary = qemu-common.qemuBinary pkgs.qemu_test; - qemuFlags = "-boot order=n -m 2000"; - netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe"; + startCommand = mkStartCommand ({ + pxe = ipxeBootDir; } // extraConfig); in makeTest { name = "boot-netboot-" + name; nodes = { }; testScript = '' - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.shutdown() ''; }; - uefiBinary = { - x86_64-linux = "${pkgs.OVMF.fd}/FV/OVMF.fd"; - aarch64-linux = "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd"; - }.${pkgs.stdenv.hostPlatform.system}; in { uefiCdrom = makeBootTest "uefi-cdrom" { + uefi = true; cdrom = "${iso}/iso/${iso.isoName}"; - bios = uefiBinary; }; uefiUsb = makeBootTest "uefi-usb" { + uefi = true; usb = "${iso}/iso/${iso.isoName}"; - bios = uefiBinary; }; uefiNetboot = makeNetbootTest "uefi" { - bios = uefiBinary; - # Custom ROM is needed for EFI PXE boot. I failed to understand exactly why, because QEMU should still use iPXE for EFI. - netFrontendArgs = "romfile=${pkgs.ipxe}/ipxe.efirom"; + uefi = true; }; -} // optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") { +} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") { biosCdrom = makeBootTest "bios-cdrom" { cdrom = "${iso}/iso/${iso.isoName}"; }; @@ -124,9 +142,12 @@ in { sdImage = "${sd}/sd-image/${sd.imageName}"; mutableImage = "/tmp/linked-image.qcow2"; - machineConfig = pythonDict { - bios = "${pkgs.ubootQemuX86}/u-boot.rom"; - qemuFlags = "-m 768 -machine type=pc,accel=tcg -drive file=${mutableImage},if=ide,format=qcow2"; + startCommand = mkStartCommand { + extraFlags = [ + "-bios" "${pkgs.ubootQemuX86}/u-boot.rom" + "-machine" "type=pc,accel=tcg" + "-drive" "file=${mutableImage},if=virtio" + ]; }; in makeTest { name = "boot-uboot-extlinux"; @@ -138,11 +159,14 @@ in { if os.system("qemu-img create -f qcow2 -F raw -b ${sdImage} ${mutableImage}") != 0: raise RuntimeError("Could not create mutable linked image") - machine = create_machine(${machineConfig}) + machine = create_machine("${startCommand}") machine.start() machine.wait_for_unit("multi-user.target") machine.succeed("nix store verify -r --no-trust --option experimental-features nix-command /run/current-system") machine.shutdown() ''; + + # kernel can't find rootfs after boot - investigate? + meta.broken = true; }; } diff --git a/nixpkgs/nixos/tests/common/ec2.nix b/nixpkgs/nixos/tests/common/ec2.nix index 1a64c464039b..82922102f07b 100644 --- a/nixpkgs/nixos/tests/common/ec2.nix +++ b/nixpkgs/nixos/tests/common/ec2.nix @@ -61,7 +61,7 @@ with pkgs.lib; + " $QEMU_OPTS" ) - machine = create_machine({"startCommand": start_command}) + machine = create_machine(start_command) try: '' + indentLines script + '' finally: diff --git a/nixpkgs/nixos/tests/consul.nix b/nixpkgs/nixos/tests/consul.nix index 6233234ff083..c819312068dc 100644 --- a/nixpkgs/nixos/tests/consul.nix +++ b/nixpkgs/nixos/tests/consul.nix @@ -42,6 +42,8 @@ let ]; networking.firewall = firewallSettings; + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ]; + services.consul = { enable = true; inherit webUi; @@ -65,6 +67,8 @@ let ]; networking.firewall = firewallSettings; + nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ]; + services.consul = assert builtins.elem thisConsensusServerHost allConsensusServerHosts; { diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix index f252eb9ff61e..7d91076600f9 100644 --- a/nixpkgs/nixos/tests/docker-tools.nix +++ b/nixpkgs/nixos/tests/docker-tools.nix @@ -58,6 +58,20 @@ let ''; config.Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "/testfile" ]; }; + + nonRootTestImage = + pkgs.dockerTools.streamLayeredImage rec { + name = "non-root-test"; + tag = "latest"; + uid = 1000; + gid = 1000; + uname = "user"; + gname = "user"; + config = { + User = "user"; + Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "${pkgs.coreutils}/bin/stat" ]; + }; + }; in { name = "docker-tools"; meta = with pkgs.lib.maintainers; { @@ -181,7 +195,7 @@ in { ): docker.succeed( "docker load --input='${examples.bashLayeredWithUser}'", - "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'", + "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 755 == $(stat --format=%a /nix) && test 755 == $(stat --format=%a /nix/store)'", "docker rmi ${examples.bashLayeredWithUser.imageName}", ) @@ -604,5 +618,11 @@ in { "${chownTestImage} | docker load", "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" ) + + with subtest("streamLayeredImage: with non-root user"): + docker.succeed( + "${nonRootTestImage} | docker load", + "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)" + ) ''; }) diff --git a/nixpkgs/nixos/tests/incus/container.nix b/nixpkgs/nixos/tests/incus/container.nix index 0f42d16f133d..eb00429e53fe 100644 --- a/nixpkgs/nixos/tests/incus/container.nix +++ b/nixpkgs/nixos/tests/incus/container.nix @@ -5,6 +5,8 @@ let configuration = { # Building documentation makes the test unnecessarily take a longer time: documentation.enable = lib.mkForce false; + + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; } // extra; }; @@ -40,6 +42,12 @@ in with machine.nested("Waiting for instance to start and be usable"): retry(instance_is_up) + def check_sysctl(instance): + with subtest("systemd sysctl settings are applied"): + machine.succeed(f"incus exec {instance} -- systemctl status systemd-sysctl") + sysctl = machine.succeed(f"incus exec {instance} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1] + assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1" + machine.wait_for_unit("incus.service") # no preseed should mean no service @@ -83,6 +91,7 @@ in with subtest("lxc-container generator configures plain container"): # reuse the existing container to save some time machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf") + check_sysctl("container") with subtest("lxc-container generator configures nested container"): machine.execute("incus delete --force container") @@ -94,6 +103,8 @@ in target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip() assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service" + check_sysctl("container") + with subtest("lxc-container generator configures privileged container"): machine.execute("incus delete --force container") machine.succeed("incus launch nixos container --config security.privileged=true") @@ -101,5 +112,7 @@ in retry(instance_is_up) machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf") + + check_sysctl("container") ''; }) diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix index b6cb6a0c6d45..97bb7f8def59 100644 --- a/nixpkgs/nixos/tests/installer.nix +++ b/nixpkgs/nixos/tests/installer.nix @@ -83,46 +83,34 @@ let , postInstallCommands, preBootCommands, postBootCommands, extraConfig , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest }: - let iface = "virtio"; - isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); - bios = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd"; + let + qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; }; + isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); + qemu = qemu-common.qemuBinary pkgs.qemu_test; in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then '' machine.succeed("true") '' else '' + import os import subprocess - tpm_folder = os.environ['NIX_BUILD_TOP'] - def assemble_qemu_flags(): - flags = "-cpu max" - ${if (system == "x86_64-linux" || system == "i686-linux") - then ''flags += " -m 1024"'' - else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"'' - } - ${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''} - ${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''} - return flags + tpm_folder = os.environ['NIX_BUILD_TOP'] - qemu_flags = {"qemuFlags": assemble_qemu_flags()} + startcommand = "${qemu} -m 2048" - import os + ${optionalString clevisTest '' + startcommand += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0" + startcommand += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\"" + ''} + ${optionalString isEfi '' + startcommand +=" -drive if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware} -drive if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}" + ''} image_dir = machine.state_dir disk_image = os.path.join(image_dir, "machine.qcow2") - - hd_flags = { - "hdaInterface": "${iface}", - "hda": disk_image, - } - ${optionalString isEfi '' - hd_flags.update( - bios="${pkgs.OVMF.fd}/FV/${bios}" - )'' - } - default_flags = {**hd_flags, **qemu_flags} - + startcommand += f" -drive file={disk_image},if=virtio,werror=report" def create_machine_named(name): - return create_machine({**default_flags, "name": name}) + return create_machine(startcommand, name=name) class Tpm: def __init__(self): @@ -471,7 +459,7 @@ let # builds stuff in the VM, needs more juice virtualisation.diskSize = 8 * 1024; virtualisation.cores = 8; - virtualisation.memorySize = 1536; + virtualisation.memorySize = 2048; boot.initrd.systemd.enable = systemdStage1; diff --git a/nixpkgs/nixos/tests/k3s/default.nix b/nixpkgs/nixos/tests/k3s/default.nix index e168f8233c76..512dc06ee77e 100644 --- a/nixpkgs/nixos/tests/k3s/default.nix +++ b/nixpkgs/nixos/tests/k3s/default.nix @@ -6,6 +6,11 @@ let allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs; in { + # Testing K3s with Etcd backend + etcd = lib.mapAttrs (_: k3s: import ./etcd.nix { + inherit system pkgs k3s; + inherit (pkgs) etcd; + }) allK3s; # Run a single node k3s cluster and verify a pod can run single-node = lib.mapAttrs (_: k3s: import ./single-node.nix { inherit system pkgs k3s; }) allK3s; # Run a multi-node k3s cluster and verify pod networking works across nodes diff --git a/nixpkgs/nixos/tests/k3s/etcd.nix b/nixpkgs/nixos/tests/k3s/etcd.nix new file mode 100644 index 000000000000..d6e9a294adb1 --- /dev/null +++ b/nixpkgs/nixos/tests/k3s/etcd.nix @@ -0,0 +1,100 @@ +import ../make-test-python.nix ({ pkgs, lib, k3s, etcd, ... }: + +{ + name = "${k3s.name}-etcd"; + + nodes = { + + etcd = { ... }: { + services.etcd = { + enable = true; + openFirewall = true; + listenClientUrls = [ "http://192.168.1.1:2379" "http://127.0.0.1:2379" ]; + listenPeerUrls = [ "http://192.168.1.1:2380" ]; + initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ]; + initialCluster = [ "etcd=http://192.168.1.1:2380" ]; + }; + networking = { + useDHCP = false; + defaultGateway = "192.168.1.1"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { address = "192.168.1.1"; prefixLength = 24; } + ]; + }; + }; + + k3s = { pkgs, ... }: { + environment.systemPackages = with pkgs; [ jq ]; + # k3s uses enough resources the default vm fails. + virtualisation.memorySize = 1536; + virtualisation.diskSize = 4096; + + services.k3s = { + enable = true; + role = "server"; + extraFlags = builtins.toString [ + "--datastore-endpoint=\"http://192.168.1.1:2379\"" + "--disable" "coredns" + "--disable" "local-storage" + "--disable" "metrics-server" + "--disable" "servicelb" + "--disable" "traefik" + "--node-ip" "192.168.1.2" + ]; + }; + + networking = { + firewall = { + allowedTCPPorts = [ 2379 2380 6443 ]; + allowedUDPPorts = [ 8472 ]; + }; + useDHCP = false; + defaultGateway = "192.168.1.2"; + interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [ + { address = "192.168.1.2"; prefixLength = 24; } + ]; + }; + }; + + }; + + testScript = '' + with subtest("should start etcd"): + etcd.start() + etcd.wait_for_unit("etcd.service") + + with subtest("should wait for etcdctl endpoint status to succeed"): + etcd.wait_until_succeeds("etcdctl endpoint status") + + with subtest("should start k3s"): + k3s.start() + k3s.wait_for_unit("k3s") + + with subtest("should test if kubectl works"): + k3s.wait_until_succeeds("k3s kubectl get node") + + with subtest("should wait for service account to show up; takes a sec"): + k3s.wait_until_succeeds("k3s kubectl get serviceaccount default") + + with subtest("should create a sample secret object"): + k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra") + + with subtest("should check if secret is correct"): + k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]") + + with subtest("should have a secret in database"): + etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + + with subtest("should delete the secret"): + k3s.succeed("k3s kubectl delete secret nixossecret") + + with subtest("should not have a secret in database"): + etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]") + + with subtest("should shutdown k3s and etcd"): + k3s.shutdown() + etcd.shutdown() + ''; + + meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers; +}) diff --git a/nixpkgs/nixos/tests/kernel-generic.nix b/nixpkgs/nixos/tests/kernel-generic.nix index 0dcab39f3fad..9714a94382ee 100644 --- a/nixpkgs/nixos/tests/kernel-generic.nix +++ b/nixpkgs/nixos/tests/kernel-generic.nix @@ -30,7 +30,6 @@ let linux_5_10_hardened linux_5_15_hardened linux_6_1_hardened - linux_6_5_hardened linux_6_6_hardened linux_6_7_hardened linux_rt_5_4 diff --git a/nixpkgs/nixos/tests/lomiri-system-settings.nix b/nixpkgs/nixos/tests/lomiri-system-settings.nix new file mode 100644 index 000000000000..867fc14797e7 --- /dev/null +++ b/nixpkgs/nixos/tests/lomiri-system-settings.nix @@ -0,0 +1,99 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "lomiri-system-settings-standalone"; + meta.maintainers = lib.teams.lomiri.members; + + nodes.machine = { config, pkgs, ... }: { + imports = [ + ./common/x11.nix + ]; + + services.xserver.enable = true; + + environment = { + systemPackages = with pkgs.lomiri; [ + suru-icon-theme + lomiri-system-settings + ]; + variables = { + UITK_ICON_THEME = "suru"; + }; + }; + + i18n.supportedLocales = [ "all" ]; + + fonts.packages = with pkgs; [ + # Intended font & helps with OCR + ubuntu_font_family + ]; + + services.upower.enable = true; + }; + + enableOCR = true; + + testScript = let + settingsPages = [ + # Base pages + { name = "wifi"; type = "internal"; element = "networks"; } + { name = "bluetooth"; type = "internal"; element = "discoverable|None detected"; } + # only text we can really look for with VPN is on a button, OCR on CI struggles with it + { name = "vpn"; type = "internal"; element = "Add|Manual|Configuration"; skipOCR = true; } + { name = "appearance"; type = "internal"; element = "Background image|blur effects"; } + { name = "desktop"; type = "internal"; element = "workspaces|Icon size"; } + { name = "sound"; type = "internal"; element = "Silent Mode|Message sound"; } + { name = "language"; type = "internal"; element = "Display language|External keyboard"; } + { name = "notification"; type = "internal"; element = "Apps that notify"; } + { name = "gestures"; type = "internal"; element = "Edge drag"; } + { name = "mouse"; type = "internal"; element = "Cursor speed|Wheel scrolling speed"; } + { name = "timedate"; type = "internal"; element = "Time zone|Set the time and date"; } + + # External plugins + { name = "security-privacy"; type = "external"; element = "Locking|unlocking|permissions"; elementLocalised = "Sperren|Entsperren|Berechtigungen"; } + ]; + in + '' + machine.wait_for_x() + + with subtest("lomiri system settings launches"): + machine.execute("lomiri-system-settings >&2 &") + machine.wait_for_text("System Settings") + machine.screenshot("lss_open") + + # Move focus to start of plugins list for following list of tests + machine.send_key("tab") + machine.send_key("tab") + machine.screenshot("lss_focus") + + # tab through & open all sub-menus, to make sure none of them fail + '' + (lib.strings.concatMapStringsSep "\n" (page: '' + machine.send_key("tab") + machine.send_key("kp_enter") + '' + + lib.optionalString (!(page.skipOCR or false)) '' + with subtest("lomiri system settings ${page.name} works"): + machine.wait_for_text(r"(${page.element})") + machine.screenshot("lss_page_${page.name}") + '') settingsPages) + '' + + machine.execute("pkill -f lomiri-system-settings") + + with subtest("lomiri system settings localisation works"): + machine.execute("env LANG=de_DE.UTF-8 lomiri-system-settings >&2 &") + machine.wait_for_text("Systemeinstellungen") + machine.screenshot("lss_localised_open") + + # Move focus to start of plugins list for following list of tests + machine.send_key("tab") + machine.send_key("tab") + machine.screenshot("lss_focus_localised") + + '' + (lib.strings.concatMapStringsSep "\n" (page: '' + machine.send_key("tab") + machine.send_key("kp_enter") + '' + lib.optionalString (page.type == "external") '' + with subtest("lomiri system settings ${page.name} localisation works"): + machine.wait_for_text(r"(${page.elementLocalised})") + machine.screenshot("lss_localised_page_${page.name}") + '') settingsPages) + '' + ''; +}) diff --git a/nixpkgs/nixos/tests/mealie.nix b/nixpkgs/nixos/tests/mealie.nix new file mode 100644 index 000000000000..88f749c71294 --- /dev/null +++ b/nixpkgs/nixos/tests/mealie.nix @@ -0,0 +1,24 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +{ + name = "mealie"; + meta = with pkgs.lib.maintainers; { + maintainers = [ litchipi ]; + }; + + nodes = { + server = { + services.mealie = { + enable = true; + port = 9001; + }; + }; + }; + + testScript = '' + start_all() + server.wait_for_unit("mealie.service") + server.wait_for_open_port(9001) + server.succeed("curl --fail http://localhost:9001") + ''; +}) diff --git a/nixpkgs/nixos/tests/monado.nix b/nixpkgs/nixos/tests/monado.nix new file mode 100644 index 000000000000..8368950951e7 --- /dev/null +++ b/nixpkgs/nixos/tests/monado.nix @@ -0,0 +1,39 @@ +import ./make-test-python.nix ({ pkgs, ... }: { + name = "monado"; + + nodes.machine = + { pkgs, ... }: + + { + hardware.opengl.enable = true; + users.users.alice = { + isNormalUser = true; + uid = 1000; + }; + + services.monado = { + enable = true; + defaultRuntime = true; + }; + # Stop Monado from probing for any hardware + systemd.user.services.monado.environment.SIMULATED_ENABLE = "1"; + + environment.systemPackages = with pkgs; [ openxr-loader ]; + }; + + testScript = { nodes, ... }: + let + userId = toString nodes.machine.users.users.alice.uid; + runtimePath = "/run/user/${userId}"; + in + '' + machine.succeed("loginctl enable-linger alice") + machine.wait_for_unit("user@${userId}.service") + + machine.wait_for_unit("monado.socket", "alice") + machine.systemctl("start monado.service", "alice") + machine.wait_for_unit("monado.service", "alice") + + machine.succeed("su -- alice -c env XDG_RUNTIME_DIR=${runtimePath} openxr_runtime_list") + ''; +}) diff --git a/nixpkgs/nixos/tests/plasma6.nix b/nixpkgs/nixos/tests/plasma6.nix new file mode 100644 index 000000000000..ec5b3f24ef74 --- /dev/null +++ b/nixpkgs/nixos/tests/plasma6.nix @@ -0,0 +1,64 @@ +import ./make-test-python.nix ({ pkgs, ...} : + +{ + name = "plasma6"; + meta = with pkgs.lib.maintainers; { + maintainers = [ k900 ]; + }; + + nodes.machine = { ... }: + + { + imports = [ ./common/user-account.nix ]; + services.xserver.enable = true; + services.xserver.displayManager.sddm.enable = true; + # FIXME: this should be testing Wayland + services.xserver.displayManager.defaultSession = "plasmax11"; + services.xserver.desktopManager.plasma6.enable = true; + environment.plasma6.excludePackages = [ pkgs.kdePackages.elisa ]; + services.xserver.displayManager.autoLogin = { + enable = true; + user = "alice"; + }; + }; + + testScript = { nodes, ... }: let + user = nodes.machine.users.users.alice; + xdo = "${pkgs.xdotool}/bin/xdotool"; + in '' + with subtest("Wait for login"): + start_all() + machine.wait_for_file("/tmp/xauth_*") + machine.succeed("xauth merge /tmp/xauth_*") + + with subtest("Check plasmashell started"): + machine.wait_until_succeeds("pgrep plasmashell") + machine.wait_for_window("^Desktop ") + + with subtest("Check that KDED is running"): + machine.succeed("pgrep kded6") + + with subtest("Ensure Elisa is not installed"): + machine.fail("which elisa") + + machine.succeed("su - ${user.name} -c 'xauth merge /tmp/xauth_*'") + + with subtest("Run Dolphin"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'") + machine.wait_for_window(" Dolphin") + + with subtest("Run Konsole"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 konsole >&2 &'") + machine.wait_for_window("Konsole") + + with subtest("Run systemsettings"): + machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 systemsettings >&2 &'") + machine.wait_for_window("Settings") + + with subtest("Wait to get a screenshot"): + machine.execute( + "${xdo} key Alt+F1 sleep 10" + ) + machine.screenshot("screen") + ''; +}) diff --git a/nixpkgs/nixos/tests/searx.nix b/nixpkgs/nixos/tests/searx.nix index 2f808cb65266..02a88f690db7 100644 --- a/nixpkgs/nixos/tests/searx.nix +++ b/nixpkgs/nixos/tests/searx.nix @@ -36,7 +36,7 @@ import ./make-test-python.nix ({ pkgs, ...} : }; # fancy setup: run in uWSGI and use nginx as proxy - nodes.fancy = { ... }: { + nodes.fancy = { config, ... }: { imports = [ ../modules/profiles/minimal.nix ]; services.searx = { @@ -65,7 +65,7 @@ import ./make-test-python.nix ({ pkgs, ...} : include ${pkgs.nginx}/conf/uwsgi_params; uwsgi_pass unix:/run/searx/uwsgi.sock; ''; - locations."/searx/static/".alias = "${pkgs.searx}/share/static/"; + locations."/searx/static/".alias = "${config.services.searx.package}/share/static/"; }; # allow nginx access to the searx socket @@ -108,7 +108,7 @@ import ./make-test-python.nix ({ pkgs, ...} : "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2" ) fancy.succeed( - "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/oscar/js/bootstrap.min.js >&2" + "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2" ) ''; }) diff --git a/nixpkgs/nixos/tests/systemd-boot.nix b/nixpkgs/nixos/tests/systemd-boot.nix index ce3245f3d862..1b7e83253e59 100644 --- a/nixpkgs/nixos/tests/systemd-boot.nix +++ b/nixpkgs/nixos/tests/systemd-boot.nix @@ -14,6 +14,72 @@ let boot.loader.efi.canTouchEfiVariables = true; environment.systemPackages = [ pkgs.efibootmgr ]; }; + + commonXbootldr = { config, lib, pkgs, ... }: + let + diskImage = import ../lib/make-disk-image.nix { + inherit config lib pkgs; + label = "nixos"; + format = "qcow2"; + partitionTableType = "efixbootldr"; + touchEFIVars = true; + installBootLoader = true; + }; + in + { + imports = [ common ]; + virtualisation.useBootLoader = lib.mkForce false; # Only way to tell qemu-vm not to create the default system image + virtualisation.directBoot.enable = false; # But don't direct boot either because we're testing systemd-boot + + system.build.diskImage = diskImage; # Use custom disk image with an XBOOTLDR partition + virtualisation.efi.variables = "${diskImage}/efi-vars.fd"; + + virtualisation.useDefaultFilesystems = false; # Needs custom setup for `diskImage` + virtualisation.bootPartition = null; + virtualisation.fileSystems = { + "/" = { + device = "/dev/vda3"; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/vda2"; + fsType = "vfat"; + noCheck = true; + }; + "/efi" = { + device = "/dev/vda1"; + fsType = "vfat"; + noCheck = true; + }; + }; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.efiSysMountPoint = "/efi"; + boot.loader.systemd-boot.xbootldrMountPoint = "/boot"; + }; + + customDiskImage = nodes: '' + import os + import subprocess + import tempfile + + tmp_disk_image = tempfile.NamedTemporaryFile() + + subprocess.run([ + "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img", + "create", + "-f", + "qcow2", + "-b", + "${nodes.machine.system.build.diskImage}/nixos.qcow2", + "-F", + "qcow2", + tmp_disk_image.name, + ]) + + # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. + os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name + ''; in { basic = makeTest { @@ -65,6 +131,32 @@ in ''; }; + basicXbootldr = makeTest { + name = "systemd-boot-xbootldr"; + meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; + + nodes.machine = commonXbootldr; + + testScript = { nodes, ... }: '' + ${customDiskImage nodes} + + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") + machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf") + + # Ensure we actually booted using systemd-boot + # Magic number is the vendor UUID used by systemd-boot. + machine.succeed( + "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" + ) + + # "bootctl install" should have created an EFI entry + machine.succeed('efibootmgr | grep "Linux Boot Manager"') + ''; + }; + # Check that specialisations create corresponding boot entries. specialisation = makeTest { name = "systemd-boot-specialisation"; @@ -184,6 +276,29 @@ in ''; }; + entryFilenameXbootldr = makeTest { + name = "systemd-boot-entry-filename-xbootldr"; + meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ]; + + nodes.machine = { pkgs, lib, ... }: { + imports = [ commonXbootldr ]; + boot.loader.systemd-boot.memtest86.enable = true; + boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf"; + }; + + testScript = { nodes, ... }: '' + ${customDiskImage nodes} + + machine.start() + machine.wait_for_unit("multi-user.target") + + machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi") + machine.fail("test -e /boot/loader/entries/memtest86.conf") + machine.succeed("test -e /boot/loader/entries/apple.conf") + machine.succeed("test -e /boot/EFI/memtest86/memtest.efi") + ''; + }; + extraEntries = makeTest { name = "systemd-boot-extra-entries"; meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ]; |