From fd05f17fa7e0253bc98698e84a1f8939bcb6616f Mon Sep 17 00:00:00 2001 From: Lukas Spiss <35728419+Spissable@users.noreply.github.com> Date: Fri, 18 Jul 2025 12:38:18 +0100 Subject: [PATCH] go: Support raw string subtest names (#34636) Currently, we're not able to run Go sub-tests that have a raw string (e.g. we're using multi-line names a lot) via the UI. I added the changes that are needed, plus a handful of tests to cover the basics. Quick comparison: Before: before After: after Release Notes: - Added support for Go subtest runner with raw string names --- crates/languages/src/go.rs | 121 +++++++++++++++++++++++++- crates/languages/src/go/runnables.scm | 5 +- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index 25aa5a67b9..16c1b67203 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -41,7 +41,7 @@ static VERSION_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"\d+\.\d+\.\d+").expect("Failed to create VERSION_REGEX")); static GO_ESCAPE_SUBTEST_NAME_REGEX: LazyLock = LazyLock::new(|| { - Regex::new(r#"[.*+?^${}()|\[\]\\]"#).expect("Failed to create GO_ESCAPE_SUBTEST_NAME_REGEX") + Regex::new(r#"[.*+?^${}()|\[\]\\"']"#).expect("Failed to create GO_ESCAPE_SUBTEST_NAME_REGEX") }); const BINARY: &str = if cfg!(target_os = "windows") { @@ -685,11 +685,20 @@ impl ContextProvider for GoContextProvider { } fn extract_subtest_name(input: &str) -> Option { - let replaced_spaces = input.trim_matches('"').replace(' ', "_"); + let content = if input.starts_with('`') && input.ends_with('`') { + input.trim_matches('`') + } else { + input.trim_matches('"') + }; + + let processed = content + .chars() + .map(|c| if c.is_whitespace() { '_' } else { c }) + .collect::(); Some( GO_ESCAPE_SUBTEST_NAME_REGEX - .replace_all(&replaced_spaces, |caps: ®ex::Captures| { + .replace_all(&processed, |caps: ®ex::Captures| { format!("\\{}", &caps[0]) }) .to_string(), @@ -700,7 +709,7 @@ fn extract_subtest_name(input: &str) -> Option { mod tests { use super::*; use crate::language; - use gpui::Hsla; + use gpui::{AppContext, Hsla, TestAppContext}; use theme::SyntaxTheme; #[gpui::test] @@ -790,4 +799,108 @@ mod tests { }) ); } + + #[gpui::test] + fn test_go_runnable_detection(cx: &mut TestAppContext) { + let language = language("go", tree_sitter_go::LANGUAGE.into()); + + let interpreted_string_subtest = r#" + package main + + import "testing" + + func TestExample(t *testing.T) { + t.Run("subtest with double quotes", func(t *testing.T) { + // test code + }) + } + "#; + + let raw_string_subtest = r#" + package main + + import "testing" + + func TestExample(t *testing.T) { + t.Run(`subtest with + multiline + backticks`, func(t *testing.T) { + // test code + }) + } + "#; + + let buffer = cx.new(|cx| { + crate::Buffer::local(interpreted_string_subtest, 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..interpreted_string_subtest.len()) + .collect() + }); + + assert!( + runnables.len() == 2, + "Should find test function and subtest with double quotes, found: {}", + runnables.len() + ); + + let buffer = cx.new(|cx| { + crate::Buffer::local(raw_string_subtest, 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..raw_string_subtest.len()) + .collect() + }); + + assert!( + runnables.len() == 2, + "Should find test function and subtest with backticks, found: {}", + runnables.len() + ); + } + + #[test] + fn test_extract_subtest_name() { + // Interpreted string literal + let input_double_quoted = r#""subtest with double quotes""#; + let result = extract_subtest_name(input_double_quoted); + assert_eq!(result, Some(r#"subtest_with_double_quotes"#.to_string())); + + let input_double_quoted_with_backticks = r#""test with `backticks` inside""#; + let result = extract_subtest_name(input_double_quoted_with_backticks); + assert_eq!(result, Some(r#"test_with_`backticks`_inside"#.to_string())); + + // Raw string literal + let input_with_backticks = r#"`subtest with backticks`"#; + let result = extract_subtest_name(input_with_backticks); + assert_eq!(result, Some(r#"subtest_with_backticks"#.to_string())); + + let input_raw_with_quotes = r#"`test with "quotes" and other chars`"#; + let result = extract_subtest_name(input_raw_with_quotes); + assert_eq!( + result, + Some(r#"test_with_\"quotes\"_and_other_chars"#.to_string()) + ); + + let input_multiline = r#"`subtest with + multiline + backticks`"#; + let result = extract_subtest_name(input_multiline); + assert_eq!( + result, + Some(r#"subtest_with_________multiline_________backticks"#.to_string()) + ); + + let input_with_double_quotes = r#"`test with "double quotes"`"#; + let result = extract_subtest_name(input_with_double_quotes); + assert_eq!(result, Some(r#"test_with_\"double_quotes\""#.to_string())); + } } diff --git a/crates/languages/src/go/runnables.scm b/crates/languages/src/go/runnables.scm index bdeb77b46c..49e112b860 100644 --- a/crates/languages/src/go/runnables.scm +++ b/crates/languages/src/go/runnables.scm @@ -38,7 +38,10 @@ arguments: ( argument_list . - (interpreted_string_literal) @_subtest_name + [ + (interpreted_string_literal) + (raw_string_literal) + ] @_subtest_name . (func_literal parameters: (