Home | History | Annotate | Download | only in checker
      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 
     15 from common.logger import Logger
     16 from common.mixins import EqualityMixin, PrintableMixin
     17 
     18 import re
     19 
     20 class 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 
     37 class 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 
     74 class 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 
    118 class 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