From 5cf194380bcad0fd763fb0893ec49bfc91fda15d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Thu, 28 Jan 2021 22:11:38 +0800 Subject: [PATCH] fix(coverage): use source maps when printing pretty reports (#9278) This commits makes use of source maps and the original source when printing lacking line coverage in the pretty printer. Only the executable lines are checked as before (as non-executable lines will always be ignored anyways). The lines then mapped to the appropriate source line when a source map is present. --- cli/tests/test_branch_coverage.out | 6 +-- cli/tests/test_coverage.out | 33 +++++++------- cli/tests/test_run_combined_coverage.out | 21 +++++---- cli/tests/test_run_run_coverage.out | 35 +++++++------- cli/tests/test_run_test_coverage.out | 39 ++++++++-------- cli/tools/coverage.rs | 58 ++++++++++++++++++++++-- 6 files changed, 125 insertions(+), 67 deletions(-) diff --git a/cli/tests/test_branch_coverage.out b/cli/tests/test_branch_coverage.out index 69e81f8818..e52c4bac0b 100644 --- a/cli/tests/test_branch_coverage.out +++ b/cli/tests/test_branch_coverage.out @@ -5,6 +5,6 @@ test branch ... ok ([WILDCARD]) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD]) cover [WILDCARD]/tests/subdir/branch.ts ... 66.667% (6/9) - 5 | else { - 6 | return false; - 7 | } + 4 | } else { + 5 | return false; + 6 | } diff --git a/cli/tests/test_coverage.out b/cli/tests/test_coverage.out index b8423e7fd7..709d57d3b3 100644 --- a/cli/tests/test_coverage.out +++ b/cli/tests/test_coverage.out @@ -4,22 +4,23 @@ test returnsFooSuccess ... ok ([WILDCARD]) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD]) -cover [WILDCARD]/cli/tests/subdir/mod1.ts ... 35.714% (5/14) - 2 | export function returnsHi() { - 3 | return "Hi"; - 4 | } +cover [WILDCARD]/tests/subdir/mod1.ts ... 35.714% (5/14) + 3 | export function returnsHi(): string { + 4 | return "Hi"; + 5 | } -----|----- - 8 | export function printHello3() { - 9 | printHello2(); - 10 | } - 11 | export function throwsError() { - 12 | throw Error("exception from mod1"); + 11 | export function printHello3(): void { + 12 | printHello2(); 13 | } -cover [WILDCARD]/cli/tests/subdir/print_hello.ts ... 25.000% (1/4) - 1 | export function printHello() { - 2 | console.log("Hello"); +-----|----- + 15 | export function throwsError(): void { + 16 | throw Error("exception from mod1"); + 17 | } +cover [WILDCARD]/tests/subdir/print_hello.ts ... 25.000% (1/4) + 1 | export function printHello(): void { + 2 | console.log("Hello"); 3 | } -cover [WILDCARD]/cli/tests/subdir/subdir2/mod2.ts ... 62.500% (5/8) - 5 | export function printHello2() { - 6 | printHello(); - 7 | } +cover [WILDCARD]/tests/subdir/subdir2/mod2.ts ... 62.500% (5/8) + 7 | export function printHello2(): void { + 8 | printHello(); + 9 | } diff --git a/cli/tests/test_run_combined_coverage.out b/cli/tests/test_run_combined_coverage.out index 476e9adcc7..24ceb9955c 100644 --- a/cli/tests/test_run_combined_coverage.out +++ b/cli/tests/test_run_combined_coverage.out @@ -14,18 +14,19 @@ test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WIL cover [WILDCARD]/tests/run_coverage.ts ... 100.000% (3/3) cover [WILDCARD]/tests/subdir/mod1.ts ... 57.143% (8/14) - 8 | export function printHello3() { - 9 | printHello2(); - 10 | } - 11 | export function throwsError() { - 12 | throw Error("exception from mod1"); + 11 | export function printHello3(): void { + 12 | printHello2(); 13 | } +-----|----- + 15 | export function throwsError(): void { + 16 | throw Error("exception from mod1"); + 17 | } cover [WILDCARD]/tests/subdir/print_hello.ts ... 25.000% (1/4) - 1 | export function printHello() { - 2 | console.log("Hello"); + 1 | export function printHello(): void { + 2 | console.log("Hello"); 3 | } cover [WILDCARD]/tests/subdir/subdir2/mod2.ts ... 62.500% (5/8) - 5 | export function printHello2() { - 6 | printHello(); - 7 | } + 7 | export function printHello2(): void { + 8 | printHello(); + 9 | } cover [WILDCARD]/tests/test_coverage.ts ... 100.000% (5/5) diff --git a/cli/tests/test_run_run_coverage.out b/cli/tests/test_run_run_coverage.out index 2f18020d3f..97eb292640 100644 --- a/cli/tests/test_run_run_coverage.out +++ b/cli/tests/test_run_run_coverage.out @@ -7,23 +7,26 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WIL cover [WILDCARD]/tests/run_coverage.ts ... 100.000% (3/3) cover [WILDCARD]/tests/subdir/mod1.ts ... 35.714% (5/14) - 5 | export function returnsFoo2() { - 6 | return returnsFoo(); - 7 | } - 8 | export function printHello3() { - 9 | printHello2(); - 10 | } - 11 | export function throwsError() { - 12 | throw Error("exception from mod1"); + 7 | export function returnsFoo2(): string { + 8 | return returnsFoo(); + 9 | } +-----|----- + 11 | export function printHello3(): void { + 12 | printHello2(); 13 | } +-----|----- + 15 | export function throwsError(): void { + 16 | throw Error("exception from mod1"); + 17 | } cover [WILDCARD]/tests/subdir/print_hello.ts ... 25.000% (1/4) - 1 | export function printHello() { - 2 | console.log("Hello"); + 1 | export function printHello(): void { + 2 | console.log("Hello"); 3 | } cover [WILDCARD]/tests/subdir/subdir2/mod2.ts ... 25.000% (2/8) - 2 | export function returnsFoo() { - 3 | return "Foo"; - 4 | } - 5 | export function printHello2() { - 6 | printHello(); - 7 | } + 3 | export function returnsFoo(): string { + 4 | return "Foo"; + 5 | } +-----|----- + 7 | export function printHello2(): void { + 8 | printHello(); + 9 | } diff --git a/cli/tests/test_run_test_coverage.out b/cli/tests/test_run_test_coverage.out index bcaae01a12..186ecba139 100644 --- a/cli/tests/test_run_test_coverage.out +++ b/cli/tests/test_run_test_coverage.out @@ -1,6 +1,6 @@ -Check [WILDCARD]/$deno$test.ts +Check [WILDCARD]/tests/$deno$test.ts running 1 tests -test spawn test ... Check [WILDCARD]/$deno$test.ts +test spawn test ... Check [WILDCARD]/tests/$deno$test.ts running 1 tests test returnsFooSuccess ... ok ([WILDCARD]) @@ -10,23 +10,24 @@ ok ([WILDCARD]) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD]) -cover [WILDCARD]/subdir/mod1.ts ... 35.714% (5/14) - 2 | export function returnsHi() { - 3 | return "Hi"; - 4 | } +cover [WILDCARD]/tests/subdir/mod1.ts ... 35.714% (5/14) + 3 | export function returnsHi(): string { + 4 | return "Hi"; + 5 | } -----|----- - 8 | export function printHello3() { - 9 | printHello2(); - 10 | } - 11 | export function throwsError() { - 12 | throw Error("exception from mod1"); + 11 | export function printHello3(): void { + 12 | printHello2(); 13 | } -cover [WILDCARD]/subdir/print_hello.ts ... 25.000% (1/4) - 1 | export function printHello() { - 2 | console.log("Hello"); +-----|----- + 15 | export function throwsError(): void { + 16 | throw Error("exception from mod1"); + 17 | } +cover [WILDCARD]/tests/subdir/print_hello.ts ... 25.000% (1/4) + 1 | export function printHello(): void { + 2 | console.log("Hello"); 3 | } -cover [WILDCARD]/subdir/subdir2/mod2.ts ... 62.500% (5/8) - 5 | export function printHello2() { - 6 | printHello(); - 7 | } -cover [WILDCARD]/test_coverage.ts ... 100.000% (5/5) +cover [WILDCARD]/tests/subdir/subdir2/mod2.ts ... 62.500% (5/8) + 7 | export function printHello2(): void { + 8 | printHello(); + 9 | } +cover [WILDCARD]/tests/test_coverage.ts ... 100.000% (5/5) diff --git a/cli/tools/coverage.rs b/cli/tools/coverage.rs index e3092975fb..b7d64e8f26 100644 --- a/cli/tools/coverage.rs +++ b/cli/tools/coverage.rs @@ -6,6 +6,7 @@ use crate::colors; use crate::media_type::MediaType; use crate::module_graph::TypeLib; use crate::program_state::ProgramState; +use crate::source_maps::SourceMapGetter; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::json; @@ -15,6 +16,7 @@ use deno_runtime::inspector::InspectorSession; use deno_runtime::permissions::Permissions; use serde::Deserialize; use serde::Serialize; +use sourcemap::SourceMap; use std::fs; use std::path::PathBuf; use std::sync::Arc; @@ -130,7 +132,15 @@ impl PrettyCoverageReporter { &mut self, script_coverage: &ScriptCoverage, script_source: &str, + maybe_source_map: Option>, + maybe_original_source: Option, ) { + let maybe_source_map = if let Some(source_map) = maybe_source_map { + Some(SourceMap::from_slice(&source_map).unwrap()) + } else { + None + }; + let mut ignored_spans: Vec = Vec::new(); for item in ast::lex("", script_source, &MediaType::JavaScript) { if let TokenOrComment::Token(_) = item.inner { @@ -221,8 +231,39 @@ impl PrettyCoverageReporter { println!("{}", colors::red(&line_coverage)); } + let output_lines = + if let Some(original_source) = maybe_original_source.as_ref() { + original_source.split('\n').collect::>() + } 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::>(); + + indices.sort_unstable(); + indices.dedup(); + + indices + } else { + uncovered_lines + }; + let mut last_line = None; - for line_index in uncovered_lines { + for line_index in output_indices { const WIDTH: usize = 4; const SEPERATOR: &str = "|"; @@ -238,7 +279,7 @@ impl PrettyCoverageReporter { "{:width$} {} {}", line_index + 1, colors::gray(SEPERATOR), - colors::red(&lines[line_index]), + colors::red(&output_lines[line_index]), width = WIDTH ); @@ -354,7 +395,18 @@ pub async fn report_coverages( let module_source = program_state.load(module_specifier.clone(), None)?; let script_source = &module_source.code; - coverage_reporter.visit_coverage(&script_coverage, &script_source); + let maybe_source_map = program_state.get_source_map(&script_coverage.url); + let maybe_cached_source = program_state + .file_fetcher + .get_source(&module_specifier) + .map(|f| f.source); + + coverage_reporter.visit_coverage( + &script_coverage, + &script_source, + maybe_source_map, + maybe_cached_source, + ); } Ok(())