Repository: bitcoin/bitcoin
File: test/functional/tool_utils.py (Python)

Code before:
            execrun = self.bins.util_argv() + testObj["args"]
        elif testObj["exec"] == "./bitcoin-tx":
            execrun = self.bins.tx_argv() + testObj["args"]

        # Read the input data (if there is any)
        inputData = None
        if "input" in testObj:
            with open(self.testcase_dir / testObj["input"], encoding="utf8") as f:
                inputData = f.read()

        # Read the expected output data (if there is any)
        outputFn = None
        outputData = None
        outputType = None
        if "output_cmp" in testObj:
            outputFn = testObj['output_cmp']
            outputType = os.path.splitext(outputFn)[1][1:]  # output type from file extension (determines how to compare)
            try:
                with open(self.testcase_dir / outputFn, encoding="utf8") as f:
                    outputData = f.read()
            except Exception:
                logging.error("Output file " + outputFn + " cannot be opened")
                raise
            if not outputData:
                logging.error("Output data missing for " + outputFn)
                raise Exception
            if not outputType:
                logging.error("Output file %s does not have a file extension" % outputFn)
                raise Exception

        # Run the test
        try:
            res = subprocess.run(execrun, capture_output=True, text=True, input=inputData)
        except OSError:
            logging.error("OSError, Failed to execute " + str(execrun))
            raise

        if outputData:
            data_mismatch, formatting_mismatch = False, False
            # Parse command output and expected output
            try:
                a_parsed = parse_output(res.stdout, outputType)
            except Exception as e:
                logging.error(f"Error parsing command output as {outputType}: '{str(e)}'; res: {str(res)}")
                raise
            try:
                b_parsed = parse_output(outputData, outputType)
            except Exception as e:
                logging.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e))
                raise
            # Compare data

Review comment (suggestion):
In faa18bf287fc02339b7af29d54af862a289bf96d "test: Turn util/test_runner into functional test"

This could be a good time to move the error message into the Exception rather than an error log.

Code after:
        elif testObj["exec"] == "./bitcoin-tx":
            execrun = self.bins.tx_argv() + testObj["args"]

        # Read the input data (if there is any)
        inputData = None
        if "input" in testObj:
            with open(self.testcase_dir / testObj["input"], encoding="utf8") as f:
                inputData = f.read()

        # Read the expected output data (if there is any)
        outputFn = None
        outputData = None
        outputType = None
        if "output_cmp" in testObj:
            outputFn = testObj['output_cmp']
            outputType = os.path.splitext(outputFn)[1][1:]  # output type from file extension (determines how to compare)
            with open(self.testcase_dir / outputFn, encoding="utf8") as f:
                outputData = f.read()
            if not outputData:
                raise Exception(f"Output data missing for {outputFn}")
            if not outputType:
                raise Exception(f"Output file {outputFn} does not have a file extension")

        # Run the test
        res = subprocess.run(execrun, capture_output=True, text=True, input=inputData)

        if outputData:
            data_mismatch, formatting_mismatch = False, False
            # Parse command output and expected output
            try:
                a_parsed = parse_output(res.stdout, outputType)
            except Exception as e:
                self.log.error(f"Error parsing command output as {outputType}: '{str(e)}'; res: {str(res)}")
                raise
            try:
                b_parsed = parse_output(outputData, outputType)
            except Exception as e:
                self.log.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e))
                raise
            # Compare data
            if a_parsed != b_parsed:
                self.log.error(f"Output data mismatch for {outputFn} (format {outputType}); res: {str(res)}")
                data_mismatch = True
            # Compare formatting
            if res.stdout != outputData:
                error_message = f"Output formatting mismatch for {outputFn}:\nres: {str(res)}\n"
                error_message += "".join(difflib.context_diff(outputData.splitlines(True),
                                                              res.stdout.splitlines(True),
                                                              fromfile=outputFn,
                                                              tofile="returned"))
                self.log.error(error_message)