]>
Commit | Line | Data |
---|---|---|
aadf3aa1 JG |
1 | #!/usr/bin/env python2 |
2 | # | |
3 | # Execute all of the automated tests related to Zcash. | |
4 | # | |
5 | ||
6 | import argparse | |
7 | import os | |
c6af0aa4 | 8 | import re |
aadf3aa1 JG |
9 | import subprocess |
10 | import sys | |
11 | ||
12 | REPOROOT = os.path.dirname( | |
13 | os.path.dirname( | |
14 | os.path.dirname( | |
15 | os.path.abspath(__file__) | |
16 | ) | |
17 | ) | |
18 | ) | |
19 | ||
20 | def repofile(filename): | |
21 | return os.path.join(REPOROOT, filename) | |
22 | ||
23 | ||
105b2b62 JG |
24 | # |
25 | # Custom test runners | |
26 | # | |
27 | ||
c6af0aa4 JG |
28 | RE_RPATH_RUNPATH = re.compile('No RPATH.*No RUNPATH') |
29 | RE_FORTIFY_AVAILABLE = re.compile('FORTIFY_SOURCE support available.*Yes') | |
30 | RE_FORTIFY_USED = re.compile('Binary compiled with FORTIFY_SOURCE support.*Yes') | |
31 | ||
32 | def test_rpath_runpath(filename): | |
33 | output = subprocess.check_output( | |
34 | [repofile('qa/zcash/checksec.sh'), '--file', repofile(filename)] | |
35 | ) | |
36 | if RE_RPATH_RUNPATH.search(output): | |
37 | print('PASS: %s has no RPATH or RUNPATH.' % filename) | |
38 | return True | |
39 | else: | |
40 | print('FAIL: %s has an RPATH or a RUNPATH.' % filename) | |
41 | print(output) | |
42 | return False | |
43 | ||
44 | def test_fortify_source(filename): | |
45 | proc = subprocess.Popen( | |
46 | [repofile('qa/zcash/checksec.sh'), '--fortify-file', repofile(filename)], | |
47 | stdout=subprocess.PIPE, | |
48 | ) | |
49 | line1 = proc.stdout.readline() | |
50 | line2 = proc.stdout.readline() | |
51 | proc.terminate() | |
52 | if RE_FORTIFY_AVAILABLE.search(line1) and RE_FORTIFY_USED.search(line2): | |
53 | print('PASS: %s has FORTIFY_SOURCE.' % filename) | |
54 | return True | |
55 | else: | |
56 | print('FAIL: %s is missing FORTIFY_SOURCE.' % filename) | |
57 | return False | |
58 | ||
59 | def check_security_hardening(): | |
60 | ret = True | |
61 | ||
62 | # PIE, RELRO, Canary, and NX are tested by make check-security. | |
63 | ret &= subprocess.call(['make', '-C', repofile('src'), 'check-security']) == 0 | |
64 | ||
65 | ret &= test_rpath_runpath('src/zcashd') | |
66 | ret &= test_rpath_runpath('src/zcash-cli') | |
67 | ret &= test_rpath_runpath('src/zcash-gtest') | |
68 | ret &= test_rpath_runpath('src/zcash-tx') | |
69 | ret &= test_rpath_runpath('src/test/test_bitcoin') | |
70 | ret &= test_rpath_runpath('src/zcash/GenerateParams') | |
71 | ||
72 | # NOTE: checksec.sh does not reliably determine whether FORTIFY_SOURCE | |
73 | # is enabled for the entire binary. See issue #915. | |
74 | ret &= test_fortify_source('src/zcashd') | |
75 | ret &= test_fortify_source('src/zcash-cli') | |
76 | ret &= test_fortify_source('src/zcash-gtest') | |
77 | ret &= test_fortify_source('src/zcash-tx') | |
78 | ret &= test_fortify_source('src/test/test_bitcoin') | |
79 | ret &= test_fortify_source('src/zcash/GenerateParams') | |
80 | ||
81 | return ret | |
82 | ||
105b2b62 JG |
83 | def ensure_no_dot_so_in_depends(): |
84 | arch_dir = os.path.join( | |
85 | REPOROOT, | |
86 | 'depends', | |
87 | 'x86_64-unknown-linux-gnu', | |
88 | ) | |
89 | ||
90 | exit_code = 0 | |
91 | ||
92 | if os.path.isdir(arch_dir): | |
93 | lib_dir = os.path.join(arch_dir, 'lib') | |
94 | libraries = os.listdir(lib_dir) | |
95 | ||
96 | for lib in libraries: | |
97 | if lib.find(".so") != -1: | |
98 | print lib | |
99 | exit_code = 1 | |
100 | else: | |
101 | exit_code = 2 | |
102 | print "arch-specific build dir not present: {}".format(arch_dir) | |
103 | print "Did you build the ./depends tree?" | |
104 | print "Are you on a currently unsupported architecture?" | |
105 | ||
106 | if exit_code == 0: | |
107 | print "PASS." | |
108 | else: | |
109 | print "FAIL." | |
110 | ||
111 | return exit_code == 0 | |
112 | ||
88fbdc48 JG |
113 | def util_test(): |
114 | return subprocess.call( | |
115 | [repofile('src/test/bitcoin-util-test.py')], | |
116 | cwd=repofile('src'), | |
117 | env={'PYTHONPATH': repofile('src/test'), 'srcdir': repofile('src')} | |
118 | ) == 0 | |
119 | ||
105b2b62 | 120 | |
aadf3aa1 JG |
121 | # |
122 | # Tests | |
123 | # | |
124 | ||
125 | STAGES = [ | |
126 | 'btest', | |
127 | 'gtest', | |
128 | 'sec-hard', | |
129 | 'no-dot-so', | |
88fbdc48 | 130 | 'util-test', |
aadf3aa1 | 131 | 'secp256k1', |
91dd425b | 132 | 'libsnark', |
aadf3aa1 JG |
133 | 'univalue', |
134 | 'rpc', | |
135 | ] | |
136 | ||
137 | STAGE_COMMANDS = { | |
138 | 'btest': [repofile('src/test/test_bitcoin'), '-p'], | |
139 | 'gtest': [repofile('src/zcash-gtest')], | |
c6af0aa4 | 140 | 'sec-hard': check_security_hardening, |
105b2b62 | 141 | 'no-dot-so': ensure_no_dot_so_in_depends, |
88fbdc48 | 142 | 'util-test': util_test, |
aadf3aa1 | 143 | 'secp256k1': ['make', '-C', repofile('src/secp256k1'), 'check'], |
91dd425b | 144 | 'libsnark': ['make', '-C', repofile('src'), 'libsnark-tests'], |
aadf3aa1 JG |
145 | 'univalue': ['make', '-C', repofile('src/univalue'), 'check'], |
146 | 'rpc': [repofile('qa/pull-tester/rpc-tests.sh')], | |
147 | } | |
148 | ||
149 | ||
150 | # | |
151 | # Test driver | |
152 | # | |
153 | ||
154 | def run_stage(stage): | |
155 | print('Running stage %s' % stage) | |
156 | print('=' * (len(stage) + 14)) | |
157 | ||
158 | ||
105b2b62 JG |
159 | cmd = STAGE_COMMANDS[stage] |
160 | if type(cmd) == type([]): | |
161 | ret = subprocess.call(cmd) == 0 | |
162 | else: | |
163 | ret = cmd() | |
aadf3aa1 JG |
164 | |
165 | ||
166 | print('-' * (len(stage) + 15)) | |
167 | print('Finished stage %s' % stage) | |
168 | ||
169 | ||
105b2b62 | 170 | return ret |
aadf3aa1 JG |
171 | |
172 | def main(): | |
173 | parser = argparse.ArgumentParser() | |
174 | parser.add_argument('stage', nargs='*', default=STAGES, | |
175 | help='One of %s'%STAGES) | |
176 | args = parser.parse_args() | |
177 | ||
178 | # Check validity of stages | |
179 | for s in args.stage: | |
180 | if s not in STAGES: | |
181 | print("Invalid stage '%s' (choose from %s)" % (s, STAGES)) | |
182 | sys.exit(1) | |
183 | ||
184 | # Run the stages | |
185 | passed = True | |
186 | for s in args.stage: | |
187 | passed &= run_stage(s) | |
188 | ||
189 | if not passed: | |
6e98511c | 190 | print("!!! One or more test stages failed !!!") |
aadf3aa1 JG |
191 | sys.exit(1) |
192 | ||
193 | if __name__ == '__main__': | |
194 | main() |