struct.py revision 711411957a433555eda4bcf8d1f05aabf04425e8
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 __eq__(self, other): 30 return isinstance(other, self.__class__) \ 31 and self.testCases == other.testCases 32 33 34class TestCase(PrintableMixin): 35 36 def __init__(self, parent, name, startLineNo): 37 assert isinstance(parent, CheckerFile) 38 39 self.parent = parent 40 self.name = name 41 self.assertions = [] 42 self.startLineNo = startLineNo 43 44 if not self.name: 45 Logger.fail("Test case does not have a name", self.fileName, self.startLineNo) 46 47 self.parent.addTestCase(self) 48 49 @property 50 def fileName(self): 51 return self.parent.fileName 52 53 def addAssertion(self, new_assertion): 54 if new_assertion.variant == TestAssertion.Variant.NextLine: 55 if not self.assertions or \ 56 (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \ 57 self.assertions[-1].variant != TestAssertion.Variant.NextLine): 58 Logger.fail("A next-line assertion can only be placed after an " 59 "in-order assertion or another next-line assertion.", 60 new_assertion.fileName, new_assertion.lineNo) 61 self.assertions.append(new_assertion) 62 63 def __eq__(self, other): 64 return isinstance(other, self.__class__) \ 65 and self.name == other.name \ 66 and self.assertions == other.assertions 67 68 69class TestAssertion(PrintableMixin): 70 71 class Variant(object): 72 """Supported types of assertions.""" 73 InOrder, NextLine, DAG, Not = range(4) 74 75 def __init__(self, parent, variant, originalText, lineNo): 76 assert isinstance(parent, TestCase) 77 78 self.parent = parent 79 self.variant = variant 80 self.expressions = [] 81 self.lineNo = lineNo 82 self.originalText = originalText 83 84 self.parent.addAssertion(self) 85 86 @property 87 def fileName(self): 88 return self.parent.fileName 89 90 def addExpression(self, new_expression): 91 assert isinstance(new_expression, RegexExpression) 92 if self.variant == TestAssertion.Variant.Not: 93 if new_expression.variant == RegexExpression.Variant.VarDef: 94 Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) 95 self.expressions.append(new_expression) 96 97 def toRegex(self): 98 """ Returns a regex pattern for this entire assertion. Only used in tests. """ 99 regex = "" 100 for expression in self.expressions: 101 if expression.variant == RegexExpression.Variant.Separator: 102 regex = regex + ", " 103 else: 104 regex = regex + "(" + expression.pattern + ")" 105 return regex 106 107 def __eq__(self, other): 108 return isinstance(other, self.__class__) \ 109 and self.variant == other.variant \ 110 and self.expressions == other.expressions 111 112 113class RegexExpression(EqualityMixin, PrintableMixin): 114 115 class Variant(object): 116 """Supported language constructs.""" 117 Text, Pattern, VarRef, VarDef, Separator = range(5) 118 119 class Regex(object): 120 rName = r"([a-zA-Z][a-zA-Z0-9]*)" 121 rRegex = r"(.+?)" 122 rPatternStartSym = r"(\{\{)" 123 rPatternEndSym = r"(\}\})" 124 rVariableStartSym = r"(<<)" 125 rVariableEndSym = r"(>>)" 126 rVariableSeparator = r"(:)" 127 128 regexPattern = rPatternStartSym + rRegex + rPatternEndSym 129 regexVariableReference = rVariableStartSym + rName + rVariableEndSym 130 regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym 131 132 def __init__(self, variant, name, pattern): 133 self.variant = variant 134 self.name = name 135 self.pattern = pattern 136 137 def __eq__(self, other): 138 return isinstance(other, self.__class__) \ 139 and self.variant == other.variant \ 140 and self.name == other.name \ 141 and self.pattern == other.pattern 142 143 @staticmethod 144 def createSeparator(): 145 return RegexExpression(RegexExpression.Variant.Separator, None, None) 146 147 @staticmethod 148 def createText(text): 149 return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text)) 150 151 @staticmethod 152 def createPattern(pattern): 153 return RegexExpression(RegexExpression.Variant.Pattern, None, pattern) 154 155 @staticmethod 156 def createVariableReference(name): 157 assert re.match(RegexExpression.Regex.rName, name) 158 return RegexExpression(RegexExpression.Variant.VarRef, name, None) 159 160 @staticmethod 161 def createVariableDefinition(name, pattern): 162 assert re.match(RegexExpression.Regex.rName, name) 163 return RegexExpression(RegexExpression.Variant.VarDef, name, pattern) 164