Diff options
Diffstat (limited to '')
-rw-r--r--tests/extensions/fake-lexers/lexer.lua17
-rw-r--r--tests/extensions/harness.lua38
-rw-r--r--tests/extensions/lib.sh41
-rw-r--r--tests/extensions/test-about-render.lua10
-rw-r--r--tests/extensions/test-auth.lua147
-rw-r--r--tests/extensions/test-syntax-highlight.lua13
-rw-r--r--tests/filters/dump.lua2
-rwxr-xr-xtests/filters/dump.sh2
-rwxr-xr-xtests/setup.sh80
-rwxr-xr-xtests/t0002-html-validity.sh33
-rwxr-xr-xtests/t0003-cache.sh249
-rwxr-xr-xtests/t0004-docs.sh18
-rwxr-xr-xtests/t0005-markup.sh104
-rwxr-xr-xtests/t0006-nolua.sh8
-rwxr-xr-xtests/t0101-index.sh28
-rwxr-xr-xtests/t0102-summary.sh48
-rwxr-xr-xtests/t0103-log.sh57
-rwxr-xr-xtests/t0104-tree.sh76
-rwxr-xr-xtests/t0105-plain.sh16
-rwxr-xr-xtests/t0106-commit.sh12
-rwxr-xr-xtests/t0107-diff.sh49
-rwxr-xr-xtests/t0108-rawdiff.sh45
-rwxr-xr-xtests/t0109-patch.sh62
-rwxr-xr-xtests/t0110-snapshot.sh133
-rwxr-xr-xtests/t0111-atom.sh23
-rwxr-xr-xtests/t0112-stats.sh71
-rwxr-xr-xtests/t0113-tag.sh7
-rwxr-xr-xtests/t0201-filters.sh68
-rwxr-xr-xtests/t0202-submodule-links.sh12
-rwxr-xr-xtests/t0203-dates.sh28
-rwxr-xr-xtests/t0204-limits.sh80
-rwxr-xr-xtests/t0205-config.sh41
-rwxr-xr-xtests/t0206-trailers.sh2
-rwxr-xr-xtests/t0208-mailmap.sh6
-rwxr-xr-xtests/t0301-security.sh233
-rwxr-xr-xtests/t0302-home-access.sh13
-rwxr-xr-xtests/t0303-robustness.sh277
-rwxr-xr-xtests/t0401-serve.sh9
-rwxr-xr-xtests/t0501-about-render.sh25
-rwxr-xr-xtests/t0502-syntax-highlight.sh21
-rwxr-xr-xtests/t0503-link-commits.sh8
-rwxr-xr-xtests/t0504-email-avatar.sh10
-rwxr-xr-xtests/t0505-auth.sh14
-rwxr-xr-xtests/t0506-link-trailers.sh8
-rwxr-xr-xtests/t0507-hooks.sh27
45 files changed, 1033 insertions, 1238 deletions
diff --git a/tests/extensions/fake-lexers/lexer.lua b/tests/extensions/fake-lexers/lexer.lua
index ea7768e..bb869de 100644
--- a/tests/extensions/fake-lexers/lexer.lua
+++ b/tests/extensions/fake-lexers/lexer.lua
@@ -1,10 +1,10 @@
-- A miniature stand-in for the Scintillua lexer module, just enough for the
-- syntax-highlight checks to drive every path through the filter without the
--- real collection installed. load() hands out one of the toy lexers below by
--- name and detect() recognises a single filename suffix or a shebang naming
--- fake, so a check can tell the detection path apart from the extension path
--- by which lexer ends up running. Anything the filter treats as optional, a lexer that will not
--- load or one that raises while lexing, has a name here too.
+-- real collection installed. detect() answers for the suffixes it knows,
+-- matched by case as Scintillua's own does, or fake for a shebang naming it,
+-- and load() hands out the toy lexer of that name. Besides fake there is one
+-- that stops short and one that raises, and python, which detect() knows but
+-- load() cannot find, as a collection missing a lexer would answer.
local M = {}
@@ -68,14 +68,13 @@ function M.load(name)
return lexer
end
+local suffixes = { fake = "fake", short = "short", badlex = "badlex", py = "python" }
+
function M.detect(filename, line)
- if filename:match("%.viadetect$") then
- return "fake"
- end
if line ~= nil and line:match("^#!.*fake") then
return "fake"
end
- return nil
+ return suffixes[filename:match("%.([^.]+)$") or ""]
end
return M
diff --git a/tests/extensions/harness.lua b/tests/extensions/harness.lua
index 3df4719..2fe722f 100644
--- a/tests/extensions/harness.lua
+++ b/tests/extensions/harness.lua
@@ -54,18 +54,6 @@ html_url_path = sink(function(s)
end))
end)
--- No shipped extension calls the remaining sinks, so rather than risk an
--- unfaithful copy quietly passing a test, using one fails loudly until its
--- escaping is mirrored from source/html.c the way the ones above are.
-local function unmirrored(name)
- return function()
- error(name .. " is not mirrored by the test harness yet")
- end
-end
-
-html_url_arg = unmirrored("html_url_arg")
-html_include = unmirrored("html_include")
-
function harness.reset()
pieces = {}
end
@@ -113,7 +101,7 @@ end
-- Loaders registered here win over any real module on the package path, so a
-- test can hand a script a deterministic stand-in, or with a failing loader
-- prove the script's fallback for a module that is not installed.
-function harness.preload(name, module)
+local function preload(name, module)
package.preload[name] = function()
return module
end
@@ -164,7 +152,7 @@ end
-- The slice of the luaossl digest interface the avatar filters use.
function harness.stub_digest()
- harness.preload("openssl.digest", {
+ preload("openssl.digest", {
new = function(algorithm)
return {
final = function(self, text)
@@ -188,27 +176,13 @@ local function fake_crypt(password, setting)
return prefix .. password
end
--- The slices of luaossl and luaposix the auth filters use. The link and
--- unlink stubs serve the secret creation path, which the auth checks bypass
--- by replacing get_secret, so they only have to exist.
+-- The slices of luaossl and luaposix the auth checks reach.
function harness.stub_auth_modules()
local rand_counter = 0
- harness.preload("posix.sys.stat", {
- umask = function(mask)
- return 18
- end,
- })
- harness.preload("posix.unistd", {
+ preload("posix.unistd", {
crypt = fake_crypt,
- link = function(from, to)
- return nil
- end,
- unlink = function(path)
- os.remove(path)
- return 0
- end,
})
- harness.preload("openssl.rand", {
+ preload("openssl.rand", {
bytes = function(count)
local out = {}
for i = 1, count do
@@ -218,7 +192,7 @@ function harness.stub_auth_modules()
return table.concat(out)
end,
})
- harness.preload("openssl.hmac", {
+ preload("openssl.hmac", {
new = function(key, algorithm)
return {
final = function(self, payload)
diff --git a/tests/extensions/lib.sh b/tests/extensions/lib.sh
index 30a9cc8..92db245 100644
--- a/tests/extensions/lib.sh
+++ b/tests/extensions/lib.sh
@@ -1,6 +1,6 @@
# Shared groundwork for the t05xx scripts, sourced after setup.sh from inside
# the trash directory. It locates the shipped extensions and the unit checks
-# beside this file, finds the standalone Lua interpreters the checks can run
+# beside this file, lists the standalone Lua interpreters the checks can run
# under, and offers a probe that asks the Lua inside the cgit binary itself
# whether a module is available to it, since that interpreter can differ from
# every standalone one on the path.
@@ -8,30 +8,21 @@
EXTENSIONS_DIRECTORY=$(cd "$TEST_OUTPUT_DIRECTORY/../custom/extensions" && pwd)
EXT_TEST_DIRECTORY=$(cd "$TEST_OUTPUT_DIRECTORY/extensions" && pwd)
-# Prints the Lua version a binary reports, like 5.1, which is also what
-# LuaJIT reports.
-ext_lua_version() {
- "$1" -e 'io.write(string.match(_VERSION, "%d+%.%d+"))' 2>/dev/null
-}
-
-# Prints the interpreters found on the path whose version falls between 5.1
-# and the given 5.x ceiling, one per line, so a script is never failed on a
-# version it never claimed to run on.
-ext_lua_interpreters() {
- ext_lua_ceiling=$1
- for ext_lua_bin in luajit lua5.1 lua5.2 lua5.3 lua5.4 lua5.5 lua
- do
- command -v "$ext_lua_bin" >/dev/null 2>&1 || continue
- case "$(ext_lua_version "$ext_lua_bin")" in
- 5.[1-9])
- ext_lua_minor=$(ext_lua_version "$ext_lua_bin")
- ext_lua_minor=${ext_lua_minor#5.}
- test "$ext_lua_minor" -le "$ext_lua_ceiling" &&
- echo "$ext_lua_bin"
- ;;
- esac
- done
-}
+# The interpreters on the path whose version the extensions claim, 5.1 to
+# 5.5 with LuaJIT counting as 5.1, so no script is failed on a version it
+# never claimed to run on.
+interpreters=
+for ext_lua_bin in luajit lua5.1 lua5.2 lua5.3 lua5.4 lua5.5 lua
+do
+ command -v "$ext_lua_bin" >/dev/null 2>&1 || continue
+ case "$("$ext_lua_bin" -e 'io.write(_VERSION)' 2>/dev/null)" in
+ "Lua 5."[1-5])
+ interpreters="$interpreters $ext_lua_bin"
+ ;;
+ esac
+done
+test -n "$interpreters" ||
+ say 'no standalone lua on the path, unit checks skipped'
# Asks whether cgit's own Lua can require every module named, by pointing a
# throwaway commit-filter at the given repository and reading its answer off
diff --git a/tests/extensions/test-about-render.lua b/tests/extensions/test-about-render.lua
index 88c136c..e901d28 100644
--- a/tests/extensions/test-about-render.lua
+++ b/tests/extensions/test-about-render.lua
@@ -219,8 +219,7 @@ out = render("README.md", "[![a](x.svg)](y)\n![b](z.svg)\n\ntext")
h.contains("a paragraph of badges leaves nothing behind", out, "<div class='markdown'><p>text</p></div>")
out = render("README.md", "```lua\nlocal x = 1 < 2\n```\nafter")
-h.contains("a fenced block carries its language", out,
- "<pre><code data-lang='lua'>local x = 1 &lt; 2\n</code></pre>")
+h.contains("a fenced block renders as code", out, "<pre><code>local x = 1 &lt; 2\n</code></pre>")
h.contains("text after the fence renders on", out, "<p>after</p>")
out = render("README.md", "~~~\n**not bold**\n~~~")
@@ -329,7 +328,7 @@ h.contains("an oversized readme renders as plain text", out, "<pre class='plaint
-- Man pages.
out = render("page.1", ".SH NAME")
-h.contains("a section heading renders", out, "<div class='markdown manpage'><h2 id='name'>NAME</h2></div>")
+h.contains("a section heading renders", out, "<div class='markdown'><h2 id='name'>NAME</h2></div>")
out = render("page.man", '.SH "TWO WORDS"')
h.contains("a quoted heading argument is unquoted", out, "<h2 id='two-words'>TWO WORDS</h2>")
@@ -393,7 +392,7 @@ h.contains("text after the definitions renders", out, "<p>after</p>")
out = render("page.1", ".if n \\\n\\{\\\n.ds x y\n.\\}\nafter")
h.contains("a conditional block's requests and braces draw nothing", out,
- "<div class='markdown manpage'><p>after</p></div>")
+ "<div class='markdown'><p>after</p></div>")
out = render("page.1", "The \\m[blue]\\fBManual\\fR\\m[]\\s-2\\u[1]\\d\\s+2 here")
h.contains("DocBook's link colours and footnote marks draw nothing", out,
@@ -401,13 +400,12 @@ h.contains("DocBook's link colours and footnote marks draw nothing", out,
out = render("page.1", ".de x\n.B hidden\n.. \nshown")
h.contains("a definition ends at two dots with blanks after them", out,
- "<div class='markdown manpage'><p>shown</p></div>")
+ "<div class='markdown'><p>shown</p></div>")
out = render("page.1", ".TP\n.PD 0\n.B \\-a\nall\n.PD\nDon\\*(Aqt")
h.excludes("a spacing macro draws nothing", out, ">0")
h.contains("DocBook's apostrophe string expands", out, "Don't")
-
out = render("page.1", '.\\" a comment\nvisible')
h.excludes("a roff comment is dropped", out, "comment")
h.contains("the line after it renders", out, "visible")
diff --git a/tests/extensions/test-auth.lua b/tests/extensions/test-auth.lua
index 742eabe..974544d 100644
--- a/tests/extensions/test-auth.lua
+++ b/tests/extensions/test-auth.lua
@@ -94,7 +94,6 @@ h.equals("an account is found whatever its case", hash, stored_hash)
h.equals("an unknown account is not", account_hash("nobody"), nil)
local _, stand_in = account_hash("nobody")
h.equals("it brings another account's hash to spend instead", stand_in, stored_hash)
-h.equals("a nil user is not", account_hash(nil), nil)
local userset = repo_userset("secret-repo")
h.check("a protected repository lists its users",
@@ -113,12 +112,14 @@ if variant == "file" then
h.equals("a missing users file turns every login down", account_hash("alice"), nil)
h.redirect_file("/etc/cgit-auth/users", "auth-users")
-- Without the repos file nothing tells a protected repository from a
- -- public one, so every repository is refused while the index stays up.
+ -- public one, so every page is refused, the index too, which cgit asks
+ -- about with an empty repository name.
h.redirect_file("/etc/cgit-auth/repos", "auth-repos-missing")
local refused = repo_userset("unlisted")
h.check("a missing repos file refuses every repository",
refused ~= nil and next(refused) == nil)
- h.equals("the index stays public all the same", repo_userset(nil), nil)
+ local index = repo_userset("")
+ h.check("and the index as well", index ~= nil and next(index) == nil)
h.redirect_file("/etc/cgit-auth/repos", "auth-repos")
end
@@ -141,7 +142,7 @@ h.equals("no header yields no cookie", get_cookie(nil, "cgitauth"), nil)
h.equals("a longer name does not match", get_cookie("xcgitauth=z", "cgitauth"), nil)
h.equals("a magic character in the name is taken literally", get_cookie("a-b=z", "a-b"), "z")
h.equals("spaces around a pair are ignored", get_cookie(" foo=1 ; cgitauth=abc ", "cgitauth"), "abc")
-h.equals("a value keeps its own equals signs", get_cookie("cgitauth=a=b", "cgitauth"), "a=b")
+h.equals("a cookie value keeps its own equals signs", get_cookie("cgitauth=a=b", "cgitauth"), "a=b")
-- The Cookie header is the visitor's to write, so a run of spaces in it
-- must not cost the square of its length.
@@ -161,8 +162,6 @@ h.check("a scheme relative target is not", not is_safe_redirect("//evil"))
h.check("a backslash variant is not", not is_safe_redirect("/\\evil"))
h.check("a missing target is not", not is_safe_redirect(nil))
-h.equals("control characters are stripped from header values", strip_controls("a\r\nb"), "ab")
-
-- Signing and verification.
local now = os.time()
@@ -192,6 +191,7 @@ h.equals("a value signed for one field does not serve another", validate_value("
h.equals("no cookie does not verify", validate_value("username", nil), nil)
h.equals("a tiny cookie does not verify", validate_value("username", "ab"), nil)
h.equals("a leading separator does not verify", validate_value("username", "|x|1|s|sig"), nil)
+h.equals("a trailing field does not verify", validate_value("username", cookie .. "|x"), nil)
h.equals("an unsigned cookie does not verify",
validate_value("username", "username|alice|123|salt"), nil)
h.equals("an exponent spelling of the expiry does not verify",
@@ -237,106 +237,96 @@ local function run_action(action, opts)
return h.output(), ret
end
-local ret
+-- A login posts the form, and a visit asks about a repository, with a
+-- session cookie when it is given one, and comes back with the answer.
+local function login(user, pass, redirect)
+ local body = "username=" .. url_encode(user) .. "&password=" .. url_encode(pass)
+ if redirect then
+ body = body .. "&redirect=" .. url_encode(redirect)
+ end
+ return run_action("authenticate-post", { method = "POST", body = body })
+end
-out, ret = run_action("authenticate-cookie", { repo = "unlisted" })
-h.equals("a public repository needs no cookie", ret, 1)
+local function visit(repo, token)
+ local _, ret = run_action("authenticate-cookie",
+ { repo = repo, cookie = token and session_name .. "=" .. token })
+ return ret
+end
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo" })
-h.equals("a protected repository turns a bare request away", ret, 0)
+-- Where the signed way back in a login form leads.
+local function way_back_in(form)
+ local token = form:match("name='redirect' value='([^']*)'")
+ return token and validate_value("redirect", token)
+end
+
+h.equals("a public repository needs no cookie", visit("unlisted"), 1)
+h.equals("a protected repository turns a bare request away", visit("secret-repo"), 0)
local session = secure_value("username", "Alice", now + 3600, stored_hash)
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. session })
-h.equals("a signed session for a member is let through", ret, 1)
+h.equals("a signed session for a member is let through", visit("secret-repo", session), 1)
-- A session is signed with its account's hash, so a new password or a
-- removed account ends it.
local stale = secure_value("username", "Alice", now + 3600, "$6$rounds=300000$old$hash")
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. stale })
-h.equals("a session from before a password change is turned away", ret, 0)
+h.equals("a session from before a password change is turned away", visit("secret-repo", stale), 0)
local orphan = secure_value("username", "bob", now + 3600, stored_hash)
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. orphan })
-h.equals("a session for a member with no account is turned away", ret, 0)
+h.equals("a session for a member with no account is turned away", visit("secret-repo", orphan), 0)
local unbound = secure_value("username", "Alice", now + 3600)
h.equals("a session signed without its binding does not verify",
validate_value("username", unbound, account_hash), nil)
-out, ret = run_action("authenticate-cookie", { repo = "empty-repo", cookie = session_name .. "=" .. session })
-h.equals("a memberless repository denies even a valid session", ret, 0)
+h.equals("a memberless repository denies even a valid session", visit("empty-repo", session), 0)
local outsider = secure_value("username", "carol", now + 3600, stored_hash)
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. outsider })
-h.equals("a signed session for an outsider is turned away", ret, 0)
+h.equals("a signed session for an outsider is turned away", visit("secret-repo", outsider), 0)
local forged = session:sub(1, -2) .. (session:sub(-1) == "0" and "1" or "0")
-out, ret = run_action("authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. forged })
-h.equals("a forged session is turned away", ret, 0)
+h.equals("a forged session is turned away", visit("secret-repo", forged), 0)
-out, ret = run_action("no-such-action", {})
+local _, ret = run_action("no-such-action", {})
h.equals("an unknown action denies rather than raising", ret, 0)
-out, ret = run_action("body", { url = "/repo/log/?q=x" })
+out = run_action("body", { url = "/repo/log/?q=x" })
h.contains("the login form posts to the login url", out, "<form class='login' method='post' action='/?p=login'>")
h.equals("a first visit says nothing failed", out:find("login-error", 1, true), nil)
-local token = out:match("name='redirect' value='([^']*)'")
-h.equals("the form carries a signed way back", token and validate_value("redirect", token), "/repo/log/?q=x")
+h.equals("the form carries a signed way back", way_back_in(out), "/repo/log/?q=x")
-out, ret = run_action("body", { url = "//evil.example/x" })
-token = out:match("name='redirect' value='([^']*)'")
-h.equals("an unsafe destination is swapped for the login page",
- token and validate_value("redirect", token), "/?p=login")
+out = run_action("body", { url = "//evil.example/x" })
+h.equals("an unsafe destination is swapped for the login page", way_back_in(out), "/?p=login")
h.equals("and is not taken for a failed login", out:find("login-error", 1, true), nil)
-out, ret = run_action("body", { url = "/repo/log/?q=x&login=failed" })
+out = run_action("body", { url = "/repo/log/?q=x&login=failed" })
h.contains("a failed login says so", out,
"<p class='login-error' role='alert'>Wrong username or password.</p>")
-token = out:match("name='redirect' value='([^']*)'")
-h.equals("and leaves the flag out of the way back", token and validate_value("redirect", token), "/repo/log/?q=x")
+h.equals("and leaves the flag out of the way back", way_back_in(out), "/repo/log/?q=x")
local way_back = secure_value("redirect", "/repo/", 0)
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. url_encode(password) .. "&redirect=" .. url_encode(way_back),
-})
+out = login("Alice", password, way_back)
h.contains("a good login redirects back", out, "Status: 302")
h.contains("to where the form said", out, "Location: /repo/\n")
local granted = out:match("Set%-Cookie: __Host%-cgitauth=([^;]*);")
h.equals("and grants a session that verifies", granted and validate_value("username", granted, account_hash), "Alice")
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=wrong&redirect=" .. url_encode(way_back),
-})
+out = login("Alice", "wrong", way_back)
h.contains("a bad password redirects the same way", out, "Status: 302")
h.contains("back to the page with the failure flagged", out, "Location: /repo/?login=failed\n")
h.contains("but clears the session cookie", out, "Set-Cookie: " .. session_name .. "=; ")
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=nobody&password=x&redirect=" .. url_encode(way_back),
-})
-h.contains("an unknown user is told nothing different", out, "Set-Cookie: " .. session_name .. "=; ")
-h.contains("and lands on the same flagged page", out, "Location: /repo/?login=failed\n")
-
-- A failed login for a name with no account spends crypt on a configured
-- hash, the same work a real check costs, so timing tells nothing.
crypt_settings = {}
-run_action("authenticate-post", {
- method = "POST",
- body = "username=nobody&password=x&redirect=" .. url_encode(way_back),
-})
+out = login("nobody", "x", way_back)
+h.contains("an unknown user is told nothing different", out, "Set-Cookie: " .. session_name .. "=; ")
+h.contains("and lands on the same flagged page", out, "Location: /repo/?login=failed\n")
h.equals("a missing account spends crypt on a configured hash", crypt_settings[1], stored_hash)
if variant == "file" then
h.redirect_file("/etc/cgit-auth/users", "auth-users-missing")
crypt_settings = {}
- run_action("authenticate-post", {
- method = "POST",
- body = "username=nobody&password=x&redirect=" .. url_encode(way_back),
- })
+ login("nobody", "x", way_back)
h.equals("with no accounts at all nothing is spent", #crypt_settings, 0)
h.redirect_file("/etc/cgit-auth/users", "auth-users")
end
@@ -344,41 +334,36 @@ end
-- crypt's work grows with the password, so one past the cap is turned down
-- before crypt sees it.
crypt_settings = {}
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. string.rep("a", 257) .. "&redirect=" .. url_encode(way_back),
-})
+out = login("Alice", string.rep("a", 257), way_back)
h.contains("an overlong password is turned down", out, "Set-Cookie: " .. session_name .. "=; ")
h.equals("without spending crypt on it", #crypt_settings, 0)
-run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. string.rep("a", 256) .. "&redirect=" .. url_encode(way_back),
-})
+login("Alice", string.rep("a", 256), way_back)
h.equals("a password at the cap is still checked", #crypt_settings, 1)
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. url_encode(password),
-})
+out = login("Alice", password)
h.contains("a post without the signed token is not served", out, "Status: 404")
-local hijack = secure_value("redirect", "//evil.example/", 0)
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. url_encode(password) .. "&redirect=" .. url_encode(hijack),
-})
+out = login("Alice", password, secure_value("redirect", "//evil.example/", 0))
h.contains("even a signed unsafe destination is not followed", out, "Status: 404")
-- Over plain http the cookie cannot be Secure, and so loses the __Host- name.
load_script({ { "local cookie_insecure = false", "local cookie_insecure = true" } })
-out, ret = run_action("authenticate-post", {
- method = "POST",
- body = "username=Alice&password=" .. url_encode(password) .. "&redirect=" .. url_encode(way_back),
-})
+out = login("Alice", password, way_back)
h.contains("an insecure session cookie keeps its plain name", out, "Set-Cookie: cgitauth=")
h.excludes("and is not marked Secure", out, "; Secure")
load_script()
+-- A cookie carries whole seconds, while on Lua 5.3 and later a session length
+-- worked out with a fraction is a float, which tostring writes with a ".0".
+load_script({ { "local session_seconds = 7 * 24 * 60 * 60",
+ "local session_seconds = 1.5 * 24 * 60 * 60" } })
+out = login("Alice", password, way_back)
+granted = out:match("Set%-Cookie: __Host%-cgitauth=([^;]*);")
+h.equals("a session a day and a half long still verifies",
+ granted and validate_value("username", granted, account_hash), "Alice")
+h.contains("and its cookie lasts as many whole seconds", out, "; Max-Age=129600\n")
+load_script()
+
-- The libraries load only once a request needs one, so with every one of
-- them gone a public repository is still served, while a protected one
-- fails closed instead of reaching its content.
@@ -386,10 +371,8 @@ for _, name in ipairs({ "openssl.rand", "openssl.hmac", "posix.sys.stat", "posix
package.loaded[name] = nil
h.preload_failure(name)
end
-out, ret = run_action("authenticate-cookie", { repo = "unlisted" })
-h.equals("without the libraries a public repository is served", ret, 1)
-h.check("while a protected one fails closed", not pcall(run_action,
- "authenticate-cookie", { repo = "secret-repo", cookie = session_name .. "=" .. session }))
+h.equals("without the libraries a public repository is served", visit("unlisted"), 1)
+h.check("while a protected one fails closed", not pcall(visit, "secret-repo", session))
h.check("and so does its login form", not pcall(run_action, "body", {}))
h.finish()
diff --git a/tests/extensions/test-syntax-highlight.lua b/tests/extensions/test-syntax-highlight.lua
index 50c7bc8..a0debc6 100644
--- a/tests/extensions/test-syntax-highlight.lua
+++ b/tests/extensions/test-syntax-highlight.lua
@@ -70,10 +70,9 @@ h.equals("a lexer stopping early leaves an escaped tail", out, "<span class='hl-
out = render("boom.badlex", "some <code>")
h.equals("a lexer that raises falls back to escaped text", out, "some &lt;code&gt;")
--- The viadetect lexer name only resolves through detect(), so highlighted
--- output proves the detection path ran ahead of the extension path.
-out = render("script.viadetect", "if")
-h.equals("filename detection picks the lexer", out, "<span class='hl-keyword'>if</span>")
+out = render("HELLO.FAKE", "if 42")
+h.contains("an extension in capitals finds its lexer", out,
+ "<span class='hl-keyword'>if</span>")
out = render("post-receive", "#!/usr/bin/fake\nif")
h.contains("a shebang picks the lexer of a file with no extension", out,
@@ -111,10 +110,4 @@ out, ret = render("empty.fake", "")
h.equals("an empty blob renders empty", out, "")
h.equals("an empty blob still answers zero", ret, 0)
--- cgit's C sink stops at the first NUL byte, which the script documents for
--- binaries that slip past the text detection.
-out = render("blob.bin", "ab\0cd")
-h.contains("bytes before a nul survive", out, "ab")
-h.excludes("bytes after a nul are dropped", out, "cd")
-
h.finish()
diff --git a/tests/filters/dump.lua b/tests/filters/dump.lua
index d654621..585538a 100644
--- a/tests/filters/dump.lua
+++ b/tests/filters/dump.lua
@@ -1,4 +1,4 @@
--- Test fixture for the cgit Lua filter API, exercised by t0201-filters.sh.
+-- Test fixture for the cgit Lua filter API.
-- It echoes the filter_open arguments and upper-cases the body, which lets
-- the test confirm arguments and content flow through the lua: filter path.
diff --git a/tests/filters/dump.sh b/tests/filters/dump.sh
index a4c1571..1340fe5 100755
--- a/tests/filters/dump.sh
+++ b/tests/filters/dump.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Test fixture for the cgit exec filter API, exercised by t0201-filters.sh.
+# Test fixture for the cgit exec filter API.
# It echoes the arguments it was invoked with and upper-cases the body, which
# lets the test confirm that both flow through the exec: filter path.
diff --git a/tests/setup.sh b/tests/setup.sh
index a1f2d15..92451b7 100755
--- a/tests/setup.sh
+++ b/tests/setup.sh
@@ -1,13 +1,11 @@
# Shared groundwork sourced by every test script. It builds a handful of small
# repositories under repos/ and writes a cgitrc pointing at them, then offers
-# the helpers that ask cgit for a page so a test only has to look at what comes
-# back. Sourcing it also brings in the test library from the bundled Git tree,
-# which is where test_expect_success and the trash directory come from. Set
-# CGIT_TEST_NO_CREATE_REPOS to get the helpers without paying for the fixtures.
+# the helpers that write a config of a test's own and ask cgit for a page, so
+# a test only has to look at what comes back. Sourcing it also brings in the
+# test library from the bundled Git tree, which is where test_expect_success
+# and the trash directory come from. Set CGIT_TEST_NO_CREATE_REPOS to get the
+# helpers without paying for the fixtures.
-# The Git test library would run every Git command under Valgrind if it saw
-# --valgrind, and only cgit is worth watching, so the option is taken out
-# here and acted on further down.
LF='
'
# Trash directories are collected under one trash/, which keeps the ignore
@@ -15,6 +13,9 @@ LF='
# one given on the command line still wins.
test_argv="${LF}--root=trash"
+# The Git test library would run every Git command under Valgrind if it saw
+# --valgrind, and only cgit is worth watching, so the option is taken out
+# here and acted on further down.
while test $# != 0
do
case "$1" in
@@ -43,11 +44,10 @@ TEST_NO_CREATE_REPO=YesPlease
# The library spells its results directory test-results and derives these
# variables before it can be told otherwise, so they are re-pointed at the
-# plainer results/. The --tee and --stress raw logs write earlier and keep
-# the library's name.
+# plainer results/. The --tee and --stress raw logs and the leak logs write
+# earlier and keep the library's name.
TEST_RESULTS_DIR="$TEST_OUTPUT_DIRECTORY/results"
TEST_RESULTS_BASE="$TEST_RESULTS_DIR/$TEST_NAME$TEST_STRESS_JOB_SFX"
-TEST_RESULTS_SAN_DIR="$TEST_RESULTS_BASE.$TEST_RESULTS_SAN_DIR_SFX"
# The library has moved into the trash directory by now, so paths outside it
# are anchored to TEST_OUTPUT_DIRECTORY. The tests run cgit by name, so the
@@ -69,9 +69,10 @@ FILTER_DIRECTORY=$(cd "$TEST_OUTPUT_DIRECTORY/filters" && pwd)
# them have to ask the binary rather than assume.
if cgit --version | grep -F -q "[+] Lua scripting"
then
- export CGIT_HAS_LUA=1
+ CGIT_HAS_LUA=1
+ test_set_prereq CGIT_LUA
else
- export CGIT_HAS_LUA=0
+ CGIT_HAS_LUA=0
fi
# Creates a repository holding the given number of one file commits. A third
@@ -107,8 +108,7 @@ mkrepo() {
}
setup_repos() {
- rm -rf cache
- mkdir -p cache
+ mkdir cache
mkrepo repos/foo 5 >/dev/null
mkrepo repos/bar 50 commit-graph >/dev/null
mkrepo repos/foo+bar 10 testplus >/dev/null
@@ -169,12 +169,60 @@ EOF
fi
}
+# Writes a config to the file named first that serves repos/<url> under the
+# url named second, with the cache off and links rooted at /, and takes the
+# rest as settings. A repository copies its defaults from the global settings
+# read before its repo.url, so whatever order they come in, the repo.* ones
+# go after the repository and all others ahead of it. A repo.path setting
+# names a repository kept somewhere else.
+cgitrc_repo() {
+ cgitrc_file=$1
+ cgitrc_url=$2
+ shift 2
+ cgitrc_path="repo.path=$PWD/repos/$cgitrc_url/.git"
+ cgitrc_before=
+ cgitrc_after=
+ for cgitrc_setting
+ do
+ case "$cgitrc_setting" in
+ repo.path=*)
+ cgitrc_path=$cgitrc_setting
+ ;;
+ repo.*)
+ cgitrc_after="$cgitrc_after$cgitrc_setting$LF"
+ ;;
+ *)
+ cgitrc_before="$cgitrc_before$cgitrc_setting$LF"
+ ;;
+ esac
+ done
+ printf '%s' "virtual-root=/$LF" "cache-size=0$LF" "$cgitrc_before" \
+ "repo.url=$cgitrc_url$LF" "$cgitrc_path$LF" "$cgitrc_after" >"$cgitrc_file"
+}
+
+# Writes the global settings named after the file ahead of a copy of the
+# shared cgitrc. The copy turns the cache off, because a page is cached under
+# its request alone and would otherwise come back as the shared config made
+# it.
+cgitrc_copy() {
+ cgitrc_file=$1
+ shift
+ {
+ test $# = 0 || printf '%s\n' "$@"
+ sed "s/^cache-size=.*/cache-size=0/" cgitrc
+ } >"$cgitrc_file"
+}
+
+cgit_rc() {
+ CGIT_CONFIG="$PWD/$1" QUERY_STRING="$2" cgit
+}
+
cgit_query() {
- CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="$1" cgit
+ cgit_rc cgitrc "$1"
}
cgit_url() {
- CGIT_CONFIG="$PWD/cgitrc" QUERY_STRING="url=$1" cgit
+ cgit_rc cgitrc "url=$1"
}
# The response headers run down to the first empty line, and a test comparing
diff --git a/tests/t0002-html-validity.sh b/tests/t0002-html-validity.sh
index f5e918c..bdccaf2 100755
--- a/tests/t0002-html-validity.sh
+++ b/tests/t0002-html-validity.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Runs the tidy checker over one page of each kind cgit renders, caring only
+# Runs the tidy checker over one page of most kinds cgit renders, caring only
# about the shape of the markup. What the pages say is the business of the
# t01xx scripts. Tidy is optional and old versions predate the elements cgit
# uses, so the file steps aside when a usable one cannot be found.
@@ -9,36 +9,21 @@ test_description='Validate html with tidy'
. ./setup.sh
test_url() {
- tidy_options="-eq"
- test -z "$NO_TIDY_WARNINGS" || tidy_options="$tidy_options --show-warnings no"
# A second argument names a config of its own, for pages the shared
# config keeps turned off.
- if test -n "$2"
- then
- CGIT_CONFIG="$PWD/$2" QUERY_STRING="url=$1" cgit >tidy-$test_count.tmp || return
- else
- cgit_url "$1" >tidy-$test_count.tmp || return
- fi
+ cgit_rc "${2:-cgitrc}" "url=$1" >tidy-$test_count.tmp || return
# Tidy reads what it is given as a whole document, so the response
# headers are dropped before it sees the page.
strip_headers <tidy-$test_count.tmp >tidy-$test_count || return
- "$tidy" $tidy_options tidy-$test_count
- status=$?
-
# Tidy leaves with 1 for warnings and 2 for errors, and only an error is
# worth failing the test over.
- if test $status = 2
- then
- false
- else
- :
- fi
+ "$tidy" -eq tidy-$test_count
+ test $? != 2
}
tidy=$(command -v tidy) || {
skip_all='Skipping html validation tests: tidy not found'
test_done
- exit
}
# Releases of tidy that predate HTML5 reject the semantic elements cgit emits,
@@ -49,7 +34,6 @@ printf '<!DOCTYPE html>\n<html lang="en"><head><title>t</title></head><body><hea
test $? -ge 2 && {
skip_all='Skipping html validation tests: tidy is too old for HTML5'
test_done
- exit
}
test_expect_success 'index page' 'test_url ""'
@@ -65,14 +49,7 @@ test_expect_success 'error page' 'test_url "no-such-repo"'
# Stats and blame stay off in the shared config, so their pages validate
# through a config of their own.
test_expect_success 'set up a features config' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-stats=year" &&
- echo "enable-blame=1" &&
- echo "repo.url=feat" &&
- echo "repo.path=$PWD/repos/foo/.git"
- } >featrc
+ cgitrc_repo featrc feat max-stats=year enable-blame=1 "repo.path=$PWD/repos/foo/.git"
'
test_expect_success 'stats page' 'test_url "feat/stats/" featrc'
diff --git a/tests/t0003-cache.sh b/tests/t0003-cache.sh
index 06cdd99..e07fcd0 100755
--- a/tests/t0003-cache.sh
+++ b/tests/t0003-cache.sh
@@ -2,8 +2,6 @@
# Exercises the cache. The first three tests set cache-size to nothing, one
# slot and the full table in turn, then count what the requests left behind.
-# The rest cover a page longer than the output buffer and a key too long to
-# be read back.
test_description='Validate cache'
. ./setup.sh
@@ -31,8 +29,7 @@ test_expect_success 'verify cache-size=0' '
rm -f cache/* &&
set_cache_size 0 &&
request_all_pages &&
- ls cache >output &&
- test_line_count = 0 output
+ test_dir_is_empty cache
'
test_expect_success 'verify cache-size=1' '
@@ -79,29 +76,10 @@ test_expect_success 'set up a repo with a page larger than the output buffer' '
rm -rf cache2 && mkdir cache2
'
-# The second request lands while the first is inside its filter, which says
-# so through a marker file, since a fresh binary can take a moment to start.
-overlap_requests() {
- n=0
- rm -f filter-running
- CGIT_CONFIG="$PWD/lockrc" QUERY_STRING="url=bigpage/about/" cgit >first.out 2>first.err &
- while ! test -f filter-running && test $n -lt 10
- do
- sleep 1
- n=$((n + 1))
- done
- CGIT_CONFIG="$PWD/lockrc" QUERY_STRING="url=bigpage/log/" cgit >second.out 2>second.err
- wait
-}
-
-bigpage_query() {
- CGIT_CONFIG="$PWD/bigrc" QUERY_STRING="$1" cgit
-}
-
test_expect_success 'the first request fills the slot and the second replays it' '
- bigpage_query "url=bigpage/tree/big.txt" >big.first &&
+ cgit_rc bigrc "url=bigpage/tree/big.txt" >big.first &&
test $(wc -c <big.first) -gt 65536 &&
- bigpage_query "url=bigpage/tree/big.txt" >big.second &&
+ cgit_rc bigrc "url=bigpage/tree/big.txt" >big.second &&
strip_headers <big.first >big.first.body &&
strip_headers <big.second >big.second.body &&
test_cmp big.first.body big.second.body
@@ -109,7 +87,7 @@ test_expect_success 'the first request fills the slot and the second replays it'
test_expect_success 'the cached body matches one generated with the cache off' '
sed -e "s/^cache-size=64$/cache-size=0/" bigrc >bignocache &&
- CGIT_CONFIG="$PWD/bignocache" QUERY_STRING="url=bigpage/tree/big.txt" cgit >big.nocache &&
+ cgit_rc bignocache "url=bigpage/tree/big.txt" >big.nocache &&
strip_headers <big.nocache >big.nocache.body &&
test_cmp big.nocache.body big.second.body
'
@@ -126,15 +104,14 @@ test_expect_success 'an over-long cache key leaves no slot behind' '
rm -rf cache3 && mkdir cache3 &&
sed -e "s|^cache-root=.*|cache-root=$PWD/cache3|" bigrc >bigkeyrc &&
long=$(awk "BEGIN{s=\"\";for(i=0;i<6000;i++)s=s \"k\"; print s}") &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/&q=$long" cgit >key.out 2>key.err &&
+ cgit_rc bigkeyrc "url=bigpage/&q=$long" >key.out 2>/dev/null &&
grep "</html>" key.out &&
- ls cache3 >key.slots &&
- test_line_count = 0 key.slots
+ test_dir_is_empty cache3
'
test_expect_success 'an ordinary key still fills a slot' '
rm -rf cache3 && mkdir cache3 &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/" cgit >/dev/null &&
+ cgit_rc bigkeyrc "url=bigpage/" >/dev/null &&
ls cache3 >key.slots &&
test_line_count = 1 key.slots
'
@@ -160,11 +137,10 @@ test_expect_success 'set up a repo missing a parent object' '
'
test_expect_success 'an error after output began ends the page with it and caches nothing' '
- CGIT_CONFIG="$PWD/brokenrc" QUERY_STRING="url=broken/commit/&id=$tip" cgit >broken.out &&
+ cgit_rc brokenrc "url=broken/commit/&id=$tip" >broken.out &&
grep "Not a commit" broken.out &&
test $(grep -c "^Status:" broken.out) = 1 &&
- ls cache4 >broken.slots &&
- test_line_count = 0 broken.slots
+ test_dir_is_empty cache4
'
# An error page reports a condition the repository may grow out of, and a
@@ -176,20 +152,18 @@ test_expect_success 'an error page leaves no slot behind' '
CGIT_CONFIG="$PWD/bigkeyrc" \
QUERY_STRING="url=bigpage/commit/&id=0123456789abcdef0123456789abcdef01234567" cgit >error.out &&
grep "Not a commit" error.out &&
- ls cache3 >error.slots &&
- test_line_count = 0 error.slots
+ test_dir_is_empty cache3
'
# The dumb transport serves files that already sit on the disk, so a copy in
# a slot would only double the disk they take.
test_expect_success 'a clone file leaves no slot behind' '
rm -rf cache3 && mkdir cache3 &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/info/refs" cgit >clone.out &&
+ cgit_rc bigkeyrc "url=bigpage/info/refs" >clone.out &&
grep "refs/heads/master" clone.out &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/objects/info/packs" cgit >packs.out &&
+ cgit_rc bigkeyrc "url=bigpage/objects/info/packs" >packs.out &&
grep "^Status: 200" packs.out &&
- ls cache3 >clone.slots &&
- test_line_count = 0 clone.slots
+ test_dir_is_empty cache3
'
# A response past cache-max-slot-size is served whole but kept nowhere, both
@@ -202,25 +176,23 @@ test_expect_success 'a response over cache-max-slot-size is served but not kept'
echo "cache-max-slot-size=32" &&
cat bigkeyrc
} >slotsizerc &&
- CGIT_CONFIG="$PWD/slotsizerc" QUERY_STRING="url=bigpage/tree/big.txt" cgit >big.unkept &&
+ cgit_rc slotsizerc "url=bigpage/tree/big.txt" >big.unkept &&
tail -c 200 big.unkept | grep "</html>" &&
- ls cache3 >unkept.slots &&
- test_line_count = 0 unkept.slots &&
- CGIT_CONFIG="$PWD/slotsizerc" QUERY_STRING="url=bigpage/" cgit >/dev/null &&
+ test_dir_is_empty cache3 &&
+ cgit_rc slotsizerc "url=bigpage/" >/dev/null &&
ls cache3 >kept.slots &&
test_line_count = 1 kept.slots
'
test_expect_success 'an expired slot is dropped when its refill is too large' '
rm -rf cache3 && mkdir cache3 &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/tree/big.txt" cgit >/dev/null &&
+ cgit_rc bigkeyrc "url=bigpage/tree/big.txt" >/dev/null &&
ls cache3 >filled.slots &&
test_line_count = 1 filled.slots &&
touch -t 200001010000 cache3/* &&
- CGIT_CONFIG="$PWD/slotsizerc" QUERY_STRING="url=bigpage/tree/big.txt" cgit >big.refill &&
+ cgit_rc slotsizerc "url=bigpage/tree/big.txt" >big.refill &&
tail -c 200 big.refill | grep "</html>" &&
- ls cache3 >refill.slots &&
- test_line_count = 0 refill.slots
+ test_dir_is_empty cache3
'
# The summary page keeps a ttl of its own, while every other page that follows
@@ -232,12 +204,11 @@ test_expect_success 'pages following a branch take the dynamic ttl' '
echo "cache-dynamic-ttl=0" &&
cat bigkeyrc
} >dynamicrc &&
- CGIT_CONFIG="$PWD/dynamicrc" QUERY_STRING="url=bigpage/log/" cgit >/dev/null &&
- CGIT_CONFIG="$PWD/dynamicrc" QUERY_STRING="url=bigpage/log/&h=master" cgit >/dev/null &&
- ls cache3 >dynamic.slots &&
- test_line_count = 0 dynamic.slots &&
- CGIT_CONFIG="$PWD/dynamicrc" QUERY_STRING="url=bigpage/" cgit >/dev/null &&
- CGIT_CONFIG="$PWD/dynamicrc" QUERY_STRING="url=bigpage/summary/" cgit >/dev/null &&
+ cgit_rc dynamicrc "url=bigpage/log/" >/dev/null &&
+ cgit_rc dynamicrc "url=bigpage/log/&h=master" >/dev/null &&
+ test_dir_is_empty cache3 &&
+ cgit_rc dynamicrc "url=bigpage/" >/dev/null &&
+ cgit_rc dynamicrc "url=bigpage/summary/" >/dev/null &&
ls cache3 >summary.slots &&
test_line_count = 2 summary.slots
'
@@ -251,17 +222,37 @@ test_expect_success 'a failed final write still delivers the page' '
(
trap "" XFSZ &&
ulimit -f 2 &&
- CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/" cgit 2>flush.err
+ cgit_rc bigkeyrc "url=bigpage/" 2>flush.err
) | cat >flush.out &&
grep "^Status: 200" flush.out &&
grep "</html>" flush.out &&
! grep "called many times" flush.err
'
+# The second request lands while the first is inside its filter, which says
+# so through a marker file, since a fresh binary can take a moment to start.
+start_first_request() {
+ n=0
+ rm -f filter-running
+ cgit_rc lockrc "url=bigpage/about/" >first.out 2>first.err &
+ while ! test -f filter-running && test $n -lt 10
+ do
+ sleep 1
+ n=$((n + 1))
+ done
+}
+
+overlap_requests() {
+ start_first_request
+ cgit_rc lockrc "url=bigpage/log/" >/dev/null 2>second.err
+ wait
+}
+
# The fill writes the page into the lock file through stdout, and an exec
# filter puts its own pipe on stdout while it runs. The slot lock has to
# outlast that, or a second request on the slot rebuilds it under the first
-# and the first visitor gets the two pages spliced together.
+# and the first visitor gets the two pages spliced together. The second one
+# finding the lock held is ordinary contention, which logs nothing.
test_expect_success 'a slot stays locked while an exec filter runs' '
rm -rf cache3 && mkdir cache3 &&
write_script slow-filter.sh <<-EOF &&
@@ -278,7 +269,153 @@ test_expect_success 'a slot stays locked while an exec filter runs' '
overlap_requests &&
test $(grep -c "^Status:" first.out) = 1 &&
grep "id=.about." first.out &&
- ! grep "Unable to publish" first.err
+ ! grep "Unable to publish" first.err &&
+ ! grep "Unable to lock" second.err
+'
+
+# A request that opens the lock file just before its holder renames it into
+# place as the live slot goes on to lock the slot itself. strace holds the
+# second request's flock back until the first has finished, and the check
+# that the path still names the locked file turns that lock down, so the
+# slot keeps the first request's key. strace needs ptrace and a version that
+# can delay a call, which containers and older systems may not give.
+test_lazy_prereq STRACE_DELAY '
+ strace -o /dev/null -e inject=flock:delay_enter=1 true
+'
+
+test_expect_success STRACE_DELAY 'a lock taken after the rename leaves the slot alone' '
+ rm -rf cache3 && mkdir cache3 &&
+ start_first_request &&
+ strace -o /dev/null -e inject=flock:delay_enter=4000000 \
+ -E CGIT_CONFIG="$PWD/lockrc" -E QUERY_STRING="url=bigpage/log/" \
+ cgit >second.out 2>second.err &&
+ wait &&
+ grep "id=.about." first.out &&
+ grep "</html>" second.out &&
+ tr "\000" "\n" <"cache3/$(ls cache3)" | head -n 1 >slot.key &&
+ grep "about" slot.key &&
+ ! grep "log" slot.key
+'
+
+# An flock lasts until the last copy of its descriptor closes, so a filter
+# may hold the slot's lock file only as the stdout it writes the page to. A
+# stray copy would keep the slot locked for as long as the filter, or
+# anything it leaves running, lives on.
+test_expect_success PERL 'a filter holds the lock file only as its stdout' '
+ rm -rf cache3 && mkdir cache3 &&
+ write_script fd-filter "$PERL_PATH" <<-\EOF &&
+ use POSIX qw(fstat);
+ 1 while <STDIN>;
+ my @lock = stat $ENV{LOCK_FILE} or die "$!\n";
+ my @fds = grep {
+ my @fd = fstat($_);
+ @fd && $fd[0] == $lock[0] && $fd[1] == $lock[1]
+ } 0 .. 255;
+ print "lock file on fd @fds.\n";
+ EOF
+ {
+ echo "cache-size=1" &&
+ sed "/^cache-size=/d" bigkeyrc &&
+ echo "repo.readme=:file-1" &&
+ echo "repo.about-filter=exec:$PWD/fd-filter"
+ } >fdrc &&
+ LOCK_FILE="$PWD/cache3/00000000.lock" CGIT_CONFIG="$PWD/fdrc" \
+ QUERY_STRING="url=bigpage/about/" cgit >fd.out &&
+ grep -F "lock file on fd 1." fd.out
+'
+
+# A refresh that cannot take the lock serves the expired copy, and says why
+# unless the reason is another request doing the refresh. A line appended
+# to the copy shows that it is what was served.
+test_expect_success SANITY 'a refresh that cannot lock serves the expired slot and says why' '
+ rm -rf cache3 && mkdir cache3 &&
+ cgit_rc bigkeyrc "url=bigpage/" >/dev/null &&
+ slot=$(ls cache3) &&
+ echo "the expired copy" >>"cache3/$slot" &&
+ touch -t 200001010000 "cache3/$slot" &&
+ test_when_finished "chmod u+w cache3" &&
+ chmod a-w cache3 &&
+ cgit_rc bigkeyrc "url=bigpage/" >stale.out 2>stale.err &&
+ grep "the expired copy" stale.out &&
+ grep "Unable to open .*\.lock: Permission denied" stale.err
+'
+
+# A lock file that cannot take the key goes with its lock, rather than
+# staying behind locked while the page renders uncached. The limit is on
+# files, so the log goes through the pipe as well.
+test_expect_success 'a slot lock that cannot take its key is let go' '
+ rm -rf cache3 && mkdir cache3 &&
+ (
+ trap "" XFSZ &&
+ ulimit -f 0 &&
+ cgit_rc bigkeyrc "url=bigpage/" 2>&1
+ ) | cat >nokey.out &&
+ grep "</html>" nokey.out &&
+ grep "Unable to write .*\.lock" nokey.out &&
+ test_dir_is_empty cache3
+'
+
+# A scan is cached in a file named after the hash of its path, so a first
+# scan finds out what that name is. The index page is left uncached, which
+# makes each request read the list afresh.
+test_expect_success 'set up a cached scan of two repositories' '
+ mkrepo scanned/one 1 &&
+ mkrepo scanned/two 1 &&
+ {
+ echo "virtual-root=/" &&
+ echo "cache-root=$PWD/cache5" &&
+ echo "cache-size=64" &&
+ echo "cache-index-ttl=0" &&
+ echo "scan-path=$PWD/scanned"
+ } >scanrc &&
+ rm -rf cache5 && mkdir cache5 &&
+ cgit_rc scanrc "url=" >/dev/null &&
+ scanned_rc=$(ls cache5) &&
+ test_path_is_file "cache5/$scanned_rc"
+'
+
+# A request that finds the scan's lock held and has no list to go on scans
+# in its own process, leaving the file to the holder. That is only another
+# request at work and no cause for a line in the log.
+test_expect_success PERL 'a scan finding its lock held runs uncached' '
+ write_script hold-lock "$PERL_PATH" <<-\EOF &&
+ use Fcntl qw(:flock);
+ open(my $lock, ">>", shift) or die "$!\n";
+ flock($lock, LOCK_EX | LOCK_NB) or die "$!\n";
+ exit(system(@ARGV) ? 1 : 0);
+ EOF
+ rm "cache5/$scanned_rc" &&
+ CGIT_CONFIG="$PWD/scanrc" QUERY_STRING="url=" \
+ ./hold-lock "cache5/$scanned_rc.lock" cgit >held.out 2>held.err &&
+ grep ">one</a>" held.out &&
+ grep ">two</a>" held.out &&
+ test_path_is_missing "cache5/$scanned_rc" &&
+ test_must_be_empty held.err
+'
+
+# A run that died before its rename leaves a lock file behind, here longer
+# than anything the next run writes and ending in a repository and a marker.
+# Emptying the file before reuse keeps that tail out of both caches.
+test_expect_success 'a stale lock file reaches neither the scan nor a slot' '
+ {
+ printf "%01024d\n" 0 0 0 0 &&
+ echo "repo.url=ghost" &&
+ echo "repo.path=$PWD/scanned/one/.git"
+ } >stale.lock &&
+ rm -f "cache5/$scanned_rc" &&
+ cp stale.lock "cache5/$scanned_rc.lock" &&
+ CGIT_CONFIG="$PWD/scanrc" QUERY_STRING="url=" cgit >scan.out &&
+ grep ">one</a>" scan.out &&
+ ! grep ">ghost</a>" scan.out &&
+ rm -rf cache3 && mkdir cache3 &&
+ CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/" cgit >page.out &&
+ slot=$(ls cache3) &&
+ rm "cache3/$slot" &&
+ { cat page.out page.out && echo "STALE-TAIL"; } >"cache3/$slot.lock" &&
+ CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/" cgit >/dev/null &&
+ CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/" cgit >replay.out &&
+ grep "</html>" replay.out &&
+ ! grep "STALE-TAIL" replay.out
'
test_done
diff --git a/tests/t0004-docs.sh b/tests/t0004-docs.sh
index 1eca51e..79c36b1 100755
--- a/tests/t0004-docs.sh
+++ b/tests/t0004-docs.sh
@@ -2,11 +2,13 @@
# Checks that the configuration documents stay consistent with each other.
# The manual documents every key the reference config sets, the reference
-# config sets every key the manual documents, both settings sections of the
-# manual keep their entries sorted, and the three server configs carry one
-# identical content security policy.
+# config sets every key the manual documents and puts scan-path after every
+# global setting, both settings sections of the manual keep their entries
+# sorted, and the three server configs carry one identical content security
+# policy.
test_description='Check the configuration documents'
+CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
ROOT="$TEST_OUTPUT_DIRECTORY/.."
@@ -33,6 +35,16 @@ test_expect_success 'every key the reference config sets is documented' '
test_must_be_empty undocumented
'
+# A scanned repository inherits the global settings above scan-path, so the
+# sample keeps none below it.
+test_expect_success 'the reference config scans after every global setting' '
+ sed -n "/^#\{0,1\}scan-path=/,\$p" "$ROOT/custom/cgitrc" |
+ sed -n "s/^#\{0,1\}\([a-z][a-zA-Z0-9.<>-]*\)=.*/\1/p" |
+ grep -v "^repo\." >after-scan-path &&
+ echo scan-path >expect &&
+ test_cmp expect after-scan-path
+'
+
test_expect_success 'the manual lists its global settings in order' '
sed -n "/^Global settings$/,/^Repository settings$/p" "$ROOT/MANUAL.txt" |
sed -n "s/^\([a-z][a-zA-Z0-9.<>-]*\)::$/\1/p" >global-order &&
diff --git a/tests/t0005-markup.sh b/tests/t0005-markup.sh
index 5fa866d..a6dd028 100755
--- a/tests/t0005-markup.sh
+++ b/tests/t0005-markup.sh
@@ -5,6 +5,7 @@
# names the page it renders and the attribute or element it expects.
test_description='Check the markup hooks a theme relies on'
+CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
test_expect_success 'set up a repository with a file that has an extension' '
@@ -16,23 +17,13 @@ test_expect_success 'set up a repository with a file that has an extension' '
git add dir &&
git commit -m sources
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-blame=1" &&
- echo "max-stats=year" &&
- echo "readme=:file-1" &&
- echo "repo.url=hooks" &&
- echo "repo.path=$PWD/repos/hooks/.git"
- } >hooksrc
+ cgitrc_repo hooksrc hooks enable-blame=1 max-stats=year readme=:file-1
'
-hooks() { CGIT_CONFIG="$PWD/hooksrc" QUERY_STRING="url=$1" cgit; }
-
test_expect_success 'the root names the page and the repository' '
- hooks "" >tmp &&
+ cgit_rc hooksrc "url=" >tmp &&
grep "<div id=.cgit. data-page=.repolist.>" tmp &&
- hooks "hooks/log/" >tmp &&
+ cgit_rc hooksrc "url=hooks/log/" >tmp &&
grep "<div id=.cgit. data-page=.log. data-repo=.hooks.>" tmp
'
@@ -45,14 +36,14 @@ test_expect_success 'the landmarks and the search form are in place' '
'
test_expect_success 'the pages name the versions only when asked to' '
- hooks "hooks/" >footer &&
+ cgit_rc hooksrc "url=hooks/" >footer &&
grep "<meta name=.generator. content=.cgit.>" footer &&
grep "<footer class=.footer.>generated by <a href=[^>]*>cgit</a></footer>" footer &&
{
echo "enable-version-info=1" &&
cat hooksrc
} >versionrc &&
- CGIT_CONFIG="$PWD/versionrc" QUERY_STRING="url=hooks/" cgit >footer &&
+ cgit_rc versionrc "url=hooks/" >footer &&
grep "<meta name=.generator. content=.cgit v[0-9][^>]*>" footer &&
grep "<footer class=.footer.>generated by <a href=[^>]*>cgit v[0-9][^<]*</a> (<a href=[^>]*>git [0-9][^<]*</a>)</footer>" footer &&
echo "<footer>our own</footer>" >footer.html &&
@@ -61,7 +52,7 @@ test_expect_success 'the pages name the versions only when asked to' '
echo "footer=$PWD/footer.html" &&
cat hooksrc
} >versionrc &&
- CGIT_CONFIG="$PWD/versionrc" QUERY_STRING="url=hooks/" cgit >footer &&
+ cgit_rc versionrc "url=hooks/" >footer &&
grep "<footer>our own</footer>" footer &&
! grep "git-scm" footer
'
@@ -72,58 +63,58 @@ test_expect_success 'the listings name themselves, their sections and their rows
grep "<tr class=.commit.>" tmp &&
grep "class=.active. aria-current=.page." tmp &&
grep "<th class=.left. scope=.col.>" tmp &&
- hooks "" >tmp &&
+ cgit_rc hooksrc "url=" >tmp &&
grep "<table class=.list repolist. aria-label=.Repositories.>" tmp &&
grep "<tbody class=.repos.>" tmp &&
grep "<tr class=.repo.>" tmp &&
- hooks "hooks/" >tmp &&
+ cgit_rc hooksrc "url=hooks/" >tmp &&
grep "<table class=.list summary. aria-label=.Summary.>" tmp &&
grep "<tbody class=.branches.>" tmp &&
grep "<tr class=.branch.>" tmp &&
- hooks "hooks/tree/" >tmp &&
+ cgit_rc hooksrc "url=hooks/tree/" >tmp &&
grep "<table class=.list tree. aria-label=.Files.>" tmp &&
grep "<tr class=.dir.>" tmp &&
grep "<tr class=.blob.>" tmp &&
- hooks "hooks/tree/dir/" >tmp &&
+ cgit_rc hooksrc "url=hooks/tree/dir/" >tmp &&
grep "class=.ls-blob ext-c." tmp
'
test_expect_success 'the diff pages wrap each file and label their controls' '
- hooks "hooks/commit/" >tmp &&
+ cgit_rc hooksrc "url=hooks/commit/" >tmp &&
grep "<table class=.commit-info.>" tmp &&
grep "<th scope=.row.>author</th>" tmp &&
grep "<div class=.file. data-path=.dir/hello.c.>" tmp &&
grep "<label for=.diff-context.>context</label>" tmp &&
grep "<select id=.diff-context. name=.context.>" tmp &&
grep "<form method=.get. aria-label=.Diff options.>" tmp &&
- hooks "hooks/diff/&ss=1" >tmp &&
+ cgit_rc hooksrc "url=hooks/diff/&ss=1" >tmp &&
grep "<tbody class=.file. data-path=.dir/hello.c.>" tmp &&
- hooks "hooks/stats/" >tmp &&
+ cgit_rc hooksrc "url=hooks/stats/" >tmp &&
grep "<label for=.stats-authors.>authors</label>" tmp
'
test_expect_success 'the blob, blame and about pages open with their own containers' '
- hooks "hooks/tree/dir/hello.c" >tmp &&
+ cgit_rc hooksrc "url=hooks/tree/dir/hello.c" >tmp &&
grep "<div class=.blob-header.>" tmp &&
grep "<a id=.n1." tmp &&
- hooks "hooks/blame/dir/hello.c" >tmp &&
+ cgit_rc hooksrc "url=hooks/blame/dir/hello.c" >tmp &&
grep "<div class=.blob-header.>" tmp &&
- hooks "hooks/about/" >tmp &&
+ cgit_rc hooksrc "url=hooks/about/" >tmp &&
grep "<div id=.about.>" tmp
'
test_expect_success 'each page names itself, then the repository and the site' '
site="Git repository browser" &&
- hooks "hooks/" >tmp &&
+ cgit_rc hooksrc "url=hooks/" >tmp &&
grep "<title>hooks - $site</title>" tmp &&
for page in about log refs tree stats
do
- hooks "hooks/$page/" >tmp &&
+ cgit_rc hooksrc "url=hooks/$page/" >tmp &&
grep "<title>$page - hooks - $site</title>" tmp || return 1
done &&
- hooks "hooks/tree/dir/hello.c" >tmp &&
+ cgit_rc hooksrc "url=hooks/tree/dir/hello.c" >tmp &&
grep "<title>hello.c &lt; dir - hooks - $site</title>" tmp &&
- hooks "hooks/blame/dir/hello.c" >tmp &&
+ cgit_rc hooksrc "url=hooks/blame/dir/hello.c" >tmp &&
grep "<title>blame - hello.c &lt; dir - hooks - $site</title>" tmp
'
@@ -150,7 +141,7 @@ test_expect_success 'the page background is the same everywhere it is set' '
test_line_count = 1 expect &&
sed -n "/^html.cgit-standalone {\$/,/^}/s/^ background: light-dark(\(#[0-9a-f]*\), \(#[0-9a-f]*\));\$/\1 \2/p" "$css" >actual &&
test_cmp expect actual &&
- hooks "hooks/" >tmp &&
+ cgit_rc hooksrc "url=hooks/" >tmp &&
light=$(sed -n "s/.*theme-color. media=.(prefers-color-scheme: light). content=.\(#[0-9a-f]*\).*/\1/p" tmp) &&
dark=$(sed -n "s/.*theme-color. media=.(prefers-color-scheme: dark). content=.\(#[0-9a-f]*\).*/\1/p" tmp) &&
echo "$light $dark" >actual &&
@@ -158,21 +149,33 @@ test_expect_success 'the page background is the same everywhere it is set' '
'
test_expect_success 'the script waits for the page instead of holding it up' '
- hooks "hooks/" >tmp &&
+ cgit_rc hooksrc "url=hooks/" >tmp &&
grep "<script src=./cgit.js. defer></script>" tmp
'
-# The rest of the hooks need a tag, a symlink, a submodule, a trailer, a lone
-# carriage return, clone urls, an empty repository and a second page of
-# repositories, so they get a commit and a config of their own after the checks
-# above have read the first ones.
+# A collection from getElementsByClassName is live, so an age the loop moves to
+# another unit drops out of it at once and the age after it is skipped.
+test_expect_success 'the script walks the ages from a list that holds still' '
+ ! grep "getElementsByClassName" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.js"
+'
+
+# The line anchors carry a scroll margin of half the window, which centres a
+# line scrolled to the top of the view. Asking scrollIntoView to centre it as
+# well centres the margin too and drops a range three quarters of the way down.
+test_expect_success 'a line range scrolls where a single line does' '
+ grep "^ scroll-margin-top: calc(50vh - 1em);$" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.css" &&
+ grep "first.scrollIntoView();" "$TEST_OUTPUT_DIRECTORY/../assets/cgit.js"
+'
+
+# The rest of the hooks need a tag, a symlink, a submodule, a trailer, clone
+# urls, an empty repository and a second page of repositories, so they get a
+# commit and a config of their own after the checks above have read the first
+# ones.
test_expect_success 'set up the remaining kinds of entry' '
(
cd repos/hooks &&
git tag -a -m "first release" v1 &&
test_ln_s_add file-1 link &&
- printf "a\\rb\\n" >cr.txt &&
- git add cr.txt &&
git update-index --add --cacheinfo "160000,$(git rev-parse HEAD),sub" &&
git commit -m "more kinds of entry" -m "Fixes: $(git rev-parse --short HEAD)"
) &&
@@ -192,36 +195,29 @@ test_expect_success 'set up the remaining kinds of entry' '
} >morerc
'
-more() { CGIT_CONFIG="$PWD/morerc" QUERY_STRING="url=$1" cgit; }
-
test_expect_success 'the refs, summary and tree name every section and row' '
- more "hooks/refs/" >tmp &&
+ cgit_rc morerc "url=hooks/refs/" >tmp &&
grep "<table class=.list refs. aria-label=.Branches and tags.>" tmp &&
grep "<tbody class=.tags.>" tmp &&
grep "<tr class=.tag.>" tmp &&
- more "hooks/" >tmp &&
+ cgit_rc morerc "url=hooks/" >tmp &&
grep "<tbody class=.clone.>" tmp &&
- more "hooks/tree/" >tmp &&
+ cgit_rc morerc "url=hooks/tree/" >tmp &&
grep "<tbody class=.tree.>" tmp &&
grep "<tr class=.link.>" tmp &&
grep "<tr class=.mod.>" tmp
'
-test_expect_success 'a character the browser would break a line at is an escape' '
- more "hooks/tree/cr.txt" >tmp &&
- grep "a<span class=.escape.>" tmp
-'
-
test_expect_success 'the commit page has its trailer table and diffstat' '
- more "hooks/commit/" >tmp &&
+ cgit_rc morerc "url=hooks/commit/" >tmp &&
grep "<table class=.commit-trailers.>" tmp &&
grep "<table class=.diffstat.>" tmp
'
test_expect_success 'the breadcrumb, the pager and the ages carry their hooks' '
- more "hooks/tree/dir/" >tmp &&
+ cgit_rc morerc "url=hooks/tree/dir/" >tmp &&
grep "<nav class=.path. aria-label=.breadcrumb.>" tmp &&
- more "" >tmp &&
+ cgit_rc morerc "url=" >tmp &&
grep "<nav class=.pager. aria-label=.Pager.>" tmp &&
grep "class=.current. aria-current=.page." tmp &&
grep "<time class=.age-[a-z]*. datetime=.[^ ]* data-ut=.[0-9]*." tmp
@@ -232,13 +228,13 @@ test_expect_success 'a date shown in full is a plain time element' '
echo "enable-relative-dates=0" &&
cat morerc
} >fullrc &&
- CGIT_CONFIG="$PWD/fullrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc fullrc "url=" >tmp &&
grep "<time datetime=" tmp &&
! grep "data-ut=" tmp
'
test_expect_success 'an empty repository lists its clone urls as a clone table' '
- more "empty/" >tmp &&
+ cgit_rc morerc "url=empty/" >tmp &&
grep "<table class=.list clone. aria-label=.Clone urls.>" tmp &&
grep "<tbody class=.clone.>" tmp
'
@@ -248,7 +244,7 @@ test_expect_success 'embedded mode marks the root' '
echo "embedded=1" &&
cat hooksrc
} >embeddedrc &&
- CGIT_CONFIG="$PWD/embeddedrc" QUERY_STRING="url=hooks/" cgit >tmp &&
+ cgit_rc embeddedrc "url=hooks/" >tmp &&
grep "<div id=.cgit. class=.cgit-embedded. data-page=.summary." tmp
'
diff --git a/tests/t0006-nolua.sh b/tests/t0006-nolua.sh
index c315e41..cd35fca 100755
--- a/tests/t0006-nolua.sh
+++ b/tests/t0006-nolua.sh
@@ -13,7 +13,6 @@ nolua="$TEST_OUTPUT_DIRECTORY/../build/nolua/cgit"
test -x "$nolua" || {
skip_all='Skipping the Lua-less checks: build/nolua/cgit is missing, run make test'
test_done
- exit
}
nolua_query() { CGIT_CONFIG="$PWD/$1" QUERY_STRING="$2" "$nolua"; }
@@ -24,12 +23,7 @@ test_expect_success 'the binary reports no Lua' '
'
test_expect_success 'it serves a page' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=foo" &&
- echo "repo.path=$PWD/repos/foo/.git"
- } >plainrc &&
+ cgitrc_repo plainrc foo &&
nolua_query plainrc "url=foo/log/" >tmp &&
grep "^Status: 200" tmp &&
grep ">commit 5</a>" tmp
diff --git a/tests/t0101-index.sh b/tests/t0101-index.sh
index 8d389b6..2e49ed8 100755
--- a/tests/t0101-index.sh
+++ b/tests/t0101-index.sh
@@ -11,9 +11,9 @@ 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 repo' 'grep ">foo</a>" 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 repo' 'grep ">bar</a>" 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 links' 'test $(grep -o "href=./foo%2Bbar/." tmp | wc -l) = 2'
@@ -22,23 +22,15 @@ test_expect_success 'no tree-link' '! grep "foo/tree" tmp'
test_expect_success 'no log-link' '! grep "foo/log" tmp'
test_expect_success 'index links lead to the summary, log and tree' '
- echo "enable-index-links=1" >links-cgitrc &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc >>links-cgitrc &&
- CGIT_CONFIG="$PWD/links-cgitrc" QUERY_STRING="url=" cgit >tmp &&
+ cgitrc_copy links-cgitrc enable-index-links=1 &&
+ cgit_rc links-cgitrc "url=" >tmp &&
grep ">summary</a><a class=.button. href=./foo/log/.>log</a>" tmp &&
grep "href=./foo/log/.>log</a><a class=.button. href=./foo/tree/.>tree<" tmp
'
test_expect_success 'each css and js entry gets a link of its own' '
- {
- echo "css=/a.css" &&
- echo "css=/b.css" &&
- echo "js=/a.js" &&
- echo "js=" &&
- echo "js=/b.js"
- } >headrc &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc >>headrc &&
- CGIT_CONFIG="$PWD/headrc" QUERY_STRING="url=" cgit >tmp &&
+ cgitrc_copy headrc css=/a.css css=/b.css js=/a.js js= js=/b.js &&
+ cgit_rc headrc "url=" >tmp &&
grep "<link rel=.stylesheet. href=./a.css.>" tmp &&
grep "<link rel=.stylesheet. href=./b.css.>" tmp &&
grep "<script src=" tmp >scripts &&
@@ -47,12 +39,6 @@ test_expect_success 'each css and js entry gets a link of its own' '
grep "/b.js" scripts
'
-test_expect_success 'the feed link encodes the repository and the branch' '
- sed "s/^cache-size=.*/cache-size=0/" cgitrc >feedrc &&
- CGIT_CONFIG="$PWD/feedrc" HTTP_HOST=example.org QUERY_STRING="url=foo%2Bbar/log/&h=1%2B2" cgit >tmp &&
- grep "title=.Atom feed. href=.http://example.org/foo%2Bbar/atom/?h=1%2B2." tmp
-'
-
# The description limit counts bytes, so a cut has to land between
# characters rather than leave half of one on the page.
test_expect_success 'a cut description keeps whole characters' '
@@ -63,7 +49,7 @@ test_expect_success 'a cut description keeps whole characters' '
echo "repo.path=$PWD/repos/foo/.git" &&
echo "repo.desc=日本語の説明文です"
} >descrc &&
- CGIT_CONFIG="$PWD/descrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc descrc "url=" >tmp &&
grep ">日本語\.\.\.</a>" tmp
'
diff --git a/tests/t0102-summary.sh b/tests/t0102-summary.sh
index e95f270..9e0e14c 100755
--- a/tests/t0102-summary.sh
+++ b/tests/t0102-summary.sh
@@ -11,7 +11,7 @@ test_description='Check content on summary page'
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 'find branch master' 'grep "<tr class=.branch.><td><a[^>]*>master</a>" tmp'
test_expect_success 'no tags' '! grep "Tag</th>" tmp'
test_expect_success 'clone-url expanded correctly' '
grep "git://example.org/foo.git" tmp
@@ -24,11 +24,6 @@ test_expect_success 'repo description reaches the meta tag' '
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 "Tag</th>" tmp'
-test_expect_success 'clone-url expanded correctly' '
- grep "git://example.org/bar.git" tmp
-'
# A clone url is shown as written, but a browser cannot follow the ssh, scp
# or git forms, so those rows link to the first http url in the list, or to
@@ -38,17 +33,40 @@ test_expect_success 'a git clone url row falls back to the page url' '
'
test_expect_success 'an scp clone url row links to the http url' '
+ cgitrc_repo clonerc foo "repo.clone-url=git@example.org:foo https://example.org/foo" &&
+ cgit_rc clonerc "url=foo" >tmp &&
+ grep "title=.foo Git repository. href=.https://example.org/foo.>git@example.org:foo</a>" tmp &&
+ grep "vcs-git. href=.https://example.org/foo." tmp &&
+ ! grep "vcs-git. href=.git@" tmp
+'
+
+# A repository can turn the global clone-url off with an empty one of its own.
+# Neither its summary nor the page an empty repository gets then draws a Clone
+# heading over no rows.
+test_expect_success 'an empty clone url draws no clone heading' '
+ git init -q --bare repos/empty.git &&
{
echo "virtual-root=/" &&
echo "cache-size=0" &&
+ echo "clone-url=https://example.org/\$CGIT_REPO_URL" &&
echo "repo.url=foo" &&
echo "repo.path=$PWD/repos/foo/.git" &&
- echo "repo.clone-url=git@example.org:foo https://example.org/foo"
- } >clonerc &&
- CGIT_CONFIG="$PWD/clonerc" QUERY_STRING="url=foo" cgit >tmp &&
- grep "title=.foo Git repository. href=.https://example.org/foo.>git@example.org:foo</a>" tmp &&
- grep "vcs-git. href=.https://example.org/foo." tmp &&
- ! grep "vcs-git. href=.git@" tmp
+ echo "repo.clone-url=" &&
+ echo "repo.url=empty" &&
+ echo "repo.path=$PWD/repos/empty.git" &&
+ echo "repo.clone-url=" &&
+ echo "repo.url=cloned" &&
+ echo "repo.path=$PWD/repos/empty.git"
+ } >emptyrc &&
+ cgit_rc emptyrc "url=foo" >tmp &&
+ grep "commit 1" tmp &&
+ ! grep "Clone</th>" tmp &&
+ cgit_rc emptyrc "url=empty" >tmp &&
+ grep "Repository seems to be empty" tmp &&
+ ! grep "Clone</th>" tmp &&
+ cgit_rc emptyrc "url=cloned" >tmp &&
+ grep "Clone</th>" tmp &&
+ grep "https://example.org/cloned</a>" tmp
'
# Prints how many columns each row of the page's list table spans, one row to
@@ -74,7 +92,7 @@ row_widths() {
# Fails unless every row of the list table at the url is as many columns wide
# as the second argument says. A third names a config other than colsrc.
test_widths() {
- CGIT_CONFIG="$PWD/${3:-colsrc}" QUERY_STRING="url=$1" cgit >tmp &&
+ cgit_rc "${3:-colsrc}" "url=$1" >tmp &&
row_widths tmp | sort -u >actual &&
echo "$2" >expect &&
test_cmp expect actual
@@ -86,7 +104,7 @@ test_widths() {
# Each repo below shows the same history with different counts, and every
# section is cut short so that its [...] row is drawn too.
test_expect_success 'set up a repo cut short in every section' '
- mkrepo repos/cols 3 >/dev/null &&
+ mkrepo repos/cols 3 &&
git -C repos/cols branch other HEAD~1 &&
git -C repos/cols tag one HEAD~1 &&
git -C repos/cols tag -a -m two two &&
@@ -108,7 +126,7 @@ test_expect_success 'set up a repo cut short in every section' '
echo "repo.enable-log-filecount=1" &&
echo "repo.enable-log-linecount=1"
} >colsrc &&
- CGIT_CONFIG="$PWD/colsrc" QUERY_STRING="url=none" cgit >tmp &&
+ cgit_rc colsrc "url=none" >tmp &&
grep "<tbody class=.tags.>" tmp &&
grep "<tbody class=.clone.>" tmp &&
test $(grep -c ">\[...\]</a>" tmp) = 3
diff --git a/tests/t0103-log.sh b/tests/t0103-log.sh
index d7e745d..9796394 100755
--- a/tests/t0103-log.sh
+++ b/tests/t0103-log.sh
@@ -12,7 +12,7 @@ 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 1' 'grep ">commit 1<" tmp'
test_expect_success 'find commit 50' 'grep "commit 50" tmp'
# The switch button submits the select as h, so whatever head the request
@@ -38,7 +38,7 @@ test_expect_success 'a branch head keeps its plain option' '
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 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'
@@ -68,24 +68,17 @@ test_expect_success 'set up a renamed file to follow' '
git commit -am "edit b $i" || return 1
done
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-follow-links=1" &&
- echo "max-commit-count=3" &&
- echo "repo.url=fol" &&
- echo "repo.path=$PWD/repos/fol/.git"
- } >folrc
+ cgitrc_repo folrc fol enable-follow-links=1 max-commit-count=3
'
test_expect_success 'a followed log reaches the root commit' '
- CGIT_CONFIG="$PWD/folrc" QUERY_STRING="url=fol/log/b.txt&follow=1&ofs=6" cgit >tmp &&
+ cgit_rc folrc "url=fol/log/b.txt&follow=1&ofs=6" >tmp &&
grep ">edit a 2<" tmp &&
grep ">create a<" tmp
'
test_expect_success 'the pager of a followed log keeps the name followed' '
- CGIT_CONFIG="$PWD/folrc" QUERY_STRING="url=fol/log/b.txt&follow=1&ofs=3" cgit >tmp &&
+ cgit_rc folrc "url=fol/log/b.txt&follow=1&ofs=3" >tmp &&
grep ">edit a 3<" tmp &&
grep "href=./fol/log/b.txt?follow=1.>\[prev\]" tmp &&
grep "href=./fol/log/b.txt?ofs=6&amp;follow=1.>\[next\]" tmp
@@ -93,24 +86,30 @@ test_expect_success 'the pager of a followed log keeps the name followed' '
test_expect_success 'a cut subject keeps whole characters' '
git -C repos/foo commit --allow-empty -m "日本語の説明文です日本語の説明文です" &&
- {
- echo "max-message-length=20" &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc
- } >subjectrc &&
- CGIT_CONFIG="$PWD/subjectrc" QUERY_STRING="url=foo/log/" cgit >tmp &&
+ cgitrc_copy subjectrc max-message-length=20 &&
+ cgit_rc subjectrc "url=foo/log/" >tmp &&
grep ">日本語の説\.\.\.</a>" tmp
'
test_expect_success 'the log draws the commit graph when enabled' '
- {
- echo "enable-commit-graph=1" &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc
- } >graphrc &&
- CGIT_CONFIG="$PWD/graphrc" QUERY_STRING="url=foo/log/" cgit >tmp &&
+ cgitrc_copy graphrc enable-commit-graph=1 &&
+ cgit_rc graphrc "url=foo/log/" >tmp &&
grep "class=.commitgraph." tmp &&
grep "<th scope=.col. aria-label=.Graph.></th>" tmp
'
+# A followed log is walked without the graph, so its first column holds the
+# age whatever enable-commit-graph says.
+test_expect_success 'a followed log heads its age column with the graph on' '
+ {
+ echo "enable-commit-graph=1" &&
+ cat folrc
+ } >folgraphrc &&
+ cgit_rc folgraphrc "url=fol/log/b.txt&follow=1" >tmp &&
+ grep "<th class=.left. scope=.col.>Age</th><th class=.left. scope=.col.>Commit message" tmp &&
+ ! grep "aria-label=.Graph." tmp
+'
+
# The search is literal, so text that is no valid regular expression still
# finds the commit holding it.
test_expect_success 'a search for brackets matches them literally' '
@@ -120,4 +119,18 @@ test_expect_success 'a search for brackets matches them literally' '
grep "fix \\[brackets\\] in docs" tmp
'
+# A range is walked in full before the first row, so a missing parent fails
+# the walk while the page can still answer with an error of its own.
+test_expect_success 'a walk that cannot start answers one error page' '
+ git clone -q repos/foo gap &&
+ old=$(git -C gap rev-parse HEAD~4) &&
+ parent=$(git -C gap rev-parse HEAD~2) &&
+ rm gap/.git/objects/$(echo $parent | cut -c1-2)/$(echo $parent | cut -c3-) &&
+ cgitrc_repo gaprc gap "repo.path=$PWD/gap/.git" &&
+ cgit_rc gaprc "url=gap/log/&qt=range&q=$old..master" >tmp 2>err &&
+ test $(grep -c "^Status:" tmp) = 1 &&
+ grep "^Status: 500" tmp &&
+ grep "Unable to read the history" tmp
+'
+
test_done
diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh
index 41134a4..26a0cb4 100755
--- a/tests/t0104-tree.sh
+++ b/tests/t0104-tree.sh
@@ -9,7 +9,7 @@ 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-1' 'grep ">file-1<" tmp'
test_expect_success 'find file-50' 'grep "file-50" tmp'
test_expect_success 'the links column has a name' '
grep "<th class=.ls-links. scope=.col. aria-label=.Links.></th>" tmp
@@ -44,17 +44,11 @@ test_expect_success 'set up a file holding the characters a browser breaks at' '
git add breaks.txt &&
git commit -m breaks
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-blame=1" &&
- echo "repo.url=breaks" &&
- echo "repo.path=$PWD/repos/breaks/.git"
- } >breaksrc
+ cgitrc_repo breaksrc breaks enable-blame=1
'
test_expect_success 'each of them is shown as an escape in the tree' '
- CGIT_CONFIG="$PWD/breaksrc" QUERY_STRING="url=breaks/tree/breaks.txt" cgit >tmp &&
+ cgit_rc breaksrc "url=breaks/tree/breaks.txt" >tmp &&
grep "<code>a<span class=.escape.>\\\\r</span>b<span class=.escape.>\\\\v</span>c" tmp &&
grep "c<span class=.escape.>\\\\f</span>d<span class=.escape.>\\\\u0085</span>e" tmp &&
grep "e<span class=.escape.>\\\\u2028</span>f<span class=.escape.>\\\\u2029</span>g$" tmp
@@ -73,7 +67,7 @@ test_expect_success 'the numbers count only the lines git counts' '
'
test_expect_success 'the blame page escapes them the same way' '
- CGIT_CONFIG="$PWD/breaksrc" QUERY_STRING="url=breaks/blame/breaks.txt" cgit >tmp &&
+ cgit_rc breaksrc "url=breaks/blame/breaks.txt" >tmp &&
grep "<code>a<span class=.escape.>\\\\r</span>b<span class=.escape.>\\\\v</span>c" tmp &&
grep "e<span class=.escape.>\\\\u2028</span>f<span class=.escape.>\\\\u2029</span>g$" tmp &&
grep "^i<span class=.escape.>\\\\r</span></code>" tmp
@@ -105,17 +99,11 @@ test_expect_success 'set up a repo with a nested submodule' '
git update-index --add --cacheinfo 160000,$sub,vendor/sub &&
git commit -m gitlink
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-blame=1" &&
- echo "repo.url=submodules" &&
- echo "repo.path=$PWD/repos/submodules/.git"
- } >submodrc
+ cgitrc_repo submodrc submodules enable-blame=1
'
test_expect_success 'generate submodules/tree/vendor' '
- CGIT_CONFIG="$PWD/submodrc" QUERY_STRING="url=submodules/tree/vendor" cgit >tmp
+ cgit_rc submodrc "url=submodules/tree/vendor" >tmp
'
test_expect_success 'a submodule row shows the entry name alone' '
@@ -134,14 +122,13 @@ test_expect_success 'a submodule row shows no size' '
'
test_expect_success 'a submodule row offers log but not blame or plain' '
- grep "ls-mod" tmp >row &&
grep ">log</a>" row &&
! grep ">blame</a>" row &&
! grep ">plain</a>" row
'
test_expect_success 'a file row still offers blame' '
- CGIT_CONFIG="$PWD/submodrc" QUERY_STRING="url=submodules/tree/" cgit >tmp &&
+ cgit_rc submodrc "url=submodules/tree/" >tmp &&
grep ">blame</a>" tmp
'
@@ -159,13 +146,8 @@ test_expect_success 'only a link target inside the tree is linked' '
git update-index --add --cacheinfo 120000,$abs,abs &&
git commit -m links
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=links" &&
- echo "repo.path=$PWD/repos/links/.git"
- } >linksrc &&
- CGIT_CONFIG="$PWD/linksrc" QUERY_STRING="url=links/tree/" cgit >tmp &&
+ cgitrc_repo linksrc links &&
+ cgit_rc linksrc "url=links/tree/" >tmp &&
grep " -> <a class=.ls-blob. href=./links/tree/target.txt.>target.txt</a>" tmp &&
grep " -> /etc/passwd</td>" tmp &&
! grep "href=./links/tree/etc/passwd" tmp
@@ -187,17 +169,34 @@ test_expect_success 'a long directory chain ends on a link' '
git add . &&
git commit -m deep
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=deep" &&
- echo "repo.path=$PWD/repos/deep/.git"
- } >deeprc &&
- CGIT_CONFIG="$PWD/deeprc" QUERY_STRING="url=deep/tree/" cgit >tmp &&
+ cgitrc_repo deeprc deep &&
+ cgit_rc deeprc "url=deep/tree/" >tmp &&
grep ">d15</a></td>" tmp &&
! grep " / </td>" tmp
'
+# Git holds one object per id, as the type it was first looked up as, so a
+# folder the commit also names as its parent cannot be read as a tree and the
+# chain stops on it. The rest of the row still belongs to the first folder.
+test_expect_success 'a broken chain keeps the row links on the first folder' '
+ test_create_repo repos/cut &&
+ (
+ cd repos/cut &&
+ mkdir -p a/b &&
+ echo x >a/b/file &&
+ git add a &&
+ git commit -m base &&
+ odd=$(printf "tree %s\\nparent %s\\nauthor A <a@x> 1700000000 +0000\\ncommitter A <a@x> 1700000000 +0000\\n\\nodd\\n" \
+ $(git rev-parse HEAD^{tree}) $(git rev-parse HEAD:a/b) |
+ git hash-object -t commit -w --stdin --literally) &&
+ git update-ref refs/heads/odd $odd
+ ) &&
+ cgitrc_repo cutrc cut &&
+ cgit_rc cutrc "url=cut/tree/&h=odd" >tmp &&
+ grep ">b</a></td>" tmp &&
+ grep "href=./cut/log/a?h=odd.>log</a>" tmp
+'
+
test_expect_success 'a binary file in the tree shows as a hex dump' '
test_create_repo repos/bin &&
(
@@ -206,13 +205,8 @@ test_expect_success 'a binary file in the tree shows as a hex dump' '
git add blob.bin &&
git commit -m binary
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=bin" &&
- echo "repo.path=$PWD/repos/bin/.git"
- } >binrc &&
- CGIT_CONFIG="$PWD/binrc" QUERY_STRING="url=bin/tree/blob.bin" cgit >tmp &&
+ cgitrc_repo binrc bin &&
+ cgit_rc binrc "url=bin/tree/blob.bin" >tmp &&
grep "<table class=.bin-blob.>" tmp &&
grep "<td class=.right.>0000</td><td class=.hex.> 00 01 02 62 69 6e" tmp
'
diff --git a/tests/t0105-plain.sh b/tests/t0105-plain.sh
index 4615dc5..6a96b61 100755
--- a/tests/t0105-plain.sh
+++ b/tests/t0105-plain.sh
@@ -65,9 +65,9 @@ test_expect_success 'only text and xml types carry a charset' '
echo "mimetype.svg=image/svg+xml" &&
cat cgitrc
} >typesrc &&
- CGIT_CONFIG="$PWD/typesrc" QUERY_STRING="url=foo/plain/doc.docx" cgit >tmp &&
+ cgit_rc typesrc "url=foo/plain/doc.docx" >tmp &&
sed "/^$/q" tmp | grep "^Content-Type: $docx$" &&
- CGIT_CONFIG="$PWD/typesrc" QUERY_STRING="url=foo/plain/pic.svg" cgit >tmp &&
+ cgit_rc typesrc "url=foo/plain/pic.svg" >tmp &&
sed "/^$/q" tmp | grep "^Content-Type: image/svg+xml; charset=UTF-8$"
'
@@ -83,14 +83,8 @@ test_expect_success 'plain answers once for duplicate tree entries' '
c=$(echo dup | git commit-tree $top) &&
git update-ref refs/heads/master $c
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "mimetype.png=image/png" &&
- echo "repo.url=dup" &&
- echo "repo.path=$PWD/repos/dup/.git"
- } >duprc &&
- CGIT_CONFIG="$PWD/duprc" QUERY_STRING="url=dup/plain/x.png" cgit >tmp &&
+ cgitrc_repo duprc dup mimetype.png=image/png &&
+ cgit_rc duprc "url=dup/plain/x.png" >tmp &&
test $(grep -c "^Status:" tmp) = 1 &&
test $(grep -c "^Content-Type:" tmp) = 1 &&
grep "^Content-Type: image/png" tmp
@@ -106,7 +100,7 @@ test_expect_success 'a control character in a file name stays out of the headers
c=$(echo ctl | git commit-tree $top) &&
git update-ref refs/heads/ctl $c
) &&
- CGIT_CONFIG="$PWD/duprc" QUERY_STRING="url=dup/plain/ctl%01name.txt&h=ctl" cgit >tmp &&
+ cgit_rc duprc "url=dup/plain/ctl%01name.txt&h=ctl" >tmp &&
grep "^Content-Disposition: inline; filename=.ctl?name.txt.$" tmp
'
diff --git a/tests/t0106-commit.sh b/tests/t0106-commit.sh
index 5564038..9a0a7e1 100755
--- a/tests/t0106-commit.sh
+++ b/tests/t0106-commit.sh
@@ -1,8 +1,7 @@
#!/bin/sh
# The commit page. Most checks read the markup for the tree link, the parent
-# link, the subject and the diffstat. The last few use a root commit, which
-# has no parent and diffs against the empty tree.
+# link, the subject and the diffstat.
test_description='Check content on commit page'
. ./setup.sh
@@ -38,8 +37,9 @@ test_expect_success 'a commit that changes no files draws no diffstat table' '
! grep "<table class=.diffstat.>" tmp
'
+# A root commit has no parent, so it diffs against the empty tree.
test_expect_success 'get root commit' '
- root=$(cd repos/foo && git rev-list --reverse HEAD | head -1) &&
+ root=$(git -C repos/foo rev-list --max-parents=0 HEAD) &&
cgit_url "foo/commit&id=$root" >tmp &&
grep "</html>" tmp
'
@@ -66,7 +66,7 @@ test_expect_success 'a message that is not UTF-8 has its stray bytes replaced' '
cid=$(git hash-object --literally -t commit -w raw) &&
git update-ref refs/heads/latin "$cid"
) &&
- cgit_query "url=foo/commit&h=latin" >tmp &&
+ cgit_url "foo/commit&h=latin" >tmp &&
grep "<div class=.commit-subject.>caf? subject<" tmp &&
grep "body ? end" tmp &&
! grep "caf$(printf "\351")" tmp
@@ -78,9 +78,9 @@ test_expect_success 'an octopus merge, which draws no diff, offers no diff optio
cid=$(git commit-tree -p HEAD -p HEAD~1 -p HEAD~2 -m octopus HEAD^{tree}) &&
git update-ref refs/heads/octopus "$cid"
) &&
- cgit_query "url=foo/commit&h=octopus" >tmp &&
+ cgit_url "foo/commit&h=octopus" >tmp &&
! grep "cgit-panel" tmp &&
- cgit_query "url=foo/commit" >tmp &&
+ cgit_url "foo/commit" >tmp &&
grep "cgit-panel" tmp
'
diff --git a/tests/t0107-diff.sh b/tests/t0107-diff.sh
index f058246..e173d00 100755
--- a/tests/t0107-diff.sh
+++ b/tests/t0107-diff.sh
@@ -1,8 +1,8 @@
#!/bin/sh
-# The diff page, unified or side by side under dt=1. The first checks read
+# The diff page, unified or side-by-side under dt=1. The first checks read
# the markup for one added file in the fixture repository. The rest build
-# repositories of their own, shaped for the side by side renderer's two
+# repositories of their own, shaped for the side-by-side renderer's two
# trickiest cases, a hunk covering a single line and a file where every
# line changed.
@@ -22,6 +22,16 @@ test_expect_success 'find added line' '
grep "<div class=.add.>+5</div>" tmp
'
+# A revision that names no commit is quoted the way the request wrote it, as
+# the commit and patch pages quote it, on either side of the diff.
+test_expect_success 'a tree on either side is named as the request gave it' '
+ tree=$(git -C repos/foo rev-parse --short HEAD^{tree}) &&
+ cgit_url "foo/diff/&id=$tree" >tmp &&
+ grep "<div class=.error.>Not a commit: $tree</div>" tmp &&
+ cgit_url "foo/diff/&id2=$tree" >tmp &&
+ grep "<div class=.error.>Not a commit: $tree</div>" tmp
+'
+
# git writes a hunk length only when the hunk spans more than one line, so the
# header for a single line reads "@@ -1 +1 @@" with no comma in it. Requiring
# that comma left those hunks numbered from zero, which dropped the line
@@ -36,24 +46,19 @@ test_expect_success 'set up a repo with single-line hunks' '
printf "ONLY\n" >one.txt &&
git commit -am "change one.txt"
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=hunk1" &&
- echo "repo.path=$PWD/repos/hunk1/.git"
- } >hunk1rc &&
+ cgitrc_repo hunk1rc hunk1 &&
git -C repos/hunk1 diff HEAD~1 HEAD >raw.diff &&
grep "^@@ -1 +1 @@" raw.diff
'
test_expect_success 'ssdiff numbers a comma-less hunk from its real line' '
- CGIT_CONFIG="$PWD/hunk1rc" QUERY_STRING="url=hunk1/diff/&dt=1" cgit >tmp &&
+ cgit_rc hunk1rc "url=hunk1/diff/&dt=1" >tmp &&
grep "#n1.>1</a>" tmp &&
! grep "#n0.>0</a>" tmp
'
test_expect_success 'ssdiff still numbers a hunk that carries a length' '
- cgit_query "url=foo/diff&dt=1" >tmp &&
+ cgit_url "foo/diff&dt=1" >tmp &&
grep "#n1.>1</a>" tmp
'
@@ -75,16 +80,11 @@ test_expect_success 'set up a repo with shrinking changed lines' '
s=s sprintf(\"%c\",c)} print s}}" >lines.txt &&
git commit -am "change every line"
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=lcs" &&
- echo "repo.path=$PWD/repos/lcs/.git"
- } >lcsrc
+ cgitrc_repo lcsrc lcs
'
test_expect_success 'every changed pair gets both a del and an add span' '
- CGIT_CONFIG="$PWD/lcsrc" QUERY_STRING="url=lcs/diff/&dt=1" cgit >tmp &&
+ cgit_rc lcsrc "url=lcs/diff/&dt=1" >tmp &&
deletions=$(grep -o "<span class=.del.>" tmp | wc -l) &&
additions=$(grep -o "<span class=.add.>" tmp | wc -l) &&
test "$deletions" -gt 0 &&
@@ -93,22 +93,15 @@ test_expect_success 'every changed pair gets both a del and an add span' '
test_expect_success 'stripping the highlight leaves every line intact' '
sed -e "s/<[^>]*>//g" tmp >plain.out &&
- git -C repos/lcs show HEAD~1:lines.txt >old.txt &&
- git -C repos/lcs show HEAD:lines.txt >new.txt &&
- while read -r line
- do
- grep -qF "$line" plain.out || {
- echo "missing old line: $line"
- return 1
- }
- done <old.txt &&
+ git -C repos/lcs show HEAD~1:lines.txt >lines &&
+ git -C repos/lcs show HEAD:lines.txt >>lines &&
while read -r line
do
grep -qF "$line" plain.out || {
- echo "missing new line: $line"
+ echo "missing line: $line"
return 1
}
- done <new.txt
+ done <lines
'
test_done
diff --git a/tests/t0108-rawdiff.sh b/tests/t0108-rawdiff.sh
index f68628a..9b4eb37 100755
--- a/tests/t0108-rawdiff.sh
+++ b/tests/t0108-rawdiff.sh
@@ -8,51 +8,48 @@ test_description='Check content on rawdiff page'
. ./setup.sh
test_expect_success 'generate foo/rawdiff' '
- cgit_query "url=foo/rawdiff" >tmp
+ cgit_url "foo/rawdiff" >tmp
'
-# cgit writes its CGI headers and the blank line that ends them ahead of the
-# body while git writes only the diff, so the head of the page comes off
-# before the two are compared.
test_expect_success 'compare with output of git-diff(1)' '
- git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 &&
- strip_headers <tmp >tmp_ &&
- cmp tmp_ tmp2
+ git -C repos/foo diff HEAD^.. >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual
'
test_expect_success 'find initial commit' '
- root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+ root=$(git -C repos/foo rev-list --max-parents=0 HEAD)
'
test_expect_success 'generate diff for initial commit' '
- cgit_query "url=foo/rawdiff&id=$root" >tmp
+ cgit_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 &&
- strip_headers <tmp >tmp_ &&
- cmp tmp_ tmp2
+ git -C repos/foo diff-tree -p --no-commit-id --root "$root" >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual
'
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
+ id=$(git -C repos/foo rev-parse HEAD) &&
+ id2=$(git -C repos/foo rev-parse HEAD~3) &&
+ cgit_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 &&
- strip_headers <tmp >tmp_ &&
- cmp tmp_ tmp2
+ git -C repos/foo diff HEAD~3..HEAD >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual
'
test_expect_success 'a path limits the diff to that file' '
- cgit_query "url=foo/rawdiff/file-4&id=$id&id2=$id2" >tmp &&
- git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD -- file-4 >tmp2 &&
- strip_headers <tmp >tmp_ &&
- cmp tmp_ tmp2 &&
- grep "file-4" tmp_ &&
- ! grep "file-5" tmp_
+ cgit_url "foo/rawdiff/file-4&id=$id&id2=$id2" >tmp &&
+ git -C repos/foo diff HEAD~3..HEAD -- file-4 >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual &&
+ grep "file-4" actual &&
+ ! grep "file-5" actual
'
test_done
diff --git a/tests/t0109-patch.sh b/tests/t0109-patch.sh
index 6840f0c..bc942fe 100755
--- a/tests/t0109-patch.sh
+++ b/tests/t0109-patch.sh
@@ -2,8 +2,7 @@
# The patch page serves mail ready patches, compared line for line against
# git format-patch for a single commit and for a range, once the CGI headers
-# are stripped. The last check sets max-patch-count so a range wider than
-# the limit comes back cut short.
+# are stripped.
test_description='Check content on patch page'
. ./setup.sh
@@ -19,38 +18,22 @@ check_cgit_signature() {
}
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' '
- check_cgit_signature
+ cgit_url "foo/patch" >tmp
'
test_expect_success 'compare with output of git-format-patch(1)' '
- git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" \
- --signature="cgit" --stdout HEAD^ >tmp2 &&
- strip_headers <tmp >tmp_ &&
- test_cmp tmp_ tmp2
+ git -C repos/foo format-patch --subject-prefix="" \
+ --signature="cgit" --stdout HEAD^ >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual
'
test_expect_success 'find initial commit' '
- root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD)
+ root=$(git -C repos/foo rev-list --max-parents=0 HEAD)
'
test_expect_success 'generate patch for initial commit' '
- cgit_query "url=foo/patch&id=$root" >tmp
+ cgit_url "foo/patch&id=$root" >tmp
'
test_expect_success 'find `cgit` signature on the initial commit' '
@@ -58,20 +41,16 @@ test_expect_success 'find `cgit` signature on the initial commit' '
'
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 on the range' '
- check_cgit_signature
+ id=$(git -C repos/foo rev-parse HEAD) &&
+ id2=$(git -C repos/foo rev-parse HEAD~3) &&
+ cgit_url "foo/patch&id=$id&id2=$id2" >tmp
'
test_expect_success 'compare the range with git-format-patch(1)' '
- git --git-dir="$PWD/repos/foo/.git" format-patch --subject-prefix="" \
- -N --signature="cgit" --stdout HEAD~3..HEAD >tmp2 &&
- strip_headers <tmp >tmp_ &&
- test_cmp tmp_ tmp2
+ git -C repos/foo format-patch --subject-prefix="" \
+ -N --signature="cgit" --stdout HEAD~3..HEAD >expect &&
+ strip_headers <tmp >actual &&
+ test_cmp expect actual
'
test_expect_success 'max-patch-count bounds a patch range' '
@@ -81,18 +60,13 @@ test_expect_success 'max-patch-count bounds a patch range' '
echo "repo.url=foo" &&
echo "repo.path=$PWD/repos/foo/.git"
} >patchcountrc &&
- id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
- root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) &&
- CGIT_CONFIG="$PWD/patchcountrc" QUERY_STRING="url=foo/patch&id=$id&id2=$root" cgit >tmp &&
+ cgit_rc patchcountrc "url=foo/patch&id=$id&id2=$root" >tmp &&
test $(grep -c "^From " tmp) -eq 2
'
test_expect_success 'the signature names the version when asked to' '
- {
- echo "enable-version-info=1" &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc
- } >versionrc &&
- CGIT_CONFIG="$PWD/versionrc" QUERY_STRING="url=foo/patch" cgit >tmp &&
+ cgitrc_copy versionrc enable-version-info=1 &&
+ cgit_rc versionrc "url=foo/patch" >tmp &&
check_cgit_signature "$CGIT_VERSION"
'
diff --git a/tests/t0110-snapshot.sh b/tests/t0110-snapshot.sh
index bec0e3d..faebf5e 100755
--- a/tests/t0110-snapshot.sh
+++ b/tests/t0110-snapshot.sh
@@ -1,38 +1,43 @@
#!/bin/sh
# The snapshot page hands a branch back as an archive. One function below
-# registers the same checks for every tar based format, and each format
+# registers the same checks for every compressed tar format, and each format
# needs the same compressor cgit pipes through on the server, so one missing
-# tool skips that format on both grounds at once. zip follows at the end,
-# since unzip shares no flags with the rest.
+# tool skips that format on both grounds at once. Plain tar and zip get checks
+# of their own, since cgit runs no compressor for either and unzip shares no
+# flags with the rest.
test_description='Verify snapshot'
. ./setup.sh
-# Registers the checks for one tar based format. Takes the archive suffix,
-# the content type the response must carry, the compressor to unpack with and
-# the name of the prerequisite guarding the checks.
+check_snapshot_headers() {
+ head -n 1 tmp | grep "Status: 200 OK" &&
+ head -n 2 tmp | grep "^Content-Type: $2$" &&
+ head -n 3 tmp | grep "Content-Disposition: inline; filename=.master.$1."
+}
+
+check_snapshot_content() {
+ ls master/ >output &&
+ test_line_count = 5 output &&
+ grep "^5$" master/file-5 &&
+ test_line_count = 1 master/file-5
+}
+
+# Registers the checks for one compressed tar format.
test_tar_snapshot() {
ext=$1
type=$2
tool=$3
prereq=$4
- if command -v "$tool" >/dev/null 2>&1
- then
- test_set_prereq $prereq
- else
- say "Skipping $ext validation tests: $tool not found"
- fi
+ test_lazy_prereq $prereq "command -v $tool"
test_expect_success $prereq "get foo/snapshot/master.$ext" "
cgit_url 'foo/snapshot/master.$ext' >tmp
"
test_expect_success $prereq "check $ext response headers" "
- head -n 1 tmp | grep 'Status: 200 OK' &&
- head -n 2 tmp | grep '^Content-Type: $type\$' &&
- head -n 3 tmp | grep 'Content-Disposition: inline; filename=.master.$ext.'
+ check_snapshot_headers $ext $type
"
test_expect_success $prereq "verify $ext format" "
@@ -43,10 +48,7 @@ test_tar_snapshot() {
test_expect_success $prereq "untar $ext and verify the content" "
rm -rf master &&
'$tool' -dc 'master.$ext' | tar -xf - &&
- ls master/ >output &&
- test_line_count = 5 output &&
- grep '^5\$' master/file-5 &&
- test_line_count = 1 master/file-5
+ check_snapshot_content
"
}
@@ -57,19 +59,14 @@ test_expect_success 'get foo/snapshot/master.tar' '
'
test_expect_success 'check tar response headers' '
- head -n 1 tmp | grep "Status: 200 OK" &&
- head -n 2 tmp | grep "^Content-Type: application/x-tar$" &&
- head -n 3 tmp | grep "Content-Disposition: inline; filename=.master.tar."
+ check_snapshot_headers tar application/x-tar
'
test_expect_success 'untar and verify the content' '
strip_headers <tmp >master.tar &&
rm -rf master &&
tar -xf master.tar &&
- ls master/ >output &&
- test_line_count = 5 output &&
- grep "^5$" master/file-5 &&
- test_line_count = 1 master/file-5
+ check_snapshot_content
'
test_tar_snapshot tar.gz application/gzip gzip GZIP
@@ -78,21 +75,12 @@ test_tar_snapshot tar.lz application/x-lzip lzip LZIP
test_tar_snapshot tar.xz application/x-xz xz XZ
test_tar_snapshot tar.zst application/zstd zstd ZSTD
-if command -v unzip >/dev/null 2>&1
-then
- test_set_prereq UNZIP
-else
- say 'Skipping zip validation tests: unzip not found'
-fi
-
test_expect_success 'get foo/snapshot/master.zip' '
cgit_url "foo/snapshot/master.zip" >tmp
'
test_expect_success 'check zip response headers' '
- head -n 1 tmp | grep "Status: 200 OK" &&
- head -n 2 tmp | grep "^Content-Type: application/zip$" &&
- head -n 3 tmp | grep "Content-Disposition: inline; filename=.master.zip."
+ check_snapshot_headers zip application/zip
'
test_expect_success 'strip off the header lines' '
@@ -100,16 +88,13 @@ test_expect_success 'strip off the header lines' '
'
test_expect_success UNZIP 'verify zip format' '
- unzip -t master.zip
+ "$GIT_UNZIP" -t master.zip
'
test_expect_success UNZIP 'unzip and verify the content' '
rm -rf master &&
- unzip master.zip &&
- ls master/ >output &&
- test_line_count = 5 output &&
- grep "^5$" master/file-5 &&
- test_line_count = 1 master/file-5
+ "$GIT_UNZIP" master.zip &&
+ check_snapshot_content
'
# A tag named v1.0 sheds its v in the snapshot name unless dropping it would
@@ -120,17 +105,11 @@ test_expect_success UNZIP 'unzip and verify the content' '
test_expect_success 'set up a repo with a versioned tag' '
mkrepo repos/vtags 2 &&
git -C repos/vtags update-ref refs/tags/v1.0 HEAD &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "snapshots=tar.gz" &&
- echo "repo.url=vtags" &&
- echo "repo.path=$PWD/repos/vtags/.git"
- } >vtagsrc
+ cgitrc_repo vtagsrc vtags snapshots=tar.gz
'
test_expect_success 'a loose v tag still sheds its v in the snapshot name' '
- CGIT_CONFIG="$PWD/vtagsrc" QUERY_STRING="url=vtags/refs/" cgit >tmp &&
+ cgit_rc vtagsrc "url=vtags/refs/" >tmp &&
grep "vtags-1.0.tar.gz" tmp &&
! grep "vtags-v1.0.tar.gz" tmp
'
@@ -140,7 +119,7 @@ test_expect_success 'a loose v tag still sheds its v in the snapshot name' '
test_expect_success 'a real case collision keeps the v on both tags' '
git -C repos/vtags pack-refs --all &&
git -C repos/vtags update-ref refs/tags/V1.0 HEAD~1 &&
- CGIT_CONFIG="$PWD/vtagsrc" QUERY_STRING="url=vtags/refs/" cgit >tmp &&
+ cgit_rc vtagsrc "url=vtags/refs/" >tmp &&
grep "vtags-v1.0.tar.gz" tmp &&
grep "vtags-V1.0.tar.gz" tmp &&
! grep "vtags-1.0.tar.gz" tmp
@@ -151,7 +130,7 @@ test_expect_success 'a real case collision keeps the v on both tags' '
test_expect_success 'a snapshot name with nothing before its suffix is refused' '
git -C repos/vtags branch v HEAD &&
sed "s/^snapshots=tar.gz$/snapshots=tar/" vtagsrc >vtagstarrc &&
- CGIT_CONFIG="$PWD/vtagstarrc" QUERY_STRING="url=vtags/snapshot/.tar" cgit >tmp &&
+ cgit_rc vtagstarrc "url=vtags/snapshot/.tar" >tmp &&
grep "^Status: 404" tmp
'
@@ -160,17 +139,47 @@ test_expect_success 'an empty snapshot-prefix puts no slash ahead of the members
cat vtagstarrc &&
echo "repo.snapshot-prefix="
} >noprefixrc &&
- CGIT_CONFIG="$PWD/noprefixrc" QUERY_STRING="url=vtags/snapshot/vtags.tar&id=master" cgit >tmp &&
+ cgit_rc noprefixrc "url=vtags/snapshot/vtags.tar&id=master" >tmp &&
strip_headers <tmp >noprefix.tar &&
tar -tf noprefix.tar >members &&
grep "^file-1$" members &&
! grep "^/" members
'
+# A snapshot is named after the last component of the url once its .git
+# suffix is gone, so a url of .git alone leaves the ref as the whole name.
+test_expect_success 'set up repositories whose urls end in .git' '
+ mkrepo repos/dotgit 1 &&
+ git -C repos/dotgit tag v1.0 &&
+ {
+ echo "virtual-root=/" &&
+ echo "cache-size=0" &&
+ echo "snapshots=tar" &&
+ echo "repo.url=.git" &&
+ echo "repo.path=$PWD/repos/dotgit/.git" &&
+ echo "repo.url=sub/.git" &&
+ echo "repo.path=$PWD/repos/dotgit/.git"
+ } >dotgitrc
+'
+
+test_expect_success 'a url of .git names a snapshot after the ref alone' '
+ cgit_rc dotgitrc "url=.git/refs/" >tmp &&
+ grep ">v1.0.tar<" tmp &&
+ cgit_rc dotgitrc "url=.git/snapshot/v1.0.tar" >tmp &&
+ strip_headers <tmp >dotgit.tar &&
+ tar -tf dotgit.tar >members &&
+ grep "^v1.0/file-1$" members
+'
+
+test_expect_success 'a url ending in /.git names a snapshot after its directory' '
+ cgit_rc dotgitrc "url=sub/.git/refs/" >tmp &&
+ grep ">sub-1.0.tar<" tmp
+'
+
# A server may run cgit with no PATH at all, and the compressor is then looked
# for on the default path execvp searches.
test_expect_success GZIP 'a compressed snapshot is made without a PATH' '
- sed "s/^cache-size=.*/cache-size=0/" cgitrc >nopathrc &&
+ cgitrc_copy nopathrc &&
cgit_bin=$(command -v cgit) &&
(
unset PATH &&
@@ -183,9 +192,9 @@ test_expect_success GZIP 'a compressed snapshot is made without a PATH' '
# A link offers the snapshot of a tag, and the name on it has to lead back to
# that tag even when a branch is spelled like the tag's stem.
-test_expect_success 'a snapshot name leads to the tag ahead of a branch' '
+test_expect_success GZIP 'a snapshot name leads to the tag ahead of a branch' '
git -C repos/vtags update-ref refs/heads/1.0 $(git -C repos/vtags commit-tree -m shadow HEAD^{tree}) &&
- CGIT_CONFIG="$PWD/vtagsrc" QUERY_STRING="url=vtags/snapshot/vtags-1.0.tar.gz" cgit >tmp &&
+ cgit_rc vtagsrc "url=vtags/snapshot/vtags-1.0.tar.gz" >tmp &&
strip_headers <tmp >tag.tar.gz &&
test "$(gzip -dc tag.tar.gz | git -C repos/vtags get-tar-commit-id)" = "$(git -C repos/vtags rev-parse refs/tags/v1.0^{commit})"
'
@@ -198,7 +207,7 @@ test_expect_success 'a signature past max-blob-size is refused' '
echo "max-blob-size=1" &&
cat vtagsrc
} >sigrc &&
- CGIT_CONFIG="$PWD/sigrc" QUERY_STRING="url=vtags/snapshot/vtags-1.0.tar.gz.asc" cgit >tmp &&
+ cgit_rc sigrc "url=vtags/snapshot/vtags-1.0.tar.gz.asc" >tmp &&
grep "^Status: 413" tmp
'
@@ -213,14 +222,8 @@ test_expect_success 'a snapshot and its signature are linked as one unit' '
sig=$(echo signature | git hash-object -w --stdin) &&
git notes --ref signatures/tar.gz add -C $sig one
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "snapshots=tar.gz" &&
- echo "repo.url=signed" &&
- echo "repo.path=$PWD/repos/signed/.git"
- } >signedrc &&
- CGIT_CONFIG="$PWD/signedrc" QUERY_STRING="url=signed/refs/" cgit >tmp &&
+ cgitrc_repo signedrc signed snapshots=tar.gz &&
+ cgit_rc signedrc "url=signed/refs/" >tmp &&
grep "<span class=.snapshot.><a href=./signed/snapshot/signed-one.tar.gz.>signed-one.tar.gz</a> (<a href=./signed/snapshot/signed-one.tar.gz.asc.>sig</a>)</span>" tmp
'
diff --git a/tests/t0111-atom.sh b/tests/t0111-atom.sh
index 48448ef..608ab78 100755
--- a/tests/t0111-atom.sh
+++ b/tests/t0111-atom.sh
@@ -34,24 +34,13 @@ test_expect_success 'ten entries is the default ceiling' '
'
test_expect_success 'max-atom-items overrides the ceiling' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-atom-items=3" &&
- echo "repo.url=bar" &&
- echo "repo.path=$PWD/repos/bar/.git"
- } >atomrc &&
- CGIT_CONFIG="$PWD/atomrc" QUERY_STRING="url=bar/atom" cgit >tmp &&
+ cgitrc_repo atomrc bar max-atom-items=3 &&
+ cgit_rc atomrc "url=bar/atom" >tmp &&
strip_headers <tmp >feed &&
test $(grep -c "<entry>" feed) -eq 3
'
-if command -v xmllint >/dev/null 2>&1
-then
- test_set_prereq XMLLINT
-else
- say 'Skipping well-formedness check: xmllint not found'
-fi
+test_lazy_prereq XMLLINT 'command -v xmllint'
test_expect_success XMLLINT 'the feed is well-formed xml' '
cgit_url "foo/atom" >tmp &&
@@ -68,4 +57,10 @@ test_expect_success 'a path with pathspec magic is refused' '
grep "^Status: 200" tmp
'
+test_expect_success 'the feed link encodes the repository and the branch' '
+ cgitrc_copy feedrc &&
+ CGIT_CONFIG="$PWD/feedrc" HTTP_HOST=example.org QUERY_STRING="url=foo%2Bbar/log/&h=1%2B2" cgit >tmp &&
+ grep "title=.Atom feed. href=.http://example.org/foo%2Bbar/atom/?h=1%2B2." tmp
+'
+
test_done
diff --git a/tests/t0112-stats.sh b/tests/t0112-stats.sh
index 7b08a6b..722a61c 100755
--- a/tests/t0112-stats.sh
+++ b/tests/t0112-stats.sh
@@ -28,50 +28,32 @@ test_expect_success 'set up a stats config' '
git commit --allow-empty -m fresh &&
git commit-graph write --reachable
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-stats=year" &&
- echo "repo.url=stat" &&
- echo "repo.path=$PWD/repos/pulse/.git"
- } >statsrc &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-stats=month" &&
- echo "repo.url=stat" &&
- echo "repo.path=$PWD/repos/pulse/.git"
- } >statmaxrc
+ cgitrc_repo statsrc stat max-stats=year "repo.path=$PWD/repos/pulse/.git" &&
+ cgitrc_repo statmaxrc stat max-stats=month "repo.path=$PWD/repos/pulse/.git"
'
-statq() { CGIT_CONFIG="$PWD/statsrc" QUERY_STRING="$1" cgit; }
-
test_expect_success 'stats tab appears when enabled' '
- statq "url=stat/" >tmp &&
+ cgit_rc statsrc "url=stat/" >tmp &&
grep ">stats</a>" tmp
'
test_expect_success 'stats page renders the authors table' '
- statq "url=stat/stats/" >tmp &&
+ cgit_rc statsrc "url=stat/stats/" >tmp &&
grep "<h2>Commits per author" tmp
'
-test_expect_success 'no language breakdown is rendered' '
- ! grep -i "language" tmp
-'
-
test_expect_success 'stat options sit above the content' '
tr -d "\n" <tmp | sed "s/<h2>Commits per author.*//" | grep "cgit-panel"
'
test_expect_success 'stats below a path stay a normal page' '
- statq "url=stat/stats/file-1" >tmp &&
+ cgit_rc statsrc "url=stat/stats/file-1" >tmp &&
grep "Status: 200" tmp &&
grep "<h2>Commits per author" tmp
'
test_expect_success 'max-stats=year offers all four periods' '
- statq "url=stat/stats/" >tmp &&
+ cgit_rc statsrc "url=stat/stats/" >tmp &&
grep "<option value=.w." tmp &&
grep "<option value=.m." tmp &&
grep "<option value=.q." tmp &&
@@ -79,21 +61,52 @@ test_expect_success 'max-stats=year offers all four periods' '
'
test_expect_success 'max-stats bounds the selectable periods' '
- CGIT_CONFIG="$PWD/statmaxrc" QUERY_STRING="url=stat/stats/&period=y" cgit >tmp &&
+ cgit_rc statmaxrc "url=stat/stats/&period=y" >tmp &&
grep "Statistics type disabled" tmp &&
- CGIT_CONFIG="$PWD/statmaxrc" QUERY_STRING="url=stat/stats/" cgit >tmp &&
+ cgit_rc statmaxrc "url=stat/stats/" >tmp &&
! grep "<option value=.q." tmp
'
# Each period buckets commits and labels its columns its own way, so every one
# gets a page of its own here.
test_expect_success 'every period renders its own columns' '
- statq "url=stat/stats/&period=m" >tmp &&
+ cgit_rc statsrc "url=stat/stats/&period=m" >tmp &&
grep -E "<th[^>]*>[A-Z][a-z][a-z] [0-9]{4}</th>" tmp &&
- statq "url=stat/stats/&period=q" >tmp &&
+ cgit_rc statsrc "url=stat/stats/&period=q" >tmp &&
grep -E "<th[^>]*>Q[1-4] [0-9]{4}</th>" tmp &&
- statq "url=stat/stats/&period=y" >tmp &&
+ cgit_rc statsrc "url=stat/stats/&period=y" >tmp &&
grep -E "<th[^>]*>[0-9]{4}</th>" tmp
'
+# The yearly window opens on the first of January three years back. cgit reads
+# the real clock, so the two commits sit either side of midnight on that day.
+test_expect_success 'the window opens at midnight on its first day' '
+ first=$(($(date -u +%Y) - 3)) &&
+ test_create_repo repos/edge &&
+ (
+ cd repos/edge &&
+ GIT_AUTHOR_NAME="Too Early" \
+ GIT_AUTHOR_DATE="$((first - 1))-12-31 23:59:59 +0000" \
+ GIT_COMMITTER_DATE="$((first - 1))-12-31 23:59:59 +0000" \
+ git commit --allow-empty -m before &&
+ GIT_AUTHOR_NAME="Early Bird" \
+ GIT_AUTHOR_DATE="$first-01-01 00:00:00 +0000" \
+ GIT_COMMITTER_DATE="$first-01-01 00:00:00 +0000" \
+ git commit --allow-empty -m first
+ ) &&
+ cgitrc_repo edgerc edge max-stats=year &&
+ cgit_rc edgerc "url=edge/stats/&period=y" >tmp &&
+ grep ">Early Bird<" tmp &&
+ ! grep ">Too Early<" tmp
+'
+
+# A path starting with a colon would reach git as pathspec magic, which the
+# log, the feed and the patch page refuse as well.
+test_expect_success 'a path with pathspec magic is refused' '
+ cgit_rc statsrc "url=stat/stats/%3A%28icase%29FILE-1" >tmp &&
+ grep "^Status: 400" tmp &&
+ cgit_rc statsrc "url=stat/stats/file-1" >tmp &&
+ grep "^Status: 200" tmp
+'
+
test_done
diff --git a/tests/t0113-tag.sh b/tests/t0113-tag.sh
index 2043385..8aea27e 100755
--- a/tests/t0113-tag.sh
+++ b/tests/t0113-tag.sh
@@ -54,9 +54,7 @@ test_expect_success 'a tag of anything but a commit offers no download' '
! grep ">download<" tmp &&
cgit_url "foo/tag/&h=blob-tag" >tmp &&
grep "tagged object" tmp &&
- ! grep ">download<" tmp &&
- cgit_url "foo/tag/&h=v1.0" >tmp &&
- grep ">download<" tmp
+ ! grep ">download<" tmp
'
# git reads the signature off the end of a tag's message, so a block shaped
@@ -82,8 +80,7 @@ test_expect_success 'a signed tag shows its signature in the message' '
) &&
cgit_url "foo/tag/&h=signed" >tmp &&
tr -d "\n" <tmp >flat &&
- grep "<div class=.commit-msg.>Notes.-----BEGIN PGP SIGNATURE-----iQEzBAABCAAdFiEE-----END PGP SIGNATURE-----</div>" flat &&
- ! grep "<details" tmp
+ grep "<div class=.commit-msg.>Notes.-----BEGIN PGP SIGNATURE-----iQEzBAABCAAdFiEE-----END PGP SIGNATURE-----</div>" flat
'
test_expect_success 'a tag page names the tag in its title' '
diff --git a/tests/t0201-filters.sh b/tests/t0201-filters.sh
index 6d33803..8d8743a 100755
--- a/tests/t0201-filters.sh
+++ b/tests/t0201-filters.sh
@@ -1,8 +1,8 @@
#!/bin/sh
# Checks the source, about, commit and email filters, which hand a piece of
-# page content to an outside program before it is written out. The whole
-# body runs once per kind of filter, the exec kind always and the lua kind
+# page content to an outside program before it is written out. The checks in
+# the loop run once per kind of filter, the exec kind always and the lua kind
# only where cgit was built with lua, because both have to produce the same
# page from the same repository.
@@ -21,8 +21,14 @@ do
cgit_url 'filter-$prefix/tree/a%2bb' >tmp
"
+ # Page output is buffered and has to be flushed as stdout is handed
+ # to a filter and back. A missed flush reorders the page rather than
+ # losing any of it, so the test below watches which side of the
+ # filtered text the surrounding markup lands on.
test_expect_success "check whether the $prefix source filter works" '
- grep "<code>a+b HELLO$" tmp
+ grep "<code>a+b HELLO$" tmp &&
+ tr -d "\n" <tmp >flat.out &&
+ grep "<code>a+b HELLO</code>" flat.out
'
test_expect_success "generate filter-$prefix/about/" "
@@ -48,27 +54,11 @@ do
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
'
-
- # Page output is buffered and has to be flushed as stdout is handed
- # to a filter and back. A missed flush reorders the page rather than
- # losing any of it, so the two tests below watch which side of the
- # filtered text the surrounding markup lands on.
- test_expect_success "the $prefix source filter output stays inside its cell" "
- cgit_url 'filter-$prefix/tree/a%2bb' >tmp &&
- tr -d '\n' <tmp >flat.out &&
- grep '<code>a+b HELLO</code>' flat.out
- "
-
- test_expect_success "the $prefix about filter output stays inside its div" "
- cgit_url 'filter-$prefix/about/' >tmp &&
- grep \"<div id='about'>a+b /filter-$prefix/tree/ HELLO\" tmp
- "
done
test_expect_success 'the blame page goes through the source filter too' '
- echo "enable-blame=1" >blamerc &&
- sed "s/^cache-size=.*/cache-size=0/" cgitrc >>blamerc &&
- CGIT_CONFIG="$PWD/blamerc" QUERY_STRING="url=filter-exec/blame/a%2bb" cgit >tmp &&
+ cgitrc_copy blamerc enable-blame=1 &&
+ cgit_rc blamerc "url=filter-exec/blame/a%2bb" >tmp &&
grep "<td class=.linenumbers.>" tmp &&
grep "<code>a+b HELLO$" tmp
'
@@ -76,22 +66,14 @@ test_expect_success 'the blame page goes through the source filter too' '
# A readme read at another branch links into the tree at that branch, which
# the filter is told as the query to follow a path with.
test_expect_success 'the about filter gets the tree url at the readme branch' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "readme=:a+b" &&
- echo "about-filter=exec:$FILTER_DIRECTORY/dump.sh" &&
- echo "root-readme=$PWD/root.txt" &&
- echo "repo.url=filtered" &&
- echo "repo.path=$PWD/repos/filter/.git"
- } >aboutargsrc &&
- CGIT_CONFIG="$PWD/aboutargsrc" QUERY_STRING="url=filtered/about/&h=1%2b2" cgit >tmp &&
+ cgitrc_repo aboutargsrc filtered readme=:a+b "about-filter=exec:$FILTER_DIRECTORY/dump.sh" "root-readme=$PWD/root.txt" "repo.path=$PWD/repos/filter/.git" &&
+ cgit_rc aboutargsrc "url=filtered/about/&h=1%2b2" >tmp &&
grep "<div id=.about.>a+b /filtered/tree/ ?h=1%2B2 HELLO" tmp
'
test_expect_success 'the site readme leaves the tree url empty' '
echo site >root.txt &&
- CGIT_CONFIG="$PWD/aboutargsrc" QUERY_STRING="p=about" cgit >tmp &&
+ cgit_rc aboutargsrc "p=about" >tmp &&
grep "root.txt SITE" tmp
'
@@ -101,15 +83,12 @@ test_expect_success 'only a repository page hands filters its variables' '
echo "repo url: $CGIT_REPO_URL."
EOF
sed "s|^about-filter=.*|about-filter=exec:$PWD/env.sh|" aboutargsrc >envrc &&
- CGIT_CONFIG="$PWD/envrc" QUERY_STRING="url=filtered/about/" cgit >tmp &&
+ cgit_rc envrc "url=filtered/about/" >tmp &&
grep "repo url: filtered\." tmp &&
- CGIT_CONFIG="$PWD/envrc" QUERY_STRING="p=about" cgit >tmp &&
+ cgit_rc envrc "p=about" >tmp &&
grep "repo url: \." tmp
'
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
-test "$CGIT_HAS_LUA" -eq 1 || say 'cgit built without lua, error page check skipped'
-
# A die inside a lua filter has to reach the visitor as an error page rather
# than being fed back into the filter that just failed, and the reason goes to
# the log.
@@ -125,7 +104,7 @@ test_expect_success CGIT_LUA 'a failing lua filter still renders an error page'
echo "repo.path=$PWD/repos/foo/.git" &&
echo "auth-filter=lua:$PWD/broken.lua"
} >brokenrc &&
- CGIT_CONFIG="$PWD/brokenrc" QUERY_STRING="url=foo/commit" cgit >tmp 2>err &&
+ cgit_rc brokenrc "url=foo/commit" >tmp 2>err &&
grep "Status: 500" tmp &&
grep "Lua error in" err
'
@@ -138,7 +117,7 @@ test_expect_success CGIT_LUA 'a lua filter raising a table is named by type' '
function filter_close() return 0 end
EOF
sed "s|broken.lua|tabled.lua|" brokenrc >tabledrc &&
- CGIT_CONFIG="$PWD/tabledrc" QUERY_STRING="url=foo/commit" cgit >tmp 2>err &&
+ cgit_rc tabledrc "url=foo/commit" >tmp 2>err &&
grep "Status: 500" tmp &&
grep "Lua error in .*tabled.lua: (error object is a table value)" err
'
@@ -158,7 +137,7 @@ test_expect_success CGIT_LUA 'a lua filter that writes while loading still rende
echo "repo.path=$PWD/repos/foo/.git" &&
echo "repo.commit-filter=lua:$PWD/loud.lua"
} >loudrc &&
- CGIT_CONFIG="$PWD/loudrc" QUERY_STRING="url=foo/commit" cgit >tmp &&
+ cgit_rc loudrc "url=foo/commit" >tmp &&
grep "written while loading" tmp &&
grep "</html>" tmp
'
@@ -184,13 +163,8 @@ test_expect_success CGIT_LUA 'a lua filter can encode an argument and include a
end
function filter_close() return 0 end
EOF
- {
- echo "cache-size=0" &&
- echo "repo.url=foo" &&
- echo "repo.path=$PWD/repos/foo/.git" &&
- echo "repo.commit-filter=lua:$PWD/apis.lua"
- } >apisrc &&
- CGIT_CONFIG="$PWD/apisrc" QUERY_STRING="url=foo/commit" cgit >tmp &&
+ sed "s|loud.lua|apis.lua|" loudrc >apisrc &&
+ cgit_rc apisrc "url=foo/commit" >tmp &&
grep "a+b%26c|included <b>bold</b>" tmp
'
diff --git a/tests/t0202-submodule-links.sh b/tests/t0202-submodule-links.sh
index 6db164f..a5810a0 100755
--- a/tests/t0202-submodule-links.sh
+++ b/tests/t0202-submodule-links.sh
@@ -11,9 +11,9 @@ test_description='Check submodule links derived from .gitmodules'
test_expect_success 'set up a parent repo with submodules' '
mkrepo repos/subtarget 1 &&
mkrepo repos/subparent 1 &&
+ sub=$(git -C repos/subparent rev-parse HEAD) &&
(
cd repos/subparent &&
- sub=$(git rev-parse HEAD) &&
git update-index --add --cacheinfo 160000,$sub,local-ssh &&
git update-index --add --cacheinfo 160000,$sub,sibling &&
git update-index --add --cacheinfo 160000,$sub,forge-https &&
@@ -55,21 +55,18 @@ test_expect_success 'set up a parent repo with submodules' '
'
test_expect_success 'generate subparent/tree' '
- CGIT_CONFIG="$PWD/subrc" QUERY_STRING="url=subparent/tree/" cgit >tmp
+ cgit_rc subrc "url=subparent/tree/" >tmp
'
test_expect_success 'an ssh url served here links to the local repo' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=./subtarget/tree/?id=$sub.>local-ssh</a>" tmp
'
test_expect_success 'its hash links to the local commit page' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=./subtarget/commit/?id=$sub." tmp
'
test_expect_success 'a relative url resolves against this repository' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=./subtarget/tree/?id=$sub.>sibling</a>" tmp
'
@@ -78,18 +75,15 @@ test_expect_success 'a web url is linked as it is' '
'
test_expect_success 'a known forge hash links to its commit page' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=.https://github.com/example/project/commit/$sub." tmp
'
test_expect_success 'an ssh url to a known forge is rewritten' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=.https://github.com/example/other.>forge-ssh</a>" tmp &&
grep "href=.https://github.com/example/other/commit/$sub." tmp
'
test_expect_success 'a cgit host keeps its .git suffix in the commit link' '
- sub=$(git -C repos/subparent rev-parse HEAD~1) &&
grep "href=.https://git.kernel.org/pub/scm/git/git.git/commit/?id=$sub." tmp
'
@@ -99,7 +93,7 @@ test_expect_success 'an unresolvable url stays unlinked with a tooltip' '
test_expect_success 'the links stay off without the flag' '
sed "/enable-gitmodules-links/d" subrc >subrcoff &&
- CGIT_CONFIG="$PWD/subrcoff" QUERY_STRING="url=subparent/tree/" cgit >tmp &&
+ cgit_rc subrcoff "url=subparent/tree/" >tmp &&
! grep "subtarget/tree" tmp &&
grep "class=.ls-mod.>local-ssh</span>" tmp
'
diff --git a/tests/t0203-dates.sh b/tests/t0203-dates.sh
index 3c8ecab..5c3567f 100755
--- a/tests/t0203-dates.sh
+++ b/tests/t0203-dates.sh
@@ -24,8 +24,6 @@ test_expect_success 'set up a dated repo' '
)
'
-# Writes the config afresh, one extra line per argument, which is what lets a
-# test below name a setting or two and say nothing about the rest.
daterc() {
{
echo "virtual-root=/"
@@ -36,66 +34,64 @@ daterc() {
} >daterc
}
-dateq() { CGIT_CONFIG="$PWD/daterc" QUERY_STRING="$1" cgit; }
-
test_expect_success 'relative dates are on by default' '
daterc &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep "class=.age-" tmp
'
test_expect_success 'enable-relative-dates=0 shows a calendar date' '
daterc "enable-relative-dates=0" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
! grep "class=.age-" tmp &&
grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp
'
test_expect_success 'the index and log columns follow the same flag' '
daterc "enable-relative-dates=0" &&
- dateq "" >tmp &&
+ cgit_rc daterc "" >tmp &&
! grep "class=.age-" tmp &&
- dateq "url=dated/log/" >tmp &&
+ cgit_rc daterc "url=dated/log/" >tmp &&
! grep "class=.age-" tmp
'
test_expect_success 'date-format selects the calendar format' '
daterc "enable-relative-dates=0" "date-format=iso8601" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep ">[0-9-]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +0000<" tmp
'
test_expect_success 'date-format accepts a strftime format' '
daterc "enable-relative-dates=0" "date-format=format:%Y/%m/%d" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep ">[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]<" tmp
'
test_expect_success 'an unknown date-format falls back to short' '
daterc "enable-relative-dates=0" "date-format=nonsense" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp &&
daterc "enable-relative-dates=0" "date-format=format:" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp
'
test_expect_success 'a date-format git would refuse falls back to short' '
daterc "enable-relative-dates=0" "date-format=local-local" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep "^Status: 200" tmp &&
grep "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]<" tmp
'
test_expect_success 'date-format=local is the default format in local time' '
daterc "enable-relative-dates=0" "date-format=local" &&
- dateq "url=dated/refs/" >tmp &&
+ cgit_rc daterc "url=dated/refs/" >tmp &&
grep ">[A-Z][a-z][a-z] [A-Z][a-z][a-z] [0-9]* [0-9:]* [0-9]*<" tmp
'
test_expect_success 'date-format still formats the log fallback' '
daterc "date-format=rfc2822" &&
- dateq "url=dated/log/" >tmp &&
+ cgit_rc daterc "url=dated/log/" >tmp &&
grep "class=.age-" tmp &&
grep ">[A-Z][a-z][a-z], [0-9]* Apr 2005 " tmp
'
@@ -103,7 +99,7 @@ test_expect_success 'date-format still formats the log fallback' '
test_expect_success 'date-format leaves the commit page on ISO 8601' '
daterc "date-format=rfc2822" &&
sha=$(git -C repos/dated rev-parse HEAD~1) &&
- dateq "url=dated/commit/&id=$sha" >tmp &&
+ cgit_rc daterc "url=dated/commit/&id=$sha" >tmp &&
grep "2005-04-07 [0-9:]* +0000" tmp &&
! grep "Apr 2005" tmp
'
diff --git a/tests/t0204-limits.sh b/tests/t0204-limits.sh
index 2d33368..ecc33bf 100755
--- a/tests/t0204-limits.sh
+++ b/tests/t0204-limits.sh
@@ -4,17 +4,10 @@
# on the lines and files a diff renders inline, on the size of a blob any
# view will inflate, and the clamp on index query length. Crossing one has
# to trim the page or point somewhere better suited, never drop the request.
-# The last case is the same idea applied to a filter, which degrades to
-# escaped text when its highlighting library is absent.
test_description='Check the ref listing and diff size limits'
. ./setup.sh
-if test "$CGIT_HAS_LUA" -eq 1
-then
- test_set_prereq LUA
-fi
-
# The limits only show themselves against content that crosses them, so the
# fixture carries more branches and tags than max-ref-count allows and a
# commit whose oversized file diff sits beside a small one, which is what
@@ -36,33 +29,16 @@ test_expect_success 'set up limit fixtures' '
git tag tag-$i || exit 1
done
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-ref-count=2" &&
- echo "max-diff-lines=20" &&
- echo "max-diff-files=0" &&
- echo "repo.url=limits" &&
- echo "repo.path=$PWD/repos/limits/.git"
- } >limitrc &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-diff-files=1" &&
- echo "repo.url=limits" &&
- echo "repo.path=$PWD/repos/limits/.git"
- } >limitfilesrc
+ cgitrc_repo limitrc limits max-ref-count=2 max-diff-lines=20 max-diff-files=0 &&
+ cgitrc_repo limitfilesrc limits max-diff-files=1
'
-limitq() { CGIT_CONFIG="$PWD/limitrc" QUERY_STRING="$1" cgit; }
-
# An index query is compared against every repository the listing holds, so
# an enormous one multiplies out across the whole index. Clamping it rather
# than refusing it keeps the cost bounded without making an ordinary search
# behave any differently.
test_expect_success 'an enormous query is clamped, not rejected' '
long=$(awk "BEGIN{s=\"\";for(i=0;i<4000;i++)s=s \"a\"; print s}") &&
- test ${#long} -eq 4000 &&
cgit_query "q=$long" >tmp &&
grep "</html>" tmp &&
longest=$(grep -o "aaaa*" tmp | awk "{print length}" | sort -n | tail -1) &&
@@ -71,7 +47,7 @@ test_expect_success 'an enormous query is clamped, not rejected' '
test_expect_success 'an ordinary query still filters the index' '
cgit_query "q=foo" >tmp &&
- grep "foo" tmp &&
+ grep ">foo</a>" tmp &&
! grep ">bar<" tmp
'
@@ -79,7 +55,7 @@ test_expect_success 'an ordinary query still filters the index' '
# hands the overflow to page on their own, so a limit that leaked between the
# two sections would show up as the wrong link or the wrong count here.
test_expect_success 'refs page lists max-ref-count branches and tags' '
- limitq "url=limits/refs/" >tmp &&
+ cgit_rc limitrc "url=limits/refs/" >tmp &&
test $(grep -c "log/?h=" tmp) -eq 2 &&
test $(grep -c "/tag/?h=tag-" tmp) -eq 2
'
@@ -90,16 +66,16 @@ test_expect_success 'refs page links each overflow to its own category' '
'
test_expect_success 'branch page paginates independently' '
- limitq "url=limits/refs/heads/" >tmp &&
+ cgit_rc limitrc "url=limits/refs/heads/" >tmp &&
test $(grep -c "log/?h=" tmp) -eq 2 &&
grep "\[next\]" tmp &&
- limitq "url=limits/refs/heads/&ofs=2" >tmp &&
+ cgit_rc limitrc "url=limits/refs/heads/&ofs=2" >tmp &&
grep "\[prev\]" tmp &&
! grep "/tag/?h=tag-" tmp
'
test_expect_success 'tag page paginates independently' '
- limitq "url=limits/refs/tags/&ofs=2" >tmp &&
+ cgit_rc limitrc "url=limits/refs/tags/&ofs=2" >tmp &&
grep "/tag/?h=tag-" tmp &&
grep "\[prev\]" tmp &&
! grep "log/?h=" tmp
@@ -115,8 +91,8 @@ test_expect_success 'a negative ref count lists every ref' '
echo "summary-tags=$n" &&
sed "s/^max-ref-count=2$/max-ref-count=$n/" limitrc
} >count$n.rc &&
- CGIT_CONFIG="$PWD/count$n.rc" QUERY_STRING="url=limits/refs/" cgit >refs$n &&
- CGIT_CONFIG="$PWD/count$n.rc" QUERY_STRING="url=limits/" cgit >summary$n ||
+ cgit_rc count$n.rc "url=limits/refs/" >refs$n &&
+ cgit_rc count$n.rc "url=limits/" >summary$n ||
return 1
done &&
grep "log/?h=branch-3" refs0 &&
@@ -130,7 +106,7 @@ test_expect_success 'a negative ref count lists every ref' '
# The rest of the commit still renders, so one huge file cannot hide the
# small change beside it.
test_expect_success 'oversized file diff is replaced by a link' '
- limitq "url=limits/commit/" >tmp &&
+ cgit_rc limitrc "url=limits/commit/" >tmp &&
grep "too large to be rendered inline" tmp &&
grep "View it on its own page" tmp
'
@@ -140,7 +116,7 @@ test_expect_success 'small file in the same commit still renders inline' '
'
test_expect_success 'the single-file diff page always renders in full' '
- limitq "url=limits/diff/big.c" >tmp &&
+ cgit_rc limitrc "url=limits/diff/big.c" >tmp &&
grep "class=.hunk." tmp &&
! grep "too large to be rendered inline" tmp
'
@@ -149,13 +125,13 @@ test_expect_success 'the single-file diff page always renders in full' '
# so it drops the whole body back to the diffstat while a request naming a
# single file stays unaffected.
test_expect_success 'a commit changing too many files shows stat only' '
- CGIT_CONFIG="$PWD/limitfilesrc" QUERY_STRING="url=limits/commit/" cgit >tmp &&
+ cgit_rc limitfilesrc "url=limits/commit/" >tmp &&
grep "too large to be rendered inline" tmp &&
! grep "class=.hunk." tmp
'
test_expect_success 'the single-file page is not limited by max-diff-files' '
- CGIT_CONFIG="$PWD/limitfilesrc" QUERY_STRING="url=limits/diff/small.c" cgit >tmp &&
+ cgit_rc limitfilesrc "url=limits/diff/small.c" >tmp &&
grep "class=.hunk." tmp
'
@@ -172,15 +148,9 @@ test_expect_success 'a diff of an oversized blob is not inlined' '
awk "BEGIN{for(i=0;i<401;i++)print \"aaaaaaaa\"}" >big.txt &&
git commit -am change
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-blob-size=1" &&
- echo "repo.url=blobdiff" &&
- echo "repo.path=$PWD/repos/blobdiff/.git"
- } >blobdiffrc &&
+ cgitrc_repo blobdiffrc blobdiff max-blob-size=1 &&
sha=$(git -C repos/blobdiff rev-parse HEAD) &&
- CGIT_CONFIG="$PWD/blobdiffrc" QUERY_STRING="url=blobdiff/commit/&id=$sha" cgit >tmp &&
+ cgit_rc blobdiffrc "url=blobdiff/commit/&id=$sha" >tmp &&
grep "Binary files differ" tmp &&
! grep "aaaaaaaa" tmp
'
@@ -192,28 +162,12 @@ test_expect_success 'every page showing a blob refuses an oversized one alike' '
} >blobpagesrc &&
for page in tree plain blob blame
do
- CGIT_CONFIG="$PWD/blobpagesrc" QUERY_STRING="url=blobdiff/$page/big.txt" cgit >tmp &&
+ cgit_rc blobpagesrc "url=blobdiff/$page/big.txt" >tmp &&
grep "^Status: 413 Content Too Large" tmp &&
grep "Blob size (3 KB) exceeds the limit (1 KB)" tmp || return 1
done
'
-# A missing scintillua leaves the shipped filter with nothing to highlight
-# with, and a filter that failed there would take the whole page down with
-# it, so it has to hand the source back escaped instead.
-test_expect_success LUA 'highlight filter passes text through without scintillua' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "source-filter=lua:$(cd "$TEST_OUTPUT_DIRECTORY/../custom/extensions" && pwd)/syntax-highlight.lua" &&
- echo "repo.url=limits" &&
- echo "repo.path=$PWD/repos/limits/.git"
- } >hlrc &&
- CGIT_SCINTILLUA_PATH=/nonexistent CGIT_CONFIG="$PWD/hlrc" \
- QUERY_STRING="url=limits/tree/small.c" cgit >tmp &&
- grep "int y;" tmp
-'
-
# The patch and rawdiff pages hand the work to git, whose own limit on an
# inlined blob is far past max-blob-size, so the cap is passed through to it.
test_expect_success 'patch and rawdiff show an oversized blob as binary' '
@@ -226,7 +180,7 @@ test_expect_success 'patch and rawdiff show an oversized blob as binary' '
} >blobrc &&
for p in patch rawdiff
do
- CGIT_CONFIG="$PWD/blobrc" QUERY_STRING="url=limits/$p/" cgit >tmp &&
+ cgit_rc blobrc "url=limits/$p/" >tmp &&
grep "^Binary files" tmp &&
! grep "^+1999$" tmp || return 1
done
diff --git a/tests/t0205-config.sh b/tests/t0205-config.sh
index 4536e49..32c260e 100755
--- a/tests/t0205-config.sh
+++ b/tests/t0205-config.sh
@@ -1,43 +1,35 @@
#!/bin/sh
-# Checks what the configuration parser reports on stderr. A key it does not
-# know, a repo key before any repository and a filter set by a scanned
-# repository without trust-scan-config are each ignored with a warning naming
-# the key, a repo filter in the main cgitrc passes without one, a repository
-# whose path is missing answers 404 on every page, and a config cgit fully
-# understands stays silent.
+# Checks what the configuration parser reports on stderr.
test_description='Check configuration diagnostics'
. ./setup.sh
test_expect_success 'set up configs' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "no-such-key=1" &&
- echo "repo.url=diag" &&
- echo "repo.path=$PWD/repos/foo/.git" &&
- echo "repo.no-such-repo-key=1"
- } >diagrc &&
+ write_script drain-filter <<-\EOF &&
+ cat >/dev/null
+ EOF
+ cgitrc_repo diagrc diag no-such-key=1 "repo.path=$PWD/repos/foo/.git" repo.no-such-repo-key=1 &&
{
echo "virtual-root=/" &&
echo "cache-size=0" &&
echo "repo.desc=early" &&
echo "repo.url=diag" &&
echo "repo.path=$PWD/repos/foo/.git" &&
- echo "repo.about-filter=exec:/bin/true"
+ echo "repo.readme=:file-1" &&
+ echo "repo.about-filter=exec:$PWD/drain-filter"
} >earlyrc
'
test_expect_success 'unknown keys are reported and the page still renders' '
- CGIT_CONFIG="$PWD/diagrc" QUERY_STRING="url=diag/" cgit >tmp 2>err &&
+ cgit_rc diagrc "url=diag/" >tmp 2>err &&
grep "Status: 200" tmp &&
grep "Unknown config key: no-such-key" err &&
grep "Unknown repo config key: no-such-repo-key" err
'
test_expect_success 'a repo key before any repo.url is reported' '
- CGIT_CONFIG="$PWD/earlyrc" QUERY_STRING="url=diag/" cgit >tmp 2>err &&
+ cgit_rc earlyrc "url=diag/" >tmp 2>err &&
grep "Status: 200" tmp &&
grep "Ignoring repo.desc before any repo.url" err
'
@@ -50,7 +42,7 @@ test_expect_success 'a filter in a scanned repository is reported without its fl
echo "cache-size=0" &&
echo "scan-path=$PWD/scan"
} >scanrc &&
- CGIT_CONFIG="$PWD/scanrc" QUERY_STRING="url=foo.git/commit/" cgit >tmp 2>err &&
+ cgit_rc scanrc "url=foo.git/commit/" >tmp 2>err &&
grep "<div class=.commit-subject.>commit 5" tmp &&
grep "Ignoring commit-filter in $PWD/scan/foo.git/: trust-scan-config is not set" err
'
@@ -60,14 +52,17 @@ test_expect_success 'the flag lets a scanned repository set its filters' '
echo "trust-scan-config=1" &&
cat scanrc
} >scanrc-on &&
- CGIT_CONFIG="$PWD/scanrc-on" QUERY_STRING="url=foo.git/commit/" cgit >tmp 2>err &&
+ cgit_rc scanrc-on "url=foo.git/commit/" >tmp 2>err &&
grep "<div class=.commit-subject.>COMMIT 5" tmp &&
! grep "Ignoring" err
'
+# The filter prints nothing, so an empty about div shows that it ran in place
+# of the readme. It reads its input first, so its exit cannot race cgit's
+# write.
test_expect_success 'a repo filter in the main cgitrc needs no flag' '
- ! grep "about-filter" err &&
- CGIT_CONFIG="$PWD/earlyrc" QUERY_STRING="url=diag/" cgit >tmp 2>err &&
+ cgit_rc earlyrc "url=diag/about/" >tmp 2>err &&
+ grep "<div id=.about.></div>" tmp &&
! grep "about-filter" err
'
@@ -82,7 +77,7 @@ test_expect_success 'a repository whose path is missing answers 404' '
mkdir -p gone-cache &&
for page in "" log/ commit/ "tag/&h=x" atom/ plain/f
do
- CGIT_CONFIG="$PWD/gonerc" QUERY_STRING="url=gone/$page" cgit >tmp 2>err &&
+ cgit_rc gonerc "url=gone/$page" >tmp 2>err &&
grep "^Status: 404 Not Found" tmp &&
grep "Failed to open gone: No such file or directory" tmp || return 1
done &&
@@ -106,7 +101,7 @@ test_expect_success 'a NUL inside a comment does not end it' '
echo "repo.path=$PWD/repos/foo/.git"
} >nulrc &&
grep -c Smuggled nulrc &&
- CGIT_CONFIG="$PWD/nulrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc nulrc "url=" >tmp &&
grep "</html>" tmp &&
! grep Smuggled tmp
'
diff --git a/tests/t0206-trailers.sh b/tests/t0206-trailers.sh
index 61fba94..15c3600 100755
--- a/tests/t0206-trailers.sh
+++ b/tests/t0206-trailers.sh
@@ -87,7 +87,7 @@ test_expect_success 'a message with no block gets no table' '
test_expect_success 'with the flag off the message stays whole' '
sed "s/^enable-commit-trailers=1/enable-commit-trailers=0/" cgitrc >cgitrc-off &&
- CGIT_CONFIG="$PWD/cgitrc-off" QUERY_STRING="url=trailers/commit/" cgit >tmp &&
+ cgit_rc cgitrc-off "url=trailers/commit/" >tmp &&
! grep "commit-trailers" tmp &&
grep "Signed-off-by: A U Thor" tmp
'
diff --git a/tests/t0208-mailmap.sh b/tests/t0208-mailmap.sh
index 86f6d50..9e9ae0f 100755
--- a/tests/t0208-mailmap.sh
+++ b/tests/t0208-mailmap.sh
@@ -62,9 +62,9 @@ test_expect_success 'the atom feed uses the mapped name and address' '
'
test_expect_success 'the author search matches the mapped name' '
- cgit_query "url=mapped/log/&qt=author&q=New+Name" >tmp &&
+ cgit_url "mapped/log/&qt=author&q=New+Name" >tmp &&
grep ">first commit</a>" tmp &&
- cgit_query "url=mapped/log/&qt=author&q=Nobody" >tmp &&
+ cgit_url "mapped/log/&qt=author&q=Nobody" >tmp &&
! grep ">first commit</a>" tmp
'
@@ -73,7 +73,7 @@ test_expect_success 'with the flag off the old ident shows' '
echo "enable-mailmap=0" &&
cat cgitrc
} >cgitrc-off &&
- CGIT_CONFIG="$PWD/cgitrc-off" QUERY_STRING="url=mapped/log/" cgit >tmp &&
+ cgit_rc cgitrc-off "url=mapped/log/" >tmp &&
grep "<td class=.col-author.>Old Name</td>" tmp
'
diff --git a/tests/t0301-security.sh b/tests/t0301-security.sh
index d9ec265..b62fc62 100755
--- a/tests/t0301-security.sh
+++ b/tests/t0301-security.sh
@@ -8,15 +8,13 @@
test_description='Check security fixes and fork-specific behaviour'
. ./setup.sh
-# Most of what follows shares one repository and one config, so the fixture
-# carries everything they need at once, a blob over the size limit, readmes
-# holding markup that must not reach the page as markup, and a subdirectory
-# to sort ahead of the files.
+# Some cases below share one repository and one config, so the fixture
+# carries everything they need at once, readmes holding markup that must not
+# reach the page as markup and a subdirectory to sort ahead of the files.
test_expect_success 'set up security fixtures' '
mkrepo repos/sec 1 &&
(
cd repos/sec &&
- dd if=/dev/zero bs=1024 count=4 2>/dev/null | tr "\0" "X" >big.txt &&
printf "# Title\n<script>alert(1)</script>\n" >README.md &&
printf "<script>alert(2)</script>\n" >readme.txt &&
printf "top\n" >afile &&
@@ -25,25 +23,14 @@ test_expect_success 'set up security fixtures' '
git add -A &&
git commit -m fixtures
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "max-blob-size=1" &&
- echo "enable-blame=1" &&
- echo "enable-tree-group-dirs=1" &&
- echo "repo.url=sec" &&
- echo "repo.path=$PWD/repos/sec/.git"
- } >seccgitrc
+ cgitrc_repo seccgitrc sec max-blob-size=1 enable-tree-group-dirs=1
'
-secq() { CGIT_CONFIG="$PWD/seccgitrc" QUERY_STRING="$1" cgit; }
-
# A revision beginning with a dash reaches git as an option rather than as a
# tip, so a request for id=--output=<path> could create or truncate any file
# the server is able to write.
test_expect_success 'log id=--output does not write a file' '
- rm -f pwned &&
- cgit_query "url=foo/log&id=--output=$PWD/pwned" >tmp 2>&1 &&
+ cgit_url "foo/log&id=--output=$PWD/pwned" >tmp 2>&1 &&
! test -e pwned
'
@@ -51,34 +38,15 @@ test_expect_success 'log id=--output is rejected as an invalid revision' '
grep -i "invalid revision" tmp
'
-test_expect_success 'a normal log still renders' '
- cgit_query "url=foo/log" >tmp &&
- grep -i "commit 5" tmp
-'
-
test_expect_success 'a valid id= still renders the log' '
sha=$(git -C repos/foo rev-parse HEAD) &&
- cgit_query "url=foo/log&id=$sha" >tmp &&
+ cgit_url "foo/log&id=$sha" >tmp &&
grep -i "commit 5" tmp
'
-# max-blob-size is enforced before the object is read, so every view that
-# would otherwise inline a file has to turn the same one away rather than
-# inflate it first and think better of it afterwards.
-test_expect_success 'tree view refuses an oversized blob' '
- secq "url=sec/tree/big.txt" | grep -iE "exceeds|too large"
-'
-
-test_expect_success 'plain view refuses an oversized blob' '
- secq "url=sec/plain/big.txt" | grep -iE "exceeds|too large|413"
-'
-
-test_expect_success 'blame view refuses an oversized blob' '
- secq "url=sec/blame/big.txt" | grep -iE "exceeds|too large"
-'
-
test_expect_success 'a small blob is still served' '
- secq "url=sec/plain/afile" | grep -F "top"
+ cgit_rc seccgitrc "url=sec/plain/afile" >tmp &&
+ grep -F top tmp
'
# A readme is repository content, so with no about filter configured it has
@@ -90,31 +58,24 @@ test_expect_success 'markdown readme without a filter is escaped as plain text'
echo "cache-size=0" &&
echo "repo.url=md" &&
echo "repo.path=$PWD/repos/sec/.git" &&
- echo "repo.readme=master:README.md"
- } >secmdrc &&
- CGIT_CONFIG="$PWD/secmdrc" QUERY_STRING="url=md/about/" cgit >tmp &&
+ echo "repo.readme=master:README.md" &&
+ echo "repo.url=txt" &&
+ echo "repo.path=$PWD/repos/sec/.git" &&
+ echo "repo.readme=master:readme.txt"
+ } >secreadmerc &&
+ cgit_rc secreadmerc "url=md/about/" >tmp &&
grep "pre class=.plaintext." tmp &&
grep "&lt;script&gt;" tmp &&
! grep "<script>alert(1)</script>" tmp
'
-test_expect_success 'non-markdown readme without a filter is escaped' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=txt" &&
- echo "repo.path=$PWD/repos/sec/.git" &&
- echo "repo.readme=master:readme.txt"
- } >sectxtrc &&
- CGIT_CONFIG="$PWD/sectxtrc" QUERY_STRING="url=txt/about/" cgit >tmp &&
+test_expect_success 'non-markdown readme without a filter is escaped as plain text' '
+ cgit_rc secreadmerc "url=txt/about/" >tmp &&
+ grep "pre class=.plaintext." tmp &&
grep "&lt;script&gt;" tmp &&
! grep "<script>alert(2)</script>" tmp
'
-test_expect_success 'non-markdown readme keeps its line structure' '
- grep "pre class=.plaintext." tmp
-'
-
# root-readme is the site's own file rather than repository content, so it
# goes out as written, on an about page of its own.
test_expect_success 'root-readme without a filter is its own page, as written' '
@@ -124,10 +85,10 @@ test_expect_success 'root-readme without a filter is its own page, as written' '
echo "cache-size=0" &&
echo "root-readme=$PWD/root.html"
} >secrootrc &&
- CGIT_CONFIG="$PWD/secrootrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc secrootrc "url=" >tmp &&
grep "<li><a title=.About this site. href=./?p=about.>about</a>" tmp &&
! grep "<b>site</b>" tmp &&
- CGIT_CONFIG="$PWD/secrootrc" QUERY_STRING="p=about" cgit >tmp &&
+ cgit_rc secrootrc "p=about" >tmp &&
grep "^<b>site</b>$" tmp
'
@@ -136,7 +97,7 @@ test_expect_success 'root-readme without a filter is its own page, as written' '
# button and its selects carry no handlers at all.
test_expect_success 'diff options submit through a button' '
sha=$(git -C repos/foo rev-parse HEAD) &&
- cgit_query "url=foo/commit&id=$sha" >tmp &&
+ cgit_url "foo/commit&id=$sha" >tmp &&
grep "type=.submit. value=.reload." tmp &&
! grep "onchange" tmp
'
@@ -144,7 +105,7 @@ test_expect_success 'diff options submit through a button' '
# Grouping directories ahead of files is behaviour this fork adds, so nothing
# upstream covers it.
test_expect_success 'tree groups directories before files' '
- secq "url=sec/tree/" >tmp &&
+ cgit_rc seccgitrc "url=sec/tree/" >tmp &&
dirline=$(grep -n "tree/zsub" tmp | head -1 | cut -d: -f1) &&
fileline=$(grep -n "tree/afile" tmp | head -1 | cut -d: -f1) &&
test -n "$dirline" &&
@@ -153,7 +114,7 @@ test_expect_success 'tree groups directories before files' '
'
# A file name is repository content and may hold a quote, which would break
-# out of the href attribute on the line number links of a side by side diff,
+# out of the href attribute on the line number links of a side-by-side diff,
# so the path is percent-encoded on its way into them.
test_expect_success 'ssdiff percent-encodes a quoted file path' '
mkrepo repos/xss 1 &&
@@ -166,14 +127,9 @@ test_expect_success 'ssdiff percent-encodes a quoted file path' '
printf "a\nc\n" >"$name" &&
git commit -am change
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=xss" &&
- echo "repo.path=$PWD/repos/xss/.git"
- } >xssrc &&
+ cgitrc_repo xssrc xss &&
sha=$(git -C repos/xss rev-parse HEAD) &&
- CGIT_CONFIG="$PWD/xssrc" QUERY_STRING="url=xss/diff/&id=$sha&ss=1" cgit >tmp &&
+ cgit_rc xssrc "url=xss/diff/&id=$sha&ss=1" >tmp &&
grep "tree/x%27y.txt" tmp &&
! grep "href=.[^>]*x.y.txt.[^>]*>" tmp
'
@@ -189,15 +145,10 @@ test_expect_success 'a message-less commit renders without crashing' '
cid=$(git hash-object -t commit -w raw) &&
git update-ref refs/heads/master "$cid"
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=nomsg" &&
- echo "repo.path=$PWD/repos/nomsg/.git"
- } >nomsgrc &&
- CGIT_CONFIG="$PWD/nomsgrc" QUERY_STRING="url=nomsg/log/&showmsg=1" cgit >tmp &&
+ cgitrc_repo nomsgrc nomsg &&
+ cgit_rc nomsgrc "url=nomsg/log/&showmsg=1" >tmp &&
grep "no commit message" tmp &&
- CGIT_CONFIG="$PWD/nomsgrc" QUERY_STRING="url=nomsg/" cgit >tmp &&
+ cgit_rc nomsgrc "url=nomsg/" >tmp &&
grep "no commit message" tmp
'
@@ -218,42 +169,21 @@ test_expect_success 'set up a repo whose branches point at odd objects' '
grep -q "tree.refs/heads/points-at-tree" refs.out &&
grep -q "tag.refs/heads/points-at-tag" refs.out
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "branch-sort=age" &&
- echo "repo.url=oddref" &&
- echo "repo.path=$PWD/repos/oddref/.git"
- } >oddrefrc
-'
-
-test_expect_success 'refs page sorts such branches without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/refs/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
-'
-
-test_expect_success 'the branch page sorts them without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/refs/heads/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
+ cgitrc_repo oddrefrc oddref branch-sort=age
'
-test_expect_success 'the summary page sorts them without crashing' '
- CGIT_CONFIG="$PWD/oddrefrc" QUERY_STRING="url=oddref/" cgit >tmp &&
- grep "points-at-tree" tmp &&
- grep "</html>" tmp
+test_expect_success 'the refs, branch and summary pages sort such branches' '
+ for page in refs/ refs/heads/ ""
+ do
+ cgit_rc oddrefrc "url=oddref/$page" >tmp &&
+ grep "points-at-tree" tmp &&
+ grep "</html>" tmp || return 1
+ done
'
test_expect_success 'name-sorted branches are unaffected' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "branch-sort=name" &&
- echo "repo.url=oddref" &&
- echo "repo.path=$PWD/repos/oddref/.git"
- } >oddrefnamerc &&
- CGIT_CONFIG="$PWD/oddrefnamerc" QUERY_STRING="url=oddref/refs/heads/" cgit >tmp &&
+ sed "s/^branch-sort=age$/branch-sort=name/" oddrefrc >oddrefnamerc &&
+ cgit_rc oddrefnamerc "url=oddref/refs/heads/" >tmp &&
grep "points-at-tree" tmp &&
grep "</html>" tmp
'
@@ -266,9 +196,9 @@ test_expect_success 'name-sorted branches are unaffected' '
# working.
test_expect_success 'set up a submodule fixture with a hostile module-link' '
mkrepo repos/modlink 1 &&
+ sub=$(git -C repos/modlink rev-parse HEAD) &&
(
cd repos/modlink &&
- sub=$(git rev-parse HEAD) &&
git update-index --add --cacheinfo 160000,$sub,submod &&
git commit -m gitlink
) &&
@@ -284,7 +214,7 @@ test_expect_success 'set up a submodule fixture with a hostile module-link' '
'
test_expect_success 'a surplus conversion does not crash the tree view' '
- CGIT_CONFIG="$PWD/modlinkrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgit_rc modlinkrc "url=modlink/tree/" >tmp &&
grep "ls-mod" tmp
'
@@ -293,41 +223,20 @@ test_expect_success 'a surplus conversion is shown literally, not filled' '
'
test_expect_success 'a well-formed module-link still takes name and sha1' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "module-link=/mod/%s/commit/?id=%s" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkokrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkokrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkokrc modlink "module-link=/mod/%s/commit/?id=%s" &&
+ cgit_rc modlinkokrc "url=modlink/tree/" >tmp &&
grep "href=./mod/submod/commit/?id=$sub." tmp
'
test_expect_success 'a per-path module-link takes only the sha1' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git" &&
- echo "repo.module-link.submod=https://example.com/s/?id=%s"
- } >modlinkpathrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkpathrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkpathrc modlink "repo.module-link.submod=https://example.com/s/?id=%s" &&
+ cgit_rc modlinkpathrc "url=modlink/tree/" >tmp &&
grep "href=.https://example.com/s/?id=$sub." tmp
'
test_expect_success 'a doubled percent in a module-link renders as one' '
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "module-link=/m/%s/%%/%s" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkpctrc &&
- sub=$(git -C repos/modlink rev-parse HEAD~1) &&
- CGIT_CONFIG="$PWD/modlinkpctrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgitrc_repo modlinkpctrc modlink "module-link=/m/%s/%%/%s" &&
+ cgit_rc modlinkpctrc "url=modlink/tree/" >tmp &&
grep "href=./m/submod/%/$sub." tmp
'
@@ -345,7 +254,7 @@ test_expect_success 'a clone endpoint with http clone off answers the same way'
echo "enable-http-clone=0" &&
cat cgitrc
} >noclonerc &&
- CGIT_CONFIG="$PWD/noclonerc" QUERY_STRING="url=foo/info/refs" cgit >tmp &&
+ cgit_rc noclonerc "url=foo/info/refs" >tmp &&
grep "^Status: 404 Not Found" tmp &&
grep "<select name=.h. " tmp
'
@@ -369,17 +278,11 @@ test_expect_success 'set up a hostile .gitmodules' '
git add .gitmodules &&
git commit -m hostile
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "enable-gitmodules-links=1" &&
- echo "repo.url=modlink" &&
- echo "repo.path=$PWD/repos/modlink/.git"
- } >modlinkgmrc
+ cgitrc_repo modlinkgmrc modlink enable-gitmodules-links=1
'
test_expect_success 'a javascript url never reaches an href' '
- CGIT_CONFIG="$PWD/modlinkgmrc" QUERY_STRING="url=modlink/tree/" cgit >tmp &&
+ cgit_rc modlinkgmrc "url=modlink/tree/" >tmp &&
! grep "href=.javascript:" tmp &&
grep "class=.ls-mod. title=.javascript:alert(1).>submod</span>" tmp
'
@@ -412,8 +315,7 @@ test_expect_success 'set up the advisory fixtures' '
) &&
mkdir -p readme &&
printf "readme text\n" >readme/README &&
- cat >auth.sh <<-\EOF &&
- #!/bin/sh
+ write_script auth.sh <<-\EOF &&
case "$1" in
authenticate-post)
cat >/dev/null
@@ -422,22 +324,11 @@ test_expect_success 'set up the advisory fixtures' '
esac
exit 1
EOF
- chmod +x auth.sh &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "auth-filter=exec:$PWD/auth.sh" &&
- echo "repo.url=cve" &&
- echo "repo.path=$PWD/repos/cve/.git" &&
- echo "repo.readme=$PWD/readme/README" &&
- echo "repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh"
- } >cverc
+ cgitrc_repo cverc cve "auth-filter=exec:$PWD/auth.sh" "repo.readme=$PWD/readme/README" "repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh"
'
-cveq() { CGIT_CONFIG="$PWD/cverc" QUERY_STRING="$1" cgit; }
-
test_expect_success 'a newline in a file name cannot split the headers' '
- cveq "url=cve/plain/crlf%0d%0aX-Injected:%201.txt" >tmp &&
+ cgit_rc cverc "url=cve/plain/crlf%0d%0aX-Injected:%201.txt" >tmp &&
grep "^Status: 200" tmp &&
! grep "^X-Injected" tmp &&
grep "^Content-Disposition: inline; filename=.crlf\\\\r\\\\nX-Injected: 1.txt.$" tmp
@@ -451,48 +342,48 @@ test_expect_success 'a posted length past any buffer is clamped' '
'
test_expect_success 'the dumb transport refuses a path that climbs out' '
- cveq "url=cve/objects/../config" >tmp &&
+ cgit_rc cverc "url=cve/objects/../config" >tmp &&
grep "^Status: 404" tmp &&
! grep "repositoryformatversion" tmp &&
- cveq "url=cve/objects/info/../../config" >tmp &&
+ cgit_rc cverc "url=cve/objects/info/../../config" >tmp &&
grep "^Status: 404" tmp &&
! grep "repositoryformatversion" tmp
'
test_expect_success 'the about page serves nothing above the readme directory' '
- cveq "url=cve/about/../cverc" >tmp &&
+ cgit_rc cverc "url=cve/about/../cverc" >tmp &&
grep "^Status: 404" tmp &&
! grep "virtual-root" tmp &&
- cveq "url=cve/about/" >tmp &&
+ cgit_rc cverc "url=cve/about/" >tmp &&
grep "readme text" tmp
'
test_expect_success 'a commit with an empty author renders everywhere' '
- cveq "url=cve/log/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/log/&h=noname" >tmp &&
grep ">empty author</a>" tmp &&
- cveq "url=cve/commit/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/commit/&h=noname" >tmp &&
grep "<div class=.commit-subject.>empty author<" tmp &&
- cveq "url=cve/atom/&h=noname" >tmp &&
+ cgit_rc cverc "url=cve/atom/&h=noname" >tmp &&
grep "<name>unknown</name>" tmp &&
! grep "<email></email>" tmp
'
test_expect_success 'a percent sign without digits behind it is read as itself' '
- cveq "url=cve/log/&q=%" >tmp &&
+ cgit_rc cverc "url=cve/log/&q=%" >tmp &&
grep "^Status: 200" tmp &&
- cveq "url=cve/log/&q=%zz%2" >tmp &&
+ cgit_rc cverc "url=cve/log/&q=%zz%2" >tmp &&
grep "^Status: 200" tmp &&
grep "value=.%zz%2." tmp
'
test_expect_success 'script in a file name is escaped on the diff page' '
- cveq "url=cve/diff/" >tmp &&
+ cgit_rc cverc "url=cve/diff/" >tmp &&
! grep "<script>alert" tmp &&
grep "&lt;script&gt;alert(1)x.txt" tmp
'
test_expect_success 'shell syntax in a file name reaches a filter as a plain argument' '
- cveq "url=cve/tree/%24(touch%20pwned).c" >tmp &&
+ cgit_rc cverc "url=cve/tree/%24(touch%20pwned).c" >tmp &&
grep "^Status: 200" tmp &&
grep "pwned).c" tmp &&
! test -e pwned &&
diff --git a/tests/t0302-home-access.sh b/tests/t0302-home-access.sh
index df55e0d..e6faa45 100755
--- a/tests/t0302-home-access.sh
+++ b/tests/t0302-home-access.sh
@@ -16,26 +16,15 @@ test_description='Ensure that git does not access $HOME'
command -v strace >/dev/null 2>&1 || {
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() {
- # A home that happened to exist would be one git may legitimately
- # read, leaving the check below with nothing to catch, so extend the
- # path until nothing is there.
- missing_home="/path/to/some/place/that/does/not/possibly/exist"
- depth=0
- while test -d "$missing_home"
- do
- depth=$((depth + 1))
- missing_home="$missing_home/$depth"
- done &&
+ missing_home=/path/to/some/place/that/does/not/possibly/exist &&
strace \
-E HOME="$missing_home" \
-E CGIT_CONFIG="$PWD/cgitrc" \
diff --git a/tests/t0303-robustness.sh b/tests/t0303-robustness.sh
index 07f7c09..c881045 100755
--- a/tests/t0303-robustness.sh
+++ b/tests/t0303-robustness.sh
@@ -10,17 +10,9 @@ test_description='Check the audit regressions'
test_expect_success 'set up a repository and a config to vary' '
mkrepo repos/rob 3 &&
sha=$(git -C repos/rob rev-parse HEAD~1) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "snapshots=tar.gz" &&
- echo "repo.url=rob" &&
- echo "repo.path=$PWD/repos/rob/.git"
- } >robrc
+ cgitrc_repo robrc rob snapshots=tar.gz
'
-robq() { CGIT_CONFIG="$PWD/robrc" QUERY_STRING="$1" cgit; }
-
test_expect_success 'a repository without a path answers 404' '
{
echo "virtual-root=/" &&
@@ -31,7 +23,7 @@ test_expect_success 'a repository without a path answers 404' '
} >nopathrc &&
for r in nopath emptypath
do
- CGIT_CONFIG="$PWD/nopathrc" QUERY_STRING="url=$r/log/" cgit >tmp &&
+ cgit_rc nopathrc "url=$r/log/" >tmp &&
grep "^Status: 404 Not Found" tmp &&
grep "Failed to open $r: Not a valid git repository" tmp || return 1
done
@@ -42,7 +34,7 @@ test_expect_success 'a repository without a path answers 404' '
test_expect_success 'a repository without a path has no age on the index' '
mkdir -p "(null)/refs/heads" &&
>"(null)/refs/heads/master" &&
- CGIT_CONFIG="$PWD/nopathrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc nopathrc "url=" >tmp &&
grep "</html>" tmp &&
! grep "<time " tmp
'
@@ -57,12 +49,12 @@ test_expect_success 'a repository with an empty url is skipped with a warning' '
echo "repo.url=rob" &&
echo "repo.path=$PWD/repos/rob/.git"
} >nourlrc &&
- CGIT_CONFIG="$PWD/nourlrc" QUERY_STRING="url=rob/" cgit >tmp 2>err &&
+ cgit_rc nourlrc "url=rob/" >tmp 2>err &&
grep "^Status: 200" tmp &&
grep "Ignoring repository with an empty url" err &&
- CGIT_CONFIG="$PWD/nourlrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc nourlrc "url=" >tmp &&
! grep nourl tmp &&
- CGIT_CONFIG="$PWD/nourlrc" QUERY_STRING="r=" cgit >tmp &&
+ cgit_rc nourlrc "r=" >tmp &&
! grep nourl tmp
'
@@ -73,7 +65,7 @@ test_expect_success 'a scan path that is itself a repository is named after it'
echo "remove-suffix=1" &&
echo "scan-path=$PWD/repos/rob"
} >rootrc &&
- CGIT_CONFIG="$PWD/rootrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc rootrc "url=" >tmp &&
grep "toplevel-repo.><a href=./rob/.>rob</a>" tmp
'
@@ -86,7 +78,7 @@ test_expect_success 'a repo key after scan-path is reported instead of applied'
echo "scan-path=$PWD/repos" &&
echo "repo.desc=misplaced"
} >stalerc &&
- CGIT_CONFIG="$PWD/stalerc" QUERY_STRING="url=" cgit >tmp 2>err &&
+ cgit_rc stalerc "url=" >tmp 2>err &&
! grep "misplaced" tmp &&
grep "Ignoring repo.desc before any repo.url" err
'
@@ -100,7 +92,7 @@ test_expect_success 'a valueless or broken git config in a scanned repository is
echo "enable-git-config=1" &&
echo "scan-path=$PWD/scan"
} >gitcfgrc &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=" cgit >tmp 2>err &&
+ cgit_rc gitcfgrc "url=" >tmp 2>err &&
grep "toplevel-repo.><a href=./a.git/.>" tmp &&
grep "Ignoring unreadable config in $PWD/scan/a.git/config" err
'
@@ -113,7 +105,7 @@ test_expect_success 'only descriptive keys are taken from a scanned repository'
echo "head-content=<script>alert(1)</script>" &&
echo "logo-link=javascript:alert(2)"
} >scan/b.git/cgitrc &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=b.git/about/" cgit >tmp 2>err &&
+ cgit_rc gitcfgrc "url=b.git/about/" >tmp 2>err &&
grep "from the repo" tmp &&
grep "pre class=.plaintext." tmp &&
! grep "alert(" tmp &&
@@ -123,7 +115,7 @@ test_expect_success 'only descriptive keys are taken from a scanned repository'
test_expect_success 'a filesystem readme from a scanned repository is refused' '
echo "readme=/etc/hosts" >scan/b.git/cgitrc &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=b.git/about/" cgit >tmp 2>err &&
+ cgit_rc gitcfgrc "url=b.git/about/" >tmp 2>err &&
grep "Ignoring readme in " err
'
@@ -132,7 +124,7 @@ test_expect_success 'a filesystem readme from a scanned repository is refused' '
test_expect_success 'a readme ending in a colon is refused like a path on disk' '
echo "outside the repository" >"scan/outside:" &&
echo "readme=../outside:" >scan/b.git/cgitrc &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=b.git/about/" cgit >tmp 2>err &&
+ cgit_rc gitcfgrc "url=b.git/about/" >tmp 2>err &&
grep "Ignoring readme in " err &&
! grep "outside the repository" tmp
'
@@ -143,7 +135,7 @@ test_expect_success 'a readme set after scan-path reaches a scanned repository'
rm scan/b.git/cgitrc &&
cat gitcfgrc >laterrc &&
echo "readme=master:file-1" >>laterrc &&
- CGIT_CONFIG="$PWD/laterrc" QUERY_STRING="url=b.git/about/" cgit >tmp &&
+ cgit_rc laterrc "url=b.git/about/" >tmp &&
grep "<pre class=.plaintext.>1$" tmp
'
@@ -167,7 +159,7 @@ test_expect_success 'html serving from a scanned repository waits on trust' '
echo "mimetype.html=text/html" &&
echo "scan-path=$PWD/scan2"
} >htmlrc &&
- CGIT_CONFIG="$PWD/htmlrc" QUERY_STRING="url=c.git/plain/page.html" cgit >tmp 2>err &&
+ cgit_rc htmlrc "url=c.git/plain/page.html" >tmp 2>err &&
grep "^Content-Type: text/plain" tmp &&
grep "^X-Content-Type-Options: nosniff" tmp &&
grep "Ignoring enable-html-serving in $PWD/scan2/c.git/: trust-scan-config is not set" err
@@ -178,41 +170,46 @@ test_expect_success 'with trust-scan-config the same repository serves html' '
echo "trust-scan-config=1" &&
cat htmlrc
} >htmltrustrc &&
- CGIT_CONFIG="$PWD/htmltrustrc" QUERY_STRING="url=c.git/plain/page.html" cgit >tmp &&
+ cgit_rc htmltrustrc "url=c.git/plain/page.html" >tmp &&
grep "^Content-Type: text/html" tmp &&
! grep "^X-Content-Type-Options" tmp
'
test_expect_success SYMLINKS 'a symlink cycle under the scan path is entered once' '
ln -s . scan/loop &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc gitcfgrc "url=" >tmp &&
test $(grep -c "toplevel-repo" tmp) = 2
'
test_expect_success 'a bare query parameter does not swallow the next one' '
- robq "url=rob/commit/&x&id=$sha" >tmp &&
+ cgit_rc robrc "url=rob/commit/&x&id=$sha" >tmp &&
grep "<div class=.commit-subject.>commit 2" tmp
'
+# Whether cgit first sees the filter's exit status or its closed pipe
+# depends on which side runs first, so either message will do.
test_expect_success 'a filter that cannot run answers one error page' '
{
echo "commit-filter=exec:$PWD/no/such/filter" &&
cat robrc
} >badfilterrc &&
- CGIT_CONFIG="$PWD/badfilterrc" QUERY_STRING="url=rob/commit/" cgit >tmp 2>err &&
+ cgit_rc badfilterrc "url=rob/commit/" >tmp 2>err &&
test $(grep -c "^Status:" tmp) = 1 &&
grep "Unable to complete the request" tmp &&
- grep "Unable to run filter $PWD/no/such/filter" err &&
+ grep -e "Unable to run filter $PWD/no/such/filter" -e "Unable to write the page" err &&
grep "Unable to exec filter" err
'
+# The filter may exit before or after cgit writes to it, so the page either
+# completes or ends where the write failed. Either way cgit answers once and
+# exits cleanly rather than dying of SIGPIPE.
test_expect_success 'a filter that exits without reading does not end cgit' '
{
- echo "email-filter=exec:/usr/bin/true" &&
+ echo "email-filter=exec:true" &&
cat robrc
} >truerc &&
- CGIT_CONFIG="$PWD/truerc" QUERY_STRING="url=rob/log/" cgit >tmp &&
- grep "^Status:" tmp
+ cgit_rc truerc "url=rob/log/" >tmp &&
+ test $(grep -c "^Status:" tmp) = 1
'
test_expect_success 'a commit encoding header is read without its newline' '
@@ -222,11 +219,13 @@ test_expect_success 'a commit encoding header is read without its newline' '
git add file-1 &&
git -c i18n.commitEncoding=ISO-8859-1 commit -m "$(printf "caf\351")"
) &&
- robq "url=rob/commit/" >tmp &&
+ cgit_rc robrc "url=rob/commit/" >tmp &&
grep "<div class=.commit-subject.>caf$(printf "\303\251")<" tmp
'
-test_expect_success 'a submodule below the about path does not end the request' '
+# The about page shows the readme alone and its links lead into the tree,
+# so a path below it names nothing, the site about page included.
+test_expect_success 'a path below the about page answers 404' '
(
cd repos/rob &&
git update-index --add --cacheinfo 160000,$sha,sub &&
@@ -237,45 +236,45 @@ test_expect_success 'a submodule below the about path does not end the request'
cat robrc &&
echo "repo.readme=master:file-1"
} >aboutrc &&
- CGIT_CONFIG="$PWD/aboutrc" QUERY_STRING="url=rob/about/sub" cgit >tmp 2>err &&
- grep "^Status: 404" tmp &&
- ! grep "fatal" err
-'
-
-test_expect_success 'a missing image on the about page errors as html' '
- CGIT_CONFIG="$PWD/aboutrc" QUERY_STRING="url=rob/about/missing.png" cgit >tmp &&
- grep "^Status: 404" tmp &&
- grep "^Content-Type: text/html" tmp
+ for path in sub sub/ missing.png file-1 ../robrc
+ do
+ cgit_rc aboutrc "url=rob/about/$path" >tmp 2>err &&
+ grep "^Status: 404" tmp &&
+ grep "^Content-Type: text/html" tmp &&
+ ! grep fatal err || return 1
+ done &&
+ cgit_rc aboutrc "p=about&path=x" >tmp &&
+ grep "^Status: 404" tmp
'
test_expect_success 'a snapshot name holding a slash is refused' '
- robq "url=rob/snapshot/:/../../...tar.gz" >tmp &&
+ cgit_rc robrc "url=rob/snapshot/:/../../...tar.gz" >tmp &&
grep "^Status: 404" tmp &&
- robq "url=rob/snapshot/rob-master.tar.gz" >tmp &&
+ cgit_rc robrc "url=rob/snapshot/rob-master.tar.gz" >tmp &&
grep "^Status: 200" tmp
'
test_expect_success 'a path opening with a colon is refused on the log and patch pages' '
- robq "url=rob/log/:%25x" >tmp &&
+ cgit_rc robrc "url=rob/log/:%25x" >tmp &&
grep "^Status: 400" tmp &&
- robq "url=rob/patch/:%25x" >tmp &&
+ cgit_rc robrc "url=rob/patch/:%25x" >tmp &&
grep "^Status: 400" tmp &&
test $(grep -c "^Status:" tmp) = 1
'
test_expect_success 'a revision spelled like an option is refused' '
git -C repos/rob update-ref refs/heads/--stdin HEAD &&
- robq "url=rob/log/&h=--stdin" >tmp &&
+ cgit_rc robrc "url=rob/log/&h=--stdin" >tmp &&
grep "^Status: 404" tmp &&
- robq "url=rob/blame/file-1&id=--stdin" >tmp &&
+ cgit_rc robrc "url=rob/blame/file-1&id=--stdin" >tmp &&
grep "^Status: 400" tmp &&
- robq "url=rob/atom/&h=--stdin" >tmp &&
+ cgit_rc robrc "url=rob/atom/&h=--stdin" >tmp &&
grep "^Status: 404" tmp
'
test_expect_success 'the blob page reads a file through an annotated tag' '
git -C repos/rob -c tag.gpgsign=false tag -a -m note ann HEAD &&
- robq "url=rob/blob/&h=ann&path=file-1" >tmp &&
+ cgit_rc robrc "url=rob/blob/&h=ann&path=file-1" >tmp &&
strip_headers <tmp >body &&
printf "more\n" >want &&
test_cmp want body
@@ -285,14 +284,14 @@ test_expect_success 'the blob page reads a file through an annotated tag' '
# blob page used to answer such a request with the raw commit object.
test_expect_success 'the blob page walks a path out of the commit or tag the id names' '
tip=$(git -C repos/rob rev-parse HEAD) &&
- robq "url=rob/blob/file-1&id=$tip" >tmp &&
+ cgit_rc robrc "url=rob/blob/file-1&id=$tip" >tmp &&
strip_headers <tmp >body &&
test_cmp want body &&
tag=$(git -C repos/rob rev-parse ann) &&
- robq "url=rob/blob/file-1&id=$tag" >tmp &&
+ cgit_rc robrc "url=rob/blob/file-1&id=$tag" >tmp &&
strip_headers <tmp >body &&
test_cmp want body &&
- robq "url=rob/blob/missing&id=$tip" >tmp &&
+ cgit_rc robrc "url=rob/blob/missing&id=$tip" >tmp &&
grep "^Status: 404" tmp
'
@@ -316,31 +315,24 @@ test_expect_success 'the commit page shows a message whole when text follows its
echo "enable-commit-trailers=1" &&
cat robrc
} >trailrc &&
- CGIT_CONFIG="$PWD/trailrc" QUERY_STRING="url=rob/commit/" cgit >tmp &&
+ cgit_rc trailrc "url=rob/commit/" >tmp &&
! grep "commit-trailers" tmp &&
grep "Conflicts:" tmp
'
test_expect_success 'a revision expression that walks the history is refused' '
- robq "url=rob/log/&h=:/zzzz" >tmp &&
+ cgit_rc robrc "url=rob/log/&h=:/zzzz" >tmp &&
grep "^Status: 404" tmp &&
- robq "url=rob/commit/&id=HEAD^{/zzzz}" >tmp &&
+ cgit_rc robrc "url=rob/commit/&id=HEAD^{/zzzz}" >tmp &&
grep "^Status: 400" tmp &&
- robq "url=rob/log/&qt=range&q=:/zzzz" >tmp &&
+ cgit_rc robrc "url=rob/log/&qt=range&q=:/zzzz" >tmp &&
grep "^Status: 400" tmp &&
- robq "url=rob/log/&qt=range&q=master~1..master" >tmp &&
+ cgit_rc robrc "url=rob/log/&qt=range&q=master~1..master" >tmp &&
grep "^Status: 400" tmp &&
- robq "url=rob/log/&qt=range&q=$sha..master" >tmp &&
+ cgit_rc robrc "url=rob/log/&qt=range&q=$sha..master" >tmp &&
grep "^Status: 200" tmp
'
-test_expect_success 'the log search is literal' '
- robq "url=rob/log/&qt=grep&q=commit.1" >tmp &&
- ! grep ">commit 1</a>" tmp &&
- robq "url=rob/log/&qt=grep&q=commit%201" >tmp &&
- grep ">commit 1</a>" tmp
-'
-
test_expect_success 'a commit with a broken tree line does not crash the blob page' '
git clone -q repos/rob broken &&
(
@@ -349,20 +341,14 @@ test_expect_success 'a commit with a broken tree line does not crash the blob pa
git hash-object -t commit -w --stdin --literally) &&
echo $bad >.git/refs/heads/bad
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=broken" &&
- echo "repo.path=$PWD/broken/.git" &&
- echo "repo.readme=:file-1"
- } >brokenrc &&
- CGIT_CONFIG="$PWD/brokenrc" QUERY_STRING="url=broken/blob/&h=bad&path=file-1" cgit >tmp &&
+ cgitrc_repo brokenrc broken "repo.path=$PWD/broken/.git" repo.readme=:file-1 &&
+ cgit_rc brokenrc "url=broken/blob/&h=bad&path=file-1" >tmp &&
grep "^Status: 404" tmp
'
test_expect_success 'a die inside git answers one 500 page and logs the reason' '
printf "[core\n" >>broken/.git/config &&
- CGIT_CONFIG="$PWD/brokenrc" QUERY_STRING="url=broken/log/" cgit >tmp 2>err &&
+ cgit_rc brokenrc "url=broken/log/" >tmp 2>err &&
grep "^Status: 500" tmp &&
test $(grep -c "^Status:" tmp) = 1 &&
grep "bad config line" err &&
@@ -374,17 +360,12 @@ test_expect_success 'a history with a missing parent renders up to the gap' '
older=$(git -C gap rev-parse HEAD~3) &&
parent=$(git -C gap rev-parse HEAD~2) &&
rm gap/.git/objects/$(echo $parent | cut -c1-2)/$(echo $parent | cut -c3-) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=gap" &&
- echo "repo.path=$PWD/gap/.git"
- } >gaprc &&
- CGIT_CONFIG="$PWD/gaprc" QUERY_STRING="url=gap/log/" cgit >tmp 2>err &&
+ cgitrc_repo gaprc gap "repo.path=$PWD/gap/.git" &&
+ cgit_rc gaprc "url=gap/log/" >tmp 2>err &&
test $(grep -c "^Status:" tmp) = 1 &&
grep "Unable to complete the request" tmp &&
grep "Failed to traverse parents" err &&
- CGIT_CONFIG="$PWD/gaprc" QUERY_STRING="url=gap/patch/&id2=$older" cgit >tmp &&
+ cgit_rc gaprc "url=gap/patch/&id2=$older" >tmp &&
test $(grep -c "^Status:" tmp) = 1 &&
grep "^Status: 500" tmp
'
@@ -407,20 +388,14 @@ test_expect_success 'a corrupt blob in a collected diff answers the error' '
test_copy_bytes $((size / 2)) <$obj >half &&
mv half $obj
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=corrupt" &&
- echo "repo.path=$PWD/corrupt/.git"
- } >corruptrc &&
- CGIT_CONFIG="$PWD/corruptrc" QUERY_STRING="url=corrupt/commit/" cgit >tmp 2>err &&
+ cgitrc_repo corruptrc corrupt "repo.path=$PWD/corrupt/.git" &&
+ cgit_rc corruptrc "url=corrupt/commit/" >tmp 2>err &&
grep "is corrupt" err &&
grep "Unable to complete the request" tmp
'
test_expect_success 'a filter pipeline sees the default SIGPIPE disposition' '
- cat >pipe.sh <<-\EOF &&
- #!/bin/sh
+ write_script pipe.sh <<-\EOF &&
n=0
while test $n -lt 20000
do
@@ -428,12 +403,11 @@ test_expect_success 'a filter pipeline sees the default SIGPIPE disposition' '
n=$((n + 1))
done | head -c 5
EOF
- chmod +x pipe.sh &&
{
echo "source-filter=exec:$PWD/pipe.sh" &&
cat robrc
} >piperc &&
- CGIT_CONFIG="$PWD/piperc" QUERY_STRING="url=rob/tree/file-1" cgit >tmp 2>err &&
+ cgit_rc piperc "url=rob/tree/file-1" >tmp 2>err &&
grep "^Status: 200" tmp &&
! grep -i "broken pipe" err
'
@@ -441,7 +415,7 @@ test_expect_success 'a filter pipeline sees the default SIGPIPE disposition' '
test_expect_success 'the fallback branch skips a name spelled like an option' '
git -C repos/rob update-ref refs/heads/-first HEAD &&
git -C repos/rob symbolic-ref HEAD refs/heads/gone &&
- robq "url=rob/" >tmp &&
+ cgit_rc robrc "url=rob/" >tmp &&
git -C repos/rob symbolic-ref HEAD refs/heads/master &&
grep "^Status: 200" tmp
'
@@ -465,18 +439,17 @@ test_expect_success 'a blank line in the mimetype file is skipped' '
echo "mimetype-file=$PWD/mime.types" &&
cat robrc
} >mimerc &&
- CGIT_CONFIG="$PWD/mimerc" QUERY_STRING="url=rob/plain/note.cgt" cgit >tmp &&
+ cgit_rc mimerc "url=rob/plain/note.cgt" >tmp &&
grep "^Content-Type: image/x-cgit" tmp
'
test_expect_success 'an indented comment in the mimetype file is skipped' '
printf " # cgt used to be image/x-old\nimage/x-cgit cgt\n" >mime.types &&
- CGIT_CONFIG="$PWD/mimerc" QUERY_STRING="url=rob/plain/note.cgt" cgit >tmp &&
+ cgit_rc mimerc "url=rob/plain/note.cgt" >tmp &&
grep "^Content-Type: image/x-cgit" tmp
'
test_expect_success 'a refs page named with an id is not cached for ever' '
- mkdir -p cache &&
{
echo "cache-size=10" &&
echo "cache-root=$PWD/cache" &&
@@ -486,9 +459,9 @@ test_expect_success 'a refs page named with an id is not cached for ever' '
grep -v "^cache-size" robrc
} >ttlrc &&
full=$(git -C repos/rob rev-parse HEAD) &&
- CGIT_CONFIG="$PWD/ttlrc" QUERY_STRING="url=rob/refs/&id=$full" cgit >tmp &&
+ cgit_rc ttlrc "url=rob/refs/&id=$full" >tmp &&
git -C repos/rob tag later HEAD &&
- CGIT_CONFIG="$PWD/ttlrc" QUERY_STRING="url=rob/refs/&id=$full" cgit >tmp &&
+ cgit_rc ttlrc "url=rob/refs/&id=$full" >tmp &&
grep ">later<" tmp
'
@@ -507,15 +480,15 @@ test_expect_success 'the diff caps hold when the path names a directory' '
echo "max-diff-files=2" &&
cat robrc
} >caprc &&
- CGIT_CONFIG="$PWD/caprc" QUERY_STRING="url=rob/diff/dir" cgit >tmp &&
+ cgit_rc caprc "url=rob/diff/dir" >tmp &&
grep "too large to be rendered inline" tmp &&
- CGIT_CONFIG="$PWD/caprc" QUERY_STRING="url=rob/diff/dir/f1" cgit >tmp &&
+ cgit_rc caprc "url=rob/diff/dir/f1" >tmp &&
! grep "too large to be rendered inline" tmp
'
test_expect_success 'a snapshot of a tag spelled like an option is archived' '
git -C repos/rob update-ref refs/tags/-l HEAD &&
- robq "url=rob/snapshot/-l.tar.gz" >tmp &&
+ cgit_rc robrc "url=rob/snapshot/-l.tar.gz" >tmp &&
grep "^Status: 200" tmp &&
strip_headers <tmp | gzip -dc | tar -tf - >list &&
grep "file-1" list
@@ -542,9 +515,9 @@ test_expect_success 'a disk readme in the repository directory serves no sibling
cat robrc &&
echo "repo.readme=README"
} >diskrc &&
- CGIT_CONFIG="$PWD/diskrc" QUERY_STRING="url=rob/about/" cgit >tmp &&
+ cgit_rc diskrc "url=rob/about/" >tmp &&
grep "hello" tmp &&
- CGIT_CONFIG="$PWD/diskrc" QUERY_STRING="url=rob/about/config" cgit >tmp &&
+ cgit_rc diskrc "url=rob/about/config" >tmp &&
grep "^Status: 404" tmp &&
! grep "repositoryformatversion" tmp
'
@@ -552,23 +525,18 @@ test_expect_success 'a disk readme in the repository directory serves no sibling
test_expect_success 'a tag pointing at a tag links the inner tag by id' '
git -C repos/rob -c tag.gpgsign=false tag -a -m outer outer ann &&
inner=$(git -C repos/rob rev-parse ann) &&
- robq "url=rob/tag/&id=$inner" >tmp &&
+ cgit_rc robrc "url=rob/tag/&id=$inner" >tmp &&
grep "^Status: 200" tmp &&
grep "tagged object" tmp
'
test_expect_success 'the dumb transport serves an empty repository' '
git init -q --bare empty.git &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=empty" &&
- echo "repo.path=$PWD/empty.git"
- } >emptyrc &&
- CGIT_CONFIG="$PWD/emptyrc" QUERY_STRING="url=empty/info/refs" cgit >tmp &&
+ cgitrc_repo emptyrc empty "repo.path=$PWD/empty.git" &&
+ cgit_rc emptyrc "url=empty/info/refs" >tmp &&
grep "^Status: 200" tmp &&
grep "^Content-Type: text/plain" tmp &&
- CGIT_CONFIG="$PWD/emptyrc" QUERY_STRING="url=empty/objects/info" cgit >tmp &&
+ cgit_rc emptyrc "url=empty/objects/info" >tmp &&
grep "^Status: 404" tmp
'
@@ -576,10 +544,10 @@ test_expect_success 'the dumb transport serves an empty repository' '
# its size up front and compare what arrived against it.
test_expect_success 'the dumb transport sends a file with its size' '
git -C repos/rob repack -q -d &&
- robq "url=rob/objects/info/packs" >tmp &&
+ cgit_rc robrc "url=rob/objects/info/packs" >tmp &&
pack=$(strip_headers <tmp | sed -n "s/^P //p" | head -1) &&
test -n "$pack" &&
- robq "url=rob/objects/pack/$pack" >tmp &&
+ cgit_rc robrc "url=rob/objects/pack/$pack" >tmp &&
grep "^Status: 200" tmp &&
size=$(wc -c <repos/rob/.git/objects/pack/$pack | tr -d " ") &&
grep "^Content-Length: $size$" tmp &&
@@ -595,43 +563,38 @@ test_expect_success 'the dumb transport withholds the alternates file' '
for path in info/alternates info//alternates info/./alternates ./info/alternates \
info/Alternates /info/alternates
do
- robq "url=rob/objects/$path" >tmp &&
+ cgit_rc robrc "url=rob/objects/$path" >tmp &&
grep "^Status: 404" tmp &&
! grep "repos/foo" tmp || return 1
done &&
rm repos/rob/.git/objects/info/alternates &&
- robq "url=rob/objects/info/packs" >tmp &&
+ cgit_rc robrc "url=rob/objects/info/packs" >tmp &&
grep "^Status: 200" tmp
'
# A reftable repository keeps only a placeholder in its HEAD file, so the
# branch a client is to check out has to come from the refs instead.
test_expect_success 'the dumb transport answers HEAD from the refs' '
- robq "url=rob/HEAD" >tmp &&
+ cgit_rc robrc "url=rob/HEAD" >tmp &&
grep "^Status: 200" tmp &&
strip_headers <tmp >head &&
echo "ref: refs/heads/master" >expect &&
test_cmp expect head &&
git init -q --bare --ref-format=reftable -b trunk rt.git &&
git -C repos/rob push -q "$PWD/rt.git" master:trunk &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=rt" &&
- echo "repo.path=$PWD/rt.git"
- } >rtrc &&
- CGIT_CONFIG="$PWD/rtrc" QUERY_STRING="url=rt/HEAD" cgit >tmp &&
+ cgitrc_repo rtrc rt "repo.path=$PWD/rt.git" &&
+ cgit_rc rtrc "url=rt/HEAD" >tmp &&
strip_headers <tmp >head &&
echo "ref: refs/heads/trunk" >expect &&
test_cmp expect head &&
- CGIT_CONFIG="$PWD/emptyrc" QUERY_STRING="url=empty/HEAD" cgit >tmp &&
+ cgit_rc emptyrc "url=empty/HEAD" >tmp &&
strip_headers <tmp >head &&
echo "ref: refs/heads/master" >expect &&
test_cmp expect head
'
test_expect_success 'a reftable repository shows its age on the index' '
- CGIT_CONFIG="$PWD/rtrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc rtrc "url=" >tmp &&
grep "<td class=.col-idle.><time " tmp
'
@@ -645,7 +608,7 @@ test_expect_success 'a client that disconnects ends the request quietly' '
git add long.txt &&
git commit -m long
) &&
- { robq "url=rob/tree/long.txt" 2>err; echo $? >status; } | head -c 1 >/dev/null &&
+ { cgit_rc robrc "url=rob/tree/long.txt" 2>err; echo $? >status; } | head -c 1 >/dev/null &&
test "$(cat status)" = 0 &&
! grep -i "broken pipe" err &&
! grep "die()" err
@@ -655,20 +618,18 @@ test_expect_success 'a client that disconnects ends the request quietly' '
# shared with other visitors has to be told, while a site without a filter
# keeps its pages free of any such header.
test_expect_success 'pages behind an auth filter are marked private' '
- cat >letin.sh <<-\EOF &&
- #!/bin/sh
+ write_script letin.sh <<-\EOF &&
exit 1
EOF
- chmod +x letin.sh &&
{
echo "auth-filter=exec:$PWD/letin.sh" &&
cat robrc
} >letinrc &&
- CGIT_CONFIG="$PWD/letinrc" QUERY_STRING="url=rob/log/" cgit >tmp &&
+ cgit_rc letinrc "url=rob/log/" >tmp &&
grep "^Status: 200" tmp &&
grep "^Cache-Control: private$" tmp &&
grep "^Vary: Cookie$" tmp &&
- robq "url=rob/log/" >tmp &&
+ cgit_rc robrc "url=rob/log/" >tmp &&
! grep "^Cache-Control" tmp &&
! grep "^Vary" tmp
'
@@ -677,10 +638,10 @@ test_expect_success 'pages behind an auth filter are marked private' '
# the branch asked for has to survive that hop.
test_expect_success 'the about redirect keeps the query string' '
git -C repos/rob branch other HEAD &&
- robq "url=rob/about&h=other&x=1" >tmp &&
+ cgit_rc robrc "url=rob/about&h=other&x=1" >tmp &&
grep "^Status: 302" tmp &&
grep "^Location: /rob/?h=other&x=1$" tmp &&
- CGIT_CONFIG="$PWD/aboutrc" QUERY_STRING="url=rob/about&h=other" cgit >tmp &&
+ cgit_rc aboutrc "url=rob/about&h=other" >tmp &&
grep "^Status: 200" tmp
'
@@ -692,9 +653,9 @@ test_expect_success 'the readme follows an annotated tag as the head' '
cat robrc &&
echo "repo.readme=:file-1"
} >tagreadmerc &&
- CGIT_CONFIG="$PWD/tagreadmerc" QUERY_STRING="url=rob/about/&h=v1" cgit >tmp &&
+ cgit_rc tagreadmerc "url=rob/about/&h=v1" >tmp &&
grep "^Status: 200" tmp &&
- CGIT_CONFIG="$PWD/tagreadmerc" QUERY_STRING="url=rob/tree/&h=v1" cgit >tmp &&
+ cgit_rc tagreadmerc "url=rob/tree/&h=v1" >tmp &&
grep ">about<" tmp
'
@@ -704,18 +665,6 @@ test_expect_success 'the about redirect encodes the repository url once' '
grep "^Location: /foo%2Bbar/$" tmp
'
-# The about page shows the readme alone and its links lead into the tree,
-# so a path below it names nothing, the site about page included.
-test_expect_success 'a path below the about page answers 404' '
- for path in file-1 sub/ ../robrc
- do
- CGIT_CONFIG="$PWD/aboutrc" QUERY_STRING="url=rob/about/$path" cgit >tmp &&
- grep "^Status: 404" tmp || return 1
- done &&
- CGIT_CONFIG="$PWD/aboutrc" QUERY_STRING="p=about&path=x" cgit >tmp &&
- grep "^Status: 404" tmp
-'
-
test_expect_success 'a symlink whose target is a large blob is listed without it' '
big=$(head -c 5000 /dev/zero | tr "\0" a | git -C repos/rob hash-object -w --stdin) &&
(
@@ -723,7 +672,7 @@ test_expect_success 'a symlink whose target is a large blob is listed without it
git update-index --add --cacheinfo 120000,$big,biglink &&
git commit -m biglink
) &&
- robq "url=rob/tree/" >tmp &&
+ cgit_rc robrc "url=rob/tree/" >tmp &&
grep "biglink" tmp &&
! grep "aaaaaaaaaa" tmp
'
@@ -737,7 +686,7 @@ test_expect_success 'a chain of single directories is followed a bounded number
git add d &&
git commit -m deep
) &&
- robq "url=rob/tree/" >tmp &&
+ cgit_rc robrc "url=rob/tree/" >tmp &&
test $(grep -o "class=.ls-dir." tmp | wc -l) -le 17
'
@@ -752,7 +701,7 @@ test_expect_success 'an unreadable project list is named with its error' '
echo "project-list=$PWD/projdir" &&
echo "scan-path=$PWD/repos"
} >projrc &&
- CGIT_CONFIG="$PWD/projrc" QUERY_STRING="url=" cgit >tmp 2>err &&
+ cgit_rc projrc "url=" >tmp 2>err &&
grep "project list .*projdir: Is a directory" err
'
@@ -765,7 +714,7 @@ test_expect_success SANITY 'a scan entry that cannot be searched is named in ful
echo "cache-size=0" &&
echo "scan-path=$PWD/scanperm"
} >scanpermrc &&
- CGIT_CONFIG="$PWD/scanpermrc" QUERY_STRING="url=" cgit >tmp 2>err &&
+ cgit_rc scanpermrc "url=" >tmp 2>err &&
grep "Unable to stat .*scanperm/locked/objects: Permission denied" err
'
@@ -780,7 +729,7 @@ test_expect_success 'a readme past max-blob-size is reported on the about page'
cat robrc &&
echo "repo.readme=:big-readme"
} >bigreadmerc &&
- CGIT_CONFIG="$PWD/bigreadmerc" QUERY_STRING="url=rob/about/" cgit >tmp &&
+ cgit_rc bigreadmerc "url=rob/about/" >tmp &&
grep "The readme is larger than max-blob-size (1 KB)" tmp
'
@@ -788,10 +737,9 @@ test_expect_success 'a readme past max-blob-size is reported on the about page'
# that was just refused must not be looked up to get there, since the wider
# revision syntax walks the whole history for a match.
test_expect_success 'the error page does not resolve the id it refused' '
- sha=$(git -C repos/rob rev-parse HEAD~1) &&
- robq "url=rob/log/&id=$sha" >tmp &&
+ cgit_rc robrc "url=rob/log/&id=$sha" >tmp &&
grep "<title>.* @ " tmp &&
- robq "url=rob/log/&id=%3A/commit" >tmp &&
+ cgit_rc robrc "url=rob/log/&id=%3A/commit" >tmp &&
grep "^Status: 400" tmp &&
! grep "<title>.* @ " tmp
'
@@ -816,22 +764,17 @@ test_expect_success 'odd dates and zones in pushed objects render' '
git hash-object -t commit -w --literally --stdin) &&
git update-ref refs/heads/zone $zone
) &&
- {
- echo "virtual-root=/" &&
- echo "cache-size=0" &&
- echo "repo.url=dates" &&
- echo "repo.path=$PWD/repos/dates/.git"
- } >datesrc &&
+ cgitrc_repo datesrc dates &&
for q in "url=dates/" "url=dates/refs/" "url=dates/tag/&h=early" \
"url=dates/log/&h=far" "url=dates/commit/&h=far" "url=dates/commit/&h=zone" \
"url=dates/atom/&h=far"
do
- CGIT_CONFIG="$PWD/datesrc" QUERY_STRING="$q" cgit >tmp 2>err &&
+ cgit_rc datesrc "$q" >tmp 2>err &&
grep "^Status: 200" tmp &&
! grep "Unable to complete" tmp &&
! grep "Timestamp" err || return 1
done &&
- CGIT_CONFIG="$PWD/datesrc" QUERY_STRING="url=dates/commit/&h=far" cgit >tmp &&
+ cgit_rc datesrc "url=dates/commit/&h=far" >tmp &&
grep "1970-01-01" tmp
'
@@ -848,7 +791,7 @@ test_expect_success 'a notes ref that names no tree is passed over' '
for q in "url=dates/" "url=dates/log/" "url=dates/refs/" "url=dates/tree/" \
"url=dates/snapshot/dates-base.tar.asc"
do
- CGIT_CONFIG="$PWD/notesrc" QUERY_STRING="$q" cgit >tmp 2>err &&
+ cgit_rc notesrc "$q" >tmp 2>err &&
! grep "Unable to complete" tmp &&
! grep "notes tree" err || return 1
done &&
@@ -861,7 +804,7 @@ test_expect_success 'a scanned defbranch cannot name a path outside the reposito
mkdir -p secret &&
touch -t 200001010000 secret/file &&
echo "defbranch=../../../../secret/file" >scan/b.git/cgitrc &&
- CGIT_CONFIG="$PWD/gitcfgrc" QUERY_STRING="url=" cgit >tmp &&
+ cgit_rc gitcfgrc "url=" >tmp &&
grep ">b.git</a>" tmp &&
! grep "data-ut=.946" tmp
'
diff --git a/tests/t0401-serve.sh b/tests/t0401-serve.sh
index 5a2b9b6..341bbb6 100755
--- a/tests/t0401-serve.sh
+++ b/tests/t0401-serve.sh
@@ -1,11 +1,9 @@
#!/bin/sh
# Checks how tools/serve.py, the preview server that runs the built binary and
-# hands its output back over HTTP, tells the CGI headers from the body. That
-# split once keyed on the wrong blank line and swallowed the document head,
-# which left a page that still validated but arrived with no stylesheet and no
-# chrome. The cases live in serve-split-check.py beside this file, and nothing
-# here touches a repository, so the fixtures stay unbuilt.
+# hands its output back over HTTP, tells the CGI headers from the body. The
+# cases live in serve-split-check.py beside this file, and nothing here touches
+# a repository, so the fixtures stay unbuilt.
test_description='Check the preview server splits CGI output correctly'
CGIT_TEST_NO_CREATE_REPOS=YesPlease
@@ -14,7 +12,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
command -v python3 >/dev/null 2>&1 || {
skip_all='Skipping preview server tests: python3 not found'
test_done
- exit
}
test_expect_success 'CGI headers and body are split at the first blank line' '
diff --git a/tests/t0501-about-render.sh b/tests/t0501-about-render.sh
index 339c5e2..e4eaf5c 100755
--- a/tests/t0501-about-render.sh
+++ b/tests/t0501-about-render.sh
@@ -13,10 +13,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
test_expect_success "unit checks under $lua" "
@@ -77,18 +73,13 @@ test_expect_success 'point cgit at it through the about filter' '
EOF
'
-if test "$CGIT_HAS_LUA" -eq 1
+if cgit_lua_probe "$PWD/repos/md/.git" lpeg
+then
+ test_set_prereq CGIT_LUA_LPEG
+elif test "$CGIT_HAS_LUA" -eq 1
then
- test_set_prereq CGIT_LUA
- if cgit_lua_probe "$PWD/repos/md/.git" lpeg
- then
- test_set_prereq CGIT_LUA_LPEG
- else
- test_set_prereq CGIT_LUA_NOLPEG
- say 'cgit lua has no lpeg, expecting the plain text fallback'
- fi
-else
- say 'cgit built without lua, filter checks through cgit skipped'
+ test_set_prereq CGIT_LUA_NOLPEG
+ say 'cgit lua has no lpeg, expecting the plain text fallback'
fi
test_expect_success CGIT_LUA_LPEG 'the about page renders the markdown' '
@@ -109,13 +100,13 @@ test_expect_success CGIT_LUA_LPEG 'a relative link leads into the tree' '
'
test_expect_success CGIT_LUA_LPEG 'the link stays on the branch shown' '
- cgit_query "url=md/about/&h=next" >tmp &&
+ cgit_url "md/about/&h=next" >tmp &&
grep "<a href=./md/tree/ROADMAP.md?h=next#plan.>roadmap</a>" tmp
'
test_expect_success CGIT_LUA_LPEG 'the about page renders a man page' '
cgit_url "man/about/" >tmp &&
- grep "<div class=.markdown manpage.><h2 id=.name.>NAME</h2>" tmp &&
+ grep "<div class=.markdown.><h2 id=.name.>NAME</h2>" tmp &&
grep "tool - does one thing — well" tmp &&
grep "<strong>-v</strong><br>" tmp &&
grep "<pre><code>tool -v &lt;file&gt;" tmp &&
diff --git a/tests/t0502-syntax-highlight.sh b/tests/t0502-syntax-highlight.sh
index ced9edd..2ca3c9f 100755
--- a/tests/t0502-syntax-highlight.sh
+++ b/tests/t0502-syntax-highlight.sh
@@ -7,17 +7,14 @@
# fake lexers found and once with an empty directory so the escaped fallback
# is what everything renders through. The run through cgit itself places the
# fake lexers beside the config file, which is the probe the filter documents
-# for a scintillua directory next to cgitrc.
+# for a scintillua directory next to cgitrc, and then points the lexer path
+# at nothing to see the page fall back to escaped text.
test_description='Check the shipped syntax-highlight extension'
CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
test_expect_success "highlighting checks under $lua" "
@@ -34,10 +31,6 @@ do
"
done
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
-test "$CGIT_HAS_LUA" -eq 1 ||
- say 'cgit built without lua, filter checks through cgit skipped'
-
test_expect_success 'create a repository holding a lexable file' '
test_create_repo repos/fake &&
(
@@ -70,6 +63,16 @@ test_expect_success CGIT_LUA 'bytes between tokens reach the page escaped' '
grep "<span class=.hl-number.>42</span> &amp; " tmp
'
+# A missing scintillua leaves the filter with nothing to highlight with, and
+# a filter that failed there would take the whole page down with it, so it
+# has to hand the source back escaped instead.
+test_expect_success CGIT_LUA 'without scintillua the source is served escaped' '
+ CGIT_SCINTILLUA_PATH=/nonexistent CGIT_CONFIG="$PWD/cgitrc" \
+ QUERY_STRING="url=fake/tree/hello.fake" cgit >tmp &&
+ grep "if 42 &amp; zz" tmp &&
+ ! grep "hl-keyword" tmp
+'
+
test_expect_success CGIT_LUA 'a lone carriage return reaches the page as an escape' '
(
cd repos/fake &&
diff --git a/tests/t0503-link-commits.sh b/tests/t0503-link-commits.sh
index e6e8816..d38093a 100755
--- a/tests/t0503-link-commits.sh
+++ b/tests/t0503-link-commits.sh
@@ -11,10 +11,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
test_expect_success "unit checks under $lua" "
@@ -22,10 +18,6 @@ do
"
done
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
-test "$CGIT_HAS_LUA" -eq 1 ||
- say 'cgit built without lua, filter checks through cgit skipped'
-
test_expect_success 'create a repository whose commit message holds matches' '
test_create_repo repos/linky &&
(
diff --git a/tests/t0504-email-avatar.sh b/tests/t0504-email-avatar.sh
index ec1f5da..60db6d6 100755
--- a/tests/t0504-email-avatar.sh
+++ b/tests/t0504-email-avatar.sh
@@ -14,10 +14,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
for script in email-gravatar email-libravatar
@@ -48,18 +44,14 @@ test_expect_success 'create a repository with one commit' '
)
'
-if test "$CGIT_HAS_LUA" -eq 1 &&
- cgit_lua_probe "$PWD/repos/avatar/.git" openssl.digest
+if cgit_lua_probe "$PWD/repos/avatar/.git" openssl.digest
then
test_set_prereq CGIT_LUA_OSSL
elif test "$CGIT_HAS_LUA" -eq 1
then
test_set_prereq CGIT_LUA_NOOSSL
say 'cgit lua has no luaossl, expecting names without icons'
-else
- say 'cgit built without lua, filter checks through cgit skipped'
fi
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
test_expect_success CGIT_LUA 'point cgit at it through the email filter' '
cat >cgitrc <<-EOF
diff --git a/tests/t0505-auth.sh b/tests/t0505-auth.sh
index be0f0ce..7dd4a36 100755
--- a/tests/t0505-auth.sh
+++ b/tests/t0505-auth.sh
@@ -13,10 +13,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
for variant in inline file
@@ -38,17 +34,11 @@ test_expect_success 'create a repository with one commit' '
'
if test "$CGIT_HAS_LUA" -eq 1 &&
- cgit_lua_probe "$PWD/repos/authy/.git" openssl.rand openssl.hmac posix.sys.stat posix.unistd
-then
- test_set_prereq CGIT_LUA_AUTH
-elif test "$CGIT_HAS_LUA" -eq 1
+ ! cgit_lua_probe "$PWD/repos/authy/.git" openssl.rand openssl.hmac posix.sys.stat posix.unistd
then
test_set_prereq CGIT_LUA_NOAUTH
say 'cgit lua lacks luaossl or luaposix, expecting protected pages refused'
-else
- say 'cgit built without lua, checks through cgit skipped'
fi
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
test_expect_success CGIT_LUA 'point cgit at the unedited auth filter' '
cat >cgitrc <<-EOF
@@ -79,7 +69,7 @@ test_expect_success CGIT_LUA_NOAUTH 'without the modules a protected repository
repo.url=authy
repo.path=$PWD/repos/authy/.git
EOF
- CGIT_CONFIG="$PWD/guardedrc" QUERY_STRING="url=authy/log/" cgit >tmp &&
+ cgit_rc guardedrc "url=authy/log/" >tmp &&
! grep "guarded commit" tmp &&
grep "Unable to complete the request" tmp
'
diff --git a/tests/t0506-link-trailers.sh b/tests/t0506-link-trailers.sh
index c2e4f91..03db63a 100755
--- a/tests/t0506-link-trailers.sh
+++ b/tests/t0506-link-trailers.sh
@@ -11,10 +11,6 @@ CGIT_TEST_NO_CREATE_REPOS=YesPlease
. ./setup.sh
. "$TEST_OUTPUT_DIRECTORY/extensions/lib.sh"
-interpreters=$(ext_lua_interpreters 5)
-test -z "$interpreters" &&
- say 'no standalone lua on the path, unit checks skipped'
-
for lua in $interpreters
do
test_expect_success "unit checks under $lua" "
@@ -22,10 +18,6 @@ do
"
done
-test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
-test "$CGIT_HAS_LUA" -eq 1 ||
- say 'cgit built without lua, filter checks through cgit skipped'
-
test_expect_success 'create a repository whose commit carries linkable trailers' '
test_create_repo repos/linky &&
(
diff --git a/tests/t0507-hooks.sh b/tests/t0507-hooks.sh
index 8a45f81..ec31546 100755
--- a/tests/t0507-hooks.sh
+++ b/tests/t0507-hooks.sh
@@ -32,8 +32,8 @@ test_expect_success 'set up a cache of two repositories and the index' '
CGIT_CONFIG="$PWD/cachedrc" PATH_INFO="$page" QUERY_STRING= cgit >/dev/null ||
return 1
done &&
- CGIT_CONFIG="$PWD/cachedrc" QUERY_STRING="url=a.git/refs/" cgit >/dev/null &&
- CGIT_CONFIG="$PWD/cachedrc" QUERY_STRING="url=b.git/refs/" cgit >/dev/null &&
+ cgit_rc cachedrc "url=a.git/refs/" >/dev/null &&
+ cgit_rc cachedrc "url=b.git/refs/" >/dev/null &&
echo writer >slots/0123abcd.lock &&
cp -R slots pristine &&
sed -e "s|^cache_root=.*|cache_root='\''$PWD/slots'\''|" \
@@ -115,6 +115,29 @@ do
"
done
+for shell in $shells
+do
+ test_expect_success SANITY "the age hook shows a pusher no error under $shell" "
+ echo kept >kept &&
+ cp kept plain.git/info/web/last-modified &&
+ test_when_finished 'chmod 755 plain.git/info/web' &&
+ chmod 555 plain.git/info/web &&
+ (cd plain.git && $shell \"\$HOOK_DIRECTORY/post-receive.cgit-age\" 2>../err) &&
+ test_must_be_empty err &&
+ test_cmp kept plain.git/info/web/last-modified
+ "
+done
+
+test_expect_success SANITY 'the age hook shows no error when it cannot make its directory' '
+ git init -q --bare closed.git &&
+ git -C work push -q ../closed.git HEAD:refs/heads/main &&
+ test_when_finished "chmod 755 closed.git/info" &&
+ chmod 555 closed.git/info &&
+ (cd closed.git && sh "$HOOK_DIRECTORY/post-receive.cgit-age" 2>../err) &&
+ test_must_be_empty err &&
+ test_path_is_missing closed.git/info/web
+'
+
# git keeps a shared repository group writable whatever the pusher's umask,
# and the next member to push has to be able to replace the file.
test_expect_success SANITY 'the age hook keeps a shared directory group writable' '