Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (C) 2013 Google Inc. All rights reserved. | 1 # Copyright (C) 2013 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 29 """Generates a fake TestExpectations file consisting of flaky tests from the bot | 29 """Generates a fake TestExpectations file consisting of flaky tests from the bot |
| 30 corresponding to the give port.""" | 30 corresponding to the give port.""" |
| 31 | 31 |
| 32 import json | 32 import json |
| 33 import logging | 33 import logging |
| 34 import os.path | 34 import os.path |
| 35 import urllib | 35 import urllib |
| 36 import urllib2 | 36 import urllib2 |
| 37 | 37 |
| 38 from webkitpy.layout_tests.port import builders | 38 from webkitpy.layout_tests.port import builders |
| 39 from webkitpy.layout_tests.models.test_expectations import TestExpectations | 39 from webkitpy.layout_tests.models.test_expectations import TestExpectations, PAS S |
| 40 from webkitpy.layout_tests.models.test_expectations import TestExpectationLine | 40 from webkitpy.layout_tests.models.test_expectations import TestExpectationLine |
| 41 | 41 |
| 42 | 42 |
| 43 _log = logging.getLogger(__name__) | 43 _log = logging.getLogger(__name__) |
| 44 | 44 |
| 45 | 45 |
| 46 # results.json v4 format: | 46 # results.json v4 format: |
| 47 # { | 47 # { |
| 48 # 'version': 4, | 48 # 'version': 4, |
| 49 # 'builder name' : { | 49 # 'builder name' : { |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 141 def expectations_for_builder(self, builder): | 141 def expectations_for_builder(self, builder): |
| 142 results_json = self._results_json_for_builder(builder) | 142 results_json = self._results_json_for_builder(builder) |
| 143 if not results_json: | 143 if not results_json: |
| 144 return None | 144 return None |
| 145 return BotTestExpectations(results_json) | 145 return BotTestExpectations(results_json) |
| 146 | 146 |
| 147 class BotTestExpectations(object): | 147 class BotTestExpectations(object): |
| 148 # FIXME: Get this from the json instead of hard-coding it. | 148 # FIXME: Get this from the json instead of hard-coding it. |
| 149 RESULT_TYPES_TO_IGNORE = ['N', 'X', 'Y'] # NO_DATA, SKIP, NOTRUN | 149 RESULT_TYPES_TO_IGNORE = ['N', 'X', 'Y'] # NO_DATA, SKIP, NOTRUN |
| 150 | 150 |
| 151 # TODO(ojan): Remove this once crbug.com/514378 is fixed. | 151 # TODO(ojan): Remove this once crbug.com/514378 is fixed. |
|
joelo
2015/08/17 23:43:29
should be able to remove now?
| |
| 152 # The JSON can contain results for expectations, not just actual result type s. | 152 # The JSON can contain results for expectations, not just actual result type s. |
| 153 NON_RESULT_TYPES = ['S', 'X'] # SLOW, SKIP | 153 NON_RESULT_TYPES = ['S', 'X'] # SLOW, SKIP |
| 154 | 154 |
| 155 PASS_EXPECTATION = 'PASS' | |
| 156 | |
| 157 # specifiers arg is used in unittests to avoid the static dependency on buil ders. | 155 # specifiers arg is used in unittests to avoid the static dependency on buil ders. |
| 158 def __init__(self, results_json, specifiers=None): | 156 def __init__(self, results_json, specifiers=None): |
| 159 self.results_json = results_json | 157 self.results_json = results_json |
| 160 self.specifiers = specifiers or set(builders.specifiers_for_builder(resu lts_json.builder_name)) | 158 self.specifiers = specifiers or set(builders.specifiers_for_builder(resu lts_json.builder_name)) |
| 161 | 159 |
| 162 def _line_from_test_and_flaky_types(self, test_path, flaky_types): | 160 def _line_from_test_and_flaky_types(self, test_path, flaky_types): |
| 163 line = TestExpectationLine() | 161 line = TestExpectationLine() |
| 164 line.original_string = test_path | 162 line.original_string = test_path |
| 165 line.name = test_path | 163 line.name = test_path |
| 166 line.filename = test_path | 164 line.filename = test_path |
| 167 line.path = test_path # FIXME: Should this be normpath? | 165 line.path = test_path # FIXME: Should this be normpath? |
| 168 line.matching_tests = [test_path] | 166 line.matching_tests = [test_path] |
| 169 line.bugs = ["crbug.com/FILE_A_BUG_BEFORE_COMMITTING_THIS"] | 167 line.bugs = ["crbug.com/FILE_A_BUG_BEFORE_COMMITTING_THIS"] |
| 170 line.expectations = sorted(map(self.results_json.expectation_for_type, f laky_types)) | 168 line.expectations = sorted(flaky_types) |
| 171 line.specifiers = self.specifiers | 169 line.specifiers = self.specifiers |
| 172 return line | 170 return line |
| 173 | 171 |
| 174 def flakes_by_path(self, only_ignore_very_flaky): | 172 def flakes_by_path(self, only_ignore_very_flaky): |
| 175 """Sets test expectations to bot results if there are at least two disti nct results.""" | 173 """Sets test expectations to bot results if there are at least two disti nct results.""" |
| 176 flakes_by_path = {} | 174 flakes_by_path = {} |
| 177 for test_path, entry in self.results_json.walk_results(): | 175 for test_path, entry in self.results_json.walk_results(): |
| 178 flaky_types = self._flaky_types_in_results(entry, only_ignore_very_f laky) | 176 flaky_types = self._flaky_types_in_results(entry, only_ignore_very_f laky) |
| 179 if len(flaky_types) <= 1: | 177 if len(flaky_types) <= 1: |
| 180 continue | 178 continue |
| 181 flakes_by_path[test_path] = sorted(map(self.results_json.expectation _for_type, flaky_types)) | 179 flakes_by_path[test_path] = sorted(flaky_types) |
| 182 return flakes_by_path | 180 return flakes_by_path |
| 183 | 181 |
| 184 def unexpected_results_by_path(self): | 182 def unexpected_results_by_path(self): |
| 185 """For tests with unexpected results, returns original expectations + re sults.""" | 183 """For tests with unexpected results, returns original expectations + re sults.""" |
| 186 def exp_to_string(exp): | 184 def exp_to_string(exp): |
| 187 return TestExpectations.EXPECTATIONS_TO_STRING.get(exp, None).upper( ) | 185 return TestExpectations.EXPECTATIONS_TO_STRING.get(exp, None).upper( ) |
| 188 | 186 |
| 189 def string_to_exp(string): | 187 def string_to_exp(string): |
| 190 # Needs a bit more logic than the method above, | 188 # Needs a bit more logic than the method above, |
| 191 # since a PASS is 0 and evaluates to False. | 189 # since a PASS is 0 and evaluates to False. |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 | 238 |
| 241 for result_item in run_length_encoded_results: | 239 for result_item in run_length_encoded_results: |
| 242 _, result_types = self.results_json.occurances_and_type_from_result_ item(result_item) | 240 _, result_types = self.results_json.occurances_and_type_from_result_ item(result_item) |
| 243 | 241 |
| 244 for result_type in result_types: | 242 for result_type in result_types: |
| 245 if result_type not in self.RESULT_TYPES_TO_IGNORE: | 243 if result_type not in self.RESULT_TYPES_TO_IGNORE: |
| 246 results.add(result_type) | 244 results.add(result_type) |
| 247 | 245 |
| 248 return results | 246 return results |
| 249 | 247 |
| 248 def _result_to_enum(self, result): | |
| 249 return TestExpectations.EXPECTATIONS[result.lower()] | |
| 250 | |
| 250 def _flaky_types_in_results(self, results_entry, only_ignore_very_flaky): | 251 def _flaky_types_in_results(self, results_entry, only_ignore_very_flaky): |
| 251 flaky_results = set() | 252 flaky_results = set() |
| 252 | 253 |
| 253 latest_expectations = [self.PASS_EXPECTATION] | 254 # Always include pass as an expected result. Passes will never turn the bot red. |
| 255 # This fixes cases where the expectations have an implicit Pass, e.g. [ Slow ]. | |
| 256 latest_expectations = [PASS] | |
| 254 if self.results_json.EXPECTATIONS_KEY in results_entry: | 257 if self.results_json.EXPECTATIONS_KEY in results_entry: |
| 255 latest_expectations = results_entry[self.results_json.EXPECTATIONS_K EY].split(' ') | 258 expectations_list = results_entry[self.results_json.EXPECTATIONS_KEY ].split(' ') |
| 259 latest_expectations += [self._result_to_enum(expectation) for expect ation in expectations_list] | |
| 256 | 260 |
| 257 for result_item in results_entry[self.results_json.RESULTS_KEY]: | 261 for result_item in results_entry[self.results_json.RESULTS_KEY]: |
| 258 _, result_types_str = self.results_json.occurances_and_type_from_res ult_item(result_item) | 262 _, result_types_str = self.results_json.occurances_and_type_from_res ult_item(result_item) |
| 259 | 263 |
| 260 result_types = [] | 264 result_types = [] |
| 261 for result_type in result_types_str: | 265 for result_type in result_types_str: |
| 262 # TODO(ojan): Remove this if-statement once crbug.com/514378 is fixed. | 266 # TODO(ojan): Remove this if-statement once crbug.com/514378 is fixed. |
|
joelo
2015/08/17 23:43:30
same here
ojan
2015/08/19 00:42:54
Last I checked, some of the slower bots still had
| |
| 263 if result_type not in self.NON_RESULT_TYPES: | 267 if result_type not in self.NON_RESULT_TYPES: |
| 264 result_types.append(result_type) | 268 result_types.append(self.results_json.expectation_for_type(r esult_type)) |
| 265 | 269 |
| 266 # It didn't flake if it didn't retry. | 270 # It didn't flake if it didn't retry. |
| 267 if len(result_types) <= 1: | 271 if len(result_types) <= 1: |
| 268 continue | 272 continue |
| 269 | 273 |
| 270 # If the test ran as expected after only one retry, it's not very fl aky. | 274 # If the test ran as expected after only one retry, it's not very fl aky. |
| 271 # It's only very flaky if it failed the first run and the first retr y | 275 # It's only very flaky if it failed the first run and the first retr y |
| 272 # and then ran as expected in one of the subsequent retries. | 276 # and then ran as expected in one of the subsequent retries. |
| 273 if only_ignore_very_flaky and self.results_json.expectation_for_type (result_types[1]) in latest_expectations: | 277 # If there are only two entries, then that means it failed on the fi rst |
| 274 # Once we get a pass, we don't retry again. So if the first retr y passed, | 278 # try and ran as expected on the second because otherwise we'd have |
| 275 # then there should only be 2 entries because we don't do anothe r retry. | 279 # a third entry from the next try. |
| 276 # TODO(ojan): Reenable this assert once crbug.com/514378 is fixe d. | 280 second_result_type_enum_value = self._result_to_enum(result_types[1] ) |
| 277 # assert(len(result_types) == 2) | 281 if only_ignore_very_flaky and len(result_types) == 2: |
| 278 continue | 282 continue |
| 279 | 283 |
| 280 flaky_results = flaky_results.union(set(result_types)) | 284 has_unexpected_results = False |
| 285 for result_type in result_types: | |
| 286 result_enum = self._result_to_enum(result_type) | |
| 287 # TODO(ojan): We really should be grabbing the expected results from the time | |
| 288 # of the run instead of looking at the latest expected results. That's a lot | |
| 289 # more complicated though. So far we've been looking at the aggr egated | |
| 290 # results_small.json off test_results.appspot, which has all the information | |
| 291 # for the last 100 runs. In order to do this, we'd need to look at the | |
| 292 # individual runs' full_results.json, which would be slow and mo re complicated. | |
| 293 # The only thing we lose by not fixing this is that a test that was flaky | |
| 294 # and got fixed will still get printed out until 100 runs have p assed. | |
| 295 if not TestExpectations.result_was_expected(result_enum, latest_ expectations, test_needs_rebaselining=False): | |
| 296 has_unexpected_results = True | |
| 297 break | |
| 298 | |
| 299 if has_unexpected_results: | |
| 300 flaky_results = flaky_results.union(set(result_types)) | |
| 281 | 301 |
| 282 return flaky_results | 302 return flaky_results |
| OLD | NEW |