upgrate our bats-core to v1.6.0 (#803)

pull/804/head
Josh Rabinowitz 2 years ago committed by GitHub
parent eefa10623a
commit c7325b2d9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

9
vendor/README.md vendored

@ -1,15 +1,10 @@
README for git-secret/vendor directory
We import bats-core v1.3.0 here for
We import bats-core 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.
If you want upgrade bats-core used by git-secret, 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`

@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Type: Bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- Bats Version [e.g. 1.4.0 or commit hash]
- OS: [e.g. Linux, FreeBSD, MacOS]
- Bash version: [e.g. 5.1]
**Additional context**
Add any other context about the problem here.

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'Type: Enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context about the feature request here.

@ -0,0 +1,10 @@
#!/usr/bin/bash
get_pr_json() {
curl -s -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/bats-core/bats-core/pulls/$1"
}
PR_NUMBER="$1"
LABEL="$2"
get_pr_json "$PR_NUMBER" | jq .labels[].name | grep "$LABEL"

@ -0,0 +1,30 @@
name: Release
on:
release: { types: [published] }
workflow_dispatch:
jobs:
npmjs:
runs-on: ubuntu-latest
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
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 }}

@ -0,0 +1,38 @@
name: Release to docker hub
on:
release: { types: [published] }
workflow_dispatch:
inputs:
version:
description: 'Version to simulate for deploy'
required: true
jobs:
dockerhub:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: version
run: |
EXPECTED_VERSION=${{ github.event.inputs.version }}
TAG_VERSION=${GITHUB_REF#refs/tags/v} # refs/tags/v1.2.3 -> 1.2.3
echo ::set-output name=version::${EXPECTED_VERSION:-$TAG_VERSION}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
- uses: docker/build-push-action@v2
with:
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7,linux/arm/v6
tags: ${{ secrets.DOCKER_USERNAME }}/bats:${{ steps.version.outputs.version }},${{ secrets.DOCKER_USERNAME }}/bats:latest
push: true

@ -0,0 +1,167 @@
name: Tests
# Controls when the action will run.
on: [push, pull_request, workflow_dispatch]
jobs:
changelog:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Check that PR is mentioned in Changelog
run: |
if ! ./.github/workflows/check_pr_label.sh "${{github.event.pull_request.number}}" "no changelog"; then
grep "#${{github.event.pull_request.number}}" docs/CHANGELOG.md
fi
if: ${{github.event.pull_request}}
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']
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 --print-output-on-failure --formatter tap test"
npm_on_linux:
strategy:
matrix:
os: ['ubuntu-20.04', 'ubuntu-18.04']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@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: |
npm pack ./
sudo npm install -g ./bats-*.tgz
bats test
windows:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- run: |
bash --version
bash -c "time bin/bats --print-output-on-failure --formatter tap test"
npm_on_windows:
strategy:
matrix:
os: ['windows-2019']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm pack ./
- run: npm install -g (get-item .\bats-*.tgz).FullName
- run: bats -T --print-output-on-failure 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 --print-output-on-failure --formatter tap test"
npm_on_macos:
strategy:
matrix:
os: ['macos-10.15']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@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: |
npm pack ./
# somehow there is already an intalled bats version around
npm install --force -g ./bats-*.tgz
bats --print-output-on-failure 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 }}" --print-output-on-failure --tap /opt/bats/test
alpine:
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
time docker run -it -v $PWD:/opt/bats alpine sh -c "apk add bash ncurses; /opt/bats/bin/bats --print-output-on-failure --tap /opt/bats/test"
freebsd:
runs-on: macos-10.15
strategy:
matrix:
packages:
- flock
- ""
steps:
- uses: actions/checkout@v2
- uses: vmactions/freebsd-vm@v0.1.5
with:
prepare: pkg install -y bash parallel ${{ matrix.packages }}
run: |
time ./bin/bats --print-output-on-failure test/

@ -11,8 +11,6 @@
Bats is a [TAP](https://testanything.org/)-compliant testing framework for Bash. It provides a simple
way to verify that the UNIX programs you write behave as expected.
[TAP]: https://testanything.org
A Bats test file is a Bash script with special syntax for defining test cases.
Under the hood, each test case is just a function with a description.
@ -86,8 +84,7 @@ or look at the other communication channels.
## Contact
- We are `#bats` on freenode;
- Or leave a message on [gitter].
- You can find and chat with us on our [Gitter].
## Version history
@ -127,4 +124,4 @@ Bats is released under an MIT-style license; see `LICENSE.md` for details.
See the [parent project](https://github.com/bats-core) at GitHub or the
[AUTHORS](AUTHORS) file for the current project maintainer team.
[gitter]: https://gitter.im/bats-core/bats-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[gitter]: https://gitter.im/bats-core/bats-core

@ -3,7 +3,7 @@
%global repo bats-core
Name: bats
Version: 1.5.0
Version: 1.6.0
Release: 1%{?dist}
Summary: Bash Automated Testing System

@ -8,6 +8,35 @@ The format is based on [Keep a Changelog][kac] and this project adheres to
[kac]: https://keepachangelog.com/en/1.0.0/
[semver]: https://semver.org/
## [Unreleased]
## [1.6.0] - 2022-02-24
### Added
* new flag `--code-quote-style` (and `$BATS_CODE_QUOTE_STYLE`) to customize
quotes around code blocks in error output (#506)
* an example/regression test for running background tasks without blocking the
test run (#525, #535)
* `bats_load_library` for loading libraries from the search path
`$BATS_LIB_PATH` (#548)
### Fixed
* improved error trace for some broken cases (#279)
* removed leftover debug file `/tmp/latch` in selftest suite
(single use latch) (#516)
* fix recurring errors on CTRL+C tests with NPM on Windows in selftest suite (#516)
* fixed leaking of local variables from debug trap (#520)
* don't mark FD3 output from `teardown_file` as `<failure>` in junit output (#532)
* fix unbound variable error with Bash pre 4.4 (#550)
#### Documentation
* remove links to defunct freenode IRC channel (#515)
* improved grammar (#534)
* fixed link to TAP spec (#537)
## [1.5.0] - 2021-10-22
### Added

@ -57,8 +57,7 @@ CONTRIBUTING.md file][atom].
## Quick links &#x1f517;
- [Gitter channel →][gitterurl]: These messages sync with the IRC channel
- [IRC Channel (#bats on freenode) →][ircurl]: These messages sync with Gitter
- [Gitter channel →][gitterurl]: Feel free to come chat with us on Gitter
- [README →][README]
- [Code of conduct →][CODE_OF_CONDUCT]
- [License information →][LICENSE]
@ -101,7 +100,7 @@ specifics, see the [CODE_OF_CONDUCT][] file.
Please check the [README][] or existing [issues][repoissues] first.
If you cannot find an answer to your question, please feel free to hop on our
[gitter][gitterurl] [![Gitter](https://badges.gitter.im/bats-core/bats-core.svg)](https://gitter.im/bats-core/bats-core?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) or [via IRC (#bats on freenode)][ircurl].
[Gitter][gitterurl]. [![Gitter](https://badges.gitter.im/bats-core/bats-core.svg)](https://gitter.im/bats-core/bats-core)
### Reporting issues
@ -391,4 +390,3 @@ by the [Free Software Foundation](https://www.fsf.org/), 2016 under the [Free Ar
[osmit]: https://opensource.org/licenses/MIT
[gitterurl]: https://gitter.im/bats-core/bats-core
[ircurl]: https://kiwiirc.com/client/irc.freenode.net:+6697/#bats

@ -18,8 +18,8 @@
# -- Project information -----------------------------------------------------
project = 'bats-core'
copyright = '2021, bats-core origanization'
author = 'bats-core origanization'
copyright = '2021, bats-core organization'
author = 'bats-core organization'
# The full version, including alpha/beta/rc tags
release = '1'
@ -69,4 +69,4 @@ 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)
app.add_transform(AutoStructify)

@ -6,7 +6,7 @@ How do I set the working directory?
The working directory is simply the directory where you started when executing bats.
If you want to enforce a specific directory, you can use `cd` in the `setup_file`/`setup` functions.
However, be aware that code outside any function will run before any of these setup functions and my interfere with bats' internals.
However, be aware that code outside any function will run before any of these setup functions and might interfere with bats' internals.
How do I see the output of the command under `run` when a test fails?
@ -166,4 +166,4 @@ There is also `BATS_TEST_SKIPPED` which will be non-empty (contains the skip mes
How can I setup/cleanup before/after all tests?
-----------------------------------------------
Currently, this is not supported. Please contribute your usecase to issue `#39 <https://github.com/bats-core/bats-core/issues/39>`_.
Currently, this is not supported. Please contribute your usecase to issue `#39 <https://github.com/bats-core/bats-core/issues/39>`_.

@ -118,3 +118,14 @@ don't abort the test when they fail, unless they are the last command before the
making their exit code the return code.
`[ ]` does not suffer from this, but is no replacement for all `[[ ]]` usecases. Appending ` || false` will work in all cases.
Background tasks prevent the test run from terminating when finished
--------------------------------------------------------------------
When running a task in background, it will inherit the opened FDs of the process it was forked from.
This means that the background task forked from a Bats test will hold the FD for the pipe to the formatter that prints to the terminal,
thus keeping it open until the background task finished.
Due to implementation internals of Bats and bash, this pipe might be held in multiple FDs which all have to be closed by the background task.
You can use `close_non_std_fds from `test/fixtures/bats/issue-205.bats` in the background job to close all FDs except stdin, stdout and stderr, thus solving the problem.
More details about the issue can be found in [#205](https://github.com/bats-core/bats-core/issues/205#issuecomment-973572596).

@ -32,7 +32,7 @@ $ bats addition.bats
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].
displayed in human-readable, machine-parsable [TAP format][https://testanything.org].
You can force TAP output from a terminal by invoking Bats with the `--formatter tap`
option.

@ -12,6 +12,23 @@ For sample test files, see [examples](https://github.com/bats-core/bats-core/tre
[bats-eval]: https://github.com/bats-core/bats-core/wiki/Bats-Evaluation-Process
## 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`
alternative:
```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`.
## `run`: Test other commands
Many Bats tests need to run a command and then make assertions about its exit
@ -80,29 +97,13 @@ All additional parameters to run should come before the command.
If you want to run a command that starts with `-`, prefix it with `--` to
prevent `run` from parsing it as an option.
## 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`
alternative:
```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
You may want to share common code across multiple test files. Bats
includes a convenient `load` command for sourcing a Bash source files
relative to the current test file and from absolute paths.
For example, if you have a Bats test in `test/foo.bats`, the command
```bash
load test_helper.bash
@ -111,18 +112,83 @@ 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.
resolving paths.
If `load` encounters errors - e.g. because the targeted source file
errored - it will print a message with the failing library and Bats
exits.
As pointed out by @iatrou in <https://www.tldp.org/LDP/abs/html/declareref.html>,
To allow to use `load` in conditions `bats_load_safe` has been added.
`bats_load_safe` prints a message and returns `1` if a source file cannot be
loaded instead of exiting Bats.
Aside from that `bats_load_safe` acts exactly like `load`.
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`.
### `load` argument resolution
`load` supports the following arguments:
- absolute paths
- relative paths (to the current test file)
> 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.
If `argument` is an absolute path `load` tries to determine the load
path directly.
If `argument` is a relative path or a name `load` looks for a matching
path in the directory of the current test.
## `bats_load_library`: Load system wide libraries
Some libraries are installed on the system, e.g. by `npm` or `brew`.
These should not be `load`ed, as their path depends on the installation method.
Instead, one should use `bats_load_library` together with setting
`BATS_LIB_PATH`, a `PATH`-like colon-delimited variable.
`bats_load_library` has two modes of resolving requests:
1. by relative path from the `BATS_LIB_PATH` to a file in the library
2. by library name, expecting libraries to have a `load.bash` entrypoint
For example if your `BATS_LIB_PATH` is set to
`~/.bats/libs:/usr/lib/bats`, then `bats_load_library test_helper`
would look for existing files with the following paths:
- `~/.bats/libs/test_helper`
- `~/.bats/libs/test_helper/load.bash`
- `/usr/lib/bats/test_helper`
- `/usr/lib/bats/test_helper/load.bash`
The first existing file in this list will be sourced.
If you want to load only part of a library or the entry point is not named `load.bash`,
you have to include it in the argument:
`bats_load_library library_name/file_to_load` will try
- `~/.bats/libs/library_name/file_to_load`
- `~/.bats/libs/library_name/file_to_load/load.bash`
- `/usr/lib/bats/library_name/file_to_load`
- `/usr/lib/bats/library_name/file_to_load/load.bash`
Apart from the changed lookup rules, `bats_load_library` behaves like `load`.
__Note:__ As seen above `load.bash` is the entry point for libraries and
meant to load more files from its directory or other libraries.
__Note:__ Obviously, the actual `BATS_LIB_PATH` is highly dependant on the environment.
To maintain a uniform location across systems, (distribution) package maintainers
are encouraged to use `/usr/lib/bats/` as the install path for libraries where possible.
However, if the package manager has another preferred location, like `npm` or `brew`,
you should use this instead.
## `skip`: Easily skip tests
Tests can be skipped by using the `skip` command at the point in a test you wish
@ -225,7 +291,7 @@ 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
Bats produces output compliant with [version 12 of the TAP protocol](https://testanything.org/tap-specification.html). 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.

@ -7,4 +7,18 @@ bats_prefix_lines_for_tap_output() {
if [[ -n "$line" ]]; then
printf '# %s\n' "$line"
fi
}
}
function bats_replace_filename() {
local line
while read -r line; do
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
done
if [[ -n "$line" ]]; then
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
fi
}
bats_quote_code() { # <var> <code>
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
}

@ -43,15 +43,16 @@ function bats_parse_internal_extended_tap() {
;;
'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
scope=skipped
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
scope=ok
if [[ "$line" =~ $timing_expr ]]; then
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
else

@ -3,30 +3,162 @@
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}"
# find_in_bats_lib_path echoes the first recognized load path to
# a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
#
# Libraries relative to BATS_TEST_DIRNAME take precedence over
# BATS_LIB_PATH.
#
# Library load paths are recognized using find_library_load_path.
#
# If no library is found find_in_bats_lib_path returns 1.
find_in_bats_lib_path() { # <return-var> <library-name>
local return_var="${1:?}"
local library_name="${2:?}"
local -a bats_lib_paths
IFS=: read -ra bats_lib_paths <<< "$BATS_LIB_PATH"
for path in "${bats_lib_paths[@]}"; do
if [[ -f "$path/$library_name" ]]; then
printf -v "$return_var" "%s" "$path/$library_name"
# A library load path was found, return
return
elif [[ -f "$path/$library_name/load.bash" ]]; then
printf -v "$return_var" "%s" "$path/$library_name/load.bash"
# A library load path was found, return
return
fi
done
return 1
}
# bats_internal_load expects an absolute path that is a library load path.
#
# If the library load path points to a file (a library loader) it is
# sourced.
#
# If it points to a directory all files ending in .bash inside of the
# directory are sourced.
#
# If the sourcing of the library loader or of a file in a library
# directory fails bats_internal_load prints an error message and returns 1.
#
# If the passed library load path is not absolute or is not a valid file
# or directory bats_internal_load prints an error message and returns 1.
bats_internal_load() {
local library_load_path="${1:?}"
if [[ "${library_load_path:0:1}" != / ]]; then
printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
return 1
fi
# library_load_path is a library loader
if [[ -f "$library_load_path" ]]; then
# shellcheck disable=SC1090
if ! source "$library_load_path"; then
printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
return 1
fi
return
fi
printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
return 1
}
# bats_load_safe accepts an argument called 'slug' and attempts to find and
# source a library based on the slug.
#
# A slug can be an absolute path, a library name or a relative path.
#
# If the slug is an absolute path bats_load_safe attempts to find the library
# load path using find_library_load_path.
# What is considered a library load path is documented in the
# documentation for find_library_load_path.
#
# If the slug is not an absolute path it is considered a library name or
# relative path. bats_load_safe attempts to find the library load path using
# find_in_bats_lib_path.
#
# If bats_load_safe can find a library load path it is passed to bats_internal_load.
# If bats_internal_load fails bats_load_safe returns 1.
#
# If no library load path can be found bats_load_safe prints an error message
# and returns 1.
bats_load_safe() {
local slug="${1:?}"
if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
slug="$BATS_TEST_DIRNAME/$slug"
fi
if [[ ! -f "$file" ]] && ! type -P "$file" >/dev/null; then
printf 'bats: %s does not exist\n' "$file" >&2
if [[ -f "$slug.bash" ]]; then
bats_internal_load "$slug.bash"
return
elif [[ -f "$slug" ]]; then
bats_internal_load "$slug"
return
fi
# loading from PATH (retained for backwards compatibility)
if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
# shellcheck disable=SC1090
source "$1"
return
fi
# No library load path can be found
printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
return 1
}
bats_require_lib_path() {
if [[ -z "${BATS_LIB_PATH:-}" ]]; then
printf "%s: requires BATS_LIB_PATH to be set!\n" "${FUNCNAME[1]}" >&2
exit 1
fi
}
bats_load_library_safe() { # <slug>
local slug="${1:?}" library_path
bats_require_lib_path
# Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
if [[ ${slug:0:1} != / ]]; then
find_in_bats_lib_path library_path "$slug"
if [[ -z "$library_path" ]]; then
printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
return 1
fi
else
# absolute paths are taken as is
library_path="$slug"
if [[ ! -f "$library_path" ]]; then
printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
return 1
fi
fi
# Dynamically loaded user file provided outside of Bats.
# Note: 'source "$file" || exit' doesn't work on bash3.2.
# shellcheck disable=SC1090
source "${file}"
bats_internal_load "$library_path"
return $?
}
# immediately exit on error, use bats_load_library_safe to catch and handle errors
bats_load_library() { # <slug>
bats_require_lib_path
if ! bats_load_library_safe "$@"; then
exit 1
fi
}
# load acts like bats_load_safe but exits the shell instead of returning 1.
load() {
if ! bats_load_safe "$@"; then
exit 1
fi
}
bats_redirect_stderr_into_file() {
@ -39,8 +171,8 @@ bats_merge_stdout_and_stderr() {
# write separate lines from <input-var> into <output-array>
bats_separate_lines() { # <output-array> <input-var>
output_array_name="$1"
input_var_name="$2"
local output_array_name="$1"
local input_var_name="$2"
if [[ $keep_empty_lines ]]; then
local bats_separate_lines_lines=()
while IFS= read -r line; do
@ -49,11 +181,14 @@ bats_separate_lines() { # <output-array> <input-var>
eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
else
# shellcheck disable=SC2034,SC2206
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}"
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
fi
}
run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
# This has to be restored on exit from this function to avoid leaking our trap INT into surrounding code.
# Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
# which allows us to avoid duplicating the restore code on every exit path
trap bats_interrupt_trap_in_run INT
local expected_rc=
local keep_empty_lines=
@ -84,6 +219,10 @@ run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run..
shift # eat the -- before breaking away
break
;;
*)
printf "Usage error: unknown flag '%s'" "$1" >&2
return 1
;;
esac
shift
done
@ -104,17 +243,16 @@ run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run..
local origFlags="$-"
set +eET
local origIFS="$IFS"
status=0
if [[ $keep_empty_lines ]]; then
# 'output', 'status', 'lines' are global variables available to tests.
# preserve trailing newlines by appending . and removing it later
# shellcheck disable=SC2034
output="$($pre_command "$@"; status=$?; printf .; exit $status)" || status="$?"
output="$($pre_command "$@"; status=$?; printf .; exit $status)" && status=0 || status=$?
output="${output%.}"
else
# 'output', 'status', 'lines' are global variables available to tests.
# shellcheck disable=SC2034
output="$($pre_command "$@")" || status="$?"
output="$($pre_command "$@")" && status=0 || status=$?
fi
bats_separate_lines lines output
@ -137,17 +275,17 @@ run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run..
if [[ -n "$expected_rc" ]]; then
if [[ "$expected_rc" = "-1" ]]; then
if [[ "$status" -eq 0 ]]; then
bats_capture_stack_trace # fix backtrace
BATS_ERROR_SUFFIX=", expected nonzero exit code!"
return 1
fi
elif [ "$status" -ne "$expected_rc" ]; then
bats_capture_stack_trace # fix backtrace
# shellcheck disable=SC2034
BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
return 1
fi
fi
# don't leak our trap into surrounding code
trap bats_interrupt_trap INT
}
setup() {

@ -5,21 +5,14 @@ bats_capture_stack_trace() {
local funcname
local i
# 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_STACK_TRACE=()
BATS_DEBUG_LAST_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")
BATS_DEBUG_LAST_STACK_TRACE+=("${BASH_LINENO[$((i-1))]} $funcname $test_file")
case "$funcname" in
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file)
break
@ -31,6 +24,22 @@ bats_capture_stack_trace() {
done
}
bats_get_failure_stack_trace() {
local stack_trace_var
# See bats_debug_trap for details.
if [[ -n "${BATS_DEBUG_LAST_STACK_TRACE_IS_VALID}" ]]; then
stack_trace_var=BATS_DEBUG_LAST_STACK_TRACE
else
stack_trace_var=BATS_DEBUG_LASTLAST_STACK_TRACE
fi
# shellcheck disable=SC2016
eval "$(printf \
'%s=(${%s[@]+"${%s[@]}"})' \
"${1}" \
"${stack_trace_var}" \
"${stack_trace_var}")"
}
bats_print_stack_trace() {
local frame
local index=1
@ -55,7 +64,9 @@ bats_print_stack_trace() {
# don't print "from function `source'"",
# when failing in free code during `source $test_file` from bats-exec-file
! [[ "$fn" == 'source' && $index -eq $count ]]; then
printf "from function \`%s' " "$fn"
local quoted_fn
bats_quote_code quoted_fn "$fn"
printf "from function %s " "$quoted_fn"
fi
if [[ $index -eq $count ]]; then
@ -69,10 +80,11 @@ bats_print_stack_trace() {
}
bats_print_failed_command() {
if [[ ${#BATS_STACK_TRACE[@]} -eq 0 ]]; then
local stack_trace=("${@}")
if [[ ${#stack_trace[@]} -eq 0 ]]; then
return
fi
local frame="${BATS_STACK_TRACE[${#BATS_STACK_TRACE[@]} - 1]}"
local frame="${stack_trace[${#stack_trace[@]} - 1]}"
local filename
local lineno
local failed_line
@ -82,7 +94,9 @@ bats_print_failed_command() {
bats_frame_lineno "$frame" 'lineno'
bats_extract_line "$filename" "$lineno" 'failed_line'
bats_strip_string "$failed_line" 'failed_command'
printf '%s' "# \`${failed_command}' "
local quoted_failed_command
bats_quote_code quoted_failed_command "$failed_command"
printf '# %s ' "${quoted_failed_command}"
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
printf 'failed%s\n' "$BATS_ERROR_SUFFIX"
@ -134,8 +148,10 @@ bats_trim_filename() {
# normalize a windows path from e.g. C:/directory to /c/directory
# The path must point to an existing/accessable directory, not a file!
bats_normalize_windows_dir_path() { # <output-var> <path>
local output_var="$1"
local path="$2"
local output_var="$1" path="$2"
if [[ "$output_var" != NORMALIZED_INPUT ]]; then
local NORMALIZED_INPUT
fi
if [[ $path == ?:* ]]; then
NORMALIZED_INPUT="$(cd "$path" || exit 1; pwd)"
else
@ -169,6 +185,52 @@ bats_emit_trace() {
fi
}
# bats_debug_trap tracks the last line of code executed within a test. This is
# necessary because $BASH_LINENO is often incorrect inside of ERR and EXIT
# trap handlers.
#
# Below are tables describing different command failure scenarios and the
# reliability of $BASH_LINENO within different the executed DEBUG, ERR, and EXIT
# trap handlers. Naturally, the behaviors change between versions of Bash.
#
# Table rows should be read left to right. For example, on bash version
# 4.0.44(2)-release, if a test executes `false` (or any other failing external
# command), bash will do the following in order:
# 1. Call the DEBUG trap handler (bats_debug_trap) with $BASH_LINENO referring
# to the source line containing the `false` command, then
# 2. Call the DEBUG trap handler again, but with an incorrect $BASH_LINENO, then
# 3. Call the ERR trap handler, but with a (possibly-different) incorrect
# $BASH_LINENO, then
# 4. Call the DEBUG trap handler again, but with $BASH_LINENO set to 1, then
# 5. Call the EXIT trap handler, with $BASH_LINENO set to 1.
#
# bash version 4.4.20(1)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | OK | OK | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# (( 1 = 2 )) | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# ! true | OK | --- | BAD[4] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# bash version 4.0.44(2)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | BAD[3] | BAD[3] | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | --- | BAD[3] | --- | BAD[1]
# (( 1 = 2 )) | OK | --- | BAD[3] | --- | BAD[1]
# ! true | OK | --- | BAD[3] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# [1] The reported line number is always 1.
# [2] The reported source location is that of the beginning of the function
# calling the command.
# [3] The reported line is that of the last command executed in the DEBUG trap
# handler.
# [4] The reported source location is that of the call to the function calling
# the command.
bats_debug_trap() {
# on windows we sometimes get a mix of paths (when install via nmp install -g)
# which have C:/... or /c/... comparing them is going to be problematic.
@ -176,7 +238,7 @@ bats_debug_trap() {
local NORMALIZED_INPUT
bats_normalize_windows_dir_path NORMALIZED_INPUT "${1%/*}"
local file_excluded='' path
for path in "${_BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
for path in "${BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
if [[ "$NORMALIZED_INPUT" == "$path"* ]]; then
file_excluded=1
break
@ -186,6 +248,12 @@ bats_debug_trap() {
# don't update the trace within library functions or we get backtraces from inside traps
# also don't record new stack traces while handling interruptions, to avoid overriding the interrupted command
if [[ -z "$file_excluded" && "${BATS_INTERRUPTED-NOTSET}" == NOTSET ]]; then
BATS_DEBUG_LASTLAST_STACK_TRACE=(
${BATS_DEBUG_LAST_STACK_TRACE[@]+"${BATS_DEBUG_LAST_STACK_TRACE[@]}"}
)
BATS_DEBUG_LAST_LINENO=(${BASH_LINENO[@]+"${BASH_LINENO[@]}"})
BATS_DEBUG_LAST_SOURCE=(${BASH_SOURCE[@]+"${BASH_SOURCE[@]}"})
bats_capture_stack_trace
bats_emit_trace
fi
@ -195,21 +263,19 @@ bats_debug_trap() {
# 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.
# For this reason, we call `bats_check_status_from_trap` at the very beginning
# of `bats_teardown_trap` 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() {
bats_check_status_from_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
}
@ -220,15 +286,16 @@ bats_add_debug_exclude_path() { # <path>
return 1
fi
if [[ "$OSTYPE" == cygwin || "$OSTYPE" == msys ]]; then
local normalized_dir
bats_normalize_windows_dir_path normalized_dir "$1"
_BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
else
_BATS_DEBUG_EXCLUDE_PATHS+=("$1")
BATS_DEBUG_EXCLUDE_PATHS+=("$1")
fi
}
bats_setup_tracing() {
_BATS_DEBUG_EXCLUDE_PATHS=()
BATS_DEBUG_EXCLUDE_PATHS=()
# exclude some paths by default
bats_add_debug_exclude_path "$BATS_ROOT/lib/"
bats_add_debug_exclude_path "$BATS_ROOT/libexec/"
@ -247,6 +314,7 @@ bats_setup_tracing() {
done < <(find "$PWD" -type d -name bats-assert -o -name bats-support)
fi
local exclude_paths path
# exclude user defined libraries
IFS=':' read -r exclude_paths <<< "${BATS_DEBUG_EXCLUDE_PATHS:-}"
for path in "${exclude_paths[@]}"; do
@ -258,4 +326,37 @@ bats_setup_tracing() {
# turn on traps after setting excludedes to avoid tracing the exclude setup
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
trap 'bats_error_trap' ERR
}
}
bats_error_trap() {
bats_check_status_from_trap
# If necessary, undo the most recent stack trace captured by bats_debug_trap.
# See bats_debug_trap for details.
if [[ "${BASH_LINENO[*]}" = "${BATS_DEBUG_LAST_LINENO[*]:-}"
&& "${BASH_SOURCE[*]}" = "${BATS_DEBUG_LAST_SOURCE[*]:-}"
&& -z "$BATS_DEBUG_LAST_STACK_TRACE_IS_VALID" ]]; then
BATS_DEBUG_LAST_STACK_TRACE=(
${BATS_DEBUG_LASTLAST_STACK_TRACE[@]+"${BATS_DEBUG_LASTLAST_STACK_TRACE[@]}"}
)
fi
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
}
bats_interrupt_trap() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
# debug trap fires before interrupt trap but gets wrong linenumber (line 1)
# -> use last last stack trace
exit $BATS_ERROR_STATUS
}
# this is used inside run()
bats_interrupt_trap_in_run() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=true
exit $BATS_ERROR_STATUS
}

@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -e
export BATS_VERSION='1.5.0'
export BATS_VERSION='1.6.0'
VALID_FORMATTERS="pretty, junit, tap, tap13"
version() {
@ -29,6 +29,10 @@ HELP_TEXT_HEADER
containing Bats test files (ending with ".bats")
-c, --count Count test cases without running any tests
--code-quote-style <style>
A two character string of code quote delimiters
or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and
$BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE
-f, --filter <regex> Only run tests that match the regular expression
-F, --formatter <type> Switch between formatters: pretty (default),
tap (default w/o term), tap13, junit
@ -220,6 +224,10 @@ while [[ "$#" -ne 0 ]]; do
fi
flags+=(--gather-test-outputs-in "$output_dir")
;;
--code-quote-style)
shift
BATS_CODE_QUOTE_STYLE="$1"
;;
-*)
abort "Bad command line option '$1'"
;;
@ -298,6 +306,29 @@ if [[ -n "$report_formatter" ]]; then
esac
fi
if [[ "${BATS_CODE_QUOTE_STYLE-BATS_CODE_QUOTE_STYLE_UNSET}" == BATS_CODE_QUOTE_STYLE_UNSET ]]; then
BATS_CODE_QUOTE_STYLE="\`'"
fi
case "${BATS_CODE_QUOTE_STYLE}" in
??)
BATS_BEGIN_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE::1}"
BATS_END_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE:1:1}"
export BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
;;
custom)
if [[ ${BATS_BEGIN_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET
|| ${BATS_END_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET ]]; then
printf "ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set\n" >&2
exit 1
fi
;;
*)
printf "ERROR: Unknown BATS_CODE_QUOTE_STYLE: %s\n" "$BATS_CODE_QUOTE_STYLE" >&2
exit 1
;;
esac
if [[ -n "$output" ]]; then
if [[ ! -w "${output}" ]]; then
abort "Output path ${output} is not writeable"

@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -eET
export flags=()
num_jobs=1
export flags=('--dummy-flag')
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
filter=''
extended_syntax=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
@ -80,10 +80,6 @@ bats_run_setup_file() {
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
@ -102,29 +98,18 @@ bats_run_setup_file() {
local status=0
# get the setup_file/teardown_file functions for this file (if it has them)
# shellcheck disable=SC1090
source "$BATS_TEST_SOURCE" > >(bats_replace_filename) 2>&1
source "$BATS_TEST_SOURCE" >>"$BATS_OUT" 2>&1
BATS_SOURCE_FILE_COMPLETED=1
setup_file > >(bats_replace_filename >>"$BATS_OUT") 2>&1
setup_file >>"$BATS_OUT" 2>&1
BATS_SETUP_FILE_COMPLETED=1
}
function bats_replace_filename() {
local line
while read -r line; do
printf "%s\n" "${line//$BATS_TEST_SOURCE/$filename}"
done
if [[ -n "$line" ]]; then
printf "%s\n" "${line//$BATS_TEST_SOURCE/$filename}"
fi
}
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
@ -132,10 +117,8 @@ bats_run_teardown_file() {
}
bats_file_teardown_trap() {
bats_error_trap
local status=0
bats_run_teardown_file
local status=0
bats_file_exit_trap
}
@ -155,9 +138,11 @@ bats_file_exit_trap() {
FAILURE_REASON='unknown internal'
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
bats_prefix_lines_for_tap_output < "$BATS_OUT" >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
rm -rf "$BATS_OUT"
status=1
fi
@ -325,8 +310,16 @@ fi
bats_create_file_tempdirs
bats_preprocess_source "$filename"
trap bats_interrupt_trap INT
bats_run_setup_file
# during tests, we don't want to get backtraces from this level
# just wait for the test to be interrupted and display their trace
trap 'BATS_INTERRUPTED=true' INT
bats_run_tests
trap bats_interrupt_trap INT
bats_run_teardown_file
exit $status

@ -3,7 +3,7 @@ set -e
count_only_flag=''
filter=''
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS-1}
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
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

@ -30,6 +30,8 @@ while [[ "$#" -ne 0 ]]; do
# shellcheck disable=SC2034
BATS_EXTENDED_SYNTAX='-x'
;;
--dummy-flag)
;;
--trace)
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
;;
@ -91,7 +93,7 @@ source "$BATS_ROOT/lib/bats-core/test_functions.bash"
source "$BATS_ROOT/lib/bats-core/tracing.bash"
bats_teardown_trap() {
bats_error_trap
bats_check_status_from_trap
local status=0
# mark the start of this function to distinguish where skip is called
# parameter 1 will signify the reason why this function was called
@ -102,6 +104,7 @@ bats_teardown_trap() {
if [[ $status -eq 0 ]]; then
BATS_TEARDOWN_COMPLETED=1
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
BATS_ERROR_STATUS="$status"
fi
@ -112,7 +115,6 @@ bats_teardown_trap() {
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_exit_trap() {
local line
local status
local skipped=''
trap - ERR EXIT
@ -142,12 +144,13 @@ bats_exit_trap() {
# If instead the test fails, and the `teardown()` error happens while
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
# output, since there's no way to reach the `bats_exit_trap` call.
BATS_STACK_TRACE=("${BATS_CURRENT_STACK_TRACE[@]}")
BATS_ERROR_STATUS=1
fi
printf 'not ok %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
bats_print_stack_trace "${BATS_STACK_TRACE[@]}" >&3
bats_print_failed_command >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE && -n "${output:-}" ]]; then
printf "Last output:\n%s\n" "$output" >> "$BATS_OUT"
@ -162,7 +165,7 @@ bats_exit_trap() {
fi
if [[ $print_bats_out ]]; then
bats_prefix_lines_for_tap_output < "$BATS_OUT" >&3
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
fi
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
@ -187,19 +190,31 @@ get_mills_since_epoch() {
bats_perform_test() {
if ! declare -F "$BATS_TEST_NAME" &>/dev/null; then
printf "bats: unknown test name \`%s'\n" "$BATS_TEST_NAME" >&2
local quoted_test_name
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
exit 1
fi
# Some versions of Bash will reset BASH_LINENO to the first line of the
# function when the ERR trap fires. All versions of Bash appear to reset it
# on an unbound variable access error. bats_debug_trap will fire both before
# the offending line is executed, and when the error is triggered.
# Consequently, we use `BATS_CURRENT_STACK_TRACE` recorded by the
# first call to bats_debug_trap, _before_ the ERR trap or unbound variable
# access fires.
BATS_STACK_TRACE=()
BATS_CURRENT_STACK_TRACE=()
# Variables for capturing accurate stack traces. See bats_debug_trap for
# details.
#
# BATS_DEBUG_LAST_LINENO, BATS_DEBUG_LAST_SOURCE, and
# BATS_DEBUG_LAST_STACK_TRACE hold data from the most recent call to
# bats_debug_trap.
#
# BATS_DEBUG_LASTLAST_STACK_TRACE holds data from two bats_debug_trap calls
# ago.
#
# BATS_DEBUG_LAST_STACK_TRACE_IS_VALID indicates that
# BATS_DEBUG_LAST_STACK_TRACE contains the stack trace of the test's error. If
# unset, BATS_DEBUG_LAST_STACK_TRACE is unreliable and
# BATS_DEBUG_LASTLAST_STACK_TRACE should be used instead.
BATS_DEBUG_LASTLAST_STACK_TRACE=()
BATS_DEBUG_LAST_LINENO=()
BATS_DEBUG_LAST_SOURCE=()
BATS_DEBUG_LAST_STACK_TRACE=()
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
BATS_TEST_COMPLETED=
BATS_TEST_SKIPPED=
@ -218,22 +233,6 @@ bats_perform_test() {
bats_teardown_trap "" # pass empty parameter to signify call outside trap
}
bats_interrupt_trap() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
exit 130
}
# keep in sync with bats_interrupt_trap
# this is used inside run()
bats_interrupt_trap_in_run() {
BATS_INTERRUPTED=true
# we don't get a second stack trace when interrupted in run,
# so we have to use the first!
BATS_CURRENT_STACK_TRACE=("${BATS_STACK_TRACE[@]+"${BATS_STACK_TRACE[@]}"}")
exit 130
}
trap bats_interrupt_trap INT
# shellcheck source=lib/bats-core/preprocessing.bash

@ -220,13 +220,21 @@ bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name
}
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
local comment="$1" scope="$2"
case "$scope" in
begin)
# everything that happens between begin and [not] ok is FD3 output from the test
log_system_out "$comment"
;;
ok)
# non failed tests can produce FD3 output
log_system_out "$comment"
;;
*)
# everything else is considered error output
log "$1"
;;
esac
}
bats_tap_stream_suite() { # <file name>

5
vendor/bats-core/man/bats.1 generated vendored

@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BATS" "1" "August 2021" "bats-core" "Bash Automated Testing System"
.TH "BATS" "1" "November 2021" "bats-core" "Bash Automated Testing System"
.
.SH "NAME"
\fBbats\fR \- Bash Automated Testing System
@ -36,6 +36,9 @@ You can invoke the \fBbats\fR interpreter with multiple test file arguments, or
\fB\-c\fR, \fB\-\-count\fR: Count the number of test cases without running any tests
.
.IP "\(bu" 4
\fB\-\-code\-quote\-style <style>\fR: A two character string of code quote delimiters or \fBcustom\fR which requires setting \fB$BATS_BEGIN_CODE_QUOTE\fR and \fB$BATS_END_CODE_QUOTE\fR\. Can also be set via \fB$BATS_CODE_QUOTE_STYLE\fR\.
.
.IP "\(bu" 4
\fB\-f\fR, \fB\-\-filter <regex>\fR: Filter test cases by names matching the regular expression
.
.IP "\(bu" 4

@ -49,6 +49,11 @@ OPTIONS
* `-c`, `--count`:
Count the number of test cases without running any tests
* `--code-quote-style <style>`:
A two character string of code quote delimiters or `custom`
which requires setting `$BATS_BEGIN_CODE_QUOTE` and
`$BATS_END_CODE_QUOTE`.
Can also be set via `$BATS_CODE_QUOTE_STYLE`.
* `-f`, `--filter <regex>`:
Filter test cases by names matching the regular expression
* `-F`, `--formatter <type>`:
@ -88,7 +93,6 @@ OPTIONS
* `-v`, `--version`:
Display the version number
OUTPUT
------

34
vendor/bats-core/man/bats.7 generated vendored

@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "BATS" "7" "August 2021" "bats-core" "Bash Automated Testing System"
.TH "BATS" "7" "November 2021" "bats-core" "Bash Automated Testing System"
.
.SH "NAME"
\fBbats\fR \- Bats test file format
@ -33,7 +33,7 @@ A Bats test file is a Bash script with special syntax for defining test cases\.
Each Bats test file is evaluated n+1 times, where \fIn\fR 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\.
.
.SH "THE RUN HELPER"
Usage: run [OPTIONS] [\-\-] <command\.\.\.> Options: ! check for non zero exit code =\fIN\fR check that exit code is \fIN\fR \-\-output {merged,separate,stdout,stderr} control which output is recorded \-\-keep\-empty\-lines retain emtpy lines in \fB${lines[@]}\fR
Usage: run [OPTIONS] [\-\-] <command\.\.\.> Options: ! check for non zero exit code \-\fIN\fR check that exit code is \fIN\fR \-\-separate\-stderr split stderr and stdout \-\-keep\-empty\-lines retain emtpy lines in \fB${lines[@]}\fR/\fB${stderr_lines[@]}\fR
.
.P
Many Bats tests need to run a command and then make assertions about its exit status and output\. Bats includes a \fBrun\fR helper that invokes its arguments as a command, saves the exit status and output into special global variables, and (optionally) checks exit status against a given expected value\. If successful, \fBrun\fR returns with a \fB0\fR status code so you can continue to make assertions in your test case\.
@ -46,7 +46,7 @@ For example, let\'s say you\'re testing that the \fBfoo\fR command, when passed
.nf
@test "invoking foo with a nonexistent file prints an error" {
run -1 foo nonexistent_filename
run \-1 foo nonexistent_filename
[ "$output" = "foo: no such file \'nonexistent_filename\'" ]
}
.
@ -55,14 +55,14 @@ For example, let\'s say you\'re testing that the \fBfoo\fR command, when passed
.IP "" 0
.
.P
The \fB=1\fR as first argument tells \fBrun\fR to expect 1 as an exit status, and to fail if the command exits with any other value\. On failure, both actual and expected values will be displayed, along with the invoked command and its output:
The \fB\-1\fR as first argument tells \fBrun\fR to expect 1 as an exit status, and to fail if the command exits with any other value\. On failure, both actual and expected values will be displayed, along with the invoked command and its output:
.
.IP "" 4
.
.nf
(in test file test\.bats, line 2)
`run -1 foo nonexistent_filename\' failed, expected exit code 1, got 127
`run \-1 foo nonexistent_filename\' failed, expected exit code 1, got 127
.
.fi
.
@ -82,7 +82,7 @@ A third special variable, the \fB$lines\fR array, is available for easily access
.nf
@test "invoking foo without arguments prints usage" {
run -1 foo
run \-1 foo
[ "${lines[0]}" = "usage: foo <filename>" ]
}
.
@ -94,21 +94,7 @@ A third special variable, the \fB$lines\fR array, is available for easily access
By default \fBrun\fR leaves out empty lines in \fB${lines[@]}\fR\. Use \fBrun \-\-keep\-empty\-lines\fR to retain them\.
.
.P
Additionally, you can use \fBrun \-\-output <mode>\fR to control what goes into \fB$output\fR and \fB$lines\fR\. The available values for \fB<mode>\fR are:
.
.IP "\(bu" 4
\fBmerged\fR: the default when \fB\-\-output\fR is not specified, interleaves stdout and stderr
.
.IP "\(bu" 4
\fBseparate\fR: splits stderr off to \fB$stderr\fR and \fB${stderr_lines[@]}\fR, stdout is still available as \fB$output\fR and \fB${lines[@]}\fR
.
.IP "\(bu" 4
\fBstderr\fR: discards stdout and fills \'$stderr\fBand\fR${stderr_lines[@]}`
.
.IP "\(bu" 4
\fBstdout\fR: discards stdout and fills \fB$output\fR and \fB${lines[@]}\fR
.
.IP "" 0
Additionally, you can use \fB\-\-separate\-stderr\fR to split stdout and stderr into \fB$output\fR/\fB$stderr\fR and \fB${lines[@]}\fR/\fB${stderr_lines[@]}\fR\.
.
.P
All additional parameters to run should come before the command\. If you want to run a command that starts with \fB\-\fR, prefix it with \fB\-\-\fR to prevent \fBrun\fR from parsing it as an option\.
@ -138,7 +124,7 @@ Tests can be skipped by using the \fBskip\fR command at the point in a test you
@test "A test I don\'t want to execute for now" {
skip
run -0 foo
run \-0 foo
}
.
.fi
@ -154,7 +140,7 @@ Optionally, you may include a reason for skipping:
@test "A test I don\'t want to execute for now" {
skip "This command will return zero soon, but not now"
run -0 foo
run \-0 foo
}
.
.fi
@ -173,7 +159,7 @@ Or you can skip conditionally:
skip "foo isn\'t bar"
fi
run -0 foo
run \-0 foo
}
.
.fi

@ -1,6 +1,6 @@
{
"name": "bats",
"version": "1.5.0",
"version": "1.6.0",
"description": "Bash Automated Testing System",
"homepage": "https://github.com/bats-core/bats-core#readme",
"license": "MIT",

@ -134,6 +134,33 @@ setup() {
[ "${lines[4]}" = "# \`failing_helper' failed" ]
}
@test "failing bash condition logs correct line number" {
run bats "$FIXTURE_ROOT/failing_with_bash_cond.bats"
[ "$status" -eq 1 ]
[ "${#lines[@]}" -eq 4 ]
[ "${lines[1]}" = 'not ok 1 a failing test' ]
[ "${lines[2]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/failing_with_bash_cond.bats, line 4)" ]
[ "${lines[3]}" = "# \`[[ 1 == 2 ]]' failed" ]
}
@test "failing bash expression logs correct line number" {
run bats "$FIXTURE_ROOT/failing_with_bash_expression.bats"
[ "$status" -eq 1 ]
[ "${#lines[@]}" -eq 4 ]
[ "${lines[1]}" = 'not ok 1 a failing test' ]
[ "${lines[2]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/failing_with_bash_expression.bats, line 3)" ]
[ "${lines[3]}" = "# \`(( 1 == 2 ))' failed" ]
}
@test "failing negated command logs correct line number" {
run bats "$FIXTURE_ROOT/failing_with_negated_command.bats"
[ "$status" -eq 1 ]
[ "${#lines[@]}" -eq 4 ]
[ "${lines[1]}" = 'not ok 1 a failing test' ]
[ "${lines[2]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/failing_with_negated_command.bats, line 3)" ]
[ "${lines[3]}" = "# \`! true' failed" ]
}
@test "test environments are isolated" {
run bats "$FIXTURE_ROOT/environment.bats"
[ $status -eq 0 ]
@ -195,81 +222,6 @@ setup() {
[ "${lines[2]}" = "# (in test file $FIXTURE_ROOT/failing.bats, line 4)" ]
}
@test "load sources scripts relative to the current test file" {
run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load sources relative scripts with filename extension" {
HELPER_NAME="test_helper.bash" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load aborts if the specified script does not exist" {
HELPER_NAME="nonexistent" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 1 ]
}
@test "load sources scripts by absolute path" {
HELPER_NAME="${FIXTURE_ROOT}/test_helper.bash" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load aborts if the script, specified by an absolute path, does not exist" {
HELPER_NAME="${FIXTURE_ROOT}/nonexistent" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 1 ]
}
@test "load relative script with ambiguous name" {
HELPER_NAME="ambiguous" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load supports scripts on the PATH" {
path_dir="$BATS_TMPNAME/path"
mkdir -p "$path_dir"
cp "${FIXTURE_ROOT}/test_helper.bash" "${path_dir}/on_path"
PATH="${path_dir}:$PATH" HELPER_NAME="on_path" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load supports plain symbols" {
local -r helper="${BATS_TEST_TMPDIR}/load_helper_plain"
{
echo "plain_variable='value of plain variable'"
echo "plain_array=(test me hard)"
} > "${helper}"
load "${helper}"
# shellcheck disable=SC2154
[ "${plain_variable}" = 'value of plain variable' ]
# shellcheck disable=SC2154
[ "${plain_array[2]}" = 'hard' ]
rm "${helper}"
}
@test "load doesn't support _declare_d symbols" {
local -r helper="${BATS_TEST_TMPDIR}/load_helper_declared"
{
echo "declare declared_variable='value of declared variable'"
echo "declare -r a_constant='constant value'"
echo "declare -i an_integer=0x7e4"
echo "declare -a an_array=(test me hard)"
echo "declare -x exported_variable='value of exported variable'"
} > "${helper}"
load "${helper}"
[ "${declared_variable:-}" != 'value of declared variable' ]
[ "${a_constant:-}" != 'constant value' ]
(( "${an_integer:-2019}" != 2020 ))
[ "${an_array[2]:-}" != 'hard' ]
[ "${exported_variable:-}" != 'value of exported variable' ]
rm "${helper}"
}
@test "output is discarded for passing tests and printed for failing tests" {
run bats "$FIXTURE_ROOT/output.bats"
[ $status -eq 1 ]
@ -419,10 +371,24 @@ setup() {
@test 'ensure compatibility with unofficial Bash strict mode' {
local expected='ok 1 unofficial Bash strict mode conditions met'
if [[ -n "$BATS_NUMBER_OF_PARALLEL_JOBS" ]]; then
if [[ -z "$BATS_NO_PARALLELIZE_ACROSS_FILES" ]]; then
type -p parallel &>/dev/null || skip "Don't check file parallelized without GNU parallel"
fi
(type -p flock &>/dev/null || type -p shlock &>/dev/null) || skip "Don't check parallelized without flock/shlock "
fi
# PATH required for windows
# HOME required to avoid error from GNU Parallel
# Run Bats under SHELLOPTS=nounset (recursive `set -u`) to catch
# as many unset variable accesses as possible.
run run_under_clean_bats_env env SHELLOPTS=nounset \
"${BATS_ROOT}/bin/bats" "$FIXTURE_ROOT/unofficial_bash_strict_mode.bats"
run env - \
"PATH=$PATH" \
"HOME=$HOME" \
"BATS_NO_PARALLELIZE_ACROSS_FILES=$BATS_NO_PARALLELIZE_ACROSS_FILES" \
"BATS_NUMBER_OF_PARALLEL_JOBS=$BATS_NUMBER_OF_PARALLEL_JOBS" \
SHELLOPTS=nounset \
"${BATS_ROOT}/bin/bats" "$FIXTURE_ROOT/unofficial_bash_strict_mode.bats"
if [[ "$status" -ne 0 || "${lines[1]}" != "$expected" ]]; then
cat <<END_OF_ERR_MSG
@ -560,8 +526,8 @@ END_OF_ERR_MSG
[ "${lines[4]}" = '# foo' ]
[ "${lines[5]}" = '# bar' ]
[ "${lines[6]}" = 'not ok 2 test function returns nonzero' ]
[ "${lines[7]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/no-final-newline.bats, line 7)" ]
[ "${lines[8]}" = "# \`printf 'foo\nbar'' failed" ]
[ "${lines[7]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/no-final-newline.bats, line 8)" ]
[ "${lines[8]}" = "# \`return 1' failed" ]
[ "${lines[9]}" = '# foo' ]
[ "${lines[10]}" = '# bar' ]
}
@ -583,12 +549,12 @@ END_OF_ERR_MSG
[ "${lines[2]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/unbound_variable.bats, line 9)" ]
[ "${lines[3]}" = "# \`foo=\$unset_variable' failed" ]
# shellcheck disable=SC2076
[[ "${lines[4]}" =~ ".src: line 9:" ]]
[[ "${lines[4]}" =~ ".bats: line 9:" ]]
[ "${lines[5]}" = 'not ok 2 access second unbound variable' ]
[ "${lines[6]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/unbound_variable.bats, line 15)" ]
[ "${lines[7]}" = "# \`foo=\$second_unset_variable' failed" ]
# shellcheck disable=SC2076
[[ "${lines[8]}" =~ ".src: line 15:" ]]
[[ "${lines[8]}" =~ ".bats: line 15:" ]]
}
@test "report correct line on external function calls" {
@ -596,26 +562,31 @@ END_OF_ERR_MSG
[ "$status" -eq 1 ]
expectedNumberOfTests=12
linesOfOutputPerTest=3
[ "${#lines[@]}" -gt $((expectedNumberOfTests * linesOfOutputPerTest + 1)) ]
linesPerTest=5
outputOffset=1
currentErrorLine=9
linesPerTest=5
for t in $(seq $expectedNumberOfTests); do
[[ "${lines[$outputOffset]}" == "not ok $t "* ]]
# Skip backtrace into external function if set
if [[ "${lines[$((outputOffset + 1))]}" == "# (from function "* ]]; then
outputOffset=$((outputOffset + 1))
parenChar=" "
else
parenChar="("
fi
[ "${lines[$((outputOffset + 1))]}" = "# ${parenChar}in test file $RELATIVE_FIXTURE_ROOT/external_function_calls.bats, line $currentErrorLine)" ]
[[ "${lines[$((outputOffset + 2))]}" =~ " failed" ]]
outputOffset=$((outputOffset + 3))
# shellcheck disable=SC2076
[[ "${lines[$outputOffset]}" =~ "not ok $t " ]]
[[ "${lines[$outputOffset]}" =~ stackdepth=([0-9]+) ]]
stackdepth="${BASH_REMATCH[1]}"
case "${stackdepth}" in
1)
[ "${lines[$((outputOffset + 1))]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/external_function_calls.bats, line $currentErrorLine)" ]
outputOffset=$((outputOffset + 3))
;;
2)
[[ "${lines[$((outputOffset + 1))]}" =~ ^'# (from function `'.*\'' in file '.*'/test_helper.bash, line '[0-9]+,$ ]]
[ "${lines[$((outputOffset + 2))]}" = "# in test file $RELATIVE_FIXTURE_ROOT/external_function_calls.bats, line $currentErrorLine)" ]
outputOffset=$((outputOffset + 4))
;;
*)
printf 'error: stackdepth=%s not implemented\n' "${stackdepth}" >&2
return 1
esac
currentErrorLine=$((currentErrorLine + linesPerTest))
done
}
@ -676,6 +647,9 @@ END_OF_ERR_MSG
}
@test "Don't hang on CTRL-C (issue #353)" {
if [[ "$BATS_NUMBER_OF_PARALLEL_JOBS" -gt 1 ]]; then
skip "Aborts don't work in parallel mode"
fi
load 'concurrent-coordination'
# shellcheck disable=SC2031,SC2030
export SINGLE_USE_LATCH_DIR="${BATS_TEST_TMPDIR}"
@ -849,7 +823,7 @@ EOF
single-use-latch::wait hang_in_test 1 10 || (cat "$TEMPFILE"; false) # still forward output on timeout
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
@ -857,10 +831,10 @@ EOF
run cat "$TEMPFILE"
echo "$output"
[[ "${lines[1]}" == "not ok 1 test" ]]
[[ "${lines[2]}" == "# (in test file ${RELATIVE_FIXTURE_ROOT}/hang_in_test.bats, line 7)" ]]
[[ "${lines[3]}" == "# \`sleep 10' failed with status 130" ]]
[[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]]
[ "${lines[1]}" == "not ok 1 test" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[3]}" == *"failed with status 130" ]] || false
[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]
}
@test "CTRL-C aborts and fails the current run" {
@ -885,7 +859,7 @@ EOF
single-use-latch::wait hang_in_run 1 10
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
@ -893,8 +867,43 @@ EOF
run cat "$TEMPFILE"
[ "${lines[1]}" == "not ok 1 test" ]
[ "${lines[2]}" == "# (in test file ${RELATIVE_FIXTURE_ROOT}/hang_in_run.bats, line 7)" ]
[ "${lines[3]}" == "# \`run sleep 10' failed with status 130" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[3]}" == *"failed with status 130" ]] || false
[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]
}
@test "CTRL-C aborts and fails after run" {
if [[ "$BATS_NUMBER_OF_PARALLEL_JOBS" -gt 1 ]]; then
skip "Aborts don't work in parallel mode"
fi
# shellcheck disable=SC2031,2030
export TEMPFILE="$BATS_TEST_TMPDIR/$BATS_TEST_NAME.log"
# guarantee that background processes get their own process group -> pid=pgid
set -m
load 'concurrent-coordination'
# shellcheck disable=SC2031,SC2030
export SINGLE_USE_LATCH_DIR="${BATS_SUITE_TMPDIR}"
# we cannot use run for a background task, so we have to store the output for later
bats "$FIXTURE_ROOT/hang_after_run.bats" --tap >"$TEMPFILE" 2>&1 & # don't block execution, or we cannot send signals
SUBPROCESS_PID=$!
single-use-latch::wait hang_after_run 1 10
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
run cat "$TEMPFILE"
[ "${lines[1]}" == "not ok 1 test" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[3]}" == *"failed with status 130" ]] || false
[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]
}
@ -920,7 +929,7 @@ EOF
single-use-latch::wait hang_in_teardown 1 10
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
@ -928,10 +937,10 @@ EOF
run cat "$TEMPFILE"
echo "$output"
[[ "${lines[1]}" == "not ok 1 empty" ]]
[[ "${lines[2]}" == "# (from function \`teardown' in test file ${RELATIVE_FIXTURE_ROOT}/hang_in_teardown.bats, line 4)" ]]
[[ "${lines[3]}" == "# \`sleep 10' failed" ]]
[[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]]
[ "${lines[1]}" == "not ok 1 empty" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[3]}" == *"failed with status 130" ]] || false
[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]
}
@test "CTRL-C aborts and fails the current setup_file" {
@ -956,7 +965,7 @@ EOF
single-use-latch::wait hang_in_setup_file 1 10
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
@ -964,10 +973,10 @@ EOF
run cat "$TEMPFILE"
echo "$output"
[[ "${lines[1]}" == "not ok 1 setup_file failed" ]]
[[ "${lines[2]}" == "# (from function \`setup_file' in test file ${RELATIVE_FIXTURE_ROOT}/hang_in_setup_file.bats, line 4)" ]]
[[ "${lines[3]}" == "# \`sleep 10' failed with status 130" ]]
[[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]]
[ "${lines[1]}" == "not ok 1 setup_file failed" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[3]}" == *"failed with status 130" ]] || false
[ "${lines[4]}" == "# Received SIGINT, aborting ..." ]
}
@test "CTRL-C aborts and fails the current teardown_file" {
@ -991,7 +1000,7 @@ EOF
single-use-latch::wait hang_in_teardown_file 1 10
# emulate CTRL-C by sending SIGINT to the whole process group
kill -SIGINT -- -$SUBPROCESS_PID
kill -SIGINT -- -$SUBPROCESS_PID || (cat "$TEMPFILE"; false)
# the test suite must be marked as failed!
wait $SUBPROCESS_PID && return 1
@ -999,13 +1008,13 @@ EOF
run cat "$TEMPFILE"
echo "$output"
[[ "${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}/hang_in_teardown_file.bats, line 4)" ]]
[[ "${lines[4]}" == "# \`sleep 10' failed with status 130" ]]
[[ "${lines[5]}" == "# Received SIGINT, aborting ..." ]]
[[ "${lines[6]}" == "# bats warning: Executed 2 instead of expected 1 tests" ]]
[ "${lines[0]}" == "1..1" ]
[ "${lines[1]}" == "ok 1 empty" ]
[ "${lines[2]}" == "not ok 2 teardown_file failed" ]
# due to scheduling the exact line will vary but we should exit with 130
[[ "${lines[4]}" == *"failed with status 130" ]] || false
[ "${lines[5]}" == "# Received SIGINT, aborting ..." ]
[ "${lines[6]}" == "# bats warning: Executed 2 instead of expected 1 tests" ]
}
@ -1146,4 +1155,107 @@ EOF
@test "Test with a name that is waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long" {
skip "This test should only check if the long name chokes bats' internals during execution"
}
}
@test "BATS_CODE_QUOTE_STYLE works with any two characters (even unicode)" {
BATS_CODE_QUOTE_STYLE='``' run -1 bats --tap "${FIXTURE_ROOT}/failing.bats"
# shellcheck disable=SC2016
[ "${lines[3]}" == '# `eval "( exit ${STATUS:-1} )"` failed' ]
export BATS_CODE_QUOTE_STYLE='😁😂'
if [[ ${#BATS_CODE_QUOTE_STYLE} -ne 2 ]]; then
# for example, this happens on windows!
skip 'Unicode chars are not counted as one char in this system'
fi
run -1 bats --tap "${FIXTURE_ROOT}/failing.bats"
# shellcheck disable=SC2016
[ "${lines[3]}" == '# 😁eval "( exit ${STATUS:-1} )"😂 failed' ]
}
@test "BATS_CODE_QUOTE_STYLE=custom requires BATS_CODE_QUOTE_BEGIN/END" {
# unset because they are set in the surrounding scope
unset BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
BATS_CODE_QUOTE_STYLE=custom run -1 bats --tap "${FIXTURE_ROOT}/passing.bats"
[ "${lines[0]}" == 'ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set' ]
# shellcheck disable=SC2016
BATS_CODE_QUOTE_STYLE=custom \
BATS_BEGIN_CODE_QUOTE='$(' \
BATS_END_CODE_QUOTE=')' \
run -1 bats --tap "${FIXTURE_ROOT}/failing.bats"
# shellcheck disable=SC2016
[ "${lines[3]}" == '# $(eval "( exit ${STATUS:-1} )") failed' ]
}
@test "Warn about invalid BATS_CODE_QUOTE_STYLE" {
BATS_CODE_QUOTE_STYLE='' run -1 bats --tap "${FIXTURE_ROOT}/passing.bats"
[ "${lines[0]}" == 'ERROR: Unknown BATS_CODE_QUOTE_STYLE: ' ]
BATS_CODE_QUOTE_STYLE='1' run -1 bats --tap "${FIXTURE_ROOT}/passing.bats"
[ "${lines[0]}" == 'ERROR: Unknown BATS_CODE_QUOTE_STYLE: 1' ]
BATS_CODE_QUOTE_STYLE='three' run -1 bats --tap "${FIXTURE_ROOT}/passing.bats"
[ "${lines[0]}" == 'ERROR: Unknown BATS_CODE_QUOTE_STYLE: three' ]
}
@test "Debug trap must only override variables that are prefixed with bats_ (issue #519)" {
# use declare -p to gather variables in pristine bash and bats @test environment
# then compare which ones are introduced in @test compared to bash
# make declare's output more readable and suitable for `comm`
if [[ "${BASH_VERSINFO[0]}" -eq 3 ]]; then
normalize_variable_list() {
# `declare -p`: VAR_NAME="VALUE"
# will also contain function definitions!
while read -r line; do
# Skip variable assignments in function definitions!
# (They will be indented.)
declare_regex='^declare -[^[:space:]]+ ([^=]+)='
plain_regex='^([^=[:space]]+)='
if [[ $line =~ $declare_regex ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
elif [[ $line =~ $plain_regex ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
fi
done | sort
}
else
normalize_variable_list() {
# `declare -p`: declare -X VAR_NAME="VALUE"
while IFS=' =' read -r _declare _ variable _; do
if [[ "$_declare" == declare ]]; then # skip multiline variables' values
printf "%s\n" "$variable"
fi
done | sort
}
fi
# get the bash baseline
# add variables that should be ignored like PIPESTATUS here
BASH_DECLARED_VARIABLES=$(env -i PIPESTATUS= "$BASH" -c "declare -p")
local BATS_DECLARED_VARIABLES_FILE="${BATS_TEST_TMPDIR}/variables.log"
# now capture bats @test environment
run -0 env -i PATH="$PATH" BATS_DECLARED_VARIABLES_FILE="$BATS_DECLARED_VARIABLES_FILE" bash "${BATS_ROOT}/bin/bats" "${FIXTURE_ROOT}/issue-519.bats"
# use function to allow failing via !, run is a bit unwiedly with the pipe and subshells
check_no_new_variables() {
# -23 -> only look at additions on the bats list
! comm -23 <(normalize_variable_list <"$BATS_DECLARED_VARIABLES_FILE") \
<(normalize_variable_list <<< "$BASH_DECLARED_VARIABLES" ) \
| grep -v '^BATS_' # variables that are prefixed with BATS_ don't count
}
check_no_new_variables
}
@test "Don't wait for disowned background jobs to finish because of open FDs (#205)" {
SECONDS=0
export LOG_FILE="$BATS_TEST_TMPDIR/fds.log"
run -0 bats --show-output-of-passing-tests --tap "${FIXTURE_ROOT}/issue-205.bats"
echo "Whole suite took: $SECONDS seconds"
FDS_LOG=$(<"$LOG_FILE")
echo "$FDS_LOG"
[ $SECONDS -lt 10 ]
[[ $FDS_LOG == *'otherfunc fds after: (0 1 2)'* ]] || false
[[ $FDS_LOG == *'setup_file fds after: (0 1 2)'* ]] || false
}

@ -58,7 +58,6 @@ single-use-latch::signal() { # <latch-name>
# mark our passing
# concurrent process might interleave but will still post their newline
echo "passed-$$" >> "$LATCH_FILE"
echo "passed-$$ >> $LATCH_FILE" >> /tmp/latch
}
single-use-latch::_filename() { # <latch-name>

@ -94,6 +94,9 @@ not ok 1 failing test
}
@test "teardown_file should run even after user abort via CTRL-C" {
if [[ "$BATS_NUMBER_OF_PARALLEL_JOBS" -gt 1 ]]; then
skip "Aborts don't work in parallel mode"
fi
# shellcheck disable=SC2031,SC2030
export LOG="$BATS_TEST_TMPDIR/teardown_file_abort.log"
# guarantee that background processes get their own process group -> pid=pgid

@ -4,62 +4,62 @@ load test_helper
# Tests are designed so the first statement succeeds and 2nd fails
# All tests fail on the same line so checking can be automated
@test "Call true function && false" {
@test "Call true function && false stackdepth=1" {
help_me
help_me && false
}
@test "Call true function && return 1" {
@test "Call true function && return 1 stackdepth=1" {
help_me
help_me && return 1
}
@test "Call true function and invert" {
@test "Call true function and invert stackdepth=2" {
help_me
! help_me
}
@test "Call false function || false" {
@test "Call false function || false stackdepth=1" {
! failing_helper
failing_helper || false
}
@test "Call false function && return 1" {
@test "Call false function && return 1 stackdepth=1" {
! failing_helper
failing_helper || return 1
}
@test "Call false function" {
@test "Call false function stackdepth=2" {
! failing_helper
failing_helper
}
@test "Call return_0 function && false" {
@test "Call return_0 function && false stackdepth=1" {
return_0
return_0 && false
}
@test "Call return_0 function && return 1" {
@test "Call return_0 function && return 1 stackdepth=1" {
return_0
return_0 && return 1
}
@test "Call return_0 function and invert" {
@test "Call return_0 function and invert stackdepth=2" {
return_0
! return_0
}
@test "Call return_1 function || false" {
@test "Call return_1 function || false stackdepth=1" {
! return_1
return_1 || false
}
@test "Call return_1 function && return 1" {
@test "Call return_1 function && return 1 stackdepth=1" {
! return_1
return_1 || return 1
}
@test "Call return_1 function" {
@test "Call return_1 function stackdepth=2" {
! return_1
return_1
}

@ -0,0 +1,5 @@
@test "a failing test" {
true
# shellcheck disable=SC2050
[[ 1 == 2 ]]
}

@ -0,0 +1,4 @@
@test "a failing test" {
true
(( 1 == 2 ))
}

@ -0,0 +1,4 @@
@test "a failing test" {
true
! true
}

@ -0,0 +1,9 @@
setup() {
load '../../concurrent-coordination'
}
@test "test" {
single-use-latch::signal hang_after_run
run true
sleep 10
}

@ -0,0 +1,103 @@
#!/usr/bin/env bats
function bgfunc {
get_open_fds
echo "${FUNCNAME[1]} fds before: (${open_fds[*]})" >>"${LOG_FILE}"
close_non_std_fds
get_open_fds
echo "${FUNCNAME[1]} fds after: (${open_fds[*]})" >>"${LOG_FILE}"
sleep 10
echo "bgfunc done"
return 0
}
# store the list of open FDs in array open_fds
function get_open_fds() {
open_fds=() # reset output array in case it was already set
if [[ ${BASH_VERSINFO[0]} == 3 ]]; then
local BASHPID
BASHPID=$(bash -c 'echo $PPID')
fi
local tmpfile
tmpfile=$(mktemp "$BATS_SUITE_TMPDIR/fds-XXXXXX")
# Avoid opening a new fd to read fds: Don't use <(), glob expansion.
# Instead, redirect stdout to file which does not create an extra FD.
if [[ -d /proc/$BASHPID/fd ]]; then # Linux
ls -1 "/proc/$BASHPID/fd" > "$tmpfile"
IFS=$'\n' read -d '' -ra open_fds <"$tmpfile" || true
elif command -v lsof >/dev/null ; then # MacOS
local -a fds
lsof -F f -p "$BASHPID" >"$tmpfile"
IFS=$'\n' read -d '' -ra fds < "$tmpfile" || true
for fd in "${fds[@]}"; do
case $fd in
f[0-9]*) # filter non fd entries (mainly pid?)
open_fds+=("${fd#f}") # cut off f prefix
;;
esac
done
elif command -v procstat >/dev/null ; then # BSDs
local -a columns header
procstat fds "$BASHPID" > "$tmpfile"
{
read -r -a header
local fd_column_index=-1
for ((i=0; i<${#header[@]}; ++i)); do
if [[ ${header[$i]} == *FD* ]]; then
fd_column_index=$i
break
fi
done
if [[ $fd_column_index -eq -1 ]]; then
printf "Could not find FD column in procstat" >&2
exit 1
fi
while read -r -a columns; do
local fd=${columns[$fd_column_index]}
if [[ $fd == [0-9]* ]]; then # only take up numeric entries
open_fds+=("$fd")
fi
done
} < "$tmpfile"
else
# TODO: MSYS (Windows)
printf "Neither FD discovery mechanism available\n" >&2
exit 1
fi
}
function close_non_std_fds() {
local open_fds non_std_fds=()
get_open_fds
for fd in "${open_fds[@]}"; do
if [[ $fd -gt 2 ]]; then
non_std_fds+=("$fd")
fi
done
close_fds "${non_std_fds[@]}"
}
function close_fds() { # <fds...>
for fd in "$@"; do
eval "exec $fd>&-"
done
}
function otherfunc {
bgfunc &
PID=$!
disown
return 0
}
setup_file (){ # see issue #530
bgfunc &
}
@test "min bg" {
echo "sec: $SECONDS"
otherfunc
sleep 1 # leave some space for the background job to print/fail early
kill -s 0 -- $PID # fail it the process already finished due to error!
echo "sec: $SECONDS"
}

@ -0,0 +1,3 @@
@test "no unprefixed variables" {
declare -p >"${BATS_DECLARED_VARIABLES_FILE?}"
}

@ -0,0 +1,16 @@
#!/usr/bin/env bats
setup_file() {
echo "# setup_file stdout"
echo "# setup_file fd3" >&3
}
teardown_file() {
echo "# teardown_file stdout"
echo "# teardown_file fd3" >&3
}
@test "My test" {
echo "# test stdout"
echo "# test fd3" >&3
}

@ -0,0 +1,6 @@
[ -n "$HELPER_NAME" ] || HELPER_NAME="test_helper"
bats_load_library "$HELPER_NAME"
@test "calling a loaded helper" {
help_me
}

@ -0,0 +1,5 @@
bats_load_library "return1"
@test "true" {
true
}

@ -0,0 +1,5 @@
load "return1"
@test "true" {
true
}

@ -0,0 +1,5 @@
@test "find a library" {
run find_in_bats_lib_path "$LIBRARY_NAME"
[ $status -eq 0 ]
[ "${lines[0]}" = "$LIBRARY_PATH" ]
}

@ -0,0 +1,4 @@
@test "does not find a library" {
run find_in_bats_lib_path "$LIBRARY_NAME"
[ $status -eq 1 ]
}

@ -0,0 +1,6 @@
[ -n "$HELPER_NAME" ] || HELPER_NAME="test_helper"
load "$HELPER_NAME"
@test "calling a loaded helper" {
help_me
}

@ -0,0 +1,19 @@
help_me() {
true
}
failing_helper() {
false
}
return_0() {
# Just return 0. Intentional assignment to boost line numbers
result=0
return $result
}
return_1() {
# Just return 0. Intentional assignment to boost line numbers
result=1
return $result
}

@ -138,3 +138,15 @@ TESTSUITES_REGEX="<testsuites time=\"$FLOAT_REGEX\">"
[[ "${lines[17]}" == ' </testcase>' ]]
[[ "${lines[18]}" == '</testsuite>' ]]
}
@test "junit does not mark tests with FD 3 output in teardown_file as failed (issue #531)" {
run -0 bats --formatter junit "$FIXTURE_ROOT/issue_531.bats"
[[ "${lines[2]}" == '<testsuite name="issue_531.bats" '*'>' ]]
[[ "${lines[3]}" == ' <testcase classname="issue_531.bats" '*'>' ]]
# only the outputs on FD3 should be visible on a successful test
[[ "${lines[4]}" == ' <system-out>test fd3' ]]
[[ "${lines[5]}" == 'teardown_file fd3</system-out>' ]]
[[ "${lines[6]}" == ' </testcase>' ]]
[[ "${lines[7]}" == '</testsuite>' ]]
}

@ -0,0 +1,185 @@
#!/usr/bin/env bats
setup() {
load test_helper
fixtures load
}
@test "find_in_bats_lib_path recognizes files relative to test file" {
test_dir="$BATS_TEST_TMPDIR/find_in_bats_lib_path/bats_test_dirname_priorty"
mkdir -p "$test_dir"
cp "$FIXTURE_ROOT/test_helper.bash" "$test_dir/"
cp "$FIXTURE_ROOT/find_library_helper.bats" "$test_dir"
BATS_LIB_PATH="" LIBRARY_NAME="test_helper" LIBRARY_PATH="$test_dir/test_helper.bash" run bats "$test_dir/find_library_helper.bats"
}
@test "find_in_bats_lib_path recognizes files in BATS_LIB_PATH" {
test_dir="$BATS_TEST_TMPDIR/find_in_bats_lib_path/bats_test_dirname_priorty"
mkdir -p "$test_dir"
cp "$FIXTURE_ROOT/test_helper.bash" "$test_dir/"
BATS_LIB_PATH="$test_dir" LIBRARY_NAME="test_helper" LIBRARY_PATH="$test_dir/test_helper.bash" run bats "$FIXTURE_ROOT/find_library_helper.bats"
}
@test "find_in_bats_lib_path returns 1 if no load path is found" {
test_dir="$BATS_TEST_TMPDIR/find_in_bats_lib_path/no_load_path_found"
mkdir -p "$test_dir"
cp "$FIXTURE_ROOT/test_helper.bash" "$test_dir/"
BATS_LIB_PATH="$test_dir" LIBRARY_NAME="test_helper" run bats "$FIXTURE_ROOT/find_library_helper_err.bats"
}
@test "find_in_bats_lib_path follows the priority of BATS_LIB_PATH" {
test_dir="$BATS_TEST_TMPDIR/find_in_bats_lib_path/follows_priority"
first_dir="$test_dir/first"
mkdir -p "$first_dir"
cp "$FIXTURE_ROOT/test_helper.bash" "$first_dir/target.bash"
second_dir="$test_dir/second"
mkdir -p "$second_dir"
cp "$FIXTURE_ROOT/exit1.bash" "$second_dir/target.bash"
BATS_LIB_PATH="$first_dir:$second_dir" LIBRARY_NAME="target" LIBRARY_PATH="$first_dir/target.bash" run bats "$FIXTURE_ROOT/find_library_helper.bats"
}
@test "load sources scripts relative to the current test file" {
run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load sources relative scripts with filename extension" {
HELPER_NAME="test_helper.bash" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load aborts if the specified script does not exist" {
HELPER_NAME="nonexistent" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 1 ]
}
@test "load sources scripts by absolute path" {
HELPER_NAME="${FIXTURE_ROOT}/test_helper.bash" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load aborts if the script, specified by an absolute path, does not exist" {
HELPER_NAME="${FIXTURE_ROOT}/nonexistent" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 1 ]
}
@test "load relative script with ambiguous name" {
HELPER_NAME="ambiguous" run bats "$FIXTURE_ROOT/load.bats"
[ $status -eq 0 ]
}
@test "load does not use the BATS_LIB_PATH" {
path_dir="$BATS_TEST_TMPDIR/path"
mkdir -p "$path_dir/on_path"
cp "${FIXTURE_ROOT}/test_helper.bash" "${path_dir}/on_path/load.bash"
# shellcheck disable=SC2030,SC2031
export BATS_LIB_PATH="${path_dir}" HELPER_NAME="on_path"
run ! bats "$FIXTURE_ROOT/load.bats"
run -0 bats "$FIXTURE_ROOT/bats_load_library.bats"
}
@test "load supports plain symbols" {
local -r helper="${BATS_TEST_TMPDIR}/load_helper_plain"
{
echo "plain_variable='value of plain variable'"
echo "plain_array=(test me hard)"
} > "${helper}"
load "${helper}"
# shellcheck disable=SC2154
[ "${plain_variable}" = 'value of plain variable' ]
# shellcheck disable=SC2154
[ "${plain_array[2]}" = 'hard' ]
rm "${helper}"
}
@test "load doesn't support _declare_d symbols" {
local -r helper="${BATS_TEST_TMPDIR}/load_helper_declared"
{
echo "declare declared_variable='value of declared variable'"
echo "declare -r a_constant='constant value'"
echo "declare -i an_integer=0x7e4"
echo "declare -a an_array=(test me hard)"
echo "declare -x exported_variable='value of exported variable'"
} > "${helper}"
load "${helper}"
[ "${declared_variable:-}" != 'value of declared variable' ]
[ "${a_constant:-}" != 'constant value' ]
(( "${an_integer:-2019}" != 2020 ))
[ "${an_array[2]:-}" != 'hard' ]
[ "${exported_variable:-}" != 'value of exported variable' ]
rm "${helper}"
}
@test "load supports scripts on the PATH" {
path_dir="$BATS_TEST_TMPDIR/path"
mkdir -p "$path_dir"
cp "${FIXTURE_ROOT}/test_helper.bash" "${path_dir}/on_path"
# shellcheck disable=SC2030,SC2031
export PATH="${path_dir}:$PATH" HELPER_NAME="on_path"
run -0 bats "$FIXTURE_ROOT/load.bats"
}
@test "bats_load_library requires BATS_LIB_PATH to be set" {
unset BATS_LIB_PATH
run ! bats "$FIXTURE_ROOT/bats_load_library.bats"
[ "${lines[4]}" == '# bats_load_library: requires BATS_LIB_PATH to be set!' ]
}
@test "bats_load_library supports libraries with loaders on the BATS_LIB_PATH" {
path_dir="$BATS_TEST_TMPDIR/libraries/$BATS_TEST_NAME"
mkdir -p "$path_dir"
cp "${FIXTURE_ROOT}/test_helper.bash" "${path_dir}/load.bash"
cp "${FIXTURE_ROOT}/exit1.bash" "${path_dir}/exit1.bash"
# shellcheck disable=SC2030,SC2031
export BATS_LIB_PATH="${BATS_TEST_TMPDIR}/libraries" HELPER_NAME="$BATS_TEST_NAME"
run -0 bats "$FIXTURE_ROOT/bats_load_library.bats"
run ! bats "$FIXTURE_ROOT/bats_load.bats" # load does not use BATS_LIB_PATH!
}
@test "bats_load_library supports libraries with loaders on the BATS_LIB_PATH with multiple libraries" {
path_dir="$BATS_TEST_TMPDIR/libraries/"
for lib in liba libb libc; do
mkdir -p "$path_dir/$lib"
cp "${FIXTURE_ROOT}/exit1.bash" "$path_dir/$lib/load.bash"
done
mkdir -p "$path_dir/$BATS_TEST_NAME"
cp "${FIXTURE_ROOT}/test_helper.bash" "$path_dir/$BATS_TEST_NAME/load.bash"
# shellcheck disable=SC2030,SC2031
export BATS_LIB_PATH="$path_dir" HELPER_NAME="$BATS_TEST_NAME"
run -0 bats "$FIXTURE_ROOT/bats_load_library.bats"
run ! bats "$FIXTURE_ROOT/load.bats" # load does not use BATS_LIB_PATH!
}
@test "bats_load_library can handle whitespaces in BATS_LIB_PATH" {
path_dir="$BATS_TEST_TMPDIR/libraries with spaces/"
for lib in liba libb libc; do
mkdir -p "$path_dir/$lib"
cp "${FIXTURE_ROOT}/exit1.bash" "$path_dir/$lib/load.bash"
done
mkdir -p "$path_dir/$BATS_TEST_NAME"
cp "${FIXTURE_ROOT}/test_helper.bash" "$path_dir/$BATS_TEST_NAME/load.bash"
# shellcheck disable=SC2030,SC2031
export BATS_LIB_PATH="$path_dir" HELPER_NAME="$BATS_TEST_NAME"
run -0 bats "$FIXTURE_ROOT/bats_load_library.bats"
}
@test "bats_load_library errors when a library errors while sourcing" {
path_dir="$BATS_TEST_TMPDIR/libraries_err_sourcing/"
mkdir -p "$path_dir/return1"
cp "${FIXTURE_ROOT}/return1.bash" "$path_dir/return1/load.bash"
# shellcheck disable=SC2030,SC2031
export BATS_LIB_PATH="$path_dir"
run -1 bats "$FIXTURE_ROOT/failing_bats_load_library.bats"
}

@ -27,10 +27,3 @@ emit_debug_output() {
# shellcheck disable=SC2154
printf '%s\n' 'output:' "$output" >&2
}
run_under_clean_bats_env() {
# we want the variable names to be separate
# shellcheck disable=SC2086
unset ${!BATS_@}
"$@"
}

Loading…
Cancel
Save