| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 json | 5 import json |
| 6 import re | 6 import re |
| 7 import time | 7 import time |
| 8 import urllib | 8 import urllib |
| 9 | 9 |
| 10 from . import config_validation |
| 10 from . import depot_config | 11 from . import depot_config |
| 11 from . import revision_state | 12 from . import revision_state |
| 12 | 13 |
| 13 _DEPS_SHA_PATCH = """ | 14 _DEPS_SHA_PATCH = """ |
| 14 diff --git DEPS.sha DEPS.sha | 15 diff --git DEPS.sha DEPS.sha |
| 15 new file mode 100644 | 16 new file mode 100644 |
| 16 --- /dev/null | 17 --- /dev/null |
| 17 +++ DEPS.sha | 18 +++ DEPS.sha |
| 18 @@ -0,0 +1 @@ | 19 @@ -0,0 +1 @@ |
| 19 +%(deps_sha)s | 20 +%(deps_sha)s |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 """This class abstracts an ongoing bisect (or n-sect) job.""" | 69 """This class abstracts an ongoing bisect (or n-sect) job.""" |
| 69 | 70 |
| 70 def __init__(self, api, bisect_config, revision_class, init_revisions=True): | 71 def __init__(self, api, bisect_config, revision_class, init_revisions=True): |
| 71 """Initializes the state of a new bisect job from a dictionary. | 72 """Initializes the state of a new bisect job from a dictionary. |
| 72 | 73 |
| 73 Note that the initial good_rev and bad_rev MUST resolve to a commit position | 74 Note that the initial good_rev and bad_rev MUST resolve to a commit position |
| 74 in the chromium repo. | 75 in the chromium repo. |
| 75 """ | 76 """ |
| 76 super(Bisector, self).__init__() | 77 super(Bisector, self).__init__() |
| 77 self._api = api | 78 self._api = api |
| 79 self.result_codes = set() |
| 78 self.ensure_sync_master_branch() | 80 self.ensure_sync_master_branch() |
| 79 self.bisect_config = bisect_config | 81 self.bisect_config = bisect_config |
| 80 self.config_step() | 82 self.config_step() |
| 83 self._validate_config() |
| 81 self.revision_class = revision_class | 84 self.revision_class = revision_class |
| 82 self.result_codes = set() | |
| 83 self.last_tested_revision = None | 85 self.last_tested_revision = None |
| 84 | 86 |
| 85 # Test-only properties. | 87 # Test-only properties. |
| 86 # TODO: Replace these with proper mod_test_data. | 88 # TODO: Replace these with proper mod_test_data. |
| 87 self.dummy_builds = bisect_config.get('dummy_builds', False) | 89 self.dummy_builds = bisect_config.get('dummy_builds', False) |
| 88 self.bypass_stats_check = bool(bisect_config.get('bypass_stats_check')) | 90 self.bypass_stats_check = bool(bisect_config.get('bypass_stats_check')) |
| 89 | 91 |
| 90 # Load configuration items. | 92 # Load configuration items. |
| 91 self.test_type = bisect_config.get('test_type', 'perf') | 93 self.test_type = bisect_config.get('test_type', 'perf') |
| 92 self.improvement_direction = int(bisect_config.get( | 94 self.improvement_direction = int(bisect_config.get( |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 results = step_result.stdout | 190 results = step_result.stdout |
| 189 if results is None: | 191 if results is None: |
| 190 assert self.dummy_builds | 192 assert self.dummy_builds |
| 191 return True | 193 return True |
| 192 significantly_different = results['significantly_different'] | 194 significantly_different = results['significantly_different'] |
| 193 step_result.presentation.logs[str(significantly_different)] = [ | 195 step_result.presentation.logs[str(significantly_different)] = [ |
| 194 'See json.output for details'] | 196 'See json.output for details'] |
| 195 return significantly_different | 197 return significantly_different |
| 196 | 198 |
| 197 def config_step(self): | 199 def config_step(self): |
| 198 """Yields a simple echo step that outputs the bisect config.""" | 200 """Yields a step that prints the bisect config.""" |
| 199 api = self.api | 201 api = self.api |
| 202 |
| 200 # bisect_config may come as a FrozenDict (which is not serializable). | 203 # bisect_config may come as a FrozenDict (which is not serializable). |
| 201 bisect_config = dict(self.bisect_config) | 204 bisect_config = dict(self.bisect_config) |
| 202 | 205 |
| 203 def fix_windows_backslashes(s): | 206 def fix_windows_backslashes(s): |
| 204 backslash_regex = re.compile(r'(?<!\\)\\(?!\\)') | 207 backslash_regex = re.compile(r'(?<!\\)\\(?!\\)') |
| 205 return backslash_regex.sub(r'\\', s) | 208 return backslash_regex.sub(r'\\', s) |
| 206 | 209 |
| 207 for k, v in bisect_config.iteritems(): | 210 for k, v in bisect_config.iteritems(): |
| 208 if isinstance(v, basestring): | 211 if isinstance(v, basestring): |
| 209 bisect_config[k] = fix_windows_backslashes(v) | 212 bisect_config[k] = fix_windows_backslashes(v) |
| 213 |
| 210 # We sort the keys to prevent problems with orders changing when | 214 # We sort the keys to prevent problems with orders changing when |
| 211 # recipe_simulation_test compares against expectation files. | 215 # recipe_simulation_test compares against expectation files. |
| 212 config_string = json.dumps(bisect_config, indent=2, sort_keys=True) | 216 config_string = json.dumps(bisect_config, indent=2, sort_keys=True) |
| 213 result = api.m.step('config', []) | 217 step = api.m.step('config', []) |
| 214 config_lines = config_string.splitlines() | 218 config_lines = config_string.splitlines() |
| 215 result.presentation.logs['Bisect job configuration'] = config_lines | 219 step.presentation.logs['Bisect job configuration'] = config_lines |
| 220 |
| 221 def _validate_config(self): |
| 222 """Raises an error and halts the bisect job if the config is invalid.""" |
| 223 try: |
| 224 config_validation.validate_bisect_config(self.bisect_config) |
| 225 except config_validation.ValidationFail as error: |
| 226 self.surface_result('BAD_CONFIG') |
| 227 self.api.m.halt(error.message) |
| 228 raise self.api.m.step.StepFailure(error.message) |
| 216 | 229 |
| 217 @property | 230 @property |
| 218 def api(self): | 231 def api(self): |
| 219 return self._api | 232 return self._api |
| 220 | 233 |
| 221 def compute_relative_change(self): | 234 def compute_relative_change(self): |
| 222 old_value = float(self.good_rev.mean_value) | 235 old_value = float(self.good_rev.mean_value) |
| 223 new_value = float(self.bad_rev.mean_value) | 236 new_value = float(self.bad_rev.mean_value) |
| 224 | 237 |
| 225 if new_value and not old_value: # pragma: no cover | 238 if new_value and not old_value: # pragma: no cover |
| (...skipping 688 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 914 }) | 927 }) |
| 915 return revision_rows | 928 return revision_rows |
| 916 | 929 |
| 917 def _get_build_url(self): | 930 def _get_build_url(self): |
| 918 properties = self.api.m.properties | 931 properties = self.api.m.properties |
| 919 bot_url = properties.get('buildbotURL', | 932 bot_url = properties.get('buildbotURL', |
| 920 'http://build.chromium.org/p/chromium/') | 933 'http://build.chromium.org/p/chromium/') |
| 921 builder_name = urllib.quote(properties.get('buildername', '')) | 934 builder_name = urllib.quote(properties.get('buildername', '')) |
| 922 builder_number = str(properties.get('buildnumber', '')) | 935 builder_number = str(properties.get('buildnumber', '')) |
| 923 return '%sbuilders/%s/builds/%s' % (bot_url, builder_name, builder_number) | 936 return '%sbuilders/%s/builds/%s' % (bot_url, builder_name, builder_number) |
| OLD | NEW |