#!/bin/sh # 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 # The three counting tests below visit the same pages, the index plus six # pages in each of two repositories, so the counts they expect stay tied to # this one list of thirteen requests. request_all_pages() { cgit_url "" && for repo in foo bar do for page in "" /refs /tree /log /diff /patch do cgit_url "$repo$page" || return 1 done done } set_cache_size() { sed -e "s/^cache-size=.*$/cache-size=$1/" cgitrc >cgitrc.tmp && mv -f cgitrc.tmp cgitrc } 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_expect_success 'verify cache-size=1' ' rm -f cache/* && set_cache_size 1 && request_all_pages && ls cache >output && test_line_count = 1 output ' test_expect_success 'verify cache-size=1021' ' rm -f cache/* && set_cache_size 1021 && request_all_pages && ls cache >output && test_line_count = 13 output && cgit_url "foo/ls_cache" >output.full && strip_headers output && test_line_count = 13 output && cgit_url "foo/ls_cache" >output.second && test_cmp output.full output.second ' # Page output is buffered, so a slot only holds the whole page if the buffer is # emptied before the size of what was generated is taken. The blob set up here # is larger than one buffer, so a missing flush would leave the cached copy cut # short. test_expect_success 'set up a repo with a page larger than the output buffer' ' mkrepo repos/bigpage 1 && ( cd repos/bigpage && awk "BEGIN{for(i=0;i<20000;i++) print \"line \" i \" of the big file\"}" >big.txt && git add big.txt && git commit -m "add big.txt" ) && { echo "virtual-root=/" && echo "cache-root=$PWD/cache2" && echo "cache-size=64" && echo "enable-tree-linenumbers=1" && echo "repo.url=bigpage" && echo "repo.path=$PWD/repos/bigpage/.git" } >bigrc && rm -rf cache2 && mkdir cache2 ' 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 && test $(wc -c big.second && strip_headers big.first.body && strip_headers big.second.body && test_cmp big.first.body big.second.body ' 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 && strip_headers big.nocache.body && # The footer carries the time the page was generated, which the two runs # cannot agree on. sed -e "s/generated by .*//" big.nocache.body >big.nocache.timeless && sed -e "s/generated by .*//" big.second.body >big.cached.timeless && test_cmp big.nocache.timeless big.cached.timeless ' test_expect_success 'the page ends where it should, so nothing was dropped' ' tail -c 200 big.second.body | grep "" ' # A slot stores its key ahead of the content and only the first few kilobytes of # it are read back, so a key longer than that could never match. A request # carrying one used to leave a slot behind on every visit that nothing could # ever go on to use. 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 && grep "" key.out && ls cache3 >key.slots && test_line_count = 0 key.slots ' 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 && ls cache3 >key.slots && test_line_count = 1 key.slots ' # An error page fired after rendering began abandons the fill with part of # the page already written into the lock file. That fragment must be # discarded along with the file, not replayed to the visitor behind the # error page. A commit whose parent object is missing renders its whole # info table before the diff machinery fails, which makes it the trigger. test_expect_success 'set up a repo missing a parent object' ' mkrepo repos/broken 2 && parent=$(git -C repos/broken rev-parse HEAD^) && tip=$(git -C repos/broken rev-parse HEAD) && rm "repos/broken/.git/objects/$(echo "$parent" | cut -c1-2)/$(echo "$parent" | cut -c3-)" && { echo "virtual-root=/" && echo "cache-root=$PWD/cache4" && echo "cache-size=64" && echo "repo.url=broken" && echo "repo.path=$PWD/repos/broken/.git" } >brokenrc && rm -rf cache4 && mkdir cache4 ' 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 && grep "Not a commit" broken.out && test $(grep -c "^Status:" broken.out) = 1 && ls cache4 >broken.slots && test_line_count = 0 broken.slots ' # An error page reports a condition the repository may grow out of, and a # request pinned to an object id would cache it under the never-expiring # static ttl. Asking for a commit that does not exist yet must therefore # leave nothing behind, or the real page could never appear once it does. test_expect_success 'an error page leaves no slot behind' ' rm -rf cache3 && mkdir cache3 && 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 ' # 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 && grep "refs/heads/master" clone.out && CGIT_CONFIG="$PWD/bigkeyrc" QUERY_STRING="url=bigpage/objects/info/packs" cgit >packs.out && grep "^Status: 200" packs.out && ls cache3 >clone.slots && test_line_count = 0 clone.slots ' # A response past cache-max-slot-size is served whole but kept nowhere, both # on a first fill and when it would have replaced an expired slot, which is # dropped along with it. The summary page stays under the limit set here # while the big blob page is well past it. test_expect_success 'a response over cache-max-slot-size is served but not kept' ' rm -rf cache3 && mkdir cache3 && { echo "cache-max-slot-size=32" && cat bigkeyrc } >slotsizerc && CGIT_CONFIG="$PWD/slotsizerc" QUERY_STRING="url=bigpage/tree/big.txt" cgit >big.unkept && tail -c 200 big.unkept | grep "" && ls cache3 >unkept.slots && test_line_count = 0 unkept.slots && CGIT_CONFIG="$PWD/slotsizerc" QUERY_STRING="url=bigpage/" cgit >/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 && 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 && tail -c 200 big.refill | grep "" && ls cache3 >refill.slots && test_line_count = 0 refill.slots ' test_done