blob: f0253c351b7f8c2914ab3839515db55cec6f91f8 [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.checker.struct import TestAssertion, RegexExpression
17
18import re
19
20def __isMatchAtStart(match):
21 """ Tests if the given Match occurred at the beginning of the line. """
22 return (match is not None) and (match.start() == 0)
23
24def __generatePattern(checkLine, linePart, varState):
25 """ Returns the regex pattern to be matched in the output line. Variable
26 references are substituted with their current values provided in the
27 'varState' argument.
28
29 An exception is raised if a referenced variable is undefined.
30 """
31 if linePart.variant == RegexExpression.Variant.VarRef:
32 try:
33 return re.escape(varState[linePart.name])
34 except KeyError:
35 Logger.testFailed("Use of undefined variable \"" + linePart.name + "\"",
36 checkLine.fileName, checkLine.lineNo)
37 else:
38 return linePart.pattern
39
40def __isSeparated(outputLine, matchStart):
41 return (matchStart == 0) or (outputLine[matchStart - 1:matchStart].isspace())
42
43def MatchLines(checkLine, outputLine, initialVarState):
44 """ Attempts to match the check line against a line from the output file with
45 the given initial variable values. It returns the new variable state if
46 successful and None otherwise.
47 """
48 # Do the full matching on a shadow copy of the variable state. If the
49 # matching fails half-way, we will not need to revert the state.
50 varState = dict(initialVarState)
51
52 matchStart = 0
53 isAfterSeparator = True
54
55 # Now try to parse all of the parts of the check line in the right order.
56 # Variable values are updated on-the-fly, meaning that a variable can
57 # be referenced immediately after its definition.
58 for part in checkLine.expressions:
59 if part.variant == RegexExpression.Variant.Separator:
60 isAfterSeparator = True
61 continue
62
63 # Find the earliest match for this line part.
64 pattern = __generatePattern(checkLine, part, varState)
65 while True:
66 match = re.search(pattern, outputLine[matchStart:])
67 if (match is None) or (not isAfterSeparator and not __isMatchAtStart(match)):
68 return None
69 matchEnd = matchStart + match.end()
70 matchStart += match.start()
71
72 # Check if this is a valid match if we expect a whitespace separator
73 # before the matched text. Otherwise loop and look for another match.
74 if not isAfterSeparator or __isSeparated(outputLine, matchStart):
75 break
76 else:
77 matchStart += 1
78
79 if part.variant == RegexExpression.Variant.VarDef:
80 if part.name in varState:
81 Logger.testFailed("Multiple definitions of variable \"" + part.name + "\"",
82 checkLine.fileName, checkLine.lineNo)
83 varState[part.name] = outputLine[matchStart:matchEnd]
84
85 matchStart = matchEnd
86 isAfterSeparator = False
87
88 # All parts were successfully matched. Return the new variable state.
89 return varState