Unverified Commit eb76459e authored by Cosima Neidahl's avatar Cosima Neidahl Committed by GitHub
Browse files

Merge pull request #334952 from OPNA2608/fix/ayatana-lomiri-indicator-marking

nixos/ayatana-indicators: Split ayatana and lomiri indicators
parents 67602af1 1b4795b3
Loading
Loading
Loading
Loading
+28 −7
Original line number Diff line number Diff line
@@ -20,11 +20,12 @@ in
      example = lib.literalExpression "with pkgs; [ ayatana-indicator-messages ]";
      description = ''
        List of packages containing Ayatana Indicator services
        that should be brought up by the SystemD "ayatana-indicators" user target.
        that should be brought up by a SystemD "ayatana-indicators" user target.

        Packages specified here must have passthru.ayatana-indicators set correctly.

        If, how, and where these indicators are displayed will depend on your DE.
        Which target they will be brought up by depends on the packages' passthru.ayatana-indicators.
      '';
    };
  };
@@ -39,16 +40,36 @@ in
    # libayatana-common's ayatana-indicators.target with explicit Wants & Before to bring up requested indicator services
    systemd.user.targets =
      let
        indicatorServices = lib.lists.flatten (
          map (pkg: (map (ind: "${ind}.service") pkg.passthru.ayatana-indicators)) cfg.packages
        namesToServices = map (indicator: "${indicator}.service");
        indicatorServices =
          target:
          lib.lists.flatten (
            map (
              pkg:
              if lib.isList pkg.passthru.ayatana-indicators then
                # Old format, add to every target
                (lib.warn "${pkg.name} is using the old passthru.ayatana-indicators format, please update it!" (
                  namesToServices pkg.passthru.ayatana-indicators
                ))
              else
                # New format, filter by target being mentioned
                (namesToServices (
                  builtins.filter (
                    service:
                    builtins.any (
                      targetPrefix: "${targetPrefix}-indicators" == target
                    ) pkg.passthru.ayatana-indicators.${service}
                  ) (builtins.attrNames pkg.passthru.ayatana-indicators)
                ))
            ) cfg.packages
          );
      in
      lib.attrsets.mapAttrs
        (_: desc: {
        (name: desc: {
          description = "Target representing the lifecycle of the ${desc}. Each indicator should be bound to it in its individual service file";
          partOf = [ "graphical-session.target" ];
          wants = indicatorServices;
          before = indicatorServices;
          wants = indicatorServices name;
          before = indicatorServices name;
        })
        {
          ayatana-indicators = "Ayatana Indicators";
+1 −1
Original line number Diff line number Diff line
@@ -141,7 +141,7 @@ in {
  authelia = handleTest ./authelia.nix {};
  avahi = handleTest ./avahi.nix {};
  avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
  ayatana-indicators = handleTest ./ayatana-indicators.nix {};
  ayatana-indicators = runTest ./ayatana-indicators.nix;
  babeld = handleTest ./babeld.nix {};
  bazarr = handleTest ./bazarr.nix {};
  bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {};
+138 −106
Original line number Diff line number Diff line
import ./make-test-python.nix ({ pkgs, lib, ... }: let
{ pkgs, lib, ... }:
let
  user = "alice";
in {
in
{
  name = "ayatana-indicators";

  meta = {
    maintainers = lib.teams.lomiri.members;
  };

  nodes.machine = { config, ... }: {
  nodes.machine =
    { config, ... }:
    {
      imports = [
        ./common/auto.nix
        ./common/user-account.nix
@@ -26,14 +30,17 @@ in {

      services.ayatana-indicators = {
        enable = true;
      packages = with pkgs; [
        packages =
          with pkgs;
          [
            ayatana-indicator-datetime
            ayatana-indicator-display
            ayatana-indicator-messages
            ayatana-indicator-power
            ayatana-indicator-session
            ayatana-indicator-sound
      ] ++ (with pkgs.lomiri; [
          ]
          ++ (with pkgs.lomiri; [
            lomiri-indicator-network
            telephony-service
          ]);
@@ -51,13 +58,9 @@ in {
      networking.networkmanager.enable = true; # lomiri-network-indicator
      # TODO potentially urfkill for lomiri-network-indicator?

    services.dbus.packages = with pkgs.lomiri; [
      libusermetrics
    ];
      services.dbus.packages = with pkgs.lomiri; [ libusermetrics ];

    environment.systemPackages = with pkgs.lomiri; [
      lomiri-schemas
    ];
      environment.systemPackages = with pkgs.lomiri; [ lomiri-schemas ];

      services.telepathy.enable = true;

@@ -72,18 +75,17 @@ in {
    };

  # TODO session indicator starts up in a semi-broken state, but works fine after a restart. maybe being started before graphical session is truly up & ready?
  testScript = { nodes, ... }: let
    runCommandOverServiceList = list: command:
      lib.strings.concatMapStringsSep "\n" command list;
  testScript =
    { nodes, ... }:
    let
      runCommandOverServiceList = list: command: lib.strings.concatMapStringsSep "\n" command list;

      runCommandOverAyatanaIndicators = runCommandOverServiceList
      (builtins.filter
        (service: !(lib.strings.hasPrefix "lomiri" service || lib.strings.hasPrefix "telephony-service" service))
        nodes.machine.systemd.user.targets."ayatana-indicators".wants);
        nodes.machine.systemd.user.targets.ayatana-indicators.wants;

    runCommandOverAllIndicators = runCommandOverServiceList
      nodes.machine.systemd.user.targets."ayatana-indicators".wants;
  in ''
      runCommandOverLomiriIndicators = runCommandOverServiceList nodes.machine.systemd.user.targets.lomiri-indicators.wants;
    in
    ''
      start_all()
      machine.wait_for_x()

@@ -95,10 +97,18 @@ in {
      machine.sleep(10)

      # Now check if all indicators were brought up successfully, and kill them for later
  '' + (runCommandOverAyatanaIndicators (service: let serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service; in ''
    ''
    + (runCommandOverAyatanaIndicators (
      service:
      let
        serviceExec = builtins.replaceStrings [ "." ] [ "-" ] service;
      in
      ''
        machine.wait_until_succeeds("pgrep -u ${user} -f ${serviceExec}")
        machine.succeed("pkill -f ${serviceExec}")
  '')) + ''
      ''
    ))
    + ''

      # Ayatana target is the preferred way of starting up indicators on SystemD session, the graphical session is responsible for starting this if it supports them.
      # Mate currently doesn't do this, so start it manually for checking (https://github.com/mate-desktop/mate-indicator-applet/issues/63)
@@ -110,7 +120,29 @@ in {
      machine.sleep(10)

      # Now check if all indicator services were brought up successfully
  '' + runCommandOverAllIndicators (service: ''
    ''
    + runCommandOverAyatanaIndicators (service: ''
      machine.wait_for_unit("${service}", "${user}")
    '')
    + ''
      # Stop the target
      machine.systemctl("stop ayatana-indicators.target", "${user}")

      # Let all indicator services do their shutdowns
      # Not sure if there's a better way of awaiting this without false-positive potential
      machine.sleep(10)

      # Lomiri uses a different target, which launches a slightly different set of indicators
      machine.systemctl("start lomiri-indicators.target", "${user}")
      machine.wait_for_unit("lomiri-indicators.target", "${user}")

      # Let all indicator services do their startups, potential post-launch crash & restart cycles so we can properly check for failures
      # Not sure if there's a better way of awaiting this without false-positive potential
      machine.sleep(10)

      # Now check if all indicator services were brought up successfully
    ''
    + runCommandOverLomiriIndicators (service: ''
      machine.wait_for_unit("${service}", "${user}")
    '');
})
}
+260 −59
Original line number Diff line number Diff line
@@ -38,6 +38,13 @@ in
      testScript =
        { nodes, ... }:
        ''
          def wait_for_text(text):
              """
              Wait for on-screen text, and try to optimise retry count for slow hardware.
              """
              machine.sleep(10)
              machine.wait_for_text(text)

          start_all()
          machine.wait_for_unit("multi-user.target")

@@ -47,12 +54,12 @@ in
              machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")

              # Start page shows current time
              machine.wait_for_text(r"(AM|PM)")
              wait_for_text(r"(AM|PM)")
              machine.screenshot("lomiri_greeter_launched")

              # Advance to login part
              machine.send_key("ret")
              machine.wait_for_text("${description}")
              wait_for_text("${description}")
              machine.screenshot("lomiri_greeter_login")

              # Login
@@ -62,16 +69,16 @@ in
              # Output rendering from Lomiri has started when it starts printing performance diagnostics
              machine.wait_for_console_text("Last frame took")
              # Look for datetime's clock, one of the last elements to load
              machine.wait_for_text(r"(AM|PM)")
              wait_for_text(r"(AM|PM)")
              machine.screenshot("lomiri_launched")
        '';
    }
  );

  desktop = makeTest (
  desktop-basics = makeTest (
    { pkgs, lib, ... }:
    {
      name = "lomiri-desktop";
      name = "lomiri-desktop-basics";

      meta = {
        maintainers = lib.teams.lomiri.members;
@@ -89,8 +96,6 @@ in

          users.users.${user} = {
            inherit description password;
            # polkit agent test
            extraGroups = [ "wheel" ];
          };

          test-support.displayManager.auto = {
@@ -128,15 +133,7 @@ in
              };
            };

            variables = {
              # So we can test what content-hub is working behind the scenes
              CONTENT_HUB_LOGGING_LEVEL = "2";
            };

            systemPackages = with pkgs; [
              # For a convenient way of kicking off content-hub peer collection
              lomiri.content-hub.examples

              # Forcing alacritty to run as an X11 app when opened from the starter menu
              (symlinkJoin {
                name = "x11-${alacritty.name}";
@@ -205,6 +202,230 @@ in
      testScript =
        { nodes, ... }:
        ''
          def wait_for_text(text):
              """
              Wait for on-screen text, and try to optimise retry count for slow hardware.
              """
              machine.sleep(10)
              machine.wait_for_text(text)

          def mouse_click(xpos, ypos):
              """
              Move the mouse to a screen location and hit left-click.
              """

              # Need to reset to top-left, --absolute doesn't work?
              machine.execute("ydotool mousemove -- -10000 -10000")
              machine.sleep(2)

              # Move
              machine.execute(f"ydotool mousemove -- {xpos} {ypos}")
              machine.sleep(2)

              # Click (C0 - left button: down & up)
              machine.execute("ydotool click 0xC0")
              machine.sleep(2)

          def open_starter():
              """
              Open the starter, and ensure it's opened.
              """

              # Using the keybind has a chance of instantly closing the menu again? Just click the button
              mouse_click(20, 30)

          start_all()
          machine.wait_for_unit("multi-user.target")

          # The session should start, and not be stuck in i.e. a crash loop
          with subtest("lomiri starts"):
              machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
              # Output rendering from Lomiri has started when it starts printing performance diagnostics
              machine.wait_for_console_text("Last frame took")
              # Look for datetime's clock, one of the last elements to load
              wait_for_text(r"(AM|PM)")
              machine.screenshot("lomiri_launched")

          # Working terminal keybind is good
          with subtest("terminal keybind works"):
              machine.send_key("ctrl-alt-t")
              wait_for_text(r"(${user}|machine)")
              machine.screenshot("terminal_opens")

              # lomiri-terminal-app has a separate VM test to test its basic functionality

              machine.send_key("alt-f4")

          # We want the ability to launch applications
          with subtest("starter menu works"):
              open_starter()
              machine.screenshot("starter_opens")

              # Just try the terminal again, we know that it should work
              machine.send_chars("Terminal\n")
              wait_for_text(r"(${user}|machine)")
              machine.send_key("alt-f4")

          # We want support for X11 apps
          with subtest("xwayland support works"):
              open_starter()
              machine.send_chars("Alacritty\n")
              wait_for_text(r"(${user}|machine)")
              machine.screenshot("alacritty_opens")
              machine.send_key("alt-f4")

          # Morph is how we go online
          with subtest("morph browser works"):
              open_starter()
              machine.send_chars("Morph\n")
              wait_for_text(r"(Bookmarks|address|site|visited any)")
              machine.screenshot("morph_open")

              # morph-browser has a separate VM test to test its basic functionalities

              machine.send_key("alt-f4")

          # LSS provides DE settings
          with subtest("system settings open"):
              open_starter()
              machine.send_chars("System Settings\n")
              wait_for_text("Rotation Lock")
              machine.screenshot("settings_open")

              # lomiri-system-settings has a separate VM test to test its basic functionalities

              machine.send_key("alt-f4")
        '';
    }
  );

  desktop-appinteractions = makeTest (
    { pkgs, lib, ... }:
    {
      name = "lomiri-desktop-appinteractions";

      meta = {
        maintainers = lib.teams.lomiri.members;
      };

      nodes.machine =
        { config, ... }:
        {
          imports = [
            ./common/auto.nix
            ./common/user-account.nix
          ];

          virtualisation.memorySize = 2047;

          users.users.${user} = {
            inherit description password;
            # polkit agent test
            extraGroups = [ "wheel" ];
          };

          test-support.displayManager.auto = {
            enable = true;
            inherit user;
          };

          # To control mouse via scripting
          programs.ydotool.enable = true;

          services.desktopManager.lomiri.enable = lib.mkForce true;
          services.displayManager.defaultSession = lib.mkForce "lomiri";

          # Help with OCR
          fonts.packages = [ pkgs.inconsolata ];

          environment = {
            # Help with OCR
            etc."xdg/alacritty/alacritty.yml".text = lib.generators.toYAML { } {
              font = rec {
                normal.family = "Inconsolata";
                bold.family = normal.family;
                italic.family = normal.family;
                bold_italic.family = normal.family;
                size = 16;
              };
              colors = rec {
                primary = {
                  foreground = "0x000000";
                  background = "0xffffff";
                };
                normal = {
                  green = primary.foreground;
                };
              };
            };

            variables = {
              # So we can test what content-hub is working behind the scenes
              CONTENT_HUB_LOGGING_LEVEL = "2";
            };

            systemPackages = with pkgs; [
              # For a convenient way of kicking off content-hub peer collection
              lomiri.content-hub.examples
            ];
          };

          # Help with OCR
          systemd.tmpfiles.settings =
            let
              white = "255, 255, 255";
              black = "0, 0, 0";
              colorSection = color: {
                Color = color;
                Bold = true;
                Transparency = false;
              };
              terminalColors = pkgs.writeText "customized.colorscheme" (
                lib.generators.toINI { } {
                  Background = colorSection white;
                  Foreground = colorSection black;
                  Color2 = colorSection black;
                  Color2Intense = colorSection black;
                }
              );
              terminalConfig = pkgs.writeText "terminal.ubports.conf" (
                lib.generators.toINI { } {
                  General = {
                    colorScheme = "customized";
                    fontSize = "16";
                    fontStyle = "Inconsolata";
                  };
                }
              );
              confBase = "${config.users.users.${user}.home}/.config";
              userDirArgs = {
                mode = "0700";
                user = user;
                group = "users";
              };
            in
            {
              "10-lomiri-test-setup" = {
                "${confBase}".d = userDirArgs;
                "${confBase}/terminal.ubports".d = userDirArgs;
                "${confBase}/terminal.ubports/customized.colorscheme".L.argument = "${terminalColors}";
                "${confBase}/terminal.ubports/terminal.ubports.conf".L.argument = "${terminalConfig}";
              };
            };
        };

      enableOCR = true;

      testScript =
        { nodes, ... }:
        ''
          def wait_for_text(text):
              """
              Wait for on-screen text, and try to optimise retry count for slow hardware.
              """
              machine.sleep(10)
              machine.wait_for_text(text)

          def toggle_maximise():
              """
              Maximise the current window.
@@ -252,20 +473,20 @@ in
              # Output rendering from Lomiri has started when it starts printing performance diagnostics
              machine.wait_for_console_text("Last frame took")
              # Look for datetime's clock, one of the last elements to load
              machine.wait_for_text(r"(AM|PM)")
              wait_for_text(r"(AM|PM)")
              machine.screenshot("lomiri_launched")

          # Working terminal keybind is good
          with subtest("terminal keybind works"):
              machine.send_key("ctrl-alt-t")
              machine.wait_for_text(r"(${user}|machine)")
              wait_for_text(r"(${user}|machine)")
              machine.screenshot("terminal_opens")

              # lomiri-terminal-app has a separate VM test to test its basic functionality

              # for the LSS content-hub test to work reliably, we need to kick off peer collecting
              machine.send_chars("content-hub-test-importer\n")
              machine.wait_for_text(r"(/build/source|hub.cpp|handler.cpp|void|virtual|const)") # awaiting log messages from content-hub
              wait_for_text(r"(/build/source|hub.cpp|handler.cpp|void|virtual|const)") # awaiting log messages from content-hub
              machine.send_key("ctrl-c")

              # Doing this here, since we need an in-session shell & separately starting a terminal again wastes time
@@ -282,40 +503,11 @@ in

              machine.send_key("alt-f4")

          # We want the ability to launch applications
          with subtest("starter menu works"):
              open_starter()
              machine.screenshot("starter_opens")

              # Just try the terminal again, we know that it should work
              machine.send_chars("Terminal\n")
              machine.wait_for_text(r"(${user}|machine)")
              machine.send_key("alt-f4")

          # We want support for X11 apps
          with subtest("xwayland support works"):
              open_starter()
              machine.send_chars("Alacritty\n")
              machine.wait_for_text(r"(${user}|machine)")
              machine.screenshot("alacritty_opens")
              machine.send_key("alt-f4")

          # Morph is how we go online
          with subtest("morph browser works"):
              open_starter()
              machine.send_chars("Morph\n")
              machine.wait_for_text(r"(Bookmarks|address|site|visited any)")
              machine.screenshot("morph_open")

              # morph-browser has a separate VM test, there isn't anything new we could test here

              machine.send_key("alt-f4")

          # LSS provides DE settings
          with subtest("system settings open"):
              open_starter()
              machine.send_chars("System Settings\n")
              machine.wait_for_text("Rotation Lock")
              wait_for_text("Rotation Lock")
              machine.screenshot("settings_open")

              # lomiri-system-settings has a separate VM test, only test Lomiri-specific content-hub functionalities here
@@ -331,7 +523,7 @@ in
              machine.send_key("tab")
              machine.send_key("tab")
              machine.send_key("ret")
              machine.wait_for_text("Background image")
              wait_for_text("Background image")

              # Try to load custom background
              machine.send_key("shift-tab")
@@ -343,14 +535,14 @@ in
              machine.send_key("ret")

              # Peers should be loaded
              machine.wait_for_text("Morph") # or Gallery, but Morph is already packaged
              wait_for_text("Morph") # or Gallery, but Morph is already packaged
              machine.screenshot("settings_content-hub_peers")

              # Select Morph as content source
              mouse_click(370, 100)

              # Expect Morph to be brought into the foreground, with its Downloads page open
              machine.wait_for_text("No downloads")
              wait_for_text("No downloads")

              # If content-hub encounters a problem, it may have crashed the original application issuing the request.
              # Check that it's still alive
@@ -403,6 +595,8 @@ in

          # Help with OCR
          fonts.packages = [ pkgs.inconsolata ];

          environment.systemPackages = with pkgs; [ qt5.qttools ];
        };

      enableOCR = true;
@@ -410,6 +604,13 @@ in
      testScript =
        { nodes, ... }:
        ''
          def wait_for_text(text):
              """
              Wait for on-screen text, and try to optimise retry count for slow hardware.
              """
              machine.sleep(10)
              machine.wait_for_text(text)

          def mouse_click(xpos, ypos):
              """
              Move the mouse to a screen location and hit left-click.
@@ -436,14 +637,14 @@ in
              # Output rendering from Lomiri has started when it starts printing performance diagnostics
              machine.wait_for_console_text("Last frame took")
              # Look for datetime's clock, one of the last elements to load
              machine.wait_for_text(r"(AM|PM)")
              wait_for_text(r"(AM|PM)")
              machine.screenshot("lomiri_launched")

          # The ayatana indicators are an important part of the experience, and they hold the only graphical way of exiting the session.
          # There's a test app we could use that also displays their contents, but it's abit inconsistent.
          with subtest("ayatana indicators work"):
              mouse_click(735, 0) # the cog in the top-right, for the session indicator
              machine.wait_for_text(r"(Notifications|Rotation|Battery|Sound|Time|Date|System)")
              wait_for_text(r"(Notifications|Rotation|Battery|Sound|Time|Date|System)")
              machine.screenshot("indicators_open")

              # Indicator order within the menus *should* be fixed based on per-indicator order setting
@@ -457,32 +658,32 @@ in

              with subtest("ayatana indicator display works"):
                  # We start on this, don't go right
                  machine.wait_for_text("Lock")
                  wait_for_text("Lock")
                  machine.screenshot("indicators_display")

              with subtest("lomiri indicator network works"):
                  machine.send_key("right")
                  machine.wait_for_text(r"(Flight|Wi-Fi)")
                  wait_for_text(r"(Flight|Wi-Fi)")
                  machine.screenshot("indicators_network")

              with subtest("ayatana indicator sound works"):
                  machine.send_key("right")
                  machine.wait_for_text(r"(Silent|Volume)")
                  wait_for_text(r"(Silent|Volume)")
                  machine.screenshot("indicators_sound")

              with subtest("ayatana indicator power works"):
                  machine.send_key("right")
                  machine.wait_for_text(r"(Charge|Battery settings)")
                  wait_for_text(r"(Charge|Battery settings)")
                  machine.screenshot("indicators_power")

              with subtest("ayatana indicator datetime works"):
                  machine.send_key("right")
                  machine.wait_for_text("Time and Date Settings")
                  wait_for_text("Time and Date Settings")
                  machine.screenshot("indicators_timedate")

              with subtest("ayatana indicator session works"):
                  machine.send_key("right")
                  machine.wait_for_text("Log Out")
                  wait_for_text("Log Out")
                  machine.screenshot("indicators_session")

                  # We should be able to log out and return to the greeter
+72 −65

File changed.

Preview size limit exceeded, changes collapsed.

Loading