From 6a1c45a27a1e40a3e8a48c116ab298e70a0ecca9 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Thu, 9 Jun 2022 22:55:24 +0200 Subject: [PATCH 1/7] feat: add query to detect subtests --- lua/neotest-go/init.lua | 21 ++++++++++++++++- neotest_go/cases.go | 5 ++++ neotest_go/cases_test.go | 49 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 neotest_go/cases.go create mode 100644 neotest_go/cases_test.go diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index e243baf..ba29acf 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -135,6 +135,13 @@ function adapter.discover_positions(path) (#match? @test.name "^Test")) @test.definition + (call_expression + function: (selector_expression + field: (field_identifier) @test.method) + (#match? @test.method "^Run$") + arguments: (argument_list . (_) @test.name)) + @test.definition + (package_clause (package_identifier) @namespace.name) @namespace.definition @@ -145,6 +152,18 @@ function adapter.discover_positions(path) }) end +---@param tree neotest.Tree +---@param name string +---@return string +local function get_prefix(tree, name) + local parent_tree = tree:parent() + if not parent_tree then + return '' + end + local parent_name = parent_tree:data().name + return parent_name .. '/' .. name +end + ---@async ---@param args neotest.RunArgs ---@return neotest.RunSpec @@ -158,7 +177,7 @@ function adapter.build_spec(args) dir = { dir .. '/...' }, file = { position.path }, namespace = { package }, - test = { '-run', position.name .. '$', dir }, + test = { '-run', get_prefix(args.tree, position.name) .. '$', dir }, })[position.type] local command = { 'go', 'test', '-v', '-json', unpack(cmd_args) } diff --git a/neotest_go/cases.go b/neotest_go/cases.go new file mode 100644 index 0000000..ab3bbf7 --- /dev/null +++ b/neotest_go/cases.go @@ -0,0 +1,5 @@ +package main + +func doesAThing(a, b int) int { + return a + b +} diff --git a/neotest_go/cases_test.go b/neotest_go/cases_test.go new file mode 100644 index 0000000..5490e6b --- /dev/null +++ b/neotest_go/cases_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDoesAThing(t *testing.T) { + testCases := []struct { + desc string + a int + b int + want int + }{ + { + desc: "test one", + a: 1, + b: 2, + want: 3, + }, + { + desc: "test two", + a: 1, + b: 2, + want: 7, + }, + } + for _, tC := range testCases { + t.Run(tC.desc, func(t *testing.T) { + assert.Equal(t, tC.want, doesAThing(tC.a, tC.b)) + }) + } +} + +func TestDoesAThingAnotherWay(t *testing.T) { + t.Run("test one", func(t *testing.T) { + assert.Equal(t, 3, doesAThing(1, 2)) + }) + + t.Run("test two", func(t *testing.T) { + assert.Equal(t, 5, doesAThing(1, 2)) + }) + + variable := "string" + t.Run(variable, func(t *testing.T) { + assert.Equal(t, 3, doesAThing(1, 2)) + }) +} From b331b4b3e199c58d5002440722a172e5c23d81c4 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sat, 11 Jun 2022 00:53:02 +0200 Subject: [PATCH 2/7] refactor: detect subtests by prefixing with parent name --- lua/neotest-go/init.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index ba29acf..68f9f14 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -30,6 +30,11 @@ local function sanitize_output(output) return output:gsub('\n', ''):gsub('\t', '') end +-- replace whitespace with underscores and remove surrounding quotes +local function transform_test_name(name) + return name:gsub('[%s]', '_'):gsub('^"(.*)"$', '%1') +end + ---Get a line in a buffer, defaulting to the first if none is specified ---@param buf number ---@param nr number? @@ -158,7 +163,7 @@ end local function get_prefix(tree, name) local parent_tree = tree:parent() if not parent_tree then - return '' + return name end local parent_name = parent_tree:data().name return parent_name .. '/' .. name @@ -214,7 +219,8 @@ function adapter.results(_, result, tree) empty_result_fname = async.fn.tempname() fn.writefile(tests.__unnamed.output, empty_result_fname) end - for _, value in tree:iter() do + for _, node in tree:iter_nodes() do + local value = node:data() if no_results then results[value.id] = { status = 'skipped', @@ -222,6 +228,16 @@ function adapter.results(_, result, tree) } else local test_output = tests[value.name] + -- If the test is nested there is a chance it is a subtest + -- so extract the nodes parent name and see if a nested test result + -- exists for it. + if not test_output then + local parent = node:parent() and node:parent():data().name or nil + if parent then + local path = get_prefix(node, transform_test_name(value.name)) + test_output = tests[path] + end + end if test_output then local fname = async.fn.tempname() fn.writefile(test_output.output, fname) From 3ee044eaff72f4fc64b8c63234ebf0d5f2e26526 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sat, 11 Jun 2022 00:53:43 +0200 Subject: [PATCH 3/7] fix: resolve incorrect command by concatenating command table --- lua/neotest-go/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index 68f9f14..7e4adba 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -192,7 +192,7 @@ function adapter.build_spec(args) end return { - command = command, + command = table.concat(command, ' '), context = { results_path = results_path, file = position.path, From 5715a179c9fd3d21945bf463f3de23e2719c0f55 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sat, 11 Jun 2022 00:55:20 +0200 Subject: [PATCH 4/7] chore: add comment showing query for func namespace --- lua/neotest-go/init.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index 7e4adba..e8f0f74 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -134,6 +134,16 @@ end ---@async ---@return neotest.Tree| nil function adapter.discover_positions(path) + -- NOTE: this is a query to annotate a test function as a namespace + -- ((function_declaration + -- name: (identifier) @namespace.name + -- body: (block + -- (call_expression + -- function: (selector_expression + -- field: (field_identifier) @_method) + -- (#match? @_method "^Run")))) + -- (#match? @namespace.name "^Test")) + -- @namespace.definition local query = [[ ((function_declaration name: (identifier) @test.name) From c503827fc222d7efd3cf0b13608e8201bbced255 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sat, 11 Jun 2022 13:07:50 +0200 Subject: [PATCH 5/7] refactor: construct id for use with go tests for easier parsing of subtest ids --- lua/neotest-go/init.lua | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index e8f0f74..032ed88 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -131,6 +131,19 @@ function adapter.is_test_file(file_path) return is_test end +---@param position neotest.Position The position to return an ID for +---@param namespaces neotest.Position[] Any namespaces the position is within +local function generate_position_id(position, namespaces) + local prefix = {} + for _, namespace in ipairs(namespaces) do + if namespace.type ~= 'file' then + table.insert(prefix, namespace.name) + end + end + local name = transform_test_name(position.name) + return table.concat(vim.tbl_flatten({ position.path, prefix, name }), '::') +end + ---@async ---@return neotest.Tree| nil function adapter.discover_positions(path) @@ -164,6 +177,7 @@ function adapter.discover_positions(path) return lib.treesitter.parse_positions(path, query, { require_namespaces = false, nested_tests = true, + position_id = generate_position_id, }) end @@ -237,17 +251,9 @@ function adapter.results(_, result, tree) output = empty_result_fname, } else - local test_output = tests[value.name] - -- If the test is nested there is a chance it is a subtest - -- so extract the nodes parent name and see if a nested test result - -- exists for it. - if not test_output then - local parent = node:parent() and node:parent():data().name or nil - if parent then - local path = get_prefix(node, transform_test_name(value.name)) - test_output = tests[path] - end - end + local id_parts = vim.split(value.id, "::") + table.remove(id_parts, 1) + local test_output = tests[table.concat(id_parts, '/')] if test_output then local fname = async.fn.tempname() fn.writefile(test_output.output, fname) From 4e494e16534bba96c54817b49659eb714e6d1aa2 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sun, 12 Jun 2022 12:53:30 +0200 Subject: [PATCH 6/7] chore(neotest_go): make test funcs clearer --- neotest_go/cases.go | 6 +++++- neotest_go/cases_test.go | 12 ++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/neotest_go/cases.go b/neotest_go/cases.go index ab3bbf7..aa5c918 100644 --- a/neotest_go/cases.go +++ b/neotest_go/cases.go @@ -1,5 +1,9 @@ package main -func doesAThing(a, b int) int { +func add(a, b int) int { return a + b } + +func subtract(a, b int) int { + return a - b +} diff --git a/neotest_go/cases_test.go b/neotest_go/cases_test.go index 5490e6b..7c39201 100644 --- a/neotest_go/cases_test.go +++ b/neotest_go/cases_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDoesAThing(t *testing.T) { +func TestSubtract(t *testing.T) { testCases := []struct { desc string a int @@ -28,22 +28,22 @@ func TestDoesAThing(t *testing.T) { } for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { - assert.Equal(t, tC.want, doesAThing(tC.a, tC.b)) + assert.Equal(t, tC.want, subtract(tC.a, tC.b)) }) } } -func TestDoesAThingAnotherWay(t *testing.T) { +func TestAdd(t *testing.T) { t.Run("test one", func(t *testing.T) { - assert.Equal(t, 3, doesAThing(1, 2)) + assert.Equal(t, 3, add(1, 2)) }) t.Run("test two", func(t *testing.T) { - assert.Equal(t, 5, doesAThing(1, 2)) + assert.Equal(t, 5, add(1, 2)) }) variable := "string" t.Run(variable, func(t *testing.T) { - assert.Equal(t, 3, doesAThing(1, 2)) + assert.Equal(t, 3, add(1, 2)) }) } From 1700f4020290b53e0a1a48b9fbb4f2d51d3236e0 Mon Sep 17 00:00:00 2001 From: Akin Sowemimo <22454918+akinsho@users.noreply.github.com> Date: Sun, 12 Jun 2022 12:54:05 +0200 Subject: [PATCH 7/7] fix: don't add filenames to test prefixes --- lua/neotest-go/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/neotest-go/init.lua b/lua/neotest-go/init.lua index 032ed88..f7a7699 100644 --- a/lua/neotest-go/init.lua +++ b/lua/neotest-go/init.lua @@ -186,7 +186,7 @@ end ---@return string local function get_prefix(tree, name) local parent_tree = tree:parent() - if not parent_tree then + if not parent_tree or parent_tree:data().type == 'file' then return name end local parent_name = parent_tree:data().name @@ -251,7 +251,7 @@ function adapter.results(_, result, tree) output = empty_result_fname, } else - local id_parts = vim.split(value.id, "::") + local id_parts = vim.split(value.id, '::') table.remove(id_parts, 1) local test_output = tests[table.concat(id_parts, '/')] if test_output then