0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-08 07:16:56 -05:00

fix(coverage): better handling of multi-byte characters (#15159)

This commit is contained in:
David Sherret 2022-07-11 19:02:11 -04:00 committed by cjihrig
parent 99bf821727
commit 4854cffcc1
No known key found for this signature in database
GPG key ID: 7434390BDBE9B9C5
8 changed files with 129 additions and 113 deletions

14
Cargo.lock generated
View file

@ -838,7 +838,7 @@ dependencies = [
"tempfile", "tempfile",
"test_util", "test_util",
"text-size", "text-size",
"text_lines", "text_lines 0.6.0",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tower-lsp", "tower-lsp",
@ -881,7 +881,7 @@ dependencies = [
"swc_ecma_transforms_typescript", "swc_ecma_transforms_typescript",
"swc_ecma_utils", "swc_ecma_utils",
"swc_ecma_visit", "swc_ecma_visit",
"text_lines", "text_lines 0.4.1",
"url 2.2.2", "url 2.2.2",
] ]
@ -1370,7 +1370,7 @@ dependencies = [
"dprint-core", "dprint-core",
"jsonc-parser", "jsonc-parser",
"serde", "serde",
"text_lines", "text_lines 0.4.1",
] ]
[[package]] [[package]]
@ -1412,7 +1412,7 @@ dependencies = [
"swc_common", "swc_common",
"swc_ecma_ast", "swc_ecma_ast",
"swc_ecma_parser", "swc_ecma_parser",
"text_lines", "text_lines 0.4.1",
] ]
[[package]] [[package]]
@ -4580,6 +4580,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "text_lines"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf"
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.15.0" version = "0.15.0"

View file

@ -94,7 +94,7 @@ serde = { version = "=1.0.139", features = ["derive"] }
serde_repr = "=0.1.8" serde_repr = "=0.1.8"
shell-escape = "=0.1.5" shell-escape = "=0.1.5"
text-size = "=1.1.0" text-size = "=1.1.0"
text_lines = "=0.4.1" text_lines = "=0.6.0"
tokio = { version = "=1.19", features = ["full"] } tokio = { version = "=1.19", features = ["full"] }
tokio-util = "=0.7.2" tokio-util = "=0.7.2"
tower-lsp = "=0.17.0" tower-lsp = "=0.17.0"

View file

@ -46,9 +46,9 @@ DA:64,0
DA:65,0 DA:65,0
DA:66,0 DA:66,0
DA:67,0 DA:67,0
DA:68,1 DA:68,0
DA:71,0 DA:71,0
DA:74,1 DA:74,1
LH:23 LH:22
LF:38 LF:38
end_of_record end_of_record

View file

@ -1,4 +1,4 @@
cover [WILDCARD]/coverage/complex.ts ... 60.526% (23/38) cover [WILDCARD]/coverage/complex.ts ... 57.895% (22/38)
46 | export function unused( 46 | export function unused(
47 | foo: string, 47 | foo: string,
48 | bar: string, 48 | bar: string,
@ -15,5 +15,6 @@ cover [WILDCARD]/coverage/complex.ts ... 60.526% (23/38)
65 | return ( 65 | return (
66 | 0 66 | 0
67 | ); 67 | );
68 | }
-----|----- -----|-----
71 | console.log("%s", () => 1); 71 | console.log("%s", () => 1);

View file

@ -6,10 +6,12 @@ use serde::Serialize;
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CoverageRange { pub struct CoverageRange {
/// Start byte index. /// Start character index.
pub start_offset: usize, #[serde(rename = "startOffset")]
/// End byte index. pub start_char_offset: usize,
pub end_offset: usize, /// End character index.
#[serde(rename = "endOffset")]
pub end_char_offset: usize,
pub count: i64, pub count: i64,
} }

View file

@ -54,15 +54,15 @@ pub fn merge_scripts(
let first: &ScriptCoverage = &scripts[0]; let first: &ScriptCoverage = &scripts[0];
(first.script_id.clone(), first.url.clone()) (first.script_id.clone(), first.url.clone())
}; };
let mut range_to_funcs: BTreeMap<Range, Vec<FunctionCoverage>> = let mut range_to_funcs: BTreeMap<CharRange, Vec<FunctionCoverage>> =
BTreeMap::new(); BTreeMap::new();
for script_cov in scripts { for script_cov in scripts {
for func_cov in script_cov.functions { for func_cov in script_cov.functions {
let root_range = { let root_range = {
let root_range_cov: &CoverageRange = &func_cov.ranges[0]; let root_range_cov: &CoverageRange = &func_cov.ranges[0];
Range { CharRange {
start: root_range_cov.start_offset, start: root_range_cov.start_char_offset,
end: root_range_cov.end_offset, end: root_range_cov.end_char_offset,
} }
}; };
range_to_funcs range_to_funcs
@ -85,12 +85,12 @@ pub fn merge_scripts(
} }
#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
struct Range { struct CharRange {
start: usize, start: usize,
end: usize, end: usize,
} }
impl Ord for Range { impl Ord for CharRange {
fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
if self.start != other.start { if self.start != other.start {
self.start.cmp(&other.start) self.start.cmp(&other.start)
@ -100,7 +100,7 @@ impl Ord for Range {
} }
} }
impl PartialOrd for Range { impl PartialOrd for CharRange {
fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
if self.start != other.start { if self.start != other.start {
self.start.partial_cmp(&other.start) self.start.partial_cmp(&other.start)
@ -249,7 +249,7 @@ fn merge_range_tree_children<'a>(
Vec::with_capacity(parent_trees.len()); Vec::with_capacity(parent_trees.len());
let mut wrapped_children: Vec<Vec<&'a mut RangeTree<'a>>> = let mut wrapped_children: Vec<Vec<&'a mut RangeTree<'a>>> =
Vec::with_capacity(parent_trees.len()); Vec::with_capacity(parent_trees.len());
let mut open_range: Option<Range> = None; let mut open_range: Option<CharRange> = None;
for _parent_tree in parent_trees.iter() { for _parent_tree in parent_trees.iter() {
flat_children.push(Vec::new()); flat_children.push(Vec::new());
@ -318,7 +318,7 @@ fn merge_range_tree_children<'a>(
.push(tree); .push(tree);
} }
start_event_queue.set_pending_offset(open_range_end); start_event_queue.set_pending_offset(open_range_end);
open_range = Some(Range { open_range = Some(CharRange {
start: event.offset, start: event.offset,
end: open_range_end, end: open_range_end,
}); });
@ -452,8 +452,8 @@ mod tests {
function_name: String::from("lib"), function_name: String::from("lib"),
is_block_coverage: true, is_block_coverage: true,
ranges: vec![CoverageRange { ranges: vec![CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 1, count: 1,
}], }],
}], }],
@ -467,8 +467,8 @@ mod tests {
function_name: String::from("lib"), function_name: String::from("lib"),
is_block_coverage: true, is_block_coverage: true,
ranges: vec![CoverageRange { ranges: vec![CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 2, count: 2,
}], }],
}], }],
@ -483,8 +483,8 @@ mod tests {
function_name: String::from("lib"), function_name: String::from("lib"),
is_block_coverage: true, is_block_coverage: true,
ranges: vec![CoverageRange { ranges: vec![CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 3, count: 3,
}], }],
}], }],
@ -506,13 +506,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 10, count: 10,
}, },
CoverageRange { CoverageRange {
start_offset: 3, start_char_offset: 3,
end_offset: 6, end_char_offset: 6,
count: 1, count: 1,
}, },
], ],
@ -528,13 +528,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 20, count: 20,
}, },
CoverageRange { CoverageRange {
start_offset: 3, start_char_offset: 3,
end_offset: 6, end_char_offset: 6,
count: 2, count: 2,
}, },
], ],
@ -551,13 +551,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 30, count: 30,
}, },
CoverageRange { CoverageRange {
start_offset: 3, start_char_offset: 3,
end_offset: 6, end_char_offset: 6,
count: 3, count: 3,
}, },
], ],
@ -580,13 +580,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 10, count: 10,
}, },
CoverageRange { CoverageRange {
start_offset: 2, start_char_offset: 2,
end_offset: 5, end_char_offset: 5,
count: 1, count: 1,
}, },
], ],
@ -602,13 +602,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 20, count: 20,
}, },
CoverageRange { CoverageRange {
start_offset: 4, start_char_offset: 4,
end_offset: 7, end_char_offset: 7,
count: 2, count: 2,
}, },
], ],
@ -625,23 +625,23 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 30, count: 30,
}, },
CoverageRange { CoverageRange {
start_offset: 2, start_char_offset: 2,
end_offset: 5, end_char_offset: 5,
count: 21, count: 21,
}, },
CoverageRange { CoverageRange {
start_offset: 4, start_char_offset: 4,
end_offset: 5, end_char_offset: 5,
count: 3, count: 3,
}, },
CoverageRange { CoverageRange {
start_offset: 5, start_char_offset: 5,
end_offset: 7, end_char_offset: 7,
count: 12, count: 12,
}, },
], ],
@ -664,23 +664,23 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 1, count: 1,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 8, end_char_offset: 8,
count: 6, count: 6,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 5, end_char_offset: 5,
count: 5, count: 5,
}, },
CoverageRange { CoverageRange {
start_offset: 5, start_char_offset: 5,
end_offset: 8, end_char_offset: 8,
count: 7, count: 7,
}, },
], ],
@ -696,23 +696,23 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 4, count: 4,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 8, end_char_offset: 8,
count: 8, count: 8,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 5, end_char_offset: 5,
count: 9, count: 9,
}, },
CoverageRange { CoverageRange {
start_offset: 5, start_char_offset: 5,
end_offset: 8, end_char_offset: 8,
count: 7, count: 7,
}, },
], ],
@ -729,13 +729,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 5, count: 5,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 8, end_char_offset: 8,
count: 14, count: 14,
}, },
], ],
@ -758,13 +758,13 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 7, end_char_offset: 7,
count: 10, count: 10,
}, },
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 4, end_char_offset: 4,
count: 1, count: 1,
}, },
], ],
@ -780,18 +780,18 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 7, end_char_offset: 7,
count: 20, count: 20,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 6, end_char_offset: 6,
count: 11, count: 11,
}, },
CoverageRange { CoverageRange {
start_offset: 2, start_char_offset: 2,
end_offset: 5, end_char_offset: 5,
count: 2, count: 2,
}, },
], ],
@ -808,23 +808,23 @@ mod tests {
is_block_coverage: true, is_block_coverage: true,
ranges: vec![ ranges: vec![
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 7, end_char_offset: 7,
count: 30, count: 30,
}, },
CoverageRange { CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 6, end_char_offset: 6,
count: 21, count: 21,
}, },
CoverageRange { CoverageRange {
start_offset: 1, start_char_offset: 1,
end_offset: 5, end_char_offset: 5,
count: 12, count: 12,
}, },
CoverageRange { CoverageRange {
start_offset: 2, start_char_offset: 2,
end_offset: 4, end_char_offset: 4,
count: 3, count: 3,
}, },
], ],

View file

@ -202,16 +202,18 @@ fn generate_coverage_report(
continue; continue;
} }
let source_line_index = let dest_line_index = text_lines.line_index(
text_lines.line_index(function.ranges[0].start_offset); text_lines
.byte_index_from_char_index(function.ranges[0].start_char_offset),
);
let line_index = if let Some(source_map) = maybe_source_map.as_ref() { let line_index = if let Some(source_map) = maybe_source_map.as_ref() {
source_map source_map
.tokens() .tokens()
.find(|token| token.get_dst_line() as usize == source_line_index) .find(|token| token.get_dst_line() as usize == dest_line_index)
.map(|token| token.get_src_line() as usize) .map(|token| token.get_src_line() as usize)
.unwrap_or(0) .unwrap_or(0)
} else { } else {
source_line_index dest_line_index
}; };
coverage_report.named_functions.push(FunctionCoverageItem { coverage_report.named_functions.push(FunctionCoverageItem {
@ -224,7 +226,9 @@ fn generate_coverage_report(
for (block_number, function) in script_coverage.functions.iter().enumerate() { for (block_number, function) in script_coverage.functions.iter().enumerate() {
let block_hits = function.ranges[0].count; let block_hits = function.ranges[0].count;
for (branch_number, range) in function.ranges[1..].iter().enumerate() { for (branch_number, range) in function.ranges[1..].iter().enumerate() {
let source_line_index = text_lines.line_index(range.start_offset); let source_line_index = text_lines.line_index(
text_lines.byte_index_from_char_index(range.start_char_offset),
);
let line_index = if let Some(source_map) = maybe_source_map.as_ref() { let line_index = if let Some(source_map) = maybe_source_map.as_ref() {
source_map source_map
.tokens() .tokens()
@ -264,11 +268,14 @@ fn generate_coverage_report(
// parts of a line in color (word diff style) instead of the entire line. // parts of a line in color (word diff style) instead of the entire line.
let mut line_counts = Vec::with_capacity(text_lines.lines_count()); let mut line_counts = Vec::with_capacity(text_lines.lines_count());
for line_index in 0..text_lines.lines_count() { for line_index in 0..text_lines.lines_count() {
let line_start_offset = text_lines.line_start(line_index); let line_start_byte_offset = text_lines.line_start(line_index);
let line_end_offset = text_lines.line_end(line_index); let line_start_char_offset = text_lines.char_index(line_start_byte_offset);
let line_end_byte_offset = text_lines.line_end(line_index);
let line_end_char_offset = text_lines.char_index(line_end_byte_offset);
let ignore = comment_ranges.iter().any(|range| { let ignore = comment_ranges.iter().any(|range| {
range.start <= line_start_offset && range.end >= line_end_offset range.start <= line_start_byte_offset && range.end >= line_end_byte_offset
}) || script_source[line_start_offset..line_end_offset] }) || script_source
[line_start_byte_offset..line_end_byte_offset]
.trim() .trim()
.is_empty(); .is_empty();
let mut count = 0; let mut count = 0;
@ -280,8 +287,8 @@ fn generate_coverage_report(
// as long as the code has been evaluated. // as long as the code has been evaluated.
for function in &script_coverage.functions { for function in &script_coverage.functions {
for range in &function.ranges { for range in &function.ranges {
if range.start_offset <= line_start_offset if range.start_char_offset <= line_start_char_offset
&& range.end_offset >= line_end_offset && range.end_char_offset >= line_end_char_offset
{ {
count += range.count; count += range.count;
} }
@ -295,8 +302,8 @@ fn generate_coverage_report(
continue; continue;
} }
let overlaps = range.start_offset < line_end_offset let overlaps = range.start_char_offset < line_end_char_offset
&& range.end_offset > line_start_offset; && range.end_char_offset > line_start_char_offset;
if overlaps { if overlaps {
count = 0; count = 0;
} }

View file

@ -134,8 +134,8 @@ impl<'rt> RangeTree<'rt> {
while let Some((cur, parent_count)) = stack.pop() { while let Some((cur, parent_count)) = stack.pop() {
let count: i64 = parent_count + cur.delta; let count: i64 = parent_count + cur.delta;
ranges.push(CoverageRange { ranges.push(CoverageRange {
start_offset: cur.start, start_char_offset: cur.start,
end_offset: cur.end, end_char_offset: cur.end,
count, count,
}); });
for child in cur.children.iter().rev() { for child in cur.children.iter().rev() {
@ -165,14 +165,14 @@ impl<'rt> RangeTree<'rt> {
) -> Option<&'a mut RangeTree<'a>> { ) -> Option<&'a mut RangeTree<'a>> {
let has_range: bool = match ranges.peek() { let has_range: bool = match ranges.peek() {
None => false, None => false,
Some(range) => range.start_offset < parent_end, Some(range) => range.start_char_offset < parent_end,
}; };
if !has_range { if !has_range {
return None; return None;
} }
let range = ranges.next().unwrap(); let range = ranges.next().unwrap();
let start: usize = range.start_offset; let start: usize = range.start_char_offset;
let end: usize = range.end_offset; let end: usize = range.end_char_offset;
let count: i64 = range.count; let count: i64 = range.count;
let delta: i64 = count - parent_count; let delta: i64 = count - parent_count;
let mut children: Vec<&mut RangeTree> = Vec::new(); let mut children: Vec<&mut RangeTree> = Vec::new();
@ -193,8 +193,8 @@ mod tests {
fn from_sorted_ranges_empty() { fn from_sorted_ranges_empty() {
let rta = RangeTreeArena::new(); let rta = RangeTreeArena::new();
let inputs: Vec<CoverageRange> = vec![CoverageRange { let inputs: Vec<CoverageRange> = vec![CoverageRange {
start_offset: 0, start_char_offset: 0,
end_offset: 9, end_char_offset: 9,
count: 1, count: 1,
}]; }];
let actual: Option<&mut RangeTree> = let actual: Option<&mut RangeTree> =