diff --git a/src/common.sh b/src/common.sh index f721136..aa2af42 100644 --- a/src/common.sh +++ b/src/common.sh @@ -28,6 +28,9 @@ __desc_case= # stderr file __desc_errpath="$(mktemp)" +# env file +__desc_envpath="$(mktemp)" + # most recent expect result and its exit code __desc_result= __desc_rexit=0 @@ -162,9 +165,16 @@ bail() expect() { __desc-assert-within it expect $(caller) - __desc_result="$($@ 2>"$__desc_errpath")" __desc_rexit=$? __desc-push ":expect $(caller) $@" + + # stdout remains on stdout; stderr to a tmp file; environment to a tmp + # file (note that we use declare instead of env to ensure that + # non-exported variables are also included) + __desc_result="$( + $@ 2>"$__desc_errpath" + declare -p >"$__desc_envpath" + )" } to() diff --git a/src/expect.sh b/src/expect.sh index 2d44875..56db94e 100644 --- a/src/expect.sh +++ b/src/expect.sh @@ -23,6 +23,7 @@ __INC_EXPECT_CORE=1 source expect/output.sh +source expect/env.sh ## diff --git a/src/expect/env.sh b/src/expect/env.sh new file mode 100644 index 0000000..771d0c6 --- /dev/null +++ b/src/expect/env.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Environment expectations +# +# Copyright (C) 2014 Mike Gewitz +# +# This file is part of shspec. +# +# shspec is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +## + +[ -z $__INC_EXPECT_ENV ] || return +__INC_EXPECT_ENV=1 + + +## +# Expect that an environment variable is set to a particular value +# +# The variable need not be exported. +# +_expect--set() +{ + local -ri shiftn="$2" + local -r envpath="$4" + shift "$shiftn" + + # ensure envpath is available + __chk-shiftn 4 "$shiftn" + + local -r var="$1" cmp="$2" + shift 2 + local -r expect="$@" + + # TODO: support escaped newlines; use awk (do not source the file) + local -r line="$( grep "^declare \.*-- $var=" "$envpath" )" + local -r valq="${line##*=\"}" + local -r val="${valq%%\"}" + + # cannot quote regex without causing problems, and [[ syntax does not + # allow a variable comparison operator; further, argument order varies + # with certain operators; whitelist to explicitly document support and + # prevent oddities + case "$cmp" in + =~) + [[ "$val" =~ $expect ]];; + + -[nz]) + test "$cmp" "$val";; + + =|==|!=|-eq|-ne|-lt|-le|-gt|-ge) + test "$val" $cmp "$expect";; + + *) false;; + esac +} + diff --git a/src/spec.sh b/src/spec.sh index 16cce58..3d898a7 100644 --- a/src/spec.sh +++ b/src/spec.sh @@ -33,7 +33,7 @@ source specstack.sh source expect.sh # number of internal arguments before remainder clause -declare -ir __SHIFTN=3 +declare -ir __SHIFTN=4 ## @@ -48,6 +48,9 @@ mktemp-shm() # stderr file declare -r __spec_errpath="$(mktemp-shm)" +# env dump file +declare -r __spec_envpath="$(mktemp-shm)" + # most recent expect result and its exit code declare __spec_result= declare -i __spec_rexit=0 @@ -162,7 +165,8 @@ to() _sstack-assert-follow :expect to $(caller) _sstack-pop - __handle-to "$__spec_rexit" $__SHIFTN "$__spec_errpath" "$@" \ + __handle-to "$__spec_rexit" $__SHIFTN \ + "$__spec_errpath" "$__spec_envpath" "$@" \ || fail "$*" __spec_caller= @@ -182,6 +186,7 @@ __handle-to() local -ri rexit="$1" local -ri shiftn="$2" local -r errpath="$( [ $shiftn -gt 2 ] && echo "$3" )" + local -r envpath="$( [ $shiftn -gt 3 ] && echo "$4" )" shift "$shiftn" local -r type="$1" @@ -196,7 +201,7 @@ __handle-to() # output file, and all remaining arguments are said remainder clause; the # shift argument allows the implementation to vary without breaking BC so # long as the meaning of the shifted arguments do not change - $assert $rexit $__SHIFTN "$errpath" "$@" \ + $assert $rexit $__SHIFTN "$errpath" "$envpath" "$@" \ < <( echo -n "$__spec_result" ) } diff --git a/test/test-expect-core b/test/test-expect-core index a57f786..1b222e8 100644 --- a/test/test-expect-core +++ b/test/test-expect-core @@ -188,7 +188,7 @@ describe silent # no arguments within context of the DSL, that is it accepts no arguments - expect _expect--silent 0 2 foo < <(:) + expect _expect--silent 0 2 foo < <(:) to fail end end diff --git a/test/test-expect-env b/test/test-expect-env new file mode 100644 index 0000000..e5fb3be --- /dev/null +++ b/test/test-expect-env @@ -0,0 +1,207 @@ +#!/bin/bash +# Environment expectation tests +# +# Copyright (C) 2014 Mike Gewitz +# +# This file is part of shspec. +# +# shspec is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +## + +declare -r stubenv=' +declare -- var="val" +declare -- long="foo bar baz" +declare -- empty="" +declare -- one="1"' + + +function setchk() +{ + _expect--set 0 4 <(:) <( echo "$stubenv" ) "$@" +} + + +describe set + describe = and == operators + it succeeds on string equality + expect setchk var = val + to succeed + + expect setchk var == val + to succeed + end + + it fails on string inequality + expect setchk var = unval + to fail + + expect setchk var == unval + to fail + end + end + + + describe != operator + it succeeds on string inequality + expect setchk var != foo + to succeed + end + + it fails on string equality + expect setchk var != val + to fail + end + end + + + describe =~ operator + it succeeds on a match + expect setchk \ + long =~ fo+ ba. baz\$ + to succeed + end + + # note that this also ensures that *all* arguments are part of the match + it fails on a mismatch + expect setchk \ + long =~ fo+ baX baz\$ + to fail + end + end + + + describe -n operator + it succeeds when string is non-empty + expect setchk var -n + to succeed + end + + it fails when string is empty + expect setchk empty -n + to fail + end + end + + + describe -z operator + it succeeds when string is empty + expect setchk empty -z + to succeed + end + + it fails when string is non-empty + expect setchk var -z + to fail + end + end + + + describe -eq operator + it succeeds on numeric equality + expect setchk one -eq 1 + to succeed + end + + it fails on numeric inequality + expect setchk one -eq 2 + to fail + end + end + + + describe -gt operator + it succeeds when numerically greater + expect setchk one -gt 0 + to succeed + end + + it fails when not numerically greater + expect setchk one -gt 1 + to fail + end + end + + + describe -ge operator + it succeeds when numerically greater + expect setchk one -ge 0 + to succeed + end + + it succeeds when numerically equal + expect setchk one -ge 1 + to succeed + end + + it fails when numerically less than + expect setchk one -ge 2 + to fail + end + end + + + describe -lt operator + it succeeds when numerically less than + expect setchk one -lt 2 + to succeed + end + + it fails when not numerically less than + expect setchk one -lt 1 + to fail + end + end + + + describe -le operator + it succeeds when numerically less than + expect setchk one -le 2 + to succeed + end + + it succeeds when numerically equal + expect setchk one -le 1 + to succeed + end + + it fails when numerically greater than + expect setchk one -le 0 + to fail + end + end + + + describe -ne operator + it succeeds when numerically unequal + expect setchk one -ne 2 + to succeed + end + + it fails when numerically equal + expect setchk one -ne 1 + to fail + end + end + + + # primarily for safety and strict documentation, but no other tests make + # sense at the moment + it fails on unrecognized operators + # shell injection (not that this is realistically a problem, because we + # can execute arbitrary shell code anyway) + expect setchk var "!= foo -a 1 -eq" 1 + to fail + end +end +