github.com/whiteboxio/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/corev1alpha1/actor/replicator_test.go (about)

     1  package actor
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/awesome-flow/flow/pkg/cfg"
    10  	core "github.com/awesome-flow/flow/pkg/corev1alpha1"
    11  	testutil "github.com/awesome-flow/flow/pkg/util/test"
    12  	flowtest "github.com/awesome-flow/flow/pkg/util/test/corev1alpha1"
    13  )
    14  
    15  func TestMaskFanout(t *testing.T) {
    16  	tests := []struct {
    17  		maskin  uint64
    18  		lenq    int
    19  		maskout uint64
    20  	}{
    21  		{0, 0, 0},
    22  		{0, 1, 1},
    23  		{1, 1, 1},
    24  		{2, 1, 1},
    25  
    26  		{0, 4, 1},
    27  		{1, 4, 2},
    28  		{2, 4, 4},
    29  		{4, 4, 8},
    30  		{8, 4, 1},
    31  
    32  		{0, 3, 1},
    33  		{1, 3, 2},
    34  		{2, 3, 4},
    35  		{4, 3, 1},
    36  	}
    37  
    38  	for _, testCase := range tests {
    39  		if maskout := maskFanout(testCase.maskin, testCase.lenq); maskout != testCase.maskout {
    40  			t.Fatalf("unexpected maskFanout value for input {maskin: %d, lenq: %d}: got: %0b, want: %0b", testCase.maskin, testCase.lenq, maskout, testCase.maskout)
    41  		}
    42  	}
    43  }
    44  
    45  func TestReplicate(t *testing.T) {
    46  	repo := cfg.NewRepository()
    47  	ctx, err := core.NewContext(core.NewConfig(repo))
    48  	if err != nil {
    49  		t.Fatalf("failed to initialize context: %s", err)
    50  	}
    51  	if err := ctx.Start(); err != nil {
    52  		t.Fatalf("failed to start context: %s", err)
    53  	}
    54  
    55  	t.Parallel()
    56  
    57  	nthreads := 4
    58  	var npeers uint8 = 5 // greater than 4 less than 8
    59  
    60  	var mask uint64
    61  	for mask = 0; mask < (1<<npeers)-1; mask++ {
    62  		func(mask uint64) {
    63  			tname := fmt.Sprintf("mask[%06b]", mask)
    64  			t.Run(tname, func(t *testing.T) {
    65  				// mode doesn't matter here
    66  				r, err := NewReplicator("replicator", ctx, core.Params{"mode": "each"})
    67  				if err != nil {
    68  					t.Fatalf("failed to initialize replicator: %s", err)
    69  				}
    70  				if err := r.Start(); err != nil {
    71  					t.Fatalf("failed to start replicator: %s", err)
    72  				}
    73  				var lock sync.Mutex
    74  				peers := make([]core.Actor, 0, npeers)
    75  				mailbox := make([][]*core.Message, npeers)
    76  
    77  				for i := 0; i < int(npeers); i++ {
    78  					mailbox[i] = make([]*core.Message, 0, 1)
    79  					peer, err := flowtest.NewTestActor(
    80  						fmt.Sprintf("test-actor-%d", i),
    81  						ctx,
    82  						core.Params{},
    83  					)
    84  					if err != nil {
    85  						t.Fatalf("failed to initialize test actor: %s", err)
    86  					}
    87  					func(i int) {
    88  						peer.(*flowtest.TestActor).OnReceive(func(msg *core.Message) {
    89  							lock.Lock()
    90  							defer lock.Unlock()
    91  							msg.Complete(core.MsgStatusDone)
    92  							mailbox[i] = append(mailbox[i], msg)
    93  							peer.(*flowtest.TestActor).Flush()
    94  						})
    95  					}(i)
    96  
    97  					peers = append(peers, peer)
    98  					if err := peer.Start(); err != nil {
    99  						t.Fatalf("failed to start test actor: %s", err)
   100  					}
   101  					if err := r.Connect(nthreads, peer); err != nil {
   102  						t.Fatalf("failed to connect test actor: %s", err)
   103  					}
   104  				}
   105  
   106  				msg := core.NewMessage(testutil.RandBytes(1024))
   107  				if err := r.(*Replicator).replicate(msg, mask); err != nil {
   108  					t.Fatalf("failed to send message: %s", err)
   109  				}
   110  				s := msg.Await()
   111  				if s != core.MsgStatusDone {
   112  					t.Fatalf("unexpected msg status: got: %d, want: %d", s, core.MsgStatusDone)
   113  				}
   114  
   115  				for i := 0; i < int(npeers); i++ {
   116  					if err := peers[i].Stop(); err != nil {
   117  						t.Fatalf("failed to stop peer: %s", err)
   118  					}
   119  				}
   120  				if err := r.Stop(); err != nil {
   121  					t.Fatalf("failed to stop replicator: %s", err)
   122  				}
   123  
   124  				ix := 0
   125  				maskdec := mask
   126  				for maskdec > 0 {
   127  					shouldmatch := (maskdec & 0x1) == 1
   128  					maskdec >>= 1
   129  					if len(mailbox[ix]) == 0 {
   130  						if shouldmatch {
   131  							t.Fatalf("expected to receive a message for ix %d mask %06b, got none: %+v", ix, mask, mailbox)
   132  						}
   133  						ix++
   134  						continue
   135  					}
   136  					lastmsg := mailbox[ix][len(mailbox[ix])-1]
   137  					msgmatch := reflect.DeepEqual(lastmsg.Body(), msg.Body())
   138  					if !(shouldmatch && msgmatch) {
   139  						t.Fatalf("message mismatch: should match: %t, got: %s, want: %s", shouldmatch, lastmsg.Body(), msg.Body())
   140  					}
   141  					ix++
   142  				}
   143  			})
   144  		}(mask)
   145  	}
   146  }
   147  
   148  func TestReplicateStatus(t *testing.T) {
   149  	tests := []struct {
   150  		name      string
   151  		statuses  []core.MsgStatus
   152  		expstatus core.MsgStatus
   153  	}{
   154  		{
   155  			name:      "all done",
   156  			statuses:  []core.MsgStatus{core.MsgStatusDone, core.MsgStatusDone, core.MsgStatusDone, core.MsgStatusDone},
   157  			expstatus: core.MsgStatusDone,
   158  		},
   159  		{
   160  			name:      "partial send",
   161  			statuses:  []core.MsgStatus{core.MsgStatusPartialSend, core.MsgStatusDone, core.MsgStatusDone, core.MsgStatusDone},
   162  			expstatus: core.MsgStatusPartialSend,
   163  		},
   164  		{
   165  			name:      "timeout",
   166  			statuses:  []core.MsgStatus{core.MsgStatusTimedOut, core.MsgStatusDone, core.MsgStatusDone, core.MsgStatusDone},
   167  			expstatus: core.MsgStatusTimedOut,
   168  		},
   169  		{
   170  			name:      "other failures",
   171  			statuses:  []core.MsgStatus{core.MsgStatusThrottled, core.MsgStatusFailed, core.MsgStatusInvalid, core.MsgStatusUnroutable},
   172  			expstatus: core.MsgStatusFailed,
   173  		},
   174  		{
   175  			name:      "mixed failures resolved as failure",
   176  			statuses:  []core.MsgStatus{core.MsgStatusDone, core.MsgStatusPartialSend, core.MsgStatusTimedOut, core.MsgStatusFailed},
   177  			expstatus: core.MsgStatusFailed,
   178  		},
   179  		{
   180  			name:      "mixed failures resolved as timeout",
   181  			statuses:  []core.MsgStatus{core.MsgStatusDone, core.MsgStatusPartialSend, core.MsgStatusTimedOut},
   182  			expstatus: core.MsgStatusTimedOut,
   183  		},
   184  		{
   185  			name:      "mixed failures resolved as timeout",
   186  			statuses:  []core.MsgStatus{core.MsgStatusDone, core.MsgStatusPartialSend},
   187  			expstatus: core.MsgStatusPartialSend,
   188  		},
   189  	}
   190  
   191  	t.Parallel()
   192  
   193  	for _, testCase := range tests {
   194  		t.Run(testCase.name, func(t *testing.T) {
   195  			repo := cfg.NewRepository()
   196  			ctx, err := core.NewContext(core.NewConfig(repo))
   197  			if err != nil {
   198  				t.Fatalf("failed to create context: %s", err)
   199  			}
   200  			if err := ctx.Start(); err != nil {
   201  				t.Fatalf("failed to start context: %s", err)
   202  			}
   203  
   204  			r, err := NewReplicator("replicator", ctx, core.Params{"mode": "each"})
   205  			if err != nil {
   206  				t.Fatalf("failed to create replicator: %s", err)
   207  			}
   208  
   209  			npeers := len(testCase.statuses)
   210  			nthreads := 4
   211  			for i := 0; i < npeers; i++ {
   212  				peer, err := flowtest.NewTestActor(
   213  					fmt.Sprintf("test-actor-%d", i),
   214  					ctx,
   215  					core.Params{},
   216  				)
   217  				if err != nil {
   218  					t.Fatalf("failed to create test actor: %s", err)
   219  				}
   220  
   221  				func(status core.MsgStatus) {
   222  					peer.(*flowtest.TestActor).OnReceive(func(msg *core.Message) {
   223  						msg.Complete(status)
   224  						peer.(*flowtest.TestActor).Flush()
   225  					})
   226  				}(testCase.statuses[i])
   227  
   228  				if err := r.Connect(nthreads, peer); err != nil {
   229  					t.Fatalf("failed to connect test actor: %s", err)
   230  				}
   231  				if err := peer.Start(); err != nil {
   232  					t.Fatalf("failed to start test actor: %s", err)
   233  				}
   234  			}
   235  
   236  			if err := r.Start(); err != nil {
   237  				t.Fatalf("failed to start replicator: %s", err)
   238  			}
   239  
   240  			msg := core.NewMessage(testutil.RandBytes(1024))
   241  			if err := r.(*Replicator).replicate(msg, (1<<uint8(npeers))-1); err != nil {
   242  				t.Fatalf("failed to send message: %s", err)
   243  			}
   244  
   245  			if s := msg.Await(); s != testCase.expstatus {
   246  				t.Fatalf("unexpected message status: got: %d, want: %d", s, testCase.expstatus)
   247  			}
   248  		})
   249  	}
   250  }