4b2bb6998a
Añade soporte C++ al registry: vendor submodules (glfw, imgui, implot, tracy), sistema de build con CMake y toolchains cross-platform, runner C++ en fn CLI, parser de tests Google Test, y funciones bash para build Linux/Windows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
153 lines
4.2 KiB
Go
153 lines
4.2 KiB
Go
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
|
|
}
|