struct.py revision 5e2c8d323fbab4db8a71041ff94b6baf3953bca9
1# Copyright (C) 2014 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#   http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from common.logger import Logger
16from common.mixins import EqualityMixin, PrintableMixin
17
18import re
19
20class CheckerFile(PrintableMixin):
21
22  def __init__(self, fileName):
23    self.fileName = fileName
24    self.testCases = []
25
26  def addTestCase(self, new_test_case):
27    self.testCases.append(new_test_case)
28
29  def testCasesForArch(self, targetArch):
30    return [t for t in self.testCases if t.testArch == targetArch]
31
32  def __eq__(self, other):
33    return isinstance(other, self.__class__) \
34       and self.testCases == other.testCases
35
36
37class TestCase(PrintableMixin):
38
39  def __init__(self, parent, name, startLineNo, testArch = None):
40    assert isinstance(parent, CheckerFile)
41
42    self.parent = parent
43    self.name = name
44    self.assertions = []
45    self.startLineNo = startLineNo
46    self.testArch = testArch
47
48    if not self.name:
49      Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
50
51    self.parent.addTestCase(self)
52
53  @property
54  def fileName(self):
55    return self.parent.fileName
56
57  def addAssertion(self, new_assertion):
58    if new_assertion.variant == TestAssertion.Variant.NextLine:
59      if not self.assertions or \
60         (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \
61          self.assertions[-1].variant != TestAssertion.Variant.NextLine):
62        Logger.fail("A next-line assertion can only be placed after an "
63                    "in-order assertion or another next-line assertion.",
64                    new_assertion.fileName, new_assertion.lineNo)
65    self.assertions.append(new_assertion)
66
67  def __eq__(self, other):
68    return isinstance(other, self.__class__) \
69       and self.name == other.name \
70       and self.assertions == other.assertions
71
72
73class TestAssertion(PrintableMixin):
74
75  class Variant(object):
76    """Supported types of assertions."""
77    InOrder, NextLine, DAG, Not = range(4)
78
79  def __init__(self, parent, variant, originalText, lineNo):
80    assert isinstance(parent, TestCase)
81
82    self.parent = parent
83    self.variant = variant
84    self.expressions = []
85    self.lineNo = lineNo
86    self.originalText = originalText
87
88    self.parent.addAssertion(self)
89
90  @property
91  def fileName(self):
92    return self.parent.fileName
93
94  def addExpression(self, new_expression):
95    assert isinstance(new_expression, RegexExpression)
96    if self.variant == TestAssertion.Variant.Not:
97      if new_expression.variant == RegexExpression.Variant.VarDef:
98        Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
99    self.expressions.append(new_expression)
100
101  def toRegex(self):
102    """ Returns a regex pattern for this entire assertion. Only used in tests. """
103    regex = ""
104    for expression in self.expressions:
105      if expression.variant == RegexExpression.Variant.Separator:
106        regex = regex + ", "
107      else:
108        regex = regex + "(" + expression.pattern + ")"
109    return regex
110
111  def __eq__(self, other):
112    return isinstance(other, self.__class__) \
113       and self.variant == other.variant \
114       and self.expressions == other.expressions
115
116
117class RegexExpression(EqualityMixin, PrintableMixin):
118
119  class Variant(object):
120    """Supported language constructs."""
121    Text, Pattern, VarRef, VarDef, Separator = range(5)
122
123  class Regex(object):
124    rName = r"([a-zA-Z][a-zA-Z0-9]*)"
125    rRegex = r"(.+?)"
126    rPatternStartSym = r"(\{\{)"
127    rPatternEndSym = r"(\}\})"
128    rVariableStartSym = r"(<<)"
129    rVariableEndSym = r"(>>)"
130    rVariableSeparator = r"(:)"
131
132    regexPattern = rPatternStartSym + rRegex + rPatternEndSym
133    regexVariableReference = rVariableStartSym + rName + rVariableEndSym
134    regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym
135
136  def __init__(self, variant, name, pattern):
137    self.variant = variant
138    self.name = name
139    self.pattern = pattern
140
141  def __eq__(self, other):
142    return isinstance(other, self.__class__) \
143       and self.variant == other.variant \
144       and self.name == other.name \
145       and self.pattern == other.pattern
146
147  @staticmethod
148  def createSeparator():
149    return RegexExpression(RegexExpression.Variant.Separator, None, None)
150
151  @staticmethod
152  def createText(text):
153    return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text))
154
155  @staticmethod
156  def createPattern(pattern):
157    return RegexExpression(RegexExpression.Variant.Pattern, None, pattern)
158
159  @staticmethod
160  def createVariableReference(name):
161    assert re.match(RegexExpression.Regex.rName, name)
162    return RegexExpression(RegexExpression.Variant.VarRef, name, None)
163
164  @staticmethod
165  def createVariableDefinition(name, pattern):
166    assert re.match(RegexExpression.Regex.rName, name)
167    return RegexExpression(RegexExpression.Variant.VarDef, name, pattern)
168