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

     1  package tests
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sort"
     7  	"testing"
     8  
     9  	"github.com/ergo-services/ergo"
    10  	"github.com/ergo-services/ergo/etf"
    11  	"github.com/ergo-services/ergo/gen"
    12  	"github.com/ergo-services/ergo/lib"
    13  	"github.com/ergo-services/ergo/node"
    14  )
    15  
    16  type testMonitor struct {
    17  	gen.Server
    18  	v chan interface{}
    19  }
    20  
    21  func (tgs *testMonitor) Init(process *gen.ServerProcess, args ...etf.Term) error {
    22  	tgs.v <- process.Self()
    23  	return nil
    24  }
    25  func (tgs *testMonitor) HandleCast(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
    26  	tgs.v <- message
    27  	return gen.ServerStatusOK
    28  }
    29  func (tgs *testMonitor) HandleCall(process *gen.ServerProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
    30  	return message, gen.ServerStatusOK
    31  }
    32  func (tgs *testMonitor) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
    33  	tgs.v <- message
    34  	return gen.ServerStatusOK
    35  }
    36  
    37  /*
    38  	Test cases for Local-Local
    39  	Monitor
    40  		by Pid		- doesnt_exist, terminate, demonitor
    41  		by Name		- doesnt_exist, terminate, demonitor
    42  		by Tuple	- doesnt_exist, terminate, demonitor
    43  */
    44  func TestMonitorLocalLocal(t *testing.T) {
    45  	fmt.Printf("\n=== Test Monitor Local-Local\n")
    46  	fmt.Printf("Starting node: nodeM1LocalLocal@localhost: ")
    47  	node1, _ := ergo.StartNode("nodeM1LocalLocal@localhost", "cookies", node.Options{})
    48  	if node1 == nil {
    49  		t.Fatal("can't start node")
    50  	}
    51  	fmt.Println("OK")
    52  	gs1 := &testMonitor{
    53  		v: make(chan interface{}, 2),
    54  	}
    55  	gs2 := &testMonitor{
    56  		v: make(chan interface{}, 2),
    57  	}
    58  	// starting gen servers
    59  
    60  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
    61  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
    62  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
    63  
    64  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
    65  	node1gs2, _ := node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
    66  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
    67  
    68  	// by Pid
    69  	fmt.Printf("... by Pid Local-Local: gs1 -> gs2. monitor/demonitor: ")
    70  	ref := node1gs1.MonitorProcess(node1gs2.Self())
    71  
    72  	if !node1gs2.IsMonitor(ref) {
    73  		t.Fatal("monitor reference has been lost")
    74  	}
    75  	node1gs1.DemonitorProcess(ref)
    76  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	fmt.Println("OK")
    80  
    81  	fmt.Printf("... by Pid Local-Local: gs1 -> gs2. monitor/terminate: ")
    82  	ref = node1gs1.MonitorProcess(node1gs2.Self())
    83  	node1gs2.Exit("normal")
    84  	result := gen.MessageDown{
    85  		Ref:    ref,
    86  		Pid:    node1gs2.Self(),
    87  		Reason: "normal",
    88  	}
    89  	waitForResultWithValue(t, gs1.v, result)
    90  
    91  	if err := checkCleanProcessRef(node1gs2, ref); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	fmt.Print("... by Pid Local-Local: gs1 -> monitor unknownPid: ")
    96  	ref = node1gs1.MonitorProcess(node1gs2.Self())
    97  	result = gen.MessageDown{
    98  		Ref:    ref,
    99  		Pid:    node1gs2.Self(),
   100  		Reason: "noproc",
   101  	}
   102  	waitForResultWithValue(t, gs1.v, result)
   103  
   104  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
   105  	node1gs2, _ = node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   106  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
   107  	// by Name
   108  	fmt.Printf("... by Name Local-Local: gs1 -> gs2. monitor/demonitor: ")
   109  	ref = node1gs1.MonitorProcess("gs2")
   110  	if err := checkCleanProcessRef(node1gs1, ref); err == nil {
   111  		t.Fatal("monitor reference has been lost")
   112  	}
   113  	node1gs1.DemonitorProcess(ref)
   114  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	fmt.Println("OK")
   118  
   119  	fmt.Printf("... by Name Local-Local: gs1 -> gs2. monitor/terminate: ")
   120  	ref = node1gs1.MonitorProcess("gs2")
   121  	node1gs2.Exit("normal")
   122  	result = gen.MessageDown{
   123  		Ref:       ref,
   124  		ProcessID: gen.ProcessID{Name: "gs2", Node: node1.Name()},
   125  		Reason:    "normal",
   126  	}
   127  	waitForResultWithValue(t, gs1.v, result)
   128  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	fmt.Print("... by Name Local-Local: gs1 -> monitor unknown name: ")
   132  	ref = node1gs1.MonitorProcess("asdfasdf")
   133  	result = gen.MessageDown{
   134  		Ref:       ref,
   135  		ProcessID: gen.ProcessID{Name: "asdfasdf", Node: node1.Name()},
   136  		Reason:    "noproc",
   137  	}
   138  	waitForResultWithValue(t, gs1.v, result)
   139  
   140  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
   141  	node1gs2, _ = node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   142  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
   143  
   144  	// by Name gen.ProcessID{ProcessName, Node}
   145  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Local: gs1 -> gs2. demonitor: ")
   146  	processID := gen.ProcessID{Name: "gs2", Node: node1.Name()}
   147  	ref = node1gs1.MonitorProcess(processID)
   148  	if err := checkCleanProcessRef(node1gs1, ref); err == nil {
   149  		t.Fatal("monitor reference has been lost")
   150  	}
   151  	node1gs1.DemonitorProcess(ref)
   152  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	fmt.Println("OK")
   156  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Local: gs1 -> gs2. terminate: ")
   157  	ref = node1gs1.MonitorProcess(processID)
   158  	node1gs2.Exit("normal")
   159  	result = gen.MessageDown{
   160  		Ref:       ref,
   161  		ProcessID: processID,
   162  		Reason:    "normal",
   163  	}
   164  	waitForResultWithValue(t, gs1.v, result)
   165  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	fmt.Print("... by gen.ProcessID{Name, Node} Local-Local: gs1 -> unknownPid: ")
   169  	processID = gen.ProcessID{Name: "gs2222", Node: node1.Name()}
   170  	ref = node1gs1.MonitorProcess(processID)
   171  	result = gen.MessageDown{
   172  		Ref:       ref,
   173  		ProcessID: processID,
   174  		Reason:    "noproc",
   175  	}
   176  	waitForResultWithValue(t, gs1.v, result)
   177  
   178  	node1.Stop()
   179  }
   180  
   181  /*
   182  	Test cases for Local-Remote
   183  	Monitor
   184  		by Pid		- doesnt_exist, terminate, demonitor, on_node_down, node_unknown
   185  		by Tuple	- doesnt_exist, terminate, demonitor, on_node_down, node_unknown
   186  	Link
   187  		by Pid		- doesnt_exist, terminate, unlink, node_down, node_unknown
   188  */
   189  
   190  func TestMonitorLocalRemoteByPid(t *testing.T) {
   191  	fmt.Printf("\n=== Test Monitor Local-Remote by Pid\n")
   192  	fmt.Printf("Starting nodes: nodeM1LocalRemoteByPid@localhost, nodeM2LocalRemoteByPid@localhost: ")
   193  	node1, err1 := ergo.StartNode("nodeM1LocalRemoteByPid@localhost", "cookies", node.Options{})
   194  	node2, err2 := ergo.StartNode("nodeM2LocalRemoteByPid@localhost", "cookies", node.Options{})
   195  	if err1 != nil {
   196  		t.Fatal("can't start node1:", err1)
   197  	}
   198  	if err2 != nil {
   199  		t.Fatal("can't start node2:", err2)
   200  	}
   201  
   202  	fmt.Println("OK")
   203  
   204  	gs1 := &testMonitor{
   205  		v: make(chan interface{}, 2),
   206  	}
   207  	gs2 := &testMonitor{
   208  		v: make(chan interface{}, 2),
   209  	}
   210  
   211  	// starting gen servers
   212  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   213  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   214  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   215  
   216  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
   217  	node2gs2, _ := node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   218  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   219  
   220  	// by Pid
   221  	fmt.Printf("... by Pid Local-Remote: gs1 -> gs2. monitor/demonitor: ")
   222  	ref := node1gs1.MonitorProcess(node2gs2.Self())
   223  	// wait a bit for the MessageDown if something went wrong
   224  	waitForTimeout(t, gs1.v)
   225  	if node1gs1.IsMonitor(ref) == false {
   226  		t.Fatal("monitor reference has been lost on node 1")
   227  	}
   228  	if node2gs2.IsMonitor(ref) == false {
   229  		t.Fatal("monitor reference has been lost on node 2")
   230  	}
   231  	if found := node1gs1.DemonitorProcess(ref); found == false {
   232  		t.Fatal("lost monitoring reference on node1")
   233  	}
   234  	// Demonitoring is the async message with nothing as a feedback.
   235  	// use waitForTimeout just as a short timer
   236  	waitForTimeout(t, gs1.v)
   237  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   238  		t.Fatal(err)
   239  	}
   240  	if err := checkCleanProcessRef(node2gs2, ref); err != nil {
   241  		t.Fatal(err)
   242  	}
   243  	fmt.Println("OK")
   244  
   245  	fmt.Printf("... by Pid Local-Remote: gs1 -> gs2. monitor/terminate: ")
   246  	ref = node1gs1.MonitorProcess(node2gs2.Self())
   247  	// wait a bit for the MessageDown if something went wrong
   248  	waitForTimeout(t, gs1.v)
   249  	node2gs2.Exit("normal")
   250  	result := gen.MessageDown{
   251  		Ref:    ref,
   252  		Pid:    node2gs2.Self(),
   253  		Reason: "normal",
   254  	}
   255  	waitForResultWithValue(t, gs1.v, result)
   256  
   257  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   258  		t.Fatal(err)
   259  	}
   260  	if err := checkCleanProcessRef(node2gs2, ref); err != nil {
   261  		t.Fatal(err)
   262  	}
   263  
   264  	fmt.Printf("... by Pid Local-Remote: gs1 -> monitor unknownPid: ")
   265  	ref = node1gs1.MonitorProcess(node2gs2.Self())
   266  	result = gen.MessageDown{
   267  		Ref:    ref,
   268  		Pid:    node2gs2.Self(),
   269  		Reason: "noproc",
   270  	}
   271  	waitForResultWithValue(t, gs1.v, result)
   272  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
   277  	node2gs2, _ = node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   278  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   279  
   280  	fmt.Printf("... by Pid Local-Remote: gs1 -> gs2. monitor/NodeDown: ")
   281  	ref = node1gs1.MonitorProcess(node2gs2.Self())
   282  	// wait a bit for the MessageDown if something went wrong
   283  	waitForTimeout(t, gs1.v)
   284  	node1.Disconnect(node2.Name())
   285  	node2.Stop()
   286  	result = gen.MessageDown{
   287  		Ref:    ref,
   288  		Pid:    node2gs2.Self(),
   289  		Reason: "noconnection",
   290  	}
   291  	waitForResultWithValue(t, gs1.v, result)
   292  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	fmt.Printf("... by Pid Local-Remote: gs1 -> gs2. monitor unknown node: ")
   297  	ref = node1gs1.MonitorProcess(node2gs2.Self())
   298  	result.Ref = ref
   299  	waitForResultWithValue(t, gs1.v, result)
   300  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   301  		t.Fatal(err)
   302  	}
   303  	node1.Stop()
   304  }
   305  
   306  func TestMonitorLocalRemoteByName(t *testing.T) {
   307  	fmt.Printf("\n=== Test Monitor Local-Remote by Name\n")
   308  	fmt.Printf("Starting nodes: nodeM1LocalRemoteByTuple@localhost, nodeM2LocalRemoteByTuple@localhost: ")
   309  	node1, _ := ergo.StartNode("nodeM1LocalRemoteByTuple@localhost", "cookies", node.Options{})
   310  	node2, _ := ergo.StartNode("nodeM2LocalRemoteByTuple@localhost", "cookies", node.Options{})
   311  	if node1 == nil || node2 == nil {
   312  		t.Fatal("can't start nodes")
   313  	} else {
   314  		fmt.Println("OK")
   315  	}
   316  
   317  	gs1 := &testMonitor{
   318  		v: make(chan interface{}, 2),
   319  	}
   320  	gs2 := &testMonitor{
   321  		v: make(chan interface{}, 2),
   322  	}
   323  
   324  	// starting gen servers
   325  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   326  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   327  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   328  
   329  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
   330  	node2gs2, _ := node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   331  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   332  
   333  	processID := gen.ProcessID{Name: "gs2", Node: node2.Name()}
   334  
   335  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Remote: gs1 -> gs2. monitor/demonitor: ")
   336  	ref := node1gs1.MonitorProcess(processID)
   337  	// wait a bit for the MessageDown if something went wrong
   338  	waitForTimeout(t, gs1.v)
   339  	if err := checkCleanProcessRef(node1gs1, ref); err == nil {
   340  		t.Fatal("monitor reference has been lost on node 1")
   341  	}
   342  	if err := checkCleanProcessRef(node2gs2, ref); err == nil {
   343  		t.Fatal("monitor reference has been lost on node 2")
   344  	}
   345  	if found := node1gs1.DemonitorProcess(ref); found == false {
   346  		t.Fatal("lost monitoring reference on node1")
   347  	}
   348  	// Demonitoring is the async message with nothing as a feedback.
   349  	// use waitForTimeout just as a short timer
   350  	waitForTimeout(t, gs1.v)
   351  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	if err := checkCleanProcessRef(node2gs2, ref); err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	fmt.Println("OK")
   358  
   359  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Remote: gs1 -> gs2. monitor/terminate: ")
   360  	ref = node1gs1.MonitorProcess(processID)
   361  	// wait a bit for the MessageDown if something went wrong
   362  	waitForTimeout(t, gs1.v)
   363  	node2gs2.Exit("normal")
   364  	result := gen.MessageDown{
   365  		Ref:       ref,
   366  		ProcessID: processID,
   367  		Reason:    "normal",
   368  	}
   369  	waitForResultWithValue(t, gs1.v, result)
   370  
   371  	if node1gs1.IsMonitor(ref) {
   372  		t.Fatal("monitor ref is still alive")
   373  	}
   374  	if node2gs2.IsMonitor(ref) {
   375  		t.Fatal("monitor ref is still alive")
   376  	}
   377  
   378  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Remote: gs1 -> monitor unknown remote name: ")
   379  	ref = node1gs1.MonitorProcess(processID)
   380  	result = gen.MessageDown{
   381  		Ref:       ref,
   382  		ProcessID: processID,
   383  		Reason:    "noproc",
   384  	}
   385  	waitForResultWithValue(t, gs1.v, result)
   386  	if node1gs1.IsMonitor(ref) {
   387  		t.Fatal("monitor ref is still alive")
   388  	}
   389  
   390  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
   391  	node2gs2, _ = node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   392  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   393  
   394  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Remote: gs1 -> gs2. monitor/onNodeDown: ")
   395  	ref = node1gs1.MonitorProcess(processID)
   396  	node1.Disconnect(node2.Name())
   397  	node2.Stop()
   398  	result = gen.MessageDown{
   399  		Ref:       ref,
   400  		ProcessID: processID,
   401  		Reason:    "noconnection",
   402  	}
   403  	waitForResultWithValue(t, gs1.v, result)
   404  	if node1gs1.IsMonitor(ref) {
   405  		t.Fatal("monitor ref is still alive")
   406  	}
   407  
   408  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Remote: gs1 -> gs2. monitor unknown node: ")
   409  	ref = node1gs1.MonitorProcess(processID)
   410  	result.Ref = ref
   411  	waitForResultWithValue(t, gs1.v, result)
   412  	if node1gs1.IsMonitor(ref) {
   413  		t.Fatal("monitor ref is still alive")
   414  	}
   415  	node1.Stop()
   416  }
   417  
   418  func TestMonitorLocalProxyRemoteByPid(t *testing.T) {
   419  	fmt.Printf("\n=== Test Monitor Remote via Proxy by Pid\n")
   420  	fmt.Printf("Starting nodes: nodeM1ProxyRemoteByPid@localhost, nodeM2ProxyRemoteByPid@localhost, nodeM3ProxyRemoteByPid@localhost : ")
   421  	opts1 := node.Options{}
   422  	opts1.Proxy.Flags = node.DefaultProxyFlags()
   423  	opts1.Proxy.Flags.EnableMonitor = false
   424  	node1, err := ergo.StartNode("nodeM1ProxyRemoteByPid@localhost", "cookies", opts1)
   425  	if err != nil {
   426  		t.Fatal("can't start node:", err)
   427  	}
   428  	opts2 := node.Options{}
   429  	opts2.Proxy.Transit = true
   430  	node2, err := ergo.StartNode("nodeM2ProxyRemoteByPid@localhost", "cookies", opts2)
   431  	if err != nil {
   432  		t.Fatal("can't start node:", err, node2.Name())
   433  	}
   434  	opts3 := node.Options{}
   435  	opts3.Proxy.Accept = true
   436  	node3, err := ergo.StartNode("nodeM3ProxyRemoteByPid@localhost", "cookies", opts3)
   437  	if err != nil {
   438  		t.Fatal("can't start node:", err)
   439  	}
   440  
   441  	route := node.ProxyRoute{
   442  		Name:  node3.Name(),
   443  		Proxy: node2.Name(),
   444  	}
   445  	node1.AddProxyRoute(route)
   446  	node1.Connect(node3.Name())
   447  	fmt.Println("OK")
   448  
   449  	gs1 := &testMonitor{
   450  		v: make(chan interface{}, 2),
   451  	}
   452  	gs3 := &testMonitor{
   453  		v: make(chan interface{}, 2),
   454  	}
   455  
   456  	// starting gen servers
   457  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   458  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   459  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   460  
   461  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
   462  	node3gs3, _ := node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
   463  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
   464  
   465  	// by Pid
   466  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> gs3. monitor/demonitor: ")
   467  	ref := node1gs1.MonitorProcess(node3gs3.Self())
   468  	// wait a bit for the MessageDown if something went wrong
   469  	waitForTimeout(t, gs1.v)
   470  	if node1gs1.IsMonitor(ref) == false {
   471  		t.Fatal("monitor reference has been lost on node 1")
   472  	}
   473  	if node3gs3.IsMonitor(ref) == false {
   474  		t.Fatal("monitor reference has been lost on node 3")
   475  	}
   476  	if found := node1gs1.DemonitorProcess(ref); found == false {
   477  		t.Fatal("lost monitoring reference on node1")
   478  	}
   479  	// Demonitoring is the async message with nothing as a feedback.
   480  	// use waitForTimeout just as a short timer
   481  	waitForTimeout(t, gs1.v)
   482  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   483  		t.Fatal(err)
   484  	}
   485  	if err := checkCleanProcessRef(node3gs3, ref); err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	fmt.Println("OK")
   489  
   490  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> gs3. monitor/terminate: ")
   491  	ref = node1gs1.MonitorProcess(node3gs3.Self())
   492  	// wait a bit for the MessageDown if something went wrong
   493  	waitForTimeout(t, gs1.v)
   494  	node3gs3.Exit("normal")
   495  	result := gen.MessageDown{
   496  		Ref:    ref,
   497  		Pid:    node3gs3.Self(),
   498  		Reason: "normal",
   499  	}
   500  	waitForResultWithValue(t, gs1.v, result)
   501  
   502  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   503  		t.Fatal(err)
   504  	}
   505  	if err := checkCleanProcessRef(node3gs3, ref); err != nil {
   506  		t.Fatal(err)
   507  	}
   508  
   509  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> monitor unknownPid: ")
   510  	ref = node1gs1.MonitorProcess(node3gs3.Self())
   511  	result = gen.MessageDown{
   512  		Ref:    ref,
   513  		Pid:    node3gs3.Self(),
   514  		Reason: "noproc",
   515  	}
   516  	waitForResultWithValue(t, gs1.v, result)
   517  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   518  		t.Fatal(err)
   519  	}
   520  
   521  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
   522  	node3gs3, _ = node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
   523  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
   524  
   525  	fmt.Printf("... by Pid Local-Proxy-Remote: gs3 -> gs1. monitor/(node1: ProxyFlags.EnableMonitor = false): ")
   526  
   527  	ref = node3gs3.MonitorProcess(node1gs1.Self())
   528  	result = gen.MessageDown{
   529  		Ref:    ref,
   530  		Pid:    node1gs1.Self(),
   531  		Reason: "unsupported",
   532  	}
   533  	waitForResultWithValue(t, gs3.v, result)
   534  
   535  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> gs3. monitor/ProxyDown: ")
   536  	ref = node1gs1.MonitorProcess(node3gs3.Self())
   537  	waitForTimeout(t, gs1.v)
   538  	node2.Stop()
   539  	result = gen.MessageDown{
   540  		Ref:    ref,
   541  		Pid:    node3gs3.Self(),
   542  		Reason: "noproxy",
   543  	}
   544  	waitForResultWithValue(t, gs1.v, result)
   545  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   546  		t.Fatal(err)
   547  	}
   548  
   549  	node2, err = ergo.StartNode("nodeM2ProxyRemoteByPid@localhost", "cookies", opts2)
   550  	if err != nil {
   551  		t.Fatal("can't start node:", err, node2.Name())
   552  	}
   553  
   554  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> gs3. monitor/NodeDown: ")
   555  	ref = node1gs1.MonitorProcess(node3gs3.Self())
   556  	// wait a bit for the MessageDown if something went wrong
   557  	waitForTimeout(t, gs1.v)
   558  	node3.Stop()
   559  	result = gen.MessageDown{
   560  		Ref:    ref,
   561  		Pid:    node3gs3.Self(),
   562  		Reason: "noconnection",
   563  	}
   564  	waitForResultWithValue(t, gs1.v, result)
   565  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   566  		t.Fatal(err)
   567  	}
   568  
   569  	fmt.Printf("... by Pid Local-Proxy-Remote: gs1 -> gs3. monitor unknown node: ")
   570  	ref = node1gs1.MonitorProcess(node3gs3.Self())
   571  	result.Ref = ref
   572  	waitForResultWithValue(t, gs1.v, result)
   573  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   574  		t.Fatal(err)
   575  	}
   576  	node1.Stop()
   577  }
   578  
   579  func TestMonitorLocalProxyRemoteByName(t *testing.T) {
   580  	fmt.Printf("\n=== Test Monitor Local-Proxy-Remote by Name\n")
   581  	fmt.Printf("Starting nodes: nodeM1ProxyRemoteByName@localhost, nodeM2RemoteByName@localhost, nodeM3RemoteByName@localhost: ")
   582  	opts1 := node.Options{}
   583  	opts1.Proxy.Flags = node.DefaultProxyFlags()
   584  	opts1.Proxy.Flags.EnableMonitor = false
   585  	node1, err := ergo.StartNode("nodeM1RemoteByName@localhost", "cookies", opts1)
   586  	if err != nil {
   587  		t.Fatal("can't start node:", err)
   588  	}
   589  	opts2 := node.Options{}
   590  	opts2.Proxy.Transit = true
   591  	node2, err := ergo.StartNode("nodeM2RemoteByName@localhost", "cookies", opts2)
   592  	if err != nil {
   593  		t.Fatal("can't start node:", err)
   594  	}
   595  	opts3 := node.Options{}
   596  	opts3.Proxy.Accept = true
   597  	node3, err := ergo.StartNode("nodeM3RemoteByName@localhost", "cookies", opts3)
   598  	if err != nil {
   599  		t.Fatal("can't start node:", err)
   600  	}
   601  	route := node.ProxyRoute{
   602  		Name:  node3.Name(),
   603  		Proxy: node2.Name(),
   604  	}
   605  	node1.AddProxyRoute(route)
   606  	node1.Connect(node3.Name())
   607  	fmt.Println("OK")
   608  
   609  	gs1 := &testMonitor{
   610  		v: make(chan interface{}, 2),
   611  	}
   612  	gs3 := &testMonitor{
   613  		v: make(chan interface{}, 2),
   614  	}
   615  
   616  	// starting gen servers
   617  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   618  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   619  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   620  
   621  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
   622  	node3gs3, _ := node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
   623  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
   624  
   625  	processID := gen.ProcessID{Name: "gs3", Node: node3.Name()}
   626  
   627  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> gs3. monitor/demonitor: ")
   628  	ref := node1gs1.MonitorProcess(processID)
   629  	// wait a bit for the MessageDown if something went wrong
   630  	waitForTimeout(t, gs1.v)
   631  	if err := checkCleanProcessRef(node1gs1, ref); err == nil {
   632  		t.Fatal("monitor reference has been lost on node 1")
   633  	}
   634  	if err := checkCleanProcessRef(node3gs3, ref); err == nil {
   635  		t.Fatal("monitor reference has been lost on node 3")
   636  	}
   637  	if found := node1gs1.DemonitorProcess(ref); found == false {
   638  		t.Fatal("lost monitoring reference on node1")
   639  	}
   640  	// Demonitoring is the async message with nothing as a feedback.
   641  	// use waitForTimeout just as a short timer
   642  	waitForTimeout(t, gs1.v)
   643  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   644  		t.Fatal(err)
   645  	}
   646  	if err := checkCleanProcessRef(node3gs3, ref); err != nil {
   647  		t.Fatal(err)
   648  	}
   649  	fmt.Println("OK")
   650  
   651  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> gs3. monitor/terminate: ")
   652  	ref = node1gs1.MonitorProcess(processID)
   653  	// wait a bit for the MessageDown if something went wrong
   654  	waitForTimeout(t, gs1.v)
   655  	if node1gs1.IsMonitor(ref) == false {
   656  		t.Fatal("monitor reference has been lost on node 1")
   657  	}
   658  	if node3gs3.IsMonitor(ref) == false {
   659  		t.Fatal("monitor reference has been lost on node 3")
   660  	}
   661  	node3gs3.Exit("normal")
   662  	result := gen.MessageDown{
   663  		Ref:       ref,
   664  		ProcessID: processID,
   665  		Reason:    "normal",
   666  	}
   667  	waitForResultWithValue(t, gs1.v, result)
   668  
   669  	if node1gs1.IsMonitor(ref) {
   670  		t.Fatal("monitor ref is still alive")
   671  	}
   672  	if node3gs3.IsMonitor(ref) {
   673  		t.Fatal("monitor ref is still alive")
   674  	}
   675  
   676  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> monitor unknown remote name: ")
   677  	ref = node1gs1.MonitorProcess(processID)
   678  	result = gen.MessageDown{
   679  		Ref:       ref,
   680  		ProcessID: processID,
   681  		Reason:    "noproc",
   682  	}
   683  	waitForResultWithValue(t, gs1.v, result)
   684  	if node1gs1.IsMonitor(ref) {
   685  		t.Fatal("monitor ref is still alive")
   686  	}
   687  
   688  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
   689  	node3gs3, _ = node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
   690  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
   691  
   692  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs3 -> gs1. monitor/(node1: ProxyFlags.EnableMonitor = false): ")
   693  
   694  	processID1 := gen.ProcessID{Name: node1gs1.Name(), Node: node1.Name()}
   695  	ref = node3gs3.MonitorProcess(processID1)
   696  	result = gen.MessageDown{
   697  		Ref:       ref,
   698  		ProcessID: processID1,
   699  		Reason:    "unsupported",
   700  	}
   701  	waitForResultWithValue(t, gs3.v, result)
   702  
   703  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> gs3. monitor/ProxyDown: ")
   704  	ref = node1gs1.MonitorProcess(processID)
   705  	waitForTimeout(t, gs1.v)
   706  	node2.Stop()
   707  	result = gen.MessageDown{
   708  		Ref:       ref,
   709  		ProcessID: processID,
   710  		Reason:    "noproxy",
   711  	}
   712  	waitForResultWithValue(t, gs1.v, result)
   713  	if err := checkCleanProcessRef(node1gs1, ref); err != nil {
   714  		t.Fatal(err)
   715  	}
   716  
   717  	node2, err = ergo.StartNode("nodeM2RemoteByName@localhost", "cookies", opts2)
   718  	if err != nil {
   719  		t.Fatal("can't start node:", err, node2.Name())
   720  	}
   721  
   722  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> gs3. monitor/NodeDown: ")
   723  	ref = node1gs1.MonitorProcess(processID)
   724  	waitForTimeout(t, gs1.v)
   725  	node3.Stop()
   726  	result = gen.MessageDown{
   727  		Ref:       ref,
   728  		ProcessID: processID,
   729  		Reason:    "noconnection",
   730  	}
   731  	waitForResultWithValue(t, gs1.v, result)
   732  	if node1gs1.IsMonitor(ref) {
   733  		t.Fatal("monitor ref is still alive")
   734  	}
   735  
   736  	fmt.Printf("... by gen.ProcessID{Name, Node} Local-Proxy-Remote: gs1 -> gs3. monitor unknown node: ")
   737  	ref = node1gs1.MonitorProcess(processID)
   738  	result.Ref = ref
   739  	waitForResultWithValue(t, gs1.v, result)
   740  	if node1gs1.IsMonitor(ref) {
   741  		t.Fatal("monitor ref is still alive")
   742  	}
   743  	node1.Stop()
   744  }
   745  
   746  /*
   747  	Test cases for Local-Local
   748  	Link
   749  		by Pid		- equal_pids, already_linked, doesnt_exist, terminate, unlink
   750  */
   751  
   752  func TestLinkLocal(t *testing.T) {
   753  	fmt.Printf("\n=== Test Link Local\n")
   754  	fmt.Printf("Starting node: nodeL1LocalLocal@localhost: ")
   755  	node1, _ := ergo.StartNode("nodeL1LocalLocal@localhost", "cookies", node.Options{})
   756  	if node1 == nil {
   757  		t.Fatal("can't start node")
   758  	} else {
   759  		fmt.Println("OK")
   760  	}
   761  	gs1 := &testMonitor{
   762  		v: make(chan interface{}, 2),
   763  	}
   764  	gs2 := &testMonitor{
   765  		v: make(chan interface{}, 2),
   766  	}
   767  	// starting gen servers
   768  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   769  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   770  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   771  
   772  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
   773  	node1gs2, _ := node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   774  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
   775  
   776  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs1 (link to itself is not allowed): ")
   777  	node1gs1.Link(node1gs1.Self())
   778  	if err := checkCleanLinkPid(node1gs1, node1gs1.Self()); err != nil {
   779  		t.Fatal(err)
   780  	}
   781  	fmt.Println("OK")
   782  
   783  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. link/unlink: ")
   784  	node1gs1.Link(node1gs2.Self())
   785  
   786  	if err := checkLinkPid(node1gs1, node1gs2.Self()); err != nil {
   787  		t.Fatal(err)
   788  	}
   789  	if err := checkLinkPid(node1gs2, node1gs1.Self()); err != nil {
   790  		t.Fatal(err)
   791  	}
   792  
   793  	node1gs1.Unlink(node1gs2.Self())
   794  	if err := checkCleanLinkPid(node1gs1, node1gs2.Self()); err != nil {
   795  		t.Fatal(err)
   796  	}
   797  	if err := checkCleanLinkPid(node1gs2, node1gs1.Self()); err != nil {
   798  		t.Fatal(err)
   799  	}
   800  	fmt.Println("OK")
   801  
   802  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. already_linked: ")
   803  	node1gs1.Link(node1gs2.Self())
   804  	if err := checkLinkPid(node1gs1, node1gs2.Self()); err != nil {
   805  		t.Fatal(err)
   806  	}
   807  	if err := checkLinkPid(node1gs2, node1gs1.Self()); err != nil {
   808  		t.Fatal("link missing for node1gs2")
   809  	}
   810  	gs1links := node1gs1.Links()
   811  	gs2links := node1gs2.Links()
   812  	node1gs2.Link(node1gs1.Self())
   813  
   814  	if len(gs1links) != len(node1gs1.Links()) || len(gs2links) != len(node1gs2.Links()) {
   815  		t.Fatal("number of links has changed on the second Link call")
   816  	}
   817  	fmt.Println("OK")
   818  
   819  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. terminate (trap_exit = true): ")
   820  	// do not link these process since they are already linked after the previous test
   821  	//node1gs1.Link(node1gs2.Self())
   822  
   823  	if checkLinkPid(node1gs1, node1gs2.Self()) != nil {
   824  		t.Fatal("link missing for node1gs1")
   825  	}
   826  	if checkLinkPid(node1gs2, node1gs1.Self()) != nil {
   827  		t.Fatal("link missing for node1gs2")
   828  	}
   829  
   830  	node1gs1.SetTrapExit(true)
   831  	node1gs2.Exit("normal")
   832  	result := gen.MessageExit{Pid: node1gs2.Self(), Reason: "normal"}
   833  	waitForResultWithValue(t, gs1.v, result)
   834  
   835  	if err := checkCleanLinkPid(node1gs2, node1gs1.Self()); err != nil {
   836  		t.Fatal(err)
   837  	}
   838  	if node1gs2.IsAlive() {
   839  		t.Fatal("node1gs2 must be terminated")
   840  	}
   841  	if err := checkCleanLinkPid(node1gs1, node1gs2.Self()); err != nil {
   842  		t.Fatal(err)
   843  	}
   844  	if !node1gs1.IsAlive() {
   845  		t.Fatal("gs1 should be alive after gs2 exit due to enabled trap exit on gs1")
   846  	}
   847  
   848  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. doesnt_exist: ")
   849  	node1gs1.Link(node1gs2.Self())
   850  	result = gen.MessageExit{Pid: node1gs2.Self(), Reason: "noproc"}
   851  	waitForResultWithValue(t, gs1.v, result)
   852  
   853  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
   854  	node1gs2, _ = node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   855  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
   856  
   857  	node1gs1.SetTrapExit(false)
   858  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. terminate (trap_exit = false): ")
   859  	node1gs1.Link(node1gs2.Self())
   860  
   861  	if checkLinkPid(node1gs2, node1gs1.Self()) != nil {
   862  		t.Fatal("link missing for node1gs1")
   863  	}
   864  	if checkLinkPid(node1gs1, node1gs2.Self()) != nil {
   865  		t.Fatal("link missing for node1gs2")
   866  	}
   867  
   868  	node1gs2.Exit("normal")
   869  
   870  	// wait a bit to make sure if we receive anything (shouldnt receive)
   871  	waitForTimeout(t, gs1.v)
   872  	fmt.Println("OK")
   873  
   874  	if err := checkCleanLinkPid(node1gs2, node1gs1.Self()); err != nil {
   875  		t.Fatal(err)
   876  	}
   877  	if err := checkCleanLinkPid(node1gs1, node1gs2.Self()); err != nil {
   878  		t.Fatal(err)
   879  	}
   880  	if node1gs1.IsAlive() {
   881  		t.Fatal("gs1 shouldnt be alive after gs2 exit due to disable trap exit on gs1")
   882  	}
   883  	if node1gs2.IsAlive() {
   884  		t.Fatal("node1gs2 must be terminated")
   885  	}
   886  
   887  	node1.Stop()
   888  }
   889  
   890  /*
   891  	Test cases for Local-Remote
   892  	Link
   893  		by Pid		- already_linked, doesnt_exist, terminate, unlink, node_down, node_unknown
   894  */
   895  func TestLinkRemote(t *testing.T) {
   896  	fmt.Printf("\n=== Test Link Remote by Pid\n")
   897  	fmt.Printf("Starting nodes: nodeL1LocalRemoteByPid@localhost, nodeL2LocalRemoteByPid@localhost: ")
   898  	node1, _ := ergo.StartNode("nodeL1LocalRemoteByPid@localhost", "cookies", node.Options{})
   899  	node2, _ := ergo.StartNode("nodeL2LocalRemoteByPid@localhost", "cookies", node.Options{})
   900  	if node1 == nil || node2 == nil {
   901  		t.Fatal("can't start nodes")
   902  	} else {
   903  		fmt.Println("OK")
   904  	}
   905  
   906  	gs1 := &testMonitor{
   907  		v: make(chan interface{}, 2),
   908  	}
   909  	gs2 := &testMonitor{
   910  		v: make(chan interface{}, 2),
   911  	}
   912  
   913  	// starting gen servers
   914  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
   915  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
   916  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
   917  
   918  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
   919  	node2gs2, _ := node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   920  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   921  
   922  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. unlink: ")
   923  	node1gs1.Link(node2gs2.Self())
   924  	// wait a bit since linking process is async
   925  	waitForTimeout(t, gs1.v)
   926  
   927  	if checkLinkPid(node1gs1, node2gs2.Self()) != nil {
   928  		t.Fatal("link missing on node1gs1")
   929  	}
   930  	if checkLinkPid(node2gs2, node1gs1.Self()) != nil {
   931  		t.Fatal("link missing on node2gs2 ")
   932  	}
   933  
   934  	node1gs1.Unlink(node2gs2.Self())
   935  	// wait a bit since unlinking process is async
   936  	waitForTimeout(t, gs1.v)
   937  	if err := checkCleanLinkPid(node1gs1, node2gs2.Self()); err != nil {
   938  		t.Fatal(err)
   939  	}
   940  	if err := checkCleanLinkPid(node2gs2, node1gs1.Self()); err != nil {
   941  		t.Fatal(err)
   942  	}
   943  	fmt.Println("OK")
   944  
   945  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. already_linked: ")
   946  	node1gs1.Link(node2gs2.Self())
   947  	if checkLinkPid(node1gs1, node2gs2.Self()) != nil {
   948  		t.Fatal("link missing on node1gs1")
   949  	}
   950  	// wait a bit since linking process is async
   951  	waitForTimeout(t, gs1.v)
   952  	if checkLinkPid(node2gs2, node1gs1.Self()) != nil {
   953  		t.Fatal("link missing on node2gs2")
   954  	}
   955  	ll1 := len(node1gs1.Links())
   956  	ll2 := len(node2gs2.Links())
   957  
   958  	node2gs2.Link(node1gs1.Self())
   959  	// wait a bit since linking process is async
   960  	waitForTimeout(t, gs2.v)
   961  
   962  	if ll1 != len(node1gs1.Links()) || ll2 != len(node2gs2.Links()) {
   963  		t.Fatal("number of links has changed on the second Link call")
   964  	}
   965  	fmt.Println("OK")
   966  
   967  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. terminate (trap_exit = true): ")
   968  	// do not link these process since they are already linked after the previous test
   969  
   970  	node1gs1.SetTrapExit(true)
   971  
   972  	node2gs2.Exit("normal")
   973  	result := gen.MessageExit{Pid: node2gs2.Self(), Reason: "normal"}
   974  	waitForResultWithValue(t, gs1.v, result)
   975  
   976  	if err := checkCleanLinkPid(node1gs1, node2gs2.Self()); err != nil {
   977  		t.Fatal(err)
   978  	}
   979  	if err := checkCleanLinkPid(node2gs2, node1gs1.Self()); err != nil {
   980  		t.Fatal(err)
   981  	}
   982  	if !node1gs1.IsAlive() {
   983  		t.Fatal("gs1 should be alive after gs2 exit due to enabled trap exit on gs1")
   984  	}
   985  
   986  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. doesnt_exist: ")
   987  	ll1 = len(node1gs1.Links())
   988  	node1gs1.Link(node2gs2.Self())
   989  	result = gen.MessageExit{Pid: node2gs2.Self(), Reason: "noproc"}
   990  	waitForResultWithValue(t, gs1.v, result)
   991  	if ll1 != len(node1gs1.Links()) {
   992  		t.Fatal("number of links has changed on the second Link call")
   993  	}
   994  
   995  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
   996  	node2gs2, _ = node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
   997  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
   998  
   999  	node1gs1.SetTrapExit(false)
  1000  	fmt.Printf("Testing Link process (by Pid only) Local-Local: gs1 -> gs2. terminate (trap_exit = false): ")
  1001  	node1gs1.Link(node2gs2.Self())
  1002  	waitForTimeout(t, gs2.v)
  1003  
  1004  	if checkLinkPid(node1gs1, node2gs2.Self()) != nil {
  1005  		t.Fatal("link missing on node1gs1")
  1006  	}
  1007  	if checkLinkPid(node2gs2, node1gs1.Self()) != nil {
  1008  		t.Fatal("link missing on node2gs2")
  1009  	}
  1010  
  1011  	node2gs2.Exit("normal")
  1012  
  1013  	// wait a bit to make sure if we receive anything (shouldnt receive)
  1014  	waitForTimeout(t, gs1.v)
  1015  
  1016  	if err := checkCleanLinkPid(node1gs1, node2gs2.Self()); err != nil {
  1017  		t.Fatal(err)
  1018  	}
  1019  	if err := checkCleanLinkPid(node2gs2, node1gs1.Self()); err != nil {
  1020  		t.Fatal(err)
  1021  	}
  1022  	if node1gs1.IsAlive() {
  1023  		t.Fatal("gs1 shouldnt be alive after gs2 exit due to disable trap exit on gs1")
  1024  	}
  1025  	if node2gs2.IsAlive() {
  1026  		t.Fatal("gs2 must be terminated")
  1027  	}
  1028  	fmt.Println("OK")
  1029  
  1030  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
  1031  	node1gs1, _ = node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
  1032  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
  1033  	fmt.Printf("    wait for start of gs2 on %#v: ", node2.Name())
  1034  	node2gs2, _ = node2.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
  1035  	waitForResultWithValue(t, gs2.v, node2gs2.Self())
  1036  
  1037  	node1gs1.SetTrapExit(true)
  1038  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. node_down: ")
  1039  	node1gs1.Link(node2gs2.Self())
  1040  	waitForTimeout(t, gs1.v)
  1041  
  1042  	if checkLinkPid(node1gs1, node2gs2.Self()) != nil {
  1043  		t.Fatal("link missing for node1gs1")
  1044  	}
  1045  	if checkCleanLinkPid(node2gs2, node1gs1.Self()) == nil {
  1046  		t.Fatal("link missing for node2gs2")
  1047  	}
  1048  
  1049  	// race conditioned case.
  1050  	// processing of the process termination (on the remote peer) can be done faster than
  1051  	// the link termination there, so MessageExit with "kill" reason will be arrived
  1052  	// earlier.
  1053  	node2.Stop()
  1054  	result1 := gen.MessageExit{Pid: node2gs2.Self(), Reason: "noconnection"}
  1055  	result2 := gen.MessageExit{Pid: node2gs2.Self(), Reason: "kill"}
  1056  
  1057  	waitForResultWithValueOrValue(t, gs1.v, result1, result2)
  1058  
  1059  	if err := checkCleanLinkPid(node1gs1, node2gs2.Self()); err != nil {
  1060  		t.Fatal(err)
  1061  	}
  1062  	// must wait a bit
  1063  	waitForTimeout(t, gs1.v)
  1064  	if err := checkCleanLinkPid(node2gs2, node1gs1.Self()); err != nil {
  1065  		t.Fatal(err)
  1066  	}
  1067  
  1068  	ll1 = len(node1gs1.Links())
  1069  	fmt.Printf("Testing Link process (by Pid only) Local-Remote: gs1 -> gs2. node_unknown: ")
  1070  	node1gs1.Link(node2gs2.Self())
  1071  	result = gen.MessageExit{Pid: node2gs2.Self(), Reason: "noconnection"}
  1072  	waitForResultWithValue(t, gs1.v, result)
  1073  
  1074  	if ll1 != len(node1gs1.Links()) {
  1075  		t.Fatal("number of links has changed on the second Link call")
  1076  	}
  1077  	node1.Stop()
  1078  }
  1079  
  1080  func TestLinkRemoteProxy(t *testing.T) {
  1081  	fmt.Printf("\n=== Test Link Remote Via Proxy\n")
  1082  	fmt.Printf("Starting nodes: nodeL1RemoteViaProxy@localhost, nodeL2RemoteViaProxy@localhost, nodeL3RemoteViaProxy@localhost: ")
  1083  	node1, err := ergo.StartNode("nodeL1RemoteViaProxy@localhost", "cookies", node.Options{})
  1084  	if err != nil {
  1085  		t.Fatal(err)
  1086  	}
  1087  	node2opts := node.Options{}
  1088  	node2opts.Proxy.Transit = true
  1089  	node2, err := ergo.StartNode("nodeL2RemoteViaProxy@localhost", "cookies", node2opts)
  1090  	if err != nil {
  1091  		t.Fatal(err)
  1092  	}
  1093  	node3opts := node.Options{}
  1094  	node3opts.Proxy.Accept = true
  1095  	node3, err := ergo.StartNode("nodeL3RemoteViaProxy@localhost", "cookies", node3opts)
  1096  	if err != nil {
  1097  		t.Fatal(err)
  1098  	}
  1099  	fmt.Println("OK")
  1100  
  1101  	route := node.ProxyRoute{
  1102  		Name:  node3.Name(),
  1103  		Proxy: node2.Name(),
  1104  	}
  1105  	route.Flags = node.DefaultProxyFlags()
  1106  	route.Flags.EnableLink = false
  1107  	node1.AddProxyRoute(route)
  1108  
  1109  	fmt.Printf("    check connectivity of %s with %s via proxy %s: ", node1.Name(), node3.Name(), node2.Name())
  1110  	if err := node1.Connect(node3.Name()); err != nil {
  1111  		t.Fatal(err)
  1112  	}
  1113  	node1indirect := node1.NodesIndirect()
  1114  	node3indirect := node3.NodesIndirect()
  1115  	if len(node1indirect) != 1 || len(node3indirect) != 1 {
  1116  		t.Fatal("wrong indirect nodes (node1:", node1indirect, "; node3:", node3indirect, ")")
  1117  	}
  1118  	if node1indirect[0] != node3.Name() || node3indirect[0] != node1.Name() {
  1119  		t.Fatal("wrong indirect nodes (node1:", node1indirect, "; node3:", node3indirect, ")")
  1120  	}
  1121  	fmt.Println("OK")
  1122  	gs1 := &testMonitor{
  1123  		v: make(chan interface{}, 2),
  1124  	}
  1125  	gs3 := &testMonitor{
  1126  		v: make(chan interface{}, 2),
  1127  	}
  1128  
  1129  	// starting gen servers
  1130  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
  1131  	node1gs1, _ := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
  1132  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
  1133  
  1134  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
  1135  	node3gs3, _ := node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
  1136  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
  1137  
  1138  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. unlink: ")
  1139  	node1gs1.Link(node3gs3.Self())
  1140  	// wait a bit since linking process is async
  1141  	waitForTimeout(t, gs1.v)
  1142  
  1143  	if checkLinkPid(node1gs1, node3gs3.Self()) != nil {
  1144  		t.Fatal("link missing on node1gs1")
  1145  	}
  1146  	if checkLinkPid(node3gs3, node1gs1.Self()) != nil {
  1147  		t.Fatal("link missing on node3gs3 ")
  1148  	}
  1149  
  1150  	node1gs1.Unlink(node3gs3.Self())
  1151  	// wait a bit since unlinking process is async
  1152  	waitForTimeout(t, gs1.v)
  1153  	if err := checkCleanLinkPid(node1gs1, node3gs3.Self()); err != nil {
  1154  		t.Fatal(err)
  1155  	}
  1156  	if err := checkCleanLinkPid(node3gs3, node1gs1.Self()); err != nil {
  1157  		t.Fatal(err)
  1158  	}
  1159  	fmt.Println("OK")
  1160  
  1161  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. already_linked: ")
  1162  	node1gs1.Link(node3gs3.Self())
  1163  	if checkLinkPid(node1gs1, node3gs3.Self()) != nil {
  1164  		t.Fatal("link missing on node1gs1")
  1165  	}
  1166  	// wait a bit since linking process is async
  1167  	waitForTimeout(t, gs1.v)
  1168  	if checkLinkPid(node3gs3, node1gs1.Self()) != nil {
  1169  		t.Fatal("link missing on node3gs3")
  1170  	}
  1171  	ll1 := len(node1gs1.Links())
  1172  	ll3 := len(node3gs3.Links())
  1173  
  1174  	node3gs3.Link(node1gs1.Self())
  1175  	// wait a bit since linking process is async
  1176  	waitForTimeout(t, gs3.v)
  1177  
  1178  	if ll1 != len(node1gs1.Links()) || ll3 != len(node3gs3.Links()) {
  1179  		t.Fatal("number of links has changed on the second Link call")
  1180  	}
  1181  	fmt.Println("OK")
  1182  
  1183  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. terminate (trap_exit = true): ")
  1184  	// do not link these process since they are already linked after the previous test
  1185  
  1186  	node1gs1.SetTrapExit(true)
  1187  
  1188  	node3gs3.Exit("normal")
  1189  	result := gen.MessageExit{Pid: node3gs3.Self(), Reason: "normal"}
  1190  	waitForResultWithValue(t, gs1.v, result)
  1191  
  1192  	if err := checkCleanLinkPid(node1gs1, node3gs3.Self()); err != nil {
  1193  		t.Fatal(err)
  1194  	}
  1195  	if err := checkCleanLinkPid(node3gs3, node1gs1.Self()); err != nil {
  1196  		t.Fatal(err)
  1197  	}
  1198  	if !node1gs1.IsAlive() {
  1199  		t.Fatal("gs1 should be alive after gs3 exit due to enabled trap exit on gs1")
  1200  	}
  1201  
  1202  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. doesnt_exist: ")
  1203  	ll1 = len(node1gs1.Links())
  1204  	node1gs1.Link(node3gs3.Self())
  1205  	result = gen.MessageExit{Pid: node3gs3.Self(), Reason: "noproc"}
  1206  	waitForResultWithValue(t, gs1.v, result)
  1207  	if ll1 != len(node1gs1.Links()) {
  1208  		t.Fatal("number of links has changed on the second Link call")
  1209  	}
  1210  
  1211  	fmt.Printf("    wait for start of gs3 on %#v: ", node1.Name())
  1212  	node3gs3, _ = node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
  1213  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
  1214  
  1215  	node1gs1.SetTrapExit(false)
  1216  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. terminate (trap_exit = false): ")
  1217  	node1gs1.Link(node3gs3.Self())
  1218  	waitForTimeout(t, gs3.v)
  1219  
  1220  	if checkLinkPid(node1gs1, node3gs3.Self()) != nil {
  1221  		t.Fatal("link missing on node1gs1")
  1222  	}
  1223  	if checkLinkPid(node3gs3, node1gs1.Self()) != nil {
  1224  		t.Fatal("link missing on node3gs3")
  1225  	}
  1226  
  1227  	node3gs3.Exit("normal")
  1228  
  1229  	// wait a bit to make sure if we receive anything (shouldnt receive)
  1230  	waitForTimeout(t, gs1.v)
  1231  
  1232  	if err := checkCleanLinkPid(node1gs1, node3gs3.Self()); err != nil {
  1233  		t.Fatal(err)
  1234  	}
  1235  	if err := checkCleanLinkPid(node3gs3, node1gs1.Self()); err != nil {
  1236  		t.Fatal(err)
  1237  	}
  1238  	if node1gs1.IsAlive() {
  1239  		t.Fatal("gs1 shouldnt be alive after gs3 exit due to disable trap exit on gs1")
  1240  	}
  1241  	if node3gs3.IsAlive() {
  1242  		t.Fatal("gs3 must be terminated")
  1243  	}
  1244  	fmt.Println("OK")
  1245  
  1246  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
  1247  	node1gs1, _ = node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
  1248  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
  1249  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
  1250  	node3gs3, _ = node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
  1251  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
  1252  
  1253  	node1gs1.SetTrapExit(true)
  1254  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. node_down: ")
  1255  	node1gs1.Link(node3gs3.Self())
  1256  	waitForTimeout(t, gs1.v)
  1257  
  1258  	if checkLinkPid(node1gs1, node3gs3.Self()) != nil {
  1259  		t.Fatal("link missing for node1gs1")
  1260  	}
  1261  	if checkCleanLinkPid(node3gs3, node1gs1.Self()) == nil {
  1262  		t.Fatal("link missing for node3gs3")
  1263  	}
  1264  
  1265  	// race conditioned case.
  1266  	// processing of the process termination (on the remote peer) can be done faster than
  1267  	// the link termination there, so MessageExit with "kill" reason will be arrived
  1268  	// earlier.
  1269  	node3.Stop()
  1270  	result1 := gen.MessageExit{Pid: node3gs3.Self(), Reason: "noconnection"}
  1271  	result2 := gen.MessageExit{Pid: node3gs3.Self(), Reason: "kill"}
  1272  
  1273  	waitForResultWithValueOrValue(t, gs1.v, result1, result2)
  1274  
  1275  	if err := checkCleanLinkPid(node1gs1, node3gs3.Self()); err != nil {
  1276  		t.Fatal(err)
  1277  	}
  1278  	// must wait a bit
  1279  	waitForTimeout(t, gs1.v)
  1280  	if err := checkCleanLinkPid(node3gs3, node1gs1.Self()); err != nil {
  1281  		t.Fatal(err)
  1282  	}
  1283  
  1284  	ll1 = len(node1gs1.Links())
  1285  	fmt.Printf("Testing Link process Local-Proxy-Remote: gs1 -> gs3. node_unknown: ")
  1286  	node1gs1.Link(node3gs3.Self())
  1287  	result = gen.MessageExit{Pid: node3gs3.Self(), Reason: "noconnection"}
  1288  	waitForResultWithValue(t, gs1.v, result)
  1289  
  1290  	if ll1 != len(node1gs1.Links()) {
  1291  		t.Fatal("number of links has changed on the second Link call")
  1292  	}
  1293  
  1294  	node3opts = node.Options{}
  1295  	node3opts.Proxy.Accept = true
  1296  	node3, err = ergo.StartNode("nodeL3RemoteViaProxy@localhost", "cookies", node3opts)
  1297  	fmt.Printf("    starting node: %s", node3.Name())
  1298  	if err != nil {
  1299  		t.Fatal(err)
  1300  	}
  1301  	fmt.Println("OK")
  1302  	fmt.Printf("    wait for start of gs3 on %#v: ", node3.Name())
  1303  	node3gs3, _ = node3.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
  1304  	waitForResultWithValue(t, gs3.v, node3gs3.Self())
  1305  
  1306  	if err := node1.Connect(node3.Name()); err != nil {
  1307  		t.Fatal(err)
  1308  	}
  1309  
  1310  	node3gs3.SetTrapExit(true)
  1311  	fmt.Printf("Testing Proxy Local-Proxy-Remote for link gs3 -> gs1 (Node1 ProxyFlags.EnableLink = false): ")
  1312  	node3gs3.Link(node1gs1.Self())
  1313  	result = gen.MessageExit{Pid: node1gs1.Self(), Reason: lib.ErrPeerUnsupported.Error()}
  1314  	waitForResultWithValue(t, gs3.v, result)
  1315  
  1316  	node1gs1.Link(node3gs3.Self())
  1317  	waitForTimeout(t, gs1.v)
  1318  
  1319  	if checkLinkPid(node1gs1, node3gs3.Self()) != nil {
  1320  		t.Fatal("link missing for node1gs1")
  1321  	}
  1322  	if checkCleanLinkPid(node3gs3, node1gs1.Self()) == nil {
  1323  		t.Fatal("link missing for node3gs3")
  1324  	}
  1325  	fmt.Println("Testing Proxy Down Local-Proxy-Remote for linked gs1 -> gs3 (trap_exit = true): ")
  1326  	node2.Stop()
  1327  
  1328  	fmt.Printf("    wait for MessageExit with reason 'noproxy' on gs1: ")
  1329  	result = gen.MessageExit{Pid: node3gs3.Self(), Reason: "noproxy"}
  1330  	waitForResultWithValue(t, gs1.v, result)
  1331  
  1332  	fmt.Printf("    wait for MessageExit with reason 'noproxy' on gs3: ")
  1333  	result = gen.MessageExit{Pid: node1gs1.Self(), Reason: "noproxy"}
  1334  	waitForResultWithValue(t, gs3.v, result)
  1335  
  1336  	node1.Stop()
  1337  }
  1338  
  1339  func TestMonitorNode(t *testing.T) {
  1340  	fmt.Printf("\n=== Test Monitor Node \n")
  1341  	fmt.Printf("... start nodes A, B, C, D: ")
  1342  	optsA := node.Options{}
  1343  	nodeA, e := ergo.StartNode("monitornodeAproxy@localhost", "secret", optsA)
  1344  	if e != nil {
  1345  		t.Fatal(e)
  1346  	}
  1347  	optsB := node.Options{}
  1348  	optsB.Proxy.Transit = true
  1349  	nodeB, e := ergo.StartNode("monitornodeBproxy@localhost", "secret", optsB)
  1350  	if e != nil {
  1351  		t.Fatal(e)
  1352  	}
  1353  	optsC := node.Options{}
  1354  	optsC.Proxy.Transit = true
  1355  	nodeC, e := ergo.StartNode("monitornodeCproxy@localhost", "secret", optsC)
  1356  	if e != nil {
  1357  		t.Fatal(e)
  1358  	}
  1359  
  1360  	optsD := node.Options{}
  1361  	optsD.Proxy.Accept = true
  1362  	nodeD, e := ergo.StartNode("monitornodeDproxy@localhost", "secret", optsD)
  1363  	if e != nil {
  1364  		t.Fatal(e)
  1365  	}
  1366  	fmt.Println("OK")
  1367  
  1368  	gsA := &testMonitor{
  1369  		v: make(chan interface{}, 2),
  1370  	}
  1371  	gsB := &testMonitor{
  1372  		v: make(chan interface{}, 2),
  1373  	}
  1374  	gsD := &testMonitor{
  1375  		v: make(chan interface{}, 2),
  1376  	}
  1377  	fmt.Printf("... start processA on node A: ")
  1378  	pA, err := nodeA.Spawn("", gen.ProcessOptions{}, gsA)
  1379  	if err != nil {
  1380  		t.Fatal(err)
  1381  	}
  1382  	waitForResultWithValue(t, gsA.v, pA.Self())
  1383  	fmt.Printf("... start processB on node B: ")
  1384  	pB, err := nodeB.Spawn("", gen.ProcessOptions{}, gsB)
  1385  	if err != nil {
  1386  		t.Fatal(err)
  1387  	}
  1388  	waitForResultWithValue(t, gsB.v, pB.Self())
  1389  	fmt.Printf("... start processD on node D: ")
  1390  	pD, err := nodeD.Spawn("", gen.ProcessOptions{}, gsD)
  1391  	if err != nil {
  1392  		t.Fatal(err)
  1393  	}
  1394  	waitForResultWithValue(t, gsD.v, pD.Self())
  1395  	fmt.Printf("... add proxy route on A to the node D via B: ")
  1396  	routeAtoDviaB := node.ProxyRoute{
  1397  		Name:  nodeD.Name(),
  1398  		Proxy: nodeB.Name(),
  1399  	}
  1400  	if err := nodeA.AddProxyRoute(routeAtoDviaB); err != nil {
  1401  		t.Fatal(err)
  1402  	}
  1403  	fmt.Println("OK")
  1404  
  1405  	fmt.Printf("... add proxy transit route on B to the node D via C: ")
  1406  	route := node.ProxyRoute{
  1407  		Name:  nodeD.Name(),
  1408  		Proxy: nodeC.Name(),
  1409  	}
  1410  	if err := nodeB.AddProxyRoute(route); err != nil {
  1411  		t.Fatal(err)
  1412  	}
  1413  	fmt.Println("OK")
  1414  
  1415  	fmt.Printf("... monitor D by processA (via proxy connection): ")
  1416  	refA := pA.MonitorNode(nodeD.Name())
  1417  	fmt.Println("OK")
  1418  	fmt.Printf("... monitor A by processD (via proxy connection): ")
  1419  	refD := pD.MonitorNode(nodeA.Name())
  1420  	fmt.Println("OK")
  1421  	fmt.Printf("... monitor C by processB (via direct connection): ")
  1422  	refB := pB.MonitorNode(nodeC.Name())
  1423  	fmt.Println("OK")
  1424  	fmt.Printf("... check connectivity (A -> D via B and C, D -> A via C and B): ")
  1425  	nodelist := []string{nodeB.Name(), nodeD.Name()}
  1426  	nodesA := nodeA.Nodes()
  1427  	sort.Strings(nodesA)
  1428  	if reflect.DeepEqual(nodesA, nodelist) == false {
  1429  		t.Fatal("node A has wrong peers", nodeA.Nodes())
  1430  	}
  1431  	if reflect.DeepEqual(nodeA.NodesIndirect(), []string{nodeD.Name()}) == false {
  1432  		t.Fatal("node A has wrong proxy peer", nodeA.NodesIndirect())
  1433  	}
  1434  	nodelist = []string{nodeA.Name(), nodeC.Name()}
  1435  	nodesB := nodeB.Nodes()
  1436  	sort.Strings(nodesB)
  1437  	if reflect.DeepEqual(nodesB, nodelist) == false {
  1438  		t.Fatal("node B has wrong peers", nodeB.Nodes())
  1439  	}
  1440  	if reflect.DeepEqual(nodeB.NodesIndirect(), []string{}) == false {
  1441  		t.Fatal("node B has wrong proxy peer", nodeB.NodesIndirect())
  1442  	}
  1443  	nodelist = []string{nodeB.Name(), nodeD.Name()}
  1444  	nodesC := nodeC.Nodes()
  1445  	sort.Strings(nodesC)
  1446  	if reflect.DeepEqual(nodesC, nodelist) == false {
  1447  		t.Fatal("node C has wrong peers", nodeC.Nodes())
  1448  	}
  1449  	if reflect.DeepEqual(nodeC.NodesIndirect(), []string{}) == false {
  1450  		t.Fatal("node C has wrong proxy peer", nodeC.NodesIndirect())
  1451  	}
  1452  	nodelist = []string{nodeA.Name(), nodeC.Name()}
  1453  	nodesD := nodeD.Nodes()
  1454  	sort.Strings(nodesD)
  1455  	if reflect.DeepEqual(nodesD, nodelist) == false {
  1456  		t.Fatal("node D has wrong peers", nodeD.Nodes())
  1457  	}
  1458  	if reflect.DeepEqual(nodeD.NodesIndirect(), []string{nodeA.Name()}) == false {
  1459  		t.Fatal("node D has wrong proxy peer", nodeD.NodesIndirect())
  1460  	}
  1461  	fmt.Println("OK")
  1462  	fmt.Printf("... stop node C : ")
  1463  	nodeC.Stop()
  1464  	fmt.Println("OK")
  1465  	resultMessageProxyDown := gen.MessageProxyDown{Ref: refD, Node: nodeC.Name(), Proxy: nodeD.Name(), Reason: "noconnection"}
  1466  	fmt.Printf("... processD must receive gen.MessageProxyDown{Node: C, Proxy: D,...}: ")
  1467  	waitForResultWithValue(t, gsD.v, resultMessageProxyDown)
  1468  	resultMessageProxyDown = gen.MessageProxyDown{Ref: refA, Node: nodeC.Name(), Proxy: nodeB.Name(), Reason: "noconnection"}
  1469  	fmt.Printf("... processA must receive gen.MessageProxyDown{Node: C, Proxy: B,...}: ")
  1470  	waitForResultWithValue(t, gsA.v, resultMessageProxyDown)
  1471  	resultMessageDown := gen.MessageNodeDown{Ref: refB, Name: nodeC.Name()}
  1472  	fmt.Printf("... processB must receive gen.MessageDown: ")
  1473  	waitForResultWithValue(t, gsB.v, resultMessageDown)
  1474  
  1475  	fmt.Printf("... check connectivity (A <-> B, C is down, D has no peers): ")
  1476  	if reflect.DeepEqual(nodeA.Nodes(), []string{nodeB.Name()}) == false {
  1477  		t.Fatal("node A has wrong peer", nodeA.Nodes())
  1478  	}
  1479  	if reflect.DeepEqual(nodeB.Nodes(), []string{nodeA.Name()}) == false {
  1480  		t.Fatal("node B has wrong peer", nodeB.Nodes())
  1481  	}
  1482  	if nodeC.IsAlive() == true {
  1483  		t.Fatal("node C is still alive")
  1484  	}
  1485  	if reflect.DeepEqual(nodeC.Nodes(), []string{}) == false {
  1486  		t.Fatal("node C has peers", nodeC.Nodes())
  1487  	}
  1488  	if reflect.DeepEqual(nodeD.Nodes(), []string{}) == false {
  1489  		t.Fatal("node D has peers", nodeD.Nodes())
  1490  	}
  1491  	fmt.Println("OK")
  1492  	nodeD.Stop()
  1493  	nodeA.Stop()
  1494  	nodeB.Stop()
  1495  }
  1496  
  1497  type testMonitorEvent struct {
  1498  	gen.Server
  1499  	v chan interface{}
  1500  }
  1501  
  1502  type testEventCmdRegister struct {
  1503  	event    gen.Event
  1504  	messages []gen.EventMessage
  1505  }
  1506  type testEventCmdUnregister struct {
  1507  	event gen.Event
  1508  }
  1509  type testEventCmdMonitor struct {
  1510  	event gen.Event
  1511  }
  1512  type testEventCmdSend struct {
  1513  	event   gen.Event
  1514  	message gen.EventMessage
  1515  }
  1516  
  1517  type testMessageEventA struct {
  1518  	value string
  1519  }
  1520  
  1521  func (tgs *testMonitorEvent) Init(process *gen.ServerProcess, args ...etf.Term) error {
  1522  	tgs.v <- process.Self()
  1523  	return nil
  1524  }
  1525  func (tgs *testMonitorEvent) HandleDirect(process *gen.ServerProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
  1526  	switch cmd := message.(type) {
  1527  	case testEventCmdRegister:
  1528  		return nil, process.RegisterEvent(cmd.event, cmd.messages...)
  1529  	case testEventCmdUnregister:
  1530  		return nil, process.UnregisterEvent(cmd.event)
  1531  	case testEventCmdMonitor:
  1532  		return nil, process.MonitorEvent(cmd.event)
  1533  	case testEventCmdSend:
  1534  		return nil, process.SendEventMessage(cmd.event, cmd.message)
  1535  
  1536  	default:
  1537  		return nil, fmt.Errorf("unknown cmd")
  1538  
  1539  	}
  1540  }
  1541  
  1542  func (tgs *testMonitorEvent) HandleInfo(process *gen.ServerProcess, message etf.Term) gen.ServerStatus {
  1543  	tgs.v <- message
  1544  	return gen.ServerStatusOK
  1545  }
  1546  
  1547  func TestMonitorEvents(t *testing.T) {
  1548  	fmt.Printf("\n=== Test Monitor Events\n")
  1549  	fmt.Printf("Starting node: nodeM1Events@localhost: ")
  1550  	node1, _ := ergo.StartNode("nodeM1Events@localhost", "cookies", node.Options{})
  1551  	if node1 == nil {
  1552  		t.Fatal("can't start node")
  1553  	}
  1554  	defer node1.Stop()
  1555  
  1556  	fmt.Println("OK")
  1557  	gs1 := &testMonitorEvent{
  1558  		v: make(chan interface{}, 2),
  1559  	}
  1560  	gs2 := &testMonitorEvent{
  1561  		v: make(chan interface{}, 2),
  1562  	}
  1563  	gs3 := &testMonitorEvent{
  1564  		v: make(chan interface{}, 2),
  1565  	}
  1566  	// starting gen servers
  1567  
  1568  	fmt.Printf("    wait for start of gs1 on %#v: ", node1.Name())
  1569  	node1gs1, err := node1.Spawn("gs1", gen.ProcessOptions{}, gs1, nil)
  1570  	if err != nil {
  1571  		t.Fatal(err)
  1572  	}
  1573  	waitForResultWithValue(t, gs1.v, node1gs1.Self())
  1574  
  1575  	fmt.Printf("    wait for start of gs2 on %#v: ", node1.Name())
  1576  	node1gs2, err := node1.Spawn("gs2", gen.ProcessOptions{}, gs2, nil)
  1577  	if err != nil {
  1578  		t.Fatal(err)
  1579  	}
  1580  	waitForResultWithValue(t, gs2.v, node1gs2.Self())
  1581  
  1582  	fmt.Printf("    wait for start of gs3 on %#v: ", node1.Name())
  1583  	node1gs3, err := node1.Spawn("gs3", gen.ProcessOptions{}, gs3, nil)
  1584  	if err != nil {
  1585  		t.Fatal(err)
  1586  	}
  1587  	waitForResultWithValue(t, gs3.v, node1gs3.Self())
  1588  
  1589  	fmt.Printf("... register new event : ")
  1590  	cmd := testEventCmdRegister{
  1591  		event:    "testEvent",
  1592  		messages: []gen.EventMessage{testMessageEventA{}},
  1593  	}
  1594  	_, err = node1gs1.Direct(cmd)
  1595  	if err != nil {
  1596  		t.Fatal(err)
  1597  	}
  1598  	fmt.Println("OK")
  1599  
  1600  	fmt.Printf("... register new event with the same name: ")
  1601  	_, err = node1gs1.Direct(cmd)
  1602  	if err != lib.ErrTaken {
  1603  		t.Fatal(err)
  1604  	}
  1605  	fmt.Println("OK")
  1606  
  1607  	fmt.Printf("... unregister unknown event: ")
  1608  	cmd1 := testEventCmdUnregister{
  1609  		event: "unknownEvent",
  1610  	}
  1611  	_, err = node1gs1.Direct(cmd1)
  1612  	if err != lib.ErrEventUnknown {
  1613  		t.Fatal(err)
  1614  	}
  1615  	fmt.Println("OK")
  1616  
  1617  	fmt.Printf("... unregister event by not an owner: ")
  1618  	cmd1 = testEventCmdUnregister{
  1619  		event: "testEvent",
  1620  	}
  1621  	_, err = node1gs2.Direct(cmd1)
  1622  	if err != lib.ErrEventOwner {
  1623  		t.Fatal(err)
  1624  	}
  1625  	fmt.Println("OK")
  1626  
  1627  	fmt.Printf("... unregister event by the owner: ")
  1628  	cmd1 = testEventCmdUnregister{
  1629  		event: "testEvent",
  1630  	}
  1631  	_, err = node1gs1.Direct(cmd1)
  1632  	if err != nil {
  1633  		t.Fatal(err)
  1634  	}
  1635  	fmt.Println("OK")
  1636  
  1637  	fmt.Printf("... monitor unknown event: ")
  1638  	cmd2 := testEventCmdMonitor{
  1639  		event: "testEvent",
  1640  	}
  1641  	_, err = node1gs2.Direct(cmd2)
  1642  	if err != lib.ErrEventUnknown {
  1643  		t.Fatal(err)
  1644  	}
  1645  	fmt.Println("OK")
  1646  
  1647  	fmt.Printf("... monitor event: ")
  1648  	cmd = testEventCmdRegister{
  1649  		event:    "testEvent",
  1650  		messages: []gen.EventMessage{testMessageEventA{}},
  1651  	}
  1652  	_, err = node1gs1.Direct(cmd)
  1653  	if err != nil {
  1654  		t.Fatal(err)
  1655  	}
  1656  
  1657  	cmd2 = testEventCmdMonitor{
  1658  		event: "testEvent",
  1659  	}
  1660  	_, err = node1gs2.Direct(cmd2)
  1661  	if err != nil {
  1662  		t.Fatal(err)
  1663  	}
  1664  	fmt.Println("OK")
  1665  
  1666  	fmt.Printf("... monitor event itself: ")
  1667  	cmd2 = testEventCmdMonitor{
  1668  		event: "testEvent",
  1669  	}
  1670  	_, err = node1gs1.Direct(cmd2)
  1671  	if err != lib.ErrEventSelf {
  1672  		t.Fatal(err)
  1673  	}
  1674  	fmt.Println("OK")
  1675  
  1676  	fmt.Printf("... send unknown event: ")
  1677  	msg := testMessageEventA{value: "test"}
  1678  	cmd3 := testEventCmdSend{
  1679  		event:   "unknownEvent",
  1680  		message: msg,
  1681  	}
  1682  	_, err = node1gs1.Direct(cmd3)
  1683  	if err != lib.ErrEventUnknown {
  1684  		t.Fatal(err)
  1685  	}
  1686  	fmt.Println("OK")
  1687  
  1688  	fmt.Printf("... send event with wrong message type: ")
  1689  	msgWrong := "wrong type"
  1690  	cmd3 = testEventCmdSend{
  1691  		event:   "testEvent",
  1692  		message: msgWrong,
  1693  	}
  1694  	_, err = node1gs1.Direct(cmd3)
  1695  	if err != lib.ErrEventMismatch {
  1696  		t.Fatal(err)
  1697  	}
  1698  	fmt.Println("OK")
  1699  
  1700  	fmt.Printf("... send event by not an owner: ")
  1701  	cmd3 = testEventCmdSend{
  1702  		event:   "testEvent",
  1703  		message: msg,
  1704  	}
  1705  	_, err = node1gs2.Direct(cmd3)
  1706  	if err != lib.ErrEventOwner {
  1707  		t.Fatal(err)
  1708  	}
  1709  	fmt.Println("OK")
  1710  
  1711  	fmt.Printf("... send event: ")
  1712  	cmd3 = testEventCmdSend{
  1713  		event:   "testEvent",
  1714  		message: msg,
  1715  	}
  1716  	_, err = node1gs1.Direct(cmd3)
  1717  	if err != nil {
  1718  		t.Fatal(err)
  1719  	}
  1720  	waitForResultWithValue(t, gs2.v, msg)
  1721  
  1722  	fmt.Printf("... monitor event twice: ")
  1723  	cmd2 = testEventCmdMonitor{
  1724  		event: "testEvent",
  1725  	}
  1726  	_, err = node1gs2.Direct(cmd2)
  1727  	if err != nil {
  1728  		t.Fatal(err)
  1729  	}
  1730  	fmt.Println("OK")
  1731  
  1732  	fmt.Printf("... send event. must be received twice: ")
  1733  	cmd3 = testEventCmdSend{
  1734  		event:   "testEvent",
  1735  		message: msg,
  1736  	}
  1737  	_, err = node1gs1.Direct(cmd3)
  1738  	if err != nil {
  1739  		t.Fatal(err)
  1740  	}
  1741  	fmt.Println("OK")
  1742  	fmt.Printf("... receive first event message: ")
  1743  	waitForResultWithValue(t, gs2.v, msg)
  1744  	fmt.Printf("... receive second event message: ")
  1745  	waitForResultWithValue(t, gs2.v, msg)
  1746  
  1747  	down := gen.MessageEventDown{
  1748  		Event:  "testEvent",
  1749  		Reason: "unregistered",
  1750  	}
  1751  	fmt.Printf("... unregister event owner. must be received gen.MessageEventDown twice with reason 'unregistered': ")
  1752  	cmd1 = testEventCmdUnregister{
  1753  		event: "testEvent",
  1754  	}
  1755  	_, err = node1gs1.Direct(cmd1)
  1756  	if err != nil {
  1757  		t.Fatal(err)
  1758  	}
  1759  	fmt.Println("OK")
  1760  	fmt.Printf("... receive first event down message: ")
  1761  	waitForResultWithValue(t, gs2.v, down)
  1762  	fmt.Printf("... receive second event down message: ")
  1763  	waitForResultWithValue(t, gs2.v, down)
  1764  
  1765  	cmd = testEventCmdRegister{
  1766  		event:    "testEvent",
  1767  		messages: []gen.EventMessage{testMessageEventA{}},
  1768  	}
  1769  	_, err = node1gs3.Direct(cmd)
  1770  	if err != nil {
  1771  		t.Fatal(err)
  1772  	}
  1773  
  1774  	cmd2 = testEventCmdMonitor{
  1775  		event: "testEvent",
  1776  	}
  1777  	_, err = node1gs2.Direct(cmd2)
  1778  	if err != nil {
  1779  		t.Fatal(err)
  1780  	}
  1781  	fmt.Printf("... terminate event owner. must be received gen.MessageEventDown with reason 'kill': ")
  1782  	node1gs3.Kill()
  1783  	down = gen.MessageEventDown{
  1784  		Event:  "testEvent",
  1785  		Reason: "kill",
  1786  	}
  1787  	waitForResultWithValue(t, gs2.v, down)
  1788  }
  1789  
  1790  // helpers
  1791  func checkCleanProcessRef(p gen.Process, ref etf.Ref) error {
  1792  	if p.IsMonitor(ref) {
  1793  		return fmt.Errorf("monitor process reference hasn't been cleaned correctly")
  1794  	}
  1795  
  1796  	return nil
  1797  }
  1798  
  1799  func checkCleanLinkPid(p gen.Process, pid etf.Pid) error {
  1800  	for _, l := range p.Links() {
  1801  		if l == pid {
  1802  			return fmt.Errorf("process link reference hasn't been cleaned correctly")
  1803  		}
  1804  	}
  1805  	return nil
  1806  }
  1807  func checkLinkPid(p gen.Process, pid etf.Pid) error {
  1808  	for _, l := range p.Links() {
  1809  		if l == pid {
  1810  			return nil
  1811  		}
  1812  	}
  1813  	return fmt.Errorf("process %s has no link to %s", p.Self(), pid)
  1814  }