summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/integration/test_tests.rs5
-rw-r--r--cli/tests/testdata/test/ops_sanitizer_timeout_failure.out6
-rw-r--r--cli/tests/testdata/test/ops_sanitizer_timeout_failure.ts22
-rw-r--r--runtime/js/40_testing.js35
4 files changed, 67 insertions, 1 deletions
diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs
index 3f23efca2..07c1b6c7d 100644
--- a/cli/tests/integration/test_tests.rs
+++ b/cli/tests/integration/test_tests.rs
@@ -151,6 +151,11 @@ itest!(ops_sanitizer_unstable {
output: "test/ops_sanitizer_unstable.out",
});
+itest!(ops_sanitizer_timeout_failure {
+ args: "test test/ops_sanitizer_timeout_failure.ts",
+ output: "test/ops_sanitizer_timeout_failure.out",
+});
+
itest!(exit_sanitizer {
args: "test test/exit_sanitizer.ts",
output: "test/exit_sanitizer.out",
diff --git a/cli/tests/testdata/test/ops_sanitizer_timeout_failure.out b/cli/tests/testdata/test/ops_sanitizer_timeout_failure.out
new file mode 100644
index 000000000..81a1af63b
--- /dev/null
+++ b/cli/tests/testdata/test/ops_sanitizer_timeout_failure.out
@@ -0,0 +1,6 @@
+Check [WILDCARD]/testdata/test/ops_sanitizer_timeout_failure.ts
+running 1 test from [WILDCARD]/testdata/test/ops_sanitizer_timeout_failure.ts
+test wait ... ok ([WILDCARD])
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD])
+
diff --git a/cli/tests/testdata/test/ops_sanitizer_timeout_failure.ts b/cli/tests/testdata/test/ops_sanitizer_timeout_failure.ts
new file mode 100644
index 000000000..d40a5a258
--- /dev/null
+++ b/cli/tests/testdata/test/ops_sanitizer_timeout_failure.ts
@@ -0,0 +1,22 @@
+let intervalHandle: number;
+let firstIntervalPromise: Promise<void>;
+
+addEventListener("load", () => {
+ firstIntervalPromise = new Promise((resolve) => {
+ let firstIntervalCalled = false;
+ intervalHandle = setInterval(() => {
+ if (!firstIntervalCalled) {
+ resolve();
+ firstIntervalCalled = true;
+ }
+ }, 5);
+ });
+});
+
+addEventListener("unload", () => {
+ clearInterval(intervalHandle);
+});
+
+Deno.test("wait", async function () {
+ await firstIntervalPromise;
+});
diff --git a/runtime/js/40_testing.js b/runtime/js/40_testing.js
index 053afc5da..e8b07d494 100644
--- a/runtime/js/40_testing.js
+++ b/runtime/js/40_testing.js
@@ -30,6 +30,37 @@
} = window.__bootstrap.primordials;
let testStepsEnabled = false;
+ let opSanitizerDelayResolve = null;
+
+ // Even if every resource is closed by the end of a test, there can be a delay
+ // until the pending ops have all finished. This function returns a promise
+ // that resolves when it's (probably) fine to run the op sanitizer.
+ //
+ // This is implemented by adding a macrotask callback that runs after the
+ // timer macrotasks, so we can guarantee that a currently running interval
+ // will have an associated op. An additional `setTimeout` of 0 is needed
+ // before that, though, in order to give time for worker message ops to finish
+ // (since timeouts of 0 don't queue tasks in the timer queue immediately).
+ function opSanitizerDelay() {
+ return new Promise((resolve, reject) => {
+ setTimeout(() => {
+ if (opSanitizerDelayResolve !== null) {
+ reject(new Error("There is an op sanitizer delay already."));
+ } else {
+ opSanitizerDelayResolve = resolve;
+ }
+ }, 0);
+ });
+ }
+
+ function handleOpSanitizerDelayMacrotask() {
+ if (opSanitizerDelayResolve !== null) {
+ opSanitizerDelayResolve();
+ opSanitizerDelayResolve = null;
+ }
+ return true;
+ }
+
// Wrap test function in additional assertion that makes sure
// the test case does not leak async "ops" - ie. number of async
// completed ops after the test is the same as number of dispatched
@@ -45,7 +76,7 @@
// Defer until next event loop turn - that way timeouts and intervals
// cleared can actually be removed from resource table, otherwise
// false positives may occur (https://github.com/denoland/deno/issues/4591)
- await new Promise((resolve) => setTimeout(resolve, 0));
+ await opSanitizerDelay();
}
if (step.shouldSkipSanitizers) {
@@ -466,6 +497,8 @@ finishing test case.`;
filter = null,
shuffle = null,
} = {}) {
+ core.setMacrotaskCallback(handleOpSanitizerDelayMacrotask);
+
const origin = getTestOrigin();
const originalConsole = globalThis.console;