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.assertions = [] 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 addAssertion(self, new_assertion): 59 if new_assertion.variant == TestAssertion.Variant.NextLine: 60 if not self.assertions or \ 61 (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \ 62 self.assertions[-1].variant != TestAssertion.Variant.NextLine): 63 Logger.fail("A next-line assertion can only be placed after an " 64 "in-order assertion or another next-line assertion.", 65 new_assertion.fileName, new_assertion.lineNo) 66 self.assertions.append(new_assertion) 67 68 def __eq__(self, other): 69 return isinstance(other, self.__class__) \ 70 and self.name == other.name \ 71 and self.assertions == other.assertions 72 73 74class TestAssertion(PrintableMixin): 75 76 class Variant(object): 77 """Supported types of assertions.""" 78 InOrder, NextLine, DAG, Not, Eval = range(5) 79 80 def __init__(self, parent, variant, originalText, lineNo): 81 assert isinstance(parent, TestCase) 82 83 self.parent = parent 84 self.variant = variant 85 self.expressions = [] 86 self.lineNo = lineNo 87 self.originalText = originalText 88 89 self.parent.addAssertion(self) 90 91 @property 92 def fileName(self): 93 return self.parent.fileName 94 95 def addExpression(self, new_expression): 96 assert isinstance(new_expression, TestExpression) 97 if self.variant == TestAssertion.Variant.Not: 98 if new_expression.variant == TestExpression.Variant.VarDef: 99 Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) 100 self.expressions.append(new_expression) 101 102 def toRegex(self): 103 """ Returns a regex pattern for this entire assertion. Only used in tests. """ 104 regex = "" 105 for expression in self.expressions: 106 if expression.variant == TestExpression.Variant.Separator: 107 regex = regex + ", " 108 else: 109 regex = regex + "(" + expression.text + ")" 110 return regex 111 112 def __eq__(self, other): 113 return isinstance(other, self.__class__) \ 114 and self.variant == other.variant \ 115 and self.expressions == other.expressions 116 117 118class TestExpression(EqualityMixin, PrintableMixin): 119 120 class Variant(object): 121 """Supported language constructs.""" 122 PlainText, Pattern, VarRef, VarDef, Separator = range(5) 123 124 class Regex(object): 125 rName = r"([a-zA-Z][a-zA-Z0-9]*)" 126 rRegex = r"(.+?)" 127 rPatternStartSym = r"(\{\{)" 128 rPatternEndSym = r"(\}\})" 129 rVariableStartSym = r"(<<)" 130 rVariableEndSym = r"(>>)" 131 rVariableSeparator = r"(:)" 132 rVariableDefinitionBody = rName + rVariableSeparator + rRegex 133 134 regexPattern = rPatternStartSym + rRegex + rPatternEndSym 135 regexVariableReference = rVariableStartSym + rName + rVariableEndSym 136 regexVariableDefinition = rVariableStartSym + rVariableDefinitionBody + rVariableEndSym 137 138 def __init__(self, variant, name, text): 139 self.variant = variant 140 self.name = name 141 self.text = text 142 143 def __eq__(self, other): 144 return isinstance(other, self.__class__) \ 145 and self.variant == other.variant \ 146 and self.name == other.name \ 147 and self.text == other.text 148 149 @staticmethod 150 def createSeparator(): 151 return TestExpression(TestExpression.Variant.Separator, None, None) 152 153 @staticmethod 154 def createPlainText(text): 155 return TestExpression(TestExpression.Variant.PlainText, None, text) 156 157 @staticmethod 158 def createPatternFromPlainText(text): 159 return TestExpression(TestExpression.Variant.Pattern, None, re.escape(text)) 160 161 @staticmethod 162 def createPattern(pattern): 163 return TestExpression(TestExpression.Variant.Pattern, None, pattern) 164 165 @staticmethod 166 def createVariableReference(name): 167 assert re.match(TestExpression.Regex.rName, name) 168 return TestExpression(TestExpression.Variant.VarRef, name, None) 169 170 @staticmethod 171 def createVariableDefinition(name, pattern): 172 assert re.match(TestExpression.Regex.rName, name) 173 return TestExpression(TestExpression.Variant.VarDef, name, pattern) 174