-
-
Notifications
You must be signed in to change notification settings - Fork 127
feat: treat timeouts as failure or success based on config #2742
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
base: main
Are you sure you want to change the base?
Conversation
|
Just confirmed that the failing tests in CI are these 3, which are snapshot tests for the formatting output. So all other tests are passing |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2742 +/- ##
==========================================
+ Coverage 77.41% 77.84% +0.43%
==========================================
Files 112 112
Lines 26155 26205 +50
==========================================
+ Hits 20247 20400 +153
+ Misses 5908 5805 -103 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
sunshowers
left a comment
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.
Thanks. One commit is fine for this change (though there's one particular thing in this PR that should be in a followup).
| period: far_future_duration(), | ||
| terminate_after: None, | ||
| grace_period: Duration::from_secs(10), | ||
| timeout_result: SlowTimeoutResult::Fail, |
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.
An inconsistency here -- we use SlowTimeoutResult::Fail here but default below. Let's pick one or the other -- I'm partial to using Fail and removing the Default impl, unless there's a good reason to retain 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.
The only issue with not having a SlowTimeoutResult::default is that AFAICT (I'm not that familiar with serde) the #[serde(default)] line in SlowTimeout's definition will have to be something like #[serde(default = "default_result")] where default_result is a custom function that returns the default value for SlowTimeoutResult. At that point I think it's just better to just keep the derived default and change this usage from SlowTimeoutResult::Fail to default.
From
pub struct SlowTimeout {
// Omitted fields
#[serde(default)]
pub(crate) on_timeout: SlowTimeoutResult,
}
To
pub struct SlowTimeout {
// Omitted fields
#[serde(default = "default_on_timeout")]
pub(crate) on_timeout: SlowTimeoutResult,
}
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 just noticed something, since VERY_LARGE is const, I can't actually call SlowTimeoutResult::default() since it's not const, so now I'm unsure what to do:
- Keep
#[derive(Default)]inSlowTimeoutResult, useSlowTimeoutResult::defaultin non-constcontexts andSlowTimeoutResult::Failinconstcontexts. - Keep
#[derive(Default)]just for#[serde(default)], useSlowTimeoutResult::Faileverywhere else - Remove
#[derive(Default)], add apub const fn default() -> Selfwhich can be used everywhere. This does mean that instead of#[serde(default)]we'll have#[serde(default = "SlowTimeoutResult::default")]inSlowTimeout's definition.
Option 1 doesn't seem that great to me, I think I prefer option 3, but option 2 is also fine.
| /// The test is marked as failed. | ||
| Fail, | ||
|
|
||
| /// The test is marked as passed. |
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 put in an explanation here because "the test is marked as passed" is pretty strange on a timeout being hit. Just talk about the fact that there are some use cases that benefit from being run up to a time limit.
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 do you think of something like this?
/// The result of controlling slow timeout behavior.
///
/// For most situations, a timed out test should be marked failing. However, there are certain
/// classes of tests which are expected to run indefinetely long (or just too long), like fuzzing
/// and property tests, which both explore a huge state space. For these tests it's nice to be able
/// to treat a timeout as a success since, they generally check for invariants and other properties
/// of the code under test. A timeout in this context doesn't necessarily mean that there are no
/// failing inputs, just that they weren't found up until that moment, which is still valuable
/// information.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum SlowTimeoutResult {
/// The test is marked as failed.
Fail,
/// The test is marked as passed.
Pass,
}
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.
Thanks. This is pretty good. Some notes:
- It's "property-based tests" -- "property testing" has a different meaning.
- Generally PBT tends to run for a limited number of iterations -- the confidence in them comes from the fact that they get run in CI all the time. So I'd drop the note about them, and instead just say fuzzing.
| ExecutionResult::Timeout { | ||
| result: SlowTimeoutResult::Fail, | ||
| } => { | ||
| self.failed += 1; |
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.
Note this is a logic change and makes the failed_count function incorrect. Let's address this in a followup rather than changing the semantics 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 see, should I also move the changes to RunStats (adding passed_timed_out and renaming timed_out to failed_timed_out) into that followup? Or keep the struct changes in the first commit and address only the logic changes in a followup?
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 struct changes have to go in the first commit, I think. The semantic changes to the meaning of things like failed should go in the followup.
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.
Ah the failing tests are unfortunate -- can we split them up into another PR?
c2d5c73 to
c4e6773
Compare
Adds a new configuration option inside "slow-timeout": "on-timeout" which when set to "pass" makes nextest treat timed out tests as passing. The logic for actually counting how many timed out tests were treated as successes will be made in a followup commit.
c4e6773 to
5954546
Compare
|
So I've split off the changes into two commits, where the second one only deals with the logic in |
| ExecutionResult::Timeout { | ||
| result: SlowTimeoutResult::Fail, | ||
| } => { | ||
| self.failed += 1; |
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.
Ah the failing tests are unfortunate -- can we split them up into another PR?
| slow_timeout.grace_period, | ||
| ).await; | ||
| status = Some(ExecutionResult::Timeout); | ||
| status = Some(ExecutionResult::Timeout {result: slow_timeout.on_timeout}); |
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.
rustfmt should have fixed this -- did that not work? (Oh maybe not because this is inside a tokio::select! arm, ugh.)
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.
Would the "correct" formatting be this?
status = Some(ExecutionResult::Timeout { result: slow_timeout.on_timeout });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.
Yeah, either that or across multiple lines:
status = Some(ExecutionResult::Timeout {
result: slow_timeout.on_timeout,
});Since timed out tests can now be treated as either successes or failures, update the logic for handling them. Note that before `self.failed` and `self.timed_out` were separate, but now `self.failed` includes `self.failed_timed_out`.
5954546 to
42f92f1
Compare
|
Tests are now passing, I changed the logic in pub fn failed_count(&self) -> usize {
self.failed + self.exec_failed + self.failed_timed_out
}I haven't added any new tests, but I'll get started on that now that all the old existing tests are passing. |
|
Looks great -- yeah, just missing new tests otherwise this is good to go. |
|
I have two questions regarding junit:
|
|
Did this in #2761. |
Adds a new configuration option inside "slow-timeout": "on-timeout" which when set to "pass" makes nextest treat timed out tests as passing.
This PR adds a new
SlowTimeoutResultstruct similar toLeakTimeoutResultwhich characterizes a timeout as passing or failing.There are some things which need to be addressed, and most of them are marked with
TODOs in the code, these should all be fixed before merging. Most of them have to do with how to display this information, since we now have a new category of tests to display: tests that passed with a timeout. Any opinions here would be great!There are some failing tests due to the formatting changes made, I decided not to accept the snapshots just yet since I'm not sure this is the final formatting we're going to end up with.
P.S.: I noticed nextest follows atomic commits, I figured only one commit would be enough for this change but I can split it up into smaller commits if it's deemed better.
Closes #1291