diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index 2203bbd3497659..97d53c097261d7 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -149,7 +149,7 @@ function stopTest(timeout, signal) { disposeFunction = () => { abortListener[SymbolDispose](); - timer[SymbolDispose](); + clearTimeout(timer); }; } diff --git a/test/fixtures/test-runner/mock-timers-with-timeout.js b/test/fixtures/test-runner/mock-timers-with-timeout.js new file mode 100644 index 00000000000000..4eb94ec5d6d8e9 --- /dev/null +++ b/test/fixtures/test-runner/mock-timers-with-timeout.js @@ -0,0 +1,43 @@ +'use strict'; + +// Simulate @sinonjs/fake-timers: patch the timers module BEFORE +// the test runner is loaded, so the test runner captures the patched +// versions at import time. +const nodeTimers = require('node:timers'); +const originalSetTimeout = nodeTimers.setTimeout; +const originalClearTimeout = nodeTimers.clearTimeout; + +const fakeTimers = new Map(); +let nextId = 1; + +nodeTimers.setTimeout = (fn, delay, ...args) => { + const id = nextId++; + const timer = originalSetTimeout(fn, delay, ...args); + fakeTimers.set(id, timer); + // Sinon fake timers return an object with unref/ref but without + // Symbol.dispose, which would cause the test runner to throw. + return { id, unref() {}, ref() {} }; +}; + +nodeTimers.clearTimeout = (id) => { + if (id != null && typeof id === 'object') id = id.id; + const timer = fakeTimers.get(id); + if (timer) { + originalClearTimeout(timer); + fakeTimers.delete(id); + } +}; + +// Now load the test runner - it will capture our patched setTimeout/clearTimeout +const { test } = require('node:test'); + +test('test with fake timers and timeout', { timeout: 10_000 }, () => { + // This test verifies that the test runner works when setTimeout returns + // an object without Symbol.dispose (like sinon fake timers). + // Previously, the test runner called timer[Symbol.dispose]() which would + // throw TypeError on objects returned by fake timer implementations. +}); + +// Restore +nodeTimers.setTimeout = originalSetTimeout; +nodeTimers.clearTimeout = originalClearTimeout; diff --git a/test/parallel/test-runner-mock-timers-with-timeout.js b/test/parallel/test-runner-mock-timers-with-timeout.js new file mode 100644 index 00000000000000..67f266851fe1ed --- /dev/null +++ b/test/parallel/test-runner-mock-timers-with-timeout.js @@ -0,0 +1,14 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('node:assert'); +const { spawnSync } = require('node:child_process'); +const { test } = require('node:test'); + +test('mock timers do not break test timeout cleanup', async () => { + const fixture = fixtures.path('test-runner', 'mock-timers-with-timeout.js'); + const cp = spawnSync(process.execPath, ['--test', fixture], { + timeout: 30_000, + }); + assert.strictEqual(cp.status, 0, `Test failed:\nstdout: ${cp.stdout}\nstderr: ${cp.stderr}`); +}); diff --git a/tools/v8/fetch_deps.py b/tools/v8/fetch_deps.py index 728e48eb14e899..055ffb50eafba5 100755 --- a/tools/v8/fetch_deps.py +++ b/tools/v8/fetch_deps.py @@ -24,6 +24,8 @@ "deps_file" : "DEPS", "managed" : False, "custom_deps" : { + # Update depot_tools for compatibility with Python 3.12. + "v8/third_party/depot_tools" : "https://chromium.googlesource.com/chromium/tools/depot_tools.git@284c5ccb591c3de4e9f71be4a4beb5d1916d5383", # These deps are already part of Node.js. "v8/base/trace_event/common" : None, "v8/third_party/abseil-cpp" : None,