Pass shellcheck, more specs are handled, preserve function whitespace

This closes #49.
This commit is contained in:
Tyler Akins 2023-04-10 08:10:14 -05:00
parent e0e9189355
commit 7604ce3054
No known key found for this signature in database
GPG Key ID: 8F3B8C432F4393BD
46 changed files with 308 additions and 273 deletions

223
mo
View File

@ -15,26 +15,59 @@
#/
#/ Options:
#/
#/ --allow-function-arguments
#/ Permit functions to be called with additional arguments. Otherwise,
#/ the only way to get access to the arguments is to use the
#/ MO_FUNCTION_ARGS environment variable.
#/ -d, --debug
#/ Enable debug logging to stderr.
#/ -u, --fail-not-set
#/ Fail upon expansion of an unset variable.
#/ Fail upon expansion of an unset variable. Will silently ignore by
#/ default. Alternately, set MO_FAIL_ON_UNSET to a non-empty value.
#/ -x, --fail-on-function
#/ Fail when a function returns a non-zero status code.
#/ Fail when a function returns a non-zero status code instead of
#/ silently ignoring it. Alternately, set MO_FAIL_ON_FUNCTION to a
#/ non-empty value.
#/ -f, --fail-on-file
#/ Fail when a file (from command-line or partial) does not exist.
#/ Alternately, set MO_FAIL_ON_FILE to a non-empty value.
#/ -e, --false
#/ Treat the string "false" as empty for conditionals.
#/ Treat the string "false" as empty for conditionals. Alternately,
#/ set MO_FALSE_IS_EMPTY to a non-empty value.
#/ -h, --help
#/ This message.
#/ -s=FILE, --source=FILE
#/ Load FILE into the environment before processing templates.
#/ Can be used multiple times.
#/ -d, --debug
#/ Enable debug logging to stderr.
#
# Mo is under a MIT style licence with an additional non-advertising clause.
# See LICENSE.md for the full text.
#
# This is open source! Please feel free to contribute.
#
# https://github.com/tests-always-included/mo
#/ -- Indicate the end of options. All arguments after this will be
#/ treated as filenames only. Use when filenames may start with
#/ hyphens.
#/
#/ Mo uses the following environment variables:
#/
#/ MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
#/ functions referenced in templates to receive additional options and
#/ arguments.
#/ MO_DEBUG - When set to a non-empty value, additional debug information is
#/ written to stderr.
#/ MO_FUNCTION_ARGS - Arguments passed to the function.
#/ MO_FAIL_ON_FILE - If a filename from the command-line is missing or a
#/ partial does not exist, abort with an error.
#/ MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
#/ with an error.
#/ MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
#/ variable will be aborted with an error.
#/ MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will
#/ be treated as an empty value for the purposes of conditionals.
#/ MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
#/ help message.
#/
#/ Mo is under a MIT style licence with an additional non-advertising clause.
#/ See LICENSE.md for the full text.
#/
#/ This is open source! Please feel free to contribute.
#/
#/ https://github.com/tests-always-included/mo
# Public: Template parser function. Writes templates to stdout.
@ -42,63 +75,7 @@
# $0 - Name of the mo file, used for getting the help message.
# $@ - Filenames to parse.
#
# Options:
#
# --allow-function-arguments
#
# Permit functions in templates to be called with additional arguments. This
# puts template data directly in to the path of an eval statement. Use with
# caution. Not listed in the help because it only makes sense when mo is
# sourced.
#
# -u, --fail-not-set
#
# Fail upon expansion of an unset variable. Default behavior is to silently
# ignore and expand into empty string.
#
# -x, --fail-on-function
#
# Fail when a function used by a template returns an error status code.
# Alternately, ou may set the MO_FAIL_ON_FUNCTION environment variable to a
# non-empty value to enable this behavior.
#
# -e, --false
#
# Treat "false" as an empty value. You may set the MO_FALSE_IS_EMPTY
# environment variable instead to a non-empty value to enable this behavior.
#
# -h, --help
#
# Display a help message.
#
# -s=FILE, --source=FILE
#
# Source a file into the environment before processing template files.
# This can be used multiple times.
#
# --
#
# Used to indicate the end of options. You may optionally use this when
# filenames may start with two hyphens.
#
# Mo uses the following environment variables:
#
# MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
# functions referenced in templates to receive additional
# options and arguments. This puts the content from the
# template directly into an eval statement. Use with extreme
# care.
# MO_DEBUG - When set to a non-empty value, additional debug information is
# written to stderr.
# MO_FUNCTION_ARGS - Arguments passed to the function.
# MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
# with an error.
# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
# variable will be aborted with an error.
# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will be
# treated as an empty value for the purposes of conditionals.
# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
# help message.
# See the comment above for details.
#
# Returns nothing.
mo() (
@ -140,6 +117,11 @@ mo() (
MO_FAIL_ON_FUNCTION=true
;;
-p | --fail-on-file)
# shellcheck disable=SC2030
MO_FAIL_ON_FILE=true
;;
-e | --false)
# shellcheck disable=SC2030
MO_FALSE_IS_EMPTY=true
@ -162,6 +144,7 @@ mo() (
;;
-d | --debug)
# shellcheck disable=SC2030
MO_DEBUG=true
;;
@ -170,6 +153,10 @@ mo() (
moDoubleHyphens=true
;;
-*)
mo::error "Unknown option: $arg (See --help for options)"
;;
*)
#: Every arg that is not a flag or a option should be a file
moFiles=(${moFiles[@]+"${moFiles[@]}"} "$arg")
@ -192,6 +179,7 @@ mo() (
#
# Returns nothing.
mo::debug() {
# shellcheck disable=SC2031
if [[ -n "${MO_DEBUG:-}" ]]; then
echo "DEBUG: $1" >&2
fi
@ -205,7 +193,7 @@ mo::debug() {
# Returns nothing. Exits the program.
mo::error() {
echo "ERROR: $1" >&2
exit ${2:-1}
exit "${2:-1}"
}
@ -264,17 +252,26 @@ mo::content() {
#
# Returns nothing.
mo::contentFile() {
local moContent moLen
local moContent moFile
# The subshell removes any trailing newlines. We forcibly add
# a dot to the content to preserve all newlines.
# As a future optimization, it would be worth considering removing
# cat and replacing this with a read loop.
mo::debug "Loading content: ${2:-/dev/stdin}"
moContent=$(cat -- "${2:-/dev/stdin}" && echo '.') || return 1
moLen=$((${#moContent} - 1))
moContent=${moContent:0:$moLen} # Remove last dot
moFile=${2:-/dev/stdin}
# shellcheck disable=SC2031
if [[ -e "$moFile" ]]; then
mo::debug "Loading content: $moFile"
moContent=$(cat -- "$moFile" && echo '.') || return 1
moContent=${moContent%.} # Remove last dot
elif [[ -n "${MO_FAIL_ON_FILE-}" ]]; then
mo::error "No such file: $moFile"
else
mo::debug "File does not exist: $moFile"
moContent=""
fi
local "$1" && mo::indirect "$1" "$moContent"
}
@ -384,9 +381,9 @@ mo::trim() {
while [[ "$moContent" != "$moLast" ]]; do
moLast=$moContent
moContent=${moContent# }
moContent=${moContent#$moR}
moContent=${moContent#$moN}
moContent=${moContent#$moT}
moContent=${moContent#"$moR"}
moContent=${moContent#"$moN"}
moContent=${moContent#"$moT"}
done
local "$1" && mo::indirect "$1" "$moContent"
@ -406,9 +403,9 @@ mo::chomp() {
moN=$'\n'
moT=$'\t'
moTemp=${2%% *}
moTemp=${moTemp%%$moR*}
moTemp=${moTemp%%$moN*}
moTemp=${moTemp%%$moT*}
moTemp=${moTemp%%"$moR"*}
moTemp=${moTemp%%"$moN"*}
moTemp=${moTemp%%"$moT"*}
local "$1" && mo::indirect "$1" "$moTemp"
}
@ -440,7 +437,7 @@ mo::chomp() {
#
# Returns nothing.
mo::parse() {
local moContent moCurrent moOpenDelimiter moCloseDelimieter moResult moSplit moParseChunk moFastMode moStandaloneContent moRemainder
local moContent moCurrent moOpenDelimiter moCloseDelimiter moResult moSplit moParseChunk moFastMode moStandaloneContent moRemainder
moContent=$2
moCurrent=$3
moCurrentBlock=$4
@ -777,15 +774,15 @@ mo::parsePartial() {
moCloseDelimiter=$5
moFastMode=$6
moStandaloneContent=$7
mo::chomp moFilename "${moContent%%$moCloseDelimiter*}"
moContent="${moContent#*$moCloseDelimiter}"
mo::chomp moFilename "${moContent%%"$moCloseDelimiter"*}"
moContent="${moContent#*"$moCloseDelimiter"}"
moIndentation=""
if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then
moN=$'\n'
moR=$'\r'
moIndentation="$moN${moPrevious//$moR/$moN}"
moIndentation=${moIndentation##*$moN}
moIndentation="$moN${moPrevious//"$moR"/"$moN"}"
moIndentation=${moIndentation##*"$moN"}
mo::debug "Adding indentation to partial: '$moIndentation'"
mo::standaloneProcessBefore moPrevious "$moPrevious"
mo::standaloneProcessAfter moContent "$moContent"
@ -796,7 +793,6 @@ mo::parsePartial() {
if [[ -n "$moFastMode" ]]; then
moResult=""
moLen=0
else
mo::debug "Parsing partial: $moFilename"
@ -901,7 +897,7 @@ mo::parseComment() {
moContent=$3
moCloseDelimiter=$4
moStandaloneContent=$5
moContent=${moContent#*$moCloseDelimiter}
moContent=${moContent#*"$moCloseDelimiter"}
mo::debug "Parsing comment"
if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then
@ -942,8 +938,8 @@ mo::parseDelimiter() {
mo::chomp moOpen "$moContent"
moContent=${moContent:${#moOpen}}
mo::trim moContent "$moContent"
moClose="${moContent%%=$moCloseDelimiter*}"
moContent=${moContent#*=$moCloseDelimiter}
moClose="${moContent%%="$moCloseDelimiter"*}"
moContent=${moContent#*="$moCloseDelimiter"}
mo::debug "Parsing delimiters: $moOpen $moClose"
if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then
@ -983,7 +979,7 @@ mo::parseValue() {
moOpenDelimiter=$5
moCloseDelimiter=$6
moFastMode=$7
mo::trim moContent "${moContentOriginal#$moOpenDelimiter}"
mo::trim moContent "${moContentOriginal#"$moOpenDelimiter"}"
mo::parseValueInner moArgs "$moContent" "$moCurrent" "$moCloseDelimiter"
moContent=${moArgs[0]}
@ -1252,7 +1248,7 @@ mo::getArgumentDefault() {
local moTemp moContent
moTemp=$2
mo::chomp moTemp "${moTemp%%$3*}"
mo::chomp moTemp "${moTemp%%"$3"*}"
moTemp=${moTemp%%)*}
moTemp=${moTemp%%\}*}
moContent=${2:${#moTemp}}
@ -1384,6 +1380,7 @@ mo::isTruthy() {
moTruthy=true
# shellcheck disable=SC2031
if [[ -z "${1-}" ]]; then
moTruthy=false
elif [[ -n "${MO_FALSE_IS_EMPTY-}" ]] && [[ "${1-}" == "false" ]]; then
@ -1533,7 +1530,7 @@ mo::evaluateKey() {
#
# Returns nothing.
mo::evaluateVariable() {
local moResult moCurrent moArg moNameParts moJoined moKey moValue
local moResult moCurrent moArg moNameParts
moArg=$2
moCurrent=$3
@ -1543,7 +1540,7 @@ mo::evaluateVariable() {
if [[ -z "${moNameParts[1]}" ]]; then
if mo::isArray "$moArg"; then
eval mo::join moResult "," "\${$moArg[@]}"
eval mo::join moResult "," "\${${moArg}[@]}"
else
# shellcheck disable=SC2031
if mo::isVarSet "$moArg"; then
@ -1560,7 +1557,7 @@ mo::evaluateVariable() {
fi
fi
local $1 && mo::indirect "$1" "$moResult"
local "$1" && mo::indirect "$1" "$moResult"
}
@ -1648,7 +1645,7 @@ moJoin() {
#
# Returns nothing.
mo::evaluateFunction() {
local moArgs moContent moFunctionArgs moFunctionResult moTarget moFunction moTemp moFunctionCall
local moArgs moContent moFunctionResult moTarget moFunction moTemp moFunctionCall
moTarget=$1
moContent=$2
@ -1675,16 +1672,18 @@ mo::evaluateFunction() {
fi
mo::debug "Calling function: $moFunctionCall"
moContent=$(export MO_FUNCTION_ARGS=("${moArgs[@]}"); echo -n "$moContent" | eval "$moFunctionCall") || {
# Call the function in a subshell for safety. Employ the trick to preserve
# whitespace at the end of the output.
moContent=$(export MO_FUNCTION_ARGS=("${moArgs[@]}"); echo -n "$moContent" | eval "$moFunctionCall ; moFunctionResult=\$? ; echo -n '.' ; exit \"\$moFunctionResult\"") || {
moFunctionResult=$?
# shellcheck disable=SC2031
if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
mo::error "Function '$moFunction' with args (${moArgs[@]+"${moArgs[@]}"}) failed with status code $moFunctionResult" "$moFunctionResult"
mo::error "Function failed with status code $moFunctionResult: $moFunctionCall" "$moFunctionResult"
fi
}
# shellcheck disable=SC2031
local "$moTarget" && mo::indirect "$moTarget" "$moContent"
local "$moTarget" && mo::indirect "$moTarget" "${moContent%.}"
}
@ -1704,7 +1703,7 @@ mo::standaloneCheck() {
moT=$'\t'
# Check the content before
moContent=${1//$moR/$moN}
moContent=${1//"$moR"/"$moN"}
if [[ "$moContent" != *"$moN"* ]]; then
mo::debug "Not a standalone tag - no newline before"
@ -1712,8 +1711,8 @@ mo::standaloneCheck() {
return 1
fi
moContent=${moContent##*$moN}
moContent=${moContent//$moT/}
moContent=${moContent##*"$moN"}
moContent=${moContent//"$moT"/}
moContent=${moContent// /}
if [[ -n "$moContent" ]]; then
@ -1723,9 +1722,9 @@ mo::standaloneCheck() {
fi
# Check the content after
moContent=${2//$moR/$moN}
moContent=${moContent%%$moN*}
moContent=${moContent//$moT/}
moContent=${2//"$moR"/"$moN"}
moContent=${moContent%%"$moN"*}
moContent=${moContent//"$moT"/}
moContent=${moContent// /}
if [[ -n "$moContent" ]]; then
@ -1756,7 +1755,7 @@ mo::standaloneProcessBefore() {
while [[ "$moLast" != "$moContent" ]]; do
moLast=$moContent
moContent=${moContent% }
moContent=${moContent%$moT}
moContent=${moContent%"$moT"}
done
local "$1" && mo::indirect "$1" "$moContent"
@ -1783,11 +1782,11 @@ mo::standaloneProcessAfter() {
while [[ "$moLast" != "$moContent" ]]; do
moLast=$moContent
moContent=${moContent# }
moContent=${moContent#$moT}
moContent=${moContent#"$moT"}
done
moContent=${moContent#$moR}
moContent=${moContent#$moN}
moContent=${moContent#"$moR"}
moContent=${moContent#"$moN"}
local "$1" && mo::indirect "$1" "$moContent"
}
@ -1816,8 +1815,8 @@ mo::indentLines() {
mo::debug "Applying indentation: '${moIndentation}'"
while [[ -n "$moContent" ]]; do
moChunk=${moContent%%$moN*}
moChunk=${moChunk%%$moR*}
moChunk=${moContent%%"$moN"*}
moChunk=${moChunk%%"$moR"*}
moContent=${moContent:${#moChunk}}
if [[ -n "$moChunk" ]]; then

View File

@ -11,6 +11,12 @@ const fsPromises = require("fs").promises;
//
// To override any test property, just define that property.
const testOverrides = {
"Comments -> Variable Name Collision": {
// Can't use variables with exclamation points easily
data: {
comment: 4
}
},
"Interpolation -> HTML Escaping": {
skip: "HTML escaping is not supported"
},
@ -20,6 +26,9 @@ const testOverrides = {
"Lambdas -> Escaping": {
skip: "HTML escaping is not supported"
},
"Partials -> Recursion": {
skip: "Complex objects are not supported and context is reset to the global level, so the recursion will loop forever"
},
"Sections -> Deeply Nested Contexts": {
skip: "Nested objects are not supported"
},
@ -268,6 +277,7 @@ function applyTestOverrides(test) {
test[key] = value;
}
test.overridesApplied = true;
test.valuesBeforeOverride = originals;
}
@ -310,6 +320,7 @@ function processSpecFile(filename) {
testSet.pass = 0;
testSet.fail = 0;
testSet.skip = 0;
testSet.passOverride = 0;
for (const test of testSet.tests) {
if (test.isFailure) {
@ -318,10 +329,14 @@ function processSpecFile(filename) {
testSet.skip += 1;
} else {
testSet.pass += 1;
if (test.overridesApplied) {
testSet.passOverride += 1;
}
}
}
console.log(
`### ${testSet.name} Results = ${testSet.pass} passed, ${testSet.fail} failed, ${testSet.skip} skipped`
`### ${testSet.name} Results = ${testSet.pass} passed (with ${testSet.passOverride} overridden), ${testSet.fail} failed, ${testSet.skip} skipped`
);
return testSet;
@ -344,16 +359,18 @@ processArraySequentially(process.argv.slice(2), processSpecFile).then(
let pass = 0,
fail = 0,
skip = 0,
total = 0;
total = 0,
passOverride = 0;
for (const testSet of result) {
pass += testSet.pass;
fail += testSet.fail;
skip += testSet.skip;
total += testSet.tests.length;
passOverride += testSet.passOverride;
console.log(
`* ${testSet.name}: ${testSet.tests.length} total, ${testSet.pass} pass, ${testSet.fail} fail, ${testSet.skip} skip`
`* ${testSet.name}: ${testSet.tests.length} total, ${testSet.pass} pass (with ${passOverride} overridden), ${testSet.fail} fail, ${testSet.skip} skip`
);
for (const test of testSet.tests) {
@ -365,7 +382,7 @@ processArraySequentially(process.argv.slice(2), processSpecFile).then(
console.log("");
console.log(
`Final result: ${total} total, ${pass} pass, ${fail} fail, ${skip} skip`
`Final result: ${total} total, ${pass} pass (with ${passOverride} overridden), ${fail} fail, ${skip} skip`
);
if (fail) {

View File

@ -52,6 +52,8 @@ runTest() (
if [[ -n "${MO_DEBUG_TEST-}" ]]; then
declare -p testExpected
# Align the two declare outputs
echo -n " "
declare -p testActual
fi

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
thing="Works"
template="{{&thing}}"
expected="Works"
export thing="Works"
export template="{{&thing}}"
export expected="Works"
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
repo=( "resque" "hub" "rip" )
export repo=( "resque" "hub" "rip" )
template() {
cat <<EOF
{{#repo}}

View File

@ -6,6 +6,7 @@ declare -A repo
repo[resque]="Resque"
repo[hub]="Hub"
repo[rip]="Rip"
export repo
template() {
cat <<EOF
{{#repo}}

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
template="Wor{{!comment}}ks"
expected="Works"
export template="Wor{{!comment}}ks"
export expected="Works"
runTest

View File

@ -10,10 +10,6 @@ run through multiple
lines}}.</h1>
EOF
}
expected() {
cat <<EOF
<h1>Today.</h1>
EOF
}
export expected=$'<h1>Today.</h1>\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
template="Wor{{! comment }}ks"
expected="Works"
export template="Wor{{! comment }}ks"
export expected="Works"
runTest

View File

@ -2,9 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
thing="Wor"
thing2="ks"
template="{{thing thing2}}"
expected="Works"
export thing="Wor"
export thing2="ks"
export template="{{thing thing2}}"
export expected="Works"
runTest

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
thing="Works"
template="{{=| |=}}|thing|"
expected="Works"
export thing="Works"
export template="{{=| |=}}|thing|"
export expected="Works"
runTest

View File

@ -2,9 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
arguments=(-- --help)
returnCode=1
template=""
expected="cat: --help: No such file or directory"$'\n'
export arguments=(--fail-on-file -- --help)
export returnCode=1
export template=""
export expected=$'ERROR: No such file: --help\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
template='{{"Works"}}'
expected="Works"
export template='{{"Works"}}'
export expected="Works"
runTest

View File

@ -3,10 +3,10 @@ cd "${0%/*}" || exit 1
. ../run-tests
unset __NO_SUCH_VAR
POPULATED="words"
EMPTY=""
arguments=(--fail-not-set)
returnCode=1
export POPULATED="words"
export EMPTY=""
export arguments=(--fail-not-set)
export returnCode=1
template() {
cat <<EOF
@ -15,10 +15,6 @@ Empty: {{EMPTY}};
Unset: {{__NO_SUCH_VAR}};
EOF
}
expected() {
cat <<EOF
ERROR: Environment variable not set: __NO_SUCH_VAR
EOF
}
export expected=$'ERROR: Environment variable not set: __NO_SUCH_VAR\n'
runTest

View File

@ -5,14 +5,9 @@ cd "${0%/*}" || exit 1
failFunction() {
false
}
arguments=(--fail-on-function)
returnCode=1
template="Fail on function? {{failFunction}}"
expected() {
cat <<EOF
ERROR: Function 'failFunction' with args () failed with status code 1
EOF
}
export arguments=(--fail-on-function)
export returnCode=1
export template="Fail on function? {{failFunction}}"
export expected=$'ERROR: Function failed with status code 1: "failFunction"\n'
runTest

View File

@ -2,9 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
USER=j.doe
ADMIN=false
arguments=(--false)
export USER=j.doe
export ADMIN=false
export arguments=(--false)
template() {
cat <<EOF
The user {{USER}} exists.
@ -13,10 +13,6 @@ WRONG - should not be an admin.
{{/ADMIN}}
EOF
}
expected() {
cat <<EOF
The user j.doe exists.
EOF
}
export expected=$'The user j.doe exists.\n'
runTest

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
USER=j.doe
ADMIN=false
export USER=j.doe
export ADMIN=false
MO_FALSE_IS_EMPTY=yeppers
template() {
cat <<EOF
@ -13,10 +13,6 @@ WRONG - should not be an admin.
{{/ADMIN}}
EOF
}
expected() {
cat <<EOF
The user j.doe exists.
EOF
}
export expected=$'The user j.doe exists.\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
person=""
export person=""
template() {
cat <<EOF
Shown.
@ -11,10 +11,6 @@ Shown.
{{/person}}
EOF
}
expected() {
cat <<EOF
Shown.
EOF
}
export expected=$'Shown.\n'
runTest

View File

@ -2,9 +2,10 @@
cd "${0%/*}" || exit 1
. ../run-tests
name=Willy
export name=Willy
wrapped() {
# This eats the newline in the content
# Wrapping 'cat' in a subshell eats the trailing whitespace
# The echo adds a newline, which is preserved.
echo "<b>$(cat)</b>"
}
template() {
@ -15,10 +16,6 @@ template() {
... this is the last line.
EOF
}
expected() {
cat <<EOF
<b> Willy is awesome.</b>... this is the last line.
EOF
}
export expected=$'<b> Willy is awesome.</b>\n... this is the last line.\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
name=Willy
export name=Willy
MO_ALLOW_FUNCTION_ARGUMENTS=true
pipeTo() {

View File

@ -3,8 +3,10 @@ cd "${0%/*}" || exit 1
. ../run-tests
testArgs() {
local args=$(declare -p MO_FUNCTION_ARGS)
echo "${args#*=}"
local args
# shellcheck disable=SC2031
args=$(declare -p MO_FUNCTION_ARGS)
echo -n "${args#*=}"
}
template() {
cat <<EOF

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
STR=abc
DATA=(111 222)
export STR=abc
export DATA=(111 222)
template() {
cat <<EOF
Issue #7

View File

@ -2,10 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
template=""
arguments=(--help)
export arguments=(--help)
expected() {
cat <<EOF
cat <<'EOF'
Mo is a mustache template rendering software written in bash. It inserts
environment variables into templates.
@ -21,19 +20,59 @@ Simple usage:
Options:
--allow-function-arguments
Permit functions to be called with additional arguments. Otherwise,
the only way to get access to the arguments is to use the
MO_FUNCTION_ARGS environment variable.
-d, --debug
Enable debug logging to stderr.
-u, --fail-not-set
Fail upon expansion of an unset variable.
Fail upon expansion of an unset variable. Will silently ignore by
default. Alternately, set MO_FAIL_ON_UNSET to a non-empty value.
-x, --fail-on-function
Fail when a function returns a non-zero status code.
Fail when a function returns a non-zero status code instead of
silently ignoring it. Alternately, set MO_FAIL_ON_FUNCTION to a
non-empty value.
-f, --fail-on-file
Fail when a file (from command-line or partial) does not exist.
Alternately, set MO_FAIL_ON_FILE to a non-empty value.
-e, --false
Treat the string "false" as empty for conditionals.
Treat the string "false" as empty for conditionals. Alternately,
set MO_FALSE_IS_EMPTY to a non-empty value.
-h, --help
This message.
-s=FILE, --source=FILE
Load FILE into the environment before processing templates.
Can be used multiple times.
-d, --debug
Enable debug logging to stderr.
-- Indicate the end of options. All arguments after this will be
treated as filenames only. Use when filenames may start with
hyphens.
Mo uses the following environment variables:
MO_ALLOW_FUNCTION_ARGUMENTS - When set to a non-empty value, this allows
functions referenced in templates to receive additional options and
arguments.
MO_DEBUG - When set to a non-empty value, additional debug information is
written to stderr.
MO_FUNCTION_ARGS - Arguments passed to the function.
MO_FAIL_ON_FILE - If a filename from the command-line is missing or a
partial does not exist, abort with an error.
MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
with an error.
MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset env
variable will be aborted with an error.
MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" will
be treated as an empty value for the purposes of conditionals.
MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
help message.
Mo is under a MIT style licence with an additional non-advertising clause.
See LICENSE.md for the full text.
This is open source! Please feel free to contribute.
https://github.com/tests-always-included/mo
MO_VERSION=3.0.0
EOF

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
thisIsTrue=true
export thisIsTrue=true
template() {
cat <<EOF
With spacing

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
boolean=true
template=$' | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n'
expected=$' | \n | \n'
export boolean=true
export template=$' | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n'
export expected=$' | \n | \n'
runTest

View File

@ -2,14 +2,10 @@
cd "${0%/*}" || exit 1
. ../run-tests
person=""
template=""
returnCode=1
arguments=(--something)
expected() {
cat <<EOF
cat: --something: No such file or directory
EOF
}
export person=""
export template=""
export returnCode=1
export arguments=(--something)
export expected=$'ERROR: Unknown option: --something (See --help for options)\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
repo=()
export repo=()
template() {
cat <<EOF
{{#repo}}
@ -13,10 +13,6 @@ template() {
{{/repo}}
EOF
}
expected() {
cat <<EOF
No repos :(
EOF
}
export expected=$' No repos :(\n'
runTest

View File

@ -2,13 +2,15 @@
cd "${0%/*}" || exit 1
. ../run-tests
a=foo
b=wrong
export a=foo
export b=wrong
declare -A sec
sec=([b]="bar")
export sec
declare -A c
c=([d]="baz")
template="{{#sec}}{{a}} {{b}} {{c.d}}{{/sec}}"
expected="foo bar baz"
export c
export template="{{#sec}}{{a}} {{b}} {{c.d}}{{/sec}}"
export expected="foo bar baz"
runTest

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
name="Chris"
company="<b>GitHub</b>"
export name="Chris"
export company="<b>GitHub</b>"
template() {
cat <<EOF
* .{{name}}.

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
multilineData=$'line 1\nline 2'
export multilineData=$'line 1\nline 2'
template() {
cat <<EOF
Partial:
@ -26,7 +26,10 @@ Indented:
line 1
line 2
EOF
# This one looks odd, but if you check the spec spec/specs/partials.yaml, name "Standalone Indentation" (mirrors "standalone-indentation" in tests/), then the spec clearly shows that the indentation is applied before rendering.
# This one looks odd, but if you check the spec spec/specs/partials.yaml,
# name "Standalone Indentation" (mirrors "standalone-indentation" in
# tests/), then the spec clearly shows that the indentation is applied
# before rendering.
}
runTest

View File

@ -2,12 +2,12 @@
cd "${0%/*}" || exit 1
. ../run-tests
USER=jwerle
GENDER=male
THING=apple
COLOR=red
PERSON=tobi
ADJECTIVE=cool
export USER=jwerle
export GENDER=male
export THING=apple
export COLOR=red
export PERSON=tobi
export ADJECTIVE=cool
template() {
cat <<EOF
{{! this is a comment }}

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
template="Works"
expected="Works"
export template="Works"
export expected="Works"
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
names=( "Tyler" )
export names=( "Tyler" )
template() {
cat <<EOF
<h2>Names</h2>

9
tests/partial-bad-file Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
cd "${0%/*}" || exit 1
. ../run-tests
# This file intentionally does not exist
export template="{{>fixtures/partial-bad-file.partial}}"
export expected=""
runTest

View File

@ -2,8 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
returnCode=1
person=""
export returnCode=1
export arguments=(--fail-on-file)
export person=""
template() {
cat <<EOF
Won't be there: {{> fixtures/partial-missing.partial}}
@ -11,7 +12,7 @@ EOF
}
expected() {
cat <<EOF
cat: partial-missing.partial: No such file or directory
ERROR: No such file: partial-missing.partial
EOF
}

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
template="{{'Works'}}"
expected="Works"
export template="{{'Works'}}"
export expected="Works"
runTest

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
thing="Works"
template="{{thing}}"
expected="Works"
export thing="Works"
export template="{{thing}}"
export expected="Works"
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
arguments=(--source=fixtures/source.vars)
export arguments=(--source=fixtures/source.vars)
template() {
cat <<EOF
{{VAR}}

View File

@ -2,13 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
arguments=(--source=invalid)
returnCode=1
template="Do not display this"
expected() {
cat <<EOF
No such file: invalid
EOF
}
export arguments=(--source=invalid)
export returnCode=1
export template="Do not display this"
export expected=$'No such file: invalid\n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
arguments=(--source=fixtures/source-multiple-1.vars --source=fixtures/source-multiple-2.vars)
export arguments=(--source=fixtures/source-multiple-1.vars --source=fixtures/source-multiple-2.vars)
template() {
cat <<EOF
A: {{A}}

View File

@ -2,9 +2,9 @@
cd "${0%/*}" || exit 1
. ../run-tests
arguments=(--source=)
returnCode=1
template="Do not display this"
expected=$'No such file: \n'
export arguments=(--source=)
export returnCode=1
export template="Do not display this"
export expected=$'No such file: \n'
runTest

View File

@ -2,7 +2,7 @@
cd "${0%/*}" || exit 1
. ../run-tests
content=$'<\n->'
export content=$'<\n->'
template() {
cat <<EOF
\

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
thing="Works"
template="{{{thing}}}"
expected="Works"
export thing="Works"
export template="{{{thing}}}"
export expected="Works"
runTest

View File

@ -2,10 +2,10 @@
cd "${0%/*}" || exit 1
. ../run-tests
NAME="Chris"
VALUE=10000
TAXED_VALUE=6000
IN_CA=true
export NAME="Chris"
export VALUE=10000
export TAXED_VALUE=6000
export IN_CA=true
template() {
cat <<EOF
Hello {{NAME}}

View File

@ -3,8 +3,8 @@ cd "${0%/*}" || exit 1
. ../run-tests
declare -A a
a=()
template="o{{#a.b}}WRONG{{/a.b}}k"
expected="ok"
export a=()
export template="o{{#a.b}}WRONG{{/a.b}}k"
export expected="ok"
runTest

View File

@ -2,8 +2,8 @@
cd "${0%/*}" || exit 1
. ../run-tests
foo=bar
template="{{#foo}}{{.}} is {{foo}}{{/foo}}"
expected="bar is bar"
export foo=bar
export template="{{#foo}}{{.}} is {{foo}}{{/foo}}"
export expected="bar is bar"
runTest