git » git-arr » commit 957aa42

test: Add a simple performance test

author Alberto Bertogli
2025-11-27 23:34:24 UTC
committer Alberto Bertogli
2025-11-27 23:52:01 UTC
parent bedab61ba418482e886163cbcddf3f31f53678d8

test: Add a simple performance test

This patch adds a performance test, that clones the repo into a
temporary directory, runs git-arr for an initial generation, then does a
test commit, and re-runs git-arr for incremental generation.

The git-arr runs are done with debugging enabled, and optionally
coverage or profiling too.

test/perf_test.conf +10 -0
test/perf_test.sh +128 -0

diff --git a/test/perf_test.conf b/test/perf_test.conf
new file mode 100644
index 0000000..5dc51ac
--- /dev/null
+++ b/test/perf_test.conf
@@ -0,0 +1,10 @@
+[git-arr]
+path = TEMPDIR_PLACEHOLDER/git-arr
+desc = git-arr performance test repository
+tree = yes
+commits_in_summary = 10
+commits_per_page = 50
+max_pages = 250
+generate_patch = yes
+embed_markdown = yes
+embed_images = no
diff --git a/test/perf_test.sh b/test/perf_test.sh
new file mode 100755
index 0000000..46c4aaf
--- /dev/null
+++ b/test/perf_test.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+#
+# Set KEEP_TEMP=1 to preserve the temporary directory after the test.
+# Set COVERAGE=1 to generate coverage reports with python3-coverage.
+# Set PROFILE=1 to generate profiling data with cProfile.
+
+set -e -u
+
+KEEP_TEMP=${KEEP_TEMP:-0}
+COVERAGE=${COVERAGE:-0}
+PROFILE=${PROFILE:-0}
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+# Run git-arr with debug logging, to get timing information.
+export GIT_ARR_DEBUG=1
+
+# Check for mutually exclusive options.
+if [ "$PROFILE" = 1 ] && [ "$COVERAGE" = 1 ]; then
+    echo "Error: PROFILE and COVERAGE cannot both be enabled" >&2
+    exit 1
+fi
+
+# Set up profiling or coverage if requested.
+if [ "$PROFILE" = 1 ]; then
+    INITIAL_PROF="$SCRIPT_DIR/.logs/initial_generation.prof"
+    INCREMENTAL_PROF="$SCRIPT_DIR/.logs/incremental_generation.prof"
+    RUNNER_INITIAL="python3 -m cProfile -o $INITIAL_PROF"
+    RUNNER_INCREMENTAL="python3 -m cProfile -o $INCREMENTAL_PROF"
+elif [ "$COVERAGE" = 1 ]; then
+    export COVERAGE_FILE="$SCRIPT_DIR/.logs/coverage"
+    RUNNER_INITIAL="python3-coverage run -a --source=."
+    RUNNER_INCREMENTAL="$RUNNER_INITIAL"
+else
+    RUNNER_INITIAL=""
+    RUNNER_INCREMENTAL=""
+fi
+
+# Create temporary directory.
+TEMP_DIR=$(mktemp -d -t git-arr-perf.XXXXXX)
+
+echo "Temporary directory: $TEMP_DIR"
+
+# Paths.
+TMP_REPO="$TEMP_DIR/git-arr"
+OUTPUT_DIR="$TEMP_DIR/output"
+CONFIG_FILE="$TEMP_DIR/test.conf"
+INITIAL_LOG="$SCRIPT_DIR/.logs/initial_generation.log"
+INITIAL_DEBUG_LOG="$SCRIPT_DIR/.logs/initial_generation_debug.log"
+INCREMENTAL_LOG="$SCRIPT_DIR/.logs/incremental_generation.log"
+INCREMENTAL_DEBUG_LOG="$SCRIPT_DIR/.logs/incremental_generation_debug.log"
+
+echo "Cloning git-arr repository..."
+git clone -q "$REPO_ROOT" "$TMP_REPO"
+
+# Generate config file.
+sed "s|TEMPDIR_PLACEHOLDER|$TEMP_DIR|g" "$SCRIPT_DIR/perf_test.conf" > "$CONFIG_FILE"
+
+# Create output directory, and logs directory.
+mkdir -p "$OUTPUT_DIR" "$SCRIPT_DIR/.logs/"
+
+cd "$REPO_ROOT"
+
+
+# Run initial generation.
+echo "Initial generation"
+$RUNNER_INITIAL ./git-arr --config "$CONFIG_FILE" generate --output "$OUTPUT_DIR" \
+	> "$INITIAL_LOG" \
+	2> "$INITIAL_DEBUG_LOG" \
+	|| cat "$INITIAL_LOG"
+
+# Modify files in the cloned repo.
+echo "# Performance test run: $(date)" >> "$TMP_REPO/README.md"
+echo '# Modified for performance testing' >> "$TMP_REPO/git-arr"
+echo '# Modified for performance testing' >> "$TMP_REPO/utils.py"
+
+# Commit changes.
+cd "$TMP_REPO"
+git add README.md git-arr utils.py
+git commit -q -m "Performance test: modified files"
+echo "Test commit: $(git rev-parse --short HEAD)"
+cd "$REPO_ROOT"
+
+# Run incremental generation.
+echo "Incremental generation"
+$RUNNER_INCREMENTAL ./git-arr --config "$CONFIG_FILE" generate --output "$OUTPUT_DIR" \
+	> "$INCREMENTAL_LOG" \
+	2> "$INCREMENTAL_DEBUG_LOG" \
+	|| cat "$INCREMENTAL_LOG"
+
+echo "Logs saved to $SCRIPT_DIR/.logs/"
+
+if [ "$COVERAGE" = 1 ]; then
+    echo
+    echo "Coverage report:"
+    python3-coverage report
+    python3-coverage html -q -d "$SCRIPT_DIR/.logs/htmlcov"
+    echo
+    echo "HTML coverage report: file://$SCRIPT_DIR/.logs/htmlcov/index.html"
+fi
+
+
+pyprof_summary() {
+	python3 -c "import pstats; \
+		p = pstats.Stats('$1'); \
+		p.sort_stats('cumulative'); \
+		p.print_stats(20)"
+}
+
+if [ "$PROFILE" = 1 ]; then
+    echo
+    echo "Profile report (initial generation):"
+    pyprof_summary "$INITIAL_PROF"
+
+    echo
+    echo "Profile report (incremental generation):"
+    pyprof_summary "$INCREMENTAL_PROF"
+
+    echo
+    echo "Profile data saved:"
+    echo "  Initial:      $INITIAL_PROF"
+    echo "  Incremental:  $INCREMENTAL_PROF"
+fi
+
+if [ "$KEEP_TEMP" = 0 ]; then
+    echo "Cleaning up temporary directory"
+    rm -rf "$TEMP_DIR"
+fi