Tests that even if a registered service fails to shut down cleanly, it does not influece the rest of the shutdown invocations.
(t *testing.T)
| 321 | // Tests that even if a registered service fails to shut down cleanly, it does |
| 322 | // not influece the rest of the shutdown invocations. |
| 323 | func TestServiceTerminationGuarantee(t *testing.T) { |
| 324 | stack, err := New(testNodeConfig()) |
| 325 | if err != nil { |
| 326 | t.Fatalf("failed to create protocol stack: %v", err) |
| 327 | } |
| 328 | // Register a batch of good services |
| 329 | services := map[string]InstrumentingWrapper{ |
| 330 | "A": InstrumentedServiceMakerA, |
| 331 | "B": InstrumentedServiceMakerB, |
| 332 | "C": InstrumentedServiceMakerC, |
| 333 | } |
| 334 | started := make(map[string]bool) |
| 335 | stopped := make(map[string]bool) |
| 336 | |
| 337 | for id, maker := range services { |
| 338 | id := id // Closure for the constructor |
| 339 | constructor := func(*ServiceContext) (Service, error) { |
| 340 | return &InstrumentedService{ |
| 341 | startHook: func(*p2p.Server) { started[id] = true }, |
| 342 | stopHook: func() { stopped[id] = true }, |
| 343 | }, nil |
| 344 | } |
| 345 | if err := stack.Register(maker(constructor)); err != nil { |
| 346 | t.Fatalf("service %s: registration failed: %v", id, err) |
| 347 | } |
| 348 | } |
| 349 | // Register a service that fails to shot down cleanly |
| 350 | failure := errors.New("fail") |
| 351 | failer := func(*ServiceContext) (Service, error) { |
| 352 | return &InstrumentedService{ |
| 353 | stop: failure, |
| 354 | }, nil |
| 355 | } |
| 356 | if err := stack.Register(failer); err != nil { |
| 357 | t.Fatalf("failer registration failed: %v", err) |
| 358 | } |
| 359 | // Start the protocol stack, and ensure that a failing shut down terminates all |
| 360 | for i := 0; i < 100; i++ { |
| 361 | // Start the stack and make sure all is online |
| 362 | if err := stack.Start(); err != nil { |
| 363 | t.Fatalf("iter %d: failed to start protocol stack: %v", i, err) |
| 364 | } |
| 365 | for id := range services { |
| 366 | if !started[id] { |
| 367 | t.Fatalf("iter %d, service %s: service not running", i, id) |
| 368 | } |
| 369 | if stopped[id] { |
| 370 | t.Fatalf("iter %d, service %s: service already stopped", i, id) |
| 371 | } |
| 372 | } |
| 373 | // Stop the stack, verify failure and check all terminations |
| 374 | err := stack.Stop() |
| 375 | if err, ok := err.(*StopError); !ok { |
| 376 | t.Fatalf("iter %d: termination failure mismatch: have %v, want StopError", i, err) |
| 377 | } else { |
| 378 | failer := reflect.TypeOf(&InstrumentedService{}) |
| 379 | if err.Services[failer] != failure { |
| 380 | t.Fatalf("iter %d: failer termination failure mismatch: have %v, want %v", i, err.Services[failer], failure) |
nothing calls this directly
no test coverage detected