OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Tests generated CIPD packages. | 6 """Tests generated CIPD packages. |
7 | 7 |
8 Supposed to be invoked after build.py has run. Uses packages from out/*.cipd and | 8 Supposed to be invoked after build.py has run. Uses packages from out/*.cipd and |
9 tests from tests/*.py. | 9 tests from tests/*.py. |
10 | 10 |
11 Assumes cipd client is built in ../go/bin/cipd (true after build.py has run). | 11 Assumes cipd client is built in out/.cipd_client/cipd_* (true after build.py has |
12 run). | |
12 """ | 13 """ |
13 | 14 |
14 import argparse | 15 import argparse |
15 import glob | 16 import glob |
16 import os | 17 import os |
17 import re | 18 import re |
18 import shutil | 19 import shutil |
19 import subprocess | 20 import subprocess |
20 import sys | 21 import sys |
21 import tempfile | 22 import tempfile |
(...skipping 23 matching lines...) Expand all Loading... | |
45 def get_docstring(test_script): | 46 def get_docstring(test_script): |
46 """Hacky way to grab a first line of a module docstring using regexps.""" | 47 """Hacky way to grab a first line of a module docstring using regexps.""" |
47 with open(test_script, 'rt') as f: | 48 with open(test_script, 'rt') as f: |
48 text = f.read() | 49 text = f.read() |
49 m = re.match(r'^.*"""(.*?)"""', text, re.DOTALL) | 50 m = re.match(r'^.*"""(.*?)"""', text, re.DOTALL) |
50 if not m: | 51 if not m: |
51 return None | 52 return None |
52 return m.group(1).strip().splitlines()[0] | 53 return m.group(1).strip().splitlines()[0] |
53 | 54 |
54 | 55 |
56 def find_cipd_client(out_dir): | |
57 """Returns path to cipd client built by build.py. | |
58 | |
59 See build_cipd_client in build.py. It puts cipd client into | |
60 '<out_dir>/.cipd_client/cipd_<digest>' and there's only one such file there. | |
61 | |
62 Prints error message and returns None if the file cannot be found. | |
63 """ | |
64 out_dir = os.path.join(out_dir, '.cipd_client') | |
65 files = [f for f in os.listdir(out_dir) if f.startswith('cipd_')] | |
66 if not files: | |
67 print >> sys.stderr, 'Cannot find CIPD client in %s' % out_dir | |
68 return None | |
69 if len(files) != 1: | |
70 print >> sys.stderr, ( | |
71 'There should be only one cipd client binary in %s, found %s' % | |
72 (out_dir, files)) | |
73 return None | |
74 cipd_client = os.path.join(out_dir, files[0]) | |
75 if not os.access(cipd_client, os.X_OK): | |
76 print >> sys.stderr, 'CIPD client at %s is not runnable' | |
77 return None | |
78 return cipd_client | |
79 | |
80 | |
55 def run_test(cipd_client, package, work_dir, test_script): | 81 def run_test(cipd_client, package, work_dir, test_script): |
56 """Extracts a package to a dir and runs test_script with cwd == work_dir.""" | 82 """Extracts a package to a dir and runs test_script with cwd == work_dir.""" |
57 print_title('Deploying %s' % os.path.basename(package)) | 83 print_title('Deploying %s' % os.path.basename(package)) |
58 if not os.access(cipd_client, os.X_OK): | |
59 print >> sys.stderr, ( | |
60 'CIPD client at %s doesn\'t exist or not runnable. Run build.py to ' | |
61 'build it.' % cipd_client) | |
62 return 1 | |
63 cmd_line = ['cipd', 'pkg-deploy', '-root', work_dir, package] | 84 cmd_line = ['cipd', 'pkg-deploy', '-root', work_dir, package] |
64 print ' '.join(cmd_line) | 85 print ' '.join(cmd_line) |
65 if subprocess.call(args=cmd_line, executable=cipd_client): | 86 if subprocess.call(args=cmd_line, executable=cipd_client): |
66 raise TestException('Failed to install %s, see logs' % package) | 87 raise TestException('Failed to install %s, see logs' % package) |
67 | 88 |
68 print_title(get_docstring(test_script) or 'Running tests...') | 89 print_title(get_docstring(test_script) or 'Running tests...') |
69 cmd_line = ['python', test_script] | 90 cmd_line = ['python', test_script] |
70 print '%s in %s' % (' '.join(cmd_line), work_dir) | 91 print '%s in %s' % (' '.join(cmd_line), work_dir) |
71 env = os.environ.copy() | 92 env = os.environ.copy() |
72 env.pop('PYTHONPATH', None) | 93 env.pop('PYTHONPATH', None) |
73 ret = subprocess.call( | 94 ret = subprocess.call( |
74 args=cmd_line, executable=sys.executable, env=env, cwd=work_dir) | 95 args=cmd_line, executable=sys.executable, env=env, cwd=work_dir) |
75 if ret: | 96 if ret: |
76 raise TestException('Non zero exit code (%d)' % ret) | 97 raise TestException('Non zero exit code (%d)' % ret) |
77 | 98 |
78 | 99 |
79 def run( | 100 def run( |
80 cipd_client, | |
81 package_out_dir, | 101 package_out_dir, |
82 package_tests_dir, | 102 package_tests_dir, |
83 work_dir, | 103 work_dir, |
84 packages): | 104 packages): |
85 """Deployes build *.cipd package locally and runs tests against them. | 105 """Deployes build *.cipd package locally and runs tests against them. |
86 | 106 |
87 Used to verify the packaged code works when installed as CIPD package, it is | 107 Used to verify the packaged code works when installed as CIPD package, it is |
88 important for infra_python package that has non-trivial structure. | 108 important for infra_python package that has non-trivial structure. |
89 | 109 |
90 Args: | 110 Args: |
91 cipd_client: path to cipd client executable. | |
92 package_out_dir: where to search for built packages. | 111 package_out_dir: where to search for built packages. |
93 work_dir: where to install/update packages into. | 112 work_dir: where to install/update packages into. |
94 packages: names of *.cipd files in package_out_dir or [] for all. | 113 packages: names of *.cipd files in package_out_dir or [] for all. |
95 | 114 |
96 Returns: | 115 Returns: |
97 0 on success, 1 or error. | 116 0 on success, 1 or error. |
98 """ | 117 """ |
99 # Discover what to test. | 118 # Discover what to test. |
100 paths = [] | 119 paths = [] |
101 if not packages: | 120 if not packages: |
102 paths = glob.glob(os.path.join(package_out_dir, '*.cipd')) | 121 # Enumerate all known tests in tests/*.py and filter them based on |
Vadim Sh.
2016/06/25 03:51:04
this method no longer works, since package_out_dir
| |
122 # availability of corresponding *.cipd package in package_out_dir. It will | |
123 # skip any cross-compiled packages, since they have additional '+<platform>' | |
124 # suffix in the package file name. | |
125 for test in os.listdir(package_tests_dir): | |
126 if not test.endswith('.py'): | |
127 continue | |
128 pkg_file = os.path.join( | |
129 package_out_dir, os.path.splitext(test)[0] + '.cipd') | |
130 if os.path.exists(pkg_file): | |
131 paths.append(pkg_file) | |
103 else: | 132 else: |
104 for name in packages: | 133 for name in packages: |
105 abs_path = os.path.join(package_out_dir, name) | 134 abs_path = os.path.join(package_out_dir, name) |
106 if not os.path.isfile(abs_path): | 135 if not os.path.isfile(abs_path): |
107 raise TestException('No such package file: %s' % name) | 136 raise TestException('No such package file: %s' % name) |
108 paths.append(abs_path) | 137 paths.append(abs_path) |
109 paths = sorted(paths) | 138 paths = sorted(paths) |
110 if not paths: | 139 if not paths: |
111 print 'Nothing to test.' | 140 print 'Nothing to test.' |
112 return 0 | 141 return 0 |
113 | 142 |
114 # Run all tests sequentially. There're like 2 of them tops. | 143 cipd_client = find_cipd_client(package_out_dir) |
144 if not cipd_client: | |
145 return 1 | |
146 | |
147 # Run all tests sequentially. Most of the are extra fast. | |
115 nuke_temp = False | 148 nuke_temp = False |
116 if not work_dir: | 149 if not work_dir: |
117 work_dir = tempfile.mkdtemp(suffix='cipd_test') | 150 work_dir = tempfile.mkdtemp(suffix='cipd_test') |
118 nuke_temp = True | 151 nuke_temp = True |
119 work_dir = os.path.abspath(work_dir) | 152 work_dir = os.path.abspath(work_dir) |
120 try: | 153 try: |
121 fail = False | 154 fail = False |
122 for path in paths: | 155 for path in paths: |
123 name = os.path.splitext(os.path.basename(path))[0] | 156 name = os.path.splitext(os.path.basename(path))[0] |
124 test_script = os.path.join(package_tests_dir, '%s.py' % name) | 157 test_script = os.path.join(package_tests_dir, '%s.py' % name) |
(...skipping 17 matching lines...) Expand all Loading... | |
142 finally: | 175 finally: |
143 if nuke_temp: | 176 if nuke_temp: |
144 try: | 177 try: |
145 shutil.rmtree(work_dir, ignore_errors=True) | 178 shutil.rmtree(work_dir, ignore_errors=True) |
146 except OSError as exc: | 179 except OSError as exc: |
147 print >> sys.stderr, 'Failed to delete %s: %s' % (work_dir, exc) | 180 print >> sys.stderr, 'Failed to delete %s: %s' % (work_dir, exc) |
148 | 181 |
149 | 182 |
150 def main( | 183 def main( |
151 args, | 184 args, |
152 go_workspace=os.path.join(ROOT, 'go'), | |
153 package_out_dir=os.path.join(ROOT, 'build', 'out'), | 185 package_out_dir=os.path.join(ROOT, 'build', 'out'), |
154 package_tests_dir=os.path.join(ROOT, 'build', 'tests')): | 186 package_tests_dir=os.path.join(ROOT, 'build', 'tests')): |
155 parser = argparse.ArgumentParser(description='Tests infra CIPD packages') | 187 parser = argparse.ArgumentParser(description='Tests infra CIPD packages') |
156 parser.add_argument( | 188 parser.add_argument( |
157 'packages', metavar='NAME', type=str, nargs='*', | 189 'packages', metavar='NAME', type=str, nargs='*', |
158 help='name of a build package file in build/out/* to deploy and test') | 190 help='name of a built package file in build/out/* to deploy and test') |
159 parser.add_argument( | 191 parser.add_argument( |
160 '--work-dir', metavar='DIR', dest='work_dir', | 192 '--work-dir', metavar='DIR', dest='work_dir', |
161 help='directory to deploy packages into (temporary dir by default)') | 193 help='directory to deploy packages into (temporary dir by default)') |
162 args = parser.parse_args(args) | 194 args = parser.parse_args(args) |
163 return run( | 195 return run( |
164 os.path.join(go_workspace, 'bin', 'cipd' + EXE_SUFFIX), | |
165 package_out_dir, | 196 package_out_dir, |
166 package_tests_dir, | 197 package_tests_dir, |
167 args.work_dir, | 198 args.work_dir, |
168 [n + '.cipd' if not n.endswith('.cipd') else n for n in args.packages]) | 199 [n + '.cipd' if not n.endswith('.cipd') else n for n in args.packages]) |
169 | 200 |
170 | 201 |
171 if __name__ == '__main__': | 202 if __name__ == '__main__': |
172 sys.exit(main(sys.argv[1:])) | 203 sys.exit(main(sys.argv[1:])) |
OLD | NEW |