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 }