├── .github
├── dependabot.yaml
└── workflows
│ ├── scorecard.yaml
│ └── tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── load.bash
├── package.json
├── src
├── error.bash
├── lang.bash
└── output.bash
└── test
├── 50-output-10-batslib_err.bats
├── 50-output-11-batslib_count_lines.bats
├── 50-output-12-batslib_is_single_line.bats
├── 50-output-13-batslib_get_max_single_line_key_width.bats
├── 50-output-14-batslib_print_kv_single.bats
├── 50-output-15-batslib_print_kv_multi.bats
├── 50-output-16-batslib_print_kv_single_or_multi.bats
├── 50-output-17-batslib_prefix.bats
├── 50-output-18-batslib_mark.bats
├── 50-output-19-batslib_decorate.bats
├── 51-error-10-fail.bats
├── 52-lang-10-batslib_is_caller.bats
├── cat
└── test_helper.bash
/.github/dependabot.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | target-branch: master
8 |
--------------------------------------------------------------------------------
/.github/workflows/scorecard.yaml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are provided
2 | # by a third-party and are governed by separate terms of service, privacy
3 | # policy, and support documentation.
4 |
5 | name: Scorecard supply-chain security
6 | on:
7 | # For Branch-Protection check. Only the default branch is supported. See
8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9 | branch_protection_rule:
10 | # To guarantee Maintained check is occasionally updated. See
11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12 | schedule:
13 | - cron: '44 10 * * 6'
14 | push:
15 | branches: [ "master" ]
16 |
17 | # Declare default permissions as read only.
18 | permissions: read-all
19 |
20 | jobs:
21 | analysis:
22 | name: Scorecard analysis
23 | runs-on: ubuntu-latest
24 | permissions:
25 | # Needed to upload the results to code-scanning dashboard.
26 | security-events: write
27 | # Needed to publish results and get a badge (see publish_results below).
28 | id-token: write
29 | # Uncomment the permissions below if installing in a private repository.
30 | # contents: read
31 | # actions: read
32 |
33 | steps:
34 | - name: "Checkout code"
35 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
36 | with:
37 | persist-credentials: false
38 |
39 | - name: "Run analysis"
40 | uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
41 | with:
42 | results_file: results.sarif
43 | results_format: sarif
44 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
45 | # - you want to enable the Branch-Protection check on a *public* repository, or
46 | # - you are installing Scorecard on a *private* repository
47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
48 | # repo_token: ${{ secrets.SCORECARD_TOKEN }}
49 |
50 | # Public repositories:
51 | # - Publish results to OpenSSF REST API for easy access by consumers
52 | # - Allows the repository to include the Scorecard badge.
53 | # - See https://github.com/ossf/scorecard-action#publishing-results.
54 | # For private repositories:
55 | # - `publish_results` will always be set to `false`, regardless
56 | # of the value entered here.
57 | publish_results: true
58 |
59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
60 | # format to the repository Actions tab.
61 | - name: "Upload artifact"
62 | uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
63 | with:
64 | name: SARIF file
65 | path: results.sarif
66 | retention-days: 5
67 |
68 | # Upload the results to GitHub's code scanning dashboard.
69 | - name: "Upload to code-scanning"
70 | uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7
71 | with:
72 | sarif_file: results.sarif
73 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | # 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
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Tests
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2
19 | - uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2
20 | - uses: actions/cache@8492260343ad570701412c2f464a5877dc76bace # v2
21 | with:
22 | path: ~/.npm
23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
24 | restore-keys: |
25 | ${{ runner.os }}-node-
26 | - run: npm install
27 | - run: npm test
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /package-lock.json
3 | /yarn.lock
4 | /bats-support-*.tgz
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file.
4 | This project adheres to [Semantic Versioning](http://semver.org/).
5 |
6 |
7 | ## [0.3.0] - 2016-11-29
8 |
9 | ### Added
10 |
11 | - Restricting invocation to specific locations with
12 | `batslib_is_caller()`
13 |
14 |
15 | ## [0.2.0] - 2016-03-22
16 |
17 | ### Added
18 |
19 | - `npm` support
20 | - Reporting arbitrary failures with `fail()` (moved from `bats-assert`)
21 |
22 | ### Changed
23 |
24 | - Library renamed to `bats-support`
25 |
26 |
27 | ## 0.1.0 - 2016-02-16
28 |
29 | ### Added
30 |
31 | - Two-column key-value formatting with `batslib_print_kv_single()`
32 | - Multi-line key-value formatting with `batslib_print_kv_multi()`
33 | - Mixed formatting with `batslib_print_kv_single_or_multi()`
34 | - Header and footer decoration with `batslib_decorate()`
35 | - Prefixing lines with `batslib_prefix()`
36 | - Marking lines with `batslib_mark()`
37 | - Common output function `batslib_err()`
38 | - Line counting with `batslib_count_lines()`
39 | - Checking whether a text is one line long with
40 | `batslib_is_single_line()`
41 | - Determining key width for two-column and mixed formatting with
42 | `batslib_get_max_single_line_key_width()`
43 |
44 |
45 | [0.3.0]: https://github.com/ztombol/bats-support/compare/v0.2.0...v0.3.0
46 | [0.2.0]: https://github.com/ztombol/bats-support/compare/v0.1.0...v0.2.0
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bats-support
2 |
3 | [](https://raw.githubusercontent.com/bats-core/bats-support/master/LICENSE)
4 | [](https://github.com/bats-core/bats-support/releases/latest)
5 | [](https://github.com/bats-core/bats-support/actions?query=workflow%3ATests)
6 |
7 |
8 | `bats-support` is a supporting library providing common functions to
9 | test helper libraries written for [Bats][bats].
10 |
11 | Features:
12 | - [error reporting](#error-reporting)
13 | - [output formatting](#output-formatting)
14 | - [language tools](#language-and-execution)
15 |
16 | See the [shared documentation][bats-docs] to learn how to install and
17 | load this library.
18 |
19 | If you want to use this library in your own helpers or just want to
20 | learn about its internals see the developer documentation in the [source
21 | files](src).
22 |
23 |
24 | ## Error reporting
25 |
26 | ### `fail`
27 |
28 | Display an error message and fail. This function provides a convenient
29 | way to report failure in arbitrary situations. You can use it to
30 | implement your own helpers when the ones available do not meet your
31 | needs. Other functions use it internally as well.
32 |
33 | ```bash
34 | @test 'fail()' {
35 | fail 'this test always fails'
36 | }
37 | ```
38 |
39 | The message can also be specified on the standard input.
40 |
41 | ```bash
42 | @test 'fail() with pipe' {
43 | echo 'this test always fails' | fail
44 | }
45 | ```
46 |
47 | This function always fails and simply outputs the given message.
48 |
49 | ```
50 | this test always fails
51 | ```
52 |
53 |
54 | ## Output formatting
55 |
56 | Many test helpers need to produce human readable output. This library
57 | provides a simple way to format simple messages and key value pairs, and
58 | display them on the standard error.
59 |
60 |
61 | ### Simple message
62 |
63 | Simple messages without structure, e.g. one-line error messages, are
64 | simply wrapped in a header and a footer to help them stand out.
65 |
66 | ```
67 | -- ERROR: assert_output --
68 | `--partial' and `--regexp' are mutually exclusive
69 | --
70 | ```
71 |
72 |
73 | ### Key-Value pairs
74 |
75 | Some helpers, e.g. [assertions][bats-assert], structure output as
76 | key-value pairs. This library provides two ways to format them.
77 |
78 | When the value is one line long, a pair can be displayed in a columnar
79 | fashion called ***two-column*** format.
80 |
81 | ```
82 | -- output differs --
83 | expected : want
84 | actual : have
85 | --
86 | ```
87 |
88 | When the value is longer than one line, the key and value must be
89 | displayed on separate lines. First, the key is displayed along with the
90 | number of lines in the value. Then, the value, indented by two spaces
91 | for added readability, starting on the next line. This is called
92 | ***multi-line*** format.
93 |
94 | ```
95 | -- command failed --
96 | status : 1
97 | output (2 lines):
98 | Error! Something went terribly wrong!
99 | Our engineers are panicing... \`>`;/
100 | --
101 | ```
102 |
103 | Sometimes, for clarity, it is a good idea to display related values also
104 | in this format, even if they are just one line long.
105 |
106 | ```
107 | -- output differs --
108 | expected (1 lines):
109 | want
110 | actual (3 lines):
111 | have 1
112 | have 2
113 | have 3
114 | --
115 | ```
116 |
117 | ## Language and Execution
118 |
119 | ### Restricting invocation to specific locations
120 |
121 | Sometimes a helper may work properly only when called from a certain
122 | location. Because it depends on variables to be set or some other side
123 | effect.
124 |
125 | A good example is cleaning up temporary files only if the test has
126 | succeeded. The outcome of a test is only available in `teardown`. Thus,
127 | to avoid programming mistakes, it makes sense to restrict such a
128 | clean-up helper to that function.
129 |
130 | `batslib_is_caller` checks the call stack and returns `0` if the caller
131 | was invoked from a given function, and `1` otherwise. This function
132 | becomes really useful with the `--indirect` option, which allows calls
133 | through intermediate functions, e.g. the calling function may be called
134 | from a function that was called from the given function.
135 |
136 | Staying with the example above, the following code snippet implements a
137 | helper that is restricted to `teardown` or any function called
138 | indirectly from it.
139 |
140 | ```shell
141 | clean_up() {
142 | # Check caller.
143 | if batslib_is_caller --indirect 'teardown'; then
144 | echo "Must be called from \`teardown'" \
145 | | batslib_decorate 'ERROR: clean_up' \
146 | | fail
147 | return $?
148 | fi
149 |
150 | # Body goes here...
151 | }
152 | ```
153 |
154 | In some cases a helper may be called from multiple locations. For
155 | example, a logging function that uses the test name, description or
156 | number, information only available in `setup`, `@test` or `teardown`, to
157 | distinguish entries. The following snippet implements this restriction.
158 |
159 | ```shell
160 | log_test() {
161 | # Check caller.
162 | if ! ( batslib_is_caller --indirect 'setup' \
163 | || batslib_is_caller --indirect "$BATS_TEST_NAME" \
164 | || batslib_is_caller --indirect 'teardown' )
165 | then
166 | echo "Must be called from \`setup', \`@test' or \`teardown'" \
167 | | batslib_decorate 'ERROR: log_test' \
168 | | fail
169 | return $?
170 | fi
171 |
172 | # Body goes here...
173 | }
174 | ```
175 |
176 |
177 |
178 |
179 | [bats]: https://github.com/bats-core/bats-core
180 | [bats-docs]: https://github.com/bats-core/bats-docs
181 | [bats-assert]: https://github.com/bats-core/bats-assert
182 |
--------------------------------------------------------------------------------
/load.bash:
--------------------------------------------------------------------------------
1 | # Preserve path at the time this file was sourced
2 | # This prevents using of user-defined mocks/stubs that modify the PATH
3 |
4 | # BATS_SAVED_PATH was introduced in bats-core v1.10.0
5 | # if it is already set, we can use its more robust value
6 | # else we try to recreate it here
7 | BATS_SAVED_PATH="${BATS_SAVED_PATH-$PATH}"
8 |
9 | source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash"
10 | source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash"
11 | source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash"
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bats-support",
3 | "version": "0.3.0",
4 | "description": "Supporting library for Bats test helpers",
5 | "homepage": "https://github.com/jasonkarns/bats-support",
6 | "license": "CC0-1.0",
7 | "author": "Zoltán Tömböl (https://github.com/ztombol)",
8 | "contributors": [
9 | "Jason Karns (http://jason.karns.name)"
10 | ],
11 | "repository": "github:jasonkarns/bats-support",
12 | "bugs": "https://github.com/jasonkarns/bats-support/issues",
13 | "directories": {
14 | "lib": "src",
15 | "test": "test"
16 | },
17 | "files": [
18 | "load.bash",
19 | "src"
20 | ],
21 | "scripts": {
22 | "test": "bats ${CI+-t} test"
23 | },
24 | "devDependencies": {
25 | "bats": "^1"
26 | },
27 | "peerDependencies": {
28 | "bats": "0.4 || ^1"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/error.bash:
--------------------------------------------------------------------------------
1 | #
2 | # bats-support - Supporting library for Bats test helpers
3 | #
4 | # Written in 2016 by Zoltan Tombol
5 | #
6 | # To the extent possible under law, the author(s) have dedicated all
7 | # copyright and related and neighboring rights to this software to the
8 | # public domain worldwide. This software is distributed without any
9 | # warranty.
10 | #
11 | # You should have received a copy of the CC0 Public Domain Dedication
12 | # along with this software. If not, see
13 | # .
14 | #
15 |
16 | #
17 | # error.bash
18 | # ----------
19 | #
20 | # Functions implementing error reporting. Used by public helper
21 | # functions or test suits directly.
22 | #
23 |
24 | # Fail and display a message. When no parameters are specified, the
25 | # message is read from the standard input. Other functions use this to
26 | # report failure.
27 | #
28 | # Globals:
29 | # none
30 | # Arguments:
31 | # $@ - [=STDIN] message
32 | # Returns:
33 | # 1 - always
34 | # Inputs:
35 | # STDIN - [=$@] message
36 | # Outputs:
37 | # STDERR - message
38 | fail() {
39 | (( $# == 0 )) && batslib_err || batslib_err "$@"
40 | return 1
41 | }
42 |
--------------------------------------------------------------------------------
/src/lang.bash:
--------------------------------------------------------------------------------
1 | #
2 | # bats-util - Various auxiliary functions for Bats
3 | #
4 | # Written in 2016 by Zoltan Tombol
5 | #
6 | # To the extent possible under law, the author(s) have dedicated all
7 | # copyright and related and neighboring rights to this software to the
8 | # public domain worldwide. This software is distributed without any
9 | # warranty.
10 | #
11 | # You should have received a copy of the CC0 Public Domain Dedication
12 | # along with this software. If not, see
13 | # .
14 | #
15 |
16 | #
17 | # lang.bash
18 | # ---------
19 | #
20 | # Bash language and execution related functions. Used by public helper
21 | # functions.
22 | #
23 |
24 | # Check whether the calling function was called from a given function.
25 | #
26 | # By default, direct invocation is checked. The function succeeds if the
27 | # calling function was called directly from the given function. In other
28 | # words, if the given function is the next element on the call stack.
29 | #
30 | # When `--indirect' is specified, indirect invocation is checked. The
31 | # function succeeds if the calling function was called from the given
32 | # function with any number of intermediate calls. In other words, if the
33 | # given function can be found somewhere on the call stack.
34 | #
35 | # Direct invocation is a form of indirect invocation with zero
36 | # intermediate calls.
37 | #
38 | # Globals:
39 | # FUNCNAME
40 | # Options:
41 | # -i, --indirect - check indirect invocation
42 | # Arguments:
43 | # $1 - calling function's name
44 | # Returns:
45 | # 0 - current function was called from the given function
46 | # 1 - otherwise
47 | batslib_is_caller() {
48 | local -i is_mode_direct=1
49 |
50 | # Handle options.
51 | while (( $# > 0 )); do
52 | case "$1" in
53 | -i|--indirect) is_mode_direct=0; shift ;;
54 | --) shift; break ;;
55 | *) break ;;
56 | esac
57 | done
58 |
59 | # Arguments.
60 | local -r func="$1"
61 |
62 | # Check call stack.
63 | if (( is_mode_direct )); then
64 | [[ $func == "${FUNCNAME[2]}" ]] && return 0
65 | else
66 | local -i depth
67 | for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
68 | [[ $func == "${FUNCNAME[$depth]}" ]] && return 0
69 | done
70 | fi
71 |
72 | return 1
73 | }
74 |
--------------------------------------------------------------------------------
/src/output.bash:
--------------------------------------------------------------------------------
1 | #
2 | # bats-support - Supporting library for Bats test helpers
3 | #
4 | # Written in 2016 by Zoltan Tombol
5 | #
6 | # To the extent possible under law, the author(s) have dedicated all
7 | # copyright and related and neighboring rights to this software to the
8 | # public domain worldwide. This software is distributed without any
9 | # warranty.
10 | #
11 | # You should have received a copy of the CC0 Public Domain Dedication
12 | # along with this software. If not, see
13 | # .
14 | #
15 |
16 | #
17 | # output.bash
18 | # -----------
19 | #
20 | # Private functions implementing output formatting. Used by public
21 | # helper functions.
22 | #
23 |
24 | # Print a message to the standard error. When no parameters are
25 | # specified, the message is read from the standard input.
26 | #
27 | # Globals:
28 | # none
29 | # Arguments:
30 | # $@ - [=STDIN] message
31 | # Returns:
32 | # none
33 | # Inputs:
34 | # STDIN - [=$@] message
35 | # Outputs:
36 | # STDERR - message
37 | batslib_err() {
38 | { if (( $# > 0 )); then
39 | echo "$@"
40 | else
41 | PATH="$BATS_SAVED_PATH" command cat -
42 | fi
43 | } >&2
44 | }
45 |
46 | # Count the number of lines in the given string.
47 | #
48 | # TODO(ztombol): Fix tests and remove this note after #93 is resolved!
49 | # NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
50 | # give the same result as `${#lines[@]}' when the output contains
51 | # empty lines.
52 | # See PR #93 (https://github.com/sstephenson/bats/pull/93).
53 | #
54 | # Globals:
55 | # none
56 | # Arguments:
57 | # $1 - string
58 | # Returns:
59 | # none
60 | # Outputs:
61 | # STDOUT - number of lines
62 | batslib_count_lines() {
63 | local -i n_lines=0
64 | local line
65 | while IFS='' read -r line || [[ -n $line ]]; do
66 | (( ++n_lines ))
67 | done < <(printf '%s' "$1")
68 | echo "$n_lines"
69 | }
70 |
71 | # Determine whether all strings are single-line.
72 | #
73 | # Globals:
74 | # none
75 | # Arguments:
76 | # $@ - strings
77 | # Returns:
78 | # 0 - all strings are single-line
79 | # 1 - otherwise
80 | batslib_is_single_line() {
81 | for string in "$@"; do
82 | (( $(batslib_count_lines "$string") > 1 )) && return 1
83 | done
84 | return 0
85 | }
86 |
87 | # Determine the length of the longest key that has a single-line value.
88 | #
89 | # This function is useful in determining the correct width of the key
90 | # column in two-column format when some keys may have multi-line values
91 | # and thus should be excluded.
92 | #
93 | # Globals:
94 | # none
95 | # Arguments:
96 | # $odd - key
97 | # $even - value of the previous key
98 | # Returns:
99 | # none
100 | # Outputs:
101 | # STDOUT - length of longest key
102 | batslib_get_max_single_line_key_width() {
103 | local -i max_len=-1
104 | while (( $# != 0 )); do
105 | local -i key_len="${#1}"
106 | batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
107 | shift 2
108 | done
109 | echo "$max_len"
110 | }
111 |
112 | # Print key-value pairs in two-column format.
113 | #
114 | # Keys are displayed in the first column, and their corresponding values
115 | # in the second. To evenly line up values, the key column is fixed-width
116 | # and its width is specified with the first parameter (possibly computed
117 | # using `batslib_get_max_single_line_key_width').
118 | #
119 | # Globals:
120 | # none
121 | # Arguments:
122 | # $1 - width of key column
123 | # $even - key
124 | # $odd - value of the previous key
125 | # Returns:
126 | # none
127 | # Outputs:
128 | # STDOUT - formatted key-value pairs
129 | batslib_print_kv_single() {
130 | local -ir col_width="$1"; shift
131 | while (( $# != 0 )); do
132 | printf '%-*s : %s\n' "$col_width" "$1" "$2"
133 | shift 2
134 | done
135 | }
136 |
137 | # Print key-value pairs in multi-line format.
138 | #
139 | # The key is displayed first with the number of lines of its
140 | # corresponding value in parenthesis. Next, starting on the next line,
141 | # the value is displayed. For better readability, it is recommended to
142 | # indent values using `batslib_prefix'.
143 | #
144 | # Globals:
145 | # none
146 | # Arguments:
147 | # $odd - key
148 | # $even - value of the previous key
149 | # Returns:
150 | # none
151 | # Outputs:
152 | # STDOUT - formatted key-value pairs
153 | batslib_print_kv_multi() {
154 | while (( $# != 0 )); do
155 | printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
156 | printf '%s\n' "$2"
157 | shift 2
158 | done
159 | }
160 |
161 | # Print all key-value pairs in either two-column or multi-line format
162 | # depending on whether all values are single-line.
163 | #
164 | # If all values are single-line, print all pairs in two-column format
165 | # with the specified key column width (identical to using
166 | # `batslib_print_kv_single').
167 | #
168 | # Otherwise, print all pairs in multi-line format after indenting values
169 | # with two spaces for readability (identical to using `batslib_prefix'
170 | # and `batslib_print_kv_multi')
171 | #
172 | # Globals:
173 | # none
174 | # Arguments:
175 | # $1 - width of key column (for two-column format)
176 | # $even - key
177 | # $odd - value of the previous key
178 | # Returns:
179 | # none
180 | # Outputs:
181 | # STDOUT - formatted key-value pairs
182 | batslib_print_kv_single_or_multi() {
183 | local -ir width="$1"; shift
184 | local -a pairs=( "$@" )
185 |
186 | local -a values=()
187 | local -i i
188 | for (( i=1; i < ${#pairs[@]}; i+=2 )); do
189 | values+=( "${pairs[$i]}" )
190 | done
191 |
192 | if batslib_is_single_line "${values[@]}"; then
193 | batslib_print_kv_single "$width" "${pairs[@]}"
194 | else
195 | local -i i
196 | for (( i=1; i < ${#pairs[@]}; i+=2 )); do
197 | pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
198 | done
199 | batslib_print_kv_multi "${pairs[@]}"
200 | fi
201 | }
202 |
203 | # Prefix each line read from the standard input with the given string.
204 | #
205 | # Globals:
206 | # none
207 | # Arguments:
208 | # $1 - [= ] prefix string
209 | # Returns:
210 | # none
211 | # Inputs:
212 | # STDIN - lines
213 | # Outputs:
214 | # STDOUT - prefixed lines
215 | batslib_prefix() {
216 | local -r prefix="${1:- }"
217 | local line
218 | while IFS='' read -r line || [[ -n $line ]]; do
219 | printf '%s%s\n' "$prefix" "$line"
220 | done
221 | }
222 |
223 | # Mark select lines of the text read from the standard input by
224 | # overwriting their beginning with the given string.
225 | #
226 | # Usually the input is indented by a few spaces using `batslib_prefix'
227 | # first.
228 | #
229 | # Globals:
230 | # none
231 | # Arguments:
232 | # $1 - marking string
233 | # $@ - indices (zero-based) of lines to mark
234 | # Returns:
235 | # none
236 | # Inputs:
237 | # STDIN - lines
238 | # Outputs:
239 | # STDOUT - lines after marking
240 | batslib_mark() {
241 | local -r symbol="$1"; shift
242 | # Sort line numbers.
243 | set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
244 |
245 | local line
246 | local -i idx=0
247 | while IFS='' read -r line || [[ -n $line ]]; do
248 | if (( ${1:--1} == idx )); then
249 | printf '%s\n' "${symbol}${line:${#symbol}}"
250 | shift
251 | else
252 | printf '%s\n' "$line"
253 | fi
254 | (( ++idx ))
255 | done
256 | }
257 |
258 | # Enclose the input text in header and footer lines.
259 | #
260 | # The header contains the given string as title. The output is preceded
261 | # and followed by an additional newline to make it stand out more.
262 | #
263 | # Globals:
264 | # none
265 | # Arguments:
266 | # $1 - title
267 | # Returns:
268 | # none
269 | # Inputs:
270 | # STDIN - text
271 | # Outputs:
272 | # STDOUT - decorated text
273 | batslib_decorate() {
274 | echo
275 | echo "-- $1 --"
276 | PATH="$BATS_SAVED_PATH" command cat -
277 | echo '--'
278 | echo
279 | }
280 |
--------------------------------------------------------------------------------
/test/50-output-10-batslib_err.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_err() : displays ' {
6 | run batslib_err 'm1' 'm2'
7 | [ "$status" -eq 0 ]
8 | [ "$output" == 'm1 m2' ]
9 | }
10 |
11 | @test 'batslib_err(): reads from STDIN' {
12 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
13 | echo 'm1' 'm2' | batslib_err"
14 | [ "$status" -eq 0 ]
15 | [ "$output" == 'm1 m2' ]
16 | }
17 |
18 | @test 'batslib_err() works with modified path' {
19 | export PATH="$BATS_TEST_DIRNAME:$PATH"
20 | echo 'm1 m2' | {
21 | # Verify stub
22 | run which cat
23 | [ "$status" -eq 0 ]
24 | [ "$output" = "$BATS_TEST_DIRNAME/cat" ]
25 | # Should still work
26 | run batslib_err
27 | [ "$status" -eq 0 ]
28 | [ "$output" == 'm1 m2' ]
29 | }
30 | }
31 |
32 | @test 'batslib_err() works with mock function' {
33 | echo 'm1 m2' | {
34 | function cat {
35 | echo "Mocked cat"
36 | }
37 | [ "$(cat)" = "Mocked cat" ]
38 | # Should still work
39 | run batslib_err
40 | [ "$status" -eq 0 ]
41 | [ "$output" == 'm1 m2' ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/test/50-output-11-batslib_count_lines.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_count_lines() : displays the number of lines in ' {
6 | run batslib_count_lines $'a\nb\nc\n'
7 | [ "$status" -eq 0 ]
8 | [ "$output" == '3' ]
9 | }
10 |
11 | @test 'batslib_count_lines() : counts the last line when it is not terminated by a newline' {
12 | run batslib_count_lines $'a\nb\nc'
13 | [ "$status" -eq 0 ]
14 | [ "$output" == '3' ]
15 | }
16 |
17 | @test 'batslib_count_lines() : counts empty lines' {
18 | run batslib_count_lines $'\n\n\n'
19 | [ "$status" -eq 0 ]
20 | [ "$output" == '3' ]
21 | }
22 |
--------------------------------------------------------------------------------
/test/50-output-12-batslib_is_single_line.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_is_single_line() : returns 0 if all are single-line' {
6 | run batslib_is_single_line 'a' $'b\n' 'c'
7 | [ "$status" -eq 0 ]
8 | }
9 |
10 | @test 'batslib_is_single_line() : returns 1 if at least one of is longer than one line' {
11 | run batslib_is_single_line 'a' $'b\nb' 'c'
12 | [ "$status" -eq 1 ]
13 | }
14 |
--------------------------------------------------------------------------------
/test/50-output-13-batslib_get_max_single_line_key_width.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_get_max_single_line_key_width() : displays the length of the longest key' {
6 | local -ar pairs=( 'k _1' 'v 1'
7 | 'k 2' 'v 2'
8 | 'k __3' 'v 3' )
9 | run batslib_get_max_single_line_key_width "${pairs[@]}"
10 | [ "$status" -eq 0 ]
11 | [ "$output" == '5' ]
12 | }
13 |
14 | @test 'batslib_get_max_single_line_key_width() : only considers keys with single-line values' {
15 | local -ar pairs=( 'k _1' 'v 1'
16 | 'k 2' 'v 2'
17 | 'k __3' $'v\n3' )
18 | run batslib_get_max_single_line_key_width "${pairs[@]}"
19 | [ "$status" -eq 0 ]
20 | [ "$output" == '4' ]
21 | }
22 |
--------------------------------------------------------------------------------
/test/50-output-14-batslib_print_kv_single.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_print_kv_single() : displays in two-column format with wide key column' {
6 | local -ar pairs=( 'k _1' 'v 1'
7 | 'k 2 ' 'v 2'
8 | 'k __3' 'v 3' )
9 | run batslib_print_kv_single 5 "${pairs[@]}"
10 | [ "$status" -eq 0 ]
11 | [ "${#lines[@]}" == '3' ]
12 | [ "${lines[0]}" == 'k _1 : v 1' ]
13 | [ "${lines[1]}" == 'k 2 : v 2' ]
14 | [ "${lines[2]}" == 'k __3 : v 3' ]
15 | }
16 |
17 | @test 'batslib_print_kv_single() : does not truncate keys when the column is too narrow' {
18 | local -ar pairs=( 'k _1' 'v 1'
19 | 'k 2' 'v 2'
20 | 'k __3' 'v 3' )
21 | run batslib_print_kv_single 0 "${pairs[@]}"
22 | [ "$status" -eq 0 ]
23 | [ "${#lines[@]}" == '3' ]
24 | [ "${lines[0]}" == 'k _1 : v 1' ]
25 | [ "${lines[1]}" == 'k 2 : v 2' ]
26 | [ "${lines[2]}" == 'k __3 : v 3' ]
27 | }
28 |
--------------------------------------------------------------------------------
/test/50-output-15-batslib_print_kv_multi.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_print_kv_multi() : displays in multi-line format' {
6 | local -ar pairs=( 'k _1' 'v 1'
7 | 'k 2' $'v 2-1\nv 2-2'
8 | 'k __3' 'v 3' )
9 | run batslib_print_kv_multi "${pairs[@]}"
10 | [ "$status" -eq 0 ]
11 | [ "${#lines[@]}" == '7' ]
12 | [ "${lines[0]}" == 'k _1 (1 lines):' ]
13 | [ "${lines[1]}" == 'v 1' ]
14 | [ "${lines[2]}" == 'k 2 (2 lines):' ]
15 | [ "${lines[3]}" == 'v 2-1' ]
16 | [ "${lines[4]}" == 'v 2-2' ]
17 | [ "${lines[5]}" == 'k __3 (1 lines):' ]
18 | [ "${lines[6]}" == 'v 3' ]
19 | }
20 |
--------------------------------------------------------------------------------
/test/50-output-16-batslib_print_kv_single_or_multi.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_print_kv_single_or_multi() : displays in two-column format if all values are one line long' {
6 | local -ar pairs=( 'k _1' 'v 1'
7 | 'k 2 ' 'v 2'
8 | 'k __3' 'v 3' )
9 | run batslib_print_kv_single_or_multi 5 "${pairs[@]}"
10 | [ "$status" -eq 0 ]
11 | [ "${#lines[@]}" == '3' ]
12 | [ "${lines[0]}" == 'k _1 : v 1' ]
13 | [ "${lines[1]}" == 'k 2 : v 2' ]
14 | [ "${lines[2]}" == 'k __3 : v 3' ]
15 | }
16 |
17 | @test 'batslib_print_kv_single_or_multi() : displays in multi-line format if at least one value is longer than one line' {
18 | local -ar pairs=( 'k _1' 'v 1'
19 | 'k 2' $'v 2-1\nv 2-2'
20 | 'k __3' 'v 3' )
21 | run batslib_print_kv_single_or_multi 5 "${pairs[@]}"
22 | [ "$status" -eq 0 ]
23 | [ "${#lines[@]}" == '7' ]
24 | [ "${lines[0]}" == 'k _1 (1 lines):' ]
25 | [ "${lines[1]}" == ' v 1' ]
26 | [ "${lines[2]}" == 'k 2 (2 lines):' ]
27 | [ "${lines[3]}" == ' v 2-1' ]
28 | [ "${lines[4]}" == ' v 2-2' ]
29 | [ "${lines[5]}" == 'k __3 (1 lines):' ]
30 | [ "${lines[6]}" == ' v 3' ]
31 | }
32 |
--------------------------------------------------------------------------------
/test/50-output-17-batslib_prefix.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_prefix() : prefixes each line of the input with ' {
6 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
7 | printf 'a\nb\nc\n' | batslib_prefix '_'"
8 | [ "$status" -eq 0 ]
9 | [ "${#lines[@]}" -eq 3 ]
10 | [ "${lines[0]}" == '_a' ]
11 | [ "${lines[1]}" == '_b' ]
12 | [ "${lines[2]}" == '_c' ]
13 | }
14 |
15 | @test 'batslib_prefix() : prefixes the last line when it is not terminated by a newline' {
16 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
17 | printf 'a\nb\nc' | batslib_prefix '_'"
18 | [ "$status" -eq 0 ]
19 | [ "${#lines[@]}" -eq 3 ]
20 | [ "${lines[0]}" == '_a' ]
21 | [ "${lines[1]}" == '_b' ]
22 | [ "${lines[2]}" == '_c' ]
23 | }
24 |
25 | @test 'batslib_prefix() : prefixes empty lines' {
26 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
27 | printf '\n\n\n' | batslib_prefix '_'"
28 | [ "$status" -eq 0 ]
29 | [ "${#lines[@]}" -eq 3 ]
30 | [ "${lines[0]}" == '_' ]
31 | [ "${lines[1]}" == '_' ]
32 | [ "${lines[2]}" == '_' ]
33 | }
34 |
35 | @test 'batslib_prefix(): default to two spaces' {
36 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
37 | printf 'a\nb\nc\n' | batslib_prefix"
38 | [ "$status" -eq 0 ]
39 | [ "${#lines[@]}" -eq 3 ]
40 | [ "${lines[0]}" == ' a' ]
41 | [ "${lines[1]}" == ' b' ]
42 | [ "${lines[2]}" == ' c' ]
43 | }
44 |
--------------------------------------------------------------------------------
/test/50-output-18-batslib_mark.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_mark() : marks the -th line of the input with ' {
6 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
7 | printf ' a\n b\n c\n' | batslib_mark '>' 0"
8 | [ "$status" -eq 0 ]
9 | [ "${#lines[@]}" -eq 3 ]
10 | [ "${lines[0]}" == '>a' ]
11 | [ "${lines[1]}" == ' b' ]
12 | [ "${lines[2]}" == ' c' ]
13 | }
14 |
15 | @test 'batslib_mark() : marks multiple lines when is in ascending order' {
16 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
17 | printf ' a\n b\n c\n' | batslib_mark '>' 1 2"
18 | [ "$status" -eq 0 ]
19 | [ "${#lines[@]}" -eq 3 ]
20 | [ "${lines[0]}" == ' a' ]
21 | [ "${lines[1]}" == '>b' ]
22 | [ "${lines[2]}" == '>c' ]
23 | }
24 |
25 | @test 'batslib_mark() : marks multiple lines when is in random order' {
26 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
27 | printf ' a\n b\n c\n d\n' | batslib_mark '>' 2 1 3"
28 | [ "$status" -eq 0 ]
29 | [ "${#lines[@]}" -eq 4 ]
30 | [ "${lines[0]}" == ' a' ]
31 | [ "${lines[1]}" == '>b' ]
32 | [ "${lines[2]}" == '>c' ]
33 | [ "${lines[3]}" == '>d' ]
34 | }
35 |
36 | @test 'batslib_mark() : ignores duplicate indices' {
37 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
38 | printf ' a\n b\n c\n' | batslib_mark '>' 1 2 1"
39 | [ "$status" -eq 0 ]
40 | [ "${#lines[@]}" -eq 3 ]
41 | [ "${lines[0]}" == ' a' ]
42 | [ "${lines[1]}" == '>b' ]
43 | [ "${lines[2]}" == '>c' ]
44 | }
45 |
46 | @test 'batslib_mark() : outputs the input untouched if is the empty string' {
47 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
48 | printf ' a\n b\n c\n' | batslib_mark '' 1"
49 | [ "$status" -eq 0 ]
50 | [ "${#lines[@]}" -eq 3 ]
51 | [ "${lines[0]}" == ' a' ]
52 | [ "${lines[1]}" == ' b' ]
53 | [ "${lines[2]}" == ' c' ]
54 | }
55 |
56 | @test 'batslib_mark() : marks the last line when it is not terminated by a newline' {
57 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
58 | printf ' a\n b\n c' | batslib_mark '>' 2"
59 | [ "$status" -eq 0 ]
60 | [ "${#lines[@]}" -eq 3 ]
61 | [ "${lines[0]}" == ' a' ]
62 | [ "${lines[1]}" == ' b' ]
63 | [ "${lines[2]}" == '>c' ]
64 | }
65 |
66 | @test 'batslib_mark() : does not truncate if it is longer than the marked line' {
67 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
68 | printf '\n' | batslib_mark '>' 0"
69 | [ "$status" -eq 0 ]
70 | [ "${#lines[@]}" -eq 1 ]
71 | [ "${lines[0]}" == '>' ]
72 | }
73 |
--------------------------------------------------------------------------------
/test/50-output-19-batslib_decorate.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'batslib_decorate() : encloses the input in a footer line and a header line containing ' {
6 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
7 | echo 'body' | batslib_decorate 'title'"
8 | [ "$status" -eq 0 ]
9 | [ "${#lines[@]}" -eq 3 ]
10 | [ "${lines[0]}" == '-- title --' ]
11 | [ "${lines[1]}" == 'body' ]
12 | [ "${lines[2]}" == '--' ]
13 | }
14 |
15 | @test 'batslib_decorate() works with modified path' {
16 | export PATH="$BATS_TEST_DIRNAME:$PATH"
17 | echo body | {
18 | # Verify stub
19 | run which cat
20 | [ "$status" -eq 0 ]
21 | [ "$output" = "$BATS_TEST_DIRNAME/cat" ]
22 | # Should still work
23 | run batslib_decorate 'title'
24 | [ "$status" -eq 0 ]
25 | [ "${#lines[@]}" -eq 3 ]
26 | [ "${lines[0]}" == '-- title --' ]
27 | [ "${lines[1]}" == 'body' ]
28 | [ "${lines[2]}" == '--' ]
29 | }
30 | }
31 |
32 | @test 'batslib_decorate() works with mock function' {
33 | echo body | {
34 | function cat {
35 | echo "Mocked cat"
36 | }
37 | [ "$(cat)" = "Mocked cat" ]
38 | # Should still work
39 | run batslib_decorate 'title'
40 | [ "$status" -eq 0 ]
41 | [ "${#lines[@]}" -eq 3 ]
42 | [ "${lines[0]}" == '-- title --' ]
43 | [ "${lines[1]}" == 'body' ]
44 | [ "${lines[2]}" == '--' ]
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/test/51-error-10-fail.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load test_helper
4 |
5 | @test 'fail() : returns 1 and displays ' {
6 | run fail 'message'
7 | [ "$status" -eq 1 ]
8 | [ "$output" == 'message' ]
9 | }
10 |
11 | @test 'fail(): reads from STDIN' {
12 | run bash -c "source '${TEST_MAIN_DIR}/load.bash'
13 | echo 'message' | fail"
14 | [ "$status" -eq 1 ]
15 | [ "$output" == 'message' ]
16 | }
17 |
--------------------------------------------------------------------------------
/test/52-lang-10-batslib_is_caller.bats:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bats
2 |
3 | load 'test_helper'
4 |
5 |
6 | # Test functions
7 | test_func_lvl_2() {
8 | test_func_lvl_1 "$@"
9 | }
10 |
11 | test_func_lvl_1() {
12 | test_func_lvl_0 "$@"
13 | }
14 |
15 | test_func_lvl_0() {
16 | batslib_is_caller "$@"
17 | }
18 |
19 |
20 | #
21 | # Direct invocation
22 | #
23 |
24 | # Interface
25 | @test 'batslib_is_caller() : returns 0 if the current function was called directly from ' {
26 | run test_func_lvl_1 test_func_lvl_1
27 | [ "$status" -eq 0 ]
28 | [ "${#lines[@]}" -eq 0 ]
29 | }
30 |
31 | @test 'batslib_is_caller() : returns 1 if the current function was not called directly from ' {
32 | run test_func_lvl_0 test_func_lvl_1
33 | [ "$status" -eq 1 ]
34 | [ "${#lines[@]}" -eq 0 ]
35 | }
36 |
37 | # Correctness
38 | @test 'batslib_is_caller() : the current function does not appear on the call stack' {
39 | run test_func_lvl_0 test_func_lvl_0
40 | [ "$status" -eq 1 ]
41 | [ "${#lines[@]}" -eq 0 ]
42 | }
43 |
44 |
45 | #
46 | # Indirect invocation
47 | #
48 |
49 | # Options
50 | test_i_indirect() {
51 | run test_func_lvl_2 "$@"
52 | [ "$status" -eq 0 ]
53 | [ "${#lines[@]}" -eq 0 ]
54 | }
55 |
56 | @test 'batslib_is_caller() -i : enables indirect checking' {
57 | test_i_indirect -i test_func_lvl_2
58 | }
59 |
60 | @test 'batslib_is_caller() --indirect : enables indirect checking' {
61 | test_i_indirect --indirect test_func_lvl_2
62 | }
63 |
64 | # Interface
65 | @test 'batslib_is_caller() --indirect : returns 0 if the current function was called indirectly from ' {
66 | run test_func_lvl_2 --indirect test_func_lvl_2
67 | [ "$status" -eq 0 ]
68 | [ "${#lines[@]}" -eq 0 ]
69 | }
70 |
71 | @test 'batslib_is_caller() --indirect : returns 1 if the current function was not called indirectly from ' {
72 | run test_func_lvl_1 --indirect test_func_lvl_2
73 | [ "$status" -eq 1 ]
74 | [ "${#lines[@]}" -eq 0 ]
75 | }
76 |
77 | # Correctness
78 | @test 'batslib_is_caller() --indirect : direct invocation is a special case of indirect invocation with zero intermediate calls' {
79 | run test_func_lvl_1 --indirect test_func_lvl_1
80 | [ "$status" -eq 0 ]
81 | [ "${#lines[@]}" -eq 0 ]
82 | }
83 |
84 | @test 'batslib_is_caller() --indirect : the current function does not appear on the call stack' {
85 | run test_func_lvl_0 --indirect test_func_lvl_0
86 | [ "$status" -eq 1 ]
87 | [ "${#lines[@]}" -eq 0 ]
88 | }
89 |
--------------------------------------------------------------------------------
/test/cat:
--------------------------------------------------------------------------------
1 | # Dummy file stubbing a stub/mock
2 |
--------------------------------------------------------------------------------
/test/test_helper.bash:
--------------------------------------------------------------------------------
1 | setup() {
2 | export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.."
3 |
4 | # Load library.
5 | load '../load'
6 | }
7 |
--------------------------------------------------------------------------------