Files
2026-07-20 04:51:20 +00:00

81 lines
2.3 KiB
Go

//go:build linux
package scenario
import (
"context"
"errors"
"sync/atomic"
"testing"
)
func TestHeldSessionAdapterCanceledWaiterRetainsCleanupForLaterCaller(t *testing.T) {
cleanupStarted := make(chan struct{})
cleanupRelease := make(chan struct{})
cleanupErr := errors.New("cleanup failed")
var cleanupCount atomic.Int32
lifecycle := heldTestLifecycle(t, "")
adapter := &heldSessionAdapter{
lifecycle: lifecycle,
cleanup: func(cleanupContext context.Context) error {
cleanupCount.Add(1)
if err := cleanupContext.Err(); err != nil {
t.Errorf("cleanup context canceled before release: %v", err)
}
close(cleanupStarted)
<-cleanupRelease
return cleanupErr
},
}
canceled, cancel := context.WithCancel(context.Background())
cancel()
firstResult := make(chan error, 1)
go func() { firstResult <- adapter.Close(canceled) }()
<-cleanupStarted
if err := <-firstResult; !errors.Is(err, context.Canceled) {
t.Fatalf("canceled Close error = %v", err)
}
close(cleanupRelease)
if err := adapter.Close(context.Background()); !errors.Is(err, cleanupErr) {
t.Fatalf("later Close error = %v", err)
}
if count := cleanupCount.Load(); count != 1 {
t.Fatalf("cleanup invocation count = %d, want 1", count)
}
}
type heldSessionAdapter struct {
lifecycle *heldSessionLifecycle
cleanup func(context.Context) error
}
func (adapter *heldSessionAdapter) Plan() StressSessionPlan { return adapter.lifecycle.Plan() }
func (adapter *heldSessionAdapter) WaitLive(ctx context.Context) error {
return adapter.lifecycle.WaitLive(ctx)
}
func (adapter *heldSessionAdapter) IOStreamID() (string, bool) { return adapter.lifecycle.IOStreamID() }
func (adapter *heldSessionAdapter) WaitClosed(ctx context.Context) error {
return adapter.lifecycle.WaitClosed(ctx)
}
func (adapter *heldSessionAdapter) Done() <-chan struct{} { return adapter.lifecycle.Done() }
func (adapter *heldSessionAdapter) CloseResult() error { return adapter.lifecycle.CloseResult() }
func (adapter *heldSessionAdapter) Close(ctx context.Context) error {
owner, won := adapter.lifecycle.beginClose()
if !won {
return adapter.lifecycle.WaitClosed(ctx)
}
go func() {
cleanupContext, cancel := owner.cleanupContext()
defer cancel()
owner.markClosed(adapter.cleanup(cleanupContext))
}()
return adapter.lifecycle.WaitClosed(ctx)
}
var _ heldSession = (*heldSessionAdapter)(nil)