github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/test/testhelpers.sh (about)

     1  #!/usr/bin/env bash
     2  
     3  # assert_pointer confirms that the pointer in the repository for $path in the
     4  # given $ref matches the given $oid and $size.
     5  #
     6  #   $ assert_pointer "master" "path/to/file" "some-oid" 123
     7  assert_pointer() {
     8    local ref="$1"
     9    local path="$2"
    10    local oid="$3"
    11    local size="$4"
    12  
    13    gitblob=$(git ls-tree -lrz "$ref" |
    14      while read -r -d $'\0' x; do
    15        echo $x
    16      done |
    17      grep "$path" | cut -f 3 -d " ")
    18  
    19    actual=$(git cat-file -p $gitblob)
    20    expected=$(pointer $oid $size)
    21  
    22    if [ "$expected" != "$actual" ]; then
    23      exit 1
    24    fi
    25  }
    26  
    27  # assert_local_object confirms that an object file is stored for the given oid &
    28  # has the correct size
    29  # $ assert_local_object "some-oid" size
    30  assert_local_object() {
    31    local oid="$1"
    32    local size="$2"
    33    local cfg=`git lfs env | grep LocalMediaDir`
    34    local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
    35    actualsize=$(wc -c <"$f" | tr -d '[[:space:]]')
    36    if [ "$size" != "$actualsize" ]; then
    37      exit 1
    38    fi
    39  }
    40  
    41  # refute_local_object confirms that an object file is NOT stored for an oid.
    42  # If "$size" is given as the second argument, assert that the file exists _and_
    43  # that it does _not_ the expected size
    44  #
    45  # $ refute_local_object "some-oid"
    46  # $ refute_local_object "some-oid" "123"
    47  refute_local_object() {
    48    local oid="$1"
    49    local size="$2"
    50    local cfg=`git lfs env | grep LocalMediaDir`
    51    local regex="LocalMediaDir=(\S+)"
    52    local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
    53    if [ -e $f ]; then
    54      if [ -z "$size" ]; then
    55        exit 1
    56      fi
    57  
    58      actual_size="$(wc -c < "$f" | awk '{ print $1 }')"
    59      if [ "$size" -eq "$actual_size" ]; then
    60        echo >&2 "fatal: expected object $oid not to have size: $size"
    61        exit 1
    62      fi
    63    fi
    64  }
    65  
    66  # delete_local_object deletes the local storage for an oid
    67  # $ delete_local_object "some-oid"
    68  delete_local_object() {
    69    local oid="$1"
    70    local cfg=`git lfs env | grep LocalMediaDir`
    71    local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
    72    rm "$f"
    73  }
    74  
    75  # corrupt_local_object corrupts the local storage for an oid
    76  # $ corrupt_local_object "some-oid"
    77  corrupt_local_object() {
    78    local oid="$1"
    79    local cfg=`git lfs env | grep LocalMediaDir`
    80    local f="${cfg:14}/${oid:0:2}/${oid:2:2}/$oid"
    81    cp /dev/null "$f"
    82  }
    83  
    84  
    85  # check that the object does not exist in the git lfs server. HTTP log is
    86  # written to http.log. JSON output is written to http.json.
    87  #
    88  #   $ refute_server_object "reponame" "oid"
    89  refute_server_object() {
    90    local reponame="$1"
    91    local oid="$2"
    92    curl -v "$GITSERVER/$reponame.git/info/lfs/objects/batch" \
    93      -u "user:pass" \
    94      -o http.json \
    95      -d "{\"operation\":\"download\",\"objects\":[{\"oid\":\"$oid\"}]}" \
    96      -H "Accept: application/vnd.git-lfs+json" \
    97      -H "X-Check-Object: 1" \
    98      -H "X-Ignore-Retries: true" 2>&1 |
    99      tee http.log
   100  
   101    [ "0" = "$(grep -c "download" http.json)" ] || {
   102      cat http.json
   103      exit 1
   104    }
   105  }
   106  
   107  # Delete an object on the lfs server. HTTP log is
   108  # written to http.log. JSON output is written to http.json.
   109  #
   110  #   $ delete_server_object "reponame" "oid"
   111  delete_server_object() {
   112    local reponame="$1"
   113    local oid="$2"
   114    curl -v "$GITSERVER/$reponame.git/info/lfs/objects/$oid" \
   115      -X DELETE \
   116      -u "user:pass" \
   117      -o http.json \
   118      -H "Accept: application/vnd.git-lfs+json" 2>&1 |
   119      tee http.log
   120  
   121    grep "200 OK" http.log
   122  }
   123  
   124  # check that the object does exist in the git lfs server. HTTP log is written
   125  # to http.log. JSON output is written to http.json.
   126  assert_server_object() {
   127    local reponame="$1"
   128    local oid="$2"
   129    local refspec="$3"
   130    curl -v "$GITSERVER/$reponame.git/info/lfs/objects/batch" \
   131      -u "user:pass" \
   132      -o http.json \
   133      -d "{\"operation\":\"download\",\"objects\":[{\"oid\":\"$oid\"}],\"ref\":{\"name\":\"$refspec\"}}" \
   134      -H "Accept: application/vnd.git-lfs+json" \
   135      -H "X-Check-Object: 1" \
   136      -H "X-Ignore-Retries: true" 2>&1 |
   137      tee http.log
   138    grep "200 OK" http.log
   139  
   140    grep "download" http.json || {
   141      cat http.json
   142      exit 1
   143    }
   144  }
   145  
   146  # This asserts the lock path and returns the lock ID by parsing the response of
   147  #
   148  #   git lfs lock --json <path>
   149  assert_lock() {
   150    local log="$1"
   151    local path="$2"
   152  
   153    if [ $(grep -c "\"path\":\"$path\"" "$log") -eq 0 ]; then
   154      echo "path '$path' not found in:"
   155      cat "$log"
   156      exit 1
   157    fi
   158  
   159    local jsonid=$(grep -oh "\"id\":\"\w\+\"" "$log")
   160    echo "${jsonid:3}" | tr -d \"\:
   161  }
   162  
   163  # assert that a lock with the given ID exists on the test server
   164  assert_server_lock() {
   165    local reponame="$1"
   166    local id="$2"
   167    local refspec="$3"
   168  
   169    curl -v "$GITSERVER/$reponame.git/info/lfs/locks?refspec=$refspec" \
   170      -u "user:pass" \
   171      -o http.json \
   172      -H "Accept:application/vnd.git-lfs+json" 2>&1 |
   173      tee http.log
   174  
   175    grep "200 OK" http.log
   176    grep "$id" http.json || {
   177      cat http.json
   178      exit 1
   179    }
   180  }
   181  
   182  # refute that a lock with the given ID exists on the test server
   183  refute_server_lock() {
   184    local reponame="$1"
   185    local id="$2"
   186    local refspec="$3"
   187  
   188    curl -v "$GITSERVER/$reponame.git/info/lfs/locks?refspec=$refspec" \
   189      -u "user:pass" \
   190      -o http.json \
   191      -H "Accept:application/vnd.git-lfs+json" 2>&1 | tee http.log
   192  
   193    grep "200 OK" http.log
   194  
   195    [ $(grep -c "$id" http.json) -eq 0 ]
   196  }
   197  
   198  # Assert that .gitattributes contains a given attribute N times
   199  assert_attributes_count() {
   200    local fileext="$1"
   201    local attrib="$2"
   202    local count="$3"
   203  
   204    pattern="\(*.\)\?$fileext\(.*\)$attrib"
   205    actual=$(grep -e "$pattern" .gitattributes | wc -l)
   206    if [ "$(printf "%d" "$actual")" != "$count" ]; then
   207      echo "wrong number of $attrib entries for $fileext"
   208      echo "expected: $count actual: $actual"
   209      cat .gitattributes
   210      exit 1
   211    fi
   212  }
   213  
   214  assert_file_writeable() {
   215    ls -l "$1" | grep -e "^-rw"
   216  }
   217  
   218  refute_file_writeable() {
   219    ls -l "$1" | grep -e "^-r-"
   220  }
   221  
   222  git_root() {
   223    git rev-parse --show-toplevel 2>/dev/null
   224  }
   225  
   226  dot_git_dir() {
   227    echo "$(git_root)/.git"
   228  }
   229  
   230  assert_hooks() {
   231    local git_root="$1"
   232  
   233    if [ -z "$git_root" ]; then
   234      echo >&2 "fatal: (assert_hooks) not in git repository"
   235      exit 1
   236    fi
   237  
   238    [ -x "$git_root/hooks/post-checkout" ]
   239    [ -x "$git_root/hooks/post-commit" ]
   240    [ -x "$git_root/hooks/post-merge" ]
   241    [ -x "$git_root/hooks/pre-push" ]
   242  }
   243  
   244  assert_clean_status() {
   245    status="$(git status)"
   246    echo "$status" | grep "working tree clean" || {
   247      echo $status
   248      git lfs status
   249    }
   250  }
   251  
   252  # pointer returns a string Git LFS pointer file.
   253  #
   254  #   $ pointer abc-some-oid 123 <version>
   255  #   > version ...
   256  pointer() {
   257    local oid=$1
   258    local size=$2
   259    local version=${3:-https://git-lfs.github.com/spec/v1}
   260    printf "version %s
   261  oid sha256:%s
   262  size %s
   263  " "$version" "$oid" "$size"
   264  }
   265  
   266  # wait_for_file simply sleeps until a file exists.
   267  #
   268  #   $ wait_for_file "path/to/upcoming/file"
   269  wait_for_file() {
   270    local filename="$1"
   271    n=0
   272    wait_time=1
   273    while [ $n -lt 17 ]; do
   274      if [ -s $filename ]; then
   275        return 0
   276      fi
   277  
   278      sleep $wait_time
   279      n=`expr $n + 1`
   280      if [ $wait_time -lt 4 ]; then
   281        wait_time=`expr $wait_time \* 2`
   282      fi
   283    done
   284  
   285    echo "$filename did not appear after 60 seconds."
   286    return 1
   287  }
   288  
   289  # setup_remote_repo initializes a bare Git repository that is accessible through
   290  # the test Git server. The `pwd` is set to the repository's directory, in case
   291  # further commands need to be run. This server is running for every test in a
   292  # script/integration run, so every test file should setup its own remote
   293  # repository to avoid conflicts.
   294  #
   295  #   $ setup_remote_repo "some-name"
   296  #
   297  setup_remote_repo() {
   298    local reponame="$1"
   299    echo "set up remote git repository: $reponame"
   300    repodir="$REMOTEDIR/$reponame.git"
   301    mkdir -p "$repodir"
   302    cd "$repodir"
   303    git init --bare
   304    git config http.receivepack true
   305    git config receive.denyCurrentBranch ignore
   306  }
   307  
   308  # creates a bare remote repository for a local clone. Useful to test pushing to
   309  # a fresh remote server.
   310  #
   311  #   $ setup_alternate_remote "$reponame-whatever"
   312  #   $ setup_alternate_remote "$reponame-whatever" "other-remote-name"
   313  #
   314  setup_alternate_remote() {
   315    local newRemoteName=$1
   316    local remote=${2:-origin}
   317  
   318    wd=`pwd`
   319  
   320    setup_remote_repo "$newRemoteName"
   321    cd $wd
   322    git remote rm "$remote"
   323    git remote add "$remote" "$GITSERVER/$newRemoteName"
   324  }
   325  
   326  # clone_repo clones a repository from the test Git server to the subdirectory
   327  # $dir under $TRASHDIR. setup_remote_repo() needs to be run first. Output is
   328  # written to clone.log.
   329  clone_repo() {
   330    cd "$TRASHDIR"
   331  
   332    local reponame="$1"
   333    local dir="$2"
   334    echo "clone local git repository $reponame to $dir"
   335    out=$(git clone "$GITSERVER/$reponame" "$dir" 2>&1)
   336    cd "$dir"
   337  
   338    git config credential.helper lfstest
   339    echo "$out" > clone.log
   340    echo "$out"
   341  }
   342  
   343  # clone_repo_url clones a Git repository to the subdirectory $dir under $TRASHDIR.
   344  # setup_remote_repo() needs to be run first. Output is written to clone.log.
   345  clone_repo_url() {
   346    cd "$TRASHDIR"
   347  
   348    local repo="$1"
   349    local dir="$2"
   350    echo "clone git repository $repo to $dir"
   351    out=$(git clone "$repo" "$dir" 2>&1)
   352    cd "$dir"
   353  
   354    git config credential.helper lfstest
   355    echo "$out" > clone.log
   356    echo "$out"
   357  }
   358  
   359  # clone_repo_ssl clones a repository from the test Git server to the subdirectory
   360  # $dir under $TRASHDIR, using the SSL endpoint.
   361  # setup_remote_repo() needs to be run first. Output is written to clone_ssl.log.
   362  clone_repo_ssl() {
   363    cd "$TRASHDIR"
   364  
   365    local reponame="$1"
   366    local dir="$2"
   367    echo "clone local git repository $reponame to $dir"
   368    out=$(git clone "$SSLGITSERVER/$reponame" "$dir" 2>&1)
   369    cd "$dir"
   370  
   371    git config credential.helper lfstest
   372  
   373    echo "$out" > clone_ssl.log
   374    echo "$out"
   375  }
   376  
   377  # clone_repo_clientcert clones a repository from the test Git server to the subdirectory
   378  # $dir under $TRASHDIR, using the client cert endpoint.
   379  # setup_remote_repo() needs to be run first. Output is written to clone_client_cert.log.
   380  clone_repo_clientcert() {
   381    cd "$TRASHDIR"
   382  
   383    local reponame="$1"
   384    local dir="$2"
   385    echo "clone $CLIENTCERTGITSERVER/$reponame to $dir"
   386    set +e
   387    out=$(git clone "$CLIENTCERTGITSERVER/$reponame" "$dir" 2>&1)
   388    res="${PIPESTATUS[0]}"
   389    set -e
   390  
   391    if [ "0" -eq "$res" ]; then
   392      cd "$dir"
   393      echo "$out" > clone_client_cert.log
   394  
   395      git config credential.helper lfstest
   396      exit 0
   397    fi
   398  
   399    echo "$out" > clone_client_cert.log
   400    if [ $(grep -c "NSInvalidArgumentException" clone_client_cert.log) -gt 0 ]; then
   401      echo "client-cert-mac-openssl" > clone_client_cert.log
   402      exit 0
   403    fi
   404  
   405    exit 1
   406  }
   407  
   408  # setup_remote_repo_with_file creates a remote repo, clones it locally, commits
   409  # a file tracked by LFS, and pushes it to the remote:
   410  #
   411  #     setup_remote_repo_with_file "reponame" "filename"
   412  setup_remote_repo_with_file() {
   413    local reponame="$1"
   414    local filename="$2"
   415    local dirname="$(dirname "$filename")"
   416  
   417    setup_remote_repo "$reponame"
   418    clone_repo "$reponame" "clone_$reponame"
   419  
   420    mkdir -p "$dirname"
   421  
   422    git lfs track "$filename"
   423    echo "$filename" > "$filename"
   424    git add .gitattributes $filename
   425    git commit -m "add $filename" | tee commit.log
   426  
   427    grep "master (root-commit)" commit.log
   428    grep "2 files changed" commit.log
   429    grep "create mode 100644 $filename" commit.log
   430    grep "create mode 100644 .gitattributes" commit.log
   431  
   432    git push origin master 2>&1 | tee push.log
   433    grep "master -> master" push.log
   434  }
   435  
   436  # substring_position returns the position of a substring in a 1-indexed search
   437  # space.
   438  #
   439  #     [ "$(substring_position "foo bar baz" "baz")" -eq "9" ]
   440  substring_position() {
   441    local str="$1"
   442    local substr="$2"
   443  
   444    # 1) Print the string...
   445    # 2) Remove the substring and everything after it
   446    # 3) Count the number of characters (bytes) left, i.e., the offset of the
   447    #    string we were looking for.
   448  
   449    echo "$str" \
   450      | sed "s/$substr.*$//" \
   451      | wc -c
   452  }
   453  
   454  # repo_endpoint returns the LFS endpoint for a given server and repository.
   455  #
   456  #     [ "$GITSERVER/example/repo.git/info/lfs" = "$(repo_endpoint $GITSERVER example-repo)" ]
   457  repo_endpoint() {
   458    local server="$1"
   459    local repo="$2"
   460  
   461    echo "$server/$repo.git/info/lfs"
   462  }
   463  
   464  # setup initializes the clean, isolated environment for integration tests.
   465  setup() {
   466    cd "$ROOTDIR"
   467  
   468    rm -rf "$REMOTEDIR"
   469    mkdir "$REMOTEDIR"
   470  
   471    if [ -z "$SKIPCOMPILE" ] && [ -z "$LFS_BIN" ]; then
   472      echo "compile git-lfs for $0"
   473      script/bootstrap || {
   474        return $?
   475      }
   476    fi
   477  
   478    echo "Git LFS: ${LFS_BIN:-$(which git-lfs)}"
   479    git lfs version
   480    git version
   481  
   482    if [ -z "$SKIPCOMPILE" ]; then
   483      [ $IS_WINDOWS -eq 1 ] && EXT=".exe"
   484      for go in test/cmd/*.go; do
   485        GO15VENDOREXPERIMENT=1 go build -o "$BINPATH/$(basename $go .go)$EXT" "$go"
   486      done
   487      if [ -z "$SKIPAPITESTCOMPILE" ]; then
   488        # Ensure API test util is built during tests to ensure it stays in sync
   489        GO15VENDOREXPERIMENT=1 go build -o "$BINPATH/git-lfs-test-server-api$EXT" "test/git-lfs-test-server-api/main.go" "test/git-lfs-test-server-api/testdownload.go" "test/git-lfs-test-server-api/testupload.go"
   490      fi
   491    fi
   492  
   493    LFSTEST_URL="$LFS_URL_FILE" LFSTEST_SSL_URL="$LFS_SSL_URL_FILE" LFSTEST_CLIENT_CERT_URL="$LFS_CLIENT_CERT_URL_FILE" LFSTEST_DIR="$REMOTEDIR" LFSTEST_CERT="$LFS_CERT_FILE" LFSTEST_CLIENT_CERT="$LFS_CLIENT_CERT_FILE" LFSTEST_CLIENT_KEY="$LFS_CLIENT_KEY_FILE" lfstest-gitserver > "$REMOTEDIR/gitserver.log" 2>&1 &
   494  
   495    wait_for_file "$LFS_URL_FILE"
   496    wait_for_file "$LFS_SSL_URL_FILE"
   497    wait_for_file "$LFS_CLIENT_CERT_URL_FILE"
   498    wait_for_file "$LFS_CERT_FILE"
   499    wait_for_file "$LFS_CLIENT_CERT_FILE"
   500    wait_for_file "$LFS_CLIENT_KEY_FILE"
   501  
   502    LFS_CLIENT_CERT_URL=`cat $LFS_CLIENT_CERT_URL_FILE`
   503  
   504    # Set up the initial git config and osx keychain if applicable
   505    HOME="$TESTHOME"
   506    mkdir "$HOME"
   507    git lfs install --skip-repo
   508    git config --global credential.usehttppath true
   509    git config --global credential.helper lfstest
   510    git config --global user.name "Git LFS Tests"
   511    git config --global user.email "git-lfs@example.com"
   512    git config --global http.sslcainfo "$LFS_CERT_FILE"
   513    git config --global http.$LFS_CLIENT_CERT_URL/.sslKey "$LFS_CLIENT_KEY_FILE"
   514    git config --global http.$LFS_CLIENT_CERT_URL/.sslCert "$LFS_CLIENT_CERT_FILE"
   515    git config --global http.$LFS_CLIENT_CERT_URL/.sslVerify "false"
   516  
   517    ( grep "git-lfs clean" "$REMOTEDIR/home/.gitconfig" > /dev/null && grep "git-lfs filter-process" "$REMOTEDIR/home/.gitconfig" > /dev/null ) || {
   518      echo "global git config should be set in $REMOTEDIR/home"
   519      ls -al "$REMOTEDIR/home"
   520      exit 1
   521    }
   522  
   523    # setup the git credential password storage
   524    mkdir -p "$CREDSDIR"
   525    printf "user:pass" > "$CREDSDIR/127.0.0.1"
   526  
   527    echo
   528    echo "HOME: $HOME"
   529    echo "TMP: $TMPDIR"
   530    echo "CREDS: $CREDSDIR"
   531    echo "lfstest-gitserver:"
   532    echo "  LFSTEST_URL=$LFS_URL_FILE"
   533    echo "  LFSTEST_SSL_URL=$LFS_SSL_URL_FILE"
   534    echo "  LFSTEST_CLIENT_CERT_URL=$LFS_CLIENT_CERT_URL_FILE ($LFS_CLIENT_CERT_URL)"
   535    echo "  LFSTEST_CERT=$LFS_CERT_FILE"
   536    echo "  LFSTEST_CLIENT_CERT=$LFS_CLIENT_CERT_FILE"
   537    echo "  LFSTEST_CLIENT_KEY=$LFS_CLIENT_KEY_FILE"
   538    echo "  LFSTEST_DIR=$REMOTEDIR"
   539    echo "GIT:"
   540    git config --global --get-regexp "lfs|credential|user"
   541  
   542    echo
   543  }
   544  
   545  # shutdown cleans the $TRASHDIR and shuts the test Git server down.
   546  shutdown() {
   547    # every test/test-*.sh file should cleanup its trashdir
   548    [ -z "$KEEPTRASH" ] && rm -rf "$TRASHDIR"
   549  
   550    if [ "$SHUTDOWN_LFS" != "no" ]; then
   551      # only cleanup test/remote after script/integration done OR a single
   552      # test/test-*.sh file is run manually.
   553      if [ -s "$LFS_URL_FILE" ]; then
   554        curl -s "$(cat "$LFS_URL_FILE")/shutdown"
   555      fi
   556  
   557      [ -z "$KEEPTRASH" ] && rm -rf "$REMOTEDIR"
   558  
   559      # delete entire lfs test root if we created it (double check pattern)
   560      if [ -z "$KEEPTRASH" ] && [ "$RM_GIT_LFS_TEST_DIR" = "yes" ] && [[ $GIT_LFS_TEST_DIR == *"$TEMPDIR_PREFIX"* ]]; then
   561        rm -rf "$GIT_LFS_TEST_DIR"
   562      fi
   563  fi
   564  }
   565  
   566  ensure_git_version_isnt() {
   567    local expectedComparison=$1
   568    local version=$2
   569  
   570    local gitVersion=$(git version | cut -d" " -f3)
   571  
   572    set +e
   573    compare_version $gitVersion $version
   574    result=$?
   575    set -e
   576  
   577    if [[ $result == $expectedComparison ]]; then
   578      echo "skip: $0 (git version $(comparison_to_operator $expectedComparison) $version)"
   579      exit
   580    fi
   581  }
   582  
   583  VERSION_EQUAL=0
   584  VERSION_HIGHER=1
   585  VERSION_LOWER=2
   586  
   587  # Compare $1 and $2 and return VERSION_EQUAL / VERSION_LOWER / VERSION_HIGHER
   588  compare_version() {
   589      if [[ $1 == $2 ]]
   590      then
   591          return $VERSION_EQUAL
   592      fi
   593      local IFS=.
   594      local i ver1=($1) ver2=($2)
   595      # fill empty fields in ver1 with zeros
   596      for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
   597      do
   598          ver1[i]=0
   599      done
   600      for ((i=0; i<${#ver1[@]}; i++))
   601      do
   602          if [[ -z ${ver2[i]} ]]
   603          then
   604              # fill empty fields in ver2 with zeros
   605              ver2[i]=0
   606          fi
   607          if ((10#${ver1[i]} > 10#${ver2[i]}))
   608          then
   609              return $VERSION_HIGHER
   610          fi
   611          if ((10#${ver1[i]} < 10#${ver2[i]}))
   612          then
   613              return $VERSION_LOWER
   614          fi
   615      done
   616      return $VERSION_EQUAL
   617  }
   618  
   619  comparison_to_operator() {
   620    local comparison=$1
   621    if [[ $1 == $VERSION_EQUAL ]]; then
   622      echo "=="
   623    elif [[ $1 == $VERSION_HIGHER ]]; then
   624      echo ">"
   625    elif [[ $1 == $VERSION_LOWER ]]; then
   626      echo "<"
   627    else
   628      echo "???"
   629    fi
   630  }
   631  
   632  # Calculate the object ID from the string passed as the argument
   633  calc_oid() {
   634    printf "$1" | $SHASUM | cut -f 1 -d " "
   635  }
   636  
   637  # Calculate the object ID from the file passed as the argument
   638  calc_oid_file() {
   639    $SHASUM "$1" | cut -f 1 -d " "
   640  }
   641  
   642  # Get a date string with an offset
   643  # Args: One or more date offsets of the form (regex) "[+-]\d+[dmyHM]"
   644  # e.g. +1d = 1 day forward from today
   645  #      -5y = 5 years before today
   646  # Example call:
   647  #   D=$(get_date +1y +1m -5H)
   648  # returns date as string in RFC3339 format ccyy-mm-ddThh:MM:ssZ
   649  # note returns in UTC time not local time hence Z and not +/-
   650  get_date() {
   651    # Wrapped because BSD (inc OSX) & GNU 'date' functions are different
   652    # on Windows under Git Bash it's GNU
   653    if date --version >/dev/null 2>&1 ; then # GNU
   654      ARGS=""
   655      for var in "$@"
   656      do
   657          # GNU offsets are more verbose
   658          unit=${var: -1}
   659          val=${var:0:${#var}-1}
   660          case "$unit" in
   661            d) unit="days" ;;
   662            m) unit="months" ;;
   663            y) unit="years"  ;;
   664            H) unit="hours"  ;;
   665            M) unit="minutes" ;;
   666          esac
   667          ARGS="$ARGS $val $unit"
   668      done
   669      date -d "$ARGS" -u +%Y-%m-%dT%TZ
   670    else # BSD
   671      ARGS=""
   672      for var in "$@"
   673      do
   674          ARGS="$ARGS -v$var"
   675      done
   676      date $ARGS -u +%Y-%m-%dT%TZ
   677    fi
   678  }
   679  
   680  # Convert potentially MinGW bash paths to native Windows paths
   681  # Needed to match generic built paths in test scripts to native paths generated from Go
   682  native_path() {
   683    local arg=$1
   684    if [ $IS_WINDOWS -eq 1 ]; then
   685      # Use params form to avoid interpreting any '\' characters
   686      printf '%s' "$(cygpath -w $arg)"
   687    else
   688      printf '%s' "$arg"
   689    fi
   690  }
   691  
   692  # escape any instance of '\' with '\\' on Windows
   693  escape_path() {
   694    local unescaped="$1"
   695    if [ $IS_WINDOWS -eq 1 ]; then
   696      printf '%s' "${unescaped//\\/\\\\}"
   697    else
   698      printf '%s' "$unescaped"
   699    fi
   700  }
   701  
   702  # As native_path but escape all backslash characters to "\\"
   703  native_path_escaped() {
   704    local unescaped=$(native_path "$1")
   705    escape_path "$unescaped"
   706  }
   707  
   708  cat_end() {
   709    if [ $IS_WINDOWS -eq 1 ]; then
   710      printf '^M$'
   711    else
   712      printf '$'
   713    fi
   714  }
   715  
   716  # Compare 2 lists which are newline-delimited in a string, ignoring ordering and blank lines
   717  contains_same_elements() {
   718    # Remove blank lines then sort
   719    printf '%s' "$1" | grep -v '^$' | sort > a.txt
   720    printf '%s' "$2" | grep -v '^$' | sort > b.txt
   721  
   722    set +e
   723    diff -u a.txt b.txt 1>&2
   724    res=$?
   725    set -e
   726    rm a.txt b.txt
   727    exit $res
   728  }
   729  
   730  is_stdin_attached() {
   731    test -t0
   732    echo $?
   733  }
   734  
   735  has_test_dir() {
   736    if [ -z "$GIT_LFS_TEST_DIR" ]; then
   737      echo "No GIT_LFS_TEST_DIR. Skipping..."
   738      exit 0
   739    fi
   740  }