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

Side by Side Diff: scripts/slave/unittests/recipe_configs_test.py

Issue 1151423002: Move recipe engine to third_party/recipe_engine. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved field_composer_test with its buddies Created 5 years, 6 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 #!/usr/bin/python 1 #!/usr/bin/env python
2 # Copyright 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Provides test coverage for common recipe configurations.
7
8 recipe config expectations are located in ../recipe_configs_test/*.expected
9
10 In training mode, this will loop over every config item in ../recipe_configs.py
11 crossed with every platform, and spit out the as_json() representation to
12 ../recipe_configs_test
13
14 You must have 100% coverage of ../recipe_configs.py for this test to pass.
15 """
16
17 import argparse
18 import multiprocessing
19 import os 6 import os
20 import sys 7 import sys
21 import traceback
22 from itertools import product, imap
23 8
24 import test_env # "relative import" pylint: disable=W0403,W0611 9 import test_env # pylint: disable=W0403,W0611
25 10
26 from slave import recipe_loader 11 from recipe_engine import configs_test
27 from slave import recipe_util 12 from slave import recipe_universe
28
29 import coverage
30
31 SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
32 SLAVE_DIR = os.path.abspath(os.path.join(SCRIPT_PATH, os.pardir))
33
34 COVERAGE = (lambda: coverage.coverage(
35 include=[os.path.join(x, '*', '*config.py')
36 for x in recipe_util.MODULE_DIRS()],
37 data_file='.recipe_configs_test_coverage', data_suffix=True))()
38
39 def covered(fn, *args, **kwargs):
40 COVERAGE.start()
41 try:
42 return fn(*args, **kwargs)
43 finally:
44 COVERAGE.stop()
45
46 UNIVERSE = recipe_loader.RecipeUniverse()
47
48 def load_recipe_modules():
49 modules = {}
50 for modpath in recipe_loader.loop_over_recipe_modules():
51 # That's right, we're using the path as the local name! The local
52 # name really could be anything unique, we don't use it.
53 modules[modpath] = UNIVERSE.load(recipe_loader.PathDependency(
54 modpath, local_name=modpath, base_path=os.curdir))
55 return modules
56
57
58 RECIPE_MODULES = None
59 def init_recipe_modules():
60 global RECIPE_MODULES
61 RECIPE_MODULES = covered(load_recipe_modules)
62
63 from slave import recipe_config # pylint: disable=F0401
64
65
66 def evaluate_configurations(args):
67 mod_id, var_assignments = args
68 mod = RECIPE_MODULES[mod_id]
69 ctx = mod.CONFIG_CTX
70
71 config_name = None
72 try:
73 make_item = lambda: covered(ctx.CONFIG_SCHEMA, **var_assignments)
74
75 # Try ROOT_CONFIG_ITEM first. If it raises BadConf, then we can skip
76 # this config.
77 root_item = ctx.ROOT_CONFIG_ITEM
78 if root_item:
79 config_name = root_item.__name__
80 try:
81 result = covered(root_item, make_item())
82 if result.complete():
83 covered(result.as_jsonish)
84 except recipe_config.BadConf, e:
85 pass # This is a possibly expected failure mode.
86
87 for config_name, fn in ctx.CONFIG_ITEMS.iteritems():
88 if fn.NO_TEST or fn.IS_ROOT:
89 continue
90 try:
91 result = covered(fn, make_item())
92 if result.complete():
93 covered(result.as_jsonish)
94 except recipe_config.BadConf:
95 pass # This is a possibly expected failure mode.
96 return True
97 except Exception as e:
98 print ('Caught unknown exception [%s] for config name [%s] for module '
99 '[%s] with args %s') % (e, config_name, mod_id, var_assignments)
100 traceback.print_exc()
101 return False
102
103
104 def multiprocessing_init():
105 # HACK: multiprocessing doesn't work with atexit, so shim the exit functions
106 # instead. This allows us to save exactly one coverage file per subprocess.
107 # pylint: disable=W0212
108 real_os_exit = multiprocessing.forking.exit
109 def exitfn(code):
110 COVERAGE.save()
111 real_os_exit(code)
112 multiprocessing.forking.exit = exitfn
113
114 # This check mirrors the logic in multiprocessing.forking.exit
115 if sys.platform != 'win32':
116 # Even though multiprocessing.forking.exit is defined, it's not used in the
117 # non-win32 version of multiprocessing.forking.Popen... *loss for words*
118 os._exit = exitfn
119
120
121 def coverage_parallel_map(fn):
122 combination_generator = (
123 (mod_id, var_assignments)
124 for mod_id, mod in RECIPE_MODULES.iteritems()
125 if mod.CONFIG_CTX
126 for var_assignments in imap(dict, product(*[
127 [(key_name, val) for val in vals]
128 for key_name, vals in mod.CONFIG_CTX.VAR_TEST_MAP.iteritems()
129 ]))
130 )
131
132 pool = multiprocessing.Pool(initializer=multiprocessing_init)
133 try:
134 return pool.map_async(fn, combination_generator).get(999999)
135 finally:
136 # necessary so that the subprocesses will write out their coverage due to
137 # the hack in multiprocessing_init()
138 pool.close()
139 pool.join()
140
141
142 def main():
143 COVERAGE.erase()
144 init_recipe_modules()
145
146 success = all(coverage_parallel_map(evaluate_configurations))
147
148 COVERAGE.combine()
149 total_covered = COVERAGE.report()
150 all_covered = total_covered == 100.0
151
152 if not success:
153 print 'FATAL: Some recipe configuration(s) failed'
154 if not all_covered:
155 print 'FATAL: Recipes configs are not at 100% coverage.'
156
157 return 1 if (not success or not all_covered) else 0
158
159 13
160 if __name__ == '__main__': 14 if __name__ == '__main__':
161 sys.exit(main()) 15 configs_test.main(recipe_universe.get_universe())
OLDNEW
« no previous file with comments | « scripts/slave/unittests/field_composer_test.py ('k') | scripts/slave/unittests/recipe_lint_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698