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