Skip to content

Commit 81890ac

Browse files
committed
Add OWN_GIL tests for Channel, ByteChannel, and Buffer operations
- Channel class: receive, iteration, context manager - ByteChannel: send_bytes, try_receive_bytes, iteration - Buffer: read methods, at_eof detection
1 parent c758da1 commit 81890ac

2 files changed

Lines changed: 277 additions & 2 deletions

File tree

test/py_owngil_features_SUITE.erl

Lines changed: 207 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,18 @@
108108
owngil_atom_create_different_test/1,
109109
owngil_atom_cache_test/1,
110110
owngil_ref_roundtrip_test/1,
111-
owngil_pid_operations_test/1
111+
owngil_pid_operations_test/1,
112+
%% Channel class tests
113+
owngil_channel_class_test/1,
114+
owngil_channel_iteration_test/1,
115+
owngil_channel_context_manager_test/1,
116+
%% ByteChannel tests
117+
owngil_bytechannel_send_receive_test/1,
118+
owngil_bytechannel_try_receive_test/1,
119+
owngil_bytechannel_iteration_test/1,
120+
%% Buffer tests
121+
owngil_buffer_read_methods_test/1,
122+
owngil_buffer_at_eof_test/1
112123
]).
113124

114125
all() ->
@@ -194,7 +205,18 @@ groups() ->
194205
owngil_atom_create_different_test,
195206
owngil_atom_cache_test,
196207
owngil_ref_roundtrip_test,
197-
owngil_pid_operations_test
208+
owngil_pid_operations_test,
209+
%% Channel tests
210+
owngil_channel_class_test,
211+
owngil_channel_iteration_test,
212+
owngil_channel_context_manager_test,
213+
%% ByteChannel tests
214+
owngil_bytechannel_send_receive_test,
215+
owngil_bytechannel_try_receive_test,
216+
owngil_bytechannel_iteration_test,
217+
%% Buffer tests
218+
owngil_buffer_read_methods_test,
219+
owngil_buffer_at_eof_test
198220
]}].
199221

200222
init_per_suite(Config) ->
@@ -1753,3 +1775,186 @@ owngil_pid_operations_test(Config) ->
17531775
{ok, true} = py_context:call(Ctx, py_test_pid_send, pid_hash_equal, [Pid, Pid], #{}),
17541776

17551777
py_context:stop(Ctx).
1778+
1779+
%%% ============================================================================
1780+
%%% Channel Class Tests (erlang_api group)
1781+
%%% ============================================================================
1782+
1783+
%% @doc Test Channel class receive/try_receive in OWN_GIL
1784+
owngil_channel_class_test(Config) ->
1785+
{ok, Ctx} = py_context:start_link(1, owngil),
1786+
TestDir = proplists:get_value(test_dir, Config),
1787+
1788+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1789+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1790+
1791+
{ok, Ch} = py_channel:new(),
1792+
1793+
%% Send message from Erlang
1794+
ok = py_channel:send(Ch, <<"test_channel_class">>),
1795+
1796+
%% Receive via Channel class in Python
1797+
{ok, <<"test_channel_class">>} = py_context:call(Ctx, py_test_pid_send,
1798+
channel_receive_test, [Ch], #{}),
1799+
1800+
py_channel:close(Ch),
1801+
py_context:stop(Ctx).
1802+
1803+
%% @doc Test Channel iteration in OWN_GIL
1804+
owngil_channel_iteration_test(Config) ->
1805+
{ok, Ctx} = py_context:start_link(1, owngil),
1806+
TestDir = proplists:get_value(test_dir, Config),
1807+
1808+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1809+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1810+
1811+
{ok, Ch} = py_channel:new(),
1812+
1813+
%% Send multiple messages
1814+
ok = py_channel:send(Ch, <<"msg1">>),
1815+
ok = py_channel:send(Ch, <<"msg2">>),
1816+
ok = py_channel:send(Ch, <<"msg3">>),
1817+
1818+
%% Iterate in Python
1819+
{ok, 3} = py_context:call(Ctx, py_test_pid_send,
1820+
channel_iteration_test, [Ch, 3], #{}),
1821+
1822+
py_channel:close(Ch),
1823+
py_context:stop(Ctx).
1824+
1825+
%% @doc Test Channel as context manager in OWN_GIL
1826+
owngil_channel_context_manager_test(Config) ->
1827+
{ok, Ctx} = py_context:start_link(1, owngil),
1828+
TestDir = proplists:get_value(test_dir, Config),
1829+
1830+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1831+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1832+
1833+
{ok, Ch} = py_channel:new(),
1834+
1835+
%% Send a message for the context manager test to try_receive
1836+
ok = py_channel:send(Ch, <<"context_msg">>),
1837+
1838+
%% Test context manager usage
1839+
{ok, true} = py_context:call(Ctx, py_test_pid_send,
1840+
channel_context_manager_test, [Ch], #{}),
1841+
1842+
py_channel:close(Ch),
1843+
py_context:stop(Ctx).
1844+
1845+
%%% ============================================================================
1846+
%%% ByteChannel Tests (erlang_api group)
1847+
%%% ============================================================================
1848+
1849+
%% @doc Test ByteChannel send_bytes/receive_bytes in OWN_GIL
1850+
owngil_bytechannel_send_receive_test(Config) ->
1851+
{ok, Ctx} = py_context:start_link(1, owngil),
1852+
TestDir = proplists:get_value(test_dir, Config),
1853+
1854+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1855+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1856+
1857+
{ok, Ch} = py_byte_channel:new(),
1858+
1859+
%% Send bytes from Python via ByteChannel
1860+
{ok, true} = py_context:call(Ctx, py_test_pid_send,
1861+
bytechannel_send_receive_test, [Ch], #{}),
1862+
1863+
%% Receive raw bytes from Erlang using byte channel API
1864+
{ok, <<"hello_owngil">>} = py_byte_channel:try_receive(Ch),
1865+
1866+
py_byte_channel:close(Ch),
1867+
py_context:stop(Ctx).
1868+
1869+
%% @doc Test ByteChannel non-blocking try_receive_bytes in OWN_GIL
1870+
owngil_bytechannel_try_receive_test(Config) ->
1871+
{ok, Ctx} = py_context:start_link(1, owngil),
1872+
TestDir = proplists:get_value(test_dir, Config),
1873+
1874+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1875+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1876+
1877+
{ok, Ch} = py_byte_channel:new(),
1878+
1879+
%% Send raw bytes from Erlang using byte channel API
1880+
ok = py_byte_channel:send(Ch, <<"bytes_data">>),
1881+
1882+
%% Receive via ByteChannel in Python
1883+
{ok, <<"bytes_data">>} = py_context:call(Ctx, py_test_pid_send,
1884+
bytechannel_try_receive_test, [Ch], #{}),
1885+
1886+
py_byte_channel:close(Ch),
1887+
py_context:stop(Ctx).
1888+
1889+
%% @doc Test ByteChannel iteration in OWN_GIL
1890+
owngil_bytechannel_iteration_test(Config) ->
1891+
{ok, Ctx} = py_context:start_link(1, owngil),
1892+
TestDir = proplists:get_value(test_dir, Config),
1893+
1894+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1895+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1896+
1897+
{ok, Ch} = py_byte_channel:new(),
1898+
1899+
%% Send multiple byte chunks using byte channel API
1900+
ok = py_byte_channel:send(Ch, <<"chunk1">>),
1901+
ok = py_byte_channel:send(Ch, <<"chunk2">>),
1902+
1903+
%% Iterate in Python
1904+
{ok, 2} = py_context:call(Ctx, py_test_pid_send,
1905+
bytechannel_iteration_test, [Ch, 2], #{}),
1906+
1907+
py_byte_channel:close(Ch),
1908+
py_context:stop(Ctx).
1909+
1910+
%%% ============================================================================
1911+
%%% Buffer Tests (erlang_api group)
1912+
%%% ============================================================================
1913+
1914+
%% @doc Test buffer read/read_nonblock/readable_amount in OWN_GIL
1915+
owngil_buffer_read_methods_test(Config) ->
1916+
{ok, Ctx} = py_context:start_link(1, owngil),
1917+
TestDir = proplists:get_value(test_dir, Config),
1918+
1919+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1920+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1921+
1922+
{ok, Buf} = py_buffer:new(),
1923+
1924+
%% Write data to buffer
1925+
ok = py_buffer:write(Buf, <<"test_data">>),
1926+
1927+
%% Test read methods from Python
1928+
{ok, Result} = py_context:call(Ctx, py_test_pid_send,
1929+
buffer_read_methods_test, [Buf], #{}),
1930+
1931+
%% Verify readable amount and data
1932+
#{<<"readable">> := Readable, <<"data">> := Data} = Result,
1933+
true = Readable > 0,
1934+
<<"test_data">> = Data,
1935+
1936+
py_buffer:close(Buf),
1937+
py_context:stop(Ctx).
1938+
1939+
%% @doc Test buffer at_eof detection in OWN_GIL
1940+
owngil_buffer_at_eof_test(Config) ->
1941+
{ok, Ctx} = py_context:start_link(1, owngil),
1942+
TestDir = proplists:get_value(test_dir, Config),
1943+
1944+
ok = py_context:exec(Ctx, iolist_to_binary(io_lib:format(
1945+
"import sys; sys.path.insert(0, '~s')", [TestDir]))),
1946+
1947+
{ok, Buf} = py_buffer:new(),
1948+
1949+
%% Buffer not yet at EOF (not closed)
1950+
{ok, false} = py_context:call(Ctx, py_test_pid_send,
1951+
buffer_at_eof_test, [Buf], #{}),
1952+
1953+
%% Close buffer
1954+
ok = py_buffer:close(Buf),
1955+
1956+
%% Now at EOF
1957+
{ok, true} = py_context:call(Ctx, py_test_pid_send,
1958+
buffer_at_eof_test, [Buf], #{}),
1959+
1960+
py_context:stop(Ctx).

test/py_test_pid_send.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,3 +282,73 @@ def atom_type_name():
282282
import erlang
283283
a = erlang.atom('type_check_atom')
284284
return type(a).__name__
285+
286+
287+
# OWN_GIL test helpers for Channel, ByteChannel, and Buffer operations
288+
289+
def channel_receive_test(channel_ref):
290+
"""Test Channel.try_receive() in OWN_GIL."""
291+
from erlang import Channel
292+
ch = Channel(channel_ref)
293+
msg = ch.try_receive()
294+
return msg
295+
296+
297+
def channel_iteration_test(channel_ref, expected_count):
298+
"""Test Channel iteration."""
299+
from erlang import Channel
300+
ch = Channel(channel_ref)
301+
messages = []
302+
for msg in ch:
303+
messages.append(msg)
304+
if len(messages) >= expected_count:
305+
break
306+
return len(messages)
307+
308+
309+
def channel_context_manager_test(channel_ref):
310+
"""Test Channel as context manager."""
311+
from erlang import Channel
312+
with Channel(channel_ref) as ch:
313+
msg = ch.try_receive()
314+
return msg is not None or True # Just verifies context manager works
315+
316+
317+
def bytechannel_send_receive_test(channel_ref):
318+
"""Test ByteChannel send_bytes/try_receive_bytes."""
319+
from erlang import ByteChannel
320+
ch = ByteChannel(channel_ref)
321+
ch.send_bytes(b"hello_owngil")
322+
return True
323+
324+
325+
def bytechannel_try_receive_test(channel_ref):
326+
"""Test ByteChannel try_receive_bytes."""
327+
from erlang import ByteChannel
328+
ch = ByteChannel(channel_ref)
329+
data = ch.try_receive_bytes()
330+
return data
331+
332+
333+
def bytechannel_iteration_test(channel_ref, expected_count):
334+
"""Test ByteChannel iteration."""
335+
from erlang import ByteChannel
336+
ch = ByteChannel(channel_ref)
337+
chunks = []
338+
for chunk in ch:
339+
chunks.append(chunk)
340+
if len(chunks) >= expected_count:
341+
break
342+
return len(chunks)
343+
344+
345+
def buffer_read_methods_test(buf):
346+
"""Test buffer read methods."""
347+
readable = buf.readable_amount()
348+
data = buf.read_nonblock(readable) if readable > 0 else b''
349+
return {'readable': readable, 'data': data}
350+
351+
352+
def buffer_at_eof_test(buf):
353+
"""Test buffer at_eof detection."""
354+
return buf.at_eof()

0 commit comments

Comments
 (0)