1313import datetime
1414import signal
1515import shutil
16+ import difflib
1617import hashlib
1718
1819from .schema_tags import OutputTags
@@ -138,32 +139,77 @@ def parse_git_url(git_url: str) -> Tuple[Optional[str], Optional[str], Optional[
138139 return domain , user , project
139140
140141
142+ class OutputMismatch :
143+ def __init__ (self , input : str , expected_output : str , actual_output : str ) -> None :
144+ """Initialize the output mismatch class."""
145+ self ._input = input
146+ self ._expected_output = expected_output
147+ self ._actual_output = actual_output
148+
149+ @property
150+ def input (self : OutputMismatch ) -> str :
151+ """Get input."""
152+ return self ._input
153+
154+ @property
155+ def expected_output (self : OutputMismatch ) -> str :
156+ """Get expected output."""
157+ return self ._expected_output
158+
159+ @property
160+ def actual_output (self : OutputMismatch ) -> str :
161+ """Get actual output."""
162+ return self ._actual_output
163+
164+ def diff (self : OutputMismatch ) -> str :
165+ actual = str (self ._actual_output )
166+ expected = str (self ._expected_output )
167+ diff = difflib .unified_diff (
168+ actual .split ("\n " ),
169+ expected .split ("\n " ),
170+ fromfile = "Actual output" ,
171+ tofile = "Expected output" ,
172+ )
173+ diff_str = ""
174+ for line in diff :
175+ diff_str += line + "\n "
176+ return diff_str
177+
178+ def __repr__ (self : OutputMismatch ) -> str :
179+ """Representation of the output mismatch object."""
180+ return "input: {}, expected: {}, actual: {}" .format (
181+ self ._input , self ._expected_output , self ._actual_output
182+ )
183+
184+
141185class CmdResult :
142186 """A small container for command result."""
143187
144188 SUCCESS = 0
145189 FAILURE = 13
190+ TIMEOUT = 42
146191
147192 def __init__ (
148- self : CmdResult , returncode : int = None , stdout : str = None , stderr : str = None
193+ self : CmdResult ,
194+ status : int ,
195+ stdout : str = None ,
196+ stderr : str = None ,
197+ output_mismatch : OutputMismatch = None ,
149198 ):
150199 """Initialize either stdout of stderr."""
151- self ._returncode = returncode
200+ self ._status = status
152201 self ._stdout = stdout
153202 self ._stderr = stderr
203+ self ._output_mismatch = output_mismatch
154204
155205 def succeeded (self : CmdResult ) -> bool :
156206 """Check if the command succeeded."""
157- if self .returncode is not None :
158- return self .returncode == CmdResult .SUCCESS
159- if self .stderr :
160- return False
161- return True
207+ return self ._status == CmdResult .SUCCESS
162208
163209 @property
164- def returncode (self : CmdResult ) -> Optional [ int ] :
165- """Get returncode ."""
166- return self ._returncode
210+ def status (self : CmdResult ) -> int :
211+ """Get status ."""
212+ return self ._status
167213
168214 @property
169215 def stdout (self : CmdResult ) -> Optional [str ]:
@@ -175,24 +221,26 @@ def stderr(self: CmdResult) -> Optional[str]:
175221 """Get stderr."""
176222 return self ._stderr
177223
178- @stderr . setter
179- def stderr (self , value : str ) :
180- self . _returncode = None # We can't rely on returncode anymore
181- self ._stderr = value
224+ @property
225+ def output_mismatch (self : CmdResult ) -> Optional [ OutputMismatch ] :
226+ """Get output_mismatch."""
227+ return self ._output_mismatch
182228
183229 @staticmethod
184230 def success () -> CmdResult :
185231 """Return a cmd result that is a success."""
186- return CmdResult (stdout = "Success!" )
232+ return CmdResult (status = CmdResult . SUCCESS )
187233
188234 def __repr__ (self : CmdResult ) -> str :
189- """Representatin of command result."""
190- stdout = self .stdout
191- if not stdout :
192- stdout = ""
193- if self .stderr :
194- return "stdout: {}, stderr: {}" .format (stdout .strip (), self .stderr .strip ())
195- return stdout .strip ()
235+ """Representation of command result."""
236+ repr = "status: {} " .format (self ._status )
237+ if self ._stdout :
238+ repr += "stdout: {} " .format (self ._stdout )
239+ if self ._stderr :
240+ repr += "stderr: {} " .format (self ._stderr )
241+ if self ._output_mismatch :
242+ repr += "output_mismatch: {}" .format (self ._output_mismatch )
243+ return repr .strip ()
196244
197245
198246def run_command (
@@ -228,21 +276,21 @@ def run_command(
228276 timeout = timeout ,
229277 )
230278 return CmdResult (
231- returncode = process .returncode ,
279+ status = process .returncode ,
232280 stdout = process .stdout .decode ("utf-8" ),
233281 stderr = process .stderr .decode ("utf-8" ),
234282 )
235283 except subprocess .CalledProcessError as error :
236284 output_text = error .output .decode ("utf-8" )
237- log .error ("command '%s' finished with code: %s" , error .cmd , error .returncode )
285+ log .error ("command '%s' finished with code: %s" , error .cmd , error .status )
238286 log .debug ("command output: \n %s" , output_text )
239- return CmdResult (returncode = error .returncode , stderr = output_text )
287+ return CmdResult (status = error .status , stderr = output_text )
240288 except subprocess .TimeoutExpired as error :
241289 output_text = "Timeout: command '{}' ran longer than {} seconds" .format (
242290 error .cmd .strip (), error .timeout
243291 )
244292 log .error (output_text )
245- return CmdResult (returncode = 1 , stderr = output_text )
293+ return CmdResult (status = CmdResult . TIMEOUT , stderr = output_text )
246294
247295
248296def __run_subprocess (
@@ -281,11 +329,11 @@ def __run_subprocess(
281329 raise TimeoutExpired (
282330 process .args , timeout , output = stdout , stderr = stderr
283331 ) from timeout_error
284- retcode = process .poll ()
285- if retcode is None :
286- retcode = 1
287- if check and retcode :
332+ return_code = process .poll ()
333+ if return_code is None :
334+ return_code = 1
335+ if check and return_code :
288336 raise CalledProcessError (
289- retcode , process .args , output = stdout , stderr = stderr
337+ return_code , process .args , output = stdout , stderr = stderr
290338 )
291- return CompletedProcess (process .args , retcode , stdout , stderr )
339+ return CompletedProcess (process .args , return_code , stdout , stderr )
0 commit comments