languages: Fix Debug Test for Go subtests (#53680)

The `go-subtest` task template wrapped the `-run` arg in single quotes
for shell safety. This works for Run Test (terminal strips quotes), but
Debug Test sends the arg through Delve’s DAP protocol with no shell
involved, so the literal quote characters ended up in the regex and
prevented any match.

All other Go task templates (`go-test`, `go-testify-suite`,
`go-table-test-case`) use the backslash-escaped format which `GoLocator`
already knows how to handle. Align the `go-subtest` template to the same
format.

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #53230.

Release Notes:

- Fixed Debug Test for Go subtests

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
Kunall BanerjeeandSmit Barmase authored and GitHub committed 2026-06-23 11:10:05 +00:00
1 parent 275c7e616c
commit 5449aa67df
2 files changed
+107 -1

No files matched your search

+69 -1
View File
@@ -842,7 +842,7 @@ impl ContextProvider for GoContextProvider {
"-v".into(),
"-run".into(),
format!(
"'^{}$/^{}$'",
"\\^{}\\$/\\^{}\\$",
VariableName::Symbol.template_value(),
GO_SUBTEST_NAME_TASK_VARIABLE.template_value(),
),
@@ -943,6 +943,7 @@ mod tests {
use super::*;
use crate::language;
use gpui::{AppContext, Hsla, TestAppContext};
use task::TaskContext;
use theme::SyntaxTheme;
fn go_language() -> Arc<Language> {
@@ -1219,6 +1220,73 @@ mod tests {
);
}
#[gpui::test]
async fn test_go_test_templates_run_arg_is_shell_escaped(cx: &mut TestAppContext) {
let templates = cx
.update(|cx| GoContextProvider.associated_tasks(None, cx))
.await
.expect("Go context provider returns associated tasks");
// `resolve_task` returns `None` for any `ZED_` variable a template
// references but the context omits, so supply all of them.
let context = TaskContext {
cwd: None,
task_variables: TaskVariables::from_iter([
(VariableName::Symbol, "TestFoo".to_string()),
(GO_SUBTEST_NAME_TASK_VARIABLE, "simple_subtest".to_string()),
(
GO_TABLE_TEST_CASE_NAME_TASK_VARIABLE,
"table_case".to_string(),
),
(GO_SUITE_NAME_TASK_VARIABLE, "Suite".to_string()),
(GO_PACKAGE_TASK_VARIABLE, ".".to_string()),
(VariableName::Dirname, "/tmp".to_string()),
]),
project_env: HashMap::default(),
};
// `go-benchmark` is excluded: its `-run='^$'` form intentionally quotes.
let escaped_run_arg_tags = [
"go-test",
"go-example",
"go-subtest",
"go-fuzz",
"go-testify-suite",
"go-table-test-case",
];
for tag in escaped_run_arg_tags {
let template = templates
.0
.iter()
.find(|template| template.tags.iter().any(|template_tag| template_tag == tag))
.unwrap_or_else(|| panic!("`{tag}` task template exists"));
let resolved = template
.resolve_task("go", &context)
.unwrap_or_else(|| panic!("`{tag}` template resolves"));
let run_index = resolved
.resolved
.args
.iter()
.position(|arg| arg == "-run")
.unwrap_or_else(|| panic!("`{tag}` resolved args contain a `-run` flag"));
let run_arg = &resolved.resolved.args[run_index + 1];
assert!(
!run_arg.contains('\''),
"`{tag}` -run arg must not shell-quote the regex; single quotes leak \
literally into Delve's regex and break Debug Test (#53230), got {run_arg:?}"
);
assert!(
run_arg.starts_with("\\^") && run_arg.ends_with("\\$"),
"`{tag}` -run arg must escape its regex anchors as \\^...\\$ so the shell \
and GoLocator both strip them, got {run_arg:?}"
);
}
}
#[gpui::test]
fn test_go_example_test_detection(cx: &mut TestAppContext) {
let language = go_language();
@@ -173,6 +173,44 @@ mod go_locator {
);
}
#[gpui::test]
async fn test_go_locator_unescapes_nested_subtest_regex(_: &mut TestAppContext) {
let locator = GoLocator;
let delve = DebugAdapterName("Delve".into());
// Delve receives the `-run` regex with no shell, so GoLocator must strip
// the escaping itself.
let task = TaskTemplate {
label: "test subtest".into(),
command: "go".into(),
args: vec![
"test".to_string(),
"-v".to_string(),
"-run".to_string(),
"\\^TestFoo\\$/\\^simple_subtest\\$".to_string(),
],
..Default::default()
};
let result = locator.create_scenario(&task, "", &delve).await.unwrap();
let config: DelveLaunchRequest = serde_json::from_value(result.config).unwrap();
assert_eq!(
config,
DelveLaunchRequest {
request: "launch".to_string(),
mode: "test".to_string(),
program: ".".to_string(),
build_flags: vec![],
args: vec![
"-test.v".to_string(),
"-test.run".to_string(),
"^TestFoo$/^simple_subtest$".to_string(),
],
env: Default::default(),
cwd: None,
}
);
}
#[gpui::test]
async fn test_skip_unsupported_go_commands(_: &mut TestAppContext) {
let locator = GoLocator;