3 # assert_pointer confirms that the pointer in the repository for $path in the
4 # given $ref matches the given $oid and $size.
6 # $ assert_pointer "master" "path/to/file" "some-oid" 123
13 gitblob=$(git ls-tree -lrz "$ref" |
14 while read -r -d $'\0' x; do
17 grep "$path" | cut -f 3 -d " ")
19 actual=$(git cat-file -p $gitblob)
20 expected=$(pointer $oid $size)
22 if [ "$expected" != "$actual" ]; then
27 # refute_pointer confirms that the file in the repository for $path in the
28 # given $ref is _not_ a pointer.
30 # $ refute_pointer "master" "path/to/file"
35 gitblob=$(git ls-tree -lrz "$ref" |
36 while read -r -d $'\0' x; do
39 grep "$path" | cut -f 3 -d " ")
41 file=$(git cat-file -p $gitblob)
42 version="version https://git-lfs.github.com/spec/v[0-9]"
43 oid="oid sha256:[0-9a-f]\{64\}"
45 regex="$version.*$oid.*$size"
47 if echo $file | grep -q "$regex"; then
52 # assert_local_object confirms that an object file is stored for the given oid &
53 # has the correct size
54 # $ assert_local_object "some-oid" size
55 assert_local_object() {
58 local cfg=`git lfs env | grep LocalMediaDir`
59 local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
60 actualsize=$(wc -c <"$f" | tr -d '[[:space:]]')
61 if [ "$size" != "$actualsize" ]; then
66 # refute_local_object confirms that an object file is NOT stored for an oid.
67 # If "$size" is given as the second argument, assert that the file exists _and_
68 # that it does _not_ the expected size
70 # $ refute_local_object "some-oid"
71 # $ refute_local_object "some-oid" "123"
72 refute_local_object() {
75 local cfg=`git lfs env | grep LocalMediaDir`
76 local regex="LocalMediaDir=(\S+)"
77 local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
79 if [ -z "$size" ]; then
83 actual_size="$(wc -c < "$f" | awk '{ print $1 }')"
84 if [ "$size" -eq "$actual_size" ]; then
85 echo >&2 "fatal: expected object $oid not to have size: $size"
91 # delete_local_object deletes the local storage for an oid
92 # $ delete_local_object "some-oid"
93 delete_local_object() {
95 local cfg=`git lfs env | grep LocalMediaDir`
96 local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
100 # corrupt_local_object corrupts the local storage for an oid
101 # $ corrupt_local_object "some-oid"
102 corrupt_local_object() {
104 local cfg=`git lfs env | grep LocalMediaDir`
105 local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
110 # check that the object does not exist in the git lfs server. HTTP log is
111 # written to http.log. JSON output is written to http.json.
113 # $ refute_server_object "reponame" "oid"
114 refute_server_object() {
117 curl -v "$GITSERVER/$reponame.git/info/lfs/objects/batch" \
120 -d "{\"operation\":\"download\",\"objects\":[{\"oid\":\"$oid\"}]}" \
121 -H "Accept: application/vnd.git-lfs+json" \
122 -H "X-Check-Object: 1" \
123 -H "X-Ignore-Retries: true" 2>&1 |
126 [ "0" = "$(grep -c "download" http.json)" ] || {
132 # Delete an object on the lfs server. HTTP log is
133 # written to http.log. JSON output is written to http.json.
135 # $ delete_server_object "reponame" "oid"
136 delete_server_object() {
139 curl -v "$GITSERVER/$reponame.git/info/lfs/objects/$oid" \
143 -H "Accept: application/vnd.git-lfs+json" 2>&1 |
146 grep "200 OK" http.log
149 # check that the object does exist in the git lfs server. HTTP log is written
150 # to http.log. JSON output is written to http.json.
151 assert_server_object() {
155 curl -v "$GITSERVER/$reponame.git/info/lfs/objects/batch" \
158 -d "{\"operation\":\"download\",\"objects\":[{\"oid\":\"$oid\"}],\"ref\":{\"name\":\"$refspec\"}}" \
159 -H "Accept: application/vnd.git-lfs+json" \
160 -H "X-Check-Object: 1" \
161 -H "X-Ignore-Retries: true" 2>&1 |
163 grep "200 OK" http.log
165 grep "download" http.json || {
171 # This asserts the lock path and returns the lock ID by parsing the response of
173 # git lfs lock --json <path>
178 if [ $(grep -c "\"path\":\"$path\"" "$log") -eq 0 ]; then
179 echo "path '$path' not found in:"
184 local jsonid=$(grep -oh "\"id\":\"\w\+\"" "$log")
185 echo "${jsonid:3}" | tr -d \"\:
188 # assert that a lock with the given ID exists on the test server
189 assert_server_lock() {
194 curl -v "$GITSERVER/$reponame.git/info/lfs/locks?refspec=$refspec" \
197 -H "Accept:application/vnd.git-lfs+json" 2>&1 |
200 grep "200 OK" http.log
201 grep "$id" http.json || {
207 # refute that a lock with the given ID exists on the test server
208 refute_server_lock() {
213 curl -v "$GITSERVER/$reponame.git/info/lfs/locks?refspec=$refspec" \
216 -H "Accept:application/vnd.git-lfs+json" 2>&1 | tee http.log
218 grep "200 OK" http.log
220 [ $(grep -c "$id" http.json) -eq 0 ]
223 # Assert that .gitattributes contains a given attribute N times
224 assert_attributes_count() {
229 pattern="\(*.\)\?$fileext\(.*\)$attrib"
230 actual=$(grep -e "$pattern" .gitattributes | wc -l)
231 if [ "$(printf "%d" "$actual")" != "$count" ]; then
232 echo "wrong number of $attrib entries for $fileext"
233 echo "expected: $count actual: $actual"
239 assert_file_writeable() {
240 ls -l "$1" | grep -e "^-rw"
243 refute_file_writeable() {
244 ls -l "$1" | grep -e "^-r-"
248 git rev-parse --show-toplevel 2>/dev/null
252 echo "$(git_root)/.git"
258 if [ -z "$git_root" ]; then
259 echo >&2 "fatal: (assert_hooks) not in git repository"
263 [ -x "$git_root/hooks/post-checkout" ]
264 [ -x "$git_root/hooks/post-commit" ]
265 [ -x "$git_root/hooks/post-merge" ]
266 [ -x "$git_root/hooks/pre-push" ]
269 assert_clean_status() {
270 status="$(git status)"
271 echo "$status" | grep "working tree clean" || {
277 # pointer returns a string Git LFS pointer file.
279 # $ pointer abc-some-oid 123 <version>
284 local version=${3:-https://git-lfs.github.com/spec/v1}
288 " "$version" "$oid" "$size"
291 # wait_for_file simply sleeps until a file exists.
293 # $ wait_for_file "path/to/upcoming/file"
298 while [ $n -lt 17 ]; do
299 if [ -s $filename ]; then
305 if [ $wait_time -lt 4 ]; then
306 wait_time=`expr $wait_time \* 2`
310 echo "$filename did not appear after 60 seconds."
314 # setup_remote_repo initializes a bare Git repository that is accessible through
315 # the test Git server. The `pwd` is set to the repository's directory, in case
316 # further commands need to be run. This server is running for every test in an
317 # integration run, so every test file should setup its own remote repository to
320 # $ setup_remote_repo "some-name"
322 setup_remote_repo() {
324 echo "set up remote git repository: $reponame"
325 repodir="$REMOTEDIR/$reponame.git"
329 git config http.receivepack true
330 git config receive.denyCurrentBranch ignore
333 # creates a bare remote repository for a local clone. Useful to test pushing to
334 # a fresh remote server.
336 # $ setup_alternate_remote "$reponame-whatever"
337 # $ setup_alternate_remote "$reponame-whatever" "other-remote-name"
339 setup_alternate_remote() {
340 local newRemoteName=$1
341 local remote=${2:-origin}
345 setup_remote_repo "$newRemoteName"
347 git remote rm "$remote"
348 git remote add "$remote" "$GITSERVER/$newRemoteName"
351 # clone_repo clones a repository from the test Git server to the subdirectory
352 # $dir under $TRASHDIR. setup_remote_repo() needs to be run first. Output is
353 # written to clone.log.
359 echo "clone local git repository $reponame to $dir"
360 out=$(git clone "$GITSERVER/$reponame" "$dir" 2>&1)
363 git config credential.helper lfstest
364 echo "$out" > clone.log
368 # clone_repo_url clones a Git repository to the subdirectory $dir under $TRASHDIR.
369 # setup_remote_repo() needs to be run first. Output is written to clone.log.
375 echo "clone git repository $repo to $dir"
376 out=$(git clone "$repo" "$dir" 2>&1)
379 git config credential.helper lfstest
380 echo "$out" > clone.log
384 # clone_repo_ssl clones a repository from the test Git server to the subdirectory
385 # $dir under $TRASHDIR, using the SSL endpoint.
386 # setup_remote_repo() needs to be run first. Output is written to clone_ssl.log.
392 echo "clone local git repository $reponame to $dir"
393 out=$(git clone "$SSLGITSERVER/$reponame" "$dir" 2>&1)
396 git config credential.helper lfstest
398 echo "$out" > clone_ssl.log
402 # clone_repo_clientcert clones a repository from the test Git server to the subdirectory
403 # $dir under $TRASHDIR, using the client cert endpoint.
404 # setup_remote_repo() needs to be run first. Output is written to clone_client_cert.log.
405 clone_repo_clientcert() {
410 echo "clone $CLIENTCERTGITSERVER/$reponame to $dir"
412 out=$(git clone "$CLIENTCERTGITSERVER/$reponame" "$dir" 2>&1)
413 res="${PIPESTATUS[0]}"
416 if [ "0" -eq "$res" ]; then
418 echo "$out" > clone_client_cert.log
420 git config credential.helper lfstest
424 echo "$out" > clone_client_cert.log
425 if [ $(grep -c "NSInvalidArgumentException" clone_client_cert.log) -gt 0 ]; then
426 echo "client-cert-mac-openssl" > clone_client_cert.log
433 # setup_remote_repo_with_file creates a remote repo, clones it locally, commits
434 # a file tracked by LFS, and pushes it to the remote:
436 # setup_remote_repo_with_file "reponame" "filename"
437 setup_remote_repo_with_file() {
440 local dirname="$(dirname "$filename")"
442 setup_remote_repo "$reponame"
443 clone_repo "$reponame" "clone_$reponame"
447 git lfs track "$filename"
448 echo "$filename" > "$filename"
449 git add .gitattributes $filename
450 git commit -m "add $filename" | tee commit.log
452 grep "master (root-commit)" commit.log
453 grep "2 files changed" commit.log
454 grep "create mode 100644 $filename" commit.log
455 grep "create mode 100644 .gitattributes" commit.log
457 git push origin master 2>&1 | tee push.log
458 grep "master -> master" push.log
461 # substring_position returns the position of a substring in a 1-indexed search
464 # [ "$(substring_position "foo bar baz" "baz")" -eq "9" ]
465 substring_position() {
469 # 1) Print the string...
470 # 2) Remove the substring and everything after it
471 # 3) Count the number of characters (bytes) left, i.e., the offset of the
472 # string we were looking for.
475 | sed "s/$substr.*$//" \
479 # repo_endpoint returns the LFS endpoint for a given server and repository.
481 # [ "$GITSERVER/example/repo.git/info/lfs" = "$(repo_endpoint $GITSERVER example-repo)" ]
486 echo "$server/$repo.git/info/lfs"
489 # setup initializes the clean, isolated environment for integration tests.
493 if [ ! -d "$REMOTEDIR" ]; then
497 echo "# Git LFS: ${LFS_BIN:-$(which git-lfs)}"
498 git lfs version | sed -e 's/^/# /g'
499 git version | sed -e 's/^/# /g'
501 if [ -z "$GIT_LFS_NO_TEST_COUNT" ]; then
502 LFSTEST_URL="$LFS_URL_FILE" \
503 LFSTEST_SSL_URL="$LFS_SSL_URL_FILE" \
504 LFSTEST_CLIENT_CERT_URL="$LFS_CLIENT_CERT_URL_FILE" \
505 LFSTEST_DIR="$REMOTEDIR" \
506 LFSTEST_CERT="$LFS_CERT_FILE" \
507 LFSTEST_CLIENT_CERT="$LFS_CLIENT_CERT_FILE" \
508 LFSTEST_CLIENT_KEY="$LFS_CLIENT_KEY_FILE" \
509 lfstest-count-tests increment
512 wait_for_file "$LFS_URL_FILE"
513 wait_for_file "$LFS_SSL_URL_FILE"
514 wait_for_file "$LFS_CLIENT_CERT_URL_FILE"
515 wait_for_file "$LFS_CERT_FILE"
516 wait_for_file "$LFS_CLIENT_CERT_FILE"
517 wait_for_file "$LFS_CLIENT_KEY_FILE"
519 LFS_CLIENT_CERT_URL=`cat $LFS_CLIENT_CERT_URL_FILE`
521 # Set up the initial git config and osx keychain if applicable
523 if [ ! -d "$HOME" ]; then
527 if [ ! -f $HOME/.gitconfig ]; then
528 git lfs install --skip-repo
529 git config --global credential.usehttppath true
530 git config --global credential.helper lfstest
531 git config --global user.name "Git LFS Tests"
532 git config --global user.email "git-lfs@example.com"
533 git config --global http.sslcainfo "$LFS_CERT_FILE"
534 git config --global http.$LFS_CLIENT_CERT_URL/.sslKey "$LFS_CLIENT_KEY_FILE"
535 git config --global http.$LFS_CLIENT_CERT_URL/.sslCert "$LFS_CLIENT_CERT_FILE"
536 git config --global http.$LFS_CLIENT_CERT_URL/.sslVerify "false"
537 fi | sed -e 's/^/# /g'
539 # setup the git credential password storage
541 printf "user:pass" > "$CREDSDIR/127.0.0.1"
545 echo "# TMP: $TMPDIR"
546 echo "# CREDS: $CREDSDIR"
547 echo "# lfstest-gitserver:"
548 echo "# LFSTEST_URL=$LFS_URL_FILE"
549 echo "# LFSTEST_SSL_URL=$LFS_SSL_URL_FILE"
550 echo "# LFSTEST_CLIENT_CERT_URL=$LFS_CLIENT_CERT_URL_FILE ($LFS_CLIENT_CERT_URL)"
551 echo "# LFSTEST_CERT=$LFS_CERT_FILE"
552 echo "# LFSTEST_CLIENT_CERT=$LFS_CLIENT_CERT_FILE"
553 echo "# LFSTEST_CLIENT_KEY=$LFS_CLIENT_KEY_FILE"
554 echo "# LFSTEST_DIR=$REMOTEDIR"
557 # shutdown cleans the $TRASHDIR and shuts the test Git server down.
559 # every t/t-*.sh file should cleanup its trashdir
560 [ -z "$KEEPTRASH" ] && rm -rf "$TRASHDIR"
562 if [ -z "$GIT_LFS_NO_TEST_COUNT" ]; then
563 LFSTEST_DIR="$REMOTEDIR" \
564 LFS_URL_FILE="$LFS_URL_FILE" \
565 lfstest-count-tests decrement
572 printf "1..%i\n" "$tests"
575 ensure_git_version_isnt() {
576 local expectedComparison=$1
579 local gitVersion=$(git version | cut -d" " -f3)
582 compare_version $gitVersion $version
586 if [[ $result == $expectedComparison ]]; then
587 echo "skip: $0 (git version $(comparison_to_operator $expectedComparison) $version)"
596 # Compare $1 and $2 and return VERSION_EQUAL / VERSION_LOWER / VERSION_HIGHER
600 return $VERSION_EQUAL
603 local i ver1=($1) ver2=($2)
604 # fill empty fields in ver1 with zeros
605 for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
609 for ((i=0; i<${#ver1[@]}; i++))
611 if [[ -z ${ver2[i]} ]]
613 # fill empty fields in ver2 with zeros
616 if ((10#${ver1[i]} > 10#${ver2[i]}))
618 return $VERSION_HIGHER
620 if ((10#${ver1[i]} < 10#${ver2[i]}))
622 return $VERSION_LOWER
625 return $VERSION_EQUAL
628 comparison_to_operator() {
630 if [[ $1 == $VERSION_EQUAL ]]; then
632 elif [[ $1 == $VERSION_HIGHER ]]; then
634 elif [[ $1 == $VERSION_LOWER ]]; then
641 # Calculate the object ID from the string passed as the argument
643 printf "$1" | $SHASUM | cut -f 1 -d " "
646 # Calculate the object ID from the file passed as the argument
648 $SHASUM "$1" | cut -f 1 -d " "
651 # Get a date string with an offset
652 # Args: One or more date offsets of the form (regex) "[+-]\d+[dmyHM]"
653 # e.g. +1d = 1 day forward from today
654 # -5y = 5 years before today
656 # D=$(get_date +1y +1m -5H)
657 # returns date as string in RFC3339 format ccyy-mm-ddThh:MM:ssZ
658 # note returns in UTC time not local time hence Z and not +/-
660 # Wrapped because BSD (inc OSX) & GNU 'date' functions are different
661 # on Windows under Git Bash it's GNU
662 if date --version >/dev/null 2>&1 ; then # GNU
666 # GNU offsets are more verbose
668 val=${var:0:${#var}-1}
676 ARGS="$ARGS $val $unit"
678 date -d "$ARGS" -u +%Y-%m-%dT%TZ
685 date $ARGS -u +%Y-%m-%dT%TZ
689 # Convert potentially MinGW bash paths to native Windows paths
690 # Needed to match generic built paths in test scripts to native paths generated from Go
693 if [ $IS_WINDOWS -eq 1 ]; then
694 # Use params form to avoid interpreting any '\' characters
695 printf '%s' "$(cygpath -w $arg)"
701 # escape any instance of '\' with '\\' on Windows
704 if [ $IS_WINDOWS -eq 1 ]; then
705 printf '%s' "${unescaped//\\/\\\\}"
707 printf '%s' "$unescaped"
711 # As native_path but escape all backslash characters to "\\"
712 native_path_escaped() {
713 local unescaped=$(native_path "$1")
714 escape_path "$unescaped"
717 # native_path_list_separator prints the operating system-specific path list
719 native_path_list_separator() {
720 if [ "$IS_WINDOWS" -eq 1 ]; then
728 if [ $IS_WINDOWS -eq 1 ]; then
735 # Compare 2 lists which are newline-delimited in a string, ignoring ordering and blank lines
736 contains_same_elements() {
737 # Remove blank lines then sort
738 diff -u <(printf '%s' "$1" | grep -v '^$' | sort) <(printf '%s' "$2" | grep -v '^$' | sort)
741 is_stdin_attached() {
747 if [ -z "$GIT_LFS_TEST_DIR" ]; then
748 echo "No GIT_LFS_TEST_DIR. Skipping..."
757 prefix=`git rev-parse --show-prefix`
758 hashsrc=`printf "$src" | git hash-object -w --stdin`
760 git update-index --add --cacheinfo 120000 "$hashsrc" "$prefix$dest"
761 git checkout -- "$dest"