struct.py revision c2c48ffdd623b4e58b34115d1521b0988a42b217
12c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# Copyright (C) 2014 The Android Open Source Project
22c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil#
32c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# Licensed under the Apache License, Version 2.0 (the "License");
42c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# you may not use this file except in compliance with the License.
52c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# You may obtain a copy of the License at
62c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil#
72c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil#   http://www.apache.org/licenses/LICENSE-2.0
82c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil#
92c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# Unless required by applicable law or agreed to in writing, software
102c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# distributed under the License is distributed on an "AS IS" BASIS,
112c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# See the License for the specific language governing permissions and
132c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil# limitations under the License.
142c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
152c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilfrom common.logger import Logger
162c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilfrom common.mixins import EqualityMixin, PrintableMixin
172c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
182c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilimport re
192c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
202c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilclass CheckerFile(PrintableMixin):
212c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
222c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __init__(self, fileName):
232c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.fileName = fileName
242c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.testCases = []
252c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
262c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def addTestCase(self, new_test_case):
272c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.testCases.append(new_test_case)
282c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
292c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __eq__(self, other):
302c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return isinstance(other, self.__class__) \
312c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.testCases == other.testCases
322c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
332c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
342c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilclass TestCase(PrintableMixin):
352c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
362c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __init__(self, parent, name, startLineNo):
372c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    assert isinstance(parent, CheckerFile)
382c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
392c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.parent = parent
402c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.name = name
412c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.assertions = []
422c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.startLineNo = startLineNo
432c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
442c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    if not self.name:
452c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil      Logger.fail("Test case does not have a name", self.parent.fileName, self.startLineNo)
462c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
472c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.parent.addTestCase(self)
482c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
492c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @property
502c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def fileName(self):
512c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return self.parent.fileName
522c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
532c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def addAssertion(self, new_assertion):
542c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.assertions.append(new_assertion)
552c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
562c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __eq__(self, other):
572c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return isinstance(other, self.__class__) \
582c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.name == other.name \
592c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.assertions == other.assertions
602c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
612c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
622c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilclass TestAssertion(PrintableMixin):
632c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
642c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  class Variant(object):
652c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    """Supported types of assertions."""
662c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    InOrder, DAG, Not = range(3)
672c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
682c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __init__(self, parent, variant, originalText, lineNo):
692c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    assert isinstance(parent, TestCase)
702c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
712c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.parent = parent
722c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.variant = variant
732c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.expressions = []
742c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.lineNo = lineNo
752c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.originalText = originalText
762c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
772c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.parent.addAssertion(self)
782c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
792c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @property
802c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def fileName(self):
812c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return self.parent.fileName
822c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
832c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def addExpression(self, new_expression):
842c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    assert isinstance(new_expression, RegexExpression)
852c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    if self.variant == TestAssertion.Variant.Not:
862c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil      if new_expression.variant == RegexExpression.Variant.VarDef:
872c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil        Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
882c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.expressions.append(new_expression)
892c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
902c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def toRegex(self):
912c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    """ Returns a regex pattern for this entire assertion. Only used in tests. """
922c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    regex = ""
932c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    for expression in self.expressions:
942c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil      if expression.variant == RegexExpression.Variant.Separator:
952c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil        regex = regex + ", "
962c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil      else:
972c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil        regex = regex + "(" + expression.pattern + ")"
982c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return regex
992c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1002c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __eq__(self, other):
1012c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return isinstance(other, self.__class__) \
1022c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.variant == other.variant \
1032c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.expressions == other.expressions
1042c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1052c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1062c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdilclass RegexExpression(EqualityMixin, PrintableMixin):
1072c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1082c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  class Variant(object):
1092c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    """Supported language constructs."""
1102c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    Text, Pattern, VarRef, VarDef, Separator = range(5)
1112c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1122c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  class Regex(object):
1132c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    rName = r"([a-zA-Z][a-zA-Z0-9]*)"
1142c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    rRegex = r"(.+?)"
1152c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    rPatternStartSym = r"(\{\{)"
1162c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    rPatternEndSym = r"(\}\})"
117c2c48ffdd623b4e58b34115d1521b0988a42b217David Brazdil    rVariableStartSym = r"(<<)"
118c2c48ffdd623b4e58b34115d1521b0988a42b217David Brazdil    rVariableEndSym = r"(>>)"
1192c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    rVariableSeparator = r"(:)"
1202c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1212c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    regexPattern = rPatternStartSym + rRegex + rPatternEndSym
1222c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    regexVariableReference = rVariableStartSym + rName + rVariableEndSym
1232c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym
1242c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1252c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __init__(self, variant, name, pattern):
1262c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.variant = variant
1272c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.name = name
1282c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    self.pattern = pattern
1292c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1302c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def __eq__(self, other):
1312c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return isinstance(other, self.__class__) \
1322c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.variant == other.variant \
1332c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.name == other.name \
1342c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil       and self.pattern == other.pattern
1352c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1362c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @staticmethod
1372c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def createSeparator():
1382c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return RegexExpression(RegexExpression.Variant.Separator, None, None)
1392c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1402c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @staticmethod
1412c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def createText(text):
1422c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text))
1432c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1442c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @staticmethod
1452c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def createPattern(pattern):
1462c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return RegexExpression(RegexExpression.Variant.Pattern, None, pattern)
1472c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1482c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @staticmethod
1492c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def createVariableReference(name):
1502c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    assert re.match(RegexExpression.Regex.rName, name)
1512c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return RegexExpression(RegexExpression.Variant.VarRef, name, None)
1522c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil
1532c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  @staticmethod
1542c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil  def createVariableDefinition(name, pattern):
1552c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    assert re.match(RegexExpression.Regex.rName, name)
1562c27f2ccf316aebf96cf365d33d2834a8206444dDavid Brazdil    return RegexExpression(RegexExpression.Variant.VarDef, name, pattern)
157