From 6223f9adf0f07b51ae882025e11825ef4aefd9c0 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Mon, 5 May 2025 09:59:26 +0100 Subject: [PATCH 1/9] moved test helper to correct location --- .../.github/workflows/sync-master.yml | 8 + .../bats-assert/.github/workflows/test.yml | 9 + tests/bash/test_helper/bats-assert/.gitignore | 3 + tests/bash/test_helper/bats-assert/LICENSE | 116 ++ tests/bash/test_helper/bats-assert/README.md | 1038 +++++++++++++++++ tests/bash/test_helper/bats-assert/load.bash | 33 + .../test_helper/bats-assert/package-lock.json | 36 + .../bash/test_helper/bats-assert/package.json | 48 + .../test_helper/bats-assert/src/assert.bash | 42 + .../bats-assert/src/assert_equal.bash | 42 + .../bats-assert/src/assert_failure.bash | 78 ++ .../bats-assert/src/assert_line.bash | 301 +++++ .../bats-assert/src/assert_not_equal.bash | 42 + .../bats-assert/src/assert_output.bash | 249 ++++ .../bats-assert/src/assert_regex.bash | 56 + .../bats-assert/src/assert_success.bash | 44 + .../test_helper/bats-assert/src/refute.bash | 42 + .../bats-assert/src/refute_line.bash | 318 +++++ .../bats-assert/src/refute_output.bash | 246 ++++ .../bats-assert/src/refute_regex.bash | 66 ++ .../test_helper/bats-assert/test/assert.bats | 19 + .../bats-assert/test/assert_equal.bats | 62 + .../bats-assert/test/assert_failure.bats | 75 ++ .../bats-assert/test/assert_line.bats | 362 ++++++ .../bats-assert/test/assert_not_equal.bats | 57 + .../bats-assert/test/assert_output.bats | 296 +++++ .../bats-assert/test/assert_regex.bats | 87 ++ .../bats-assert/test/assert_stderr.bats | 298 +++++ .../bats-assert/test/assert_stderr_line.bats | 364 ++++++ .../bats-assert/test/assert_success.bats | 40 + .../test_helper/bats-assert/test/refute.bats | 18 + .../bats-assert/test/refute_line.bats | 355 ++++++ .../bats-assert/test/refute_output.bats | 230 ++++ .../bats-assert/test/refute_regex.bats | 98 ++ .../bats-assert/test/refute_stderr.bats | 242 ++++ .../bats-assert/test/refute_stderr_line.bats | 356 ++++++ .../bats-assert/test/test_helper.bash | 30 + .../bats-support/.github/dependabot.yaml | 7 + .../.github/workflows/scorecard.yaml | 72 ++ .../bats-support/.github/workflows/tests.yml | 27 + .../bash/test_helper/bats-support/.gitignore | 4 + .../test_helper/bats-support/CHANGELOG.md | 46 + tests/bash/test_helper/bats-support/LICENSE | 116 ++ tests/bash/test_helper/bats-support/README.md | 181 +++ tests/bash/test_helper/bats-support/load.bash | 11 + .../test_helper/bats-support/package.json | 30 + .../test_helper/bats-support/src/error.bash | 41 + .../test_helper/bats-support/src/lang.bash | 73 ++ .../test_helper/bats-support/src/output.bash | 279 +++++ .../test/50-output-10-batslib_err.bats | 43 + .../50-output-11-batslib_count_lines.bats | 21 + .../50-output-12-batslib_is_single_line.bats | 13 + ...batslib_get_max_single_line_key_width.bats | 21 + .../50-output-14-batslib_print_kv_single.bats | 27 + .../50-output-15-batslib_print_kv_multi.bats | 19 + ...t-16-batslib_print_kv_single_or_multi.bats | 31 + .../test/50-output-17-batslib_prefix.bats | 43 + .../test/50-output-18-batslib_mark.bats | 72 ++ .../test/50-output-19-batslib_decorate.bats | 46 + .../bats-support/test/51-error-10-fail.bats | 16 + .../test/52-lang-10-batslib_is_caller.bats | 88 ++ tests/bash/test_helper/bats-support/test/cat | 1 + .../bats-support/test/test_helper.bash | 6 + tests/test_helper/bats-assert | 1 - tests/test_helper/bats-support | 1 - 65 files changed, 7140 insertions(+), 2 deletions(-) create mode 100644 tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml create mode 100644 tests/bash/test_helper/bats-assert/.github/workflows/test.yml create mode 100644 tests/bash/test_helper/bats-assert/.gitignore create mode 100644 tests/bash/test_helper/bats-assert/LICENSE create mode 100644 tests/bash/test_helper/bats-assert/README.md create mode 100644 tests/bash/test_helper/bats-assert/load.bash create mode 100644 tests/bash/test_helper/bats-assert/package-lock.json create mode 100644 tests/bash/test_helper/bats-assert/package.json create mode 100644 tests/bash/test_helper/bats-assert/src/assert.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_equal.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_failure.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_line.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_not_equal.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_output.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_regex.bash create mode 100644 tests/bash/test_helper/bats-assert/src/assert_success.bash create mode 100644 tests/bash/test_helper/bats-assert/src/refute.bash create mode 100644 tests/bash/test_helper/bats-assert/src/refute_line.bash create mode 100644 tests/bash/test_helper/bats-assert/src/refute_output.bash create mode 100644 tests/bash/test_helper/bats-assert/src/refute_regex.bash create mode 100644 tests/bash/test_helper/bats-assert/test/assert.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_equal.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_failure.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_line.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_not_equal.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_output.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_regex.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_stderr.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats create mode 100644 tests/bash/test_helper/bats-assert/test/assert_success.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute_line.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute_output.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute_regex.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute_stderr.bats create mode 100644 tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats create mode 100644 tests/bash/test_helper/bats-assert/test/test_helper.bash create mode 100644 tests/bash/test_helper/bats-support/.github/dependabot.yaml create mode 100644 tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml create mode 100644 tests/bash/test_helper/bats-support/.github/workflows/tests.yml create mode 100644 tests/bash/test_helper/bats-support/.gitignore create mode 100644 tests/bash/test_helper/bats-support/CHANGELOG.md create mode 100644 tests/bash/test_helper/bats-support/LICENSE create mode 100644 tests/bash/test_helper/bats-support/README.md create mode 100644 tests/bash/test_helper/bats-support/load.bash create mode 100644 tests/bash/test_helper/bats-support/package.json create mode 100644 tests/bash/test_helper/bats-support/src/error.bash create mode 100644 tests/bash/test_helper/bats-support/src/lang.bash create mode 100644 tests/bash/test_helper/bats-support/src/output.bash create mode 100644 tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats create mode 100644 tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats create mode 100644 tests/bash/test_helper/bats-support/test/51-error-10-fail.bats create mode 100644 tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats create mode 100644 tests/bash/test_helper/bats-support/test/cat create mode 100644 tests/bash/test_helper/bats-support/test/test_helper.bash delete mode 160000 tests/test_helper/bats-assert delete mode 160000 tests/test_helper/bats-support diff --git a/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml b/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml new file mode 100644 index 0000000..4577123 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml @@ -0,0 +1,8 @@ +name: Sync Master +on: + push: { branches: main } + workflow_dispatch: + +jobs: + sync: + uses: bats-core/.github/.github/workflows/sync-master.yml@v1 diff --git a/tests/bash/test_helper/bats-assert/.github/workflows/test.yml b/tests/bash/test_helper/bats-assert/.github/workflows/test.yml new file mode 100644 index 0000000..6c95ce2 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/.github/workflows/test.yml @@ -0,0 +1,9 @@ +name: Test +on: + push: + pull_request: + workflow_dispatch: + +jobs: + test: + uses: bats-core/.github/.github/workflows/test.yml@v1 diff --git a/tests/bash/test_helper/bats-assert/.gitignore b/tests/bash/test_helper/bats-assert/.gitignore new file mode 100644 index 0000000..41a3ee1 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/.gitignore @@ -0,0 +1,3 @@ +/node_modules +/yarn.lock +/bats-assert-*.tgz diff --git a/tests/bash/test_helper/bats-assert/LICENSE b/tests/bash/test_helper/bats-assert/LICENSE new file mode 100644 index 0000000..670154e --- /dev/null +++ b/tests/bash/test_helper/bats-assert/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/tests/bash/test_helper/bats-assert/README.md b/tests/bash/test_helper/bats-assert/README.md new file mode 100644 index 0000000..8a5fc17 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/README.md @@ -0,0 +1,1038 @@ +# bats-assert + +[![License](https://img.shields.io/npm/l/bats-assert.svg)](https://github.com/bats-core/bats-assert/blob/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/bats-core/bats-assert.svg)](https://github.com/bats-core/bats-assert/releases/latest) +[![npm release](https://img.shields.io/npm/v/bats-assert.svg)](https://www.npmjs.com/package/bats-assert) +[![Tests](https://github.com/bats-core/bats-assert/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/bats-core/bats-assert/actions/workflows/test.yml) + +`bats-assert` is a helper library providing common assertions for [Bats][bats]. + +- [Install](#install) +- [Usage](#usage) +- [Options](#options) +- [Full Assertion API](#full-assertion-api) + +In the context of this project, an [assertion][wikipedia-assertions] is a function that perform a test and returns `1` on failure or `0` on success. +To make debugging easier, the assertion also outputs relevant information on failure. +The output is [formatted][bats-support-output] for readability. +To make assertions usable outside of `@test` blocks, the output is sent to [stderr][wikipedia-stderr]. + +The most recent invocation of Bats' `run` function is used for testing assertions on output and status code. + +[wikipedia-assertions]: https://en.wikipedia.org/wiki/Assertion_(software_development) +[wikipedia-stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) + +## Install + +This project has one dependency, for output formatting: [`bats-support`][bats-support] + +Read the [shared documentation][bats-docs] to learn how to install and load both libraries. + +## Usage + +This project provides the following functions: + + - [assert](#assert) / [refute](#refute) Assert a given expression evaluates to `true` or `false`. + - [assert_equal](#assert_equal) Assert two parameters are equal. + - [assert_not_equal](#assert_not_equal) Assert two parameters are not equal. + - [assert_success](#assert_success) / [assert_failure](#assert_failure) Assert exit status is `0` or `1`. + - [assert_output](#assert_output) / [refute_output](#refute_output) Assert output does (or does not) contain given content. + - [assert_line](#assert_line) / [refute_line](#refute_line) Assert a specific line of output does (or does not) contain given content. + - [assert_regex](#assert_regex) / [refute_regex](#refute_regex) Assert a parameter does (or does not) match given pattern. + - [assert_stderr](#assert_stderr) / [refute_stderr](#refute_stderr) Assert stderr does (or does not) contain given content. + - [assert_stderr_line](#assert_stderr_line) / [refute_stderr_line](#refute_stderr_line) Assert a specific line of stderr does (or does not) contain given content. + +These commands are described in more detail below. + +## Options + +For functions that have options, `--` disables option parsing for the remaining arguments to allow using arguments identical to one of the allowed options. + +```bash +assert_output -- '-p' +``` + +Specifying `--` as an argument is similarly simple. + +```bash +refute_line -- '--' +``` + + +## Full Assertion API + +### `assert` + +Fail if the given expression evaluates to false. + +> _**Note**: +> The expression must be a simple command. +> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._ + +```bash +@test 'assert()' { + assert [ 1 -lt 0 ] +} +``` + +On failure, the failed expression is displayed. + +``` +-- assertion failed -- +expression : [ 1 -lt 0 ] +-- +``` + + +### `refute` + +Fail if the given expression evaluates to true. + +> _**Note** +> The expression must be a simple command. +> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._ + +```bash +@test 'refute()' { + refute [ 1 -gt 0 ] +} +``` + +On failure, the successful expression is displayed. + +``` +-- assertion succeeded, but it was expected to fail -- +expression : [ 1 -gt 0 ] +-- +``` + + +### `assert_equal` + +Fail if the two parameters, actual and expected value respectively, do not equal. + +```bash +@test 'assert_equal()' { + assert_equal 'have' 'want' +} +``` + +On failure, the expected and actual values are displayed. + +``` +-- values do not equal -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in *multi-line* format. + + +### `assert_not_equal` + +Fail if the two parameters, actual and unexpected value respectively, are equal. + +```bash +@test 'assert_not_equal()' { + assert_not_equal 'foobar' 'foobar' +} +``` + +On failure, the expected and actual values are displayed. + +``` +-- values should not be equal -- +unexpected : foobar +actual : foobar +-- +``` + +If either value is longer than one line both are displayed in *multi-line* format. + + +### `assert_success` + +Fail if `$status` is not 0. + +```bash +@test 'assert_success() status only' { + run bash -c "echo 'Error!'; exit 1" + assert_success +} +``` + +On failure, `$status` and `$output` are displayed. + +``` +-- command failed -- +status : 1 +output : Error! +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* format. + + +### `assert_failure` + +Fail if `$status` is 0. + +```bash +@test 'assert_failure() status only' { + run echo 'Success!' + assert_failure +} +``` + +On failure, `$output` is displayed. + +``` +-- command succeeded, but it was expected to fail -- +output : Success! +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* format. + +#### Expected status + +When one parameter is specified, fail if `$status` does not equal the expected status specified by the parameter. + +```bash +@test 'assert_failure() with expected status' { + run bash -c "echo 'Error!'; exit 1" + assert_failure 2 +} +``` + +On failure, the expected and actual status, and `$output` are displayed. + +``` +-- command failed as expected, but status differs -- +expected : 2 +actual : 1 +output : Error! +-- +``` + +If `$output` is longer than one line, it is displayed in *multi-line* format. + + +### `assert_output` + +This function helps to verify that a command or function produces the correct output by checking that the specified expected output matches the actual output. +Matching can be literal (default), partial or regular expression. +This function is the logical complement of `refute_output`. + +#### Literal matching + +By default, literal matching is performed. +The assertion fails if `$output` does not equal the expected output. + +```bash +@test 'assert_output()' { + run echo 'have' + assert_output 'want' +} +``` + +On failure, the expected and actual output are displayed. + +``` +-- output differs -- +expected : want +actual : have +-- +``` + +If either value is longer than one line both are displayed in *multi-line* format. + +#### Existence + +To assert that any (non-empty) output exists at all, simply omit the matching argument. + +```bash +@test 'assert_output()' { + run echo 'have' + assert_output +} +``` + +On failure, an error message is displayed. + +``` +-- no output -- +expected non-empty output, but output was empty +-- +``` + +#### Partial matching + +Partial matching can be enabled with the `--partial` option (`-p` for short). +When used, the assertion fails if the expected *substring* is not found in `$output`. + +```bash +@test 'assert_output() partial matching' { + run echo 'ERROR: no such file or directory' + assert_output --partial 'SUCCESS' +} +``` + +On failure, the substring and the output are displayed. + +``` +-- output does not contain substring -- +substring : SUCCESS +output : ERROR: no such file or directory +-- +``` + +This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. +An error is displayed when used simultaneously. + +#### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +When used, the assertion fails if the *[extended regular expression]* does not match `$output`. + +[extended regular expression]: https://en.wikibooks.org/wiki/Regular_Expressions/POSIX-Extended_Regular_Expressions + +> [!IMPORTANT] +> Bash [doesn't support](https://stackoverflow.com/a/48898886/5432315) certain parts of regular expressions you may be used to: +> * `\d` `\D` `\s` `\S` `\w` `\W` — these can be replaced with POSIX character class equivalents `[[:digit:]]`, `[^[:digit:]]`, `[[:space:]]`, `[^[:space:]]`, `[_[:alnum:]]`, and `[^_[:alnum:]]`, respectively. (Notice the last case, where the `[:alnum:]` POSIX character class is augmented with underscore to be exactly equivalent to the Perl `\w` shorthand.) +> * Non-greedy matching. You can sometimes replace `a.*?b` with something like `a[^ab]*b` to get a similar effect in practice, though the two are not exactly equivalent. +> * Non-capturing parentheses `(?:...)`. In the trivial case, just use capturing parentheses `(...)` instead; though of course, if you use capture groups and/or backreferences, this will renumber your capture groups. +> * Lookarounds like `(?<=before)` or `(?!after)`. (In fact anything with `(?` is a Perl extension.) There is no simple general workaround for these, though you can sometimes rephrase your problem into one where lookarounds can be avoided. + +> _**Note**: +> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._ + +```bash +@test 'assert_output() regular expression matching' { + run echo 'Foobar 0.1.0' + assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +} +``` + +On failure, the regular expression and the output are displayed. + +``` +-- regular expression does not match output -- +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar 0.1.0 +-- +``` + +An error is displayed if the specified extended regular expression is invalid. + +This option and partial matching (`--partial` or `-p`) are mutually exclusive. +An error is displayed when used simultaneously. + +#### Standard Input, HereDocs and HereStrings + +The expected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option. + +```bash +@test 'assert_output() with pipe' { + run echo 'hello' + echo 'hello' | assert_output - +} + +@test 'assert_output() with herestring' { + run echo 'hello' + assert_output - <<< hello +} +``` + + +### `refute_output` + +This function helps to verify that a command or function produces the correct output by checking that the specified unexpected output does not match the actual output. +Matching can be literal (default), partial or regular expression. +This function is the logical complement of `assert_output`. + +#### Literal matching + +By default, literal matching is performed. +The assertion fails if `$output` equals the unexpected output. + +```bash +@test 'refute_output()' { + run echo 'want' + refute_output 'want' +} +``` + +On failure, the output is displayed. + +``` +-- output equals, but it was expected to differ -- +output : want +-- +``` + +If output is longer than one line it is displayed in *multi-line* format. + +#### Existence + +To assert that there is no output at all, simply omit the matching argument. + +```bash +@test 'refute_output()' { + run foo --silent + refute_output +} +``` + +On failure, an error message is displayed. + +``` +-- unexpected output -- +expected no output, but output was non-empty +-- +``` + +#### Partial matching + +Partial matching can be enabled with the `--partial` option (`-p` for short). +When used, the assertion fails if the unexpected *substring* is found in `$output`. + +```bash +@test 'refute_output() partial matching' { + run echo 'ERROR: no such file or directory' + refute_output --partial 'ERROR' +} +``` + +On failure, the substring and the output are displayed. + +``` +-- output should not contain substring -- +substring : ERROR +output : ERROR: no such file or directory +-- +``` + +This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. +An error is displayed when used simultaneously. + +#### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +When used, the assertion fails if the *extended regular expression* matches `$output`. + +> _**Note**: +> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._ + +```bash +@test 'refute_output() regular expression matching' { + run echo 'Foobar v0.1.0' + refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +} +``` + +On failure, the regular expression and the output are displayed. + +``` +-- regular expression should not match output -- +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +output : Foobar v0.1.0 +-- +``` + +An error is displayed if the specified extended regular expression is invalid. + +This option and partial matching (`--partial` or `-p`) are mutually exclusive. +An error is displayed when used simultaneously. + +#### Standard Input, HereDocs and HereStrings + +The unexpected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option. + +```bash +@test 'refute_output() with pipe' { + run echo 'hello' + echo 'world' | refute_output - +} + +@test 'refute_output() with herestring' { + run echo 'hello' + refute_output - <<< world +} +``` + + +### `assert_line` + +Similarly to `assert_output`, this function helps to verify that a command or function produces the correct output. +It checks that the expected line appears in the output (default) or in a specific line of it. +Matching can be literal (default), partial or regular expression. +This function is the logical complement of `refute_line`. + +> _**Warning**: +> Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, +> causing line indices to change and preventing testing for empty lines._ + +[bats-93]: https://github.com/sstephenson/bats/pull/93 + +#### Looking for a line in the output + +By default, the entire output is searched for the expected line. +The assertion fails if the expected line is not found in `${lines[@]}`. + +```bash +@test 'assert_line() looking for line' { + run echo $'have-0\nhave-1\nhave-2' + assert_line 'want' +} +``` + +On failure, the expected line and the output are displayed. + +> _**Warning**: +> The output displayed does not contain empty lines. +> See the Warning above for more._ + +``` +-- output does not contain line -- +line : want +output (3 lines): + have-0 + have-1 + have-2 +-- +``` + +If output is not longer than one line, it is displayed in *two-column* format. + +#### Matching a specific line + +When the `--index ` option is used (`-n ` 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[]}`. + +```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 ` option is used (`-n ` 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[]}`. + +```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 ` 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 ` the regular expression replaces the unexpected line. + +``` +-- regular expression should not match line -- +index : 0 +regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +line : Foobar v0.1.0 +-- +``` + +An error is displayed if the specified extended regular expression is invalid. + +This option and partial matching (`--partial` or `-p`) are mutually exclusive. +An error is displayed when used simultaneously. + +### `assert_regex` + +This function is similar to `assert_equal` but uses pattern matching instead of +equality, by wrapping `[[ value =~ pattern ]]`. + +Fail if the value (first parameter) does not match the pattern (second +parameter). + +```bash +@test 'assert_regex()' { + assert_regex 'what' 'x$' +} +``` + +On failure, the value and the pattern are displayed. + +``` +-- values does not match regular expression -- +value : what +pattern : x$ +-- +``` + +If the value is longer than one line then it is displayed in *multi-line* +format. + +An error is displayed if the specified extended regular expression is invalid. + +For description of the matching behavior, refer to the documentation of the +`=~` operator in the [Bash manual][bash-conditional]. + +> _**Note**: +> the `BASH_REMATCH` array is available immediately after the assertion succeeds but is fragile; +> i.e. prone to being overwritten as a side effect of other actions._ + +### `refute_regex` + +This function is similar to `refute_equal` but uses pattern matching instead of +equality, by wrapping `! [[ value =~ pattern ]]`. + +Fail if the value (first parameter) matches the pattern (second parameter). + +```bash +@test 'refute_regex()' { + refute_regex 'WhatsApp' 'Threema' +} +``` + +On failure, the value, the pattern and the match are displayed. + +```bash +@test 'refute_regex()' { + refute_regex 'WhatsApp' 'What.' +} + +-- value matches regular expression -- +value : WhatsApp +pattern : What. +match : Whats +case : sensitive +-- +``` + +If the value or pattern is longer than one line then it is displayed in +*multi-line* format. + +An error is displayed if the specified extended regular expression is invalid. + +For description of the matching behavior, refer to the documentation of the +`=~` operator in the [Bash manual][bash-conditional]. + +> _**Note**: +> the `BASH_REMATCH` array is available immediately after the assertion fails but is fragile; +> i.e. prone to being overwritten as a side effect of other actions like calling `run`. +> Thus, it's good practice to avoid using `BASH_REMATCH` in conjunction with `refute_regex()`. +> The valuable information the array contains is the matching part of the value which is printed in the failing test log, as mentioned above._ + +### `assert_stderr` + +> _**Note**: +> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. +> If not, `$stderr` will be empty, causing `assert_stderr` to always fail. + +Similarly to `assert_output`, this function verifies that a command or function produces the expected stderr. +The stderr matching can be literal (the default), partial or by regular expression. +The expected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. + +#### Literal matching + +By default, literal matching is performed. +The assertion fails if `$stderr` does not equal the expected stderr. + + ```bash + echo_err() { + echo "$@" >&2 + } + + @test 'assert_stderr()' { + run --separate-stderr echo_err 'have' + assert_stderr 'want' + } + + @test 'assert_stderr() with pipe' { + run --separate-stderr echo_err 'hello' + echo_err 'hello' | assert_stderr - + } + + @test 'assert_stderr() with herestring' { + run --separate-stderr echo_err 'hello' + assert_stderr - <<< hello + } + ``` + +On failure, the expected and actual stderr are displayed. + + ``` + -- stderr differs -- + expected : want + actual : have + -- + ``` + +#### Existence + +To assert that any stderr exists at all, omit the `expected` argument. + + ```bash + @test 'assert_stderr()' { + run --separate-stderr echo_err 'have' + assert_stderr + } + ``` + +On failure, an error message is displayed. + + ``` + -- no stderr -- + expected non-empty stderr, but stderr was empty + -- + ``` + +#### Partial matching + +Partial matching can be enabled with the `--partial` option (`-p` for short). +When used, the assertion fails if the expected _substring_ is not found in `$stderr`. + + ```bash + @test 'assert_stderr() partial matching' { + run --separate-stderr echo_err 'ERROR: no such file or directory' + assert_stderr --partial 'SUCCESS' + } + ``` + +On failure, the substring and the stderr are displayed. + + ``` + -- stderr does not contain substring -- + substring : SUCCESS + stderr : ERROR: no such file or directory + -- + ``` + +#### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +When used, the assertion fails if the *extended regular expression* does not match `$stderr`. + +*Note: The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire stderr; not individual lines.* + + ```bash + @test 'assert_stderr() regular expression matching' { + run --separate-stderr echo_err 'Foobar 0.1.0' + assert_stderr --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' + } + ``` + +On failure, the regular expression and the stderr are displayed. + + ``` + -- regular expression does not match stderr -- + regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ + stderr : Foobar 0.1.0 + -- + ``` + +### `refute_stderr` + +> _**Note**: +> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. +> If not, `$stderr` will be empty, causing `refute_stderr` to always pass. + +Similar to `refute_output`, this function verifies that a command or function does not produce the unexpected stderr. +(It is the logical complement of `assert_stderr`.) +The stderr matching can be literal (the default), partial or by regular expression. +The unexpected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. + +### `assert_stderr_line` + +> _**Note**: +> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. +> If not, `$stderr` will be empty, causing `assert_stderr_line` to always fail. + +Similarly to `assert_stderr`, this function verifies that a command or function produces the expected stderr. +It checks that the expected line appears in the stderr (default) or at a specific line number. +Matching can be literal (default), partial or regular expression. +This function is the logical complement of `refute_stderr_line`. + +#### Looking for a line in the stderr + +By default, the entire stderr is searched for the expected line. +The assertion fails if the expected line is not found in `${stderr_lines[@]}`. + + ```bash + echo_err() { + echo "$@" >&2 + } + + @test 'assert_stderr_line() looking for line' { + run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' + assert_stderr_line 'want' + } + ``` + +On failure, the expected line and the stderr are displayed. + + ``` + -- stderr does not contain line -- + line : want + stderr (3 lines): + have-0 + have-1 + have-2 + -- + ``` + +#### Matching a specific line + +When the `--index ` option is used (`-n ` 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 `${stderr_lines[]}`. + + ```bash + @test 'assert_stderr_line() specific line' { + run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' + assert_stderr_line --index 1 'want-1' + } + ``` + +On failure, the index and the compared stderr_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_stderr_line() partial matching' { + run --separate-stderr echo_err $'have 1\nhave 2\nhave 3' + assert_stderr_line --partial 'want' + } + ``` + +On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line. + + ``` + -- no stderr line contains substring -- + substring : want + stderr (3 lines): + have 1 + have 2 + have 3 + -- + ``` + +#### Regular expression matching + +Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +When used, a match fails if the *extended regular expression* does not match the line being tested. + +*Note: As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* + + ```bash + @test 'assert_stderr_line() regular expression matching' { + run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' + assert_stderr_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 + -- + ``` + +### `refute_stderr_line` + +> _**Note**: +> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. +> If not, `$stderr` will be empty, causing `refute_stderr_line` to always pass. + +Similarly to `refute_stderr`, this function helps to verify that a command or function produces the correct stderr. +It checks that the unexpected line does not appear in the stderr (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_stderr_line`. + + + +[bats]: https://github.com/bats-core/bats-core +[bash-comp-cmd]: https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands +[bash-conditional]: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs + +[bats-docs]: https://bats-core.readthedocs.io/ +[bats-support-output]: https://github.com/bats-core/bats-support#output-formatting +[bats-support]: https://github.com/bats-core/bats-support diff --git a/tests/bash/test_helper/bats-assert/load.bash b/tests/bash/test_helper/bats-assert/load.bash new file mode 100644 index 0000000..c67d9e8 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/load.bash @@ -0,0 +1,33 @@ +# bats-assert - Common assertions for Bats +# +# Written in 2016 by Zoltan Tombol +# +# 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 +# . +# +# Assertions are functions that perform a test and output relevant +# information on failure to help debugging. They return 1 on failure +# and 0 otherwise. +# +# All output is formatted for readability using the functions of +# `output.bash' and sent to the standard error. + +# shellcheck disable=1090 +source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash" diff --git a/tests/bash/test_helper/bats-assert/package-lock.json b/tests/bash/test_helper/bats-assert/package-lock.json new file mode 100644 index 0000000..8f0dcc6 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "bats-assert", + "version": "2.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bats-assert", + "version": "2.1.0", + "license": "CC0-1.0", + "devDependencies": { + "bats": "^1", + "bats-support": "^0.3" + }, + "peerDependencies": { + "bats": "0.4 || ^1", + "bats-support": "^0.3" + } + }, + "node_modules/bats": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/bats/-/bats-1.9.0.tgz", + "integrity": "sha512-Z5BJaAmmHv/ujj7obhjEzJ//OL+ZtjVq0iRnHu+2fE9OeUaPMbJpBgYiOdNbDrG3E2hqe84/AXNnS/UiXl/UcA==", + "dev": true, + "bin": { + "bats": "bin/bats" + } + }, + "node_modules/bats-support": { + "version": "0.3.0", + "resolved": "git+ssh://git@github.com/jasonkarns/bats-support.git#24a72e14349690bcbf7c151b9d2d1cdd32d36eb1", + "integrity": "sha512-42f2THEaN02dVj7Zhj0C4AM5FdePq3YPlXvnu/fKUQwCv1qM2dyACBKFXQkLw9QMzhDmwuFcke90XwHMUsBjGQ==", + "dev": true + } + } +} diff --git a/tests/bash/test_helper/bats-assert/package.json b/tests/bash/test_helper/bats-assert/package.json new file mode 100644 index 0000000..7048995 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/package.json @@ -0,0 +1,48 @@ +{ + "name": "bats-assert", + "version": "2.1.0", + "description": "Common assertions for Bats", + "homepage": "https://github.com/bats-core/bats-assert", + "license": "CC0-1.0", + "author": "Zoltán Tömböl (https://github.com/ztombol)", + "contributors": [ + "Sam Stephenson (http://sstephenson.us/)", + "Jason Karns (http://jason.karns.name)", + "Mislav Marohnić (http://mislav.net/)", + "Tim Pope (https://github.com/tpope)" + ], + "repository": "github:bats-core/bats-assert", + "bugs": "https://github.com/bats-core/bats-assert/issues", + "directories": { + "lib": "src", + "test": "test" + }, + "files": [ + "load.bash", + "src" + ], + "scripts": { + "test": "bats ${CI+-t} test", + "postversion": "npm publish", + "prepublishOnly": "npm run publish:github", + "publish:github": "git push --follow-tags" + }, + "devDependencies": { + "bats": "^1", + "bats-support": "^0.3" + }, + "peerDependencies": { + "bats": "0.4 || ^1", + "bats-support": "^0.3" + }, + "keywords": [ + "bats", + "bash", + "shell", + "test", + "unit", + "assert", + "assertion", + "helper" + ] +} diff --git a/tests/bash/test_helper/bats-assert/src/assert.bash b/tests/bash/test_helper/bats-assert/src/assert.bash new file mode 100644 index 0000000..0260ce2 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert.bash @@ -0,0 +1,42 @@ +# assert +# ====== +# +# Summary: Fail if the given expression evaluates to false. +# +# Usage: assert + +# Options: +# The expression to evaluate for truthiness. +# *__Note:__ The expression must be a simple command. +# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), +# such as `[[`, can be used only when executed with `bash -c`.* +# +# IO: +# STDERR - the failed expression, on failure +# Globals: +# none +# Returns: +# 0 - if expression evaluates to true +# 1 - otherwise +# +# ```bash +# @test 'assert()' { +# touch '/var/log/test.log' +# assert [ -e '/var/log/test.log' ] +# } +# ``` +# +# On failure, the failed expression is displayed. +# +# ``` +# -- assertion failed -- +# expression : [ -e /var/log/test.log ] +# -- +# ``` +assert() { + if ! "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion failed' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_equal.bash b/tests/bash/test_helper/bats-assert/src/assert_equal.bash new file mode 100644 index 0000000..4ef1297 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_equal.bash @@ -0,0 +1,42 @@ +# assert_equal +# ============ +# +# Summary: Fail if the actual and expected values are not equal. +# +# Usage: assert_equal +# +# Options: +# The value being compared. +# The value to compare against. +# +# ```bash +# @test 'assert_equal()' { +# assert_equal 'have' 'want' +# } +# ``` +# +# IO: +# STDERR - expected and actual values, on failure +# Globals: +# none +# Returns: +# 0 - if values equal +# 1 - otherwise +# +# On failure, the expected and actual values are displayed. +# +# ``` +# -- values do not equal -- +# expected : want +# actual : have +# -- +# ``` +assert_equal() { + if [[ $1 != "$2" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$2" \ + 'actual' "$1" \ + | batslib_decorate 'values do not equal' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_failure.bash b/tests/bash/test_helper/bats-assert/src/assert_failure.bash new file mode 100644 index 0000000..d906059 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_failure.bash @@ -0,0 +1,78 @@ +# assert_failure +# ============== +# +# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status. +# +# Usage: assert_failure [] +# +# Options: +# The specific status code to check against. +# If not provided, simply asserts status is != 0. +# +# IO: +# STDERR - `$output`, on failure; +# - also, `$status` and `expected_status`, if provided +# Globals: +# status +# output +# Returns: +# 0 - if `$status' is 0, +# or if expected_status is provided but does not equal `$status' +# 1 - otherwise +# +# ```bash +# @test 'assert_failure() status only' { +# run echo 'Success!' +# assert_failure +# } +# ``` +# +# On failure, `$output` is displayed. +# +# ``` +# -- command succeeded, but it was expected to fail -- +# output : Success! +# -- +# ``` +# +# ## Expected status +# +# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`. +# +# ```bash +# @test 'assert_failure() with expected status' { +# run bash -c "echo 'Error!'; exit 1" +# assert_failure 2 +# } +# ``` +# +# On failure, both the expected and actual statuses, and `$output` are displayed. +# +# ``` +# -- command failed as expected, but status differs -- +# expected : 2 +# actual : 1 +# output : Error! +# -- +# ``` +assert_failure() { + : "${output?}" + : "${status?}" + + (( $# > 0 )) && local -r expected="$1" + if (( status == 0 )); then + batslib_print_kv_single_or_multi 6 'output' "$output" \ + | batslib_decorate 'command succeeded, but it was expected to fail' \ + | fail + elif (( $# > 0 )) && (( status != expected )); then + { local -ir width=8 + batslib_print_kv_single "$width" \ + 'expected' "$expected" \ + 'actual' "$status" + batslib_print_kv_single_or_multi "$width" \ + 'output' "$output" + } \ + | batslib_decorate 'command failed as expected, but status differs' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_line.bash b/tests/bash/test_helper/bats-assert/src/assert_line.bash new file mode 100644 index 0000000..bf9140d --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_line.bash @@ -0,0 +1,301 @@ +# assert_line +# =========== +# +# Summary: Fail if the expected line is not found in the output (default) or at a specific line number. +# +# Usage: assert_line [-n index] [-p | -e] [--] +# +# Options: +# -n, --index Match the th line +# -p, --partial Match if `expected` is a substring of `$output` or line +# -e, --regexp Treat `expected` as an extended regular expression +# The expected line string, substring, or regular expression +# +# IO: +# STDERR - details, on failure +# error message, on error +# Globals: +# output +# lines +# Returns: +# 0 - if matching line found +# 1 - otherwise +# +# Similarly to `assert_output`, this function verifies that a command or function produces the expected output. +# (It is the logical complement of `refute_line`.) +# It checks that the expected line appears in the output (default) or at a specific line number. +# Matching can be literal (default), partial or regular expression. +# +# *__Warning:__ +# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, +# causing line indices to change and preventing testing for empty lines.* +# +# [bats-93]: https://github.com/sstephenson/bats/pull/93 +# +# ## Looking for a line in the output +# +# By default, the entire output is searched for the expected line. +# The assertion fails if the expected line is not found in `${lines[@]}`. +# +# ```bash +# @test 'assert_line() looking for line' { +# run echo $'have-0\nhave-1\nhave-2' +# assert_line 'want' +# } +# ``` +# +# On failure, the expected line and the output are displayed. +# +# ``` +# -- output does not contain line -- +# line : want +# output (3 lines): +# have-0 +# have-1 +# have-2 +# -- +# ``` +# +# ## Matching a specific line +# +# When the `--index ` option is used (`-n ` 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[]}`. +# +# ```bash +# @test 'assert_line() specific line' { +# run echo $'have-0\nhave-1\nhave-2' +# assert_line --index 1 'want-1' +# } +# ``` +# +# On failure, the index and the compared lines are displayed. +# +# ``` +# -- line differs -- +# index : 1 +# expected : want-1 +# actual : have-1 +# -- +# ``` +# +# ## Partial matching +# +# Partial matching can be enabled with the `--partial` option (`-p` for short). +# When used, a match fails if the expected *substring* is not found in the matched line. +# +# ```bash +# @test 'assert_line() partial matching' { +# run echo $'have 1\nhave 2\nhave 3' +# assert_line --partial 'want' +# } +# ``` +# +# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line. +# +# ``` +# -- no output line contains substring -- +# substring : want +# output (3 lines): +# have 1 +# have 2 +# have 3 +# -- +# ``` +# +# ## Regular expression matching +# +# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +# When used, a match fails if the *extended regular expression* does not match the line being tested. +# +# *__Note__: +# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* +# +# ```bash +# @test 'assert_line() regular expression matching' { +# run echo $'have-0\nhave-1\nhave-2' +# assert_line --index 1 --regexp '^want-[0-9]$' +# } +# ``` +# +# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line. +# +# ``` +# -- regular expression does not match line -- +# index : 1 +# regexp : ^want-[0-9]$ +# line : have-1 +# -- +# ``` +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +assert_line() { + __assert_line "$@" +} + +# assert_stderr_line +# =========== +# +# Summary: Fail if the expected line is not found in the stderr (default) or at a specific line number. +# +# Usage: assert_stderr_line [-n index] [-p | -e] [--] +# +# Options: +# -n, --index Match the th line +# -p, --partial Match if `expected` is a substring of `$stderr` or line +# -e, --regexp Treat `expected` as an extended regular expression +# The expected line string, substring, or regular expression +# +# IO: +# STDERR - details, on failure +# error message, on error +# Globals: +# stderr +# stderr_lines +# Returns: +# 0 - if matching line found +# 1 - otherwise +# +# Similarly to `assert_stderr`, this function verifies that a command or function produces the expected stderr. +# (It is the logical complement of `refute_stderr_line`.) +# It checks that the expected line appears in the stderr (default) or at a specific line number. +# Matching can be literal (default), partial or regular expression. +# +assert_stderr_line() { + __assert_line "$@" +} + +__assert_line() { + local -r caller=${FUNCNAME[1]} + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + if [[ "${caller}" == "assert_line" ]]; then + : "${lines?}" + local -ar stream_lines=("${lines[@]}") + local -r stream_type=output + elif [[ "${caller}" == "assert_stderr_line" ]]; then + : "${stderr_lines?}" + local -ar stream_lines=("${stderr_lines[@]}") + local -r stream_type=stderr + else + # Unknown caller + echo "Unexpected call to \`${FUNCNAME[0]}\` +Did you mean to call \`assert_line\` or \`assert_stderr_line\`?" \ + | batslib_decorate "ERROR: ${FUNCNAME[0]}" \ + | fail + return $? + fi + + # 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: ${caller}" \ + | 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: ${caller}" \ + | 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: ${caller}" \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if ! [[ ${stream_lines[$idx]} =~ $expected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$expected" \ + 'line' "${stream_lines[$idx]}" \ + | batslib_decorate 'regular expression does not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${stream_lines[$idx]} != *"$expected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$expected" \ + 'line' "${stream_lines[$idx]}" \ + | batslib_decorate 'line does not contain substring' \ + | fail + fi + else + if [[ ${stream_lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${stream_lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | fail + fi + fi + else + # Contained in output/error stream. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do + [[ ${stream_lines[$idx]} =~ $expected ]] && return 0 + done + { local -ar single=( 'regexp' "$expected" ) + local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 ${stream_type} line matches regular expression" \ + | fail + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do + [[ ${stream_lines[$idx]} == *"$expected"* ]] && return 0 + done + { local -ar single=( 'substring' "$expected" ) + local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 ${stream_type} line contains substring" \ + | fail + else + local -i idx + for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do + [[ ${stream_lines[$idx]} == "$expected" ]] && return 0 + done + { local -ar single=( 'line' "$expected" ) + local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 "${stream_type} does not contain line" \ + | fail + fi + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash b/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash new file mode 100644 index 0000000..933bb71 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash @@ -0,0 +1,42 @@ +# assert_not_equal +# ============ +# +# Summary: Fail if the actual and unexpected values are equal. +# +# Usage: assert_not_equal +# +# Options: +# The value being compared. +# The value to compare against. +# +# ```bash +# @test 'assert_not_equal()' { +# assert_not_equal 'foo' 'foo' +# } +# ``` +# +# IO: +# STDERR - expected and actual values, on failure +# Globals: +# none +# Returns: +# 0 - if actual does not equal unexpected +# 1 - otherwise +# +# On failure, the unexpected and actual values are displayed. +# +# ``` +# -- values should not be equal -- +# unexpected : foo +# actual : foo +# -- +# ``` +assert_not_equal() { + if [[ "$1" == "$2" ]]; then + batslib_print_kv_single_or_multi 10 \ + 'unexpected' "$2" \ + 'actual' "$1" \ + | batslib_decorate 'values should not be equal' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_output.bash b/tests/bash/test_helper/bats-assert/src/assert_output.bash new file mode 100644 index 0000000..168d246 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_output.bash @@ -0,0 +1,249 @@ +# assert_output +# ============= +# +# Summary: Fail if `$output' does not match the expected output. +# +# Usage: assert_output [-p | -e] [- | [--] ] +# +# Options: +# -p, --partial Match if `expected` is a substring of `$output` +# -e, --regexp Treat `expected` as an extended regular expression +# -, --stdin Read `expected` value from STDIN +# The expected value, substring or regular expression +# +# IO: +# STDIN - [=$1] expected output +# STDERR - details, on failure +# error message, on error +# Globals: +# output +# Returns: +# 0 - if output matches the expected value/partial/regexp +# 1 - otherwise +# +# This function verifies that a command or function produces the expected output. +# (It is the logical complement of `refute_output`.) +# Output matching can be literal (the default), partial or by regular expression. +# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. +# +# ## Literal matching +# +# By default, literal matching is performed. +# The assertion fails if `$output` does not equal the expected output. +# +# ```bash +# @test 'assert_output()' { +# run echo 'have' +# assert_output 'want' +# } +# +# @test 'assert_output() with pipe' { +# run echo 'hello' +# echo 'hello' | assert_output - +# } +# +# @test 'assert_output() with herestring' { +# run echo 'hello' +# assert_output - <<< hello +# } +# ``` +# +# On failure, the expected and actual output are displayed. +# +# ``` +# -- output differs -- +# expected : want +# actual : have +# -- +# ``` +# +# ## Existence +# +# To assert that any output exists at all, omit the `expected` argument. +# +# ```bash +# @test 'assert_output()' { +# run echo 'have' +# assert_output +# } +# ``` +# +# On failure, an error message is displayed. +# +# ``` +# -- no output -- +# expected non-empty output, but output was empty +# -- +# ``` +# +# ## Partial matching +# +# Partial matching can be enabled with the `--partial` option (`-p` for short). +# When used, the assertion fails if the expected _substring_ is not found in `$output`. +# +# ```bash +# @test 'assert_output() partial matching' { +# run echo 'ERROR: no such file or directory' +# assert_output --partial 'SUCCESS' +# } +# ``` +# +# On failure, the substring and the output are displayed. +# +# ``` +# -- output does not contain substring -- +# substring : SUCCESS +# output : ERROR: no such file or directory +# -- +# ``` +# +# ## Regular expression matching +# +# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +# When used, the assertion fails if the *extended regular expression* does not match `$output`. +# +# *__Note__: +# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output; +# not individual lines.* +# +# ```bash +# @test 'assert_output() regular expression matching' { +# run echo 'Foobar 0.1.0' +# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +# } +# ``` +# +# On failure, the regular expression and the output are displayed. +# +# ``` +# -- regular expression does not match output -- +# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +# output : Foobar 0.1.0 +# -- +# ``` +assert_output() { + __assert_stream "$@" +} + +# assert_stderr +# ============= +# +# Summary: Fail if `$stderr' does not match the expected stderr. +# +# Usage: assert_stderr [-p | -e] [- | [--] ] +# +# Options: +# -p, --partial Match if `expected` is a substring of `$stderr` +# -e, --regexp Treat `expected` as an extended regular expression +# -, --stdin Read `expected` value from STDIN +# The expected value, substring or regular expression +# +# IO: +# STDIN - [=$1] expected stderr +# STDERR - details, on failure +# error message, on error +# Globals: +# stderr +# Returns: +# 0 - if stderr matches the expected value/partial/regexp +# 1 - otherwise +# +# Similarly to `assert_output`, this function verifies that a command or function produces the expected stderr. +# (It is the logical complement of `refute_stderr`.) +# The stderr matching can be literal (the default), partial or by regular expression. +# The expected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. +# +assert_stderr() { + __assert_stream "$@" +} + +__assert_stream() { + local -r caller=${FUNCNAME[1]} + local -r stream_type=${caller/assert_/} + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + local -i is_mode_nonempty=0 + local -i use_stdin=0 + + if [[ ${stream_type} == "output" ]]; then + : "${output?}" + elif [[ ${stream_type} == "stderr" ]]; then + : "${stderr?}" + else + # Unknown caller + echo "Unexpected call to \`${FUNCNAME[0]}\` +Did you mean to call \`assert_output\` or \`assert_stderr\`?" | + batslib_decorate "ERROR: ${FUNCNAME[0]}" | + fail + return $? + fi + local -r stream="${!stream_type}" + + # Handle options. + if (( $# == 0 )); then + is_mode_nonempty=1 + fi + + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + -|--stdin) use_stdin=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate "ERROR: ${caller}" \ + | fail + return $? + fi + + # Arguments. + local expected + if (( use_stdin )); then + expected="$(cat -)" + else + expected="${1-}" + fi + + # Matching. + if (( is_mode_nonempty )); then + if [ -z "$stream" ]; then + echo "expected non-empty $stream_type, but $stream_type was empty" \ + | batslib_decorate "no $stream_type" \ + | fail + fi + elif (( is_mode_regexp )); then + # shellcheck disable=2319 + if [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate "ERROR: ${caller}" \ + | fail + elif ! [[ $stream =~ $expected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$expected" \ + "$stream_type" "$stream" \ + | batslib_decorate "regular expression does not match $stream_type" \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $stream != *"$expected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$expected" \ + "$stream_type" "$stream" \ + | batslib_decorate "$stream_type does not contain substring" \ + | fail + fi + else + if [[ $stream != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$stream" \ + | batslib_decorate "$stream_type differs" \ + | fail + fi + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_regex.bash b/tests/bash/test_helper/bats-assert/src/assert_regex.bash new file mode 100644 index 0000000..17a7057 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_regex.bash @@ -0,0 +1,56 @@ +# `assert_regex` +# +# This function is similar to `assert_equal` but uses pattern matching instead +# of equality, by wrapping `[[ value =~ pattern ]]`. +# +# Fail if the value (first parameter) does not match the pattern (second +# parameter). +# +# ```bash +# @test 'assert_regex()' { +# assert_regex 'what' 'x$' +# } +# ``` +# +# On failure, the value and the pattern are displayed. +# +# ``` +# -- values does not match regular expression -- +# value : what +# pattern : x$ +# -- +# ``` +# +# If the value is longer than one line then it is displayed in *multi-line* +# format. +# +# An error is displayed if the specified extended regular expression is invalid. +# +# For description of the matching behavior, refer to the documentation of the +# `=~` operator in the +# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. +# Note that the `BASH_REMATCH` array is available immediately after the +# assertion succeeds but is fragile, i.e. prone to being overwritten as a side +# effect of other actions. +assert_regex() { + local -r value="${1}" + local -r pattern="${2}" + + if [[ '' =~ ${pattern} ]]; (( ${?} == 2 )); then + echo "Invalid extended regular expression: \`${pattern}'" \ + | batslib_decorate 'ERROR: assert_regex' \ + | fail + elif ! [[ "${value}" =~ ${pattern} ]]; then + if shopt -p nocasematch &>/dev/null; then + local case_sensitive=insensitive + else + local case_sensitive=sensitive + fi + batslib_print_kv_single_or_multi 8 \ + 'value' "${value}" \ + 'pattern' "${pattern}" \ + 'case' "${case_sensitive}" \ + | batslib_decorate 'value does not match regular expression' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/assert_success.bash b/tests/bash/test_helper/bats-assert/src/assert_success.bash new file mode 100644 index 0000000..a8b2a38 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/assert_success.bash @@ -0,0 +1,44 @@ +# assert_success +# ============== +# +# Summary: Fail if `$status` is not 0. +# +# Usage: assert_success +# +# IO: +# STDERR - `$status` and `$output`, on failure +# Globals: +# status +# output +# Returns: +# 0 - if `$status' is 0 +# 1 - otherwise +# +# ```bash +# @test 'assert_success() status only' { +# run bash -c "echo 'Error!'; exit 1" +# assert_success +# } +# ``` +# +# On failure, `$status` and `$output` are displayed. +# +# ``` +# -- command failed -- +# status : 1 +# output : Error! +# -- +# ``` +assert_success() { + : "${output?}" + : "${status?}" + + if (( status != 0 )); then + { local -ir width=6 + batslib_print_kv_single "$width" 'status' "$status" + batslib_print_kv_single_or_multi "$width" 'output' "$output" + } \ + | batslib_decorate 'command failed' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/refute.bash b/tests/bash/test_helper/bats-assert/src/refute.bash new file mode 100644 index 0000000..e7c47da --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/refute.bash @@ -0,0 +1,42 @@ +# refute +# ====== +# +# Summary: Fail if the given expression evaluates to true. +# +# Usage: refute +# +# Options: +# The expression to evaluate for falsiness. +# *__Note:__ The expression must be a simple command. +# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), +# such as `[[`, can be used only when executed with `bash -c`.* +# +# IO: +# STDERR - the successful expression, on failure +# Globals: +# none +# Returns: +# 0 - if expression evaluates to false +# 1 - otherwise +# +# ```bash +# @test 'refute()' { +# rm -f '/var/log/test.log' +# refute [ -e '/var/log/test.log' ] +# } +# ``` +# +# On failure, the successful expression is displayed. +# +# ``` +# -- assertion succeeded, but it was expected to fail -- +# expression : [ -e /var/log/test.log ] +# -- +# ``` +refute() { + if "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion succeeded, but it was expected to fail' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/refute_line.bash b/tests/bash/test_helper/bats-assert/src/refute_line.bash new file mode 100644 index 0000000..bb7337d --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/refute_line.bash @@ -0,0 +1,318 @@ +# refute_line +# =========== +# +# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number. +# +# Usage: refute_line [-n index] [-p | -e] [--] +# +# Options: +# -n, --index Match the th line +# -p, --partial Match if `unexpected` is a substring of `$output` or line +# -e, --regexp Treat `unexpected` as an extended regular expression +# The unexpected line string, substring, or regular expression. +# +# IO: +# STDERR - details, on failure +# error message, on error +# Globals: +# output +# lines +# Returns: +# 0 - if match not found +# 1 - otherwise +# +# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output. +# (It is the logical complement of `assert_line`.) +# It checks that the unexpected line does not appear in the output (default) or at a specific line number. +# Matching can be literal (default), partial or regular expression. +# +# ## Looking for a line in the output +# +# By default, the entire output is searched for the unexpected line. +# The assertion fails if the unexpected line is found in `${lines[@]}`. +# +# ```bash +# @test 'refute_line() looking for line' { +# run echo $'have-0\nwant\nhave-2' +# refute_line 'want' +# } +# ``` +# +# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed. +# +# ``` +# -- line should not be in output -- +# line : want +# index : 1 +# output (3 lines): +# have-0 +# > want +# have-2 +# -- +# ``` +# +# ## Matching a specific line +# +# When the `--index ` option is used (`-n ` 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[]}`. +# +# ```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 ` the substring replaces the unexpected line. +# +# ``` +# -- no line should contain substring -- +# substring : want +# index : 1 +# output (3 lines): +# have 1 +# > want 2 +# have 3 +# -- +# ``` +# +# ## Regular expression matching +# +# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +# When used, a match fails if the *extended regular expression* matches the line being tested. +# +# *__Note__: +# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* +# +# ```bash +# @test 'refute_line() regular expression matching' { +# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29' +# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +# } +# ``` +# +# On failure, in addition to the details of literal matching, the regular expression is also displayed. +# When used with `--index ` the regular expression replaces the unexpected line. +# +# ``` +# -- regular expression should not match line -- +# index : 0 +# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +# line : Foobar v0.1.0 +# -- +# ``` +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +refute_line() { + __refute_stream_line "$@" +} + +# refute_stderr_line +# ================== +# +# Summary: Fail if the unexpected line is found in the stderr (default) or at a specific line number. +# +# Usage: refute_stderr_line [-n index] [-p | -e] [--] +# +# Options: +# -n, --index Match the th line +# -p, --partial Match if `unexpected` is a substring of `$stderr` or line +# -e, --regexp Treat `unexpected` as an extended regular expression +# The unexpected line string, substring, or regular expression. +# +# IO: +# STDERR - details, on failure +# error message, on error +# Globals: +# stderr +# stderr_lines +# Returns: +# 0 - if match not found +# 1 - otherwise +# +# Similarly to `refute_stderr`, this function verifies that a command or function does not produce the unexpected stderr. +# (It is the logical complement of `assert_stderr_line`.) +# It checks that the unexpected line does not appear in the stderr (default) or at a specific line number. +# Matching can be literal (default), partial or regular expression. +# +refute_stderr_line() { + __refute_stream_line "$@" +} + +__refute_stream_line() { + local -r caller=${FUNCNAME[1]} + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + if [[ "${caller}" == "refute_line" ]]; then + : "${lines?}" + local -ar stream_lines=("${lines[@]}") + local -r stream_type=output + elif [[ "${caller}" == "refute_stderr_line" ]]; then + : "${stderr_lines?}" + local -ar stream_lines=("${stderr_lines[@]}") + local -r stream_type=stderr + else + # Unknown caller + echo "Unexpected call to \`${FUNCNAME[0]}\` +Did you mean to call \`refute_line\` or \`refute_stderr_line\`?" | + batslib_decorate "ERROR: ${FUNCNAME[0]}" | + fail + return $? + fi + + # 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: ${caller}" \ + | 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: ${caller}" \ + | 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: ${caller}" \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$unexpected" \ + 'line' "${stream_lines[$idx]}" \ + | batslib_decorate 'regular expression should not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$unexpected" \ + 'line' "${stream_lines[$idx]}" \ + | batslib_decorate 'line should not contain substring' \ + | fail + fi + else + if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "${stream_lines[$idx]}" \ + | batslib_decorate 'line should differ' \ + | fail + fi + fi + else + # Line contained in output/error stream. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do + if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then + { local -ar single=( 'regexp' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 < ${#stream_lines[@]}; ++idx )); do + if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then + { local -ar single=( 'substring' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 < ${#stream_lines[@]}; ++idx )); do + if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then + { local -ar single=( 'line' "$unexpected" 'index' "$idx" ) + local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) + 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 ${stream_type}" \ + | fail + return $? + fi + done + fi + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/refute_output.bash b/tests/bash/test_helper/bats-assert/src/refute_output.bash new file mode 100644 index 0000000..d656515 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/refute_output.bash @@ -0,0 +1,246 @@ +# refute_output +# ============= +# +# Summary: Fail if `$output' matches the unexpected output. +# +# Usage: refute_output [-p | -e] [- | [--] ] +# +# Options: +# -p, --partial Match if `unexpected` is a substring of `$output` +# -e, --regexp Treat `unexpected` as an extended regular expression +# -, --stdin Read `unexpected` value from STDIN +# The unexpected value, substring, or regular expression +# +# IO: +# STDIN - [=$1] unexpected output +# STDERR - details, on failure +# error message, on error +# Globals: +# output +# Returns: +# 0 - if output matches the unexpected value/partial/regexp +# 1 - otherwise +# +# This function verifies that a command or function does not produce the unexpected output. +# (It is the logical complement of `assert_output`.) +# Output matching can be literal (the default), partial or by regular expression. +# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. +# +# ## Literal matching +# +# By default, literal matching is performed. +# The assertion fails if `$output` equals the unexpected output. +# +# ```bash +# @test 'refute_output()' { +# run echo 'want' +# refute_output 'want' +# } +# +# @test 'refute_output() with pipe' { +# run echo 'hello' +# echo 'world' | refute_output - +# } +# +# @test 'refute_output() with herestring' { +# run echo 'hello' +# refute_output - <<< world +# } +# ``` +# +# On failure, the output is displayed. +# +# ``` +# -- output equals, but it was expected to differ -- +# output : want +# -- +# ``` +# +# ## Existence +# +# To assert that there is no output at all, omit the matching argument. +# +# ```bash +# @test 'refute_output()' { +# run foo --silent +# refute_output +# } +# ``` +# +# On failure, an error message is displayed. +# +# ``` +# -- unexpected output -- +# expected no output, but output was non-empty +# -- +# ``` +# +# ## Partial matching +# +# Partial matching can be enabled with the `--partial` option (`-p` for short). +# When used, the assertion fails if the unexpected _substring_ is found in `$output`. +# +# ```bash +# @test 'refute_output() partial matching' { +# run echo 'ERROR: no such file or directory' +# refute_output --partial 'ERROR' +# } +# ``` +# +# On failure, the substring and the output are displayed. +# +# ``` +# -- output should not contain substring -- +# substring : ERROR +# output : ERROR: no such file or directory +# -- +# ``` +# +# ## Regular expression matching +# +# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). +# When used, the assertion fails if the *extended regular expression* matches `$output`. +# +# *__Note__: +# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output; +# not individual lines.* +# +# ```bash +# @test 'refute_output() regular expression matching' { +# run echo 'Foobar v0.1.0' +# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' +# } +# ``` +# +# On failure, the regular expression and the output are displayed. +# +# ``` +# -- regular expression should not match output -- +# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ +# output : Foobar v0.1.0 +# -- +# ``` +refute_output() { + __refute_stream "$@" +} + +# refute_stderr +# ============= +# +# Summary: Fail if `$stderr' matches the unexpected output. +# +# Usage: refute_stderr [-p | -e] [- | [--] ] +# +# Options: +# -p, --partial Match if `unexpected` is a substring of `$stderr` +# -e, --regexp Treat `unexpected` as an extended regular expression +# -, --stdin Read `unexpected` value from STDIN +# The unexpected value, substring, or regular expression +# +# IO: +# STDIN - [=$1] unexpected stderr +# STDERR - details, on failure +# error message, on error +# Globals: +# stderr +# Returns: +# 0 - if stderr matches the unexpected value/partial/regexp +# 1 - otherwise +# +# Similar to `refute_output`, this function verifies that a command or function does not produce the unexpected stderr. +# (It is the logical complement of `assert_stderr`.) +# The stderr matching can be literal (the default), partial or by regular expression. +# The unexpected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. +# +refute_stderr() { + __refute_stream "$@" +} + +__refute_stream() { + local -r caller=${FUNCNAME[1]} + local -r stream_type=${caller/refute_/} + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + local -i is_mode_empty=0 + local -i use_stdin=0 + + if [[ ${stream_type} == "output" ]]; then + : "${output?}" + elif [[ ${stream_type} == "stderr" ]]; then + : "${stderr?}" + else + # Not reachable: should be either output or stderr + : + fi + local -r stream="${!stream_type}" + + # Handle options. + if (( $# == 0 )); then + is_mode_empty=1 + fi + + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + -|--stdin) use_stdin=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate "ERROR: ${caller}" \ + | fail + return $? + fi + + # Arguments. + local unexpected + if (( use_stdin )); then + unexpected="$(cat -)" + else + unexpected="${1-}" + fi + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate "ERROR: ${caller}" \ + | fail + return $? + fi + + # Matching. + if (( is_mode_empty )); then + if [ -n "${stream}" ]; then + batslib_print_kv_single_or_multi 6 \ + "${stream_type}" "${stream}" \ + | batslib_decorate "${stream_type} non-empty, but expected no ${stream_type}" \ + | fail + fi + elif (( is_mode_regexp )); then + if [[ ${stream} =~ $unexpected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$unexpected" \ + "${stream_type}" "${stream}" \ + | batslib_decorate "regular expression should not match ${stream_type}" \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${stream} == *"$unexpected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$unexpected" \ + "${stream_type}" "${stream}" \ + | batslib_decorate "${stream_type} should not contain substring" \ + | fail + fi + else + if [[ ${stream} == "$unexpected" ]]; then + batslib_print_kv_single_or_multi 6 \ + "${stream_type}" "${stream}" \ + | batslib_decorate "${stream_type} equals, but it was expected to differ" \ + | fail + fi + fi +} diff --git a/tests/bash/test_helper/bats-assert/src/refute_regex.bash b/tests/bash/test_helper/bats-assert/src/refute_regex.bash new file mode 100644 index 0000000..0918793 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/src/refute_regex.bash @@ -0,0 +1,66 @@ +# `refute_regex` +# +# This function is similar to `refute_equal` but uses pattern matching instead +# of equality, by wrapping `! [[ value =~ pattern ]]`. +# +# Fail if the value (first parameter) matches the pattern (second parameter). +# +# ```bash +# @test 'refute_regex()' { +# refute_regex 'WhatsApp' 'Threema' +# } +# ``` +# +# On failure, the value, the pattern and the match are displayed. +# +# ``` +# @test 'refute_regex()' { +# refute_regex 'WhatsApp' 'What.' +# } +# +# -- value matches regular expression -- +# value : WhatsApp +# pattern : What. +# match : Whats +# case : sensitive +# -- +# ``` +# +# If the value or pattern is longer than one line then it is displayed in +# *multi-line* format. +# +# An error is displayed if the specified extended regular expression is invalid. +# +# For description of the matching behavior, refer to the documentation of the +# `=~` operator in the +# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. +# +# Note that the `BASH_REMATCH` array is available immediately after the +# assertion fails but is fragile, i.e. prone to being overwritten as a side +# effect of other actions like calling `run`. Thus, it's good practice to avoid +# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable +# information the array contains is the matching part of the value which is +# printed in the failing test log, as mentioned above. +refute_regex() { + local -r value="${1}" + local -r pattern="${2}" + + if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then + echo "Invalid extended regular expression: \`${pattern}'" \ + | batslib_decorate 'ERROR: refute_regex' \ + | fail + elif [[ "${value}" =~ ${pattern} ]]; then + if shopt -p nocasematch &>/dev/null; then + local case_sensitive=insensitive + else + local case_sensitive=sensitive + fi + batslib_print_kv_single_or_multi 8 \ + 'value' "${value}" \ + 'pattern' "${pattern}" \ + 'match' "${BASH_REMATCH[0]}" \ + 'case' "${case_sensitive}" \ + | batslib_decorate 'value matches regular expression' \ + | fail + fi +} diff --git a/tests/bash/test_helper/bats-assert/test/assert.bats b/tests/bash/test_helper/bats-assert/test/assert.bats new file mode 100644 index 0000000..d1a34a2 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert.bats @@ -0,0 +1,19 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert() : returns 0 if evaluates to TRUE' { + run assert true + assert_test_pass +} + +@test 'assert() : returns 1 and displays if it evaluates to FALSE' { + run assert false + + assert_test_fail <<'ERR_MSG' + +-- assertion failed -- +expression : false +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_equal.bats b/tests/bash/test_helper/bats-assert/test/assert_equal.bats new file mode 100644 index 0000000..7d3fc86 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_equal.bats @@ -0,0 +1,62 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_equal() : returns 0 if equals ' { + run assert_equal 'a' 'a' + assert_test_pass +} + +@test 'assert_equal() : returns 1 and displays details if does not equal ' { + run assert_equal 'a' 'b' + + assert_test_fail <<'ERR_MSG' + +-- values do not equal -- +expected : b +actual : a +-- +ERR_MSG +} + +@test 'assert_equal() : displays details in multi-line format if is longer than one line' { + run assert_equal $'a 0\na 1' 'b' + + assert_test_fail <<'ERR_MSG' + +-- values do not equal -- +expected (1 lines): + b +actual (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +@test 'assert_equal() : displays details in multi-line format if is longer than one line' { + run assert_equal 'a' $'b 0\nb 1' + + assert_test_fail <<'ERR_MSG' + +-- values do not equal -- +expected (2 lines): + b 0 + b 1 +actual (1 lines): + a +-- +ERR_MSG +} + +@test 'assert_equal() : performs literal matching' { + run assert_equal 'a' '*' + + assert_test_fail <<'ERR_MSG' + +-- values do not equal -- +expected : * +actual : a +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_failure.bats b/tests/bash/test_helper/bats-assert/test/assert_failure.bats new file mode 100644 index 0000000..6291afe --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_failure.bats @@ -0,0 +1,75 @@ +#!/usr/bin/env bats + +load test_helper + +@test "assert_failure(): returns 0 if \`\$status' is not 0" { + run false + run assert_failure + assert_test_pass +} + +@test "assert_failure(): returns 1 and displays details if \`\$status' is 0" { + run bash -c 'echo "a" + exit 0' + run assert_failure + + assert_test_fail <<'ERR_MSG' + +-- command succeeded, but it was expected to fail -- +output : a +-- +ERR_MSG +} + +@test "assert_failure(): displays \`\$output' in multi-line format if it is longer then one line" { + run bash -c 'printf "a 0\na 1" + exit 0' + run assert_failure + + assert_test_fail <<'ERR_MSG' + +-- command succeeded, but it was expected to fail -- +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +@test "assert_failure() : returns 0 if \`\$status' equals " { + run bash -c 'exit 1' + run assert_failure 1 + assert_test_pass +} + +@test "assert_failure() : returns 1 and displays details if \`\$status' does not equal " { + run bash -c 'echo "a" + exit 1' + run assert_failure 2 + + assert_test_fail <<'ERR_MSG' + +-- command failed as expected, but status differs -- +expected : 2 +actual : 1 +output : a +-- +ERR_MSG +} + +@test "assert_failure() : displays \`\$output' in multi-line format if it is longer then one line" { + run bash -c 'printf "a 0\na 1" + exit 1' + run assert_failure 2 + + assert_test_fail <<'ERR_MSG' + +-- command failed as expected, but status differs -- +expected : 2 +actual : 1 +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_line.bats b/tests/bash/test_helper/bats-assert/test/assert_line.bats new file mode 100644 index 0000000..b69ed59 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_line.bats @@ -0,0 +1,362 @@ +#!/usr/bin/env bats + +load test_helper + + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "assert_line() : returns 0 if is a line in \`\${lines[@]}'" { + run printf 'a\nb\nc' + run assert_line 'b' + assert_test_pass +} + +@test "assert_line() : returns 1 and displays details if is not a line in \`\${lines[@]}'" { + run echo 'b' + run assert_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain line -- +line : a +output : b +-- +ERR_MSG +} + +# Output formatting +@test "assert_line() : displays \`\$output' in multi-line format if it is longer than one line" { + run printf 'b 0\nb 1' + run assert_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain line -- +line : a +output (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +# Options +@test 'assert_line() : performs literal matching by default' { + run echo 'a' + run assert_line '*' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain line -- +line : * +output : a +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'assert_line() -p : enables partial matching' { + run printf 'a\n_b_\nc' + run assert_line -p 'b' + assert_test_pass +} + +@test 'assert_line() --partial : enables partial matching' { + run printf 'a\n_b_\nc' + run assert_line --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_line() --partial : returns 0 if is a substring in any line in \`\${lines[@]}'" { + run printf 'a\n_b_\nc' + run assert_line --partial 'b' + assert_test_pass +} + +@test "assert_line() --partial : returns 1 and displays details if is not a substring in any lines in \`\${lines[@]}'" { + run echo 'b' + run assert_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no output line contains substring -- +substring : a +output : b +-- +ERR_MSG +} + +# Output formatting +@test "assert_line() --partial : displays \`\$output' in multi-line format if it is longer than one line" { + run printf 'b 0\nb 1' + run assert_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no output line contains substring -- +substring : a +output (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'assert_line() -e : enables regular expression matching' { + run printf 'a\n_b_\nc' + run assert_line -e '^.b' + assert_test_pass +} + +@test 'assert_line() --regexp : enables regular expression matching' { + run printf 'a\n_b_\nc' + run assert_line --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "assert_line() --regexp : returns 0 if matches any line in \`\${lines[@]}'" { + run printf 'a\n_b_\nc' + run assert_line --regexp '^.b' + assert_test_pass +} + +@test "assert_line() --regexp : returns 1 and displays details if does not match any lines in \`\${lines[@]}'" { + run echo 'b' + run assert_line --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- no output line matches regular expression -- +regexp : ^.a +output : b +-- +ERR_MSG +} + +# Output formatting +@test "assert_line() --regexp : displays \`\$output' in multi-line format if longer than one line" { + run printf 'b 0\nb 1' + run assert_line --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- no output line matches regular expression -- +regexp : ^.a +output (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +@test 'assert_line() -n : matches against the -th line only' { + run printf 'a\nb\nc' + run assert_line -n 1 'b' + assert_test_pass +} + +@test 'assert_line() --index : matches against the -th line only' { + run printf 'a\nb\nc' + run assert_line --index 1 'b' + assert_test_pass +} + +@test 'assert_line() --index : returns 1 and displays an error message if is not an integer' { + run assert_line --index 1a + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_line -- +`--index' requires an integer argument: `1a' +-- +ERR_MSG +} + + +# +# Literal matching +# + +# Correctness +@test "assert_line() --index : returns 0 if equals \`\${lines[]}'" { + run printf 'a\nb\nc' + run assert_line --index 1 'b' + assert_test_pass +} + +@test "assert_line() --index : returns 1 and displays details if does not equal \`\${lines[]}'" { + run printf 'a\nb\nc' + run assert_line --index 1 'a' + + assert_test_fail <<'ERR_MSG' + +-- line differs -- +index : 1 +expected : a +actual : b +-- +ERR_MSG +} + +# Options +@test 'assert_line() --index : performs literal matching by default' { + run printf 'a\nb\nc' + run assert_line --index 1 '*' + + assert_test_fail <<'ERR_MSG' + +-- line differs -- +index : 1 +expected : * +actual : b +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'assert_line() --index -p : enables partial matching' { + run printf 'a\n_b_\nc' + run assert_line --index 1 -p 'b' + assert_test_pass +} + +@test 'assert_line() --index --partial : enables partial matching' { + run printf 'a\n_b_\nc' + run assert_line --index 1 --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_line() --index --partial : returns 0 if is a substring in \`\${lines[]}'" { + run printf 'a\n_b_\nc' + run assert_line --index 1 --partial 'b' + assert_test_pass +} + +@test "assert_line() --index --partial : returns 1 and displays details if is not a substring in \`\${lines[]}'" { + run printf 'b 0\nb 1' + run assert_line --index 1 --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- line does not contain substring -- +index : 1 +substring : a +line : b 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'assert_line() --index -e : enables regular expression matching' { + run printf 'a\n_b_\nc' + run assert_line --index 1 -e '^.b' + assert_test_pass +} + +@test 'assert_line() --index --regexp : enables regular expression matching' { + run printf 'a\n_b_\nc' + run assert_line --index 1 --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "assert_line() --index --regexp : returns 0 if matches \`\${lines[]}'" { + run printf 'a\n_b_\nc' + run assert_line --index 1 --regexp '^.b' + assert_test_pass +} + +@test "assert_line() --index --regexp : returns 1 and displays details if does not match \`\${lines[]}'" { + run printf 'a\nb\nc' + run assert_line --index 1 --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match line -- +index : 1 +regexp : ^.a +line : b +-- +ERR_MSG +} + + +############################################################################### +# Common +############################################################################### + +@test "assert_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_line --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_line -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test 'assert_line() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run assert_line --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_line -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + +@test "assert_line(): \`--' stops parsing options" { + run printf 'a\n-p\nc' + run assert_line -- '-p' + assert_test_pass +} + +@test "__assert_line(): call to __assert_line shows error" { + run __assert_line + assert_test_fail <<'ERR_MSG' + +-- ERROR: __assert_line -- +Unexpected call to `__assert_line` +Did you mean to call `assert_line` or `assert_stderr_line`? +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats b/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats new file mode 100644 index 0000000..42b5315 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats @@ -0,0 +1,57 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'assert_not_equal() : returns 0 if does not equal ' { + run assert_not_equal foo bar + assert_test_pass + + run assert_not_equal "foo" "bar" + assert_test_pass + + run assert_not_equal "foo" "" + assert_test_pass + + run assert_not_equal "" "foo" + assert_test_pass +} + +@test 'assert_not_equal() : returns 1 and displays details if equals ' { + run assert_not_equal 'foobar' 'foobar' + assert_test_fail <<'ERR_MSG' + +-- values should not be equal -- +unexpected : foobar +actual : foobar +-- +ERR_MSG + + run assert_not_equal 1 1 + assert_test_fail <<'ERR_MSG' + +-- values should not be equal -- +unexpected : 1 +actual : 1 +-- +ERR_MSG +} + +@test 'assert_not_equal() : displays details in multi-line format if and are longer than one line' { + run assert_not_equal $'foo\nbar' $'foo\nbar' + assert_test_fail <<'ERR_MSG' + +-- values should not be equal -- +unexpected (2 lines): + foo + bar +actual (2 lines): + foo + bar +-- +ERR_MSG +} + +@test 'assert_not_equal() : performs literal matching' { + run assert_not_equal 'a' '*' + assert_test_pass +} \ No newline at end of file diff --git a/tests/bash/test_helper/bats-assert/test/assert_output.bats b/tests/bash/test_helper/bats-assert/test/assert_output.bats new file mode 100644 index 0000000..6671203 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_output.bats @@ -0,0 +1,296 @@ +#!/usr/bin/env bats + +load test_helper + +# +# Literal matching +# + +# Correctness +@test "assert_output() : returns 0 if equals \`\$output'" { + run echo 'a' + run assert_output 'a' + assert_test_pass +} + +@test "assert_output() : returns 1 and displays details if does not equal \`\$output'" { + run echo 'b' + run assert_output 'a' + + assert_test_fail <<'ERR_MSG' + +-- output differs -- +expected : a +actual : b +-- +ERR_MSG +} + +@test 'assert_output(): succeeds if output is non-empty' { + run echo 'a' + run assert_output + + assert_test_pass +} + +@test 'assert_output(): fails if output is empty' { + run echo '' + run assert_output + + assert_test_fail <<'ERR_MSG' + +-- no output -- +expected non-empty output, but output was empty +-- +ERR_MSG +} + +@test 'assert_output() - : reads from STDIN' { + run echo 'a' + run assert_output - < from STDIN' { + run echo 'a' + run assert_output --stdin <: displays details in multi-line format if \`\$output' is longer than one line" { + run printf 'b 0\nb 1' + run assert_output 'a' + + assert_test_fail <<'ERR_MSG' + +-- output differs -- +expected (1 lines): + a +actual (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_output() : displays details in multi-line format if is longer than one line' { + run echo 'b' + run assert_output $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- output differs -- +expected (2 lines): + a 0 + a 1 +actual (1 lines): + b +-- +ERR_MSG +} + +# Options +@test 'assert_output() : performs literal matching by default' { + run echo 'a' + run assert_output '*' + + assert_test_fail <<'ERR_MSG' + +-- output differs -- +expected : * +actual : a +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +@test 'assert_output() -p : enables partial matching' { + run echo 'abc' + run assert_output -p 'b' + assert_test_pass +} + +@test 'assert_output() --partial : enables partial matching' { + run echo 'abc' + run assert_output --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_output() --partial : returns 0 if is a substring in \`\$output'" { + run printf 'a\nb\nc' + run assert_output --partial 'b' + assert_test_pass +} + +@test "assert_output() --partial : returns 1 and displays details if is not a substring in \`\$output'" { + run echo 'b' + run assert_output --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain substring -- +substring : a +output : b +-- +ERR_MSG +} + +# Output formatting +@test "assert_output() --partial : displays details in multi-line format if \`\$output' is longer than one line" { + run printf 'b 0\nb 1' + run assert_output --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain substring -- +substring (1 lines): + a +output (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_output() --partial : displays details in multi-line format if is longer than one line' { + run echo 'b' + run assert_output --partial $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- output does not contain substring -- +substring (2 lines): + a 0 + a 1 +output (1 lines): + b +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +@test 'assert_output() -e : enables regular expression matching' { + run echo 'abc' + run assert_output -e '^a' + assert_test_pass +} + +@test 'assert_output() --regexp : enables regular expression matching' { + run echo 'abc' + run assert_output --regexp '^a' + assert_test_pass +} + +# Correctness +@test "assert_output() --regexp : returns 0 if matches \`\$output'" { + run printf 'a\nb\nc' + run assert_output --regexp '.*b.*' + assert_test_pass +} + +@test "assert_output() --regexp : returns 1 and displays details if does not match \`\$output'" { + run echo 'b' + run assert_output --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match output -- +regexp : .*a.* +output : b +-- +ERR_MSG +} + +# Output formatting +@test "assert_output() --regexp : displays details in multi-line format if \`\$output' is longer than one line" { + run printf 'b 0\nb 1' + run assert_output --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match output -- +regexp (1 lines): + .*a.* +output (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_output() --regexp : displays details in multi-line format if is longer than one line' { + run echo 'b' + run assert_output --regexp $'.*a\nb.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match output -- +regexp (2 lines): + .*a + b.* +output (1 lines): + b +-- +ERR_MSG +} + +# Error handling +@test 'assert_output() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run assert_output --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_output -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + + +# +# Common +# + +@test "assert_output(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_output --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_output -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test "assert_output(): \`--' stops parsing options" { + run echo '-p' + run assert_output -- '-p' + assert_test_pass +} + +@test "__assert_stream(): call to __assert_stream shows error" { + run __assert_stream + assert_test_fail <<'ERR_MSG' + +-- ERROR: __assert_stream -- +Unexpected call to `__assert_stream` +Did you mean to call `assert_output` or `assert_stderr`? +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_regex.bats b/tests/bash/test_helper/bats-assert/test/assert_regex.bats new file mode 100644 index 0000000..0a9c902 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_regex.bats @@ -0,0 +1,87 @@ +#!/usr/bin/env bats + +load test_helper + +# +# Literal matching +# + +# Correctness +@test "assert_regex() : succeeds if a substring matches extended regular expression " { + run assert_regex 'abc' '^[a-z]b[c-z]+' + assert_test_pass +} + +@test "assert_regex() : fails if no substring matches extended regular expression " { + run assert_regex 'bcd' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value : bcd +pattern : ^[a-z]b[c-z]+ +case : sensitive +-- +ERR_MSG +} + +@test "assert_regex() : provides results in BASH_REMATCH" { + unset -v BASH_REMATCH + + assert_regex 'abcd' 'b.d' + declare -p BASH_REMATCH + [ "${BASH_REMATCH[0]}" = 'bcd' ] +} + +@test "assert_regex() : matches case-insensitively when 'nocasematch' is set" { + shopt -s nocasematch + + assert_regex 'aBc' 'ABC' +} + +@test "assert_regex() : outputs multi-line nicely when it fails" { + run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value (2 lines): + bcd + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +case (1 lines): + sensitive +-- +ERR_MSG + + shopt -s nocasematch + run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value does not match regular expression -- +value (2 lines): + bcd + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +case (1 lines): + insensitive +-- +ERR_MSG +} + +# Error handling +@test "assert_regex() : returns 1 and displays an error message if is not a valid extended regular expression" { + run assert_regex value '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_regex -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + +@test "assert_regex allows regex matching empty string (see #53)" { + run assert_regex any_value '.*' + assert_success +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_stderr.bats b/tests/bash/test_helper/bats-assert/test/assert_stderr.bats new file mode 100644 index 0000000..ebedde8 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_stderr.bats @@ -0,0 +1,298 @@ +#!/usr/bin/env bats + +load test_helper + +setup_file() { + bats_require_minimum_version 1.5.0 +} + +echo_err() { + echo "$@" >&2 +} + +printf_err() { + # shellcheck disable=2059 + printf "$@" >&2 +} + +# +# Literal matching +# + +# Correctness +@test "assert_stderr() : returns 0 if equals \`\$stderr'" { + run --separate-stderr echo_err 'a' + run assert_stderr 'a' + assert_test_pass +} + +@test "assert_stderr() : returns 1 and displays details if does not equal \`\$stderr'" { + run --separate-stderr echo_err 'b' + run assert_stderr 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr differs -- +expected : a +actual : b +-- +ERR_MSG +} + +@test 'assert_stderr(): succeeds if stderr is non-empty' { + run --separate-stderr echo_err 'a' + run assert_stderr + + assert_test_pass +} + +@test 'assert_stderr(): fails if stderr is empty' { + run --separate-stderr echo_err '' + run assert_stderr + + assert_test_fail <<'ERR_MSG' + +-- no stderr -- +expected non-empty stderr, but stderr was empty +-- +ERR_MSG +} + +@test 'assert_stderr() - : reads from STDIN' { + run --separate-stderr echo_err 'a' + run assert_stderr - < from STDIN' { + run --separate-stderr echo_err 'a' + run assert_stderr --stdin <: displays details in multi-line format if \`\$stderr' is longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr differs -- +expected (1 lines): + a +actual (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_stderr() : displays details in multi-line format if is longer than one line' { + run --separate-stderr echo_err 'b' + run assert_stderr $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- stderr differs -- +expected (2 lines): + a 0 + a 1 +actual (1 lines): + b +-- +ERR_MSG +} + +# Options +@test 'assert_stderr() : performs literal matching by default' { + run --separate-stderr echo_err 'a' + run assert_stderr '*' + + assert_test_fail <<'ERR_MSG' + +-- stderr differs -- +expected : * +actual : a +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +@test 'assert_stderr() -p : enables partial matching' { + run --separate-stderr echo_err 'abc' + run assert_stderr -p 'b' + assert_test_pass +} + +@test 'assert_stderr() --partial : enables partial matching' { + run --separate-stderr echo_err 'abc' + run assert_stderr --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_stderr() --partial : returns 0 if is a substring in \`\$stderr'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr --partial 'b' + assert_test_pass +} + +@test "assert_stderr() --partial : returns 1 and displays details if is not a substring in \`\$stderr'" { + run --separate-stderr echo_err 'b' + run assert_stderr --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain substring -- +substring : a +stderr : b +-- +ERR_MSG +} + +# stderr formatting +@test "assert_stderr() --partial : displays details in multi-line format if \`\$stderr' is longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain substring -- +substring (1 lines): + a +stderr (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_stderr() --partial : displays details in multi-line format if is longer than one line' { + run --separate-stderr echo_err 'b' + run assert_stderr --partial $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain substring -- +substring (2 lines): + a 0 + a 1 +stderr (1 lines): + b +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +@test 'assert_stderr() -e : enables regular expression matching' { + run --separate-stderr echo_err 'abc' + run assert_stderr -e '^a' + assert_test_pass +} + +@test 'assert_stderr() --regexp : enables regular expression matching' { + run --separate-stderr echo_err 'abc' + run assert_stderr --regexp '^a' + assert_test_pass +} + +# Correctness +@test "assert_stderr() --regexp : returns 0 if matches \`\$stderr'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr --regexp '.*b.*' + assert_test_pass +} + +@test "assert_stderr() --regexp : returns 1 and displays details if does not match \`\$stderr'" { + run --separate-stderr echo_err 'b' + run assert_stderr --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match stderr -- +regexp : .*a.* +stderr : b +-- +ERR_MSG +} + +# stderr formatting +@test "assert_stderr() --regexp : displays details in multi-line format if \`\$stderr' is longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match stderr -- +regexp (1 lines): + .*a.* +stderr (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +@test 'assert_stderr() --regexp : displays details in multi-line format if is longer than one line' { + run --separate-stderr echo_err 'b' + run assert_stderr --regexp $'.*a\nb.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match stderr -- +regexp (2 lines): + .*a + b.* +stderr (1 lines): + b +-- +ERR_MSG +} + +# Error handling +@test 'assert_stderr() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run assert_stderr --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_stderr -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + + +# +# Common +# + +@test "assert_stderr(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_stderr --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_stderr -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test "assert_stderr(): \`--' stops parsing options" { + run --separate-stderr echo_err '-p' + run assert_stderr -- '-p' + assert_test_pass +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats b/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats new file mode 100644 index 0000000..bb3a071 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats @@ -0,0 +1,364 @@ +#!/usr/bin/env bats + +load test_helper + +setup_file() { + bats_require_minimum_version 1.5.0 +} + +echo_err() { + echo "$@" >&2 +} + +printf_err() { + # shellcheck disable=2059 + printf "$@" >&2 +} + + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "assert_stderr_line() : returns 0 if is a line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line 'b' + assert_test_pass +} + +@test "assert_stderr_line() : returns 1 and displays details if is not a line in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'b' + run assert_stderr_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain line -- +line : a +stderr : b +-- +ERR_MSG +} + +# stderr formatting +@test "assert_stderr_line() : displays \`\$stderr' in multi-line format if it is longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain line -- +line : a +stderr (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + +# Options +@test 'assert_stderr_line() : performs literal matching by default' { + run --separate-stderr echo_err 'a' + run assert_stderr_line '*' + + assert_test_fail <<'ERR_MSG' + +-- stderr does not contain line -- +line : * +stderr : a +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'assert_stderr_line() -p : enables partial matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line -p 'b' + assert_test_pass +} + +@test 'assert_stderr_line() --partial : enables partial matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_stderr_line() --partial : returns 0 if is a substring in any line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --partial 'b' + assert_test_pass +} + +@test "assert_stderr_line() --partial : returns 1 and displays details if is not a substring in any lines in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'b' + run assert_stderr_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no stderr line contains substring -- +substring : a +stderr : b +-- +ERR_MSG +} + +# stderr formatting +@test "assert_stderr_line() --partial : displays \`\$stderr' in multi-line format if it is longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no stderr line contains substring -- +substring : a +stderr (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'assert_stderr_line() -e : enables regular expression matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line -e '^.b' + assert_test_pass +} + +@test 'assert_stderr_line() --regexp : enables regular expression matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "assert_stderr_line() --regexp : returns 0 if matches any line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --regexp '^.b' + assert_test_pass +} + +@test "assert_stderr_line() --regexp : returns 1 and displays details if does not match any lines in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'b' + run assert_stderr_line --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- no stderr line matches regular expression -- +regexp : ^.a +stderr : b +-- +ERR_MSG +} + +# stderr formatting +@test "assert_stderr_line() --regexp : displays \`\$stderr' in multi-line format if longer than one line" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr_line --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- no stderr line matches regular expression -- +regexp : ^.a +stderr (2 lines): + b 0 + b 1 +-- +ERR_MSG +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +@test 'assert_stderr_line() -n : matches against the -th line only' { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line -n 1 'b' + assert_test_pass +} + +@test 'assert_stderr_line() --index : matches against the -th line only' { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line --index 1 'b' + assert_test_pass +} + +@test 'assert_stderr_line() --index : returns 1 and displays an error message if is not an integer' { + run assert_stderr_line --index 1a + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_stderr_line -- +`--index' requires an integer argument: `1a' +-- +ERR_MSG +} + + +# +# Literal matching +# + +# Correctness +@test "assert_stderr_line() --index : returns 0 if equals \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line --index 1 'b' + assert_test_pass +} + +@test "assert_stderr_line() --index : returns 1 and displays details if does not equal \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line --index 1 'a' + + assert_test_fail <<'ERR_MSG' + +-- line differs -- +index : 1 +expected : a +actual : b +-- +ERR_MSG +} + +# Options +@test 'assert_stderr_line() --index : performs literal matching by default' { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line --index 1 '*' + + assert_test_fail <<'ERR_MSG' + +-- line differs -- +index : 1 +expected : * +actual : b +-- +ERR_MSG +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'assert_stderr_line() --index -p : enables partial matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 -p 'b' + assert_test_pass +} + +@test 'assert_stderr_line() --index --partial : enables partial matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 --partial 'b' + assert_test_pass +} + +# Correctness +@test "assert_stderr_line() --index --partial : returns 0 if is a substring in \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 --partial 'b' + assert_test_pass +} + +@test "assert_stderr_line() --index --partial : returns 1 and displays details if is not a substring in \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'b 0\nb 1' + run assert_stderr_line --index 1 --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- line does not contain substring -- +index : 1 +substring : a +line : b 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'assert_stderr_line() --index -e : enables regular expression matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 -e '^.b' + assert_test_pass +} + +@test 'assert_stderr_line() --index --regexp : enables regular expression matching' { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "assert_stderr_line() --index --regexp : returns 0 if matches \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\n_b_\nc' + run assert_stderr_line --index 1 --regexp '^.b' + assert_test_pass +} + +@test "assert_stderr_line() --index --regexp : returns 1 and displays details if does not match \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run assert_stderr_line --index 1 --regexp '^.a' + + assert_test_fail <<'ERR_MSG' + +-- regular expression does not match line -- +index : 1 +regexp : ^.a +line : b +-- +ERR_MSG +} + + +############################################################################### +# Common +############################################################################### + +@test "assert_stderr_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run assert_stderr_line --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_stderr_line -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test 'assert_stderr_line() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run assert_stderr_line --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: assert_stderr_line -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + +@test "assert_stderr_line(): \`--' stops parsing options" { + run --separate-stderr printf_err 'a\n-p\nc' + run assert_stderr_line -- '-p' + assert_test_pass +} diff --git a/tests/bash/test_helper/bats-assert/test/assert_success.bats b/tests/bash/test_helper/bats-assert/test/assert_success.bats new file mode 100644 index 0000000..66f5aa7 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/assert_success.bats @@ -0,0 +1,40 @@ +#!/usr/bin/env bats + +load test_helper + +@test "assert_success(): returns 0 if \`\$status' is 0" { + run true + run assert_success + + assert_test_pass +} + +@test "assert_success(): returns 1 and displays details if \`\$status' is not 0" { + run bash -c 'echo "a" + exit 1' + run assert_success + + assert_test_fail <<'ERR_MSG' + +-- command failed -- +status : 1 +output : a +-- +ERR_MSG +} + +@test "assert_success(): displays \`\$output' in multi-line format if it is longer than one line" { + run bash -c 'printf "a 0\na 1" + exit 1' + run assert_success + + assert_test_fail <<'ERR_MSG' + +-- command failed -- +status : 1 +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/refute.bats b/tests/bash/test_helper/bats-assert/test/refute.bats new file mode 100644 index 0000000..f9e2737 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute.bats @@ -0,0 +1,18 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'refute() : returns 0 if evaluates to FALSE' { + run refute false + assert_test_pass +} + +@test 'refute() : returns 1 and displays if it evaluates to TRUE' { + run refute true + assert_test_fail <<'ERR_MSG' + +-- assertion succeeded, but it was expected to fail -- +expression : true +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/refute_line.bats b/tests/bash/test_helper/bats-assert/test/refute_line.bats new file mode 100644 index 0000000..e0ad762 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute_line.bats @@ -0,0 +1,355 @@ +#!/usr/bin/env bats + +load test_helper + + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "refute_line() : returns 0 if is not a line in \`\${lines[@]}'" { + run printf 'a\nb\nc' + run refute_line 'd' + assert_test_pass +} + +@test "refute_line() : returns 1 and displays details if is not a line in \`\${lines[@]}'" { + run echo 'a' + run refute_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- line should not be in output -- +line : a +index : 0 +output : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_line() : displays \`\$output' in multi-line format if it is longer than one line" { + run printf 'a 0\na 1\na 2' + run refute_line 'a 1' + + assert_test_fail <<'ERR_MSG' + +-- line should not be in output -- +line : a 1 +index : 1 +output (3 lines): + a 0 +> a 1 + a 2 +-- +ERR_MSG +} + +# Options +@test 'refute_line() : performs literal matching by default' { + run echo 'a' + run refute_line '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_line() -p : enables partial matching' { + run printf 'a\nb\nc' + run refute_line -p 'd' + assert_test_pass +} + +@test 'refute_line() --partial : enables partial matching' { + run printf 'a\nb\nc' + run refute_line --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_line() --partial : returns 0 if is not a substring in any line in \`\${lines[@]}'" { + run printf 'a\nb\nc' + run refute_line --partial 'd' + assert_test_pass +} + +@test "refute_line() --partial : returns 1 and displays details if is a substring in any line in \`\${lines[@]}'" { + run echo 'a' + run refute_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no line should contain substring -- +substring : a +index : 0 +output : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_line() --partial : displays \`\$output' in multi-line format if it is longer than one line" { + run printf 'a\nabc\nc' + run refute_line --partial 'b' + + assert_test_fail <<'ERR_MSG' + +-- no line should contain substring -- +substring : b +index : 1 +output (3 lines): + a +> abc + c +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_line() -e : enables regular expression matching' { + run printf 'a\nb\nc' + run refute_line -e '^.d' + assert_test_pass +} + +@test 'refute_line() --regexp : enables regular expression matching' { + run printf 'a\nb\nc' + run refute_line --regexp '^.d' + assert_test_pass +} + +# Correctness +@test "refute_line() --regexp : returns 0 if does not match any line in \`\${lines[@]}'" { + run printf 'a\nb\nc' + run refute_line --regexp '.*d.*' + assert_test_pass +} + +@test "refute_line() --regexp : returns 1 and displays details if matches any lines in \`\${lines[@]}'" { + run echo 'a' + run refute_line --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- no line should match the regular expression -- +regexp : .*a.* +index : 0 +output : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_line() --regexp : displays \`\$output' in multi-line format if longer than one line" { + run printf 'a\nabc\nc' + run refute_line --regexp '.*b.*' + + assert_test_fail <<'ERR_MSG' + +-- no line should match the regular expression -- +regexp : .*b.* +index : 1 +output (3 lines): + a +> abc + c +-- +ERR_MSG +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +@test 'refute_line() -n : matches against the -th line only' { + run printf 'a\nb\nc' + run refute_line -n 1 'd' + assert_test_pass +} + +@test 'refute_line() --index : matches against the -th line only' { + run printf 'a\nb\nc' + run refute_line --index 1 'd' + assert_test_pass +} + +@test 'refute_line() --index : returns 1 and displays an error message if is not an integer' { + run refute_line --index 1a + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_line -- +`--index' requires an integer argument: `1a' +-- +ERR_MSG +} + + +# +# Literal matching +# + +# Correctness +@test "refute_line() --index : returns 0 if does not equal \`\${lines[]}'" { + run printf 'a\nb\nc' + run refute_line --index 1 'd' + assert_test_pass +} + +@test "refute_line() --index : returns 1 and displays details if equals \`\${lines[]}'" { + run printf 'a\nb\nc' + run refute_line --index 1 'b' + + assert_test_fail <<'ERR_MSG' + +-- line should differ -- +index : 1 +line : b +-- +ERR_MSG +} + +# Options +@test 'refute_line() --index : performs literal matching by default' { + run printf 'a\nb\nc' + run refute_line --index 1 '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_line() --index -p : enables partial matching' { + run printf 'a\nb\nc' + run refute_line --index 1 -p 'd' + assert_test_pass +} + +@test 'refute_line() --index --partial : enables partial matching' { + run printf 'a\nb\nc' + run refute_line --index 1 --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_line() --index --partial : returns 0 if is not a substring in \`\${lines[]}'" { + run printf 'a\nabc\nc' + run refute_line --index 1 --partial 'd' + assert_test_pass +} + +@test "refute_line() --index --partial : returns 1 and displays details if is a substring in \`\${lines[]}'" { + run printf 'a\nabc\nc' + run refute_line --index 1 --partial 'b' + + assert_test_fail <<'ERR_MSG' + +-- line should not contain substring -- +index : 1 +substring : b +line : abc +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_line() --index -e : enables regular expression matching' { + run printf 'a\nb\nc' + run refute_line --index 1 -e '^.b' + assert_test_pass +} + +@test 'refute_line() --index --regexp : enables regular expression matching' { + run printf 'a\nb\nc' + run refute_line --index 1 --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "refute_line() --index --regexp : returns 0 if does not match \`\${lines[]}'" { + run printf 'a\nabc\nc' + run refute_line --index 1 --regexp '.*d.*' + assert_test_pass +} + +@test "refute_line() --index --regexp : returns 1 and displays details if matches \`\${lines[]}'" { + run printf 'a\nabc\nc' + run refute_line --index 1 --regexp '.*b.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match line -- +index : 1 +regexp : .*b.* +line : abc +-- +ERR_MSG +} + + +############################################################################### +# Common +############################################################################### + +@test "refute_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_line --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_line -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test 'refute_line() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run refute_line --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_line -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + +@test "refute_line(): \`--' stops parsing options" { + run printf 'a\n--\nc' + run refute_line -- '-p' + assert_test_pass +} + +@test "__refute_stream_line(): call to __refute_stream_line shows error" { + run __refute_stream_line + assert_test_fail <<'ERR_MSG' + +-- ERROR: __refute_stream_line -- +Unexpected call to `__refute_stream_line` +Did you mean to call `refute_line` or `refute_stderr_line`? +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/refute_output.bats b/tests/bash/test_helper/bats-assert/test/refute_output.bats new file mode 100644 index 0000000..9d8711d --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute_output.bats @@ -0,0 +1,230 @@ +#!/usr/bin/env bats + +load test_helper + + +# +# Literal matching +# + +# Correctness +@test "refute_output() : returns 0 if does not equal \`\$output'" { + run echo 'b' + run refute_output 'a' + assert_test_pass +} + +@test "refute_output() : returns 1 and displays details if equals \`\$output'" { + run echo 'a' + run refute_output 'a' + + assert_test_fail <<'ERR_MSG' + +-- output equals, but it was expected to differ -- +output : a +-- +ERR_MSG +} + +@test 'refute_output(): succeeds if output is empty' { + run echo '' + run refute_output + + assert_test_pass +} + +@test 'refute_output(): fails if output is non-empty' { + run echo 'a' + run refute_output + + assert_test_fail <<'ERR_MSG' + +-- output non-empty, but expected no output -- +output : a +-- +ERR_MSG +} + +@test 'refute_output() - : reads from STDIN' { + run echo '-' + run refute_output - < from STDIN' { + run echo '--stdin' + run refute_output --stdin <: displays details in multi-line format if necessary' { + run printf 'a 0\na 1' + run refute_output $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- output equals, but it was expected to differ -- +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +# Options +@test 'refute_output() : performs literal matching by default' { + run echo 'a' + run refute_output '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_output() -p : enables partial matching' { + run echo 'abc' + run refute_output -p 'd' + assert_test_pass +} + +@test 'refute_output() --partial : enables partial matching' { + run echo 'abc' + run refute_output --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_output() --partial : returns 0 if is not a substring in \`\$output'" { + run printf 'a\nb\nc' + run refute_output --partial 'd' + assert_test_pass +} + +@test "refute_output() --partial : returns 1 and displays details if is a substring in \`\$output'" { + run echo 'a' + run refute_output --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- output should not contain substring -- +substring : a +output : a +-- +ERR_MSG +} + +# Output formatting +@test 'refute_output() --partial : displays details in multi-line format if necessary' { + run printf 'a 0\na 1' + run refute_output --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- output should not contain substring -- +substring (1 lines): + a +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_output() -e : enables regular expression matching' { + run echo 'abc' + run refute_output -e '^d' + assert_test_pass +} + +@test 'refute_output() --regexp : enables regular expression matching' { + run echo 'abc' + run refute_output --regexp '^d' + assert_test_pass +} + +# Correctness +@test "refute_output() --regexp : returns 0 if does not match \`\$output'" { + run printf 'a\nb\nc' + run refute_output --regexp '.*d.*' + assert_test_pass +} + +@test "refute_output() --regexp : returns 1 and displays details if matches \`\$output'" { + run echo 'a' + run refute_output --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match output -- +regexp : .*a.* +output : a +-- +ERR_MSG +} + +# Output formatting +@test 'refute_output() --regexp : displays details in multi-line format if necessary' { + run printf 'a 0\na 1' + run refute_output --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match output -- +regexp (1 lines): + .*a.* +output (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +# Error handling +@test 'refute_output() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run refute_output --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_output -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + + +# +# Common +# + +@test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_output --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_output -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test "refute_output(): \`--' stops parsing options" { + run echo '--' + run refute_output -- '-p' + assert_test_pass +} diff --git a/tests/bash/test_helper/bats-assert/test/refute_regex.bats b/tests/bash/test_helper/bats-assert/test/refute_regex.bats new file mode 100644 index 0000000..c1fd1b2 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute_regex.bats @@ -0,0 +1,98 @@ +#!/usr/bin/env bats + +load test_helper + +# +# Literal matching +# + +# Correctness +@test "refute_regex() : fails if a substring matches extended regular expression " { + run refute_regex 'abc' '^[a-z]b' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value : abc +pattern : ^[a-z]b +match : ab +case : sensitive +-- +ERR_MSG +} + +@test "refute_regex() : succeeds if no substring matches extended regular expression " { + run refute_regex 'bcd' '^[a-z]b[c-z]+' + assert_test_pass +} + +@test "refute_regex() : provides results in BASH_REMATCH on failure" { + unset -v BASH_REMATCH + + refute_regex 'abcd' 'b.d' \ + || { + declare -p BASH_REMATCH && \ + [ "${BASH_REMATCH[0]}" = 'bcd' ] + } +} + +@test "refute_regex() : matches case-insensitively when 'nocasematch' is set" { + shopt -s nocasematch + + run refute_regex 'aBc' 'ABC' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value : aBc +pattern : ABC +match : aBc +case : insensitive +-- +ERR_MSG +} + +@test "refute_regex() : outputs multi-line nicely when it fails" { + run refute_regex $'abc\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value (2 lines): + abc + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +match (1 lines): + abc +case (1 lines): + sensitive +-- +ERR_MSG + + shopt -s nocasematch + run refute_regex $'aBc\n123' '^[a-z]b[c-z]+' + assert_test_fail <<'ERR_MSG' + +-- value matches regular expression -- +value (2 lines): + aBc + 123 +pattern (1 lines): + ^[a-z]b[c-z]+ +match (1 lines): + aBc +case (1 lines): + insensitive +-- +ERR_MSG +} + +# Error handling +@test "refute_regex() : returns 1 and displays an error message if is not a valid extended regular expression" { + run refute_regex value '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_regex -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} diff --git a/tests/bash/test_helper/bats-assert/test/refute_stderr.bats b/tests/bash/test_helper/bats-assert/test/refute_stderr.bats new file mode 100644 index 0000000..d1ec134 --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute_stderr.bats @@ -0,0 +1,242 @@ +#!/usr/bin/env bats + +load test_helper + +setup_file() { + bats_require_minimum_version 1.5.0 +} + +echo_err() { + echo "$@" >&2 +} + +printf_err() { + # shellcheck disable=2059 + printf "$@" >&2 +} + +# +# Literal matching +# + +# Correctness +@test "refute_stderr() : returns 0 if does not equal \`\$stderr'" { + run --separate-stderr echo_err 'b' + run refute_stderr 'a' + assert_test_pass +} + +@test "refute_stderr() : returns 1 and displays details if equals \`\$stderr'" { + run --separate-stderr echo_err 'a' + run refute_stderr 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr equals, but it was expected to differ -- +stderr : a +-- +ERR_MSG +} + +@test 'refute_stderr(): succeeds if stderr is empty' { + run --separate-stderr echo_err '' + run refute_stderr + + assert_test_pass +} + +@test 'refute_stderr(): fails if stderr is non-empty' { + run --separate-stderr echo_err 'a' + run refute_stderr + + assert_test_fail <<'ERR_MSG' + +-- stderr non-empty, but expected no stderr -- +stderr : a +-- +ERR_MSG +} + +@test 'refute_stderr() - : reads from STDIN' { + run --separate-stderr echo_err '-' + run refute_stderr - < from STDIN' { + run --separate-stderr echo_err '--stdin' + run refute_stderr --stdin <: displays details in multi-line format if necessary' { + run --separate-stderr printf_err 'a 0\na 1' + run refute_stderr $'a 0\na 1' + + assert_test_fail <<'ERR_MSG' + +-- stderr equals, but it was expected to differ -- +stderr (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +# Options +@test 'refute_stderr() : performs literal matching by default' { + run --separate-stderr echo_err 'a' + run refute_stderr '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_stderr() -p : enables partial matching' { + run --separate-stderr echo_err 'abc' + run refute_stderr -p 'd' + assert_test_pass +} + +@test 'refute_stderr() --partial : enables partial matching' { + run --separate-stderr echo_err 'abc' + run refute_stderr --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_stderr() --partial : returns 0 if is not a substring in \`\$stderr'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr --partial 'd' + assert_test_pass +} + +@test "refute_stderr() --partial : returns 1 and displays details if is a substring in \`\$stderr'" { + run --separate-stderr echo_err 'a' + run refute_stderr --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr should not contain substring -- +substring : a +stderr : a +-- +ERR_MSG +} + +# Output formatting +@test 'refute_stderr() --partial : displays details in multi-line format if necessary' { + run --separate-stderr printf_err 'a 0\na 1' + run refute_stderr --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- stderr should not contain substring -- +substring (1 lines): + a +stderr (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_stderr() -e : enables regular expression matching' { + run --separate-stderr echo_err 'abc^d' + run refute_stderr -e '^d' + assert_test_pass +} + +@test 'refute_stderr() --regexp : enables regular expression matching' { + run --separate-stderr echo_err 'abc' + run refute_stderr --regexp '^d' + assert_test_pass +} + +# Correctness +@test "refute_stderr() --regexp : returns 0 if does not match \`\$stderr'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr --regexp '.*d.*' + assert_test_pass +} + +@test "refute_stderr() --regexp : returns 1 and displays details if matches \`\$stderr'" { + run --separate-stderr echo_err 'a' + run refute_stderr --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match stderr -- +regexp : .*a.* +stderr : a +-- +ERR_MSG +} + +# Output formatting +@test 'refute_stderr() --regexp : displays details in multi-line format if necessary' { + run --separate-stderr printf_err 'a 0\na 1' + run refute_stderr --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match stderr -- +regexp (1 lines): + .*a.* +stderr (2 lines): + a 0 + a 1 +-- +ERR_MSG +} + +# Error handling +@test 'refute_stderr() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run refute_stderr --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_stderr -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + + +# +# Common +# + +@test "refute_stderr(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_stderr --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_stderr -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test "refute_stderr(): \`--' stops parsing options" { + run echo_err '--' + run refute_stderr -- '-p' + assert_test_pass +} diff --git a/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats b/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats new file mode 100644 index 0000000..ecb210b --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats @@ -0,0 +1,356 @@ +#!/usr/bin/env bats + +load test_helper + +setup_file() { + bats_require_minimum_version 1.5.0 +} + +echo_err() { + echo "$@" >&2 +} + +printf_err() { + # shellcheck disable=2059 + printf "$@" >&2 +} + +############################################################################### +# Containing a line +############################################################################### + +# +# Literal matching +# + +# Correctness +@test "refute_stderr_line() : returns 0 if is not a line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line 'd' + assert_test_pass +} + +@test "refute_stderr_line() : returns 1 and displays details if is not a line in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'a' + run refute_stderr_line 'a' + + assert_test_fail <<'ERR_MSG' + +-- line should not be in stderr -- +line : a +index : 0 +stderr : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_stderr_line() : displays \`\$stderr' in multi-line format if it is longer than one line" { + run --separate-stderr printf_err 'a 0\na 1\na 2' + run refute_stderr_line 'a 1' + + assert_test_fail <<'ERR_MSG' + +-- line should not be in stderr -- +line : a 1 +index : 1 +stderr (3 lines): + a 0 +> a 1 + a 2 +-- +ERR_MSG +} + +# Options +@test 'refute_stderr_line() : performs literal matching by default' { + run --separate-stderr echo_err 'a' + run refute_stderr_line '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_stderr_line() -p : enables partial matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line -p 'd' + assert_test_pass +} + +@test 'refute_stderr_line() --partial : enables partial matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_stderr_line() --partial : returns 0 if is not a substring in any line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --partial 'd' + assert_test_pass +} + +@test "refute_stderr_line() --partial : returns 1 and displays details if is a substring in any line in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'a' + run refute_stderr_line --partial 'a' + + assert_test_fail <<'ERR_MSG' + +-- no line should contain substring -- +substring : a +index : 0 +stderr : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_stderr_line() --partial : displays \`\$stderr' in multi-line format if it is longer than one line" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --partial 'b' + + assert_test_fail <<'ERR_MSG' + +-- no line should contain substring -- +substring : b +index : 1 +stderr (3 lines): + a +> abc + c +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_stderr_line() -e : enables regular expression matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line -e '^.d' + assert_test_pass +} + +@test 'refute_stderr_line() --regexp : enables regular expression matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --regexp '^.d' + assert_test_pass +} + +# Correctness +@test "refute_stderr_line() --regexp : returns 0 if does not match any line in \`\${stderr_lines[@]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --regexp '.*d.*' + assert_test_pass +} + +@test "refute_stderr_line() --regexp : returns 1 and displays details if matches any lines in \`\${stderr_lines[@]}'" { + run --separate-stderr echo_err 'a' + run refute_stderr_line --regexp '.*a.*' + + assert_test_fail <<'ERR_MSG' + +-- no line should match the regular expression -- +regexp : .*a.* +index : 0 +stderr : a +-- +ERR_MSG +} + +# Output formatting +@test "refute_stderr_line() --regexp : displays \`\$stderr' in multi-line format if longer than one line" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --regexp '.*b.*' + + assert_test_fail <<'ERR_MSG' + +-- no line should match the regular expression -- +regexp : .*b.* +index : 1 +stderr (3 lines): + a +> abc + c +-- +ERR_MSG +} + + +############################################################################### +# Matching single line: `-n' and `--index' +############################################################################### + +# Options +@test 'refute_stderr_line() -n : matches against the -th line only' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line -n 1 'd' + assert_test_pass +} + +@test 'refute_stderr_line() --index : matches against the -th line only' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 'd' + assert_test_pass +} + +@test 'refute_stderr_line() --index : returns 1 and displays an error message if is not an integer' { + run refute_stderr_line --index 1a + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_stderr_line -- +`--index' requires an integer argument: `1a' +-- +ERR_MSG +} + + +# +# Literal matching +# + +# Correctness +@test "refute_stderr_line() --index : returns 0 if does not equal \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 'd' + assert_test_pass +} + +@test "refute_stderr_line() --index : returns 1 and displays details if equals \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 'b' + + assert_test_fail <<'ERR_MSG' + +-- line should differ -- +index : 1 +line : b +-- +ERR_MSG +} + +# Options +@test 'refute_stderr_line() --index : performs literal matching by default' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 '*' + assert_test_pass +} + + +# +# Partial matching: `-p' and `--partial' +# + +# Options +@test 'refute_stderr_line() --index -p : enables partial matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 -p 'd' + assert_test_pass +} + +@test 'refute_stderr_line() --index --partial : enables partial matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 --partial 'd' + assert_test_pass +} + +# Correctness +@test "refute_stderr_line() --index --partial : returns 0 if is not a substring in \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --index 1 --partial 'd' + assert_test_pass +} + +@test "refute_stderr_line() --index --partial : returns 1 and displays details if is a substring in \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --index 1 --partial 'b' + + assert_test_fail <<'ERR_MSG' + +-- line should not contain substring -- +index : 1 +substring : b +line : abc +-- +ERR_MSG +} + + +# +# Regular expression matching: `-e' and `--regexp' +# + +# Options +@test 'refute_stderr_line() --index -e : enables regular expression matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 -e '[^.b]' + assert_test_pass +} + +@test 'refute_stderr_line() --index --regexp : enables regular expression matching' { + run --separate-stderr printf_err 'a\nb\nc' + run refute_stderr_line --index 1 --regexp '^.b' + assert_test_pass +} + +# Correctness +@test "refute_stderr_line() --index --regexp : returns 0 if does not match \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --index 1 --regexp '.*d.*' + assert_test_pass +} + +@test "refute_stderr_line() --index --regexp : returns 1 and displays details if matches \`\${stderr_lines[]}'" { + run --separate-stderr printf_err 'a\nabc\nc' + run refute_stderr_line --index 1 --regexp '.*b.*' + + assert_test_fail <<'ERR_MSG' + +-- regular expression should not match line -- +index : 1 +regexp : .*b.* +line : abc +-- +ERR_MSG +} + + +############################################################################### +# Common +############################################################################### + +@test "refute_stderr_line(): \`--partial' and \`--regexp' are mutually exclusive" { + run refute_stderr_line --partial --regexp + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_stderr_line -- +`--partial' and `--regexp' are mutually exclusive +-- +ERR_MSG +} + +@test 'refute_stderr_line() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { + run refute_stderr_line --regexp '[.*' + + assert_test_fail <<'ERR_MSG' + +-- ERROR: refute_stderr_line -- +Invalid extended regular expression: `[.*' +-- +ERR_MSG +} + +@test "refute_stderr_line(): \`--' stops parsing options" { + run --separate-stderr printf_err 'a\n--\nc' + run refute_stderr_line -- '-p' + assert_test_pass +} diff --git a/tests/bash/test_helper/bats-assert/test/test_helper.bash b/tests/bash/test_helper/bats-assert/test/test_helper.bash new file mode 100644 index 0000000..af1ee0a --- /dev/null +++ b/tests/bash/test_helper/bats-assert/test/test_helper.bash @@ -0,0 +1,30 @@ +# Load dependencies. +BATS_LIB_PATH=$PWD/node_modules:${BATS_LIB_PATH-} +bats_load_library 'bats-support' + +# Load library. +load '../load' + +# validate that bats-assert is safe to use under -u +set -u + +: "${status:=}" +: "${lines:=}" +: "${output:=}" +: "${stderr:=}" +: "${stderr_lines:=}" + +assert_test_pass() { + test "$status" -eq 0 + test "${#lines[@]}" -eq 0 +} + +assert_test_fail() { + local err_msg="${1-$(cat -)}" + local num_lines + num_lines="$(printf '%s' "$err_msg" | wc -l)" + + test "$status" -eq 1 + test "${#lines[@]}" -eq "$num_lines" + test "$output" == "$err_msg" +} diff --git a/tests/bash/test_helper/bats-support/.github/dependabot.yaml b/tests/bash/test_helper/bats-support/.github/dependabot.yaml new file mode 100644 index 0000000..c519084 --- /dev/null +++ b/tests/bash/test_helper/bats-support/.github/dependabot.yaml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + target-branch: master diff --git a/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml b/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml new file mode 100644 index 0000000..b93fefe --- /dev/null +++ b/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '44 10 * * 6' + push: + branches: [ "master" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + with: + sarif_file: results.sarif diff --git a/tests/bash/test_helper/bats-support/.github/workflows/tests.yml b/tests/bash/test_helper/bats-support/.github/workflows/tests.yml new file mode 100644 index 0000000..097283b --- /dev/null +++ b/tests/bash/test_helper/bats-support/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 + - uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2 + - uses: actions/cache@8492260343ad570701412c2f464a5877dc76bace # v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - run: npm install + - run: npm test diff --git a/tests/bash/test_helper/bats-support/.gitignore b/tests/bash/test_helper/bats-support/.gitignore new file mode 100644 index 0000000..05f900c --- /dev/null +++ b/tests/bash/test_helper/bats-support/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/package-lock.json +/yarn.lock +/bats-support-*.tgz diff --git a/tests/bash/test_helper/bats-support/CHANGELOG.md b/tests/bash/test_helper/bats-support/CHANGELOG.md new file mode 100644 index 0000000..324d247 --- /dev/null +++ b/tests/bash/test_helper/bats-support/CHANGELOG.md @@ -0,0 +1,46 @@ +# 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-11-29 + +### Added + +- Restricting invocation to specific locations with + `batslib_is_caller()` + + +## [0.2.0] - 2016-03-22 + +### Added + +- `npm` support +- Reporting arbitrary failures with `fail()` (moved from `bats-assert`) + +### Changed + +- Library renamed to `bats-support` + + +## 0.1.0 - 2016-02-16 + +### Added + +- Two-column key-value formatting with `batslib_print_kv_single()` +- Multi-line key-value formatting with `batslib_print_kv_multi()` +- Mixed formatting with `batslib_print_kv_single_or_multi()` +- Header and footer decoration with `batslib_decorate()` +- Prefixing lines with `batslib_prefix()` +- Marking lines with `batslib_mark()` +- Common output function `batslib_err()` +- Line counting with `batslib_count_lines()` +- Checking whether a text is one line long with + `batslib_is_single_line()` +- Determining key width for two-column and mixed formatting with + `batslib_get_max_single_line_key_width()` + + +[0.3.0]: https://github.com/ztombol/bats-support/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/ztombol/bats-support/compare/v0.1.0...v0.2.0 diff --git a/tests/bash/test_helper/bats-support/LICENSE b/tests/bash/test_helper/bats-support/LICENSE new file mode 100644 index 0000000..670154e --- /dev/null +++ b/tests/bash/test_helper/bats-support/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/tests/bash/test_helper/bats-support/README.md b/tests/bash/test_helper/bats-support/README.md new file mode 100644 index 0000000..0554349 --- /dev/null +++ b/tests/bash/test_helper/bats-support/README.md @@ -0,0 +1,181 @@ +# bats-support + +[![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/bats-core/bats-support/master/LICENSE) +[![GitHub release](https://img.shields.io/github/release/bats-core/bats-support.svg)](https://github.com/bats-core/bats-support/releases/latest) +[![Build Status](https://github.com/bats-core/bats-support/workflows/Tests/badge.svg)](https://github.com/bats-core/bats-support/actions?query=workflow%3ATests) + + +`bats-support` is a supporting library providing common functions to +test helper libraries written for [Bats][bats]. + +Features: +- [error reporting](#error-reporting) +- [output formatting](#output-formatting) +- [language tools](#language-and-execution) + +See the [shared documentation][bats-docs] to learn how to install and +load this library. + +If you want to use this library in your own helpers or just want to +learn about its internals see the developer documentation in the [source +files](src). + + +## Error reporting + +### `fail` + +Display an error message and fail. This function provides a convenient +way to report failure in arbitrary situations. You can use it to +implement your own helpers when the ones available do not meet your +needs. Other functions use it internally as well. + +```bash +@test 'fail()' { + fail 'this test always fails' +} +``` + +The message can also be specified on the standard input. + +```bash +@test 'fail() with pipe' { + echo 'this test always fails' | fail +} +``` + +This function always fails and simply outputs the given message. + +``` +this test always fails +``` + + +## Output formatting + +Many test helpers need to produce human readable output. This library +provides a simple way to format simple messages and key value pairs, and +display them on the standard error. + + +### Simple message + +Simple messages without structure, e.g. one-line error messages, are +simply wrapped in a header and a footer to help them stand out. + +``` +-- ERROR: assert_output -- +`--partial' and `--regexp' are mutually exclusive +-- +``` + + +### Key-Value pairs + +Some helpers, e.g. [assertions][bats-assert], structure output as +key-value pairs. This library provides two ways to format them. + +When the value is one line long, a pair can be displayed in a columnar +fashion called ***two-column*** format. + +``` +-- output differs -- +expected : want +actual : have +-- +``` + +When the value is longer than one line, the key and value must be +displayed on separate lines. First, the key is displayed along with the +number of lines in the value. Then, the value, indented by two spaces +for added readability, starting on the next line. This is called +***multi-line*** format. + +``` +-- command failed -- +status : 1 +output (2 lines): + Error! Something went terribly wrong! + Our engineers are panicing... \`>`;/ +-- +``` + +Sometimes, for clarity, it is a good idea to display related values also +in this format, even if they are just one line long. + +``` +-- output differs -- +expected (1 lines): + want +actual (3 lines): + have 1 + have 2 + have 3 +-- +``` + +## Language and Execution + +### Restricting invocation to specific locations + +Sometimes a helper may work properly only when called from a certain +location. Because it depends on variables to be set or some other side +effect. + +A good example is cleaning up temporary files only if the test has +succeeded. The outcome of a test is only available in `teardown`. Thus, +to avoid programming mistakes, it makes sense to restrict such a +clean-up helper to that function. + +`batslib_is_caller` checks the call stack and returns `0` if the caller +was invoked from a given function, and `1` otherwise. This function +becomes really useful with the `--indirect` option, which allows calls +through intermediate functions, e.g. the calling function may be called +from a function that was called from the given function. + +Staying with the example above, the following code snippet implements a +helper that is restricted to `teardown` or any function called +indirectly from it. + +```shell +clean_up() { + # Check caller. + if batslib_is_caller --indirect 'teardown'; then + echo "Must be called from \`teardown'" \ + | batslib_decorate 'ERROR: clean_up' \ + | fail + return $? + fi + + # Body goes here... +} +``` + +In some cases a helper may be called from multiple locations. For +example, a logging function that uses the test name, description or +number, information only available in `setup`, `@test` or `teardown`, to +distinguish entries. The following snippet implements this restriction. + +```shell +log_test() { + # Check caller. + if ! ( batslib_is_caller --indirect 'setup' \ + || batslib_is_caller --indirect "$BATS_TEST_NAME" \ + || batslib_is_caller --indirect 'teardown' ) + then + echo "Must be called from \`setup', \`@test' or \`teardown'" \ + | batslib_decorate 'ERROR: log_test' \ + | fail + return $? + fi + + # Body goes here... +} +``` + + + + +[bats]: https://github.com/bats-core/bats-core +[bats-docs]: https://github.com/bats-core/bats-docs +[bats-assert]: https://github.com/bats-core/bats-assert diff --git a/tests/bash/test_helper/bats-support/load.bash b/tests/bash/test_helper/bats-support/load.bash new file mode 100644 index 0000000..3f52722 --- /dev/null +++ b/tests/bash/test_helper/bats-support/load.bash @@ -0,0 +1,11 @@ +# Preserve path at the time this file was sourced +# This prevents using of user-defined mocks/stubs that modify the PATH + +# BATS_SAVED_PATH was introduced in bats-core v1.10.0 +# if it is already set, we can use its more robust value +# else we try to recreate it here +BATS_SAVED_PATH="${BATS_SAVED_PATH-$PATH}" + +source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash" diff --git a/tests/bash/test_helper/bats-support/package.json b/tests/bash/test_helper/bats-support/package.json new file mode 100644 index 0000000..5015687 --- /dev/null +++ b/tests/bash/test_helper/bats-support/package.json @@ -0,0 +1,30 @@ +{ + "name": "bats-support", + "version": "0.3.0", + "description": "Supporting library for Bats test helpers", + "homepage": "https://github.com/jasonkarns/bats-support", + "license": "CC0-1.0", + "author": "Zoltán Tömböl (https://github.com/ztombol)", + "contributors": [ + "Jason Karns (http://jason.karns.name)" + ], + "repository": "github:jasonkarns/bats-support", + "bugs": "https://github.com/jasonkarns/bats-support/issues", + "directories": { + "lib": "src", + "test": "test" + }, + "files": [ + "load.bash", + "src" + ], + "scripts": { + "test": "bats ${CI+-t} test" + }, + "devDependencies": { + "bats": "^1" + }, + "peerDependencies": { + "bats": "0.4 || ^1" + } +} diff --git a/tests/bash/test_helper/bats-support/src/error.bash b/tests/bash/test_helper/bats-support/src/error.bash new file mode 100644 index 0000000..e5d9791 --- /dev/null +++ b/tests/bash/test_helper/bats-support/src/error.bash @@ -0,0 +1,41 @@ +# +# bats-support - Supporting library for Bats test helpers +# +# Written in 2016 by Zoltan Tombol +# +# 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 +# . +# + +# +# error.bash +# ---------- +# +# Functions implementing error reporting. Used by public helper +# functions or test suits directly. +# + +# Fail and display a message. When no parameters are specified, the +# message is read from the standard input. Other functions use this to +# report failure. +# +# Globals: +# none +# Arguments: +# $@ - [=STDIN] message +# Returns: +# 1 - always +# Inputs: +# STDIN - [=$@] message +# Outputs: +# STDERR - message +fail() { + (( $# == 0 )) && batslib_err || batslib_err "$@" + return 1 +} diff --git a/tests/bash/test_helper/bats-support/src/lang.bash b/tests/bash/test_helper/bats-support/src/lang.bash new file mode 100644 index 0000000..c57e299 --- /dev/null +++ b/tests/bash/test_helper/bats-support/src/lang.bash @@ -0,0 +1,73 @@ +# +# bats-util - Various auxiliary functions for Bats +# +# Written in 2016 by Zoltan Tombol +# +# 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 +# . +# + +# +# lang.bash +# --------- +# +# Bash language and execution related functions. Used by public helper +# functions. +# + +# Check whether the calling function was called from a given function. +# +# By default, direct invocation is checked. The function succeeds if the +# calling function was called directly from the given function. In other +# words, if the given function is the next element on the call stack. +# +# When `--indirect' is specified, indirect invocation is checked. The +# function succeeds if the calling function was called from the given +# function with any number of intermediate calls. In other words, if the +# given function can be found somewhere on the call stack. +# +# Direct invocation is a form of indirect invocation with zero +# intermediate calls. +# +# Globals: +# FUNCNAME +# Options: +# -i, --indirect - check indirect invocation +# Arguments: +# $1 - calling function's name +# Returns: +# 0 - current function was called from the given function +# 1 - otherwise +batslib_is_caller() { + local -i is_mode_direct=1 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -i|--indirect) is_mode_direct=0; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + # Arguments. + local -r func="$1" + + # Check call stack. + if (( is_mode_direct )); then + [[ $func == "${FUNCNAME[2]}" ]] && return 0 + else + local -i depth + for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do + [[ $func == "${FUNCNAME[$depth]}" ]] && return 0 + done + fi + + return 1 +} diff --git a/tests/bash/test_helper/bats-support/src/output.bash b/tests/bash/test_helper/bats-support/src/output.bash new file mode 100644 index 0000000..10ca8ae --- /dev/null +++ b/tests/bash/test_helper/bats-support/src/output.bash @@ -0,0 +1,279 @@ +# +# bats-support - Supporting library for Bats test helpers +# +# Written in 2016 by Zoltan Tombol +# +# 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 +# . +# + +# +# output.bash +# ----------- +# +# Private functions implementing output formatting. Used by public +# helper functions. +# + +# Print a message to the standard error. When no parameters are +# specified, the message is read from the standard input. +# +# Globals: +# none +# Arguments: +# $@ - [=STDIN] message +# Returns: +# none +# Inputs: +# STDIN - [=$@] message +# Outputs: +# STDERR - message +batslib_err() { + { if (( $# > 0 )); then + echo "$@" + else + PATH="$BATS_SAVED_PATH" command cat - + fi + } >&2 +} + +# Count the number of lines in the given string. +# +# TODO(ztombol): Fix tests and remove this note after #93 is resolved! +# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not +# give the same result as `${#lines[@]}' when the output contains +# empty lines. +# See PR #93 (https://github.com/sstephenson/bats/pull/93). +# +# Globals: +# none +# Arguments: +# $1 - string +# Returns: +# none +# Outputs: +# STDOUT - number of lines +batslib_count_lines() { + local -i n_lines=0 + local line + while IFS='' read -r line || [[ -n $line ]]; do + (( ++n_lines )) + done < <(printf '%s' "$1") + echo "$n_lines" +} + +# Determine whether all strings are single-line. +# +# Globals: +# none +# Arguments: +# $@ - strings +# Returns: +# 0 - all strings are single-line +# 1 - otherwise +batslib_is_single_line() { + for string in "$@"; do + (( $(batslib_count_lines "$string") > 1 )) && return 1 + done + return 0 +} + +# Determine the length of the longest key that has a single-line value. +# +# This function is useful in determining the correct width of the key +# column in two-column format when some keys may have multi-line values +# and thus should be excluded. +# +# Globals: +# none +# Arguments: +# $odd - key +# $even - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - length of longest key +batslib_get_max_single_line_key_width() { + local -i max_len=-1 + while (( $# != 0 )); do + local -i key_len="${#1}" + batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" + shift 2 + done + echo "$max_len" +} + +# Print key-value pairs in two-column format. +# +# Keys are displayed in the first column, and their corresponding values +# in the second. To evenly line up values, the key column is fixed-width +# and its width is specified with the first parameter (possibly computed +# using `batslib_get_max_single_line_key_width'). +# +# Globals: +# none +# Arguments: +# $1 - width of key column +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_single() { + local -ir col_width="$1"; shift + while (( $# != 0 )); do + printf '%-*s : %s\n' "$col_width" "$1" "$2" + shift 2 + done +} + +# Print key-value pairs in multi-line format. +# +# The key is displayed first with the number of lines of its +# corresponding value in parenthesis. Next, starting on the next line, +# the value is displayed. For better readability, it is recommended to +# indent values using `batslib_prefix'. +# +# Globals: +# none +# Arguments: +# $odd - key +# $even - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_multi() { + while (( $# != 0 )); do + printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" + printf '%s\n' "$2" + shift 2 + done +} + +# Print all key-value pairs in either two-column or multi-line format +# depending on whether all values are single-line. +# +# If all values are single-line, print all pairs in two-column format +# with the specified key column width (identical to using +# `batslib_print_kv_single'). +# +# Otherwise, print all pairs in multi-line format after indenting values +# with two spaces for readability (identical to using `batslib_prefix' +# and `batslib_print_kv_multi') +# +# Globals: +# none +# Arguments: +# $1 - width of key column (for two-column format) +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_single_or_multi() { + local -ir width="$1"; shift + local -a pairs=( "$@" ) + + local -a values=() + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + values+=( "${pairs[$i]}" ) + done + + if batslib_is_single_line "${values[@]}"; then + batslib_print_kv_single "$width" "${pairs[@]}" + else + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" + done + batslib_print_kv_multi "${pairs[@]}" + fi +} + +# Prefix each line read from the standard input with the given string. +# +# Globals: +# none +# Arguments: +# $1 - [= ] prefix string +# Returns: +# none +# Inputs: +# STDIN - lines +# Outputs: +# STDOUT - prefixed lines +batslib_prefix() { + local -r prefix="${1:- }" + local line + while IFS='' read -r line || [[ -n $line ]]; do + printf '%s%s\n' "$prefix" "$line" + done +} + +# Mark select lines of the text read from the standard input by +# overwriting their beginning with the given string. +# +# Usually the input is indented by a few spaces using `batslib_prefix' +# first. +# +# Globals: +# none +# Arguments: +# $1 - marking string +# $@ - indices (zero-based) of lines to mark +# Returns: +# none +# Inputs: +# STDIN - lines +# Outputs: +# STDOUT - lines after marking +batslib_mark() { + local -r symbol="$1"; shift + # Sort line numbers. + set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) + + local line + local -i idx=0 + while IFS='' read -r line || [[ -n $line ]]; do + if (( ${1:--1} == idx )); then + printf '%s\n' "${symbol}${line:${#symbol}}" + shift + else + printf '%s\n' "$line" + fi + (( ++idx )) + done +} + +# Enclose the input text in header and footer lines. +# +# The header contains the given string as title. The output is preceded +# and followed by an additional newline to make it stand out more. +# +# Globals: +# none +# Arguments: +# $1 - title +# Returns: +# none +# Inputs: +# STDIN - text +# Outputs: +# STDOUT - decorated text +batslib_decorate() { + echo + echo "-- $1 --" + PATH="$BATS_SAVED_PATH" command cat - + echo '--' + echo +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats b/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats new file mode 100644 index 0000000..89b9db1 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats @@ -0,0 +1,43 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_err() : displays ' { + run batslib_err 'm1' 'm2' + [ "$status" -eq 0 ] + [ "$output" == 'm1 m2' ] +} + +@test 'batslib_err(): reads from STDIN' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + echo 'm1' 'm2' | batslib_err" + [ "$status" -eq 0 ] + [ "$output" == 'm1 m2' ] +} + +@test 'batslib_err() works with modified path' { + export PATH="$BATS_TEST_DIRNAME:$PATH" + echo 'm1 m2' | { + # Verify stub + run which cat + [ "$status" -eq 0 ] + [ "$output" = "$BATS_TEST_DIRNAME/cat" ] + # Should still work + run batslib_err + [ "$status" -eq 0 ] + [ "$output" == 'm1 m2' ] + } +} + +@test 'batslib_err() works with mock function' { + echo 'm1 m2' | { + function cat { + echo "Mocked cat" + } + [ "$(cat)" = "Mocked cat" ] + # Should still work + run batslib_err + [ "$status" -eq 0 ] + [ "$output" == 'm1 m2' ] + } +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats b/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats new file mode 100644 index 0000000..ea172c3 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_count_lines() : displays the number of lines in ' { + run batslib_count_lines $'a\nb\nc\n' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} + +@test 'batslib_count_lines() : counts the last line when it is not terminated by a newline' { + run batslib_count_lines $'a\nb\nc' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} + +@test 'batslib_count_lines() : counts empty lines' { + run batslib_count_lines $'\n\n\n' + [ "$status" -eq 0 ] + [ "$output" == '3' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats b/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats new file mode 100644 index 0000000..484b64d --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats @@ -0,0 +1,13 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_is_single_line() : returns 0 if all are single-line' { + run batslib_is_single_line 'a' $'b\n' 'c' + [ "$status" -eq 0 ] +} + +@test 'batslib_is_single_line() : returns 1 if at least one of is longer than one line' { + run batslib_is_single_line 'a' $'b\nb' 'c' + [ "$status" -eq 1 ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats b/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats new file mode 100644 index 0000000..e6af161 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_get_max_single_line_key_width() : displays the length of the longest key' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' 'v 3' ) + run batslib_get_max_single_line_key_width "${pairs[@]}" + [ "$status" -eq 0 ] + [ "$output" == '5' ] +} + +@test 'batslib_get_max_single_line_key_width() : only considers keys with single-line values' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' $'v\n3' ) + run batslib_get_max_single_line_key_width "${pairs[@]}" + [ "$status" -eq 0 ] + [ "$output" == '4' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats b/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats new file mode 100644 index 0000000..7637897 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_single() : displays in two-column format with wide key column' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' ) + run batslib_print_kv_single 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} + +@test 'batslib_print_kv_single() : does not truncate keys when the column is too narrow' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' 'v 2' + 'k __3' 'v 3' ) + run batslib_print_kv_single 0 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats b/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats new file mode 100644 index 0000000..6ad4b3d --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats @@ -0,0 +1,19 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_multi() : displays in multi-line format' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' ) + run batslib_print_kv_multi "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '7' ] + [ "${lines[0]}" == 'k _1 (1 lines):' ] + [ "${lines[1]}" == 'v 1' ] + [ "${lines[2]}" == 'k 2 (2 lines):' ] + [ "${lines[3]}" == 'v 2-1' ] + [ "${lines[4]}" == 'v 2-2' ] + [ "${lines[5]}" == 'k __3 (1 lines):' ] + [ "${lines[6]}" == 'v 3' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats b/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats new file mode 100644 index 0000000..c20d101 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats @@ -0,0 +1,31 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_print_kv_single_or_multi() : displays in two-column format if all values are one line long' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2 ' 'v 2' + 'k __3' 'v 3' ) + run batslib_print_kv_single_or_multi 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '3' ] + [ "${lines[0]}" == 'k _1 : v 1' ] + [ "${lines[1]}" == 'k 2 : v 2' ] + [ "${lines[2]}" == 'k __3 : v 3' ] +} + +@test 'batslib_print_kv_single_or_multi() : displays in multi-line format if at least one value is longer than one line' { + local -ar pairs=( 'k _1' 'v 1' + 'k 2' $'v 2-1\nv 2-2' + 'k __3' 'v 3' ) + run batslib_print_kv_single_or_multi 5 "${pairs[@]}" + [ "$status" -eq 0 ] + [ "${#lines[@]}" == '7' ] + [ "${lines[0]}" == 'k _1 (1 lines):' ] + [ "${lines[1]}" == ' v 1' ] + [ "${lines[2]}" == 'k 2 (2 lines):' ] + [ "${lines[3]}" == ' v 2-1' ] + [ "${lines[4]}" == ' v 2-2' ] + [ "${lines[5]}" == 'k __3 (1 lines):' ] + [ "${lines[6]}" == ' v 3' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats b/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats new file mode 100644 index 0000000..817fd33 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats @@ -0,0 +1,43 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_prefix() : prefixes each line of the input with ' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf 'a\nb\nc\n' | batslib_prefix '_'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '_a' ] + [ "${lines[1]}" == '_b' ] + [ "${lines[2]}" == '_c' ] +} + +@test 'batslib_prefix() : prefixes the last line when it is not terminated by a newline' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf 'a\nb\nc' | batslib_prefix '_'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '_a' ] + [ "${lines[1]}" == '_b' ] + [ "${lines[2]}" == '_c' ] +} + +@test 'batslib_prefix() : prefixes empty lines' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf '\n\n\n' | batslib_prefix '_'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '_' ] + [ "${lines[1]}" == '_' ] + [ "${lines[2]}" == '_' ] +} + +@test 'batslib_prefix(): default to two spaces' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf 'a\nb\nc\n' | batslib_prefix" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats b/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats new file mode 100644 index 0000000..c5d0975 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats @@ -0,0 +1,72 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_mark() : marks the -th line of the input with ' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c\n' | batslib_mark '>' 0" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '>a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_mark() : marks multiple lines when is in ascending order' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c\n' | batslib_mark '>' 1 2" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() : marks multiple lines when is in random order' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c\n d\n' | batslib_mark '>' 2 1 3" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 4 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] + [ "${lines[3]}" == '>d' ] +} + +@test 'batslib_mark() : ignores duplicate indices' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c\n' | batslib_mark '>' 1 2 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == '>b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() : outputs the input untouched if is the empty string' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c\n' | batslib_mark '' 1" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == ' c' ] +} + +@test 'batslib_mark() : marks the last line when it is not terminated by a newline' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf ' a\n b\n c' | batslib_mark '>' 2" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == ' a' ] + [ "${lines[1]}" == ' b' ] + [ "${lines[2]}" == '>c' ] +} + +@test 'batslib_mark() : does not truncate if it is longer than the marked line' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + printf '\n' | batslib_mark '>' 0" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 1 ] + [ "${lines[0]}" == '>' ] +} diff --git a/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats b/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats new file mode 100644 index 0000000..a4d4fcb --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'batslib_decorate() : encloses the input in a footer line and a header line containing <title>' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + echo 'body' | batslib_decorate 'title'" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- title --' ] + [ "${lines[1]}" == 'body' ] + [ "${lines[2]}" == '--' ] +} + +@test 'batslib_decorate() works with modified path' { + export PATH="$BATS_TEST_DIRNAME:$PATH" + echo body | { + # Verify stub + run which cat + [ "$status" -eq 0 ] + [ "$output" = "$BATS_TEST_DIRNAME/cat" ] + # Should still work + run batslib_decorate 'title' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- title --' ] + [ "${lines[1]}" == 'body' ] + [ "${lines[2]}" == '--' ] + } +} + +@test 'batslib_decorate() works with mock function' { + echo body | { + function cat { + echo "Mocked cat" + } + [ "$(cat)" = "Mocked cat" ] + # Should still work + run batslib_decorate 'title' + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + [ "${lines[0]}" == '-- title --' ] + [ "${lines[1]}" == 'body' ] + [ "${lines[2]}" == '--' ] + } +} diff --git a/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats b/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats new file mode 100644 index 0000000..1d8691a --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats @@ -0,0 +1,16 @@ +#!/usr/bin/env bats + +load test_helper + +@test 'fail() <message>: returns 1 and displays <message>' { + run fail 'message' + [ "$status" -eq 1 ] + [ "$output" == 'message' ] +} + +@test 'fail(): reads <message> from STDIN' { + run bash -c "source '${TEST_MAIN_DIR}/load.bash' + echo 'message' | fail" + [ "$status" -eq 1 ] + [ "$output" == 'message' ] +} diff --git a/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats b/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats new file mode 100644 index 0000000..68fd59b --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats @@ -0,0 +1,88 @@ +#!/usr/bin/env bats + +load 'test_helper' + + +# Test functions +test_func_lvl_2() { + test_func_lvl_1 "$@" +} + +test_func_lvl_1() { + test_func_lvl_0 "$@" +} + +test_func_lvl_0() { + batslib_is_caller "$@" +} + + +# +# Direct invocation +# + +# Interface +@test 'batslib_is_caller() <function>: returns 0 if the current function was called directly from <function>' { + run test_func_lvl_1 test_func_lvl_1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'batslib_is_caller() <function>: returns 1 if the current function was not called directly from <function>' { + run test_func_lvl_0 test_func_lvl_1 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 0 ] +} + +# Correctness +@test 'batslib_is_caller() <function>: the current function does not appear on the call stack' { + run test_func_lvl_0 test_func_lvl_0 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 0 ] +} + + +# +# Indirect invocation +# + +# Options +test_i_indirect() { + run test_func_lvl_2 "$@" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'batslib_is_caller() -i <function>: enables indirect checking' { + test_i_indirect -i test_func_lvl_2 +} + +@test 'batslib_is_caller() --indirect <function>: enables indirect checking' { + test_i_indirect --indirect test_func_lvl_2 +} + +# Interface +@test 'batslib_is_caller() --indirect <function>: returns 0 if the current function was called indirectly from <function>' { + run test_func_lvl_2 --indirect test_func_lvl_2 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'batslib_is_caller() --indirect <function>: returns 1 if the current function was not called indirectly from <function>' { + run test_func_lvl_1 --indirect test_func_lvl_2 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 0 ] +} + +# Correctness +@test 'batslib_is_caller() --indirect <function>: direct invocation is a special case of indirect invocation with zero intermediate calls' { + run test_func_lvl_1 --indirect test_func_lvl_1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test 'batslib_is_caller() --indirect <function>: the current function does not appear on the call stack' { + run test_func_lvl_0 --indirect test_func_lvl_0 + [ "$status" -eq 1 ] + [ "${#lines[@]}" -eq 0 ] +} diff --git a/tests/bash/test_helper/bats-support/test/cat b/tests/bash/test_helper/bats-support/test/cat new file mode 100644 index 0000000..c45d58f --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/cat @@ -0,0 +1 @@ +# Dummy file stubbing a stub/mock diff --git a/tests/bash/test_helper/bats-support/test/test_helper.bash b/tests/bash/test_helper/bats-support/test/test_helper.bash new file mode 100644 index 0000000..ca16775 --- /dev/null +++ b/tests/bash/test_helper/bats-support/test/test_helper.bash @@ -0,0 +1,6 @@ +setup() { + export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.." + + # Load library. + load '../load' +} diff --git a/tests/test_helper/bats-assert b/tests/test_helper/bats-assert deleted file mode 160000 index b93143a..0000000 --- a/tests/test_helper/bats-assert +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b93143a1bfbde41d9b7343aab0d36f3ef6549e6b diff --git a/tests/test_helper/bats-support b/tests/test_helper/bats-support deleted file mode 160000 index d007fc1..0000000 --- a/tests/test_helper/bats-support +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d007fc1f451abbad55204fa9c9eb3e6ed5dc5f61 From 3f328635ab1e2cef7d29b4cdfb65ea8239306689 Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:05:03 +0100 Subject: [PATCH 2/9] removed the incorrectly configured submoules --- .../.github/workflows/sync-master.yml | 8 - .../bats-assert/.github/workflows/test.yml | 9 - tests/bash/test_helper/bats-assert/.gitignore | 3 - tests/bash/test_helper/bats-assert/LICENSE | 116 -- tests/bash/test_helper/bats-assert/README.md | 1038 ----------------- tests/bash/test_helper/bats-assert/load.bash | 33 - .../test_helper/bats-assert/package-lock.json | 36 - .../bash/test_helper/bats-assert/package.json | 48 - .../test_helper/bats-assert/src/assert.bash | 42 - .../bats-assert/src/assert_equal.bash | 42 - .../bats-assert/src/assert_failure.bash | 78 -- .../bats-assert/src/assert_line.bash | 301 ----- .../bats-assert/src/assert_not_equal.bash | 42 - .../bats-assert/src/assert_output.bash | 249 ---- .../bats-assert/src/assert_regex.bash | 56 - .../bats-assert/src/assert_success.bash | 44 - .../test_helper/bats-assert/src/refute.bash | 42 - .../bats-assert/src/refute_line.bash | 318 ----- .../bats-assert/src/refute_output.bash | 246 ---- .../bats-assert/src/refute_regex.bash | 66 -- .../test_helper/bats-assert/test/assert.bats | 19 - .../bats-assert/test/assert_equal.bats | 62 - .../bats-assert/test/assert_failure.bats | 75 -- .../bats-assert/test/assert_line.bats | 362 ------ .../bats-assert/test/assert_not_equal.bats | 57 - .../bats-assert/test/assert_output.bats | 296 ----- .../bats-assert/test/assert_regex.bats | 87 -- .../bats-assert/test/assert_stderr.bats | 298 ----- .../bats-assert/test/assert_stderr_line.bats | 364 ------ .../bats-assert/test/assert_success.bats | 40 - .../test_helper/bats-assert/test/refute.bats | 18 - .../bats-assert/test/refute_line.bats | 355 ------ .../bats-assert/test/refute_output.bats | 230 ---- .../bats-assert/test/refute_regex.bats | 98 -- .../bats-assert/test/refute_stderr.bats | 242 ---- .../bats-assert/test/refute_stderr_line.bats | 356 ------ .../bats-assert/test/test_helper.bash | 30 - .../bats-support/.github/dependabot.yaml | 7 - .../.github/workflows/scorecard.yaml | 72 -- .../bats-support/.github/workflows/tests.yml | 27 - .../bash/test_helper/bats-support/.gitignore | 4 - .../test_helper/bats-support/CHANGELOG.md | 46 - tests/bash/test_helper/bats-support/LICENSE | 116 -- tests/bash/test_helper/bats-support/README.md | 181 --- tests/bash/test_helper/bats-support/load.bash | 11 - .../test_helper/bats-support/package.json | 30 - .../test_helper/bats-support/src/error.bash | 41 - .../test_helper/bats-support/src/lang.bash | 73 -- .../test_helper/bats-support/src/output.bash | 279 ----- .../test/50-output-10-batslib_err.bats | 43 - .../50-output-11-batslib_count_lines.bats | 21 - .../50-output-12-batslib_is_single_line.bats | 13 - ...batslib_get_max_single_line_key_width.bats | 21 - .../50-output-14-batslib_print_kv_single.bats | 27 - .../50-output-15-batslib_print_kv_multi.bats | 19 - ...t-16-batslib_print_kv_single_or_multi.bats | 31 - .../test/50-output-17-batslib_prefix.bats | 43 - .../test/50-output-18-batslib_mark.bats | 72 -- .../test/50-output-19-batslib_decorate.bats | 46 - .../bats-support/test/51-error-10-fail.bats | 16 - .../test/52-lang-10-batslib_is_caller.bats | 88 -- tests/bash/test_helper/bats-support/test/cat | 1 - .../bats-support/test/test_helper.bash | 6 - 63 files changed, 7140 deletions(-) delete mode 100644 tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml delete mode 100644 tests/bash/test_helper/bats-assert/.github/workflows/test.yml delete mode 100644 tests/bash/test_helper/bats-assert/.gitignore delete mode 100644 tests/bash/test_helper/bats-assert/LICENSE delete mode 100644 tests/bash/test_helper/bats-assert/README.md delete mode 100644 tests/bash/test_helper/bats-assert/load.bash delete mode 100644 tests/bash/test_helper/bats-assert/package-lock.json delete mode 100644 tests/bash/test_helper/bats-assert/package.json delete mode 100644 tests/bash/test_helper/bats-assert/src/assert.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_equal.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_failure.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_line.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_not_equal.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_output.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_regex.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/assert_success.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/refute.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/refute_line.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/refute_output.bash delete mode 100644 tests/bash/test_helper/bats-assert/src/refute_regex.bash delete mode 100644 tests/bash/test_helper/bats-assert/test/assert.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_equal.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_failure.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_line.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_not_equal.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_output.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_regex.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_stderr.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/assert_success.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute_line.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute_output.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute_regex.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute_stderr.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats delete mode 100644 tests/bash/test_helper/bats-assert/test/test_helper.bash delete mode 100644 tests/bash/test_helper/bats-support/.github/dependabot.yaml delete mode 100644 tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml delete mode 100644 tests/bash/test_helper/bats-support/.github/workflows/tests.yml delete mode 100644 tests/bash/test_helper/bats-support/.gitignore delete mode 100644 tests/bash/test_helper/bats-support/CHANGELOG.md delete mode 100644 tests/bash/test_helper/bats-support/LICENSE delete mode 100644 tests/bash/test_helper/bats-support/README.md delete mode 100644 tests/bash/test_helper/bats-support/load.bash delete mode 100644 tests/bash/test_helper/bats-support/package.json delete mode 100644 tests/bash/test_helper/bats-support/src/error.bash delete mode 100644 tests/bash/test_helper/bats-support/src/lang.bash delete mode 100644 tests/bash/test_helper/bats-support/src/output.bash delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats delete mode 100644 tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats delete mode 100644 tests/bash/test_helper/bats-support/test/51-error-10-fail.bats delete mode 100644 tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats delete mode 100644 tests/bash/test_helper/bats-support/test/cat delete mode 100644 tests/bash/test_helper/bats-support/test/test_helper.bash diff --git a/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml b/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml deleted file mode 100644 index 4577123..0000000 --- a/tests/bash/test_helper/bats-assert/.github/workflows/sync-master.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Sync Master -on: - push: { branches: main } - workflow_dispatch: - -jobs: - sync: - uses: bats-core/.github/.github/workflows/sync-master.yml@v1 diff --git a/tests/bash/test_helper/bats-assert/.github/workflows/test.yml b/tests/bash/test_helper/bats-assert/.github/workflows/test.yml deleted file mode 100644 index 6c95ce2..0000000 --- a/tests/bash/test_helper/bats-assert/.github/workflows/test.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Test -on: - push: - pull_request: - workflow_dispatch: - -jobs: - test: - uses: bats-core/.github/.github/workflows/test.yml@v1 diff --git a/tests/bash/test_helper/bats-assert/.gitignore b/tests/bash/test_helper/bats-assert/.gitignore deleted file mode 100644 index 41a3ee1..0000000 --- a/tests/bash/test_helper/bats-assert/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules -/yarn.lock -/bats-assert-*.tgz diff --git a/tests/bash/test_helper/bats-assert/LICENSE b/tests/bash/test_helper/bats-assert/LICENSE deleted file mode 100644 index 670154e..0000000 --- a/tests/bash/test_helper/bats-assert/LICENSE +++ /dev/null @@ -1,116 +0,0 @@ -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/> diff --git a/tests/bash/test_helper/bats-assert/README.md b/tests/bash/test_helper/bats-assert/README.md deleted file mode 100644 index 8a5fc17..0000000 --- a/tests/bash/test_helper/bats-assert/README.md +++ /dev/null @@ -1,1038 +0,0 @@ -# bats-assert - -[![License](https://img.shields.io/npm/l/bats-assert.svg)](https://github.com/bats-core/bats-assert/blob/master/LICENSE) -[![GitHub release](https://img.shields.io/github/release/bats-core/bats-assert.svg)](https://github.com/bats-core/bats-assert/releases/latest) -[![npm release](https://img.shields.io/npm/v/bats-assert.svg)](https://www.npmjs.com/package/bats-assert) -[![Tests](https://github.com/bats-core/bats-assert/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/bats-core/bats-assert/actions/workflows/test.yml) - -`bats-assert` is a helper library providing common assertions for [Bats][bats]. - -- [Install](#install) -- [Usage](#usage) -- [Options](#options) -- [Full Assertion API](#full-assertion-api) - -In the context of this project, an [assertion][wikipedia-assertions] is a function that perform a test and returns `1` on failure or `0` on success. -To make debugging easier, the assertion also outputs relevant information on failure. -The output is [formatted][bats-support-output] for readability. -To make assertions usable outside of `@test` blocks, the output is sent to [stderr][wikipedia-stderr]. - -The most recent invocation of Bats' `run` function is used for testing assertions on output and status code. - -[wikipedia-assertions]: https://en.wikipedia.org/wiki/Assertion_(software_development) -[wikipedia-stderr]: https://en.wikipedia.org/wiki/Standard_streams#Standard_error_(stderr) - -## Install - -This project has one dependency, for output formatting: [`bats-support`][bats-support] - -Read the [shared documentation][bats-docs] to learn how to install and load both libraries. - -## Usage - -This project provides the following functions: - - - [assert](#assert) / [refute](#refute) Assert a given expression evaluates to `true` or `false`. - - [assert_equal](#assert_equal) Assert two parameters are equal. - - [assert_not_equal](#assert_not_equal) Assert two parameters are not equal. - - [assert_success](#assert_success) / [assert_failure](#assert_failure) Assert exit status is `0` or `1`. - - [assert_output](#assert_output) / [refute_output](#refute_output) Assert output does (or does not) contain given content. - - [assert_line](#assert_line) / [refute_line](#refute_line) Assert a specific line of output does (or does not) contain given content. - - [assert_regex](#assert_regex) / [refute_regex](#refute_regex) Assert a parameter does (or does not) match given pattern. - - [assert_stderr](#assert_stderr) / [refute_stderr](#refute_stderr) Assert stderr does (or does not) contain given content. - - [assert_stderr_line](#assert_stderr_line) / [refute_stderr_line](#refute_stderr_line) Assert a specific line of stderr does (or does not) contain given content. - -These commands are described in more detail below. - -## Options - -For functions that have options, `--` disables option parsing for the remaining arguments to allow using arguments identical to one of the allowed options. - -```bash -assert_output -- '-p' -``` - -Specifying `--` as an argument is similarly simple. - -```bash -refute_line -- '--' -``` - - -## Full Assertion API - -### `assert` - -Fail if the given expression evaluates to false. - -> _**Note**: -> The expression must be a simple command. -> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._ - -```bash -@test 'assert()' { - assert [ 1 -lt 0 ] -} -``` - -On failure, the failed expression is displayed. - -``` --- assertion failed -- -expression : [ 1 -lt 0 ] --- -``` - - -### `refute` - -Fail if the given expression evaluates to true. - -> _**Note** -> The expression must be a simple command. -> [Compound commands][bash-comp-cmd], such as `[[`, can be used only when executed with `bash -c`._ - -```bash -@test 'refute()' { - refute [ 1 -gt 0 ] -} -``` - -On failure, the successful expression is displayed. - -``` --- assertion succeeded, but it was expected to fail -- -expression : [ 1 -gt 0 ] --- -``` - - -### `assert_equal` - -Fail if the two parameters, actual and expected value respectively, do not equal. - -```bash -@test 'assert_equal()' { - assert_equal 'have' 'want' -} -``` - -On failure, the expected and actual values are displayed. - -``` --- values do not equal -- -expected : want -actual : have --- -``` - -If either value is longer than one line both are displayed in *multi-line* format. - - -### `assert_not_equal` - -Fail if the two parameters, actual and unexpected value respectively, are equal. - -```bash -@test 'assert_not_equal()' { - assert_not_equal 'foobar' 'foobar' -} -``` - -On failure, the expected and actual values are displayed. - -``` --- values should not be equal -- -unexpected : foobar -actual : foobar --- -``` - -If either value is longer than one line both are displayed in *multi-line* format. - - -### `assert_success` - -Fail if `$status` is not 0. - -```bash -@test 'assert_success() status only' { - run bash -c "echo 'Error!'; exit 1" - assert_success -} -``` - -On failure, `$status` and `$output` are displayed. - -``` --- command failed -- -status : 1 -output : Error! --- -``` - -If `$output` is longer than one line, it is displayed in *multi-line* format. - - -### `assert_failure` - -Fail if `$status` is 0. - -```bash -@test 'assert_failure() status only' { - run echo 'Success!' - assert_failure -} -``` - -On failure, `$output` is displayed. - -``` --- command succeeded, but it was expected to fail -- -output : Success! --- -``` - -If `$output` is longer than one line, it is displayed in *multi-line* format. - -#### Expected status - -When one parameter is specified, fail if `$status` does not equal the expected status specified by the parameter. - -```bash -@test 'assert_failure() with expected status' { - run bash -c "echo 'Error!'; exit 1" - assert_failure 2 -} -``` - -On failure, the expected and actual status, and `$output` are displayed. - -``` --- command failed as expected, but status differs -- -expected : 2 -actual : 1 -output : Error! --- -``` - -If `$output` is longer than one line, it is displayed in *multi-line* format. - - -### `assert_output` - -This function helps to verify that a command or function produces the correct output by checking that the specified expected output matches the actual output. -Matching can be literal (default), partial or regular expression. -This function is the logical complement of `refute_output`. - -#### Literal matching - -By default, literal matching is performed. -The assertion fails if `$output` does not equal the expected output. - -```bash -@test 'assert_output()' { - run echo 'have' - assert_output 'want' -} -``` - -On failure, the expected and actual output are displayed. - -``` --- output differs -- -expected : want -actual : have --- -``` - -If either value is longer than one line both are displayed in *multi-line* format. - -#### Existence - -To assert that any (non-empty) output exists at all, simply omit the matching argument. - -```bash -@test 'assert_output()' { - run echo 'have' - assert_output -} -``` - -On failure, an error message is displayed. - -``` --- no output -- -expected non-empty output, but output was empty --- -``` - -#### Partial matching - -Partial matching can be enabled with the `--partial` option (`-p` for short). -When used, the assertion fails if the expected *substring* is not found in `$output`. - -```bash -@test 'assert_output() partial matching' { - run echo 'ERROR: no such file or directory' - assert_output --partial 'SUCCESS' -} -``` - -On failure, the substring and the output are displayed. - -``` --- output does not contain substring -- -substring : SUCCESS -output : ERROR: no such file or directory --- -``` - -This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, the assertion fails if the *[extended regular expression]* does not match `$output`. - -[extended regular expression]: https://en.wikibooks.org/wiki/Regular_Expressions/POSIX-Extended_Regular_Expressions - -> [!IMPORTANT] -> Bash [doesn't support](https://stackoverflow.com/a/48898886/5432315) certain parts of regular expressions you may be used to: -> * `\d` `\D` `\s` `\S` `\w` `\W` — these can be replaced with POSIX character class equivalents `[[:digit:]]`, `[^[:digit:]]`, `[[:space:]]`, `[^[:space:]]`, `[_[:alnum:]]`, and `[^_[:alnum:]]`, respectively. (Notice the last case, where the `[:alnum:]` POSIX character class is augmented with underscore to be exactly equivalent to the Perl `\w` shorthand.) -> * Non-greedy matching. You can sometimes replace `a.*?b` with something like `a[^ab]*b` to get a similar effect in practice, though the two are not exactly equivalent. -> * Non-capturing parentheses `(?:...)`. In the trivial case, just use capturing parentheses `(...)` instead; though of course, if you use capture groups and/or backreferences, this will renumber your capture groups. -> * Lookarounds like `(?<=before)` or `(?!after)`. (In fact anything with `(?` is a Perl extension.) There is no simple general workaround for these, though you can sometimes rephrase your problem into one where lookarounds can be avoided. - -> _**Note**: -> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._ - -```bash -@test 'assert_output() regular expression matching' { - run echo 'Foobar 0.1.0' - assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -} -``` - -On failure, the regular expression and the output are displayed. - -``` --- regular expression does not match output -- -regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -output : Foobar 0.1.0 --- -``` - -An error is displayed if the specified extended regular expression is invalid. - -This option and partial matching (`--partial` or `-p`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Standard Input, HereDocs and HereStrings - -The expected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option. - -```bash -@test 'assert_output() with pipe' { - run echo 'hello' - echo 'hello' | assert_output - -} - -@test 'assert_output() with herestring' { - run echo 'hello' - assert_output - <<< hello -} -``` - - -### `refute_output` - -This function helps to verify that a command or function produces the correct output by checking that the specified unexpected output does not match the actual output. -Matching can be literal (default), partial or regular expression. -This function is the logical complement of `assert_output`. - -#### Literal matching - -By default, literal matching is performed. -The assertion fails if `$output` equals the unexpected output. - -```bash -@test 'refute_output()' { - run echo 'want' - refute_output 'want' -} -``` - -On failure, the output is displayed. - -``` --- output equals, but it was expected to differ -- -output : want --- -``` - -If output is longer than one line it is displayed in *multi-line* format. - -#### Existence - -To assert that there is no output at all, simply omit the matching argument. - -```bash -@test 'refute_output()' { - run foo --silent - refute_output -} -``` - -On failure, an error message is displayed. - -``` --- unexpected output -- -expected no output, but output was non-empty --- -``` - -#### Partial matching - -Partial matching can be enabled with the `--partial` option (`-p` for short). -When used, the assertion fails if the unexpected *substring* is found in `$output`. - -```bash -@test 'refute_output() partial matching' { - run echo 'ERROR: no such file or directory' - refute_output --partial 'ERROR' -} -``` - -On failure, the substring and the output are displayed. - -``` --- output should not contain substring -- -substring : ERROR -output : ERROR: no such file or directory --- -``` - -This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, the assertion fails if the *extended regular expression* matches `$output`. - -> _**Note**: -> The anchors `^` and `$` bind to the beginning and the end of the entire output (not individual lines), respectively._ - -```bash -@test 'refute_output() regular expression matching' { - run echo 'Foobar v0.1.0' - refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -} -``` - -On failure, the regular expression and the output are displayed. - -``` --- regular expression should not match output -- -regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -output : Foobar v0.1.0 --- -``` - -An error is displayed if the specified extended regular expression is invalid. - -This option and partial matching (`--partial` or `-p`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Standard Input, HereDocs and HereStrings - -The unexpected output can be specified via standard input (also heredoc/herestring) with the `-`/`--stdin` option. - -```bash -@test 'refute_output() with pipe' { - run echo 'hello' - echo 'world' | refute_output - -} - -@test 'refute_output() with herestring' { - run echo 'hello' - refute_output - <<< world -} -``` - - -### `assert_line` - -Similarly to `assert_output`, this function helps to verify that a command or function produces the correct output. -It checks that the expected line appears in the output (default) or in a specific line of it. -Matching can be literal (default), partial or regular expression. -This function is the logical complement of `refute_line`. - -> _**Warning**: -> Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, -> causing line indices to change and preventing testing for empty lines._ - -[bats-93]: https://github.com/sstephenson/bats/pull/93 - -#### Looking for a line in the output - -By default, the entire output is searched for the expected line. -The assertion fails if the expected line is not found in `${lines[@]}`. - -```bash -@test 'assert_line() looking for line' { - run echo $'have-0\nhave-1\nhave-2' - assert_line 'want' -} -``` - -On failure, the expected line and the output are displayed. - -> _**Warning**: -> The output displayed does not contain empty lines. -> See the Warning above for more._ - -``` --- output does not contain line -- -line : want -output (3 lines): - have-0 - have-1 - have-2 --- -``` - -If output is not longer than one line, it is displayed in *two-column* format. - -#### Matching a specific line - -When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index. -The assertion fails if the expected line does not equal `${lines[<idx>]}`. - -```bash -@test 'assert_line() specific line' { - run echo $'have-0\nhave-1\nhave-2' - assert_line --index 1 'want-1' -} -``` - -On failure, the index and the compared lines are displayed. - -``` --- line differs -- -index : 1 -expected : want-1 -actual : have-1 --- -``` - -#### Partial matching - -Partial matching can be enabled with the `--partial` option (`-p` for short). -When used, a match fails if the expected *substring* is not found in the matched line. - -```bash -@test 'assert_line() partial matching' { - run echo $'have 1\nhave 2\nhave 3' - assert_line --partial 'want' -} -``` - -On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line. - -``` --- no output line contains substring -- -substring : want -output (3 lines): - have 1 - have 2 - have 3 --- -``` - -This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, a match fails if the *extended regular expression* does not match the line being tested. - -> _**Note**: -> As expected, the anchors `^` and `$` bind to the beginning and the end of the matched line, respectively._ - -```bash -@test 'assert_line() regular expression matching' { - run echo $'have-0\nhave-1\nhave-2' - assert_line --index 1 --regexp '^want-[0-9]$' -} -``` - -On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line. - -``` --- regular expression does not match line -- -index : 1 -regexp : ^want-[0-9]$ -line : have-1 --- -``` - -An error is displayed if the specified extended regular expression is invalid. - -This option and partial matching (`--partial` or `-p`) are mutually exclusive. -An error is displayed when used simultaneously. - - -### `refute_line` - -Similarly to `refute_output`, this function helps to verify that a command or function produces the correct output. -It checks that the unexpected line does not appear in the output (default) or in a specific line of it. -Matching can be literal (default), partial or regular expression. -This function is the logical complement of `assert_line`. - -> _**Warning**: -> Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, -> causing line indices to change and preventing testing for empty lines._ - -[bats-93]: https://github.com/sstephenson/bats/pull/93 - -#### Looking for a line in the output - -By default, the entire output is searched for the unexpected line. -The assertion fails if the unexpected line is found in `${lines[@]}`. - -```bash -@test 'refute_line() looking for line' { - run echo $'have-0\nwant\nhave-2' - refute_line 'want' -} -``` - -On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed. - -> _**Warning**: -> The output displayed does not contain empty lines. -> See the Warning above for more._ - -``` --- line should not be in output -- -line : want -index : 1 -output (3 lines): - have-0 -> want - have-2 --- -``` - -If output is not longer than one line, it is displayed in *two-column* format. - -#### Matching a specific line - -When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index. -The assertion fails if the unexpected line equals `${lines[<idx>]}`. - -```bash -@test 'refute_line() specific line' { - run echo $'have-0\nwant-1\nhave-2' - refute_line --index 1 'want-1' -} -``` - -On failure, the index and the unexpected line are displayed. - -``` --- line should differ -- -index : 1 -line : want-1 --- -``` - -#### Partial matching - -Partial matching can be enabled with the `--partial` option (`-p` for short). -When used, a match fails if the unexpected *substring* is found in the matched line. - -```bash -@test 'refute_line() partial matching' { - run echo $'have 1\nwant 2\nhave 3' - refute_line --partial 'want' -} -``` - -On failure, in addition to the details of literal matching, the substring is also displayed. -When used with `--index <idx>` the substring replaces the unexpected line. - -``` --- no line should contain substring -- -substring : want -index : 1 -output (3 lines): - have 1 -> want 2 - have 3 --- -``` - -This option and regular expression matching (`--regexp` or `-e`) are mutually exclusive. -An error is displayed when used simultaneously. - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, a match fails if the *extended regular expression* matches the line being tested. - -> _**Note**: -> As expected, the anchors `^` and `$` bind to the beginning and the end of the matched line, respectively._ - -```bash -@test 'refute_line() regular expression matching' { - run echo $'Foobar v0.1.0\nRelease date: 2015-11-29' - refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -} -``` - -On failure, in addition to the details of literal matching, the regular expression is also displayed. -When used with `--index <idx>` the regular expression replaces the unexpected line. - -``` --- regular expression should not match line -- -index : 0 -regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -line : Foobar v0.1.0 --- -``` - -An error is displayed if the specified extended regular expression is invalid. - -This option and partial matching (`--partial` or `-p`) are mutually exclusive. -An error is displayed when used simultaneously. - -### `assert_regex` - -This function is similar to `assert_equal` but uses pattern matching instead of -equality, by wrapping `[[ value =~ pattern ]]`. - -Fail if the value (first parameter) does not match the pattern (second -parameter). - -```bash -@test 'assert_regex()' { - assert_regex 'what' 'x$' -} -``` - -On failure, the value and the pattern are displayed. - -``` --- values does not match regular expression -- -value : what -pattern : x$ --- -``` - -If the value is longer than one line then it is displayed in *multi-line* -format. - -An error is displayed if the specified extended regular expression is invalid. - -For description of the matching behavior, refer to the documentation of the -`=~` operator in the [Bash manual][bash-conditional]. - -> _**Note**: -> the `BASH_REMATCH` array is available immediately after the assertion succeeds but is fragile; -> i.e. prone to being overwritten as a side effect of other actions._ - -### `refute_regex` - -This function is similar to `refute_equal` but uses pattern matching instead of -equality, by wrapping `! [[ value =~ pattern ]]`. - -Fail if the value (first parameter) matches the pattern (second parameter). - -```bash -@test 'refute_regex()' { - refute_regex 'WhatsApp' 'Threema' -} -``` - -On failure, the value, the pattern and the match are displayed. - -```bash -@test 'refute_regex()' { - refute_regex 'WhatsApp' 'What.' -} - --- value matches regular expression -- -value : WhatsApp -pattern : What. -match : Whats -case : sensitive --- -``` - -If the value or pattern is longer than one line then it is displayed in -*multi-line* format. - -An error is displayed if the specified extended regular expression is invalid. - -For description of the matching behavior, refer to the documentation of the -`=~` operator in the [Bash manual][bash-conditional]. - -> _**Note**: -> the `BASH_REMATCH` array is available immediately after the assertion fails but is fragile; -> i.e. prone to being overwritten as a side effect of other actions like calling `run`. -> Thus, it's good practice to avoid using `BASH_REMATCH` in conjunction with `refute_regex()`. -> The valuable information the array contains is the matching part of the value which is printed in the failing test log, as mentioned above._ - -### `assert_stderr` - -> _**Note**: -> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. -> If not, `$stderr` will be empty, causing `assert_stderr` to always fail. - -Similarly to `assert_output`, this function verifies that a command or function produces the expected stderr. -The stderr matching can be literal (the default), partial or by regular expression. -The expected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. - -#### Literal matching - -By default, literal matching is performed. -The assertion fails if `$stderr` does not equal the expected stderr. - - ```bash - echo_err() { - echo "$@" >&2 - } - - @test 'assert_stderr()' { - run --separate-stderr echo_err 'have' - assert_stderr 'want' - } - - @test 'assert_stderr() with pipe' { - run --separate-stderr echo_err 'hello' - echo_err 'hello' | assert_stderr - - } - - @test 'assert_stderr() with herestring' { - run --separate-stderr echo_err 'hello' - assert_stderr - <<< hello - } - ``` - -On failure, the expected and actual stderr are displayed. - - ``` - -- stderr differs -- - expected : want - actual : have - -- - ``` - -#### Existence - -To assert that any stderr exists at all, omit the `expected` argument. - - ```bash - @test 'assert_stderr()' { - run --separate-stderr echo_err 'have' - assert_stderr - } - ``` - -On failure, an error message is displayed. - - ``` - -- no stderr -- - expected non-empty stderr, but stderr was empty - -- - ``` - -#### Partial matching - -Partial matching can be enabled with the `--partial` option (`-p` for short). -When used, the assertion fails if the expected _substring_ is not found in `$stderr`. - - ```bash - @test 'assert_stderr() partial matching' { - run --separate-stderr echo_err 'ERROR: no such file or directory' - assert_stderr --partial 'SUCCESS' - } - ``` - -On failure, the substring and the stderr are displayed. - - ``` - -- stderr does not contain substring -- - substring : SUCCESS - stderr : ERROR: no such file or directory - -- - ``` - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, the assertion fails if the *extended regular expression* does not match `$stderr`. - -*Note: The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire stderr; not individual lines.* - - ```bash - @test 'assert_stderr() regular expression matching' { - run --separate-stderr echo_err 'Foobar 0.1.0' - assert_stderr --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' - } - ``` - -On failure, the regular expression and the stderr are displayed. - - ``` - -- regular expression does not match stderr -- - regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ - stderr : Foobar 0.1.0 - -- - ``` - -### `refute_stderr` - -> _**Note**: -> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. -> If not, `$stderr` will be empty, causing `refute_stderr` to always pass. - -Similar to `refute_output`, this function verifies that a command or function does not produce the unexpected stderr. -(It is the logical complement of `assert_stderr`.) -The stderr matching can be literal (the default), partial or by regular expression. -The unexpected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. - -### `assert_stderr_line` - -> _**Note**: -> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. -> If not, `$stderr` will be empty, causing `assert_stderr_line` to always fail. - -Similarly to `assert_stderr`, this function verifies that a command or function produces the expected stderr. -It checks that the expected line appears in the stderr (default) or at a specific line number. -Matching can be literal (default), partial or regular expression. -This function is the logical complement of `refute_stderr_line`. - -#### Looking for a line in the stderr - -By default, the entire stderr is searched for the expected line. -The assertion fails if the expected line is not found in `${stderr_lines[@]}`. - - ```bash - echo_err() { - echo "$@" >&2 - } - - @test 'assert_stderr_line() looking for line' { - run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' - assert_stderr_line 'want' - } - ``` - -On failure, the expected line and the stderr are displayed. - - ``` - -- stderr does not contain line -- - line : want - stderr (3 lines): - have-0 - have-1 - have-2 - -- - ``` - -#### Matching a specific line - -When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index. -The assertion fails if the expected line does not equal `${stderr_lines[<idx>]}`. - - ```bash - @test 'assert_stderr_line() specific line' { - run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' - assert_stderr_line --index 1 'want-1' - } - ``` - -On failure, the index and the compared stderr_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_stderr_line() partial matching' { - run --separate-stderr echo_err $'have 1\nhave 2\nhave 3' - assert_stderr_line --partial 'want' - } - ``` - -On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line. - - ``` - -- no stderr line contains substring -- - substring : want - stderr (3 lines): - have 1 - have 2 - have 3 - -- - ``` - -#### Regular expression matching - -Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -When used, a match fails if the *extended regular expression* does not match the line being tested. - -*Note: As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* - - ```bash - @test 'assert_stderr_line() regular expression matching' { - run --separate-stderr echo_err $'have-0\nhave-1\nhave-2' - assert_stderr_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 - -- - ``` - -### `refute_stderr_line` - -> _**Note**: -> `run` has to be called with `--separate-stderr` to separate stdout and stderr into `$output` and `$stderr`. -> If not, `$stderr` will be empty, causing `refute_stderr_line` to always pass. - -Similarly to `refute_stderr`, this function helps to verify that a command or function produces the correct stderr. -It checks that the unexpected line does not appear in the stderr (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_stderr_line`. - -<!-- REFERENCES --> - -[bats]: https://github.com/bats-core/bats-core -[bash-comp-cmd]: https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands -[bash-conditional]: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs - -[bats-docs]: https://bats-core.readthedocs.io/ -[bats-support-output]: https://github.com/bats-core/bats-support#output-formatting -[bats-support]: https://github.com/bats-core/bats-support diff --git a/tests/bash/test_helper/bats-assert/load.bash b/tests/bash/test_helper/bats-assert/load.bash deleted file mode 100644 index c67d9e8..0000000 --- a/tests/bash/test_helper/bats-assert/load.bash +++ /dev/null @@ -1,33 +0,0 @@ -# bats-assert - Common assertions for Bats -# -# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com> -# -# To the extent possible under law, the author(s) have dedicated all -# copyright and related and neighboring rights to this software to the -# public domain worldwide. This software is distributed without any -# warranty. -# -# You should have received a copy of the CC0 Public Domain Dedication -# along with this software. If not, see -# <http://creativecommons.org/publicdomain/zero/1.0/>. -# -# Assertions are functions that perform a test and output relevant -# information on failure to help debugging. They return 1 on failure -# and 0 otherwise. -# -# All output is formatted for readability using the functions of -# `output.bash' and sent to the standard error. - -# shellcheck disable=1090 -source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash" diff --git a/tests/bash/test_helper/bats-assert/package-lock.json b/tests/bash/test_helper/bats-assert/package-lock.json deleted file mode 100644 index 8f0dcc6..0000000 --- a/tests/bash/test_helper/bats-assert/package-lock.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "bats-assert", - "version": "2.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "bats-assert", - "version": "2.1.0", - "license": "CC0-1.0", - "devDependencies": { - "bats": "^1", - "bats-support": "^0.3" - }, - "peerDependencies": { - "bats": "0.4 || ^1", - "bats-support": "^0.3" - } - }, - "node_modules/bats": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/bats/-/bats-1.9.0.tgz", - "integrity": "sha512-Z5BJaAmmHv/ujj7obhjEzJ//OL+ZtjVq0iRnHu+2fE9OeUaPMbJpBgYiOdNbDrG3E2hqe84/AXNnS/UiXl/UcA==", - "dev": true, - "bin": { - "bats": "bin/bats" - } - }, - "node_modules/bats-support": { - "version": "0.3.0", - "resolved": "git+ssh://git@github.com/jasonkarns/bats-support.git#24a72e14349690bcbf7c151b9d2d1cdd32d36eb1", - "integrity": "sha512-42f2THEaN02dVj7Zhj0C4AM5FdePq3YPlXvnu/fKUQwCv1qM2dyACBKFXQkLw9QMzhDmwuFcke90XwHMUsBjGQ==", - "dev": true - } - } -} diff --git a/tests/bash/test_helper/bats-assert/package.json b/tests/bash/test_helper/bats-assert/package.json deleted file mode 100644 index 7048995..0000000 --- a/tests/bash/test_helper/bats-assert/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "bats-assert", - "version": "2.1.0", - "description": "Common assertions for Bats", - "homepage": "https://github.com/bats-core/bats-assert", - "license": "CC0-1.0", - "author": "Zoltán Tömböl (https://github.com/ztombol)", - "contributors": [ - "Sam Stephenson <sstephenson@gmail.com> (http://sstephenson.us/)", - "Jason Karns <jason.karns@gmail.com> (http://jason.karns.name)", - "Mislav Marohnić <mislav.marohnic@gmail.com> (http://mislav.net/)", - "Tim Pope (https://github.com/tpope)" - ], - "repository": "github:bats-core/bats-assert", - "bugs": "https://github.com/bats-core/bats-assert/issues", - "directories": { - "lib": "src", - "test": "test" - }, - "files": [ - "load.bash", - "src" - ], - "scripts": { - "test": "bats ${CI+-t} test", - "postversion": "npm publish", - "prepublishOnly": "npm run publish:github", - "publish:github": "git push --follow-tags" - }, - "devDependencies": { - "bats": "^1", - "bats-support": "^0.3" - }, - "peerDependencies": { - "bats": "0.4 || ^1", - "bats-support": "^0.3" - }, - "keywords": [ - "bats", - "bash", - "shell", - "test", - "unit", - "assert", - "assertion", - "helper" - ] -} diff --git a/tests/bash/test_helper/bats-assert/src/assert.bash b/tests/bash/test_helper/bats-assert/src/assert.bash deleted file mode 100644 index 0260ce2..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert.bash +++ /dev/null @@ -1,42 +0,0 @@ -# assert -# ====== -# -# Summary: Fail if the given expression evaluates to false. -# -# Usage: assert <expression> - -# Options: -# <expression> The expression to evaluate for truthiness. -# *__Note:__ The expression must be a simple command. -# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), -# such as `[[`, can be used only when executed with `bash -c`.* -# -# IO: -# STDERR - the failed expression, on failure -# Globals: -# none -# Returns: -# 0 - if expression evaluates to true -# 1 - otherwise -# -# ```bash -# @test 'assert()' { -# touch '/var/log/test.log' -# assert [ -e '/var/log/test.log' ] -# } -# ``` -# -# On failure, the failed expression is displayed. -# -# ``` -# -- assertion failed -- -# expression : [ -e /var/log/test.log ] -# -- -# ``` -assert() { - if ! "$@"; then - batslib_print_kv_single 10 'expression' "$*" \ - | batslib_decorate 'assertion failed' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_equal.bash b/tests/bash/test_helper/bats-assert/src/assert_equal.bash deleted file mode 100644 index 4ef1297..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_equal.bash +++ /dev/null @@ -1,42 +0,0 @@ -# assert_equal -# ============ -# -# Summary: Fail if the actual and expected values are not equal. -# -# Usage: assert_equal <actual> <expected> -# -# Options: -# <actual> The value being compared. -# <expected> The value to compare against. -# -# ```bash -# @test 'assert_equal()' { -# assert_equal 'have' 'want' -# } -# ``` -# -# IO: -# STDERR - expected and actual values, on failure -# Globals: -# none -# Returns: -# 0 - if values equal -# 1 - otherwise -# -# On failure, the expected and actual values are displayed. -# -# ``` -# -- values do not equal -- -# expected : want -# actual : have -# -- -# ``` -assert_equal() { - if [[ $1 != "$2" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$2" \ - 'actual' "$1" \ - | batslib_decorate 'values do not equal' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_failure.bash b/tests/bash/test_helper/bats-assert/src/assert_failure.bash deleted file mode 100644 index d906059..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_failure.bash +++ /dev/null @@ -1,78 +0,0 @@ -# assert_failure -# ============== -# -# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status. -# -# Usage: assert_failure [<expected_status>] -# -# Options: -# <expected_status> The specific status code to check against. -# If not provided, simply asserts status is != 0. -# -# IO: -# STDERR - `$output`, on failure; -# - also, `$status` and `expected_status`, if provided -# Globals: -# status -# output -# Returns: -# 0 - if `$status' is 0, -# or if expected_status is provided but does not equal `$status' -# 1 - otherwise -# -# ```bash -# @test 'assert_failure() status only' { -# run echo 'Success!' -# assert_failure -# } -# ``` -# -# On failure, `$output` is displayed. -# -# ``` -# -- command succeeded, but it was expected to fail -- -# output : Success! -# -- -# ``` -# -# ## Expected status -# -# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`. -# -# ```bash -# @test 'assert_failure() with expected status' { -# run bash -c "echo 'Error!'; exit 1" -# assert_failure 2 -# } -# ``` -# -# On failure, both the expected and actual statuses, and `$output` are displayed. -# -# ``` -# -- command failed as expected, but status differs -- -# expected : 2 -# actual : 1 -# output : Error! -# -- -# ``` -assert_failure() { - : "${output?}" - : "${status?}" - - (( $# > 0 )) && local -r expected="$1" - if (( status == 0 )); then - batslib_print_kv_single_or_multi 6 'output' "$output" \ - | batslib_decorate 'command succeeded, but it was expected to fail' \ - | fail - elif (( $# > 0 )) && (( status != expected )); then - { local -ir width=8 - batslib_print_kv_single "$width" \ - 'expected' "$expected" \ - 'actual' "$status" - batslib_print_kv_single_or_multi "$width" \ - 'output' "$output" - } \ - | batslib_decorate 'command failed as expected, but status differs' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_line.bash b/tests/bash/test_helper/bats-assert/src/assert_line.bash deleted file mode 100644 index bf9140d..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_line.bash +++ /dev/null @@ -1,301 +0,0 @@ -# assert_line -# =========== -# -# Summary: Fail if the expected line is not found in the output (default) or at a specific line number. -# -# Usage: assert_line [-n index] [-p | -e] [--] <expected> -# -# Options: -# -n, --index <idx> Match the <idx>th line -# -p, --partial Match if `expected` is a substring of `$output` or line <idx> -# -e, --regexp Treat `expected` as an extended regular expression -# <expected> The expected line string, substring, or regular expression -# -# IO: -# STDERR - details, on failure -# error message, on error -# Globals: -# output -# lines -# Returns: -# 0 - if matching line found -# 1 - otherwise -# -# Similarly to `assert_output`, this function verifies that a command or function produces the expected output. -# (It is the logical complement of `refute_line`.) -# It checks that the expected line appears in the output (default) or at a specific line number. -# Matching can be literal (default), partial or regular expression. -# -# *__Warning:__ -# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`, -# causing line indices to change and preventing testing for empty lines.* -# -# [bats-93]: https://github.com/sstephenson/bats/pull/93 -# -# ## Looking for a line in the output -# -# By default, the entire output is searched for the expected line. -# The assertion fails if the expected line is not found in `${lines[@]}`. -# -# ```bash -# @test 'assert_line() looking for line' { -# run echo $'have-0\nhave-1\nhave-2' -# assert_line 'want' -# } -# ``` -# -# On failure, the expected line and the output are displayed. -# -# ``` -# -- output does not contain line -- -# line : want -# output (3 lines): -# have-0 -# have-1 -# have-2 -# -- -# ``` -# -# ## Matching a specific line -# -# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index. -# The assertion fails if the expected line does not equal `${lines[<idx>]}`. -# -# ```bash -# @test 'assert_line() specific line' { -# run echo $'have-0\nhave-1\nhave-2' -# assert_line --index 1 'want-1' -# } -# ``` -# -# On failure, the index and the compared lines are displayed. -# -# ``` -# -- line differs -- -# index : 1 -# expected : want-1 -# actual : have-1 -# -- -# ``` -# -# ## Partial matching -# -# Partial matching can be enabled with the `--partial` option (`-p` for short). -# When used, a match fails if the expected *substring* is not found in the matched line. -# -# ```bash -# @test 'assert_line() partial matching' { -# run echo $'have 1\nhave 2\nhave 3' -# assert_line --partial 'want' -# } -# ``` -# -# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line. -# -# ``` -# -- no output line contains substring -- -# substring : want -# output (3 lines): -# have 1 -# have 2 -# have 3 -# -- -# ``` -# -# ## Regular expression matching -# -# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -# When used, a match fails if the *extended regular expression* does not match the line being tested. -# -# *__Note__: -# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* -# -# ```bash -# @test 'assert_line() regular expression matching' { -# run echo $'have-0\nhave-1\nhave-2' -# assert_line --index 1 --regexp '^want-[0-9]$' -# } -# ``` -# -# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line. -# -# ``` -# -- regular expression does not match line -- -# index : 1 -# regexp : ^want-[0-9]$ -# line : have-1 -# -- -# ``` -# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! -assert_line() { - __assert_line "$@" -} - -# assert_stderr_line -# =========== -# -# Summary: Fail if the expected line is not found in the stderr (default) or at a specific line number. -# -# Usage: assert_stderr_line [-n index] [-p | -e] [--] <expected> -# -# Options: -# -n, --index <idx> Match the <idx>th line -# -p, --partial Match if `expected` is a substring of `$stderr` or line <idx> -# -e, --regexp Treat `expected` as an extended regular expression -# <expected> The expected line string, substring, or regular expression -# -# IO: -# STDERR - details, on failure -# error message, on error -# Globals: -# stderr -# stderr_lines -# Returns: -# 0 - if matching line found -# 1 - otherwise -# -# Similarly to `assert_stderr`, this function verifies that a command or function produces the expected stderr. -# (It is the logical complement of `refute_stderr_line`.) -# It checks that the expected line appears in the stderr (default) or at a specific line number. -# Matching can be literal (default), partial or regular expression. -# -assert_stderr_line() { - __assert_line "$@" -} - -__assert_line() { - local -r caller=${FUNCNAME[1]} - local -i is_match_line=0 - local -i is_mode_partial=0 - local -i is_mode_regexp=0 - - if [[ "${caller}" == "assert_line" ]]; then - : "${lines?}" - local -ar stream_lines=("${lines[@]}") - local -r stream_type=output - elif [[ "${caller}" == "assert_stderr_line" ]]; then - : "${stderr_lines?}" - local -ar stream_lines=("${stderr_lines[@]}") - local -r stream_type=stderr - else - # Unknown caller - echo "Unexpected call to \`${FUNCNAME[0]}\` -Did you mean to call \`assert_line\` or \`assert_stderr_line\`?" \ - | batslib_decorate "ERROR: ${FUNCNAME[0]}" \ - | fail - return $? - fi - - # 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: ${caller}" \ - | 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: ${caller}" \ - | 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: ${caller}" \ - | fail - return $? - fi - - # Matching. - if (( is_match_line )); then - # Specific line. - if (( is_mode_regexp )); then - if ! [[ ${stream_lines[$idx]} =~ $expected ]]; then - batslib_print_kv_single 6 \ - 'index' "$idx" \ - 'regexp' "$expected" \ - 'line' "${stream_lines[$idx]}" \ - | batslib_decorate 'regular expression does not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${stream_lines[$idx]} != *"$expected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$expected" \ - 'line' "${stream_lines[$idx]}" \ - | batslib_decorate 'line does not contain substring' \ - | fail - fi - else - if [[ ${stream_lines[$idx]} != "$expected" ]]; then - batslib_print_kv_single 8 \ - 'index' "$idx" \ - 'expected' "$expected" \ - 'actual' "${stream_lines[$idx]}" \ - | batslib_decorate 'line differs' \ - | fail - fi - fi - else - # Contained in output/error stream. - if (( is_mode_regexp )); then - local -i idx - for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do - [[ ${stream_lines[$idx]} =~ $expected ]] && return 0 - done - { local -ar single=( 'regexp' "$expected" ) - local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 ${stream_type} line matches regular expression" \ - | fail - elif (( is_mode_partial )); then - local -i idx - for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do - [[ ${stream_lines[$idx]} == *"$expected"* ]] && return 0 - done - { local -ar single=( 'substring' "$expected" ) - local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 ${stream_type} line contains substring" \ - | fail - else - local -i idx - for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do - [[ ${stream_lines[$idx]} == "$expected" ]] && return 0 - done - { local -ar single=( 'line' "$expected" ) - local -ar may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 "${stream_type} does not contain line" \ - | fail - fi - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash b/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash deleted file mode 100644 index 933bb71..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_not_equal.bash +++ /dev/null @@ -1,42 +0,0 @@ -# assert_not_equal -# ============ -# -# Summary: Fail if the actual and unexpected values are equal. -# -# Usage: assert_not_equal <actual> <unexpected> -# -# Options: -# <actual> The value being compared. -# <unexpected> The value to compare against. -# -# ```bash -# @test 'assert_not_equal()' { -# assert_not_equal 'foo' 'foo' -# } -# ``` -# -# IO: -# STDERR - expected and actual values, on failure -# Globals: -# none -# Returns: -# 0 - if actual does not equal unexpected -# 1 - otherwise -# -# On failure, the unexpected and actual values are displayed. -# -# ``` -# -- values should not be equal -- -# unexpected : foo -# actual : foo -# -- -# ``` -assert_not_equal() { - if [[ "$1" == "$2" ]]; then - batslib_print_kv_single_or_multi 10 \ - 'unexpected' "$2" \ - 'actual' "$1" \ - | batslib_decorate 'values should not be equal' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_output.bash b/tests/bash/test_helper/bats-assert/src/assert_output.bash deleted file mode 100644 index 168d246..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_output.bash +++ /dev/null @@ -1,249 +0,0 @@ -# assert_output -# ============= -# -# Summary: Fail if `$output' does not match the expected output. -# -# Usage: assert_output [-p | -e] [- | [--] <expected>] -# -# Options: -# -p, --partial Match if `expected` is a substring of `$output` -# -e, --regexp Treat `expected` as an extended regular expression -# -, --stdin Read `expected` value from STDIN -# <expected> The expected value, substring or regular expression -# -# IO: -# STDIN - [=$1] expected output -# STDERR - details, on failure -# error message, on error -# Globals: -# output -# Returns: -# 0 - if output matches the expected value/partial/regexp -# 1 - otherwise -# -# This function verifies that a command or function produces the expected output. -# (It is the logical complement of `refute_output`.) -# Output matching can be literal (the default), partial or by regular expression. -# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. -# -# ## Literal matching -# -# By default, literal matching is performed. -# The assertion fails if `$output` does not equal the expected output. -# -# ```bash -# @test 'assert_output()' { -# run echo 'have' -# assert_output 'want' -# } -# -# @test 'assert_output() with pipe' { -# run echo 'hello' -# echo 'hello' | assert_output - -# } -# -# @test 'assert_output() with herestring' { -# run echo 'hello' -# assert_output - <<< hello -# } -# ``` -# -# On failure, the expected and actual output are displayed. -# -# ``` -# -- output differs -- -# expected : want -# actual : have -# -- -# ``` -# -# ## Existence -# -# To assert that any output exists at all, omit the `expected` argument. -# -# ```bash -# @test 'assert_output()' { -# run echo 'have' -# assert_output -# } -# ``` -# -# On failure, an error message is displayed. -# -# ``` -# -- no output -- -# expected non-empty output, but output was empty -# -- -# ``` -# -# ## Partial matching -# -# Partial matching can be enabled with the `--partial` option (`-p` for short). -# When used, the assertion fails if the expected _substring_ is not found in `$output`. -# -# ```bash -# @test 'assert_output() partial matching' { -# run echo 'ERROR: no such file or directory' -# assert_output --partial 'SUCCESS' -# } -# ``` -# -# On failure, the substring and the output are displayed. -# -# ``` -# -- output does not contain substring -- -# substring : SUCCESS -# output : ERROR: no such file or directory -# -- -# ``` -# -# ## Regular expression matching -# -# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -# When used, the assertion fails if the *extended regular expression* does not match `$output`. -# -# *__Note__: -# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output; -# not individual lines.* -# -# ```bash -# @test 'assert_output() regular expression matching' { -# run echo 'Foobar 0.1.0' -# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -# } -# ``` -# -# On failure, the regular expression and the output are displayed. -# -# ``` -# -- regular expression does not match output -- -# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -# output : Foobar 0.1.0 -# -- -# ``` -assert_output() { - __assert_stream "$@" -} - -# assert_stderr -# ============= -# -# Summary: Fail if `$stderr' does not match the expected stderr. -# -# Usage: assert_stderr [-p | -e] [- | [--] <expected>] -# -# Options: -# -p, --partial Match if `expected` is a substring of `$stderr` -# -e, --regexp Treat `expected` as an extended regular expression -# -, --stdin Read `expected` value from STDIN -# <expected> The expected value, substring or regular expression -# -# IO: -# STDIN - [=$1] expected stderr -# STDERR - details, on failure -# error message, on error -# Globals: -# stderr -# Returns: -# 0 - if stderr matches the expected value/partial/regexp -# 1 - otherwise -# -# Similarly to `assert_output`, this function verifies that a command or function produces the expected stderr. -# (It is the logical complement of `refute_stderr`.) -# The stderr matching can be literal (the default), partial or by regular expression. -# The expected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. -# -assert_stderr() { - __assert_stream "$@" -} - -__assert_stream() { - local -r caller=${FUNCNAME[1]} - local -r stream_type=${caller/assert_/} - local -i is_mode_partial=0 - local -i is_mode_regexp=0 - local -i is_mode_nonempty=0 - local -i use_stdin=0 - - if [[ ${stream_type} == "output" ]]; then - : "${output?}" - elif [[ ${stream_type} == "stderr" ]]; then - : "${stderr?}" - else - # Unknown caller - echo "Unexpected call to \`${FUNCNAME[0]}\` -Did you mean to call \`assert_output\` or \`assert_stderr\`?" | - batslib_decorate "ERROR: ${FUNCNAME[0]}" | - fail - return $? - fi - local -r stream="${!stream_type}" - - # Handle options. - if (( $# == 0 )); then - is_mode_nonempty=1 - fi - - while (( $# > 0 )); do - case "$1" in - -p|--partial) is_mode_partial=1; shift ;; - -e|--regexp) is_mode_regexp=1; shift ;; - -|--stdin) use_stdin=1; shift ;; - --) shift; break ;; - *) break ;; - esac - done - - if (( is_mode_partial )) && (( is_mode_regexp )); then - echo "\`--partial' and \`--regexp' are mutually exclusive" \ - | batslib_decorate "ERROR: ${caller}" \ - | fail - return $? - fi - - # Arguments. - local expected - if (( use_stdin )); then - expected="$(cat -)" - else - expected="${1-}" - fi - - # Matching. - if (( is_mode_nonempty )); then - if [ -z "$stream" ]; then - echo "expected non-empty $stream_type, but $stream_type was empty" \ - | batslib_decorate "no $stream_type" \ - | fail - fi - elif (( is_mode_regexp )); then - # shellcheck disable=2319 - if [[ '' =~ $expected ]] || (( $? == 2 )); then - echo "Invalid extended regular expression: \`$expected'" \ - | batslib_decorate "ERROR: ${caller}" \ - | fail - elif ! [[ $stream =~ $expected ]]; then - batslib_print_kv_single_or_multi 6 \ - 'regexp' "$expected" \ - "$stream_type" "$stream" \ - | batslib_decorate "regular expression does not match $stream_type" \ - | fail - fi - elif (( is_mode_partial )); then - if [[ $stream != *"$expected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$expected" \ - "$stream_type" "$stream" \ - | batslib_decorate "$stream_type does not contain substring" \ - | fail - fi - else - if [[ $stream != "$expected" ]]; then - batslib_print_kv_single_or_multi 8 \ - 'expected' "$expected" \ - 'actual' "$stream" \ - | batslib_decorate "$stream_type differs" \ - | fail - fi - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_regex.bash b/tests/bash/test_helper/bats-assert/src/assert_regex.bash deleted file mode 100644 index 17a7057..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_regex.bash +++ /dev/null @@ -1,56 +0,0 @@ -# `assert_regex` -# -# This function is similar to `assert_equal` but uses pattern matching instead -# of equality, by wrapping `[[ value =~ pattern ]]`. -# -# Fail if the value (first parameter) does not match the pattern (second -# parameter). -# -# ```bash -# @test 'assert_regex()' { -# assert_regex 'what' 'x$' -# } -# ``` -# -# On failure, the value and the pattern are displayed. -# -# ``` -# -- values does not match regular expression -- -# value : what -# pattern : x$ -# -- -# ``` -# -# If the value is longer than one line then it is displayed in *multi-line* -# format. -# -# An error is displayed if the specified extended regular expression is invalid. -# -# For description of the matching behavior, refer to the documentation of the -# `=~` operator in the -# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. -# Note that the `BASH_REMATCH` array is available immediately after the -# assertion succeeds but is fragile, i.e. prone to being overwritten as a side -# effect of other actions. -assert_regex() { - local -r value="${1}" - local -r pattern="${2}" - - if [[ '' =~ ${pattern} ]]; (( ${?} == 2 )); then - echo "Invalid extended regular expression: \`${pattern}'" \ - | batslib_decorate 'ERROR: assert_regex' \ - | fail - elif ! [[ "${value}" =~ ${pattern} ]]; then - if shopt -p nocasematch &>/dev/null; then - local case_sensitive=insensitive - else - local case_sensitive=sensitive - fi - batslib_print_kv_single_or_multi 8 \ - 'value' "${value}" \ - 'pattern' "${pattern}" \ - 'case' "${case_sensitive}" \ - | batslib_decorate 'value does not match regular expression' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/assert_success.bash b/tests/bash/test_helper/bats-assert/src/assert_success.bash deleted file mode 100644 index a8b2a38..0000000 --- a/tests/bash/test_helper/bats-assert/src/assert_success.bash +++ /dev/null @@ -1,44 +0,0 @@ -# assert_success -# ============== -# -# Summary: Fail if `$status` is not 0. -# -# Usage: assert_success -# -# IO: -# STDERR - `$status` and `$output`, on failure -# Globals: -# status -# output -# Returns: -# 0 - if `$status' is 0 -# 1 - otherwise -# -# ```bash -# @test 'assert_success() status only' { -# run bash -c "echo 'Error!'; exit 1" -# assert_success -# } -# ``` -# -# On failure, `$status` and `$output` are displayed. -# -# ``` -# -- command failed -- -# status : 1 -# output : Error! -# -- -# ``` -assert_success() { - : "${output?}" - : "${status?}" - - if (( status != 0 )); then - { local -ir width=6 - batslib_print_kv_single "$width" 'status' "$status" - batslib_print_kv_single_or_multi "$width" 'output' "$output" - } \ - | batslib_decorate 'command failed' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/refute.bash b/tests/bash/test_helper/bats-assert/src/refute.bash deleted file mode 100644 index e7c47da..0000000 --- a/tests/bash/test_helper/bats-assert/src/refute.bash +++ /dev/null @@ -1,42 +0,0 @@ -# refute -# ====== -# -# Summary: Fail if the given expression evaluates to true. -# -# Usage: refute <expression> -# -# Options: -# <expression> The expression to evaluate for falsiness. -# *__Note:__ The expression must be a simple command. -# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands), -# such as `[[`, can be used only when executed with `bash -c`.* -# -# IO: -# STDERR - the successful expression, on failure -# Globals: -# none -# Returns: -# 0 - if expression evaluates to false -# 1 - otherwise -# -# ```bash -# @test 'refute()' { -# rm -f '/var/log/test.log' -# refute [ -e '/var/log/test.log' ] -# } -# ``` -# -# On failure, the successful expression is displayed. -# -# ``` -# -- assertion succeeded, but it was expected to fail -- -# expression : [ -e /var/log/test.log ] -# -- -# ``` -refute() { - if "$@"; then - batslib_print_kv_single 10 'expression' "$*" \ - | batslib_decorate 'assertion succeeded, but it was expected to fail' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/refute_line.bash b/tests/bash/test_helper/bats-assert/src/refute_line.bash deleted file mode 100644 index bb7337d..0000000 --- a/tests/bash/test_helper/bats-assert/src/refute_line.bash +++ /dev/null @@ -1,318 +0,0 @@ -# refute_line -# =========== -# -# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number. -# -# Usage: refute_line [-n index] [-p | -e] [--] <unexpected> -# -# Options: -# -n, --index <idx> Match the <idx>th line -# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx> -# -e, --regexp Treat `unexpected` as an extended regular expression -# <unexpected> The unexpected line string, substring, or regular expression. -# -# IO: -# STDERR - details, on failure -# error message, on error -# Globals: -# output -# lines -# Returns: -# 0 - if match not found -# 1 - otherwise -# -# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output. -# (It is the logical complement of `assert_line`.) -# It checks that the unexpected line does not appear in the output (default) or at a specific line number. -# Matching can be literal (default), partial or regular expression. -# -# ## Looking for a line in the output -# -# By default, the entire output is searched for the unexpected line. -# The assertion fails if the unexpected line is found in `${lines[@]}`. -# -# ```bash -# @test 'refute_line() looking for line' { -# run echo $'have-0\nwant\nhave-2' -# refute_line 'want' -# } -# ``` -# -# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed. -# -# ``` -# -- line should not be in output -- -# line : want -# index : 1 -# output (3 lines): -# have-0 -# > want -# have-2 -# -- -# ``` -# -# ## Matching a specific line -# -# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index. -# The assertion fails if the unexpected line equals `${lines[<idx>]}`. -# -# ```bash -# @test 'refute_line() specific line' { -# run echo $'have-0\nwant-1\nhave-2' -# refute_line --index 1 'want-1' -# } -# ``` -# -# On failure, the index and the unexpected line are displayed. -# -# ``` -# -- line should differ -- -# index : 1 -# line : want-1 -# -- -# ``` -# -# ## Partial matching -# -# Partial matching can be enabled with the `--partial` option (`-p` for short). -# When used, a match fails if the unexpected *substring* is found in the matched line. -# -# ```bash -# @test 'refute_line() partial matching' { -# run echo $'have 1\nwant 2\nhave 3' -# refute_line --partial 'want' -# } -# ``` -# -# On failure, in addition to the details of literal matching, the substring is also displayed. -# When used with `--index <idx>` the substring replaces the unexpected line. -# -# ``` -# -- no line should contain substring -- -# substring : want -# index : 1 -# output (3 lines): -# have 1 -# > want 2 -# have 3 -# -- -# ``` -# -# ## Regular expression matching -# -# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -# When used, a match fails if the *extended regular expression* matches the line being tested. -# -# *__Note__: -# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.* -# -# ```bash -# @test 'refute_line() regular expression matching' { -# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29' -# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -# } -# ``` -# -# On failure, in addition to the details of literal matching, the regular expression is also displayed. -# When used with `--index <idx>` the regular expression replaces the unexpected line. -# -# ``` -# -- regular expression should not match line -- -# index : 0 -# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -# line : Foobar v0.1.0 -# -- -# ``` -# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! -refute_line() { - __refute_stream_line "$@" -} - -# refute_stderr_line -# ================== -# -# Summary: Fail if the unexpected line is found in the stderr (default) or at a specific line number. -# -# Usage: refute_stderr_line [-n index] [-p | -e] [--] <unexpected> -# -# Options: -# -n, --index <idx> Match the <idx>th line -# -p, --partial Match if `unexpected` is a substring of `$stderr` or line <idx> -# -e, --regexp Treat `unexpected` as an extended regular expression -# <unexpected> The unexpected line string, substring, or regular expression. -# -# IO: -# STDERR - details, on failure -# error message, on error -# Globals: -# stderr -# stderr_lines -# Returns: -# 0 - if match not found -# 1 - otherwise -# -# Similarly to `refute_stderr`, this function verifies that a command or function does not produce the unexpected stderr. -# (It is the logical complement of `assert_stderr_line`.) -# It checks that the unexpected line does not appear in the stderr (default) or at a specific line number. -# Matching can be literal (default), partial or regular expression. -# -refute_stderr_line() { - __refute_stream_line "$@" -} - -__refute_stream_line() { - local -r caller=${FUNCNAME[1]} - local -i is_match_line=0 - local -i is_mode_partial=0 - local -i is_mode_regexp=0 - - if [[ "${caller}" == "refute_line" ]]; then - : "${lines?}" - local -ar stream_lines=("${lines[@]}") - local -r stream_type=output - elif [[ "${caller}" == "refute_stderr_line" ]]; then - : "${stderr_lines?}" - local -ar stream_lines=("${stderr_lines[@]}") - local -r stream_type=stderr - else - # Unknown caller - echo "Unexpected call to \`${FUNCNAME[0]}\` -Did you mean to call \`refute_line\` or \`refute_stderr_line\`?" | - batslib_decorate "ERROR: ${FUNCNAME[0]}" | - fail - return $? - fi - - # 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: ${caller}" \ - | 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: ${caller}" \ - | 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: ${caller}" \ - | fail - return $? - fi - - # Matching. - if (( is_match_line )); then - # Specific line. - if (( is_mode_regexp )); then - if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then - batslib_print_kv_single 6 \ - 'index' "$idx" \ - 'regexp' "$unexpected" \ - 'line' "${stream_lines[$idx]}" \ - | batslib_decorate 'regular expression should not match line' \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then - batslib_print_kv_single 9 \ - 'index' "$idx" \ - 'substring' "$unexpected" \ - 'line' "${stream_lines[$idx]}" \ - | batslib_decorate 'line should not contain substring' \ - | fail - fi - else - if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then - batslib_print_kv_single 5 \ - 'index' "$idx" \ - 'line' "${stream_lines[$idx]}" \ - | batslib_decorate 'line should differ' \ - | fail - fi - fi - else - # Line contained in output/error stream. - if (( is_mode_regexp )); then - local -i idx - for (( idx = 0; idx < ${#stream_lines[@]}; ++idx )); do - if [[ ${stream_lines[$idx]} =~ $unexpected ]]; then - { local -ar single=( 'regexp' "$unexpected" 'index' "$idx" ) - local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 < ${#stream_lines[@]}; ++idx )); do - if [[ ${stream_lines[$idx]} == *"$unexpected"* ]]; then - { local -ar single=( 'substring' "$unexpected" 'index' "$idx" ) - local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 < ${#stream_lines[@]}; ++idx )); do - if [[ ${stream_lines[$idx]} == "$unexpected" ]]; then - { local -ar single=( 'line' "$unexpected" 'index' "$idx" ) - local -a may_be_multi=( "${stream_type}" "${!stream_type}" ) - 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 ${stream_type}" \ - | fail - return $? - fi - done - fi - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/refute_output.bash b/tests/bash/test_helper/bats-assert/src/refute_output.bash deleted file mode 100644 index d656515..0000000 --- a/tests/bash/test_helper/bats-assert/src/refute_output.bash +++ /dev/null @@ -1,246 +0,0 @@ -# refute_output -# ============= -# -# Summary: Fail if `$output' matches the unexpected output. -# -# Usage: refute_output [-p | -e] [- | [--] <unexpected>] -# -# Options: -# -p, --partial Match if `unexpected` is a substring of `$output` -# -e, --regexp Treat `unexpected` as an extended regular expression -# -, --stdin Read `unexpected` value from STDIN -# <unexpected> The unexpected value, substring, or regular expression -# -# IO: -# STDIN - [=$1] unexpected output -# STDERR - details, on failure -# error message, on error -# Globals: -# output -# Returns: -# 0 - if output matches the unexpected value/partial/regexp -# 1 - otherwise -# -# This function verifies that a command or function does not produce the unexpected output. -# (It is the logical complement of `assert_output`.) -# Output matching can be literal (the default), partial or by regular expression. -# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. -# -# ## Literal matching -# -# By default, literal matching is performed. -# The assertion fails if `$output` equals the unexpected output. -# -# ```bash -# @test 'refute_output()' { -# run echo 'want' -# refute_output 'want' -# } -# -# @test 'refute_output() with pipe' { -# run echo 'hello' -# echo 'world' | refute_output - -# } -# -# @test 'refute_output() with herestring' { -# run echo 'hello' -# refute_output - <<< world -# } -# ``` -# -# On failure, the output is displayed. -# -# ``` -# -- output equals, but it was expected to differ -- -# output : want -# -- -# ``` -# -# ## Existence -# -# To assert that there is no output at all, omit the matching argument. -# -# ```bash -# @test 'refute_output()' { -# run foo --silent -# refute_output -# } -# ``` -# -# On failure, an error message is displayed. -# -# ``` -# -- unexpected output -- -# expected no output, but output was non-empty -# -- -# ``` -# -# ## Partial matching -# -# Partial matching can be enabled with the `--partial` option (`-p` for short). -# When used, the assertion fails if the unexpected _substring_ is found in `$output`. -# -# ```bash -# @test 'refute_output() partial matching' { -# run echo 'ERROR: no such file or directory' -# refute_output --partial 'ERROR' -# } -# ``` -# -# On failure, the substring and the output are displayed. -# -# ``` -# -- output should not contain substring -- -# substring : ERROR -# output : ERROR: no such file or directory -# -- -# ``` -# -# ## Regular expression matching -# -# Regular expression matching can be enabled with the `--regexp` option (`-e` for short). -# When used, the assertion fails if the *extended regular expression* matches `$output`. -# -# *__Note__: -# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output; -# not individual lines.* -# -# ```bash -# @test 'refute_output() regular expression matching' { -# run echo 'Foobar v0.1.0' -# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$' -# } -# ``` -# -# On failure, the regular expression and the output are displayed. -# -# ``` -# -- regular expression should not match output -- -# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$ -# output : Foobar v0.1.0 -# -- -# ``` -refute_output() { - __refute_stream "$@" -} - -# refute_stderr -# ============= -# -# Summary: Fail if `$stderr' matches the unexpected output. -# -# Usage: refute_stderr [-p | -e] [- | [--] <unexpected>] -# -# Options: -# -p, --partial Match if `unexpected` is a substring of `$stderr` -# -e, --regexp Treat `unexpected` as an extended regular expression -# -, --stdin Read `unexpected` value from STDIN -# <unexpected> The unexpected value, substring, or regular expression -# -# IO: -# STDIN - [=$1] unexpected stderr -# STDERR - details, on failure -# error message, on error -# Globals: -# stderr -# Returns: -# 0 - if stderr matches the unexpected value/partial/regexp -# 1 - otherwise -# -# Similar to `refute_output`, this function verifies that a command or function does not produce the unexpected stderr. -# (It is the logical complement of `assert_stderr`.) -# The stderr matching can be literal (the default), partial or by regular expression. -# The unexpected stderr can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag. -# -refute_stderr() { - __refute_stream "$@" -} - -__refute_stream() { - local -r caller=${FUNCNAME[1]} - local -r stream_type=${caller/refute_/} - local -i is_mode_partial=0 - local -i is_mode_regexp=0 - local -i is_mode_empty=0 - local -i use_stdin=0 - - if [[ ${stream_type} == "output" ]]; then - : "${output?}" - elif [[ ${stream_type} == "stderr" ]]; then - : "${stderr?}" - else - # Not reachable: should be either output or stderr - : - fi - local -r stream="${!stream_type}" - - # Handle options. - if (( $# == 0 )); then - is_mode_empty=1 - fi - - while (( $# > 0 )); do - case "$1" in - -p|--partial) is_mode_partial=1; shift ;; - -e|--regexp) is_mode_regexp=1; shift ;; - -|--stdin) use_stdin=1; shift ;; - --) shift; break ;; - *) break ;; - esac - done - - if (( is_mode_partial )) && (( is_mode_regexp )); then - echo "\`--partial' and \`--regexp' are mutually exclusive" \ - | batslib_decorate "ERROR: ${caller}" \ - | fail - return $? - fi - - # Arguments. - local unexpected - if (( use_stdin )); then - unexpected="$(cat -)" - else - unexpected="${1-}" - fi - - if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then - echo "Invalid extended regular expression: \`$unexpected'" \ - | batslib_decorate "ERROR: ${caller}" \ - | fail - return $? - fi - - # Matching. - if (( is_mode_empty )); then - if [ -n "${stream}" ]; then - batslib_print_kv_single_or_multi 6 \ - "${stream_type}" "${stream}" \ - | batslib_decorate "${stream_type} non-empty, but expected no ${stream_type}" \ - | fail - fi - elif (( is_mode_regexp )); then - if [[ ${stream} =~ $unexpected ]]; then - batslib_print_kv_single_or_multi 6 \ - 'regexp' "$unexpected" \ - "${stream_type}" "${stream}" \ - | batslib_decorate "regular expression should not match ${stream_type}" \ - | fail - fi - elif (( is_mode_partial )); then - if [[ ${stream} == *"$unexpected"* ]]; then - batslib_print_kv_single_or_multi 9 \ - 'substring' "$unexpected" \ - "${stream_type}" "${stream}" \ - | batslib_decorate "${stream_type} should not contain substring" \ - | fail - fi - else - if [[ ${stream} == "$unexpected" ]]; then - batslib_print_kv_single_or_multi 6 \ - "${stream_type}" "${stream}" \ - | batslib_decorate "${stream_type} equals, but it was expected to differ" \ - | fail - fi - fi -} diff --git a/tests/bash/test_helper/bats-assert/src/refute_regex.bash b/tests/bash/test_helper/bats-assert/src/refute_regex.bash deleted file mode 100644 index 0918793..0000000 --- a/tests/bash/test_helper/bats-assert/src/refute_regex.bash +++ /dev/null @@ -1,66 +0,0 @@ -# `refute_regex` -# -# This function is similar to `refute_equal` but uses pattern matching instead -# of equality, by wrapping `! [[ value =~ pattern ]]`. -# -# Fail if the value (first parameter) matches the pattern (second parameter). -# -# ```bash -# @test 'refute_regex()' { -# refute_regex 'WhatsApp' 'Threema' -# } -# ``` -# -# On failure, the value, the pattern and the match are displayed. -# -# ``` -# @test 'refute_regex()' { -# refute_regex 'WhatsApp' 'What.' -# } -# -# -- value matches regular expression -- -# value : WhatsApp -# pattern : What. -# match : Whats -# case : sensitive -# -- -# ``` -# -# If the value or pattern is longer than one line then it is displayed in -# *multi-line* format. -# -# An error is displayed if the specified extended regular expression is invalid. -# -# For description of the matching behavior, refer to the documentation of the -# `=~` operator in the -# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html. -# -# Note that the `BASH_REMATCH` array is available immediately after the -# assertion fails but is fragile, i.e. prone to being overwritten as a side -# effect of other actions like calling `run`. Thus, it's good practice to avoid -# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable -# information the array contains is the matching part of the value which is -# printed in the failing test log, as mentioned above. -refute_regex() { - local -r value="${1}" - local -r pattern="${2}" - - if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then - echo "Invalid extended regular expression: \`${pattern}'" \ - | batslib_decorate 'ERROR: refute_regex' \ - | fail - elif [[ "${value}" =~ ${pattern} ]]; then - if shopt -p nocasematch &>/dev/null; then - local case_sensitive=insensitive - else - local case_sensitive=sensitive - fi - batslib_print_kv_single_or_multi 8 \ - 'value' "${value}" \ - 'pattern' "${pattern}" \ - 'match' "${BASH_REMATCH[0]}" \ - 'case' "${case_sensitive}" \ - | batslib_decorate 'value matches regular expression' \ - | fail - fi -} diff --git a/tests/bash/test_helper/bats-assert/test/assert.bats b/tests/bash/test_helper/bats-assert/test/assert.bats deleted file mode 100644 index d1a34a2..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert.bats +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'assert() <expression>: returns 0 if <expression> evaluates to TRUE' { - run assert true - assert_test_pass -} - -@test 'assert() <expression>: returns 1 and displays <expression> if it evaluates to FALSE' { - run assert false - - assert_test_fail <<'ERR_MSG' - --- assertion failed -- -expression : false --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_equal.bats b/tests/bash/test_helper/bats-assert/test/assert_equal.bats deleted file mode 100644 index 7d3fc86..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_equal.bats +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'assert_equal() <actual> <expected>: returns 0 if <actual> equals <expected>' { - run assert_equal 'a' 'a' - assert_test_pass -} - -@test 'assert_equal() <actual> <expected>: returns 1 and displays details if <actual> does not equal <expected>' { - run assert_equal 'a' 'b' - - assert_test_fail <<'ERR_MSG' - --- values do not equal -- -expected : b -actual : a --- -ERR_MSG -} - -@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <actual> is longer than one line' { - run assert_equal $'a 0\na 1' 'b' - - assert_test_fail <<'ERR_MSG' - --- values do not equal -- -expected (1 lines): - b -actual (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -@test 'assert_equal() <actual> <expected>: displays details in multi-line format if <expected> is longer than one line' { - run assert_equal 'a' $'b 0\nb 1' - - assert_test_fail <<'ERR_MSG' - --- values do not equal -- -expected (2 lines): - b 0 - b 1 -actual (1 lines): - a --- -ERR_MSG -} - -@test 'assert_equal() <actual> <expected>: performs literal matching' { - run assert_equal 'a' '*' - - assert_test_fail <<'ERR_MSG' - --- values do not equal -- -expected : * -actual : a --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_failure.bats b/tests/bash/test_helper/bats-assert/test/assert_failure.bats deleted file mode 100644 index 6291afe..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_failure.bats +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test "assert_failure(): returns 0 if \`\$status' is not 0" { - run false - run assert_failure - assert_test_pass -} - -@test "assert_failure(): returns 1 and displays details if \`\$status' is 0" { - run bash -c 'echo "a" - exit 0' - run assert_failure - - assert_test_fail <<'ERR_MSG' - --- command succeeded, but it was expected to fail -- -output : a --- -ERR_MSG -} - -@test "assert_failure(): displays \`\$output' in multi-line format if it is longer then one line" { - run bash -c 'printf "a 0\na 1" - exit 0' - run assert_failure - - assert_test_fail <<'ERR_MSG' - --- command succeeded, but it was expected to fail -- -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -@test "assert_failure() <status>: returns 0 if \`\$status' equals <status>" { - run bash -c 'exit 1' - run assert_failure 1 - assert_test_pass -} - -@test "assert_failure() <status>: returns 1 and displays details if \`\$status' does not equal <status>" { - run bash -c 'echo "a" - exit 1' - run assert_failure 2 - - assert_test_fail <<'ERR_MSG' - --- command failed as expected, but status differs -- -expected : 2 -actual : 1 -output : a --- -ERR_MSG -} - -@test "assert_failure() <status>: displays \`\$output' in multi-line format if it is longer then one line" { - run bash -c 'printf "a 0\na 1" - exit 1' - run assert_failure 2 - - assert_test_fail <<'ERR_MSG' - --- command failed as expected, but status differs -- -expected : 2 -actual : 1 -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_line.bats b/tests/bash/test_helper/bats-assert/test/assert_line.bats deleted file mode 100644 index b69ed59..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_line.bats +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - - -############################################################################### -# Containing a line -############################################################################### - -# -# Literal matching -# - -# Correctness -@test "assert_line() <expected>: returns 0 if <expected> is a line in \`\${lines[@]}'" { - run printf 'a\nb\nc' - run assert_line 'b' - assert_test_pass -} - -@test "assert_line() <expected>: returns 1 and displays details if <expected> is not a line in \`\${lines[@]}'" { - run echo 'b' - run assert_line 'a' - - assert_test_fail <<'ERR_MSG' - --- output does not contain line -- -line : a -output : b --- -ERR_MSG -} - -# Output formatting -@test "assert_line() <expected>: displays \`\$output' in multi-line format if it is longer than one line" { - run printf 'b 0\nb 1' - run assert_line 'a' - - assert_test_fail <<'ERR_MSG' - --- output does not contain line -- -line : a -output (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -# Options -@test 'assert_line() <expected>: performs literal matching by default' { - run echo 'a' - run assert_line '*' - - assert_test_fail <<'ERR_MSG' - --- output does not contain line -- -line : * -output : a --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'assert_line() -p <partial>: enables partial matching' { - run printf 'a\n_b_\nc' - run assert_line -p 'b' - assert_test_pass -} - -@test 'assert_line() --partial <partial>: enables partial matching' { - run printf 'a\n_b_\nc' - run assert_line --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_line() --partial <partial>: returns 0 if <partial> is a substring in any line in \`\${lines[@]}'" { - run printf 'a\n_b_\nc' - run assert_line --partial 'b' - assert_test_pass -} - -@test "assert_line() --partial <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${lines[@]}'" { - run echo 'b' - run assert_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no output line contains substring -- -substring : a -output : b --- -ERR_MSG -} - -# Output formatting -@test "assert_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" { - run printf 'b 0\nb 1' - run assert_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no output line contains substring -- -substring : a -output (2 lines): - b 0 - b 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'assert_line() -e <regexp>: enables regular expression matching' { - run printf 'a\n_b_\nc' - run assert_line -e '^.b' - assert_test_pass -} - -@test 'assert_line() --regexp <regexp>: enables regular expression matching' { - run printf 'a\n_b_\nc' - run assert_line --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "assert_line() --regexp <regexp>: returns 0 if <regexp> matches any line in \`\${lines[@]}'" { - run printf 'a\n_b_\nc' - run assert_line --regexp '^.b' - assert_test_pass -} - -@test "assert_line() --regexp <regexp>: returns 1 and displays details if <regexp> does not match any lines in \`\${lines[@]}'" { - run echo 'b' - run assert_line --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- no output line matches regular expression -- -regexp : ^.a -output : b --- -ERR_MSG -} - -# Output formatting -@test "assert_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" { - run printf 'b 0\nb 1' - run assert_line --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- no output line matches regular expression -- -regexp : ^.a -output (2 lines): - b 0 - b 1 --- -ERR_MSG -} - - -############################################################################### -# Matching single line: `-n' and `--index' -############################################################################### - -# Options -@test 'assert_line() -n <idx> <expected>: matches against the <idx>-th line only' { - run printf 'a\nb\nc' - run assert_line -n 1 'b' - assert_test_pass -} - -@test 'assert_line() --index <idx> <expected>: matches against the <idx>-th line only' { - run printf 'a\nb\nc' - run assert_line --index 1 'b' - assert_test_pass -} - -@test 'assert_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { - run assert_line --index 1a - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_line -- -`--index' requires an integer argument: `1a' --- -ERR_MSG -} - - -# -# Literal matching -# - -# Correctness -@test "assert_line() --index <idx> <expected>: returns 0 if <expected> equals \`\${lines[<idx>]}'" { - run printf 'a\nb\nc' - run assert_line --index 1 'b' - assert_test_pass -} - -@test "assert_line() --index <idx> <expected>: returns 1 and displays details if <expected> does not equal \`\${lines[<idx>]}'" { - run printf 'a\nb\nc' - run assert_line --index 1 'a' - - assert_test_fail <<'ERR_MSG' - --- line differs -- -index : 1 -expected : a -actual : b --- -ERR_MSG -} - -# Options -@test 'assert_line() --index <idx> <expected>: performs literal matching by default' { - run printf 'a\nb\nc' - run assert_line --index 1 '*' - - assert_test_fail <<'ERR_MSG' - --- line differs -- -index : 1 -expected : * -actual : b --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'assert_line() --index <idx> -p <partial>: enables partial matching' { - run printf 'a\n_b_\nc' - run assert_line --index 1 -p 'b' - assert_test_pass -} - -@test 'assert_line() --index <idx> --partial <partial>: enables partial matching' { - run printf 'a\n_b_\nc' - run assert_line --index 1 --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_line() --index <idx> --partial <partial>: returns 0 if <partial> is a substring in \`\${lines[<idx>]}'" { - run printf 'a\n_b_\nc' - run assert_line --index 1 --partial 'b' - assert_test_pass -} - -@test "assert_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\${lines[<idx>]}'" { - run printf 'b 0\nb 1' - run assert_line --index 1 --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- line does not contain substring -- -index : 1 -substring : a -line : b 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'assert_line() --index <idx> -e <regexp>: enables regular expression matching' { - run printf 'a\n_b_\nc' - run assert_line --index 1 -e '^.b' - assert_test_pass -} - -@test 'assert_line() --index <idx> --regexp <regexp>: enables regular expression matching' { - run printf 'a\n_b_\nc' - run assert_line --index 1 --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "assert_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> matches \`\${lines[<idx>]}'" { - run printf 'a\n_b_\nc' - run assert_line --index 1 --regexp '^.b' - assert_test_pass -} - -@test "assert_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\${lines[<idx>]}'" { - run printf 'a\nb\nc' - run assert_line --index 1 --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match line -- -index : 1 -regexp : ^.a -line : b --- -ERR_MSG -} - - -############################################################################### -# Common -############################################################################### - -@test "assert_line(): \`--partial' and \`--regexp' are mutually exclusive" { - run assert_line --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_line -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test 'assert_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run assert_line --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_line -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - -@test "assert_line(): \`--' stops parsing options" { - run printf 'a\n-p\nc' - run assert_line -- '-p' - assert_test_pass -} - -@test "__assert_line(): call to __assert_line shows error" { - run __assert_line - assert_test_fail <<'ERR_MSG' - --- ERROR: __assert_line -- -Unexpected call to `__assert_line` -Did you mean to call `assert_line` or `assert_stderr_line`? --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats b/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats deleted file mode 100644 index 42b5315..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_not_equal.bats +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'assert_not_equal() <actual> <unexpected>: returns 0 if <actual> does not equal <unexpected>' { - run assert_not_equal foo bar - assert_test_pass - - run assert_not_equal "foo" "bar" - assert_test_pass - - run assert_not_equal "foo" "" - assert_test_pass - - run assert_not_equal "" "foo" - assert_test_pass -} - -@test 'assert_not_equal() <actual> <unexpected>: returns 1 and displays details if <actual> equals <unexpected>' { - run assert_not_equal 'foobar' 'foobar' - assert_test_fail <<'ERR_MSG' - --- values should not be equal -- -unexpected : foobar -actual : foobar --- -ERR_MSG - - run assert_not_equal 1 1 - assert_test_fail <<'ERR_MSG' - --- values should not be equal -- -unexpected : 1 -actual : 1 --- -ERR_MSG -} - -@test 'assert_not_equal() <actual> <unexpected>: displays details in multi-line format if <actual> and <unexpected> are longer than one line' { - run assert_not_equal $'foo\nbar' $'foo\nbar' - assert_test_fail <<'ERR_MSG' - --- values should not be equal -- -unexpected (2 lines): - foo - bar -actual (2 lines): - foo - bar --- -ERR_MSG -} - -@test 'assert_not_equal() <actual> <unexpected>: performs literal matching' { - run assert_not_equal 'a' '*' - assert_test_pass -} \ No newline at end of file diff --git a/tests/bash/test_helper/bats-assert/test/assert_output.bats b/tests/bash/test_helper/bats-assert/test/assert_output.bats deleted file mode 100644 index 6671203..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_output.bats +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -# -# Literal matching -# - -# Correctness -@test "assert_output() <expected>: returns 0 if <expected> equals \`\$output'" { - run echo 'a' - run assert_output 'a' - assert_test_pass -} - -@test "assert_output() <expected>: returns 1 and displays details if <expected> does not equal \`\$output'" { - run echo 'b' - run assert_output 'a' - - assert_test_fail <<'ERR_MSG' - --- output differs -- -expected : a -actual : b --- -ERR_MSG -} - -@test 'assert_output(): succeeds if output is non-empty' { - run echo 'a' - run assert_output - - assert_test_pass -} - -@test 'assert_output(): fails if output is empty' { - run echo '' - run assert_output - - assert_test_fail <<'ERR_MSG' - --- no output -- -expected non-empty output, but output was empty --- -ERR_MSG -} - -@test 'assert_output() - : reads <expected> from STDIN' { - run echo 'a' - run assert_output - <<STDIN -a -STDIN - - assert_test_pass -} - -@test 'assert_output() --stdin : reads <expected> from STDIN' { - run echo 'a' - run assert_output --stdin <<STDIN -a -STDIN - - assert_test_pass -} - -# Output formatting -@test "assert_output() <expected>: displays details in multi-line format if \`\$output' is longer than one line" { - run printf 'b 0\nb 1' - run assert_output 'a' - - assert_test_fail <<'ERR_MSG' - --- output differs -- -expected (1 lines): - a -actual (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_output() <expected>: displays details in multi-line format if <expected> is longer than one line' { - run echo 'b' - run assert_output $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- output differs -- -expected (2 lines): - a 0 - a 1 -actual (1 lines): - b --- -ERR_MSG -} - -# Options -@test 'assert_output() <expected>: performs literal matching by default' { - run echo 'a' - run assert_output '*' - - assert_test_fail <<'ERR_MSG' - --- output differs -- -expected : * -actual : a --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -@test 'assert_output() -p <partial>: enables partial matching' { - run echo 'abc' - run assert_output -p 'b' - assert_test_pass -} - -@test 'assert_output() --partial <partial>: enables partial matching' { - run echo 'abc' - run assert_output --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_output() --partial <partial>: returns 0 if <partial> is a substring in \`\$output'" { - run printf 'a\nb\nc' - run assert_output --partial 'b' - assert_test_pass -} - -@test "assert_output() --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\$output'" { - run echo 'b' - run assert_output --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- output does not contain substring -- -substring : a -output : b --- -ERR_MSG -} - -# Output formatting -@test "assert_output() --partial <partial>: displays details in multi-line format if \`\$output' is longer than one line" { - run printf 'b 0\nb 1' - run assert_output --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- output does not contain substring -- -substring (1 lines): - a -output (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_output() --partial <partial>: displays details in multi-line format if <partial> is longer than one line' { - run echo 'b' - run assert_output --partial $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- output does not contain substring -- -substring (2 lines): - a 0 - a 1 -output (1 lines): - b --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -@test 'assert_output() -e <regexp>: enables regular expression matching' { - run echo 'abc' - run assert_output -e '^a' - assert_test_pass -} - -@test 'assert_output() --regexp <regexp>: enables regular expression matching' { - run echo 'abc' - run assert_output --regexp '^a' - assert_test_pass -} - -# Correctness -@test "assert_output() --regexp <regexp>: returns 0 if <regexp> matches \`\$output'" { - run printf 'a\nb\nc' - run assert_output --regexp '.*b.*' - assert_test_pass -} - -@test "assert_output() --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\$output'" { - run echo 'b' - run assert_output --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match output -- -regexp : .*a.* -output : b --- -ERR_MSG -} - -# Output formatting -@test "assert_output() --regexp <regexp>: displays details in multi-line format if \`\$output' is longer than one line" { - run printf 'b 0\nb 1' - run assert_output --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match output -- -regexp (1 lines): - .*a.* -output (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_output() --regexp <regexp>: displays details in multi-line format if <regexp> is longer than one line' { - run echo 'b' - run assert_output --regexp $'.*a\nb.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match output -- -regexp (2 lines): - .*a - b.* -output (1 lines): - b --- -ERR_MSG -} - -# Error handling -@test 'assert_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run assert_output --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_output -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - - -# -# Common -# - -@test "assert_output(): \`--partial' and \`--regexp' are mutually exclusive" { - run assert_output --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_output -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test "assert_output(): \`--' stops parsing options" { - run echo '-p' - run assert_output -- '-p' - assert_test_pass -} - -@test "__assert_stream(): call to __assert_stream shows error" { - run __assert_stream - assert_test_fail <<'ERR_MSG' - --- ERROR: __assert_stream -- -Unexpected call to `__assert_stream` -Did you mean to call `assert_output` or `assert_stderr`? --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_regex.bats b/tests/bash/test_helper/bats-assert/test/assert_regex.bats deleted file mode 100644 index 0a9c902..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_regex.bats +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -# -# Literal matching -# - -# Correctness -@test "assert_regex() <value> <pattern>: succeeds if a <value> substring matches extended regular expression <pattern>" { - run assert_regex 'abc' '^[a-z]b[c-z]+' - assert_test_pass -} - -@test "assert_regex() <value> <pattern>: fails if no <value> substring matches extended regular expression <pattern>" { - run assert_regex 'bcd' '^[a-z]b[c-z]+' - assert_test_fail <<'ERR_MSG' - --- value does not match regular expression -- -value : bcd -pattern : ^[a-z]b[c-z]+ -case : sensitive --- -ERR_MSG -} - -@test "assert_regex() <value> <pattern>: provides results in BASH_REMATCH" { - unset -v BASH_REMATCH - - assert_regex 'abcd' 'b.d' - declare -p BASH_REMATCH - [ "${BASH_REMATCH[0]}" = 'bcd' ] -} - -@test "assert_regex() <value> <pattern>: matches case-insensitively when 'nocasematch' is set" { - shopt -s nocasematch - - assert_regex 'aBc' 'ABC' -} - -@test "assert_regex() <value> <pattern>: outputs multi-line <value> nicely when it fails" { - run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' - assert_test_fail <<'ERR_MSG' - --- value does not match regular expression -- -value (2 lines): - bcd - 123 -pattern (1 lines): - ^[a-z]b[c-z]+ -case (1 lines): - sensitive --- -ERR_MSG - - shopt -s nocasematch - run assert_regex $'bcd\n123' '^[a-z]b[c-z]+' - assert_test_fail <<'ERR_MSG' - --- value does not match regular expression -- -value (2 lines): - bcd - 123 -pattern (1 lines): - ^[a-z]b[c-z]+ -case (1 lines): - insensitive --- -ERR_MSG -} - -# Error handling -@test "assert_regex() <value> <pattern>: returns 1 and displays an error message if <pattern> is not a valid extended regular expression" { - run assert_regex value '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_regex -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - -@test "assert_regex allows regex matching empty string (see #53)" { - run assert_regex any_value '.*' - assert_success -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_stderr.bats b/tests/bash/test_helper/bats-assert/test/assert_stderr.bats deleted file mode 100644 index ebedde8..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_stderr.bats +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -setup_file() { - bats_require_minimum_version 1.5.0 -} - -echo_err() { - echo "$@" >&2 -} - -printf_err() { - # shellcheck disable=2059 - printf "$@" >&2 -} - -# -# Literal matching -# - -# Correctness -@test "assert_stderr() <expected>: returns 0 if <expected> equals \`\$stderr'" { - run --separate-stderr echo_err 'a' - run assert_stderr 'a' - assert_test_pass -} - -@test "assert_stderr() <expected>: returns 1 and displays details if <expected> does not equal \`\$stderr'" { - run --separate-stderr echo_err 'b' - run assert_stderr 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr differs -- -expected : a -actual : b --- -ERR_MSG -} - -@test 'assert_stderr(): succeeds if stderr is non-empty' { - run --separate-stderr echo_err 'a' - run assert_stderr - - assert_test_pass -} - -@test 'assert_stderr(): fails if stderr is empty' { - run --separate-stderr echo_err '' - run assert_stderr - - assert_test_fail <<'ERR_MSG' - --- no stderr -- -expected non-empty stderr, but stderr was empty --- -ERR_MSG -} - -@test 'assert_stderr() - : reads <expected> from STDIN' { - run --separate-stderr echo_err 'a' - run assert_stderr - <<STDIN -a -STDIN - - assert_test_pass -} - -@test 'assert_stderr() --stdin : reads <expected> from STDIN' { - run --separate-stderr echo_err 'a' - run assert_stderr --stdin <<STDIN -a -STDIN - - assert_test_pass -} - -# stderr formatting -@test "assert_stderr() <expected>: displays details in multi-line format if \`\$stderr' is longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr differs -- -expected (1 lines): - a -actual (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_stderr() <expected>: displays details in multi-line format if <expected> is longer than one line' { - run --separate-stderr echo_err 'b' - run assert_stderr $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- stderr differs -- -expected (2 lines): - a 0 - a 1 -actual (1 lines): - b --- -ERR_MSG -} - -# Options -@test 'assert_stderr() <expected>: performs literal matching by default' { - run --separate-stderr echo_err 'a' - run assert_stderr '*' - - assert_test_fail <<'ERR_MSG' - --- stderr differs -- -expected : * -actual : a --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -@test 'assert_stderr() -p <partial>: enables partial matching' { - run --separate-stderr echo_err 'abc' - run assert_stderr -p 'b' - assert_test_pass -} - -@test 'assert_stderr() --partial <partial>: enables partial matching' { - run --separate-stderr echo_err 'abc' - run assert_stderr --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_stderr() --partial <partial>: returns 0 if <partial> is a substring in \`\$stderr'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr --partial 'b' - assert_test_pass -} - -@test "assert_stderr() --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\$stderr'" { - run --separate-stderr echo_err 'b' - run assert_stderr --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain substring -- -substring : a -stderr : b --- -ERR_MSG -} - -# stderr formatting -@test "assert_stderr() --partial <partial>: displays details in multi-line format if \`\$stderr' is longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain substring -- -substring (1 lines): - a -stderr (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_stderr() --partial <partial>: displays details in multi-line format if <partial> is longer than one line' { - run --separate-stderr echo_err 'b' - run assert_stderr --partial $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain substring -- -substring (2 lines): - a 0 - a 1 -stderr (1 lines): - b --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -@test 'assert_stderr() -e <regexp>: enables regular expression matching' { - run --separate-stderr echo_err 'abc' - run assert_stderr -e '^a' - assert_test_pass -} - -@test 'assert_stderr() --regexp <regexp>: enables regular expression matching' { - run --separate-stderr echo_err 'abc' - run assert_stderr --regexp '^a' - assert_test_pass -} - -# Correctness -@test "assert_stderr() --regexp <regexp>: returns 0 if <regexp> matches \`\$stderr'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr --regexp '.*b.*' - assert_test_pass -} - -@test "assert_stderr() --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\$stderr'" { - run --separate-stderr echo_err 'b' - run assert_stderr --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match stderr -- -regexp : .*a.* -stderr : b --- -ERR_MSG -} - -# stderr formatting -@test "assert_stderr() --regexp <regexp>: displays details in multi-line format if \`\$stderr' is longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match stderr -- -regexp (1 lines): - .*a.* -stderr (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -@test 'assert_stderr() --regexp <regexp>: displays details in multi-line format if <regexp> is longer than one line' { - run --separate-stderr echo_err 'b' - run assert_stderr --regexp $'.*a\nb.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match stderr -- -regexp (2 lines): - .*a - b.* -stderr (1 lines): - b --- -ERR_MSG -} - -# Error handling -@test 'assert_stderr() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run assert_stderr --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_stderr -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - - -# -# Common -# - -@test "assert_stderr(): \`--partial' and \`--regexp' are mutually exclusive" { - run assert_stderr --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_stderr -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test "assert_stderr(): \`--' stops parsing options" { - run --separate-stderr echo_err '-p' - run assert_stderr -- '-p' - assert_test_pass -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats b/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats deleted file mode 100644 index bb3a071..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_stderr_line.bats +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -setup_file() { - bats_require_minimum_version 1.5.0 -} - -echo_err() { - echo "$@" >&2 -} - -printf_err() { - # shellcheck disable=2059 - printf "$@" >&2 -} - - -############################################################################### -# Containing a line -############################################################################### - -# -# Literal matching -# - -# Correctness -@test "assert_stderr_line() <expected>: returns 0 if <expected> is a line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line 'b' - assert_test_pass -} - -@test "assert_stderr_line() <expected>: returns 1 and displays details if <expected> is not a line in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'b' - run assert_stderr_line 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain line -- -line : a -stderr : b --- -ERR_MSG -} - -# stderr formatting -@test "assert_stderr_line() <expected>: displays \`\$stderr' in multi-line format if it is longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr_line 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain line -- -line : a -stderr (2 lines): - b 0 - b 1 --- -ERR_MSG -} - -# Options -@test 'assert_stderr_line() <expected>: performs literal matching by default' { - run --separate-stderr echo_err 'a' - run assert_stderr_line '*' - - assert_test_fail <<'ERR_MSG' - --- stderr does not contain line -- -line : * -stderr : a --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'assert_stderr_line() -p <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line -p 'b' - assert_test_pass -} - -@test 'assert_stderr_line() --partial <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_stderr_line() --partial <partial>: returns 0 if <partial> is a substring in any line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --partial 'b' - assert_test_pass -} - -@test "assert_stderr_line() --partial <partial>: returns 1 and displays details if <partial> is not a substring in any lines in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'b' - run assert_stderr_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no stderr line contains substring -- -substring : a -stderr : b --- -ERR_MSG -} - -# stderr formatting -@test "assert_stderr_line() --partial <partial>: displays \`\$stderr' in multi-line format if it is longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no stderr line contains substring -- -substring : a -stderr (2 lines): - b 0 - b 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'assert_stderr_line() -e <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line -e '^.b' - assert_test_pass -} - -@test 'assert_stderr_line() --regexp <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "assert_stderr_line() --regexp <regexp>: returns 0 if <regexp> matches any line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --regexp '^.b' - assert_test_pass -} - -@test "assert_stderr_line() --regexp <regexp>: returns 1 and displays details if <regexp> does not match any lines in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'b' - run assert_stderr_line --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- no stderr line matches regular expression -- -regexp : ^.a -stderr : b --- -ERR_MSG -} - -# stderr formatting -@test "assert_stderr_line() --regexp <regexp>: displays \`\$stderr' in multi-line format if longer than one line" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr_line --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- no stderr line matches regular expression -- -regexp : ^.a -stderr (2 lines): - b 0 - b 1 --- -ERR_MSG -} - - -############################################################################### -# Matching single line: `-n' and `--index' -############################################################################### - -# Options -@test 'assert_stderr_line() -n <idx> <expected>: matches against the <idx>-th line only' { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line -n 1 'b' - assert_test_pass -} - -@test 'assert_stderr_line() --index <idx> <expected>: matches against the <idx>-th line only' { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line --index 1 'b' - assert_test_pass -} - -@test 'assert_stderr_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { - run assert_stderr_line --index 1a - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_stderr_line -- -`--index' requires an integer argument: `1a' --- -ERR_MSG -} - - -# -# Literal matching -# - -# Correctness -@test "assert_stderr_line() --index <idx> <expected>: returns 0 if <expected> equals \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line --index 1 'b' - assert_test_pass -} - -@test "assert_stderr_line() --index <idx> <expected>: returns 1 and displays details if <expected> does not equal \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line --index 1 'a' - - assert_test_fail <<'ERR_MSG' - --- line differs -- -index : 1 -expected : a -actual : b --- -ERR_MSG -} - -# Options -@test 'assert_stderr_line() --index <idx> <expected>: performs literal matching by default' { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line --index 1 '*' - - assert_test_fail <<'ERR_MSG' - --- line differs -- -index : 1 -expected : * -actual : b --- -ERR_MSG -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'assert_stderr_line() --index <idx> -p <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 -p 'b' - assert_test_pass -} - -@test 'assert_stderr_line() --index <idx> --partial <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 --partial 'b' - assert_test_pass -} - -# Correctness -@test "assert_stderr_line() --index <idx> --partial <partial>: returns 0 if <partial> is a substring in \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 --partial 'b' - assert_test_pass -} - -@test "assert_stderr_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is not a substring in \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'b 0\nb 1' - run assert_stderr_line --index 1 --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- line does not contain substring -- -index : 1 -substring : a -line : b 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'assert_stderr_line() --index <idx> -e <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 -e '^.b' - assert_test_pass -} - -@test 'assert_stderr_line() --index <idx> --regexp <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "assert_stderr_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> matches \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\n_b_\nc' - run assert_stderr_line --index 1 --regexp '^.b' - assert_test_pass -} - -@test "assert_stderr_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> does not match \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run assert_stderr_line --index 1 --regexp '^.a' - - assert_test_fail <<'ERR_MSG' - --- regular expression does not match line -- -index : 1 -regexp : ^.a -line : b --- -ERR_MSG -} - - -############################################################################### -# Common -############################################################################### - -@test "assert_stderr_line(): \`--partial' and \`--regexp' are mutually exclusive" { - run assert_stderr_line --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_stderr_line -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test 'assert_stderr_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run assert_stderr_line --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: assert_stderr_line -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - -@test "assert_stderr_line(): \`--' stops parsing options" { - run --separate-stderr printf_err 'a\n-p\nc' - run assert_stderr_line -- '-p' - assert_test_pass -} diff --git a/tests/bash/test_helper/bats-assert/test/assert_success.bats b/tests/bash/test_helper/bats-assert/test/assert_success.bats deleted file mode 100644 index 66f5aa7..0000000 --- a/tests/bash/test_helper/bats-assert/test/assert_success.bats +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test "assert_success(): returns 0 if \`\$status' is 0" { - run true - run assert_success - - assert_test_pass -} - -@test "assert_success(): returns 1 and displays details if \`\$status' is not 0" { - run bash -c 'echo "a" - exit 1' - run assert_success - - assert_test_fail <<'ERR_MSG' - --- command failed -- -status : 1 -output : a --- -ERR_MSG -} - -@test "assert_success(): displays \`\$output' in multi-line format if it is longer than one line" { - run bash -c 'printf "a 0\na 1" - exit 1' - run assert_success - - assert_test_fail <<'ERR_MSG' - --- command failed -- -status : 1 -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/refute.bats b/tests/bash/test_helper/bats-assert/test/refute.bats deleted file mode 100644 index f9e2737..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute.bats +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'refute() <expression>: returns 0 if <expression> evaluates to FALSE' { - run refute false - assert_test_pass -} - -@test 'refute() <expression>: returns 1 and displays <expression> if it evaluates to TRUE' { - run refute true - assert_test_fail <<'ERR_MSG' - --- assertion succeeded, but it was expected to fail -- -expression : true --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/refute_line.bats b/tests/bash/test_helper/bats-assert/test/refute_line.bats deleted file mode 100644 index e0ad762..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute_line.bats +++ /dev/null @@ -1,355 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - - -############################################################################### -# Containing a line -############################################################################### - -# -# Literal matching -# - -# Correctness -@test "refute_line() <unexpected>: returns 0 if <unexpected> is not a line in \`\${lines[@]}'" { - run printf 'a\nb\nc' - run refute_line 'd' - assert_test_pass -} - -@test "refute_line() <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${lines[@]}'" { - run echo 'a' - run refute_line 'a' - - assert_test_fail <<'ERR_MSG' - --- line should not be in output -- -line : a -index : 0 -output : a --- -ERR_MSG -} - -# Output formatting -@test "refute_line() <unexpected>: displays \`\$output' in multi-line format if it is longer than one line" { - run printf 'a 0\na 1\na 2' - run refute_line 'a 1' - - assert_test_fail <<'ERR_MSG' - --- line should not be in output -- -line : a 1 -index : 1 -output (3 lines): - a 0 -> a 1 - a 2 --- -ERR_MSG -} - -# Options -@test 'refute_line() <unexpected>: performs literal matching by default' { - run echo 'a' - run refute_line '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_line() -p <partial>: enables partial matching' { - run printf 'a\nb\nc' - run refute_line -p 'd' - assert_test_pass -} - -@test 'refute_line() --partial <partial>: enables partial matching' { - run printf 'a\nb\nc' - run refute_line --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_line() --partial <partial>: returns 0 if <partial> is not a substring in any line in \`\${lines[@]}'" { - run printf 'a\nb\nc' - run refute_line --partial 'd' - assert_test_pass -} - -@test "refute_line() --partial <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${lines[@]}'" { - run echo 'a' - run refute_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no line should contain substring -- -substring : a -index : 0 -output : a --- -ERR_MSG -} - -# Output formatting -@test "refute_line() --partial <partial>: displays \`\$output' in multi-line format if it is longer than one line" { - run printf 'a\nabc\nc' - run refute_line --partial 'b' - - assert_test_fail <<'ERR_MSG' - --- no line should contain substring -- -substring : b -index : 1 -output (3 lines): - a -> abc - c --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_line() -e <regexp>: enables regular expression matching' { - run printf 'a\nb\nc' - run refute_line -e '^.d' - assert_test_pass -} - -@test 'refute_line() --regexp <regexp>: enables regular expression matching' { - run printf 'a\nb\nc' - run refute_line --regexp '^.d' - assert_test_pass -} - -# Correctness -@test "refute_line() --regexp <regexp>: returns 0 if <regexp> does not match any line in \`\${lines[@]}'" { - run printf 'a\nb\nc' - run refute_line --regexp '.*d.*' - assert_test_pass -} - -@test "refute_line() --regexp <regexp>: returns 1 and displays details if <regexp> matches any lines in \`\${lines[@]}'" { - run echo 'a' - run refute_line --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- no line should match the regular expression -- -regexp : .*a.* -index : 0 -output : a --- -ERR_MSG -} - -# Output formatting -@test "refute_line() --regexp <regexp>: displays \`\$output' in multi-line format if longer than one line" { - run printf 'a\nabc\nc' - run refute_line --regexp '.*b.*' - - assert_test_fail <<'ERR_MSG' - --- no line should match the regular expression -- -regexp : .*b.* -index : 1 -output (3 lines): - a -> abc - c --- -ERR_MSG -} - - -############################################################################### -# Matching single line: `-n' and `--index' -############################################################################### - -# Options -@test 'refute_line() -n <idx> <expected>: matches against the <idx>-th line only' { - run printf 'a\nb\nc' - run refute_line -n 1 'd' - assert_test_pass -} - -@test 'refute_line() --index <idx> <expected>: matches against the <idx>-th line only' { - run printf 'a\nb\nc' - run refute_line --index 1 'd' - assert_test_pass -} - -@test 'refute_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { - run refute_line --index 1a - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_line -- -`--index' requires an integer argument: `1a' --- -ERR_MSG -} - - -# -# Literal matching -# - -# Correctness -@test "refute_line() --index <idx> <unexpected>: returns 0 if <unexpected> does not equal \`\${lines[<idx>]}'" { - run printf 'a\nb\nc' - run refute_line --index 1 'd' - assert_test_pass -} - -@test "refute_line() --index <idx> <unexpected>: returns 1 and displays details if <unexpected> equals \`\${lines[<idx>]}'" { - run printf 'a\nb\nc' - run refute_line --index 1 'b' - - assert_test_fail <<'ERR_MSG' - --- line should differ -- -index : 1 -line : b --- -ERR_MSG -} - -# Options -@test 'refute_line() --index <idx> <unexpected>: performs literal matching by default' { - run printf 'a\nb\nc' - run refute_line --index 1 '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_line() --index <idx> -p <partial>: enables partial matching' { - run printf 'a\nb\nc' - run refute_line --index 1 -p 'd' - assert_test_pass -} - -@test 'refute_line() --index <idx> --partial <partial>: enables partial matching' { - run printf 'a\nb\nc' - run refute_line --index 1 --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_line() --index <idx> --partial <partial>: returns 0 if <partial> is not a substring in \`\${lines[<idx>]}'" { - run printf 'a\nabc\nc' - run refute_line --index 1 --partial 'd' - assert_test_pass -} - -@test "refute_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\${lines[<idx>]}'" { - run printf 'a\nabc\nc' - run refute_line --index 1 --partial 'b' - - assert_test_fail <<'ERR_MSG' - --- line should not contain substring -- -index : 1 -substring : b -line : abc --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_line() --index <idx> -e <regexp>: enables regular expression matching' { - run printf 'a\nb\nc' - run refute_line --index 1 -e '^.b' - assert_test_pass -} - -@test 'refute_line() --index <idx> --regexp <regexp>: enables regular expression matching' { - run printf 'a\nb\nc' - run refute_line --index 1 --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "refute_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> does not match \`\${lines[<idx>]}'" { - run printf 'a\nabc\nc' - run refute_line --index 1 --regexp '.*d.*' - assert_test_pass -} - -@test "refute_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\${lines[<idx>]}'" { - run printf 'a\nabc\nc' - run refute_line --index 1 --regexp '.*b.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match line -- -index : 1 -regexp : .*b.* -line : abc --- -ERR_MSG -} - - -############################################################################### -# Common -############################################################################### - -@test "refute_line(): \`--partial' and \`--regexp' are mutually exclusive" { - run refute_line --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_line -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test 'refute_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run refute_line --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_line -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - -@test "refute_line(): \`--' stops parsing options" { - run printf 'a\n--\nc' - run refute_line -- '-p' - assert_test_pass -} - -@test "__refute_stream_line(): call to __refute_stream_line shows error" { - run __refute_stream_line - assert_test_fail <<'ERR_MSG' - --- ERROR: __refute_stream_line -- -Unexpected call to `__refute_stream_line` -Did you mean to call `refute_line` or `refute_stderr_line`? --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/refute_output.bats b/tests/bash/test_helper/bats-assert/test/refute_output.bats deleted file mode 100644 index 9d8711d..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute_output.bats +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - - -# -# Literal matching -# - -# Correctness -@test "refute_output() <unexpected>: returns 0 if <unexpected> does not equal \`\$output'" { - run echo 'b' - run refute_output 'a' - assert_test_pass -} - -@test "refute_output() <unexpected>: returns 1 and displays details if <unexpected> equals \`\$output'" { - run echo 'a' - run refute_output 'a' - - assert_test_fail <<'ERR_MSG' - --- output equals, but it was expected to differ -- -output : a --- -ERR_MSG -} - -@test 'refute_output(): succeeds if output is empty' { - run echo '' - run refute_output - - assert_test_pass -} - -@test 'refute_output(): fails if output is non-empty' { - run echo 'a' - run refute_output - - assert_test_fail <<'ERR_MSG' - --- output non-empty, but expected no output -- -output : a --- -ERR_MSG -} - -@test 'refute_output() - : reads <unexpected> from STDIN' { - run echo '-' - run refute_output - <<INPUT -b -INPUT - - assert_test_pass -} - -@test 'refute_output() --stdin : reads <unexpected> from STDIN' { - run echo '--stdin' - run refute_output --stdin <<INPUT -b -INPUT - - assert_test_pass -} - -# Output formatting -@test 'refute_output() <unexpected>: displays details in multi-line format if necessary' { - run printf 'a 0\na 1' - run refute_output $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- output equals, but it was expected to differ -- -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -# Options -@test 'refute_output() <unexpected>: performs literal matching by default' { - run echo 'a' - run refute_output '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_output() -p <partial>: enables partial matching' { - run echo 'abc' - run refute_output -p 'd' - assert_test_pass -} - -@test 'refute_output() --partial <partial>: enables partial matching' { - run echo 'abc' - run refute_output --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_output() --partial <partial>: returns 0 if <partial> is not a substring in \`\$output'" { - run printf 'a\nb\nc' - run refute_output --partial 'd' - assert_test_pass -} - -@test "refute_output() --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\$output'" { - run echo 'a' - run refute_output --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- output should not contain substring -- -substring : a -output : a --- -ERR_MSG -} - -# Output formatting -@test 'refute_output() --partial <partial>: displays details in multi-line format if necessary' { - run printf 'a 0\na 1' - run refute_output --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- output should not contain substring -- -substring (1 lines): - a -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_output() -e <regexp>: enables regular expression matching' { - run echo 'abc' - run refute_output -e '^d' - assert_test_pass -} - -@test 'refute_output() --regexp <regexp>: enables regular expression matching' { - run echo 'abc' - run refute_output --regexp '^d' - assert_test_pass -} - -# Correctness -@test "refute_output() --regexp <regexp>: returns 0 if <regexp> does not match \`\$output'" { - run printf 'a\nb\nc' - run refute_output --regexp '.*d.*' - assert_test_pass -} - -@test "refute_output() --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\$output'" { - run echo 'a' - run refute_output --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match output -- -regexp : .*a.* -output : a --- -ERR_MSG -} - -# Output formatting -@test 'refute_output() --regexp <regexp>: displays details in multi-line format if necessary' { - run printf 'a 0\na 1' - run refute_output --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match output -- -regexp (1 lines): - .*a.* -output (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -# Error handling -@test 'refute_output() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run refute_output --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_output -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - - -# -# Common -# - -@test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" { - run refute_output --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_output -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test "refute_output(): \`--' stops parsing options" { - run echo '--' - run refute_output -- '-p' - assert_test_pass -} diff --git a/tests/bash/test_helper/bats-assert/test/refute_regex.bats b/tests/bash/test_helper/bats-assert/test/refute_regex.bats deleted file mode 100644 index c1fd1b2..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute_regex.bats +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -# -# Literal matching -# - -# Correctness -@test "refute_regex() <value> <pattern>: fails if a <value> substring matches extended regular expression <pattern>" { - run refute_regex 'abc' '^[a-z]b' - assert_test_fail <<'ERR_MSG' - --- value matches regular expression -- -value : abc -pattern : ^[a-z]b -match : ab -case : sensitive --- -ERR_MSG -} - -@test "refute_regex() <value> <pattern>: succeeds if no <value> substring matches extended regular expression <pattern>" { - run refute_regex 'bcd' '^[a-z]b[c-z]+' - assert_test_pass -} - -@test "refute_regex() <value> <pattern>: provides results in BASH_REMATCH on failure" { - unset -v BASH_REMATCH - - refute_regex 'abcd' 'b.d' \ - || { - declare -p BASH_REMATCH && \ - [ "${BASH_REMATCH[0]}" = 'bcd' ] - } -} - -@test "refute_regex() <value> <pattern>: matches case-insensitively when 'nocasematch' is set" { - shopt -s nocasematch - - run refute_regex 'aBc' 'ABC' - assert_test_fail <<'ERR_MSG' - --- value matches regular expression -- -value : aBc -pattern : ABC -match : aBc -case : insensitive --- -ERR_MSG -} - -@test "refute_regex() <value> <pattern>: outputs multi-line <value> nicely when it fails" { - run refute_regex $'abc\n123' '^[a-z]b[c-z]+' - assert_test_fail <<'ERR_MSG' - --- value matches regular expression -- -value (2 lines): - abc - 123 -pattern (1 lines): - ^[a-z]b[c-z]+ -match (1 lines): - abc -case (1 lines): - sensitive --- -ERR_MSG - - shopt -s nocasematch - run refute_regex $'aBc\n123' '^[a-z]b[c-z]+' - assert_test_fail <<'ERR_MSG' - --- value matches regular expression -- -value (2 lines): - aBc - 123 -pattern (1 lines): - ^[a-z]b[c-z]+ -match (1 lines): - aBc -case (1 lines): - insensitive --- -ERR_MSG -} - -# Error handling -@test "refute_regex() <value> <pattern>: returns 1 and displays an error message if <pattern> is not a valid extended regular expression" { - run refute_regex value '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_regex -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} diff --git a/tests/bash/test_helper/bats-assert/test/refute_stderr.bats b/tests/bash/test_helper/bats-assert/test/refute_stderr.bats deleted file mode 100644 index d1ec134..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute_stderr.bats +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -setup_file() { - bats_require_minimum_version 1.5.0 -} - -echo_err() { - echo "$@" >&2 -} - -printf_err() { - # shellcheck disable=2059 - printf "$@" >&2 -} - -# -# Literal matching -# - -# Correctness -@test "refute_stderr() <unexpected>: returns 0 if <unexpected> does not equal \`\$stderr'" { - run --separate-stderr echo_err 'b' - run refute_stderr 'a' - assert_test_pass -} - -@test "refute_stderr() <unexpected>: returns 1 and displays details if <unexpected> equals \`\$stderr'" { - run --separate-stderr echo_err 'a' - run refute_stderr 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr equals, but it was expected to differ -- -stderr : a --- -ERR_MSG -} - -@test 'refute_stderr(): succeeds if stderr is empty' { - run --separate-stderr echo_err '' - run refute_stderr - - assert_test_pass -} - -@test 'refute_stderr(): fails if stderr is non-empty' { - run --separate-stderr echo_err 'a' - run refute_stderr - - assert_test_fail <<'ERR_MSG' - --- stderr non-empty, but expected no stderr -- -stderr : a --- -ERR_MSG -} - -@test 'refute_stderr() - : reads <unexpected> from STDIN' { - run --separate-stderr echo_err '-' - run refute_stderr - <<INPUT -b -INPUT - - assert_test_pass -} - -@test 'refute_stderr() --stdin : reads <unexpected> from STDIN' { - run --separate-stderr echo_err '--stdin' - run refute_stderr --stdin <<INPUT -b -INPUT - - assert_test_pass -} - -# Output formatting -@test 'refute_stderr() <unexpected>: displays details in multi-line format if necessary' { - run --separate-stderr printf_err 'a 0\na 1' - run refute_stderr $'a 0\na 1' - - assert_test_fail <<'ERR_MSG' - --- stderr equals, but it was expected to differ -- -stderr (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -# Options -@test 'refute_stderr() <unexpected>: performs literal matching by default' { - run --separate-stderr echo_err 'a' - run refute_stderr '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_stderr() -p <partial>: enables partial matching' { - run --separate-stderr echo_err 'abc' - run refute_stderr -p 'd' - assert_test_pass -} - -@test 'refute_stderr() --partial <partial>: enables partial matching' { - run --separate-stderr echo_err 'abc' - run refute_stderr --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_stderr() --partial <partial>: returns 0 if <partial> is not a substring in \`\$stderr'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr --partial 'd' - assert_test_pass -} - -@test "refute_stderr() --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\$stderr'" { - run --separate-stderr echo_err 'a' - run refute_stderr --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr should not contain substring -- -substring : a -stderr : a --- -ERR_MSG -} - -# Output formatting -@test 'refute_stderr() --partial <partial>: displays details in multi-line format if necessary' { - run --separate-stderr printf_err 'a 0\na 1' - run refute_stderr --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- stderr should not contain substring -- -substring (1 lines): - a -stderr (2 lines): - a 0 - a 1 --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_stderr() -e <regexp>: enables regular expression matching' { - run --separate-stderr echo_err 'abc^d' - run refute_stderr -e '^d' - assert_test_pass -} - -@test 'refute_stderr() --regexp <regexp>: enables regular expression matching' { - run --separate-stderr echo_err 'abc' - run refute_stderr --regexp '^d' - assert_test_pass -} - -# Correctness -@test "refute_stderr() --regexp <regexp>: returns 0 if <regexp> does not match \`\$stderr'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr --regexp '.*d.*' - assert_test_pass -} - -@test "refute_stderr() --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\$stderr'" { - run --separate-stderr echo_err 'a' - run refute_stderr --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match stderr -- -regexp : .*a.* -stderr : a --- -ERR_MSG -} - -# Output formatting -@test 'refute_stderr() --regexp <regexp>: displays details in multi-line format if necessary' { - run --separate-stderr printf_err 'a 0\na 1' - run refute_stderr --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match stderr -- -regexp (1 lines): - .*a.* -stderr (2 lines): - a 0 - a 1 --- -ERR_MSG -} - -# Error handling -@test 'refute_stderr() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run refute_stderr --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_stderr -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - - -# -# Common -# - -@test "refute_stderr(): \`--partial' and \`--regexp' are mutually exclusive" { - run refute_stderr --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_stderr -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test "refute_stderr(): \`--' stops parsing options" { - run echo_err '--' - run refute_stderr -- '-p' - assert_test_pass -} diff --git a/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats b/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats deleted file mode 100644 index ecb210b..0000000 --- a/tests/bash/test_helper/bats-assert/test/refute_stderr_line.bats +++ /dev/null @@ -1,356 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -setup_file() { - bats_require_minimum_version 1.5.0 -} - -echo_err() { - echo "$@" >&2 -} - -printf_err() { - # shellcheck disable=2059 - printf "$@" >&2 -} - -############################################################################### -# Containing a line -############################################################################### - -# -# Literal matching -# - -# Correctness -@test "refute_stderr_line() <unexpected>: returns 0 if <unexpected> is not a line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line 'd' - assert_test_pass -} - -@test "refute_stderr_line() <unexpected>: returns 1 and displays details if <unexpected> is not a line in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'a' - run refute_stderr_line 'a' - - assert_test_fail <<'ERR_MSG' - --- line should not be in stderr -- -line : a -index : 0 -stderr : a --- -ERR_MSG -} - -# Output formatting -@test "refute_stderr_line() <unexpected>: displays \`\$stderr' in multi-line format if it is longer than one line" { - run --separate-stderr printf_err 'a 0\na 1\na 2' - run refute_stderr_line 'a 1' - - assert_test_fail <<'ERR_MSG' - --- line should not be in stderr -- -line : a 1 -index : 1 -stderr (3 lines): - a 0 -> a 1 - a 2 --- -ERR_MSG -} - -# Options -@test 'refute_stderr_line() <unexpected>: performs literal matching by default' { - run --separate-stderr echo_err 'a' - run refute_stderr_line '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_stderr_line() -p <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line -p 'd' - assert_test_pass -} - -@test 'refute_stderr_line() --partial <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_stderr_line() --partial <partial>: returns 0 if <partial> is not a substring in any line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --partial 'd' - assert_test_pass -} - -@test "refute_stderr_line() --partial <partial>: returns 1 and displays details if <partial> is a substring in any line in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'a' - run refute_stderr_line --partial 'a' - - assert_test_fail <<'ERR_MSG' - --- no line should contain substring -- -substring : a -index : 0 -stderr : a --- -ERR_MSG -} - -# Output formatting -@test "refute_stderr_line() --partial <partial>: displays \`\$stderr' in multi-line format if it is longer than one line" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --partial 'b' - - assert_test_fail <<'ERR_MSG' - --- no line should contain substring -- -substring : b -index : 1 -stderr (3 lines): - a -> abc - c --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_stderr_line() -e <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line -e '^.d' - assert_test_pass -} - -@test 'refute_stderr_line() --regexp <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --regexp '^.d' - assert_test_pass -} - -# Correctness -@test "refute_stderr_line() --regexp <regexp>: returns 0 if <regexp> does not match any line in \`\${stderr_lines[@]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --regexp '.*d.*' - assert_test_pass -} - -@test "refute_stderr_line() --regexp <regexp>: returns 1 and displays details if <regexp> matches any lines in \`\${stderr_lines[@]}'" { - run --separate-stderr echo_err 'a' - run refute_stderr_line --regexp '.*a.*' - - assert_test_fail <<'ERR_MSG' - --- no line should match the regular expression -- -regexp : .*a.* -index : 0 -stderr : a --- -ERR_MSG -} - -# Output formatting -@test "refute_stderr_line() --regexp <regexp>: displays \`\$stderr' in multi-line format if longer than one line" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --regexp '.*b.*' - - assert_test_fail <<'ERR_MSG' - --- no line should match the regular expression -- -regexp : .*b.* -index : 1 -stderr (3 lines): - a -> abc - c --- -ERR_MSG -} - - -############################################################################### -# Matching single line: `-n' and `--index' -############################################################################### - -# Options -@test 'refute_stderr_line() -n <idx> <expected>: matches against the <idx>-th line only' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line -n 1 'd' - assert_test_pass -} - -@test 'refute_stderr_line() --index <idx> <expected>: matches against the <idx>-th line only' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 'd' - assert_test_pass -} - -@test 'refute_stderr_line() --index <idx>: returns 1 and displays an error message if <idx> is not an integer' { - run refute_stderr_line --index 1a - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_stderr_line -- -`--index' requires an integer argument: `1a' --- -ERR_MSG -} - - -# -# Literal matching -# - -# Correctness -@test "refute_stderr_line() --index <idx> <unexpected>: returns 0 if <unexpected> does not equal \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 'd' - assert_test_pass -} - -@test "refute_stderr_line() --index <idx> <unexpected>: returns 1 and displays details if <unexpected> equals \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 'b' - - assert_test_fail <<'ERR_MSG' - --- line should differ -- -index : 1 -line : b --- -ERR_MSG -} - -# Options -@test 'refute_stderr_line() --index <idx> <unexpected>: performs literal matching by default' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 '*' - assert_test_pass -} - - -# -# Partial matching: `-p' and `--partial' -# - -# Options -@test 'refute_stderr_line() --index <idx> -p <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 -p 'd' - assert_test_pass -} - -@test 'refute_stderr_line() --index <idx> --partial <partial>: enables partial matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 --partial 'd' - assert_test_pass -} - -# Correctness -@test "refute_stderr_line() --index <idx> --partial <partial>: returns 0 if <partial> is not a substring in \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --index 1 --partial 'd' - assert_test_pass -} - -@test "refute_stderr_line() --index <idx> --partial <partial>: returns 1 and displays details if <partial> is a substring in \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --index 1 --partial 'b' - - assert_test_fail <<'ERR_MSG' - --- line should not contain substring -- -index : 1 -substring : b -line : abc --- -ERR_MSG -} - - -# -# Regular expression matching: `-e' and `--regexp' -# - -# Options -@test 'refute_stderr_line() --index <idx> -e <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 -e '[^.b]' - assert_test_pass -} - -@test 'refute_stderr_line() --index <idx> --regexp <regexp>: enables regular expression matching' { - run --separate-stderr printf_err 'a\nb\nc' - run refute_stderr_line --index 1 --regexp '^.b' - assert_test_pass -} - -# Correctness -@test "refute_stderr_line() --index <idx> --regexp <regexp>: returns 0 if <regexp> does not match \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --index 1 --regexp '.*d.*' - assert_test_pass -} - -@test "refute_stderr_line() --index <idx> --regexp <regexp>: returns 1 and displays details if <regexp> matches \`\${stderr_lines[<idx>]}'" { - run --separate-stderr printf_err 'a\nabc\nc' - run refute_stderr_line --index 1 --regexp '.*b.*' - - assert_test_fail <<'ERR_MSG' - --- regular expression should not match line -- -index : 1 -regexp : .*b.* -line : abc --- -ERR_MSG -} - - -############################################################################### -# Common -############################################################################### - -@test "refute_stderr_line(): \`--partial' and \`--regexp' are mutually exclusive" { - run refute_stderr_line --partial --regexp - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_stderr_line -- -`--partial' and `--regexp' are mutually exclusive --- -ERR_MSG -} - -@test 'refute_stderr_line() --regexp <regexp>: returns 1 and displays an error message if <regexp> is not a valid extended regular expression' { - run refute_stderr_line --regexp '[.*' - - assert_test_fail <<'ERR_MSG' - --- ERROR: refute_stderr_line -- -Invalid extended regular expression: `[.*' --- -ERR_MSG -} - -@test "refute_stderr_line(): \`--' stops parsing options" { - run --separate-stderr printf_err 'a\n--\nc' - run refute_stderr_line -- '-p' - assert_test_pass -} diff --git a/tests/bash/test_helper/bats-assert/test/test_helper.bash b/tests/bash/test_helper/bats-assert/test/test_helper.bash deleted file mode 100644 index af1ee0a..0000000 --- a/tests/bash/test_helper/bats-assert/test/test_helper.bash +++ /dev/null @@ -1,30 +0,0 @@ -# Load dependencies. -BATS_LIB_PATH=$PWD/node_modules:${BATS_LIB_PATH-} -bats_load_library 'bats-support' - -# Load library. -load '../load' - -# validate that bats-assert is safe to use under -u -set -u - -: "${status:=}" -: "${lines:=}" -: "${output:=}" -: "${stderr:=}" -: "${stderr_lines:=}" - -assert_test_pass() { - test "$status" -eq 0 - test "${#lines[@]}" -eq 0 -} - -assert_test_fail() { - local err_msg="${1-$(cat -)}" - local num_lines - num_lines="$(printf '%s' "$err_msg" | wc -l)" - - test "$status" -eq 1 - test "${#lines[@]}" -eq "$num_lines" - test "$output" == "$err_msg" -} diff --git a/tests/bash/test_helper/bats-support/.github/dependabot.yaml b/tests/bash/test_helper/bats-support/.github/dependabot.yaml deleted file mode 100644 index c519084..0000000 --- a/tests/bash/test_helper/bats-support/.github/dependabot.yaml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - target-branch: master diff --git a/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml b/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml deleted file mode 100644 index b93fefe..0000000 --- a/tests/bash/test_helper/bats-support/.github/workflows/scorecard.yaml +++ /dev/null @@ -1,72 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. They are provided -# by a third-party and are governed by separate terms of service, privacy -# policy, and support documentation. - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '44 10 * * 6' - push: - branches: [ "master" ] - -# Declare default permissions as read only. -permissions: read-all - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - # Uncomment the permissions below if installing in a private repository. - # contents: read - # actions: read - - steps: - - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 - with: - results_file: results.sarif - results_format: sarif - # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: - # - you want to enable the Branch-Protection check on a *public* repository, or - # - you are installing Scorecard on a *private* repository - # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. - # repo_token: ${{ secrets.SCORECARD_TOKEN }} - - # Public repositories: - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories: - # - `publish_results` will always be set to `false`, regardless - # of the value entered here. - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard. - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 - with: - sarif_file: results.sarif diff --git a/tests/bash/test_helper/bats-support/.github/workflows/tests.yml b/tests/bash/test_helper/bats-support/.github/workflows/tests.yml deleted file mode 100644 index 097283b..0000000 --- a/tests/bash/test_helper/bats-support/.github/workflows/tests.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Tests - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 - - uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2 - - uses: actions/cache@8492260343ad570701412c2f464a5877dc76bace # v2 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - run: npm install - - run: npm test diff --git a/tests/bash/test_helper/bats-support/.gitignore b/tests/bash/test_helper/bats-support/.gitignore deleted file mode 100644 index 05f900c..0000000 --- a/tests/bash/test_helper/bats-support/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/node_modules -/package-lock.json -/yarn.lock -/bats-support-*.tgz diff --git a/tests/bash/test_helper/bats-support/CHANGELOG.md b/tests/bash/test_helper/bats-support/CHANGELOG.md deleted file mode 100644 index 324d247..0000000 --- a/tests/bash/test_helper/bats-support/CHANGELOG.md +++ /dev/null @@ -1,46 +0,0 @@ -# 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-11-29 - -### Added - -- Restricting invocation to specific locations with - `batslib_is_caller()` - - -## [0.2.0] - 2016-03-22 - -### Added - -- `npm` support -- Reporting arbitrary failures with `fail()` (moved from `bats-assert`) - -### Changed - -- Library renamed to `bats-support` - - -## 0.1.0 - 2016-02-16 - -### Added - -- Two-column key-value formatting with `batslib_print_kv_single()` -- Multi-line key-value formatting with `batslib_print_kv_multi()` -- Mixed formatting with `batslib_print_kv_single_or_multi()` -- Header and footer decoration with `batslib_decorate()` -- Prefixing lines with `batslib_prefix()` -- Marking lines with `batslib_mark()` -- Common output function `batslib_err()` -- Line counting with `batslib_count_lines()` -- Checking whether a text is one line long with - `batslib_is_single_line()` -- Determining key width for two-column and mixed formatting with - `batslib_get_max_single_line_key_width()` - - -[0.3.0]: https://github.com/ztombol/bats-support/compare/v0.2.0...v0.3.0 -[0.2.0]: https://github.com/ztombol/bats-support/compare/v0.1.0...v0.2.0 diff --git a/tests/bash/test_helper/bats-support/LICENSE b/tests/bash/test_helper/bats-support/LICENSE deleted file mode 100644 index 670154e..0000000 --- a/tests/bash/test_helper/bats-support/LICENSE +++ /dev/null @@ -1,116 +0,0 @@ -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/> diff --git a/tests/bash/test_helper/bats-support/README.md b/tests/bash/test_helper/bats-support/README.md deleted file mode 100644 index 0554349..0000000 --- a/tests/bash/test_helper/bats-support/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# bats-support - -[![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/bats-core/bats-support/master/LICENSE) -[![GitHub release](https://img.shields.io/github/release/bats-core/bats-support.svg)](https://github.com/bats-core/bats-support/releases/latest) -[![Build Status](https://github.com/bats-core/bats-support/workflows/Tests/badge.svg)](https://github.com/bats-core/bats-support/actions?query=workflow%3ATests) - - -`bats-support` is a supporting library providing common functions to -test helper libraries written for [Bats][bats]. - -Features: -- [error reporting](#error-reporting) -- [output formatting](#output-formatting) -- [language tools](#language-and-execution) - -See the [shared documentation][bats-docs] to learn how to install and -load this library. - -If you want to use this library in your own helpers or just want to -learn about its internals see the developer documentation in the [source -files](src). - - -## Error reporting - -### `fail` - -Display an error message and fail. This function provides a convenient -way to report failure in arbitrary situations. You can use it to -implement your own helpers when the ones available do not meet your -needs. Other functions use it internally as well. - -```bash -@test 'fail()' { - fail 'this test always fails' -} -``` - -The message can also be specified on the standard input. - -```bash -@test 'fail() with pipe' { - echo 'this test always fails' | fail -} -``` - -This function always fails and simply outputs the given message. - -``` -this test always fails -``` - - -## Output formatting - -Many test helpers need to produce human readable output. This library -provides a simple way to format simple messages and key value pairs, and -display them on the standard error. - - -### Simple message - -Simple messages without structure, e.g. one-line error messages, are -simply wrapped in a header and a footer to help them stand out. - -``` --- ERROR: assert_output -- -`--partial' and `--regexp' are mutually exclusive --- -``` - - -### Key-Value pairs - -Some helpers, e.g. [assertions][bats-assert], structure output as -key-value pairs. This library provides two ways to format them. - -When the value is one line long, a pair can be displayed in a columnar -fashion called ***two-column*** format. - -``` --- output differs -- -expected : want -actual : have --- -``` - -When the value is longer than one line, the key and value must be -displayed on separate lines. First, the key is displayed along with the -number of lines in the value. Then, the value, indented by two spaces -for added readability, starting on the next line. This is called -***multi-line*** format. - -``` --- command failed -- -status : 1 -output (2 lines): - Error! Something went terribly wrong! - Our engineers are panicing... \`>`;/ --- -``` - -Sometimes, for clarity, it is a good idea to display related values also -in this format, even if they are just one line long. - -``` --- output differs -- -expected (1 lines): - want -actual (3 lines): - have 1 - have 2 - have 3 --- -``` - -## Language and Execution - -### Restricting invocation to specific locations - -Sometimes a helper may work properly only when called from a certain -location. Because it depends on variables to be set or some other side -effect. - -A good example is cleaning up temporary files only if the test has -succeeded. The outcome of a test is only available in `teardown`. Thus, -to avoid programming mistakes, it makes sense to restrict such a -clean-up helper to that function. - -`batslib_is_caller` checks the call stack and returns `0` if the caller -was invoked from a given function, and `1` otherwise. This function -becomes really useful with the `--indirect` option, which allows calls -through intermediate functions, e.g. the calling function may be called -from a function that was called from the given function. - -Staying with the example above, the following code snippet implements a -helper that is restricted to `teardown` or any function called -indirectly from it. - -```shell -clean_up() { - # Check caller. - if batslib_is_caller --indirect 'teardown'; then - echo "Must be called from \`teardown'" \ - | batslib_decorate 'ERROR: clean_up' \ - | fail - return $? - fi - - # Body goes here... -} -``` - -In some cases a helper may be called from multiple locations. For -example, a logging function that uses the test name, description or -number, information only available in `setup`, `@test` or `teardown`, to -distinguish entries. The following snippet implements this restriction. - -```shell -log_test() { - # Check caller. - if ! ( batslib_is_caller --indirect 'setup' \ - || batslib_is_caller --indirect "$BATS_TEST_NAME" \ - || batslib_is_caller --indirect 'teardown' ) - then - echo "Must be called from \`setup', \`@test' or \`teardown'" \ - | batslib_decorate 'ERROR: log_test' \ - | fail - return $? - fi - - # Body goes here... -} -``` - - -<!-- REFERENCES --> - -[bats]: https://github.com/bats-core/bats-core -[bats-docs]: https://github.com/bats-core/bats-docs -[bats-assert]: https://github.com/bats-core/bats-assert diff --git a/tests/bash/test_helper/bats-support/load.bash b/tests/bash/test_helper/bats-support/load.bash deleted file mode 100644 index 3f52722..0000000 --- a/tests/bash/test_helper/bats-support/load.bash +++ /dev/null @@ -1,11 +0,0 @@ -# Preserve path at the time this file was sourced -# This prevents using of user-defined mocks/stubs that modify the PATH - -# BATS_SAVED_PATH was introduced in bats-core v1.10.0 -# if it is already set, we can use its more robust value -# else we try to recreate it here -BATS_SAVED_PATH="${BATS_SAVED_PATH-$PATH}" - -source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash" -source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash" diff --git a/tests/bash/test_helper/bats-support/package.json b/tests/bash/test_helper/bats-support/package.json deleted file mode 100644 index 5015687..0000000 --- a/tests/bash/test_helper/bats-support/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "bats-support", - "version": "0.3.0", - "description": "Supporting library for Bats test helpers", - "homepage": "https://github.com/jasonkarns/bats-support", - "license": "CC0-1.0", - "author": "Zoltán Tömböl (https://github.com/ztombol)", - "contributors": [ - "Jason Karns <jason.karns@gmail.com> (http://jason.karns.name)" - ], - "repository": "github:jasonkarns/bats-support", - "bugs": "https://github.com/jasonkarns/bats-support/issues", - "directories": { - "lib": "src", - "test": "test" - }, - "files": [ - "load.bash", - "src" - ], - "scripts": { - "test": "bats ${CI+-t} test" - }, - "devDependencies": { - "bats": "^1" - }, - "peerDependencies": { - "bats": "0.4 || ^1" - } -} diff --git a/tests/bash/test_helper/bats-support/src/error.bash b/tests/bash/test_helper/bats-support/src/error.bash deleted file mode 100644 index e5d9791..0000000 --- a/tests/bash/test_helper/bats-support/src/error.bash +++ /dev/null @@ -1,41 +0,0 @@ -# -# bats-support - Supporting library for Bats test helpers -# -# 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/>. -# - -# -# error.bash -# ---------- -# -# Functions implementing error reporting. Used by public helper -# functions or test suits directly. -# - -# Fail and display a message. When no parameters are specified, the -# message is read from the standard input. Other functions use this to -# report failure. -# -# Globals: -# none -# Arguments: -# $@ - [=STDIN] message -# Returns: -# 1 - always -# Inputs: -# STDIN - [=$@] message -# Outputs: -# STDERR - message -fail() { - (( $# == 0 )) && batslib_err || batslib_err "$@" - return 1 -} diff --git a/tests/bash/test_helper/bats-support/src/lang.bash b/tests/bash/test_helper/bats-support/src/lang.bash deleted file mode 100644 index c57e299..0000000 --- a/tests/bash/test_helper/bats-support/src/lang.bash +++ /dev/null @@ -1,73 +0,0 @@ -# -# bats-util - Various auxiliary functions 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/>. -# - -# -# lang.bash -# --------- -# -# Bash language and execution related functions. Used by public helper -# functions. -# - -# Check whether the calling function was called from a given function. -# -# By default, direct invocation is checked. The function succeeds if the -# calling function was called directly from the given function. In other -# words, if the given function is the next element on the call stack. -# -# When `--indirect' is specified, indirect invocation is checked. The -# function succeeds if the calling function was called from the given -# function with any number of intermediate calls. In other words, if the -# given function can be found somewhere on the call stack. -# -# Direct invocation is a form of indirect invocation with zero -# intermediate calls. -# -# Globals: -# FUNCNAME -# Options: -# -i, --indirect - check indirect invocation -# Arguments: -# $1 - calling function's name -# Returns: -# 0 - current function was called from the given function -# 1 - otherwise -batslib_is_caller() { - local -i is_mode_direct=1 - - # Handle options. - while (( $# > 0 )); do - case "$1" in - -i|--indirect) is_mode_direct=0; shift ;; - --) shift; break ;; - *) break ;; - esac - done - - # Arguments. - local -r func="$1" - - # Check call stack. - if (( is_mode_direct )); then - [[ $func == "${FUNCNAME[2]}" ]] && return 0 - else - local -i depth - for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do - [[ $func == "${FUNCNAME[$depth]}" ]] && return 0 - done - fi - - return 1 -} diff --git a/tests/bash/test_helper/bats-support/src/output.bash b/tests/bash/test_helper/bats-support/src/output.bash deleted file mode 100644 index 10ca8ae..0000000 --- a/tests/bash/test_helper/bats-support/src/output.bash +++ /dev/null @@ -1,279 +0,0 @@ -# -# bats-support - Supporting library for Bats test helpers -# -# 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/>. -# - -# -# output.bash -# ----------- -# -# Private functions implementing output formatting. Used by public -# helper functions. -# - -# Print a message to the standard error. When no parameters are -# specified, the message is read from the standard input. -# -# Globals: -# none -# Arguments: -# $@ - [=STDIN] message -# Returns: -# none -# Inputs: -# STDIN - [=$@] message -# Outputs: -# STDERR - message -batslib_err() { - { if (( $# > 0 )); then - echo "$@" - else - PATH="$BATS_SAVED_PATH" command cat - - fi - } >&2 -} - -# Count the number of lines in the given string. -# -# TODO(ztombol): Fix tests and remove this note after #93 is resolved! -# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not -# give the same result as `${#lines[@]}' when the output contains -# empty lines. -# See PR #93 (https://github.com/sstephenson/bats/pull/93). -# -# Globals: -# none -# Arguments: -# $1 - string -# Returns: -# none -# Outputs: -# STDOUT - number of lines -batslib_count_lines() { - local -i n_lines=0 - local line - while IFS='' read -r line || [[ -n $line ]]; do - (( ++n_lines )) - done < <(printf '%s' "$1") - echo "$n_lines" -} - -# Determine whether all strings are single-line. -# -# Globals: -# none -# Arguments: -# $@ - strings -# Returns: -# 0 - all strings are single-line -# 1 - otherwise -batslib_is_single_line() { - for string in "$@"; do - (( $(batslib_count_lines "$string") > 1 )) && return 1 - done - return 0 -} - -# Determine the length of the longest key that has a single-line value. -# -# This function is useful in determining the correct width of the key -# column in two-column format when some keys may have multi-line values -# and thus should be excluded. -# -# Globals: -# none -# Arguments: -# $odd - key -# $even - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - length of longest key -batslib_get_max_single_line_key_width() { - local -i max_len=-1 - while (( $# != 0 )); do - local -i key_len="${#1}" - batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" - shift 2 - done - echo "$max_len" -} - -# Print key-value pairs in two-column format. -# -# Keys are displayed in the first column, and their corresponding values -# in the second. To evenly line up values, the key column is fixed-width -# and its width is specified with the first parameter (possibly computed -# using `batslib_get_max_single_line_key_width'). -# -# Globals: -# none -# Arguments: -# $1 - width of key column -# $even - key -# $odd - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_single() { - local -ir col_width="$1"; shift - while (( $# != 0 )); do - printf '%-*s : %s\n' "$col_width" "$1" "$2" - shift 2 - done -} - -# Print key-value pairs in multi-line format. -# -# The key is displayed first with the number of lines of its -# corresponding value in parenthesis. Next, starting on the next line, -# the value is displayed. For better readability, it is recommended to -# indent values using `batslib_prefix'. -# -# Globals: -# none -# Arguments: -# $odd - key -# $even - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_multi() { - while (( $# != 0 )); do - printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" - printf '%s\n' "$2" - shift 2 - done -} - -# Print all key-value pairs in either two-column or multi-line format -# depending on whether all values are single-line. -# -# If all values are single-line, print all pairs in two-column format -# with the specified key column width (identical to using -# `batslib_print_kv_single'). -# -# Otherwise, print all pairs in multi-line format after indenting values -# with two spaces for readability (identical to using `batslib_prefix' -# and `batslib_print_kv_multi') -# -# Globals: -# none -# Arguments: -# $1 - width of key column (for two-column format) -# $even - key -# $odd - value of the previous key -# Returns: -# none -# Outputs: -# STDOUT - formatted key-value pairs -batslib_print_kv_single_or_multi() { - local -ir width="$1"; shift - local -a pairs=( "$@" ) - - local -a values=() - local -i i - for (( i=1; i < ${#pairs[@]}; i+=2 )); do - values+=( "${pairs[$i]}" ) - done - - if batslib_is_single_line "${values[@]}"; then - batslib_print_kv_single "$width" "${pairs[@]}" - else - local -i i - for (( i=1; i < ${#pairs[@]}; i+=2 )); do - pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" - done - batslib_print_kv_multi "${pairs[@]}" - fi -} - -# Prefix each line read from the standard input with the given string. -# -# Globals: -# none -# Arguments: -# $1 - [= ] prefix string -# Returns: -# none -# Inputs: -# STDIN - lines -# Outputs: -# STDOUT - prefixed lines -batslib_prefix() { - local -r prefix="${1:- }" - local line - while IFS='' read -r line || [[ -n $line ]]; do - printf '%s%s\n' "$prefix" "$line" - done -} - -# Mark select lines of the text read from the standard input by -# overwriting their beginning with the given string. -# -# Usually the input is indented by a few spaces using `batslib_prefix' -# first. -# -# Globals: -# none -# Arguments: -# $1 - marking string -# $@ - indices (zero-based) of lines to mark -# Returns: -# none -# Inputs: -# STDIN - lines -# Outputs: -# STDOUT - lines after marking -batslib_mark() { - local -r symbol="$1"; shift - # Sort line numbers. - set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) - - local line - local -i idx=0 - while IFS='' read -r line || [[ -n $line ]]; do - if (( ${1:--1} == idx )); then - printf '%s\n' "${symbol}${line:${#symbol}}" - shift - else - printf '%s\n' "$line" - fi - (( ++idx )) - done -} - -# Enclose the input text in header and footer lines. -# -# The header contains the given string as title. The output is preceded -# and followed by an additional newline to make it stand out more. -# -# Globals: -# none -# Arguments: -# $1 - title -# Returns: -# none -# Inputs: -# STDIN - text -# Outputs: -# STDOUT - decorated text -batslib_decorate() { - echo - echo "-- $1 --" - PATH="$BATS_SAVED_PATH" command cat - - echo '--' - echo -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats b/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats deleted file mode 100644 index 89b9db1..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-10-batslib_err.bats +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_err() <message...>: displays <message...>' { - run batslib_err 'm1' 'm2' - [ "$status" -eq 0 ] - [ "$output" == 'm1 m2' ] -} - -@test 'batslib_err(): reads <message...> from STDIN' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - echo 'm1' 'm2' | batslib_err" - [ "$status" -eq 0 ] - [ "$output" == 'm1 m2' ] -} - -@test 'batslib_err() works with modified path' { - export PATH="$BATS_TEST_DIRNAME:$PATH" - echo 'm1 m2' | { - # Verify stub - run which cat - [ "$status" -eq 0 ] - [ "$output" = "$BATS_TEST_DIRNAME/cat" ] - # Should still work - run batslib_err - [ "$status" -eq 0 ] - [ "$output" == 'm1 m2' ] - } -} - -@test 'batslib_err() works with mock function' { - echo 'm1 m2' | { - function cat { - echo "Mocked cat" - } - [ "$(cat)" = "Mocked cat" ] - # Should still work - run batslib_err - [ "$status" -eq 0 ] - [ "$output" == 'm1 m2' ] - } -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats b/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats deleted file mode 100644 index ea172c3..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-11-batslib_count_lines.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_count_lines() <string>: displays the number of lines in <string>' { - run batslib_count_lines $'a\nb\nc\n' - [ "$status" -eq 0 ] - [ "$output" == '3' ] -} - -@test 'batslib_count_lines() <string>: counts the last line when it is not terminated by a newline' { - run batslib_count_lines $'a\nb\nc' - [ "$status" -eq 0 ] - [ "$output" == '3' ] -} - -@test 'batslib_count_lines() <string>: counts empty lines' { - run batslib_count_lines $'\n\n\n' - [ "$status" -eq 0 ] - [ "$output" == '3' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats b/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats deleted file mode 100644 index 484b64d..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-12-batslib_is_single_line.bats +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_is_single_line() <string...>: returns 0 if all <string...> are single-line' { - run batslib_is_single_line 'a' $'b\n' 'c' - [ "$status" -eq 0 ] -} - -@test 'batslib_is_single_line() <string...>: returns 1 if at least one of <string...> is longer than one line' { - run batslib_is_single_line 'a' $'b\nb' 'c' - [ "$status" -eq 1 ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats b/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats deleted file mode 100644 index e6af161..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-13-batslib_get_max_single_line_key_width.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_get_max_single_line_key_width() <pair...>: displays the length of the longest key' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2' 'v 2' - 'k __3' 'v 3' ) - run batslib_get_max_single_line_key_width "${pairs[@]}" - [ "$status" -eq 0 ] - [ "$output" == '5' ] -} - -@test 'batslib_get_max_single_line_key_width() <pair...>: only considers keys with single-line values' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2' 'v 2' - 'k __3' $'v\n3' ) - run batslib_get_max_single_line_key_width "${pairs[@]}" - [ "$status" -eq 0 ] - [ "$output" == '4' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats b/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats deleted file mode 100644 index 7637897..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-14-batslib_print_kv_single.bats +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_print_kv_single() <width> <pair...>: displays <pair...> in two-column format with <width> wide key column' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2 ' 'v 2' - 'k __3' 'v 3' ) - run batslib_print_kv_single 5 "${pairs[@]}" - [ "$status" -eq 0 ] - [ "${#lines[@]}" == '3' ] - [ "${lines[0]}" == 'k _1 : v 1' ] - [ "${lines[1]}" == 'k 2 : v 2' ] - [ "${lines[2]}" == 'k __3 : v 3' ] -} - -@test 'batslib_print_kv_single() <width> <pair...>: does not truncate keys when the column is too narrow' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2' 'v 2' - 'k __3' 'v 3' ) - run batslib_print_kv_single 0 "${pairs[@]}" - [ "$status" -eq 0 ] - [ "${#lines[@]}" == '3' ] - [ "${lines[0]}" == 'k _1 : v 1' ] - [ "${lines[1]}" == 'k 2 : v 2' ] - [ "${lines[2]}" == 'k __3 : v 3' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats b/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats deleted file mode 100644 index 6ad4b3d..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-15-batslib_print_kv_multi.bats +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_print_kv_multi() <pair...>: displays <pair...> in multi-line format' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2' $'v 2-1\nv 2-2' - 'k __3' 'v 3' ) - run batslib_print_kv_multi "${pairs[@]}" - [ "$status" -eq 0 ] - [ "${#lines[@]}" == '7' ] - [ "${lines[0]}" == 'k _1 (1 lines):' ] - [ "${lines[1]}" == 'v 1' ] - [ "${lines[2]}" == 'k 2 (2 lines):' ] - [ "${lines[3]}" == 'v 2-1' ] - [ "${lines[4]}" == 'v 2-2' ] - [ "${lines[5]}" == 'k __3 (1 lines):' ] - [ "${lines[6]}" == 'v 3' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats b/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats deleted file mode 100644 index c20d101..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-16-batslib_print_kv_single_or_multi.bats +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_print_kv_single_or_multi() <width> <pair...>: displays <pair...> in two-column format if all values are one line long' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2 ' 'v 2' - 'k __3' 'v 3' ) - run batslib_print_kv_single_or_multi 5 "${pairs[@]}" - [ "$status" -eq 0 ] - [ "${#lines[@]}" == '3' ] - [ "${lines[0]}" == 'k _1 : v 1' ] - [ "${lines[1]}" == 'k 2 : v 2' ] - [ "${lines[2]}" == 'k __3 : v 3' ] -} - -@test 'batslib_print_kv_single_or_multi() <width> <pair...>: displays <pair...> in multi-line format if at least one value is longer than one line' { - local -ar pairs=( 'k _1' 'v 1' - 'k 2' $'v 2-1\nv 2-2' - 'k __3' 'v 3' ) - run batslib_print_kv_single_or_multi 5 "${pairs[@]}" - [ "$status" -eq 0 ] - [ "${#lines[@]}" == '7' ] - [ "${lines[0]}" == 'k _1 (1 lines):' ] - [ "${lines[1]}" == ' v 1' ] - [ "${lines[2]}" == 'k 2 (2 lines):' ] - [ "${lines[3]}" == ' v 2-1' ] - [ "${lines[4]}" == ' v 2-2' ] - [ "${lines[5]}" == 'k __3 (1 lines):' ] - [ "${lines[6]}" == ' v 3' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats b/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats deleted file mode 100644 index 817fd33..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-17-batslib_prefix.bats +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_prefix() <prefix>: prefixes each line of the input with <prefix>' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf 'a\nb\nc\n' | batslib_prefix '_'" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '_a' ] - [ "${lines[1]}" == '_b' ] - [ "${lines[2]}" == '_c' ] -} - -@test 'batslib_prefix() <prefix>: prefixes the last line when it is not terminated by a newline' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf 'a\nb\nc' | batslib_prefix '_'" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '_a' ] - [ "${lines[1]}" == '_b' ] - [ "${lines[2]}" == '_c' ] -} - -@test 'batslib_prefix() <prefix>: prefixes empty lines' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf '\n\n\n' | batslib_prefix '_'" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '_' ] - [ "${lines[1]}" == '_' ] - [ "${lines[2]}" == '_' ] -} - -@test 'batslib_prefix(): <prefix> default to two spaces' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf 'a\nb\nc\n' | batslib_prefix" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == ' c' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats b/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats deleted file mode 100644 index c5d0975..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-18-batslib_mark.bats +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_mark() <mark> <index>: marks the <index>-th line of the input with <mark>' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c\n' | batslib_mark '>' 0" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '>a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == ' c' ] -} - -@test 'batslib_mark() <mark> <index...>: marks multiple lines when <index...> is in ascending order' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c\n' | batslib_mark '>' 1 2" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == '>b' ] - [ "${lines[2]}" == '>c' ] -} - -@test 'batslib_mark() <mark> <index...>: marks multiple lines when <index...> is in random order' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c\n d\n' | batslib_mark '>' 2 1 3" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 4 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == '>b' ] - [ "${lines[2]}" == '>c' ] - [ "${lines[3]}" == '>d' ] -} - -@test 'batslib_mark() <mark> <index...>: ignores duplicate indices' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c\n' | batslib_mark '>' 1 2 1" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == '>b' ] - [ "${lines[2]}" == '>c' ] -} - -@test 'batslib_mark() <mark> <index...>: outputs the input untouched if <mark> is the empty string' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c\n' | batslib_mark '' 1" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == ' c' ] -} - -@test 'batslib_mark() <mark> <index>: marks the last line when it is not terminated by a newline' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf ' a\n b\n c' | batslib_mark '>' 2" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == ' a' ] - [ "${lines[1]}" == ' b' ] - [ "${lines[2]}" == '>c' ] -} - -@test 'batslib_mark() <mark> <index>: does not truncate <mark> if it is longer than the marked line' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - printf '\n' | batslib_mark '>' 0" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 1 ] - [ "${lines[0]}" == '>' ] -} diff --git a/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats b/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats deleted file mode 100644 index a4d4fcb..0000000 --- a/tests/bash/test_helper/bats-support/test/50-output-19-batslib_decorate.bats +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'batslib_decorate() <title>: encloses the input in a footer line and a header line containing <title>' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - echo 'body' | batslib_decorate 'title'" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- title --' ] - [ "${lines[1]}" == 'body' ] - [ "${lines[2]}" == '--' ] -} - -@test 'batslib_decorate() works with modified path' { - export PATH="$BATS_TEST_DIRNAME:$PATH" - echo body | { - # Verify stub - run which cat - [ "$status" -eq 0 ] - [ "$output" = "$BATS_TEST_DIRNAME/cat" ] - # Should still work - run batslib_decorate 'title' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- title --' ] - [ "${lines[1]}" == 'body' ] - [ "${lines[2]}" == '--' ] - } -} - -@test 'batslib_decorate() works with mock function' { - echo body | { - function cat { - echo "Mocked cat" - } - [ "$(cat)" = "Mocked cat" ] - # Should still work - run batslib_decorate 'title' - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 3 ] - [ "${lines[0]}" == '-- title --' ] - [ "${lines[1]}" == 'body' ] - [ "${lines[2]}" == '--' ] - } -} diff --git a/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats b/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats deleted file mode 100644 index 1d8691a..0000000 --- a/tests/bash/test_helper/bats-support/test/51-error-10-fail.bats +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bats - -load test_helper - -@test 'fail() <message>: returns 1 and displays <message>' { - run fail 'message' - [ "$status" -eq 1 ] - [ "$output" == 'message' ] -} - -@test 'fail(): reads <message> from STDIN' { - run bash -c "source '${TEST_MAIN_DIR}/load.bash' - echo 'message' | fail" - [ "$status" -eq 1 ] - [ "$output" == 'message' ] -} diff --git a/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats b/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats deleted file mode 100644 index 68fd59b..0000000 --- a/tests/bash/test_helper/bats-support/test/52-lang-10-batslib_is_caller.bats +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bats - -load 'test_helper' - - -# Test functions -test_func_lvl_2() { - test_func_lvl_1 "$@" -} - -test_func_lvl_1() { - test_func_lvl_0 "$@" -} - -test_func_lvl_0() { - batslib_is_caller "$@" -} - - -# -# Direct invocation -# - -# Interface -@test 'batslib_is_caller() <function>: returns 0 if the current function was called directly from <function>' { - run test_func_lvl_1 test_func_lvl_1 - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'batslib_is_caller() <function>: returns 1 if the current function was not called directly from <function>' { - run test_func_lvl_0 test_func_lvl_1 - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 0 ] -} - -# Correctness -@test 'batslib_is_caller() <function>: the current function does not appear on the call stack' { - run test_func_lvl_0 test_func_lvl_0 - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 0 ] -} - - -# -# Indirect invocation -# - -# Options -test_i_indirect() { - run test_func_lvl_2 "$@" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'batslib_is_caller() -i <function>: enables indirect checking' { - test_i_indirect -i test_func_lvl_2 -} - -@test 'batslib_is_caller() --indirect <function>: enables indirect checking' { - test_i_indirect --indirect test_func_lvl_2 -} - -# Interface -@test 'batslib_is_caller() --indirect <function>: returns 0 if the current function was called indirectly from <function>' { - run test_func_lvl_2 --indirect test_func_lvl_2 - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'batslib_is_caller() --indirect <function>: returns 1 if the current function was not called indirectly from <function>' { - run test_func_lvl_1 --indirect test_func_lvl_2 - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 0 ] -} - -# Correctness -@test 'batslib_is_caller() --indirect <function>: direct invocation is a special case of indirect invocation with zero intermediate calls' { - run test_func_lvl_1 --indirect test_func_lvl_1 - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 0 ] -} - -@test 'batslib_is_caller() --indirect <function>: the current function does not appear on the call stack' { - run test_func_lvl_0 --indirect test_func_lvl_0 - [ "$status" -eq 1 ] - [ "${#lines[@]}" -eq 0 ] -} diff --git a/tests/bash/test_helper/bats-support/test/cat b/tests/bash/test_helper/bats-support/test/cat deleted file mode 100644 index c45d58f..0000000 --- a/tests/bash/test_helper/bats-support/test/cat +++ /dev/null @@ -1 +0,0 @@ -# Dummy file stubbing a stub/mock diff --git a/tests/bash/test_helper/bats-support/test/test_helper.bash b/tests/bash/test_helper/bats-support/test/test_helper.bash deleted file mode 100644 index ca16775..0000000 --- a/tests/bash/test_helper/bats-support/test/test_helper.bash +++ /dev/null @@ -1,6 +0,0 @@ -setup() { - export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.." - - # Load library. - load '../load' -} From 32251cde9297b39a600d73627ce6985d4b83f6e2 Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:06:06 +0100 Subject: [PATCH 3/9] Fix: add bats-assert and bats-support as proper submodules --- .gitmodules | 6 ++++++ tests/bash/test_helper/bats-assert | 1 + tests/bash/test_helper/bats-support | 1 + 3 files changed, 8 insertions(+) create mode 160000 tests/bash/test_helper/bats-assert create mode 160000 tests/bash/test_helper/bats-support diff --git a/.gitmodules b/.gitmodules index 367a773..565e2dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,9 @@ [submodule "tests/test_helper/bats-assert"] path = tests/test_helper/bats-assert url = https://github.com/bats-core/bats-assert +[submodule "tests/bash/test_helper/bats-support"] + path = tests/bash/test_helper/bats-support + url = https://github.com/bats-core/bats-support +[submodule "tests/bash/test_helper/bats-assert"] + path = tests/bash/test_helper/bats-assert + url = https://github.com/bats-core/bats-assert diff --git a/tests/bash/test_helper/bats-assert b/tests/bash/test_helper/bats-assert new file mode 160000 index 0000000..b93143a --- /dev/null +++ b/tests/bash/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit b93143a1bfbde41d9b7343aab0d36f3ef6549e6b diff --git a/tests/bash/test_helper/bats-support b/tests/bash/test_helper/bats-support new file mode 160000 index 0000000..d007fc1 --- /dev/null +++ b/tests/bash/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit d007fc1f451abbad55204fa9c9eb3e6ed5dc5f61 From 250a3435d607500d28595c536139defd01102c3f Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:18:26 +0100 Subject: [PATCH 4/9] Fix: add bats-assert and bats-support as proper submodules --- README.md | 33 +++++++++++++++++---------------- config.json | 1 - rpcp.sh | 6 +++--- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index a492d0b..f45dbdb 100644 --- a/README.md +++ b/README.md @@ -3,20 +3,20 @@ [![Release](https://img.shields.io/github/v/release/dickymoore/repocopy)](https://github.com/dickymoore/repocopy/releases) [![CI](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml/badge.svg)](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml) -### What is it? +📎 What is it? A lightweight utility to copy the file contents of a local directory to the clipboard. -### Is that all it does? +🤔 Is that all it does? It has the ability to filter out files matched on name or type, or to redact text patterns. -### But why? -In case you've got sensitive information or huge irrelevant files in the repo +❓ But why? +In case you've got sensitive information or huge irrelevant files in the repo. -### No, I mean why copy your repo at all? -Various reasons, but it can be useful for when using using web-based tooling to help with development or debugging, such as when giving an LLM like ChatGPT the full context to a coding issue. +🧠 No, I mean why copy your repo at all? +Various reasons, but it can be useful for when using web-based tooling to help with development or debugging — such as giving an LLM like ChatGPT the full context to a coding issue. -### Why would I want to do that? -I dunno mate. You might not. Go do something more fun. +😐 Why would I want to do that? +I dunno mate. You might not, in which case, go enjoy yourself. --- @@ -42,7 +42,8 @@ When working with AI-assisted development ("vibe coding"), you often need to pro 1. **Clone the repocopy repository** ```bash - git clone https://github.com/<your-org>/repocopy.git + git clone --recurse-submodules https://github.com/dickymoore/repocopy.git + git submodule update --init --recursive ``` 2. **Add to your PATH** @@ -69,7 +70,7 @@ When working with AI-assisted development ("vibe coding"), you often need to pro ``` - **Bash** (in your shell profile): ```bash - alias rpcp='repocopy.sh' + alias rpcp='rpcp.sh' ``` Now you can run: @@ -82,7 +83,7 @@ rpcp ## ⚙️ Configuration (`config.json`) -Place a `config.json` alongside `rpcp.ps1` or `repocopy.sh`. Example: +Place a `config.json` alongside `rpcp.ps1` or `rpcp.sh`. Example: ```json { @@ -139,20 +140,20 @@ rpcp -Verbose rpcp # Override max size, suppress summary: -repocopy.sh --max-file-size 0 --show-copied-files=false +rpcp.sh --max-file-size 0 --show-copied-files=false # Scan a different directory: -repocopy.sh --repo-path /path/to/project +rpcp.sh --repo-path /path/to/project # Verbose debugging: -repocopy.sh --verbose +rpcp.sh --verbose ``` --- ## 🎯 Vibe Coding -After running `rpcp`, your clipboard contains all relevant files with context. Paste directly into your AI tool (ChatGPT, Copilot Chat, etc.) to provide the full structure and content, no manual file hunting required. +After running `rpcp`, your clipboard contains all relevant files with context. Paste directly into your AI tool to provide the full structure and content, no manual file hunting required. --- @@ -179,4 +180,4 @@ Invoke-Pester -Path tests/powershell -Output Detailed ## 📄 License -MIT License · © 2025 Your Name +MIT License · © 2025 Dicky Moore diff --git a/config.json b/config.json index 66d820b..e8ff26a 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,6 @@ "ignoreFolders": [".git", ".github", ".terraform", "node_modules","plugin-cache", "terraform-provider*", "logo-drafts","build", ".archive","test_helper"], "ignoreFiles": ["manifest.json", "package-lock.json","*.png","*.jpg","*.jpeg","*.gif","*.svg","*.zip","*.tar.gz","*.tgz","*.tfstate", "*.tfstate.backup", "*.tfvars", "*.tfvars.json", "*.tfplan", "*.tfplan.json","*.avif","*.webp"], "replacements": { "PARENT_COMPANY":"pca","Bob":"Redacted_name","PROJECT_ACRONYM":"wla" }, - "replacements": { "PARENT_COMPANY":"pca","CLIENT_NAME":"ClientName","PROJECT_ACRONYM":"wla" }, "showCopiedFiles": true, "autoInstallDeps": true } \ No newline at end of file diff --git a/rpcp.sh b/rpcp.sh index a578989..ff50b02 100644 --- a/rpcp.sh +++ b/rpcp.sh @@ -1,19 +1,19 @@ #!/usr/bin/env bash # -# repocopy.sh — Copy filtered parts of a repo to the clipboard according to config.json +# rpcp.sh — Copy filtered parts of a repo to the clipboard according to config.json # # Dependencies: # - jq (for JSON parsing) # - pbcopy (macOS), xclip (Linux), clip.exe or powershell.exe (WSL) for clipboard # # Usage: -# repocopy.sh [--repo-path path] [--config-file file] +# rpcp.sh [--repo-path path] [--config-file file] # [--max-file-size bytes] [--ignore-folders pat1,pat2] # [--ignore-files f1,f2] [--replacements '{"T":"v",...}'] # [--show-copied-files] [--verbose] # # Example: -# repocopy.sh --verbose +# rpcp.sh --verbose # set -euo pipefail From e0b089e26635fb8e61c45f6b69b4426451b9b559 Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:21:46 +0100 Subject: [PATCH 5/9] Fix: add bats-assert and bats-support as proper submodules --- README.md | 201 ++++++++++++++++++++++-------------------------------- 1 file changed, 83 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index f45dbdb..e9ada99 100644 --- a/README.md +++ b/README.md @@ -1,183 +1,148 @@ + # repocopy (rpcp) + [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Release](https://img.shields.io/github/v/release/dickymoore/repocopy)](https://github.com/dickymoore/repocopy/releases) [![CI](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml/badge.svg)](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml) -📎 What is it? -A lightweight utility to copy the file contents of a local directory to the clipboard. - -🤔 Is that all it does? -It has the ability to filter out files matched on name or type, or to redact text patterns. - -❓ But why? -In case you've got sensitive information or huge irrelevant files in the repo. - -🧠 No, I mean why copy your repo at all? -Various reasons, but it can be useful for when using web-based tooling to help with development or debugging — such as giving an LLM like ChatGPT the full context to a coding issue. - -😐 Why would I want to do that? -I dunno mate. You might not, in which case, go enjoy yourself. +> **TL;DR** – `rpcp` is a one‑shot clipboard copier for *codebases*. +> It walks a folder, excludes junk (`.git`, `node_modules`, binaries, etc.), +> redacts secrets you define, then puts the result on your clipboard +> ready to paste into an AI assistant or chat. --- -## 🎯 Use Case - -When working with AI-assisted development ("vibe coding"), you often need to provide the AI with the exact context of your repository or a specific directory. With **repocopy**, you can quickly copy all relevant files (excluding things like `.git`, `node_modules`, large binaries, etc.) into your clipboard in one shot. Then just paste that into your AI tool to give it full awareness of your codebase structure and content. - ---- +## ✨ Why might I need this? -## 🚀 Features +Sometimes you’re pair‑programming with an LLM (ChatGPT, Claude, Copilot Chat etc.) +and you need to give it your *entire* repo or a large sub‑directory for context. +Copy‑pasting file‑by‑file gets old fast – **repocopy** does it in a single command +while letting you: -- **Config-driven**: Exclude folders, files, set max file size, and define token replacements via `config.json`. -- **One-command operation**: Clone the repo, add the script directory to your `PATH`, then run `rpcp` to copy contents of the current folder. -- **Automatic replacements**: Swap tokens (e.g. `PARENT_COMPANY`, `CLIENT_NAME`) as defined in your config. -- **Verbose mode**: See exactly which files were included or excluded and why. -- **Show summary**: Optionally list which files got copied, either via CLI or config. -- **Cross-platform support**: Run on Windows PowerShell or macOS/Linux Bash. +* redact sensitive tokens (`ACME` → `ClientName`) +* skip whole directories or globs +* honour a max file‑size +* view exactly which files were included --- -## 📦 Installation +## 🚀 Quick‑start -1. **Clone the repocopy repository** - - ```bash - git clone --recurse-submodules https://github.com/dickymoore/repocopy.git - git submodule update --init --recursive - ``` - -2. **Add to your PATH** +```bash +# clone with submodules & add rpcp to your PATH +git clone --recurse-submodules https://github.com/dickymoore/repocopy.git +export PATH="$PATH:$(pwd)/repocopy" - - **Windows (PowerShell)**: - 1. Locate the folder where you cloned `repocopy` (e.g. `C:\tools\repocopy`). - 2. Open System Settings → Environment Variables → User Variables → `Path` → Edit → New. - 3. Paste the full path to your `repocopy` folder and click OK. - 4. Restart your PowerShell session. +# copy the *current* folder (default) +rpcp - - **macOS/Linux (Bash)**: - 1. Ensure you have [PowerShell Core](https://github.com/PowerShell/PowerShell) or use the Bash script directly. - 2. Add the folder to your `PATH`: - ```bash - export PATH="$PATH:/path/to/repocopy" - ``` - 3. Add that line to your shell profile (`~/.bashrc`, `~/.zshrc`, or `~/.profile`). +# copy another repo, verbosely +rpcp --repo-path ~/src/my‑project --verbose +``` -3. **Optionally create an alias** +> **Clipboard helpers** +> +> * Linux: requires `xclip` > * macOS: uses `pbcopy` > * Windows / WSL: uses `clip.exe` or `powershell Set‑Clipboard` - - **PowerShell** (in your PowerShell profile): - ```powershell - Set-Alias rpcp "$(Join-Path 'C:\tools\repocopy' 'rpcp.ps1')" - ``` - - **Bash** (in your shell profile): - ```bash - alias rpcp='rpcp.sh' - ``` +--- -Now you can run: +## 📦 Requirements -```bash -rpcp -``` +* **Bash** 4+ **or** **PowerShell** 7+ +* `jq` – for the Bash version (auto‑installed if `autoInstallDeps = true`) +* A clipboard tool (`pbcopy`, `xclip`, `clip.exe`, or `pwsh`) --- ## ⚙️ Configuration (`config.json`) -Place a `config.json` alongside `rpcp.ps1` or `rpcp.sh`. Example: - -```json +```jsonc { + // folder to scan – “.” = working directory "repoPath": ".", + + // ignore folders / files (globs ok) + "ignoreFolders": ["build", ".git", "node_modules"], + "ignoreFiles": ["manifest.json", "*.png"], + + // max bytes per file (0 = unlimited) "maxFileSize": 204800, - "ignoreFolders": [ - ".git", ".github", ".terraform", "node_modules", - "plugin-cache", "terraform-provider*", "logo-drafts", - "build", ".archive" - ], - "ignoreFiles": [ - "manifest.json", "package-lock.json" - ], + + // string replacements applied to every file "replacements": { - "PARENT_COMPANY": "pca", - "CLIENT_NAME": "ClientName", - "PROJECT_ACRONYM": "wla" + "ClientName": "ACME" }, - "showCopiedFiles": true + + // print a summary afterwards? + "showCopiedFiles": true, + + // let rpcp.sh auto‑install jq if missing + "autoInstallDeps": true } ``` -- **repoPath**: Default folder to scan (`.` = current directory). -- **maxFileSize**: Max bytes per file (0 = no limit). -- **ignoreFolders**: Wildcard patterns of folder names to skip. -- **ignoreFiles**: Exact file names to skip. -- **replacements**: Key/value pairs to replace in file contents. -- **showCopiedFiles**: `true` to list included files after copying. +Every CLI switch overrides the matching JSON field – handy when you just +need to bump `--max-file-size` for one run. --- -## 💻 Usage +## 💻 Usage snippets -### PowerShell (Windows or Core) +### PowerShell ```powershell -# Copy current directory +# basic rpcp -# Override max size, suppress summary: -rpcp -MaxFileSize 0 -ShowCopiedFiles:$false - -# Scan a different directory: -rpcp -RepoPath 'C:\Projects\MyApp' +# disable size filter +rpcp -MaxFileSize 0 -# Verbose debugging: -rpcp -Verbose +# different folder, quiet output +rpcp -RepoPath C:\Code\Foo -Verbose:$false ``` -### Bash (macOS/Linux) +### Bash ```bash -# Copy current directory +# basic rpcp -# Override max size, suppress summary: -rpcp.sh --max-file-size 0 --show-copied-files=false +# disable size filter & summary +rpcp --max-file-size 0 --show-copied-files=false -# Scan a different directory: -rpcp.sh --repo-path /path/to/project - -# Verbose debugging: -rpcp.sh --verbose +# different folder +rpcp --repo-path ~/Code/Foo ``` --- -## 🎯 Vibe Coding - -After running `rpcp`, your clipboard contains all relevant files with context. Paste directly into your AI tool to provide the full structure and content, no manual file hunting required. +## 🧪 Running tests locally ---- - -## 🧪 Testing & Linting - -# Running tests locally +```bash +# one‑time: fetch the helper submodules +git submodule update --init --recursive -## Bash (Bats) +# Bash tests +sudo apt install bats jq xclip # linux example +bats tests/bash -```bash -sudo apt install bats jq xclip # or the equivalent for your OS -bats tests/bash/repocopy.bats +# PowerShell tests +pwsh -Command 'Install-Module Pester -Scope CurrentUser -Force' +pwsh -Command 'Invoke-Pester tests/powershell' ``` -## PowerShell (Pester) +--- -``` -Install-Module Pester -Force -Scope CurrentUser # once -Invoke-Pester -Path tests/powershell -Output Detailed -``` +## 📝 Development notes +* Shell files are expected to use **LF** endings. + A `.gitattributes` is provided so Windows Git converts on checkout. +* CI runs both Pester (PowerShell) and Bats (Bash) on + **ubuntu‑latest** and **windows‑latest** runners. +* Want to contribute? See **CONTRIBUTING.md**. --- ## 📄 License -MIT License · © 2025 Dicky Moore +MIT © 2025 Dicky Moore From 57571e8e883722baf2a6c49f2ca4e4fec8693c9c Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:24:09 +0100 Subject: [PATCH 6/9] Fix: add bats-assert and bats-support as proper submodules --- .gitattributes | 10 +++++++++ .github/workflows/ci.yml | 44 +++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..309da3f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# Normalise line endings for all shell-ish files (keeps Bats happy) +*.sh text eol=lf +*.bash text eol=lf +*.bats text eol=lf + +# (Optional) keep CRLF for Windows-native scripts +*.ps1 text eol=crlf + +# Fall-back: let Git auto-detect for everything else +* text=auto diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8c8900..306eea5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,29 +2,41 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] jobs: - test-bash: - runs-on: ubuntu-latest + tests: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + runs-on: ${{ matrix.os }} + steps: - - uses: actions/checkout@v3 - - name: Install dependencies - run: sudo apt update && sudo apt install -y jq xclip bats - - name: Run Bash tests + - name: Checkout (with submodules) + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Linux test deps + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install -y bats jq xclip + - name: Run Bash test-suite (Bats) + if: runner.os == 'Linux' run: bats tests/bash/rpcp.bats - pester-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install Pester - shell: pwsh + - name: Install Windows test deps + if: runner.os == 'Windows' run: | - Install-Module Pester -Force -Scope CurrentUser - - name: Run PowerShell tests + choco install bats --version=1.10.0 -y + choco install jq -y + - name: Run PowerShell test-suite (Pester) + if: runner.os == 'Windows' shell: pwsh run: | + Install-Module Pester -Force -Scope CurrentUser Invoke-Pester -Path tests/powershell -CI -Output Detailed From bf5edf7bafa86da2b874a98c6c8e659fe98ba7f2 Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:26:34 +0100 Subject: [PATCH 7/9] Fix: add bats-assert and bats-support as proper submodules --- pre-commit-config.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 pre-commit-config.yaml diff --git a/pre-commit-config.yaml b/pre-commit-config.yaml new file mode 100644 index 0000000..56294a0 --- /dev/null +++ b/pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.2.5 + hooks: + - id: prettier + name: prettier‑sh + additional_dependencies: ["@prettier/plugin-sh"] + types: [shell] + + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.9.0.4 + hooks: + - id: shellcheck + args: ["--severity", "warning"] + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: mixed-line-ending + args: [--fix=lf] From de74333c5e204188c7879d99fece66ebdf7bb82c Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:29:51 +0100 Subject: [PATCH 8/9] Fix: add bats-assert and bats-support as proper submodules --- README.md | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e9ada99..bf5cd4a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ - # repocopy (rpcp) - [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Release](https://img.shields.io/github/v/release/dickymoore/repocopy)](https://github.com/dickymoore/repocopy/releases) [![CI](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml/badge.svg)](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml) @@ -28,27 +26,51 @@ while letting you: ## 🚀 Quick‑start +### PowerShell 7+ (Windows, macOS, Linux) + +```powershell +# 1. Clone with submodules & jump in +git clone --recurse-submodules https://github.com/dickymoore/repocopy.git +cd repocopy + +# 2. Add rpcp to your PATH (current session) +$env:PATH += ';' + (Get-Location) + +# 3. Copy the *current* folder (default) +rpcp + +# 4. Copy another repo, verbose +rpcp -RepoPath 'C:\src\my-project' -Verbose +``` + +> **Clipboard helper** – uses the built‑in **Set‑Clipboard** cmdlet (no extra installs). + +--- + +### Bash / Zsh (macOS, Linux, WSL) + ```bash -# clone with submodules & add rpcp to your PATH +# 1. Clone with submodules & add rpcp to your PATH git clone --recurse-submodules https://github.com/dickymoore/repocopy.git export PATH="$PATH:$(pwd)/repocopy" -# copy the *current* folder (default) +# 2. Copy the *current* folder (default) rpcp -# copy another repo, verbosely -rpcp --repo-path ~/src/my‑project --verbose +# 3. Copy another repo, verbosely +rpcp --repo-path ~/src/my-project --verbose ``` -> **Clipboard helpers** -> -> * Linux: requires `xclip` > * macOS: uses `pbcopy` > * Windows / WSL: uses `clip.exe` or `powershell Set‑Clipboard` +> **Clipboard helpers** – +> • Linux: requires `xclip` +> • macOS: uses `pbcopy` +> • WSL: uses `clip.exe` --- ## 📦 Requirements -* **Bash** 4+ **or** **PowerShell** 7+ +* **Bash** 4+ **or** **PowerShell** 7+ * `jq` – for the Bash version (auto‑installed if `autoInstallDeps = true`) * A clipboard tool (`pbcopy`, `xclip`, `clip.exe`, or `pwsh`) From 13c2b74b473f154666c61827c281dcc3fa82bc8b Mon Sep 17 00:00:00 2001 From: Richard Moore <dickymoore@gmail.com> Date: Mon, 5 May 2025 10:36:45 +0100 Subject: [PATCH 9/9] fixed line endings --- LICENSE.md | 42 +- README.md | 340 ++++++------ config.json | 16 +- rpcp.ps1 | 544 ++++++++++---------- tests/bash/rpcp.bats | 222 ++++---- tests/fixtures/sample-repo/build/output.txt | 2 +- tests/fixtures/sample-repo/config.json | 16 +- tests/fixtures/sample-repo/manifest.json | 2 +- tests/fixtures/sample-repo/src/include.txt | 2 +- tests/powershell/rpcp.tests.ps1 | 292 +++++------ 10 files changed, 739 insertions(+), 739 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 8648ca5..7566204 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2025 dickymoore - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2025 dickymoore + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index bf5cd4a..9db1bd0 100644 --- a/README.md +++ b/README.md @@ -1,170 +1,170 @@ -# repocopy (rpcp) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Release](https://img.shields.io/github/v/release/dickymoore/repocopy)](https://github.com/dickymoore/repocopy/releases) -[![CI](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml/badge.svg)](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml) - -> **TL;DR** – `rpcp` is a one‑shot clipboard copier for *codebases*. -> It walks a folder, excludes junk (`.git`, `node_modules`, binaries, etc.), -> redacts secrets you define, then puts the result on your clipboard -> ready to paste into an AI assistant or chat. - ---- - -## ✨ Why might I need this? - -Sometimes you’re pair‑programming with an LLM (ChatGPT, Claude, Copilot Chat etc.) -and you need to give it your *entire* repo or a large sub‑directory for context. -Copy‑pasting file‑by‑file gets old fast – **repocopy** does it in a single command -while letting you: - -* redact sensitive tokens (`ACME` → `ClientName`) -* skip whole directories or globs -* honour a max file‑size -* view exactly which files were included - ---- - -## 🚀 Quick‑start - -### PowerShell 7+ (Windows, macOS, Linux) - -```powershell -# 1. Clone with submodules & jump in -git clone --recurse-submodules https://github.com/dickymoore/repocopy.git -cd repocopy - -# 2. Add rpcp to your PATH (current session) -$env:PATH += ';' + (Get-Location) - -# 3. Copy the *current* folder (default) -rpcp - -# 4. Copy another repo, verbose -rpcp -RepoPath 'C:\src\my-project' -Verbose -``` - -> **Clipboard helper** – uses the built‑in **Set‑Clipboard** cmdlet (no extra installs). - ---- - -### Bash / Zsh (macOS, Linux, WSL) - -```bash -# 1. Clone with submodules & add rpcp to your PATH -git clone --recurse-submodules https://github.com/dickymoore/repocopy.git -export PATH="$PATH:$(pwd)/repocopy" - -# 2. Copy the *current* folder (default) -rpcp - -# 3. Copy another repo, verbosely -rpcp --repo-path ~/src/my-project --verbose -``` - -> **Clipboard helpers** – -> • Linux: requires `xclip` -> • macOS: uses `pbcopy` -> • WSL: uses `clip.exe` - ---- - -## 📦 Requirements - -* **Bash** 4+ **or** **PowerShell** 7+ -* `jq` – for the Bash version (auto‑installed if `autoInstallDeps = true`) -* A clipboard tool (`pbcopy`, `xclip`, `clip.exe`, or `pwsh`) - ---- - -## ⚙️ Configuration (`config.json`) - -```jsonc -{ - // folder to scan – “.” = working directory - "repoPath": ".", - - // ignore folders / files (globs ok) - "ignoreFolders": ["build", ".git", "node_modules"], - "ignoreFiles": ["manifest.json", "*.png"], - - // max bytes per file (0 = unlimited) - "maxFileSize": 204800, - - // string replacements applied to every file - "replacements": { - "ClientName": "ACME" - }, - - // print a summary afterwards? - "showCopiedFiles": true, - - // let rpcp.sh auto‑install jq if missing - "autoInstallDeps": true -} -``` - -Every CLI switch overrides the matching JSON field – handy when you just -need to bump `--max-file-size` for one run. - ---- - -## 💻 Usage snippets - -### PowerShell - -```powershell -# basic -rpcp - -# disable size filter -rpcp -MaxFileSize 0 - -# different folder, quiet output -rpcp -RepoPath C:\Code\Foo -Verbose:$false -``` - -### Bash - -```bash -# basic -rpcp - -# disable size filter & summary -rpcp --max-file-size 0 --show-copied-files=false - -# different folder -rpcp --repo-path ~/Code/Foo -``` - ---- - -## 🧪 Running tests locally - -```bash -# one‑time: fetch the helper submodules -git submodule update --init --recursive - -# Bash tests -sudo apt install bats jq xclip # linux example -bats tests/bash - -# PowerShell tests -pwsh -Command 'Install-Module Pester -Scope CurrentUser -Force' -pwsh -Command 'Invoke-Pester tests/powershell' -``` - ---- - -## 📝 Development notes - -* Shell files are expected to use **LF** endings. - A `.gitattributes` is provided so Windows Git converts on checkout. -* CI runs both Pester (PowerShell) and Bats (Bash) on - **ubuntu‑latest** and **windows‑latest** runners. -* Want to contribute? See **CONTRIBUTING.md**. - ---- - -## 📄 License - -MIT © 2025 Dicky Moore +# repocopy (rpcp) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Release](https://img.shields.io/github/v/release/dickymoore/repocopy)](https://github.com/dickymoore/repocopy/releases) +[![CI](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml/badge.svg)](https://github.com/dickymoore/repocopy/actions/workflows/ci.yml) + +> **TL;DR** – `rpcp` is a one‑shot clipboard copier for *codebases*. +> It walks a folder, excludes junk (`.git`, `node_modules`, binaries, etc.), +> redacts secrets you define, then puts the result on your clipboard +> ready to paste into an AI assistant or chat. + +--- + +## ✨ Why might I need this? + +Sometimes you’re pair‑programming with an LLM (ChatGPT, Claude, Copilot Chat etc.) +and you need to give it your *entire* repo or a large sub‑directory for context. +Copy‑pasting file‑by‑file gets old fast – **repocopy** does it in a single command +while letting you: + +* redact sensitive tokens (`ACME` → `ClientName`) +* skip whole directories or globs +* honour a max file‑size +* view exactly which files were included + +--- + +## 🚀 Quick‑start + +### PowerShell 7+ (Windows, macOS, Linux) + +```powershell +# 1. Clone with submodules & jump in +git clone --recurse-submodules https://github.com/dickymoore/repocopy.git +cd repocopy + +# 2. Add rpcp to your PATH (current session) +$env:PATH += ';' + (Get-Location) + +# 3. Copy the *current* folder (default) +rpcp + +# 4. Copy another repo, verbose +rpcp -RepoPath 'C:\src\my-project' -Verbose +``` + +> **Clipboard helper** – uses the built‑in **Set‑Clipboard** cmdlet (no extra installs). + +--- + +### Bash / Zsh (macOS, Linux, WSL) + +```bash +# 1. Clone with submodules & add rpcp to your PATH +git clone --recurse-submodules https://github.com/dickymoore/repocopy.git +export PATH="$PATH:$(pwd)/repocopy" + +# 2. Copy the *current* folder (default) +rpcp + +# 3. Copy another repo, verbosely +rpcp --repo-path ~/src/my-project --verbose +``` + +> **Clipboard helpers** – +> • Linux: requires `xclip` +> • macOS: uses `pbcopy` +> • WSL: uses `clip.exe` + +--- + +## 📦 Requirements + +* **Bash** 4+ **or** **PowerShell** 7+ +* `jq` – for the Bash version (auto‑installed if `autoInstallDeps = true`) +* A clipboard tool (`pbcopy`, `xclip`, `clip.exe`, or `pwsh`) + +--- + +## ⚙️ Configuration (`config.json`) + +```jsonc +{ + // folder to scan – “.” = working directory + "repoPath": ".", + + // ignore folders / files (globs ok) + "ignoreFolders": ["build", ".git", "node_modules"], + "ignoreFiles": ["manifest.json", "*.png"], + + // max bytes per file (0 = unlimited) + "maxFileSize": 204800, + + // string replacements applied to every file + "replacements": { + "ClientName": "ACME" + }, + + // print a summary afterwards? + "showCopiedFiles": true, + + // let rpcp.sh auto‑install jq if missing + "autoInstallDeps": true +} +``` + +Every CLI switch overrides the matching JSON field – handy when you just +need to bump `--max-file-size` for one run. + +--- + +## 💻 Usage snippets + +### PowerShell + +```powershell +# basic +rpcp + +# disable size filter +rpcp -MaxFileSize 0 + +# different folder, quiet output +rpcp -RepoPath C:\Code\Foo -Verbose:$false +``` + +### Bash + +```bash +# basic +rpcp + +# disable size filter & summary +rpcp --max-file-size 0 --show-copied-files=false + +# different folder +rpcp --repo-path ~/Code/Foo +``` + +--- + +## 🧪 Running tests locally + +```bash +# one‑time: fetch the helper submodules +git submodule update --init --recursive + +# Bash tests +sudo apt install bats jq xclip # linux example +bats tests/bash + +# PowerShell tests +pwsh -Command 'Install-Module Pester -Scope CurrentUser -Force' +pwsh -Command 'Invoke-Pester tests/powershell' +``` + +--- + +## 📝 Development notes + +* Shell files are expected to use **LF** endings. + A `.gitattributes` is provided so Windows Git converts on checkout. +* CI runs both Pester (PowerShell) and Bats (Bash) on + **ubuntu‑latest** and **windows‑latest** runners. +* Want to contribute? See **CONTRIBUTING.md**. + +--- + +## 📄 License + +MIT © 2025 Dicky Moore diff --git a/config.json b/config.json index e8ff26a..c4c8354 100644 --- a/config.json +++ b/config.json @@ -1,9 +1,9 @@ -{ - "repoPath": ".", - "maxFileSize": 204800, - "ignoreFolders": [".git", ".github", ".terraform", "node_modules","plugin-cache", "terraform-provider*", "logo-drafts","build", ".archive","test_helper"], - "ignoreFiles": ["manifest.json", "package-lock.json","*.png","*.jpg","*.jpeg","*.gif","*.svg","*.zip","*.tar.gz","*.tgz","*.tfstate", "*.tfstate.backup", "*.tfvars", "*.tfvars.json", "*.tfplan", "*.tfplan.json","*.avif","*.webp"], - "replacements": { "PARENT_COMPANY":"pca","Bob":"Redacted_name","PROJECT_ACRONYM":"wla" }, - "showCopiedFiles": true, - "autoInstallDeps": true +{ + "repoPath": ".", + "maxFileSize": 204800, + "ignoreFolders": [".git", ".github", ".terraform", "node_modules","plugin-cache", "terraform-provider*", "logo-drafts","build", ".archive","test_helper"], + "ignoreFiles": ["manifest.json", "package-lock.json","*.png","*.jpg","*.jpeg","*.gif","*.svg","*.zip","*.tar.gz","*.tgz","*.tfstate", "*.tfstate.backup", "*.tfvars", "*.tfvars.json", "*.tfplan", "*.tfplan.json","*.avif","*.webp"], + "replacements": { "PARENT_COMPANY":"pca","Bob":"Redacted_name","PROJECT_ACRONYM":"wla" }, + "showCopiedFiles": true, + "autoInstallDeps": true } \ No newline at end of file diff --git a/rpcp.ps1 b/rpcp.ps1 index 5385fbd..8ac4b26 100644 --- a/rpcp.ps1 +++ b/rpcp.ps1 @@ -1,272 +1,272 @@ -<# -.SYNOPSIS - Copy filtered parts of a repo to the clipboard according to config.json. - -.DESCRIPTION - Loads configuration from a JSON file (by default in the same folder as the script), - enumerates all files under the target folder, excludes or includes based on: - • ignoreFolders (wildcards OK) - • ignoreFiles (exact names) - • maxFileSize (bytes, 0 = no limit) - Always applies token replacements from the config. Copies the final concatenated - content to the clipboard. Command-line parameters can override any config value, - and a JSON setting “showCopiedFiles” will automatically list the files copied - unless overridden on the CLI. - -.PARAMETER RepoPath - Path of the repository to scan. Defaults to the current directory. - Must be an existing folder. - -.PARAMETER ConfigFile - Path to the JSON configuration file. Defaults to "config.json" in the script’s folder. - Must be an existing file. - -.PARAMETER MaxFileSize - Maximum file size in bytes to include; set to 0 to disable size filtering. - Overrides the value in the config file if specified. - -.PARAMETER IgnoreFolders - Array of folder name patterns to ignore (supports wildcards). - Overrides the config file’s ignoreFolders when specified. - -.PARAMETER IgnoreFiles - Array of file names to ignore (exact matches). - Overrides the config file’s ignoreFiles when specified. - -.PARAMETER Replacements - Hashtable of token → replacement pairs. - Overrides the config file’s replacements when specified. - -.PARAMETER ShowCopiedFiles - If specified (or if “showCopiedFiles” is true in config.json), after copying - to clipboard the script will list every file that was included. - -.PARAMETER Verbose - Standard PowerShell -Verbose switch. When used, logs every file’s include/exclude - decision and the reason. - -.EXAMPLE - .\rpcp.ps1 - # Uses config.json, applies its settings. - -.EXAMPLE - .\rpcp.ps1 -MaxFileSize 0 -ShowCopiedFiles:$false - # Disables size filtering; suppresses the copied-file list. - -.EXAMPLE - .\rpcp.ps1 -RepoPath 'C:\MyProject' -ConfigFile '.\myconfig.json' -#> -[CmdletBinding()] -Param( - [Parameter()] - [ValidateScript({ Test-Path $_ -PathType Container })] - [string] $RepoPath = '.', - - [Parameter()] - [ValidateScript({ Test-Path $_ -PathType Leaf })] - [string] $ConfigFile, - - [Parameter()] - [ValidateRange(0, [long]::MaxValue)] - [long] $MaxFileSize, - - [Parameter()] - [AllowNull()] - [string[]] $IgnoreFolders, - - [Parameter()] - [AllowNull()] - [string[]] $IgnoreFiles, - - [Parameter()] - [AllowNull()] - - [hashtable]$Replacements, - - [Parameter()] - [switch] $ShowCopiedFiles -) - -function Get-Config { - [CmdletBinding()] - Param( - [Parameter(Mandatory)] - [ValidateScript({ Test-Path $_ -PathType Leaf })] - [string] $ConfigFilePath - ) - try { - $text = Get-Content -Raw -Path $ConfigFilePath -ErrorAction Stop - return $text | ConvertFrom-Json -ErrorAction Stop - } catch { - Write-Error "Failed to load config from '$ConfigFilePath': $_" -ErrorAction Stop - } -} - -function Get-FilesToInclude { - [CmdletBinding()] - Param( - [Parameter()] - [ValidateScript({ Test-Path $_ -PathType Container })] - [string] $RepoRoot, - - # ↓↓↓ CHANGE #1 – remove Mandatory, give default @() - [Parameter()] - [string[]] $IgnoreFolders = @(), - - # ↓↓↓ CHANGE #2 – remove Mandatory, give default @() - [Parameter()] - [string[]] $IgnoreFiles = @(), - - [Parameter()] - - [ValidateRange(0, [long]::MaxValue)] - [long] $MaxFileSize - ) - $all = Get-ChildItem -Path (Join-Path $RepoRoot '*') -Recurse -File -ErrorAction Stop - $result = [System.Collections.Generic.List[System.IO.FileInfo]]::new() - - foreach ($f in $all) { - $reason = $null - # Folder pattern check - $dirs = $f.DirectoryName.Split([IO.Path]::DirectorySeparatorChar) - foreach ($pat in $IgnoreFolders) { - $sepRegex = [Regex]::Escape([IO.Path]::DirectorySeparatorChar) - $segments = $f.DirectoryName -split $sepRegex # safe on Win & *nix - - foreach ($pat in $IgnoreFolders) { - if ($segments -like $pat) { - $reason = "matched ignore-folder '$pat'" - break - } - - } - } - # File name check - if (-not $reason) { - foreach ($pattern in $IgnoreFiles) { - if ($f.Name -like $pattern) { - $reason = "filename '$($f.Name)' matches ignore pattern '$pattern'" - break - } - } - } - - # Size check - if (-not $reason -and $MaxFileSize -gt 0 -and $f.Length -gt $MaxFileSize) { - $reason = "exceeds maxFileSize ($MaxFileSize bytes)" - } - - if ($reason) { - Write-Verbose "EXCLUDING: $($f.FullName) → $reason" - } else { - Write-Verbose "INCLUDING: $($f.FullName)" - $result.Add($f) - } - } - - return $result -} - -function Build-ClipboardContent { - [CmdletBinding()] - Param( - [Parameter(Mandatory)] - [System.Collections.Generic.List[System.IO.FileInfo]] $Files, - - [Parameter()] - [hashtable] $Replacements - ) - $sb = [System.Text.StringBuilder]::new() - foreach ($f in $Files) { - $sb.AppendLine("File: $($f.FullName)") | Out-Null - $sb.AppendLine(('-' * 60)) | Out-Null - $text = Get-Content -Raw -LiteralPath $f.FullName -ErrorAction Stop - - foreach ($token in $Replacements.Keys) { - $val = $Replacements[$token] - $text = $text -replace ([Regex]::Escape($token)), $val - } - - $sb.AppendLine($text) | Out-Null - $sb.AppendLine() | Out-Null - } - return $sb.ToString() -} - -function Copy-ToClipboard { - [CmdletBinding()] - Param( - [Parameter(Mandatory)] - [string] $Content, - - [Parameter()] - [switch] $ShowList, - - [Parameter()] - [System.Collections.Generic.List[System.IO.FileInfo]] $Files - ) - # Pass the entire string as a single Value, rather than via the pipeline - Set-Clipboard -Value $Content - - Write-Host "✅ Copied $($Files.Count) file(s) to clipboard." - if ($ShowList) { - Write-Host "`nFiles included:" - foreach ($f in $Files) { - Write-Host " - $($f.FullName)" - } - } -} - -#–– Begin script logic –– - -if (-not $PSBoundParameters.ContainsKey('ConfigFile')) { - # Are we running from a script file? - if ($MyInvocation.MyCommand.Path) { - # Use the folder the script lives in - $scriptFolder = Split-Path -Parent $MyInvocation.MyCommand.Path - } - else { - # Interactive / dot-sourced: use the cwd - $scriptFolder = (Get-Location).ProviderPath - } - - $ConfigFile = Join-Path $scriptFolder 'config.json' -} - - -# Load and merge config -$config = Get-Config -ConfigFilePath $ConfigFile - -# Merge CLI parameters over config values -$rp = if ($PSBoundParameters.ContainsKey('RepoPath')) { $RepoPath } else { $config.repoPath } -$mf = if ($PSBoundParameters.ContainsKey('MaxFileSize')) { $MaxFileSize } else { [long]$config.maxFileSize } -$if = if ($PSBoundParameters.ContainsKey('IgnoreFolders') -and $IgnoreFolders) { $IgnoreFolders } else { @($config.ignoreFolders) } -$ifl = if ($PSBoundParameters.ContainsKey('IgnoreFiles') -and $IgnoreFiles) { $IgnoreFiles } else { @($config.ignoreFiles) } -$rep = if ($PSBoundParameters.ContainsKey('Replacements')) { $Replacements } else { - $h = @{}; foreach ($p in $config.replacements.PSObject.Properties) { $h[$p.Name] = $p.Value }; $h -} -$scf = if ($PSBoundParameters.ContainsKey('ShowCopiedFiles')) { - $ShowCopiedFiles.IsPresent - } else { - [bool]$config.showCopiedFiles - } - -if ($null -eq $if) { $if = @() } -if ($null -eq $ifl) { $ifl = @() } - -# Gather, filter, and log -$filesToCopy = Get-FilesToInclude ` - -RepoRoot $rp ` - -IgnoreFolders $if ` - -IgnoreFiles $ifl ` - -MaxFileSize $mf - - -if ($filesToCopy.Count -eq 0) { - Write-Warning 'No files passed the filters; nothing to copy.' - return -} - -# Build content & copy -$content = Build-ClipboardContent -Files $filesToCopy -Replacements $rep -Copy-ToClipboard -Content $content -ShowList:$scf -Files $filesToCopy +<# +.SYNOPSIS + Copy filtered parts of a repo to the clipboard according to config.json. + +.DESCRIPTION + Loads configuration from a JSON file (by default in the same folder as the script), + enumerates all files under the target folder, excludes or includes based on: + • ignoreFolders (wildcards OK) + • ignoreFiles (exact names) + • maxFileSize (bytes, 0 = no limit) + Always applies token replacements from the config. Copies the final concatenated + content to the clipboard. Command-line parameters can override any config value, + and a JSON setting “showCopiedFiles” will automatically list the files copied + unless overridden on the CLI. + +.PARAMETER RepoPath + Path of the repository to scan. Defaults to the current directory. + Must be an existing folder. + +.PARAMETER ConfigFile + Path to the JSON configuration file. Defaults to "config.json" in the script’s folder. + Must be an existing file. + +.PARAMETER MaxFileSize + Maximum file size in bytes to include; set to 0 to disable size filtering. + Overrides the value in the config file if specified. + +.PARAMETER IgnoreFolders + Array of folder name patterns to ignore (supports wildcards). + Overrides the config file’s ignoreFolders when specified. + +.PARAMETER IgnoreFiles + Array of file names to ignore (exact matches). + Overrides the config file’s ignoreFiles when specified. + +.PARAMETER Replacements + Hashtable of token → replacement pairs. + Overrides the config file’s replacements when specified. + +.PARAMETER ShowCopiedFiles + If specified (or if “showCopiedFiles” is true in config.json), after copying + to clipboard the script will list every file that was included. + +.PARAMETER Verbose + Standard PowerShell -Verbose switch. When used, logs every file’s include/exclude + decision and the reason. + +.EXAMPLE + .\rpcp.ps1 + # Uses config.json, applies its settings. + +.EXAMPLE + .\rpcp.ps1 -MaxFileSize 0 -ShowCopiedFiles:$false + # Disables size filtering; suppresses the copied-file list. + +.EXAMPLE + .\rpcp.ps1 -RepoPath 'C:\MyProject' -ConfigFile '.\myconfig.json' +#> +[CmdletBinding()] +Param( + [Parameter()] + [ValidateScript({ Test-Path $_ -PathType Container })] + [string] $RepoPath = '.', + + [Parameter()] + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string] $ConfigFile, + + [Parameter()] + [ValidateRange(0, [long]::MaxValue)] + [long] $MaxFileSize, + + [Parameter()] + [AllowNull()] + [string[]] $IgnoreFolders, + + [Parameter()] + [AllowNull()] + [string[]] $IgnoreFiles, + + [Parameter()] + [AllowNull()] + + [hashtable]$Replacements, + + [Parameter()] + [switch] $ShowCopiedFiles +) + +function Get-Config { + [CmdletBinding()] + Param( + [Parameter(Mandatory)] + [ValidateScript({ Test-Path $_ -PathType Leaf })] + [string] $ConfigFilePath + ) + try { + $text = Get-Content -Raw -Path $ConfigFilePath -ErrorAction Stop + return $text | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-Error "Failed to load config from '$ConfigFilePath': $_" -ErrorAction Stop + } +} + +function Get-FilesToInclude { + [CmdletBinding()] + Param( + [Parameter()] + [ValidateScript({ Test-Path $_ -PathType Container })] + [string] $RepoRoot, + + # ↓↓↓ CHANGE #1 – remove Mandatory, give default @() + [Parameter()] + [string[]] $IgnoreFolders = @(), + + # ↓↓↓ CHANGE #2 – remove Mandatory, give default @() + [Parameter()] + [string[]] $IgnoreFiles = @(), + + [Parameter()] + + [ValidateRange(0, [long]::MaxValue)] + [long] $MaxFileSize + ) + $all = Get-ChildItem -Path (Join-Path $RepoRoot '*') -Recurse -File -ErrorAction Stop + $result = [System.Collections.Generic.List[System.IO.FileInfo]]::new() + + foreach ($f in $all) { + $reason = $null + # Folder pattern check + $dirs = $f.DirectoryName.Split([IO.Path]::DirectorySeparatorChar) + foreach ($pat in $IgnoreFolders) { + $sepRegex = [Regex]::Escape([IO.Path]::DirectorySeparatorChar) + $segments = $f.DirectoryName -split $sepRegex # safe on Win & *nix + + foreach ($pat in $IgnoreFolders) { + if ($segments -like $pat) { + $reason = "matched ignore-folder '$pat'" + break + } + + } + } + # File name check + if (-not $reason) { + foreach ($pattern in $IgnoreFiles) { + if ($f.Name -like $pattern) { + $reason = "filename '$($f.Name)' matches ignore pattern '$pattern'" + break + } + } + } + + # Size check + if (-not $reason -and $MaxFileSize -gt 0 -and $f.Length -gt $MaxFileSize) { + $reason = "exceeds maxFileSize ($MaxFileSize bytes)" + } + + if ($reason) { + Write-Verbose "EXCLUDING: $($f.FullName) → $reason" + } else { + Write-Verbose "INCLUDING: $($f.FullName)" + $result.Add($f) + } + } + + return $result +} + +function Build-ClipboardContent { + [CmdletBinding()] + Param( + [Parameter(Mandatory)] + [System.Collections.Generic.List[System.IO.FileInfo]] $Files, + + [Parameter()] + [hashtable] $Replacements + ) + $sb = [System.Text.StringBuilder]::new() + foreach ($f in $Files) { + $sb.AppendLine("File: $($f.FullName)") | Out-Null + $sb.AppendLine(('-' * 60)) | Out-Null + $text = Get-Content -Raw -LiteralPath $f.FullName -ErrorAction Stop + + foreach ($token in $Replacements.Keys) { + $val = $Replacements[$token] + $text = $text -replace ([Regex]::Escape($token)), $val + } + + $sb.AppendLine($text) | Out-Null + $sb.AppendLine() | Out-Null + } + return $sb.ToString() +} + +function Copy-ToClipboard { + [CmdletBinding()] + Param( + [Parameter(Mandatory)] + [string] $Content, + + [Parameter()] + [switch] $ShowList, + + [Parameter()] + [System.Collections.Generic.List[System.IO.FileInfo]] $Files + ) + # Pass the entire string as a single Value, rather than via the pipeline + Set-Clipboard -Value $Content + + Write-Host "✅ Copied $($Files.Count) file(s) to clipboard." + if ($ShowList) { + Write-Host "`nFiles included:" + foreach ($f in $Files) { + Write-Host " - $($f.FullName)" + } + } +} + +#–– Begin script logic –– + +if (-not $PSBoundParameters.ContainsKey('ConfigFile')) { + # Are we running from a script file? + if ($MyInvocation.MyCommand.Path) { + # Use the folder the script lives in + $scriptFolder = Split-Path -Parent $MyInvocation.MyCommand.Path + } + else { + # Interactive / dot-sourced: use the cwd + $scriptFolder = (Get-Location).ProviderPath + } + + $ConfigFile = Join-Path $scriptFolder 'config.json' +} + + +# Load and merge config +$config = Get-Config -ConfigFilePath $ConfigFile + +# Merge CLI parameters over config values +$rp = if ($PSBoundParameters.ContainsKey('RepoPath')) { $RepoPath } else { $config.repoPath } +$mf = if ($PSBoundParameters.ContainsKey('MaxFileSize')) { $MaxFileSize } else { [long]$config.maxFileSize } +$if = if ($PSBoundParameters.ContainsKey('IgnoreFolders') -and $IgnoreFolders) { $IgnoreFolders } else { @($config.ignoreFolders) } +$ifl = if ($PSBoundParameters.ContainsKey('IgnoreFiles') -and $IgnoreFiles) { $IgnoreFiles } else { @($config.ignoreFiles) } +$rep = if ($PSBoundParameters.ContainsKey('Replacements')) { $Replacements } else { + $h = @{}; foreach ($p in $config.replacements.PSObject.Properties) { $h[$p.Name] = $p.Value }; $h +} +$scf = if ($PSBoundParameters.ContainsKey('ShowCopiedFiles')) { + $ShowCopiedFiles.IsPresent + } else { + [bool]$config.showCopiedFiles + } + +if ($null -eq $if) { $if = @() } +if ($null -eq $ifl) { $ifl = @() } + +# Gather, filter, and log +$filesToCopy = Get-FilesToInclude ` + -RepoRoot $rp ` + -IgnoreFolders $if ` + -IgnoreFiles $ifl ` + -MaxFileSize $mf + + +if ($filesToCopy.Count -eq 0) { + Write-Warning 'No files passed the filters; nothing to copy.' + return +} + +# Build content & copy +$content = Build-ClipboardContent -Files $filesToCopy -Replacements $rep +Copy-ToClipboard -Content $content -ShowList:$scf -Files $filesToCopy diff --git a/tests/bash/rpcp.bats b/tests/bash/rpcp.bats index 2421898..6bef195 100644 --- a/tests/bash/rpcp.bats +++ b/tests/bash/rpcp.bats @@ -1,111 +1,111 @@ -#!/usr/bin/env bats -# -# End-to-end tests for the Bash version of repocopy (rpcp.sh) -# ───────────────────────────────────────────────────────────── -# • Spins-up a temp repo each run (safe & hermetic) -# • Stubs xclip/pbcopy so we can inspect what hits the clipboard -# • Verifies: -# 1. happy-path copy & token replacement -# 2. max-file-size exclusion -# 3. override of max-file-size via CLI -# 4. folder-ignore pattern ("build") -# 5. behaviour when --show-copied-files is used -# -export BATS_LIB_PATH="$PWD/tests/test_helper" -load 'test_helper/bats-support/load' -load 'test_helper/bats-assert/load' - -setup() { - # ── ① disposable sandbox repo ────────────────────────────── - TMP_REPO="$(mktemp -d)" - mkdir -p "$TMP_REPO/src" "$TMP_REPO/build" - - # source files - printf 'hello ClientName\n' >"$TMP_REPO/src/include.txt" - printf 'ignore me\n' >"$TMP_REPO/manifest.json" - head -c 10 </dev/urandom >"$TMP_REPO/image.png" - - # a 300-KiB file to test the size filter - dd if=/dev/zero of="$TMP_REPO/src/big.bin" bs=1k count=300 2>/dev/null - - # something inside an ignored folder - printf 'ignore me\n' >"$TMP_REPO/build/output.txt" - - # config.json that matches the Pester suite - cat >"$TMP_REPO/config.json" <<'JSON' -{ - "repoPath": ".", - "maxFileSize": 204800, - "ignoreFolders": [ "build" ], - "ignoreFiles": [ "manifest.json", "*.png", "config.json" ], - "replacements": { "ClientName": "Bob" }, - "showCopiedFiles": false, - "autoInstallDeps": false -} -JSON - - # ── ② stub clipboard (xclip) ─────────────────────────────── - CLIP_FILE="$(mktemp)" - STUB_DIR="$(mktemp -d)" - cat >"$STUB_DIR/xclip" <<STUB -#!/usr/bin/env bash -cat > "$CLIP_FILE" -STUB - chmod +x "$STUB_DIR/xclip" - PATH="$STUB_DIR:$PATH" -} - -teardown() { - rm -rf "$TMP_REPO" "$CLIP_FILE" "$STUB_DIR" -} - -# helper to run rpcp.sh and slurp clipboard into $CLIP variable -run_rpcp() { - run bash ./rpcp.sh "$@" - # copy the clipboard text into a shell variable for assertions - CLIP="$(cat "$CLIP_FILE")" -} - -# ───────────────────────────────────────────────────────────── -@test "default run: copies only permitted files & replaces tokens" { - run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" - - assert_success - assert_line --partial "✅ Copied" # sanity - - assert_regex "$CLIP" "include\\.txt" - assert_regex "$CLIP" "hello Bob" - - refute_regex "$CLIP" "manifest\\.json" - refute_regex "$CLIP" "image\\.png" - refute_regex "$CLIP" "config\\.json" - refute_regex "$CLIP" "output\\.txt" - refute_regex "$CLIP" "big\\.bin" -} - -@test "size filter: big.bin is excluded by default" { - run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" - refute_regex "$CLIP" "big\\.bin" -} - -@test "size override: big.bin appears when --max-file-size 0 is used" { - run_rpcp --repo-path "$TMP_REPO" \ - --config-file "$TMP_REPO/config.json" \ - --max-file-size 0 - assert_regex "$CLIP" "big\\.bin" -} - -@test "folder ignore: anything under build/ is skipped" { - run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" - refute_regex "$CLIP" "build/output\\.txt" -} - -@test "--show-copied-files does not affect clipboard content" { - run_rpcp --repo-path "$TMP_REPO" \ - --config-file "$TMP_REPO/config.json" \ - --show-copied-files - - # The script prints the file list to stdout; - # we just need to ensure normal data is still on the clipboard - assert_regex "$CLIP" "include\\.txt" -} +#!/usr/bin/env bats +# +# End-to-end tests for the Bash version of repocopy (rpcp.sh) +# ───────────────────────────────────────────────────────────── +# • Spins-up a temp repo each run (safe & hermetic) +# • Stubs xclip/pbcopy so we can inspect what hits the clipboard +# • Verifies: +# 1. happy-path copy & token replacement +# 2. max-file-size exclusion +# 3. override of max-file-size via CLI +# 4. folder-ignore pattern ("build") +# 5. behaviour when --show-copied-files is used +# +export BATS_LIB_PATH="$PWD/tests/test_helper" +load 'test_helper/bats-support/load' +load 'test_helper/bats-assert/load' + +setup() { + # ── ① disposable sandbox repo ────────────────────────────── + TMP_REPO="$(mktemp -d)" + mkdir -p "$TMP_REPO/src" "$TMP_REPO/build" + + # source files + printf 'hello ClientName\n' >"$TMP_REPO/src/include.txt" + printf 'ignore me\n' >"$TMP_REPO/manifest.json" + head -c 10 </dev/urandom >"$TMP_REPO/image.png" + + # a 300-KiB file to test the size filter + dd if=/dev/zero of="$TMP_REPO/src/big.bin" bs=1k count=300 2>/dev/null + + # something inside an ignored folder + printf 'ignore me\n' >"$TMP_REPO/build/output.txt" + + # config.json that matches the Pester suite + cat >"$TMP_REPO/config.json" <<'JSON' +{ + "repoPath": ".", + "maxFileSize": 204800, + "ignoreFolders": [ "build" ], + "ignoreFiles": [ "manifest.json", "*.png", "config.json" ], + "replacements": { "ClientName": "Bob" }, + "showCopiedFiles": false, + "autoInstallDeps": false +} +JSON + + # ── ② stub clipboard (xclip) ─────────────────────────────── + CLIP_FILE="$(mktemp)" + STUB_DIR="$(mktemp -d)" + cat >"$STUB_DIR/xclip" <<STUB +#!/usr/bin/env bash +cat > "$CLIP_FILE" +STUB + chmod +x "$STUB_DIR/xclip" + PATH="$STUB_DIR:$PATH" +} + +teardown() { + rm -rf "$TMP_REPO" "$CLIP_FILE" "$STUB_DIR" +} + +# helper to run rpcp.sh and slurp clipboard into $CLIP variable +run_rpcp() { + run bash ./rpcp.sh "$@" + # copy the clipboard text into a shell variable for assertions + CLIP="$(cat "$CLIP_FILE")" +} + +# ───────────────────────────────────────────────────────────── +@test "default run: copies only permitted files & replaces tokens" { + run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" + + assert_success + assert_line --partial "✅ Copied" # sanity + + assert_regex "$CLIP" "include\\.txt" + assert_regex "$CLIP" "hello Bob" + + refute_regex "$CLIP" "manifest\\.json" + refute_regex "$CLIP" "image\\.png" + refute_regex "$CLIP" "config\\.json" + refute_regex "$CLIP" "output\\.txt" + refute_regex "$CLIP" "big\\.bin" +} + +@test "size filter: big.bin is excluded by default" { + run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" + refute_regex "$CLIP" "big\\.bin" +} + +@test "size override: big.bin appears when --max-file-size 0 is used" { + run_rpcp --repo-path "$TMP_REPO" \ + --config-file "$TMP_REPO/config.json" \ + --max-file-size 0 + assert_regex "$CLIP" "big\\.bin" +} + +@test "folder ignore: anything under build/ is skipped" { + run_rpcp --repo-path "$TMP_REPO" --config-file "$TMP_REPO/config.json" + refute_regex "$CLIP" "build/output\\.txt" +} + +@test "--show-copied-files does not affect clipboard content" { + run_rpcp --repo-path "$TMP_REPO" \ + --config-file "$TMP_REPO/config.json" \ + --show-copied-files + + # The script prints the file list to stdout; + # we just need to ensure normal data is still on the clipboard + assert_regex "$CLIP" "include\\.txt" +} diff --git a/tests/fixtures/sample-repo/build/output.txt b/tests/fixtures/sample-repo/build/output.txt index f709266..592fd25 100644 --- a/tests/fixtures/sample-repo/build/output.txt +++ b/tests/fixtures/sample-repo/build/output.txt @@ -1 +1 @@ -ignore me +ignore me diff --git a/tests/fixtures/sample-repo/config.json b/tests/fixtures/sample-repo/config.json index c891671..e3e4a22 100644 --- a/tests/fixtures/sample-repo/config.json +++ b/tests/fixtures/sample-repo/config.json @@ -1,8 +1,8 @@ -{ - "repoPath": ".", - "maxFileSize": 204800, - "ignoreFolders": [ "build" ], - "ignoreFiles" : [ "manifest.json", "*.png", "config.json" ], - "replacements" : { "ClientName": "Bob" }, - "showCopiedFiles": false -} +{ + "repoPath": ".", + "maxFileSize": 204800, + "ignoreFolders": [ "build" ], + "ignoreFiles" : [ "manifest.json", "*.png", "config.json" ], + "replacements" : { "ClientName": "Bob" }, + "showCopiedFiles": false +} diff --git a/tests/fixtures/sample-repo/manifest.json b/tests/fixtures/sample-repo/manifest.json index c1c52bb..d9dcd53 100644 --- a/tests/fixtures/sample-repo/manifest.json +++ b/tests/fixtures/sample-repo/manifest.json @@ -1 +1 @@ -{ "note": "this file should be ignored by rpcp" } +{ "note": "this file should be ignored by rpcp" } diff --git a/tests/fixtures/sample-repo/src/include.txt b/tests/fixtures/sample-repo/src/include.txt index cc9d96b..bacd588 100644 --- a/tests/fixtures/sample-repo/src/include.txt +++ b/tests/fixtures/sample-repo/src/include.txt @@ -1 +1 @@ -hello Bob +hello Bob diff --git a/tests/powershell/rpcp.tests.ps1 b/tests/powershell/rpcp.tests.ps1 index 8c876e3..a7b5193 100644 --- a/tests/powershell/rpcp.tests.ps1 +++ b/tests/powershell/rpcp.tests.ps1 @@ -1,146 +1,146 @@ -# Pester 5 tests for repocopy (PowerShell edition) -# ────────────────────────────────────────────────────────────── -Import-Module Pester -ErrorAction Stop -Import-Module Microsoft.PowerShell.Management -Force # Set-Clipboard - -$ErrorActionPreference = 'Stop' - -Describe 'rpcp.ps1 end-to-end behaviour (fixture repo)' { - - # ────────────────────────────────────────────────────────── - # ❶ Shared one-time setup - # ────────────────────────────────────────────────────────── - BeforeAll { - $ProjectRoot = (Resolve-Path "$PSScriptRoot/../../").Path - $Script = Join-Path $ProjectRoot 'rpcp.ps1' - $FixtureRoot = Join-Path $ProjectRoot 'tests/fixtures/sample-repo' - - # -- ensure fixture tree exists (idempotent) ------------------------ - if (-not (Test-Path $FixtureRoot)) { - New-Item -ItemType Directory -Path "$FixtureRoot/src" -Force | Out-Null - } - - # minimal source file & image (re-create only if missing) - if (-not (Test-Path "$FixtureRoot/src/include.txt")) { - 'hello ClientName' | Set-Content "$FixtureRoot/src/include.txt" - } - if (-not (Test-Path "$FixtureRoot/manifest.json")) { - '{ "note":"ignore" }' | Set-Content "$FixtureRoot/manifest.json" - } - if (-not (Test-Path "$FixtureRoot/image.png")) { - [IO.File]::WriteAllBytes("$FixtureRoot/image.png",[byte[]](0..9)) - } - @' -{ - "repoPath": ".", - "maxFileSize": 204800, - "ignoreFolders": [ "build" ], - "ignoreFiles" : [ "manifest.json", "*.png", "config.json" ], - "replacements" : { "ClientName": "Bob" }, - "showCopiedFiles": false -} -'@ | Set-Content "$FixtureRoot/config.json" - # -- extra files for later tests ----------------------------------- - if (-not (Test-Path "$FixtureRoot/src/big.bin")) { - $size = 300kb # 300 KiB > maxFileSize - $bytes = New-Object byte[] $size - [System.Random]::new().NextBytes($bytes) - [IO.File]::WriteAllBytes("$FixtureRoot/src/big.bin", $bytes) - } - - if (-not (Test-Path "$FixtureRoot/build")) { - New-Item -ItemType Directory -Path "$FixtureRoot/build" | Out-Null - 'ignore me' | Set-Content "$FixtureRoot/build/output.txt" - } - - # -- helper: run rpcp & capture what it puts on the clipboard ------- - # ── helper: run rpcp & capture what it puts on the clipboard ───────────── -function Invoke-Rpcp { - [CmdletBinding()] - param( - [Parameter(Mandatory)] - [hashtable]$Param - ) - - Mock Set-Clipboard { - param($Value) - # flatten any array → single string so tests are simpler - Set-Variable -Name CapturedClipboard ` - -Value ($Value -join "`n") ` - -Scope Global - } - - & $Script @Param | Out-Null - - # copy-out *before* we purge the global - $result = $CapturedClipboard - Remove-Variable -Name CapturedClipboard -Scope Global -ErrorAction SilentlyContinue - return $result -} - - } - - # ────────────────────────────────────────────────────────── - Context 'Default run (config.json only)' { - It 'copies only permitted files and performs replacements' { - $copied = Invoke-Rpcp -Param @{ - RepoPath = $FixtureRoot - ConfigFile = "$FixtureRoot/config.json" - } - - $copied | Should -Match 'include\.txt' - $copied | Should -Match 'hello Bob' - - $copied | Should -Not -Match 'manifest\.json' - $copied | Should -Not -Match 'image\.png' - $copied | Should -Not -Match 'config\.json' - $copied | Should -Not -Match 'output\.txt' - $copied | Should -Not -Match 'big\.bin' - } - } - - Context 'Max file-size filter' { - It 'excludes files bigger than maxFileSize' { - $copied = Invoke-Rpcp -Param @{ - RepoPath = $FixtureRoot - ConfigFile = "$FixtureRoot/config.json" # 204 KB limit - } - - $copied | Should -Not -Match 'big\.bin' - } - - It 'includes big file when CLI overrides maxFileSize' { - $copied = Invoke-Rpcp -Param @{ - RepoPath = $FixtureRoot - ConfigFile = "$FixtureRoot/config.json" - MaxFileSize = 0 # disable size filtering - } - - $copied | Should -Match 'big\.bin' - } - } - - Context 'Folder ignore pattern' { - It 'skips anything inside folders named "build"' { - $copied = Invoke-Rpcp -Param @{ - RepoPath = $FixtureRoot - ConfigFile = "$FixtureRoot/config.json" - } - - $pattern = [regex]::Escape('build/output.txt') - $copied | Should -Not -Match $pattern - } - } - - Context 'ShowCopiedFiles switch' { - It 'still copies correctly when -ShowCopiedFiles is used' { - $copied = Invoke-Rpcp -Param @{ - RepoPath = $FixtureRoot - ConfigFile = "$FixtureRoot/config.json" - ShowCopiedFiles = $true - } - - $copied | Should -Match 'include\.txt' # sanity check - } - } -} +# Pester 5 tests for repocopy (PowerShell edition) +# ────────────────────────────────────────────────────────────── +Import-Module Pester -ErrorAction Stop +Import-Module Microsoft.PowerShell.Management -Force # Set-Clipboard + +$ErrorActionPreference = 'Stop' + +Describe 'rpcp.ps1 end-to-end behaviour (fixture repo)' { + + # ────────────────────────────────────────────────────────── + # ❶ Shared one-time setup + # ────────────────────────────────────────────────────────── + BeforeAll { + $ProjectRoot = (Resolve-Path "$PSScriptRoot/../../").Path + $Script = Join-Path $ProjectRoot 'rpcp.ps1' + $FixtureRoot = Join-Path $ProjectRoot 'tests/fixtures/sample-repo' + + # -- ensure fixture tree exists (idempotent) ------------------------ + if (-not (Test-Path $FixtureRoot)) { + New-Item -ItemType Directory -Path "$FixtureRoot/src" -Force | Out-Null + } + + # minimal source file & image (re-create only if missing) + if (-not (Test-Path "$FixtureRoot/src/include.txt")) { + 'hello ClientName' | Set-Content "$FixtureRoot/src/include.txt" + } + if (-not (Test-Path "$FixtureRoot/manifest.json")) { + '{ "note":"ignore" }' | Set-Content "$FixtureRoot/manifest.json" + } + if (-not (Test-Path "$FixtureRoot/image.png")) { + [IO.File]::WriteAllBytes("$FixtureRoot/image.png",[byte[]](0..9)) + } + @' +{ + "repoPath": ".", + "maxFileSize": 204800, + "ignoreFolders": [ "build" ], + "ignoreFiles" : [ "manifest.json", "*.png", "config.json" ], + "replacements" : { "ClientName": "Bob" }, + "showCopiedFiles": false +} +'@ | Set-Content "$FixtureRoot/config.json" + # -- extra files for later tests ----------------------------------- + if (-not (Test-Path "$FixtureRoot/src/big.bin")) { + $size = 300kb # 300 KiB > maxFileSize + $bytes = New-Object byte[] $size + [System.Random]::new().NextBytes($bytes) + [IO.File]::WriteAllBytes("$FixtureRoot/src/big.bin", $bytes) + } + + if (-not (Test-Path "$FixtureRoot/build")) { + New-Item -ItemType Directory -Path "$FixtureRoot/build" | Out-Null + 'ignore me' | Set-Content "$FixtureRoot/build/output.txt" + } + + # -- helper: run rpcp & capture what it puts on the clipboard ------- + # ── helper: run rpcp & capture what it puts on the clipboard ───────────── +function Invoke-Rpcp { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [hashtable]$Param + ) + + Mock Set-Clipboard { + param($Value) + # flatten any array → single string so tests are simpler + Set-Variable -Name CapturedClipboard ` + -Value ($Value -join "`n") ` + -Scope Global + } + + & $Script @Param | Out-Null + + # copy-out *before* we purge the global + $result = $CapturedClipboard + Remove-Variable -Name CapturedClipboard -Scope Global -ErrorAction SilentlyContinue + return $result +} + + } + + # ────────────────────────────────────────────────────────── + Context 'Default run (config.json only)' { + It 'copies only permitted files and performs replacements' { + $copied = Invoke-Rpcp -Param @{ + RepoPath = $FixtureRoot + ConfigFile = "$FixtureRoot/config.json" + } + + $copied | Should -Match 'include\.txt' + $copied | Should -Match 'hello Bob' + + $copied | Should -Not -Match 'manifest\.json' + $copied | Should -Not -Match 'image\.png' + $copied | Should -Not -Match 'config\.json' + $copied | Should -Not -Match 'output\.txt' + $copied | Should -Not -Match 'big\.bin' + } + } + + Context 'Max file-size filter' { + It 'excludes files bigger than maxFileSize' { + $copied = Invoke-Rpcp -Param @{ + RepoPath = $FixtureRoot + ConfigFile = "$FixtureRoot/config.json" # 204 KB limit + } + + $copied | Should -Not -Match 'big\.bin' + } + + It 'includes big file when CLI overrides maxFileSize' { + $copied = Invoke-Rpcp -Param @{ + RepoPath = $FixtureRoot + ConfigFile = "$FixtureRoot/config.json" + MaxFileSize = 0 # disable size filtering + } + + $copied | Should -Match 'big\.bin' + } + } + + Context 'Folder ignore pattern' { + It 'skips anything inside folders named "build"' { + $copied = Invoke-Rpcp -Param @{ + RepoPath = $FixtureRoot + ConfigFile = "$FixtureRoot/config.json" + } + + $pattern = [regex]::Escape('build/output.txt') + $copied | Should -Not -Match $pattern + } + } + + Context 'ShowCopiedFiles switch' { + It 'still copies correctly when -ShowCopiedFiles is used' { + $copied = Invoke-Rpcp -Param @{ + RepoPath = $FixtureRoot + ConfigFile = "$FixtureRoot/config.json" + ShowCopiedFiles = $true + } + + $copied | Should -Match 'include\.txt' # sanity check + } + } +}