github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/actor/supervision_test.go (about) 1 package actor 2 3 import ( 4 "reflect" 5 "sync" 6 "testing" 7 "time" 8 ) 9 10 type actorWithSupervisor struct { 11 wg *sync.WaitGroup 12 } 13 14 func (a *actorWithSupervisor) Receive(ctx Context) { 15 switch ctx.Message().(type) { 16 case *Started: 17 child := ctx.Spawn(PropsFromProducer(func() Actor { return &failingChildActor{} })) 18 ctx.Send(child, "Fail!") 19 } 20 } 21 22 func (a *actorWithSupervisor) HandleFailure(*ActorSystem, Supervisor, *PID, *RestartStatistics, interface{}, interface{}) { 23 a.wg.Done() 24 } 25 26 type failingChildActor struct{} 27 28 func (a *failingChildActor) Receive(ctx Context) { 29 switch ctx.Message().(type) { 30 case string: 31 panic("Oh noes!") 32 } 33 } 34 35 func TestActorWithOwnSupervisorCanHandleFailure(t *testing.T) { 36 wg := &sync.WaitGroup{} 37 wg.Add(1) 38 props := PropsFromProducer(func() Actor { return &actorWithSupervisor{wg: wg} }) 39 rootContext.Spawn(props) 40 wg.Wait() 41 } 42 43 func NewObserver() (func(ReceiverFunc) ReceiverFunc, *Expector) { 44 c := make(chan interface{}) 45 e := &Expector{C: c} 46 f := func(next ReceiverFunc) ReceiverFunc { 47 fn := func(context ReceiverContext, env *MessageEnvelope) { 48 message := env.Message 49 c <- message 50 next(context, env) 51 } 52 53 return fn 54 } 55 return f, e 56 } 57 58 type Expector struct { 59 C <-chan interface{} 60 } 61 62 func (e *Expector) ExpectMsg(expected interface{}, t *testing.T) { 63 actual := <-e.C 64 if actual == expected { 65 } else { 66 67 at := reflect.TypeOf(actual) 68 et := reflect.TypeOf(expected) 69 t.Errorf("Expected %v:%v, got %v:%v", et, expected, at, actual) 70 } 71 } 72 73 func (e *Expector) ExpectNoMsg(t *testing.T) { 74 select { 75 case actual := <-e.C: 76 at := reflect.TypeOf(actual) 77 t.Errorf("Expected no message got %v:%v", at, actual) 78 case <-time.After(time.Second * 1): 79 // pass 80 } 81 } 82 83 func TestActorStopsAfterXRestarts(t *testing.T) { 84 m, e := NewObserver() 85 props := PropsFromProducer(func() Actor { return &failingChildActor{} }, WithReceiverMiddleware(m)) 86 child := rootContext.Spawn(props) 87 fail := "fail!" 88 89 e.ExpectMsg(startedMessage, t) 90 91 // root supervisor allows 10 restarts 92 for i := 0; i < 10; i++ { 93 rootContext.Send(child, fail) 94 e.ExpectMsg(fail, t) 95 e.ExpectMsg(restartingMessage, t) 96 e.ExpectMsg(startedMessage, t) 97 } 98 rootContext.Send(child, fail) 99 e.ExpectMsg(fail, t) 100 // the 11th time should cause a termination 101 e.ExpectMsg(stoppingMessage, t) 102 }