Full Code of ztombol/bats-assert for AI

master 9f88b4207da7 cached
18 files
82.0 KB
24.6k tokens
1 requests
Download .txt
Repository: ztombol/bats-assert
Branch: master
Commit: 9f88b4207da7
Files: 18
Total size: 82.0 KB

Directory structure:
gitextract_l4osbfqa/

├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── load.bash
├── package.json
├── script/
│   └── install-bats.sh
├── src/
│   └── assert.bash
└── test/
    ├── 50-assert-11-assert.bats
    ├── 50-assert-12-assert_equal.bats
    ├── 50-assert-13-assert_success.bats
    ├── 50-assert-14-assert_failure.bats
    ├── 50-assert-15-assert_output.bats
    ├── 50-assert-16-refute_output.bats
    ├── 50-assert-17-assert_line.bats
    ├── 50-assert-18-refute_line.bats
    ├── 50-assert-19-refute.bats
    └── test_helper.bash

================================================
FILE CONTENTS
================================================

================================================
FILE: .travis.yml
================================================
language: bash
before_install:
  - ./script/install-bats.sh
  - git clone --depth 1 https://github.com/ztombol/bats-support ../bats-support
before_script:
  - export PATH="${HOME}/.local/bin:${PATH}"
script:
  - bats test


================================================
FILE: CHANGELOG.md
================================================
# Change Log

All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).


## [0.3.0] - 2016-03-22

### Removed

- Move `fail()` to `bats-support`


## [0.2.0] - 2016-03-11

### Added

- `refute()` to complement `assert()`
- `npm` support

### Fixed

- Not consuming the `--` when stopping option parsing in
  `assert_output`, `refute_output`, `assert_line` and `refute_line`


## 0.1.0 - 2016-02-16

### Added

- Reporting arbitrary failures with `fail()`
- Generic assertions with `assert()` and `assert_equal()`
- Testing exit status with `assert_success()` and `assert_failure()`
- Testing output with `assert_output()` and `refute_output()`
- Testing individual lines with `assert_line()` and `refute_line()`


[0.3.0]: https://github.com/ztombol/bats-assert/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/ztombol/bats-assert/compare/v0.1.0...v0.2.0


================================================
FILE: LICENSE
================================================
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/>


================================================
FILE: README.md
================================================
# bats-assert

[![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/ztombol/bats-assert/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/ztombol/bats-assert.svg)](https://github.com/ztombol/bats-assert/releases/latest)
[![Build Status](https://travis-ci.org/ztombol/bats-assert.svg?branch=master)](https://travis-ci.org/ztombol/bats-assert)

`bats-assert` is a helper library providing common assertions for
[Bats][bats].

Assertions are functions that perform a test and output relevant
information on failure to help debugging. They return 1 on failure and 0
otherwise. Output, [formatted][bats-support-output] for readability, is
sent to the standard error to make assertions usable outside of `@test`
blocks too.

Assertions testing exit code and output operate on the results of the
most recent invocation of `run`.

Dependencies:
- [`bats-support`][bats-support] (formerly `bats-core`) - output
  formatting

See the [shared documentation][bats-docs] to learn how to install and
load this library.


## Usage

### `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()' {
  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 ]
--
```


### `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()' {
  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 ]
--
```


### `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_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'
}
```

The expected output can be specified with a heredoc or standard input as well.

```bash
@test 'assert_output() with pipe' {
  run echo 'have'
  echo 'want' | assert_output
}
```

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.

#### 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.


### `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'
}
```

-The unexpected output can be specified with a heredoc or standard input as well.

```bash
@test 'refute_output() with pipe' {
  run echo 'want'
  echo 'want' | refute_output
}
```

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.

#### 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.


### `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.


## 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 -- '--'
```


<!-- REFERENCES -->

[bats]: https://github.com/sstephenson/bats
[bats-support-output]: https://github.com/ztombol/bats-support#output-formatting
[bats-support]: https://github.com/ztombol/bats-support
[bats-docs]: https://github.com/ztombol/bats-docs
[bash-comp-cmd]: https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands


================================================
FILE: load.bash
================================================
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"


================================================
FILE: package.json
================================================
{
  "name": "bats-assert",
  "version": "0.3.0",
  "private": true,
  "peerDependencies": {
    "bats-support": "git+https://github.com/ztombol/bats-support.git#v0.2.0"
  }
}


================================================
FILE: script/install-bats.sh
================================================
#!/bin/sh
set -o errexit
set -o xtrace

git clone --depth 1 https://github.com/sstephenson/bats
cd bats && ./install.sh "${HOME}/.local" && cd .. && rm -rf bats


================================================
FILE: src/assert.bash
================================================
#
# 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/>.
#

#
# assert.bash
# -----------
#
# 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.
#

# Fail and display the expression if it evaluates to false.
#
# NOTE: The expression must be a simple command. Compound commands, such
#       as `[[', can be used only when executed with `bash -c'.
#
# Globals:
#   none
# Arguments:
#   $1 - expression
# Returns:
#   0 - expression evaluates to TRUE
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
assert() {
  if ! "$@"; then
    batslib_print_kv_single 10 'expression' "$*" \
      | batslib_decorate 'assertion failed' \
      | fail
  fi
}

# Fail and display the expression if it evaluates to true.
#
# NOTE: The expression must be a simple command. Compound commands, such
#       as `[[', can be used only when executed with `bash -c'.
#
# Globals:
#   none
# Arguments:
#   $1 - expression
# Returns:
#   0 - expression evaluates to FALSE
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
refute() {
  if "$@"; then
    batslib_print_kv_single 10 'expression' "$*" \
      | batslib_decorate 'assertion succeeded, but it was expected to fail' \
      | fail
  fi
}

# Fail and display details if the expected and actual values do not
# equal. Details include both values.
#
# Globals:
#   none
# Arguments:
#   $1 - actual value
#   $2 - expected value
# Returns:
#   0 - values equal
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
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
}

# Fail and display details if `$status' is not 0. Details include
# `$status' and `$output'.
#
# Globals:
#   status
#   output
# Arguments:
#   none
# Returns:
#   0 - `$status' is 0
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
assert_success() {
  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
}

# Fail and display details if `$status' is 0. Details include `$output'.
#
# Optionally, when the expected status is specified, fail when it does
# not equal `$status'. In this case, details include the expected and
# actual status, and `$output'.
#
# Globals:
#   status
#   output
# Arguments:
#   $1 - [opt] expected status
# Returns:
#   0 - `$status' is not 0, or
#       `$status' equals the expected status
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
assert_failure() {
  (( $# > 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
}

# Fail and display details if `$output' does not match the expected
# output. The expected output can be specified either by the first
# parameter or on the standard input.
#
# By default, literal matching is performed. The assertion fails if the
# expected output does not equal `$output'. Details include both values.
#
# Option `--partial' enables partial matching. The assertion fails if
# the expected substring cannot be found in `$output'.
#
# Option `--regexp' enables regular expression matching. The assertion
# fails if the extended regular expression does not match `$output'. An
# invalid regular expression causes an error to be displayed.
#
# It is an error to use partial and regular expression matching
# simultaneously.
#
# Globals:
#   output
# Options:
#   -p, --partial - partial matching
#   -e, --regexp - extended regular expression matching
# Arguments:
#   $1 - [=STDIN] expected output
# Returns:
#   0 - expected matches the actual output
#   1 - otherwise
# Inputs:
#   STDIN - [=$1] expected output
# Outputs:
#   STDERR - details, on failure
#            error message, on error
assert_output() {
  local -i is_mode_partial=0
  local -i is_mode_regexp=0

  # Handle options.
  while (( $# > 0 )); do
    case "$1" in
      -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_output' \
      | fail
    return $?
  fi

  # Arguments.
  local expected
  (( $# == 0 )) && expected="$(cat -)" || expected="$1"

  # Matching.
  if (( is_mode_regexp )); then
    if [[ '' =~ $expected ]] || (( $? == 2 )); then
      echo "Invalid extended regular expression: \`$expected'" \
        | batslib_decorate 'ERROR: assert_output' \
        | fail
      return $?
    fi
    if ! [[ $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
}

# Fail and display details if `$output' matches the unexpected output.
# The unexpected output can be specified either by the first parameter
# or on the standard input.
#
# By default, literal matching is performed. The assertion fails if the
# unexpected output equals `$output'. Details include `$output'.
#
# Option `--partial' enables partial matching. The assertion fails if
# the unexpected substring is found in `$output'. The unexpected
# substring is added to details.
#
# Option `--regexp' enables regular expression matching. The assertion
# fails if the extended regular expression does matches `$output'. The
# regular expression is added to details. An invalid regular expression
# causes an error to be displayed.
#
# It is an error to use partial and regular expression matching
# simultaneously.
#
# Globals:
#   output
# Options:
#   -p, --partial - partial matching
#   -e, --regexp - extended regular expression matching
# Arguments:
#   $1 - [=STDIN] unexpected output
# Returns:
#   0 - unexpected matches the actual output
#   1 - otherwise
# Inputs:
#   STDIN - [=$1] unexpected output
# Outputs:
#   STDERR - details, on failure
#            error message, on error
refute_output() {
  local -i is_mode_partial=0
  local -i is_mode_regexp=0

  # Handle options.
  while (( $# > 0 )); do
    case "$1" in
      -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_output' \
      | fail
    return $?
  fi

  # Arguments.
  local unexpected
  (( $# == 0 )) && unexpected="$(cat -)" || unexpected="$1"

  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_regexp )); then
    if [[ $output =~ $unexpected ]] || (( $? == 0 )); 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
}

# Fail and display details if the expected line is not found in the
# output (default) or in a specific line of it.
#
# By default, the entire output is searched for the expected line. The
# expected line is matched against every element of `${lines[@]}'. If no
# match is found, the assertion fails. Details include the expected line
# and `${lines[@]}'.
#
# When `--index <idx>' is specified, only the <idx>-th line is matched.
# If the expected line does not match `${lines[<idx>]}', the assertion
# fails. Details include <idx> and the compared lines.
#
# By default, literal matching is performed. A literal match fails if
# the expected string does not equal the matched string.
#
# Option `--partial' enables partial matching. A partial match fails if
# the expected substring is not found in the target string.
#
# Option `--regexp' enables regular expression matching. A regular
# expression match fails if the extended regular expression does not
# match the target string. An invalid regular expression causes an error
# to be displayed.
#
# It is an error to use partial and regular expression matching
# simultaneously.
#
# Mandatory arguments to long options are mandatory for short options
# too.
#
# Globals:
#   output
#   lines
# Options:
#   -n, --index <idx> - match the <idx>-th line
#   -p, --partial - partial matching
#   -e, --regexp - extended regular expression matching
# Arguments:
#   $1 - expected line
# Returns:
#   0 - match found
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
#            error message, on error
# 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

  # 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
}

# Fail and display details if the unexpected line is found in the output
# (default) or in a specific line of it.
#
# By default, the entire output is searched for the unexpected line. The
# unexpected line is matched against every element of `${lines[@]}'. If
# a match is found, the assertion fails. Details include the unexpected
# line, the index of the first match and `${lines[@]}' with the matching
# line highlighted if `${lines[@]}' is longer than one line.
#
# When `--index <idx>' is specified, only the <idx>-th line is matched.
# If the unexpected line matches `${lines[<idx>]}', the assertion fails.
# Details include <idx> and the unexpected line.
#
# By default, literal matching is performed. A literal match fails if
# the unexpected string does not equal the matched string.
#
# Option `--partial' enables partial matching. A partial match fails if
# the unexpected substring is found in the target string. When used with
# `--index <idx>', the unexpected substring is also displayed on
# failure.
#
# Option `--regexp' enables regular expression matching. A regular
# expression match fails if the extended regular expression matches the
# target string. When used with `--index <idx>', the regular expression
# is also displayed on failure. An invalid regular expression causes an
# error to be displayed.
#
# It is an error to use partial and regular expression matching
# simultaneously.
#
# Mandatory arguments to long options are mandatory for short options
# too.
#
# Globals:
#   output
#   lines
# Options:
#   -n, --index <idx> - match the <idx>-th line
#   -p, --partial - partial matching
#   -e, --regexp - extended regular expression matching
# Arguments:
#   $1 - unexpected line
# Returns:
#   0 - match not found
#   1 - otherwise
# Outputs:
#   STDERR - details, on failure
#            error message, on error
# 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

  # 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 ]] || (( $? == 0 )); 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
}


================================================
FILE: test/50-assert-11-assert.bats
================================================
#!/usr/bin/env bats

load test_helper

@test 'assert() <expression>: returns 0 if <expression> evaluates to TRUE' {
  run assert true
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert() <expression>: returns 1 and displays <expression> if it evaluates to FALSE' {
  run assert false
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- assertion failed --' ]
  [ "${lines[1]}" == 'expression : false' ]
  [ "${lines[2]}" == '--' ]
}


================================================
FILE: test/50-assert-12-assert_equal.bats
================================================
#!/usr/bin/env bats

load test_helper

@test 'assert_equal() <actual> <expected>: returns 0 if <actual> equals <expected>' {
  run assert_equal 'a' 'a'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_equal() <actual> <expected>: returns 1 and displays details if <actual> does not equal <expected>' {
  run assert_equal 'a' 'b'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- values do not equal --' ]
  [ "${lines[1]}" == 'expected : b' ]
  [ "${lines[2]}" == 'actual   : a' ]
  [ "${lines[3]}" == '--' ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- values do not equal --' ]
  [ "${lines[1]}" == 'expected (1 lines):' ]
  [ "${lines[2]}" == '  b' ]
  [ "${lines[3]}" == 'actual (2 lines):' ]
  [ "${lines[4]}" == '  a 0' ]
  [ "${lines[5]}" == '  a 1' ]
  [ "${lines[6]}" == '--' ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- values do not equal --' ]
  [ "${lines[1]}" == 'expected (2 lines):' ]
  [ "${lines[2]}" == '  b 0' ]
  [ "${lines[3]}" == '  b 1' ]
  [ "${lines[4]}" == 'actual (1 lines):' ]
  [ "${lines[5]}" == '  a' ]
  [ "${lines[6]}" == '--' ]
}

@test 'assert_equal() <actual> <expected>: performs literal matching' {
  run assert_equal 'a' '*'
  [ "$status" -eq 1 ]
}


================================================
FILE: test/50-assert-13-assert_success.bats
================================================
#!/usr/bin/env bats

load test_helper

@test "assert_success(): returns 0 if \`\$status' is 0" {
  run true
  run assert_success
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "assert_success(): returns 1 and displays details if \`\$status' is not 0" {
  run bash -c 'echo "a"
               exit 1'
  run assert_success
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- command failed --' ]
  [ "${lines[1]}" == 'status : 1' ]
  [ "${lines[2]}" == 'output : a' ]
  [ "${lines[3]}" == '--' ]
}

@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
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 6 ]
  [ "${lines[0]}" == '-- command failed --' ]
  [ "${lines[1]}" == 'status : 1' ]
  [ "${lines[2]}" == 'output (2 lines):' ]
  [ "${lines[3]}" == '  a 0' ]
  [ "${lines[4]}" == '  a 1' ]
  [ "${lines[5]}" == '--' ]
}


================================================
FILE: test/50-assert-14-assert_failure.bats
================================================
#!/usr/bin/env bats

load test_helper

@test "assert_failure(): returns 0 if \`\$status' is not 0" {
  run false
  run assert_failure
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "assert_failure(): returns 1 and displays details if \`\$status' is 0" {
  run bash -c 'echo "a"
               exit 0'
  run assert_failure
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ]
  [ "${lines[1]}" == 'output : a' ]
  [ "${lines[2]}" == '--' ]
}

@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
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ]
  [ "${lines[1]}" == 'output (2 lines):' ]
  [ "${lines[2]}" == '  a 0' ]
  [ "${lines[3]}" == '  a 1' ]
  [ "${lines[4]}" == '--' ]
}

@test "assert_failure() <status>: returns 0 if \`\$status' equals <status>" {
  run bash -c 'exit 1'
  run assert_failure 1
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- command failed as expected, but status differs --' ]
  [ "${lines[1]}" == 'expected : 2' ]
  [ "${lines[2]}" == 'actual   : 1' ]
  [ "${lines[3]}" == 'output   : a' ]
  [ "${lines[4]}" == '--' ]
}

@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
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- command failed as expected, but status differs --' ]
  [ "${lines[1]}" == 'expected : 2' ]
  [ "${lines[2]}" == 'actual   : 1' ]
  [ "${lines[3]}" == 'output (2 lines):' ]
  [ "${lines[4]}" == '  a 0' ]
  [ "${lines[5]}" == '  a 1' ]
  [ "${lines[6]}" == '--' ]
}


================================================
FILE: test/50-assert-15-assert_output.bats
================================================
#!/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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "assert_output() <expected>: returns 1 and displays details if <expected> does not equal \`\$output'" {
  run echo 'b'
  run assert_output 'a'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- output differs --' ]
  [ "${lines[1]}" == 'expected : a' ]
  [ "${lines[2]}" == 'actual   : b' ]
  [ "${lines[3]}" == '--' ]
}

@test 'assert_output(): reads <expected> from STDIN' {
  run echo 'a'
  run assert_output <<STDIN
a
STDIN
echo "$output"
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- output differs --' ]
  [ "${lines[1]}" == 'expected (1 lines):' ]
  [ "${lines[2]}" == '  a' ]
  [ "${lines[3]}" == 'actual (2 lines):' ]
  [ "${lines[4]}" == '  b 0' ]
  [ "${lines[5]}" == '  b 1' ]
  [ "${lines[6]}" == '--' ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- output differs --' ]
  [ "${lines[1]}" == 'expected (2 lines):' ]
  [ "${lines[2]}" == '  a 0' ]
  [ "${lines[3]}" == '  a 1' ]
  [ "${lines[4]}" == 'actual (1 lines):' ]
  [ "${lines[5]}" == '  b' ]
  [ "${lines[6]}" == '--' ]
}

# Options
@test 'assert_output() <expected>: performs literal matching by default' {
  run echo 'a'
  run assert_output '*'
  [ "$status" -eq 1 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_p_partial () {
  run echo 'abc'
  run assert_output "$1" 'b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_output() -p <partial>: enables partial matching' {
  test_p_partial -p
}

@test 'assert_output() --partial <partial>: enables partial matching' {
  test_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- output does not contain substring --' ]
  [ "${lines[1]}" == 'substring : a' ]
  [ "${lines[2]}" == 'output    : b' ]
  [ "${lines[3]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- output does not contain substring --' ]
  [ "${lines[1]}" == 'substring (1 lines):' ]
  [ "${lines[2]}" == '  a' ]
  [ "${lines[3]}" == 'output (2 lines):' ]
  [ "${lines[4]}" == '  b 0' ]
  [ "${lines[5]}" == '  b 1' ]
  [ "${lines[6]}" == '--' ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- output does not contain substring --' ]
  [ "${lines[1]}" == 'substring (2 lines):' ]
  [ "${lines[2]}" == '  a 0' ]
  [ "${lines[3]}" == '  a 1' ]
  [ "${lines[4]}" == 'output (1 lines):' ]
  [ "${lines[5]}" == '  b' ]
  [ "${lines[6]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_r_regexp () {
  run echo 'abc'
  run assert_output "$1" '^a'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_output() -e <regexp>: enables regular expression matching' {
  test_r_regexp -e
}

@test 'assert_output() --regexp <regexp>: enables regular expression matching' {
  test_r_regexp --regexp
}

# Correctness
@test "assert_output() --regexp <regexp>: returns 0 if <regexp> matches \`\$output'" {
  run printf 'a\nb\nc'
  run assert_output --regexp '.*b.*'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "assert_output() --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\$output'" {
  run echo 'b'
  run assert_output --regexp '.*a.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- regular expression does not match output --' ]
  [ "${lines[1]}" == 'regexp : .*a.*' ]
  [ "${lines[2]}" == 'output : b' ]
  [ "${lines[3]}" == '--' ]
}

# 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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- regular expression does not match output --' ]
  [ "${lines[1]}" == 'regexp (1 lines):' ]
  [ "${lines[2]}" == '  .*a.*' ]
  [ "${lines[3]}" == 'output (2 lines):' ]
  [ "${lines[4]}" == '  b 0' ]
  [ "${lines[5]}" == '  b 1' ]
  [ "${lines[6]}" == '--' ]
}

@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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- regular expression does not match output --' ]
  [ "${lines[1]}" == 'regexp (2 lines):' ]
  [ "${lines[2]}" == '  .*a' ]
  [ "${lines[3]}" == '  b.*' ]
  [ "${lines[4]}" == 'output (1 lines):' ]
  [ "${lines[5]}" == '  b' ]
  [ "${lines[6]}" == '--' ]
}

# 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 '[.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: assert_output --' ]
  [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ]
  [ "${lines[2]}" == '--' ]
}


#
# Common
#

@test "assert_output(): \`--partial' and \`--regexp' are mutually exclusive" {
  run assert_output --partial --regexp
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: assert_output --' ]
  [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ]
  [ "${lines[2]}" == '--' ]
}

@test "assert_output(): \`--' stops parsing options" {
  run echo '-p'
  run assert_output -- '-p'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}


================================================
FILE: test/50-assert-16-refute_output.bats
================================================
#!/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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "refute_output() <unexpected>: returns 1 and displays details if <unexpected> equals \`\$output'" {
  run echo 'a'
  run refute_output 'a'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ]
  [ "${lines[1]}" == 'output : a' ]
  [ "${lines[2]}" == '--' ]
}

@test 'refute_output(): reads <unexpected> from STDIN' {
  run echo 'a'
  run refute_output <<INPUT
b
INPUT
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ]
  [ "${lines[1]}" == 'output (2 lines):' ]
  [ "${lines[2]}" == '  a 0' ]
  [ "${lines[3]}" == '  a 1' ]
  [ "${lines[4]}" == '--' ]
}

# Options
@test 'refute_output() <unexpected>: performs literal matching by default' {
  run echo 'a'
  run refute_output '*'
  [ "$status" -eq 0 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_p_partial () {
  run echo 'abc'
  run refute_output "$1" 'd'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_output() -p <partial>: enables partial matching' {
  test_p_partial -p
}

@test 'refute_output() --partial <partial>: enables partial matching' {
  test_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- output should not contain substring --' ]
  [ "${lines[1]}" == 'substring : a' ]
  [ "${lines[2]}" == 'output    : a' ]
  [ "${lines[3]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- output should not contain substring --' ]
  [ "${lines[1]}" == 'substring (1 lines):' ]
  [ "${lines[2]}" == '  a' ]
  [ "${lines[3]}" == 'output (2 lines):' ]
  [ "${lines[4]}" == '  a 0' ]
  [ "${lines[5]}" == '  a 1' ]
  [ "${lines[6]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_r_regexp () {
  run echo 'abc'
  run refute_output "$1" '^d'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_output() -e <regexp>: enables regular expression matching' {
  test_r_regexp -e
}

@test 'refute_output() --regexp <regexp>: enables regular expression matching' {
  test_r_regexp --regexp
}

# Correctness
@test "refute_output() --regexp <regexp>: returns 0 if <regexp> does not match \`\$output'" {
  run printf 'a\nb\nc'
  run refute_output --regexp '.*d.*'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "refute_output() --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\$output'" {
  run echo 'a'
  run refute_output --regexp '.*a.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- regular expression should not match output --' ]
  [ "${lines[1]}" == 'regexp : .*a.*' ]
  [ "${lines[2]}" == 'output : a' ]
  [ "${lines[3]}" == '--' ]
}

# 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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 7 ]
  [ "${lines[0]}" == '-- regular expression should not match output --' ]
  [ "${lines[1]}" == 'regexp (1 lines):' ]
  [ "${lines[2]}" == '  .*a.*' ]
  [ "${lines[3]}" == 'output (2 lines):' ]
  [ "${lines[4]}" == '  a 0' ]
  [ "${lines[5]}" == '  a 1' ]
  [ "${lines[6]}" == '--' ]
}

# 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 '[.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: refute_output --' ]
  [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ]
  [ "${lines[2]}" == '--' ]
}


#
# Common
#

@test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" {
  run refute_output --partial --regexp
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: refute_output --' ]
  [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ]
  [ "${lines[2]}" == '--' ]
}

@test "refute_output(): \`--' stops parsing options" {
  run echo '--'
  run refute_output -- '-p'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}


================================================
FILE: test/50-assert-17-assert_line.bats
================================================
#!/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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "assert_line() <expected>: returns 1 and displays details if <expected> is not a line in \`\${lines[@]}'" {
  run echo 'b'
  run assert_line 'a'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- output does not contain line --' ]
  [ "${lines[1]}" == 'line   : a' ]
  [ "${lines[2]}" == 'output : b' ]
  [ "${lines[3]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 6 ]
  [ "${lines[0]}" == '-- output does not contain line --' ]
  [ "${lines[1]}" == 'line : a' ]
  [ "${lines[2]}" == 'output (2 lines):' ]
  [ "${lines[3]}" == '  b 0' ]
  [ "${lines[4]}" == '  b 1' ]
  [ "${lines[5]}" == '--' ]
}

# Options
@test 'assert_line() <expected>: performs literal matching by default' {
  run echo 'a'
  run assert_line '*'
  [ "$status" -eq 1 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_p_partial () {
  run printf 'a\n_b_\nc'
  run assert_line "$1" 'b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_line() -p <partial>: enables partial matching' {
  test_p_partial -p
}

@test 'assert_line() --partial <partial>: enables partial matching' {
  test_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- no output line contains substring --' ]
  [ "${lines[1]}" == 'substring : a' ]
  [ "${lines[2]}" == 'output    : b' ]
  [ "${lines[3]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 6 ]
  [ "${lines[0]}" == '-- no output line contains substring --' ]
  [ "${lines[1]}" == 'substring : a' ]
  [ "${lines[2]}" == 'output (2 lines):' ]
  [ "${lines[3]}" == '  b 0' ]
  [ "${lines[4]}" == '  b 1' ]
  [ "${lines[5]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_r_regexp () {
  run printf 'a\n_b_\nc'
  run assert_line "$1" '^.b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_line() -e <regexp>: enables regular expression matching' {
  test_r_regexp -e
}

@test 'assert_line() --regexp <regexp>: enables regular expression matching' {
  test_r_regexp --regexp
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- no output line matches regular expression --' ]
  [ "${lines[1]}" == 'regexp : ^.a' ]
  [ "${lines[2]}" == 'output : b' ]
  [ "${lines[3]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 6 ]
  [ "${lines[0]}" == '-- no output line matches regular expression --' ]
  [ "${lines[1]}" == 'regexp : ^.a' ]
  [ "${lines[2]}" == 'output (2 lines):' ]
  [ "${lines[3]}" == '  b 0' ]
  [ "${lines[4]}" == '  b 1' ]
  [ "${lines[5]}" == '--' ]
}


###############################################################################
# Matching single line: `-n' and `--index'
###############################################################################

# Options
test_n_index () {
  run printf 'a\nb\nc'
  run assert_line "$1" 1 'b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_line() -n <idx> <expected>: matches against the <idx>-th line only' {
  test_n_index -n
}

@test 'assert_line() --index <idx> <expected>: matches against the <idx>-th line only' {
  test_n_index --index
}

@test 'assert_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' {
  run assert_line --index 1a
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: assert_line --' ]
  [ "${lines[1]}" == "\`--index' requires an integer argument: \`1a'" ]
  [ "${lines[2]}" == '--' ]
}


#
# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- line differs --' ]
  [ "${lines[1]}" == 'index    : 1' ]
  [ "${lines[2]}" == 'expected : a' ]
  [ "${lines[3]}" == 'actual   : b' ]
  [ "${lines[4]}" == '--' ]
}

# Options
@test 'assert_line() --index <idx> <expected>: performs literal matching by default' {
  run printf 'a\nb\nc'
  run assert_line --index 1 '*'
  [ "$status" -eq 1 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_index_p_partial () {
  run printf 'a\n_b_\nc'
  run assert_line --index 1 "$1" 'b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_line() --index <idx> -p <partial>: enables partial matching' {
  test_index_p_partial -p
}

@test 'assert_line() --index <idx> --partial <partial>: enables partial matching' {
  test_index_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- line does not contain substring --' ]
  [ "${lines[1]}" == 'index     : 1' ]
  [ "${lines[2]}" == 'substring : a' ]
  [ "${lines[3]}" == 'line      : b 1' ]
  [ "${lines[4]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_index_r_regexp () {
  run printf 'a\n_b_\nc'
  run assert_line --index 1 "$1" '^.b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'assert_line() --index <idx> -e <regexp>: enables regular expression matching' {
  test_index_r_regexp -e
}

@test 'assert_line() --index <idx> --regexp <regexp>: enables regular expression matching' {
  test_index_r_regexp --regexp
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- regular expression does not match line --' ]
  [ "${lines[1]}" == 'index  : 1' ]
  [ "${lines[2]}" == 'regexp : ^.a' ]
  [ "${lines[3]}" == 'line   : b' ]
  [ "${lines[4]}" == '--' ]
}


###############################################################################
# Common
###############################################################################

@test "assert_line(): \`--partial' and \`--regexp' are mutually exclusive" {
  run assert_line --partial --regexp
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: assert_line --' ]
  [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ]
  [ "${lines[2]}" == '--' ]
}

@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 '[.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: assert_line --' ]
  [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ]
  [ "${lines[2]}" == '--' ]
}

@test "assert_line(): \`--' stops parsing options" {
  run printf 'a\n-p\nc'
  run assert_line -- '-p'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}


================================================
FILE: test/50-assert-18-refute_line.bats
================================================
#!/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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test "refute_line() <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${lines[@]}'" {
  run echo 'a'
  run refute_line 'a'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- line should not be in output --' ]
  [ "${lines[1]}" == 'line   : a' ]
  [ "${lines[2]}" == 'index  : 0' ]
  [ "${lines[3]}" == 'output : a' ]
  [ "${lines[4]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 8 ]
  [ "${lines[0]}" == '-- line should not be in output --' ]
  [ "${lines[1]}" == 'line  : a 1' ]
  [ "${lines[2]}" == 'index : 1' ]
  [ "${lines[3]}" == 'output (3 lines):' ]
  [ "${lines[4]}" == '  a 0' ]
  [ "${lines[5]}" == '> a 1' ]
  [ "${lines[6]}" == '  a 2' ]
  [ "${lines[7]}" == '--' ]
}

# Options
@test 'refute_line() <unexpected>: performs literal matching by default' {
  run echo 'a'
  run refute_line '*'
  [ "$status" -eq 0 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_p_partial () {
  run printf 'a\nb\nc'
  run refute_line "$1" 'd'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_line() -p <partial>: enables partial matching' {
  test_p_partial -p
}

@test 'refute_line() --partial <partial>: enables partial matching' {
  test_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- no line should contain substring --' ]
  [ "${lines[1]}" == 'substring : a' ]
  [ "${lines[2]}" == 'index     : 0' ]
  [ "${lines[3]}" == 'output    : a' ]
  [ "${lines[4]}" == '--' ]
}

# 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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 8 ]
  [ "${lines[0]}" == '-- no line should contain substring --' ]
  [ "${lines[1]}" == 'substring : b' ]
  [ "${lines[2]}" == 'index     : 1' ]
  [ "${lines[3]}" == 'output (3 lines):' ]
  [ "${lines[4]}" == '  a' ]
  [ "${lines[5]}" == '> abc' ]
  [ "${lines[6]}" == '  c' ]
  [ "${lines[7]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_r_regexp () {
  run printf 'a\nb\nc'
  run refute_line "$1" '^.d'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_line() -e <regexp>: enables regular expression matching' {
  test_r_regexp -e
}

@test 'refute_line() --regexp <regexp>: enables regular expression matching' {
  test_r_regexp --regexp
}

# 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.*'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- no line should match the regular expression --' ]
  [ "${lines[1]}" == 'regexp : .*a.*' ]
  [ "${lines[2]}" == 'index  : 0' ]
  [ "${lines[3]}" == 'output : a' ]
  [ "${lines[4]}" == '--' ]
}

# 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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 8 ]
  [ "${lines[0]}" == '-- no line should match the regular expression --' ]
  [ "${lines[1]}" == 'regexp : .*b.*' ]
  [ "${lines[2]}" == 'index  : 1' ]
  [ "${lines[3]}" == 'output (3 lines):' ]
  [ "${lines[4]}" == '  a' ]
  [ "${lines[5]}" == '> abc' ]
  [ "${lines[6]}" == '  c' ]
  [ "${lines[7]}" == '--' ]
}


###############################################################################
# Matching single line: `-n' and `--index'
###############################################################################

# Options
test_n_index () {
  run printf 'a\nb\nc'
  run refute_line "$1" 1 'd'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_line() -n <idx> <expected>: matches against the <idx>-th line only' {
  test_n_index -n
}

@test 'refute_line() --index <idx> <expected>: matches against the <idx>-th line only' {
  test_n_index --index
}

@test 'refute_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' {
  run refute_line --index 1a
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: refute_line --' ]
  [ "${lines[1]}" == "\`--index' requires an integer argument: \`1a'" ]
  [ "${lines[2]}" == '--' ]
}


#
# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 4 ]
  [ "${lines[0]}" == '-- line should differ --' ]
  [ "${lines[1]}" == 'index : 1' ]
  [ "${lines[2]}" == 'line  : b' ]
  [ "${lines[3]}" == '--' ]
}

# Options
@test 'refute_line() --index <idx> <unexpected>: performs literal matching by default' {
  run printf 'a\nb\nc'
  run refute_line --index 1 '*'
  [ "$status" -eq 0 ]
}


#
# Partial matching: `-p' and `--partial'
#

# Options
test_index_p_partial () {
  run printf 'a\nb\nc'
  run refute_line --index 1 "$1" 'd'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_line() --index <idx> -p <partial>: enables partial matching' {
  test_index_p_partial -p
}

@test 'refute_line() --index <idx> --partial <partial>: enables partial matching' {
  test_index_p_partial --partial
}

# 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'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- line should not contain substring --' ]
  [ "${lines[1]}" == 'index     : 1' ]
  [ "${lines[2]}" == 'substring : b' ]
  [ "${lines[3]}" == 'line      : abc' ]
  [ "${lines[4]}" == '--' ]
}


#
# Regular expression matching: `-e' and `--regexp'
#

# Options
test_index_r_regexp () {
  run printf 'a\nb\nc'
  run refute_line --index 1 "$1" '^.b'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute_line() --index <idx> -e <regexp>: enables regular expression matching' {
  test_index_r_regexp -e
}

@test 'refute_line() --index <idx> --regexp <regexp>: enables regular expression matching' {
  test_index_r_regexp --regexp
}

# 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.*'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@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.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 5 ]
  [ "${lines[0]}" == '-- regular expression should not match line --' ]
  [ "${lines[1]}" == 'index  : 1' ]
  [ "${lines[2]}" == 'regexp : .*b.*' ]
  [ "${lines[3]}" == 'line   : abc' ]
  [ "${lines[4]}" == '--' ]
}


###############################################################################
# Common
###############################################################################

@test "refute_line(): \`--partial' and \`--regexp' are mutually exclusive" {
  run refute_line --partial --regexp
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: refute_line --' ]
  [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ]
  [ "${lines[2]}" == '--' ]
}

@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 '[.*'
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- ERROR: refute_line --' ]
  [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ]
  [ "${lines[2]}" == '--' ]
}

@test "refute_line(): \`--' stops parsing options" {
  run printf 'a\n--\nc'
  run refute_line -- '-p'
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}


================================================
FILE: test/50-assert-19-refute.bats
================================================
#!/usr/bin/env bats

load test_helper

@test 'refute() <expression>: returns 0 if <expression> evaluates to FALSE' {
  run refute false
  [ "$status" -eq 0 ]
  [ "${#lines[@]}" -eq 0 ]
}

@test 'refute() <expression>: returns 1 and displays <expression> if it evaluates to TRUE' {
  run refute true
  [ "$status" -eq 1 ]
  [ "${#lines[@]}" -eq 3 ]
  [ "${lines[0]}" == '-- assertion succeeded, but it was expected to fail --' ]
  [ "${lines[1]}" == 'expression : true' ]
  [ "${lines[2]}" == '--' ]
}


================================================
FILE: test/test_helper.bash
================================================
setup() {
  export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.."
  export TEST_DEPS_DIR="${TEST_DEPS_DIR-${TEST_MAIN_DIR}/..}"

  # Load dependencies.
  load "${TEST_DEPS_DIR}/bats-support/load.bash"

  # Load library.
  load '../load'
}
Download .txt
gitextract_l4osbfqa/

├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── load.bash
├── package.json
├── script/
│   └── install-bats.sh
├── src/
│   └── assert.bash
└── test/
    ├── 50-assert-11-assert.bats
    ├── 50-assert-12-assert_equal.bats
    ├── 50-assert-13-assert_success.bats
    ├── 50-assert-14-assert_failure.bats
    ├── 50-assert-15-assert_output.bats
    ├── 50-assert-16-refute_output.bats
    ├── 50-assert-17-assert_line.bats
    ├── 50-assert-18-refute_line.bats
    ├── 50-assert-19-refute.bats
    └── test_helper.bash
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (90K chars).
[
  {
    "path": ".travis.yml",
    "chars": 222,
    "preview": "language: bash\nbefore_install:\n  - ./script/install-bats.sh\n  - git clone --depth 1 https://github.com/ztombol/bats-supp"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 937,
    "preview": "# Change Log\n\nAll notable changes to this project will be documented in this file.\nThis project adheres to [Semantic Ver"
  },
  {
    "path": "LICENSE",
    "chars": 6555,
    "preview": "CC0 1.0 Universal\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclus"
  },
  {
    "path": "README.md",
    "chars": 16050,
    "preview": "# bats-assert\n\n[![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/"
  },
  {
    "path": "load.bash",
    "chars": 56,
    "preview": "source \"$(dirname \"${BASH_SOURCE[0]}\")/src/assert.bash\"\n"
  },
  {
    "path": "package.json",
    "chars": 175,
    "preview": "{\n  \"name\": \"bats-assert\",\n  \"version\": \"0.3.0\",\n  \"private\": true,\n  \"peerDependencies\": {\n    \"bats-support\": \"git+htt"
  },
  {
    "path": "script/install-bats.sh",
    "chars": 161,
    "preview": "#!/bin/sh\nset -o errexit\nset -o xtrace\n\ngit clone --depth 1 https://github.com/sstephenson/bats\ncd bats && ./install.sh "
  },
  {
    "path": "src/assert.bash",
    "chars": 22640,
    "preview": "#\n# bats-assert - Common assertions for Bats\n#\n# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>\n#"
  },
  {
    "path": "test/50-assert-11-assert.bats",
    "chars": 470,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n@test 'assert() <expression>: returns 0 if <expression> evaluates to TRUE' {\n  ru"
  },
  {
    "path": "test/50-assert-12-assert_equal.bats",
    "chars": 1608,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n@test 'assert_equal() <actual> <expected>: returns 0 if <actual> equals <expected"
  },
  {
    "path": "test/50-assert-13-assert_success.bats",
    "chars": 973,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n@test \"assert_success(): returns 0 if \\`\\$status' is 0\" {\n  run true\n  run assert"
  },
  {
    "path": "test/50-assert-14-assert_failure.bats",
    "chars": 2118,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n@test \"assert_failure(): returns 0 if \\`\\$status' is not 0\" {\n  run false\n  run a"
  },
  {
    "path": "test/50-assert-15-assert_output.bats",
    "chars": 6817,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n\n#\n# Literal matching\n#\n\n# Correctness\n@test \"assert_output() <expected>: returns"
  },
  {
    "path": "test/50-assert-16-refute_output.bats",
    "chars": 5226,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n\n#\n# Literal matching\n#\n\n# Correctness\n@test \"refute_output() <unexpected>: retur"
  },
  {
    "path": "test/50-assert-17-assert_line.bats",
    "chars": 9466,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n\n###############################################################################\n"
  },
  {
    "path": "test/50-assert-18-refute_line.bats",
    "chars": 9786,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n\n###############################################################################\n"
  },
  {
    "path": "test/50-assert-19-refute.bats",
    "chars": 501,
    "preview": "#!/usr/bin/env bats\n\nload test_helper\n\n@test 'refute() <expression>: returns 0 if <expression> evaluates to FALSE' {\n  r"
  },
  {
    "path": "test/test_helper.bash",
    "chars": 232,
    "preview": "setup() {\n  export TEST_MAIN_DIR=\"${BATS_TEST_DIRNAME}/..\"\n  export TEST_DEPS_DIR=\"${TEST_DEPS_DIR-${TEST_MAIN_DIR}/..}\""
  }
]

About this extraction

This page contains the full source code of the ztombol/bats-assert GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (82.0 KB), approximately 24.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!