2 makeTest = import ./make-test-python.nix;
3 # Just to make sure everything is the same, need it for OCR & navigating greeter
5 description = "Alice Foobar";
8 # tmpfiles setup to make OCRing on terminal output more reliable
9 terminalOcrTmpfilesSetup =
16 white = "255, 255, 255";
18 colorSection = color: {
23 terminalColors = pkgs.writeText "customized.colorscheme" (
24 lib.generators.toINI { } {
25 Background = colorSection white;
26 Foreground = colorSection black;
27 Color2 = colorSection black;
28 Color2Intense = colorSection black;
31 terminalConfig = pkgs.writeText "terminal.ubports.conf" (
32 lib.generators.toINI { } {
34 colorScheme = "customized";
36 fontStyle = "Inconsolata";
40 confBase = "${config.users.users.${user}.home}/.config";
48 "${confBase}".d = userDirArgs;
49 "${confBase}/terminal.ubports".d = userDirArgs;
50 "${confBase}/terminal.ubports/customized.colorscheme".L.argument = "${terminalColors}";
51 "${confBase}/terminal.ubports/terminal.ubports.conf".L.argument = "${terminalConfig}";
58 name = "lomiri-greeter";
61 maintainers = lib.teams.lomiri.members;
67 imports = [ ./common/user-account.nix ];
69 virtualisation.memorySize = 2047;
71 users.users.${user} = {
72 inherit description password;
75 services.desktopManager.lomiri.enable = lib.mkForce true;
76 services.displayManager.defaultSession = lib.mkForce "lomiri";
79 fonts.packages = [ pkgs.inconsolata ];
87 def wait_for_text(text):
89 Wait for on-screen text, and try to optimise retry count for slow hardware.
92 machine.wait_for_text(text)
95 machine.wait_for_unit("multi-user.target")
97 # Lomiri in greeter mode should work & be able to start a session
98 with subtest("lomiri greeter works"):
99 machine.wait_for_unit("display-manager.service")
100 machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
102 # Start page shows current time
103 wait_for_text(r"(AM|PM)")
104 machine.screenshot("lomiri_greeter_launched")
106 # Advance to login part
107 machine.send_key("ret")
108 wait_for_text("${description}")
109 machine.screenshot("lomiri_greeter_login")
112 machine.send_chars("${password}\n")
113 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
115 # Output rendering from Lomiri has started when it starts printing performance diagnostics
116 machine.wait_for_console_text("Last frame took")
117 # Look for datetime's clock, one of the last elements to load
118 wait_for_text(r"(AM|PM)")
119 machine.screenshot("lomiri_launched")
124 desktop-basics = makeTest (
127 name = "lomiri-desktop-basics";
130 maintainers = lib.teams.lomiri.members;
138 ./common/user-account.nix
141 virtualisation.memorySize = 2047;
143 users.users.${user} = {
144 inherit description password;
147 test-support.displayManager.auto = {
152 # To control mouse via scripting
153 programs.ydotool.enable = true;
155 services.desktopManager.lomiri.enable = lib.mkForce true;
156 services.displayManager.defaultSession = lib.mkForce "lomiri";
159 fonts.packages = [ pkgs.inconsolata ];
163 etc."xdg/alacritty/alacritty.yml".text = lib.generators.toYAML { } {
165 normal.family = "Inconsolata";
166 bold.family = normal.family;
167 italic.family = normal.family;
168 bold_italic.family = normal.family;
173 foreground = "0x000000";
174 background = "0xffffff";
177 green = primary.foreground;
182 systemPackages = with pkgs; [
183 # Forcing alacritty to run as an X11 app when opened from the starter menu
185 name = "x11-${alacritty.name}";
187 paths = [ alacritty ];
189 nativeBuildInputs = [ makeWrapper ];
192 wrapProgram $out/bin/alacritty \
193 --set WINIT_UNIX_BACKEND x11 \
194 --set WAYLAND_DISPLAY ""
197 inherit (alacritty) meta;
203 systemd.tmpfiles.settings = {
204 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
213 def wait_for_text(text):
215 Wait for on-screen text, and try to optimise retry count for slow hardware.
218 machine.wait_for_text(text)
220 def mouse_click(xpos, ypos):
222 Move the mouse to a screen location and hit left-click.
225 # Need to reset to top-left, --absolute doesn't work?
226 machine.execute("ydotool mousemove -- -10000 -10000")
230 machine.execute(f"ydotool mousemove -- {xpos} {ypos}")
233 # Click (C0 - left button: down & up)
234 machine.execute("ydotool click 0xC0")
239 Open the starter, and ensure it's opened.
242 # Using the keybind has a chance of instantly closing the menu again? Just click the button
246 machine.wait_for_unit("multi-user.target")
248 # The session should start, and not be stuck in i.e. a crash loop
249 with subtest("lomiri starts"):
250 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
251 # Output rendering from Lomiri has started when it starts printing performance diagnostics
252 machine.wait_for_console_text("Last frame took")
253 # Look for datetime's clock, one of the last elements to load
254 wait_for_text(r"(AM|PM)")
255 machine.screenshot("lomiri_launched")
257 # Working terminal keybind is good
258 with subtest("terminal keybind works"):
259 machine.send_key("ctrl-alt-t")
260 wait_for_text(r"(${user}|machine)")
261 machine.screenshot("terminal_opens")
263 # lomiri-terminal-app has a separate VM test to test its basic functionality
265 machine.send_key("alt-f4")
267 # We want the ability to launch applications
268 with subtest("starter menu works"):
270 machine.screenshot("starter_opens")
272 # Just try the terminal again, we know that it should work
273 machine.send_chars("Terminal\n")
274 wait_for_text(r"(${user}|machine)")
275 machine.send_key("alt-f4")
277 # We want support for X11 apps
278 with subtest("xwayland support works"):
280 machine.send_chars("Alacritty\n")
281 wait_for_text(r"(${user}|machine)")
282 machine.screenshot("alacritty_opens")
283 machine.send_key("alt-f4")
285 # Morph is how we go online
286 with subtest("morph browser works"):
288 machine.send_chars("Morph\n")
289 wait_for_text(r"(Bookmarks|address|site|visited any)")
290 machine.screenshot("morph_open")
292 # morph-browser has a separate VM test to test its basic functionalities
294 machine.send_key("alt-f4")
296 # LSS provides DE settings
297 with subtest("system settings open"):
299 machine.send_chars("System Settings\n")
300 wait_for_text("Rotation Lock")
301 machine.screenshot("settings_open")
303 # lomiri-system-settings has a separate VM test to test its basic functionalities
305 machine.send_key("alt-f4")
310 desktop-appinteractions = makeTest (
313 name = "lomiri-desktop-appinteractions";
316 maintainers = lib.teams.lomiri.members;
324 ./common/user-account.nix
327 virtualisation.memorySize = 2047;
329 users.users.${user} = {
330 inherit description password;
332 extraGroups = [ "wheel" ];
335 test-support.displayManager.auto = {
340 # To control mouse via scripting
341 programs.ydotool.enable = true;
343 services.desktopManager.lomiri.enable = lib.mkForce true;
344 services.displayManager.defaultSession = lib.mkForce "lomiri";
347 fonts.packages = [ pkgs.inconsolata ];
351 etc."xdg/alacritty/alacritty.yml".text = lib.generators.toYAML { } {
353 normal.family = "Inconsolata";
354 bold.family = normal.family;
355 italic.family = normal.family;
356 bold_italic.family = normal.family;
361 foreground = "0x000000";
362 background = "0xffffff";
365 green = primary.foreground;
371 # So we can test what lomiri-content-hub is working behind the scenes
372 LOMIRI_CONTENT_HUB_LOGGING_LEVEL = "2";
375 systemPackages = with pkgs; [
376 # For a convenient way of kicking off lomiri-content-hub peer collection
377 lomiri.lomiri-content-hub.examples
382 systemd.tmpfiles.settings = {
383 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
392 def wait_for_text(text):
394 Wait for on-screen text, and try to optimise retry count for slow hardware.
397 machine.wait_for_text(text)
399 def toggle_maximise():
401 Maximise the current window.
403 machine.send_key("ctrl-meta_l-up")
405 # For some reason, Lomiri in these VM tests very frequently opens the starter menu a few seconds after sending the above.
406 # Because this isn't 100% reproducible all the time, and there is no command to await when OCR doesn't pick up some text,
407 # the best we can do is send some Escape input after waiting some arbitrary time and hope that it works out fine.
409 machine.send_key("esc")
412 def mouse_click(xpos, ypos):
414 Move the mouse to a screen location and hit left-click.
417 # Need to reset to top-left, --absolute doesn't work?
418 machine.execute("ydotool mousemove -- -10000 -10000")
422 machine.execute(f"ydotool mousemove -- {xpos} {ypos}")
425 # Click (C0 - left button: down & up)
426 machine.execute("ydotool click 0xC0")
431 Open the starter, and ensure it's opened.
434 # Using the keybind has a chance of instantly closing the menu again? Just click the button
438 machine.wait_for_unit("multi-user.target")
440 # The session should start, and not be stuck in i.e. a crash loop
441 with subtest("lomiri starts"):
442 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
443 # Output rendering from Lomiri has started when it starts printing performance diagnostics
444 machine.wait_for_console_text("Last frame took")
445 # Look for datetime's clock, one of the last elements to load
446 wait_for_text(r"(AM|PM)")
447 machine.screenshot("lomiri_launched")
449 # Working terminal keybind is good
450 with subtest("terminal keybind works"):
451 machine.send_key("ctrl-alt-t")
452 wait_for_text(r"(${user}|machine)")
453 machine.screenshot("terminal_opens")
455 # lomiri-terminal-app has a separate VM test to test its basic functionality
457 # for the LSS lomiri-content-hub test to work reliably, we need to kick off peer collecting
458 machine.send_chars("lomiri-content-hub-test-importer\n")
459 wait_for_text(r"(/build/source|hub.cpp|handler.cpp|void|virtual|const)") # awaiting log messages from lomiri-content-hub
460 machine.send_key("ctrl-c")
462 # Doing this here, since we need an in-session shell & separately starting a terminal again wastes time
463 with subtest("polkit agent works"):
464 machine.send_chars("pkexec touch /tmp/polkit-test\n")
465 # There's an authentication notification here that gains focus, but we struggle with OCRing it
466 # Just hope that it's up after a short wait
468 machine.screenshot("polkit_agent")
469 machine.send_chars("${password}")
470 machine.sleep(2) # Hopefully enough delay to make sure all the password characters have been registered? Maybe just placebo
471 machine.send_chars("\n")
472 machine.wait_for_file("/tmp/polkit-test", 10)
474 machine.send_key("alt-f4")
476 # LSS provides DE settings
477 with subtest("system settings open"):
479 machine.send_chars("System Settings\n")
480 wait_for_text("Rotation Lock")
481 machine.screenshot("settings_open")
483 # lomiri-system-settings has a separate VM test, only test Lomiri-specific lomiri-content-hub functionalities here
485 # Make fullscreen, can't navigate to Background plugin via keyboard unless window has non-phone-like aspect ratio
488 # Load Background plugin
489 machine.send_key("tab")
490 machine.send_key("tab")
491 machine.send_key("tab")
492 machine.send_key("tab")
493 machine.send_key("tab")
494 machine.send_key("tab")
495 machine.send_key("ret")
496 wait_for_text("Background image")
498 # Try to load custom background
499 machine.send_key("shift-tab")
500 machine.send_key("shift-tab")
501 machine.send_key("shift-tab")
502 machine.send_key("shift-tab")
503 machine.send_key("shift-tab")
504 machine.send_key("shift-tab")
505 machine.send_key("ret")
507 # Peers should be loaded
508 wait_for_text("Morph") # or Gallery, but Morph is already packaged
509 machine.screenshot("settings_lomiri-content-hub_peers")
511 # Select Morph as content source
512 mouse_click(370, 100)
514 # Expect Morph to be brought into the foreground, with its Downloads page open
515 wait_for_text("No downloads")
517 # If lomiri-content-hub encounters a problem, it may have crashed the original application issuing the request.
518 # Check that it's still alive
519 machine.succeed("pgrep -u ${user} -f lomiri-system-settings")
521 machine.screenshot("lomiri-content-hub_exchange")
523 # Testing any more would require more applications & setup, the fact that it's already being attempted is a good sign
524 machine.send_key("esc")
526 machine.sleep(2) # sleep a tiny bit so morph can close & the focus can return to LSS
527 machine.send_key("alt-f4")
532 desktop-ayatana-indicators = makeTest (
535 name = "lomiri-desktop-ayatana-indicators";
538 maintainers = lib.teams.lomiri.members;
546 ./common/user-account.nix
549 virtualisation.memorySize = 2047;
551 users.users.${user} = {
552 inherit description password;
555 test-support.displayManager.auto = {
560 # To control mouse via scripting
561 programs.ydotool.enable = true;
563 services.desktopManager.lomiri.enable = lib.mkForce true;
564 services.displayManager.defaultSession = lib.mkForce "lomiri";
567 fonts.packages = [ pkgs.inconsolata ];
569 environment.systemPackages = with pkgs; [ qt5.qttools ];
577 def wait_for_text(text):
579 Wait for on-screen text, and try to optimise retry count for slow hardware.
582 machine.wait_for_text(text)
584 def mouse_click(xpos, ypos):
586 Move the mouse to a screen location and hit left-click.
589 # Need to reset to top-left, --absolute doesn't work?
590 machine.execute("ydotool mousemove -- -10000 -10000")
594 machine.execute(f"ydotool mousemove -- {xpos} {ypos}")
597 # Click (C0 - left button: down & up)
598 machine.execute("ydotool click 0xC0")
602 machine.wait_for_unit("multi-user.target")
604 # The session should start, and not be stuck in i.e. a crash loop
605 with subtest("lomiri starts"):
606 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
607 # Output rendering from Lomiri has started when it starts printing performance diagnostics
608 machine.wait_for_console_text("Last frame took")
609 # Look for datetime's clock, one of the last elements to load
610 wait_for_text(r"(AM|PM)")
611 machine.screenshot("lomiri_launched")
613 # The ayatana indicators are an important part of the experience, and they hold the only graphical way of exiting the session.
614 # There's a test app we could use that also displays their contents, but it's abit inconsistent.
615 with subtest("ayatana indicators work"):
616 mouse_click(735, 0) # the cog in the top-right, for the session indicator
617 wait_for_text(r"(Notifications|Rotation|Battery|Sound|Time|Date|System)")
618 machine.screenshot("indicators_open")
620 # Indicator order within the menus *should* be fixed based on per-indicator order setting
621 # Session is the one we clicked, but the last we should test (logout). Go as far left as we can test.
622 machine.send_key("left")
623 machine.send_key("left")
624 machine.send_key("left")
625 machine.send_key("left")
626 machine.send_key("left")
627 machine.send_key("left")
628 # Notifications are usually empty, nothing to check there
630 with subtest("ayatana indicator display works"):
631 # We start on this, don't go right
632 wait_for_text("Lock")
633 machine.screenshot("indicators_display")
635 with subtest("ayatana indicator bluetooth works"):
636 machine.send_key("right")
637 wait_for_text("Bluetooth settings")
638 machine.screenshot("indicators_bluetooth")
640 with subtest("lomiri indicator network works"):
641 machine.send_key("right")
642 wait_for_text(r"(Flight|Wi-Fi)")
643 machine.screenshot("indicators_network")
645 with subtest("ayatana indicator sound works"):
646 machine.send_key("right")
647 wait_for_text(r"(Silent|Volume)")
648 machine.screenshot("indicators_sound")
650 with subtest("ayatana indicator power works"):
651 machine.send_key("right")
652 wait_for_text(r"(Charge|Battery settings)")
653 machine.screenshot("indicators_power")
655 with subtest("ayatana indicator datetime works"):
656 machine.send_key("right")
657 wait_for_text("Time and Date Settings")
658 machine.screenshot("indicators_timedate")
660 with subtest("ayatana indicator session works"):
661 machine.send_key("right")
662 wait_for_text("Log Out")
663 machine.screenshot("indicators_session")
665 # We should be able to log out and return to the greeter
666 mouse_click(720, 280) # "Log Out"
667 mouse_click(400, 240) # confirm logout
668 machine.wait_until_fails("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
681 name = "lomiri-keymap";
684 maintainers = lib.teams.lomiri.members;
690 imports = [ ./common/user-account.nix ];
692 virtualisation.memorySize = 2047;
694 users.users.${user} = {
696 password = lib.mkForce pwOutput;
699 services.desktopManager.lomiri.enable = lib.mkForce true;
700 services.displayManager.defaultSession = lib.mkForce "lomiri";
703 fonts.packages = [ pkgs.inconsolata ];
705 services.xserver.xkb.layout = lib.strings.concatStringsSep "," [
706 # Start with a non-QWERTY keymap to test keymap patch
708 # Then a QWERTY one to test switching
713 systemd.tmpfiles.settings = {
714 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
723 def wait_for_text(text):
725 Wait for on-screen text, and try to optimise retry count for slow hardware.
728 machine.wait_for_text(text)
731 machine.wait_for_unit("multi-user.target")
733 # Lomiri in greeter mode should use the correct keymap
734 with subtest("lomiri greeter keymap works"):
735 machine.wait_for_unit("display-manager.service")
736 machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
738 # Start page shows current time
739 wait_for_text(r"(AM|PM)")
740 machine.screenshot("lomiri_greeter_launched")
742 # Advance to login part
743 machine.send_key("ret")
744 wait_for_text("${description}")
745 machine.screenshot("lomiri_greeter_login")
748 machine.send_chars("${pwInput}\n")
749 machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
751 # Output rendering from Lomiri has started when it starts printing performance diagnostics
752 machine.wait_for_console_text("Last frame took")
753 # Look for datetime's clock, one of the last elements to load
754 wait_for_text(r"(AM|PM)")
755 machine.screenshot("lomiri_launched")
757 # Lomiri in desktop mode should use the correct keymap
758 with subtest("lomiri session keymap works"):
759 machine.send_key("ctrl-alt-t")
760 wait_for_text(r"(${user}|machine)")
761 machine.screenshot("terminal_opens")
763 machine.send_chars("touch ${pwInput}\n")
764 machine.wait_for_file("/home/alice/${pwOutput}", 10)
766 # Issues with this keybind: input leaks to focused surface, may open launcher
767 # Don't have the keyboard indicator to handle this better
768 machine.send_key("meta_l-spc")
769 machine.wait_for_console_text('SET KEYMAP "us"')
771 # Handle keybind fallout
772 machine.sleep(10) # wait for everything to settle
773 machine.send_key("esc") # close launcher in case it was opened
774 machine.sleep(2) # wait for animation to finish
775 # Make sure input leaks are gone
776 machine.send_key("backspace")
777 machine.send_key("backspace")
778 machine.send_key("backspace")
779 machine.send_key("backspace")
780 machine.send_key("backspace")
781 machine.send_key("backspace")
782 machine.send_key("backspace")
783 machine.send_key("backspace")
784 machine.send_key("backspace")
785 machine.send_key("backspace")
787 machine.send_chars("touch ${pwInput}\n")
788 machine.wait_for_file("/home/alice/${pwInput}", 10)
790 machine.send_key("alt-f4")