diff --git a/test/git/object_test.lua b/test/git/object_test.lua new file mode 100644 index 0000000..785620e --- /dev/null +++ b/test/git/object_test.lua @@ -0,0 +1,174 @@ +local Revision = require("git.revision") +local h = require("test.git.helpers") +local object = require("git.object") +local t = require("test") + +require("git").init() + +---@return integer? buf +local function find_git_buf() + for _, b in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_get_name(b):match("^git://") then + return b + end + end +end + +---@param buf integer +---@param prefix string +---@return integer? lnum +local function find_line(buf, prefix) + for i, l in ipairs(vim.api.nvim_buf_get_lines(buf, 0, -1, false)) do + if l:sub(1, #prefix) == prefix then + return i + end + end +end + +t.test("parse_uri / format_uri round-trip for base", function() + local uri = "git://HEAD" + local rev = assert(object.parse_uri(uri)) + t.eq(object.format_uri(rev), uri) +end) + +t.test("parse_uri / format_uri round-trip for base + path", function() + local uri = "git://HEAD:lua/foo.lua" + local rev = assert(object.parse_uri(uri)) + t.eq(object.format_uri(rev), uri) +end) + +t.test("parse_uri / format_uri round-trip for stage + path", function() + local uri = "git://:2:lua/foo.lua" + local rev = assert(object.parse_uri(uri)) + t.eq(object.format_uri(rev), uri) +end) + +t.test("parse_uri normalizes bare :path to stage 0", function() + local rev = assert(object.parse_uri("git://:foo")) + t.eq(rev.stage, 0) + t.eq(rev.path, "foo") + t.eq(object.format_uri(rev), "git://:0:foo") +end) + +t.test("parse_uri returns nil for non-git URIs", function() + t.falsy(object.parse_uri("file:///tmp/x")) + t.falsy(object.parse_uri("/tmp/x")) + t.falsy(object.parse_uri("gitlog:///tmp/x")) +end) + +t.test("M.open(HEAD) names buffer with full sha", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + local sha = h.git(dir, "rev-parse", "HEAD").stdout + + object.open(r, "HEAD", { split = false }) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. sha) +end) + +t.test("M.open() canonicalizes to full sha", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + local sha = h.git(dir, "rev-parse", "HEAD").stdout + local short = h.git(dir, "rev-parse", "--short", "HEAD").stdout + t.truthy(#short < #sha, "short sha must be shorter than full") + + object.open(r, short, { split = false }) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. sha) +end) + +t.test("M.open(HEAD:) loads file content at HEAD", function() + local dir = h.make_repo({ ["a.txt"] = "first\nsecond\n" }) + local r = assert(require("git.repo").resolve(dir)) + local sha = h.git(dir, "rev-parse", "HEAD").stdout + + object.open(r, "HEAD:a.txt", { split = false }) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. sha .. ":a.txt") + t.eq( + vim.api.nvim_buf_get_lines(0, 0, -1, false), + { "first", "second" } + ) +end) + +t.test("M.open errors on a bogus base, no buffer is opened", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + + t.quietly(function() + object.open(r, "deadbeefdeadbeef", { split = false }) + end) + t.falsy(find_git_buf(), "no git:// buffer should exist") +end) + +t.test("M.open errors on a missing path, no buffer is opened", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + + t.quietly(function() + object.open(r, "HEAD:does-not-exist", { split = false }) + end) + t.falsy(find_git_buf(), "no git:// buffer should exist") +end) + +t.test("read_uri opens stage-0 entry as a writable index buffer", function() + local dir = h.make_repo({ ["a.txt"] = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + local rev = Revision.new({ stage = 0, path = "a.txt" }) + + local buf = object.buf_for(r, rev) + t.eq(vim.bo[buf].buftype, "acwrite") + t.truthy(vim.bo[buf].modifiable) + t.eq(vim.api.nvim_buf_get_lines(buf, 0, -1, false), { "first" }) +end) + +t.test("open_under_cursor on a 'tree ' line opens the tree", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + local tree_sha = h.git(dir, "rev-parse", "HEAD^{tree}").stdout + + object.open(r, "HEAD", { split = false }) + local lnum = assert(find_line(0, "tree "), "expected a tree line") + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + + t.truthy(object.open_under_cursor()) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. tree_sha) +end) + +t.test("open_under_cursor on a 'parent ' line opens the parent", function() + local dir = h.make_repo({ a = "first\n" }) + t.write(dir, "a", "second\n") + h.git(dir, "add", "a") + h.git(dir, "commit", "-q", "-m", "second") + local r = assert(require("git.repo").resolve(dir)) + local parent_sha = h.git(dir, "rev-parse", "HEAD~").stdout + + object.open(r, "HEAD", { split = false }) + local lnum = assert(find_line(0, "parent "), "expected a parent line") + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + + t.truthy(object.open_under_cursor()) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. parent_sha) +end) + +t.test("open_under_cursor on a '+++ b/' line loads the blob", function() + local dir = h.make_repo({ ["a.txt"] = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + local blob_sha = h.git(dir, "rev-parse", "HEAD:a.txt").stdout + + object.open(r, "HEAD", { split = false }) + local lnum = assert(find_line(0, "+++ b/a.txt"), "expected a +++ line") + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + + t.truthy(object.open_under_cursor()) + t.eq(vim.api.nvim_buf_get_name(0), "git://" .. blob_sha) +end) + +t.test("open_under_cursor returns false on a non-dispatchable line", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.repo").resolve(dir)) + + object.open(r, "HEAD", { split = false }) + local lnum = assert(find_line(0, "author "), "expected an author line") + vim.api.nvim_win_set_cursor(0, { lnum, 0 }) + + t.falsy(object.open_under_cursor()) +end) diff --git a/test/init.lua b/test/init.lua index 17c91b2..517e353 100644 --- a/test/init.lua +++ b/test/init.lua @@ -161,4 +161,17 @@ function M.wait_for(cond, msg, timeout) M.truthy(vim.wait(timeout or 1000, cond), "timed out waiting for: " .. msg) end +---Run `fn` with `vim.notify` stubbed to a no-op so error/warning paths +---don't bleed onto the test runner's stdout. +---@param fn fun() +function M.quietly(fn) + local orig = vim.notify + vim.notify = function() end + local ok, err = pcall(fn) + vim.notify = orig + if not ok then + error(err, 0) + end +end + return M