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

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: Fix formatting + comment 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, util
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 ALL_MODULES = object()
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
86 sp.add_argument( 97 sp.add_argument(
87 '--test_list', metavar='FILE', 98 '--test_list', metavar='FILE',
88 help='take the list of test globs from the FILE (use "-" for stdin)' 99 help='take the list of test globs from the FILE (use "-" for stdin)'
89 ).completer = lambda **_: [] 100 ).completer = lambda **_: []
90 101
91 sp.add_argument( 102 sp.add_argument(
92 '--html_report', metavar='DIR', 103 '--html_report', metavar='DIR',
93 help='directory to write html report (default: disabled)' 104 help='directory to write html report (default: disabled)'
94 ).completer = lambda **_: [] 105 ).completer = lambda **_: []
95 106
96 sp.add_argument( 107 sp.add_argument(
97 'test_glob', nargs='*', help=( 108 'test_glob', nargs='*', help=(
98 'glob to filter the tests acted on. If the glob begins with "-" ' 109 '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 ' 110 '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 ' 111 'will be skipped. If a glob doesn\'t have "*" in it, "*" will be '
101 'implicitly appended to the end') 112 'implicitly appended to the end')
102 ).completer = _test_completer(test_gen) 113 ).completer = _test_completer(test_modules)
103 114
104 opts = parser.parse_args(args) 115 opts = parser.parse_args(args)
105 116
106 if not hasattr(opts, 'jobs'): 117 if not hasattr(opts, 'jobs'):
107 opts.jobs = 0 118 opts.jobs = 0
108 elif opts.jobs < 1: 119 elif opts.jobs < 1:
109 parser.error('--jobs was less than 1') 120 parser.error('--jobs was less than 1')
110 121
111 if opts.test_list: 122 if opts.test_list:
112 fh = sys.stdin if opts.test_list == '-' else open(opts.test_list, 'rb') 123 fh = sys.stdin if opts.test_list == '-' else open(opts.test_list, 'rb')
113 with fh as tl: 124 with fh as tl:
114 opts.test_glob += [l.strip() for l in tl.readlines()] 125 opts.test_glob += [l.strip() for l in tl.readlines()]
115 126
116 opts.handler = HANDLERS[opts.mode] 127 opts.handler = HANDLERS[opts.mode]
117 128
118 del opts.test_list 129 del opts.test_list
119 del opts.mode 130 del opts.mode
120 131
121 return opts 132 return opts
122 133
123 134
124 def main(name, test_gen, cover_branches=False, args=None): 135 def get_test_gens(test_modules):
136 test_gens = []
137 if not test_modules or test_modules is ALL_MODULES:
138 # if we're running directly
139 if __name__ == '__main__' or test_modules is ALL_MODULES:
140 test_modules = []
141 for importer, modname, ispkg in pkgutil.walk_packages(path=['.']):
142 if not ispkg and modname.endswith('_test'):
143 if modname in sys.modules:
144 test_modules.append(sys.modules[modname])
145 else:
146 test_modules.append(
147 importer.find_module(modname).load_module(modname))
148 else: # a wrapper main() script
149 test_modules = [sys.modules['__main__']]
150 for mod in test_modules:
151 for obj in mod.__dict__.values():
152 if util.is_test_generator(obj):
153 test_gens.append(obj)
154 elif _is_unittest(obj):
155 test_gens.append(UnittestTestCase(obj))
156 return test_gens
157
158
159 # TODO(iannucci): have Test determine cover_branches
160 def main(cover_branches=False, test_modules=None, args=None):
125 """Entry point for tests using expect_tests. 161 """Entry point for tests using expect_tests.
126 162
127 Example: 163 Example:
128 import expect_tests 164 >>> import expect_tests
129 165
130 def happy_fn(val): 166 >>> def happy_fn(val):
131 # Usually you would return data which is the result of some deterministic 167 >>> # Usually you would return data which is the result of some
132 # computation. 168 >>> #deterministic computation.
133 return expect_tests.Result({'neet': '%s string value' % val}) 169 >>> return expect_tests.Result({'neet': '%s string value' % val})
134 170
135 def Gen(): 171 >>> @expect_tests.test_generator
136 yield expect_tests.Test('happy', happy_fn, args=('happy',)) 172 >>> def gen():
173 >>> yield expect_tests.Test(
174 >>> __package__ + '.happy',
175 >>> expect_tests.FuncCall(happy_fn, 'happy'))
137 176
138 if __name__ == '__main__': 177 >>> if __name__ == '__main__':
139 expect_tests.main('happy_test_suite', Gen) 178 >>> expect_tests.main(cover_branches=True)
140 179
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 180 @param cover_branches: Include branch coverage data (rather than just line
144 coverage) 181 coverage)
182 @param test_modules: Modules containing expect_tests generators and/or
183 unittests. Defaults to the __main__ module, or if
184 this script is invoked directly, all '_test' modules
185 under the current working directory.
145 @param args: Commandline args (starting at argv[1]) 186 @param args: Commandline args (starting at argv[1])
146 """ 187 """
147 try: 188 try:
148 opts = _parse_args(args, test_gen) 189 opts = _parse_args(args, test_modules)
149 190
150 cover_ctx = CoverageContext(name, cover_branches, opts.html_report, 191 cover_ctx = CoverageContext(cover_branches, opts.html_report,
151 not opts.handler.SKIP_RUNLOOP) 192 not opts.handler.SKIP_RUNLOOP)
152 193
194 with cover_ctx.create_subprocess_context():
195 test_gens = get_test_gens(test_modules)
196
153 error, killed = result_loop( 197 error, killed = result_loop(
154 test_gen, cover_ctx.create_subprocess_context(), opts) 198 test_gens, cover_ctx.create_subprocess_context(), opts)
155 199
156 cover_ctx.cleanup() 200 cover_ctx.cleanup()
157 if not killed and not opts.test_glob: 201 if not killed and (opts.force_coverage or not opts.test_glob):
158 if not cover_ctx.report(opts.verbose): 202 if not cover_ctx.report(opts.verbose):
159 sys.exit(2) 203 sys.exit(2)
160 204
161 sys.exit(error or killed) 205 sys.exit(error or killed)
162 except KeyboardInterrupt: 206 except KeyboardInterrupt:
163 pass 207 pass
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698