From 12835d77accfe2852a673295a4dd1fb02f93ca0b Mon Sep 17 00:00:00 2001 From: Adam Yost Date: Tue, 13 Dec 2022 11:20:14 -0700 Subject: [PATCH] add simple repro test for async setInterruptHandler --- ts/quickjs.test.ts | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/ts/quickjs.test.ts b/ts/quickjs.test.ts index 6bd9f582..e3749b74 100644 --- a/ts/quickjs.test.ts +++ b/ts/quickjs.test.ts @@ -747,6 +747,9 @@ function contextTests(getContext: () => Promise) { function asyncContextTests(getContext: () => Promise) { let vm: QuickJSAsyncContext = undefined as any + let testId = 0 + + const getTestId = () => `test-${getContext.name}-${testId}` beforeEach(async () => { vm = await getContext() @@ -909,6 +912,95 @@ function asyncContextTests(getContext: () => Promise) { ) }) }) + + describe("interrupt handler", () => { + it("is called with the expected VM", async () => { + let calls = 0 + const interruptId = getTestId() + const interruptHandler: InterruptHandler = (interruptVm) => { + assert.strictEqual( + interruptVm, + vm.runtime, + "ShouldInterruptHandler callback runtime is the runtime" + ) + debugLog("interruptHandler called", interruptId) + calls++ + return false + } + + debugLog("setInterruptHandler", interruptId) + vm.runtime.setInterruptHandler(interruptHandler) + const result = await vm.evalCodeAsync("1 + 1") + vm.unwrapResult(result).dispose() + + assert(calls > 0, "interruptHandler called at least once") + }) + + it("is called regularly as the code executes", async () => { + let calls = 0 + const interruptId = getTestId() + const interruptHandler: InterruptHandler = (interruptVm) => { + assert.strictEqual( + interruptVm, + vm.runtime, + "ShouldInterruptHandler callback runtime is the runtime" + ) + debugLog("interruptHandler called", interruptId) + calls++ + return false + } + + debugLog("setInterruptHandler", interruptId) + + const delayUtil = (ms = 0): Promise => { + return new Promise((resolve, _reject) => { + const setTimeoutHandle = setTimeout(() => { + clearTimeout(setTimeoutHandle) + return resolve() + }, ms) + }) + } + + const delayFunc = vm.newFunction('$delay', (ms) => { + const delayMs = vm.getNumber(ms) ?? 0 + const deferred = vm.newPromise() + delayUtil(delayMs).then( + (_) => { + deferred.resolve() + while (vm.runtime.hasPendingJob()) { + vm.runtime.executePendingJobs() + } + }, + (err) => deferred.reject(err) + ) + return deferred.handle + }) + + delayFunc.consume((f) => vm.setProp(vm.global, '$delay', f)) + + const deferred: QuickJSDeferredPromise = vm.newPromise() + const asyncDone = vm.newFunction('done', (err, results) => { + if (err) { + deferred.reject(err) + } else { + deferred.resolve(results) + } + return deferred.handle + }) + + asyncDone.consume((f) => vm.setProp(vm.global, 'done', f)) + + vm.runtime.setInterruptHandler(interruptHandler) + const result = await vm.evalCodeAsync("$delay(3000).then((_, err) => done(err, _))") + while (vm.runtime.hasPendingJob()) { + vm.runtime.executePendingJobs() + } + await deferred.settled + vm.unwrapResult(result).dispose() + + assert(calls > 1, `interruptHandler called ${calls} time(s)`) + }) + }) } describe("QuickJSContext", function () {