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
|
||||
|
||||
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,
|
||||
"Don't depend on network during builds (re: 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
|
||||
- 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
|
||||
|
||||
set -e
|
||||
|
||||
export BATS_READLINK='true'
|
||||
if command -v 'greadlink' >/dev/null; then
|
||||
BATS_READLINK='greadlink'
|
||||
elif command -v 'readlink' >/dev/null; then
|
||||
BATS_READLINK='readlink'
|
||||
set -euo pipefail
|
||||
|
||||
if command -v greadlink >/dev/null; then
|
||||
bats_readlinkf() {
|
||||
greadlink -f "$1"
|
||||
}
|
||||
else
|
||||
bats_readlinkf() {
|
||||
readlink -f "$1"
|
||||
}
|
||||
fi
|
||||
|
||||
bats_resolve_link() {
|
||||
if ! "$BATS_READLINK" "$1"; then
|
||||
return 0
|
||||
fi
|
||||
fallback_to_readlinkf_posix() {
|
||||
bats_readlinkf() {
|
||||
[ "${1:-}" ] || return 1
|
||||
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() {
|
||||
local cwd="$PWD"
|
||||
local path="$1"
|
||||
local result="$2"
|
||||
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
|
||||
}
|
||||
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
|
||||
fallback_to_readlinkf_posix
|
||||
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
|
||||
fi
|
||||
|
||||
export BATS_ROOT
|
||||
bats_resolve_absolute_root_dir "$0" 'BATS_ROOT'
|
||||
exec "$BATS_ROOT/libexec/bats-core/bats" "$@"
|
||||
export BATS_ROOT=${BATS_PATH%/*/*}
|
||||
export -f bats_readlinkf
|
||||
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
|
||||
set -e
|
||||
|
||||
count_only_flag=""
|
||||
if [[ "$1" = "-c" ]]; then
|
||||
count_only_flag=1
|
||||
shift
|
||||
fi
|
||||
count_only_flag=''
|
||||
filter=''
|
||||
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS-1}
|
||||
have_gnu_parallel=
|
||||
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=""
|
||||
if [[ "$1" = "-x" ]]; then
|
||||
extended_syntax_flag="-x"
|
||||
while [[ "$#" -ne 0 ]]; do
|
||||
case "$1" in
|
||||
-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
|
||||
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
|
||||
|
||||
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
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then
|
||||
let count+=1
|
||||
if [[ ! -f "$filename" ]]; then
|
||||
abort "Test file \"${filename}\" does not exist"
|
||||
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
|
||||
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
|
||||
|
||||
test_count="${#all_tests[@]}"
|
||||
|
||||
if [[ -n "$count_only_flag" ]]; then
|
||||
printf '%d\n' "$count"
|
||||
printf '%d\n' "${test_count}"
|
||||
exit
|
||||
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
|
||||
offset=0
|
||||
for filename in "$@"; do
|
||||
index=0
|
||||
{
|
||||
IFS= read -r # 1..n
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
"begin "* )
|
||||
let index+=1
|
||||
printf '%s\n' "${line/ $index / $(($offset + $index)) }"
|
||||
;;
|
||||
"ok "* | "not ok "* )
|
||||
if [[ -z "$extended_syntax_flag" ]]; then
|
||||
let index+=1
|
||||
fi
|
||||
printf '%s\n' "${line/ $index / $(($offset + $index)) }"
|
||||
if [[ "${line:0:6}" == "not ok" ]]; then
|
||||
status=1
|
||||
fi
|
||||
;;
|
||||
* )
|
||||
printf '%s\n' "$line"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
} < <( bats-exec-test $extended_syntax_flag "$filename" )
|
||||
offset=$(($offset + $index))
|
||||
done
|
||||
printf '1..%d\n' "${test_count}"
|
||||
|
||||
# No point on continuing if there's no tests.
|
||||
if [[ "${test_count}" == 0 ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
|
||||
# (see https://github.com/bats-core/bats-core/issues/329)
|
||||
# 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.
|
||||
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 [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||
# run files in parallel to get the maximum pool of parallel tasks
|
||||
if [[ ${#flags[@]} -eq 0 ]]; then
|
||||
# if there are no flags, our quoting below would keep an empty arg, which is wrong
|
||||
parallel "${bats_parallel_args[@]}" --keep-order --jobs "$num_jobs" bats-exec-file "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || status=1
|
||||
else
|
||||
# shellcheck disable=SC2086,SC2068
|
||||
# we need to handle the quoting of ${flags[@]} ourselves,
|
||||
# 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
|
||||
fi
|
||||
else
|
||||
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
|
||||
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || status=1
|
||||
done
|
||||
fi
|
||||
|
||||
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
|
||||
ORG := bats-core
|
||||
MANUAL := 'Bash Automated Testing System'
|
||||
ISOFMT := $(shell date -I)
|
||||
RM := rm -f
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(PAGES)
|
||||
|
||||
bats.1: bats.1.ronn
|
||||
$(RONN) -r $<
|
||||
$(RONN) --date=$(ISOFMT) --manual=$(MANUAL) --organization=$(ORG) --roff $<
|
||||
|
||||
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