github.com/awesome-flow/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 }