1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

fix(coverage): Error if the emit cache is invalid (#16850)

This commit is contained in:
sigmaSd 2022-11-29 18:43:54 +01:00 committed by GitHub
parent e4fe5ee72a
commit 2656af2544
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 23 deletions

25
cli/cache/emit.rs vendored
View file

@ -44,7 +44,7 @@ impl EmitCache {
pub fn get_emit_code( pub fn get_emit_code(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
expected_source_hash: Option<u64>, expected_source_hash: u64,
) -> Option<String> { ) -> Option<String> {
let meta_filename = self.get_meta_filename(specifier)?; let meta_filename = self.get_meta_filename(specifier)?;
let emit_filename = self.get_emit_filename(specifier)?; let emit_filename = self.get_emit_filename(specifier)?;
@ -52,11 +52,9 @@ impl EmitCache {
// load and verify the meta data file is for this source and CLI version // load and verify the meta data file is for this source and CLI version
let bytes = self.disk_cache.get(&meta_filename).ok()?; let bytes = self.disk_cache.get(&meta_filename).ok()?;
let meta: EmitMetadata = serde_json::from_slice(&bytes).ok()?; let meta: EmitMetadata = serde_json::from_slice(&bytes).ok()?;
if let Some(expected_source_hash) = expected_source_hash {
if meta.source_hash != expected_source_hash.to_string() { if meta.source_hash != expected_source_hash.to_string() {
return None; return None;
} }
}
// load and verify the emit is for the meta data // load and verify the emit is for the meta data
let emit_bytes = self.disk_cache.get(&emit_filename).ok()?; let emit_bytes = self.disk_cache.get(&emit_filename).ok()?;
@ -173,31 +171,26 @@ mod test {
let specifier2 = let specifier2 =
ModuleSpecifier::from_file_path(temp_dir.path().join("file2.ts")) ModuleSpecifier::from_file_path(temp_dir.path().join("file2.ts"))
.unwrap(); .unwrap();
assert_eq!(cache.get_emit_code(&specifier1, Some(1)), None); assert_eq!(cache.get_emit_code(&specifier1, 1), None);
let emit_code1 = "text1".to_string(); let emit_code1 = "text1".to_string();
let emit_code2 = "text2".to_string(); let emit_code2 = "text2".to_string();
cache.set_emit_code(&specifier1, 10, &emit_code1); cache.set_emit_code(&specifier1, 10, &emit_code1);
cache.set_emit_code(&specifier2, 2, &emit_code2); cache.set_emit_code(&specifier2, 2, &emit_code2);
// providing the incorrect source hash // providing the incorrect source hash
assert_eq!(cache.get_emit_code(&specifier1, Some(5)), None); assert_eq!(cache.get_emit_code(&specifier1, 5), None);
// providing the correct source hash // providing the correct source hash
assert_eq!( assert_eq!(
cache.get_emit_code(&specifier1, Some(10)), cache.get_emit_code(&specifier1, 10),
Some(emit_code1.clone()),
);
assert_eq!(cache.get_emit_code(&specifier2, Some(2)), Some(emit_code2));
// providing no hash
assert_eq!(
cache.get_emit_code(&specifier1, None),
Some(emit_code1.clone()), Some(emit_code1.clone()),
); );
assert_eq!(cache.get_emit_code(&specifier2, 2), Some(emit_code2));
// try changing the cli version (should not load previous ones) // try changing the cli version (should not load previous ones)
let cache = EmitCache { let cache = EmitCache {
disk_cache: disk_cache.clone(), disk_cache: disk_cache.clone(),
cli_version: "2.0.0".to_string(), cli_version: "2.0.0".to_string(),
}; };
assert_eq!(cache.get_emit_code(&specifier1, Some(10)), None); assert_eq!(cache.get_emit_code(&specifier1, 10), None);
cache.set_emit_code(&specifier1, 5, &emit_code1); cache.set_emit_code(&specifier1, 5, &emit_code1);
// recreating the cache should still load the data because the CLI version is the same // recreating the cache should still load the data because the CLI version is the same
@ -205,12 +198,12 @@ mod test {
disk_cache, disk_cache,
cli_version: "2.0.0".to_string(), cli_version: "2.0.0".to_string(),
}; };
assert_eq!(cache.get_emit_code(&specifier1, Some(5)), Some(emit_code1)); assert_eq!(cache.get_emit_code(&specifier1, 5), Some(emit_code1));
// adding when already exists should not cause issue // adding when already exists should not cause issue
let emit_code3 = "asdf".to_string(); let emit_code3 = "asdf".to_string();
cache.set_emit_code(&specifier1, 20, &emit_code3); cache.set_emit_code(&specifier1, 20, &emit_code3);
assert_eq!(cache.get_emit_code(&specifier1, Some(5)), None); assert_eq!(cache.get_emit_code(&specifier1, 5), None);
assert_eq!(cache.get_emit_code(&specifier1, Some(20)), Some(emit_code3)); assert_eq!(cache.get_emit_code(&specifier1, 20), Some(emit_code3));
} }
} }

View file

@ -12,7 +12,7 @@ use std::sync::Arc;
/// A hashing function that takes the source code and emit options /// A hashing function that takes the source code and emit options
/// hash then generates a string hash which can be stored to /// hash then generates a string hash which can be stored to
/// determine if the cached emit is valid or not. /// determine if the cached emit is valid or not.
fn get_source_hash(source_text: &str, emit_options_hash: u64) -> u64 { pub fn get_source_hash(source_text: &str, emit_options_hash: u64) -> u64 {
FastInsecureHasher::new() FastInsecureHasher::new()
.write_str(source_text) .write_str(source_text)
.write_u64(emit_options_hash) .write_u64(emit_options_hash)
@ -30,9 +30,7 @@ pub fn emit_parsed_source(
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
let source_hash = get_source_hash(source, emit_config_hash); let source_hash = get_source_hash(source, emit_config_hash);
if let Some(emit_code) = if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) {
emit_cache.get_emit_code(specifier, Some(source_hash))
{
Ok(emit_code) Ok(emit_code)
} else { } else {
// this will use a cached version if it exists // this will use a cached version if it exists

View file

@ -26,6 +26,68 @@ mod coverage {
no_snaps_included("no_snaps_included", "ts"); no_snaps_included("no_snaps_included", "ts");
} }
#[test]
fn error_if_invalid_cache() {
let deno_dir = TempDir::new();
let deno_dir_path = deno_dir.path();
let tempdir = TempDir::new();
let tempdir = tempdir.path().join("cov");
let invalid_cache_path =
util::testdata_path().join("coverage/invalid_cache");
let mod_before_path = util::testdata_path()
.join(&invalid_cache_path)
.join("mod_before.ts");
let mod_after_path = util::testdata_path()
.join(&invalid_cache_path)
.join("mod_after.ts");
let mod_test_path = util::testdata_path()
.join(&invalid_cache_path)
.join("mod.test.ts");
let mod_temp_path = deno_dir_path.join("mod.ts");
let mod_test_temp_path = deno_dir_path.join("mod.test.ts");
// Write the inital mod.ts file
std::fs::copy(mod_before_path, &mod_temp_path).unwrap();
// And the test file
std::fs::copy(mod_test_path, &mod_test_temp_path).unwrap();
// Generate coverage
let status = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(deno_dir_path)
.arg("test")
.arg("--quiet")
.arg(format!("--coverage={}", tempdir.to_str().unwrap()))
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
.status()
.unwrap();
assert!(status.success());
// Modify the file between deno test and deno coverage, thus invalidating the cache
std::fs::copy(mod_after_path, mod_temp_path).unwrap();
let output = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(deno_dir_path)
.arg("coverage")
.arg(format!("{}/", tempdir.to_str().unwrap()))
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.unwrap();
assert!(output.stdout.is_empty());
// Expect error
let error =
util::strip_ansi_codes(std::str::from_utf8(&output.stderr).unwrap())
.to_string();
assert!(error.contains("error: Missing transpiled source code"));
assert!(error.contains("Before generating coverage report, run `deno test --coverage` to ensure consistent state."));
}
fn run_coverage_text(test_name: &str, extension: &str) { fn run_coverage_text(test_name: &str, extension: &str) {
let deno_dir = TempDir::new(); let deno_dir = TempDir::new();
let tempdir = TempDir::new(); let tempdir = TempDir::new();

View file

@ -0,0 +1,2 @@
import { test } from "./mod.ts";
Deno.test("test", () => void test());

View file

@ -0,0 +1,6 @@
export function test() {
return 42;
}
if (import.meta.main) {
test();
}

View file

@ -0,0 +1,15 @@
export function test() {
console.log("1");
console.log("2");
console.log("3");
console.log("4");
console.log("5");
console.log("6");
console.log("7");
console.log("8");
console.log("9");
return 42;
}
if (import.meta.main) {
test();
}

View file

@ -3,6 +3,7 @@
use crate::args::CoverageFlags; use crate::args::CoverageFlags;
use crate::args::Flags; use crate::args::Flags;
use crate::colors; use crate::colors;
use crate::emit::get_source_hash;
use crate::proc_state::ProcState; use crate::proc_state::ProcState;
use crate::tools::fmt::format_json; use crate::tools::fmt::format_json;
use crate::util::fs::collect_files; use crate::util::fs::collect_files;
@ -677,7 +678,8 @@ pub async fn cover_files(
| MediaType::Mts | MediaType::Mts
| MediaType::Cts | MediaType::Cts
| MediaType::Tsx => { | MediaType::Tsx => {
match ps.emit_cache.get_emit_code(&file.specifier, None) { let source_hash = get_source_hash(&file.source, ps.emit_options_hash);
match ps.emit_cache.get_emit_code(&file.specifier, source_hash) {
Some(code) => code, Some(code) => code,
None => { None => {
return Err(anyhow!( return Err(anyhow!(