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

Unified Diff: webkit/tools/layout_tests/layout_package/test_expectations.py

Issue 545145: Move the layout test scripts into a 'webkitpy' subdirectory in preparation... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: try to de-confuse svn and the try bots Created 10 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: webkit/tools/layout_tests/layout_package/test_expectations.py
===================================================================
--- webkit/tools/layout_tests/layout_package/test_expectations.py (revision 36724)
+++ webkit/tools/layout_tests/layout_package/test_expectations.py (working copy)
@@ -1,783 +0,0 @@
-# Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""A helper class for reading in and dealing with tests expectations
-for layout tests.
-"""
-
-import logging
-import os
-import re
-import sys
-import time
-import path_utils
-
-sys.path.append(path_utils.PathFromBase('third_party'))
-import simplejson
-
-# Test expectation and modifier constants.
-(PASS, FAIL, TEXT, IMAGE, IMAGE_PLUS_TEXT, TIMEOUT, CRASH, SKIP, WONTFIX,
- DEFER, SLOW, REBASELINE, MISSING, FLAKY, NOW, NONE) = range(16)
-
-# Test expectation file update action constants
-(NO_CHANGE, REMOVE_TEST, REMOVE_PLATFORM, ADD_PLATFORMS_EXCEPT_THIS) = range(4)
-
-
-class TestExpectations:
- TEST_LIST = "test_expectations.txt"
-
- def __init__(self, tests, directory, platform, is_debug_mode, is_lint_mode,
- tests_are_present=True):
- """Reads the test expectations files from the given directory."""
- path = os.path.join(directory, self.TEST_LIST)
- self._expected_failures = TestExpectationsFile(path, tests, platform,
- is_debug_mode, is_lint_mode, tests_are_present=tests_are_present)
-
- # TODO(ojan): Allow for removing skipped tests when getting the list of
- # tests to run, but not when getting metrics.
- # TODO(ojan): Replace the Get* calls here with the more sane API exposed
- # by TestExpectationsFile below. Maybe merge the two classes entirely?
-
- def GetExpectationsJsonForAllPlatforms(self):
- return self._expected_failures.GetExpectationsJsonForAllPlatforms()
-
- def GetRebaseliningFailures(self):
- return (self._expected_failures.GetTestSet(REBASELINE, FAIL) |
- self._expected_failures.GetTestSet(REBASELINE, IMAGE) |
- self._expected_failures.GetTestSet(REBASELINE, TEXT) |
- self._expected_failures.GetTestSet(REBASELINE,
- IMAGE_PLUS_TEXT))
-
- def GetOptions(self, test):
- return self._expected_failures.GetOptions(test)
-
- def GetExpectations(self, test):
- return self._expected_failures.GetExpectations(test)
-
- def GetExpectationsString(self, test):
- """Returns the expectatons for the given test as an uppercase string.
- If there are no expectations for the test, then "PASS" is returned."""
- expectations = self.GetExpectations(test)
- retval = []
-
- for expectation in expectations:
- for item in TestExpectationsFile.EXPECTATIONS.items():
- if item[1] == expectation:
- retval.append(item[0])
- break
-
- return " ".join(retval).upper()
-
- def GetTimelineForTest(self, test):
- return self._expected_failures.GetTimelineForTest(test)
-
- def GetTestsWithResultType(self, result_type):
- return self._expected_failures.GetTestsWithResultType(result_type)
-
- def GetTestsWithTimeline(self, timeline):
- return self._expected_failures.GetTestsWithTimeline(timeline)
-
- def MatchesAnExpectedResult(self, test, result):
- """Returns whether we got one of the expected results for this test."""
- return (result in self._expected_failures.GetExpectations(test) or
- (result in (IMAGE, TEXT, IMAGE_PLUS_TEXT) and
- FAIL in self._expected_failures.GetExpectations(test)) or
- result == MISSING and self.IsRebaselining(test) or
- result == SKIP and self._expected_failures.HasModifier(test,
- SKIP))
-
- def IsRebaselining(self, test):
- return self._expected_failures.HasModifier(test, REBASELINE)
-
- def HasModifier(self, test, modifier):
- return self._expected_failures.HasModifier(test, modifier)
-
- def RemovePlatformFromFile(self, tests, platform, backup=False):
- return self._expected_failures.RemovePlatformFromFile(tests,
- platform,
- backup)
-
-
-def StripComments(line):
- """Strips comments from a line and return None if the line is empty
- or else the contents of line with leading and trailing spaces removed
- and all other whitespace collapsed"""
-
- commentIndex = line.find('//')
- if commentIndex is -1:
- commentIndex = len(line)
-
- line = re.sub(r'\s+', ' ', line[:commentIndex].strip())
- if line == '':
- return None
- else:
- return line
-
-
-class ModifiersAndExpectations:
- """A holder for modifiers and expectations on a test that serializes to
- JSON."""
-
- def __init__(self, modifiers, expectations):
- self.modifiers = modifiers
- self.expectations = expectations
-
-
-class ExpectationsJsonEncoder(simplejson.JSONEncoder):
- """JSON encoder that can handle ModifiersAndExpectations objects.
- """
-
- def default(self, obj):
- if isinstance(obj, ModifiersAndExpectations):
- return {"modifiers": obj.modifiers,
- "expectations": obj.expectations}
- else:
- return JSONEncoder.default(self, obj)
-
-
-class TestExpectationsFile:
- """Test expectation files consist of lines with specifications of what
- to expect from layout test cases. The test cases can be directories
- in which case the expectations apply to all test cases in that
- directory and any subdirectory. The format of the file is along the
- lines of:
-
- LayoutTests/fast/js/fixme.js = FAIL
- LayoutTests/fast/js/flaky.js = FAIL PASS
- LayoutTests/fast/js/crash.js = CRASH TIMEOUT FAIL PASS
- ...
-
- To add other options:
- SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- DEBUG : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- LINUX DEBUG SKIP : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
- DEFER LINUX WIN : LayoutTests/fast/js/no-good.js = TIMEOUT PASS
-
- SKIP: Doesn't run the test.
- SLOW: The test takes a long time to run, but does not timeout indefinitely.
- WONTFIX: For tests that we never intend to pass on a given platform.
- DEFER: Test does not count in our statistics for the current release.
- DEBUG: Expectations apply only to the debug build.
- RELEASE: Expectations apply only to release build.
- LINUX/WIN/WIN-XP/WIN-VISTA/WIN-7/MAC: Expectations apply only to these
- platforms.
-
- Notes:
- -A test cannot be both SLOW and TIMEOUT
- -A test cannot be both DEFER and WONTFIX
- -A test should only be one of IMAGE, TEXT, IMAGE+TEXT, or FAIL. FAIL is
- a migratory state that currently means either IMAGE, TEXT, or
- IMAGE+TEXT. Once we have finished migrating the expectations, we will
- change FAIL to have the meaning of IMAGE+TEXT and remove the IMAGE+TEXT
- identifier.
- -A test can be included twice, but not via the same path.
- -If a test is included twice, then the more precise path wins.
- -CRASH tests cannot be DEFER or WONTFIX
- """
-
- EXPECTATIONS = {'pass': PASS,
- 'fail': FAIL,
- 'text': TEXT,
- 'image': IMAGE,
- 'image+text': IMAGE_PLUS_TEXT,
- 'timeout': TIMEOUT,
- 'crash': CRASH,
- 'missing': MISSING}
-
- EXPECTATION_DESCRIPTIONS = {SKIP: ('skipped', 'skipped'),
- PASS: ('pass', 'passes'),
- FAIL: ('failure', 'failures'),
- TEXT: ('text diff mismatch',
- 'text diff mismatch'),
- IMAGE: ('image mismatch', 'image mismatch'),
- IMAGE_PLUS_TEXT: ('image and text mismatch',
- 'image and text mismatch'),
- CRASH: ('test shell crash',
- 'test shell crashes'),
- TIMEOUT: ('test timed out', 'tests timed out'),
- MISSING: ('no expected result found',
- 'no expected results found')}
-
- EXPECTATION_ORDER = (PASS, CRASH, TIMEOUT, MISSING, IMAGE_PLUS_TEXT,
- TEXT, IMAGE, FAIL, SKIP)
-
- BASE_PLATFORMS = ('linux', 'mac', 'win')
- PLATFORMS = BASE_PLATFORMS + ('win-xp', 'win-vista', 'win-7')
-
- BUILD_TYPES = ('debug', 'release')
-
- MODIFIERS = {'skip': SKIP,
- 'wontfix': WONTFIX,
- 'defer': DEFER,
- 'slow': SLOW,
- 'rebaseline': REBASELINE,
- 'none': NONE}
-
- TIMELINES = {'wontfix': WONTFIX,
- 'now': NOW,
- 'defer': DEFER}
-
- RESULT_TYPES = {'skip': SKIP,
- 'pass': PASS,
- 'fail': FAIL,
- 'flaky': FLAKY}
-
- def __init__(self, path, full_test_list, platform, is_debug_mode,
- is_lint_mode, expectations_as_str=None, suppress_errors=False,
- tests_are_present=True):
- """
- path: The path to the expectation file. An error is thrown if a test is
- listed more than once.
- full_test_list: The list of all tests to be run pending processing of
- the expections for those tests.
- platform: Which platform from self.PLATFORMS to filter tests for.
- is_debug_mode: Whether we testing a test_shell built debug mode.
- is_lint_mode: Whether this is just linting test_expecatations.txt.
- expectations_as_str: Contents of the expectations file. Used instead of
- the path. This makes unittesting sane.
- suppress_errors: Whether to suppress lint errors.
- tests_are_present: Whether the test files are present in the local
- filesystem. The LTTF Dashboard uses False here to avoid having to
- keep a local copy of the tree.
- """
-
- self._path = path
- self._expectations_as_str = expectations_as_str
- self._is_lint_mode = is_lint_mode
- self._tests_are_present = tests_are_present
- self._full_test_list = full_test_list
- self._suppress_errors = suppress_errors
- self._errors = []
- self._non_fatal_errors = []
- self._platform = self.ToTestPlatformName(platform)
- if self._platform is None:
- raise Exception("Unknown platform '%s'" % (platform))
- self._is_debug_mode = is_debug_mode
-
- # Maps relative test paths as listed in the expectations file to a
- # list of maps containing modifiers and expectations for each time
- # the test is listed in the expectations file.
- self._all_expectations = {}
-
- # Maps a test to its list of expectations.
- self._test_to_expectations = {}
-
- # Maps a test to its list of options (string values)
- self._test_to_options = {}
-
- # Maps a test to its list of modifiers: the constants associated with
- # the options minus any bug or platform strings
- self._test_to_modifiers = {}
-
- # Maps a test to the base path that it was listed with in the list.
- self._test_list_paths = {}
-
- self._modifier_to_tests = self._DictOfSets(self.MODIFIERS)
- self._expectation_to_tests = self._DictOfSets(self.EXPECTATIONS)
- self._timeline_to_tests = self._DictOfSets(self.TIMELINES)
- self._result_type_to_tests = self._DictOfSets(self.RESULT_TYPES)
-
- self._Read(self._GetIterableExpectations())
-
- def _DictOfSets(self, strings_to_constants):
- """Takes a dict of strings->constants and returns a dict mapping
- each constant to an empty set."""
- d = {}
- for c in strings_to_constants.values():
- d[c] = set()
- return d
-
- def _GetIterableExpectations(self):
- """Returns an object that can be iterated over. Allows for not caring
- about whether we're iterating over a file or a new-line separated
- string."""
- if self._expectations_as_str:
- iterable = [x + "\n" for x in
- self._expectations_as_str.split("\n")]
- # Strip final entry if it's empty to avoid added in an extra
- # newline.
- if iterable[len(iterable) - 1] == "\n":
- return iterable[:len(iterable) - 1]
- return iterable
- else:
- return open(self._path)
-
- def ToTestPlatformName(self, name):
- """Returns the test expectation platform that will be used for a
- given platform name, or None if there is no match."""
- chromium_prefix = 'chromium-'
- name = name.lower()
- if name.startswith(chromium_prefix):
- name = name[len(chromium_prefix):]
- if name in self.PLATFORMS:
- return name
- return None
-
- def GetTestSet(self, modifier, expectation=None, include_skips=True):
- if expectation is None:
- tests = self._modifier_to_tests[modifier]
- else:
- tests = (self._expectation_to_tests[expectation] &
- self._modifier_to_tests[modifier])
-
- if not include_skips:
- tests = tests - self.GetTestSet(SKIP, expectation)
-
- return tests
-
- def GetTestsWithResultType(self, result_type):
- return self._result_type_to_tests[result_type]
-
- def GetTestsWithTimeline(self, timeline):
- return self._timeline_to_tests[timeline]
-
- def GetOptions(self, test):
- """This returns the entire set of options for the given test
- (the modifiers plus the BUGXXXX identifier). This is used by the
- LTTF dashboard."""
- return self._test_to_options[test]
-
- def HasModifier(self, test, modifier):
- return test in self._modifier_to_tests[modifier]
-
- def GetExpectations(self, test):
- return self._test_to_expectations[test]
-
- def GetExpectationsJsonForAllPlatforms(self):
- # Specify separators in order to get compact encoding.
- return ExpectationsJsonEncoder(separators=(',', ':')).encode(
- self._all_expectations)
-
- def Contains(self, test):
- return test in self._test_to_expectations
-
- def RemovePlatformFromFile(self, tests, platform, backup=False):
- """Remove the platform option from test expectations file.
-
- If a test is in the test list and has an option that matches the given
- platform, remove the matching platform and save the updated test back
- to the file. If no other platforms remaining after removal, delete the
- test from the file.
-
- Args:
- tests: list of tests that need to update..
- platform: which platform option to remove.
- backup: if true, the original test expectations file is saved as
- [self.TEST_LIST].orig.YYYYMMDDHHMMSS
-
- Returns:
- no
- """
-
- new_file = self._path + '.new'
- logging.debug('Original file: "%s"', self._path)
- logging.debug('New file: "%s"', new_file)
- f_orig = self._GetIterableExpectations()
- f_new = open(new_file, 'w')
-
- tests_removed = 0
- tests_updated = 0
- lineno = 0
- for line in f_orig:
- lineno += 1
- action = self._GetPlatformUpdateAction(line, lineno, tests,
- platform)
- if action == NO_CHANGE:
- # Save the original line back to the file
- logging.debug('No change to test: %s', line)
- f_new.write(line)
- elif action == REMOVE_TEST:
- tests_removed += 1
- logging.info('Test removed: %s', line)
- elif action == REMOVE_PLATFORM:
- parts = line.split(':')
- new_options = parts[0].replace(platform.upper() + ' ', '', 1)
- new_line = ('%s:%s' % (new_options, parts[1]))
- f_new.write(new_line)
- tests_updated += 1
- logging.info('Test updated: ')
- logging.info(' old: %s', line)
- logging.info(' new: %s', new_line)
- elif action == ADD_PLATFORMS_EXCEPT_THIS:
- parts = line.split(':')
- new_options = parts[0]
- for p in self.PLATFORMS:
- if not p == platform:
- new_options += p.upper() + ' '
- new_line = ('%s:%s' % (new_options, parts[1]))
- f_new.write(new_line)
- tests_updated += 1
- logging.info('Test updated: ')
- logging.info(' old: %s', line)
- logging.info(' new: %s', new_line)
- else:
- logging.error('Unknown update action: %d; line: %s',
- action, line)
-
- logging.info('Total tests removed: %d', tests_removed)
- logging.info('Total tests updated: %d', tests_updated)
-
- f_orig.close()
- f_new.close()
-
- if backup:
- date_suffix = time.strftime('%Y%m%d%H%M%S',
- time.localtime(time.time()))
- backup_file = ('%s.orig.%s' % (self._path, date_suffix))
- if os.path.exists(backup_file):
- os.remove(backup_file)
- logging.info('Saving original file to "%s"', backup_file)
- os.rename(self._path, backup_file)
- else:
- os.remove(self._path)
-
- logging.debug('Saving new file to "%s"', self._path)
- os.rename(new_file, self._path)
- return True
-
- def ParseExpectationsLine(self, line, lineno):
- """Parses a line from test_expectations.txt and returns a tuple
- with the test path, options as a list, expectations as a list."""
- line = StripComments(line)
- if not line:
- return (None, None, None)
-
- options = []
- if line.find(":") is -1:
- test_and_expectation = line.split("=")
- else:
- parts = line.split(":")
- options = self._GetOptionsList(parts[0])
- test_and_expectation = parts[1].split('=')
-
- test = test_and_expectation[0].strip()
- if (len(test_and_expectation) is not 2):
- self._AddError(lineno, "Missing expectations.",
- test_and_expectation)
- expectations = None
- else:
- expectations = self._GetOptionsList(test_and_expectation[1])
-
- return (test, options, expectations)
-
- def _GetPlatformUpdateAction(self, line, lineno, tests, platform):
- """Check the platform option and return the action needs to be taken.
-
- Args:
- line: current line in test expectations file.
- lineno: current line number of line
- tests: list of tests that need to update..
- platform: which platform option to remove.
-
- Returns:
- NO_CHANGE: no change to the line (comments, test not in the list etc)
- REMOVE_TEST: remove the test from file.
- REMOVE_PLATFORM: remove this platform option from the test.
- ADD_PLATFORMS_EXCEPT_THIS: add all the platforms except this one.
- """
- test, options, expectations = self.ParseExpectationsLine(line, lineno)
- if not test or test not in tests:
- return NO_CHANGE
-
- has_any_platform = False
- for option in options:
- if option in self.PLATFORMS:
- has_any_platform = True
- if not option == platform:
- return REMOVE_PLATFORM
-
- # If there is no platform specified, then it means apply to all
- # platforms. Return the action to add all the platforms except this
- # one.
- if not has_any_platform:
- return ADD_PLATFORMS_EXCEPT_THIS
-
- return REMOVE_TEST
-
- def _HasValidModifiersForCurrentPlatform(self, options, lineno,
- test_and_expectations, modifiers):
- """Returns true if the current platform is in the options list or if
- no platforms are listed and if there are no fatal errors in the
- options list.
-
- Args:
- options: List of lowercase options.
- lineno: The line in the file where the test is listed.
- test_and_expectations: The path and expectations for the test.
- modifiers: The set to populate with modifiers.
- """
- has_any_platform = False
- has_bug_id = False
- for option in options:
- if option in self.MODIFIERS:
- modifiers.add(option)
- elif option in self.PLATFORMS:
- has_any_platform = True
- elif option.startswith('bug'):
- has_bug_id = True
- elif option not in self.BUILD_TYPES:
- self._AddError(lineno, 'Invalid modifier for test: %s' %
- option, test_and_expectations)
-
- if has_any_platform and not self._MatchPlatform(options):
- return False
-
- if not has_bug_id and 'wontfix' not in options:
- # TODO(ojan): Turn this into an AddError call once all the
- # tests have BUG identifiers.
- self._LogNonFatalError(lineno, 'Test lacks BUG modifier.',
- test_and_expectations)
-
- if 'release' in options or 'debug' in options:
- if self._is_debug_mode and 'debug' not in options:
- return False
- if not self._is_debug_mode and 'release' not in options:
- return False
-
- if 'wontfix' in options and 'defer' in options:
- self._AddError(lineno, 'Test cannot be both DEFER and WONTFIX.',
- test_and_expectations)
-
- if self._is_lint_mode and 'rebaseline' in options:
- self._AddError(lineno, 'REBASELINE should only be used for running'
- 'rebaseline.py. Cannot be checked in.', test_and_expectations)
-
- return True
-
- def _MatchPlatform(self, options):
- """Match the list of options against our specified platform. If any
- of the options prefix-match self._platform, return True. This handles
- the case where a test is marked WIN and the platform is WIN-VISTA.
-
- Args:
- options: list of options
- """
- for opt in options:
- if self._platform.startswith(opt):
- return True
- return False
-
- def _AddToAllExpectations(self, test, options, expectations):
- # Make all paths unix-style so the dashboard doesn't need to.
- test = test.replace('\\', '/')
- if not test in self._all_expectations:
- self._all_expectations[test] = []
- self._all_expectations[test].append(
- ModifiersAndExpectations(options, expectations))
-
- def _Read(self, expectations):
- """For each test in an expectations iterable, generate the
- expectations for it."""
- lineno = 0
- for line in expectations:
- lineno += 1
-
- test_list_path, options, expectations = \
- self.ParseExpectationsLine(line, lineno)
- if not expectations:
- continue
-
- self._AddToAllExpectations(test_list_path,
- " ".join(options).upper(),
- " ".join(expectations).upper())
-
- modifiers = set()
- if options and not self._HasValidModifiersForCurrentPlatform(
- options, lineno, test_list_path, modifiers):
- continue
-
- expectations = self._ParseExpectations(expectations, lineno,
- test_list_path)
-
- if 'slow' in options and TIMEOUT in expectations:
- self._AddError(lineno,
- 'A test can not be both slow and timeout. If it times out '
- 'indefinitely, then it should be just timeout.',
- test_list_path)
-
- full_path = os.path.join(path_utils.LayoutTestsDir(),
- test_list_path)
- full_path = os.path.normpath(full_path)
- # WebKit's way of skipping tests is to add a -disabled suffix.
- # So we should consider the path existing if the path or the
- # -disabled version exists.
- if (self._tests_are_present and not os.path.exists(full_path)
- and not os.path.exists(full_path + '-disabled')):
- # Log a non fatal error here since you hit this case any
- # time you update test_expectations.txt without syncing
- # the LayoutTests directory
- self._LogNonFatalError(lineno, 'Path does not exist.',
- test_list_path)
- continue
-
- if not self._full_test_list:
- tests = [test_list_path]
- else:
- tests = self._ExpandTests(test_list_path)
-
- self._AddTests(tests, expectations, test_list_path, lineno,
- modifiers, options)
-
- if not self._suppress_errors and (
- len(self._errors) or len(self._non_fatal_errors)):
- if self._is_debug_mode:
- build_type = 'DEBUG'
- else:
- build_type = 'RELEASE'
- print "\nFAILURES FOR PLATFORM: %s, BUILD_TYPE: %s" \
- % (self._platform.upper(), build_type)
-
- for error in self._non_fatal_errors:
- logging.error(error)
- if len(self._errors):
- raise SyntaxError('\n'.join(map(str, self._errors)))
-
- # Now add in the tests that weren't present in the expectations file
- expectations = set([PASS])
- options = []
- modifiers = []
- if self._full_test_list:
- for test in self._full_test_list:
- if not test in self._test_list_paths:
- self._AddTest(test, modifiers, expectations, options)
-
- def _GetOptionsList(self, listString):
- return [part.strip().lower() for part in listString.strip().split(' ')]
-
- def _ParseExpectations(self, expectations, lineno, test_list_path):
- result = set()
- for part in expectations:
- if not part in self.EXPECTATIONS:
- self._AddError(lineno, 'Unsupported expectation: %s' % part,
- test_list_path)
- continue
- expectation = self.EXPECTATIONS[part]
- result.add(expectation)
- return result
-
- def _ExpandTests(self, test_list_path):
- """Convert the test specification to an absolute, normalized
- path and make sure directories end with the OS path separator."""
- path = os.path.join(path_utils.LayoutTestsDir(), test_list_path)
- path = os.path.normpath(path)
- path = self._FixDir(path)
-
- result = []
- for test in self._full_test_list:
- if test.startswith(path):
- result.append(test)
- return result
-
- def _FixDir(self, path):
- """Check to see if the path points to a directory, and if so, append
- the directory separator if necessary."""
- if self._tests_are_present:
- if os.path.isdir(path):
- path = os.path.join(path, '')
- else:
- # If we can't check the filesystem to see if this is a directory,
- # we assume that files w/o an extension are directories.
- # TODO(dpranke): What happens w/ LayoutTests/css2.1 ?
- if os.path.splitext(path)[1] == '':
- path = os.path.join(path, '')
- return path
-
- def _AddTests(self, tests, expectations, test_list_path, lineno, modifiers,
- options):
- for test in tests:
- if self._AlreadySeenTest(test, test_list_path, lineno):
- continue
-
- self._ClearExpectationsForTest(test, test_list_path)
- self._AddTest(test, modifiers, expectations, options)
-
- def _AddTest(self, test, modifiers, expectations, options):
- """Sets the expected state for a given test.
-
- This routine assumes the test has not been added before. If it has,
- use _ClearExpectationsForTest() to reset the state prior to
- calling this.
-
- Args:
- test: test to add
- modifiers: sequence of modifier keywords ('wontfix', 'slow', etc.)
- expectations: sequence of expectations (PASS, IMAGE, etc.)
- options: sequence of keywords and bug identifiers."""
- self._test_to_expectations[test] = expectations
- for expectation in expectations:
- self._expectation_to_tests[expectation].add(test)
-
- self._test_to_options[test] = options
- self._test_to_modifiers[test] = set()
- for modifier in modifiers:
- mod_value = self.MODIFIERS[modifier]
- self._modifier_to_tests[mod_value].add(test)
- self._test_to_modifiers[test].add(mod_value)
-
- if 'wontfix' in modifiers:
- self._timeline_to_tests[WONTFIX].add(test)
- elif 'defer' in modifiers:
- self._timeline_to_tests[DEFER].add(test)
- else:
- self._timeline_to_tests[NOW].add(test)
-
- if 'skip' in modifiers:
- self._result_type_to_tests[SKIP].add(test)
- elif expectations == set([PASS]):
- self._result_type_to_tests[PASS].add(test)
- elif len(expectations) > 1:
- self._result_type_to_tests[FLAKY].add(test)
- else:
- self._result_type_to_tests[FAIL].add(test)
-
- def _ClearExpectationsForTest(self, test, test_list_path):
- """Remove prexisting expectations for this test.
- This happens if we are seeing a more precise path
- than a previous listing.
- """
- if test in self._test_list_paths:
- self._test_to_expectations.pop(test, '')
- self._RemoveFromSets(test, self._expectation_to_tests)
- self._RemoveFromSets(test, self._modifier_to_tests)
- self._RemoveFromSets(test, self._timeline_to_tests)
- self._RemoveFromSets(test, self._result_type_to_tests)
-
- self._test_list_paths[test] = os.path.normpath(test_list_path)
-
- def _RemoveFromSets(self, test, dict):
- """Removes the given test from the sets in the dictionary.
-
- Args:
- test: test to look for
- dict: dict of sets of files"""
- for set_of_tests in dict.itervalues():
- if test in set_of_tests:
- set_of_tests.remove(test)
-
- def _AlreadySeenTest(self, test, test_list_path, lineno):
- """Returns true if we've already seen a more precise path for this test
- than the test_list_path.
- """
- if not test in self._test_list_paths:
- return False
-
- prev_base_path = self._test_list_paths[test]
- if (prev_base_path == os.path.normpath(test_list_path)):
- self._AddError(lineno, 'Duplicate expectations.', test)
- return True
-
- # Check if we've already seen a more precise path.
- return prev_base_path.startswith(os.path.normpath(test_list_path))
-
- def _AddError(self, lineno, msg, path):
- """Reports an error that will prevent running the tests. Does not
- immediately raise an exception because we'd like to aggregate all the
- errors so they can all be printed out."""
- self._errors.append('\nLine:%s %s %s' % (lineno, msg, path))
-
- def _LogNonFatalError(self, lineno, msg, path):
- """Reports an error that will not prevent running the tests. These are
- still errors, but not bad enough to warrant breaking test running."""
- self._non_fatal_errors.append('Line:%s %s %s' % (lineno, msg, path))

Powered by Google App Engine
This is Rietveld 408576698