@@ -109,7 +109,8 @@ def compute_argv(self, test_path: str,
109109 return argv
110110
111111 def run_test (self , config : Config , argv : List [str ]) -> Result :
112- # pylint: disable=too-many-branches
112+ config .dry_run ()
113+ # pylint: disable=too-many-branches,too-many-locals
113114 result = Result (is_executed = True , failures = [])
114115
115116 proc : subprocess .Popen [Any ] | None = None
@@ -123,36 +124,30 @@ def run_test(self, config: Config, argv: List[str]) -> Result:
123124 proc = subprocess .Popen (argv , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True )
124125 cleanup_dirs = dirs
125126 case Read () as read :
126- if proc is None :
127- result .failures .append (Failure .unexpected ("Read operation called before Run" ))
128- else :
129- # Instance asserts might seem redudant here, given the match.
130- # Asserts merely exist to ensure that mypy can fully resolve the underlying type;
131- # else it will report errors like:
132- # wasi_test_runner/runtime_adapter.py:131: error: Argument 2 to "_handle_read"
133- # has incompatible type "Read"; expected "Read" [arg-type]
134- assert isinstance (read , Read )
135- _handle_read (proc , read , result )
127+ assert proc is not None
128+ # Instance asserts might seem redudant here, given the match.
129+ # Asserts merely exist to ensure that mypy can fully resolve the underlying type;
130+ # else it will report errors like:
131+ # wasi_test_runner/runtime_adapter.py:131: error: Argument 2 to "_handle_read"
132+ # has incompatible type "Read"; expected "Read" [arg-type]
133+ assert isinstance (read , Read )
134+ _handle_read (proc , read , result )
136135 case Wait () as wait :
137- if proc is None :
138- result . failures . append ( Failure . unexpected ( "Wait operation called before Run" ) )
139- else :
140- assert isinstance ( wait , Wait )
141- _handle_wait ( proc , wait , result )
136+ assert proc is not None
137+ assert isinstance ( wait , Wait )
138+ _handle_wait ( proc , wait , result )
139+ # The wait handler will `kill` the process
140+ proc = None
142141 case Connect () as conn :
143- if proc is None :
144- result .failures .append (Failure .unexpected ("Connect operation called before Run" ))
145- else :
146- assert isinstance (conn , Connect )
147- _handle_connect (proc , config , conn , result )
142+ assert proc is not None
143+ assert isinstance (conn , Connect )
144+ _handle_connect (proc , config , conn , result )
148145 case Send () as send :
149146 assert isinstance (send , Send )
150147 _handle_send (config , send , result )
151148 case Recv () as recv :
152149 assert isinstance (recv , Recv )
153150 _handle_recv (config , recv , result )
154- case _:
155- pass
156151
157152 finally :
158153 if cleanup_dirs :
@@ -164,44 +159,36 @@ def run_test(self, config: Config, argv: List[str]) -> Result:
164159 _ , _ = proc .communicate (timeout = 5 )
165160 except subprocess .TimeoutExpired :
166161 proc .kill ()
167- result .failures .append (Failure .unexpected ("Process timed out" ))
162+ out , err = proc .communicate ()
163+ msg = "Process timed out"
164+ msg = _append_stdout_and_stderr (msg , out , err )
165+ result .failures .append (Failure .unexpected (msg ))
168166
169167 return result
170168
171169
172170def _handle_read (proc : subprocess .Popen [Any ], spec : Read , result : Result ) -> None :
173- if spec .id == "stdout" :
174- if proc .stdout is None :
175- result .failures .append (Failure .unexpected (f"{ spec .id } is not available" ))
176- return
177- payload = proc .stdout .readline ().strip ()
178- if payload != spec .payload :
179- result .failures .append (Failure .expectation (f"{ spec .id } failed: expected { spec .payload } , got { payload } " ))
171+ stream = getattr (proc , spec .id , None )
172+ if stream is None :
173+ result .failures .append (Failure .unexpected (f"{ spec } { spec .id } is not available" ))
174+ return
180175
181- if spec .id == "stderr" :
182- if proc .stderr is None :
183- result .failures .append (Failure .unexpected (f"{ spec .id } is not available" ))
184- return
185- payload = proc .stderr .readline ().strip ()
186- if payload != spec .payload :
187- result .failures .append (Failure .expectation (f"{ spec .id } failed: expected { spec .payload } , got { payload } " ))
176+ payload = stream .readline ().strip ()
177+ if payload != spec .payload :
178+ result .failures .append (Failure .expectation (f"{ spec } { spec .id } failed: expected { spec .payload } , got { payload } " ))
188179
189180
190181def _handle_wait (proc : subprocess .Popen [Any ], spec : Wait , result : Result ) -> None :
191182 try :
192183 out , err = proc .communicate (timeout = 5 )
193184 if spec .exit_code != proc .returncode :
194185 msg = f"{ spec } failed: expected { spec .exit_code } , got { proc .returncode } "
195-
196- if out :
197- msg += f"\n \n ==STDOUT==\n { out } "
198-
199- if err :
200- msg += f"\n \n ==STDERR==\n { err } "
201-
186+ msg = _append_stdout_and_stderr (msg , out , err )
202187 result .failures .append (Failure .expectation (msg ))
203188
204189 except subprocess .TimeoutExpired :
190+ proc .kill ()
191+ out , err = proc .communicate ()
205192 result .failures .append (Failure .expectation (f"{ spec } failed: timeout expired" ))
206193
207194
@@ -220,35 +207,55 @@ def _handle_connect(proc: subprocess.Popen[Any], config: Config, spec: Connect,
220207 result .failures .append (Failure .unexpected (f"{ spec } : No connection information available" ))
221208 return
222209
223- host , port = proc .stdout .readline ().strip ().split (':' )
210+ try :
211+ line = proc .stdout .readline ().strip ()
212+ parts = line .split (':' )
213+ if len (parts ) != 2 :
214+ msg = f"{ spec } : Expected address information to be available as <host>: <port>, found { line } "
215+ result .failures .append (Failure .unexpected (msg ))
216+ return
217+ host , port_str = parts
218+ if not host or not port_str :
219+ msg = f"{ spec } : Expected address information to be available as <host>: <port>, found { line } "
220+ result .failures .append (Failure .unexpected (msg ))
221+ return
222+ port = int (port_str )
223+ except ValueError as e :
224+ result .failures .append (Failure .unexpected (f"{ spec } : Failed to parse connection information: { e } " ))
225+ return
226+
227+ sock = None
224228 try :
225229 sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
226- sock .connect ((host , int ( port ) ))
230+ sock .connect ((host , port ))
227231 config .connections [spec .id ] = sock
228- except socket .timeout :
229- host_port = host + ":" + port
230- result .failures .append (Failure .unexpected (f"{ spec } : Could not connect to { host_port } " ))
232+ except (socket .timeout , ConnectionRefusedError , OSError ) as e :
233+ if sock is not None :
234+ sock .close ()
235+ result .failures .append (Failure .unexpected (f"{ spec } : Could not connect to { host } : { port } - { e } " ))
231236
232237
233238def _handle_send (config : Config , spec : Send , result : Result ) -> None :
234- if config .connections [spec .id ] is None :
235- result .failures .append (Failure .unexpected (f"{ spec } : No connection declared for id { spec .id } " ))
236- return
237-
238239 sock = config .connections [spec .id ]
239- sock .sendall (spec .payload .encode ('utf-8' ))
240+ assert sock is not None
241+ try :
242+ sock .sendall (spec .payload .encode ('utf-8' ))
243+ except (OSError , socket .error ) as e :
244+ result .failures .append (Failure .unexpected (f"{ spec } : Failed to send data: { e } " ))
240245
241246
242247def _handle_recv (config : Config , spec : Recv , result : Result ) -> None :
243- if config .connections [spec .id ] is None :
244- result .failures .append (Failure .unexpected (f"{ spec } : No connection declared for id { spec .id } " ))
245- return
246-
247248 sock = config .connections [spec .id ]
248- response_bytes = sock .recv (len (spec .payload ))
249- response = response_bytes .decode ('utf-8' )
250- if response != spec .payload :
251- result .failures .append (Failure .unexpected (f"{ spec } : Expected { spec .payload } , got { response } " ))
249+ assert sock is not None
250+ try :
251+ response_bytes = sock .recv (len (spec .payload ))
252+ response = response_bytes .decode ('utf-8' )
253+ if response != spec .payload :
254+ result .failures .append (Failure .unexpected (f"{ spec } : Expected { spec .payload } , got { response } " ))
255+ except (OSError , socket .error ) as e :
256+ result .failures .append (Failure .unexpected (f"{ spec } : Failed to receive data: { e } " ))
257+ except UnicodeDecodeError as e :
258+ result .failures .append (Failure .unexpected (f"{ spec } : Failed to decode response: { e } " ))
252259
253260
254261def _cleanup_test_output (dirs : List [Tuple [Path , str ]]) -> None :
@@ -258,3 +265,13 @@ def _cleanup_test_output(dirs: List[Tuple[Path, str]]) -> None:
258265 f .unlink ()
259266 elif f .is_dir ():
260267 shutil .rmtree (f )
268+
269+
270+ def _append_stdout_and_stderr (msg : str , out : str | None , err : str | None ) -> str :
271+ if out :
272+ msg += f"\n \n ==STDOUT==\n { out } "
273+
274+ if err :
275+ msg += f"\n \n ==STDERR==\n { err } "
276+
277+ return msg
0 commit comments