| 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 """ | |
| 6 This recipe can be used by components like v8 to verify blink tests with a | |
| 7 low false positive rate. Similar to a trybot, this recipe compares test | |
| 8 failures from a build with a current component revision with test failures | |
| 9 from a build with a pinned component revision. | |
| 10 | |
| 11 Summary of the recipe flow: | |
| 12 1. Sync chromium to HEAD | |
| 13 2. Sync blink to HEAD | |
| 14 3. Sync component X to revision Y | |
| 15 4. Run blink tests | |
| 16 -> In case of failures: | |
| 17 5. Sync chromium to same revision as 1 | |
| 18 6. Sync blink to same revision as 2 | |
| 19 7. Sync component X to pinned revision from DEPS file | |
| 20 8. Run blink tests | |
| 21 -> If failures in 4 don't happen in 8, then revision Y reveals a problem not | |
| 22 present in the pinned revision | |
| 23 | |
| 24 Revision Y will be the revision property as provided by buildbot or HEAD (i.e. | |
| 25 in a forced build with no revision provided). | |
| 26 """ | |
| 27 | |
| 28 from recipe_engine.types import freeze | |
| 29 | |
| 30 DEPS = [ | |
| 31 'build/chromium', | |
| 32 'build/chromium_checkout', | |
| 33 'build/chromium_tests', | |
| 34 'build/test_utils', | |
| 35 'depot_tools/bot_update', | |
| 36 'depot_tools/gclient', | |
| 37 'recipe_engine/json', | |
| 38 'recipe_engine/path', | |
| 39 'recipe_engine/platform', | |
| 40 'recipe_engine/properties', | |
| 41 'recipe_engine/python', | |
| 42 'recipe_engine/raw_io', | |
| 43 'recipe_engine/step', | |
| 44 ] | |
| 45 | |
| 46 | |
| 47 def V8Builder(config, bits, platform): | |
| 48 chromium_configs = [] | |
| 49 if config == 'Debug': | |
| 50 chromium_configs.append('v8_optimize_medium') | |
| 51 chromium_configs.append('v8_slow_dchecks') | |
| 52 return { | |
| 53 'gclient_apply_config': ['show_v8_revision'], | |
| 54 'chromium_apply_config': chromium_configs, | |
| 55 'chromium_config_kwargs': { | |
| 56 'BUILD_CONFIG': config, | |
| 57 'TARGET_BITS': bits, | |
| 58 }, | |
| 59 'test_args': ['--no-pixel-tests'], | |
| 60 'additional_expectations': [ | |
| 61 'v8', 'tools', 'blink_tests', 'TestExpectations', | |
| 62 ], | |
| 63 'component': {'path': 'src/v8', 'revision': '%s'}, | |
| 64 'testing': {'platform': platform}, | |
| 65 } | |
| 66 | |
| 67 | |
| 68 BUILDERS = freeze({ | |
| 69 'client.v8.fyi': { | |
| 70 'builders': { | |
| 71 'V8-Blink Win': V8Builder('Release', 32, 'win'), | |
| 72 'V8-Blink Mac': V8Builder('Release', 64, 'mac'), | |
| 73 'V8-Blink Linux 64': V8Builder('Release', 64, 'linux'), | |
| 74 'V8-Blink Linux 64 - ignition': V8Builder('Release', 64, 'linux'), | |
| 75 'V8-Blink Linux 64 (dbg)': V8Builder('Debug', 64, 'linux'), | |
| 76 }, | |
| 77 }, | |
| 78 }) | |
| 79 | |
| 80 | |
| 81 def determine_new_ignition_failures(caller_api, extra_args): | |
| 82 tests = [ | |
| 83 caller_api.chromium_tests.steps.BlinkTest( | |
| 84 extra_args=extra_args + [ | |
| 85 '--additional-expectations', | |
| 86 caller_api.path['checkout'].join( | |
| 87 'v8', 'tools', 'blink_tests', 'TestExpectationsIgnition'), | |
| 88 '--additional-driver-flag', | |
| 89 '--js-flags=--ignition', | |
| 90 ], | |
| 91 ), | |
| 92 ] | |
| 93 | |
| 94 failing_tests = caller_api.test_utils.run_tests_with_patch(caller_api, tests) | |
| 95 if not failing_tests: | |
| 96 return | |
| 97 | |
| 98 try: | |
| 99 # HACK(machenbach): Blink tests store state about failing tests. In order | |
| 100 # to rerun without ignition, we need to remove the extra args from the | |
| 101 # existing test object. TODO(machenbach): Remove this once ignition ships. | |
| 102 failing_tests[0]._extra_args = extra_args | |
| 103 caller_api.test_utils.run_tests(caller_api, failing_tests, 'without patch') | |
| 104 finally: | |
| 105 with caller_api.step.defer_results(): | |
| 106 for t in failing_tests: | |
| 107 caller_api.test_utils._summarize_retried_test(caller_api, t) | |
| 108 | |
| 109 | |
| 110 def RunSteps(api): | |
| 111 mastername = api.properties.get('mastername') | |
| 112 buildername = api.properties.get('buildername') | |
| 113 master_dict = BUILDERS.get(mastername, {}) | |
| 114 bot_config = master_dict.get('builders', {}).get(buildername) | |
| 115 | |
| 116 # Sync chromium to HEAD. | |
| 117 api.gclient.set_config('chromium', GIT_MODE=True) | |
| 118 api.gclient.c.revisions['src'] = 'HEAD' | |
| 119 api.chromium.set_config('blink', | |
| 120 **bot_config.get('chromium_config_kwargs', {})) | |
| 121 | |
| 122 for c in bot_config.get('gclient_apply_config', []): | |
| 123 api.gclient.apply_config(c) | |
| 124 for c in bot_config.get('chromium_apply_config', []): | |
| 125 api.chromium.apply_config(c) | |
| 126 | |
| 127 # Sync component to current component revision. | |
| 128 component_revision = api.properties.get('revision') or 'HEAD' | |
| 129 api.gclient.c.revisions[bot_config['component']['path']] = ( | |
| 130 bot_config['component']['revision'] % component_revision) | |
| 131 | |
| 132 # Ensure we remember the chromium revision. | |
| 133 api.gclient.c.got_revision_mapping['src'] = 'got_cr_revision' | |
| 134 | |
| 135 context = {} | |
| 136 checkout_dir = api.chromium_checkout.get_checkout_dir(bot_config) | |
| 137 if checkout_dir: | |
| 138 context['cwd'] = checkout_dir | |
| 139 | |
| 140 # Run all steps in the checkout dir (consistent with chromium_tests). | |
| 141 with api.step.context(context): | |
| 142 # TODO(phajdan.jr): remove redundant **context below once we fix things | |
| 143 # to behave the same without it. | |
| 144 step_result = api.bot_update.ensure_checkout(**context) | |
| 145 | |
| 146 api.chromium.ensure_goma() | |
| 147 | |
| 148 api.chromium.c.project_generator.tool = 'mb' | |
| 149 api.chromium.runhooks() | |
| 150 | |
| 151 api.chromium_tests.run_mb_and_compile( | |
| 152 ['blink_tests'], [], | |
| 153 name_suffix=' (with patch)', | |
| 154 ) | |
| 155 | |
| 156 api.chromium.runtest('webkit_unit_tests', xvfb=True) | |
| 157 | |
| 158 def component_pinned_fn(_failing_steps): | |
| 159 bot_update_json = step_result.json.output | |
| 160 api.gclient.c.revisions['src'] = str( | |
| 161 bot_update_json['properties']['got_cr_revision']) | |
| 162 # Reset component revision to the pinned revision from chromium's DEPS | |
| 163 # for comparison. | |
| 164 del api.gclient.c.revisions[bot_config['component']['path']] | |
| 165 # Update without changing got_revision. The first sync is the revision | |
| 166 # that is tested. The second is just for comparison. Setting got_revision | |
| 167 # again confuses the waterfall's console view. | |
| 168 api.bot_update.ensure_checkout(update_presentation=False) | |
| 169 | |
| 170 api.chromium_tests.run_mb_and_compile( | |
| 171 ['blink_tests'], [], | |
| 172 name_suffix=' (without patch)', | |
| 173 ) | |
| 174 | |
| 175 extra_args = list(bot_config.get('test_args', [])) | |
| 176 if bot_config.get('additional_expectations'): | |
| 177 extra_args.extend([ | |
| 178 '--additional-expectations', | |
| 179 api.path['checkout'].join(*bot_config['additional_expectations']), | |
| 180 ]) | |
| 181 | |
| 182 tests = [ | |
| 183 api.chromium_tests.steps.BlinkTest(extra_args=extra_args), | |
| 184 ] | |
| 185 | |
| 186 if 'ignition' in buildername: | |
| 187 determine_new_ignition_failures(api, extra_args) | |
| 188 else: | |
| 189 api.test_utils.determine_new_failures(api, tests, component_pinned_fn) | |
| 190 | |
| 191 | |
| 192 def _sanitize_nonalpha(text): | |
| 193 return ''.join(c if c.isalnum() else '_' for c in text) | |
| 194 | |
| 195 | |
| 196 def GenTests(api): | |
| 197 canned_test = api.test_utils.canned_test_output | |
| 198 with_patch = 'webkit_tests (with patch)' | |
| 199 without_patch = 'webkit_tests (without patch)' | |
| 200 | |
| 201 def properties(mastername, buildername): | |
| 202 return ( | |
| 203 api.properties.generic(mastername=mastername, | |
| 204 buildername=buildername, | |
| 205 revision='20123', | |
| 206 path_config='kitchen') | |
| 207 ) | |
| 208 | |
| 209 for mastername, master_config in BUILDERS.iteritems(): | |
| 210 for buildername, bot_config in master_config['builders'].iteritems(): | |
| 211 test_name = 'full_%s_%s' % (_sanitize_nonalpha(mastername), | |
| 212 _sanitize_nonalpha(buildername)) | |
| 213 tests = [] | |
| 214 for (pass_first, suffix) in ((True, '_pass'), (False, '_fail')): | |
| 215 test = ( | |
| 216 properties(mastername, buildername) + | |
| 217 api.platform( | |
| 218 bot_config['testing']['platform'], | |
| 219 bot_config.get( | |
| 220 'chromium_config_kwargs', {}).get('TARGET_BITS', 64)) + | |
| 221 api.test(test_name + suffix) + | |
| 222 api.override_step_data(with_patch, canned_test(passing=pass_first)) | |
| 223 ) | |
| 224 if not pass_first: | |
| 225 test += api.override_step_data( | |
| 226 without_patch, canned_test(passing=False, minimal=True)) | |
| 227 tests.append(test) | |
| 228 | |
| 229 for test in tests: | |
| 230 yield test | |
| 231 | |
| 232 # This tests that if the first fails, but the second pass succeeds | |
| 233 # that we fail the whole build. | |
| 234 yield ( | |
| 235 api.test('minimal_pass_continues') + | |
| 236 properties('client.v8.fyi', 'V8-Blink Linux 64') + | |
| 237 api.override_step_data(with_patch, canned_test(passing=False)) + | |
| 238 api.override_step_data(without_patch, | |
| 239 canned_test(passing=True, minimal=True)) | |
| 240 ) | |
| 241 | |
| 242 | |
| 243 # This tests what happens if something goes horribly wrong in | |
| 244 # run-webkit-tests and we return an internal error; the step should | |
| 245 # be considered a hard failure and we shouldn't try to compare the | |
| 246 # lists of failing tests. | |
| 247 # 255 == test_run_results.UNEXPECTED_ERROR_EXIT_STATUS in run-webkit-tests. | |
| 248 yield ( | |
| 249 api.test('webkit_tests_unexpected_error') + | |
| 250 properties('client.v8.fyi', 'V8-Blink Linux 64') + | |
| 251 api.override_step_data(with_patch, canned_test(passing=False, retcode=255)) | |
| 252 ) | |
| 253 | |
| 254 # TODO(dpranke): crbug.com/357866 . This tests what happens if we exceed the | |
| 255 # number of failures specified with --exit-after-n-crashes-or-times or | |
| 256 # --exit-after-n-failures; the step should be considered a hard failure and | |
| 257 # we shouldn't try to compare the lists of failing tests. | |
| 258 # 130 == test_run_results.INTERRUPTED_EXIT_STATUS in run-webkit-tests. | |
| 259 yield ( | |
| 260 api.test('webkit_tests_interrupted') + | |
| 261 properties('client.v8.fyi', 'V8-Blink Linux 64') + | |
| 262 api.override_step_data(with_patch, canned_test(passing=False, retcode=130)) | |
| 263 ) | |
| 264 | |
| 265 # This tests what happens if we don't trip the thresholds listed | |
| 266 # above, but fail more tests than we can safely fit in a return code. | |
| 267 # (this should be a soft failure and we can still retry w/o the patch | |
| 268 # and compare the lists of failing tests). | |
| 269 yield ( | |
| 270 api.test('too_many_failures_for_retcode') + | |
| 271 properties('client.v8.fyi', 'V8-Blink Linux 64') + | |
| 272 api.override_step_data(with_patch, | |
| 273 canned_test(passing=False, | |
| 274 num_additional_failures=125)) + | |
| 275 api.override_step_data(without_patch, | |
| 276 canned_test(passing=True, minimal=True)) | |
| 277 ) | |
| OLD | NEW |