summaryrefslogtreecommitdiffstats
path: root/tools/coverage
diff options
context:
space:
mode:
authorKevin Rocard <kevinx.rocard@intel.com>2013-06-10 19:05:46 +0200
committerDavid Wagner <david.wagner@intel.com>2014-02-12 17:04:02 +0100
commit1dff2e64afb47af468cc211efa229627367789b7 (patch)
treeae67109745402189b765d1ca70fac1ea4bec575c /tools/coverage
parent02726ec37a9686572f825520a04700061ffe5d06 (diff)
downloadexternal_parameter-framework-1dff2e64afb47af468cc211efa229627367789b7.zip
external_parameter-framework-1dff2e64afb47af468cc211efa229627367789b7.tar.gz
external_parameter-framework-1dff2e64afb47af468cc211efa229627367789b7.tar.bz2
[coverage] Move coverage generator in it's own folder
BZ: 115218 The coverage report generated script was in the tools folder. Other files will be used for coverage reports. Create a coverage folder in order to contain all coverage report utilities. Change-Id: I23ed75fb109cd4a5f6804398ef17d99e046fd49d Signed-off-by: Kevin Rocard <kevinx.rocard@intel.com> Reviewed-on: http://android.intel.com:8080/114667 Reviewed-by: Denneulin, Guillaume <guillaume.denneulin@intel.com> Reviewed-by: Gonzalve, Sebastien <sebastien.gonzalve@intel.com> Reviewed-by: cactus <cactus@intel.com> Tested-by: Dixon, CharlesX <charlesx.dixon@intel.com> Reviewed-by: buildbot <buildbot@intel.com> Tested-by: buildbot <buildbot@intel.com>
Diffstat (limited to 'tools/coverage')
-rwxr-xr-xtools/coverage/coverage.py1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/tools/coverage/coverage.py b/tools/coverage/coverage.py
new file mode 100755
index 0000000..df79b5b
--- /dev/null
+++ b/tools/coverage/coverage.py
@@ -0,0 +1,1048 @@
+#!/usr/bin/env python3
+
+# INTEL CONFIDENTIAL
+# Copyright 2013 Intel
+# Corporation All Rights Reserved.
+#
+# The source code contained or described herein and all documents related to
+# the source code ("Material") are owned by Intel Corporation or its suppliers
+# or licensors. Title to the Material remains with Intel Corporation or its
+# suppliers and licensors. The Material contains trade secrets and proprietary
+# and confidential information of Intel or its suppliers and licensors. The
+# Material is protected by worldwide copyright and trade secret laws and
+# treaty provisions. No part of the Material may be used, copied, reproduced,
+# modified, published, uploaded, posted, transmitted, distributed, or
+# disclosed in any way without Intels prior express written permission.
+#
+# No license under any patent, copyright, trade secret or other intellectual
+# property right is granted to or conferred upon you by disclosure or delivery
+# of the Materials, either expressly, by implication, inducement, estoppel or
+# otherwise. Any license under such intellectual property rights must be
+# express and approved by Intel in writing.
+
+"""
+Generate a coverage report by parsing parameter framework log.
+
+The coverage report contains the:
+ - domain
+ - configuration
+ - rule
+ - criterion
+basic coverage statistics.
+"""
+
+import xml.dom.minidom
+import sys
+import re
+import logging
+
+FORMAT = '%(levelname)s: %(message)s'
+logging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
+logger = logging.getLogger("Coverage")
+
+class CustomError(Exception):
+ pass
+
+class ChildError(CustomError):
+ def __init__(self, parent, child):
+ self.parent = parent
+ self.child = child
+
+class ChildNotFoundError(ChildError):
+ def __str__(self):
+ return "Unable to find the child %s in %s" % (self.child, self.parent)
+
+class DuplicatedChildError(ChildError):
+ def __str__(self):
+ return "Add existing child %s in %s." % (self.child, self.parent)
+
+class Element():
+ """Root class for all coverage elements"""
+ tag = "element"
+
+ def __init__(self, name):
+
+ self.parent = None
+ self.children = []
+
+ self.nbUse = 0
+
+ self.name = name
+
+ self.debug("New element")
+
+
+ def __str__(self):
+ return "%s (%s)" % (self.name, self.tag)
+
+ def __eq__(self, compared):
+ return (self.name == compared.name) and (self.children == compared.children)
+
+ def getName(self, default=""):
+ return self.name or default
+
+ def hasChildren(self):
+ return bool(self.children)
+
+ def getChildren(self):
+ return self.children
+
+ def _getDescendants(self):
+ for child in self.children:
+ yield child
+ for descendant in child._getDescendants() :
+ yield descendant
+
+ def getChildFromName(self, childName):
+
+ for child in self.children :
+
+ if child.getName() == childName :
+ return child
+
+ self.debug("Child %s not found" % childName, logging.ERROR)
+
+ self.debug("Child list :")
+
+ for child in self.children :
+ self.debug(" - %s" % child)
+
+ raise ChildNotFoundError(self, childName)
+
+
+ def addChild(self, child):
+ self.debug("new child: " + child.name)
+ self.children.append(child)
+ child._adoptedBy(self)
+
+ def _adoptedBy(self, parent):
+ assert(not self.parent)
+ self.parent = parent
+
+ def _getElementNames(self, elementList):
+ return (substate.name for substate in elementList)
+
+ def _description(self, withCoverage, withNbUse):
+ description = self.name
+
+ if withNbUse or withCoverage :
+ description += " has been used " + str(self.nbUse) + " time"
+
+ if withCoverage :
+ description += self._coverageFormating(self._getCoverage())
+
+ return description
+
+
+ def _getCoverage(self):
+ """Return the coverage of the element between 0 and 1
+
+ If the element has no coverage dependency (usually child) return 0 or 1.
+ otherwise the element coverage is the dependency coverage average"""
+ coverageDependanceElements = list(self._getCoverageDependanceElements())
+
+ nbcoverageDependence = len(coverageDependanceElements)
+
+ if nbcoverageDependence == 0:
+ if self.nbUse == 0:
+ return 0
+ else:
+ return 1
+
+ coverageDependenceValues = (depElement._getCoverage()
+ for depElement in coverageDependanceElements)
+
+ return sum(coverageDependenceValues) / nbcoverageDependence
+
+ def _getCoverageDependanceElements(self):
+ return self.children
+
+ def _coverageFormating(self, coverage):
+ # If no coverage provided
+ if not coverage :
+ return ""
+
+ # Calculate coverage
+ return " (%s coverage)" % self._number2percent(coverage)
+
+ @staticmethod
+ def _number2percent(number):
+ """Format a number to a integer % string
+
+ example: _number2percent(0.6666) -> "67%"
+ """
+ return "{0:.0f}%".format(100 * number)
+
+
+ def _dumpDescription(self, withCoverage, withNbUse):
+
+ self.debug("yelding description")
+ yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
+
+ for dumped in self._dumpPropagate(withCoverage, withNbUse) :
+ yield dumped
+
+ def _dumpPropagate(self, withCoverage, withNbUse):
+
+ for child in self.children :
+ for dumpedDescription in child._dumpDescription(withCoverage, withNbUse) :
+ yield dumpedDescription.increasedRank()
+
+
+ def dump(self, withCoverage=False, withNbUse=True):
+
+ return "\n".join(
+ str(dumpedDescription) for dumpedDescription in
+ self._dumpDescription(withCoverage, withNbUse))
+
+ def exportToXML(self, domElement=None):
+ if domElement == None:
+ domElement = xml.dom.minidom.Element(self.tag)
+
+ self._XMLaddAttributes(domElement)
+
+ for child in self.children :
+ domElement.appendChild(child.exportToXML())
+
+ return domElement
+
+ def _XMLaddAttributes(self, domElement):
+ attributes = self._getXMLAttributes()
+
+ coverage = self._getCoverage()
+ if coverage != None :
+ attributes["Coverage"] = self._number2percent(coverage)
+
+ for key, value in attributes.items():
+ domElement.setAttribute(key, value)
+
+ def _getXMLAttributes(self):
+ return {
+ "Name": self.name,
+ "NbUse": str(self.nbUse)
+ }
+
+ def _incNbUse(self):
+ self.nbUse += 1
+
+ def childUsed(self, child):
+ self._incNbUse()
+ # Propagate to parent
+ self._tellParentThatChildUsed()
+
+ def _tellParentThatChildUsed(self):
+ if self.parent :
+ self.parent.childUsed(self)
+
+
+ def parentUsed(self):
+ self._incNbUse()
+ # Propagate to children
+ for child in self.children :
+ child.parentUsed()
+
+ def hasBeenUsed(self):
+ return self.nbUse > 0
+
+ def operationOnChild(self, path, operation):
+
+ if path:
+ return self._operationPropagate(path, operation)
+ else :
+ self.debug("operating on self")
+ return operation(self)
+
+ def _operationPropagate(self, path, operation):
+
+ childName = path.pop(0)
+ child = self.getChildFromName(childName)
+
+ return child.operationOnChild(path, operation)
+
+
+
+ def debug(self, stringOrFunction, level=logging.DEBUG):
+ """Print a debug line on stderr in tree form
+
+ If the debug line is expensive to generate, provide callable
+ object, it will be called if log is enable for this level.
+ This callable object should return the logline string.
+ """
+ if logger.isEnabledFor(level):
+
+ # TODO: use buildin callable if python >= 3.2
+ if hasattr(stringOrFunction, "__call__"):
+ string = stringOrFunction()
+ else:
+ string = stringOrFunction
+
+ rankedLine = DebugRankedLine("%s: %s" % (self, string))
+ self._logDebug(rankedLine, level)
+
+ def _logDebug(self, rankedLine, level):
+
+ if self.parent:
+ self.parent._logDebug(rankedLine.increasedRank(), level)
+ else :
+ logger.log(level, str(rankedLine))
+
+
+
+
+class FromDomElement(Element):
+ def __init__(self, DomElement):
+ self._initFromDom(DomElement)
+ super().__init__(self.name)
+
+
+ def _initFromDom(self, DomElement):
+ self.name = DomElement.getAttribute("Name")
+
+
+
+class DomElementLocation():
+ def __init__(self, classConstructor, path=None):
+ self.classConstructor = classConstructor
+ if path :
+ self.path = path
+ else :
+ self.path = []
+
+ self.path.append(classConstructor.tag)
+
+
+class DomPopulatedElement(Element):
+ """Default child populate
+
+ Look for each dom element with tag specified in self.tag
+ and instantiate it with the dom element
+ """
+ childClasses = []
+
+ def populate(self, dom):
+
+ for childDomElementLocation in self.childClasses :
+
+ self.debug("Looking for child %s in path %s" % (
+ childDomElementLocation.path[-1], childDomElementLocation.path))
+
+ for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path) :
+
+ childElement = childDomElementLocation.classConstructor(childDomElement)
+ self.addChild(childElement)
+
+ childElement.populate(childDomElement)
+
+ def _findChildFromTagPath(self, dom, path):
+ if not path :
+ yield dom
+ else :
+ # Copy list
+ path = list(path)
+
+ tag = path.pop(0)
+
+ # Find element with tag
+ self.debug("Going to find elements with tag %s in %s" % (tag, dom))
+ self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
+
+ for elementByTag in dom.getElementsByTagName(tag) :
+
+ self.debug("Found element: %s" % elementByTag)
+
+ # If the same tag is found
+ if elementByTag in dom.childNodes :
+
+ # Yield next level
+ for element in self._findChildFromTagPath(elementByTag, path) :
+ yield element
+
+
+class Rule(Element):
+
+ def usedIfApplicable(self, criteria):
+ childApplicability = (child.usedIfApplicable(criteria)
+ for child in self.children)
+
+ isApplicable = self._isApplicable(criteria, childApplicability)
+
+ if isApplicable :
+ self._incNbUse()
+
+ self.debug("Rule applicability: %s" % isApplicable)
+ assert(isApplicable == True or isApplicable == False)
+
+ return isApplicable
+
+
+ def _isApplicable(self, criteria, childApplicability):
+ """Return the rule applicability depending on children applicability.
+
+ If at least one child is applicable, return true"""
+ # Lazy evaluation as in the PFW
+ return all(childApplicability)
+
+
+class CriterionRule(FromDomElement, DomPopulatedElement, Rule):
+ tag = "SelectionCriterionRule"
+ childClasses = []
+ isApplicableOperations = {
+ "Includes" : lambda criterion, value: criterion.stateIncludes(value),
+ "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
+ "Is" : lambda criterion, value: criterion.stateIs(value),
+ "IsNot" : lambda criterion, value: not criterion.stateIs(value)
+ }
+
+ def _initFromDom(self, DomElement):
+ self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
+ self.matchesWhen = DomElement.getAttribute("MatchesWhen")
+ self.value = DomElement.getAttribute("Value")
+ self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
+
+ applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
+ self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion, self.value)
+
+ def _isApplicable(self, criteria, childApplicability):
+
+ return criteria.operationOnChild([self.selectionCriterion],
+ self.isApplicableOperation)
+
+
+class CompoundRule(FromDomElement, DomPopulatedElement, Rule):
+ """CompoundRule can be of type ALL or ANY"""
+ tag = "CompoundRule"
+ # Declare childClasses but define it at first class instantiation
+ childClasses = None
+
+ def __init__(self, dom):
+ # Define childClasses at first class instantiation
+ if self.childClasses == None :
+ self.childClasses = [DomElementLocation(CriterionRule),
+ DomElementLocation(CompoundRule)]
+ super().__init__(dom)
+
+ def _initFromDom(self, DomElement):
+
+ type = DomElement.getAttribute("Type")
+ self.ofTypeAll = {"All" : True, "Any" : False}[type]
+ self.name = type
+
+ def _isApplicable(self, criteria, childApplicability):
+ if self.ofTypeAll :
+ applicability = super()._isApplicable(criteria, childApplicability)
+ else:
+ # Lazy evaluation as in the PFW
+ applicability = any(childApplicability)
+
+ return applicability
+
+class RootRule(DomPopulatedElement, Rule):
+ tag = "RootRule"
+ childClasses = [DomElementLocation(CompoundRule)]
+
+ def populate(self, dom):
+ super().populate(dom)
+ self.debug("Children: %s" % self.children)
+ # A configuration can only have one or no rule
+ assert(len(self.children) <= 1)
+
+ def _getCoverageDependanceElements(self):
+ return self._getDescendants()
+
+
+class CriteronStates(Element):
+ """Root of configuration application criterion state"""
+ tag = "CriterionStates"
+
+ def parentUsed(self, criteria):
+ """Add criteria to child if not exist, if exist increase it's nbUse"""
+ self._incNbUse()
+
+ matches = [child for child in self.children if child == criteria]
+
+ assert(len(matches) <= 1)
+
+ if matches :
+ self.debug("Criteria state has already been encounter")
+ currentcriteria = matches[0]
+ else :
+ self.debug("Criteria state has never been encounter, saving it")
+ currentcriteria = criteria
+ self.addChild(criteria)
+
+ currentcriteria.parentUsed()
+
+
+
+class Configuration(FromDomElement, DomPopulatedElement):
+ tag = "Configuration"
+ childClasses = []
+
+ class IneligibleConfigurationAppliedError(CustomError):
+
+ def __init__(self, configuration, criteria):
+ self.configuration = configuration
+ self.criteria = criteria
+
+ def __str__(self):
+
+ return ("Applying ineligible %s, "
+ "rule:\n%s\n"
+ "Criteria current state:\n%s" % (
+ self.configuration,
+ self.configuration.rootRule.dump(withCoverage=False, withNbUse=False),
+ self.criteria.dump(withCoverage=False, withNbUse=False)
+ ))
+
+ def __init__(self, DomElement):
+ super().__init__(DomElement)
+
+ self.rootRule = RootRule("RootRule")
+ self.addChild(self.rootRule)
+
+ self.criteronStates = CriteronStates("CriterionStates")
+ self.addChild(self.criteronStates)
+
+ def populate(self, dom):
+ # Delegate to rootRule
+ self.rootRule.populate(dom)
+
+ def _getCoverage(self):
+ # Delegate to rootRule
+ return self.rootRule._getCoverage()
+
+ def used(self, criteria):
+
+ self._incNbUse()
+
+ # Propagate use to parents
+ self._tellParentThatChildUsed()
+
+ # Propagate to criterion coverage
+ self.criteronStates.parentUsed(criteria.export())
+
+ # Propagate to rules
+ if not self.rootRule.usedIfApplicable(criteria) :
+
+ self.debug("Applied but rule does not match current "
+ "criteria (parent: %s) " % self.parent.name,
+ logging.ERROR)
+
+ raise self.IneligibleConfigurationAppliedError(self, criteria.export())
+
+ def _dumpPropagate(self, withCoverage, withNbUse):
+ self.debug("Going to ask %s for description" % self.rootRule)
+ for dumpedDescription in self.rootRule._dumpDescription(
+ withCoverage=withCoverage,
+ withNbUse=withNbUse) :
+ yield dumpedDescription.increasedRank()
+
+ self.debug("Going to ask %s for description" % self.criteronStates)
+ for dumpedDescription in self.criteronStates._dumpDescription(
+ withCoverage=False,
+ withNbUse=withNbUse) :
+ yield dumpedDescription.increasedRank()
+
+
+class Domain(FromDomElement, DomPopulatedElement):
+ tag = "ConfigurableDomain"
+ childClasses = [DomElementLocation(Configuration, ["Configurations"])]
+
+
+class Domains(DomPopulatedElement):
+ tag = "Domains"
+ childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
+
+
+class RankedLine():
+ def __init__(self, string,
+ stringPrefix="|-- ",
+ rankString="| ",
+ linePrefix="",
+ lineSuffix="\n"):
+ self.string = string
+ self.rank = 0
+ self.stringPrefix = stringPrefix
+ self.rankString = rankString
+ self.linePrefix = linePrefix
+ self.lineSuffix = lineSuffix
+
+ def increasedRank(self):
+ self.rank += 1
+ return self
+
+ def __str__(self):
+ return self.linePrefix + \
+ self.rank * self.rankString + \
+ self.stringPrefix + \
+ self.string + \
+ self.lineSuffix
+
+class DebugRankedLine(RankedLine):
+
+ def __init__(self, string, lineSuffix=""):
+ super().__init__(string,
+ stringPrefix="",
+ rankString=" ",
+ linePrefix="",
+ lineSuffix=lineSuffix)
+
+
+class CriterionState(Element):
+ tag = "CriterionState"
+ def used(self):
+ self._incNbUse()
+
+
+class Criterion(Element):
+ tag = "Criterion"
+ inclusivenessTranslate = {True: "Inclusive", False: "Exclusive"}
+
+ class ChangeRequestToNonAccessibleState(CustomError):
+ def __init__(self, requestedState, detail):
+ self.requestedState = requestedState
+ self.detail = detail
+
+ def __str__(self):
+ return ("Change request to non accessible state %s. Detail: %s" %
+ (self.requestedState, self.detail))
+
+ def __init__(self, name, isInclusif,
+ stateNamesList, currentStateNamesList,
+ ignoreIntegrity=False):
+ super().__init__(name)
+ self.isInclusif = isInclusif
+
+ for state in stateNamesList :
+ self.addChild(CriterionState(state))
+
+ self.currentState = []
+ self.initStateNamesList = list(currentStateNamesList)
+ self.changeState(self.initStateNamesList, ignoreIntegrity)
+
+ def reset(self):
+ # Set current state as provided at initialisation
+ self.changeState(self.initStateNamesList, ignoreIntegrity=True)
+
+ def changeState(self, subStateNames, ignoreIntegrity=False):
+ self.debug("Changing state from: %s to: %s" % (
+ list(self._getElementNames(self.currentState)),
+ subStateNames))
+
+ if not ignoreIntegrity and not self.isIntegre(subStateNames):
+ raise self.ChangeRequestToNonAccessibleState(subStateNames,
+ "An exclusive criterion must have a non empty state")
+
+ newCurrentState = []
+ for subStateName in subStateNames :
+ subState = self.getChildFromName(subStateName)
+ subState.used()
+ newCurrentState.append(subState)
+
+ self.currentState = newCurrentState
+
+ self._incNbUse()
+ self._tellParentThatChildUsed()
+
+ def isIntegre(self, subStateNames):
+ return self.isInclusif or len(subStateNames) == 1
+
+ def childUsed(self, child):
+ self.currentState = child
+ super().childUsed(child)
+
+ def export(self):
+ subStateNames = self._getElementNames(self.currentState)
+ return Criterion(self.name, self.isInclusif, subStateNames, subStateNames,
+ ignoreIntegrity=True)
+
+ def stateIncludes(self, subStateName):
+ subStateCurrentNames = list(self._getElementNames(self.currentState))
+
+ self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
+
+ isIncluded = subStateName in subStateCurrentNames
+ self.debug("IsIncluded: %s" % isIncluded)
+
+ return isIncluded
+
+
+ def stateIs(self, subStateNames):
+ if len(self.currentState) != 1 :
+ return False
+ else :
+ return self.stateIncludes(subStateNames)
+
+ def _getXMLAttributes(self):
+ attributes = super()._getXMLAttributes()
+ attributes["Type"] = self.inclusivenessTranslate[self.isInclusif]
+ return attributes
+
+
+class Criteria(Element):
+ tag = "Criteria"
+
+ class DuplicatedCriterionError(DuplicatedChildError):
+ pass
+
+ def export(self):
+ self.debug("Exporting criteria")
+ assert(self.children)
+
+ exported = Criteria(self.name)
+ for child in self.children :
+ exported.addChild(child.export())
+ return exported
+
+ def addChild(self, child):
+ if child in self.children:
+ raise self.DuplicatedCriterionError(self, child)
+ super().addChild(child)
+
+class ConfigAppliedWithoutCriteriaError(CustomError):
+ def __init__(self, configurationName, domainName):
+ self.configurationName = configurationName
+ self.domainName = domainName
+ def __str__(self):
+ return ('Applying configuration "%s" from domain "%s" before declaring criteria' %
+ (self.configurationName, self.domainName))
+
+class ParsePFWlog():
+ MATCH = "match"
+ ACTION = "action"
+
+ def __init__(self, domains, criteria, ErrorsToIgnore=()):
+
+ self.domains = domains;
+ self.criteria = criteria;
+ self.ErrorsToIgnore = ErrorsToIgnore
+
+ configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
+ matchConfigApplicationLine = re.compile(configApplicationRegext).match
+
+ criterionCreationRegext = ", ".join([
+ r""".*Criterion name: (.*)""",
+ r"""type kind: (.*)""",
+ r"""current state: (.*)""",
+ r"""states: {(.*)}"""
+ ])
+ matchCriterionCreationLine = re.compile(criterionCreationRegext).match
+
+ changingCriterionRegext = r""".*Selection criterion changed event: Criterion name: (.*), current state: ([^\n\r]*)"""
+ matchChangingCriterionLine = re.compile(changingCriterionRegext).match
+
+ self.lineLogTypes = [
+ {
+ self.MATCH: matchConfigApplicationLine,
+ self.ACTION: self._configApplication
+ }, {
+ self.MATCH: matchCriterionCreationLine,
+ self.ACTION: self._criterionCreation
+ }, {
+ self.MATCH: matchChangingCriterionLine,
+ self.ACTION: self._changingCriterion
+ }
+ ]
+
+ @staticmethod
+ def _formatCriterionList(liststring, separator):
+ list = liststring.split(separator)
+ if len(list) == 1 and list[0] == "<none>":
+ list = []
+ return list
+
+ def _criterionCreation(self, matchCriterionCreation):
+ # Unpack
+ criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
+
+ criterionStateList = self._formatCriterionList(criterionStates, ", ")
+
+ criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
+
+ currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
+
+ logger.info("Creating criterion: " + criterionName +
+ " (" + criterionType + ") " +
+ " with current state: " + str(currentcriterionStateList) +
+ ", possible states:" + str(criterionStateList))
+
+ try:
+ self.criteria.addChild(Criterion(
+ criterionName,
+ criterionIsInclusif,
+ criterionStateList,
+ currentcriterionStateList
+ ))
+ except self.criteria.DuplicatedCriterionError as ex:
+ logger.debug(ex)
+ logger.warning("Reseting criterion %s. Did you reset the PFW ?" % criterionName)
+ self.criteria.operationOnChild(
+ [criterionName],
+ lambda criterion: criterion.reset()
+ )
+
+
+
+ def _changingCriterion(self, matchChangingCriterion):
+ # Unpack
+ criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
+
+ newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
+
+ logger.info("Changing criterion %s to %s" % (criterionName , newCriterionState))
+
+ path = [criterionName]
+ changeCriterionOperation = lambda criterion : criterion.changeState(newCriterionState)
+ self.criteria.operationOnChild(path, changeCriterionOperation)
+
+ def _configApplication(self, matchConfig):
+ # Unpack
+ configurationName, domainName = matchConfig.group(1, 2)
+
+ # Check that at least one criterion exist
+ if not self.criteria.hasChildren() :
+ logger.error("Applying configuration before declaring criteria")
+ logger.info("Is the log starting at PFW boot ?")
+ raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
+
+ # Change criterion state
+ path = [domainName, configurationName]
+ usedOperation = lambda element : element.used(self.criteria)
+
+ logger.info("Applying configuration %s from domain %s" % (
+ configurationName, domainName))
+
+ self.domains.operationOnChild(path, usedOperation)
+
+
+ def _digest(self, lineLogType, lineLog):
+
+ match = lineLogType[self.MATCH](lineLog)
+ if match :
+ lineLogType[self.ACTION](match)
+ return True
+ return False
+
+
+ def parsePFWlog(self, lines):
+ for lineNb, lineLog in enumerate(lines):
+
+ logger.debug("Parsing line :%s" % lineLog.rstrip())
+
+ digested = (self._digest(lineLogType, lineLog)
+ for lineLogType in self.lineLogTypes)
+
+ try:
+ success = any(digested)
+
+ # Catch some exception in order to print the current parsing line,
+ # then raise the exception again if not continue of error
+ except CustomError as ex:
+ logger.error('Error raised while parsing line %s: "%s"' %
+ (lineNb, repr(lineLog)))
+
+ # If exception is a subclass of ErrorsToIgnore, log it and continue
+ # otherwise raise it again.
+ if not issubclass(type(ex), self.ErrorsToIgnore):
+ raise ex
+ else:
+ logger.error('Ignoring exception:"%s", '
+ 'can not guarantee database integrity' % ex)
+ else:
+ if not success:
+ logger.debug("Line does not match, dropped")
+
+
+class Root(Element):
+ tag = "CoverageReport"
+ def __init__(self, name, dom):
+ super().__init__(name)
+ # Create domain tree
+ self.domains = Domains("Domains")
+ self.domains.populate(dom)
+ self.addChild(self.domains)
+ # Create criterion list
+ self.criteria = Criteria("CriterionRoot")
+ self.addChild(self.criteria)
+
+ def exportToXML(self):
+ """Export tree to an xml document"""
+ impl = xml.dom.minidom.getDOMImplementation()
+ newdoc = impl.createDocument(namespaceURI=None, qualifiedName=self.tag, doctype=None)
+ super().exportToXML(newdoc.documentElement)
+
+ return newdoc
+
+# ============================
+# Command line argument parser
+# ============================
+
+
+class ArgumentParser:
+ """class that parse command line arguments with argparse library
+
+ Result of parsing are the class attributes.
+ """
+ levelTranslate = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
+
+ def __init__(self):
+
+ try:
+ # As argparse is only in the stdlib since python 3.2,
+ # testing its availability
+ import argparse
+
+ except ImportError:
+ logger.warning("Unable to import argparse "
+ "(parser for command-line options and arguments), "
+ "using default argument values:")
+
+ logger.warning(" - InputFile: stdin")
+ self.inputFile = sys.stdin
+
+ logger.warning(" - OutputFile: stdout")
+ self.outputFile = sys.stdout
+
+ try:
+ self.domainsFile = sys.argv[1]
+ except IndexError as ex:
+ logger.fatal("No domain file provided (first argument)")
+ raise ex
+ else:
+ logger.warning(" - Domain file: " + self.domainsFile)
+
+ logger.warning(" - Output format: xml")
+ self.XMLreport = True
+
+ logger.warning(" - Debug level: error")
+ self.debugLevel = logging.ERROR
+ else :
+
+ myArgParser = argparse.ArgumentParser(description='Generate PFW report')
+
+ myArgParser.add_argument(
+ 'domainsFile',
+ type=argparse.FileType('r'),
+ help="the PFW domain XML file"
+ )
+ myArgParser.add_argument(
+ 'pfwlog', nargs='?',
+ type=argparse.FileType('r'), default=sys.stdin,
+ help="the PFW log file, default stdin"
+ )
+ myArgParser.add_argument(
+ '-o', '--output',
+ dest="outputFile",
+ type=argparse.FileType('w'), default=sys.stdout,
+ help="the coverage report output file, default stdout"
+ )
+ myArgParser.add_argument(
+ '-v', '--verbose',
+ dest="debugLevel", default=0,
+ action='count',
+ help="print debug warnings from warning (default) to debug (-vv)"
+ )
+
+ outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
+
+ outputFormatGroupe.add_argument(
+ '--xml',
+ dest="xmlFlag",
+ action='store_true',
+ help=" XML coverage output report"
+ )
+ outputFormatGroupe.add_argument(
+ '--raw',
+ dest="rawFlag",
+ action='store_true',
+ help="raw coverage output report"
+ )
+
+ myArgParser.add_argument(
+ '--ignore-incoherent-criterion-state',
+ dest="incoherentCriterionFlag",
+ action='store_true',
+ help="ignore criterion transition to incoherent state"
+ )
+
+ myArgParser.add_argument(
+ '--ignore-ineligible-configuration-application',
+ dest="ineligibleConfigurationApplicationFlag",
+ action='store_true',
+ help="ignore application of configuration with a false rule "
+ "(not applicable configuration)"
+ )
+
+ # Process command line arguments
+ options = myArgParser.parse_args()
+
+ # Mapping to attributes
+ self.inputFile = options.pfwlog
+ self.outputFile = options.outputFile
+ self.domainsFile = options.domainsFile
+
+ # Output report in xml if flag not set
+ self.XMLreport = not options.rawFlag
+
+ # Setting logger level
+ levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
+ self.debugLevel = self.levelTranslate[levelCapped]
+
+ # Setting ignore options
+ errorToIgnore = []
+ if options.ineligibleConfigurationApplicationFlag :
+ errorToIgnore.append(Configuration.IneligibleConfigurationAppliedError)
+
+ if options.incoherentCriterionFlag:
+ errorToIgnore.append(Criterion.ChangeRequestToNonAccessibleState)
+
+ self.errorToIgnore = tuple(errorToIgnore)
+
+
+
+def main():
+
+ errorDuringLogParsing = -1
+ errorDuringArgumentParsing = 1
+
+ try:
+ commandLineArguments = ArgumentParser()
+ except LookupError as ex:
+ logger.error("Error during argument parsing")
+ logger.debug(str(ex))
+ sys.exit(errorDuringArgumentParsing)
+
+ # Setting logger level
+ logger.setLevel(commandLineArguments.debugLevel)
+ logger.info("Log level set to: %s" %
+ logging.getLevelName(commandLineArguments.debugLevel))
+
+ # Create tree from XML
+ dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
+
+ # Create element tree
+ root = Root("DomainCoverage", dom)
+
+ # Parse PFW events
+ parser = ParsePFWlog(root.domains, root.criteria, commandLineArguments.errorToIgnore)
+
+ try:
+ parser.parsePFWlog(commandLineArguments.inputFile.readlines())
+ except CustomError as ex:
+ logger.fatal("Error during parsing log file %s: %s" %
+ (commandLineArguments.inputFile, ex))
+ sys.exit(errorDuringLogParsing)
+
+ # Output report
+ outputFile = commandLineArguments.outputFile
+
+ if not commandLineArguments.XMLreport :
+ outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
+ else :
+ outputFile.write(root.exportToXML().toprettyxml())
+
+
+if __name__ == "__main__" :
+ """ Execute main if the python interpreter is running this module as the main program """
+ main()
+