aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorSteven Arcangeli <stevearc@stevearc.com>2023-08-27 18:15:04 -0700
committerSteven Arcangeli <stevearc@stevearc.com>2023-08-27 18:15:09 -0700
commitf87f3ea322b1111e1929d149224ff736c8390db3 (patch)
tree44b11765d02fbb8b88bedafa4bde69ff33d22ad9 /tests
parent446aa570048586f9c13f1ea88e280567f336691e (diff)
test: add a test suite
Diffstat (limited to 'tests')
-rwxr-xr-xtests/fake_formatter.sh17
-rw-r--r--tests/minimal_init.lua5
-rw-r--r--tests/runner_spec.lua243
-rw-r--r--tests/test_util.lua34
4 files changed, 299 insertions, 0 deletions
diff --git a/tests/fake_formatter.sh b/tests/fake_formatter.sh
new file mode 100755
index 0000000..a060a4c
--- /dev/null
+++ b/tests/fake_formatter.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+
+if [ -e "tests/fake_formatter_output" ]; then
+ cat tests/fake_formatter_output
+else
+ cat
+fi
+
+if [ "$1" = "--fail" ]; then
+ echo "failure" >&2
+ exit 1
+elif [ "$1" = "--timeout" ]; then
+ sleep 4
+fi
+
diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua
new file mode 100644
index 0000000..262d9ec
--- /dev/null
+++ b/tests/minimal_init.lua
@@ -0,0 +1,5 @@
+vim.cmd([[set runtimepath+=.]])
+
+vim.o.swapfile = false
+vim.bo.swapfile = false
+require("tests.test_util").reset_editor()
diff --git a/tests/runner_spec.lua b/tests/runner_spec.lua
new file mode 100644
index 0000000..016c8a4
--- /dev/null
+++ b/tests/runner_spec.lua
@@ -0,0 +1,243 @@
+require("plenary.async").tests.add_to_env()
+local test_util = require("tests.test_util")
+local conform = require("conform")
+local runner = require("conform.runner")
+
+describe("runner", function()
+ after_each(function()
+ test_util.reset_editor()
+ end)
+
+ it("resolves config function", function()
+ conform.formatters.test = function()
+ return {
+ meta = { url = "", description = "" },
+ command = "echo",
+ }
+ end
+ local config = assert(conform.get_formatter_config("test"))
+ assert.are.same({
+ meta = { url = "", description = "" },
+ command = "echo",
+ stdin = true,
+ }, config)
+ end)
+
+ describe("build_context", function()
+ it("sets the filename and dirname", function()
+ vim.cmd.edit({ args = { "README.md" } })
+ local bufnr = vim.api.nvim_get_current_buf()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "echo",
+ }
+ local config = assert(conform.get_formatter_config("test"))
+ local ctx = runner.build_context(0, config)
+ local filename = vim.api.nvim_buf_get_name(bufnr)
+ assert.are.same({
+ buf = bufnr,
+ filename = filename,
+ dirname = vim.fs.dirname(filename),
+ }, ctx)
+ end)
+
+ it("sets temp file when stdin = false", function()
+ vim.cmd.edit({ args = { "README.md" } })
+ local bufnr = vim.api.nvim_get_current_buf()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "echo",
+ stdin = false,
+ }
+ local config = assert(conform.get_formatter_config("test"))
+ local ctx = runner.build_context(0, config)
+ local bufname = vim.api.nvim_buf_get_name(bufnr)
+ local dirname = vim.fs.dirname(bufname)
+ assert.equal(bufnr, ctx.buf)
+ assert.equal(dirname, ctx.dirname)
+ assert.truthy(ctx.filename:match(dirname .. "/.conform.%d+.README.md$"))
+ end)
+ end)
+
+ describe("build_cmd", function()
+ it("replaces $FILENAME in args", function()
+ vim.cmd.edit({ args = { "README.md" } })
+ local bufnr = vim.api.nvim_get_current_buf()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "echo",
+ args = { "$FILENAME" },
+ }
+ local config = assert(conform.get_formatter_config("test"))
+ local ctx = runner.build_context(0, config)
+ local cmd = runner.build_cmd(ctx, config)
+ assert.are.same({ "echo", vim.api.nvim_buf_get_name(bufnr) }, cmd)
+ end)
+
+ it("replaces $DIRNAME in args", function()
+ vim.cmd.edit({ args = { "README.md" } })
+ local bufnr = vim.api.nvim_get_current_buf()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "echo",
+ args = { "$DIRNAME" },
+ }
+ local config = assert(conform.get_formatter_config("test"))
+ local ctx = runner.build_context(0, config)
+ local cmd = runner.build_cmd(ctx, config)
+ assert.are.same({ "echo", vim.fs.dirname(vim.api.nvim_buf_get_name(bufnr)) }, cmd)
+ end)
+
+ it("resolves arg function", function()
+ vim.cmd.edit({ args = { "README.md" } })
+ local bufnr = vim.api.nvim_get_current_buf()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "echo",
+ args = function()
+ return { "--stdin" }
+ end,
+ }
+ local config = assert(conform.get_formatter_config("test"))
+ local ctx = runner.build_context(0, config)
+ local cmd = runner.build_cmd(ctx, config)
+ assert.are.same({ "echo", "--stdin" }, cmd)
+ end)
+ end)
+
+ describe("e2e", function()
+ before_each(function()
+ conform.formatters.test = {
+ meta = { url = "", description = "" },
+ command = "tests/fake_formatter.sh",
+ }
+ end)
+
+ ---@param buf_content string
+ ---@param expected string
+ ---@param opts? table
+ local function run_formatter(buf_content, expected, opts)
+ local bufnr = vim.fn.bufadd("testfile")
+ vim.fn.bufload(bufnr)
+ vim.api.nvim_set_current_buf(bufnr)
+ local lines = vim.split(buf_content, "\n", { plain = true })
+ vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, lines)
+ vim.bo[bufnr].modified = false
+ local expected_lines = vim.split(expected, "\n", { plain = true })
+ test_util.set_formatter_output(expected_lines)
+ conform.format(vim.tbl_extend("force", opts or {}, { formatters = { "test" } }))
+ return expected_lines
+ end
+
+ ---@param buf_content string
+ ---@param new_content string
+ ---@param expected? string[]
+ local function run_formatter_test(buf_content, new_content, expected)
+ local lines = run_formatter(buf_content, new_content)
+ assert.are.same(expected or lines, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ end
+
+ it("sets the correct output", function()
+ run_formatter_test(
+ [[
+ if true {
+ print("hello")
+ }]],
+ [[
+ if true {
+ print("hello")
+ }]]
+ )
+ run_formatter_test(
+ [[
+ if true {
+ print("hello")
+ }]],
+ [[
+ if true {
+ print("goodbye")
+ }]]
+ )
+ run_formatter_test(
+ [[
+ if true {
+ print("hello")
+ }]],
+ [[
+ if true {
+ print("hello world")
+ print("hello world")
+ print("hello world")
+ }]]
+ )
+ run_formatter_test(
+ [[
+print("a")
+print("b")
+print("c")
+ ]],
+ [[
+print("c")
+print("b")
+print("a")
+ ]]
+ )
+ run_formatter_test("hello\ngoodbye", "hello\n\n\ngoodbye", { "hello", "", "", "goodbye" })
+ run_formatter_test("hello", "hello\ngoodbye", { "hello", "goodbye" })
+ run_formatter_test("", "hello", { "hello" })
+ run_formatter_test("\nfoo", "\nhello\nfoo", { "", "hello", "foo" })
+ run_formatter_test("hello", "hello\n\n", { "hello", "" })
+ run_formatter_test("hello", "hello\n", { "hello" })
+ assert.falsy(vim.bo.modified)
+ run_formatter_test("hello\n", "hello", { "hello" })
+ run_formatter_test("hello\n ", "hello", { "hello" })
+ end)
+
+ it("does not change output if formatter fails", function()
+ conform.formatters.test.args = { "--fail" }
+ run_formatter("hello", "goodbye")
+ assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ end)
+
+ it("allows nonzero exit codes", function()
+ conform.formatters.test.args = { "--fail" }
+ conform.formatters.test.exit_codes = { 0, 1 }
+ run_formatter_test("hello", "goodbye")
+ end)
+
+ it("does not format if it times out", function()
+ conform.formatters.test.args = { "--timeout" }
+ run_formatter("hello", "goodbye", { timeout_ms = 10 })
+ assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ end)
+
+ it("can format async", function()
+ run_formatter("hello", "goodbye", { async = true })
+ assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ vim.wait(100)
+ assert.are.same({ "goodbye" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ end)
+
+ it("discards formatting changes if buffer has been concurrently modified", function()
+ run_formatter("hello", "goodbye", { async = true })
+ assert.are.same({ "hello" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, { "newcontent" })
+ vim.wait(100)
+ assert.are.same({ "newcontent" }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
+ end)
+
+ it("formats on save", function()
+ conform.setup({
+ formatters_by_ft = { ["*"] = { "test" } },
+ format_on_save = true,
+ })
+ vim.cmd.edit({ args = { "tests/testfile.txt" } })
+ vim.api.nvim_buf_set_lines(0, 0, -1, true, { "hello" })
+ test_util.set_formatter_output({ "goodbye" })
+ vim.cmd.write()
+ local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
+ vim.fn.delete("tests/testfile.txt")
+ assert.are.same({ "goodbye" }, lines)
+ end)
+ end)
+end)
diff --git a/tests/test_util.lua b/tests/test_util.lua
new file mode 100644
index 0000000..a225aec
--- /dev/null
+++ b/tests/test_util.lua
@@ -0,0 +1,34 @@
+require("plenary.async").tests.add_to_env()
+local conform = require("conform")
+local M = {}
+
+local OUTPUT_FILE = "tests/fake_formatter_output"
+
+M.reset_editor = function()
+ vim.cmd.tabonly({ mods = { silent = true } })
+ for i, winid in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
+ if i > 1 then
+ vim.api.nvim_win_close(winid, true)
+ end
+ end
+ vim.api.nvim_win_set_buf(0, vim.api.nvim_create_buf(false, true))
+ for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
+ vim.api.nvim_buf_delete(bufnr, { force = true })
+ end
+ conform.formatters = {}
+ conform.formatters_by_ft = {}
+ pcall(vim.api.nvim_del_augroup_by_name, "Conform")
+ if vim.fn.filereadable(OUTPUT_FILE) == 1 then
+ vim.fn.delete(OUTPUT_FILE)
+ end
+end
+
+---@param lines string[]
+M.set_formatter_output = function(lines)
+ local content = table.concat(lines, "\n")
+ local fd = assert(vim.loop.fs_open(OUTPUT_FILE, "w", 420)) -- 0644
+ vim.loop.fs_write(fd, content)
+ vim.loop.fs_close(fd)
+end
+
+return M