diff mbox series

[2/2] t6402: check exit status of ls-files

Message ID 20210421104102.3409-2-congdanhqx@gmail.com (mailing list archive)
State New
Headers show
Series [1/2] t6400: check exit status of ls-files | expand

Commit Message

Đoàn Trần Công Danh April 21, 2021, 10:41 a.m. UTC
We will lose the exit status of "git ls-files" if it's being run in
anywhere-but-not-final part of a pipe.

Let's send the output of "git ls-files" to a file first,
and adjust the expected result for "git ls-files -o" since a new
untracked file will be created as a side effect.

Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
---
 t/t6402-merge-rename.sh | 148 +++++++++++++++++++++++++++-------------
 1 file changed, 101 insertions(+), 47 deletions(-)

Comments

Eric Sunshine April 21, 2021, 4:55 p.m. UTC | #1
On Wed, Apr 21, 2021 at 6:41 AM Đoàn Trần Công Danh
<congdanhqx@gmail.com> wrote:
> We will lose the exit status of "git ls-files" if it's being run in
> anywhere-but-not-final part of a pipe.
>
> Let's send the output of "git ls-files" to a file first,
> and adjust the expected result for "git ls-files -o" since a new
> untracked file will be created as a side effect.
>
> Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
> ---
> diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
> @@ -631,20 +663,33 @@ test_expect_success 'check handling of differently renamed file with D/F conflic
> -               test 5 -eq "$(git ls-files -s | wc -l)" &&
> -               test 3 -eq "$(git ls-files -u | wc -l)" &&
> -               test 1 -eq "$(git ls-files -u one~HEAD | wc -l)" &&
> +               git ls-files -s >output &&
> +               test_line_count = 5 output &&
> +               git ls-files -u >output &&
> +               test_line_count = 3 output &&
> +               git ls-files -u one~HEAD >output &&
> +               test_line_count = 1 output &&

This idiom crops up so frequently in this test script that it almost
begs for the introduction of a helper function rather than applying
the manual transformation repeatedly. For instance, the helper might
be called like this:

    count_ls_files 5 -s &&
    count_ls_files 3 -u &&
    count_ls_files 1 -u one~HEAD &&
    ...

The nice thing about having a helper function is that it can clean up
after itself by not leaving a new file lying around, thus you wouldn't
have to make adjustments to the expected number of untracked files (as
mentioned in the commit message). If this is the sort of thing which
comes up often enough (if there are more such cases beyond the two
scripts you changed in this series), then it might make sense to
promote the helper function to test-lib-functions.sh.
Eric Sunshine April 21, 2021, 5:32 p.m. UTC | #2
On Wed, Apr 21, 2021 at 12:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>     count_ls_files 5 -s &&
>     count_ls_files 3 -u &&
>     count_ls_files 1 -u one~HEAD &&
>     ...
>
> The nice thing about having a helper function is that it can clean up
> after itself by not leaving a new file lying around, thus you wouldn't
> have to make adjustments to the expected number of untracked files (as
> mentioned in the commit message). If this is the sort of thing which
> comes up often enough (if there are more such cases beyond the two
> scripts you changed in this series), then it might make sense to
> promote the helper function to test-lib-functions.sh.

The frequency with which this idiom crops up with commands beyond
git-ls-files suggests the more general solution of supporting it
directly in test-lib-functions.sh for any command. For instance:

    test_cmd_line_count = 3 git ls-files -u &&

Or, perhaps, a new mode of test_line_count():

    test_line_count = 3 -c git ls-files -u &&
Junio C Hamano April 21, 2021, 11:32 p.m. UTC | #3
Eric Sunshine <sunshine@sunshineco.com> writes:

> On Wed, Apr 21, 2021 at 12:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>>     count_ls_files 5 -s &&
>>     count_ls_files 3 -u &&
>>     count_ls_files 1 -u one~HEAD &&
>>     ...
>>
>> The nice thing about having a helper function is that it can clean up
>> after itself by not leaving a new file lying around, thus you wouldn't
>> have to make adjustments to the expected number of untracked files (as
>> mentioned in the commit message). If this is the sort of thing which
>> comes up often enough (if there are more such cases beyond the two
>> scripts you changed in this series), then it might make sense to
>> promote the helper function to test-lib-functions.sh.
>
> The frequency with which this idiom crops up with commands beyond
> git-ls-files suggests the more general solution of supporting it
> directly in test-lib-functions.sh for any command. For instance:
>
>     test_cmd_line_count = 3 git ls-files -u &&
>
> Or, perhaps, a new mode of test_line_count():
>
>     test_line_count = 3 -c git ls-files -u &&

That looks nice on paper, but may be going too far.

We may want to count the lines in the error message, or we may want
to count the lines after filtering the output with pipe.

A test file that is dedicated to test ls-files with a file local
helper "count_ls_files" smells like a better place to stop, at least
to me.
Ævar Arnfjörð Bjarmason April 22, 2021, 12:45 p.m. UTC | #4
On Wed, Apr 21 2021, Eric Sunshine wrote:

> On Wed, Apr 21, 2021 at 6:41 AM Đoàn Trần Công Danh
> <congdanhqx@gmail.com> wrote:
>> We will lose the exit status of "git ls-files" if it's being run in
>> anywhere-but-not-final part of a pipe.
>>
>> Let's send the output of "git ls-files" to a file first,
>> and adjust the expected result for "git ls-files -o" since a new
>> untracked file will be created as a side effect.
>>
>> Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
>> ---
>> diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
>> @@ -631,20 +663,33 @@ test_expect_success 'check handling of differently renamed file with D/F conflic
>> -               test 5 -eq "$(git ls-files -s | wc -l)" &&
>> -               test 3 -eq "$(git ls-files -u | wc -l)" &&
>> -               test 1 -eq "$(git ls-files -u one~HEAD | wc -l)" &&
>> +               git ls-files -s >output &&
>> +               test_line_count = 5 output &&
>> +               git ls-files -u >output &&
>> +               test_line_count = 3 output &&
>> +               git ls-files -u one~HEAD >output &&
>> +               test_line_count = 1 output &&
>
> This idiom crops up so frequently in this test script that it almost
> begs for the introduction of a helper function rather than applying
> the manual transformation repeatedly. For instance, the helper might
> be called like this:
>
>     count_ls_files 5 -s &&
>     count_ls_files 3 -u &&
>     count_ls_files 1 -u one~HEAD &&
>     ...
>
> The nice thing about having a helper function is that it can clean up
> after itself by not leaving a new file lying around, thus you wouldn't
> have to make adjustments to the expected number of untracked files (as
> mentioned in the commit message). If this is the sort of thing which
> comes up often enough (if there are more such cases beyond the two
> scripts you changed in this series), then it might make sense to
> promote the helper function to test-lib-functions.sh.

Agreed, but I'd saythat it makes more sense to have something like:

	test_line_count = 1 -- git ls-files

Or maybe another function, but in any case not something specific to
ls-files, but just a wrapper like the various "$@" wrappers in
test-lib-functions.sh to count the lines emitted by an arbitrary
command.

It would also leave things open for:

    test_line_count --stout=1 --stderr=2 -- git ls-files
    test_line_count --combined=3 -- git ls-files

Or whatever, to count the different output streams.
Đoàn Trần Công Danh April 22, 2021, 1:49 p.m. UTC | #5
On 2021-04-21 16:32:40-0700, Junio C Hamano <gitster@pobox.com> wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
> 
> > On Wed, Apr 21, 2021 at 12:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> >>     count_ls_files 5 -s &&
> >>     count_ls_files 3 -u &&
> >>     count_ls_files 1 -u one~HEAD &&
> >>     ...
> >>
> >> The nice thing about having a helper function is that it can clean up
> >> after itself by not leaving a new file lying around, thus you wouldn't
> >> have to make adjustments to the expected number of untracked files (as
> >> mentioned in the commit message). If this is the sort of thing which
> >> comes up often enough (if there are more such cases beyond the two
> >> scripts you changed in this series), then it might make sense to
> >> promote the helper function to test-lib-functions.sh.
> >
> > The frequency with which this idiom crops up with commands beyond
> > git-ls-files suggests the more general solution of supporting it
> > directly in test-lib-functions.sh for any command. For instance:
> >
> >     test_cmd_line_count = 3 git ls-files -u &&
> >
> > Or, perhaps, a new mode of test_line_count():
> >
> >     test_line_count = 3 -c git ls-files -u &&

Hm, I'm not sure how would we implement such mode:

* Will we run such command in a subprocess and and pipe to "wc -l"
  directly to avoid a temporary file, but we will lose the exit code
  of running command in process?
* Will we run in a separated chain? Thus, a temporary file would be
  created, skimming over test-lib-functions.sh, I couldn't find any
  convention for creating such files, except for specific use cases,
  let's say "*.config".
* Another clever hacks that I don't know *shrug*

> 
> That looks nice on paper, but may be going too far.
> 
> We may want to count the lines in the error message,

Let's assume that we solve above puzzle.

Count the lines in the error messages is not too hard to be imagined,
let's say by -c2 or something like that.

> or we may want
> to count the lines after filtering the output with pipe.

However, when it involved a pipe, things becomes complicated.

> A test file that is dedicated to test ls-files with a file local
> helper "count_ls_files" smells like a better place to stop, at least
> to me.

Hence, I'll stick with local help "count_ls_files" for now.
Ævar Arnfjörð Bjarmason April 22, 2021, 3:04 p.m. UTC | #6
On Thu, Apr 22 2021, Đoàn Trần Công Danh wrote:

> On 2021-04-21 16:32:40-0700, Junio C Hamano <gitster@pobox.com> wrote:
>> Eric Sunshine <sunshine@sunshineco.com> writes:
>> 
>> > On Wed, Apr 21, 2021 at 12:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
>> >>     count_ls_files 5 -s &&
>> >>     count_ls_files 3 -u &&
>> >>     count_ls_files 1 -u one~HEAD &&
>> >>     ...
>> >>
>> >> The nice thing about having a helper function is that it can clean up
>> >> after itself by not leaving a new file lying around, thus you wouldn't
>> >> have to make adjustments to the expected number of untracked files (as
>> >> mentioned in the commit message). If this is the sort of thing which
>> >> comes up often enough (if there are more such cases beyond the two
>> >> scripts you changed in this series), then it might make sense to
>> >> promote the helper function to test-lib-functions.sh.
>> >
>> > The frequency with which this idiom crops up with commands beyond
>> > git-ls-files suggests the more general solution of supporting it
>> > directly in test-lib-functions.sh for any command. For instance:
>> >
>> >     test_cmd_line_count = 3 git ls-files -u &&
>> >
>> > Or, perhaps, a new mode of test_line_count():
>> >
>> >     test_line_count = 3 -c git ls-files -u &&
>
> Hm, I'm not sure how would we implement such mode:
>
> * Will we run such command in a subprocess and and pipe to "wc -l"
>   directly to avoid a temporary file, but we will lose the exit code
>   of running command in process?

Just use a tempfile.

> * Will we run in a separated chain? Thus, a temporary file would be
>   created, skimming over test-lib-functions.sh, I couldn't find any
>   convention for creating such files, except for specific use cases,
>   let's say "*.config".

Yeah we happen not to have one of those in test-lib-functions.sh, but a
bunch of helpers create those.

> * Another clever hacks that I don't know *shrug*

>> 
>> That looks nice on paper, but may be going too far.
>> 
>> We may want to count the lines in the error message,
>
> Let's assume that we solve above puzzle.
>
> Count the lines in the error messages is not too hard to be imagined,
> let's say by -c2 or something like that.
>
>> or we may want
>> to count the lines after filtering the output with pipe.
>
> However, when it involved a pipe, things becomes complicated.

Yes, there's no portable way to both stream stdout/stderr in the correct
order and to intercept it. You'd need a mkfifo. See also how the "--tee"
mode works (i.e. they're squashed).

>> A test file that is dedicated to test ls-files with a file local
>> helper "count_ls_files" smells like a better place to stop, at least
>> to me.
>
> Hence, I'll stick with local help "count_ls_files" for now.

Here's an implementation that works, sans the fancy opts parsing etc. I
just use -1 for "don't care". You can see "test_commit" etc. for how to
do that.

It doesn't emit output, but that's just a matter of re-cat-ing the
relevant files.

Yes we wouldn't have combined output when you do that, but I don't think
we care, you can chain this thing, and most thing that would do so would
only care about piping stdout/stderr to their own files anyway, and not
that we emit one line of stdout, then another of stderr, another of
stdout etc.

diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 425dad97d54..c7251102a3d 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -154,14 +154,10 @@ test_expect_success 'pull conflicting renames' \
 	git reset --hard &&
 	git show-branch &&
 	test_expect_code 1 git pull . blue &&
-	git ls-files -u A >a.stages &&
-	test_line_count = 1 a.stages &&
-	git ls-files -u B >b.stages &&
-	test_line_count = 1 b.stages &&
-	git ls-files -u C >c.stages &&
-	test_line_count = 1 c.stages &&
-	git ls-files -s N >n.stages &&
-	test_line_count = 1 n.stages &&
+	test_line_count_command = 1 0 git ls-files -u A &&
+	test_line_count_command = 1 0 git ls-files -u B &&
+	test_line_count_command = 1 0 git ls-files -u C &&
+	test_line_count_command = 1 0 git ls-files -s N &&
 	sed -ne "/^g/{
 	p
 	q
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index b823c140271..10e3cbe0d47 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -817,6 +817,24 @@ test_line_count () {
 	fi
 }
 
+test_line_count_command () {
+	local op=$1
+	local ocnt=$2
+	local ecnt=$3
+	shift 3
+	"$@" 2>cnt.err >cnt.out
+	local exit_code=$?
+	if test "$ocnt" -gt -1
+	then
+		test_line_count "$op" "$ocnt" cnt.out
+	fi &&
+	if test "$ecnt" -gt -1
+	then
+		test_line_count "$op" "$ecnt" cnt.err
+	fi &&
+	return "$exit_code"
+}
+
 test_file_size () {
 	test "$#" -ne 1 && BUG "1 param"
 	test-tool path-utils file-size "$1"
Đoàn Trần Công Danh April 22, 2021, 3:07 p.m. UTC | #7
On 2021-04-22 20:49:04+0700, Đoàn Trần Công Danh <congdanhqx@gmail.com> wrote:
> On 2021-04-21 16:32:40-0700, Junio C Hamano <gitster@pobox.com> wrote:
> > Eric Sunshine <sunshine@sunshineco.com> writes:
> > 
> > > On Wed, Apr 21, 2021 at 12:55 PM Eric Sunshine <sunshine@sunshineco.com> wrote:
> > >>     count_ls_files 5 -s &&
> > >>     count_ls_files 3 -u &&
> > >>     count_ls_files 1 -u one~HEAD &&
> > >>     ...
> > >>
> > >> The nice thing about having a helper function is that it can clean up
> > >> after itself by not leaving a new file lying around, thus you wouldn't
> > >> have to make adjustments to the expected number of untracked files (as
> > >> mentioned in the commit message). If this is the sort of thing which
> > >> comes up often enough (if there are more such cases beyond the two
> > >> scripts you changed in this series), then it might make sense to
> > >> promote the helper function to test-lib-functions.sh.
> > >
> > > The frequency with which this idiom crops up with commands beyond
> > > git-ls-files suggests the more general solution of supporting it
> > > directly in test-lib-functions.sh for any command. For instance:
> > >
> > >     test_cmd_line_count = 3 git ls-files -u &&
> > >
> > > Or, perhaps, a new mode of test_line_count():
> > >
> > >     test_line_count = 3 -c git ls-files -u &&
> 
> Hm, I'm not sure how would we implement such mode:
> 
> * Will we run such command in a subprocess and and pipe to "wc -l"
>   directly to avoid a temporary file, but we will lose the exit code
>   of running command in process?
> * Will we run in a separated chain? Thus, a temporary file would be
>   created, skimming over test-lib-functions.sh, I couldn't find any
>   convention for creating such files, except for specific use cases,
>   let's say "*.config".
> * Another clever hacks that I don't know *shrug*

Hm, I figured out, just a bit insane for reviewing.
-----8<--------
Subject: [PATCH] test-lib-functions: add test_line_count_in helper

Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
---
 t/t0000-basic.sh        |  7 +++++
 t/test-lib-functions.sh | 62 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 705d62cc27..2ddb50e919 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -60,6 +60,13 @@ test_expect_success '.git/objects should have 3 subdirectories' '
 	test_line_count = 3 full-of-directories
 '
 
+test_expect_success 'check test_line_count_in' '
+	test_line_count_in --stdout = 4 --stderr = 3 -- sh -c "
+		printf \"%s\\n\" 1 2 3 4
+		printf >&2 \"%s\\n\" a b c
+		"
+'
+
 ################################################################
 # Test harness
 test_expect_success 'success is reported like this' '
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 6348e8d733..3b3dc3020a 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -817,6 +817,68 @@ test_line_count () {
 	fi
 }
 
+# test_line_count_in checks if stdout and/or stderr has the number of lines it
+# ought to. E.g.
+#
+# 	test_expect_success 'product 1 line in stdout and 2 lines in stderr' '
+# 		test_line_count_in --stdout = 1 --stderr = 3 do_something
+# 	'
+test_line_count_in () {
+	local out_ops=-ge
+	local out_val=0
+	local err_ops=-ge
+	local err_val=0
+	local opt_set=
+
+	while test $# != 0
+	do
+		case "$1" in
+		--stdout)
+			if test $# -lt 3
+			then
+				BUG "need ops and value for --stdout"
+			fi
+			out_ops=$2
+			out_val=$3
+			opt_set=yes
+			shift 3
+			;;
+		--stderr)
+			if test $# -lt 3
+			then
+				BUG "need ops and value for --stderr"
+			fi
+			err_ops=$2
+			err_val=$3
+			opt_set=yes
+			shift 3
+			;;
+		--)
+			shift
+			break
+			;;
+		esac
+	done
+
+	if test -z "$opt_set"
+	then
+		BUG "need check ops for test_line_count_in"
+	else
+		! (
+		test $(
+			(
+			test $(
+				( "$@" || echo "'$*' run into failure" >&3) |
+				wc -l
+				) "$out_ops" "$out_val" ||
+			echo "test_line_count_in --stdout: !$out_ops $out_val '$*'" >&3
+			) 2>&1 | wc -l
+		) "$err_ops" "$err_val" ||
+		echo "test_line_count_in --stderr: !$out_ops $out_val '$*'" >&3
+		) 3>&1 | grep .
+	fi
+}
+
 test_file_size () {
 	test "$#" -ne 1 && BUG "1 param"
 	test-tool path-utils file-size "$1"
Eric Sunshine April 22, 2021, 3:30 p.m. UTC | #8
On Thu, Apr 22, 2021 at 11:04 AM Ævar Arnfjörð Bjarmason
<avarab@gmail.com> wrote:
> On Thu, Apr 22 2021, Đoàn Trần Công Danh wrote:
> > Hm, I'm not sure how would we implement such mode:
> >
> > * Will we run such command in a subprocess and and pipe to "wc -l"
> >   directly to avoid a temporary file, but we will lose the exit code
> >   of running command in process?
>
> Just use a tempfile.

That's what I had in mind when I suggested the function. The function
would remove the tempfile if the check succeeded.

> Here's an implementation that works, sans the fancy opts parsing etc. I
> just use -1 for "don't care". You can see "test_commit" etc. for how to
> do that.
>
> diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
> @@ -817,6 +817,24 @@ test_line_count () {
> +test_line_count_command () {
> +       local op=$1
> +       local ocnt=$2
> +       local ecnt=$3
> +       shift 3
> +       "$@" 2>cnt.err >cnt.out
> +       local exit_code=$?
> +       if test "$ocnt" -gt -1
> +       then
> +               test_line_count "$op" "$ocnt" cnt.out
> +       fi &&
> +       if test "$ecnt" -gt -1
> +       then
> +               test_line_count "$op" "$ecnt" cnt.err
> +       fi &&
> +       return "$exit_code"
> +}

It's not clear why you manually catch the exit code of the command
being run but then use &&-chain for the other parts.

An important reason I suggested creating a function was because the
function could clean up after itself thus freeing the test author from
worrying about adjusting counts in later tests (or later in the same
test). So, just before this function returns, I'd expect to see:

    rm -f cnt.out cnt.err &&
diff mbox series

Patch

diff --git a/t/t6402-merge-rename.sh b/t/t6402-merge-rename.sh
index 425dad97d5..d705847f26 100755
--- a/t/t6402-merge-rename.sh
+++ b/t/t6402-merge-rename.sh
@@ -330,8 +330,10 @@  test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' '
 		test_i18ngrep "Adding as dir~HEAD instead" output
 	fi &&
 
-	test 3 -eq "$(git ls-files -u | wc -l)" &&
-	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 3 output &&
+	git ls-files -u dir/file-in-the-way >output &&
+	test_line_count = 2 output &&
 
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
@@ -357,8 +359,10 @@  test_expect_success 'Same as previous, but merged other way' '
 		test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output
 	fi &&
 
-	test 3 -eq "$(git ls-files -u | wc -l)" &&
-	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 3 output &&
+	git ls-files -u dir/file-in-the-way >output &&
+	test_line_count = 2 output &&
 
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
@@ -374,8 +378,10 @@  test_expect_success 'Rename+D/F conflict; renamed file cannot merge, dir not in
 	git checkout -q renamed-file-has-conflicts^0 &&
 	test_must_fail git merge --strategy=recursive dir-not-in-way &&
 
-	test 3 -eq "$(git ls-files -u | wc -l)" &&
-	test 3 -eq "$(git ls-files -u dir | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 3 output &&
+	git ls-files -u dir >output &&
+	test_line_count = 3 output &&
 
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
@@ -409,14 +415,19 @@  test_expect_success 'Rename+D/F conflict; renamed file cannot merge and dir in t
 	git checkout -q renamed-file-has-conflicts^0 &&
 	test_must_fail git merge --strategy=recursive dir-in-way &&
 
-	test 5 -eq "$(git ls-files -u | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 5 output &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test 3 -eq "$(git ls-files -u dir~HEAD | wc -l)"
+		git ls-files -u dir~HEAD >output &&
+		test_line_count = 3 output
 	else
-		test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)"
+		git ls-files -u dir >output &&
+		grep -v file-in-the-way output >filtered
+		test_line_count = 3 filtered
 	fi &&
-	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+	git ls-files -u dir/file-in-the-way >output &&
+	test_line_count = 2 output &&
 
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
@@ -432,14 +443,19 @@  test_expect_success 'Same as previous, but merged other way' '
 	git checkout -q dir-in-way^0 &&
 	test_must_fail git merge --strategy=recursive renamed-file-has-conflicts &&
 
-	test 5 -eq "$(git ls-files -u | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 5 output &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test 3 -eq "$(git ls-files -u dir~renamed-file-has-conflicts | wc -l)"
+		git ls-files -u dir~renamed-file-has-conflicts >output &&
+		test_line_count = 3 output
 	else
-		test 3 -eq "$(git ls-files -u dir | grep -v file-in-the-way | wc -l)"
+		git ls-files -u dir >output &&
+		grep -v file-in-the-way output >filtered
+		test_line_count = 3 filtered
 	fi &&
-	test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" &&
+	git ls-files -u dir/file-in-the-way >output &&
+	test_line_count = 2 output &&
 
 	test_must_fail git diff --quiet &&
 	test_must_fail git diff --cached --quiet &&
@@ -494,11 +510,12 @@  test_expect_success 'both rename source and destination involved in D/F conflict
 	git checkout -q rename-dest^0 &&
 	test_must_fail git merge --strategy=recursive source-conflict &&
 
+	git ls-files -u >output &&
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test 2 -eq "$(git ls-files -u | wc -l)"
+		test_line_count = 2 output
 	else
-		test 1 -eq "$(git ls-files -u | wc -l)"
+		test_line_count = 1 output
 	fi &&
 
 	test_must_fail git diff --quiet &&
@@ -540,9 +557,13 @@  then
 		mkdir one &&
 		test_must_fail git merge --strategy=recursive rename-two &&
 
-		test 4 -eq "$(git ls-files -u | wc -l)" &&
-		test 2 -eq "$(git ls-files -u one | wc -l)" &&
-		test 2 -eq "$(git ls-files -u two | wc -l)" &&
+		git ls-files -u >output &&
+		test_line_count = 4 output &&
+		git ls-files -u one >output &&
+		test_line_count = 2 output &&
+		git ls-files -u two >output &&
+		test_line_count = 2 output &&
+		rm -f output &&
 
 		test_must_fail git diff --quiet &&
 
@@ -559,9 +580,13 @@  else
 		mkdir one &&
 		test_must_fail git merge --strategy=recursive rename-two &&
 
-		test 2 -eq "$(git ls-files -u | wc -l)" &&
-		test 1 -eq "$(git ls-files -u one | wc -l)" &&
-		test 1 -eq "$(git ls-files -u two | wc -l)" &&
+		git ls-files -u >output &&
+		test_line_count = 2 output &&
+		git ls-files -u one >output &&
+		test_line_count = 1 output &&
+		git ls-files -u two >output &&
+		test_line_count = 1 output &&
+		rm -f output &&
 
 		test_must_fail git diff --quiet &&
 
@@ -582,14 +607,21 @@  test_expect_success 'pair rename to parent of other (D/F conflicts) w/ clean sta
 
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test 4 -eq "$(git ls-files -u | wc -l)" &&
-		test 2 -eq "$(git ls-files -u one | wc -l)" &&
-		test 2 -eq "$(git ls-files -u two | wc -l)"
+		git ls-files -u >output &&
+		test_line_count = 4 output &&
+		git ls-files -u one >output &&
+		test_line_count = 2 output &&
+		git ls-files -u two >output &&
+		test_line_count = 2 output
 	else
-		test 2 -eq "$(git ls-files -u | wc -l)" &&
-		test 1 -eq "$(git ls-files -u one | wc -l)" &&
-		test 1 -eq "$(git ls-files -u two | wc -l)"
+		git ls-files -u >output &&
+		test_line_count = 2 output &&
+		git ls-files -u one >output &&
+		test_line_count = 1 output &&
+		git ls-files -u two >output &&
+		test_line_count = 1 output
 	fi &&
+	rm -f output &&
 
 	test_must_fail git diff --quiet &&
 
@@ -631,20 +663,33 @@  test_expect_success 'check handling of differently renamed file with D/F conflic
 
 	if test "$GIT_TEST_MERGE_ALGORITHM" = ort
 	then
-		test 5 -eq "$(git ls-files -s | wc -l)" &&
-		test 3 -eq "$(git ls-files -u | wc -l)" &&
-		test 1 -eq "$(git ls-files -u one~HEAD | wc -l)" &&
-		test 1 -eq "$(git ls-files -u two~second-rename | wc -l)" &&
-		test 1 -eq "$(git ls-files -u original | wc -l)" &&
-		test 0 -eq "$(git ls-files -o | wc -l)"
+		git ls-files -s >output &&
+		test_line_count = 5 output &&
+		git ls-files -u >output &&
+		test_line_count = 3 output &&
+		git ls-files -u one~HEAD >output &&
+		test_line_count = 1 output &&
+		git ls-files -u two~second-rename >output &&
+		test_line_count = 1 output &&
+		git ls-files -u original >output &&
+		test_line_count = 1 output &&
+		git ls-files -o >output &&
+		test_line_count = 1 output
 	else
-		test 5 -eq "$(git ls-files -s | wc -l)" &&
-		test 3 -eq "$(git ls-files -u | wc -l)" &&
-		test 1 -eq "$(git ls-files -u one | wc -l)" &&
-		test 1 -eq "$(git ls-files -u two | wc -l)" &&
-		test 1 -eq "$(git ls-files -u original | wc -l)" &&
-		test 2 -eq "$(git ls-files -o | wc -l)"
+		git ls-files -s >output &&
+		test_line_count = 5 output &&
+		git ls-files -u >output &&
+		test_line_count = 3 output &&
+		git ls-files -u one >output &&
+		test_line_count = 1 output &&
+		git ls-files -u two >output &&
+		test_line_count = 1 output &&
+		git ls-files -u original >output &&
+		test_line_count = 1 output &&
+		git ls-files -o >output &&
+		test_line_count = 3 output
 	fi &&
+	rm -f output &&
 
 	test_path_is_file one/file &&
 	test_path_is_file two/file &&
@@ -679,11 +724,17 @@  test_expect_success 'check handling of differently renamed file with D/F conflic
 	git checkout -q first-rename-redo^0 &&
 	test_must_fail git merge --strategy=recursive second-rename-redo &&
 
-	test 3 -eq "$(git ls-files -u | wc -l)" &&
-	test 1 -eq "$(git ls-files -u one | wc -l)" &&
-	test 1 -eq "$(git ls-files -u two | wc -l)" &&
-	test 1 -eq "$(git ls-files -u original | wc -l)" &&
-	test 0 -eq "$(git ls-files -o | wc -l)" &&
+	git ls-files -u >output &&
+	test_line_count = 3 output &&
+	git ls-files -u one >output &&
+	test_line_count = 1 output &&
+	git ls-files -u two >output &&
+	test_line_count = 1 output &&
+	git ls-files -u original >output &&
+	test_line_count = 1 output &&
+	git ls-files -o >output &&
+	test_line_count = 1 output &&
+	rm -f output &&
 
 	test_path_is_file one &&
 	test_path_is_file two &&
@@ -861,8 +912,11 @@  test_expect_success 'setup merge of rename + small change' '
 test_expect_success 'merge rename + small change' '
 	git merge rename_branch &&
 
-	test 1 -eq $(git ls-files -s | wc -l) &&
-	test 0 -eq $(git ls-files -o | wc -l) &&
+	git ls-files -s >output &&
+	test_line_count = 1 output &&
+	git ls-files -o >output &&
+	test_line_count = 1 output &&
+	rm -f output &&
 	test $(git rev-parse HEAD:renamed_file) = $(git rev-parse HEAD~1:file)
 '