go: Stop running ghost tests, fix broken go test -run for suites (#38167)
Closed #33759 Closed #38166 ### Summary This PR fixes the way `go test` commands are generated for **testify suite test methods**. Previously, only the method name was included in the `-run` flag, which caused Go’s test runner to fail to find suite test cases. --- ### Problem https://github.com/user-attachments/assets/e6f80a77-bcf3-457c-8bfb-a7286d44ff71 1. **Incorrect command** was generated for suite tests: ```bash go test -run TestSomething_Success ``` This results in: ``` testing: warning: no tests to run ``` 2. The correct format requires the **suite name + method name**: ```bash go test -run ^TestFooSuite$/TestSomething_Success$ ``` Without the suite prefix (`TestFooSuite`), Go cannot locate test methods defined on a suite struct. --- ### Changes Made * **Updated `runnables.scm`**: * Added a new query rule for suite methods (`.*Suite` receiver types). * Ensures only methods on suite structs (e.g., `FooSuite`) are matched. * Tagged these with `go-testify-suite` in addition to `go-test`. * **Extended task template generation**: * Introduced `GO_SUITE_NAME_TASK_VARIABLE` to capture the suite name. * Create a `TaskTemplate` for the testify suite. * **Improved labeling**: * Labels now show the full path (`go test ./pkg -v -run TestFooSuite/TestSomething_Success`) for clarity. * **Added a test** `test_testify_suite_detection`: * Covered testify suite cases to ensure correct detection and command generation. --- ### Impact https://github.com/user-attachments/assets/ef509183-534a-4aa4-9dc7-01402ac32260 * **Before**: Running a suite test method produced “no tests to run.” * **After**: Suite test methods are runnable individually with the correct `-run` command, and full suites can still be executed as before. ### Release Notes * Fixed generation of `go test` commands for **testify suite test methods**. Suite methods now include both the suite name and the method name in the `-run` flag (e.g., `^TestFooSuite$/TestSomething_Success$`), ensuring they are properly detected and runnable individually.
This commit is contained in:
@@ -479,6 +479,8 @@ const GO_SUBTEST_NAME_TASK_VARIABLE: VariableName =
|
|||||||
VariableName::Custom(Cow::Borrowed("GO_SUBTEST_NAME"));
|
VariableName::Custom(Cow::Borrowed("GO_SUBTEST_NAME"));
|
||||||
const GO_TABLE_TEST_CASE_NAME_TASK_VARIABLE: VariableName =
|
const GO_TABLE_TEST_CASE_NAME_TASK_VARIABLE: VariableName =
|
||||||
VariableName::Custom(Cow::Borrowed("GO_TABLE_TEST_CASE_NAME"));
|
VariableName::Custom(Cow::Borrowed("GO_TABLE_TEST_CASE_NAME"));
|
||||||
|
const GO_SUITE_NAME_TASK_VARIABLE: VariableName =
|
||||||
|
VariableName::Custom(Cow::Borrowed("GO_SUITE_NAME"));
|
||||||
|
|
||||||
impl ContextProvider for GoContextProvider {
|
impl ContextProvider for GoContextProvider {
|
||||||
fn build_context(
|
fn build_context(
|
||||||
@@ -537,19 +539,26 @@ impl ContextProvider for GoContextProvider {
|
|||||||
let go_subtest_variable = extract_subtest_name(_subtest_name.unwrap_or(""))
|
let go_subtest_variable = extract_subtest_name(_subtest_name.unwrap_or(""))
|
||||||
.map(|subtest_name| (GO_SUBTEST_NAME_TASK_VARIABLE.clone(), subtest_name));
|
.map(|subtest_name| (GO_SUBTEST_NAME_TASK_VARIABLE.clone(), subtest_name));
|
||||||
|
|
||||||
let table_test_case_name = variables.get(&VariableName::Custom(Cow::Borrowed(
|
let _table_test_case_name = variables.get(&VariableName::Custom(Cow::Borrowed(
|
||||||
"_table_test_case_name",
|
"_table_test_case_name",
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let go_table_test_case_variable = table_test_case_name
|
let go_table_test_case_variable = _table_test_case_name
|
||||||
.and_then(extract_subtest_name)
|
.and_then(extract_subtest_name)
|
||||||
.map(|case_name| (GO_TABLE_TEST_CASE_NAME_TASK_VARIABLE.clone(), case_name));
|
.map(|case_name| (GO_TABLE_TEST_CASE_NAME_TASK_VARIABLE.clone(), case_name));
|
||||||
|
|
||||||
|
let _suite_name = variables.get(&VariableName::Custom(Cow::Borrowed("_suite_name")));
|
||||||
|
|
||||||
|
let go_suite_variable = _suite_name
|
||||||
|
.and_then(extract_subtest_name)
|
||||||
|
.map(|suite_name| (GO_SUITE_NAME_TASK_VARIABLE.clone(), suite_name));
|
||||||
|
|
||||||
Task::ready(Ok(TaskVariables::from_iter(
|
Task::ready(Ok(TaskVariables::from_iter(
|
||||||
[
|
[
|
||||||
go_package_variable,
|
go_package_variable,
|
||||||
go_subtest_variable,
|
go_subtest_variable,
|
||||||
go_table_test_case_variable,
|
go_table_test_case_variable,
|
||||||
|
go_suite_variable,
|
||||||
go_module_root_variable,
|
go_module_root_variable,
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -566,6 +575,28 @@ impl ContextProvider for GoContextProvider {
|
|||||||
let module_cwd = Some(GO_MODULE_ROOT_TASK_VARIABLE.template_value());
|
let module_cwd = Some(GO_MODULE_ROOT_TASK_VARIABLE.template_value());
|
||||||
|
|
||||||
Task::ready(Some(TaskTemplates(vec![
|
Task::ready(Some(TaskTemplates(vec![
|
||||||
|
TaskTemplate {
|
||||||
|
label: format!(
|
||||||
|
"go test {} -v -run Test{}/{}",
|
||||||
|
GO_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
GO_SUITE_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
VariableName::Symbol.template_value(),
|
||||||
|
),
|
||||||
|
command: "go".into(),
|
||||||
|
args: vec![
|
||||||
|
"test".into(),
|
||||||
|
"-v".into(),
|
||||||
|
"-run".into(),
|
||||||
|
format!(
|
||||||
|
"\\^Test{}\\$/\\^{}\\$",
|
||||||
|
GO_SUITE_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
VariableName::Symbol.template_value(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
cwd: package_cwd.clone(),
|
||||||
|
tags: vec!["go-testify-suite".to_owned()],
|
||||||
|
..TaskTemplate::default()
|
||||||
|
},
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
"go test {} -v -run {}/{}",
|
"go test {} -v -run {}/{}",
|
||||||
@@ -819,6 +850,59 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn test_testify_suite_detection(cx: &mut TestAppContext) {
|
||||||
|
let language = language("go", tree_sitter_go::LANGUAGE.into());
|
||||||
|
|
||||||
|
let testify_suite = r#"
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExampleSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ExampleSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ExampleSuite) TestSomething_Success() {
|
||||||
|
// test code
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let buffer = cx
|
||||||
|
.new(|cx| crate::Buffer::local(testify_suite, 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..testify_suite.len()).collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
let tag_strings: Vec<String> = 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-testify-suite".to_string()),
|
||||||
|
"Should find go-testify-suite tag, found: {:?}",
|
||||||
|
tag_strings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_go_runnable_detection(cx: &mut TestAppContext) {
|
fn test_go_runnable_detection(cx: &mut TestAppContext) {
|
||||||
let language = language("go", tree_sitter_go::LANGUAGE.into());
|
let language = language("go", tree_sitter_go::LANGUAGE.into());
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
; Functions names start with `Test`
|
; Functions names start with `Test`
|
||||||
(
|
(
|
||||||
[
|
(
|
||||||
(function_declaration name: (_) @run
|
(function_declaration name: (_) @run
|
||||||
(#match? @run "^Test.*"))
|
(#match? @run "^Test.*"))
|
||||||
|
) @_
|
||||||
|
(#set! tag go-test)
|
||||||
|
)
|
||||||
|
|
||||||
|
; Suite test methods (testify/suite)
|
||||||
|
(
|
||||||
(method_declaration
|
(method_declaration
|
||||||
receiver: (parameter_list
|
receiver: (parameter_list
|
||||||
(parameter_declaration
|
(parameter_declaration
|
||||||
name: (identifier) @_receiver_name
|
type: [
|
||||||
type: [
|
(pointer_type (type_identifier) @_suite_name)
|
||||||
(pointer_type (type_identifier) @_receiver_type)
|
(type_identifier) @_suite_name
|
||||||
(type_identifier) @_receiver_type
|
]
|
||||||
]
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
name: (field_identifier) @run @_method_name
|
name: (field_identifier) @run @_subtest_name
|
||||||
(#match? @_method_name "^Test.*"))
|
(#match? @_subtest_name "^Test.*")
|
||||||
] @_
|
(#match? @_suite_name ".*Suite")
|
||||||
(#set! tag go-test)
|
) @_
|
||||||
|
(#set! tag go-testify-suite)
|
||||||
)
|
)
|
||||||
|
|
||||||
; `go:generate` comments
|
; `go:generate` comments
|
||||||
|
|||||||
Reference in New Issue
Block a user