44import random
55from dataclasses import dataclass , field
66from enum import Enum
7- from typing import Dict , List , Optional
7+ from typing import Dict , List , Optional , Tuple
88
99import yaml
1010from pytest import CaptureFixture , MonkeyPatch
@@ -104,6 +104,7 @@ class FailedResults:
104104 wrong_results : Dict [str , str ] = field (default_factory = lambda : {})
105105 unexpected_parse_error : Dict [str , str ] = field (default_factory = lambda : {})
106106 unexpected_runtime_error : Dict [str , str ] = field (default_factory = lambda : {})
107+ wrong_replay_results : Dict [str , str ] = field (default_factory = lambda : {})
107108
108109
109110# pylint: disable=too-many-instance-attributes
@@ -161,7 +162,9 @@ def __init__(self, monkeypatch: MonkeyPatch) -> None:
161162 self .__collect_expected_results ()
162163
163164 # Inits execution results for each PDL file
164- self .execution_results : Dict [str , ExecutionResult ] = {}
165+ self .execution_results : Dict [
166+ str , Tuple [ExecutionResult , ExecutionResult | None ]
167+ ] = {}
165168
166169 # Init failed results
167170 self .failed_results = FailedResults ()
@@ -199,13 +202,11 @@ def __collect_expected_results(self) -> None:
199202
200203 self .expected_results [file ] = expected_result
201204
202- def __execute_file (self , pdl_file_name : str ) -> None :
205+ def __execute_and_replay_file (self , pdl_file_name : str ) -> None :
203206 """
204207 Tests the result of a single file and returns the result output and the error code
205208 """
206209
207- exec_result = ExecutionResult ()
208-
209210 pdl_file_path = pathlib .Path (pdl_file_name )
210211 scope : ScopeType = PdlDict ({})
211212
@@ -217,13 +218,27 @@ def __execute_file(self, pdl_file_name: str) -> None:
217218 if inputs .scope is not None :
218219 scope = inputs .scope
219220
221+ exec_result , output = self .__execute_file (pdl_file_path , scope , replay = {})
222+
223+ if output is not None :
224+ replay_result , _ = self .__execute_file (
225+ pdl_file_path , scope , replay = output ["replay" ]
226+ )
227+ else :
228+ replay_result = None
229+
230+ self .execution_results [pdl_file_name ] = exec_result , replay_result
231+
232+ def __execute_file (self , pdl_file_path , scope , replay ):
233+ exec_result = ExecutionResult ()
234+ output = None
220235 try :
221236 # Execute file
222237 output = pdl .exec_file (
223238 pdl_file_path ,
224239 scope = scope ,
225240 output = "all" ,
226- config = pdl .InterpreterConfig (batch = 1 ),
241+ config = pdl .InterpreterConfig (batch = 1 , replay = replay ),
227242 )
228243
229244 exec_result .result = str (output ["result" ])
@@ -235,8 +250,7 @@ def __execute_file(self, pdl_file_name: str) -> None:
235250 except Exception as exc :
236251 exec_result .result = str (exc )
237252 exec_result .error_code = ExecutionErrorCode .RUNTIME_ERROR
238-
239- self .execution_results [pdl_file_name ] = exec_result
253+ return exec_result , output
240254
241255 def populate_exec_result_for_checks (self ) -> None :
242256 """
@@ -245,7 +259,7 @@ def populate_exec_result_for_checks(self) -> None:
245259
246260 for file in self .check :
247261 if file not in self .skip :
248- self .__execute_file (file )
262+ self .__execute_and_replay_file (file )
249263
250264 def validate_expected_and_actual (self ) -> None :
251265 """
@@ -256,11 +270,12 @@ def validate_expected_and_actual(self) -> None:
256270 wrong_result : Dict [str , str ] = {}
257271 unexpected_parse_error : Dict [str , str ] = {}
258272 unexpected_runtime_error : Dict [str , str ] = {}
273+ wrong_replay_result : Dict [str , str ] = {}
259274
260275 for file in self .check :
261276 if file not in self .skip :
262277 expected_result = self .expected_results [file ]
263- actual_result = self .execution_results [file ]
278+ actual_result , replay_result = self .execution_results [file ]
264279 match = expected_result .compare_to_execution (actual_result )
265280
266281 if not match :
@@ -274,7 +289,14 @@ def validate_expected_and_actual(self) -> None:
274289 if actual_result .result is not None :
275290 wrong_result [file ] = actual_result .result
276291
292+ if replay_result is not None :
293+ match_replay = expected_result .compare_to_execution (replay_result )
294+ if not match_replay :
295+ if replay_result .result is not None :
296+ wrong_replay_result [file ] = replay_result .result
297+
277298 self .failed_results .wrong_results = wrong_result
299+ self .failed_results .wrong_replay_results = wrong_replay_result
278300 self .failed_results .unexpected_parse_error = unexpected_parse_error
279301 self .failed_results .unexpected_runtime_error = unexpected_runtime_error
280302
@@ -347,6 +369,16 @@ def test_example_runs(capsys: CaptureFixture[str], monkeypatch: MonkeyPatch) ->
347369 f"Actual result (copy everything below this line):\n ✂️ ------------------------------------------------------------\n { actual } \n -------------------------------------------------------------"
348370 )
349371
372+ # Print the actual results for wrong replay results
373+ for file , actual in background .failed_results .wrong_replay_results .items ():
374+ print (
375+ "\n ============================================================================"
376+ )
377+ print (f"File that produced wrong REPLAY result: { file } " )
378+ print (
379+ f"Actual result:\n ------------------------------------------------------------\n { actual } \n -------------------------------------------------------------"
380+ )
381+
350382 assert (
351383 len (background .failed_results .unexpected_parse_error ) == 0
352384 ), f"Unexpected parse error: { background .failed_results .unexpected_parse_error } "
0 commit comments