@@ -306,6 +306,14 @@ def raw_tcp_request(ip, port, payload, buffer_size=4096):
306306
307307 return json .loads (data .decode ())
308308
309+ @staticmethod
310+ def send_partial_tcp_payload_and_close (ip , port , payload ):
311+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM , 0 ) as conn_socket :
312+ conn_socket .connect ((ip , port ))
313+ if isinstance (payload , str ):
314+ payload = payload .encode ()
315+ conn_socket .sendall (payload )
316+
309317 def start_tcp_manager (self , manager ):
310318 manager .start ()
311319 self .addCleanup (manager .kill ) # at the end, kill the TCP mngr
@@ -536,6 +544,44 @@ def test_rust_build_plain_invalid_request_details(self):
536544 utf8_status .message .startswith (
537545 "invalid request: request body is not valid UTF-8" ))
538546
547+ def test_rust_build_plain_survives_invalid_request (self ):
548+ mng = self .start_tcp_manager (og .tcp .OptimizerTcpManager (
549+ RustBuildTestCase .TEST_DIR + '/plain' ,
550+ ip = '127.0.0.1' ,
551+ port = 13759 ))
552+
553+ malformed_response = og .tcp .SolverResponse (
554+ RustBuildTestCase .raw_tcp_request ('127.0.0.1' , 13759 , '{"Run":' ))
555+ self .assertFalse (malformed_response .is_ok ())
556+ malformed_status = malformed_response .get ()
557+ self .assertEqual (1000 , malformed_status .code )
558+
559+ pong = mng .ping ()
560+ self .assertEqual (1 , pong ["Pong" ])
561+
562+ response = mng .call (p = [2.0 , 10.0 ])
563+ self .assertTrue (response .is_ok ())
564+ self .assertEqual ("Converged" , response .get ().exit_status )
565+
566+ def test_rust_build_plain_survives_disconnect_mid_request (self ):
567+ mng = self .start_tcp_manager (og .tcp .OptimizerTcpManager (
568+ RustBuildTestCase .TEST_DIR + '/plain' ,
569+ ip = '127.0.0.1' ,
570+ port = 13760 ))
571+
572+ RustBuildTestCase .send_partial_tcp_payload_and_close (
573+ '127.0.0.1' ,
574+ 13760 ,
575+ '{"Run":{"parameter":[2.0'
576+ )
577+
578+ pong = mng .ping ()
579+ self .assertEqual (1 , pong ["Pong" ])
580+
581+ response = mng .call (p = [2.0 , 10.0 ])
582+ self .assertTrue (response .is_ok ())
583+ self .assertEqual ("Converged" , response .get ().exit_status )
584+
539585 def test_rust_build_solver_error_details (self ):
540586 mng = self .start_tcp_manager (og .tcp .OptimizerTcpManager (
541587 RustBuildTestCase .TEST_DIR + '/solver_error' ))
@@ -623,6 +669,41 @@ def test_c_bindings(self):
623669 self .assertEqual (0 , rc1 )
624670 self .assertEqual (0 , rc2 )
625671
672+ def test_tcp_generated_server_builds (self ):
673+ tcp_iface_dir = os .path .join (
674+ RustBuildTestCase .TEST_DIR , "plain" , "tcp_iface_plain" )
675+ process = subprocess .Popen (
676+ ["cargo" , "build" , "--quiet" ],
677+ cwd = tcp_iface_dir ,
678+ stdout = subprocess .PIPE ,
679+ stderr = subprocess .PIPE ,
680+ )
681+ _stdout , stderr = process .communicate ()
682+
683+ self .assertEqual (
684+ 0 ,
685+ process .returncode ,
686+ msg = stderr .decode ()
687+ )
688+
689+ def test_tcp_manager_start_fails_cleanly_when_port_is_in_use (self ):
690+ mng1 = self .start_tcp_manager (og .tcp .OptimizerTcpManager (
691+ RustBuildTestCase .TEST_DIR + '/plain' ,
692+ ip = '127.0.0.1' ,
693+ port = 13761 ))
694+
695+ mng2 = og .tcp .OptimizerTcpManager (
696+ RustBuildTestCase .TEST_DIR + '/only_f1' ,
697+ ip = '127.0.0.1' ,
698+ port = 13761 )
699+ with self .assertRaises (Exception ) as context :
700+ mng2 .start ()
701+
702+ self .assertIn ("Port 13761 not available" , str (context .exception ))
703+
704+ pong = mng1 .ping ()
705+ self .assertEqual (1 , pong ["Pong" ])
706+
626707 def test_tcp_manager_remote_cannot_start (self ):
627708 remote_tcp_manager = og .tcp .OptimizerTcpManager (
628709 ip = '10.8.0.1' , port = 3345 )
0 commit comments