summary refs log tree commit diff
path: root/www/git.causal.agency/cgit/tests
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-12-27 18:45:04 -0500
committerJune McEnroe <june@causal.agency>2020-12-27 18:45:35 -0500
commit7b715a386a625ea3cf92b1281a4392e2473b830a (patch)
tree9353eea94b35e91f229be03061835b8fb788570a /www/git.causal.agency/cgit/tests
parentRemove 1sh (diff)
parentSquashed 'www/git.causal.agency/cgit/' content from commit 02221fd3 (diff)
downloadsrc-7b715a386a625ea3cf92b1281a4392e2473b830a.tar.gz
src-7b715a386a625ea3cf92b1281a4392e2473b830a.zip
Merge commit '85016e706cd00e527dba3fa83b2783dfb56a4ffa' as 'www/git.causal.agency/cgit'
From tag 'v1.2.3'.
Diffstat (limited to 'www/git.causal.agency/cgit/tests')
-rw-r--r--www/git.causal.agency/cgit/tests/.gitignore2
-rw-r--r--www/git.causal.agency/cgit/tests/Makefile17
-rw-r--r--www/git.causal.agency/cgit/tests/filters/dump.lua17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/filters/dump.sh4
-rwxr-xr-xwww/git.causal.agency/cgit/tests/setup.sh176
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh45
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0010-validate-html.sh40
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0020-validate-cache.sh78
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0101-index.sh17
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0102-summary.sh25
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0103-log.sh24
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0104-tree.sh32
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0105-commit.sh36
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0106-diff.sh19
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0107-snapshot.sh208
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0108-patch.sh62
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0109-gitconfig.sh48
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0110-rawdiff.sh42
-rwxr-xr-xwww/git.causal.agency/cgit/tests/t0111-filter.sh46
-rwxr-xr-xwww/git.causal.agency/cgit/tests/valgrind/bin/cgit12
20 files changed, 950 insertions, 0 deletions
diff --git a/www/git.causal.agency/cgit/tests/.gitignore b/www/git.causal.agency/cgit/tests/.gitignore
new file mode 100644
index 00000000..3fd2e965
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/.gitignore
@@ -0,0 +1,2 @@
+trash\ directory.t*
+test-results
diff --git a/www/git.causal.agency/cgit/tests/Makefile b/www/git.causal.agency/cgit/tests/Makefile
new file mode 100644
index 00000000..65e11173
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/Makefile
@@ -0,0 +1,17 @@
+include ../git/config.mak.uname
+-include ../cgit.conf
+
+SHELL_PATH ?= $(SHELL)
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+all: $(T)
+
+$(T):
+	@'$(SHELL_PATH_SQ)' $@ $(CGIT_TEST_OPTS)
+
+clean:
+	$(RM) -rf trash
+
+.PHONY: $(T) clean
diff --git a/www/git.causal.agency/cgit/tests/filters/dump.lua b/www/git.causal.agency/cgit/tests/filters/dump.lua
new file mode 100644
index 00000000..1f15c931
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/filters/dump.lua
@@ -0,0 +1,17 @@
+function filter_open(...)
+	buffer = ""
+	for i = 1, select("#", ...) do
+		buffer = buffer .. select(i, ...) .. " "
+	end
+end
+
+function filter_close()
+	html(buffer)
+	return 0
+end
+
+function filter_write(str)
+	buffer = buffer .. string.upper(str)
+end
+
+
diff --git a/www/git.causal.agency/cgit/tests/filters/dump.sh b/www/git.causal.agency/cgit/tests/filters/dump.sh
new file mode 100755
index 00000000..da6f7a1b
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/filters/dump.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+[ "$#" -gt 0 ] && printf "%s " "$*"
+tr '[:lower:]' '[:upper:]'
diff --git a/www/git.causal.agency/cgit/tests/setup.sh b/www/git.causal.agency/cgit/tests/setup.sh
new file mode 100755
index 00000000..5879348e
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/setup.sh
@@ -0,0 +1,176 @@
+# This file should be sourced by all test-scripts
+#
+# Main functions:
+#   prepare_tests(description) - setup for testing, i.e. create repos+config
+#   run_test(description, script) - run one test, i.e. eval script
+#
+# Helper functions
+#   cgit_query(querystring) - call cgit with the specified querystring
+#   cgit_url(url) - call cgit with the specified virtual url
+#
+# Example script:
+#
+# . setup.sh
+# prepare_tests "html validation"
+# run_test 'repo index' 'cgit_url "/" | tidy -e'
+# run_test 'repo summary' 'cgit_url "/foo" | tidy -e'
+
+# We don't want to run Git commands through Valgrind, so we filter out the
+# --valgrind option here and handle it ourselves.  We copy the arguments
+# assuming that none contain a newline, although other whitespace is
+# preserved.
+LF='
+'
+test_argv=
+
+while test $# != 0
+do
+	case "$1" in
+	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+		cgit_valgrind=t
+		test_argv="$test_argv${LF}--verbose"
+		;;
+	*)
+		test_argv="$test_argv$LF$1"
+		;;
+	esac
+	shift
+done
+
+OLDIFS=$IFS
+IFS=$LF
+set -- $test_argv
+IFS=$OLDIFS
+
+: ${TEST_DIRECTORY=$(pwd)/../git/t}
+: ${TEST_OUTPUT_DIRECTORY=$(pwd)}
+TEST_NO_CREATE_REPO=YesPlease
+. "$TEST_DIRECTORY"/test-lib.sh
+
+# Prepend the directory containing cgit to PATH.
+if test -n "$cgit_valgrind"
+then
+	GIT_VALGRIND="$TEST_DIRECTORY/valgrind"
+	CGIT_VALGRIND=$(cd ../valgrind && pwd)
+	PATH="$CGIT_VALGRIND/bin:$PATH"
+	export GIT_VALGRIND CGIT_VALGRIND
+else
+	PATH="$(pwd)/../..:$PATH"
+fi
+
+FILTER_DIRECTORY=$(cd ../filters && pwd)
+
+if cgit --version | grep -F -q "[+] Lua scripting"; then
+	export CGIT_HAS_LUA=1
+else
+	export CGIT_HAS_LUA=0
+fi
+
+mkrepo() {
+	name=$1
+	count=$2
+	test_create_repo "$name"
+	(
+		cd "$name"
+		n=1
+		while test $n -le $count
+		do
+			echo $n >file-$n
+			git add file-$n
+			git commit -m "commit $n"
+			n=$(expr $n + 1)
+		done
+		if test "$3" = "testplus"
+		then
+			echo "hello" >a+b
+			git add a+b
+			git commit -m "add a+b"
+			git branch "1+2"
+		fi
+	)
+}
+
+setup_repos()
+{
+	rm -rf cache
+	mkdir -p cache
+	mkrepo repos/foo 5 >/dev/null
+	mkrepo repos/bar 50 >/dev/null
+	mkrepo repos/foo+bar 10 testplus >/dev/null
+	mkrepo "repos/with space" 2 >/dev/null
+	mkrepo repos/filter 5 testplus >/dev/null
+	cat >cgitrc <<EOF
+virtual-root=/
+cache-root=$PWD/cache
+
+cache-size=1021
+snapshots=tar.gz tar.bz tar.lz tar.xz tar.zst zip
+enable-log-filecount=1
+enable-log-linecount=1
+summary-log=5
+summary-branches=5
+summary-tags=5
+clone-url=git://example.org/\$CGIT_REPO_URL.git
+enable-filter-overrides=1
+
+repo.url=foo
+repo.path=$PWD/repos/foo/.git
+# Do not specify a description for this repo, as it then will be assigned
+# the constant value "[no description]" (which actually used to cause a
+# segfault).
+
+repo.url=bar
+repo.path=$PWD/repos/bar/.git
+repo.desc=the bar repo
+
+repo.url=foo+bar
+repo.path=$PWD/repos/foo+bar/.git
+repo.desc=the foo+bar repo
+
+repo.url=with space
+repo.path=$PWD/repos/with space/.git
+repo.desc=spaced repo
+
+repo.url=filter-exec
+repo.path=$PWD/repos/filter/.git
+repo.desc=filtered repo
+repo.about-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.commit-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh
+repo.readme=master:a+b
+EOF
+
+	if [ $CGIT_HAS_LUA -eq 1 ]; then
+		cat >>cgitrc <<EOF
+repo.url=filter-lua
+repo.path=$PWD/repos/filter/.git
+repo.desc=filtered repo
+repo.about-filter=lua:$FILTER_DIRECTORY/dump.lua
+repo.commit-filter=lua:$FILTER_DIRECTORY/dump.lua
+repo.email-filter=lua:$FILTER_DIRECTORY/dump.lua
+repo.source-filter=lua:$FILTER_DIRECTORY/dump.lua
+repo.readme=master:a+b
+EOF
+	fi
+}
+
+cgit_query()
+{
+	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="$1" cgit
+}
+
+cgit_url()
+{
+	CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="url=$1" cgit
+}
+
+strip_headers() {
+	while read -r line
+	do
+		test -z "$line" && break
+	done
+	cat
+}
+
+test -z "$CGIT_TEST_NO_CREATE_REPOS" && setup_repos
diff --git a/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
new file mode 100755
index 00000000..73bd32f5
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0001-validate-git-versions.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+if [ "${CGIT_TEST_NO_GIT_VERSION}" = "YesPlease" ]; then
+	exit 0
+fi
+
+test_description='Check Git version is correct'
+CGIT_TEST_NO_CREATE_REPOS=YesPlease
+. ./setup.sh
+
+test_expect_success 'extract Git version from Makefile' '
+	sed -n -e "/^GIT_VER[ 	]*=/ {
+		s/^GIT_VER[ 	]*=[ 	]*//
+		p
+	}" ../../Makefile >makefile_version
+'
+
+# Note that Git's GIT-VERSION-GEN script applies "s/-/./g" to the version
+# string to produce the internal version in the GIT-VERSION-FILE, so we
+# must apply the same transformation to the version in the Makefile before
+# comparing them.
+test_expect_success 'test Git version matches Makefile' '
+	( cat ../../git/GIT-VERSION-FILE || echo "No GIT-VERSION-FILE" ) |
+	sed -e "s/GIT_VERSION[ 	]*=[ 	]*//" -e "s/\\.dirty$//" >git_version &&
+	sed -e "s/-/./g" makefile_version >makefile_git_version &&
+	test_cmp git_version makefile_git_version
+'
+
+test_expect_success 'test submodule version matches Makefile' '
+	if ! test -e ../../git/.git
+	then
+		echo "git/ is not a Git repository" >&2
+	else
+		(
+			cd ../.. &&
+			sm_sha1=$(git ls-files --stage -- git |
+				sed -e "s/^[0-9]* \\([0-9a-f]*\\) [0-9]	.*$/\\1/") &&
+			cd git &&
+			git describe --match "v[0-9]*" $sm_sha1
+		) | sed -e "s/^v//" -e "s/-/./" >sm_version &&
+		test_cmp sm_version makefile_version
+	fi
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0010-validate-html.sh b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
new file mode 100755
index 00000000..ca08d69d
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0010-validate-html.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='Validate html with tidy'
+. ./setup.sh
+
+
+test_url()
+{
+	tidy_opt="-eq"
+	test -z "$NO_TIDY_WARNINGS" || tidy_opt+=" --show-warnings no"
+	cgit_url "$1" >tidy-$test_count.tmp || return
+	sed -e "1,4d" tidy-$test_count.tmp >tidy-$test_count || return
+	"$tidy" $tidy_opt tidy-$test_count
+	rc=$?
+
+	# tidy returns with exitcode 1 on warnings, 2 on error
+	if test $rc = 2
+	then
+		false
+	else
+		:
+	fi
+}
+
+tidy=`which tidy 2>/dev/null`
+test -n "$tidy" || {
+	skip_all='Skipping html validation tests: tidy not found'
+	test_done
+	exit
+}
+
+test_expect_success 'index page' 'test_url ""'
+test_expect_success 'foo' 'test_url "foo"'
+test_expect_success 'foo/log' 'test_url "foo/log"'
+test_expect_success 'foo/tree' 'test_url "foo/tree"'
+test_expect_success 'foo/tree/file-1' 'test_url "foo/tree/file-1"'
+test_expect_success 'foo/commit' 'test_url "foo/commit"'
+test_expect_success 'foo/diff' 'test_url "foo/diff"'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
new file mode 100755
index 00000000..657765d8
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0020-validate-cache.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='Validate cache'
+. ./setup.sh
+
+test_expect_success 'verify cache-size=0' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=1021$/cache-size=0/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 0 output
+'
+
+test_expect_success 'verify cache-size=1' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=0$/cache-size=1/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 1 output
+'
+
+test_expect_success 'verify cache-size=1021' '
+
+	rm -f cache/* &&
+	sed -e "s/cache-size=1$/cache-size=1021/" cgitrc >cgitrc.tmp &&
+	mv -f cgitrc.tmp cgitrc &&
+	cgit_url "" &&
+	cgit_url "foo" &&
+	cgit_url "foo/refs" &&
+	cgit_url "foo/tree" &&
+	cgit_url "foo/log" &&
+	cgit_url "foo/diff" &&
+	cgit_url "foo/patch" &&
+	cgit_url "bar" &&
+	cgit_url "bar/refs" &&
+	cgit_url "bar/tree" &&
+	cgit_url "bar/log" &&
+	cgit_url "bar/diff" &&
+	cgit_url "bar/patch" &&
+	ls cache >output &&
+	test_line_count = 13 output &&
+	cgit_url "foo/ls_cache" >output.full &&
+	strip_headers <output.full >output &&
+	test_line_count = 13 output &&
+	# Check that ls_cache output is cached correctly
+	cgit_url "foo/ls_cache" >output.second &&
+	test_cmp output.full output.second
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0101-index.sh b/www/git.causal.agency/cgit/tests/t0101-index.sh
new file mode 100755
index 00000000..82ef9b04
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0101-index.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description='Check content on index page'
+. ./setup.sh
+
+test_expect_success 'generate index page' 'cgit_url "" >tmp'
+test_expect_success 'find foo repo' 'grep "foo" tmp'
+test_expect_success 'find foo description' 'grep "\[no description\]" tmp'
+test_expect_success 'find bar repo' 'grep "bar" tmp'
+test_expect_success 'find bar description' 'grep "the bar repo" tmp'
+test_expect_success 'find foo+bar repo' 'grep ">foo+bar<" tmp'
+test_expect_success 'verify foo+bar link' 'grep "/foo+bar/" tmp'
+test_expect_success 'verify "with%20space" link' 'grep "/with%20space/" tmp'
+test_expect_success 'no tree-link' '! grep "foo/tree" tmp'
+test_expect_success 'no log-link' '! grep "foo/log" tmp'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0102-summary.sh b/www/git.causal.agency/cgit/tests/t0102-summary.sh
new file mode 100755
index 00000000..b8864cb1
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0102-summary.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='Check content on summary page'
+. ./setup.sh
+
+test_expect_success 'generate foo summary' 'cgit_url "foo" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 5' 'grep "commit 5" tmp'
+test_expect_success 'find branch master' 'grep "master" tmp'
+test_expect_success 'no tags' '! grep "tags" tmp'
+test_expect_success 'clone-url expanded correctly' '
+	grep "git://example.org/foo.git" tmp
+'
+
+test_expect_success 'generate bar summary' 'cgit_url "bar" >tmp'
+test_expect_success 'no commit 45' '! grep "commit 45" tmp'
+test_expect_success 'find commit 46' 'grep "commit 46" tmp'
+test_expect_success 'find commit 50' 'grep "commit 50" tmp'
+test_expect_success 'find branch master' 'grep "master" tmp'
+test_expect_success 'no tags' '! grep "tags" tmp'
+test_expect_success 'clone-url expanded correctly' '
+	grep "git://example.org/bar.git" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0103-log.sh b/www/git.causal.agency/cgit/tests/t0103-log.sh
new file mode 100755
index 00000000..bdf1435a
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0103-log.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='Check content on log page'
+. ./setup.sh
+
+test_expect_success 'generate foo/log' 'cgit_url "foo/log" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 5' 'grep "commit 5" tmp'
+
+test_expect_success 'generate bar/log' 'cgit_url "bar/log" >tmp'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find commit 50' 'grep "commit 50" tmp'
+
+test_expect_success 'generate "with%20space/log?qt=grep&q=commit+1"' '
+	cgit_url "with+space/log&qt=grep&q=commit+1" >tmp
+'
+test_expect_success 'find commit 1' 'grep "commit 1" tmp'
+test_expect_success 'find link with %20 in path' 'grep "/with%20space/log/?qt=grep" tmp'
+test_expect_success 'find link with + in arg' 'grep "/log/?qt=grep&amp;q=commit+1" tmp'
+test_expect_success 'no links with space in path' '! grep "href=./with space/" tmp'
+test_expect_success 'no links with space in arg' '! grep "q=commit 1" tmp'
+test_expect_success 'commit 2 is not visible' '! grep "commit 2" tmp'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0104-tree.sh b/www/git.causal.agency/cgit/tests/t0104-tree.sh
new file mode 100755
index 00000000..2e140f59
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0104-tree.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='Check content on tree page'
+. ./setup.sh
+
+test_expect_success 'generate bar/tree' 'cgit_url "bar/tree" >tmp'
+test_expect_success 'find file-1' 'grep "file-1" tmp'
+test_expect_success 'find file-50' 'grep "file-50" tmp'
+
+test_expect_success 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >tmp'
+
+test_expect_success 'find line 1' '
+	grep "<a id=.n1. href=.#n1.>1</a>" tmp
+'
+
+test_expect_success 'no line 2' '
+	! grep "<a id=.n2. href=.#n2.>2</a>" tmp
+'
+
+test_expect_success 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >tmp'
+
+test_expect_success 'verify a+b link' '
+	grep "/foo+bar/tree/a+b" tmp
+'
+
+test_expect_success 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >tmp'
+
+test_expect_success 'verify a+b?h=1+2 link' '
+	grep "/foo+bar/tree/a+b?h=1%2b2" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0105-commit.sh b/www/git.causal.agency/cgit/tests/t0105-commit.sh
new file mode 100755
index 00000000..9cdf55c0
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0105-commit.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='Check content on commit page'
+. ./setup.sh
+
+test_expect_success 'generate foo/commit' 'cgit_url "foo/commit" >tmp'
+test_expect_success 'find tree link' 'grep "<a href=./foo/tree/.>" tmp'
+test_expect_success 'find parent link' 'grep -E "<a href=./foo/commit/\?id=.+>" tmp'
+
+test_expect_success 'find commit subject' '
+	grep "<div class=.commit-subject.>commit 5<" tmp
+'
+
+test_expect_success 'find commit msg' 'grep "<div class=.commit-msg.></div>" tmp'
+test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp'
+
+test_expect_success 'find diff summary' '
+	grep "1 files changed, 1 insertions, 0 deletions" tmp
+'
+
+test_expect_success 'get root commit' '
+	root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) &&
+	cgit_url "foo/commit&id=$root" >tmp &&
+	grep "</html>" tmp
+'
+
+test_expect_success 'root commit contains diffstat' '
+	grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40\}.>file-1</a>" tmp
+'
+
+test_expect_success 'root commit contains diff' '
+	grep ">diff --git a/file-1 b/file-1<" tmp &&
+	grep "<div class=.add.>+1</div>" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0106-diff.sh b/www/git.causal.agency/cgit/tests/t0106-diff.sh
new file mode 100755
index 00000000..82b645ec
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0106-diff.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='Check content on diff page'
+. ./setup.sh
+
+test_expect_success 'generate foo/diff' 'cgit_url "foo/diff" >tmp'
+test_expect_success 'find diff header' 'grep "a/file-5 b/file-5" tmp'
+test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp'
+test_expect_success 'find added file' 'grep "new file mode 100644" tmp'
+
+test_expect_success 'find hunk header' '
+	grep "<div class=.hunk.>@@ -0,0 +1 @@</div>" tmp
+'
+
+test_expect_success 'find added line' '
+	grep "<div class=.add.>+5</div>" tmp
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0107-snapshot.sh b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
new file mode 100755
index 00000000..c164d3e2
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0107-snapshot.sh
@@ -0,0 +1,208 @@
+#!/bin/sh
+
+test_description='Verify snapshot'
+. ./setup.sh
+
+test_expect_success 'get foo/snapshot/master.tar.gz' '
+	cgit_url "foo/snapshot/master.tar.gz" >tmp
+'
+
+test_expect_success 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-gzip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.gz."
+'
+
+test_expect_success 'strip off the header lines' '
+	strip_headers <tmp >master.tar.gz
+'
+
+test_expect_success 'verify gzip format' '
+	gunzip --test master.tar.gz
+'
+
+test_expect_success 'untar' '
+	rm -rf master &&
+	tar -xzf master.tar.gz
+'
+
+test_expect_success 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which lzip 2>/dev/null)"; then
+	test_set_prereq LZIP
+else
+	say 'Skipping LZIP validation tests: lzip not found'
+fi
+
+test_expect_success LZIP 'get foo/snapshot/master.tar.lz' '
+	cgit_url "foo/snapshot/master.tar.lz" >tmp
+'
+
+test_expect_success LZIP 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-lzip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.lz."
+'
+
+test_expect_success LZIP 'strip off the header lines' '
+	strip_headers <tmp >master.tar.lz
+'
+
+test_expect_success LZIP 'verify lzip format' '
+	lzip --test master.tar.lz &&
+	cp master.tar.lz /tmp/.
+'
+
+test_expect_success LZIP 'untar' '
+	rm -rf master &&
+	tar --lzip -xf master.tar.lz
+'
+
+test_expect_success LZIP 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success LZIP 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which xz 2>/dev/null)"; then
+	test_set_prereq XZ
+else
+	say 'Skipping XZ validation tests: xz not found'
+fi
+
+test_expect_success XZ 'get foo/snapshot/master.tar.xz' '
+	cgit_url "foo/snapshot/master.tar.xz" >tmp
+'
+
+test_expect_success XZ 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-xz" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.xz."
+'
+
+test_expect_success XZ 'strip off the header lines' '
+	strip_headers <tmp >master.tar.xz
+'
+
+test_expect_success XZ 'verify xz format' '
+	xz --test master.tar.xz &&
+	cp master.tar.xz /tmp/.
+'
+
+test_expect_success XZ 'untar' '
+	rm -rf master &&
+	tar --xz -xf master.tar.xz
+'
+
+test_expect_success XZ 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success XZ 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+if test -n "$(which zstd 2>/dev/null)"; then
+	test_set_prereq ZSTD
+else
+	say 'Skipping ZSTD validation tests: zstd not found'
+fi
+
+test_expect_success ZSTD 'get foo/snapshot/master.tar.zst' '
+	cgit_url "foo/snapshot/master.tar.zst" >tmp
+'
+
+test_expect_success ZSTD 'check html headers' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-zstd" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.tar.zst."
+'
+
+test_expect_success ZSTD 'strip off the header lines' '
+	strip_headers <tmp >master.tar.zst
+'
+
+test_expect_success ZSTD 'verify zstd format' '
+	zstd --test master.tar.zst &&
+	cp master.tar.zst /tmp/.
+'
+
+test_expect_success ZSTD 'untar' '
+	rm -rf master &&
+	tar --zstd -xf master.tar.zst
+'
+
+test_expect_success ZSTD 'count files' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success ZSTD 'verify untarred file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+test_expect_success 'get foo/snapshot/master.zip' '
+	cgit_url "foo/snapshot/master.zip" >tmp
+'
+
+test_expect_success 'check HTML headers (zip)' '
+	head -n 1 tmp |
+	grep "Content-Type: application/x-zip" &&
+
+	head -n 2 tmp |
+	grep "Content-Disposition: inline; filename=.master.zip."
+'
+
+test_expect_success 'strip off the header lines (zip)' '
+	strip_headers <tmp >master.zip
+'
+
+if test -n "$(which unzip 2>/dev/null)"; then
+	test_set_prereq UNZIP
+else
+	say 'Skipping ZIP validation tests: unzip not found'
+fi
+
+test_expect_success UNZIP 'verify zip format' '
+	unzip -t master.zip
+'
+
+test_expect_success UNZIP 'unzip' '
+	rm -rf master &&
+	unzip master.zip
+'
+
+test_expect_success UNZIP 'count files (zip)' '
+	ls master/ >output &&
+	test_line_count = 5 output
+'
+
+test_expect_success UNZIP 'verify unzipped file-5' '
+	grep "^5$" master/file-5 &&
+	test_line_count = 1 master/file-5
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0108-patch.sh b/www/git.causal.agency/cgit/tests/t0108-patch.sh
new file mode 100755
index 00000000..013d6802
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0108-patch.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='Check content on patch page'
+. ./setup.sh
+
+test_expect_success 'generate foo/patch' '
+	cgit_query "url=foo/patch" >tmp
+'
+
+test_expect_success 'find `From:` line' '
+	grep "^From: " tmp
+'
+
+test_expect_success 'find `Date:` line' '
+	grep "^Date: " tmp
+'
+
+test_expect_success 'find `Subject:` line' '
+	grep "^Subject: commit 5" tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'compare with output of git-format-patch(1)' '
+	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
+	git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD^ >tmp2 &&
+	strip_headers <tmp >tmp_ &&
+	test_cmp tmp_ tmp2
+'
+
+test_expect_success 'find initial commit' '
+	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+'
+
+test_expect_success 'generate patch for initial commit' '
+	cgit_query "url=foo/patch&id=$root" >tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'generate patches for multiple commits' '
+	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
+	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
+	cgit_query "url=foo/patch&id=$id&id2=$id2" >tmp
+'
+
+test_expect_success 'find `cgit` signature' '
+	tail -2 tmp | head -1 | grep "^cgit"
+'
+
+test_expect_success 'compare with output of git-format-patch(1)' '
+	CGIT_VERSION=$(sed -n "s/CGIT_VERSION = //p" ../../VERSION) &&
+	git --git-dir="$PWD/repos/foo/.git" format-patch -N --subject-prefix="" --signature="cgit $CGIT_VERSION" --stdout HEAD~3..HEAD >tmp2 &&
+	strip_headers <tmp >tmp_ &&
+	test_cmp tmp_ tmp2
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
new file mode 100755
index 00000000..8cee75cd
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0109-gitconfig.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='Ensure that git does not access $HOME'
+. ./setup.sh
+
+test -n "$(which strace 2>/dev/null)" || {
+	skip_all='Skipping access validation tests: strace not found'
+	test_done
+	exit
+}
+
+strace true 2>/dev/null || {
+	skip_all='Skipping access validation tests: strace not functional'
+	test_done
+	exit
+}
+
+test_no_home_access () {
+	non_existent_path="/path/to/some/place/that/does/not/possibly/exist"
+	while test -d "$non_existent_path"; do
+		non_existent_path="$non_existent_path/$(date +%N)"
+	done &&
+	strace \
+		-E HOME="$non_existent_path" \
+		-E CGIT_CONFIG="$PWD/cgitrc" \
+		-E QUERY_STRING="url=$1" \
+		-e access -f -o strace.out cgit &&
+	test_must_fail grep "$non_existent_path" strace.out
+}
+
+test_no_home_access_success() {
+	test_expect_success "do not access \$HOME: $1" "
+		test_no_home_access '$1'
+	"
+}
+
+test_no_home_access_success
+test_no_home_access_success foo
+test_no_home_access_success foo/refs
+test_no_home_access_success foo/log
+test_no_home_access_success foo/tree
+test_no_home_access_success foo/tree/file-1
+test_no_home_access_success foo/commit
+test_no_home_access_success foo/diff
+test_no_home_access_success foo/patch
+test_no_home_access_success foo/snapshot/master.tar.gz
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
new file mode 100755
index 00000000..66fa7d5d
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0110-rawdiff.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='Check content on rawdiff page'
+. ./setup.sh
+
+test_expect_success 'generate foo/rawdiff' '
+	cgit_query "url=foo/rawdiff" >tmp
+'
+
+test_expect_success 'compare with output of git-diff(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_expect_success 'find initial commit' '
+	root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+'
+
+test_expect_success 'generate diff for initial commit' '
+	cgit_query "url=foo/rawdiff&id=$root" >tmp
+'
+
+test_expect_success 'compare with output of git-diff-tree(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_expect_success 'generate diff for multiple commits' '
+	id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
+	id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
+	cgit_query "url=foo/rawdiff&id=$id&id2=$id2" >tmp
+'
+
+test_expect_success 'compare with output of git-diff(1)' '
+	git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2 &&
+	sed "1,4d" tmp >tmp_ &&
+	cmp tmp_ tmp2
+'
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/t0111-filter.sh b/www/git.causal.agency/cgit/tests/t0111-filter.sh
new file mode 100755
index 00000000..2fdc3669
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/t0111-filter.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='Check filtered content'
+. ./setup.sh
+
+prefixes="exec"
+if [ $CGIT_HAS_LUA -eq 1 ]; then
+	prefixes="$prefixes lua"
+fi
+
+for prefix in $prefixes
+do
+	test_expect_success "generate filter-$prefix/tree/a%2bb" "
+		cgit_url 'filter-$prefix/tree/a%2bb' >tmp
+	"
+
+	test_expect_success "check whether the $prefix source filter works" '
+		grep "<code>a+b HELLO$" tmp
+	'
+
+	test_expect_success "generate filter-$prefix/about/" "
+		cgit_url 'filter-$prefix/about/' >tmp
+	"
+
+	test_expect_success "check whether the $prefix about filter works" '
+		grep "<div id='"'"'summary'"'"'>a+b HELLO$" tmp
+	'
+
+	test_expect_success "generate filter-$prefix/commit/" "
+		cgit_url 'filter-$prefix/commit/' >tmp
+	"
+
+	test_expect_success "check whether the $prefix commit filter works" '
+		grep "<div class='"'"'commit-subject'"'"'>ADD A+B" tmp
+	'
+
+	test_expect_success "check whether the $prefix email filter works for authors" '
+		grep "<author@example.com> commit A U THOR &LT;AUTHOR@EXAMPLE.COM&GT;" tmp
+	'
+
+	test_expect_success "check whether the $prefix email filter works for committers" '
+		grep "<committer@example.com> commit C O MITTER &LT;COMMITTER@EXAMPLE.COM&GT;" tmp
+	'
+done
+
+test_done
diff --git a/www/git.causal.agency/cgit/tests/valgrind/bin/cgit b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
new file mode 100755
index 00000000..dcdfbe53
--- /dev/null
+++ b/www/git.causal.agency/cgit/tests/valgrind/bin/cgit
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Note that we currently use Git's suppression file and there are variables
+# $GIT_VALGRIND and $CGIT_VALGRIND which point to different places.
+exec valgrind -q --error-exitcode=126 \
+	--suppressions="$GIT_VALGRIND/default.supp" \
+	--gen-suppressions=all \
+	--leak-check=no \
+	--track-origins=yes \
+	--log-fd=4 \
+	--input-fd=4 \
+	"$CGIT_VALGRIND/../../cgit" "$@"