Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

bitcoin-util-test.py 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #!/usr/bin/env python3
  2. # Copyright 2014 BitPay Inc.
  3. # Copyright 2016-2017 The Bitcoin Core developers
  4. # Distributed under the MIT software license, see the accompanying
  5. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  6. """Test framework for bitcoin utils.
  7. Runs automatically during `make check`.
  8. Can also be run manually."""
  9. from __future__ import division,print_function,unicode_literals
  10. import argparse
  11. import binascii
  12. try:
  13. import configparser
  14. except ImportError:
  15. import ConfigParser as configparser
  16. import difflib
  17. import json
  18. import logging
  19. import os
  20. import pprint
  21. import subprocess
  22. import sys
  23. def main():
  24. config = configparser.ConfigParser()
  25. config.optionxform = str
  26. config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini")))
  27. env_conf = dict(config.items('environment'))
  28. parser = argparse.ArgumentParser(description=__doc__)
  29. parser.add_argument('-v', '--verbose', action='store_true')
  30. args = parser.parse_args()
  31. verbose = args.verbose
  32. if verbose:
  33. level = logging.DEBUG
  34. else:
  35. level = logging.ERROR
  36. formatter = '%(asctime)s - %(levelname)s - %(message)s'
  37. # Add the format/level to the logger
  38. logging.basicConfig(format=formatter, level=level)
  39. bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf)
  40. def bctester(testDir, input_basename, buildenv):
  41. """ Loads and parses the input file, runs all tests and reports results"""
  42. input_filename = testDir + "/" + input_basename
  43. raw_data = open(input_filename).read()
  44. input_data = json.loads(raw_data)
  45. failed_testcases = []
  46. for testObj in input_data:
  47. try:
  48. bctest(testDir, testObj, buildenv)
  49. logging.info("PASSED: " + testObj["description"])
  50. except:
  51. logging.info("FAILED: " + testObj["description"])
  52. failed_testcases.append(testObj["description"])
  53. if failed_testcases:
  54. error_message = "FAILED_TESTCASES:\n"
  55. error_message += pprint.pformat(failed_testcases, width=400)
  56. logging.error(error_message)
  57. sys.exit(1)
  58. else:
  59. sys.exit(0)
  60. def bctest(testDir, testObj, buildenv):
  61. """Runs a single test, comparing output and RC to expected output and RC.
  62. Raises an error if input can't be read, executable fails, or output/RC
  63. are not as expected. Error is caught by bctester() and reported.
  64. """
  65. # Get the exec names and arguments
  66. execprog = buildenv["BUILDDIR"] + "/src/" + testObj['exec'] + buildenv["EXEEXT"]
  67. execargs = testObj['args']
  68. execrun = [execprog] + execargs
  69. # Read the input data (if there is any)
  70. stdinCfg = None
  71. inputData = None
  72. if "input" in testObj:
  73. filename = testDir + "/" + testObj['input']
  74. inputData = open(filename).read()
  75. stdinCfg = subprocess.PIPE
  76. # Read the expected output data (if there is any)
  77. outputFn = None
  78. outputData = None
  79. if "output_cmp" in testObj:
  80. outputFn = testObj['output_cmp']
  81. outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare)
  82. try:
  83. outputData = open(testDir + "/" + outputFn).read()
  84. except:
  85. logging.error("Output file " + outputFn + " can not be opened")
  86. raise
  87. if not outputData:
  88. logging.error("Output data missing for " + outputFn)
  89. raise Exception
  90. # Run the test
  91. proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
  92. try:
  93. outs = proc.communicate(input=inputData)
  94. except OSError:
  95. logging.error("OSError, Failed to execute " + execprog)
  96. raise
  97. if outputData:
  98. data_mismatch, formatting_mismatch = False, False
  99. # Parse command output and expected output
  100. try:
  101. a_parsed = parse_output(outs[0], outputType)
  102. except Exception as e:
  103. logging.error('Error parsing command output as %s: %s' % (outputType, e))
  104. raise
  105. try:
  106. b_parsed = parse_output(outputData, outputType)
  107. except Exception as e:
  108. logging.error('Error parsing expected output %s as %s: %s' % (outputFn, outputType, e))
  109. raise
  110. # Compare data
  111. if a_parsed != b_parsed:
  112. logging.error("Output data mismatch for " + outputFn + " (format " + outputType + ")")
  113. data_mismatch = True
  114. # Compare formatting
  115. if outs[0] != outputData:
  116. error_message = "Output formatting mismatch for " + outputFn + ":\n"
  117. error_message += "".join(difflib.context_diff(outputData.splitlines(True),
  118. outs[0].splitlines(True),
  119. fromfile=outputFn,
  120. tofile="returned"))
  121. logging.error(error_message)
  122. formatting_mismatch = True
  123. assert not data_mismatch and not formatting_mismatch
  124. # Compare the return code to the expected return code
  125. wantRC = 0
  126. if "return_code" in testObj:
  127. wantRC = testObj['return_code']
  128. if proc.returncode != wantRC:
  129. logging.error("Return code mismatch for " + outputFn)
  130. raise Exception
  131. if "error_txt" in testObj:
  132. want_error = testObj["error_txt"]
  133. # Compare error text
  134. # TODO: ideally, we'd compare the strings exactly and also assert
  135. # That stderr is empty if no errors are expected. However, bitcoin-tx
  136. # emits DISPLAY errors when running as a windows application on
  137. # linux through wine. Just assert that the expected error text appears
  138. # somewhere in stderr.
  139. if want_error not in outs[1]:
  140. logging.error("Error mismatch:\n" + "Expected: " + want_error + "\nReceived: " + outs[1].rstrip())
  141. raise Exception
  142. def parse_output(a, fmt):
  143. """Parse the output according to specified format.
  144. Raise an error if the output can't be parsed."""
  145. if fmt == 'json': # json: compare parsed data
  146. return json.loads(a)
  147. elif fmt == 'hex': # hex: parse and compare binary data
  148. return binascii.a2b_hex(a.strip())
  149. else:
  150. raise NotImplementedError("Don't know how to compare %s" % fmt)
  151. if __name__ == '__main__':
  152. main()