blob: 9b5f8c45296d43e60702e16598cf70b67fb2729a [file] [log] [blame]
Kevin Rocard326e39e2012-12-14 16:11:05 +01001#!/usr/bin/env python3
2
3"""
4Generate a coverage report by parsing parameter framework log.
5
6The coverage report contains the:
7 - domain
8 - configuration
9 - rule
10 - criterion
11basic coverage statistics.
12"""
13
14import xml.dom.minidom
15import sys
16import re
17import logging
18
19FORMAT = '%(levelname)s: %(message)s'
20logging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
21logger = logging.getLogger("Coverage")
22
23class ChildNotFoundError(Exception):
24 def __init__(self, parent, child):
25 self.parent = parent
26 self.child = child
27 def __str__(self):
28 return ("Unable to find child %s in parent %s" %
29 (self.parent, self.child))
30
31class Element():
32 """Root class for all coverage elements"""
33 tag = "element"
34
35 def __init__(self, name):
36
37 self.parent = None
38 self.children = []
39
40 self.nbUse = 0
41
42 self.name = name
43
44 self.debug("New element")
45
46
47 def __str__(self):
48 return "%s (%s)" % (self.name, self.tag)
49
50 def __eq__(self, compared):
51 self.debug(lambda : "Comparing:\n%s" % self.dump())
52 self.debug(lambda : "With:\n%s" % compared.dump())
53 result = self.name == compared.name and self.children == compared.children
54 self.debug("Result is %s" % result)
55 return result
56
57
58 def getName(self, default=""):
59 return self.name or default
60
61 def hasChildren(self):
62 return bool(self.children)
63
64 def getChildren(self):
65 return self.children
66
67 def _getDescendants(self):
68 for child in self.children:
69 yield child
70 for descendant in child._getDescendants() :
71 yield descendant
72
73 def getChildFromName(self, childName):
74
75 for child in self.children :
76
77 if child.getName() == childName :
78 return child
79
80 self.debug("Child %s not found" % childName, logging.ERROR)
81
82 self.debug("Child list :")
83
84 for child in self.children :
85 self.debug(" - %s" % child)
86
87 raise ChildNotFoundError(self, child)
88
89
90 def addChild(self, child):
91 self.debug("new child: " + child.name)
92 self.children.append(child)
93 child._adoptedBy(self)
94
95 def _adoptedBy(self, parent):
96 assert(not self.parent)
97 self.parent = parent
98
99 def _getElementNames(self, elementList):
100 return (substate.name for substate in elementList)
101
102 def _description(self, withCoverage, withNbUse):
103 description = self.name
104
105 if withNbUse or withCoverage :
106 description += " has been used " + str(self.nbUse) + " time"
107
108 if withCoverage :
109 description += self._coverageFormating(self._getCoverage())
110
111 return description
112
113
114 def _getCoverage(self):
115
116 coverageDependance = list(self._getCoverageDependance())
117
118 nbcoverageDependence = len(coverageDependance)
119
120 if nbcoverageDependence == 0:
121 # Coverage makes no sense without any dependence
122 return None
123
124 nbcoverageDependanceUsed = len([element
125 for element in coverageDependance
126 if element.hasBeenUsed()])
127
128 return nbcoverageDependanceUsed / nbcoverageDependence
129
130 def _getCoverageDependance(self):
131 return self.children
132
133 def _coverageFormating(self, coverage):
134 # If no coverage provided
135 if not coverage :
136 return ""
137
138 # Calculate coverage
139 return " (%s coverage)" % self._number2percent(coverage)
140
141 @staticmethod
142 def _number2percent(number):
143 """Format a number to a integer % string
144
145 example: _number2percent(0.6666) -> "67%"
146 """
147 return "{0:.0f}%".format(100 * number)
148
149
150 def _dumpDescription(self, withCoverage, withNbUse):
151
152 self.debug("yelding description")
153 yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
154
155 for dumped in self._dumpPropagate(withCoverage, withNbUse) :
156 yield dumped
157
158 def _dumpPropagate(self, withCoverage, withNbUse):
159
160 for child in self.children :
161 for dumpedDescription in child._dumpDescription(withCoverage, withNbUse) :
162 yield dumpedDescription.increasedRank()
163
164
165 def dump(self, withCoverage=False, withNbUse=True):
166
167 return "\n".join(
168 str(dumpedDescription) for dumpedDescription in
169 self._dumpDescription(withCoverage, withNbUse))
170
171 def exportToXML(self):
172 domElement = xml.dom.minidom.Element(self.tag)
173 self._XMLaddAttributes(domElement)
174
175 for child in self.children :
176 domElement.appendChild(child.exportToXML())
177
178 return domElement
179
180 def _XMLaddAttributes(self, domElement):
181 attributes = {
182 "Name": self.name,
183 "NbUse": str(self.nbUse)
184 }
185
186 coverage = self._getCoverage()
187 if coverage :
188 attributes["coverage"] = self._number2percent(coverage)
189
190 for key, value in attributes.items():
191 domElement.setAttribute(key, value)
192
193
194 def _incNbUse(self):
195 self.nbUse += 1
196
197 def childUsed(self, child):
198 self._incNbUse()
199 # Propagate to parent
200 self._tellParentThatChildUsed()
201
202 def _tellParentThatChildUsed(self):
203 if self.parent :
204 self.parent.childUsed(self)
205
206
207 def parentUsed(self):
208 self._incNbUse()
209 # Propagate to children
210 for child in self.children :
211 child.parentUsed()
212
213 def hasBeenUsed(self):
214 return self.nbUse > 0
215
216 def operationOnChild(self, path, operation):
217 # Stop if path is not consume yet but there is no child
218 assert(self.children or not path)
219
220 if path:
221 return self._operationPropagate(path, operation)
222 else :
223 self.debug("operating on self")
224 return operation(self)
225
226 def _operationPropagate(self, path, operation):
227
228 childName = path.pop(0)
229 child = self.getChildFromName(childName)
230
231 return child.operationOnChild(path, operation)
232
233
234
235 def debug(self, stringOrFunction, level=logging.DEBUG):
236 """Print a debug line on stderr in tree form
237
238 If the debug line is expensive to generate, provide callable
239 object, it will be called if log is enable for this level.
240 This callable object should return the logline string.
241 """
242 if logger.isEnabledFor(level):
243
244 # TODO: use buildin callable if python >= 3.2
245 if hasattr(stringOrFunction, "__call__"):
246 string = stringOrFunction()
247 else:
248 string = stringOrFunction
249
250 rankedLine = DebugRankedLine("%s: %s" % (self, string))
251 self._logDebug(rankedLine, level)
252
253 def _logDebug(self, rankedLine, level):
254
255 if self.parent:
256 self.parent._logDebug(rankedLine.increasedRank(), level)
257 else :
258 logger.log(level, str(rankedLine))
259
260
261
262
263class FromDomElement(Element):
264 def __init__(self, DomElement):
265 self._initFromDom(DomElement)
266 super().__init__(self.name)
267
268
269 def _initFromDom(self, DomElement):
270 self.name = DomElement.getAttribute("Name")
271
272
273
274class DomElementLocation():
275 def __init__(self, classConstructor, path=None):
276 self.classConstructor = classConstructor
277 if path :
278 self.path = path
279 else :
280 self.path = []
281
282 self.path.append(classConstructor.tag)
283
284
285class DomPopulatedElement(Element):
286 """Default child populate
287
288 Look for each dom element with tag specified in self.tag
289 and instantiate it with the dom element
290 """
291 childClasses = []
292
293 def populate(self, dom):
294
295 for childDomElementLocation in self.childClasses :
296
297 self.debug("Looking for child %s in path %s" % (
298 childDomElementLocation.path[-1], childDomElementLocation.path))
299
300 for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path) :
301
302 childElement = childDomElementLocation.classConstructor(childDomElement)
303 self.addChild(childElement)
304
305 childElement.populate(childDomElement)
306
307 def _findChildFromTagPath(self, dom, path):
308 if not path :
309 yield dom
310 else :
311 # Copy list
312 path = list(path)
313
314 tag = path.pop(0)
315
316 # Find element with tag
317 self.debug("Going to find elements with tag %s in %s" % (tag, dom))
318 self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
319
320 for elementByTag in dom.getElementsByTagName(tag) :
321
322 self.debug("Found element: %s" % elementByTag)
323
324 # If the same tag is found
325 if elementByTag in dom.childNodes :
326
327 # Yield next level
328 for element in self._findChildFromTagPath(elementByTag, path) :
329 yield element
330
331
332class Rule(Element):
333
334 def usedIfApplicable(self, criteria):
335 childApplicability = (child.usedIfApplicable(criteria)
336 for child in self.children)
337
338 isApplicable = self._isApplicable(criteria, childApplicability)
339
340 if isApplicable :
341 self._incNbUse()
342
343 self.debug("Rule applicability: %s" % isApplicable)
344 assert(isApplicable == True or isApplicable == False)
345
346 return isApplicable
347
348
349 def _isApplicable(self, criteria, childApplicability):
350 # Forcing evaluation of all child by the list creation
351 return all(list(childApplicability))
352
353
354class CriterionRule(FromDomElement, DomPopulatedElement, Rule):
355 tag = "SelectionCriterionRule"
356 childClasses = []
357 isApplicableOperations = {
358 "Includes" : lambda criterion, value: criterion.stateIncludes(value),
359 "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
360 "Is" : lambda criterion, value: criterion.stateIs(value),
361 "IsNot" : lambda criterion, value: not criterion.stateIs(value)
362 }
363
364 def _initFromDom(self, DomElement):
365 self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
366 self.matchesWhen = DomElement.getAttribute("MatchesWhen")
367 self.value = DomElement.getAttribute("Value")
368 self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
369
370 applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
371 self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion, self.value)
372
373 def _isApplicable(self, criteria, childApplicability):
374
375 return criteria.operationOnChild([self.selectionCriterion],
376 self.isApplicableOperation)
377
378
379class CompoundRule(FromDomElement, DomPopulatedElement, Rule):
380 """CompoundRule can be of type ALL or ANY"""
381 tag = "CompoundRule"
382 # Declare childClasses but define it at first class instantiation
383 childClasses = None
384
385 def __init__(self, dom):
386 # Define childClasses at first class instantiation
387 if self.childClasses == None :
388 self.childClasses = [DomElementLocation(CriterionRule),
389 DomElementLocation(CompoundRule)]
390 super().__init__(dom)
391
392 def _initFromDom(self, DomElement):
393
394 type = DomElement.getAttribute("Type")
395 self.ofTypeAll = {"All" : True, "Any" : False}[type]
396 self.name = type
397
398 def _isApplicable(self, criteria, childApplicability):
399 if self.ofTypeAll :
400 applicability = super()._isApplicable(criteria, childApplicability)
401 else:
402 # Forcing evaluation of all child by the list creation
403 applicability = any(list(childApplicability))
404
405 return applicability
406
407class RootRule(DomPopulatedElement, Rule):
408 tag = "RootRule"
409 childClasses = [DomElementLocation(CompoundRule)]
410
411 def populate(self, dom):
412 super().populate(dom)
413 self.debug("Children: %s" % self.children)
414 # A configuration can only have one or no rule
415 assert(len(self.children) <= 1)
416
417 def _getCoverageDependance(self):
418 return self._getDescendants()
419
420
421class CriteronStates(Element):
422 """Root of configuration application criterion state"""
423 tag = "CriterionStates"
424
425 def parentUsed(self, criteria):
426 """Add criteria to child if not exist, if exist increase it's nbUse"""
427 self._incNbUse()
428
429 matches = [child for child in self.children if child == criteria]
430
431 assert(len(matches) <= 1)
432
433 if matches :
434 self.debug("Criteria state has already been encounter")
435 currentcriteria = matches[0]
436 else :
437 self.debug("Criteria state has never been encounter, saving it")
438 currentcriteria = criteria
439 self.addChild(criteria)
440
441 currentcriteria.parentUsed()
442
443class IneligibleConfigurationAppliedError(Exception):
444
445 def __init__(self, configuration, criteria):
446 self.configuration = configuration
447 self.criteria = criteria
448
449 def __str__(self):
450
451 return ("Applying ineligible configuration %s from domain %s."
452 "Configuration rule:\n%s\n"
453 "Criteria:\n%s\n" %
454 (self.configuration, configuration.rootRule.dump(), criteria))
455
456
457class Configuration(FromDomElement, DomPopulatedElement):
458 tag = "Configuration"
459 childClasses = []
460
461 def __init__(self, DomElement):
462 super().__init__(DomElement)
463
464 self.rootRule = RootRule("RootRule")
465 self.addChild(self.rootRule)
466
467 self.criteronStates = CriteronStates("CriterionStates")
468 self.addChild(self.criteronStates)
469
470 def populate(self, dom):
471 # Delegate to rootRule
472 self.rootRule.populate(dom)
473
474 def _getCoverage(self):
475 # Delegate to rootRule
476 return self.rootRule._getCoverage()
477
478 def used(self, criteria):
479
480 self._incNbUse()
481
482 # Propagate use to parents
483 self._tellParentThatChildUsed()
484
485 # Propagate to criterion coverage
486 self.criteronStates.parentUsed(criteria.export())
487
488 # Propagate to rules
489 if not self.rootRule.usedIfApplicable(criteria) :
490
491 self.debug("Applied (parent: %s) "
492 "but rule does not match current criteria." % self.parent.name,
493 logging.FATAL)
494
495 self.debug("Rule :\n%s" % self.rootRule.dump(), logging.INFO)
496 self.debug("Current criteria:\n%s" % criteria.export().dump(), logging.INFO)
497
498 raise IneligibleConfigurationAppliedError(self, criteria.export())
499
500 def _dumpPropagate(self, withCoverage, withNbUse):
501 self.debug("Going to ask %s for description" % self.rootRule)
502 for dumpedDescription in self.rootRule._dumpDescription(
503 withCoverage=withCoverage,
504 withNbUse=withNbUse) :
505 yield dumpedDescription.increasedRank()
506
507 self.debug("Going to ask %s for description" % self.criteronStates)
508 for dumpedDescription in self.criteronStates._dumpDescription(
509 withCoverage=False,
510 withNbUse=withNbUse) :
511 yield dumpedDescription.increasedRank()
512
513
514class Domain(FromDomElement, DomPopulatedElement):
515 tag = "ConfigurableDomain"
516 childClasses = [DomElementLocation(Configuration, ["Configurations"])]
517
518
519class Domains(DomPopulatedElement):
520 tag = "Domains"
521 childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
522
523
524class RankedLine():
525 def __init__(self, string,
526 stringPrefix="|-- ",
527 rankString="| ",
528 linePrefix="",
529 lineSuffix="\n"):
530 self.string = string
531 self.rank = 0
532 self.stringPrefix = stringPrefix
533 self.rankString = rankString
534 self.linePrefix = linePrefix
535 self.lineSuffix = lineSuffix
536
537 def increasedRank(self):
538 self.rank += 1
539 return self
540
541 def __str__(self):
542 return self.linePrefix + \
543 self.rank * self.rankString + \
544 self.stringPrefix + \
545 self.string + \
546 self.lineSuffix
547
548class DebugRankedLine(RankedLine):
549
550 def __init__(self, string, lineSuffix=""):
551 super().__init__(string,
552 stringPrefix="",
553 rankString=" ",
554 linePrefix="",
555 lineSuffix=lineSuffix)
556
557
558class CriterionState(Element):
559 tag = "CriterionState"
560 def used(self):
561 self._incNbUse()
562
563
564class Criterion(Element):
565 tag = "Criterion"
566 def __init__(self, name, isInclusif, stateNamesList, currentStateNamesList):
567 super().__init__(name)
568 self.isInclusif = isInclusif
569
570 assert(stateNamesList)
571
572 for state in stateNamesList :
573 self.addChild(CriterionState(state))
574
575 self.currentState = []
576
577 # Set current state as provided
578 self.currentState = [self.getChildFromName(childName)
579 for childName in currentStateNamesList]
580
581 def childUsed(self, child):
582 self.currentState = child
583 super().childUsed(child)
584
585 def changeState(self, subStateNames):
586 self.debug("Changing state from: %s to: %s" % (
587 list(self._getElementNames(self.currentState)),
588 subStateNames),
589 logging.INFO)
590
591 assert(len(subStateNames) > 0 or self.isInclusif)
592
593 newCurrentState = []
594 for subStateName in subStateNames :
595 subState = self.getChildFromName(subStateName)
596 subState.used()
597 newCurrentState.append(subState)
598
599 self.currentState = newCurrentState
600
601 self._incNbUse()
602 self._tellParentThatChildUsed()
603
604 def export(self):
605 subStateNames = self._getElementNames(self.currentState)
606 return Criterion(self.name, self.isInclusif, subStateNames, subStateNames)
607
608 def stateIncludes(self, subStateName):
609 subStateCurrentNames = list(self._getElementNames(self.currentState))
610
611 self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
612
613 isIncluded = subStateName in subStateCurrentNames
614 self.debug("IsIncluded: %s" % isIncluded)
615
616 return isIncluded
617
618
619 def stateIs(self, subStateNames):
620
621 if len(self.currentState) != 1 :
622 return False
623 else :
624 return self.stateIncludes(subStateNames)
625
626
627class Criteria(Element):
628 tag = "Criteria"
629
630 def export(self):
631 self.debug("Exporting criteria")
632 assert(self.children)
633
634 exported = Criteria(self.name)
635 for child in self.children :
636 exported.addChild(child.export())
637 return exported
638
639class ConfigAppliedWithoutCriteriaError(Exception):
640 def __init__(self, configurationName, domainName):
641 self.configurationName = configurationName
642 self.domainName = domainName
643 def __str__(self):
644 return ("Applying configuration %s from domain %s before declaring criteria" %
645 self.configurationName, self.domainName)
646
647class ParsePFWlog():
648 MATCH = "match"
649 ACTION = "action"
650
651 def __init__(self, domains, criteria):
652
653 self.domains = domains;
654 self.criteria = criteria;
655
656 configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
657 matchConfigApplicationLine = re.compile(configApplicationRegext).match
658
659 criterionCreationRegext = ", ".join([
660 r""".*Criterion name: (.*)""",
661 r"""type kind: (.*)""",
662 r"""current state: (.*)""",
663 r"""states: {(.*)}"""
664 ])
665 matchCriterionCreationLine = re.compile(criterionCreationRegext).match
666
667 changingCriterionRegext = r""".*Selection criterion changed event: (.*) = ([^\n\r]*)"""
668 matchChangingCriterionLine = re.compile(changingCriterionRegext).match
669
670 self.lineLogTypes = [
671 {
672 self.MATCH: matchConfigApplicationLine,
673 self.ACTION: self._configApplication
674 }, {
675 self.MATCH: matchCriterionCreationLine,
676 self.ACTION: self._criterionCreation
677 }, {
678 self.MATCH: matchChangingCriterionLine,
679 self.ACTION: self._changingCriterion
680 }
681 ]
682
683 @staticmethod
684 def _formatCriterionList(liststring, separator):
685 list = liststring.split(separator)
686 if len(list) == 1 and list[0] == "<none>":
687 list = []
688 return list
689
690 def _criterionCreation(self, matchCriterionCreation):
691 # Unpack
692 criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
693
694 criterionStateList = self._formatCriterionList(criterionStates, ", ")
695
696 criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
697
698 currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
699
700 logger.info("Creating criterion: " + criterionName +
701 " (" + criterionType + ") " +
702 " with current state: " + str(currentcriterionStateList) +
703 ", possible states:" + str(criterionStateList))
704
705 self.criteria.addChild(Criterion(
706 criterionName,
707 criterionIsInclusif,
708 criterionStateList,
709 currentcriterionStateList
710 ))
711
712 def _changingCriterion(self, matchChangingCriterion):
713 # Unpack
714 criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
715
716 newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
717
718 path = [criterionName]
719 changeCriterionOperation = lambda criterion : criterion.changeState(newCriterionState)
720 self.criteria.operationOnChild(path, changeCriterionOperation)
721
722 def _configApplication(self, matchConfig):
723 # Check that at least one criterion exist
724 if not self.criteria.hasChildren() :
725 logger.error("Applying configuration before declaring criteria")
726 logger.info("Is the log starting at PFW boot ?")
727 raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
728
729 # Unpack
730 configurationName, domainName = matchConfig.group(1, 2)
731 # Change criterion state
732 path = [domainName, configurationName]
733 usedOperation = lambda element : element.used(self.criteria)
734
735 logger.info("Applying configuration %s from domain %s" % (
736 configurationName, domainName))
737
738 self.domains.operationOnChild(path, usedOperation)
739
740
741 def _digest(self, lineLogType, lineLog):
742 match = lineLogType[self.MATCH](lineLog)
743 if match :
744 lineLogType[self.ACTION](match)
745 return True
746 return False
747
748 def parsePFWlog(self, lines):
749
750 for lineLog in lines:
751
752 logger.debug("Parsing line :%s" % lineLog.rstrip())
753
754 digested = (self._digest(lineLogType, lineLog)
755 for lineLogType in self.lineLogTypes)
756
757 if not any(digested):
758 logger.debug("Line does not match, dropped")
759
760
761class Root(Element):
762 tag = "Root"
763 def __init__(self, name, dom):
764 super().__init__(name)
765 # Create domain tree
766 self.domains = Domains("Domains")
767 self.domains.populate(dom)
768 self.addChild(self.domains)
769 # Create criterion list
770 self.criteria = Criteria("CriterionRoot")
771 self.addChild(self.criteria)
772
773 def exportToXML(self):
774 """Export tree to an xml document"""
775 impl = xml.dom.minidom.getDOMImplementation()
776 newdoc = impl.createDocument(None, self.name, None)
777 XMLDocElement = newdoc.documentElement
778
779 for child in self.children:
780 XMLDocElement.appendChild(child.exportToXML())
781
782 return newdoc
783
784# ============================
785# Command line argument parser
786# ============================
787
788
789class ArgumentParser:
790 """class that parse command line arguments with argparse library
791
792 Result of parsing are the class attributes.
793 """
794 levelTranslate = [logging.WARNING, logging.INFO, logging.DEBUG]
795
796 def __init__(self):
797
798 try:
799 # As argparse is only in the stdlib since python 3.2, testing its availability
800 import argparse
801
802 except ImportError:
803 logger.warning("Unable to import argparse (argument parseur module), ")
804 logger.warning("using default argument values:")
805
806 self.inputFile = sys.stdin
807 logger.warning("InputFile: stdin")
808
809 self.outputFile = sys.stdout
810 logger.warning("OutputFile: stdout")
811
812 self.domainsFile = sys.argv[1]
813 logger.warning("Domain file: " + self.domainsFile)
814
815 self.XMLreport = True
816 logger.warning("OutputFormat: xml")
817
818 self.debugLevel = 0
819 else :
820
821 myArgParser = argparse.ArgumentParser(description='Generate PFW report')
822
823 myArgParser.add_argument(
824 'domainsFile',
825 type=argparse.FileType('r'),
826 help="the PFW domain XML file"
827 )
828 myArgParser.add_argument(
829 'pfwlog', nargs='?',
830 type=argparse.FileType('r'), default=sys.stdin,
831 help="the PFW log file, default stdin"
832 )
833 myArgParser.add_argument(
834 '-o', '--output',
835 dest="outputFile",
836 type=argparse.FileType('w'), default=sys.stdout,
837 help="the coverage report output file, default stdout"
838 )
839 myArgParser.add_argument(
840 '-v', '--verbose',
841 dest="debugLevel", default=0,
842 action='count',
843 help="print debug warnings from warning (default) to debug (-vv)"
844 )
845
846 outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
847
848 outputFormatGroupe.add_argument(
849 '--xml',
850 dest="xmlFlag",
851 action='store_true',
852 help=" XML coverage output report"
853 )
854 outputFormatGroupe.add_argument(
855 '--raw',
856 dest="rawFlag",
857 action='store_true',
858 help="raw coverage output report"
859 )
860
861 # Process command line arguments
862 options = myArgParser.parse_args()
863
864 # Mapping to attributes
865 self.inputFile = options.pfwlog
866 self.outputFile = options.outputFile
867 self.domainsFile = options.domainsFile
868
869 # Output report in xml if flag not set
870 self.XMLreport = not options.rawFlag
871
872 # Setting logger level
873 levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
874 self.debugLevel = self.levelTranslate[levelCapped]
875
876
877
878def main():
879
880 commandLineArguments = ArgumentParser()
881
882 # Setting logger level
883 logger.setLevel(commandLineArguments.debugLevel)
884 logger.info("Log level set to: %s" %
885 logging.getLevelName(commandLineArguments.debugLevel))
886
887 # Create tree from XML
888 dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
889
890 root = Root("Coverage", dom)
891
892 parser = ParsePFWlog(root.domains, root.criteria)
893 try:
894 parser.parsePFWlog(commandLineArguments.inputFile.readlines())
895 except Exception as ex:
896 sys.stderr.write("Error during parsing log file %s:\n%s\n" %
897 (commandLineArguments.inputFile, ex))
898
899 outputFile = commandLineArguments.outputFile
900
901 if not commandLineArguments.XMLreport :
902
903 outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
904
905 else :
906
907 outputFile.write(root.exportToXML().toprettyxml())
908
909
910# Execute main function if the python interpreter is running this module as the main program
911if __name__ == "__main__" :
912 main()
913