|
3 | 3 | # Distributed under the MIT software license, see the accompanying
|
4 | 4 | # file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
5 | 5 |
|
6 |
| -# |
7 |
| -# Run Regression Test Suite |
8 |
| -# |
| 6 | +""" |
| 7 | +Run Regression Test Suite |
| 8 | +
|
| 9 | +This module calls down into individual test cases via subprocess. It will |
| 10 | +forward all unrecognized arguments onto the individual test scripts, other |
| 11 | +than: |
| 12 | +
|
| 13 | + - `-extended`: run the "extended" test suite in addition to the basic one. |
| 14 | + - `-win`: signal that this is running in a Windows environment, and we |
| 15 | + should run the tests. |
| 16 | + - `--coverage`: this generates a basic coverage report for the RPC |
| 17 | + interface. |
| 18 | +
|
| 19 | +For a description of arguments recognized by test scripts, see |
| 20 | +`qa/pull-tester/test_framework/test_framework.py:BitcoinTestFramework.main`. |
| 21 | +
|
| 22 | +""" |
9 | 23 |
|
10 | 24 | import os
|
| 25 | +import shutil |
11 | 26 | import sys
|
12 | 27 | import subprocess
|
| 28 | +import tempfile |
13 | 29 | import re
|
| 30 | + |
14 | 31 | from tests_config import *
|
15 |
| -from sets import Set |
16 | 32 |
|
17 | 33 | #If imported values are not defined then set to zero (or disabled)
|
18 | 34 | if not vars().has_key('ENABLE_WALLET'):
|
|
24 | 40 | if not vars().has_key('ENABLE_ZMQ'):
|
25 | 41 | ENABLE_ZMQ=0
|
26 | 42 |
|
| 43 | +ENABLE_COVERAGE=0 |
| 44 | + |
27 | 45 | #Create a set to store arguments and create the passOn string
|
28 |
| -opts = Set() |
| 46 | +opts = set() |
29 | 47 | passOn = ""
|
30 | 48 | p = re.compile("^--")
|
31 |
| -for i in range(1,len(sys.argv)): |
32 |
| - if (p.match(sys.argv[i]) or sys.argv[i] == "-h"): |
33 |
| - passOn += " " + sys.argv[i] |
| 49 | + |
| 50 | +for arg in sys.argv[1:]: |
| 51 | + if arg == '--coverage': |
| 52 | + ENABLE_COVERAGE = 1 |
| 53 | + elif (p.match(arg) or arg == "-h"): |
| 54 | + passOn += " " + arg |
34 | 55 | else:
|
35 |
| - opts.add(sys.argv[i]) |
| 56 | + opts.add(arg) |
36 | 57 |
|
37 | 58 | #Set env vars
|
38 | 59 | buildDir = BUILDDIR
|
|
98 | 119 | if ENABLE_ZMQ == 1:
|
99 | 120 | testScripts.append('zmq_test.py')
|
100 | 121 |
|
101 |
| -if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): |
102 |
| - rpcTestDir = buildDir + '/qa/rpc-tests/' |
103 |
| - #Run Tests |
104 |
| - for i in range(len(testScripts)): |
105 |
| - if (len(opts) == 0 or (len(opts) == 1 and "-win" in opts ) or '-extended' in opts |
106 |
| - or testScripts[i] in opts or re.sub(".py$", "", testScripts[i]) in opts ): |
107 |
| - print "Running testscript " + testScripts[i] + "..." |
108 |
| - subprocess.check_call(rpcTestDir + testScripts[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) |
109 |
| - #exit if help is called so we print just one set of instructions |
110 |
| - p = re.compile(" -h| --help") |
111 |
| - if p.match(passOn): |
112 |
| - sys.exit(0) |
113 |
| - |
114 |
| - #Run Extended Tests |
115 |
| - for i in range(len(testScriptsExt)): |
116 |
| - if ('-extended' in opts or testScriptsExt[i] in opts |
117 |
| - or re.sub(".py$", "", testScriptsExt[i]) in opts): |
118 |
| - print "Running 2nd level testscript " + testScriptsExt[i] + "..." |
119 |
| - subprocess.check_call(rpcTestDir + testScriptsExt[i] + " --srcdir " + buildDir + '/src ' + passOn,shell=True) |
120 |
| -else: |
121 |
| - print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" |
| 122 | + |
| 123 | +def runtests(): |
| 124 | + coverage = None |
| 125 | + |
| 126 | + if ENABLE_COVERAGE: |
| 127 | + coverage = RPCCoverage() |
| 128 | + print("Initializing coverage directory at %s" % coverage.dir) |
| 129 | + |
| 130 | + if(ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): |
| 131 | + rpcTestDir = buildDir + '/qa/rpc-tests/' |
| 132 | + run_extended = '-extended' in opts |
| 133 | + cov_flag = coverage.flag if coverage else '' |
| 134 | + flags = " --srcdir %s/src %s %s" % (buildDir, cov_flag, passOn) |
| 135 | + |
| 136 | + #Run Tests |
| 137 | + for i in range(len(testScripts)): |
| 138 | + if (len(opts) == 0 |
| 139 | + or (len(opts) == 1 and "-win" in opts ) |
| 140 | + or run_extended |
| 141 | + or testScripts[i] in opts |
| 142 | + or re.sub(".py$", "", testScripts[i]) in opts ): |
| 143 | + print("Running testscript " + testScripts[i] + "...") |
| 144 | + |
| 145 | + subprocess.check_call( |
| 146 | + rpcTestDir + testScripts[i] + flags, shell=True) |
| 147 | + |
| 148 | + # exit if help is called so we print just one set of |
| 149 | + # instructions |
| 150 | + p = re.compile(" -h| --help") |
| 151 | + if p.match(passOn): |
| 152 | + sys.exit(0) |
| 153 | + |
| 154 | + # Run Extended Tests |
| 155 | + for i in range(len(testScriptsExt)): |
| 156 | + if (run_extended or testScriptsExt[i] in opts |
| 157 | + or re.sub(".py$", "", testScriptsExt[i]) in opts): |
| 158 | + print( |
| 159 | + "Running 2nd level testscript " |
| 160 | + + testScriptsExt[i] + "...") |
| 161 | + |
| 162 | + subprocess.check_call( |
| 163 | + rpcTestDir + testScriptsExt[i] + flags, shell=True) |
| 164 | + |
| 165 | + if coverage: |
| 166 | + coverage.report_rpc_coverage() |
| 167 | + |
| 168 | + print("Cleaning up coverage data") |
| 169 | + coverage.cleanup() |
| 170 | + |
| 171 | + else: |
| 172 | + print "No rpc tests to run. Wallet, utils, and bitcoind must all be enabled" |
| 173 | + |
| 174 | + |
| 175 | +class RPCCoverage(object): |
| 176 | + """ |
| 177 | + Coverage reporting utilities for pull-tester. |
| 178 | +
|
| 179 | + Coverage calculation works by having each test script subprocess write |
| 180 | + coverage files into a particular directory. These files contain the RPC |
| 181 | + commands invoked during testing, as well as a complete listing of RPC |
| 182 | + commands per `bitcoin-cli help` (`rpc_interface.txt`). |
| 183 | +
|
| 184 | + After all tests complete, the commands run are combined and diff'd against |
| 185 | + the complete list to calculate uncovered RPC commands. |
| 186 | +
|
| 187 | + See also: qa/rpc-tests/test_framework/coverage.py |
| 188 | +
|
| 189 | + """ |
| 190 | + def __init__(self): |
| 191 | + self.dir = tempfile.mkdtemp(prefix="coverage") |
| 192 | + self.flag = '--coveragedir %s' % self.dir |
| 193 | + |
| 194 | + def report_rpc_coverage(self): |
| 195 | + """ |
| 196 | + Print out RPC commands that were unexercised by tests. |
| 197 | +
|
| 198 | + """ |
| 199 | + uncovered = self._get_uncovered_rpc_commands() |
| 200 | + |
| 201 | + if uncovered: |
| 202 | + print("Uncovered RPC commands:") |
| 203 | + print("".join((" - %s\n" % i) for i in sorted(uncovered))) |
| 204 | + else: |
| 205 | + print("All RPC commands covered.") |
| 206 | + |
| 207 | + def cleanup(self): |
| 208 | + return shutil.rmtree(self.dir) |
| 209 | + |
| 210 | + def _get_uncovered_rpc_commands(self): |
| 211 | + """ |
| 212 | + Return a set of currently untested RPC commands. |
| 213 | +
|
| 214 | + """ |
| 215 | + # This is shared from `qa/rpc-tests/test-framework/coverage.py` |
| 216 | + REFERENCE_FILENAME = 'rpc_interface.txt' |
| 217 | + COVERAGE_FILE_PREFIX = 'coverage.' |
| 218 | + |
| 219 | + coverage_ref_filename = os.path.join(self.dir, REFERENCE_FILENAME) |
| 220 | + coverage_filenames = set() |
| 221 | + all_cmds = set() |
| 222 | + covered_cmds = set() |
| 223 | + |
| 224 | + if not os.path.isfile(coverage_ref_filename): |
| 225 | + raise RuntimeError("No coverage reference found") |
| 226 | + |
| 227 | + with open(coverage_ref_filename, 'r') as f: |
| 228 | + all_cmds.update([i.strip() for i in f.readlines()]) |
| 229 | + |
| 230 | + for root, dirs, files in os.walk(self.dir): |
| 231 | + for filename in files: |
| 232 | + if filename.startswith(COVERAGE_FILE_PREFIX): |
| 233 | + coverage_filenames.add(os.path.join(root, filename)) |
| 234 | + |
| 235 | + for filename in coverage_filenames: |
| 236 | + with open(filename, 'r') as f: |
| 237 | + covered_cmds.update([i.strip() for i in f.readlines()]) |
| 238 | + |
| 239 | + return all_cmds - covered_cmds |
| 240 | + |
| 241 | + |
| 242 | +if __name__ == '__main__': |
| 243 | + runtests() |
0 commit comments