Add Gnome Graphical Integration Test #14

Merged
MatthewCroughan merged 6 commits from mc/vm-test into main 2023-05-20 06:08:43 -04:00
5 changed files with 587 additions and 25 deletions

View File

@@ -1,12 +0,0 @@
name: build
on: [pull_request, push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v19
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
- run: nix build
- run: nix flake check

View File

@@ -19,4 +19,27 @@ cargo build
## Install
```bash
cargo install
```
```
## 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`

359
flake.lock generated
View File

@@ -8,11 +8,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1681971746,
"narHash": "sha256-RAFvwcAds5WeCSUYONE+YzR0uKVuLc6BIeN4WFix4f0=",
"lastModified": 1683786056,
"narHash": "sha256-Wrz/X9D0t8akhvEGj5a93xgpxI3vAcdPGcwn6tKHooc=",
"owner": "nix-community",
"repo": "fenix",
"rev": "3a0b59a2ea946a533c62ac417596835779087f0e",
"rev": "5816c7bbcc385d2e65877631497df3f7d66b354a",
"type": "github"
},
"original": {
@@ -21,13 +21,276 @@
"type": "github"
}
},
"fenix_2": {
"inputs": {
"nixpkgs": [
"flatland",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src_2"
},
"locked": {
"lastModified": 1678775037,
"narHash": "sha256-chx0tWnXKpcayPkPY3Qh+2hNwptvX8XE3o+fYZ+GNzg=",
"owner": "nix-community",
"repo": "fenix",
"rev": "ee59e1c769657b1e27e608f8b981fa8f6b715583",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1678379998,
"narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c13d60b89adea3dc20704c045ec4d50dd964d447",
"type": "github"
},
"original": {
"id": "flake-parts",
"type": "indirect"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"hercules-ci-effects",
"hercules-ci-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1678379998,
"narHash": "sha256-TZdfNqftHhDuIFwBcN9MUThx5sQXCTeZk9je5byPKRw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c13d60b89adea3dc20704c045ec4d50dd964d447",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flatland": {
"inputs": {
"fenix": "fenix_2",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1683766358,
"narHash": "sha256-wX1Lpj95kkHUZAloB1fGs+ixaRycaOJq4F77+HvaJCQ=",
"owner": "StardustXR",
"repo": "flatland",
"rev": "24613a496841bdf38e5f136608d5295860a75fce",
"type": "github"
},
"original": {
"owner": "StardustXR",
"repo": "flatland",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"hercules-ci-effects",
"hercules-ci-agent",
"pre-commit-hooks-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1660459072,
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"haskell-flake": {
"locked": {
"lastModified": 1678138103,
"narHash": "sha256-D0lao82bV3t2gEFjHiU6RN233t+1MnkQV+bq8MEu2ic=",
"owner": "hercules-ci",
"repo": "haskell-flake",
"rev": "1e1660e6dd00838ba73bc7952e6e73be67da18d1",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"ref": "0.1-extraLibraries",
"repo": "haskell-flake",
"type": "github"
}
},
"hercules-ci-agent": {
"inputs": {
"flake-parts": "flake-parts_2",
"haskell-flake": "haskell-flake",
"nix-darwin": "nix-darwin",
"nixpkgs": "nixpkgs_2",
"pre-commit-hooks-nix": "pre-commit-hooks-nix"
},
"locked": {
"lastModified": 1678446614,
"narHash": "sha256-Z6Gsba5ahn/N0QlF0vJfIEfnZgCs4qr1IZtXAqjbE7s=",
"owner": "hercules-ci",
"repo": "hercules-ci-agent",
"rev": "0b90d1a87c117a5861785cb85833dd1c9df0b6ef",
"type": "github"
},
"original": {
"id": "hercules-ci-agent",
"type": "indirect"
}
},
"hercules-ci-effects": {
"inputs": {
"flake-parts": "flake-parts",
"hercules-ci-agent": "hercules-ci-agent",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1681898675,
"narHash": "sha256-nIJ7CAdiHv4i1no/VgDoeTJLzbLYwu5+/Ycoyzn0S78=",
"owner": "hercules-ci",
"repo": "hercules-ci-effects",
"rev": "15ff4f63e5f28070391a5b09a82f6d5c6cc5c9d0",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "hercules-ci-effects",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"hercules-ci-effects",
"hercules-ci-agent",
"nixpkgs"
]
},
"locked": {
"lastModified": 1673295039,
"narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=",
"owner": "LnL7",
"repo": "nix-darwin",
"rev": "87b9d090ad39b25b2400029c64825fc2a8868943",
"type": "github"
},
"original": {
"owner": "LnL7",
"repo": "nix-darwin",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1682879489,
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
"lastModified": 1678703398,
"narHash": "sha256-Y1mW3dBsoWLHpYm+UIHb5VZ7rx024NNHaF16oZBx++o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
"rev": "67f26c1cfc5d5783628231e776a81c1ade623e0b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
"lastModified": 1678375444,
"narHash": "sha256-XIgHfGvjFvZQ8hrkfocanCDxMefc/77rXeHvYdzBMc8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "130fa0baaa2b93ec45523fdcde942f6844ee9f6e",
"type": "github"
},
"original": {
"dir": "lib",
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1673800717,
"narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1678293141,
"narHash": "sha256-lLlQHaR0y+q6nd6kfpydPTGHhl1rS9nU9OQmztzKOYs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c90c4025bb6e0c4eaf438128a3b2640314b1c58d",
"type": "github"
},
"original": {
@@ -37,20 +300,96 @@
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1678891326,
"narHash": "sha256-cjgrjKx7y+hO9I8O2b6QvBaTt9w7Xhk/5hsnJYTUb2I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1544ef240132d4357d9a39a40c8e6afd1678b052",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1683408522,
"narHash": "sha256-9kcPh6Uxo17a3kK3XCHhcWiV1Yu1kYj22RHiymUhMkU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "897876e4c484f1e8f92009fd11b7d988a121a4e7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"gitignore": "gitignore",
"nixpkgs": [
"hercules-ci-effects",
"hercules-ci-agent",
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1678376203,
"narHash": "sha256-3tyYGyC8h7fBwncLZy5nCUjTJPrHbmNwp47LlNLOHSM=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "1a20b9708962096ec2481eeb2ddca29ed747770a",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"nixpkgs": "nixpkgs"
"flatland": "flatland",
"hercules-ci-effects": "hercules-ci-effects",
"nixpkgs": "nixpkgs_4"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1681913140,
"narHash": "sha256-+m+eXpFa5c8nQ4hAZyshIjovKzhMfd5Eo6jQDk6eeig=",
"lastModified": 1683653808,
"narHash": "sha256-GiKwJySG/YCPIKwz9wSm9fJa5e4CU3GvTYAKZzjBhFo=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "2400b36a2ed40f68a26473f69ac208ba10d98af9",
"rev": "b7cdd93f3e1533e96d4cfa1ac8573e6210a2bedf",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"rust-analyzer-src_2": {
"flake": false,
"locked": {
"lastModified": 1678695923,
"narHash": "sha256-rDhiiU8P6sf6mgj5IKgCuTRN9uYeqWr6xl4XLkKnMWg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "95497533524537b1cc7a2870ce94b0b14503be8b",
"type": "github"
},
"original": {

View File

@@ -1,11 +1,22 @@
{
nixConfig = {
extra-substituters = [ "https://stardustxr.cachix.org" ];
extra-trusted-public-keys = [ "stardustxr.cachix.org-1:mWSn8Ap2RLsIWT/8gsj+VfbJB6xoOkPaZpbjO+r9HBo=" ];
};
# 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 }:
inputs.hercules-ci-effects.url = "github:hercules-ci/hercules-ci-effects";
outputs = { self, nixpkgs, fenix, hercules-ci-effects, flatland, ... }:
let
name = "server";
pkgs = system: import nixpkgs {
@@ -27,7 +38,19 @@
rustc = toolchain;
}).buildRustPackage rec {
pname = "stardust-xr-${name}";
src = ./.;
src = builtins.path {
name = "stardust-xr-source";
path = toString ./.;
filter = path: type:
nixpkgs.lib.all
(n: builtins.baseNameOf path != n)
[
"flake.nix"
"flake.lock"
"nix"
"README.md"
];
};
# ---- START package specific settings ----
version = "0.10.2";
@@ -70,7 +93,41 @@
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");
herculesCI.ciSystems = [ "x86_64-linux" ];
effects = let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
hci-effects = hercules-ci-effects.lib.withPkgs pkgs;
in { branch, rev, ... }: {
gnome-graphical-test = hci-effects.mkEffect {
secretsMap."stardustxrDiscord" = "stardustxrDiscord";
secretsMap."stardustxrIpfs" = "stardustxrIpfs";
effectScript = ''
readSecretString stardustxrDiscord .webhook > .webhook
readSecretString stardustxrIpfs .basicauth > .basicauth
set -x
export RESPONSE=$(curl -H @.basicauth -F file=@${self.packages."x86_64-linux".gnome-graphical-test}/screen.png https://ipfs-api.stardustxr.org/api/v0/add)
export CID=$(echo "$RESPONSE" | ${pkgs.jq}/bin/jq -r .Hash)
set +x
export ADDRESS="https://ipfs.stardustxr.org/ipfs/$CID"
${pkgs.discord-sh}/bin/discord.sh \
--description "\`stardustxr/server\` has been modified, here's how it renders \`weston-cliptest\` on \`flatland\` via \`monado-service\` inside of the \`gnome-graphical-test\`" \
--field "Branch;${branch}" \
--field "Commit ID;${rev}" \
--field "Flatland Revision;${flatland.rev}" \
--field "Reproducer;\`nix build github:stardustxr/server/${rev}#gnome-graphical-test\`" \
--image "$ADDRESS"
'';
};
};
};
}

View File

@@ -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")
'';
}