├── .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 | [![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/bats-core/bats-support/master/LICENSE) 4 | [![GitHub release](https://img.shields.io/github/release/bats-core/bats-support.svg)](https://github.com/bats-core/bats-support/releases/latest) 5 | [![Build Status](https://github.com/bats-core/bats-support/workflows/Tests/badge.svg)](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 <title>' { 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() <message>: returns 1 and displays <message>' { 6 | run fail 'message' 7 | [ "$status" -eq 1 ] 8 | [ "$output" == 'message' ] 9 | } 10 | 11 | @test 'fail(): reads <message> 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() <function>: returns 0 if the current function was called directly from <function>' { 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() <function>: returns 1 if the current function was not called directly from <function>' { 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() <function>: 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 <function>: enables indirect checking' { 57 | test_i_indirect -i test_func_lvl_2 58 | } 59 | 60 | @test 'batslib_is_caller() --indirect <function>: enables indirect checking' { 61 | test_i_indirect --indirect test_func_lvl_2 62 | } 63 | 64 | # Interface 65 | @test 'batslib_is_caller() --indirect <function>: returns 0 if the current function was called indirectly from <function>' { 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 <function>: returns 1 if the current function was not called indirectly from <function>' { 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 <function>: 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 <function>: 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 | --------------------------------------------------------------------------------