diff --git a/test_git.sh b/test_git.sh new file mode 100755 index 0000000..97dc4e8 --- /dev/null +++ b/test_git.sh @@ -0,0 +1,988 @@ +#!/bin/bash +# +# Copyright 2023 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -o errexit +set -o nounset +set -o pipefail + +function fail() { + echo "FAIL:" "$@" >&3 + return 42 +} + +function pass() { + echo "PASS" +} + +function assert_file_exists() { + if ! [[ -f "$1" ]]; then + fail "$1 does not exist" + fi +} + +function assert_file_absent() { + if [[ -f "$1" ]]; then + fail "$1 exists but should not" + fi +} + +function assert_eq() { + if [[ "$1" == "$2" ]]; then + return + fi + fail "'$1' does not equal '$2'" +} + +function assert_substr() { + if [[ "$1" =~ "$2" ]]; then + return + fi + fail "'$1' does not contain '$2'" +} + +# DIR is the directory in which all this test's state lives. +RUNID="${RANDOM}${RANDOM}" +DIR="/tmp/git-sync-git.$RUNID" +mkdir "$DIR" + +# WORKDIR is where test cases run +WORKDIR="$DIR/work" +function clean_workdir() { + rm -rf "$WORKDIR" + mkdir -p "$WORKDIR" +} + +# +# After all the test functions are defined, we can iterate over them and run +# them all automatically. See the end of this file. +# + +############################################## +# Test `git init` on an existing repo +############################################## +function git::reinit_existing_repo() { + git init -b main + date > file + git add file + git commit -qam 'commit_1' + TREE1="$(git ls-tree HEAD | cut -d' ' -f3 | cut -f1)" + git init + TREE2="$(git ls-tree HEAD | cut -d' ' -f3 | cut -f1)" + assert_eq "$TREE1" "$TREE2" +} + +############################################## +# Test `git fetch` of a branch +############################################## +function git::fetch_upstream_branch() { + mkdir upstream + pushd upstream >/dev/null + git init -b main + + # A commit on branch 1 + git checkout -b upstream_branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + + # A commit on branch 2 + git checkout -b upstream_branch_2 + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + assert_substr "$(git cat-file -t "$SHA1" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + + git fetch "file://$WORKDIR/upstream" upstream_branch_1 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + git checkout "$SHA1" + assert_file_exists file_1 + assert_file_absent file_2 + + git fetch "file://$WORKDIR/upstream" upstream_branch_2 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + git checkout "$SHA2" + assert_file_exists file_1 + assert_file_exists file_2 +} + +############################################## +# Test `git fetch` of a tag +############################################## +function git::fetch_upstream_tag() { + mkdir upstream + pushd upstream >/dev/null + git init -b main + + # A tag on branch 1 (not at HEAD) + git checkout -b upstream_branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + git tag upstream_tag_1 + + # Another tag on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + git tag upstream_tag_2 + + # A tag on branch 2 (not at HEAD) + git checkout -b upstream_branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + git tag upstream_tag_3 + + # Another tag on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + git tag upstream_tag_4 + + popd >/dev/null + + mkdir clone + pushd clone >/dev/null + git init -b clone_branch + + assert_substr "$(git cat-file -t "$SHA1" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + + git fetch "file://$WORKDIR/upstream" upstream_tag_1 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA1" + assert_file_exists file_1 + assert_file_absent file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_tag_2 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA2" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_tag_3 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA3" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_tag_4 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_eq "$(git cat-file -t "$SHA4")" "commit" + git checkout "$SHA4" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_exists file_4 +} + +############################################## +# Test `git fetch` of an annotated tag +############################################## +function git::fetch_upstream_tag_annotated() { + mkdir upstream + pushd upstream >/dev/null + git init -b main + + # A tag on branch 1 (not at HEAD) + git checkout -b upstream_branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + git tag -am "anntag_1" upstream_anntag_1 + + # Another tag on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + git tag -am "anntag_2" upstream_anntag_2 + + # A tag on branch 2 (not at HEAD) + git checkout -b upstream_branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + git tag -am "anntag_3" upstream_anntag_3 + + # Another tag on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + git tag -am "anntag_4" upstream_anntag_4 + + popd >/dev/null + + mkdir clone + pushd clone >/dev/null + git init -b clone_branch + + assert_substr "$(git cat-file -t "$SHA1" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + + git fetch "file://$WORKDIR/upstream" upstream_anntag_1 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA1" + assert_file_exists file_1 + assert_file_absent file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_anntag_2 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA2" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_anntag_3 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA3" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" upstream_anntag_4 + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_eq "$(git cat-file -t "$SHA4")" "commit" + git checkout "$SHA4" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_exists file_4 +} + +############################################## +# Test `git fetch` of a SHA +############################################## +function git::fetch_upstream_sha() { + mkdir upstream + pushd upstream >/dev/null + git init -b main + + # A commit on branch 1 (not at HEAD) + git checkout -b upstream_branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + + # Another commit on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + + # A commit on branch 2 (not at HEAD) + git checkout -b upstream_branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + + # Another commit on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + + popd >/dev/null + + mkdir clone + pushd clone >/dev/null + git init -b clone_branch + + assert_substr "$(git cat-file -t "$SHA1" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + + git fetch "file://$WORKDIR/upstream" "$SHA1" + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_substr "$(git cat-file -t "$SHA2" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA1" + assert_file_exists file_1 + assert_file_absent file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" "$SHA2" + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_substr "$(git cat-file -t "$SHA3" 2>&1 || true)" "could not get object info" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA2" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_absent file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" "$SHA3" + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_substr "$(git cat-file -t "$SHA4" 2>&1 || true)" "could not get object info" + git checkout "$SHA3" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_absent file_4 + + git fetch "file://$WORKDIR/upstream" "$SHA4" + assert_eq "$(git cat-file -t "$SHA1")" "commit" + assert_eq "$(git cat-file -t "$SHA2")" "commit" + assert_eq "$(git cat-file -t "$SHA3")" "commit" + assert_eq "$(git cat-file -t "$SHA4")" "commit" + git checkout "$SHA4" + assert_file_exists file_1 + assert_file_exists file_2 + assert_file_exists file_3 + assert_file_exists file_4 +} + +############################################## +# Test git shallow fetch from a branch +############################################## +function git::shallow_fetch_branch() { + mkdir upstream + pushd upstream >/dev/null + git init -b upstream_branch + + # Some commits + date > file_1 + git add file_1 + git commit -qam 'commit_1' + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA="$(git rev-parse HEAD)" + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + git fetch "file://$WORKDIR/upstream" upstream_branch + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" + + git fetch "file://$WORKDIR/upstream" upstream_branch --depth 1 + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "true" + + git fetch "file://$WORKDIR/upstream" upstream_branch --unshallow + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" +} + +############################################## +# Test git shallow fetch from a tag +############################################## +function git::shallow_fetch_tag() { + mkdir upstream + pushd upstream >/dev/null + git init -b upstream_branch + + # Some commits + date > file_1 + git add file_1 + git commit -qam 'commit_1' + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA="$(git rev-parse HEAD)" + git tag upstream_tag + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + git fetch "file://$WORKDIR/upstream" upstream_tag + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" + + git fetch "file://$WORKDIR/upstream" upstream_tag --depth 1 + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "true" + + git fetch "file://$WORKDIR/upstream" upstream_tag --unshallow + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" +} + +############################################## +# Test git shallow fetch from an annotated tag +############################################## +function git::shallow_fetch_tag_annotated() { + mkdir upstream + pushd upstream >/dev/null + git init -b upstream_branch + + # Some commits + date > file_1 + git add file_1 + git commit -qam 'commit_1' + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA="$(git rev-parse HEAD)" + git tag -am "upstream_anntag" upstream_anntag + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + git fetch "file://$WORKDIR/upstream" upstream_anntag + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" + + git fetch "file://$WORKDIR/upstream" upstream_anntag --depth 1 + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "true" + + git fetch "file://$WORKDIR/upstream" upstream_anntag --unshallow + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" +} + +############################################## +# Test git shallow fetch from a SHA +############################################## +function git::shallow_fetch_sha() { + mkdir upstream + pushd upstream >/dev/null + git init -b upstream_branch + + # Some commits + date > file_1 + git add file_1 + git commit -qam 'commit_1' + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA="$(git rev-parse HEAD)" + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + git fetch "file://$WORKDIR/upstream" "$SHA" + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" + + git fetch "file://$WORKDIR/upstream" "$SHA" --depth 1 + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "true" + + git fetch "file://$WORKDIR/upstream" "$SHA" --unshallow + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" +} + +############################################## +# Test git fetch with depth too large +############################################## +function git::fetch_too_large_depth() { + mkdir upstream + pushd upstream >/dev/null + git init -b upstream_branch + + # Some commits + date > file_1 + git add file_1 + git commit -qam 'commit_1' + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA="$(git rev-parse HEAD)" + + popd >/dev/null + + mkdir clone + cd clone + git init -b clone_branch + + git fetch "file://$WORKDIR/upstream" upstream_branch --depth 1000 + git checkout "$SHA" + assert_file_exists file_1 + assert_file_exists file_2 + assert_eq "$(git rev-parse --is-shallow-repository)" "false" +} + +############################################## +# Test git rev-parse with a branch +############################################## +function git::rev_parse_branch() { + mkdir repo + pushd repo >/dev/null + git init -b main + + # A commit on branch 1 + git checkout -b branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + + # A commit on branch 2 + git checkout -b branch_2 + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + + assert_eq "$(git rev-parse branch_1)" "$SHA1" + assert_eq "$(git rev-parse branch_1^{commit})" "$SHA1" + assert_eq "$(git rev-parse branch_2)" "$SHA2" + assert_eq "$(git rev-parse branch_2^{commit})" "$SHA2" +} + +############################################## +# Test git rev-parse with a tag +############################################## +function git::rev_parse_tag() { + mkdir repo + pushd repo >/dev/null + git init -b main + + # A tag on branch 1 (not at HEAD) + git checkout -b branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + git tag tag_1 + + # Another tag on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + git tag tag_2 + + # A tag on branch 2 (not at HEAD) + git checkout -b branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + git tag tag_3 + + # Another tag on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + git tag tag_4 + + assert_eq "$(git rev-parse tag_1)" "$SHA1" + assert_eq "$(git rev-parse tag_1^{commit})" "$SHA1" + assert_eq "$(git rev-parse tag_2)" "$SHA2" + assert_eq "$(git rev-parse tag_2^{commit})" "$SHA2" + assert_eq "$(git rev-parse tag_3)" "$SHA3" + assert_eq "$(git rev-parse tag_3^{commit})" "$SHA3" + assert_eq "$(git rev-parse tag_4)" "$SHA4" + assert_eq "$(git rev-parse tag_4^{commit})" "$SHA4" +} + +############################################## +# Test git rev-parse with an annotated tag +############################################## +function git::rev_parse_tag_annotated() { + mkdir repo + pushd repo >/dev/null + git init -b main + + # A tag on branch 1 (not at HEAD) + git checkout -b branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + git tag -am "anntag_1" anntag_1 + + # Another tag on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + git tag -am "anntag_2" anntag_2 + + # A tag on branch 2 (not at HEAD) + git checkout -b branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + git tag -am "anntag_3" anntag_3 + + # Another tag on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + git tag -am "anntag_4" anntag_4 + + # Annotated tags have their own SHA, which can be found with rev-parse, but + # it doesn't make sense to test rev-parse against itself. + assert_eq "$(git rev-parse anntag_1^{commit})" "$SHA1" + assert_eq "$(git rev-parse anntag_2^{commit})" "$SHA2" + assert_eq "$(git rev-parse anntag_3^{commit})" "$SHA3" + assert_eq "$(git rev-parse anntag_4^{commit})" "$SHA4" +} + +############################################## +# Test git rev-parse with a SHA +############################################## +function git::rev_parse_sha() { + mkdir repo + pushd repo >/dev/null + git init -b main + + # A commit on branch 1 (not at HEAD) + git checkout -b branch_1 + date > file_1 + git add file_1 + git commit -qam 'commit_1' + SHA1="$(git rev-parse HEAD)" + SHORT1="$(echo "$SHA1" | sed 's/........$//')" + + # Another commit on branch 1 (at HEAD) + date > file_2 + git add file_2 + git commit -qam 'commit_2' + SHA2="$(git rev-parse HEAD)" + SHORT2="$(echo "$SHA2" | sed 's/........$//')" + + # A commit on branch 2 (not at HEAD) + git checkout -b branch_2 + date > file_3 + git add file_3 + git commit -qam 'commit_3' + SHA3="$(git rev-parse HEAD)" + SHORT3="$(echo "$SHA3" | sed 's/........$//')" + + # Another commit on branch 2 (at HEAD) + date > file_4 + git add file_4 + git commit -qam 'commit_4' + SHA4="$(git rev-parse HEAD)" + SHORT4="$(echo "$SHA4" | sed 's/........$//')" + + assert_eq "$(git rev-parse "$SHA1")" "$SHA1" + assert_eq "$(git rev-parse "$SHA1^{commit}")" "$SHA1" + assert_eq "$(git rev-parse "$SHORT1")" "$SHA1" + assert_eq "$(git rev-parse "$SHA2")" "$SHA2" + assert_eq "$(git rev-parse "$SHA2^{commit}")" "$SHA2" + assert_eq "$(git rev-parse "$SHORT2")" "$SHA2" + assert_eq "$(git rev-parse "$SHA3")" "$SHA3" + assert_eq "$(git rev-parse "$SHA3^{commit}")" "$SHA3" + assert_eq "$(git rev-parse "$SHORT3")" "$SHA3" + assert_eq "$(git rev-parse "$SHA4")" "$SHA4" + assert_eq "$(git rev-parse "$SHA4^{commit}")" "$SHA4" + assert_eq "$(git rev-parse "$SHORT4")" "$SHA4" +} + +############################################## +# Test git rev-parse with a non-existent ref +############################################## +function git::rev_parse_non_existent_name() { + mkdir repo + pushd repo >/dev/null + git init -b main + + assert_substr "$(git rev-parse does-not-exist 2>&1 || true)" "unknown revision" +} + +############################################## +# Test git rev-parse with a non-existent sha +############################################## +function git::rev_parse_non_existent_sha() { + mkdir repo + pushd repo >/dev/null + git init -b main + + # As long as it tastes like a SHA, rev-parse is happy, but there is no + # commit for it. + assert_eq "$(git rev-parse 0123456789abcdef0123456789abcdef01234567)" "0123456789abcdef0123456789abcdef01234567" + assert_substr "$(git rev-parse 0123456789abcdef0123456789abcdef01234567^{commit} 2>&1 || true)" "unknown revision" + # Less-than-full SHAs do not work. + assert_substr "$(git rev-parse 0123456789abcdef 2>&1 || true)" "unknown revision" + assert_substr "$(git rev-parse 0123456789abcdef^{commit} 2>&1 || true)" "unknown revision" +} + +# +# main +# + +function list_tests() { + ( + shopt -s extdebug + declare -F \ + | cut -f3 -d' ' \ + | grep "^git::" \ + | while read X; do declare -F $X; done \ + | sort -n -k2 \ + | cut -f1 -d' ' \ + | sed 's/^git:://' + ) +} + +# Figure out which, if any, tests to run. +all_tests=($(list_tests)) +tests_to_run=() + +function print_tests() { + echo "available tests:" + for t in "${all_tests[@]}"; do + echo " $t" + done +} + +# Validate and accumulate tests to run if args are specified. +for arg; do + # Use -? to list known tests. + if [[ "${arg}" == "-?" ]]; then + print_tests + exit 0 + fi + if [[ "${arg}" =~ ^- ]]; then + echo "ERROR: unknown flag '${arg}'" + exit 1 + fi + # Make sure each non-flag arg matches at least one test. + nmatches=0 + for t in "${all_tests[@]}"; do + if [[ "${t}" =~ ${arg} ]]; then + nmatches=$((nmatches+1)) + # Don't run tests twice, just keep the first match. + if [[ " ${tests_to_run[*]} " =~ " ${t} " ]]; then + continue + fi + tests_to_run+=("${t}") + continue + fi + done + if [[ ${nmatches} == 0 ]]; then + echo "ERROR: no tests match pattern '${arg}'" + echo + print_tests + exit 1 + fi + tests_to_run+=("${matches[@]}") +done +set -- "${tests_to_run[@]}" + +# If no tests were specified, run them all. +if [[ "$#" == 0 ]]; then + set -- "${all_tests[@]}" +fi + +function finish() { + r=$? + trap "" INT EXIT ERR + if [[ $r != 0 ]]; then + echo + echo "the directory $DIR was not removed as it contains"\ + "log files useful for debugging" + fi + exit $r +} +trap finish INT EXIT ERR + +echo +echo "test root is $DIR" +echo + +# Run a test function and return its error code. This is needed because POSIX +# dictates that `errexit` does not apply inside a function called in an `if` +# context. But if we don't call it with `if`, then it terminates the whole +# test run as soon as one test fails. So this jumps through hoops to let the +# individual test functions run outside of `if` and return a code in a +# variable. +# +# Args: +# $1: the name of a variable to populate with the return code +# $2+: the test function to run and optional args +function run_test() { + retvar=$1 + shift + + declare -g "$retvar" + local restore_opts=$(set +o) + set +o errexit + set +o nounset + set +o pipefail + ( + set -o errexit + set -o nounset + set -o pipefail + "$@" + ) + eval "$retvar=$?" + eval "$restore_opts" +} + +# Override local configs for predictability in this test. +export GIT_CONFIG_GLOBAL=/dev/null +export GIT_CONFIG_SYSTEM=/dev/null + +# TODO: add a flag to run all the tests inside the git-sync container image +# Iterate over the chosen tests and run them. +FAILS=() +FINAL_RET=0 +RUNS="${RUNS:-1}" +for t; do + TEST_RET=0 + RUN=0 + while [[ "${RUN}" < "${RUNS}" ]]; do + clean_workdir + + pushd "$WORKDIR" >/dev/null + + sfx="" + if [[ "${RUNS}" > 1 ]]; then + sfx=" ($((RUN+1))/${RUNS})" + fi + echo -n "testcase ${t}${sfx}: " + + # Set &3 for our own output, let testcases use &2 and &1. + exec 3>&1 + + # See comments on run_test for details. + RUN_RET=0 + LOG="${DIR}/log.$t" + run_test RUN_RET "git::${t}" >"${LOG}.${RUN}" 2>&1 + if [[ "$RUN_RET" == 0 ]]; then + pass + else + TEST_RET=1 + if [[ "$RUN_RET" != 42 ]]; then + echo "FAIL: unknown error" + fi + fi + RUN=$((RUN+1)) + + popd >/dev/null + done + if [[ "$TEST_RET" != 0 ]]; then + FINAL_RET=1 + FAILS+=("$t (log: ${LOG}.*)") + fi +done +if [[ "$FINAL_RET" != 0 ]]; then + echo + echo "the following tests failed:" + for f in "${FAILS[@]}"; do + echo " $f" + done + exit 1 +fi + +# Finally... +echo +if [[ "${CLEANUP:-}" == 0 ]]; then + echo "leaving logs in $DIR" +else + rm -rf "$DIR" +fi +