| 103 | | |
|---|
| 104 | | coverage.get_ready() |
|---|
| 105 | | runs = coverage.cexecuted.keys() |
|---|
| 106 | | if runs: |
|---|
| 107 | | yield """<form action='menu'> |
|---|
| 108 | | <input type='hidden' name='base' value='%s' /> |
|---|
| 109 | | <input type='submit' value='Show %%' /> |
|---|
| 110 | | threshold: <input type='text' id='pct' name='pct' value='%s' size='3' />%% |
|---|
| 111 | | </form>""" % (base, pct or "50") |
|---|
| | 96 | |
|---|
| | 97 | TEMPLATE_FORM = """ |
|---|
| | 98 | <form action='menu' method=GET> |
|---|
| | 99 | <input type='hidden' name='base' value='%(base)s' /> |
|---|
| | 100 | <h3>Options</h3> |
|---|
| | 101 | <input type='checkbox' %(showpct)s name='showpct' value='checked'/> |
|---|
| | 102 | show percentages <br /> |
|---|
| | 103 | Hide files over <input type='text' id='pct' name='pct' value='%(pct)s' size='3' />%%<br /> |
|---|
| | 104 | Exclude files matching<br /> |
|---|
| | 105 | <input type='text' id='exclude' name='exclude' value='%(exclude)s' size='20' /> |
|---|
| | 106 | <br /> |
|---|
| | 107 | |
|---|
| | 108 | <input type='submit' value='Change view' /> |
|---|
| | 109 | </form>""" |
|---|
| | 110 | |
|---|
| | 111 | TEMPLATE_FRAMESET = """<html> |
|---|
| | 112 | <head><title>CherryPy coverage data</title></head> |
|---|
| | 113 | <frameset cols='250, 1*'> |
|---|
| | 114 | <frame src='menu?base=%s' /> |
|---|
| | 115 | <frame name='main' src='' /> |
|---|
| | 116 | </frameset> |
|---|
| | 117 | </html> |
|---|
| | 118 | """ % initial_base.lower() |
|---|
| | 119 | |
|---|
| | 120 | TEMPLATE_COVERAGE = """<html> |
|---|
| | 121 | <head> |
|---|
| | 122 | <title>Coverage for %(name)s</title> |
|---|
| | 123 | <style> |
|---|
| | 124 | h2 { margin-bottom: .25em; } |
|---|
| | 125 | p { margin: .25em; } |
|---|
| | 126 | .covered { color: #000; background-color: #fff; } |
|---|
| | 127 | .notcovered { color: #fee; background-color: #500; } |
|---|
| | 128 | .excluded { color: #00f; background-color: #fff; } |
|---|
| | 129 | table .covered, table .notcovered, table .excluded |
|---|
| | 130 | { font-family: Andale Mono, monospace; |
|---|
| | 131 | font-size: 10pt; white-space: pre; } |
|---|
| | 132 | |
|---|
| | 133 | .lineno { background-color: #eee;} |
|---|
| | 134 | .notcovered .lineno { background-color: #000;} |
|---|
| | 135 | table { border-collapse: collapse; |
|---|
| | 136 | </style> |
|---|
| | 137 | </head> |
|---|
| | 138 | <body> |
|---|
| | 139 | <h2>%(name)s</h2> |
|---|
| | 140 | <p>%(fullpath)s</p> |
|---|
| | 141 | <p>Coverage: %(pc)s%%</p>""" |
|---|
| | 142 | |
|---|
| | 143 | TEMPLATE_LOC_COVERED = """<tr class="covered"> |
|---|
| | 144 | <td class="lineno">%s </td> |
|---|
| | 145 | <td>%s</td> |
|---|
| | 146 | </tr>\n""" |
|---|
| | 147 | TEMPLATE_LOC_NOT_COVERED = """<tr class="notcovered"> |
|---|
| | 148 | <td class="lineno">%s </td> |
|---|
| | 149 | <td>%s</td> |
|---|
| | 150 | </tr>\n""" |
|---|
| | 151 | TEMPLATE_LOC_EXCLUDED = """<tr class="excluded"> |
|---|
| | 152 | <td class="lineno">%s </td> |
|---|
| | 153 | <td>%s</td> |
|---|
| | 154 | </tr>\n""" |
|---|
| | 155 | |
|---|
| | 156 | |
|---|
| | 157 | def _skip_file(path, exclude): |
|---|
| | 158 | if exclude: |
|---|
| | 159 | return bool(re.search(exclude, path)) |
|---|
| | 160 | |
|---|
| | 161 | def _percent(statements, missing): |
|---|
| | 162 | s = len(statements) |
|---|
| | 163 | e = s - len(missing) |
|---|
| | 164 | if s > 0: |
|---|
| | 165 | return int(round(100.0 * e / s)) |
|---|
| | 166 | return 0 |
|---|
| | 167 | |
|---|
| | 168 | def _show_branch(root, base="", path="", pct=0, showpct=False, exclude=""): |
|---|
| | 169 | |
|---|
| | 170 | # Show the directory name and any of our children |
|---|
| | 171 | dirs = [k for k, v in root.iteritems() if v is not None] |
|---|
| | 172 | dirs.sort() |
|---|
| | 173 | for name in dirs: |
|---|
| | 174 | if path: |
|---|
| | 175 | newpath = os.sep.join((path, name)) |
|---|
| | 176 | else: |
|---|
| | 177 | newpath = name |
|---|
| | 178 | |
|---|
| | 179 | if newpath.startswith(base): |
|---|
| | 180 | relpath = newpath[len(base):] |
|---|
| | 181 | yield "<nobr>" + ("| " * relpath.count(os.sep)) + "<b>" |
|---|
| | 182 | yield ("<a href='menu?base=%s&exclude=%s'>%s</a>" % |
|---|
| | 183 | (newpath, urllib.quote_plus(exclude), name)) |
|---|
| | 184 | yield "</b></nobr><br />\n" |
|---|
| | 185 | |
|---|
| | 186 | for chunk in _show_branch(root[name], base, newpath, pct, showpct, exclude): |
|---|
| | 187 | yield chunk |
|---|
| | 188 | |
|---|
| | 189 | # Now list the files |
|---|
| | 190 | if path.startswith(base): |
|---|
| | 191 | relpath = path[len(base):] |
|---|
| | 192 | files = [k for k, v in root.iteritems() if v is None] |
|---|
| | 193 | files.sort() |
|---|
| | 194 | for name in files: |
|---|
| | 195 | if path: |
|---|
| | 196 | newpath = os.sep.join((path, name)) |
|---|
| | 197 | else: |
|---|
| | 198 | newpath = name |
|---|
| 127 | | def show(root, depth=0, path=""): |
|---|
| 128 | | dirs = [k for k, v in root.iteritems() if v is not None] |
|---|
| 129 | | dirs.sort() |
|---|
| 130 | | for name in dirs: |
|---|
| 131 | | if path: |
|---|
| 132 | | newpath = os.sep.join((path, name)) |
|---|
| 133 | | else: |
|---|
| 134 | | newpath = name |
|---|
| 135 | | |
|---|
| 136 | | yield "<nobr>" + ("| " * depth) + "<b>" |
|---|
| 137 | | yield "<a href='menu?base=%s'>%s</a>" % (newpath, name) |
|---|
| 138 | | yield "</b></nobr><br />\n" |
|---|
| 139 | | for chunk in show(root[name], depth + 1, newpath): |
|---|
| 140 | | yield chunk |
|---|
| 141 | | |
|---|
| 142 | | files = [k for k, v in root.iteritems() if v is None] |
|---|
| 143 | | files.sort() |
|---|
| 144 | | for name in files: |
|---|
| 145 | | if path: |
|---|
| 146 | | newpath = os.sep.join((path, name)) |
|---|
| 147 | | else: |
|---|
| 148 | | newpath = name |
|---|
| 149 | | |
|---|
| 150 | | pc_str = "" |
|---|
| 151 | | if pct: |
|---|
| 152 | | try: |
|---|
| 153 | | _, statements, _, missing, _ = coverage.analysis2(newpath) |
|---|
| 154 | | except: |
|---|
| 155 | | # Yes, we really want to pass on all errors. |
|---|
| 156 | | pass |
|---|
| 157 | | else: |
|---|
| 158 | | s = len(statements) |
|---|
| 159 | | e = s - len(missing) |
|---|
| 160 | | if s > 0: |
|---|
| 161 | | pc = 100.0 * e / s |
|---|
| 162 | | pc_str = "%d%% " % pc |
|---|
| 163 | | if pc < 100: |
|---|
| 164 | | pc_str = " " + pc_str |
|---|
| 165 | | if pc < 10: |
|---|
| 166 | | pc_str = " " + pc_str |
|---|
| 167 | | if pc < float(pct): |
|---|
| 168 | | pc_str = "<span class='fail'>%s</span>" % pc_str |
|---|
| 169 | | yield ("<nobr>%s%s<a href='report?name=%s' target='main'>%s</a></nobr><br />\n" |
|---|
| 170 | | % ("| " * depth, pc_str, newpath, name)) |
|---|
| 171 | | |
|---|
| 172 | | for chunk in show(tree): |
|---|
| | 215 | yield ("<nobr>%s%s<a href='report?name=%s' target='main'>%s</a></nobr><br />\n" |
|---|
| | 216 | % ("| " * (relpath.count(os.sep) + 1), pc_str, newpath, name)) |
|---|
| | 217 | |
|---|
| | 218 | def get_tree(base, exclude): |
|---|
| | 219 | """Return covered module names as a nested dict.""" |
|---|
| | 220 | tree = {} |
|---|
| | 221 | coverage.get_ready() |
|---|
| | 222 | runs = coverage.cexecuted.keys() |
|---|
| | 223 | if runs: |
|---|
| | 224 | tree = {} |
|---|
| | 225 | def graft(path): |
|---|
| | 226 | head, tail = os.path.split(path) |
|---|
| | 227 | if tail: |
|---|
| | 228 | return graft(head).setdefault(tail, {}) |
|---|
| | 229 | else: |
|---|
| | 230 | return tree.setdefault(head.strip(r"\/"), {}) |
|---|
| | 231 | |
|---|
| | 232 | for path in runs: |
|---|
| | 233 | if not _skip_file(path, exclude) and not os.path.isdir(path): |
|---|
| | 234 | head, tail = os.path.split(path) |
|---|
| | 235 | if head.startswith(base): |
|---|
| | 236 | graft(head)[tail] = None |
|---|
| | 237 | return tree |
|---|
| | 238 | |
|---|
| | 239 | |
|---|
| | 240 | class CoverStats(object): |
|---|
| | 241 | |
|---|
| | 242 | def index(self): |
|---|
| | 243 | return TEMPLATE_FRAMESET |
|---|
| | 244 | index.exposed = True |
|---|
| | 245 | |
|---|
| | 246 | def menu(self, base="", pct="50", showpct="", |
|---|
| | 247 | exclude=r'python\d\.\d|test|tut\d|tutorial'): |
|---|
| | 248 | |
|---|
| | 249 | # The coverage module uses all-lower-case names. |
|---|
| | 250 | base = base.lower().rstrip(os.sep) |
|---|
| | 251 | |
|---|
| | 252 | yield TEMPLATE_MENU |
|---|
| | 253 | yield TEMPLATE_FORM % locals() |
|---|
| | 254 | |
|---|
| | 255 | yield "<div id='tree'>" |
|---|
| | 256 | |
|---|
| | 257 | # Start by showing links for parent paths |
|---|
| | 258 | path = "" |
|---|
| | 259 | atoms = base.split(os.sep) |
|---|
| | 260 | atoms.pop() |
|---|
| | 261 | for atom in atoms: |
|---|
| | 262 | path += atom + os.sep |
|---|
| | 263 | yield ("<nobr><b><a href='menu?base=%s&exclude=%s'>%s</a></b></nobr>%s\n" |
|---|
| | 264 | % (path, urllib.quote_plus(exclude), atom, os.sep)) |
|---|
| | 265 | |
|---|
| | 266 | tree = get_tree(base, exclude) |
|---|
| | 267 | if not tree: |
|---|
| | 268 | yield "<p>No modules covered.</p>" |
|---|
| | 269 | else: |
|---|
| | 270 | # Now show all visible branches |
|---|
| | 271 | yield "<br />" |
|---|
| | 272 | for chunk in _show_branch(tree, base, "", pct, showpct=='checked', exclude): |
|---|
| 193 | | while i < len(statements) and statements[i] < lineno: |
|---|
| 194 | | i = i + 1 |
|---|
| 195 | | while j < len(missing) and missing[j] < lineno: |
|---|
| 196 | | j = j + 1 |
|---|
| 197 | | if i < len(statements) and statements[i] == lineno: |
|---|
| 198 | | covered = j >= len(missing) or missing[j] > lineno |
|---|
| 199 | | if coverage.blank_re.match(line): |
|---|
| 200 | | dest.write(' ') |
|---|
| 201 | | elif coverage.else_re.match(line): |
|---|
| 202 | | # Special logic for lines containing only |
|---|
| 203 | | # 'else:'. See [GDR 2001-12-04b, 3.2]. |
|---|
| 204 | | if i >= len(statements) and j >= len(missing): |
|---|
| 205 | | dest.write('! ') |
|---|
| 206 | | elif i >= len(statements) or j >= len(missing): |
|---|
| 207 | | dest.write('> ') |
|---|
| 208 | | elif statements[i] == missing[j]: |
|---|
| 209 | | dest.write('! ') |
|---|
| 210 | | else: |
|---|
| 211 | | dest.write('> ') |
|---|
| 212 | | elif lineno in excluded: |
|---|
| 213 | | dest.write('- ') |
|---|
| 214 | | elif covered: |
|---|
| 215 | | dest.write('> ') |
|---|
| | 288 | if line == '': |
|---|
| | 289 | yield ' ' |
|---|
| | 290 | continue |
|---|
| | 291 | if lineno in excluded: |
|---|
| | 292 | template = TEMPLATE_LOC_EXCLUDED |
|---|
| | 293 | elif lineno in missing: |
|---|
| | 294 | template = TEMPLATE_LOC_NOT_COVERED |
|---|