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