| 1 |
{% extends "base.html" %} |
| 2 |
{% import "repo_tabs.html" as m %} |
| 3 |
{% block title %}Coverage · {{ repo.owner }}/{{ repo.name }}{% endblock %} |
| 4 |
{% block tabs %}{% call m::tabs(repo, "coverage") %}{% endblock %} |
| 5 |
{% block content %} |
| 6 |
{% if let Some(r) = report %} |
| 7 |
<section class="cov-hero"> |
| 8 |
<div class="cov-hero-head"> |
| 9 |
<div> |
| 10 |
<h1>Coverage</h1> |
| 11 |
<div class="cov-sub">{% if bookmarks.len() > 1 %}<form class="inline" method="get" action="{{ repo.url }}/coverage"><label for="cov-bm" class="mute">bookmark</label> <select id="cov-bm" name="bookmark" onchange="this.form.submit()">{% for b in bookmarks %}<option value="{{ b }}"{% if b.as_str() == bookmark.as_str() %} selected{% endif %}>{{ b }}</option>{% endfor %}</select> <noscript><button>Show</button></noscript></form>{% else %}<span class="mute">on</span> <strong>{{ bookmark }}</strong>{% endif %} |
| 12 |
· <a href="{{ repo.url }}/flow/{{ r.run_number }}">run #{{ r.run_number }}</a> · <time title="{{ r.created_ms|when_i }}">{{ r.created_ms|ago_i }}</time></div> |
| 13 |
</div> |
| 14 |
<div class="cov-score {{ self.class_of(r.percent()) }}"> |
| 15 |
<div class="cov-big">{{ self.pct(r.percent()) }}</div> |
| 16 |
{% if let Some(d) = delta %}<div class="cov-change {{ self.delta_class(d) }}" title="Against run #{% if let Some(p) = previous %}{{ p.run_number }}{% endif %}">{{ self.signed(d) }} since the run before</div>{% else %}<div class="cov-change same">the first report here</div>{% endif %} |
| 17 |
</div> |
| 18 |
</div> |
| 19 |
{% if runs > 1 %}<div class="cov-hero-chart">{{ coverage_chart|safe }}</div>{% endif %} |
| 20 |
</section> |
| 21 |
|
| 22 |
<section class="cov-facts"> |
| 23 |
<div><span class="k">Lines covered</span><span class="v">{{ r.lines_hit }} <small>of {{ r.lines_found }}</small></span></div> |
| 24 |
<div><span class="k">Hits per line</span><span class="v">{{ self.hits(r) }}</span></div> |
| 25 |
{% if r.functions_found > 0 %}<div><span class="k">Functions</span><span class="v">{{ self.pct(r.functions_percent()) }} <small>{{ r.functions_hit }} of {{ r.functions_found }}</small></span></div>{% endif %} |
| 26 |
{% if r.branches_found > 0 %}<div><span class="k">Branches</span><span class="v">{{ self.pct(r.branches_percent()) }} <small>{{ r.branches_hit }} of {{ r.branches_found }}</small></span></div>{% endif %} |
| 27 |
<div><span class="k">Files</span><span class="v">{{ r.files }}</span></div> |
| 28 |
{% if let Some(t) = first_ms %}<div><span class="k">Reporting since</span><span class="v small"><time title="{{ t|when_i }}">{{ t|ago_i }}</time></span></div>{% endif %} |
| 29 |
<div><span class="k">Badge</span><span class="v"><img src="{{ repo.url }}/coverage/badge.svg" alt="coverage {{ self.pct(r.percent()) }}" class="cov-badge"><button type="button" class="cov-copy" data-copy="[]({{ shell.base_url }}{{ repo.url }}/coverage)" title="Copy the Markdown for a README">copy Markdown</button></span></div> |
| 30 |
</section> |
| 31 |
|
| 32 |
<div class="cov-grid"> |
| 33 |
<section class="panel cov-card"> |
| 34 |
<h2>This run</h2> |
| 35 |
<ul class="cov-stats"> |
| 36 |
<li><strong>{{ r.lines_hit }}</strong> of <strong>{{ r.lines_found }}</strong> relevant lines covered <span class="{{ self.class_of(r.percent()) }}">({{ self.pct(r.percent()) }})</span></li> |
| 37 |
{% if let Some((found, hit)) = self.lines_delta() %}<li>{% if *found >= 0 %}+{% endif %}{{ found }} relevant lines, {% if *hit >= 0 %}+{% endif %}{{ hit }} covered, against run #{% if let Some(p) = previous %}{{ p.run_number }}{% endif %}</li>{% endif %} |
| 38 |
<li><strong>{{ self.hits(r) }}</strong> hits per line</li> |
| 39 |
<li>Job <strong>{{ r.job }}</strong>{% if r.dropped > 0 %}; {{ r.dropped }} files the report named are not in this state and are left out{% endif %}</li> |
| 40 |
{% if added > 0 %}<li>{{ added }} new files</li>{% endif %} |
| 41 |
</ul> |
| 42 |
{% if !moved.is_empty() %}<h3>Coverage moved in</h3> |
| 43 |
<table class="cov-moved">{% for f in moved %}<tr><td><a href="{{ repo.url }}/coverage/{{ r.id }}/file/{{ f.path }}">{{ f.path }}</a></td><td class="num mute">{{ self.pct_of(f.before) }} → {{ self.pct_of(f.after) }}</td><td class="num cov-change {{ self.delta_class(&f.delta) }}">{{ self.signed(&f.delta) }}</td></tr>{% endfor %}</table>{% endif %} |
| 44 |
</section> |
| 45 |
<section class="panel cov-card"> |
| 46 |
<h2>Files by coverage</h2> |
| 47 |
<div class="cov-bands">{% for b in bands %}<div class="cov-band"><span class="cov-band-lbl">{{ b.label }}</span><span class="bar {{ b.class }}"><span style="width: {{ b.width }}%"></span></span><span class="n">{{ b.files }}</span></div>{% endfor %}</div> |
| 48 |
</section> |
| 49 |
</div> |
| 50 |
|
| 51 |
{% if runs > 1 %}<section class="panel"> |
| 52 |
<h2>Relevant lines covered <small class="mute"><span class="cov-key covered">covered</span> <span class="cov-key relevant">missed</span></small></h2> |
| 53 |
{{ lines_chart|safe }} |
| 54 |
</section>{% endif %} |
| 55 |
|
| 56 |
{% if recent.len() > 1 %}<section class="panel"> |
| 57 |
<h2>Recent runs on {{ bookmark }}</h2> |
| 58 |
<div class="table"><table class="cov-runs"> |
| 59 |
<tr><th>Run</th><th>Job</th><th>Coverage</th><th class="num">Change</th><th class="num">Lines</th><th>When</th></tr> |
| 60 |
{% for x in recent %}<tr{% if x.report.id == r.id %} class="on"{% endif %}> |
| 61 |
<td><a href="{{ repo.url }}/coverage?report={{ x.report.id }}">#{{ x.report.run_number }}</a></td> |
| 62 |
<td>{{ x.report.job }}</td> |
| 63 |
<td><span class="cov-bar {{ self.class_of(x.report.percent()) }}"><span style="width: {% if let Some(p) = x.report.percent() %}{{ p }}{% else %}0{% endif %}%"></span></span> {{ self.pct(x.report.percent()) }}</td> |
| 64 |
<td class="num">{% if let Some(d) = x.delta %}<span class="cov-change {{ self.delta_class(d) }}">{{ self.signed(d) }}</span>{% endif %}</td> |
| 65 |
<td class="num">{{ x.report.lines_hit }} / {{ x.report.lines_found }}</td> |
| 66 |
<td class="small"><time title="{{ x.report.created_ms|when_i }}">{{ x.report.created_ms|ago_i }}</time></td> |
| 67 |
</tr>{% endfor %} |
| 68 |
</table></div> |
| 69 |
</section>{% endif %} |
| 70 |
|
| 71 |
<section class="panel"> |
| 72 |
<h2>Files <small class="mute">sorted by {% if sort == "missed" %}lines missed{% else if sort == "percent" %}coverage, lowest first{% else %}path{% endif %}: |
| 73 |
{% if sort != "missed" %}<a href="?report={{ r.id }}&sort=missed">lines missed</a> {% endif %}{% if sort != "percent" %}<a href="?report={{ r.id }}&sort=percent">lowest first</a> {% endif %}{% if sort != "path" %}<a href="?report={{ r.id }}&sort=path">path</a>{% endif %}</small></h2> |
| 74 |
<div class="table"><table class="cov-files"> |
| 75 |
<tr><th>File</th><th>Coverage</th><th class="num">Lines</th><th class="num">Missed</th></tr> |
| 76 |
{% for f in files %}<tr> |
| 77 |
<td><a href="{{ repo.url }}/coverage/{{ r.id }}/file/{{ f.path }}">{{ f.path }}</a></td> |
| 78 |
<td><span class="cov-bar {{ self.class_of(f.percent()) }}" role="img" aria-label="{{ self.pct(f.percent()) }}"><span style="width: {% if let Some(p) = f.percent() %}{{ p }}{% else %}0{% endif %}%"></span></span> {{ self.pct(f.percent()) }}</td> |
| 79 |
<td class="num">{{ f.lines_found }}</td> |
| 80 |
<td class="num">{{ f.missed() }}</td> |
| 81 |
</tr>{% endfor %} |
| 82 |
</table></div> |
| 83 |
</section> |
| 84 |
{% else %} |
| 85 |
<div class="head"><h1>Coverage <small>of {{ bookmark }}</small></h1></div> |
| 86 |
<section class="panel"> |
| 87 |
<p>No coverage report for <strong>{{ bookmark }}</strong> yet.</p> |
| 88 |
<p>Give a job in <code>.flow/pipeline.kdl</code> an artifact named <code>coverage</code> holding an LCOV report, and the hub reads it when the job uploads it. For Rust:</p> |
| 89 |
<pre>job "coverage" { |
| 90 |
sandbox "rust" |
| 91 |
exec "cargo" "llvm-cov" "--workspace" "--lcov" "--output-path" "/src/target/lcov.info" |
| 92 |
output { |
| 93 |
artifact "coverage" { path "/src/target/lcov.info" } |
| 94 |
} |
| 95 |
}</pre> |
| 96 |
<p class="small mute">Istanbul (<code>--reporter=lcov</code>), coverage.py (<code>coverage lcov</code>) and gcov with lcov write LCOV too. Paths are read relative to the job's <code>/src</code>.</p> |
| 97 |
</section> |
| 98 |
{% endif %} |
| 99 |
{% endblock %} |