@@ -124,7 +124,7 @@ def mscolab_session_server(mscolab_session_app, mscolab_session_managers):
124124 with _running_eventlet_server (mscolab_session_app ) as url :
125125 # Wait until the Flask-SocketIO server is ready for connections
126126 sio = socketio .Client ()
127- sio .connect (url , retry = True )
127+ sio .connect (url , retry = True , wait_timeout = 60 )
128128 sio .disconnect ()
129129 del sio
130130 yield url
@@ -186,32 +186,62 @@ def mswms_server(mswms_app):
186186 yield url
187187
188188
189+ def _start_eventlet_server (host , port_queue , app ):
190+ """
191+ Starts the Eventlet server and sends the chosen port back to the parent process.
192+ """
193+ sock = eventlet .listen ((host , 0 ))
194+ port = sock .getsockname ()[1 ]
195+ port_queue .put (port )
196+ eventlet .wsgi .server (sock , app , log_output = False )
197+
198+
189199@contextmanager
190200def _running_eventlet_server (app ):
191201 """Context manager that starts the app in an eventlet server and returns its URL."""
192202 scheme = "http"
193203 host = "127.0.0.1"
194- socket = eventlet .listen ((host , 0 ))
195- port = socket .getsockname ()[1 ]
196- url = f"{ scheme } ://{ host } :{ port } "
197- app .config ['URL' ] = url
204+
198205 if "fork" not in multiprocessing .get_all_start_methods ():
199206 pytest .skip ("requires the multiprocessing start_method 'fork', which is unavailable on this system" )
207+
200208 ctx = multiprocessing .get_context ("fork" )
201- process = ctx .Process (target = eventlet .wsgi .server , args = (socket , app ), daemon = True )
209+ # We are using a queue to retrieve the port selected in the child process.
210+ port_queue = ctx .Queue ()
211+
212+ process = ctx .Process (target = _start_eventlet_server , args = (host , port_queue , app ), daemon = True )
202213 try :
203214 process .start ()
215+ # Retrieve the port from the queue
216+ try :
217+ port = port_queue .get (timeout = 10 )
218+ except multiprocessing .queues .Empty :
219+ raise RuntimeError ("Could not retrieve port from server process" )
220+
221+ url = f"{ scheme } ://{ host } :{ port } "
222+ app .config ['URL' ] = url
223+
204224 start_time = time .time ()
205225 sleep_time = 0.01
206- while not is_url_response_ok (urllib .parse .urljoin (url , "index" )):
207- if (time .time () - start_time ) > 5 :
208- raise RuntimeError (f"Server did not start within 5 seconds at { url } " )
226+ time_out = 20
227+ # we check only for the root url, index.html may take longer
228+ readiness_url = urllib .parse .urljoin (url , "/" )
229+ while not is_url_response_ok (readiness_url ):
230+ if not process .is_alive ():
231+ # show the exitcode for further debugging
232+ raise RuntimeError (f"Server process exited early with code { process .exitcode } at { url } " )
233+ if (time .time () - start_time ) > time_out :
234+ raise RuntimeError (f"Server did not start within { time_out } seconds at { url } " )
209235 time .sleep (sleep_time )
210236 sleep_time *= 2
211237 if sleep_time > 1 :
212238 sleep_time = 1
213239 yield url
214240 finally :
215241 process .terminate ()
216- process .join (10 )
242+ process .join (timeout = 10 )
243+ if process .is_alive ():
244+ # when it is still alive after 10 seconds, kill it
245+ process .kill ()
246+ process .join (timeout = 5 )
217247 process .close ()
0 commit comments