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