xref: /aosp_15_r20/external/parameter-framework/upstream/tools/coverage/coverage.py (revision c33452fb792a5495ec310a9626f2638b053af5dd)
1*c33452fbSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c33452fbSAndroid Build Coastguard Worker
3*c33452fbSAndroid Build Coastguard Worker# Copyright (c) 2011-2014, Intel Corporation
4*c33452fbSAndroid Build Coastguard Worker# All rights reserved.
5*c33452fbSAndroid Build Coastguard Worker#
6*c33452fbSAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without modification,
7*c33452fbSAndroid Build Coastguard Worker# are permitted provided that the following conditions are met:
8*c33452fbSAndroid Build Coastguard Worker#
9*c33452fbSAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright notice, this
10*c33452fbSAndroid Build Coastguard Worker# list of conditions and the following disclaimer.
11*c33452fbSAndroid Build Coastguard Worker#
12*c33452fbSAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright notice,
13*c33452fbSAndroid Build Coastguard Worker# this list of conditions and the following disclaimer in the documentation and/or
14*c33452fbSAndroid Build Coastguard Worker# other materials provided with the distribution.
15*c33452fbSAndroid Build Coastguard Worker#
16*c33452fbSAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the names of its contributors
17*c33452fbSAndroid Build Coastguard Worker# may be used to endorse or promote products derived from this software without
18*c33452fbSAndroid Build Coastguard Worker# specific prior written permission.
19*c33452fbSAndroid Build Coastguard Worker#
20*c33452fbSAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21*c33452fbSAndroid Build Coastguard Worker# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22*c33452fbSAndroid Build Coastguard Worker# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23*c33452fbSAndroid Build Coastguard Worker# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24*c33452fbSAndroid Build Coastguard Worker# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*c33452fbSAndroid Build Coastguard Worker# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26*c33452fbSAndroid Build Coastguard Worker# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27*c33452fbSAndroid Build Coastguard Worker# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*c33452fbSAndroid Build Coastguard Worker# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29*c33452fbSAndroid Build Coastguard Worker# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*c33452fbSAndroid Build Coastguard Worker
31*c33452fbSAndroid Build Coastguard Worker
32*c33452fbSAndroid Build Coastguard Worker"""
33*c33452fbSAndroid Build Coastguard WorkerGenerate a coverage report by parsing parameter framework log.
34*c33452fbSAndroid Build Coastguard Worker
35*c33452fbSAndroid Build Coastguard WorkerThe coverage report contains the:
36*c33452fbSAndroid Build Coastguard Worker - domain
37*c33452fbSAndroid Build Coastguard Worker - configuration
38*c33452fbSAndroid Build Coastguard Worker - rule
39*c33452fbSAndroid Build Coastguard Worker - criterion
40*c33452fbSAndroid Build Coastguard Workerbasic coverage statistics.
41*c33452fbSAndroid Build Coastguard Worker"""
42*c33452fbSAndroid Build Coastguard Worker
43*c33452fbSAndroid Build Coastguard Workerimport xml.dom.minidom
44*c33452fbSAndroid Build Coastguard Workerimport sys
45*c33452fbSAndroid Build Coastguard Workerimport re
46*c33452fbSAndroid Build Coastguard Workerimport logging
47*c33452fbSAndroid Build Coastguard Worker
48*c33452fbSAndroid Build Coastguard WorkerFORMAT = '%(levelname)s: %(message)s'
49*c33452fbSAndroid Build Coastguard Workerlogging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
50*c33452fbSAndroid Build Coastguard Workerlogger = logging.getLogger("Coverage")
51*c33452fbSAndroid Build Coastguard Worker
52*c33452fbSAndroid Build Coastguard Workerclass CustomError(Exception):
53*c33452fbSAndroid Build Coastguard Worker    pass
54*c33452fbSAndroid Build Coastguard Worker
55*c33452fbSAndroid Build Coastguard Workerclass ChildError(CustomError):
56*c33452fbSAndroid Build Coastguard Worker    def __init__(self, parent, child):
57*c33452fbSAndroid Build Coastguard Worker        self.parent = parent
58*c33452fbSAndroid Build Coastguard Worker        self.child = child
59*c33452fbSAndroid Build Coastguard Worker
60*c33452fbSAndroid Build Coastguard Workerclass ChildNotFoundError(ChildError):
61*c33452fbSAndroid Build Coastguard Worker    def __str__(self):
62*c33452fbSAndroid Build Coastguard Worker        return 'Unable to find the child "%s" in "%s"' % (self.child, self.parent)
63*c33452fbSAndroid Build Coastguard Worker
64*c33452fbSAndroid Build Coastguard Workerclass DuplicatedChildError(ChildError):
65*c33452fbSAndroid Build Coastguard Worker    def __str__(self):
66*c33452fbSAndroid Build Coastguard Worker        return 'Add existing child "%s" in "%s".' % (self.child, self.parent)
67*c33452fbSAndroid Build Coastguard Worker
68*c33452fbSAndroid Build Coastguard Workerclass Element():
69*c33452fbSAndroid Build Coastguard Worker    """Root class for all coverage elements"""
70*c33452fbSAndroid Build Coastguard Worker    tag = "element"
71*c33452fbSAndroid Build Coastguard Worker
72*c33452fbSAndroid Build Coastguard Worker    def __init__(self, name):
73*c33452fbSAndroid Build Coastguard Worker
74*c33452fbSAndroid Build Coastguard Worker        self.parent = None
75*c33452fbSAndroid Build Coastguard Worker        self.children = []
76*c33452fbSAndroid Build Coastguard Worker
77*c33452fbSAndroid Build Coastguard Worker        self.nbUse = 0
78*c33452fbSAndroid Build Coastguard Worker
79*c33452fbSAndroid Build Coastguard Worker        self.name = name
80*c33452fbSAndroid Build Coastguard Worker
81*c33452fbSAndroid Build Coastguard Worker        self.debug("New element")
82*c33452fbSAndroid Build Coastguard Worker
83*c33452fbSAndroid Build Coastguard Worker
84*c33452fbSAndroid Build Coastguard Worker    def __str__(self):
85*c33452fbSAndroid Build Coastguard Worker        return  "%s (%s)" % (self.name, self.tag)
86*c33452fbSAndroid Build Coastguard Worker
87*c33452fbSAndroid Build Coastguard Worker    def __eq__(self, compared):
88*c33452fbSAndroid Build Coastguard Worker        return (self.name == compared.name) and (self.children == compared.children)
89*c33452fbSAndroid Build Coastguard Worker
90*c33452fbSAndroid Build Coastguard Worker    def getName(self, default=""):
91*c33452fbSAndroid Build Coastguard Worker        return self.name or default
92*c33452fbSAndroid Build Coastguard Worker
93*c33452fbSAndroid Build Coastguard Worker    def hasChildren(self):
94*c33452fbSAndroid Build Coastguard Worker        return bool(self.children)
95*c33452fbSAndroid Build Coastguard Worker
96*c33452fbSAndroid Build Coastguard Worker    def getChildren(self):
97*c33452fbSAndroid Build Coastguard Worker        return self.children
98*c33452fbSAndroid Build Coastguard Worker
99*c33452fbSAndroid Build Coastguard Worker    def _getDescendants(self):
100*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
101*c33452fbSAndroid Build Coastguard Worker            yield child
102*c33452fbSAndroid Build Coastguard Worker            for descendant in child._getDescendants():
103*c33452fbSAndroid Build Coastguard Worker                yield descendant
104*c33452fbSAndroid Build Coastguard Worker
105*c33452fbSAndroid Build Coastguard Worker    def getChildFromName(self, childName):
106*c33452fbSAndroid Build Coastguard Worker
107*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
108*c33452fbSAndroid Build Coastguard Worker
109*c33452fbSAndroid Build Coastguard Worker            if child.getName() == childName:
110*c33452fbSAndroid Build Coastguard Worker                return child
111*c33452fbSAndroid Build Coastguard Worker
112*c33452fbSAndroid Build Coastguard Worker        self.debug('Child "%s" not found' % childName, logging.ERROR)
113*c33452fbSAndroid Build Coastguard Worker
114*c33452fbSAndroid Build Coastguard Worker        self.debug("Child list :")
115*c33452fbSAndroid Build Coastguard Worker
116*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
117*c33452fbSAndroid Build Coastguard Worker            self.debug("  - %s" % child)
118*c33452fbSAndroid Build Coastguard Worker
119*c33452fbSAndroid Build Coastguard Worker        raise ChildNotFoundError(self, childName)
120*c33452fbSAndroid Build Coastguard Worker
121*c33452fbSAndroid Build Coastguard Worker
122*c33452fbSAndroid Build Coastguard Worker    def addChild(self, child):
123*c33452fbSAndroid Build Coastguard Worker        self.debug("new child: " + child.name)
124*c33452fbSAndroid Build Coastguard Worker        self.children.append(child)
125*c33452fbSAndroid Build Coastguard Worker        child._adoptedBy(self)
126*c33452fbSAndroid Build Coastguard Worker
127*c33452fbSAndroid Build Coastguard Worker    def _adoptedBy(self, parent):
128*c33452fbSAndroid Build Coastguard Worker        assert(not self.parent)
129*c33452fbSAndroid Build Coastguard Worker        self.parent = parent
130*c33452fbSAndroid Build Coastguard Worker
131*c33452fbSAndroid Build Coastguard Worker    def _getElementNames(self, elementList):
132*c33452fbSAndroid Build Coastguard Worker        return (substate.name for substate in elementList)
133*c33452fbSAndroid Build Coastguard Worker
134*c33452fbSAndroid Build Coastguard Worker    def _description(self, withCoverage, withNbUse):
135*c33452fbSAndroid Build Coastguard Worker        description = self.name
136*c33452fbSAndroid Build Coastguard Worker
137*c33452fbSAndroid Build Coastguard Worker        if withNbUse or withCoverage:
138*c33452fbSAndroid Build Coastguard Worker            description += " has been used " + str(self.nbUse) + " time"
139*c33452fbSAndroid Build Coastguard Worker
140*c33452fbSAndroid Build Coastguard Worker        if withCoverage:
141*c33452fbSAndroid Build Coastguard Worker            description += self._coverageFormating(self._getCoverage())
142*c33452fbSAndroid Build Coastguard Worker
143*c33452fbSAndroid Build Coastguard Worker        return description
144*c33452fbSAndroid Build Coastguard Worker
145*c33452fbSAndroid Build Coastguard Worker
146*c33452fbSAndroid Build Coastguard Worker    def _getCoverage(self):
147*c33452fbSAndroid Build Coastguard Worker        """Return the coverage of the element between 0 and 1
148*c33452fbSAndroid Build Coastguard Worker
149*c33452fbSAndroid Build Coastguard Worker        If the element has no coverage dependency (usually child) return 0 or 1.
150*c33452fbSAndroid Build Coastguard Worker        otherwise the element coverage is the dependency coverage average"""
151*c33452fbSAndroid Build Coastguard Worker        coverageDependanceElements = list(self._getCoverageDependanceElements())
152*c33452fbSAndroid Build Coastguard Worker
153*c33452fbSAndroid Build Coastguard Worker        nbcoverageDependence = len(coverageDependanceElements)
154*c33452fbSAndroid Build Coastguard Worker
155*c33452fbSAndroid Build Coastguard Worker        if nbcoverageDependence == 0:
156*c33452fbSAndroid Build Coastguard Worker            if self.nbUse == 0:
157*c33452fbSAndroid Build Coastguard Worker                return 0
158*c33452fbSAndroid Build Coastguard Worker            else:
159*c33452fbSAndroid Build Coastguard Worker                return 1
160*c33452fbSAndroid Build Coastguard Worker
161*c33452fbSAndroid Build Coastguard Worker        coverageDependenceValues = (depElement._getCoverage()
162*c33452fbSAndroid Build Coastguard Worker                                    for depElement in coverageDependanceElements)
163*c33452fbSAndroid Build Coastguard Worker
164*c33452fbSAndroid Build Coastguard Worker        return sum(coverageDependenceValues) / nbcoverageDependence
165*c33452fbSAndroid Build Coastguard Worker
166*c33452fbSAndroid Build Coastguard Worker    def _getCoverageDependanceElements(self):
167*c33452fbSAndroid Build Coastguard Worker        return self.children
168*c33452fbSAndroid Build Coastguard Worker
169*c33452fbSAndroid Build Coastguard Worker    def _coverageFormating(self, coverage):
170*c33452fbSAndroid Build Coastguard Worker        # If no coverage provided
171*c33452fbSAndroid Build Coastguard Worker        if not coverage:
172*c33452fbSAndroid Build Coastguard Worker            return ""
173*c33452fbSAndroid Build Coastguard Worker
174*c33452fbSAndroid Build Coastguard Worker        # Calculate coverage
175*c33452fbSAndroid Build Coastguard Worker        return " (%s coverage)" % self._number2percent(coverage)
176*c33452fbSAndroid Build Coastguard Worker
177*c33452fbSAndroid Build Coastguard Worker    @staticmethod
178*c33452fbSAndroid Build Coastguard Worker    def _number2percent(number):
179*c33452fbSAndroid Build Coastguard Worker        """Format a number to a integer % string
180*c33452fbSAndroid Build Coastguard Worker
181*c33452fbSAndroid Build Coastguard Worker        example: _number2percent(0.6666) -> "67%"
182*c33452fbSAndroid Build Coastguard Worker        """
183*c33452fbSAndroid Build Coastguard Worker        return "{0:.0f}%".format(100 * number)
184*c33452fbSAndroid Build Coastguard Worker
185*c33452fbSAndroid Build Coastguard Worker
186*c33452fbSAndroid Build Coastguard Worker    def _dumpDescription(self, withCoverage, withNbUse):
187*c33452fbSAndroid Build Coastguard Worker
188*c33452fbSAndroid Build Coastguard Worker        self.debug("yelding description")
189*c33452fbSAndroid Build Coastguard Worker        yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
190*c33452fbSAndroid Build Coastguard Worker
191*c33452fbSAndroid Build Coastguard Worker        for dumped in self._dumpPropagate(withCoverage, withNbUse):
192*c33452fbSAndroid Build Coastguard Worker            yield dumped
193*c33452fbSAndroid Build Coastguard Worker
194*c33452fbSAndroid Build Coastguard Worker    def _dumpPropagate(self, withCoverage, withNbUse):
195*c33452fbSAndroid Build Coastguard Worker
196*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
197*c33452fbSAndroid Build Coastguard Worker            for dumpedDescription in child._dumpDescription(withCoverage, withNbUse):
198*c33452fbSAndroid Build Coastguard Worker                yield dumpedDescription.increasedRank()
199*c33452fbSAndroid Build Coastguard Worker
200*c33452fbSAndroid Build Coastguard Worker
201*c33452fbSAndroid Build Coastguard Worker    def dump(self, withCoverage=False, withNbUse=True):
202*c33452fbSAndroid Build Coastguard Worker
203*c33452fbSAndroid Build Coastguard Worker        return "\n".join(str(dumpedDescription) for dumpedDescription in
204*c33452fbSAndroid Build Coastguard Worker                         self._dumpDescription(withCoverage, withNbUse))
205*c33452fbSAndroid Build Coastguard Worker
206*c33452fbSAndroid Build Coastguard Worker    def exportToXML(self, document, domElement=None):
207*c33452fbSAndroid Build Coastguard Worker        if domElement == None:
208*c33452fbSAndroid Build Coastguard Worker            domElement = document.createElement(self.tag)
209*c33452fbSAndroid Build Coastguard Worker
210*c33452fbSAndroid Build Coastguard Worker        self._XMLaddAttributes(domElement)
211*c33452fbSAndroid Build Coastguard Worker
212*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
213*c33452fbSAndroid Build Coastguard Worker            domElement.appendChild(child.exportToXML(document))
214*c33452fbSAndroid Build Coastguard Worker
215*c33452fbSAndroid Build Coastguard Worker        return domElement
216*c33452fbSAndroid Build Coastguard Worker
217*c33452fbSAndroid Build Coastguard Worker    def _XMLaddAttributes(self, domElement):
218*c33452fbSAndroid Build Coastguard Worker        attributes = self._getXMLAttributes()
219*c33452fbSAndroid Build Coastguard Worker
220*c33452fbSAndroid Build Coastguard Worker        coverage = self._getCoverage()
221*c33452fbSAndroid Build Coastguard Worker        if coverage != None:
222*c33452fbSAndroid Build Coastguard Worker            attributes["Coverage"] = self._number2percent(coverage)
223*c33452fbSAndroid Build Coastguard Worker
224*c33452fbSAndroid Build Coastguard Worker        for key, value in attributes.items():
225*c33452fbSAndroid Build Coastguard Worker            domElement.setAttribute(key, value)
226*c33452fbSAndroid Build Coastguard Worker
227*c33452fbSAndroid Build Coastguard Worker    def _getXMLAttributes(self):
228*c33452fbSAndroid Build Coastguard Worker        return {"Name": self.name,
229*c33452fbSAndroid Build Coastguard Worker                "NbUse": str(self.nbUse)
230*c33452fbSAndroid Build Coastguard Worker               }
231*c33452fbSAndroid Build Coastguard Worker
232*c33452fbSAndroid Build Coastguard Worker    def _incNbUse(self):
233*c33452fbSAndroid Build Coastguard Worker        self.nbUse += 1
234*c33452fbSAndroid Build Coastguard Worker
235*c33452fbSAndroid Build Coastguard Worker    def childUsed(self, child):
236*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
237*c33452fbSAndroid Build Coastguard Worker        # Propagate to parent
238*c33452fbSAndroid Build Coastguard Worker        self._tellParentThatChildUsed()
239*c33452fbSAndroid Build Coastguard Worker
240*c33452fbSAndroid Build Coastguard Worker    def _tellParentThatChildUsed(self):
241*c33452fbSAndroid Build Coastguard Worker        if self.parent:
242*c33452fbSAndroid Build Coastguard Worker            self.parent.childUsed(self)
243*c33452fbSAndroid Build Coastguard Worker
244*c33452fbSAndroid Build Coastguard Worker
245*c33452fbSAndroid Build Coastguard Worker    def parentUsed(self):
246*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
247*c33452fbSAndroid Build Coastguard Worker        # Propagate to children
248*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
249*c33452fbSAndroid Build Coastguard Worker            child.parentUsed()
250*c33452fbSAndroid Build Coastguard Worker
251*c33452fbSAndroid Build Coastguard Worker    def hasBeenUsed(self):
252*c33452fbSAndroid Build Coastguard Worker        return self.nbUse > 0
253*c33452fbSAndroid Build Coastguard Worker
254*c33452fbSAndroid Build Coastguard Worker    def operationOnChild(self, path, operation):
255*c33452fbSAndroid Build Coastguard Worker
256*c33452fbSAndroid Build Coastguard Worker        if path:
257*c33452fbSAndroid Build Coastguard Worker            return self._operationPropagate(path, operation)
258*c33452fbSAndroid Build Coastguard Worker        else:
259*c33452fbSAndroid Build Coastguard Worker            self.debug("operating on self")
260*c33452fbSAndroid Build Coastguard Worker            return operation(self)
261*c33452fbSAndroid Build Coastguard Worker
262*c33452fbSAndroid Build Coastguard Worker    def _operationPropagate(self, path, operation):
263*c33452fbSAndroid Build Coastguard Worker
264*c33452fbSAndroid Build Coastguard Worker        childName = path.pop(0)
265*c33452fbSAndroid Build Coastguard Worker        child = self.getChildFromName(childName)
266*c33452fbSAndroid Build Coastguard Worker
267*c33452fbSAndroid Build Coastguard Worker        return child.operationOnChild(path, operation)
268*c33452fbSAndroid Build Coastguard Worker
269*c33452fbSAndroid Build Coastguard Worker
270*c33452fbSAndroid Build Coastguard Worker
271*c33452fbSAndroid Build Coastguard Worker    def debug(self, stringOrFunction, level=logging.DEBUG):
272*c33452fbSAndroid Build Coastguard Worker        """Print a debug line on stderr in tree form
273*c33452fbSAndroid Build Coastguard Worker
274*c33452fbSAndroid Build Coastguard Worker        If the debug line is expensive to generate, provide callable
275*c33452fbSAndroid Build Coastguard Worker        object, it will be called if log is enable for this level.
276*c33452fbSAndroid Build Coastguard Worker        This callable object should return the logline string.
277*c33452fbSAndroid Build Coastguard Worker        """
278*c33452fbSAndroid Build Coastguard Worker        if logger.isEnabledFor(level):
279*c33452fbSAndroid Build Coastguard Worker
280*c33452fbSAndroid Build Coastguard Worker            # TODO: use buildin callable if python >= 3.2
281*c33452fbSAndroid Build Coastguard Worker            if hasattr(stringOrFunction, "__call__"):
282*c33452fbSAndroid Build Coastguard Worker                string = stringOrFunction()
283*c33452fbSAndroid Build Coastguard Worker            else:
284*c33452fbSAndroid Build Coastguard Worker                string = stringOrFunction
285*c33452fbSAndroid Build Coastguard Worker
286*c33452fbSAndroid Build Coastguard Worker            rankedLine = DebugRankedLine("%s: %s" % (self, string))
287*c33452fbSAndroid Build Coastguard Worker            self._logDebug(rankedLine, level)
288*c33452fbSAndroid Build Coastguard Worker
289*c33452fbSAndroid Build Coastguard Worker    def _logDebug(self, rankedLine, level):
290*c33452fbSAndroid Build Coastguard Worker
291*c33452fbSAndroid Build Coastguard Worker        if self.parent:
292*c33452fbSAndroid Build Coastguard Worker            self.parent._logDebug(rankedLine.increasedRank(), level)
293*c33452fbSAndroid Build Coastguard Worker        else:
294*c33452fbSAndroid Build Coastguard Worker            logger.log(level, str(rankedLine))
295*c33452fbSAndroid Build Coastguard Worker
296*c33452fbSAndroid Build Coastguard Worker
297*c33452fbSAndroid Build Coastguard Worker
298*c33452fbSAndroid Build Coastguard Worker
299*c33452fbSAndroid Build Coastguard Workerclass FromDomElement(Element):
300*c33452fbSAndroid Build Coastguard Worker    def __init__(self, DomElement):
301*c33452fbSAndroid Build Coastguard Worker        self._initFromDom(DomElement)
302*c33452fbSAndroid Build Coastguard Worker        super().__init__(self.name)
303*c33452fbSAndroid Build Coastguard Worker
304*c33452fbSAndroid Build Coastguard Worker
305*c33452fbSAndroid Build Coastguard Worker    def _initFromDom(self, DomElement):
306*c33452fbSAndroid Build Coastguard Worker        self.name = DomElement.getAttribute("Name")
307*c33452fbSAndroid Build Coastguard Worker
308*c33452fbSAndroid Build Coastguard Worker
309*c33452fbSAndroid Build Coastguard Worker
310*c33452fbSAndroid Build Coastguard Workerclass DomElementLocation():
311*c33452fbSAndroid Build Coastguard Worker    def __init__(self, classConstructor, path=None):
312*c33452fbSAndroid Build Coastguard Worker        self.classConstructor = classConstructor
313*c33452fbSAndroid Build Coastguard Worker        if path:
314*c33452fbSAndroid Build Coastguard Worker            self.path = path
315*c33452fbSAndroid Build Coastguard Worker        else:
316*c33452fbSAndroid Build Coastguard Worker            self.path = []
317*c33452fbSAndroid Build Coastguard Worker
318*c33452fbSAndroid Build Coastguard Worker        self.path.append(classConstructor.tag)
319*c33452fbSAndroid Build Coastguard Worker
320*c33452fbSAndroid Build Coastguard Worker
321*c33452fbSAndroid Build Coastguard Workerclass DomPopulatedElement(Element):
322*c33452fbSAndroid Build Coastguard Worker    """Default child populate
323*c33452fbSAndroid Build Coastguard Worker
324*c33452fbSAndroid Build Coastguard Worker    Look for each dom element with tag specified in self.tag
325*c33452fbSAndroid Build Coastguard Worker    and instantiate it with the dom element
326*c33452fbSAndroid Build Coastguard Worker    """
327*c33452fbSAndroid Build Coastguard Worker    childClasses = []
328*c33452fbSAndroid Build Coastguard Worker
329*c33452fbSAndroid Build Coastguard Worker    def populate(self, dom):
330*c33452fbSAndroid Build Coastguard Worker
331*c33452fbSAndroid Build Coastguard Worker        for childDomElementLocation in self.childClasses:
332*c33452fbSAndroid Build Coastguard Worker
333*c33452fbSAndroid Build Coastguard Worker            self.debug("Looking for child %s in path %s" % (
334*c33452fbSAndroid Build Coastguard Worker                childDomElementLocation.path[-1], childDomElementLocation.path))
335*c33452fbSAndroid Build Coastguard Worker
336*c33452fbSAndroid Build Coastguard Worker            for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path):
337*c33452fbSAndroid Build Coastguard Worker
338*c33452fbSAndroid Build Coastguard Worker                childElement = childDomElementLocation.classConstructor(childDomElement)
339*c33452fbSAndroid Build Coastguard Worker                self.addChild(childElement)
340*c33452fbSAndroid Build Coastguard Worker
341*c33452fbSAndroid Build Coastguard Worker                childElement.populate(childDomElement)
342*c33452fbSAndroid Build Coastguard Worker
343*c33452fbSAndroid Build Coastguard Worker    def _findChildFromTagPath(self, dom, path):
344*c33452fbSAndroid Build Coastguard Worker        if not path:
345*c33452fbSAndroid Build Coastguard Worker            yield dom
346*c33452fbSAndroid Build Coastguard Worker        else:
347*c33452fbSAndroid Build Coastguard Worker            # Copy list
348*c33452fbSAndroid Build Coastguard Worker            path = list(path)
349*c33452fbSAndroid Build Coastguard Worker
350*c33452fbSAndroid Build Coastguard Worker            tag = path.pop(0)
351*c33452fbSAndroid Build Coastguard Worker
352*c33452fbSAndroid Build Coastguard Worker            # Find element with tag
353*c33452fbSAndroid Build Coastguard Worker            self.debug("Going to find elements with tag %s in %s" % (tag, dom))
354*c33452fbSAndroid Build Coastguard Worker            self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
355*c33452fbSAndroid Build Coastguard Worker
356*c33452fbSAndroid Build Coastguard Worker            for elementByTag in dom.getElementsByTagName(tag):
357*c33452fbSAndroid Build Coastguard Worker
358*c33452fbSAndroid Build Coastguard Worker                self.debug("Found element: %s" % elementByTag)
359*c33452fbSAndroid Build Coastguard Worker
360*c33452fbSAndroid Build Coastguard Worker                # If the same tag is found
361*c33452fbSAndroid Build Coastguard Worker                if elementByTag in dom.childNodes:
362*c33452fbSAndroid Build Coastguard Worker
363*c33452fbSAndroid Build Coastguard Worker                    # Yield next level
364*c33452fbSAndroid Build Coastguard Worker                    for element in self._findChildFromTagPath(elementByTag, path):
365*c33452fbSAndroid Build Coastguard Worker                        yield element
366*c33452fbSAndroid Build Coastguard Worker
367*c33452fbSAndroid Build Coastguard Worker
368*c33452fbSAndroid Build Coastguard Workerclass Rule(Element):
369*c33452fbSAndroid Build Coastguard Worker
370*c33452fbSAndroid Build Coastguard Worker    def usedIfApplicable(self, criteria):
371*c33452fbSAndroid Build Coastguard Worker        childApplicability = (child.usedIfApplicable(criteria)
372*c33452fbSAndroid Build Coastguard Worker                              for child in self.children)
373*c33452fbSAndroid Build Coastguard Worker
374*c33452fbSAndroid Build Coastguard Worker        isApplicable = self._isApplicable(criteria, childApplicability)
375*c33452fbSAndroid Build Coastguard Worker
376*c33452fbSAndroid Build Coastguard Worker        if isApplicable:
377*c33452fbSAndroid Build Coastguard Worker            self._incNbUse()
378*c33452fbSAndroid Build Coastguard Worker
379*c33452fbSAndroid Build Coastguard Worker        self.debug("Rule applicability: %s" % isApplicable)
380*c33452fbSAndroid Build Coastguard Worker        assert(isApplicable == True or isApplicable == False)
381*c33452fbSAndroid Build Coastguard Worker
382*c33452fbSAndroid Build Coastguard Worker        return isApplicable
383*c33452fbSAndroid Build Coastguard Worker
384*c33452fbSAndroid Build Coastguard Worker
385*c33452fbSAndroid Build Coastguard Worker    def _isApplicable(self, criteria, childApplicability):
386*c33452fbSAndroid Build Coastguard Worker        """Return the rule applicability depending on children applicability.
387*c33452fbSAndroid Build Coastguard Worker
388*c33452fbSAndroid Build Coastguard Worker        If at least one child is applicable, return true"""
389*c33452fbSAndroid Build Coastguard Worker        # Lazy evaluation as in the PFW
390*c33452fbSAndroid Build Coastguard Worker        return all(childApplicability)
391*c33452fbSAndroid Build Coastguard Worker
392*c33452fbSAndroid Build Coastguard Worker
393*c33452fbSAndroid Build Coastguard Workerclass CriterionRule(FromDomElement, DomPopulatedElement, Rule):
394*c33452fbSAndroid Build Coastguard Worker    tag = "SelectionCriterionRule"
395*c33452fbSAndroid Build Coastguard Worker    childClasses = []
396*c33452fbSAndroid Build Coastguard Worker    isApplicableOperations = {
397*c33452fbSAndroid Build Coastguard Worker        "Includes" : lambda criterion, value: criterion.stateIncludes(value),
398*c33452fbSAndroid Build Coastguard Worker        "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
399*c33452fbSAndroid Build Coastguard Worker        "Is"       : lambda criterion, value: criterion.stateIs(value),
400*c33452fbSAndroid Build Coastguard Worker        "IsNot"    : lambda criterion, value: not criterion.stateIs(value)
401*c33452fbSAndroid Build Coastguard Worker    }
402*c33452fbSAndroid Build Coastguard Worker
403*c33452fbSAndroid Build Coastguard Worker    def _initFromDom(self, DomElement):
404*c33452fbSAndroid Build Coastguard Worker        self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
405*c33452fbSAndroid Build Coastguard Worker        self.matchesWhen = DomElement.getAttribute("MatchesWhen")
406*c33452fbSAndroid Build Coastguard Worker        self.value = DomElement.getAttribute("Value")
407*c33452fbSAndroid Build Coastguard Worker        self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
408*c33452fbSAndroid Build Coastguard Worker
409*c33452fbSAndroid Build Coastguard Worker        applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
410*c33452fbSAndroid Build Coastguard Worker        self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion,
411*c33452fbSAndroid Build Coastguard Worker                                                                                       self.value)
412*c33452fbSAndroid Build Coastguard Worker
413*c33452fbSAndroid Build Coastguard Worker    def _isApplicable(self, criteria, childApplicability):
414*c33452fbSAndroid Build Coastguard Worker
415*c33452fbSAndroid Build Coastguard Worker        return criteria.operationOnChild([self.selectionCriterion], self.isApplicableOperation)
416*c33452fbSAndroid Build Coastguard Worker
417*c33452fbSAndroid Build Coastguard Worker
418*c33452fbSAndroid Build Coastguard Workerclass CompoundRule(FromDomElement, DomPopulatedElement, Rule):
419*c33452fbSAndroid Build Coastguard Worker    """CompoundRule can be of type ALL or ANY"""
420*c33452fbSAndroid Build Coastguard Worker    tag = "CompoundRule"
421*c33452fbSAndroid Build Coastguard Worker    # Declare childClasses but define it at first class instantiation
422*c33452fbSAndroid Build Coastguard Worker    childClasses = None
423*c33452fbSAndroid Build Coastguard Worker
424*c33452fbSAndroid Build Coastguard Worker    def __init__(self, dom):
425*c33452fbSAndroid Build Coastguard Worker        # Define childClasses at first class instantiation
426*c33452fbSAndroid Build Coastguard Worker        if self.childClasses == None:
427*c33452fbSAndroid Build Coastguard Worker            self.childClasses = [DomElementLocation(CriterionRule),
428*c33452fbSAndroid Build Coastguard Worker                                 DomElementLocation(CompoundRule)]
429*c33452fbSAndroid Build Coastguard Worker        super().__init__(dom)
430*c33452fbSAndroid Build Coastguard Worker
431*c33452fbSAndroid Build Coastguard Worker    def _initFromDom(self, DomElement):
432*c33452fbSAndroid Build Coastguard Worker
433*c33452fbSAndroid Build Coastguard Worker        type = DomElement.getAttribute("Type")
434*c33452fbSAndroid Build Coastguard Worker        self.ofTypeAll = {"All" : True, "Any" : False}[type]
435*c33452fbSAndroid Build Coastguard Worker        self.name = type
436*c33452fbSAndroid Build Coastguard Worker
437*c33452fbSAndroid Build Coastguard Worker    def _isApplicable(self, criteria, childApplicability):
438*c33452fbSAndroid Build Coastguard Worker        if self.ofTypeAll:
439*c33452fbSAndroid Build Coastguard Worker            applicability = super()._isApplicable(criteria, childApplicability)
440*c33452fbSAndroid Build Coastguard Worker        else:
441*c33452fbSAndroid Build Coastguard Worker            # Lazy evaluation as in the PFW
442*c33452fbSAndroid Build Coastguard Worker            applicability = any(childApplicability)
443*c33452fbSAndroid Build Coastguard Worker
444*c33452fbSAndroid Build Coastguard Worker        return applicability
445*c33452fbSAndroid Build Coastguard Worker
446*c33452fbSAndroid Build Coastguard Workerclass RootRule(DomPopulatedElement, Rule):
447*c33452fbSAndroid Build Coastguard Worker    tag = "RootRule"
448*c33452fbSAndroid Build Coastguard Worker    childClasses = [DomElementLocation(CompoundRule)]
449*c33452fbSAndroid Build Coastguard Worker
450*c33452fbSAndroid Build Coastguard Worker    def populate(self, dom):
451*c33452fbSAndroid Build Coastguard Worker        super().populate(dom)
452*c33452fbSAndroid Build Coastguard Worker        self.debug("Children: %s" % self.children)
453*c33452fbSAndroid Build Coastguard Worker        # A configuration can only have one or no rule
454*c33452fbSAndroid Build Coastguard Worker        assert(len(self.children) <= 1)
455*c33452fbSAndroid Build Coastguard Worker
456*c33452fbSAndroid Build Coastguard Worker    def _getCoverageDependanceElements(self):
457*c33452fbSAndroid Build Coastguard Worker        return self._getDescendants()
458*c33452fbSAndroid Build Coastguard Worker
459*c33452fbSAndroid Build Coastguard Worker
460*c33452fbSAndroid Build Coastguard Workerclass CriteronStates(Element):
461*c33452fbSAndroid Build Coastguard Worker    """Root of configuration application criterion state"""
462*c33452fbSAndroid Build Coastguard Worker    tag = "CriterionStates"
463*c33452fbSAndroid Build Coastguard Worker
464*c33452fbSAndroid Build Coastguard Worker    def parentUsed(self, criteria):
465*c33452fbSAndroid Build Coastguard Worker        """Add criteria to child if not exist, if exist increase it's nbUse"""
466*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
467*c33452fbSAndroid Build Coastguard Worker
468*c33452fbSAndroid Build Coastguard Worker        matches = [child for child in self.children if child == criteria]
469*c33452fbSAndroid Build Coastguard Worker
470*c33452fbSAndroid Build Coastguard Worker        assert(len(matches) <= 1)
471*c33452fbSAndroid Build Coastguard Worker
472*c33452fbSAndroid Build Coastguard Worker        if matches:
473*c33452fbSAndroid Build Coastguard Worker            self.debug("Criteria state has already been encounter")
474*c33452fbSAndroid Build Coastguard Worker            currentcriteria = matches[0]
475*c33452fbSAndroid Build Coastguard Worker        else:
476*c33452fbSAndroid Build Coastguard Worker            self.debug("Criteria state has never been encounter, saving it")
477*c33452fbSAndroid Build Coastguard Worker            currentcriteria = criteria
478*c33452fbSAndroid Build Coastguard Worker            self.addChild(criteria)
479*c33452fbSAndroid Build Coastguard Worker
480*c33452fbSAndroid Build Coastguard Worker        currentcriteria.parentUsed()
481*c33452fbSAndroid Build Coastguard Worker
482*c33452fbSAndroid Build Coastguard Worker
483*c33452fbSAndroid Build Coastguard Worker
484*c33452fbSAndroid Build Coastguard Workerclass Configuration(FromDomElement, DomPopulatedElement):
485*c33452fbSAndroid Build Coastguard Worker    tag = "Configuration"
486*c33452fbSAndroid Build Coastguard Worker    childClasses = []
487*c33452fbSAndroid Build Coastguard Worker
488*c33452fbSAndroid Build Coastguard Worker    class IneligibleConfigurationAppliedError(CustomError):
489*c33452fbSAndroid Build Coastguard Worker
490*c33452fbSAndroid Build Coastguard Worker        def __init__(self, configuration, criteria):
491*c33452fbSAndroid Build Coastguard Worker            self.configuration = configuration
492*c33452fbSAndroid Build Coastguard Worker            self.criteria = criteria
493*c33452fbSAndroid Build Coastguard Worker
494*c33452fbSAndroid Build Coastguard Worker        def __str__(self):
495*c33452fbSAndroid Build Coastguard Worker
496*c33452fbSAndroid Build Coastguard Worker            return ("Applying ineligible %s, "
497*c33452fbSAndroid Build Coastguard Worker                    "rule:\n%s\n"
498*c33452fbSAndroid Build Coastguard Worker                    "Criteria current state:\n%s" % (
499*c33452fbSAndroid Build Coastguard Worker                        self.configuration,
500*c33452fbSAndroid Build Coastguard Worker                        self.configuration.rootRule.dump(withCoverage=False, withNbUse=False),
501*c33452fbSAndroid Build Coastguard Worker                        self.criteria.dump(withCoverage=False, withNbUse=False)
502*c33452fbSAndroid Build Coastguard Worker                    ))
503*c33452fbSAndroid Build Coastguard Worker
504*c33452fbSAndroid Build Coastguard Worker    def __init__(self, DomElement):
505*c33452fbSAndroid Build Coastguard Worker        super().__init__(DomElement)
506*c33452fbSAndroid Build Coastguard Worker
507*c33452fbSAndroid Build Coastguard Worker        self.rootRule = RootRule("RootRule")
508*c33452fbSAndroid Build Coastguard Worker        self.addChild(self.rootRule)
509*c33452fbSAndroid Build Coastguard Worker
510*c33452fbSAndroid Build Coastguard Worker        self.criteronStates = CriteronStates("CriterionStates")
511*c33452fbSAndroid Build Coastguard Worker        self.addChild(self.criteronStates)
512*c33452fbSAndroid Build Coastguard Worker
513*c33452fbSAndroid Build Coastguard Worker    def populate(self, dom):
514*c33452fbSAndroid Build Coastguard Worker        # Delegate to rootRule
515*c33452fbSAndroid Build Coastguard Worker        self.rootRule.populate(dom)
516*c33452fbSAndroid Build Coastguard Worker
517*c33452fbSAndroid Build Coastguard Worker    def _getCoverage(self):
518*c33452fbSAndroid Build Coastguard Worker        # Delegate to rootRule
519*c33452fbSAndroid Build Coastguard Worker        return self.rootRule._getCoverage()
520*c33452fbSAndroid Build Coastguard Worker
521*c33452fbSAndroid Build Coastguard Worker    def used(self, criteria):
522*c33452fbSAndroid Build Coastguard Worker
523*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
524*c33452fbSAndroid Build Coastguard Worker
525*c33452fbSAndroid Build Coastguard Worker        # Propagate use to parents
526*c33452fbSAndroid Build Coastguard Worker        self._tellParentThatChildUsed()
527*c33452fbSAndroid Build Coastguard Worker
528*c33452fbSAndroid Build Coastguard Worker        # Propagate to criterion coverage
529*c33452fbSAndroid Build Coastguard Worker        self.criteronStates.parentUsed(criteria.export())
530*c33452fbSAndroid Build Coastguard Worker
531*c33452fbSAndroid Build Coastguard Worker        # Propagate to rules
532*c33452fbSAndroid Build Coastguard Worker        if not self.rootRule.usedIfApplicable(criteria):
533*c33452fbSAndroid Build Coastguard Worker
534*c33452fbSAndroid Build Coastguard Worker            self.debug("Applied but rule does not match current "
535*c33452fbSAndroid Build Coastguard Worker                       "criteria (parent: %s) " % self.parent.name,
536*c33452fbSAndroid Build Coastguard Worker                       logging.ERROR)
537*c33452fbSAndroid Build Coastguard Worker
538*c33452fbSAndroid Build Coastguard Worker            raise self.IneligibleConfigurationAppliedError(self, criteria.export())
539*c33452fbSAndroid Build Coastguard Worker
540*c33452fbSAndroid Build Coastguard Worker    def _dumpPropagate(self, withCoverage, withNbUse):
541*c33452fbSAndroid Build Coastguard Worker        self.debug("Going to ask %s for description" % self.rootRule)
542*c33452fbSAndroid Build Coastguard Worker        for dumpedDescription in self.rootRule._dumpDescription(
543*c33452fbSAndroid Build Coastguard Worker                withCoverage=withCoverage,
544*c33452fbSAndroid Build Coastguard Worker                withNbUse=withNbUse):
545*c33452fbSAndroid Build Coastguard Worker            yield dumpedDescription.increasedRank()
546*c33452fbSAndroid Build Coastguard Worker
547*c33452fbSAndroid Build Coastguard Worker        self.debug("Going to ask %s for description" % self.criteronStates)
548*c33452fbSAndroid Build Coastguard Worker        for dumpedDescription in self.criteronStates._dumpDescription(
549*c33452fbSAndroid Build Coastguard Worker                withCoverage=False,
550*c33452fbSAndroid Build Coastguard Worker                withNbUse=withNbUse):
551*c33452fbSAndroid Build Coastguard Worker            yield dumpedDescription.increasedRank()
552*c33452fbSAndroid Build Coastguard Worker
553*c33452fbSAndroid Build Coastguard Worker
554*c33452fbSAndroid Build Coastguard Workerclass Domain(FromDomElement, DomPopulatedElement):
555*c33452fbSAndroid Build Coastguard Worker    tag = "ConfigurableDomain"
556*c33452fbSAndroid Build Coastguard Worker    childClasses = [DomElementLocation(Configuration, ["Configurations"])]
557*c33452fbSAndroid Build Coastguard Worker
558*c33452fbSAndroid Build Coastguard Worker
559*c33452fbSAndroid Build Coastguard Workerclass Domains(DomPopulatedElement):
560*c33452fbSAndroid Build Coastguard Worker    tag = "Domains"
561*c33452fbSAndroid Build Coastguard Worker    childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
562*c33452fbSAndroid Build Coastguard Worker
563*c33452fbSAndroid Build Coastguard Worker
564*c33452fbSAndroid Build Coastguard Workerclass RankedLine():
565*c33452fbSAndroid Build Coastguard Worker    def __init__(self, string,
566*c33452fbSAndroid Build Coastguard Worker                 stringPrefix="|-- ",
567*c33452fbSAndroid Build Coastguard Worker                 rankString="|   ",
568*c33452fbSAndroid Build Coastguard Worker                 linePrefix="",
569*c33452fbSAndroid Build Coastguard Worker                 lineSuffix="\n"):
570*c33452fbSAndroid Build Coastguard Worker        self.string = string
571*c33452fbSAndroid Build Coastguard Worker        self.rank = 0
572*c33452fbSAndroid Build Coastguard Worker        self.stringPrefix = stringPrefix
573*c33452fbSAndroid Build Coastguard Worker        self.rankString = rankString
574*c33452fbSAndroid Build Coastguard Worker        self.linePrefix = linePrefix
575*c33452fbSAndroid Build Coastguard Worker        self.lineSuffix = lineSuffix
576*c33452fbSAndroid Build Coastguard Worker
577*c33452fbSAndroid Build Coastguard Worker    def increasedRank(self):
578*c33452fbSAndroid Build Coastguard Worker        self.rank += 1
579*c33452fbSAndroid Build Coastguard Worker        return self
580*c33452fbSAndroid Build Coastguard Worker
581*c33452fbSAndroid Build Coastguard Worker    def __str__(self):
582*c33452fbSAndroid Build Coastguard Worker        return self.linePrefix + \
583*c33452fbSAndroid Build Coastguard Worker            self.rank * self.rankString + \
584*c33452fbSAndroid Build Coastguard Worker            self.stringPrefix + \
585*c33452fbSAndroid Build Coastguard Worker            self.string + \
586*c33452fbSAndroid Build Coastguard Worker            self.lineSuffix
587*c33452fbSAndroid Build Coastguard Worker
588*c33452fbSAndroid Build Coastguard Workerclass DebugRankedLine(RankedLine):
589*c33452fbSAndroid Build Coastguard Worker
590*c33452fbSAndroid Build Coastguard Worker    def __init__(self, string, lineSuffix=""):
591*c33452fbSAndroid Build Coastguard Worker        super().__init__(string,
592*c33452fbSAndroid Build Coastguard Worker                         stringPrefix="",
593*c33452fbSAndroid Build Coastguard Worker                         rankString="   ",
594*c33452fbSAndroid Build Coastguard Worker                         linePrefix="",
595*c33452fbSAndroid Build Coastguard Worker                         lineSuffix=lineSuffix)
596*c33452fbSAndroid Build Coastguard Worker
597*c33452fbSAndroid Build Coastguard Worker
598*c33452fbSAndroid Build Coastguard Workerclass CriterionState(Element):
599*c33452fbSAndroid Build Coastguard Worker    tag = "CriterionState"
600*c33452fbSAndroid Build Coastguard Worker    def used(self):
601*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
602*c33452fbSAndroid Build Coastguard Worker
603*c33452fbSAndroid Build Coastguard Worker
604*c33452fbSAndroid Build Coastguard Workerclass Criterion(Element):
605*c33452fbSAndroid Build Coastguard Worker    tag = "Criterion"
606*c33452fbSAndroid Build Coastguard Worker    inclusivenessTranslate = {True: "Inclusive", False: "Exclusive"}
607*c33452fbSAndroid Build Coastguard Worker
608*c33452fbSAndroid Build Coastguard Worker    class ChangeRequestToNonAccessibleState(CustomError):
609*c33452fbSAndroid Build Coastguard Worker        def __init__(self, requestedState, detail):
610*c33452fbSAndroid Build Coastguard Worker            self.requestedState = requestedState
611*c33452fbSAndroid Build Coastguard Worker            self.detail = detail
612*c33452fbSAndroid Build Coastguard Worker
613*c33452fbSAndroid Build Coastguard Worker        def __str__(self):
614*c33452fbSAndroid Build Coastguard Worker            return ("Change request to non accessible state %s. Detail: %s" %
615*c33452fbSAndroid Build Coastguard Worker                    (self.requestedState, self.detail))
616*c33452fbSAndroid Build Coastguard Worker
617*c33452fbSAndroid Build Coastguard Worker    def __init__(self, name, isInclusif,
618*c33452fbSAndroid Build Coastguard Worker                 stateNamesList, currentStateNamesList,
619*c33452fbSAndroid Build Coastguard Worker                 ignoreIntegrity=False):
620*c33452fbSAndroid Build Coastguard Worker        super().__init__(name)
621*c33452fbSAndroid Build Coastguard Worker        self.isInclusif = isInclusif
622*c33452fbSAndroid Build Coastguard Worker
623*c33452fbSAndroid Build Coastguard Worker        for state in stateNamesList:
624*c33452fbSAndroid Build Coastguard Worker            self.addChild(CriterionState(state))
625*c33452fbSAndroid Build Coastguard Worker
626*c33452fbSAndroid Build Coastguard Worker        self.currentState = []
627*c33452fbSAndroid Build Coastguard Worker        self.initStateNamesList = list(currentStateNamesList)
628*c33452fbSAndroid Build Coastguard Worker        self.changeState(self.initStateNamesList, ignoreIntegrity)
629*c33452fbSAndroid Build Coastguard Worker
630*c33452fbSAndroid Build Coastguard Worker    def reset(self):
631*c33452fbSAndroid Build Coastguard Worker        # Set current state as provided at initialisation
632*c33452fbSAndroid Build Coastguard Worker        self.changeState(self.initStateNamesList, ignoreIntegrity=True)
633*c33452fbSAndroid Build Coastguard Worker
634*c33452fbSAndroid Build Coastguard Worker    def changeState(self, subStateNames, ignoreIntegrity=False):
635*c33452fbSAndroid Build Coastguard Worker        self.debug("Changing state from: %s to: %s" % (
636*c33452fbSAndroid Build Coastguard Worker            list(self._getElementNames(self.currentState)),
637*c33452fbSAndroid Build Coastguard Worker            subStateNames))
638*c33452fbSAndroid Build Coastguard Worker
639*c33452fbSAndroid Build Coastguard Worker        if not ignoreIntegrity and not self.isIntegre(subStateNames):
640*c33452fbSAndroid Build Coastguard Worker            raise self.ChangeRequestToNonAccessibleState(subStateNames,
641*c33452fbSAndroid Build Coastguard Worker                                                         "An exclusive criterion must have a non \
642*c33452fbSAndroid Build Coastguard Worker                                                         empty state")
643*c33452fbSAndroid Build Coastguard Worker
644*c33452fbSAndroid Build Coastguard Worker        newCurrentState = []
645*c33452fbSAndroid Build Coastguard Worker        for subStateName in subStateNames:
646*c33452fbSAndroid Build Coastguard Worker            subState = self.getChildFromName(subStateName)
647*c33452fbSAndroid Build Coastguard Worker            subState.used()
648*c33452fbSAndroid Build Coastguard Worker            newCurrentState.append(subState)
649*c33452fbSAndroid Build Coastguard Worker
650*c33452fbSAndroid Build Coastguard Worker        self.currentState = newCurrentState
651*c33452fbSAndroid Build Coastguard Worker
652*c33452fbSAndroid Build Coastguard Worker        self._incNbUse()
653*c33452fbSAndroid Build Coastguard Worker        self._tellParentThatChildUsed()
654*c33452fbSAndroid Build Coastguard Worker
655*c33452fbSAndroid Build Coastguard Worker    def isIntegre(self, subStateNames):
656*c33452fbSAndroid Build Coastguard Worker        return self.isInclusif or len(subStateNames) == 1
657*c33452fbSAndroid Build Coastguard Worker
658*c33452fbSAndroid Build Coastguard Worker    def childUsed(self, child):
659*c33452fbSAndroid Build Coastguard Worker        self.currentState = child
660*c33452fbSAndroid Build Coastguard Worker        super().childUsed(child)
661*c33452fbSAndroid Build Coastguard Worker
662*c33452fbSAndroid Build Coastguard Worker    def export(self):
663*c33452fbSAndroid Build Coastguard Worker        subStateNames = self._getElementNames(self.currentState)
664*c33452fbSAndroid Build Coastguard Worker        return Criterion(self.name, self.isInclusif, subStateNames, subStateNames,
665*c33452fbSAndroid Build Coastguard Worker                         ignoreIntegrity=True)
666*c33452fbSAndroid Build Coastguard Worker
667*c33452fbSAndroid Build Coastguard Worker    def stateIncludes(self, subStateName):
668*c33452fbSAndroid Build Coastguard Worker        subStateCurrentNames = list(self._getElementNames(self.currentState))
669*c33452fbSAndroid Build Coastguard Worker
670*c33452fbSAndroid Build Coastguard Worker        self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
671*c33452fbSAndroid Build Coastguard Worker
672*c33452fbSAndroid Build Coastguard Worker        isIncluded = subStateName in subStateCurrentNames
673*c33452fbSAndroid Build Coastguard Worker        self.debug("IsIncluded: %s" % isIncluded)
674*c33452fbSAndroid Build Coastguard Worker
675*c33452fbSAndroid Build Coastguard Worker        return isIncluded
676*c33452fbSAndroid Build Coastguard Worker
677*c33452fbSAndroid Build Coastguard Worker
678*c33452fbSAndroid Build Coastguard Worker    def stateIs(self, subStateNames):
679*c33452fbSAndroid Build Coastguard Worker        if len(self.currentState) != 1:
680*c33452fbSAndroid Build Coastguard Worker            return False
681*c33452fbSAndroid Build Coastguard Worker        else:
682*c33452fbSAndroid Build Coastguard Worker            return self.stateIncludes(subStateNames)
683*c33452fbSAndroid Build Coastguard Worker
684*c33452fbSAndroid Build Coastguard Worker    def _getXMLAttributes(self):
685*c33452fbSAndroid Build Coastguard Worker        attributes = super()._getXMLAttributes()
686*c33452fbSAndroid Build Coastguard Worker        attributes["Type"] = self.inclusivenessTranslate[self.isInclusif]
687*c33452fbSAndroid Build Coastguard Worker        return attributes
688*c33452fbSAndroid Build Coastguard Worker
689*c33452fbSAndroid Build Coastguard Worker
690*c33452fbSAndroid Build Coastguard Workerclass Criteria(Element):
691*c33452fbSAndroid Build Coastguard Worker    tag = "Criteria"
692*c33452fbSAndroid Build Coastguard Worker
693*c33452fbSAndroid Build Coastguard Worker    class DuplicatedCriterionError(DuplicatedChildError):
694*c33452fbSAndroid Build Coastguard Worker        pass
695*c33452fbSAndroid Build Coastguard Worker
696*c33452fbSAndroid Build Coastguard Worker    def export(self):
697*c33452fbSAndroid Build Coastguard Worker        self.debug("Exporting criteria")
698*c33452fbSAndroid Build Coastguard Worker        assert(self.children)
699*c33452fbSAndroid Build Coastguard Worker
700*c33452fbSAndroid Build Coastguard Worker        exported = Criteria(self.name)
701*c33452fbSAndroid Build Coastguard Worker        for child in self.children:
702*c33452fbSAndroid Build Coastguard Worker            exported.addChild(child.export())
703*c33452fbSAndroid Build Coastguard Worker        return exported
704*c33452fbSAndroid Build Coastguard Worker
705*c33452fbSAndroid Build Coastguard Worker    def addChild(self, child):
706*c33452fbSAndroid Build Coastguard Worker        if child in self.children:
707*c33452fbSAndroid Build Coastguard Worker            raise self.DuplicatedCriterionError(self, child)
708*c33452fbSAndroid Build Coastguard Worker        super().addChild(child)
709*c33452fbSAndroid Build Coastguard Worker
710*c33452fbSAndroid Build Coastguard Workerclass ConfigAppliedWithoutCriteriaError(CustomError):
711*c33452fbSAndroid Build Coastguard Worker    def __init__(self, configurationName, domainName):
712*c33452fbSAndroid Build Coastguard Worker        self.configurationName = configurationName
713*c33452fbSAndroid Build Coastguard Worker        self.domainName = domainName
714*c33452fbSAndroid Build Coastguard Worker    def __str__(self):
715*c33452fbSAndroid Build Coastguard Worker        return ('Applying configuration "%s" from domain "%s" before declaring criteria' %
716*c33452fbSAndroid Build Coastguard Worker                (self.configurationName, self.domainName))
717*c33452fbSAndroid Build Coastguard Worker
718*c33452fbSAndroid Build Coastguard Workerclass ParsePFWlog():
719*c33452fbSAndroid Build Coastguard Worker    MATCH = "match"
720*c33452fbSAndroid Build Coastguard Worker    ACTION = "action"
721*c33452fbSAndroid Build Coastguard Worker
722*c33452fbSAndroid Build Coastguard Worker    class ChangeRequestOnUnknownCriterion(CustomError):
723*c33452fbSAndroid Build Coastguard Worker        def __init__(self, criterion):
724*c33452fbSAndroid Build Coastguard Worker            self.criterion = criterion
725*c33452fbSAndroid Build Coastguard Worker
726*c33452fbSAndroid Build Coastguard Worker        def __str__(self):
727*c33452fbSAndroid Build Coastguard Worker            return ("Change request on an unknown criterion %s." %
728*c33452fbSAndroid Build Coastguard Worker                    self.criterion)
729*c33452fbSAndroid Build Coastguard Worker
730*c33452fbSAndroid Build Coastguard Worker    def __init__(self, domains, criteria, ErrorsToIgnore=()):
731*c33452fbSAndroid Build Coastguard Worker
732*c33452fbSAndroid Build Coastguard Worker        self.domains = domains
733*c33452fbSAndroid Build Coastguard Worker        self.criteria = criteria
734*c33452fbSAndroid Build Coastguard Worker        self.ErrorsToIgnore = ErrorsToIgnore
735*c33452fbSAndroid Build Coastguard Worker
736*c33452fbSAndroid Build Coastguard Worker        configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
737*c33452fbSAndroid Build Coastguard Worker        matchConfigApplicationLine = re.compile(configApplicationRegext).match
738*c33452fbSAndroid Build Coastguard Worker
739*c33452fbSAndroid Build Coastguard Worker        criterionCreationRegext = ", ".join([
740*c33452fbSAndroid Build Coastguard Worker            r""".*Criterion name: (.*)""",
741*c33452fbSAndroid Build Coastguard Worker            r"""type kind: (.*)""",
742*c33452fbSAndroid Build Coastguard Worker            r"""current state: (.*)""",
743*c33452fbSAndroid Build Coastguard Worker            r"""states: {(.*)}"""
744*c33452fbSAndroid Build Coastguard Worker        ])
745*c33452fbSAndroid Build Coastguard Worker        matchCriterionCreationLine = re.compile(criterionCreationRegext).match
746*c33452fbSAndroid Build Coastguard Worker
747*c33452fbSAndroid Build Coastguard Worker        changingCriterionRegext = r""".*Selection criterion changed event: Criterion name: (.*), \
748*c33452fbSAndroid Build Coastguard Worker            current state: ([^\n\r]*)"""
749*c33452fbSAndroid Build Coastguard Worker        matchChangingCriterionLine = re.compile(changingCriterionRegext).match
750*c33452fbSAndroid Build Coastguard Worker
751*c33452fbSAndroid Build Coastguard Worker        self.lineLogTypes = [
752*c33452fbSAndroid Build Coastguard Worker            {
753*c33452fbSAndroid Build Coastguard Worker                self.MATCH: matchConfigApplicationLine,
754*c33452fbSAndroid Build Coastguard Worker                self.ACTION: self._configApplication
755*c33452fbSAndroid Build Coastguard Worker            }, {
756*c33452fbSAndroid Build Coastguard Worker                self.MATCH: matchCriterionCreationLine,
757*c33452fbSAndroid Build Coastguard Worker                self.ACTION: self._criterionCreation
758*c33452fbSAndroid Build Coastguard Worker            }, {
759*c33452fbSAndroid Build Coastguard Worker                self.MATCH: matchChangingCriterionLine,
760*c33452fbSAndroid Build Coastguard Worker                self.ACTION: self._changingCriterion
761*c33452fbSAndroid Build Coastguard Worker            }
762*c33452fbSAndroid Build Coastguard Worker        ]
763*c33452fbSAndroid Build Coastguard Worker
764*c33452fbSAndroid Build Coastguard Worker    @staticmethod
765*c33452fbSAndroid Build Coastguard Worker    def _formatCriterionList(liststring, separator):
766*c33452fbSAndroid Build Coastguard Worker        list = liststring.split(separator)
767*c33452fbSAndroid Build Coastguard Worker        if len(list) == 1 and list[0] == "<none>":
768*c33452fbSAndroid Build Coastguard Worker            list = []
769*c33452fbSAndroid Build Coastguard Worker        return list
770*c33452fbSAndroid Build Coastguard Worker
771*c33452fbSAndroid Build Coastguard Worker    def _criterionCreation(self, matchCriterionCreation):
772*c33452fbSAndroid Build Coastguard Worker        # Unpack
773*c33452fbSAndroid Build Coastguard Worker        criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
774*c33452fbSAndroid Build Coastguard Worker
775*c33452fbSAndroid Build Coastguard Worker        criterionStateList = self._formatCriterionList(criterionStates, ", ")
776*c33452fbSAndroid Build Coastguard Worker
777*c33452fbSAndroid Build Coastguard Worker        criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
778*c33452fbSAndroid Build Coastguard Worker
779*c33452fbSAndroid Build Coastguard Worker        currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
780*c33452fbSAndroid Build Coastguard Worker
781*c33452fbSAndroid Build Coastguard Worker        logger.info("Creating criterion: " + criterionName +
782*c33452fbSAndroid Build Coastguard Worker                    " (" + criterionType + ") " +
783*c33452fbSAndroid Build Coastguard Worker                    " with current state: " + str(currentcriterionStateList) +
784*c33452fbSAndroid Build Coastguard Worker                    ", possible states:" + str(criterionStateList))
785*c33452fbSAndroid Build Coastguard Worker
786*c33452fbSAndroid Build Coastguard Worker        try:
787*c33452fbSAndroid Build Coastguard Worker            self.criteria.addChild(Criterion(
788*c33452fbSAndroid Build Coastguard Worker                criterionName,
789*c33452fbSAndroid Build Coastguard Worker                criterionIsInclusif,
790*c33452fbSAndroid Build Coastguard Worker                criterionStateList,
791*c33452fbSAndroid Build Coastguard Worker                currentcriterionStateList
792*c33452fbSAndroid Build Coastguard Worker            ))
793*c33452fbSAndroid Build Coastguard Worker        except self.criteria.DuplicatedCriterionError as ex:
794*c33452fbSAndroid Build Coastguard Worker            logger.debug(ex)
795*c33452fbSAndroid Build Coastguard Worker            logger.warning("Reseting criterion %s. Did you reset the PFW ?" % criterionName)
796*c33452fbSAndroid Build Coastguard Worker            self.criteria.operationOnChild(
797*c33452fbSAndroid Build Coastguard Worker                [criterionName],
798*c33452fbSAndroid Build Coastguard Worker                lambda criterion: criterion.reset()
799*c33452fbSAndroid Build Coastguard Worker            )
800*c33452fbSAndroid Build Coastguard Worker
801*c33452fbSAndroid Build Coastguard Worker
802*c33452fbSAndroid Build Coastguard Worker
803*c33452fbSAndroid Build Coastguard Worker    def _changingCriterion(self, matchChangingCriterion):
804*c33452fbSAndroid Build Coastguard Worker        # Unpack
805*c33452fbSAndroid Build Coastguard Worker        criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
806*c33452fbSAndroid Build Coastguard Worker
807*c33452fbSAndroid Build Coastguard Worker        newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
808*c33452fbSAndroid Build Coastguard Worker
809*c33452fbSAndroid Build Coastguard Worker        logger.info("Changing criterion %s to %s" % (criterionName, newCriterionState))
810*c33452fbSAndroid Build Coastguard Worker
811*c33452fbSAndroid Build Coastguard Worker        path = [criterionName]
812*c33452fbSAndroid Build Coastguard Worker        changeCriterionOperation = lambda criterion: criterion.changeState(newCriterionState)
813*c33452fbSAndroid Build Coastguard Worker        try:
814*c33452fbSAndroid Build Coastguard Worker            self.criteria.operationOnChild(path, changeCriterionOperation)
815*c33452fbSAndroid Build Coastguard Worker        except ChildNotFoundError:
816*c33452fbSAndroid Build Coastguard Worker            raise self.ChangeRequestOnUnknownCriterion(criterionName)
817*c33452fbSAndroid Build Coastguard Worker
818*c33452fbSAndroid Build Coastguard Worker    def _configApplication(self, matchConfig):
819*c33452fbSAndroid Build Coastguard Worker        # Unpack
820*c33452fbSAndroid Build Coastguard Worker        configurationName, domainName = matchConfig.group(1, 2)
821*c33452fbSAndroid Build Coastguard Worker
822*c33452fbSAndroid Build Coastguard Worker        # Check that at least one criterion exist
823*c33452fbSAndroid Build Coastguard Worker        if not self.criteria.hasChildren():
824*c33452fbSAndroid Build Coastguard Worker            logger.error("Applying configuration before declaring criteria")
825*c33452fbSAndroid Build Coastguard Worker            logger.info("Is the log starting at PFW boot ?")
826*c33452fbSAndroid Build Coastguard Worker            raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
827*c33452fbSAndroid Build Coastguard Worker
828*c33452fbSAndroid Build Coastguard Worker        # Change criterion state
829*c33452fbSAndroid Build Coastguard Worker        path = [domainName, configurationName]
830*c33452fbSAndroid Build Coastguard Worker        usedOperation = lambda element: element.used(self.criteria)
831*c33452fbSAndroid Build Coastguard Worker
832*c33452fbSAndroid Build Coastguard Worker        logger.info("Applying configuration %s from domain %s" % (
833*c33452fbSAndroid Build Coastguard Worker            configurationName, domainName))
834*c33452fbSAndroid Build Coastguard Worker
835*c33452fbSAndroid Build Coastguard Worker        self.domains.operationOnChild(path, usedOperation)
836*c33452fbSAndroid Build Coastguard Worker
837*c33452fbSAndroid Build Coastguard Worker
838*c33452fbSAndroid Build Coastguard Worker    def _digest(self, lineLogType, lineLog):
839*c33452fbSAndroid Build Coastguard Worker
840*c33452fbSAndroid Build Coastguard Worker        match = lineLogType[self.MATCH](lineLog)
841*c33452fbSAndroid Build Coastguard Worker        if match:
842*c33452fbSAndroid Build Coastguard Worker            lineLogType[self.ACTION](match)
843*c33452fbSAndroid Build Coastguard Worker            return True
844*c33452fbSAndroid Build Coastguard Worker        return False
845*c33452fbSAndroid Build Coastguard Worker
846*c33452fbSAndroid Build Coastguard Worker
847*c33452fbSAndroid Build Coastguard Worker    def parsePFWlog(self, lines):
848*c33452fbSAndroid Build Coastguard Worker        for lineNb, lineLog in enumerate(lines, 1): # line number starts at 1
849*c33452fbSAndroid Build Coastguard Worker
850*c33452fbSAndroid Build Coastguard Worker            logger.debug("Parsing line :%s" % lineLog.rstrip())
851*c33452fbSAndroid Build Coastguard Worker
852*c33452fbSAndroid Build Coastguard Worker            digested = (self._digest(lineLogType, lineLog)
853*c33452fbSAndroid Build Coastguard Worker                        for lineLogType in self.lineLogTypes)
854*c33452fbSAndroid Build Coastguard Worker
855*c33452fbSAndroid Build Coastguard Worker            try:
856*c33452fbSAndroid Build Coastguard Worker                success = any(digested)
857*c33452fbSAndroid Build Coastguard Worker
858*c33452fbSAndroid Build Coastguard Worker            # Catch some exception in order to print the current parsing line,
859*c33452fbSAndroid Build Coastguard Worker            # then raise the exception again if not continue of error
860*c33452fbSAndroid Build Coastguard Worker            except CustomError as ex:
861*c33452fbSAndroid Build Coastguard Worker                logger.error('Error raised while parsing line %s: "%s"' %
862*c33452fbSAndroid Build Coastguard Worker                             (lineNb, repr(lineLog)))
863*c33452fbSAndroid Build Coastguard Worker
864*c33452fbSAndroid Build Coastguard Worker                # If exception is a subclass of ErrorsToIgnore, log it and continue
865*c33452fbSAndroid Build Coastguard Worker                # otherwise raise it again.
866*c33452fbSAndroid Build Coastguard Worker                if not issubclass(type(ex), self.ErrorsToIgnore):
867*c33452fbSAndroid Build Coastguard Worker                    raise ex
868*c33452fbSAndroid Build Coastguard Worker                else:
869*c33452fbSAndroid Build Coastguard Worker                    logger.error('Ignoring exception:"%s", '
870*c33452fbSAndroid Build Coastguard Worker                                 'can not guarantee database integrity' % ex)
871*c33452fbSAndroid Build Coastguard Worker            else:
872*c33452fbSAndroid Build Coastguard Worker                if not success:
873*c33452fbSAndroid Build Coastguard Worker                    logger.debug("Line does not match, dropped")
874*c33452fbSAndroid Build Coastguard Worker
875*c33452fbSAndroid Build Coastguard Worker
876*c33452fbSAndroid Build Coastguard Workerclass Root(Element):
877*c33452fbSAndroid Build Coastguard Worker    tag = "CoverageReport"
878*c33452fbSAndroid Build Coastguard Worker    def __init__(self, name, dom):
879*c33452fbSAndroid Build Coastguard Worker        super().__init__(name)
880*c33452fbSAndroid Build Coastguard Worker        # Create domain tree
881*c33452fbSAndroid Build Coastguard Worker        self.domains = Domains("Domains")
882*c33452fbSAndroid Build Coastguard Worker        self.domains.populate(dom)
883*c33452fbSAndroid Build Coastguard Worker        self.addChild(self.domains)
884*c33452fbSAndroid Build Coastguard Worker        # Create criterion list
885*c33452fbSAndroid Build Coastguard Worker        self.criteria = Criteria("CriterionRoot")
886*c33452fbSAndroid Build Coastguard Worker        self.addChild(self.criteria)
887*c33452fbSAndroid Build Coastguard Worker
888*c33452fbSAndroid Build Coastguard Worker    def exportToXML(self):
889*c33452fbSAndroid Build Coastguard Worker        """Export tree to an xml document"""
890*c33452fbSAndroid Build Coastguard Worker        impl = xml.dom.minidom.getDOMImplementation()
891*c33452fbSAndroid Build Coastguard Worker        document = impl.createDocument(namespaceURI=None, qualifiedName=self.tag, doctype=None)
892*c33452fbSAndroid Build Coastguard Worker        super().exportToXML(document, document.documentElement)
893*c33452fbSAndroid Build Coastguard Worker
894*c33452fbSAndroid Build Coastguard Worker        return document
895*c33452fbSAndroid Build Coastguard Worker
896*c33452fbSAndroid Build Coastguard Worker# ============================
897*c33452fbSAndroid Build Coastguard Worker# Command line argument parser
898*c33452fbSAndroid Build Coastguard Worker# ============================
899*c33452fbSAndroid Build Coastguard Worker
900*c33452fbSAndroid Build Coastguard Worker
901*c33452fbSAndroid Build Coastguard Workerclass ArgumentParser:
902*c33452fbSAndroid Build Coastguard Worker    """class that parse command line arguments with argparse library
903*c33452fbSAndroid Build Coastguard Worker
904*c33452fbSAndroid Build Coastguard Worker    Result of parsing are the class attributes.
905*c33452fbSAndroid Build Coastguard Worker    """
906*c33452fbSAndroid Build Coastguard Worker    levelTranslate = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
907*c33452fbSAndroid Build Coastguard Worker
908*c33452fbSAndroid Build Coastguard Worker    def __init__(self):
909*c33452fbSAndroid Build Coastguard Worker
910*c33452fbSAndroid Build Coastguard Worker        try:
911*c33452fbSAndroid Build Coastguard Worker            # As argparse is only in the stdlib since python 3.2,
912*c33452fbSAndroid Build Coastguard Worker            # testing its availability
913*c33452fbSAndroid Build Coastguard Worker            import argparse
914*c33452fbSAndroid Build Coastguard Worker
915*c33452fbSAndroid Build Coastguard Worker        except ImportError:
916*c33452fbSAndroid Build Coastguard Worker            logger.warning("Unable to import argparse "
917*c33452fbSAndroid Build Coastguard Worker                           "(parser for command-line options and arguments), "
918*c33452fbSAndroid Build Coastguard Worker                           "using default argument values:")
919*c33452fbSAndroid Build Coastguard Worker
920*c33452fbSAndroid Build Coastguard Worker            logger.warning(" - InputFile: stdin")
921*c33452fbSAndroid Build Coastguard Worker            self.inputFile = sys.stdin
922*c33452fbSAndroid Build Coastguard Worker
923*c33452fbSAndroid Build Coastguard Worker            logger.warning(" - OutputFile: stdout")
924*c33452fbSAndroid Build Coastguard Worker            self.outputFile = sys.stdout
925*c33452fbSAndroid Build Coastguard Worker
926*c33452fbSAndroid Build Coastguard Worker            try:
927*c33452fbSAndroid Build Coastguard Worker                self.domainsFile = sys.argv[1]
928*c33452fbSAndroid Build Coastguard Worker            except IndexError as ex:
929*c33452fbSAndroid Build Coastguard Worker                logger.fatal("No domain file provided (first argument)")
930*c33452fbSAndroid Build Coastguard Worker                raise ex
931*c33452fbSAndroid Build Coastguard Worker            else:
932*c33452fbSAndroid Build Coastguard Worker                logger.warning(" - Domain file: " + self.domainsFile)
933*c33452fbSAndroid Build Coastguard Worker
934*c33452fbSAndroid Build Coastguard Worker            logger.warning(" - Output format: xml")
935*c33452fbSAndroid Build Coastguard Worker            self.XMLreport = True
936*c33452fbSAndroid Build Coastguard Worker
937*c33452fbSAndroid Build Coastguard Worker            logger.warning(" - Debug level: error")
938*c33452fbSAndroid Build Coastguard Worker            self.debugLevel = logging.ERROR
939*c33452fbSAndroid Build Coastguard Worker        else:
940*c33452fbSAndroid Build Coastguard Worker
941*c33452fbSAndroid Build Coastguard Worker            myArgParser = argparse.ArgumentParser(description='Generate PFW report')
942*c33452fbSAndroid Build Coastguard Worker
943*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
944*c33452fbSAndroid Build Coastguard Worker                'domainsFile',
945*c33452fbSAndroid Build Coastguard Worker                type=argparse.FileType('r'),
946*c33452fbSAndroid Build Coastguard Worker                help="the PFW domain XML file"
947*c33452fbSAndroid Build Coastguard Worker            )
948*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
949*c33452fbSAndroid Build Coastguard Worker                'pfwlog', nargs='?',
950*c33452fbSAndroid Build Coastguard Worker                type=argparse.FileType('r'), default=sys.stdin,
951*c33452fbSAndroid Build Coastguard Worker                help="the PFW log file, default stdin"
952*c33452fbSAndroid Build Coastguard Worker            )
953*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
954*c33452fbSAndroid Build Coastguard Worker                '-o', '--output',
955*c33452fbSAndroid Build Coastguard Worker                dest="outputFile",
956*c33452fbSAndroid Build Coastguard Worker                type=argparse.FileType('w'), default=sys.stdout,
957*c33452fbSAndroid Build Coastguard Worker                help="the coverage report output file, default stdout"
958*c33452fbSAndroid Build Coastguard Worker            )
959*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
960*c33452fbSAndroid Build Coastguard Worker                '-v', '--verbose',
961*c33452fbSAndroid Build Coastguard Worker                dest="debugLevel", default=0,
962*c33452fbSAndroid Build Coastguard Worker                action='count',
963*c33452fbSAndroid Build Coastguard Worker                help="print debug warnings from warning (default) to debug (-vv)"
964*c33452fbSAndroid Build Coastguard Worker            )
965*c33452fbSAndroid Build Coastguard Worker
966*c33452fbSAndroid Build Coastguard Worker            outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
967*c33452fbSAndroid Build Coastguard Worker
968*c33452fbSAndroid Build Coastguard Worker            outputFormatGroupe.add_argument(
969*c33452fbSAndroid Build Coastguard Worker                '--xml',
970*c33452fbSAndroid Build Coastguard Worker                dest="xmlFlag",
971*c33452fbSAndroid Build Coastguard Worker                action='store_true',
972*c33452fbSAndroid Build Coastguard Worker                help=" XML coverage output report"
973*c33452fbSAndroid Build Coastguard Worker            )
974*c33452fbSAndroid Build Coastguard Worker            outputFormatGroupe.add_argument(
975*c33452fbSAndroid Build Coastguard Worker                '--raw',
976*c33452fbSAndroid Build Coastguard Worker                dest="rawFlag",
977*c33452fbSAndroid Build Coastguard Worker                action='store_true',
978*c33452fbSAndroid Build Coastguard Worker                help="raw coverage output report"
979*c33452fbSAndroid Build Coastguard Worker            )
980*c33452fbSAndroid Build Coastguard Worker
981*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
982*c33452fbSAndroid Build Coastguard Worker                '--ignore-unknown-criterion',
983*c33452fbSAndroid Build Coastguard Worker                dest="unknwonCriterionFlag",
984*c33452fbSAndroid Build Coastguard Worker                action='store_true',
985*c33452fbSAndroid Build Coastguard Worker                help="ignore unknown criterion"
986*c33452fbSAndroid Build Coastguard Worker            )
987*c33452fbSAndroid Build Coastguard Worker
988*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
989*c33452fbSAndroid Build Coastguard Worker                '--ignore-incoherent-criterion-state',
990*c33452fbSAndroid Build Coastguard Worker                dest="incoherentCriterionFlag",
991*c33452fbSAndroid Build Coastguard Worker                action='store_true',
992*c33452fbSAndroid Build Coastguard Worker                help="ignore criterion transition to incoherent state"
993*c33452fbSAndroid Build Coastguard Worker            )
994*c33452fbSAndroid Build Coastguard Worker
995*c33452fbSAndroid Build Coastguard Worker            myArgParser.add_argument(
996*c33452fbSAndroid Build Coastguard Worker                '--ignore-ineligible-configuration-application',
997*c33452fbSAndroid Build Coastguard Worker                dest="ineligibleConfigurationApplicationFlag",
998*c33452fbSAndroid Build Coastguard Worker                action='store_true',
999*c33452fbSAndroid Build Coastguard Worker                help="ignore application of configuration with a false rule "
1000*c33452fbSAndroid Build Coastguard Worker                "(not applicable configuration)"
1001*c33452fbSAndroid Build Coastguard Worker            )
1002*c33452fbSAndroid Build Coastguard Worker
1003*c33452fbSAndroid Build Coastguard Worker            # Process command line arguments
1004*c33452fbSAndroid Build Coastguard Worker            options = myArgParser.parse_args()
1005*c33452fbSAndroid Build Coastguard Worker
1006*c33452fbSAndroid Build Coastguard Worker            # Mapping to attributes
1007*c33452fbSAndroid Build Coastguard Worker            self.inputFile = options.pfwlog
1008*c33452fbSAndroid Build Coastguard Worker            self.outputFile = options.outputFile
1009*c33452fbSAndroid Build Coastguard Worker            self.domainsFile = options.domainsFile
1010*c33452fbSAndroid Build Coastguard Worker
1011*c33452fbSAndroid Build Coastguard Worker            # Output report in xml if flag not set
1012*c33452fbSAndroid Build Coastguard Worker            self.XMLreport = not options.rawFlag
1013*c33452fbSAndroid Build Coastguard Worker
1014*c33452fbSAndroid Build Coastguard Worker            # Setting logger level
1015*c33452fbSAndroid Build Coastguard Worker            levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
1016*c33452fbSAndroid Build Coastguard Worker            self.debugLevel = self.levelTranslate[levelCapped]
1017*c33452fbSAndroid Build Coastguard Worker
1018*c33452fbSAndroid Build Coastguard Worker            # Setting ignore options
1019*c33452fbSAndroid Build Coastguard Worker            errorToIgnore = []
1020*c33452fbSAndroid Build Coastguard Worker            if options.ineligibleConfigurationApplicationFlag:
1021*c33452fbSAndroid Build Coastguard Worker                errorToIgnore.append(Configuration.IneligibleConfigurationAppliedError)
1022*c33452fbSAndroid Build Coastguard Worker
1023*c33452fbSAndroid Build Coastguard Worker            if options.incoherentCriterionFlag:
1024*c33452fbSAndroid Build Coastguard Worker                errorToIgnore.append(Criterion.ChangeRequestToNonAccessibleState)
1025*c33452fbSAndroid Build Coastguard Worker
1026*c33452fbSAndroid Build Coastguard Worker            if options.unknwonCriterionFlag:
1027*c33452fbSAndroid Build Coastguard Worker                errorToIgnore.append(ParsePFWlog.ChangeRequestOnUnknownCriterion)
1028*c33452fbSAndroid Build Coastguard Worker
1029*c33452fbSAndroid Build Coastguard Worker            self.errorToIgnore = tuple(errorToIgnore)
1030*c33452fbSAndroid Build Coastguard Worker
1031*c33452fbSAndroid Build Coastguard Worker
1032*c33452fbSAndroid Build Coastguard Worker
1033*c33452fbSAndroid Build Coastguard Workerdef main():
1034*c33452fbSAndroid Build Coastguard Worker
1035*c33452fbSAndroid Build Coastguard Worker    errorDuringLogParsing = -1
1036*c33452fbSAndroid Build Coastguard Worker    errorDuringArgumentParsing = 1
1037*c33452fbSAndroid Build Coastguard Worker
1038*c33452fbSAndroid Build Coastguard Worker    try:
1039*c33452fbSAndroid Build Coastguard Worker        commandLineArguments = ArgumentParser()
1040*c33452fbSAndroid Build Coastguard Worker    except LookupError as ex:
1041*c33452fbSAndroid Build Coastguard Worker        logger.error("Error during argument parsing")
1042*c33452fbSAndroid Build Coastguard Worker        logger.debug(str(ex))
1043*c33452fbSAndroid Build Coastguard Worker        sys.exit(errorDuringArgumentParsing)
1044*c33452fbSAndroid Build Coastguard Worker
1045*c33452fbSAndroid Build Coastguard Worker    # Setting logger level
1046*c33452fbSAndroid Build Coastguard Worker    logger.setLevel(commandLineArguments.debugLevel)
1047*c33452fbSAndroid Build Coastguard Worker    logger.info("Log level set to: %s" %
1048*c33452fbSAndroid Build Coastguard Worker                logging.getLevelName(commandLineArguments.debugLevel))
1049*c33452fbSAndroid Build Coastguard Worker
1050*c33452fbSAndroid Build Coastguard Worker    # Create tree from XML
1051*c33452fbSAndroid Build Coastguard Worker    dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
1052*c33452fbSAndroid Build Coastguard Worker
1053*c33452fbSAndroid Build Coastguard Worker    # Create element tree
1054*c33452fbSAndroid Build Coastguard Worker    root = Root("DomainCoverage", dom)
1055*c33452fbSAndroid Build Coastguard Worker
1056*c33452fbSAndroid Build Coastguard Worker    # Parse PFW events
1057*c33452fbSAndroid Build Coastguard Worker    parser = ParsePFWlog(root.domains, root.criteria, commandLineArguments.errorToIgnore)
1058*c33452fbSAndroid Build Coastguard Worker
1059*c33452fbSAndroid Build Coastguard Worker    try:
1060*c33452fbSAndroid Build Coastguard Worker        parser.parsePFWlog(commandLineArguments.inputFile.readlines())
1061*c33452fbSAndroid Build Coastguard Worker    except CustomError as ex:
1062*c33452fbSAndroid Build Coastguard Worker        logger.fatal("Error during parsing log file %s: %s" %
1063*c33452fbSAndroid Build Coastguard Worker                     (commandLineArguments.inputFile, ex))
1064*c33452fbSAndroid Build Coastguard Worker        sys.exit(errorDuringLogParsing)
1065*c33452fbSAndroid Build Coastguard Worker
1066*c33452fbSAndroid Build Coastguard Worker    # Output report
1067*c33452fbSAndroid Build Coastguard Worker    outputFile = commandLineArguments.outputFile
1068*c33452fbSAndroid Build Coastguard Worker
1069*c33452fbSAndroid Build Coastguard Worker    if not commandLineArguments.XMLreport:
1070*c33452fbSAndroid Build Coastguard Worker        outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
1071*c33452fbSAndroid Build Coastguard Worker    else:
1072*c33452fbSAndroid Build Coastguard Worker        outputFile.write(root.exportToXML().toprettyxml())
1073*c33452fbSAndroid Build Coastguard Worker
1074*c33452fbSAndroid Build Coastguard Worker
1075*c33452fbSAndroid Build Coastguard Workerif __name__ == "__main__":
1076*c33452fbSAndroid Build Coastguard Worker    """ Execute main if the python interpreter is running this module as the main program """
1077*c33452fbSAndroid Build Coastguard Worker    main()
1078*c33452fbSAndroid Build Coastguard Worker
1079