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
6 files changed, 102 insertions, 164 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()