Beispiel #1
0
def diff_coverage_data(data_a, data_b, cfg):
    """
    """
    files_a = set(data_a.get_data().measured_files())
    files_b = set(data_b.get_data().measured_files())
    common_files = files_a.intersection(files_b)

    for fname in common_files:
        a = create_analysis(data_a, fname)
        b = create_analysis(data_b, fname)
        a_has = []
        b_has = []

        if a.statements != b.statements:
            click.echo("{}: statement mismatch".format(fname))
        else:
            for x in a.statements:
                if x in a.missing:
                    if x not in b.missing:
                        b_has.append(x)
                else:
                    if x in b.missing:
                        a_has.append(x)
            if a_has != [] or b_has != []:
                click.echo(
                    "{}: {} vs. {}".format(
                        fname,
                        click.style(','.join([str(s) for s in a_has]), fg='red'),
                        click.style(','.join([str(s) for s in b_has]), fg='green'),
                    )
                )
Beispiel #2
0
def next(ctx, ignore, line_numbers, fnames):
    """
    Display the next uncovered chunk.

    This finds the next file that has some uncovered lines and then
    runs:

       cuv lessopen <filename> | less -p \u258c -j 4

    ..which means you can keep pressing 'n' to see the next uncovered
    line. If you include an argument, only files with that arg in the
    name are considered as "next".
    """
    cfg = ctx.obj
    anything_found = False
    for fname in sorted(cfg.measured_filenames()):
        if any([ign in fname for ign in ignore]):
            continue
        if fnames and not any([fn in fname for fn in fnames]):
            continue
        anything_found = True
        data = create_analysis(cfg.data, fname)
        if data.missing:
            subprocess.call(
                u'cuv lessopen {} | less -R {} -p \u258c -j 4'.format(
                    fname, '-N' if line_numbers else ''),
                shell=True,
            )
            return
    if fnames and not anything_found:
        click.echo(u"No filenames matched any keywords: {}".format(
            ', '.join(fnames)))
Beispiel #3
0
def graph_coverage(keywords, cfg):
    file_coverage = []

    start_time = time.time()
    file_coverage = list(cfg.measured_filenames(keywords))
    file_coverage.sort()
    diff = time.time() - start_time

    common = file_coverage[0]
    for fname in file_coverage[1:]:
        common = ''.join([x[0] for x in zip(common, fname) if x[0] == x[1]])

    click.echo("Coverage in: {}".format(common))

    common = len(common)

    if True:
        lines_per_col = 8
    else:
        max_statements = max([len(d.statements) for name, d in file_coverage])
        lines_per_col = math.ceil(float(max_statements) / float(graph_width))
        if lines_per_col < 8:
            lines_per_col = 8

    max_fname = max([len(nm) - common for nm in file_coverage])
    width = click.get_terminal_size()[0]
    graph_width = width - 5 - max_fname

    def percent_to_bar(prct):
        # 0x2581 is _ like bar
        # 0x2588 is completely solid
        if prct < 0.125:
            return click.style(u' ', fg='red', bg='green')
        return click.style(
            six.unichr(0x2580 + int(prct / 0.125)), fg='red', bg='green'
        )

    last_fname = None
    last_prefix = 0
    for fname in file_coverage:
        try:
            data = create_analysis(cfg.data, fname)
        except Exception as e:
            click.echo(u"error: {}: {}".format(fname, e))
        short = fname[common:]
        graph = ''
        glyphs = 0
        printed_fname = False
        bad = total = 0
        percent = 100.0  # if no statements, it's all covered, right?
        if data.statements:
            percent = (1.0 - (len(data.missing) / float(len(data.statements)))) * 100.0
        if last_fname is not None:
            last_prefix = 0
            for (a, b) in zip(last_fname.split('/'), short.split('/')):
                if a != b:
                    break
                last_prefix += len(a) + 1
            if last_prefix > 0:
                last_prefix -= 1
        last_fname = short

        for statement in data.statements:
            total += 1
            if statement in data.missing:
                bad += 1
            if total == lines_per_col:
                graph += percent_to_bar(float(bad) / float(total))
                glyphs += 1
                bad = total = 0

            if glyphs >= graph_width:
                if printed_fname:
                    click.echo(u'{} {}'.format(u' ' * max_fname, graph), color=True)
                else:
                    printed_fname = True
                    thisname = (u' ' * last_prefix) + short[last_prefix:]
                    thisname = click.style(fname[common:common + last_prefix], fg='black') + click.style(short[last_prefix:], bold=True)
                    click.echo(
                        u'{}{} {} {}'.format(
                            thisname,
                            u' ' * (max_fname - len(short)),
                            click.style(
                                u'{:3d}'.format(int(percent)),
                                fg='red' if percent < 60 else 'magenta' if percent < 80 else 'green',
                            ),
                            graph,
                        ),
                        color=True,
                    )
                    last_prefix = 0
                graph = ''
                glyphs = 0

        if total > 0:
            graph += percent_to_bar(float(bad) / float(total))
            glyphs += 1

        if glyphs == 0:
            graph = click.style('no statements', dim=True, fg='black')

        if printed_fname:
            click.echo(u'{} {}'.format(u' ' * max_fname, graph), color=True)
        else:
            printed_fname = True
            thisname = (u' ' * last_prefix) + short[last_prefix:]
            thisname = click.style(fname[common:common + last_prefix], fg='black') + click.style(short[last_prefix:], bold=True)
            click.echo(
                u'{}{} {} {}'.format(
                    thisname,
                    u' ' * (max_fname - len(short)),
                    click.style(
                        u'{:3d}'.format(int(percent)),
                        fg='red' if percent < 60 else 'magenta' if percent < 80 else 'green',
                    ),
                    graph,
                ),
                color=True,
            )
        graph = ''
        glyphs = 0
Beispiel #4
0
def pixel_vis(cfg, pixel_size, height, show_image):
    target_fname = 'coverage_pixel.png'
    randomize_brightness = False
    show_comments = True
    header_size = 8
    column_chars = 80
    maximum_height = height
    biggest_prefix = None
    total_statements = 0
    total_missing = 0
    total_files = 0
    total_covered = 0
    frmt = None

    coverage_data = []
    for fname in cfg.measured_filenames():
        try:
            covdata = create_analysis(cfg.data, fname)
        except Exception as e:
            click.echo("{}: {}".format(fname, e))
            continue

        if biggest_prefix is None:
            biggest_prefix = fname
        else:
            for (i, ch) in enumerate(fname[:len(biggest_prefix)]):
                if ch != biggest_prefix[i]:
                    biggest_prefix = biggest_prefix[:i]
                    break

        if len(covdata.statements) == 0:
            continue

        # can't we derive this info. from stuff in coverage?
        # "statements"/n_statements isn't every line
        if show_comments:
            lines = len(open(covdata.fname).readlines())
        else:
            # actually, ignoring comments etc seems more-sane sometimes
            # ...but then the code doesn't "look" right :/
            lines = len(covdata.statements)

        total_statements += lines
        total_missing += len(covdata.missing)
        total_files += 1
        total_covered += (len(covdata.statements) - len(covdata.missing))
        coverage_data.append((fname, covdata, lines))

    fnt = ImageFont.truetype(
        pkg_resources.resource_filename("cuv", "source-code-pro.ttf"),
        header_size,
    )
    fnt_height = fnt.getsize('0')[1]

    column_width = (column_chars * pixel_size[0]) + 2

    lines_per_column = maximum_height / pixel_size[1]
    total_pixel_height = (pixel_size[1] * total_statements) + (len(coverage_data) * (fnt_height + 2))

    num_columns = int(math.ceil(total_pixel_height / float(maximum_height)))
    click.echo(u"need {} columns for {} lines".format(num_columns, total_statements))
    # 3 for vertical 'total-coverage' bar on left
    width = (num_columns * column_width) + 3
    height = maximum_height
    click.echo(u"size: {}x{}".format(width, height))
    img = Image.new('RGBA', (width, height), (0, 0, 0, 255))
    draw = ImageDraw.Draw(img)

    if False:
        # do the biggest data first
        coverage_data.sort(lambda a, b: cmp(len(a[1].statements), len(b[1].statements)))
        coverage_data.reverse()

    from pygments import highlight
    from pygments.token import Token
    from pygments.lexers import get_lexer_by_name
    from pygments.formatter import Formatter

    def parse_color(col):
        r = int(col[:2], 16)
        g = int(col[2:4], 16)
        b = int(col[4:6], 16)
        return (r, g, b, 64)

    class Position(object):
        def __init__(self, x=0, y=0):
            self.x = x
            self.y = y
    pos = Position(3, 0)

    class ImageFormatter(Formatter):

        def __init__(self, draw, covdata, origin, **kw):
            Formatter.__init__(self, **kw)
            self.draw = draw
            self.covdata = covdata
            self.origin = origin

            #: maps Token -> r,g,b triple
            self.token_color = {}

            for (token, style) in self.style:
                if 'color' in style and style['color']:
                    c = parse_color(style['color'])
                    self.token_color[token] = c

        def draw_line_background(self, line_index, alpha=255):
            bg = (32, 32, 32, alpha)
            if (line_index + 1) in self.covdata.missing:
                bg = (164, 32, 32, alpha)
#            elif (line_index + 1) in self.covdata.branch_lines():
#                bg = (128, 128, 32, alpha)
            elif (line_index + 1) in self.covdata.statements:
                bg = (32, 44, 32, alpha)
            self.draw.rectangle((self.origin.x, pos.y, self.origin.x + column_width - 2, pos.y + pixel_size[1]), fill=bg)
            return bg

        def format(self, tokensource, outfile):
            index = 0
            bg = self.draw_line_background(index)

            for ttype, value in tokensource:
                try:
                    color = self.token_color[ttype]
                except KeyError:
                    color = (255, 255, 0, 255)

                text = u'{}'.format(value)

                fg_amt = 1
                bg_amt = 7
                color = (
                    int(((bg[0] * bg_amt) + (color[0] * fg_amt)) / (fg_amt + bg_amt)),
                    int(((bg[1] * bg_amt) + (color[1] * fg_amt)) / (fg_amt + bg_amt)),
                    int(((bg[2] * bg_amt) + (color[2] * fg_amt)) / (fg_amt + bg_amt)),
                    255
                )

                for snip in text.splitlines(True):
                    self.draw_text(snip, color, index)
                    assert snip.count('\n') <= 1
                    if '\n' in snip:
                        index += 1
                        bg = self.draw_line_background(index)

        def draw_text(self, text, color, line_index):
            assert text.count('\n') <= 1
            for ch in text:
                if pos.x >= self.origin.x + column_width:
                    pos.x = self.origin.x
                    break
                if ch not in ' \t\n\r':
                    if randomize_brightness:
                        dimness = 16 - random.randrange(32)
                        c = [x + dimness for x in color]
                        c[3] = 255
                        c = tuple(c)
                    else:
                        c = color
                    self.draw.rectangle((pos.x, pos.y, pos.x + pixel_size[0], pos.y + pixel_size[1]), fill=c)
                pos.x += pixel_size[0]
            # we ensure there's at most one newline in "format"
            if u'\n' in text:
                pos.y += pixel_size[1]
                pos.x = self.origin.x
            if pos.y > maximum_height:
                pos.y = 0
                pos.x = self.origin.x + column_width
                self.origin = Position(pos.x, pos.y)

    column = row = 0
    for (fname, covdata, lines) in coverage_data:
        percent = float(lines - len(covdata.missing)) / lines
        barmax = column_width - 2
        pwid = math.ceil(barmax * percent)

        if True:
            pos.y += pixel_size[1] + 1
            draw.rectangle([pos.x, pos.y, pos.x + pwid, pos.y + fnt_height - 1], fill=(32, 96, 32, 255))
            draw.rectangle([pos.x + pwid, pos.y, pos.x + barmax, pos.y + fnt_height - 1], fill=(96, 32, 32, 255))

        trunc_fname = fname
        while fnt.getsize(trunc_fname)[0] > column_width:
            trunc_fname = trunc_fname[1:]
        text_x = column_width - fnt.getsize(trunc_fname)[0]  # what if negative?
        text_x += pos.x
        draw.text((text_x, pos.y - 2), trunc_fname, fill=(255, 255, 255, 128), font=fnt)
        pos.y += fnt_height

        if True:
            new_x = pos.x
            if frmt:
                new_x = frmt.origin.x
            frmt = ImageFormatter(draw, covdata, Position(new_x, pos.y), style='monokai')
            highlight(open(fname, 'r').read(), get_lexer_by_name('python'), formatter=frmt)

    prct = float(total_statements - total_missing) / total_statements
    click.echo(u"total coverage: {}%".format(int(prct * 100)))
    prct_y = height * prct
    draw.rectangle([0, 0, 1, prct_y], fill=(0, 200, 0, 255))
    draw.rectangle([0, prct_y + 1, 1, height], fill=(200, 0, 0, 255))
    fname = 'coverage_cascade_pixel.png'
    img.save(fname)
    click.echo(u"wrote '{}'".format(fname))
    if show_image:
        img.show()
Beispiel #5
0
def graph_coverage(keywords, cfg):
    file_coverage = []

    start_time = time.time()
    file_coverage = list(cfg.measured_filenames(keywords))
    file_coverage.sort()
    diff = time.time() - start_time

    common = len(common_root_path(file_coverage))

    if True:
        lines_per_col = 8
    else:
        max_statements = max([len(d.statements) for name, d in file_coverage])
        lines_per_col = math.ceil(float(max_statements) / float(graph_width))
        if lines_per_col < 8:
            lines_per_col = 8

    max_fname = max([len(nm) - common for nm in file_coverage])
    width = click.get_terminal_size()[0]
    graph_width = width - 13 - max_fname

    def percent_to_bar(prct):
        # 0x2581 is _ like bar
        # 0x2588 is completely solid
        if prct < 0.125:
            return click.style(u' ', fg='red', bg='green')
        return click.style(six.unichr(0x2580 + int(prct / 0.125)),
                           fg='red',
                           bg='green')

    last_fname = None
    last_prefix = 0

    format_str = u'{:^%d} percent missing' % (max_fname + len('filename') -
                                              4, )
    click.echo(format_str.format(click.style('filename', bold=True)))

    total_lines = 0
    total_missing = 0

    for fname in file_coverage:
        try:
            data = create_analysis(cfg.data, fname)
        except Exception as e:
            click.echo(u"error: {}: {}".format(fname, e))
        short = fname[common:]
        graph = ''
        glyphs = 0
        printed_fname = False
        bad = total = 0
        percent = 100.0  # if no statements, it's all covered, right?
        if data.statements:
            percent = (
                1.0 -
                (len(data.missing) / float(len(data.statements)))) * 100.0
        if last_fname is not None:
            last_prefix = 0
            for (a, b) in zip(last_fname.split('/'), short.split('/')):
                if a != b:
                    break
                last_prefix += len(a) + 1
            if last_prefix > 0:
                last_prefix -= 1
        last_fname = short

        total_lines += len(data.statements)
        total_missing += len(data.missing)

        # compute each glyph's total (i.e. each chunk of ~8 lines)
        for statement in data.statements:
            total += 1
            if statement in data.missing:
                bad += 1
            if total == lines_per_col:
                graph += percent_to_bar(float(bad) / float(total))
                glyphs += 1
                bad = total = 0

            if glyphs >= graph_width:
                if printed_fname:
                    click.echo(u'{} {}'.format(u' ' * max_fname, graph),
                               color=True)
                else:
                    printed_fname = True
                    thisname = (u' ' * last_prefix) + short[last_prefix:]
                    thisname = click.style(fname[common:common + last_prefix],
                                           fg='black') + click.style(
                                               short[last_prefix:], bold=True)

                    # XXX also, all this duplicated code :(
                    if len(data.missing) > 0:
                        nice_missing = u' ({})'.format(
                            click.style(u'{:5d}'.format(-len(data.missing)),
                                        fg='red'))
                    else:
                        nice_missing = u' ' * 8
                    # XXX unit-tests!! this only gets hit on really-long files
                    click.echo(
                        u'{}{} {}{} {}'.format(
                            thisname,
                            u' ' * (max_fname - len(short)),
                            click.style(
                                u'{:3d}'.format(int(percent)),
                                fg='red' if percent < 60 else
                                'magenta' if percent < 80 else 'green',
                            ),
                            nice_missing,
                            graph,
                        ),
                        color=True,
                    )
                    last_prefix = 0
                graph = ''
                glyphs = 0

        if total > 0:
            graph += percent_to_bar(float(bad) / float(total))
            glyphs += 1

        if glyphs == 0:
            graph = click.style('no statements', dim=True, fg='black')

        if printed_fname:
            click.echo(u'{} {}'.format(u' ' * (max_fname + 12), graph),
                       color=True)
        else:
            printed_fname = True
            thisname = (u' ' * last_prefix) + short[last_prefix:]
            thisname = click.style(fname[common:common + last_prefix],
                                   fg='black') + click.style(
                                       short[last_prefix:], bold=True)
            # XXX code nearly identical to this above...
            if len(data.missing) > 0:
                nice_missing = u' ({})'.format(
                    click.style(u'{:5d}'.format(-len(data.missing)), fg='red'))
            else:
                nice_missing = u' ' * 8

            click.echo(
                u'{}{} {}{} {}'.format(
                    thisname,
                    u' ' * (max_fname - len(short)),
                    click.style(
                        u'{:3d}'.format(int(percent)),
                        fg='red' if percent < 60 else
                        'magenta' if percent < 80 else 'green',
                    ),
                    #click.style(u' ({:5d})'.format(-len(data.missing)), fg='red'),
                    nice_missing,
                    graph,
                ),
                color=True,
            )
        graph = ''
        glyphs = 0

    covered_percent = (float(total_lines - total_missing) /
                       total_lines) * 100.0
    uncovered_percent = (float(total_missing) / total_lines) * 100.0
    click.echo(u'From {} files: {} total lines, {} missing ({} {})'.format(
        len(file_coverage),
        click.style(str(total_lines), fg='green'),
        click.style(str(total_missing), fg='red'),
        click.style('{:3.3}%'.format(covered_percent), fg='green'),
        click.style('{:3.3}%'.format(uncovered_percent), fg='red'),
    ))