#!/usr/bin/env bash

test_count=0
pass_count=0
fail_count=0

function passed {
    let pass_count++
    printf '.'
}

function failed {
    let fail_count++
    echo
    echo " $fail_count) FAILURE in $test"
    stacktrace 1
}

function assert {
    if eval "$@"; then
        passed
    else
        failed
        echo " assert failed"
        echo " non-zero exit: $@"
    fi
}

function assert_equal {
   if [[ $1 == "$2" ]]; then
       passed
   else
       failed
       echo " assert_equal failed"
       echo " expected: $1"
       echo "   actual: $2"
   fi
}

function assert_match {
    if [[ $2 =~ $1 ]]; then
        passed
    else
        failed
        echo " assert_match failed"
        echo " expected =~ $1"
        echo "   actual    $2"
    fi
}

function stacktrace {
  let frame++

  while caller=$(caller $frame); do
    local line=$(expr "$caller" : '\([0-9]*\)')
    local func=$(expr "$caller" : '[0-9]* \([^ ]*\)')
    local file=$(expr "$caller" : '[0-9]* [^ ]* \([^ ]*\)')
    echo "     $file:$line in $func"
    let frame++
  done
}

function test_results {
    echo
    echo "Ran $test_count tests containing $((pass_count + fail_count)) assertions."
    echo "$fail_count failures."

    if [[ $fail_count -ne 0 ]]; then
        echo "Tests failed."
    fi
}

function init_fixtures {
    eval "function setup    { :; }"
    eval "function teardown { :; }"
}

function run_all_tests {
    cd $(dirname $0)

    local files=()
    while read -r -d '' file; do
        [[ $file =~ \# ]] && continue
        [[ -z $1 || $file == ./$1.sh ]] || continue
        files+=("$file")
    done < <(find . \( -name helpers -prune -or -name \*.sh \) -type f -print0)

    for file in "${files[@]}"; do
        local tests=()
        while read -r line; do
            if fn=$(expr "$line" : 'function \(test_[^ ]*\) {'); then
                [[ -z $2 || $fn == $2 ]] || continue
                tests+=("$fn")
            fi
        done < "$file"

        suite=$(basename $file .sh)

        echo
        printf "%s %s\n" "$0" "$suite"
        init_fixtures
        source ./helpers/init.sh
        source "$file"

        for test in "${tests[@]}"; do
            ((test_count++))
            setup
            [[ -z $VERBOSE ]] || printf "\n%s %s %s\n" "$0" "$suite" "$test"
            $test
            teardown
        done
    done

    cd - > /dev/null
}

export PATH=../bin:$PATH
run_all_tests "$@"
test_results
