Runs a single test, comparing output and RC to expected output and RC. Raises an error if input can't be read, executable fails, or output/RC are not as expected. Error is caught by bctester() and reported.
(self, testObj)
| 36 | self.test_one(test_obj) |
| 37 | |
| 38 | def test_one(self, testObj): |
| 39 | """Runs a single test, comparing output and RC to expected output and RC. |
| 40 | |
| 41 | Raises an error if input can't be read, executable fails, or output/RC |
| 42 | are not as expected. Error is caught by bctester() and reported. |
| 43 | """ |
| 44 | # Get the exec names and arguments |
| 45 | if testObj["exec"] == "./bitcoin-util": |
| 46 | execrun = self.bins.util_argv() + testObj["args"] |
| 47 | elif testObj["exec"] == "./bitcoin-tx": |
| 48 | execrun = self.bins.tx_argv() + testObj["args"] |
| 49 | |
| 50 | # Read the input data (if there is any) |
| 51 | inputData = None |
| 52 | if "input" in testObj: |
| 53 | with open(self.testcase_dir / testObj["input"]) as f: |
| 54 | inputData = f.read() |
| 55 | |
| 56 | # Read the expected output data (if there is any) |
| 57 | outputFn = None |
| 58 | outputData = None |
| 59 | outputType = None |
| 60 | if "output_cmp" in testObj: |
| 61 | outputFn = testObj['output_cmp'] |
| 62 | outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare) |
| 63 | with open(self.testcase_dir / outputFn) as f: |
| 64 | outputData = f.read() |
| 65 | if not outputData: |
| 66 | raise Exception(f"Output data missing for {outputFn}") |
| 67 | if not outputType: |
| 68 | raise Exception(f"Output file {outputFn} does not have a file extension") |
| 69 | |
| 70 | # Run the test |
| 71 | res = subprocess.run(execrun, capture_output=True, text=True, input=inputData) |
| 72 | |
| 73 | if outputData: |
| 74 | data_mismatch, formatting_mismatch = False, False |
| 75 | # Parse command output and expected output |
| 76 | try: |
| 77 | a_parsed = parse_output(res.stdout, outputType) |
| 78 | except Exception as e: |
| 79 | self.log.error(f"Error parsing command output as {outputType}: '{str(e)}'; res: {str(res)}") |
| 80 | raise |
| 81 | try: |
| 82 | b_parsed = parse_output(outputData, outputType) |
| 83 | except Exception as e: |
| 84 | self.log.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e)) |
| 85 | raise |
| 86 | # Compare data |
| 87 | if a_parsed != b_parsed: |
| 88 | self.log.error(f"Output data mismatch for {outputFn} (format {outputType}); res: {str(res)}") |
| 89 | data_mismatch = True |
| 90 | # Compare formatting |
| 91 | if res.stdout != outputData: |
| 92 | error_message = f"Output formatting mismatch for {outputFn}:\nres: {str(res)}\n" |
| 93 | error_message += "".join(difflib.context_diff(outputData.splitlines(True), |
| 94 | res.stdout.splitlines(True), |
| 95 | fromfile=outputFn, |