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(
&self,
specifier: &ModuleSpecifier,
expected_source_hash: Option<u64>,
expected_source_hash: u64,
) -> Option<String> {
let meta_filename = self.get_meta_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
let bytes = self.disk_cache.get(&meta_filename).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() {
return None;
}
}
// load and verify the emit is for the meta data
let emit_bytes = self.disk_cache.get(&emit_filename).ok()?;
@ -173,31 +171,26 @@ mod test {
let specifier2 =
ModuleSpecifier::from_file_path(temp_dir.path().join("file2.ts"))
.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_code2 = "text2".to_string();
cache.set_emit_code(&specifier1, 10, &emit_code1);
cache.set_emit_code(&specifier2, 2, &emit_code2);
// 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
assert_eq!(
cache.get_emit_code(&specifier1, Some(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),
cache.get_emit_code(&specifier1, 10),
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)
let cache = EmitCache {
disk_cache: disk_cache.clone(),
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);
// recreating the cache should still load the data because the CLI version is the same
@ -205,12 +198,12 @@ mod test {
disk_cache,
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
let emit_code3 = "asdf".to_string();
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, Some(20)), Some(emit_code3));
assert_eq!(cache.get_emit_code(&specifier1, 5), None);
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
/// hash then generates a string hash which can be stored to
/// 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()
.write_str(source_text)
.write_u64(emit_options_hash)
@ -30,9 +30,7 @@ pub fn emit_parsed_source(
) -> Result<String, AnyError> {
let source_hash = get_source_hash(source, emit_config_hash);
if let Some(emit_code) =
emit_cache.get_emit_code(specifier, Some(source_hash))
{
if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) {
Ok(emit_code)
} else {
// this will use a cached version if it exists

View file

@ -26,6 +26,68 @@ mod coverage {
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) {
let deno_dir = 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::Flags;
use crate::colors;
use crate::emit::get_source_hash;
use crate::proc_state::ProcState;
use crate::tools::fmt::format_json;
use crate::util::fs::collect_files;
@ -677,7 +678,8 @@ pub async fn cover_files(
| MediaType::Mts
| MediaType::Cts
| 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,
None => {
return Err(anyhow!(