diff --git a/actors.go b/actors.go index ad6aed8..c430c12 100644 --- a/actors.go +++ b/actors.go @@ -82,14 +82,15 @@ func (e SignalError) Is(err error) bool { } // As fixes a design error in the SignalError type, so that errors.As with the -// literal `&SignalError{}` will return true. +// literal `&SignalError{}` will return true and, per the errors.As contract, +// populate target with the underlying signal. func (e SignalError) As(target interface{}) bool { - switch target.(type) { - case *SignalError, SignalError: - return true - default: + t, ok := target.(*SignalError) + if !ok { return false } + *t = e + return true } // ErrSignal is returned by SignalHandler when a signal triggers termination. diff --git a/actors_test.go b/actors_test.go index d8d7ea1..3bb568f 100644 --- a/actors_test.go +++ b/actors_test.go @@ -58,6 +58,27 @@ func TestSignalError(t *testing.T) { } } +// errors.As must populate a value-typed SignalError target, not merely +// report a match; #27 made it return true without ever assigning. +func TestSignalErrorAsAssignsTarget(t *testing.T) { + testc := make(chan os.Signal, 1) + ctx := putTestSigChan(context.Background(), testc) + + var rg Group + rg.Add(SignalHandler(ctx, os.Interrupt)) + testc <- os.Interrupt + err := rg.Run() + + var target SignalError + if want, have := true, errors.As(err, &target); want != have { + t.Fatalf("errors.As(err, &target): want %v, have %v", want, have) + } + + if want, have := os.Interrupt, target.Signal; want != have { + t.Errorf("target.Signal: want %v, have %v (errors.As reported success but did not assign the target)", want, have) + } +} + func TestSignalHandlerNil(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) var rg Group