aboutsummaryrefslogtreecommitdiff
path: root/lib/test.sh
blob: 1da470b119150c7de67a81706a1f5bb399a87ee3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
. "${CLOYSTER_SCRIPT_ROOT}/lib/test/output.sh"

cloyster_test_varname() {
  if [ -n "${1}" ]; then echo "_cloyster_test_${1}"; else echo "_cloyster_test"; fi
}

cloyster_test_init() {
  export _cloyster_test_tests_counter_count=0
  export _cloyster_test_tests_counter_success=0
  export _cloyster_test_tests_counter_skipped=0
  export _cloyster_test_tests_counter_failed=0
  export _cloyster_test_tests_counter_ignored=0
  export _cloyster_test_tests_failed=
  export _cloyster_test_tests_skipped=
  export _cloyster_test_tests_success=
  export _cloyster_test_tests_ignored=
  export _cloyster_test_tests=
  export _cloyster_test_groups=
  export _cloyster_test_init=y
}

cloyster_test_add_shellcheck() {
  ignore=
  if ! command -v shellcheck >/dev/null; then ign=".ignore:SHELLCHECK_REQUIRED"; fi
  _cloyster__test__shellcheck() {
    shellcheck -e SC1008,SC2239 "${CLOYSTER_COMMAND_FILE}"
  }
  cloyster_test_add -a shellcheck shellcheck "_cloyster__test__shellcheck" "${ignore}"
}

cloyster_test_add() {
  append=
  while getopts :a _opt; do case "${_opt}" in
    a) append=y;;
  esac; done; shift $((OPTIND - 1))
  id=$1
  name=$2
  fun=$3
  args=$4
  if [ -z "${id}" ]; then return 1; fi
  if [ -z "${name}" ]; then return 1; fi
  list=$(cloyster_test_varname "tests")
  var=$(cloyster_test_varname "tests_${id}")
  if [ -n "$(readvar "$var")" ]; then
    echo "ERROR: A test '${id}' has already been defined"
    return 1
  fi
  if is_true "${append}"; then _cloyster_test_tests="${name} ${_cloyster_test_tests}"; else _cloyster_test_tests="${_cloyster_test_tests} ${name}"; fi
  setvar "${var}" "${var}"
  setvar "${var}__name" "${name}"
  setvar "${var}__fun" "${fun}"
  setvar "${var}__args" "${args}"
}

cloyster_test_run() {
  cloyster_test_log "${_green}${_bold}⚡⚡⚡ Running cloyster tests for ${_bold}${_yellow}${CLOYSTER_COMMAND}${_rst} ⚡⚡⚡"
  cloyster_test_log "${_darkgray} (did you knew that #!/bin/sh is twenty years older than emojis ?)${_rst}\n"
  cloyster_test_add_shellcheck #|| s=$?; _error "Failed to lol $s"; exit 1

  incr() {
    var="_cloyster_test_tests_counter_${1}"
    val=$(($(readvar "$var") + 1))
    setvar "$var" "$val"
  }

  _echo_block_init
  all_start_us=$(posix_time_microseconds)
  for id in ${_cloyster_test_tests}; do
    incr "count"
    name=$(readvar "_cloyster_test_tests_${id}__name")
    args=$(readvar "_cloyster_test_tests_${id}__args")
    line=$(readvar "_cloyster_test_tests_${id}__line")
    fun=$(readvar "_cloyster_test_tests_${id}__fun")
    fun="${fun:-"_test_${id}"}"
    testlog="${_tmp_dir}/test_${id}.log"
    _debug "Running test '${name}' line=${line} fun=${fun} args=${args}"
    refute=; skip=; tags=; status=; precond_status=; ignore=; fatal=;
    skip_reason=; ignore_reason= fatal_reason=
    for arg in $args; do
       tag=$(echo "$arg" | cut -d ":" -f 1)
       targ=$(echo "$arg" | sed "s/^${tag}://g")
       case $tag in
        .refute) refute=y;;
        .skip) skip=y; skip_reason=${targ:-.skip};;
        .ignore) ignore=y; ignore_reason=${targ:-.ignore};;
        .fatal) fatal=y; fatal_reason=${targ:-.fatal};;
        .tag) if [ -n "${targ}" ]; then tags="${tags} ${targ}"; fi;;
        *) ;;
      esac
    done

## -- Preconditions
if [ ! "$skip" = "y" ]; then
    (
      preconds=$(
        for arg in $args; do
          cond=$(echo "$arg" | cut -d ":" -f 2,100)
          case $arg in
            .if:*) if $($cond); then miss=y; break; fi;;
            .not:*) if ! $($cond); then miss=y; break; fi;;
            *) ;;
          esac
        done
        if [ "${miss}" = "y" ]; then
          echo "$(tput AF 105)Precondition failed: ${arg}"  >> "${testlog}"
          exit 2
        fi
      )
      precond_status=$?
      exit $precond_status
    )  2>> "${testlog}" >> "${testlog}"
    precond_status=$?
else
  echo "$(tput AF 105)${skip_reason}" >> "${testlog}"
fi

# -- TEST SUBSHELL
if [ "${precond_status}" = "0" ] && [ ! "${skip}" = "y" ]; then
(
. "${CLOYSTER_SCRIPT_ROOT}/lib/test/asserts.sh"
_assert_reset_count
start_us=$(posix_time_microseconds)
(
  "$fun"
  s=$?
  asserts=$(_assert_count)
  if [ ! "${asserts}" = "0" ]; then echo "☀️  Passed ${asserts} assertions."; fi
  exit $s
)
status=$?
end_us=$(posix_time_microseconds)
duration=$(bc -e "$end_us - $start_us")
if is_true "$refute" && [ "${status}" = "0" ]; then echo "⚠️  Exited with code ${_bold}${status}${_rst} (refuting, expected >0)"; fi
if is_true "$refute" && [ ! "${status}" = "0" ]; then echo "🌗 Exited with code ${status} (refuting)"; fi
if ! is_true "$refute" && [ ! "${status}" = "0" ]; then echo "⚠️  Exited with code ${_bold}${status}${_rst} (expected 0)"; fi
echo "$(tput AF 59)Ran in ${duration}ms${_rst}"
exit $status
) 2>> "${testlog}" >> "${testlog}"
status=$?
fi

## -- TEST FINISHED

    cmpstatus="$status"
    if is_true "$refute"; then
      if [ "$status" = "0" ]; then cmpstatus=1; else cmpstatus=0; fi
    fi
    if [ "$precond_status" = "0" ] && [ "$cmpstatus" = "0" ]; then
      _echo_block success
      _cloyster_test_tests_success="${_cloyster_test_tests_success}${id} "
      incr "success"
    elif [ "$precond_status" = "2" ] || [ "$skip" = "y" ]; then
      _echo_block skipped
      _cloyster_test_tests_skipped="${_cloyster_test_tests_skipped} ${id}"
      incr "skipped"
    else
      if [ ! "$precond_status" = "0" ]; then
        err="precondition_error:${precond_status}"
      else
        err="${status}"
      fi
      _action=failed
      if is_true "${ignore}"; then _action="ignored"; fi
      _echo_block "${_action}"
      setvar "_cloyster_test_tests_${id}__status" "$err"
      setvar "_cloyster_test_tests_${id}__ignore" "$ignore"
      setvar "_cloyster_test_tests_${_action}" "$(readvar "_cloyster_test_tests_${_action}") ${id}"
      incr "${_action}"
    fi

  done
  _echo_block_flush
  all_end_us=$(posix_time_microseconds)
  duration=$(bc -e "$all_end_us - $all_start_us")

  _test_output_results
  echo ""
  _test_output_overview
}