github.com/weaveworks/common@v0.0.0-20230728070032-dd9e68f319d5/tools/integration/assert.sh (about)

     1  #!/bin/bash
     2  # assert.sh 1.1 - bash unit testing framework
     3  # Copyright (C) 2009-2015 Robert Lehmann
     4  #
     5  # http://github.com/lehmannro/assert.sh
     6  #
     7  # This program is free software: you can redistribute it and/or modify
     8  # it under the terms of the GNU Lesser General Public License as published
     9  # by the Free Software Foundation, either version 3 of the License, or
    10  # (at your option) any later version.
    11  #
    12  # This program is distributed in the hope that it will be useful,
    13  # but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  # GNU Lesser General Public License for more details.
    16  #
    17  # You should have received a copy of the GNU Lesser General Public License
    18  # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    19  
    20  export DISCOVERONLY=${DISCOVERONLY:-}
    21  export DEBUG=${DEBUG:-}
    22  export STOP=${STOP:-}
    23  export INVARIANT=${INVARIANT:-}
    24  export CONTINUE=${CONTINUE:-}
    25  
    26  args="$(getopt -n "$0" -l \
    27      verbose,help,stop,discover,invariant,continue vhxdic "$@")" \
    28      || exit -1
    29  for arg in $args; do
    30      case "$arg" in
    31          -h)
    32              echo "$0 [-vxidc]" \
    33                  "[--verbose] [--stop] [--invariant] [--discover] [--continue]"
    34              echo "$(sed 's/./ /g' <<<"$0") [-h] [--help]"
    35              exit 0
    36              ;;
    37          --help)
    38              cat <<EOF
    39  Usage: $0 [options]
    40  Language-agnostic unit tests for subprocesses.
    41  
    42  Options:
    43    -v, --verbose    generate output for every individual test case
    44    -x, --stop       stop running tests after the first failure
    45    -i, --invariant  do not measure timings to remain invariant between runs
    46    -d, --discover   collect test suites only, do not run any tests
    47    -c, --continue   do not modify exit code to test suite status
    48    -h               show brief usage information and exit
    49    --help           show this help message and exit
    50  EOF
    51              exit 0
    52              ;;
    53          -v | --verbose)
    54              DEBUG=1
    55              ;;
    56          -x | --stop)
    57              STOP=1
    58              ;;
    59          -i | --invariant)
    60              INVARIANT=1
    61              ;;
    62          -d | --discover)
    63              DISCOVERONLY=1
    64              ;;
    65          -c | --continue)
    66              CONTINUE=1
    67              ;;
    68      esac
    69  done
    70  
    71  _indent=$'\n\t' # local format helper
    72  
    73  _assert_reset() {
    74      tests_ran=0
    75      tests_failed=0
    76      tests_errors=()
    77      tests_starttime="$(date +%s%N)" # nanoseconds_since_epoch
    78  }
    79  
    80  assert_end() {
    81      # assert_end [suite ..]
    82      tests_endtime="$(date +%s%N)"
    83      # required visible decimal place for seconds (leading zeros if needed)
    84      tests_time="$(
    85          printf "%010d" "$((${tests_endtime/%N/000000000} - ${tests_starttime/%N/000000000}))"
    86      )" # in ns
    87      tests="$tests_ran ${*:+$* }tests"
    88      [[ -n "$DISCOVERONLY" ]] && echo "collected $tests." && _assert_reset && return
    89      [[ -n "$DEBUG" ]] && echo
    90      # to get report_time split tests_time on 2 substrings:
    91      #   ${tests_time:0:${#tests_time}-9} - seconds
    92      #   ${tests_time:${#tests_time}-9:3} - milliseconds
    93      if [[ -z "$INVARIANT" ]]; then
    94          idx=$((${#tests_time} - 9))
    95          report_time=" in ${tests_time:0:${idx}}.${tests_time:${idx}:3}s"
    96      else
    97          report_time=
    98      fi
    99      if [[ "$tests_failed" -eq 0 ]]; then
   100          echo "all $tests passed$report_time."
   101      else
   102          for error in "${tests_errors[@]}"; do echo "$error"; done
   103          echo "$tests_failed of $tests failed$report_time."
   104      fi
   105      [[ $tests_failed -gt 0 ]] && tests_suite_status=1
   106      _assert_reset
   107  }
   108  
   109  assert() {
   110      # assert <command> <expected stdout> [stdin]
   111      ((tests_ran++)) || :
   112      [[ -z "$DISCOVERONLY" ]] || return
   113      expected=$(echo -ne "${2:-}")
   114      result="$(eval "$1" 2>/dev/null <<<"${3:-}")" || true
   115      if [[ "$result" == "$expected" ]]; then
   116          [[ -z "$DEBUG" ]] || echo -n .
   117          return
   118      fi
   119      result="$(sed -e :a -e '$!N;s/\n/\\n/;ta' <<<"$result")"
   120      [[ -z "$result" ]] && result="nothing" || result="\"$result\""
   121      [[ -z "$2" ]] && expected="nothing" || expected="\"$2\""
   122      _assert_fail "expected $expected${_indent}got $result" "$1" "$3"
   123  }
   124  
   125  assert_raises() {
   126      # assert_raises <command> <expected code> [stdin]
   127      ((tests_ran++)) || :
   128      [[ -z "$DISCOVERONLY" ]] || return
   129      status=0
   130      (eval "$1" <<<"${3:-}") >/dev/null 2>&1 || status=$?
   131      expected=${2:-0}
   132      if [[ "$status" -eq "$expected" ]]; then
   133          [[ -z "$DEBUG" ]] || echo -n .
   134          return
   135      fi
   136      _assert_fail "program terminated with code $status instead of $expected" "$1" "$3"
   137  }
   138  
   139  _assert_fail() {
   140      # _assert_fail <failure> <command> <stdin>
   141      [[ -n "$DEBUG" ]] && echo -n X
   142      report="test #$tests_ran \"$2${3:+ <<< $3}\" failed:${_indent}$1"
   143      if [[ -n "$STOP" ]]; then
   144          [[ -n "$DEBUG" ]] && echo
   145          echo "$report"
   146          exit 1
   147      fi
   148      tests_errors[$tests_failed]="$report"
   149      ((tests_failed++)) || :
   150  }
   151  
   152  skip_if() {
   153      # skip_if <command ..>
   154      (eval "$@") >/dev/null 2>&1 && status=0 || status=$?
   155      [[ "$status" -eq 0 ]] || return
   156      skip
   157  }
   158  
   159  skip() {
   160      # skip  (no arguments)
   161      shopt -q extdebug && tests_extdebug=0 || tests_extdebug=1
   162      shopt -q -o errexit && tests_errexit=0 || tests_errexit=1
   163      # enable extdebug so returning 1 in a DEBUG trap handler skips next command
   164      shopt -s extdebug
   165      # disable errexit (set -e) so we can safely return 1 without causing exit
   166      set +o errexit
   167      tests_trapped=0
   168      trap _skip DEBUG
   169  }
   170  _skip() {
   171      if [[ $tests_trapped -eq 0 ]]; then
   172          # DEBUG trap for command we want to skip.  Do not remove the handler
   173          # yet because *after* the command we need to reset extdebug/errexit (in
   174          # another DEBUG trap.)
   175          tests_trapped=1
   176          [[ -z "$DEBUG" ]] || echo -n s
   177          return 1
   178      else
   179          trap - DEBUG
   180          [[ $tests_extdebug -eq 0 ]] || shopt -u extdebug
   181          [[ $tests_errexit -eq 1 ]] || set -o errexit
   182          return 0
   183      fi
   184  }
   185  
   186  _assert_reset
   187  : ${tests_suite_status:=0} # remember if any of the tests failed so far
   188  _assert_cleanup() {
   189      local status=$?
   190      # modify exit code if it's not already non-zero
   191      [[ $status -eq 0 && -z $CONTINUE ]] && exit $tests_suite_status
   192  }
   193  trap _assert_cleanup EXIT