#! /bin/bash
#

################################################################################
#                                    CLI                                       #
################################################################################

usage()
{
    printf "usage: %q FILE

Positional argument:
   FILE   path to the shell script that defines the stack_trap() function\n" \
      "${BASH_SOURCE[0]##*/}"
}

[ $# -eq 1 ] || { usage; exit 64; }
source "$1" || exit $?

################################################################################
#                               Test framework                                 #
################################################################################

assert_equal()
{
    [ "$1" == "$2" ] && return 0

    printf "'%s' != '%s'\n" "$1" "$2" >&2
    exit 1
}

################################################################################
#                                    Tests                                     #
################################################################################

# stack_trap behaves like trap when used once
test_single_trap()
{
    local result="$(stack_trap "echo 0" EXIT
                    trap -p EXIT
                    trap -- "" EXIT)"

    local expectation="$(trap "echo 0" EXIT
                         trap -p EXIT
                         trap -- "" EXIT)"

    assert_equal "$result" "$expectation"
}

# stack_trap... stacks traps
test_multiple_traps()
{
    local result="$(stack_trap "echo 1" EXIT
                    stack_trap "echo 0" EXIT
                    trap -p EXIT
                    trap -- "" EXIT)"

    local expectation="$(trap "echo 0; echo 1" EXIT
                         trap -p EXIT
                         trap -- "" EXIT)"

    assert_equal "$result" "$expectation"
}

# stack_trap handles quoting as expected
test_quoting()
{
    # Single quotes in double quotes
    assert_equal "$(stack_trap "printf '0 1'" EXIT)" "0 1"

    # Double quotes in single quotes
    assert_equal "$(stack_trap 'printf "0 1"' EXIT)" "0 1"

    # Double quotes in double quotes 
    assert_equal "$(stack_trap "printf \"0 1\"" EXIT)" "0 1"

    # Single quotes in single quotes
    assert_equal "$(stack_trap 'printf '\''0 1'\' EXIT)" "0 1"
}

# stack_trap handles quoting through multiple calls
test_multiple_traps_and_quoting()
{
    # Single quotes in double quotes
    local result="$(stack_trap "printf '2 3'" EXIT
                    stack_trap "printf '0 1'" EXIT)"

    assert_equal "$result" "0 12 3"

    # Double quotes in single quotes
    local result="$(stack_trap 'printf "2 3"' EXIT
                    stack_trap 'printf "0 1"' EXIT)"

    assert_equal "$result" "0 12 3"

    # Double quotes in double quotes
    local result="$(stack_trap "printf \"2 3\"" EXIT
                    stack_trap "printf \"0 1\"" EXIT)"

    assert_equal "$result" "0 12 3"

    # Single quotes in single quotes
    local result="$(stack_trap 'printf '\''2 3'\' EXIT
                    stack_trap 'printf '\''0 1'\' EXIT)"

    assert_equal "$result" "0 12 3"
}

################################################################################
#                                    Main                                      #
################################################################################

logfile="${BASH_SOURCE[0]##*/}"
logfile="/dev/shm/${logfile%.*}.log"
rm -f "$logfile"

for test__ in $(declare -F | awk '{ print $NF }' | grep "^test_"); do
    printf "### $test__: "
    ( printf "\n### $test__\n"; $test__ ) &>> "$logfile" &&
        printf "\e[32m[PASSED]\e[39m\n" ||
        printf "\e[31m[FAILED]\e[39m\n"
done
