aboutsummaryrefslogtreecommitdiff
path: root/catapult/common/py_utils/py_utils/expectations_parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/common/py_utils/py_utils/expectations_parser.py')
-rw-r--r--catapult/common/py_utils/py_utils/expectations_parser.py124
1 files changed, 124 insertions, 0 deletions
diff --git a/catapult/common/py_utils/py_utils/expectations_parser.py b/catapult/common/py_utils/py_utils/expectations_parser.py
new file mode 100644
index 00000000..6fa94070
--- /dev/null
+++ b/catapult/common/py_utils/py_utils/expectations_parser.py
@@ -0,0 +1,124 @@
+# Copyright 2017 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.
+
+import re
+
+
+class ParseError(Exception):
+ pass
+
+
+class Expectation(object):
+ def __init__(self, reason, test, conditions, results):
+ """Constructor for expectations.
+
+ Args:
+ reason: String that indicates the reason for disabling.
+ test: String indicating which test is being disabled.
+ conditions: List of tags indicating which conditions to disable for.
+ Conditions are combined using logical and. Example: ['Mac', 'Debug']
+ results: List of outcomes for test. Example: ['Skip', 'Pass']
+ """
+ assert isinstance(reason, basestring) or reason is None
+ self._reason = reason
+ assert isinstance(test, basestring)
+ self._test = test
+ assert isinstance(conditions, list)
+ self._conditions = conditions
+ assert isinstance(results, list)
+ self._results = results
+
+ def __eq__(self, other):
+ return (self.reason == other.reason and
+ self.test == other.test and
+ self.conditions == other.conditions and
+ self.results == other.results)
+
+ @property
+ def reason(self):
+ return self._reason
+
+ @property
+ def test(self):
+ return self._test
+
+ @property
+ def conditions(self):
+ return self._conditions
+
+ @property
+ def results(self):
+ return self._results
+
+
+class TestExpectationParser(object):
+ """Parse expectations data in TA/DA format.
+
+ This parser covers the 'tagged' test lists format in:
+ bit.ly/chromium-test-list-format
+
+ Takes raw expectations data as a string read from the TA/DA expectation file
+ in the format:
+
+ # This is an example expectation file.
+ #
+ # tags: Mac Mac10.10 Mac10.11
+ # tags: Win Win8
+
+ crbug.com/123 [ Win ] benchmark/story [ Skip ]
+ ...
+ """
+
+ TAG_TOKEN = '# tags:'
+ _MATCH_STRING = r'^(?:(crbug.com/\d+) )?' # The bug field (optional).
+ _MATCH_STRING += r'(?:\[ (.+) \] )?' # The label field (optional).
+ _MATCH_STRING += r'(\S+) ' # The test path field.
+ _MATCH_STRING += r'\[ ([^\[.]+) \]' # The expectation field.
+ _MATCH_STRING += r'(\s+#.*)?$' # End comment (optional).
+ MATCHER = re.compile(_MATCH_STRING)
+
+ def __init__(self, raw_data):
+ self._tags = []
+ self._expectations = []
+ self._ParseRawExpectationData(raw_data)
+
+ def _ParseRawExpectationData(self, raw_data):
+ for count, line in list(enumerate(raw_data.splitlines(), start=1)):
+ # Handle metadata and comments.
+ if line.startswith(self.TAG_TOKEN):
+ for word in line[len(self.TAG_TOKEN):].split():
+ # Expectations must be after all tags are declared.
+ if self._expectations:
+ raise ParseError('Tag found after first expectation.')
+ self._tags.append(word)
+ elif line.startswith('#') or not line:
+ continue # Ignore, it is just a comment or empty.
+ else:
+ self._expectations.append(
+ self._ParseExpectationLine(count, line, self._tags))
+
+ def _ParseExpectationLine(self, line_number, line, tags):
+ match = self.MATCHER.match(line)
+ if not match:
+ raise ParseError(
+ 'Expectation has invalid syntax on line %d: %s'
+ % (line_number, line))
+ # Unused group is optional trailing comment.
+ reason, raw_conditions, test, results, _ = match.groups()
+ conditions = [c for c in raw_conditions.split()] if raw_conditions else []
+
+ for c in conditions:
+ if c not in tags:
+ raise ParseError(
+ 'Condition %s not found in expectations tag data. Line %d'
+ % (c, line_number))
+ return Expectation(reason, test, conditions, [r for r in results.split()])
+
+ @property
+ def expectations(self):
+ return self._expectations
+
+ @property
+ def tags(self):
+ return self._tags