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

     1  package tests
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/ergo-services/ergo"
     9  	"github.com/ergo-services/ergo/etf"
    10  	"github.com/ergo-services/ergo/gen"
    11  	"github.com/ergo-services/ergo/lib"
    12  	"github.com/ergo-services/ergo/node"
    13  )
    14  
    15  const (
    16  	defaultDispatcherBufferSize = 10000
    17  	defaultAutoDemandCount      = 3
    18  )
    19  
    20  type StageProducerTest struct {
    21  	gen.Stage
    22  	value      chan interface{}
    23  	dispatcher gen.StageDispatcherBehavior
    24  }
    25  
    26  type sendEvents struct {
    27  	events etf.List
    28  }
    29  type cancelSubscription struct {
    30  	subscription gen.StageSubscription
    31  	reason       string
    32  }
    33  
    34  type demandRequest struct {
    35  	subscription gen.StageSubscription
    36  	count        uint
    37  }
    38  type newSubscription struct {
    39  	producer etf.Term
    40  	opts     gen.StageSubscribeOptions
    41  }
    42  type demandHandle struct {
    43  	enable bool
    44  }
    45  type autoDemand struct {
    46  	subscription gen.StageSubscription
    47  	enable       bool
    48  }
    49  
    50  //
    51  // a simple Stage Producer
    52  //
    53  
    54  func (gs *StageProducerTest) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) {
    55  	opts := gen.StageOptions{
    56  		Dispatcher: gs.dispatcher,
    57  	}
    58  	return opts, nil
    59  }
    60  func (gs *StageProducerTest) HandleDemand(process *gen.StageProcess, subscription gen.StageSubscription, count uint) (etf.List, gen.StageStatus) {
    61  	gs.value <- etf.Tuple{subscription, count}
    62  	return nil, gen.StageStatusOK
    63  }
    64  
    65  func (gs *StageProducerTest) HandleSubscribe(process *gen.StageProcess, subscription gen.StageSubscription, options gen.StageSubscribeOptions) gen.StageStatus {
    66  	gs.value <- subscription
    67  	return gen.StageStatusOK
    68  }
    69  func (gs *StageProducerTest) HandleCancel(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus {
    70  	gs.value <- etf.Tuple{"cancel", subscription}
    71  	return gen.StageStatusOK
    72  }
    73  
    74  // add this callback only for the 'not a producer' case
    75  func (gs *StageProducerTest) HandleCanceled(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus {
    76  	gs.value <- etf.Tuple{"canceled", subscription, reason}
    77  	return gen.StageStatusOK
    78  }
    79  
    80  func (s *StageProducerTest) SendEvents(p gen.Process, events etf.List) error {
    81  	message := sendEvents{
    82  		events: events,
    83  	}
    84  	_, err := p.Direct(message)
    85  	return err
    86  }
    87  
    88  func (s *StageProducerTest) Cancel(p gen.Process, subscription gen.StageSubscription, reason string) error {
    89  	message := cancelSubscription{
    90  		subscription: subscription,
    91  		reason:       reason,
    92  	}
    93  	_, err := p.Direct(message)
    94  	return err
    95  }
    96  func (gs *StageProducerTest) Subscribe(p gen.Process, producer etf.Term, opts gen.StageSubscribeOptions) (gen.StageSubscription, error) {
    97  	message := newSubscription{
    98  		producer: producer,
    99  		opts:     opts,
   100  	}
   101  	s, err := p.Direct(message)
   102  	if err != nil {
   103  		return gen.StageSubscription{}, err
   104  	}
   105  	return s.(gen.StageSubscription), nil
   106  }
   107  
   108  func (gs *StageProducerTest) SetDemandHandle(p gen.Process, enable bool) error {
   109  	message := demandHandle{
   110  		enable: enable,
   111  	}
   112  	_, err := p.Direct(message)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	return nil
   117  }
   118  func (gs *StageProducerTest) SetAutoDemand(p gen.Process, subscription gen.StageSubscription, enable bool) error {
   119  	message := autoDemand{
   120  		subscription: subscription,
   121  		enable:       enable,
   122  	}
   123  	_, err := p.Direct(message)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	return nil
   128  }
   129  
   130  func (s *StageProducerTest) HandleStageDirect(process *gen.StageProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
   131  	switch m := message.(type) {
   132  	case demandHandle:
   133  		process.SetDemandHandle(m.enable)
   134  		return nil, nil
   135  	case newSubscription:
   136  		return process.Subscribe(m.producer, m.opts)
   137  	case sendEvents:
   138  		process.SendEvents(m.events)
   139  		return nil, nil
   140  
   141  	case cancelSubscription:
   142  		err := process.Cancel(m.subscription, m.reason)
   143  		return nil, err
   144  	case makeCall:
   145  		return process.Call(m.to, m.message)
   146  	case makeCast:
   147  		return nil, process.Cast(m.to, m.message)
   148  
   149  	default:
   150  		return nil, lib.ErrUnsupportedRequest
   151  	}
   152  }
   153  
   154  //
   155  // a simple Stage Consumer
   156  //
   157  type StageConsumerTest struct {
   158  	gen.Stage
   159  	value chan interface{}
   160  }
   161  
   162  func (gs *StageConsumerTest) InitStage(process *gen.StageProcess, args ...etf.Term) (gen.StageOptions, error) {
   163  	return gen.StageOptions{}, nil
   164  }
   165  func (gs *StageConsumerTest) HandleEvents(process *gen.StageProcess, subscription gen.StageSubscription, events etf.List) gen.StageStatus {
   166  	gs.value <- etf.Tuple{"events", subscription, events}
   167  	return gen.StageStatusOK
   168  }
   169  func (gs *StageConsumerTest) HandleSubscribed(process *gen.StageProcess, subscription gen.StageSubscription, opts gen.StageSubscribeOptions) (bool, gen.StageStatus) {
   170  	gs.value <- subscription
   171  	return opts.ManualDemand, gen.StageStatusOK
   172  }
   173  func (gs *StageConsumerTest) HandleCanceled(process *gen.StageProcess, subscription gen.StageSubscription, reason string) gen.StageStatus {
   174  	gs.value <- etf.Tuple{"canceled", subscription, reason}
   175  	return gen.StageStatusOK
   176  }
   177  func (gs *StageConsumerTest) HandleStageCall(process *gen.StageProcess, from gen.ServerFrom, message etf.Term) (etf.Term, gen.ServerStatus) {
   178  	gs.value <- message
   179  	return "ok", gen.ServerStatusOK
   180  }
   181  func (gs *StageConsumerTest) HandleStageCast(process *gen.StageProcess, message etf.Term) gen.ServerStatus {
   182  	gs.value <- message
   183  	return gen.ServerStatusOK
   184  }
   185  func (gs *StageConsumerTest) HandleStageInfo(process *gen.StageProcess, message etf.Term) gen.ServerStatus {
   186  	gs.value <- message
   187  	return gen.ServerStatusOK
   188  }
   189  
   190  func (s *StageConsumerTest) HandleStageDirect(p *gen.StageProcess, ref etf.Ref, message interface{}) (interface{}, gen.DirectStatus) {
   191  	switch m := message.(type) {
   192  	case newSubscription:
   193  		return p.Subscribe(m.producer, m.opts)
   194  	case demandRequest:
   195  		err := p.Ask(m.subscription, m.count)
   196  		return nil, err
   197  	case autoDemand:
   198  		err := p.SetAutoDemand(m.subscription, m.enable)
   199  		return nil, err
   200  
   201  	case cancelSubscription:
   202  		err := p.Cancel(m.subscription, m.reason)
   203  		return nil, err
   204  	case makeCall:
   205  		return p.Call(m.to, m.message)
   206  	case makeCast:
   207  		return nil, p.Cast(m.to, m.message)
   208  	}
   209  	return nil, lib.ErrUnsupportedRequest
   210  }
   211  
   212  func (s *StageConsumerTest) HandleStageTerminate(p *gen.StageProcess, reason string) {
   213  	//fmt.Println("StageConsumerTest process terminated with reason", reason)
   214  }
   215  
   216  func (gs *StageConsumerTest) SetAutoDemand(p gen.Process, subscription gen.StageSubscription, enable bool) error {
   217  	message := autoDemand{
   218  		subscription: subscription,
   219  		enable:       enable,
   220  	}
   221  	_, err := p.Direct(message)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	return nil
   226  }
   227  
   228  func (gs *StageConsumerTest) Subscribe(p gen.Process, producer etf.Term, opts gen.StageSubscribeOptions) (gen.StageSubscription, error) {
   229  	message := newSubscription{
   230  		producer: producer,
   231  		opts:     opts,
   232  	}
   233  	s, err := p.Direct(message)
   234  	if err != nil {
   235  		return gen.StageSubscription{}, err
   236  	}
   237  	return s.(gen.StageSubscription), nil
   238  }
   239  
   240  func (s *StageConsumerTest) Cancel(p gen.Process, subscription gen.StageSubscription, reason string) error {
   241  	message := cancelSubscription{
   242  		subscription: subscription,
   243  		reason:       reason,
   244  	}
   245  	_, err := p.Direct(message)
   246  	return err
   247  }
   248  
   249  func (s *StageConsumerTest) Ask(p gen.Process, subscription gen.StageSubscription, count uint) error {
   250  	message := demandRequest{
   251  		subscription: subscription,
   252  		count:        count,
   253  	}
   254  	_, err := p.Direct(message)
   255  	return err
   256  }
   257  
   258  func TestStageSimple(t *testing.T) {
   259  
   260  	fmt.Printf("\n=== Test StageSimple\n")
   261  	fmt.Printf("Starting node: nodeStageSimple01@localhost...")
   262  
   263  	node, _ := ergo.StartNode("nodeStageSimple01@localhost", "cookies", node.Options{})
   264  
   265  	if node == nil {
   266  		t.Fatal("can't start node")
   267  		return
   268  	}
   269  	fmt.Println("OK")
   270  
   271  	producer := &StageProducerTest{
   272  		value: make(chan interface{}, 2),
   273  	}
   274  	consumer := &StageConsumerTest{
   275  		value: make(chan interface{}, 2),
   276  	}
   277  	//	producerProcess, _ :=
   278  	fmt.Printf("... starting Producer and Consumer processes: ")
   279  	producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   280  	if errP != nil {
   281  		t.Fatal(errP)
   282  	}
   283  	consumerProcess, errC := node.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil)
   284  	if errC != nil {
   285  		t.Fatal(errC)
   286  	}
   287  	fmt.Println("OK")
   288  
   289  	subOpts := gen.StageSubscribeOptions{
   290  		MinDemand:    4,
   291  		MaxDemand:    5,
   292  		ManualDemand: true,
   293  		// use Temporary to keep this process running
   294  		Cancel: gen.StageCancelTemporary,
   295  	}
   296  
   297  	// case 1: subscribe
   298  	fmt.Println("Subscribing/resubscribing/cancelation:")
   299  	sub, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts)
   300  	fmt.Printf("... Producer handled subscription request from Consumer: ")
   301  	waitForResultWithValue(t, producer.value, sub)
   302  	fmt.Printf("... Consumer handled subscription confirmation from Producer: ")
   303  	waitForResultWithValue(t, consumer.value, sub)
   304  	// case 2: subscribe one more time (prev subscription should be canceled automatically)
   305  	fmt.Printf("... Consumer subscribes to Producer without cancelation previous subscription: ")
   306  	sub1, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts)
   307  	fmt.Println("OK")
   308  	// previous subscription should be canceled
   309  	fmt.Printf("... Producer canceled previous subscription and handled the new subscription request from Consumer: ")
   310  	waitForResultWithValue(t, producer.value, sub1)
   311  	fmt.Printf("... Consumer handled cancelation and subscription confirmation from Producer: ")
   312  	waitForResultWithMultiValue(t, consumer.value, etf.List{sub1, etf.Tuple{"canceled", sub, "resubscribed"}})
   313  
   314  	fmt.Printf("... Consumer invokes Cancel subscription explicitly:")
   315  	if err := consumer.Cancel(consumerProcess, sub1, "normal"); err != nil {
   316  		t.Fatal(err)
   317  	}
   318  	fmt.Println("OK")
   319  	fmt.Printf("... Producer handled cancelation request from Consumer: ")
   320  	waitForResultWithValue(t, producer.value, etf.Tuple{"cancel", sub1})
   321  	fmt.Printf("... Consumer handled cancelation confirmation from Producer: ")
   322  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub1, "normal"})
   323  
   324  	fmt.Println("make another subscription for the testing of explicit cancelation from the Producer side")
   325  	sub2, _ := consumer.Subscribe(consumerProcess, "stageProducer", subOpts)
   326  	fmt.Printf("... Producer handled subscription request from Consumer: ")
   327  	waitForResultWithValue(t, producer.value, sub2)
   328  	fmt.Printf("... Consumer handled subscription confirmation from Producer: ")
   329  	waitForResultWithValue(t, consumer.value, sub2)
   330  	if err := producer.Cancel(producerProcess, sub2, "normal"); err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	fmt.Printf("... Producer handled cancelation request from itself: ")
   334  	waitForResultWithValue(t, producer.value, etf.Tuple{"cancel", sub2})
   335  	fmt.Printf("... Consumer handled cancelation confirmation from Producer: ")
   336  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub2, "normal"})
   337  	waitForTimeout(t, producer.value)
   338  	waitForTimeout(t, consumer.value)
   339  
   340  	// case 3:
   341  	fmt.Printf("... trying to subscribe on Consumer (should fail with error 'not a producer'): ")
   342  	// let's subscribe using Pid instead of registered name "stageConsumer"
   343  	sub3, _ := producer.Subscribe(producerProcess, consumerProcess.Self(), subOpts)
   344  	waitForResultWithValue(t, producer.value, etf.Tuple{"canceled", sub3, "not a producer"})
   345  
   346  	// case 4: invoking Server callbacks
   347  	fmt.Printf("... Invoking Server's callback handlers HandleStageCall: ")
   348  	call := makeCall{
   349  		to:      "stageConsumer",
   350  		message: "test call",
   351  	}
   352  	if _, err := producerProcess.Direct(call); err != nil {
   353  		t.Fatal(err)
   354  	}
   355  	waitForResultWithValue(t, consumer.value, "test call")
   356  	fmt.Printf("... Invoking Server's callback handlers HandleStageCast: ")
   357  	cast := makeCast{
   358  		to:      "stageConsumer",
   359  		message: "test cast",
   360  	}
   361  	producerProcess.Direct(cast)
   362  	waitForResultWithValue(t, consumer.value, "test cast")
   363  	fmt.Printf("... Invoking Server's callback handlers HandleStageInfo: ")
   364  	producerProcess.Send("stageConsumer", "test info")
   365  	waitForResultWithValue(t, consumer.value, "test info")
   366  
   367  	// case 5:
   368  	//    - subscribe, ask and send events
   369  	//    - resubscribe with updated min/max, ask and send events
   370  	//    keep this subscription for the next case
   371  	subOpts.MinDemand = 2
   372  	subOpts.MaxDemand = 4
   373  	subOpts.ManualDemand = true
   374  	fmt.Println("make yet another subscription for the testing subscribe/ask/send_events")
   375  	sub4, _ := consumer.Subscribe(consumerProcess, producerProcess.Self(), subOpts)
   376  	fmt.Printf("... Producer handled subscription request from Consumer: ")
   377  	waitForResultWithValue(t, producer.value, sub4)
   378  	fmt.Printf("... Consumer handled subscription confirmation from Producer: ")
   379  	waitForResultWithValue(t, consumer.value, sub4)
   380  	fmt.Printf("... Consumer sent 'Ask' request with count = 1 (min 2, max 4) : ")
   381  	consumer.Ask(consumerProcess, sub4, 1)
   382  	waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(1)})
   383  	waitForTimeout(t, consumer.value) // shouldn't receive anything here
   384  	events := etf.List{
   385  		"a", "b", "c", "d", "e",
   386  	}
   387  	fmt.Printf("... Consumer sent 'Ask' request with count = 4 (min 2, max 4) : ")
   388  	consumer.Ask(consumerProcess, sub4, 4)
   389  	waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(4)})
   390  	fmt.Printf("... Producer sent 5 events. Consumer should receive 4: ")
   391  	producer.SendEvents(producerProcess, events)
   392  	expected := etf.Tuple{"events", sub4, events[0:4]}
   393  	waitForResultWithValue(t, consumer.value, expected)
   394  	events = etf.List{
   395  		"f",
   396  	}
   397  	fmt.Printf("... Producer sent 1 event. Consumer shouldn't receive anything until make yet another 'Ask' request: ")
   398  	producer.SendEvents(producerProcess, events)
   399  	waitForTimeout(t, consumer.value) // shouldn't receive anything here
   400  	fmt.Println("OK")
   401  	fmt.Printf("1... Consumer sent 'Ask' request with count = 1 (min 2, max 4) : ")
   402  	if err := consumer.Ask(consumerProcess, sub4, 1); err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	waitForResultWithValue(t, producer.value, etf.Tuple{sub4, uint(1)})
   406  	fmt.Printf("... Consumer should receive 2 events: ")
   407  	expected = etf.Tuple{"events", sub4, etf.List{"e", "f"}}
   408  	waitForResultWithValue(t, consumer.value, expected)
   409  
   410  	// case 6:
   411  	//    - ask, disable forwarding, send events, ask, enable forwarding
   412  	//    keep this subscription for the next case
   413  
   414  	fmt.Printf("... Disable handling demand on Producer: ")
   415  	if err := producer.SetDemandHandle(producerProcess, false); err != nil {
   416  		t.Fatal(err)
   417  	}
   418  	fmt.Println("OK")
   419  	fmt.Printf("... Consumer sent 'Ask' request with count = 1 (min 2, max 4). Stage shouldn't call HandleDemand : ")
   420  	consumer.Ask(consumerProcess, sub4, 1)
   421  	waitForTimeout(t, producer.value) // shouldn't receive anything here
   422  	fmt.Println("OK")
   423  
   424  	events = etf.List{
   425  		"a", "b", "c", "d", "e", "f", "g", "h", "1", "2",
   426  	}
   427  	producer.SendEvents(producerProcess, events)
   428  	fmt.Printf("... Producer sent 10 events. Dispatcher should keep them all since demand was buffered: ")
   429  	waitForTimeout(t, consumer.value) // shouldn't receive anything here
   430  	fmt.Println("OK")
   431  	fmt.Printf("... Consumer sent yet another 'Ask'. Stage shouldn't call HandleDemand as well: ")
   432  	consumer.Ask(consumerProcess, sub4, 9)
   433  	waitForTimeout(t, producer.value) // shouldn't receive anything here
   434  	fmt.Println("OK")
   435  
   436  	fmt.Printf("... Enable handling demand on Producer: ")
   437  	if err := producer.SetDemandHandle(producerProcess, true); err != nil {
   438  		t.Fatal(err)
   439  	}
   440  	fmt.Println("OK")
   441  	fmt.Printf("... Producer should receive demands count =1 and count =9: ")
   442  	expected1 := etf.Tuple{sub4, uint(1)}
   443  	expected2 := etf.Tuple{sub4, uint(9)}
   444  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   445  	fmt.Printf("... Customer should receive 3 messages with 4, 4 and 2 events: ")
   446  	expected1 = etf.Tuple{"events", sub4, etf.List{"a", "b", "c", "d"}}
   447  	expected2 = etf.Tuple{"events", sub4, etf.List{"e", "f", "g", "h"}}
   448  	expected3 := etf.Tuple{"events", sub4, etf.List{"1", "2"}}
   449  	waitForResultWithMultiValue(t, consumer.value, etf.List{expected1, expected2, expected3})
   450  
   451  	waitForTimeout(t, consumer.value) // shouldn't receive anything here
   452  	waitForTimeout(t, producer.value) // shouldn't receive anything here
   453  
   454  	// case 7:
   455  	//    - enable auto demand, send events, try to ask (should get error), disable auto demands
   456  	fmt.Printf("... Enable AutoDemand on the Producer (should fail): ")
   457  	if err := producer.SetAutoDemand(producerProcess, sub4, true); err == nil {
   458  		t.Fatal("should fail here")
   459  	}
   460  	fmt.Println("OK")
   461  	fmt.Printf("... Enable AutoDemand on the Consumer. Producer should receive demand with count %d: ", defaultAutoDemandCount)
   462  	if err := consumer.SetAutoDemand(consumerProcess, sub4, true); err != nil {
   463  		t.Fatal(err)
   464  	}
   465  	waitForResultWithValue(t, producer.value, etf.Tuple{sub4, subOpts.MaxDemand})
   466  	fmt.Printf("... Customer sent 'Ask' request (should fail): ")
   467  	if err := consumer.Ask(consumerProcess, sub4, 2); err == nil {
   468  		t.Fatal("should fail here")
   469  	}
   470  	fmt.Println("OK")
   471  	events = etf.List{
   472  		"a", "b", "c", "d", "e",
   473  	}
   474  	producer.SendEvents(producerProcess, events)
   475  	fmt.Printf("... Producer sent 5 events. Consumer should receive 4. Demands counter = 2 now: ")
   476  	expected = etf.Tuple{"events", sub4, etf.List{"a", "b", "c", "d"}}
   477  	waitForResultWithValue(t, consumer.value, expected)
   478  	fmt.Printf("... Consumer should send auto demand with count %d: ", subOpts.MaxDemand)
   479  	waitForResultWithValue(t, producer.value, etf.Tuple{sub4, subOpts.MaxDemand})
   480  
   481  	node.Stop()
   482  }
   483  
   484  func TestStageDistributed(t *testing.T) {
   485  	fmt.Printf("\n=== Test StageDistributed\n")
   486  	fmt.Printf("Starting node: nodeStageDistributed01@localhost...")
   487  	node1, _ := ergo.StartNode("nodeStageDistributed01@localhost", "cookies", node.Options{})
   488  	if node1 == nil {
   489  		t.Fatal("can't start node")
   490  		return
   491  	}
   492  	fmt.Println("OK")
   493  	fmt.Printf("Starting node: nodeStageDistributed02@localhost...")
   494  	node2, _ := ergo.StartNode("nodeStageDistributed02@localhost", "cookies", node.Options{})
   495  	if node2 == nil {
   496  		t.Fatal("can't start node")
   497  		return
   498  	}
   499  	fmt.Println("OK")
   500  
   501  	producer := &StageProducerTest{
   502  		value: make(chan interface{}, 2),
   503  	}
   504  	consumer := &StageConsumerTest{
   505  		value: make(chan interface{}, 2),
   506  	}
   507  	fmt.Printf("... starting Producer and Consumer processes: ")
   508  	producerProcess, errP := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   509  	if errP != nil {
   510  		t.Fatal(errP)
   511  	}
   512  	consumerProcess, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil)
   513  	if errC != nil {
   514  		t.Fatal(errC)
   515  	}
   516  	fmt.Println("OK")
   517  	subOpts := gen.StageSubscribeOptions{
   518  		MinDemand:    2,
   519  		MaxDemand:    4,
   520  		ManualDemand: true,
   521  		// use Temporary to keep this process running
   522  		Cancel: gen.StageCancelTemporary,
   523  	}
   524  	fmt.Println("Consumer@node2 subscribes on Producer@node1: ")
   525  	sub, _ := consumer.Subscribe(consumerProcess, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts)
   526  	fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ")
   527  	waitForResultWithValue(t, producer.value, sub)
   528  	fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1: ")
   529  	waitForResultWithValue(t, consumer.value, sub)
   530  
   531  	fmt.Printf("... Consumer@node2 sent 'Ask' request with count = 4 (min 2, max 4) : ")
   532  	if err := consumer.Ask(consumerProcess, sub, 4); err != nil {
   533  		t.Fatal(err)
   534  	}
   535  	waitForResultWithValue(t, producer.value, etf.Tuple{sub, uint(4)})
   536  	waitForTimeout(t, consumer.value) // shouldn't receive anything here
   537  	events := etf.List{
   538  		"a", "b", "c", "d", "e",
   539  	}
   540  	fmt.Printf("... Producer@node1 sent 5 events. Consumer@node2 should receive 4: ")
   541  	producer.SendEvents(producerProcess, events)
   542  	expected := etf.Tuple{"events", sub, events[0:4]}
   543  	waitForResultWithValue(t, consumer.value, expected)
   544  
   545  	producerProcess.Kill()
   546  	fmt.Printf("... Producer process killed. Consumer should receive 'canceled' with reason 'kill': ")
   547  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub, "kill"})
   548  	if !consumerProcess.IsAlive() {
   549  		t.Fatal("Consumer process should be alive here")
   550  	}
   551  
   552  	// case 2: StageCancelTransient
   553  
   554  	fmt.Printf("... Starting Producer process (test StageCancelTransient) : ")
   555  	producerProcess1, errP1 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   556  	if errP1 != nil {
   557  		t.Fatal(errP1)
   558  	}
   559  	fmt.Println("OK")
   560  
   561  	subOpts.Cancel = gen.StageCancelTransient
   562  	sub1, _ := consumer.Subscribe(consumerProcess, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts)
   563  	fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ")
   564  	waitForResultWithValue(t, producer.value, sub1)
   565  	fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelTransient): ")
   566  	waitForResultWithValue(t, consumer.value, sub1)
   567  
   568  	producerProcess1.Kill()
   569  	if err := producerProcess1.WaitWithTimeout(500 * time.Millisecond); err != nil {
   570  		t.Fatal(err)
   571  	}
   572  	fmt.Printf("... Producer process killed. Consumer should receive 'canceled' with reason 'kill': ")
   573  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub1, "kill"})
   574  	fmt.Printf("... Consumer process should be terminated due to reason 'kill': ")
   575  	if err := consumerProcess.WaitWithTimeout(500 * time.Millisecond); err != nil {
   576  		t.Fatal(err)
   577  	}
   578  	fmt.Println("OK")
   579  
   580  	// case 3: StageCancelPermanent
   581  
   582  	fmt.Printf("... Starting Producer process (test StageCancelPermanent) : ")
   583  	producerProcess2, errP2 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   584  	if errP2 != nil {
   585  		t.Fatal(errP2)
   586  	}
   587  	consumerProcess1, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil)
   588  	if errC != nil {
   589  		t.Fatal(errC)
   590  	}
   591  	fmt.Println("OK")
   592  
   593  	subOpts.Cancel = gen.StageCancelPermanent
   594  	sub2, _ := consumer.Subscribe(consumerProcess1, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts)
   595  	fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ")
   596  	waitForResultWithValue(t, producer.value, sub2)
   597  	fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelPermanent): ")
   598  	waitForResultWithValue(t, consumer.value, sub2)
   599  
   600  	producerProcess2.Exit("normal")
   601  	if err := producerProcess2.WaitWithTimeout(500 * time.Millisecond); err != nil {
   602  		t.Fatal(err)
   603  	}
   604  	fmt.Printf("... Producer process terminated normally. Consumer should receive 'canceled' with reason 'normal': ")
   605  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub2, "normal"})
   606  	fmt.Printf("... Consumer process should be terminated due to StageCancelPermanent mode: ")
   607  	if err := consumerProcess1.WaitWithTimeout(500 * time.Millisecond); err != nil {
   608  		t.Fatal(err)
   609  	}
   610  	fmt.Println("OK")
   611  
   612  	// case 4: Cancel on producer's node termination
   613  	fmt.Printf("... Starting Producer process (test cancel on node termination) : ")
   614  	_, errP3 := node1.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   615  	if errP3 != nil {
   616  		t.Fatal(errP3)
   617  	}
   618  	consumerProcess2, errC := node2.Spawn("stageConsumer", gen.ProcessOptions{}, consumer, nil)
   619  	if errC != nil {
   620  		t.Fatal(errC)
   621  	}
   622  	fmt.Println("OK")
   623  	subOpts.Cancel = gen.StageCancelTemporary
   624  	sub3, err := consumer.Subscribe(consumerProcess2, gen.ProcessID{Name: "stageProducer", Node: "nodeStageDistributed01@localhost"}, subOpts)
   625  	if err != nil {
   626  		t.Fatal(err)
   627  	}
   628  	fmt.Printf("... Producer@node1 handled subscription request from Consumer@node2: ")
   629  	waitForResultWithValue(t, producer.value, sub3)
   630  	fmt.Printf("... Consumer@node2 handled subscription confirmation from Producer@node1 (StageCancelTemporary): ")
   631  	waitForResultWithValue(t, consumer.value, sub3)
   632  
   633  	node2.Disconnect(node1.Name())
   634  	node1.Stop()
   635  	fmt.Printf("... Stopping node1: ")
   636  	if err := node1.WaitWithTimeout(1000 * time.Millisecond); err != nil {
   637  		t.Fatal(err)
   638  	}
   639  	fmt.Println("OK")
   640  	waitForResultWithValue(t, consumer.value, etf.Tuple{"canceled", sub3, "noconnection"})
   641  
   642  	node2.Stop()
   643  }
   644  
   645  func TestStageDispatcherDemand(t *testing.T) {
   646  	fmt.Printf("\n=== Test StageDispatcherDemand\n")
   647  	fmt.Printf("Starting node: StageDispatcherDemand@localhost...")
   648  	node, _ := ergo.StartNode("StageDispatcherDemand@localhost", "cookies", node.Options{})
   649  	if node == nil {
   650  		t.Fatal("can't start node")
   651  		return
   652  	}
   653  	fmt.Println("OK")
   654  
   655  	subOpts := gen.StageSubscribeOptions{
   656  		MinDemand: 4,
   657  		MaxDemand: 4,
   658  	}
   659  
   660  	producer := &StageProducerTest{
   661  		value: make(chan interface{}, 2),
   662  		// StageDispatcherDemand - this is default dispatcher, so we shouldn't create it explicitly
   663  		// dispatcher: CreateStageDispatcherDemand(),
   664  	}
   665  	consumer := &StageConsumerTest{
   666  		value: make(chan interface{}, 2),
   667  	}
   668  	fmt.Printf("... starting Producer (StageDispatcherDemand): ")
   669  	producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   670  	if errP != nil {
   671  		t.Fatal(errP)
   672  	}
   673  	fmt.Println("OK")
   674  	fmt.Printf("... starting Consumer1: ")
   675  	consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer, nil)
   676  	if errC1 != nil {
   677  		t.Fatal(errC1)
   678  	}
   679  	fmt.Println("OK")
   680  	sub1, _ := consumer.Subscribe(consumer1Process, "stageProducer", subOpts)
   681  	fmt.Printf("... Producer handled subscription request from Consumer1: ")
   682  	expected1 := etf.Tuple{sub1, uint(subOpts.MaxDemand)}
   683  	expected2 := sub1
   684  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   685  
   686  	fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ")
   687  	waitForResultWithValue(t, consumer.value, sub1)
   688  
   689  	fmt.Printf("... starting Consumer2: ")
   690  	consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer, nil)
   691  	if errC2 != nil {
   692  		t.Fatal(errC2)
   693  	}
   694  	fmt.Println("OK")
   695  	sub2, _ := consumer.Subscribe(consumer2Process, "stageProducer", subOpts)
   696  	fmt.Printf("... Producer handled subscription request from Consumer2: ")
   697  	expected1 = etf.Tuple{sub2, uint(subOpts.MaxDemand)}
   698  	expected2 = sub2
   699  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   700  	fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ")
   701  	waitForResultWithValue(t, consumer.value, sub2)
   702  
   703  	fmt.Printf("... starting Consumer3: ")
   704  	consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer, nil)
   705  	if errC3 != nil {
   706  		t.Fatal(errC3)
   707  	}
   708  	fmt.Println("OK")
   709  	sub3, _ := consumer.Subscribe(consumer3Process, "stageProducer", subOpts)
   710  	fmt.Printf("... Producer handled subscription request from Consumer3: ")
   711  	expected1 = etf.Tuple{sub3, uint(subOpts.MaxDemand)}
   712  	expected2 = sub3
   713  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   714  	fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ")
   715  	waitForResultWithValue(t, consumer.value, sub3)
   716  
   717  	fmt.Printf("... Producer sent 5 events. Consumer1 should receive 4: ")
   718  	events := etf.List{
   719  		"a", "b", "c", "d", "e",
   720  	}
   721  	producer.SendEvents(producerProcess, events)
   722  	expected := etf.Tuple{"events", sub1, events[0:4]}
   723  	waitForResultWithValue(t, consumer.value, expected)
   724  
   725  	fmt.Printf("... Producer sent 5 events. Consumer2 should receive 4: ")
   726  	events = etf.List{
   727  		"f", "g", "h", "1", "2",
   728  	}
   729  	producer.SendEvents(producerProcess, events)
   730  	expected = etf.Tuple{"events", sub2, etf.List{"e", "f", "g", "h"}}
   731  	waitForResultWithValue(t, consumer.value, expected)
   732  
   733  	fmt.Printf("... Producer sent 2 events. Consumer3 should receive 4: ")
   734  	events = etf.List{
   735  		"3", "4",
   736  	}
   737  	producer.SendEvents(producerProcess, events)
   738  	expected = etf.Tuple{"events", sub3, etf.List{"1", "2", "3", "4"}}
   739  	waitForResultWithValue(t, consumer.value, expected)
   740  
   741  	node.Stop()
   742  }
   743  
   744  func TestStageDispatcherBroadcast(t *testing.T) {
   745  	fmt.Printf("\n=== Test StageDispatcherBroadcast\n")
   746  	fmt.Printf("Starting node: StageDispatcherBroadcast@localhost...")
   747  	node, _ := ergo.StartNode("StageDispatcherBroadcast@localhost", "cookies", node.Options{})
   748  	if node == nil {
   749  		t.Fatal("can't start node")
   750  		return
   751  	}
   752  	fmt.Println("OK")
   753  
   754  	subOpts := gen.StageSubscribeOptions{
   755  		MinDemand: 4,
   756  		MaxDemand: 4,
   757  	}
   758  
   759  	producer := &StageProducerTest{
   760  		value:      make(chan interface{}, 2),
   761  		dispatcher: gen.CreateStageDispatcherBroadcast(),
   762  	}
   763  	consumer1 := &StageConsumerTest{
   764  		value: make(chan interface{}, 2),
   765  	}
   766  	consumer2 := &StageConsumerTest{
   767  		value: make(chan interface{}, 2),
   768  	}
   769  	consumer3 := &StageConsumerTest{
   770  		value: make(chan interface{}, 2),
   771  	}
   772  	fmt.Printf("... starting Producer (StageDispatcherBroadcast): ")
   773  	producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   774  	if errP != nil {
   775  		t.Fatal(errP)
   776  	}
   777  	fmt.Println("OK")
   778  	fmt.Printf("... starting Consumer1: ")
   779  	consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer1, nil)
   780  	if errC1 != nil {
   781  		t.Fatal(errC1)
   782  	}
   783  	fmt.Println("OK")
   784  	sub1, _ := consumer1.Subscribe(consumer1Process, "stageProducer", subOpts)
   785  	fmt.Printf("... Producer handled subscription request from Consumer1: ")
   786  	expected1 := etf.Tuple{sub1, uint(subOpts.MaxDemand)}
   787  	expected2 := sub1
   788  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   789  
   790  	fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ")
   791  	waitForResultWithValue(t, consumer1.value, sub1)
   792  
   793  	fmt.Printf("... starting Consumer2: ")
   794  	consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer2, nil)
   795  	if errC2 != nil {
   796  		t.Fatal(errC2)
   797  	}
   798  	fmt.Println("OK")
   799  	sub2, _ := consumer2.Subscribe(consumer2Process, "stageProducer", subOpts)
   800  	fmt.Printf("... Producer handled subscription request from Consumer2: ")
   801  	expected1 = etf.Tuple{sub2, uint(subOpts.MaxDemand)}
   802  	expected2 = sub2
   803  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   804  	fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ")
   805  	waitForResultWithValue(t, consumer2.value, sub2)
   806  
   807  	fmt.Printf("... starting Consumer3: ")
   808  	consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer3, nil)
   809  	if errC3 != nil {
   810  		t.Fatal(errC3)
   811  	}
   812  	fmt.Println("OK")
   813  	sub3, _ := consumer3.Subscribe(consumer3Process, "stageProducer", subOpts)
   814  	fmt.Printf("... Producer handled subscription request from Consumer3: ")
   815  	expected1 = etf.Tuple{sub3, uint(subOpts.MaxDemand)}
   816  	expected2 = sub3
   817  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   818  	fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ")
   819  	waitForResultWithValue(t, consumer3.value, sub3)
   820  
   821  	fmt.Printf("... Producer sent 5 events:  ")
   822  	events := etf.List{
   823  		"a", "b", "c", "d", "e",
   824  	}
   825  	producer.SendEvents(producerProcess, events)
   826  	fmt.Println("OK")
   827  	exp1 := etf.Tuple{"events", sub1, events[0:4]}
   828  	fmt.Printf("... Consumer1 should receive 4: ")
   829  	waitForResultWithValue(t, consumer1.value, exp1)
   830  	exp2 := etf.Tuple{"events", sub2, events[0:4]}
   831  	fmt.Printf("... Consumer2 should receive 4: ")
   832  	waitForResultWithValue(t, consumer2.value, exp2)
   833  	exp3 := etf.Tuple{"events", sub3, events[0:4]}
   834  	fmt.Printf("... Consumer2 should receive 4: ")
   835  	waitForResultWithValue(t, consumer3.value, exp3)
   836  
   837  	node.Stop()
   838  
   839  }
   840  
   841  func TestStageDispatcherPartition(t *testing.T) {
   842  	fmt.Printf("\n=== Test StageDispatcherPartition\n")
   843  	fmt.Printf("Starting node: StageDispatcherPartition@localhost...")
   844  	node, _ := ergo.StartNode("StageDispatcherPartition@localhost", "cookies", node.Options{})
   845  	if node == nil {
   846  		t.Fatal("can't start node")
   847  		return
   848  	}
   849  	fmt.Println("OK")
   850  
   851  	subOpts1 := gen.StageSubscribeOptions{
   852  		MinDemand: 3,
   853  		MaxDemand: 4,
   854  		Partition: 0,
   855  	}
   856  	subOpts2 := gen.StageSubscribeOptions{
   857  		MinDemand: 3,
   858  		MaxDemand: 4,
   859  		Partition: 1,
   860  	}
   861  	subOpts3 := gen.StageSubscribeOptions{
   862  		MinDemand: 3,
   863  		MaxDemand: 4,
   864  		Partition: 2,
   865  	}
   866  
   867  	hash := func(t etf.Term) int {
   868  		i, ok := t.(int)
   869  		if !ok {
   870  			// filtering out
   871  			return -1
   872  		}
   873  
   874  		if i > 1000 {
   875  			return 2
   876  		}
   877  		if i > 100 {
   878  			return 1
   879  		}
   880  		return 0
   881  	}
   882  
   883  	producer := &StageProducerTest{
   884  		value:      make(chan interface{}, 2),
   885  		dispatcher: gen.CreateStageDispatcherPartition(3, hash),
   886  	}
   887  	consumer1 := &StageConsumerTest{
   888  		value: make(chan interface{}, 2),
   889  	}
   890  	consumer2 := &StageConsumerTest{
   891  		value: make(chan interface{}, 2),
   892  	}
   893  	consumer3 := &StageConsumerTest{
   894  		value: make(chan interface{}, 2),
   895  	}
   896  
   897  	fmt.Printf("... starting Producer (StageDispatcherPartition): ")
   898  	producerProcess, errP := node.Spawn("stageProducer", gen.ProcessOptions{}, producer, nil)
   899  	if errP != nil {
   900  		t.Fatal(errP)
   901  	}
   902  	fmt.Println("OK")
   903  	fmt.Printf("... starting Consumer1: ")
   904  	consumer1Process, errC1 := node.Spawn("stageConsumer1", gen.ProcessOptions{}, consumer1, nil)
   905  	if errC1 != nil {
   906  		t.Fatal(errC1)
   907  	}
   908  	fmt.Println("OK")
   909  	sub1, _ := consumer1.Subscribe(consumer1Process, "stageProducer", subOpts1)
   910  	fmt.Printf("... Producer handled subscription request from Consumer1: ")
   911  	expected1 := etf.Tuple{sub1, uint(subOpts1.MaxDemand)}
   912  	expected2 := sub1
   913  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   914  
   915  	fmt.Printf("... Consumer1 handled subscription confirmation from Producer: ")
   916  	waitForResultWithValue(t, consumer1.value, sub1)
   917  
   918  	fmt.Printf("... starting Consumer2: ")
   919  	consumer2Process, errC2 := node.Spawn("stageConsumer2", gen.ProcessOptions{}, consumer2, nil)
   920  	if errC2 != nil {
   921  		t.Fatal(errC2)
   922  	}
   923  	fmt.Println("OK")
   924  	sub2, _ := consumer2.Subscribe(consumer2Process, "stageProducer", subOpts2)
   925  	fmt.Printf("... Producer handled subscription request from Consumer2: ")
   926  	expected1 = etf.Tuple{sub2, uint(subOpts2.MaxDemand)}
   927  	expected2 = sub2
   928  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   929  	fmt.Printf("... Consumer2 handled subscription confirmation from Producer: ")
   930  	waitForResultWithValue(t, consumer2.value, sub2)
   931  
   932  	fmt.Printf("... starting Consumer3: ")
   933  	consumer3Process, errC3 := node.Spawn("stageConsumer3", gen.ProcessOptions{}, consumer3, nil)
   934  	if errC3 != nil {
   935  		t.Fatal(errC3)
   936  	}
   937  	fmt.Println("OK")
   938  	sub3, _ := consumer3.Subscribe(consumer3Process, "stageProducer", subOpts3)
   939  	fmt.Printf("... Producer handled subscription request from Consumer3: ")
   940  	expected1 = etf.Tuple{sub3, uint(subOpts3.MaxDemand)}
   941  	expected2 = sub3
   942  	waitForResultWithMultiValue(t, producer.value, etf.List{expected1, expected2})
   943  	fmt.Printf("... Consumer3 handled subscription confirmation from Producer: ")
   944  	waitForResultWithValue(t, consumer3.value, sub3)
   945  
   946  	fmt.Printf("... Producer sent 15 events. 3 of them should be discarded by 'hash' function:  ")
   947  	events := etf.List{
   948  		1, 2000, 200, "a", 90, "b", 80, 3000, 600, 9000, "c", 5, 1000, 100, 30,
   949  	}
   950  	producer.SendEvents(producerProcess, events)
   951  	fmt.Println("OK")
   952  	expEvents1 := etf.List{
   953  		1, 90, 80, 5, // left in the queue: 100, 30,
   954  	}
   955  	exp1 := etf.Tuple{"events", sub1, expEvents1}
   956  	fmt.Printf("... Consumer1 should receive 4: ")
   957  	waitForResultWithValue(t, consumer1.value, exp1)
   958  	expEvents2 := etf.List{
   959  		200, 600, 1000,
   960  	}
   961  	exp2 := etf.Tuple{"events", sub2, expEvents2}
   962  	fmt.Printf("... Consumer2 should receive 3: ")
   963  	waitForResultWithValue(t, consumer2.value, exp2)
   964  	expEvents3 := etf.List{
   965  		2000, 3000, 9000,
   966  	}
   967  	exp3 := etf.Tuple{"events", sub3, expEvents3}
   968  	fmt.Printf("... Consumer2 should receive 3: ")
   969  	waitForResultWithValue(t, consumer3.value, exp3)
   970  
   971  	node.Stop()
   972  
   973  }