Git fork
at reftables-rust 372 lines 10 kB view raw
1# Performance testing framework. Each perf script starts much like 2# a normal test script, except it sources this library instead of 3# test-lib.sh. See t/perf/README for documentation. 4# 5# Copyright (c) 2011 Thomas Rast 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see https://www.gnu.org/licenses/ . 19 20# These variables must be set before the inclusion of test-lib.sh below, 21# because it will change our working directory. 22TEST_DIRECTORY=$(pwd)/.. 23TEST_OUTPUT_DIRECTORY=$(pwd) 24 25TEST_NO_CREATE_REPO=t 26TEST_NO_MALLOC_CHECK=t 27 28# GIT-BUILD-OPTIONS, sourced by test-lib.sh, overwrites the `GIT_PERF_*` 29# values that are set by the user (if any). Let's stash them away as 30# `eval`-able assignments. 31git_perf_settings="$(env | 32 sed -n "/^GIT_PERF_/{ 33 # escape all single-quotes in the value 34 s/'/'\\\\''/g 35 # turn this into an eval-able assignment 36 s/^\\([^=]*=\\)\\(.*\\)/\\1'\\2'/p 37 }")" 38 39# While test-lib.sh computes the build directory for us, we also have to do the 40# same thing in order to locate the script via GIT-BUILD-OPTIONS in the first 41# place. 42GIT_BUILD_DIR="${GIT_BUILD_DIR:-$TEST_DIRECTORY/..}" 43if test -f "$GIT_BUILD_DIR/GIT-BUILD-DIR" 44then 45 GIT_BUILD_DIR="$(cat "$GIT_BUILD_DIR/GIT-BUILD-DIR")" || exit 1 46 # On Windows, we must convert Windows paths lest they contain a colon 47 case "$(uname -s)" in 48 *MINGW*) 49 GIT_BUILD_DIR="$(cygpath -au "$GIT_BUILD_DIR")" 50 ;; 51 esac 52fi 53 54if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS 55then 56 echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).' 57 exit 1 58fi 59 60. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS 61. "$GIT_SOURCE_DIR"/t/test-lib.sh 62 63# Then restore GIT_PERF_* settings. 64eval "$git_perf_settings" 65 66unset GIT_CONFIG_NOSYSTEM 67GIT_CONFIG_SYSTEM="$TEST_DIRECTORY/perf/config" 68export GIT_CONFIG_SYSTEM 69 70if test -n "$GIT_TEST_INSTALLED" && test -z "$PERF_SET_GIT_TEST_INSTALLED" 71then 72 error "Do not use GIT_TEST_INSTALLED with the perf tests. 73 74Instead use: 75 76 ./run <path-to-git> -- <tests> 77 78See t/perf/README for details." 79fi 80 81# Variables from test-lib that are normally internal to the tests; we 82# need to export them for test_perf subshells 83export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP 84 85MODERN_GIT=$GIT_BUILD_DIR/bin-wrappers/git 86export MODERN_GIT 87 88MODERN_SCALAR=$GIT_BUILD_DIR/bin-wrappers/scalar 89export MODERN_SCALAR 90 91perf_results_dir=$TEST_RESULTS_DIR 92test -n "$GIT_PERF_SUBSECTION" && perf_results_dir="$perf_results_dir/$GIT_PERF_SUBSECTION" 93mkdir -p "$perf_results_dir" 94rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests 95 96die_if_build_dir_not_repo () { 97 if ! ( cd "$TEST_DIRECTORY/.." && 98 git rev-parse --build-dir >/dev/null 2>&1 ); then 99 error "No $1 defined, and your build directory is not a repo" 100 fi 101} 102 103if test -z "$GIT_PERF_REPO"; then 104 die_if_build_dir_not_repo '$GIT_PERF_REPO' 105 GIT_PERF_REPO=$TEST_DIRECTORY/.. 106fi 107if test -z "$GIT_PERF_LARGE_REPO"; then 108 die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO' 109 GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/.. 110fi 111 112test_perf_do_repo_symlink_config_ () { 113 test_have_prereq SYMLINKS || git config core.symlinks false 114} 115 116test_perf_copy_repo_contents () { 117 for stuff in "$1"/* 118 do 119 case "$stuff" in 120 */objects|*/hooks|*/config|*/commondir|*/gitdir|*/worktrees|*/fsmonitor--daemon*) 121 ;; 122 *) 123 cp -R "$stuff" "$repo/.git/" || exit 1 124 ;; 125 esac 126 done 127} 128 129test_perf_create_repo_from () { 130 test "$#" = 2 || 131 BUG "not 2 parameters to test-create-repo" 132 repo="$1" 133 source="$2" 134 source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)" 135 objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)" 136 common_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-common-dir)" 137 refformat="$("$MODERN_GIT" -C "$source" rev-parse --show-ref-format)" 138 objectformat="$("$MODERN_GIT" -C "$source" rev-parse --show-object-format)" 139 mkdir -p "$repo/.git" 140 ( 141 cd "$source" && 142 { cp -Rl "$objects_dir" "$repo/.git/" 2>/dev/null || 143 cp -R "$objects_dir" "$repo/.git/"; } && 144 145 # common_dir must come first here, since we want source_git to 146 # take precedence and overwrite any overlapping files 147 test_perf_copy_repo_contents "$common_dir" 148 if test "$source_git" != "$common_dir" 149 then 150 test_perf_copy_repo_contents "$source_git" 151 fi 152 ) && 153 ( 154 cd "$repo" && 155 "$MODERN_GIT" init -q --ref-format="$refformat" --object-format="$objectformat" && 156 test_perf_do_repo_symlink_config_ && 157 mv .git/hooks .git/hooks-disabled 2>/dev/null && 158 if test -f .git/index.lock 159 then 160 # We may be copying a repo that can't run "git 161 # status" due to a locked index. Since we have 162 # a copy it's fine to remove the lock. 163 rm .git/index.lock 164 fi && 165 if test_bool_env GIT_PERF_USE_SCALAR false 166 then 167 "$MODERN_SCALAR" register 168 fi 169 ) || error "failed to copy repository '$source' to '$repo'" 170} 171 172# call at least one of these to establish an appropriately-sized repository 173test_perf_fresh_repo () { 174 repo="${1:-$TRASH_DIRECTORY}" 175 "$MODERN_GIT" init -q "$repo" && 176 ( 177 cd "$repo" && 178 test_perf_do_repo_symlink_config_ && 179 if test_bool_env GIT_PERF_USE_SCALAR false 180 then 181 "$MODERN_SCALAR" register 182 fi 183 ) 184} 185 186test_perf_default_repo () { 187 test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO" 188} 189test_perf_large_repo () { 190 if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then 191 echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2 192 echo "warning: This will work, but may not be a sufficiently large repo" >&2 193 echo "warning: for representative measurements." >&2 194 fi 195 test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO" 196} 197test_checkout_worktree () { 198 git checkout-index -u -a || 199 error "git checkout-index failed" 200} 201 202# Performance tests should never fail. If they do, stop immediately 203immediate=t 204 205# Perf tests require GNU time 206case "$(uname -s)" in Darwin) GTIME="${GTIME:-gtime}";; esac 207GTIME="${GTIME:-/usr/bin/time}" 208 209test_run_perf_ () { 210 test_cleanup=: 211 test_export_="test_cleanup" 212 export test_cleanup test_export_ 213 "$GTIME" -f "%E %U %S" -o test_time.$i "$TEST_SHELL_PATH" -c ' 214. '"$TEST_DIRECTORY"/test-lib-functions.sh' 215test_export () { 216 test_export_="$test_export_ $*" 217} 218'"$1"' 219ret=$? 220needles= 221for v in $test_export_ 222do 223 needles="$needles;s/^$v=/export $v=/p" 224done 225set | sed -n "s'"/'/'\\\\''/g"'$needles" >test_vars 226exit $ret' >&3 2>&4 227 eval_ret=$? 228 229 if test $eval_ret = 0 || test -n "$expecting_failure" 230 then 231 test_eval_ "$test_cleanup" 232 . ./test_vars || error "failed to load updated environment" 233 fi 234 if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then 235 echo "" 236 fi 237 return "$eval_ret" 238} 239 240test_wrapper_ () { 241 local test_wrapper_func_="$1"; shift 242 local test_title_="$1"; shift 243 test_start_ 244 test_prereq= 245 test_perf_setup_= 246 while test $# != 0 247 do 248 case $1 in 249 --prereq) 250 test_prereq=$2 251 shift 252 ;; 253 --setup) 254 test_perf_setup_=$2 255 shift 256 ;; 257 *) 258 break 259 ;; 260 esac 261 shift 262 done 263 test "$#" = 1 || BUG "test_wrapper_ needs 2 positional parameters" 264 export test_prereq 265 export test_perf_setup_ 266 267 if ! test_skip "$test_title_" "$@" 268 then 269 base=$(basename "$0" .sh) 270 echo "$test_count" >>"$perf_results_dir"/$base.subtests 271 echo "$test_title_" >"$perf_results_dir"/$base.$test_count.descr 272 base="$perf_results_dir"/"$PERF_RESULTS_PREFIX$(basename "$0" .sh)"."$test_count" 273 "$test_wrapper_func_" "$test_title_" "$@" 274 fi 275 276 test_finish_ 277} 278 279test_perf_ () { 280 if test -z "$verbose"; then 281 printf "%s" "perf $test_count - $1:" 282 else 283 echo "perf $test_count - $1:" 284 fi 285 for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do 286 if test -n "$test_perf_setup_" 287 then 288 say >&3 "setup: $test_perf_setup_" 289 if ! test_eval_ $test_perf_setup_ 290 then 291 test_failure_ "$test_perf_setup_" 292 break 293 fi 294 295 fi 296 say >&3 "running: $2" 297 if test_run_perf_ "$2" 298 then 299 if test -z "$verbose"; then 300 printf " %s" "$i" 301 else 302 echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:" 303 fi 304 else 305 test -z "$verbose" && echo 306 test_failure_ "$@" 307 break 308 fi 309 done 310 if test -z "$verbose"; then 311 echo " ok" 312 else 313 test_ok_ "$1" 314 fi 315 "$PERL_PATH" "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".result 316 rm test_time.* 317} 318 319# Usage: test_perf 'title' [options] 'perf-test' 320# Run the performance test script specified in perf-test with 321# optional prerequisite and setup steps. 322# Options: 323# --prereq prerequisites: Skip the test if prerequisites aren't met 324# --setup "setup-steps": Run setup steps prior to each measured iteration 325# 326test_perf () { 327 test_wrapper_ test_perf_ "$@" 328} 329 330test_size_ () { 331 if test -n "$test_perf_setup_" 332 then 333 say >&3 "setup: $test_perf_setup_" 334 test_eval_ $test_perf_setup_ 335 fi 336 337 say >&3 "running: $2" 338 if test_eval_ "$2" 3>"$base".result; then 339 test_ok_ "$1" 340 else 341 test_failure_ "$@" 342 fi 343} 344 345# Usage: test_size 'title' [options] 'size-test' 346# Run the size test script specified in size-test with optional 347# prerequisites and setup steps. Returns the numeric value 348# returned by size-test. 349# Options: 350# --prereq prerequisites: Skip the test if prerequisites aren't met 351# --setup "setup-steps": Run setup steps prior to the size measurement 352 353test_size () { 354 test_wrapper_ test_size_ "$@" 355} 356 357# We extend test_done to print timings at the end (./run disables this 358# and does it after running everything) 359test_at_end_hook_ () { 360 if test -z "$GIT_PERF_AGGREGATING_LATER"; then 361 ( 362 cd "$TEST_DIRECTORY"/perf && 363 "$PERL_PATH" "$GIT_SOURCE_DIR"/t/perf/aggregate.perl --results-dir="$TEST_RESULTS_DIR" $(basename "$0") 364 ) 365 fi 366} 367 368test_export () { 369 export "$@" 370} 371 372test_lazy_prereq PERF_EXTRA 'test_bool_env GIT_PERF_EXTRA false'