├── .github └── CODEOWNERS ├── .gitignore ├── .gitmodules ├── .gittrees ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CyberArk_Open_Source_Contributor_Agreement.pdf ├── Jenkinsfile ├── LICENSE ├── README.md ├── SECURITY.md ├── filehandling └── lib ├── git └── lib ├── github └── lib ├── helpers └── lib ├── init ├── k8s ├── Dockerfile ├── lib └── platform_login ├── logging └── lib ├── ruby └── lib ├── run-tests ├── secrets.yml ├── test-utils ├── bats-assert-1 │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── load.bash │ ├── package.json │ ├── script │ │ └── install-bats.sh │ ├── src │ │ └── assert.bash │ └── test │ │ ├── 50-assert-11-assert.bats │ │ ├── 50-assert-12-assert_equal.bats │ │ ├── 50-assert-13-assert_success.bats │ │ ├── 50-assert-14-assert_failure.bats │ │ ├── 50-assert-15-assert_output.bats │ │ ├── 50-assert-16-refute_output.bats │ │ ├── 50-assert-17-assert_line.bats │ │ ├── 50-assert-18-refute_line.bats │ │ ├── 50-assert-19-refute.bats │ │ └── test_helper.bash ├── bats-support │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── load.bash │ ├── package.json │ ├── script │ │ └── install-bats.sh │ ├── 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 │ │ └── test_helper.bash ├── bats │ ├── .appveyor.yml │ ├── .gitattributes │ ├── .travis.yml │ ├── AUTHORS │ ├── Dockerfile │ ├── LICENSE.md │ ├── README.md │ ├── bin │ │ └── bats │ ├── contrib │ │ └── rpm │ │ │ └── bats.spec │ ├── docs │ │ ├── CODEOWNERS │ │ ├── CODE_OF_CONDUCT.md │ │ ├── CONTRIBUTING.md │ │ ├── PULL_REQUEST_TEMPLATE.md │ │ └── usage.md │ ├── install.sh │ ├── libexec │ │ └── bats-core │ │ │ ├── bats │ │ │ ├── bats-exec-suite │ │ │ ├── bats-exec-test │ │ │ ├── bats-format-tap-stream │ │ │ └── bats-preprocess │ ├── man │ │ ├── Makefile │ │ ├── README.md │ │ ├── bats.1 │ │ ├── bats.1.ronn │ │ ├── bats.7 │ │ └── bats.7.ronn │ ├── package.json │ └── test │ │ ├── bats.bats │ │ ├── fixtures │ │ ├── bats │ │ │ ├── dos_line.bats │ │ │ ├── duplicate-tests.bats │ │ │ ├── empty.bats │ │ │ ├── environment.bats │ │ │ ├── expand_var_in_test_name.bats │ │ │ ├── exported_function.bats │ │ │ ├── failing.bats │ │ │ ├── failing_and_passing.bats │ │ │ ├── failing_helper.bats │ │ │ ├── failing_setup.bats │ │ │ ├── failing_teardown.bats │ │ │ ├── intact.bats │ │ │ ├── invalid_tap.bats │ │ │ ├── load.bats │ │ │ ├── loop_keep_IFS.bats │ │ │ ├── no-final-newline.bats │ │ │ ├── output.bats │ │ │ ├── passing.bats │ │ │ ├── passing_and_failing.bats │ │ │ ├── passing_and_skipping.bats │ │ │ ├── passing_failing_and_skipping.bats │ │ │ ├── quoted_and_unquoted_test_names.bats │ │ │ ├── reference_unset_parameter.bats │ │ │ ├── reference_unset_parameter_in_setup.bats │ │ │ ├── reference_unset_parameter_in_teardown.bats │ │ │ ├── setup.bats │ │ │ ├── single_line.bats │ │ │ ├── skipped.bats │ │ │ ├── skipped_with_parens.bats │ │ │ ├── source_nonexistent_file.bats │ │ │ ├── source_nonexistent_file_in_setup.bats │ │ │ ├── source_nonexistent_file_in_teardown.bats │ │ │ ├── teardown.bats │ │ │ ├── test_helper.bash │ │ │ ├── unofficial_bash_strict_mode.bash │ │ │ ├── unofficial_bash_strict_mode.bats │ │ │ ├── whitespace.bats │ │ │ └── without_trailing_newline.bats │ │ └── suite │ │ │ ├── empty │ │ │ └── .gitkeep │ │ │ ├── multiple │ │ │ ├── a.bats │ │ │ └── b.bats │ │ │ ├── recursive │ │ │ ├── subsuite │ │ │ │ └── test2.bats │ │ │ └── test.bats │ │ │ └── single │ │ │ └── test.bats │ │ ├── install.bats │ │ ├── root.bats │ │ ├── suite.bats │ │ └── test_helper.bash ├── lib └── tap2junit │ ├── Dockerfile │ ├── constraints.txt │ ├── requirements.txt │ └── tap2junit.py └── tests-for-this-repo ├── filehandling.bats ├── fixtures ├── ruby │ └── ruby_gems_api_response.json └── test-utils │ ├── tap2junit.in │ └── tap2junit.out ├── git.bats ├── github.bats ├── helpers.bats ├── k8s.bats ├── lint.bats ├── logging.bats ├── python-lint ├── Dockerfile ├── constraints.txt └── requirements.txt ├── ruby.bats ├── run-bats-tests ├── run-gitleaks ├── run-python-lint ├── test-utils.bats └── validate-changelog /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @cyberark/conjur-infra-team 2 | 3 | # Changes to .trivyignore require Security Architect approval 4 | .trivyignore @cyberark/security-architects 5 | 6 | # Changes to .codeclimate.yml require Quality Architect approval 7 | .codeclimate.yml @cyberark/quality-architects 8 | 9 | # Changes to SECURITY.md require Security Architect approval 10 | SECURITY.md @cyberark/security-architects 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/bash-lib/238610390647a0ae4b7eeb1e52814026e87853b5/.gitmodules -------------------------------------------------------------------------------- /.gittrees: -------------------------------------------------------------------------------- 1 | # Git Subtrees 2 | 3 | # The advantage of subtrees is that users don't have to care about them - its 4 | # just a single repo. The disadvantage is that git doesn't track the metadata 5 | # as it does for submodules. 6 | 7 | # This file provides an enumeration of the subtrees in this repo, and the URLs 8 | # they came from. 9 | 10 | # subtree_path remote_url remote_name 11 | test-utils/bats https://github.com/bats-core/bats bats 12 | test-utils/bats-support https://github.com/ztombol/bats-support bats-support 13 | test-utils/bats-assert-1 https://github.com/jasonkarns/bats-assert-1 bats-assert-1 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [2.0.6] - 2021-08-23 10 | ### Changed 11 | - bl_shellcheck_script now passes SHELLCHECK_OPTS through to the container that runs shellcheck 12 | 13 | ## [2.0.5] - 2021-02-16 14 | ### Changed 15 | - bl_all_files_in_repo now excludes submodules 16 | 17 | ## [2.0.4] - 2020-09-25 18 | ### Changed 19 | - bl_hub_issue_number_for_title now returns the first matching issue number only, instead of all matching issue numbers separated by newlines. 20 | 21 | ## [2.0.3] - 2020-03-13 22 | ### Added 23 | - bl_validate_changelog: Validate a changelog against keepachangelog.com format. 24 | 25 | ### Changed 26 | - bl_in_git_repo now fails (return 1) rather than exiting 1 27 | - Github issues can now be created with a label 28 | 29 | ## [2.0.2] - 2020-03-10 30 | ### Added 31 | - Retrieve latest version for gem from rubygems.org 32 | 33 | ## [2.0.1] - 2020-02-19 34 | ### Added 35 | - Github issue related functions via the `hub` cli 36 | 37 | ### Changed 38 | - Helpers lib now uses bash-lib logging functions 39 | 40 | ### Fixed 41 | - Ensured that all variables used within bash-lib functions are declared as local 42 | 43 | ## [2.0.0] - 2020-01-17 44 | ### Added 45 | - Logging Functions, with basic log level support 46 | - Retry Constant function for when you want to retry but don't want increasing 47 | backoff. 48 | 49 | ### Changed 50 | - Prefixed all function names with bl_ to prevent name clashes with builtins 51 | and other libraries. 52 | - Libraries are now loaded by the init script, so they don't need to be 53 | sourced individually. 54 | 55 | ## [1.0.0] - 2019-11-20 56 | ### Added 57 | - filehandling, git, helpers, k8s, logging and test-utils libraries 58 | - Test Suite 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in bash-lib! 4 | 5 | For general contribution and community guidelines, please see the [community repo](https://github.com/cyberark/community). 6 | In particular, before contributing please review our [contributor licensing guide](https://github.com/cyberark/community/blob/master/CONTRIBUTING.md#when-the-repo-does-not-include-the-cla) 7 | to ensure your contribution is compliant with our contributor license agreements. 8 | 9 | Contributed bash functions are most welcome! The more we share the less we 10 | duplicate each other. In order to keep this repo tidy, every function must be 11 | documented in the readme and tested, the lint scripts enforce these rules. 12 | 13 | 1. Add the libraries or functions that you need 14 | 2. Add BATS tests for all new top level functions 15 | 3. Add descriptions for each function to the contents table in this readme 16 | 4. Run ./run-tests to ensure all tests pass before submitting 17 | 5. Create a PR 18 | 6. Wait for review 19 | 20 | ## Style Guide 21 | Follow the [google shell style guide](https://google.github.io/styleguide/shell.xml#Naming_Conventions). 22 | TL;DR: 23 | 1. Use snake_case function and variable names 24 | 1. Use `function` when declaring functions. 25 | 1. Don't use .sh extensions 26 | 27 | ## Testing 28 | Tests are written using [BATS](https://github.com/bats-core/bats). Each lib has a `lib-name.bats` file in [tests-for-this-repo](/tests-for-this-repo). 29 | Asserts are provided by [bats-assert-1](https://github.com/jasonkarns/bats-assert-1). Asserts provide useful debugging output when the assertion fails, eg expected x got y. 30 | 31 | Example: 32 | ```bash 33 | # source support and assert libraries 34 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 35 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 36 | 37 | # source the library under test 38 | . "${BASH_LIB_DIR}/git/lib" 39 | 40 | # define a test that calls a library function 41 | @test "it does the thing" { 42 | some_prep_work 43 | # run is a wrapper that catches failures so that assertsions can be run, 44 | # otherwise the test would immediately fail. 45 | run does_the_thing 46 | assert_success 47 | assert_output "thing done" 48 | } 49 | ``` 50 | 51 | Test fixtures should go in /tests-for-this-repo/[fixtures](tests-for-this-repo/fixtures)/lib-name. 52 | -------------------------------------------------------------------------------- /CyberArk_Open_Source_Contributor_Agreement.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/bash-lib/238610390647a0ae4b7eeb1e52814026e87853b5/CyberArk_Open_Source_Contributor_Agreement.pdf -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | pipeline { 4 | agent { label 'executor-v2' } 5 | 6 | options { 7 | timestamps() 8 | buildDiscarder(logRotator(numToKeepStr: '30')) 9 | } 10 | 11 | triggers { 12 | cron(getDailyCronString()) 13 | } 14 | 15 | environment { 16 | BATS_OUTPUT_FORMAT="junit" 17 | } 18 | 19 | stages { 20 | 21 | stage('Validate Changelog'){ 22 | steps{ 23 | parseChangelog() 24 | } 25 | } 26 | 27 | stage('BATS Tests') { 28 | steps { 29 | sh './tests-for-this-repo/run-bats-tests' 30 | } 31 | } 32 | 33 | stage('Python Linting') { 34 | steps { 35 | sh './tests-for-this-repo/run-python-lint' 36 | } 37 | } 38 | 39 | stage('Secrets Leak Check') { 40 | steps { 41 | sh './tests-for-this-repo/run-gitleaks' 42 | } 43 | } 44 | 45 | } 46 | 47 | post { 48 | always { 49 | junit '*-junit.xml' 50 | cleanupAndNotify(currentBuild.currentResult) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policies and Procedures 2 | 3 | This document outlines security procedures and general policies for the CyberArk Conjur 4 | suite of tools and products. 5 | 6 | * [Reporting a Bug](#reporting-a-bug) 7 | * [Disclosure Policy](#disclosure-policy) 8 | * [Comments on this Policy](#comments-on-this-policy) 9 | 10 | ## Reporting a Bug 11 | 12 | The CyberArk Conjur team and community take all security bugs in the Conjur suite seriously. 13 | Thank you for improving the security of the Conjur suite. We appreciate your efforts and 14 | responsible disclosure and will make every effort to acknowledge your 15 | contributions. 16 | 17 | Report security bugs by emailing the lead maintainers at security@conjur.org. 18 | 19 | The maintainers will acknowledge your email within 2 business days. Subsequently, we will 20 | send a more detailed response within 2 business days of our acknowledgement indicating 21 | the next steps in handling your report. After the initial reply to your report, the security 22 | team will endeavor to keep you informed of the progress towards a fix and full 23 | announcement, and may ask for additional information or guidance. 24 | 25 | Report security bugs in third-party modules to the person or team maintaining 26 | the module. 27 | 28 | ## Disclosure Policy 29 | 30 | When the security team receives a security bug report, they will assign it to a 31 | primary handler. This person will coordinate the fix and release process, 32 | involving the following steps: 33 | 34 | * Confirm the problem and determine the affected versions. 35 | * Audit code to find any potential similar problems. 36 | * Prepare fixes for all releases still under maintenance. These fixes will be 37 | released as fast as possible. 38 | 39 | ## Comments on this Policy 40 | 41 | If you have suggestions on how this process could be improved please submit a 42 | pull request. 43 | -------------------------------------------------------------------------------- /filehandling/lib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 4 | 5 | #https://stackoverflow.com/a/23002317 6 | function bl_abs_path() { 7 | # generate absolute path from relative path 8 | # path : relative filename 9 | # return : absolute path 10 | 11 | local path 12 | 13 | if [[ -z "${1:-}" ]]; then 14 | path="." 15 | else 16 | path="${1}" 17 | fi 18 | if [ -d "${path}" ]; then 19 | # dir 20 | (bl_spushd "${path}"; pwd) 21 | elif [ -f "${path}" ]; then 22 | # file 23 | if [[ ${path} = /* ]]; then 24 | echo "${path}" 25 | elif [[ ${path} == */* ]]; then 26 | echo "$(bl_spushd "${path%/*}"; pwd)/${path##*/}" 27 | else 28 | echo "$(pwd)/${path}" 29 | fi 30 | fi 31 | } 32 | -------------------------------------------------------------------------------- /git/lib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 4 | 5 | 6 | function bl_git_available(){ 7 | type git &>/dev/null || bl_fail "Git binary not found in ${PATH}" 8 | } 9 | 10 | function bl_in_git_repo(){ 11 | bl_git_available 12 | git status >/dev/null || bl_fail "$(pwd) is not within a git repo." 13 | } 14 | 15 | function bl_github_owner_repo(){ 16 | bl_in_git_repo 17 | remote="${1:-origin}" 18 | 19 | git remote -v | grep -q "${remote}" || bl_die "Remote ${remote} doesn't exist for repo ${PWD}" 20 | git remote -v | grep -q "${remote}.*github" || bl_die "Remote ${remote} is not a github remote in repo ${PWD}" 21 | [[ "$(git remote -v |grep "${remote}")" =~ github.com[:/]([^ .]*) ]] 22 | echo "${BASH_REMATCH[1]}" 23 | } 24 | 25 | # Get the top level of a git repo 26 | function bl_repo_root(){ 27 | bl_in_git_repo 28 | git rev-parse --show-toplevel 29 | } 30 | 31 | # List files tracked by git excluding submodules 32 | function bl_all_files_in_repo(){ 33 | bl_in_git_repo 34 | git ls-files -s | grep -v ^16 | cut -f2- 35 | # ^- show status code ^16 = submodule 36 | # ^- exclude submodules 37 | # ^- return only file paths (not status code) 38 | } 39 | 40 | # Find the latest tag available at a repo url 41 | # Returns tag name, not sha 42 | function bl_remote_latest_tag(){ 43 | bl_in_git_repo 44 | 45 | local -r remote_url="${1}" 46 | # In ls-remote the ^{} suffix refers to a peeled/dereferenced object. 47 | # eg refs/tags/v0.0.1^{} shows the SHA of the commit that was tagged, 48 | # not the SHA of the tag itself. 49 | # Adding --refs hides peeled tags 50 | git ls-remote --tags --refs --quiet \ 51 | "${remote_url}" \ 52 | | tail -n 1 \ 53 | | cut -f 2 \ 54 | | sed -e 's+refs/tags/++' 55 | } 56 | 57 | # Find the SHA of the latests commit to be tagged in a remote repo 58 | function bl_remote_latest_tagged_commit(){ 59 | bl_in_git_repo 60 | 61 | local -r remote="${1}" 62 | local -r tag="$(bl_remote_latest_tag "${remote}")" 63 | git ls-remote "${remote}" | awk "/refs\/tags\/${tag}\^/{print \$1}" 64 | } 65 | 66 | function bl_remote_sha_for_ref(){ 67 | bl_in_git_repo 68 | 69 | local -r remote="${1}" 70 | local -r ref="${2}" 71 | local peeled_ref 72 | 73 | # First try adding ^{} to the ref, incase it's a tag 74 | # and needs peeling. If nothing is found for that, 75 | # try without. 76 | peeled_ref=$( 77 | git ls-remote "${remote}" \ 78 | | awk "/${ref}[^$]/{print \$1}" 79 | ) 80 | 81 | if [[ -n "${peeled_ref}" ]]; then 82 | echo "${peeled_ref}" 83 | else 84 | git ls-remote "${remote}" \ 85 | | awk "/${ref}/{print \$1}" 86 | fi 87 | } 88 | 89 | function bl_remote_tag_for_sha(){ 90 | bl_in_git_repo 91 | 92 | local -r remote="${1}" 93 | local -r sha="${2}" 94 | git ls-remote "${remote}" \ 95 | | awk -F'/' "/${sha}.*tag/{ gsub(/\^\{\}\$/, \"\"); print \$3 }" 96 | } 97 | 98 | 99 | ## Minimal git subtree functionality required for tests to pass 100 | # full subtree functionality is not ready for merge. 101 | function bl_gittrees_present(){ 102 | bl_in_git_repo 103 | local -r git_trees="$(bl_repo_root)/.gittrees" 104 | [[ -e "${git_trees}" ]] 105 | } 106 | 107 | function bl_cat_gittrees(){ 108 | bl_in_git_repo 109 | local -r git_trees="$(bl_repo_root)/.gittrees" 110 | local -r subtrees_file_format=".gittrees should contain one subtree per line,\ 111 | space seperated with three fields: subtree_path renmote_url remote_name" 112 | bl_gittrees_present || bl_die ".gittrees file ${git_trees} not found. ${subtrees_file_format}" 113 | grep -E -v '^\s*$|^\s*#' "$(bl_repo_root)/.gittrees" || true 114 | } 115 | 116 | function bl_tracked_files_excluding_subtrees(){ 117 | bl_in_git_repo 118 | local subtrees 119 | if bl_gittrees_present; then 120 | subtrees="$(bl_cat_gittrees | awk '{print $1}' | paste -sd '|' -)" 121 | bl_all_files_in_repo | grep -E -v "${subtrees}" 122 | else 123 | bl_log debug ".gittrees not found subtress not excluded" stderr 124 | bl_all_files_in_repo 125 | fi 126 | } 127 | -------------------------------------------------------------------------------- /github/lib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 4 | 5 | function bl_hub_available(){ 6 | # type instead of which, so it can be stubbed in tests 7 | type hub &>/dev/null || bl_fail "hub (github cli) binary not found, please install it via your package manager or use bl_hub_download_latest." 8 | } 9 | 10 | function bl_hub_creds_available(){ 11 | config_file="${HUB_CONFIG:-${HOME}/.config/hub}" 12 | [[ -n "${GITHUB_USER:-}" ]] && [[ -n "${GITHUB_TOKEN:-}" ]] && return 13 | [[ -e "${config_file}" ]] && return 14 | bl_fail "No credentials found for (git)hub please set GITHUB_USER and GITHUB_TOKEN or create ~/.config/hub" 15 | } 16 | 17 | function bl_hub_check(){ 18 | bl_in_git_repo \ 19 | && bl_hub_available \ 20 | && bl_hub_creds_available 21 | } 22 | 23 | function bl_hub_download_latest(){ 24 | local install_dir="${1:-${HOME}/bin}" 25 | local os_arch="${2:-}" 26 | local tmpdir=".hubdl" 27 | local download_url 28 | local bin_path 29 | 30 | bl_curl_available 31 | 32 | if [[ -z "${os_arch}" ]]; then 33 | if [[ "${OSTYPE}" =~ "darwin" ]]; then 34 | os_arch="darwin-amd64" 35 | else 36 | os_arch="linux-amd64" 37 | fi 38 | bl_debug "Hub Download detected arch: ${os_arch}" 39 | fi 40 | 41 | asset_url="$(curl https://api.github.com/repos/github/hub/releases/latest \ 42 | |jq -r ".assets | map(select(.name | contains(\"${os_arch}\")))[0].url")" 43 | 44 | download_url="$(curl "${asset_url}" |jq -r '.browser_download_url')" 45 | 46 | bin_path="${install_dir}/hub" 47 | mkdir -p "${install_dir}" 48 | 49 | mkdir -p "${tmpdir}" 50 | bl_spushd "${tmpdir}" 51 | curl -s -L "${download_url}" > hub.tgz 52 | tar xf hub.tgz 53 | bl_spopd 54 | mv "${tmpdir}"/*/bin/hub "${bin_path}" 55 | rm -rf "${tmpdir}" 56 | 57 | bl_info "${download_url}/bin/hub --> ${bin_path}" 58 | } 59 | 60 | function bl_hub_issue_number_for_title(){ 61 | local title="${1}" 62 | bl_hub_check 63 | hub issue \ 64 | |grep "${title}" \ 65 | |awk -F'[ #]+' '{print $2; exit}' 66 | } 67 | 68 | function bl_hub_add_issue_comment(){ 69 | local issue_number="${1}" 70 | local comment="${2}" 71 | 72 | bl_hub_check 73 | 74 | [[ -n "${comment}" ]] || bl_die "bl_hub_add_issue_comment: Comment must not be empty" 75 | hub issue show "${issue_number}" >/dev/null || bl_die "Github Issue number ${issue_number} isn't valid for repo $(pwd)" 76 | 77 | owner_repo="$(bl_github_owner_repo)" 78 | if hub api "repos/${owner_repo}/issues/${issue_number}/comments" --field body="${comment}" >/dev/null; then 79 | bl_debug "Added comment: \"${comment}\" to https://github.com/${owner_repo}/issues/${issue_number}" 80 | else 81 | bl_fail "Failed to add comment: ${comment} to issue: ${owner_repo}#${issue_number}" 82 | fi 83 | } 84 | 85 | 86 | function bl_hub_comment_or_create_issue(){ 87 | local title="${1}" 88 | local message="${2}" 89 | local label="${3:-}" 90 | local label_param="" 91 | local issue_number 92 | local issue_url 93 | local action 94 | local owner_repo 95 | bl_hub_check 96 | 97 | owner_repo="$(bl_github_owner_repo)" 98 | issue_number="$(bl_hub_issue_number_for_title "${title}" ||:)" 99 | 100 | if [[ -z "${issue_number}" ]]; then 101 | action="created" 102 | if [[ -n "${label}" ]]; then 103 | label_param="-l ${label}" 104 | fi 105 | 106 | # issue doesn't exist create it 107 | 108 | # The following prevents a shellcheck warning about label_param 109 | # getting split into multiple words. That is exactly what should 110 | # happen as "-l" and "labelname" should be separate tokens for 111 | # the hub command. 112 | # shellcheck disable=SC2086 113 | issue_url="$(hub issue create -m "${title} 114 | 115 | ${message}" ${label_param})" 116 | 117 | # Example issue url: https://github.com/{owner}/{repo}/issues/{issue number}" 118 | # To find the issue number, split on / and take the last field 119 | issue_number="$(awk -F'/' '{print $NF}' <<<"${issue_url}" )" 120 | 121 | bl_debug "Created issue: ${issue_url} with title \"${title}\"" 122 | else 123 | issue_url="https://github.com/${owner_repo}/issues/${issue_number}" 124 | action="commented" 125 | bl_debug "Found existing issue for title \"${title}\": ${issue_url}" 126 | bl_hub_add_issue_comment "${issue_number}" "${message}" 127 | fi 128 | cat </dev/null; then 18 | bl_die "pushd ${1} failed :(" 19 | fi 20 | } 21 | 22 | #safe popd 23 | function bl_spopd(){ 24 | popd >/dev/null || bl_die "popd failed :(" 25 | } 26 | 27 | # Test if a variable contains a number 28 | function bl_is_num(){ 29 | [[ ${1:-invalid} =~ ^-?[0-9\.]*$ ]] 30 | } 31 | 32 | # Retry a command multiple times until it succeeds, with escalating 33 | # delay between attempts. 34 | # Delay is 2 * n + random up to 30s, then 30s + random after that. 35 | # For large numbers of retries the max delay is effectively the retry 36 | # count in minutes. 37 | # Based on: 38 | # https://gist.github.com/sj26/88e1c6584397bb7c13bd11108a579746 39 | # but now quite heavily modified. 40 | function bl_retry { 41 | # Maxiumum amount of fixed delay between attempts 42 | # a random value will still be added. 43 | local -r MAX_BACKOFF=30 44 | local rc 45 | local count 46 | local retries 47 | local backoff 48 | 49 | if [[ ${#} -lt 2 ]]; then 50 | bl_die "retry usage: retry " 51 | fi 52 | 53 | retries=$1 54 | shift 55 | 56 | if ! bl_is_num "${retries}"; then 57 | bl_die "Invalid number of retries: ${retries} for command '${*}'". 58 | fi 59 | 60 | count=0 61 | until eval "$@"; do 62 | # Command failed, otherwise until would have skipped the loop 63 | 64 | # Store return code so it can be reported to the user 65 | rc=$? 66 | count=$((count + 1)) 67 | if [ "${count}" -lt "${retries}" ]; then 68 | # There are still retries left, calculate delay and notify user. 69 | backoff=$((2 * count)) 70 | if [[ "${backoff}" -gt "${MAX_BACKOFF}" ]]; then 71 | backoff=${MAX_BACKOFF} 72 | fi; 73 | 74 | # Add a random amount to the delay to prevent competing processes 75 | # from re-colliding. 76 | wait=$(( backoff + (RANDOM % count) )) 77 | bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $wait seconds..." 78 | sleep $wait 79 | else 80 | # Out of retries :( 81 | bl_error "Retry $count/$retries exited $rc, no more retries left." 82 | return $rc 83 | fi 84 | done 85 | return 0 86 | } 87 | 88 | # retry function that waits a constant number of seconds between attempts. 89 | function bl_retry_constant { 90 | if [[ ${#} -lt 3 ]]; then 91 | bl_die "retry usage: retry " 92 | fi 93 | 94 | local retries=$1; shift 95 | local interval=$1; shift 96 | local count 97 | local rc 98 | local interval 99 | 100 | if ! bl_is_num "${retries}"; then 101 | bl_die "Invalid number of retries: ${retries} for command '${*}'" 102 | fi 103 | 104 | if ! bl_is_num "${interval}"; then 105 | bl_die "Invalid interval in seconds: ${retries} for command '${*}'". 106 | fi 107 | 108 | count=0 109 | until eval "$@"; do 110 | # Command failed, otherwise until would have skipped the loop 111 | 112 | # Store return code so it can be reported to the user 113 | rc=$? 114 | count=$((count + 1)) 115 | if [ "${count}" -lt "${retries}" ]; then 116 | bl_info "'${*}' Retry $count/$retries exited $rc, retrying in $interval seconds..." 117 | sleep "${interval}" 118 | else 119 | # Out of retries :( 120 | bl_error "Retry $count/$retries exited $rc, no more retries left." 121 | return $rc 122 | fi 123 | done 124 | return 0 125 | } 126 | -------------------------------------------------------------------------------- /init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ## Initialisation Functions for the 4 | ## Conjurinc Bash Library 5 | 6 | if (( BASH_VERSINFO[0] < 4 )); then 7 | echo "Bash Lib requires bash v4 or greater" 8 | echo "Current Bash Version: ${BASH_VERSION}" 9 | exit 1 10 | fi 11 | 12 | # Shell Otions 13 | set -euo pipefail 14 | 15 | # This script should be sourced before any of 16 | # the other scripts in this repo. Other scripts 17 | # make use of ${BASH_LIB_DIR} to find each other. 18 | 19 | # Get the relative path to the repo root 20 | # shellcheck disable=SC2086 21 | BASH_LIB_DIR_RELATIVE="$(dirname ${BASH_SOURCE[0]})" 22 | 23 | # Must be set in order to load the filehandling 24 | # module. Will be updated when abs_path is available. 25 | BASH_LIB_DIR="${BASH_LIB_DIR_RELATIVE}" 26 | 27 | # Load the filehandling module for the abspath 28 | # function 29 | for lib in helpers logging filehandling git github k8s test-utils ruby; do 30 | . "${BASH_LIB_DIR_RELATIVE}/${lib}/lib" 31 | done 32 | 33 | # Filter functions and re export only bash-lib functions to subshells 34 | eval "$(declare -F | sed -e 's/-f /-fx /' | grep 'x bl_')" 35 | 36 | # Export the absolute path 37 | # shellcheck disable=SC2086 38 | BASH_LIB_DIR="$(bl_abs_path ${BASH_LIB_DIR_RELATIVE})" 39 | export BASH_LIB_DIR 40 | 41 | # Update Submodules, but ignore any errors. This way it won't fail if it's not in a git repo. 42 | bl_spushd "${BASH_LIB_DIR}" 43 | git submodule update --init --recursive || true 44 | bl_spopd 45 | 46 | export BATS_CMD="${BASH_LIB_DIR}/test-utils/bats/bin/bats" 47 | -------------------------------------------------------------------------------- /k8s/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM google/cloud-sdk 2 | 3 | ARG KUBECTL_CLI_URL 4 | 5 | RUN mkdir -p /src \ 6 | /scripts 7 | WORKDIR /src 8 | COPY platform_login /scripts 9 | 10 | # Install Docker client 11 | RUN apt-get update -y \ 12 | && apt-get install \ 13 | -y \ 14 | apt-transport-https \ 15 | ca-certificates \ 16 | curl \ 17 | gnupg2 \ 18 | software-properties-common \ 19 | wget \ 20 | && curl \ 21 | -fsSL \ 22 | https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg \ 23 | | apt-key add - \ 24 | && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"\ 25 | && apt-get update \ 26 | && apt-get install -y docker-ce \ 27 | && rm -rf /var/lib/apt/lists/* 28 | 29 | # Install kubectl CLI 30 | RUN wget -O \ 31 | /usr/local/bin/kubectl \ 32 | ${KUBECTL_CLI_URL:-https://storage.googleapis.com/kubernetes-release/release/v1.7.6/bin/linux/amd64/kubectl} \ 33 | && chmod +x /usr/local/bin/kubectl -------------------------------------------------------------------------------- /k8s/lib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 4 | 5 | # Sets additional required environment variables that aren't available in the 6 | # secrets.yml file, and performs other preparatory steps 7 | function bl_build_gke_image() { 8 | local image="gke-utils:latest" 9 | local rc=0 10 | docker rmi ${image} || true 11 | bl_spushd "${BASH_LIB_DIR}/k8s" 12 | # Prepare Docker images 13 | docker build --tag "${image}"\ 14 | --build-arg KUBECTL_CLI_URL="${KUBECTL_CLI_URL}" \ 15 | . 1>&2 16 | rc=${?} 17 | bl_spopd 18 | 19 | return ${rc} 20 | } 21 | 22 | # Delete an image from GCR, unless it is has multiple tags pointing to it 23 | # This means another parallel build is using the image and we should 24 | # just untag it to be deleted by the later job 25 | function bl_delete_gke_image() { 26 | local image_and_tag="${1}" 27 | 28 | bl_run_docker_gke_command " 29 | gcloud container images delete --force-delete-tags -q ${image_and_tag} 30 | " 31 | } 32 | 33 | function bl_run_docker_gke_command() { 34 | docker run --rm \ 35 | -i \ 36 | -e DOCKER_REGISTRY_URL \ 37 | -e DOCKER_REGISTRY_PATH \ 38 | -e GCLOUD_SERVICE_KEY="/tmp${GCLOUD_SERVICE_KEY}" \ 39 | -e GCLOUD_CLUSTER_NAME \ 40 | -e GCLOUD_ZONE \ 41 | -e SECRETLESS_IMAGE \ 42 | -e KUBECTL_CLI_URL \ 43 | -e GCLOUD_PROJECT_NAME \ 44 | -v "${GCLOUD_SERVICE_KEY}:/tmp${GCLOUD_SERVICE_KEY}" \ 45 | -v /var/run/docker.sock:/var/run/docker.sock \ 46 | -v ~/.config:/root/.config \ 47 | -v "${PWD}:/src" \ 48 | -w /src \ 49 | "gke-utils:latest" \ 50 | bash -ec " 51 | /scripts/platform_login 52 | ${1} 53 | " 54 | } 55 | -------------------------------------------------------------------------------- /k8s/platform_login: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | function main() { 6 | echo Starting platform login 7 | 8 | gcloud auth activate-service-account \ 9 | --key-file "${GCLOUD_SERVICE_KEY}" 10 | 11 | gcloud container clusters get-credentials \ 12 | "${GCLOUD_CLUSTER_NAME}" \ 13 | --zone "${GCLOUD_ZONE}" \ 14 | --project "${GCLOUD_PROJECT_NAME}" 15 | 16 | docker login "${DOCKER_REGISTRY_URL}" \ 17 | -u oauth2accesstoken \ 18 | -p "$(gcloud auth print-access-token)" 19 | 20 | echo Platform Login Complete 21 | } 22 | 23 | main 24 | -------------------------------------------------------------------------------- /logging/lib: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 3 | 4 | export BASH_LIB_LOG_LEVEL=info 5 | 6 | # Add logging functions here 7 | 8 | function bl_announce() { 9 | echo "++++++++++++++++++++++++++++++++++++++" 10 | echo " " 11 | echo "$@" 12 | echo " " 13 | echo "++++++++++++++++++++++++++++++++++++++" 14 | } 15 | 16 | function bl_check_log_level(){ 17 | local level="${1}" 18 | if [[ ${level} =~ debug|info|warn|error|fatal ]]; 19 | then 20 | return 0 21 | else 22 | echo "${level} is not a valid BASH_LIB_LOG_LEVEL, it should be debug|info|warn|error|fatal" 23 | return 1 24 | fi 25 | } 26 | 27 | function bl_log { 28 | declare -A BASH_LIB_LOG_LEVELS=( [debug]=1 [info]=2 [warn]=3 [error]=4 [fatal]=5 ) 29 | declare -A BASH_LIB_LOG_COLOURS=( [debug]="0;37;40" [info]="0;36;40" [warn]="0;33;40" [error]="1;31;40" [fatal]="1;37;41" ) 30 | local runtime_log_level="${BASH_LIB_LOG_LEVEL}" 31 | local write_log_level="${1}" 32 | local msg="${2}" 33 | local out="${3:-stdout}" 34 | 35 | bl_check_log_level "${runtime_log_level}" 36 | bl_check_log_level "${write_log_level}" 37 | 38 | local runtime_level_num="${BASH_LIB_LOG_LEVELS[${runtime_log_level}]}" 39 | local write_level_num="${BASH_LIB_LOG_LEVELS[${write_log_level}]}" 40 | 41 | if (( write_level_num < runtime_level_num )); then 42 | return 43 | fi 44 | 45 | if [[ "${out}" == "stderr" ]]; then 46 | echo -e "\e[${BASH_LIB_LOG_COLOURS[${write_log_level}]}m${msg}\e[0m" 1>&2 47 | else 48 | echo -e "\e[${BASH_LIB_LOG_COLOURS[${write_log_level}]}m${msg}\e[0m" 49 | fi 50 | } 51 | 52 | function bl_debug(){ 53 | bl_log debug "${*}" 54 | } 55 | 56 | function bl_info(){ 57 | bl_log info "${*}" 58 | } 59 | 60 | function bl_warn(){ 61 | bl_log warn "${*}" 62 | } 63 | 64 | function bl_error(){ 65 | bl_log error "${*}" 66 | } 67 | 68 | function bl_fatal(){ 69 | bl_log fatal "${*}" 70 | } 71 | -------------------------------------------------------------------------------- /ruby/lib: -------------------------------------------------------------------------------- 1 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 2 | 3 | function bl_jq_available(){ 4 | type jq >/dev/null || bl_fail "jq not found :(" 5 | } 6 | 7 | function bl_curl_available(){ 8 | type curl >/dev/null || bl_fail "curl not found :(" 9 | } 10 | 11 | function bl_gem_latest_version(){ 12 | bl_jq_available 13 | bl_curl_available 14 | 15 | gem="${1:-}" 16 | 17 | if [[ -z "${gem}" ]]; then 18 | bl_fail "usage: bl_gem_version " 19 | fi 20 | 21 | curl https://rubygems.org/api/v1/gems/${gem}.json \ 22 | |jq -r '.version' 23 | } 24 | -------------------------------------------------------------------------------- /run-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is an entry point, so init 4 | # is not assumed to have been run 5 | # shellcheck disable=SC2086 6 | . "$(dirname ${BASH_SOURCE[0]})/init" 7 | 8 | bl_info "Checking the changelog complies with keepachangelog.com format" 9 | "${BASH_LIB_DIR}/tests-for-this-repo/validate-changelog" 10 | 11 | bl_info "Running BATS Tests" 12 | "${BASH_LIB_DIR}/tests-for-this-repo/run-bats-tests" 13 | 14 | bl_info "Running Python Lint" 15 | "${BASH_LIB_DIR}/tests-for-this-repo/run-python-lint" 16 | 17 | bl_info "Running gitleaks" 18 | "${BASH_LIB_DIR}/tests-for-this-repo/run-gitleaks" 19 | 20 | bl_info "Sucess! All tests passed." 21 | -------------------------------------------------------------------------------- /secrets.yml: -------------------------------------------------------------------------------- 1 | KUBECTL_CLI_URL: https://storage.googleapis.com/kubernetes-release/release/v1.22.2/bin/linux/amd64/kubectl 2 | 3 | GCLOUD_CLUSTER_NAME: !var ci/gke/rapid/cluster-name 4 | GCLOUD_ZONE: !var ci/gke/zone 5 | GCLOUD_PROJECT_NAME: !var ci/gke/project-name 6 | GCLOUD_SERVICE_KEY: !var:file ci/gke/service-key 7 | 8 | DOCKER_REGISTRY_URL: us.gcr.io 9 | DOCKER_REGISTRY_PATH: us.gcr.io/refreshing-mark-284016 10 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/.gitignore: -------------------------------------------------------------------------------- 1 | /bats-assert-*.tgz 2 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | before_install: 3 | - ./script/install-bats.sh 4 | - git clone --depth 1 https://github.com/ztombol/bats-support ../bats-support 5 | before_script: 6 | - export PATH="${HOME}/.local/bin:${PATH}" 7 | script: 8 | - bats test 9 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/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-03-22 8 | 9 | ### Removed 10 | 11 | - Move `fail()` to `bats-support` 12 | 13 | 14 | ## [0.2.0] - 2016-03-11 15 | 16 | ### Added 17 | 18 | - `refute()` to complement `assert()` 19 | - `npm` support 20 | 21 | ### Fixed 22 | 23 | - Not consuming the `--` when stopping option parsing in 24 | `assert_output`, `refute_output`, `assert_line` and `refute_line` 25 | 26 | 27 | ## 0.1.0 - 2016-02-16 28 | 29 | ### Added 30 | 31 | - Reporting arbitrary failures with `fail()` 32 | - Generic assertions with `assert()` and `assert_equal()` 33 | - Testing exit status with `assert_success()` and `assert_failure()` 34 | - Testing output with `assert_output()` and `refute_output()` 35 | - Testing individual lines with `assert_line()` and `refute_line()` 36 | 37 | 38 | [0.3.0]: https://github.com/ztombol/bats-assert/compare/v0.2.0...v0.3.0 39 | [0.2.0]: https://github.com/ztombol/bats-assert/compare/v0.1.0...v0.2.0 40 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/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 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/load.bash: -------------------------------------------------------------------------------- 1 | source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash" 2 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bats-assert", 3 | "version": "2.0.0", 4 | "description": "Common assertions for Bats", 5 | "homepage": "https://github.com/jasonkarns/bats-assert-1", 6 | "license": "CC0-1.0", 7 | "contributors": [ 8 | "Zoltán Tömböl (https://github.com/ztombol)", 9 | "Sam Stephenson (http://sstephenson.us/)", 10 | "Jason Karns (http://jason.karns.name)", 11 | "Mislav Marohnić (http://mislav.net/)", 12 | "Tim Pope (https://github.com/tpope)" 13 | ], 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/jasonkarns/bats-assert-1.git" 17 | }, 18 | "bugs": { 19 | "url": "https://github.com/jasonkarns/bats-assert-1/issues" 20 | }, 21 | "directories": { 22 | "lib": "src", 23 | "test": "test" 24 | }, 25 | "files": [ 26 | "load.bash", 27 | "src" 28 | ], 29 | "scripts": { 30 | "postversion": "npm publish", 31 | "prepublishOnly": "npm run publish:github", 32 | "publish:github": "git push --follow-tags" 33 | }, 34 | "peerDependencies": { 35 | "bats-support": "git+https://github.com/ztombol/bats-support.git#v0.2.0" 36 | }, 37 | "keywords": [ 38 | "bats", 39 | "bash", 40 | "shell", 41 | "test", 42 | "unit", 43 | "assert", 44 | "assertion", 45 | "helper" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/script/install-bats.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o errexit 3 | set -o xtrace 4 | 5 | git clone --depth 1 https://github.com/sstephenson/bats 6 | cd bats && ./install.sh "${HOME}/.local" && cd .. && rm -rf bats 7 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-11-assert.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test 'assert() : returns 0 if evaluates to TRUE' { 6 | run assert true 7 | [ "$status" -eq 0 ] 8 | [ "${#lines[@]}" -eq 0 ] 9 | } 10 | 11 | @test 'assert() : returns 1 and displays if it evaluates to FALSE' { 12 | run assert false 13 | [ "$status" -eq 1 ] 14 | [ "${#lines[@]}" -eq 3 ] 15 | [ "${lines[0]}" == '-- assertion failed --' ] 16 | [ "${lines[1]}" == 'expression : false' ] 17 | [ "${lines[2]}" == '--' ] 18 | } 19 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-12-assert_equal.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test 'assert_equal() : returns 0 if equals ' { 6 | run assert_equal 'a' 'a' 7 | [ "$status" -eq 0 ] 8 | [ "${#lines[@]}" -eq 0 ] 9 | } 10 | 11 | @test 'assert_equal() : returns 1 and displays details if does not equal ' { 12 | run assert_equal 'a' 'b' 13 | [ "$status" -eq 1 ] 14 | [ "${#lines[@]}" -eq 4 ] 15 | [ "${lines[0]}" == '-- values do not equal --' ] 16 | [ "${lines[1]}" == 'expected : b' ] 17 | [ "${lines[2]}" == 'actual : a' ] 18 | [ "${lines[3]}" == '--' ] 19 | } 20 | 21 | @test 'assert_equal() : displays details in multi-line format if is longer than one line' { 22 | run assert_equal $'a 0\na 1' 'b' 23 | [ "$status" -eq 1 ] 24 | [ "${#lines[@]}" -eq 7 ] 25 | [ "${lines[0]}" == '-- values do not equal --' ] 26 | [ "${lines[1]}" == 'expected (1 lines):' ] 27 | [ "${lines[2]}" == ' b' ] 28 | [ "${lines[3]}" == 'actual (2 lines):' ] 29 | [ "${lines[4]}" == ' a 0' ] 30 | [ "${lines[5]}" == ' a 1' ] 31 | [ "${lines[6]}" == '--' ] 32 | } 33 | 34 | @test 'assert_equal() : displays details in multi-line format if is longer than one line' { 35 | run assert_equal 'a' $'b 0\nb 1' 36 | [ "$status" -eq 1 ] 37 | [ "${#lines[@]}" -eq 7 ] 38 | [ "${lines[0]}" == '-- values do not equal --' ] 39 | [ "${lines[1]}" == 'expected (2 lines):' ] 40 | [ "${lines[2]}" == ' b 0' ] 41 | [ "${lines[3]}" == ' b 1' ] 42 | [ "${lines[4]}" == 'actual (1 lines):' ] 43 | [ "${lines[5]}" == ' a' ] 44 | [ "${lines[6]}" == '--' ] 45 | } 46 | 47 | @test 'assert_equal() : performs literal matching' { 48 | run assert_equal 'a' '*' 49 | [ "$status" -eq 1 ] 50 | } 51 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-13-assert_success.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "assert_success(): returns 0 if \`\$status' is 0" { 6 | run true 7 | run assert_success 8 | [ "$status" -eq 0 ] 9 | [ "${#lines[@]}" -eq 0 ] 10 | } 11 | 12 | @test "assert_success(): returns 1 and displays details if \`\$status' is not 0" { 13 | run bash -c 'echo "a" 14 | exit 1' 15 | run assert_success 16 | [ "$status" -eq 1 ] 17 | [ "${#lines[@]}" -eq 4 ] 18 | [ "${lines[0]}" == '-- command failed --' ] 19 | [ "${lines[1]}" == 'status : 1' ] 20 | [ "${lines[2]}" == 'output : a' ] 21 | [ "${lines[3]}" == '--' ] 22 | } 23 | 24 | @test "assert_success(): displays \`\$output' in multi-line format if it is longer than one line" { 25 | run bash -c 'printf "a 0\na 1" 26 | exit 1' 27 | run assert_success 28 | [ "$status" -eq 1 ] 29 | [ "${#lines[@]}" -eq 6 ] 30 | [ "${lines[0]}" == '-- command failed --' ] 31 | [ "${lines[1]}" == 'status : 1' ] 32 | [ "${lines[2]}" == 'output (2 lines):' ] 33 | [ "${lines[3]}" == ' a 0' ] 34 | [ "${lines[4]}" == ' a 1' ] 35 | [ "${lines[5]}" == '--' ] 36 | } 37 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-14-assert_failure.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test "assert_failure(): returns 0 if \`\$status' is not 0" { 6 | run false 7 | run assert_failure 8 | [ "$status" -eq 0 ] 9 | [ "${#lines[@]}" -eq 0 ] 10 | } 11 | 12 | @test "assert_failure(): returns 1 and displays details if \`\$status' is 0" { 13 | run bash -c 'echo "a" 14 | exit 0' 15 | run assert_failure 16 | [ "$status" -eq 1 ] 17 | [ "${#lines[@]}" -eq 3 ] 18 | [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] 19 | [ "${lines[1]}" == 'output : a' ] 20 | [ "${lines[2]}" == '--' ] 21 | } 22 | 23 | @test "assert_failure(): displays \`\$output' in multi-line format if it is longer then one line" { 24 | run bash -c 'printf "a 0\na 1" 25 | exit 0' 26 | run assert_failure 27 | [ "$status" -eq 1 ] 28 | [ "${#lines[@]}" -eq 5 ] 29 | [ "${lines[0]}" == '-- command succeeded, but it was expected to fail --' ] 30 | [ "${lines[1]}" == 'output (2 lines):' ] 31 | [ "${lines[2]}" == ' a 0' ] 32 | [ "${lines[3]}" == ' a 1' ] 33 | [ "${lines[4]}" == '--' ] 34 | } 35 | 36 | @test "assert_failure() : returns 0 if \`\$status' equals " { 37 | run bash -c 'exit 1' 38 | run assert_failure 1 39 | [ "$status" -eq 0 ] 40 | [ "${#lines[@]}" -eq 0 ] 41 | } 42 | 43 | @test "assert_failure() : returns 1 and displays details if \`\$status' does not equal " { 44 | run bash -c 'echo "a" 45 | exit 1' 46 | run assert_failure 2 47 | [ "$status" -eq 1 ] 48 | [ "${#lines[@]}" -eq 5 ] 49 | [ "${lines[0]}" == '-- command failed as expected, but status differs --' ] 50 | [ "${lines[1]}" == 'expected : 2' ] 51 | [ "${lines[2]}" == 'actual : 1' ] 52 | [ "${lines[3]}" == 'output : a' ] 53 | [ "${lines[4]}" == '--' ] 54 | } 55 | 56 | @test "assert_failure() : displays \`\$output' in multi-line format if it is longer then one line" { 57 | run bash -c 'printf "a 0\na 1" 58 | exit 1' 59 | run assert_failure 2 60 | [ "$status" -eq 1 ] 61 | [ "${#lines[@]}" -eq 7 ] 62 | [ "${lines[0]}" == '-- command failed as expected, but status differs --' ] 63 | [ "${lines[1]}" == 'expected : 2' ] 64 | [ "${lines[2]}" == 'actual : 1' ] 65 | [ "${lines[3]}" == 'output (2 lines):' ] 66 | [ "${lines[4]}" == ' a 0' ] 67 | [ "${lines[5]}" == ' a 1' ] 68 | [ "${lines[6]}" == '--' ] 69 | } 70 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-16-refute_output.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | 6 | # 7 | # Literal matching 8 | # 9 | 10 | # Correctness 11 | @test "refute_output() : returns 0 if does not equal \`\$output'" { 12 | run echo 'b' 13 | run refute_output 'a' 14 | [ "$status" -eq 0 ] 15 | [ "${#lines[@]}" -eq 0 ] 16 | } 17 | 18 | @test "refute_output() : returns 1 and displays details if equals \`\$output'" { 19 | run echo 'a' 20 | run refute_output 'a' 21 | [ "$status" -eq 1 ] 22 | [ "${#lines[@]}" -eq 3 ] 23 | [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ] 24 | [ "${lines[1]}" == 'output : a' ] 25 | [ "${lines[2]}" == '--' ] 26 | } 27 | 28 | @test 'refute_output(): succeeds if output is empty' { 29 | run echo '' 30 | run refute_output 31 | [ "$status" -eq 0 ] 32 | [ "${#lines[@]}" -eq 0 ] 33 | } 34 | 35 | @test 'refute_output(): fails if output is non-empty' { 36 | run echo 'a' 37 | run refute_output 38 | [ "$status" -eq 1 ] 39 | [ "${#lines[@]}" -eq 3 ] 40 | [ "${lines[0]}" == '-- output non-empty, but expected no output --' ] 41 | [ "${lines[1]}" == 'output : a' ] 42 | [ "${lines[2]}" == '--' ] 43 | } 44 | 45 | @test 'refute_output() - : reads from STDIN' { 46 | run echo '-' 47 | run refute_output - < from STDIN' { 55 | run echo '--stdin' 56 | run refute_output --stdin <: displays details in multi-line format if necessary' { 65 | run printf 'a 0\na 1' 66 | run refute_output $'a 0\na 1' 67 | [ "$status" -eq 1 ] 68 | [ "${#lines[@]}" -eq 5 ] 69 | [ "${lines[0]}" == '-- output equals, but it was expected to differ --' ] 70 | [ "${lines[1]}" == 'output (2 lines):' ] 71 | [ "${lines[2]}" == ' a 0' ] 72 | [ "${lines[3]}" == ' a 1' ] 73 | [ "${lines[4]}" == '--' ] 74 | } 75 | 76 | # Options 77 | @test 'refute_output() : performs literal matching by default' { 78 | run echo 'a' 79 | run refute_output '*' 80 | [ "$status" -eq 0 ] 81 | } 82 | 83 | 84 | # 85 | # Partial matching: `-p' and `--partial' 86 | # 87 | 88 | # Options 89 | test_p_partial () { 90 | run echo 'abc' 91 | run refute_output "$1" 'd' 92 | [ "$status" -eq 0 ] 93 | [ "${#lines[@]}" -eq 0 ] 94 | } 95 | 96 | @test 'refute_output() -p : enables partial matching' { 97 | test_p_partial -p 98 | } 99 | 100 | @test 'refute_output() --partial : enables partial matching' { 101 | test_p_partial --partial 102 | } 103 | 104 | # Correctness 105 | @test "refute_output() --partial : returns 0 if is not a substring in \`\$output'" { 106 | run printf 'a\nb\nc' 107 | run refute_output --partial 'd' 108 | [ "$status" -eq 0 ] 109 | [ "${#lines[@]}" -eq 0 ] 110 | } 111 | 112 | @test "refute_output() --partial : returns 1 and displays details if is a substring in \`\$output'" { 113 | run echo 'a' 114 | run refute_output --partial 'a' 115 | [ "$status" -eq 1 ] 116 | [ "${#lines[@]}" -eq 4 ] 117 | [ "${lines[0]}" == '-- output should not contain substring --' ] 118 | [ "${lines[1]}" == 'substring : a' ] 119 | [ "${lines[2]}" == 'output : a' ] 120 | [ "${lines[3]}" == '--' ] 121 | } 122 | 123 | # Output formatting 124 | @test 'refute_output() --partial : displays details in multi-line format if necessary' { 125 | run printf 'a 0\na 1' 126 | run refute_output --partial 'a' 127 | [ "$status" -eq 1 ] 128 | [ "${#lines[@]}" -eq 7 ] 129 | [ "${lines[0]}" == '-- output should not contain substring --' ] 130 | [ "${lines[1]}" == 'substring (1 lines):' ] 131 | [ "${lines[2]}" == ' a' ] 132 | [ "${lines[3]}" == 'output (2 lines):' ] 133 | [ "${lines[4]}" == ' a 0' ] 134 | [ "${lines[5]}" == ' a 1' ] 135 | [ "${lines[6]}" == '--' ] 136 | } 137 | 138 | 139 | # 140 | # Regular expression matching: `-e' and `--regexp' 141 | # 142 | 143 | # Options 144 | test_r_regexp () { 145 | run echo 'abc' 146 | run refute_output "$1" '^d' 147 | [ "$status" -eq 0 ] 148 | [ "${#lines[@]}" -eq 0 ] 149 | } 150 | 151 | @test 'refute_output() -e : enables regular expression matching' { 152 | test_r_regexp -e 153 | } 154 | 155 | @test 'refute_output() --regexp : enables regular expression matching' { 156 | test_r_regexp --regexp 157 | } 158 | 159 | # Correctness 160 | @test "refute_output() --regexp : returns 0 if does not match \`\$output'" { 161 | run printf 'a\nb\nc' 162 | run refute_output --regexp '.*d.*' 163 | [ "$status" -eq 0 ] 164 | [ "${#lines[@]}" -eq 0 ] 165 | } 166 | 167 | @test "refute_output() --regexp : returns 1 and displays details if matches \`\$output'" { 168 | run echo 'a' 169 | run refute_output --regexp '.*a.*' 170 | [ "$status" -eq 1 ] 171 | [ "${#lines[@]}" -eq 4 ] 172 | [ "${lines[0]}" == '-- regular expression should not match output --' ] 173 | [ "${lines[1]}" == 'regexp : .*a.*' ] 174 | [ "${lines[2]}" == 'output : a' ] 175 | [ "${lines[3]}" == '--' ] 176 | } 177 | 178 | # Output formatting 179 | @test 'refute_output() --regexp : displays details in multi-line format if necessary' { 180 | run printf 'a 0\na 1' 181 | run refute_output --regexp '.*a.*' 182 | [ "$status" -eq 1 ] 183 | [ "${#lines[@]}" -eq 7 ] 184 | [ "${lines[0]}" == '-- regular expression should not match output --' ] 185 | [ "${lines[1]}" == 'regexp (1 lines):' ] 186 | [ "${lines[2]}" == ' .*a.*' ] 187 | [ "${lines[3]}" == 'output (2 lines):' ] 188 | [ "${lines[4]}" == ' a 0' ] 189 | [ "${lines[5]}" == ' a 1' ] 190 | [ "${lines[6]}" == '--' ] 191 | } 192 | 193 | # Error handling 194 | @test 'refute_output() --regexp : returns 1 and displays an error message if is not a valid extended regular expression' { 195 | run refute_output --regexp '[.*' 196 | [ "$status" -eq 1 ] 197 | [ "${#lines[@]}" -eq 3 ] 198 | [ "${lines[0]}" == '-- ERROR: refute_output --' ] 199 | [ "${lines[1]}" == "Invalid extended regular expression: \`[.*'" ] 200 | [ "${lines[2]}" == '--' ] 201 | } 202 | 203 | 204 | # 205 | # Common 206 | # 207 | 208 | @test "refute_output(): \`--partial' and \`--regexp' are mutually exclusive" { 209 | run refute_output --partial --regexp 210 | [ "$status" -eq 1 ] 211 | [ "${#lines[@]}" -eq 3 ] 212 | [ "${lines[0]}" == '-- ERROR: refute_output --' ] 213 | [ "${lines[1]}" == "\`--partial' and \`--regexp' are mutually exclusive" ] 214 | [ "${lines[2]}" == '--' ] 215 | } 216 | 217 | @test "refute_output(): \`--' stops parsing options" { 218 | run echo '--' 219 | run refute_output -- '-p' 220 | [ "$status" -eq 0 ] 221 | [ "${#lines[@]}" -eq 0 ] 222 | } 223 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/50-assert-19-refute.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | @test 'refute() : returns 0 if evaluates to FALSE' { 6 | run refute false 7 | [ "$status" -eq 0 ] 8 | [ "${#lines[@]}" -eq 0 ] 9 | } 10 | 11 | @test 'refute() : returns 1 and displays if it evaluates to TRUE' { 12 | run refute true 13 | [ "$status" -eq 1 ] 14 | [ "${#lines[@]}" -eq 3 ] 15 | [ "${lines[0]}" == '-- assertion succeeded, but it was expected to fail --' ] 16 | [ "${lines[1]}" == 'expression : true' ] 17 | [ "${lines[2]}" == '--' ] 18 | } 19 | -------------------------------------------------------------------------------- /test-utils/bats-assert-1/test/test_helper.bash: -------------------------------------------------------------------------------- 1 | setup() { 2 | export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.." 3 | export TEST_DEPS_DIR="${TEST_DEPS_DIR-${TEST_MAIN_DIR}/..}" 4 | 5 | # Load dependencies. 6 | load "${TEST_DEPS_DIR}/bats-support/load.bash" 7 | 8 | # Load library. 9 | load '../load' 10 | } 11 | -------------------------------------------------------------------------------- /test-utils/bats-support/.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | before_install: 3 | - ./script/install-bats.sh 4 | before_script: 5 | - export PATH="${HOME}/.local/bin:${PATH}" 6 | script: 7 | - bats test 8 | -------------------------------------------------------------------------------- /test-utils/bats-support/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-30 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 | -------------------------------------------------------------------------------- /test-utils/bats-support/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 | -------------------------------------------------------------------------------- /test-utils/bats-support/README.md: -------------------------------------------------------------------------------- 1 | *__Important:__ `bats-core` has been renamed to `bats-support`. GitHub 2 | automatically redirects all references, e.g. submodules and clones will 3 | continue to work, but you are encouraged to [update][github-rename] 4 | them. Version numbering continues where `bats-core` left off.* 5 | 6 | [github-rename]: https://help.github.com/articles/renaming-a-repository/ 7 | 8 | - - - - - 9 | 10 | # bats-support 11 | 12 | [![GitHub license](https://img.shields.io/badge/license-CC0-blue.svg)](https://raw.githubusercontent.com/ztombol/bats-support/master/LICENSE) 13 | [![GitHub release](https://img.shields.io/github/release/ztombol/bats-support.svg)](https://github.com/ztombol/bats-support/releases/latest) 14 | [![Build Status](https://travis-ci.org/ztombol/bats-support.svg?branch=master)](https://travis-ci.org/ztombol/bats-support) 15 | 16 | `bats-support` is a supporting library providing common functions to 17 | test helper libraries written for [Bats][bats]. 18 | 19 | Features: 20 | - [error reporting](#error-reporting) 21 | - [output formatting](#output-formatting) 22 | - [language tools](#language-and-execution) 23 | 24 | See the [shared documentation][bats-docs] to learn how to install and 25 | load this library. 26 | 27 | If you want to use this library in your own helpers or just want to 28 | learn about its internals see the developer documentation in the [source 29 | files](src). 30 | 31 | 32 | ## Error reporting 33 | 34 | ### `fail` 35 | 36 | Display an error message and fail. This function provides a convenient 37 | way to report failure in arbitrary situations. You can use it to 38 | implement your own helpers when the ones available do not meet your 39 | needs. Other functions use it internally as well. 40 | 41 | ```bash 42 | @test 'fail()' { 43 | fail 'this test always fails' 44 | } 45 | ``` 46 | 47 | The message can also be specified on the standard input. 48 | 49 | ```bash 50 | @test 'fail() with pipe' { 51 | echo 'this test always fails' | fail 52 | } 53 | ``` 54 | 55 | This function always fails and simply outputs the given message. 56 | 57 | ``` 58 | this test always fails 59 | ``` 60 | 61 | 62 | ## Output formatting 63 | 64 | Many test helpers need to produce human readable output. This library 65 | provides a simple way to format simple messages and key value pairs, and 66 | display them on the standard error. 67 | 68 | 69 | ### Simple message 70 | 71 | Simple messages without structure, e.g. one-line error messages, are 72 | simply wrapped in a header and a footer to help them stand out. 73 | 74 | ``` 75 | -- ERROR: assert_output -- 76 | `--partial' and `--regexp' are mutually exclusive 77 | -- 78 | ``` 79 | 80 | 81 | ### Key-Value pairs 82 | 83 | Some helpers, e.g. [assertions][bats-assert], structure output as 84 | key-value pairs. This library provides two ways to format them. 85 | 86 | When the value is one line long, a pair can be displayed in a columnar 87 | fashion called ***two-column*** format. 88 | 89 | ``` 90 | -- output differs -- 91 | expected : want 92 | actual : have 93 | -- 94 | ``` 95 | 96 | When the value is longer than one line, the key and value must be 97 | displayed on separate lines. First, the key is displayed along with the 98 | number of lines in the value. Then, the value, indented by two spaces 99 | for added readability, starting on the next line. This is called 100 | ***multi-line*** format. 101 | 102 | ``` 103 | -- command failed -- 104 | status : 1 105 | output (2 lines): 106 | Error! Something went terribly wrong! 107 | Our engineers are panicing... \`>`;/ 108 | -- 109 | ``` 110 | 111 | Sometimes, for clarity, it is a good idea to display related values also 112 | in this format, even if they are just one line long. 113 | 114 | ``` 115 | -- output differs -- 116 | expected (1 lines): 117 | want 118 | actual (3 lines): 119 | have 1 120 | have 2 121 | have 3 122 | -- 123 | ``` 124 | 125 | ## Language and Execution 126 | 127 | ### Restricting invocation to specific locations 128 | 129 | Sometimes a helper may work properly only when called from a certain 130 | location. Because it depends on variables to be set or some other side 131 | effect. 132 | 133 | A good example is cleaning up temporary files only if the test has 134 | succeeded. The outcome of a test is only available in `teardown`. Thus, 135 | to avoid programming mistakes, it makes sense to restrict such a 136 | clean-up helper to that function. 137 | 138 | `batslib_is_caller` checks the call stack and returns `0` if the caller 139 | was invoked from a given function, and `1` otherwise. This function 140 | becomes really useful with the `--indirect` option, which allows calls 141 | through intermediate functions, e.g. the calling function may be called 142 | from a function that was called from the given function. 143 | 144 | Staying with the example above, the following code snippet implements a 145 | helper that is restricted to `teardown` or any function called 146 | indirectly from it. 147 | 148 | ```shell 149 | clean_up() { 150 | # Check caller. 151 | if batslib_is_caller --indirect 'teardown'; then 152 | echo "Must be called from \`teardown'" \ 153 | | batslib_decorate 'ERROR: clean_up' \ 154 | | fail 155 | return $? 156 | fi 157 | 158 | # Body goes here... 159 | } 160 | ``` 161 | 162 | In some cases a helper may be called from multiple locations. For 163 | example, a logging function that uses the test name, description or 164 | number, information only available in `setup`, `@test` or `teardown`, to 165 | distinguish entries. The following snippet implements this restriction. 166 | 167 | ```shell 168 | log_test() { 169 | # Check caller. 170 | if ! ( batslib_is_caller --indirect 'setup' \ 171 | || batslib_is_caller --indirect "$BATS_TEST_NAME" \ 172 | || batslib_is_caller --indirect 'teardown' ) 173 | then 174 | echo "Must be called from \`setup', \`@test' or \`teardown'" \ 175 | | batslib_decorate 'ERROR: log_test' \ 176 | | fail 177 | return $? 178 | fi 179 | 180 | # Body goes here... 181 | } 182 | ``` 183 | 184 | 185 | 186 | 187 | [bats]: https://github.com/sstephenson/bats 188 | [bats-docs]: https://github.com/ztombol/bats-docs 189 | [bats-assert]: https://github.com/ztombol/bats-assert 190 | -------------------------------------------------------------------------------- /test-utils/bats-support/load.bash: -------------------------------------------------------------------------------- 1 | source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash" 2 | source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash" 3 | source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash" 4 | -------------------------------------------------------------------------------- /test-utils/bats-support/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bats-support", 3 | "version": "0.3.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /test-utils/bats-support/script/install-bats.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -o errexit 3 | set -o xtrace 4 | 5 | git clone --depth 1 https://github.com/sstephenson/bats 6 | cd bats && ./install.sh "${HOME}/.local" && cd .. && rm -rf bats 7 | -------------------------------------------------------------------------------- /test-utils/bats-support/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 | -------------------------------------------------------------------------------- /test-utils/bats-support/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 | -------------------------------------------------------------------------------- /test-utils/bats-support/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 | 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 | cat - 277 | echo '--' 278 | echo 279 | } 280 | -------------------------------------------------------------------------------- /test-utils/bats-support/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 | -------------------------------------------------------------------------------- /test-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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-utils/bats-support/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 | -------------------------------------------------------------------------------- /test-utils/bats-support/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-utils/bats-support/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-utils/bats-support/test/test_helper.bash: -------------------------------------------------------------------------------- 1 | setup() { 2 | export TEST_MAIN_DIR="${BATS_TEST_DIRNAME}/.." 3 | 4 | # Load library. 5 | load '../load' 6 | } 7 | -------------------------------------------------------------------------------- /test-utils/bats/.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 'v1.1.0.{build}' 2 | 3 | build: off 4 | 5 | # This presumes that Git bash is installed at `C:\Program Files\Git` and the 6 | # bash we're using is `C:\Program Files\Git\bin\bash.exe`. 7 | # 8 | # If instead it finds the Windows Subsystem for Linux bash at 9 | # `C:\Windows\System32\bash.exe`, it will fail with an error like: 10 | # /mnt/c/.../bats-core/test/test_helper.bash: line 1: 11 | # syntax error near unexpected token `$'{\r'' 12 | test_script: 13 | - where bash 14 | - bash --version 15 | - bash -c 'export' 16 | - bash -c 'time PATH="/usr/bin:${PATH}" bin/bats test' 17 | -------------------------------------------------------------------------------- /test-utils/bats/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh eol=lf 3 | libexec/* eol=lf 4 | -------------------------------------------------------------------------------- /test-utils/bats/.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | os: 4 | - linux 5 | 6 | env: 7 | - BASHVER= 8 | - BASHVER=3.2 9 | - BASHVER=4.0 10 | - BASHVER=4.1 11 | - BASHVER=4.2 12 | - BASHVER=4.3 13 | - BASHVER=4.4 14 | 15 | matrix: 16 | include: 17 | - os: osx 18 | 19 | services: 20 | - docker 21 | 22 | script: 23 | - | 24 | if [[ "$TRAVIS_OS_NAME" == 'linux' && -n "$BASHVER" ]]; then 25 | docker build --build-arg bashver=${BASHVER} --tag bats/bats:bash-${BASHVER} . 26 | docker run -it bash:${BASHVER} --version 27 | time docker run -it bats/bats:bash-${BASHVER} --tap /opt/bats/test 28 | else 29 | time bin/bats --tap test 30 | fi 31 | 32 | notifications: 33 | email: 34 | on_success: never 35 | -------------------------------------------------------------------------------- /test-utils/bats/AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Martin (https://control-plane.io/) 2 | Bianca Tamayo <hi@biancatamayo.me> (https://biancatamayo.me/) 3 | Jason Karns <jason.karns@gmail.com> (http://jasonkarns.com/) 4 | Mike Bland <mbland@acm.org> (https://mike-bland.com/) 5 | -------------------------------------------------------------------------------- /test-utils/bats/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG bashver=latest 2 | 3 | FROM bash:${bashver} 4 | 5 | RUN ln -s /opt/bats/bin/bats /usr/sbin/bats 6 | 7 | COPY . /opt/bats/ 8 | 9 | ENTRYPOINT ["bash", "/usr/sbin/bats"] 10 | -------------------------------------------------------------------------------- /test-utils/bats/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 bats-core contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- 23 | 24 | * [bats-core] is a continuation of [bats]. Copyright for portions of the 25 | bats-core project are held by Sam Stephenson, 2014 as part of the project 26 | [bats], licensed under MIT: 27 | 28 | Copyright (c) 2014 Sam Stephenson 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining 31 | a copy of this software and associated documentation files (the 32 | "Software"), to deal in the Software without restriction, including 33 | without limitation the rights to use, copy, modify, merge, publish, 34 | distribute, sublicense, and/or sell copies of the Software, and to 35 | permit persons to whom the Software is furnished to do so, subject to 36 | the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be 39 | included in all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 44 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 45 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 46 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 47 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | For details, please see the [version control history][commits]. 50 | 51 | [bats-core]: https://github.com/bats-core/bats-core 52 | [bats]:https://github.com/sstephenson/bats 53 | [commits]:https://github.com/bats-core/bats-core/commits/master 54 | -------------------------------------------------------------------------------- /test-utils/bats/bin/bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export BATS_READLINK='true' 6 | if command -v 'greadlink' >/dev/null; then 7 | BATS_READLINK='greadlink' 8 | elif command -v 'readlink' >/dev/null; then 9 | BATS_READLINK='readlink' 10 | fi 11 | 12 | bats_resolve_link() { 13 | if ! "$BATS_READLINK" "$1"; then 14 | return 0 15 | fi 16 | } 17 | 18 | bats_resolve_absolute_root_dir() { 19 | local cwd="$PWD" 20 | local path="$1" 21 | local result="$2" 22 | local target_dir 23 | local target_name 24 | local original_shell_options="$-" 25 | 26 | # Resolve the parent directory, e.g. /bin => /usr/bin on CentOS (#113). 27 | set -P 28 | 29 | while true; do 30 | target_dir="${path%/*}" 31 | target_name="${path##*/}" 32 | 33 | if [[ "$target_dir" != "$path" ]]; then 34 | cd "$target_dir" 35 | fi 36 | 37 | if [[ -L "$target_name" ]]; then 38 | path="$(bats_resolve_link "$target_name")" 39 | else 40 | printf -v "$result" -- '%s' "${PWD%/*}" 41 | set +P "-$original_shell_options" 42 | cd "$cwd" 43 | return 44 | fi 45 | done 46 | } 47 | 48 | export BATS_ROOT 49 | bats_resolve_absolute_root_dir "$0" 'BATS_ROOT' 50 | exec "$BATS_ROOT/libexec/bats-core/bats" "$@" 51 | -------------------------------------------------------------------------------- /test-utils/bats/contrib/rpm/bats.spec: -------------------------------------------------------------------------------- 1 | %global provider github.com 2 | %global project bats-core 3 | %global repo bats-core 4 | 5 | Name: bats 6 | Version: 1.1.0 7 | Release: 1%{?dist} 8 | Summary: Bash Automated Testing System 9 | 10 | Group: Development/Libraries 11 | License: MIT 12 | URL: https://%{provider}/%{project}/%{repo} 13 | Source0: https://%{provider}/%{project}/%{repo}/archive/v%{version}.tar.gz 14 | 15 | BuildArch: noarch 16 | 17 | Requires: bash 18 | 19 | %description 20 | Bats is a TAP-compliant testing framework for Bash. 21 | It provides a simple way to verify that the UNIX programs you write behave as expected. 22 | Bats is most useful when testing software written in Bash, but you can use it to test any UNIX program. 23 | 24 | %prep 25 | %setup -q -n %{repo}-%{version} 26 | 27 | %install 28 | mkdir -p ${RPM_BUILD_ROOT}%{_prefix} ${RPM_BUILD_ROOT}%{_libexecdir} ${RPM_BUILD_ROOT}%{_mandir} 29 | ./install.sh ${RPM_BUILD_ROOT}%{_prefix} 30 | 31 | %clean 32 | rm -rf $RPM_BUILD_ROOT 33 | 34 | %check 35 | 36 | %files 37 | %doc README.md LICENSE.md 38 | %{_bindir}/%{name} 39 | %{_libexecdir}/%{repo} 40 | %{_mandir}/man1/%{name}.1.gz 41 | %{_mandir}/man7/%{name}.7.gz 42 | 43 | %changelog 44 | * Tue Jul 08 2018 mbland <mbland@acm.org> - 1.1.0-1 45 | - Increase version to match upstream release 46 | 47 | * Mon Jun 18 2018 pixdrift <support@pixeldrift.net> - 1.0.2-1 48 | - Increase version to match upstream release 49 | - Relocate libraries to bats-core subdirectory 50 | 51 | * Sat Jun 09 2018 pixdrift <support@pixeldrift.net> - 1.0.1-1 52 | - Increase version to match upstream release 53 | 54 | * Fri Jun 08 2018 pixdrift <support@pixeldrift.net> - 1.0.0-1 55 | - Initial package build of forked (bats-core) github project 56 | -------------------------------------------------------------------------------- /test-utils/bats/docs/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This enables automatic code review requests per: 2 | # - https://help.github.com/articles/about-codeowners/ 3 | # - https://help.github.com/articles/enabling-required-reviews-for-pull-requests/ 4 | * @bats-core/bats-core 5 | -------------------------------------------------------------------------------- /test-utils/bats/docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting one of the current [project maintainers](#project-maintainers) listed below. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Project Maintainers 69 | 70 | ### Current Maintainers 71 | 72 | * [Bianca Tamayo][bt-gh] 73 | * [Mike Bland][mb-gh] 74 | * [Jason Karns][jk-gh] 75 | * [Andrew Martin][am-gh] 76 | 77 | ### Past Maintainers 78 | 79 | * Sam Stephenson <<sstephenson@gmail.com>> (Original author) 80 | 81 | ## Attribution 82 | 83 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 84 | available at [http://contributor-covenant.org/version/1/4][version] 85 | 86 | [bt-gh]: https://github.com/btamayo 87 | [mb-gh]: https://github.com/mbland 88 | [jk-gh]: https://github.com/jasonkarns 89 | [am-gh]: https://github.com/sublimino 90 | 91 | [homepage]: https://contributor-covenant.org 92 | [version]: https://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /test-utils/bats/docs/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - [ ] I have reviewed the [Contributor Guidelines][contributor]. 2 | - [ ] I have reviewed the [Code of Conduct][coc] and agree to abide by it 3 | 4 | [contributor]: https://github.com/bats-core/bats-core/blob/master/docs/CONTRIBUTING.md 5 | [coc]: https://github.com/bats-core/bats-core/blob/master/docs/CODE_OF_CONDUCT.md 6 | -------------------------------------------------------------------------------- /test-utils/bats/docs/usage.md: -------------------------------------------------------------------------------- 1 | # Docker Usage Guide 2 | 3 | - [Docker Usage Guide](#docker-usage-guide) 4 | * [Basic Usage](#basic-usage) 5 | * [Docker Gotchas](#docker-gotchas) 6 | * [Extending from the base image](#extending-from-the-base-image) 7 | 8 | ## Basic Usage 9 | 10 | To build and run `bats`' own tests: 11 | ```bash 12 | $ git clone https://github.com/bats-core/bats-core.git 13 | Cloning into 'bats-core'... 14 | remote: Counting objects: 1222, done. 15 | remote: Compressing objects: 100% (53/53), done. 16 | remote: Total 1222 (delta 34), reused 55 (delta 21), pack-reused 1146 17 | Receiving objects: 100% (1222/1222), 327.28 KiB | 1.70 MiB/s, done. 18 | Resolving deltas: 100% (661/661), done. 19 | 20 | $ cd bats-core/ 21 | $ docker build --tag bats:latest . 22 | ... 23 | $ docker run -it bats:latest --tap /opt/bats/test 24 | ``` 25 | 26 | To mount your tests into the container, first build the image as above. Then, for example with `bats`: 27 | ```bash 28 | $ docker run -it -v "$PWD:/opt/bats" bats:latest /opt/bats/test 29 | ``` 30 | This runs the `test/` directory from the bats-core repository inside the bats Docker container. 31 | 32 | For test suites that are intended to run in isolation from the project (i.e. the tests do not depend on project files outside of the test directory), you can mount the test directory by itself and execute the tests like so: 33 | 34 | ```bash 35 | $ docker run -it -v "$PWD/test:/test" bats:latest /test 36 | ``` 37 | 38 | ## Docker Gotchas 39 | 40 | Relying on functionality provided by your environment (ssh keys or agent, installed binaries, fixtures outside the mounted test directory) will fail when running inside Docker. 41 | 42 | `--interactive`/`-i` attaches an interactive terminal and is useful to kill hanging processes (otherwise has to be done via docker stop command). `--tty`/`-t` simulates a tty (often not used, but most similar to test runs from a Bash prompt). Interactivity is important to a user, but not a build, and TTYs are probably more important to a headless build. Everything's least-surprising to a new Docker use if both are used. 43 | 44 | ## Extending from the base image 45 | 46 | Docker operates on a principle of isolation, and bundles all dependencies required into the Docker image. These can be mounted in at runtime (for test files, configuration, etc). For binary dependencies it may be better to extend the base Docker image with further tools and files. 47 | 48 | ```dockerfile 49 | FROM bats 50 | 51 | RUN \ 52 | apk \ 53 | --no-cache \ 54 | --update \ 55 | add \ 56 | openssh 57 | 58 | ``` 59 | -------------------------------------------------------------------------------- /test-utils/bats/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BATS_ROOT="${0%/*}" 6 | PREFIX="$1" 7 | 8 | if [[ -z "$PREFIX" ]]; then 9 | printf '%s\n' \ 10 | "usage: $0 <prefix>" \ 11 | " e.g. $0 /usr/local" >&2 12 | exit 1 13 | fi 14 | 15 | install -d -m 755 "$PREFIX"/{bin,libexec/bats-core,share/man/man{1,7}} 16 | install -m 755 "$BATS_ROOT/bin"/* "$PREFIX/bin" 17 | install -m 755 "$BATS_ROOT/libexec/bats-core"/* "$PREFIX/libexec/bats-core" 18 | install -m 644 "$BATS_ROOT/man/bats.1" "$PREFIX/share/man/man1" 19 | install -m 644 "$BATS_ROOT/man/bats.7" "$PREFIX/share/man/man7" 20 | 21 | echo "Installed Bats to $PREFIX/bin/bats" 22 | -------------------------------------------------------------------------------- /test-utils/bats/libexec/bats-core/bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | version() { 5 | printf 'Bats 1.1.0\n' 6 | } 7 | 8 | usage() { 9 | version 10 | printf 'Usage: bats [-c] [-r] [-p | -t] <test> [<test> ...]\n' 11 | } 12 | 13 | abort() { 14 | printf 'Error: %s\n' "$1" >&2 15 | usage >&2 16 | exit 1 17 | } 18 | 19 | help() { 20 | local line 21 | usage 22 | while read -r line; do 23 | printf '%s\n' "$line" 24 | done <<END_OF_HELP_TEXT 25 | 26 | <test> is the path to a Bats test file, or the path to a directory 27 | containing Bats test files. 28 | 29 | -c, --count Count the number of test cases without running any tests 30 | -h, --help Display this help message 31 | -p, --pretty Show results in pretty format (default for terminals) 32 | -r, --recursive Include tests in subdirectories 33 | -t, --tap Show results in TAP format 34 | -v, --version Display the version number 35 | 36 | For more information, see https://github.com/bats-core/bats-core 37 | 38 | END_OF_HELP_TEXT 39 | } 40 | 41 | expand_path() { 42 | local path="${1%/}" 43 | local dirname="${path%/*}" 44 | local result="$2" 45 | 46 | if [[ "$dirname" == "$path" ]]; then 47 | dirname="$PWD" 48 | else 49 | cd "$dirname" 50 | dirname="$PWD" 51 | cd "$OLDPWD" 52 | fi 53 | printf -v "$result" '%s/%s' "$dirname" "${path##*/}" 54 | } 55 | 56 | export BATS_CWD="$PWD" 57 | export BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$" 58 | export PATH="$BATS_ROOT/libexec/bats-core:$PATH" 59 | 60 | options=() 61 | arguments=() 62 | for arg in "$@"; do 63 | if [[ "${arg:0:1}" = "-" ]]; then 64 | if [[ "${arg:1:1}" = "-" ]]; then 65 | options[${#options[*]}]="${arg:2}" 66 | else 67 | index=1 68 | while option="${arg:$index:1}"; do 69 | if [[ -z "$option" ]]; then 70 | break 71 | fi 72 | options[${#options[*]}]="$option" 73 | let index+=1 74 | done 75 | fi 76 | else 77 | arguments[${#arguments[*]}]="$arg" 78 | fi 79 | done 80 | 81 | unset count_flag pretty recursive 82 | count_flag='' 83 | pretty='' 84 | recursive='' 85 | if [[ -z "${CI:-}" && -t 0 && -t 1 ]]; then 86 | pretty=1 87 | fi 88 | 89 | if [[ "${#options[@]}" -ne 0 ]]; then 90 | for option in "${options[@]}"; do 91 | case "$option" in 92 | "h" | "help" ) 93 | help 94 | exit 0 95 | ;; 96 | "v" | "version" ) 97 | version 98 | exit 0 99 | ;; 100 | "c" | "count" ) 101 | count_flag="-c" 102 | ;; 103 | "r" | "recursive" ) 104 | recursive=1 105 | ;; 106 | "t" | "tap" ) 107 | pretty="" 108 | ;; 109 | "p" | "pretty" ) 110 | pretty=1 111 | ;; 112 | * ) 113 | abort "Bad command line option '-$option'" 114 | ;; 115 | esac 116 | done 117 | fi 118 | 119 | if [[ "${#arguments[@]}" -eq 0 ]]; then 120 | abort 'Must specify at least one <test>' 121 | fi 122 | 123 | filenames=() 124 | for filename in "${arguments[@]}"; do 125 | expand_path "$filename" 'filename' 126 | 127 | if [[ -d "$filename" ]]; then 128 | shopt -s nullglob 129 | if [[ "$recursive" -eq 1 ]]; then 130 | while IFS= read -r -d $'\0' file; do 131 | filenames["${#filenames[@]}"]="$file" 132 | done < <(find "$filename" -type f -name "*.bats" -print0 | sort -z) 133 | else 134 | for suite_filename in "$filename"/*.bats; do 135 | filenames["${#filenames[@]}"]="$suite_filename" 136 | done 137 | fi 138 | shopt -u nullglob 139 | else 140 | filenames["${#filenames[@]}"]="$filename" 141 | fi 142 | done 143 | 144 | if [[ "${#filenames[@]}" -eq 1 ]]; then 145 | command="bats-exec-test" 146 | else 147 | command="bats-exec-suite" 148 | fi 149 | 150 | set -o pipefail execfail 151 | if [[ -z "$pretty" ]]; then 152 | exec "$command" $count_flag "${filenames[@]}" 153 | else 154 | extended_syntax_flag="-x" 155 | formatter="bats-format-tap-stream" 156 | exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | 157 | "$formatter" 158 | fi 159 | -------------------------------------------------------------------------------- /test-utils/bats/libexec/bats-core/bats-exec-suite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | count_only_flag="" 5 | if [[ "$1" = "-c" ]]; then 6 | count_only_flag=1 7 | shift 8 | fi 9 | 10 | extended_syntax_flag="" 11 | if [[ "$1" = "-x" ]]; then 12 | extended_syntax_flag="-x" 13 | shift 14 | fi 15 | 16 | trap "kill 0; exit 1" int 17 | 18 | count=0 19 | for filename in "$@"; do 20 | while IFS= read -r line; do 21 | if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then 22 | let count+=1 23 | fi 24 | done <"$filename" 25 | done 26 | 27 | if [[ -n "$count_only_flag" ]]; then 28 | printf '%d\n' "$count" 29 | exit 30 | fi 31 | 32 | printf '1..%d\n' "$count" 33 | status=0 34 | offset=0 35 | for filename in "$@"; do 36 | index=0 37 | { 38 | IFS= read -r # 1..n 39 | while IFS= read -r line; do 40 | case "$line" in 41 | "begin "* ) 42 | let index+=1 43 | printf '%s\n' "${line/ $index / $(($offset + $index)) }" 44 | ;; 45 | "ok "* | "not ok "* ) 46 | if [[ -z "$extended_syntax_flag" ]]; then 47 | let index+=1 48 | fi 49 | printf '%s\n' "${line/ $index / $(($offset + $index)) }" 50 | if [[ "${line:0:6}" == "not ok" ]]; then 51 | status=1 52 | fi 53 | ;; 54 | * ) 55 | printf '%s\n' "$line" 56 | ;; 57 | esac 58 | done 59 | } < <( bats-exec-test $extended_syntax_flag "$filename" ) 60 | offset=$(($offset + $index)) 61 | done 62 | 63 | exit "$status" 64 | -------------------------------------------------------------------------------- /test-utils/bats/libexec/bats-core/bats-format-tap-stream: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Just stream the TAP output (sans extended syntax) if tput is missing 5 | if ! command -v tput >/dev/null; then 6 | exec grep -v "^begin " 7 | fi 8 | 9 | header_pattern='[0-9]+\.\.[0-9]+' 10 | IFS= read -r header 11 | 12 | if [[ "$header" =~ $header_pattern ]]; then 13 | count="${header:3}" 14 | index=0 15 | failures=0 16 | skipped=0 17 | name="" 18 | count_column_width=$(( ${#count} * 2 + 2 )) 19 | else 20 | # If the first line isn't a TAP plan, print it and pass the rest through 21 | printf "%s\n" "$header" 22 | exec cat 23 | fi 24 | 25 | update_screen_width() { 26 | screen_width="$(tput cols)" 27 | count_column_left=$(( $screen_width - $count_column_width )) 28 | } 29 | 30 | trap update_screen_width WINCH 31 | update_screen_width 32 | 33 | begin() { 34 | go_to_column 0 35 | printf_with_truncation $(( $count_column_left - 1 )) " %s" "$name" 36 | clear_to_end_of_line 37 | go_to_column $count_column_left 38 | printf "%${#count}s/${count}" "$index" 39 | go_to_column 1 40 | } 41 | 42 | pass() { 43 | go_to_column 0 44 | printf " ✓ %s" "$name" 45 | advance 46 | } 47 | 48 | skip() { 49 | local reason="$1" 50 | if [[ -n "$reason" ]]; then 51 | reason=": $reason" 52 | fi 53 | go_to_column 0 54 | printf " - %s (skipped%s)" "$name" "$reason" 55 | advance 56 | } 57 | 58 | fail() { 59 | go_to_column 0 60 | set_color 1 bold 61 | printf " ✗ %s" "$name" 62 | advance 63 | } 64 | 65 | log() { 66 | set_color 1 67 | printf " %s\n" "$1" 68 | clear_color 69 | } 70 | 71 | summary() { 72 | printf "\n%d test" "$count" 73 | if [[ "$count" -ne 1 ]]; then 74 | printf 's' 75 | fi 76 | 77 | printf ", %d failure" "$failures" 78 | if [[ "$failures" -ne 1 ]]; then 79 | printf 's' 80 | fi 81 | 82 | if [[ "$skipped" -gt 0 ]]; then 83 | printf ", %d skipped" "$skipped" 84 | fi 85 | 86 | printf "\n" 87 | } 88 | 89 | printf_with_truncation() { 90 | local width="$1" 91 | shift 92 | local string 93 | 94 | printf -v 'string' -- "$@" 95 | 96 | if [[ "${#string}" -gt "$width" ]]; then 97 | printf "%s..." "${string:0:$(( $width - 4 ))}" 98 | else 99 | printf "%s" "$string" 100 | fi 101 | } 102 | 103 | go_to_column() { 104 | local column="$1" 105 | printf "\x1B[%dG" $(( $column + 1 )) 106 | } 107 | 108 | clear_to_end_of_line() { 109 | printf "\x1B[K" 110 | } 111 | 112 | advance() { 113 | clear_to_end_of_line 114 | printf '\n' 115 | clear_color 116 | } 117 | 118 | set_color() { 119 | local color="$1" 120 | local weight=22 121 | 122 | if [[ "$2" == 'bold' ]]; then 123 | weight=1 124 | fi 125 | printf "\x1B[%d;%dm" $(( 30 + $color )) "$weight" 126 | } 127 | 128 | clear_color() { 129 | printf "\x1B[0m" 130 | } 131 | 132 | _buffer="" 133 | 134 | buffer() { 135 | _buffer="${_buffer}$("$@")" 136 | } 137 | 138 | flush() { 139 | printf "%s" "$_buffer" 140 | _buffer="" 141 | } 142 | 143 | finish() { 144 | flush 145 | printf "\n" 146 | } 147 | 148 | trap finish EXIT 149 | 150 | while IFS= read -r line; do 151 | case "$line" in 152 | "begin "* ) 153 | let index+=1 154 | name="${line#* $index }" 155 | buffer begin 156 | flush 157 | ;; 158 | "ok "* ) 159 | skip_expr="ok $index (.*) # skip ?(([[:print:]]*))?" 160 | if [[ "$line" =~ $skip_expr ]]; then 161 | let skipped+=1 162 | buffer skip "${BASH_REMATCH[2]}" 163 | else 164 | buffer pass 165 | fi 166 | ;; 167 | "not ok "* ) 168 | let failures+=1 169 | buffer fail 170 | ;; 171 | "# "* ) 172 | buffer log "${line:2}" 173 | ;; 174 | esac 175 | done 176 | 177 | buffer summary 178 | -------------------------------------------------------------------------------- /test-utils/bats/libexec/bats-core/bats-preprocess: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | encode_name() { 5 | local name="$1" 6 | local result="test_" 7 | local hex_code 8 | 9 | if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then 10 | name="${name//_/-5f}" 11 | name="${name//-/-2d}" 12 | name="${name// /_}" 13 | result+="$name" 14 | else 15 | local length="${#name}" 16 | local char i 17 | 18 | for ((i=0; i<length; i++)); do 19 | char="${name:$i:1}" 20 | if [[ "$char" = " " ]]; then 21 | result+="_" 22 | elif [[ "$char" =~ [[:alnum:]] ]]; then 23 | result+="$char" 24 | else 25 | printf -v 'hex_code' -- "-%02x" \'"$char" 26 | result+="$hex_code" 27 | fi 28 | done 29 | fi 30 | 31 | printf -v "$2" '%s' "$result" 32 | } 33 | 34 | tests=() 35 | index=0 36 | 37 | while IFS= read -r line; do 38 | line="${line//$'\r'}" 39 | let index+=1 40 | if [[ "$line" =~ $BATS_TEST_PATTERN ]]; then 41 | name="${BASH_REMATCH[1]#[\'\"]}" 42 | name="${name%[\'\"]}" 43 | body="${BASH_REMATCH[2]}" 44 | encode_name "$name" 'encoded_name' 45 | tests["${#tests[@]}"]="$encoded_name" 46 | printf '%s() { bats_test_begin "%s" %d; %s\n' "$encoded_name" "$name" \ 47 | "$index" "$body" 48 | else 49 | printf "%s\n" "$line" 50 | fi 51 | done 52 | 53 | for test_name in "${tests[@]}"; do 54 | printf 'bats_test_function %s\n' "$test_name" 55 | done 56 | -------------------------------------------------------------------------------- /test-utils/bats/man/Makefile: -------------------------------------------------------------------------------- 1 | RONN := ronn 2 | PAGES := bats.1 bats.7 3 | 4 | all: $(PAGES) 5 | 6 | bats.1: bats.1.ronn 7 | $(RONN) -r $< 8 | 9 | bats.7: bats.7.ronn 10 | $(RONN) -r $< 11 | -------------------------------------------------------------------------------- /test-utils/bats/man/README.md: -------------------------------------------------------------------------------- 1 | Bats man pages are generated with [Ronn](http://rtomayko.github.io/ronn/). 2 | 3 | After making changes to `bats.1.ronn` or `bats.7.ronn`, run `make` in 4 | this directory to generate `bats.1` and `bats.7`. **Do not edit the 5 | `bats.1` or `bats.7` files directly.** 6 | -------------------------------------------------------------------------------- /test-utils/bats/man/bats.1: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BATS" "1" "August 2014" "" "" 5 | . 6 | .SH "NAME" 7 | \fBbats\fR \- Bash Automated Testing System 8 | . 9 | .SH "SYNOPSIS" 10 | bats [\-c] [\-p | \-t] \fItest\fR [\fItest\fR \.\.\.] 11 | . 12 | .P 13 | \fItest\fR is the path to a Bats test file, or the path to a directory containing Bats test files\. 14 | . 15 | .SH "DESCRIPTION" 16 | Bats is a TAP\-compliant testing framework for Bash\. It provides a simple way to verify that the UNIX programs you write behave as expected\. 17 | . 18 | .P 19 | A Bats test file is a Bash script with special syntax for defining test cases\. Under the hood, each test case is just a function with a description\. 20 | . 21 | .P 22 | Test cases consist of standard shell commands\. Bats makes use of Bash\'s \fBerrexit\fR (\fBset \-e\fR) option when running test cases\. If every command in the test case exits with a \fB0\fR status code (success), the test passes\. In this way, each line is an assertion of truth\. 23 | . 24 | .P 25 | See \fBbats\fR(7) for more information on writing Bats tests\. 26 | . 27 | .SH "RUNNING TESTS" 28 | To run your tests, invoke the \fBbats\fR interpreter with a path to a test file\. The file\'s test cases are run sequentially and in isolation\. If all the test cases pass, \fBbats\fR exits with a \fB0\fR status code\. If there are any failures, \fBbats\fR exits with a \fB1\fR status code\. 29 | . 30 | .P 31 | You can invoke the \fBbats\fR interpreter with multiple test file arguments, or with a path to a directory containing multiple \fB\.bats\fR files\. Bats will run each test file individually and aggregate the results\. If any test case fails, \fBbats\fR exits with a \fB1\fR status code\. 32 | . 33 | .SH "OPTIONS" 34 | . 35 | .TP 36 | \fB\-c\fR, \fB\-\-count\fR 37 | Count the number of test cases without running any tests 38 | . 39 | .TP 40 | \fB\-h\fR, \fB\-\-help\fR 41 | Display help message 42 | . 43 | .TP 44 | \fB\-p\fR, \fB\-\-pretty\fR 45 | Show results in pretty format (default for terminals) 46 | . 47 | .TP 48 | \fB\-r\fR, \fB\-\-recursive\fR 49 | Include tests in subdirectories 50 | . 51 | .TP 52 | \fB\-t\fR, \fB\-\-tap\fR 53 | Show results in TAP format 54 | . 55 | .TP 56 | \fB\-v\fR, \fB\-\-version\fR 57 | Display the version number 58 | . 59 | .SH "OUTPUT" 60 | When you run Bats from a terminal, you\'ll see output as each test is performed, with a check\-mark next to the test\'s name if it passes or an "X" if it fails\. 61 | . 62 | .IP "" 4 63 | . 64 | .nf 65 | 66 | $ bats addition\.bats 67 | ✓ addition using bc 68 | ✓ addition using dc 69 | 70 | 2 tests, 0 failures 71 | . 72 | .fi 73 | . 74 | .IP "" 0 75 | . 76 | .P 77 | If Bats is not connected to a terminal\-\-in other words, if you run it from a continuous integration system or redirect its output to a file\-\-the results are displayed in human\-readable, machine\-parsable TAP format\. You can force TAP output from a terminal by invoking Bats with the \fB\-\-tap\fR option\. 78 | . 79 | .IP "" 4 80 | . 81 | .nf 82 | 83 | $ bats \-\-tap addition\.bats 84 | 1\.\.2 85 | ok 1 addition using bc 86 | ok 2 addition using dc 87 | . 88 | .fi 89 | . 90 | .IP "" 0 91 | . 92 | .SH "EXIT STATUS" 93 | The \fBbats\fR interpreter exits with a value of \fB0\fR if all test cases pass, or \fB1\fR if one or more test cases fail\. 94 | . 95 | .SH "SEE ALSO" 96 | Bats wiki: \fIhttps://github\.com/bats\-core/bats\-core/wiki/\fR 97 | . 98 | .P 99 | \fBbash\fR(1), \fBbats\fR(7) 100 | . 101 | .SH "COPYRIGHT" 102 | 103 | (c) 2017 Bianca Tamayo (bats-core organization) 104 | (c) 2014 Sam Stephenson 105 | . 106 | .P 107 | Bats is released under the terms of an MIT\-style license\. 108 | -------------------------------------------------------------------------------- /test-utils/bats/man/bats.1.ronn: -------------------------------------------------------------------------------- 1 | bats(1) -- Bash Automated Testing System 2 | ======================================== 3 | 4 | 5 | SYNOPSIS 6 | -------- 7 | 8 | bats [-c] [-p | -t] <test> [<test> ...] 9 | 10 | <test> is the path to a Bats test file, or the path to a directory 11 | containing Bats test files. 12 | 13 | 14 | DESCRIPTION 15 | ----------- 16 | 17 | Bats is a TAP-compliant testing framework for Bash. It provides a simple 18 | way to verify that the UNIX programs you write behave as expected. 19 | 20 | A Bats test file is a Bash script with special syntax for defining 21 | test cases. Under the hood, each test case is just a function with a 22 | description. 23 | 24 | Test cases consist of standard shell commands. Bats makes use of 25 | Bash's `errexit` (`set -e`) option when running test cases. If every 26 | command in the test case exits with a `0` status code (success), the 27 | test passes. In this way, each line is an assertion of truth. 28 | 29 | See `bats`(7) for more information on writing Bats tests. 30 | 31 | 32 | RUNNING TESTS 33 | ------------- 34 | 35 | To run your tests, invoke the `bats` interpreter with a path to a test 36 | file. The file's test cases are run sequentially and in isolation. If 37 | all the test cases pass, `bats` exits with a `0` status code. If there 38 | are any failures, `bats` exits with a `1` status code. 39 | 40 | You can invoke the `bats` interpreter with multiple test file arguments, 41 | or with a path to a directory containing multiple `.bats` files. Bats 42 | will run each test file individually and aggregate the results. If any 43 | test case fails, `bats` exits with a `1` status code. 44 | 45 | 46 | OPTIONS 47 | ------- 48 | 49 | * `-c`, `--count`: 50 | Count the number of test cases without running any tests 51 | * `-h`, `--help`: 52 | Display help message 53 | * `-p`, `--pretty`: 54 | Show results in pretty format (default for terminals) 55 | * `-r`, `--recursive`: 56 | Include tests in subdirectories 57 | * `-t`, `--tap`: 58 | Show results in TAP format 59 | * `-v`, `--version`: 60 | Display the version number 61 | 62 | 63 | OUTPUT 64 | ------ 65 | 66 | When you run Bats from a terminal, you'll see output as each test is 67 | performed, with a check-mark next to the test's name if it passes or 68 | an "X" if it fails. 69 | 70 | $ bats addition.bats 71 | ✓ addition using bc 72 | ✓ addition using dc 73 | 74 | 2 tests, 0 failures 75 | 76 | If Bats is not connected to a terminal--in other words, if you run it 77 | from a continuous integration system or redirect its output to a 78 | file--the results are displayed in human-readable, machine-parsable 79 | TAP format. You can force TAP output from a terminal by invoking Bats 80 | with the `--tap` option. 81 | 82 | $ bats --tap addition.bats 83 | 1..2 84 | ok 1 addition using bc 85 | ok 2 addition using dc 86 | 87 | 88 | EXIT STATUS 89 | ----------- 90 | 91 | The `bats` interpreter exits with a value of `0` if all test cases pass, 92 | or `1` if one or more test cases fail. 93 | 94 | 95 | SEE ALSO 96 | -------- 97 | 98 | Bats wiki: _https://github.com/bats\-core/bats\-core/wiki/_ 99 | 100 | `bash`(1), `bats`(7) 101 | 102 | 103 | COPYRIGHT 104 | --------- 105 | 106 | (c) 2017 Bianca Tamayo (bats-core organization) 107 | (c) 2014 Sam Stephenson 108 | 109 | Bats is released under the terms of an MIT-style license. 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /test-utils/bats/man/bats.7: -------------------------------------------------------------------------------- 1 | .\" generated with Ronn/v0.7.3 2 | .\" http://github.com/rtomayko/ronn/tree/0.7.3 3 | . 4 | .TH "BATS" "7" "November 2013" "" "" 5 | . 6 | .SH "NAME" 7 | \fBbats\fR \- Bats test file format 8 | . 9 | .SH "DESCRIPTION" 10 | A Bats test file is a Bash script with special syntax for defining test cases\. Under the hood, each test case is just a function with a description\. 11 | . 12 | .IP "" 4 13 | . 14 | .nf 15 | 16 | #!/usr/bin/env bats 17 | 18 | @test "addition using bc" { 19 | result="$(echo 2+2 | bc)" 20 | [ "$result" \-eq 4 ] 21 | } 22 | 23 | @test "addition using dc" { 24 | result="$(echo 2 2+p | dc)" 25 | [ "$result" \-eq 4 ] 26 | } 27 | . 28 | .fi 29 | . 30 | .IP "" 0 31 | . 32 | .P 33 | Each Bats test file is evaluated n+1 times, where \fIn\fR is the number of test cases in the file\. The first run counts the number of test cases, then iterates over the test cases and executes each one in its own process\. 34 | . 35 | .SH "THE RUN HELPER" 36 | Many Bats tests need to run a command and then make assertions about its exit status and output\. Bats includes a \fBrun\fR helper that invokes its arguments as a command, saves the exit status and output into special global variables, and then returns with a \fB0\fR status code so you can continue to make assertions in your test case\. 37 | . 38 | .P 39 | For example, let\'s say you\'re testing that the \fBfoo\fR command, when passed a nonexistent filename, exits with a \fB1\fR status code and prints an error message\. 40 | . 41 | .IP "" 4 42 | . 43 | .nf 44 | 45 | @test "invoking foo with a nonexistent file prints an error" { 46 | run foo nonexistent_filename 47 | [ "$status" \-eq 1 ] 48 | [ "$output" = "foo: no such file \'nonexistent_filename\'" ] 49 | } 50 | . 51 | .fi 52 | . 53 | .IP "" 0 54 | . 55 | .P 56 | The \fB$status\fR variable contains the status code of the command, and the \fB$output\fR variable contains the combined contents of the command\'s standard output and standard error streams\. 57 | . 58 | .P 59 | A third special variable, the \fB$lines\fR array, is available for easily accessing individual lines of output\. For example, if you want to test that invoking \fBfoo\fR without any arguments prints usage information on the first line: 60 | . 61 | .IP "" 4 62 | . 63 | .nf 64 | 65 | @test "invoking foo without arguments prints usage" { 66 | run foo 67 | [ "$status" \-eq 1 ] 68 | [ "${lines[0]}" = "usage: foo <filename>" ] 69 | } 70 | . 71 | .fi 72 | . 73 | .IP "" 0 74 | . 75 | .SH "THE LOAD COMMAND" 76 | You may want to share common code across multiple test files\. Bats includes a convenient \fBload\fR command for sourcing a Bash source file relative to the location of the current test file\. For example, if you have a Bats test in \fBtest/foo\.bats\fR, the command 77 | . 78 | .IP "" 4 79 | . 80 | .nf 81 | 82 | load test_helper 83 | . 84 | .fi 85 | . 86 | .IP "" 0 87 | . 88 | .P 89 | will source the script \fBtest/test_helper\.bash\fR in your test file\. This can be useful for sharing functions to set up your environment or load fixtures\. 90 | . 91 | .SH "THE SKIP COMMAND" 92 | Tests can be skipped by using the \fBskip\fR command at the point in a test you wish to skip\. 93 | . 94 | .IP "" 4 95 | . 96 | .nf 97 | 98 | @test "A test I don\'t want to execute for now" { 99 | skip 100 | run foo 101 | [ "$status" \-eq 0 ] 102 | } 103 | . 104 | .fi 105 | . 106 | .IP "" 0 107 | . 108 | .P 109 | Optionally, you may include a reason for skipping: 110 | . 111 | .IP "" 4 112 | . 113 | .nf 114 | 115 | @test "A test I don\'t want to execute for now" { 116 | skip "This command will return zero soon, but not now" 117 | run foo 118 | [ "$status" \-eq 0 ] 119 | } 120 | . 121 | .fi 122 | . 123 | .IP "" 0 124 | . 125 | .P 126 | Or you can skip conditionally: 127 | . 128 | .IP "" 4 129 | . 130 | .nf 131 | 132 | @test "A test which should run" { 133 | if [ foo != bar ]; then 134 | skip "foo isn\'t bar" 135 | fi 136 | 137 | run foo 138 | [ "$status" \-eq 0 ] 139 | } 140 | . 141 | .fi 142 | . 143 | .IP "" 0 144 | . 145 | .SH "SETUP AND TEARDOWN FUNCTIONS" 146 | You can define special \fBsetup\fR and \fBteardown\fR functions which run before and after each test case, respectively\. Use these to load fixtures, set up your environment, and clean up when you\'re done\. 147 | . 148 | .SH "CODE OUTSIDE OF TEST CASES" 149 | You can include code in your test file outside of \fB@test\fR functions\. For example, this may be useful if you want to check for dependencies and fail immediately if they\'re not present\. However, any output that you print in code outside of \fB@test\fR, \fBsetup\fR or \fBteardown\fR functions must be redirected to \fBstderr\fR (\fB>&2\fR)\. Otherwise, the output may cause Bats to fail by polluting the TAP stream on \fBstdout\fR\. 150 | . 151 | .SH "SPECIAL VARIABLES" 152 | There are several global variables you can use to introspect on Bats tests: 153 | . 154 | .IP "\(bu" 4 155 | \fB$BATS_TEST_FILENAME\fR is the fully expanded path to the Bats test file\. 156 | . 157 | .IP "\(bu" 4 158 | \fB$BATS_TEST_DIRNAME\fR is the directory in which the Bats test file is located\. 159 | . 160 | .IP "\(bu" 4 161 | \fB$BATS_TEST_NAMES\fR is an array of function names for each test case\. 162 | . 163 | .IP "\(bu" 4 164 | \fB$BATS_TEST_NAME\fR is the name of the function containing the current test case\. 165 | . 166 | .IP "\(bu" 4 167 | \fB$BATS_TEST_DESCRIPTION\fR is the description of the current test case\. 168 | . 169 | .IP "\(bu" 4 170 | \fB$BATS_TEST_NUMBER\fR is the (1\-based) index of the current test case in the test file\. 171 | . 172 | .IP "\(bu" 4 173 | \fB$BATS_TMPDIR\fR is the location to a directory that may be used to store temporary files\. 174 | . 175 | .IP "" 0 176 | . 177 | .SH "SEE ALSO" 178 | \fBbash\fR(1), \fBbats\fR(1) 179 | -------------------------------------------------------------------------------- /test-utils/bats/man/bats.7.ronn: -------------------------------------------------------------------------------- 1 | bats(7) -- Bats test file format 2 | ================================ 3 | 4 | 5 | DESCRIPTION 6 | ----------- 7 | 8 | A Bats test file is a Bash script with special syntax for defining 9 | test cases. Under the hood, each test case is just a function with a 10 | description. 11 | 12 | #!/usr/bin/env bats 13 | 14 | @test "addition using bc" { 15 | result="$(echo 2+2 | bc)" 16 | [ "$result" -eq 4 ] 17 | } 18 | 19 | @test "addition using dc" { 20 | result="$(echo 2 2+p | dc)" 21 | [ "$result" -eq 4 ] 22 | } 23 | 24 | 25 | Each Bats test file is evaluated n+1 times, where _n_ is the number of 26 | test cases in the file. The first run counts the number of test cases, 27 | then iterates over the test cases and executes each one in its own 28 | process. 29 | 30 | 31 | THE RUN HELPER 32 | -------------- 33 | 34 | Many Bats tests need to run a command and then make assertions about 35 | its exit status and output. Bats includes a `run` helper that invokes 36 | its arguments as a command, saves the exit status and output into 37 | special global variables, and then returns with a `0` status code so 38 | you can continue to make assertions in your test case. 39 | 40 | For example, let's say you're testing that the `foo` command, when 41 | passed a nonexistent filename, exits with a `1` status code and prints 42 | an error message. 43 | 44 | @test "invoking foo with a nonexistent file prints an error" { 45 | run foo nonexistent_filename 46 | [ "$status" -eq 1 ] 47 | [ "$output" = "foo: no such file 'nonexistent_filename'" ] 48 | } 49 | 50 | The `$status` variable contains the status code of the command, and 51 | the `$output` variable contains the combined contents of the command's 52 | standard output and standard error streams. 53 | 54 | A third special variable, the `$lines` array, is available for easily 55 | accessing individual lines of output. For example, if you want to test 56 | that invoking `foo` without any arguments prints usage information on 57 | the first line: 58 | 59 | @test "invoking foo without arguments prints usage" { 60 | run foo 61 | [ "$status" -eq 1 ] 62 | [ "${lines[0]}" = "usage: foo <filename>" ] 63 | } 64 | 65 | 66 | THE LOAD COMMAND 67 | ---------------- 68 | 69 | You may want to share common code across multiple test files. Bats 70 | includes a convenient `load` command for sourcing a Bash source file 71 | relative to the location of the current test file. For example, if you 72 | have a Bats test in `test/foo.bats`, the command 73 | 74 | load test_helper 75 | 76 | will source the script `test/test_helper.bash` in your test file. This 77 | can be useful for sharing functions to set up your environment or load 78 | fixtures. 79 | 80 | 81 | THE SKIP COMMAND 82 | ---------------- 83 | 84 | Tests can be skipped by using the `skip` command at the point in a 85 | test you wish to skip. 86 | 87 | @test "A test I don't want to execute for now" { 88 | skip 89 | run foo 90 | [ "$status" -eq 0 ] 91 | } 92 | 93 | Optionally, you may include a reason for skipping: 94 | 95 | @test "A test I don't want to execute for now" { 96 | skip "This command will return zero soon, but not now" 97 | run foo 98 | [ "$status" -eq 0 ] 99 | } 100 | 101 | Or you can skip conditionally: 102 | 103 | @test "A test which should run" { 104 | if [ foo != bar ]; then 105 | skip "foo isn't bar" 106 | fi 107 | 108 | run foo 109 | [ "$status" -eq 0 ] 110 | } 111 | 112 | 113 | SETUP AND TEARDOWN FUNCTIONS 114 | ---------------------------- 115 | 116 | You can define special `setup` and `teardown` functions which run 117 | before and after each test case, respectively. Use these to load 118 | fixtures, set up your environment, and clean up when you're done. 119 | 120 | 121 | CODE OUTSIDE OF TEST CASES 122 | -------------------------- 123 | 124 | You can include code in your test file outside of `@test` functions. 125 | For example, this may be useful if you want to check for dependencies 126 | and fail immediately if they're not present. However, any output that 127 | you print in code outside of `@test`, `setup` or `teardown` functions 128 | must be redirected to `stderr` (`>&2`). Otherwise, the output may 129 | cause Bats to fail by polluting the TAP stream on `stdout`. 130 | 131 | 132 | SPECIAL VARIABLES 133 | ----------------- 134 | 135 | There are several global variables you can use to introspect on Bats 136 | tests: 137 | 138 | * `$BATS_TEST_FILENAME` is the fully expanded path to the Bats test 139 | file. 140 | * `$BATS_TEST_DIRNAME` is the directory in which the Bats test file is 141 | located. 142 | * `$BATS_TEST_NAMES` is an array of function names for each test case. 143 | * `$BATS_TEST_NAME` is the name of the function containing the current 144 | test case. 145 | * `$BATS_TEST_DESCRIPTION` is the description of the current test 146 | case. 147 | * `$BATS_TEST_NUMBER` is the (1-based) index of the current test case 148 | in the test file. 149 | * `$BATS_TMPDIR` is the location to a directory that may be used to 150 | store temporary files. 151 | 152 | 153 | SEE ALSO 154 | -------- 155 | 156 | `bash`(1), `bats`(1) 157 | -------------------------------------------------------------------------------- /test-utils/bats/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bats", 3 | "version": "1.1.0", 4 | "description": "Bash Automated Testing System", 5 | "homepage": "https://github.com/bats-core/bats-core#readme", 6 | "license": "MIT", 7 | "author": "Sam Stephenson <sstephenson@gmail.com> (http://sstephenson.us/)", 8 | "repository": "github:bats-core/bats-core", 9 | "bugs": "https://github.com/bats-core/bats-core/issues", 10 | "files": [ 11 | "bin", 12 | "libexec", 13 | "man" 14 | ], 15 | "directories": { 16 | "bin": "bin", 17 | "doc": "docs", 18 | "man": "man", 19 | "test": "test" 20 | }, 21 | "scripts": { 22 | "test": "bin/bats test" 23 | }, 24 | "keywords": [ 25 | "bats", 26 | "bash", 27 | "shell", 28 | "test", 29 | "unit" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/dos_line.bats: -------------------------------------------------------------------------------- 1 | @test "foo" { 2 | echo "foo" 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/duplicate-tests.bats: -------------------------------------------------------------------------------- 1 | # This does not fail as expected 2 | @test "gizmo test" { 3 | false 4 | } 5 | 6 | @test "gizmo test" "this does fail, as expected" { 7 | false 8 | } 9 | 10 | # This overrides any previous test from the suite with the same description 11 | @test "gizmo test" { 12 | true 13 | } 14 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/empty.bats: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/bash-lib/238610390647a0ae4b7eeb1e52814026e87853b5/test-utils/bats/test/fixtures/bats/empty.bats -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/environment.bats: -------------------------------------------------------------------------------- 1 | @test "setting a variable" { 2 | variable=1 3 | [ $variable -eq 1 ] 4 | } 5 | 6 | @test "variables do not persist across tests" { 7 | [ -z "$variable" ] 8 | } 9 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/expand_var_in_test_name.bats: -------------------------------------------------------------------------------- 1 | @test "$SUITE: test with variable in name" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/exported_function.bats: -------------------------------------------------------------------------------- 1 | if exported_function; then 2 | a='exported_function' 3 | fi 4 | 5 | @test "failing test" { 6 | echo "a='$a'" 7 | false 8 | } 9 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/failing.bats: -------------------------------------------------------------------------------- 1 | @test "a failing test" { 2 | true 3 | true 4 | eval "( exit ${STATUS:-1} )" 5 | } 6 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/failing_and_passing.bats: -------------------------------------------------------------------------------- 1 | @test "a failing test" { 2 | false 3 | } 4 | 5 | @test "a passing test" { 6 | true 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/failing_helper.bats: -------------------------------------------------------------------------------- 1 | load "test_helper" 2 | 3 | @test "failing helper function" { 4 | true 5 | failing_helper 6 | } 7 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/failing_setup.bats: -------------------------------------------------------------------------------- 1 | setup() { 2 | false 3 | } 4 | 5 | @test "truth" { 6 | true 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/failing_teardown.bats: -------------------------------------------------------------------------------- 1 | teardown() { 2 | eval "( exit ${STATUS:-1} )" 3 | } 4 | 5 | @test "truth" { 6 | [ "$PASS" = 1 ] 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/intact.bats: -------------------------------------------------------------------------------- 1 | @test "dash-e on beginning of line" { 2 | run cat - <<INPUT 3 | -e 4 | INPUT 5 | test "$output" = "-e" 6 | } 7 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/invalid_tap.bats: -------------------------------------------------------------------------------- 1 | echo "This isn't TAP!" 2 | echo "Good day to you" 3 | exit 1 4 | 5 | @test "truth" { 6 | true 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/load.bats: -------------------------------------------------------------------------------- 1 | [ -n "$HELPER_NAME" ] || HELPER_NAME="test_helper" 2 | load "$HELPER_NAME" 3 | 4 | @test "calling a loaded helper" { 5 | help_me 6 | } 7 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/loop_keep_IFS.bats: -------------------------------------------------------------------------------- 1 | # see issue #89 2 | loop_func() { 3 | local search="none one two tree" 4 | local d 5 | 6 | for d in $search ; do 7 | echo $d 8 | done 9 | } 10 | 11 | @test "loop_func" { 12 | run loop_func 13 | [[ "${lines[3]}" == 'tree' ]] 14 | run loop_func 15 | [[ "${lines[2]}" == 'two' ]] 16 | } 17 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/no-final-newline.bats: -------------------------------------------------------------------------------- 1 | @test "no final newline" { 2 | printf 'foo\nbar\nbaz' >&2 && return 1 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/output.bats: -------------------------------------------------------------------------------- 1 | @test "success writing to stdout" { 2 | echo "success stdout 1" 3 | echo "success stdout 2" 4 | } 5 | 6 | @test "success writing to stderr" { 7 | echo "success stderr" >&2 8 | } 9 | 10 | @test "failure writing to stdout" { 11 | echo "failure stdout 1" 12 | echo "failure stdout 2" 13 | false 14 | } 15 | 16 | @test "failure writing to stderr" { 17 | echo "failure stderr" >&2 18 | false 19 | } 20 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/passing.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/passing_and_failing.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a failing test" { 6 | false 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/passing_and_skipping.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a skipped test with no reason" { 6 | skip 7 | } 8 | 9 | @test "a skipped test with a reason" { 10 | skip "for a really good reason" 11 | } 12 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/passing_failing_and_skipping.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | 5 | @test "a skipping test" { 6 | skip 7 | } 8 | 9 | @test "a failing test" { 10 | false 11 | } 12 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/quoted_and_unquoted_test_names.bats: -------------------------------------------------------------------------------- 1 | @test 'single-quoted name' { 2 | true 3 | } 4 | 5 | @test "double-quoted name" { 6 | true 7 | } 8 | 9 | @test unquoted name { 10 | true 11 | } 12 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/reference_unset_parameter.bats: -------------------------------------------------------------------------------- 1 | @test "referencing unset parameter fails" { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/reference_unset_parameter_in_setup.bats: -------------------------------------------------------------------------------- 1 | setup() { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | 6 | teardown() { 7 | echo "should not capture the next line" 8 | [ 1 -eq 2 ] 9 | } 10 | 11 | @test "referencing unset parameter fails in setup" { 12 | : 13 | } 14 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/reference_unset_parameter_in_teardown.bats: -------------------------------------------------------------------------------- 1 | teardown() { 2 | set -u 3 | echo "$unset_parameter" 4 | } 5 | 6 | @test "referencing unset parameter fails in teardown" { 7 | : 8 | } 9 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/setup.bats: -------------------------------------------------------------------------------- 1 | LOG="$BATS_TEST_SUITE_TMPDIR/setup.log" 2 | 3 | setup() { 4 | echo "$BATS_TEST_NAME" >> "$LOG" 5 | } 6 | 7 | @test "one" { 8 | [ "$(tail -n 1 "$LOG")" = "test_one" ] 9 | } 10 | 11 | @test "two" { 12 | [ "$(tail -n 1 "$LOG")" = "test_two" ] 13 | } 14 | 15 | @test "three" { 16 | [ "$(tail -n 1 "$LOG")" = "test_three" ] 17 | } 18 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/single_line.bats: -------------------------------------------------------------------------------- 1 | @test "empty" { } 2 | 3 | @test "passing" { true; } 4 | 5 | @test "input redirection" { diff - <( echo hello ); } <<EOS 6 | hello 7 | EOS 8 | 9 | @test "failing" { false; } 10 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/skipped.bats: -------------------------------------------------------------------------------- 1 | @test "a skipped test" { 2 | skip 3 | } 4 | 5 | @test "a skipped test with a reason" { 6 | skip "a reason" 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/skipped_with_parens.bats: -------------------------------------------------------------------------------- 1 | @test "a skipped test with parentheses in the reason" { 2 | skip "a reason (with parentheses)" 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/source_nonexistent_file.bats: -------------------------------------------------------------------------------- 1 | @test "sourcing nonexistent file fails" { 2 | source "nonexistent file" 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/source_nonexistent_file_in_setup.bats: -------------------------------------------------------------------------------- 1 | setup() { 2 | source "nonexistent file" 3 | } 4 | 5 | teardown() { 6 | echo "should not capture the next line" 7 | [ 1 -eq 2 ] 8 | } 9 | 10 | @test "sourcing nonexistent file fails in setup" { 11 | : 12 | } 13 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/source_nonexistent_file_in_teardown.bats: -------------------------------------------------------------------------------- 1 | teardown() { 2 | source "nonexistent file" 3 | } 4 | 5 | @test "sourcing nonexistent file fails in teardown" { 6 | : 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/teardown.bats: -------------------------------------------------------------------------------- 1 | LOG="$BATS_TEST_SUITE_TMPDIR/teardown.log" 2 | 3 | teardown() { 4 | echo "$BATS_TEST_NAME" >> "$LOG" 5 | } 6 | 7 | @test "one" { 8 | true 9 | } 10 | 11 | @test "two" { 12 | false 13 | } 14 | 15 | @test "three" { 16 | true 17 | } 18 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/test_helper.bash: -------------------------------------------------------------------------------- 1 | help_me() { 2 | true 3 | } 4 | 5 | failing_helper() { 6 | false 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/unofficial_bash_strict_mode.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/unofficial_bash_strict_mode.bats: -------------------------------------------------------------------------------- 1 | load unofficial_bash_strict_mode 2 | @test "unofficial Bash strict mode conditions met" { 3 | : 4 | } 5 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/whitespace.bats: -------------------------------------------------------------------------------- 1 | @test "no extra whitespace" { 2 | : 3 | } 4 | 5 | @test "tab at beginning of line" { 6 | : 7 | } 8 | 9 | @test "tab before description" { 10 | : 11 | } 12 | 13 | @test "tab before opening brace" { 14 | : 15 | } 16 | 17 | @test "tabs at beginning of line and before description" { 18 | : 19 | } 20 | 21 | @test "tabs at beginning, before description, before brace" { 22 | : 23 | } 24 | 25 | @test "extra whitespace around single-line test" { :; } 26 | 27 | @test "no extra whitespace around single-line test" {:;} 28 | 29 | @test parse unquoted name between extra whitespace {:;} 30 | 31 | @test { {:;} # unquote single brace is a valid description 32 | 33 | @test ' {:;} # empty name from single quote 34 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/bats/without_trailing_newline.bats: -------------------------------------------------------------------------------- 1 | @test "truth" { 2 | true 3 | } -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyberark/bash-lib/238610390647a0ae4b7eeb1e52814026e87853b5/test-utils/bats/test/fixtures/suite/empty/.gitkeep -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/multiple/a.bats: -------------------------------------------------------------------------------- 1 | @test "truth" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/multiple/b.bats: -------------------------------------------------------------------------------- 1 | @test "more truth" { 2 | true 3 | } 4 | 5 | @test "quasi-truth" { 6 | [ -z "$FLUNK" ] 7 | } 8 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/recursive/subsuite/test2.bats: -------------------------------------------------------------------------------- 1 | @test "another passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/recursive/test.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/fixtures/suite/single/test.bats: -------------------------------------------------------------------------------- 1 | @test "a passing test" { 2 | true 3 | } 4 | -------------------------------------------------------------------------------- /test-utils/bats/test/install.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | 5 | INSTALL_DIR= 6 | BATS_ROOT= 7 | 8 | setup() { 9 | make_bats_test_suite_tmpdir 10 | INSTALL_DIR="$BATS_TEST_SUITE_TMPDIR/bats-core" 11 | BATS_ROOT="${BATS_TEST_DIRNAME%/*}" 12 | } 13 | 14 | @test "install.sh creates a valid installation" { 15 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 16 | emit_debug_output 17 | [ "$status" -eq 0 ] 18 | [ "$output" == "Installed Bats to $INSTALL_DIR/bin/bats" ] 19 | [ -x "$INSTALL_DIR/bin/bats" ] 20 | [ -x "$INSTALL_DIR/libexec/bats-core/bats" ] 21 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-exec-suite" ] 22 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-exec-test" ] 23 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-format-tap-stream" ] 24 | [ -x "$INSTALL_DIR/libexec/bats-core/bats-preprocess" ] 25 | [ -f "$INSTALL_DIR/share/man/man1/bats.1" ] 26 | [ -f "$INSTALL_DIR/share/man/man7/bats.7" ] 27 | 28 | run "$INSTALL_DIR/bin/bats" -v 29 | [ "$status" -eq 0 ] 30 | [ "${output%% *}" == 'Bats' ] 31 | } 32 | 33 | @test "install.sh only updates permissions for Bats files" { 34 | mkdir -p "$INSTALL_DIR"/{bin,libexec/bats-core} 35 | 36 | local dummy_bin="$INSTALL_DIR/bin/dummy" 37 | printf 'dummy' >"$dummy_bin" 38 | 39 | local dummy_libexec="$INSTALL_DIR/libexec/bats-core/dummy" 40 | printf 'dummy' >"$dummy_libexec" 41 | 42 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 43 | [ "$status" -eq 0 ] 44 | [ -f "$dummy_bin" ] 45 | [ ! -x "$dummy_bin" ] 46 | [ -f "$dummy_libexec" ] 47 | [ ! -x "$dummy_libexec" ] 48 | } 49 | 50 | @test "bin/bats is resilient to symbolic links" { 51 | run "$BATS_ROOT/install.sh" "$INSTALL_DIR" 52 | [ "$status" -eq 0 ] 53 | 54 | # Simulate a symlink to bin/bats (without using a symlink, for Windows sake) 55 | # by creating a wrapper script that executes bin/bats via a relative path. 56 | # 57 | # root.bats contains tests that use real symlinks on platforms that support 58 | # them, as does the .travis.yml script that exercises the Dockerfile. 59 | local bats_symlink="$INSTALL_DIR/bin/bats-link" 60 | printf '%s\n' '#! /usr/bin/env bash' \ 61 | "cd '$INSTALL_DIR/bin'" \ 62 | './bats "$@"' >"$bats_symlink" 63 | chmod 700 "$bats_symlink" 64 | 65 | run "$bats_symlink" -v 66 | [ "$status" -eq 0 ] 67 | [ "${output%% *}" == 'Bats' ] 68 | } 69 | -------------------------------------------------------------------------------- /test-utils/bats/test/root.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | # 3 | # This suite is dedicated to calculating BATS_ROOT when going through various 4 | # permutations of symlinks. It was inspired by the report in issue #113 that the 5 | # calculation was broken on CentOS, where /bin is symlinked to /usr/bin. 6 | # 7 | # The basic test environment is (all paths relative to BATS_TEST_SUITE_TMPDIR): 8 | # 9 | # - /bin is a relative symlink to /usr/bin, exercising the symlink resolution of 10 | # the `bats` parent directory (i.e. "${0%/*}") 11 | # - /usr/bin/bats is an absolute symlink to /opt/bats-core/bin/bats, exercising 12 | # the symlink resolution of the `bats` executable itself (i.e. "${0##*/}") 13 | 14 | load test_helper 15 | 16 | # This would make a good candidate for a one-time setup/teardown per #39. 17 | setup() { 18 | make_bats_test_suite_tmpdir 19 | cd "$BATS_TEST_SUITE_TMPDIR" 20 | mkdir -p {usr/bin,opt/bats-core} 21 | "$BATS_ROOT/install.sh" "opt/bats-core" 22 | 23 | ln -s "usr/bin" "bin" 24 | 25 | if [[ ! -L "bin" ]]; then 26 | cd - >/dev/null 27 | skip "symbolic links aren't functional on OSTYPE=$OSTYPE" 28 | fi 29 | 30 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats-core/bin/bats" \ 31 | "$BATS_TEST_SUITE_TMPDIR/usr/bin/bats" 32 | cd - >/dev/null 33 | } 34 | 35 | @test "#113: set BATS_ROOT when /bin is a symlink to /usr/bin" { 36 | run "$BATS_TEST_SUITE_TMPDIR/bin/bats" -v 37 | [ "$status" -eq 0 ] 38 | [ "${output%% *}" == 'Bats' ] 39 | } 40 | 41 | # The resolution scheme here is: 42 | # 43 | # - /bin => /usr/bin (relative directory) 44 | # - /usr/bin/foo => /usr/bin/bar (relative executable) 45 | # - /usr/bin/bar => /opt/bats/bin0/bar (absolute executable) 46 | # - /opt/bats/bin0 => /opt/bats/bin1 (relative directory) 47 | # - /opt/bats/bin1 => /opt/bats/bin2 (absolute directory) 48 | # - /opt/bats/bin2/bar => /opt/bats-core/bin/bar (absolute executable) 49 | # - /opt/bats-core/bin/bar => /opt/bats-core/bin/baz (relative executable) 50 | # - /opt/bats-core/bin/baz => /opt/bats-core/bin/bats (relative executable) 51 | @test "set BATS_ROOT with extreme symlink resolution" { 52 | cd "$BATS_TEST_SUITE_TMPDIR" 53 | mkdir -p "opt/bats/bin2" 54 | 55 | ln -s bar usr/bin/foo 56 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats/bin0/bar" usr/bin/bar 57 | ln -s bin1 opt/bats/bin0 58 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats/bin2" opt/bats/bin1 59 | ln -s "$BATS_TEST_SUITE_TMPDIR/opt/bats-core/bin/bar" opt/bats/bin2/bar 60 | ln -s baz opt/bats-core/bin/bar 61 | ln -s bats opt/bats-core/bin/baz 62 | 63 | cd - >/dev/null 64 | run "$BATS_TEST_SUITE_TMPDIR/bin/foo" -v 65 | [ "$status" -eq 0 ] 66 | [ "${output%% *}" == 'Bats' ] 67 | } 68 | -------------------------------------------------------------------------------- /test-utils/bats/test/suite.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load test_helper 4 | fixtures suite 5 | 6 | @test "running a suite with no test files" { 7 | run bats "$FIXTURE_ROOT/empty" 8 | [ $status -eq 0 ] 9 | [ "$output" = "1..0" ] 10 | } 11 | 12 | @test "running a suite with one test file" { 13 | run bats "$FIXTURE_ROOT/single" 14 | [ $status -eq 0 ] 15 | [ "${lines[0]}" = "1..1" ] 16 | [ "${lines[1]}" = "ok 1 a passing test" ] 17 | } 18 | 19 | @test "counting tests in a suite" { 20 | run bats -c "$FIXTURE_ROOT/single" 21 | [ $status -eq 0 ] 22 | [ "$output" -eq 1 ] 23 | 24 | run bats -c "$FIXTURE_ROOT/multiple" 25 | [ $status -eq 0 ] 26 | [ "$output" -eq 3 ] 27 | } 28 | 29 | @test "aggregated output of multiple tests in a suite" { 30 | run bats "$FIXTURE_ROOT/multiple" 31 | [ $status -eq 0 ] 32 | [ "${lines[0]}" = "1..3" ] 33 | echo "$output" | grep "^ok . truth" 34 | echo "$output" | grep "^ok . more truth" 35 | echo "$output" | grep "^ok . quasi-truth" 36 | } 37 | 38 | @test "a failing test in a suite results in an error exit code" { 39 | FLUNK=1 run bats "$FIXTURE_ROOT/multiple" 40 | [ $status -eq 1 ] 41 | [ "${lines[0]}" = "1..3" ] 42 | echo "$output" | grep "^not ok . quasi-truth" 43 | } 44 | 45 | @test "running an ad-hoc suite by specifying multiple test files" { 46 | run bats "$FIXTURE_ROOT/multiple/a.bats" "$FIXTURE_ROOT/multiple/b.bats" 47 | [ $status -eq 0 ] 48 | [ "${lines[0]}" = "1..3" ] 49 | echo "$output" | grep "^ok . truth" 50 | echo "$output" | grep "^ok . more truth" 51 | echo "$output" | grep "^ok . quasi-truth" 52 | } 53 | 54 | @test "extended syntax in suite" { 55 | FLUNK=1 run bats-exec-suite -x "$FIXTURE_ROOT/multiple/"*.bats 56 | [ $status -eq 1 ] 57 | [ "${lines[0]}" = "1..3" ] 58 | [ "${lines[1]}" = "begin 1 truth" ] 59 | [ "${lines[2]}" = "ok 1 truth" ] 60 | [ "${lines[3]}" = "begin 2 more truth" ] 61 | [ "${lines[4]}" = "ok 2 more truth" ] 62 | [ "${lines[5]}" = "begin 3 quasi-truth" ] 63 | [ "${lines[6]}" = "not ok 3 quasi-truth" ] 64 | } 65 | 66 | @test "recursive support (short option)" { 67 | run bats -r "${FIXTURE_ROOT}/recursive" 68 | [ $status -eq 0 ] 69 | [ "${lines[0]}" = "1..2" ] 70 | [ "${lines[1]}" = "ok 1 another passing test" ] 71 | [ "${lines[2]}" = "ok 2 a passing test" ] 72 | } 73 | 74 | @test "recursive support (long option)" { 75 | run bats --recursive "${FIXTURE_ROOT}/recursive" 76 | [ $status -eq 0 ] 77 | [ "${lines[0]}" = "1..2" ] 78 | [ "${lines[1]}" = "ok 1 another passing test" ] 79 | [ "${lines[2]}" = "ok 2 a passing test" ] 80 | } 81 | -------------------------------------------------------------------------------- /test-utils/bats/test/test_helper.bash: -------------------------------------------------------------------------------- 1 | fixtures() { 2 | FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures/$1" 3 | bats_trim_filename "$FIXTURE_ROOT" 'RELATIVE_FIXTURE_ROOT' 4 | } 5 | 6 | make_bats_test_suite_tmpdir() { 7 | export BATS_TEST_SUITE_TMPDIR="$BATS_TMPDIR/bats-test-tmp" 8 | mkdir -p "$BATS_TEST_SUITE_TMPDIR" 9 | } 10 | 11 | filter_control_sequences() { 12 | "$@" | sed $'s,\x1b\\[[0-9;]*[a-zA-Z],,g' 13 | } 14 | 15 | if ! command -v tput >/dev/null; then 16 | tput() { 17 | printf '1000\n' 18 | } 19 | export -f tput 20 | fi 21 | 22 | emit_debug_output() { 23 | printf '%s\n' 'output:' "$output" >&2 24 | } 25 | 26 | teardown() { 27 | if [[ -n "$BATS_TEST_SUITE_TMPDIR" ]]; then 28 | rm -rf "$BATS_TEST_SUITE_TMPDIR" 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /test-utils/lib: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | : "${BASH_LIB_DIR:?BASH_LIB_DIR must be set. Please source bash-lib/init before other scripts from bash-lib.}" 4 | 5 | SHELLCHECK_IMAGE="${SHELLCHECK_IMAGE:-koalaman/shellcheck}" 6 | SHELLCHECK_TAG="${SHELLCHECK_TAG:-v0.6.0}" 7 | 8 | # Check a single shell script for syntax 9 | # and common errors. 10 | function bl_shellcheck_script(){ 11 | # NOTE (HughSaunders): I tried using the checkstyle output of 12 | # _shellcheck along with a checkstyle2junit xslt stylesheet 13 | # from the shellcheck author. However Jenkins only reported 14 | # on error per file, as the style sheet created a test element 15 | # per file, with mulitple failure elements within. 16 | # Jenkins expects one failure element per test. 17 | 18 | local -r script="${1}" 19 | echo -e "\nChecking ${script}" 20 | 21 | # SC1091 - sourced scripts are not followed, ok because all scripts in the repo are found. 22 | # SC1090 - can't follow non-constant source, ok for because all scripts are checked. 23 | local -r ignores="-e SC1091 -e SC1090" 24 | 25 | if bash -n "${script}"; then 26 | # shellcheck disable=SC2086 27 | docker run \ 28 | --rm \ 29 | -v "${PWD}:/mnt" \ 30 | -e SHELLCHECK_OPTS \ 31 | ${SHELLCHECK_IMAGE}:${SHELLCHECK_TAG} ${ignores} ${script} 32 | else 33 | return 1 34 | fi 35 | } 36 | 37 | function bl_find_scripts(){ 38 | bl_tracked_files_excluding_subtrees \ 39 | | while read -r file; do 40 | grep --files-with-match '^#!.*bash' "${file}" || true 41 | done 42 | } 43 | 44 | function bl_tap2junit(){ 45 | local -r suite="${1:-BATS}" 46 | 47 | bl_spushd "${BASH_LIB_DIR}/test-utils/tap2junit" 48 | docker build . -t tap-junit 1>&2 49 | bl_spopd 50 | 51 | # Run tap-junit docker image to convert BATS TAP output to Junit for consumption by jenkins 52 | # filters stdin to stdout 53 | docker run --rm -i tap-junit -s "${suite}" 54 | } 55 | 56 | # Checks a Changelog file against keepachangelog.com format. 57 | function bl_validate_changelog(){ 58 | local CHANGELOG="" 59 | 60 | if [[ -z "${1:-}" ]]; then 61 | # Changelog file not specified 62 | 63 | # Look in the current directory 64 | if [[ -r CHANGELOG.md ]]; then 65 | CHANGELOG="CHANGELOG.md" 66 | fi 67 | 68 | # Look in the repo root 69 | if [[ -z "${CHANGELOG}" ]] && bl_in_git_repo; then 70 | guess="$(bl_repo_root)/CHANGELOG.md" 71 | [[ -r "${guess}" ]] && CHANGELOG="${guess}" 72 | fi 73 | 74 | if [[ -z "${CHANGELOG}" ]]; then 75 | bl_fail "CHANGELOG.md not found in current directory or root of git "\ 76 | "repo, please specify the path to the changelog. " \ 77 | "Usage: bl_validate_changelog /path/to/CHANGELOG.md" 78 | fi 79 | else 80 | # Changelog specified as parameter, use that. 81 | CHANGELOG="${1}" 82 | [[ -r "${CHANGELOG}" ]] || bl_fail "Can't read changelog file: ${CHANGELOG}" 83 | fi 84 | 85 | # Docker volume paths need to be absolute 86 | CHANGELOG="$(bl_abs_path "${CHANGELOG}")" 87 | 88 | docker run \ 89 | --rm \ 90 | --volume "${CHANGELOG}:/CHANGELOG.md" \ 91 | cyberark/parse-a-changelog 92 | } 93 | -------------------------------------------------------------------------------- /test-utils/tap2junit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | ENTRYPOINT python tap2junit.py 3 | COPY requirements.txt constraints.txt tap2junit.py / 4 | RUN pip install --no-cache-dir -r requirements.txt -c constraints.txt -------------------------------------------------------------------------------- /test-utils/tap2junit/constraints.txt: -------------------------------------------------------------------------------- 1 | junit-xml==1.8 2 | six==1.12.0 3 | -------------------------------------------------------------------------------- /test-utils/tap2junit/requirements.txt: -------------------------------------------------------------------------------- 1 | junit-xml 2 | -------------------------------------------------------------------------------- /test-utils/tap2junit/tap2junit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import re 5 | import sys 6 | from junit_xml import TestSuite, TestCase 7 | 8 | 9 | class Tap2JUnit: 10 | """ This class reads a subset of TAP (Test Anything protocol) 11 | and writes JUnit XML. 12 | 13 | Two line formats are read: 14 | 1. (not )?ok testnum testname 15 | 2. # diagnostic output 16 | 17 | 1. Starts a new test result. 18 | 2. Adds diagnostic information to the last read result 19 | 20 | Any 2. lines found before a 1. line are ignored. 21 | Any lines not matching either pattern are ignored. 22 | 23 | This script was written because none of the tap2junit converters 24 | I could find inserted the failure output into the junit correctly. 25 | And IMO a failed test with no indication of why is useless. 26 | """ 27 | 28 | def __init__(self, test_suite, test_class): 29 | self.test_suite = test_suite 30 | self.test_class = test_class 31 | # This Regex matches a (not) ok testnum testname line from the 32 | # TAP specification, using named capture groups 33 | self.result_re = re.compile( 34 | r"^(?P<result>not )?ok\s*(?P<testnum>[0-9])+\s*(?P<testname>.*)$") 35 | self.comment_re = re.compile(r"^\s*#") 36 | self.case = None 37 | self.cases = [] 38 | 39 | def process_line(self, line): 40 | """ This funuction reads a tap stream line by line 41 | and groups the diagnostic output with the relevant 42 | result in a dictionary. 43 | 44 | Outputs a list of dicts, one for each result 45 | """ 46 | match = self.result_re.match(line) 47 | if match: 48 | # This line starts a new test result 49 | self.case = match.groupdict() 50 | self.case['stderr'] = [] 51 | self.cases.append(self.case) 52 | 53 | return 54 | 55 | match = self.comment_re.match(line) 56 | if match and self.case: 57 | # This line contains diagnostic 58 | # output from a failed test 59 | self.case['stderr'].append(re.sub(r'^\s*#', '', line).rstrip()) 60 | 61 | def convert(self, infile=sys.stdin, out=sys.stdout): 62 | """ Reads a subset of TAP and writes JUnit XML """ 63 | # read lines 64 | for line in infile.readlines(): 65 | self.process_line(line) 66 | 67 | # Convert line dicts to test case objects 68 | case_objs = [] 69 | for case in self.cases: 70 | case_obj = TestCase(case['testname'], self.test_class, 0, '', '') 71 | if case['result'] == 'not ': 72 | case_obj.add_failure_info(output="\n".join(case['stderr'])) 73 | case_objs.append(case_obj) 74 | 75 | # Combine test cases into a suite 76 | suite = TestSuite(self.test_suite, case_objs) 77 | 78 | # Write the suite out as XML 79 | TestSuite.to_file(out, [suite]) 80 | 81 | 82 | def main(): 83 | t2j = Tap2JUnit( 84 | os.environ.get('JUNIT_TEST_SUITE', 'tap2junit'), 85 | os.environ.get('JUNIT_TEST_CLASS', 'tap2junit') 86 | ) 87 | t2j.convert() 88 | 89 | 90 | if __name__ == "__main__": 91 | main() 92 | -------------------------------------------------------------------------------- /tests-for-this-repo/filehandling.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | . "${BASH_LIB_DIR}/init" 5 | 6 | @test "bl_abs_path returns absolute path for PWD" { 7 | run bl_abs_path . 8 | assert_output $PWD 9 | assert_success 10 | } 11 | 12 | @test "bl_abs_path returns PWD when no arg specified" { 13 | run bl_abs_path 14 | assert_output $PWD 15 | assert_success 16 | } 17 | 18 | @test "bl_abs_path returns same path when already absolute" { 19 | run bl_abs_path /tmp 20 | assert_output /tmp 21 | assert_success 22 | } 23 | -------------------------------------------------------------------------------- /tests-for-this-repo/fixtures/ruby/ruby_gems_api_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parse_a_changelog", 3 | "downloads": 6660, 4 | "version": "1.0.1", 5 | "version_downloads": 168, 6 | "platform": "ruby", 7 | "authors": "John Tuttle", 8 | "info": "Uses a grammar describing the keep-a-changelog format to attempt to parse a given file.", 9 | "licenses": [ 10 | "Apache-2.0" 11 | ], 12 | "metadata": {}, 13 | "yanked": false, 14 | "sha": "c081ae854570083ba56097d84a1fc66d47dd31f1a015edcb1ba2cbf9e2a4fe4a", 15 | "project_uri": "https://rubygems.org/gems/parse_a_changelog", 16 | "gem_uri": "https://rubygems.org/gems/parse_a_changelog-1.0.1.gem", 17 | "homepage_uri": "http://github.com/cyberark/parse-a-changelog", 18 | "wiki_uri": null, 19 | "documentation_uri": "http://www.rubydoc.info/gems/parse_a_changelog/1.0.1", 20 | "mailing_list_uri": null, 21 | "source_code_uri": null, 22 | "bug_tracker_uri": null, 23 | "changelog_uri": null, 24 | "dependencies": { 25 | "development": [ 26 | { 27 | "name": "rspec", 28 | "requirements": "~> 3.8" 29 | }, 30 | { 31 | "name": "rspec_junit_formatter", 32 | "requirements": "~> 0.4.1" 33 | } 34 | ], 35 | "runtime": [ 36 | { 37 | "name": "treetop", 38 | "requirements": "~> 1.6" 39 | } 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests-for-this-repo/fixtures/test-utils/tap2junit.in: -------------------------------------------------------------------------------- 1 | 1..3 2 | ok 1 Passed Test 3 | not ok 2 Failed Test 4 | # Diagnostic output 5 | # more diagnostic output 6 | ok 3 Another Passed Test -------------------------------------------------------------------------------- /tests-for-this-repo/fixtures/test-utils/tap2junit.out: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" ?> 2 | <testsuites disabled="0" errors="0" failures="1" tests="3" time="0.0"> 3 | <testsuite disabled="0" errors="0" failures="1" name="tap2junit" skipped="0" tests="3" time="0"> 4 | <testcase classname="tap2junit" name="Passed Test"/> 5 | <testcase classname="tap2junit" name="Failed Test"> 6 | <failure type="failure"> Diagnostic output 7 | more diagnostic output</failure> 8 | </testcase> 9 | <testcase classname="tap2junit" name="Another Passed Test"/> 10 | </testsuite> 11 | </testsuites> 12 | -------------------------------------------------------------------------------- /tests-for-this-repo/git.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | . "${BASH_LIB_DIR}/init" 5 | 6 | # run before every test 7 | setup(){ 8 | local -r temp_dir="${BATS_TMPDIR}/testtemp" 9 | local -r repo_dir="${temp_dir/}/repo" 10 | rm -rf "${temp_dir}" 11 | mkdir -p "${repo_dir}" 12 | pushd ${repo_dir} 13 | 14 | git init 15 | git config user.email "conj_ops_ci@cyberark.com" 16 | git config user.name "Jenkins" 17 | SKIP_GITLEAKS=YES git commit --allow-empty -m "initial" 18 | echo "some content" > a_file 19 | git add a_file 20 | # Add a submodule as that trips up some operations 21 | git submodule add https://github.com/cyberark/conjur conjur 22 | git submodule update --init 23 | SKIP_GITLEAKS=YES git commit -a -m "some operations fail on empty repos" 24 | } 25 | 26 | teardown(){ 27 | local -r temp_dir="${BATS_TMPDIR}/testtemp" 28 | rm -rf "${temp_dir}" 29 | } 30 | 31 | @test "bl_git_available fails when git is not available" { 32 | real_path="${PATH}" 33 | PATH="" 34 | run bl_git_available 35 | PATH="${real_path}" 36 | assert_failure 37 | assert_output --partial "binary not found" 38 | } 39 | 40 | @test "bl_git_available succeeds when git is available" { 41 | git(){ :; } 42 | run bl_git_available 43 | assert_success 44 | assert_output "" 45 | } 46 | 47 | @test "bl_in_git_repo fails when not in a git repo" { 48 | rm -rf .git 49 | run bl_in_git_repo 50 | assert_failure 51 | assert_output --partial "not within a git repo" 52 | } 53 | 54 | @test "bl_in_git_repo succeeds when in a git repo" { 55 | run bl_in_git_repo 56 | assert_success 57 | assert_output "" 58 | } 59 | 60 | @test "bl_github_owner_repo extracts owner and repo from origin remote" { 61 | git remote add origin git@github.com:owner/repo 62 | run bl_github_owner_repo 63 | assert_success 64 | assert_output "owner/repo" 65 | } 66 | 67 | @test "bl_github_owner_repo fails when origin doesn't exist" { 68 | run bl_github_owner_repo 69 | assert_failure 70 | assert_output --partial "doesn't exist" 71 | } 72 | 73 | @test "bl_github_owner_repo fails when origin doesn't point to github" { 74 | git remote add origin foo@foo.com:owner/repo 75 | run bl_github_owner_repo 76 | assert_failure 77 | assert_output --partial "not a github remote" 78 | } 79 | 80 | @test "bl_github_owner_repo succeeds with https remote" { 81 | git remote add origin "https://github.com/owner/repo" 82 | run bl_github_owner_repo 83 | assert_success 84 | assert_output "owner/repo" 85 | } 86 | 87 | @test "bl_github_owner_repo succeeds with git remote" { 88 | git remote add origin "git@github.com:owner/repo" 89 | run bl_github_owner_repo 90 | assert_success 91 | assert_output "owner/repo" 92 | } 93 | 94 | @test "bl_github_owner_repo succeeds with .git suffix" { 95 | git remote add origin "https://github.com/owner/repo.git" 96 | run bl_github_owner_repo 97 | assert_success 98 | assert_output "owner/repo" 99 | } 100 | 101 | @test "bl_repo_root returns root of current repo" { 102 | pushd ${BASH_LIB_DIR} 103 | run bl_repo_root 104 | assert_output $PWD 105 | assert_success 106 | } 107 | 108 | @test "bl_repo_root fails when not run from a git repo" { 109 | pushd /tmp 110 | run bl_repo_root 111 | assert_failure 112 | } 113 | 114 | @test "bl_all_files_in_repo lists all git tracked files" { 115 | # untracked file shouldn't be listed in output 116 | date > b 117 | run bl_all_files_in_repo 118 | assert_output ".gitmodules 119 | a_file" 120 | assert_success 121 | } 122 | 123 | @test "bl_remote_latest_tag gets latest tag from a remote" { 124 | # For this test the "remote" will be local, 125 | # because It hard to guarantee an actual remote 126 | # won't gain new tags over time. 127 | 128 | date > a 129 | git add a 130 | git commit -m v1 131 | git tag -a -m v1 v1 132 | 133 | date > b 134 | git add b 135 | git commit -m v2 136 | git tag -a -m v2 v2 137 | 138 | run bl_remote_latest_tag . 139 | assert_output v2 140 | assert_success 141 | } 142 | 143 | @test "bl_remote_latest_tagged_commit returns sha of last tagged commit, not sha of the tag" { 144 | date > a 145 | git add a 146 | git commit -m v1 147 | git tag -a -m v1 v1 148 | 149 | date > b 150 | git add b 151 | git commit -m v2 152 | 153 | run bl_remote_latest_tagged_commit . 154 | assert_output "$(git rev-parse v1^{})" 155 | assert_success 156 | } 157 | 158 | @test "bl_remote_sha_for_ref looks up a sha for a given ref" { 159 | git checkout -b testbranch 160 | run bl_remote_sha_for_ref . testbranch 161 | assert_output "$(git rev-parse HEAD)" 162 | assert_success 163 | } 164 | 165 | @test "bl_remote_tag_for_sha looks up a tag for a given sha" { 166 | git tag -a -m v1 v1 167 | date > a 168 | git add a 169 | git commit -m v2 170 | git tag -a -m v2 v2 171 | 172 | run bl_remote_tag_for_sha . "$(git rev-parse v1^{})" 173 | assert_output v1 174 | assert_success 175 | } 176 | 177 | @test "bl_gittrees_present succeeds when .gittrees file is present" { 178 | touch .gittrees 179 | run bl_gittrees_present 180 | assert_success 181 | assert_output "" 182 | } 183 | 184 | @test "bl_gittrees_present fails when .gittrees file is not present" { 185 | run bl_gittrees_present 186 | assert_failure 187 | assert_output "" 188 | } 189 | 190 | @test "bl_cat_gittrees dies when gittrees doesn't exist" { 191 | run bl_cat_gittrees 192 | assert_failure 193 | assert_output --partial "should contain" 194 | } 195 | 196 | @test "bl_cat_gitrees skips comments" { 197 | cat >.gittrees <<EOF 198 | # comment 1 199 | # comment 2 200 | a b c 201 | EOF 202 | run bl_cat_gittrees 203 | assert_output "a b c" 204 | refute_output --partial "comment 1" 205 | refute_output --partial "comment 2" 206 | assert_success 207 | } 208 | 209 | @test "bl_tracked_files_excluding_subtrees excludes files in subtrees" { 210 | # use add_subtree when available 211 | run git subtree add --squash --prefix bats "https://github.com/bats-core/bats" v1.0.0 212 | assert_success 213 | 214 | echo "bats https://github.com/bats-core/bats bats" >.gittrees 215 | 216 | assert [ -e bats/README.md ] 217 | 218 | date > untracked_file 219 | 220 | run bl_tracked_files_excluding_subtrees 221 | refute_output --partial bats 222 | refute_output --partial untracked_file 223 | assert_output --partial a_file 224 | assert_success 225 | assert_output --partial a_file 226 | assert_success 227 | } 228 | -------------------------------------------------------------------------------- /tests-for-this-repo/github.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | # run before every test 5 | setup(){ 6 | . "${BASH_LIB_DIR}/init" 7 | local -r temp_dir="${BATS_TMPDIR}/testtemp" 8 | local -r repo_dir="${temp_dir/}/repo" 9 | rm -rf "${temp_dir}" 10 | mkdir -p "${repo_dir}" 11 | pushd ${repo_dir} 12 | 13 | git init 14 | git config user.email "conj_ops_ci@cyberark.com" 15 | git config user.name "Jenkins" 16 | SKIP_GITLEAKS=YES git commit --allow-empty -m "initial" 17 | echo "some content" > a_file 18 | git add a_file 19 | git commit -a -m "some operations fail on empty repos" 20 | git remote add origin git@github.com:owner/repo 21 | } 22 | 23 | teardown(){ 24 | local -r temp_dir="${BATS_TMPDIR}/testtemp" 25 | rm -rf "${temp_dir}" 26 | unset GITHUB_TOKEN 27 | unset GITHUB_USER 28 | unset hub 29 | } 30 | 31 | @test "bl_hub_available fails when hub isn't available" { 32 | REAL_PATH="${PATH}" 33 | PATH="${PWD}" 34 | run bl_hub_available 35 | PATH="${REAL_PATH}" 36 | assert_output --partial "github cli" 37 | assert_failure 38 | } 39 | 40 | @test "bl_hub_available succeeds when hub is available" { 41 | hub(){ :; } 42 | run bl_hub_available 43 | assert_success 44 | } 45 | 46 | @test "bl_hub_check fails when not in a git repo" { 47 | rm -rf .git 48 | run bl_hub_check 49 | assert_failure 50 | assert_output --partial "not within a git repo" 51 | } 52 | 53 | @test "bl_hub_check fails when hub not availble" { 54 | bl_in_git_repo(){ :; } 55 | REAL_PATH="${PATH}" 56 | PATH="${PWD}" 57 | run bl_hub_check 58 | PATH="${REAL_PATH}" 59 | assert_output --partial "github cli" 60 | assert_failure 61 | } 62 | 63 | @test "bl_hub_creds_available fails when creds are not available" { 64 | export HUB_CONFIG="./hub_config" 65 | run bl_hub_creds_available 66 | assert_failure 67 | assert_output --partial "No credentials found" 68 | } 69 | 70 | @test "bl_hub_creds_available succeeds when env vars are set" { 71 | export GITHUB_USER=user 72 | export GITHUB_TOKEN=token 73 | export HUB_CONFIG="./hub_config" 74 | run bl_hub_creds_available 75 | unset GITHUB_USER 76 | unset GITHUB_TOKEN 77 | assert_success 78 | assert_output "" 79 | } 80 | 81 | @test "bl_hub_creds_available succeeds when hub config file is present" { 82 | unset GITHUB_USER 83 | unset GITHUB_TOKEN 84 | export HUB_CONFIG="./hub_config" 85 | touch hub_config 86 | run bl_hub_creds_available 87 | assert_success 88 | assert_output "" 89 | } 90 | 91 | @test "bl_hub_check succeeds when in a git repo, hub is available and creds supplied" { 92 | touch hub_config 93 | hub(){ :; } 94 | run bl_hub_check ./hub_config 95 | } 96 | 97 | @test "bl_hub_download_latest downloads hub binary to specified location, with the correct arch" { 98 | run bl_hub_download_latest "${PWD}" 99 | assert_success 100 | assert test -e hub 101 | 102 | run ./hub --version 103 | assert_success 104 | assert_output --partial "hub version" 105 | } 106 | 107 | @test "bl_hub_issue_number_for_title returns only the issue number" { 108 | bl_hub_check(){ :; } 109 | hub(){ 110 | [[ "${1}" == "issue" ]] || bl_die "issue subcommand not specified" 111 | cat <<EOF 112 | #19 Add code coverage to bash-lib kind/quality 113 | #11 Generated Documentation 114 | #7 Clean up 115 | EOF 116 | } 117 | 118 | run bl_hub_issue_number_for_title "Clean up" 119 | assert_success 120 | assert_output "7" 121 | } 122 | 123 | @test "bl_hub_issue_number_for_title returns a single issue number" { 124 | bl_hub_check(){ :; } 125 | hub(){ 126 | [[ "${1}" == "issue" ]] || bl_die "issue subcommand not specified" 127 | cat <<EOF 128 | #19 Add code coverage to bash-lib kind/quality 129 | #11 Generated Documentation 130 | #12 Duplicate 131 | #13 Duplicate 132 | #7 Clean up 133 | EOF 134 | } 135 | 136 | run bl_hub_issue_number_for_title "Duplicate" 137 | assert_success 138 | assert_output "12" 139 | } 140 | 141 | @test "bl_hub_add_issue_comment uses the correct URL" { 142 | bl_hub_check(){ :; } 143 | hub(){ 144 | if [[ "${1}" == "api" ]]; then 145 | [[ "${3}" != "repos/owner/repo/issues/6" ]] || bl_die "Incorrect api url: ${3}" 146 | fi 147 | } 148 | 149 | run bl_hub_add_issue_comment 6 "A comment" 150 | assert_success 151 | assert_output "" 152 | } 153 | 154 | @test "bl_hub_add_issue_comment fails when an invalid issue number is supplied" { 155 | bl_hub_check(){ :; } 156 | hub(){ 157 | if [[ "${1}" == "issue" ]]; then 158 | return 1 159 | fi 160 | } 161 | 162 | run bl_hub_add_issue_comment 6 "A comment" 163 | assert_failure 164 | assert_output --partial "isn't valid" 165 | assert_output --partial "6" 166 | } 167 | 168 | @test "bl_hub_add_issue_comment fails when adding a comment fails" { 169 | bl_hub_check(){ :; } 170 | hub(){ 171 | if [[ "${1}" == "api" ]]; then 172 | return 1 173 | fi 174 | } 175 | 176 | run bl_hub_add_issue_comment 6 "A comment" 177 | assert_failure 178 | assert_output --partial "Failed to add comment" 179 | assert_output --partial "A comment" 180 | assert_output --partial "owner/repo#6" 181 | } 182 | 183 | @test "bl_hub_comment_or_create_issue creates issue when it doesnt exist" { 184 | bl_hub_check(){ :; } 185 | bl_hub_issue_number_for_title(){ :; } 186 | bl_github_owner_repo(){ echo "owner/repo"; } 187 | hub(){ 188 | if [[ "${2:-}" == "create" ]]; then 189 | [[ "${3}" == "-m" ]] || bl_die "unexpected argument to hub \"${3}\" expected \"-m\" """ 190 | [[ "${4}" == "$(echo -e "title\n\nmessage")" ]] \ 191 | || bl_die "unexpected argument to hub \"${4}\"" 192 | fi 193 | echo "https://github.com/owner/repo/issues/1" 194 | } 195 | run bl_hub_comment_or_create_issue title message 196 | assert_success 197 | assert_output --partial "https://github.com/owner/repo/issues/1" 198 | assert_output --partial "create" 199 | } 200 | 201 | @test "bl_hub_comment_or_create_issue adds comment when issue already exists" { 202 | bl_hub_check(){ :; } 203 | hub(){ :; } 204 | bl_hub_issue_number_for_title(){ echo 1; } 205 | bl_github_owner_repo(){ echo "owner/repo"; } 206 | bl_hub_add_issue_comment(){ 207 | [[ "${1}" == 1 ]] || bl_die "Expected issue number to be: 1" 208 | [[ "${2}" == "message" ]] || bl_die "Expected message to be: message" 209 | bl_info "Added comment: \"message\" to https://github.com/owner/repo/issues/1" 210 | } 211 | run bl_hub_comment_or_create_issue title message 212 | assert_success 213 | assert_output --partial "comment" 214 | assert_output --partial "https://github.com/owner/repo/issues/1" 215 | } 216 | 217 | @test "bl_hub_comment_or_create_issue passes label to hub" { 218 | bl_hub_check(){ :; } 219 | bl_hub_issue_number_for_title(){ :; } 220 | bl_github_owner_repo(){ echo "owner/repo"; } 221 | hub(){ 222 | [[ "${5}" == "-l" ]] || bl_die "expected -l for specifying a label" 223 | [[ "${6}" == "label" ]] || bl_die "expected 'label' as label name" 224 | echo "https://github.com/owner/repo/issues/1" 225 | } 226 | run bl_hub_comment_or_create_issue title message label 227 | assert_success 228 | assert_output --partial "https://github.com/owner/repo/issues/1" 229 | assert_output --partial "create" 230 | } 231 | -------------------------------------------------------------------------------- /tests-for-this-repo/helpers.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | . "${BASH_LIB_DIR}/init" 5 | 6 | # run before every test 7 | setup(){ 8 | temp_dir="${BATS_TMPDIR}/testtemp" 9 | mkdir "${temp_dir}" 10 | afile="${temp_dir}/appendfile" 11 | } 12 | 13 | teardown(){ 14 | temp_dir="${BATS_TMPDIR}/testtemp" 15 | rm -rf "${temp_dir}" 16 | } 17 | 18 | @test "bl_die exits and prints message" { 19 | run bash -c ". ${BASH_LIB_DIR}/init; bl_die msg" 20 | assert_output --partial msg 21 | assert_failure 22 | } 23 | 24 | @test "bl_fail fails but does not exit" { 25 | run bl_fail message 26 | assert_failure 27 | assert_output --partial message 28 | } 29 | 30 | @test "bl_spushd is quiet on stdout" { 31 | run bl_spushd /tmp 32 | assert_output "" 33 | assert_success 34 | } 35 | 36 | @test "bl_spopd is quiet on stdout" { 37 | pushd . 38 | run bl_spopd 39 | assert_output "" 40 | assert_success 41 | } 42 | 43 | @test "bl_spushd dies on failure" { 44 | run bash -c ". ${BASH_LIB_DIR}/init; bl_spushd /this-doesnt-exist" 45 | assert_output --partial "No such file or directory" 46 | assert_failure 47 | } 48 | 49 | @test "bl_spopd dies on failure" { 50 | run bash -c ". ${BASH_LIB_DIR}/init; bl_spopd" 51 | assert_output --partial "stack empty" 52 | assert_failure 53 | } 54 | 55 | @test "bl_is_num fails with no arguments" { 56 | run bl_is_num 57 | assert_output "" 58 | assert_failure 59 | } 60 | 61 | @test "bl_is_num fails with alphabetical input" { 62 | run bl_is_num foo 63 | assert_output "" 64 | assert_failure 65 | } 66 | 67 | @test "bl_is_num suceeds with integer" { 68 | run bl_is_num 123 69 | assert_output "" 70 | assert_success 71 | } 72 | 73 | @test "bl_is_num suceeds with negative integer" { 74 | run bl_is_num -123 75 | assert_output "" 76 | assert_success 77 | } 78 | 79 | @test "bl_is_num suceeds with float" { 80 | run bl_is_num 123.4 81 | assert_output "" 82 | assert_success 83 | } 84 | 85 | @test "bl_is_num suceeds with negative float" { 86 | run bl_is_num -123.4 87 | assert_output "" 88 | assert_success 89 | } 90 | 91 | @test "bl_retry runs command only once if it succeeds the first time" { 92 | retryme(){ 93 | date >> ${afile} 94 | } 95 | run bl_retry 3 retryme 96 | assert_success 97 | assert_equal $(wc -l <${afile}) 1 98 | } 99 | 100 | @test "bl_retry doesn't introduce delay when the command succeeds first time" { 101 | retryme(){ 102 | date >> ${afile} 103 | } 104 | start=$(date +%s) 105 | run bl_retry 3 retryme 106 | end=$(date +%s) 107 | assert [ "$(( start + 1 ))" -ge "${end}" ] 108 | assert_success 109 | } 110 | 111 | @test "bl_retry runs n times on consecutive failure and waits between attempts" { 112 | retryme(){ 113 | date >> ${afile} 114 | false 115 | } 116 | start=$(date +%s) 117 | run bl_retry 2 retryme 118 | end=$(date +%s) 119 | # introduces at least a two second delay between attempts 120 | assert [ "$(( start + 2 ))" -le "${end}" ] 121 | assert_failure 122 | assert_equal $(wc -l <${afile}) 2 123 | } 124 | 125 | @test "bl_retry returns after first success" { 126 | retryme(){ 127 | date >> "${afile}" 128 | case $(wc -l < ${afile}) in 129 | *1) 130 | return 1 131 | ;; 132 | *) 133 | return 0 134 | ;; 135 | esac 136 | } 137 | run bl_retry 3 retryme 138 | assert_success 139 | assert_equal $(wc -l <${afile}) 2 140 | } 141 | 142 | @test "bl_retry fails with less than two arguments" { 143 | run bl_retry 3 144 | assert_failure 145 | assert_output --partial usage 146 | assert [ ! -e "${temp_dir}/appendfile" ] 147 | } 148 | 149 | @test "bl_retry fails with non-integer retry count" { 150 | run bl_retry "this" date 151 | assert_failure 152 | assert_output --partial number 153 | assert [ ! -e "${temp_dir}/appendfile" ] 154 | } 155 | 156 | @test "bl_retry succeeds with compound statements" { 157 | run bl_retry 3 "true && date >> ${afile}" 158 | assert_success 159 | assert_equal $(wc -l <${afile}) 1 160 | } 161 | 162 | 163 | 164 | 165 | # *************** 166 | 167 | 168 | @test "bl_retry_constant runs command only once if it succeeds the first time" { 169 | retry_me(){ 170 | date >> ${afile} 171 | } 172 | run bl_retry_constant 3 1 retry_me 173 | assert_success 174 | assert_equal $(wc -l <${afile}) 1 175 | } 176 | 177 | @test "bl_retry_constant doesn't introduce delay when the command succeeds first time" { 178 | retry_me(){ 179 | date >> ${afile} 180 | } 181 | start=$(date +%s) 182 | run bl_retry_constant 3 10 retry_me 183 | end=$(date +%s) 184 | assert [ "$(( start + 1 ))" -ge "${end}" ] 185 | assert_success 186 | } 187 | 188 | @test "bl_retry_constant runs n times on consecutive failure and waits between attempts" { 189 | retry_me(){ 190 | date >> ${afile} 191 | false 192 | } 193 | start=$(date +%s) 194 | run bl_retry_constant 2 2 retry_me 195 | end=$(date +%s) 196 | # introduces at least a two second delay between attempts 197 | assert [ "$(( start + 2 ))" -le "${end}" ] 198 | assert_failure 199 | assert_equal $(wc -l <${afile}) 2 200 | } 201 | 202 | @test "bl_retry_constant returns after first success" { 203 | retry_me(){ 204 | date >> "${afile}" 205 | case $(wc -l < ${afile}) in 206 | *1) 207 | return 1 208 | ;; 209 | *) 210 | return 0 211 | ;; 212 | esac 213 | } 214 | run bl_retry_constant 3 1 retry_me 215 | assert_success 216 | assert_equal $(wc -l <${afile}) 2 217 | } 218 | 219 | @test "bl_retry_constant fails with less than three arguments" { 220 | run bl_retry_constant 3 1 221 | assert_failure 222 | assert_output --partial usage 223 | assert [ ! -e "${temp_dir}/appendfile" ] 224 | } 225 | 226 | @test "bl_retry_constant fails with non-integer retry count" { 227 | run bl_retry_constant "this" 1 date 228 | assert_failure 229 | assert_output --partial number 230 | assert [ ! -e "${temp_dir}/appendfile" ] 231 | } 232 | 233 | @test "bl_retry_constant fails with non-integer interval" { 234 | run bl_retry_constant 2 "this" date 235 | assert_failure 236 | assert_output --partial interval 237 | assert [ ! -e "${temp_dir}/appendfile" ] 238 | } 239 | 240 | @test "bl_retry_constant succeeds with compound statements" { 241 | run bl_retry_constant 3 1 "true && date >> ${afile}" 242 | assert_success 243 | assert_equal $(wc -l <${afile}) 1 244 | } -------------------------------------------------------------------------------- /tests-for-this-repo/k8s.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | . "${BASH_LIB_DIR}/init" 5 | 6 | @test "gke-utils image builds" { 7 | run bl_build_gke_image 8 | assert_success 9 | } 10 | 11 | @test "Kubernetes Cluster Is Available" { 12 | : ${KUBECTL_CLI_URL:?Required Var, did you run tests via summon?} 13 | run bl_run_docker_gke_command "kubectl cluster-info" 14 | assert_output --regexp "Kubernetes.* is running at .*https://" 15 | assert_success 16 | } 17 | 18 | @test "Can delete gke image" { 19 | local -r image="${DOCKER_REGISTRY_PATH}/alpine-test-${RANDOM}" 20 | bl_run_docker_gke_command " 21 | docker pull alpine 22 | docker tag alpine ${image} 23 | docker push ${image} 24 | " 25 | run bl_delete_gke_image "${image}" 26 | assert_success 27 | } 28 | -------------------------------------------------------------------------------- /tests-for-this-repo/lint.bats: -------------------------------------------------------------------------------- 1 | 2 | . "${BASH_LIB_DIR}/init" 3 | 4 | setup() { 5 | bl_spushd ${BASH_LIB_DIR} 6 | } 7 | 8 | # Find and check shell scripts 9 | @test "Syntax and Shellcheck" { 10 | FAILED="" 11 | echo "Starting Bash Lint checks" 12 | for script in $(bl_find_scripts); do 13 | bl_shellcheck_script "${script}"\ 14 | || FAILED="${FAILED} ${script}" 15 | done 16 | 17 | [[ "${FAILED}" == "" ]] 18 | } 19 | 20 | @test "Bash scripts do not have .sh suffix" { 21 | rc=0 22 | for file in $(bl_find_scripts); do 23 | if [[ "${file}" =~ .sh$ ]]; then 24 | # script has .sh suffix 25 | echo "Script found with .sh suffix: ${file}, please rename" 26 | rc=1 27 | fi 28 | done 29 | return ${rc} 30 | } 31 | 32 | @test "All functions referenced in readme" { 33 | rc=0 34 | for file in $(bl_find_scripts | grep "/lib$"); do 35 | for func_name in $(grep 'function.*()\s*{\s*$' ${file} | awk '{print $2}'| tr -dc '[a-zA-Z0-9_-\n]'); do 36 | if ! grep -q "${func_name}" "${BASH_LIB_DIR}/README.md"; then 37 | echo "Function ${func_name} from libriary ${file} is not mentioned in the README.md, please add a description" 38 | rc=1 39 | fi 40 | done 41 | 42 | if ! grep -q "${file}" "${BASH_LIB_DIR}/README.md"; then 43 | echo "Library ${file} is not mentioned in the README.md, please add a description" 44 | rc=1 45 | fi 46 | done 47 | return ${rc} 48 | } 49 | 50 | @test "All functions tested" { 51 | local rc=0 52 | for file in $(bl_find_scripts | grep "/lib$"); do 53 | local lib_name="$(dirname ${file})" 54 | local bats_file="tests-for-this-repo/${lib_name}.bats" 55 | if [[ ! -e "${bats_file}" ]]; then 56 | echo "BATS test file ${bats_file} is missing for library ${file}" 57 | rc=1 58 | else 59 | for func_name in $(grep 'function.*()\s*{' "${file}" | awk '{print $2}'| tr -dc '[a-zA-Z0-9_\n]'); do 60 | if ! grep -q "${func_name}" "${bats_file}"; then 61 | echo "Function ${func_name} from libriary ${file} is not tested in ${bats_file}, please add a test." 62 | rc=1 63 | fi 64 | done 65 | fi 66 | done 67 | return ${rc} 68 | } 69 | -------------------------------------------------------------------------------- /tests-for-this-repo/logging.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | . "${BASH_LIB_DIR}/init" 4 | 5 | teardown() { 6 | # reset to default log level 7 | export BASH_LIB_LOG_LEVEL=info 8 | echo teardown 9 | } 10 | 11 | @test "bl_announce prints all arguments" { 12 | run bl_announce one two one two 13 | assert_output --partial "one two one two" 14 | assert_success 15 | } 16 | 17 | @test "bl_check_log_level succeeds with valid level" { 18 | run bl_check_log_level error 19 | assert_success 20 | } 21 | 22 | @test "bl_check_log_level fails with invalid level" { 23 | run BASH_LIB_LOG_LEVEL="foo" bl_check_log_level 24 | assert_failure 25 | } 26 | 27 | @test "bl_log outputs message" { 28 | run bl_log info test 29 | assert_success 30 | assert_output --partial test 31 | } 32 | 33 | @test "bl_log outputs mesage when stderr is selected. Note: bats combines stdout and stderr" { 34 | run bl_log info test stderr 35 | assert_success 36 | assert_output --partial "test" 37 | } 38 | 39 | @test "bl_debug doesn't output anything using the default info level" { 40 | run bl_debug foo 41 | assert_success 42 | refute_output foo 43 | } 44 | 45 | @test "bl_debug outputs its inputs while using the debug level" { 46 | export BASH_LIB_LOG_LEVEL="debug" 47 | run bl_debug foo 48 | assert_success 49 | assert_output --partial foo 50 | } 51 | 52 | @test "bl_info outputs its inputs while using the info level" { 53 | export BASH_LIB_LOG_LEVEL="info" 54 | run bl_info foo 55 | assert_success 56 | assert_output --partial foo 57 | } 58 | 59 | @test "bl_warn uses the correct colour" { 60 | run bl_warn warning 61 | assert_success 62 | assert_output --partial $(echo -e "\e[0;33;40m") 63 | assert_output --partial warning 64 | } 65 | 66 | @test "bl_error resets colour at the end of output" { 67 | run bl_error error 68 | assert_success 69 | assert_output --partial $(echo -e "\e[0m") 70 | assert_output --partial error 71 | } 72 | 73 | @test "bl_fatal outputs its input" { 74 | run bl_fatal reallybad 75 | assert_success 76 | assert_output --partial reallybad 77 | } -------------------------------------------------------------------------------- /tests-for-this-repo/python-lint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-alpine 2 | 3 | ENTRYPOINT pytest --flake8 --junit-xml junit.xml 4 | VOLUME ["/mnt"] 5 | COPY requirements.txt constraints.txt / 6 | RUN pip install --no-cache-dir -r requirements.txt -c constraints.txt 7 | WORKDIR /mnt -------------------------------------------------------------------------------- /tests-for-this-repo/python-lint/constraints.txt: -------------------------------------------------------------------------------- 1 | atomicwrites==1.3.0 2 | attrs==19.1.0 3 | entrypoints==0.3 4 | flake8==3.7.7 5 | mccabe==0.6.1 6 | more-itertools==7.0.0 7 | pluggy==0.9.0 8 | py==1.8.0 9 | pycodestyle==2.5.0 10 | pyflakes==2.1.1 11 | pytest==4.4.1 12 | pytest-flake8==1.0.4 13 | six==1.12.0 14 | -------------------------------------------------------------------------------- /tests-for-this-repo/python-lint/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest-flake8 2 | -------------------------------------------------------------------------------- /tests-for-this-repo/ruby.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | 4 | . "${BASH_LIB_DIR}/init" 5 | 6 | teardown(){ 7 | unset curl 8 | } 9 | 10 | 11 | @test "bl_jq_available succeeds when jq is available" { 12 | jq(){ :; } 13 | run bl_jq_available 14 | assert_success 15 | } 16 | 17 | @test "bl_jq_available fails when jq is not available" { 18 | real_path="${PATH}" 19 | PATH="" 20 | run bl_jq_available 21 | PATH="${real_path}" 22 | assert_failure 23 | assert_output --partial "jq not found" 24 | } 25 | 26 | @test "bl_curl_available succeeds when jq is available" { 27 | jq(){ :; } 28 | run bl_curl_available 29 | assert_success 30 | } 31 | 32 | @test "bl_curl_available fails when jq is not available" { 33 | real_path="${PATH}" 34 | PATH="" 35 | run bl_curl_available 36 | PATH="${real_path}" 37 | assert_failure 38 | assert_output --partial "curl not found" 39 | } 40 | 41 | @test "bl_gem_latest_version fails when no gem name is supplied" { 42 | run bl_gem_latest_version 43 | assert_failure 44 | assert_output --partial "usage" 45 | } 46 | 47 | @test "bl_gem_latest_version returns only the version number" { 48 | curl(){ 49 | fixtures_dir="${BASH_LIB_DIR}/tests-for-this-repo/fixtures/ruby" 50 | cat ${fixtures_dir}/ruby_gems_api_response.json 51 | } 52 | 53 | run bl_gem_latest_version parse_a_changelog 54 | assert_success 55 | assert_output "1.0.1" 56 | } 57 | -------------------------------------------------------------------------------- /tests-for-this-repo/run-bats-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # This script runs the self tests for the bash-lib repo. 6 | 7 | # This script is an entry point, so init 8 | # is not assumed to have been run 9 | # shellcheck disable=SC2086,SC2046 10 | . $(dirname ${BASH_SOURCE[0]})/../init 11 | set +eu 12 | 13 | 14 | # Check vital tools are installed 15 | if ! command -v summon >/dev/null; then 16 | bl_die "Summon must be installed and configured in order to run tests" 17 | fi 18 | 19 | if ! command -v docker >/dev/null; then 20 | bl_die "Docker must be installed and configured in order to run tests" 21 | fi 22 | 23 | if ! docker ps >/dev/null; then 24 | bl_die "Docker Daemon must be accessible in order to run tests" 25 | fi 26 | 27 | # could be tap, junit or pretty 28 | readonly BATS_OUTPUT_FORMAT="${BATS_OUTPUT_FORMAT:-pretty}" 29 | readonly BATS_SUITE="${BATS_SUITE:-BATS}" 30 | readonly TAP_FILE="${BASH_LIB_DIR}/bats.tap" 31 | 32 | # return code 33 | rc=0 34 | 35 | if [[ ${#} -eq 0 ]]; then 36 | echo "No test scripts specified, running all." 37 | scripts="${BASH_LIB_DIR}/tests-for-this-repo/*.bats" 38 | else 39 | scripts="${*}" 40 | fi 41 | 42 | readonly summon_cmd="summon -f ${BASH_LIB_DIR}/secrets.yml" 43 | 44 | case $BATS_OUTPUT_FORMAT in 45 | pretty) 46 | # shellcheck disable=SC2086 47 | if ! ${summon_cmd} ${BATS_CMD} ${scripts}; then 48 | rc=1 49 | fi 50 | ;; 51 | tap | junit) 52 | # shellcheck disable=SC2086 53 | if ! ${summon_cmd} ${BATS_CMD} ${scripts} > ${TAP_FILE}; then 54 | rc=1 55 | fi 56 | echo "TAP Output written to ${TAP_FILE}" 57 | ;; 58 | *) 59 | echo "Invalid BATS_OUTPUT_FORMAT: ${BATS_OUTPUT_FORMAT}, valid options: pretty, junit, tap." 60 | exit 1 61 | ;; 62 | esac 63 | 64 | #Convert TAP to Junit when required 65 | if [[ "${BATS_OUTPUT_FORMAT}" == junit ]]; then 66 | # Run tap2junit docker image to convert BATS TAP output to Junit for consumption by jenkins 67 | readonly JUNIT_FILE="${BASH_LIB_DIR}/${BATS_SUITE}-junit.xml" 68 | bl_tap2junit < "${TAP_FILE}" > "${JUNIT_FILE}" 69 | echo "Junit output written to ${JUNIT_FILE}" 70 | fi 71 | 72 | exit ${rc} 73 | -------------------------------------------------------------------------------- /tests-for-this-repo/run-gitleaks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Check this repo for secret leaks 4 | 5 | # This script is an entry point, so init 6 | # is not assumed to have been run 7 | # shellcheck disable=SC2086,SC2046 8 | . $(dirname "${BASH_SOURCE[0]}")/../init 9 | 10 | docker run \ 11 | --rm \ 12 | --name=gitleaks \ 13 | -v ${BASH_LIB_DIR}:/code/ \ 14 | zricethezav/gitleaks:v2.0.0 \ 15 | -v \ 16 | --repo-path=/code/ 17 | -------------------------------------------------------------------------------- /tests-for-this-repo/run-python-lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is an entry point, so init 4 | # is not assumed to have been run 5 | 6 | # shellcheck disable=SC2086,SC2046 7 | . $(dirname ${BASH_SOURCE[0]})/../init 8 | 9 | rc=0 10 | 11 | bl_spushd ${BASH_LIB_DIR}/tests-for-this-repo/python-lint 12 | docker build . -t pytest-flake8 13 | if ! docker run --rm -v "${BASH_LIB_DIR}:/mnt" pytest-flake8; then 14 | rc=1 15 | fi 16 | mv "${BASH_LIB_DIR}/junit.xml" "${BASH_LIB_DIR}/python-lint-junit.xml" 17 | bl_spopd 18 | 19 | exit ${rc} 20 | -------------------------------------------------------------------------------- /tests-for-this-repo/test-utils.bats: -------------------------------------------------------------------------------- 1 | . "${BASH_LIB_DIR}/test-utils/bats-support/load.bash" 2 | . "${BASH_LIB_DIR}/test-utils/bats-assert-1/load.bash" 3 | . "${BASH_LIB_DIR}/init" 4 | 5 | bl_docker_safe_tmp(){ 6 | # neither mktemp -d not $BATS_TMPDIR 7 | # produce dirs that docker can mount from 8 | # in macos. 9 | local -r tmp_dir="/tmp/${RANDOM}/spgs" 10 | ( 11 | rm -rf "${tmp_dir}" 12 | mkdir -p "${tmp_dir}" 13 | ) 1>&2 14 | echo "${tmp_dir}" 15 | } 16 | 17 | @test "bl_shellcheck notices compile error" { 18 | tmp_dir="$(bl_docker_safe_tmp)" 19 | bl_spushd "${tmp_dir}" 20 | 21 | echo "'" > bad_script 22 | run bl_shellcheck_script bad_script 23 | assert_failure 24 | assert_output --partial "syntax error" 25 | 26 | bl_spopd 27 | rm -rf "/tmp/${tmp_dir#/tmp/}" 28 | } 29 | 30 | @test "shellcheck passes good script" { 31 | tmp_dir="$(bl_docker_safe_tmp)" 32 | bl_spushd "${tmp_dir}" 33 | 34 | echo -e "#!/bin/bash\n:" > good_script 35 | run bl_shellcheck_script good_script 36 | rm -rf "${tmp_dir}" 37 | assert_output --partial "Checking good_script" 38 | assert_success 39 | 40 | bl_spopd 41 | rm -rf "/tmp/${tmp_dir#/tmp/}" 42 | } 43 | 44 | @test "bl_find_scripts finds git tracked files containing bash shebang" { 45 | tmp_dir="${BATS_TMPDIR}/ffgtfwse" 46 | rm -rf "${tmp_dir}" 47 | mkdir -p "${tmp_dir}" 48 | pushd ${tmp_dir} 49 | git init 50 | git config user.email "ci@ci.ci" 51 | git config user.name "Jenkins" 52 | 53 | echo '#!/bin/bash' > a 54 | echo '#!/bin/bash' > b 55 | date > c 56 | date > d 57 | 58 | git add a c 59 | SKIP_GITLEAKS=YES git commit -a -m "initial" 60 | 61 | run bl_find_scripts 62 | assert_output "a" 63 | assert_success 64 | popd 65 | } 66 | 67 | @test "bl_tap2junit correctly converts test file" { 68 | rc=0 69 | fdir="${BASH_LIB_DIR}/tests-for-this-repo/fixtures/test-utils" 70 | # Can't use run / assert_output here 71 | # because assert_output uses $output 72 | # which is a combination of stdout and stderr 73 | # and we are only interested in stdout. 74 | stdout=$(bl_tap2junit < "${fdir}/tap2junit.in") 75 | rc=${?} 76 | assert_equal "${stdout}" "$(cat ${fdir}/tap2junit.out)" 77 | assert_equal "${rc}" "0" 78 | } 79 | 80 | @test "bl_validate_changelog validates changelog specified as paremeter" { 81 | run bl_validate_changelog "${BASH_LIB_DIR}"/CHANGELOG.md 82 | assert_success 83 | } 84 | 85 | @test "bl_validate_changelog finds changelog in current directory" { 86 | pushd "${BASH_LIB_DIR}" 87 | run bl_validate_changelog 88 | popd 89 | assert_success 90 | } 91 | 92 | @test "bl_validate_changelog finds changelog in git root" { 93 | pushd "${BASH_LIB_DIR}/tests-for-this-repo" 94 | run bl_validate_changelog 95 | popd 96 | assert_success 97 | } 98 | 99 | @test "bl_validate_changelog fails with invalid changelog path" { 100 | run bl_validate_changelog notavalidchangelog.md 101 | assert_failure 102 | } 103 | 104 | @test "bl_validate_changelog fails with invalid changelog" { 105 | run bl_validate_changelog README.md 106 | assert_failure 107 | } 108 | -------------------------------------------------------------------------------- /tests-for-this-repo/validate-changelog: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | . "$(dirname "${BASH_SOURCE[0]}")/../init" 4 | 5 | bl_validate_changelog 6 | --------------------------------------------------------------------------------