#!/bin/sh # The tree page. The checks against foo+bar cover a file name and a branch # name that both contain a plus, which cgit percent-encodes in a path as # well as in a query, since a bare plus in a served path reads back as a # space through a query string. test_description='Check content on tree page' . ./setup.sh test_expect_success 'generate bar/tree' 'cgit_url "bar/tree" >tmp' test_expect_success 'find file-1' 'grep ">file-1<" tmp' test_expect_success 'find file-50' 'grep "file-50" tmp' test_expect_success 'the links column has a name' ' grep "" tmp ' test_expect_success 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >tmp' test_expect_success 'find line 1' ' grep "1" tmp ' test_expect_success 'no line 2' ' ! grep "2" tmp ' # The table only sets the numbers beside the code, so a screen reader is told # to read it as layout rather than as a table of data. test_expect_success 'the file table is read as layout' ' grep "" tmp ' # The numbers and the code are two columns of text that a browser keeps in # step only while both are set in one font. Only a browser shows the drift, # so this checks the rule that prevents it. test_expect_success 'the code is set in the font of its line numbers' ' sed -n "/^div#cgit table.blob code {/,/^}/p" \ "$TEST_OUTPUT_DIRECTORY/../assets/cgit.css" >rule && grep "^ font: inherit;$" rule ' # A browser starts a new line at more characters than git ends one at, and # each would push the rest of the code a line below its numbers. test_expect_success 'set up a file holding the characters a browser breaks at' ' test_create_repo repos/breaks && ( cd repos/breaks && printf "a\\rb\\vc\\fd\\302\\205e\\342\\200\\250f\\342\\200\\251g\\nh\\r\\ni\\r" >breaks.txt && git add breaks.txt && git commit -m breaks ) && cgitrc_repo breaksrc breaks enable-blame=1 ' test_expect_success 'each of them is shown as an escape in the tree' ' cgit_rc breaksrc "url=breaks/tree/breaks.txt" >tmp && grep "a\\\\rb\\\\vc" tmp && grep "c\\\\fd\\\\u0085e" tmp && grep "e\\\\u2028f\\\\u2029g$" tmp ' test_expect_success 'only a carriage return before a line feed is left as it is' ' printf "h\\r\\n" >expect && grep "^h" tmp >actual && test_cmp expect actual && grep "^i\\\\r" tmp ' test_expect_success 'the numbers count only the lines git counts' ' grep "3" tmp && ! grep "4" tmp ' test_expect_success 'the blame page escapes them the same way' ' cgit_rc breaksrc "url=breaks/blame/breaks.txt" >tmp && grep "a\\\\rb\\\\vc" tmp && grep "e\\\\u2028f\\\\u2029g$" tmp && grep "^i\\\\r" tmp ' # The striped background is padded with spaces to the widest line, which is # 31 columns once its six escapes are written out, though it is 18 bytes. test_expect_success 'the blame stripes are as wide as the escaped line' ' grep "^ \\{31\\}" tmp ' # Blame takes a file alone, so on its page the crumbs for the folders above # the file lead to the tree. test_expect_success 'the folder crumbs of a blame page lead to the tree' ' mkrepo repos/nested 1 && ( cd repos/nested && mkdir -p a/b && echo x >a/b/file.txt && git add a && git commit -m nested ) && cgitrc_repo nestedrc nested enable-blame=1 && cgit_rc nestedrc "url=nested/blame/a/b/file.txt" >tmp && grep "path: root/a/b/file.txt" tmp ' # A commit's lines link to the file as the parent had it, under the name it # had there, and the commit that created the file has nothing to link to. test_expect_success 'blame links a commit to the file before it' ' mkrepo repos/renamed 1 && ( cd repos/renamed && test_seq 1 10 >a.txt && git add a.txt && git commit -m "add a.txt" && git mv a.txt b.txt && test_seq 1 9 >b.txt && echo ten >>b.txt && git commit -am "rename a.txt" ) && cgitrc_repo renamedrc renamed enable-blame=1 && cgit_rc renamedrc "url=renamed/blame/b.txt" >tmp && added=$(git -C repos/renamed rev-parse HEAD~1) && grep "title=.Blame the previous revision. href=./renamed/blame/a.txt?id=$added.>^" tmp && test $(grep -c "Blame the previous revision" tmp) = 1 ' test_expect_success 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >tmp' test_expect_success 'verify a+b link' ' grep "/foo%2Bbar/tree/a%2Bb" tmp ' test_expect_success 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >tmp' test_expect_success 'verify a+b?h=1+2 link' ' grep "/foo%2Bbar/tree/a%2Bb?h=1%2B2" tmp ' test_expect_success 'set up a repo with a nested submodule' ' mkrepo repos/submodules 1 && ( cd repos/submodules && sub=$(git rev-parse HEAD) && git update-index --add --cacheinfo 160000,$sub,vendor/sub && git commit -m gitlink ) && cgitrc_repo submodrc submodules enable-blame=1 ' test_expect_success 'generate submodules/tree/vendor' ' cgit_rc submodrc "url=submodules/tree/vendor" >tmp ' test_expect_success 'a submodule row shows the entry name alone' ' grep "class=.ls-mod.>sub" tmp && ! grep ">vendor/sub" tmp ' test_expect_success 'the submodule hash is titled with the full id' ' sub=$(git -C repos/submodules rev-parse HEAD~1) && grep "class=.ls-mod-hash. title=.$sub.>" tmp ' test_expect_success 'a submodule row shows no size' ' grep "ls-mod" tmp >row && grep "class=.ls-size.>-" row ' test_expect_success 'a submodule row offers log but not blame or plain' ' grep ">log" row && ! grep ">blame" row && ! grep ">plain" row ' test_expect_success 'a file row still offers blame' ' cgit_rc submodrc "url=submodules/tree/" >tmp && grep ">blame" tmp ' # A submodule's commit lives in another repository, so its path points there # the way its row in the listing does. test_expect_success 'a submodule path is answered with the submodule' ' sub=$(git -C repos/submodules rev-parse HEAD~1) && cgit_rc submodrc "url=submodules/tree/vendor/sub" >tmp && grep "^Status: 404" tmp && grep "
This path is a submodule: sub @ " tmp ' # A link target inside the tree becomes a link, while an absolute one names a # file on whatever machine checks the tree out, not a path in the tree. test_expect_success 'only a link target inside the tree is linked' ' test_create_repo repos/links && ( cd repos/links && echo hi >target.txt && git add target.txt && rel=$(printf target.txt | git hash-object -w --stdin) && abs=$(printf /etc/passwd | git hash-object -w --stdin) && git update-index --add --cacheinfo 120000,$rel,rel && git update-index --add --cacheinfo 120000,$abs,abs && git commit -m links ) && cgitrc_repo linksrc links && cgit_rc linksrc "url=links/tree/" >tmp && grep " -> target.txt" tmp && grep " -> /etc/passwd" tmp && ! grep "href=./links/tree/etc/passwd" tmp ' # Blame takes a regular file alone, so a symlink is not offered it. test_expect_success 'a symlink is not offered blame' ' cgitrc_repo linksblamerc links enable-blame=1 && cgit_rc linksblamerc "url=links/tree/" >tmp && grep "href=./links/blame/target.txt.>blame" tmp && ! grep "href=./links/blame/rel.>blame" tmp && cgit_rc linksblamerc "url=links/tree/rel" >tmp && grep "
" tmp && ! grep ">blame" tmp ' # A row follows a chain of single directories for a set number of levels and # stops on the last one it links, with no separator left hanging after it. test_expect_success 'a long directory chain ends on a link' ' test_create_repo repos/deep && ( cd repos/deep && p=d1 && for i in $(test_seq 2 17) do p=$p/d$i || return 1 done && mkdir -p $p && echo deep >$p/file && git add . && git commit -m deep ) && cgitrc_repo deeprc deep && cgit_rc deeprc "url=deep/tree/" >tmp && grep ">d15" tmp && ! grep " / " 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 1700000000 +0000\\ncommitter A 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" tmp && grep "href=./cut/log/a?h=odd.>log" tmp ' test_expect_success 'a binary file in the tree shows as a hex dump' ' test_create_repo repos/bin && ( cd repos/bin && printf "\\000\\001\\002bin" >blob.bin && git add blob.bin && git commit -m binary ) && cgitrc_repo binrc bin && cgit_rc binrc "url=bin/tree/blob.bin" >tmp && grep "
" tmp && grep "" tmp && ! grep "" tmp && grep "
Only the first 64 KB of this file are shown. View the whole file.
" tmp ' # The blame page refuses a file git counts as binary and one past # max-blob-size, so a listing offers blame for neither. test_expect_success 'a listing offers blame only where it works' ' ( cd repos/bin && echo hello >small.txt && test_seq 1 2000 >large.txt && git add small.txt large.txt && git commit -m "text files" ) && cgitrc_repo binblamerc bin enable-blame=1 max-blob-size=4 && cgit_rc binblamerc "url=bin/tree/" >tmp && grep "href=./bin/blame/small.txt.>blame" tmp && ! grep "href=./bin/blame/large.txt.>blame" tmp && ! grep "href=./bin/blame/blob.bin.>blame" tmp && ! grep "href=./bin/blame/big.bin.>blame" tmp ' test_done
0000 00 01 02 62 69 6e" tmp && ! grep "class=.truncated." tmp ' # The dump scrolls sideways inside its own table, which a keyboard reaches # by focusing the table, so the table takes the focus ring links have. test_expect_success 'a focused hex dump shows the focus ring' ' sed -n "/^div#cgit a:focus-visible,$/,/^}/p" \ "$TEST_OUTPUT_DIRECTORY/../assets/cgit.css" >rule && grep "^div#cgit table.bin-blob:focus-visible" rule && grep "^ outline: solid 2px var(--link);$" rule ' # Each byte of the dump costs several bytes of markup, so a large binary is # dumped up to its first 64 KB and links to the whole file. test_expect_success 'a large binary is dumped only in part' ' ( cd repos/bin && printf "\\000" >big.bin && test_seq 1 20000 >>big.bin && git add big.bin && git commit -m "large binary" ) && cgit_rc binrc "url=bin/tree/big.bin" >tmp && grep "ffe010000