mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
397 lines
7.1 KiB
Go
397 lines
7.1 KiB
Go
package extgen
|
|
|
|
import (
|
|
"github.com/stretchr/testify/require"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestSourceAnalyzer_Analyze(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
sourceContent string
|
|
expectedImports []string
|
|
expectedFunctions []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "simple file with imports and functions",
|
|
sourceContent: `package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func regularFunction() {
|
|
fmt.Println("hello")
|
|
}
|
|
|
|
//export_php:function
|
|
func exportedFunction() string {
|
|
return "exported"
|
|
}`,
|
|
expectedImports: []string{`"fmt"`, `"strings"`},
|
|
expectedFunctions: []string{
|
|
`func regularFunction() {
|
|
fmt.Println("hello")
|
|
}`,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "file with named imports",
|
|
sourceContent: `package main
|
|
|
|
import (
|
|
custom "fmt"
|
|
. "strings"
|
|
_ "os"
|
|
)
|
|
|
|
func test() {}`,
|
|
expectedImports: []string{`custom "fmt"`, `. "strings"`, `_ "os"`},
|
|
expectedFunctions: []string{
|
|
`func test() {}`,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "file with multiple functions and export comments",
|
|
sourceContent: `package main
|
|
|
|
func internalOne() {
|
|
// some code
|
|
}
|
|
|
|
// This function is exported to PHP
|
|
//export_php:function
|
|
func exportedOne() int {
|
|
return 42
|
|
}
|
|
|
|
func internalTwo() string {
|
|
return "internal"
|
|
}
|
|
|
|
// Another exported function
|
|
//export_php:function
|
|
func exportedTwo() bool {
|
|
return true
|
|
}`,
|
|
expectedImports: []string{},
|
|
expectedFunctions: []string{
|
|
`func internalOne() {
|
|
// some code
|
|
}`,
|
|
`func internalTwo() string {
|
|
return "internal"
|
|
}`,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "file with nested braces",
|
|
sourceContent: `package main
|
|
|
|
func complexFunction() {
|
|
if true {
|
|
for i := 0; i < 10; i++ {
|
|
if i%2 == 0 {
|
|
fmt.Println(i)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//export_php:function
|
|
func exportedComplex() {
|
|
obj := struct{
|
|
field string
|
|
}{
|
|
field: "value",
|
|
}
|
|
fmt.Println(obj)
|
|
}`,
|
|
expectedImports: []string{},
|
|
expectedFunctions: []string{
|
|
`func complexFunction() {
|
|
if true {
|
|
for i := 0; i < 10; i++ {
|
|
if i%2 == 0 {
|
|
fmt.Println(i)
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "empty file",
|
|
sourceContent: `package main`,
|
|
expectedImports: []string{},
|
|
expectedFunctions: []string{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "file with only exported functions",
|
|
sourceContent: `package main
|
|
|
|
//export_php:function
|
|
func onlyExported() {}
|
|
|
|
//export_php:function
|
|
func anotherExported() string {
|
|
return "test"
|
|
}`,
|
|
expectedImports: []string{},
|
|
expectedFunctions: []string{},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "file with export comment not immediately before function",
|
|
sourceContent: `package main
|
|
|
|
//export_php:function
|
|
// Some other comment
|
|
func shouldNotBeExported() {}
|
|
|
|
func normalFunction() {
|
|
//export_php:function inside function should not count
|
|
}`,
|
|
expectedImports: []string{},
|
|
expectedFunctions: []string{
|
|
`func normalFunction() {
|
|
//export_php:function inside function should not count
|
|
}`,
|
|
},
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
filename := filepath.Join(tempDir, "test.go")
|
|
|
|
require.NoError(t, os.WriteFile(filename, []byte(tt.sourceContent), 0644))
|
|
|
|
analyzer := &SourceAnalyzer{}
|
|
imports, functions, err := analyzer.analyze(filename)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err, "expected error")
|
|
return
|
|
}
|
|
|
|
assert.NoError(t, err, "unexpected error")
|
|
|
|
if len(imports) != 0 && len(tt.expectedImports) != 0 {
|
|
assert.Equal(t, tt.expectedImports, imports, "imports mismatch")
|
|
}
|
|
|
|
assert.Len(t, functions, len(tt.expectedFunctions), "function count mismatch")
|
|
|
|
for i, expected := range tt.expectedFunctions {
|
|
assert.Equal(t, expected, functions[i], "function %d mismatch", i)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSourceAnalyzer_Analyze_InvalidFile(t *testing.T) {
|
|
analyzer := &SourceAnalyzer{}
|
|
|
|
t.Run("nonexistent file", func(t *testing.T) {
|
|
_, _, err := analyzer.analyze("/nonexistent/file.go")
|
|
assert.Error(t, err, "expected error for nonexistent file")
|
|
})
|
|
|
|
t.Run("invalid Go syntax", func(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
filename := filepath.Join(tempDir, "invalid.go")
|
|
|
|
invalidContent := `package main
|
|
func incomplete( {
|
|
// invalid syntax
|
|
`
|
|
|
|
require.NoError(t, os.WriteFile(filename, []byte(invalidContent), 0644))
|
|
|
|
_, _, err := analyzer.analyze(filename)
|
|
assert.Error(t, err, "expected error for invalid syntax")
|
|
})
|
|
}
|
|
|
|
func TestSourceAnalyzer_ExtractInternalFunctions(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
content string
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "single function without export",
|
|
content: `func test() {
|
|
fmt.Println("test")
|
|
}`,
|
|
expected: []string{
|
|
`func test() {
|
|
fmt.Println("test")
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
name: "function with export comment",
|
|
content: `//export_php:function
|
|
func exported() {}`,
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "mixed functions",
|
|
content: `func internal() {}
|
|
|
|
//export_php:function
|
|
func exported() {}
|
|
|
|
func anotherInternal() {
|
|
return "test"
|
|
}`,
|
|
expected: []string{
|
|
"func internal() {}",
|
|
`func anotherInternal() {
|
|
return "test"
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
name: "export comment with spacing",
|
|
content: `//export_php:function
|
|
func exported1() {}
|
|
|
|
//export_php:function
|
|
func exported2() {}
|
|
|
|
// export_php:function
|
|
func exported3() {}`,
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "complex function with nested braces",
|
|
content: `func complex() {
|
|
if true {
|
|
for {
|
|
switch x {
|
|
case 1:
|
|
{
|
|
// nested block
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
expected: []string{
|
|
`func complex() {
|
|
if true {
|
|
for {
|
|
switch x {
|
|
case 1:
|
|
{
|
|
// nested block
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`,
|
|
},
|
|
},
|
|
{
|
|
name: "empty content",
|
|
content: "",
|
|
expected: []string{},
|
|
},
|
|
{
|
|
name: "no functions",
|
|
content: `package main
|
|
|
|
import "fmt"
|
|
|
|
var x = 10`,
|
|
expected: []string{},
|
|
},
|
|
}
|
|
|
|
analyzer := &SourceAnalyzer{}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := analyzer.extractInternalFunctions(tt.content)
|
|
|
|
assert.Len(t, result, len(tt.expected), "function count mismatch")
|
|
|
|
for i, expected := range tt.expected {
|
|
assert.Equal(t, expected, result[i], "function %d mismatch", i)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkSourceAnalyzer_Analyze(b *testing.B) {
|
|
content := `package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"os"
|
|
)
|
|
|
|
func internalOne() {
|
|
fmt.Println("test")
|
|
}
|
|
|
|
//export_php:function
|
|
func exported() string {
|
|
return "exported"
|
|
}
|
|
|
|
func internalTwo() {
|
|
for i := 0; i < 100; i++ {
|
|
if i%2 == 0 {
|
|
fmt.Println(i)
|
|
}
|
|
}
|
|
}`
|
|
|
|
tempDir := b.TempDir()
|
|
filename := filepath.Join(tempDir, "bench.go")
|
|
|
|
require.NoError(b, os.WriteFile(filename, []byte(content), 0644))
|
|
|
|
analyzer := &SourceAnalyzer{}
|
|
|
|
for b.Loop() {
|
|
_, _, err := analyzer.analyze(filename)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkSourceAnalyzer_ExtractInternalFunctions(b *testing.B) {
|
|
content := `func test1() { fmt.Println("1") }
|
|
func test2() { fmt.Println("2") }
|
|
//export_php:function
|
|
func exported() {}
|
|
func test3() {
|
|
for i := 0; i < 10; i++ {
|
|
fmt.Println(i)
|
|
}
|
|
}`
|
|
|
|
analyzer := &SourceAnalyzer{}
|
|
|
|
for b.Loop() {
|
|
analyzer.extractInternalFunctions(content)
|
|
}
|
|
}
|