mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -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
29
cli/cache/emit.rs
vendored
29
cli/cache/emit.rs
vendored
|
@ -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,10 +52,8 @@ 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;
|
||||
}
|
||||
if meta.source_hash != expected_source_hash.to_string() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// load and verify the emit is for the meta data
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
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::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!(
|
||||
|
|
Loading…
Reference in a new issue