#!/usr/bin/env bash # Automatic tests for rdlink # Two kinds of tests are supported # * `tocompete` : competition between readlink and rdlink # * `toassert` : assert expected path and input path # test_rdlink.sh [OPTIONS] [PATH TO COMPETE] # [OPTIONS] # -a, --run-all : Run all tests (failed and successful) # -x, --extended-output : Show more information for each test # -xd : set -x and also show rdlink debug run for failed tests # -oa, --only-assert : Run only assert tests # -oc, --only-compete : Run only competion tests # -e, --error : Print only failed tests # # Internal tests will be executed if no arguments are found readonly test_dir="$(cd "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" readonly tool_rdlink="${test_dir}/../rdlink.sh" readonly tool_readlink="$(command -v readlink) -f --" readonly tool_rdlink_version="$(git describe --long --tags --always --dirty)" readonly config_path_width=45 color_red= ; [ -t 1 ] && color_red='\033[1;31m' color_yellow= ; [ -t 1 ] && color_yellow='\033[1;33m' color_green= ; [ -t 1 ] && color_green='\033[1;32m' color_blue= ; [ -t 1 ] && color_blue='\033[1;34m' color_less= ; [ -t 1 ] && color_less='\033[0m' flag_title=0 flag_runall=0 flag_extendedOutput=0 flag_verbose=0 flag_onlyassert=0 flag_onlycompete=0 flag_printerror=0 toassert=() tocompete=() . "${test_dir}/totest.sh" rl::test() { local i=0 local path= local arraywalker= local firstelement= local testend=0 local arg= local excludemsg= local tests_success=0 local tests_failed=0 local tests_skipped=0 for arg in "$@"; do case "$1" in --) ## End of options shift && break ;; -a|--run-all) ## Run all even if tests fail flag_runall=1 shift ;; -x|--extended-output) flag_extendedOutput=1 shift ;; -xd|--verbose) flag_extendedOutput=1 flag_verbose=1 shift ;; -oa|--only-assert) flag_onlyassert=1 shift ;; -oc|--only-compete) flag_onlycompete=1 shift ;; -e|--error) flag_printerror=1 shift ;; -*|--*) printf "Invalid argument\n" exit 1 ;; esac done # Check if assertion tests need to be performed (( ! flag_onlycompete )) && [[ ! "$@" ]] && toassert_init # Compare against expected result if (( ${#toassert[@]} )) ; then rl::printTitle printf "\n## Assertion tests (\"Expected path\" == rdlink)\n" fi for testarray in "${toassert[@]}"; do i=0 arraywalker="$testarray"[@] for path in "${!arraywalker}"; do if (( ! i )); then # Print title in array element 0 printf "### %b\n" "${color_yellow}${path}${color_less}" (( ! flag_extendedOutput )) && printf "%${config_path_width}s %s %s\n" "" "" "โœ— [ACTUAL]" elif [ -z "$firstelement" ]; then # Save first element for string compare firstelement="${path}" elif excludemsg="$(toexclude "${firstelement}")"; then # Current path is excluded (( tests_skipped++ )) rl::printPath "$(( i/2 ))" "${firstelement}" && printf " %b skip (%s)\n" "๐Ÿ›‡" "${excludemsg}" else # Execute test case if ! rl::testcmp "$(( i/2 ))" "${firstelement}" \ "${path}" "$($tool_rdlink -- "${firstelement}")"; then (( tests_failed++ )) # Run all tests if option -a is pressend (( ! $flag_runall )) && testend=1 && break else (( tests_success++ )) fi firstelement= fi ((i++)) done (( testend )) && break done (( testend )) && rl::printTestSummary $tests_success $tests_failed $tests_skipped && return 1 # Initialize competition tests (( ! flag_onlyassert )) && tocompete_init # Only run `compete_args` if arguments are available if [[ "$@" ]]; then compete_args=( "Tests from command line" "$@" ) tocompete=(compete_args) fi # Compare output of rdlink and readlink -f if (( ${#tocompete[@]} )) ; then rl::printTitle printf "\n## Competition tests (readlink -f == rdlink)\n" fi for testarray in "${tocompete[@]}"; do i=0 arraywalker="$testarray"[@] for path in "${!arraywalker}"; do if (( ! i )); then # Print title in array element 0 printf "### %b\n" "${color_yellow}${path}${color_less}" (( ! flag_extendedOutput )) && printf "%${config_path_width}s %s %s\n" "" "" "โœ— [ACTUAL]" else if excludemsg="$(toexclude "${path}")" ; then # Current path is excluded (( tests_skipped++ )) rl::printPath "${i}" "${path}" && printf " %b skip (%s)\n" "๐Ÿ›‡" "${excludemsg}" elif ! rl::testcmp "${i}" "${path}" \ "$(${tool_readlink} "$path")" "$(${tool_rdlink} -- "$path")"; then # Test case failed (( tests_failed++ )) # Run all tests if option -a is pressend (( ! $flag_runall )) && testend=1 && break else (( tests_success++ )) fi fi ((i++)) done (( testend )) && break done rl::printTestSummary $tests_success $tests_failed $tests_skipped return ${tests_failed} } rl::printTitle() { (( flag_title )) && return 0 flag_title=1 # See if some tests will be performed printf "# Testing rdlink version %s\n\n" "${tool_rdlink_version}" printf " Testdate: %s\n" "$(date -u +"%Y-%m-%d %H:%M:%S UTC")" } rl::printPath() { local testnum="${1:-0}" local input="${2:-"-"}" local truncate_graphic= local inputwidth=${config_path_width} if (( flag_extendedOutput )); then printf -- "%-5d Inp: %-${config_path_width}s ---" ${testnum} "${input}" else # Truncate input string on the left if longer than $config_path_width if (( ${#input} > ${config_path_width} )) ; then # +1 : prepending truncate_graphic input="${input:$(( ${#input} - ${config_path_width} + 1 ))}" # -1 : prepending truncate_graphic inputwidth=$((config_path_width - 1)) truncate_graphic="โœ€" fi # Print input and expected printf "%b%${inputwidth}s" "${truncate_graphic}" "${input}" fi } rl::printTestSummary() { local success=${1:-0} local failed=${2:-0} local skipped=${3:-0} local total=$(( success + failed + skipped )) readonly columnwidth=7 readonly tableformat="%${columnwidth}s | %${columnwidth}s | %${columnwidth}s | %${columnwidth}s\n" printf "\n# Result : ${tableformat}" "success" "failed" "skipped" "total" printf " ${tableformat}" "${success}" "${failed}" "${skipped}" "${total}" } # rl::testcmp # Compare results and print summary rl::testcmp() { local testnum="${1:-0}" local input="${2:-"-"}" local expect="${3:-"-"}" local actual="${4:-"-"}" local testresult=0 #failed local testresult_graphic="${color_red}โœ—${color_less}" # alt. symbol โ‰  local link="$(readlink -- "${input}")" if [[ "${expect}" == "${actual}" ]] ; then # Don't print success for this flag (( flag_printerror )) && return 0 testresult=1 testresult_graphic="${color_green}โœ”${color_less}" # alt. symbol โœ“ fi # Show result as table (without debug) if (( flag_extendedOutput )); then rl::printPath ${testnum} "${input}" printf " %b\n" "${testresult_graphic}" [[ "$link" ]] && printf "%10s %b %s%b\n" "" "${color_blue}โคท" "${link}" "${color_less}" if (( ! testresult )); then # Test failed printf " Result: %s\n Exp: %s\n" "${actual}" "${expect}" if (( flag_verbose )); then [ -e "${input}" ] && printf "\n Subject:\n" && ls -al "${input}" # Print debug output of tool_a printf "\n Debug:\n" ( ${tool_rdlink} -d -- "${input}" ) fi return 1 fi return 0 fi # Change result graphic if test failed (( ! testresult )) && testresult_graphic="โ†’" # "${color_green}โ†’${color_less}" rl::printPath "" "${input}" printf " %b %s\n" "${testresult_graphic}" "${expect}" [[ "$link" ]] && printf "%b" "${color_blue}" && rl::printPath "" "${link}โคถ" && printf " %b\n" "${color_less}" #โคท # Print actual result if test failed if (( ! testresult )); then printf "%$((config_path_width))s %b %s\n" " " "${color_red}โœ—${color_less}" "${actual}" return 1 fi return 0 } #time rl::test "$@" rl::test "$@"