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
15import enum
16
17from common.logger import Logger
18from common.mixins import EqualityMixin, PrintableMixin
19
20import re
21
22
23class CheckerFile(PrintableMixin):
24
25  def __init__(self, filename):
26    self.file_name = filename
27    self.test_cases = []
28
29  def add_test_case(self, new_test_case):
30    self.test_cases.append(new_test_case)
31
32  def test_cases_for_arch(self, target_arch):
33    return [t for t in self.test_cases if t.test_arch == target_arch]
34
35  def __eq__(self, other):
36    return isinstance(other, self.__class__) and self.test_cases == other.test_cases
37
38
39class TestCase(PrintableMixin):
40
41  def __init__(self, parent, name, start_line_no, test_arch=None, for_debuggable=False):
42    assert isinstance(parent, CheckerFile)
43
44    self.parent = parent
45    self.name = name
46    self.statements = []
47    self.start_line_no = start_line_no
48    self.test_arch = test_arch
49    self.for_debuggable = for_debuggable
50
51    if not self.name:
52      Logger.fail("Test case does not have a name", self.filename, self.start_line_no)
53
54    self.parent.add_test_case(self)
55
56  @property
57  def filename(self):
58    return self.parent.file_name
59
60  def add_statement(self, new_statement):
61    self.statements.append(new_statement)
62
63  def __eq__(self, other):
64    return (isinstance(other, self.__class__)
65            and self.name == other.name
66            and self.statements == other.statements)
67
68
69class TestStatement(PrintableMixin):
70  class Variant(enum.IntEnum):
71    """Supported types of statements."""
72    IN_ORDER, NEXT_LINE, DAG, NOT, EVAL, IF, ELIF, ELSE, FI = range(9)
73
74  def __init__(self, parent, variant, original_text, line_no):
75    assert isinstance(parent, TestCase)
76
77    self.parent = parent
78    self.variant = variant
79    self.expressions = []
80    self.line_no = line_no
81    self.original_text = original_text
82
83    self.parent.add_statement(self)
84
85  @property
86  def filename(self):
87    return self.parent.filename
88
89  def is_pattern_match_content_statement(self):
90    return self.variant in [TestStatement.Variant.IN_ORDER,
91                            TestStatement.Variant.NEXT_LINE,
92                            TestStatement.Variant.DAG,
93                            TestStatement.Variant.NOT]
94
95  def is_eval_content_statement(self):
96    return self.variant in [TestStatement.Variant.EVAL,
97                            TestStatement.Variant.IF,
98                            TestStatement.Variant.ELIF]
99
100  def is_no_content_statement(self):
101    return self.variant in [TestStatement.Variant.ELSE,
102                            TestStatement.Variant.FI]
103
104  def add_expression(self, new_expression):
105    assert isinstance(new_expression, TestExpression)
106    if self.variant == TestStatement.Variant.NOT:
107      if new_expression.variant == TestExpression.Variant.VAR_DEF:
108        Logger.fail("CHECK-NOT lines cannot define variables", self.filename, self.line_no)
109    self.expressions.append(new_expression)
110
111  def to_regex(self):
112    """ Returns a regex pattern for this entire statement. Only used in tests. """
113    regex = ""
114    for expression in self.expressions:
115      if expression.variant == TestExpression.Variant.SEPARATOR:
116        regex = regex + ", "
117      else:
118        regex = regex + "(" + expression.text + ")"
119    return regex
120
121  def __eq__(self, other):
122    return (isinstance(other, self.__class__)
123            and self.variant == other.variant
124            and self.expressions == other.expressions)
125
126
127class TestExpression(EqualityMixin, PrintableMixin):
128  class Variant:
129    """Supported language constructs."""
130    PLAIN_TEXT, PATTERN, VAR_REF, VAR_DEF, SEPARATOR = range(5)
131
132  class Regex:
133    R_NAME = r"([a-zA-Z][a-zA-Z0-9]*)"
134    R_REGEX = r"(.+?)"
135    R_PATTERN_START_SYM = r"(\{\{)"
136    R_PATTERN_END_SYM = r"(\}\})"
137    R_VARIABLE_START_SYM = r"(<<)"
138    R_VARIABLE_END_SYM = r"(>>)"
139    R_VARIABLE_SEPARATOR = r"(:)"
140    R_VARIABLE_DEFINITION_BODY = R_NAME + R_VARIABLE_SEPARATOR + R_REGEX
141
142    REGEX_PATTERN = R_PATTERN_START_SYM + R_REGEX + R_PATTERN_END_SYM
143    REGEX_VARIABLE_REFERENCE = R_VARIABLE_START_SYM + R_NAME + R_VARIABLE_END_SYM
144    REGEX_VARIABLE_DEFINITION = (R_VARIABLE_START_SYM + R_VARIABLE_DEFINITION_BODY
145                                 + R_VARIABLE_END_SYM)
146
147  def __init__(self, variant, name, text):
148    self.variant = variant
149    self.name = name
150    self.text = text
151
152  def __eq__(self, other):
153    return (isinstance(other, self.__class__)
154            and self.variant == other.variant
155            and self.name == other.name
156            and self.text == other.text)
157
158  @staticmethod
159  def create_separator():
160    return TestExpression(TestExpression.Variant.SEPARATOR, None, None)
161
162  @staticmethod
163  def create_plain_text(text):
164    return TestExpression(TestExpression.Variant.PLAIN_TEXT, None, text)
165
166  @staticmethod
167  def create_pattern_from_plain_text(text):
168    return TestExpression(TestExpression.Variant.PATTERN, None, re.escape(text))
169
170  @staticmethod
171  def create_pattern(pattern):
172    return TestExpression(TestExpression.Variant.PATTERN, None, pattern)
173
174  @staticmethod
175  def create_variable_reference(name):
176    assert re.match(TestExpression.Regex.R_NAME, name)
177    return TestExpression(TestExpression.Variant.VAR_REF, name, None)
178
179  @staticmethod
180  def create_variable_definition(name, pattern):
181    assert re.match(TestExpression.Regex.R_NAME, name)
182    return TestExpression(TestExpression.Variant.VAR_DEF, name, pattern)
183