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 |