1b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang# Copyright 2017 The Chromium Authors. All rights reserved. 2b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang# Use of this source code is governed by a BSD-style license that can be 3b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang# found in the LICENSE file. 4b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 5b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangimport re 6b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 7b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 8b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangclass ParseError(Exception): 9b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang pass 10b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 11b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 12b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangclass Expectation(object): 13b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def __init__(self, reason, test, conditions, results): 14b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """Constructor for expectations. 15b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 16b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang Args: 17b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang reason: String that indicates the reason for disabling. 18b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang test: String indicating which test is being disabled. 19b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang conditions: List of tags indicating which conditions to disable for. 20b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang Conditions are combined using logical and. Example: ['Mac', 'Debug'] 21b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang results: List of outcomes for test. Example: ['Skip', 'Pass'] 22b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """ 23b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang assert isinstance(reason, basestring) or reason is None 24b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._reason = reason 25b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang assert isinstance(test, basestring) 26b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._test = test 27b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang assert isinstance(conditions, list) 28b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._conditions = conditions 29b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang assert isinstance(results, list) 30b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._results = results 31b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 32b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def __eq__(self, other): 33b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return (self.reason == other.reason and 34b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self.test == other.test and 35b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self.conditions == other.conditions and 36b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self.results == other.results) 37b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 38b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 39b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def reason(self): 40b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._reason 41b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 42b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 43b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def test(self): 44b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._test 45b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 46b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 47b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def conditions(self): 48b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._conditions 49b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 50b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 51b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def results(self): 52b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._results 53b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 54b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 55b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangclass TestExpectationParser(object): 56b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """Parse expectations data in TA/DA format. 57b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 58b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang This parser covers the 'tagged' test lists format in: 59b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang bit.ly/chromium-test-list-format 60b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 61b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang Takes raw expectations data as a string read from the TA/DA expectation file 62b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang in the format: 63b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 64b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # This is an example expectation file. 65b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # 66b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # tags: Mac Mac10.10 Mac10.11 67b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # tags: Win Win8 68b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 69b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang crbug.com/123 [ Win ] benchmark/story [ Skip ] 70b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang ... 71b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang """ 72b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 73b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang TAG_TOKEN = '# tags:' 74b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _MATCH_STRING = r'^(?:(crbug.com/\d+) )?' # The bug field (optional). 75b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _MATCH_STRING += r'(?:\[ (.+) \] )?' # The label field (optional). 76b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _MATCH_STRING += r'(\S+) ' # The test path field. 77b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _MATCH_STRING += r'\[ ([^\[.]+) \]' # The expectation field. 78b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang _MATCH_STRING += r'(\s+#.*)?$' # End comment (optional). 79b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang MATCHER = re.compile(_MATCH_STRING) 80b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 81b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def __init__(self, raw_data): 82b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._tags = [] 83b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._expectations = [] 84b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._ParseRawExpectationData(raw_data) 85b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 86b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def _ParseRawExpectationData(self, raw_data): 87b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang for count, line in list(enumerate(raw_data.splitlines(), start=1)): 88b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # Handle metadata and comments. 89b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang if line.startswith(self.TAG_TOKEN): 90b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang for word in line[len(self.TAG_TOKEN):].split(): 91b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # Expectations must be after all tags are declared. 92b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang if self._expectations: 93b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang raise ParseError('Tag found after first expectation.') 94b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._tags.append(word) 95b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang elif line.startswith('#') or not line: 96b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang continue # Ignore, it is just a comment or empty. 97b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang else: 98b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._expectations.append( 99b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang self._ParseExpectationLine(count, line, self._tags)) 100b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 101b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def _ParseExpectationLine(self, line_number, line, tags): 102b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang match = self.MATCHER.match(line) 103b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang if not match: 104b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang raise ParseError( 105b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 'Expectation has invalid syntax on line %d: %s' 106b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang % (line_number, line)) 107b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang # Unused group is optional trailing comment. 108b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang reason, raw_conditions, test, results, _ = match.groups() 109b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang conditions = [c for c in raw_conditions.split()] if raw_conditions else [] 110b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 111b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang for c in conditions: 112b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang if c not in tags: 113b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang raise ParseError( 114b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 'Condition %s not found in expectations tag data. Line %d' 115b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang % (c, line_number)) 116b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return Expectation(reason, test, conditions, [r for r in results.split()]) 117b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 118b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 119b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def expectations(self): 120b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._expectations 121b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang 122b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang @property 123b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang def tags(self): 124b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang return self._tags 125