about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-11-30 23:08:21 +0100
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2024-11-30 23:08:21 +0100
commitc0e331f216f5cb25636a07a764c60e0af6be98fa (patch)
tree868f8bded8b99e77b86a24a1ee82aaf5e1c11070
parentfeat(modules/lf): Add a keymap for cd-ing to the download directory (diff)
downloadnixos-config-c0e331f216f5cb25636a07a764c60e0af6be98fa.tar.gz
nixos-config-c0e331f216f5cb25636a07a764c60e0af6be98fa.zip
tests(infrastructure/driver): Support executing commands
Diffstat (limited to '')
-rw-r--r--tests/README.md5
-rw-r--r--tests/infrastructure/default.nix22
-rw-r--r--tests/infrastructure/driver.sh92
3 files changed, 112 insertions, 7 deletions
diff --git a/tests/README.md b/tests/README.md
index 698c5d83..f789c990 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -90,3 +90,8 @@ regex does not match.
 
 Set the golden file (the file used for the hash generation) to `ARGS`.
 `ARGS` must be a valid file path.
+
+#### `Exec`
+
+Executes the command (`ARGS`) and waits until it finishes.
+This will hang forever, when the command that does not exit.
diff --git a/tests/infrastructure/default.nix b/tests/infrastructure/default.nix
index 0e0408e7..e0ad6889 100644
--- a/tests/infrastructure/default.nix
+++ b/tests/infrastructure/default.nix
@@ -12,6 +12,8 @@
   description,
   hash,
   testData,
+  testShell ? pkgs.dash,
+  alternateScreen ? false,
 }:
 nixos-lib.runTest {
   hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
@@ -56,6 +58,7 @@ nixos-lib.runTest {
     testDir = "${nodes.machine.home-manager.users.soispha.home.homeDirectory}/test";
     goldenFile = "${testDir}/__test_golden";
     logFile = "${testDir}/__test_log";
+    testTmux = lib.getExe pkgs.tmux;
   in
     /*
     python
@@ -63,6 +66,8 @@ nixos-lib.runTest {
     ''
       start_all()
 
+      __TEST_TMUX="${testTmux}"
+
       machine.succeed("sudo -u soispha ${pkgs.writeShellScript "mkTestEnvironment" ''
         set -e
 
@@ -91,9 +96,15 @@ nixos-lib.runTest {
 
         cd "${testDir}"
 
-        __TEST_TMUX="${lib.getExe pkgs.tmux}"
+        __TEST_TMUX="${testTmux}"
+        __TEST_SHELL="${lib.getExe testShell}"
         __TEST_TMUX_PANE="__TEST_TMUX_PANE"
 
+        __TEST_EVAL_USE_ALTERNATE_SCREEN="${
+          if alternateScreen
+          then "true"
+          else "false"
+        }"
         __TEST_EVAL_AWK_CLEAN_FILE="${./clean.awk}"
         __TEST_EVAL_LOG_FILE="${logFile}"
         __TEST_EVAL_GOLDEN_FILE="$(mktemp)"
@@ -101,12 +112,17 @@ nixos-lib.runTest {
 
         . ${./driver.sh}
 
-        "$__TEST_TMUX" new-session -d -s "$__TEST_TMUX_PANE"
+        "$__TEST_TMUX" new-session -d -s "$__TEST_TMUX_PANE" "$__TEST_SHELL"
+
+        # Warm up the shell in the tmux sesssion
+        "$__TEST_TMUX" send-keys -t "$__TEST_TMUX_PANE" "echo hi" "Enter" "clear" "Enter"
+        sleep 2
+
         "$__TEST_TMUX" pipe-pane -t "$__TEST_TMUX_PANE" -o 'cat >>${goldenFile}'
 
         __test_eval "${description}"
 
-        # Clear the pipe again
+        # Remove the pipe
         "$__TEST_TMUX" pipe-pane -t "$__TEST_TMUX_PANE"
 
         # Check if the golden file was changed.
diff --git a/tests/infrastructure/driver.sh b/tests/infrastructure/driver.sh
index f8688161..4992b5bc 100644
--- a/tests/infrastructure/driver.sh
+++ b/tests/infrastructure/driver.sh
@@ -1,4 +1,4 @@
-#! /usr/bin/env sh
+#! /usr/bin/env bash
 set -e
 
 msg() {
@@ -9,6 +9,65 @@ msg() {
     fi
 }
 
+# Use bash built-ins to trim a string
+# source: https://stackoverflow.com/a/3352015
+trim() {
+    local var="$*"
+    # remove leading whitespace characters
+    var="${var#"${var%%[![:space:]]*}"}"
+    # remove trailing whitespace characters
+    var="${var%"${var##*[![:space:]]}"}"
+    printf '%s' "$var"
+}
+
+# contains(string, substring)
+#
+# Returns 0 if the specified string contains the specified substring,
+# otherwise returns 1.
+contains() {
+    string="$1"
+    substring="$2"
+    if [ "${string#*"$substring"}" != "$string" ]; then
+        return 0 # $substring is in $string
+    else
+        return 1 # $substring is not in $string
+    fi
+}
+
+__test_wait_for_pid() {
+    local pre_pids_file="$1"
+
+    exec_pids="$(mktemp)"
+    ps -eo tty,pid | awk "--assign=tmuxTty=$tmux_tty" '{if ($1 == tmuxTty) {print $2}}' >"$exec_pids"
+
+    while read -r pid; do
+        sed --in-place "s/$pid//" "$exec_pids"
+    done <"$pre_pids_file"
+
+    gawk --include inplace '{if (NF) {print $0}}' "$exec_pids"
+
+    if [ "$(wc -l <"$exec_pids")" -eq 0 ]; then
+        # No further spawned processes left
+        return 0
+    else
+        # Some other program is still running
+        pid="$(tail -n 1 "$exec_pids")"
+        rm "$exec_pids"
+        name="$(trim "$(tr '\0' ' ' <"/proc/$pid/cmdline")")"
+
+        msg "Waiting until command ('$name') finishes (has pid: $pid).."
+
+        # This allows waiting for non-children of the current shell
+        # source: https://stackoverflow.com/a/76046235
+        tail --pid "$pid" --follow /dev/null &
+        wait $!
+
+        # Give the `testShell` some time, to process the next command from a chained command.
+        sleep 0.2
+        __test_wait_for_pid "$pre_pids_file"
+    fi
+}
+
 __test_eval() {
     tmux="$__TEST_TMUX"
     tpane="$__TEST_TMUX_PANE"
@@ -24,11 +83,36 @@ __test_eval() {
             msg "Sleeping for '$args' seconds.."
             sleep "$args"
             ;;
+        "Exec")
+            local pre_exec_pids
+            local tmux_tty
+
+            msg "Executing command '$args'.."
+            tmux_tty="$("$tmux" list-panes -t "$tpane" -F "#{pane_tty}" | sed 's|/dev/||')"
+
+            pre_exec_pids="$(mktemp)"
+            ps -eo tty,pid | awk "--assign=tmuxTty=$tmux_tty" '{if ($1 == tmuxTty) {print $2}}' >"$pre_exec_pids"
+
+            "$tmux" send-keys -t "$tpane": "$args" "Enter"
+            sleep 1
+
+            __test_wait_for_pid "$pre_exec_pids"
+
+            rm "$pre_exec_pids"
+            msg "Finished command '$args'."
+            ;;
         "Expect" | "ExpectNot")
             msg "Trying to match regex ('$args') for currently visible content.."
 
+            get_plane_text() {
+                alternate=""
+                [ "$__TEST_EVAL_USE_ALTERNATE_SCREEN" = "true" ] && alternate="-a"
+
+                "$tmux" capture-pane -t "$tpane" -p $alternate -S 0 -E -
+            }
+
             matched=""
-            if "$tmux" capture-pane -t "$tpane" -p -S 0 -E - | grep "$args"; then
+            if get_plane_text | grep "$args"; then
                 matched=true
             else
                 matched=false
@@ -41,7 +125,7 @@ __test_eval() {
                 else
                     msg "Failed to find string, matched by regex '$args' on the screen"
                     msg current screen:
-                    "$tmux" capture-pane -t "$tpane" -p -S 0 -E - | msg
+                    get_plane_text | msg
 
                     exit 1
                 fi
@@ -52,7 +136,7 @@ __test_eval() {
                 else
                     msg "Found to find string, matched by regex '$args' on the screen. But expected none"
                     msg current screen:
-                    "$tmux" capture-pane -t "$tpane" -p -S 0 -E - | msg
+                    get_plane_text | msg
 
                     exit 1
                 fi