From bdadc527d2718e9453936264cf7242c488451b94 Mon Sep 17 00:00:00 2001 From: matthewcroughan Date: Thu, 11 May 2023 15:43:41 +0100 Subject: [PATCH] nix: add graphical-gnome-test This VM integration test spawns Gnome, monado-service, stardust-xr-server, flatland and weston-cliptest and tests that the functionality works correctly. The result is a screenshot. If any program in the test produces an exit code above 0 it will fail the test, graphical rendering bugs should be visible in the resulting screenshot --- README.md | 25 +++++- flake.nix | 12 ++- nix/gnome-graphical-test.nix | 155 +++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 nix/gnome-graphical-test.nix diff --git a/README.md b/README.md index 2aff0ee..4fbaae7 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,27 @@ cargo build ## Install ```bash cargo install -``` \ No newline at end of file +``` + +## Test + +##### Gnome Graphical Integration Test + +- `nix build .#gnome-graphical-test` + + This test uses Nix to reproducibly execute a QEMU virtual machine which + spawns a full Gnome desktop. It runs `monado-service`, `stardust-xr-server` + `flatland` underneath of Gnome and then attaches `weston-cliptest` to the + `flatland` process running underneath of `stardust-xr-server`, the result is + a screenshot in PNG format that should look like expected. If any process in + this test produces an exit code above 0, the test will fail, graphical bugs + should be visible in the screenshot. An example of the result is below. + + ###### Result + + ![image](https://github.com/StardustXR/server/assets/26458780/e21cd039-2528-4568-b20a-ce4abfab6d9b) + +##### Everything + +`nix flake check` will build every test underneath of the `checks` attribute in the `flake.nix` + diff --git a/flake.nix b/flake.nix index 3fb9b46..1d2ee09 100644 --- a/flake.nix +++ b/flake.nix @@ -2,10 +2,14 @@ # 22.11 does not include PR #218472, hence we use the unstable version inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable; + # Since we do not have a monorepo, we have to fetch Flatland in order to use + # it to create VM Tests + inputs.flatland.url = "github:StardustXR/flatland"; + inputs.fenix.url = github:nix-community/fenix; inputs.fenix.inputs.nixpkgs.follows = "nixpkgs"; - outputs = { self, nixpkgs, fenix }: + outputs = { self, nixpkgs, fenix, ... }: let name = "server"; pkgs = system: import nixpkgs { @@ -82,6 +86,12 @@ packages."x86_64-linux".default = package (pkgs "x86_64-linux"); packages."aarch64-linux".default = package (pkgs "aarch64-linux"); + packages."x86_64-linux".gnome-graphical-test = self.checks.x86_64-linux.gnome-graphical-test; + packages."aarch64-linux".gnome-graphical-test = self.checks.aarch64-linux.gnome-graphical-test; + + checks."x86_64-linux".gnome-graphical-test = (pkgs "x86_64-linux").nixosTest (import ./nix/gnome-graphical-test.nix { pkgs = (pkgs "x86_64-linux"); inherit self; }); + checks."aarch64-linux".gnome-graphical-test = (pkgs "aarch64-linux").nixosTest (import ./nix/gnome-graphical-test.nix { pkgs = (pkgs "aarch64-linux"); inherit self; }); + devShells."x86_64-linux".default = shell (pkgs "x86_64-linux"); devShells."aarch64-linux".default = shell (pkgs "aarch64-linux"); }; diff --git a/nix/gnome-graphical-test.nix b/nix/gnome-graphical-test.nix new file mode 100644 index 0000000..95b7235 --- /dev/null +++ b/nix/gnome-graphical-test.nix @@ -0,0 +1,155 @@ +{ pkgs, lib ? pkgs.lib, self, ... }: +# Some code is copy-pasted from https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/gnome.nix +# TODO: make this less boiler-platey and make a function like mkGnomeTest that does all this and upstream it to nixpkgs +{ + name = "stardust-xr-server-gnome-vmtest"; + meta = with lib; { + maintainers = [ maintainers.matthewcroughan ]; + }; + nodes.machine = { ... }: { + imports = [ "${pkgs.path}/nixos/tests/common/user-account.nix" ]; + virtualisation.qemu.options = [ + "-device virtio-gpu-pci" + ]; + environment.systemPackages = [ pkgs.monado ]; + services.xserver = { + enable = true; + desktopManager.gnome = { + enable = true; + debug = true; + # Set a nice desktop background that is pleasing to the eyes :3 + extraGSettingsOverrides = '' + [org.gnome.desktop.background] + picture-uri='file://${pkgs.gnome.gnome-backgrounds}/share/backgrounds/gnome/blobs-l.svg' + picture-uri-dark='file://${pkgs.gnome.gnome-backgrounds}/share/backgrounds/gnome/blobs-l.svg' + ''; + }; + displayManager = { + gdm = { + enable = true; + debug = true; + }; + autoLogin = { + enable = true; + user = "alice"; + }; + }; + }; + systemd.user.services = { + "monado" = { + after = [ "graphical-session.target" "default.target" "org.gnome.Shell@wayland.service" ]; + environment = { + XRT_COMPOSITOR_FORCE_WAYLAND = "1"; + WAYLAND_DISPLAY = "wayland-0"; + }; + serviceConfig = { + ExecStartPre = [ + "${pkgs.writeShellScript "sleep" '' + sleep 3 + ''}" + ]; + ExecStart = let + # stdin disappears in NixOS test driver ( machine.succeed() ), requiring us to specify < /dev/ttyS0 to fake stdin + exec-monado-service = pkgs.writeShellScript "exec-monado-service" "${pkgs.monado}/bin/monado-service < /dev/ttyS0"; + in [ + "${exec-monado-service}" + ]; + }; + }; + "stardust-xr-server" = { + after = [ "monado.service" ]; + serviceConfig = { + Type = "notify"; + NotifyAccess = "all"; + ExecStartPre = [ + "${pkgs.writeShellScript "sleep" '' + sleep 3 + ''}" + ]; + ExecStart = let + notifyReady = pkgs.writeShellScript "notifyReady" "systemd-notify --ready"; + exec-stardust-xr-server = pkgs.writeShellScript "exec-stardust-xr-server" "${self.packages.${pkgs.hostPlatform.system}.default}/bin/stardust-xr-server -e ${notifyReady}"; + in [ + "${exec-stardust-xr-server}" + ]; + }; + }; + "weston-cliptest" = { + after = [ "flatland.service" ]; + environment.WAYLAND_DISPLAY = "wayland-1"; + serviceConfig = { + ExecStart = [ + "${pkgs.weston}/bin/weston-cliptest" + ]; + }; + }; + "flatland" = { + after = [ "stardust-xr-server.service" ]; + serviceConfig = { + ExecStart = [ + "${self.inputs.flatland.packages.${pkgs.hostPlatform.system}.default}/bin/flatland" + ]; + }; + }; + "org.gnome.Shell@wayland" = { + wants = [ "monado.service" "stardust-xr-server.service" "flatland.service" "weston-cliptest.service" ]; + serviceConfig = { + ExecStart = [ + # Clear the list before overriding it. + "" + # Eval API is now internal so Shell needs to run in unsafe mode. + # TODO: improve test driver so that it supports openqa-like manipulation + # that would allow us to drop this mess. + "${pkgs.gnome.gnome-shell}/bin/gnome-shell --unsafe-mode" + ]; + }; + }; + }; + }; + + testScript = { nodes, ... }: let + # Keep line widths somewhat managable + user = nodes.machine.config.users.users.alice; + uid = toString user.uid; + bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; + gdbus = "${bus} gdbus"; + su = command: "su ${user.name} -c '${command}'"; + + # Call javascript in gnome shell, returns a tuple (success, output), where + # `success` is true if the dbus call was successful and output is what the + # javascript evaluates to. + eval = "call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval"; + + # False when startup is done + startingUp = su "${gdbus} ${eval} Main.layoutManager._startingUp"; + in '' + with subtest("Login to GNOME with GDM"): + # wait for gdm to start + machine.wait_for_unit("display-manager.service") + # wait for the wayland server + machine.wait_for_file("/run/user/${uid}/wayland-0") + # wait for alice to be logged in + machine.wait_for_unit("default.target", "${user.name}") + # check that logging in has given the user ownership of devices + assert "alice" in machine.succeed("getfacl -p /dev/snd/timer") + + with subtest("Wait for GNOME Shell"): + # correct output should be (true, 'false') + machine.wait_until_succeeds( + "${startingUp} | grep -q 'true,..false'" + ) + + # To allow monado-service to use < /dev/ttyS0 + machine.succeed("chown alice /dev/ttyS0") + + with subtest("Open Monado and StardustXR"): + # Close the Activities view so that Shell can correctly track the focused window. + machine.send_key("esc") + machine.wait_for_unit("monado.service", "${user.name}") + machine.wait_for_unit("stardust-xr-server.service", "${user.name}") + machine.wait_for_unit("flatland.service", "${user.name}") + machine.wait_for_unit("weston-cliptest.service", "${user.name}") + machine.sleep(3) + machine.screenshot("screen") + ''; +}