coverage.py revision 804e064dcd02b87e04b9a189422cc14205e8125c
1326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard#!/usr/bin/env python3
2326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
3fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# INTEL CONFIDENTIAL
4fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# Copyright  2013 Intel
5fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# Corporation All Rights Reserved.
6fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard#
7fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# The source code contained or described herein and all documents related to
8fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# the source code ("Material") are owned by Intel Corporation or its suppliers
9fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# or licensors. Title to the Material remains with Intel Corporation or its
10fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# suppliers and licensors. The Material contains trade secrets and proprietary
11fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# and confidential information of Intel or its suppliers and licensors. The
12fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# Material is protected by worldwide copyright and trade secret laws and
13fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# treaty provisions. No part of the Material may be used, copied, reproduced,
14fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# modified, published, uploaded, posted, transmitted, distributed, or
15fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# disclosed in any way without Intels prior express written permission.
16fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard#
17fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# No license under any patent, copyright, trade secret or other intellectual
18fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# property right is granted to or conferred upon you by disclosure or delivery
19fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# of the Materials, either expressly, by implication, inducement, estoppel or
20fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# otherwise. Any license under such intellectual property rights must be
21fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard# express and approved by Intel in writing.
22fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard
23326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard"""
24326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardGenerate a coverage report by parsing parameter framework log.
25326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
26326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardThe coverage report contains the:
27326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - domain
28326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - configuration
29326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - rule
30326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - criterion
31326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardbasic coverage statistics.
32326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard"""
33326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
34326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport xml.dom.minidom
35326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport sys
36326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport re
37326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport logging
38326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
39326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardFORMAT = '%(levelname)s: %(message)s'
40326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardlogging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
41326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardlogger = logging.getLogger("Coverage")
42326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
433aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass CustomError(Exception):
44804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    pass
453aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
463aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ChildError(CustomError):
47804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, parent, child):
48804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = parent
49804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.child = child
503aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
513aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ChildNotFoundError(ChildError):
52804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
53804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "Unable to find the child %s in %s" % (self.child, self.parent)
543aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
553aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass DuplicatedChildError(ChildError):
56804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
57804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "Add existing child %s in %s." % (self.child, self.parent)
58326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
59326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Element():
60804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Root class for all coverage elements"""
61804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "element"
62326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
63804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name):
64326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
65804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = None
66804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.children = []
67326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
68804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.nbUse = 0
69326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
70804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = name
71326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
72804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("New element")
73326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
74326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
75804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
76804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return  "%s (%s)" % (self.name, self.tag)
77326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
78804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __eq__(self, compared):
79804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return (self.name == compared.name) and (self.children == compared.children)
80326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
81804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getName(self, default=""):
82804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.name or default
83326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
84804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def hasChildren(self):
85804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return bool(self.children)
86326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
87804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getChildren(self):
88804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.children
89326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
90804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getDescendants(self):
91804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children:
92804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield child
93804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for descendant in child._getDescendants() :
94804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                yield descendant
95326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
96804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getChildFromName(self, childName):
97326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
98804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
99326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
100804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if child.getName() == childName :
101804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return child
102326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
103804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Child %s not found" % childName, logging.ERROR)
104326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
105804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Child list :")
106326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
107804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
108804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("  - %s" % child)
109326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
110804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        raise ChildNotFoundError(self, childName)
111326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
112326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
113804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def addChild(self, child):
114804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("new child: " + child.name)
115804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.children.append(child)
116804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        child._adoptedBy(self)
117326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
118804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _adoptedBy(self, parent):
119804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(not self.parent)
120804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = parent
121326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
122804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getElementNames(self, elementList):
123804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return (substate.name for substate in elementList)
124326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
125804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _description(self, withCoverage, withNbUse):
126804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        description = self.name
127326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
128804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if withNbUse or withCoverage :
129804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            description += " has been used " + str(self.nbUse) + " time"
130326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
131804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if withCoverage :
132804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            description += self._coverageFormating(self._getCoverage())
133326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
134804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return description
135326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
136326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
137804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverage(self):
138804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Return the coverage of the element between 0 and 1
139326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
140804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If the element has no coverage dependency (usually child) return 0 or 1.
141804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        otherwise the element coverage is the dependency coverage average"""
142804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverageDependanceElements = list(self._getCoverageDependanceElements())
143326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
144804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        nbcoverageDependence = len(coverageDependanceElements)
145326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
146804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if nbcoverageDependence == 0:
147804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if self.nbUse == 0:
148804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return 0
149804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
150804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return 1
151326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
152804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverageDependenceValues = (depElement._getCoverage()
153804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                for depElement in coverageDependanceElements)
154326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
155804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return sum(coverageDependenceValues) / nbcoverageDependence
156326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
157804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverageDependanceElements(self):
158804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.children
159326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
160804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _coverageFormating(self, coverage):
161804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # If no coverage provided
162804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not coverage :
163804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ""
164326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
165804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Calculate coverage
166804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return " (%s coverage)" % self._number2percent(coverage)
167326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
168804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    @staticmethod
169804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _number2percent(number):
170804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Format a number to a integer % string
171326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
172804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        example: _number2percent(0.6666) -> "67%"
173804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """
174804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "{0:.0f}%".format(100 * number)
175326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
176326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
177804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpDescription(self, withCoverage, withNbUse):
178326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
179804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("yelding description")
180804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
181326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
182804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumped in self._dumpPropagate(withCoverage, withNbUse) :
183804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumped
184326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
185804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpPropagate(self, withCoverage, withNbUse):
186326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
187804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
188804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for dumpedDescription in child._dumpDescription(withCoverage, withNbUse) :
189804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                yield dumpedDescription.increasedRank()
190326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
191326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
192804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def dump(self, withCoverage=False, withNbUse=True):
193326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
194804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "\n".join(
195804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                str(dumpedDescription) for dumpedDescription in
196804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self._dumpDescription(withCoverage, withNbUse))
197326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
198804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def exportToXML(self, domElement=None):
199804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if domElement == None:
200804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            domElement = xml.dom.minidom.Element(self.tag)
20102726ec37a9686572f825520a04700061ffe5d06Kevin Rocard
202804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._XMLaddAttributes(domElement)
203326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
204804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
205804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            domElement.appendChild(child.exportToXML())
206326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
207804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return domElement
208326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
209804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _XMLaddAttributes(self, domElement):
210804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes = self._getXMLAttributes()
211326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
212804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverage = self._getCoverage()
213804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if coverage != None :
214804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            attributes["Coverage"] = self._number2percent(coverage)
215326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
216804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for key, value in attributes.items():
217804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            domElement.setAttribute(key, value)
218326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
219804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getXMLAttributes(self):
220804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return {
221804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Name": self.name,
222804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "NbUse": str(self.nbUse)
223804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                }
224326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
225804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _incNbUse(self):
226804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.nbUse += 1
227326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
228804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def childUsed(self, child):
229804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
230804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to parent
231804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
232326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
233804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _tellParentThatChildUsed(self):
234804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.parent :
235804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.parent.childUsed(self)
236326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
237326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
238804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parentUsed(self):
239804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
240804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to children
241804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
242804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            child.parentUsed()
243326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
244804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def hasBeenUsed(self):
245804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.nbUse > 0
246326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
247804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def operationOnChild(self, path, operation):
248326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
249804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if path:
250804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return self._operationPropagate(path, operation)
251804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
252804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("operating on self")
253804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return operation(self)
254326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
255804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _operationPropagate(self, path, operation):
256326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
257804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        childName = path.pop(0)
258804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        child = self.getChildFromName(childName)
259326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
260804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return child.operationOnChild(path, operation)
261326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
262326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
263326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
264804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def debug(self, stringOrFunction, level=logging.DEBUG):
265804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Print a debug line on stderr in tree form
266326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
267804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If the debug line is expensive to generate, provide callable
268804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        object, it will be called if log is enable for this level.
269804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        This callable object should return the logline string.
270804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """
271804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if logger.isEnabledFor(level):
272326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
273804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # TODO: use buildin callable if python >= 3.2
274804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if hasattr(stringOrFunction, "__call__"):
275804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                string = stringOrFunction()
276804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
277804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                string = stringOrFunction
278326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
279804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            rankedLine = DebugRankedLine("%s: %s" % (self, string))
280804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self._logDebug(rankedLine, level)
281326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
282804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _logDebug(self, rankedLine, level):
283326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
284804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.parent:
285804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.parent._logDebug(rankedLine.increasedRank(), level)
286804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
287804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.log(level, str(rankedLine))
288326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
289326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
290326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
291326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
292326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass FromDomElement(Element):
293804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, DomElement):
294804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._initFromDom(DomElement)
295804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(self.name)
296326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
297326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
298804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
299804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = DomElement.getAttribute("Name")
300326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
301326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
302326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
303326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DomElementLocation():
304804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, classConstructor, path=None):
305804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.classConstructor = classConstructor
306804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if path :
307804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.path = path
308804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
309804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.path = []
310326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
311804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.path.append(classConstructor.tag)
312326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
313326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
314326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DomPopulatedElement(Element):
315804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Default child populate
316326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
317804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    Look for each dom element with tag specified in self.tag
318804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    and instantiate it with the dom element
319804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """
320804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
321326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
322804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
323326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
324804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for childDomElementLocation in self.childClasses :
325326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
326804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Looking for child %s in path %s" % (
327804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childDomElementLocation.path[-1], childDomElementLocation.path))
328326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
329804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path) :
330326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
331804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childElement = childDomElementLocation.classConstructor(childDomElement)
332804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.addChild(childElement)
333326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
334804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childElement.populate(childDomElement)
335326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
336804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _findChildFromTagPath(self, dom, path):
337804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not path :
338804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dom
339804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
340804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Copy list
341804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            path = list(path)
342326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
343804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            tag = path.pop(0)
344326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
345804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Find element with tag
346804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Going to find elements with tag %s in %s" % (tag, dom))
347804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
348326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
349804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for elementByTag in dom.getElementsByTagName(tag) :
350326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
351804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.debug("Found element: %s" % elementByTag)
352326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
353804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # If the same tag is found
354804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if elementByTag in dom.childNodes :
355326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
356804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    # Yield next level
357804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    for element in self._findChildFromTagPath(elementByTag, path) :
358804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        yield element
359326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
360326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
361326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Rule(Element):
362326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
363804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def usedIfApplicable(self, criteria):
364804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        childApplicability = (child.usedIfApplicable(criteria)
365804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                for child in self.children)
366326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
367804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        isApplicable = self._isApplicable(criteria, childApplicability)
368326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
369804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if isApplicable :
370804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self._incNbUse()
371326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
372804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Rule applicability: %s" % isApplicable)
373804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(isApplicable == True or isApplicable == False)
374326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
375804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return isApplicable
376326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
377326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
378804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
379804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Return the rule applicability depending on children applicability.
380cf031996ede8428065b6d7648e34720a1874f5faKevin Rocard
381804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If at least one child is applicable, return true"""
382804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Lazy evaluation as in the PFW
383804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return all(childApplicability)
384326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
385326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
386326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriterionRule(FromDomElement, DomPopulatedElement, Rule):
387804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "SelectionCriterionRule"
388804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
389804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    isApplicableOperations = {
390804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Includes" : lambda criterion, value:     criterion.stateIncludes(value),
391804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
392804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Is"       : lambda criterion, value:     criterion.stateIs(value),
393804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "IsNot"    : lambda criterion, value: not criterion.stateIs(value)
394804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            }
395326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
396804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
397804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
398804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.matchesWhen = DomElement.getAttribute("MatchesWhen")
399804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.value = DomElement.getAttribute("Value")
400804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
401326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
402804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
403804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion, self.value)
404326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
405804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
406326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
407804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return criteria.operationOnChild([self.selectionCriterion],
408804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.isApplicableOperation)
409326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
410326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
411326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CompoundRule(FromDomElement, DomPopulatedElement, Rule):
412804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """CompoundRule can be of type ALL or ANY"""
413804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CompoundRule"
414804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Declare childClasses but define it at first class instantiation
415804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = None
416326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
417804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, dom):
418804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Define childClasses at first class instantiation
419804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.childClasses == None :
420804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.childClasses = [DomElementLocation(CriterionRule),
421804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    DomElementLocation(CompoundRule)]
422804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(dom)
423326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
424804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
425326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
426804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        type = DomElement.getAttribute("Type")
427804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.ofTypeAll = {"All" : True, "Any" : False}[type]
428804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = type
429326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
430804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
431804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.ofTypeAll :
432804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            applicability = super()._isApplicable(criteria, childApplicability)
433804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else:
434804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Lazy evaluation as in the PFW
435804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            applicability = any(childApplicability)
436326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
437804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return applicability
438326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
439326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass RootRule(DomPopulatedElement, Rule):
440804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "RootRule"
441804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(CompoundRule)]
442326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
443804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
444804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().populate(dom)
445804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Children: %s" % self.children)
446804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # A configuration can only have one or no rule
447804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(len(self.children) <= 1)
448326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
449804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverageDependanceElements(self):
450804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self._getDescendants()
451326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
452326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
453326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriteronStates(Element):
454804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Root of configuration application criterion state"""
455804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CriterionStates"
456326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
457804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parentUsed(self, criteria):
458804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Add criteria to child if not exist, if exist increase it's nbUse"""
459804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
460326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
461804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matches = [child for child in self.children if child == criteria]
462326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
463804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(len(matches) <= 1)
464326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
465804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if matches :
466804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Criteria state has already been encounter")
467804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            currentcriteria = matches[0]
468804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
469804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Criteria state has never been encounter, saving it")
470804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            currentcriteria = criteria
471804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.addChild(criteria)
472326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
473804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        currentcriteria.parentUsed()
474326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
475326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
476326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
477326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Configuration(FromDomElement, DomPopulatedElement):
478804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Configuration"
479804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
480326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
481804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class IneligibleConfigurationAppliedError(CustomError):
48297dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
483804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __init__(self, configuration, criteria):
484804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.configuration = configuration
485804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria = criteria
48697dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
487804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __str__(self):
48897dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
489804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ("Applying ineligible %s, "
490804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "rule:\n%s\n"
491804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Criteria current state:\n%s" % (
492804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.configuration,
493804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.configuration.rootRule.dump(withCoverage=False, withNbUse=False),
494804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.criteria.dump(withCoverage=False, withNbUse=False)
495804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    ))
49697dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
497804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, DomElement):
498804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(DomElement)
499326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
500804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rootRule = RootRule("RootRule")
501804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.rootRule)
502326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
503804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteronStates = CriteronStates("CriterionStates")
504804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.criteronStates)
505326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
506804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
507804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Delegate to rootRule
508804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rootRule.populate(dom)
509326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
510804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverage(self):
511804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Delegate to rootRule
512804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.rootRule._getCoverage()
513326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
514804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def used(self, criteria):
515326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
516804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
517326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
518804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate use to parents
519804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
520326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
521804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to criterion coverage
522804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteronStates.parentUsed(criteria.export())
523326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
524804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to rules
525804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not self.rootRule.usedIfApplicable(criteria) :
526326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
527804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Applied but rule does not match current "
5283aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard                       "criteria (parent: %s) " % self.parent.name,
529804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logging.ERROR)
530326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
531804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.IneligibleConfigurationAppliedError(self, criteria.export())
532326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
533804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpPropagate(self, withCoverage, withNbUse):
534804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Going to ask %s for description" % self.rootRule)
535804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumpedDescription in self.rootRule._dumpDescription(
536804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withCoverage=withCoverage,
537804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withNbUse=withNbUse) :
538804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumpedDescription.increasedRank()
539326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
540804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Going to ask %s for description" % self.criteronStates)
541804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumpedDescription in self.criteronStates._dumpDescription(
542804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withCoverage=False,
543804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withNbUse=withNbUse) :
544804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumpedDescription.increasedRank()
545326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
546326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
547326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Domain(FromDomElement, DomPopulatedElement):
548804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "ConfigurableDomain"
549804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(Configuration, ["Configurations"])]
550326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
551326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
552326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Domains(DomPopulatedElement):
553804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Domains"
554804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
555326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
556326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
557326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass RankedLine():
558804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, string,
559804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stringPrefix="|-- ",
560804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                rankString="|   ",
561804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                linePrefix="",
562804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lineSuffix="\n"):
563804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.string = string
564804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rank = 0
565804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.stringPrefix = stringPrefix
566804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rankString = rankString
567804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.linePrefix = linePrefix
568804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.lineSuffix = lineSuffix
569804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
570804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def increasedRank(self):
571804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rank += 1
572804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self
573804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
574804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
575804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.linePrefix + \
576804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.rank * self.rankString + \
577804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.stringPrefix + \
578804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.string + \
579804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.lineSuffix
580326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
581326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DebugRankedLine(RankedLine):
582326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
583804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, string, lineSuffix=""):
584804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(string,
585804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stringPrefix="",
586804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                rankString="   ",
587804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                linePrefix="",
588804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lineSuffix=lineSuffix)
589326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
590326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
591326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriterionState(Element):
592804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CriterionState"
593804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def used(self):
594804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
595326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
596326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
597326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Criterion(Element):
598804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Criterion"
599804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    inclusivenessTranslate = {True: "Inclusive", False: "Exclusive"}
6003aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
601804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class ChangeRequestToNonAccessibleState(CustomError):
602804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __init__(self, requestedState, detail):
603804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.requestedState = requestedState
604804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.detail = detail
605556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
606804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __str__(self):
607804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ("Change request to non accessible state %s. Detail: %s" %
608804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                (self.requestedState, self.detail))
609556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
610804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name, isInclusif,
611804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stateNamesList, currentStateNamesList,
612804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ignoreIntegrity=False):
613804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(name)
614804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.isInclusif = isInclusif
615326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
616804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for state in stateNamesList :
617804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.addChild(CriterionState(state))
618326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
619804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = []
620804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.initStateNamesList = list(currentStateNamesList)
621804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.changeState(self.initStateNamesList, ignoreIntegrity)
622326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
623804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def reset(self):
624804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Set current state as provided at initialisation
625804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.changeState(self.initStateNamesList, ignoreIntegrity=True)
626326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
627804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def changeState(self, subStateNames, ignoreIntegrity=False):
628804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Changing state from: %s to: %s" % (
629804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    list(self._getElementNames(self.currentState)),
630804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    subStateNames))
631326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
632804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not ignoreIntegrity and not self.isIntegre(subStateNames):
633804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.ChangeRequestToNonAccessibleState(subStateNames,
634804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "An exclusive criterion must have a non empty state")
635326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
636804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        newCurrentState = []
637804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for subStateName in subStateNames :
638804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            subState = self.getChildFromName(subStateName)
639804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            subState.used()
640804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            newCurrentState.append(subState)
641326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
642804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = newCurrentState
643326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
644804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
645804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
646326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
647804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def isIntegre(self, subStateNames):
648804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.isInclusif or len(subStateNames) == 1
649556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
650804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def childUsed(self, child):
651804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = child
652804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().childUsed(child)
653556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
654804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def export(self):
655804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        subStateNames = self._getElementNames(self.currentState)
656804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return Criterion(self.name, self.isInclusif, subStateNames, subStateNames,
657804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            ignoreIntegrity=True)
658326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
659804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def stateIncludes(self, subStateName):
660804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        subStateCurrentNames = list(self._getElementNames(self.currentState))
661326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
662804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
663326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
664804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        isIncluded = subStateName in subStateCurrentNames
665804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("IsIncluded: %s" % isIncluded)
666326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
667804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return isIncluded
668326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
669326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
670804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def stateIs(self, subStateNames):
671804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if len(self.currentState) != 1 :
672804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return False
673804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
674804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return self.stateIncludes(subStateNames)
675326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
676804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getXMLAttributes(self):
677804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes = super()._getXMLAttributes()
678804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes["Type"] = self.inclusivenessTranslate[self.isInclusif]
679804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return attributes
6803aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
681326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
682326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Criteria(Element):
683804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Criteria"
684326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
685804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class DuplicatedCriterionError(DuplicatedChildError):
686804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        pass
687d077c557e360615a67d97ee249260a1ef7919f09Kevin Rocard
688804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def export(self):
689804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Exporting criteria")
690804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(self.children)
691326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
692804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        exported = Criteria(self.name)
693804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
694804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            exported.addChild(child.export())
695804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return exported
696326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
697804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def addChild(self, child):
698804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if child in self.children:
699804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.DuplicatedCriterionError(self, child)
700804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().addChild(child)
7013aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
7023aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ConfigAppliedWithoutCriteriaError(CustomError):
703804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, configurationName, domainName):
704804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.configurationName = configurationName
705804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domainName = domainName
706804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
707804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return ('Applying configuration "%s" from domain "%s" before declaring criteria' %
708804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                (self.configurationName, self.domainName))
709326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
710326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass ParsePFWlog():
711804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    MATCH = "match"
712804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    ACTION = "action"
713804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
714804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, domains, criteria, ErrorsToIgnore=()):
715804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
716804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains = domains;
717804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteria = criteria;
718804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.ErrorsToIgnore = ErrorsToIgnore
719326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
720804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
721804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchConfigApplicationLine = re.compile(configApplicationRegext).match
722804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
723804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionCreationRegext = ", ".join([
724804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r""".*Criterion name: (.*)""",
725804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""type kind: (.*)""",
726804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""current state: (.*)""",
727804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""states: {(.*)}"""
728804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ])
729804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchCriterionCreationLine = re.compile(criterionCreationRegext).match
730326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
731804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        changingCriterionRegext = r""".*Selection criterion changed event: Criterion name: (.*), current state: ([^\n\r]*)"""
732804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchChangingCriterionLine = re.compile(changingCriterionRegext).match
733326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
734804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.lineLogTypes = [
735804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    {
736804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchConfigApplicationLine,
737804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._configApplication
738804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }, {
739804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchCriterionCreationLine,
740804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._criterionCreation
741804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }, {
742804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchChangingCriterionLine,
743804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._changingCriterion
744804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }
745804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ]
746326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
747804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    @staticmethod
748804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _formatCriterionList(liststring, separator):
749804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        list = liststring.split(separator)
750804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if len(list) == 1 and list[0] == "<none>":
751804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            list = []
752804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return list
753804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
754804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _criterionCreation(self, matchCriterionCreation):
755804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
756804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
757326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
758804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionStateList = self._formatCriterionList(criterionStates, ", ")
759326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
760804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
761326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
762804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
763326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
764804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Creating criterion: " + criterionName +
765804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    " (" + criterionType + ") " +
766804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    " with current state: " + str(currentcriterionStateList) +
767804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    ", possible states:" + str(criterionStateList))
768326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
769804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        try:
770804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria.addChild(Criterion(
771804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionName,
772804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionIsInclusif,
773804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionStateList,
774804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    currentcriterionStateList
775804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ))
776804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        except self.criteria.DuplicatedCriterionError as ex:
777804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.debug(ex)
778804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning("Reseting criterion %s. Did you reset the PFW ?" % criterionName)
779804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria.operationOnChild(
780804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                [criterionName],
781804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lambda criterion: criterion.reset()
782804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            )
783ea87422e14735633cb8e28de114a18e22eaa82fbKevin Rocard
784ea87422e14735633cb8e28de114a18e22eaa82fbKevin Rocard
785326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
786804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _changingCriterion(self, matchChangingCriterion):
787804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
788804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
789326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
790804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
791326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
792804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Changing criterion %s to %s" % (criterionName , newCriterionState))
7933aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
794804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        path = [criterionName]
795804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        changeCriterionOperation = lambda criterion : criterion.changeState(newCriterionState)
796804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteria.operationOnChild(path, changeCriterionOperation)
797326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
798804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _configApplication(self, matchConfig):
799804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
800804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        configurationName, domainName = matchConfig.group(1, 2)
8013aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
802804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Check that at least one criterion exist
803804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not self.criteria.hasChildren() :
804804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.error("Applying configuration before declaring criteria")
805804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.info("Is the log starting at PFW boot ?")
806804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
807326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
808804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Change criterion state
809804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        path = [domainName, configurationName]
810804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        usedOperation = lambda element : element.used(self.criteria)
811326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
812804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Applying configuration %s from domain %s" % (
813804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                configurationName, domainName))
814804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
815804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains.operationOnChild(path, usedOperation)
816326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
817326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
818804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _digest(self, lineLogType, lineLog):
8199050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
820804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        match = lineLogType[self.MATCH](lineLog)
821804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if match :
822804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            lineLogType[self.ACTION](match)
823804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return True
824804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return False
825326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
8269050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
827804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parsePFWlog(self, lines):
828804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for lineNb, lineLog in enumerate(lines):
829326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
830804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.debug("Parsing line :%s" % lineLog.rstrip())
831326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
832804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            digested = (self._digest(lineLogType, lineLog)
833804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    for lineLogType in self.lineLogTypes)
834326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
835804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            try:
836804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                success = any(digested)
8379050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
838804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Catch some exception in order to print the current parsing line,
839804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # then raise the exception again if not continue of error
840804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            except CustomError as ex:
841804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.error('Error raised while parsing line %s: "%s"' %
842804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                            (lineNb, repr(lineLog)))
8439050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
844804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # If exception is a subclass of ErrorsToIgnore, log it and continue
845804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # otherwise raise it again.
846804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if not issubclass(type(ex), self.ErrorsToIgnore):
847804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    raise ex
848804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                else:
849804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logger.error('Ignoring exception:"%s", '
850804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                                'can not guarantee database integrity' % ex)
851804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
852804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if not success:
853804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logger.debug("Line does not match, dropped")
854326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
855326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
856326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Root(Element):
857804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CoverageReport"
858804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name, dom):
859804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(name)
860804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Create domain tree
861804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains = Domains("Domains")
862804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains.populate(dom)
863804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.domains)
864804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Create criterion list
865804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteria = Criteria("CriterionRoot")
866804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.criteria)
867804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
868804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def exportToXML(self):
869804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Export tree to an xml document"""
870804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        impl = xml.dom.minidom.getDOMImplementation()
871804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        newdoc = impl.createDocument(namespaceURI=None, qualifiedName=self.tag, doctype=None)
872804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().exportToXML(newdoc.documentElement)
873804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
874804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return newdoc
875326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
876326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# ============================
877326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# Command line argument parser
878326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# ============================
879326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
880326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
881326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass ArgumentParser:
882804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """class that parse command line arguments with argparse library
883804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
884804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    Result of parsing are the class attributes.
885804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """
886804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    levelTranslate = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
887804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
888804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self):
889804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
890804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        try:
891804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # As argparse is only in the stdlib since python 3.2,
892804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # testing its availability
893804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            import argparse
894804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
895804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        except ImportError:
896804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning("Unable to import argparse "
897804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                           "(parser for command-line options and arguments), "
898804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                           "using default argument values:")
899804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
900804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - InputFile: stdin")
901804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.inputFile = sys.stdin
902804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
903804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - OutputFile: stdout")
904804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.outputFile = sys.stdout
905804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
906804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            try:
907804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.domainsFile = sys.argv[1]
908804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            except IndexError as ex:
909804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.fatal("No domain file provided (first argument)")
910804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                raise ex
911804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
912804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.warning(" - Domain file: " + self.domainsFile)
913804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
914804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - Output format: xml")
915804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.XMLreport = True
916804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
917804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - Debug level: error")
918804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debugLevel = logging.ERROR
919804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
920804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
921804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser = argparse.ArgumentParser(description='Generate PFW report')
922804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
923804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
924804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        'domainsFile',
925804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('r'),
926804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the PFW domain XML file"
927804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
928804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
929804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        'pfwlog', nargs='?',
930804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('r'), default=sys.stdin,
931804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the PFW log file, default stdin"
932804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
933804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
934804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '-o', '--output',
935804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="outputFile",
936804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('w'), default=sys.stdout,
937804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the coverage report output file, default stdout"
938804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
939804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
940804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '-v', '--verbose',
941804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="debugLevel", default=0,
942804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='count',
943804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="print debug warnings from warning (default) to debug (-vv)"
944804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
945804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
946804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
947804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
948804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe.add_argument(
949804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--xml',
950804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="xmlFlag",
951804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
952804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help=" XML coverage output report"
953804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
954804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe.add_argument(
955804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--raw',
956804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="rawFlag",
957804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
958804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="raw coverage output report"
959804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
960804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
961804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
962804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--ignore-incoherent-criterion-state',
963804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="incoherentCriterionFlag",
964804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
965804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="ignore criterion transition to incoherent state"
966804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
967804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
968804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
969804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--ignore-ineligible-configuration-application',
970804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="ineligibleConfigurationApplicationFlag",
971804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
972804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="ignore application of configuration with a false rule "
973804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        "(not applicable configuration)"
974804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
975804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
976804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Process command line arguments
977804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            options = myArgParser.parse_args()
978804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
979804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Mapping to attributes
980804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.inputFile = options.pfwlog
981804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.outputFile = options.outputFile
982804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.domainsFile = options.domainsFile
983804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
984804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Output report in xml if flag not set
985804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.XMLreport = not options.rawFlag
986804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
987804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Setting logger level
988804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
989804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debugLevel = self.levelTranslate[levelCapped]
990804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
991804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Setting ignore options
992804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            errorToIgnore = []
993804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if options.ineligibleConfigurationApplicationFlag :
994804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                errorToIgnore.append(Configuration.IneligibleConfigurationAppliedError)
995804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
996804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if options.incoherentCriterionFlag:
997804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                errorToIgnore.append(Criterion.ChangeRequestToNonAccessibleState)
998804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
999804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.errorToIgnore = tuple(errorToIgnore)
10009050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
1001326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1002326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1003326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocarddef main():
1004326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1005804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    errorDuringLogParsing = -1
1006804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    errorDuringArgumentParsing = 1
100753493b2f22bc2d491f7607672e1d07e588677efaKevin Rocard
1008804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    try:
1009804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        commandLineArguments = ArgumentParser()
1010804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    except LookupError as ex:
1011804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.error("Error during argument parsing")
1012804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.debug(str(ex))
1013804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        sys.exit(errorDuringArgumentParsing)
1014326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1015804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Setting logger level
1016804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    logger.setLevel(commandLineArguments.debugLevel)
1017804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    logger.info("Log level set to: %s" %
1018804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logging.getLevelName(commandLineArguments.debugLevel))
1019326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1020804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Create tree from XML
1021804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
1022326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1023804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Create element tree
1024804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    root = Root("DomainCoverage", dom)
1025326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1026804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Parse PFW events
1027804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    parser = ParsePFWlog(root.domains, root.criteria, commandLineArguments.errorToIgnore)
102853493b2f22bc2d491f7607672e1d07e588677efaKevin Rocard
1029804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    try:
1030804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        parser.parsePFWlog(commandLineArguments.inputFile.readlines())
1031804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    except CustomError as ex:
1032804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.fatal("Error during parsing log file %s: %s" %
1033804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            (commandLineArguments.inputFile, ex))
1034804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        sys.exit(errorDuringLogParsing)
1035326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1036804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Output report
1037804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    outputFile = commandLineArguments.outputFile
1038326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1039804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    if not commandLineArguments.XMLreport :
1040804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
1041804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    else :
1042804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        outputFile.write(root.exportToXML().toprettyxml())
1043326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1044326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1045326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardif __name__ == "__main__" :
1046804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """ Execute main if the python interpreter is running this module as the main program """
1047804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    main()
1048326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1049