mirror of https://github.com/sobolevn/git-secret
Adds fedora testing
parent
9efae709db
commit
28dbdf3fde
@ -0,0 +1,19 @@
|
|||||||
|
FROM fedora:34
|
||||||
|
|
||||||
|
LABEL maintainer="mail@sobolevn.me"
|
||||||
|
LABEL vendor="git-secret team"
|
||||||
|
|
||||||
|
RUN dnf -y update \
|
||||||
|
&& dnf install -y \
|
||||||
|
# Direct dependencies:
|
||||||
|
bash \
|
||||||
|
gawk \
|
||||||
|
git \
|
||||||
|
gnupg \
|
||||||
|
# Assumed to be present:
|
||||||
|
diffutils \
|
||||||
|
findutils \
|
||||||
|
procps \
|
||||||
|
make \
|
||||||
|
&& dnf clean all \
|
||||||
|
&& rm -rf /var/cache/yum
|
@ -0,0 +1 @@
|
|||||||
|
vendor/ linguist-vendored
|
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BRANCH_NAME=$(git branch | grep '\*' | sed 's/* //')
|
|
||||||
|
|
||||||
if [[ "$BRANCH_NAME" == 'master' ]]; then
|
|
||||||
# Compare script version and the latest tag:
|
|
||||||
NEWEST_TAG=$(git describe --abbrev=0 --tags)
|
|
||||||
SCRIPT_VERSION=$(bash "${PWD}/git-secret" --version)
|
|
||||||
|
|
||||||
if [[ "$NEWEST_TAG" != "v${SCRIPT_VERSION}" ]]; then
|
|
||||||
# Create new release:
|
|
||||||
git tag -a "v${SCRIPT_VERSION}" -m "version $SCRIPT_VERSION"
|
|
||||||
echo "Created new tag 'v${SCRIPT_VERSION}'"
|
|
||||||
fi
|
|
||||||
fi
|
|
@ -1,29 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
BRANCH_NAME=$(git branch | grep '\*' | sed 's/* //')
|
|
||||||
|
|
||||||
if [[ "$BRANCH_NAME" != '(no branch)' ]]; then
|
|
||||||
unset GIT_WORK_TREE
|
|
||||||
|
|
||||||
# Set marker, that we running tests from `git commit`,
|
|
||||||
# so some tests will be skipped. It is done, because `git rev-parse`
|
|
||||||
# is not working when running from pre-commit hook. See #334
|
|
||||||
export BATS_RUNNING_FROM_GIT=1
|
|
||||||
if [[ "$(uname -s)" == MINGW* ]]; then
|
|
||||||
export GITSECRET_DIST="windows"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run tests:
|
|
||||||
make test
|
|
||||||
|
|
||||||
if [[ "$BRANCH_NAME" == "master" ]]; then
|
|
||||||
# Build new manuals:
|
|
||||||
make build-man
|
|
||||||
|
|
||||||
# Add new files:
|
|
||||||
git add man/man1/*
|
|
||||||
git add man/man7/*
|
|
||||||
fi
|
|
||||||
fi
|
|
@ -1,9 +1,15 @@
|
|||||||
README for git-secret/vendor directory
|
README for git-secret/vendor directory
|
||||||
|
|
||||||
We import bats-core v1.1.0 here for
|
We import bats-core v1.3.0 here for
|
||||||
https://github.com/sobolevn/git-secret/issues/377,
|
https://github.com/sobolevn/git-secret/issues/377,
|
||||||
"Don't depend on network during builds (re: bats-core)"
|
"Don't depend on network during builds (re: bats-core)"
|
||||||
|
|
||||||
If you want upgrade bats-core, replace the files in vendor/bats-core.
|
If you want upgrade bats-core, replace the files in vendor/bats-core.
|
||||||
They must remain exactly as distributed by the chosen release of bats-core
|
They must remain exactly as distributed by the chosen release of bats-core
|
||||||
- see issue linked above for details.
|
- see issue linked above for details.
|
||||||
|
|
||||||
|
|
||||||
|
## Changes:
|
||||||
|
|
||||||
|
- 2021-05-03: Version update from `bats-core@1.1.0` to `bats-core@1.3.0`
|
||||||
|
- Initial import of `bats-core@1.1.0`
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
version: 'v1.1.0.{build}'
|
|
||||||
|
|
||||||
build: off
|
|
||||||
|
|
||||||
# This presumes that Git bash is installed at `C:\Program Files\Git` and the
|
|
||||||
# bash we're using is `C:\Program Files\Git\bin\bash.exe`.
|
|
||||||
#
|
|
||||||
# If instead it finds the Windows Subsystem for Linux bash at
|
|
||||||
# `C:\Windows\System32\bash.exe`, it will fail with an error like:
|
|
||||||
# /mnt/c/.../bats-core/test/test_helper.bash: line 1:
|
|
||||||
# syntax error near unexpected token `$'{\r''
|
|
||||||
test_script:
|
|
||||||
- where bash
|
|
||||||
- bash --version
|
|
||||||
- bash -c 'export'
|
|
||||||
- bash -c 'time PATH="/usr/bin:${PATH}" bin/bats test'
|
|
@ -0,0 +1,15 @@
|
|||||||
|
ARG bashver=latest
|
||||||
|
|
||||||
|
FROM bash:${bashver}
|
||||||
|
|
||||||
|
# Install parallel and accept the citation notice (we aren't using this in a
|
||||||
|
# context where it make sense to cite GNU Parallel).
|
||||||
|
RUN echo "@edgecomm http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \
|
||||||
|
apk update && \
|
||||||
|
apk add --no-cache parallel ncurses shellcheck@edgecomm && \
|
||||||
|
mkdir -p ~/.parallel && touch ~/.parallel/will-cite
|
||||||
|
|
||||||
|
RUN ln -s /opt/bats/bin/bats /usr/sbin/bats
|
||||||
|
COPY . /opt/bats/
|
||||||
|
|
||||||
|
ENTRYPOINT ["bash", "/usr/sbin/bats"]
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Bats core development environment",
|
||||||
|
"dockerFile": "Dockerfile",
|
||||||
|
"build": {"args": {"bashver": "4.3"}}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
max_line_length = 80
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# The JSON files contain newlines inconsistently
|
||||||
|
[*.json]
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = ignore
|
||||||
|
|
||||||
|
# YAML
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# Makefiles always use tabs for recipe indentation
|
||||||
|
[{Makefile,*.mak}]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
# Markdown
|
||||||
|
[*.{md,rmd,mkd,mkdn,mdwn,mdown,markdown,litcoffee}]
|
||||||
|
max_line_length = 80
|
||||||
|
# tabs behave as if they were replaced by spaces with a tab stop of 4 characters
|
||||||
|
tab_width = 4
|
||||||
|
# trailing spaces indicates word wrap
|
||||||
|
trim_trailing_spaces = false
|
||||||
|
trim_trailing_whitespace = false
|
@ -0,0 +1,65 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release: { types: [published] }
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'Version to simulate for deploy'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
version-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- run: |
|
||||||
|
EXPECTED_VERSION=${{ github.event.inputs.version }}
|
||||||
|
EXPECTED_VERSION=${EXPECTED_VERSION:-${GITHUB_REF/refs\/tags\//}}
|
||||||
|
echo "EXPECTED_VERSION=$EXPECTED_VERSION" >> $GITHUB_ENV
|
||||||
|
- name: Check tag version matches artifact versions
|
||||||
|
run: |
|
||||||
|
echo "Expected version: $EXPECTED_VERSION"
|
||||||
|
# use double negation to see the result unless we get a match
|
||||||
|
(./bin/bats --version | grep -F "$EXPECTED_VERSION") || (echo "Bats version check failed: "; ./bin/bats --version; exit -1)
|
||||||
|
(npm view . version | grep -F "$EXPECTED_VERSION") || (echo "npm version check failed: "; npm view . version; exit -1)
|
||||||
|
(grep '^Version:' 'contrib/rpm/bats.spec' | grep -F "$EXPECTED_VERSION") || (echo "debian package version check failed: "; grep '^Version:' 'contrib/rpm/bats.spec'; exit -1)
|
||||||
|
|
||||||
|
npmjs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: version-check
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
- run: npm publish --ignore-scripts
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
|
github-npm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: version-check
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
registry-url: "https://npm.pkg.github.com"
|
||||||
|
- name: scope package name as required by GHPR
|
||||||
|
run: npm init -y --scope ${{ github.repository_owner }}
|
||||||
|
- run: npm publish --ignore-scripts
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
dockerhub:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: version-check
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: docker/build-push-action@v1
|
||||||
|
with:
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/amd64
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
tags: bats/bats:${GITHUB_REF/refs\/tags\//}
|
@ -0,0 +1,91 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
# Controls when the action will run.
|
||||||
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shellcheck:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run shellcheck
|
||||||
|
run: |
|
||||||
|
sudo apt-get update -y
|
||||||
|
sudo apt-get install shellcheck
|
||||||
|
./shellcheck.sh
|
||||||
|
|
||||||
|
linux:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ['ubuntu-20.04', 'ubuntu-18.04', 'ubuntu-16.04']
|
||||||
|
env_vars:
|
||||||
|
- ''
|
||||||
|
# allow for some parallelity without GNU parallel, since it is not installed by default
|
||||||
|
- 'BATS_NO_PARALLELIZE_ACROSS_FILES=1 BATS_NUMBER_OF_PARALLEL_JOBS=2'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run test on OS ${{ matrix.os }}
|
||||||
|
shell: 'script -q -e -c "bash {0}"' # work around tty issues
|
||||||
|
env:
|
||||||
|
TERM: linux # fix tput for tty issue work around
|
||||||
|
run: |
|
||||||
|
bash --version
|
||||||
|
bash -c "time ${{ matrix.env_vars }} bin/bats --formatter tap test"
|
||||||
|
|
||||||
|
windows:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ['windows-2019']
|
||||||
|
env_vars:
|
||||||
|
- ''
|
||||||
|
# allow for some parallelity without GNU parallel, since it is not installed by default
|
||||||
|
- 'BATS_NO_PARALLELIZE_ACROSS_FILES=1 BATS_NUMBER_OF_PARALLEL_JOBS=2'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run test on OS ${{ matrix.os }}
|
||||||
|
run: |
|
||||||
|
bash --version
|
||||||
|
bash -c "time ${{ matrix.env_vars }} bin/bats --formatter tap test"
|
||||||
|
|
||||||
|
macos:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: ['macos-10.15']
|
||||||
|
env_vars:
|
||||||
|
- ''
|
||||||
|
# allow for some parallelity without GNU parallel, since it is not installed by default
|
||||||
|
- 'BATS_NO_PARALLELIZE_ACROSS_FILES=1 BATS_NUMBER_OF_PARALLEL_JOBS=2'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install unbuffer via expect
|
||||||
|
run: brew install expect
|
||||||
|
- name: Run test on OS ${{ matrix.os }}
|
||||||
|
shell: 'unbuffer bash {0}' # work around tty issues
|
||||||
|
env:
|
||||||
|
TERM: linux # fix tput for tty issue work around
|
||||||
|
run: |
|
||||||
|
bash --version
|
||||||
|
bash -c "time ${{ matrix.env_vars }} bin/bats --formatter tap test"
|
||||||
|
|
||||||
|
bash-version:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
version: ['3.2', '4.0', '4.1', '4.2', '4.3', '4.4', '4', '5.0', '5.1', '5', 'latest']
|
||||||
|
env_vars:
|
||||||
|
- ''
|
||||||
|
# also test running (recursively!) in parallel
|
||||||
|
- '-e BATS_NUMBER_OF_PARALLEL_JOBS=2'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Run test on bash version ${{ matrix.version }}
|
||||||
|
shell: 'script -q -e -c "bash {0}"' # work around tty issues
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
docker build --build-arg bashver="${{ matrix.version }}" --tag "bats/bats:bash-${{ matrix.version }}" .
|
||||||
|
docker run -it "bash:${{ matrix.version }}" --version
|
||||||
|
time docker run -it ${{ matrix.env_vars }} "bats/bats:bash-${{ matrix.version }}" --tap /opt/bats/test
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
/docker-compose.override.yml
|
||||||
|
/docs/build
|
||||||
|
|
||||||
|
# npm
|
||||||
|
/bats-*.tgz
|
||||||
|
# we don't have any deps; un-ignore if that changes
|
||||||
|
/package-lock.json
|
@ -0,0 +1,9 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
sphinx:
|
||||||
|
configuration: docs/source/conf.py
|
||||||
|
|
||||||
|
python:
|
||||||
|
version: 3.7
|
||||||
|
install:
|
||||||
|
- requirements: docs/source/requirements.txt
|
@ -1,34 +0,0 @@
|
|||||||
language: bash
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
|
|
||||||
env:
|
|
||||||
- BASHVER=
|
|
||||||
- BASHVER=3.2
|
|
||||||
- BASHVER=4.0
|
|
||||||
- BASHVER=4.1
|
|
||||||
- BASHVER=4.2
|
|
||||||
- BASHVER=4.3
|
|
||||||
- BASHVER=4.4
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: osx
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
script:
|
|
||||||
- |
|
|
||||||
if [[ "$TRAVIS_OS_NAME" == 'linux' && -n "$BASHVER" ]]; then
|
|
||||||
docker build --build-arg bashver=${BASHVER} --tag bats/bats:bash-${BASHVER} .
|
|
||||||
docker run -it bash:${BASHVER} --version
|
|
||||||
time docker run -it bats/bats:bash-${BASHVER} --tap /opt/bats/test
|
|
||||||
else
|
|
||||||
time bin/bats --tap test
|
|
||||||
fi
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
on_success: never
|
|
@ -1,50 +1,59 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
export BATS_READLINK='true'
|
if command -v greadlink >/dev/null; then
|
||||||
if command -v 'greadlink' >/dev/null; then
|
bats_readlinkf() {
|
||||||
BATS_READLINK='greadlink'
|
greadlink -f "$1"
|
||||||
elif command -v 'readlink' >/dev/null; then
|
}
|
||||||
BATS_READLINK='readlink'
|
else
|
||||||
|
bats_readlinkf() {
|
||||||
|
readlink -f "$1"
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
bats_resolve_link() {
|
fallback_to_readlinkf_posix() {
|
||||||
if ! "$BATS_READLINK" "$1"; then
|
bats_readlinkf() {
|
||||||
return 0
|
[ "${1:-}" ] || return 1
|
||||||
fi
|
max_symlinks=40
|
||||||
|
CDPATH='' # to avoid changing to an unexpected directory
|
||||||
|
|
||||||
|
target=$1
|
||||||
|
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
|
||||||
|
[ -d "${target:-/}" ] && target="$target/"
|
||||||
|
|
||||||
|
cd -P . 2>/dev/null || return 1
|
||||||
|
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
|
||||||
|
if [ ! "$target" = "${target%/*}" ]; then
|
||||||
|
case $target in
|
||||||
|
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
|
||||||
|
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
|
||||||
|
esac
|
||||||
|
target=${target##*/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -L "$target" ]; then
|
||||||
|
target="${PWD%/}${target:+/}${target}"
|
||||||
|
printf '%s\n' "${target:-/}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
|
||||||
|
# <file mode>, <number of links>, <owner name>, <group name>,
|
||||||
|
# <size>, <date and time>, <pathname of link>, <contents of link>
|
||||||
|
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
|
||||||
|
link=$(ls -dl -- "$target" 2>/dev/null) || break
|
||||||
|
target=${link#*" $target -> "}
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bats_resolve_absolute_root_dir() {
|
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
|
||||||
local cwd="$PWD"
|
fallback_to_readlinkf_posix
|
||||||
local path="$1"
|
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
|
||||||
local result="$2"
|
fi
|
||||||
local target_dir
|
|
||||||
local target_name
|
|
||||||
local original_shell_options="$-"
|
|
||||||
|
|
||||||
# Resolve the parent directory, e.g. /bin => /usr/bin on CentOS (#113).
|
|
||||||
set -P
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
target_dir="${path%/*}"
|
|
||||||
target_name="${path##*/}"
|
|
||||||
|
|
||||||
if [[ "$target_dir" != "$path" ]]; then
|
|
||||||
cd "$target_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -L "$target_name" ]]; then
|
|
||||||
path="$(bats_resolve_link "$target_name")"
|
|
||||||
else
|
|
||||||
printf -v "$result" -- '%s' "${PWD%/*}"
|
|
||||||
set +P "-$original_shell_options"
|
|
||||||
cd "$cwd"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
export BATS_ROOT
|
export BATS_ROOT=${BATS_PATH%/*/*}
|
||||||
bats_resolve_absolute_root_dir "$0" 'BATS_ROOT'
|
export -f bats_readlinkf
|
||||||
exec "$BATS_ROOT/libexec/bats-core/bats" "$@"
|
exec env BATS_ROOT="$BATS_ROOT" "$BATS_ROOT/libexec/bats-core/bats" "$@"
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# bats-core git releaser
|
||||||
|
#
|
||||||
|
## Usage: %SCRIPT_NAME% [options]
|
||||||
|
##
|
||||||
|
## Options:
|
||||||
|
## --major Major version bump
|
||||||
|
## --minor Minor version bump
|
||||||
|
## --patch Patch version bump
|
||||||
|
##
|
||||||
|
## -v, --version Print version
|
||||||
|
## --debug Enable debug mode
|
||||||
|
## -h, --help Display this message
|
||||||
|
##
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
DIR=$(cd "$(dirname "${0}")" && pwd)
|
||||||
|
THIS_SCRIPT="${DIR}/$(basename "${0}")"
|
||||||
|
BATS_VERSION=$(
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source <(grep '^export BATS_VERSION=' libexec/bats-core/bats)
|
||||||
|
echo "${BATS_VERSION}"
|
||||||
|
)
|
||||||
|
declare -r DIR
|
||||||
|
declare -r THIS_SCRIPT
|
||||||
|
declare -r BATS_VERSION
|
||||||
|
|
||||||
|
BUMP_INTERVAL=""
|
||||||
|
NEW_BATS_VERSION=""
|
||||||
|
|
||||||
|
main() {
|
||||||
|
handle_arguments "${@}"
|
||||||
|
|
||||||
|
if [[ "${BUMP_INTERVAL:-}" == "" ]]; then
|
||||||
|
echo "${BATS_VERSION}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local NEW_BATS_VERSION
|
||||||
|
NEW_BATS_VERSION=$(semver bump "${BUMP_INTERVAL}" "${BATS_VERSION}")
|
||||||
|
declare -r NEW_BATS_VERSION
|
||||||
|
|
||||||
|
local BATS_RELEASE_NOTES="/tmp/bats-release-${NEW_BATS_VERSION}"
|
||||||
|
|
||||||
|
echo "Releasing: ${BATS_VERSION} to ${NEW_BATS_VERSION}"
|
||||||
|
echo
|
||||||
|
|
||||||
|
echo "Ensure docs/CHANGELOG.md is correctly updated"
|
||||||
|
|
||||||
|
replace_in_files
|
||||||
|
|
||||||
|
write_changelog
|
||||||
|
|
||||||
|
git diff --staged
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
1. Version numbers have been updated. Commit the changes:
|
||||||
|
|
||||||
|
git commit -m "feat: release Bats v${NEW_BATS_VERSION}"
|
||||||
|
|
||||||
|
2. Verify this autogenerated changelog (from docs/CHANGELOG.md):
|
||||||
|
|
||||||
|
# changelog start
|
||||||
|
EOF
|
||||||
|
|
||||||
|
local DELIM=$(echo -en "\001");
|
||||||
|
sed -E -n "\\${DELIM}^## \[${NEW_BATS_VERSION}\]${DELIM},\\${DELIM}^## ${DELIM}p" docs/CHANGELOG.md \
|
||||||
|
| head -n -1 \
|
||||||
|
| sed -E \
|
||||||
|
-e 's,^## \[([0-9\.]+)] - (.*),Bats \1\n\nReleased: \2,' \
|
||||||
|
-e 's,^### (.*),\1:,g' \
|
||||||
|
| tee "${BATS_RELEASE_NOTES}"
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
# changelog end
|
||||||
|
|
||||||
|
3. Tag the release using the autogenerated changelog:
|
||||||
|
|
||||||
|
git tag -a -s "v${NEW_BATS_VERSION}" --message "${BATS_RELEASE_NOTES}"
|
||||||
|
|
||||||
|
4. Push the changes:
|
||||||
|
|
||||||
|
git push --follow-tags
|
||||||
|
|
||||||
|
5. Use Github hub to make a draft release:
|
||||||
|
|
||||||
|
hub release create "v${NEW_BATS_VERSION}" --draft --file "${BATS_RELEASE_NOTES}"
|
||||||
|
|
||||||
|
6. Navigate to the provided URL, verify changes, and release Bats ${NEW_BATS_VERSION}.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_in_files() {
|
||||||
|
declare -a FILE_REPLACEMENTS=(
|
||||||
|
"contrib/rpm/bats.spec,^Version:"
|
||||||
|
"libexec/bats-core/bats,^export BATS_VERSION="
|
||||||
|
"package.json,^ \"version\":"
|
||||||
|
)
|
||||||
|
|
||||||
|
for FILE_REPLACEMENT in "${FILE_REPLACEMENTS[@]}"; do
|
||||||
|
FILE="${FILE_REPLACEMENT/,*/}"
|
||||||
|
MATCH="${FILE_REPLACEMENT/*,/}"
|
||||||
|
sed -E -i.bak "/${MATCH}/ { s,${BATS_VERSION},${NEW_BATS_VERSION},g; }" "${FILE}"
|
||||||
|
rm "${FILE}.bak" || true
|
||||||
|
git add -f "${FILE}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
write_changelog() {
|
||||||
|
local FILE="docs/CHANGELOG.md"
|
||||||
|
sed -E -i.bak "/## \[Unreleased\]/ a \\\n## [${NEW_BATS_VERSION}] - $(date +%Y-%m-%d)" "${FILE}"
|
||||||
|
|
||||||
|
rm "${FILE}.bak" || true
|
||||||
|
|
||||||
|
cp "${FILE}" "${FILE}.new"
|
||||||
|
sed -E -i.bak '/## \[Unreleased\]/,+1d' "${FILE}"
|
||||||
|
git add -f "${FILE}"
|
||||||
|
mv "${FILE}.new" "${FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_arguments() {
|
||||||
|
parse_arguments "${@:-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_arguments() {
|
||||||
|
local CURRENT_ARG
|
||||||
|
|
||||||
|
if [[ "${#}" == 1 && "${1:-}" == "" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [[ "${#}" -gt 0 ]]; do
|
||||||
|
CURRENT_ARG="${1}"
|
||||||
|
|
||||||
|
case ${CURRENT_ARG} in
|
||||||
|
--major)
|
||||||
|
BUMP_INTERVAL="major"
|
||||||
|
;;
|
||||||
|
# ---
|
||||||
|
--minor)
|
||||||
|
BUMP_INTERVAL="minor"
|
||||||
|
;;
|
||||||
|
--patch)
|
||||||
|
BUMP_INTERVAL="patch"
|
||||||
|
;;
|
||||||
|
-h | --help) usage ;;
|
||||||
|
-v | --version)
|
||||||
|
get_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--debug)
|
||||||
|
set -xe
|
||||||
|
;;
|
||||||
|
-*) usage "${CURRENT_ARG}: unknown option" ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
semver() {
|
||||||
|
"${DIR}/semver" "${@:-}"
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
sed -n '/^##/,/^$/s/^## \{0,1\}//p' "${THIS_SCRIPT}" | sed "s/%SCRIPT_NAME%/$(basename "${THIS_SCRIPT}")/g"
|
||||||
|
exit 2
|
||||||
|
} 2>/dev/null
|
||||||
|
|
||||||
|
get_version() {
|
||||||
|
echo "${THIS_SCRIPT_VERSION:-0.1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "${@}"
|
@ -0,0 +1,281 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# v3.0.0
|
||||||
|
# https://github.com/fsaintjacques/semver-tool
|
||||||
|
|
||||||
|
set -o errexit -o nounset -o pipefail
|
||||||
|
|
||||||
|
NAT='0|[1-9][0-9]*'
|
||||||
|
ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'
|
||||||
|
IDENT="$NAT|$ALPHANUM"
|
||||||
|
FIELD='[0-9A-Za-z-]+'
|
||||||
|
|
||||||
|
SEMVER_REGEX="\
|
||||||
|
^[vV]?\
|
||||||
|
($NAT)\\.($NAT)\\.($NAT)\
|
||||||
|
(\\-(${IDENT})(\\.(${IDENT}))*)?\
|
||||||
|
(\\+${FIELD}(\\.${FIELD})*)?$"
|
||||||
|
|
||||||
|
PROG=semver
|
||||||
|
PROG_VERSION="3.0.0"
|
||||||
|
|
||||||
|
USAGE="\
|
||||||
|
Usage:
|
||||||
|
$PROG bump (major|minor|patch|release|prerel <prerel>|build <build>) <version>
|
||||||
|
$PROG compare <version> <other_version>
|
||||||
|
$PROG get (major|minor|patch|release|prerel|build) <version>
|
||||||
|
$PROG --help
|
||||||
|
$PROG --version
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<version> A version must match the following regular expression:
|
||||||
|
\"${SEMVER_REGEX}\"
|
||||||
|
In English:
|
||||||
|
-- The version must match X.Y.Z[-PRERELEASE][+BUILD]
|
||||||
|
where X, Y and Z are non-negative integers.
|
||||||
|
-- PRERELEASE is a dot separated sequence of non-negative integers and/or
|
||||||
|
identifiers composed of alphanumeric characters and hyphens (with
|
||||||
|
at least one non-digit). Numeric identifiers must not have leading
|
||||||
|
zeros. A hyphen (\"-\") introduces this optional part.
|
||||||
|
-- BUILD is a dot separated sequence of identifiers composed of alphanumeric
|
||||||
|
characters and hyphens. A plus (\"+\") introduces this optional part.
|
||||||
|
|
||||||
|
<other_version> See <version> definition.
|
||||||
|
|
||||||
|
<prerel> A string as defined by PRERELEASE above.
|
||||||
|
|
||||||
|
<build> A string as defined by BUILD above.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --version Print the version of this tool.
|
||||||
|
-h, --help Print this help message.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
bump Bump by one of major, minor, patch; zeroing or removing
|
||||||
|
subsequent parts. \"bump prerel\" sets the PRERELEASE part and
|
||||||
|
removes any BUILD part. \"bump build\" sets the BUILD part.
|
||||||
|
\"bump release\" removes any PRERELEASE or BUILD parts.
|
||||||
|
The bumped version is written to stdout.
|
||||||
|
|
||||||
|
compare Compare <version> with <other_version>, output to stdout the
|
||||||
|
following values: -1 if <other_version> is newer, 0 if equal, 1 if
|
||||||
|
older. The BUILD part is not used in comparisons.
|
||||||
|
|
||||||
|
get Extract given part of <version>, where part is one of major, minor,
|
||||||
|
patch, prerel, build, or release.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
https://semver.org -- Semantic Versioning 2.0.0"
|
||||||
|
|
||||||
|
function error {
|
||||||
|
echo -e "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage-help {
|
||||||
|
error "$USAGE"
|
||||||
|
}
|
||||||
|
|
||||||
|
function usage-version {
|
||||||
|
echo -e "${PROG}: $PROG_VERSION"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate-version {
|
||||||
|
local version=$1
|
||||||
|
if [[ "$version" =~ $SEMVER_REGEX ]]; then
|
||||||
|
# if a second argument is passed, store the result in var named by $2
|
||||||
|
if [ "$#" -eq "2" ]; then
|
||||||
|
local major=${BASH_REMATCH[1]}
|
||||||
|
local minor=${BASH_REMATCH[2]}
|
||||||
|
local patch=${BASH_REMATCH[3]}
|
||||||
|
local prere=${BASH_REMATCH[4]}
|
||||||
|
local build=${BASH_REMATCH[8]}
|
||||||
|
eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")"
|
||||||
|
else
|
||||||
|
echo "$version"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "version $version does not match the semver scheme 'X.Y.Z(-PRERELEASE)(+BUILD)'. See help for more information."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function is-nat {
|
||||||
|
[[ "$1" =~ ^($NAT)$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
function is-null {
|
||||||
|
[ -z "$1" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
function order-nat {
|
||||||
|
[ "$1" -lt "$2" ] && { echo -1 ; return ; }
|
||||||
|
[ "$1" -gt "$2" ] && { echo 1 ; return ; }
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function order-string {
|
||||||
|
[[ $1 < $2 ]] && { echo -1 ; return ; }
|
||||||
|
[[ $1 > $2 ]] && { echo 1 ; return ; }
|
||||||
|
echo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# given two (named) arrays containing NAT and/or ALPHANUM fields, compare them
|
||||||
|
# one by one according to semver 2.0.0 spec. Return -1, 0, 1 if left array ($1)
|
||||||
|
# is less-than, equal, or greater-than the right array ($2). The longer array
|
||||||
|
# is considered greater-than the shorter if the shorter is a prefix of the longer.
|
||||||
|
#
|
||||||
|
function compare-fields {
|
||||||
|
local l="$1[@]"
|
||||||
|
local r="$2[@]"
|
||||||
|
local leftfield=( "${!l}" )
|
||||||
|
local rightfield=( "${!r}" )
|
||||||
|
local left
|
||||||
|
local right
|
||||||
|
|
||||||
|
local i=$(( -1 ))
|
||||||
|
local order=$(( 0 ))
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
[ $order -ne 0 ] && { echo $order ; return ; }
|
||||||
|
|
||||||
|
: $(( i++ ))
|
||||||
|
left="${leftfield[$i]}"
|
||||||
|
right="${rightfield[$i]}"
|
||||||
|
|
||||||
|
is-null "$left" && is-null "$right" && { echo 0 ; return ; }
|
||||||
|
is-null "$left" && { echo -1 ; return ; }
|
||||||
|
is-null "$right" && { echo 1 ; return ; }
|
||||||
|
|
||||||
|
is-nat "$left" && is-nat "$right" && { order=$(order-nat "$left" "$right") ; continue ; }
|
||||||
|
is-nat "$left" && { echo -1 ; return ; }
|
||||||
|
is-nat "$right" && { echo 1 ; return ; }
|
||||||
|
{ order=$(order-string "$left" "$right") ; continue ; }
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2206 # checked by "validate"; ok to expand prerel id's into array
|
||||||
|
function compare-version {
|
||||||
|
local order
|
||||||
|
validate-version "$1" V
|
||||||
|
validate-version "$2" V_
|
||||||
|
|
||||||
|
# compare major, minor, patch
|
||||||
|
|
||||||
|
local left=( "${V[0]}" "${V[1]}" "${V[2]}" )
|
||||||
|
local right=( "${V_[0]}" "${V_[1]}" "${V_[2]}" )
|
||||||
|
|
||||||
|
order=$(compare-fields left right)
|
||||||
|
[ "$order" -ne 0 ] && { echo "$order" ; return ; }
|
||||||
|
|
||||||
|
# compare pre-release ids when M.m.p are equal
|
||||||
|
|
||||||
|
local prerel="${V[3]:1}"
|
||||||
|
local prerel_="${V_[3]:1}"
|
||||||
|
local left=( ${prerel//./ } )
|
||||||
|
local right=( ${prerel_//./ } )
|
||||||
|
|
||||||
|
# if left and right have no pre-release part, then left equals right
|
||||||
|
# if only one of left/right has pre-release part, that one is less than simple M.m.p
|
||||||
|
|
||||||
|
[ -z "$prerel" ] && [ -z "$prerel_" ] && { echo 0 ; return ; }
|
||||||
|
[ -z "$prerel" ] && { echo 1 ; return ; }
|
||||||
|
[ -z "$prerel_" ] && { echo -1 ; return ; }
|
||||||
|
|
||||||
|
# otherwise, compare the pre-release id's
|
||||||
|
|
||||||
|
compare-fields left right
|
||||||
|
}
|
||||||
|
|
||||||
|
function command-bump {
|
||||||
|
local new; local version; local sub_version; local command;
|
||||||
|
|
||||||
|
case $# in
|
||||||
|
2) case $1 in
|
||||||
|
major|minor|patch|release) command=$1; version=$2;;
|
||||||
|
*) usage-help;;
|
||||||
|
esac ;;
|
||||||
|
3) case $1 in
|
||||||
|
prerel|build) command=$1; sub_version=$2 version=$3 ;;
|
||||||
|
*) usage-help;;
|
||||||
|
esac ;;
|
||||||
|
*) usage-help;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
validate-version "$version" parts
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
local major="${parts[0]}"
|
||||||
|
local minor="${parts[1]}"
|
||||||
|
local patch="${parts[2]}"
|
||||||
|
local prere="${parts[3]}"
|
||||||
|
local build="${parts[4]}"
|
||||||
|
|
||||||
|
case "$command" in
|
||||||
|
major) new="$((major + 1)).0.0";;
|
||||||
|
minor) new="${major}.$((minor + 1)).0";;
|
||||||
|
patch) new="${major}.${minor}.$((patch + 1))";;
|
||||||
|
release) new="${major}.${minor}.${patch}";;
|
||||||
|
prerel) new=$(validate-version "${major}.${minor}.${patch}-${sub_version}");;
|
||||||
|
build) new=$(validate-version "${major}.${minor}.${patch}${prere}+${sub_version}");;
|
||||||
|
*) usage-help ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "$new"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function command-compare {
|
||||||
|
local v; local v_;
|
||||||
|
|
||||||
|
case $# in
|
||||||
|
2) v=$(validate-version "$1"); v_=$(validate-version "$2") ;;
|
||||||
|
*) usage-help ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
set +u # need unset array element to evaluate to null
|
||||||
|
compare-version "$v" "$v_"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
function command-get {
|
||||||
|
local part version
|
||||||
|
|
||||||
|
if [[ "$#" -ne "2" ]] || [[ -z "$1" ]] || [[ -z "$2" ]]; then
|
||||||
|
usage-help
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
part="$1"
|
||||||
|
version="$2"
|
||||||
|
|
||||||
|
validate-version "$version" parts
|
||||||
|
local major="${parts[0]}"
|
||||||
|
local minor="${parts[1]}"
|
||||||
|
local patch="${parts[2]}"
|
||||||
|
local prerel="${parts[3]:1}"
|
||||||
|
local build="${parts[4]:1}"
|
||||||
|
local release="${major}.${minor}.${patch}"
|
||||||
|
|
||||||
|
case "$part" in
|
||||||
|
major|minor|patch|release|prerel|build) echo "${!part}" ;;
|
||||||
|
*) usage-help ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
case $# in
|
||||||
|
0) echo "Unknown command: $*"; usage-help;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
--help|-h) echo -e "$USAGE"; exit 0;;
|
||||||
|
--version|-v) usage-version ;;
|
||||||
|
bump) shift; command-bump "$@";;
|
||||||
|
get) shift; command-get "$@";;
|
||||||
|
compare) shift; command-compare "$@";;
|
||||||
|
*) echo "Unknown arguments: $*"; usage-help;;
|
||||||
|
esac
|
@ -0,0 +1,8 @@
|
|||||||
|
# Copy this file to docker-compose.override.yml
|
||||||
|
version: '3.6'
|
||||||
|
services:
|
||||||
|
bats:
|
||||||
|
entrypoint:
|
||||||
|
- "bash"
|
||||||
|
networks:
|
||||||
|
default:
|
@ -0,0 +1,13 @@
|
|||||||
|
version: '3.6'
|
||||||
|
services:
|
||||||
|
bats:
|
||||||
|
build:
|
||||||
|
context: "."
|
||||||
|
dockerfile: "Dockerfile"
|
||||||
|
networks:
|
||||||
|
- "default"
|
||||||
|
user: "root"
|
||||||
|
volumes:
|
||||||
|
- "./:/opt/bats"
|
||||||
|
networks:
|
||||||
|
default:
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"MD024": { "siblings_only": true }
|
||||||
|
}
|
@ -0,0 +1,231 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog][kac] and this project adheres to
|
||||||
|
[Semantic Versioning][semver].
|
||||||
|
|
||||||
|
[kac]: https://keepachangelog.com/en/1.0.0/
|
||||||
|
[semver]: https://semver.org/
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.3.0] - 2021-03-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* custom test-file extension via `BATS_FILE_EXTENSION` when searching for test
|
||||||
|
files in a directory (#376)
|
||||||
|
* TAP13 formatter, including millisecond timing (#337)
|
||||||
|
* automatic release to NPM via Github Actions (#406)
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
* added documentation about overusing `run` (#343)
|
||||||
|
* improved documentation of `load` (#332)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* recursive suite mode will follow symlinks now (#370)
|
||||||
|
* split options for (file-) `--report-formatter` and (stdout) `--formatter` (#345)
|
||||||
|
* **WARNING**: This changes the meaning of `--formatter junit`.
|
||||||
|
stdout will now show unified xml instead of TAP. From now on, please use
|
||||||
|
`--report-formatter junit` to obtain the `.xml` report file!
|
||||||
|
* removed `--parallel-preserve-environment` flag, as this is the default
|
||||||
|
behavior (#324)
|
||||||
|
* moved CI from Travis/Appveyor to Github Actions (#405)
|
||||||
|
* preprocessed files are no longer removed if `--no-tempdir-cleanup` is
|
||||||
|
specified (#395)
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
* moved documentation to [readthedocs](https://bats-core.readthedocs.io/en/latest/)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
#### Correctness
|
||||||
|
|
||||||
|
* fix internal failures due to unbound variables when test files use `set -u` (#392)
|
||||||
|
* fix internal failures due to changes to `$PATH` in test files (#387)
|
||||||
|
* fix test duration always being 0 on busybox installs (#363)
|
||||||
|
* fix hangs on CTRL+C (#354)
|
||||||
|
* make `BATS_TEST_NUMBER` count per file again (#326)
|
||||||
|
* include `lib/` in npm package (#352)
|
||||||
|
|
||||||
|
#### Performance
|
||||||
|
|
||||||
|
* don't fork bomb in parallel mode (#339)
|
||||||
|
* preprocess each file only once (#335)
|
||||||
|
* avoid running duplicate files n^2 times (#338)
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
* fix documentation for `--formatter junit` (#334)
|
||||||
|
* fix documentation for `setup_file` variables (#333)
|
||||||
|
* fix link to examples page (#331)
|
||||||
|
* fix link to "File Descriptor 3" section (#301)
|
||||||
|
|
||||||
|
## [1.2.1] - 2020-07-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* JUnit output and extensible formatter rewrite (#246)
|
||||||
|
* `load` function now reads from absolute and relative paths, and $PATH (#282)
|
||||||
|
* Beginner-friendly examples in /docs/examples (#243)
|
||||||
|
* @peshay's `bats-file` fork contributed to `bats-core/bats-file` (#276)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Duplicate test names now error (previous behaviour was to issue a warning) (#286)
|
||||||
|
* Changed default formatter in Docker to pretty by adding `ncurses` to
|
||||||
|
Dockerfile, override with `--tap` (#239)
|
||||||
|
* Replace "readlink -f" dependency with Bash solution (#217)
|
||||||
|
|
||||||
|
## [1.2.0] - 2020-04-25
|
||||||
|
|
||||||
|
Support parallel suite execution and filtering by test name.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* docs/CHANGELOG.md and docs/releasing.md (#122)
|
||||||
|
* The `-f, --filter` flag to run only the tests matching a regular expression (#126)
|
||||||
|
* Optimize stack trace capture (#138)
|
||||||
|
* `--jobs n` flag to support parallel execution of tests with GNU parallel (#172)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* AppVeyor builds are now semver-compliant (#123)
|
||||||
|
* Add Bash 5 as test target (#181)
|
||||||
|
* Always use upper case signal names to avoid locale dependent err… (#215)
|
||||||
|
* Fix for tests reading from stdin (#227)
|
||||||
|
* Fix wrong line numbers of errors in bash < 4.4 (#229)
|
||||||
|
* Remove preprocessed source after test run (#232)
|
||||||
|
|
||||||
|
## [1.1.0] - 2018-07-08
|
||||||
|
|
||||||
|
This is the first release with new features relative to the original Bats 0.4.0.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* The `-r, --recursive` flag to scan directory arguments recursively for
|
||||||
|
`*.bats` files (#109)
|
||||||
|
* The `contrib/rpm/bats.spec` file to build RPMs (#111)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Travis exercises latest versions of Bash from 3.2 through 4.4 (#116, #117)
|
||||||
|
* Error output highlights invalid command line options (#45, #46, #118)
|
||||||
|
* Replaced `echo` with `printf` (#120)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fixed `BATS_ERROR_STATUS` getting lost when `bats_error_trap` fired multiple
|
||||||
|
times under Bash 4.2.x (#110)
|
||||||
|
* Updated `bin/bats` symlink resolution, handling the case on CentOS where
|
||||||
|
`/bin` is a symlink to `/usr/bin` (#113, #115)
|
||||||
|
|
||||||
|
## [1.0.2] - 2018-06-18
|
||||||
|
|
||||||
|
* Fixed sstephenson/bats#240, whereby `skip` messages containing parentheses
|
||||||
|
were truncated (#48)
|
||||||
|
* Doc improvements:
|
||||||
|
* Docker usage (#94)
|
||||||
|
* Better README badges (#101)
|
||||||
|
* Better installation instructions (#102, #104)
|
||||||
|
* Packaging/installation improvements:
|
||||||
|
* package.json update (#100)
|
||||||
|
* Moved `libexec/` files to `libexec/bats-core/`, improved `install.sh` (#105)
|
||||||
|
|
||||||
|
## [1.0.1] - 2018-06-09
|
||||||
|
|
||||||
|
* Fixed a `BATS_CWD` bug introduced in #91 whereby it was set to the parent of
|
||||||
|
`PWD`, when it should've been set to `PWD` itself (#98). This caused file
|
||||||
|
names in stack traces to contain the basename of `PWD` as a prefix, when the
|
||||||
|
names should've been purely relative to `PWD`.
|
||||||
|
* Ensure the last line of test output prints when it doesn't end with a newline
|
||||||
|
(#99). This was a quasi-bug introduced by replacing `sed` with `while` in #88.
|
||||||
|
|
||||||
|
## [1.0.0] - 2018-06-08
|
||||||
|
|
||||||
|
`1.0.0` generally preserves compatibility with `0.4.0`, but with some Bash
|
||||||
|
compatibility improvements and a massive performance boost. In other words:
|
||||||
|
|
||||||
|
* all existing tests should remain compatible
|
||||||
|
* tests that might've failed or exhibited unexpected behavior on earlier
|
||||||
|
versions of Bash should now also pass or behave as expected
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* Added support for Docker.
|
||||||
|
* Added support for test scripts that have the [unofficial strict
|
||||||
|
mode](http://redsymbol.net/articles/unofficial-bash-strict-mode/) enabled.
|
||||||
|
* Improved stability on Windows and macOS platforms.
|
||||||
|
* Massive performance improvements, especially on Windows (#8)
|
||||||
|
* Workarounds for inconsistent behavior between Bash versions (#82)
|
||||||
|
* Workaround for preserving stack info after calling an exported function under
|
||||||
|
Bash < 4.4 (#87)
|
||||||
|
* Fixed TAP compliance for skipped tests
|
||||||
|
* Added support for tabs in test names.
|
||||||
|
* `bin/bats` and `install.sh` now work reliably on Windows (#91)
|
||||||
|
|
||||||
|
## [0.4.0] - 2014-08-13
|
||||||
|
|
||||||
|
* Improved the display of failing test cases. Bats now shows the source code of
|
||||||
|
failing test lines, along with full stack traces including function names,
|
||||||
|
filenames, and line numbers.
|
||||||
|
* Improved the display of the pretty-printed test summary line to include the
|
||||||
|
number of skipped tests, if any.
|
||||||
|
* Improved the speed of the preprocessor, dramatically shortening test and suite
|
||||||
|
startup times.
|
||||||
|
* Added support for absolute pathnames to the `load` helper.
|
||||||
|
* Added support for single-line `@test` definitions.
|
||||||
|
* Added bats(1) and bats(7) manual pages.
|
||||||
|
* Modified the `bats` command to default to TAP output when the `$CI` variable
|
||||||
|
is set, to better support environments such as Travis CI.
|
||||||
|
|
||||||
|
## [0.3.1] - 2013-10-28
|
||||||
|
|
||||||
|
* Fixed an incompatibility with the pretty formatter in certain environments
|
||||||
|
such as tmux.
|
||||||
|
* Fixed a bug where the pretty formatter would crash if the first line of a test
|
||||||
|
file's output was invalid TAP.
|
||||||
|
|
||||||
|
## [0.3.0] - 2013-10-21
|
||||||
|
|
||||||
|
* Improved formatting for tests run from a terminal. Failing tests are now
|
||||||
|
colored in red, and the total number of failing tests is displayed at the end
|
||||||
|
of the test run. When Bats is not connected to a terminal (e.g. in CI runs),
|
||||||
|
or when invoked with the `--tap` flag, output is displayed in standard TAP
|
||||||
|
format.
|
||||||
|
* Added the ability to skip tests using the `skip` command.
|
||||||
|
* Added a message to failing test case output indicating the file and line
|
||||||
|
number of the statement that caused the test to fail.
|
||||||
|
* Added "ad-hoc" test suite support. You can now invoke `bats` with multiple
|
||||||
|
filename or directory arguments to run all the specified tests in aggregate.
|
||||||
|
* Added support for test files with Windows line endings.
|
||||||
|
* Fixed regular expression warnings from certain versions of Bash.
|
||||||
|
* Fixed a bug running tests containing lines that begin with `-e`.
|
||||||
|
|
||||||
|
## [0.2.0] - 2012-11-16
|
||||||
|
|
||||||
|
* Added test suite support. The `bats` command accepts a directory name
|
||||||
|
containing multiple test files to be run in aggregate.
|
||||||
|
* Added the ability to count the number of test cases in a file or suite by
|
||||||
|
passing the `-c` flag to `bats`.
|
||||||
|
* Preprocessed sources are cached between test case runs in the same file for
|
||||||
|
better performance.
|
||||||
|
|
||||||
|
## [0.1.0] - 2011-12-30
|
||||||
|
|
||||||
|
* Initial public release.
|
||||||
|
|
||||||
|
[Unreleased]: https://github.com/bats-core/bats-core/compare/v1.1.0...HEAD
|
||||||
|
[1.1.0]: https://github.com/bats-core/bats-core/compare/v1.0.2...v1.1.0
|
||||||
|
[1.0.2]: https://github.com/bats-core/bats-core/compare/v1.0.1...v1.0.2
|
||||||
|
[1.0.1]: https://github.com/bats-core/bats-core/compare/v1.0.0...v1.0.1
|
||||||
|
[1.0.0]: https://github.com/bats-core/bats-core/compare/v0.4.0...v1.0.0
|
||||||
|
[0.4.0]: https://github.com/bats-core/bats-core/compare/v0.3.1...v0.4.0
|
||||||
|
[0.3.1]: https://github.com/bats-core/bats-core/compare/v0.3.0...v0.3.1
|
||||||
|
[0.3.0]: https://github.com/bats-core/bats-core/compare/v0.2.0...v0.3.0
|
||||||
|
[0.2.0]: https://github.com/bats-core/bats-core/compare/v0.1.0...v0.2.0
|
||||||
|
[0.1.0]: https://github.com/bats-core/bats-core/commits/v0.1.0
|
@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = source
|
||||||
|
BUILDDIR = build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
@ -0,0 +1,6 @@
|
|||||||
|
# Examples
|
||||||
|
|
||||||
|
This directory contains example .bats files.
|
||||||
|
See the [bats-core wiki][examples] for more details.
|
||||||
|
|
||||||
|
[examples]: https://github.com/bats-core/bats-core/wiki/Examples
|
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# "unofficial" bash strict mode
|
||||||
|
# See: http://redsymbol.net/articles/unofficial-bash-strict-mode
|
||||||
|
set -o errexit # Exit when simple command fails 'set -e'
|
||||||
|
set -o errtrace # Exit on error inside any functions or subshells.
|
||||||
|
set -o nounset # Trigger error when expanding unset variables 'set -u'
|
||||||
|
set -o pipefail # Do not hide errors within pipes 'set -o pipefail'
|
||||||
|
set -o xtrace # Display expanded command and arguments 'set -x'
|
||||||
|
IFS=$'\n\t' # Split words on \n\t rather than spaces
|
||||||
|
|
||||||
|
main() {
|
||||||
|
tar -czf "$dst_tarball" -C "$src_dir" .
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
export dst_tarball="${BATS_TMPDIR}/dst.tar.gz"
|
||||||
|
export src_dir="${BATS_TMPDIR}/src_dir"
|
||||||
|
|
||||||
|
rm -rf "${dst_tarball}" "${src_dir}"
|
||||||
|
mkdir "${src_dir}"
|
||||||
|
touch "${src_dir}"/{a,b,c}
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
bash "${BATS_TEST_DIRNAME}"/package-tarball
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'fail when \$src_dir and \$dst_tarball are unbound' {
|
||||||
|
unset src_dir dst_tarball
|
||||||
|
|
||||||
|
run main
|
||||||
|
[ "${status}" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'fail when \$src_dir is a non-existent directory' {
|
||||||
|
src_dir='not-a-dir'
|
||||||
|
|
||||||
|
run main
|
||||||
|
[ "${status}" -ne 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'pass when \$src_dir directory is empty' {
|
||||||
|
rm -rf "${src_dir:?}/*"
|
||||||
|
|
||||||
|
run main
|
||||||
|
echo "$output"
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test 'files in \$src_dir are added to tar archive' {
|
||||||
|
run main
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
|
||||||
|
run tar tf "$dst_tarball"
|
||||||
|
[ "${status}" -eq 0 ]
|
||||||
|
[[ "${output}" =~ a ]]
|
||||||
|
[[ "${output}" =~ b ]]
|
||||||
|
[[ "${output}" =~ c ]]
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=source
|
||||||
|
set BUILDDIR=build
|
||||||
|
|
||||||
|
if "%1" == "" goto help
|
||||||
|
|
||||||
|
%SPHINXBUILD% >NUL 2>NUL
|
||||||
|
if errorlevel 9009 (
|
||||||
|
echo.
|
||||||
|
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||||
|
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||||
|
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||||
|
echo.may add the Sphinx directory to PATH.
|
||||||
|
echo.
|
||||||
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
|
echo.http://sphinx-doc.org/
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:help
|
||||||
|
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||||
|
|
||||||
|
:end
|
||||||
|
popd
|
@ -0,0 +1,127 @@
|
|||||||
|
# Releasing a new Bats version
|
||||||
|
|
||||||
|
These notes reflect the current process. There's a lot more we could do, in
|
||||||
|
terms of automation and expanding the number of platforms to which we formally
|
||||||
|
release (see #103).
|
||||||
|
|
||||||
|
## Update docs/CHANGELOG.md
|
||||||
|
|
||||||
|
Create a new entry at the top of `docs/CHANGELOG.md` that enumerates the
|
||||||
|
significant updates to the new version.
|
||||||
|
|
||||||
|
## Bumping the version number
|
||||||
|
|
||||||
|
Bump the version numbers in the following files:
|
||||||
|
|
||||||
|
- contrib/rpm/bats.spec
|
||||||
|
- libexec/bats-core/bats
|
||||||
|
- package.json
|
||||||
|
|
||||||
|
Commit these changes (including the `docs/CHANGELOG.md` changes) in a commit
|
||||||
|
with the message `Bats <VERSION>`, where `<VERSION>` is the new version number.
|
||||||
|
|
||||||
|
Create a new signed, annotated tag with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git tag -a -s <VERSION>
|
||||||
|
```
|
||||||
|
|
||||||
|
Include the `docs/CHANGELOG.md` notes corresponding to the new version as the
|
||||||
|
tag annotation, except the first line should be: `Bats <VERSION> - YYYY-MM-DD`
|
||||||
|
and any Markdown headings should become plain text, e.g.:
|
||||||
|
|
||||||
|
```md
|
||||||
|
### Added
|
||||||
|
```
|
||||||
|
|
||||||
|
should become:
|
||||||
|
|
||||||
|
```md
|
||||||
|
Added:
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a GitHub release
|
||||||
|
|
||||||
|
Push the new version commit and tag to GitHub via the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git push --follow-tags
|
||||||
|
```
|
||||||
|
|
||||||
|
Then visit https://github.com/bats-core/bats-core/releases, and:
|
||||||
|
|
||||||
|
* Click **Draft a new release**.
|
||||||
|
* Select the new version tag.
|
||||||
|
* Name the release: `Bats <VERSION>`.
|
||||||
|
* Paste the same notes from the version tag annotation as the description,
|
||||||
|
except change the first line to read: `Released: YYYY-MM-DD`.
|
||||||
|
* Click **Publish release**.
|
||||||
|
|
||||||
|
For more on `git push --follow-tags`, see:
|
||||||
|
|
||||||
|
* [git push --follow-tags in the online manual][ft-man]
|
||||||
|
* [Stack Overflow: How to push a tag to a remote repository using Git?][ft-so]
|
||||||
|
|
||||||
|
[ft-man]: https://git-scm.com/docs/git-push#git-push---follow-tags
|
||||||
|
[ft-so]: https://stackoverflow.com/a/26438076
|
||||||
|
|
||||||
|
## NPM
|
||||||
|
|
||||||
|
`npm publish`. Pretty easy!
|
||||||
|
|
||||||
|
For the paranoid, use `npm pack` and install the resulting tarball locally with
|
||||||
|
`npm install` before publishing.
|
||||||
|
|
||||||
|
## Homebrew
|
||||||
|
|
||||||
|
The basic instructions are in the [Submit a new version of an existing
|
||||||
|
formula][brew] section of the Homebrew docs.
|
||||||
|
|
||||||
|
[brew]: https://github.com/Homebrew/brew/blob/master/docs/How-To-Open-a-Homebrew-Pull-Request.md#submit-a-new-version-of-an-existing-formula
|
||||||
|
|
||||||
|
An example using v1.1.0 (notice that this uses the sha256 sum of the tarball):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -LOv https://github.com/bats-core/bats-core/archive/v1.1.0.tar.gz
|
||||||
|
$ openssl sha256 v1.1.0.tar.gz
|
||||||
|
SHA256(v1.1.0.tar.gz)=855d8b8bed466bc505e61123d12885500ef6fcdb317ace1b668087364717ea82
|
||||||
|
|
||||||
|
# Add the --dry-run flag to see the individual steps without executing.
|
||||||
|
$ brew bump-formula-pr \
|
||||||
|
--url=https://github.com/bats-core/bats-core/archive/v1.1.0.tar.gz \
|
||||||
|
--sha256=855d8b8bed466bc505e61123d12885500ef6fcdb317ace1b668087364717ea82
|
||||||
|
```
|
||||||
|
This resulted in https://github.com/Homebrew/homebrew-core/pull/29864, which was
|
||||||
|
automatically merged once the build passed.
|
||||||
|
|
||||||
|
## Alpine Linux
|
||||||
|
|
||||||
|
An example using v1.1.0 (notice that this uses the sha512 sum of the Zip file):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl -LOv https://github.com/bats-core/bats-core/archive/v1.1.0.zip
|
||||||
|
$ openssl sha512 v1.1.0.zip
|
||||||
|
SHA512(v1.1.0.zip)=accd83cfec0025a2be40982b3f9a314c2bbf72f5c85daffa9e9419611904a8d34e376919a5d53e378382e0f3794d2bd781046d810225e2a77812474e427bed9e
|
||||||
|
```
|
||||||
|
|
||||||
|
After cloning alpinelinux/aports, I used the above information to create:
|
||||||
|
https://github.com/alpinelinux/aports/pull/4696
|
||||||
|
|
||||||
|
**Note:** Currently users must enable the `edge` branch of the `community` repo
|
||||||
|
by adding/uncommenting the corresponding entry in `/etc/apk/repositories`.
|
||||||
|
|
||||||
|
## Announce
|
||||||
|
|
||||||
|
It's worth making a brief announcement like [the v1.1.0 announcement via
|
||||||
|
Gitter][gitter]:
|
||||||
|
|
||||||
|
[gitter]: https://gitter.im/bats-core/bats-core?at=5b42c9a57b811a6d63daacb5
|
||||||
|
|
||||||
|
```
|
||||||
|
v1.1.0 is now available via Homebrew and npm:
|
||||||
|
https://github.com/bats-core/bats-core/releases/tag/v1.1.0
|
||||||
|
|
||||||
|
It'll eventually be available in Alpine via the edge branch of the community
|
||||||
|
repo once alpinelinux/aports#4696 gets merged. (Check /etc/apk/repositories to
|
||||||
|
ensure this repo is enabled.)
|
||||||
|
```
|
@ -0,0 +1,72 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Path setup --------------------------------------------------------------
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
|
project = 'bats-core'
|
||||||
|
copyright = '2020, bats-core origanization'
|
||||||
|
author = 'bats-core origanization'
|
||||||
|
|
||||||
|
# The full version, including alpha/beta/rc tags
|
||||||
|
release = '1'
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = [
|
||||||
|
'recommonmark',
|
||||||
|
'sphinxcontrib.programoutput'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
html_sidebars = { '**': [
|
||||||
|
'about.html',
|
||||||
|
'navigation.html',
|
||||||
|
'relations.html',
|
||||||
|
'searchbox.html',
|
||||||
|
'donate.html'] }
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
|
exclude_patterns = []
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
#html_theme = 'alabaster'
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
#man_pages = [ ('man.1', 'bats', 'bats documentation', ['bats-core Contributors'], 1)]
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_config_value('recommonmark_config', {'enable_eval_rst': True}, True)
|
||||||
|
import recommonmark
|
||||||
|
from recommonmark.transform import AutoStructify
|
||||||
|
app.add_transform(AutoStructify)
|
@ -0,0 +1,14 @@
|
|||||||
|
Welcome to bats-core's documentation!
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Versions before v1.2.1 are documented over `there <https://github.com/bats-core/bats-core/blob/master/docs/versions.md>`_.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
installation
|
||||||
|
usage
|
||||||
|
docker-usage
|
||||||
|
writing-tests
|
||||||
|
|
@ -0,0 +1,152 @@
|
|||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
Supported Bash versions
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following is a list of Bash versions that are currently supported by Bats.
|
||||||
|
This list is composed of platforms that Bats has been tested on and is known to
|
||||||
|
work on without issues.
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
Bash versions:
|
||||||
|
|
||||||
|
|
||||||
|
* Everything from ``3.2.57(1)`` and higher (macOS's highest version)
|
||||||
|
|
||||||
|
*
|
||||||
|
Operating systems:
|
||||||
|
|
||||||
|
|
||||||
|
* Arch Linux
|
||||||
|
* Alpine Linux
|
||||||
|
* Ubuntu Linux
|
||||||
|
* FreeBSD ``10.x`` and ``11.x``
|
||||||
|
* macOS
|
||||||
|
* Windows 10
|
||||||
|
|
||||||
|
*
|
||||||
|
Latest version for the following Windows platforms:
|
||||||
|
|
||||||
|
|
||||||
|
* Git for Windows Bash (MSYS2 based)
|
||||||
|
* Windows Subsystem for Linux
|
||||||
|
* MSYS2
|
||||||
|
* Cygwin
|
||||||
|
|
||||||
|
Homebrew
|
||||||
|
^^^^^^^^
|
||||||
|
|
||||||
|
On macOS, you can install `Homebrew <https://brew.sh/>`_ if you haven't already,
|
||||||
|
then run:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ brew install bats-core
|
||||||
|
|
||||||
|
npm
|
||||||
|
^^^
|
||||||
|
|
||||||
|
You can install the `Bats npm package <https://www.npmjs.com/package/bats>`_ via:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
# To install globally:
|
||||||
|
$ npm install -g bats
|
||||||
|
|
||||||
|
# To install into your project and save it as one of the "devDependencies" in
|
||||||
|
# your package.json:
|
||||||
|
$ npm install --save-dev bats
|
||||||
|
|
||||||
|
Installing Bats from source
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Check out a copy of the Bats repository. Then, either add the Bats ``bin``
|
||||||
|
directory to your ``$PATH``\ , or run the provided ``install.sh`` command with the
|
||||||
|
location to the prefix in which you want to install Bats. For example, to
|
||||||
|
install Bats into ``/usr/local``\ ,
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ git clone https://github.com/bats-core/bats-core.git
|
||||||
|
$ cd bats-core
|
||||||
|
$ ./install.sh /usr/local
|
||||||
|
|
||||||
|
|
||||||
|
**Note:** You may need to run ``install.sh`` with ``sudo`` if you do not have
|
||||||
|
permission to write to the installation prefix.
|
||||||
|
|
||||||
|
Installing Bats from source onto Windows Git Bash
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Check out a copy of the Bats repository and install it to ``$HOME``. This
|
||||||
|
will place the ``bats`` executable in ``$HOME/bin``\ , which should already be
|
||||||
|
in ``$PATH``.
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ git clone https://github.com/bats-core/bats-core.git
|
||||||
|
$ cd bats-core
|
||||||
|
$ ./install.sh $HOME
|
||||||
|
|
||||||
|
|
||||||
|
Running Bats in Docker
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
There is an official image on the Docker Hub:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ docker run -it bats/bats:latest --version
|
||||||
|
|
||||||
|
|
||||||
|
Building a Docker image
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Check out a copy of the Bats repository, then build a container image:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ git clone https://github.com/bats-core/bats-core.git
|
||||||
|
$ cd bats-core
|
||||||
|
$ docker build --tag bats/bats:latest .
|
||||||
|
|
||||||
|
|
||||||
|
This creates a local Docker image called ``bats/bats:latest`` based on `Alpine
|
||||||
|
Linux <https://github.com/gliderlabs/docker-alpine/blob/master/docs/usage.md>`_
|
||||||
|
(to push to private registries, tag it with another organisation, e.g.
|
||||||
|
``my-org/bats:latest``\ ).
|
||||||
|
|
||||||
|
To run Bats' internal test suite (which is in the container image at
|
||||||
|
``/opt/bats/test``\ ):
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ docker run -it bats/bats:latest /opt/bats/test
|
||||||
|
|
||||||
|
|
||||||
|
To run a test suite from a directory called ``test`` in the current directory of
|
||||||
|
your local machine, mount in a volume and direct Bats to its path inside the
|
||||||
|
container:
|
||||||
|
|
||||||
|
.. code-block::
|
||||||
|
|
||||||
|
$ docker run -it -v "${PWD}:/code" bats/bats:latest test
|
||||||
|
|
||||||
|
|
||||||
|
..
|
||||||
|
|
||||||
|
``/code`` is the working directory of the Docker image. "${PWD}/test" is the
|
||||||
|
location of the test directory on the local machine.
|
||||||
|
|
||||||
|
|
||||||
|
This is a minimal Docker image. If more tools are required this can be used as a
|
||||||
|
base image in a Dockerfile using ``FROM <Docker image>``. In the future there may
|
||||||
|
be images based on Debian, and/or with more tools installed (\ ``curl`` and ``openssl``\ ,
|
||||||
|
for example). If you require a specific configuration please search and +1 an
|
||||||
|
issue or `raise a new issue <https://github.com/bats-core/bats-core/issues>`_.
|
||||||
|
|
||||||
|
Further usage examples are in
|
||||||
|
`the wiki <https://github.com/bats-core/bats-core/wiki/Docker-Usage-Examples>`_.
|
@ -0,0 +1,2 @@
|
|||||||
|
sphinxcontrib-programoutput
|
||||||
|
recommonmark
|
@ -0,0 +1,94 @@
|
|||||||
|
# Usage
|
||||||
|
|
||||||
|
Bats comes with two manual pages. After installation you can view them with `man
|
||||||
|
1 bats` (usage manual) and `man 7 bats` (writing test files manual). Also, you
|
||||||
|
can view the available command line options that Bats supports by calling Bats
|
||||||
|
with the `-h` or `--help` options. These are the options that Bats currently
|
||||||
|
supports:
|
||||||
|
|
||||||
|
``` eval_rst
|
||||||
|
.. program-output:: ../../bin/bats --help
|
||||||
|
```
|
||||||
|
|
||||||
|
To run your tests, invoke the `bats` interpreter with one or more paths to test
|
||||||
|
files ending with the `.bats` extension, or paths to directories containing test
|
||||||
|
files. (`bats` will only execute `.bats` files at the top level of each
|
||||||
|
directory; it will not recurse unless you specify the `-r` flag.)
|
||||||
|
|
||||||
|
Test cases from each file are run sequentially and in isolation. If all the test
|
||||||
|
cases pass, `bats` exits with a `0` status code. If there are any failures,
|
||||||
|
`bats` exits with a `1` status code.
|
||||||
|
|
||||||
|
When you run Bats from a terminal, you'll see output as each test is performed,
|
||||||
|
with a check-mark next to the test's name if it passes or an "X" if it fails.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ bats addition.bats
|
||||||
|
✓ addition using bc
|
||||||
|
✓ addition using dc
|
||||||
|
|
||||||
|
2 tests, 0 failures
|
||||||
|
```
|
||||||
|
|
||||||
|
If Bats is not connected to a terminal—in other words, if you run it from a
|
||||||
|
continuous integration system, or redirect its output to a file—the results are
|
||||||
|
displayed in human-readable, machine-parsable [TAP format][TAP].
|
||||||
|
|
||||||
|
You can force TAP output from a terminal by invoking Bats with the `--formatter tap`
|
||||||
|
option.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ bats --formatter tap addition.bats
|
||||||
|
1..2
|
||||||
|
ok 1 addition using bc
|
||||||
|
ok 2 addition using dc
|
||||||
|
```
|
||||||
|
|
||||||
|
With `--formatter junit`, it is possible
|
||||||
|
to output junit-compatible report files.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ bats --formatter junit addition.bats
|
||||||
|
1..2
|
||||||
|
ok 1 addition using bc
|
||||||
|
ok 2 addition using dc
|
||||||
|
```
|
||||||
|
|
||||||
|
Test reports will be output in the executing directory, but may be placed elsewhere
|
||||||
|
by specifying the `--output` flag.
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ bats --formatter junit addition.bats --output /tmp
|
||||||
|
1..2
|
||||||
|
ok 1 addition using bc
|
||||||
|
ok 2 addition using dc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parallel Execution
|
||||||
|
|
||||||
|
``` eval_rst
|
||||||
|
.. versionadded:: 1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, Bats will execute your tests serially. However, Bats supports
|
||||||
|
parallel execution of tests (provided you have [GNU parallel][gnu-parallel] or
|
||||||
|
a compatible replacement installed) using the `--jobs` parameter. This can
|
||||||
|
result in your tests completing faster (depending on your tests and the testing
|
||||||
|
hardware).
|
||||||
|
|
||||||
|
Ordering of parallised tests is not guaranteed, so this mode may break suites
|
||||||
|
with dependencies between tests (or tests that write to shared locations). When
|
||||||
|
enabling `--jobs` for the first time be sure to re-run bats multiple times to
|
||||||
|
identify any inter-test dependencies or non-deterministic test behaviour.
|
||||||
|
|
||||||
|
When parallelizing, the results of a file only become visible after it has been finished.
|
||||||
|
You can use `--no-parallelize-across-files` to get immediate output at the cost of reduced
|
||||||
|
overall parallelity, as parallelization will only happen within files and files will be run
|
||||||
|
sequentially.
|
||||||
|
|
||||||
|
If you have files where tests within the file would interfere with each other, you can use
|
||||||
|
`--no-parallelize-within-files` to disable parallelization within all files.
|
||||||
|
If you want more finegrained control, you can `export BATS_NO_PARALLELIZE_WITHIN_FILE=true` in `setup_file()`
|
||||||
|
or outside any function to disable parallelization only within the containing file.
|
||||||
|
|
||||||
|
[gnu-parallel]: https://www.gnu.org/software/parallel/
|
@ -0,0 +1,323 @@
|
|||||||
|
# Writing tests
|
||||||
|
|
||||||
|
Each Bats test file is evaluated _n+1_ times, where _n_ is the number of
|
||||||
|
test cases in the file. The first run counts the number of test cases,
|
||||||
|
then iterates over the test cases and executes each one in its own
|
||||||
|
process.
|
||||||
|
|
||||||
|
For more details about how Bats evaluates test files, see [Bats Evaluation
|
||||||
|
Process][bats-eval] on the wiki.
|
||||||
|
|
||||||
|
For sample test files, see [examples](https://github.com/bats-core/bats-core/tree/master/docs/examples).
|
||||||
|
|
||||||
|
[bats-eval]: https://github.com/bats-core/bats-core/wiki/Bats-Evaluation-Process
|
||||||
|
|
||||||
|
## `run`: Test other commands
|
||||||
|
|
||||||
|
Many Bats tests need to run a command and then make assertions about its exit
|
||||||
|
status and output. Bats includes a `run` helper that invokes its arguments as a
|
||||||
|
command, saves the exit status and output into special global variables, and
|
||||||
|
then returns with a `0` status code so you can continue to make assertions in
|
||||||
|
your test case.
|
||||||
|
|
||||||
|
For example, let's say you're testing that the `foo` command, when passed a
|
||||||
|
nonexistent filename, exits with a `1` status code and prints an error message.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
@test "invoking foo with a nonexistent file prints an error" {
|
||||||
|
run foo nonexistent_filename
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[ "$output" = "foo: no such file 'nonexistent_filename'" ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `$status` variable contains the status code of the command, and the
|
||||||
|
`$output` variable contains the combined contents of the command's standard
|
||||||
|
output and standard error streams.
|
||||||
|
|
||||||
|
A third special variable, the `$lines` array, is available for easily accessing
|
||||||
|
individual lines of output. For example, if you want to test that invoking `foo`
|
||||||
|
without any arguments prints usage information on the first line:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
@test "invoking foo without arguments prints usage" {
|
||||||
|
run foo
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[ "${lines[0]}" = "usage: foo <filename>" ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
__Note:__ The `run` helper executes its argument(s) in a subshell, so if
|
||||||
|
writing tests against environmental side-effects like a variable's value
|
||||||
|
being changed, these changes will not persist after `run` completes.
|
||||||
|
|
||||||
|
### When not to use `run`
|
||||||
|
|
||||||
|
In some cases, using `run` is redundant and results in a longer and less readable code.
|
||||||
|
Here are a few examples.
|
||||||
|
|
||||||
|
#### 1. In case you only need to check the command succeeded, it is better to not use run, since
|
||||||
|
|
||||||
|
```bash
|
||||||
|
run command args ...
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
is equivalent to
|
||||||
|
|
||||||
|
```bash
|
||||||
|
command args ...
|
||||||
|
```
|
||||||
|
|
||||||
|
since bats sets `set -e` for all tests.
|
||||||
|
|
||||||
|
#### 2. In case you want to hide the command output (which `run` does), use output redirection instead
|
||||||
|
|
||||||
|
This
|
||||||
|
|
||||||
|
```bash
|
||||||
|
run command ...
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
is equivalent to
|
||||||
|
|
||||||
|
```bash
|
||||||
|
command ... >/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the output is only shown if the test case fails.
|
||||||
|
|
||||||
|
#### 3. In case you need to assign command output to a variable (and maybe check the command exit status), it is better to not use run, since
|
||||||
|
|
||||||
|
```bash
|
||||||
|
run command args ...
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
var="$output"
|
||||||
|
```
|
||||||
|
|
||||||
|
is equivalent to
|
||||||
|
|
||||||
|
```bash
|
||||||
|
var=$(command args ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Comment syntax
|
||||||
|
|
||||||
|
External tools (like `shellcheck`, `shfmt`, and various IDE's) may not support
|
||||||
|
the standard `.bats` syntax. Because of this, we provide a valid `bash`
|
||||||
|
alterntative:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
function invoking_foo_without_arguments_prints_usage { #@test
|
||||||
|
run foo
|
||||||
|
[ "$status" -eq 1 ]
|
||||||
|
[ "${lines[0]}" = "usage: foo <filename>" ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When using this syntax, the function name will be the title in the result output
|
||||||
|
and the value checked when using `--filter`.
|
||||||
|
|
||||||
|
### `load`: Share common code
|
||||||
|
|
||||||
|
You may want to share common code across multiple test files. Bats includes a
|
||||||
|
convenient `load` command for sourcing a Bash source file relative to the
|
||||||
|
location of the current test file. For example, if you have a Bats test in
|
||||||
|
`test/foo.bats`, the command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
load test_helper.bash
|
||||||
|
```
|
||||||
|
|
||||||
|
will source the script `test/test_helper.bash` in your test file (limitations
|
||||||
|
apply, see below). This can be useful for sharing functions to set up your
|
||||||
|
environment or load fixtures. `load` delegates to Bash's `source` command after
|
||||||
|
resolving relative paths.
|
||||||
|
|
||||||
|
As pointed out by @iatrou in <https://www.tldp.org/LDP/abs/html/declareref.html>,
|
||||||
|
using the `declare` builtin restricts scope of a variable. Thus, since actual
|
||||||
|
`source`-ing is performed in context of the `load` function, `declare`d symbols
|
||||||
|
will _not_ be made available to callers of `load`.
|
||||||
|
|
||||||
|
> For backwards compatibility `load` first searches for a file ending in
|
||||||
|
> `.bash` (e.g. `load test_helper` searches for `test_helper.bash` before
|
||||||
|
> it looks for `test_helper`). This behaviour is deprecated and subject to
|
||||||
|
> change, please use exact filenames instead.
|
||||||
|
|
||||||
|
### `skip`: Easily skip tests
|
||||||
|
|
||||||
|
Tests can be skipped by using the `skip` command at the point in a test you wish
|
||||||
|
to skip.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
@test "A test I don't want to execute for now" {
|
||||||
|
skip
|
||||||
|
run foo
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Optionally, you may include a reason for skipping:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
@test "A test I don't want to execute for now" {
|
||||||
|
skip "This command will return zero soon, but not now"
|
||||||
|
run foo
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can skip conditionally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
@test "A test which should run" {
|
||||||
|
if [ foo != bar ]; then
|
||||||
|
skip "foo isn't bar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run foo
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
__Note:__ `setup` and `teardown` hooks still run for skipped tests.
|
||||||
|
|
||||||
|
### `setup` and `teardown`: Pre- and post-test hooks
|
||||||
|
|
||||||
|
You can define special `setup` and `teardown` functions, which run before and
|
||||||
|
after each test case, respectively. Use these to load fixtures, set up your
|
||||||
|
environment, and clean up when you're done.
|
||||||
|
|
||||||
|
You can also define `setup_file` and `teardown_file`, which will run once before the first test's `setup` and after the last test's `teardown` for the containing file. Variables that are exported in `setup_file` will be visible to all following functions (`setup`, the test itself, `teardown`, `teardown_file`).
|
||||||
|
|
||||||
|
<!-- markdownlint-disable MD033 -->
|
||||||
|
<details>
|
||||||
|
<summary>Example of setup/setup_file/teardown/teardown_file call order</summary>
|
||||||
|
For example the following call order would result from two files (file 1 with tests 1 and 2, and file 2 with test3) beeing tested:
|
||||||
|
|
||||||
|
```text
|
||||||
|
setup_file # from file 1, on entering file 1
|
||||||
|
setup
|
||||||
|
test1
|
||||||
|
teardown
|
||||||
|
setup
|
||||||
|
test2
|
||||||
|
teardown
|
||||||
|
teardown_file # from file 1, on leaving file 1
|
||||||
|
setup_file # from file 2, on enter file 2
|
||||||
|
setup
|
||||||
|
test3
|
||||||
|
teardown
|
||||||
|
teardown_file # from file 2, on leaving file 2
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<!-- markdownlint-enable MD033 -->
|
||||||
|
|
||||||
|
### Code outside of test cases
|
||||||
|
|
||||||
|
You can include code in your test file outside of `@test` functions. For
|
||||||
|
example, this may be useful if you want to check for dependencies and fail
|
||||||
|
immediately if they're not present. However, any output that you print in code
|
||||||
|
outside of `@test`, `setup` or `teardown` functions must be redirected to
|
||||||
|
`stderr` (`>&2`). Otherwise, the output may cause Bats to fail by polluting the
|
||||||
|
TAP stream on `stdout`.
|
||||||
|
|
||||||
|
### File descriptor 3 (read this if Bats hangs)
|
||||||
|
|
||||||
|
Bats makes a separation between output from the code under test and output that
|
||||||
|
forms the TAP stream (which is produced by Bats internals). This is done in
|
||||||
|
order to produce TAP-compliant output. In the [Printing to the
|
||||||
|
terminal](#printing-to-the-terminal) section, there are details on how to use
|
||||||
|
file descriptor 3 to print custom text properly.
|
||||||
|
|
||||||
|
A side effect of using file descriptor 3 is that, under some circumstances, it
|
||||||
|
can cause Bats to block and execution to seem dead without reason. This can
|
||||||
|
happen if a child process is spawned in the background from a test. In this
|
||||||
|
case, the child process will inherit file descriptor 3. Bats, as the parent
|
||||||
|
process, will wait for the file descriptor to be closed by the child process
|
||||||
|
before continuing execution. If the child process takes a lot of time to
|
||||||
|
complete (eg if the child process is a `sleep 100` command or a background
|
||||||
|
service that will run indefinitely), Bats will be similarly blocked for the same
|
||||||
|
amount of time.
|
||||||
|
|
||||||
|
**To prevent this from happening, close FD 3 explicitly when running any command
|
||||||
|
that may launch long-running child processes**, e.g. `command_name 3>&-` .
|
||||||
|
|
||||||
|
### Printing to the terminal
|
||||||
|
|
||||||
|
Bats produces output compliant with [version 12 of the TAP protocol][TAP]. The
|
||||||
|
produced TAP stream is by default piped to a pretty formatter for human
|
||||||
|
consumption, but if Bats is called with the `-t` flag, then the TAP stream is
|
||||||
|
directly printed to the console.
|
||||||
|
|
||||||
|
This has implications if you try to print custom text to the terminal. As
|
||||||
|
mentioned in [File descriptor 3](#file-descriptor-3-read-this-if-bats-hangs),
|
||||||
|
bats provides a special file descriptor, `&3`, that you should use to print
|
||||||
|
your custom text. Here are some detailed guidelines to refer to:
|
||||||
|
|
||||||
|
- Printing **from within a test function**:
|
||||||
|
- To have text printed from within a test function you need to redirect the
|
||||||
|
output to file descriptor 3, eg `echo 'text' >&3`. This output will become
|
||||||
|
part of the TAP stream. You are encouraged to prepend text printed this way
|
||||||
|
with a hash (eg `echo '# text' >&3`) in order to produce 100% TAP compliant
|
||||||
|
output. Otherwise, depending on the 3rd-party tools you use to analyze the
|
||||||
|
TAP stream, you can encounter unexpected behavior or errors.
|
||||||
|
|
||||||
|
- The pretty formatter that Bats uses by default to process the TAP stream
|
||||||
|
will filter out and not print text output to file descriptor 3.
|
||||||
|
|
||||||
|
- Text that is output directly to stdout or stderr (file descriptor 1 or 2),
|
||||||
|
ie `echo 'text'` is considered part of the test function output and is
|
||||||
|
printed only on test failures for diagnostic purposes, regardless of the
|
||||||
|
formatter used (TAP or pretty).
|
||||||
|
|
||||||
|
- Printing **from within the `setup` or `teardown` functions**: The same hold
|
||||||
|
true as for printing with test functions.
|
||||||
|
|
||||||
|
- Printing **outside test or `setup`/`teardown` functions**:
|
||||||
|
- Regardless of where text is redirected to (stdout, stderr or file descriptor
|
||||||
|
3) text is immediately visible in the terminal.
|
||||||
|
|
||||||
|
- Text printed in such a way, will disable pretty formatting. Also, it will
|
||||||
|
make output non-compliant with the TAP spec. The reason for this is that
|
||||||
|
each test file is evaluated n+1 times (as mentioned
|
||||||
|
[earlier](#writing-tests)). The first run will cause such output to be
|
||||||
|
produced before the [_plan line_][tap-plan] is printed, contrary to the spec
|
||||||
|
that requires the _plan line_ to be either the first or the last line of the
|
||||||
|
output.
|
||||||
|
|
||||||
|
- Due to internal pipes/redirects, output to stderr is always printed first.
|
||||||
|
|
||||||
|
[tap-plan]: https://testanything.org/tap-specification.html#the-plan
|
||||||
|
|
||||||
|
### Special variables
|
||||||
|
|
||||||
|
There are several global variables you can use to introspect on Bats tests:
|
||||||
|
|
||||||
|
- `$BATS_TEST_FILENAME` is the fully expanded path to the Bats test file.
|
||||||
|
- `$BATS_TEST_DIRNAME` is the directory in which the Bats test file is located.
|
||||||
|
- `$BATS_TEST_NAMES` is an array of function names for each test case.
|
||||||
|
- `$BATS_TEST_NAME` is the name of the function containing the current test case.
|
||||||
|
- `$BATS_TEST_DESCRIPTION` is the description of the current test case.
|
||||||
|
- `$BATS_TEST_NUMBER` is the (1-based) index of the current test case in the test file.
|
||||||
|
- `$BATS_SUITE_TEST_NUMBER` is the (1-based) index of the current test case in the test suite (over all files).
|
||||||
|
- `$BATS_TMPDIR` is the location to a directory that may be used to store temporary files.
|
||||||
|
- `$BATS_FILE_EXTENSION` (default: `bats`) specifies the extension of test files that should be found when running a suite (via `bats [-r] suite_folder/`)
|
||||||
|
|
||||||
|
### Libraries and Add-ons
|
||||||
|
|
||||||
|
Bats supports loading external assertion libraries and helpers. Those under `bats-core` are officially supported libraries (integration tests welcome!):
|
||||||
|
|
||||||
|
- <https://github.com/bats-core/bats-assert> - common assertions for Bats
|
||||||
|
- <https://github.com/bats-core/bats-support> - supporting library for Bats test helpers
|
||||||
|
- <https://github.com/bats-core/bats-file> - common filesystem assertions for Bats
|
||||||
|
- <https://github.com/bats-core/bats-detik> - e2e tests of applications in K8s environments
|
||||||
|
|
||||||
|
and some external libraries, supported on a "best-effort" basis:
|
||||||
|
|
||||||
|
- <https://github.com/ztombol/bats-docs> (still relevant? Requires review)
|
||||||
|
- <https://github.com/grayhemp/bats-mock> (as per #147)
|
||||||
|
- <https://github.com/jasonkarns/bats-mock> (how is this different from grayhemp/bats-mock?)
|
@ -0,0 +1,9 @@
|
|||||||
|
Here are the docs of following versions:
|
||||||
|
|
||||||
|
* [v1.2.0](../../v1.2.0/README.md)
|
||||||
|
* [v1.1.0](../../v1.1.0/README.md)
|
||||||
|
* [v1.0.2](../../v1.0.2/README.md)
|
||||||
|
* [v0.4.0](../../v0.4.0/README.md)
|
||||||
|
* [v0.3.1](../../v0.3.1/README.md)
|
||||||
|
* [v0.2.0](../../v0.2.0/README.md)
|
||||||
|
* [v0.1.0](../../v0.1.0/README.md)
|
@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# reads (extended) bats tap streams from stdin and calls callback functions for each line
|
||||||
|
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
|
||||||
|
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
|
||||||
|
# bats_tap_stream_ok [--duration <milliseconds] <test index> <test name> -> when a test was successful
|
||||||
|
# bats_tap_stream_not_ok [--duration <milliseconds>] <test index> <test name> -> when a test has failed
|
||||||
|
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
|
||||||
|
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
|
||||||
|
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
|
||||||
|
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
|
||||||
|
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
|
||||||
|
# scope @see bats_tap_stream_comment
|
||||||
|
# forwards all input as is, when there is no TAP test plan header
|
||||||
|
function bats_parse_internal_extended_tap() {
|
||||||
|
local header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
bats_tap_stream_plan "${header:3}"
|
||||||
|
else
|
||||||
|
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||||
|
printf '%s\n' "$header"
|
||||||
|
exec cat
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok_line_regexpr="ok ([0-9]+) (.*)"
|
||||||
|
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
|
||||||
|
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
|
||||||
|
|
||||||
|
timing_expr="in ([0-9]+)ms$"
|
||||||
|
local test_name begin_index ok_index not_ok_index index scope
|
||||||
|
begin_index=0
|
||||||
|
index=0
|
||||||
|
scope=plan
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'begin '*) # this might only be called in extended tap output
|
||||||
|
((++begin_index))
|
||||||
|
scope=begin
|
||||||
|
test_name="${line#* $begin_index }"
|
||||||
|
bats_tap_stream_begin "$begin_index" "$test_name"
|
||||||
|
;;
|
||||||
|
'ok '*)
|
||||||
|
((++index))
|
||||||
|
scope=ok
|
||||||
|
if [[ "$line" =~ $ok_line_regexpr ]]; then
|
||||||
|
ok_index="${BASH_REMATCH[1]}"
|
||||||
|
test_name="${BASH_REMATCH[2]}"
|
||||||
|
if [[ "$line" =~ $skip_line_regexpr ]]; then
|
||||||
|
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
|
||||||
|
local skip_reason="${BASH_REMATCH[4]}"
|
||||||
|
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
|
||||||
|
else
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
|
||||||
|
else
|
||||||
|
bats_tap_stream_ok "$ok_index" "$test_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "ERROR: could not match ok line: %s" "$line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'not ok '*)
|
||||||
|
((++index))
|
||||||
|
scope=not_ok
|
||||||
|
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
|
||||||
|
not_ok_index="${BASH_REMATCH[1]}"
|
||||||
|
test_name="${BASH_REMATCH[2]}"
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
bats_tap_stream_not_ok --duration "${BASH_REMATCH[1]}" "$not_ok_index" "$test_name"
|
||||||
|
else
|
||||||
|
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "ERROR: could not match not ok line: %s" "$line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'# '*)
|
||||||
|
bats_tap_stream_comment "${line:2}" "$scope"
|
||||||
|
;;
|
||||||
|
'suite '*)
|
||||||
|
scope=suite
|
||||||
|
# pass on the
|
||||||
|
bats_tap_stream_suite "${line:6}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
bats_tap_stream_unknown "$line" "$scope"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# given a prefix and a path, remove the prefix if the path starts with it
|
||||||
|
# e.g.
|
||||||
|
# remove_prefix /usr/bin /usr/bin/bash -> bash
|
||||||
|
# remove_prefix /usr /usr/lib/bash -> lib/bash
|
||||||
|
# remove_prefix /usr/bin /usr/local/bin/bash -> /usr/local/bin/bash
|
||||||
|
remove_prefix() {
|
||||||
|
base_path="$1"
|
||||||
|
path="$2"
|
||||||
|
if [[ "$path" == "$base_path"* ]]; then
|
||||||
|
# cut off the common prefix
|
||||||
|
printf "%s" "${path:${#base_path}}"
|
||||||
|
else
|
||||||
|
printf "%s" "$path"
|
||||||
|
fi
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ -z "${TMPDIR:-}" ]]; then
|
||||||
|
export BATS_TMPDIR='/tmp'
|
||||||
|
else
|
||||||
|
export BATS_TMPDIR="${TMPDIR%/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_TMPNAME="$BATS_RUN_TMPDIR/bats.$$"
|
||||||
|
BATS_PARENT_TMPNAME="$BATS_RUN_TMPDIR/bats.$PPID"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_OUT="${BATS_TMPNAME}.out" # used in bats-exec-file
|
||||||
|
|
||||||
|
bats_preprocess_source() {
|
||||||
|
# export to make it visible to bats_evaluate_preprocessed_source
|
||||||
|
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
|
||||||
|
export BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
|
||||||
|
bats-preprocess "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_evaluate_preprocessed_source() {
|
||||||
|
if [[ -z "${BATS_TEST_SOURCE:-}" ]]; then
|
||||||
|
BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src"
|
||||||
|
fi
|
||||||
|
# Dynamically loaded user files provided outside of Bats.
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$BATS_TEST_SOURCE"
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# $1 - output directory for stdout/stderr
|
||||||
|
# $@ - command to run
|
||||||
|
# run the given command in a semaphore
|
||||||
|
# block when there is no free slot for the semaphore
|
||||||
|
# when there is a free slot, run the command in background
|
||||||
|
# gather the output of the command in files in the given directory
|
||||||
|
bats_semaphore_run() {
|
||||||
|
local output_dir=$1
|
||||||
|
shift
|
||||||
|
local semaphore_slot
|
||||||
|
semaphore_slot=$(bats_semaphore_acquire_slot)
|
||||||
|
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
|
||||||
|
printf "%d\n" "$!"
|
||||||
|
}
|
||||||
|
|
||||||
|
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
|
||||||
|
|
||||||
|
# $1 - output directory for stdout/stderr
|
||||||
|
# $@ - command to run
|
||||||
|
# this wraps the actual function call to install some traps on exiting
|
||||||
|
bats_semaphore_release_wrapper() {
|
||||||
|
local output_dir="$1"
|
||||||
|
local semaphore_name="$2"
|
||||||
|
shift 2 # all other parameters will be use for the command to execute
|
||||||
|
|
||||||
|
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
|
||||||
|
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
|
||||||
|
|
||||||
|
mkdir -p "$output_dir"
|
||||||
|
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
|
||||||
|
local status=$?
|
||||||
|
|
||||||
|
# bash bug: the exit trap is not called for the background process
|
||||||
|
bats_semaphore_release_slot "$semaphore_name"
|
||||||
|
trap - EXIT # avoid calling release twice
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_acquire_while_locked() {
|
||||||
|
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||||
|
local slot=0
|
||||||
|
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
|
||||||
|
(( ++slot ))
|
||||||
|
done
|
||||||
|
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
|
||||||
|
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f bats_semaphore_acquire_while_locked
|
||||||
|
|
||||||
|
if command -v flock >/dev/null; then
|
||||||
|
bats_run_under_lock() {
|
||||||
|
flock "$BATS_SEMAPHORE_DIR" "$@"
|
||||||
|
}
|
||||||
|
elif command -v shlock >/dev/null; then
|
||||||
|
bats_run_under_lock() {
|
||||||
|
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
|
||||||
|
while ! shlock -p $$ -f "$lockfile"; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# we got the lock now, execute the command
|
||||||
|
"$@"
|
||||||
|
# free the lock
|
||||||
|
rm -f "$lockfile"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# block until a semaphore slot becomes free
|
||||||
|
# prints the number of the slot that it received
|
||||||
|
bats_semaphore_acquire_slot() {
|
||||||
|
mkdir -p "$BATS_SEMAPHORE_DIR"
|
||||||
|
# wait for a slot to become free
|
||||||
|
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
|
||||||
|
while true; do
|
||||||
|
# don't lock for reading, we are fine with spuriously getting no free slot
|
||||||
|
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||||
|
bats_run_under_lock bash -c bats_semaphore_acquire_while_locked && break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_release_slot() {
|
||||||
|
# we don't need to lock this, since only our process owns this file
|
||||||
|
# and freeing a semaphore cannot lead to conflicts with others
|
||||||
|
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not aqcuired a semaphore!
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_get_free_slot_count() {
|
||||||
|
# find might error out without returning something useful when a file is deleted,
|
||||||
|
# while the directory is traversed -> only continue when there was no error
|
||||||
|
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
|
||||||
|
echo $(( BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots ))
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f bats_semaphore_get_free_slot_count
|
@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
|
||||||
|
BATS_TEST_NAMES=()
|
||||||
|
|
||||||
|
# Shorthand for source-ing files relative to the BATS_TEST_DIRNAME,
|
||||||
|
# optionally with a .bash suffix appended. If the argument doesn't
|
||||||
|
# resolve relative to BATS_TEST_DIRNAME it is sourced as-is.
|
||||||
|
load() {
|
||||||
|
local file="${1:?}"
|
||||||
|
|
||||||
|
# For backwards-compatibility first look for a .bash-suffixed file.
|
||||||
|
# TODO consider flipping the order here; it would be more consistent
|
||||||
|
# and less surprising to look for an exact-match first.
|
||||||
|
if [[ -f "${BATS_TEST_DIRNAME}/${file}.bash" ]]; then
|
||||||
|
file="${BATS_TEST_DIRNAME}/${file}.bash"
|
||||||
|
elif [[ -f "${BATS_TEST_DIRNAME}/${file}" ]]; then
|
||||||
|
file="${BATS_TEST_DIRNAME}/${file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$file" ]] && ! type -P "$file" >/dev/null; then
|
||||||
|
printf 'bats: %s does not exist\n' "$file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dynamically loaded user file provided outside of Bats.
|
||||||
|
# Note: 'source "$file" || exit' doesn't work on bash3.2.
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "${file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
local origFlags="$-"
|
||||||
|
set +eET
|
||||||
|
local origIFS="$IFS"
|
||||||
|
# 'output', 'status', 'lines' are global variables available to tests.
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
output="$("$@" 2>&1)"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
status="$?"
|
||||||
|
# shellcheck disable=SC2034,SC2206
|
||||||
|
IFS=$'\n' lines=($output)
|
||||||
|
IFS="$origIFS"
|
||||||
|
set "-$origFlags"
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
# if this is a skip in teardown ...
|
||||||
|
if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
|
||||||
|
# ... we want to skip the rest of teardown.
|
||||||
|
# communicate to bats_exit_trap that the teardown was completed without error
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEARDOWN_COMPLETED=1
|
||||||
|
# if we are already in the exit trap (e.g. due to previous skip) ...
|
||||||
|
if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
|
||||||
|
# ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
|
||||||
|
bats_exit_trap
|
||||||
|
# and then do the exit (at the end of this function)
|
||||||
|
fi
|
||||||
|
# if we aren't in exit trap, the normal exit handling should suffice
|
||||||
|
else
|
||||||
|
# ... this is either skip in test or skip in setup.
|
||||||
|
# Following variables are used in bats-exec-test which sources this file
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEST_SKIPPED="${1:-1}"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEST_COMPLETED=1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_test_begin() {
|
||||||
|
BATS_TEST_DESCRIPTION="$1"
|
||||||
|
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
|
||||||
|
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "$BATS_TEST_DESCRIPTION" >&3
|
||||||
|
fi
|
||||||
|
setup
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_test_function() {
|
||||||
|
local test_name="$1"
|
||||||
|
BATS_TEST_NAMES+=("$test_name")
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
bats_capture_stack_trace() {
|
||||||
|
local test_file
|
||||||
|
local funcname
|
||||||
|
local i
|
||||||
|
|
||||||
|
BATS_STACK_TRACE=()
|
||||||
|
|
||||||
|
for ((i = 2; i != ${#FUNCNAME[@]}; ++i)); do
|
||||||
|
# Use BATS_TEST_SOURCE if necessary to work around Bash < 4.4 bug whereby
|
||||||
|
# calling an exported function erases the test file's BASH_SOURCE entry.
|
||||||
|
test_file="${BASH_SOURCE[$i]:-$BATS_TEST_SOURCE}"
|
||||||
|
funcname="${FUNCNAME[$i]}"
|
||||||
|
BATS_STACK_TRACE+=("${BASH_LINENO[$((i - 1))]} $funcname $test_file")
|
||||||
|
if [[ "$test_file" == "$BATS_TEST_SOURCE" ]]; then
|
||||||
|
case "$funcname" in
|
||||||
|
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_print_stack_trace() {
|
||||||
|
local frame
|
||||||
|
local index=1
|
||||||
|
local count="${#@}"
|
||||||
|
local filename
|
||||||
|
local lineno
|
||||||
|
|
||||||
|
for frame in "$@"; do
|
||||||
|
bats_frame_filename "$frame" 'filename'
|
||||||
|
bats_trim_filename "$filename" 'filename'
|
||||||
|
bats_frame_lineno "$frame" 'lineno'
|
||||||
|
|
||||||
|
if [[ $index -eq 1 ]]; then
|
||||||
|
printf '# ('
|
||||||
|
else
|
||||||
|
printf '# '
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fn
|
||||||
|
bats_frame_function "$frame" 'fn'
|
||||||
|
if [[ "$fn" != "$BATS_TEST_NAME" ]]; then
|
||||||
|
printf "from function \`%s' " "$fn"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $index -eq $count ]]; then
|
||||||
|
printf 'in test file %s, line %d)\n' "$filename" "$lineno"
|
||||||
|
else
|
||||||
|
printf 'in file %s, line %d,\n' "$filename" "$lineno"
|
||||||
|
fi
|
||||||
|
|
||||||
|
((++index))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_print_failed_command() {
|
||||||
|
local frame="${BATS_STACK_TRACE[${#BATS_STACK_TRACE[@]} - 1]}"
|
||||||
|
local filename
|
||||||
|
local lineno
|
||||||
|
local failed_line
|
||||||
|
local failed_command
|
||||||
|
|
||||||
|
bats_frame_filename "$frame" 'filename'
|
||||||
|
bats_frame_lineno "$frame" 'lineno'
|
||||||
|
bats_extract_line "$filename" "$lineno" 'failed_line'
|
||||||
|
bats_strip_string "$failed_line" 'failed_command'
|
||||||
|
printf '%s' "# \`${failed_command}' "
|
||||||
|
|
||||||
|
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
|
||||||
|
printf 'failed\n'
|
||||||
|
else
|
||||||
|
printf 'failed with status %d\n' "$BATS_ERROR_STATUS"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_lineno() {
|
||||||
|
printf -v "$2" '%s' "${1%% *}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_function() {
|
||||||
|
local __bff_function="${1#* }"
|
||||||
|
printf -v "$2" '%s' "${__bff_function%% *}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_filename() {
|
||||||
|
local __bff_filename="${1#* }"
|
||||||
|
__bff_filename="${__bff_filename#* }"
|
||||||
|
|
||||||
|
if [[ "$__bff_filename" == "$BATS_TEST_SOURCE" ]]; then
|
||||||
|
__bff_filename="$BATS_TEST_FILENAME"
|
||||||
|
fi
|
||||||
|
printf -v "$2" '%s' "$__bff_filename"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_extract_line() {
|
||||||
|
local __bats_extract_line_line
|
||||||
|
local __bats_extract_line_index=0
|
||||||
|
|
||||||
|
while IFS= read -r __bats_extract_line_line; do
|
||||||
|
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
|
||||||
|
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done <"$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_strip_string() {
|
||||||
|
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
|
||||||
|
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_trim_filename() {
|
||||||
|
printf -v "$2" '%s' "${1#$BATS_CWD/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_debug_trap() {
|
||||||
|
# don't update the trace within library functions or we get backtraces from inside traps
|
||||||
|
if [[ "$1" != $BATS_ROOT/lib/* && "$1" != $BATS_ROOT/libexec/* ]]; then
|
||||||
|
# The last entry in the stack trace is not useful when en error occured:
|
||||||
|
# It is either duplicated (kinda correct) or has wrong line number (Bash < 4.4)
|
||||||
|
# Therefore we capture the stacktrace but use it only after the next debug
|
||||||
|
# trap fired.
|
||||||
|
# Expansion is required for empty arrays which otherwise error
|
||||||
|
BATS_CURRENT_STACK_TRACE=("${BATS_STACK_TRACE[@]+"${BATS_STACK_TRACE[@]}"}")
|
||||||
|
bats_capture_stack_trace
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# For some versions of Bash, the `ERR` trap may not always fire for every
|
||||||
|
# command failure, but the `EXIT` trap will. Also, some command failures may not
|
||||||
|
# set `$?` properly. See #72 and #81 for details.
|
||||||
|
#
|
||||||
|
# For this reason, we call `bats_error_trap` at the very beginning of
|
||||||
|
# `bats_teardown_trap` (the `DEBUG` trap for the call will fix the stack trace)
|
||||||
|
# and check the value of `$BATS_TEST_COMPLETED` before taking other actions.
|
||||||
|
# We also adjust the exit status value if needed.
|
||||||
|
#
|
||||||
|
# See `bats_exit_trap` for an additional EXIT error handling case when `$?`
|
||||||
|
# isn't set properly during `teardown()` errors.
|
||||||
|
bats_error_trap() {
|
||||||
|
local status="$?"
|
||||||
|
if [[ -z "$BATS_TEST_COMPLETED" ]]; then
|
||||||
|
BATS_ERROR_STATUS="${BATS_ERROR_STATUS:-$status}"
|
||||||
|
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
||||||
|
BATS_ERROR_STATUS=1
|
||||||
|
fi
|
||||||
|
BATS_STACK_TRACE=("${BATS_CURRENT_STACK_TRACE[@]}")
|
||||||
|
trap - DEBUG
|
||||||
|
fi
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
bats_test_count_validator() {
|
||||||
|
header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
# repeat the header
|
||||||
|
printf "%s\n" "$header"
|
||||||
|
|
||||||
|
# if we detect a TAP plan
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
# extract the number of tests ...
|
||||||
|
local expected_number_of_tests="${header:3}"
|
||||||
|
# ... count the actual number of [not ] oks...
|
||||||
|
local actual_number_of_tests=0
|
||||||
|
while IFS= read -r line; do
|
||||||
|
# forward line
|
||||||
|
printf "%s\n" "$line"
|
||||||
|
case "$line" in
|
||||||
|
'ok '*)
|
||||||
|
(( ++actual_number_of_tests ))
|
||||||
|
;;
|
||||||
|
'not ok'*)
|
||||||
|
(( ++actual_number_of_tests ))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# ... and error if they are not the same
|
||||||
|
if [[ "${actual_number_of_tests}" != "${expected_number_of_tests}" ]]; then
|
||||||
|
printf '# bats warning: Executed %s instead of expected %s tests\n' "$actual_number_of_tests" "$expected_number_of_tests"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# forward output unchanged
|
||||||
|
cat
|
||||||
|
fi
|
||||||
|
}
|
@ -0,0 +1,262 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eET
|
||||||
|
|
||||||
|
export flags=()
|
||||||
|
num_jobs=1
|
||||||
|
filter=''
|
||||||
|
extended_syntax=''
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-c) ;;
|
||||||
|
|
||||||
|
-f)
|
||||||
|
shift
|
||||||
|
filter="$1"
|
||||||
|
flags+=('-f' "$filter")
|
||||||
|
;;
|
||||||
|
-j)
|
||||||
|
shift
|
||||||
|
num_jobs="$1"
|
||||||
|
;;
|
||||||
|
-T)
|
||||||
|
flags+=('-T')
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
flags+=('-x')
|
||||||
|
extended_syntax=1
|
||||||
|
;;
|
||||||
|
--no-parallelize-within-files)
|
||||||
|
# use singular to allow for users to override in file
|
||||||
|
BATS_NO_PARALLELIZE_WITHIN_FILE=1
|
||||||
|
;;
|
||||||
|
--dummy-flag)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
filename="$1"
|
||||||
|
TESTS_FILE="$2"
|
||||||
|
|
||||||
|
if [[ ! -f "$filename" ]]; then
|
||||||
|
printf 'Testfile "%s" not found\n' "$filename" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_TEST_FILENAME="$filename"
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/preprocessing.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
|
||||||
|
|
||||||
|
bats_run_setup_file() {
|
||||||
|
# shellcheck source=lib/bats-core/tracing.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||||
|
# shellcheck source=lib/bats-core/test_functions.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
|
||||||
|
|
||||||
|
exec 3<&1
|
||||||
|
|
||||||
|
BATS_STACK_TRACE=()
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_CURRENT_STACK_TRACE=() # used in tracing.bash
|
||||||
|
|
||||||
|
# these are defined only to avoid errors when referencing undefined variables down the line
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_TEST_NAME= # used in tracing.bash
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_TEST_COMPLETED= # used in tracing.bash
|
||||||
|
|
||||||
|
BATS_SETUP_FILE_COMPLETED=
|
||||||
|
BATS_TEARDOWN_FILE_COMPLETED=
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_ERROR_STATUS= # used in tracing.bash
|
||||||
|
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
|
||||||
|
trap 'bats_error_trap' ERR
|
||||||
|
trap 'bats_file_teardown_trap' EXIT
|
||||||
|
|
||||||
|
touch "$BATS_OUT"
|
||||||
|
# get the setup_file/teardown_file functions for this file (if it has them)
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$BATS_TEST_SOURCE"
|
||||||
|
setup_file >>"$BATS_OUT" 2>&1
|
||||||
|
|
||||||
|
BATS_SETUP_FILE_COMPLETED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_teardown_file() {
|
||||||
|
# avoid running the therdown trap due to errors in teardown_file
|
||||||
|
trap 'bats_file_exit_trap' EXIT
|
||||||
|
local status=0
|
||||||
|
# rely on bats_error_trap to catch failures
|
||||||
|
teardown_file >>"$BATS_OUT" 2>&1
|
||||||
|
|
||||||
|
BATS_TEARDOWN_FILE_COMPLETED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_file_teardown_trap() {
|
||||||
|
bats_error_trap
|
||||||
|
local status=0
|
||||||
|
bats_run_teardown_file
|
||||||
|
|
||||||
|
bats_file_exit_trap
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_file_exit_trap() {
|
||||||
|
trap - ERR EXIT
|
||||||
|
if [[ -z "$BATS_SETUP_FILE_COMPLETED" || -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
|
||||||
|
if [[ -z "$BATS_SETUP_FILE_COMPLETED" ]]; then
|
||||||
|
FAILURE_REASON='setup_file'
|
||||||
|
else
|
||||||
|
FAILURE_REASON='teardown_file'
|
||||||
|
fi
|
||||||
|
printf "not ok %d %s\n" "$((test_number_in_suite + 1))" "$FAILURE_REASON failed" >&3
|
||||||
|
bats_print_stack_trace "${BATS_STACK_TRACE[@]}" >&3
|
||||||
|
bats_print_failed_command >&3
|
||||||
|
while IFS= read -r line; do
|
||||||
|
printf "# %s\n" "$line"
|
||||||
|
done <"$BATS_OUT" >&3
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
printf '# %s\n' "$line"
|
||||||
|
fi
|
||||||
|
rm -rf "$BATS_OUT"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
exit $status
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup_file() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown_file() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_forward_output_of_parallel_test() {
|
||||||
|
local test_number_in_suite=$1
|
||||||
|
local status=0
|
||||||
|
wait "$(cat "$output_folder/$test_number_in_suite/pid")" || status=1
|
||||||
|
cat "$output_folder/$test_number_in_suite/stdout"
|
||||||
|
cat "$output_folder/$test_number_in_suite/stderr" >&2
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_is_next_parallel_test_finished() {
|
||||||
|
local PID
|
||||||
|
# get the pid of the next potentially finished test
|
||||||
|
PID=$(cat "$output_folder/$(( test_number_in_suite_of_last_finished_test + 1 ))/pid")
|
||||||
|
# try to send a signal to this process
|
||||||
|
# if it fails, the process exited,
|
||||||
|
# if it succeeds, the process is still running
|
||||||
|
if kill -0 "$PID" 2>/dev/null; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# prints output from all tests in the order they were started
|
||||||
|
# $1 == "blocking": wait for a test to finish before printing
|
||||||
|
# != "blocking": abort printing, when a test has not finished
|
||||||
|
bats_forward_output_for_parallel_tests() {
|
||||||
|
local status=0
|
||||||
|
# was the next test already started?
|
||||||
|
while [[ $(( test_number_in_suite_of_last_finished_test + 1 )) -le $test_number_in_suite ]]; do
|
||||||
|
# if we are okay with waiting or if the test has already been finished
|
||||||
|
if [[ "$1" == "blocking" ]] || bats_is_next_parallel_test_finished ; then
|
||||||
|
(( ++test_number_in_suite_of_last_finished_test ))
|
||||||
|
bats_forward_output_of_parallel_test "$test_number_in_suite_of_last_finished_test" || status=1
|
||||||
|
else
|
||||||
|
# non-blocking and the process has not finished -> abort the printing
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_tests_in_parallel() {
|
||||||
|
local output_folder="$BATS_RUN_TMPDIR/parallel_output"
|
||||||
|
local status=0
|
||||||
|
mkdir -p "$output_folder"
|
||||||
|
# shellcheck source=lib/bats-core/semaphore.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/semaphore.bash"
|
||||||
|
# the test_number_in_file is not yet incremented -> one before the next test to run
|
||||||
|
local test_number_in_suite_of_last_finished_test="$test_number_in_suite" # stores which test was printed last
|
||||||
|
for test_name in "${tests_to_run[@]}"; do
|
||||||
|
# Only handle non-empty lines
|
||||||
|
if [[ $test_name ]]; then
|
||||||
|
((++test_number_in_suite))
|
||||||
|
((++test_number_in_file))
|
||||||
|
mkdir -p "$output_folder/$test_number_in_suite"
|
||||||
|
bats_semaphore_run "$output_folder/$test_number_in_suite" \
|
||||||
|
"$BATS_LIBEXEC/bats-exec-test" "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" \
|
||||||
|
> "$output_folder/$test_number_in_suite/pid"
|
||||||
|
fi
|
||||||
|
# print results early to get interactive feedback
|
||||||
|
bats_forward_output_for_parallel_tests non-blocking || status=1 # ignore if we did not finish yet
|
||||||
|
done
|
||||||
|
bats_forward_output_for_parallel_tests blocking || status=1
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_tests() {
|
||||||
|
status=0
|
||||||
|
tests_to_run=()
|
||||||
|
local line_number=0
|
||||||
|
# the global test number must be visible to traps -> not local
|
||||||
|
first_test_number_in_suite=''
|
||||||
|
while read -r test_line; do
|
||||||
|
# check if the line begins with filename
|
||||||
|
# filename might contain some hard to parse characters,
|
||||||
|
# use simple string operations to work around that issue
|
||||||
|
if [[ "$filename" == "${test_line::${#filename}}" ]]; then
|
||||||
|
# get the rest of the line without the separator \t
|
||||||
|
test_name=${test_line:$((1 + ${#filename} ))}
|
||||||
|
tests_to_run+=("$test_name")
|
||||||
|
# save the first test's number for later iteration
|
||||||
|
# this assumes that tests for a file are stored consecutive in the file!
|
||||||
|
if [[ -z "$first_test_number_in_suite" ]]; then
|
||||||
|
first_test_number_in_suite=$line_number
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
((++line_number))
|
||||||
|
done <"$TESTS_FILE"
|
||||||
|
|
||||||
|
test_number_in_suite="$first_test_number_in_suite"
|
||||||
|
test_number_in_file=0
|
||||||
|
if [[ "$num_jobs" != 1 && "${BATS_NO_PARALLELIZE_WITHIN_FILE-False}" == False ]]; then
|
||||||
|
export BATS_SEMAPHORE_NUMBER_OF_SLOTS="$num_jobs"
|
||||||
|
bats_run_tests_in_parallel "$BATS_RUN_TMPDIR/parallel_output" || status=1
|
||||||
|
else
|
||||||
|
for test_name in "${tests_to_run[@]}"; do
|
||||||
|
# Only handle non-empty lines
|
||||||
|
if [[ $test_name ]]; then
|
||||||
|
((++test_number_in_suite))
|
||||||
|
((++test_number_in_file))
|
||||||
|
# deal with empty flags to avoid spurious "unbound variable" errors on Bash 4.3 and lower
|
||||||
|
if [[ "${#flags[@]}" -gt 0 ]]; then
|
||||||
|
"$BATS_LIBEXEC/bats-exec-test" "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" || status=1
|
||||||
|
else
|
||||||
|
"$BATS_LIBEXEC/bats-exec-test" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" || status=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
export status
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n "$extended_syntax" ]]; then
|
||||||
|
printf "suite %s\n" "$filename"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_preprocess_source "$filename"
|
||||||
|
bats_run_setup_file
|
||||||
|
bats_run_tests
|
||||||
|
bats_run_teardown_file
|
||||||
|
|
||||||
|
exit $status
|
@ -1,63 +1,146 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
count_only_flag=""
|
count_only_flag=''
|
||||||
if [[ "$1" = "-c" ]]; then
|
filter=''
|
||||||
count_only_flag=1
|
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS-1}
|
||||||
shift
|
have_gnu_parallel=
|
||||||
fi
|
bats_parallel_args=()
|
||||||
|
bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
|
||||||
|
bats_no_parallelize_within_files=
|
||||||
|
flags=('--dummy-flag') # add a dummy flag to prevent unset varialeb errors on empty array expansion in old bash versions
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
printf 'Error: %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
extended_syntax_flag=""
|
while [[ "$#" -ne 0 ]]; do
|
||||||
if [[ "$1" = "-x" ]]; then
|
case "$1" in
|
||||||
extended_syntax_flag="-x"
|
-c)
|
||||||
|
count_only_flag=1
|
||||||
|
;;
|
||||||
|
-f)
|
||||||
|
shift
|
||||||
|
filter="$1"
|
||||||
|
flags+=('-f' "$filter")
|
||||||
|
;;
|
||||||
|
-j)
|
||||||
|
shift
|
||||||
|
num_jobs="$1"
|
||||||
|
flags+=('-j' "$num_jobs")
|
||||||
|
;;
|
||||||
|
-T)
|
||||||
|
flags+=('-T')
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
flags+=('-x')
|
||||||
|
;;
|
||||||
|
--no-parallelize-across-files)
|
||||||
|
bats_no_parallelize_across_files=1
|
||||||
|
;;
|
||||||
|
--no-parallelize-within-files)
|
||||||
|
bats_no_parallelize_within_files=1
|
||||||
|
flags+=("--no-parallelize-within-files")
|
||||||
|
;;
|
||||||
|
--dummy-flag)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
shift
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if (type -p parallel &>/dev/null); then
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
have_gnu_parallel=1
|
||||||
|
elif [[ "$num_jobs" != 1 && -z "$bats_no_parallelize_across_files" ]]; then
|
||||||
|
abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
trap "kill 0; exit 1" int
|
trap 'kill 0; exit 1' INT
|
||||||
|
|
||||||
|
# create a file that contains all (filtered) tests to run from all files
|
||||||
|
TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
|
||||||
|
|
||||||
count=0
|
all_tests=()
|
||||||
for filename in "$@"; do
|
for filename in "$@"; do
|
||||||
while IFS= read -r line; do
|
if [[ ! -f "$filename" ]]; then
|
||||||
if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then
|
abort "Test file \"${filename}\" does not exist"
|
||||||
let count+=1
|
fi
|
||||||
|
|
||||||
|
test_names=()
|
||||||
|
test_dupes=()
|
||||||
|
while read -r line; do
|
||||||
|
if [[ ! "$line" =~ ^bats_test_function\ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
line="${line%$'\r'}"
|
||||||
|
line="${line#* }"
|
||||||
|
test_line=$(printf "%s\t%s" "$filename" "$line")
|
||||||
|
all_tests+=("$test_line")
|
||||||
|
printf "%s\n" "$test_line" >>"$TESTS_LIST_FILE"
|
||||||
|
# avoid unbound variable errors on empty array expansion with old bash versions
|
||||||
|
if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
|
||||||
|
test_dupes+=("$line")
|
||||||
|
continue
|
||||||
fi
|
fi
|
||||||
done <"$filename"
|
test_names+=("$line")
|
||||||
|
done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename")
|
||||||
|
|
||||||
|
if [[ "${#test_dupes[@]}" -ne 0 ]]; then
|
||||||
|
abort "Duplicate test name(s) in file \"${filename}\": ${test_dupes[*]}"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_count="${#all_tests[@]}"
|
||||||
|
|
||||||
if [[ -n "$count_only_flag" ]]; then
|
if [[ -n "$count_only_flag" ]]; then
|
||||||
printf '%d\n' "$count"
|
printf '%d\n' "${test_count}"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '1..%d\n' "$count"
|
if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||||
|
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||||
|
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
status=0
|
status=0
|
||||||
offset=0
|
printf '1..%d\n' "${test_count}"
|
||||||
for filename in "$@"; do
|
|
||||||
index=0
|
# No point on continuing if there's no tests.
|
||||||
{
|
if [[ "${test_count}" == 0 ]]; then
|
||||||
IFS= read -r # 1..n
|
exit
|
||||||
while IFS= read -r line; do
|
fi
|
||||||
case "$line" in
|
|
||||||
"begin "* )
|
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
|
||||||
let index+=1
|
# (see https://github.com/bats-core/bats-core/issues/329)
|
||||||
printf '%s\n' "${line/ $index / $(($offset + $index)) }"
|
# If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
|
||||||
;;
|
# Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
|
||||||
"ok "* | "not ok "* )
|
IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@"| nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
|
||||||
if [[ -z "$extended_syntax_flag" ]]; then
|
|
||||||
let index+=1
|
if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||||
fi
|
# run files in parallel to get the maximum pool of parallel tasks
|
||||||
printf '%s\n' "${line/ $index / $(($offset + $index)) }"
|
if [[ ${#flags[@]} -eq 0 ]]; then
|
||||||
if [[ "${line:0:6}" == "not ok" ]]; then
|
# if there are no flags, our quoting below would keep an empty arg, which is wrong
|
||||||
status=1
|
parallel "${bats_parallel_args[@]}" --keep-order --jobs "$num_jobs" bats-exec-file "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || status=1
|
||||||
fi
|
else
|
||||||
;;
|
# shellcheck disable=SC2086,SC2068
|
||||||
* )
|
# we need to handle the quoting of ${flags[@]} ourselves,
|
||||||
printf '%s\n' "$line"
|
# because parallel can only quote it as one
|
||||||
;;
|
parallel --keep-order --jobs "$num_jobs" bats-exec-file "$(printf "%q " "${flags[@]}")" "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || status=1
|
||||||
esac
|
fi
|
||||||
done
|
else
|
||||||
} < <( bats-exec-test $extended_syntax_flag "$filename" )
|
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
|
||||||
offset=$(($offset + $index))
|
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || status=1
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
exit "$status"
|
exit "$status"
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cat
|
@ -0,0 +1,253 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BASE_PATH=.
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--base-path)
|
||||||
|
shift
|
||||||
|
# the relative path root to use for reporting filenames
|
||||||
|
# this is mainly intended for suite mode, where this will be the suite root folder
|
||||||
|
BASE_PATH="$1"
|
||||||
|
# use the containing directory when --base-path is a file
|
||||||
|
if [[ ! -d "$BASE_PATH" ]]; then
|
||||||
|
BASE_PATH="$(dirname "$BASE_PATH")"
|
||||||
|
fi
|
||||||
|
# get the absolute path
|
||||||
|
BASE_PATH="$(cd "$BASE_PATH"; pwd)"
|
||||||
|
# ensure the path ends with / to strip that later on
|
||||||
|
if [[ "${BASE_PATH}" != *"/" ]]; then
|
||||||
|
BASE_PATH="$BASE_PATH/"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
init_suite() {
|
||||||
|
suite_test_exec_time=0
|
||||||
|
# since we have to print the suite header before its contents but we don't know the contents before the header,
|
||||||
|
# we have to buffer the contents
|
||||||
|
_suite_buffer=""
|
||||||
|
test_result_state="" # declare for the first flush, when no test has been encountered
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer_log=
|
||||||
|
init_file() {
|
||||||
|
file_count=0
|
||||||
|
file_failures=0
|
||||||
|
file_skipped=0
|
||||||
|
file_exec_time=0
|
||||||
|
test_exec_time=0
|
||||||
|
_buffer=""
|
||||||
|
_buffer_log=""
|
||||||
|
_system_out_log=""
|
||||||
|
test_result_state="" # mark that no test has run in this file so far
|
||||||
|
}
|
||||||
|
|
||||||
|
host() {
|
||||||
|
local hostname="${HOST:-}"
|
||||||
|
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
|
||||||
|
[[ -z "$hostname" ]] && hostname="$(uname -n)"
|
||||||
|
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
|
||||||
|
|
||||||
|
echo "$hostname"
|
||||||
|
}
|
||||||
|
|
||||||
|
# convert $1 (time in milliseconds) to seconds
|
||||||
|
milliseconds_to_seconds() {
|
||||||
|
# we cannot rely on having bc for this calculation
|
||||||
|
full_seconds=$(($1 / 1000))
|
||||||
|
remaining_milliseconds=$(($1 % 1000))
|
||||||
|
if [[ $remaining_milliseconds -eq 0 ]]; then
|
||||||
|
printf "%d" "$full_seconds"
|
||||||
|
else
|
||||||
|
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_header() {
|
||||||
|
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||||
|
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
file_header() {
|
||||||
|
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
|
||||||
|
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
|
||||||
|
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
|
||||||
|
}
|
||||||
|
|
||||||
|
file_footer() {
|
||||||
|
printf "</testsuite>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_footer() {
|
||||||
|
printf "</testsuites>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_test_case() {
|
||||||
|
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
|
||||||
|
# pass and no output can be shortened
|
||||||
|
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||||
|
else
|
||||||
|
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||||
|
if [[ -n "$_system_out_log" ]]; then
|
||||||
|
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
|
||||||
|
fi
|
||||||
|
if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
|
||||||
|
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
|
||||||
|
fi
|
||||||
|
if [[ "$test_result_state" == skipped ]]; then
|
||||||
|
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
|
||||||
|
fi
|
||||||
|
printf " </testcase>\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
xml_escape() {
|
||||||
|
output=${1//&/&}
|
||||||
|
output=${output//</<}
|
||||||
|
output=${output//>/>}
|
||||||
|
output=${output//'"'/"}
|
||||||
|
output=${output//\'/'}
|
||||||
|
local CONTROL_CHAR=$'\033'
|
||||||
|
output="${output//$CONTROL_CHAR/}"
|
||||||
|
printf "%s" "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_buffer() {
|
||||||
|
local output
|
||||||
|
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||||
|
_suite_buffer="${_suite_buffer}${output%x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_flush() {
|
||||||
|
echo -n "${_suite_buffer}"
|
||||||
|
_suite_buffer=""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer() {
|
||||||
|
local output
|
||||||
|
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||||
|
_buffer="${_buffer}${output%x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
echo -n "${_buffer}"
|
||||||
|
_buffer=""
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
if [[ -n "$_buffer_log" ]]; then
|
||||||
|
_buffer_log="${_buffer_log}
|
||||||
|
$1"
|
||||||
|
else
|
||||||
|
_buffer_log="$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
flush_log() {
|
||||||
|
if [[ -n "$test_result_state" ]]; then
|
||||||
|
buffer print_test_case
|
||||||
|
fi
|
||||||
|
_buffer_log=""
|
||||||
|
_system_out_log=""
|
||||||
|
}
|
||||||
|
|
||||||
|
log_system_out() {
|
||||||
|
if [[ -n "$_system_out_log" ]]; then
|
||||||
|
_system_out_log="${_system_out_log}
|
||||||
|
$1"
|
||||||
|
else
|
||||||
|
_system_out_log="$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_file() {
|
||||||
|
if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
|
||||||
|
file_header
|
||||||
|
printf "%s\n" "${_buffer}"
|
||||||
|
file_footer
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_suite() {
|
||||||
|
flush_log
|
||||||
|
suite_header
|
||||||
|
suite_flush
|
||||||
|
finish_file # must come after suite flush to not print the last file before the others
|
||||||
|
suite_footer
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
bats_tap_stream_plan() { # <number of tests>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
init_suite
|
||||||
|
trap finish_suite EXIT
|
||||||
|
|
||||||
|
bats_tap_stream_begin() { # <test index> <test name>
|
||||||
|
flush_log
|
||||||
|
# set after flushing to avoid overriding name of test
|
||||||
|
name="$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
test_exec_time="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
test_exec_time=0
|
||||||
|
fi
|
||||||
|
((file_count += 1))
|
||||||
|
test_result_state='ok'
|
||||||
|
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||||
|
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
|
||||||
|
((file_count += 1))
|
||||||
|
((file_skipped += 1))
|
||||||
|
test_result_state='skipped'
|
||||||
|
test_exec_time=0
|
||||||
|
test_skip_message="$3"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||||
|
((file_count += 1))
|
||||||
|
((file_failures += 1))
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
test_exec_time="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
test_exec_time=0
|
||||||
|
fi
|
||||||
|
test_result_state=not_ok
|
||||||
|
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||||
|
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
|
||||||
|
if [[ "$2" == begin ]]; then
|
||||||
|
# everything that happens between begin and [not] ok is FD3 output from the test
|
||||||
|
log_system_out "$1"
|
||||||
|
else
|
||||||
|
# everything else is considered error output
|
||||||
|
log "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() { # <file name>
|
||||||
|
flush_log
|
||||||
|
suite_buffer finish_file
|
||||||
|
init_file
|
||||||
|
class="$(remove_prefix "$BASE_PATH" "$1")"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_unknown() { # <full line>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
@ -0,0 +1,235 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-T)
|
||||||
|
BATS_ENABLE_TIMING="-T"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
update_count_column_width() {
|
||||||
|
count_column_width=$((${#count} * 2 + 2))
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
# additional space for ' in %s sec'
|
||||||
|
count_column_width=$((count_column_width + ${#SECONDS} + 8))
|
||||||
|
fi
|
||||||
|
# also update dependent value
|
||||||
|
update_count_column_left
|
||||||
|
}
|
||||||
|
|
||||||
|
update_screen_width() {
|
||||||
|
screen_width="$(tput cols)"
|
||||||
|
# also update dependent value
|
||||||
|
update_count_column_left
|
||||||
|
}
|
||||||
|
|
||||||
|
update_count_column_left() {
|
||||||
|
count_column_left=$((screen_width - count_column_width))
|
||||||
|
}
|
||||||
|
|
||||||
|
trap update_screen_width WINCH
|
||||||
|
update_screen_width
|
||||||
|
|
||||||
|
begin() {
|
||||||
|
go_to_column 0
|
||||||
|
update_count_column_width
|
||||||
|
buffer_with_truncation $((count_column_left - 1)) ' %s' "$name"
|
||||||
|
clear_to_end_of_line
|
||||||
|
go_to_column $count_column_left
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
|
||||||
|
else
|
||||||
|
buffer "%${#count}s/${count}" "$index"
|
||||||
|
fi
|
||||||
|
go_to_column 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
go_to_column 0
|
||||||
|
buffer ' ✓ %s' "$name"
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
set_color 2
|
||||||
|
buffer ' [%s]' "$1"
|
||||||
|
fi
|
||||||
|
advance
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
local reason="$1"
|
||||||
|
if [[ -n "$reason" ]]; then
|
||||||
|
reason=": $reason"
|
||||||
|
fi
|
||||||
|
go_to_column 0
|
||||||
|
buffer ' - %s (skipped%s)' "$name" "$reason"
|
||||||
|
advance
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
go_to_column 0
|
||||||
|
set_color 1 bold
|
||||||
|
buffer ' ✗ %s' "$name"
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
set_color 2
|
||||||
|
buffer ' [%s]' "$1"
|
||||||
|
fi
|
||||||
|
advance
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
set_color 1
|
||||||
|
buffer ' %s\n' "$1"
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
summary() {
|
||||||
|
buffer '\n%d test' "$count"
|
||||||
|
if [[ "$count" -ne 1 ]]; then
|
||||||
|
buffer 's'
|
||||||
|
fi
|
||||||
|
|
||||||
|
buffer ', %d failure' "$failures"
|
||||||
|
if [[ "$failures" -ne 1 ]]; then
|
||||||
|
buffer 's'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$skipped" -gt 0 ]]; then
|
||||||
|
buffer ', %d skipped' "$skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
not_run=$((count - passed - failures - skipped))
|
||||||
|
if [[ "$not_run" -gt 0 ]]; then
|
||||||
|
buffer ', %d not run' "$not_run"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
buffer " in $SECONDS seconds"
|
||||||
|
fi
|
||||||
|
|
||||||
|
buffer '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_with_truncation() {
|
||||||
|
local width="$1"
|
||||||
|
shift
|
||||||
|
local string
|
||||||
|
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf -v 'string' -- "$@"
|
||||||
|
|
||||||
|
if [[ "${#string}" -gt "$width" ]]; then
|
||||||
|
buffer '%s...' "${string:0:$((width - 4))}"
|
||||||
|
else
|
||||||
|
buffer '%s' "$string"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
go_to_column() {
|
||||||
|
local column="$1"
|
||||||
|
buffer '\x1B[%dG' $((column + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_to_end_of_line() {
|
||||||
|
buffer '\x1B[K'
|
||||||
|
}
|
||||||
|
|
||||||
|
advance() {
|
||||||
|
clear_to_end_of_line
|
||||||
|
buffer '\n'
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
set_color() {
|
||||||
|
local color="$1"
|
||||||
|
local weight=22
|
||||||
|
|
||||||
|
if [[ "$2" == 'bold' ]]; then
|
||||||
|
weight=1
|
||||||
|
fi
|
||||||
|
buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_color() {
|
||||||
|
buffer '\x1B[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer=
|
||||||
|
|
||||||
|
buffer() {
|
||||||
|
local content
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf -v content -- "$@"
|
||||||
|
_buffer+="$content"
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
printf '%s' "$_buffer"
|
||||||
|
_buffer=
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
flush
|
||||||
|
printf '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
trap finish EXIT
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
bats_tap_stream_plan() {
|
||||||
|
count="$1"
|
||||||
|
index=0
|
||||||
|
passed=0
|
||||||
|
failures=0
|
||||||
|
skipped=0
|
||||||
|
name=
|
||||||
|
update_count_column_width
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_begin() {
|
||||||
|
index="$1"
|
||||||
|
name="$2"
|
||||||
|
begin
|
||||||
|
flush
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() {
|
||||||
|
index="$1"
|
||||||
|
((++passed))
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
pass "$2"
|
||||||
|
else
|
||||||
|
pass
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() {
|
||||||
|
index="$1"
|
||||||
|
((++skipped))
|
||||||
|
skip "$3"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() {
|
||||||
|
index="$1"
|
||||||
|
((++failures))
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
fail "$2"
|
||||||
|
else
|
||||||
|
fail
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() {
|
||||||
|
log "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() {
|
||||||
|
: #test_file="$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
||||||
|
|
||||||
|
summary
|
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
bats_tap_stream_plan() {
|
||||||
|
printf "1..%d\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_begin() { #<test index> <test name>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
printf "ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||||
|
else
|
||||||
|
printf "ok %d %s\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
printf "not ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||||
|
else
|
||||||
|
printf "not ok %d %s\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() { # <test index> <test name> <reason>
|
||||||
|
if [[ -n "$3" ]]; then
|
||||||
|
printf "ok %d %s # skip %s\n" "$1" "$2" "$3"
|
||||||
|
else
|
||||||
|
printf "ok %d %s # skip\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() { # <comment text without leading '# '>
|
||||||
|
printf "# %s\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() { # <file name>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_unknown() { # <full line>
|
||||||
|
printf "%s\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
@ -1,177 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Just stream the TAP output (sans extended syntax) if tput is missing
|
|
||||||
if ! command -v tput >/dev/null; then
|
|
||||||
exec grep -v "^begin "
|
|
||||||
fi
|
|
||||||
|
|
||||||
header_pattern='[0-9]+\.\.[0-9]+'
|
|
||||||
IFS= read -r header
|
|
||||||
|
|
||||||
if [[ "$header" =~ $header_pattern ]]; then
|
|
||||||
count="${header:3}"
|
|
||||||
index=0
|
|
||||||
failures=0
|
|
||||||
skipped=0
|
|
||||||
name=""
|
|
||||||
count_column_width=$(( ${#count} * 2 + 2 ))
|
|
||||||
else
|
|
||||||
# If the first line isn't a TAP plan, print it and pass the rest through
|
|
||||||
printf "%s\n" "$header"
|
|
||||||
exec cat
|
|
||||||
fi
|
|
||||||
|
|
||||||
update_screen_width() {
|
|
||||||
screen_width="$(tput cols)"
|
|
||||||
count_column_left=$(( $screen_width - $count_column_width ))
|
|
||||||
}
|
|
||||||
|
|
||||||
trap update_screen_width WINCH
|
|
||||||
update_screen_width
|
|
||||||
|
|
||||||
begin() {
|
|
||||||
go_to_column 0
|
|
||||||
printf_with_truncation $(( $count_column_left - 1 )) " %s" "$name"
|
|
||||||
clear_to_end_of_line
|
|
||||||
go_to_column $count_column_left
|
|
||||||
printf "%${#count}s/${count}" "$index"
|
|
||||||
go_to_column 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pass() {
|
|
||||||
go_to_column 0
|
|
||||||
printf " ✓ %s" "$name"
|
|
||||||
advance
|
|
||||||
}
|
|
||||||
|
|
||||||
skip() {
|
|
||||||
local reason="$1"
|
|
||||||
if [[ -n "$reason" ]]; then
|
|
||||||
reason=": $reason"
|
|
||||||
fi
|
|
||||||
go_to_column 0
|
|
||||||
printf " - %s (skipped%s)" "$name" "$reason"
|
|
||||||
advance
|
|
||||||
}
|
|
||||||
|
|
||||||
fail() {
|
|
||||||
go_to_column 0
|
|
||||||
set_color 1 bold
|
|
||||||
printf " ✗ %s" "$name"
|
|
||||||
advance
|
|
||||||
}
|
|
||||||
|
|
||||||
log() {
|
|
||||||
set_color 1
|
|
||||||
printf " %s\n" "$1"
|
|
||||||
clear_color
|
|
||||||
}
|
|
||||||
|
|
||||||
summary() {
|
|
||||||
printf "\n%d test" "$count"
|
|
||||||
if [[ "$count" -ne 1 ]]; then
|
|
||||||
printf 's'
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf ", %d failure" "$failures"
|
|
||||||
if [[ "$failures" -ne 1 ]]; then
|
|
||||||
printf 's'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$skipped" -gt 0 ]]; then
|
|
||||||
printf ", %d skipped" "$skipped"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
printf_with_truncation() {
|
|
||||||
local width="$1"
|
|
||||||
shift
|
|
||||||
local string
|
|
||||||
|
|
||||||
printf -v 'string' -- "$@"
|
|
||||||
|
|
||||||
if [[ "${#string}" -gt "$width" ]]; then
|
|
||||||
printf "%s..." "${string:0:$(( $width - 4 ))}"
|
|
||||||
else
|
|
||||||
printf "%s" "$string"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
go_to_column() {
|
|
||||||
local column="$1"
|
|
||||||
printf "\x1B[%dG" $(( $column + 1 ))
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_to_end_of_line() {
|
|
||||||
printf "\x1B[K"
|
|
||||||
}
|
|
||||||
|
|
||||||
advance() {
|
|
||||||
clear_to_end_of_line
|
|
||||||
printf '\n'
|
|
||||||
clear_color
|
|
||||||
}
|
|
||||||
|
|
||||||
set_color() {
|
|
||||||
local color="$1"
|
|
||||||
local weight=22
|
|
||||||
|
|
||||||
if [[ "$2" == 'bold' ]]; then
|
|
||||||
weight=1
|
|
||||||
fi
|
|
||||||
printf "\x1B[%d;%dm" $(( 30 + $color )) "$weight"
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_color() {
|
|
||||||
printf "\x1B[0m"
|
|
||||||
}
|
|
||||||
|
|
||||||
_buffer=""
|
|
||||||
|
|
||||||
buffer() {
|
|
||||||
_buffer="${_buffer}$("$@")"
|
|
||||||
}
|
|
||||||
|
|
||||||
flush() {
|
|
||||||
printf "%s" "$_buffer"
|
|
||||||
_buffer=""
|
|
||||||
}
|
|
||||||
|
|
||||||
finish() {
|
|
||||||
flush
|
|
||||||
printf "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
trap finish EXIT
|
|
||||||
|
|
||||||
while IFS= read -r line; do
|
|
||||||
case "$line" in
|
|
||||||
"begin "* )
|
|
||||||
let index+=1
|
|
||||||
name="${line#* $index }"
|
|
||||||
buffer begin
|
|
||||||
flush
|
|
||||||
;;
|
|
||||||
"ok "* )
|
|
||||||
skip_expr="ok $index (.*) # skip ?(([[:print:]]*))?"
|
|
||||||
if [[ "$line" =~ $skip_expr ]]; then
|
|
||||||
let skipped+=1
|
|
||||||
buffer skip "${BASH_REMATCH[2]}"
|
|
||||||
else
|
|
||||||
buffer pass
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"not ok "* )
|
|
||||||
let failures+=1
|
|
||||||
buffer fail
|
|
||||||
;;
|
|
||||||
"# "* )
|
|
||||||
buffer log "${line:2}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
buffer summary
|
|
@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-T)
|
||||||
|
BATS_ENABLE_TIMING="-T"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
printf "TAP version 13\n"
|
||||||
|
printf "%s\n" "$header"
|
||||||
|
else
|
||||||
|
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||||
|
printf '%s\n' "$header"
|
||||||
|
exec cat
|
||||||
|
fi
|
||||||
|
|
||||||
|
yaml_block_open=''
|
||||||
|
add_yaml_entry() {
|
||||||
|
if [[ -z "$yaml_block_open" ]]; then
|
||||||
|
printf " ...\n"
|
||||||
|
fi
|
||||||
|
printf " %s: %s\n" "$1" "$2"
|
||||||
|
yaml_block_open=1
|
||||||
|
}
|
||||||
|
|
||||||
|
close_previous_yaml_block() {
|
||||||
|
if [[ -n "$yaml_block_open" ]]; then
|
||||||
|
printf " ...\n"
|
||||||
|
yaml_block_open=''
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'begin '*) ;;
|
||||||
|
'ok '*)
|
||||||
|
close_previous_yaml_block
|
||||||
|
number_of_printed_log_lines_for_this_test_so_far=0
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
timing_expr="(ok [0-9]+ .+) in ([0-9]+)ms$"
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
printf "%s\n" "${BASH_REMATCH[1]}"
|
||||||
|
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||||
|
else
|
||||||
|
echo "Could not match output line to timing regex: $line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s\n" "${line}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'not ok '*)
|
||||||
|
close_previous_yaml_block
|
||||||
|
number_of_printed_log_lines_for_this_test_so_far=0
|
||||||
|
timing_expr="not ok [0-9]+ (.)+ in ([0-9])+ms$"
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
printf "%s\n" "${BATS_REMATCH[1]}"
|
||||||
|
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||||
|
else
|
||||||
|
echo "Could not match failure line to timing regex: $line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s\n" "${line}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'# '*)
|
||||||
|
if [[ $number_of_printed_log_lines_for_this_test_so_far -eq 0 ]]; then
|
||||||
|
add_yaml_entry "message" "|" # use a multiline string for this entry
|
||||||
|
fi
|
||||||
|
((++number_of_printed_log_lines_for_this_test_so_far))
|
||||||
|
printf " %s\n" "$(echo "\"${line}\"" | cut -b 3-)"
|
||||||
|
;;
|
||||||
|
'suite '*) ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# close the final block if there was one
|
||||||
|
close_previous_yaml_block
|
@ -1,10 +1,23 @@
|
|||||||
RONN := ronn
|
# Makefile
|
||||||
|
#
|
||||||
|
# bats-core manpages
|
||||||
|
#
|
||||||
|
RONN := ronn -W
|
||||||
PAGES := bats.1 bats.7
|
PAGES := bats.1 bats.7
|
||||||
|
ORG := bats-core
|
||||||
|
MANUAL := 'Bash Automated Testing System'
|
||||||
|
ISOFMT := $(shell date -I)
|
||||||
|
RM := rm -f
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
all: $(PAGES)
|
all: $(PAGES)
|
||||||
|
|
||||||
bats.1: bats.1.ronn
|
bats.1: bats.1.ronn
|
||||||
$(RONN) -r $<
|
$(RONN) --date=$(ISOFMT) --manual=$(MANUAL) --organization=$(ORG) --roff $<
|
||||||
|
|
||||||
bats.7: bats.7.ronn
|
bats.7: bats.7.ronn
|
||||||
$(RONN) -r $<
|
$(RONN) --date=$(ISOFMT) --manual=$(MANUAL) --organization=$(ORG) --roff $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(PAGES)
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
targets=()
|
||||||
|
while IFS= read -r -d $'\0'; do
|
||||||
|
targets+=("$REPLY")
|
||||||
|
done < <(
|
||||||
|
find \
|
||||||
|
bin/bats \
|
||||||
|
libexec/bats-core \
|
||||||
|
lib/bats-core \
|
||||||
|
shellcheck.sh \
|
||||||
|
-type f \
|
||||||
|
-print0
|
||||||
|
)
|
||||||
|
|
||||||
|
LC_ALL=C.UTF-8 shellcheck "${targets[@]}"
|
||||||
|
|
||||||
|
exit $?
|
@ -0,0 +1,179 @@
|
|||||||
|
load 'test_helper'
|
||||||
|
fixtures file_setup_teardown
|
||||||
|
|
||||||
|
setup_file() {
|
||||||
|
export SETUP_FILE_EXPORT_TEST=true
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
# give each test their own tmpdir to allow for parallelization without interference
|
||||||
|
make_bats_test_suite_tmpdir "$BATS_TEST_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
test_helper::cleanup_tmpdir "$BATS_TEST_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "setup_file is run once per file" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/setup_file_once.log"
|
||||||
|
bats "$FIXTURE_ROOT/setup_file.bats"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file is run once per file" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/teardown_file_once.log"
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file.bats"
|
||||||
|
[[ $status -eq 0 ]]
|
||||||
|
# output the log for faster debugging
|
||||||
|
cat "$LOG"
|
||||||
|
# expect to find an entry for the tested file
|
||||||
|
grep 'teardown_file.bats' "$LOG"
|
||||||
|
# it should be the only entry
|
||||||
|
run wc -l < "$LOG"
|
||||||
|
[[ $output -eq 1 ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "setup_file is called correctly in multi file suite" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/setup_file_multi_file_suite.log"
|
||||||
|
run bats "$FIXTURE_ROOT/setup_file.bats" "$FIXTURE_ROOT/no_setup_file.bats" "$FIXTURE_ROOT/setup_file2.bats"
|
||||||
|
[[ $status -eq 0 ]]
|
||||||
|
run wc -l < "$LOG"
|
||||||
|
# each setup_file[2].bats is in the log exactly once!
|
||||||
|
[[ $output -eq 2 ]]
|
||||||
|
grep setup_file.bats "$LOG"
|
||||||
|
grep setup_file2.bats "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file is called correctly in multi file suite" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/teardown_file_multi_file_suite.log"
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file.bats" "$FIXTURE_ROOT/no_teardown_file.bats" "$FIXTURE_ROOT/teardown_file2.bats"
|
||||||
|
[[ $status -eq 0 ]]
|
||||||
|
run wc -l < "$LOG"
|
||||||
|
# each teardown_file[2].bats is in the log exactly once!
|
||||||
|
[[ $output -eq 2 ]]
|
||||||
|
grep teardown_file.bats "$LOG"
|
||||||
|
grep teardown_file2.bats "$LOG"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "setup_file failure aborts tests for this file" {
|
||||||
|
# this might need to mark them as skipped as the test count is already determined at this point
|
||||||
|
run bats "$FIXTURE_ROOT/setup_file_failed.bats"
|
||||||
|
echo "$output"
|
||||||
|
[[ "${lines[0]}" == "1..2" ]]
|
||||||
|
[[ "${lines[1]}" == "not ok 1 setup_file failed" ]]
|
||||||
|
[[ "${lines[2]}" == "# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/setup_file_failed.bats, line 2)" ]]
|
||||||
|
[[ "${lines[3]}" == "# \`false' failed" ]]
|
||||||
|
[[ "${lines[4]}" == "# bats warning: Executed 1 instead of expected 2 tests" ]] # this warning is expected
|
||||||
|
# to appease the count validator, we would have to reduce the expected number of tests (retroactively?) or
|
||||||
|
# output even those tests that should be skipped due to a failed setup_file.
|
||||||
|
# Since we are already in a failure mode, the additional error does not hurt and is less verbose than
|
||||||
|
# printing all the failed/skipped tests due to the setup failure.
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file failure fails at least one test from the file" {
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file_failed.bats"
|
||||||
|
[[ $status -ne 0 ]]
|
||||||
|
echo "$output"
|
||||||
|
[[ "${lines[0]}" == "1..1" ]]
|
||||||
|
[[ "${lines[1]}" == "ok 1 test" ]]
|
||||||
|
[[ "${lines[2]}" == "not ok 2 teardown_file failed" ]]
|
||||||
|
[[ "${lines[3]}" == "# (from function \`teardown_file' in test file $RELATIVE_FIXTURE_ROOT/teardown_file_failed.bats, line 3)" ]]
|
||||||
|
[[ "${lines[4]}" == "# \`false' failed" ]]
|
||||||
|
[[ "${lines[5]}" == "# bats warning: Executed 2 instead of expected 1 tests" ]] # for now this warning is expected
|
||||||
|
# for a failed teardown_file not to change the number of tests being reported, we would have to alter at least one provious test result report
|
||||||
|
# this would require arbitrary amounts of buffering so we simply add our own line with a fake test number
|
||||||
|
# tripping the count validator won't change the overall result, as we already are in a failure mode
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file runs even if any test in the file failed" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/teardown_file_failed.log"
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file_after_failing_test.bats"
|
||||||
|
[[ $status -ne 0 ]]
|
||||||
|
grep teardown_file_after_failing_test.bats "$LOG"
|
||||||
|
echo "$output"
|
||||||
|
[[ $output == "1..1
|
||||||
|
not ok 1 failing test
|
||||||
|
# (in test file $RELATIVE_FIXTURE_ROOT/teardown_file_after_failing_test.bats, line 6)
|
||||||
|
# \`false' failed" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file should run even after user abort via CTRL-C" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/teardown_file_abort.log"
|
||||||
|
STARTTIME=$SECONDS
|
||||||
|
# guarantee that background processes get their own process group -> pid=pgid
|
||||||
|
set -m
|
||||||
|
SECONDS=0
|
||||||
|
# run testsubprocess in background to not avoid blocking this test
|
||||||
|
bats "$FIXTURE_ROOT/teardown_file_after_long_test.bats"&
|
||||||
|
SUBPROCESS_PID=$!
|
||||||
|
# wait until we enter the test
|
||||||
|
sleep 2
|
||||||
|
# fake sending SIGINT (CTRL-C) to the process group of the background subprocess
|
||||||
|
kill -SIGINT -- -$SUBPROCESS_PID
|
||||||
|
wait # for the test to finish either way (SIGINT or normal execution)
|
||||||
|
echo "Waited: $SECONDS seconds"
|
||||||
|
[[ $SECONDS -lt 10 ]] # make sure we really cut it short with SIGINT
|
||||||
|
# check that teardown_file ran and created the log file
|
||||||
|
[[ -f "$LOG" ]]
|
||||||
|
grep teardown_file_after_long_test.bats "$LOG"
|
||||||
|
# but the test must not have run to the end!
|
||||||
|
! grep "test finished successfully" "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "setup_file runs even if all tests in the file are skipped" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/setup_file_skipped.log"
|
||||||
|
run bats "$FIXTURE_ROOT/setup_file_even_if_all_tests_are_skipped.bats"
|
||||||
|
[[ -f "$LOG" ]]
|
||||||
|
grep setup_file_even_if_all_tests_are_skipped.bats "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file runs even if all tests in the file are skipped" {
|
||||||
|
export LOG="$BATS_TEST_SUITE_TMPDIR/teardown_file_skipped.log"
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file_even_if_all_tests_are_skipped.bats"
|
||||||
|
[[ $status -eq 0 ]]
|
||||||
|
[[ -f "$LOG" ]]
|
||||||
|
grep teardown_file_even_if_all_tests_are_skipped.bats "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "setup_file must not leak context between tests in the same suite" {
|
||||||
|
# example: BATS_ROOT was unset in one test but used in others, therefore, the suite failed
|
||||||
|
# Simulate leaking env var from first to second test by: export SETUP_FILE_VAR="LEAK!"
|
||||||
|
run bats "$FIXTURE_ROOT/setup_file_does_not_leak_env.bats" "$FIXTURE_ROOT/setup_file_does_not_leak_env2.bats"
|
||||||
|
[[ $status -eq 0 ]] || (echo $output; return 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "teardown_file must not leak context between tests in the same suite" {
|
||||||
|
# example: BATS_ROOT was unset in one test but used in others, therefore, the suite failed
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file_does_not_leak.bats" "$FIXTURE_ROOT/teardown_file_does_not_leak2.bats"
|
||||||
|
echo "$output"
|
||||||
|
[[ $status -eq 0 ]]
|
||||||
|
[[ $output == "1..2
|
||||||
|
ok 1 test
|
||||||
|
ok 2 must not see variable from first run" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "halfway setup_file errors are caught and reported" {
|
||||||
|
run bats "$FIXTURE_ROOT/setup_file_halfway_error.bats"
|
||||||
|
[[ $status -ne 0 ]]
|
||||||
|
echo "$output"
|
||||||
|
[[ "$output" == "1..1
|
||||||
|
not ok 1 setup_file failed
|
||||||
|
# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/setup_file_halfway_error.bats, line 3)
|
||||||
|
# \`false' failed" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "halfway teardown_file errors are caught and reported" {
|
||||||
|
run bats "$FIXTURE_ROOT/teardown_file_halfway_error.bats"
|
||||||
|
echo "$output"
|
||||||
|
[[ $status -ne 0 ]]
|
||||||
|
[[ "${lines[0]}" == "1..1" ]]
|
||||||
|
[[ "${lines[1]}" == "ok 1 empty" ]]
|
||||||
|
[[ "${lines[2]}" == "not ok 2 teardown_file failed" ]]
|
||||||
|
[[ "${lines[3]}" == "# (from function \`teardown_file' in test file $RELATIVE_FIXTURE_ROOT/teardown_file_halfway_error.bats, line 3)" ]]
|
||||||
|
[[ "${lines[4]}" == "# \`false' failed" ]]
|
||||||
|
[[ "${lines[5]}" == "# bats warning: Executed 2 instead of expected 1 tests" ]] # for now this warning is expected
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "variables exported in setup_file are visible in tests" {
|
||||||
|
[[ $SETUP_FILE_EXPORT_TEST == "true" ]]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
# Helper to detect regressions in load's lookup ordering.
|
||||||
|
# An exact name match should be prioritized over name.bash.
|
||||||
|
|
||||||
|
echo "Should not have loaded this file!" >&2
|
||||||
|
exit 1
|
@ -0,0 +1,6 @@
|
|||||||
|
# Helper to detect regressions in load's lookup ordering.
|
||||||
|
# An exact name match should be prioritized over name.bash.
|
||||||
|
|
||||||
|
echo "This is the expected file to load."
|
||||||
|
|
||||||
|
help_me() { :; }
|
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Fractional timeout supported in bash 4+
|
||||||
|
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
|
||||||
|
timeout=1
|
||||||
|
else
|
||||||
|
timeout=0.01
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Just reading from stdin
|
||||||
|
while read -r -t $timeout foo; do
|
||||||
|
if [ "$foo" == "EXIT" ]; then
|
||||||
|
echo "Found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Not found"
|
||||||
|
exit 1
|
@ -0,0 +1,32 @@
|
|||||||
|
function should_be_found { # @test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
function should_be_found_with_trailing_whitespace { # @test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be_found_with_parens() { #@test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be_found_with_parens_and_whitespace () { #@test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
function should_be_found_with_function_and_parens() { #@test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
function should_be_found_with_function_parens_and_whitespace () { #@test
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
should_not_be_found() {
|
||||||
|
false
|
||||||
|
#@test
|
||||||
|
}
|
||||||
|
|
||||||
|
should_not_be_found() {
|
||||||
|
false
|
||||||
|
} #@test
|
@ -0,0 +1,5 @@
|
|||||||
|
echo "file1" >> "$TEMPFILE"
|
||||||
|
|
||||||
|
@test "test1" {
|
||||||
|
:
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
echo "file2" >> "$TEMPFILE"
|
||||||
|
|
||||||
|
@test "test 1" {
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "test 2" {
|
||||||
|
:
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue