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, forDebuggable = False):
40    assert isinstance(parent, CheckerFile)
41
42    self.parent = parent
43    self.name = name
44    self.statements = []
45    self.startLineNo = startLineNo
46    self.testArch = testArch
47    self.forDebuggable = forDebuggable
48
49    if not self.name:
50      Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
51
52    self.parent.addTestCase(self)
53
54  @property
55  def fileName(self):
56    return self.parent.fileName
57
58  def addStatement(self, new_statement):
59    self.statements.append(new_statement)
60
61  def __eq__(self, other):
62    return isinstance(other, self.__class__) \
63       and self.name == other.name \
64       and self.statements == other.statements
65
66
67class TestStatement(PrintableMixin):
68
69  class Variant(object):
70    """Supported types of statements."""
71    InOrder, NextLine, DAG, Not, Eval, If, Elif, Else, Fi = range(9)
72
73  def __init__(self, parent, variant, originalText, lineNo):
74    assert isinstance(parent, TestCase)
75
76    self.parent = parent
77    self.variant = variant
78    self.expressions = []
79    self.lineNo = lineNo
80    self.originalText = originalText
81
82    self.parent.addStatement(self)
83
84  @property
85  def fileName(self):
86    return self.parent.fileName
87
88  def isPatternMatchContentStatement(self):
89    return self.variant in [ TestStatement.Variant.InOrder,
90                             TestStatement.Variant.NextLine,
91                             TestStatement.Variant.DAG,
92                             TestStatement.Variant.Not ]
93
94  def isEvalContentStatement(self):
95    return self.variant in [ TestStatement.Variant.Eval,
96                             TestStatement.Variant.If,
97                             TestStatement.Variant.Elif ]
98
99  def isNoContentStatement(self):
100    return self.variant in [ TestStatement.Variant.Else,
101                             TestStatement.Variant.Fi ]
102
103  def addExpression(self, new_expression):
104    assert isinstance(new_expression, TestExpression)
105    if self.variant == TestStatement.Variant.Not:
106      if new_expression.variant == TestExpression.Variant.VarDef:
107        Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
108    self.expressions.append(new_expression)
109
110  def toRegex(self):
111    """ Returns a regex pattern for this entire statement. Only used in tests. """
112    regex = ""
113    for expression in self.expressions:
114      if expression.variant == TestExpression.Variant.Separator:
115        regex = regex + ", "
116      else:
117        regex = regex + "(" + expression.text + ")"
118    return regex
119
120  def __eq__(self, other):
121    return isinstance(other, self.__class__) \
122       and self.variant == other.variant \
123       and self.expressions == other.expressions
124
125
126class TestExpression(EqualityMixin, PrintableMixin):
127
128  class Variant(object):
129    """Supported language constructs."""
130    PlainText, Pattern, VarRef, VarDef, Separator = range(5)
131
132  class Regex(object):
133    rName = r"([a-zA-Z][a-zA-Z0-9]*)"
134    rRegex = r"(.+?)"
135    rPatternStartSym = r"(\{\{)"
136    rPatternEndSym = r"(\}\})"
137    rVariableStartSym = r"(<<)"
138    rVariableEndSym = r"(>>)"
139    rVariableSeparator = r"(:)"
140    rVariableDefinitionBody = rName + rVariableSeparator + rRegex
141
142    regexPattern = rPatternStartSym + rRegex + rPatternEndSym
143    regexVariableReference = rVariableStartSym + rName + rVariableEndSym
144    regexVariableDefinition = rVariableStartSym + rVariableDefinitionBody + rVariableEndSym
145
146  def __init__(self, variant, name, text):
147    self.variant = variant
148    self.name = name
149    self.text = text
150
151  def __eq__(self, other):
152    return isinstance(other, self.__class__) \
153       and self.variant == other.variant \
154       and self.name == other.name \
155       and self.text == other.text
156
157  @staticmethod
158  def createSeparator():
159    return TestExpression(TestExpression.Variant.Separator, None, None)
160
161  @staticmethod
162  def createPlainText(text):
163    return TestExpression(TestExpression.Variant.PlainText, None, text)
164
165  @staticmethod
166  def createPatternFromPlainText(text):
167    return TestExpression(TestExpression.Variant.Pattern, None, re.escape(text))
168
169  @staticmethod
170  def createPattern(pattern):
171    return TestExpression(TestExpression.Variant.Pattern, None, pattern)
172
173  @staticmethod
174  def createVariableReference(name):
175    assert re.match(TestExpression.Regex.rName, name)
176    return TestExpression(TestExpression.Variant.VarRef, name, None)
177
178  @staticmethod
179  def createVariableDefinition(name, pattern):
180    assert re.match(TestExpression.Regex.rName, name)
181    return TestExpression(TestExpression.Variant.VarDef, name, pattern)
182