aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-05-07 00:50:47 +0900
committerOphestra <cat@gensokyo.uk>2026-05-07 00:58:02 +0900
commit4aba014eac0c11bd184364b506b3e15d5716e947 (patch)
treec4b92431689c08595dfd6eaf24957e752687a19d /container
parent779ba994ce8811a747a902359fe2e9c488a8563e (diff)
container: abandon response on termination
This prevents blocking on early failure. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/container_test.go143
1 files changed, 79 insertions, 64 deletions
diff --git a/container/container_test.go b/container/container_test.go
index 00a86b29..f34035b2 100644
--- a/container/container_test.go
+++ b/container/container_test.go
@@ -16,6 +16,7 @@ import (
"strings"
"syscall"
"testing"
+ "time"
"unsafe"
"hakurei.app/check"
@@ -408,37 +409,6 @@ var containerTestCases = []struct {
func TestContainer(t *testing.T) {
t.Parallel()
- t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) {
- wantErr := context.Canceled
- wantExitCode := 0
- if err := c.Wait(); !reflect.DeepEqual(err, wantErr) {
- if m, ok := container.InternalMessageFromError(err); ok {
- t.Error(m)
- }
- t.Errorf("Wait: error = %#v, want %#v", err, wantErr)
- }
- if ps := c.ProcessState(); ps == nil {
- t.Errorf("ProcessState unexpectedly returned nil")
- } else if code := ps.ExitCode(); code != wantExitCode {
- t.Errorf("ExitCode: %d, want %d", code, wantExitCode)
- }
- }))
-
- t.Run("forward", testContainerCancel(func(c *container.Container) {
- c.ForwardCancel = true
- }, func(t *testing.T, c *container.Container) {
- var exitError *exec.ExitError
- if err := c.Wait(); !errors.As(err, &exitError) {
- if m, ok := container.InternalMessageFromError(err); ok {
- t.Error(m)
- }
- t.Errorf("Wait: error = %v", err)
- }
- if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
- t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
- }
- }))
-
for i, tc := range containerTestCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
@@ -563,49 +533,94 @@ func hostnameFromTestCase(name string) string {
}
func testContainerCancel(
+ t *testing.T,
containerExtra func(c *container.Container),
- waitCheck func(t *testing.T, c *container.Container),
-) func(t *testing.T) {
- return func(t *testing.T) {
- t.Parallel()
- ctx, cancel := context.WithCancel(t.Context())
+ waitCheck func(ps *os.ProcessState, waitErr error),
+) {
+ t.Parallel()
+ ctx, cancel := context.WithCancel(t.Context())
- c := helperNewContainer(ctx, "block")
- c.Stdout, c.Stderr = os.Stdout, os.Stderr
- if containerExtra != nil {
- containerExtra(c)
+ c := helperNewContainer(ctx, "block")
+ c.Stdout, c.Stderr = os.Stdout, os.Stderr
+ if containerExtra != nil {
+ containerExtra(c)
+ }
+
+ ready := make(chan struct{})
+ var waitErr error
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatalf("cannot pipe: %v", err)
+ }
+
+ c.ExtraFiles = append(c.ExtraFiles, w)
+ go func() {
+ defer close(ready)
+ if _, _err := r.Read(make([]byte, 1)); _err != nil {
+ panic(_err)
}
+ }()
- ready := make(chan struct{})
- if r, w, err := os.Pipe(); err != nil {
- t.Fatalf("cannot pipe: %v", err)
+ if err = c.Start(); err != nil {
+ if m, ok := container.InternalMessageFromError(err); ok {
+ t.Fatal(m)
} else {
- c.ExtraFiles = append(c.ExtraFiles, w)
- go func() {
- defer close(ready)
- if _, err = r.Read(make([]byte, 1)); err != nil {
- panic(err.Error())
- }
- }()
+ t.Fatalf("cannot start container: %v", err)
+ }
+ }
+
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ waitErr = c.Wait()
+ _ = r.SetReadDeadline(time.Now())
+ }()
+
+ if err = c.Serve(); err != nil {
+ if m, ok := container.InternalMessageFromError(err); ok {
+ t.Error(m)
+ } else {
+ t.Errorf("cannot serve setup params: %v", err)
}
+ }
+ <-ready
+ cancel()
+ <-done
+ waitCheck(c.ProcessState(), waitErr)
+}
- if err := c.Start(); err != nil {
- if m, ok := container.InternalMessageFromError(err); ok {
- t.Fatal(m)
- } else {
- t.Fatalf("cannot start container: %v", err)
+func TestForward(t *testing.T) {
+ testContainerCancel(t, func(c *container.Container) {
+ c.ForwardCancel = true
+ }, func(ps *os.ProcessState, waitErr error) {
+ var exitError *exec.ExitError
+ if !errors.As(waitErr, &exitError) {
+ if m, ok := container.InternalMessageFromError(waitErr); ok {
+ t.Error(m)
}
- } else if err = c.Serve(); err != nil {
- if m, ok := container.InternalMessageFromError(err); ok {
+ t.Errorf("Wait: error = %v", waitErr)
+ }
+ if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
+ t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
+ }
+ })
+}
+
+func TestCancel(t *testing.T) {
+ testContainerCancel(t, nil, func(ps *os.ProcessState, waitErr error) {
+ wantErr := context.Canceled
+ if !reflect.DeepEqual(waitErr, wantErr) {
+ if m, ok := container.InternalMessageFromError(waitErr); ok {
t.Error(m)
- } else {
- t.Errorf("cannot serve setup params: %v", err)
}
+ t.Errorf("Wait: error = %#v, want %#v", waitErr, wantErr)
}
- <-ready
- cancel()
- waitCheck(t, c)
- }
+ if ps == nil {
+ t.Errorf("ProcessState unexpectedly returned nil")
+ } else if code := ps.ExitCode(); code != 0 {
+ t.Errorf("ExitCode: %d, want %d", code, 0)
+ }
+ })
}
func TestContainerString(t *testing.T) {
@@ -827,7 +842,7 @@ func TestMain(m *testing.M) {
}
c.MustParse(os.Args[1:], func(err error) {
if err != nil {
- log.Fatal(err.Error())
+ log.Fatal(err)
}
})
return