diff mbox series

[15/16] Revert and amend "test-lib-functions: assert correct parameter count"

Message ID patch-15.16-0cd511206c4-20210412T110456Z-avarab@gmail.com (mailing list archive)
State Superseded
Headers show
Series test-lib.sh: new test_commit args, simplification & fixes | expand

Commit Message

Ævar Arnfjörð Bjarmason April 12, 2021, 11:09 a.m. UTC
This reverts and amends my my own e7884b353b7 (test-lib-functions:
assert correct parameter count, 2021-02-12) in order to improve the -x
output.

When I added these BUG assertions in e7884b353b7 I missed that this
made the -x output much more verbose.

E.g. for each test_cmp invocation we'd now emit:

    + test_cmp expect actual
    + test 2 -ne 2
    + eval diff -u "$@"
    + diff -u expect actual

That "test 2 -ne 2" line is new in e7884b353b7. As noted in
45a2686441b (test-lib-functions: remove bug-inducing "diagnostics"
helper param, 2021-02-12) we had buggy invocations of some of these
functions with too many parameters.

Let's instead use "$@" instead of "$1" to achieve the same ends with
no extra -x output verbosity. The "test" operator will die with an
error if given more than one argument in these contexts, so using "$@"
achieves the same goal.

The same goes for "cmp" and "diff -u" (which we typically use for
test_cmp).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/test-lib-functions.sh | 41 ++++++++++++++++-------------------------
 1 file changed, 16 insertions(+), 25 deletions(-)

Comments

Junio C Hamano April 12, 2021, 7:37 p.m. UTC | #1
Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

>  test_path_is_file () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	if ! test -f "$1"
> +	if ! test -f "$@"
>  	then
> -		echo "File $1 doesn't exist"
> +		echo "File $@ doesn't exist"
>  		return 1

What does it even mean to call

	test_path_is_file Documentation/ Makefile

with this patch applied?

If there were three files "COPYING Makefile", "COPYING", and
"Makefile", what would happen when you did

	test_path_is_file COPYING Makefile

(without dq around them)?

I think this particular medicine is far worse than the symptom it
tries to cure.
Ævar Arnfjörð Bjarmason April 15, 2021, 11:32 a.m. UTC | #2
On Mon, Apr 12 2021, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>>  test_path_is_file () {
>> -	test "$#" -ne 1 && BUG "1 param"
>> -	if ! test -f "$1"
>> +	if ! test -f "$@"
>>  	then
>> -		echo "File $1 doesn't exist"
>> +		echo "File $@ doesn't exist"
>>  		return 1
>
> What does it even mean to call
>
> 	test_path_is_file Documentation/ Makefile
>
> with this patch applied?
>
> If there were three files "COPYING Makefile", "COPYING", and
> "Makefile", what would happen when you did
>
> 	test_path_is_file COPYING Makefile
>
> (without dq around them)?
>
> I think this particular medicine is far worse than the symptom it
> tries to cure.

We'll error with:

    test: foo: unexpected operator

And then say:

    File COPYING Makefile doesn't exist

I thought guarding just for the one-off development error of not using
the function correctly wasn't worth it, but I thought it made sense not
to litter all of this with:
	
	diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
	index 28b8826e565..0bd7367a07e 100644
	--- a/t/test-lib-functions.sh
	+++ b/t/test-lib-functions.sh
	@@ -690,6 +690,7 @@ test_expect_success () {
	 test_path_is_file () {
	 	if ! test -f "$@"
	 	then
	+		test $# -eq 1 || BUG "Do not call test_path_is_file() with more than one argument!"
	 		echo "File $@ doesn't exist"
	 		return 1
	 	fi

But I could do that if you think it's better.

We could also just emit $1 in the "echo". I don't feel strongly about
that, but think that's arguably worse, since having it be $@ means the
developer is more likely to notice the invalid usage right away.
Andreas Schwab April 15, 2021, 12:31 p.m. UTC | #3
On Apr 15 2021, Ævar Arnfjörð Bjarmason wrote:

> On Mon, Apr 12 2021, Junio C Hamano wrote:
>
>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>>
>>>  test_path_is_file () {
>>> -	test "$#" -ne 1 && BUG "1 param"
>>> -	if ! test -f "$1"
>>> +	if ! test -f "$@"
>>>  	then
>>> -		echo "File $1 doesn't exist"
>>> +		echo "File $@ doesn't exist"
>>>  		return 1
>>
>> What does it even mean to call
>>
>> 	test_path_is_file Documentation/ Makefile
>>
>> with this patch applied?
>>
>> If there were three files "COPYING Makefile", "COPYING", and
>> "Makefile", what would happen when you did
>>
>> 	test_path_is_file COPYING Makefile
>>
>> (without dq around them)?
>>
>> I think this particular medicine is far worse than the symptom it
>> tries to cure.
>
> We'll error with:
>
>     test: foo: unexpected operator

If you want a single argument you should use "$*", not "$@".

Andreas.
Junio C Hamano April 15, 2021, 6:35 p.m. UTC | #4
Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Mon, Apr 12 2021, Junio C Hamano wrote:
>
>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>>
>>>  test_path_is_file () {
>>> -	test "$#" -ne 1 && BUG "1 param"
>>> -	if ! test -f "$1"
>>> +	if ! test -f "$@"
>>>  	then
>>> -		echo "File $1 doesn't exist"
>>> +		echo "File $@ doesn't exist"
>>>  		return 1
>>
>> What does it even mean to call
>>
>> 	test_path_is_file Documentation/ Makefile
>>
>> with this patch applied?
>>
>> If there were three files "COPYING Makefile", "COPYING", and
>> "Makefile", what would happen when you did
>>
>> 	test_path_is_file COPYING Makefile
>>
>> (without dq around them)?
>>
>> I think this particular medicine is far worse than the symptom it
>> tries to cure.
>
> We'll error with:
>
>     test: foo: unexpected operator

Ah, so use of "$@" was intentional.  That's clever (I thought it was
a common typo people make when they mean "$*").

Of course, it would not work if the caller did a nonsense like so:

	test_path_is_file foo -o ok

but as long as we trust that the callers would not make stupid
mistakes, this is OK.  Is that the reasoning behind this removal of
the BUG?

> I thought guarding just for the one-off development error of not using
> the function correctly wasn't worth it, but I thought it made sense not
> to litter all of this with:
> 	
> 	diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> 	index 28b8826e565..0bd7367a07e 100644
> 	--- a/t/test-lib-functions.sh
> 	+++ b/t/test-lib-functions.sh
> 	@@ -690,6 +690,7 @@ test_expect_success () {
> 	 test_path_is_file () {
> 	 	if ! test -f "$@"
> 	 	then
> 	+		test $# -eq 1 || BUG "Do not call test_path_is_file() with more than one argument!"

But this breaks our assumption that the caller would not be making
stupid mistakes, so I am not sure if it is worth it.  If we were to
have a sanity check, shouldn't we do the check upfront, like the
original?

Thanks.
SZEDER Gábor April 15, 2021, 10:05 p.m. UTC | #5
On Mon, Apr 12, 2021 at 01:09:04PM +0200, Ævar Arnfjörð Bjarmason wrote:
> This reverts and amends my my own e7884b353b7 (test-lib-functions:
> assert correct parameter count, 2021-02-12) in order to improve the -x
> output.
> 
> When I added these BUG assertions in e7884b353b7 I missed that this
> made the -x output much more verbose.
> 
> E.g. for each test_cmp invocation we'd now emit:
> 
>     + test_cmp expect actual
>     + test 2 -ne 2
>     + eval diff -u "$@"
>     + diff -u expect actual
> 
> That "test 2 -ne 2" line is new in e7884b353b7. As noted in
> 45a2686441b (test-lib-functions: remove bug-inducing "diagnostics"
> helper param, 2021-02-12) we had buggy invocations of some of these
> functions with too many parameters.
> 
> Let's instead use "$@" instead of "$1" to achieve the same ends with
> no extra -x output verbosity. The "test" operator will die with an
> error if given more than one argument in these contexts, so using "$@"
> achieves the same goal.

I prefer the current check for its explicitness over the implicit and
somewhat cryptic approach introduced in this patch.  I hope that
sooner or later I'll finish up my patch series to suppress '-x' output
from test helper functions, and then this issue will become moot
anyway.

> The same goes for "cmp" and "diff -u" (which we typically use for
> test_cmp).
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  t/test-lib-functions.sh | 41 ++++++++++++++++-------------------------
>  1 file changed, 16 insertions(+), 25 deletions(-)
> 
> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> index c46bf0ff09c..2cf72b56851 100644
> --- a/t/test-lib-functions.sh
> +++ b/t/test-lib-functions.sh
> @@ -759,39 +759,35 @@ test_external_without_stderr () {
>  # debugging-friendly alternatives to "test [-f|-d|-e]"
>  # The commands test the existence or non-existence of $1
>  test_path_is_file () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	if ! test -f "$1"
> +	if ! test -f "$@"
>  	then
> -		echo "File $1 doesn't exist"
> +		echo "File $@ doesn't exist"
>  		return 1
>  	fi
>  }
>  
>  test_path_is_dir () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	if ! test -d "$1"
> +	if ! test -d "$@"
>  	then
> -		echo "Directory $1 doesn't exist"
> +		echo "Directory $@ doesn't exist"
>  		return 1
>  	fi
>  }
>  
>  test_path_exists () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	if ! test -e "$1"
> +	if ! test -e "$@"
>  	then
> -		echo "Path $1 doesn't exist"
> +		echo "Path $@ doesn't exist"
>  		return 1
>  	fi
>  }
>  
>  # Check if the directory exists and is empty as expected, barf otherwise.
>  test_dir_is_empty () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	test_path_is_dir "$1" &&
> -	if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
> +	test_path_is_dir "$@" &&
> +	if test -n "$(ls -a1 "$@" | egrep -v '^\.\.?$')"
>  	then
> -		echo "Directory '$1' is not empty, it contains:"
> +		echo "Directory '$@' is not empty, it contains:"
>  		ls -la "$1"
>  		return 1
>  	fi
> @@ -799,17 +795,15 @@ test_dir_is_empty () {
>  
>  # Check if the file exists and has a size greater than zero
>  test_file_not_empty () {
> -	test "$#" = 2 && BUG "2 param"
> -	if ! test -s "$1"
> +	if ! test -s "$@"
>  	then
> -		echo "'$1' is not a non-empty file."
> +		echo "'$@' is not a non-empty file."
>  		return 1
>  	fi
>  }
>  
>  test_path_is_missing () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	if test -e "$1"
> +	if test -e "$@"
>  	then
>  		echo "Path $1 exists!"
>  		false
> @@ -1013,7 +1007,6 @@ test_expect_code () {
>  # - not all diff versions understand "-u"
>  
>  test_cmp () {
> -	test "$#" -ne 2 && BUG "2 param"
>  	eval "$GIT_TEST_CMP" '"$@"'
>  }
>  
> @@ -1043,7 +1036,6 @@ test_cmp_config () {
>  # test_cmp_bin - helper to compare binary files
>  
>  test_cmp_bin () {
> -	test "$#" -ne 2 && BUG "2 param"
>  	cmp "$@"
>  }
>  
> @@ -1104,12 +1096,11 @@ verbose () {
>  # otherwise.
>  
>  test_must_be_empty () {
> -	test "$#" -ne 1 && BUG "1 param"
> -	test_path_is_file "$1" &&
> -	if test -s "$1"
> +	test_path_is_file "$@" &&
> +	if test -s "$@"
>  	then
> -		echo "'$1' is not empty, it contains:"
> -		cat "$1"
> +		echo "'$@' is not empty, it contains:"
> +		cat "$@"
>  		return 1
>  	fi
>  }
> -- 
> 2.31.1.634.gb41287a30b0
>
Junio C Hamano April 15, 2021, 10:24 p.m. UTC | #6
SZEDER Gábor <szeder.dev@gmail.com> writes:

> ...  suppress '-x' output
> from test helper functions, and then this issue will become moot
> anyway.

That is certainly a better approach than tweaking each call site of
BUG.

The interface to BUG is "write a code to determine condition and
then die by calling BUG", which means under '-x' you are bound to
see the trace from "code to determine condition" part.

I wonder if introducing a BUG_ON helper function that

 - turns off '-x' trace upon entry;
 - takes a condition as one of its arguments and evals it;
 - issues a message and dies if needed;
 - otherwise arranges to turn '-x' trace on and return.

would solve it well?

Thanks.
Ævar Arnfjörð Bjarmason April 16, 2021, 9:06 p.m. UTC | #7
On Thu, Apr 15 2021, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> On Mon, Apr 12 2021, Junio C Hamano wrote:
>>
>>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>>>
>>>>  test_path_is_file () {
>>>> -	test "$#" -ne 1 && BUG "1 param"
>>>> -	if ! test -f "$1"
>>>> +	if ! test -f "$@"
>>>>  	then
>>>> -		echo "File $1 doesn't exist"
>>>> +		echo "File $@ doesn't exist"
>>>>  		return 1
>>>
>>> What does it even mean to call
>>>
>>> 	test_path_is_file Documentation/ Makefile
>>>
>>> with this patch applied?
>>>
>>> If there were three files "COPYING Makefile", "COPYING", and
>>> "Makefile", what would happen when you did
>>>
>>> 	test_path_is_file COPYING Makefile
>>>
>>> (without dq around them)?
>>>
>>> I think this particular medicine is far worse than the symptom it
>>> tries to cure.
>>
>> We'll error with:
>>
>>     test: foo: unexpected operator
>
> Ah, so use of "$@" was intentional.  That's clever (I thought it was
> a common typo people make when they mean "$*").
>
> Of course, it would not work if the caller did a nonsense like so:
>
> 	test_path_is_file foo -o ok
>
> but as long as we trust that the callers would not make stupid
> mistakes, this is OK.  Is that the reasoning behind this removal of
> the BUG?

The reasoning is to get rid of verbosity in the trace output, while
still effectively retaining the error checking.

Yes you could do "foo -o ok", but as my already-on-master fixes to the
few misuses showed we only realistically have to worry about them being
used with many normal looking file names (if that).

>> I thought guarding just for the one-off development error of not using
>> the function correctly wasn't worth it, but I thought it made sense not
>> to litter all of this with:
>> 	
>> 	diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
>> 	index 28b8826e565..0bd7367a07e 100644
>> 	--- a/t/test-lib-functions.sh
>> 	+++ b/t/test-lib-functions.sh
>> 	@@ -690,6 +690,7 @@ test_expect_success () {
>> 	 test_path_is_file () {
>> 	 	if ! test -f "$@"
>> 	 	then
>> 	+		test $# -eq 1 || BUG "Do not call test_path_is_file() with more than one argument!"
>
> But this breaks our assumption that the caller would not be making
> stupid mistakes, so I am not sure if it is worth it.  If we were to
> have a sanity check, shouldn't we do the check upfront, like the
> original?

It's fine to do sanity checking if it's in the "else" branch where we're
already emitting an error, and we only do so in the BUG case.

I.e. if it's written like e.g. this:
	
	test_path_is_file () {
		if test $# -ne 1
		then
			BUG "Do not call test_path_is_file() with more than one argument!"
		elif ! test -f "$@"
		then
			echo "File $@ doesn't exist"

Then with e.g.:

    ./t3600-rm.sh  --run=1-3 -vx

We get:
	
	+ test_path_is_file foo
	+ test 1 -ne 1
	+ test -f foo
	+ git ls-files --error-unmatch foo

But if we, as my patch does, piggy-pack on the test-built in to panic on
too many arguments we get the much more succinct:
	
	+ test_path_is_file foo
	+ test -f foo
	+ git ls-files --error-unmatch foo

I think that's the only trace output that matters, having "test 2 -ne 1"
or whatever in the case where we're just about to invoke BUG anyway is
fine.
Ævar Arnfjörð Bjarmason April 16, 2021, 11:48 p.m. UTC | #8
On Fri, Apr 16 2021, SZEDER Gábor wrote:

> On Mon, Apr 12, 2021 at 01:09:04PM +0200, Ævar Arnfjörð Bjarmason wrote:
>> This reverts and amends my my own e7884b353b7 (test-lib-functions:
>> assert correct parameter count, 2021-02-12) in order to improve the -x
>> output.
>> 
>> When I added these BUG assertions in e7884b353b7 I missed that this
>> made the -x output much more verbose.
>> 
>> E.g. for each test_cmp invocation we'd now emit:
>> 
>>     + test_cmp expect actual
>>     + test 2 -ne 2
>>     + eval diff -u "$@"
>>     + diff -u expect actual
>> 
>> That "test 2 -ne 2" line is new in e7884b353b7. As noted in
>> 45a2686441b (test-lib-functions: remove bug-inducing "diagnostics"
>> helper param, 2021-02-12) we had buggy invocations of some of these
>> functions with too many parameters.
>> 
>> Let's instead use "$@" instead of "$1" to achieve the same ends with
>> no extra -x output verbosity. The "test" operator will die with an
>> error if given more than one argument in these contexts, so using "$@"
>> achieves the same goal.
>
> I prefer the current check for its explicitness over the implicit and
> somewhat cryptic approach introduced in this patch.

Fair enough, I think it's worth it to have a bit of a non-obvious pattern there for less trace verbosity across the board.

> I hope that sooner or later I'll finish up my patch series to suppress
> '-x' output from test helper functions, and then this issue will
> become moot anyway.

That sounds like an interesting feature for those who want it, but it's
entirely orthagonal to the direction this patch is taking.

I'd like to have trace output for when tests descend into
test-lib-functions.sh and friends, I'd just like the most frequently
used code there to not be needlessly verbose.

That's not the same as wanting to hide it entirely, i.e. treat
test_path_is_file et al as though they were shell built-ins.
diff mbox series

Patch

diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index c46bf0ff09c..2cf72b56851 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -759,39 +759,35 @@  test_external_without_stderr () {
 # debugging-friendly alternatives to "test [-f|-d|-e]"
 # The commands test the existence or non-existence of $1
 test_path_is_file () {
-	test "$#" -ne 1 && BUG "1 param"
-	if ! test -f "$1"
+	if ! test -f "$@"
 	then
-		echo "File $1 doesn't exist"
+		echo "File $@ doesn't exist"
 		return 1
 	fi
 }
 
 test_path_is_dir () {
-	test "$#" -ne 1 && BUG "1 param"
-	if ! test -d "$1"
+	if ! test -d "$@"
 	then
-		echo "Directory $1 doesn't exist"
+		echo "Directory $@ doesn't exist"
 		return 1
 	fi
 }
 
 test_path_exists () {
-	test "$#" -ne 1 && BUG "1 param"
-	if ! test -e "$1"
+	if ! test -e "$@"
 	then
-		echo "Path $1 doesn't exist"
+		echo "Path $@ doesn't exist"
 		return 1
 	fi
 }
 
 # Check if the directory exists and is empty as expected, barf otherwise.
 test_dir_is_empty () {
-	test "$#" -ne 1 && BUG "1 param"
-	test_path_is_dir "$1" &&
-	if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+	test_path_is_dir "$@" &&
+	if test -n "$(ls -a1 "$@" | egrep -v '^\.\.?$')"
 	then
-		echo "Directory '$1' is not empty, it contains:"
+		echo "Directory '$@' is not empty, it contains:"
 		ls -la "$1"
 		return 1
 	fi
@@ -799,17 +795,15 @@  test_dir_is_empty () {
 
 # Check if the file exists and has a size greater than zero
 test_file_not_empty () {
-	test "$#" = 2 && BUG "2 param"
-	if ! test -s "$1"
+	if ! test -s "$@"
 	then
-		echo "'$1' is not a non-empty file."
+		echo "'$@' is not a non-empty file."
 		return 1
 	fi
 }
 
 test_path_is_missing () {
-	test "$#" -ne 1 && BUG "1 param"
-	if test -e "$1"
+	if test -e "$@"
 	then
 		echo "Path $1 exists!"
 		false
@@ -1013,7 +1007,6 @@  test_expect_code () {
 # - not all diff versions understand "-u"
 
 test_cmp () {
-	test "$#" -ne 2 && BUG "2 param"
 	eval "$GIT_TEST_CMP" '"$@"'
 }
 
@@ -1043,7 +1036,6 @@  test_cmp_config () {
 # test_cmp_bin - helper to compare binary files
 
 test_cmp_bin () {
-	test "$#" -ne 2 && BUG "2 param"
 	cmp "$@"
 }
 
@@ -1104,12 +1096,11 @@  verbose () {
 # otherwise.
 
 test_must_be_empty () {
-	test "$#" -ne 1 && BUG "1 param"
-	test_path_is_file "$1" &&
-	if test -s "$1"
+	test_path_is_file "$@" &&
+	if test -s "$@"
 	then
-		echo "'$1' is not empty, it contains:"
-		cat "$1"
+		echo "'$@' is not empty, it contains:"
+		cat "$@"
 		return 1
 	fi
 }