diff --git a/CHANGELOG.md b/CHANGELOG.md index f94531b3..193798f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - set_up_before_script - tear_down_after_script - Fix false negative from `assert_have_been_called_with` with pipes +- Fix preservation of trailing whitespace in final argument to `data_set` ## [0.24.0](https://github.com/TypedDevs/bashunit/compare/0.23.0...0.24.0) - 2025-09-14 diff --git a/src/globals.sh b/src/globals.sh index d1105b24..67fb23d8 100644 --- a/src/globals.sh +++ b/src/globals.sh @@ -128,5 +128,5 @@ function data_set() { printf ' %q' "$arg" fi done - printf '\n' + printf ' %q\n' "" } diff --git a/src/runner.sh b/src/runner.sh index 3544e382..2d7cdec1 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -114,7 +114,29 @@ function runner::parse_data_provider_args() { local arg local encoded_arg local -a args=() - # Parse args from the input string into an array, respecting quotes and escapes + + # Check for shell metacharacters that would break eval or cause globbing + local has_metachar=false + if [[ "$input" =~ [^\\][\|\&\;\*] ]] || [[ "$input" =~ ^[\|\&\;\*] ]]; then + has_metachar=true + fi + + # Try eval first (needed for $'...' from printf '%q'), unless metacharacters present + if [[ "$has_metachar" == false ]] && eval "args=($input)" 2>/dev/null && [[ ${#args[@]} -gt 0 ]]; then + # Successfully parsed - remove sentinel if present + local last_idx=$((${#args[@]} - 1)) + if [[ -z "${args[$last_idx]}" ]]; then + unset 'args[$last_idx]' + fi + # Print args and return early + for arg in "${args[@]}"; do + encoded_arg="$(helper::encode_base64 "${arg}")" + printf '%s\n' "$encoded_arg" + done + return + fi + + # Fallback: parse args from the input string into an array, respecting quotes and escapes for ((i=0; i<${#input}; i++)); do local char="${input:$i:1}" if [ "$escaped" = true ]; then diff --git a/tests/functional/provider_test.sh b/tests/functional/provider_test.sh index badcf1b4..b80cccc2 100644 --- a/tests/functional/provider_test.sh +++ b/tests/functional/provider_test.sh @@ -103,6 +103,20 @@ function provide_value_with_whitespace() { data_set "first value" "second value" } +# @data_provider provide_value_with_trailing_whitespace +function test_trailing_whitespace_in_last_value_from_data_provider() { + local expected="$1" + local actual="$2" + + assert_same "${expected}" "${actual}" +} + +function provide_value_with_trailing_whitespace() { + # Each data_set is passed the same value twice (expected, actual) to verify preservation + data_set "value " "value " + data_set "value " "value " +} + # @data_provider provide_eval_gotchas function test_eval_gotchas_from_data_provider() { input=$1 diff --git a/tests/unit/helpers_test.sh b/tests/unit/helpers_test.sh index e836c238..35fa7fb8 100644 --- a/tests/unit/helpers_test.sh +++ b/tests/unit/helpers_test.sh @@ -150,7 +150,7 @@ function test_get_provider_data_array() { } assert_same \ - "one two three" \ + "one two three ''" \ "$(helper::get_provider_data "fake_function_get_provider_data_array" "${BASH_SOURCE[0]}")" }