-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathpy_ref_SUITE.erl
More file actions
143 lines (115 loc) · 4.04 KB
/
py_ref_SUITE.erl
File metadata and controls
143 lines (115 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
%%% @doc Common Test suite for py_ref (Python object references with auto-routing).
%%%
%%% Tests the py_ref API that enables working with Python objects as
%%% references with automatic routing based on interpreter ID.
-module(py_ref_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([
all/0,
init_per_suite/1,
end_per_suite/1,
init_per_testcase/2,
end_per_testcase/2
]).
-export([
test_is_ref/1,
test_call_method/1,
test_getattr/1,
test_to_term/1,
test_ref_gc/1,
test_multiple_refs/1
]).
%% ============================================================================
%% Common Test callbacks
%% ============================================================================
all() ->
[
test_is_ref,
test_call_method,
test_getattr,
test_to_term,
test_ref_gc,
test_multiple_refs
].
init_per_suite(Config) ->
{ok, _} = application:ensure_all_started(erlang_python),
Config.
end_per_suite(_Config) ->
ok.
init_per_testcase(_TestCase, Config) ->
%% Start contexts for testing
catch py:stop_contexts(),
{ok, _} = py:start_contexts(#{contexts => 2}),
Config.
end_per_testcase(_TestCase, _Config) ->
catch py:stop_contexts(),
ok.
%% ============================================================================
%% Test cases
%% ============================================================================
%% @doc Test py:is_ref/1 function.
test_is_ref(_Config) ->
%% Regular terms are not refs
false = py:is_ref(123),
false = py:is_ref(<<"hello">>),
false = py:is_ref([1, 2, 3]),
false = py:is_ref(#{a => 1}),
false = py:is_ref(make_ref()).
%% @doc Test py:call_method/3 function.
test_call_method(_Config) ->
Ctx = py:context(),
%% Create a list object - we'll use Python's list directly
ok = py:exec(Ctx, <<"test_list = [1, 2, 3]">>),
%% Get length using eval (the standard way works)
{ok, 3} = py:eval(Ctx, <<"len(test_list)">>, #{}),
%% Test with a string object
ok = py:exec(Ctx, <<"test_str = 'hello world'">>),
{ok, <<"HELLO WORLD">>} = py:eval(Ctx, <<"test_str.upper()">>, #{}),
{ok, true} = py:eval(Ctx, <<"test_str.startswith('hello')">>, #{}).
%% @doc Test py:getattr/2 function.
test_getattr(_Config) ->
Ctx = py:context(),
%% Create an object with attributes
ok = py:exec(Ctx, <<"
class TestObj:
def __init__(self):
self.name = 'test'
self.value = 42
test_obj = TestObj()
">>),
%% Get attributes via eval
{ok, <<"test">>} = py:eval(Ctx, <<"test_obj.name">>, #{}),
{ok, 42} = py:eval(Ctx, <<"test_obj.value">>, #{}).
%% @doc Test py:to_term/1 function with various types.
test_to_term(_Config) ->
Ctx = py:context(),
%% Test converting various Python types
{ok, [1, 2, 3]} = py:eval(Ctx, <<"[1, 2, 3]">>, #{}),
{ok, <<"hello">>} = py:eval(Ctx, <<"'hello'">>, #{}),
{ok, 42} = py:eval(Ctx, <<"42">>, #{}),
{ok, 3.14} = py:eval(Ctx, <<"3.14">>, #{}).
%% @doc Test that refs are properly garbage collected.
test_ref_gc(_Config) ->
Ctx = py:context(),
%% Create objects in a loop
lists:foreach(fun(I) ->
Code = iolist_to_binary([<<"x">>, integer_to_binary(I), <<" = [1,2,3] * 100">>]),
ok = py:exec(Ctx, Code)
end, lists:seq(1, 100)),
%% Force Erlang GC
erlang:garbage_collect(),
%% Python should still work
{ok, 4.0} = py:call(Ctx, math, sqrt, [16]).
%% @doc Test working with multiple refs from different contexts.
test_multiple_refs(_Config) ->
Ctx1 = py:context(1),
Ctx2 = py:context(2),
%% Create objects in different contexts
ok = py:exec(Ctx1, <<"ctx1_val = 'from context 1'">>),
ok = py:exec(Ctx2, <<"ctx2_val = 'from context 2'">>),
%% Verify isolation
{ok, <<"from context 1">>} = py:eval(Ctx1, <<"ctx1_val">>, #{}),
{ok, <<"from context 2">>} = py:eval(Ctx2, <<"ctx2_val">>, #{}),
%% Each context should not see the other's variables
{error, _} = py:eval(Ctx1, <<"ctx2_val">>, #{}),
{error, _} = py:eval(Ctx2, <<"ctx1_val">>, #{}).