#! /usr/bin/env bash set -e msg() { if [ "$#" -ne 0 ]; then echo "$@" | tee --append "$__TEST_EVAL_LOG_FILE" >&2 else cat | tee --append "$__TEST_EVAL_LOG_FILE" >&2 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" file="$1" awk --file "$__TEST_EVAL_AWK_CLEAN_FILE" "$file" | while read -r cmd args; do case "$cmd" in "Type") msg "Sending keys to application '$args'.." "$tmux" send-keys -t "$tpane": "$args" ;; "Sleep") 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 get_plane_text | grep "$args"; then matched=true else matched=false fi case "$cmd" in "Expect") if [ "$matched" = true ]; then msg "Regex matched." else msg "Failed to find string, matched by regex '$args' on the screen" msg current screen: get_plane_text | msg exit 1 fi ;; "ExpectNot") if [ "$matched" = false ]; then msg "Regex successfully not matched." else msg "Found to find string, matched by regex '$args' on the screen. But expected none" msg current screen: get_plane_text | msg exit 1 fi ;; *) msg "Entered unrechable code. This is a bug." exit 1 ;; esac ;; "SetGolden") msg "Trying to set '$args' as golden file." [ -f "$args" ] || { msg "Argument is not a file!" exit 1 } printf "%s" "$args" >"$__TEST_EVAL_GOLDEN_FILE" msg "Set golden file to: '$args'" ;; *) msg "Unrecognized command: '$cmd'" exit 1 ;; esac done }