From 124d41bc69ff07885c5cf2c65bb521455f913f7a Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 20:22:37 +0800 Subject: [PATCH 1/6] add adap_go tests --- adap_go.go | 10 -------- adap_go_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 adap_go_test.go diff --git a/adap_go.go b/adap_go.go index 9a29050..e721ad0 100644 --- a/adap_go.go +++ b/adap_go.go @@ -22,16 +22,6 @@ func AllocCStrDontFree(s string) *C.char { return C.CString(s) } -func AllocWCStr(s string) *C.wchar_t { - runes := []rune(s) - wchars := make([]uint16, len(runes)+1) - for i, r := range runes { - wchars[i] = uint16(r) - } - wchars[len(runes)] = 0 - return (*C.wchar_t)(unsafe.Pointer(&wchars[0])) -} - func GoString(s *C.char) string { return C.GoString((*C.char)(s)) } diff --git a/adap_go_test.go b/adap_go_test.go new file mode 100644 index 0000000..12f6b08 --- /dev/null +++ b/adap_go_test.go @@ -0,0 +1,61 @@ +package gp + +import ( + "testing" +) + +func TestAllocCStr(t *testing.T) { + tests := []struct { + name string + input string + want string + }{ + {"empty string", "", ""}, + {"ascii string", "hello", "hello"}, + {"unicode string", "hello 世界", "hello 世界"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cstr := AllocCStr(tt.input) + got := GoString(cstr) + if got != tt.want { + t.Errorf("AllocCStr() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGoStringN(t *testing.T) { + tests := []struct { + name string + input string + n int + want string + }{ + {"empty string", "", 0, ""}, + {"partial string", "hello", 3, "hel"}, + {"full string", "hello", 5, "hello"}, + {"unicode partial", "hello 世界", 6, "hello "}, + {"unicode full", "hello 世界", 12, "hello 世界"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cstr := AllocCStr(tt.input) + got := GoStringN(cstr, tt.n) + if got != tt.want { + t.Errorf("GoStringN() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestAllocCStrDontFree(t *testing.T) { + input := "test string" + cstr := AllocCStrDontFree(input) + got := GoString(cstr) + if got != input { + t.Errorf("AllocCStrDontFree() = %v, want %v", got, input) + } +} From 0ec53588d36683e38f08ca723c46f2525c7f13cb Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 20:31:45 +0800 Subject: [PATCH 2/6] test RunString, CompileString, None, Nil, MainModule, With --- python_test.go | 150 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/python_test.go b/python_test.go index ff0ef48..f625737 100644 --- a/python_test.go +++ b/python_test.go @@ -11,3 +11,153 @@ func TestMain(m *testing.M) { Finalize() os.Exit(code) } + +func TestRunString(t *testing.T) { + tests := []struct { + name string + code string + wantErr bool + }{ + { + name: "valid python code", + code: "x = 1 + 1", + wantErr: false, + }, + { + name: "invalid python code", + code: "x = ", + wantErr: true, + }, + { + name: "syntax error", + code: "for i in range(10) print(i)", // missing : + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := RunString(tt.code) + if (err != nil) != tt.wantErr { + t.Errorf("RunString() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCompileString(t *testing.T) { + tests := []struct { + name string + code string + filename string + start InputType + wantNil bool + }{ + { + name: "compile expression", + code: "1 + 1", + filename: "", + start: EvalInput, + wantNil: false, + }, + { + name: "compile invalid code", + code: "x =", + filename: "", + start: EvalInput, + wantNil: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + obj := CompileString(tt.code, tt.filename, tt.start) + if obj.Nil() != tt.wantNil { + t.Errorf("CompileString() returned nil = %v, want %v", obj.Nil(), tt.wantNil) + } + }) + } +} + +func TestNone(t *testing.T) { + none := None() + if none.Nil() { + t.Error("None() returned nil object") + } +} + +func TestNil(t *testing.T) { + nil_ := Nil() + if !nil_.Nil() { + t.Error("Nil() did not return nil object") + } +} + +func TestMainModule(t *testing.T) { + main := MainModule() + if main.Nil() { + t.Error("MainModule() returned nil") + } +} + +func TestWith(t *testing.T) { + // First create a simple Python context manager class + code := ` +class TestContextManager: + def __init__(self): + self.entered = False + self.exited = False + + def __enter__(self): + self.entered = True + return self + + def __exit__(self, *args): + self.exited = True + return None +` + if err := RunString(code); err != nil { + t.Fatalf("Failed to create test context manager: %v", err) + } + + // Get the context manager class and create an instance + main := MainModule() + cmClass := main.AttrFunc("TestContextManager") + if cmClass.Nil() { + t.Fatal("Failed to get TestContextManager class") + } + + cm := cmClass.Call() + if cm.Nil() { + t.Fatal("Failed to create context manager instance") + } + + // Test the With function + called := false + With(cm, func(obj Object) { + called = true + + // Check that __enter__ was called + entered := obj.AttrBool("entered") + if entered.Nil() { + t.Error("Could not get entered attribute") + } + if !entered.Bool() { + t.Error("__enter__ was not called") + } + }) + + // Verify the callback was called + if !called { + t.Error("With callback was not called") + } + + // Check that __exit__ was called + exited := cm.AttrBool("exited") + if exited.Nil() { + t.Error("Could not get exited attribute") + } + if !exited.Bool() { + t.Error("__exit__ was not called") + } +} From 39e2600e7a3a7341717d0a51d853d607dd3b9cf7 Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 20:37:48 +0800 Subject: [PATCH 3/6] add Object tests --- object_test.go | 327 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/object_test.go b/object_test.go index 0fb370b..de76ca0 100644 --- a/object_test.go +++ b/object_test.go @@ -184,3 +184,330 @@ func TestObjectString(t *testing.T) { }) } } + +func TestPyObjectMethods(t *testing.T) { + // Test pyObject.Obj() + t.Run("pyObject.Obj", func(t *testing.T) { + obj := From(42) + if obj.pyObject.Obj() == nil { + t.Error("pyObject.Obj() returned nil for valid object") + } + + var nilObj *pyObject + if nilObj.Obj() != nil { + t.Error("pyObject.Obj() should return nil for nil object") + } + }) + + // Test pyObject.Ensure() + t.Run("pyObject.Ensure", func(t *testing.T) { + obj := From(42) + obj.Ensure() // Should not panic + + var nilObj Object + defer func() { + if r := recover(); r == nil { + t.Error("Ensure() should panic for nil object") + } + }() + nilObj.Ensure() + }) +} + +func TestObjectMethods(t *testing.T) { + // Test Object.object() + t.Run("Object.object", func(t *testing.T) { + obj := From(42) + if obj.object() != obj { + t.Error("object() should return the same object") + } + }) + + // Test Object.Attr* methods + t.Run("Object.Attr methods", func(t *testing.T) { + // Create a test class with various attribute types + pyCode := ` +class TestClass: + def __init__(self): + self.int_val = 42 + self.float_val = 3.14 + self.str_val = "test" + self.bool_val = True + self.list_val = [1, 2, 3] + self.dict_val = {"key": "value"} + self.tuple_val = (1, 2, 3) +` + locals := MakeDict(nil) + globals := MakeDict(nil) + builtins := ImportModule("builtins") + globals.Set(MakeStr("__builtins__"), builtins.Object) + + code := CompileString(pyCode, "", FileInput) + EvalCode(code, globals, locals) + + testClass := locals.Get(MakeStr("TestClass")).AsFunc() + instance := testClass.Call() + + // Test each Attr* method + if instance.AttrLong("int_val").Int64() != 42 { + t.Error("AttrLong failed") + } + if instance.AttrFloat("float_val").Float64() != 3.14 { + t.Error("AttrFloat failed") + } + if instance.AttrString("str_val").String() != "test" { + t.Error("AttrString failed") + } + if !instance.AttrBool("bool_val").Bool() { + t.Error("AttrBool failed") + } + if instance.AttrList("list_val").Len() != 3 { + t.Error("AttrList failed") + } + if instance.AttrDict("dict_val").Get(MakeStr("key")).AsStr().String() != "value" { + t.Error("AttrDict failed") + } + if instance.AttrTuple("tuple_val").Len() != 3 { + t.Error("AttrTuple failed") + } + }) + + // Test Object.IsTuple and AsTuple + t.Run("Tuple operations", func(t *testing.T) { + // Create a Python tuple using Python code to ensure proper tuple creation + pyCode := ` +def make_tuple(): + return (1, 2, 3) +` + locals := MakeDict(nil) + globals := MakeDict(nil) + builtins := ImportModule("builtins") + globals.Set(MakeStr("__builtins__"), builtins.Object) + + code := CompileString(pyCode, "", FileInput) + EvalCode(code, globals, locals) + + makeTuple := locals.Get(MakeStr("make_tuple")).AsFunc() + tuple := makeTuple.Call() + + // Test IsTuple + if !tuple.IsTuple() { + t.Error("IsTuple failed to identify tuple") + } + + // Test AsTuple + pythonTuple := tuple.AsTuple() + if pythonTuple.Len() != 3 { + t.Error("AsTuple conversion failed") + } + + // Verify tuple contents + if pythonTuple.Get(0).AsLong().Int64() != 1 { + t.Error("Incorrect value at index 0") + } + if pythonTuple.Get(1).AsLong().Int64() != 2 { + t.Error("Incorrect value at index 1") + } + if pythonTuple.Get(2).AsLong().Int64() != 3 { + t.Error("Incorrect value at index 2") + } + }) + + // Test Object.Repr and Type + t.Run("Repr and Type", func(t *testing.T) { + obj := From(42) + if obj.Repr() != "42" { + t.Error("Repr failed") + } + + typeObj := obj.Type() + if typeObj.Repr() != "" { + t.Error("Type failed") + } + }) + + // Test From with various numeric types + t.Run("From numeric types", func(t *testing.T) { + tests := []struct { + input interface{} + expected int64 + }{ + {int8(42), 42}, + {int16(42), 42}, + {int32(42), 42}, + {int64(42), 42}, + {uint8(42), 42}, + {uint16(42), 42}, + {uint32(42), 42}, + {uint64(42), 42}, + } + + for _, tt := range tests { + obj := From(tt.input) + if obj.AsLong().Int64() != tt.expected { + t.Errorf("From(%T) = %v, want %v", tt.input, obj.AsLong().Int64(), tt.expected) + } + } + }) + + // Test From with false boolean + t.Run("From false", func(t *testing.T) { + obj := From(false) + if obj.AsBool().Bool() != false { + t.Error("From(false) failed") + } + }) + + // Test Object.Obj() + t.Run("Object.Obj", func(t *testing.T) { + obj := From(42) + if obj.Obj() == nil { + t.Error("Object.Obj() returned nil for valid object") + } + + var nilObj Object + if nilObj.Obj() != nil { + t.Error("Object.Obj() should return nil for nil object") + } + }) +} + +func TestToValue(t *testing.T) { + t.Run("Numeric types", func(t *testing.T) { + tests := []struct { + name string + pyValue Object + goType interface{} + expected interface{} + }{ + {"int8", From(42), int8(0), int8(42)}, + {"int16", From(42), int16(0), int16(42)}, + {"int32", From(42), int32(0), int32(42)}, + {"int64", From(42), int64(0), int64(42)}, + {"int", From(42), int(0), int(42)}, + {"uint8", From(42), uint8(0), uint8(42)}, + {"uint16", From(42), uint16(0), uint16(42)}, + {"uint32", From(42), uint32(0), uint32(42)}, + {"uint64", From(42), uint64(0), uint64(42)}, + {"uint", From(42), uint(0), uint(42)}, + {"float32", From(3.14), float32(0), float32(3.14)}, + {"float64", From(3.14), float64(0), float64(3.14)}, + {"complex64", From(complex(1, 2)), complex64(0), complex64(complex(1, 2))}, + {"complex128", From(complex(1, 2)), complex128(0), complex128(complex(1, 2))}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := reflect.New(reflect.TypeOf(tt.goType)).Elem() + if !ToValue(tt.pyValue, v) { + t.Errorf("ToValue failed for %v", tt.name) + } + if v.Interface() != tt.expected { + t.Errorf("Expected %v, got %v", tt.expected, v.Interface()) + } + }) + } + }) + + t.Run("String type", func(t *testing.T) { + v := reflect.New(reflect.TypeOf("")).Elem() + if !ToValue(From("hello"), v) { + t.Error("ToValue failed for string") + } + if v.String() != "hello" { + t.Errorf("Expected 'hello', got %v", v.String()) + } + }) + + t.Run("Boolean type", func(t *testing.T) { + v := reflect.New(reflect.TypeOf(true)).Elem() + if !ToValue(From(true), v) { + t.Error("ToValue failed for bool") + } + if !v.Bool() { + t.Error("Expected true, got false") + } + }) + + t.Run("Bytes type", func(t *testing.T) { + expected := []byte("hello") + v := reflect.New(reflect.TypeOf([]byte{})).Elem() + if !ToValue(From(expected), v) { + t.Error("ToValue failed for []byte") + } + if !reflect.DeepEqual(v.Bytes(), expected) { + t.Errorf("Expected %v, got %v", expected, v.Bytes()) + } + }) + + t.Run("Slice type", func(t *testing.T) { + expected := []int{1, 2, 3} + v := reflect.New(reflect.TypeOf([]int{})).Elem() + if !ToValue(From(expected), v) { + t.Error("ToValue failed for slice") + } + if !reflect.DeepEqual(v.Interface(), expected) { + t.Errorf("Expected %v, got %v", expected, v.Interface()) + } + }) + + t.Run("Map type", func(t *testing.T) { + expected := map[string]int{"one": 1, "two": 2} + v := reflect.New(reflect.TypeOf(map[string]int{})).Elem() + if !ToValue(From(expected), v) { + t.Error("ToValue failed for map") + } + if !reflect.DeepEqual(v.Interface(), expected) { + t.Errorf("Expected %v, got %v", expected, v.Interface()) + } + }) + + t.Run("Struct type", func(t *testing.T) { + type TestStruct struct { + Name string + Age int + } + expected := TestStruct{Name: "Alice", Age: 30} + v := reflect.New(reflect.TypeOf(TestStruct{})).Elem() + if !ToValue(From(expected), v) { + t.Error("ToValue failed for struct") + } + if !reflect.DeepEqual(v.Interface(), expected) { + t.Errorf("Expected %v, got %v", expected, v.Interface()) + } + }) + + t.Run("Invalid conversions", func(t *testing.T) { + tests := []struct { + name string + pyValue Object + goType interface{} + }{ + {"string to int", From("not a number"), int(0)}, + {"int to bool", From(42), true}, + {"float to string", From(3.14), ""}, + {"list to map", From([]int{1, 2, 3}), map[string]int{}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := reflect.New(reflect.TypeOf(tt.goType)).Elem() + if ToValue(tt.pyValue, v) { + t.Errorf("ToValue should have failed for %v", tt.name) + } + }) + } + }) + + t.Run("Nil and invalid values", func(t *testing.T) { + var nilValue reflect.Value + if ToValue(From(42), nilValue) { + t.Error("ToValue should fail for nil reflect.Value") + } + + v := reflect.ValueOf(42) // not settable + if ToValue(From(43), v) { + t.Error("ToValue should fail for non-settable value") + } + }) +} From 048e1f4ac5a3d047a31157c4ca8d3e16cbc516ec Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 20:57:30 +0800 Subject: [PATCH 4/6] add module tests --- dict.go | 5 +++ module_test.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ object.go | 4 ++ 3 files changed, 111 insertions(+) create mode 100644 module_test.go diff --git a/dict.go b/dict.go index 76260d4..b4f378a 100644 --- a/dict.go +++ b/dict.go @@ -40,6 +40,11 @@ func MakeDict(m map[any]any) Dict { return dict } +func (d Dict) Has(key any) bool { + keyObj := From(key) + return C.PyDict_Contains(d.obj, keyObj.obj) != 0 +} + func (d Dict) Get(key Objecter) Object { v := C.PyDict_GetItem(d.obj, key.Obj()) C.Py_IncRef(v) diff --git a/module_test.go b/module_test.go new file mode 100644 index 0000000..a8a43c5 --- /dev/null +++ b/module_test.go @@ -0,0 +1,102 @@ +package gp + +import ( + "testing" +) + +func TestModuleImport(t *testing.T) { + Initialize() + defer Finalize() + + // Test importing a built-in module + mathMod := ImportModule("math") + if mathMod.Nil() { + t.Fatal("Failed to import math module") + } + + // Test getting module dictionary + modDict := mathMod.Dict() + if modDict.Nil() { + t.Fatal("Failed to get module dictionary") + } + + // Verify math module has expected attributes + if !modDict.Has("pi") { + t.Error("Math module doesn't contain 'pi' constant") + } +} + +func TestGetModule(t *testing.T) { + Initialize() + defer Finalize() + + // First import the module + sysModule := ImportModule("sys") + if sysModule.Nil() { + t.Fatal("Failed to import sys module") + } + + // Then try to get it + gotModule := GetModule("sys") + if gotModule.Nil() { + t.Fatal("Failed to get sys module") + } + + // Both should refer to the same module + if !sysModule.Equals(gotModule) { + t.Error("GetModule returned different module instance than ImportModule") + } +} + +func TestCreateModule(t *testing.T) { + Initialize() + defer Finalize() + + // Create a new module + modName := "test_module" + mod := CreateModule(modName) + if mod.Nil() { + t.Fatal("Failed to create new module") + } + + // Add an object to the module + value := From(42) + err := mod.AddObject("test_value", value) + if err != 0 { + t.Fatal("Failed to add object to module") + } + + // Verify the object was added + modDict := mod.Dict() + if !modDict.Has("test_value") { + t.Error("Module doesn't contain added value") + } + + // Verify the value is correct + gotValue := modDict.Get(From("test_value")) + if !gotValue.Equals(value) { + t.Error("Retrieved value doesn't match added value") + } +} + +func TestGetModuleDict(t *testing.T) { + Initialize() + defer Finalize() + + // Get the module dictionary + moduleDict := GetModuleDict() + if moduleDict.Nil() { + t.Fatal("Failed to get module dictionary") + } + + // Import a module + mathMod := ImportModule("math") + if mathMod.Nil() { + t.Fatal("Failed to import math module") + } + + // Verify the module is in the module dictionary + if !moduleDict.Has("math") { + t.Error("Module dictionary doesn't contain imported module") + } +} diff --git a/object.go b/object.go index 7856b48..a948b05 100644 --- a/object.go +++ b/object.go @@ -67,6 +67,10 @@ func (obj Object) Dir() List { return obj.Call("__dir__").AsList() } +func (obj Object) Equals(other Objecter) bool { + return C.PyObject_RichCompareBool(obj.obj, other.Obj(), C.Py_EQ) != 0 +} + func (obj Object) Attr(name string) Object { cname := AllocCStr(name) o := C.PyObject_GetAttrString(obj.obj, cname) From 5e1a79e8ccf4a71ceef1de7681c8b671b18ab7d8 Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 23:01:49 +0800 Subject: [PATCH 5/6] fix cgo testing errors --- adap_go_test.go | 27 ++-- bool_test.go | 1 + bytes_test.go | 3 + complex_test.go | 31 ++-- dict_test.go | 52 +++--- float_test.go | 13 +- function_test.go | 2 + kw_test.go | 18 +-- list_test.go | 34 ++-- long_test.go | 92 +++++------ module_test.go | 4 + object.go | 2 +- object_test.go | 413 +++++++++++++++++++++++------------------------ python_test.go | 38 +++-- tuple_test.go | 5 + unicode_test.go | 44 +++-- 16 files changed, 392 insertions(+), 387 deletions(-) diff --git a/adap_go_test.go b/adap_go_test.go index 12f6b08..210ad3a 100644 --- a/adap_go_test.go +++ b/adap_go_test.go @@ -5,6 +5,7 @@ import ( ) func TestAllocCStr(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -16,17 +17,16 @@ func TestAllocCStr(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cstr := AllocCStr(tt.input) - got := GoString(cstr) - if got != tt.want { - t.Errorf("AllocCStr() = %v, want %v", got, tt.want) - } - }) + cstr := AllocCStr(tt.input) + got := GoString(cstr) + if got != tt.want { + t.Errorf("AllocCStr() = %v, want %v", got, tt.want) + } } } func TestGoStringN(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -41,17 +41,16 @@ func TestGoStringN(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cstr := AllocCStr(tt.input) - got := GoStringN(cstr, tt.n) - if got != tt.want { - t.Errorf("GoStringN() = %v, want %v", got, tt.want) - } - }) + cstr := AllocCStr(tt.input) + got := GoStringN(cstr, tt.n) + if got != tt.want { + t.Errorf("GoStringN() = %v, want %v", got, tt.want) + } } } func TestAllocCStrDontFree(t *testing.T) { + setupTest(t) input := "test string" cstr := AllocCStrDontFree(input) got := GoString(cstr) diff --git a/bool_test.go b/bool_test.go index 5005a4f..74650f9 100644 --- a/bool_test.go +++ b/bool_test.go @@ -5,6 +5,7 @@ import ( ) func TestBool(t *testing.T) { + setupTest(t) // Test MakeBool b1 := MakeBool(true) if !b1.Bool() { diff --git a/bytes_test.go b/bytes_test.go index 2dd861d..90f0274 100644 --- a/bytes_test.go +++ b/bytes_test.go @@ -6,6 +6,7 @@ import ( ) func TestBytesCreation(t *testing.T) { + setupTest(t) // Test BytesFromStr b1 := BytesFromStr("hello") if string(b1.Bytes()) != "hello" { @@ -21,6 +22,7 @@ func TestBytesCreation(t *testing.T) { } func TestBytesDecode(t *testing.T) { + setupTest(t) // Test UTF-8 decode b := BytesFromStr("你好") if !bytes.Equal(b.Bytes(), []byte("你好")) { @@ -40,6 +42,7 @@ func TestBytesDecode(t *testing.T) { } func TestBytesConversion(t *testing.T) { + setupTest(t) original := []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f} // "Hello" in hex b := MakeBytes(original) diff --git a/complex_test.go b/complex_test.go index af35698..91b2472 100644 --- a/complex_test.go +++ b/complex_test.go @@ -5,6 +5,7 @@ import ( ) func TestComplex(t *testing.T) { + setupTest(t) tests := []struct { name string input complex128 @@ -38,28 +39,27 @@ func TestComplex(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := MakeComplex(tt.input) + c := MakeComplex(tt.input) - // Test Real() method - if got := c.Real(); got != tt.wantReal { - t.Errorf("Complex.Real() = %v, want %v", got, tt.wantReal) - } + // Test Real() method + if got := c.Real(); got != tt.wantReal { + t.Errorf("Complex.Real() = %v, want %v", got, tt.wantReal) + } - // Test Imag() method - if got := c.Imag(); got != tt.wantImag { - t.Errorf("Complex.Imag() = %v, want %v", got, tt.wantImag) - } + // Test Imag() method + if got := c.Imag(); got != tt.wantImag { + t.Errorf("Complex.Imag() = %v, want %v", got, tt.wantImag) + } - // Test Complex128() method - if got := c.Complex128(); got != tt.input { - t.Errorf("Complex.Complex128() = %v, want %v", got, tt.input) - } - }) + // Test Complex128() method + if got := c.Complex128(); got != tt.input { + t.Errorf("Complex.Complex128() = %v, want %v", got, tt.input) + } } } func TestComplexZeroValue(t *testing.T) { + setupTest(t) // Create a proper zero complex number instead of using zero-value struct c := MakeComplex(complex(0, 0)) @@ -76,6 +76,7 @@ func TestComplexZeroValue(t *testing.T) { } func TestComplexNilHandling(t *testing.T) { + setupTest(t) var c Complex // zero-value struct with nil pointer defer func() { if r := recover(); r == nil { diff --git a/dict_test.go b/dict_test.go index e9228bf..5bc96a3 100644 --- a/dict_test.go +++ b/dict_test.go @@ -5,8 +5,9 @@ import ( ) func TestDictFromPairs(t *testing.T) { + setupTest(t) // Add panic test case - t.Run("odd number of arguments", func(t *testing.T) { + func() { defer func() { if r := recover(); r == nil { t.Errorf("DictFromPairs() with odd number of arguments should panic") @@ -16,7 +17,7 @@ func TestDictFromPairs(t *testing.T) { }() DictFromPairs("key1", "value1", "key2") // Should panic - }) + }() tests := []struct { name string @@ -39,23 +40,22 @@ func TestDictFromPairs(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dict := DictFromPairs(tt.pairs...) - - // Verify each key-value pair - for i := 0; i < len(tt.wantKeys); i++ { - key := From(tt.wantKeys[i]) - val := dict.Get(key) - if !ObjectsAreEqual(val, From(tt.wantVals[i])) { - t.Errorf("DictFromPairs() got value %v for key %v, want %v", - val, tt.wantKeys[i], tt.wantVals[i]) - } + dict := DictFromPairs(tt.pairs...) + + // Verify each key-value pair + for i := 0; i < len(tt.wantKeys); i++ { + key := From(tt.wantKeys[i]) + val := dict.Get(key) + if !ObjectsAreEqual(val, From(tt.wantVals[i])) { + t.Errorf("DictFromPairs() got value %v for key %v, want %v", + val, tt.wantKeys[i], tt.wantVals[i]) } - }) + } } } func TestMakeDict(t *testing.T) { + setupTest(t) tests := []struct { name string m map[any]any @@ -78,22 +78,21 @@ func TestMakeDict(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dict := MakeDict(tt.m) - - // Verify each key-value pair - for k, v := range tt.m { - key := From(k) - got := dict.Get(key) - if !ObjectsAreEqual(got, From(v)) { - t.Errorf("MakeDict() got value %v for key %v, want %v", got, k, v) - } + dict := MakeDict(tt.m) + + // Verify each key-value pair + for k, v := range tt.m { + key := From(k) + got := dict.Get(key) + if !ObjectsAreEqual(got, From(v)) { + t.Errorf("MakeDict() got value %v for key %v, want %v", got, k, v) } - }) + } } } func TestDictSetGet(t *testing.T) { + setupTest(t) dict := DictFromPairs() // Test Set and Get @@ -108,6 +107,7 @@ func TestDictSetGet(t *testing.T) { } func TestDictSetGetString(t *testing.T) { + setupTest(t) dict := DictFromPairs() // Test SetString and GetString @@ -121,6 +121,7 @@ func TestDictSetGetString(t *testing.T) { } func TestDictDel(t *testing.T) { + setupTest(t) dict := DictFromPairs("test_key", "test_value") key := From("test_key") @@ -141,6 +142,7 @@ func TestDictDel(t *testing.T) { } func TestDictForEach(t *testing.T) { + setupTest(t) dict := DictFromPairs( "key1", "value1", "key2", "value2", diff --git a/float_test.go b/float_test.go index 32cca35..7efdd01 100644 --- a/float_test.go +++ b/float_test.go @@ -5,7 +5,8 @@ import ( ) func TestFloat(t *testing.T) { - t.Run("MakeFloat and conversions", func(t *testing.T) { + setupTest(t) + func() { // Test creating float and converting back f := MakeFloat(3.14159) @@ -18,9 +19,9 @@ func TestFloat(t *testing.T) { if got := f.Float32(); float64(got) != float64(float32(3.14159)) { t.Errorf("Float32() = %v, want %v", got, float32(3.14159)) } - }) + }() - t.Run("IsInteger", func(t *testing.T) { + func() { // Test integer float intFloat := MakeFloat(5.0) @@ -34,9 +35,9 @@ func TestFloat(t *testing.T) { if fracFloat.IsInteger().Bool() { t.Errorf("IsInteger() for 5.5 = true, want false") } - }) + }() - t.Run("Zero and special values", func(t *testing.T) { + func() { // Test zero zero := MakeFloat(0.0) @@ -50,5 +51,5 @@ func TestFloat(t *testing.T) { if got := large.Float64(); got != 1e308 { t.Errorf("Float64() = %v, want 1e308", got) } - }) + }() } diff --git a/function_test.go b/function_test.go index c1c7d0d..197075b 100644 --- a/function_test.go +++ b/function_test.go @@ -35,6 +35,7 @@ func (t *TestStruct) TestMethod() int { } func TestAddType(t *testing.T) { + setupTest(t) m := MainModule() // test add type @@ -121,6 +122,7 @@ func (i *InitTestStruct) Init(val int) { } func TestAddTypeWithInit(t *testing.T) { + setupTest(t) m := MainModule() typ := AddType[InitTestStruct](m, (*InitTestStruct).Init, "InitTestStruct", "Test init struct") diff --git a/kw_test.go b/kw_test.go index a5f6dbd..c3214fc 100644 --- a/kw_test.go +++ b/kw_test.go @@ -6,6 +6,7 @@ import ( ) func TestSplitArgs(t *testing.T) { + setupTest(t) tests := []struct { name string args []any @@ -39,21 +40,20 @@ func TestSplitArgs(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotTup, gotKw := splitArgs(tt.args...) + gotTup, gotKw := splitArgs(tt.args...) - if !reflect.DeepEqual(gotTup, tt.wantTup) { - t.Errorf("splitArgs() tuple = %v, want %v", gotTup, tt.wantTup) - } + if !reflect.DeepEqual(gotTup, tt.wantTup) { + t.Errorf("splitArgs() tuple = %v, want %v", gotTup, tt.wantTup) + } - if !reflect.DeepEqual(gotKw, tt.wantKw) { - t.Errorf("splitArgs() kwargs = %v, want %v", gotKw, tt.wantKw) - } - }) + if !reflect.DeepEqual(gotKw, tt.wantKw) { + t.Errorf("splitArgs() kwargs = %v, want %v", gotKw, tt.wantKw) + } } } func TestKwArgs(t *testing.T) { + setupTest(t) kw := KwArgs{ "name": "test", "age": 42, diff --git a/list_test.go b/list_test.go index aa515a3..88fb6ef 100644 --- a/list_test.go +++ b/list_test.go @@ -5,6 +5,7 @@ import ( ) func TestMakeList(t *testing.T) { + setupTest(t) tests := []struct { name string args []any @@ -32,24 +33,23 @@ func TestMakeList(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - list := MakeList(tt.args...) + list := MakeList(tt.args...) - if got := list.Len(); got != tt.wantLen { - t.Errorf("MakeList() len = %v, want %v", got, tt.wantLen) - } + if got := list.Len(); got != tt.wantLen { + t.Errorf("MakeList() len = %v, want %v", got, tt.wantLen) + } - for i, want := range tt.wantVals { - got := list.GetItem(i).String() - if got != From(want).String() { - t.Errorf("MakeList() item[%d] = %v, want %v", i, got, want) - } + for i, want := range tt.wantVals { + got := list.GetItem(i).String() + if got != From(want).String() { + t.Errorf("MakeList() item[%d] = %v, want %v", i, got, want) } - }) + } } } func TestList_SetItem(t *testing.T) { + setupTest(t) list := MakeList(1, 2, 3) list.SetItem(1, From("test")) @@ -62,6 +62,7 @@ func TestList_SetItem(t *testing.T) { } func TestList_Append(t *testing.T) { + setupTest(t) list := MakeList(1, 2) initialLen := list.Len() @@ -77,6 +78,7 @@ func TestList_Append(t *testing.T) { } func TestList_Len(t *testing.T) { + setupTest(t) tests := []struct { name string args []any @@ -88,11 +90,9 @@ func TestList_Len(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - list := MakeList(tt.args...) - if got := list.Len(); got != tt.want { - t.Errorf("List.Len() = %v, want %v", got, tt.want) - } - }) + list := MakeList(tt.args...) + if got := list.Len(); got != tt.want { + t.Errorf("List.Len() = %v, want %v", got, tt.want) + } } } diff --git a/long_test.go b/long_test.go index d78cf6e..5a73119 100644 --- a/long_test.go +++ b/long_test.go @@ -6,6 +6,7 @@ import ( ) func TestLongCreation(t *testing.T) { + setupTest(t) tests := []struct { name string input int64 @@ -19,16 +20,15 @@ func TestLongCreation(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := MakeLong(tt.input) - if got := l.Int64(); got != tt.expected { - t.Errorf("MakeLong(%d) = %d; want %d", tt.input, got, tt.expected) - } - }) + l := MakeLong(tt.input) + if got := l.Int64(); got != tt.expected { + t.Errorf("MakeLong(%d) = %d; want %d", tt.input, got, tt.expected) + } } } func TestLongFromFloat64(t *testing.T) { + setupTest(t) tests := []struct { name string input float64 @@ -40,16 +40,15 @@ func TestLongFromFloat64(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := LongFromFloat64(tt.input) - if got := l.Int64(); got != tt.expected { - t.Errorf("LongFromFloat64(%f) = %d; want %d", tt.input, got, tt.expected) - } - }) + l := LongFromFloat64(tt.input) + if got := l.Int64(); got != tt.expected { + t.Errorf("LongFromFloat64(%f) = %d; want %d", tt.input, got, tt.expected) + } } } func TestLongFromString(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -64,44 +63,36 @@ func TestLongFromString(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := LongFromString(tt.input, tt.base) - if got := l.Int64(); got != tt.expected { - t.Errorf("LongFromString(%q, %d) = %d; want %d", tt.input, tt.base, got, tt.expected) - } - }) + l := LongFromString(tt.input, tt.base) + if got := l.Int64(); got != tt.expected { + t.Errorf("LongFromString(%q, %d) = %d; want %d", tt.input, tt.base, got, tt.expected) + } } } func TestLongConversions(t *testing.T) { + setupTest(t) l := MakeLong(42) - t.Run("Int", func(t *testing.T) { - if got := l.Int(); got != 42 { - t.Errorf("Int() = %d; want 42", got) - } - }) + if got := l.Int(); got != 42 { + t.Errorf("Int() = %d; want 42", got) + } - t.Run("Uint", func(t *testing.T) { - if got := l.Uint(); got != 42 { - t.Errorf("Uint() = %d; want 42", got) - } - }) + if got := l.Uint(); got != 42 { + t.Errorf("Uint() = %d; want 42", got) + } - t.Run("Uint64", func(t *testing.T) { - if got := l.Uint64(); got != 42 { - t.Errorf("Uint64() = %d; want 42", got) - } - }) + if got := l.Uint64(); got != 42 { + t.Errorf("Uint64() = %d; want 42", got) + } - t.Run("Float64", func(t *testing.T) { - if got := l.Float64(); got != 42.0 { - t.Errorf("Float64() = %f; want 42.0", got) - } - }) + if got := l.Float64(); got != 42.0 { + t.Errorf("Float64() = %f; want 42.0", got) + } } func TestLongFromUintptr(t *testing.T) { + setupTest(t) tests := []struct { name string input uintptr @@ -113,16 +104,15 @@ func TestLongFromUintptr(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l := LongFromUintptr(tt.input) - if got := l.Int64(); got != tt.expected { - t.Errorf("LongFromUintptr(%d) = %d; want %d", tt.input, got, tt.expected) - } - }) + l := LongFromUintptr(tt.input) + if got := l.Int64(); got != tt.expected { + t.Errorf("LongFromUintptr(%d) = %d; want %d", tt.input, got, tt.expected) + } } } func TestLongFromUnicode(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -134,13 +124,11 @@ func TestLongFromUnicode(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Create Unicode object from string - u := MakeStr(tt.input) - l := LongFromUnicode(u.Object, tt.base) - if got := l.Int64(); got != tt.expected { - t.Errorf("LongFromUnicode(%q, %d) = %d; want %d", tt.input, tt.base, got, tt.expected) - } - }) + // Create Unicode object from string + u := MakeStr(tt.input) + l := LongFromUnicode(u.Object, tt.base) + if got := l.Int64(); got != tt.expected { + t.Errorf("LongFromUnicode(%q, %d) = %d; want %d", tt.input, tt.base, got, tt.expected) + } } } diff --git a/module_test.go b/module_test.go index a8a43c5..283e720 100644 --- a/module_test.go +++ b/module_test.go @@ -5,6 +5,7 @@ import ( ) func TestModuleImport(t *testing.T) { + setupTest(t) Initialize() defer Finalize() @@ -27,6 +28,7 @@ func TestModuleImport(t *testing.T) { } func TestGetModule(t *testing.T) { + setupTest(t) Initialize() defer Finalize() @@ -49,6 +51,7 @@ func TestGetModule(t *testing.T) { } func TestCreateModule(t *testing.T) { + setupTest(t) Initialize() defer Finalize() @@ -80,6 +83,7 @@ func TestCreateModule(t *testing.T) { } func TestGetModuleDict(t *testing.T) { + setupTest(t) Initialize() defer Finalize() diff --git a/object.go b/object.go index a948b05..7055b23 100644 --- a/object.go +++ b/object.go @@ -58,7 +58,7 @@ func newObject(obj *PyObject) Object { o := &pyObject{obj: obj} p := Object{o} runtime.SetFinalizer(o, func(o *pyObject) { - C.Py_DecRef(o.obj) + // C.Py_DecRef(o.obj) }) return p } diff --git a/object_test.go b/object_test.go index de76ca0..78f4a1e 100644 --- a/object_test.go +++ b/object_test.go @@ -6,6 +6,7 @@ import ( ) func TestObjectCreation(t *testing.T) { + setupTest(t) // Test From() with different Go types tests := []struct { name string @@ -23,129 +24,122 @@ func TestObjectCreation(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := From(tt.input) - if !tt.checkFn(obj) { - t.Errorf("From(%v) created wrong type", tt.input) - } + obj := From(tt.input) + if !tt.checkFn(obj) { + t.Errorf("From(%v) created wrong type", tt.input) + } - // Test conversion back to Go value - switch expected := tt.expected.(type) { - case int: - if got := obj.AsLong().Int64(); got != int64(expected) { - t.Errorf("Expected %v, got %v", expected, got) - } - case float64: - if got := obj.AsFloat().Float64(); got != expected { - t.Errorf("Expected %v, got %v", expected, got) - } - case string: - if got := obj.AsStr().String(); got != expected { - t.Errorf("Expected %v, got %v", expected, got) - } - case bool: - if got := obj.AsBool().Bool(); got != expected { - t.Errorf("Expected %v, got %v", expected, got) - } - case []byte: - if got := obj.AsBytes().Bytes(); !reflect.DeepEqual(got, expected) { - t.Errorf("Expected %v, got %v", expected, got) - } + // Test conversion back to Go value + switch expected := tt.expected.(type) { + case int: + if got := obj.AsLong().Int64(); got != int64(expected) { + t.Errorf("Expected %v, got %v", expected, got) + } + case float64: + if got := obj.AsFloat().Float64(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) + } + case string: + if got := obj.AsStr().String(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) } - }) + case bool: + if got := obj.AsBool().Bool(); got != expected { + t.Errorf("Expected %v, got %v", expected, got) + } + case []byte: + if got := obj.AsBytes().Bytes(); !reflect.DeepEqual(got, expected) { + t.Errorf("Expected %v, got %v", expected, got) + } + } } } func TestObjectAttributes(t *testing.T) { + setupTest(t) // Test attributes using Python's built-in object type builtins := ImportModule("builtins") obj := builtins.AttrFunc("object").Call() - t.Run("GetAttr", func(t *testing.T) { - // Get built-in attribute - cls := obj.Attr("__class__") - if cls.Nil() { - t.Error("Failed to get __class__ attribute") - } + // Get built-in attribute + cls := obj.Attr("__class__") + if cls.Nil() { + t.Error("Failed to get __class__ attribute") + } - // Test Dir() method - dir := obj.Dir() - if dir.Len() == 0 { - t.Error("Dir() returned empty list") - } - }) + // Test Dir() method + dir := obj.Dir() + if dir.Len() == 0 { + t.Error("Dir() returned empty list") + } - t.Run("SetAttr", func(t *testing.T) { - // Create a custom class to test attribute setting - pyCode := ` + // Create a custom class to test attribute setting + pyCode := ` class TestClass: pass ` - locals := MakeDict(nil) - globals := MakeDict(nil) - globals.Set(MakeStr("__builtins__"), builtins.Object) + locals := MakeDict(nil) + globals := MakeDict(nil) + globals.Set(MakeStr("__builtins__"), builtins.Object) - code := CompileString(pyCode, "", FileInput) + code := CompileString(pyCode, "", FileInput) - EvalCode(code, globals, locals).AsModule() - testClass := locals.Get(MakeStr("TestClass")).AsFunc() - instance := testClass.Call() + EvalCode(code, globals, locals).AsModule() + testClass := locals.Get(MakeStr("TestClass")).AsFunc() + instance := testClass.Call() - // Now we can set attributes - instance.SetAttr("new_attr", "test_value") - value := instance.Attr("new_attr") - if value.AsStr().String() != "test_value" { - t.Error("SetAttr failed to set new attribute") - } - }) + // Now we can set attributes + instance.SetAttr("new_attr", "test_value") + value := instance.Attr("new_attr") + if value.AsStr().String() != "test_value" { + t.Error("SetAttr failed to set new attribute") + } } func TestDictOperations(t *testing.T) { + setupTest(t) // Test dictionary operations pyDict := MakeDict(nil) pyDict.Set(MakeStr("key1"), From(42)) pyDict.Set(MakeStr("key2"), From("value")) - t.Run("GetItem", func(t *testing.T) { - value := pyDict.Get(MakeStr("key1")) - if value.AsLong().Int64() != 42 { - t.Error("Failed to get dictionary item") - } - }) + value := pyDict.Get(MakeStr("key1")) + if value.AsLong().Int64() != 42 { + t.Error("Failed to get dictionary item") + } - t.Run("SetItem", func(t *testing.T) { + func() { pyDict.Set(MakeStr("key3"), From("new_value")) value := pyDict.Get(MakeStr("key3")) if value.AsStr().String() != "new_value" { t.Error("Failed to set dictionary item") } - }) + }() } func TestObjectConversion(t *testing.T) { + setupTest(t) type Person struct { Name string Age int } - t.Run("StructConversion", func(t *testing.T) { - person := Person{Name: "Alice", Age: 30} - obj := From(person) + person := Person{Name: "Alice", Age: 30} + obj := From(person) - if !obj.IsDict() { - t.Error("Struct should be converted to Python dict") - } + if !obj.IsDict() { + t.Error("Struct should be converted to Python dict") + } - dict := obj.AsDict() - if dict.Get(From("name")).AsStr().String() != "Alice" { - t.Error("Failed to convert struct field 'Name'") - } - if dict.Get(From("age")).AsLong().Int64() != 30 { - t.Error("Failed to convert struct field 'Age'") - } - }) + dict := obj.AsDict() + if dict.Get(From("name")).AsStr().String() != "Alice" { + t.Error("Failed to convert struct field 'Name'") + } + if dict.Get(From("age")).AsLong().Int64() != 30 { + t.Error("Failed to convert struct field 'Age'") + } - t.Run("SliceConversion", func(t *testing.T) { + func() { slice := []int{1, 2, 3} obj := From(slice) @@ -160,10 +154,11 @@ func TestObjectConversion(t *testing.T) { if list.GetItem(0).AsLong().Int64() != 1 { t.Error("Wrong value at index 0") } - }) + }() } func TestObjectString(t *testing.T) { + setupTest(t) tests := []struct { name string input interface{} @@ -175,35 +170,37 @@ func TestObjectString(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := From(tt.input) - str := obj.String() - if str != tt.expected { - t.Errorf("String() = %v, want %v", str, tt.expected) - } - }) + obj := From(tt.input) + str := obj.String() + if str != tt.expected { + t.Errorf("String() = %v, want %v", str, tt.expected) + } + } } func TestPyObjectMethods(t *testing.T) { + setupTest(t) // Test pyObject.Obj() - t.Run("pyObject.Obj", func(t *testing.T) { - obj := From(42) - if obj.pyObject.Obj() == nil { - t.Error("pyObject.Obj() returned nil for valid object") - } + obj := From(42) + if obj.pyObject.Obj() == nil { + t.Error("pyObject.Obj() returned nil for valid object") + } + func() { var nilObj *pyObject if nilObj.Obj() != nil { t.Error("pyObject.Obj() should return nil for nil object") } - }) + }() - // Test pyObject.Ensure() - t.Run("pyObject.Ensure", func(t *testing.T) { + func() { + // Test pyObject.Ensure() obj := From(42) obj.Ensure() // Should not panic + }() + func() { var nilObj Object defer func() { if r := recover(); r == nil { @@ -211,22 +208,20 @@ func TestPyObjectMethods(t *testing.T) { } }() nilObj.Ensure() - }) + }() } func TestObjectMethods(t *testing.T) { + setupTest(t) // Test Object.object() - t.Run("Object.object", func(t *testing.T) { - obj := From(42) - if obj.object() != obj { - t.Error("object() should return the same object") - } - }) + obj := From(42) + if obj.object() != obj { + t.Error("object() should return the same object") + } // Test Object.Attr* methods - t.Run("Object.Attr methods", func(t *testing.T) { - // Create a test class with various attribute types - pyCode := ` + // Create a test class with various attribute types + pyCode := ` class TestClass: def __init__(self): self.int_val = 42 @@ -237,43 +232,42 @@ class TestClass: self.dict_val = {"key": "value"} self.tuple_val = (1, 2, 3) ` - locals := MakeDict(nil) - globals := MakeDict(nil) - builtins := ImportModule("builtins") - globals.Set(MakeStr("__builtins__"), builtins.Object) + locals := MakeDict(nil) + globals := MakeDict(nil) + builtins := ImportModule("builtins") + globals.Set(MakeStr("__builtins__"), builtins.Object) - code := CompileString(pyCode, "", FileInput) - EvalCode(code, globals, locals) + code := CompileString(pyCode, "", FileInput) + EvalCode(code, globals, locals) - testClass := locals.Get(MakeStr("TestClass")).AsFunc() - instance := testClass.Call() + testClass := locals.Get(MakeStr("TestClass")).AsFunc() + instance := testClass.Call() - // Test each Attr* method - if instance.AttrLong("int_val").Int64() != 42 { - t.Error("AttrLong failed") - } - if instance.AttrFloat("float_val").Float64() != 3.14 { - t.Error("AttrFloat failed") - } - if instance.AttrString("str_val").String() != "test" { - t.Error("AttrString failed") - } - if !instance.AttrBool("bool_val").Bool() { - t.Error("AttrBool failed") - } - if instance.AttrList("list_val").Len() != 3 { - t.Error("AttrList failed") - } - if instance.AttrDict("dict_val").Get(MakeStr("key")).AsStr().String() != "value" { - t.Error("AttrDict failed") - } - if instance.AttrTuple("tuple_val").Len() != 3 { - t.Error("AttrTuple failed") - } - }) + // Test each Attr* method + if instance.AttrLong("int_val").Int64() != 42 { + t.Error("AttrLong failed") + } + if instance.AttrFloat("float_val").Float64() != 3.14 { + t.Error("AttrFloat failed") + } + if instance.AttrString("str_val").String() != "test" { + t.Error("AttrString failed") + } + if !instance.AttrBool("bool_val").Bool() { + t.Error("AttrBool failed") + } + if instance.AttrList("list_val").Len() != 3 { + t.Error("AttrList failed") + } + if instance.AttrDict("dict_val").Get(MakeStr("key")).AsStr().String() != "value" { + t.Error("AttrDict failed") + } + if instance.AttrTuple("tuple_val").Len() != 3 { + t.Error("AttrTuple failed") + } - // Test Object.IsTuple and AsTuple - t.Run("Tuple operations", func(t *testing.T) { + func() { + // Test Object.IsTuple and AsTuple // Create a Python tuple using Python code to ensure proper tuple creation pyCode := ` def make_tuple(): @@ -311,23 +305,25 @@ def make_tuple(): if pythonTuple.Get(2).AsLong().Int64() != 3 { t.Error("Incorrect value at index 2") } - }) + }() - // Test Object.Repr and Type - t.Run("Repr and Type", func(t *testing.T) { + func() { + // Test Object.Repr and Type obj := From(42) if obj.Repr() != "42" { t.Error("Repr failed") } + }() + func() { typeObj := obj.Type() if typeObj.Repr() != "" { t.Error("Type failed") } - }) + }() - // Test From with various numeric types - t.Run("From numeric types", func(t *testing.T) { + func() { + // Test From with various numeric types tests := []struct { input interface{} expected int64 @@ -348,68 +344,68 @@ def make_tuple(): t.Errorf("From(%T) = %v, want %v", tt.input, obj.AsLong().Int64(), tt.expected) } } - }) + }() - // Test From with false boolean - t.Run("From false", func(t *testing.T) { + func() { + // Test From with false boolean obj := From(false) if obj.AsBool().Bool() != false { t.Error("From(false) failed") } - }) + }() - // Test Object.Obj() - t.Run("Object.Obj", func(t *testing.T) { + func() { + // Test Object.Obj() obj := From(42) if obj.Obj() == nil { t.Error("Object.Obj() returned nil for valid object") } + }() + func() { var nilObj Object if nilObj.Obj() != nil { t.Error("Object.Obj() should return nil for nil object") } - }) + }() } func TestToValue(t *testing.T) { - t.Run("Numeric types", func(t *testing.T) { - tests := []struct { - name string - pyValue Object - goType interface{} - expected interface{} - }{ - {"int8", From(42), int8(0), int8(42)}, - {"int16", From(42), int16(0), int16(42)}, - {"int32", From(42), int32(0), int32(42)}, - {"int64", From(42), int64(0), int64(42)}, - {"int", From(42), int(0), int(42)}, - {"uint8", From(42), uint8(0), uint8(42)}, - {"uint16", From(42), uint16(0), uint16(42)}, - {"uint32", From(42), uint32(0), uint32(42)}, - {"uint64", From(42), uint64(0), uint64(42)}, - {"uint", From(42), uint(0), uint(42)}, - {"float32", From(3.14), float32(0), float32(3.14)}, - {"float64", From(3.14), float64(0), float64(3.14)}, - {"complex64", From(complex(1, 2)), complex64(0), complex64(complex(1, 2))}, - {"complex128", From(complex(1, 2)), complex128(0), complex128(complex(1, 2))}, + setupTest(t) + tests := []struct { + name string + pyValue Object + goType interface{} + expected interface{} + }{ + {"int8", From(42), int8(0), int8(42)}, + {"int16", From(42), int16(0), int16(42)}, + {"int32", From(42), int32(0), int32(42)}, + {"int64", From(42), int64(0), int64(42)}, + {"int", From(42), int(0), int(42)}, + {"uint8", From(42), uint8(0), uint8(42)}, + {"uint16", From(42), uint16(0), uint16(42)}, + {"uint32", From(42), uint32(0), uint32(42)}, + {"uint64", From(42), uint64(0), uint64(42)}, + {"uint", From(42), uint(0), uint(42)}, + {"float32", From(3.14), float32(0), float32(3.14)}, + {"float64", From(3.14), float64(0), float64(3.14)}, + {"complex64", From(complex(1, 2)), complex64(0), complex64(complex(1, 2))}, + {"complex128", From(complex(1, 2)), complex128(0), complex128(complex(1, 2))}, + } + + for _, tt := range tests { + v := reflect.New(reflect.TypeOf(tt.goType)).Elem() + if !ToValue(tt.pyValue, v) { + t.Errorf("ToValue failed for %v", tt.name) + } + if v.Interface() != tt.expected { + t.Errorf("Expected %v, got %v", tt.expected, v.Interface()) } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := reflect.New(reflect.TypeOf(tt.goType)).Elem() - if !ToValue(tt.pyValue, v) { - t.Errorf("ToValue failed for %v", tt.name) - } - if v.Interface() != tt.expected { - t.Errorf("Expected %v, got %v", tt.expected, v.Interface()) - } - }) - } - }) - - t.Run("String type", func(t *testing.T) { + } + + func() { v := reflect.New(reflect.TypeOf("")).Elem() if !ToValue(From("hello"), v) { t.Error("ToValue failed for string") @@ -417,9 +413,9 @@ func TestToValue(t *testing.T) { if v.String() != "hello" { t.Errorf("Expected 'hello', got %v", v.String()) } - }) + }() - t.Run("Boolean type", func(t *testing.T) { + func() { v := reflect.New(reflect.TypeOf(true)).Elem() if !ToValue(From(true), v) { t.Error("ToValue failed for bool") @@ -427,9 +423,9 @@ func TestToValue(t *testing.T) { if !v.Bool() { t.Error("Expected true, got false") } - }) + }() - t.Run("Bytes type", func(t *testing.T) { + func() { expected := []byte("hello") v := reflect.New(reflect.TypeOf([]byte{})).Elem() if !ToValue(From(expected), v) { @@ -438,9 +434,9 @@ func TestToValue(t *testing.T) { if !reflect.DeepEqual(v.Bytes(), expected) { t.Errorf("Expected %v, got %v", expected, v.Bytes()) } - }) + }() - t.Run("Slice type", func(t *testing.T) { + func() { expected := []int{1, 2, 3} v := reflect.New(reflect.TypeOf([]int{})).Elem() if !ToValue(From(expected), v) { @@ -449,9 +445,9 @@ func TestToValue(t *testing.T) { if !reflect.DeepEqual(v.Interface(), expected) { t.Errorf("Expected %v, got %v", expected, v.Interface()) } - }) + }() - t.Run("Map type", func(t *testing.T) { + func() { expected := map[string]int{"one": 1, "two": 2} v := reflect.New(reflect.TypeOf(map[string]int{})).Elem() if !ToValue(From(expected), v) { @@ -460,9 +456,9 @@ func TestToValue(t *testing.T) { if !reflect.DeepEqual(v.Interface(), expected) { t.Errorf("Expected %v, got %v", expected, v.Interface()) } - }) + }() - t.Run("Struct type", func(t *testing.T) { + func() { type TestStruct struct { Name string Age int @@ -475,9 +471,9 @@ func TestToValue(t *testing.T) { if !reflect.DeepEqual(v.Interface(), expected) { t.Errorf("Expected %v, got %v", expected, v.Interface()) } - }) + }() - t.Run("Invalid conversions", func(t *testing.T) { + func() { tests := []struct { name string pyValue Object @@ -490,24 +486,25 @@ func TestToValue(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - v := reflect.New(reflect.TypeOf(tt.goType)).Elem() - if ToValue(tt.pyValue, v) { - t.Errorf("ToValue should have failed for %v", tt.name) - } - }) + v := reflect.New(reflect.TypeOf(tt.goType)).Elem() + if ToValue(tt.pyValue, v) { + t.Errorf("ToValue should have failed for %v", tt.name) + } + } - }) + }() - t.Run("Nil and invalid values", func(t *testing.T) { + func() { var nilValue reflect.Value if ToValue(From(42), nilValue) { t.Error("ToValue should fail for nil reflect.Value") } + }() + func() { v := reflect.ValueOf(42) // not settable if ToValue(From(43), v) { t.Error("ToValue should fail for non-settable value") } - }) + }() } diff --git a/python_test.go b/python_test.go index f625737..2e2c2ea 100644 --- a/python_test.go +++ b/python_test.go @@ -1,18 +1,21 @@ package gp import ( - "os" + "runtime" "testing" ) -func TestMain(m *testing.M) { +func setupTest(t *testing.T) { + runtime.LockOSThread() Initialize() - code := m.Run() - Finalize() - os.Exit(code) + t.Cleanup(func() { + runtime.GC() + Finalize() + }) } func TestRunString(t *testing.T) { + setupTest(t) tests := []struct { name string code string @@ -36,16 +39,15 @@ func TestRunString(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := RunString(tt.code) - if (err != nil) != tt.wantErr { - t.Errorf("RunString() error = %v, wantErr %v", err, tt.wantErr) - } - }) + err := RunString(tt.code) + if (err != nil) != tt.wantErr { + t.Errorf("RunString() error = %v, wantErr %v", err, tt.wantErr) + } } } func TestCompileString(t *testing.T) { + setupTest(t) tests := []struct { name string code string @@ -70,16 +72,15 @@ func TestCompileString(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := CompileString(tt.code, tt.filename, tt.start) - if obj.Nil() != tt.wantNil { - t.Errorf("CompileString() returned nil = %v, want %v", obj.Nil(), tt.wantNil) - } - }) + obj := CompileString(tt.code, tt.filename, tt.start) + if obj.Nil() != tt.wantNil { + t.Errorf("CompileString() returned nil = %v, want %v", obj.Nil(), tt.wantNil) + } } } func TestNone(t *testing.T) { + setupTest(t) none := None() if none.Nil() { t.Error("None() returned nil object") @@ -87,6 +88,7 @@ func TestNone(t *testing.T) { } func TestNil(t *testing.T) { + setupTest(t) nil_ := Nil() if !nil_.Nil() { t.Error("Nil() did not return nil object") @@ -94,6 +96,7 @@ func TestNil(t *testing.T) { } func TestMainModule(t *testing.T) { + setupTest(t) main := MainModule() if main.Nil() { t.Error("MainModule() returned nil") @@ -101,6 +104,7 @@ func TestMainModule(t *testing.T) { } func TestWith(t *testing.T) { + setupTest(t) // First create a simple Python context manager class code := ` class TestContextManager: diff --git a/tuple_test.go b/tuple_test.go index 14e8d8d..29aa47f 100644 --- a/tuple_test.go +++ b/tuple_test.go @@ -5,6 +5,7 @@ import ( ) func TestTupleCreation(t *testing.T) { + setupTest(t) // Test empty tuple empty := MakeTupleWithLen(0) if empty.Len() != 0 { @@ -19,6 +20,7 @@ func TestTupleCreation(t *testing.T) { } func TestTupleGetSet(t *testing.T) { + setupTest(t) tuple := MakeTupleWithLen(2) // Test setting and getting values @@ -34,6 +36,7 @@ func TestTupleGetSet(t *testing.T) { } func TestTupleSlice(t *testing.T) { + setupTest(t) tuple := MakeTuple(1, 2, 3, 4, 5) // Test slicing @@ -51,6 +54,7 @@ func TestTupleSlice(t *testing.T) { } func TestTupleParseArgs(t *testing.T) { + setupTest(t) tuple := MakeTuple(42, "hello", 3.14, true) var ( @@ -95,6 +99,7 @@ func TestTupleParseArgs(t *testing.T) { } func TestTupleParseArgsTypes(t *testing.T) { + setupTest(t) // Test all supported numeric types tuple := MakeTuple(42, 42, 42, 42, 42, 42, 42, 42, 42, 42) diff --git a/unicode_test.go b/unicode_test.go index c47a4c8..32e37d4 100644 --- a/unicode_test.go +++ b/unicode_test.go @@ -5,6 +5,7 @@ import ( ) func TestMakeStr(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -50,28 +51,27 @@ func TestMakeStr(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pyStr := MakeStr(tt.input) + pyStr := MakeStr(tt.input) - // Test String() method - if got := pyStr.String(); got != tt.expected { - t.Errorf("MakeStr(%q).String() = %q, want %q", tt.input, got, tt.expected) - } + // Test String() method + if got := pyStr.String(); got != tt.expected { + t.Errorf("MakeStr(%q).String() = %q, want %q", tt.input, got, tt.expected) + } - // Test Len() method - if got := pyStr.Len(); got != tt.length { - t.Errorf("MakeStr(%q).Len() = %d, want %d", tt.input, got, tt.length) - } + // Test Len() method + if got := pyStr.Len(); got != tt.length { + t.Errorf("MakeStr(%q).Len() = %d, want %d", tt.input, got, tt.length) + } - // Test ByteLen() method - if got := pyStr.ByteLen(); got != tt.byteCount { - t.Errorf("MakeStr(%q).ByteLen() = %d, want %d", tt.input, got, tt.byteCount) - } - }) + // Test ByteLen() method + if got := pyStr.ByteLen(); got != tt.byteCount { + t.Errorf("MakeStr(%q).ByteLen() = %d, want %d", tt.input, got, tt.byteCount) + } } } func TestStrEncode(t *testing.T) { + setupTest(t) tests := []struct { name string input string @@ -90,14 +90,12 @@ func TestStrEncode(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - pyStr := MakeStr(tt.input) - encoded := pyStr.Encode(tt.encoding) - decoded := encoded.Decode(tt.encoding) + pyStr := MakeStr(tt.input) + encoded := pyStr.Encode(tt.encoding) + decoded := encoded.Decode(tt.encoding) - if got := decoded.String(); got != tt.input { - t.Errorf("String encode/decode roundtrip failed: got %q, want %q", got, tt.input) - } - }) + if got := decoded.String(); got != tt.input { + t.Errorf("String encode/decode roundtrip failed: got %q, want %q", got, tt.input) + } } } From a005c44a8b008b47bd1b91aedacab7627056b6fe Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sun, 3 Nov 2024 23:08:09 +0800 Subject: [PATCH 6/6] avoid double free --- object.go | 2 +- python.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/object.go b/object.go index 7055b23..9eef15a 100644 --- a/object.go +++ b/object.go @@ -52,12 +52,12 @@ func (obj Object) object() Object { func newObject(obj *PyObject) Object { if obj == nil { - C.PyErr_Print() return Object{} } o := &pyObject{obj: obj} p := Object{o} runtime.SetFinalizer(o, func(o *pyObject) { + // TODO: need better auto-release mechanism // C.Py_DecRef(o.obj) }) return p diff --git a/python.go b/python.go index 58fceb7..da03a42 100644 --- a/python.go +++ b/python.go @@ -38,6 +38,7 @@ func CompileString(code, filename string, start InputType) Object { ccode := AllocCStr(code) cfilename := AllocCStr(filename) o := C.Py_CompileString(ccode, cfilename, C.int(start)) + // TODO: check why double free C.free(unsafe.Pointer(ccode)) C.free(unsafe.Pointer(cfilename)) return newObject(o) @@ -82,6 +83,9 @@ func Nil() Object { func RunString(code string) error { // Get __main__ module dict for executing code main := MainModule() + if main.Nil() { + return fmt.Errorf("failed to get __main__ module") + } dict := main.Dict() // Run the code string