From b6ff556d6b7fc1db31f9209db4e3e4962618a8c0 Mon Sep 17 00:00:00 2001 From: Lev Zakharov Date: Tue, 27 Jan 2026 08:54:22 +0300 Subject: [PATCH] go: Add support for running sub-tests in table tests without explicit variables for test cases (#46645) This PR extends support to run Go table-test subtests (#35657), handling tests without explicitly declaring variables for test scenarios. go-table-tests Release Notes: - Improved support to run Go table-test subtests, handling tests without explicitly declaring variables for test scenarios. Co-authored-by: Conrad Irwin --- crates/languages/src/go.rs | 148 +++++++++++++++++++++++++- crates/languages/src/go/runnables.scm | 96 ++++++++++++++++- 2 files changed, 242 insertions(+), 2 deletions(-) diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index ed6b456b1c74d0ef1e0611e0017d240a0158a4f0..84ca4bafda1720696a863e26e7d2560090c60716 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -594,7 +594,10 @@ impl ContextProvider for GoContextProvider { ), ], cwd: package_cwd.clone(), - tags: vec!["go-table-test-case".to_owned()], + tags: vec![ + "go-table-test-case".to_owned(), + "go-table-test-case-without-explicit-variable".to_owned(), + ], ..TaskTemplate::default() }, TaskTemplate { @@ -1117,6 +1120,149 @@ mod tests { // ); } + #[gpui::test] + fn test_go_table_test_slice_without_explicit_variable_detection(cx: &mut TestAppContext) { + let language = language("go", tree_sitter_go::LANGUAGE.into()); + + let table_test = r#" + package main + + import "testing" + + func TestExample(t *testing.T) { + for _, tc := range []struct{ + name string + anotherStr string + }{ + { + name: "test case 1", + anotherStr: "foo", + }, + { + name: "test case 2", + anotherStr: "bar", + }, + { + name: "test case 3", + anotherStr: "baz", + }, + } { + t.Run(tc.name, func(t *testing.T) { + // test code here + }) + } + } + "#; + + let buffer = + cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx)); + cx.executor().run_until_parked(); + + let runnables: Vec<_> = buffer.update(cx, |buffer, _| { + let snapshot = buffer.snapshot(); + snapshot.runnable_ranges(0..table_test.len()).collect() + }); + + let tag_strings: Vec = runnables + .iter() + .flat_map(|r| &r.runnable.tags) + .map(|tag| tag.0.to_string()) + .collect(); + + assert!( + tag_strings.contains(&"go-test".to_string()), + "Should find go-test tag, found: {:?}", + tag_strings + ); + assert!( + tag_strings.contains(&"go-table-test-case-without-explicit-variable".to_string()), + "Should find go-table-test-case-without-explicit-variable tag, found: {:?}", + tag_strings + ); + + let go_test_count = tag_strings.iter().filter(|&tag| tag == "go-test").count(); + + assert!( + go_test_count == 1, + "Should find exactly 1 go-test, found: {}", + go_test_count + ); + } + + #[gpui::test] + fn test_go_table_test_map_without_explicit_variable_detection(cx: &mut TestAppContext) { + let language = language("go", tree_sitter_go::LANGUAGE.into()); + + let table_test = r#" + package main + + import "testing" + + func TestExample(t *testing.T) { + for name, tc := range map[string]struct { + someStr string + fail bool + }{ + "test failure": { + someStr: "foo", + fail: true, + }, + "test success": { + someStr: "bar", + fail: false, + }, + } { + t.Run(name, func(t *testing.T) { + // test code here + }) + } + } + "#; + + let buffer = + cx.new(|cx| crate::Buffer::local(table_test, cx).with_language(language.clone(), cx)); + cx.executor().run_until_parked(); + + let runnables: Vec<_> = buffer.update(cx, |buffer, _| { + let snapshot = buffer.snapshot(); + snapshot.runnable_ranges(0..table_test.len()).collect() + }); + + let tag_strings: Vec = runnables + .iter() + .flat_map(|r| &r.runnable.tags) + .map(|tag| tag.0.to_string()) + .collect(); + + assert!( + tag_strings.contains(&"go-test".to_string()), + "Should find go-test tag, found: {:?}", + tag_strings + ); + assert!( + tag_strings.contains(&"go-table-test-case-without-explicit-variable".to_string()), + "Should find go-table-test-case-without-explicit-variable tag, found: {:?}", + tag_strings + ); + + let go_test_count = tag_strings.iter().filter(|&tag| tag == "go-test").count(); + let go_table_test_count = tag_strings + .iter() + .filter(|&tag| tag == "go-table-test-case-without-explicit-variable") + .count(); + + assert!( + go_test_count == 1, + "Should find exactly 1 go-test, found: {}", + go_test_count + ); + assert!( + go_table_test_count == 2, + "Should find exactly 2 go-table-test-case-without-explicit-variable, found: {}", + go_table_test_count + ); + } + #[gpui::test] fn test_go_table_test_slice_ignored(cx: &mut TestAppContext) { let language = language("go", tree_sitter_go::LANGUAGE.into()); diff --git a/crates/languages/src/go/runnables.scm b/crates/languages/src/go/runnables.scm index d3002a06cce9f3a12456eca438ddc6cdbb0233a5..a5081a21190e41a44601ac75fa49268f1d710165 100644 --- a/crates/languages/src/go/runnables.scm +++ b/crates/languages/src/go/runnables.scm @@ -107,7 +107,7 @@ (#set! tag go-main) ) -; Table test cases - slice and map +; Table test cases - slice and map with explicit variable ( (short_var_declaration left: (expression_list (identifier) @_collection_var) @@ -206,3 +206,97 @@ ) @_ (#set! tag go-table-test-case) ) + +; Table test cases - slice and map declared right inside the loop without +; explicit variable +( + (for_statement + (range_clause + left: (expression_list + [ + ( + (identifier) + (identifier) @_loop_var_inner + ) + (identifier) @_loop_var_outer + ] + ) + right: (composite_literal + type: [ + (slice_type) + (map_type + key: (type_identifier) @_key_type + (#eq? @_key_type "string") + ) + ] + body: (literal_value + [ + (literal_element + (literal_value + (keyed_element + (literal_element + (identifier) @_field_name + ) + (literal_element + [ + (interpreted_string_literal) @run @_table_test_case_name + (raw_string_literal) @run @_table_test_case_name + ] + ) + ) + ) + ) + (keyed_element + (literal_element + [ + (interpreted_string_literal) @run @_table_test_case_name + (raw_string_literal) @run @_table_test_case_name + ] + ) + ) + ] + ) + ) + ) + body: (block + (expression_statement + (call_expression + function: (selector_expression + operand: (identifier) + field: (field_identifier) @_run_method + (#eq? @_run_method "Run") + ) + arguments: (argument_list + . + [ + (selector_expression + operand: (identifier) @_tc_var + (#eq? @_tc_var @_loop_var_inner) + field: (field_identifier) @_field_check + (#eq? @_field_check @_field_name) + ) + (identifier) @_arg_var + (#eq? @_arg_var @_loop_var_outer) + ] + . + (func_literal + parameters: (parameter_list + (parameter_declaration + type: (pointer_type + (qualified_type + package: (package_identifier) @_pkg + name: (type_identifier) @_type + (#eq? @_pkg "testing") + (#eq? @_type "T") + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) @_ + (#set! tag go-table-test-case-without-explicit-variable) +)