Skip to content

Commit 3a33581

Browse files
committed
coverage: Replace color terminal tests with HTML output tests
1 parent 1eb882e commit 3a33581

File tree

10 files changed

+434
-87
lines changed

10 files changed

+434
-87
lines changed

src/tools/compiletest/src/common.rs

+2
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ pub const UI_EXTENSIONS: &[&str] = &[
726726
UI_STDERR_32,
727727
UI_STDERR_16,
728728
UI_COVERAGE,
729+
UI_COVERAGE_HTML,
729730
UI_COVERAGE_MAP,
730731
];
731732
pub const UI_STDERR: &str = "stderr";
@@ -739,6 +740,7 @@ pub const UI_STDERR_64: &str = "64bit.stderr";
739740
pub const UI_STDERR_32: &str = "32bit.stderr";
740741
pub const UI_STDERR_16: &str = "16bit.stderr";
741742
pub const UI_COVERAGE: &str = "coverage";
743+
pub const UI_COVERAGE_HTML: &str = "coverage.html";
742744
pub const UI_COVERAGE_MAP: &str = "cov-map";
743745

744746
/// Absolute path to the directory where all output for all tests in the given

src/tools/compiletest/src/runtest.rs

+51-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc};
99
use crate::common::{CompareMode, FailMode, PassMode};
1010
use crate::common::{Config, TestPaths};
1111
use crate::common::{CoverageMap, CoverageRun, Pretty, RunPassValgrind};
12-
use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP, UI_RUN_STDERR, UI_RUN_STDOUT};
12+
use crate::common::{UI_COVERAGE, UI_COVERAGE_HTML, UI_COVERAGE_MAP, UI_RUN_STDERR, UI_RUN_STDOUT};
1313
use crate::compute_diff::{write_diff, write_filtered_diff};
1414
use crate::errors::{self, Error, ErrorKind};
1515
use crate::header::TestProps;
@@ -579,13 +579,18 @@ impl<'test> TestCx<'test> {
579579
self.fatal_proc_rec("llvm-cov show failed!", &proc_res);
580580
}
581581

582-
let kind = UI_COVERAGE;
582+
let is_html = self.props.llvm_cov_flags.iter().any(|s| s.contains("-format=html"));
583+
let kind = if is_html { UI_COVERAGE_HTML } else { UI_COVERAGE };
583584

584585
let expected_coverage = self.load_expected_output(kind);
585-
let normalized_actual_coverage =
586-
self.normalize_coverage_output(&proc_res.stdout).unwrap_or_else(|err| {
586+
587+
let normalized_actual_coverage = if is_html {
588+
self.normalize_coverage_html(&proc_res.stdout)
589+
} else {
590+
self.normalize_coverage_text(&proc_res.stdout).unwrap_or_else(|err| {
587591
self.fatal_proc_rec(&err, &proc_res);
588-
});
592+
})
593+
};
589594

590595
let coverage_errors =
591596
self.compare_output(kind, &normalized_actual_coverage, &expected_coverage);
@@ -703,17 +708,52 @@ impl<'test> TestCx<'test> {
703708
proc_res
704709
}
705710

706-
fn normalize_coverage_output(&self, coverage: &str) -> Result<String, String> {
707-
let normalized = self.normalize_output(coverage, &[]);
708-
let normalized = Self::anonymize_coverage_line_numbers(&normalized);
711+
fn normalize_coverage_text(&self, coverage: &str) -> Result<String, String> {
712+
let coverage = self.normalize_output(coverage, &[]);
713+
let coverage = Self::anonymize_coverage_line_numbers(&coverage);
709714

710-
let mut lines = normalized.lines().collect::<Vec<_>>();
715+
let mut lines = coverage.lines().collect::<Vec<_>>();
711716

712717
Self::sort_coverage_file_sections(&mut lines)?;
713718
Self::sort_coverage_subviews(&mut lines)?;
714719

715-
let joined_lines = lines.iter().flat_map(|line| [line, "\n"]).collect::<String>();
716-
Ok(joined_lines)
720+
let coverage = lines.iter().flat_map(|line| [line, "\n"]).collect::<String>();
721+
Ok(coverage)
722+
}
723+
724+
fn normalize_coverage_html(&self, coverage: &str) -> String {
725+
let coverage = self.normalize_output(&coverage, &[]);
726+
727+
// HTML coverage reports are produced by a known tool from input we control,
728+
// so we can get away with using simple regexes and string replacement.
729+
730+
// Replace line number hyperlinks with the placeholder `LL`,
731+
// so that the tests are less sensitive to lines being added/removed.
732+
// (We match a lot of context so that we don't scrub execution counts.)
733+
static LINE_NUMBER: Lazy<Regex> = Lazy::new(|| {
734+
let re = r"(<td class='line-number'><a) name='L\d+' href='#L\d+'(><pre>)\d+(</pre></a></td>)";
735+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ LL ^^^^^^^^^^^^^^^
736+
Regex::new(re).unwrap()
737+
});
738+
let coverage = LINE_NUMBER.replace_all(&coverage, "${1}${2}LL${3}");
739+
740+
// Also remove the line link from "jump to first uncovered line".
741+
static JUMP_TO: Lazy<Regex> = Lazy::new(|| {
742+
let re = r"(<a) href='#L\d+'(>jump to first)";
743+
// ^^ ^^^^^^^^^^^^^^
744+
Regex::new(re).unwrap()
745+
});
746+
let coverage = JUMP_TO.replace_all(&coverage, "${1}${2}");
747+
748+
// FIXME(Zalathar): Figure out how to sort file sections, if we ever
749+
// need an HTML coverage test with multiple files.
750+
751+
// Add a line break after `</tr>`, for ease of reading and nicer diffs.
752+
let mut coverage = coverage.replace("</tr>", "</tr>\n");
753+
754+
// Add a final line break so that git doesn't bug us about it.
755+
coverage.push('\n');
756+
coverage
717757
}
718758

719759
/// Replace line numbers in coverage reports with the placeholder `LL`,

tests/coverage/color.coverage

-13
This file was deleted.

tests/coverage/color.rs

-11
This file was deleted.

tests/coverage/html.coverage.html

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
<!doctype html><html><head><meta name='viewport' content='width=device-width,initial-scale=1'><meta charset='UTF-8'><style>.red {
2+
background-color: #ffd0d0;
3+
}
4+
.cyan {
5+
background-color: cyan;
6+
}
7+
body {
8+
font-family: -apple-system, sans-serif;
9+
}
10+
pre {
11+
margin-top: 0px !important;
12+
margin-bottom: 0px !important;
13+
}
14+
.source-name-title {
15+
padding: 5px 10px;
16+
border-bottom: 1px solid #dbdbdb;
17+
background-color: #eee;
18+
line-height: 35px;
19+
}
20+
.centered {
21+
display: table;
22+
margin-left: left;
23+
margin-right: auto;
24+
border: 1px solid #dbdbdb;
25+
border-radius: 3px;
26+
}
27+
.expansion-view {
28+
background-color: rgba(0, 0, 0, 0);
29+
margin-left: 0px;
30+
margin-top: 5px;
31+
margin-right: 5px;
32+
margin-bottom: 5px;
33+
border: 1px solid #dbdbdb;
34+
border-radius: 3px;
35+
}
36+
table {
37+
border-collapse: collapse;
38+
}
39+
.light-row {
40+
background: #ffffff;
41+
border: 1px solid #dbdbdb;
42+
border-left: none;
43+
border-right: none;
44+
}
45+
.light-row-bold {
46+
background: #ffffff;
47+
border: 1px solid #dbdbdb;
48+
border-left: none;
49+
border-right: none;
50+
font-weight: bold;
51+
}
52+
.column-entry {
53+
text-align: left;
54+
}
55+
.column-entry-bold {
56+
font-weight: bold;
57+
text-align: left;
58+
}
59+
.column-entry-yellow {
60+
text-align: left;
61+
background-color: #ffffd0;
62+
}
63+
.column-entry-yellow:hover, tr:hover .column-entry-yellow {
64+
background-color: #fffff0;
65+
}
66+
.column-entry-red {
67+
text-align: left;
68+
background-color: #ffd0d0;
69+
}
70+
.column-entry-red:hover, tr:hover .column-entry-red {
71+
background-color: #fff0f0;
72+
}
73+
.column-entry-gray {
74+
text-align: left;
75+
background-color: #fbfbfb;
76+
}
77+
.column-entry-gray:hover, tr:hover .column-entry-gray {
78+
background-color: #f0f0f0;
79+
}
80+
.column-entry-green {
81+
text-align: left;
82+
background-color: #d0ffd0;
83+
}
84+
.column-entry-green:hover, tr:hover .column-entry-green {
85+
background-color: #f0fff0;
86+
}
87+
.line-number {
88+
text-align: right;
89+
color: #aaa;
90+
}
91+
.covered-line {
92+
text-align: right;
93+
color: #0080ff;
94+
}
95+
.uncovered-line {
96+
text-align: right;
97+
color: #ff3300;
98+
}
99+
.tooltip {
100+
position: relative;
101+
display: inline;
102+
background-color: #b3e6ff;
103+
text-decoration: none;
104+
}
105+
.tooltip span.tooltip-content {
106+
position: absolute;
107+
width: 100px;
108+
margin-left: -50px;
109+
color: #FFFFFF;
110+
background: #000000;
111+
height: 30px;
112+
line-height: 30px;
113+
text-align: center;
114+
visibility: hidden;
115+
border-radius: 6px;
116+
}
117+
.tooltip span.tooltip-content:after {
118+
content: '';
119+
position: absolute;
120+
top: 100%;
121+
left: 50%;
122+
margin-left: -8px;
123+
width: 0; height: 0;
124+
border-top: 8px solid #000000;
125+
border-right: 8px solid transparent;
126+
border-left: 8px solid transparent;
127+
}
128+
:hover.tooltip span.tooltip-content {
129+
visibility: visible;
130+
opacity: 0.8;
131+
bottom: 30px;
132+
left: 50%;
133+
z-index: 999;
134+
}
135+
th, td {
136+
vertical-align: top;
137+
padding: 2px 8px;
138+
border-collapse: collapse;
139+
border-right: solid 1px #eee;
140+
border-left: solid 1px #eee;
141+
text-align: left;
142+
}
143+
td pre {
144+
display: inline-block;
145+
}
146+
td:first-child {
147+
border-left: none;
148+
}
149+
td:last-child {
150+
border-right: none;
151+
}
152+
tr:hover {
153+
background-color: #f0f0f0;
154+
}
155+
tr:last-child {
156+
border-bottom: none;
157+
}
158+
tr:has(> td >a:target) > td.code > pre {
159+
background-color: #ffa;
160+
}
161+
</style></head><body><div class='centered'><table><div class='source-name-title'><pre>$DIR/html.rs</pre></div><tr><td><pre>Line</pre></td><td><pre>Count</pre></td><td><pre>Source (<a>jump to first uncovered line</a>)</pre></td></tr>
162+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>//@ edition: 2021</pre></td></tr>
163+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>//@ ignore-mode-coverage-map</pre></td></tr>
164+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>//@ llvm-cov-flags: --format=html</pre></td></tr>
165+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre></pre></td></tr>
166+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre>// Verify that telling `llvm-cov` to emit HTML actually works.</pre></td></tr>
167+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='uncovered-line'></td><td class='code'><pre></pre></td></tr>
168+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='covered-line'><pre>1</pre></td><td class='code'><pre>fn main() {</pre></td></tr>
169+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='covered-line'><pre>1</pre></td><td class='code'><pre> for <div class='tooltip'><span class='red'>_i</span><span class='tooltip-content'>0</span></div> in 0..0 <div class='tooltip'><span class='red'>{}</span><span class='tooltip-content'>0</span></div></pre></td></tr>
170+
<tr><td class='line-number'><a><pre>LL</pre></a></td><td class='covered-line'><pre>1</pre></td><td class='code'><pre>}</pre></td></tr>
171+
</table></div></body></html>

tests/coverage/html.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ edition: 2021
2+
//@ ignore-mode-coverage-map
3+
//@ llvm-cov-flags: --format=html
4+
5+
// Verify that telling `llvm-cov` to emit HTML actually works.
6+
7+
fn main() {
8+
for _i in 0..0 {}
9+
}

tests/coverage/unicode.cov-map

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Function name: unicode::main
2-
Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 0e, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 22, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02]
2+
Raw bytes (67): 0x[01, 01, 09, 01, 05, 03, 05, 1e, 0d, 22, 09, 03, 05, 11, 1b, 1e, 0d, 22, 09, 03, 05, 09, 01, 09, 01, 00, 0b, 05, 01, 09, 00, 0c, 03, 00, 10, 00, 1b, 05, 00, 1c, 00, 28, 22, 02, 08, 00, 25, 09, 00, 29, 00, 46, 11, 00, 47, 02, 06, 1b, 02, 06, 00, 07, 17, 02, 05, 01, 02]
33
Number of files: 1
44
- file 0 => global file 1
55
Number of expressions: 9
@@ -13,7 +13,7 @@ Number of expressions: 9
1313
- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(2)
1414
- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(1)
1515
Number of file 0 mappings: 9
16-
- Code(Counter(0)) at (prev + 14, 1) to (start + 0, 11)
16+
- Code(Counter(0)) at (prev + 9, 1) to (start + 0, 11)
1717
- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 12)
1818
- Code(Expression(0, Add)) at (prev + 0, 16) to (start + 0, 27)
1919
= (c0 + c1)
@@ -28,18 +28,18 @@ Number of file 0 mappings: 9
2828
= (c4 + ((((c0 + c1) - c1) - c2) + c3))
2929

3030
Function name: unicode::他 (unused)
31-
Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 19, 00, 25]
31+
Raw bytes (9): 0x[01, 01, 00, 01, 00, 19, 19, 00, 25]
3232
Number of files: 1
3333
- file 0 => global file 1
3434
Number of expressions: 0
3535
Number of file 0 mappings: 1
36-
- Code(Zero) at (prev + 30, 25) to (start + 0, 37)
36+
- Code(Zero) at (prev + 25, 25) to (start + 0, 37)
3737

3838
Function name: unicode::申し訳ございません
39-
Raw bytes (9): 0x[01, 01, 00, 01, 01, 18, 01, 02, 02]
39+
Raw bytes (9): 0x[01, 01, 00, 01, 01, 13, 01, 02, 02]
4040
Number of files: 1
4141
- file 0 => global file 1
4242
Number of expressions: 0
4343
Number of file 0 mappings: 1
44-
- Code(Counter(0)) at (prev + 24, 1) to (start + 2, 2)
44+
- Code(Counter(0)) at (prev + 19, 1) to (start + 2, 2)
4545

tests/coverage/unicode.coverage

-39
This file was deleted.

0 commit comments

Comments
 (0)