-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
testing: run Cleanup if SIGINT occurs #41891
Comments
A note on possible complexity from @jayconrod: "One problem is that we can't handle SIGINT on Windows. It looks like there a couple similar things (CTRL_CLOSE_EVENT). Not sure if we can hook into that without cgo." |
Not sure whether we should handle SIGINT in all tests and benchmarks. User code may install signal handlers (for example, in an initializer in a cgo library), and we might accidentally replace those. It makes more sense for fuzzing, specifically when running the fuzzing engine in worker processes, since 1) we don't need to be compatible with existing fuzz targets because there are none, and 2) it will be common for users to stop fuzzing with ^C. |
Can the test function itself register the For example, something like: https://play.golang.org/p/ZsRDGgDMgGz |
We were looking for something more general purpose with fuzzing. The fuzzing coordinator process (started by |
I don't think The |
Related #39382 |
Knative have written some test-infra code that deals with handling interrupts but it's not ideal to maintain. Generally
I also noticed timeouts don't cancel tests gracefully either - #42217 |
Thanks everyone for the input here, @ianlancetaylor raised the cautionary tale and precedent of us previously having tried to handle SIGINT in testing before, and then rollbacked, and @bcmills showed how the caller could invoke it and handle directly. Perhaps the fuzzing package could handle it somehow or if the testing package could detect that fuzzing is being done that it could add hooks. Either way, we haven’t resolved this issue for Go1.16, and I shall kindly implore us to try again for Go1.17. Moving to Go1.17. Thank you. |
My use case is to terminate a docker container that is started in a test helper. Since the container process is not parented to the test process it keeps running after the test is aborted. |
@cbednarski, #24050 (comment) might be more relevant for that use-case? |
I don't see how we can make this change. I'm at least inclined to change to milestone, which is currently Go1.17. |
@katiehockman This is in the Go 1.18 milestone. Is it likely to happen for 1.18? Or should it move to Backlog? Thanks. |
I went ahead and moved it to Backlog, thanks. |
Does anyone have any workaround for this, which one can implement temporarily? |
@krashish8 https://github.com/knative/serving/blob/4e17128158b206220eccbfa8b60c749811dab9a1/test/cleanup.go is a decent place to look for a pattern here. Basically replace |
Thanks a lot for the link @benmoss |
For folks who landed here from #42217 , I have made a workaround to run all cleanup functions (yes go testing's cleanup, and I promise you no extra framework/slice is needed). (Sorry for spamming this issue, the timeout related issue is closed and we can not comment there) func RunAndFailTestBeforeTimeout(t *testing.T, tf func(*testing.T)) {
deadline, hasTimeout := t.Deadline()
if !hasTimeout {
tf(t)
return
}
const gracePercentage = 0.05
tillDeadline := deadline.Sub(time.Now())
gracePeriod := time.Duration(float64(tillDeadline) * gracePercentage)
waitPeriod := tillDeadline - gracePeriod
finished := make(chan any, 1)
go func() {
defer func() { finished <- recover() }()
tf(t)
}()
select {
case <-time.After(waitPeriod):
t.Fatalf("test failed early, %v before actual deadline %v", gracePeriod, deadline)
case somePanic := <-finished: // This may look like over-engineering, but it allows you to run the cleanups.
if somePanic != nil {
panic(somePanic)
}
}
} Usage:// Before
func TestXyz(t *testing.T) {
// ...
}
// After
func TestXyz(t *testing.T) {
RunAndFailTestBeforeTimeout(t, XTestXyz)
}
func XTestXyz(t *testing.T) {
// ...
}
Check the playground: https://go.dev/play/p/KMOyjuu5D-x As a bonus it takes care of cleanup functions even if the code-under-test panics (recoverable). I believe we can build a similar solution for signals as well, and apply it to existing tests without moving away from
|
Currently,
testing.T.Cleanup
andtesting.B.Cleanup
are only run if the test and all of its subtests have finished running. This means it won't run if the test is stopped with a SIGINT (e.g. Ctrl+C). There are several things that may be in such aCleanup
function that would be helpful to run regardless of whether or not the test fully completed.This will be especially unfortunate in the case of fuzzing (http://golang.org/s/draft-fuzzing-design). Take for example:
It may be a very common use case that someone could run
go test -fuzz FuzzFoo
and give a SIGINT after a few hours. If no crash occurs, there would be no way to run any cleanup code or do any checks after fuzzing completed since the fuzzing runs in an infinite loop.I would like to expand the behavior of Cleanup to not only run if tests finished, but also run in the case of a SIGINT. This could of course be special cased to fuzzing, but I can see good reasons why it should be a part of unit and benchmark testing as well (e.g. if the test hangs for some reason).
/cc @jayconrod @bcmills @FiloSottile
The text was updated successfully, but these errors were encountered: