Skip to content

Commit

Permalink
[GH-15] Reverted adjacent option-value syntax for long options (#27)
Browse files Browse the repository at this point in the history
- Reverted adjacent option-value syntax for long options
- Test helper improvements:
  - Added the ability to pre-process test output using multiple sed expressions.
  - Added the capability to match the context of a single output line against a regex.
  • Loading branch information
UrsaDK authored Dec 13, 2024
1 parent 0cbfb8e commit f266f21
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 42 deletions.
12 changes: 1 addition & 11 deletions lib/getopts_long.bash
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,7 @@ getopts_long() {
builtin getopts -- "${optspec_short}" "${optvar}" "${@}" || return 1
[[ "${!optvar}" == '-' ]] || return 0

if [[ "${OPTARG}" == *=* ]]; then
printf -v "${optvar}" "%s" "${OPTARG%%=*}"
else
for optspec in $(echo "${optspec_long}" | tr ' ' '\n' | sort -ur); do
if [[ "${optspec}" == *: && "${OPTARG}" == "${optspec%?}"* ]]; then
printf -v "${optvar}" "%s" "${optspec%?}"
break
fi
done
[[ "${!optvar}" == '-' ]] && printf -v "${optvar}" "%s" "${OPTARG}"
fi
printf -v "${optvar}" "%s" "${OPTARG%%=*}"

if [[ " ${optspec_long} " == *" ${!optvar}: "* ]]; then
OPTARG="${OPTARG#"${!optvar}"}"
Expand Down
21 changes: 0 additions & 21 deletions test/bats/github_15.bats

This file was deleted.

98 changes: 98 additions & 0 deletions test/bats/github_15a.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env bats

load ../test_helper

# Neither bash getopts nor getopts_long OPTSPEC includes [-], but
# getopts_long always appends [-:] to the end of the short OPTSPEC.

# Standard getopts should see:
# -t - a toggle
# -- - an invalid option
# -- - an invalid option
# -t - a toggle
# Getopts_long should see:
# -t - a toggle
# --- - an invalid option
# -t - a toggle
@test "${FEATURE}: short toggle, silent" {
compare '-t-- -t user_arg' \
'-t-- -t user_arg' \
'3{/^INVALID OPTION/d}'
}
@test "${FEATURE}: short toggle, verbose" {
compare '-t-- -t user_arg' \
'-t-- -t user_arg' \
'4{/getopts-verbose: illegal option -- -$/d}' \
'5{/^INVALID OPTION or MISSING ARGUMENT -- OPTARG is unset$/d}' \
's/getopts[[:alpha:]_-]*/GETOPTS-NORMALISED/'
}

# Standard getopts should see:
# -t - a toggle
# -- - an invalid option
# -- - an invalid option
# -t - a toggle
# Getopts_long should see:
# --toggle-- - an invalid option
# --toggle - a toggle
@test "${FEATURE}: long toggle, silent" {
compare '-t-- -t user_arg' \
'--toggle-- --toggle user_arg' \
'1{/^toggle triggered/d}' \
'/^INVALID OPTION/d'
expect "${bash_getopts_lines[0]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[1]}" == 'INVALID OPTION -- OPTARG=-'
expect "${bash_getopts_lines[2]}" == 'INVALID OPTION -- OPTARG=-'
expect "${bash_getopts_lines[3]}" == 'toggle triggered -- OPTARG is unset'
expect "${getopts_long_lines[0]}" == 'INVALID OPTION -- OPTARG=toggle--'
expect "${getopts_long_lines[1]}" == 'toggle triggered -- OPTARG is unset'
}
@test "${FEATURE}: long toggle, verbose" {
compare '-t-- -t user_arg' \
'--toggle-- --toggle user_arg' \
'1{/^toggle triggered/d}' \
'4{/getopts-verbose: illegal option -- -$/d}' \
'5{/^INVALID OPTION or MISSING ARGUMENT/d}' \
's/getopts[[:alpha:]_-]*/GETOPTS-NORMALISED/' \
's/(illegal option --) (-|toggle--)/\1 TOGGLE-NORMALISED/'
expect "${bash_getopts_lines[0]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[1]}" =~ 'getopts-verbose: illegal option -- -$'
expect "${bash_getopts_lines[3]}" =~ 'getopts-verbose: illegal option -- -$'
expect "${bash_getopts_lines[5]}" == 'toggle triggered -- OPTARG is unset'
expect "${getopts_long_lines[0]}" =~ 'getopts_long-verbose: illegal option -- toggle--$'
expect "${getopts_long_lines[2]}" == 'toggle triggered -- OPTARG is unset'
}

# Both implementations should see:
# -o -- - an option (-o) with a value (--)
# -t - a toggle
@test "${FEATURE}: short option, silent" {
compare '-o-- -t user_arg' \
'-o-- -t user_arg'
}
@test "${FEATURE}: short option, verbose" {
compare '-o-- -t user_arg' \
'-o-- -t user_arg'
}

# Standard getopts should see:
# -o -- - an option with a value (--)
# -t - a toggle
# Getopts_long should see:
# --option-- - an invalid option
# --toggle - a toggle
@test "${FEATURE}: long option, silent" {
compare '-o-- -t user_arg' \
'--option-- --toggle user_arg' \
'1{/(option supplied|INVALID OPTION)/d}'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=--'
expect "${getopts_long_lines[0]}" == 'INVALID OPTION -- OPTARG=option--'
}
@test "${FEATURE}: long option, verbose" {
compare '-o-- -t user_arg' \
'--option-- --toggle user_arg' \
'1{/(option supplied|illegal option)/d}' \
'2{/^INVALID OPTION or MISSING ARGUMENT/d}'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=--'
expect "${getopts_long_lines[0]}" =~ "getopts_long-verbose: illegal option -- option--$"
}
82 changes: 82 additions & 0 deletions test/bats/github_15b.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bats

load ../test_helper

# Both bash getopts and getopts_long OPTSPEC includes [-]
export GETOPTS_TEST_BIN='getopts-github_15b'
export GETOPTS_LONG_TEST_BIN='getopts_long-github_15b'

# Bash getopts should see four toggles: -t -- -- -t
# Getopts_long should see three toggles: -t --- -t
@test "${FEATURE}: short toggle, silent" {
compare '-t-- -t user_arg' \
'-t-- -t user_arg' \
'4{/^toggle triggered/d}'
}
@test "${FEATURE}: short toggle, verbose" {
compare '-t-- -t user_arg' \
'-t-- -t user_arg' \
'4{/^toggle triggered/d}'
}

# Bash getopts should see four toggles: -t -- -- -t
# Getopts_long should see an invalid option (--toggle--) and a toggle
@test "${FEATURE}: long toggle, silent" {
compare '-t-- -t user_arg' \
'--toggle-- --toggle user_arg' \
'/^toggle triggered/d' \
'/^INVALID OPTION --/d'
expect "${bash_getopts_lines[0]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[1]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[2]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[3]}" == 'toggle triggered -- OPTARG is unset'
expect "${getopts_long_lines[0]}" == 'INVALID OPTION -- OPTARG=toggle--'
expect "${getopts_long_lines[1]}" == 'toggle triggered -- OPTARG is unset'
}
@test "${FEATURE}: long toggle, verbose" {
compare '-t-- -t user_arg' \
'--toggle-- --toggle user_arg' \
'/^toggle triggered/d' \
'/illegal option -- toggle--$/d' \
'/^INVALID OPTION or MISSING ARGUMENT --/d'
expect "${bash_getopts_lines[0]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[1]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[2]}" == 'toggle triggered -- OPTARG is unset'
expect "${bash_getopts_lines[3]}" == 'toggle triggered -- OPTARG is unset'
expect "${getopts_long_lines[0]}" =~ 'getopts_long-\w+-verbose: illegal option -- toggle--'
expect "${getopts_long_lines[2]}" == 'toggle triggered -- OPTARG is unset'
}

# Both implementations should see:
# -o -- - an option (-o) with a value (--)
# -t - a toggle
@test "${FEATURE}: short option, silent" {
compare '-o-- -t user_arg' \
'-o-- -t user_arg'
}
@test "${FEATURE}: short option, verbose" {
compare '-o-- -t user_arg' \
'-o-- -t user_arg'
}

# Standard getopts should see:
# -o -- - an option with a value (--)
# -t - a toggle
# Getopts_long should see:
# --option-- - an invalid option
# --toggle - a toggle
@test "${FEATURE}: long option, silent" {
compare '-o-- -t user_arg' \
'--option-- --toggle user_arg' \
'1{/(option supplied|INVALID OPTION)/d}'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=--'
expect "${getopts_long_lines[0]}" == 'INVALID OPTION -- OPTARG=option--'
}
@test "${FEATURE}: long option, verbose" {
compare '-o-- -t user_arg' \
'--option-- --toggle user_arg' \
'1{/(option supplied|illegal option)/d}' \
'2{/^INVALID OPTION or MISSING ARGUMENT/d}'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=--'
expect "${getopts_long_lines[0]}" =~ 'getopts_long-\w+-verbose: illegal option -- option--$'
}
12 changes: 10 additions & 2 deletions test/bats/option_supplied.bats
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,18 @@ load ../test_helper

@test "${FEATURE}: long option, adjoined value, silent" {
compare '-ouser_val' \
'--optionuser_val'
'--optionuser_val' \
'1d'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=user_val'
expect "${getopts_long_lines[0]}" == 'INVALID OPTION -- OPTARG=optionuser_val'

}

@test "${FEATURE}: long option, adjoined value, verbose" {
compare '-ouser_val' \
'--optionuser_val'
'--optionuser_val' \
'1d' \
'2{/^INVALID OPTION or MISSING ARGUMENT/d}'
expect "${bash_getopts_lines[0]}" == 'option supplied -- OPTARG=user_val'
expect "${getopts_long_lines[0]}" =~ 'getopts_long-verbose: illegal option -- optionuser_val'
}
34 changes: 34 additions & 0 deletions test/bin/getopts-github_15b-silent
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

while getopts ":to:v:-" OPTKEY; do
case ${OPTKEY} in
'-'|'t')
printf 'toggle triggered'
;;
'o')
printf 'option supplied'
;;
'v')
printf 'value supplied'
;;
'?')
printf "INVALID OPTION"
;;
':')
printf "MISSING ARGUMENT"
;;
*)
printf "NEVER REACHED"
;;
esac
printf ' -- '
[[ -z "${OPTARG+SET}" ]] && echo 'OPTARG is unset' || echo "OPTARG=${OPTARG}"
done

shift $(( OPTIND - 1 ))

echo "OPTERR: ${OPTERR}"
echo "OPTKEY: ${OPTKEY}"
echo "OPTARG: ${OPTARG}"
echo "OPTIND: ${OPTIND}"
echo "\$@: ${*}"
31 changes: 31 additions & 0 deletions test/bin/getopts-github_15b-verbose
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env bash

while getopts "to:v:-" OPTKEY; do
case ${OPTKEY} in
'-'|'t')
printf 'toggle triggered'
;;
'o')
printf 'option supplied'
;;
'v')
printf 'value supplied'
;;
'?')
printf "INVALID OPTION or MISSING ARGUMENT"
;;
*)
printf "NEVER REACHED"
;;
esac
printf ' -- '
[[ -z "${OPTARG+SET}" ]] && echo 'OPTARG is unset' || echo "OPTARG=${OPTARG}"
done

shift $(( OPTIND - 1 ))

echo "OPTERR: ${OPTERR}"
echo "OPTKEY: ${OPTKEY}"
echo "OPTARG: ${OPTARG}"
echo "OPTIND: ${OPTIND}"
echo "\$@: ${*}"
38 changes: 38 additions & 0 deletions test/bin/getopts_long-github_15b-silent
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash

TOPDIR="$(cd "$(dirname "${0}")"/../.. && pwd)"
# shellcheck disable=SC1090
source "${TOPDIR}/lib/getopts_long.bash"

while getopts_long ':to:v: - toggle option: variable:' OPTKEY; do
case ${OPTKEY} in
'-'|'t'|'toggle')
printf 'toggle triggered'
;;
'o'|'option')
printf 'option supplied'
;;
'v'|'variable')
printf 'value supplied'
;;
'?')
printf "INVALID OPTION"
;;
':')
printf "MISSING ARGUMENT"
;;
*)
printf "NEVER REACHED"
;;
esac
printf ' -- '
[[ -z "${OPTARG+SET}" ]] && echo 'OPTARG is unset' || echo "OPTARG=${OPTARG}"
done

shift $(( OPTIND - 1 ))

echo "OPTERR: ${OPTERR}"
echo "OPTKEY: ${OPTKEY}"
echo "OPTARG: ${OPTARG}"
echo "OPTIND: ${OPTIND}"
echo "\$@: ${*}"
35 changes: 35 additions & 0 deletions test/bin/getopts_long-github_15b-verbose
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

TOPDIR="$(cd "$(dirname "${0}")"/../.. && pwd)"
# shellcheck disable=SC1090
source "${TOPDIR}/lib/getopts_long.bash"

while getopts_long 'to:v: - toggle option: variable:' OPTKEY; do
case ${OPTKEY} in
'-'|'t'|'toggle')
printf 'toggle triggered'
;;
'o'|'option')
printf 'option supplied'
;;
'v'|'variable')
printf 'value supplied'
;;
'?')
printf "INVALID OPTION or MISSING ARGUMENT"
;;
*)
printf "NEVER REACHED"
;;
esac
printf ' -- '
[[ -z "${OPTARG+SET}" ]] && echo 'OPTARG is unset' || echo "OPTARG=${OPTARG}"
done

shift $(( OPTIND - 1 ))

echo "OPTERR: ${OPTERR}"
echo "OPTKEY: ${OPTKEY}"
echo "OPTARG: ${OPTARG}"
echo "OPTIND: ${OPTIND}"
echo "\$@: ${*}"
Loading

0 comments on commit f266f21

Please sign in to comment.