MIAOU-BASH is a collection of scripts and configurations designed to enhance your terminal experience. As the name suggests, it is built on Bash and is designed to work on most modern GNU/Linux systems.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
7.9 KiB

#!/usr/bin/env bash
# CONSTANTS
TMP_DIRECTORY="/tmp/$(basename "$0")-$UID"
TMP_OUT="$TMP_DIRECTORY/_stdout"
TMP_ERR="$TMP_DIRECTORY/_stderr"
TMP_MIAOU="$TMP_DIRECTORY/_miaou"
readonly TMP_DIRECTORY TMP_OUT TMP_ERR TMP_MIAOU
source "$MIAOU_BASH_DIR"/lib/ansi.bash # ANSI associative array
# functions
function ansi_code {
local code
for code in "$@"; do
if [[ -v ANSI[$code] ]]; then
echo -ne "\e[${ANSI[$code]}m"
else
>&2 echo "unknown ansi key: <$code>"
break
fi
done
}
function print_passed {
ansi_code FG_GREEN
echo -en '.'
ansi_code RESET
}
function print_bypassed {
ansi_code FG_YELLOW
echo -en 'b'
ansi_code RESET
}
function print_failed {
ansi_code FG_RED
echo -en 'F'
ansi_code RESET
}
function assert_tty {
((test_count++))
if [[ -w /dev/tty ]]; then
print_passed
return
fi
print_failed
failures+=("test -w /dev/tty")
return 1
}
function assert {
((test_count++))
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
if $cmd >/dev/null 2>/dev/null 3>"$TMP_MIAOU"; then
if [[ -s "$TMP_MIAOU" ]]; then
print_passed
return
fi
fi
print_failed
failures+=("$cmd")
return 1
}
# assert performing '$1:script' successfully + output must contain '$2:content'
# in case of extra args (4th position or more) must be provided after -- (3rd position)
# 1: script
# 2: content
# 3: --
# 4..: extra args
function assert_grep {
((test_count++))
local script="$1" content="$2" extra_args=()
[[ $# -gt 3 ]] && shift 3 && extra_args=("$@")
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $script"
if $cmd "${extra_args[@]}" >"$TMP_OUT" 2>/dev/null 3>/dev/null; then
if grep -q "$content" "$TMP_OUT"; then
print_passed
return
fi
fi
print_failed
#TODO: quote extra_args
for i in "${extra_args[@]}"; do safe_args+=$(printf '%q ' "$i"); done
failures+=("$cmd $safe_args")
return 1
}
function fail_type {
((test_count++))
local success regex hint_message cmd lang_options
success=false
hint_message="${3:-}"
lang_options=()
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
if [[ -v TEST_LANG ]]; then
local locale_found=false
for gen_locale in "${GEN_LOCALES[@]}"; do
if [[ "$gen_locale" == "$TEST_LANG" ]]; then
locale_found=true
break
fi
done
if ! $locale_found; then
((test_bypassed++))
BYPASSED_LOCALES["$TEST_LANG"]=true
print_bypassed
return
fi
fi
eval "${lang_options[*]} $cmd" >/dev/null 2>"$TMP_ERR" 3>"$TMP_MIAOU"
mapfile -t stderr <"$TMP_ERR"
last_error="${stderr[-1]}"
regex="^(.*): line ([0-9]+): miaou_error: $2 ([0-9]+)"
[[ "${last_error}" =~ $regex ]] && success=true
if [[ $success == true && -n "${hint_message}" ]]; then
grep -q "${hint_message}" "$TMP_MIAOU" || success=false
fi
if [[ $success == true ]]; then
print_passed
return
fi
print_failed
failures+=("${lang_options[*]} $cmd")
return 1
}
function fail_stderr_grep {
((test_count++))
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
stderr_line="$2"
if ! $cmd 2>"$TMP_ERR" && grep -q "$stderr_line" "$TMP_ERR"; then
print_passed
return
fi
print_failed
failures+=("$cmd 2>&1 | grep -q \"$stderr_line\"")
return 1
}
function fail_stderr_count {
((test_count++))
local regex count="$2"
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
if ! $cmd >/dev/null 2>"$TMP_ERR" 3>/dev/null; then
mapfile -t stderr <"$TMP_ERR"
if [[ "${#stderr[@]}" == "$count" ]]; then
print_passed
return
fi
fi
print_failed
failures+=("$cmd 2>$TMP_ERR")
return 1
}
function fail_miaou_count {
((test_count++))
local regex count="$2"
cmd="$MIAOU_BASH_DIR/test/stub/scenario.bash $1"
if ! $cmd >/dev/null 2>/dev/null 3>"$TMP_MIAOU"; then
mapfile -t miaou <"$TMP_MIAOU"
if [[ "${#miaou[@]}" == "$count" ]]; then
print_passed
return
fi
fi
print_failed
failures+=("$cmd 3>$TMP_MIAOU")
return 1
}
function assert_locales_equally_populated {
((test_count++))
local locales_dir="$MIAOU_BASH_DIR"/locales
local main_locale_file="$locales_dir"/"$1".sh
if [[ ! -f $main_locale_file ]]; then
print_failed
failures+=("missing locale file: $main_locale_file for locale: $1")
return 1
fi
shift
if [[ $# -gt 0 ]]; then
local extra_locale_file difference variable_name
for extra in "$@"; do
extra_locale_file="$locales_dir"/"$extra".sh
if [[ ! -f $extra_locale_file ]]; then
print_failed
failures+=("missing locale file: $extra_locale_file for locale: $extra")
return 1
fi
mapfile -t difference < <(
diff <(grep -oP '^\s*[A-Za-z_][A-Za-z0-9_]*' "$main_locale_file" | sort) \
<(grep -oP '^\s*[A-Za-z_][A-Za-z0-9_]*' "$extra_locale_file" | sort) \
2>&1 | head -n2
)
if [[ ${#difference[@]} -gt 0 ]]; then
variable_name=$(echo "${difference[1]}" | cut -d' ' -f2)
change_type="${difference[0]}"
print_failed
if [[ "$change_type" =~ d ]]; then
failures+=("locale=$extra (in $extra_locale_file) misses variable (to add): ${variable_name}")
else
failures+=("locale=$extra (in $extra_locale_file) useless variable (to remove): ${variable_name}")
fi
return 1
fi
done
fi
print_passed
}
# main
set -uo pipefail
[[ ! -v MIAOU_BASH_DIR ]] && >&2 echo MIAOU_BASH_DIR missing && builtin exit 1
test_count=0
test_bypassed=0
failures=()
declare -A BYPASSED_LOCALES=()
mapfile -t GEN_LOCALES < <(locale -a)
mkdir -p "$TMP_DIRECTORY"
TIMEFORMAT=$'\nelapsed: %Rs'
time {
assert_tty
assert success
assert exit_0
assert return_conditional
assert_grep success_with_args 'count args: 2' -- 'A B' C
assert heredoc
assert pipe1
assert pipe2
TEST_LANG=en_US.utf8 fail_type unbound_variable UNBOUND_VARIABLE
TEST_LANG=es_ES.utf8 fail_type unbound_variable UNBOUND_VARIABLE
TEST_LANG=fr_FR.utf8 fail_type unbound_variable UNBOUND_VARIABLE
TEST_LANG=en_US.utf8 fail_type unbound_associative UNBOUND_VARIABLE
TEST_LANG=es_ES.utf8 fail_type unbound_associative UNBOUND_VARIABLE
TEST_LANG=fr_FR.utf8 fail_type unbound_associative UNBOUND_VARIABLE
TEST_LANG=en_US.utf8 fail_type command_not_found COMMAND_NOT_FOUND
TEST_LANG=es_ES.utf8 fail_type command_not_found COMMAND_NOT_FOUND
TEST_LANG=fr_FR.utf8 fail_type command_not_found COMMAND_NOT_FOUND
TEST_LANG=en_US.utf8 fail_type source_not_found SOURCE_NOT_FOUND
TEST_LANG=es_ES.utf8 fail_type source_not_found SOURCE_NOT_FOUND
TEST_LANG=fr_FR.utf8 fail_type source_not_found SOURCE_NOT_FOUND
TEST_LANG=en_US.utf8 fail_type file_not_found FILE_NOT_FOUND
TEST_LANG=es_ES.utf8 fail_type file_not_found FILE_NOT_FOUND
TEST_LANG=fr_FR.utf8 fail_type file_not_found FILE_NOT_FOUND
fail_type command_error COMMAND_ERROR
fail_type subshell_term SUBSHELL_ERROR
fail_type false FALSE
fail_type last_false COMMAND_ERROR
fail_type subshell_term SUBSHELL_ERROR
fail_type exit EXIT
fail_type child_error CHILD_ERROR 'prefer source than child process'
fail_stderr_count unbound_variable 1
fail_stderr_count child_error 4
fail_miaou_count command_error_multiline 7
fail_miaou_count unknown 1
fail_stderr_grep two_options 'ERROR: too many args 2, either single option or SCRIPT + args accepted!'
fail_stderr_grep script_not_found "ERROR: script 'doesnt_exist' not found!"
fail_stderr_grep no_script 'ERROR: script expected!'
assert_locales_equally_populated en es fr
}
failed=${#failures[@]}
success=$((test_count - failed))
optional_bypassed=()
if [[ ${#BYPASSED_LOCALES[@]} -gt 0 ]]; then
optional_bypassed=(", $test_bypassed bypassed (missing locales: ${!BYPASSED_LOCALES[*]})")
fi
if [[ $failed -gt 0 ]]; then
ratio=$((100 * success / test_count))
echo "${ratio}% passed, ${test_count} tests, ${failed} failed!${optional_bypassed[*]}"
echo failures:
for failure in "${failures[@]}"; do
echo -e "\t- $failure"
done
exit 1
else
echo "100% passed, ${test_count} tests${optional_bypassed[*]}"
fi