at 25.11-pre 6.3 kB view raw
1import ./make-test-python.nix ( 2 { pkgs, lib, ... }: 3 { 4 name = "gnome-extensions"; 5 meta.maintainers = [ ]; 6 7 nodes.machine = 8 { pkgs, ... }: 9 { 10 imports = [ ./common/user-account.nix ]; 11 12 # Install all extensions 13 environment.systemPackages = lib.filter (e: e ? extensionUuid) ( 14 lib.attrValues pkgs.gnomeExtensions 15 ); 16 17 # Some extensions are broken, but that's kind of the point of a testing VM 18 nixpkgs.config.allowBroken = true; 19 # There are some aliases which throw exceptions; ignore them. 20 # Also prevent duplicate extensions under different names. 21 nixpkgs.config.allowAliases = false; 22 23 # Configure GDM 24 services.xserver.enable = true; 25 services.xserver.displayManager = { 26 gdm = { 27 enable = true; 28 debug = true; 29 wayland = true; 30 }; 31 autoLogin = { 32 enable = true; 33 user = "alice"; 34 }; 35 }; 36 37 # Configure Gnome 38 services.xserver.desktopManager.gnome.enable = true; 39 services.xserver.desktopManager.gnome.debug = true; 40 41 systemd.user.services = { 42 "org.gnome.Shell@wayland" = { 43 serviceConfig = { 44 ExecStart = [ 45 # Clear the list before overriding it. 46 "" 47 # Eval API is now internal so Shell needs to run in unsafe mode. 48 # TODO: improve test driver so that it supports openqa-like manipulation 49 # that would allow us to drop this mess. 50 "${pkgs.gnome-shell}/bin/gnome-shell --unsafe-mode" 51 ]; 52 }; 53 }; 54 }; 55 56 }; 57 58 testScript = 59 { nodes, ... }: 60 let 61 # Keep line widths somewhat manageable 62 user = nodes.machine.users.users.alice; 63 uid = toString user.uid; 64 bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${uid}/bus"; 65 # Run a command in the appropriate user environment 66 run = command: "su - ${user.name} -c '${bus} ${command}'"; 67 68 # Call javascript in gnome shell, returns a tuple (success, output), where 69 # `success` is true if the dbus call was successful and output is what the 70 # javascript evaluates to. 71 eval = 72 command: 73 run "gdbus call --session -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval ${command}"; 74 75 # False when startup is done 76 startingUp = eval "Main.layoutManager._startingUp"; 77 78 # Extensions to keep always enabled together 79 # Those are extensions that are usually always on for many users, and that we expect to work 80 # well together with most others without conflicts 81 alwaysOnExtensions = map (name: pkgs.gnomeExtensions.${name}.extensionUuid) [ 82 "applications-menu" 83 "user-themes" 84 ]; 85 86 # Extensions to enable and disable individually 87 # Extensions like dash-to-dock and dash-to-panel cannot be enabled at the same time. 88 testExtensions = map (name: pkgs.gnomeExtensions.${name}.extensionUuid) [ 89 "appindicator" 90 "dash-to-dock" 91 "dash-to-panel" 92 "ddterm" 93 "gsconnect" 94 "system-monitor-next" 95 "desktop-icons-ng-ding" 96 "workspace-indicator" 97 "vitals" 98 ]; 99 in 100 '' 101 with subtest("Login to GNOME with GDM"): 102 # wait for gdm to start 103 machine.wait_for_unit("display-manager.service") 104 # wait for the wayland server 105 machine.wait_for_file("/run/user/${uid}/wayland-0") 106 # wait for alice to be logged in 107 machine.wait_for_unit("default.target", "${user.name}") 108 # check that logging in has given the user ownership of devices 109 assert "alice" in machine.succeed("getfacl -p /dev/snd/timer") 110 111 with subtest("Wait for GNOME Shell"): 112 # correct output should be (true, 'false') 113 machine.wait_until_succeeds( 114 "${startingUp} | grep -q 'true,..false'" 115 ) 116 117 # Close the Activities view so that Shell can correctly track the focused window. 118 machine.send_key("esc") 119 # # Disable extension version validation (only use for manual testing) 120 # machine.succeed( 121 # "${run "gsettings set org.gnome.shell disable-extension-version-validation true"}" 122 # ) 123 124 def getState(extension): 125 return machine.succeed( 126 f"${run "gnome-extensions info {extension}"} | grep '^ State: .*$'" 127 ) 128 129 # Assert that some extension is in a specific state 130 def checkState(target, extension): 131 state = getState(extension) 132 assert target in state, f"{state} instead of {target}" 133 134 def checkExtension(extension, disable): 135 with subtest(f"Enable extension '{extension}'"): 136 # Check that the extension is properly initialized; skip out of date ones 137 state = machine.succeed( 138 f"${run "gnome-extensions info {extension}"} | grep '^ State: .*$'" 139 ) 140 if "OUT OF DATE" in state: 141 machine.log(f"Extension {extension} will be skipped because out of date") 142 return 143 144 assert "INITIALIZED" in state, f"{state} instead of INITIALIZED" 145 146 # Enable and optionally disable 147 148 machine.succeed(f"${run "gnome-extensions enable {extension}"}") 149 wait_time = 5 150 while getState(extension) == "ACTIVATING" and (wait_time := wait_time - 1) > 0: 151 machine.log(f"Extension {extension} is still activating, waiting {wait_time} more seconds") 152 machine.sleep(1) 153 checkState("ACTIVE", extension) 154 155 if disable: 156 machine.succeed(f"${run "gnome-extensions disable {extension}"}") 157 checkState("INACTIVE", extension) 158 '' 159 + lib.concatLines (map (e: ''checkExtension("${e}", False)'') alwaysOnExtensions) 160 + lib.concatLines (map (e: ''checkExtension("${e}", True)'') testExtensions); 161 } 162)