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.
8 Runs automatically during `make check`.
10 Can also be run manually."""
12 from __future__
import division
,print_function
,unicode_literals
19 import ConfigParser
as configparser
29 config
= configparser
.ConfigParser()
30 config
.optionxform
= str
31 config
.readfp(open(os
.path
.join(os
.path
.dirname(__file__
), "../config.ini")))
32 env_conf
= dict(config
.items('environment'))
34 parser
= argparse
.ArgumentParser(description
=__doc__
)
35 parser
.add_argument('-v', '--verbose', action
='store_true')
36 args
= parser
.parse_args()
37 verbose
= args
.verbose
43 formatter
= '%(asctime)s - %(levelname)s - %(message)s'
44 # Add the format/level to the logger
45 logging
.basicConfig(format
=formatter
, level
=level
)
47 bctester(os
.path
.join(env_conf
["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf
)
49 def bctester(testDir
, input_basename
, buildenv
):
50 """ Loads and parses the input file, runs all tests and reports results"""
51 input_filename
= os
.path
.join(testDir
, input_basename
)
52 raw_data
= open(input_filename
).read()
53 input_data
= json
.loads(raw_data
)
57 for testObj
in input_data
:
59 bctest(testDir
, testObj
, buildenv
)
60 logging
.info("PASSED: " + testObj
["description"])
62 logging
.info("FAILED: " + testObj
["description"])
63 failed_testcases
.append(testObj
["description"])
66 error_message
= "FAILED_TESTCASES:\n"
67 error_message
+= pprint
.pformat(failed_testcases
, width
=400)
68 logging
.error(error_message
)
73 def bctest(testDir
, testObj
, buildenv
):
74 """Runs a single test, comparing output and RC to expected output and RC.
76 Raises an error if input can't be read, executable fails, or output/RC
77 are not as expected. Error is caught by bctester() and reported.
79 # Get the exec names and arguments
80 execprog
= os
.path
.join(buildenv
["BUILDDIR"], "src", testObj
["exec"] + buildenv
["EXEEXT"])
81 execargs
= testObj
['args']
82 execrun
= [execprog
] + execargs
84 # Read the input data (if there is any)
87 if "input" in testObj
:
88 filename
= os
.path
.join(testDir
, testObj
["input"])
89 inputData
= open(filename
).read()
90 stdinCfg
= subprocess
.PIPE
92 # Read the expected output data (if there is any)
96 if "output_cmp" in testObj
:
97 outputFn
= testObj
['output_cmp']
98 outputType
= os
.path
.splitext(outputFn
)[1][1:] # output type from file extension (determines how to compare)
100 outputData
= open(os
.path
.join(testDir
, outputFn
)).read()
102 logging
.error("Output file " + outputFn
+ " can not be opened")
105 logging
.error("Output data missing for " + outputFn
)
108 logging
.error("Output file %s does not have a file extension" % outputFn
)
112 proc
= subprocess
.Popen(execrun
, stdin
=stdinCfg
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, universal_newlines
=True)
114 outs
= proc
.communicate(input=inputData
)
116 logging
.error("OSError, Failed to execute " + execprog
)
120 data_mismatch
, formatting_mismatch
= False, False
121 # Parse command output and expected output
123 a_parsed
= parse_output(outs
[0], outputType
)
124 except Exception as e
:
125 logging
.error('Error parsing command output as %s: %s' % (outputType
, e
))
128 b_parsed
= parse_output(outputData
, outputType
)
129 except Exception as e
:
130 logging
.error('Error parsing expected output %s as %s: %s' % (outputFn
, outputType
, e
))
133 if a_parsed
!= b_parsed
:
134 logging
.error("Output data mismatch for " + outputFn
+ " (format " + outputType
+ ")")
137 if outs
[0] != outputData
:
138 error_message
= "Output formatting mismatch for " + outputFn
+ ":\n"
139 error_message
+= "".join(difflib
.context_diff(outputData
.splitlines(True),
140 outs
[0].splitlines(True),
143 logging
.error(error_message
)
144 formatting_mismatch
= True
146 assert not data_mismatch
and not formatting_mismatch
148 # Compare the return code to the expected return code
150 if "return_code" in testObj
:
151 wantRC
= testObj
['return_code']
152 if proc
.returncode
!= wantRC
:
153 logging
.error("Return code mismatch for " + outputFn
)
156 if "error_txt" in testObj
:
157 want_error
= testObj
["error_txt"]
159 # TODO: ideally, we'd compare the strings exactly and also assert
160 # That stderr is empty if no errors are expected. However, bitcoin-tx
161 # emits DISPLAY errors when running as a windows application on
162 # linux through wine. Just assert that the expected error text appears
163 # somewhere in stderr.
164 if want_error
not in outs
[1]:
165 logging
.error("Error mismatch:\n" + "Expected: " + want_error
+ "\nReceived: " + outs
[1].rstrip())
168 def parse_output(a
, fmt
):
169 """Parse the output according to specified format.
171 Raise an error if the output can't be parsed."""
172 if fmt
== 'json': # json: compare parsed data
174 elif fmt
== 'hex': # hex: parse and compare binary data
175 return binascii
.a2b_hex(a
.strip())
177 raise NotImplementedError("Don't know how to compare %s" % fmt
)
179 if __name__
== '__main__':