mirror of
https://github.com/denoland/deno.git
synced 2025-08-02 18:12:39 +00:00
fix(cli/coverage): display mapped instrumentation line counts (#9310)
This commit is contained in:
parent
013b8fe606
commit
9965fc8cc3
11 changed files with 180 additions and 108 deletions
|
@ -151,119 +151,127 @@ impl PrettyCoverageReporter {
|
|||
}
|
||||
|
||||
let lines = script_source.split('\n').collect::<Vec<_>>();
|
||||
let mut covered_lines: Vec<usize> = Vec::new();
|
||||
let mut uncovered_lines: Vec<usize> = Vec::new();
|
||||
|
||||
let mut line_start_offset = 0;
|
||||
for (index, line) in lines.iter().enumerate() {
|
||||
let line_end_offset = line_start_offset + line.len();
|
||||
let line_offsets = {
|
||||
let mut offsets: Vec<(usize, usize)> = Vec::new();
|
||||
let mut index = 0;
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
let ignore = ignored_spans.iter().any(|span| {
|
||||
(span.lo.0 as usize) <= line_start_offset
|
||||
&& (span.hi.0 as usize) >= line_end_offset
|
||||
});
|
||||
|
||||
if ignore {
|
||||
covered_lines.push(index);
|
||||
continue;
|
||||
for line in &lines {
|
||||
offsets.push((index, index + line.len() + 1));
|
||||
index += line.len() + 1;
|
||||
}
|
||||
|
||||
// Count the hits of ranges that include the entire line which will always be at-least one
|
||||
// as long as the code has been evaluated.
|
||||
for function in &script_coverage.functions {
|
||||
for range in &function.ranges {
|
||||
if range.start_offset <= line_start_offset
|
||||
&& range.end_offset >= line_end_offset
|
||||
{
|
||||
count += range.count;
|
||||
offsets
|
||||
};
|
||||
|
||||
let line_counts = line_offsets
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (line_start_offset, line_end_offset))| {
|
||||
let ignore = ignored_spans.iter().any(|span| {
|
||||
(span.lo.0 as usize) <= *line_start_offset
|
||||
&& (span.hi.0 as usize) >= *line_end_offset
|
||||
});
|
||||
|
||||
if ignore {
|
||||
return (index, 1);
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
// Count the hits of ranges that include the entire line which will always be at-least one
|
||||
// as long as the code has been evaluated.
|
||||
for function in &script_coverage.functions {
|
||||
for range in &function.ranges {
|
||||
if range.start_offset <= *line_start_offset
|
||||
&& range.end_offset >= *line_end_offset
|
||||
{
|
||||
count += range.count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the count if any block intersects with the current line has a count of
|
||||
// zero.
|
||||
//
|
||||
// We check for intersection instead of inclusion here because a block may be anywhere
|
||||
// inside a line.
|
||||
for function in &script_coverage.functions {
|
||||
for range in &function.ranges {
|
||||
if range.count > 0 {
|
||||
continue;
|
||||
}
|
||||
// Reset the count if any block intersects with the current line has a count of
|
||||
// zero.
|
||||
//
|
||||
// We check for intersection instead of inclusion here because a block may be anywhere
|
||||
// inside a line.
|
||||
for function in &script_coverage.functions {
|
||||
for range in &function.ranges {
|
||||
if range.count > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range.start_offset < line_start_offset
|
||||
&& range.end_offset > line_start_offset)
|
||||
|| (range.start_offset < line_end_offset
|
||||
&& range.end_offset > line_end_offset)
|
||||
{
|
||||
count = 0;
|
||||
if (range.start_offset < *line_start_offset
|
||||
&& range.end_offset > *line_start_offset)
|
||||
|| (range.start_offset < *line_end_offset
|
||||
&& range.end_offset > *line_end_offset)
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
covered_lines.push(index);
|
||||
} else {
|
||||
uncovered_lines.push(index);
|
||||
}
|
||||
(index, count)
|
||||
})
|
||||
.collect::<Vec<(usize, usize)>>();
|
||||
|
||||
line_start_offset += line.len() + 1;
|
||||
}
|
||||
let lines = if let Some(original_source) = maybe_original_source.as_ref() {
|
||||
original_source.split('\n').collect::<Vec<_>>()
|
||||
} else {
|
||||
lines
|
||||
};
|
||||
|
||||
let line_counts = if let Some(source_map) = maybe_source_map.as_ref() {
|
||||
let mut line_counts = line_counts
|
||||
.iter()
|
||||
.map(|(index, count)| {
|
||||
source_map
|
||||
.tokens()
|
||||
.filter(move |token| token.get_dst_line() as usize == *index)
|
||||
.map(move |token| (token.get_src_line() as usize, *count))
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<(usize, usize)>>();
|
||||
|
||||
line_counts.sort_unstable_by_key(|(index, _)| *index);
|
||||
line_counts.dedup_by_key(|(index, _)| *index);
|
||||
|
||||
line_counts
|
||||
} else {
|
||||
line_counts
|
||||
};
|
||||
|
||||
if !self.quiet {
|
||||
print!("cover {} ... ", script_coverage.url);
|
||||
|
||||
let line_coverage_ratio = covered_lines.len() as f32 / lines.len() as f32;
|
||||
let line_coverage = format!(
|
||||
"{:.3}% ({}/{})",
|
||||
line_coverage_ratio * 100.0,
|
||||
covered_lines.len(),
|
||||
lines.len()
|
||||
);
|
||||
let hit_lines = line_counts
|
||||
.iter()
|
||||
.filter(|(_, count)| *count != 0)
|
||||
.map(|(index, _)| *index);
|
||||
|
||||
if line_coverage_ratio >= 0.9 {
|
||||
let missed_lines = line_counts
|
||||
.iter()
|
||||
.filter(|(_, count)| *count == 0)
|
||||
.map(|(index, _)| *index);
|
||||
|
||||
let lines_found = line_counts.len();
|
||||
let lines_hit = hit_lines.count();
|
||||
let line_ratio = lines_hit as f32 / lines_found as f32;
|
||||
|
||||
let line_coverage =
|
||||
format!("{:.3}% ({}/{})", line_ratio * 100.0, lines_hit, lines_found,);
|
||||
|
||||
if line_ratio >= 0.9 {
|
||||
println!("{}", colors::green(&line_coverage));
|
||||
} else if line_coverage_ratio >= 0.75 {
|
||||
} else if line_ratio >= 0.75 {
|
||||
println!("{}", colors::yellow(&line_coverage));
|
||||
} else {
|
||||
println!("{}", colors::red(&line_coverage));
|
||||
}
|
||||
|
||||
let output_lines =
|
||||
if let Some(original_source) = maybe_original_source.as_ref() {
|
||||
original_source.split('\n').collect::<Vec<_>>()
|
||||
} else {
|
||||
lines
|
||||
};
|
||||
|
||||
let output_indices = if let Some(source_map) = maybe_source_map.as_ref() {
|
||||
// The compiled executable source lines have to be mapped to all the original source lines that they
|
||||
// came from; this happens in a couple of emit scenarios, the most common example being function
|
||||
// declarations where the compiled JavaScript code only takes a line but the original
|
||||
// TypeScript source spans 10 lines.
|
||||
let mut indices = uncovered_lines
|
||||
.iter()
|
||||
.map(|i| {
|
||||
source_map
|
||||
.tokens()
|
||||
.filter(move |token| token.get_dst_line() as usize == *i)
|
||||
.map(|token| token.get_src_line() as usize)
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<usize>>();
|
||||
|
||||
indices.sort_unstable();
|
||||
indices.dedup();
|
||||
|
||||
indices
|
||||
} else {
|
||||
uncovered_lines
|
||||
};
|
||||
|
||||
let mut last_line = None;
|
||||
for line_index in output_indices {
|
||||
for line_index in missed_lines {
|
||||
const WIDTH: usize = 4;
|
||||
const SEPERATOR: &str = "|";
|
||||
|
||||
|
@ -279,7 +287,7 @@ impl PrettyCoverageReporter {
|
|||
"{:width$} {} {}",
|
||||
line_index + 1,
|
||||
colors::gray(SEPERATOR),
|
||||
colors::red(&output_lines[line_index]),
|
||||
colors::red(&lines[line_index]),
|
||||
width = WIDTH
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue