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