Skip to content

Commit

Permalink
Minor release 0.9.0
Browse files Browse the repository at this point in the history
Signed-off-by: Torsten Long <[email protected]>
  • Loading branch information
razziel89 committed Jun 24, 2024
1 parent d913b84 commit 28816fd
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 91 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ default: build
check-dependencies:
command -v bash &>/dev/null || (echo "ERROR, please install bash" >&2; exit 1)
command -v bats &>/dev/null || (echo "ERROR, please install bats" >&2; exit 1)
command -v find &>/dev/null || (echo "ERROR, please install find" >&2; exit 1)
command -v shellcheck &>/dev/null || (echo "ERROR, please install shellcheck" >&2; exit 1)
command -v shfmt &>/dev/null || (echo "ERROR, please install shfmt" >&2; exit 1)
command -v jq &>/dev/null || (echo "ERROR, please install jq" >&2; exit 1)
Expand Down
14 changes: 1 addition & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,18 @@ See [below](#documentation-overview) for `shellmock`'s documentation.
The following tools are needed to use `shellmock`:

- `base32`
- `base64`
- `basename`
- `bash` (at least version 4.4)
- `cat`
- `chmod`
- `env`
- `find`
- `flock`
- `gawk`
- `grep`
- `mkdir`
- `mktemp`
- `ps`
- `rm`
- `sed`
- `sort`
- `touch`
- `tr`
- `xargs`

On Debian-based systems, they can be installed via:

```bash
sudo apt install -yqq bash coreutils findutils gawk grep procps sed util-linux
sudo apt install -yqq bash coreutils util-linux
```

You also need the [bats-core] testing framework that
Expand Down
62 changes: 42 additions & 20 deletions bin/mock_exe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,39 @@ env_var_check() {

get_and_ensure_outdir() {
local cmd_b32="$1"

local max_num_calls=${SHELLMOCK_MAX_CALLS_PER_MOCK:-1000}
if ! [[ ${max_num_calls} =~ ^[0-9][0-9]*$ ]]; then
echo >&2 "SHELLMOCK_MAX_CALLS_PER_MOCK must be a number."
_kill_parent "${PPID}"
return 1
fi
local tmp
tmp=$((max_num_calls - 1))
local max_digits=${#tmp}

# Ensure no two calls overwrite each other in a thread-safe way.
local count=0
local count
count=$(printf "%0${max_digits}d" "0")
local outdir="${__SHELLMOCK_OUTPUT}/${cmd_b32}/${count}"
while ! (
# Increment the counter until we find one that has not been used before.
flock -n 9 || exit 1
[[ -d ${outdir} ]] && exit 1
mkdir -p "${outdir}"
) 9> "${__SHELLMOCK_OUTPUT}/lockfile_${cmd_b32}_${count}"; do
count=$((count + 1))
count=$(printf "%0${max_digits}d" "$((count + 1))")
outdir="${__SHELLMOCK_OUTPUT}/${cmd_b32}/${count}"
done

if [[ ${count} -ge ${max_num_calls} ]]; then
echo >&2 "The maximum number of calls per mock is ${max_num_calls}." \
"Consider increasing SHELLMOCK_MAX_CALLS_PER_MOCK, which is currently" \
"set to '${max_num_calls}'."
_kill_parent "${PPID}"
return 1
fi

echo "${outdir}"
}

Expand Down Expand Up @@ -114,7 +135,7 @@ _match_spec() {
local spec
while read -r spec; do
local id val
id="$(gawk -F: '{print $1}' <<< "${spec}")"
read -r -d ':' id <<< "${spec}"
val="${spec##"${id}":}"

if [[ ${spec} =~ ^any: ]]; then
Expand All @@ -138,7 +159,7 @@ _match_spec() {
errecho "Internal error, incorrect spec ${spec}"
return 1
fi
done < <(base64 --decode <<< "${full_spec}") && wait $! || return 1
done < <(base32 --decode <<< "${full_spec}") && wait $! || return 1
}

# Check whether the given process is a bats process. A bats process is a bash
Expand Down Expand Up @@ -172,10 +193,9 @@ _kill_parent() {
return 0
fi

errecho "Killing parent process with information:"
# In case the `ps` command fails (e.g. because we mock it), don't fail this
# mock.
errecho "$(ps -p "${parent}" -lF || :)"
local cmd_w_args
mapfile -t -d $'\0' cmd_w_args < "/proc/${parent}/cmdline"
errecho "Killing parent process: ${cmd_w_args[*]@Q}"
kill "${parent}"
}

Expand All @@ -187,19 +207,20 @@ find_matching_argspec() {
local cmd_b32="${3}"
shift 3

local env_var
local env_var var
while read -r env_var; do

if _match_spec "${!env_var}" "$@"; then
echo "${env_var##MOCK_ARGSPEC_BASE64_}"
echo "${env_var##MOCK_ARGSPEC_BASE32_}"
echo "${env_var}" > "${outdir}/argspec"
return 0
fi
done < <(
env | sed 's/=.*$//' \
| {
grep -x "MOCK_ARGSPEC_BASE64_${cmd_b32}_[0-9][0-9]*" || :
} | sort -u
for var in "${!MOCK_ARGSPEC_BASE32_@}"; do
if [[ ${var} == "MOCK_ARGSPEC_BASE32_${cmd_b32}_"* ]]; then
echo "${var}"
fi
done
) && wait $! || return 1

errecho "SHELLMOCK: unexpected call '${cmd} $*'"
Expand All @@ -209,11 +230,11 @@ find_matching_argspec() {

provide_output() {
local cmd_spec="$1"
# Base64 encoding is an easy way to be able to store arbitrary data in
# Base32 encoding is an easy way to be able to store arbitrary data in
# environment variables.
output_base64="MOCK_OUTPUT_BASE64_${cmd_spec}"
if [[ -n ${!output_base64-} ]]; then
base64 --decode <<< "${!output_base64}"
output_base32="MOCK_OUTPUT_BASE32_${cmd_spec}"
if [[ -n ${!output_base32-} ]]; then
base32 --decode <<< "${!output_base32}"
fi
}

Expand Down Expand Up @@ -289,8 +310,9 @@ main() {
# Determine our name. This assumes that the first value in argv is the name of
# the command. This is almost always so.
local cmd cmd_b32 args
cmd="$(basename "$0")"
cmd_b32="$(base32 -w0 <<< "${cmd}" | tr "=" "_")"
cmd="${0##*/}"
cmd_b32="$(base32 -w0 <<< "${cmd}")"
cmd_b32="${cmd_b32//=/_}"
local outdir
outdir="$(get_and_ensure_outdir "${cmd_b32}")"
declare -g STDERR="${outdir}/stderr"
Expand Down
4 changes: 2 additions & 2 deletions docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
# Build the Deployable

Simply run the script `./generate_deployable.sh` at the top level of this
repository to generate `shellmoch.bash`.
It uses only the standard Unix tools `bash`, `cat`, and `gawk` to generate it.
repository to generate `shellmock.bash`.
It uses only the standard Unix tools `bash` and `cat` to generate it.
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ func2() {
local ls=ls
func1
# Output all files in a directory using `cat` via their full paths. This calls
# `cat` and `basename` via `xargs`.
# `cat` and `readlink` via `xargs`.
# shellmock: uses-command=readlink,cat
find . -type f | xargs readlink -f | xargs cat
# shellmock: uses-command=ls # Calling "ls" with its name in a variable.
Expand Down
27 changes: 21 additions & 6 deletions generate_deployable.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ __SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/" &> /dev/null && pwd)"
deployable() {
# Output header including the licence file.
echo '#!/bin/bash'
sed 's/^/# /' LICENSE
(
IFS=
while read -r line; do
echo "# ${line}"
done < LICENSE
)
cat << 'EOF'
# This file is auto-generated. It is the main deployable of shellmock. To make
Expand All @@ -46,11 +51,21 @@ __shellmock__help() {
This is shellmock, a tool to mock executables called within shell scripts.
ENDOFFILE

gawk \
-v start='<!-- shellmock-helptext-start -->' \
-v end='<!-- shellmock-helptext-end -->' \
'BEGIN{act=0} {if($0==end){act=0}; if(act==1){print}; if($0==start){act=1;};}' \
./docs/usage.md
(
IFS=
do_print=0
while read -r line; do
if [[ ${line} == '<!-- shellmock-helptext-end -->' ]]; then
do_print=0
fi
if [[ ${do_print} -eq 1 ]]; then
echo "${line}"
fi
if [[ ${line} == '<!-- shellmock-helptext-start -->' ]]; then
do_print=1
fi
done < ./docs/usage.md
)

cat << 'ENDOFFILE'
EOF
Expand Down
Loading

0 comments on commit 28816fd

Please sign in to comment.