diff --git a/ext/node/analyze.rs b/ext/node/analyze.rs index 3b06a90e08..d80108733a 100644 --- a/ext/node/analyze.rs +++ b/ext/node/analyze.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeSet; use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; @@ -106,7 +107,8 @@ impl NodeCodeTranslator { .to_string(), ]; - let mut all_exports = analysis.exports.into_iter().collect::>(); + // use a BTreeSet to make the output deterministic for v8's code cache + let mut all_exports = analysis.exports.into_iter().collect::>(); if !analysis.reexports.is_empty() { let mut errors = Vec::new(); @@ -159,7 +161,7 @@ impl NodeCodeTranslator { &'a self, entry_specifier: &url::Url, reexports: Vec, - all_exports: &mut HashSet, + all_exports: &mut BTreeSet, // this goes through the modules concurrently, so collect // the errors in order to be deterministic errors: &mut Vec, diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 0d1804a288..b8263be291 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -5043,8 +5043,8 @@ fn run_etag_delete_source_cache() { #[test] fn code_cache_test() { - let deno_dir = TempDir::new(); let test_context = TestContextBuilder::new().use_temp_cwd().build(); + let deno_dir = test_context.deno_dir(); let temp_dir = test_context.temp_dir(); temp_dir.write("main.js", "console.log('Hello World - A');"); @@ -5052,17 +5052,14 @@ fn code_cache_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .arg("run") - .arg("-Ldebug") - .arg("main.js") + .args("run -Ldebug main.js") .split_output() .run(); output .assert_stdout_matches_text("Hello World - A[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]"); - assert!(!output.stderr().contains("V8 code cache hit")); + assert_not_contains!(output.stderr(), "V8 code cache hit"); // Check that the code cache database exists. let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME); @@ -5073,35 +5070,28 @@ fn code_cache_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .arg("run") - .arg("-Ldebug") - .arg("main.js") + .args("run -Ldebug main.js") .split_output() .run(); output .assert_stdout_matches_text("Hello World - A[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/main.js[WILDCARD]"); - assert!(!output.stderr().contains("Updating V8 code cache")); + assert_not_contains!(output.stderr(), "Updating V8 code cache"); } // Rerun with --no-code-cache. { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .arg("run") - .arg("-Ldebug") - .arg("--no-code-cache") - .arg("main.js") + .args("run -Ldebug --no-code-cache main.js") .split_output() .run(); output .assert_stdout_matches_text("Hello World - A[WILDCARD]") .skip_stderr_check(); - assert!(!output.stderr().contains("V8 code cache")); + assert_not_contains!(output.stderr(), "V8 code cache"); } // Modify the script, and make sure that the cache is rejected. @@ -5109,27 +5099,21 @@ fn code_cache_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .arg("run") - .arg("-Ldebug") - .arg("main.js") + .args("run -Ldebug main.js") .split_output() .run(); output .assert_stdout_matches_text("Hello World - B[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]"); - assert!(!output.stderr().contains("V8 code cache hit")); + assert_not_contains!(output.stderr(), "V8 code cache hit"); } } #[test] fn code_cache_npm_test() { - let deno_dir = TempDir::new(); - let test_context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .build(); + let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let deno_dir = test_context.deno_dir(); let temp_dir = test_context.temp_dir(); temp_dir.write( "main.js", @@ -5140,12 +5124,7 @@ fn code_cache_npm_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .envs(env_vars_for_npm_tests()) - .arg("run") - .arg("-Ldebug") - .arg("-A") - .arg("main.js") + .args("run -Ldebug -A main.js") .split_output() .run(); @@ -5153,7 +5132,7 @@ fn code_cache_npm_test() { .assert_stdout_matches_text("Hello World[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/main.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/chalk/5.[WILDCARD]/source/index.js[WILDCARD]"); - assert!(!output.stderr().contains("V8 code cache hit")); + assert_not_contains!(output.stderr(), "V8 code cache hit"); // Check that the code cache database exists. let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME); @@ -5164,12 +5143,7 @@ fn code_cache_npm_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .envs(env_vars_for_npm_tests()) - .arg("run") - .arg("-Ldebug") - .arg("-A") - .arg("main.js") + .args("run -Ldebug -A main.js") .split_output() .run(); @@ -5177,17 +5151,14 @@ fn code_cache_npm_test() { .assert_stdout_matches_text("Hello World[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/main.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/chalk/5.[WILDCARD]/source/index.js[WILDCARD]"); - assert!(!output.stderr().contains("Updating V8 code cache")); + assert_not_contains!(output.stderr(), "Updating V8 code cache"); } } #[test] fn code_cache_npm_with_require_test() { - let deno_dir = TempDir::new(); - let test_context = TestContextBuilder::new() - .use_temp_cwd() - .use_http_server() - .build(); + let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let deno_dir = test_context.deno_dir(); let temp_dir = test_context.temp_dir(); temp_dir.write( "main.js", @@ -5198,12 +5169,7 @@ fn code_cache_npm_with_require_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .envs(env_vars_for_npm_tests()) - .arg("run") - .arg("-Ldebug") - .arg("-A") - .arg("main.js") + .args("run -Ldebug -A main.js") .split_output() .run(); @@ -5213,7 +5179,7 @@ fn code_cache_npm_with_require_test() { .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for ES module: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for script: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]Updating V8 code cache for script: file:///[WILDCARD]/browserslist/[WILDCARD]/index.js[WILDCARD]"); - assert!(!output.stderr().contains("V8 code cache hit")); + assert_not_contains!(output.stderr(), "V8 code cache hit"); // Check that the code cache database exists. let code_cache_path = deno_dir.path().join(CODE_CACHE_DB_FILE_NAME); @@ -5224,12 +5190,7 @@ fn code_cache_npm_with_require_test() { { let output = test_context .new_command() - .env("DENO_DIR", deno_dir.path()) - .envs(env_vars_for_npm_tests()) - .arg("run") - .arg("-Ldebug") - .arg("-A") - .arg("main.js") + .args("run -Ldebug -A main.js") .split_output() .run(); @@ -5239,7 +5200,54 @@ fn code_cache_npm_with_require_test() { .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for ES module: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for script: file:///[WILDCARD]/autoprefixer/[WILDCARD]/autoprefixer.js[WILDCARD]") .assert_stderr_matches_text("[WILDCARD]V8 code cache hit for script: file:///[WILDCARD]/browserslist/[WILDCARD]/index.js[WILDCARD]"); - assert!(!output.stderr().contains("Updating V8 code cache")); + assert_not_contains!(output.stderr(), "Updating V8 code cache"); + } +} + +#[test] +fn code_cache_npm_cjs_wrapper_module_many_exports() { + // The code cache was being invalidated because the CJS wrapper module + // had indeterministic output. + let test_context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let temp_dir = test_context.temp_dir(); + temp_dir.write( + "main.js", + // this package has a few exports + "import { hello } from \"npm:@denotest/cjs-reexport-collision\";hello.sayHello();", + ); + + // First run with no prior cache. + { + let output = test_context + .new_command() + .args("run -Ldebug -A main.js") + .split_output() + .run(); + + assert_not_contains!(output.stderr(), "V8 code cache hit"); + assert_contains!(output.stderr(), "Updating V8 code cache"); + output.skip_stdout_check(); + } + + // 2nd run with cache. + { + let output = test_context + .new_command() + .args("run -Ldebug -A main.js") + .split_output() + .run(); + assert_contains!(output.stderr(), "V8 code cache hit"); + assert_not_contains!(output.stderr(), "Updating V8 code cache"); + output.skip_stdout_check(); + + // should have two occurrences of this (one for entrypoint and one for wrapper module) + assert_eq!( + output + .stderr() + .split("V8 code cache hit for ES module") + .count(), + 3 + ); } }