Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1557)

Side by Side Diff: scripts/slave/unittests/expect_tests/main.py

Issue 354913003: Add module discovery and autoloading to expect_tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: add --force_coverage option Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import argparse 5 import argparse
6 import multiprocessing 6 import multiprocessing
7 import pkgutil
7 import sys 8 import sys
8 9
9 from .cover import CoverageContext 10 from .cover import CoverageContext
10 11
11 from . import handle_list, handle_debug, handle_train, handle_test 12 from . import handle_list, handle_debug, handle_train, handle_test
12 13
13 from .pipeline import result_loop 14 from .pipeline import result_loop
14 15
16 from .unittest_helper import _is_unittest, UnittestTestCase
17
18
19 EVERYTHING = object()
Vadim Sh. 2014/06/27 21:40:08 It's a not very descriptive name. every what? ALL
iannucci 2014/06/28 16:22:17 Done.
20
15 21
16 HANDLERS = { 22 HANDLERS = {
17 'list': handle_list.ListHandler, 23 'list': handle_list.ListHandler,
18 'debug': handle_debug.DebugHandler, 24 'debug': handle_debug.DebugHandler,
19 'train': handle_train.TrainHandler, 25 'train': handle_train.TrainHandler,
20 'test': handle_test.TestHandler, 26 'test': handle_test.TestHandler,
21 } 27 }
22 28
23 29
24 class _test_completer(object): 30 class _test_completer(object):
25 """Implements the argcomplete completer interface for the test_glob command 31 """Implements the argcomplete completer interface for the test_glob command
26 line argument. 32 line argument.
27 33
28 See: https://pypi.python.org/pypi/argcomplete 34 See: https://pypi.python.org/pypi/argcomplete
29 35
30 This is automatically wired up if you have enabled bash completion in the 36 This is automatically wired up if you have enabled bash completion in the
31 infra repo: https://chromium.googlesource.com/infra/infra 37 infra repo: https://chromium.googlesource.com/infra/infra
32 """ 38 """
33 class FakeOptions(object): 39 class FakeOptions(object):
34 def __init__(self, **kwargs): 40 def __init__(self, **kwargs):
35 for k, v in kwargs.iteritems(): 41 for k, v in kwargs.iteritems():
36 setattr(self, k, v) 42 setattr(self, k, v)
37 43
38 def __init__(self, gen): 44 def __init__(self, test_modules):
39 self._gen = gen 45 self._test_modules = test_modules
40 46
41 def __call__(self, prefix, **_): 47 def __call__(self, prefix, **_):
42 handle_list.ListHandler.COMPLETION_LIST = [] 48 handle_list.ListHandler.COMPLETION_LIST = []
43 options = self.FakeOptions( 49 options = self.FakeOptions(
44 handler=handle_list.ListHandler, 50 handler=handle_list.ListHandler,
45 test_glob=[prefix], 51 test_glob=[prefix],
46 jobs=1, 52 jobs=1,
47 ) 53 )
48 ctx = CoverageContext('', [], [], False, None, None, False) 54 ctx = CoverageContext(False, False, False)
49 result_loop(self._gen, ctx.create_subprocess_context(), options) 55 test_gens = get_test_gens(self._test_modules)
56 result_loop(test_gens, ctx.create_subprocess_context(), options)
50 return handle_list.ListHandler.COMPLETION_LIST 57 return handle_list.ListHandler.COMPLETION_LIST
51 58
52 59
53 def _parse_args(args, test_gen): 60 def _parse_args(args, test_modules):
54 args = args or sys.argv[1:] 61 args = args or sys.argv[1:]
55 62
56 # Set the default mode if not specified and not passing --help 63 # Set the default mode if not specified and not passing --help
57 search_names = set(HANDLERS.keys() + ['-h', '--help']) 64 search_names = set(HANDLERS.keys() + ['-h', '--help'])
58 if not any(arg in search_names for arg in args): 65 if not any(arg in search_names for arg in args):
59 args.insert(0, 'test') 66 args.insert(0, 'test')
60 67
61 parser = argparse.ArgumentParser() 68 parser = argparse.ArgumentParser()
62 subparsers = parser.add_subparsers( 69 subparsers = parser.add_subparsers(
63 title='Mode (default "test")', dest='mode', 70 title='Mode (default "test")', dest='mode',
(...skipping 12 matching lines...) Expand all
76 help='be quiet (only print failures)') 83 help='be quiet (only print failures)')
77 mg.add_argument( 84 mg.add_argument(
78 '--verbose', action='store_true', help='be verbose') 85 '--verbose', action='store_true', help='be verbose')
79 86
80 if not h.SKIP_RUNLOOP: 87 if not h.SKIP_RUNLOOP:
81 sp.add_argument( 88 sp.add_argument(
82 '--jobs', metavar='N', type=int, 89 '--jobs', metavar='N', type=int,
83 default=multiprocessing.cpu_count(), 90 default=multiprocessing.cpu_count(),
84 help='run N jobs in parallel (default %(default)s)') 91 help='run N jobs in parallel (default %(default)s)')
85 92
93 sp.add_argument(
94 '--force_coverage', action='store_true',
95 help='Enable coverage report even when specifying a test filter.'
96 )
Vadim Sh. 2014/06/27 21:40:08 nit: keep ) on previous line or move ) on line 91
iannucci 2014/06/28 16:22:17 Done.
97
86 sp.add_argument( 98 sp.add_argument(
87 '--test_list', metavar='FILE', 99 '--test_list', metavar='FILE',
88 help='take the list of test globs from the FILE (use "-" for stdin)' 100 help='take the list of test globs from the FILE (use "-" for stdin)'
89 ).completer = lambda **_: [] 101 ).completer = lambda **_: []
90 102
91 sp.add_argument( 103 sp.add_argument(
92 '--html_report', metavar='DIR', 104 '--html_report', metavar='DIR',
93 help='directory to write html report (default: disabled)' 105 help='directory to write html report (default: disabled)'
94 ).completer = lambda **_: [] 106 ).completer = lambda **_: []
95 107
96 sp.add_argument( 108 sp.add_argument(
97 'test_glob', nargs='*', help=( 109 'test_glob', nargs='*', help=(
98 'glob to filter the tests acted on. If the glob begins with "-" ' 110 '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 ' 111 '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 ' 112 'will be skipped. If a glob doesn\'t have "*" in it, "*" will be '
101 'implicitly appended to the end') 113 'implicitly appended to the end')
102 ).completer = _test_completer(test_gen) 114 ).completer = _test_completer(test_modules)
103 115
104 opts = parser.parse_args(args) 116 opts = parser.parse_args(args)
105 117
106 if not hasattr(opts, 'jobs'): 118 if not hasattr(opts, 'jobs'):
107 opts.jobs = 0 119 opts.jobs = 0
108 elif opts.jobs < 1: 120 elif opts.jobs < 1:
109 parser.error('--jobs was less than 1') 121 parser.error('--jobs was less than 1')
110 122
111 if opts.test_list: 123 if opts.test_list:
112 fh = sys.stdin if opts.test_list == '-' else open(opts.test_list, 'rb') 124 fh = sys.stdin if opts.test_list == '-' else open(opts.test_list, 'rb')
113 with fh as tl: 125 with fh as tl:
114 opts.test_glob += [l.strip() for l in tl.readlines()] 126 opts.test_glob += [l.strip() for l in tl.readlines()]
115 127
116 opts.handler = HANDLERS[opts.mode] 128 opts.handler = HANDLERS[opts.mode]
117 129
118 del opts.test_list 130 del opts.test_list
119 del opts.mode 131 del opts.mode
120 132
121 return opts 133 return opts
122 134
123 135
124 def main(name, test_gen, cover_branches=False, args=None): 136 def get_test_gens(test_modules):
137 test_gens = []
138 if not test_modules or test_modules is EVERYTHING:
139 # if we're running directly
140 if __name__ == '__main__' or test_modules is EVERYTHING:
141 test_modules = []
142 for importer, modname, ispkg in pkgutil.walk_packages(path=['.']):
143 if not ispkg and modname.endswith('_test'):
144 if modname in sys.modules:
145 test_modules.append(sys.modules[modname])
146 else:
147 test_modules.append(
148 importer.find_module(modname).load_module(modname))
149 else: # a wrapper main() script
150 test_modules = [sys.modules['__main__']]
151 for mod in test_modules:
152 for obj in mod.__dict__.values():
153 if getattr(obj, '_expect_tests_generator', False):
154 test_gens.append(obj)
155 elif _is_unittest(obj):
156 test_gens.append(UnittestTestCase(obj))
157 return test_gens
158
159
160 # TODO(iannucci): have Test determine cover_branches
161 def main(cover_branches=False, test_modules=None, args=None):
125 """Entry point for tests using expect_tests. 162 """Entry point for tests using expect_tests.
126 163
127 Example: 164 Example:
128 import expect_tests 165 import expect_tests
129 166
130 def happy_fn(val): 167 def happy_fn(val):
131 # Usually you would return data which is the result of some deterministic 168 # Usually you would return data which is the result of some deterministic
132 # computation. 169 # computation.
133 return expect_tests.Result({'neet': '%s string value' % val}) 170 return expect_tests.Result({'neet': '%s string value' % val})
134 171
135 def Gen(): 172 def Gen():
Vadim Sh. 2014/06/27 21:40:08 You seemed to converge on convention to use Capita
iannucci 2014/06/28 16:22:17 I think it's just inconsistent. The idea was Camel
pgervais 2014/06/30 13:56:03 Don't we have a style guide for this? I really don
Vadim Sh. 2014/06/30 17:15:46 +1 to keeping single naming convention for public
136 yield expect_tests.Test('happy', happy_fn, args=('happy',)) 173 yield expect_tests.Test('happy', happy_fn, args=('happy',))
137 174
138 if __name__ == '__main__': 175 if __name__ == '__main__':
139 expect_tests.main('happy_test_suite', Gen) 176 expect_tests.main('happy_test_suite', Gen)
140 177
141 @param name: Name of the test suite. 178 @param test_modules: Modules containing expect_tests generators and/or
142 @param test_gen: A Generator which yields Test objects. 179 unittests. Defaults to the __main__ module, or if
180 this script is invoked directly, all '_test' modules
181 under the current working directory.
143 @param cover_branches: Include branch coverage data (rather than just line 182 @param cover_branches: Include branch coverage data (rather than just line
144 coverage) 183 coverage)
145 @param args: Commandline args (starting at argv[1]) 184 @param args: Commandline args (starting at argv[1])
146 """ 185 """
147 try: 186 try:
148 opts = _parse_args(args, test_gen) 187 opts = _parse_args(args, test_modules)
149 188
150 cover_ctx = CoverageContext(name, cover_branches, opts.html_report, 189 cover_ctx = CoverageContext(cover_branches, opts.html_report,
151 not opts.handler.SKIP_RUNLOOP) 190 not opts.handler.SKIP_RUNLOOP)
152 191
192 with cover_ctx.create_subprocess_context():
193 test_gens = get_test_gens(test_modules)
194
153 error, killed = result_loop( 195 error, killed = result_loop(
154 test_gen, cover_ctx.create_subprocess_context(), opts) 196 test_gens, cover_ctx.create_subprocess_context(), opts)
155 197
156 cover_ctx.cleanup() 198 cover_ctx.cleanup()
157 if not killed and not opts.test_glob: 199 if not killed and (opts.force_coverage or not opts.test_glob):
158 if not cover_ctx.report(opts.verbose): 200 if not cover_ctx.report(opts.verbose):
159 sys.exit(2) 201 sys.exit(2)
160 202
161 sys.exit(error or killed) 203 sys.exit(error or killed)
162 except KeyboardInterrupt: 204 except KeyboardInterrupt:
163 pass 205 pass
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698