|
| 1 | +#!/usr/bin/env python |
| 2 | +""" |
| 3 | +runtests.py [OPTIONS] [-- ARGS] |
| 4 | +
|
| 5 | +Run tests, building the project first. |
| 6 | +
|
| 7 | +Examples:: |
| 8 | +
|
| 9 | + $ python runtests.py |
| 10 | + $ python runtests.py -t {SAMPLE_TEST} |
| 11 | + $ python runtests.py --ipython |
| 12 | +
|
| 13 | +""" |
| 14 | + |
| 15 | +# |
| 16 | +# This is a generic test runner script for projects using Numpy's test |
| 17 | +# framework. Change the following values to adapt to your project: |
| 18 | +# |
| 19 | + |
| 20 | +PROJECT_MODULE = "control" |
| 21 | +PROJECT_ROOT_FILES = ['setup.py'] |
| 22 | +SAMPLE_TEST = "control/tests/margin_test.py" |
| 23 | + |
| 24 | +# --------------------------------------------------------------------- |
| 25 | + |
| 26 | +__doc__ = __doc__.format(**globals()) |
| 27 | + |
| 28 | +import sys |
| 29 | +import os |
| 30 | + |
| 31 | +# In case we are run from the source directory, we don't want to import the |
| 32 | +# project from there: |
| 33 | +sys.path.pop(0) |
| 34 | + |
| 35 | +import shutil |
| 36 | +import subprocess |
| 37 | +from argparse import ArgumentParser, REMAINDER |
| 38 | + |
| 39 | +def main(argv): |
| 40 | + parser = ArgumentParser(usage=__doc__.lstrip()) |
| 41 | + parser.add_argument("--verbose", "-v", action="count", default=1, |
| 42 | + help="more verbosity") |
| 43 | + parser.add_argument("--no-build", "-n", action="store_true", default=False, |
| 44 | + help="do not build the project (use system installed version)") |
| 45 | + parser.add_argument("--build-only", "-b", action="store_true", default=False, |
| 46 | + help="just build, do not run any tests") |
| 47 | + parser.add_argument("--doctests", action="store_true", default=False, |
| 48 | + help="Run doctests in module") |
| 49 | + parser.add_argument("--coverage", action="store_true", default=False, |
| 50 | + help=("report coverage of project code. HTML output goes " |
| 51 | + "under build/coverage")) |
| 52 | + parser.add_argument("--mode", "-m", default="fast", |
| 53 | + help="'fast', 'full', or something that could be " |
| 54 | + "passed to nosetests -A [default: fast]") |
| 55 | + parser.add_argument("--submodule", "-s", default=None, |
| 56 | + help="Submodule whose tests to run (cluster, constants, ...)") |
| 57 | + parser.add_argument("--pythonpath", "-p", default=None, |
| 58 | + help="Paths to prepend to PYTHONPATH") |
| 59 | + parser.add_argument("--tests", "-t", action='append', |
| 60 | + help="Specify tests to run") |
| 61 | + parser.add_argument("--python", action="store_true", |
| 62 | + help="Start a Python shell with PYTHONPATH set") |
| 63 | + parser.add_argument("--ipython", "-i", action="store_true", |
| 64 | + help="Start IPython shell with PYTHONPATH set") |
| 65 | + parser.add_argument("--shell", action="store_true", |
| 66 | + help="Start Unix shell with PYTHONPATH set") |
| 67 | + parser.add_argument("--debug", "-g", action="store_true", |
| 68 | + help="Debug build") |
| 69 | + parser.add_argument("args", metavar="ARGS", default=[], nargs=REMAINDER, |
| 70 | + help="Arguments to pass to Nose") |
| 71 | + args = parser.parse_args(argv) |
| 72 | + |
| 73 | + if args.pythonpath: |
| 74 | + for p in reversed(args.pythonpath.split(os.pathsep)): |
| 75 | + sys.path.insert(0, p) |
| 76 | + |
| 77 | + if not args.no_build: |
| 78 | + site_dir = build_project(args) |
| 79 | + sys.path.insert(0, site_dir) |
| 80 | + os.environ['PYTHONPATH'] = site_dir |
| 81 | + |
| 82 | + if args.python: |
| 83 | + import code |
| 84 | + code.interact() |
| 85 | + sys.exit(0) |
| 86 | + |
| 87 | + if args.ipython: |
| 88 | + import IPython |
| 89 | + IPython.embed() |
| 90 | + sys.exit(0) |
| 91 | + |
| 92 | + if args.shell: |
| 93 | + shell = os.environ.get('SHELL', 'sh') |
| 94 | + print("Spawning a Unix shell...") |
| 95 | + os.execv(shell, [shell]) |
| 96 | + sys.exit(1) |
| 97 | + |
| 98 | + extra_argv = args.args |
| 99 | + |
| 100 | + if args.coverage: |
| 101 | + dst_dir = os.path.join('build', 'coverage') |
| 102 | + fn = os.path.join(dst_dir, 'coverage_html.js') |
| 103 | + if os.path.isdir(dst_dir) and os.path.isfile(fn): |
| 104 | + shutil.rmtree(dst_dir) |
| 105 | + extra_argv += ['--cover-html', |
| 106 | + '--cover-html-dir='+dst_dir] |
| 107 | + |
| 108 | + if args.build_only: |
| 109 | + sys.exit(0) |
| 110 | + elif args.submodule: |
| 111 | + modname = PROJECT_MODULE + '.' + args.submodule |
| 112 | + try: |
| 113 | + __import__(modname) |
| 114 | + test = sys.modules[modname].test |
| 115 | + except (ImportError, KeyError, AttributeError): |
| 116 | + print("Cannot run tests for %s" % modname) |
| 117 | + sys.exit(2) |
| 118 | + elif args.tests: |
| 119 | + def test(*a, **kw): |
| 120 | + extra_argv = kw.pop('extra_argv', ()) |
| 121 | + extra_argv = extra_argv + args.tests[1:] |
| 122 | + kw['extra_argv'] = extra_argv |
| 123 | + from numpy.testing import Tester |
| 124 | + return Tester(args.tests[0]).test(*a, **kw) |
| 125 | + else: |
| 126 | + __import__(PROJECT_MODULE) |
| 127 | + test = sys.modules[PROJECT_MODULE].test |
| 128 | + |
| 129 | + result = test(args.mode, |
| 130 | + verbose=args.verbose, |
| 131 | + extra_argv=args.args, |
| 132 | + doctests=args.doctests, |
| 133 | + coverage=args.coverage) |
| 134 | + |
| 135 | + if result.wasSuccessful(): |
| 136 | + sys.exit(0) |
| 137 | + else: |
| 138 | + sys.exit(1) |
| 139 | + |
| 140 | +def build_project(args): |
| 141 | + """ |
| 142 | + Build a dev version of the project. |
| 143 | +
|
| 144 | + Returns |
| 145 | + ------- |
| 146 | + site_dir |
| 147 | + site-packages directory where it was installed |
| 148 | +
|
| 149 | + """ |
| 150 | + |
| 151 | + root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__))) |
| 152 | + root_ok = [os.path.exists(os.path.join(root_dir, fn)) |
| 153 | + for fn in PROJECT_ROOT_FILES] |
| 154 | + if not all(root_ok): |
| 155 | + print("To build the project, run runtests.py in " |
| 156 | + "git checkout or unpacked source") |
| 157 | + sys.exit(1) |
| 158 | + |
| 159 | + dst_dir = os.path.join(root_dir, 'build', 'testenv') |
| 160 | + |
| 161 | + from distutils.sysconfig import get_python_lib |
| 162 | + site_dir = get_python_lib(prefix=dst_dir) |
| 163 | + |
| 164 | + env = dict(os.environ) |
| 165 | + cmd = [sys.executable, 'setup.py'] |
| 166 | + |
| 167 | + # Always use ccache if available |
| 168 | + env['PATH'] = os.pathsep.join(['/usr/lib/ccache'] |
| 169 | + + env.get('PATH', '').split(os.pathsep)) |
| 170 | + |
| 171 | + if args.debug: |
| 172 | + # assume everyone uses gcc/gfortran |
| 173 | + env['OPT'] = '-O0 -ggdb' |
| 174 | + env['FOPT'] = '-O0 -ggdb' |
| 175 | + cmd += ["build", "--debug"] |
| 176 | + |
| 177 | + cmd += ['install', '--prefix=' + dst_dir] |
| 178 | + |
| 179 | + # Setup for setuptools |
| 180 | + cmd += ['--single-version-externally-managed', |
| 181 | + '--record=' + os.path.join(dst_dir, 'record.lst')] |
| 182 | + if not os.path.isdir(site_dir): |
| 183 | + os.makedirs(site_dir) |
| 184 | + env['PYTHONPATH'] = os.pathsep.join([site_dir] |
| 185 | + + env.get('PATH', '').split(os.pathsep)) |
| 186 | + |
| 187 | + # Build it. |
| 188 | + print("Building, see build.log...") |
| 189 | + with open('build.log', 'w') as log: |
| 190 | + ret = subprocess.call(cmd, env=env, stdout=log, stderr=log, |
| 191 | + cwd=root_dir) |
| 192 | + |
| 193 | + if ret == 0: |
| 194 | + print("Build OK") |
| 195 | + else: |
| 196 | + with open('build.log', 'r') as f: |
| 197 | + print(f.read()) |
| 198 | + print("Build failed!") |
| 199 | + sys.exit(1) |
| 200 | + |
| 201 | + return site_dir |
| 202 | + |
| 203 | +if __name__ == "__main__": |
| 204 | + main(argv=sys.argv[1:]) |
0 commit comments