It can be useful for tests to evaluate small `assert`-like expressions.
This patch adds such support to Checker, with a new CHECK-EVAL line.
See README file for more details.
Change-Id: I184f7c8e8b53f7e93cfb08fcf9630b4724fa5412
state of the control-flow graph before and after each optimization pass
against a set of assertions specified alongside the tests.
-Tests are written in Java, turned into DEX and compiled with the Optimizing
-compiler. "Check lines" are assertions formatted as comments of the Java file.
-They begin with prefix 'CHECK' followed by a pattern that the engine attempts
-to match in the compiler-generated output.
+Tests are written in Java or Smali, turned into DEX and compiled with the
+Optimizing compiler. "Check lines" are assertions formatted as comments of the
+source file. They begin with prefix "/// CHECK" or "## CHECK", respectively,
+followed by a pattern that the engine attempts to match in the compiler output.
Assertions are tested in groups which correspond to the individual compiler
passes. Each group of check lines therefore must start with a 'CHECK-START'
Matching of check lines is carried out in the order of appearance in the
source file. There are three types of check lines:
- - CHECK: Must match an output line which appears in the output group
- later than lines matched against any preceeding checks. Output
- lines must therefore match the check lines in the same order.
- These are referred to as "in-order" checks in the code.
- - CHECK-DAG: Must match an output line which appears in the output group
- later than lines matched against any preceeding in-order checks.
- In other words, the order of output lines does not matter
- between consecutive DAG checks.
- - CHECK-NOT: Must not match any output line which appears in the output group
- later than lines matched against any preceeding checks and
- earlier than lines matched against any subsequent checks.
- Surrounding non-negative checks (or boundaries of the group)
- therefore create a scope within which the assertion is verified.
+ - CHECK: Must match an output line which appears in the output group
+ later than lines matched against any preceeding checks. Output
+ lines must therefore match the check lines in the same order.
+ These are referred to as "in-order" checks in the code.
+ - CHECK-DAG: Must match an output line which appears in the output group
+ later than lines matched against any preceeding in-order checks.
+ In other words, the order of output lines does not matter
+ between consecutive DAG checks.
+ - CHECK-NOT: Must not match any output line which appears in the output group
+ later than lines matched against any preceeding checks and
+ earlier than lines matched against any subsequent checks.
+ Surrounding non-negative checks (or boundaries of the group)
+ therefore create a scope within which the assertion is verified.
+ - CHECK-NEXT: Must match the output line which comes right after the line which
+ matched the previous check. Cannot be used after any but the
+ in-order CHECK.
+ - CHECK-EVAL: Specifies a Python expression which must evaluate to 'True'.
Check-line patterns are treated as plain text rather than regular expressions
but are whitespace agnostic.
Example:
The following assertions can be placed in a Java source file:
- // CHECK-START: int MyClass.MyMethod() constant_folding (after)
- // CHECK: <<ID:i\d+>> IntConstant {{11|22}}
- // CHECK: Return [<<ID>>]
+ /// CHECK-START: int MyClass.MyMethod() constant_folding (after)
+ /// CHECK: <<ID:i\d+>> IntConstant {{11|22}}
+ /// CHECK: Return [<<ID>>]
The engine will attempt to match the check lines against the output of the
group named on the first line. Together they verify that the CFG after
constant folding returns an integer constant with value either 11 or 22.
+
+Of the language constructs above, 'CHECK-EVAL' lines support only referencing of
+variables. Any other surrounding text will be passed to Python's `eval` as is.
+
+Example:
+ /// CHECK-START: int MyClass.MyMethod() liveness (after)
+ /// CHECK: InstructionA liveness:<<VarA:\d+>>
+ /// CHECK: InstructionB liveness:<<VarB:\d+>>
+ /// CHECK-EVAL: <<VarA>> != <<VarB>>
+
+
A group of check lines can be made architecture-specific by inserting '-<arch>'
after the 'CHECK-START' keyword. The previous example can be updated to run for
arm64 only with:
- // CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after)
- // CHECK: <<ID:i\d+>> IntConstant {{11|22}}
- // CHECK: Return [<<ID>>]
+Example:
+ /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after)
+ /// CHECK: <<ID:i\d+>> IntConstant {{11|22}}
+ /// CHECK: Return [<<ID>>]
from common.archs import archs_list
from common.logger import Logger
from file_format.common import SplitStream
-from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression
import re
if notLine is not None:
return (notLine, TestAssertion.Variant.Not, lineNo), None, None
+ # 'CHECK-EVAL' lines evaluate a Python expression.
+ evalLine = __extractLine(prefix + "-EVAL", line)
+ if evalLine is not None:
+ return (evalLine, TestAssertion.Variant.Eval, lineNo), None, None
+
Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
def __isMatchAtStart(match):
def ParseCheckerAssertion(parent, line, variant, lineNo):
""" This method parses the content of a check line stripped of the initial
- comment symbol and the CHECK keyword.
+ comment symbol and the CHECK-* keyword.
"""
assertion = TestAssertion(parent, variant, line, lineNo)
+ isEvalLine = (variant == TestAssertion.Variant.Eval)
+
# Loop as long as there is something to parse.
while line:
# Search for the nearest occurrence of the special markers.
- matchWhitespace = re.search(r"\s+", line)
- matchPattern = re.search(RegexExpression.Regex.regexPattern, line)
- matchVariableReference = re.search(RegexExpression.Regex.regexVariableReference, line)
- matchVariableDefinition = re.search(RegexExpression.Regex.regexVariableDefinition, line)
+ if isEvalLine:
+ # The following constructs are not supported in CHECK-EVAL lines
+ matchWhitespace = None
+ matchPattern = None
+ matchVariableDefinition = None
+ else:
+ matchWhitespace = re.search(r"\s+", line)
+ matchPattern = re.search(TestExpression.Regex.regexPattern, line)
+ matchVariableDefinition = re.search(TestExpression.Regex.regexVariableDefinition, line)
+ matchVariableReference = re.search(TestExpression.Regex.regexVariableReference, line)
# If one of the above was identified at the current position, extract them
# from the line, parse them and add to the list of line parts.
# A whitespace in the check line creates a new separator of line parts.
# This allows for ignored output between the previous and next parts.
line = line[matchWhitespace.end():]
- assertion.addExpression(RegexExpression.createSeparator())
+ assertion.addExpression(TestExpression.createSeparator())
elif __isMatchAtStart(matchPattern):
pattern = line[0:matchPattern.end()]
pattern = pattern[2:-2]
line = line[matchPattern.end():]
- assertion.addExpression(RegexExpression.createPattern(pattern))
+ assertion.addExpression(TestExpression.createPattern(pattern))
elif __isMatchAtStart(matchVariableReference):
var = line[0:matchVariableReference.end()]
line = line[matchVariableReference.end():]
name = var[2:-2]
- assertion.addExpression(RegexExpression.createVariableReference(name))
+ assertion.addExpression(TestExpression.createVariableReference(name))
elif __isMatchAtStart(matchVariableDefinition):
var = line[0:matchVariableDefinition.end()]
line = line[matchVariableDefinition.end():]
colonPos = var.find(":")
name = var[2:colonPos]
body = var[colonPos+1:-2]
- assertion.addExpression(RegexExpression.createVariableDefinition(name, body))
+ assertion.addExpression(TestExpression.createVariableDefinition(name, body))
else:
# If we're not currently looking at a special marker, this is a plain
# text match all the way until the first special marker (or the end
line)
text = line[0:firstMatch]
line = line[firstMatch:]
- assertion.addExpression(RegexExpression.createText(text))
+ if isEvalLine:
+ assertion.addExpression(TestExpression.createPlainText(text))
+ else:
+ assertion.addExpression(TestExpression.createPatternFromPlainText(text))
return assertion
def ParseCheckerStream(fileName, prefix, stream):
class Variant(object):
"""Supported types of assertions."""
- InOrder, NextLine, DAG, Not = range(4)
+ InOrder, NextLine, DAG, Not, Eval = range(5)
def __init__(self, parent, variant, originalText, lineNo):
assert isinstance(parent, TestCase)
return self.parent.fileName
def addExpression(self, new_expression):
- assert isinstance(new_expression, RegexExpression)
+ assert isinstance(new_expression, TestExpression)
if self.variant == TestAssertion.Variant.Not:
- if new_expression.variant == RegexExpression.Variant.VarDef:
+ if new_expression.variant == TestExpression.Variant.VarDef:
Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo)
self.expressions.append(new_expression)
""" Returns a regex pattern for this entire assertion. Only used in tests. """
regex = ""
for expression in self.expressions:
- if expression.variant == RegexExpression.Variant.Separator:
+ if expression.variant == TestExpression.Variant.Separator:
regex = regex + ", "
else:
- regex = regex + "(" + expression.pattern + ")"
+ regex = regex + "(" + expression.text + ")"
return regex
def __eq__(self, other):
and self.expressions == other.expressions
-class RegexExpression(EqualityMixin, PrintableMixin):
+class TestExpression(EqualityMixin, PrintableMixin):
class Variant(object):
"""Supported language constructs."""
- Text, Pattern, VarRef, VarDef, Separator = range(5)
+ PlainText, Pattern, VarRef, VarDef, Separator = range(5)
class Regex(object):
rName = r"([a-zA-Z][a-zA-Z0-9]*)"
regexPattern = rPatternStartSym + rRegex + rPatternEndSym
regexVariableReference = rVariableStartSym + rName + rVariableEndSym
- regexVariableDefinition = rVariableStartSym + rName + rVariableSeparator + rRegex + rVariableEndSym
+ regexVariableDefinition = (rVariableStartSym +
+ rName + rVariableSeparator + rRegex +
+ rVariableEndSym)
- def __init__(self, variant, name, pattern):
+ def __init__(self, variant, name, text):
self.variant = variant
self.name = name
- self.pattern = pattern
+ self.text = text
def __eq__(self, other):
return isinstance(other, self.__class__) \
and self.variant == other.variant \
and self.name == other.name \
- and self.pattern == other.pattern
+ and self.text == other.text
@staticmethod
def createSeparator():
- return RegexExpression(RegexExpression.Variant.Separator, None, None)
+ return TestExpression(TestExpression.Variant.Separator, None, None)
@staticmethod
- def createText(text):
- return RegexExpression(RegexExpression.Variant.Text, None, re.escape(text))
+ def createPlainText(text):
+ return TestExpression(TestExpression.Variant.PlainText, None, text)
+
+ @staticmethod
+ def createPatternFromPlainText(text):
+ return TestExpression(TestExpression.Variant.Pattern, None, re.escape(text))
@staticmethod
def createPattern(pattern):
- return RegexExpression(RegexExpression.Variant.Pattern, None, pattern)
+ return TestExpression(TestExpression.Variant.Pattern, None, pattern)
@staticmethod
def createVariableReference(name):
- assert re.match(RegexExpression.Regex.rName, name)
- return RegexExpression(RegexExpression.Variant.VarRef, name, None)
+ assert re.match(TestExpression.Regex.rName, name)
+ return TestExpression(TestExpression.Variant.VarRef, name, None)
@staticmethod
def createVariableDefinition(name, pattern):
- assert re.match(RegexExpression.Regex.rName, name)
- return RegexExpression(RegexExpression.Variant.VarDef, name, pattern)
+ assert re.match(TestExpression.Regex.rName, name)
+ return TestExpression(TestExpression.Variant.VarDef, name, pattern)
from common.archs import archs_list
from common.testing import ToUnicode
from file_format.checker.parser import ParseCheckerStream
-from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, TestExpression
import io
import unittest
self.assertParses(" ///CHECK: foo")
self.assertParses("/// CHECK: foo")
-class CheckerParser_RegexExpressionTest(unittest.TestCase):
+class CheckerParser_TestExpressionTest(unittest.TestCase):
def parseAssertion(self, string, variant=""):
- checkerText = u"/// CHECK-START: pass\n/// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string)
+ checkerText = (u"/// CHECK-START: pass\n" +
+ u"/// CHECK" + ToUnicode(variant) + u": " + ToUnicode(string))
checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
self.assertEqual(len(checkerFile.testCases), 1)
testCase = checkerFile.testCases[0]
self.assertEqual(expected, self.parseAssertion(string).toRegex())
def assertEqualsText(self, string, text):
- self.assertEqual(self.parseExpression(string), RegexExpression.createText(text))
+ self.assertEqual(self.parseExpression(string), TestExpression.createPatternFromPlainText(text))
def assertEqualsPattern(self, string, pattern):
- self.assertEqual(self.parseExpression(string), RegexExpression.createPattern(pattern))
+ self.assertEqual(self.parseExpression(string), TestExpression.createPattern(pattern))
def assertEqualsVarRef(self, string, name):
- self.assertEqual(self.parseExpression(string), RegexExpression.createVariableReference(name))
+ self.assertEqual(self.parseExpression(string), TestExpression.createVariableReference(name))
def assertEqualsVarDef(self, string, name, pattern):
self.assertEqual(self.parseExpression(string),
- RegexExpression.createVariableDefinition(name, pattern))
+ TestExpression.createVariableDefinition(name, pattern))
def assertVariantNotEqual(self, string, variant):
self.assertNotEqual(variant, self.parseExpression(string).variant)
self.assertEqualsVarDef("<<ABC:(a[bc])>>", "ABC", "(a[bc])")
def test_Empty(self):
- self.assertVariantNotEqual("{{}}", RegexExpression.Variant.Pattern)
- self.assertVariantNotEqual("<<>>", RegexExpression.Variant.VarRef)
- self.assertVariantNotEqual("<<:>>", RegexExpression.Variant.VarDef)
+ self.assertEqualsText("{{}}", "{{}}")
+ self.assertVariantNotEqual("<<>>", TestExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<:>>", TestExpression.Variant.VarDef)
def test_InvalidVarName(self):
- self.assertVariantNotEqual("<<0ABC>>", RegexExpression.Variant.VarRef)
- self.assertVariantNotEqual("<<AB=C>>", RegexExpression.Variant.VarRef)
- self.assertVariantNotEqual("<<ABC=>>", RegexExpression.Variant.VarRef)
- self.assertVariantNotEqual("<<0ABC:abc>>", RegexExpression.Variant.VarDef)
- self.assertVariantNotEqual("<<AB=C:abc>>", RegexExpression.Variant.VarDef)
- self.assertVariantNotEqual("<<ABC=:abc>>", RegexExpression.Variant.VarDef)
+ self.assertVariantNotEqual("<<0ABC>>", TestExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<AB=C>>", TestExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<ABC=>>", TestExpression.Variant.VarRef)
+ self.assertVariantNotEqual("<<0ABC:abc>>", TestExpression.Variant.VarDef)
+ self.assertVariantNotEqual("<<AB=C:abc>>", TestExpression.Variant.VarDef)
+ self.assertVariantNotEqual("<<ABC=:abc>>", TestExpression.Variant.VarDef)
def test_BodyMatchNotGreedy(self):
self.assertEqualsRegex("{{abc}}{{def}}", "(abc)(def)")
content = assertionEntry[0]
variant = assertionEntry[1]
assertion = TestAssertion(testCase, variant, content, 0)
- assertion.addExpression(RegexExpression.createText(content))
+ assertion.addExpression(TestExpression.createPatternFromPlainText(content))
return testFile
def assertParsesTo(self, checkerText, expectedData):
self.parse(
"""
/// CHECK-START: Example Group
+ /// CHECK-EVAL: foo
+ /// CHECK-NEXT: bar
+ """)
+ with self.assertRaises(CheckerException):
+ self.parse(
+ """
+ /// CHECK-START: Example Group
/// CHECK-NEXT: bar
""")
-
class CheckerParser_ArchTests(unittest.TestCase):
self.assertEqual(len(checkerFile.testCases), 1)
self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1)
self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
+
+
+class CheckerParser_EvalTests(unittest.TestCase):
+ def parseTestCase(self, string):
+ checkerText = u"/// CHECK-START: pass\n" + ToUnicode(string)
+ checkerFile = ParseCheckerStream("<test-file>", "CHECK", io.StringIO(checkerText))
+ self.assertEqual(len(checkerFile.testCases), 1)
+ return checkerFile.testCases[0]
+
+ def parseExpressions(self, string):
+ testCase = self.parseTestCase("/// CHECK-EVAL: " + string)
+ self.assertEqual(len(testCase.assertions), 1)
+ assertion = testCase.assertions[0]
+ self.assertEqual(assertion.variant, TestAssertion.Variant.Eval)
+ self.assertEqual(assertion.originalText, string)
+ return assertion.expressions
+
+ def assertParsesToPlainText(self, text):
+ testCase = self.parseTestCase("/// CHECK-EVAL: " + text)
+ self.assertEqual(len(testCase.assertions), 1)
+ assertion = testCase.assertions[0]
+ self.assertEqual(assertion.variant, TestAssertion.Variant.Eval)
+ self.assertEqual(assertion.originalText, text)
+ self.assertEqual(len(assertion.expressions), 1)
+ expression = assertion.expressions[0]
+ self.assertEqual(expression.variant, TestExpression.Variant.PlainText)
+ self.assertEqual(expression.text, text)
+
+ def test_PlainText(self):
+ self.assertParsesToPlainText("XYZ")
+ self.assertParsesToPlainText("True")
+ self.assertParsesToPlainText("{{abc}}")
+ self.assertParsesToPlainText("<<ABC:abc>>")
+ self.assertParsesToPlainText("<<ABC=>>")
+
+ def test_VariableReference(self):
+ self.assertEqual(self.parseExpressions("<<ABC>>"),
+ [ TestExpression.createVariableReference("ABC") ])
+ self.assertEqual(self.parseExpressions("123<<ABC>>"),
+ [ TestExpression.createPlainText("123"),
+ TestExpression.createVariableReference("ABC") ])
+ self.assertEqual(self.parseExpressions("123 <<ABC>>"),
+ [ TestExpression.createPlainText("123 "),
+ TestExpression.createVariableReference("ABC") ])
+ self.assertEqual(self.parseExpressions("<<ABC>>XYZ"),
+ [ TestExpression.createVariableReference("ABC"),
+ TestExpression.createPlainText("XYZ") ])
+ self.assertEqual(self.parseExpressions("<<ABC>> XYZ"),
+ [ TestExpression.createVariableReference("ABC"),
+ TestExpression.createPlainText(" XYZ") ])
+ self.assertEqual(self.parseExpressions("123<<ABC>>XYZ"),
+ [ TestExpression.createPlainText("123"),
+ TestExpression.createVariableReference("ABC"),
+ TestExpression.createPlainText("XYZ") ])
+ self.assertEqual(self.parseExpressions("123 <<ABC>> XYZ"),
+ [ TestExpression.createPlainText("123 "),
+ TestExpression.createVariableReference("ABC"),
+ TestExpression.createPlainText(" XYZ") ])
from common.logger import Logger
from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
-from match.line import MatchLines
+from match.line import MatchLines, EvaluateLine
MatchScope = namedtuple("MatchScope", ["start", "end"])
MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
if MatchLines(assertion, line, variables) is not None:
raise MatchFailedException(assertion, i)
+def testEvalGroup(assertions, scope, variables):
+ for assertion in assertions:
+ if not EvaluateLine(assertion, variables):
+ raise MatchFailedException(assertion, scope.start)
+
def MatchTestCase(testCase, c1Pass):
""" Runs a test case against a C1visualizer graph dump.
assert len(assertionGroup) == 1
scope = MatchScope(matchFrom, matchFrom + 1)
match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
- else:
+ elif assertionGroup[0].variant == TestAssertion.Variant.DAG:
# A group of DAG assertions. Match them all starting from the same point.
- assert assertionGroup[0].variant == TestAssertion.Variant.DAG
scope = MatchScope(matchFrom, c1Length)
match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
+ else:
+ assert assertionGroup[0].variant == TestAssertion.Variant.Eval
+ scope = MatchScope(matchFrom, c1Length)
+ testEvalGroup(assertionGroup, scope, variables)
+ continue
if pendingNotAssertions:
# Previous group were NOT assertions. Make sure they don't match any lines
# limitations under the License.
from common.logger import Logger
-from file_format.checker.struct import RegexExpression
+from file_format.checker.struct import TestExpression, TestAssertion
import re
return list[0], list[1:]
def splitAtSeparators(expressions):
- """ Splits a list of RegexExpressions at separators. """
+ """ Splits a list of TestExpressions at separators. """
splitExpressions = []
wordStart = 0
for index, expression in enumerate(expressions):
- if expression.variant == RegexExpression.Variant.Separator:
+ if expression.variant == TestExpression.Variant.Separator:
splitExpressions.append(expressions[wordStart:index])
wordStart = index + 1
splitExpressions.append(expressions[wordStart:])
return splitExpressions
+def getVariable(name, variables, pos):
+ if name in variables:
+ return variables[name]
+ else:
+ Logger.testFailed("Missing definition of variable \"{}\"".format(name),
+ pos.fileName, pos.lineNo)
+
+def setVariable(name, value, variables, pos):
+ if name not in variables:
+ return variables.copyWith(name, value)
+ else:
+ Logger.testFailed("Multiple definitions of variable \"{}\"".format(name),
+ pos.fileName, pos.lineNo)
+
def matchWords(checkerWord, stringWord, variables, pos):
- """ Attempts to match a list of RegexExpressions against a string.
+ """ Attempts to match a list of TestExpressions against a string.
Returns updated variable dictionary if successful and None otherwise.
"""
for expression in checkerWord:
# If `expression` is a variable reference, replace it with the value.
- if expression.variant == RegexExpression.Variant.VarRef:
- if expression.name in variables:
- pattern = re.escape(variables[expression.name])
- else:
- Logger.testFailed("Missing definition of variable \"{}\"".format(expression.name),
- pos.fileName, pos.lineNo)
+ if expression.variant == TestExpression.Variant.VarRef:
+ pattern = re.escape(getVariable(expression.name, variables, pos))
else:
- pattern = expression.pattern
+ pattern = expression.text
# Match the expression's regex pattern against the remainder of the word.
# Note: re.match will succeed only if matched from the beginning.
return None
# If `expression` was a variable definition, set the variable's value.
- if expression.variant == RegexExpression.Variant.VarDef:
- if expression.name not in variables:
- variables = variables.copyWith(expression.name, stringWord[:match.end()])
- else:
- Logger.testFailed("Multiple definitions of variable \"{}\"".format(expression.name),
- pos.fileName, pos.lineNo)
+ if expression.variant == TestExpression.Variant.VarDef:
+ variables = setVariable(expression.name, stringWord[:match.end()], variables, pos)
# Move cursor by deleting the matched characters.
stringWord = stringWord[match.end():]
""" Attempts to match a CHECK line against a string. Returns variable state
after the match if successful and None otherwise.
"""
+ assert checkerLine.variant != TestAssertion.Variant.Eval
+
checkerWords = splitAtSeparators(checkerLine.expressions)
stringWords = stringLine.split()
while checkerWords:
- # Get the next run of RegexExpressions which must match one string word.
+ # Get the next run of TestExpressions which must match one string word.
checkerWord, checkerWords = headAndTail(checkerWords)
# Keep reading words until a match is found.
if not wordMatched:
return None
- # All RegexExpressions matched. Return new variable state.
+ # All TestExpressions matched. Return new variable state.
return variables
+
+def getEvalText(expression, variables, pos):
+ if expression.variant == TestExpression.Variant.PlainText:
+ return expression.text
+ else:
+ assert expression.variant == TestExpression.Variant.VarRef
+ return getVariable(expression.name, variables, pos)
+
+def EvaluateLine(checkerLine, variables):
+ assert checkerLine.variant == TestAssertion.Variant.Eval
+ eval_string = "".join(map(lambda expr: getEvalText(expr, variables, checkerLine),
+ checkerLine.expressions))
+ return eval(eval_string)
from file_format.c1visualizer.parser import ParseC1visualizerStream
from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
from file_format.checker.parser import ParseCheckerStream, ParseCheckerAssertion
-from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
+from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
from match.file import MatchTestCase, MatchFailedException
from match.line import MatchLines
abc
bar
""")
+
+ def test_EvalAssertions(self):
+ self.assertMatches("/// CHECK-EVAL: True", "foo")
+ self.assertDoesNotMatch("/// CHECK-EVAL: False", "foo")
+
+ self.assertMatches("/// CHECK-EVAL: 1 + 2 == 3", "foo")
+ self.assertDoesNotMatch("/// CHECK-EVAL: 1 + 2 == 4", "foo")
+
+ twoVarTestCase = """
+ /// CHECK-DAG: <<X:\d+>> <<Y:\d+>>
+ /// CHECK-EVAL: <<X>> > <<Y>>
+ """
+ self.assertMatches(twoVarTestCase, "42 41");
+ self.assertDoesNotMatch(twoVarTestCase, "42 43")
from common.logger import Logger
from file_format.c1visualizer.test import C1visualizerParser_Test
from file_format.checker.test import CheckerParser_PrefixTest, \
- CheckerParser_RegexExpressionTest, \
+ CheckerParser_TestExpressionTest, \
CheckerParser_FileLayoutTest, \
- CheckerParser_ArchTests
+ CheckerParser_ArchTests, \
+ CheckerParser_EvalTests
from match.test import MatchLines_Test, \
MatchFiles_Test