github.com/ergo-services/ergo@v1.999.224/tests/supervisor_ofo_test.go (about)

     1  package tests
     2  
     3  // - Supervisor
     4  
     5  //  - one for one (permanent)
     6  //    start node1
     7  //    start supevisor sv1 with genservers gs1,gs2,gs3
     8  //    gs1.stop(normal) (sv1 restarting gs1)
     9  //    gs2.stop(shutdown) (sv1 restarting gs2)
    10  //    gs3.stop(panic) (sv1 restarting gs3)
    11  
    12  //  - one for one (transient)
    13  //    start node1
    14  //    start supevisor sv1 with genservers gs1,gs2,gs3
    15  //    gs1.stop(normal) (sv1 wont restart gs1)
    16  //    gs2.stop(shutdown) (sv1 wont restart gs2)
    17  //    gs3.stop(panic) (sv1 restarting gs3 only)
    18  
    19  //  - one for one (temporary)
    20  //    start node1
    21  //    start supevisor sv1 with genservers gs1,gs2,gs3
    22  //    gs1.stop(normal) (sv1 wont restart gs1)
    23  //    gs2.stop(shutdown) (sv1 wont restart gs2)
    24  //    gs3.stop(panic) (sv1 wont gs3 only)
    25  
    26  import (
    27  	"fmt"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/ergo-services/ergo"
    32  	"github.com/ergo-services/ergo/etf"
    33  	"github.com/ergo-services/ergo/gen"
    34  	"github.com/ergo-services/ergo/lib"
    35  	"github.com/ergo-services/ergo/node"
    36  )
    37  
    38  type testSupervisorOneForOne struct {
    39  	gen.Supervisor
    40  	ch chan interface{}
    41  }
    42  
    43  type testSupervisorGenServer struct {
    44  	gen.Server
    45  }
    46  
    47  type testMessageStarted struct {
    48  	pid   etf.Pid
    49  	name  string
    50  	order int
    51  }
    52  
    53  type testMessageTerminated struct {
    54  	name  string
    55  	order int
    56  	pid   etf.Pid
    57  }
    58  
    59  type testSupervisorGenServerState struct {
    60  	ch    chan interface{}
    61  	order int
    62  }
    63  
    64  func (tsv *testSupervisorGenServer) Init(process *gen.ServerProcess, args ...etf.Term) error {
    65  	st := &testSupervisorGenServerState{
    66  		ch:    args[0].(chan interface{}),
    67  		order: args[1].(int),
    68  	}
    69  	process.State = st
    70  
    71  	st.ch <- testMessageStarted{
    72  		pid:   process.Self(),
    73  		name:  process.Name(),
    74  		order: st.order,
    75  	}
    76  
    77  	return nil
    78  }
    79  func (tsv *testSupervisorGenServer) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
    80  	// message has the stop reason
    81  
    82  	return gen.ServerStatusStopWithReason(message.(string))
    83  }
    84  func (tsv *testSupervisorGenServer) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
    85  	return message, gen.ServerStatusOK
    86  }
    87  
    88  func (tsv *testSupervisorGenServer) HandleDirect(process *gen.ServerProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
    89  	switch m := message.(type) {
    90  	case makeCall:
    91  		return process.Call(m.to, m.message)
    92  	case makeCast:
    93  		return nil, process.Cast(m.to, m.message)
    94  	}
    95  	return nil, lib.ErrUnsupportedRequest
    96  }
    97  
    98  func (tsv *testSupervisorGenServer) Terminate(process *gen.ServerProcess, reason string) {
    99  	st := process.State.(*testSupervisorGenServerState)
   100  	st.ch <- testMessageTerminated{
   101  		name:  process.Name(),
   102  		pid:   process.Self(),
   103  		order: st.order,
   104  	}
   105  }
   106  
   107  func TestSupervisorOneForOne(t *testing.T) {
   108  	var err error
   109  
   110  	fmt.Printf("\n=== Test Supervisor - one for one\n")
   111  	fmt.Printf("Starting node nodeSvOneForOne@localhost: ")
   112  	node1, _ := ergo.StartNode("nodeSvOneForOne@localhost", "cookies", node.Options{})
   113  	if node1 == nil {
   114  		t.Fatal("can't start node")
   115  	} else {
   116  		fmt.Println("OK")
   117  	}
   118  
   119  	// ===================================================================================================
   120  	// test SupervisorStrategyRestartPermanent
   121  	fmt.Printf("Starting supervisor 'testSupervisorPermanent' (%s)... ", gen.SupervisorStrategyRestartPermanent)
   122  	sv := &testSupervisorOneForOne{
   123  		ch: make(chan interface{}, 10),
   124  	}
   125  	processSV, err := node1.Spawn("testSupervisorPermanent", gen.ProcessOptions{}, sv, gen.SupervisorStrategyRestartPermanent, sv.ch)
   126  	children := make([]etf.Pid, 3)
   127  
   128  	children, err = waitNeventsSupervisorChildren(sv.ch, 3, children)
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	} else {
   132  		fmt.Println("OK")
   133  	}
   134  
   135  	fmt.Printf("... stopping children with 'normal' reason and waiting for their starting ... ")
   136  	for i := range children {
   137  		processSV.Send(children[i], "normal") // stopping child with reason "normal"
   138  	}
   139  
   140  	time.Sleep(1 * time.Second)
   141  	if children1, err := waitNeventsSupervisorChildren(sv.ch, 6, children); err != nil { // waiting for 3 terminates and 3 starts
   142  		t.Fatal(err)
   143  	} else {
   144  		statuses := []string{"new", "new", "new"}
   145  		if checkExpectedChildrenStatus(children[:], children1[:], statuses) {
   146  			fmt.Println("OK")
   147  			children = children1
   148  		} else {
   149  			e := fmt.Errorf("got something else except we expected (%v). old: %v new: %v", statuses, children, children1)
   150  			t.Fatal(e)
   151  		}
   152  	}
   153  
   154  	fmt.Printf("Stopping supervisor 'testSupervisorPermanent' (%s)... ", gen.SupervisorStrategyRestartPermanent)
   155  	processSV.Exit("x")
   156  	if children1, err := waitNeventsSupervisorChildren(sv.ch, 3, children); err != nil {
   157  		t.Fatal(err)
   158  	} else {
   159  		statuses := []string{"empty", "empty", "empty"}
   160  		if checkExpectedChildrenStatus(children[:], children1[:], statuses) {
   161  			fmt.Println("OK")
   162  			children = children1
   163  		} else {
   164  			e := fmt.Errorf("got something else except we expected (%v). old: %v new: %v", statuses, children, children1)
   165  			t.Fatal(e)
   166  		}
   167  	}
   168  
   169  	// ===================================================================================================
   170  	// test SupervisorStrategyRestartTransient
   171  	fmt.Printf("Starting supervisor 'testSupervisorTransient' (%s)... ", gen.SupervisorStrategyRestartTransient)
   172  	sv = &testSupervisorOneForOne{
   173  		ch: make(chan interface{}, 10),
   174  	}
   175  	processSV, _ = node1.Spawn("testSupervisorTransient", gen.ProcessOptions{}, sv, gen.SupervisorStrategyRestartTransient, sv.ch)
   176  	children = make([]etf.Pid, 3)
   177  
   178  	children, err = waitNeventsSupervisorChildren(sv.ch, 3, children)
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	} else {
   182  		fmt.Println("OK")
   183  	}
   184  	fmt.Printf("... stopping children with 'abnormal' reason and waiting for their starting ... ")
   185  	for i := range children {
   186  		processSV.Send(children[i], "abnormal") // stopping child
   187  	}
   188  
   189  	if children1, err := waitNeventsSupervisorChildren(sv.ch, 6, children); err != nil { // waiting for 3 terminates and 3 starts
   190  		t.Fatal(err)
   191  	} else {
   192  		statuses := []string{"new", "new", "new"}
   193  		if checkExpectedChildrenStatus(children[:], children1[:], statuses) {
   194  			fmt.Println("OK")
   195  			children = children1
   196  		} else {
   197  			e := fmt.Errorf("got something else except we expected (%v). old: %v new: %v", statuses, children, children1)
   198  			t.Fatal(e)
   199  		}
   200  	}
   201  
   202  	fmt.Printf("... stopping children with 'normal' reason and they are haven't be restarted ... ")
   203  	for i := range children {
   204  		processSV.Send(children[i], "normal") // stopping child
   205  	}
   206  
   207  	if children1, err := waitNeventsSupervisorChildren(sv.ch, 3, children); err != nil {
   208  		t.Fatal(err)
   209  	} else {
   210  		statuses := []string{"empty", "empty", "empty"}
   211  		if checkExpectedChildrenStatus(children[:], children1[:], statuses) {
   212  			fmt.Println("OK")
   213  			children = children1
   214  		} else {
   215  			e := fmt.Errorf("got something else except we expected (%v). old: %v new: %v", statuses, children, children1)
   216  			t.Fatal(e)
   217  		}
   218  	}
   219  	fmt.Printf("Stopping supervisor 'testSupervisorTransient' (%s)... ", gen.SupervisorStrategyRestartTransient)
   220  	processSV.Exit("x")
   221  	fmt.Println("OK")
   222  
   223  	// ===================================================================================================
   224  	// test SupervisorStrategyRestartTemporary
   225  
   226  	fmt.Printf("Starting supervisor 'testSupervisorTemporary' (%s)... ", gen.SupervisorStrategyRestartTemporary)
   227  	sv = &testSupervisorOneForOne{
   228  		ch: make(chan interface{}, 10),
   229  	}
   230  	processSV, _ = node1.Spawn("testSupervisorTemporary", gen.ProcessOptions{}, sv, gen.SupervisorStrategyRestartTemporary, sv.ch)
   231  	children = make([]etf.Pid, 3)
   232  
   233  	children, err = waitNeventsSupervisorChildren(sv.ch, 3, children)
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	} else {
   237  		fmt.Println("OK")
   238  	}
   239  
   240  	fmt.Printf("... stopping children with 'normal', 'abnornal','shutdown' reasons and they are haven't be restarted ... ")
   241  	processSV.Send(children[0], "normal")   // stopping child
   242  	processSV.Send(children[1], "abnormal") // stopping child
   243  	processSV.Send(children[2], "shutdown") // stopping child
   244  
   245  	if children1, err := waitNeventsSupervisorChildren(sv.ch, 3, children); err != nil {
   246  		t.Fatal(err)
   247  	} else {
   248  		statuses := []string{"empty", "empty", "empty"}
   249  		if checkExpectedChildrenStatus(children[:], children1[:], statuses) {
   250  			fmt.Println("OK")
   251  			children = children1
   252  		} else {
   253  			e := fmt.Errorf("got something else except we expected (%v). old: %v new: %v", statuses, children, children1)
   254  			t.Fatal(e)
   255  		}
   256  	}
   257  	fmt.Printf("Stopping supervisor 'testSupervisorTemporary' (%s)... ", gen.SupervisorStrategyRestartTemporary)
   258  	processSV.Exit("x")
   259  	fmt.Println("OK")
   260  
   261  }
   262  
   263  func (ts *testSupervisorOneForOne) Init(args ...etf.Term) (gen.SupervisorSpec, error) {
   264  	restart := args[0].(string)
   265  	ch := args[1].(chan interface{})
   266  	return gen.SupervisorSpec{
   267  		Children: []gen.SupervisorChildSpec{
   268  			{
   269  				Name:  "testGS1",
   270  				Child: &testSupervisorGenServer{},
   271  				Args:  []etf.Term{ch, 0},
   272  			},
   273  			{
   274  				Name:  "testGS2",
   275  				Child: &testSupervisorGenServer{},
   276  				Args:  []etf.Term{ch, 1},
   277  			},
   278  			{
   279  				Name:  "testGS3",
   280  				Child: &testSupervisorGenServer{},
   281  				Args:  []etf.Term{ch, 2},
   282  			},
   283  		},
   284  		Strategy: gen.SupervisorStrategy{
   285  			Type:      gen.SupervisorStrategyOneForOne,
   286  			Intensity: 10,
   287  			Period:    5,
   288  			Restart:   restart,
   289  		},
   290  	}, nil
   291  }
   292  
   293  func waitNeventsSupervisorChildren(ch chan interface{}, n int, children []etf.Pid) ([]etf.Pid, error) {
   294  	// n - number of events that have to be awaited
   295  	// start for-loop with 'n+1' to handle exceeded number of events
   296  	childrenNew := make([]etf.Pid, len(children))
   297  	copy(childrenNew, children)
   298  	for i := 0; i < n+1; i++ {
   299  		select {
   300  		case c := <-ch:
   301  			switch child := c.(type) {
   302  			case testMessageTerminated:
   303  				childrenNew[child.order] = etf.Pid{} // set empty pid
   304  			case testMessageStarted:
   305  				childrenNew[child.order] = child.pid
   306  			}
   307  
   308  		case <-time.After(200 * time.Millisecond):
   309  			if i == n {
   310  				return childrenNew, nil
   311  			}
   312  			if i < n {
   313  				return childrenNew, fmt.Errorf("expected %d events, but got %d. TIMEOUT", n, i)
   314  			}
   315  
   316  		}
   317  	}
   318  	return childrenNew, fmt.Errorf("expected %d events, but got %d. ", n, n+1)
   319  }
   320  
   321  func checkExpectedChildrenStatus(children, children1 []etf.Pid, statuses []string) bool {
   322  	empty := etf.Pid{}
   323  	for i := 0; i < len(statuses); i++ {
   324  		switch statuses[i] {
   325  		case "new":
   326  			if children1[i] == empty { // is the epmty pid (child has been stopped)
   327  				return false
   328  			}
   329  			if children[i] == children1[i] { // this value has to be different
   330  				return false
   331  			}
   332  
   333  		case "epmty":
   334  			if children1[i] != empty {
   335  				return false
   336  			}
   337  
   338  		case "old":
   339  			if children[i] == children1[i] {
   340  				return true
   341  			}
   342  		}
   343  
   344  	}
   345  	return true
   346  }