blob: 3ded07482f9e787e7304cf3151209b2614251970 [file] [log] [blame]
David Brazdil2c27f2c2015-05-12 18:06:38 +01001# 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
David Brazdil6423cf52015-05-20 14:57:54 +010015from collections import namedtuple
David Brazdilc4de9432015-05-20 11:03:22 +010016from common.immutables import ImmutableDict
David Brazdil2c27f2c2015-05-12 18:06:38 +010017from common.logger import Logger
18from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
19from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
David Brazdilb34c35e2015-08-20 11:46:04 +010020from match.line import MatchLines, EvaluateLine
David Brazdil2c27f2c2015-05-12 18:06:38 +010021
David Brazdil6423cf52015-05-20 14:57:54 +010022MatchScope = namedtuple("MatchScope", ["start", "end"])
23MatchInfo = namedtuple("MatchInfo", ["scope", "variables"])
David Brazdil2c27f2c2015-05-12 18:06:38 +010024
David Brazdil6423cf52015-05-20 14:57:54 +010025class MatchFailedException(Exception):
26 def __init__(self, assertion, lineNo):
27 self.assertion = assertion
28 self.lineNo = lineNo
29
30def splitIntoGroups(assertions):
31 """ Breaks up a list of assertions, grouping instructions which should be
32 tested in the same scope (consecutive DAG and NOT instructions).
33 """
34 splitAssertions = []
35 lastVariant = None
36 for assertion in assertions:
37 if (assertion.variant == lastVariant and
38 assertion.variant in [TestAssertion.Variant.DAG, TestAssertion.Variant.Not]):
39 splitAssertions[-1].append(assertion)
40 else:
41 splitAssertions.append([assertion])
42 lastVariant = assertion.variant
43 return splitAssertions
44
45def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]):
46 """ Finds the first line in `c1Pass` which matches `assertion`.
47
48 Scan only lines numbered between `scope.start` and `scope.end` and not on the
49 `excludeLines` list.
50
51 Returns the index of the `c1Pass` line matching the assertion and variables
52 values after the match.
53
54 Raises MatchFailedException if no such `c1Pass` line can be found.
David Brazdil2c27f2c2015-05-12 18:06:38 +010055 """
David Brazdil6423cf52015-05-20 14:57:54 +010056 for i in range(scope.start, scope.end):
57 if i in excludeLines: continue
58 newVariables = MatchLines(assertion, c1Pass.body[i], variables)
59 if newVariables is not None:
60 return MatchInfo(MatchScope(i, i), newVariables)
61 raise MatchFailedException(assertion, scope.start)
David Brazdil2c27f2c2015-05-12 18:06:38 +010062
David Brazdil6423cf52015-05-20 14:57:54 +010063def matchDagGroup(assertions, c1Pass, scope, variables):
64 """ Attempts to find matching `c1Pass` lines for a group of DAG assertions.
65
66 Assertions are matched in the list order and variable values propagated. Only
67 lines in `scope` are scanned and each line can only match one assertion.
68
69 Returns the range of `c1Pass` lines covered by this group (min/max of matching
70 line numbers) and the variable values after the match of the last assertion.
71
72 Raises MatchFailedException when an assertion cannot be satisfied.
David Brazdil2c27f2c2015-05-12 18:06:38 +010073 """
David Brazdil2c27f2c2015-05-12 18:06:38 +010074 matchedLines = []
David Brazdil6423cf52015-05-20 14:57:54 +010075 for assertion in assertions:
76 assert assertion.variant == TestAssertion.Variant.DAG
77 match = findMatchingLine(assertion, c1Pass, scope, variables, matchedLines)
78 variables = match.variables
79 assert match.scope.start == match.scope.end
80 assert match.scope.start not in matchedLines
81 matchedLines.append(match.scope.start)
82 return MatchInfo(MatchScope(min(matchedLines), max(matchedLines)), variables)
David Brazdil2c27f2c2015-05-12 18:06:38 +010083
David Brazdil6423cf52015-05-20 14:57:54 +010084def testNotGroup(assertions, c1Pass, scope, variables):
85 """ Verifies that none of the given NOT assertions matches a line inside
86 the given `scope` of `c1Pass` lines.
David Brazdil2c27f2c2015-05-12 18:06:38 +010087
David Brazdil6423cf52015-05-20 14:57:54 +010088 Raises MatchFailedException if an assertion matches a line in the scope.
David Brazdil2c27f2c2015-05-12 18:06:38 +010089 """
David Brazdil6423cf52015-05-20 14:57:54 +010090 for i in range(scope.start, scope.end):
91 line = c1Pass.body[i]
92 for assertion in assertions:
93 assert assertion.variant == TestAssertion.Variant.Not
94 if MatchLines(assertion, line, variables) is not None:
95 raise MatchFailedException(assertion, i)
David Brazdil2c27f2c2015-05-12 18:06:38 +010096
David Brazdilb34c35e2015-08-20 11:46:04 +010097def testEvalGroup(assertions, scope, variables):
98 for assertion in assertions:
99 if not EvaluateLine(assertion, variables):
100 raise MatchFailedException(assertion, scope.start)
101
David Brazdil6423cf52015-05-20 14:57:54 +0100102def MatchTestCase(testCase, c1Pass):
103 """ Runs a test case against a C1visualizer graph dump.
104
105 Raises MatchFailedException when an assertion cannot be satisfied.
David Brazdil2c27f2c2015-05-12 18:06:38 +0100106 """
David Brazdil6423cf52015-05-20 14:57:54 +0100107 assert testCase.name == c1Pass.name
David Brazdil2c27f2c2015-05-12 18:06:38 +0100108
David Brazdil6423cf52015-05-20 14:57:54 +0100109 matchFrom = 0
110 variables = ImmutableDict()
111 c1Length = len(c1Pass.body)
David Brazdil2c27f2c2015-05-12 18:06:38 +0100112
David Brazdil6423cf52015-05-20 14:57:54 +0100113 # NOT assertions are verified retrospectively, once the scope is known.
114 pendingNotAssertions = None
David Brazdil2c27f2c2015-05-12 18:06:38 +0100115
David Brazdil6423cf52015-05-20 14:57:54 +0100116 # Prepare assertions by grouping those that are verified in the same scope.
117 # We also add None as an EOF assertion that will set scope for NOTs.
118 assertionGroups = splitIntoGroups(testCase.assertions)
119 assertionGroups.append(None)
David Brazdil2c27f2c2015-05-12 18:06:38 +0100120
David Brazdil6423cf52015-05-20 14:57:54 +0100121 for assertionGroup in assertionGroups:
122 if assertionGroup is None:
123 # EOF marker always matches the last+1 line of c1Pass.
124 match = MatchInfo(MatchScope(c1Length, c1Length), None)
125 elif assertionGroup[0].variant == TestAssertion.Variant.Not:
126 # NOT assertions will be tested together with the next group.
127 assert not pendingNotAssertions
128 pendingNotAssertions = assertionGroup
129 continue
130 elif assertionGroup[0].variant == TestAssertion.Variant.InOrder:
131 # Single in-order assertion. Find the first line that matches.
132 assert len(assertionGroup) == 1
133 scope = MatchScope(matchFrom, c1Length)
134 match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
David Brazdil71141192015-05-19 18:29:40 +0100135 elif assertionGroup[0].variant == TestAssertion.Variant.NextLine:
136 # Single next-line assertion. Test if the current line matches.
137 assert len(assertionGroup) == 1
138 scope = MatchScope(matchFrom, matchFrom + 1)
139 match = findMatchingLine(assertionGroup[0], c1Pass, scope, variables)
David Brazdilb34c35e2015-08-20 11:46:04 +0100140 elif assertionGroup[0].variant == TestAssertion.Variant.DAG:
David Brazdil6423cf52015-05-20 14:57:54 +0100141 # A group of DAG assertions. Match them all starting from the same point.
David Brazdil6423cf52015-05-20 14:57:54 +0100142 scope = MatchScope(matchFrom, c1Length)
143 match = matchDagGroup(assertionGroup, c1Pass, scope, variables)
David Brazdilb34c35e2015-08-20 11:46:04 +0100144 else:
145 assert assertionGroup[0].variant == TestAssertion.Variant.Eval
146 scope = MatchScope(matchFrom, c1Length)
147 testEvalGroup(assertionGroup, scope, variables)
148 continue
David Brazdil6423cf52015-05-20 14:57:54 +0100149
150 if pendingNotAssertions:
151 # Previous group were NOT assertions. Make sure they don't match any lines
152 # in the [matchFrom, match.start) scope.
153 scope = MatchScope(matchFrom, match.scope.start)
154 testNotGroup(pendingNotAssertions, c1Pass, scope, variables)
155 pendingNotAssertions = None
156
157 # Update state.
158 assert matchFrom <= match.scope.end
159 matchFrom = match.scope.end + 1
160 variables = match.variables
David Brazdil2c27f2c2015-05-12 18:06:38 +0100161
David Brazdil5cc343d2015-10-08 11:35:32 +0100162def MatchFiles(checkerFile, c1File, targetArch, debuggableMode):
David Brazdil2c27f2c2015-05-12 18:06:38 +0100163 for testCase in checkerFile.testCases:
Alexandre Rames5e2c8d32015-08-06 14:49:28 +0100164 if testCase.testArch not in [None, targetArch]:
165 continue
David Brazdil5cc343d2015-10-08 11:35:32 +0100166 if testCase.forDebuggable != debuggableMode:
167 continue
168
David Brazdil2c27f2c2015-05-12 18:06:38 +0100169 # TODO: Currently does not handle multiple occurrences of the same group
170 # name, e.g. when a pass is run multiple times. It will always try to
171 # match a check group against the first output group of the same name.
172 c1Pass = c1File.findPass(testCase.name)
173 if c1Pass is None:
David Brazdil6423cf52015-05-20 14:57:54 +0100174 Logger.fail("Test case \"{}\" not found in the CFG file".format(testCase.name),
Calin Juravlea8b85b22015-05-14 17:30:21 +0100175 testCase.fileName, testCase.startLineNo)
David Brazdil6423cf52015-05-20 14:57:54 +0100176
David Brazdil2c27f2c2015-05-12 18:06:38 +0100177 Logger.startTest(testCase.name)
David Brazdil6423cf52015-05-20 14:57:54 +0100178 try:
179 MatchTestCase(testCase, c1Pass)
180 Logger.testPassed()
181 except MatchFailedException as e:
182 lineNo = c1Pass.startLineNo + e.lineNo
183 if e.assertion.variant == TestAssertion.Variant.Not:
184 Logger.testFailed("NOT assertion matched line {}".format(lineNo),
185 e.assertion.fileName, e.assertion.lineNo)
186 else:
187 Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo),
188 e.assertion.fileName, e.assertion.lineNo)