package registry import ( "fmt" "os" "regexp" "strings" ) // testCase represents a single test extracted from a test file. type testCase struct { Name string Code string } // testPos marks the start of a test within a file. type testPos struct { name string startLine int } // parseTestFile reads a test file and extracts individual test cases. // Supports Go, Python, and Bash test formats. func parseTestFile(path, lang string) ([]testCase, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("reading test file %s: %w", path, err) } content := string(data) switch lang { case "go": return parseGoTests(content), nil case "py": return parsePythonTests(content), nil case "bash": return parseBashTests(content), nil case "cpp": return parseCppTests(content), nil default: return nil, nil } } // parseGoTests extracts Go test functions (func TestXxx). var goTestFuncRe = regexp.MustCompile(`(?m)^func\s+(Test\w+)\s*\(`) func parseGoTests(content string) []testCase { lines := strings.Split(content, "\n") var positions []testPos for i, line := range lines { if m := goTestFuncRe.FindStringSubmatch(line); m != nil { positions = append(positions, testPos{name: m[1], startLine: i}) } } return extractBlocks(lines, positions) } // parsePythonTests extracts Python test functions (def test_xxx). var pyTestFuncRe = regexp.MustCompile(`(?m)^def\s+(test_\w+)\s*\(`) func parsePythonTests(content string) []testCase { lines := strings.Split(content, "\n") var positions []testPos for i, line := range lines { if m := pyTestFuncRe.FindStringSubmatch(line); m != nil { positions = append(positions, testPos{name: m[1], startLine: i}) } } return extractBlocks(lines, positions) } // parseBashTests extracts Bash test blocks. // Tries three conventions in order: // 1. test_xxx() { ... } — function-based tests // 2. === section === — section headers (echo "=== name ===") // 3. # Test: ... — comment-based test blocks var bashTestFuncRe = regexp.MustCompile(`(?m)^(test_\w+)\s*\(\)\s*\{`) var bashTestCommentRe = regexp.MustCompile(`(?m)^#\s*[Tt]est:\s*(.+)`) var bashSectionRe = regexp.MustCompile(`(?i)^(?:echo\s+["'])?===\s*(\w[\w\s]*\w)\s*===["']?\s*$`) func parseBashTests(content string) []testCase { lines := strings.Split(content, "\n") // Strategy 1: test_xxx() { ... } var positions []testPos for i, line := range lines { if m := bashTestFuncRe.FindStringSubmatch(line); m != nil { positions = append(positions, testPos{name: m[1], startLine: i}) } } if len(positions) > 0 { return extractBlocks(lines, positions) } // Strategy 2: === section === headers for i, line := range lines { trimmed := strings.TrimSpace(line) if m := bashSectionRe.FindStringSubmatch(trimmed); m != nil { positions = append(positions, testPos{name: m[1], startLine: i}) } } if len(positions) > 0 { return extractBlocks(lines, positions) } // Strategy 3: # Test: ... comments for i, line := range lines { if m := bashTestCommentRe.FindStringSubmatch(line); m != nil { positions = append(positions, testPos{name: strings.TrimSpace(m[1]), startLine: i}) } } return extractBlocks(lines, positions) } // parseCppTests extracts C++ test functions (Google Test TEST/TEST_F macros). var cppTestRe = regexp.MustCompile(`(?m)^(?:TEST|TEST_F|TEST_P)\s*\(\s*(\w+)\s*,\s*(\w+)\s*\)`) func parseCppTests(content string) []testCase { lines := strings.Split(content, "\n") var positions []testPos for i, line := range lines { if m := cppTestRe.FindStringSubmatch(line); m != nil { name := m[1] + "." + m[2] // Suite.TestName positions = append(positions, testPos{name: name, startLine: i}) } } return extractBlocks(lines, positions) } // extractBlocks splits lines into code blocks based on test positions. func extractBlocks(lines []string, positions []testPos) []testCase { var tests []testCase for i, pos := range positions { endLine := len(lines) if i+1 < len(positions) { endLine = positions[i+1].startLine } for endLine > pos.startLine && strings.TrimSpace(lines[endLine-1]) == "" { endLine-- } code := strings.Join(lines[pos.startLine:endLine], "\n") tests = append(tests, testCase{Name: pos.name, Code: code}) } return tests }