Git fork

Introduce a performance testing framework

This introduces a performance testing framework under t/perf/. It
tries to be as close to the test-lib.sh infrastructure as possible,
and thus should be easy to get used to for git developers.

The following points were considered for the implementation:

1. You usually want to compare arbitrary revisions/build trees against
each other. They may not have the performance test under
consideration, or even the perf-lib.sh infrastructure.

To cope with this, the 'run' script lets you specify arbitrary
build dirs and revisions. It even automatically builds the revisions
if it doesn't have them at hand yet.

2. Usually you would not want to run all tests. It would take too
long anyway. The 'run' script lets you specify which tests to run;
or you can also do it manually. There is a Makefile for
discoverability and 'make clean', but it is not meant for
real-world use.

3. Creating test repos from scratch in every test is extremely
time-consuming, and shipping or downloading such large/weird repos
is out of the question.

We leave this decision to the user. Two different sizes of test
repos can be configured, and the scripts just copy one or more of
those (using hardlinks for the object store). By default it tries
to use the build tree's git.git repository.

This is fairly fast and versatile. Using a copy instead of a clone
preserves many properties that the user may want to test for, such
as lots of loose objects, unpacked refs, etc.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Thomas Rast and committed by
Junio C Hamano
342e9ef2 12a29b1a

+774 -5
+21 -1
Makefile
··· 2361 2361 @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@ 2362 2362 @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@ 2363 2363 @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@ 2364 + @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@ 2365 + ifdef GIT_TEST_OPTS 2366 + @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@ 2367 + endif 2364 2368 ifdef GIT_TEST_CMP 2365 2369 @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@ 2366 2370 endif ··· 2369 2373 endif 2370 2374 @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@ 2371 2375 @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@ 2372 - @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@ 2376 + ifdef GIT_PERF_REPEAT_COUNT 2377 + @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@ 2378 + endif 2379 + ifdef GIT_PERF_REPO 2380 + @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@ 2381 + endif 2382 + ifdef GIT_PERF_LARGE_REPO 2383 + @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@ 2384 + endif 2385 + ifdef GIT_PERF_MAKE_OPTS 2386 + @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@ 2387 + endif 2373 2388 2374 2389 ### Detect Tck/Tk interpreter path changes 2375 2390 ifndef NO_TCLTK ··· 2404 2419 2405 2420 test: all 2406 2421 $(MAKE) -C t/ all 2422 + 2423 + perf: all 2424 + $(MAKE) -C t/perf/ all 2425 + 2426 + .PHONY: test perf 2407 2427 2408 2428 test-ctype$X: ctype.o 2409 2429
+42 -1
t/Makefile
··· 73 73 valgrind: 74 74 $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind" 75 75 76 - .PHONY: pre-clean $(T) aggregate-results clean valgrind 76 + perf: 77 + $(MAKE) -C perf/ all 78 + 79 + # Smoke testing targets 80 + -include ../GIT-VERSION-FILE 81 + uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown') 82 + uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown') 83 + 84 + test-results: 85 + mkdir -p test-results 86 + 87 + test-results/git-smoke.tar.gz: test-results 88 + $(PERL_PATH) ./harness \ 89 + --archive="test-results/git-smoke.tar.gz" \ 90 + $(T) 91 + 92 + smoke: test-results/git-smoke.tar.gz 93 + 94 + SMOKE_UPLOAD_FLAGS = 95 + ifdef SMOKE_USERNAME 96 + SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)" 97 + endif 98 + ifdef SMOKE_COMMENT 99 + SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)" 100 + endif 101 + ifdef SMOKE_TAGS 102 + SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)" 103 + endif 104 + 105 + smoke_report: smoke 106 + curl \ 107 + -H "Expect: " \ 108 + -F project=Git \ 109 + -F architecture="$(uname_M)" \ 110 + -F platform="$(uname_S)" \ 111 + -F revision="$(GIT_VERSION)" \ 112 + -F report_file=@test-results/git-smoke.tar.gz \ 113 + $(SMOKE_UPLOAD_FLAGS) \ 114 + http://smoke.git.nix.is/app/projects/process_add_report/1 \ 115 + | grep -v ^Redirecting 116 + 117 + .PHONY: pre-clean $(T) aggregate-results clean valgrind perf
+2
t/perf/.gitignore
··· 1 + build/ 2 + test-results/
+15
t/perf/Makefile
··· 1 + -include ../../config.mak 2 + export GIT_TEST_OPTIONS 3 + 4 + all: perf 5 + 6 + perf: pre-clean 7 + ./run 8 + 9 + pre-clean: 10 + rm -rf test-results 11 + 12 + clean: 13 + rm -rf build "trash directory".* test-results 14 + 15 + .PHONY: all perf pre-clean clean
+146
t/perf/README
··· 1 + Git performance tests 2 + ===================== 3 + 4 + This directory holds performance testing scripts for git tools. The 5 + first part of this document describes the various ways in which you 6 + can run them. 7 + 8 + When fixing the tools or adding enhancements, you are strongly 9 + encouraged to add tests in this directory to cover what you are 10 + trying to fix or enhance. The later part of this short document 11 + describes how your test scripts should be organized. 12 + 13 + 14 + Running Tests 15 + ------------- 16 + 17 + The easiest way to run tests is to say "make". This runs all 18 + the tests on the current git repository. 19 + 20 + === Running 2 tests in this tree === 21 + [...] 22 + Test this tree 23 + --------------------------------------------------------- 24 + 0001.1: rev-list --all 0.54(0.51+0.02) 25 + 0001.2: rev-list --all --objects 6.14(5.99+0.11) 26 + 7810.1: grep worktree, cheap regex 0.16(0.16+0.35) 27 + 7810.2: grep worktree, expensive regex 7.90(29.75+0.37) 28 + 7810.3: grep --cached, cheap regex 3.07(3.02+0.25) 29 + 7810.4: grep --cached, expensive regex 9.39(30.57+0.24) 30 + 31 + You can compare multiple repositories and even git revisions with the 32 + 'run' script: 33 + 34 + $ ./run . origin/next /path/to/git-tree p0001-rev-list.sh 35 + 36 + where . stands for the current git tree. The full invocation is 37 + 38 + ./run [<revision|directory>...] [--] [<test-script>...] 39 + 40 + A '.' argument is implied if you do not pass any other 41 + revisions/directories. 42 + 43 + You can also manually test this or another git build tree, and then 44 + call the aggregation script to summarize the results: 45 + 46 + $ ./p0001-rev-list.sh 47 + [...] 48 + $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh 49 + [...] 50 + $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh 51 + 52 + aggregate.perl has the same invocation as 'run', it just does not run 53 + anything beforehand. 54 + 55 + You can set the following variables (also in your config.mak): 56 + 57 + GIT_PERF_REPEAT_COUNT 58 + Number of times a test should be repeated for best-of-N 59 + measurements. Defaults to 5. 60 + 61 + GIT_PERF_MAKE_OPTS 62 + Options to use when automatically building a git tree for 63 + performance testing. E.g., -j6 would be useful. 64 + 65 + GIT_PERF_REPO 66 + GIT_PERF_LARGE_REPO 67 + Repositories to copy for the performance tests. The normal 68 + repo should be at least git.git size. The large repo should 69 + probably be about linux-2.6.git size for optimal results. 70 + Both default to the git.git you are running from. 71 + 72 + You can also pass the options taken by ordinary git tests; the most 73 + useful one is: 74 + 75 + --root=<directory>:: 76 + Create "trash" directories used to store all temporary data during 77 + testing under <directory>, instead of the t/ directory. 78 + Using this option with a RAM-based filesystem (such as tmpfs) 79 + can massively speed up the test suite. 80 + 81 + 82 + Naming Tests 83 + ------------ 84 + 85 + The performance test files are named as: 86 + 87 + pNNNN-commandname-details.sh 88 + 89 + where N is a decimal digit. The same conventions for choosing NNNN as 90 + for normal tests apply. 91 + 92 + 93 + Writing Tests 94 + ------------- 95 + 96 + The perf script starts much like a normal test script, except it 97 + sources perf-lib.sh: 98 + 99 + #!/bin/sh 100 + # 101 + # Copyright (c) 2005 Junio C Hamano 102 + # 103 + 104 + test_description='xxx performance test' 105 + . ./perf-lib.sh 106 + 107 + After that you will want to use some of the following: 108 + 109 + test_perf_default_repo # sets up a "normal" repository 110 + test_perf_large_repo # sets up a "large" repository 111 + 112 + test_perf_default_repo sub # ditto, in a subdir "sub" 113 + 114 + test_checkout_worktree # if you need the worktree too 115 + 116 + At least one of the first two is required! 117 + 118 + You can use test_expect_success as usual. For actual performance 119 + tests, use 120 + 121 + test_perf 'descriptive string' ' 122 + command1 && 123 + command2 124 + ' 125 + 126 + test_perf spawns a subshell, for lack of better options. This means 127 + that 128 + 129 + * you _must_ export all variables that you need in the subshell 130 + 131 + * you _must_ flag all variables that you want to persist from the 132 + subshell with 'test_export': 133 + 134 + test_perf 'descriptive string' ' 135 + foo=$(git rev-parse HEAD) && 136 + test_export foo 137 + ' 138 + 139 + The so-exported variables are automatically marked for export in the 140 + shell executing the perf test. For your convenience, test_export is 141 + the same as export in the main shell. 142 + 143 + This feature relies on a bit of magic using 'set' and 'source'. 144 + While we have tried to make sure that it can cope with embedded 145 + whitespace and other special characters, it will not work with 146 + multi-line data.
+166
t/perf/aggregate.perl
··· 1 + #!/usr/bin/perl 2 + 3 + use strict; 4 + use warnings; 5 + use Git; 6 + 7 + sub get_times { 8 + my $name = shift; 9 + open my $fh, "<", $name or return undef; 10 + my $line = <$fh>; 11 + return undef if not defined $line; 12 + close $fh or die "cannot close $name: $!"; 13 + $line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/ 14 + or die "bad input line: $line"; 15 + my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3; 16 + return ($rt, $4, $5); 17 + } 18 + 19 + sub format_times { 20 + my ($r, $u, $s, $firstr) = @_; 21 + if (!defined $r) { 22 + return "<missing>"; 23 + } 24 + my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s; 25 + if (defined $firstr) { 26 + if ($firstr > 0) { 27 + $out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr; 28 + } elsif ($r == 0) { 29 + $out .= " ="; 30 + } else { 31 + $out .= " +inf"; 32 + } 33 + } 34 + return $out; 35 + } 36 + 37 + my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests); 38 + while (scalar @ARGV) { 39 + my $arg = $ARGV[0]; 40 + my $dir; 41 + last if -f $arg or $arg eq "--"; 42 + if (! -d $arg) { 43 + my $rev = Git::command_oneline(qw(rev-parse --verify), $arg); 44 + $dir = "build/".$rev; 45 + } else { 46 + $arg =~ s{/*$}{}; 47 + $dir = $arg; 48 + $dirabbrevs{$dir} = $dir; 49 + } 50 + push @dirs, $dir; 51 + $dirnames{$dir} = $arg; 52 + my $prefix = $dir; 53 + $prefix =~ tr/^a-zA-Z0-9/_/c; 54 + $prefixes{$dir} = $prefix . '.'; 55 + shift @ARGV; 56 + } 57 + 58 + if (not @dirs) { 59 + @dirs = ('.'); 60 + } 61 + $dirnames{'.'} = $dirabbrevs{'.'} = "this tree"; 62 + $prefixes{'.'} = ''; 63 + 64 + shift @ARGV if scalar @ARGV and $ARGV[0] eq "--"; 65 + 66 + @tests = @ARGV; 67 + if (not @tests) { 68 + @tests = glob "p????-*.sh"; 69 + } 70 + 71 + my @subtests; 72 + my %shorttests; 73 + for my $t (@tests) { 74 + $t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t"; 75 + my $n = $2; 76 + my $fname = "test-results/$t.subtests"; 77 + open my $fp, "<", $fname or die "cannot open $fname: $!"; 78 + for (<$fp>) { 79 + chomp; 80 + /^(\d+)$/ or die "malformed subtest line: $_"; 81 + push @subtests, "$t.$1"; 82 + $shorttests{"$t.$1"} = "$n.$1"; 83 + } 84 + close $fp or die "cannot close $fname: $!"; 85 + } 86 + 87 + sub read_descr { 88 + my $name = shift; 89 + open my $fh, "<", $name or return "<error reading description>"; 90 + my $line = <$fh>; 91 + close $fh or die "cannot close $name"; 92 + chomp $line; 93 + return $line; 94 + } 95 + 96 + my %descrs; 97 + my $descrlen = 4; # "Test" 98 + for my $t (@subtests) { 99 + $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr"); 100 + $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen; 101 + } 102 + 103 + sub have_duplicate { 104 + my %seen; 105 + for (@_) { 106 + return 1 if exists $seen{$_}; 107 + $seen{$_} = 1; 108 + } 109 + return 0; 110 + } 111 + sub have_slash { 112 + for (@_) { 113 + return 1 if m{/}; 114 + } 115 + return 0; 116 + } 117 + 118 + my %newdirabbrevs = %dirabbrevs; 119 + while (!have_duplicate(values %newdirabbrevs)) { 120 + %dirabbrevs = %newdirabbrevs; 121 + last if !have_slash(values %dirabbrevs); 122 + %newdirabbrevs = %dirabbrevs; 123 + for (values %newdirabbrevs) { 124 + s{^[^/]*/}{}; 125 + } 126 + } 127 + 128 + my %times; 129 + my @colwidth = ((0)x@dirs); 130 + for my $i (0..$#dirs) { 131 + my $d = $dirs[$i]; 132 + my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d}); 133 + $colwidth[$i] = $w if $w > $colwidth[$i]; 134 + } 135 + for my $t (@subtests) { 136 + my $firstr; 137 + for my $i (0..$#dirs) { 138 + my $d = $dirs[$i]; 139 + $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")]; 140 + my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}}; 141 + my $w = length format_times($r,$u,$s,$firstr); 142 + $colwidth[$i] = $w if $w > $colwidth[$i]; 143 + $firstr = $r unless defined $firstr; 144 + } 145 + } 146 + my $totalwidth = 3*@dirs+$descrlen; 147 + $totalwidth += $_ for (@colwidth); 148 + 149 + printf "%-${descrlen}s", "Test"; 150 + for my $i (0..$#dirs) { 151 + my $d = $dirs[$i]; 152 + printf " %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d}); 153 + } 154 + print "\n"; 155 + print "-"x$totalwidth, "\n"; 156 + for my $t (@subtests) { 157 + printf "%-${descrlen}s", $descrs{$t}; 158 + my $firstr; 159 + for my $i (0..$#dirs) { 160 + my $d = $dirs[$i]; 161 + my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}}; 162 + printf " %-$colwidth[$i]s", format_times($r,$u,$s,$firstr); 163 + $firstr = $r unless defined $firstr; 164 + } 165 + print "\n"; 166 + }
+21
t/perf/min_time.perl
··· 1 + #!/usr/bin/perl 2 + 3 + my $minrt = 1e100; 4 + my $min; 5 + 6 + while (<>) { 7 + # [h:]m:s.xx U.xx S.xx 8 + /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/ 9 + or die "bad input line: $_"; 10 + my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3; 11 + if ($rt < $minrt) { 12 + $min = $_; 13 + $minrt = $rt; 14 + } 15 + } 16 + 17 + if (!defined $min) { 18 + die "no input found"; 19 + } 20 + 21 + print $min;
+41
t/perf/p0000-perf-lib-sanity.sh
··· 1 + #!/bin/sh 2 + 3 + test_description='Tests whether perf-lib facilities work' 4 + . ./perf-lib.sh 5 + 6 + test_perf_default_repo 7 + 8 + test_perf 'test_perf_default_repo works' ' 9 + foo=$(git rev-parse HEAD) && 10 + test_export foo 11 + ' 12 + 13 + test_checkout_worktree 14 + 15 + test_perf 'test_checkout_worktree works' ' 16 + wt=$(find . | wc -l) && 17 + idx=$(git ls-files | wc -l) && 18 + test $wt -gt $idx 19 + ' 20 + 21 + baz=baz 22 + test_export baz 23 + 24 + test_expect_success 'test_export works' ' 25 + echo "$foo" && 26 + test "$foo" = "$(git rev-parse HEAD)" && 27 + echo "$baz" && 28 + test "$baz" = baz 29 + ' 30 + 31 + test_perf 'export a weird var' ' 32 + bar="weird # variable" && 33 + test_export bar 34 + ' 35 + 36 + test_expect_success 'test_export works with weird vars' ' 37 + echo "$bar" && 38 + test "$bar" = "weird # variable" 39 + ' 40 + 41 + test_done
+17
t/perf/p0001-rev-list.sh
··· 1 + #!/bin/sh 2 + 3 + test_description="Tests history walking performance" 4 + 5 + . ./perf-lib.sh 6 + 7 + test_perf_default_repo 8 + 9 + test_perf 'rev-list --all' ' 10 + git rev-list --all >/dev/null 11 + ' 12 + 13 + test_perf 'rev-list --all --objects' ' 14 + git rev-list --all --objects >/dev/null 15 + ' 16 + 17 + test_done
+198
t/perf/perf-lib.sh
··· 1 + #!/bin/sh 2 + # 3 + # Copyright (c) 2011 Thomas Rast 4 + # 5 + # This program is free software: you can redistribute it and/or modify 6 + # it under the terms of the GNU General Public License as published by 7 + # the Free Software Foundation, either version 2 of the License, or 8 + # (at your option) any later version. 9 + # 10 + # This program is distributed in the hope that it will be useful, 11 + # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + # GNU General Public License for more details. 14 + # 15 + # You should have received a copy of the GNU General Public License 16 + # along with this program. If not, see http://www.gnu.org/licenses/ . 17 + 18 + # do the --tee work early; it otherwise confuses our careful 19 + # GIT_BUILD_DIR mangling 20 + case "$GIT_TEST_TEE_STARTED, $* " in 21 + done,*) 22 + # do not redirect again 23 + ;; 24 + *' --tee '*|*' --va'*) 25 + mkdir -p test-results 26 + BASE=test-results/$(basename "$0" .sh) 27 + (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; 28 + echo $? > $BASE.exit) | tee $BASE.out 29 + test "$(cat $BASE.exit)" = 0 30 + exit 31 + ;; 32 + esac 33 + 34 + TEST_DIRECTORY=$(pwd)/.. 35 + TEST_OUTPUT_DIRECTORY=$(pwd) 36 + if test -z "$GIT_TEST_INSTALLED"; then 37 + perf_results_prefix= 38 + else 39 + perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"." 40 + # make the tested dir absolute 41 + GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd) 42 + fi 43 + 44 + TEST_NO_CREATE_REPO=t 45 + 46 + . ../test-lib.sh 47 + 48 + perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results 49 + mkdir -p "$perf_results_dir" 50 + rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests 51 + 52 + if test -z "$GIT_PERF_REPEAT_COUNT"; then 53 + GIT_PERF_REPEAT_COUNT=3 54 + fi 55 + die_if_build_dir_not_repo () { 56 + if ! ( cd "$TEST_DIRECTORY/.." && 57 + git rev-parse --build-dir >/dev/null 2>&1 ); then 58 + error "No $1 defined, and your build directory is not a repo" 59 + fi 60 + } 61 + 62 + if test -z "$GIT_PERF_REPO"; then 63 + die_if_build_dir_not_repo '$GIT_PERF_REPO' 64 + GIT_PERF_REPO=$TEST_DIRECTORY/.. 65 + fi 66 + if test -z "$GIT_PERF_LARGE_REPO"; then 67 + die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO' 68 + GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/.. 69 + fi 70 + 71 + test_perf_create_repo_from () { 72 + test "$#" = 2 || 73 + error "bug in the test script: not 2 parameters to test-create-repo" 74 + repo="$1" 75 + source="$2" 76 + source_git=$source/$(cd "$source" && git rev-parse --git-dir) 77 + mkdir -p "$repo/.git" 78 + ( 79 + cd "$repo/.git" && 80 + { cp -Rl "$source_git/objects" . 2>/dev/null || 81 + cp -R "$source_git/objects" .; } && 82 + for stuff in "$source_git"/*; do 83 + case "$stuff" in 84 + */objects|*/hooks|*/config) 85 + ;; 86 + *) 87 + cp -R "$stuff" . || break 88 + ;; 89 + esac 90 + done && 91 + cd .. && 92 + git init -q && 93 + mv .git/hooks .git/hooks-disabled 2>/dev/null 94 + ) || error "failed to copy repository '$source' to '$repo'" 95 + } 96 + 97 + # call at least one of these to establish an appropriately-sized repository 98 + test_perf_default_repo () { 99 + test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO" 100 + } 101 + test_perf_large_repo () { 102 + if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then 103 + echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2 104 + echo "warning: This will work, but may not be a sufficiently large repo" >&2 105 + echo "warning: for representative measurements." >&2 106 + fi 107 + test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO" 108 + } 109 + test_checkout_worktree () { 110 + git checkout-index -u -a || 111 + error "git checkout-index failed" 112 + } 113 + 114 + # Performance tests should never fail. If they do, stop immediately 115 + immediate=t 116 + 117 + test_run_perf_ () { 118 + test_cleanup=: 119 + test_export_="test_cleanup" 120 + export test_cleanup test_export_ 121 + /usr/bin/time -f "%E %U %S" -o test_time.$i "$SHELL" -c ' 122 + . '"$TEST_DIRECTORY"/../test-lib-functions.sh' 123 + test_export () { 124 + [ $# != 0 ] || return 0 125 + test_export_="$test_export_\\|$1" 126 + shift 127 + test_export "$@" 128 + } 129 + '"$1"' 130 + ret=$? 131 + set | sed -n "s'"/'/'\\\\''/g"';s/^\\($test_export_\\)/export '"'&'"'/p" >test_vars 132 + exit $ret' >&3 2>&4 133 + eval_ret=$? 134 + 135 + if test $eval_ret = 0 || test -n "$expecting_failure" 136 + then 137 + test_eval_ "$test_cleanup" 138 + . ./test_vars || error "failed to load updated environment" 139 + fi 140 + if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then 141 + echo "" 142 + fi 143 + return "$eval_ret" 144 + } 145 + 146 + 147 + test_perf () { 148 + test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= 149 + test "$#" = 2 || 150 + error "bug in the test script: not 2 or 3 parameters to test-expect-success" 151 + export test_prereq 152 + if ! test_skip "$@" 153 + then 154 + base=$(basename "$0" .sh) 155 + echo "$test_count" >>"$perf_results_dir"/$base.subtests 156 + echo "$1" >"$perf_results_dir"/$base.$test_count.descr 157 + if test -z "$verbose"; then 158 + echo -n "perf $test_count - $1:" 159 + else 160 + echo "perf $test_count - $1:" 161 + fi 162 + for i in $(seq 1 $GIT_PERF_REPEAT_COUNT); do 163 + say >&3 "running: $2" 164 + if test_run_perf_ "$2" 165 + then 166 + if test -z "$verbose"; then 167 + echo -n " $i" 168 + else 169 + echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:" 170 + fi 171 + else 172 + test -z "$verbose" && echo 173 + test_failure_ "$@" 174 + break 175 + fi 176 + done 177 + if test -z "$verbose"; then 178 + echo " ok" 179 + else 180 + test_ok_ "$1" 181 + fi 182 + base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count" 183 + "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times 184 + fi 185 + echo >&3 "" 186 + } 187 + 188 + # We extend test_done to print timings at the end (./run disables this 189 + # and does it after running everything) 190 + test_at_end_hook_ () { 191 + if test -z "$GIT_PERF_AGGREGATING_LATER"; then 192 + ( cd "$TEST_DIRECTORY"/perf && ./aggregate.perl $(basename "$0") ) 193 + fi 194 + } 195 + 196 + test_export () { 197 + export "$@" 198 + }
+82
t/perf/run
··· 1 + #!/bin/sh 2 + 3 + case "$1" in 4 + --help) 5 + echo "usage: $0 [other_git_tree...] [--] [test_scripts]" 6 + exit 0 7 + ;; 8 + esac 9 + 10 + die () { 11 + echo >&2 "error: $*" 12 + exit 1 13 + } 14 + 15 + run_one_dir () { 16 + if test $# -eq 0; then 17 + set -- p????-*.sh 18 + fi 19 + echo "=== Running $# tests in ${GIT_TEST_INSTALLED:-this tree} ===" 20 + for t in "$@"; do 21 + ./$t $GIT_TEST_OPTS 22 + done 23 + } 24 + 25 + unpack_git_rev () { 26 + rev=$1 27 + mkdir -p build/$rev 28 + (cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) | 29 + (cd build/$rev && tar x) 30 + } 31 + build_git_rev () { 32 + rev=$1 33 + cp ../../config.mak build/$rev/config.mak 34 + (cd build/$rev && make $GIT_PERF_MAKE_OPTS) || 35 + die "failed to build revision '$mydir'" 36 + } 37 + 38 + run_dirs_helper () { 39 + mydir=${1%/} 40 + shift 41 + while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do 42 + shift 43 + done 44 + if test $# -gt 0 -a "$1" = --; then 45 + shift 46 + fi 47 + if [ ! -d "$mydir" ]; then 48 + rev=$(git rev-parse --verify "$mydir" 2>/dev/null) || 49 + die "'$mydir' is neither a directory nor a valid revision" 50 + if [ ! -d build/$rev ]; then 51 + unpack_git_rev $rev 52 + fi 53 + build_git_rev $rev 54 + mydir=build/$rev 55 + fi 56 + if test "$mydir" = .; then 57 + unset GIT_TEST_INSTALLED 58 + else 59 + GIT_TEST_INSTALLED="$mydir/bin-wrappers" 60 + export GIT_TEST_INSTALLED 61 + fi 62 + run_one_dir "$@" 63 + } 64 + 65 + run_dirs () { 66 + while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do 67 + run_dirs_helper "$@" 68 + shift 69 + done 70 + } 71 + 72 + GIT_PERF_AGGREGATING_LATER=t 73 + export GIT_PERF_AGGREGATING_LATER 74 + 75 + cd "$(dirname $0)" 76 + . ../../GIT-BUILD-OPTIONS 77 + 78 + if test $# = 0 -o "$1" = -- -o -f "$1"; then 79 + set -- . "$@" 80 + fi 81 + run_dirs "$@" 82 + ./aggregate.perl "$@"
+23 -3
t/test-lib.sh
··· 55 55 .*_TEST 56 56 PROVE 57 57 VALGRIND 58 + PERF_AGGREGATING_LATER 58 59 )); 59 60 my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env); 60 61 print join("\n", @vars); ··· 97 98 # Line feed 98 99 LF=' 99 100 ' 101 + 102 + export _x05 _x40 _z40 LF 100 103 101 104 # Each test should start with something like this, after copyright notices: 102 105 # ··· 313 316 esac 314 317 } 315 318 319 + # stub; perf-lib overrides it 320 + test_at_end_hook_ () { 321 + : 322 + } 323 + 316 324 test_done () { 317 325 GIT_EXIT_OK=t 318 326 319 327 if test -z "$HARNESS_ACTIVE"; then 320 - test_results_dir="$TEST_DIRECTORY/test-results" 328 + test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" 321 329 mkdir -p "$test_results_dir" 322 330 test_results_path="$test_results_dir/${0%.sh}-$$.counts" 323 331 ··· 356 364 cd "$(dirname "$remove_trash")" && 357 365 rm -rf "$(basename "$remove_trash")" 358 366 367 + test_at_end_hook_ 368 + 359 369 exit 0 ;; 360 370 361 371 *) ··· 378 388 # itself. 379 389 TEST_DIRECTORY=$(pwd) 380 390 fi 391 + if test -z "$TEST_OUTPUT_DIRECTORY" 392 + then 393 + # Similarly, override this to store the test-results subdir 394 + # elsewhere 395 + TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY 396 + fi 381 397 GIT_BUILD_DIR="$TEST_DIRECTORY"/.. 382 398 383 399 if test -n "$valgrind" ··· 513 529 test -n "$root" && test="$root/$test" 514 530 case "$test" in 515 531 /*) TRASH_DIRECTORY="$test" ;; 516 - *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; 532 + *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$test" ;; 517 533 esac 518 534 test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY 519 535 rm -fr "$test" || { ··· 525 541 HOME="$TRASH_DIRECTORY" 526 542 export HOME 527 543 528 - test_create_repo "$test" 544 + if test -z "$TEST_NO_CREATE_REPO"; then 545 + test_create_repo "$test" 546 + else 547 + mkdir -p "$test" 548 + fi 529 549 # Use -P to resolve symlinks in our working directory so that the cwd 530 550 # in subprocesses like git equals our $PWD (for pathname comparisons). 531 551 cd -P "$test" || exit 1