OLD | NEW |
(Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import argparse |
| 6 import multiprocessing |
| 7 import sys |
| 8 |
| 9 from .cover import CoverageContext |
| 10 |
| 11 from . import handle_list, handle_debug, handle_train, handle_test |
| 12 |
| 13 from .pipeline import result_loop |
| 14 |
| 15 |
| 16 HANDLERS = { |
| 17 'list': handle_list.ListHandler, |
| 18 'debug': handle_debug.DebugHandler, |
| 19 'train': handle_train.TrainHandler, |
| 20 'test': handle_test.TestHandler, |
| 21 } |
| 22 |
| 23 |
| 24 class _test_completer(object): |
| 25 """Implements the argcomplete completer interface for the test_glob command |
| 26 line argument. |
| 27 |
| 28 See: https://pypi.python.org/pypi/argcomplete |
| 29 |
| 30 This is automatically wired up if you have enabled bash completion in the |
| 31 infra repo: https://chromium.googlesource.com/infra/infra |
| 32 """ |
| 33 class FakeOptions(object): |
| 34 def __init__(self, **kwargs): |
| 35 for k, v in kwargs.iteritems(): |
| 36 setattr(self, k, v) |
| 37 |
| 38 def __init__(self, gen): |
| 39 self._gen = gen |
| 40 |
| 41 def __call__(self, prefix, **_): |
| 42 handle_list.ListHandler.COMPLETION_LIST = [] |
| 43 options = self.FakeOptions( |
| 44 handler=handle_list.ListHandler, |
| 45 test_glob=[prefix], |
| 46 jobs=1, |
| 47 ) |
| 48 ctx = CoverageContext('', [], False, False) |
| 49 result_loop(self._gen, ctx.create_subprocess_context(), options) |
| 50 return handle_list.ListHandler.COMPLETION_LIST |
| 51 |
| 52 |
| 53 def _parse_args(args, test_gen): |
| 54 args = args or sys.argv[1:] |
| 55 |
| 56 # Set the default mode if not specified and not passing --help |
| 57 search_names = set(HANDLERS.keys() + ['-h', '--help']) |
| 58 if not any(arg in search_names for arg in args): |
| 59 args.insert(0, 'test') |
| 60 |
| 61 parser = argparse.ArgumentParser() |
| 62 subparsers = parser.add_subparsers( |
| 63 title='Mode (default "test")', dest='mode', |
| 64 help='See `[mode] --help` for more options.') |
| 65 |
| 66 for k, h in HANDLERS.iteritems(): |
| 67 doc = h.__doc__ |
| 68 if doc: |
| 69 doc = doc[0].lower() + doc[1:] |
| 70 sp = subparsers.add_parser(k, help=doc) |
| 71 h.add_options(sp) |
| 72 |
| 73 mg = sp.add_mutually_exclusive_group() |
| 74 mg.add_argument( |
| 75 '--quiet', action='store_true', |
| 76 help='be quiet (only print failures)') |
| 77 mg.add_argument( |
| 78 '--verbose', action='store_true', help='be verbose') |
| 79 |
| 80 if not h.SKIP_RUNLOOP: |
| 81 sp.add_argument( |
| 82 '--jobs', metavar='N', type=int, |
| 83 default=multiprocessing.cpu_count(), |
| 84 help='run N jobs in parallel (default %(default)s)') |
| 85 |
| 86 sp.add_argument( |
| 87 '--test_list', metavar='FILE', |
| 88 help='take the list of test globs from the FILE (use "-" for stdin)' |
| 89 ).completer = lambda **_: [] |
| 90 |
| 91 sp.add_argument( |
| 92 '--html_report', metavar='DIR', |
| 93 help='directory to write html report (default: disabled)' |
| 94 ).completer = lambda **_: [] |
| 95 |
| 96 sp.add_argument( |
| 97 'test_glob', nargs='*', help=( |
| 98 'glob to filter the tests acted on. If the glob begins with "-" ' |
| 99 'then it acts as a negation glob and anything which matches it ' |
| 100 'will be skipped. If a glob doesn\'t have "*" in it, "*" will be ' |
| 101 'implicitly appended to the end') |
| 102 ).completer = _test_completer(test_gen) |
| 103 |
| 104 opts = parser.parse_args(args) |
| 105 |
| 106 if not hasattr(opts, 'jobs'): |
| 107 opts.jobs = 0 |
| 108 elif opts.jobs < 1: |
| 109 parser.error('--jobs was less than 1') |
| 110 |
| 111 if opts.test_list: |
| 112 fh = sys.stdin if opts.test_list == '-' else open(opts.test_list, 'rb') |
| 113 with fh as tl: |
| 114 opts.test_glob += [l.strip() for l in tl.readlines()] |
| 115 |
| 116 opts.handler = HANDLERS[opts.mode] |
| 117 |
| 118 del opts.test_list |
| 119 del opts.mode |
| 120 |
| 121 return opts |
| 122 |
| 123 |
| 124 def main(name, test_gen, cover_branches=False, cover_omit=None, args=None): |
| 125 """Entry point for tests using expect_tests. |
| 126 |
| 127 Example: |
| 128 import expect_tests |
| 129 |
| 130 def happy_fn(val): |
| 131 # Usually you would return data which is the result of some deterministic |
| 132 # computation. |
| 133 return expect_tests.Result({'neet': '%s string value' % val}) |
| 134 |
| 135 def Gen(): |
| 136 yield expect_tests.Test('happy', happy_fn, args=('happy',)) |
| 137 |
| 138 if __name__ == '__main__': |
| 139 expect_tests.main('happy_test_suite', Gen) |
| 140 |
| 141 @param name: Name of the test suite. |
| 142 @param test_gen: A Generator which yields Test objects. |
| 143 @param cover_branches: Include branch coverage data (rather than just line |
| 144 coverage) |
| 145 @param args: Commandline args (starting at argv[1]) |
| 146 """ |
| 147 try: |
| 148 opts = _parse_args(args, test_gen) |
| 149 |
| 150 cover_ctx = CoverageContext(name, cover_branches, opts.html_report, |
| 151 not opts.handler.SKIP_RUNLOOP) |
| 152 |
| 153 error, killed = result_loop( |
| 154 test_gen, cover_ctx.create_subprocess_context(), opts) |
| 155 |
| 156 cover_ctx.cleanup() |
| 157 if not killed and not opts.test_glob: |
| 158 if not cover_ctx.report(verbose=opts.verbose, omit=cover_omit): |
| 159 sys.exit(2) |
| 160 |
| 161 sys.exit(error or killed) |
| 162 except KeyboardInterrupt: |
| 163 pass |
OLD | NEW |