From ca8313570c4180885a5ab55cdd04bc238292d083 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Thu, 21 Aug 2025 17:39:02 -0500 Subject: [PATCH] nix: add vm-based integration tests --- .gitignore | 1 + flake.lock | 22 ++++++ flake.nix | 12 +++ nix/tests.nix | 167 ++++++++++++++++++++++++++++++++++++++++ nix/vm/common-gnome.nix | 13 ++++ nix/vm/common.nix | 7 +- 6 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 nix/tests.nix diff --git a/.gitignore b/.gitignore index e451b171a..e521f8851 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ zig-cache/ .zig-cache/ zig-out/ /result* +/.nixos-test-history example/*.wasm test/ghostty test/cases/**/*.actual.png diff --git a/flake.lock b/flake.lock index 90b97ed4a..ece49febb 100644 --- a/flake.lock +++ b/flake.lock @@ -34,6 +34,27 @@ "type": "github" } }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1755776884, + "narHash": "sha256-CPM7zm6csUx7vSfKvzMDIjepEJv1u/usmaT7zydzbuI=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "4fb695d10890e9fc6a19deadf85ff79ffb78da86", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "release-25.05", + "repo": "home-manager", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 315532800, @@ -64,6 +85,7 @@ "inputs": { "flake-compat": "flake-compat", "flake-utils": "flake-utils", + "home-manager": "home-manager", "nixpkgs": "nixpkgs", "zig": "zig", "zon2nix": "zon2nix" diff --git a/flake.nix b/flake.nix index e744c1a09..aac42fbc0 100644 --- a/flake.nix +++ b/flake.nix @@ -34,6 +34,13 @@ # nixpkgs.follows = "nixpkgs"; }; }; + + home-manager = { + url = "github:nix-community/home-manager?ref=release-25.05"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; }; outputs = { @@ -41,6 +48,7 @@ nixpkgs, zig, zon2nix, + home-manager, ... }: builtins.foldl' nixpkgs.lib.recursiveUpdate {} ( @@ -80,6 +88,10 @@ formatter.${system} = pkgs.alejandra; + checks.${system} = import ./nix/tests.nix { + inherit home-manager nixpkgs self system; + }; + apps.${system} = let runVM = ( module: let diff --git a/nix/tests.nix b/nix/tests.nix new file mode 100644 index 000000000..51fafad3e --- /dev/null +++ b/nix/tests.nix @@ -0,0 +1,167 @@ +{ + self, + system, + nixpkgs, + home-manager, + ... +}: let + nixos-version = nixpkgs.lib.trivial.release; + + pkgs = import nixpkgs { + inherit system; + overlays = [ + self.overlays.debug + ]; + }; + + pink_value = "#FF0087"; + + color_test = '' + import tempfile + import subprocess + + def check_for_pink(final=False) -> bool: + with tempfile.NamedTemporaryFile() as tmpin: + machine.send_monitor_command("screendump {}".format(tmpin.name)) + + cmd = 'convert {} -define histogram:unique-colors=true -format "%c" histogram:info:'.format( + tmpin.name + ) + ret = subprocess.run(cmd, shell=True, capture_output=True) + if ret.returncode != 0: + raise Exception( + "image analysis failed with exit code {}".format(ret.returncode) + ) + + text = ret.stdout.decode("utf-8") + return "${pink_value}" in text + ''; + + mkTestGnome = { + name, + settings, + testScript, + ocr ? false, + }: + pkgs.testers.runNixOSTest { + name = name; + + enableOCR = ocr; + + extraBaseModules = { + imports = [ + home-manager.nixosModules.home-manager + ]; + }; + + nodes = { + machine = { + config, + pkgs, + ... + }: { + imports = [ + ./vm/wayland-gnome.nix + settings + ]; + + virtualisation.vmVariant = { + virtualisation.host.pkgs = pkgs; + }; + + users.groups.ghostty = { + gid = 1000; + }; + + users.users.ghostty = { + uid = 1000; + }; + + home-manager = { + users = { + ghostty = { + home = { + username = config.users.users.ghostty.name; + homeDirectory = config.users.users.ghostty.home; + stateVersion = nixos-version; + }; + }; + }; + }; + + system.stateVersion = nixos-version; + }; + }; + + testScript = testScript; + }; +in { + basic-version-check = pkgs.testers.runNixOSTest { + name = "basic-version-check"; + nodes = { + machine = {pkgs, ...}: { + users.groups.ghostty = {}; + users.users.ghostty = { + isNormalUser = true; + group = "ghostty"; + packages = [ + pkgs.ghostty + ]; + }; + }; + }; + testScript = {...}: '' + machine.succeed("su - ghostty -c 'ghostty +version'") + ''; + }; + + basic-window-check-gnome = mkTestGnome { + name = "basic-window-check-gnome"; + settings = { + home-manager.users.ghostty = { + xdg.configFile = { + "ghostty/config".text = '' + background = ${pink_value} + ''; + }; + }; + }; + ocr = true; + testScript = {nodes, ...}: let + user = nodes.machine.users.users.ghostty; + bus_path = "/run/user/${toString user.uid}/bus"; + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=${bus_path}"; + gdbus = "${bus} gdbus"; + ghostty = "${bus} ghostty"; + su = command: "su - ${user.name} -c '${command}'"; + gseval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + wm_class = su "${gdbus} ${gseval} global.display.focus_window.wm_class"; + in '' + ${color_test} + + with subtest("wait for x"): + start_all() + machine.wait_for_x() + + machine.wait_for_file("${bus_path}") + + with subtest("Ensuring no pink is present without the terminal."): + assert ( + check_for_pink() == False + ), "Pink was present on the screen before we even launched a terminal!" + + machine.systemctl("enable app-com.mitchellh.ghostty-debug.service", user="${user.name}") + machine.succeed("${su "${ghostty} +new-window"}") + machine.wait_until_succeeds("${wm_class} | grep -q 'com.mitchellh.ghostty-debug'") + + machine.sleep(2) + + with subtest("Have the terminal display a color."): + assert( + check_for_pink() == True + ), "Pink was not found on the screen!" + + machine.systemctl("stop app-com.mitchellh.ghostty-debug.service", user="${user.name}") + ''; + }; +} diff --git a/nix/vm/common-gnome.nix b/nix/vm/common-gnome.nix index 0c2bef150..ab4aab9e9 100644 --- a/nix/vm/common-gnome.nix +++ b/nix/vm/common-gnome.nix @@ -22,6 +22,19 @@ }; }; + systemd.user.services = { + "org.gnome.Shell@wayland" = { + serviceConfig = { + ExecStart = [ + # Clear the list before overriding it. + "" + # Eval API is now internal so Shell needs to run in unsafe mode. + "${pkgs.gnome-shell}/bin/gnome-shell --unsafe-mode" + ]; + }; + }; + }; + environment.systemPackages = [ pkgs.gnomeExtensions.no-overview ]; diff --git a/nix/vm/common.nix b/nix/vm/common.nix index eefd7c1c0..63b7570b8 100644 --- a/nix/vm/common.nix +++ b/nix/vm/common.nix @@ -35,12 +35,6 @@ initialPassword = "ghostty"; }; - environment.etc = { - "xdg/autostart/com.mitchellh.ghostty.desktop" = { - source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop"; - }; - }; - environment.systemPackages = [ pkgs.kitty pkgs.fish @@ -61,6 +55,7 @@ services.displayManager = { autoLogin = { + enable = true; user = "ghostty"; }; };