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

Side by Side Diff: scripts/slave/recipe_modules/test_utils/api.py

Issue 313693003: Swarming: conditionally run tests on swarming in chromium_trybot recipe. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 6 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 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 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 from slave import recipe_api 5 from slave import recipe_api
6 6
7 class TestUtilsApi(recipe_api.RecipeApi): 7 class TestUtilsApi(recipe_api.RecipeApi):
8 @staticmethod 8 @staticmethod
9 def format_step_text(data): 9 def format_step_text(data):
10 """ 10 """
(...skipping 22 matching lines...) Expand all
33 else: # pragma: no cover 33 else: # pragma: no cover
34 raise ValueError( 34 raise ValueError(
35 'Expected a one or two-element list, got %r instead.' % section) 35 'Expected a one or two-element list, got %r instead.' % section)
36 return ''.join(step_text) 36 return ''.join(step_text)
37 37
38 class Test(object): 38 class Test(object):
39 """ 39 """
40 Base class for tests that can be retried after deapplying a previously 40 Base class for tests that can be retried after deapplying a previously
41 applied patch. 41 applied patch.
42 """ 42 """
43 # If True, 'trigger' and 'collect' should be used instead of 'run'.
M-A Ruel 2014/06/04 00:52:53 If I were another reader that didn't know about sw
Vadim Sh. 2014/06/04 02:58:26 Added more explanation, mentioned swarming.
44 async = False
43 45
44 @property 46 @property
45 def name(self): # pragma: no cover 47 def name(self): # pragma: no cover
46 """Name of the test.""" 48 """Name of the test."""
47 raise NotImplementedError() 49 raise NotImplementedError()
48 50
49 def run(self, suffix): # pragma: no cover 51 def run(self, suffix): # pragma: no cover
50 """Run the test. suffix is 'with patch' or 'without patch'.""" 52 """Run the test. suffix is 'with patch' or 'without patch'."""
51 raise NotImplementedError() 53 raise NotImplementedError()
52 54
55 def trigger(self, suffix): # pragma: no cover
56 """Launch the test asynchronously, used if self.async == True."""
57 raise NotImplementedError()
58
59 def collect(self, suffix): # pragma: no cover
60 """Wait for triggered test to finish, used if self.async == True."""
61 raise NotImplementedError()
62
53 def has_valid_results(self, suffix): # pragma: no cover 63 def has_valid_results(self, suffix): # pragma: no cover
54 """ 64 """
55 Returns True if results (failures) are valid. 65 Returns True if results (failures) are valid.
56 66
57 This makes it possible to distinguish between the case of no failures 67 This makes it possible to distinguish between the case of no failures
58 and the test failing to even report its results in machine-readable 68 and the test failing to even report its results in machine-readable
59 format. 69 format.
60 """ 70 """
61 raise NotImplementedError() 71 raise NotImplementedError()
62 72
63 def failures(self, suffix): # pragma: no cover 73 def failures(self, suffix): # pragma: no cover
64 """Return list of failures (list of strings).""" 74 """Return list of failures (list of strings)."""
65 raise NotImplementedError() 75 raise NotImplementedError()
66 76
67 def _step_name(self, suffix): 77 def _step_name(self, suffix):
68 """Helper to uniformly combine tests's name with a suffix.""" 78 """Helper to uniformly combine tests's name with a suffix."""
69 return '%s (%s)' % (self.name, suffix) 79 return '%s (%s)' % (self.name, suffix)
70 80
71 def determine_new_failures(self, tests, deapply_patch_fn): 81 def determine_new_failures(self, tests, deapply_patch_fn):
72 """ 82 """
73 Utility function for running steps with a patch applied, and retrying 83 Utility function for running steps with a patch applied, and retrying
74 failing steps without the patch. Failures from the run without the patch are 84 failing steps without the patch. Failures from the run without the patch are
75 ignored. 85 ignored.
76 86
77 Args: 87 Args:
78 tests - iterable of objects implementing the Test interface above 88 tests - iterable of objects implementing the Test interface above
79 deapply_patch_fn - function that takes a list of failing tests 89 deapply_patch_fn - function that takes a list of failing tests
80 and undoes any effect of the previously applied patch 90 and undoes any effect of the previously applied patch
81 """ 91 """
92 # Convert iterable to list, since it is enumerated multiple times.
93 tests = list(tests)
94
82 if self.m.step_history.failed: 95 if self.m.step_history.failed:
83 yield self.m.python.inline( 96 yield self.m.python.inline(
84 'Aborting due to failed build state.', 97 'Aborting due to failed build state.',
85 "import sys; sys.exit(1)", 98 "import sys; sys.exit(1)",
86 always_run=True, abort_on_failure=True) 99 always_run=True, abort_on_failure=True)
87 return # won't actually hit this, but be explicit 100 return # won't actually hit this, but be explicit
88 101
89 yield (t.run('with patch') for t in tests) 102 def run(prefix, tests):
103 """Runs synchronous and asynchronous tests."""
M-A Ruel 2014/06/04 00:52:53 Add a note about how test can run magically in par
Vadim Sh. 2014/06/04 02:58:26 Done.
104 # Trigger all async tests first, so that they are running in parallel
105 # with synchronous tests.
106 yield (t.trigger(prefix) for t in tests if t.async)
107 # Now block on all synchronous tests.
108 yield (t.run(prefix) for t in tests if not t.async)
109 # And finally wait for all pending asynchronous tests to complete.
110 yield (t.collect(prefix) for t in tests if t.async)
111
112 yield run('with patch', tests)
90 113
91 failing_tests = [] 114 failing_tests = []
92 for t in tests: 115 for t in tests:
93 if not t.has_valid_results('with patch'): 116 if not t.has_valid_results('with patch'):
94 yield self.m.python.inline( 117 yield self.m.python.inline(
95 t.name, 118 t.name,
96 r""" 119 r"""
97 import sys 120 import sys
98 print 'TEST RESULTS WERE INVALID' 121 print 'TEST RESULTS WERE INVALID'
99 sys.exit(1) 122 sys.exit(1)
100 """, 123 """,
101 always_run=True) 124 always_run=True)
102 elif t.failures('with patch'): 125 elif t.failures('with patch'):
103 failing_tests.append(t) 126 failing_tests.append(t)
104 if not failing_tests: 127 if not failing_tests:
105 return 128 return
106 129
107 yield deapply_patch_fn(failing_tests) 130 yield deapply_patch_fn(failing_tests)
108 131
109 yield (t.run('without patch') for t in failing_tests) 132 yield run('without patch', failing_tests)
110 yield (self._summarize_retried_test(t) for t in failing_tests) 133 yield (self._summarize_retried_test(t) for t in failing_tests)
111 134
112 def _summarize_retried_test(self, test): 135 def _summarize_retried_test(self, test):
113 if not test.has_valid_results('without patch'): 136 if not test.has_valid_results('without patch'):
114 return self.m.python.inline( 137 return self.m.python.inline(
115 test.name, 138 test.name,
116 r""" 139 r"""
117 import sys 140 import sys
118 print 'TEST RESULTS WERE INVALID' 141 print 'TEST RESULTS WERE INVALID'
119 sys.exit(1) 142 sys.exit(1)
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 """, 182 """,
160 args=[ 183 args=[
161 self.m.json.input({ 184 self.m.json.input({
162 'new': list(new_failures), 185 'new': list(new_failures),
163 'ignored': list(ignored_failures), 186 'ignored': list(ignored_failures),
164 }) 187 })
165 ], 188 ],
166 followup_fn=followup_fn, 189 followup_fn=followup_fn,
167 always_run=True, 190 always_run=True,
168 ) 191 )
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698