This commit is contained in:
2026-08-02 15:26:07 +02:00
parent 336b36c8e1
commit b2eb145a6f
415 changed files with 24264 additions and 36 deletions
@@ -0,0 +1,8 @@
name: Sync Master
on:
push: { branches: main }
workflow_dispatch:
jobs:
sync:
uses: bats-core/.github/.github/workflows/sync-master.yml@v1
+9
View File
@@ -0,0 +1,9 @@
name: Test
on:
push:
pull_request:
workflow_dispatch:
jobs:
test:
uses: bats-core/.github/.github/workflows/test.yml@v1
+3
View File
@@ -0,0 +1,3 @@
/node_modules
/yarn.lock
/bats-assert-*.tgz
+116
View File
@@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>
+788
View File
@@ -0,0 +1,788 @@
# bats-assert
[![License](https://img.shields.io/npm/l/bats-assert.svg)](https://github.com/bats-core/bats-assert/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/bats-core/bats-assert.svg)](https://github.com/bats-core/bats-assert/releases/latest)
[![npm release](https://img.shields.io/npm/v/bats-assert.svg)](https://www.npmjs.com/package/bats-assert)
[![Tests](https://github.com/bats-core/bats-assert/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/bats-core/bats-assert/actions/workflows/test.yml)
`bats-assert` is a helper library providing common assertions for [Bats][bats].
- [Install](#install)
- [Usage](#usage)
- [Options](#options)
- [Full Assertion API](#full-assertion-api)
In the context of this project, an [assertion][wikipedia-assertions] is a function that perform a test and returns `1` on failure or `0` on success.
To make debugging easier, the assertion also outputs relevant information on failure.
The output is [formatted][bats-support-output] for readability.
To make assertions usable outside of `@test` blocks, the output is sent to [stderr][wikipedia-stderr].
The most recent invocation of Bats' `run` function is used for testing assertions on output and status code.
[wikipedia-assertions]: https://en.wikipedia.org/wiki/Assertion_(software_development)
[wikipedia-stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr)
## Install
This project has one dependency, for output formatting: [`bats-support`][bats-support]
Read the [shared documentation][bats-docs] to learn how to install and load both libraries.
## Usage
This project provides the following functions:
- [assert](#assert) / [refute](#refute) Assert a given expression evaluates to `true` or `false`.
- [assert_equal](#assert_equal) Assert two parameters are equal.
- [assert_not_equal](#assert_not_equal) Assert two parameters are not equal.
- [assert_success](#assert_success) / [assert_failure](#assert_failure) Assert exit status is `0` or `1`.
- [assert_output](#assert_output) / [refute_output](#refute_output) Assert output does (or does not) contain given content.
- [assert_line](#assert_line) / [refute_line](#refute_line) Assert a specific line of output does (or does not) contain given content.
- [assert_regex](#assert_regex) / [refute_regex](#refute_regex) Assert a parameter does (or does not) match given pattern.
These commands are described in more detail below.
## Options
For functions that have options, `--` disables option parsing for the remaining arguments to allow using arguments identical to one of the allowed options.
```bash
assert_output -- '-p'
```
Specifying `--` as an argument is similarly simple.
```bash
refute_line -- '--'
```
## Full Assertion API
### `assert`
Fail if the given expression evaluates to false.
> _**Note**:
> The expression must be a simple command.
> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._
```bash
@test 'assert()' {
assert [ 1 -lt 0 ]
}
```
On failure, the failed expression is displayed.
```
-- assertion failed --
expression : [ 1 -lt 0 ]
--
```
### `refute`
Fail if the given expression evaluates to true.
> _**Note**
> The expression must be a simple command.
> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._
```bash
@test 'refute()' {
refute [ 1 -gt 0 ]
}
```
On failure, the successful expression is displayed.
```
-- assertion succeeded, but it was expected to fail --
expression : [ 1 -gt 0 ]
--
```
### `assert_equal`
Fail if the two parameters, actual and expected value respectively, do not equal.
```bash
@test 'assert_equal()' {
assert_equal 'have' 'want'
}
```
On failure, the expected and actual values are displayed.
```
-- values do not equal --
expected : want
actual : have
--
```
If either value is longer than one line both are displayed in *multi-line* format.
### `assert_not_equal`
Fail if the two parameters, actual and unexpected value respectively, are equal.
```bash
@test 'assert_not_equal()' {
assert_not_equal 'foobar' 'foobar'
}
```
On failure, the expected and actual values are displayed.
```
-- values should not be equal --
unexpected : foobar
actual : foobar
--
```
If either value is longer than one line both are displayed in *multi-line* format.
### `assert_success`
Fail if `$status` is not 0.
```bash
@test 'assert_success() status only' {
run bash -c "echo 'Error!'; exit 1"
assert_success
}
```
On failure, `$status` and `$output` are displayed.
```
-- command failed --
status : 1
output : Error!
--
```
If `$output` is longer than one line, it is displayed in *multi-line* format.
### `assert_failure`
Fail if `$status` is 0.
```bash
@test 'assert_failure() status only' {
run echo 'Success!'
assert_failure
}
```
On failure, `$output` is displayed.
```
-- command succeeded, but it was expected to fail --
output : Success!
--
```
If `$output` is longer than one line, it is displayed in *multi-line* format.
#### Expected status
When one parameter is specified, fail if `$status` does not equal the expected status specified by the parameter.
```bash
@test 'assert_failure() with expected status' {
run bash -c "echo 'Error!'; exit 1"
assert_failure 2
}
```
On failure, the expected and actual status, and `$output` are displayed.
```
-- command failed as expected, but status differs --
expected : 2
actual : 1
output : Error!
--
```
If `$output` is longer than one line, it is displayed in *multi-line* format.
### `assert_output`
This function helps to verify that a command or function produces the correct output by checking that the specified expected output matches the actual output.
Matching can be literal (default), partial or regular expression.
This function is the logical complement of `refute_output`.
#### Literal matching
By default, literal matching is performed.
The assertion fails if `$output` does not equal the expected output.
```bash
@test 'assert_output()' {
run echo 'have'
assert_output 'want'
}
```
On failure, the expected and actual output are displayed.
```
-- output differs --
expected : want
actual : have
--
```
If either value is longer than one line both are displayed in *multi-line* format.
#### Existence
To assert that any (non-empty) output exists at all, simply omit the matching argument.
```bash
@test 'assert_output()' {
run echo 'have'
assert_output
}
```
On failure, an error message is displayed.
```
-- no output --
expected non-empty output, but output was empty
--
```
#### Partial matching
Partial matching can be enabled with the `--partial` option (`-p` for short).
When used, the assertion fails if the expected *substring* is not found in `$output`.
```bash
@test 'assert_output() partial matching' {
run echo 'ERROR: no such file or directory'
assert_output --partial 'SUCCESS'
}
```
On failure, the substring and the output are displayed.
```
-- output does not contain substring --
substring : SUCCESS
output : ERROR: no such file or directory
--
```
This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Regular expression matching
Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
When used, the assertion fails if the *extended regular expression* does not match `$output`.
> _**Note**:
> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._
```bash
@test 'assert_output() regular expression matching' {
run echo 'Foobar 0.1.0'
assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
}
```
On failure, the regular expression and the output are displayed.
```
-- regular expression does not match output --
regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
output : Foobar 0.1.0
--
```
An error is displayed if the specified extended regular expression is invalid.
This option and partial matching (`--partial` or `-p`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Standard Input, HereDocs and HereStrings
The expected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option.
```bash
@test 'assert_output() with pipe' {
run echo 'hello'
echo 'hello' | assert_output -
}
@test 'assert_output() with herestring' {
run echo 'hello'
assert_output - <<< hello
}
```
### `refute_output`
This function helps to verify that a command or function produces the correct output by checking that the specified unexpected output does not match the actual output.
Matching can be literal (default), partial or regular expression.
This function is the logical complement of `assert_output`.
#### Literal matching
By default, literal matching is performed.
The assertion fails if `$output` equals the unexpected output.
```bash
@test 'refute_output()' {
run echo 'want'
refute_output 'want'
}
```
On failure, the output is displayed.
```
-- output equals, but it was expected to differ --
output : want
--
```
If output is longer than one line it is displayed in *multi-line* format.
#### Existence
To assert that there is no output at all, simply omit the matching argument.
```bash
@test 'refute_output()' {
run foo --silent
refute_output
}
```
On failure, an error message is displayed.
```
-- unexpected output --
expected no output, but output was non-empty
--
```
#### Partial matching
Partial matching can be enabled with the `--partial` option (`-p` for short).
When used, the assertion fails if the unexpected *substring* is found in `$output`.
```bash
@test 'refute_output() partial matching' {
run echo 'ERROR: no such file or directory'
refute_output --partial 'ERROR'
}
```
On failure, the substring and the output are displayed.
```
-- output should not contain substring --
substring : ERROR
output : ERROR: no such file or directory
--
```
This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Regular expression matching
Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
When used, the assertion fails if the *extended regular expression* matches `$output`.
> _**Note**:
> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._
```bash
@test 'refute_output() regular expression matching' {
run echo 'Foobar v0.1.0'
refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
}
```
On failure, the regular expression and the output are displayed.
```
-- regular expression should not match output --
regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
output : Foobar v0.1.0
--
```
An error is displayed if the specified extended regular expression is invalid.
This option and partial matching (`--partial` or `-p`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Standard Input, HereDocs and HereStrings
The unexpected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option.
```bash
@test 'refute_output() with pipe' {
run echo 'hello'
echo 'world' | refute_output -
}
@test 'refute_output() with herestring' {
run echo 'hello'
refute_output - <<< world
}
```
### `assert_line`
Similarly to `assert_output`, this function helps to verify that a command or function produces the correct output.
It checks that the expected line appears in the output (default) or in a specific line of it.
Matching can be literal (default), partial or regular expression.
This function is the logical complement of `refute_line`.
> _**Warning**:
> Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
> causing line indices to change and preventing testing for empty lines._
[bats-93]: https://github.com/sstephenson/bats/pull/93
#### Looking for a line in the output
By default, the entire output is searched for the expected line.
The assertion fails if the expected line is not found in `${lines[@]}`.
```bash
@test 'assert_line() looking for line' {
run echo $'have-0\nhave-1\nhave-2'
assert_line 'want'
}
```
On failure, the expected line and the output are displayed.
> _**Warning**:
> The output displayed does not contain empty lines.
> See the Warning above for more._
```
-- output does not contain line --
line : want
output (3 lines):
have-0
have-1
have-2
--
```
If output is not longer than one line, it is displayed in *two-column* format.
#### Matching a specific line
When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
The assertion fails if the expected line does not equal `${lines[<idx>]}`.
```bash
@test 'assert_line() specific line' {
run echo $'have-0\nhave-1\nhave-2'
assert_line --index 1 'want-1'
}
```
On failure, the index and the compared lines are displayed.
```
-- line differs --
index : 1
expected : want-1
actual : have-1
--
```
#### Partial matching
Partial matching can be enabled with the `--partial` option (`-p` for short).
When used, a match fails if the expected *substring* is not found in the matched line.
```bash
@test 'assert_line() partial matching' {
run echo $'have 1\nhave 2\nhave 3'
assert_line --partial 'want'
}
```
On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
```
-- no output line contains substring --
substring : want
output (3 lines):
have 1
have 2
have 3
--
```
This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Regular expression matching
Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
When used, a match fails if the *extended regular expression* does not match the line being tested.
> _**Note**:
> As expected, the anchors `^` and `$` bind to the beginning and the end of the matched line, respectively._
```bash
@test 'assert_line() regular expression matching' {
run echo $'have-0\nhave-1\nhave-2'
assert_line --index 1 --regexp '^want-[0-9]$'
}
```
On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
```
-- regular expression does not match line --
index : 1
regexp : ^want-[0-9]$
line : have-1
--
```
An error is displayed if the specified extended regular expression is invalid.
This option and partial matching (`--partial` or `-p`) are mutually exclusive.
An error is displayed when used simultaneously.
### `refute_line`
Similarly to `refute_output`, this function helps to verify that a command or function produces the correct output.
It checks that the unexpected line does not appear in the output (default) or in a specific line of it.
Matching can be literal (default), partial or regular expression.
This function is the logical complement of `assert_line`.
> _**Warning**:
> Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
> causing line indices to change and preventing testing for empty lines._
[bats-93]: https://github.com/sstephenson/bats/pull/93
#### Looking for a line in the output
By default, the entire output is searched for the unexpected line.
The assertion fails if the unexpected line is found in `${lines[@]}`.
```bash
@test 'refute_line() looking for line' {
run echo $'have-0\nwant\nhave-2'
refute_line 'want'
}
```
On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
> _**Warning**:
> The output displayed does not contain empty lines.
> See the Warning above for more._
```
-- line should not be in output --
line : want
index : 1
output (3 lines):
have-0
> want
have-2
--
```
If output is not longer than one line, it is displayed in *two-column* format.
#### Matching a specific line
When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
The assertion fails if the unexpected line equals `${lines[<idx>]}`.
```bash
@test 'refute_line() specific line' {
run echo $'have-0\nwant-1\nhave-2'
refute_line --index 1 'want-1'
}
```
On failure, the index and the unexpected line are displayed.
```
-- line should differ --
index : 1
line : want-1
--
```
#### Partial matching
Partial matching can be enabled with the `--partial` option (`-p` for short).
When used, a match fails if the unexpected *substring* is found in the matched line.
```bash
@test 'refute_line() partial matching' {
run echo $'have 1\nwant 2\nhave 3'
refute_line --partial 'want'
}
```
On failure, in addition to the details of literal matching, the substring is also displayed.
When used with `--index <idx>` the substring replaces the unexpected line.
```
-- no line should contain substring --
substring : want
index : 1
output (3 lines):
have 1
> want 2
have 3
--
```
This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive.
An error is displayed when used simultaneously.
#### Regular expression matching
Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
When used, a match fails if the *extended regular expression* matches the line being tested.
> _**Note**:
> As expected, the anchors `^` and `$` bind to the beginning and the end of the matched line, respectively._
```bash
@test 'refute_line() regular expression matching' {
run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
}
```
On failure, in addition to the details of literal matching, the regular expression is also displayed.
When used with `--index <idx>` the regular expression replaces the unexpected line.
```
-- regular expression should not match line --
index : 0
regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
line : Foobar v0.1.0
--
```
An error is displayed if the specified extended regular expression is invalid.
This option and partial matching (`--partial` or `-p`) are mutually exclusive.
An error is displayed when used simultaneously.
### `assert_regex`
This function is similar to `assert_equal` but uses pattern matching instead of
equality, by wrapping `[[ value =~ pattern ]]`.
Fail if the value (first parameter) does not match the pattern (second
parameter).
```bash
@test 'assert_regex()' {
assert_regex 'what' 'x$'
}
```
On failure, the value and the pattern are displayed.
```
-- values does not match regular expression --
value : what
pattern : x$
--
```
If the value is longer than one line then it is displayed in *multi-line*
format.
An error is displayed if the specified extended regular expression is invalid.
For description of the matching behavior, refer to the documentation of the
`=~` operator in the [Bash manual][bash-conditional].
> _**Note**:
> the `BASH_REMATCH` array is available immediately after the assertion succeeds but is fragile;
> i.e. prone to being overwritten as a side effect of other actions._
### `refute_regex`
This function is similar to `refute_equal` but uses pattern matching instead of
equality, by wrapping `! [[ value =~ pattern ]]`.
Fail if the value (first parameter) matches the pattern (second parameter).
```bash
@test 'refute_regex()' {
refute_regex 'WhatsApp' 'Threema'
}
```
On failure, the value, the pattern and the match are displayed.
```
@test 'refute_regex()' {
refute_regex 'WhatsApp' 'What.'
}
-- value matches regular expression --
value : WhatsApp
pattern : What.
match : Whats
case : sensitive
--
```
If the value or pattern is longer than one line then it is displayed in
*multi-line* format.
An error is displayed if the specified extended regular expression is invalid.
For description of the matching behavior, refer to the documentation of the
`=~` operator in the [Bash manual][bash-conditional].
> _**Note**:
> the `BASH_REMATCH` array is available immediately after the assertion fails but is fragile;
> i.e. prone to being overwritten as a side effect of other actions like calling `run`.
> Thus, it's good practice to avoid using `BASH_REMATCH` in conjunction with `refute_regex()`.
> The valuable information the array contains is the matching part of the value which is printed in the failing test log, as mentioned above._
<!-- REFERENCES -->
[bats]: https://github.com/bats-core/bats-core
[bash-comp-cmd]: https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands
[bash-conditional]: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs
[bats-docs]: https://bats-core.readthedocs.io/
[bats-support-output]: https://github.com/bats-core/bats-support#output-formatting
[bats-support]: https://github.com/bats-core/bats-support
+33
View File
@@ -0,0 +1,33 @@
# bats-assert - Common assertions for Bats
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
# Assertions are functions that perform a test and output relevant
# information on failure to help debugging. They return 1 on failure
# and 0 otherwise.
#
# All output is formatted for readability using the functions of
# `output.bash' and sent to the standard error.
# shellcheck disable=1090
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash"
+36
View File
@@ -0,0 +1,36 @@
{
"name": "bats-assert",
"version": "2.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bats-assert",
"version": "2.1.0",
"license": "CC0-1.0",
"devDependencies": {
"bats": "^1",
"bats-support": "^0.3"
},
"peerDependencies": {
"bats": "0.4 || ^1",
"bats-support": "^0.3"
}
},
"node_modules/bats": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/bats/-/bats-1.9.0.tgz",
"integrity": "sha512-Z5BJaAmmHv/ujj7obhjEzJ//OL+ZtjVq0iRnHu+2fE9OeUaPMbJpBgYiOdNbDrG3E2hqe84/AXNnS/UiXl/UcA==",
"dev": true,
"bin": {
"bats": "bin/bats"
}
},
"node_modules/bats-support": {
"version": "0.3.0",
"resolved": "git+ssh://git@github.com/jasonkarns/bats-support.git#24a72e14349690bcbf7c151b9d2d1cdd32d36eb1",
"integrity": "sha512-42f2THEaN02dVj7Zhj0C4AM5FdePq3YPlXvnu/fKUQwCv1qM2dyACBKFXQkLw9QMzhDmwuFcke90XwHMUsBjGQ==",
"dev": true
}
}
}
+48
View File
@@ -0,0 +1,48 @@
{
"name": "bats-assert",
"version": "2.1.0",
"description": "Common assertions for Bats",
"homepage": "https://github.com/bats-core/bats-assert",
"license": "CC0-1.0",
"author": "Zoltán Tömböl (https://github.com/ztombol)",
"contributors": [
"Sam Stephenson <sstephenson@gmail.com> (http://sstephenson.us/)",
"Jason Karns <jason.karns@gmail.com> (http://jason.karns.name)",
"Mislav Marohnić <mislav.marohnic@gmail.com> (http://mislav.net/)",
"Tim Pope (https://github.com/tpope)"
],
"repository": "github:bats-core/bats-assert",
"bugs": "https://github.com/bats-core/bats-assert/issues",
"directories": {
"lib": "src",
"test": "test"
},
"files": [
"load.bash",
"src"
],
"scripts": {
"test": "bats ${CI+-t} test",
"postversion": "npm publish",
"prepublishOnly": "npm run publish:github",
"publish:github": "git push --follow-tags"
},
"devDependencies": {
"bats": "^1",
"bats-support": "^0.3"
},
"peerDependencies": {
"bats": "0.4 || ^1",
"bats-support": "^0.3"
},
"keywords": [
"bats",
"bash",
"shell",
"test",
"unit",
"assert",
"assertion",
"helper"
]
}
+42
View File
@@ -0,0 +1,42 @@
# assert
# ======
#
# Summary: Fail if the given expression evaluates to false.
#
# Usage: assert <expression>
# Options:
# <expression> The expression to evaluate for truthiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the failed expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to true
# 1 - otherwise
#
# ```bash
# @test 'assert()' {
# touch '/var/log/test.log'
# assert [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the failed expression is displayed.
#
# ```
# -- assertion failed --
# expression : [ -e /var/log/test.log ]
# --
# ```
assert() {
if ! "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion failed' \
| fail
fi
}
@@ -0,0 +1,42 @@
# assert_equal
# ============
#
# Summary: Fail if the actual and expected values are not equal.
#
# Usage: assert_equal <actual> <expected>
#
# Options:
# <actual> The value being compared.
# <expected> The value to compare against.
#
# ```bash
# @test 'assert_equal()' {
# assert_equal 'have' 'want'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if values equal
# 1 - otherwise
#
# On failure, the expected and actual values are displayed.
#
# ```
# -- values do not equal --
# expected : want
# actual : have
# --
# ```
assert_equal() {
if [[ $1 != "$2" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$2" \
'actual' "$1" \
| batslib_decorate 'values do not equal' \
| fail
fi
}
@@ -0,0 +1,78 @@
# assert_failure
# ==============
#
# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status.
#
# Usage: assert_failure [<expected_status>]
#
# Options:
# <expected_status> The specific status code to check against.
# If not provided, simply asserts status is != 0.
#
# IO:
# STDERR - `$output`, on failure;
# - also, `$status` and `expected_status`, if provided
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0,
# or if expected_status is provided but does not equal `$status'
# 1 - otherwise
#
# ```bash
# @test 'assert_failure() status only' {
# run echo 'Success!'
# assert_failure
# }
# ```
#
# On failure, `$output` is displayed.
#
# ```
# -- command succeeded, but it was expected to fail --
# output : Success!
# --
# ```
#
# ## Expected status
#
# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`.
#
# ```bash
# @test 'assert_failure() with expected status' {
# run bash -c "echo 'Error!'; exit 1"
# assert_failure 2
# }
# ```
#
# On failure, both the expected and actual statuses, and `$output` are displayed.
#
# ```
# -- command failed as expected, but status differs --
# expected : 2
# actual : 1
# output : Error!
# --
# ```
assert_failure() {
: "${output?}"
: "${status?}"
(( $# > 0 )) && local -r expected="$1"
if (( status == 0 )); then
batslib_print_kv_single_or_multi 6 'output' "$output" \
| batslib_decorate 'command succeeded, but it was expected to fail' \
| fail
elif (( $# > 0 )) && (( status != expected )); then
{ local -ir width=8
batslib_print_kv_single "$width" \
'expected' "$expected" \
'actual' "$status"
batslib_print_kv_single_or_multi "$width" \
'output' "$output"
} \
| batslib_decorate 'command failed as expected, but status differs' \
| fail
fi
}
+248
View File
@@ -0,0 +1,248 @@
# assert_line
# ===========
#
# Summary: Fail if the expected line is not found in the output (default) or at a specific line number.
#
# Usage: assert_line [-n index] [-p | -e] [--] <expected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `expected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `expected` as an extended regular expression
# <expected> The expected line string, substring, or regular expression
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if matching line found
# 1 - otherwise
#
# Similarly to `assert_output`, this function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_line`.)
# It checks that the expected line appears in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the expected line.
# The assertion fails if the expected line is not found in `${lines[@]}`.
#
# ```bash
# @test 'assert_line() looking for line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line 'want'
# }
# ```
#
# On failure, the expected line and the output are displayed.
#
# ```
# -- output does not contain line --
# line : want
# output (3 lines):
# have-0
# have-1
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
# The assertion fails if the expected line does not equal `${lines[<idx>]}`.
#
# ```bash
# @test 'assert_line() specific line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the compared lines are displayed.
#
# ```
# -- line differs --
# index : 1
# expected : want-1
# actual : have-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the expected *substring* is not found in the matched line.
#
# ```bash
# @test 'assert_line() partial matching' {
# run echo $'have 1\nhave 2\nhave 3'
# assert_line --partial 'want'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
#
# ```
# -- no output line contains substring --
# substring : want
# output (3 lines):
# have 1
# have 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* does not match the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'assert_line() regular expression matching' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 --regexp '^want-[0-9]$'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
#
# ```
# -- regular expression does not match line --
# index : 1
# regexp : ^want-[0-9]$
# line : have-1
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
assert_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Arguments.
local -r expected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if ! [[ ${lines[$idx]} =~ $expected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression does not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} != *"$expected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line does not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} != "$expected" ]]; then
batslib_print_kv_single 8 \
'index' "$idx" \
'expected' "$expected" \
'actual' "${lines[$idx]}" \
| batslib_decorate 'line differs' \
| fail
fi
fi
else
# Contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} =~ $expected ]] && return 0
done
{ local -ar single=( 'regexp' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line matches regular expression' \
| fail
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
done
{ local -ar single=( 'substring' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line contains substring' \
| fail
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == "$expected" ]] && return 0
done
{ local -ar single=( 'line' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'output does not contain line' \
| fail
fi
fi
}
@@ -0,0 +1,42 @@
# assert_not_equal
# ============
#
# Summary: Fail if the actual and unexpected values are equal.
#
# Usage: assert_not_equal <actual> <unexpected>
#
# Options:
# <actual> The value being compared.
# <unexpected> The value to compare against.
#
# ```bash
# @test 'assert_not_equal()' {
# assert_not_equal 'foo' 'foo'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if actual does not equal unexpected
# 1 - otherwise
#
# On failure, the unexpected and actual values are displayed.
#
# ```
# -- values should not be equal --
# unexpected : foo
# actual : foo
# --
# ```
assert_not_equal() {
if [[ "$1" == "$2" ]]; then
batslib_print_kv_single_or_multi 10 \
'unexpected' "$2" \
'actual' "$1" \
| batslib_decorate 'values should not be equal' \
| fail
fi
}
@@ -0,0 +1,197 @@
# assert_output
# =============
#
# Summary: Fail if `$output' does not match the expected output.
#
# Usage: assert_output [-p | -e] [- | [--] <expected>]
#
# Options:
# -p, --partial Match if `expected` is a substring of `$output`
# -e, --regexp Treat `expected` as an extended regular expression
# -, --stdin Read `expected` value from STDIN
# <expected> The expected value, substring or regular expression
#
# IO:
# STDIN - [=$1] expected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the expected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` does not equal the expected output.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output 'want'
# }
#
# @test 'assert_output() with pipe' {
# run echo 'hello'
# echo 'hello' | assert_output -
# }
#
# @test 'assert_output() with herestring' {
# run echo 'hello'
# assert_output - <<< hello
# }
# ```
#
# On failure, the expected and actual output are displayed.
#
# ```
# -- output differs --
# expected : want
# actual : have
# --
# ```
#
# ## Existence
#
# To assert that any output exists at all, omit the `expected` argument.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- no output --
# expected non-empty output, but output was empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the expected _substring_ is not found in `$output`.
#
# ```bash
# @test 'assert_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# assert_output --partial 'SUCCESS'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output does not contain substring --
# substring : SUCCESS
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* does not match `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'assert_output() regular expression matching' {
# run echo 'Foobar 0.1.0'
# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression does not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar 0.1.0
# --
# ```
assert_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_nonempty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_nonempty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
# Arguments.
local expected
if (( use_stdin )); then
expected="$(cat -)"
else
expected="${1-}"
fi
# Matching.
if (( is_mode_nonempty )); then
if [ -z "$output" ]; then
echo 'expected non-empty output, but output was empty' \
| batslib_decorate 'no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_output' \
| fail
elif ! [[ $output =~ $expected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$expected" \
'output' "$output" \
| batslib_decorate 'regular expression does not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output != *"$expected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$expected" \
'output' "$output" \
| batslib_decorate 'output does not contain substring' \
| fail
fi
else
if [[ $output != "$expected" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$expected" \
'actual' "$output" \
| batslib_decorate 'output differs' \
| fail
fi
fi
}
@@ -0,0 +1,56 @@
# `assert_regex`
#
# This function is similar to `assert_equal` but uses pattern matching instead
# of equality, by wrapping `[[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) does not match the pattern (second
# parameter).
#
# ```bash
# @test 'assert_regex()' {
# assert_regex 'what' 'x$'
# }
# ```
#
# On failure, the value and the pattern are displayed.
#
# ```
# -- values does not match regular expression --
# value : what
# pattern : x$
# --
# ```
#
# If the value is longer than one line then it is displayed in *multi-line*
# format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion succeeds but is fragile, i.e. prone to being overwritten as a side
# effect of other actions.
assert_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]]; (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: assert_regex' \
| fail
elif ! [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value does not match regular expression' \
| fail
fi
}
@@ -0,0 +1,44 @@
# assert_success
# ==============
#
# Summary: Fail if `$status` is not 0.
#
# Usage: assert_success
#
# IO:
# STDERR - `$status` and `$output`, on failure
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0
# 1 - otherwise
#
# ```bash
# @test 'assert_success() status only' {
# run bash -c "echo 'Error!'; exit 1"
# assert_success
# }
# ```
#
# On failure, `$status` and `$output` are displayed.
#
# ```
# -- command failed --
# status : 1
# output : Error!
# --
# ```
assert_success() {
: "${output?}"
: "${status?}"
if (( status != 0 )); then
{ local -ir width=6
batslib_print_kv_single "$width" 'status' "$status"
batslib_print_kv_single_or_multi "$width" 'output' "$output"
} \
| batslib_decorate 'command failed' \
| fail
fi
}
+42
View File
@@ -0,0 +1,42 @@
# refute
# ======
#
# Summary: Fail if the given expression evaluates to true.
#
# Usage: refute <expression>
#
# Options:
# <expression> The expression to evaluate for falsiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the successful expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to false
# 1 - otherwise
#
# ```bash
# @test 'refute()' {
# rm -f '/var/log/test.log'
# refute [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the successful expression is displayed.
#
# ```
# -- assertion succeeded, but it was expected to fail --
# expression : [ -e /var/log/test.log ]
# --
# ```
refute() {
if "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
| fail
fi
}
+271
View File
@@ -0,0 +1,271 @@
# refute_line
# ===========
#
# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number.
#
# Usage: refute_line [-n index] [-p | -e] [--] <unexpected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `unexpected` as an extended regular expression
# <unexpected> The unexpected line string, substring, or regular expression.
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if match not found
# 1 - otherwise
#
# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_line`.)
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the unexpected line.
# The assertion fails if the unexpected line is found in `${lines[@]}`.
#
# ```bash
# @test 'refute_line() looking for line' {
# run echo $'have-0\nwant\nhave-2'
# refute_line 'want'
# }
# ```
#
# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
#
# ```
# -- line should not be in output --
# line : want
# index : 1
# output (3 lines):
# have-0
# > want
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
# The assertion fails if the unexpected line equals `${lines[<idx>]}`.
#
# ```bash
# @test 'refute_line() specific line' {
# run echo $'have-0\nwant-1\nhave-2'
# refute_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the unexpected line are displayed.
#
# ```
# -- line should differ --
# index : 1
# line : want-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the unexpected *substring* is found in the matched line.
#
# ```bash
# @test 'refute_line() partial matching' {
# run echo $'have 1\nwant 2\nhave 3'
# refute_line --partial 'want'
# }
# ```
#
# On failure, in addition to the details of literal matching, the substring is also displayed.
# When used with `--index <idx>` the substring replaces the unexpected line.
#
# ```
# -- no line should contain substring --
# substring : want
# index : 1
# output (3 lines):
# have 1
# > want 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* matches the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'refute_line() regular expression matching' {
# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, in addition to the details of literal matching, the regular expression is also displayed.
# When used with `--index <idx>` the regular expression replaces the unexpected line.
#
# ```
# -- regular expression should not match line --
# index : 0
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# line : Foobar v0.1.0
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
refute_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Arguments.
local -r unexpected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if [[ ${lines[$idx]} =~ $unexpected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression should not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} == "$unexpected" ]]; then
batslib_print_kv_single 5 \
'index' "$idx" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should differ' \
| fail
fi
fi
else
# Line contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} =~ $unexpected ]]; then
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should match the regular expression' \
| fail
return $?
fi
done
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should contain substring' \
| fail
return $?
fi
done
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == "$unexpected" ]]; then
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'line should not be in output' \
| fail
return $?
fi
done
fi
fi
}
@@ -0,0 +1,199 @@
# refute_output
# =============
#
# Summary: Fail if `$output' matches the unexpected output.
#
# Usage: refute_output [-p | -e] [- | [--] <unexpected>]
#
# Options:
# -p, --partial Match if `unexpected` is a substring of `$output`
# -e, --regexp Treat `unexpected` as an extended regular expression
# -, --stdin Read `unexpected` value from STDIN
# <unexpected> The unexpected value, substring, or regular expression
#
# IO:
# STDIN - [=$1] unexpected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the unexpected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` equals the unexpected output.
#
# ```bash
# @test 'refute_output()' {
# run echo 'want'
# refute_output 'want'
# }
#
# @test 'refute_output() with pipe' {
# run echo 'hello'
# echo 'world' | refute_output -
# }
#
# @test 'refute_output() with herestring' {
# run echo 'hello'
# refute_output - <<< world
# }
# ```
#
# On failure, the output is displayed.
#
# ```
# -- output equals, but it was expected to differ --
# output : want
# --
# ```
#
# ## Existence
#
# To assert that there is no output at all, omit the matching argument.
#
# ```bash
# @test 'refute_output()' {
# run foo --silent
# refute_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- unexpected output --
# expected no output, but output was non-empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the unexpected _substring_ is found in `$output`.
#
# ```bash
# @test 'refute_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# refute_output --partial 'ERROR'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output should not contain substring --
# substring : ERROR
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* matches `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'refute_output() regular expression matching' {
# run echo 'Foobar v0.1.0'
# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression should not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar v0.1.0
# --
# ```
refute_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_empty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_empty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Arguments.
local unexpected
if (( use_stdin )); then
unexpected="$(cat -)"
else
unexpected="${1-}"
fi
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Matching.
if (( is_mode_empty )); then
if [ -n "$output" ]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output non-empty, but expected no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ $output =~ $unexpected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$unexpected" \
'output' "$output" \
| batslib_decorate 'regular expression should not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output == *"$unexpected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$unexpected" \
'output' "$output" \
| batslib_decorate 'output should not contain substring' \
| fail
fi
else
if [[ $output == "$unexpected" ]]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output equals, but it was expected to differ' \
| fail
fi
fi
}
@@ -0,0 +1,66 @@
# `refute_regex`
#
# This function is similar to `refute_equal` but uses pattern matching instead
# of equality, by wrapping `! [[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) matches the pattern (second parameter).
#
# ```bash
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'Threema'
# }
# ```
#
# On failure, the value, the pattern and the match are displayed.
#
# ```
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'What.'
# }
#
# -- value matches regular expression --
# value : WhatsApp
# pattern : What.
# match : Whats
# case : sensitive
# --
# ```
#
# If the value or pattern is longer than one line then it is displayed in
# *multi-line* format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
#
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion fails but is fragile, i.e. prone to being overwritten as a side
# effect of other actions like calling `run`. Thus, it's good practice to avoid
# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable
# information the array contains is the matching part of the value which is
# printed in the failing test log, as mentioned above.
refute_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: refute_regex' \
| fail
elif [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'match' "${BASH_REMATCH[0]}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value matches regular expression' \
| fail
fi
}
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env bats
load test_helper
@test 'assert() <expression>: returns 0 if <expression> evaluates to TRUE' {
run assert true
assert_test_pass
}
@test 'assert() <expression>: returns 1 and displays <expression> if it evaluates to FALSE' {
run assert false
assert_test_fail <<'ERR_MSG'
-- assertion failed --
expression : false
--
ERR_MSG
}
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bats
load test_helper
@test 'assert_equal() <actual> <expected>: returns 0 if <actual> equals <expected>' {
run assert_equal 'a' 'a'
assert_test_pass
}
@test 'assert_equal() <actual> <expected>: returns 1 and displays details if <actual> does not equal <expected>' {
run assert_equal 'a' 'b'
assert_test_fail <<'ERR_MSG'
-- values do not equal --
expected : b
actual : a
--
ERR_MSG
}
@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <actual> is longer than one line' {
run assert_equal $'a 0\na 1' 'b'
assert_test_fail <<'ERR_MSG'
-- values do not equal --
expected (1 lines):
b
actual (2 lines):
a 0
a 1
--
ERR_MSG
}
@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <expected> is longer than one line' {
run assert_equal 'a' $'b 0\nb 1'
assert_test_fail <<'ERR_MSG'
-- values do not equal --
expected (2 lines):
b 0
b 1
actual (1 lines):
a
--
ERR_MSG
}
@test 'assert_equal() <actual> <expected>: performs literal matching' {
run assert_equal 'a' '*'
assert_test_fail <<'ERR_MSG'
-- values do not equal --
expected : *
actual : a
--
ERR_MSG
}
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bats
load test_helper
@test "assert_failure(): returns 0 if \`\$status' is not 0" {
run false
run assert_failure
assert_test_pass
}
@test "assert_failure(): returns 1 and displays details if \`\$status' is 0" {
run bash -c 'echo "a"
exit 0'
run assert_failure
assert_test_fail <<'ERR_MSG'
-- command succeeded, but it was expected to fail --
output : a
--
ERR_MSG
}
@test "assert_failure(): displays \`\$output' in multi-line format if it is longer then one line" {
run bash -c 'printf "a 0\na 1"
exit 0'
run assert_failure
assert_test_fail <<'ERR_MSG'
-- command succeeded, but it was expected to fail --
output (2 lines):
a 0
a 1
--
ERR_MSG
}
@test "assert_failure() <status>: returns 0 if \`\$status' equals <status>" {
run bash -c 'exit 1'
run assert_failure 1
assert_test_pass
}
@test "assert_failure() <status>: returns 1 and displays details if \`\$status' does not equal <status>" {
run bash -c 'echo "a"
exit 1'
run assert_failure 2
assert_test_fail <<'ERR_MSG'
-- command failed as expected, but status differs --
expected : 2
actual : 1
output : a
--
ERR_MSG
}
@test "assert_failure() <status>: displays \`\$output' in multi-line format if it is longer then one line" {
run bash -c 'printf "a 0\na 1"
exit 1'
run assert_failure 2
assert_test_fail <<'ERR_MSG'
-- command failed as expected, but status differs --
expected : 2
actual : 1
output (2 lines):
a 0
a 1
--
ERR_MSG
}
+351
View File
@@ -0,0 +1,351 @@
#!/usr/bin/env bats
load test_helper
###############################################################################
# Containing a line
###############################################################################
#
# Literal matching
#
# Correctness
@test "assert_line() <expected>: returns 0 if <expected> is a line in \`\${lines[@]}'" {
run printf 'a\nb\nc'
run assert_line 'b'
assert_test_pass
}
@test "assert_line() <expected>: returns 1 and displays details if <expected> is not a line in \`\${lines[@]}'" {
run echo 'b'
run assert_line 'a'
assert_test_fail <<'ERR_MSG'
-- output does not contain line --
line : a
output : b
--
ERR_MSG
}
# Output formatting
@test "assert_line() <expected>: displays \`\$output' in multi-line format if it is longer than one line" {
run printf 'b 0\nb 1'
run assert_line 'a'
assert_test_fail <<'ERR_MSG'
-- output does not contain line --
line : a
output (2 lines):
b 0
b 1
--
ERR_MSG
}
# Options
@test 'assert_line() <expected>: performs literal matching by default' {
run echo 'a'
run assert_line '*'
assert_test_fail <<'ERR_MSG'
-- output does not contain line --
line : *
output : a
--
ERR_MSG
}
#
# Partial matching: `-p' and `--partial'
#
# Options
@test 'assert_line() -p <partial>: enables partial matching' {
run printf 'a\n_b_\nc'
run assert_line -p 'b'
assert_test_pass
}
@test 'assert_line() --partial <partial>: enables partial matching' {
run printf 'a\n_b_\nc'
run assert_line --partial 'b'
assert_test_pass
}
# Correctness
@test "assert_line() --partial <partial>: returns 0 if <partial> is a substring in any line in \`\${lines[@]}'" {
run printf 'a\n_b_\nc'
run assert_line --partial 'b'
assert_test_pass
}
@test "assert_line() --partial <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${lines[@]}'" {
run echo 'b'
run assert_line --partial 'a'
assert_test_fail <<'ERR_MSG'
-- no output line contains substring --
substring : a
output : b
--
ERR_MSG
}
# Output formatting
@test "assert_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" {
run printf 'b 0\nb 1'
run assert_line --partial 'a'
assert_test_fail <<'ERR_MSG'
-- no output line contains substring --
substring : a
output (2 lines):
b 0
b 1
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
# Options
@test 'assert_line() -e <regexp>: enables regular expression matching' {
run printf 'a\n_b_\nc'
run assert_line -e '^.b'
assert_test_pass
}
@test 'assert_line() --regexp <regexp>: enables regular expression matching' {
run printf 'a\n_b_\nc'
run assert_line --regexp '^.b'
assert_test_pass
}
# Correctness
@test "assert_line() --regexp <regexp>: returns 0 if <regexp> matches any line in \`\${lines[@]}'" {
run printf 'a\n_b_\nc'
run assert_line --regexp '^.b'
assert_test_pass
}
@test "assert_line() --regexp <regexp>: returns 1 and displays details if <regexp> does not match any lines in \`\${lines[@]}'" {
run echo 'b'
run assert_line --regexp '^.a'
assert_test_fail <<'ERR_MSG'
-- no output line matches regular expression --
regexp : ^.a
output : b
--
ERR_MSG
}
# Output formatting
@test "assert_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" {
run printf 'b 0\nb 1'
run assert_line --regexp '^.a'
assert_test_fail <<'ERR_MSG'
-- no output line matches regular expression --
regexp : ^.a
output (2 lines):
b 0
b 1
--
ERR_MSG
}
###############################################################################
# Matching single line: `-n' and `--index'
###############################################################################
# Options
@test 'assert_line() -n <idx> <expected>: matches against the <idx>-th line only' {
run printf 'a\nb\nc'
run assert_line -n 1 'b'
assert_test_pass
}
@test 'assert_line() --index <idx> <expected>: matches against the <idx>-th line only' {
run printf 'a\nb\nc'
run assert_line --index 1 'b'
assert_test_pass
}
@test 'assert_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' {
run assert_line --index 1a
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_line --
`--index' requires an integer argument: `1a'
--
ERR_MSG
}
#
# Literal matching
#
# Correctness
@test "assert_line() --index <idx> <expected>: returns 0 if <expected> equals \`\${lines[<idx>]}'" {
run printf 'a\nb\nc'
run assert_line --index 1 'b'
assert_test_pass
}
@test "assert_line() --index <idx> <expected>: returns 1 and displays details if <expected> does not equal \`\${lines[<idx>]}'" {
run printf 'a\nb\nc'
run assert_line --index 1 'a'
assert_test_fail <<'ERR_MSG'
-- line differs --
index : 1
expected : a
actual : b
--
ERR_MSG
}
# Options
@test 'assert_line() --index <idx> <expected>: performs literal matching by default' {
run printf 'a\nb\nc'
run assert_line --index 1 '*'
assert_test_fail <<'ERR_MSG'
-- line differs --
index : 1
expected : *
actual : b
--
ERR_MSG
}
#
# Partial matching: `-p' and `--partial'
#
# Options
@test 'assert_line() --index <idx> -p <partial>: enables partial matching' {
run printf 'a\n_b_\nc'
run assert_line --index 1 -p 'b'
assert_test_pass
}
@test 'assert_line() --index <idx> --partial <partial>: enables partial matching' {
run printf 'a\n_b_\nc'
run assert_line --index 1 --partial 'b'
assert_test_pass
}
# Correctness
@test "assert_line() --index <idx> --partial <partial>: returns 0 if <partial> is a substring in \`\${lines[<idx>]}'" {
run printf 'a\n_b_\nc'
run assert_line --index 1 --partial 'b'
assert_test_pass
}
@test "assert_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\${lines[<idx>]}'" {
run printf 'b 0\nb 1'
run assert_line --index 1 --partial 'a'
assert_test_fail <<'ERR_MSG'
-- line does not contain substring --
index : 1
substring : a
line : b 1
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
# Options
@test 'assert_line() --index <idx> -e <regexp>: enables regular expression matching' {
run printf 'a\n_b_\nc'
run assert_line --index 1 -e '^.b'
assert_test_pass
}
@test 'assert_line() --index <idx> --regexp <regexp>: enables regular expression matching' {
run printf 'a\n_b_\nc'
run assert_line --index 1 --regexp '^.b'
assert_test_pass
}
# Correctness
@test "assert_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> matches \`\${lines[<idx>]}'" {
run printf 'a\n_b_\nc'
run assert_line --index 1 --regexp '^.b'
assert_test_pass
}
@test "assert_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\${lines[<idx>]}'" {
run printf 'a\nb\nc'
run assert_line --index 1 --regexp '^.a'
assert_test_fail <<'ERR_MSG'
-- regular expression does not match line --
index : 1
regexp : ^.a
line : b
--
ERR_MSG
}
###############################################################################
# Common
###############################################################################
@test "assert_line(): \`--partial' and \`--regexp' are mutually exclusive" {
run assert_line --partial --regexp
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_line --
`--partial' and `--regexp' are mutually exclusive
--
ERR_MSG
}
@test 'assert_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' {
run assert_line --regexp '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_line --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
@test "assert_line(): \`--' stops parsing options" {
run printf 'a\n-p\nc'
run assert_line -- '-p'
assert_test_pass
}
@@ -0,0 +1,57 @@
#!/usr/bin/env bats
load test_helper
@test 'assert_not_equal() <actual> <unexpected>: returns 0 if <actual> does not equal <unexpected>' {
run assert_not_equal foo bar
assert_test_pass
run assert_not_equal "foo" "bar"
assert_test_pass
run assert_not_equal "foo" ""
assert_test_pass
run assert_not_equal "" "foo"
assert_test_pass
}
@test 'assert_not_equal() <actual> <unexpected>: returns 1 and displays details if <actual> equals <unexpected>' {
run assert_not_equal 'foobar' 'foobar'
assert_test_fail <<'ERR_MSG'
-- values should not be equal --
unexpected : foobar
actual : foobar
--
ERR_MSG
run assert_not_equal 1 1
assert_test_fail <<'ERR_MSG'
-- values should not be equal --
unexpected : 1
actual : 1
--
ERR_MSG
}
@test 'assert_not_equal() <actual> <unexpected>: displays details in multi-line format if <actual> and <unexpected> are longer than one line' {
run assert_not_equal $'foo\nbar' $'foo\nbar'
assert_test_fail <<'ERR_MSG'
-- values should not be equal --
unexpected (2 lines):
foo
bar
actual (2 lines):
foo
bar
--
ERR_MSG
}
@test 'assert_not_equal() <actual> <unexpected>: performs literal matching' {
run assert_not_equal 'a' '*'
assert_test_pass
}
+285
View File
@@ -0,0 +1,285 @@
#!/usr/bin/env bats
load test_helper
#
# Literal matching
#
# Correctness
@test "assert_output() <expected>: returns 0 if <expected> equals \`\$output'" {
run echo 'a'
run assert_output 'a'
assert_test_pass
}
@test "assert_output() <expected>: returns 1 and displays details if <expected> does not equal \`\$output'" {
run echo 'b'
run assert_output 'a'
assert_test_fail <<'ERR_MSG'
-- output differs --
expected : a
actual : b
--
ERR_MSG
}
@test 'assert_output(): succeeds if output is non-empty' {
run echo 'a'
run assert_output
assert_test_pass
}
@test 'assert_output(): fails if output is empty' {
run echo ''
run assert_output
assert_test_fail <<'ERR_MSG'
-- no output --
expected non-empty output, but output was empty
--
ERR_MSG
}
@test 'assert_output() - : reads <expected> from STDIN' {
run echo 'a'
run assert_output - <<STDIN
a
STDIN
assert_test_pass
}
@test 'assert_output() --stdin : reads <expected> from STDIN' {
run echo 'a'
run assert_output --stdin <<STDIN
a
STDIN
assert_test_pass
}
# Output formatting
@test "assert_output() <expected>: displays details in multi-line format if \`\$output' is longer than one line" {
run printf 'b 0\nb 1'
run assert_output 'a'
assert_test_fail <<'ERR_MSG'
-- output differs --
expected (1 lines):
a
actual (2 lines):
b 0
b 1
--
ERR_MSG
}
@test 'assert_output() <expected>: displays details in multi-line format if <expected> is longer than one line' {
run echo 'b'
run assert_output $'a 0\na 1'
assert_test_fail <<'ERR_MSG'
-- output differs --
expected (2 lines):
a 0
a 1
actual (1 lines):
b
--
ERR_MSG
}
# Options
@test 'assert_output() <expected>: performs literal matching by default' {
run echo 'a'
run assert_output '*'
assert_test_fail <<'ERR_MSG'
-- output differs --
expected : *
actual : a
--
ERR_MSG
}
#
# Partial matching: `-p' and `--partial'
#
@test 'assert_output() -p <partial>: enables partial matching' {
run echo 'abc'
run assert_output -p 'b'
assert_test_pass
}
@test 'assert_output() --partial <partial>: enables partial matching' {
run echo 'abc'
run assert_output --partial 'b'
assert_test_pass
}
# Correctness
@test "assert_output() --partial <partial>: returns 0 if <partial> is a substring in \`\$output'" {
run printf 'a\nb\nc'
run assert_output --partial 'b'
assert_test_pass
}
@test "assert_output() --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\$output'" {
run echo 'b'
run assert_output --partial 'a'
assert_test_fail <<'ERR_MSG'
-- output does not contain substring --
substring : a
output : b
--
ERR_MSG
}
# Output formatting
@test "assert_output() --partial <partial>: displays details in multi-line format if \`\$output' is longer than one line" {
run printf 'b 0\nb 1'
run assert_output --partial 'a'
assert_test_fail <<'ERR_MSG'
-- output does not contain substring --
substring (1 lines):
a
output (2 lines):
b 0
b 1
--
ERR_MSG
}
@test 'assert_output() --partial <partial>: displays details in multi-line format if <partial> is longer than one line' {
run echo 'b'
run assert_output --partial $'a 0\na 1'
assert_test_fail <<'ERR_MSG'
-- output does not contain substring --
substring (2 lines):
a 0
a 1
output (1 lines):
b
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
@test 'assert_output() -e <regexp>: enables regular expression matching' {
run echo 'abc'
run assert_output -e '^a'
assert_test_pass
}
@test 'assert_output() --regexp <regexp>: enables regular expression matching' {
run echo 'abc'
run assert_output --regexp '^a'
assert_test_pass
}
# Correctness
@test "assert_output() --regexp <regexp>: returns 0 if <regexp> matches \`\$output'" {
run printf 'a\nb\nc'
run assert_output --regexp '.*b.*'
assert_test_pass
}
@test "assert_output() --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\$output'" {
run echo 'b'
run assert_output --regexp '.*a.*'
assert_test_fail <<'ERR_MSG'
-- regular expression does not match output --
regexp : .*a.*
output : b
--
ERR_MSG
}
# Output formatting
@test "assert_output() --regexp <regexp>: displays details in multi-line format if \`\$output' is longer than one line" {
run printf 'b 0\nb 1'
run assert_output --regexp '.*a.*'
assert_test_fail <<'ERR_MSG'
-- regular expression does not match output --
regexp (1 lines):
.*a.*
output (2 lines):
b 0
b 1
--
ERR_MSG
}
@test 'assert_output() --regexp <regexp>: displays details in multi-line format if <regexp> is longer than one line' {
run echo 'b'
run assert_output --regexp $'.*a\nb.*'
assert_test_fail <<'ERR_MSG'
-- regular expression does not match output --
regexp (2 lines):
.*a
b.*
output (1 lines):
b
--
ERR_MSG
}
# Error handling
@test 'assert_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' {
run assert_output --regexp '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_output --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
#
# Common
#
@test "assert_output(): \`--partial' and \`--regexp' are mutually exclusive" {
run assert_output --partial --regexp
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_output --
`--partial' and `--regexp' are mutually exclusive
--
ERR_MSG
}
@test "assert_output(): \`--' stops parsing options" {
run echo '-p'
run assert_output -- '-p'
assert_test_pass
}
@@ -0,0 +1,87 @@
#!/usr/bin/env bats
load test_helper
#
# Literal matching
#
# Correctness
@test "assert_regex() <value> <pattern>: succeeds if a <value> substring matches extended regular expression <pattern>" {
run assert_regex 'abc' '^[a-z]b[c-z]+'
assert_test_pass
}
@test "assert_regex() <value> <pattern>: fails if no <value> substring matches extended regular expression <pattern>" {
run assert_regex 'bcd' '^[a-z]b[c-z]+'
assert_test_fail <<'ERR_MSG'
-- value does not match regular expression --
value : bcd
pattern : ^[a-z]b[c-z]+
case : sensitive
--
ERR_MSG
}
@test "assert_regex() <value> <pattern>: provides results in BASH_REMATCH" {
unset -v BASH_REMATCH
assert_regex 'abcd' 'b.d'
declare -p BASH_REMATCH
[ "${BASH_REMATCH[0]}" = 'bcd' ]
}
@test "assert_regex() <value> <pattern>: matches case-insensitively when 'nocasematch' is set" {
shopt -s nocasematch
assert_regex 'aBc' 'ABC'
}
@test "assert_regex() <value> <pattern>: outputs multi-line <value> nicely when it fails" {
run assert_regex $'bcd\n123' '^[a-z]b[c-z]+'
assert_test_fail <<'ERR_MSG'
-- value does not match regular expression --
value (2 lines):
bcd
123
pattern (1 lines):
^[a-z]b[c-z]+
case (1 lines):
sensitive
--
ERR_MSG
shopt -s nocasematch
run assert_regex $'bcd\n123' '^[a-z]b[c-z]+'
assert_test_fail <<'ERR_MSG'
-- value does not match regular expression --
value (2 lines):
bcd
123
pattern (1 lines):
^[a-z]b[c-z]+
case (1 lines):
insensitive
--
ERR_MSG
}
# Error handling
@test "assert_regex() <value> <pattern>: returns 1 and displays an error message if <pattern> is not a valid extended regular expression" {
run assert_regex value '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: assert_regex --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
@test "assert_regex allows regex matching empty string (see #53)" {
run assert_regex any_value '.*'
assert_success
}
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bats
load test_helper
@test "assert_success(): returns 0 if \`\$status' is 0" {
run true
run assert_success
assert_test_pass
}
@test "assert_success(): returns 1 and displays details if \`\$status' is not 0" {
run bash -c 'echo "a"
exit 1'
run assert_success
assert_test_fail <<'ERR_MSG'
-- command failed --
status : 1
output : a
--
ERR_MSG
}
@test "assert_success(): displays \`\$output' in multi-line format if it is longer than one line" {
run bash -c 'printf "a 0\na 1"
exit 1'
run assert_success
assert_test_fail <<'ERR_MSG'
-- command failed --
status : 1
output (2 lines):
a 0
a 1
--
ERR_MSG
}
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bats
load test_helper
@test 'refute() <expression>: returns 0 if <expression> evaluates to FALSE' {
run refute false
assert_test_pass
}
@test 'refute() <expression>: returns 1 and displays <expression> if it evaluates to TRUE' {
run refute true
assert_test_fail <<'ERR_MSG'
-- assertion succeeded, but it was expected to fail --
expression : true
--
ERR_MSG
}
+344
View File
@@ -0,0 +1,344 @@
#!/usr/bin/env bats
load test_helper
###############################################################################
# Containing a line
###############################################################################
#
# Literal matching
#
# Correctness
@test "refute_line() <unexpected>: returns 0 if <unexpected> is not a line in \`\${lines[@]}'" {
run printf 'a\nb\nc'
run refute_line 'd'
assert_test_pass
}
@test "refute_line() <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${lines[@]}'" {
run echo 'a'
run refute_line 'a'
assert_test_fail <<'ERR_MSG'
-- line should not be in output --
line : a
index : 0
output : a
--
ERR_MSG
}
# Output formatting
@test "refute_line() <unexpected>: displays \`\$output' in multi-line format if it is longer than one line" {
run printf 'a 0\na 1\na 2'
run refute_line 'a 1'
assert_test_fail <<'ERR_MSG'
-- line should not be in output --
line : a 1
index : 1
output (3 lines):
a 0
> a 1
a 2
--
ERR_MSG
}
# Options
@test 'refute_line() <unexpected>: performs literal matching by default' {
run echo 'a'
run refute_line '*'
assert_test_pass
}
#
# Partial matching: `-p' and `--partial'
#
# Options
@test 'refute_line() -p <partial>: enables partial matching' {
run printf 'a\nb\nc'
run refute_line -p 'd'
assert_test_pass
}
@test 'refute_line() --partial <partial>: enables partial matching' {
run printf 'a\nb\nc'
run refute_line --partial 'd'
assert_test_pass
}
# Correctness
@test "refute_line() --partial <partial>: returns 0 if <partial> is not a substring in any line in \`\${lines[@]}'" {
run printf 'a\nb\nc'
run refute_line --partial 'd'
assert_test_pass
}
@test "refute_line() --partial <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${lines[@]}'" {
run echo 'a'
run refute_line --partial 'a'
assert_test_fail <<'ERR_MSG'
-- no line should contain substring --
substring : a
index : 0
output : a
--
ERR_MSG
}
# Output formatting
@test "refute_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" {
run printf 'a\nabc\nc'
run refute_line --partial 'b'
assert_test_fail <<'ERR_MSG'
-- no line should contain substring --
substring : b
index : 1
output (3 lines):
a
> abc
c
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
# Options
@test 'refute_line() -e <regexp>: enables regular expression matching' {
run printf 'a\nb\nc'
run refute_line -e '^.d'
assert_test_pass
}
@test 'refute_line() --regexp <regexp>: enables regular expression matching' {
run printf 'a\nb\nc'
run refute_line --regexp '^.d'
assert_test_pass
}
# Correctness
@test "refute_line() --regexp <regexp>: returns 0 if <regexp> does not match any line in \`\${lines[@]}'" {
run printf 'a\nb\nc'
run refute_line --regexp '.*d.*'
assert_test_pass
}
@test "refute_line() --regexp <regexp>: returns 1 and displays details if <regexp> matches any lines in \`\${lines[@]}'" {
run echo 'a'
run refute_line --regexp '.*a.*'
assert_test_fail <<'ERR_MSG'
-- no line should match the regular expression --
regexp : .*a.*
index : 0
output : a
--
ERR_MSG
}
# Output formatting
@test "refute_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" {
run printf 'a\nabc\nc'
run refute_line --regexp '.*b.*'
assert_test_fail <<'ERR_MSG'
-- no line should match the regular expression --
regexp : .*b.*
index : 1
output (3 lines):
a
> abc
c
--
ERR_MSG
}
###############################################################################
# Matching single line: `-n' and `--index'
###############################################################################
# Options
@test 'refute_line() -n <idx> <expected>: matches against the <idx>-th line only' {
run printf 'a\nb\nc'
run refute_line -n 1 'd'
assert_test_pass
}
@test 'refute_line() --index <idx> <expected>: matches against the <idx>-th line only' {
run printf 'a\nb\nc'
run refute_line --index 1 'd'
assert_test_pass
}
@test 'refute_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' {
run refute_line --index 1a
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_line --
`--index' requires an integer argument: `1a'
--
ERR_MSG
}
#
# Literal matching
#
# Correctness
@test "refute_line() --index <idx> <unexpected>: returns 0 if <unexpected> does not equal \`\${lines[<idx>]}'" {
run printf 'a\nb\nc'
run refute_line --index 1 'd'
assert_test_pass
}
@test "refute_line() --index <idx> <unexpected>: returns 1 and displays details if <unexpected> equals \`\${lines[<idx>]}'" {
run printf 'a\nb\nc'
run refute_line --index 1 'b'
assert_test_fail <<'ERR_MSG'
-- line should differ --
index : 1
line : b
--
ERR_MSG
}
# Options
@test 'refute_line() --index <idx> <unexpected>: performs literal matching by default' {
run printf 'a\nb\nc'
run refute_line --index 1 '*'
assert_test_pass
}
#
# Partial matching: `-p' and `--partial'
#
# Options
@test 'refute_line() --index <idx> -p <partial>: enables partial matching' {
run printf 'a\nb\nc'
run refute_line --index 1 -p 'd'
assert_test_pass
}
@test 'refute_line() --index <idx> --partial <partial>: enables partial matching' {
run printf 'a\nb\nc'
run refute_line --index 1 --partial 'd'
assert_test_pass
}
# Correctness
@test "refute_line() --index <idx> --partial <partial>: returns 0 if <partial> is not a substring in \`\${lines[<idx>]}'" {
run printf 'a\nabc\nc'
run refute_line --index 1 --partial 'd'
assert_test_pass
}
@test "refute_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\${lines[<idx>]}'" {
run printf 'a\nabc\nc'
run refute_line --index 1 --partial 'b'
assert_test_fail <<'ERR_MSG'
-- line should not contain substring --
index : 1
substring : b
line : abc
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
# Options
@test 'refute_line() --index <idx> -e <regexp>: enables regular expression matching' {
run printf 'a\nb\nc'
run refute_line --index 1 -e '^.b'
assert_test_pass
}
@test 'refute_line() --index <idx> --regexp <regexp>: enables regular expression matching' {
run printf 'a\nb\nc'
run refute_line --index 1 --regexp '^.b'
assert_test_pass
}
# Correctness
@test "refute_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> does not match \`\${lines[<idx>]}'" {
run printf 'a\nabc\nc'
run refute_line --index 1 --regexp '.*d.*'
assert_test_pass
}
@test "refute_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\${lines[<idx>]}'" {
run printf 'a\nabc\nc'
run refute_line --index 1 --regexp '.*b.*'
assert_test_fail <<'ERR_MSG'
-- regular expression should not match line --
index : 1
regexp : .*b.*
line : abc
--
ERR_MSG
}
###############################################################################
# Common
###############################################################################
@test "refute_line(): \`--partial' and \`--regexp' are mutually exclusive" {
run refute_line --partial --regexp
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_line --
`--partial' and `--regexp' are mutually exclusive
--
ERR_MSG
}
@test 'refute_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' {
run refute_line --regexp '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_line --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
@test "refute_line(): \`--' stops parsing options" {
run printf 'a\n--\nc'
run refute_line -- '-p'
assert_test_pass
}
+230
View File
@@ -0,0 +1,230 @@
#!/usr/bin/env bats
load test_helper
#
# Literal matching
#
# Correctness
@test "refute_output() <unexpected>: returns 0 if <unexpected> does not equal \`\$output'" {
run echo 'b'
run refute_output 'a'
assert_test_pass
}
@test "refute_output() <unexpected>: returns 1 and displays details if <unexpected> equals \`\$output'" {
run echo 'a'
run refute_output 'a'
assert_test_fail <<'ERR_MSG'
-- output equals, but it was expected to differ --
output : a
--
ERR_MSG
}
@test 'refute_output(): succeeds if output is empty' {
run echo ''
run refute_output
assert_test_pass
}
@test 'refute_output(): fails if output is non-empty' {
run echo 'a'
run refute_output
assert_test_fail <<'ERR_MSG'
-- output non-empty, but expected no output --
output : a
--
ERR_MSG
}
@test 'refute_output() - : reads <unexpected> from STDIN' {
run echo '-'
run refute_output - <<INPUT
b
INPUT
assert_test_pass
}
@test 'refute_output() --stdin : reads <unexpected> from STDIN' {
run echo '--stdin'
run refute_output --stdin <<INPUT
b
INPUT
assert_test_pass
}
# Output formatting
@test 'refute_output() <unexpected>: displays details in multi-line format if necessary' {
run printf 'a 0\na 1'
run refute_output $'a 0\na 1'
assert_test_fail <<'ERR_MSG'
-- output equals, but it was expected to differ --
output (2 lines):
a 0
a 1
--
ERR_MSG
}
# Options
@test 'refute_output() <unexpected>: performs literal matching by default' {
run echo 'a'
run refute_output '*'
assert_test_pass
}
#
# Partial matching: `-p' and `--partial'
#
# Options
@test 'refute_output() -p <partial>: enables partial matching' {
run echo 'abc'
run refute_output -p 'd'
assert_test_pass
}
@test 'refute_output() --partial <partial>: enables partial matching' {
run echo 'abc'
run refute_output --partial 'd'
assert_test_pass
}
# Correctness
@test "refute_output() --partial <partial>: returns 0 if <partial> is not a substring in \`\$output'" {
run printf 'a\nb\nc'
run refute_output --partial 'd'
assert_test_pass
}
@test "refute_output() --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\$output'" {
run echo 'a'
run refute_output --partial 'a'
assert_test_fail <<'ERR_MSG'
-- output should not contain substring --
substring : a
output : a
--
ERR_MSG
}
# Output formatting
@test 'refute_output() --partial <partial>: displays details in multi-line format if necessary' {
run printf 'a 0\na 1'
run refute_output --partial 'a'
assert_test_fail <<'ERR_MSG'
-- output should not contain substring --
substring (1 lines):
a
output (2 lines):
a 0
a 1
--
ERR_MSG
}
#
# Regular expression matching: `-e' and `--regexp'
#
# Options
@test 'refute_output() -e <regexp>: enables regular expression matching' {
run echo 'abc'
run refute_output -e '^d'
assert_test_pass
}
@test 'refute_output() --regexp <regexp>: enables regular expression matching' {
run echo 'abc'
run refute_output --regexp '^d'
assert_test_pass
}
# Correctness
@test "refute_output() --regexp <regexp>: returns 0 if <regexp> does not match \`\$output'" {
run printf 'a\nb\nc'
run refute_output --regexp '.*d.*'
assert_test_pass
}
@test "refute_output() --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\$output'" {
run echo 'a'
run refute_output --regexp '.*a.*'
assert_test_fail <<'ERR_MSG'
-- regular expression should not match output --
regexp : .*a.*
output : a
--
ERR_MSG
}
# Output formatting
@test 'refute_output() --regexp <regexp>: displays details in multi-line format if necessary' {
run printf 'a 0\na 1'
run refute_output --regexp '.*a.*'
assert_test_fail <<'ERR_MSG'
-- regular expression should not match output --
regexp (1 lines):
.*a.*
output (2 lines):
a 0
a 1
--
ERR_MSG
}
# Error handling
@test 'refute_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' {
run refute_output --regexp '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_output --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
#
# Common
#
@test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" {
run refute_output --partial --regexp
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_output --
`--partial' and `--regexp' are mutually exclusive
--
ERR_MSG
}
@test "refute_output(): \`--' stops parsing options" {
run echo '--'
run refute_output -- '-p'
assert_test_pass
}
@@ -0,0 +1,98 @@
#!/usr/bin/env bats
load test_helper
#
# Literal matching
#
# Correctness
@test "refute_regex() <value> <pattern>: fails if a <value> substring matches extended regular expression <pattern>" {
run refute_regex 'abc' '^[a-z]b'
assert_test_fail <<'ERR_MSG'
-- value matches regular expression --
value : abc
pattern : ^[a-z]b
match : ab
case : sensitive
--
ERR_MSG
}
@test "refute_regex() <value> <pattern>: succeeds if no <value> substring matches extended regular expression <pattern>" {
run refute_regex 'bcd' '^[a-z]b[c-z]+'
assert_test_pass
}
@test "refute_regex() <value> <pattern>: provides results in BASH_REMATCH on failure" {
unset -v BASH_REMATCH
refute_regex 'abcd' 'b.d' \
|| {
declare -p BASH_REMATCH && \
[ "${BASH_REMATCH[0]}" = 'bcd' ]
}
}
@test "refute_regex() <value> <pattern>: matches case-insensitively when 'nocasematch' is set" {
shopt -s nocasematch
run refute_regex 'aBc' 'ABC'
assert_test_fail <<'ERR_MSG'
-- value matches regular expression --
value : aBc
pattern : ABC
match : aBc
case : insensitive
--
ERR_MSG
}
@test "refute_regex() <value> <pattern>: outputs multi-line <value> nicely when it fails" {
run refute_regex $'abc\n123' '^[a-z]b[c-z]+'
assert_test_fail <<'ERR_MSG'
-- value matches regular expression --
value (2 lines):
abc
123
pattern (1 lines):
^[a-z]b[c-z]+
match (1 lines):
abc
case (1 lines):
sensitive
--
ERR_MSG
shopt -s nocasematch
run refute_regex $'aBc\n123' '^[a-z]b[c-z]+'
assert_test_fail <<'ERR_MSG'
-- value matches regular expression --
value (2 lines):
aBc
123
pattern (1 lines):
^[a-z]b[c-z]+
match (1 lines):
aBc
case (1 lines):
insensitive
--
ERR_MSG
}
# Error handling
@test "refute_regex() <value> <pattern>: returns 1 and displays an error message if <pattern> is not a valid extended regular expression" {
run refute_regex value '[.*'
assert_test_fail <<'ERR_MSG'
-- ERROR: refute_regex --
Invalid extended regular expression: `[.*'
--
ERR_MSG
}
@@ -0,0 +1,28 @@
# Load dependencies.
BATS_LIB_PATH=$PWD/node_modules:${BATS_LIB_PATH-}
bats_load_library 'bats-support'
# Load library.
load '../load'
# validate that bats-assert is safe to use under -u
set -u
: "${status:=}"
: "${lines:=}"
: "${output:=}"
assert_test_pass() {
test "$status" -eq 0
test "${#lines[@]}" -eq 0
}
assert_test_fail() {
local err_msg="${1-$(cat -)}"
local num_lines
num_lines="$(printf '%s' "$err_msg" | wc -l)"
test "$status" -eq 1
test "${#lines[@]}" -eq "$num_lines"
test "$output" == "$err_msg"
}
+3
View File
@@ -0,0 +1,3 @@
[codespell]
skip = .git,*.pdf,*.svg
ignore-words-list = dne
@@ -0,0 +1,17 @@
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 && \
curl -sSfL https://github.com/shenwei356/rush/releases/download/v0.5.0/rush_linux_amd64.tar.gz | tar xzf - -C /usr/local/bin && \
chmod +x /usr/local/bin/rush
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"}}
}
+35
View File
@@ -0,0 +1,35 @@
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
[test/fixtures/bats/*_no_shellcheck.bats]
ignore = true
+3
View File
@@ -0,0 +1,3 @@
* text=auto
*.sh eol=lf
libexec/* eol=lf
@@ -0,0 +1,30 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'Priority: NeedsTriage, Type: Bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Create example `file.bats` with following contents
2. run `bats --foo file.bats`
3. ...
**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 if applicable]
- operating system (including version): [e.g. Linux (please name your distribution!), FreeBSD, MacOS]
- `bash --version`: [e.g. 5.1]
- Install method: [e.g. git submodule, distribution package manager, npm, homebrew, ...]
**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: 'Priority: NeedsTriage, 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.
+47
View File
@@ -0,0 +1,47 @@
version: 2
updates:
- package-ecosystem: docker
directory: /.devcontainer
schedule:
interval: daily
labels:
- docker
- devcontainer
- dependencies
- no changelog
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
labels:
- github-actions
- dependencies
- no changelog
- package-ecosystem: docker
directory: /
schedule:
interval: daily
labels:
- docker
- dependencies
- no changelog
- package-ecosystem: pip
directory: /docs/source
schedule:
interval: daily
labels:
- pip
- dependencies
- no changelog
- package-ecosystem: npm
directory: /
schedule:
interval: daily
labels:
- npm
- dependencies
- no changelog
+10
View File
@@ -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"
+22
View File
@@ -0,0 +1,22 @@
---
name: Codespell
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
codespell:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Codespell
uses: codespell-project/actions-codespell@94259cd8be02ad2903ba34a22d9c13de21a74461 # v2.0
@@ -0,0 +1,22 @@
# Dependency Review Action
#
# This Action will scan dependency manifest files that change as part of a Pull Request,
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
# Once installed, if the workflow run is marked as required,
# PRs introducing known-vulnerable packages will be blocked from merging.
#
# Source repository: https://github.com/actions/dependency-review-action
name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: 'Dependency Review'
uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3
+35
View File
@@ -0,0 +1,35 @@
name: Release
on:
release: { types: [published] }
workflow_dispatch:
permissions:
contents: read
jobs:
npmjs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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
permissions:
packages: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
registry-url: "https://npm.pkg.github.com"
- name: scope package name as required by GitHub Packages
run: npm init -y --scope ${{ github.repository_owner }}
- run: npm publish --ignore-scripts
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,59 @@
name: Release to docker hub
on:
release: { types: [published] }
workflow_dispatch:
inputs:
version:
description: 'Version to simulate for deploy'
required: true
permissions:
contents: read
jobs:
dockerhub:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
- name: Login to DockerHub
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub Container registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0
- uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
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
- uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
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 }}-no-faccessat2,${{ secrets.DOCKER_USERNAME }}/bats:latest-no-faccessat2
push: true
build-args: bashver=5.1.4
+72
View File
@@ -0,0 +1,72 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '44 10 * * 6'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9
with:
sarif_file: results.sarif
@@ -0,0 +1 @@
set -u
+333
View File
@@ -0,0 +1,333 @@
name: Tests
# Controls when the action will run.
on: [push, pull_request, workflow_dispatch]
permissions:
contents: read
jobs:
changelog:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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}}
shfmt:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- run: |
curl https://github.com/mvdan/sh/releases/download/v3.5.1/shfmt_v3.5.1_linux_amd64 -o shfmt
chmod a+x shfmt
- run: ./shfmt --diff .
# Ensure we detect when a change disables Bats from reporting failure.
# This would not be detectable by Bats' selftests.
failsafe:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Check failing tests fail suite,
run: "! bin/bats test/fixtures/bats/failing.bats"
shellcheck:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Run shellcheck
run: |
sudo apt-get update -y
sudo apt-get install shellcheck
./shellcheck.sh
linux:
strategy:
matrix:
os: ['ubuntu-20.04', 'ubuntu-22.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'
- 'BATS_PARALLEL_BINARY_NAME=rush'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install rush
run: |
curl -sSfL https://github.com/shenwei356/rush/releases/download/v0.5.0/rush_linux_amd64.tar.gz | sudo tar xzf - -C /usr/local/bin
if: contains(matrix.env_vars, 'rush')
- 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"
unset_variables:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check unset variables
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
BASH_ENV: ${GITHUB_WORKSPACE}/.github/workflows/set_nounset.bash
run: bin/bats test --print-output-on-failure
npm_on_linux:
strategy:
matrix:
os: ['ubuntu-20.04', 'ubuntu-22.04']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- 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 --print-output-on-failure
windows:
runs-on: windows-2019
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check failing tests fail suite,
run: bash -c "! bin/bats test/fixtures/bats/failing.bats"
- 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- 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-11', 'macos-12']
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'
- 'BATS_PARALLEL_BINARY_NAME=rush'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install unbuffer via expect
run: brew install expect
- name: Install rush
run: |
curl -sSfL https://github.com/shenwei356/rush/releases/download/v0.5.0/rush_darwin_amd64.tar.gz | sudo tar xzf - -C /usr/local/bin
if: contains(matrix.env_vars, 'rush')
- name: Check failing tests fail suite,
shell: 'unbuffer bash {0}' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
run: bash -c '! bin/bats test/fixtures/bats/failing.bats'
- 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-11', 'macos-12']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
- 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 installed 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', 'rc']
env_vars:
- ''
# also test running (recursively!) in parallel
- '-e BATS_NUMBER_OF_PARALLEL_JOBS=2'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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
container: alpine:latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Install dependencies
run: apk add bash ncurses util-linux
- name: Run test on bash version ${{ matrix.version }}
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
run:
time ./bin/bats --print-output-on-failure test/
freebsd:
runs-on: ubuntu-latest
strategy:
matrix:
packages:
- flock
- ""
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: vmactions/freebsd-vm@35a5b20a98476a681c7576a344775be7e7f77f06 # v1.0.6
with:
prepare: pkg install -y bash parallel ${{ matrix.packages }}
run: |
time ./bin/bats --print-output-on-failure test/
find_broken_symlinks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
# list symlinks that are broken and force non-zero exit if there are any
- run: "! find . -xtype l | grep ."
rpm:
runs-on: ubuntu-latest
container: almalinux:8
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- run: dnf install -y rpm-build rpmdevtools
- name: Build and install RPM and dependencies
run: |
rpmdev-setuptree
version=$(rpmspec -q --qf '%{version}' contrib/rpm/bats.spec)
tar --transform "s,^,bats-core-${version}/," -cf /github/home/rpmbuild/SOURCES/v${version}.tar.gz ./
rpmbuild -v -bb ./contrib/rpm/bats.spec
ls -al /github/home/rpmbuild/RPMS/noarch/
dnf install -y /github/home/rpmbuild/RPMS/noarch/bats-*.rpm
dnf -y install procps-ng # avoid timeout failure
- name: Run tests
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
run: bats --print-output-on-failure --filter-tags !dep:install_sh test/
dockerfile:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0
- uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
with:
platforms: linux/amd64
tags: bats:test
load: true
- run: docker run -itv "$PWD":/code bats:test --tap --print-output-on-failure test/
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: bats-core/bats-assert
path: bats-assert
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: bats-core/bats-support
path: bats-support
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: bats-core/bats-file
path: bats-file
- run: |
<<EOF cat >test.sh
apk add sudo python3 # install bats-file's dependencies
ln -sf python3 /usr/bin/python # bats-file uses python without version
bats --tap --print-output-on-failure bats-*/test/
EOF
docker run -itv "$PWD":/code --entrypoint bash bats:test test.sh
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
coverage:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- run: |
wget https://github.com/SimonKagstrom/kcov/releases/download/v40/kcov-amd64.tar.gz
tar -xf kcov-amd64.tar.gz
- run: |
./usr/local/bin/kcov --exclude-path=/tmp $PWD/coverage ./bin/bats --filter-tags '!no-kcov' test/
shell: 'script -q -e -c "bash {0}"' # work around tty issues
env:
TERM: linux # fix tput for tty issue work around
- name: Archive code coverage results
uses: actions/upload-artifact@26f96dfa697d77e81fd5907df203aa23a56210a8 # v4.3.0
with:
name: code-coverage-report
path: coverage/*
- name: Print the Total Coverage
id: coverage-percent
shell: bash
env:
minimum_coverage: 86.40
run: |
value=$(jq '.percent_covered' <coverage/bats/coverage.json)
fixed_point() { # <float val>
int=${1%.*}
decimals=${1#*.}
echo $int${decimals::2}
}
echo "Coverage: $value%" | tee "$GITHUB_STEP_SUMMARY"
if (( $(fixed_point $value) < $(fixed_point $expected) )); then
echo " is below required minimum coverage ($minimum_coverage%)." | tee -a "$GITHUB_STEP_SUMMARY"
exit 1
fi
+10
View File
@@ -0,0 +1,10 @@
/docker-compose.override.yml
/docs/build
# npm
/bats-*.tgz
# we don't have any deps; un-ignore if that changes
/package-lock.json
test/.bats/run-logs/
# scratch file that should never be committed
/test.bats
@@ -0,0 +1,14 @@
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.16.3
hooks:
- id: gitleaks
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: 3.0.0
hooks:
- id: shellcheck
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
+13
View File
@@ -0,0 +1,13 @@
version: 2
sphinx:
configuration: docs/source/conf.py
build:
os: ubuntu-22.04
tools:
python: "3.12"
python:
install:
- requirements: docs/source/requirements.txt
+4
View File
@@ -0,0 +1,4 @@
Andrew Martin (https://control-plane.io/)
Bianca Tamayo <hi@biancatamayo.me> (https://biancatamayo.me/)
Jason Karns <jason.karns@gmail.com> (http://jasonkarns.com/)
Mike Bland <mbland@acm.org> (https://mike-bland.com/)
+43
View File
@@ -0,0 +1,43 @@
ARG bashver=latest
FROM bash:${bashver}
ARG TINI_VERSION=v0.19.0
ARG TARGETPLATFORM
ARG LIBS_VER_SUPPORT=0.3.0
ARG LIBS_VER_FILE=0.4.0
ARG LIBS_VER_ASSERT=2.1.0
ARG LIBS_VER_DETIK=1.3.0
ARG UID=1001
ARG GID=115
# https://github.com/opencontainers/image-spec/blob/main/annotations.md
LABEL maintainer="Bats-core Team"
LABEL org.opencontainers.image.authors="Bats-core Team"
LABEL org.opencontainers.image.title="Bats"
LABEL org.opencontainers.image.description="Bash Automated Testing System"
LABEL org.opencontainers.image.url="https://hub.docker.com/r/bats/bats"
LABEL org.opencontainers.image.source="https://github.com/bats-core/bats-core"
LABEL org.opencontainers.image.base.name="docker.io/bash"
COPY ./docker /tmp/docker
# default to amd64 when not running in buildx environment that provides target platform
RUN /tmp/docker/install_tini.sh "${TARGETPLATFORM-linux/amd64}"
# Install bats libs
RUN /tmp/docker/install_libs.sh support ${LIBS_VER_SUPPORT}
RUN /tmp/docker/install_libs.sh file ${LIBS_VER_FILE}
RUN /tmp/docker/install_libs.sh assert ${LIBS_VER_ASSERT}
RUN /tmp/docker/install_libs.sh detik ${LIBS_VER_DETIK}
# Install parallel and accept the citation notice (we aren't using this in a
# context where it make sense to cite GNU Parallel).
RUN apk add --no-cache parallel ncurses && \
mkdir -p ~/.parallel && touch ~/.parallel/will-cite \
&& mkdir /code
RUN ln -s /opt/bats/bin/bats /usr/local/bin/bats
COPY . /opt/bats/
WORKDIR /code/
ENTRYPOINT ["/tini", "--", "bash", "bats"]
+53
View File
@@ -0,0 +1,53 @@
Copyright (c) 2017 bats-core contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
* [bats-core] is a continuation of [bats]. Copyright for portions of the
bats-core project are held by Sam Stephenson, 2014 as part of the project
[bats], licensed under MIT:
Copyright (c) 2014 Sam Stephenson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For details, please see the [version control history][commits].
[bats-core]: https://github.com/bats-core/bats-core
[bats]:https://github.com/sstephenson/bats
[commits]:https://github.com/bats-core/bats-core/commits/master
+135
View File
@@ -0,0 +1,135 @@
[![Latest release](https://img.shields.io/github/release/bats-core/bats-core.svg)](https://github.com/bats-core/bats-core/releases/latest)
[![npm package](https://img.shields.io/npm/v/bats.svg)](https://www.npmjs.com/package/bats)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/bats-core/bats-core/blob/master/LICENSE.md)
[![Continuous integration status](https://github.com/bats-core/bats-core/workflows/Tests/badge.svg)](https://github.com/bats-core/bats-core/actions?query=workflow%3ATests)
[![Read the docs status](https://readthedocs.org/projects/bats-core/badge/)](https://bats-core.readthedocs.io)
[![Join the chat in bats-core/bats-core on gitter](https://badges.gitter.im/bats-core/bats-core.svg)][gitter]
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/source/assets/dark_mode_cube.svg">
<img alt="" src="docs/source/assets/light_mode_cube.svg">
</picture>
</div>
# Bats-core: Bash Automated Testing System
Bats is a [TAP](https://testanything.org/)-compliant testing framework for Bash
3.2 or above. It provides a simple way to verify that the UNIX programs you
write behave as expected.
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.
```bash
#!/usr/bin/env bats
@test "addition using bc" {
result="$(echo 2+2 | bc)"
[ "$result" -eq 4 ]
}
@test "addition using dc" {
result="$(echo 2 2+p | dc)"
[ "$result" -eq 4 ]
}
```
Bats is most useful when testing software written in Bash, but you can use it to
test any UNIX program.
Test cases consist of standard shell commands. Bats makes use of Bash's
`errexit` (`set -e`) option when running test cases. If every command in the
test case exits with a `0` status code (success), the test passes. In this way,
each line is an assertion of truth.
## Table of contents
**NOTE** The documentation has moved to <https://bats-core.readthedocs.io>
<!-- toc -->
- [Testing](#testing)
- [Support](#support)
- [Contributing](#contributing)
- [Contact](#contact)
- [Version history](#version-history)
- [Background](#background)
* [What's the plan and why?](#whats-the-plan-and-why)
* [Why was this fork created?](#why-was-this-fork-created)
- [Copyright](#copyright)
<!-- tocstop -->
## Testing
```sh
bin/bats --tap test
```
See also the [CI](./.github/workflows/tests.yml) settings for the current test environment and
scripts.
## Support
The Bats source code repository is [hosted on
GitHub](https://github.com/bats-core/bats-core). There you can file bugs on the
issue tracker or submit tested pull requests for review.
For real-world examples from open-source projects using Bats, see [Projects
Using Bats](https://github.com/bats-core/bats-core/wiki/Projects-Using-Bats) on
the wiki.
To learn how to set up your editor for Bats syntax highlighting, see [Syntax
Highlighting](https://github.com/bats-core/bats-core/wiki/Syntax-Highlighting)
on the wiki.
## Contributing
For now see the [`docs`](docs) folder for project guides, work with us on the wiki
or look at the other communication channels.
## Contact
- You can find and chat with us on our [Gitter].
## Version history
See `docs/CHANGELOG.md`.
## Background
<!-- markdownlint-disable MD026 -->
### Why was this fork created?
<!-- markdownlint-enable MD026 -->
There was an initial [call for maintainers][call-maintain] for the original Bats repository, but write access to it could not be obtained. With development activity stalled, this fork allowed ongoing maintenance and forward progress for Bats.
**Tuesday, September 19, 2017:** This was forked from [Bats][bats-orig] at
commit [0360811][]. It was created via `git clone --bare` and `git push
--mirror`.
As of **Thursday, April 29, 2021:** the original [Bats][bats-orig] has been
archived by the owner and is now read-only.
This [bats-core](https://github.com/bats-core/bats-core) repo is now the community-maintained Bats project.
[call-maintain]: https://github.com/sstephenson/bats/issues/150
[bats-orig]: https://github.com/sstephenson/bats
[0360811]: https://github.com/sstephenson/bats/commit/03608115df2071fff4eaaff1605768c275e5f81f
## Copyright
The Bats Logo was created by [Vukory](https://www.artstation.com/vukory) ([Github](https://github.com/vukory)) and sponsored by [SethFalco](https://github.com/SethFalco). If you want to use our logo, have a look at our [guidelines](./docs/source/assets/README.md#Usage-Guide-for-Third-Parties).
© 2017-2024 bats-core organization
© 2011-2016 Sam Stephenson
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
+9
View File
@@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
Only the greatest published version (according to semver) will be supported.
## Reporting a Vulnerability
Use GitHub's builtin reporting mechanism under https://github.com/bats-core/bats-core/security/advisories/new
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail
# Note: We first need to use POSIX's `[ ... ]' instead of Bash's `[[ ... ]]'
# because this is the check for Bash, where the shell may not be Bash. Once we
# confirm that we are in Bash, we can use [[ ... ]] and (( ... )). Note that
# these [[ ... ]] and (( ... )) do not cause syntax errors in POSIX shells,
# though they can be parsed differently.
if [ -z "${BASH_VERSION-}" ] ||
[[ -z "${BASH_VERSINFO-}" ]] ||
((BASH_VERSINFO[0] < 3 || (BASH_VERSINFO[0] == 3 && BASH_VERSINFO[1] < 2)))
then
printf 'bats: this program needs to be run by Bash >= 3.2\n' >&2
exit 1
fi
if command -v greadlink >/dev/null; then
bats_readlinkf() {
greadlink -f "$1"
}
else
bats_readlinkf() {
readlink -f "$1"
}
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
}
}
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_SAVED_PATH=$PATH
BATS_BASE_LIBDIR=lib # this will be patched with the true value in install.sh
export BATS_ROOT=${BATS_PATH%/*/*}
export -f bats_readlinkf
exec env BATS_ROOT="$BATS_ROOT" BATS_LIBDIR="${BATS_BASE_LIBDIR:-lib}" "$BATS_ROOT/libexec/bats-core/bats" "$@"
+178
View File
@@ -0,0 +1,178 @@
#!/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
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,66 @@
%global provider github.com
%global project bats-core
%global repo bats-core
Name: bats
Version: 1.11.0
Release: 1%{?dist}
Summary: Bash Automated Testing System
Group: Development/Libraries
License: MIT
URL: https://%{provider}/%{project}/%{repo}
Source0: https://%{provider}/%{project}/%{repo}/archive/v%{version}.tar.gz
BuildArch: noarch
Requires: bash
%description
Bats is a TAP-compliant testing framework for Bash.
It provides a simple way to verify that the UNIX programs you write behave as expected.
Bats is most useful when testing software written in Bash, but you can use it to test any UNIX program.
%prep
%setup -q -n %{repo}-%{version}
%install
mkdir -p ${RPM_BUILD_ROOT}%{_prefix} ${RPM_BUILD_ROOT}%{_libexecdir} ${RPM_BUILD_ROOT}%{_mandir}
./install.sh ${RPM_BUILD_ROOT}%{_prefix}
%clean
rm -rf $RPM_BUILD_ROOT
%check
%files
%doc README.md LICENSE.md
%{_bindir}/%{name}
%{_libexecdir}/%{repo}
%{_mandir}/man1/%{name}.1.gz
%{_mandir}/man7/%{name}.7.gz
/usr/lib/%{repo}/common.bash
/usr/lib/%{repo}/formatter.bash
/usr/lib/%{repo}/preprocessing.bash
/usr/lib/%{repo}/semaphore.bash
/usr/lib/%{repo}/test_functions.bash
/usr/lib/%{repo}/tracing.bash
/usr/lib/%{repo}/validator.bash
/usr/lib/%{repo}/warnings.bash
%changelog
* Wed Sep 07 2022 Marcel Hecko <marcel@blava.net> - 1.2.0-1
- Fix and test RPM build on Rocky Linux release 8.6
* Sun Jul 08 2018 mbland <mbland@acm.org> - 1.1.0-1
- Increase version to match upstream release
* Mon Jun 18 2018 pixdrift <support@pixeldrift.net> - 1.0.2-1
- Increase version to match upstream release
- Relocate libraries to bats-core subdirectory
* Sat Jun 09 2018 pixdrift <support@pixeldrift.net> - 1.0.1-1
- Increase version to match upstream release
* Fri Jun 08 2018 pixdrift <support@pixeldrift.net> - 1.0.0-1
- Initial package build of forked (bats-core) github project
+358
View File
@@ -0,0 +1,358 @@
#!/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:
+13
View File
@@ -0,0 +1,13 @@
version: '3.6'
services:
bats:
build:
context: "."
dockerfile: "Dockerfile"
networks:
- "default"
user: "root"
volumes:
- "./:/opt/bats"
networks:
default:
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
LIBNAME="${1:-support}"
LIVERSION="${2:-0.3.0}"
BASEURL='https://github.com/bats-core'
DESTDIR="${BATS_LIBS_DEST_DIR:-/usr/lib/bats}"
TMPDIR=$(mktemp -d -t bats-libs-XXXXXX)
USAGE="Please provide the bats libe name and version \nFor example: install_libs.sh support 2.0.0\n"
trap 'test -d "${TMPDIR}" && rm -fr "${TMPDIR}"' EXIT ERR SIGINT SIGTERM
[[ $# -ne 2 ]] && { _log FATAL "$USAGE"; exit 1; }
_log() {
printf "$(date "+%Y-%m-%d %H:%M:%S") - %s - %s\n" "${1}" "${2}"
}
create_temp_dirs() {
mkdir -p "${TMPDIR}/${1}"
if [[ ${LIBNAME} != "detik" ]]; then
mkdir -p "${DESTDIR}/bats-${1}/src"
else
_log INFO "Skipping src 'cause Detik does not need it"
fi
}
download_extract_source() {
wget -qO- ${BASEURL}/bats-"${1}"/archive/refs/tags/v"${2}".tar.gz | tar xz -C "${TMPDIR}/${1}" --strip-components 1
}
install_files() {
if [[ ${LIBNAME} != "detik" ]]; then
install -Dm755 "${TMPDIR}/${1}/load.bash" "${DESTDIR}/bats-${1}/load.bash"
for fn in "${TMPDIR}/${1}/src/"*.bash; do install -Dm755 "$fn" "${DESTDIR}/bats-${1}/src/$(basename "$fn")"; done
else
for fn in "${TMPDIR}/${1}/lib/"*.bash; do install -Dm755 "$fn" "${DESTDIR}/bats-${1}/$(basename "$fn")"; done
fi
}
_log INFO "Starting to install ${LIBNAME} ver ${LIVERSION}"
_log INFO "Creating directories"
create_temp_dirs "${LIBNAME}"
_log INFO "Downloading"
download_extract_source "${LIBNAME}" "${LIVERSION}"
_log INFO "Installation"
install_files "${LIBNAME}"
_log INFO "Done, cleaning.."
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -e
case ${1#linux/} in
386)
TINI_PLATFORM=i386
;;
arm/v7)
TINI_PLATFORM=armhf
;;
arm/v6)
TINI_PLATFORM=armel
;;
*)
TINI_PLATFORM=${1#linux/}
;;
esac
echo "Installing tini for $TINI_PLATFORM"
wget "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-${TINI_PLATFORM}" -O /tini
wget "https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-${TINI_PLATFORM}.asc" -O /tini.asc
chmod +x /tini
apk add gnupg
gpg --import </tmp/docker/tini.pubkey.gpg
gpg --batch --verify /tini.asc /tini
apk del gnupg
+107
View File
@@ -0,0 +1,107 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFANDtsBEACpb69Ul0Ko7D4XxRIvPGnDMuGdocb8PxR+EGbnHe0uS2tCbsfj
TOoWWUrjufrWYxGlKNqOxbEhzFA2wSQ6VD6xROPQT5dAdKaGnSCiaUg7XTzcb9u3
a5Qbx99EDZWaYDNMnLZnIElDX+YmkkEyrrmjiML63m+1P88Bz7ag18hLkqpCiIVM
TMRfQluBJVvndX7Stzm35utugN+xeTQryjLx74CO6TUWyC7hAjvQhR5IdAk4H0oT
RsOKZ9OQmpO0CJ1XXpKkDdDc60WVrLp1jwq2M7fx/Nz+z13nTHa3fDw8j10+1k0+
c2HafM+GLR5CHlXVMqveWJrimII1ZILxRj/86fFCEC8ZhVW1ym4j+mqEENrzP4I7
L3OnyKLxNKIY9CFDhfzLhNAuNeuIp6KgynzuyxWnJO4q7m/B0zcRIBcjXPrpblIx
QlT3qQ/vFdcylDDSdbgtjD+9URG6bFR9PVlRTllBDPGQEK8vjV44pxLCenm/TzdB
Y4RlEePf+3y7wVrkjg+l4rIDH57Vl188RODuWVGeLZ3IYWqvRUnYxHmta27UH6zY
7FNN5p7H2VqP6v9GFhiHOCTKdUbQhOoPLmUTyBas0WsC8sXdwpTy3mJthzfUwgVN
2SIXPnndz7RcHwZtW1x9ZtVMDr6ll99kT63+sdZJHmUdlnDr+EGEd/L61QARAQAB
tCBUaG9tYXMgT3JvemNvIDx0aG9tYXNAb3JvemNvLmZyPokCOAQTAQIAIgUCUA0O
2wIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQmoQVnXABpOViYA//dDQt
4f7NGbZNnQ0DgOGgBQjAabeaup6HX1UBVMBmU3OEKkUQoA62Aix8kOz19HJcuOl0
Y3koZ7pOrf/39s+tlZKvVuX7F6RFiJpx1+8f+f4IfQEPJRiurwkEp2zwTQfEOlo4
Jzv57pLUtmyJVnxMDi2vFeUue+j9BePp20Z1ZEaMBJDjmMZao3iVCVvSHZnS28vk
D5+y8VUbyhH4dyaDtu8sb06vCu2uD7ZsMfDTtdkDWRqOLs8FdamZLf8P8AD6AX2c
PhhASMWF7Ty2kRvhzHriuplzDUBqod38iLxoC2l5xoDjNMZsY0q9dOXBS5RsKws2
qxrJLa/F672lpxlMA9Xnm0LMCNMcnXreldUhl18zVNofIDGex5yP20djOxl7WsQ6
0VBAzbbiGLaKyfko5CuSmulzTJNOUNxHTiXjSi6Qz5+5lh0wYnHFulhiYE16EX25
/rhvDzN5BgsT3Kdyk/XT/klIB5k4eLXK8PgMsRzp+FDVUL7HKM1HpHMY65gdSR8y
ZuXzKXVC43MFPYJMrjdxoEJXJjnzhf+KO6jkyrna0OQoM1YTH6/5X9XoXzQz8TDe
7pJt4S27PZCv1NhuqNv7nPhUANr7nFMRzrbx2DNHyxUrxPvuyNmYx/KkoAzw3yaT
frbFXU6ccwsXWW6zdcyLEYI2ZqGDfLS7zkjmRsa5AQ0EUzNVzQEIAKxWZf55R676
M3IURgx8Ovt8+F3M4Tj+ifePcpY5JHSj9sGkJRugW9Nye5WehuJjFYOEhKrljCTP
/mjnE7iQqVTyYC1Ar+cTtNQpadWvQR2MW/UzimdZjBtZxdtGFCWR203jtnG+LGRs
R7HyR6A8OBKLl0heTSHx92f+dJCGESZJeVgY49xNOlG4ILl8NseYhaD36QQZReLn
Cazy2aOep2H/jz7vnzye38QMkdHcjaw/WfQHWDKzM5Wlf+5TLF/2VTPlKcmtn0QF
Rw/vw6kjwsv9eCQ3ThIn/FV/ycdUkhOfz9Su2aIbwYC4C6Xi82RxJKbnqUGOExeC
PG2luuWnyQcAEQEAAYkDRAQYAQIADwUCUzNVzQIbAgUJA8JnAAEpCRCahBWdcAGk
5cBdIAQZAQIABgUCUzNVzQAKCRAqiuDav/LlpshzCACnQxndwXLZYWwiXOiYvmUI
U9mPKynUW2vfTwaIpFBIYnqLAjVsPspwfx7zYCoMtQ+UK9d+Jhyts8vCaPwJHPqC
CrBbP/y9K2zRdcDA1DYW/cuubwcstypmO6Eh6iC6xRxa5IHZogK6U+Trqf/EGfI/
Y/sc3KBVYSdLkngASdVr5TQltVH9LsxDUo7Ba382Ci0g/VmKkUht/aFRkEfvMrrj
ONEGbyCwk2hZuwK1aE6Yweq3tbzrByOF4wqIIe6e1pwPz7lchoGYm7XJ7uckpbGi
lZojvu9ruxbGl8tirY/Bj0UZYjASDNCcXXRMvri2FZhDyWMMoeRLj7Mw8xpeWjr7
NhoP/2AA16HHwRjfPQo5fDXe7eGXIebwWhbr1nAYQtL2PHFeHG/imEVozcftENNt
tVwq/JcfjyiIcKA0niGZmroQCO0HRzRs7T85ITWfWcn/Hqaok6gmLh6QJ6daC7E9
Mse07zYM52S2/cS4osfzZj4JEC2n82VuqK9fqzR58GCkPn9rshwljEkMgEfYfTvA
k/tm8jACpSdH3bCpSP2rL6FBtm+RuL8zbAucg8BCaVPoOhtp3326MWCnxH7QdiHl
1dT4Dgmq4UluXOvyMZY7HI1cq4RXW5eXG5mG0ATQh1JIhIJUaEPzdaEtP5LOVqKE
4220MVCWWak/27O9mAFm+lBA5d8O8MWW2ERqpwyYMWg/DVvQP/pB/D6wO4Dp7CMD
MLl1LszHab9oRo8vW2cWNVS6mQ/56jgS10nRe8o/VQROfd1De6sk5k9ypk2y2iC7
pp7sime3MsnzjR0fKcxjCDAX1Sopi9QzkypM66WHO46Uamn0GeL7OhcwjcoURzJc
y5ue1jeeKmmR7ATexlgE87rA4nuMZak1uVz0s6Fsb1ekYIKFEFqwEpqNnyKgBrvt
GA8yn9Zp4wF+6luaKMqwptx0q1PmMZacN9E62XdOyGBaVXPq599F3R6049zo04oW
WMW7s7V10Vn/pqRkB4bhhUzatiUHWVuamhpR8SligZ4kWPHZuQINBFANDtsBEADJ
MvBhyWEBoLgi9nO9hgbbLxiLjKnotKzRpu5m79rhpmtqEN2k0APmoRdUUrE2Y8sd
Z551jT0TE2O1j70qLV9c5puK6qyV3BZb9OgQ8wqyCFFjmIdPQAEgnD3K92SOh8Mu
lqJW++EsxSqDBt2iVbLyzQuklbIJOg4nVK8ZgswIyewiHVeh3xgcGxJtVb3QjKSz
xhj0GYM6X7mo5rjzxEFVyiJXd8ZH79y4FHUr+tJQEzJbvWAGJClPx9czh9Drw2r+
Tgq+2v/EkAoyXdv3z7uChh/9s33oQtssJ5TVfAbC06QsPp55RJCkGbTrBQgRB8Xa
idPJhG9sIbhVgrRejn5kmK5L/8ACngbSU4zO1NTaquLZGZMzI7Jbx/QeG2DbVqjV
6BLA3X+WieM9IUa1vi/CXiKedzlOMv7+CX9N76y4Wls4zsTwXTIzmPCDoysG+75D
AoFZW1XJlMeEOGPhBDlR6X47qdLGzOI14NJZELni14+Iih6VvxhvUWmJfTnT+zE+
PWb03C0EHsF8uTYqBPag9LYw2cDqvP31bUSiV+Z5YR+4sDcfiVT40rXECd1752Ia
yANeL7uG/gBvl+6LHzb3VhI5ILkknVummVY0ry8YpAA0r1GfBE/mFLE3jwTqYkMx
nr/SDa3HIql7qBh9k9AhTMId8tXX6p3lO6jd6TeewQARAQABiQIfBBgBAgAJBQJQ
DQ7bAhsMAAoJEJqEFZ1wAaTlTeIP/R6/0EAqKyISxnx/6+VCy2j5mj4v8b+KauTT
deXJhP37i2EMsosGqUD1LMo8Wv9Az7XOSA/2lW4v8UHQolGwssLFm9L4DDTZBH8M
gOFzB++wHNxgIoD2u9vC2eehhMfVlCcH+YWtzTSs94+5gI9xcy8rejkO+AHhXYDR
0nr5MM076CWEjWtORgZdHbXWilmr/SdKnIdvkXDXvNcd7tC6izIfnDxN7/4beWOF
nv/1s0JLzIFIutFtqqYDC8Y/4JxoMcALhmop+FyYk+RUHT5uGeDauJunjwdBS3VY
5NrMcw4LxapV0OQExxu8RAMcYXx6FZmoBMlFI+J6R5ZMS6y1TKLKIpq46CsOSz2V
X1SAactdOpXuNOXLe2cv0mYswWGAURebcmcmN49n9JEn1IN8hhawFgFuYdUWjpYx
K2K6NZ0vFgRHkRnei9xrf2mW7ob1vKwzeBYGvZj/xIEu/Fv3kizS6t1IeMJKUKlp
semAObW+sEO0jLOelL+ZfUO+fImL+0fFxQyzkfNKk5dpxztPlNmv3DY6KTddc1L1
uOGCznAmsg8Jp0v0OmCB2Xl15WPwwnYi3CkLGbEcK9stFmu2pZuEeR2DuVGlz3Nr
Bu1W/34cXeyudUTxveehuvfkjYBMXVfEM35BHEUqgCtA49b5ZM8SpYgqU3omVsNz
7RjMAIe3uQINBFaoGQEBEACypQbnC5fMhpCft6augXnnVzmEh0Se2wBxUum3DMFl
U48DJYNlEsKQYsgzEvaayTI0gA1ZyeDg3E4Fnk6ysQmzW9BJ/3Q2pa0GKIkvXOgL
nwvSXSnTTqK3zCDuJI0Nj4u9gI8bX7d4PHqQyyFzPWjiIbg9tWHbhT8wwCaay1iG
qCZsTa8Iwyve0WaV+7YtRJQeXEfY9Z6oEGqjis7QJNef1MKy1gS1Kq+4sqvdwgo3
f3AFSNR14gagYv2myA30ehf4EzzIi8dh9DATc4T86CBuK3TszILLSUwFnrUVT00j
3sr2WiqWrxIq/paky8zNEV3Q0vaf2kheLSZRSZsoVH1AfCsVHj4OoWH3BOAzdr5w
sTPsV2HVR0O9K98NoA0f45eqfMFBiGVsFuJamBJatPsTVVXZ6fR9aDDKySSB540W
egHoABwai+s4FhUtjjZarLPJo47/gFmxF7HHUyNIYYKj5j99h00zcM7XOtKeyHzH
vfZvmu150u2DccY9XX5xdzDsWuBeQJQ7VdyVVTPoZGIBZ1Mw3EIfFyAzYvglTCKo
MMHH6912PnsMLcbcmF/7pJ4rrehp8bkfS7R3BBNMglLWtoZIYBSWoK8Nl0OyBzQ5
lsPvRdUHuMe6rxwA11gRnx4yLd91bWRw/xIBwxz6Jq/lk0ySgdum/hIcOmrNdg73
swARAQABiQQ+BBgBAgAJBQJWqBkBAhsCAikJEJqEFZ1wAaTlwV0gBBkBAgAGBQJW
qBkBAAoJEAtYjf8FJ6m3uPQP/jonQ5QrOEN+B7ddYmotAwj6wL9whs7rcgb+TJfI
rJskyBQG+kHymWEBiODo/AcqkKglGzTNcaDG153l38/IQ/Lo9mxZmkNHtLD6Srkb
GjNrBIksRSR+hWbr8UjA/WuqAmDQvFWebmyF4p6deEAf3Rsv2Ml/a0lvC6TyWRds
dMyWlDPtlKYn9q2qHjCVX4e2m4uB38Vq3fo8+Ypags5KRC9KyGgZFEGy3F37p+4U
/rDT+t/6oKuo5/8RWAixeWNWIli7DQ9DbA4w9qdwL4KGwcnOCcCClT9KqKA3MOds
r+dcAF+LkNrG3yvnp+eZ7LHF6PVJAkXnSLOzuEZzXp90pA+1Sw7QWGWpyV/v6CSP
sxZInLPjBPJmdVhjMrma2s9SGKs2k4eivDeK4Gr6oF3GRK3VQzsRPDYk9vr8wSbA
BR4Qh6Fy/WiME1SyEdcs2UWc+eGZ0ya7QEkQqz7Cvjp6VezUtXWFgy28pVkgEORE
sqogypjbSnybeis6Rzvxf0g681fWjWSTcMBGXKTvgW7XnLqG9XMWdx0n6pa6s5yL
nkoM5zYcjL+V6wDVQwnF9uoallcO11PEjsEMeK3gz0Y81EmLuHN6411IK1cWfxAI
HunwOoPe7nzGOH2EX1+IkCv4PPJqMloEcIIPlwP0ILxTaPuy17wed0pH3dvRZglv
J7qQU/AP/3Nhh5AjUtMRI+mDgJpXpYuMy7tNdgKAvgGIL1bP+1LnLtP2+7uy1Pok
3938/eAJwAT9sOhpG3qRPs15zj4tc20/Jsypbk4032EhRFWGL5TBtYf9LVl9doFY
hGHUgAzREWcw8l7h0/p5VNPm9K0kElCw2fN3z7xwreSPa1WGVsqMdM3nc/B44ocZ
HFqrd9kg6N4al6IxkbL7DYAUjfhjkhOQXZXU+MLPVA9lflF4694lbrr7F4GwnzfF
8/SOw9LHVh9K3agno6jHB64P/Vqtka4a5+9AKcEkojWEHDoLU8BMUXDZZu9OkFSJ
tzqByAzti0dsLE+8GGfas47b3/rjf6qyKvh2+iPw+I6zHH4k7LKpIiK+MkQ2+ZfM
zB3jQ9NPNAc63AL/YbnF5XDSFxGBRkcFZo50rZJV5WHA3p+UMISsjoNizE7agLqI
2JPnKBzB3aQFFpI3QiivWhKfsO02Vtzl+1TjqReWu4qS1uwJbSsLectAy+4K2WFn
RNZ1m/r9/enzEhyKo8zOmUBUzVLhRR/GG7S1MmxB8FSOZh7aHg1xbTYVvq62AW6a
0PKvEpn1g0wvPEoKQ+41/dc/ieWgnkZiKJJFxDshKGmxfDznWnZqnOESLz/3hbGy
U41nk0UPzHlkUZwsLo9UBdr9fUVYvlYUylBp2dV0jEc5qL62gypA
=4tCW
-----END PGP PUBLIC KEY BLOCK-----
@@ -0,0 +1,3 @@
{
"MD024": { "siblings_only": true }
}
+574
View File
@@ -0,0 +1,574 @@
# 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.11.0] - 2024-03-24
### Added
* hardened CI scripts by using hashes instead of versions for actions and restricting permissions (#754, #770)
* add security.md (#762)
* add codespell CI checks (#720)
* dynamic test registration via `bats_test_function` (#349)
* add check that Bats is executed with Bash >= 3.2 (#873)
### Fixed
* `install.sh` now works for deviating `lib/` dirs (like `lib32`,`lib64`) (#487)
* catch unset `BATS_TEST_SOURCE` in `lib/bats-core/tracing.bash` so
`set -u`/`set -o nounset` works as expected (#827)
* fix `--gather-test-outputs-in` fails on tests with multiple `/` (#789)
* install does not create unused `/usr/share/bats` anymore (#857)
* ensure IFS is unchanged in `{setup,teardown}{_suite,_file,}`, `@test` and free code (#879)
* junit formatter: remove ANSI Codes to avoid invalid XML character (#886)
### Changed
* update Docker image with the latest `bats-file` version 0.4.0 (#780)
* update Docker image with the latest `bats-detik` version 1.3.0 (#876)
#### Documentation
* clarify docker usage (#741)
* update Arch Linux package URL in installation.rst (#821)
* rename bash-bats to bats for Arch Linux in installation.rst (#836)
* fix FAQ entry about setup-/teardown_suite, as they are available now (#861)
* added logo (#881)
## [1.10.0] - 2023-07-15
### Added
* add `${BATS_TEST_TAGS[@]}` for querying the tags during a test (#705)
* print tags on failing tests (#705)
* test for negative arguments to `--jobs` (#693)
* add tests for `--formatter cat` (#710)
* test coverage in CI (#718)
* Support for [rush](https://github.com/shenwei356/rush) as alternative to GNU parallel (#729)
* add `bats_pipe` helper function for `run` that executes `\|` as pipes (#663)
* publish docker images to ghcr.io (additionally to Dockerhub) (#740)
### Documentation
* clarify use cases of `--formatter cat` (#710)
### Fixed
* fix `run` with options overwriting the value of `i` (#726, #727)
* fix `${BATS_TEST_NAMES[@]}` containing only `--tags` instead of test name since Bats v1.8.0 (#705)
* fix `run --keep-empty-lines` counting trailing `\n` as (empty) new line (#711)
* fix short flag unpacker creating bogus command lines with valued flags (#732)
* fix formatter becoming confused with retries (#734)
* fix `--gather-test-outputs-in` fails on tests with `/` (#735)
* fix overriding `date` breaks `--timing` (#736)
#### Documentation
* typos, minor edits (#704)
* simplified contributing.md (#718)
## [1.9.0] - 2023-02-12
### Added
* add installation instructions for Debian, Fedora, Gentoo, and OpenSUSE (#659)
* add `--line-reference-format` to switch file/line references in stack traces (#665)
* `comma_line` (default): `file.bats, line 1`
* `colon`: `file.bats:1`
* `uri`: `file:///path/to/file.bats:1`
* `custom`: define your own formatter in `bats_format_file_line_reference_custom`
* add `bats:focus` tag to run only focused tests (#679)
* add bats-support, bats-assert, bats-file and bats-detik to Dockerfile (#674)
### Documentation
* add `--help` text and `man` page content for `--filter-tags` (#679)
### Fixed
* explicitly check for GNU parallel (#691)
* wait for report-formatter to finish before ending `bats`' execution,
to fix empty files with `--report-fomatter junit` under Docker (#692)
#### Documentation
* improved clarity of section about output in free code (#671)
* fixed typos (#673)
* clarify use cases of `run` (#366)
## [1.8.2] - 2022-10-19
### Fixed
* fix non zero return code on successful retried tests (#670)
* fix `skip` in `setup_file` failing test suite (#687)
## [1.8.1] - 2022-10-19
### Fixed
* `shfmt` all files and enforce via CI (#651)
* avoid kernel warning flood/hang with CTRL+C on Bash 5.2 RC (#656)
* Fix infinite wait with (invalid) `-j<n>` (without space) (#657)
## [1.8.0] - 2022-09-15
### Added
* using external formatters via `--formatter <absolute path>` (also works for
`--report-formatter`) (#602)
* running only tests that failed in the last run via `--filter-status failed` (#483)
* variable `BATS_TEST_RETRIES` that specifies how often a test should be
reattempted before it is considered failed (#618)
* Docker tags `latest-no-faccessat2` and `<bats-version\>-no-faccessat2` for
avoiding `bash: bats: No such file or directory` on `docker<20.10` (or
`runc<v1.0.0-rc93`) (#622)
* `BATS_TEST_TIMEOUT` variable to force a timeout on test (including `setup()`) (#491)
* also print (nonempty) `$stderr` (from `run --separate-stderr`) with
`--print-output-on-failure` (#631)
* `# bats test_tags=<tag list>`/`# bats file_tags=<tag list>` and
`--filter-tags <tag list>` for tagging tests for execution filters (#642)
* warning BW03: inform about `setup_suite` in wrong file (`.bats` instead of `setup_suite.bash`) (#652)
#### Documentation
* update gotcha about negated statements: Recommend using `run !` on Bats
versions >=1.5.0 (#593)
* add documentation for `bats_require_minimum_version` (#595)
* improve documentation about `setup_suite` (#652)
### Fixed
* added missing shebang (#597)
* remaining instances of `run -<N>` being incorrectly documented as `run =<N>` (#599)
* allow `--gather-test-outputs-in <directory>` to work with existing, empty
directories (#603)
* also add `--clean-and-gather-test-outputs-in <directory>` for improved UX
* double slashes in paths derived from TMPDIR on MacOS (#607)
* fix `load` in `teardown` marking failed tests as not run (#612)
* fix unset variable errors (with set -u) and add regression test (#621)
* `teardown_file` errors don't swallow `setup_file` errors anymore, the behavior
is more like `teardown`'s now (only `return`/last command can trigger `teardown`
errors) (#623)
* upgraded from deprecated CI envs for MacOS (10 -> 11,12) and Ubuntu
(18.04 -> 22.04) (#630)
* add `/usr/lib/bats` as default value for `BATS_LIB_PATH` (#628)
* fix unset variable in `bats-formatter-junit` when `setup_file` fails (#632)
* unify error behavior of `teardown`/`teardown_file`/`teardown_suite` functions:
only fail via return code, not via ERREXIT (#633)
* fix unbound variable errors with `set -u` on `setup_suite` failures (#643)
* fix `load` not being available in `setup_suite` (#644)
* fix RPM spec, add regression test (#648)
* fix handling of `IFS` by `run` (#650)
* only print `setup_suite`'s stderr on errors (#649)
#### Documentation
* fix typos, spelling and links (#596, #604, #619, #627)
* fix redirection order of an example in the tutorial (#617)
## [1.7.0] - 2022-05-14
### Added
* Pretty formatter print filename when entering file (#561)
* BATS_TEST_NAME_PREFIX allows prefixing test names on stdout and in reports (#561)
* setup_suite and teardown_suite (#571, #585)
* out-of-band warning infrastructure, with following warnings:
* BW01: run command not found (exit code 127) (#586)
* BW02: run uses flags without proper `bats_require_minimum_version` guard (#587)
* `bats_require_minimum_version` to guard code that would not run on older
versions (#587)
#### Documentation
* document `$BATS_VERSION` (#557)
* document new warning infrastructure (#589, #587, #586)
### Fixed
* unbound variable errors in formatters when using `SHELLOPTS=nounset` (`-u`) (#558)
* don't require `flock` *and* `shlock` for parallel mode test (#554)
* print name of failing test when using TAP13 with timing information (#559, #555)
* removed broken symlink, added regression test (#560)
* don't show empty lines as `#` with pretty formatter (#561)
* prevent `teardown`, `teardown_file`, and `teardown_suite` from overriding bats'
exit code by setting `$status` (e.g. via calling `run`) (#581, #575)
* **CRITICAL**: this can return exit code 0 despite failed tests, thus preventing
your CI from reporting test failures! The regression happened in version 1.6.0.
* `run --keep-empty-lines` now reports 0 lines on empty `$output` (#583)
#### Documentation
* remove 2018 in title, update copyright dates in README.md (#567)
* fix broken links (#568)
* corrected invalid documentation of `run -N` (had `=N` instead) (#579)
* **CRITICAL**: using the incorrect form can lead to silent errors. See
[issue #578](https://github.com/bats-core/bats-core/issues/578) for more
details and how to find out if your tests are affected.
## [1.6.1] - 2022-05-14
### Fixed
* prevent `teardown`, `teardown_file`, and `teardown_suite` from overriding bats'
exit code by setting `$status` (e.g. via calling `run`) (#581, #575)
* **CRITICAL**: this can return exit code 0 despite failed tests, thus preventing
your CI from reporting test failures! The regression happened in version 1.6.0.
#### Documentation
* corrected invalid documentation of `run -N` (had `=N` instead) (#579)
* **CRITICAL**: using the incorrect form can lead to silent errors. See
[issue #578](https://github.com/bats-core/bats-core/issues/578) for more
details and how to find out if your tests are affected.
## [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
* new command line flags (#488)
* `--verbose-run`: Make `run` print `$output` by default
* `-x`, `--trace`: Print test commands as they are executed (like `set -x`)`
* `--show-output-of-passing-tests`: Print output of passing tests
* `--print-output-on-failure`: Automatically print the value of `$output` on
failed tests
* `--gather-test-outputs-in <directory>`: Gather the output of failing **and**
passing tests as files in directory
* Experimental: add return code checks to `run` via `!`/`-<N>` (#367, #507)
* `install.sh` and `uninstall.sh` take an optional second parameter for the lib
folder name to allow for multilib install, e.g. into lib64 (#452)
* add `run` flag `--keep-empty-lines` to retain empty lines in `${lines[@]}` (#224,
a894fbfa)
* add `run` flag `--separate-stderr` which also fills `$stderr` and
`$stderr_lines` (#47, 5c9b173d, #507)
### Fixed
* don't glob `run`'s `$output` when splitting into `${lines[@]}`
(#151, #152, #158, #156, #281, #289)
* remove empty line after test with pretty formatter on some terminals (#481)
* don't run setup_file/teardown_file on files without tests, e.g. due to
filtering (#484)
* print final line without newline on Bash 3.2 for midtest (ERREXIT) failures
too (#495, #145)
* abort with error on missing flock/shlock when running in parallel mode (#496)
* improved `set -u` test and fixed some unset variable accesses (#498, #501)
* shorten suite/file/test temporary folder paths to leave enough space even on
restricted systems (#503)
#### Documentation
* minor edits (#478)
## [1.4.1] - 2021-07-24
### Added
* Docker image architectures amd64, 386, arm64, arm/v7, arm/v6, ppc64le, s390x (#438)
### Fixed
* automatic push to Dockerhub (#438)
## [1.4.0] - 2021-07-23
### Added
* added BATS_TEST_TMPDIR, BATS_FILE_TMPDIR, BATS_SUITE_TMPDIR (#413)
* added checks and improved documentation for `$BATS_TMPDIR` (#410)
* the docker container now uses [tini](https://github.com/krallin/tini) as the
container entrypoint to improve signal forwarding (#407)
* script to uninstall bats from a given prefix (#400)
* replace preprocessed file path (e.g. `/tmp/bats-run-22908-NP0f9h/bats.23102.src`)
with original filename in stdout/err (but not FD3!) (#429)
* print aborted command on SIGINT/CTRL+C (#368)
* print error message when BATS_RUN_TMPDIR could not be created (#422)
#### Documentation
* added tutorial for new users (#397)
* fixed example invocation of docker container (#440)
* minor edits (#431, #439, #445, #463, #464, #465)
### Fixed
* fix `bats_tap_stream_unknown: command not found` with pretty formatter, when
writing non compliant extended output (#412)
* avoid collisions on `$BATS_RUN_TMPDIR` with `--no-tempdir-cleanup` and docker
by using `mktemp` additionally to PID (#409)
* pretty printer now puts text that is printed to FD 3 below the test name (#426)
* `rm semaphores/slot-: No such file or directory` in parallel mode on MacOS
(#434, #433)
* fix YAML blocks in TAP13 formatter using `...` instead of `---` to start
a block (#442)
* fixed some typos in comments (#441, #447)
* ensure `/code` exists in docker container, to make examples work again (#440)
* also display error messages from free code (#429)
* npm installed version on Windows: fix broken internal LIBEXEC paths (#459)
## [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.7.0...HEAD
[1.7.0]: https://github.com/bats-core/bats-core/compare/v1.6.1...v1.7.0
[1.6.1]: https://github.com/bats-core/bats-core/compare/v1.6.0...v1.6.1
[1.6.0]: https://github.com/bats-core/bats-core/compare/v1.5.0...v1.6.0
[1.5.0]: https://github.com/bats-core/bats-core/compare/v1.4.1...v1.5.0
[1.4.1]: https://github.com/bats-core/bats-core/compare/v1.4.0...v1.4.1
[1.4.0]: https://github.com/bats-core/bats-core/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/bats-core/bats-core/compare/v1.2.1...v1.3.0
[1.2.1]: https://github.com/bats-core/bats-core/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/bats-core/bats-core/compare/v1.1.0...v1.2.0
[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
+4
View File
@@ -0,0 +1,4 @@
# This enables automatic code review requests per:
# - https://help.github.com/articles/about-codeowners/
# - https://help.github.com/articles/enabling-required-reviews-for-pull-requests/
* @bats-core/bats-core
@@ -0,0 +1,92 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting one of the current [project maintainers](#project-maintainers) listed below. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Project Maintainers
### Current Maintainers
* [Bianca Tamayo][bt-gh]
* [Mike Bland][mb-gh]
* [Jason Karns][jk-gh]
* [Andrew Martin][am-gh]
### Past Maintainers
* Sam Stephenson <<sstephenson@gmail.com>> (Original author)
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[bt-gh]: https://github.com/btamayo
[mb-gh]: https://github.com/mbland
[jk-gh]: https://github.com/jasonkarns
[am-gh]: https://github.com/sublimino
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/4/
+232
View File
@@ -0,0 +1,232 @@
# Contributing Guidelines
## Welcome!
Thank you for considering contributing to this project's
development and/or documentation. Just a reminder: if you're new to this project
or to OSS and want to find issues to work on, please check the following labels
on issues:
- [help wanted][helpwantedlabel]
- [docs][docslabel]
- [good first issue][goodfirstissuelabel]
[docslabel]: https://github.com/bats-core/bats-core/labels/docs
[helpwantedlabel]: https://github.com/bats-core/bats-core/labels/help%20wanted
[goodfirstissuelabel]: https://github.com/bats-core/bats-core/labels/good%20first%20issue
To see all labels and their meanings, [check this wiki page][labelswiki].
[labelswiki]: https://github.com/bats-core/bats-core/wiki/GitHub-Issue-Labels
## Table of contents
* [Contributing Guidelines](#contributing-guidelines)
* [Welcome!](#welcome)
* [Table of contents](#table-of-contents)
* [Quick links](#quick-links)
* [Code of conduct](#code-of-conduct)
* [Asking questions](#asking-questions)
* [Updating documentation](#updating-documentation)
* [Testing](#testing)
* [Coding conventions](#coding-conventions)
* [Function declarations](#function-declarations)
* [Variable and parameter declarations](#variable-and-parameter-declarations)
* [Command substitution](#command-substitution)
* [Process substitution](#process-substitution)
* [Conditionals and loops](#conditionals-and-loops)
* [Generating output](#generating-output)
* [Signal names](#signal-names)
* [Gotchas](#gotchas)
* [Open Source License](#open-source-license)
* [Credits](#credits)
## Quick links
- [Gitter channel →][gitterurl]: Feel free to come chat with us on Gitter
- [README →][README]
- [Code of conduct →][CODE_OF_CONDUCT]
- [License information →][LICENSE]
- [Original repository →][repohome]
- [Issues →][repoissues]
- [Pull requests →][repoprs]
- [Milestones →][repomilestones]
- [Projects →][repoprojects]
[README]: https://github.com/bats-core/bats-core/blob/master/README.md
[CODE_OF_CONDUCT]: https://github.com/bats-core/bats-core/blob/master/docs/CODE_OF_CONDUCT.md
[LICENSE]: https://github.com/bats-core/bats-core/blob/master/LICENSE.md
## Code of conduct
Harassment or rudeness of any kind will not be tolerated, period. For
specifics, see the [CODE_OF_CONDUCT][] file.
## Asking questions
Please check the [documentation][documentation] or existing [discussions][] and [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)
## Updating documentation
We love documentation and people who love documentation!
If you love writing clear, accessible docs, please don't be shy about pull
requests. Remember: docs are just as important as code.
Also: _no typo is too small to fix!_ Really. Of course, batches of fixes are
preferred, but even one nit is one nit too many.
## Testing
- Continuous integration status: [![Tests](https://github.com/bats-core/bats-core/workflows/Tests/badge.svg)](https://github.com/bats-core/bats-core/actions?query=workflow%3ATests)
To run all tests:
```sh
bin/bats test
```
To run a single test file:
```sh
bin/bats test/file.bats
```
When running from a terminal, Bats uses the *pretty* formatter by default.
However, to debug Bats you might need to see the raw test output.
The **cat** formatter is intended as an internal debugging tool because
it does not process test outputs.
To use it, run Bats with the `--formatter cat` option.
## Coding conventions
Use (`shfmt`)[https://github.com/mvdan/sh#shfmt] and [ShellCheck](https://www.shellcheck.net/). The CI will enforce this.
Use `snake_case` for all identifiers.
### Function declarations
- Declare functions without the `function` keyword.
- Strive to always use `return`, never `exit`, unless an error condition is
severe enough to warrant it.
- Calling `exit` makes it difficult for the caller to recover from an error,
or to compose new commands from existing ones.
### Variable and parameter declarations
- Declare all variables inside functions using `local`.
- Declare temporary file-level variables using `declare`. Use `unset` to remove
them when finished.
- Don't use `local -r`, as a readonly local variable in one scope can cause a
conflict when it calls a function that declares a `local` variable of the same
name.
- Don't use type flags with `declare` or `local`. Assignments to integer
variables in particular may behave differently, and it has no effect on array
variables.
- For most functions, the first lines should use `local` declarations to
assign the original positional parameters to more meaningful names, e.g.:
```bash
format_summary() {
local cmd_name="$1"
local summary="$2"
local longest_name_len="$3"
```
For very short functions, this _may not_ be necessary, e.g.:
```bash
has_spaces() {
[[ "$1" != "${1//[[:space:]]/}" ]]
}
```
### Command substitution
- If possible, don't. While this capability is one of Bash's core strengths,
every new process created by Bats makes the framework slower, and speed is
critical to encouraging the practice of automated testing. (This is especially
true on Windows, [where process creation is one or two orders of magnitude
slower][win-slow]. See [bats-core/bats-core#8][pr-8] for an illustration of
the difference avoiding subshells makes.) Bash is quite powerful; see if you
can do what you need in pure Bash first.
- If you need to capture the output from a function, store the output using
`printf -v` instead if possible. `-v` specifies the name of the variable into
which to write the result; the caller can supply this name as a parameter.
- If you must use command substitution, use `$()` instead of backticks, as it's
more robust, more searchable, and can be nested.
[win-slow]: https://rufflewind.com/2014-08-23/windows-bash-slow
[pr-8]: https://github.com/bats-core/bats-core/pull/8
### Process substitution
- If possible, don't use it. See the advice on avoiding subprocesses and using
`printf -v` in the **Command substitution** section above.
- Use wherever necessary and possible, such as when piping input into a `while`
loop (which avoids having the loop body execute in a subshell) or running a
command taking multiple filename arguments based on output from a function or
pipeline (e.g. `diff`).
- *Warning*: It is impossible to directly determine the exit status of a process
substitution; emitting an exit status as the last line of output is a possible
workaround.
### Conditionals and loops
- Always use `[[` and `]]` for evaluating variables. Per the guideline under
**Formatting**, quote variables and strings within the brackets, but not
regular expressions (or variables containing regular expressions) appearing
on the right side of the `=~` operator.
### Generating output
- Use `printf` instead of `echo`. Both are Bash builtins, and there's no
perceptible performance difference when running Bats under the `time` builtin.
However, `printf` provides a more consistent experience in general, as `echo`
has limitations to the arguments it accepts, and even the same version of Bash
may produce different results for `echo` based on how the binary was compiled.
See [Stack Overflow: Why is printf better than echo?][printf-vs-echo] for
excruciating details.
[printf-vs-echo]: https://unix.stackexchange.com/a/65819
### Signal names
Always use upper case signal names (e.g. `trap - INT EXIT`) to avoid locale
dependent errors. In some locales (for example Turkish, see
[Turkish dotless i](https://en.wikipedia.org/wiki/Dotted_and_dotless_I)) lower
case signal names cause Bash to error. An example of the problem:
```bash
$ echo "tr_TR.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen tr_TR.UTF-8 # Ubuntu derivatives
$ LC_CTYPE=tr_TR.UTF-8 LC_MESSAGES=C bash -c 'trap - int && echo success'
bash: line 0: trap: int: invalid signal specification
$ LC_CTYPE=tr_TR.UTF-8 LC_MESSAGES=C bash -c 'trap - INT && echo success'
success
```
## Credits
The [official bash logo](https://github.com/odb/official-bash-logo) is copyrighted
by the [Free Software Foundation](https://www.fsf.org/), 2016 under the [Free Art License](http://artlibre.org/licence/lal/en/)
This guide borrows **heavily** from [@mbland's go-script-bash][gsb] (with some
sections directly quoted), which in turn was
drafted with tips from [Wrangling Web Contributions: How to Build
a CONTRIBUTING.md][moz] and with some inspiration from [the Atom project's
CONTRIBUTING.md file][atom].
[gsb]: https://github.com/mbland/go-script-bash/blob/master/CONTRIBUTING.md
[moz]: https://mozillascience.github.io/working-open-workshop/contributing/
[atom]: https://github.com/atom/atom/blob/master/CONTRIBUTING.md
[discussions]: https://github.com/bats-core/bats-core/discussions
[documentation]: https://bats-core.readthedocs.io/
[repoprojects]: https://github.com/bats-core/bats-core/projects
[repomilestones]: https://github.com/bats-core/bats-core/milestones
[repoprs]: https://github.com/bats-core/bats-core/pulls
[repoissues]: https://github.com/bats-core/bats-core/issues
[repohome]: https://github.com/bats-core/bats-core
[osmit]: https://opensource.org/licenses/MIT
[gitterurl]: https://gitter.im/bats-core/bats-core
+20
View File
@@ -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,5 @@
- [ ] I have reviewed the [Contributor Guidelines][contributor].
- [ ] I have reviewed the [Code of Conduct][coc] and agree to abide by it
[contributor]: https://github.com/bats-core/bats-core/blob/master/docs/CONTRIBUTING.md
[coc]: https://github.com/bats-core/bats-core/blob/master/docs/CODE_OF_CONDUCT.md
@@ -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 "$@"
+51
View File
@@ -0,0 +1,51 @@
#!/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" {
# shellcheck disable=SC2030
src_dir='not-a-dir'
run main
[ "${status}" -ne 0 ]
}
# shellcheck disable=SC2016
@test "pass when \$src_dir directory is empty" {
# shellcheck disable=SC2031,SC2030
rm -rf "${src_dir:?}/*"
run main
echo "$output"
[ "${status}" -eq 0 ]
}
# shellcheck disable=SC2016
@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 ]]
}
+4
View File
@@ -0,0 +1,4 @@
import pickle
with open("build/doctrees/environment.pickle", "rb") as f:
dat = pickle.load(f)
print(dat.domaindata['std']['labels'])
+35
View File
@@ -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
+127
View File
@@ -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,24 @@
# Our Logo
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="dark_mode_cube.svg">
<img alt="" src="light_mode_cube.svg">
</picture>
</div>
## Usage Guide for Third Parties
There may be cases where you want to use our logo. Please follow these rules:
**DO** use our logo as a part of thumbnails, banners/images in articles, or on a README in reference to the this project.
**DO** use our logo to relate resources back to the Bats project, like file associations or links to the project page.
**DO NOT** use our logo as the face of a third-party tool or extension that is not affiliated with the Bats project or Bats maintainers.
If in doubt, ask if your intended usecase is approved.
## Credits
The Bats Logo was created by [Vukory](https://www.artstation.com/vukory) ([Github](https://github.com/vukory)) and sponsored by [SethFalco](https://github.com/SethFalco).
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="54.261147mm"
height="43.081844mm"
viewBox="0 0 54.261147 43.081844"
version="1.1"
id="svg2478"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2475" />
<g
id="layer1"
transform="translate(-71.195776,-139.44713)">
<g
id="g9615">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
id="g24595"
transform="matrix(1.8087061,0,0,1.8087061,-1236.9066,713.01919)"
style="stroke-width:0.64503">
<path
style="fill:#b1bbc0;fill-opacity:1;stroke:none;stroke-width:0.51199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1344,-889.4993 2.9413,-2.92919 0.8498,3.37817 13.7088,-13.65367 -1.5371,6.21648 1.1421,4.5359 -3.0032,2.99127 -1.666,6.73802 -8.7932,3.67943 -3.6425,3.62793 -3.6425,-3.62793 -8.7932,-3.67943 -1.666,-6.73802 -3.0033,-2.99127 1.1421,-4.5359 -1.537,-6.21648 13.7088,13.65367 0.8498,-3.37817 z"
id="path41727" />
<g
id="g41737"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.64503">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1545.57,-882.22245 -8.7932,3.67942 4.7021,-4.68334 z"
id="path41729" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1343,-885.27404 17.5,-17.42995 -3.3983,13.74351 -5.7571,5.73411 z"
id="path41731" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1520.6986,-882.22245 8.7932,3.67942 -4.7021,-4.68334 z"
id="path41733" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1343,-885.27404 -17.5,-17.42995 3.3983,13.74351 5.7571,5.73411 z"
id="path41735" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="54mm"
height="49.773262mm"
viewBox="0 0 54 49.773262"
version="1.1"
id="svg2478"
sodipodi:docname="dark_mode_cube.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.5617892"
inkscape:cx="16.007282"
inkscape:cy="85.158741"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g9615" />
<defs
id="defs2475" />
<g
id="layer1"
transform="translate(-71.28524,-137.37328)">
<g
id="g9615">
<g
id="g1124">
<path
id="path41791"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.22665;stroke-linecap:round;stroke-linejoin:round"
d="m 96.963564,151.38471 23.654566,-13.59708 a 3.1148255,3.1148255 0 0 1 4.66711,2.70047 v 27.80743 a 5.5821378,5.5821378 0 0 1 -2.80027,4.83957 l -23.654571,13.59709 a 3.1148255,3.1148255 0 0 1 -4.667106,-2.70048 v -27.80743 a 5.5821378,5.5821378 0 0 1 2.800271,-4.83957 z" />
<path
id="path41803"
style="display:inline;fill:#45b34f;stroke-width:0.118768;stroke-linecap:round;stroke-linejoin:round"
d="m 114.81168,169.4541 5.56851,-3.20545 a 0.09387282,0.09387282 0 0 1 0.14066,0.0782 l 0.0603,1.77975 a 0.19488658,0.19488658 0 0 1 -0.0976,0.17551 l -5.5685,3.20545 a 0.09387286,0.09387286 0 0 1 -0.14066,-0.0782 l -0.0603,-1.77976 a 0.19488664,0.19488664 0 0 1 0.0976,-0.1755 z" />
<g
id="g1108">
<g
id="g9585">
<path
id="path41793"
style="fill:#b1bbc0;fill-opacity:1;stroke:none;stroke-width:0.793752;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 71.285354,146.05562 2.00944,8.127 -1.49302,5.92984 3.92614,3.91052 2.17795,8.80874 11.4956,4.81002 4.76194,4.74298 v -4e-4 -19.06604 0 l -3.84523,-3.82922 -1.11095,4.41624 z" />
<g
id="g9208">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 77.905907,172.83169 11.49544,4.81016 -6.147116,-6.12259 z"
id="path41799" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 94.163216,168.8423 -22.877976,-22.78639 4.442674,17.96707 7.526317,7.49628 z"
id="path41801" />
</g>
</g>
<g
id="g1090">
<g
id="g9214">
<path
id="path41805"
style="fill:#3e474b;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 117.04103,146.05561 -2.00944,8.1268 1.49326,5.92985 -3.92634,3.91052 -2.17795,8.80874 -11.495396,4.81022 -4.76176,4.74257 v 0 -19.06603 l 3.84524,-3.82942 1.11094,4.41623 z" />
</g>
<g
id="g1081"
transform="translate(-5e-5,-2e-4)">
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 110.42056,172.83169 -11.495474,4.81016 6.147114,-6.12259 z"
id="path41795" />
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 94.163216,168.8423 22.877974,-22.78639 -4.44263,17.96707 -7.52636,7.49628 z"
id="path41797" />
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="70.001816mm"
height="22.666498mm"
viewBox="0 0 70.001816 22.666498"
version="1.1"
id="svg2478"
sodipodi:docname="dark_mode_wordmark_lowercase.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview12"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.9994422"
inkscape:cx="123.03431"
inkscape:cy="-15.504324"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2478" />
<defs
id="defs2475" />
<g
id="g967"
transform="translate(-2.2495861,-17.599696)">
<g
id="g9615"
transform="translate(-59.217329,-138.31344)">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
id="g41829"
style="display:inline"
transform="matrix(1.1666675,0,0,1.1666675,-235.22352,200.90935)">
<path
d="m 1481.5343,-921.52789 v -22.40145 h 11.8268 c 2.2494,0 3.9423,0.53337 5.0786,1.6001 1.1595,1.06674 1.7393,2.57408 1.7393,4.52204 0,1.18268 -0.2319,2.17985 -0.6957,2.99149 -0.4406,0.78846 -1.032,1.40299 -1.7741,1.8436 1.0204,0.37104 1.8204,0.916 2.4002,1.63489 0.5797,0.71889 0.8696,1.79722 0.8696,3.23499 0,2.13347 -0.6261,3.76836 -1.8784,4.90466 -1.2522,1.11312 -3.0494,1.66968 -5.3916,1.66968 z m 4.4993,-13.28782 h 6.8068 c 0.9739,0 1.6812,-0.2203 2.1218,-0.66091 0.4406,-0.44061 0.661,-1.05514 0.661,-1.8436 0,-0.85802 -0.2204,-1.49575 -0.661,-1.91316 -0.4406,-0.44061 -1.2406,-0.66092 -2.4001,-0.66092 h -6.5285 z m 0,9.28756 h 7.3633 c 1.0204,0 1.774,-0.20871 2.261,-0.62613 0.5102,-0.41742 0.7653,-1.15949 0.7653,-2.22623 0,-0.85802 -0.2435,-1.49575 -0.7305,-1.91317 -0.487,-0.4406 -1.3218,-0.66091 -2.5045,-0.66091 h -7.1546 z"
id="path41821"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#ffffff;stroke:#ffffff;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path41823"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#ffffff;stroke:#ffffff;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1506.4181,-940.01134 v 3.60332 h 4.173 c 2.6047,0 4.2857,-0.0426 4.4195,1.62757 v 0.87865 c -0.037,0 -0.2304,6.8e-4 -0.3233,8.1e-4 h -2.4927 c 0,0 -0.049,6.8e-4 -0.099,10e-4 -0.3506,-6.8e-4 -0.6995,-10e-4 -1.0639,-0.002 -5.2023,0 -6.9053,2.88353 -7.1561,5.75075 -0.015,5.2321 3.7291,6.56658 7.1561,6.61879 2.1165,-0.009 4.173,0 4.173,0 h 3.9656 v -6.1849 -6.18478 -0.49069 c 0,-4.48165 -2.4644,-5.61832 -7.2996,-5.61832 z m 4.2893,10.10451 h 4.3032 v 2.18928 2.18941 h -1.7445 -2.5587 c -0.9821,0 -1.7058,-0.19377 -2.171,-0.58128 -0.4652,-0.38748 -0.6978,-0.91705 -0.6978,-1.5887 0,-0.007 7e-4,-0.0129 7e-4,-0.0194 h 7e-4 v -0.007 c 0,-0.15019 0.012,-0.29241 0.033,-0.4271 0.079,-0.47856 0.2998,-0.87018 0.6646,-1.17403 0.4652,-0.38751 1.1889,-0.58128 2.171,-0.58128 z" />
<path
d="m 1528.5277,-921.52789 c -3.1768,-0.15406 -4.4238,-2.60536 -4.3648,-5.87864 v -8.59296 h -2.4895 v -4.01186 h 2.4895 v -3.91799 h 4.0169 v 3.91799 h 4.4177 v 4.01186 h -4.4177 v 8.21032 c 0,0.78846 0.1971,1.48415 0.5914,1.80881 0.3942,0.30147 0.9855,0.45221 1.774,0.45221 l 2.0523,0.006 v 3.99471 z"
id="path41825"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#ffffff;stroke:#ffffff;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path41827"
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.264583;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1543.5776,-940.01134 c -5.3998,0 -7.8157,1.34284 -7.9478,5.27667 0.1079,1.84396 0.6367,3.10179 2.0737,3.96515 h 0.024 c 0.1601,0.0968 0.3347,0.18873 0.5194,0.28834 3.2668,1.76133 7.8402,1.45762 8.1892,3.67678 0,0 0.092,1.20801 -1.2599,1.28157 h -3.1895 -2.7156 -3.6349 v 3.9951 h 3.9693 3.7181 c 5.3998,0 7.8156,-1.34284 7.9478,-5.27667 -0.108,-1.84396 -0.6368,-3.10176 -2.0737,-3.96512 h -0.024 c -0.16,-0.0968 -0.3346,-0.18875 -0.5193,-0.28837 -3.2668,-1.76133 -7.8403,-1.45764 -8.1892,-3.67678 0,0 -0.092,-1.20801 1.2599,-1.28156 h 3.1895 2.7155 3.635 v -3.99511 h -3.9693 z" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="80.745293mm"
height="21.625351mm"
viewBox="0 0 80.745293 21.625351"
version="1.1"
id="svg2478"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="dark_mode_wordmark_uppercase.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2480"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.0469406"
inkscape:cx="102.20255"
inkscape:cy="46.325454"
inkscape:window-width="1284"
inkscape:window-height="1040"
inkscape:window-x="632"
inkscape:window-y="71"
inkscape:window-maximized="0"
inkscape:current-layer="g41739" />
<defs
id="defs2475" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-69.238415,-136.04173)">
<g
id="g9615">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
aria-label="BATS"
id="g41789"
style="font-size:15.875px;line-height:1.25;word-spacing:0px;display:inline;fill:#ffffff;stroke-width:0.128968"
transform="matrix(2.3934725,0,0,2.3934725,1452.4353,-1069.4974)"
inkscape:export-filename="D:\Art2021\2023\Bats-refined.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150">
<path
d="m 20.76962,71.305199 v -10.2235 h 5.3975 q 1.539875,0 2.31775,0.73025 0.79375,0.73025 0.79375,2.06375 0,0.809625 -0.3175,1.36525 -0.301625,0.53975 -0.809625,0.841375 0.6985,0.254 1.095375,0.746125 0.396875,0.492125 0.396875,1.476375 0,1.4605 -0.85725,2.238375 -0.85725,0.762 -2.460625,0.762 z m 2.555875,-6.06425 h 2.079625 q 0.66675,0 0.968375,-0.301625 0.301625,-0.301625 0.301625,-0.841375 0,-0.587375 -0.301625,-0.873125 -0.301625,-0.301625 -1.095375,-0.301625 h -1.952625 z m 0,4.238625 h 2.333625 q 0.6985,0 1.031875,-0.28575 0.34925,-0.28575 0.34925,-1.016 0,-0.587375 -0.333375,-0.873125 -0.333375,-0.301625 -1.143,-0.301625 h -2.238375 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';stroke-width:0.128968"
id="path41781" />
<path
d="m 31.152168,71.305199 3.90525,-10.2235 h 2.76225 l 3.90525,10.2235 h -2.667 l -0.92075,-2.31775 h -3.413125 l -0.904875,2.31775 z m 3.921125,-4.206875 h 2.714625 l -1.36525,-3.556 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';stroke-width:0.128968"
id="path41783" />
<path
d="m 44.89475,71.305199 v -8.255 h -3.000375 v -1.9685 H 50.451 v 1.9685 h -3.000375 v 8.255 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';stroke-width:0.128968"
id="path41785" />
<path
d="m 55.873376,71.463949 q -1.016,0 -1.905,-0.15875 -0.873125,-0.142875 -1.508125,-0.428625 v -2.174875 q 0.6985,0.301625 1.539875,0.492125 0.85725,0.1905 1.61925,0.1905 0.9525,0 1.412875,-0.174625 0.47625,-0.174625 0.47625,-0.762 0,-0.396875 -0.238125,-0.635 -0.22225,-0.238125 -0.746125,-0.41275 -0.508,-0.1905 -1.397,-0.4445 -1.04775,-0.3175 -1.666875,-0.6985 -0.619125,-0.396875 -0.889,-0.9525 -0.269875,-0.555625 -0.269875,-1.36525 0,-1.4605 1.04775,-2.238375 1.04775,-0.777875 3.095625,-0.777875 0.889,0 1.730375,0.142875 0.841375,0.127 1.36525,0.301625 v 2.19075 q -0.682625,-0.269875 -1.381125,-0.396875 -0.682625,-0.127 -1.3335,-0.127 -0.85725,0 -1.397,0.15875 -0.523875,0.15875 -0.523875,0.73025 0,0.333375 0.1905,0.53975 0.1905,0.1905 0.650875,0.34925 0.47625,0.15875 1.285875,0.381 1.254125,0.333375 1.920875,0.809625 0.66675,0.460375 0.92075,1.0795 0.254,0.60325 0.254,1.36525 0,1.349375 -1.04775,2.19075 -1.04775,0.8255 -3.20675,0.8255 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';stroke-width:0.128968"
id="path41787" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="54.261147mm"
height="43.081844mm"
viewBox="0 0 54.261147 43.081844"
version="1.1"
id="svg2478"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="light_mode_bat.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2480"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.0469406"
inkscape:cx="95.5164"
inkscape:cy="32.953158"
inkscape:window-width="1284"
inkscape:window-height="1040"
inkscape:window-x="632"
inkscape:window-y="71"
inkscape:window-maximized="0"
inkscape:current-layer="g41739" />
<defs
id="defs2475" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-71.195776,-139.44713)">
<g
id="g9615">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
id="g24595"
transform="matrix(1.8087061,0,0,1.8087061,-1236.9066,713.01919)"
style="stroke-width:0.64503">
<path
style="fill:#3e474b;fill-opacity:1;stroke:none;stroke-width:0.51199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1344,-889.4993 2.9413,-2.92919 0.8498,3.37817 13.7088,-13.65367 -1.5371,6.21648 1.1421,4.5359 -3.0032,2.99127 -1.666,6.73802 -8.7932,3.67943 -3.6425,3.62793 -3.6425,-3.62793 -8.7932,-3.67943 -1.666,-6.73802 -3.0033,-2.99127 1.1421,-4.5359 -1.537,-6.21648 13.7088,13.65367 0.8498,-3.37817 z"
id="path41727"
inkscape:export-filename="D:\Art2021\2023\BatsLogo\test.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150" />
<g
id="g41737"
style="fill:#515b60;fill-opacity:1;stroke-width:0.64503">
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1545.57,-882.22245 -8.7932,3.67942 4.7021,-4.68334 z"
id="path41729"
inkscape:export-filename="D:\Art2021\2023\BatsLogo\test.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150" />
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1343,-885.27404 17.5,-17.42995 -3.3983,13.74351 -5.7571,5.73411 z"
id="path41731"
inkscape:export-filename="D:\Art2021\2023\BatsLogo\test.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150" />
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1520.6986,-882.22245 8.7932,3.67942 -4.7021,-4.68334 z"
id="path41733"
inkscape:export-filename="D:\Art2021\2023\BatsLogo\test.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150" />
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.170933;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1533.1343,-885.27404 -17.5,-17.42995 3.3983,13.74351 5.7571,5.73411 z"
id="path41735"
inkscape:export-filename="D:\Art2021\2023\BatsLogo\test.png"
inkscape:export-xdpi="150"
inkscape:export-ydpi="150" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="54mm"
height="49.773262mm"
viewBox="0 0 54 49.773262"
version="1.1"
id="svg2478"
sodipodi:docname="light_mode_cube.svg"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview22"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="3.1235784"
inkscape:cx="81.797212"
inkscape:cy="86.439323"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2478" />
<defs
id="defs2475" />
<g
id="g1227">
<path
id="path41791"
style="fill:#3e474b;fill-opacity:1;stroke-width:0.22665;stroke-linecap:round;stroke-linejoin:round"
d="M 25.678324,14.01143 49.33289,0.41435 A 3.1148255,3.1148255 0 0 1 54,3.11482 v 27.80743 a 5.5821378,5.5821378 0 0 1 -2.80027,4.83957 L 27.545159,49.35891 A 3.1148255,3.1148255 0 0 1 22.878053,46.65843 V 18.851 a 5.5821378,5.5821378 0 0 1 2.800271,-4.83957 z" />
<path
id="path41803"
style="display:inline;fill:#45b34f;stroke-width:0.118768;stroke-linecap:round;stroke-linejoin:round"
d="m 43.52644,32.08082 5.56851,-3.20545 a 0.09387282,0.09387282 0 0 1 0.14066,0.0782 l 0.0603,1.77975 a 0.19488658,0.19488658 0 0 1 -0.0976,0.17551 l -5.5685,3.20545 a 0.09387286,0.09387286 0 0 1 -0.14066,-0.0782 l -0.0603,-1.77976 a 0.19488664,0.19488664 0 0 1 0.0976,-0.1755 z" />
<g
id="g1212">
<g
id="g9585"
transform="translate(-71.28524,-137.37328)">
<path
id="path41793"
style="fill:#3e474b;fill-opacity:1;stroke:none;stroke-width:0.793752;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 71.285354,146.05562 2.00944,8.127 -1.49302,5.92984 3.92614,3.91052 2.17795,8.80874 11.4956,4.81002 4.76194,4.74298 v -4e-4 -19.06604 0 l -3.84523,-3.82922 -1.11095,4.41624 z" />
<g
id="g9208">
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 77.905907,172.83169 11.49544,4.81016 -6.147116,-6.12259 z"
id="path41799" />
<path
style="fill:#515b60;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 94.163216,168.8423 -22.877976,-22.78639 4.442674,17.96707 7.526317,7.49628 z"
id="path41801" />
</g>
</g>
<g
id="g9214"
transform="translate(-71.28524,-137.37328)">
<path
id="path41805"
style="fill:#b1bbc0;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 117.04096,146.05572 -2.00944,8.1268 1.49326,5.92985 -3.92634,3.91052 -2.17795,8.80874 -11.495404,4.81022 -4.761755,4.74257 v 0 -19.06603 l 3.845235,-3.82942 1.110946,4.41623 z" />
<g
id="g9204"
style="fill:#ffffff;fill-opacity:1">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 110.42056,172.83169 -11.495474,4.81016 6.147114,-6.12259 z"
id="path41807" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 94.163216,168.8423 22.877974,-22.78639 -4.44263,17.96707 -7.52636,7.49628 z"
id="path41809" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="70.001816mm"
height="22.666498mm"
viewBox="0 0 70.001816 22.666498"
version="1.1"
id="svg2478"
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
sodipodi:docname="wordmark_lowercase_dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2480"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:document-units="mm"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.0469406"
inkscape:cx="115.57484"
inkscape:cy="-0.477582"
inkscape:window-width="1284"
inkscape:window-height="1040"
inkscape:window-x="632"
inkscape:window-y="71"
inkscape:window-maximized="0"
inkscape:current-layer="g41739" />
<defs
id="defs2475" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-65.637684,-148.3707)">
<g
id="g9615">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
id="g41939"
transform="matrix(1.1666675,0,0,1.1666675,-230.35762,192.10983)"
style="display:inline;fill:#3e474b;fill-opacity:1;stroke:#3e474b;stroke-opacity:1">
<path
d="m 1481.5343,-921.52789 v -22.40145 h 11.8268 c 2.2494,0 3.9423,0.53337 5.0786,1.6001 1.1595,1.06674 1.7393,2.57408 1.7393,4.52204 0,1.18268 -0.2319,2.17985 -0.6957,2.99149 -0.4406,0.78846 -1.032,1.40299 -1.7741,1.8436 1.0204,0.37104 1.8204,0.916 2.4002,1.63489 0.5797,0.71889 0.8696,1.79722 0.8696,3.23499 0,2.13347 -0.6261,3.76836 -1.8784,4.90466 -1.2522,1.11312 -3.0494,1.66968 -5.3916,1.66968 z m 4.4993,-13.28782 h 6.8068 c 0.9739,0 1.6812,-0.2203 2.1218,-0.66091 0.4406,-0.44061 0.661,-1.05514 0.661,-1.8436 0,-0.85802 -0.2204,-1.49575 -0.661,-1.91316 -0.4406,-0.44061 -1.2406,-0.66092 -2.4001,-0.66092 h -6.5285 z m 0,9.28756 h 7.3633 c 1.0204,0 1.774,-0.20871 2.261,-0.62613 0.5102,-0.41742 0.7653,-1.15949 0.7653,-2.22623 0,-0.85802 -0.2435,-1.49575 -0.7305,-1.91317 -0.487,-0.4406 -1.3218,-0.66091 -2.5045,-0.66091 h -7.1546 z"
id="path41931"
sodipodi:nodetypes="ccscsccsscsccssscscccscscscc"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#3e474b;fill-opacity:1;stroke:#3e474b;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path41933"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#3e474b;fill-opacity:1;stroke:#3e474b;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1506.4181,-940.01134 v 3.60332 h 4.173 c 2.6047,0 4.2857,-0.0426 4.4195,1.62757 v 0.87865 c -0.037,0 -0.2304,6.8e-4 -0.3233,8.1e-4 h -2.4927 c 0,0 -0.049,6.8e-4 -0.099,10e-4 -0.3506,-6.8e-4 -0.6995,-10e-4 -1.0639,-0.002 -5.2023,0 -6.9053,2.88353 -7.1561,5.75075 -0.015,5.2321 3.7291,6.56658 7.1561,6.61879 2.1165,-0.009 4.173,0 4.173,0 h 3.9656 v -6.1849 -6.18478 -0.49069 c 0,-4.48165 -2.4644,-5.61832 -7.2996,-5.61832 z m 4.2893,10.10451 h 4.3032 v 2.18928 2.18941 h -1.7445 -2.5587 c -0.9821,0 -1.7058,-0.19377 -2.171,-0.58128 -0.4652,-0.38748 -0.6978,-0.91705 -0.6978,-1.5887 0,-0.007 7e-4,-0.0129 7e-4,-0.0194 h 7e-4 v -0.007 c 0,-0.15019 0.012,-0.29241 0.033,-0.4271 0.079,-0.47856 0.2998,-0.87018 0.6646,-1.17403 0.4652,-0.38751 1.1889,-0.58128 2.171,-0.58128 z"
sodipodi:nodetypes="ccsccccccccccccscccccccsssccccscc" />
<path
d="m 1528.5277,-921.52789 c -3.1768,-0.15406 -4.4238,-2.60536 -4.3648,-5.87864 v -8.59296 h -2.4895 v -4.01186 h 2.4895 v -3.91799 h 4.0169 v 3.91799 h 4.4177 v 4.01186 h -4.4177 v 8.21032 c 0,0.78846 0.1971,1.48415 0.5914,1.80881 0.3942,0.30147 0.9855,0.45221 1.774,0.45221 l 2.0523,0.006 v 3.99471 z"
id="path41935"
style="font-weight:600;font-size:135.945px;line-height:1.25;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';word-spacing:0px;fill:#3e474b;fill-opacity:1;stroke:#3e474b;stroke-width:0.264583;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccccccccccccscsccc" />
<path
id="path41937"
style="fill:#3e474b;fill-opacity:1;stroke:#3e474b;stroke-width:0.264583;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1543.5776,-940.01134 c -5.3998,0 -7.8157,1.34284 -7.9478,5.27667 0.1079,1.84396 0.6367,3.10179 2.0737,3.96515 h 0.024 c 0.1601,0.0968 0.3347,0.18873 0.5194,0.28834 3.2668,1.76133 7.8402,1.45762 8.1892,3.67678 0,0 0.092,1.20801 -1.2599,1.28157 h -3.1895 -2.7156 -3.6349 v 3.9951 h 3.9693 3.7181 c 5.3998,0 7.8156,-1.34284 7.9478,-5.27667 -0.108,-1.84396 -0.6368,-3.10176 -2.0737,-3.96512 h -0.024 c -0.16,-0.0968 -0.3346,-0.18875 -0.5193,-0.28837 -3.2668,-1.76133 -7.8403,-1.45764 -8.1892,-3.67678 0,0 -0.092,-1.20801 1.2599,-1.28156 h 3.1895 2.7155 3.635 v -3.99511 h -3.9693 z" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="80.745293mm"
height="21.625351mm"
viewBox="0 0 80.745293 21.625351"
version="1.1"
id="svg2478"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2475" />
<g
id="layer1"
transform="translate(-69.301072,-141.15047)">
<g
id="g9615">
<g
id="g41739"
transform="matrix(0.85714226,0,0,0.85714226,-1218.3151,927.76689)"
style="display:inline;stroke-width:1.16667">
<g
aria-label="BATS"
id="g41869"
style="font-size:15.875px;line-height:1.25;word-spacing:0px;display:inline;fill:#3e474b;fill-opacity:1;stroke-width:0.128968"
transform="matrix(2.3934725,0,0,2.3934725,1452.5084,-1063.5372)">
<path
d="m 20.76962,71.305199 v -10.2235 h 5.3975 q 1.539875,0 2.31775,0.73025 0.79375,0.73025 0.79375,2.06375 0,0.809625 -0.3175,1.36525 -0.301625,0.53975 -0.809625,0.841375 0.6985,0.254 1.095375,0.746125 0.396875,0.492125 0.396875,1.476375 0,1.4605 -0.85725,2.238375 -0.85725,0.762 -2.460625,0.762 z m 2.555875,-6.06425 h 2.079625 q 0.66675,0 0.968375,-0.301625 0.301625,-0.301625 0.301625,-0.841375 0,-0.587375 -0.301625,-0.873125 -0.301625,-0.301625 -1.095375,-0.301625 h -1.952625 z m 0,4.238625 h 2.333625 q 0.6985,0 1.031875,-0.28575 0.34925,-0.28575 0.34925,-1.016 0,-0.587375 -0.333375,-0.873125 -0.333375,-0.301625 -1.143,-0.301625 h -2.238375 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';fill:#3e474b;fill-opacity:1;stroke-width:0.128968"
id="path41861" />
<path
d="m 31.152168,71.305199 3.90525,-10.2235 h 2.76225 l 3.90525,10.2235 h -2.667 l -0.92075,-2.31775 h -3.413125 l -0.904875,2.31775 z m 3.921125,-4.206875 h 2.714625 l -1.36525,-3.556 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';fill:#3e474b;fill-opacity:1;stroke-width:0.128968"
id="path41863" />
<path
d="m 44.89475,71.305199 v -8.255 h -3.000375 v -1.9685 H 50.451 v 1.9685 h -3.000375 v 8.255 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';fill:#3e474b;fill-opacity:1;stroke-width:0.128968"
id="path41865" />
<path
d="m 55.873376,71.463949 q -1.016,0 -1.905,-0.15875 -0.873125,-0.142875 -1.508125,-0.428625 v -2.174875 q 0.6985,0.301625 1.539875,0.492125 0.85725,0.1905 1.61925,0.1905 0.9525,0 1.412875,-0.174625 0.47625,-0.174625 0.47625,-0.762 0,-0.396875 -0.238125,-0.635 -0.22225,-0.238125 -0.746125,-0.41275 -0.508,-0.1905 -1.397,-0.4445 -1.04775,-0.3175 -1.666875,-0.6985 -0.619125,-0.396875 -0.889,-0.9525 -0.269875,-0.555625 -0.269875,-1.36525 0,-1.4605 1.04775,-2.238375 1.04775,-0.777875 3.095625,-0.777875 0.889,0 1.730375,0.142875 0.841375,0.127 1.36525,0.301625 v 2.19075 q -0.682625,-0.269875 -1.381125,-0.396875 -0.682625,-0.127 -1.3335,-0.127 -0.85725,0 -1.397,0.15875 -0.523875,0.15875 -0.523875,0.73025 0,0.333375 0.1905,0.53975 0.1905,0.1905 0.650875,0.34925 0.47625,0.15875 1.285875,0.381 1.254125,0.333375 1.920875,0.809625 0.66675,0.460375 0.92075,1.0795 0.254,0.60325 0.254,1.36525 0,1.349375 -1.04775,2.19075 -1.04775,0.8255 -3.20675,0.8255 z"
style="font-weight:600;font-family:Kanit;-inkscape-font-specification:'Kanit Semi-Bold';fill:#3e474b;fill-opacity:1;stroke-width:0.128968"
id="path41867" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

+74
View File
@@ -0,0 +1,74 @@
# 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 = '2022, bats-core organization'
author = 'bats-core organization'
# 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',
'sphinx.ext.autosectionlabel'
]
# 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', 'assets']
html_logo = "assets/light_mode_cube.svg"
#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,70 @@
# Docker Usage Guide
- [Docker Usage Guide](#docker-usage-guide)
* [Basic Usage](#basic-usage)
* [Basic Usage for bats project](#basic-usage-for-bats-project)
* [Docker Gotchas](#docker-gotchas)
* [Extending from the base image](#extending-from-the-base-image)
## Basic Usage
For test suites that are intended to run in isolation from their project code, you can mount the test directory and run the [official bats docker image](https://hub.docker.com/r/bats/bats):
```bash
$ docker run -it -v "$PWD:/code" bats/bats:latest /code/test
```
This Docker image includes libraries like [bats-support](https://github.com/bats-core/bats-support) and [bats-assert](https://github.com/bats-core/bats-assert), which can be loaded in `setup` like this:
```bash
setup() {
bats_load_library bats-support
bats_load_library bats-assert
}
```
## Basic Usage for Bats Project
To build and run `bats`' own tests:
```bash
$ git clone https://github.com/bats-core/bats-core.git
Cloning into 'bats-core'...
remote: Counting objects: 1222, done.
remote: Compressing objects: 100% (53/53), done.
remote: Total 1222 (delta 34), reused 55 (delta 21), pack-reused 1146
Receiving objects: 100% (1222/1222), 327.28 KiB | 1.70 MiB/s, done.
Resolving deltas: 100% (661/661), done.
$ cd bats-core/
$ docker build --tag bats/bats:latest .
...
$ docker run -it bats/bats:latest --formatter tap /opt/bats/test
```
To mount your tests into the container, first build the image as above. Then, for example with `bats`:
```bash
$ docker run -it -v "$PWD:/opt/bats" bats/bats:latest /opt/bats/test
```
This runs the `test/` directory from the bats-core repository inside the bats Docker container.
## Docker Gotchas
Relying on functionality provided by your environment (ssh keys or agent, installed binaries, fixtures outside the mounted test directory) will fail when running inside Docker.
`--interactive`/`-i` attaches an interactive terminal and is useful to kill hanging processes (otherwise has to be done via docker stop command). `--tty`/`-t` simulates a tty (often not used, but most similar to test runs from a Bash prompt). Interactivity is important to a user, but not a build, and TTYs are probably more important to a headless build. Everything's least-surprising to a new Docker use if both are used.
## Extending from the base image
Docker operates on a principle of isolation, and bundles all dependencies required into the Docker image. These can be mounted in at runtime (for test files, configuration, etc). For binary dependencies it may be better to extend the base Docker image with further tools and files.
```dockerfile
FROM bats/bats
RUN \
apk \
--no-cache \
--update \
add \
openssh
```
+172
View File
@@ -0,0 +1,172 @@
FAQ
===
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 might interfere with bats' internals.
How do I see the output of the command under `run` when a test fails?
---------------------------------------------------------------------
`run` captures stdout and stderr of its command and stores it in the `$output` and `${lines[@]}` variables.
If you want to see this output, you need to print it yourself, or use functions like `assert_output` that will reproduce it on failure.
Can I use `--filter` to exclude files/tests?
--------------------------------------------
No, not directly. `--filter` uses a regex to match against test names. So you could try to invert the regex.
The filename won't be part of the strings that are tested, so you cannot filter against files.
How can I exclude a single test from a test run?
------------------------------------------------
If you want to exclude only few tests from a run, you can either `skip` them:
.. code-block:: bash
@test "Testname" {
# yadayada
}
becomes
.. code-block:: bash
@test "Testname" {
skip 'Optional skip message'
# yadayada
}
or comment them out, e.g.:
.. code-block:: bash
@test "Testname" {
becomes
.. code-block:: bash
disabled() { # @test "Testname" {
For multiple tests or all tests of a file, this becomes tedious, so read on.
How can I exclude all tests of a file from a test run?
--------------------------------------------------------
If you run your test suite by naming individual files like:
.. code-block:: bash
$ bats test/a.bats test/b.bats ...
you can simply omit your file. When running a folder like
.. code-block:: bash
$ bats test/
you can prevent test files from being picked up by changing their extension to something other than `.bats`.
It is also possible to `skip` in `setup_file`/`setup` which will skip all tests in the file.
How can I include my own `.sh` files for testing?
-------------------------------------------------
You can simply `source <your>.sh` files. However, be aware that `source`ing files with errors outside of any function (or inside `setup_file`) will trip up bats
and lead to hard to diagnose errors.
Therefore, it is safest to only `source` inside `setup` or the test functions themselves.
How can I debug a failing test?
-------------------------------
Short of using a bash debugger you should make sure to use appropriate asserts for your task instead of raw bash comparisons, e.g.:
.. code-block:: bash
@test test {
run echo test failed
assert_output "test"
# instead of
[ "$output" = "test" ]
}
Because the former will print the output when the test fails while the latter won't.
Similarly, you should use `assert_success`/`assert_failure` instead of `[ "$status" -eq 0 ]` for return code checks.
Is there a mechanism to add file/test specific functionality to a common setup function?
----------------------------------------------------------------------------------------
Often the setup consists of parts that are common between different files of a test suite and parts that are specific to each file.
There is no suite wide setup functionality yet, so you should extract these common setup steps into their own file (e.g. `common-test-setup.sh`) and function (e.g. `commonSetup() {}`),
which can be `source`d or `load`ed and call it in `setup_file` or `setup`.
How can I use helper libraries like bats-assert?
------------------------------------------------
This is a short reproduction of https://github.com/ztombol/bats-docs.
At first, you should make sure the library is installed. This is usually done in the `test_helper/` folders alongside the `.bats` files, giving you a filesystem layout like this:
.. code-block::
test/
test.bats
test_helper/
bats-support/
bats-assert/
Next, you should load those helper libraries:
.. code-block:: bash
setup() {
load 'test_helper/bats-support/load' # this is required by bats-assert!
load 'test_helper/bats-assert/load'
}
Now, you should be able to use the functions from these helpers inside your tests, e.g.:
.. code-block:: bash
@test "test" {
run echo test
assert_output "test"
}
Note that you obviously need to load the library before using it.
If you need the library inside `setup_file` or `teardown_file` you need to load it in `setup_file`.
How to set a test timeout in bats?
----------------------------------
Set the variable `$BATS_TEST_TIMEOUT` before `setup()` starts. This means you can set it either on the command line,
in free code in the test file or in `setup_file()`.
How can I lint/shell-format my bats tests?
------------------------------------------
Due to their custom syntax (`@test`), `.bats` files are not standard bash. This prevents most tools from working with bats.
However, there is an alternative syntax `function_name { # @test` to declare tests in a bash compliant manner.
- shellcheck support since version 0.7
- shfmt support since version 3.2.0 (using `-ln bats`)
How can I check if a test failed/succeeded during teardown?
-----------------------------------------------------------
You can check `BATS_TEST_COMPLETED` which will be set to 1 if the test was successful or empty if it was not.
There is also `BATS_TEST_SKIPPED` which will be non-empty (contains the skip message or -1) when `skip` was called.
How can I setup/cleanup before/after all tests?
-----------------------------------------------
Setup/cleanup before/after all tests can be achieved using the special `setup_suite` and `teardown_suite` functions.
These functions must be placed into a dedicated `setup_suite.bash` file next to your `.bats` files.
For more information check out the :ref:`setup and teardown section <setup and teardown: pre- and post-test hooks>`.
@@ -0,0 +1,132 @@
Gotchas
=======
My test fails although I return true?
-------------------------------------
Using `return 1` to signify `true` for a success as is done often in other languages does not mesh well with Bash's
convention of using return code 0 to signify success and everything non-zero to indicate a failure.
Please adhere to this idiom while using bats, or you will constantly work against your environment.
My negated statement (e.g. ! true) does not fail the test, even when it should.
-------------------------------------------------------------------------------
Bash deliberately excludes negated return values from causing a pipeline to exit (see bash's `-e` option).
Use `run !` on Bats 1.5.0 and above. For older bats versions, use one of `! x || false` or `run` with `[ $status != 0 ]`.
If the negated command is the final statement in a test, that final statement's (negated) exit status will propagate through to the test's return code as usual.
Negated statements of one of the correct forms mentioned above will explicitly fail the test when the pipeline returns true, regardless of where they occur in the test.
I cannot register a test multiple times via for loop.
-----------------------------------------------------
The usual bats tests (`@test`) are preprocessed into functions.
Wrapping them into a for loop only redeclares this function.
If you are interested in registering multiple calls to the same function, contribute your wishes to issue `#306 <https://github.com/bats-core/bats-core/issues/306>`_.
I cannot pass parameters to test or .bats files.
------------------------------------------------
Especially while using bats via shebang:
.. code-block:: bash
#!/usr/bin/env bats
@test "test" {
# ...
}
You could be tempted to pass parameters to the test invocation like `./test.bats param1 param2`.
However, bats does not support passing parameters to files or tests.
If you need such a feature, please let us know about your usecase.
As a workaround you can use environment variables to pass parameters.
Why can't my function return results via a variable when using `run`?
---------------------------------------------------------------------
The `run` function executes its command in a subshell which means the changes to variables won't be available in the calling shell.
If you want to test these functions, you should call them without `run`.
`run` doesn't fail, although the same command without `run` does.
-----------------------------------------------------------------
`run` is a wrapper that always succeeds. The wrapped command's exit code is stored in `$status` and the stdout/stderr in `$output`.
If you want to fail the test, you should explicitly check `$status` or omit `run`. See also `when not to use run <writing-tests.html#when-not-to-use-run>`_.
`load` won't load my `.sh` files.
---------------------------------
`load` is intended as an internal helper function that always loads `.bash` files (by appending this suffix).
If you want to load an `.sh` file, you can simple `source` it.
I can't lint/shell-format my bats tests.
----------------------------------------
Bats uses a custom syntax for annotating tests (`@test`) that is not bash compliant.
Therefore, standard bash tooling won't be able to interact directly with `.bats` files.
Shellcheck supports bats' native syntax as of version 0.7.
Additionally, there is bash compatible syntax for tests:
.. code-block:: bash
function bash_compliant_function_name_as_test_name { # @test
# your code
}
The output (stdout/err) from commands under `run` is not visible in failed tests.
---------------------------------------------------------------------------------
By default, `run` only stores stdout/stderr in `$output` (and `${lines[@]}`).
If you want to see this output, you either should use bat-assert's assertions or have to print `$output` before the check that fails.
My piped command does not work under run.
-----------------------------------------
Be careful with using pipes and with `run`. While your mind model of `run` might wrap the whole command behind it, bash's parser won't
.. code-block:: bash
run echo foo | grep bar
Won't `run (echo foo | grep bar)` but will `(run echo foo) | grep bar`. If you need to incorporate pipes, you either should do
.. code-block:: bash
run bash -c 'echo foo | grep bar'
or use a function to wrap the pipe in:
.. code-block:: bash
fun_with_pipes() {
echo foo | grep bar
}
run fun_with_pipes
`[[ ]]` (or `(( ))` did not fail my test
----------------------------------------
The `set -e` handling of `[[ ]]` and `(( ))` changed in Bash 4.1. Older versions, like 3.2 on MacOS,
don't abort the test when they fail, unless they are the last command before the (test) function returns,
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).
@@ -0,0 +1,18 @@
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:
tutorial
installation
usage
docker-usage
writing-tests
gotchas
faq
warnings/index
support-matrix
@@ -0,0 +1,138 @@
Installation
============
Linux: Distribition Package Manager
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Following Linux distributions provide Bats via their package manager:
* Arch Linux: `extra/bats <https://archlinux.org/packages/extra/any/bats/>`__
* Alpine Linux: `bats <https://pkgs.alpinelinux.org/package/edge/main/x86/bats>`__
* Debian Linux: `shells/bats <https://packages.debian.org/search?keywords=bats>`__
* Fedora Linux: `rpms/bats <https://src.fedoraproject.org/rpms/bats>`__
* Gentoo Linux `dev-util/bats <https://packages.gentoo.org/packages/dev-util/bats>`__
* OpenSUSE Linux: `bats <https://software.opensuse.org/package/bats>`__
* Ubuntu Linux `shells/bats <https://packages.ubuntu.com/search?keywords=bats>`__
**Note**: Bats versions pre 1.0 are from sstephenson's original project.
Consider using one of the other installation methods below to get the latest Bats release.
The test matrix above only applies to the latest Bats version.
If your favorite distribution is not listed above,
you can try one of the following package managers or install from source.
MacOS: Homebrew
^^^^^^^^^^^^^^^
On macOS, you can install `Homebrew <https://brew.sh/>`__ if you haven't already,
then run:
.. code-block:: bash
$ brew install bats-core
Any OS: 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
Any OS: 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.
Windows: Installing Bats from source via 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,26 @@
Support Matrix
==============
Supported Bash versions
^^^^^^^^^^^^^^^^^^^^^^^
The following is a list of Bash versions that are currently supported by Bats and verified through automated tests:
* 3.2.57(1) (macOS's highest bundled version)
* 4.0, 4.1, 4.2, 4.3, 4.4
* 5.0, 5.1, 5.2
Supported Operating systems
^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following Operating Systems are supported and tested automatically (CI) or manually during development:
* Linux: Alpine (CI), Alma 8 (CI), Arch Linux (manual), Ubuntu 20.04/22.04 (CI)
* FreeBSD: 11 (CI)
* macOS: 11 (CI), 12 (CI)
* Windows: Server 2019 (CI), 10 (manual)
* Git for Windows Bash (MSYS2 based)
* Windows Subsystem for Linux
* MSYS2
* Cygwin
@@ -0,0 +1,661 @@
Tutorial
========
This tutorial is intended for beginners with bats and possibly bash.
Make sure to also read the list of gotchas and the faq.
For this tutorial we are assuming you already have a project in a git repository and want to add tests.
Ultimately they should run in the CI environment but will also be started locally during development.
..
TODO: link to example repository?
Quick installation
------------------
Since we already have an existing git repository, it is very easy to include bats and its libraries as submodules.
We are aiming for following filesystem structure:
.. code-block::
src/
project.sh
...
test/
bats/ <- submodule
test_helper/
bats-support/ <- submodule
bats-assert/ <- submodule
test.bats
...
So we start from the project root:
.. code-block:: console
git submodule add https://github.com/bats-core/bats-core.git test/bats
git submodule add https://github.com/bats-core/bats-support.git test/test_helper/bats-support
git submodule add https://github.com/bats-core/bats-assert.git test/test_helper/bats-assert
Your first test
---------------
Now we want to add our first test.
In the tutorial repository, we want to build up our project in a TDD fashion.
Thus, we start with an empty project and our first test is to just run our (nonexistent) shell script.
We start by creating a new test file `test/test.bats`
.. code-block:: bash
@test "can run our script" {
./project.sh
}
and run it by
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ can run our script
(in test file test/test.bats, line 2)
`./project.sh' failed with status 127
/tmp/bats-run-19605/bats.19627.src: line 2: ./project.sh: No such file or directory
1 test, 1 failure
Okay, our test is red. Obviously, the project.sh doesn't exist, so we create the file `src/project.sh`:
.. code-block:: console
mkdir src/
echo '#!/usr/bin/env bash' > src/project.sh
chmod a+x src/project.sh
A new test run gives us
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ can run our script
(in test file test/test.bats, line 2)
`./project.sh' failed with status 127
/tmp/bats-run-19605/bats.19627.src: line 2: ./project.sh: No such file or directory
1 test, 1 failure
Oh, we still used the wrong path. No problem, we just need to use the correct path to `project.sh`.
Since we're still in the same directory as when we started `bats`, we can simply do:
.. code-block:: bash
@test "can run our script" {
./src/project.sh
}
and get:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✓ can run our script
1 test, 0 failures
Yesss! But that victory feels shallow: What if somebody less competent than us starts bats from another directory?
Let's do some setup
-------------------
The obvious solution to becoming independent of `$PWD` is using some fixed anchor point in the filesystem.
We can use the path to the test file itself as an anchor and rely on the internal project structure.
Since we are lazy people and want to treat our project's files as first class citizens in the executable world, we will also put them on the `$PATH`.
Our new `test/test.bats` now looks like this:
.. code-block:: bash
setup() {
# get the containing directory of this file
# use $BATS_TEST_FILENAME instead of ${BASH_SOURCE[0]} or $0,
# as those will point to the bats executable's location or the preprocessed file respectively
DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
# make executables in src/ visible to PATH
PATH="$DIR/../src:$PATH"
}
@test "can run our script" {
# notice the missing ./
# As we added src/ to $PATH, we can omit the relative path to `src/project.sh`.
project.sh
}
still giving us:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✓ can run our script
1 test, 0 failures
It still works as expected. This is because the newly added `setup` function put the absolute path to `src/` onto `$PATH`.
This setup function is automatically called before each test.
Therefore, our test could execute `project.sh` directly, without using a (relative) path.
.. important::
The `setup` function will be called before each individual test in the file.
Each file can only define one setup function for all tests in the file.
However, the setup functions can differ between different files.
Dealing with output
-------------------
Okay, we have a green test but our executable does not do anything useful.
To keep things simple, let us start with an error message. Our new `src/project.sh` now reads:
.. code-block:: bash
#!/usr/bin/env bash
echo "Welcome to our project!"
echo "NOT IMPLEMENTED!" >&2
exit 1
And gives is this test output:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ can run our script
(in test file test/test.bats, line 11)
`project.sh' failed
Welcome to our project!
NOT IMPLEMENTED!
1 test, 1 failure
Okay, our test failed, because we now exit with 1 instead of 0.
Additionally, we see the stdout and stderr of the failing program.
Our goal now is to retarget our test and check that we get the welcome message.
bats-assert gives us some help with this, so we should now load it (and its dependency bats-support),
so we change `test/test.bats` to
.. code-block:: bash
setup() {
load 'test_helper/bats-support/load'
load 'test_helper/bats-assert/load'
# ... the remaining setup is unchanged
# get the containing directory of this file
# use $BATS_TEST_FILENAME instead of ${BASH_SOURCE[0]} or $0,
# as those will point to the bats executable's location or the preprocessed file respectively
DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
# make executables in src/ visible to PATH
PATH="$DIR/../src:$PATH"
}
@test "can run our script" {
run project.sh # notice `run`!
assert_output 'Welcome to our project!'
}
which gives us the following test output:
.. code-block:: console
$ LANG=C ./test/bats/bin/bats test/test.bats
✗ can run our script
(from function `assert_output' in file test/test_helper/bats-assert/src/assert_output.bash, line 194,
in test file test/test.bats, line 14)
`assert_output 'Welcome to our project!'' failed
-- output differs --
expected (1 lines):
Welcome to our project!
actual (2 lines):
Welcome to our project!
NOT IMPLEMENTED!
--
1 test, 1 failure
The first change in this output is the failure description. We now fail on assert_output instead of the call itself.
We prefixed our call to `project.sh` with `run`, which is a function provided by bats that executes the command it gets passed as parameters.
Then, `run` sucks up the stdout and stderr of the command it ran and stores it in `$output`, stores the exit code in `$status` and returns 0.
This means `run` never fails the test and won't generate any context/output in the log of a failed test on its own.
Marking the test as failed and printing context information is up to the consumers of `$status` and `$output`.
`assert_output` is such a consumer, it compares `$output` to the parameter it got and tells us quite succinctly that it did not match in this case.
For our current test we don't care about any other output or the error message, so we want it gone.
`grep` is always at our fingertips, so we tape together this ramshackle construct
.. code-block:: bash
run project.sh 2>&1 | grep Welcome
which gives us the following test result:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ can run our script
(in test file test/test.bats, line 13)
`run project.sh | grep Welcome' failed
1 test, 1 failure
Huh, what is going on? Why does it fail the `run` line again?
This is a common mistake that can happen when our mind parses the file differently than the bash parser.
`run` is just a function, so the pipe won't actually be forwarded into the function. Bash reads this as `(run project.sh) | grep Welcome`,
instead of our intended `run (project.sh | grep Welcome)`.
Unfortunately, the latter is not valid bash syntax, so we have to work around it, e.g. by using a function:
.. code-block:: bash
get_projectsh_welcome_message() {
project.sh 2>&1 | grep Welcome
}
@test "Check welcome message" {
run get_projectsh_welcome_message
assert_output 'Welcome to our project!'
}
Now our test passes again but having to write a function each time we want only a partial match does not accommodate our laziness.
Isn't there an app for that? Maybe we should look at the documentation?
Partial matching can be enabled with the --partial option (-p for short). When used, the assertion fails if the expected substring is not found in $output.
-- the documentation for `assert_output <https://github.com/bats-core/bats-assert#partial-matching>`_
Okay, so maybe we should try that:
.. code-block:: bash
@test "Check welcome message" {
run project.sh
assert_output --partial 'Welcome to our project!'
}
Aaannnd ... the test stays green. Yay!
There are many other asserts and options but this is not the place for all of them.
Skimming the documentation of `bats-assert <https://github.com/bats-core/bats-assert>`_ will give you a good idea what you can do.
You should also have a look at the other helper libraries `here <https://github.com/bats-core>`_ like `bats-file <https://github.com/bats-core/bats-file>`_,
to avoid reinventing the wheel.
Cleaning up your mess
---------------------
Often our setup or tests leave behind some artifacts that clutter our test environment.
You can define a `teardown` function which will be called after each test, regardless whether it failed or not.
For example, we now want our project.sh to only show the welcome message on the first invocation.
So we change our test to this:
.. code-block:: bash
@test "Show welcome message on first invocation" {
run project.sh
assert_output --partial 'Welcome to our project!'
run project.sh
refute_output --partial 'Welcome to our project!'
}
This test fails as expected:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ Show welcome message on first invocation
(from function `refute_output' in file test/test_helper/bats-assert/src/refute_output.bash, line 189,
in test file test/test.bats, line 17)
`refute_output --partial 'Welcome to our project!'' failed
-- output should not contain substring --
substring (1 lines):
Welcome to our project!
output (2 lines):
Welcome to our project!
NOT IMPLEMENTED!
--
1 test, 1 failure
Now, to get the test green again, we want to store the information that we already ran in the file `/tmp/bats-tutorial-project-ran`,
so our `src/project.sh` becomes:
.. code-block:: bash
#!/usr/bin/env bash
FIRST_RUN_FILE=/tmp/bats-tutorial-project-ran
if [[ ! -e "$FIRST_RUN_FILE" ]]; then
echo "Welcome to our project!"
touch "$FIRST_RUN_FILE"
fi
echo "NOT IMPLEMENTED!" >&2
exit 1
And our test says:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✓ Show welcome message on first invocation
1 test, 0 failures
Nice, we're done, or are we? Running the test again now gives:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
✗ Show welcome message on first invocation
(from function `assert_output' in file test/test_helper/bats-assert/src/assert_output.bash, line 186,
in test file test/test.bats, line 14)
`assert_output --partial 'Welcome to our project!'' failed
-- output does not contain substring --
substring : Welcome to our project!
output : NOT IMPLEMENTED!
--
1 test, 1 failure
Now the first assert failed, because of the leftover `$FIRST_RUN_FILE` from the last test run.
Luckily, bats offers the `teardown` function, which can take care of that, we add the following code to `test/test.bats`:
.. code-block:: bash
teardown() {
rm -f /tmp/bats-tutorial-project-ran
}
Now running the test again first give us the same error, as the teardown has not run yet.
On the second try we get a clean `/tmp` folder again and our test passes consistently now.
It is worth noting that we could do this `rm` in the test code itself but it would get skipped on failures.
.. important::
A test ends at its first failure. None of the subsequent commands in this test will be executed.
The `teardown` function runs after each individual test in a file, regardless of test success or failure.
Similarly to `setup`, each `.bats` file can have its own `teardown` function which will be the same for all tests in the file.
Test what you can
-----------------
Sometimes tests rely on the environment to provide infrastructure that is needed for the test.
If not all test environments provide this infrastructure but we still want to test on them,
it would be unhelpful to get errors on parts that are not testable.
Bats provides you with the `skip` command which can be used in `setup` and `test`.
.. tip::
You should `skip` as early as you know it does not make sense to continue.
In our example project we rewrite the welcome message test to `skip` instead of doing cleanup:
.. code-block:: bash
teardown() {
: # Look Ma! No cleanup!
}
@test "Show welcome message on first invocation" {
if [[ -e /tmp/bats-tutorial-project-ran ]]; then
skip 'The FIRST_RUN_FILE already exists'
fi
run project.sh
assert_output --partial 'Welcome to our project!'
run project.sh
refute_output --partial 'Welcome to our project!'
}
The first test run still works due to the cleanup from the last round. However, our second run gives us:
.. code-block:: console
$ ./test/bats/bin/bats test/test.bats
- Show welcome message on first invocation (skipped: The FIRST_RUN_FILE already exists)
1 test, 0 failures, 1 skipped
.. important::
Skipped tests won't fail a test suite and are counted separately.
No test command after `skip` will be executed. If an error occurs before `skip`, the test will fail.
An optional reason can be passed to `skip` and will be printed in the test output.
Setting up a multifile test suite
---------------------------------
With a growing project, putting all tests into one file becomes unwieldy.
For our example project, we will extract functionality into the additional file `src/helper.sh`:
.. code-block:: bash
#!/usr/bin/env bash
_is_first_run() {
local FIRST_RUN_FILE=/tmp/bats-tutorial-project-ran
if [[ ! -e "$FIRST_RUN_FILE" ]]; then
touch "$FIRST_RUN_FILE"
return 0
fi
return 1
}
This allows for testing it separately in a new file `test/helper.bats`:
.. code-block:: bash
setup() {
load 'test_helper/common-setup'
_common_setup
source "$PROJECT_ROOT/src/helper.sh"
}
teardown() {
rm -f "$NON_EXISTANT_FIRST_RUN_FILE"
rm -f "$EXISTING_FIRST_RUN_FILE"
}
@test "Check first run" {
NON_EXISTANT_FIRST_RUN_FILE=$(mktemp -u) # only create the name, not the file itself
assert _is_first_run
refute _is_first_run
refute _is_first_run
EXISTING_FIRST_RUN_FILE=$(mktemp)
refute _is_first_run
refute _is_first_run
}
Since the setup function would have duplicated much of the other files', we split that out into the file `test/test_helper/common-setup.bash`:
.. code-block:: bash
#!/usr/bin/env bash
_common_setup() {
load 'test_helper/bats-support/load'
load 'test_helper/bats-assert/load'
# get the containing directory of this file
# use $BATS_TEST_FILENAME instead of ${BASH_SOURCE[0]} or $0,
# as those will point to the bats executable's location or the preprocessed file respectively
PROJECT_ROOT="$( cd "$( dirname "$BATS_TEST_FILENAME" )/.." >/dev/null 2>&1 && pwd )"
# make executables in src/ visible to PATH
PATH="$PROJECT_ROOT/src:$PATH"
}
with the following `setup` in `test/test.bats`:
.. code-block:: bash
setup() {
load 'test_helper/common-setup'
_common_setup
}
Please note, that we gave our helper the extension `.bash`, which is automatically appended by `load`.
.. important::
`load` automatically tries to append `.bash` to its argument.
In our new `test/helper.bats` we can see, that loading `.sh` is simply done via `source`.
.. tip::
Avoid using `load` and `source` outside of any functions.
If there is an error in the test file's "free code", the diagnostics are much worse than for code in `setup` or `@test`.
With the new changes in place, we can run our tests again. However, our previous run command does not include the new file.
You could add the new file to the parameter list, e.g. by running `./test/bats/bin/bats test/*.bats`.
However, bats also can handle directories:
.. code-block:: console
$ ./test/bats/bin/bats test/
✓ Check first run
- Show welcome message on first invocation (skipped: The FIRST_RUN_FILE already exists)
2 tests, 0 failures, 1 skipped
In this mode, bats will pick up all `.bats` files in the directory it was given. There is an additional `-r` switch that will recursively search for more `.bats` files.
However, in our project layout this would pick up the test files of bats itself from `test/bats/test`. We don't have test subfolders anyways, so we can do without `-r`.
Avoiding costly repeated setups
-------------------------------
We already have seen the `setup` function in use, which is called before each test.
Sometimes our setup is very costly, such as booting up a service just for testing.
If we can reuse the same setup across multiple tests, we might want to do only one setup before all these tests.
This usecase is exactly what the `setup_file` function was created for.
It can be defined per file and will run before all tests of the respective file.
Similarly, we have `teardown_file`, which will run after all tests of the file, even when you abort a test run or a test failed.
As an example, we want to add an echo server capability to our project. First, we add the following `server.bats` to our suite:
.. code-block:: bash
setup_file() {
load 'test_helper/common-setup'
_common_setup
PORT=$(project.sh start-echo-server 2>&1 >/dev/null)
export PORT
}
@test "server is reachable" {
nc -z localhost "$PORT"
}
Which will obviously fail:
Note that `export PORT` to make it visible to the test!
Running this gives us:
..
TODO: Update this example with fixed test name reporting from setup_file? (instead of "✗ ")
.. code-block:: console
$ ./test/bats/bin/bats test/server.bats
(from function `setup_file' in test file test/server.bats, line 4)
`PORT=$(project.sh start-echo-server >/dev/null 2>&1)' failed
1 test, 1 failure
Now that we got our red test, we need to get it green again.
Our new `project.sh` now ends with:
.. code-block:: bash
case $1 in
start-echo-server)
echo "Starting echo server"
PORT=2000
ncat -l $PORT -k -c 'xargs -n1 echo' 2>/dev/null & # don't keep open this script's stderr
echo $! > /tmp/project-echo-server.pid
echo "$PORT" >&2
;;
*)
echo "NOT IMPLEMENTED!" >&2
exit 1
;;
esac
and the tests now say
.. code-block:: console
$ LANG=C ./test/bats/bin/bats test/server.bats
✓ server is reachable
1 test, 0 failures
However, running this a second time gives:
.. code-block:: console
$ ./test/bats/bin/bats test/server.bats
✗ server is reachable
(in test file test/server.bats, line 14)
`nc -z -w 2 localhost "$PORT"' failed
2000
Ncat: bind to :::2000: Address already in use. QUITTING.
nc: port number invalid: 2000
Ncat: bind to :::2000: Address already in use. QUITTING.
1 test, 1 failure
Obviously, we did not turn off our server after testing.
This is a task for `teardown_file` in `server.bats`:
.. code-block:: bash
teardown_file() {
project.sh stop-echo-server
}
Our `project.sh` should also get the new command:
.. code-block:: bash
stop-echo-server)
kill "$(< "/tmp/project-echo-server.pid")"
rm /tmp/project-echo-server.pid
;;
Now starting our tests again will overwrite the .pid file with the new instance's, so we have to do manual cleanup once.
From now on, our test should clean up after itself.
.. note::
`teardown_file` will run regardless of tests failing or succeeding.
+114
View File
@@ -0,0 +1,114 @@
# 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-format].
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
```
If you have your own formatter, you can use an absolute path to the executable
to use it:
```bash
$ bats --formatter /absolute/path/to/my-formatter addition.bats
addition using bc WORKED
addition using dc FAILED
```
You can also generate test report files via `--report-formatter` which accepts
the same options as `--formatter`. By default, the file is stored in the current
workdir. However, it may be placed elsewhere by specifying the `--output` flag.
```text
$ bats --report-formatter junit addition.bats --output /tmp
1..2
ok 1 addition using bc
ok 2 addition using dc
$ cat /tmp/report.xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuites time="0.073">
<testsuite name="addition.bats" tests="2" failures="0" errors="0" skipped="0">
<testcase classname="addition.bats" name="addition using bc" time="0.034" />
<testcase classname="addition.bats" name="addition using dc" time="0.039" />
</testsuite>
</testsuites>
```
## 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 parallelised 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 fine-grained 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.
[tap-format]: https://testanything.org
[gnu-parallel]: https://www.gnu.org/software/parallel/

Some files were not shown because too many files have changed in this diff Show More