OLD | NEW |
---|---|
1 # Copyright (C) 2010 Google Inc. All rights reserved. | 1 # Copyright (C) 2010 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 20 matching lines...) Expand all Loading... | |
31 """ | 31 """ |
32 | 32 |
33 import logging | 33 import logging |
34 import re | 34 import re |
35 | 35 |
36 from webkitpy.layout_tests.models.test_configuration import TestConfigurationCon verter | 36 from webkitpy.layout_tests.models.test_configuration import TestConfigurationCon verter |
37 | 37 |
38 _log = logging.getLogger(__name__) | 38 _log = logging.getLogger(__name__) |
39 | 39 |
40 | 40 |
41 # Test expectation and modifier constants. | 41 # Test expectation and specifier constants. |
42 # | 42 # |
43 # FIXME: range() starts with 0 which makes if expectation checks harder | 43 # FIXME: range() starts with 0 which makes if expectation checks harder |
44 # as PASS is 0. | 44 # as PASS is 0. |
45 (PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, TIMEOUT, CRASH, SKIP, WONTFIX, | 45 (PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, TIMEOUT, CRASH, SKIP, WONTFIX, |
46 SLOW, REBASELINE, NEEDS_REBASELINE, NEEDS_MANUAL_REBASELINE, MISSING, FLAKY, NO W, NONE) = range(18) | 46 SLOW, REBASELINE, NEEDS_REBASELINE, NEEDS_MANUAL_REBASELINE, MISSING, FLAKY, NO W, NONE) = range(18) |
47 | 47 |
48 # FIXME: Perhas these two routines should be part of the Port instead? | 48 # FIXME: Perhas these two routines should be part of the Port instead? |
49 BASELINE_SUFFIX_LIST = ('png', 'wav', 'txt') | 49 BASELINE_SUFFIX_LIST = ('png', 'wav', 'txt') |
50 | 50 |
51 WEBKIT_BUG_PREFIX = 'webkit.org/b/' | 51 WEBKIT_BUG_PREFIX = 'webkit.org/b/' |
(...skipping 13 matching lines...) Expand all Loading... | |
65 def __str__(self): | 65 def __str__(self): |
66 return '\n'.join(map(str, self.warnings)) | 66 return '\n'.join(map(str, self.warnings)) |
67 | 67 |
68 def __repr__(self): | 68 def __repr__(self): |
69 return 'ParseError(warnings=%s)' % self.warnings | 69 return 'ParseError(warnings=%s)' % self.warnings |
70 | 70 |
71 | 71 |
72 class TestExpectationParser(object): | 72 class TestExpectationParser(object): |
73 """Provides parsing facilities for lines in the test_expectation.txt file."" " | 73 """Provides parsing facilities for lines in the test_expectation.txt file."" " |
74 | 74 |
75 # FIXME: Rename these to *_KEYWORD as in MISSING_KEYWORD above, but make the case studdly-caps to match the actual file contents. | |
Dirk Pranke
2013/07/26 23:07:28
"studly" ?
| |
75 REBASELINE_MODIFIER = 'rebaseline' | 76 REBASELINE_MODIFIER = 'rebaseline' |
76 NEEDS_REBASELINE_MODIFIER = 'needsrebaseline' | 77 NEEDS_REBASELINE_MODIFIER = 'needsrebaseline' |
77 NEEDS_MANUAL_REBASELINE_MODIFIER = 'needsmanualrebaseline' | 78 NEEDS_MANUAL_REBASELINE_MODIFIER = 'needsmanualrebaseline' |
78 PASS_EXPECTATION = 'pass' | 79 PASS_EXPECTATION = 'pass' |
79 SKIP_MODIFIER = 'skip' | 80 SKIP_MODIFIER = 'skip' |
80 SLOW_MODIFIER = 'slow' | 81 SLOW_MODIFIER = 'slow' |
81 WONTFIX_MODIFIER = 'wontfix' | 82 WONTFIX_MODIFIER = 'wontfix' |
82 | 83 |
83 TIMEOUT_EXPECTATION = 'timeout' | 84 TIMEOUT_EXPECTATION = 'timeout' |
84 | 85 |
85 MISSING_BUG_WARNING = 'Test lacks BUG modifier.' | 86 MISSING_BUG_WARNING = 'Test lacks BUG specifier.' |
86 | 87 |
87 def __init__(self, port, full_test_list, allow_rebaseline_modifier): | 88 def __init__(self, port, full_test_list, allow_rebaseline): |
88 self._port = port | 89 self._port = port |
89 self._test_configuration_converter = TestConfigurationConverter(set(port .all_test_configurations()), port.configuration_specifier_macros()) | 90 self._test_configuration_converter = TestConfigurationConverter(set(port .all_test_configurations()), port.configuration_specifier_macros()) |
90 self._full_test_list = full_test_list | 91 self._full_test_list = full_test_list |
91 self._allow_rebaseline_modifier = allow_rebaseline_modifier | 92 self._allow_rebaseline = allow_rebaseline |
92 | 93 |
93 def parse(self, filename, expectations_string): | 94 def parse(self, filename, expectations_string): |
94 expectation_lines = [] | 95 expectation_lines = [] |
95 line_number = 0 | 96 line_number = 0 |
96 for line in expectations_string.split("\n"): | 97 for line in expectations_string.split("\n"): |
97 line_number += 1 | 98 line_number += 1 |
98 test_expectation = self._tokenize_line(filename, line, line_number) | 99 test_expectation = self._tokenize_line(filename, line, line_number) |
99 self._parse_line(test_expectation) | 100 self._parse_line(test_expectation) |
100 expectation_lines.append(test_expectation) | 101 expectation_lines.append(test_expectation) |
101 return expectation_lines | 102 return expectation_lines |
(...skipping 10 matching lines...) Expand all Loading... | |
112 def expectation_line_for_test(self, test_name, expectations): | 113 def expectation_line_for_test(self, test_name, expectations): |
113 expectation_line = self._create_expectation_line(test_name, expectations , '<Bot TestExpectations>') | 114 expectation_line = self._create_expectation_line(test_name, expectations , '<Bot TestExpectations>') |
114 self._parse_line(expectation_line) | 115 self._parse_line(expectation_line) |
115 return expectation_line | 116 return expectation_line |
116 | 117 |
117 | 118 |
118 def expectation_for_skipped_test(self, test_name): | 119 def expectation_for_skipped_test(self, test_name): |
119 if not self._port.test_exists(test_name): | 120 if not self._port.test_exists(test_name): |
120 _log.warning('The following test %s from the Skipped list doesn\'t e xist' % test_name) | 121 _log.warning('The following test %s from the Skipped list doesn\'t e xist' % test_name) |
121 expectation_line = self._create_expectation_line(test_name, [TestExpecta tionParser.PASS_EXPECTATION], '<Skipped file>') | 122 expectation_line = self._create_expectation_line(test_name, [TestExpecta tionParser.PASS_EXPECTATION], '<Skipped file>') |
122 # FIXME: It's not clear what the expectations for a skipped test should be; the expectations | 123 expectation_line.expectations = [TestExpectationParser.SKIP_MODIFIER, Te stExpectationParser.WONTFIX_MODIFIER] |
123 # might be different for different entries in a Skipped file, or from th e command line, or from | |
124 # only running parts of the tests. It's also not clear if it matters muc h. | |
125 expectation_line.modifiers = [TestExpectationParser.SKIP_MODIFIER, TestE xpectationParser.WONTFIX_MODIFIER] | |
126 expectation_line.is_skipped_outside_expectations_file = True | 124 expectation_line.is_skipped_outside_expectations_file = True |
127 self._parse_line(expectation_line) | 125 self._parse_line(expectation_line) |
128 return expectation_line | 126 return expectation_line |
129 | 127 |
130 def _parse_line(self, expectation_line): | 128 def _parse_line(self, expectation_line): |
131 if not expectation_line.name: | 129 if not expectation_line.name: |
132 return | 130 return |
133 | 131 |
134 if not self._check_test_exists(expectation_line): | 132 if not self._check_test_exists(expectation_line): |
135 return | 133 return |
136 | 134 |
137 expectation_line.is_file = self._port.test_isfile(expectation_line.name) | 135 expectation_line.is_file = self._port.test_isfile(expectation_line.name) |
138 if expectation_line.is_file: | 136 if expectation_line.is_file: |
139 expectation_line.path = expectation_line.name | 137 expectation_line.path = expectation_line.name |
140 else: | 138 else: |
141 expectation_line.path = self._port.normalize_test_name(expectation_l ine.name) | 139 expectation_line.path = self._port.normalize_test_name(expectation_l ine.name) |
142 | 140 |
143 self._collect_matching_tests(expectation_line) | 141 self._collect_matching_tests(expectation_line) |
144 | 142 |
145 self._parse_modifiers(expectation_line) | 143 self._parse_specifiers(expectation_line) |
146 self._parse_expectations(expectation_line) | 144 self._parse_expectations(expectation_line) |
147 | 145 |
148 def _parse_modifiers(self, expectation_line): | 146 def _parse_specifiers(self, expectation_line): |
149 has_wontfix = False | 147 parsed_specifiers = set([specifier.lower() for specifier in expectation_ line.specifiers]) |
150 parsed_specifiers = set() | |
151 | |
152 modifiers = [modifier.lower() for modifier in expectation_line.modifiers ] | |
153 expectations = [expectation.lower() for expectation in expectation_line. expectations] | 148 expectations = [expectation.lower() for expectation in expectation_line. expectations] |
154 | 149 |
155 if self.SLOW_MODIFIER in modifiers and self.TIMEOUT_EXPECTATION in expec tations: | 150 if self.SLOW_MODIFIER in expectations and self.TIMEOUT_EXPECTATION in ex pectations: |
156 expectation_line.warnings.append('A test can not be both SLOW and TI MEOUT. If it times out indefinitely, then it should be just TIMEOUT.') | 151 expectation_line.warnings.append('A test can not be both SLOW and TI MEOUT. If it times out indefinitely, then it should be just TIMEOUT.') |
157 | 152 |
158 for modifier in expectation_line.modifiers: | 153 if not expectation_line.bugs and self.WONTFIX_MODIFIER not in expectatio ns and self._port.warn_if_bug_missing_in_test_expectations(): |
159 # FIXME: Store the unmodified modifier. | |
160 modifier = modifier.lower() | |
161 if modifier in TestExpectations.MODIFIERS: | |
162 expectation_line.parsed_modifiers.append(modifier) | |
163 if modifier == self.WONTFIX_MODIFIER: | |
164 has_wontfix = True | |
165 else: | |
166 parsed_specifiers.add(modifier) | |
167 | |
168 if not expectation_line.bugs and not has_wontfix and self._port.warn_if_ bug_missing_in_test_expectations(): | |
169 expectation_line.warnings.append(self.MISSING_BUG_WARNING) | 154 expectation_line.warnings.append(self.MISSING_BUG_WARNING) |
170 | 155 |
171 if self._allow_rebaseline_modifier and self.REBASELINE_MODIFIER in modif iers: | 156 if self._allow_rebaseline and self.REBASELINE_MODIFIER in expectations: |
172 expectation_line.warnings.append('REBASELINE should only be used for running rebaseline.py. Cannot be checked in.') | 157 expectation_line.warnings.append('REBASELINE should only be used for running rebaseline.py. Cannot be checked in.') |
173 | 158 |
174 expectation_line.matching_configurations = self._test_configuration_conv erter.to_config_set(parsed_specifiers, expectation_line.warnings) | 159 expectation_line.matching_configurations = self._test_configuration_conv erter.to_config_set(parsed_specifiers, expectation_line.warnings) |
175 | 160 |
176 def _parse_expectations(self, expectation_line): | 161 def _parse_expectations(self, expectation_line): |
177 result = set() | 162 result = set() |
178 for part in expectation_line.expectations: | 163 for part in expectation_line.expectations: |
179 expectation = TestExpectations.expectation_from_string(part) | 164 expectation = TestExpectations.expectation_from_string(part) |
180 if expectation is None: # Careful, PASS is currently 0. | 165 if expectation is None: # Careful, PASS is currently 0. |
181 expectation_line.warnings.append('Unsupported expectation: %s' % part) | 166 expectation_line.warnings.append('Unsupported expectation: %s' % part) |
(...skipping 29 matching lines...) Expand all Loading... | |
211 if not expectation_line.is_file: | 196 if not expectation_line.is_file: |
212 # this is a test category, return all the tests of the category. | 197 # this is a test category, return all the tests of the category. |
213 expectation_line.matching_tests = [test for test in self._full_test_ list if test.startswith(expectation_line.path)] | 198 expectation_line.matching_tests = [test for test in self._full_test_ list if test.startswith(expectation_line.path)] |
214 return | 199 return |
215 | 200 |
216 # this is a test file, do a quick check if it's in the | 201 # this is a test file, do a quick check if it's in the |
217 # full test suite. | 202 # full test suite. |
218 if expectation_line.path in self._full_test_list: | 203 if expectation_line.path in self._full_test_list: |
219 expectation_line.matching_tests.append(expectation_line.path) | 204 expectation_line.matching_tests.append(expectation_line.path) |
220 | 205 |
221 # FIXME: Update the original modifiers and remove this once the old syntax i s gone. | 206 # FIXME: Update the original specifiers and remove this once the old syntax is gone. |
222 _configuration_tokens_list = [ | 207 _configuration_tokens_list = [ |
223 'Mac', 'SnowLeopard', 'Lion', 'MountainLion', | 208 'Mac', 'SnowLeopard', 'Lion', 'MountainLion', |
224 'Win', 'XP', 'Win7', | 209 'Win', 'XP', 'Win7', |
225 'Linux', | 210 'Linux', |
226 'Android', | 211 'Android', |
227 'Release', | 212 'Release', |
228 'Debug', | 213 'Debug', |
229 ] | 214 ] |
230 | 215 |
231 _configuration_tokens = dict((token, token.upper()) for token in _configurat ion_tokens_list) | 216 _configuration_tokens = dict((token, token.upper()) for token in _configurat ion_tokens_list) |
232 _inverted_configuration_tokens = dict((value, name) for name, value in _conf iguration_tokens.iteritems()) | 217 _inverted_configuration_tokens = dict((value, name) for name, value in _conf iguration_tokens.iteritems()) |
233 | 218 |
234 # FIXME: Update the original modifiers list and remove this once the old syn tax is gone. | 219 # FIXME: Update the original specifiers list and remove this once the old sy ntax is gone. |
235 _expectation_tokens = { | 220 _expectation_tokens = { |
236 'Crash': 'CRASH', | 221 'Crash': 'CRASH', |
237 'Failure': 'FAIL', | 222 'Failure': 'FAIL', |
238 'ImageOnlyFailure': 'IMAGE', | 223 'ImageOnlyFailure': 'IMAGE', |
239 MISSING_KEYWORD: 'MISSING', | 224 MISSING_KEYWORD: 'MISSING', |
240 'Pass': 'PASS', | 225 'Pass': 'PASS', |
241 'Rebaseline': 'REBASELINE', | 226 'Rebaseline': 'REBASELINE', |
242 NEEDS_REBASELINE_KEYWORD: NEEDS_REBASELINE_KEYWORD, | 227 NEEDS_REBASELINE_KEYWORD: NEEDS_REBASELINE_KEYWORD, |
243 NEEDS_MANUAL_REBASELINE_KEYWORD: NEEDS_MANUAL_REBASELINE_KEYWORD, | 228 NEEDS_MANUAL_REBASELINE_KEYWORD: NEEDS_MANUAL_REBASELINE_KEYWORD, |
244 'Skip': 'SKIP', | 229 'Skip': 'SKIP', |
245 'Slow': 'SLOW', | 230 'Slow': 'SLOW', |
246 'Timeout': 'TIMEOUT', | 231 'Timeout': 'TIMEOUT', |
247 'WontFix': 'WONTFIX', | 232 'WontFix': 'WONTFIX', |
248 } | 233 } |
249 | 234 |
250 _inverted_expectation_tokens = dict([(value, name) for name, value in _expec tation_tokens.iteritems()] + | 235 _inverted_expectation_tokens = dict([(value, name) for name, value in _expec tation_tokens.iteritems()] + |
251 [('TEXT', 'Failure'), ('IMAGE+TEXT', 'Fa ilure'), ('AUDIO', 'Failure')]) | 236 [('TEXT', 'Failure'), ('IMAGE+TEXT', 'Fa ilure'), ('AUDIO', 'Failure')]) |
252 | 237 |
253 # FIXME: Seems like these should be classmethods on TestExpectationLine inst ead of TestExpectationParser. | 238 # FIXME: Seems like these should be classmethods on TestExpectationLine inst ead of TestExpectationParser. |
254 @classmethod | 239 @classmethod |
255 def _tokenize_line(cls, filename, expectation_string, line_number): | 240 def _tokenize_line(cls, filename, expectation_string, line_number): |
256 """Tokenizes a line from TestExpectations and returns an unparsed TestEx pectationLine instance using the old format. | 241 """Tokenizes a line from TestExpectations and returns an unparsed TestEx pectationLine instance using the old format. |
257 | 242 |
258 The new format for a test expectation line is: | 243 The new format for a test expectation line is: |
259 | 244 |
260 [[bugs] [ "[" <configuration modifiers> "]" <name> [ "[" <expectations> "]" ["#" <comment>] | 245 [[bugs] [ "[" <configuration specifiers> "]" <name> [ "[" <expectations> "]" ["#" <comment>] |
261 | 246 |
262 Any errant whitespace is not preserved. | 247 Any errant whitespace is not preserved. |
263 | 248 |
264 """ | 249 """ |
265 expectation_line = TestExpectationLine() | 250 expectation_line = TestExpectationLine() |
266 expectation_line.original_string = expectation_string | 251 expectation_line.original_string = expectation_string |
267 expectation_line.filename = filename | 252 expectation_line.filename = filename |
268 expectation_line.line_numbers = str(line_number) | 253 expectation_line.line_numbers = str(line_number) |
269 | 254 |
270 comment_index = expectation_string.find("#") | 255 comment_index = expectation_string.find("#") |
271 if comment_index == -1: | 256 if comment_index == -1: |
272 comment_index = len(expectation_string) | 257 comment_index = len(expectation_string) |
273 else: | 258 else: |
274 expectation_line.comment = expectation_string[comment_index + 1:] | 259 expectation_line.comment = expectation_string[comment_index + 1:] |
275 | 260 |
276 remaining_string = re.sub(r"\s+", " ", expectation_string[:comment_index ].strip()) | 261 remaining_string = re.sub(r"\s+", " ", expectation_string[:comment_index ].strip()) |
277 if len(remaining_string) == 0: | 262 if len(remaining_string) == 0: |
278 return expectation_line | 263 return expectation_line |
279 | 264 |
280 # special-case parsing this so that we fail immediately instead of treat ing this as a test name | 265 # special-case parsing this so that we fail immediately instead of treat ing this as a test name |
281 if remaining_string.startswith('//'): | 266 if remaining_string.startswith('//'): |
282 expectation_line.warnings = ['use "#" instead of "//" for comments'] | 267 expectation_line.warnings = ['use "#" instead of "//" for comments'] |
283 return expectation_line | 268 return expectation_line |
284 | 269 |
285 bugs = [] | 270 bugs = [] |
286 modifiers = [] | 271 specifiers = [] |
287 name = None | 272 name = None |
288 expectations = [] | 273 expectations = [] |
289 warnings = [] | 274 warnings = [] |
290 has_unrecognized_expectation = False | 275 has_unrecognized_expectation = False |
291 | 276 |
292 tokens = remaining_string.split() | 277 tokens = remaining_string.split() |
293 state = 'start' | 278 state = 'start' |
294 for token in tokens: | 279 for token in tokens: |
295 if (token.startswith(WEBKIT_BUG_PREFIX) or | 280 if (token.startswith(WEBKIT_BUG_PREFIX) or |
296 token.startswith(CHROMIUM_BUG_PREFIX) or | 281 token.startswith(CHROMIUM_BUG_PREFIX) or |
(...skipping 28 matching lines...) Expand all Loading... | |
325 state = 'name' | 310 state = 'name' |
326 elif state == 'expectations': | 311 elif state == 'expectations': |
327 state = 'done' | 312 state = 'done' |
328 else: | 313 else: |
329 warnings.append('unexpected "]"') | 314 warnings.append('unexpected "]"') |
330 break | 315 break |
331 elif token in ('//', ':', '='): | 316 elif token in ('//', ':', '='): |
332 warnings.append('"%s" is not legal in the new TestExpectations s yntax.' % token) | 317 warnings.append('"%s" is not legal in the new TestExpectations s yntax.' % token) |
333 break | 318 break |
334 elif state == 'configuration': | 319 elif state == 'configuration': |
335 modifiers.append(cls._configuration_tokens.get(token, token)) | 320 specifiers.append(cls._configuration_tokens.get(token, token)) |
336 elif state == 'expectations': | 321 elif state == 'expectations': |
337 if token in ('Rebaseline', 'Skip', 'Slow', 'WontFix'): | 322 if token not in cls._expectation_tokens: |
338 modifiers.append(token.upper()) | |
339 elif token not in cls._expectation_tokens: | |
340 has_unrecognized_expectation = True | 323 has_unrecognized_expectation = True |
341 warnings.append('Unrecognized expectation "%s"' % token) | 324 warnings.append('Unrecognized expectation "%s"' % token) |
342 else: | 325 else: |
343 expectations.append(cls._expectation_tokens.get(token, token )) | 326 expectations.append(cls._expectation_tokens.get(token, token )) |
344 elif state == 'name_found': | 327 elif state == 'name_found': |
345 warnings.append('expecting "[", "#", or end of line instead of " %s"' % token) | 328 warnings.append('expecting "[", "#", or end of line instead of " %s"' % token) |
346 break | 329 break |
347 else: | 330 else: |
348 name = token | 331 name = token |
349 state = 'name_found' | 332 state = 'name_found' |
350 | 333 |
351 if not warnings: | 334 if not warnings: |
352 if not name: | 335 if not name: |
353 warnings.append('Did not find a test name.') | 336 warnings.append('Did not find a test name.') |
354 elif state not in ('name_found', 'done'): | 337 elif state not in ('name_found', 'done'): |
355 warnings.append('Missing a "]"') | 338 warnings.append('Missing a "]"') |
356 | 339 |
357 if 'WONTFIX' in modifiers and 'SKIP' not in modifiers and not expectatio ns: | 340 if 'WONTFIX' in expectations and 'SKIP' not in expectations: |
358 modifiers.append('SKIP') | 341 expectations.append('SKIP') |
359 | 342 |
360 if 'SKIP' in modifiers and expectations: | 343 if ('SKIP' in expectations or 'WONTFIX' in expectations) and len(set(exp ectations) - set(['SKIP', 'WONTFIX'])): |
361 # FIXME: This is really a semantic warning and shouldn't be here. Re move when we drop the old syntax. | 344 warnings.append('A test marked Skip or WontFix must not have other e xpectations.') |
362 warnings.append('A test marked Skip must not have other expectations .') | 345 |
363 elif not expectations: | 346 if not expectations and not has_unrecognized_expectation: |
364 if not has_unrecognized_expectation and 'SKIP' not in modifiers and 'REBASELINE' not in modifiers and NEEDS_REBASELINE_KEYWORD not in modifiers and NEEDS_MANUAL_REBASELINE_KEYWORD not in modifiers and 'SLOW' not in modifiers: | 347 warnings.append('Missing expectations.') |
365 warnings.append('Missing expectations.') | |
366 expectations = ['PASS'] | |
367 | 348 |
368 expectation_line.bugs = bugs | 349 expectation_line.bugs = bugs |
369 expectation_line.modifiers = modifiers | 350 expectation_line.specifiers = specifiers |
370 expectation_line.expectations = expectations | 351 expectation_line.expectations = expectations |
371 expectation_line.name = name | 352 expectation_line.name = name |
372 expectation_line.warnings = warnings | 353 expectation_line.warnings = warnings |
373 return expectation_line | 354 return expectation_line |
374 | 355 |
375 @classmethod | 356 @classmethod |
376 def _split_space_separated(cls, space_separated_string): | 357 def _split_space_separated(cls, space_separated_string): |
377 """Splits a space-separated string into an array.""" | 358 """Splits a space-separated string into an array.""" |
378 return [part.strip() for part in space_separated_string.strip().split(' ')] | 359 return [part.strip() for part in space_separated_string.strip().split(' ')] |
379 | 360 |
380 | 361 |
381 class TestExpectationLine(object): | 362 class TestExpectationLine(object): |
382 """Represents a line in test expectations file.""" | 363 """Represents a line in test expectations file.""" |
383 | 364 |
384 def __init__(self): | 365 def __init__(self): |
385 """Initializes a blank-line equivalent of an expectation.""" | 366 """Initializes a blank-line equivalent of an expectation.""" |
386 self.original_string = None | 367 self.original_string = None |
387 self.filename = None # this is the path to the expectations file for th is line | 368 self.filename = None # this is the path to the expectations file for th is line |
388 self.line_numbers = None | 369 self.line_numbers = None |
389 self.name = None # this is the path in the line itself | 370 self.name = None # this is the path in the line itself |
390 self.path = None # this is the normpath of self.name | 371 self.path = None # this is the normpath of self.name |
391 self.bugs = [] | 372 self.bugs = [] |
392 self.modifiers = [] | 373 self.specifiers = [] |
393 self.parsed_modifiers = [] | 374 self.parsed_specifiers = [] |
394 self.matching_configurations = set() | 375 self.matching_configurations = set() |
395 self.expectations = [] | 376 self.expectations = [] |
396 self.parsed_expectations = set() | 377 self.parsed_expectations = set() |
397 self.comment = None | 378 self.comment = None |
398 self.matching_tests = [] | 379 self.matching_tests = [] |
399 self.warnings = [] | 380 self.warnings = [] |
400 self.is_skipped_outside_expectations_file = False | 381 self.is_skipped_outside_expectations_file = False |
401 | 382 |
402 def __eq__(self, other): | 383 def __eq__(self, other): |
403 return (self.original_string == other.original_string | 384 return (self.original_string == other.original_string |
404 and self.filename == other.filename | 385 and self.filename == other.filename |
405 and self.line_numbers == other.line_numbers | 386 and self.line_numbers == other.line_numbers |
406 and self.name == other.name | 387 and self.name == other.name |
407 and self.path == other.path | 388 and self.path == other.path |
408 and self.bugs == other.bugs | 389 and self.bugs == other.bugs |
409 and self.modifiers == other.modifiers | 390 and self.specifiers == other.specifiers |
410 and self.parsed_modifiers == other.parsed_modifiers | 391 and self.parsed_specifiers == other.parsed_specifiers |
411 and self.matching_configurations == other.matching_configurations | 392 and self.matching_configurations == other.matching_configurations |
412 and self.expectations == other.expectations | 393 and self.expectations == other.expectations |
413 and self.parsed_expectations == other.parsed_expectations | 394 and self.parsed_expectations == other.parsed_expectations |
414 and self.comment == other.comment | 395 and self.comment == other.comment |
415 and self.matching_tests == other.matching_tests | 396 and self.matching_tests == other.matching_tests |
416 and self.warnings == other.warnings | 397 and self.warnings == other.warnings |
417 and self.is_skipped_outside_expectations_file == other.is_skipped_ou tside_expectations_file) | 398 and self.is_skipped_outside_expectations_file == other.is_skipped_ou tside_expectations_file) |
418 | 399 |
419 def is_invalid(self): | 400 def is_invalid(self): |
420 return self.warnings and self.warnings != [TestExpectationParser.MISSING _BUG_WARNING] | 401 return self.warnings and self.warnings != [TestExpectationParser.MISSING _BUG_WARNING] |
(...skipping 29 matching lines...) Expand all Loading... | |
450 # We only care about filenames when we're linting, in which case the fil enames are the same. | 431 # We only care about filenames when we're linting, in which case the fil enames are the same. |
451 # Not clear that there's anything better to do when not linting and the filenames are different. | 432 # Not clear that there's anything better to do when not linting and the filenames are different. |
452 if model_all_expectations: | 433 if model_all_expectations: |
453 result.filename = line2.filename | 434 result.filename = line2.filename |
454 result.line_numbers = line1.line_numbers + "," + line2.line_numbers | 435 result.line_numbers = line1.line_numbers + "," + line2.line_numbers |
455 result.name = line1.name | 436 result.name = line1.name |
456 result.path = line1.path | 437 result.path = line1.path |
457 result.parsed_expectations = set(line1.parsed_expectations) | set(line2. parsed_expectations) | 438 result.parsed_expectations = set(line1.parsed_expectations) | set(line2. parsed_expectations) |
458 result.expectations = list(set(line1.expectations) | set(line2.expectati ons)) | 439 result.expectations = list(set(line1.expectations) | set(line2.expectati ons)) |
459 result.bugs = list(set(line1.bugs) | set(line2.bugs)) | 440 result.bugs = list(set(line1.bugs) | set(line2.bugs)) |
460 result.modifiers = list(set(line1.modifiers) | set(line2.modifiers)) | 441 result.specifiers = list(set(line1.specifiers) | set(line2.specifiers)) |
461 result.parsed_modifiers = list(set(line1.parsed_modifiers) | set(line2.p arsed_modifiers)) | 442 result.parsed_specifiers = list(set(line1.parsed_specifiers) | set(line2 .parsed_specifiers)) |
462 result.matching_configurations = set(line1.matching_configurations) | se t(line2.matching_configurations) | 443 result.matching_configurations = set(line1.matching_configurations) | se t(line2.matching_configurations) |
463 result.matching_tests = list(list(set(line1.matching_tests) | set(line2. matching_tests))) | 444 result.matching_tests = list(list(set(line1.matching_tests) | set(line2. matching_tests))) |
464 result.warnings = list(set(line1.warnings) | set(line2.warnings)) | 445 result.warnings = list(set(line1.warnings) | set(line2.warnings)) |
465 result.is_skipped_outside_expectations_file = line1.is_skipped_outside_e xpectations_file or line2.is_skipped_outside_expectations_file | 446 result.is_skipped_outside_expectations_file = line1.is_skipped_outside_e xpectations_file or line2.is_skipped_outside_expectations_file |
466 return result | 447 return result |
467 | 448 |
468 def to_string(self, test_configuration_converter, include_modifiers=True, in clude_expectations=True, include_comment=True): | 449 def to_string(self, test_configuration_converter, include_specifiers=True, i nclude_expectations=True, include_comment=True): |
469 parsed_expectation_to_string = dict([[parsed_expectation, expectation_st ring] for expectation_string, parsed_expectation in TestExpectations.EXPECTATION S.items()]) | 450 parsed_expectation_to_string = dict([[parsed_expectation, expectation_st ring] for expectation_string, parsed_expectation in TestExpectations.EXPECTATION S.items()]) |
470 | 451 |
471 if self.is_invalid(): | 452 if self.is_invalid(): |
472 return self.original_string or '' | 453 return self.original_string or '' |
473 | 454 |
474 if self.name is None: | 455 if self.name is None: |
475 return '' if self.comment is None else "#%s" % self.comment | 456 return '' if self.comment is None else "#%s" % self.comment |
476 | 457 |
477 if test_configuration_converter and self.bugs: | 458 if test_configuration_converter and self.bugs: |
478 specifiers_list = test_configuration_converter.to_specifiers_list(se lf.matching_configurations) | 459 specifiers_list = test_configuration_converter.to_specifiers_list(se lf.matching_configurations) |
479 result = [] | 460 result = [] |
480 for specifiers in specifiers_list: | 461 for specifiers in specifiers_list: |
481 # FIXME: this is silly that we join the modifiers and then immed iately split them. | 462 # FIXME: this is silly that we join the specifiers and then imme diately split them. |
482 modifiers = self._serialize_parsed_modifiers(test_configuration_ converter, specifiers).split() | 463 specifiers = self._serialize_parsed_specifiers(test_configuratio n_converter, specifiers).split() |
483 expectations = self._serialize_parsed_expectations(parsed_expect ation_to_string).split() | 464 expectations = self._serialize_parsed_expectations(parsed_expect ation_to_string).split() |
484 result.append(self._format_line(self.bugs, modifiers, self.name, expectations, self.comment)) | 465 result.append(self._format_line(self.bugs, specifiers, self.name , expectations, self.comment)) |
485 return "\n".join(result) if result else None | 466 return "\n".join(result) if result else None |
486 | 467 |
487 return self._format_line(self.bugs, self.modifiers, self.name, self.expe ctations, self.comment, | 468 return self._format_line(self.bugs, self.specifiers, self.name, self.exp ectations, self.comment, |
488 include_modifiers, include_expectations, include_comment) | 469 include_specifiers, include_expectations, include_comment) |
489 | 470 |
490 def to_csv(self): | 471 def to_csv(self): |
491 # Note that this doesn't include the comments. | 472 # Note that this doesn't include the comments. |
492 return '%s,%s,%s,%s' % (self.name, ' '.join(self.bugs), ' '.join(self.mo difiers), ' '.join(self.expectations)) | 473 return '%s,%s,%s,%s' % (self.name, ' '.join(self.bugs), ' '.join(self.sp ecifiers), ' '.join(self.expectations)) |
493 | 474 |
494 def _serialize_parsed_expectations(self, parsed_expectation_to_string): | 475 def _serialize_parsed_expectations(self, parsed_expectation_to_string): |
495 result = [] | 476 result = [] |
496 for index in TestExpectations.EXPECTATION_ORDER: | 477 for index in TestExpectations.EXPECTATION_ORDER: |
497 if index in self.parsed_expectations: | 478 if index in self.parsed_expectations: |
498 result.append(parsed_expectation_to_string[index]) | 479 result.append(parsed_expectation_to_string[index]) |
499 return ' '.join(result) | 480 return ' '.join(result) |
500 | 481 |
501 def _serialize_parsed_modifiers(self, test_configuration_converter, specifie rs): | 482 def _serialize_parsed_specifiers(self, test_configuration_converter, specifi ers): |
502 result = [] | 483 result = [] |
503 result.extend(sorted(self.parsed_modifiers)) | 484 result.extend(sorted(self.parsed_specifiers)) |
504 result.extend(test_configuration_converter.specifier_sorter().sort_speci fiers(specifiers)) | 485 result.extend(test_configuration_converter.specifier_sorter().sort_speci fiers(specifiers)) |
505 return ' '.join(result) | 486 return ' '.join(result) |
506 | 487 |
507 @staticmethod | 488 @staticmethod |
508 def _filter_redundant_expectations(expectations): | 489 def _filter_redundant_expectations(expectations): |
509 if set(expectations) == set(['Pass', 'Skip']): | 490 if set(expectations) == set(['Pass', 'Skip']): |
510 return ['Skip'] | 491 return ['Skip'] |
511 if set(expectations) == set(['Pass', 'Slow']): | 492 if set(expectations) == set(['Pass', 'Slow']): |
512 return ['Slow'] | 493 return ['Slow'] |
513 return expectations | 494 return expectations |
514 | 495 |
515 @staticmethod | 496 @staticmethod |
516 def _format_line(bugs, modifiers, name, expectations, comment, include_modif iers=True, include_expectations=True, include_comment=True): | 497 def _format_line(bugs, specifiers, name, expectations, comment, include_spec ifiers=True, include_expectations=True, include_comment=True): |
517 new_modifiers = [] | 498 new_specifiers = [] |
518 new_expectations = [] | 499 new_expectations = [] |
519 for modifier in modifiers: | 500 for specifier in specifiers: |
520 # FIXME: Make this all work with the mixed-cased modifiers (e.g. Won tFix, Slow, etc). | 501 # FIXME: Make this all work with the mixed-cased specifiers (e.g. Wo ntFix, Slow, etc). |
521 modifier = modifier.upper() | 502 specifier = specifier.upper() |
522 if modifier in ('SLOW', 'SKIP', 'REBASELINE', NEEDS_REBASELINE_KEYWO RD, NEEDS_MANUAL_REBASELINE_KEYWORD, 'WONTFIX'): | 503 new_specifiers.append(TestExpectationParser._inverted_configuration_ tokens.get(specifier, specifier)) |
523 new_expectations.append(TestExpectationParser._inverted_expectat ion_tokens.get(modifier)) | |
524 else: | |
525 new_modifiers.append(TestExpectationParser._inverted_configurati on_tokens.get(modifier, modifier)) | |
526 | 504 |
527 for expectation in expectations: | 505 for expectation in expectations: |
528 expectation = expectation.upper() | 506 expectation = expectation.upper() |
529 new_expectations.append(TestExpectationParser._inverted_expectation_ tokens.get(expectation, expectation)) | 507 new_expectations.append(TestExpectationParser._inverted_expectation_ tokens.get(expectation, expectation)) |
530 | 508 |
531 result = '' | 509 result = '' |
532 if include_modifiers and (bugs or new_modifiers): | 510 if include_specifiers and (bugs or new_specifiers): |
533 if bugs: | 511 if bugs: |
534 result += ' '.join(bugs) + ' ' | 512 result += ' '.join(bugs) + ' ' |
535 if new_modifiers: | 513 if new_specifiers: |
536 result += '[ %s ] ' % ' '.join(new_modifiers) | 514 result += '[ %s ] ' % ' '.join(new_specifiers) |
537 result += name | 515 result += name |
538 if include_expectations and new_expectations: | 516 if include_expectations and new_expectations: |
539 new_expectations = TestExpectationLine._filter_redundant_expectation s(new_expectations) | 517 new_expectations = TestExpectationLine._filter_redundant_expectation s(new_expectations) |
540 result += ' [ %s ]' % ' '.join(sorted(set(new_expectations))) | 518 result += ' [ %s ]' % ' '.join(sorted(set(new_expectations))) |
541 if include_comment and comment is not None: | 519 if include_comment and comment is not None: |
542 result += " #%s" % comment | 520 result += " #%s" % comment |
543 return result | 521 return result |
544 | 522 |
545 | 523 |
546 # FIXME: Refactor API to be a proper CRUD. | 524 # FIXME: Refactor API to be a proper CRUD. |
547 class TestExpectationsModel(object): | 525 class TestExpectationsModel(object): |
548 """Represents relational store of all expectations and provides CRUD semanti cs to manage it.""" | 526 """Represents relational store of all expectations and provides CRUD semanti cs to manage it.""" |
549 | 527 |
550 def __init__(self, shorten_filename=None): | 528 def __init__(self, shorten_filename=None): |
551 # Maps a test to its list of expectations. | 529 # Maps a test to its list of expectations. |
552 self._test_to_expectations = {} | 530 self._test_to_expectations = {} |
553 | 531 |
554 # Maps a test to list of its modifiers (string values) | 532 # Maps a test to list of its specifiers (string values) |
555 self._test_to_modifiers = {} | 533 self._test_to_specifiers = {} |
556 | 534 |
557 # Maps a test to a TestExpectationLine instance. | 535 # Maps a test to a TestExpectationLine instance. |
558 self._test_to_expectation_line = {} | 536 self._test_to_expectation_line = {} |
559 | 537 |
560 self._modifier_to_tests = self._dict_of_sets(TestExpectations.MODIFIERS) | |
561 self._expectation_to_tests = self._dict_of_sets(TestExpectations.EXPECTA TIONS) | 538 self._expectation_to_tests = self._dict_of_sets(TestExpectations.EXPECTA TIONS) |
562 self._timeline_to_tests = self._dict_of_sets(TestExpectations.TIMELINES) | 539 self._timeline_to_tests = self._dict_of_sets(TestExpectations.TIMELINES) |
563 self._result_type_to_tests = self._dict_of_sets(TestExpectations.RESULT_ TYPES) | 540 self._result_type_to_tests = self._dict_of_sets(TestExpectations.RESULT_ TYPES) |
564 | 541 |
565 self._shorten_filename = shorten_filename or (lambda x: x) | 542 self._shorten_filename = shorten_filename or (lambda x: x) |
566 | 543 |
567 def _dict_of_sets(self, strings_to_constants): | 544 def _dict_of_sets(self, strings_to_constants): |
568 """Takes a dict of strings->constants and returns a dict mapping | 545 """Takes a dict of strings->constants and returns a dict mapping |
569 each constant to an empty set.""" | 546 each constant to an empty set.""" |
570 d = {} | 547 d = {} |
571 for c in strings_to_constants.values(): | 548 for c in strings_to_constants.values(): |
572 d[c] = set() | 549 d[c] = set() |
573 return d | 550 return d |
574 | 551 |
575 def get_test_set(self, modifier, expectation=None, include_skips=True): | 552 def get_test_set(self, expectation, include_skips=True): |
576 if expectation is None: | 553 tests = self._expectation_to_tests[expectation] |
577 tests = self._modifier_to_tests[modifier] | |
578 else: | |
579 tests = (self._expectation_to_tests[expectation] & | |
580 self._modifier_to_tests[modifier]) | |
581 | |
582 if not include_skips: | 554 if not include_skips: |
583 tests = tests - self.get_test_set(SKIP, expectation) | 555 tests = tests - self.get_test_set(SKIP) |
584 | |
585 return tests | 556 return tests |
586 | 557 |
587 def get_test_set_for_keyword(self, keyword): | 558 def get_test_set_for_keyword(self, keyword): |
588 # FIXME: get_test_set() is an awkward public interface because it requir es | |
589 # callers to know the difference between modifiers and expectations. We | |
590 # should replace that with this where possible. | |
591 expectation_enum = TestExpectations.EXPECTATIONS.get(keyword.lower(), No ne) | 559 expectation_enum = TestExpectations.EXPECTATIONS.get(keyword.lower(), No ne) |
592 if expectation_enum is not None: | 560 if expectation_enum is not None: |
593 return self._expectation_to_tests[expectation_enum] | 561 return self._expectation_to_tests[expectation_enum] |
594 modifier_enum = TestExpectations.MODIFIERS.get(keyword.lower(), None) | |
595 if modifier_enum is not None: | |
596 return self._modifier_to_tests[modifier_enum] | |
597 | 562 |
598 # We must not have an index on this modifier. | |
599 matching_tests = set() | 563 matching_tests = set() |
600 for test, modifiers in self._test_to_modifiers.iteritems(): | 564 for test, specifiers in self._test_to_specifiers.iteritems(): |
601 if keyword.lower() in modifiers: | 565 if keyword.lower() in specifiers: |
602 matching_tests.add(test) | 566 matching_tests.add(test) |
603 return matching_tests | 567 return matching_tests |
604 | 568 |
605 def get_tests_with_result_type(self, result_type): | 569 def get_tests_with_result_type(self, result_type): |
606 return self._result_type_to_tests[result_type] | 570 return self._result_type_to_tests[result_type] |
607 | 571 |
608 def get_tests_with_timeline(self, timeline): | 572 def get_tests_with_timeline(self, timeline): |
609 return self._timeline_to_tests[timeline] | 573 return self._timeline_to_tests[timeline] |
610 | 574 |
611 def get_modifiers(self, test): | |
612 """This returns modifiers for the given test (the modifiers plus the BUG XXXX identifier). This is used by the LTTF dashboard.""" | |
613 return self._test_to_modifiers[test] | |
614 | |
615 def has_modifier(self, test, modifier): | |
616 return test in self._modifier_to_tests[modifier] | |
617 | |
618 def has_keyword(self, test, keyword): | |
619 return (keyword.upper() in self.get_expectations_string(test) or | |
620 keyword.lower() in self.get_modifiers(test)) | |
621 | |
622 def has_test(self, test): | 575 def has_test(self, test): |
623 return test in self._test_to_expectation_line | 576 return test in self._test_to_expectation_line |
624 | 577 |
625 def get_expectation_line(self, test): | 578 def get_expectation_line(self, test): |
626 return self._test_to_expectation_line.get(test) | 579 return self._test_to_expectation_line.get(test) |
627 | 580 |
628 def get_expectations(self, test): | 581 def get_expectations(self, test): |
629 return self._test_to_expectations[test] | 582 return self._test_to_expectations[test] |
630 | 583 |
631 def get_expectations_string(self, test): | 584 def get_expectations_string(self, test): |
632 """Returns the expectatons for the given test as an uppercase string. | 585 """Returns the expectatons for the given test as an uppercase string. |
633 If there are no expectations for the test, then "PASS" is returned.""" | 586 If there are no expectations for the test, then "PASS" is returned.""" |
634 if self.get_expectation_line(test).is_skipped_outside_expectations_file: | 587 if self.get_expectation_line(test).is_skipped_outside_expectations_file: |
635 return 'NOTRUN' | 588 return 'NOTRUN' |
636 | 589 |
637 if self.has_modifier(test, WONTFIX): | |
638 return TestExpectationParser.WONTFIX_MODIFIER.upper() | |
639 | |
640 if self.has_modifier(test, SKIP): | |
641 return TestExpectationParser.SKIP_MODIFIER.upper() | |
642 | |
643 expectations = self.get_expectations(test) | 590 expectations = self.get_expectations(test) |
644 retval = [] | 591 retval = [] |
645 | 592 |
593 # FIXME: WontFix should cause the test to get skipped without artificial ly adding SKIP to the expectations list. | |
594 if WONTFIX in expectations and SKIP in expectations: | |
595 expectations.remove(SKIP) | |
596 | |
646 for expectation in expectations: | 597 for expectation in expectations: |
647 retval.append(self.expectation_to_string(expectation)) | 598 retval.append(self.expectation_to_string(expectation)) |
648 | 599 |
649 return " ".join(retval) | 600 return " ".join(retval) |
650 | 601 |
651 def expectation_to_string(self, expectation): | 602 def expectation_to_string(self, expectation): |
652 """Return the uppercased string equivalent of a given expectation.""" | 603 """Return the uppercased string equivalent of a given expectation.""" |
653 for item in TestExpectations.EXPECTATIONS.items(): | 604 for item in TestExpectations.EXPECTATIONS.items(): |
654 if item[1] == expectation: | 605 if item[1] == expectation: |
655 return item[0].upper() | 606 return item[0].upper() |
656 for item in TestExpectations.MODIFIERS.items(): | |
657 if item[1] == expectation: | |
658 return item[0].upper() | |
659 raise ValueError(expectation) | 607 raise ValueError(expectation) |
660 | 608 |
661 def remove_expectation_line(self, test): | 609 def remove_expectation_line(self, test): |
662 if not self.has_test(test): | 610 if not self.has_test(test): |
663 return | 611 return |
664 self._clear_expectations_for_test(test) | 612 self._clear_expectations_for_test(test) |
665 del self._test_to_expectation_line[test] | 613 del self._test_to_expectation_line[test] |
666 | 614 |
667 def add_expectation_line(self, expectation_line, | 615 def add_expectation_line(self, expectation_line, |
668 override_existing_matches=False, | 616 override_existing_matches=False, |
669 merge_existing_matches=False, | 617 merge_existing_matches=False, |
670 model_all_expectations=False): | 618 model_all_expectations=False): |
671 """Returns a list of warnings encountered while matching modifiers.""" | 619 """Returns a list of warnings encountered while matching specifiers.""" |
672 | 620 |
673 if expectation_line.is_invalid(): | 621 if expectation_line.is_invalid(): |
674 return | 622 return |
675 | 623 |
676 for test in expectation_line.matching_tests: | 624 for test in expectation_line.matching_tests: |
677 if (not (override_existing_matches or merge_existing_matches) | 625 if (not (override_existing_matches or merge_existing_matches) |
678 and self._already_seen_better_match(test, expectation_line)) : | 626 and self._already_seen_better_match(test, expectation_line)) : |
679 continue | 627 continue |
680 | 628 |
681 if merge_existing_matches or model_all_expectations: | 629 if merge_existing_matches or model_all_expectations: |
682 expectation_line = TestExpectationLine.merge_expectation_lines(s elf.get_expectation_line(test), expectation_line, model_all_expectations) | 630 expectation_line = TestExpectationLine.merge_expectation_lines(s elf.get_expectation_line(test), expectation_line, model_all_expectations) |
683 | 631 |
684 self._clear_expectations_for_test(test) | 632 self._clear_expectations_for_test(test) |
685 self._test_to_expectation_line[test] = expectation_line | 633 self._test_to_expectation_line[test] = expectation_line |
686 self._add_test(test, expectation_line) | 634 self._add_test(test, expectation_line) |
687 | 635 |
688 def _add_test(self, test, expectation_line): | 636 def _add_test(self, test, expectation_line): |
689 """Sets the expected state for a given test. | 637 """Sets the expected state for a given test. |
690 | 638 |
691 This routine assumes the test has not been added before. If it has, | 639 This routine assumes the test has not been added before. If it has, |
692 use _clear_expectations_for_test() to reset the state prior to | 640 use _clear_expectations_for_test() to reset the state prior to |
693 calling this.""" | 641 calling this.""" |
694 self._test_to_expectations[test] = expectation_line.parsed_expectations | 642 self._test_to_expectations[test] = expectation_line.parsed_expectations |
695 for expectation in expectation_line.parsed_expectations: | 643 for expectation in expectation_line.parsed_expectations: |
696 self._expectation_to_tests[expectation].add(test) | 644 self._expectation_to_tests[expectation].add(test) |
697 | 645 |
698 self._test_to_modifiers[test] = expectation_line.modifiers | 646 self._test_to_specifiers[test] = expectation_line.specifiers |
699 for modifier in expectation_line.parsed_modifiers: | |
700 mod_value = TestExpectations.MODIFIERS[modifier] | |
701 self._modifier_to_tests[mod_value].add(test) | |
702 | 647 |
703 if TestExpectationParser.WONTFIX_MODIFIER in expectation_line.parsed_mod ifiers: | 648 if WONTFIX in expectation_line.parsed_expectations: |
704 self._timeline_to_tests[WONTFIX].add(test) | 649 self._timeline_to_tests[WONTFIX].add(test) |
705 else: | 650 else: |
706 self._timeline_to_tests[NOW].add(test) | 651 self._timeline_to_tests[NOW].add(test) |
707 | 652 |
708 if TestExpectationParser.SKIP_MODIFIER in expectation_line.parsed_modifi ers: | 653 if SKIP in expectation_line.parsed_expectations: |
709 self._result_type_to_tests[SKIP].add(test) | 654 self._result_type_to_tests[SKIP].add(test) |
710 elif expectation_line.parsed_expectations == set([PASS]): | 655 elif expectation_line.parsed_expectations == set([PASS]): |
711 self._result_type_to_tests[PASS].add(test) | 656 self._result_type_to_tests[PASS].add(test) |
712 elif expectation_line.is_flaky(): | 657 elif expectation_line.is_flaky(): |
713 self._result_type_to_tests[FLAKY].add(test) | 658 self._result_type_to_tests[FLAKY].add(test) |
714 else: | 659 else: |
715 # FIXME: What is this? | 660 # FIXME: What is this? |
716 self._result_type_to_tests[FAIL].add(test) | 661 self._result_type_to_tests[FAIL].add(test) |
717 | 662 |
718 def _clear_expectations_for_test(self, test): | 663 def _clear_expectations_for_test(self, test): |
719 """Remove prexisting expectations for this test. | 664 """Remove prexisting expectations for this test. |
720 This happens if we are seeing a more precise path | 665 This happens if we are seeing a more precise path |
721 than a previous listing. | 666 than a previous listing. |
722 """ | 667 """ |
723 if self.has_test(test): | 668 if self.has_test(test): |
724 self._test_to_expectations.pop(test, '') | 669 self._test_to_expectations.pop(test, '') |
725 self._remove_from_sets(test, self._expectation_to_tests) | 670 self._remove_from_sets(test, self._expectation_to_tests) |
726 self._remove_from_sets(test, self._modifier_to_tests) | |
727 self._remove_from_sets(test, self._timeline_to_tests) | 671 self._remove_from_sets(test, self._timeline_to_tests) |
728 self._remove_from_sets(test, self._result_type_to_tests) | 672 self._remove_from_sets(test, self._result_type_to_tests) |
729 | 673 |
730 def _remove_from_sets(self, test, dict_of_sets_of_tests): | 674 def _remove_from_sets(self, test, dict_of_sets_of_tests): |
731 """Removes the given test from the sets in the dictionary. | 675 """Removes the given test from the sets in the dictionary. |
732 | 676 |
733 Args: | 677 Args: |
734 test: test to look for | 678 test: test to look for |
735 dict: dict of sets of files""" | 679 dict: dict of sets of files""" |
736 for set_of_tests in dict_of_sets_of_tests.itervalues(): | 680 for set_of_tests in dict_of_sets_of_tests.itervalues(): |
(...skipping 19 matching lines...) Expand all Loading... | |
756 | 700 |
757 if len(prev_expectation_line.path) > len(expectation_line.path): | 701 if len(prev_expectation_line.path) > len(expectation_line.path): |
758 # The previous path matched more of the test. | 702 # The previous path matched more of the test. |
759 return True | 703 return True |
760 | 704 |
761 if len(prev_expectation_line.path) < len(expectation_line.path): | 705 if len(prev_expectation_line.path) < len(expectation_line.path): |
762 # This path matches more of the test. | 706 # This path matches more of the test. |
763 return False | 707 return False |
764 | 708 |
765 # At this point we know we have seen a previous exact match on this | 709 # At this point we know we have seen a previous exact match on this |
766 # base path, so we need to check the two sets of modifiers. | 710 # base path, so we need to check the two sets of specifiers. |
767 | 711 |
768 # FIXME: This code was originally designed to allow lines that matched | 712 # FIXME: This code was originally designed to allow lines that matched |
769 # more modifiers to override lines that matched fewer modifiers. | 713 # more specifiers to override lines that matched fewer specifiers. |
770 # However, we currently view these as errors. | 714 # However, we currently view these as errors. |
771 # | 715 # |
772 # To use the "more modifiers wins" policy, change the errors for overrid es | 716 # To use the "more specifiers wins" policy, change the errors for overri des |
773 # to be warnings and return False". | 717 # to be warnings and return False". |
774 | 718 |
775 if prev_expectation_line.matching_configurations == expectation_line.mat ching_configurations: | 719 if prev_expectation_line.matching_configurations == expectation_line.mat ching_configurations: |
776 expectation_line.warnings.append('Duplicate or ambiguous entry lines %s:%s and %s:%s.' % ( | 720 expectation_line.warnings.append('Duplicate or ambiguous entry lines %s:%s and %s:%s.' % ( |
777 self._shorten_filename(prev_expectation_line.filename), prev_exp ectation_line.line_numbers, | 721 self._shorten_filename(prev_expectation_line.filename), prev_exp ectation_line.line_numbers, |
778 self._shorten_filename(expectation_line.filename), expectation_l ine.line_numbers)) | 722 self._shorten_filename(expectation_line.filename), expectation_l ine.line_numbers)) |
779 return True | 723 return True |
780 | 724 |
781 if prev_expectation_line.matching_configurations >= expectation_line.mat ching_configurations: | 725 if prev_expectation_line.matching_configurations >= expectation_line.mat ching_configurations: |
782 expectation_line.warnings.append('More specific entry for %s on line %s:%s overrides line %s:%s.' % (expectation_line.name, | 726 expectation_line.warnings.append('More specific entry for %s on line %s:%s overrides line %s:%s.' % (expectation_line.name, |
(...skipping 22 matching lines...) Expand all Loading... | |
805 """Test expectations consist of lines with specifications of what | 749 """Test expectations consist of lines with specifications of what |
806 to expect from layout test cases. The test cases can be directories | 750 to expect from layout test cases. The test cases can be directories |
807 in which case the expectations apply to all test cases in that | 751 in which case the expectations apply to all test cases in that |
808 directory and any subdirectory. The format is along the lines of: | 752 directory and any subdirectory. The format is along the lines of: |
809 | 753 |
810 LayoutTests/fast/js/fixme.js [ Failure ] | 754 LayoutTests/fast/js/fixme.js [ Failure ] |
811 LayoutTests/fast/js/flaky.js [ Failure Pass ] | 755 LayoutTests/fast/js/flaky.js [ Failure Pass ] |
812 LayoutTests/fast/js/crash.js [ Crash Failure Pass Timeout ] | 756 LayoutTests/fast/js/crash.js [ Crash Failure Pass Timeout ] |
813 ... | 757 ... |
814 | 758 |
815 To add modifiers: | 759 To add specifiers: |
816 LayoutTests/fast/js/no-good.js | 760 LayoutTests/fast/js/no-good.js |
817 [ Debug ] LayoutTests/fast/js/no-good.js [ Pass Timeout ] | 761 [ Debug ] LayoutTests/fast/js/no-good.js [ Pass Timeout ] |
818 [ Debug ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] | 762 [ Debug ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] |
819 [ Linux Debug ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] | 763 [ Linux Debug ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] |
820 [ Linux Win ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] | 764 [ Linux Win ] LayoutTests/fast/js/no-good.js [ Pass Skip Timeout ] |
821 | 765 |
822 Skip: Doesn't run the test. | 766 Skip: Doesn't run the test. |
823 Slow: The test takes a long time to run, but does not timeout indefinitely. | 767 Slow: The test takes a long time to run, but does not timeout indefinitely. |
824 WontFix: For tests that we never intend to pass on a given platform (treated like Skip). | 768 WontFix: For tests that we never intend to pass on a given platform (treated like Skip). |
825 | 769 |
826 Notes: | 770 Notes: |
827 -A test cannot be both SLOW and TIMEOUT | 771 -A test cannot be both SLOW and TIMEOUT |
828 -A test can be included twice, but not via the same path. | 772 -A test can be included twice, but not via the same path. |
829 -If a test is included twice, then the more precise path wins. | 773 -If a test is included twice, then the more precise path wins. |
830 -CRASH tests cannot be WONTFIX | 774 -CRASH tests cannot be WONTFIX |
831 """ | 775 """ |
832 | 776 |
833 # FIXME: Update to new syntax once the old format is no longer supported. | 777 # FIXME: Update to new syntax once the old format is no longer supported. |
834 EXPECTATIONS = {'pass': PASS, | 778 EXPECTATIONS = {'pass': PASS, |
835 'audio': AUDIO, | 779 'audio': AUDIO, |
836 'fail': FAIL, | 780 'fail': FAIL, |
837 'image': IMAGE, | 781 'image': IMAGE, |
838 'image+text': IMAGE_PLUS_TEXT, | 782 'image+text': IMAGE_PLUS_TEXT, |
839 'text': TEXT, | 783 'text': TEXT, |
840 'timeout': TIMEOUT, | 784 'timeout': TIMEOUT, |
841 'crash': CRASH, | 785 'crash': CRASH, |
842 'missing': MISSING, | 786 'missing': MISSING, |
843 'skip': SKIP, | 787 TestExpectationParser.SKIP_MODIFIER: SKIP, |
844 'needsrebaseline': NEEDS_REBASELINE, | 788 TestExpectationParser.NEEDS_REBASELINE_MODIFIER: NEEDS_REBAS ELINE, |
845 'needsmanualrebaseline': NEEDS_MANUAL_REBASELINE} | 789 TestExpectationParser.NEEDS_MANUAL_REBASELINE_MODIFIER: NEED S_MANUAL_REBASELINE, |
790 TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, | |
791 TestExpectationParser.SLOW_MODIFIER: SLOW, | |
792 TestExpectationParser.REBASELINE_MODIFIER: REBASELINE, | |
793 } | |
846 | 794 |
847 EXPECTATIONS_TO_STRING = dict((k, v) for (v, k) in EXPECTATIONS.iteritems()) | 795 EXPECTATIONS_TO_STRING = dict((k, v) for (v, k) in EXPECTATIONS.iteritems()) |
848 | 796 |
849 # (aggregated by category, pass/fail/skip, type) | 797 # (aggregated by category, pass/fail/skip, type) |
850 EXPECTATION_DESCRIPTIONS = {SKIP: 'skipped', | 798 EXPECTATION_DESCRIPTIONS = {SKIP: 'skipped', |
851 PASS: 'passes', | 799 PASS: 'passes', |
852 FAIL: 'failures', | 800 FAIL: 'failures', |
853 IMAGE: 'image-only failures', | 801 IMAGE: 'image-only failures', |
854 TEXT: 'text-only failures', | 802 TEXT: 'text-only failures', |
855 IMAGE_PLUS_TEXT: 'image and text failures', | 803 IMAGE_PLUS_TEXT: 'image and text failures', |
856 AUDIO: 'audio failures', | 804 AUDIO: 'audio failures', |
857 CRASH: 'crashes', | 805 CRASH: 'crashes', |
858 TIMEOUT: 'timeouts', | 806 TIMEOUT: 'timeouts', |
859 MISSING: 'missing results'} | 807 MISSING: 'missing results'} |
860 | 808 |
861 EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, FAIL, IMAGE, SKIP) | 809 EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, FAIL, IMAGE, SKIP) |
862 | 810 |
811 NON_TEST_OUTCOME_EXPECTATIONS = (REBASELINE, SKIP, SLOW, WONTFIX) | |
812 | |
863 BUILD_TYPES = ('debug', 'release') | 813 BUILD_TYPES = ('debug', 'release') |
864 | 814 |
865 MODIFIERS = {TestExpectationParser.SKIP_MODIFIER: SKIP, | |
866 TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, | |
867 TestExpectationParser.SLOW_MODIFIER: SLOW, | |
868 TestExpectationParser.REBASELINE_MODIFIER: REBASELINE, | |
869 TestExpectationParser.NEEDS_REBASELINE_MODIFIER: NEEDS_REBASELI NE, | |
870 TestExpectationParser.NEEDS_MANUAL_REBASELINE_MODIFIER: NEEDS_M ANUAL_REBASELINE, | |
871 'none': NONE} | |
872 | |
873 MODIFIERS_TO_STRING = dict((k, v) for (v, k) in MODIFIERS.iteritems()) | |
874 | |
875 TIMELINES = {TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, | 815 TIMELINES = {TestExpectationParser.WONTFIX_MODIFIER: WONTFIX, |
876 'now': NOW} | 816 'now': NOW} |
877 | 817 |
878 RESULT_TYPES = {'skip': SKIP, | 818 RESULT_TYPES = {'skip': SKIP, |
879 'pass': PASS, | 819 'pass': PASS, |
880 'fail': FAIL, | 820 'fail': FAIL, |
881 'flaky': FLAKY} | 821 'flaky': FLAKY} |
882 | 822 |
883 @classmethod | 823 @classmethod |
884 def expectation_from_string(cls, string): | 824 def expectation_from_string(cls, string): |
885 assert(' ' not in string) # This only handles one expectation at a time . | 825 assert(' ' not in string) # This only handles one expectation at a time . |
886 return cls.EXPECTATIONS.get(string.lower()) | 826 return cls.EXPECTATIONS.get(string.lower()) |
887 | 827 |
888 @staticmethod | 828 @staticmethod |
889 def result_was_expected(result, expected_results, test_needs_rebaselining): | 829 def result_was_expected(result, expected_results, test_needs_rebaselining): |
890 """Returns whether we got a result we were expecting. | 830 """Returns whether we got a result we were expecting. |
891 Args: | 831 Args: |
892 result: actual result of a test execution | 832 result: actual result of a test execution |
893 expected_results: set of results listed in test_expectations | 833 expected_results: set of results listed in test_expectations |
894 test_needs_rebaselining: whether test was marked as REBASELINE""" | 834 test_needs_rebaselining: whether test was marked as REBASELINE""" |
835 if not (set(expected_results) - (set(TestExpectations.NON_TEST_OUTCOME_E XPECTATIONS))): | |
836 expected_results = set([PASS]) | |
837 | |
895 if result in expected_results: | 838 if result in expected_results: |
896 return True | 839 return True |
897 if result in (PASS, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, MISSING) and (N EEDS_REBASELINE in expected_results or NEEDS_MANUAL_REBASELINE in expected_resul ts): | 840 if result in (PASS, TEXT, IMAGE, IMAGE_PLUS_TEXT, AUDIO, MISSING) and (N EEDS_REBASELINE in expected_results or NEEDS_MANUAL_REBASELINE in expected_resul ts): |
898 return True | 841 return True |
899 if result in (TEXT, IMAGE_PLUS_TEXT, AUDIO) and (FAIL in expected_result s): | 842 if result in (TEXT, IMAGE_PLUS_TEXT, AUDIO) and (FAIL in expected_result s): |
900 return True | 843 return True |
901 if result == MISSING and test_needs_rebaselining: | 844 if result == MISSING and test_needs_rebaselining: |
902 return True | 845 return True |
903 if result == SKIP: | 846 if result == SKIP: |
904 return True | 847 return True |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
985 self._has_warnings = False | 928 self._has_warnings = False |
986 self._report_warnings() | 929 self._report_warnings() |
987 self._process_tests_without_expectations() | 930 self._process_tests_without_expectations() |
988 | 931 |
989 # TODO(ojan): Allow for removing skipped tests when getting the list of | 932 # TODO(ojan): Allow for removing skipped tests when getting the list of |
990 # tests to run, but not when getting metrics. | 933 # tests to run, but not when getting metrics. |
991 def model(self): | 934 def model(self): |
992 return self._model | 935 return self._model |
993 | 936 |
994 def get_needs_rebaseline_failures(self): | 937 def get_needs_rebaseline_failures(self): |
995 return self._model.get_test_set_for_keyword(TestExpectationParser.NEEDS_ REBASELINE_MODIFIER) | 938 return self._model.get_test_set(NEEDS_REBASELINE) |
996 | 939 |
997 def get_rebaselining_failures(self): | 940 def get_rebaselining_failures(self): |
998 return self._model.get_test_set(REBASELINE) | 941 return self._model.get_test_set(REBASELINE) |
999 | 942 |
1000 # FIXME: Change the callsites to use TestExpectationsModel and remove. | 943 # FIXME: Change the callsites to use TestExpectationsModel and remove. |
1001 def get_expectations(self, test): | 944 def get_expectations(self, test): |
1002 return self._model.get_expectations(test) | 945 return self._model.get_expectations(test) |
1003 | 946 |
1004 # FIXME: Change the callsites to use TestExpectationsModel and remove. | 947 # FIXME: Change the callsites to use TestExpectationsModel and remove. |
1005 def has_modifier(self, test, modifier): | |
1006 return self._model.has_modifier(test, modifier) | |
1007 | |
1008 # FIXME: Change the callsites to use TestExpectationsModel and remove. | |
1009 def get_tests_with_result_type(self, result_type): | 948 def get_tests_with_result_type(self, result_type): |
1010 return self._model.get_tests_with_result_type(result_type) | 949 return self._model.get_tests_with_result_type(result_type) |
1011 | 950 |
1012 # FIXME: Change the callsites to use TestExpectationsModel and remove. | 951 # FIXME: Change the callsites to use TestExpectationsModel and remove. |
1013 def get_test_set(self, modifier, expectation=None, include_skips=True): | 952 def get_test_set(self, expectation, include_skips=True): |
1014 return self._model.get_test_set(modifier, expectation, include_skips) | 953 return self._model.get_test_set(expectation, include_skips) |
1015 | |
1016 # FIXME: Change the callsites to use TestExpectationsModel and remove. | |
1017 def get_modifiers(self, test): | |
1018 return self._model.get_modifiers(test) | |
1019 | 954 |
1020 # FIXME: Change the callsites to use TestExpectationsModel and remove. | 955 # FIXME: Change the callsites to use TestExpectationsModel and remove. |
1021 def get_tests_with_timeline(self, timeline): | 956 def get_tests_with_timeline(self, timeline): |
1022 return self._model.get_tests_with_timeline(timeline) | 957 return self._model.get_tests_with_timeline(timeline) |
1023 | 958 |
1024 def get_expectations_string(self, test): | 959 def get_expectations_string(self, test): |
1025 return self._model.get_expectations_string(test) | 960 return self._model.get_expectations_string(test) |
1026 | 961 |
1027 def expectation_to_string(self, expectation): | 962 def expectation_to_string(self, expectation): |
1028 return self._model.expectation_to_string(expectation) | 963 return self._model.expectation_to_string(expectation) |
1029 | 964 |
1030 def matches_an_expected_result(self, test, result, pixel_tests_are_enabled): | 965 def matches_an_expected_result(self, test, result, pixel_tests_are_enabled): |
1031 expected_results = self._model.get_expectations(test) | 966 expected_results = self._model.get_expectations(test) |
1032 if not pixel_tests_are_enabled: | 967 if not pixel_tests_are_enabled: |
1033 expected_results = self.remove_pixel_failures(expected_results) | 968 expected_results = self.remove_pixel_failures(expected_results) |
1034 return self.result_was_expected(result, expected_results, self.is_rebase lining(test)) | 969 return self.result_was_expected(result, expected_results, self.is_rebase lining(test)) |
1035 | 970 |
1036 def is_rebaselining(self, test): | 971 def is_rebaselining(self, test): |
1037 return self._model.has_modifier(test, REBASELINE) | 972 return REBASELINE in self._model.get_expectations(test) |
1038 | 973 |
1039 def _shorten_filename(self, filename): | 974 def _shorten_filename(self, filename): |
1040 if filename.startswith(self._port.path_from_webkit_base()): | 975 if filename.startswith(self._port.path_from_webkit_base()): |
1041 return self._port.host.filesystem.relpath(filename, self._port.path_ from_webkit_base()) | 976 return self._port.host.filesystem.relpath(filename, self._port.path_ from_webkit_base()) |
1042 return filename | 977 return filename |
1043 | 978 |
1044 def _report_warnings(self): | 979 def _report_warnings(self): |
1045 warnings = [] | 980 warnings = [] |
1046 for expectation in self._expectations: | 981 for expectation in self._expectations: |
1047 for warning in expectation.warnings: | 982 for warning in expectation.warnings: |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1086 index = self._expectations.index(expectation) | 1021 index = self._expectations.index(expectation) |
1087 self._expectations.remove(expectation) | 1022 self._expectations.remove(expectation) |
1088 | 1023 |
1089 if index == len(self._expectations) or self._expectations[index].is_ whitespace_or_comment(): | 1024 if index == len(self._expectations) or self._expectations[index].is_ whitespace_or_comment(): |
1090 while index and self._expectations[index - 1].is_whitespace_or_c omment(): | 1025 while index and self._expectations[index - 1].is_whitespace_or_c omment(): |
1091 index = index - 1 | 1026 index = index - 1 |
1092 self._expectations.pop(index) | 1027 self._expectations.pop(index) |
1093 | 1028 |
1094 return self.list_to_string(self._expectations, self._parser._test_config uration_converter, modified_expectations) | 1029 return self.list_to_string(self._expectations, self._parser._test_config uration_converter, modified_expectations) |
1095 | 1030 |
1096 def remove_rebaselined_tests(self, except_these_tests, filename): | |
Dirk Pranke
2013/07/26 23:07:28
I was a bit surprised that this was gone, but I gu
| |
1097 """Returns a copy of the expectations in the file with the tests removed .""" | |
1098 def without_rebaseline_modifier(expectation): | |
1099 return (expectation.filename == filename and | |
1100 not (not expectation.is_invalid() and | |
1101 expectation.name in except_these_tests and | |
1102 'rebaseline' in expectation.parsed_modifiers)) | |
1103 | |
1104 return self.list_to_string(filter(without_rebaseline_modifier, self._exp ectations), reconstitute_only_these=[]) | |
1105 | |
1106 def _add_expectations(self, expectation_list): | 1031 def _add_expectations(self, expectation_list): |
1107 for expectation_line in expectation_list: | 1032 for expectation_line in expectation_list: |
1108 if not expectation_line.expectations: | 1033 if not expectation_line.expectations: |
1109 continue | 1034 continue |
1110 | 1035 |
1111 if self._model_all_expectations or self._test_config in expectation_ line.matching_configurations: | 1036 if self._model_all_expectations or self._test_config in expectation_ line.matching_configurations: |
1112 self._model.add_expectation_line(expectation_line, model_all_exp ectations=self._model_all_expectations) | 1037 self._model.add_expectation_line(expectation_line, model_all_exp ectations=self._model_all_expectations) |
1113 | 1038 |
1114 def add_extra_skipped_tests(self, tests_to_skip): | 1039 def add_extra_skipped_tests(self, tests_to_skip): |
1115 if not tests_to_skip: | 1040 if not tests_to_skip: |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1149 # If reconstitute_only_these is an empty list, we want to return ori ginal_string. | 1074 # If reconstitute_only_these is an empty list, we want to return ori ginal_string. |
1150 # So we need to compare reconstitute_only_these to None, not just ch eck if it's falsey. | 1075 # So we need to compare reconstitute_only_these to None, not just ch eck if it's falsey. |
1151 if reconstitute_only_these is None or expectation_line in reconstitu te_only_these: | 1076 if reconstitute_only_these is None or expectation_line in reconstitu te_only_these: |
1152 return expectation_line.to_string(test_configuration_converter) | 1077 return expectation_line.to_string(test_configuration_converter) |
1153 return expectation_line.original_string | 1078 return expectation_line.original_string |
1154 | 1079 |
1155 def nones_out(expectation_line): | 1080 def nones_out(expectation_line): |
1156 return expectation_line is not None | 1081 return expectation_line is not None |
1157 | 1082 |
1158 return "\n".join(filter(nones_out, map(serialize, expectation_lines))) | 1083 return "\n".join(filter(nones_out, map(serialize, expectation_lines))) |
OLD | NEW |