diff --git a/tests/ipc/test_each_tpt.py b/tests/ipc/test_each_tpt.py new file mode 100644 index 00000000..7dc60444 --- /dev/null +++ b/tests/ipc/test_each_tpt.py @@ -0,0 +1,113 @@ +''' +Unit-ish tests for specific IPC transport protocol backends. + +''' +from __future__ import annotations +from pathlib import Path + +import pytest +import trio +import tractor +from tractor import ( + Actor, + _state, + _addr, +) + + +@pytest.fixture +def bindspace_dir_str() -> str: + + bs_dir_str: str = '/run/user/1000/doggy' + bs_dir = Path(bs_dir_str) + assert not bs_dir.is_dir() + + yield bs_dir_str + + # delete it on suite teardown. + # ?TODO? should we support this internally + # or is leaking it ok? + if bs_dir.is_dir(): + bs_dir.rmdir() + + +def test_uds_bindspace_created_implicitly( + debug_mode: bool, + bindspace_dir_str: str, +): + registry_addr: tuple = ( + f'{bindspace_dir_str}', + 'registry@doggy.sock', + ) + bs_dir_str: str = registry_addr[0] + + # XXX, ensure bindspace-dir DNE beforehand! + assert not Path(bs_dir_str).is_dir() + + async def main(): + async with tractor.open_nursery( + enable_transports=['uds'], + registry_addrs=[registry_addr], + debug_mode=debug_mode, + ) as _an: + + # XXX MUST be created implicitly by + # `.ipc._uds.start_listener()`! + assert Path(bs_dir_str).is_dir() + + root: Actor = tractor.current_actor() + assert root.is_registrar + + assert registry_addr in root.reg_addrs + assert ( + registry_addr + in + _state._runtime_vars['_registry_addrs'] + ) + assert ( + _addr.wrap_address(registry_addr) + in + root.registry_addrs + ) + + trio.run(main) + + +def test_uds_double_listen_raises_connerr( + debug_mode: bool, + bindspace_dir_str: str, +): + registry_addr: tuple = ( + f'{bindspace_dir_str}', + 'registry@doggy.sock', + ) + + async def main(): + async with tractor.open_nursery( + enable_transports=['uds'], + registry_addrs=[registry_addr], + debug_mode=debug_mode, + ) as _an: + + # runtime up + root: Actor = tractor.current_actor() + + from tractor.ipc._uds import ( + start_listener, + UDSAddress, + ) + ya_bound_addr: UDSAddress = root.registry_addrs[0] + try: + await start_listener( + addr=ya_bound_addr, + ) + except ConnectionError as connerr: + assert type(src_exc := connerr.__context__) is OSError + assert 'Address already in use' in src_exc.args + # complete, exit test. + + else: + pytest.fail('It dint raise a connerr !?') + + + trio.run(main) diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index 645819f0..e23fd8d2 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -103,8 +103,6 @@ class UDSAddress( self.filedir or self.def_bindspace - # or - # get_rt_dir() ) @property @@ -230,7 +228,14 @@ async def start_listener( addr: UDSAddress, **kwargs, ) -> SocketListener: - # sock = addr._sock = socket.socket( + ''' + Start listening for inbound connections via + a `trio.SocketListener` (task) which `socket.bind()`s on `addr`. + + Note, if the `UDSAddress.bindspace: Path` directory dne it is + implicitly created. + + ''' sock = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM @@ -241,7 +246,17 @@ async def start_listener( f'|_{addr}\n' ) + # ?TODO? should we use the `actor.lifetime_stack` + # to rm on shutdown? bindpath: Path = addr.sockpath + if not (bs := addr.bindspace).is_dir(): + log.info( + 'Creating bindspace dir in file-sys\n' + f'>{{\n' + f'|_{bs!r}\n' + ) + bs.mkdir() + with _reraise_as_connerr( src_excs=( FileNotFoundError,