summaryrefslogtreecommitdiff
path: root/cli/tools/coverage.rs
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2021-10-06 19:28:28 +0200
committerGitHub <noreply@github.com>2021-10-06 19:28:28 +0200
commit4e3068be6374cf25b663ed731152909ecb0a743f (patch)
treea8c4c6a969a0a2efff5dce08cbb159084d22c0c6 /cli/tools/coverage.rs
parentb686907a45bb7d113f863cca7c52754027e449c0 (diff)
Revert "fix(cli): ensure empty lines don't count towards coverage (#11957)" (#12348)
This reverts commit d5b38a992933db5cb2d0221e9d82af191022dad5.
Diffstat (limited to 'cli/tools/coverage.rs')
-rw-r--r--cli/tools/coverage.rs787
1 files changed, 333 insertions, 454 deletions
diff --git a/cli/tools/coverage.rs b/cli/tools/coverage.rs
index 2e8acfa99..60efe789c 100644
--- a/cli/tools/coverage.rs
+++ b/cli/tools/coverage.rs
@@ -6,29 +6,30 @@ use crate::fs_util::collect_files;
use crate::module_graph::TypeLib;
use crate::proc_state::ProcState;
use crate::source_maps::SourceMapGetter;
+use deno_ast::swc::common::Span;
+use deno_ast::MediaType;
use deno_core::error::AnyError;
-use deno_core::resolve_url_or_path;
use deno_core::serde_json;
+use deno_core::url::Url;
use deno_core::LocalInspectorSession;
-use deno_core::ModuleSpecifier;
use deno_runtime::permissions::Permissions;
use regex::Regex;
use serde::Deserialize;
use serde::Serialize;
use sourcemap::SourceMap;
-use std::cmp;
use std::fs;
use std::fs::File;
use std::io::BufWriter;
use std::io::Write;
use std::path::PathBuf;
+use std::sync::Arc;
use uuid::Uuid;
-// TODO(caspervonb) These structs are specific to the inspector protocol and should be refactored
-// into a reusable module.
+// TODO(caspervonb) all of these structs can and should be made private, possibly moved to
+// inspector::protocol.
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
-struct CoverageRange {
+pub struct CoverageRange {
pub start_offset: usize,
pub end_offset: usize,
pub count: usize,
@@ -36,36 +37,23 @@ struct CoverageRange {
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
-struct FunctionCoverage {
+pub struct FunctionCoverage {
pub function_name: String,
pub ranges: Vec<CoverageRange>,
pub is_block_coverage: bool,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
-struct LineCoverage {
- pub start_offset: usize,
- pub end_offset: usize,
- pub ranges: Vec<CoverageRange>,
-}
-
-#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
-struct ScriptCoverage {
+pub struct ScriptCoverage {
pub script_id: String,
pub url: String,
pub functions: Vec<FunctionCoverage>,
}
-#[derive(Debug, Serialize, Deserialize, Clone)]
-struct CoverageResult {
- pub lines: Vec<LineCoverage>,
- pub functions: Vec<FunctionCoverage>,
-}
-
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
-struct StartPreciseCoverageParameters {
+pub struct StartPreciseCoverageParameters {
pub call_count: bool,
pub detailed: bool,
pub allow_triggered_updates: bool,
@@ -73,13 +61,13 @@ struct StartPreciseCoverageParameters {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
-struct StartPreciseCoverageReturnObject {
+pub struct StartPreciseCoverageReturnObject {
pub timestamp: f64,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
-struct TakePreciseCoverageReturnObject {
+pub struct TakePreciseCoverageReturnObject {
pub result: Vec<ScriptCoverage>,
pub timestamp: f64,
}
@@ -181,7 +169,7 @@ impl CoverageCollector {
}
}
-enum CoverageReporterKind {
+pub enum CoverageReporterKind {
Pretty,
Lcov,
}
@@ -190,18 +178,21 @@ fn create_reporter(
kind: CoverageReporterKind,
) -> Box<dyn CoverageReporter + Send> {
match kind {
- CoverageReporterKind::Pretty => Box::new(PrettyCoverageReporter::new()),
CoverageReporterKind::Lcov => Box::new(LcovCoverageReporter::new()),
+ CoverageReporterKind::Pretty => Box::new(PrettyCoverageReporter::new()),
}
}
-trait CoverageReporter {
- fn report_result(
+pub trait CoverageReporter {
+ fn visit_coverage(
&mut self,
- specifier: &ModuleSpecifier,
- result: &CoverageResult,
- source: &str,
+ script_coverage: &ScriptCoverage,
+ script_source: &str,
+ maybe_source_map: Option<Vec<u8>>,
+ maybe_original_source: Option<Arc<String>>,
);
+
+ fn done(&mut self);
}
pub struct LcovCoverageReporter {}
@@ -213,45 +204,86 @@ impl LcovCoverageReporter {
}
impl CoverageReporter for LcovCoverageReporter {
- fn report_result(
+ fn visit_coverage(
&mut self,
- specifier: &ModuleSpecifier,
- result: &CoverageResult,
- source: &str,
+ script_coverage: &ScriptCoverage,
+ script_source: &str,
+ maybe_source_map: Option<Vec<u8>>,
+ _maybe_original_source: Option<Arc<String>>,
) {
- println!("SF:{}", specifier.to_file_path().unwrap().to_str().unwrap());
+ // TODO(caspervonb) cleanup and reduce duplication between reporters, pre-compute line coverage
+ // elsewhere.
+ let maybe_source_map = maybe_source_map
+ .map(|source_map| SourceMap::from_slice(&source_map).unwrap());
+
+ let url = Url::parse(&script_coverage.url).unwrap();
+ let file_path = url.to_file_path().unwrap();
+ println!("SF:{}", file_path.to_str().unwrap());
+
+ let mut functions_found = 0;
+ for function in &script_coverage.functions {
+ if function.function_name.is_empty() {
+ continue;
+ }
- let named_functions = result
- .functions
- .iter()
- .filter(|block| !block.function_name.is_empty())
- .collect::<Vec<&FunctionCoverage>>();
+ let source_line = script_source[0..function.ranges[0].start_offset]
+ .split('\n')
+ .count();
+
+ let line_index = if let Some(source_map) = maybe_source_map.as_ref() {
+ source_map
+ .tokens()
+ .find(|token| token.get_dst_line() as usize == source_line)
+ .map(|token| token.get_src_line() as usize)
+ .unwrap_or(0)
+ } else {
+ source_line
+ };
+
+ let function_name = &function.function_name;
- for block in &named_functions {
- let index = source[0..block.ranges[0].start_offset].split('\n').count();
+ println!("FN:{},{}", line_index + 1, function_name);
- println!("FN:{},{}", index + 1, block.function_name);
+ functions_found += 1;
}
- let hit_functions = named_functions
- .iter()
- .filter(|block| block.ranges[0].count > 0)
- .cloned()
- .collect::<Vec<&FunctionCoverage>>();
+ let mut functions_hit = 0;
+ for function in &script_coverage.functions {
+ if function.function_name.is_empty() {
+ continue;
+ }
- for block in &hit_functions {
- println!("FNDA:{},{}", block.ranges[0].count, block.function_name);
+ let execution_count = function.ranges[0].count;
+ let function_name = &function.function_name;
+
+ println!("FNDA:{},{}", execution_count, function_name);
+
+ if execution_count != 0 {
+ functions_hit += 1;
+ }
}
- println!("FNF:{}", named_functions.len());
- println!("FNH:{}", hit_functions.len());
+ println!("FNF:{}", functions_found);
+ println!("FNH:{}", functions_hit);
let mut branches_found = 0;
let mut branches_hit = 0;
- for (block_number, block) in result.functions.iter().enumerate() {
- let block_hits = block.ranges[0].count;
- for (branch_number, range) in block.ranges[1..].iter().enumerate() {
- let line_index = source[0..range.start_offset].split('\n').count();
+ for (block_number, function) in script_coverage.functions.iter().enumerate()
+ {
+ let block_hits = function.ranges[0].count;
+ for (branch_number, range) in function.ranges[1..].iter().enumerate() {
+ let source_line =
+ script_source[0..range.start_offset].split('\n').count();
+
+ let line_index = if let Some(source_map) = maybe_source_map.as_ref() {
+ source_map
+ .tokens()
+ .find(|token| token.get_dst_line() as usize == source_line)
+ .map(|token| token.get_src_line() as usize)
+ .unwrap_or(0)
+ } else {
+ source_line
+ };
// From https://manpages.debian.org/unstable/lcov/geninfo.1.en.html:
//
@@ -286,49 +318,97 @@ impl CoverageReporter for LcovCoverageReporter {
println!("BRF:{}", branches_found);
println!("BRH:{}", branches_hit);
- let enumerated_lines = result
- .lines
- .iter()
- .enumerate()
- .collect::<Vec<(usize, &LineCoverage)>>();
+ let lines = script_source.split('\n').collect::<Vec<_>>();
+ let line_offsets = {
+ let mut offsets: Vec<(usize, usize)> = Vec::new();
+ let mut index = 0;
- for (index, line) in &enumerated_lines {
- if line.ranges.is_empty() {
- continue;
+ for line in &lines {
+ offsets.push((index, index + line.len() + 1));
+ index += line.len() + 1;
}
- let mut count = 0;
- for range in &line.ranges {
- count += range.count;
+ offsets
+ };
- if range.count == 0 {
- count = 0;
- break;
+ let line_counts = line_offsets
+ .iter()
+ .map(|(line_start_offset, line_end_offset)| {
+ 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;
+ }
+ }
}
- }
- println!("DA:{},{}", index + 1, count);
- }
+ // We reset the count if any block with a zero count overlaps with the line range.
+ for function in &script_coverage.functions {
+ for range in &function.ranges {
+ if range.count > 0 {
+ continue;
+ }
- let lines_found = enumerated_lines
- .iter()
- .filter(|(_, line)| !line.ranges.is_empty())
- .count();
+ let overlaps = std::cmp::max(line_end_offset, &range.end_offset)
+ - std::cmp::min(line_start_offset, &range.start_offset)
+ < (line_end_offset - line_start_offset)
+ + (range.end_offset - range.start_offset);
- println!("LF:{}", lines_found);
+ if overlaps {
+ count = 0;
+ }
+ }
+ }
- let lines_hit = enumerated_lines
- .iter()
- .filter(|(_, line)| {
- !line.ranges.is_empty()
- && !line.ranges.iter().any(|range| range.count == 0)
+ count
})
- .count();
+ .collect::<Vec<usize>>();
+
+ let found_lines = if let Some(source_map) = maybe_source_map.as_ref() {
+ let mut found_lines = line_counts
+ .iter()
+ .enumerate()
+ .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)>>();
+
+ found_lines.sort_unstable_by_key(|(index, _)| *index);
+ found_lines.dedup_by_key(|(index, _)| *index);
+ found_lines
+ } else {
+ line_counts
+ .iter()
+ .enumerate()
+ .map(|(index, count)| (index, *count))
+ .collect::<Vec<(usize, usize)>>()
+ };
+
+ for (index, count) in &found_lines {
+ println!("DA:{},{}", index + 1, count);
+ }
+
+ let lines_hit = found_lines.iter().filter(|(_, count)| *count != 0).count();
println!("LH:{}", lines_hit);
+ let lines_found = found_lines.len();
+ println!("LF:{}", lines_found);
+
println!("end_of_record");
}
+
+ fn done(&mut self) {}
}
pub struct PrettyCoverageReporter {}
@@ -339,45 +419,135 @@ impl PrettyCoverageReporter {
}
}
-const PRETTY_LINE_WIDTH: usize = 4;
-const PRETTY_LINE_SEPERATOR: &str = "|";
impl CoverageReporter for PrettyCoverageReporter {
- fn report_result(
+ fn visit_coverage(
&mut self,
- specifier: &ModuleSpecifier,
- result: &CoverageResult,
- source: &str,
+ script_coverage: &ScriptCoverage,
+ script_source: &str,
+ maybe_source_map: Option<Vec<u8>>,
+ maybe_original_source: Option<Arc<String>>,
) {
- print!("cover {} ... ", specifier);
+ let maybe_source_map = maybe_source_map
+ .map(|source_map| SourceMap::from_slice(&source_map).unwrap());
+
+ let mut ignored_spans: Vec<Span> = Vec::new();
+ for item in deno_ast::lex(script_source, MediaType::JavaScript) {
+ if let deno_ast::TokenOrComment::Token(_) = item.inner {
+ continue;
+ }
- let enumerated_lines = result
- .lines
+ ignored_spans.push(item.span);
+ }
+
+ let lines = script_source.split('\n').collect::<Vec<_>>();
+
+ let line_offsets = {
+ let mut offsets: Vec<(usize, usize)> = Vec::new();
+ let mut index = 0;
+
+ for line in &lines {
+ offsets.push((index, index + line.len() + 1));
+ index += line.len() + 1;
+ }
+
+ offsets
+ };
+
+ // TODO(caspervonb): collect uncovered ranges on the lines so that we can highlight specific
+ // parts of a line in color (word diff style) instead of the entire line.
+ let line_counts = line_offsets
.iter()
.enumerate()
- .collect::<Vec<(usize, &LineCoverage)>>();
+ .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;
+ }
+ }
+ }
+
+ // We reset the count if any block with a zero count overlaps with the line range.
+ for function in &script_coverage.functions {
+ for range in &function.ranges {
+ if range.count > 0 {
+ continue;
+ }
+
+ let overlaps = std::cmp::max(line_end_offset, &range.end_offset)
+ - std::cmp::min(line_start_offset, &range.start_offset)
+ < (line_end_offset - line_start_offset)
+ + (range.end_offset - range.start_offset);
+
+ if overlaps {
+ count = 0;
+ }
+ }
+ }
+
+ (index, count)
+ })
+ .collect::<Vec<(usize, usize)>>();
+
+ let lines = if let Some(original_source) = maybe_original_source.as_ref() {
+ original_source.split('\n').collect::<Vec<_>>()
+ } else {
+ lines
+ };
- let found_lines = enumerated_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
+ };
+
+ print!("cover {} ... ", script_coverage.url);
+
+ let hit_lines = line_counts
.iter()
- .filter(|(_, coverage)| !coverage.ranges.is_empty())
- .cloned()
- .collect::<Vec<(usize, &LineCoverage)>>();
+ .filter(|(_, count)| *count != 0)
+ .map(|(index, _)| *index);
- let missed_lines = found_lines
+ let missed_lines = line_counts
.iter()
- .filter(|(_, coverage)| {
- coverage.ranges.iter().any(|range| range.count == 0)
- })
- .cloned()
- .collect::<Vec<(usize, &LineCoverage)>>();
-
- let line_ratio = (found_lines.len() - missed_lines.len()) as f32
- / found_lines.len() as f32;
- let line_coverage = format!(
- "{:.3}% ({}/{})",
- line_ratio * 100.0,
- found_lines.len() - missed_lines.len(),
- found_lines.len()
- );
+ .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));
@@ -387,31 +557,35 @@ impl CoverageReporter for PrettyCoverageReporter {
println!("{}", colors::red(&line_coverage));
}
- let mut maybe_last_index = None;
- for (index, line) in missed_lines {
- if let Some(last_index) = maybe_last_index {
- if last_index + 1 != index {
- let dash = colors::gray("-".repeat(PRETTY_LINE_WIDTH + 1));
- println!("{}{}{}", dash, colors::gray(PRETTY_LINE_SEPERATOR), dash);
+ let mut last_line = None;
+ for line_index in missed_lines {
+ const WIDTH: usize = 4;
+ const SEPERATOR: &str = "|";
+
+ // Put a horizontal separator between disjoint runs of lines
+ if let Some(last_line) = last_line {
+ if last_line + 1 != line_index {
+ let dash = colors::gray("-".repeat(WIDTH + 1));
+ println!("{}{}{}", dash, colors::gray(SEPERATOR), dash);
}
}
- let slice = &source[line.start_offset..line.end_offset];
-
println!(
"{:width$} {} {}",
- index + 1,
- colors::gray(PRETTY_LINE_SEPERATOR),
- colors::red(&slice),
- width = PRETTY_LINE_WIDTH,
+ line_index + 1,
+ colors::gray(SEPERATOR),
+ colors::red(&lines[line_index]),
+ width = WIDTH
);
- maybe_last_index = Some(index);
+ last_line = Some(line_index);
}
}
+
+ fn done(&mut self) {}
}
-fn collect_script_coverages(
+fn collect_coverages(
files: Vec<PathBuf>,
ignore: Vec<PathBuf>,
) -> Result<Vec<ScriptCoverage>, AnyError> {
@@ -462,7 +636,7 @@ fn collect_script_coverages(
Ok(coverages)
}
-fn filter_script_coverages(
+fn filter_coverages(
coverages: Vec<ScriptCoverage>,
include: Vec<String>,
exclude: Vec<String>,
@@ -488,321 +662,7 @@ fn filter_script_coverages(
.collect::<Vec<ScriptCoverage>>()
}
-fn offset_to_line_col(source: &str, offset: usize) -> Option<(u32, u32)> {
- let mut line = 0;
- let mut col = 0;
-
- if let Some(slice) = source.get(0..offset) {
- for ch in slice.bytes() {
- if ch == b'\n' {
- line += 1;
- col = 0;
- } else {
- col += 1;
- }
- }
-
- return Some((line, col));
- }
-
- None
-}
-
-fn line_col_to_offset(source: &str, line: u32, col: u32) -> Option<usize> {
- let mut current_col = 0;
- let mut current_line = 0;
-
- for (i, ch) in source.bytes().enumerate() {
- if current_line == line && current_col == col {
- return Some(i);
- }
-
- if ch == b'\n' {
- current_line += 1;
- current_col = 0;
- } else {
- current_col += 1;
- }
- }
-
- None
-}
-
-async fn cover_script(
- program_state: ProcState,
- script: ScriptCoverage,
-) -> Result<CoverageResult, AnyError> {
- let module_specifier = resolve_url_or_path(&script.url)?;
- let file = program_state
- .file_fetcher
- .fetch(&module_specifier, &mut Permissions::allow_all())
- .await?;
-
- let source = file.source.as_str();
-
- let line_offsets = {
- let mut line_offsets: Vec<(usize, usize)> = Vec::new();
- let mut offset = 0;
-
- for line in source.split('\n') {
- line_offsets.push((offset, offset + line.len()));
- offset += line.len() + 1;
- }
-
- line_offsets
- };
-
- program_state
- .prepare_module_load(
- module_specifier.clone(),
- TypeLib::UnstableDenoWindow,
- Permissions::allow_all(),
- Permissions::allow_all(),
- false,
- program_state.maybe_import_map.clone(),
- )
- .await?;
-
- let compiled_source =
- program_state.load(module_specifier.clone(), None)?.code;
-
- // TODO(caspervonb): source mapping is still a bit of a mess and we should try look into avoiding
- // doing any loads at this stage of execution but it'll do for now.
- let maybe_raw_source_map = program_state.get_source_map(&script.url);
- if let Some(raw_source_map) = maybe_raw_source_map {
- let source_map = SourceMap::from_slice(&raw_source_map)?;
-
- // To avoid false positives we base our line ranges on the ranges of the compiled lines
- let compiled_line_offsets = {
- let mut line_offsets: Vec<(usize, usize)> = Vec::new();
- let mut offset = 0;
-
- for line in compiled_source.split('\n') {
- line_offsets.push((offset, offset + line.len()));
- offset += line.len() + 1;
- }
-
- line_offsets
- };
-
- // First we get the adjusted ranges of these lines
- let compiled_line_ranges = compiled_line_offsets
- .iter()
- .filter_map(|(start_offset, end_offset)| {
- // We completely ignore empty lines, they just cause trouble and can't map to anything
- // meaningful.
- let line = &compiled_source[*start_offset..*end_offset];
- if line == "\n" {
- return None;
- }
-
- let ranges = script
- .functions
- .iter()
- .map(|function| {
- function.ranges.iter().filter_map(|function_range| {
- if &function_range.start_offset > end_offset {
- return None;
- }
-
- if &function_range.end_offset < start_offset {
- return None;
- }
-
- Some(CoverageRange {
- start_offset: cmp::max(
- *start_offset,
- function_range.start_offset,
- ),
- end_offset: cmp::min(*end_offset, function_range.end_offset),
- count: function_range.count,
- })
- })
- })
- .flatten()
- .collect::<Vec<CoverageRange>>();
-
- Some(ranges)
- })
- .flatten()
- .collect::<Vec<CoverageRange>>();
-
- // Then we map those adjusted ranges from their closest tokens to their source locations.
- let mapped_line_ranges = compiled_line_ranges
- .iter()
- .map(|line_range| {
- let (start_line, start_col) =
- offset_to_line_col(&compiled_source, line_range.start_offset)
- .unwrap();
-
- let start_token =
- source_map.lookup_token(start_line, start_col).unwrap();
-
- let (end_line, end_col) =
- offset_to_line_col(&compiled_source, line_range.end_offset).unwrap();
-
- let end_token = source_map.lookup_token(end_line, end_col).unwrap();
-
- let mapped_start_offset = line_col_to_offset(
- source,
- start_token.get_src_line(),
- start_token.get_src_col(),
- )
- .unwrap();
-
- let mapped_end_offset = line_col_to_offset(
- source,
- end_token.get_src_line(),
- end_token.get_src_col(),
- )
- .unwrap();
-
- CoverageRange {
- start_offset: mapped_start_offset,
- end_offset: mapped_end_offset,
- count: line_range.count,
- }
- })
- .collect::<Vec<CoverageRange>>();
-
- // Then we go through the source lines and grab any ranges that apply to any given line
- // adjusting them as we go.
- let lines = line_offsets
- .iter()
- .map(|(start_offset, end_offset)| {
- let ranges = mapped_line_ranges
- .iter()
- .filter_map(|line_range| {
- if &line_range.start_offset > end_offset {
- return None;
- }
-
- if &line_range.end_offset < start_offset {
- return None;
- }
-
- Some(CoverageRange {
- start_offset: cmp::max(*start_offset, line_range.start_offset),
- end_offset: cmp::min(*end_offset, line_range.end_offset),
- count: line_range.count,
- })
- })
- .collect();
-
- LineCoverage {
- start_offset: *start_offset,
- end_offset: *end_offset,
- ranges,
- }
- })
- .collect();
-
- let functions = script
- .functions
- .iter()
- .map(|function| {
- let ranges = function
- .ranges
- .iter()
- .map(|function_range| {
- let (start_line, start_col) =
- offset_to_line_col(&compiled_source, function_range.start_offset)
- .unwrap();
-
- let start_token =
- source_map.lookup_token(start_line, start_col).unwrap();
-
- let mapped_start_offset = line_col_to_offset(
- source,
- start_token.get_src_line(),
- start_token.get_src_col(),
- )
- .unwrap();
-
- let (end_line, end_col) =
- offset_to_line_col(&compiled_source, function_range.end_offset)
- .unwrap();
-
- let end_token = source_map.lookup_token(end_line, end_col).unwrap();
-
- let mapped_end_offset = line_col_to_offset(
- source,
- end_token.get_src_line(),
- end_token.get_src_col(),
- )
- .unwrap();
-
- CoverageRange {
- start_offset: mapped_start_offset,
- end_offset: mapped_end_offset,
- count: function_range.count,
- }
- })
- .collect();
-
- FunctionCoverage {
- ranges,
- is_block_coverage: function.is_block_coverage,
- function_name: function.function_name.clone(),
- }
- })
- .collect::<Vec<FunctionCoverage>>();
-
- return Ok(CoverageResult { lines, functions });
- }
-
- let functions = script.functions.clone();
-
- let lines = line_offsets
- .iter()
- .map(|(start_offset, end_offset)| {
- let line = &source[*start_offset..*end_offset];
- if line == "\n" {
- return LineCoverage {
- start_offset: *start_offset,
- end_offset: *end_offset,
- ranges: Vec::new(),
- };
- }
-
- let ranges = script
- .functions
- .iter()
- .map(|function| {
- function.ranges.iter().filter_map(|function_range| {
- if &function_range.start_offset > end_offset {
- return None;
- }
-
- if &function_range.end_offset < start_offset {
- return None;
- }
-
- Some(CoverageRange {
- start_offset: cmp::max(
- *start_offset,
- function_range.start_offset,
- ),
- end_offset: cmp::min(*end_offset, function_range.end_offset),
- count: function_range.count,
- })
- })
- })
- .flatten()
- .collect();
-
- LineCoverage {
- start_offset: *start_offset,
- end_offset: *end_offset,
- ranges,
- }
- })
- .collect();
-
- Ok(CoverageResult { lines, functions })
-}
-
-pub async fn cover_scripts(
+pub async fn cover_files(
flags: Flags,
files: Vec<PathBuf>,
ignore: Vec<PathBuf>,
@@ -812,9 +672,8 @@ pub async fn cover_scripts(
) -> Result<(), AnyError> {
let ps = ProcState::build(flags).await?;
- let script_coverages = collect_script_coverages(files, ignore)?;
- let script_coverages =
- filter_script_coverages(script_coverages, include, exclude);
+ let script_coverages = collect_coverages(files, ignore)?;
+ let script_coverages = filter_coverages(script_coverages, include, exclude);
let reporter_kind = if lcov {
CoverageReporterKind::Lcov
@@ -825,16 +684,36 @@ pub async fn cover_scripts(
let mut reporter = create_reporter(reporter_kind);
for script_coverage in script_coverages {
- let result = cover_script(ps.clone(), script_coverage.clone()).await?;
+ let module_specifier =
+ deno_core::resolve_url_or_path(&script_coverage.url)?;
+ ps.prepare_module_load(
+ module_specifier.clone(),
+ TypeLib::UnstableDenoWindow,
+ Permissions::allow_all(),
+ Permissions::allow_all(),
+ false,
+ ps.maybe_import_map.clone(),
+ )
+ .await?;
- let module_specifier = resolve_url_or_path(&script_coverage.url)?;
- let file = ps
- .file_fetcher
- .fetch(&module_specifier, &mut Permissions::allow_all())
- .await?;
+ let module_source = ps.load(module_specifier.clone(), None)?;
+ let script_source = &module_source.code;
- reporter.report_result(&module_specifier, &result, &file.source);
+ let maybe_source_map = ps.get_source_map(&script_coverage.url);
+ let maybe_cached_source = ps
+ .file_fetcher
+ .get_source(&module_specifier)
+ .map(|f| f.source);
+
+ reporter.visit_coverage(
+ &script_coverage,
+ script_source,
+ maybe_source_map,
+ maybe_cached_source,
+ );
}
+ reporter.done();
+
Ok(())
}