Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

Ticket #262: improve_covercp.patch

  • cherrypy/lib/covercp.py

    old new  
    4141If you run this module from the command line, it will call serve() for you. 
    4242""" 
    4343 
     44TEMPLATE_MENU = """<html> 
     45<head> 
     46    <title>CherryPy Coverage Menu</title> 
     47    <style> 
     48        body {font: 9pt Arial, serif;} 
     49        #tree {font: 8pt Courier, sans-serif;} 
     50        #tree a:active, a:focus { 
     51            background-color: #EEEEFF; 
     52            padding: 1px; 
     53            border: 1px solid #9999FF; 
     54            -moz-outline-style: none; 
     55        } 
     56        .fail {color: red;} 
     57        .pass {color: #888;} 
     58        #pct {text-align: right;} 
     59        .inputheader { font-size: small; font-weight: bold; font-style: italic; margin-top: 5px;} 
     60        input { border: 1px solid #ccc; padding: 2px; } 
     61    </style> 
     62</head> 
     63<body> 
     64<h2>CherryPy Coverage</h2>""" 
     65 
     66TEMPLATE_FORM = """ 
     67<form action='menu' method=GET> 
     68    <input type='hidden' name='base' value='%(base)s' /> 
     69 
     70    <div class="inputheader">Options </div> 
     71    Hide <input type='text' id='hide_depth' name='hide_depth' value='%(hide_depth)s' size='3' /> 
     72    parent directories<br /> 
     73    <input type='checkbox' %(showpct)s name='showpct' value='checked'/> show 
     74    percentages <br /> 
     75    Hide files over <input type='text' id='pct' name='pct' value='%(pct)s' size='3' /> 
     76    %% <br /> 
     77    Exclude files matching<br /> 
     78    <input type='text' id='exclude' name='exclude' value='%(exclude)s' size='20' /> 
     79    <br /> 
     80 
     81    <input type='submit' value='Change view' /> 
     82</form>"""  
     83 
     84TEMPLATE_FRAMESET = """<html> 
     85<head><title>CherryPy coverage data</title></head> 
     86<frameset cols='250, 1*'> 
     87    <frame src='menu' /> 
     88    <frame name='main' src='' /> 
     89</frameset> 
     90</html> 
     91""" 
     92 
     93TEMPLATE_COVERAGE = """<html> 
     94<head> 
     95    <title>Coverage for %(name)s</title> 
     96    <style> 
     97        .covered { color: #000; background-color: #fff; } 
     98        .notcovered { color: #fee; background-color: #500; } 
     99        .excluded { color: #00f; background-color: #fff; } 
     100         table .covered, table .notcovered, table .excluded 
     101             { font-family: Andale Mono, monospace; 
     102               font-size: 10px; white-space: pre; } 
     103 
     104         .lineno { background-color: #eee;} 
     105         .notcovered .lineno { background-color: #000;} 
     106         table { border-collapse: collapse; 
     107    </style> 
     108</head> 
     109<body> 
     110<h2>%(name)s</h2> 
     111<p>Coverage: %(pc)s%%</p>""" 
     112 
     113TEMPLATE_LOC_COVERED = '<tr class="covered"><td class="lineno">%s&nbsp;</td><td>%s</td></tr>\n' 
     114TEMPLATE_LOC_NOT_COVERED = '<tr class="notcovered"><td class="lineno">%s&nbsp;</td><td>%s</td></tr>\n' 
     115TEMPLATE_LOC_EXCLUDED = '<tr class="excluded"><td class="lineno">%s&nbsp;</td><td>%s</td></tr>\n' 
     116 
    44117import re 
    45118import sys 
     119import cgi 
    46120import os, os.path 
    47121localFile = os.path.join(os.path.dirname(__file__), "coverage.cache") 
    48122 
     
    51125except ImportError: 
    52126    import StringIO 
    53127 
    54  
    55128try: 
    56129    from coverage import the_coverage as coverage 
    57130    def start(): 
     
    67140    def start(): 
    68141        pass 
    69142 
     143# Guess initial depth to hide FIXME this doesn't work for non-cherrypy stuff 
     144import cherrypy 
     145initial_depth = cherrypy.__file__.count(os.sep) - 1 
    70146 
     147import re 
     148def _skip_file(path, exclude): 
     149    if exclude: 
     150        return bool(re.search(exclude, path)) 
     151 
     152def _percent(statements, missing): 
     153    s = len(statements) 
     154    e = s - len(missing) 
     155    if s > 0: 
     156        return int(round(100.0 * e / s)) 
     157    return 0 
     158 
     159def _show(root, depth=0, path="", pct=0, hide_depth=0, showpct=False): 
     160 
     161    view_depth = depth - hide_depth 
     162 
     163    # Show the directory name and any of our children 
     164    dirs = [k for k, v in root.iteritems() if v is not None] 
     165    dirs.sort() 
     166    for name in dirs: 
     167        if path: 
     168            newpath = os.sep.join((path, name)) 
     169        else: 
     170            newpath = name 
     171         
     172        if view_depth >= 0: 
     173            yield "<nobr>" + ("|&nbsp;" * view_depth) + "<b>" 
     174            yield "<a href='menu?base=%s'>%s</a>" % (newpath, name) 
     175            yield "</b></nobr><br />\n" 
     176 
     177        for chunk in _show(root[name], depth + 1, newpath, pct, 
     178                           hide_depth, showpct): 
     179            yield chunk 
     180     
     181    # Now list the files 
     182    files = [k for k, v in root.iteritems() if v is None] 
     183    files.sort() 
     184    for name in files: 
     185        if path: 
     186            newpath = os.sep.join((path, name)) 
     187        else: 
     188            newpath = name 
     189 
     190        if view_depth < 0: 
     191            continue 
     192         
     193        pc_str = "" 
     194        if showpct: 
     195            try: 
     196                _, statements, _, missing, _ = coverage.analysis2(newpath) 
     197            except: 
     198                # Yes, we really want to pass on all errors. 
     199                pass 
     200            else: 
     201                pc = _percent(statements, missing) 
     202                pc_str = ("%3d%% " % pc).replace(' ','&nbsp;') 
     203                if pc < float(pct) or pc == -1: 
     204                    pc_str = "<span class='fail'>%s</span>" % pc_str 
     205                else: 
     206                    pc_str = "<span class='pass'>%s</span>" % pc_str 
     207 
     208        yield ("<nobr>%s%s<a href='report?name=%s' target='main'>%s</a></nobr><br />\n" 
     209               % ("|&nbsp;" * view_depth, pc_str, newpath, name)) 
     210 
     211 
    71212class CoverStats(object): 
    72213     
    73214    def index(self): 
    74         return """<html> 
    75         <head><title>CherryPy coverage data</title></head> 
    76         <frameset cols='250, 1*'> 
    77             <frame src='menu' /> 
    78             <frame name='main' src='' /> 
    79         </frameset> 
    80         </html> 
    81         """ 
     215        return TEMPLATE_FRAMESET 
    82216    index.exposed = True 
    83217     
    84     def menu(self, base="", pct=""): 
    85         yield """<html> 
    86 <head> 
    87     <title>CherryPy Coverage Menu</title> 
    88     <style> 
    89         body {font: 9pt Arial, serif;} 
    90         #tree {font: 8pt Courier, sans-serif;} 
    91         #tree a:active, a:focus { 
    92             background-color: #EEEEFF; 
    93             padding: 1px; 
    94             border: 1px solid #9999FF; 
    95             -moz-outline-style: none; 
    96         } 
    97         .fail {color: red;} 
    98         #pct {text-align: right;} 
    99     </style> 
    100 </head> 
    101 <body> 
    102 <h2>CherryPy Coverage</h2>""" 
    103          
     218    def menu(self, base="", pct="50", showpct="", 
     219             exclude=r'python\d\.\d|test|tut\d|tutorial', 
     220             hide_depth=str(initial_depth)): 
     221 
     222        yield TEMPLATE_MENU        
    104223        coverage.get_ready() 
    105224        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") 
     225        if not runs: 
     226            yield "<p>No modules covered.</p>" 
     227        else: 
     228            yield TEMPLATE_FORM % locals() 
    112229             
    113230            yield "<div id='tree'>" 
    114231            tree = {} 
    115232            def graft(path): 
    116233                b, n = os.path.split(path) 
    117                 if n: 
    118                     return graft(b).setdefault(n, {}) 
    119                 else: 
    120                     return tree.setdefault(b.strip(r"\/"), {}) 
     234                if n: return graft(b).setdefault(n, {}) 
     235                else: return tree.setdefault(b.strip(r"\/"), {}) 
    121236            for path in runs: 
    122                 if not os.path.isdir(path): 
     237                if not _skip_file(path, exclude) and not os.path.isdir(path): 
    123238                    b, n = os.path.split(path) 
    124239                    if b.startswith(base): 
    125240                        graft(b)[n] = None 
    126              
    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>" + ("|&nbsp;" * 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 = "&nbsp;" + pc_str 
    165                                     if pc < 10: 
    166                                         pc_str = "&nbsp;" + 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                            % ("|&nbsp;" * depth, pc_str, newpath, name)) 
    171              
    172             for chunk in show(tree): 
     241            for chunk in _show(tree, 0, '/', pct, int(hide_depth), 
     242                               showpct=='checked'): 
    173243                yield chunk 
    174244             
    175245            yield "</div>" 
    176         else: 
    177             yield "<p>No modules covered.</p>" 
    178246        yield "</body></html>" 
    179247    menu.exposed = True 
    180248     
    181249    def annotated_file(self, filename, statements, excluded, missing): 
    182250        source = open(filename, 'r') 
    183         dest = StringIO.StringIO() 
    184251        lineno = 0 
    185         i = 0 
    186         j = 0 
    187         covered = 1 
    188252        while 1: 
    189253            line = source.readline() 
    190254            if line == '': 
    191255                break 
     256            line = line[:-1] 
    192257            lineno = lineno + 1 
    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('> ') 
     258            if line == '': 
     259                yield '&nbsp;' 
     260                continue 
     261            if lineno in excluded: 
     262                template = TEMPLATE_LOC_EXCLUDED 
     263            elif lineno in missing: 
     264                template = TEMPLATE_LOC_NOT_COVERED 
    216265            else: 
    217                 dest.write('! ') 
    218             dest.write(line) 
    219         source.close() 
    220         result = dest.getvalue() 
    221         dest.close() 
    222         return result 
     266                template = TEMPLATE_LOC_COVERED 
     267            yield template % (lineno, cgi.escape(line)) 
    223268     
    224269    def report(self, name): 
    225         import cherrypy 
    226         cherrypy.response.headerMap['Content-Type'] = 'text/plain' 
    227          
    228         yield name 
    229         yield "\n" 
    230          
    231270        coverage.get_ready() 
    232271        filename, statements, excluded, missing, _ = coverage.analysis2(name) 
    233         s = len(statements) 
    234         e = s - len(missing) 
    235         if s > 0: 
    236             pc = 100.0 * e / s 
    237             yield "%2d%% covered\n" % pc 
    238          
    239         yield "\n" 
    240         yield self.annotated_file(filename, statements, excluded, missing) 
     272        pc = _percent(statements, missing) 
     273        yield TEMPLATE_COVERAGE % dict(name=os.path.basename(name), pc=pc) 
     274        yield '<table>\n' 
     275        for line in self.annotated_file(filename, statements, excluded, 
     276                                        missing): 
     277            yield line 
     278        yield '</table>' 
     279        yield '</body>' 
     280        yield '</html>' 
    241281    report.exposed = True 
    242282 
    243283 
     
    250290    cherrypy.root = CoverStats() 
    251291    cherrypy.config.update({'server.socketPort': port, 
    252292                            'server.threadPool': 10, 
    253                             'server.environment': "production", 
     293                            'server.environment': "development", 
    254294                            }) 
    255295    cherrypy.server.start() 
    256296 
    257  
    258297if __name__ == "__main__": 
    259298    serve(*tuple(sys.argv[1:])) 
    260299 

Hosted by WebFaction

Log in as guest/cpguest to create tickets