Revamp modules: now handles dependencies and has a great boot process

This commit is contained in:
Daniel 2018-08-23 14:55:28 +02:00
parent 6f4b18fab5
commit 0fd5a04e91
6 changed files with 455 additions and 187 deletions

View file

@ -3,48 +3,182 @@
package modules
import (
"errors"
"fmt"
"testing"
"sync"
"time"
)
func newTestModule(name string, order uint8) {
var (
startOrder string
shutdownOrder string
)
fmt.Printf("up %s\n", name)
module := Register("TestModule", order)
func testPrep() error {
return nil
}
func testStart(name string) func() error {
return func() error {
startOrder = fmt.Sprintf("%s>%s", startOrder, name)
return nil
}
}
func testStop(name string) func() error {
return func() error {
shutdownOrder = fmt.Sprintf("%s>%s", shutdownOrder, name)
return nil
}
}
func testFail() error {
return errors.New("test error")
}
func testCleanExit() error {
return ErrCleanExit
}
func TestOrdering(t *testing.T) {
Register("database", testPrep, testStart("database"), testStop("database"))
Register("stats", testPrep, testStart("stats"), testStop("stats"), "database")
Register("service", testPrep, testStart("service"), testStop("service"), "database")
Register("analytics", testPrep, testStart("analytics"), testStop("analytics"), "stats", "database")
Start()
var wg sync.WaitGroup
wg.Add(1)
go func() {
<-module.Stop
fmt.Printf("down %s\n", name)
module.StopComplete()
select {
case <-ShuttingDown():
case <-time.After(1 * time.Second):
t.Error("did not receive shutdown signal")
}
wg.Done()
}()
Shutdown()
if startOrder != ">database>service>stats>analytics" &&
startOrder != ">database>stats>service>analytics" &&
startOrder != ">database>stats>analytics>service" {
t.Errorf("start order mismatch, was %s", startOrder)
}
if shutdownOrder != ">analytics>service>stats>database" &&
shutdownOrder != ">analytics>stats>service>database" &&
shutdownOrder != ">service>analytics>stats>database" {
t.Errorf("shutdown order mismatch, was %s", shutdownOrder)
}
wg.Wait()
}
func Example() {
func resetModules() {
for _, module := range modules {
module.Active.UnSet()
module.starting = false
}
}
// wait for logger registration timeout
time.Sleep(1010 * time.Millisecond)
func TestErrors(t *testing.T) {
newTestModule("1", 1)
newTestModule("4", 4)
newTestModule("3", 3)
newTestModule("2", 2)
newTestModule("5", 5)
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
InitiateFullShutdown()
// test prep error
Register("prepfail", testFail, testStart("prepfail"), testStop("prepfail"))
err := Start()
if err == nil {
t.Error("should fail")
}
time.Sleep(10 * time.Millisecond)
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// Output:
// up 1
// up 4
// up 3
// up 2
// up 5
// down 5
// down 4
// down 3
// down 2
// down 1
// test prep clean exit
Register("prepcleanexit", testCleanExit, testStart("prepcleanexit"), testStop("prepcleanexit"))
err = Start()
if err != ErrCleanExit {
t.Error("should fail with clean exit")
}
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// test invalid dependency
Register("database", testPrep, testStart("database"), testStop("database"), "invalid")
// go func() {
// time.Sleep(1 * time.Second)
// fmt.Println("===== TAKING TOO LONG FOR SHUTDOWN - PRINTING STACK TRACES =====")
// pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
// os.Exit(1)
// }()
err = Start()
if err == nil {
t.Error("should fail")
}
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// test dependency loop
Register("database", testPrep, testStart("database"), testStop("database"), "helper")
Register("helper", testPrep, testStart("helper"), testStop("helper"), "database")
err = Start()
if err == nil {
t.Error("should fail")
}
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// test failing module start
Register("startfail", testPrep, testFail, testStop("startfail"))
err = Start()
if err == nil {
t.Error("should fail")
}
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// test failing module stop
Register("stopfail", testPrep, testStart("stopfail"), testFail)
err = Start()
if err != nil {
t.Error("should not fail")
}
err = Shutdown()
if err == nil {
t.Error("should fail")
}
// reset modules
modules = make(map[string]*Module)
modulesOrder = make([]*Module, 0)
startComplete.UnSet()
// test help flag
helpFlag = true
err = Start()
if err == nil {
t.Error("should fail")
}
helpFlag = false
}