Skip to content

Commit

Permalink
fix: remove debouncer, use simpler approach
Browse files Browse the repository at this point in the history
  • Loading branch information
fredrikaverpil committed Jul 13, 2024
1 parent ce935ef commit 70a600c
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 178 deletions.
36 changes: 16 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,14 @@ You can run `:checkhealth neotest-golang` to review common issues.

## ⚙️ Configuration

| Argument | Default value | Description |
| ------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `go_test_args` | `{ "-v", "-race", "-count=1" }` | Arguments to pass into `go test`. |
| `dap_go_enabled` | `false` | Leverage [leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) for debugging tests. |
| `dap_go_opts` | `{}` | Options to pass into `require("dap-go").setup()`. |
| `testify_enabled` | `false` | Enable support for [testify](https://github.com/stretchr/testify) suites. See [here](https://github.com/fredrikaverpil/neotest-golang#testify-suites) for more info. |
| `testify_generate_lookup` | `true` | Automatically re-generate testify lookup when recalculating Neotest tree. |
| `testify_debounce_delay` | `500` | The time to debounce/delay lookup re-generation (in milliseconds). |
| `warn_test_name_dupes` | `true` | Warn about duplicate test names within the same Go package. |
| `warn_test_not_executed` | `true` | Warn if test was not executed. |
| Argument | Default value | Description |
| ------------------------ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `go_test_args` | `{ "-v", "-race", "-count=1" }` | Arguments to pass into `go test`. |
| `dap_go_enabled` | `false` | Leverage [leoluz/nvim-dap-go](https://github.com/leoluz/nvim-dap-go) for debugging tests. |
| `dap_go_opts` | `{}` | Options to pass into `require("dap-go").setup()`. |
| `testify_enabled` | `false` | Enable support for [testify](https://github.com/stretchr/testify) suites. See [here](https://github.com/fredrikaverpil/neotest-golang#testify-suites) for more info. |
| `warn_test_name_dupes` | `true` | Warn about duplicate test names within the same Go package. |
| `warn_test_not_executed` | `true` | Warn if test was not executed. |

### Example configuration: custom `go test` arguments

Expand Down Expand Up @@ -369,23 +367,21 @@ more information on this.

### Testify suites

> [!WARNING]
> [!WARNING]
> This feature comes with some caveats and nuances, which is why it
> is not enabled by default. I advise you to only enable this if you need it.
There are some real shenaningans going on behind the scenes to make this work.
😅 First, a lookup of "receiver type-to-suite test function" will be created of
all Go test files in your project. Then, the generated Neotest node tree is
modified by mutating private attributes and merging of nodes to avoid
😅 First, an in-memory lookup of "receiver type-to-suite test function" will be
created of all Go test files in your project. Then, the generated Neotest node
tree is modified by mutating private attributes and merging of nodes to avoid
duplicates. I'm personally a bit afraid of the maintenance burden of this
feature... 🙈

> [!NOTE]
> Right now, there is no way to update the lookup other than restarting
> Neotest/Neovim. So in case you are implementing a new suite, please restart to
> see the new suites/tests appear in e.g. the summary window. Also, nested tests
> or table tests are not supported. All of this can be remedied at any time.
> Feel free to dig in and open a PR!
> [!NOTE]
> Right now, nested tests and table tests are not supported. All of this
> can be remedied at any time by extending the treesitter queries. Feel free to
> dig in and open a PR!
## 🙏 PRs are welcome

Expand Down
124 changes: 50 additions & 74 deletions lua/neotest-golang/features/testify/lookup.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
--- Lookup table for renaming Neotest namespaces (receiver type to testify suite function).

local options = require("neotest-golang.options")
local lib = require("neotest-golang.lib")
local query = require("neotest-golang.features.testify.query")

local M = {}
Expand Down Expand Up @@ -64,88 +62,66 @@ M.query = [[
(identifier))))))
]]

--- The lookup table store.
--- @type table<string, table>
local lookup_table = {}
local function create_lookup_manager()
local lookup_table = {}

--- Debouncer for generating the lookup table.
--- @type function
local debounce = lib.debounce.create_debouncer()
local debounced_generate = debounce(function()
return M._generate()
end, options.get().testify_debounce_delay)

--- Generate the lookup table for testify suites.
--- @return table<string, table> The generated lookup table
function M.generate()
if options.get().testify_generate_lookup then
local get_result = debounced_generate()

-- Wait for up to 5 seconds for the result, polling every 100ms.
local max_wait_time = 5000
local interval_time = 100
vim.wait(max_wait_time, function()
lookup_table = get_result()
return not vim.tbl_isempty(lookup_table)
end, interval_time)
end
return {
init = function(file_paths)
for _, file_path in ipairs(file_paths) do
lookup_table[file_path] = M.generate_data(file_path)
end
return lookup_table
end,
create = function(file_path)
if not lookup_table[file_path] then
lookup_table[file_path] = M.generate_data(file_path)
end
return lookup_table
end,
get = function()
return lookup_table
end,
clear = function()
lookup_table = {}
end,
}
end

if vim.tbl_isempty(lookup_table) then
vim.notify(
"Warning: generating the lookup timed out.",
vim.log.levels.ERROR
)
end
-- Create an instance of the lookup manager
local lookup_manager = create_lookup_manager()

return lookup_table
end
--- Public lookup functions.
M.initialize_lookup = lookup_manager.init
M.create_lookup = lookup_manager.create
M.get_lookup = lookup_manager.get
M.clear_lookup = lookup_manager.clear

--- Generate the lookup table for testify suites.
--- Generate the lookup data for the given file.
--- @return table<string, table> The generated lookup table
function M._generate()
vim.notify("Generating testify lookup...", vim.log.levels.INFO)
local cwd = vim.fn.getcwd()
local filepaths = lib.find.go_test_filepaths(cwd)
local lookup = {}
function M.generate_data(file_path)
local data = {}

-- First pass: collect all data for the lookup table.
for _, filepath in ipairs(filepaths) do
local matches = query.run_query_on_file(filepath, M.query)

local package_name = matches.package
and matches.package[1]
and matches.package[1].text
or "unknown"

lookup[filepath] = {
package = package_name,
replacements = {},
}

for i, struct in ipairs(matches.suite_struct or {}) do
local func = matches.test_function[i]
if func then
lookup[filepath].replacements[struct.text] = func.text
end
local matches = query.run_query_on_file(file_path, M.query)

local package_name = matches.package
and matches.package[1]
and matches.package[1].text
or "unknown"

data = {
package = package_name,
replacements = {},
}

for i, struct in ipairs(matches.suite_struct or {}) do
local func = matches.test_function[i]
if func then
data.replacements[struct.text] = func.text
end
end

return lookup
end

--- Get the lookup table for testify suites.
--- @param opts table<string, boolean> Options for getting the lookup table
--- @return table<string, table> The generated lookup table
function M.get(opts)
if options.get().testify_generate_lookup and opts.generate then
return M.generate()
end
return lookup_table
end

--- Clear the lookup table.
function M.clear()
lookup_table = {}
return data
end

return M
74 changes: 67 additions & 7 deletions lua/neotest-golang/features/testify/tree_modification.lua
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
--- Functions to modify the Neotest tree, for testify suite support.

local options = require("neotest-golang.options")
local lib = require("neotest-golang.lib")
local lookup = require("neotest-golang.features.testify.lookup")

local M = {}

local lookup_table = lookup.get_lookup()
local ignore_filepaths_during_init = {}

--- Modify the neotest tree, so that testify suites can be executed
--- as Neotest namespaces.
---
--- When testify tests are discovered, they are discovered with the Go receiver
--- type as the Neotest namespace. However, to produce a valid test path,
--- this receiver type must be replaced with the testify suite name in the
--- Neotest tree.
--- @param file_path string The path to the test file
--- @param tree neotest.Tree The original neotest tree
--- @return neotest.Tree The modified tree.
function M.modify_neotest_tree(tree)
local lookup_map = lookup.get({ generate = true })
function M.modify_neotest_tree(file_path, tree)
if vim.tbl_isempty(lookup_table) then
ignore_filepaths_during_init = lib.find.go_test_filepaths(vim.fn.getcwd())
lookup_table = lookup.initialize_lookup(ignore_filepaths_during_init)
end

if vim.tbl_contains(ignore_filepaths_during_init, file_path) then
-- some optimization;
-- ignore the first call, as it is handled by the initialization above.
for i, path in ipairs(ignore_filepaths_during_init) do
if path == file_path then
table.remove(ignore_filepaths_during_init, i)
break
end
end
else
-- after initialization, always update the lookup for the given filepath.
lookup_table = lookup.create_lookup(file_path)
end

if not lookup_map then
if not lookup_table then
vim.notify(
"No lookup found. Could not modify Neotest tree for testify suite support",
vim.log.levels.WARN
)
return tree
end

local modified_tree = M.replace_receiver_with_suite(tree:root(), lookup_map)
local tree_with_merged_namespaces =
M.merge_duplicate_namespaces(modified_tree)
return tree_with_merged_namespaces
local modified_tree = {}
modified_tree = M.replace_receiver_with_suite(tree:root(), lookup_table)
modified_tree = M.merge_duplicate_namespaces(modified_tree)

return modified_tree
end

--- Replace receiver methods with their corresponding test suites in the tree.
Expand Down Expand Up @@ -177,4 +201,40 @@ function M.fix_relationships(n)
end
end

function M.hash_tree(tree)
local function hash_node(node)
local data = node:data()
local hash_parts = {}

-- Convert all data fields to strings and add to hash_parts
for k, v in pairs(data) do
if type(v) == "table" then
table.insert(hash_parts, k .. "=" .. vim.inspect(v))
else
table.insert(hash_parts, k .. "=" .. tostring(v))
end
end

-- Sort hash_parts for consistent ordering
table.sort(hash_parts)

-- Hash children recursively
local children_hashes = {}
for _, child in ipairs(node:children()) do
table.insert(children_hashes, hash_node(child))
end
table.sort(children_hashes)

-- Combine node data and children hashes
local combined = table.concat(hash_parts, "|")
.. "#"
.. table.concat(children_hashes, "|")

-- Use Neovim's built-in hash function
return vim.fn.sha256(combined)
end

return hash_node(tree)
end

return M
65 changes: 0 additions & 65 deletions lua/neotest-golang/lib/debounce.lua

This file was deleted.

1 change: 0 additions & 1 deletion lua/neotest-golang/lib/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ local M = {}

M.convert = require("neotest-golang.lib.convert")
M.cmd = require("neotest-golang.lib.cmd")
M.debounce = require("neotest-golang.lib.debounce")
M.find = require("neotest-golang.lib.find")
M.json = require("neotest-golang.lib.json")

Expand Down
2 changes: 0 additions & 2 deletions lua/neotest-golang/options.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ local opts = {
dap_go_enabled = false,
dap_go_opts = {},
testify_enabled = false,
testify_generate_lookup = true,
testify_debounce_delay = 500,
warn_test_name_dupes = true,
warn_test_not_executed = true,

Expand Down
Loading

0 comments on commit 70a600c

Please sign in to comment.