mirror of
https://github.com/php/frankenphp.git
synced 2026-03-24 00:52:11 +01:00
341 lines
9.5 KiB
Go
341 lines
9.5 KiB
Go
package extgen
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNamespacedClassName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
namespace string
|
|
className string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "no namespace",
|
|
namespace: "",
|
|
className: "MySuperClass",
|
|
expected: "MySuperClass",
|
|
},
|
|
{
|
|
name: "single level namespace",
|
|
namespace: "MyNamespace",
|
|
className: "MySuperClass",
|
|
expected: "MyNamespace_MySuperClass",
|
|
},
|
|
{
|
|
name: "multi level namespace",
|
|
namespace: `Go\Extension`,
|
|
className: "MySuperClass",
|
|
expected: "Go_Extension_MySuperClass",
|
|
},
|
|
{
|
|
name: "deep namespace",
|
|
namespace: `My\Deep\Nested\Namespace`,
|
|
className: "TestClass",
|
|
expected: "My_Deep_Nested_Namespace_TestClass",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := NamespacedName(tt.namespace, tt.className)
|
|
require.Equal(t, tt.expected, result, "expected %q, got %q", tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCFileGenerationWithNamespace(t *testing.T) {
|
|
content := `package main
|
|
|
|
//export_php:namespace Go\Extension
|
|
|
|
//export_php:class MySuperClass
|
|
type MySuperClass struct{}
|
|
|
|
//export_php:method MySuperClass test(): string
|
|
func (m *MySuperClass) Test() string {
|
|
return "test"
|
|
}
|
|
`
|
|
|
|
tmpfile, err := os.CreateTemp("", "test_cfile_namespace_*.go")
|
|
require.NoError(t, err, "Failed to create temp file")
|
|
defer func() {
|
|
err := os.Remove(tmpfile.Name())
|
|
assert.NoError(t, err, "Failed to remove temp file: %v", err)
|
|
}()
|
|
|
|
_, err = tmpfile.Write([]byte(content))
|
|
require.NoError(t, err, "Failed to write to temp file")
|
|
|
|
err = tmpfile.Close()
|
|
require.NoError(t, err, "Failed to close temp file")
|
|
|
|
generator := &Generator{
|
|
BaseName: "test_extension",
|
|
SourceFile: tmpfile.Name(),
|
|
BuildDir: t.TempDir(),
|
|
Namespace: `Go\Extension`,
|
|
Classes: []phpClass{
|
|
{
|
|
Name: "MySuperClass",
|
|
GoStruct: "MySuperClass",
|
|
Methods: []phpClassMethod{
|
|
{
|
|
Name: "test",
|
|
PhpName: "test",
|
|
Signature: "test(): string",
|
|
ReturnType: "string",
|
|
ClassName: "MySuperClass",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
cFileGen := cFileGenerator{generator: generator}
|
|
contentResult, err := cFileGen.getTemplateContent()
|
|
require.NoError(t, err, "error generating C file")
|
|
|
|
expectedCall := "register_class_Go_Extension_MySuperClass()"
|
|
require.Contains(t, contentResult, expectedCall, "C file should contain the standard function call")
|
|
|
|
oldCall := "register_class_MySuperClass()"
|
|
require.NotContains(t, contentResult, oldCall, "C file should not contain old non-namespaced call")
|
|
}
|
|
|
|
func TestCFileGenerationWithoutNamespace(t *testing.T) {
|
|
generator := &Generator{
|
|
BaseName: "test_extension",
|
|
BuildDir: t.TempDir(),
|
|
Namespace: "",
|
|
Classes: []phpClass{
|
|
{
|
|
Name: "MySuperClass",
|
|
GoStruct: "MySuperClass",
|
|
},
|
|
},
|
|
}
|
|
|
|
cFileGen := cFileGenerator{generator: generator}
|
|
contentResult, err := cFileGen.getTemplateContent()
|
|
require.NoError(t, err, "error generating C file")
|
|
|
|
expectedCall := "register_class_MySuperClass()"
|
|
require.Contains(t, contentResult, expectedCall, "C file should not contain the standard function call")
|
|
}
|
|
|
|
func TestCFileGenerationWithNamespacedConstants(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
namespace string
|
|
constants []phpConstant
|
|
contains []string
|
|
}{
|
|
{
|
|
name: "integer constant with namespace",
|
|
namespace: `Go\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "TEST_INT", Value: "42", PhpType: phpInt},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_LONG_CONSTANT("Go\\Extension", "TEST_INT", 42, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "string constant with namespace",
|
|
namespace: `Go\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "TEST_STRING", Value: `"hello"`, PhpType: phpString},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_STRING_CONSTANT("Go\\Extension", "TEST_STRING", "hello", CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "float constant with namespace",
|
|
namespace: `Go\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "TEST_FLOAT", Value: "3.14", PhpType: phpFloat},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_DOUBLE_CONSTANT("Go\\Extension", "TEST_FLOAT", 3.14, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "bool constant with namespace",
|
|
namespace: `Go\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "TEST_BOOL", Value: "true", PhpType: phpBool},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_BOOL_CONSTANT("Go\\Extension", "TEST_BOOL", true, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "iota constant with namespace",
|
|
namespace: `Go\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "STATUS_OK", Value: "STATUS_OK", PhpType: phpInt, IsIota: true},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_LONG_CONSTANT("Go\\Extension", "STATUS_OK", STATUS_OK, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "multiple constants with deep namespace",
|
|
namespace: `My\Deep\Namespace`,
|
|
constants: []phpConstant{
|
|
{Name: "CONST_INT", Value: "100", PhpType: phpInt},
|
|
{Name: "CONST_STR", Value: `"value"`, PhpType: phpString},
|
|
{Name: "CONST_FLOAT", Value: "1.5", PhpType: phpFloat},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_LONG_CONSTANT("My\\Deep\\Namespace", "CONST_INT", 100, CONST_CS | CONST_PERSISTENT);`,
|
|
`REGISTER_NS_STRING_CONSTANT("My\\Deep\\Namespace", "CONST_STR", "value", CONST_CS | CONST_PERSISTENT);`,
|
|
`REGISTER_NS_DOUBLE_CONSTANT("My\\Deep\\Namespace", "CONST_FLOAT", 1.5, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "single level namespace",
|
|
namespace: `TestNamespace`,
|
|
constants: []phpConstant{
|
|
{Name: "MY_CONST", Value: "999", PhpType: phpInt},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_LONG_CONSTANT("TestNamespace", "MY_CONST", 999, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "namespace with trailing backslash",
|
|
namespace: `TestIntegration\Extension`,
|
|
constants: []phpConstant{
|
|
{Name: "VERSION", Value: `"1.0.0"`, PhpType: phpString},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_NS_STRING_CONSTANT("TestIntegration\\Extension", "VERSION", "1.0.0", CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
generator := &Generator{
|
|
BaseName: "test_ext",
|
|
Namespace: tt.namespace,
|
|
Constants: tt.constants,
|
|
}
|
|
|
|
cGen := cFileGenerator{generator: generator}
|
|
content, err := cGen.buildContent()
|
|
require.NoError(t, err, "Failed to build C file content")
|
|
|
|
for _, expected := range tt.contains {
|
|
assert.Contains(t, content, expected, "Generated C content should contain '%s'", expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCFileGenerationWithoutNamespacedConstants(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
namespace string
|
|
constants []phpConstant
|
|
contains []string
|
|
}{
|
|
{
|
|
name: "integer constant without namespace",
|
|
namespace: "",
|
|
constants: []phpConstant{
|
|
{Name: "GLOBAL_INT", Value: "42", PhpType: phpInt},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_LONG_CONSTANT("GLOBAL_INT", 42, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "string constant without namespace",
|
|
namespace: "",
|
|
constants: []phpConstant{
|
|
{Name: "GLOBAL_STRING", Value: `"test"`, PhpType: phpString},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_STRING_CONSTANT("GLOBAL_STRING", "test", CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "float constant without namespace",
|
|
namespace: "",
|
|
constants: []phpConstant{
|
|
{Name: "GLOBAL_FLOAT", Value: "2.71", PhpType: phpFloat},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_DOUBLE_CONSTANT("GLOBAL_FLOAT", 2.71, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "bool constant without namespace",
|
|
namespace: "",
|
|
constants: []phpConstant{
|
|
{Name: "GLOBAL_BOOL", Value: "false", PhpType: phpBool},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_BOOL_CONSTANT("GLOBAL_BOOL", false, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
{
|
|
name: "iota constant without namespace",
|
|
namespace: "",
|
|
constants: []phpConstant{
|
|
{Name: "ERROR_CODE", Value: "ERROR_CODE", PhpType: phpInt, IsIota: true},
|
|
},
|
|
contains: []string{
|
|
`REGISTER_LONG_CONSTANT("ERROR_CODE", ERROR_CODE, CONST_CS | CONST_PERSISTENT);`,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
generator := &Generator{
|
|
BaseName: "test_ext",
|
|
Namespace: tt.namespace,
|
|
Constants: tt.constants,
|
|
}
|
|
|
|
cGen := cFileGenerator{generator: generator}
|
|
content, err := cGen.buildContent()
|
|
require.NoError(t, err, "Failed to build C file content")
|
|
|
|
for _, expected := range tt.contains {
|
|
assert.Contains(t, content, expected, "Generated C content should contain '%s'", expected)
|
|
}
|
|
|
|
assert.NotContains(t, content, "REGISTER_NS_", "Content should NOT contain namespaced constant macros when namespace is empty")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCFileTemplateFunctionMapCString(t *testing.T) {
|
|
generator := &Generator{
|
|
BaseName: "test_ext",
|
|
Namespace: `My\Namespace\Test`,
|
|
Constants: []phpConstant{
|
|
{Name: "MY_CONST", Value: "123", PhpType: phpInt},
|
|
},
|
|
}
|
|
|
|
cGen := cFileGenerator{generator: generator}
|
|
content, err := cGen.getTemplateContent()
|
|
require.NoError(t, err, "Failed to get template content")
|
|
|
|
assert.Contains(t, content, `"My\\Namespace\\Test"`, "Template should properly escape namespace backslashes using cString filter")
|
|
assert.NotContains(t, content, `"My\Namespace\Test"`, "Template should not contain unescaped namespace (single backslashes)")
|
|
}
|