Merge branch 'tr/test-v-and-v-subtest-only'
authorJunio C Hamano <gitster@pobox.com>
Fri, 5 Jul 2013 08:15:48 +0000 (01:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 5 Jul 2013 08:15:48 +0000 (01:15 -0700)
Allows N instances of tests run in parallel, each running 1/N parts
of the test suite under Valgrind, to speed things up.

* tr/test-v-and-v-subtest-only:
perf-lib: fix start/stop of perf tests
test-lib: support running tests under valgrind in parallel
test-lib: allow prefixing a custom string before "ok N" etc.
test-lib: valgrind for only tests matching a pattern
test-lib: verbose mode for only tests matching a pattern
test-lib: self-test that --verbose works
test-lib: rearrange start/end of test_expect_* and test_skip
test-lib: refactor $GIT_SKIP_TESTS matching
test-lib: enable MALLOC_* for the actual tests

1  2 
t/README
t/t0000-basic.sh
t/test-lib-functions.sh
t/test-lib.sh
diff --combined t/README
index ec5246886132f919ec4b6b05bfedd65eb2011690,bffd4843417ba73973b2a9f817091c0af76996ac..2167125008db5e3e1c7d13310c8da3a4243e8718
+++ b/t/README
@@@ -76,6 -76,11 +76,11 @@@ appropriately before running "make"
        command being run and their output if any are also
        output.
  
+ --verbose-only=<pattern>::
+       Like --verbose, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
  --debug::
        This may help the person who is developing a new test.
        It causes the command defined with test_debug to run.
        the 't/valgrind/' directory and use the commands under
        't/valgrind/bin/'.
  
+ --valgrind-only=<pattern>::
+       Like --valgrind, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
  --tee::
        In addition to printing the test output to the terminal,
        write it to files named 't/test-results/$TEST_NAME.out'.
@@@ -324,9 -334,6 +334,9 @@@ Don't
     use 'test_must_fail git cmd'.  This will signal a failure if git
     dies in an unexpected way (e.g. segfault).
  
 +   On the other hand, don't use test_must_fail for running regular
 +   platform commands; just use '! cmd'.
 +
   - use perl without spelling it as "$PERL_PATH". This is to help our
     friends on Windows where the platform Perl often adds CR before
     the end of line, and they bundle Git with a version of Perl that
@@@ -595,20 -602,6 +605,20 @@@ library for your script to use
                test_cmp expected actual
        '
  
 + - test_ln_s_add <path1> <path2>
 +
 +   This function helps systems whose filesystem does not support symbolic
 +   links. Use it to add a symbolic link entry to the index when it is not
 +   important that the file system entry is a symbolic link, i.e., instead
 +   of the sequence
 +
 +      ln -s foo bar &&
 +      git add bar
 +
 +   Sometimes it is possible to split a test in a part that does not need
 +   the symbolic link in the file system and a part that does; then only
 +   the latter part need be protected by a SYMLINKS prerequisite (see below).
 +
  Prerequisites
  -------------
  
diff --combined t/t0000-basic.sh
index 0f1318056cd06d8bcae615be5e573149ac5971d1,4f1844f7822f37cb841290bac48035d9e1336fd1..5c32288380bfd26d20574440c7f94d46dc89a56b
@@@ -47,8 -47,13 +47,13 @@@ test_expect_failure 'pretend we have a 
  
  run_sub_test_lib_test () {
        name="$1" descr="$2" # stdin is the body of the test code
+       shift 2
        mkdir "$name" &&
        (
+               # Pretend we're a test harness.  This prevents
+               # test-lib from writing the counts to a file that will
+               # later be summarized, showing spurious "failed" tests
+               export HARNESS_ACTIVE=t &&
                cd "$name" &&
                cat >"$name.sh" <<-EOF &&
                #!$SHELL_PATH
@@@ -65,7 -70,7 +70,7 @@@
                cat >>"$name.sh" &&
                chmod +x "$name.sh" &&
                export TEST_DIRECTORY &&
-               ./"$name.sh" >out 2>err
+               ./"$name.sh" "$@" >out 2>err
        )
  }
  
@@@ -215,6 -220,60 +220,60 @@@ test_expect_success 'pretend we have a 
        EOF
  "
  
+ test_expect_success 'test --verbose' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose "test verbose" --verbose <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       mv test-verbose/out test-verbose/out+
+       grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+       check_sub_test_lib_test test-verbose <<-\EOF
+       > expecting success: true
+       > Z
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > expecting success: false
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > Z
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+ '
+ test_expect_success 'test --verbose-only' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose-only-2 "test verbose-only=2" \
+               --verbose-only=2 <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+ '
  test_set_prereq HAVEIT
  haveit=no
  test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
@@@ -367,6 -426,22 +426,6 @@@ test_expect_success 'validate object I
  
  # Various types of objects
  
 -# Some filesystems do not support symblic links; on such systems
 -# some expected values are different
 -if test_have_prereq SYMLINKS
 -then
 -      expectfilter=cat
 -      expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
 -      expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
 -      expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
 -else
 -      expectfilter='grep -v sym'
 -      expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
 -      expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
 -      expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
 -fi
 -
 -
  test_expect_success 'adding various types of objects with git update-index --add' '
        mkdir path2 path3 path3/subp3 &&
        paths="path0 path2/file2 path3/file3 path3/subp3/file3" &&
                for p in $paths
                do
                        echo "hello $p" >$p || exit 1
 -                      if test_have_prereq SYMLINKS
 -                      then
 -                              ln -s "hello $p" ${p}sym || exit 1
 -                      fi
 +                      test_ln_s_add "hello $p" ${p}sym || exit 1
                done
        ) &&
        find path* ! -type d -print | xargs git update-index --add
@@@ -386,7 -464,7 +445,7 @@@ test_expect_success 'showing stage wit
  '
  
  test_expect_success 'validate git ls-files output for a known tree' '
 -      $expectfilter >expected <<-\EOF &&
 +      cat >expected <<-\EOF &&
        100644 f87290f8eb2cbbea7857214459a0739927eab154 0       path0
        120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0       path0sym
        100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0       path2/file2
@@@ -404,14 -482,14 +463,14 @@@ test_expect_success 'writing tree out w
  '
  
  test_expect_success 'validate object ID for a known tree' '
 -      test "$tree" = "$expectedtree"
 +      test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b
  '
  
  test_expect_success 'showing tree with git ls-tree' '
      git ls-tree $tree >current
  '
  
 -test_expect_success SYMLINKS 'git ls-tree output for a known tree' '
 +test_expect_success 'git ls-tree output for a known tree' '
        cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
@@@ -428,7 -506,7 +487,7 @@@ test_expect_success 'showing tree with 
  '
  
  test_expect_success 'git ls-tree -r output for a known tree' '
 -      $expectfilter >expected <<-\EOF &&
 +      cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
        100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7    path2/file2
@@@ -446,7 -524,7 +505,7 @@@ test_expect_success 'showing tree with 
        git ls-tree -r -t $tree >current
  '
  
 -test_expect_success SYMLINKS 'git ls-tree -r output for a known tree' '
 +test_expect_success 'git ls-tree -r output for a known tree' '
        cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
@@@ -468,7 -546,7 +527,7 @@@ test_expect_success 'writing partial tr
  '
  
  test_expect_success 'validate object ID for a known tree' '
 -      test "$ptree" = "$expectedptree1"
 +      test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3
  '
  
  test_expect_success 'writing partial tree out with git write-tree --prefix' '
  '
  
  test_expect_success 'validate object ID for a known tree' '
 -      test "$ptree" = "$expectedptree2"
 +      test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2
  '
  
  test_expect_success 'put invalid objects into the index' '
@@@ -510,7 -588,7 +569,7 @@@ test_expect_success 'git read-tree foll
  '
  
  test_expect_success 'validate git diff-files output for a know cache/work tree state' '
 -      $expectfilter >expected <<\EOF &&
 +      cat >expected <<\EOF &&
  :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M    path0
  :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M    path0sym
  :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M    path2/file2
@@@ -534,7 -612,7 +593,7 @@@ test_expect_success 'no diff after chec
  '
  
  ################################################################
 -P=$expectedtree
 +P=087704a96baf1c2d1c869a8b084481e121c88b5b
  
  test_expect_success 'git commit-tree records the correct tree in a commit' '
        commit0=$(echo NO | git commit-tree $P) &&
diff --combined t/test-lib-functions.sh
index 8828ff78f184a451fb43709771cc39bf17186cfb,ad6d60aaab15c5d7e9891e0e2a07789e2c3a9ecc..a7e9aacbb2d9b05b0d5083a0fd70cb9287fcf454
@@@ -343,6 -343,7 +343,7 @@@ test_declared_prereq () 
  }
  
  test_expect_failure () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
                        test_known_broken_failure_ "$1"
                fi
        fi
-       echo >&3 ""
+       test_finish_
  }
  
  test_expect_success () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
                        test_failure_ "$@"
                fi
        fi
-       echo >&3 ""
+       test_finish_
  }
  
  # test_external runs external test scripts that provide continuous
@@@ -609,18 -611,6 +611,18 @@@ test_cmp() 
        $GIT_TEST_CMP "$@"
  }
  
 +# Check if the file expected to be empty is indeed empty, and barfs
 +# otherwise.
 +
 +test_must_be_empty () {
 +      if test -s "$1"
 +      then
 +              echo "'$1' is not empty, it contains:"
 +              cat "$1"
 +              return 1
 +      fi
 +}
 +
  # Tests that its two parameters refer to the same revision
  test_cmp_rev () {
        git rev-parse --verify "$1" >expect.rev &&
@@@ -691,20 -681,3 +693,20 @@@ test_create_repo () 
                mv .git/hooks .git/hooks-disabled
        ) || exit
  }
 +
 +# This function helps on symlink challenged file systems when it is not
 +# important that the file system entry is a symbolic link.
 +# Use test_ln_s_add instead of "ln -s x y && git add y" to add a
 +# symbolic link entry y to the index.
 +
 +test_ln_s_add () {
 +      if test_have_prereq SYMLINKS
 +      then
 +              ln -s "$1" "$2" &&
 +              git update-index --add "$2"
 +      else
 +              printf '%s' "$1" >"$2" &&
 +              ln_s_obj=$(git hash-object -w "$2") &&
 +              git update-index --add --cacheinfo 120000 $ln_s_obj "$2"
 +      fi
 +}
diff --combined t/test-lib.sh
index eff3a653d14fc27d4e058fb06fa1a5d98c2978ae,5b5ea6d386ef3ed8a1dc25cf777c02eecfd37109..97536419e6e1b817b1bf347b6fdd63df4175177c
@@@ -54,8 -54,8 +54,8 @@@ done,*
        # do not redirect again
        ;;
  *' --tee '*|*' --va'*)
 -      mkdir -p test-results
 -      BASE=test-results/$(basename "$0" .sh)
 +      mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
 +      BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
        (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
         echo $? > $BASE.exit) | tee $BASE.out
        test "$(cat $BASE.exit)" = 0
@@@ -184,6 -184,9 +184,9 @@@ d
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
+       --verbose-only=*)
+               verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
                # Ignore --quiet under a TAP::Harness. Saying how many tests
                # passed without the ok/not ok details is always an error.
        --valgrind=*)
                valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --valgrind-only=*)
+               valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-parallel=*)
+               valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-stride=*)
+               valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-offset=*)
+               valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        --tee)
                shift ;; # was handled already
        --root=*)
                root=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --statusprefix=*)
+               statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        *)
                echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
  done
  
- test -n "$valgrind" && verbose=t
+ if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+ then
+       test -z "$valgrind" && valgrind=memcheck
+       test -z "$verbose" && verbose_only="$valgrind_only"
+ elif test -n "$valgrind"
+ then
+       verbose=t
+ fi
  
  if test -n "$color"
  then
@@@ -303,12 -328,12 +328,12 @@@ trap 'die' EXI
  
  test_ok_ () {
        test_success=$(($test_success + 1))
-       say_color "" "ok $test_count - $@"
+       say_color "" "${statusprefix}ok $test_count - $@"
  }
  
  test_failure_ () {
        test_failure=$(($test_failure + 1))
-       say_color error "not ok $test_count - $1"
+       say_color error "${statusprefix}not ok $test_count - $1"
        shift
        echo "$@" | sed -e 's/^/#       /'
        test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
  
  test_known_broken_ok_ () {
        test_fixed=$(($test_fixed+1))
-       say_color error "ok $test_count - $@ # TODO known breakage vanished"
+       say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished"
  }
  
  test_known_broken_failure_ () {
        test_broken=$(($test_broken+1))
-       say_color warn "not ok $test_count - $@ # TODO known breakage"
+       say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage"
  }
  
  test_debug () {
        test "$debug" = "" || eval "$1"
  }
  
+ match_pattern_list () {
+       arg="$1"
+       shift
+       test -z "$*" && return 1
+       for pattern_
+       do
+               case "$arg" in
+               $pattern_)
+                       return 0
+               esac
+       done
+       return 1
+ }
+ maybe_teardown_verbose () {
+       test -z "$verbose_only" && return
+       exec 4>/dev/null 3>/dev/null
+       verbose=
+ }
+ last_verbose=t
+ maybe_setup_verbose () {
+       test -z "$verbose_only" && return
+       if match_pattern_list $test_count $verbose_only ||
+               { test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
+       then
+               exec 4>&2 3>&1
+               # Emit a delimiting blank line when going from
+               # non-verbose to verbose.  Within verbose mode the
+               # delimiter is printed by test_expect_*.  The choice
+               # of the initial $last_verbose is such that before
+               # test 1, we do not print it.
+               test -z "$last_verbose" && echo >&3 ""
+               verbose=t
+       else
+               exec 4>/dev/null 3>/dev/null
+               verbose=
+       fi
+       last_verbose=$verbose
+ }
+ maybe_teardown_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       GIT_VALGRIND_ENABLED=
+ }
+ maybe_setup_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=t
+               return
+       fi
+       GIT_VALGRIND_ENABLED=
+       if match_pattern_list $test_count $valgrind_only
+       then
+               GIT_VALGRIND_ENABLED=t
+       elif test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
+       then
+               GIT_VALGRIND_ENABLED=t
+       fi
+ }
  test_eval_ () {
        # This is a separate function because some tests use
        # "return" to end a test_expect_success block early.
  test_run_ () {
        test_cleanup=:
        expecting_failure=$2
+       setup_malloc_check
        test_eval_ "$1"
        eval_ret=$?
+       teardown_malloc_check
  
        if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
        then
        return "$eval_ret"
  }
  
- test_skip () {
+ test_start_ () {
        test_count=$(($test_count+1))
+       maybe_setup_verbose
+       maybe_setup_valgrind
+ }
+ test_finish_ () {
+       echo >&3 ""
+       maybe_teardown_valgrind
+       maybe_teardown_verbose
+ }
+ test_skip () {
        to_skip=
-       for skp in $GIT_SKIP_TESTS
-       do
-               case $this_test.$test_count in
-               $skp)
-                       to_skip=t
-                       break
-               esac
-       done
+       if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+       then
+               to_skip=t
+       fi
        if test -z "$to_skip" && test -n "$test_prereq" &&
           ! test_have_prereq "$test_prereq"
        then
                        of_prereq=" of $test_prereq"
                fi
  
-               say_color skip >&3 "skipping test: $@"
-               say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
+               say_color skip >&3 "${statusprefix}skipping test: $@"
+               say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
                : true
                ;;
        *)
@@@ -395,6 -494,8 +494,8 @@@ test_at_end_hook_ () 
  test_done () {
        GIT_EXIT_OK=t
  
+       # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
+       # output file
        if test -z "$HARNESS_ACTIVE"
        then
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
  
        if test "$test_fixed" != 0
        then
-               say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
+               say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)"
        fi
        if test "$test_broken" != 0
        then
-               say_color warn "# still have $test_broken known breakage(s)"
+               say_color warn "${statusprefix}# still have $test_broken known breakage(s)"
        fi
        if test "$test_broken" != 0 || test "$test_fixed" != 0
        then
                then
                        if test $test_remaining -gt 0
                        then
-                               say_color pass "# passed all $msg"
+                               say_color pass "${statusprefix}# passed all $msg"
                        fi
-                       say "1..$test_count$skip_all"
+                       say "${statusprefix}1..$test_count$skip_all"
                fi
  
                test -d "$remove_trash" &&
        *)
                if test $test_external_has_tap -eq 0
                then
-                       say_color error "# failed $test_failure among $msg"
-                       say "1..$test_count"
+                       say_color error "${statusprefix}# failed $test_failure among $msg"
+                       say "${statusprefix}1..$test_count"
                fi
  
                exit 1 ;;
        esac
  }
  
+ # Set up a directory that we can put in PATH which redirects all git
+ # calls to 'valgrind git ...'.
  if test -n "$valgrind"
  then
        make_symlink () {
                make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
        }
  
-       # override all git executables in TEST_DIRECTORY/..
-       GIT_VALGRIND=$TEST_DIRECTORY/valgrind
-       mkdir -p "$GIT_VALGRIND"/bin
-       for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
-       do
-               make_valgrind_symlink $file
-       done
-       # special-case the mergetools loadables
-       make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
-       OLDIFS=$IFS
-       IFS=:
-       for path in $PATH
-       do
-               ls "$path"/git-* 2> /dev/null |
-               while read file
+       # In the case of --valgrind-parallel, we only need to do the
+       # wrapping once, in the main script.  The worker children all
+       # have $valgrind_only_stride set, so we can skip based on that.
+       if test -z "$valgrind_only_stride"
+       then
+               # override all git executables in TEST_DIRECTORY/..
+               GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+               mkdir -p "$GIT_VALGRIND"/bin
+               for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
                do
-                       make_valgrind_symlink "$file"
+                       make_valgrind_symlink $file
                done
-       done
-       IFS=$OLDIFS
+               # special-case the mergetools loadables
+               make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
+               OLDIFS=$IFS
+               IFS=:
+               for path in $PATH
+               do
+                       ls "$path"/git-* 2> /dev/null |
+                       while read file
+                       do
+                               make_valgrind_symlink "$file"
+                       done
+               done
+               IFS=$OLDIFS
+       fi
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
        GIT_VALGRIND_MODE="$valgrind"
        export GIT_VALGRIND_MODE
+       GIT_VALGRIND_ENABLED=t
+       if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=
+       fi
+       export GIT_VALGRIND_ENABLED
  elif test -n "$GIT_TEST_INSTALLED"
  then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
@@@ -622,21 -738,53 +738,53 @@@ the
  else
        mkdir -p "$TRASH_DIRECTORY"
  fi
+ # Gross hack to spawn N sub-instances of the tests in parallel, and
+ # summarize the results.  Note that if this is enabled, the script
+ # terminates at the end of this 'if' block.
+ if test -n "$valgrind_parallel"
+ then
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               mkdir "$root"
+               TEST_OUTPUT_DIRECTORY="$root" \
+                       ${SHELL_PATH} "$0" \
+                       --root="$root" --statusprefix="[$i] " \
+                       --valgrind="$valgrind" \
+                       --valgrind-only-stride="$valgrind_parallel" \
+                       --valgrind-only-offset="$i" &
+               pids="$pids $!"
+       done
+       trap "kill $pids" INT TERM HUP
+       wait $pids
+       trap - INT TERM HUP
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
+                       sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
+               test_count=$(expr $test_count + $inner_total)
+               test_success=$(expr $test_success + $inner_success)
+               test_fixed=$(expr $test_fixed + $inner_fixed)
+               test_broken=$(expr $test_broken + $inner_broken)
+               test_failure=$(expr $test_failure + $inner_failed)
+       done
+       test_done
+ fi
  # Use -P to resolve symlinks in our working directory so that the cwd
  # in subprocesses like git equals our $PWD (for pathname comparisons).
  cd -P "$TRASH_DIRECTORY" || exit 1
  
  this_test=${0##*/}
  this_test=${this_test%%-*}
- for skp in $GIT_SKIP_TESTS
- do
-       case "$this_test" in
-       $skp)
-               say_color info >&3 "skipping test $this_test altogether"
-               skip_all="skip all tests in $this_test"
-               test_done
-       esac
- done
+ if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+ then
+       say_color info >&3 "skipping test $this_test altogether"
+       skip_all="skip all tests in $this_test"
+       test_done
+ fi
  
  # Provide an implementation of the 'yes' utility
  yes () {