NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

covercp.py (9962B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\lib\covercp.py
      2 import re
      3 import sys
      4 import cgi
      5 from cherrypy._cpcompat import quote_plus
      6 import os, os.path
      7 localFile = os.path.join(os.path.dirname(__file__), 'coverage.cache')
      8 the_coverage = None
      9 try:
     10     from coverage import coverage
     11     the_coverage = coverage(data_file=localFile)
     12 
     13     def start():
     14         the_coverage.start()
     15 
     16 
     17 except ImportError:
     18     the_coverage = None
     19     import warnings
     20     warnings.warn('No code coverage will be performed; coverage.py could not be imported.')
     21 
     22     def start():
     23         pass
     24 
     25 
     26 start.priority = 20
     27 TEMPLATE_MENU = '<html>\n<head>\n    <title>CherryPy Coverage Menu</title>\n    <style>\n        body {font: 9pt Arial, serif;}\n        #tree {\n            font-size: 8pt;\n            font-family: Andale Mono, monospace;\n            white-space: pre;\n            }\n        #tree a:active, a:focus {\n            background-color: black;\n            padding: 1px;\n            color: white;\n            border: 0px solid #9999FF;\n            -moz-outline-style: none;\n            }\n        .fail { color: red;}\n        .pass { color: #888;}\n        #pct { text-align: right;}\n        h3 {\n            font-size: small;\n            font-weight: bold;\n            font-style: italic;\n            margin-top: 5px; \n            }\n        input { border: 1px solid #ccc; padding: 2px; }\n        .directory {\n            color: #933;\n            font-style: italic;\n            font-weight: bold;\n            font-size: 10pt;\n            }\n        .file {\n            color: #400;\n            }\n        a { text-decoration: none; }\n        #crumbs {\n            color: white;\n            font-size: 8pt;\n            font-family: Andale Mono, monospace;\n            width: 100%;\n            background-color: black;\n            }\n        #crumbs a {\n            color: #f88;\n            }\n        #options {\n            line-height: 2.3em;\n            border: 1px solid black;\n            background-color: #eee;\n            padding: 4px;\n            }\n        #exclude {\n            width: 100%;\n            margin-bottom: 3px;\n            border: 1px solid #999;\n            }\n        #submit {\n            background-color: black;\n            color: white;\n            border: 0;\n            margin-bottom: -9px;\n            }\n    </style>\n</head>\n<body>\n<h2>CherryPy Coverage</h2>'
     28 TEMPLATE_FORM = '\n<div id="options">\n<form action=\'menu\' method=GET>\n    <input type=\'hidden\' name=\'base\' value=\'%(base)s\' />\n    Show percentages <input type=\'checkbox\' %(showpct)s name=\'showpct\' value=\'checked\' /><br />\n    Hide files over <input type=\'text\' id=\'pct\' name=\'pct\' value=\'%(pct)s\' size=\'3\' />%%<br />\n    Exclude files matching<br />\n    <input type=\'text\' id=\'exclude\' name=\'exclude\' value=\'%(exclude)s\' size=\'20\' />\n    <br />\n\n    <input type=\'submit\' value=\'Change view\' id="submit"/>\n</form>\n</div>'
     29 TEMPLATE_FRAMESET = "<html>\n<head><title>CherryPy coverage data</title></head>\n<frameset cols='250, 1*'>\n    <frame src='menu?base=%s' />\n    <frame name='main' src='' />\n</frameset>\n</html>\n"
     30 TEMPLATE_COVERAGE = '<html>\n<head>\n    <title>Coverage for %(name)s</title>\n    <style>\n        h2 { margin-bottom: .25em; }\n        p { margin: .25em; }\n        .covered { color: #000; background-color: #fff; }\n        .notcovered { color: #fee; background-color: #500; }\n        .excluded { color: #00f; background-color: #fff; }\n         table .covered, table .notcovered, table .excluded\n             { font-family: Andale Mono, monospace;\n               font-size: 10pt; white-space: pre; }\n\n         .lineno { background-color: #eee;}\n         .notcovered .lineno { background-color: #000;}\n         table { border-collapse: collapse;\n    </style>\n</head>\n<body>\n<h2>%(name)s</h2>\n<p>%(fullpath)s</p>\n<p>Coverage: %(pc)s%%</p>'
     31 TEMPLATE_LOC_COVERED = '<tr class="covered">\n    <td class="lineno">%s&nbsp;</td>\n    <td>%s</td>\n</tr>\n'
     32 TEMPLATE_LOC_NOT_COVERED = '<tr class="notcovered">\n    <td class="lineno">%s&nbsp;</td>\n    <td>%s</td>\n</tr>\n'
     33 TEMPLATE_LOC_EXCLUDED = '<tr class="excluded">\n    <td class="lineno">%s&nbsp;</td>\n    <td>%s</td>\n</tr>\n'
     34 TEMPLATE_ITEM = "%s%s<a class='file' href='report?name=%s' target='main'>%s</a>\n"
     35 
     36 def _percent(statements, missing):
     37     s = len(statements)
     38     e = s - len(missing)
     39     if s > 0:
     40         return int(round(100.0 * e / s))
     41     return 0
     42 
     43 
     44 def _show_branch(root, base, path, pct = 0, showpct = False, exclude = '', coverage = the_coverage):
     45     dirs = [ k for k, v in root.items() if v ]
     46     dirs.sort()
     47     for name in dirs:
     48         newpath = os.path.join(path, name)
     49         if newpath.lower().startswith(base):
     50             relpath = newpath[len(base):]
     51             yield '| ' * relpath.count(os.sep)
     52             yield "<a class='directory' href='menu?base=%s&exclude=%s'>%s</a>\n" % (newpath, quote_plus(exclude), name)
     53         for chunk in _show_branch(root[name], base, newpath, pct, showpct, exclude, coverage=coverage):
     54             yield chunk
     55 
     56     if path.lower().startswith(base):
     57         relpath = path[len(base):]
     58         files = [ k for k, v in root.items() if not v ]
     59         files.sort()
     60         for name in files:
     61             newpath = os.path.join(path, name)
     62             pc_str = ''
     63             if showpct:
     64                 try:
     65                     _, statements, _, missing, _ = coverage.analysis2(newpath)
     66                 except:
     67                     pass
     68                 else:
     69                     pc = _percent(statements, missing)
     70                     pc_str = ('%3d%% ' % pc).replace(' ', '&nbsp;')
     71                     if pc < float(pct) or pc == -1:
     72                         pc_str = "<span class='fail'>%s</span>" % pc_str
     73                     else:
     74                         pc_str = "<span class='pass'>%s</span>" % pc_str
     75             yield TEMPLATE_ITEM % ('| ' * (relpath.count(os.sep) + 1),
     76              pc_str,
     77              newpath,
     78              name)
     79 
     80 
     81 def _skip_file(path, exclude):
     82     if exclude:
     83         return bool(re.search(exclude, path))
     84 
     85 
     86 def _graft(path, tree):
     87     d = tree
     88     p = path
     89     atoms = []
     90     while True:
     91         p, tail = os.path.split(p)
     92         if not tail:
     93             break
     94         atoms.append(tail)
     95 
     96     atoms.append(p)
     97     if p != '/':
     98         atoms.append('/')
     99     atoms.reverse()
    100     for node in atoms:
    101         if node:
    102             d = d.setdefault(node, {})
    103 
    104 
    105 def get_tree(base, exclude, coverage = the_coverage):
    106     tree = {}
    107     runs = coverage.data.executed_files()
    108     for path in runs:
    109         if not _skip_file(path, exclude) and not os.path.isdir(path):
    110             _graft(path, tree)
    111 
    112     return tree
    113 
    114 
    115 class CoverStats(object):
    116 
    117     def __init__(self, coverage, root = None):
    118         self.coverage = coverage
    119         if root is None:
    120             import cherrypy
    121             root = os.path.dirname(cherrypy.__file__)
    122         self.root = root
    123 
    124     def index(self):
    125         return TEMPLATE_FRAMESET % self.root.lower()
    126 
    127     index.exposed = True
    128 
    129     def menu(self, base = '/', pct = '50', showpct = '', exclude = 'python\\d\\.\\d|test|tut\\d|tutorial'):
    130         base = base.lower().rstrip(os.sep)
    131         yield TEMPLATE_MENU
    132         yield TEMPLATE_FORM % locals()
    133         yield "<div id='crumbs'>"
    134         path = ''
    135         atoms = base.split(os.sep)
    136         atoms.pop()
    137         for atom in atoms:
    138             path += atom + os.sep
    139             yield "<a href='menu?base=%s&exclude=%s'>%s</a> %s" % (path,
    140              quote_plus(exclude),
    141              atom,
    142              os.sep)
    143 
    144         yield '</div>'
    145         yield "<div id='tree'>"
    146         tree = get_tree(base, exclude, self.coverage)
    147         if not tree:
    148             yield '<p>No modules covered.</p>'
    149         else:
    150             for chunk in _show_branch(tree, base, '/', pct, showpct == 'checked', exclude, coverage=self.coverage):
    151                 yield chunk
    152 
    153         yield '</div>'
    154         yield '</body></html>'
    155 
    156     menu.exposed = True
    157 
    158     def annotated_file(self, filename, statements, excluded, missing):
    159         source = open(filename, 'r')
    160         buffer = []
    161         for lineno, line in enumerate(source.readlines()):
    162             lineno += 1
    163             line = line.strip('\n\r')
    164             empty_the_buffer = True
    165             if lineno in excluded:
    166                 template = TEMPLATE_LOC_EXCLUDED
    167             elif lineno in missing:
    168                 template = TEMPLATE_LOC_NOT_COVERED
    169             elif lineno in statements:
    170                 template = TEMPLATE_LOC_COVERED
    171             else:
    172                 empty_the_buffer = False
    173                 buffer.append((lineno, line))
    174             if empty_the_buffer:
    175                 for lno, pastline in buffer:
    176                     yield template % (lno, cgi.escape(pastline))
    177 
    178                 buffer = []
    179                 yield template % (lineno, cgi.escape(line))
    180 
    181     def report(self, name):
    182         filename, statements, excluded, missing, _ = self.coverage.analysis2(name)
    183         pc = _percent(statements, missing)
    184         yield TEMPLATE_COVERAGE % dict(name=os.path.basename(name), fullpath=name, pc=pc)
    185         yield '<table>\n'
    186         for line in self.annotated_file(filename, statements, excluded, missing):
    187             yield line
    188 
    189         yield '</table>'
    190         yield '</body>'
    191         yield '</html>'
    192 
    193     report.exposed = True
    194 
    195 
    196 def serve(path = localFile, port = 8080, root = None):
    197     if coverage is None:
    198         raise ImportError('The coverage module could not be imported.')
    199     from coverage import coverage
    200     cov = coverage(data_file=path)
    201     cov.load()
    202     import cherrypy
    203     cherrypy.config.update({'server.socket_port': int(port),
    204      'server.thread_pool': 10,
    205      'environment': 'production'})
    206     cherrypy.quickstart(CoverStats(cov, root))
    207 
    208 
    209 if __name__ == '__main__':
    210     serve(*tuple(sys.argv[1:]))