88import socket
99import tempfile
1010import threading
11- from typing import Any , Generator , List , Optional , Union
11+ from typing import Any , Generator , List , Optional , Union , Tuple
1212
1313import pytest
1414
1818
1919
2020@contextlib .contextmanager
21- def listen_on_port ( port : int , encoding = "latin-1" ) -> Generator [List [str ], None , None ]:
22- """Listen on TCP port `port` for caPutLog data."""
21+ def listen_on_sock ( sock : socket . socket , encoding = "latin-1" ) -> Generator [List [str ], None , None ]:
22+ """Listen on TCP socket for caPutLog data."""
2323 data = []
2424
2525 def listen ():
2626 sock .listen (1 )
2727 client , addr = sock .accept ()
2828 try :
29- logger .warning ("Accepted client on localhost:%d - %s" , port , addr )
29+ logger .warning ("Accepted client on localhost:%d - %s" ,
30+ sock .getsockname ()[1 ],
31+ addr )
3032 while True :
3133 read = client .recv (4096 )
3234 logger .info ("caPutLog TCP server received %s" , read )
@@ -36,17 +38,22 @@ def listen():
3638 finally :
3739 client .close ()
3840
39- sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
40- # Avoid "address already in use" between successive caputlog tests
41- sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
42- sock .bind (("127.0.0.1" , port ))
4341 threading .Thread (target = listen , daemon = True ).start ()
4442 try :
4543 yield data
4644 finally :
4745 sock .close ()
4846
4947
48+ def create_socket (addr : str ) -> Tuple [socket .socket , int ]:
49+ """Create a TCP socket on the specified address using a system-chosen port."""
50+ sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
51+ # Try to avoid "address already in use" between successive caputlog tests
52+ sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
53+ sock .bind ((addr , 0 ))
54+ return sock , sock .getsockname ()[1 ]
55+
56+
5057@dataclasses .dataclass
5158class Caput :
5259 """A single caputlog line, parsed into its parts."""
@@ -152,9 +159,10 @@ def test_caputlog(
152159 """
153160 Test that caPutLog works by putting to a PV and checking the output.
154161 """
162+ sock , putlog_port = create_socket ("127.0.0.1" )
155163 with (
156164 tempfile .NamedTemporaryFile () as caputlog_fp ,
157- listen_on_port ( config . default_putlog_port ) as tcp_data ,
165+ listen_on_sock ( sock ) as tcp_data ,
158166 ):
159167 with (
160168 conftest .custom_environment (
@@ -164,7 +172,7 @@ def test_caputlog(
164172 "-putlog" ,
165173 caputlog_fp .name ,
166174 "-caputlog" ,
167- f"127.0.0.1:{ config . default_putlog_port } " ,
175+ f"127.0.0.1:{ putlog_port } " ,
168176 ],
169177 ) as env ,
170178 conftest .gateway_channel_access_env (),
0 commit comments