-
Notifications
You must be signed in to change notification settings - Fork 9
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
Correctly identify test executions by utilizing repeatEachIndex
#536
Correctly identify test executions by utilizing repeatEachIndex
#536
Conversation
🦋 Changeset detectedLatest commit: e43bde8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
/release-pr |
packages/playwright/src/reporter.ts
Outdated
// TODO: this could be simplified to `${test.testId}-${test.repeatEachIndex}-${test.attempt}` | ||
// before doing that all recipients of `TestExecutionIdData` should be rechecked to see if such a change would be safe |
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.
Can you file a Linear follow-up issue for this and update the TODO comment to reference it?
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
packages/test-utils/src/reporter.ts
Outdated
// even when `minimizeUploads` is combined with `repeatEach` it always makes sense to upload the latest result | ||
// otherwise, we could upload a single successful attempt without uploading a potential failure of the same test | ||
// coming from a different `repeatEachIndex` | ||
toUpload = this._minimizeUploads ? [latestExecution] : executions; |
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 looks wrong to me.
What if the test was flaky? We're only getting the passing recording this way.
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 wonder if this should be rewritten so that we have the pass/fail/flaky check on the outside and then the minimize check within?
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.
What if the test was flaky? We're only getting the passing recording this way.
You are correct. I'll fix 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.
Something like this?
const status = computeStatus();
switch (status) {
case "failed": {
switch (this._uploadStatusThreshold) {
case "all":
case "failed":
case "failed-and-flaky": {
toUpload = this._minimizeUploads ? [latestExecution] : executions;
break;
}
}
break;
}
case "flaky": {
switch (this._uploadStatusThreshold) {
case "all":
case "failed-and-flaky": {
if (this._minimizeUploads) {
toUpload = [executions[0], executions[executions.length - 1]];
} else {
toUpload = executions;
}
break;
}
}
break;
}
case "pass": {
switch (this._uploadStatusThreshold) {
case "all": {
toUpload = this._minimizeUploads ? [latestExecution] : executions;
break;
}
}
break;
}
}
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 function uses executions from a single execution group (that currently is equivalent to repeatEachIndex
) but the uploads are minimized per test id.
We'd have an easier time if we'd push this whole decision until onEnd
(that runs after everything gets completed). I decided to keep it in onTestEnd
to start uploads asap - so we only have to await those upload jobs in onEnd
. Maybe it's not worth it but it felt nice to have it "paralellized".
We can't determine if a test case is flaky based on a signal execution group because both could have only single executions and each of them could be of a different status. I thought about having an aggregate status on this uploadable result but it felt like a complication at a time.
The version above misses that those flakes can be spread across execution groups. I like your suggestion to reorganize this, I'll try to come up with something better.
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 have rewritten it based on ur suggestion. I think it's much cleaner now but the end result isn't exactly the same as your snippet, could u take another look?
Co-authored-by: Brian Vaughn <[email protected]>
result.didUploadStatuses.failed ||= executions.some(r => r.result !== "passed"); | ||
result.didUploadStatuses.passed ||= executions.some(r => r.result === "passed"); | ||
|
||
// currently previously completed execution groups that could be entirely passed or failed are not retroactively uploaded here |
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 don't think it's particularly important to "fix" this right now but I could certainly change my mind.
If we have such execution groups, we detect flake when the third group completes:
[passed]
[passed]
[failed, passed]
We could look for all completed groups and upload them but it doesn't feel overly important to me.
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 think this needs to handle the "none" case as mentioned
if (!this._minimizeUploads) { | ||
result.didUploadStatuses.failed = true; | ||
toUpload.push(...executions); | ||
break; | ||
} | ||
if (result.didUploadStatuses.failed) { | ||
break; | ||
} | ||
result.didUploadStatuses.failed = true; | ||
toUpload.push(latestExecution); |
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 think we need to check if status threshold is "none" here before uploading?
if (!this._minimizeUploads) { | |
result.didUploadStatuses.failed = true; | |
toUpload.push(...executions); | |
break; | |
} | |
if (result.didUploadStatuses.failed) { | |
break; | |
} | |
result.didUploadStatuses.failed = true; | |
toUpload.push(latestExecution); | |
if (this._uploadStatusThreshold !== "none") { | |
if (this._minimizeUploads) { | |
if (!result.didUploadStatuses.failed) { | |
result.didUploadStatuses.failed = true; | |
toUpload.push(latestExecution); | |
} | |
} else { | |
result.didUploadStatuses.failed = true; | |
toUpload.push(...executions); | |
} | |
} |
TBH I guess we could just return early from this function if that's the status because the "flaky" branch below also needs to respect that condition.
toUpload = [latestResult]; | ||
} | ||
case "flaky": { | ||
if (this._uploadStatusThreshold === "failed") { |
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.
Unless we return early from this function I think we also need to check for "none" here too
|
||
if (passedExecution) { | ||
result.didUploadStatuses.failed = true; | ||
toUpload.push(passedExecution); |
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 way this code is organized is a little hard for me to read but I think it looks correct.
packages/test-utils/src/reporter.ts
Outdated
case "passed": { | ||
if (this._uploadStatusThreshold !== "all") { | ||
break; | ||
} | ||
if (!this._minimizeUploads) { | ||
result.didUploadStatuses.passed = true; | ||
toUpload.push(...executions); | ||
break; | ||
} | ||
if (result.didUploadStatuses.passed) { | ||
break; | ||
} | ||
result.didUploadStatuses.passed = true; | ||
toUpload.push(latestExecution); | ||
break; |
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 find interleaved break
statements a little harder to read. I think it's because it feels like more work to have to keep track of all of the handled conditions in my mind as I read further along. IF/ELSE blocks feel like they provide scaffolding to make that easier.
We never get to those functions with this threshold as there is an early return here: replay-cli/packages/test-utils/src/reporter.ts Lines 912 to 914 in be5d659
|
I don't think that's a strong enough guard. This method could be called from other places (even if it isn't currently) so I think it needs its own guard. (Avoid relying on less obvious / indirect checks for things like this is a lesson I've learned for myself over the years, not to depend on indirect or less obvious short-circuits for things like this.) |
No description provided.