Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test_runner: add timeout support to test plan #56765

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

pmarchini
Copy link
Member

This PR attempts to address #56758.

One major concern about this feature: to allow the timeout to interrupt the tests the --test-force-exit flag is required.

I'm opening this PR as a draft to validate the implementation and behaviour.
Documentation still needs to be updated!

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/test_runner

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem. labels Jan 26, 2025
@pmarchini pmarchini requested a review from cjihrig January 26, 2025 00:45
lib/internal/test_runner/test.js Outdated Show resolved Hide resolved

// Wait for the race to finish
await SafePromiseRace(promises);

this[kShouldAbort]();
this.plan?.check();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could do this more simply, and in a way that is less leaky into the rest of the test runner. By this point, the test runner believes that the user's test code has finished executing, although there can still be pending asynchronous activity that the test code scheduled. If the user has provided a plan timeout, then we can race that directly against the existing stopPromise here.

I would also consider renaming timeout to wait, and let that option be a boolean or a number. If it's a boolean, true means wait indefinitely and false means do not wait at all. false could be the default for backwards compatibility purposes. If wait is a number, then the plan would wait for that many milliseconds. All of that logic could live in the TestPlan class, away from the rest of the test runner.

I'm also concerned that with the current implementation, the plan.timeoutPromise serves almost the exact same purpose as the test() timeout option. I think by waiting to arm the timer until this point, it becomes more distinct functionality (that's also partly why I think wait is a better name here).

If the plan has already been satisfied by this point, it also means that we don't have to arm a timer at all, which is more efficient.

Suggested change
this.plan?.check();
if (this.plan !== null) {
// All of the plan waiting/checking logic goes here so that
// only tests that use a plan have the overhead.
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @cjihrig, yes, it seems a great approach!
I'll apply your suggestions as soon as possible!

}
}


Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

@@ -436,6 +532,8 @@ class Test extends AsyncResource {
super('Test');

let { fn, name, parent } = options;
// TODO(pmarchini): The plan has additional options that a user can set via t.plan.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjihrig, wdyt?

Comment on lines +1073 to +1086
ArrayPrototypePush(promises, ret);
} else {
await SafePromiseRace([PromiseResolve(promise), stopPromise]);
ArrayPrototypePush(promises, PromiseResolve(promise));
}
} else {
// This test is synchronous or using Promises.
const promise = ReflectApply(this.runInAsyncScope, this, runArgs);
await SafePromiseRace([PromiseResolve(promise), stopPromise]);
ArrayPrototypePush(promises, PromiseResolve(promise));
}

ArrayPrototypePush(promises, stopPromise);

// Wait for the race to finish
await SafePromiseRace(promises);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, this refactor could enhance readability even though it is no longer needed and could be reverted

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-ci PRs that need a full CI run. test_runner Issues and PRs related to the test runner subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants