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:
parent
e4fe5ee72a
commit
2656af2544
7 changed files with 101 additions and 23 deletions
25
cli/cache/emit.rs
vendored
25
cli/cache/emit.rs
vendored
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
2
cli/tests/testdata/coverage/invalid_cache/mod.test.ts
vendored
Normal file
2
cli/tests/testdata/coverage/invalid_cache/mod.test.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import { test } from "./mod.ts";
|
||||||
|
Deno.test("test", () => void test());
|
6
cli/tests/testdata/coverage/invalid_cache/mod_after.ts
vendored
Normal file
6
cli/tests/testdata/coverage/invalid_cache/mod_after.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export function test() {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
if (import.meta.main) {
|
||||||
|
test();
|
||||||
|
}
|
15
cli/tests/testdata/coverage/invalid_cache/mod_before.ts
vendored
Normal file
15
cli/tests/testdata/coverage/invalid_cache/mod_before.ts
vendored
Normal 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();
|
||||||
|
}
|
|
@ -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!(
|
||||||
|
|
Loading…
Reference in a new issue