mirror of
https://github.com/Llewellynvdm/Tomb.git
synced 2024-11-25 22:27:34 +00:00
first basic implementation of portable tomb
this new "flavor" of tomb uses veracrypt for mounted volumes and POSIX sh only for its scripting, is a work in progress and still lacks full functionality, but provides a proof-of-concept to be developed further if needs arise.
This commit is contained in:
parent
12684e6740
commit
be911b1e16
47
portable/GNUmakefile
Normal file
47
portable/GNUmakefile
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
.PHONY: test
|
||||||
|
|
||||||
|
system := $(shell uname -s)
|
||||||
|
# if [ ${system} == "FreeBSD" ]; then sudo kldload fusefs; fi
|
||||||
|
|
||||||
|
TOMB ?= test.tomb
|
||||||
|
|
||||||
|
#########
|
||||||
|
## BUILD
|
||||||
|
#########
|
||||||
|
all:
|
||||||
|
${CC} -O2 -Wall -Wextra --std=c99 --pedantic -o tomb-str2size strtosize.c
|
||||||
|
|
||||||
|
########
|
||||||
|
## TEST
|
||||||
|
########
|
||||||
|
create-open-close:
|
||||||
|
TOMB=${TOMB} ./test/bats/bin/bats ./test/create_open_close.bats
|
||||||
|
chmod a+r ${TOMB}
|
||||||
|
|
||||||
|
# arg: TOMB='path to tomb containing random.data'
|
||||||
|
# assumes $TOMB.hash is the pre-calculated hash on creation
|
||||||
|
check-random-data:
|
||||||
|
TOMB=${TOMB} ./test/bats/bin/bats test/check-random-data.bats
|
||||||
|
|
||||||
|
########
|
||||||
|
## LINT
|
||||||
|
########
|
||||||
|
shellcheck:
|
||||||
|
shellcheck -s sh tomb -e SC2006,SC2059,SC2034
|
||||||
|
|
||||||
|
########
|
||||||
|
## DEPS
|
||||||
|
########
|
||||||
|
download-deps-ubuntu22:
|
||||||
|
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu22-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||||
|
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||||
|
|
||||||
|
download-deps-ubuntu20:
|
||||||
|
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu20-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||||
|
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||||
|
|
||||||
|
download-deps-freebsd13:
|
||||||
|
curl https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
|
||||||
|
curl https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
||||||
|
|
||||||
|
#curl https://files.dyne.org/zenroom/nightly/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
|
64
portable/README.md
Normal file
64
portable/README.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
![Tomb's skull icon](docs/monmort.jpg)
|
||||||
|
|
||||||
|
# Portable Tomb :: the crypto undertaker runs everywhere
|
||||||
|
|
||||||
|
<!-- [![continuous integration tests badge](https://github.com/dyne/tomb3/actions/workflows/ci.yml/badge.svg)](https://github.com/dyne/Tomb3/actions) test coverage status for Ubuntu-20, FreeBSD-13 and Mac/OSX. -->
|
||||||
|
## ⚠️ WORK IN PROGRESS 🛠️
|
||||||
|
|
||||||
|
This is the portable version of [Tomb](https://github.com/dyne/tomb) using [Veracrypt](https://www.veracrypt.fr) in place of cryptsetup.
|
||||||
|
|
||||||
|
[![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org)
|
||||||
|
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
Portable tomb achieves direct **interoperable access to tomb volumes** between:
|
||||||
|
|
||||||
|
- GNU/Linux
|
||||||
|
- MS/Windows using WSL2
|
||||||
|
- Apple/OSX using [MacFUSE](https://osxfuse.github.io/)
|
||||||
|
- FreeBSD
|
||||||
|
|
||||||
|
as well reduce dependencies and in particular use only the **POSIX sh interpreter**.
|
||||||
|
|
||||||
|
## References:
|
||||||
|
- [Milestone on v2 repo](https://github.com/dyne/Tomb/milestone/8)
|
||||||
|
- [Feature poll](https://github.com/dyne/Tomb/issues/409)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
The following v2 features will be ported:
|
||||||
|
|
||||||
|
[ ] mount bind
|
||||||
|
[ ] ps / slam
|
||||||
|
[ ] resize
|
||||||
|
[ ] index & search
|
||||||
|
[ ] bury / exhume
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- FreeBSD: `fusefs-libs3 fusefs-lkl e2fsprogs util-linux`
|
||||||
|
- Linux: `fuse3 util-linux`
|
||||||
|
- crossplatform [Veracrypt binaries](https://files.dyne.org/tomb3/third-party) console-only
|
||||||
|
|
||||||
|
### Note on Veracrypt
|
||||||
|
|
||||||
|
The way upstream developers distribute Veracrypt is far from meeting our minimalist needs, but the console-only binary once installed has a few library dependencies and is all what we need. We will likely setup our own build (and eventually need to [fork veracrypt's commandline-client interface](https://github.com/dyne/Tomb3/issues/2)) meanwhile binary builds of Veracrypt v1.25.9 are provided for download on our own file repository for testing purposes: **use at your own risk**.
|
||||||
|
|
||||||
|
# Disclaimer
|
||||||
|
|
||||||
|
Tomb is Copyright (C) 2007-2022 by the Dyne.org Foundation and
|
||||||
|
developed by [Jaromil](https://github.com/jaromil).
|
||||||
|
|
||||||
|
This source code is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This source code is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
|
||||||
|
to the GNU Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Public License along with
|
||||||
|
this source code; if not, write to: Free Software Foundation, Inc.,
|
||||||
|
675 Mass Ave, Cambridge, MA 02139, USA.
|
214
portable/strtosize.c
Normal file
214
portable/strtosize.c
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/* strtosize from util-linux */
|
||||||
|
|
||||||
|
/* No copyright is claimed. This code is in the public domain. */
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
static int do_scale_by_power (uintmax_t *x, int base, int power)
|
||||||
|
{
|
||||||
|
while (power--) {
|
||||||
|
if (UINTMAX_MAX / base < *x)
|
||||||
|
return -ERANGE;
|
||||||
|
*x *= base;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* strtosize() - convert string to size (uintmax_t).
|
||||||
|
*
|
||||||
|
* Supported suffixes:
|
||||||
|
*
|
||||||
|
* XiB or X for 2^N
|
||||||
|
* where X = {K,M,G,T,P,E,Z,Y}
|
||||||
|
* or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
|
||||||
|
* for example:
|
||||||
|
* 10KiB = 10240
|
||||||
|
* 10K = 10240
|
||||||
|
*
|
||||||
|
* XB for 10^N
|
||||||
|
* where X = {K,M,G,T,P,E,Z,Y}
|
||||||
|
* for example:
|
||||||
|
* 10KB = 10000
|
||||||
|
*
|
||||||
|
* The optional 'power' variable returns number associated with used suffix
|
||||||
|
* {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
|
||||||
|
*
|
||||||
|
* The function also supports decimal point, for example:
|
||||||
|
* 0.5MB = 500000
|
||||||
|
* 0.5MiB = 512000
|
||||||
|
*
|
||||||
|
* Note that the function does not accept numbers with '-' (negative sign)
|
||||||
|
* prefix.
|
||||||
|
*/
|
||||||
|
int strtosize(const char *str, uintmax_t *res, int *power)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
char *end;
|
||||||
|
uintmax_t x, frac = 0;
|
||||||
|
int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
|
||||||
|
|
||||||
|
static const char *suf = "KMGTPEZY";
|
||||||
|
static const char *suf2 = "kmgtpezy";
|
||||||
|
const char *sp;
|
||||||
|
|
||||||
|
*res = 0;
|
||||||
|
|
||||||
|
if (!str || !*str) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only positive numbers are acceptable
|
||||||
|
*
|
||||||
|
* Note that this check is not perfect, it would be better to
|
||||||
|
* use lconv->negative_sign. But coreutils use the same solution,
|
||||||
|
* so it's probably good enough...
|
||||||
|
*/
|
||||||
|
p = str;
|
||||||
|
while (isspace((unsigned char) *p))
|
||||||
|
p++;
|
||||||
|
if (*p == '-') {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0, end = NULL;
|
||||||
|
x = strtoumax(str, &end, 0);
|
||||||
|
|
||||||
|
if (end == str ||
|
||||||
|
(errno != 0 && (x == UINTMAX_MAX || x == 0))) {
|
||||||
|
rc = errno ? -errno : -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (!end || !*end)
|
||||||
|
goto done; /* without suffix */
|
||||||
|
p = end;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check size suffixes
|
||||||
|
*/
|
||||||
|
check_suffix:
|
||||||
|
if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
|
||||||
|
base = 1024; /* XiB, 2^N */
|
||||||
|
else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
|
||||||
|
base = 1000; /* XB, 10^N */
|
||||||
|
else if (*(p + 1)) {
|
||||||
|
struct lconv const *l = localeconv();
|
||||||
|
const char *dp = l ? l->decimal_point : NULL;
|
||||||
|
size_t dpsz = dp ? strlen(dp) : 0;
|
||||||
|
|
||||||
|
if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
|
||||||
|
const char *fstr = p + dpsz;
|
||||||
|
|
||||||
|
for (p = fstr; *p == '0'; p++)
|
||||||
|
frac_zeros++;
|
||||||
|
fstr = p;
|
||||||
|
if (isdigit(*fstr)) {
|
||||||
|
errno = 0, end = NULL;
|
||||||
|
frac = strtoumax(fstr, &end, 0);
|
||||||
|
if (end == fstr ||
|
||||||
|
(errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
|
||||||
|
rc = errno ? -errno : -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
end = (char *) p;
|
||||||
|
|
||||||
|
if (frac && (!end || !*end)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err; /* without suffix, but with frac */
|
||||||
|
}
|
||||||
|
p = end;
|
||||||
|
goto check_suffix;
|
||||||
|
}
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err; /* unexpected suffix */
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = strchr(suf, *p);
|
||||||
|
if (sp)
|
||||||
|
pwr = (sp - suf) + 1;
|
||||||
|
else {
|
||||||
|
sp = strchr(suf2, *p);
|
||||||
|
if (sp)
|
||||||
|
pwr = (sp - suf2) + 1;
|
||||||
|
else {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = do_scale_by_power(&x, base, pwr);
|
||||||
|
if (power)
|
||||||
|
*power = pwr;
|
||||||
|
if (frac && pwr) {
|
||||||
|
int i;
|
||||||
|
uintmax_t frac_div = 10, frac_poz = 1, frac_base = 1;
|
||||||
|
|
||||||
|
/* mega, giga, ... */
|
||||||
|
do_scale_by_power(&frac_base, base, pwr);
|
||||||
|
|
||||||
|
/* maximal divisor for last digit (e.g. for 0.05 is
|
||||||
|
* frac_div=100, for 0.054 is frac_div=1000, etc.)
|
||||||
|
*
|
||||||
|
* Reduce frac if too large.
|
||||||
|
*/
|
||||||
|
while (frac_div < frac) {
|
||||||
|
if (frac_div <= UINTMAX_MAX/10)
|
||||||
|
frac_div *= 10;
|
||||||
|
else
|
||||||
|
frac /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 'frac' is without zeros (5 means 0.5 as well as 0.05) */
|
||||||
|
for (i = 0; i < frac_zeros; i++) {
|
||||||
|
if (frac_div <= UINTMAX_MAX/10)
|
||||||
|
frac_div *= 10;
|
||||||
|
else
|
||||||
|
frac /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Go backwardly from last digit and add to result what the
|
||||||
|
* digit represents in the frac_base. For example 0.25G
|
||||||
|
*
|
||||||
|
* 5 means 1GiB / (100/5)
|
||||||
|
* 2 means 1GiB / (10/2)
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
unsigned int seg = frac % 10; /* last digit of the frac */
|
||||||
|
uintmax_t seg_div = frac_div / frac_poz; /* what represents the segment 1000, 100, .. */
|
||||||
|
|
||||||
|
frac /= 10; /* remove last digit from frac */
|
||||||
|
frac_poz *= 10;
|
||||||
|
|
||||||
|
if (seg && seg_div / seg)
|
||||||
|
x += frac_base / (seg_div / seg);
|
||||||
|
} while (frac);
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
*res = x;
|
||||||
|
err:
|
||||||
|
if (rc < 0)
|
||||||
|
errno = -rc;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
// TODO: secure check args
|
||||||
|
uint64_t size;
|
||||||
|
if(argc!=2) return(1);
|
||||||
|
int res = strtosize(argv[1], &size, NULL);
|
||||||
|
if(res==0) {
|
||||||
|
fprintf(stdout,"%lu\n",size);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,"%s\n",strerror(errno));
|
||||||
|
}
|
||||||
|
return(res);
|
||||||
|
}
|
59
portable/test/bats/bin/bats
Executable file
59
portable/test/bats/bin/bats
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if command -v greadlink >/dev/null; then
|
||||||
|
bats_readlinkf() {
|
||||||
|
greadlink -f "$1"
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bats_readlinkf() {
|
||||||
|
readlink -f "$1"
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
fallback_to_readlinkf_posix() {
|
||||||
|
bats_readlinkf() {
|
||||||
|
[ "${1:-}" ] || return 1
|
||||||
|
max_symlinks=40
|
||||||
|
CDPATH='' # to avoid changing to an unexpected directory
|
||||||
|
|
||||||
|
target=$1
|
||||||
|
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
|
||||||
|
[ -d "${target:-/}" ] && target="$target/"
|
||||||
|
|
||||||
|
cd -P . 2>/dev/null || return 1
|
||||||
|
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
|
||||||
|
if [ ! "$target" = "${target%/*}" ]; then
|
||||||
|
case $target in
|
||||||
|
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
|
||||||
|
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
|
||||||
|
esac
|
||||||
|
target=${target##*/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -L "$target" ]; then
|
||||||
|
target="${PWD%/}${target:+/}${target}"
|
||||||
|
printf '%s\n' "${target:-/}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
|
||||||
|
# <file mode>, <number of links>, <owner name>, <group name>,
|
||||||
|
# <size>, <date and time>, <pathname of link>, <contents of link>
|
||||||
|
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
|
||||||
|
link=$(ls -dl -- "$target" 2>/dev/null) || break
|
||||||
|
target=${link#*" $target -> "}
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
|
||||||
|
fallback_to_readlinkf_posix
|
||||||
|
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BATS_ROOT=${BATS_PATH%/*/*}
|
||||||
|
export -f bats_readlinkf
|
||||||
|
exec env BATS_ROOT="$BATS_ROOT" "$BATS_ROOT/libexec/bats-core/bats" "$@"
|
98
portable/test/bats/lib/bats-core/common.bash
Normal file
98
portable/test/bats/lib/bats-core/common.bash
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
bats_prefix_lines_for_tap_output() {
|
||||||
|
while IFS= read -r line; do
|
||||||
|
printf '# %s\n' "$line" || break # avoid feedback loop when errors are redirected into BATS_OUT (see #353)
|
||||||
|
done
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
printf '# %s\n' "$line"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function bats_replace_filename() {
|
||||||
|
local line
|
||||||
|
while read -r line; do
|
||||||
|
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
||||||
|
done
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_quote_code() { # <var> <code>
|
||||||
|
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_check_valid_version() {
|
||||||
|
if [[ ! $1 =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
|
||||||
|
printf "ERROR: version '%s' must be of format <major>.<minor>.<patch>!\n" "$1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# compares two versions. Return 0 when version1 < version2
|
||||||
|
bats_version_lt() { # <version1> <version2>
|
||||||
|
bats_check_valid_version "$1"
|
||||||
|
bats_check_valid_version "$2"
|
||||||
|
|
||||||
|
local -a version1_parts version2_parts
|
||||||
|
IFS=. read -ra version1_parts <<< "$1"
|
||||||
|
IFS=. read -ra version2_parts <<< "$2"
|
||||||
|
|
||||||
|
for i in {0..2}; do
|
||||||
|
if (( version1_parts[i] < version2_parts[i] )); then
|
||||||
|
return 0
|
||||||
|
elif (( version1_parts[i] > version2_parts[i] )); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# if we made it this far, they are equal -> also not less then
|
||||||
|
return 2 # use other failing return code to distinguish equal from gt
|
||||||
|
}
|
||||||
|
|
||||||
|
# ensure a minimum version of bats is running or exit with failure
|
||||||
|
bats_require_minimum_version() { # <required version>
|
||||||
|
local required_minimum_version=$1
|
||||||
|
|
||||||
|
if bats_version_lt "$BATS_VERSION" "$required_minimum_version"; then
|
||||||
|
printf "BATS_VERSION=%s does not meet required minimum %s\n" "$BATS_VERSION" "$required_minimum_version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$required_minimum_version"; then
|
||||||
|
BATS_GUARANTEED_MINIMUM_VERSION="$required_minimum_version"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_binary_search() { # <search-value> <array-name>
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
printf "ERROR: bats_binary_search requires exactly 2 arguments: <search value> <array name>\n" >&2
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
local -r search_value=$1 array_name=$2
|
||||||
|
|
||||||
|
# we'd like to test if array is set but we cannot distinguish unset from empty arrays, so we need to skip that
|
||||||
|
|
||||||
|
local start=0 mid end mid_value
|
||||||
|
# start is inclusive, end is exclusive ...
|
||||||
|
eval "end=\${#${array_name}[@]}"
|
||||||
|
|
||||||
|
# so start == end means empty search space
|
||||||
|
while (( start < end )); do
|
||||||
|
mid=$(( (start + end) / 2 ))
|
||||||
|
eval "mid_value=\${${array_name}[$mid]}"
|
||||||
|
if [[ "$mid_value" == "$search_value" ]]; then
|
||||||
|
return 0
|
||||||
|
elif [[ "$mid_value" < "$search_value" ]]; then
|
||||||
|
# This branch excludes equality -> +1 to skip the mid element.
|
||||||
|
# This +1 also avoids endless recursion on odd sized search ranges.
|
||||||
|
start=$((mid + 1))
|
||||||
|
else
|
||||||
|
end=$mid
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# did not find it -> its not there
|
||||||
|
return 1
|
||||||
|
}
|
116
portable/test/bats/lib/bats-core/formatter.bash
Normal file
116
portable/test/bats/lib/bats-core/formatter.bash
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# reads (extended) bats tap streams from stdin and calls callback functions for each line
|
||||||
|
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
|
||||||
|
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
|
||||||
|
# bats_tap_stream_ok [--duration <milliseconds] <test index> <test name> -> when a test was successful
|
||||||
|
# bats_tap_stream_not_ok [--duration <milliseconds>] <test index> <test name> -> when a test has failed
|
||||||
|
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
|
||||||
|
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
|
||||||
|
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
|
||||||
|
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
|
||||||
|
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
|
||||||
|
# scope @see bats_tap_stream_comment
|
||||||
|
# forwards all input as is, when there is no TAP test plan header
|
||||||
|
function bats_parse_internal_extended_tap() {
|
||||||
|
local header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
bats_tap_stream_plan "${header:3}"
|
||||||
|
else
|
||||||
|
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||||
|
printf '%s\n' "$header"
|
||||||
|
exec cat
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok_line_regexpr="ok ([0-9]+) (.*)"
|
||||||
|
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
|
||||||
|
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
|
||||||
|
|
||||||
|
timing_expr="in ([0-9]+)ms$"
|
||||||
|
local test_name begin_index ok_index not_ok_index index scope
|
||||||
|
begin_index=0
|
||||||
|
index=0
|
||||||
|
scope=plan
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'begin '*) # this might only be called in extended tap output
|
||||||
|
((++begin_index))
|
||||||
|
scope=begin
|
||||||
|
test_name="${line#* "$begin_index" }"
|
||||||
|
bats_tap_stream_begin "$begin_index" "$test_name"
|
||||||
|
;;
|
||||||
|
'ok '*)
|
||||||
|
((++index))
|
||||||
|
if [[ "$line" =~ $ok_line_regexpr ]]; then
|
||||||
|
ok_index="${BASH_REMATCH[1]}"
|
||||||
|
test_name="${BASH_REMATCH[2]}"
|
||||||
|
if [[ "$line" =~ $skip_line_regexpr ]]; then
|
||||||
|
scope=skipped
|
||||||
|
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
|
||||||
|
local skip_reason="${BASH_REMATCH[4]}"
|
||||||
|
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
|
||||||
|
else
|
||||||
|
scope=ok
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
|
||||||
|
else
|
||||||
|
bats_tap_stream_ok "$ok_index" "$test_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "ERROR: could not match ok line: %s" "$line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'not ok '*)
|
||||||
|
((++index))
|
||||||
|
scope=not_ok
|
||||||
|
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
|
||||||
|
not_ok_index="${BASH_REMATCH[1]}"
|
||||||
|
test_name="${BASH_REMATCH[2]}"
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
bats_tap_stream_not_ok --duration "${BASH_REMATCH[1]}" "$not_ok_index" "$test_name"
|
||||||
|
else
|
||||||
|
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "ERROR: could not match not ok line: %s" "$line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'# '*)
|
||||||
|
bats_tap_stream_comment "${line:2}" "$scope"
|
||||||
|
;;
|
||||||
|
'#')
|
||||||
|
bats_tap_stream_comment "" "$scope"
|
||||||
|
;;
|
||||||
|
'suite '*)
|
||||||
|
scope=suite
|
||||||
|
# pass on the
|
||||||
|
bats_tap_stream_suite "${line:6}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
bats_tap_stream_unknown "$line" "$scope"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize_base_path() { # <target variable> <base path>
|
||||||
|
# the relative path root to use for reporting filenames
|
||||||
|
# this is mainly intended for suite mode, where this will be the suite root folder
|
||||||
|
local base_path="$2"
|
||||||
|
# use the containing directory when --base-path is a file
|
||||||
|
if [[ ! -d "$base_path" ]]; then
|
||||||
|
base_path="$(dirname "$base_path")"
|
||||||
|
fi
|
||||||
|
# get the absolute path
|
||||||
|
base_path="$(cd "$base_path" && pwd)"
|
||||||
|
# ensure the path ends with / to strip that later on
|
||||||
|
if [[ "${base_path}" != *"/" ]]; then
|
||||||
|
base_path="$base_path/"
|
||||||
|
fi
|
||||||
|
printf -v "$1" "%s" "$base_path"
|
||||||
|
}
|
22
portable/test/bats/lib/bats-core/preprocessing.bash
Normal file
22
portable/test/bats/lib/bats-core/preprocessing.bash
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BATS_TMPNAME="$BATS_RUN_TMPDIR/bats.$$"
|
||||||
|
BATS_PARENT_TMPNAME="$BATS_RUN_TMPDIR/bats.$PPID"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_OUT="${BATS_TMPNAME}.out" # used in bats-exec-file
|
||||||
|
|
||||||
|
bats_preprocess_source() {
|
||||||
|
# export to make it visible to bats_evaluate_preprocessed_source
|
||||||
|
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
|
||||||
|
export BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
|
||||||
|
bats-preprocess "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_evaluate_preprocessed_source() {
|
||||||
|
if [[ -z "${BATS_TEST_SOURCE:-}" ]]; then
|
||||||
|
BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src"
|
||||||
|
fi
|
||||||
|
# Dynamically loaded user files provided outside of Bats.
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$BATS_TEST_SOURCE"
|
||||||
|
}
|
107
portable/test/bats/lib/bats-core/semaphore.bash
Normal file
107
portable/test/bats/lib/bats-core/semaphore.bash
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# setup the semaphore environment for the loading file
|
||||||
|
bats_semaphore_setup() {
|
||||||
|
export -f bats_semaphore_get_free_slot_count
|
||||||
|
export -f bats_semaphore_acquire_while_locked
|
||||||
|
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
|
||||||
|
|
||||||
|
if command -v flock >/dev/null; then
|
||||||
|
bats_run_under_lock() {
|
||||||
|
flock "$BATS_SEMAPHORE_DIR" "$@"
|
||||||
|
}
|
||||||
|
elif command -v shlock >/dev/null; then
|
||||||
|
bats_run_under_lock() {
|
||||||
|
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
|
||||||
|
while ! shlock -p $$ -f "$lockfile"; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# we got the lock now, execute the command
|
||||||
|
"$@"
|
||||||
|
local status=$?
|
||||||
|
# free the lock
|
||||||
|
rm -f "$lockfile"
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf "ERROR: flock/shlock is required for parallelization within files!\n" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 - output directory for stdout/stderr
|
||||||
|
# $@ - command to run
|
||||||
|
# run the given command in a semaphore
|
||||||
|
# block when there is no free slot for the semaphore
|
||||||
|
# when there is a free slot, run the command in background
|
||||||
|
# gather the output of the command in files in the given directory
|
||||||
|
bats_semaphore_run() {
|
||||||
|
local output_dir=$1
|
||||||
|
shift
|
||||||
|
local semaphore_slot
|
||||||
|
semaphore_slot=$(bats_semaphore_acquire_slot)
|
||||||
|
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
|
||||||
|
printf "%d\n" "$!"
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 - output directory for stdout/stderr
|
||||||
|
# $@ - command to run
|
||||||
|
# this wraps the actual function call to install some traps on exiting
|
||||||
|
bats_semaphore_release_wrapper() {
|
||||||
|
local output_dir="$1"
|
||||||
|
local semaphore_name="$2"
|
||||||
|
shift 2 # all other parameters will be use for the command to execute
|
||||||
|
|
||||||
|
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
|
||||||
|
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
|
||||||
|
|
||||||
|
mkdir -p "$output_dir"
|
||||||
|
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
|
||||||
|
local status=$?
|
||||||
|
|
||||||
|
# bash bug: the exit trap is not called for the background process
|
||||||
|
bats_semaphore_release_slot "$semaphore_name"
|
||||||
|
trap - EXIT # avoid calling release twice
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_acquire_while_locked() {
|
||||||
|
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||||
|
local slot=0
|
||||||
|
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
|
||||||
|
(( ++slot ))
|
||||||
|
done
|
||||||
|
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
|
||||||
|
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# block until a semaphore slot becomes free
|
||||||
|
# prints the number of the slot that it received
|
||||||
|
bats_semaphore_acquire_slot() {
|
||||||
|
mkdir -p "$BATS_SEMAPHORE_DIR"
|
||||||
|
# wait for a slot to become free
|
||||||
|
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
|
||||||
|
while true; do
|
||||||
|
# don't lock for reading, we are fine with spuriously getting no free slot
|
||||||
|
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
|
||||||
|
bats_run_under_lock bash -c bats_semaphore_acquire_while_locked && break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_release_slot() {
|
||||||
|
# we don't need to lock this, since only our process owns this file
|
||||||
|
# and freeing a semaphore cannot lead to conflicts with others
|
||||||
|
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not acquired a semaphore!
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_semaphore_get_free_slot_count() {
|
||||||
|
# find might error out without returning something useful when a file is deleted,
|
||||||
|
# while the directory is traversed -> only continue when there was no error
|
||||||
|
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
|
||||||
|
echo $(( BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots ))
|
||||||
|
}
|
357
portable/test/bats/lib/bats-core/test_functions.bash
Normal file
357
portable/test/bats/lib/bats-core/test_functions.bash
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
|
||||||
|
BATS_TEST_NAMES=()
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/warnings.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/warnings.bash"
|
||||||
|
|
||||||
|
# find_in_bats_lib_path echoes the first recognized load path to
|
||||||
|
# a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
|
||||||
|
#
|
||||||
|
# Libraries relative to BATS_TEST_DIRNAME take precedence over
|
||||||
|
# BATS_LIB_PATH.
|
||||||
|
#
|
||||||
|
# Library load paths are recognized using find_library_load_path.
|
||||||
|
#
|
||||||
|
# If no library is found find_in_bats_lib_path returns 1.
|
||||||
|
find_in_bats_lib_path() { # <return-var> <library-name>
|
||||||
|
local return_var="${1:?}"
|
||||||
|
local library_name="${2:?}"
|
||||||
|
|
||||||
|
local -a bats_lib_paths
|
||||||
|
IFS=: read -ra bats_lib_paths <<< "$BATS_LIB_PATH"
|
||||||
|
|
||||||
|
for path in "${bats_lib_paths[@]}"; do
|
||||||
|
if [[ -f "$path/$library_name" ]]; then
|
||||||
|
printf -v "$return_var" "%s" "$path/$library_name"
|
||||||
|
# A library load path was found, return
|
||||||
|
return 0
|
||||||
|
elif [[ -f "$path/$library_name/load.bash" ]]; then
|
||||||
|
printf -v "$return_var" "%s" "$path/$library_name/load.bash"
|
||||||
|
# A library load path was found, return
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats_internal_load expects an absolute path that is a library load path.
|
||||||
|
#
|
||||||
|
# If the library load path points to a file (a library loader) it is
|
||||||
|
# sourced.
|
||||||
|
#
|
||||||
|
# If it points to a directory all files ending in .bash inside of the
|
||||||
|
# directory are sourced.
|
||||||
|
#
|
||||||
|
# If the sourcing of the library loader or of a file in a library
|
||||||
|
# directory fails bats_internal_load prints an error message and returns 1.
|
||||||
|
#
|
||||||
|
# If the passed library load path is not absolute or is not a valid file
|
||||||
|
# or directory bats_internal_load prints an error message and returns 1.
|
||||||
|
bats_internal_load() {
|
||||||
|
local library_load_path="${1:?}"
|
||||||
|
|
||||||
|
if [[ "${library_load_path:0:1}" != / ]]; then
|
||||||
|
printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# library_load_path is a library loader
|
||||||
|
if [[ -f "$library_load_path" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
if ! source "$library_load_path"; then
|
||||||
|
printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats_load_safe accepts an argument called 'slug' and attempts to find and
|
||||||
|
# source a library based on the slug.
|
||||||
|
#
|
||||||
|
# A slug can be an absolute path, a library name or a relative path.
|
||||||
|
#
|
||||||
|
# If the slug is an absolute path bats_load_safe attempts to find the library
|
||||||
|
# load path using find_library_load_path.
|
||||||
|
# What is considered a library load path is documented in the
|
||||||
|
# documentation for find_library_load_path.
|
||||||
|
#
|
||||||
|
# If the slug is not an absolute path it is considered a library name or
|
||||||
|
# relative path. bats_load_safe attempts to find the library load path using
|
||||||
|
# find_in_bats_lib_path.
|
||||||
|
#
|
||||||
|
# If bats_load_safe can find a library load path it is passed to bats_internal_load.
|
||||||
|
# If bats_internal_load fails bats_load_safe returns 1.
|
||||||
|
#
|
||||||
|
# If no library load path can be found bats_load_safe prints an error message
|
||||||
|
# and returns 1.
|
||||||
|
bats_load_safe() {
|
||||||
|
local slug="${1:?}"
|
||||||
|
if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
|
||||||
|
slug="$BATS_TEST_DIRNAME/$slug"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$slug.bash" ]]; then
|
||||||
|
bats_internal_load "$slug.bash"
|
||||||
|
return $?
|
||||||
|
elif [[ -f "$slug" ]]; then
|
||||||
|
bats_internal_load "$slug"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# loading from PATH (retained for backwards compatibility)
|
||||||
|
if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$1"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# No library load path can be found
|
||||||
|
printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_require_lib_path() {
|
||||||
|
if [[ -z "${BATS_LIB_PATH:-}" ]]; then
|
||||||
|
printf "%s: requires BATS_LIB_PATH to be set!\n" "${FUNCNAME[1]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_load_library_safe() { # <slug>
|
||||||
|
local slug="${1:?}" library_path
|
||||||
|
|
||||||
|
bats_require_lib_path
|
||||||
|
|
||||||
|
# Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
|
||||||
|
if [[ ${slug:0:1} != / ]]; then
|
||||||
|
find_in_bats_lib_path library_path "$slug"
|
||||||
|
if [[ -z "$library_path" ]]; then
|
||||||
|
printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# absolute paths are taken as is
|
||||||
|
library_path="$slug"
|
||||||
|
if [[ ! -f "$library_path" ]]; then
|
||||||
|
printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_internal_load "$library_path"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# immediately exit on error, use bats_load_library_safe to catch and handle errors
|
||||||
|
bats_load_library() { # <slug>
|
||||||
|
bats_require_lib_path
|
||||||
|
if ! bats_load_library_safe "$@"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# load acts like bats_load_safe but exits the shell instead of returning 1.
|
||||||
|
load() {
|
||||||
|
if ! bats_load_safe "$@"; then
|
||||||
|
echo "${FUNCNAME[0]} $LINENO" >&3
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_redirect_stderr_into_file() {
|
||||||
|
"$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_merge_stdout_and_stderr() {
|
||||||
|
"$@" 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# write separate lines from <input-var> into <output-array>
|
||||||
|
bats_separate_lines() { # <output-array> <input-var>
|
||||||
|
local output_array_name="$1"
|
||||||
|
local input_var_name="$2"
|
||||||
|
if [[ $keep_empty_lines ]]; then
|
||||||
|
local bats_separate_lines_lines=()
|
||||||
|
if [[ -n "${!input_var_name}" ]]; then # avoid getting an empty line for empty input
|
||||||
|
while IFS= read -r line; do
|
||||||
|
bats_separate_lines_lines+=("$line")
|
||||||
|
done <<<"${!input_var_name}"
|
||||||
|
fi
|
||||||
|
eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2034,SC2206
|
||||||
|
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
|
||||||
|
# This has to be restored on exit from this function to avoid leaking our trap INT into surrounding code.
|
||||||
|
# Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
|
||||||
|
# which allows us to avoid duplicating the restore code on every exit path
|
||||||
|
trap bats_interrupt_trap_in_run INT
|
||||||
|
local expected_rc=
|
||||||
|
local keep_empty_lines=
|
||||||
|
local output_case=merged
|
||||||
|
local has_flags=
|
||||||
|
# parse options starting with -
|
||||||
|
while [[ $# -gt 0 ]] && [[ $1 == -* || $1 == '!' ]]; do
|
||||||
|
has_flags=1
|
||||||
|
case "$1" in
|
||||||
|
'!')
|
||||||
|
expected_rc=-1
|
||||||
|
;;
|
||||||
|
-[0-9]*)
|
||||||
|
expected_rc=${1#-}
|
||||||
|
if [[ $expected_rc =~ [^0-9] ]]; then
|
||||||
|
printf "Usage error: run: '-NNN' requires numeric NNN (got: %s)\n" "$expected_rc" >&2
|
||||||
|
return 1
|
||||||
|
elif [[ $expected_rc -gt 255 ]]; then
|
||||||
|
printf "Usage error: run: '-NNN': NNN must be <= 255 (got: %d)\n" "$expected_rc" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--keep-empty-lines)
|
||||||
|
keep_empty_lines=1
|
||||||
|
;;
|
||||||
|
--separate-stderr)
|
||||||
|
output_case="separate"
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift # eat the -- before breaking away
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "Usage error: unknown flag '%s'" "$1" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n $has_flags ]]; then
|
||||||
|
bats_warn_minimum_guaranteed_version "Using flags on \`run\`" 1.5.0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pre_command=
|
||||||
|
|
||||||
|
case "$output_case" in
|
||||||
|
merged) # redirects stderr into stdout and fills only $output/$lines
|
||||||
|
pre_command=bats_merge_stdout_and_stderr
|
||||||
|
;;
|
||||||
|
separate) # splits stderr into own file and fills $stderr/$stderr_lines too
|
||||||
|
local bats_run_separate_stderr_file
|
||||||
|
bats_run_separate_stderr_file="$(mktemp "${BATS_TEST_TMPDIR}/separate-stderr-XXXXXX")"
|
||||||
|
pre_command=bats_redirect_stderr_into_file
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local origFlags="$-"
|
||||||
|
set +eET
|
||||||
|
local origIFS="$IFS"
|
||||||
|
if [[ $keep_empty_lines ]]; then
|
||||||
|
# 'output', 'status', 'lines' are global variables available to tests.
|
||||||
|
# preserve trailing newlines by appending . and removing it later
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
output="$($pre_command "$@"; status=$?; printf .; exit $status)" && status=0 || status=$?
|
||||||
|
output="${output%.}"
|
||||||
|
else
|
||||||
|
# 'output', 'status', 'lines' are global variables available to tests.
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
output="$($pre_command "$@")" && status=0 || status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_separate_lines lines output
|
||||||
|
|
||||||
|
if [[ "$output_case" == separate ]]; then
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
read -d '' -r stderr < "$bats_run_separate_stderr_file"
|
||||||
|
bats_separate_lines stderr_lines stderr
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_RUN_COMMAND="${*}"
|
||||||
|
IFS="$origIFS"
|
||||||
|
set "-$origFlags"
|
||||||
|
|
||||||
|
if [[ ${BATS_VERBOSE_RUN:-} ]]; then
|
||||||
|
printf "%s\n" "$output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [[ -n "$expected_rc" ]]; then
|
||||||
|
if [[ "$expected_rc" = "-1" ]]; then
|
||||||
|
if [[ "$status" -eq 0 ]]; then
|
||||||
|
BATS_ERROR_SUFFIX=", expected nonzero exit code!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
elif [ "$status" -ne "$expected_rc" ]; then
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
elif [[ "$status" -eq 127 ]]; then # "command not found"
|
||||||
|
bats_generate_warning 1 "$BATS_RUN_COMMAND"
|
||||||
|
fi
|
||||||
|
# don't leak our trap into surrounding code
|
||||||
|
trap bats_interrupt_trap INT
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
# if this is a skip in teardown ...
|
||||||
|
if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
|
||||||
|
# ... we want to skip the rest of teardown.
|
||||||
|
# communicate to bats_exit_trap that the teardown was completed without error
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEARDOWN_COMPLETED=1
|
||||||
|
# if we are already in the exit trap (e.g. due to previous skip) ...
|
||||||
|
if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
|
||||||
|
# ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
|
||||||
|
bats_exit_trap
|
||||||
|
# and then do the exit (at the end of this function)
|
||||||
|
fi
|
||||||
|
# if we aren't in exit trap, the normal exit handling should suffice
|
||||||
|
else
|
||||||
|
# ... this is either skip in test or skip in setup.
|
||||||
|
# Following variables are used in bats-exec-test which sources this file
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEST_SKIPPED="${1:-1}"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_TEST_COMPLETED=1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_test_begin() {
|
||||||
|
BATS_TEST_DESCRIPTION="$1"
|
||||||
|
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
|
||||||
|
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}$BATS_TEST_DESCRIPTION" >&3
|
||||||
|
fi
|
||||||
|
setup
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_test_function() {
|
||||||
|
local test_name="$1"
|
||||||
|
BATS_TEST_NAMES+=("$test_name")
|
||||||
|
}
|
||||||
|
|
||||||
|
# decides whether a failed test should be run again
|
||||||
|
bats_should_retry_test() {
|
||||||
|
# test try number starts at 1
|
||||||
|
# 0 retries means run only first try
|
||||||
|
(( BATS_TEST_TRY_NUMBER <= BATS_TEST_RETRIES ))
|
||||||
|
}
|
386
portable/test/bats/lib/bats-core/tracing.bash
Normal file
386
portable/test/bats/lib/bats-core/tracing.bash
Normal file
@ -0,0 +1,386 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/common.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||||
|
|
||||||
|
bats_capture_stack_trace() {
|
||||||
|
local test_file
|
||||||
|
local funcname
|
||||||
|
local i
|
||||||
|
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE=()
|
||||||
|
|
||||||
|
for ((i = 2; i != ${#FUNCNAME[@]}; ++i)); do
|
||||||
|
# Use BATS_TEST_SOURCE if necessary to work around Bash < 4.4 bug whereby
|
||||||
|
# calling an exported function erases the test file's BASH_SOURCE entry.
|
||||||
|
test_file="${BASH_SOURCE[$i]:-$BATS_TEST_SOURCE}"
|
||||||
|
funcname="${FUNCNAME[$i]}"
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE+=("${BASH_LINENO[$((i-1))]} $funcname $test_file")
|
||||||
|
case "$funcname" in
|
||||||
|
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file | setup_suite | teardown_suite)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ "${BASH_SOURCE[$i + 1]:-}" == *"bats-exec-file" ]] && [[ "$funcname" == 'source' ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_get_failure_stack_trace() {
|
||||||
|
local stack_trace_var
|
||||||
|
# See bats_debug_trap for details.
|
||||||
|
if [[ -n "${BATS_DEBUG_LAST_STACK_TRACE_IS_VALID:-}" ]]; then
|
||||||
|
stack_trace_var=BATS_DEBUG_LAST_STACK_TRACE
|
||||||
|
else
|
||||||
|
stack_trace_var=BATS_DEBUG_LASTLAST_STACK_TRACE
|
||||||
|
fi
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
eval "$(printf \
|
||||||
|
'%s=(${%s[@]+"${%s[@]}"})' \
|
||||||
|
"${1}" \
|
||||||
|
"${stack_trace_var}" \
|
||||||
|
"${stack_trace_var}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_print_stack_trace() {
|
||||||
|
local frame
|
||||||
|
local index=1
|
||||||
|
local count="${#@}"
|
||||||
|
local filename
|
||||||
|
local lineno
|
||||||
|
|
||||||
|
for frame in "$@"; do
|
||||||
|
bats_frame_filename "$frame" 'filename'
|
||||||
|
bats_trim_filename "$filename" 'filename'
|
||||||
|
bats_frame_lineno "$frame" 'lineno'
|
||||||
|
|
||||||
|
printf '%s' "${BATS_STACK_TRACE_PREFIX-# }"
|
||||||
|
if [[ $index -eq 1 ]]; then
|
||||||
|
printf '('
|
||||||
|
else
|
||||||
|
printf ' '
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fn
|
||||||
|
bats_frame_function "$frame" 'fn'
|
||||||
|
if [[ "$fn" != "$BATS_TEST_NAME" ]] &&
|
||||||
|
# don't print "from function `source'"",
|
||||||
|
# when failing in free code during `source $test_file` from bats-exec-file
|
||||||
|
! [[ "$fn" == 'source' && $index -eq $count ]]; then
|
||||||
|
local quoted_fn
|
||||||
|
bats_quote_code quoted_fn "$fn"
|
||||||
|
printf "from function %s " "$quoted_fn"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $index -eq $count ]]; then
|
||||||
|
printf 'in test file %s, line %d)\n' "$filename" "$lineno"
|
||||||
|
else
|
||||||
|
printf 'in file %s, line %d,\n' "$filename" "$lineno"
|
||||||
|
fi
|
||||||
|
|
||||||
|
((++index))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_print_failed_command() {
|
||||||
|
local stack_trace=("${@}")
|
||||||
|
if [[ ${#stack_trace[@]} -eq 0 ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local frame="${stack_trace[${#stack_trace[@]} - 1]}"
|
||||||
|
local filename
|
||||||
|
local lineno
|
||||||
|
local failed_line
|
||||||
|
local failed_command
|
||||||
|
|
||||||
|
bats_frame_filename "$frame" 'filename'
|
||||||
|
bats_frame_lineno "$frame" 'lineno'
|
||||||
|
bats_extract_line "$filename" "$lineno" 'failed_line'
|
||||||
|
bats_strip_string "$failed_line" 'failed_command'
|
||||||
|
local quoted_failed_command
|
||||||
|
bats_quote_code quoted_failed_command "$failed_command"
|
||||||
|
printf '# %s ' "${quoted_failed_command}"
|
||||||
|
|
||||||
|
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
|
||||||
|
printf 'failed%s\n' "$BATS_ERROR_SUFFIX"
|
||||||
|
else
|
||||||
|
printf 'failed with status %d%s\n' "$BATS_ERROR_STATUS" "$BATS_ERROR_SUFFIX"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_lineno() {
|
||||||
|
printf -v "$2" '%s' "${1%% *}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_function() {
|
||||||
|
local __bff_function="${1#* }"
|
||||||
|
printf -v "$2" '%s' "${__bff_function%% *}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_frame_filename() {
|
||||||
|
local __bff_filename="${1#* }"
|
||||||
|
__bff_filename="${__bff_filename#* }"
|
||||||
|
|
||||||
|
if [[ "$__bff_filename" == "$BATS_TEST_SOURCE" ]]; then
|
||||||
|
__bff_filename="$BATS_TEST_FILENAME"
|
||||||
|
fi
|
||||||
|
printf -v "$2" '%s' "$__bff_filename"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_extract_line() {
|
||||||
|
local __bats_extract_line_line
|
||||||
|
local __bats_extract_line_index=0
|
||||||
|
|
||||||
|
while IFS= read -r __bats_extract_line_line; do
|
||||||
|
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
|
||||||
|
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done <"$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_strip_string() {
|
||||||
|
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
|
||||||
|
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_trim_filename() {
|
||||||
|
printf -v "$2" '%s' "${1#"$BATS_CWD"/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# normalize a windows path from e.g. C:/directory to /c/directory
|
||||||
|
# The path must point to an existing/accessable directory, not a file!
|
||||||
|
bats_normalize_windows_dir_path() { # <output-var> <path>
|
||||||
|
local output_var="$1" path="$2"
|
||||||
|
if [[ "$output_var" != NORMALIZED_INPUT ]]; then
|
||||||
|
local NORMALIZED_INPUT
|
||||||
|
fi
|
||||||
|
if [[ $path == ?:* ]]; then
|
||||||
|
NORMALIZED_INPUT="$(cd "$path" || exit 1; pwd)"
|
||||||
|
else
|
||||||
|
NORMALIZED_INPUT="$path"
|
||||||
|
fi
|
||||||
|
printf -v "$output_var" "%s" "$NORMALIZED_INPUT"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_emit_trace() {
|
||||||
|
if [[ $BATS_TRACE_LEVEL -gt 0 ]]; then
|
||||||
|
local line=${BASH_LINENO[1]}
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
if [[ $BASH_COMMAND != '"$BATS_TEST_NAME" >> "$BATS_OUT" 2>&1 4>&1' && $BASH_COMMAND != "bats_test_begin "* ]] && # don't emit these internal calls
|
||||||
|
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || $line != "$BATS_LAST_BASH_LINENO" ]] &&
|
||||||
|
# avoid printing a function twice (at call site and at definition site)
|
||||||
|
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || ${BASH_LINENO[2]} != "$BATS_LAST_BASH_LINENO" || ${BASH_SOURCE[3]} != "$BATS_LAST_BASH_SOURCE" ]]; then
|
||||||
|
local file="${BASH_SOURCE[2]}" # index 2: skip over bats_emit_trace and bats_debug_trap
|
||||||
|
if [[ $file == "${BATS_TEST_SOURCE}" ]]; then
|
||||||
|
file="$BATS_TEST_FILENAME"
|
||||||
|
fi
|
||||||
|
local padding='$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$'
|
||||||
|
if (( BATS_LAST_STACK_DEPTH != ${#BASH_LINENO[@]} )); then
|
||||||
|
printf '%s [%s:%d]\n' "${padding::${#BASH_LINENO[@]}-4}" "${file##*/}" "$line" >&4
|
||||||
|
fi
|
||||||
|
printf '%s %s\n' "${padding::${#BASH_LINENO[@]}-4}" "$BASH_COMMAND" >&4
|
||||||
|
BATS_LAST_BASH_COMMAND="$BASH_COMMAND"
|
||||||
|
BATS_LAST_BASH_LINENO="$line"
|
||||||
|
BATS_LAST_BASH_SOURCE="${BASH_SOURCE[2]}"
|
||||||
|
BATS_LAST_STACK_DEPTH="${#BASH_LINENO[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# bats_debug_trap tracks the last line of code executed within a test. This is
|
||||||
|
# necessary because $BASH_LINENO is often incorrect inside of ERR and EXIT
|
||||||
|
# trap handlers.
|
||||||
|
#
|
||||||
|
# Below are tables describing different command failure scenarios and the
|
||||||
|
# reliability of $BASH_LINENO within different the executed DEBUG, ERR, and EXIT
|
||||||
|
# trap handlers. Naturally, the behaviors change between versions of Bash.
|
||||||
|
#
|
||||||
|
# Table rows should be read left to right. For example, on bash version
|
||||||
|
# 4.0.44(2)-release, if a test executes `false` (or any other failing external
|
||||||
|
# command), bash will do the following in order:
|
||||||
|
# 1. Call the DEBUG trap handler (bats_debug_trap) with $BASH_LINENO referring
|
||||||
|
# to the source line containing the `false` command, then
|
||||||
|
# 2. Call the DEBUG trap handler again, but with an incorrect $BASH_LINENO, then
|
||||||
|
# 3. Call the ERR trap handler, but with a (possibly-different) incorrect
|
||||||
|
# $BASH_LINENO, then
|
||||||
|
# 4. Call the DEBUG trap handler again, but with $BASH_LINENO set to 1, then
|
||||||
|
# 5. Call the EXIT trap handler, with $BASH_LINENO set to 1.
|
||||||
|
#
|
||||||
|
# bash version 4.4.20(1)-release
|
||||||
|
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
|
||||||
|
# -------------+-------------+--------------+---------+-------------+--------
|
||||||
|
# false | OK | OK | OK | BAD[1] | BAD[1]
|
||||||
|
# [[ 1 = 2 ]] | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
|
||||||
|
# (( 1 = 2 )) | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
|
||||||
|
# ! true | OK | --- | BAD[4] | --- | BAD[1]
|
||||||
|
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||||
|
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||||
|
#
|
||||||
|
# bash version 4.0.44(2)-release
|
||||||
|
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
|
||||||
|
# -------------+-------------+--------------+---------+-------------+--------
|
||||||
|
# false | OK | BAD[3] | BAD[3] | BAD[1] | BAD[1]
|
||||||
|
# [[ 1 = 2 ]] | OK | --- | BAD[3] | --- | BAD[1]
|
||||||
|
# (( 1 = 2 )) | OK | --- | BAD[3] | --- | BAD[1]
|
||||||
|
# ! true | OK | --- | BAD[3] | --- | BAD[1]
|
||||||
|
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||||
|
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
|
||||||
|
#
|
||||||
|
# [1] The reported line number is always 1.
|
||||||
|
# [2] The reported source location is that of the beginning of the function
|
||||||
|
# calling the command.
|
||||||
|
# [3] The reported line is that of the last command executed in the DEBUG trap
|
||||||
|
# handler.
|
||||||
|
# [4] The reported source location is that of the call to the function calling
|
||||||
|
# the command.
|
||||||
|
bats_debug_trap() {
|
||||||
|
# on windows we sometimes get a mix of paths (when install via nmp install -g)
|
||||||
|
# which have C:/... or /c/... comparing them is going to be problematic.
|
||||||
|
# We need to normalize them to a common format!
|
||||||
|
local NORMALIZED_INPUT
|
||||||
|
bats_normalize_windows_dir_path NORMALIZED_INPUT "${1%/*}"
|
||||||
|
local file_excluded='' path
|
||||||
|
for path in "${BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
|
||||||
|
if [[ "$NORMALIZED_INPUT" == "$path"* ]]; then
|
||||||
|
file_excluded=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# don't update the trace within library functions or we get backtraces from inside traps
|
||||||
|
# also don't record new stack traces while handling interruptions, to avoid overriding the interrupted command
|
||||||
|
if [[ -z "$file_excluded" && "${BATS_INTERRUPTED-NOTSET}" == NOTSET ]]; then
|
||||||
|
BATS_DEBUG_LASTLAST_STACK_TRACE=(
|
||||||
|
${BATS_DEBUG_LAST_STACK_TRACE[@]+"${BATS_DEBUG_LAST_STACK_TRACE[@]}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
BATS_DEBUG_LAST_LINENO=(${BASH_LINENO[@]+"${BASH_LINENO[@]}"})
|
||||||
|
BATS_DEBUG_LAST_SOURCE=(${BASH_SOURCE[@]+"${BASH_SOURCE[@]}"})
|
||||||
|
bats_capture_stack_trace
|
||||||
|
bats_emit_trace
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# For some versions of Bash, the `ERR` trap may not always fire for every
|
||||||
|
# command failure, but the `EXIT` trap will. Also, some command failures may not
|
||||||
|
# set `$?` properly. See #72 and #81 for details.
|
||||||
|
#
|
||||||
|
# For this reason, we call `bats_check_status_from_trap` at the very beginning
|
||||||
|
# of `bats_teardown_trap` and check the value of `$BATS_TEST_COMPLETED` before
|
||||||
|
# taking other actions. We also adjust the exit status value if needed.
|
||||||
|
#
|
||||||
|
# See `bats_exit_trap` for an additional EXIT error handling case when `$?`
|
||||||
|
# isn't set properly during `teardown()` errors.
|
||||||
|
bats_check_status_from_trap() {
|
||||||
|
local status="$?"
|
||||||
|
if [[ -z "${BATS_TEST_COMPLETED:-}" ]]; then
|
||||||
|
BATS_ERROR_STATUS="${BATS_ERROR_STATUS:-$status}"
|
||||||
|
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
||||||
|
BATS_ERROR_STATUS=1
|
||||||
|
fi
|
||||||
|
trap - DEBUG
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_add_debug_exclude_path() { # <path>
|
||||||
|
if [[ -z "$1" ]]; then # don't exclude everything
|
||||||
|
printf "bats_add_debug_exclude_path: Exclude path must not be empty!\n" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ "$OSTYPE" == cygwin || "$OSTYPE" == msys ]]; then
|
||||||
|
local normalized_dir
|
||||||
|
bats_normalize_windows_dir_path normalized_dir "$1"
|
||||||
|
BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
|
||||||
|
else
|
||||||
|
BATS_DEBUG_EXCLUDE_PATHS+=("$1")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_setup_tracing() {
|
||||||
|
# Variables for capturing accurate stack traces. See bats_debug_trap for
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# BATS_DEBUG_LAST_LINENO, BATS_DEBUG_LAST_SOURCE, and
|
||||||
|
# BATS_DEBUG_LAST_STACK_TRACE hold data from the most recent call to
|
||||||
|
# bats_debug_trap.
|
||||||
|
#
|
||||||
|
# BATS_DEBUG_LASTLAST_STACK_TRACE holds data from two bats_debug_trap calls
|
||||||
|
# ago.
|
||||||
|
#
|
||||||
|
# BATS_DEBUG_LAST_STACK_TRACE_IS_VALID indicates that
|
||||||
|
# BATS_DEBUG_LAST_STACK_TRACE contains the stack trace of the test's error. If
|
||||||
|
# unset, BATS_DEBUG_LAST_STACK_TRACE is unreliable and
|
||||||
|
# BATS_DEBUG_LASTLAST_STACK_TRACE should be used instead.
|
||||||
|
BATS_DEBUG_LASTLAST_STACK_TRACE=()
|
||||||
|
BATS_DEBUG_LAST_LINENO=()
|
||||||
|
BATS_DEBUG_LAST_SOURCE=()
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE=()
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
|
||||||
|
BATS_ERROR_SUFFIX=
|
||||||
|
BATS_DEBUG_EXCLUDE_PATHS=()
|
||||||
|
# exclude some paths by default
|
||||||
|
bats_add_debug_exclude_path "$BATS_ROOT/lib/"
|
||||||
|
bats_add_debug_exclude_path "$BATS_ROOT/libexec/"
|
||||||
|
|
||||||
|
|
||||||
|
exec 4<&1 # used for tracing
|
||||||
|
if [[ "${BATS_TRACE_LEVEL:-0}" -gt 0 ]]; then
|
||||||
|
# avoid undefined variable errors
|
||||||
|
BATS_LAST_BASH_COMMAND=
|
||||||
|
BATS_LAST_BASH_LINENO=
|
||||||
|
BATS_LAST_BASH_SOURCE=
|
||||||
|
BATS_LAST_STACK_DEPTH=
|
||||||
|
# try to exclude helper libraries if found, this is only relevant for tracing
|
||||||
|
while read -r path; do
|
||||||
|
bats_add_debug_exclude_path "$path"
|
||||||
|
done < <(find "$PWD" -type d -name bats-assert -o -name bats-support)
|
||||||
|
fi
|
||||||
|
|
||||||
|
local exclude_paths path
|
||||||
|
# exclude user defined libraries
|
||||||
|
IFS=':' read -r exclude_paths <<< "${BATS_DEBUG_EXCLUDE_PATHS:-}"
|
||||||
|
for path in "${exclude_paths[@]}"; do
|
||||||
|
if [[ -n "$path" ]]; then
|
||||||
|
bats_add_debug_exclude_path "$path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# turn on traps after setting excludes to avoid tracing the exclude setup
|
||||||
|
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
|
||||||
|
trap 'bats_error_trap' ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_error_trap() {
|
||||||
|
bats_check_status_from_trap
|
||||||
|
|
||||||
|
# If necessary, undo the most recent stack trace captured by bats_debug_trap.
|
||||||
|
# See bats_debug_trap for details.
|
||||||
|
if [[ "${BASH_LINENO[*]}" = "${BATS_DEBUG_LAST_LINENO[*]:-}"
|
||||||
|
&& "${BASH_SOURCE[*]}" = "${BATS_DEBUG_LAST_SOURCE[*]:-}"
|
||||||
|
&& -z "$BATS_DEBUG_LAST_STACK_TRACE_IS_VALID" ]]; then
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE=(
|
||||||
|
${BATS_DEBUG_LASTLAST_STACK_TRACE[@]+"${BATS_DEBUG_LASTLAST_STACK_TRACE[@]}"}
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_interrupt_trap() {
|
||||||
|
# mark the interruption, to handle during exit
|
||||||
|
BATS_INTERRUPTED=true
|
||||||
|
BATS_ERROR_STATUS=130
|
||||||
|
# debug trap fires before interrupt trap but gets wrong linenumber (line 1)
|
||||||
|
# -> use last stack trace
|
||||||
|
exit $BATS_ERROR_STATUS
|
||||||
|
}
|
||||||
|
|
||||||
|
# this is used inside run()
|
||||||
|
bats_interrupt_trap_in_run() {
|
||||||
|
# mark the interruption, to handle during exit
|
||||||
|
BATS_INTERRUPTED=true
|
||||||
|
BATS_ERROR_STATUS=130
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=true
|
||||||
|
exit $BATS_ERROR_STATUS
|
||||||
|
}
|
37
portable/test/bats/lib/bats-core/validator.bash
Normal file
37
portable/test/bats/lib/bats-core/validator.bash
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
bats_test_count_validator() {
|
||||||
|
trap '' INT # continue forwarding
|
||||||
|
header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
# repeat the header
|
||||||
|
printf "%s\n" "$header"
|
||||||
|
|
||||||
|
# if we detect a TAP plan
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
# extract the number of tests ...
|
||||||
|
local expected_number_of_tests="${header:3}"
|
||||||
|
# ... count the actual number of [not ] oks...
|
||||||
|
local actual_number_of_tests=0
|
||||||
|
while IFS= read -r line; do
|
||||||
|
# forward line
|
||||||
|
printf "%s\n" "$line"
|
||||||
|
case "$line" in
|
||||||
|
'ok '*)
|
||||||
|
(( ++actual_number_of_tests ))
|
||||||
|
;;
|
||||||
|
'not ok'*)
|
||||||
|
(( ++actual_number_of_tests ))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# ... and error if they are not the same
|
||||||
|
if [[ "${actual_number_of_tests}" != "${expected_number_of_tests}" ]]; then
|
||||||
|
printf '# bats warning: Executed %s instead of expected %s tests\n' "$actual_number_of_tests" "$expected_number_of_tests"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# forward output unchanged
|
||||||
|
cat
|
||||||
|
fi
|
||||||
|
}
|
35
portable/test/bats/lib/bats-core/warnings.bash
Normal file
35
portable/test/bats/lib/bats-core/warnings.bash
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/tracing.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||||
|
|
||||||
|
BATS_WARNING_SHORT_DESCS=(
|
||||||
|
# to start with 1
|
||||||
|
'PADDING'
|
||||||
|
# see issue #578 for context
|
||||||
|
"\`run\`'s command \`%s\` exited with code 127, indicating 'Command not found'. Use run's return code checks, e.g. \`run -127\`, to fix this message."
|
||||||
|
"%s requires at least BATS_VERSION=%s. Use \`bats_require_minimum_version %s\` to fix this message."
|
||||||
|
)
|
||||||
|
|
||||||
|
# generate a warning report for the parent call's call site
|
||||||
|
bats_generate_warning() { # <warning number> [<printf args for warning string>...]
|
||||||
|
local warning_number="$1" padding="00"
|
||||||
|
shift
|
||||||
|
if [[ $warning_number =~ [0-9]+ ]] && ((warning_number < ${#BATS_WARNING_SHORT_DESCS[@]} )); then
|
||||||
|
{
|
||||||
|
printf "BW%s: ${BATS_WARNING_SHORT_DESCS[$warning_number]}\n" "${padding:${#warning_number}}${warning_number}" "$@"
|
||||||
|
bats_capture_stack_trace
|
||||||
|
BATS_STACK_TRACE_PREFIX=' ' bats_print_stack_trace "${BATS_DEBUG_LAST_STACK_TRACE[@]}"
|
||||||
|
} >> "$BATS_WARNING_FILE" 2>&3
|
||||||
|
else
|
||||||
|
printf "Invalid Bats warning number '%s'. It must be an integer between 1 and %d." "$warning_number" "$((${#BATS_WARNING_SHORT_DESCS[@]} - 1))" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate a warning if the BATS_GUARANTEED_MINIMUM_VERSION is not high enough
|
||||||
|
bats_warn_minimum_guaranteed_version() { # <feature> <minimum required version>
|
||||||
|
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$2"; then
|
||||||
|
bats_generate_warning 2 "$1" "$2" "$2"
|
||||||
|
fi
|
||||||
|
}
|
467
portable/test/bats/libexec/bats-core/bats
Executable file
467
portable/test/bats/libexec/bats-core/bats
Executable file
@ -0,0 +1,467 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
export BATS_VERSION='1.7.0'
|
||||||
|
VALID_FORMATTERS="pretty, junit, tap, tap13"
|
||||||
|
|
||||||
|
version() {
|
||||||
|
printf 'Bats %s\n' "$BATS_VERSION"
|
||||||
|
}
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
local print_usage=1
|
||||||
|
if [[ ${1:-} == --no-print-usage ]]; then
|
||||||
|
print_usage=
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
printf 'Error: %s\n' "$1" >&2
|
||||||
|
if [[ -n $print_usage ]]; then
|
||||||
|
usage >&2
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
local cmd="${0##*/}"
|
||||||
|
local line
|
||||||
|
|
||||||
|
cat <<HELP_TEXT_HEADER
|
||||||
|
Usage: ${cmd} [OPTIONS] <tests>
|
||||||
|
${cmd} [-h | -v]
|
||||||
|
|
||||||
|
HELP_TEXT_HEADER
|
||||||
|
|
||||||
|
cat <<'HELP_TEXT_BODY'
|
||||||
|
<tests> is the path to a Bats test file, or the path to a directory
|
||||||
|
containing Bats test files (ending with ".bats")
|
||||||
|
|
||||||
|
-c, --count Count test cases without running any tests
|
||||||
|
--code-quote-style <style>
|
||||||
|
A two character string of code quote delimiters
|
||||||
|
or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and
|
||||||
|
$BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE
|
||||||
|
-f, --filter <regex> Only run tests that match the regular expression
|
||||||
|
--filter-status <status> Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run.
|
||||||
|
Valid <status> values are:
|
||||||
|
failed - runs tests that failed or were not present in the last run
|
||||||
|
missed - runs tests that were not present in the last run
|
||||||
|
-F, --formatter <type> Switch between formatters: pretty (default),
|
||||||
|
tap (default w/o term), tap13, junit, /<absolute path to formatter>
|
||||||
|
--gather-test-outputs-in <directory>
|
||||||
|
Gather the output of failing *and* passing tests
|
||||||
|
as files in directory (if existing, must be empty)
|
||||||
|
-h, --help Display this help message
|
||||||
|
-j, --jobs <jobs> Number of parallel jobs (requires GNU parallel)
|
||||||
|
--no-tempdir-cleanup Preserve test output temporary directory
|
||||||
|
--no-parallelize-across-files
|
||||||
|
Serialize test file execution instead of running
|
||||||
|
them in parallel (requires --jobs >1)
|
||||||
|
--no-parallelize-within-files
|
||||||
|
Serialize test execution within files instead of
|
||||||
|
running them in parallel (requires --jobs >1)
|
||||||
|
--report-formatter <type> Switch between reporters (same options as --formatter)
|
||||||
|
-o, --output <dir> Directory to write report files (must exist)
|
||||||
|
-p, --pretty Shorthand for "--formatter pretty"
|
||||||
|
--print-output-on-failure Automatically print the value of `$output` on failed tests
|
||||||
|
-r, --recursive Include tests in subdirectories
|
||||||
|
--show-output-of-passing-tests
|
||||||
|
Print output of passing tests
|
||||||
|
-t, --tap Shorthand for "--formatter tap"
|
||||||
|
-T, --timing Add timing information to tests
|
||||||
|
-x, --trace Print test commands as they are executed (like `set -x`)
|
||||||
|
--verbose-run Make `run` print `$output` by default
|
||||||
|
-v, --version Display the version number
|
||||||
|
|
||||||
|
For more information, see https://github.com/bats-core/bats-core
|
||||||
|
HELP_TEXT_BODY
|
||||||
|
}
|
||||||
|
|
||||||
|
expand_path() {
|
||||||
|
local path="${1%/}"
|
||||||
|
local dirname="${path%/*}"
|
||||||
|
local result="$2"
|
||||||
|
|
||||||
|
if [[ "$dirname" == "$path" ]]; then
|
||||||
|
dirname="$PWD"
|
||||||
|
else
|
||||||
|
cd "$dirname"
|
||||||
|
dirname="$PWD"
|
||||||
|
cd "$OLDPWD"
|
||||||
|
fi
|
||||||
|
printf -v "$result" '%s/%s' "$dirname" "${path##*/}"
|
||||||
|
}
|
||||||
|
|
||||||
|
BATS_LIBEXEC="$(cd "$(dirname "$(bats_readlinkf "${BASH_SOURCE[0]}")")"; pwd)"
|
||||||
|
export BATS_LIBEXEC
|
||||||
|
export BATS_CWD="$PWD"
|
||||||
|
export BATS_TEST_FILTER=
|
||||||
|
export PATH="$BATS_LIBEXEC:$PATH"
|
||||||
|
export BATS_ROOT_PID=$$
|
||||||
|
export BATS_TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
BATS_TMPDIR=${BATS_TMPDIR%/} # chop off trailing / to avoid duplication
|
||||||
|
export BATS_RUN_TMPDIR=
|
||||||
|
export BATS_GUARANTEED_MINIMUM_VERSION=0.0.0
|
||||||
|
|
||||||
|
if [[ ! -d "${BATS_TMPDIR}" ]];then
|
||||||
|
printf "Error: BATS_TMPDIR (%s) does not exist or is not a directory" "${BATS_TMPDIR}" >&2
|
||||||
|
exit 1
|
||||||
|
elif [[ ! -w "${BATS_TMPDIR}" ]];then
|
||||||
|
printf "Error: BATS_TMPDIR (%s) is not writable" "${BATS_TMPDIR}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
arguments=()
|
||||||
|
|
||||||
|
# Unpack single-character options bundled together, e.g. -cr, -pr.
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ "$arg" =~ ^-[^-]. ]]; then
|
||||||
|
index=1
|
||||||
|
while option="${arg:$((index++)):1}"; do
|
||||||
|
if [[ -z "$option" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
arguments+=("-$option")
|
||||||
|
done
|
||||||
|
else
|
||||||
|
arguments+=("$arg")
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
set -- "${arguments[@]}"
|
||||||
|
arguments=()
|
||||||
|
|
||||||
|
unset flags recursive formatter_flags
|
||||||
|
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||||
|
formatter_flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||||
|
formatter='tap'
|
||||||
|
report_formatter=''
|
||||||
|
recursive=
|
||||||
|
setup_suite_file=''
|
||||||
|
export BATS_TEMPDIR_CLEANUP=1
|
||||||
|
output=
|
||||||
|
if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then
|
||||||
|
formatter='pretty'
|
||||||
|
fi
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h | --help)
|
||||||
|
version
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-v | --version)
|
||||||
|
version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-c | --count)
|
||||||
|
flags+=('-c')
|
||||||
|
;;
|
||||||
|
-f | --filter)
|
||||||
|
shift
|
||||||
|
flags+=('-f' "$1")
|
||||||
|
;;
|
||||||
|
-F | --formatter)
|
||||||
|
shift
|
||||||
|
# allow cat formatter to see extended output but don't advertise to users
|
||||||
|
if [[ $1 =~ ^(pretty|junit|tap|tap13|cat|/.*)$ ]]; then
|
||||||
|
formatter="$1"
|
||||||
|
else
|
||||||
|
printf "Unknown formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--report-formatter)
|
||||||
|
shift
|
||||||
|
if [[ $1 =~ ^(pretty|junit|tap|tap13)$ ]]; then
|
||||||
|
report_formatter="$1"
|
||||||
|
else
|
||||||
|
printf "Unknown report formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-o | --output)
|
||||||
|
shift
|
||||||
|
output="$1"
|
||||||
|
;;
|
||||||
|
-p | --pretty)
|
||||||
|
formatter='pretty'
|
||||||
|
;;
|
||||||
|
-j | --jobs)
|
||||||
|
shift
|
||||||
|
flags+=('-j' "$1")
|
||||||
|
;;
|
||||||
|
-r | --recursive)
|
||||||
|
recursive=1
|
||||||
|
;;
|
||||||
|
-t | --tap)
|
||||||
|
formatter='tap'
|
||||||
|
;;
|
||||||
|
-T | --timing)
|
||||||
|
flags+=('-T')
|
||||||
|
formatter_flags+=('-T')
|
||||||
|
;;
|
||||||
|
# this flag is now a no-op, as it is the parallel default
|
||||||
|
--parallel-preserve-environment)
|
||||||
|
;;
|
||||||
|
--no-parallelize-across-files)
|
||||||
|
flags+=("--no-parallelize-across-files")
|
||||||
|
;;
|
||||||
|
--no-parallelize-within-files)
|
||||||
|
flags+=("--no-parallelize-within-files")
|
||||||
|
;;
|
||||||
|
--no-tempdir-cleanup)
|
||||||
|
BATS_TEMPDIR_CLEANUP=''
|
||||||
|
;;
|
||||||
|
--tempdir) # for internal test consumption only!
|
||||||
|
BATS_RUN_TMPDIR="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-x | --trace)
|
||||||
|
flags+=(--trace)
|
||||||
|
;;
|
||||||
|
--print-output-on-failure)
|
||||||
|
flags+=(--print-output-on-failure)
|
||||||
|
;;
|
||||||
|
--show-output-of-passing-tests)
|
||||||
|
flags+=(--show-output-of-passing-tests)
|
||||||
|
;;
|
||||||
|
--verbose-run)
|
||||||
|
flags+=(--verbose-run)
|
||||||
|
;;
|
||||||
|
--gather-test-outputs-in)
|
||||||
|
shift
|
||||||
|
output_dir="$1"
|
||||||
|
if [ -d "$output_dir" ]; then
|
||||||
|
if ! find "$output_dir" -mindepth 1 -exec false {} + 2>/dev/null; then
|
||||||
|
abort --no-print-usage "Directory '$output_dir' must be empty for --gather-test-outputs-in"
|
||||||
|
fi
|
||||||
|
elif ! mkdir "$output_dir" 2>/dev/null; then
|
||||||
|
abort --no-print-usage "Could not create '$output_dir' for --gather-test-outputs-in"
|
||||||
|
fi
|
||||||
|
flags+=(--gather-test-outputs-in "$output_dir")
|
||||||
|
;;
|
||||||
|
--setup-suite-file)
|
||||||
|
shift
|
||||||
|
setup_suite_file="$1"
|
||||||
|
;;
|
||||||
|
--code-quote-style)
|
||||||
|
shift
|
||||||
|
BATS_CODE_QUOTE_STYLE="$1"
|
||||||
|
;;
|
||||||
|
--filter-status)
|
||||||
|
shift
|
||||||
|
flags+=('--filter-status' "$1")
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
abort "Bad command line option '$1'"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
arguments+=("$1")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "${BATS_RUN_TMPDIR:-}" ]];then
|
||||||
|
if [[ -d "$BATS_RUN_TMPDIR" ]]; then
|
||||||
|
printf "Error: BATS_RUN_TMPDIR (%s) already exists\n" "$BATS_RUN_TMPDIR" >&2
|
||||||
|
printf "Reusing old run directories can lead to unexpected results ... aborting!\n" >&2
|
||||||
|
exit 1
|
||||||
|
elif ! mkdir -p "$BATS_RUN_TMPDIR" ;then
|
||||||
|
printf "Error: Failed to create BATS_RUN_TMPDIR (%s)\n" "$BATS_RUN_TMPDIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif ! BATS_RUN_TMPDIR=$(mktemp -d "${BATS_TMPDIR}/bats-run-XXXXXX");then
|
||||||
|
printf "Error: Failed to create BATS_RUN_TMPDIR (%s) with mktemp\n" "${BATS_TMPDIR}/bats-run-XXXXXX" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BATS_WARNING_FILE="${BATS_RUN_TMPDIR}/warnings.log"
|
||||||
|
|
||||||
|
bats_exit_trap() {
|
||||||
|
if [[ -s "$BATS_WARNING_FILE" ]]; then
|
||||||
|
local pre_cat='' post_cat=''
|
||||||
|
if [[ $formatter == pretty ]]; then
|
||||||
|
pre_cat=$'\x1B[31m'
|
||||||
|
post_cat=$'\x1B[0m'
|
||||||
|
fi
|
||||||
|
printf "\nThe following warnings were encountered during tests:\n%s" "$pre_cat"
|
||||||
|
cat "$BATS_WARNING_FILE"
|
||||||
|
printf "%s" "$post_cat"
|
||||||
|
fi >&2
|
||||||
|
|
||||||
|
if [[ -n "$BATS_TEMPDIR_CLEANUP" ]]; then
|
||||||
|
rm -rf "$BATS_RUN_TMPDIR"
|
||||||
|
else
|
||||||
|
printf "BATS_RUN_TMPDIR: %s\n" "$BATS_RUN_TMPDIR" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap bats_exit_trap EXIT
|
||||||
|
|
||||||
|
if [[ "$formatter" != "tap" ]]; then
|
||||||
|
flags+=('-x')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$report_formatter" && "$report_formatter" != "tap" ]]; then
|
||||||
|
flags+=('-x')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$formatter" == "junit" ]]; then
|
||||||
|
flags+=('-T')
|
||||||
|
formatter_flags+=('--base-path' "${arguments[0]}")
|
||||||
|
fi
|
||||||
|
if [[ "$report_formatter" == "junit" ]]; then
|
||||||
|
flags+=('-T')
|
||||||
|
report_formatter_flags+=('--base-path' "${arguments[0]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$formatter" == "pretty" ]]; then
|
||||||
|
formatter_flags+=('--base-path' "${arguments[0]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if we don't need to filter extended syntax, use the faster formatter
|
||||||
|
if [[ "$formatter" == tap && -z "$report_formatter" ]]; then
|
||||||
|
formatter="cat"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_check_formatter() { # <formatter-path>
|
||||||
|
local -r formatter="$1"
|
||||||
|
if [[ ! -f "$formatter" ]]; then
|
||||||
|
printf "ERROR: Formatter '%s' is not readable!\n" "$formatter"
|
||||||
|
exit 1
|
||||||
|
elif [[ ! -x "$formatter" ]]; then
|
||||||
|
printf "ERROR: Formatter '%s' is not executable!\n" "$formatter"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $formatter == /* ]]; then # absolute paths are direct references to formatters
|
||||||
|
bats_check_formatter "$formatter"
|
||||||
|
interpolated_formatter="$formatter"
|
||||||
|
else
|
||||||
|
interpolated_formatter="bats-format-${formatter}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${#arguments[@]}" -eq 0 ]]; then
|
||||||
|
abort 'Must specify at least one <test>'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$report_formatter" ]]; then
|
||||||
|
# default to the current directory for output
|
||||||
|
if [[ -z "$output" ]]; then
|
||||||
|
output=.
|
||||||
|
fi
|
||||||
|
# only set BATS_REPORT_FILENAME if none was given
|
||||||
|
if [[ -z "${BATS_REPORT_FILENAME:-}" ]]; then
|
||||||
|
case "$report_formatter" in
|
||||||
|
tap|tap13)
|
||||||
|
BATS_REPORT_FILE_NAME="report.tap"
|
||||||
|
;;
|
||||||
|
junit)
|
||||||
|
BATS_REPORT_FILE_NAME="report.xml"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
BATS_REPORT_FILE_NAME="report.log"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $report_formatter == /* ]]; then # absolute paths are direct references to formatters
|
||||||
|
bats_check_formatter "$report_formatter"
|
||||||
|
interpolated_report_formatter="${report_formatter}"
|
||||||
|
else
|
||||||
|
interpolated_report_formatter="bats-format-${report_formatter}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${BATS_CODE_QUOTE_STYLE-BATS_CODE_QUOTE_STYLE_UNSET}" == BATS_CODE_QUOTE_STYLE_UNSET ]]; then
|
||||||
|
BATS_CODE_QUOTE_STYLE="\`'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${BATS_CODE_QUOTE_STYLE}" in
|
||||||
|
??)
|
||||||
|
BATS_BEGIN_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE::1}"
|
||||||
|
BATS_END_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE:1:1}"
|
||||||
|
export BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
|
||||||
|
;;
|
||||||
|
custom)
|
||||||
|
if [[ ${BATS_BEGIN_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET
|
||||||
|
|| ${BATS_END_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET ]]; then
|
||||||
|
printf "ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set\n" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "ERROR: Unknown BATS_CODE_QUOTE_STYLE: %s\n" "$BATS_CODE_QUOTE_STYLE" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ -n "$output" ]]; then
|
||||||
|
if [[ ! -w "${output}" ]]; then
|
||||||
|
abort "Output path ${output} is not writeable"
|
||||||
|
fi
|
||||||
|
export BATS_REPORT_OUTPUT_PATH="$output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$setup_suite_file" && ! -f "$setup_suite_file" ]]; then
|
||||||
|
abort "--setup-suite-file $setup_suite_file does not exist!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
filenames=()
|
||||||
|
for filename in "${arguments[@]}"; do
|
||||||
|
expand_path "$filename" 'filename'
|
||||||
|
|
||||||
|
if [[ -z "$setup_suite_file" ]]; then
|
||||||
|
if [[ -d "$filename" ]]; then
|
||||||
|
dirname="$filename"
|
||||||
|
else
|
||||||
|
dirname="${filename%/*}"
|
||||||
|
fi
|
||||||
|
potential_setup_suite_file="$dirname/setup_suite.bash"
|
||||||
|
if [[ -e "$potential_setup_suite_file" ]]; then
|
||||||
|
setup_suite_file="$potential_setup_suite_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$filename" ]]; then
|
||||||
|
shopt -s nullglob
|
||||||
|
if [[ "$recursive" -eq 1 ]]; then
|
||||||
|
while IFS= read -r -d $'\0' file; do
|
||||||
|
filenames+=("$file")
|
||||||
|
done < <(find -L "$filename" -type f -name "*.${BATS_FILE_EXTENSION:-bats}" -print0 | sort -z)
|
||||||
|
else
|
||||||
|
for suite_filename in "$filename"/*."${BATS_FILE_EXTENSION:-bats}"; do
|
||||||
|
filenames+=("$suite_filename")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
shopt -u nullglob
|
||||||
|
else
|
||||||
|
filenames+=("$filename")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$setup_suite_file" ]]; then
|
||||||
|
flags+=("--setup-suite-file" "$setup_suite_file")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/validator.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/validator.bash"
|
||||||
|
|
||||||
|
trap 'BATS_INTERRUPTED=true' INT # let the lower levels handle the interruption
|
||||||
|
|
||||||
|
set -o pipefail execfail
|
||||||
|
|
||||||
|
if [[ -n "$report_formatter" ]]; then
|
||||||
|
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
|
||||||
|
tee >("$interpolated_report_formatter" "${report_formatter_flags[@]}" >"${BATS_REPORT_OUTPUT_PATH}/${BATS_REPORT_FILE_NAME}") | \
|
||||||
|
bats_test_count_validator | \
|
||||||
|
"$interpolated_formatter" "${formatter_flags[@]}"
|
||||||
|
else
|
||||||
|
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
|
||||||
|
bats_test_count_validator | \
|
||||||
|
"$interpolated_formatter" "${formatter_flags[@]}"
|
||||||
|
fi
|
338
portable/test/bats/libexec/bats-core/bats-exec-file
Executable file
338
portable/test/bats/libexec/bats-core/bats-exec-file
Executable file
@ -0,0 +1,338 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eET
|
||||||
|
|
||||||
|
flags=('--dummy-flag')
|
||||||
|
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
|
||||||
|
extended_syntax=''
|
||||||
|
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||||
|
declare -r BATS_RETRY_RETURN_CODE=126
|
||||||
|
export BATS_TEST_RETRIES=0 # no retries by default
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-j)
|
||||||
|
shift
|
||||||
|
num_jobs="$1"
|
||||||
|
;;
|
||||||
|
-T)
|
||||||
|
flags+=('-T')
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
flags+=('-x')
|
||||||
|
extended_syntax=1
|
||||||
|
;;
|
||||||
|
--no-parallelize-within-files)
|
||||||
|
# use singular to allow for users to override in file
|
||||||
|
BATS_NO_PARALLELIZE_WITHIN_FILE=1
|
||||||
|
;;
|
||||||
|
--dummy-flag)
|
||||||
|
;;
|
||||||
|
--trace)
|
||||||
|
flags+=('--trace')
|
||||||
|
;;
|
||||||
|
--print-output-on-failure)
|
||||||
|
flags+=(--print-output-on-failure)
|
||||||
|
;;
|
||||||
|
--show-output-of-passing-tests)
|
||||||
|
flags+=(--show-output-of-passing-tests)
|
||||||
|
;;
|
||||||
|
--verbose-run)
|
||||||
|
flags+=(--verbose-run)
|
||||||
|
;;
|
||||||
|
--gather-test-outputs-in)
|
||||||
|
shift
|
||||||
|
flags+=(--gather-test-outputs-in "$1")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
filename="$1"
|
||||||
|
TESTS_FILE="$2"
|
||||||
|
|
||||||
|
if [[ ! -f "$filename" ]]; then
|
||||||
|
printf 'Testfile "%s" not found\n' "$filename" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BATS_TEST_FILENAME="$filename"
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/preprocessing.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
|
||||||
|
|
||||||
|
bats_run_setup_file() {
|
||||||
|
# shellcheck source=lib/bats-core/tracing.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||||
|
# shellcheck source=lib/bats-core/test_functions.bash
|
||||||
|
# shellcheck disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
|
||||||
|
|
||||||
|
exec 3<&1
|
||||||
|
|
||||||
|
# these are defined only to avoid errors when referencing undefined variables down the line
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_TEST_NAME= # used in tracing.bash
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_TEST_COMPLETED= # used in tracing.bash
|
||||||
|
|
||||||
|
BATS_SOURCE_FILE_COMPLETED=
|
||||||
|
BATS_SETUP_FILE_COMPLETED=
|
||||||
|
BATS_TEARDOWN_FILE_COMPLETED=
|
||||||
|
# shellcheck disable=2034
|
||||||
|
BATS_ERROR_STATUS= # used in tracing.bash
|
||||||
|
touch "$BATS_OUT"
|
||||||
|
bats_setup_tracing
|
||||||
|
trap 'bats_file_teardown_trap' EXIT
|
||||||
|
|
||||||
|
local status=0
|
||||||
|
# get the setup_file/teardown_file functions for this file (if it has them)
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$BATS_TEST_SOURCE" >>"$BATS_OUT" 2>&1
|
||||||
|
|
||||||
|
BATS_SOURCE_FILE_COMPLETED=1
|
||||||
|
|
||||||
|
setup_file >>"$BATS_OUT" 2>&1
|
||||||
|
|
||||||
|
BATS_SETUP_FILE_COMPLETED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_teardown_file() {
|
||||||
|
# avoid running the therdown trap due to errors in teardown_file
|
||||||
|
trap 'bats_file_exit_trap' EXIT
|
||||||
|
# rely on bats_error_trap to catch failures
|
||||||
|
teardown_file >>"$BATS_OUT" 2>&1
|
||||||
|
|
||||||
|
BATS_TEARDOWN_FILE_COMPLETED=1
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_file_teardown_trap() {
|
||||||
|
bats_run_teardown_file
|
||||||
|
bats_file_exit_trap
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/common.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||||
|
|
||||||
|
bats_file_exit_trap() {
|
||||||
|
trap - ERR EXIT
|
||||||
|
local failure_reason
|
||||||
|
local -i failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + 1 ))
|
||||||
|
if [[ -z "$BATS_SETUP_FILE_COMPLETED" || -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
|
||||||
|
if [[ -z "$BATS_SETUP_FILE_COMPLETED" ]]; then
|
||||||
|
failure_reason='setup_file'
|
||||||
|
elif [[ -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
|
||||||
|
failure_reason='teardown_file'
|
||||||
|
failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + ${#tests_to_run[@]} + 1 ))
|
||||||
|
elif [[ -z "$BATS_SOURCE_FILE_COMPLETED" ]]; then
|
||||||
|
failure_reason='source'
|
||||||
|
else
|
||||||
|
failure_reason='unknown internal'
|
||||||
|
fi
|
||||||
|
printf "not ok %d %s\n" "$failure_test_index" "$failure_reason failed" >&3
|
||||||
|
local stack_trace
|
||||||
|
bats_get_failure_stack_trace stack_trace
|
||||||
|
bats_print_stack_trace "${stack_trace[@]}" >&3
|
||||||
|
bats_print_failed_command "${stack_trace[@]}" >&3
|
||||||
|
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
|
||||||
|
rm -rf "$BATS_OUT"
|
||||||
|
bats_exec_file_status=1
|
||||||
|
fi
|
||||||
|
exit $bats_exec_file_status
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup_file() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown_file() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_forward_output_of_parallel_test() {
|
||||||
|
local test_number_in_suite=$1
|
||||||
|
local status=0
|
||||||
|
wait "$(cat "$output_folder/$test_number_in_suite/pid")" || status=1
|
||||||
|
cat "$output_folder/$test_number_in_suite/stdout"
|
||||||
|
cat "$output_folder/$test_number_in_suite/stderr" >&2
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_is_next_parallel_test_finished() {
|
||||||
|
local PID
|
||||||
|
# get the pid of the next potentially finished test
|
||||||
|
PID=$(cat "$output_folder/$(( test_number_in_suite_of_last_finished_test + 1 ))/pid")
|
||||||
|
# try to send a signal to this process
|
||||||
|
# if it fails, the process exited,
|
||||||
|
# if it succeeds, the process is still running
|
||||||
|
if kill -0 "$PID" 2>/dev/null; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# prints output from all tests in the order they were started
|
||||||
|
# $1 == "blocking": wait for a test to finish before printing
|
||||||
|
# != "blocking": abort printing, when a test has not finished
|
||||||
|
bats_forward_output_for_parallel_tests() {
|
||||||
|
local status=0
|
||||||
|
# was the next test already started?
|
||||||
|
while (( test_number_in_suite_of_last_finished_test + 1 <= test_number_in_suite )); do
|
||||||
|
# if we are okay with waiting or if the test has already been finished
|
||||||
|
if [[ "$1" == "blocking" ]] || bats_is_next_parallel_test_finished ; then
|
||||||
|
(( ++test_number_in_suite_of_last_finished_test ))
|
||||||
|
bats_forward_output_of_parallel_test "$test_number_in_suite_of_last_finished_test" || status=$?
|
||||||
|
else
|
||||||
|
# non-blocking and the process has not finished -> abort the printing
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_test_with_retries() { # <args>
|
||||||
|
local status=0
|
||||||
|
local should_try_again=1 try_number
|
||||||
|
for ((try_number=1; should_try_again; ++try_number)); do
|
||||||
|
if "$BATS_LIBEXEC/bats-exec-test" "$@" "$try_number"; then
|
||||||
|
should_try_again=0
|
||||||
|
else
|
||||||
|
status=$?
|
||||||
|
if ((status == BATS_RETRY_RETURN_CODE)); then
|
||||||
|
should_try_again=1
|
||||||
|
else
|
||||||
|
should_try_again=0
|
||||||
|
bats_exec_file_status=$status
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_tests_in_parallel() {
|
||||||
|
local output_folder="$BATS_RUN_TMPDIR/parallel_output"
|
||||||
|
local status=0
|
||||||
|
mkdir -p "$output_folder"
|
||||||
|
# shellcheck source=lib/bats-core/semaphore.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/semaphore.bash"
|
||||||
|
bats_semaphore_setup
|
||||||
|
# the test_number_in_file is not yet incremented -> one before the next test to run
|
||||||
|
local test_number_in_suite_of_last_finished_test="$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE" # stores which test was printed last
|
||||||
|
local test_number_in_file=0 test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE
|
||||||
|
for test_name in "${tests_to_run[@]}"; do
|
||||||
|
# Only handle non-empty lines
|
||||||
|
if [[ $test_name ]]; then
|
||||||
|
((++test_number_in_suite))
|
||||||
|
((++test_number_in_file))
|
||||||
|
mkdir -p "$output_folder/$test_number_in_suite"
|
||||||
|
bats_semaphore_run "$output_folder/$test_number_in_suite" \
|
||||||
|
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" \
|
||||||
|
> "$output_folder/$test_number_in_suite/pid"
|
||||||
|
fi
|
||||||
|
# print results early to get interactive feedback
|
||||||
|
bats_forward_output_for_parallel_tests non-blocking || status=1 # ignore if we did not finish yet
|
||||||
|
done
|
||||||
|
bats_forward_output_for_parallel_tests blocking || status=1
|
||||||
|
return $status
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_read_tests_list_file() {
|
||||||
|
local line_number=0
|
||||||
|
tests_to_run=()
|
||||||
|
# the global test number must be visible to traps -> not local
|
||||||
|
local test_number_in_suite=''
|
||||||
|
while read -r test_line; do
|
||||||
|
# check if the line begins with filename
|
||||||
|
# filename might contain some hard to parse characters,
|
||||||
|
# use simple string operations to work around that issue
|
||||||
|
if [[ "$filename" == "${test_line::${#filename}}" ]]; then
|
||||||
|
# get the rest of the line without the separator \t
|
||||||
|
test_name=${test_line:$((1 + ${#filename} ))}
|
||||||
|
tests_to_run+=("$test_name")
|
||||||
|
# save the first test's number for later iteration
|
||||||
|
# this assumes that tests for a file are stored consecutive in the file!
|
||||||
|
if [[ -z "$test_number_in_suite" ]]; then
|
||||||
|
test_number_in_suite=$line_number
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
((++line_number))
|
||||||
|
done <"$TESTS_FILE"
|
||||||
|
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE="$test_number_in_suite"
|
||||||
|
declare -ri BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE # mark readonly (cannot merge assignment, because value would be lost)
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_tests() {
|
||||||
|
bats_exec_file_status=0
|
||||||
|
|
||||||
|
if [[ "$num_jobs" != 1 && "${BATS_NO_PARALLELIZE_WITHIN_FILE-False}" == False ]]; then
|
||||||
|
export BATS_SEMAPHORE_NUMBER_OF_SLOTS="$num_jobs"
|
||||||
|
bats_run_tests_in_parallel "$BATS_RUN_TMPDIR/parallel_output" || bats_exec_file_status=1
|
||||||
|
else
|
||||||
|
local test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE \
|
||||||
|
test_number_in_file=0
|
||||||
|
for test_name in "${tests_to_run[@]}"; do
|
||||||
|
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||||
|
bats_exec_file_status=130 # bash's code for SIGINT exits
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
# Only handle non-empty lines
|
||||||
|
if [[ $test_name ]]; then
|
||||||
|
((++test_number_in_suite))
|
||||||
|
((++test_number_in_file))
|
||||||
|
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" \
|
||||||
|
"$test_number_in_suite" "$test_number_in_file" || bats_exec_file_status=$?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_create_file_tempdirs() {
|
||||||
|
local bats_files_tmpdir="${BATS_RUN_TMPDIR}/file"
|
||||||
|
if ! mkdir -p "$bats_files_tmpdir"; then
|
||||||
|
printf 'Failed to create %s\n' "$bats_files_tmpdir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BATS_FILE_TMPDIR="$bats_files_tmpdir/${BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE?}"
|
||||||
|
if ! mkdir "$BATS_FILE_TMPDIR"; then
|
||||||
|
printf 'Failed to create BATS_FILE_TMPDIR=%s\n' "$BATS_FILE_TMPDIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ln -s "$BATS_TEST_FILENAME" "$BATS_FILE_TMPDIR-$(basename "$BATS_TEST_FILENAME").source_file"
|
||||||
|
export BATS_FILE_TMPDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
trap 'BATS_INTERRUPTED=true' INT
|
||||||
|
|
||||||
|
if [[ -n "$extended_syntax" ]]; then
|
||||||
|
printf "suite %s\n" "$filename"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE=0 # predeclare as Bash 3.2 does not support declare -g
|
||||||
|
bats_read_tests_list_file
|
||||||
|
|
||||||
|
# don't run potentially expensive setup/teardown_file
|
||||||
|
# when there are no tests to run
|
||||||
|
if [[ ${#tests_to_run[@]} -eq 0 ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# requires the test list to be read but not empty
|
||||||
|
bats_create_file_tempdirs
|
||||||
|
|
||||||
|
bats_preprocess_source "$filename"
|
||||||
|
|
||||||
|
trap bats_interrupt_trap INT
|
||||||
|
bats_run_setup_file
|
||||||
|
|
||||||
|
# during tests, we don't want to get backtraces from this level
|
||||||
|
# just wait for the test to be interrupted and display their trace
|
||||||
|
trap 'BATS_INTERRUPTED=true' INT
|
||||||
|
bats_run_tests
|
||||||
|
|
||||||
|
trap bats_interrupt_trap INT
|
||||||
|
bats_run_teardown_file
|
||||||
|
|
||||||
|
exit $bats_exec_file_status
|
359
portable/test/bats/libexec/bats-core/bats-exec-suite
Executable file
359
portable/test/bats/libexec/bats-core/bats-exec-suite
Executable file
@ -0,0 +1,359 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
count_only_flag=''
|
||||||
|
filter=''
|
||||||
|
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
|
||||||
|
bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
|
||||||
|
bats_no_parallelize_within_files=
|
||||||
|
filter_status=''
|
||||||
|
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
|
||||||
|
setup_suite_file=''
|
||||||
|
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
printf 'Error: %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-c)
|
||||||
|
count_only_flag=1
|
||||||
|
;;
|
||||||
|
-f)
|
||||||
|
shift
|
||||||
|
filter="$1"
|
||||||
|
;;
|
||||||
|
-j)
|
||||||
|
shift
|
||||||
|
num_jobs="$1"
|
||||||
|
flags+=('-j' "$num_jobs")
|
||||||
|
;;
|
||||||
|
-T)
|
||||||
|
flags+=('-T')
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
flags+=('-x')
|
||||||
|
;;
|
||||||
|
--no-parallelize-across-files)
|
||||||
|
bats_no_parallelize_across_files=1
|
||||||
|
;;
|
||||||
|
--no-parallelize-within-files)
|
||||||
|
bats_no_parallelize_within_files=1
|
||||||
|
flags+=("--no-parallelize-within-files")
|
||||||
|
;;
|
||||||
|
--filter-status)
|
||||||
|
shift
|
||||||
|
filter_status="$1"
|
||||||
|
;;
|
||||||
|
--dummy-flag)
|
||||||
|
;;
|
||||||
|
--trace)
|
||||||
|
flags+=('--trace')
|
||||||
|
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
|
||||||
|
;;
|
||||||
|
--print-output-on-failure)
|
||||||
|
flags+=(--print-output-on-failure)
|
||||||
|
;;
|
||||||
|
--show-output-of-passing-tests)
|
||||||
|
flags+=(--show-output-of-passing-tests)
|
||||||
|
;;
|
||||||
|
--verbose-run)
|
||||||
|
flags+=(--verbose-run)
|
||||||
|
;;
|
||||||
|
--gather-test-outputs-in)
|
||||||
|
shift
|
||||||
|
flags+=(--gather-test-outputs-in "$1")
|
||||||
|
;;
|
||||||
|
--setup-suite-file)
|
||||||
|
shift
|
||||||
|
setup_suite_file="$1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$num_jobs" != 1 ]]; then
|
||||||
|
if ! type -p parallel >/dev/null && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||||
|
abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# shellcheck source=lib/bats-core/semaphore.bash
|
||||||
|
source "${BATS_ROOT}/lib/bats-core/semaphore.bash"
|
||||||
|
bats_semaphore_setup
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create a file that contains all (filtered) tests to run from all files
|
||||||
|
TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
|
||||||
|
|
||||||
|
bats_gather_tests() {
|
||||||
|
all_tests=()
|
||||||
|
for filename in "$@"; do
|
||||||
|
if [[ ! -f "$filename" ]]; then
|
||||||
|
abort "Test file \"${filename}\" does not exist"
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_names=()
|
||||||
|
test_dupes=()
|
||||||
|
while read -r line; do
|
||||||
|
if [[ ! "$line" =~ ^bats_test_function\ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
line="${line%$'\r'}"
|
||||||
|
line="${line#* }"
|
||||||
|
test_line=$(printf "%s\t%s" "$filename" "$line")
|
||||||
|
all_tests+=("$test_line")
|
||||||
|
printf "%s\n" "$test_line" >>"$TESTS_LIST_FILE"
|
||||||
|
# avoid unbound variable errors on empty array expansion with old bash versions
|
||||||
|
if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
|
||||||
|
test_dupes+=("$line")
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
test_names+=("$line")
|
||||||
|
done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename")
|
||||||
|
|
||||||
|
if [[ "${#test_dupes[@]}" -ne 0 ]]; then
|
||||||
|
abort "Duplicate test name(s) in file \"${filename}\": ${test_dupes[*]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
test_count="${#all_tests[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ROOT=${1-}
|
||||||
|
TEST_ROOT=${TEST_ROOT%/*}
|
||||||
|
BATS_RUN_LOGS_DIRECTORY="$TEST_ROOT/.bats/run-logs"
|
||||||
|
if [[ ! -d "$BATS_RUN_LOGS_DIRECTORY" ]]; then
|
||||||
|
if [[ -n "$filter_status" ]]; then
|
||||||
|
printf "Error: --filter-status needs '%s/' to save failed tests. Please create this folder, add it to .gitignore and try again.\n" "$BATS_RUN_LOGS_DIRECTORY"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
BATS_RUN_LOGS_DIRECTORY=
|
||||||
|
fi
|
||||||
|
# discard via sink instead of having a conditional later
|
||||||
|
export BATS_RUNLOG_FILE='/dev/null'
|
||||||
|
else
|
||||||
|
# use UTC (-u) to avoid problems with TZ changes
|
||||||
|
BATS_RUNLOG_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||||
|
export BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_gather_tests "$@"
|
||||||
|
|
||||||
|
if [[ -n "$filter_status" ]]; then
|
||||||
|
# shellcheck source=lib/bats-core/common.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||||
|
case "$filter_status" in
|
||||||
|
failed)
|
||||||
|
bats_filter_test_by_status() { # <line>
|
||||||
|
! bats_binary_search "$1" "passed_tests"
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
passed)
|
||||||
|
bats_filter_test_by_status() {
|
||||||
|
! bats_binary_search "$1" "failed_tests"
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
missed)
|
||||||
|
bats_filter_test_by_status() {
|
||||||
|
! bats_binary_search "$1" "failed_tests" && ! bats_binary_search "$1" "passed_tests"
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "Error: Unknown value '%s' for --filter-status. Valid values are 'failed' and 'missed'.\n" "$filter_status">&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if IFS='' read -d $'\n' -r BATS_PREVIOUS_RUNLOG_FILE < <(ls -1r "$BATS_RUN_LOGS_DIRECTORY"); then
|
||||||
|
BATS_PREVIOUS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/$BATS_PREVIOUS_RUNLOG_FILE"
|
||||||
|
if [[ $BATS_PREVIOUS_RUNLOG_FILE == "$BATS_RUNLOG_FILE" ]]; then
|
||||||
|
count=$(find "$BATS_RUN_LOGS_DIRECTORY" -name "$BATS_RUNLOG_DATE*" | wc -l)
|
||||||
|
BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}-$count.log"
|
||||||
|
fi
|
||||||
|
failed_tests=()
|
||||||
|
passed_tests=()
|
||||||
|
# store tests that were already filtered out in the last run for the same filter reason
|
||||||
|
last_filtered_tests=()
|
||||||
|
i=0
|
||||||
|
while read -rd $'\n' line; do
|
||||||
|
((++i))
|
||||||
|
case "$line" in
|
||||||
|
"passed "*)
|
||||||
|
passed_tests+=("${line#passed }")
|
||||||
|
;;
|
||||||
|
"failed "*)
|
||||||
|
failed_tests+=("${line#failed }")
|
||||||
|
;;
|
||||||
|
"status-filtered $filter_status"*) # pick up tests that were filtered in the last round for the same status
|
||||||
|
last_filtered_tests+=("${line#status-filtered "$filter_status" }")
|
||||||
|
;;
|
||||||
|
"status-filtered "*) # ignore other status-filtered lines
|
||||||
|
;;
|
||||||
|
"#"*) # allow for comments
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "Error: %s:%d: Invalid format: %s\n" "$BATS_PREVIOUS_RUNLOG_FILE" "$i" "$line" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(sort "$BATS_PREVIOUS_RUNLOG_FILE")
|
||||||
|
|
||||||
|
filtered_tests=()
|
||||||
|
for line in "${all_tests[@]}"; do
|
||||||
|
if bats_filter_test_by_status "$line" && ! bats_binary_search "$line" last_filtered_tests; then
|
||||||
|
printf "%s\n" "$line"
|
||||||
|
filtered_tests+=("$line")
|
||||||
|
else
|
||||||
|
printf "status-filtered %s %s\n" "$filter_status" "$line" >> "$BATS_RUNLOG_FILE"
|
||||||
|
fi
|
||||||
|
done > "$TESTS_LIST_FILE"
|
||||||
|
|
||||||
|
# save filtered tests to exclude them again in next round
|
||||||
|
for test_line in "${last_filtered_tests[@]}"; do
|
||||||
|
printf "status-filtered %s %s\n" "$filter_status" "$test_line"
|
||||||
|
done >> "$BATS_RUNLOG_FILE"
|
||||||
|
|
||||||
|
test_count="${#filtered_tests[@]}"
|
||||||
|
if [[ ${#failed_tests[@]} -eq 0 && ${#filtered_tests[@]} -eq 0 ]]; then
|
||||||
|
printf "There where no failed tests in the last recorded run.\n" >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "No recording of previous runs found. Running all tests!\n" >&2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$count_only_flag" ]]; then
|
||||||
|
printf '%d\n' "${test_count}"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||||
|
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
|
||||||
|
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# only abort on the lowest levels
|
||||||
|
trap 'BATS_INTERRUPTED=true' INT
|
||||||
|
|
||||||
|
bats_exec_suite_status=0
|
||||||
|
printf '1..%d\n' "${test_count}"
|
||||||
|
|
||||||
|
# No point on continuing if there's no tests.
|
||||||
|
if [[ "${test_count}" == 0 ]]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BATS_SUITE_TMPDIR="${BATS_RUN_TMPDIR}/suite"
|
||||||
|
if ! mkdir "$BATS_SUITE_TMPDIR"; then
|
||||||
|
printf '%s\n' "Failed to create BATS_SUITE_TMPDIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
|
||||||
|
# (see https://github.com/bats-core/bats-core/issues/329)
|
||||||
|
# If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
|
||||||
|
# Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
|
||||||
|
IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@"| nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/tracing.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||||
|
bats_setup_tracing
|
||||||
|
|
||||||
|
trap bats_suite_exit_trap EXIT
|
||||||
|
|
||||||
|
bats_suite_exit_trap() {
|
||||||
|
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" || -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
|
||||||
|
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" ]]; then
|
||||||
|
printf "not ok 1 setup_suite\n"
|
||||||
|
elif [[ -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
|
||||||
|
printf "not ok %d teardown_suite\n" $((test_count+1))
|
||||||
|
fi
|
||||||
|
local stack_trace
|
||||||
|
bats_get_failure_stack_trace stack_trace
|
||||||
|
bats_print_stack_trace "${stack_trace[@]}"
|
||||||
|
bats_print_failed_command "${stack_trace[@]}"
|
||||||
|
bats_exec_suite_status=1
|
||||||
|
fi
|
||||||
|
if [[ ${BATS_INTERRUPTED-NOTSET} != NOTSET ]]; then
|
||||||
|
printf "\n# Received SIGINT, aborting ...\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$BATS_RUN_LOGS_DIRECTORY" && -n "${BATS_INTERRUPTED:-}" ]]; then
|
||||||
|
# aborting a test run with CTRL+C does not save the runlog file
|
||||||
|
rm "$BATS_RUNLOG_FILE"
|
||||||
|
fi
|
||||||
|
exit "$bats_exec_suite_status"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_run_teardown_suite() {
|
||||||
|
# avoid being called twice, in case this is not called through bats_teardown_suite_trap
|
||||||
|
# but from the end of file
|
||||||
|
trap bats_suite_exit_trap EXIT
|
||||||
|
set -eET
|
||||||
|
BATS_TEARDOWN_SUITE_COMPLETED=
|
||||||
|
teardown_suite 2>&1
|
||||||
|
BATS_TEARDOWN_SUITE_COMPLETED=1
|
||||||
|
set +ET
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_teardown_suite_trap() {
|
||||||
|
bats_run_teardown_suite
|
||||||
|
bats_suite_exit_trap
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_suite() {
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown_suite() {
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
trap bats_teardown_suite_trap EXIT
|
||||||
|
|
||||||
|
if [[ -n "$setup_suite_file" ]]; then
|
||||||
|
setup_suite() {
|
||||||
|
printf "%s does not define \`setup_suite()\`\n" "$setup_suite_file" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$setup_suite_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -eET
|
||||||
|
BATS_SETUP_SUITE_COMPLETED=
|
||||||
|
setup_suite 2>&1
|
||||||
|
BATS_SETUP_SUITE_COMPLETED=1
|
||||||
|
set +ET
|
||||||
|
|
||||||
|
if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
|
||||||
|
# run files in parallel to get the maximum pool of parallel tasks
|
||||||
|
# shellcheck disable=SC2086,SC2068
|
||||||
|
# we need to handle the quoting of ${flags[@]} ourselves,
|
||||||
|
# because parallel can only quote it as one
|
||||||
|
parallel --keep-order --jobs "$num_jobs" bats-exec-file "$(printf "%q " "${flags[@]}")" "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || bats_exec_suite_status=1
|
||||||
|
else
|
||||||
|
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
|
||||||
|
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||||
|
bats_exec_suite_status=130 # bash's code for SIGINT exits
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || bats_exec_suite_status=1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -eET
|
||||||
|
bats_run_teardown_suite
|
||||||
|
|
||||||
|
exit "$bats_exec_suite_status"
|
231
portable/test/bats/libexec/bats-core/bats-exec-test
Executable file
231
portable/test/bats/libexec/bats-core/bats-exec-test
Executable file
@ -0,0 +1,231 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eET
|
||||||
|
|
||||||
|
# Variables used in other scripts.
|
||||||
|
BATS_ENABLE_TIMING=''
|
||||||
|
BATS_EXTENDED_SYNTAX=''
|
||||||
|
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
|
||||||
|
BATS_PRINT_OUTPUT_ON_FAILURE="${BATS_PRINT_OUTPUT_ON_FAILURE:-}"
|
||||||
|
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS:-}"
|
||||||
|
BATS_VERBOSE_RUN="${BATS_VERBOSE_RUN:-}"
|
||||||
|
BATS_GATHER_TEST_OUTPUTS_IN="${BATS_GATHER_TEST_OUTPUTS_IN:-}"
|
||||||
|
BATS_TEST_NAME_PREFIX="${BATS_TEST_NAME_PREFIX:-}"
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-T)
|
||||||
|
BATS_ENABLE_TIMING='-T'
|
||||||
|
;;
|
||||||
|
-x)
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
BATS_EXTENDED_SYNTAX='-x'
|
||||||
|
;;
|
||||||
|
--dummy-flag)
|
||||||
|
;;
|
||||||
|
--trace)
|
||||||
|
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
|
||||||
|
;;
|
||||||
|
--print-output-on-failure)
|
||||||
|
BATS_PRINT_OUTPUT_ON_FAILURE=1
|
||||||
|
;;
|
||||||
|
--show-output-of-passing-tests)
|
||||||
|
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
|
||||||
|
;;
|
||||||
|
--verbose-run)
|
||||||
|
BATS_VERBOSE_RUN=1
|
||||||
|
;;
|
||||||
|
--gather-test-outputs-in)
|
||||||
|
shift
|
||||||
|
BATS_GATHER_TEST_OUTPUTS_IN="$1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
export BATS_TEST_FILENAME="$1"
|
||||||
|
export BATS_TEST_NAME="$2"
|
||||||
|
export BATS_SUITE_TEST_NUMBER="$3"
|
||||||
|
export BATS_TEST_NUMBER="$4"
|
||||||
|
BATS_TEST_TRY_NUMBER="$5"
|
||||||
|
|
||||||
|
if [[ -z "$BATS_TEST_FILENAME" ]]; then
|
||||||
|
printf 'usage: bats-exec-test <filename>\n' >&2
|
||||||
|
exit 1
|
||||||
|
elif [[ ! -f "$BATS_TEST_FILENAME" ]]; then
|
||||||
|
printf 'bats: %s does not exist\n' "$BATS_TEST_FILENAME" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_create_test_tmpdirs() {
|
||||||
|
local tests_tmpdir="${BATS_RUN_TMPDIR}/test"
|
||||||
|
if ! mkdir -p "$tests_tmpdir"; then
|
||||||
|
printf 'Failed to create: %s\n' "$tests_tmpdir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_TEST_TMPDIR="$tests_tmpdir/$BATS_SUITE_TEST_NUMBER"
|
||||||
|
if ! mkdir "$BATS_TEST_TMPDIR"; then
|
||||||
|
printf 'Failed to create BATS_TEST_TMPDIR%d: %s\n' "$BATS_TEST_TRY_NUMBER" "$BATS_TEST_TMPDIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s\n" "$BATS_TEST_NAME" > "$BATS_TEST_TMPDIR.name"
|
||||||
|
|
||||||
|
export BATS_TEST_TMPDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
# load the test helper functions like `load` or `run` that are needed to run a (preprocessed) .bats file without bash errors
|
||||||
|
# shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
|
||||||
|
# shellcheck source=lib/bats-core/tracing.bash disable=SC2153
|
||||||
|
source "$BATS_ROOT/lib/bats-core/tracing.bash"
|
||||||
|
|
||||||
|
bats_teardown_trap() {
|
||||||
|
bats_check_status_from_trap
|
||||||
|
local bats_teardown_trap_status=0
|
||||||
|
# mark the start of this function to distinguish where skip is called
|
||||||
|
# parameter 1 will signify the reason why this function was called
|
||||||
|
# this is used to identify when this is called as exit trap function
|
||||||
|
BATS_TEARDOWN_STARTED=${1:-1}
|
||||||
|
teardown >>"$BATS_OUT" 2>&1 || bats_teardown_trap_status="$?"
|
||||||
|
|
||||||
|
if [[ $bats_teardown_trap_status -eq 0 ]]; then
|
||||||
|
BATS_TEARDOWN_COMPLETED=1
|
||||||
|
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
|
||||||
|
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
|
||||||
|
BATS_ERROR_STATUS="$bats_teardown_trap_status"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bats_exit_trap
|
||||||
|
}
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/common.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/common.bash"
|
||||||
|
|
||||||
|
bats_exit_trap() {
|
||||||
|
local status
|
||||||
|
local skipped=''
|
||||||
|
trap - ERR EXIT
|
||||||
|
|
||||||
|
if [[ -n "$BATS_TEST_SKIPPED" ]]; then
|
||||||
|
skipped=' # skip'
|
||||||
|
if [[ "$BATS_TEST_SKIPPED" != '1' ]]; then
|
||||||
|
skipped+=" $BATS_TEST_SKIPPED"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_TEST_TIME=''
|
||||||
|
if [[ -z "${skipped}" && -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
BATS_TEST_TIME=" in "$(( $(get_mills_since_epoch) - BATS_TEST_START_TIME ))"ms"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
|
||||||
|
|
||||||
|
local should_retry=''
|
||||||
|
if [[ -z "$BATS_TEST_COMPLETED" || -z "$BATS_TEARDOWN_COMPLETED" || "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
|
||||||
|
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
|
||||||
|
# For some versions of bash, `$?` may not be set properly for some error
|
||||||
|
# conditions before triggering the EXIT trap directly (see #72 and #81).
|
||||||
|
# Thanks to the `BATS_TEARDOWN_COMPLETED` signal, this will pinpoint such
|
||||||
|
# errors if they happen during `teardown()` when `bats_perform_test` calls
|
||||||
|
# `bats_teardown_trap` directly after the test itself passes.
|
||||||
|
#
|
||||||
|
# If instead the test fails, and the `teardown()` error happens while
|
||||||
|
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
|
||||||
|
# output, since there's no way to reach the `bats_exit_trap` call.
|
||||||
|
BATS_ERROR_STATUS=1
|
||||||
|
fi
|
||||||
|
if bats_should_retry_test; then
|
||||||
|
should_retry=1
|
||||||
|
status=126 # signify retry
|
||||||
|
rm -r "$BATS_TEST_TMPDIR" # clean up for retry
|
||||||
|
else
|
||||||
|
printf 'not ok %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
|
||||||
|
local stack_trace
|
||||||
|
bats_get_failure_stack_trace stack_trace
|
||||||
|
bats_print_stack_trace "${stack_trace[@]}" >&3
|
||||||
|
bats_print_failed_command "${stack_trace[@]}" >&3
|
||||||
|
|
||||||
|
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE && -n "${output:-}" ]]; then
|
||||||
|
printf "Last output:\n%s\n" "$output" >> "$BATS_OUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_bats_out=1
|
||||||
|
status=1
|
||||||
|
local state=failed
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf 'ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" \
|
||||||
|
"$skipped" >&3
|
||||||
|
status=0
|
||||||
|
local state=passed
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$should_retry" ]]; then
|
||||||
|
printf "%s %s\t%s\n" "$state" "$BATS_TEST_FILENAME" "$BATS_TEST_NAME" >> "$BATS_RUNLOG_FILE"
|
||||||
|
|
||||||
|
if [[ $print_bats_out ]]; then
|
||||||
|
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
|
||||||
|
local try_suffix=
|
||||||
|
if [[ -n "$should_retry" ]]; then
|
||||||
|
try_suffix="-try$BATS_TEST_TRY_NUMBER"
|
||||||
|
fi
|
||||||
|
cp "$BATS_OUT" "$BATS_GATHER_TEST_OUTPUTS_IN/$BATS_SUITE_TEST_NUMBER$try_suffix-$BATS_TEST_DESCRIPTION.log"
|
||||||
|
fi
|
||||||
|
rm -f "$BATS_OUT"
|
||||||
|
exit "$status"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_mills_since_epoch() {
|
||||||
|
local ms_since_epoch
|
||||||
|
ms_since_epoch=$(date +%s%N)
|
||||||
|
if [[ "$ms_since_epoch" == *N || "${#ms_since_epoch}" -lt 19 ]]; then
|
||||||
|
ms_since_epoch=$(( $(date +%s) * 1000 ))
|
||||||
|
else
|
||||||
|
ms_since_epoch=$(( ms_since_epoch / 1000000 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%d\n" "$ms_since_epoch"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_perform_test() {
|
||||||
|
if ! declare -F "$BATS_TEST_NAME" &>/dev/null; then
|
||||||
|
local quoted_test_name
|
||||||
|
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
|
||||||
|
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BATS_TEST_COMPLETED=
|
||||||
|
BATS_TEST_SKIPPED=
|
||||||
|
BATS_TEARDOWN_COMPLETED=
|
||||||
|
BATS_ERROR_STATUS=
|
||||||
|
bats_setup_tracing
|
||||||
|
# mark this call as trap call
|
||||||
|
trap 'bats_teardown_trap as-exit-trap' EXIT
|
||||||
|
|
||||||
|
BATS_TEST_START_TIME=$(get_mills_since_epoch)
|
||||||
|
"$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 4>&1
|
||||||
|
|
||||||
|
BATS_TEST_COMPLETED=1
|
||||||
|
trap 'bats_exit_trap' EXIT
|
||||||
|
bats_teardown_trap "" # pass empty parameter to signify call outside trap
|
||||||
|
}
|
||||||
|
|
||||||
|
trap bats_interrupt_trap INT
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/preprocessing.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
|
||||||
|
|
||||||
|
exec 3<&1
|
||||||
|
|
||||||
|
bats_create_test_tmpdirs
|
||||||
|
# Run the given test.
|
||||||
|
bats_evaluate_preprocessed_source
|
||||||
|
bats_perform_test
|
6
portable/test/bats/libexec/bats-core/bats-format-cat
Executable file
6
portable/test/bats/libexec/bats-core/bats-format-cat
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
trap '' INT
|
||||||
|
|
||||||
|
cat
|
251
portable/test/bats/libexec/bats-core/bats-format-junit
Executable file
251
portable/test/bats/libexec/bats-core/bats-format-junit
Executable file
@ -0,0 +1,251 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
BASE_PATH=.
|
||||||
|
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--base-path)
|
||||||
|
shift
|
||||||
|
normalize_base_path BASE_PATH "$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
init_suite() {
|
||||||
|
suite_test_exec_time=0
|
||||||
|
# since we have to print the suite header before its contents but we don't know the contents before the header,
|
||||||
|
# we have to buffer the contents
|
||||||
|
_suite_buffer=""
|
||||||
|
test_result_state="" # declare for the first flush, when no test has been encountered
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer_log=
|
||||||
|
init_file() {
|
||||||
|
file_count=0
|
||||||
|
file_failures=0
|
||||||
|
file_skipped=0
|
||||||
|
file_exec_time=0
|
||||||
|
test_exec_time=0
|
||||||
|
_buffer=""
|
||||||
|
_buffer_log=""
|
||||||
|
_system_out_log=""
|
||||||
|
test_result_state="" # mark that no test has run in this file so far
|
||||||
|
}
|
||||||
|
|
||||||
|
host() {
|
||||||
|
local hostname="${HOST:-}"
|
||||||
|
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
|
||||||
|
[[ -z "$hostname" ]] && hostname="$(uname -n)"
|
||||||
|
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
|
||||||
|
|
||||||
|
echo "$hostname"
|
||||||
|
}
|
||||||
|
|
||||||
|
# convert $1 (time in milliseconds) to seconds
|
||||||
|
milliseconds_to_seconds() {
|
||||||
|
# we cannot rely on having bc for this calculation
|
||||||
|
full_seconds=$(($1 / 1000))
|
||||||
|
remaining_milliseconds=$(($1 % 1000))
|
||||||
|
if [[ $remaining_milliseconds -eq 0 ]]; then
|
||||||
|
printf "%d" "$full_seconds"
|
||||||
|
else
|
||||||
|
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_header() {
|
||||||
|
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
||||||
|
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
file_header() {
|
||||||
|
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
|
||||||
|
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
|
||||||
|
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
|
||||||
|
}
|
||||||
|
|
||||||
|
file_footer() {
|
||||||
|
printf "</testsuite>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_footer() {
|
||||||
|
printf "</testsuites>\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_test_case() {
|
||||||
|
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
|
||||||
|
# pass and no output can be shortened
|
||||||
|
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||||
|
else
|
||||||
|
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
|
||||||
|
if [[ -n "$_system_out_log" ]]; then
|
||||||
|
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
|
||||||
|
fi
|
||||||
|
if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
|
||||||
|
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
|
||||||
|
fi
|
||||||
|
if [[ "$test_result_state" == skipped ]]; then
|
||||||
|
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
|
||||||
|
fi
|
||||||
|
printf " </testcase>\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
xml_escape() {
|
||||||
|
output=${1//&/&}
|
||||||
|
output=${output//</<}
|
||||||
|
output=${output//>/>}
|
||||||
|
output=${output//'"'/"}
|
||||||
|
output=${output//\'/'}
|
||||||
|
local CONTROL_CHAR=$'\033'
|
||||||
|
output="${output//$CONTROL_CHAR/}"
|
||||||
|
printf "%s" "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_buffer() {
|
||||||
|
local output
|
||||||
|
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||||
|
_suite_buffer="${_suite_buffer}${output%x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
suite_flush() {
|
||||||
|
echo -n "${_suite_buffer}"
|
||||||
|
_suite_buffer=""
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer() {
|
||||||
|
local output
|
||||||
|
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
|
||||||
|
_buffer="${_buffer}${output%x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
echo -n "${_buffer}"
|
||||||
|
_buffer=""
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
if [[ -n "$_buffer_log" ]]; then
|
||||||
|
_buffer_log="${_buffer_log}
|
||||||
|
$1"
|
||||||
|
else
|
||||||
|
_buffer_log="$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
flush_log() {
|
||||||
|
if [[ -n "$test_result_state" ]]; then
|
||||||
|
buffer print_test_case
|
||||||
|
fi
|
||||||
|
_buffer_log=""
|
||||||
|
_system_out_log=""
|
||||||
|
}
|
||||||
|
|
||||||
|
log_system_out() {
|
||||||
|
if [[ -n "$_system_out_log" ]]; then
|
||||||
|
_system_out_log="${_system_out_log}
|
||||||
|
$1"
|
||||||
|
else
|
||||||
|
_system_out_log="$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_file() {
|
||||||
|
if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
|
||||||
|
file_header
|
||||||
|
printf "%s\n" "${_buffer}"
|
||||||
|
file_footer
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_suite() {
|
||||||
|
flush_log
|
||||||
|
suite_header
|
||||||
|
suite_flush
|
||||||
|
finish_file # must come after suite flush to not print the last file before the others
|
||||||
|
suite_footer
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_plan() { # <number of tests>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
init_suite
|
||||||
|
trap finish_suite EXIT
|
||||||
|
trap '' INT
|
||||||
|
|
||||||
|
bats_tap_stream_begin() { # <test index> <test name>
|
||||||
|
flush_log
|
||||||
|
# set after flushing to avoid overriding name of test
|
||||||
|
name="$2"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
test_exec_time="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
test_exec_time=0
|
||||||
|
fi
|
||||||
|
((file_count += 1))
|
||||||
|
test_result_state='ok'
|
||||||
|
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||||
|
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
|
||||||
|
((file_count += 1))
|
||||||
|
((file_skipped += 1))
|
||||||
|
test_result_state='skipped'
|
||||||
|
test_exec_time=0
|
||||||
|
test_skip_message="$3"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||||
|
((file_count += 1))
|
||||||
|
((file_failures += 1))
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
test_exec_time="${BASH_REMATCH[1]}"
|
||||||
|
else
|
||||||
|
test_exec_time=0
|
||||||
|
fi
|
||||||
|
test_result_state=not_ok
|
||||||
|
file_exec_time="$((file_exec_time + test_exec_time))"
|
||||||
|
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
|
||||||
|
local comment="$1" scope="$2"
|
||||||
|
case "$scope" in
|
||||||
|
begin)
|
||||||
|
# everything that happens between begin and [not] ok is FD3 output from the test
|
||||||
|
log_system_out "$comment"
|
||||||
|
;;
|
||||||
|
ok)
|
||||||
|
# non failed tests can produce FD3 output
|
||||||
|
log_system_out "$comment"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# everything else is considered error output
|
||||||
|
log "$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() { # <file name>
|
||||||
|
flush_log
|
||||||
|
suite_buffer finish_file
|
||||||
|
init_file
|
||||||
|
class="${1/$BASE_PATH}"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_unknown() { # <full line>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
328
portable/test/bats/libexec/bats-core/bats-format-pretty
Executable file
328
portable/test/bats/libexec/bats-core/bats-format-pretty
Executable file
@ -0,0 +1,328 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
BASE_PATH=.
|
||||||
|
BATS_ENABLE_TIMING=
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-T)
|
||||||
|
BATS_ENABLE_TIMING="-T"
|
||||||
|
;;
|
||||||
|
--base-path)
|
||||||
|
shift
|
||||||
|
normalize_base_path BASE_PATH "$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
update_count_column_width() {
|
||||||
|
count_column_width=$((${#count} * 2 + 2))
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
# additional space for ' in %s sec'
|
||||||
|
count_column_width=$((count_column_width + ${#SECONDS} + 8))
|
||||||
|
fi
|
||||||
|
# also update dependent value
|
||||||
|
update_count_column_left
|
||||||
|
}
|
||||||
|
|
||||||
|
update_screen_width() {
|
||||||
|
screen_width="$(tput cols)"
|
||||||
|
# also update dependent value
|
||||||
|
update_count_column_left
|
||||||
|
}
|
||||||
|
|
||||||
|
update_count_column_left() {
|
||||||
|
count_column_left=$((screen_width - count_column_width))
|
||||||
|
}
|
||||||
|
|
||||||
|
# avoid unset variables
|
||||||
|
count=0
|
||||||
|
screen_width=80
|
||||||
|
update_count_column_width
|
||||||
|
update_screen_width
|
||||||
|
test_result=
|
||||||
|
|
||||||
|
trap update_screen_width WINCH
|
||||||
|
|
||||||
|
begin() {
|
||||||
|
test_result= # reset to avoid carrying over result state from previous test
|
||||||
|
line_backoff_count=0
|
||||||
|
go_to_column 0
|
||||||
|
update_count_column_width
|
||||||
|
buffer_with_truncation $((count_column_left - 1)) ' %s' "$name"
|
||||||
|
clear_to_end_of_line
|
||||||
|
go_to_column $count_column_left
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
|
||||||
|
else
|
||||||
|
buffer "%${#count}s/${count}" "$index"
|
||||||
|
fi
|
||||||
|
go_to_column 1
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_test() {
|
||||||
|
move_up $line_backoff_count
|
||||||
|
go_to_column 0
|
||||||
|
buffer "$@"
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
set_color 2
|
||||||
|
buffer ' [%s]' "$TIMING"
|
||||||
|
fi
|
||||||
|
advance
|
||||||
|
move_down $(( line_backoff_count - 1 ))
|
||||||
|
}
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
TIMING="${1:-}"
|
||||||
|
finish_test ' ✓ %s' "$name"
|
||||||
|
test_result=pass
|
||||||
|
}
|
||||||
|
|
||||||
|
skip() {
|
||||||
|
local reason="$1"
|
||||||
|
if [[ -n "$reason" ]]; then
|
||||||
|
reason=": $reason"
|
||||||
|
fi
|
||||||
|
BATS_ENABLE_TIMING='' finish_test ' - %s (skipped%s)' "$name" "$reason"
|
||||||
|
test_result=skip
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
set_color 1 bold
|
||||||
|
TIMING="${1:-}"
|
||||||
|
finish_test ' ✗ %s' "$name"
|
||||||
|
test_result=fail
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
case ${test_result} in
|
||||||
|
pass)
|
||||||
|
clear_color
|
||||||
|
;;
|
||||||
|
fail)
|
||||||
|
set_color 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
buffer ' %s\n' "$1"
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
summary() {
|
||||||
|
if [ "$failures" -eq 0 ] ; then
|
||||||
|
set_color 2 bold
|
||||||
|
else
|
||||||
|
set_color 1 bold
|
||||||
|
fi
|
||||||
|
|
||||||
|
buffer '\n%d test' "$count"
|
||||||
|
if [[ "$count" -ne 1 ]]; then
|
||||||
|
buffer 's'
|
||||||
|
fi
|
||||||
|
|
||||||
|
buffer ', %d failure' "$failures"
|
||||||
|
if [[ "$failures" -ne 1 ]]; then
|
||||||
|
buffer 's'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$skipped" -gt 0 ]]; then
|
||||||
|
buffer ', %d skipped' "$skipped"
|
||||||
|
fi
|
||||||
|
|
||||||
|
not_run=$((count - passed - failures - skipped))
|
||||||
|
if [[ "$not_run" -gt 0 ]]; then
|
||||||
|
buffer ', %d not run' "$not_run"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
|
||||||
|
buffer " in $SECONDS seconds"
|
||||||
|
fi
|
||||||
|
|
||||||
|
buffer '\n'
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_with_truncation() {
|
||||||
|
local width="$1"
|
||||||
|
shift
|
||||||
|
local string
|
||||||
|
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf -v 'string' -- "$@"
|
||||||
|
|
||||||
|
if [[ "${#string}" -gt "$width" ]]; then
|
||||||
|
buffer '%s...' "${string:0:$((width - 4))}"
|
||||||
|
else
|
||||||
|
buffer '%s' "$string"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
move_up() {
|
||||||
|
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
|
||||||
|
buffer '\x1B[%dA' "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
move_down() {
|
||||||
|
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
|
||||||
|
buffer '\x1B[%dB' "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
go_to_column() {
|
||||||
|
local column="$1"
|
||||||
|
buffer '\x1B[%dG' $((column + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_to_end_of_line() {
|
||||||
|
buffer '\x1B[K'
|
||||||
|
}
|
||||||
|
|
||||||
|
advance() {
|
||||||
|
clear_to_end_of_line
|
||||||
|
buffer '\n'
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
set_color() {
|
||||||
|
local color="$1"
|
||||||
|
local weight=22
|
||||||
|
|
||||||
|
if [[ "${2:-}" == 'bold' ]]; then
|
||||||
|
weight=1
|
||||||
|
fi
|
||||||
|
buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_color() {
|
||||||
|
buffer '\x1B[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer=
|
||||||
|
|
||||||
|
buffer() {
|
||||||
|
local content
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf -v content -- "$@"
|
||||||
|
_buffer+="$content"
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix_buffer_with() {
|
||||||
|
local old_buffer="$_buffer"
|
||||||
|
_buffer=''
|
||||||
|
"$@"
|
||||||
|
_buffer="$_buffer$old_buffer"
|
||||||
|
}
|
||||||
|
|
||||||
|
flush() {
|
||||||
|
printf '%s' "$_buffer"
|
||||||
|
_buffer=
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
flush
|
||||||
|
printf '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
trap finish EXIT
|
||||||
|
trap '' INT
|
||||||
|
|
||||||
|
bats_tap_stream_plan() {
|
||||||
|
count="$1"
|
||||||
|
index=0
|
||||||
|
passed=0
|
||||||
|
failures=0
|
||||||
|
skipped=0
|
||||||
|
name=
|
||||||
|
update_count_column_width
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_begin() {
|
||||||
|
index="$1"
|
||||||
|
name="$2"
|
||||||
|
begin
|
||||||
|
flush
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() {
|
||||||
|
local duration=
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
duration="$2"
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
index="$1"
|
||||||
|
name="$2"
|
||||||
|
((++passed))
|
||||||
|
|
||||||
|
pass "$duration"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() {
|
||||||
|
index="$1"
|
||||||
|
name="$2"
|
||||||
|
((++skipped))
|
||||||
|
skip "$3"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() {
|
||||||
|
local duration=
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
duration="$2"
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
index="$1"
|
||||||
|
name="$2"
|
||||||
|
((++failures))
|
||||||
|
|
||||||
|
fail "$duration"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() { # <comment> <scope>
|
||||||
|
local scope=$2
|
||||||
|
# count the lines we printed after the begin text,
|
||||||
|
if [[ $line_backoff_count -eq 0 && $scope == begin ]]; then
|
||||||
|
# if this is the first line after begin, go down one line
|
||||||
|
buffer "\n"
|
||||||
|
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( ++line_backoff_count ))
|
||||||
|
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
|
||||||
|
log "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() {
|
||||||
|
#test_file="$1"
|
||||||
|
line_backoff_count=0
|
||||||
|
index=
|
||||||
|
# indicate filename for failures
|
||||||
|
local file_name="${1/$BASE_PATH}"
|
||||||
|
name="File $file_name"
|
||||||
|
set_color 4 bold
|
||||||
|
buffer "%s\n" "$file_name"
|
||||||
|
clear_color
|
||||||
|
}
|
||||||
|
|
||||||
|
line_backoff_count=0
|
||||||
|
bats_tap_stream_unknown() { # <full line> <scope>
|
||||||
|
local scope=$2
|
||||||
|
# count the lines we printed after the begin text, (or after suite, in case of syntax errors)
|
||||||
|
if [[ $line_backoff_count -eq 0 && ( $scope == begin || $scope == suite )]]; then
|
||||||
|
# if this is the first line after begin, go down one line
|
||||||
|
buffer "\n"
|
||||||
|
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( ++line_backoff_count ))
|
||||||
|
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
|
||||||
|
buffer "%s\n" "$1"
|
||||||
|
flush
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
||||||
|
|
||||||
|
summary
|
52
portable/test/bats/libexec/bats-core/bats-format-tap
Executable file
52
portable/test/bats/libexec/bats-core/bats-format-tap
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
trap '' INT
|
||||||
|
|
||||||
|
# shellcheck source=lib/bats-core/formatter.bash
|
||||||
|
source "$BATS_ROOT/lib/bats-core/formatter.bash"
|
||||||
|
|
||||||
|
bats_tap_stream_plan() {
|
||||||
|
printf "1..%d\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_begin() { #<test index> <test name>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
printf "ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||||
|
else
|
||||||
|
printf "ok %d %s\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
|
||||||
|
if [[ "$1" == "--duration" ]]; then
|
||||||
|
printf "not ok %d %s # in %d ms\n" "$3" "$4" "$2"
|
||||||
|
else
|
||||||
|
printf "not ok %d %s\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_skipped() { # <test index> <test name> <reason>
|
||||||
|
if [[ -n "$3" ]]; then
|
||||||
|
printf "ok %d %s # skip %s\n" "$1" "$2" "$3"
|
||||||
|
else
|
||||||
|
printf "ok %d %s # skip\n" "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_comment() { # <comment text without leading '# '>
|
||||||
|
printf "# %s\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_suite() { # <file name>
|
||||||
|
:
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_tap_stream_unknown() { # <full line>
|
||||||
|
printf "%s\n" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
bats_parse_internal_extended_tap
|
91
portable/test/bats/libexec/bats-core/bats-format-tap13
Executable file
91
portable/test/bats/libexec/bats-core/bats-format-tap13
Executable file
@ -0,0 +1,91 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
while [[ "$#" -ne 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-T)
|
||||||
|
BATS_ENABLE_TIMING="-T"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
header_pattern='[0-9]+\.\.[0-9]+'
|
||||||
|
IFS= read -r header
|
||||||
|
|
||||||
|
if [[ "$header" =~ $header_pattern ]]; then
|
||||||
|
printf "TAP version 13\n"
|
||||||
|
printf "%s\n" "$header"
|
||||||
|
else
|
||||||
|
# If the first line isn't a TAP plan, print it and pass the rest through
|
||||||
|
printf '%s\n' "$header"
|
||||||
|
exec cat
|
||||||
|
fi
|
||||||
|
|
||||||
|
yaml_block_open=''
|
||||||
|
add_yaml_entry() {
|
||||||
|
if [[ -z "$yaml_block_open" ]]; then
|
||||||
|
printf " ---\n"
|
||||||
|
fi
|
||||||
|
printf " %s: %s\n" "$1" "$2"
|
||||||
|
yaml_block_open=1
|
||||||
|
}
|
||||||
|
|
||||||
|
close_previous_yaml_block() {
|
||||||
|
if [[ -n "$yaml_block_open" ]]; then
|
||||||
|
printf " ...\n"
|
||||||
|
yaml_block_open=''
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap '' INT
|
||||||
|
|
||||||
|
number_of_printed_log_lines_for_this_test_so_far=0
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
'begin '*) ;;
|
||||||
|
'ok '*)
|
||||||
|
close_previous_yaml_block
|
||||||
|
number_of_printed_log_lines_for_this_test_so_far=0
|
||||||
|
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
|
||||||
|
timing_expr="(ok [0-9]+ .+) in ([0-9]+)ms$"
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
printf "%s\n" "${BASH_REMATCH[1]}"
|
||||||
|
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||||
|
else
|
||||||
|
echo "Could not match output line to timing regex: $line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s\n" "${line}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'not ok '*)
|
||||||
|
close_previous_yaml_block
|
||||||
|
number_of_printed_log_lines_for_this_test_so_far=0
|
||||||
|
timing_expr="(not ok [0-9]+ .+) in ([0-9])+ms$"
|
||||||
|
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
|
||||||
|
if [[ "$line" =~ $timing_expr ]]; then
|
||||||
|
printf "%s\n" "${BASH_REMATCH[1]}"
|
||||||
|
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
|
||||||
|
else
|
||||||
|
echo "Could not match failure line to timing regex: $line" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s\n" "${line}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
'# '*)
|
||||||
|
if [[ $number_of_printed_log_lines_for_this_test_so_far -eq 0 ]]; then
|
||||||
|
add_yaml_entry "message" "|" # use a multiline string for this entry
|
||||||
|
fi
|
||||||
|
((++number_of_printed_log_lines_for_this_test_so_far))
|
||||||
|
printf " %s\n" "${line:2}"
|
||||||
|
;;
|
||||||
|
'suite '*) ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# close the final block if there was one
|
||||||
|
close_previous_yaml_block
|
60
portable/test/bats/libexec/bats-core/bats-preprocess
Executable file
60
portable/test/bats/libexec/bats-core/bats-preprocess
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
bats_encode_test_name() {
|
||||||
|
local name="$1"
|
||||||
|
local result='test_'
|
||||||
|
local hex_code
|
||||||
|
|
||||||
|
if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
|
||||||
|
name="${name//_/-5f}"
|
||||||
|
name="${name//-/-2d}"
|
||||||
|
name="${name// /_}"
|
||||||
|
result+="$name"
|
||||||
|
else
|
||||||
|
local length="${#name}"
|
||||||
|
local char i
|
||||||
|
|
||||||
|
for ((i = 0; i < length; i++)); do
|
||||||
|
char="${name:$i:1}"
|
||||||
|
if [[ "$char" == ' ' ]]; then
|
||||||
|
result+='_'
|
||||||
|
elif [[ "$char" =~ [[:alnum:]] ]]; then
|
||||||
|
result+="$char"
|
||||||
|
else
|
||||||
|
printf -v 'hex_code' -- '-%02x' \'"$char"
|
||||||
|
result+="$hex_code"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf -v "$2" '%s' "$result"
|
||||||
|
}
|
||||||
|
|
||||||
|
BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
|
||||||
|
BATS_TEST_PATTERN_COMMENT="[[:blank:]]*([^[:blank:]()]+)[[:blank:]]*\(?\)?[[:blank:]]+\{[[:blank:]]+#[[:blank:]]*@test[[:blank:]]*\$"
|
||||||
|
|
||||||
|
test_file="$1"
|
||||||
|
tests=()
|
||||||
|
{
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line//$'\r'/}"
|
||||||
|
if [[ "$line" =~ $BATS_TEST_PATTERN ]] || [[ "$line" =~ $BATS_TEST_PATTERN_COMMENT ]]; then
|
||||||
|
name="${BASH_REMATCH[1]#[\'\"]}"
|
||||||
|
name="${name%[\'\"]}"
|
||||||
|
body="${BASH_REMATCH[2]:-}"
|
||||||
|
bats_encode_test_name "$name" 'encoded_name'
|
||||||
|
printf '%s() { bats_test_begin "%s"; %s\n' "${encoded_name:?}" "$name" "$body" || :
|
||||||
|
|
||||||
|
if [[ -z "$BATS_TEST_FILTER" || "$name" =~ $BATS_TEST_FILTER ]]; then
|
||||||
|
tests+=("$encoded_name")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf '%s\n' "$line"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
} <<<"$(<"$test_file")"$'\n'
|
||||||
|
|
||||||
|
for test_name in "${tests[@]}"; do
|
||||||
|
printf 'bats_test_function %s\n' "$test_name"
|
||||||
|
done
|
197
portable/test/bats_setup
Normal file
197
portable/test/bats_setup
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# setup paths for BATS test units
|
||||||
|
setup() {
|
||||||
|
[ ! -f ${BATS_PARENT_TMPNAME}.skip ] || skip "skip remaining tests"
|
||||||
|
bats_require_minimum_version 1.5.0
|
||||||
|
R="$BATS_TEST_DIRNAME"
|
||||||
|
# R=`cd "$T"/.. && pwd`
|
||||||
|
TMP="$BATS_FILE_TMPDIR"
|
||||||
|
load test_helper/bats-support/load
|
||||||
|
load test_helper/bats-assert/load
|
||||||
|
load test_helper/bats-file/load
|
||||||
|
ZTMP="$BATS_FILE_TMPDIR"
|
||||||
|
if [ "$ZENROOM_EXECUTABLE" == "" ]; then
|
||||||
|
ZENROOM_EXECUTABLE="/usr/local/bin/zenroom"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PATH="$PATH:/usr/local/bin:/usr/local/sbin"
|
||||||
|
MNT="/media/`basename $BATS_TEST_FILENAME`"
|
||||||
|
PIM=42
|
||||||
|
# max password stdin size to veracrypt is 128 bytes
|
||||||
|
PW="91cd69b2caab05cb64f8e1c8ae22a7c5a25564e7fc5116d0303dce1ffd26861ea0483c25bdb85adb216718c19815eb59ac14ec9783bfbbb57786ca7d9038c845"
|
||||||
|
# `openssl rand -hex 64`"
|
||||||
|
FS='ext4'
|
||||||
|
SIZE='20M'
|
||||||
|
[ "$TOMB" = "" ] && TOMB=test.tomb
|
||||||
|
|
||||||
|
# default on freebsd is 1001
|
||||||
|
user_uid=1000
|
||||||
|
user_gid=1000
|
||||||
|
|
||||||
|
system="`uname -s`"
|
||||||
|
f_create=""
|
||||||
|
f_format=""
|
||||||
|
f_map=""
|
||||||
|
f_mount=""
|
||||||
|
f_close=""
|
||||||
|
|
||||||
|
case "$system" in
|
||||||
|
FreeBSD)
|
||||||
|
f_create=portable_create
|
||||||
|
f_format=freebsd_format
|
||||||
|
f_map=portable_map
|
||||||
|
f_mount=freebsd_mount
|
||||||
|
f_close=freebsd_close
|
||||||
|
user_uid=1001
|
||||||
|
user_gid=1001
|
||||||
|
;;
|
||||||
|
Linux)
|
||||||
|
f_create=portable_create
|
||||||
|
f_format=linux_format
|
||||||
|
f_map=portable_map
|
||||||
|
f_mount=linux_mount
|
||||||
|
f_close=portable_close
|
||||||
|
user_uid=1000
|
||||||
|
user_gid=1000
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
>&2 echo "Unsupported system: $system"
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# cd $ZTMP
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
[ -n "$BATS_TEST_COMPLETED" ] || touch ${BATS_PARENT_TMPNAME}.skip
|
||||||
|
>&3 echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# usage: echo PASSWORD | portable_create file size pim
|
||||||
|
portable_create() {
|
||||||
|
local file="$1" # must not exist
|
||||||
|
local size="$2" # veracrypt format (accepts M or G)
|
||||||
|
local pim="$3" # any number
|
||||||
|
# >&2 echo "portable_create $file $size $pim"
|
||||||
|
veracrypt --non-interactive --text --stdin \
|
||||||
|
-m nokernelcrypto \
|
||||||
|
-c ${file} --volume-type normal \
|
||||||
|
--hash sha512 --encryption serpent-aes \
|
||||||
|
--filesystem none --size ${size} --pim ${pim} \
|
||||||
|
--random-source /dev/urandom -k ''
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage: echo PASSWORD | portable_map file pim
|
||||||
|
portable_map() {
|
||||||
|
local file="$1"
|
||||||
|
local pim="$2"
|
||||||
|
# >&2 echo "portable_map $file $pim"
|
||||||
|
veracrypt --non-interactive --text --stdin \
|
||||||
|
--protect-hidden no -m nokernelcrypto \
|
||||||
|
-k '' --pim ${pim} --filesystem none \
|
||||||
|
${file}
|
||||||
|
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
portable_close() {
|
||||||
|
local file="$1"
|
||||||
|
veracrypt -d ${file}
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
linux_format() {
|
||||||
|
local file="$1"
|
||||||
|
# loop="`losetup -j ${vera}/volume | cut -d: -f1`"
|
||||||
|
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
|
||||||
|
# losetup -l
|
||||||
|
>&2 echo "veramap format: ${loop}"
|
||||||
|
sync
|
||||||
|
sudo mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "$loop"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage: _mount /tmp/.veracrypt_ mountpoint
|
||||||
|
linux_mount() {
|
||||||
|
local file="$1"
|
||||||
|
local mnt="$2"
|
||||||
|
# local loop="`losetup -j ${vera}/volume | cut -d: -f1`"
|
||||||
|
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
>&3 echo "fsck $loop"
|
||||||
|
fsck.ext4 -p -C0 "$loop"
|
||||||
|
sudo mount ${loop} "$mnt"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
freebsd_format() {
|
||||||
|
file="$1"
|
||||||
|
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
|
||||||
|
mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "${loop}"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
freebsd_mount() {
|
||||||
|
local file="$1"
|
||||||
|
local mnt="$2"
|
||||||
|
>&2 echo `veracrypt -l "$file"`
|
||||||
|
local loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
>&3 echo "fsck $loop"
|
||||||
|
fsck.ext4 -p -C0 "$loop"
|
||||||
|
lklfuse -o type=ext4 "${loop}" "$mnt"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
freebsd_close() {
|
||||||
|
local file="$1"
|
||||||
|
local md=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
# umount "$mnt"
|
||||||
|
>&2 echo "md: $md"
|
||||||
|
local mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
|
||||||
|
>&2 echo "mnt: $mnt"
|
||||||
|
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
|
||||||
|
>&3 echo "kill $lkl (lklfuse on $md)"
|
||||||
|
# trying to deail with lklfuse bug
|
||||||
|
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
|
||||||
|
renice -20 $lkl
|
||||||
|
kill $lkl
|
||||||
|
sync
|
||||||
|
sleep 1
|
||||||
|
kill -9 $lkl
|
||||||
|
# lkl should have really exited now
|
||||||
|
>&3 echo "veracrypt -d $file"
|
||||||
|
veracrypt -d "$file"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
Z() {
|
||||||
|
tmptmp=0
|
||||||
|
if ! [ "$TMP" ]; then
|
||||||
|
TMP=`mktemp -d`; tmptmp=1
|
||||||
|
fi
|
||||||
|
script=$1
|
||||||
|
if [ "$script" == "-" ]; then
|
||||||
|
cat > $TMP/script_stdin
|
||||||
|
script=$TMP/script_stdin
|
||||||
|
else
|
||||||
|
if ! [ -r "$script" ]; then
|
||||||
|
script=$T/$script
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if ! [ -r $script ]; then
|
||||||
|
>&2 echo "Error - script not found: $script"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
shift 1
|
||||||
|
if [ "${1##*.}" == "zen" ]; then
|
||||||
|
$ZENROOM_EXECUTABLE $@ -z $script
|
||||||
|
else
|
||||||
|
$ZENROOM_EXECUTABLE $@ $script
|
||||||
|
fi
|
||||||
|
if [ $tmptmp = 1 ]; then
|
||||||
|
rm -f $TMP/*
|
||||||
|
rmdir $TMP
|
||||||
|
fi
|
||||||
|
}
|
26
portable/test/check-random-data.bats
Normal file
26
portable/test/check-random-data.bats
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
load bats_setup
|
||||||
|
|
||||||
|
@test "Find the tomb" {
|
||||||
|
assert_file_exists "$TOMB"
|
||||||
|
>&3 echo "Found: $TOMB"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Open tomb with key" {
|
||||||
|
mkdir -p "$TOMB".mnt
|
||||||
|
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Check integrity of random data" {
|
||||||
|
sha512sum "$TOMB".mnt/random.data | awk '{print $1}'
|
||||||
|
cat "$TOMB".hash
|
||||||
|
>&2 echo $newhash
|
||||||
|
>&2 echo $oldhash
|
||||||
|
assert_equal "$newhash" "$oldhash"
|
||||||
|
if [ -r "$TOMB".uname ]; then
|
||||||
|
>&3 cat "$TOMB".uname
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Close tomb" {
|
||||||
|
./tomb close "$TOMB"
|
||||||
|
}
|
79
portable/test/create-fillrandom.bats
Normal file
79
portable/test/create-fillrandom.bats
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
load bats_setup
|
||||||
|
|
||||||
|
# f_create=""
|
||||||
|
# f_format=""
|
||||||
|
# f_map=""
|
||||||
|
# f_mount=""
|
||||||
|
# f_close=""
|
||||||
|
|
||||||
|
@test "Create a new tomb" {
|
||||||
|
# >&3 echo $PW
|
||||||
|
>&3 echo $TOMB
|
||||||
|
echo -n "$PW" | $f_create "$TOMB" 20M ${PIM}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Map the tomb" {
|
||||||
|
echo -n "$PW" | $f_map "$TOMB" ${PIM}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Format the tomb" {
|
||||||
|
$f_format "/tmp/.veracrypt_aux_mnt1" || {
|
||||||
|
$f_close "$TOMB"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Mount the tomb" {
|
||||||
|
mkdir -p "$MNT"
|
||||||
|
$f_mount "$TOMB" "$MNT" || {
|
||||||
|
$f_close "$TOMB"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Create random.data inside the tomb" {
|
||||||
|
dd if=/dev/urandom of="$MNT"/random.data bs=1024 count=10000 || {
|
||||||
|
# sudo umount testmnt
|
||||||
|
$f_close "$TOMB"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
>&3 ls -l "$MNT"/random.data
|
||||||
|
sha512sum "$MNT"/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
|
||||||
|
uname -a > "$TOMB".uname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Close the test.tomb" {
|
||||||
|
>&3 veracrypt -l
|
||||||
|
# sudo umount "$TMP"/testmnt
|
||||||
|
$f_close "$TOMB"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "Re-Map the test.tomb" {
|
||||||
|
echo -n "$PW" | $f_map "$TOMB" ${PIM}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Re-Mount the test.tomb" {
|
||||||
|
mkdir -p "$MNT"
|
||||||
|
$f_mount "$TOMB" "$MNT" || {
|
||||||
|
$f_close "$TOMB"
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Re-Check integrity of random data" {
|
||||||
|
newhash=`sha512sum "$MNT"/random.data | awk '{print $1}'`
|
||||||
|
oldhash=`cat "$TOMB".hash`
|
||||||
|
>&2 echo $newhash
|
||||||
|
>&2 echo $oldhash
|
||||||
|
assert_equal "$newhash" "$oldhash"
|
||||||
|
if [ -r "$TOMB".uname ]; then
|
||||||
|
>&3 cat "$TOMB".uname
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "Re-Close the test.tomb" {
|
||||||
|
>&3 veracrypt -l
|
||||||
|
$f_close "$TOMB"
|
||||||
|
}
|
51
portable/test/create_open_close.bats
Normal file
51
portable/test/create_open_close.bats
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
load bats_setup
|
||||||
|
|
||||||
|
@test "Dig tomb" {
|
||||||
|
rm -f "$TOMB"
|
||||||
|
>&3 echo "$TOMB"
|
||||||
|
./tomb dig -s 20MiB "$TOMB"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Forge key" {
|
||||||
|
rm -f "$TOMB".key
|
||||||
|
./tomb forge "$TOMB".key
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Lock tomb with key" {
|
||||||
|
./tomb lock -k "$TOMB".key "$TOMB"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Open tomb with key" {
|
||||||
|
mkdir -p "$TOMB".mnt
|
||||||
|
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Create random.data inside the tomb" {
|
||||||
|
dd if=/dev/urandom of="$TOMB".mnt/random.data bs=1024 count=10000
|
||||||
|
>&3 ls -l "$TOMB".mnt/random.data
|
||||||
|
sha512sum "$TOMB".mnt/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
|
||||||
|
uname -a > "$TOMB".uname
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Close tomb" {
|
||||||
|
./tomb close "$TOMB"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Re-open tomb with key" {
|
||||||
|
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Check integrity of random data" {
|
||||||
|
newhash=`sha512sum "$TOMB".mnt/random.data | awk '{print $1}'`
|
||||||
|
oldhash=`cat "$TOMB".hash`
|
||||||
|
>&2 echo $newhash
|
||||||
|
>&2 echo $oldhash
|
||||||
|
assert_equal "$newhash" "$oldhash"
|
||||||
|
if [ -r "$TOMB".uname ]; then
|
||||||
|
>&3 cat "$TOMB".uname
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "Close the tomb again" {
|
||||||
|
./tomb close "$TOMB"
|
||||||
|
}
|
33
portable/test/test_helper/bats-assert/load.bash
Normal file
33
portable/test/test_helper/bats-assert/load.bash
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# bats-assert - Common assertions for Bats
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
# Assertions are functions that perform a test and output relevant
|
||||||
|
# information on failure to help debugging. They return 1 on failure
|
||||||
|
# and 0 otherwise.
|
||||||
|
#
|
||||||
|
# All output is formatted for readability using the functions of
|
||||||
|
# `output.bash' and sent to the standard error.
|
||||||
|
|
||||||
|
# shellcheck disable=1090
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash"
|
42
portable/test/test_helper/bats-assert/src/assert.bash
Normal file
42
portable/test/test_helper/bats-assert/src/assert.bash
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# assert
|
||||||
|
# ======
|
||||||
|
#
|
||||||
|
# Summary: Fail if the given expression evaluates to false.
|
||||||
|
#
|
||||||
|
# Usage: assert <expression>
|
||||||
|
|
||||||
|
# Options:
|
||||||
|
# <expression> The expression to evaluate for truthiness.
|
||||||
|
# *__Note:__ The expression must be a simple command.
|
||||||
|
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
|
||||||
|
# such as `[[`, can be used only when executed with `bash -c`.*
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - the failed expression, on failure
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Returns:
|
||||||
|
# 0 - if expression evaluates to true
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert()' {
|
||||||
|
# touch '/var/log/test.log'
|
||||||
|
# assert [ -e '/var/log/test.log' ]
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the failed expression is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- assertion failed --
|
||||||
|
# expression : [ -e /var/log/test.log ]
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert() {
|
||||||
|
if ! "$@"; then
|
||||||
|
batslib_print_kv_single 10 'expression' "$*" \
|
||||||
|
| batslib_decorate 'assertion failed' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
42
portable/test/test_helper/bats-assert/src/assert_equal.bash
Normal file
42
portable/test/test_helper/bats-assert/src/assert_equal.bash
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# assert_equal
|
||||||
|
# ============
|
||||||
|
#
|
||||||
|
# Summary: Fail if the actual and expected values are not equal.
|
||||||
|
#
|
||||||
|
# Usage: assert_equal <actual> <expected>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# <actual> The value being compared.
|
||||||
|
# <expected> The value to compare against.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_equal()' {
|
||||||
|
# assert_equal 'have' 'want'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - expected and actual values, on failure
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Returns:
|
||||||
|
# 0 - if values equal
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# On failure, the expected and actual values are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- values do not equal --
|
||||||
|
# expected : want
|
||||||
|
# actual : have
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert_equal() {
|
||||||
|
if [[ $1 != "$2" ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 8 \
|
||||||
|
'expected' "$2" \
|
||||||
|
'actual' "$1" \
|
||||||
|
| batslib_decorate 'values do not equal' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
# assert_failure
|
||||||
|
# ==============
|
||||||
|
#
|
||||||
|
# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status.
|
||||||
|
#
|
||||||
|
# Usage: assert_failure [<expected_status>]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# <expected_status> The specific status code to check against.
|
||||||
|
# If not provided, simply asserts status is != 0.
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - `$output`, on failure;
|
||||||
|
# - also, `$status` and `expected_status`, if provided
|
||||||
|
# Globals:
|
||||||
|
# status
|
||||||
|
# output
|
||||||
|
# Returns:
|
||||||
|
# 0 - if `$status' is 0,
|
||||||
|
# or if expected_status is provided but does not equal `$status'
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_failure() status only' {
|
||||||
|
# run echo 'Success!'
|
||||||
|
# assert_failure
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, `$output` is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- command succeeded, but it was expected to fail --
|
||||||
|
# output : Success!
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Expected status
|
||||||
|
#
|
||||||
|
# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_failure() with expected status' {
|
||||||
|
# run bash -c "echo 'Error!'; exit 1"
|
||||||
|
# assert_failure 2
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, both the expected and actual statuses, and `$output` are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- command failed as expected, but status differs --
|
||||||
|
# expected : 2
|
||||||
|
# actual : 1
|
||||||
|
# output : Error!
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert_failure() {
|
||||||
|
: "${output?}"
|
||||||
|
: "${status?}"
|
||||||
|
|
||||||
|
(( $# > 0 )) && local -r expected="$1"
|
||||||
|
if (( status == 0 )); then
|
||||||
|
batslib_print_kv_single_or_multi 6 'output' "$output" \
|
||||||
|
| batslib_decorate 'command succeeded, but it was expected to fail' \
|
||||||
|
| fail
|
||||||
|
elif (( $# > 0 )) && (( status != expected )); then
|
||||||
|
{ local -ir width=8
|
||||||
|
batslib_print_kv_single "$width" \
|
||||||
|
'expected' "$expected" \
|
||||||
|
'actual' "$status"
|
||||||
|
batslib_print_kv_single_or_multi "$width" \
|
||||||
|
'output' "$output"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'command failed as expected, but status differs' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
248
portable/test/test_helper/bats-assert/src/assert_line.bash
Normal file
248
portable/test/test_helper/bats-assert/src/assert_line.bash
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
# assert_line
|
||||||
|
# ===========
|
||||||
|
#
|
||||||
|
# Summary: Fail if the expected line is not found in the output (default) or at a specific line number.
|
||||||
|
#
|
||||||
|
# Usage: assert_line [-n index] [-p | -e] [--] <expected>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -n, --index <idx> Match the <idx>th line
|
||||||
|
# -p, --partial Match if `expected` is a substring of `$output` or line <idx>
|
||||||
|
# -e, --regexp Treat `expected` as an extended regular expression
|
||||||
|
# <expected> The expected line string, substring, or regular expression
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - details, on failure
|
||||||
|
# error message, on error
|
||||||
|
# Globals:
|
||||||
|
# output
|
||||||
|
# lines
|
||||||
|
# Returns:
|
||||||
|
# 0 - if matching line found
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# Similarly to `assert_output`, this function verifies that a command or function produces the expected output.
|
||||||
|
# (It is the logical complement of `refute_line`.)
|
||||||
|
# It checks that the expected line appears in the output (default) or at a specific line number.
|
||||||
|
# Matching can be literal (default), partial or regular expression.
|
||||||
|
#
|
||||||
|
# *__Warning:__
|
||||||
|
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
|
||||||
|
# causing line indices to change and preventing testing for empty lines.*
|
||||||
|
#
|
||||||
|
# [bats-93]: https://github.com/sstephenson/bats/pull/93
|
||||||
|
#
|
||||||
|
# ## Looking for a line in the output
|
||||||
|
#
|
||||||
|
# By default, the entire output is searched for the expected line.
|
||||||
|
# The assertion fails if the expected line is not found in `${lines[@]}`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_line() looking for line' {
|
||||||
|
# run echo $'have-0\nhave-1\nhave-2'
|
||||||
|
# assert_line 'want'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the expected line and the output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- output does not contain line --
|
||||||
|
# line : want
|
||||||
|
# output (3 lines):
|
||||||
|
# have-0
|
||||||
|
# have-1
|
||||||
|
# have-2
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Matching a specific line
|
||||||
|
#
|
||||||
|
# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
|
||||||
|
# The assertion fails if the expected line does not equal `${lines[<idx>]}`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_line() specific line' {
|
||||||
|
# run echo $'have-0\nhave-1\nhave-2'
|
||||||
|
# assert_line --index 1 'want-1'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the index and the compared lines are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- line differs --
|
||||||
|
# index : 1
|
||||||
|
# expected : want-1
|
||||||
|
# actual : have-1
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Partial matching
|
||||||
|
#
|
||||||
|
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||||
|
# When used, a match fails if the expected *substring* is not found in the matched line.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_line() partial matching' {
|
||||||
|
# run echo $'have 1\nhave 2\nhave 3'
|
||||||
|
# assert_line --partial 'want'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- no output line contains substring --
|
||||||
|
# substring : want
|
||||||
|
# output (3 lines):
|
||||||
|
# have 1
|
||||||
|
# have 2
|
||||||
|
# have 3
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Regular expression matching
|
||||||
|
#
|
||||||
|
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||||
|
# When used, a match fails if the *extended regular expression* does not match the line being tested.
|
||||||
|
#
|
||||||
|
# *__Note__:
|
||||||
|
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_line() regular expression matching' {
|
||||||
|
# run echo $'have-0\nhave-1\nhave-2'
|
||||||
|
# assert_line --index 1 --regexp '^want-[0-9]$'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- regular expression does not match line --
|
||||||
|
# index : 1
|
||||||
|
# regexp : ^want-[0-9]$
|
||||||
|
# line : have-1
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
|
||||||
|
assert_line() {
|
||||||
|
local -i is_match_line=0
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
: "${lines?}"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
# assert_not_equal
|
||||||
|
# ============
|
||||||
|
#
|
||||||
|
# Summary: Fail if the actual and unexpected values are equal.
|
||||||
|
#
|
||||||
|
# Usage: assert_not_equal <actual> <unexpected>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# <actual> The value being compared.
|
||||||
|
# <unexpected> The value to compare against.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_not_equal()' {
|
||||||
|
# assert_not_equal 'foo' 'foo'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - expected and actual values, on failure
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Returns:
|
||||||
|
# 0 - if actual does not equal unexpected
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# On failure, the unexpected and actual values are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- values should not be equal --
|
||||||
|
# unexpected : foo
|
||||||
|
# actual : foo
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert_not_equal() {
|
||||||
|
if [[ "$1" == "$2" ]]; then
|
||||||
|
batslib_print_kv_single_or_multi 10 \
|
||||||
|
'unexpected' "$2" \
|
||||||
|
'actual' "$1" \
|
||||||
|
| batslib_decorate 'values should not be equal' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
197
portable/test/test_helper/bats-assert/src/assert_output.bash
Normal file
197
portable/test/test_helper/bats-assert/src/assert_output.bash
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# assert_output
|
||||||
|
# =============
|
||||||
|
#
|
||||||
|
# Summary: Fail if `$output' does not match the expected output.
|
||||||
|
#
|
||||||
|
# Usage: assert_output [-p | -e] [- | [--] <expected>]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -p, --partial Match if `expected` is a substring of `$output`
|
||||||
|
# -e, --regexp Treat `expected` as an extended regular expression
|
||||||
|
# -, --stdin Read `expected` value from STDIN
|
||||||
|
# <expected> The expected value, substring or regular expression
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDIN - [=$1] expected output
|
||||||
|
# STDERR - details, on failure
|
||||||
|
# error message, on error
|
||||||
|
# Globals:
|
||||||
|
# output
|
||||||
|
# Returns:
|
||||||
|
# 0 - if output matches the expected value/partial/regexp
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# This function verifies that a command or function produces the expected output.
|
||||||
|
# (It is the logical complement of `refute_output`.)
|
||||||
|
# Output matching can be literal (the default), partial or by regular expression.
|
||||||
|
# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
|
||||||
|
#
|
||||||
|
# ## Literal matching
|
||||||
|
#
|
||||||
|
# By default, literal matching is performed.
|
||||||
|
# The assertion fails if `$output` does not equal the expected output.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_output()' {
|
||||||
|
# run echo 'have'
|
||||||
|
# assert_output 'want'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# @test 'assert_output() with pipe' {
|
||||||
|
# run echo 'hello'
|
||||||
|
# echo 'hello' | assert_output -
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# @test 'assert_output() with herestring' {
|
||||||
|
# run echo 'hello'
|
||||||
|
# assert_output - <<< hello
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the expected and actual output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- output differs --
|
||||||
|
# expected : want
|
||||||
|
# actual : have
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Existence
|
||||||
|
#
|
||||||
|
# To assert that any output exists at all, omit the `expected` argument.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_output()' {
|
||||||
|
# run echo 'have'
|
||||||
|
# assert_output
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, an error message is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- no output --
|
||||||
|
# expected non-empty output, but output was empty
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Partial matching
|
||||||
|
#
|
||||||
|
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||||
|
# When used, the assertion fails if the expected _substring_ is not found in `$output`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_output() partial matching' {
|
||||||
|
# run echo 'ERROR: no such file or directory'
|
||||||
|
# assert_output --partial 'SUCCESS'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the substring and the output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- output does not contain substring --
|
||||||
|
# substring : SUCCESS
|
||||||
|
# output : ERROR: no such file or directory
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Regular expression matching
|
||||||
|
#
|
||||||
|
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||||
|
# When used, the assertion fails if the *extended regular expression* does not match `$output`.
|
||||||
|
#
|
||||||
|
# *__Note__:
|
||||||
|
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
|
||||||
|
# not individual lines.*
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_output() regular expression matching' {
|
||||||
|
# run echo 'Foobar 0.1.0'
|
||||||
|
# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the regular expression and the output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- regular expression does not match output --
|
||||||
|
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||||
|
# output : Foobar 0.1.0
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert_output() {
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
local -i is_mode_nonempty=0
|
||||||
|
local -i use_stdin=0
|
||||||
|
: "${output?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
is_mode_nonempty=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
-|--stdin) use_stdin=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: assert_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local expected
|
||||||
|
if (( use_stdin )); then
|
||||||
|
expected="$(cat -)"
|
||||||
|
else
|
||||||
|
expected="${1-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_mode_nonempty )); then
|
||||||
|
if [ -z "$output" ]; then
|
||||||
|
echo 'expected non-empty output, but output was empty' \
|
||||||
|
| batslib_decorate 'no output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_regexp )); then
|
||||||
|
if [[ '' =~ $expected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$expected'" \
|
||||||
|
| batslib_decorate 'ERROR: assert_output' \
|
||||||
|
| fail
|
||||||
|
elif ! [[ $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
|
||||||
|
}
|
56
portable/test/test_helper/bats-assert/src/assert_regex.bash
Normal file
56
portable/test/test_helper/bats-assert/src/assert_regex.bash
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# `assert_regex`
|
||||||
|
#
|
||||||
|
# This function is similar to `assert_equal` but uses pattern matching instead
|
||||||
|
# of equality, by wrapping `[[ value =~ pattern ]]`.
|
||||||
|
#
|
||||||
|
# Fail if the value (first parameter) does not match the pattern (second
|
||||||
|
# parameter).
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_regex()' {
|
||||||
|
# assert_regex 'what' 'x$'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the value and the pattern are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- values does not match regular expression --
|
||||||
|
# value : what
|
||||||
|
# pattern : x$
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# If the value is longer than one line then it is displayed in *multi-line*
|
||||||
|
# format.
|
||||||
|
#
|
||||||
|
# An error is displayed if the specified extended regular expression is invalid.
|
||||||
|
#
|
||||||
|
# For description of the matching behavior, refer to the documentation of the
|
||||||
|
# `=~` operator in the
|
||||||
|
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
|
||||||
|
# Note that the `BASH_REMATCH` array is available immediately after the
|
||||||
|
# assertion succeeds but is fragile, i.e. prone to being overwritten as a side
|
||||||
|
# effect of other actions.
|
||||||
|
assert_regex() {
|
||||||
|
local -r value="${1}"
|
||||||
|
local -r pattern="${2}"
|
||||||
|
|
||||||
|
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`${pattern}'" \
|
||||||
|
| batslib_decorate 'ERROR: assert_regex' \
|
||||||
|
| fail
|
||||||
|
elif ! [[ "${value}" =~ ${pattern} ]]; then
|
||||||
|
if shopt -p nocasematch &>/dev/null; then
|
||||||
|
local case_sensitive=insensitive
|
||||||
|
else
|
||||||
|
local case_sensitive=sensitive
|
||||||
|
fi
|
||||||
|
batslib_print_kv_single_or_multi 8 \
|
||||||
|
'value' "${value}" \
|
||||||
|
'pattern' "${pattern}" \
|
||||||
|
'case' "${case_sensitive}" \
|
||||||
|
| batslib_decorate 'value does not match regular expression' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
# assert_success
|
||||||
|
# ==============
|
||||||
|
#
|
||||||
|
# Summary: Fail if `$status` is not 0.
|
||||||
|
#
|
||||||
|
# Usage: assert_success
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - `$status` and `$output`, on failure
|
||||||
|
# Globals:
|
||||||
|
# status
|
||||||
|
# output
|
||||||
|
# Returns:
|
||||||
|
# 0 - if `$status' is 0
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'assert_success() status only' {
|
||||||
|
# run bash -c "echo 'Error!'; exit 1"
|
||||||
|
# assert_success
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, `$status` and `$output` are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- command failed --
|
||||||
|
# status : 1
|
||||||
|
# output : Error!
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
assert_success() {
|
||||||
|
: "${output?}"
|
||||||
|
: "${status?}"
|
||||||
|
|
||||||
|
if (( status != 0 )); then
|
||||||
|
{ local -ir width=6
|
||||||
|
batslib_print_kv_single "$width" 'status' "$status"
|
||||||
|
batslib_print_kv_single_or_multi "$width" 'output' "$output"
|
||||||
|
} \
|
||||||
|
| batslib_decorate 'command failed' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
42
portable/test/test_helper/bats-assert/src/refute.bash
Normal file
42
portable/test/test_helper/bats-assert/src/refute.bash
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# refute
|
||||||
|
# ======
|
||||||
|
#
|
||||||
|
# Summary: Fail if the given expression evaluates to true.
|
||||||
|
#
|
||||||
|
# Usage: refute <expression>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# <expression> The expression to evaluate for falsiness.
|
||||||
|
# *__Note:__ The expression must be a simple command.
|
||||||
|
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
|
||||||
|
# such as `[[`, can be used only when executed with `bash -c`.*
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - the successful expression, on failure
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Returns:
|
||||||
|
# 0 - if expression evaluates to false
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute()' {
|
||||||
|
# rm -f '/var/log/test.log'
|
||||||
|
# refute [ -e '/var/log/test.log' ]
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the successful expression is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- assertion succeeded, but it was expected to fail --
|
||||||
|
# expression : [ -e /var/log/test.log ]
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
refute() {
|
||||||
|
if "$@"; then
|
||||||
|
batslib_print_kv_single 10 'expression' "$*" \
|
||||||
|
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
271
portable/test/test_helper/bats-assert/src/refute_line.bash
Normal file
271
portable/test/test_helper/bats-assert/src/refute_line.bash
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
# refute_line
|
||||||
|
# ===========
|
||||||
|
#
|
||||||
|
# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number.
|
||||||
|
#
|
||||||
|
# Usage: refute_line [-n index] [-p | -e] [--] <unexpected>
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -n, --index <idx> Match the <idx>th line
|
||||||
|
# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx>
|
||||||
|
# -e, --regexp Treat `unexpected` as an extended regular expression
|
||||||
|
# <unexpected> The unexpected line string, substring, or regular expression.
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDERR - details, on failure
|
||||||
|
# error message, on error
|
||||||
|
# Globals:
|
||||||
|
# output
|
||||||
|
# lines
|
||||||
|
# Returns:
|
||||||
|
# 0 - if match not found
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output.
|
||||||
|
# (It is the logical complement of `assert_line`.)
|
||||||
|
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
|
||||||
|
# Matching can be literal (default), partial or regular expression.
|
||||||
|
#
|
||||||
|
# *__Warning:__
|
||||||
|
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
|
||||||
|
# causing line indices to change and preventing testing for empty lines.*
|
||||||
|
#
|
||||||
|
# [bats-93]: https://github.com/sstephenson/bats/pull/93
|
||||||
|
#
|
||||||
|
# ## Looking for a line in the output
|
||||||
|
#
|
||||||
|
# By default, the entire output is searched for the unexpected line.
|
||||||
|
# The assertion fails if the unexpected line is found in `${lines[@]}`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_line() looking for line' {
|
||||||
|
# run echo $'have-0\nwant\nhave-2'
|
||||||
|
# refute_line 'want'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- line should not be in output --
|
||||||
|
# line : want
|
||||||
|
# index : 1
|
||||||
|
# output (3 lines):
|
||||||
|
# have-0
|
||||||
|
# > want
|
||||||
|
# have-2
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Matching a specific line
|
||||||
|
#
|
||||||
|
# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
|
||||||
|
# The assertion fails if the unexpected line equals `${lines[<idx>]}`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_line() specific line' {
|
||||||
|
# run echo $'have-0\nwant-1\nhave-2'
|
||||||
|
# refute_line --index 1 'want-1'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the index and the unexpected line are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- line should differ --
|
||||||
|
# index : 1
|
||||||
|
# line : want-1
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Partial matching
|
||||||
|
#
|
||||||
|
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||||
|
# When used, a match fails if the unexpected *substring* is found in the matched line.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_line() partial matching' {
|
||||||
|
# run echo $'have 1\nwant 2\nhave 3'
|
||||||
|
# refute_line --partial 'want'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, in addition to the details of literal matching, the substring is also displayed.
|
||||||
|
# When used with `--index <idx>` the substring replaces the unexpected line.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- no line should contain substring --
|
||||||
|
# substring : want
|
||||||
|
# index : 1
|
||||||
|
# output (3 lines):
|
||||||
|
# have 1
|
||||||
|
# > want 2
|
||||||
|
# have 3
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Regular expression matching
|
||||||
|
#
|
||||||
|
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||||
|
# When used, a match fails if the *extended regular expression* matches the line being tested.
|
||||||
|
#
|
||||||
|
# *__Note__:
|
||||||
|
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_line() regular expression matching' {
|
||||||
|
# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
|
||||||
|
# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, in addition to the details of literal matching, the regular expression is also displayed.
|
||||||
|
# When used with `--index <idx>` the regular expression replaces the unexpected line.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- regular expression should not match line --
|
||||||
|
# index : 0
|
||||||
|
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||||
|
# line : Foobar v0.1.0
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
|
||||||
|
refute_line() {
|
||||||
|
local -i is_match_line=0
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
: "${lines?}"
|
||||||
|
|
||||||
|
# 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 ]]; 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
|
||||||
|
}
|
199
portable/test/test_helper/bats-assert/src/refute_output.bash
Normal file
199
portable/test/test_helper/bats-assert/src/refute_output.bash
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
# refute_output
|
||||||
|
# =============
|
||||||
|
#
|
||||||
|
# Summary: Fail if `$output' matches the unexpected output.
|
||||||
|
#
|
||||||
|
# Usage: refute_output [-p | -e] [- | [--] <unexpected>]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -p, --partial Match if `unexpected` is a substring of `$output`
|
||||||
|
# -e, --regexp Treat `unexpected` as an extended regular expression
|
||||||
|
# -, --stdin Read `unexpected` value from STDIN
|
||||||
|
# <unexpected> The unexpected value, substring, or regular expression
|
||||||
|
#
|
||||||
|
# IO:
|
||||||
|
# STDIN - [=$1] unexpected output
|
||||||
|
# STDERR - details, on failure
|
||||||
|
# error message, on error
|
||||||
|
# Globals:
|
||||||
|
# output
|
||||||
|
# Returns:
|
||||||
|
# 0 - if output matches the unexpected value/partial/regexp
|
||||||
|
# 1 - otherwise
|
||||||
|
#
|
||||||
|
# This function verifies that a command or function does not produce the unexpected output.
|
||||||
|
# (It is the logical complement of `assert_output`.)
|
||||||
|
# Output matching can be literal (the default), partial or by regular expression.
|
||||||
|
# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
|
||||||
|
#
|
||||||
|
# ## Literal matching
|
||||||
|
#
|
||||||
|
# By default, literal matching is performed.
|
||||||
|
# The assertion fails if `$output` equals the unexpected output.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_output()' {
|
||||||
|
# run echo 'want'
|
||||||
|
# refute_output 'want'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# @test 'refute_output() with pipe' {
|
||||||
|
# run echo 'hello'
|
||||||
|
# echo 'world' | refute_output -
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# @test 'refute_output() with herestring' {
|
||||||
|
# run echo 'hello'
|
||||||
|
# refute_output - <<< world
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the output is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- output equals, but it was expected to differ --
|
||||||
|
# output : want
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Existence
|
||||||
|
#
|
||||||
|
# To assert that there is no output at all, omit the matching argument.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_output()' {
|
||||||
|
# run foo --silent
|
||||||
|
# refute_output
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, an error message is displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- unexpected output --
|
||||||
|
# expected no output, but output was non-empty
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Partial matching
|
||||||
|
#
|
||||||
|
# Partial matching can be enabled with the `--partial` option (`-p` for short).
|
||||||
|
# When used, the assertion fails if the unexpected _substring_ is found in `$output`.
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_output() partial matching' {
|
||||||
|
# run echo 'ERROR: no such file or directory'
|
||||||
|
# refute_output --partial 'ERROR'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the substring and the output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- output should not contain substring --
|
||||||
|
# substring : ERROR
|
||||||
|
# output : ERROR: no such file or directory
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# ## Regular expression matching
|
||||||
|
#
|
||||||
|
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
|
||||||
|
# When used, the assertion fails if the *extended regular expression* matches `$output`.
|
||||||
|
#
|
||||||
|
# *__Note__:
|
||||||
|
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
|
||||||
|
# not individual lines.*
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_output() regular expression matching' {
|
||||||
|
# run echo 'Foobar v0.1.0'
|
||||||
|
# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the regular expression and the output are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# -- regular expression should not match output --
|
||||||
|
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
|
||||||
|
# output : Foobar v0.1.0
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
refute_output() {
|
||||||
|
local -i is_mode_partial=0
|
||||||
|
local -i is_mode_regexp=0
|
||||||
|
local -i is_mode_empty=0
|
||||||
|
local -i use_stdin=0
|
||||||
|
: "${output?}"
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
is_mode_empty=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-p|--partial) is_mode_partial=1; shift ;;
|
||||||
|
-e|--regexp) is_mode_regexp=1; shift ;;
|
||||||
|
-|--stdin) use_stdin=1; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( is_mode_partial )) && (( is_mode_regexp )); then
|
||||||
|
echo "\`--partial' and \`--regexp' are mutually exclusive" \
|
||||||
|
| batslib_decorate 'ERROR: refute_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local unexpected
|
||||||
|
if (( use_stdin )); then
|
||||||
|
unexpected="$(cat -)"
|
||||||
|
else
|
||||||
|
unexpected="${1-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`$unexpected'" \
|
||||||
|
| batslib_decorate 'ERROR: refute_output' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Matching.
|
||||||
|
if (( is_mode_empty )); then
|
||||||
|
if [ -n "$output" ]; then
|
||||||
|
batslib_print_kv_single_or_multi 6 \
|
||||||
|
'output' "$output" \
|
||||||
|
| batslib_decorate 'output non-empty, but expected no output' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
elif (( is_mode_regexp )); then
|
||||||
|
if [[ $output =~ $unexpected ]]; 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
|
||||||
|
}
|
66
portable/test/test_helper/bats-assert/src/refute_regex.bash
Normal file
66
portable/test/test_helper/bats-assert/src/refute_regex.bash
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# `refute_regex`
|
||||||
|
#
|
||||||
|
# This function is similar to `refute_equal` but uses pattern matching instead
|
||||||
|
# of equality, by wrapping `! [[ value =~ pattern ]]`.
|
||||||
|
#
|
||||||
|
# Fail if the value (first parameter) matches the pattern (second parameter).
|
||||||
|
#
|
||||||
|
# ```bash
|
||||||
|
# @test 'refute_regex()' {
|
||||||
|
# refute_regex 'WhatsApp' 'Threema'
|
||||||
|
# }
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# On failure, the value, the pattern and the match are displayed.
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# @test 'refute_regex()' {
|
||||||
|
# refute_regex 'WhatsApp' 'What.'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# -- value matches regular expression --
|
||||||
|
# value : WhatsApp
|
||||||
|
# pattern : What.
|
||||||
|
# match : Whats
|
||||||
|
# case : sensitive
|
||||||
|
# --
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# If the value or pattern is longer than one line then it is displayed in
|
||||||
|
# *multi-line* format.
|
||||||
|
#
|
||||||
|
# An error is displayed if the specified extended regular expression is invalid.
|
||||||
|
#
|
||||||
|
# For description of the matching behavior, refer to the documentation of the
|
||||||
|
# `=~` operator in the
|
||||||
|
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
|
||||||
|
#
|
||||||
|
# Note that the `BASH_REMATCH` array is available immediately after the
|
||||||
|
# assertion fails but is fragile, i.e. prone to being overwritten as a side
|
||||||
|
# effect of other actions like calling `run`. Thus, it's good practice to avoid
|
||||||
|
# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable
|
||||||
|
# information the array contains is the matching part of the value which is
|
||||||
|
# printed in the failing test log, as mentioned above.
|
||||||
|
refute_regex() {
|
||||||
|
local -r value="${1}"
|
||||||
|
local -r pattern="${2}"
|
||||||
|
|
||||||
|
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
|
||||||
|
echo "Invalid extended regular expression: \`${pattern}'" \
|
||||||
|
| batslib_decorate 'ERROR: refute_regex' \
|
||||||
|
| fail
|
||||||
|
elif [[ "${value}" =~ ${pattern} ]]; then
|
||||||
|
if shopt -p nocasematch &>/dev/null; then
|
||||||
|
local case_sensitive=insensitive
|
||||||
|
else
|
||||||
|
local case_sensitive=sensitive
|
||||||
|
fi
|
||||||
|
batslib_print_kv_single_or_multi 8 \
|
||||||
|
'value' "${value}" \
|
||||||
|
'pattern' "${pattern}" \
|
||||||
|
'match' "${BASH_REMATCH[0]}" \
|
||||||
|
'case' "${case_sensitive}" \
|
||||||
|
| batslib_decorate 'value matches regular expression' \
|
||||||
|
| fail
|
||||||
|
fi
|
||||||
|
}
|
2
portable/test/test_helper/bats-file/load.bash
Normal file
2
portable/test/test_helper/bats-file/load.bash
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/file.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/temp.bash"
|
1111
portable/test/test_helper/bats-file/src/file.bash
Normal file
1111
portable/test/test_helper/bats-file/src/file.bash
Normal file
File diff suppressed because it is too large
Load Diff
182
portable/test/test_helper/bats-file/src/temp.bash
Executable file
182
portable/test/test_helper/bats-file/src/temp.bash
Executable file
@ -0,0 +1,182 @@
|
|||||||
|
#
|
||||||
|
# bats-file - Common filesystem assertions and helpers for Bats
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# temp.bash
|
||||||
|
# ---------
|
||||||
|
#
|
||||||
|
# Functions for handling temporary directories.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Create a temporary directory for the current test in `BATS_TMPDIR`,
|
||||||
|
# and display its path on the standard output.
|
||||||
|
#
|
||||||
|
# The directory name is derived from the test's filename and number, and
|
||||||
|
# a random string for uniqueness.
|
||||||
|
#
|
||||||
|
# <test-filename>-<test-number>-<random-string>
|
||||||
|
#
|
||||||
|
# When `--prefix <prefix>' is specified, `<prefix>' is prepended to the
|
||||||
|
# directory name.
|
||||||
|
#
|
||||||
|
# <prefix><test-filename>-<test-number>-<random-string>
|
||||||
|
#
|
||||||
|
# Must be called from `setup', `@test' or `teardown'.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# setup() {
|
||||||
|
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# teardown() {
|
||||||
|
# temp_del "$TEST_TEMP_DIR"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Globals:
|
||||||
|
# BATS_TEST_NAME
|
||||||
|
# BATS_TEST_FILENAME
|
||||||
|
# BATS_TEST_NUMBER
|
||||||
|
# BATS_TMPDIR
|
||||||
|
# Arguments:
|
||||||
|
# none
|
||||||
|
# Options:
|
||||||
|
# -p, --prefix <prefix> - prefix the directory name with `<prefix>'
|
||||||
|
# Returns:
|
||||||
|
# 0 - on success
|
||||||
|
# 1 - otherwise
|
||||||
|
# Outputs:
|
||||||
|
# STDOUT - path of temporary directory
|
||||||
|
# STDERR - error messages
|
||||||
|
temp_make() {
|
||||||
|
# Check caller.
|
||||||
|
if ! ( batslib_is_caller --indirect 'setup' \
|
||||||
|
|| batslib_is_caller --indirect 'setup_file' \
|
||||||
|
|| batslib_is_caller --indirect "$BATS_TEST_NAME" \
|
||||||
|
|| batslib_is_caller --indirect 'teardown' \
|
||||||
|
|| batslib_is_caller --indirect 'teardown_file' )
|
||||||
|
then
|
||||||
|
echo "Must be called from \`setup', \`@test' or \`teardown'" \
|
||||||
|
| batslib_decorate 'ERROR: temp_make' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
local prefix=''
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-p|--prefix)
|
||||||
|
if (( $# < 2 )); then
|
||||||
|
echo "\`--prefix' requires an argument" \
|
||||||
|
| batslib_decorate 'ERROR: temp_make' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
prefix="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Create directory.
|
||||||
|
local template="$prefix"
|
||||||
|
template+="${BATS_TEST_FILENAME##*/}"
|
||||||
|
template+="-${BATS_TEST_NUMBER}"
|
||||||
|
template+='-XXXXXXXXXX'
|
||||||
|
|
||||||
|
local path
|
||||||
|
path="$(mktemp -d -- "${BATS_TMPDIR}/${template}" 2>&1)"
|
||||||
|
if (( $? )); then
|
||||||
|
echo "$path" \
|
||||||
|
| batslib_decorate 'ERROR: temp_make' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$path"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete a temporary directory, typically created with `temp_make', and
|
||||||
|
# its contents.
|
||||||
|
#
|
||||||
|
# Note: Actually, this function can be used to delete any file or
|
||||||
|
# directory. However, it is most useful in deleting temporary
|
||||||
|
# directories created with `temp_make', hence the naming.
|
||||||
|
#
|
||||||
|
# For development and debugging, deletion can be prevented using
|
||||||
|
# environment variables.
|
||||||
|
#
|
||||||
|
# When `BATSLIB_TEMP_PRESERVE' is set to 1, the function succeeds but
|
||||||
|
# the directory is not deleted.
|
||||||
|
#
|
||||||
|
# When `BATSLIB_TEMP_PRESERVE_ON_FAILURE' is set to 1 and `temp_del' is
|
||||||
|
# called, directly or indirectly, from `teardown', the function succeeds
|
||||||
|
# but the directory is not deleted if the test has failed.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# setup() {
|
||||||
|
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# teardown() {
|
||||||
|
# temp_del "$TEST_TEMP_DIR"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Globals:
|
||||||
|
# BATSLIB_TEMP_PRESERVE
|
||||||
|
# BATSLIB_TEMP_PRESERVE_ON_FAILURE
|
||||||
|
# BATS_TEST_COMPLETED
|
||||||
|
# Arguments:
|
||||||
|
# $1 - path of directory
|
||||||
|
# Returns:
|
||||||
|
# 0 - on success
|
||||||
|
# 1 - otherwise
|
||||||
|
# Outputs:
|
||||||
|
# STDERR - error messages
|
||||||
|
temp_del() {
|
||||||
|
local -r path="$1"
|
||||||
|
|
||||||
|
# Environment variables.
|
||||||
|
if [[ ${BATSLIB_TEMP_PRESERVE-} == '1' ]]; then
|
||||||
|
return 0
|
||||||
|
elif [[ ${BATSLIB_TEMP_PRESERVE_ON_FAILURE-} == '1' ]]; then
|
||||||
|
# Check caller.
|
||||||
|
if ! ( batslib_is_caller --indirect 'teardown' \
|
||||||
|
|| batslib_is_caller --indirect 'teardown_file' )
|
||||||
|
then
|
||||||
|
echo "Must be called from \`teardown' or \`teardown_file' when using \`BATSLIB_TEMP_PRESERVE_ON_FAILURE'" \
|
||||||
|
| batslib_decorate 'ERROR: temp_del' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( ${BATS_TEST_COMPLETED:-0} != 1 )) && return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Delete directory.
|
||||||
|
local result
|
||||||
|
result="$(rm -r -- "$path" 2>&1 </dev/null)"
|
||||||
|
if (( $? )); then
|
||||||
|
echo "$result" \
|
||||||
|
| batslib_decorate 'ERROR: temp_del' \
|
||||||
|
| fail
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
}
|
3
portable/test/test_helper/bats-support/load.bash
Normal file
3
portable/test/test_helper/bats-support/load.bash
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash"
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash"
|
41
portable/test/test_helper/bats-support/src/error.bash
Normal file
41
portable/test/test_helper/bats-support/src/error.bash
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# bats-support - Supporting library for Bats test helpers
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# error.bash
|
||||||
|
# ----------
|
||||||
|
#
|
||||||
|
# Functions implementing error reporting. Used by public helper
|
||||||
|
# functions or test suits directly.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Fail and display a message. When no parameters are specified, the
|
||||||
|
# message is read from the standard input. Other functions use this to
|
||||||
|
# report failure.
|
||||||
|
#
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Arguments:
|
||||||
|
# $@ - [=STDIN] message
|
||||||
|
# Returns:
|
||||||
|
# 1 - always
|
||||||
|
# Inputs:
|
||||||
|
# STDIN - [=$@] message
|
||||||
|
# Outputs:
|
||||||
|
# STDERR - message
|
||||||
|
fail() {
|
||||||
|
(( $# == 0 )) && batslib_err || batslib_err "$@"
|
||||||
|
return 1
|
||||||
|
}
|
73
portable/test/test_helper/bats-support/src/lang.bash
Normal file
73
portable/test/test_helper/bats-support/src/lang.bash
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#
|
||||||
|
# bats-util - Various auxiliary functions for Bats
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# lang.bash
|
||||||
|
# ---------
|
||||||
|
#
|
||||||
|
# Bash language and execution related functions. Used by public helper
|
||||||
|
# functions.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Check whether the calling function was called from a given function.
|
||||||
|
#
|
||||||
|
# By default, direct invocation is checked. The function succeeds if the
|
||||||
|
# calling function was called directly from the given function. In other
|
||||||
|
# words, if the given function is the next element on the call stack.
|
||||||
|
#
|
||||||
|
# When `--indirect' is specified, indirect invocation is checked. The
|
||||||
|
# function succeeds if the calling function was called from the given
|
||||||
|
# function with any number of intermediate calls. In other words, if the
|
||||||
|
# given function can be found somewhere on the call stack.
|
||||||
|
#
|
||||||
|
# Direct invocation is a form of indirect invocation with zero
|
||||||
|
# intermediate calls.
|
||||||
|
#
|
||||||
|
# Globals:
|
||||||
|
# FUNCNAME
|
||||||
|
# Options:
|
||||||
|
# -i, --indirect - check indirect invocation
|
||||||
|
# Arguments:
|
||||||
|
# $1 - calling function's name
|
||||||
|
# Returns:
|
||||||
|
# 0 - current function was called from the given function
|
||||||
|
# 1 - otherwise
|
||||||
|
batslib_is_caller() {
|
||||||
|
local -i is_mode_direct=1
|
||||||
|
|
||||||
|
# Handle options.
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
case "$1" in
|
||||||
|
-i|--indirect) is_mode_direct=0; shift ;;
|
||||||
|
--) shift; break ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Arguments.
|
||||||
|
local -r func="$1"
|
||||||
|
|
||||||
|
# Check call stack.
|
||||||
|
if (( is_mode_direct )); then
|
||||||
|
[[ $func == "${FUNCNAME[2]}" ]] && return 0
|
||||||
|
else
|
||||||
|
local -i depth
|
||||||
|
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
|
||||||
|
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
279
portable/test/test_helper/bats-support/src/output.bash
Normal file
279
portable/test/test_helper/bats-support/src/output.bash
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
#
|
||||||
|
# bats-support - Supporting library for Bats test helpers
|
||||||
|
#
|
||||||
|
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
|
||||||
|
#
|
||||||
|
# To the extent possible under law, the author(s) have dedicated all
|
||||||
|
# copyright and related and neighboring rights to this software to the
|
||||||
|
# public domain worldwide. This software is distributed without any
|
||||||
|
# warranty.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
# along with this software. If not, see
|
||||||
|
# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# output.bash
|
||||||
|
# -----------
|
||||||
|
#
|
||||||
|
# Private functions implementing output formatting. Used by public
|
||||||
|
# helper functions.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Print a message to the standard error. When no parameters are
|
||||||
|
# specified, the message is read from the standard input.
|
||||||
|
#
|
||||||
|
# Globals:
|
||||||
|
# none
|
||||||
|
# Arguments:
|
||||||
|
# $@ - [=STDIN] message
|
||||||
|
# Returns:
|
||||||
|
# none
|
||||||
|
# Inputs:
|
||||||
|
# STDIN - [=$@] message
|
||||||
|
# Outputs:
|
||||||
|
# STDERR - message
|
||||||
|
batslib_err() {
|
||||||
|
{ if (( $# > 0 )); then
|
||||||
|
echo "$@"
|
||||||
|
else
|
||||||
|
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
|
||||||
|
}
|
402
portable/tomb
Executable file
402
portable/tomb
Executable file
@ -0,0 +1,402 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Tomb v3, the Crypto Undertaker portable version 3
|
||||||
|
#
|
||||||
|
# A commandline tool to easily operate encryption of secret data
|
||||||
|
#
|
||||||
|
# {{{ License
|
||||||
|
|
||||||
|
# Copyright (C) 2007-2022 Dyne.org Foundation
|
||||||
|
#
|
||||||
|
# Tomb is designed, written and maintained by Denis Roio <jaromil@dyne.org>
|
||||||
|
#
|
||||||
|
# Please refer to the AUTHORS file for more information.
|
||||||
|
#
|
||||||
|
# This source code is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This source code is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
|
||||||
|
# to the GNU Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Public License along with
|
||||||
|
# this source code; if not, , see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# }}} - License
|
||||||
|
|
||||||
|
# {{{ Global variables
|
||||||
|
|
||||||
|
VERSION="3.1.0"
|
||||||
|
DATE="Nov/2022"
|
||||||
|
TOMBEXEC="$0"
|
||||||
|
TMPDIR="${TMP:-/tmp}"
|
||||||
|
TOMBTMPFILES=""
|
||||||
|
PATH="${PATH}:.:/usr/local/bin"
|
||||||
|
exitcode=0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Logging functions
|
||||||
|
|
||||||
|
_success() {
|
||||||
|
echo "[*] " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
}
|
||||||
|
_message() {
|
||||||
|
echo " . " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
}
|
||||||
|
_warning() {
|
||||||
|
echo "[W] " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
}
|
||||||
|
_verbose() {
|
||||||
|
echo "[D] " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
}
|
||||||
|
_error() {
|
||||||
|
echo "[!] " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
exitcode=1
|
||||||
|
}
|
||||||
|
_failure() {
|
||||||
|
echo "[!!] " "$1" "$2" "$3" "$4" 1>&2
|
||||||
|
exitcode=1
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
_success "Starting Tomb v$VERSION"
|
||||||
|
|
||||||
|
# {{{ Internal functions
|
||||||
|
|
||||||
|
_random_string() {
|
||||||
|
len=${1:-32}
|
||||||
|
[ "$1" != "" ] && {
|
||||||
|
echo "print(O.random($len):base58())" | zenroom 2>/dev/null
|
||||||
|
}
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
_tmp_create() {
|
||||||
|
[ -d "$TMPDIR" ] || {
|
||||||
|
# we create the tempdir with the sticky bit on
|
||||||
|
mkdir -m 1777 "$TMPDIR"
|
||||||
|
[ $? = 0 ] || {
|
||||||
|
_failure "Fatal error creating the temporary directory: %s" "$TMPDIR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tfile="${TMPDIR}/`_random_string 64`" # Temporary file
|
||||||
|
umask 066
|
||||||
|
[ $? = 0 ] || {
|
||||||
|
_failure "Fatal error setting the permission umask for temporary files"
|
||||||
|
}
|
||||||
|
[ -r "$tfile" ] && {
|
||||||
|
_failure "Someone is messing up with us trying to hijack temporary files."
|
||||||
|
}
|
||||||
|
touch "$tfile"
|
||||||
|
[ $? = 0 ] || {
|
||||||
|
_failure "Fatal error creating a temporary file: %s" "$tfile"
|
||||||
|
}
|
||||||
|
_verbose "Created tempfile: ::1 temp file::" "$tfile"
|
||||||
|
TOMBTMP="$tfile"
|
||||||
|
TOMBTMPFILES="$TOMBTMPFILES:$tfile"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ FreeBSD
|
||||||
|
freebsd_mount() {
|
||||||
|
file="$1"
|
||||||
|
mnt="$2"
|
||||||
|
_verbose `veracrypt -l "$file"`
|
||||||
|
loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
_verbose "fsck $loop"
|
||||||
|
fsck.ext4 -p -C0 "$loop"
|
||||||
|
lklfuse -o type=ext4 "${loop}" "$mnt"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
freebsd_close() {
|
||||||
|
file="$1"
|
||||||
|
md=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
# umount "$mnt"
|
||||||
|
_verbose "md: $md"
|
||||||
|
mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
|
||||||
|
_verbose "mnt: $mnt"
|
||||||
|
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
|
||||||
|
_verbose "kill $lkl (lklfuse on $md)"
|
||||||
|
# trying to deail with lklfuse bug
|
||||||
|
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
|
||||||
|
renice -20 $lkl
|
||||||
|
kill $lkl
|
||||||
|
sync
|
||||||
|
sleep 1
|
||||||
|
kill -9 $lkl
|
||||||
|
# lkl should have really exited now
|
||||||
|
_verbose "veracrypt -d $file"
|
||||||
|
veracrypt --text --non-interactive -d "$file"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Linux
|
||||||
|
# usage: _mount /tmp/.veracrypt_ mountpoint
|
||||||
|
linux_mount() {
|
||||||
|
file="$1"
|
||||||
|
mnt="$2"
|
||||||
|
veralist=`veracrypt -l "$file" | awk '{print($2,":",$3,":",$4)}'`
|
||||||
|
[ "$veralist" = "" ] && {
|
||||||
|
_error "Cannot mount tomb not yet mapped " "$file"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
loop=`echo $veralist | cut -d: -f2 | xargs`
|
||||||
|
[ "`echo $veralist | cut -d: -f3 | xargs`" != "-" ] && {
|
||||||
|
_error "Tomb already mounted " "$file on $loop"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
_verbose "fsck $loop"
|
||||||
|
fsck.ext4 -p -C0 "$loop" 1>&2
|
||||||
|
_verbose "linux_mount $mnt"
|
||||||
|
mount "$loop" "$mnt"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ POSIX portable
|
||||||
|
|
||||||
|
# usage: echo PASSWORD | posix_create file size pim
|
||||||
|
posix_create() {
|
||||||
|
file="$1" # must not exist
|
||||||
|
size="$2" # veracrypt format (accepts M or G)
|
||||||
|
pim="$3" # any number
|
||||||
|
_verbose "posix_create $file $size $pim"
|
||||||
|
veracrypt --text --non-interactive --stdin \
|
||||||
|
-m nokernelcrypto \
|
||||||
|
-c "$file" --volume-type normal \
|
||||||
|
--hash sha512 --encryption serpent-aes \
|
||||||
|
--filesystem none --size "$size" --pim "$pim" \
|
||||||
|
--random-source /dev/urandom -k ''
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_format() {
|
||||||
|
file="$1"
|
||||||
|
loop=`veracrypt -l "$file" | awk '{print $3}'`
|
||||||
|
_verbose "posix_format: ${loop}"
|
||||||
|
mkfs.ext4 -L "`basename $file`" "$loop" # -E root_owner="${user_uid}:${user_gid}" "$loop"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# usage: echo PASSWORD | posix_map file pim
|
||||||
|
posix_map() {
|
||||||
|
file="$1"
|
||||||
|
pim="$2"
|
||||||
|
_verbose "posix_map $file $pim"
|
||||||
|
veracrypt --text --non-interactive --stdin \
|
||||||
|
--protect-hidden no -m nokernelcrypto \
|
||||||
|
-k '' --pim "$pim" --filesystem none \
|
||||||
|
"$file"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_close() {
|
||||||
|
file="$1"
|
||||||
|
_verbose "posix_close $file"
|
||||||
|
veracrypt --text --non-interactive -d "$file"
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Initialization
|
||||||
|
|
||||||
|
system="unknown"
|
||||||
|
create=""
|
||||||
|
format=""
|
||||||
|
map=""
|
||||||
|
mount=""
|
||||||
|
close=""
|
||||||
|
|
||||||
|
tomb_init() {
|
||||||
|
system="`uname -s`"
|
||||||
|
case "$system" in
|
||||||
|
FreeBSD)
|
||||||
|
cat <<EOF
|
||||||
|
create=posix_create
|
||||||
|
format=posix_format
|
||||||
|
map=posix_map
|
||||||
|
mount=freebsd_mount
|
||||||
|
close=freebsd_close
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
Linux)
|
||||||
|
cat <<EOF
|
||||||
|
create=posix_create
|
||||||
|
format=posix_format
|
||||||
|
map=posix_map
|
||||||
|
mount=linux_mount
|
||||||
|
close=posix_close
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_failure "Unsupported system: %s" "$system"
|
||||||
|
esac
|
||||||
|
echo "system=$system"
|
||||||
|
# _verbose "system detected: %s" $system
|
||||||
|
}
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
# {{{ Main
|
||||||
|
[ "$1" = "source" ] && return 0
|
||||||
|
eval "`tomb_init`"
|
||||||
|
tombfile=""
|
||||||
|
tombsize=""
|
||||||
|
PIM=69
|
||||||
|
|
||||||
|
cmd="$1"
|
||||||
|
shift 1
|
||||||
|
|
||||||
|
case "$cmd" in
|
||||||
|
dig)
|
||||||
|
args=2
|
||||||
|
while [ $args -gt 0 ]; do
|
||||||
|
[ "$1" = '-s' ] && { tombsize="$2"; shift 2; }
|
||||||
|
[ "$1" != "" ] && { tombfile="$1"; shift 1; }
|
||||||
|
args=$(( $args - 1 ))
|
||||||
|
done
|
||||||
|
|
||||||
|
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||||
|
[ "$tombsize" = "" ] && _failure "Size argument missing, use -s"
|
||||||
|
# TODO: check if size is integer
|
||||||
|
# [ "$tombsize" -ge 10 ] || _failure "Tombs can't be smaller than 10 mebibytes"
|
||||||
|
[ -e "$tombfile" ] && {
|
||||||
|
_error "A tomb exists already. I'm not digging here:"
|
||||||
|
ls -l "$tombfile"; exit 1
|
||||||
|
}
|
||||||
|
_message "Commanded to dig tomb " "$tombfile" " sized $tombsize"
|
||||||
|
touch "$tombfile" || _failure "Error creating the tomb " "$tombfile"
|
||||||
|
# dd if=/dev/urandom bs=1048576 count=$tombsize of="$tombfile" || {
|
||||||
|
# _failure "Error creating the tomb " "$tombfile"
|
||||||
|
# exit 1
|
||||||
|
# }
|
||||||
|
if [ "$system" = "Linux" ]; then
|
||||||
|
fallocate -x -l "$tombsize" "$tombfile" ||
|
||||||
|
_failure "Error creating the tomb " "$tombfile"
|
||||||
|
else
|
||||||
|
bytesize="`tomb-str2size $tombsize`"
|
||||||
|
[ "$bytesize" = "" ] && _failure "Error reading tomb size " "$tombsize"
|
||||||
|
dd if=/dev/zero of="$tombfile" count="$(( bytesize / 1048576))" bs=1048576 ||
|
||||||
|
_failure "Error creating the tomb " "$tombfile of $tombsize"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
forge)
|
||||||
|
args=1
|
||||||
|
[ "$1" = "" ] && _failure "Missing argument in command " "forge"
|
||||||
|
while [ $args -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
'-k') tombkey="$2"; shift 2 ;;
|
||||||
|
*) tombkey="$1"; shift 1 ;;
|
||||||
|
esac
|
||||||
|
args=$(( $args - 1 ))
|
||||||
|
done
|
||||||
|
[ "$tombkey" = "" ] && _failure "Missing path to tomb key"
|
||||||
|
[ -e "$tombkey" ] && {
|
||||||
|
_error "A tomb key exists already. I'm not forging here:"
|
||||||
|
ls -l "$tombkey"; exit 1
|
||||||
|
}
|
||||||
|
_message "Commanded to forge tomb key " "$tombkey"
|
||||||
|
touch "$tombkey" || _failure "Error creating the tomb key " "$tombkey"
|
||||||
|
cat <<EOF | zenroom -z | cut -d\" -f4 > "$tombkey"
|
||||||
|
rule check version 2.0.0
|
||||||
|
Given nothing
|
||||||
|
When I create the random object of '64' bytes
|
||||||
|
Then print the 'random object' as 'hex'
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
|
||||||
|
lock)
|
||||||
|
args=2
|
||||||
|
while [ $args -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
'-k') tombkey="$2"; shift 2 ;;
|
||||||
|
*) tombfile="$1"; shift 1 ;;
|
||||||
|
esac
|
||||||
|
args=$(( $args - 1 ))
|
||||||
|
done
|
||||||
|
case "$system" in
|
||||||
|
Linux)
|
||||||
|
tombsize=`stat "$tombfile" | awk '/Size:/ {print $2; exit}'`
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
tombsize=`stat -f '%z' "$tombfile"`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
_message "Commanded to lock tomb " "$tombfile of $tombsize with key $tombkey"
|
||||||
|
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||||
|
[ -r "$tombfile" ] || _failure "Tomb file not readable"
|
||||||
|
[ "$tombkey" = "" ] && _failure "Missing path to key"
|
||||||
|
[ -r "$tombkey" ] || _failure "Key file not readable"
|
||||||
|
"$create" "$tombfile" "$tombsize" "$PIM" < "$tombkey" || {
|
||||||
|
_failure "Error creating the tomb " "$tombfile"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
"$map" "$tombfile" "$PIM" < "$tombkey" ||
|
||||||
|
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
|
||||||
|
"$format" "$tombfile" ||
|
||||||
|
_error "Error formatting tomb " "$tombfile"
|
||||||
|
"$close" "$tombfile"
|
||||||
|
[ $exitcode = 0 ] &&
|
||||||
|
_success "Success locking tomb " "$tombfile with key $tombkey"
|
||||||
|
;;
|
||||||
|
|
||||||
|
open)
|
||||||
|
args=3
|
||||||
|
while [ $args -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
'-k') tombkey="$2"; shift 2 ;;
|
||||||
|
*) if [ "$tombfile" = "" ]; then
|
||||||
|
tombfile="$1"
|
||||||
|
else
|
||||||
|
tombmount="$1"
|
||||||
|
fi
|
||||||
|
shift 1 ;;
|
||||||
|
esac
|
||||||
|
args=$(( $args - 1 ))
|
||||||
|
done
|
||||||
|
_message "Commanded to open tomb " "$tombfile with key $tombkey on $tombmount"
|
||||||
|
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
|
||||||
|
[ -r "$tombfile" ] || _failure "Tomb file not readable"
|
||||||
|
[ "$tombkey" = "" ] && _failure "Missing path to key"
|
||||||
|
[ -r "$tombkey" ] || _failure "Key file not readable"
|
||||||
|
[ "$tombmount" = "" ] && tombmount="`basename $tombfile`"
|
||||||
|
"$map" "$tombfile" "$PIM" < "$tombkey"
|
||||||
|
[ $? != 0 ] &&
|
||||||
|
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
|
||||||
|
"$mount" "$tombfile" "$tombmount" || {
|
||||||
|
"$close" "$tombfile"
|
||||||
|
_failure "Error mounting the tomb " "$tombfile on $tombmount"
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
|
||||||
|
close)
|
||||||
|
# args=1
|
||||||
|
if [ "$1" = "all" ]; then
|
||||||
|
"$close"
|
||||||
|
elif [ -e "$1" ]; then
|
||||||
|
"$close" "`realpath $1`" ||
|
||||||
|
_failure "Error closing " "$1"
|
||||||
|
elif [ "$1" = "" ]; then
|
||||||
|
_failure "Missing argument: tomb file or mountpoint"
|
||||||
|
else
|
||||||
|
_failure "Wrong argument: $1"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
list)
|
||||||
|
veracrypt -l
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
exit 0
|
Loading…
Reference in New Issue
Block a user