New integration test for gitception
authorCathy J. Fitzpatrick <cathy@cathyjf.com>
Sat, 16 Dec 2023 12:20:53 +0000 (04:20 -0800)
committerSean Whitton <spwhitton@spwhitton.name>
Sun, 29 Dec 2024 10:04:24 +0000 (10:04 +0000)
Signed-off-by: Cathy J. Fitzpatrick <cathy@cathyjf.com>
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
tests/system-test.sh [new file with mode: 0755]

diff --git a/tests/system-test.sh b/tests/system-test.sh
new file mode 100755 (executable)
index 0000000..74de3a5
--- /dev/null
@@ -0,0 +1,223 @@
+#!/usr/bin/env bash
+# SPDX-FileCopyrightText: Copyright 2023 Cathy J. Fitzpatrick <cathy@cathyjf.com>
+# SPDX-License-Identifier: GPL-2.0-or-later
+set -efuC -o pipefail
+shopt -s inherit_errexit
+
+# Unlike the main git-remote-gcrypt program, this testing script requires bash
+# (rather than POSIX sh) and also depends on various common system utilities
+# that the git-remote-gcrypt carefully avoids using (such as mktemp(1)).
+#
+# The test proceeds by setting up a new repository, making some large commits
+# with random data into the repository, pushing the repository to another
+# remote using git-remote-gcrypt over the gitception protocol, and then cloning
+# the second repository and ensuring that the data it contains is correct.
+#
+# The random data is obtained from /dev/urandom. This script won't work
+# on systems that don't provide /dev/urandom.
+#
+# The following settings specify the parameters to be used for the test.
+num_commits=5
+files_per_commit=3
+random_source="/dev/urandom"
+random_data_per_file=5242880 # 5 MiB
+default_branch="main"
+test_user_name="git-remote-gcrypt"
+test_user_email="git-remote-gcrypt@example.com"
+pack_size_limit="12m" # If this variable is unset, there is no size limit.
+
+readonly num_commits files_per_commit random_source random_data_per_file \
+    default_branch test_user_name test_user_email pack_size_limit
+
+# Pipe text into this function to indent it with four spaces. This is used
+# to make the output of this script prettier.
+indent() {
+    sed 's/^\(.*\)$/    \1/'
+}
+
+section_break() {
+    echo
+    printf '*%.0s' {1..70}
+    echo $'\n'
+}
+
+assert() {
+    (set +e; [[ -n ${show_command:-} ]] && set -x; "${@}")
+    local -r status=${?}
+    { [[ ${status} -eq 0 ]] && echo "Verification succeeded."; } || \
+        echo "Verification failed."
+    return "${status}"
+}
+
+fastfail() {
+    "$@" || kill -- "-$$"
+}
+
+umask 077
+tempdir=$(mktemp -d)
+readonly tempdir
+# shellcheck disable=SC2064
+trap "rm -Rf -- '${tempdir}'" EXIT
+
+# Set up the PATH to favor the version of git-remote-gcrypt from the repository
+# rather than a version that might already be installed on the user's system.
+PATH=$(git rev-parse --show-toplevel):${PATH}
+readonly PATH
+export PATH
+
+# Unset any GIT_ environment variables to prevent them from affecting the test.
+git_env=$(env | sed -n 's/^\(GIT_[^=]*\)=.*$/\1/p')
+# shellcheck disable=SC2086
+IFS=$'\n' unset ${git_env}
+
+# Ensure a predictable gpg configuration.
+export GNUPGHOME="${tempdir}/gpg"
+mkdir "${GNUPGHOME}"
+# Use a wrapper for gpg(1) to avoid cluttering the test output with unnecessary
+# warnings about the obsolete `--secret-keyring` option. These warnings are
+# caused by git-remote-gcrypt passing an option to gpg(1) that only makes sense
+# for ancient versions of gpg(1), but addressing that (if it should be
+# addressed at all) is a task best left for another day.
+cat << 'EOF' > "${GNUPGHOME}/gpg"
+#!/usr/bin/env bash
+set -efuC -o pipefail; shopt -s inherit_errexit
+args=( "${@}" )
+for ((i = 0; i < ${#}; ++i)); do
+    if [[ ${args[${i}]} = "--secret-keyring" ]]; then
+        unset "args[${i}]" "args[$(( i + 1 ))]"
+        break
+    fi
+done
+exec gpg "${args[@]}"
+EOF
+chmod +x "${GNUPGHOME}/gpg"
+
+# Ensure a predictable git configuration.
+export GIT_CONFIG_SYSTEM=/dev/null
+export GIT_CONFIG_GLOBAL="${tempdir}/gitconfig"
+mkdir "${tempdir}/template" # Intentionally empty template directory.
+git config --global init.defaultBranch "${default_branch}"
+git config --global user.name "${test_user_name}"
+git config --global user.email "${test_user_email}"
+git config --global init.templateDir "${tempdir}/template"
+git config --global gpg.program "${GNUPGHOME}/gpg"
+[[ -n ${pack_size_limit:-} ]] && \
+    git config --global pack.packSizeLimit "${pack_size_limit}"
+
+# Prepare the random data that we'll be writing to the repository.
+total_files=$(( num_commits * files_per_commit ))
+random_data_size=$(( total_files * random_data_per_file ))
+random_data_file="${tempdir}/data"
+head -c "${random_data_size}" "${random_source}" > "${random_data_file}"
+
+# Create gpg key and subkey.
+echo "Step 1: Creating a new GPG key and subkey to use for testing:"
+(
+    set -x
+    gpg --batch --passphrase "" --quick-generate-key \
+        "${test_user_name} <${test_user_email}>"
+    gpg -K
+) 2>&1 | indent
+
+###
+section_break
+
+echo "Step 2: Creating new repository with random data:"
+{
+    git init -- "${tempdir}/first"
+    cd "${tempdir}/first"
+    for ((i = 0; i < num_commits; ++i)); do
+        for ((j = 0; j < files_per_commit; ++j)); do
+            file_index=$(( i * files_per_commit + j ))
+            random_data_index=$(( file_index * random_data_per_file ))
+            # shellcheck disable=SC2016
+            echo "Writing random file $((file_index + 1))/${total_files}:" \
+                '${tempdir}'/"first/$(( file_index )).data "
+            head -c "${random_data_per_file}" > "$(( file_index )).data" < \
+                <(tail -c "+${random_data_index}" "${random_data_file}" || :)
+            if command -v base64 > /dev/null; then
+                # shellcheck disable=SC2312
+                echo "First 24 bytes in base64:" \
+                    "$(fastfail head -c 24 "$(( file_index )).data" | \
+                        fastfail base64)" | indent
+            fi
+        done
+        git add -- "${tempdir}/first"
+        git commit -m "Commit #${i}"
+    done
+
+    echo
+    echo "For reference, here is the commit log for the repository:"
+    git log --format=oneline | indent
+} | indent
+
+###
+section_break
+
+echo "Step 3: Creating an empty bare repository to receive pushed data:"
+git init --bare -- "${tempdir}/second.git" | indent
+
+
+###
+section_break
+
+echo "Step 4: Pushing the first repository to the second one using gitception:"
+{
+    # Note that when pushing to a bare local repository, git-remote-gcrypt uses
+    # gitception, rather than treating the remote as a local repository.
+    (
+        set -x
+        cd "${tempdir}/first"
+        git push -f "gcrypt::${tempdir}/second.git#${default_branch}" \
+            "${default_branch}"
+    ) 2>&1
+
+    if command -v tree > /dev/null; then
+        echo
+        echo "For reference, here is the directory tree of second.git:"
+        tree "${tempdir}/second.git"
+    fi
+
+    echo
+    echo "Here is the size of each object file in second.git:"
+    (
+        cd "${tempdir}/second.git/objects"
+        find . -type f -exec du -sh {} +
+    ) | indent
+
+    echo
+    echo "Note that git-pack-objects(1) will try to ensure that each object is"
+    echo "smaller than pack.packSizeLimit (${pack_size_limit:-unlimited}" \
+        "here) but this isn't always"
+    echo "possible because each object contains at least one of our random"
+    echo "files, and each random file has a certain minimum size. As a result,"
+    echo "pack.packSizeLimit is more of a suggestion than a hard limit."
+ } | indent
+
+###
+section_break
+
+echo "Step 5: Cloning the second repository using gitception:"
+{
+    (
+        set -x
+        git clone -b "${default_branch}" \
+            "gcrypt::${tempdir}/second.git#${default_branch}" -- \
+                "${tempdir}/third"
+    ) 2>&1
+
+    echo
+    echo "Verifying that the first and third repositories have the same"
+    echo "commit log as each other:"
+    # shellcheck disable=SC2312
+    assert diff \
+        <(fastfail cd "${tempdir}/first"; fastfail git log --oneline) \
+        <(fastfail cd "${tempdir}/third"; fastfail git log --oneline) \
+            2>&1 | indent
+
+    echo
+    echo "Verifying that the first and third repositories have the same"
+    echo "files in their respective working directories:"
+    show_command=1 assert diff -r --exclude ".git" -- \
+        "${tempdir}/first" "${tempdir}/third" 2>&1 | indent
+} | indent