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