-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Draft: list 'pending' tests #1596
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
Conversation
Nice work @ace-n! Test state jumps through a lot of hoops on its way to the main process so well done following it all. I'd like to suggest a slightly different approach though, one that is a bit more explicit and requires fewer low-level changes. See the inline comments below. Let me know what you think! |
lib/runner.js
Outdated
@@ -146,17 +147,22 @@ class Runner extends EventEmitter { | |||
} | |||
|
|||
buildStats() { | |||
this.tests.finalize(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work given that TestCollection#finalize()
does the finalizing via setImmediate()
?
I think it'd be better to get an array of pending tests from TestCollection
and then iterate through it, calling addTestResult()
. I'd then modify addTestResult()
to take a second pending
argument and provide the correct value here and in run()
below. That way Test
doesn't have to be aware of pending
at all.
TestCollection#finalize()
can then be removed. For clarity I think buildStats()
should be renamed to exit()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed and done - though this does burrow a bit into the TestCollection
, I feel like adding such a loop at the top of buildStats()
(or exit()
, if we were to rename it) is a cleaner way to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like adding such a loop at the top of buildStats() (or exit(), if we were to rename it) is a cleaner way to do this.
Which is what you've done, right?
this.skipCount = sum(this.stats, 'skipCount'); | ||
this.todoCount = sum(this.stats, 'todoCount'); | ||
this.failCount = sum(this.stats, 'failCount'); | ||
this.remainingCount = this.testCount - this.passCount - this.failCount - this.skipCount - this.todoCount - this.knownFailureCount; | ||
this.remainingCount = this.testCount - this.passCount - this.failCount - this.skipCount - this.todoCount - this.knownFailureCount - this.pendingCount; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first blush you'd think this is now always 0
, but that wouldn't be the case with --fail-fast
not emitting the pending tests. Perhaps a comment would help clarify that possibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See follow-up above.
lib/reporters/mini.js
Outdated
const title = test.title; | ||
status += '\n ' + colors.title(title) + '\n'; | ||
// TODO: Output description with link | ||
// status += colors.stack(description); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't apply to pending tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/cli.js
Outdated
@@ -207,4 +207,7 @@ exports.run = () => { | |||
}); | |||
}); | |||
} | |||
|
|||
// Trap SIGINT to prevent AVA quitting immediately | |||
process.on('SIGINT', () => {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This relies on the signal still being propagated to the worker processes, right? I think we need to take a more explicit approach. For one the API needs to know not to start new workers when concurrency is being limited. Since the API is managing the worker pool it could shut down the workers, and the workers could then emit their pending tests. See https://github.com/avajs/ava/blob/e401bd151b1a81f79ccb106f034b2e18715ee125/api.js#L245:L247
(Though looking at that code I have a suspicion timeouts don't prevent new workers from being started!)
We'd need to make sure the signal isn't propagated, or otherwise is ignored in the workers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The teardown process is initiated by the SIGINT
handler added to lib/main.js
- which then calls the exit()
function in that same file (which I'm relying on to handle any required tear-down tasks).
If you disable the SIGINT
handler in lib/main.js
while keeping this one, AVA (from what I can see through the CLI) basically ignores SIGINT
s. If you disable this SIGINT
handler however, lib/main.js
doesn't have enough time to gracefully exit.
This was largely based on experimenting with the code though - so it's definitely possible that I'm initiating the wrong teardown call/event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to make sure we try intercepting SIGINT
in the main process, and then using the Api
instance to explicitly exit all workers. We may then need to trap SIGINT
in the workers to ensure they don't exit before they're told to do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - any files that haven't started running when a SIGINT
is received will trigger an AvaError
stating that they weren't run. I assume that works?
lib/reporters/verbose.js
Outdated
@@ -91,6 +91,7 @@ class VerboseReporter { | |||
' ' + colors.error(runStatus.failCount, plur('test', runStatus.failCount), 'failed') : | |||
' ' + colors.pass(runStatus.passCount, plur('test', runStatus.passCount), 'passed'), | |||
runStatus.knownFailureCount > 0 ? ' ' + colors.error(runStatus.knownFailureCount, plur('known failure', runStatus.knownFailureCount)) : '', | |||
runStatus.pendingCount > 0 ? ' ' + colors.todo(runStatus.pendingCount, plur('test', runStatus.pendingCount), 'pending') : '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure it's on your list, but this still needs to print the titles of these tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I missed this originally - good catch!
@@ -27,6 +27,9 @@ Error.stackTraceLimit = Infinity; | |||
|
|||
function test(props) { | |||
if (isFailed) { | |||
if (props.pending) { | |||
adapter.send('test', props); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only applies to when --fail-fast
is used. I don't think we don't need to print pending tests in that circumstance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine people want to be able to identify any non-passing tests with --fail-fast
. That's doable without this PR if all tests are serial (--> any tests after the failed one are pending), but harder to reason about if test order is nondeterministic or involves multiple files executing concurrently.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine people want to be able to identify any non-passing tests with --fail-fast.
No, all tests should stop running as soon as a single failure occurs. See #1158.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I misspoke - sorry about that 😄 s/non-passing/pending/g
That being said - I see two distinct use cases for --fail-fast
:
- as a simple yes/no "Do my tests pass?" check? In this case, I'd agree that people don't really care about which tests are pending.
- iterative development/debugging. Personally, I'd find knowing which tests are pending useful in this case - especially if I'm trying to troubleshoot why said tests are pending.
If Option 1 is more important, I have no objection to hiding pending tests on --fail-fast
- but I'd vote to keep things the way they are if we're prioritizing Option 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iterative development/debugging. Personally, I'd find knowing which tests are pending useful in this case - especially if I'm trying to troubleshoot why said tests are pending.
It would show tests that haven't yet completed when another test fails. I don't think that's what we're trying to solve with this PR.
lib/test-collection.js
Outdated
|
||
finalize() { | ||
globals.setImmediate(() => { | ||
Array.from(this.pendingTestInstances).forEach(test => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a for-of
loop instead of forEach
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/runner.js
Outdated
result: test | ||
}); | ||
} | ||
this.tests.pendingTestInstances.clear(); // Pending tests must be cleared |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why must they be cleared?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are spurious repeated results in some tests otherwise - namely here. This is a bit of a band-aid fix though, so let me know if further investigation or a more proper fix is required.
lib/cli.js
Outdated
@@ -207,4 +207,7 @@ exports.run = () => { | |||
}); | |||
}); | |||
} | |||
|
|||
// Trap SIGINT to prevent AVA quitting immediately | |||
process.on('SIGINT', () => {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to make sure we try intercepting SIGINT
in the main process, and then using the Api
instance to explicitly exit all workers. We may then need to trap SIGINT
in the workers to ensure they don't exit before they're told to do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another side-question: do we want to report pending after()
calls? e.g. if all your tests pass, but AVA doesn't terminate automatically - should we tell users which after()
call is pending?
Perhaps this should be part of another issue altogether? I've personally ran into this in the past, but I don't know whether this feature would be useful for others.
@@ -27,6 +27,9 @@ Error.stackTraceLimit = Infinity; | |||
|
|||
function test(props) { | |||
if (isFailed) { | |||
if (props.pending) { | |||
adapter.send('test', props); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I misspoke - sorry about that 😄 s/non-passing/pending/g
That being said - I see two distinct use cases for --fail-fast
:
- as a simple yes/no "Do my tests pass?" check? In this case, I'd agree that people don't really care about which tests are pending.
- iterative development/debugging. Personally, I'd find knowing which tests are pending useful in this case - especially if I'm trying to troubleshoot why said tests are pending.
If Option 1 is more important, I have no objection to hiding pending tests on --fail-fast
- but I'd vote to keep things the way they are if we're prioritizing Option 2.
lib/runner.js
Outdated
result: test | ||
}); | ||
} | ||
this.tests.pendingTestInstances.clear(); // Pending tests must be cleared |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are spurious repeated results in some tests otherwise - namely here. This is a bit of a band-aid fix though, so let me know if further investigation or a more proper fix is required.
lib/cli.js
Outdated
@@ -207,4 +207,7 @@ exports.run = () => { | |||
}); | |||
}); | |||
} | |||
|
|||
// Trap SIGINT to prevent AVA quitting immediately | |||
process.on('SIGINT', () => {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - any files that haven't started running when a SIGINT
is received will trigger an AvaError
stating that they weren't run. I assume that works?
const forkArgs = execArgvList[index]; | ||
if (interrupted) { | ||
const relFile = path.relative('.', file); | ||
return reject(new AvaError(`File ${relFile} was not run.`)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AVA prints this as an uncaught exception, which isn't ideal. Do you think this even needs to be printed, or can these test files be skipped silently?
const test = this._runFile(file, runStatus, forkArgs); | ||
tests.push(test); | ||
|
||
process.on('SIGINT', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will trigger "memory leak" warnings when there are more than 10 test files. Each test
is pushed onto the tests
array though so you could use that to set up one SIGINT
listener and exit each active test.
@@ -27,6 +27,9 @@ Error.stackTraceLimit = Infinity; | |||
|
|||
function test(props) { | |||
if (isFailed) { | |||
if (props.pending) { | |||
adapter.send('test', props); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iterative development/debugging. Personally, I'd find knowing which tests are pending useful in this case - especially if I'm trying to troubleshoot why said tests are pending.
It would show tests that haven't yet completed when another test fails. I don't think that's what we're trying to solve with this PR.
That would be useful, yes, but it could be done in a separate issue. |
There's been a lot of changes recently that impact this PR. I'm also looking to refactor how test results are collected and logged. That'll make this feature easier to implement. Closing this for now. @ace-n if you want to pick this up again, please check in first to see if I've actually managed to do that refactoring 👼 Meanwhile thank you for putting in so much effort! |
Fixes #583.
N.B: I haven't added any new tests yet - but I plan to once the implementation details are nailed down.
Example test.js