1 import ./make-test-python.nix ({ pkgs, lib, ... }: let
2 # Just to make sure everything is the same, need it for OCR & navigating greeter
4 description = "Alice Foobar";
10 maintainers = lib.teams.lomiri.members;
13 nodes.machine = { config, ... }: {
15 ./common/user-account.nix
18 users.users.${user} = {
19 inherit description password;
22 services.desktopManager.lomiri.enable = lib.mkForce true;
23 services.displayManager.defaultSession = lib.mkForce "lomiri";
25 fonts.packages = [ pkgs.inconsolata ];
29 etc."xdg/alacritty/alacritty.yml".text = lib.generators.toYAML { } {
31 normal.family = "Inconsolata";
32 bold.family = normal.family;
33 italic.family = normal.family;
34 bold_italic.family = normal.family;
39 foreground = "0x000000";
40 background = "0xffffff";
43 green = primary.foreground;
49 # So we can test what content-hub is working behind the scenes
50 CONTENT_HUB_LOGGING_LEVEL = "2";
53 systemPackages = with pkgs; [
54 # For a convenient way of kicking off content-hub peer collection
55 lomiri.content-hub.examples
57 # Forcing alacritty to run as an X11 app when opened from the starter menu
59 name = "x11-${alacritty.name}";
61 paths = [ alacritty ];
63 nativeBuildInputs = [ makeWrapper ];
66 wrapProgram $out/bin/alacritty \
67 --set WINIT_UNIX_BACKEND x11 \
68 --set WAYLAND_DISPLAY ""
71 inherit (alacritty) meta;
77 systemd.tmpfiles.settings = let
78 white = "255, 255, 255";
80 colorSection = color: {
85 terminalColors = pkgs.writeText "customized.colorscheme" (lib.generators.toINI {} {
86 Background = colorSection white;
87 Foreground = colorSection black;
88 Color2 = colorSection black;
89 Color2Intense = colorSection black;
91 terminalConfig = pkgs.writeText "terminal.ubports.conf" (lib.generators.toINI {} {
93 colorScheme = "customized";
95 fontStyle = "Inconsolata";
98 confBase = "${config.users.users.${user}.home}/.config";
105 "10-lomiri-test-setup" = {
106 "${confBase}".d = userDirArgs;
107 "${confBase}/terminal.ubports".d = userDirArgs;
108 "${confBase}/terminal.ubports/customized.colorscheme".L.argument = "${terminalColors}";
109 "${confBase}/terminal.ubports/terminal.ubports.conf".L.argument = "${terminalConfig}";
116 testScript = { nodes, ... }: ''
119 Open the starter, and ensure it's opened.
121 machine.send_key("meta_l-a")
122 # Look for any of the default apps
123 machine.wait_for_text(r"(Search|System|Settings|Morph|Browser|Terminal|Alacritty)")
125 def toggle_maximise():
127 Send the keybind to maximise the current window.
129 machine.send_key("ctrl-meta_l-up")
131 # For some reason, Lomiri in these VM tests very frequently opens the starter menu a few seconds after sending the above.
132 # Because this isn't 100% reproducible all the time, and there is no command to await when OCR doesn't pick up some text,
133 # the best we can do is send some Escape input after waiting some arbitrary time and hope that it works out fine.
135 machine.send_key("esc")
139 machine.wait_for_unit("multi-user.target")
141 # Lomiri in greeter mode should work & be able to start a session
142 with subtest("lomiri greeter works"):
143 machine.wait_for_unit("display-manager.service")
144 # Start page shows current tie
145 machine.wait_for_text(r"(AM|PM)")
146 machine.screenshot("lomiri_greeter_launched")
148 # Advance to login part
149 machine.send_key("ret")
150 machine.wait_for_text("${description}")
151 machine.screenshot("lomiri_greeter_login")
154 machine.send_chars("${password}\n")
155 # Best way I can think of to differenciate "Lomiri in LightDM greeter mode" from "Lomiri in user shell mode"
156 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
158 # The session should start, and not be stuck in i.e. a crash loop
159 with subtest("lomiri starts"):
160 # Output rendering from Lomiri has started when it starts printing performance diagnostics
161 machine.wait_for_console_text("Last frame took")
162 # Look for datetime's clock, one of the last elements to load
163 machine.wait_for_text(r"(AM|PM)")
164 machine.screenshot("lomiri_launched")
166 # Working terminal keybind is good
167 with subtest("terminal keybind works"):
168 machine.send_key("ctrl-alt-t")
169 machine.wait_for_text(r"(${user}|machine)")
170 machine.screenshot("terminal_opens")
172 # lomiri-terminal-app has a separate VM test to test its basic functionality
174 # for the LSS content-hub test to work reliably, we need to kick off peer collecting
175 machine.send_chars("content-hub-test-importer\n")
176 machine.wait_for_text(r"(/build/source|hub.cpp|handler.cpp|void|virtual|const)") # awaiting log messages from content-hub
177 machine.send_key("ctrl-c")
179 machine.send_key("alt-f4")
181 # We want the ability to launch applications
182 with subtest("starter menu works"):
184 machine.screenshot("starter_opens")
186 # Just try the terminal again, we know that it should work
187 machine.send_chars("Terminal\n")
188 machine.wait_for_text(r"(${user}|machine)")
189 machine.send_key("alt-f4")
191 # We want support for X11 apps
192 with subtest("xwayland support works"):
194 machine.send_chars("Alacritty\n")
195 machine.wait_for_text(r"(${user}|machine)")
196 machine.screenshot("alacritty_opens")
197 machine.send_key("alt-f4")
199 # LSS provides DE settings
200 with subtest("system settings open"):
202 machine.send_chars("System Settings\n")
203 machine.wait_for_text("Rotation Lock")
204 machine.screenshot("settings_open")
206 # lomiri-system-settings has a separate VM test, only test Lomiri-specific content-hub functionalities here
208 # Make fullscreen, can't navigate to Background plugin via keyboard unless window has non-phone-like aspect ratio
211 # Load Background plugin
212 machine.send_key("tab")
213 machine.send_key("tab")
214 machine.send_key("tab")
215 machine.send_key("tab")
216 machine.send_key("tab")
217 machine.send_key("tab")
218 machine.send_key("ret")
219 machine.wait_for_text("Background image")
221 # Try to load custom background
222 machine.send_key("shift-tab")
223 machine.send_key("shift-tab")
224 machine.send_key("shift-tab")
225 machine.send_key("shift-tab")
226 machine.send_key("shift-tab")
227 machine.send_key("shift-tab")
228 machine.send_key("ret")
230 # Peers should be loaded
231 machine.wait_for_text("Morph") # or Gallery, but Morph is already packaged
232 machine.screenshot("settings_content-hub_peers")
234 # Sadly, it doesn't seem possible to actually select a peer and attempt a content-hub data exchange with just the keyboard
236 machine.send_key("alt-f4")
238 # Morph is how we go online
239 with subtest("morph browser works"):
241 machine.send_chars("Morph\n")
242 machine.wait_for_text(r"(Bookmarks|address|site|visited any)")
243 machine.screenshot("morph_open")
245 # morph-browser has a separate VM test, there isn't anything new we could test here
247 machine.send_key("alt-f4")
249 # The ayatana indicators are an important part of the experience, and they hold the only graphical way of exiting the session.
250 # Reaching them via the intended way requires wayland mouse control, but ydotool lacks a module for its daemon:
251 # https://github.com/NixOS/nixpkgs/issues/183659
252 # Luckily, there's a test app that also displays their contents, but it's abit inconsistent. Hopefully this is *good-enough*.
253 with subtest("ayatana indicators work"):
255 machine.send_chars("Indicators\n")
256 machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
257 machine.screenshot("indicators_open")
259 # Element tab order within the indicator menus is not fully deterministic
260 # Only check that the indicators are listed & their items load
262 with subtest("lomiri indicator network works"):
263 # Select indicator-network
264 machine.send_key("tab")
265 # Don't go further down, first entry
266 machine.send_key("ret")
267 machine.wait_for_text(r"(Flight|Wi-Fi)")
268 machine.screenshot("indicators_network")
270 machine.send_key("shift-tab")
271 machine.send_key("ret")
272 machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
274 with subtest("ayatana indicator datetime works"):
275 # Select ayatana-indicator-datetime
276 machine.send_key("tab")
277 machine.send_key("down")
278 machine.send_key("ret")
279 machine.wait_for_text("Time and Date Settings")
280 machine.screenshot("indicators_timedate")
282 machine.send_key("shift-tab")
283 machine.send_key("ret")
284 machine.wait_for_text(r"(Indicators|Client|List|network|datetime|session)")
286 with subtest("ayatana indicator session works"):
287 # Select ayatana-indicator-session
288 machine.send_key("tab")
289 machine.send_key("down")
290 machine.send_key("ret")
291 machine.wait_for_text("Log Out")
292 machine.screenshot("indicators_session")