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 </td>\n <td>%s</td>\n</tr>\n' 32 TEMPLATE_LOC_NOT_COVERED = '<tr class="notcovered">\n <td class="lineno">%s </td>\n <td>%s</td>\n</tr>\n' 33 TEMPLATE_LOC_EXCLUDED = '<tr class="excluded">\n <td class="lineno">%s </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(' ', ' ') 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:]))