1#!/bin/bash
2
3WHITE="$(tput setaf 9 2>/dev/null || echo -n '')"
4YELLOW="$(tput setaf 3 2>/dev/null || echo -n '')"
5GREEN="$(tput setaf 2 2>/dev/null || echo -n '')"
6RED="$(tput setaf 1 2>/dev/null || echo -n '')"
7OFFSET=( )
8STEP="  "
9
10function with_program () {
11  local program="${1:?}"
12  hash "$program" &>/dev/null || {
13    function expect_run () {
14      echo 1>&2 "${WHITE} - skipped (missing program)"
15    }
16    function expect_run_sh () {
17      echo 1>&2 "${WHITE} - skipped (missing program)"
18    }
19  }
20}
21
22function title () {
23  echo "$WHITE-----------------------------------------------------"
24  echo "${GREEN}$*"
25  echo "$WHITE-----------------------------------------------------"
26}
27
28function _context () {
29  local name="${1:?}"
30  shift
31  echo 1>&2 "${YELLOW}${OFFSET[*]:-}[$name] $*"
32  OFFSET+=("$STEP")
33}
34
35function step () {
36  _note step "${WHITE}" "$*"
37}
38
39function stepn () {
40  step "$*" $'\n'
41}
42
43function with () {
44  _context with "$*"
45}
46
47function when () {
48  _context when "$*"
49}
50
51function _note () {
52  local name="${1:?}"
53  local color="${2:-}"
54  shift 2
55  echo 1>&2 -n "${OFFSET[*]:-}${color}[$name] ${*//  /}"
56}
57
58function it () {
59  _note it "${GREEN}" "$*"
60}
61
62function precondition () {
63  _note precondition "${WHITE}" "$*"
64}
65
66function shortcoming () {
67  _note shortcoming "${RED}" "$*"
68}
69
70function step () {
71  _note step "${WHITE}" "$*"
72}
73
74function stepn () {
75  step "$*" $'\n'
76}
77
78function fail () {
79  echo 1>&2 "${RED} $*"
80  exit 1
81}
82
83function sandbox () {
84  sandbox_tempdir="$(mktemp -t sandbox.XXXXXX -d)"
85  # shellcheck disable=2064
86  trap "popd >/dev/null" EXIT
87  pushd "$sandbox_tempdir" >/dev/null \
88   || fail "Could not change directory into temporary directory."
89
90  local custom_init="${1:-}"
91  if [ -n "$custom_init" ]; then
92    eval "$custom_init"
93  fi
94}
95
96function expect_equals () {
97  expect_run 0 test "${1:?}" = "${2:?}"
98}
99
100function expect_exists () {
101  expect_run 0 test -e "${1:?}"
102}
103
104function expect_run_sh () {
105  expect_run "${1:?}" bash -c -eu -o pipefail "${2:?}"
106}
107
108function expect_snapshot () {
109  local expected=${1:?}
110  local actual=${2:?}
111  if ! [ -e "$expected" ]; then
112    mkdir -p "${expected%/*}"
113    cp -R "$actual" "$expected"
114  fi
115  expect_run 0 diff -r -N "$expected" "$actual"
116}
117
118function expect_run () {
119  local expected_exit_code=$1
120  shift
121  local output=
122  set +e
123  if [[ -n "${SNAPSHOT_FILTER-}" ]]; then
124    output="$("$@" 2>&1 | "$SNAPSHOT_FILTER")"
125  else
126    output="$("$@" 2>&1)"
127  fi
128
129  local actual_exit_code=$?
130  if [[ "$actual_exit_code" == "$expected_exit_code" ]]; then
131    if [[ -n "${WITH_SNAPSHOT-}" ]]; then
132      local expected="$WITH_SNAPSHOT"
133      if ! [ -f "$expected" ]; then
134        mkdir -p "${expected%/*}"
135        echo -n "$output" > "$expected" || exit 1
136      fi
137      if ! diff "$expected" <(echo -n "$output"); then
138        echo 1>&2 "${RED} - FAIL"
139        echo 1>&2 "${WHITE}\$ $*"
140        echo 1>&2 "Output snapshot did not match snapshot at '$expected'"
141        echo 1>&2 "$output"
142        if [ -n "${ON_ERROR:-}" ]; then
143          eval "$ON_ERROR"
144        fi
145        exit 1
146      fi
147    fi
148    echo 1>&2
149  else
150    echo 1>&2 "${RED} - FAIL"
151    echo 1>&2 "${WHITE}\$ $*"
152    echo 1>&2 "${RED}Expected actual status $actual_exit_code to be $expected_exit_code"
153    echo 1>&2 "$output"
154    if [ -n "${ON_ERROR:-}" ]; then
155      eval "$ON_ERROR"
156    fi
157    exit 1
158  fi
159  set -e
160}
161