Commit Diff


commit - c28994868efd1da6259f805984cc93135bd74a3a
commit + 176179b2a9e99af595892c396a219c2bc11fb6c9
blob - 89f482a86586681209238c951bc26cf38de4aab9
blob + e4a5ff4006151dfd6ad11756657c4811ac086b7c
--- .gitignore
+++ .gitignore
@@ -30,3 +30,4 @@ regress/iri_test
 regress/puny-test
 regress/*.o
 regress/gg
+regress/gmid.pid
blob - 0efddad3c22f99056a4831ab0d54ec83a972bd7b
blob + 5b2d29bc8b92dc961007b069dc0ceb317a5c6ecb
--- regress/Makefile
+++ regress/Makefile
@@ -71,6 +71,7 @@ clean:
 	rm -f localhost.cert.pem localhost.key.pem
 	rm -f testca.* valid.csr valid.key valid.crt invalid.*pem
 	rm -rf testdata fill-file puny-test gg fcgi-test
+	rm -f gmid.pid
 
 testdata: fill-file
 	mkdir testdata
blob - /dev/null
blob + afee6f8df58e37649357b07a231104c674ecddff (mode 644)
--- /dev/null
+++ regress/lib.sh
@@ -0,0 +1,185 @@
+failed=
+
+gg="./gg"
+gmid="./../gmid"
+current_test=
+
+run_test() {
+	ggflags=
+	port=10965
+	config_common="
+ipv6 off
+port $port
+"
+	hdr=
+	body=
+	dont_check=no
+
+	current_test=$1
+	rm -f reg.conf
+
+	if ! $1; then
+		echo "$1 failed"
+		failed="$failed $1"
+	else
+		echo "$1 passed"
+	fi
+
+	if [ "$dont_check" != 'no' ]; then
+		return
+	fi
+
+	if ! check; then
+		echo "gmid crashed?"
+		failed="$failed $1"
+	fi
+}
+
+# usage: gen_config <global config> <server config>
+# generates a configuration file reg.conf
+gen_config() {
+	cat <<EOF > reg.conf
+$config_common
+$1
+server "localhost" {
+	cert "$PWD/cert.pem"
+	key  "$PWD/key.pem"
+	root "$PWD/testdata"
+	$2
+}
+EOF
+	if ! checkconf; then
+		echo "failed to parse the config" >&2
+		return 1
+	fi
+}
+
+checkconf() {
+	$gmid -n -c reg.conf >/dev/null
+}
+
+# usage: setup_simple_test <global config> <server config>
+# generates a configuration file with `gen_config', validates it and
+# launches the daemon
+setup_simple_test() {
+	gen_config "$1" "$2"
+	run
+}
+
+# usage: get <path>
+# return the body of the request on stdout
+get() {
+	$gg -T30 -b $ggflags "gemini://localhost:10965/$1"
+}
+
+# usage: head <path>
+# return the meta response line on stdout
+head() {
+	$gg -T30 -h $ggflags "gemini://localhost:10965/$1"
+}
+
+# usage: raw <path>
+# return both header and body
+raw() {
+	$gg -T30 $ggflags "gemini://localhost:10965/$1"
+}
+
+# usage: fetch <path>
+# fetches the header and the body.  They're returned in $hdr and
+# $body.
+fetch() {
+	if ! hdr="$(head $1)" || ! body="$(get $1)"; then
+		return 1
+	fi
+}
+
+# usage: fetch_hdr <path>
+# fetches the header into $hdr
+fetch_hdr() {
+	hdr="$(head $1)"
+	body=""
+}
+
+# usage: check_reply header body
+# checks that $hdr and $body are equal to the given strings
+check_reply() {
+	if [ "$hdr" != "$1" ]; then
+		echo "Header mismatch" >&2
+		echo "wants : $1"      >&2
+		echo "got   : $hdr"    >&2
+		return 1
+	fi
+
+	if [ "$body" != "$2" ]; then
+		echo "Body mismatch" >&2
+		echo "wants : $1"    >&2
+		echo "got   : $body" >&2
+		return 1
+	fi
+}
+
+run() {
+	if check; then
+		kill -HUP "$(cat gmid.pid)"
+		sleep 1
+		return
+	fi
+
+	$gmid -P gmid.pid -c reg.conf
+
+	# give gmid time to bind the port, otherwise we end up
+	# executing gg when gmid isn't ready yet.
+	sleep 1
+}
+
+check() {
+	if [ ! -f gmid.pid ]; then
+		return 1
+	fi
+
+	pid="$(cat gmid.pid || true)"
+	if [ "$pid" == "" ]; then
+		return 1
+	fi
+
+	# remember: we're running under ``set -e''
+	if ps $pid >/dev/null; then
+		return 0
+	fi
+
+	return 1
+}
+
+# usage: sha in out
+# writes the sha256 of `in' to `out'
+sha() {
+	if which sha256 >/dev/null 2>&1; then
+		sha256 < "$1" > "$2"
+		return $?
+	fi
+
+	if which sha256sum >/dev/null 2>&1; then
+		sha256sum "$1" | awk '{print $1}' > "$2"
+		return $?
+	fi
+
+	echo "No sha binary found" >&2
+	exit 1
+}
+
+count() {
+	wc -l | xargs
+}
+
+quit() {
+	pid="$(cat gmid.pid || true)"
+	if [ "$pid" != "" ]; then
+		kill $pid || true
+		wait || true
+	fi
+}
+
+onexit() {
+	rm -f bigfile bigfile.sha
+	quit
+}
blob - 7566a1a63f5b003417f3d114c3c76271525a509a
blob + f34ff7d201527b969798ae1b889cc2924fa212b9
--- regress/runtime
+++ regress/runtime
@@ -1,7 +1,5 @@
 #!/bin/sh
 
-set -e
-
 if [ "${SKIP_RUNTIME_TESTS:-0}" -eq 1 ]; then
 	echo
 	echo "======================"
@@ -11,387 +9,40 @@ if [ "${SKIP_RUNTIME_TESTS:-0}" -eq 1 ]; then
 	exit 0
 fi
 
-ggflags=
+rm -f gmid.pid
 
-port=10965
+. ./lib.sh
+. ./tests.sh
 
-config_common="
-ipv6 off
-port $port
-"
-
-# usage: config <global config> <stuff for localhost>
-# generates a configuration file reg.conf
-config() {
-	cat <<EOF > reg.conf
-$config_common
-$1
-server "localhost" {
-	cert "$PWD/cert.pem"
-	key  "$PWD/key.pem"
-	root "$PWD/testdata"
-	$2
-}
-EOF
-}
-
-checkconf() {
-	./../gmid -n -c reg.conf
-}
-
-# usage: get <path>
-# return the body of the request on stdout
-get() {
-	./gg -T30 -b $ggflags "gemini://localhost:10965/$1"
-}
-
-# usage: head <path>
-# return the meta response line on stdout
-head() {
-	./gg -T30 -h $ggflags "gemini://localhost:10965/$1"
-}
-
-# usage: raw <path>
-# return both header and body
-raw() {
-	./gg -T30 $ggflags "gemini://localhost:10965/$1"
-}
-
-run() {
-	./../gmid -f -c reg.conf &
-	pid=$!
-	# give gmid time to bind the port, otherwise we end up
-	# executing gg when gmid isn't ready yet.
-	sleep 1
-}
-
-# usage: check [exit-message]
-# check if gmid is still running
-check() {
-	if ! ps $pid >/dev/null; then
-		echo ${1:-"gmid crashed?"}
-		exit 1
-	fi
-}
-
-restart() {
-	kill -HUP $pid
-	sleep 1
-}
-
-# quit gmid
-quit() {
-	kill $pid || true
-	wait || true
-}
-
-count() {
-	wc -l | xargs
-}
-
-# usage: eq a b errmsg
-# if a and b aren't equal strings, exit with errmsg
-eq() {
-	if ! [ "$1" = "$2" ]; then
-		echo "$3: \"$1\" not equal \"$2\""
-		exit 1
-	fi
-}
-
-onexit() {
-	rm -f bigfile bigfile.sha
-	quit
-}
-
-# configless tests
-
-./../gmid -p $port -H localhost -d . testdata &
-pid=$!
-sleep 1
-
-eq "$(head /)"		"20 text/gemini"	"Unexpected head for /"
-eq "$(get /)"		"# hello world$ln"	"Unexpected body for /"
-echo OK GET / in configless mode
-quit
-
-# daemon tests
-
 trap 'onexit' INT TERM EXIT
 
-endl=`printf "\r\n"`
-lf=`echo`
+run_test test_configless_mode
+run_test test_static_files
+run_test test_directory_redirect
+run_test test_serve_big_files
+run_test test_dont_execute_scripts
+run_test test_custom_mime
+run_test test_default_type
+run_test test_custom_lang
+run_test test_parse_custom_lang_per_location
+run_test test_cgi_scripts
+run_test test_cgi_big_replies
+run_test test_cgi_split_query
+run_test test_custom_index
+run_test test_custom_index_default_type_per_location
+run_test test_auto_index
+run_test test_block
+run_test test_block_return_fmt
+run_test test_entrypoint
+run_test test_require_client_ca
+run_test test_root_inside_location
+run_test test_root_inside_location_with_redirect
+run_test test_fastcgi
+run_test test_macro_expansion
+run_test test_174_bugfix
 
-config "" ""
-checkconf
-run
-
-eq "$(head /)"		"20 text/gemini"	"Unexpected head for /"
-eq "$(get /)"		"# hello world$ln"	"Unexpected body for /"
-echo OK GET /
-
-eq "$(head /foo)"	"51 not found"		"Unexpected head /foo"
-eq "$(get /foo)"	""			"Unexpected body /foo"
-echo OK GET /foo
-
-# should redirect if asked for a directory but without the trailing /
-eq "$(head /dir)"	"30 /dir/"		"Unexpected redirect for /dir"
-eq "$(get  /dir)"	""			"Unexpected body for redirect"
-echo OK GET /dir
-
-# 51 for a directory without index.gmi
-eq "$(head /dir/)"	"51 not found"		"Unexpected head for /"
-eq "$(get  /dir/)"	""			"Unexpected body for error"
-echo OK GET /dir/
-
-eq "$(head /dir/foo.gmi)" "20 text/gemini"	"Unexpected head for /dir/foo.gmi"
-eq "$(get  /dir/foo.gmi)" "# hello world$ln"	"Unexpected body for /dir/foo.gmi"
-echo OK GET /dir/foo.gmi
-
-# try a big file
-eq "$(head /bigfile)"	"20 application/octet-stream" "Unexpected head for /bigfile"
-get /bigfile > bigfile
-./sha bigfile bigfile.sha
-eq "$(cat bigfile.sha)"	"$(cat testdata/bigfile.sha)" "Unexpected sha for /bigfile"
-echo OK GET /bigfile
-
-# shouldn't be executing cgi scripts
-eq "$(head /hello)"	"20 application/octet-stream" "Unexpected head for /hello"
-echo OK GET /hello
-
-check "should be running"
-
-# try with custom mime
-config 'map "text/x-funny-text" to-ext "gmi"' 'default type "application/x-trash"'
-checkconf
-restart
-
-eq "$(head /)"		"20 text/x-funny-text"		"Unexpected head for /"
-echo OK GET / with custom mime
-
-eq "$(head /hello)"	"20 application/x-trash"	"Unexpected head for /hello"
-echo OK GET /hello with custom mime
-
-check "should be running"
-
-# try with custom lang
-config '' 'lang "it"'
-checkconf
-restart
-
-eq "$(head /)"		"20 text/gemini;lang=it"	"Unexpected head for /"
-echo OK GET / with custom lang
-
-check "should be running"
-
-# make sure we can use different lang in different location rules
-config '' 'lang "it" location "/en/*" { lang "en" } location "/de/*" { lang "de" }'
-checkconf
-echo OK parse multiple locations correctly
-restart
-
-# try with CGI scripts
-config '' 'cgi "*"'
-checkconf
-restart
-
-eq "$(head /hello)"	"20 text/gemini"		"Unexpected head for /hello"
-eq "$(get  /hello)"	"# hello world$ln"		"Unexpected body for /hello"
-echo OK GET /hello with cgi
-
-eq "$(head /slow)"	"20 text/gemini"		"Unexpected head for /slow"
-eq "$(get  /slow)"	"# hello world$ln"		"Unexpected body for /slow"
-echo OK GET /slow with cgi
-
-eq "$(head /err)"	"42 CGI error"	"Unexpected head for /err"
-eq "$(get  /err)"	""		"Unexpected body for /err"
-echo OK GET /err with cgi
-
-#eq "$(raw /invalid | wc -c | xargs)" 2048		"Unexpected body for /invalid"
-#echo OK GET /invalid with cgi
-
-eq "$(raw /max-length-reply | wc -c | xargs)" 1029	"Unexpected header for /max-length-reply"
-echo OK GET /max-length-reply with cgi
-
-# try a big file
-eq "$(head /serve-bigfile)"	"20 application/octet-stream" "Unexpected head for /serve-bigfile"
-get /bigfile > bigfile
-./sha bigfile bigfile.sha
-eq "$(cat bigfile.sha)"	"$(cat testdata/bigfile.sha)" "Unexpected sha for /serve-bigfile"
-echo OK GET /serve-bigfile with cgi
-
-# ensure we split the query correctly
-eq "$(get /env | awk /^-/ | count)"		1	"Unexpected number of arguments"
-eq "$(get /env?foo | awk /^-/ | count)"		2	"Unexpected number of arguments"
-eq "$(get /env?foo+bar | awk /^-/ | count)"	3	"Unexpected number of arguments"
-eq "$(get /env?foo+bar=5 | awk /^-/ | count)"	1	"Unexpected number of arguments"
-eq "$(get /env?foo+bar%3d5 | awk /^-/ | count)"	3	"Unexpected number of arguments"
-
-check "should be running"
-
-config '' 'index "foo.gmi"'
-checkconf
-restart
-
-eq "$(head /dir/)"	"20 text/gemini"	"Unexpected head for /"
-eq "$(get  /dir/)"	"# hello world$ln"	"Unexpected body for error"
-echo OK GET /dir/ with custom index
-
-check "should be running"
-
-config '' 'location "/dir/*" { default type "text/plain" index "hello" }'
-checkconf
-restart
-
-eq "$(head /dir/hello)"	"20 text/plain"			"Unexpected head for /"
-echo OK GET /dir/hello with location and default type
-
-eq "$(head /dir/)"	"20 text/plain"			"Unexpected head for /dir/"
-eq "$(get  /dir/|tail -1)" 'echo "# hello world"'	"Unexpected body for /dir/"
-echo OK GET /dir/ with location and custom index
-
-check "should be running"
-
-config '' 'location "/dir/*" { auto index on }'
-checkconf
-restart
-
-eq "$(head /)"		"20 text/gemini"		"Unexpected head for /"
-eq "$(get  /)"		"# hello world$ln"		"Unexpected body for /"
-echo OK GET / with auto index
-
-eq "$(head /dir)"		"30 /dir/"		"Unexpected head for /dir"
-eq "$(head /dir/)"		"20 text/gemini"	"Unexpected head for /dir/"
-eq "$(get /dir/|wc -l|xargs)"	"5"			"Unexpected body for /dir/"
-echo OK GET /dir/ with auto index on
-
-check "should be running"
-
-# test block return and strip
-
-config '' 'location "*" { block }'
-checkconf
-restart
-
-eq "$(head /)"		"40 temporary failure"		"Unexpected head for /"
-eq "$(get /)"		""				"Unexpected body for /"
-echo OK GET / with block
-
-eq "$(head /nonexists)"	"40 temporary failure"		"Unexpected head for /nonexists"
-eq "$(get /nonexists)"	""				"Unexpected body for /nonexists"
-echo OK GET /nonexists with block
-
-check "should be running"
-
-config '' '
-location "/dir" {
-	strip 1
-	block return 40 "%% %p %q %P %N test"
-}
-location "*" {
-	strip 99
-	block return 40 "%% %p %q %P %N test"
-}'
-checkconf
-restart
-
-eq "$(head /dir/foo.gmi)"	"40 % /foo.gmi  10965 localhost test"
-echo OK GET /dir/foo.gmi with strip and block
-
-eq "$(head /bigfile)"		"40 % /  10965 localhost test"
-echo OK GET /bigfile with strip and block
-
-check "should be running"
-
-# test the entrypoint
-
-config '' 'entrypoint "/env"'
-checkconf
-restart
-
-eq "$(head /foo/bar)"	"20 text/plain; lang=en"	"Unknown head for /foo/bar"
-eq "$(get /foo/bar|grep PATH_INFO)" "PATH_INFO=/foo/bar" "Unexpected PATH_INFO"
-echo OK GET /foo/bar with entrypoint
-
-# test with require ca
-
-config '' 'require client ca "'$PWD'/testca.pem"'
-checkconf
-restart
-
-eq "$(head /)"		"60 client certificate required" "Unexpected head for /"
-echo OK GET / without client certificate
-
-ggflags="-C valid.crt -K valid.key"
-eq "$(head /)"		"20 text/gemini"		"Unexpected head for /"
-echo OK GET / with valid client certificate
-
-ggflags="-C invalid.cert.pem -K invalid.key.pem"
-eq "$(head /)"		"61 certificate not authorised"	"Unexpected head for /"
-echo OK GET / with invalid client certificate
-
-ggflags=''
-
-
-# test with root inside a location
-
-config '' 'location "/foo/*" { root "'$PWD'/testdata" strip 1 }'
-checkconf
-restart
-
-eq "$(head /foo)"	"51 not found"		"Unexpected head for /foo"
-eq "$(head /foo/)"	"20 text/gemini"	"Unexpected head for /foo/"
-echo OK /foo and /foo/ with root inside location
-
-# how to match both /foo and /foo/*
-config '' '
-	location "/foo"   { block return 31 "%p/" }
-	location "/foo/*" { root "'$PWD'/testdata" strip 1 }
-'
-checkconf
-restart
-
-eq "$(head /foo)"	"31 /foo/"		"Unexpected head for /foo"
-eq "$(head /foo/)"	"20 text/gemini"	"Unexpected head for /foo/"
-echo OK /foo and /foo/ with root inside location
-
-# test with fastcgi
-
-# NB: the fcgi spawn is NOT supported outside of this test suite
-
-config 'prefork 1' 'fastcgi spawn "'$PWD'/fcgi-test"'
-checkconf
-restart
-
-eq "$(head /)"		"20 text/gemini"	"Unexpected head for /"
-eq "$(get /)"		"# Hello, world!"	"Unexpected body for /"
-echo OK GET / with fastcgi
-
-# test macro expansion
-
-cat <<EOF > reg.conf
-pwd = "$PWD"
-$config_common
-
-server "localhost" {
-	# the quoting of \$ is for sh
-	cert \$pwd "/cert.pem"
-	key  \$pwd "/key.pem"
-	root \$pwd "/testdata"
-}
-EOF
-checkconf
-restart
-
-eq "$(head /)"		"20 text/gemini"	"Unexpected head for /"
-eq "$(get /)"		"# hello world$ln"	"Unexpected body for /"
-echo OK GET / with macro expansion
-
-
-# 1.7.4 bugfix: check_for_cgi goes out-of-bound processing a string
-# that doesn't contain a '/'
-config '' 'cgi "*"'
-checkconf
-restart
-
-eq "$(head /favicon.txt)"	"51 not found"	"Unexpected head for /"
-echo OK GET /favicon.txt with cgi
+if [ "$failed" != "" ]; then
+	echo
+	echo "failed tests:$failed"
+	exit 1
+fi
blob - /dev/null
blob + c01013ee788ada6fd1c527f8d6981607520a8275 (mode 644)
--- /dev/null
+++ regress/tests.sh
@@ -0,0 +1,305 @@
+test_configless_mode() {
+	dont_check=yes
+
+	$gmid -p $port -H localhost -d . testdata &
+	pid=$!
+	sleep 1
+
+	fetch /
+	kill $pid
+	check_reply "20 text/gemini" "# hello world" || return 1
+}
+
+test_static_files() {
+	setup_simple_test
+
+	fetch /
+	check_reply "20 text/gemini" "# hello world" || return 1
+
+	fetch /foo
+	check_reply "51 not found" || return 1
+
+	fetch /dir/foo.gmi
+	check_reply "20 text/gemini" "# hello world" || return 1
+}
+
+test_directory_redirect() {
+	setup_simple_test
+
+	fetch /dir
+	check_reply "30 /dir/" || return 1
+
+	fetch /dir/
+	check_reply "51 not found" || return 1
+}
+
+test_serve_big_files() {
+	setup_simple_test
+
+	hdr="$(head /bigfile)"
+	get /bigfile > bigfile
+	sha bigfile bigfile.sha
+	body="$(cat bigfile.sha)"
+
+	check_reply "20 application/octet-stream" "$(cat testdata/bigfile.sha)"
+}
+
+test_dont_execute_scripts() {
+	setup_simple_test
+
+	fetch_hdr /hello
+	check_reply "20 application/octet-stream" "" || return 1
+}
+
+test_custom_mime() {
+	setup_simple_test 'map "text/x-funny" to-ext "gmi"' ''
+
+	fetch_hdr /
+	check_reply "20 text/x-funny"
+}
+
+test_default_type() {
+	setup_simple_test '' 'default type "application/x-foo"'
+
+	fetch_hdr /hello
+	check_reply "20 application/x-foo"
+}
+
+test_custom_lang() {
+	setup_simple_test '' 'lang it'
+
+	fetch_hdr /
+	check_reply "20 text/gemini;lang=it"
+}
+
+test_parse_custom_lang_per_location() {
+	setup_simple_test '' \
+	    'lang it location "/en/*" {lang en} location "/de/*" {lang de}'
+	# can parse multiple locations
+}
+
+test_cgi_scripts() {
+	setup_simple_test '' 'cgi "*"'
+
+	fetch /hello
+	check_reply "20 text/gemini" "# hello world" || return 1
+
+	fetch /slow
+	check_reply "20 text/gemini" "# hello world" || return 1
+
+	fetch /err
+	check_reply "42 CGI error" || return 1
+
+	fetch /invalid
+	check_reply "42 CGI error" || return 1
+}
+
+test_cgi_big_replies() {
+	setup_simple_test '' 'cgi "*"'
+
+	hdr="$(head /serve-bigfile)"
+	get /bigfile > bigfile
+	sha bigfile bigfile.sha
+	body="$(cat bigfile.sha)"
+	check_reply "20 application/octet-stream" "$(cat testdata/bigfile.sha)"
+}
+
+test_cgi_split_query() {
+	setup_simple_test '' 'cgi "*"'
+
+	for s in "1" "2 ?foo" "3 ?foo+bar" "1 ?foo+bar=5" "3 ?foo+bar%3d5"; do
+		exp="$(echo $s | sed 's/ .*//')"
+		qry="$(echo $s | sed 's/^..//')"
+
+		if [ "$exp" = "$qry" ]; then
+			# the "1" case yields exp == qry
+			qry=''
+		fi
+
+		url="/env$qry"
+
+		n="$(get "$url" | awk /^-/ | count)"
+		if [ $? -ne 0 ]; then
+			echo "failed to get /$url"
+			return 1
+		fi
+
+		if [ "$n" -ne $exp ]; then
+			echo "Unexpected number of args"
+			echo "want : $exp"
+			echo "got  : $n"
+			return 1
+		fi
+	done
+}
+
+test_custom_index() {
+	setup_simple_test '' 'index "foo.gmi"'
+
+	fetch /dir/
+	check_reply "20 text/gemini" "# hello world"
+}
+
+test_custom_index_default_type_per_location() {
+	setup_simple_test '' 'location "/dir/*" { default type "text/plain" index "hello" }'
+
+	fetch /dir/
+	check_reply "20 text/plain" "$(cat hello)"
+}
+
+test_auto_index() {
+	setup_simple_test '' 'location "/dir/*" { auto index on }'
+
+	fetch /
+	check_reply "20 text/gemini" "# hello world" || return 1
+
+	fetch_hdr /dir
+	check_reply "30 /dir/" || return 1
+
+	fetch_hdr /dir/
+	check_reply "20 text/gemini"
+
+	# we expect 5 lines from the auto index
+
+	body="$(get /dir/ | count)"
+	if [ $? -ne 0 ]; then
+		echo 'failed to get /dir/'
+		return 1
+	fi
+
+	if [ "$body" -ne 5 ]; then
+		echo "expected five lines from the auto index, got $body"
+		return 1
+	fi
+}
+
+test_block() {
+	setup_simple_test '' 'location "*" { block }'
+
+	fetch /
+	check_reply "40 temporary failure" || return 1
+
+	fetch /nonexists
+	check_reply "40 temporary failure" || return 1
+}
+
+test_block_return_fmt() {
+	setup_simple_test '' '
+location "/dir" {
+	strip 1
+	block return 40 "%% %p %q %P %N test"
+}
+location "*" {
+	strip 99
+	block return 40 "%% %p %q %P %N test"
+}'
+
+	fetch_hdr /dir/foo.gmi
+	check_reply "40 % /foo.gmi  10965 localhost test" || return 1
+
+	fetch_hdr /bigfile
+	check_reply "40 % /  10965 localhost test" || return 1
+}
+
+test_entrypoint() {
+	setup_simple_test '' 'entrypoint "/env"'
+
+	fetch_hdr /foo/bar
+	check_reply "20 text/plain; lang=en" || return 1
+
+	# TODO: test something similar with plain cgi too
+
+	body="$(get /foo/bar|grep PATH_INFO)"
+	if [ $? -ne 0 ]; then
+		echo "failed to get /foo/bar"
+		return 1
+	fi
+
+	if [ "$body" != "PATH_INFO=/foo/bar" ]; then
+		echo "Invalid PATH_INFO generated"
+		echo "want : PATH_INFO=/foo/bar"
+		echo "got  : $body"
+		return 1
+	fi
+}
+
+test_require_client_ca() {
+	setup_simple_test '' 'require client ca "'$PWD'/testca.pem"'
+
+	fetch /
+	check_reply "60 client certificate required" || return 1
+
+	ggflags="-C valid.crt -K valid.key"
+	fetch_hdr /
+	check_reply "20 text/gemini" || return 1
+
+	ggflags="-C invalid.cert.pem -K invalid.key.pem"
+	fetch_hdr /
+	check_reply "61 certificate not authorised" || return 1
+}
+
+test_root_inside_location() {
+	setup_simple_test '' 'location "/foo/*" { root "'$PWD'/testdata" strip 1 }'
+
+	fetch /foo
+	check_reply "51 not found" || return 1
+
+	fetch_hdr /foo/
+	check_reply "20 text/gemini"
+}
+
+test_root_inside_location_with_redirect() {
+	setup_simple_test '' '
+location "/foo"   { block return 31 "%p/" }
+location "/foo/*" { root "'$PWD'/testdata" strip 1 }'
+
+	fetch /foo
+	check_reply "31 /foo/" || return 1
+
+	fetch_hdr /foo/
+	check_reply "20 text/gemini"
+}
+
+test_fastcgi() {
+	# XXX: prefork 1 for testing
+	setup_simple_test 'prefork 1' 'fastcgi spawn "'$PWD'/fcgi-test"'
+
+	fetch /
+	check_reply "20 text/gemini" "# Hello, world!"
+}
+
+test_macro_expansion() {
+	cat <<EOF > reg.conf
+pwd = "$PWD"
+$config_common
+
+server "localhost" {
+	# the quoting of \$ is for sh
+	cert \$pwd "/cert.pem"
+	key  \$pwd "/key.pem"
+	root \$pwd "/testdata"
+}
+EOF
+
+	if ! checkconf; then
+		echo "failed to parse the config"
+		return 1
+	fi
+
+	run
+
+	fetch /
+	check_reply "20 text/gemini" "# hello world"
+}
+
+# 1.7.4 bugfix: check_for_cgi goes out-of-bound processing a string
+# that doesn't contain a '/'
+test_174_bugfix() {
+	setup_simple_test '' 'cgi "*"'
+
+	# thanks cage :)
+	for i in 0 1 2 3 4 5 6 7 8 9; do
+		fetch /favicon.txt
+		check_reply "51 not found" || return 1
+	done
+}