blob: 2ed4aa7bc3e1d63a7ec0b4e757ecff00b2d57694 [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
15from common.logger import Logger
16from file_format.c1visualizer.struct import C1visualizerFile, C1visualizerPass
17from file_format.checker.struct import CheckerFile, TestCase, TestAssertion
18from match.line import MatchLines
19
20def __headAndTail(list):
21 return list[0], list[1:]
22
23def __splitByVariant(lines, variant):
24 """ Splits a list of check lines at index 'i' such that lines[i] is the first
25 element whose variant is not equal to the given parameter.
26 """
27 i = 0
28 while i < len(lines) and lines[i].variant == variant:
29 i += 1
30 return lines[:i], lines[i:]
31
32def __nextIndependentChecks(checkLines):
33 """ Extracts the first sequence of check lines which are independent of each
34 other's match location, i.e. either consecutive DAG lines or a single
35 InOrder line. Any Not lines preceeding this sequence are also extracted.
36 """
37 notChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.Not)
38 if not checkLines:
39 return notChecks, [], []
40
41 head, tail = __headAndTail(checkLines)
42 if head.variant == TestAssertion.Variant.InOrder:
43 return notChecks, [head], tail
44 else:
45 assert head.variant == TestAssertion.Variant.DAG
46 independentChecks, checkLines = __splitByVariant(checkLines, TestAssertion.Variant.DAG)
47 return notChecks, independentChecks, checkLines
48
49def __findFirstMatch(checkLine, outputLines, startLineNo, lineFilter, varState):
50 """ If successful, returns the line number of the first output line matching
51 the check line and the updated variable state. Otherwise returns -1 and
52 None, respectively. The 'lineFilter' parameter can be used to supply a
53 list of line numbers (counting from 1) which should be skipped.
54 """
55 matchLineNo = startLineNo
56 for outputLine in outputLines:
57 if matchLineNo not in lineFilter:
58 newVarState = MatchLines(checkLine, outputLine, varState)
59 if newVarState is not None:
60 return matchLineNo, newVarState
61 matchLineNo += 1
62 return -1, None
63
64def __matchIndependentChecks(checkLines, outputLines, startLineNo, varState):
65 """ Matches the given positive check lines against the output in order of
66 appearance. Variable state is propagated but the scope of the search
67 remains the same for all checks. Each output line can only be matched
68 once. If all check lines are matched, the resulting variable state is
69 returned together with the remaining output. The function also returns
70 output lines which appear before either of the matched lines so they can
71 be tested against Not checks.
72 """
73 # If no checks are provided, skip over the entire output.
74 if not checkLines:
75 return outputLines, [], startLineNo + len(outputLines), varState
76
77 # Keep track of which lines have been matched.
78 matchedLines = []
79
80 # Find first unused output line which matches each check line.
81 for checkLine in checkLines:
82 matchLineNo, varState = \
83 __findFirstMatch(checkLine, outputLines, startLineNo, matchedLines, varState)
84 if varState is None:
85 Logger.testFailed("Could not match check line \"" + checkLine.originalText + "\" " +
86 "starting from output line " + str(startLineNo),
87 checkLine.fileName, checkLine.lineNo)
88 matchedLines.append(matchLineNo)
89
90 # Return new variable state and the output lines which lie outside the
91 # match locations of this independent group.
92 minMatchLineNo = min(matchedLines)
93 maxMatchLineNo = max(matchedLines)
94 preceedingLines = outputLines[:minMatchLineNo - startLineNo]
95 remainingLines = outputLines[maxMatchLineNo - startLineNo + 1:]
96 return preceedingLines, remainingLines, maxMatchLineNo + 1, varState
97
98def __matchNotLines(checkLines, outputLines, startLineNo, varState):
99 """ Makes sure that the given check lines do not match any of the given output
100 lines. Variable state does not change.
101 """
102 for checkLine in checkLines:
103 assert checkLine.variant == TestAssertion.Variant.Not
104 matchLineNo, matchVarState = \
105 __findFirstMatch(checkLine, outputLines, startLineNo, [], varState)
106 if matchVarState is not None:
107 Logger.testFailed("CHECK-NOT line \"" + checkLine.originalText + "\" matches output line " + \
108 str(matchLineNo), checkLine.fileName, checkLine.lineNo)
109
110def __matchGroups(checkGroup, outputGroup):
111 """ Matches the check lines in this group against an output group. It is
112 responsible for running the checks in the right order and scope, and
113 for propagating the variable state between the check lines.
114 """
115 varState = {}
116 checkLines = checkGroup.assertions
117 outputLines = outputGroup.body
118 startLineNo = outputGroup.startLineNo
119
120 while checkLines:
121 # Extract the next sequence of location-independent checks to be matched.
122 notChecks, independentChecks, checkLines = __nextIndependentChecks(checkLines)
123
124 # Match the independent checks.
125 notOutput, outputLines, newStartLineNo, newVarState = \
126 __matchIndependentChecks(independentChecks, outputLines, startLineNo, varState)
127
128 # Run the Not checks against the output lines which lie between the last
129 # two independent groups or the bounds of the output.
130 __matchNotLines(notChecks, notOutput, startLineNo, varState)
131
132 # Update variable state.
133 startLineNo = newStartLineNo
134 varState = newVarState
135
136def MatchFiles(checkerFile, c1File):
137 for testCase in checkerFile.testCases:
138 # TODO: Currently does not handle multiple occurrences of the same group
139 # name, e.g. when a pass is run multiple times. It will always try to
140 # match a check group against the first output group of the same name.
141 c1Pass = c1File.findPass(testCase.name)
142 if c1Pass is None:
143 Logger.fail("Test case \"" + testCase.name + "\" not found in the C1visualizer output",
Calin Juravlea8b85b22015-05-14 17:30:21 +0100144 testCase.fileName, testCase.startLineNo)
David Brazdil2c27f2c2015-05-12 18:06:38 +0100145 Logger.startTest(testCase.name)
146 __matchGroups(testCase, c1Pass)
147 Logger.testPassed()