diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..547b5c22 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: php +php: + - '7.0' +sudo: false +cache: + directories: + - $HOME/cachedir +install: git clone --depth 1 https://github.com/sstephenson/bats.git $HOME/bats +before_script: + - 'git config --global user.email "travis@localhost" && git config --global user.name "Travis CI"' + - "export LOCAL_CI_TESTS_CACHEDIR=$HOME/cachedir && mkdir -p $LOCAL_CI_TESTS_CACHEDIR" + - "export LOCAL_CI_TESTS_GITDIR=$HOME/gitdir && git clone git://github.com/moodle/moodle $LOCAL_CI_TESTS_GITDIR" +script: + - $HOME/bats/bin/bats tests + diff --git a/README.md b/README.md index 31ab5407..285f5f8b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # CI local plugin +[![Build Status](https://travis-ci.org/moodlehq/moodle-local_ci.svg?branch=master)](https://travis-ci.org/moodlehq/moodle-local_ci) + This local_ci plugin contains all the scripts needed by Moodle CI servers to automate checks while integration happens. diff --git a/tests/00-setup.bats b/tests/00-setup.bats new file mode 100755 index 00000000..0808691f --- /dev/null +++ b/tests/00-setup.bats @@ -0,0 +1,9 @@ +#!/usr/bin/env bats + +load libs/shared_setup + +# This test is just used as some quick output because some tests are v.slow +@test "Git is setup for tests." { + [ -d "$gitdir/.git" ]; + assert_success +} diff --git a/tests/01-php_lint.bats b/tests/01-php_lint.bats new file mode 100755 index 00000000..3f00aa0f --- /dev/null +++ b/tests/01-php_lint.bats @@ -0,0 +1,40 @@ +#!/usr/bin/env bats + +load libs/shared_setup + +setup () { + create_git_branch MOODLE_31_STABLE v3.1.0 + + export extrapath=. +} + +@test "php_lint: lib/moodlelib.php lint free" { + cd $gitdir + export GIT_PREVIOUS_COMMIT=$($gitcmd rev-parse HEAD) + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-php_lint-ok.patch + export GIT_COMMIT=$($gitcmd rev-parse HEAD) + + cd $BATS_TEST_DIRNAME/../php_lint/ + run ./php_lint.sh + assert_success + assert_output --partial "Running php syntax check from $GIT_PREVIOUS_COMMIT to $GIT_COMMIT" + assert_output --partial "lib/moodlelib.php - OK" + assert_output --partial "No PHP syntax errors found" +} + +@test "php_lint: lib/moodlelib.php lint error detected" { + cd $gitdir + export GIT_PREVIOUS_COMMIT=$($gitcmd rev-parse HEAD) + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-php_lint-bad.patch + export GIT_COMMIT=$($gitcmd rev-parse HEAD) + + # Run test + cd $BATS_TEST_DIRNAME/../php_lint/ + run ./php_lint.sh + + # Assert result + assert_failure + assert_output --partial "Running php syntax check from $GIT_PREVIOUS_COMMIT to $GIT_COMMIT" + assert_output --partial "lib/moodlelib.php - ERROR:" + assert_output --regexp "PHP syntax errors found." +} diff --git a/tests/01-thirdparty_check.bats b/tests/01-thirdparty_check.bats new file mode 100755 index 00000000..5972fab4 --- /dev/null +++ b/tests/01-thirdparty_check.bats @@ -0,0 +1,38 @@ +#!/usr/bin/env bats + +load libs/shared_setup + +setup () { + create_git_branch MOODLE_31_STABLE v3.1.0 + + export extrapath=. +} + +@test "thirdparty_check: thirdpartyfile modified OK" { + cd $gitdir + export initialcommit=$($gitcmd rev-parse HEAD) + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-thirdparty-ok.patch + export finalcommit=$($gitcmd rev-parse HEAD) + + cd $BATS_TEST_DIRNAME/../thirdparty_check/ + run ./thirdparty_check.sh + assert_success + assert_output --partial "INFO: Checking for third party modifications from $initialcommit to $finalcommit" + assert_output --partial "INFO: Detected third party modification in lib/amd/src/mustache.js" + assert_output --partial "INFO: OK lib/thirdpartylibs.xml modified" + refute_output --partial "WARN:" +} + +@test "thirdparty_check: thirdpartyfile modified without update" { + cd $gitdir + export initialcommit=$($gitcmd rev-parse HEAD) + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-thirdparty-error.patch + export finalcommit=$($gitcmd rev-parse HEAD) + + cd $BATS_TEST_DIRNAME/../thirdparty_check/ + run ./thirdparty_check.sh + assert_success # TODO, this should be fixed! + assert_output --partial "INFO: Checking for third party modifications from $initialcommit to $finalcommit" + assert_output --partial "INFO: Detected third party modification in lib/amd/src/mustache.js" + assert_output --partial "WARN: modification to third party library (lib/amd/src/mustache.js) without update to lib/thirdpartylibs.xml or lib/amd/src/readme_moodle.txt" +} diff --git a/tests/02-less_checker.bats b/tests/02-less_checker.bats new file mode 100755 index 00000000..91f6a9f0 --- /dev/null +++ b/tests/02-less_checker.bats @@ -0,0 +1,30 @@ +#!/usr/bin/env bats + +load libs/shared_setup + + +setup () { + create_git_branch MOODLE_27_STABLE v2.7.14 + + export extrapath=. + # Setup recess base. + export recessbase=$LOCAL_CI_TESTS_CACHEDIR/recess + mkdir -p $recessbase +} + +@test "less_checker: normal" { + cd $BATS_TEST_DIRNAME/../less_checker/ + run ./less_checker.sh + assert_success + assert_output --partial "OK: All .less files are perfectly compiled and matching git contents" +} + +@test "less_checker: uncommitted less change" { + cd $gitdir + $gitcmd am $BATS_TEST_DIRNAME/fixtures/27-less-unbuilt.patch + cd $BATS_TEST_DIRNAME/../less_checker/ + run ./less_checker.sh + assert_failure + assert_output --partial "ERROR: Some .less files are not matching git contents. Changes detected:" + assert_output --partial "theme/bootstrapbase/style/moodle.css" +} diff --git a/tests/99-grunt_process.bats b/tests/99-grunt_process.bats new file mode 100755 index 00000000..6a7a4808 --- /dev/null +++ b/tests/99-grunt_process.bats @@ -0,0 +1,70 @@ +#!/usr/bin/env bats + +load libs/shared_setup + + +setup () { + create_git_branch MOODLE_31_STABLE v3.1.0 + + export extrapath=. +} + +@test "grunt_process: normal" { + cd $BATS_TEST_DIRNAME/../grunt_process/ + run ./grunt_process.sh + assert_success + assert_output --partial "Done, without errors." + assert_output --partial "OK: All modules are perfectly processed by grunt" +} + +@test "grunt_process: Uncommited .less change" { + # Create css change. + cd $gitdir + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-grunt-less-unbuilt.patch + + # Run test + cd $BATS_TEST_DIRNAME/../grunt_process/ + run ./grunt_process.sh + + # Assert result + assert_failure + assert_output --partial "Done, without errors." # Grunt shouldn't have an issue here. + assert_output --partial "ERROR: Some modules are not properly processed by grunt. Changes detected:" + assert_output --regexp "GRUNT-CHANGE: (.*)/theme/bootstrapbase/style/moodle.css" +} + +@test "grunt_process: Uncommited .js change" { + # Create js change. + cd $gitdir + $gitcmd am $BATS_TEST_DIRNAME/fixtures/31-grunt-js-unbuilt.patch + + # Run test + cd $BATS_TEST_DIRNAME/../grunt_process/ + run ./grunt_process.sh + + # Assert result + assert_failure + assert_output --partial "Done, without errors." # Grunt shouldn't have an issue here. + assert_output --partial "ERROR: Some modules are not properly processed by grunt. Changes detected:" + assert_output --regexp "GRUNT-CHANGE: (.*)/lib/amd/build/url.min.js" +} + +@test "grunt_process: Uncommited ignorefiles change" { + # When a third party library is added, developers need to commit + # ignorefiles change since 3.2. + + # Testing on in-dev 3.2dev + create_git_branch 32-dev 5a1728df39116fc701cc907e85a638aa7674f416 + cd $gitdir + $gitcmd am $BATS_TEST_DIRNAME/fixtures/32-thirdparty-lib-added.patch + + # Run test + cd $BATS_TEST_DIRNAME/../grunt_process/ + run ./grunt_process.sh + + # Assert result + assert_failure + assert_output --partial "ERROR: Some modules are not properly processed by grunt. Changes detected:" + assert_output --regexp "GRUNT-CHANGE: (.*)/.eslintignore" +} + diff --git a/tests/99-shifter_walk.bats b/tests/99-shifter_walk.bats new file mode 100755 index 00000000..b6679c4a --- /dev/null +++ b/tests/99-shifter_walk.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load libs/shared_setup + +setup () { + create_git_branch MOODLE_27_STABLE v2.7.14 + + export extrapath=. + # setup shifter base. + export shifterbase=$LOCAL_CI_TESTS_CACHEDIR/shifter + mkdir -p $shifterbase +} + +@test "shifter_walk: normal" { + cd $BATS_TEST_DIRNAME/../shifter_walk/ + run ./shifter_walk.sh + assert_success + assert_output --partial "OK: All modules are perfectly shiftered" +} + +@test "shifter_walk: Uncommitted .js change" { + cd $gitdir + $gitcmd am $BATS_TEST_DIRNAME/fixtures/27-shifter-unbuildjs.patch + cd $BATS_TEST_DIRNAME/../shifter_walk/ + run ./shifter_walk.sh + assert_failure + assert_output --partial "ERROR: Some modules are not properly shiftered. Changes detected:" + assert_output --partial "lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js" +} diff --git a/tests/fixtures/27-less-unbuilt.patch b/tests/fixtures/27-less-unbuilt.patch new file mode 100644 index 00000000..1e9506fa --- /dev/null +++ b/tests/fixtures/27-less-unbuilt.patch @@ -0,0 +1,26 @@ +From 793df60c6b5d4733191ef7d2c2b19b940803c066 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 22:35:27 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: for less tests + +This should be applied on top of v2.7.14 +--- + theme/bootstrapbase/less/moodle/reports.less | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/theme/bootstrapbase/less/moodle/reports.less b/theme/bootstrapbase/less/moodle/reports.less +index 6c90a80..211c709 100644 +--- a/theme/bootstrapbase/less/moodle/reports.less ++++ b/theme/bootstrapbase/less/moodle/reports.less +@@ -15,4 +15,6 @@ + + #page-report-participation-index.dir-rtl .participationselectform div label[for=menuinstanceid] { + margin-right: 0px; // No right margin for RTL. +-} +\ No newline at end of file ++} ++ ++.integrationcsschange{display:none;} +-- +2.8.1 + diff --git a/tests/fixtures/27-shifter-unbuildjs.patch b/tests/fixtures/27-shifter-unbuildjs.patch new file mode 100644 index 00000000..012d9e5a --- /dev/null +++ b/tests/fixtures/27-shifter-unbuildjs.patch @@ -0,0 +1,26 @@ +From f2e278d056561d0d6fbce54be6d187a514640553 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 22:20:00 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: for shifter tests + +This should be applied on top of v2.7.14 +--- + lib/editor/atto/yui/src/editor/js/editor.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/editor/atto/yui/src/editor/js/editor.js b/lib/editor/atto/yui/src/editor/js/editor.js +index ca6ca55..5260475 100644 +--- a/lib/editor/atto/yui/src/editor/js/editor.js ++++ b/lib/editor/atto/yui/src/editor/js/editor.js +@@ -363,7 +363,7 @@ Y.extend(Editor, Y.Base, { + }, + + _setPluginState: function(enable, plugin) { +- var target = 'disableButtons'; ++ var target = 'disableButtons-dan-woz-ere'; + if (enable) { + target = 'enableButtons'; + } +-- +2.8.1 + diff --git a/tests/fixtures/31-grunt-js-unbuilt.patch b/tests/fixtures/31-grunt-js-unbuilt.patch new file mode 100644 index 00000000..7c4bbc19 --- /dev/null +++ b/tests/fixtures/31-grunt-js-unbuilt.patch @@ -0,0 +1,26 @@ +From ca0ff741dab36b760e20a37418fcdb139885eb79 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 23:06:36 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: unbuilt js change for grunt + +This should be applied on top of v3.1.0 +--- + lib/amd/src/url.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/amd/src/url.js b/lib/amd/src/url.js +index b3263ee..b6348de 100644 +--- a/lib/amd/src/url.js ++++ b/lib/amd/src/url.js +@@ -61,7 +61,7 @@ define(['core/config'], function(config) { + relativeUrl: function(relativePath) { + + if (relativePath.indexOf('http:') === 0 || relativePath.indexOf('https:') === 0 || relativePath.indexOf('://') >= 0) { +- throw new Error('relativeUrl function does not accept absolute urls'); ++ throw new Error('relativeUrl function does not accept absolute urls. danp woz ere!'); + } + + // Fix non-relative paths; +-- +2.8.1 + diff --git a/tests/fixtures/31-grunt-less-unbuilt.patch b/tests/fixtures/31-grunt-less-unbuilt.patch new file mode 100644 index 00000000..d7da32c2 --- /dev/null +++ b/tests/fixtures/31-grunt-less-unbuilt.patch @@ -0,0 +1,26 @@ +From 205c355bc8ef7bfb52695cc42f2d98826759364b Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 22:35:27 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: less unbuilt for grunt tests + +This should be applied on top of v3.1.0 +--- + theme/bootstrapbase/less/moodle/reports.less | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/theme/bootstrapbase/less/moodle/reports.less b/theme/bootstrapbase/less/moodle/reports.less +index 6c90a80..211c709 100644 +--- a/theme/bootstrapbase/less/moodle/reports.less ++++ b/theme/bootstrapbase/less/moodle/reports.less +@@ -15,4 +15,6 @@ + + #page-report-participation-index.dir-rtl .participationselectform div label[for=menuinstanceid] { + margin-right: 0px; // No right margin for RTL. +-} +\ No newline at end of file ++} ++ ++.integrationcsschange{display:none;} +-- +2.8.1 + diff --git a/tests/fixtures/31-php_lint-bad.patch b/tests/fixtures/31-php_lint-bad.patch new file mode 100644 index 00000000..e3b4017b --- /dev/null +++ b/tests/fixtures/31-php_lint-bad.patch @@ -0,0 +1,22 @@ +From 7fd18b3698c14916bdfa83f502544ddd0eb65b20 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 22:54:14 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: for php_lint tests + +This should be applied on top of v3.1.0 +--- + lib/moodlelib.php | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/moodlelib.php b/lib/moodlelib.php +index 8160f89..f6c0fd7 100644 +--- a/lib/moodlelib.php ++++ b/lib/moodlelib.php +@@ -9768,3 +9768,4 @@ class lang_string { + return array('forcedstring', 'string', 'lang'); + } + } ++die('oh this is gonna be a problem! +-- +2.8.1 + diff --git a/tests/fixtures/31-php_lint-ok.patch b/tests/fixtures/31-php_lint-ok.patch new file mode 100644 index 00000000..9aeee76e --- /dev/null +++ b/tests/fixtures/31-php_lint-ok.patch @@ -0,0 +1,26 @@ +From c6385705362f492fab3dc90cf5317e372fef60c8 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 22:51:00 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 OK fixture: for php_lint tests + +This should be applied on top of v3.1.0 +--- + lib/moodlelib.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/moodlelib.php b/lib/moodlelib.php +index 8160f89..c7cadf2 100644 +--- a/lib/moodlelib.php ++++ b/lib/moodlelib.php +@@ -9321,7 +9321,7 @@ function object_array_unique($array, $keepkeyassoc = true) { + * @return boolean + */ + function is_primary_admin($userid) { +- $primaryadmin = get_admin(); ++ $primaryadmin = get_admin(); + + if ($userid == $primaryadmin->id) { + return true; +-- +2.8.1 + diff --git a/tests/fixtures/31-thirdparty-error.patch b/tests/fixtures/31-thirdparty-error.patch new file mode 100644 index 00000000..6e74337f --- /dev/null +++ b/tests/fixtures/31-thirdparty-error.patch @@ -0,0 +1,31 @@ +From 096f3a432dcc478a7277889dee49f46f833c39b5 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 23:21:24 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: for thirdparty check + +This should be applied on top of v3.1.0 +--- + lib/amd/src/mustache.js | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/lib/amd/src/mustache.js b/lib/amd/src/mustache.js +index a3ce19e..1075fad 100644 +--- a/lib/amd/src/mustache.js ++++ b/lib/amd/src/mustache.js +@@ -25,13 +25,6 @@ + // + + // Description of import into Moodle: +-// Checkout from https://github.com/moodle/custom-mustache.js +-// Rebase onto latest release tag from https://github.com/janl/mustache.js +-// Copy mustache.js into lib/amd/src/ in Moodle folder. +-// Add the license as a comment to the file and these instructions. +-// Add jshint tags so this file is not linted. +-// Remove the "global define:" comment (hint for linter) +-// Make sure that you have not removed the custom code for '$' and '<'. + + /*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript +-- +2.8.1 + diff --git a/tests/fixtures/31-thirdparty-ok.patch b/tests/fixtures/31-thirdparty-ok.patch new file mode 100644 index 00000000..770611cc --- /dev/null +++ b/tests/fixtures/31-thirdparty-ok.patch @@ -0,0 +1,45 @@ +From c0805275c6a79422632d0fb10bd7fa9f518929a8 Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Wed, 15 Jun 2016 23:21:24 +0100 +Subject: [PATCH 1/1] MDLSITE-4211 fixture: OK thirdparty check + +This should be applied on top of v3.1.0 +--- + lib/amd/src/mustache.js | 7 ------- + lib/thirdpartylibs.xml | 2 +- + 2 files changed, 1 insertion(+), 8 deletions(-) + +diff --git a/lib/amd/src/mustache.js b/lib/amd/src/mustache.js +index a3ce19e..1075fad 100644 +--- a/lib/amd/src/mustache.js ++++ b/lib/amd/src/mustache.js +@@ -25,13 +25,6 @@ + // + + // Description of import into Moodle: +-// Checkout from https://github.com/moodle/custom-mustache.js +-// Rebase onto latest release tag from https://github.com/janl/mustache.js +-// Copy mustache.js into lib/amd/src/ in Moodle folder. +-// Add the license as a comment to the file and these instructions. +-// Add jshint tags so this file is not linted. +-// Remove the "global define:" comment (hint for linter) +-// Make sure that you have not removed the custom code for '$' and '<'. + + /*! + * mustache.js - Logic-less {{mustache}} templates with JavaScript +diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml +index 6440a73..2779103 100644 +--- a/lib/thirdpartylibs.xml ++++ b/lib/thirdpartylibs.xml +@@ -261,7 +261,7 @@ + amd/src/mustache.js + Mustache.js + MIT +- 2.2.1 ++ 2.2.2 + + + graphlib.php +-- +2.8.1 + diff --git a/tests/fixtures/32-thirdparty-lib-added.patch b/tests/fixtures/32-thirdparty-lib-added.patch new file mode 100644 index 00000000..fe3599dc --- /dev/null +++ b/tests/fixtures/32-thirdparty-lib-added.patch @@ -0,0 +1,27 @@ +From 4600d7d6fd5d5bd4f5bae80c3e89b838d95f2b1d Mon Sep 17 00:00:00 2001 +From: Dan Poltawski +Date: Tue, 12 Jul 2016 11:23:56 +0100 +Subject: [PATCH 1/1] MDLSITE-4678 fixture: adding third party lib + +--- + lib/thirdpartylibs.xml | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/lib/thirdpartylibs.xml b/lib/thirdpartylibs.xml +index 6440a73..3385d2f 100644 +--- a/lib/thirdpartylibs.xml ++++ b/lib/thirdpartylibs.xml +@@ -275,4 +275,10 @@ + GPL + 2.3.0 + ++ ++ integrationtest ++ Moodle Integration test for MDLSITE-4678 ++ GPL ++ 2.3.0 ++ + +-- +2.9.0 + diff --git a/tests/libs/bats-assert/load.bash b/tests/libs/bats-assert/load.bash new file mode 100644 index 00000000..ac4a875a --- /dev/null +++ b/tests/libs/bats-assert/load.bash @@ -0,0 +1 @@ +source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash" diff --git a/tests/libs/bats-assert/src/assert.bash b/tests/libs/bats-assert/src/assert.bash new file mode 100644 index 00000000..11947538 --- /dev/null +++ b/tests/libs/bats-assert/src/assert.bash @@ -0,0 +1,720 @@ +# +# bats-assert - Common assertions for Bats +# +# Written in 2016 by Zoltan Tombol +# +# To the extent possible under law, the author(s) have dedicated all +# copyright and related and neighboring rights to this software to the +# public domain worldwide. This software is distributed without any +# warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication +# along with this software. If not, see +# . +# + +# +# assert.bash +# ----------- +# +# Assertions are functions that perform a test and output relevant +# information on failure to help debugging. They return 1 on failure +# and 0 otherwise. +# +# All output is formatted for readability using the functions of +# `output.bash' and sent to the standard error. +# + +# Fail and display the expression if it evaluates to false. +# +# NOTE: The expression must be a simple command. Compound commands, such +# as `[[', can be used only when executed with `bash -c'. +# +# Globals: +# none +# Arguments: +# $1 - expression +# Returns: +# 0 - expression evaluates to TRUE +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert() { + if ! "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion failed' \ + | fail + fi +} + +# Fail and display the expression if it evaluates to true. +# +# NOTE: The expression must be a simple command. Compound commands, such +# as `[[', can be used only when executed with `bash -c'. +# +# Globals: +# none +# Arguments: +# $1 - expression +# Returns: +# 0 - expression evaluates to FALSE +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +refute() { + if "$@"; then + batslib_print_kv_single 10 'expression' "$*" \ + | batslib_decorate 'assertion succeeded, but it was expected to fail' \ + | fail + fi +} + +# Fail and display details if the expected and actual values do not +# equal. Details include both values. +# +# Globals: +# none +# Arguments: +# $1 - actual value +# $2 - expected value +# Returns: +# 0 - values equal +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert_equal() { + if [[ $1 != "$2" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$2" \ + 'actual' "$1" \ + | batslib_decorate 'values do not equal' \ + | fail + fi +} + +# Fail and display details if `$status' is not 0. Details include +# `$status' and `$output'. +# +# Globals: +# status +# output +# Arguments: +# none +# Returns: +# 0 - `$status' is 0 +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert_success() { + if (( status != 0 )); then + { local -ir width=6 + batslib_print_kv_single "$width" 'status' "$status" + batslib_print_kv_single_or_multi "$width" 'output' "$output" + } | batslib_decorate 'command failed' \ + | fail + fi +} + +# Fail and display details if `$status' is 0. Details include `$output'. +# +# Optionally, when the expected status is specified, fail when it does +# not equal `$status'. In this case, details include the expected and +# actual status, and `$output'. +# +# Globals: +# status +# output +# Arguments: +# $1 - [opt] expected status +# Returns: +# 0 - `$status' is not 0, or +# `$status' equals the expected status +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +assert_failure() { + (( $# > 0 )) && local -r expected="$1" + if (( status == 0 )); then + batslib_print_kv_single_or_multi 6 'output' "$output" \ + | batslib_decorate 'command succeeded, but it was expected to fail' \ + | fail + elif (( $# > 0 )) && (( status != expected )); then + { local -ir width=8 + batslib_print_kv_single "$width" \ + 'expected' "$expected" \ + 'actual' "$status" + batslib_print_kv_single_or_multi "$width" \ + 'output' "$output" + } | batslib_decorate 'command failed as expected, but status differs' \ + | fail + fi +} + +# Fail and display details if `$output' does not match the expected +# output. The expected output can be specified either by the first +# parameter or on the standard input. +# +# By default, literal matching is performed. The assertion fails if the +# expected output does not equal `$output'. Details include both values. +# +# Option `--partial' enables partial matching. The assertion fails if +# the expected substring cannot be found in `$output'. +# +# Option `--regexp' enables regular expression matching. The assertion +# fails if the extended regular expression does not match `$output'. An +# invalid regular expression causes an error to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Globals: +# output +# Options: +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - [=STDIN] expected output +# Returns: +# 0 - expected matches the actual output +# 1 - otherwise +# Inputs: +# STDIN - [=$1] expected output +# Outputs: +# STDERR - details, on failure +# error message, on error +assert_output() { + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_output' \ + | fail + return $? + fi + + # Arguments. + local expected + (( $# == 0 )) && expected="$(cat -)" || expected="$1" + + # Matching. + if (( is_mode_regexp )); then + if [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_output' \ + | fail + return $? + fi + if ! [[ $output =~ $expected ]]; then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression does not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output != *"$expected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$expected" \ + 'output' "$output" \ + | batslib_decorate 'output does not contain substring' \ + | fail + fi + else + if [[ $output != "$expected" ]]; then + batslib_print_kv_single_or_multi 8 \ + 'expected' "$expected" \ + 'actual' "$output" \ + | batslib_decorate 'output differs' \ + | fail + fi + fi +} + +# Fail and display details if `$output' matches the unexpected output. +# The unexpected output can be specified either by the first parameter +# or on the standard input. +# +# By default, literal matching is performed. The assertion fails if the +# unexpected output equals `$output'. Details include `$output'. +# +# Option `--partial' enables partial matching. The assertion fails if +# the unexpected substring is found in `$output'. The unexpected +# substring is added to details. +# +# Option `--regexp' enables regular expression matching. The assertion +# fails if the extended regular expression does matches `$output'. The +# regular expression is added to details. An invalid regular expression +# causes an error to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Globals: +# output +# Options: +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - [=STDIN] unexpected output +# Returns: +# 0 - unexpected matches the actual output +# 1 - otherwise +# Inputs: +# STDIN - [=$1] unexpected output +# Outputs: +# STDERR - details, on failure +# error message, on error +refute_output() { + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Arguments. + local unexpected + (( $# == 0 )) && unexpected="$(cat -)" || unexpected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_output' \ + | fail + return $? + fi + + # Matching. + if (( is_mode_regexp )); then + if [[ $output =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single_or_multi 6 \ + 'regexp' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'regular expression should not match output' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ $output == *"$unexpected"* ]]; then + batslib_print_kv_single_or_multi 9 \ + 'substring' "$unexpected" \ + 'output' "$output" \ + | batslib_decorate 'output should not contain substring' \ + | fail + fi + else + if [[ $output == "$unexpected" ]]; then + batslib_print_kv_single_or_multi 6 \ + 'output' "$output" \ + | batslib_decorate 'output equals, but it was expected to differ' \ + | fail + fi + fi +} + +# Fail and display details if the expected line is not found in the +# output (default) or in a specific line of it. +# +# By default, the entire output is searched for the expected line. The +# expected line is matched against every element of `${lines[@]}'. If no +# match is found, the assertion fails. Details include the expected line +# and `${lines[@]}'. +# +# When `--index ' is specified, only the -th line is matched. +# If the expected line does not match `${lines[]}', the assertion +# fails. Details include and the compared lines. +# +# By default, literal matching is performed. A literal match fails if +# the expected string does not equal the matched string. +# +# Option `--partial' enables partial matching. A partial match fails if +# the expected substring is not found in the target string. +# +# Option `--regexp' enables regular expression matching. A regular +# expression match fails if the extended regular expression does not +# match the target string. An invalid regular expression causes an error +# to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Mandatory arguments to long options are mandatory for short options +# too. +# +# Globals: +# output +# lines +# Options: +# -n, --index - match the -th line +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - expected line +# Returns: +# 0 - match found +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +# error message, on error +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +assert_line() { + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + + # Arguments. + local -r expected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$expected'" \ + | batslib_decorate 'ERROR: assert_line' \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if ! [[ ${lines[$idx]} =~ $expected ]]; then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression does not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} != *"$expected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$expected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line does not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} != "$expected" ]]; then + batslib_print_kv_single 8 \ + 'index' "$idx" \ + 'expected' "$expected" \ + 'actual' "${lines[$idx]}" \ + | batslib_decorate 'line differs' \ + | fail + fi + fi + else + # Contained in output. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} =~ $expected ]] && return 0 + done + { local -ar single=( + 'regexp' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'no output line matches regular expression' \ + | fail + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == *"$expected"* ]] && return 0 + done + { local -ar single=( + 'substring' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'no output line contains substring' \ + | fail + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + [[ ${lines[$idx]} == "$expected" ]] && return 0 + done + { local -ar single=( + 'line' "$expected" + ) + local -ar may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}" + } | batslib_decorate 'output does not contain line' \ + | fail + fi + fi +} + +# Fail and display details if the unexpected line is found in the output +# (default) or in a specific line of it. +# +# By default, the entire output is searched for the unexpected line. The +# unexpected line is matched against every element of `${lines[@]}'. If +# a match is found, the assertion fails. Details include the unexpected +# line, the index of the first match and `${lines[@]}' with the matching +# line highlighted if `${lines[@]}' is longer than one line. +# +# When `--index ' is specified, only the -th line is matched. +# If the unexpected line matches `${lines[]}', the assertion fails. +# Details include and the unexpected line. +# +# By default, literal matching is performed. A literal match fails if +# the unexpected string does not equal the matched string. +# +# Option `--partial' enables partial matching. A partial match fails if +# the unexpected substring is found in the target string. When used with +# `--index ', the unexpected substring is also displayed on +# failure. +# +# Option `--regexp' enables regular expression matching. A regular +# expression match fails if the extended regular expression matches the +# target string. When used with `--index ', the regular expression +# is also displayed on failure. An invalid regular expression causes an +# error to be displayed. +# +# It is an error to use partial and regular expression matching +# simultaneously. +# +# Mandatory arguments to long options are mandatory for short options +# too. +# +# Globals: +# output +# lines +# Options: +# -n, --index - match the -th line +# -p, --partial - partial matching +# -e, --regexp - extended regular expression matching +# Arguments: +# $1 - unexpected line +# Returns: +# 0 - match not found +# 1 - otherwise +# Outputs: +# STDERR - details, on failure +# error message, on error +# FIXME(ztombol): Display `${lines[@]}' instead of `$output'! +refute_line() { + local -i is_match_line=0 + local -i is_mode_partial=0 + local -i is_mode_regexp=0 + + # Handle options. + while (( $# > 0 )); do + case "$1" in + -n|--index) + if (( $# < 2 )) || ! [[ $2 =~ ^([0-9]|[1-9][0-9]+)$ ]]; then + echo "\`--index' requires an integer argument: \`$2'" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + is_match_line=1 + local -ri idx="$2" + shift 2 + ;; + -p|--partial) is_mode_partial=1; shift ;; + -e|--regexp) is_mode_regexp=1; shift ;; + --) shift; break ;; + *) break ;; + esac + done + + if (( is_mode_partial )) && (( is_mode_regexp )); then + echo "\`--partial' and \`--regexp' are mutually exclusive" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + + # Arguments. + local -r unexpected="$1" + + if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then + echo "Invalid extended regular expression: \`$unexpected'" \ + | batslib_decorate 'ERROR: refute_line' \ + | fail + return $? + fi + + # Matching. + if (( is_match_line )); then + # Specific line. + if (( is_mode_regexp )); then + if [[ ${lines[$idx]} =~ $unexpected ]] || (( $? == 0 )); then + batslib_print_kv_single 6 \ + 'index' "$idx" \ + 'regexp' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'regular expression should not match line' \ + | fail + fi + elif (( is_mode_partial )); then + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + batslib_print_kv_single 9 \ + 'index' "$idx" \ + 'substring' "$unexpected" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should not contain substring' \ + | fail + fi + else + if [[ ${lines[$idx]} == "$unexpected" ]]; then + batslib_print_kv_single 5 \ + 'index' "$idx" \ + 'line' "${lines[$idx]}" \ + | batslib_decorate 'line should differ' \ + | fail + fi + fi + else + # Line contained in output. + if (( is_mode_regexp )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} =~ $unexpected ]]; then + { local -ar single=( + 'regexp' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'no line should match the regular expression' \ + | fail + return $? + fi + done + elif (( is_mode_partial )); then + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == *"$unexpected"* ]]; then + { local -ar single=( + 'substring' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'no line should contain substring' \ + | fail + return $? + fi + done + else + local -i idx + for (( idx = 0; idx < ${#lines[@]}; ++idx )); do + if [[ ${lines[$idx]} == "$unexpected" ]]; then + { local -ar single=( + 'line' "$unexpected" + 'index' "$idx" + ) + local -a may_be_multi=( + 'output' "$output" + ) + local -ir width="$( batslib_get_max_single_line_key_width \ + "${single[@]}" "${may_be_multi[@]}" )" + batslib_print_kv_single "$width" "${single[@]}" + if batslib_is_single_line "${may_be_multi[1]}"; then + batslib_print_kv_single "$width" "${may_be_multi[@]}" + else + may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" \ + | batslib_prefix \ + | batslib_mark '>' "$idx" )" + batslib_print_kv_multi "${may_be_multi[@]}" + fi + } | batslib_decorate 'line should not be in output' \ + | fail + return $? + fi + done + fi + fi +} diff --git a/tests/libs/bats-support/load.bash b/tests/libs/bats-support/load.bash new file mode 100644 index 00000000..0d4a5ac9 --- /dev/null +++ b/tests/libs/bats-support/load.bash @@ -0,0 +1,2 @@ +source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash" +source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash" diff --git a/tests/libs/bats-support/src/error.bash b/tests/libs/bats-support/src/error.bash new file mode 100644 index 00000000..e5d97912 --- /dev/null +++ b/tests/libs/bats-support/src/error.bash @@ -0,0 +1,41 @@ +# +# bats-support - Supporting library for Bats test helpers +# +# Written in 2016 by Zoltan Tombol +# +# To the extent possible under law, the author(s) have dedicated all +# copyright and related and neighboring rights to this software to the +# public domain worldwide. This software is distributed without any +# warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication +# along with this software. If not, see +# . +# + +# +# error.bash +# ---------- +# +# Functions implementing error reporting. Used by public helper +# functions or test suits directly. +# + +# Fail and display a message. When no parameters are specified, the +# message is read from the standard input. Other functions use this to +# report failure. +# +# Globals: +# none +# Arguments: +# $@ - [=STDIN] message +# Returns: +# 1 - always +# Inputs: +# STDIN - [=$@] message +# Outputs: +# STDERR - message +fail() { + (( $# == 0 )) && batslib_err || batslib_err "$@" + return 1 +} diff --git a/tests/libs/bats-support/src/output.bash b/tests/libs/bats-support/src/output.bash new file mode 100644 index 00000000..c6cf6a6b --- /dev/null +++ b/tests/libs/bats-support/src/output.bash @@ -0,0 +1,279 @@ +# +# bats-support - Supporting library for Bats test helpers +# +# Written in 2016 by Zoltan Tombol +# +# To the extent possible under law, the author(s) have dedicated all +# copyright and related and neighboring rights to this software to the +# public domain worldwide. This software is distributed without any +# warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication +# along with this software. If not, see +# . +# + +# +# output.bash +# ----------- +# +# Private functions implementing output formatting. Used by public +# helper functions. +# + +# Print a message to the standard error. When no parameters are +# specified, the message is read from the standard input. +# +# Globals: +# none +# Arguments: +# $@ - [=STDIN] message +# Returns: +# none +# Inputs: +# STDIN - [=$@] message +# Outputs: +# STDERR - message +batslib_err() { + { if (( $# > 0 )); then + echo "$@" + else + cat - + fi + } >&2 +} + +# Count the number of lines in the given string. +# +# TODO(ztombol): Fix tests and remove this note after #93 is resolved! +# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not +# give the same result as `${#lines[@]}' when the output contains +# empty lines. +# See PR #93 (https://github.com/sstephenson/bats/pull/93). +# +# Globals: +# none +# Arguments: +# $1 - string +# Returns: +# none +# Outputs: +# STDOUT - number of lines +batslib_count_lines() { + local -i n_lines=0 + local line + while IFS='' read -r line || [[ -n $line ]]; do + (( ++n_lines )) + done < <(printf '%s' "$1") + echo "$n_lines" +} + +# Determine whether all strings are single-line. +# +# Globals: +# none +# Arguments: +# $@ - strings +# Returns: +# 0 - all strings are single-line +# 1 - otherwise +batslib_is_single_line() { + for string in "$@"; do + (( $(batslib_count_lines "$string") > 1 )) && return 1 + done + return 0 +} + +# Determine the length of the longest key that has a single-line value. +# +# This function is useful in determining the correct width of the key +# column in two-column format when some keys may have multi-line values +# and thus should be excluded. +# +# Globals: +# none +# Arguments: +# $odd - key +# $even - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - length of longest key +batslib_get_max_single_line_key_width() { + local -i max_len=-1 + while (( $# != 0 )); do + local -i key_len="${#1}" + batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len" + shift 2 + done + echo "$max_len" +} + +# Print key-value pairs in two-column format. +# +# Keys are displayed in the first column, and their corresponding values +# in the second. To evenly line up values, the key column is fixed-width +# and its width is specified with the first parameter (possibly computed +# using `batslib_get_max_single_line_key_width'). +# +# Globals: +# none +# Arguments: +# $1 - width of key column +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_single() { + local -ir col_width="$1"; shift + while (( $# != 0 )); do + printf '%-*s : %s\n' "$col_width" "$1" "$2" + shift 2 + done +} + +# Print key-value pairs in multi-line format. +# +# The key is displayed first with the number of lines of its +# corresponding value in parenthesis. Next, starting on the next line, +# the value is displayed. For better readability, it is recommended to +# indent values using `batslib_prefix'. +# +# Globals: +# none +# Arguments: +# $odd - key +# $even - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_multi() { + while (( $# != 0 )); do + printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )" + printf '%s\n' "$2" + shift 2 + done +} + +# Print all key-value pairs in either two-column or multi-line format +# depending on whether all values are single-line. +# +# If all values are single-line, print all pairs in two-column format +# with the specified key column width (identical to using +# `batslib_print_kv_single'). +# +# Otherwise, print all pairs in multi-line format after indenting values +# with two spaces for readability (identical to using `batslib_prefix' +# and `batslib_print_kv_multi') +# +# Globals: +# none +# Arguments: +# $1 - width of key column (for two-column format) +# $even - key +# $odd - value of the previous key +# Returns: +# none +# Outputs: +# STDOUT - formatted key-value pairs +batslib_print_kv_single_or_multi() { + local -ir width="$1"; shift + local -a pairs=( "$@" ) + + local -a values=() + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + values+=( "${pairs[$i]}" ) + done + + if batslib_is_single_line "${values[@]}"; then + batslib_print_kv_single "$width" "${pairs[@]}" + else + local -i i + for (( i=1; i < ${#pairs[@]}; i+=2 )); do + pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )" + done + batslib_print_kv_multi "${pairs[@]}" + fi +} + +# Prefix each line read from the standard input with the given string. +# +# Globals: +# none +# Arguments: +# $1 - [= ] prefix string +# Returns: +# none +# Inputs: +# STDIN - lines +# Outputs: +# STDOUT - prefixed lines +batslib_prefix() { + local -r prefix="${1:- }" + local line + while IFS='' read -r line || [[ -n $line ]]; do + printf '%s%s\n' "$prefix" "$line" + done +} + +# Mark select lines of the text read from the standard input by +# overwriting their beginning with the given string. +# +# Usually the input is indented by a few spaces using `batslib_prefix' +# first. +# +# Globals: +# none +# Arguments: +# $1 - marking string +# $@ - indices (zero-based) of lines to mark +# Returns: +# none +# Inputs: +# STDIN - lines +# Outputs: +# STDOUT - lines after marking +batslib_mark() { + local -r symbol="$1"; shift + # Sort line numbers. + set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" ) + + local line + local -i idx=0 + while IFS='' read -r line || [[ -n $line ]]; do + if (( ${1:--1} == idx )); then + printf '%s\n' "${symbol}${line:${#symbol}}" + shift + else + printf '%s\n' "$line" + fi + (( ++idx )) + done +} + +# Enclose the input text in header and footer lines. +# +# The header contains the given string as title. The output is preceded +# and followed by an additional newline to make it stand out more. +# +# Globals: +# none +# Arguments: +# $1 - title +# Returns: +# none +# Inputs: +# STDIN - text +# Outputs: +# STDOUT - decorated text +batslib_decorate() { + echo + echo "-- $1 --" + cat - + echo '--' + echo +} diff --git a/tests/libs/shared_setup.bash b/tests/libs/shared_setup.bash new file mode 100644 index 00000000..5f9ad6e7 --- /dev/null +++ b/tests/libs/shared_setup.bash @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e +load 'libs/bats-support/load' +load 'libs/bats-assert/load' + +if [ -z $LOCAL_CI_TESTS_CACHEDIR ]; then + echo "Please define LOCAL_CI_TESTS_CACHEDIR" >&2 + exit 1; +fi + +if [ ! -d $LOCAL_CI_TESTS_CACHEDIR ]; then + echo "Please ensure LOCAL_CI_TESTS_CACHEDIR is a directory" >&2 + exit 1 +fi + +if [ -z $LOCAL_CI_TESTS_GITDIR ]; then + echo "Please define LOCAL_CI_TESTS_GITDIR. It should be a git clone of moodle.git." >&2 + echo "IT WILL CAUSE DESTRUCTIVE CHANGES TO THE GIT REPO, DO NOT SHARE IT WITH YOUR CODE!" >&2 + exit 1; +fi + +export WORKSPACE=$BATS_TMPDIR/workspace +mkdir -p $WORKSPACE +export gitcmd=git +export phpcmd=php +export npmcmd=npm +export npmbase=$LOCAL_CI_TESTS_CACHEDIR/npmbase +mkdir -p $npmbase +export gitdir=$LOCAL_CI_TESTS_GITDIR + +create_git_branch () { + branch=$1 + resetto=$2 + + cd $gitdir + $gitcmd checkout . -q + $gitcmd clean -fd -q + $gitcmd checkout -B $branch -q + $gitcmd reset --hard $resetto -q + + export gitbranch=$branch + cd $BATS_TEST_DIRNAME +} +