github.com/metaworking/channeld@v0.7.3/pkg/channeld/data_test.go (about) 1 package channeld 2 3 import ( 4 "container/list" 5 "fmt" 6 "math/rand" 7 "net" 8 "strconv" 9 "testing" 10 "time" 11 12 "github.com/indiest/fmutils" 13 "github.com/metaworking/channeld/internal/testpb" 14 "github.com/metaworking/channeld/pkg/channeldpb" 15 "github.com/metaworking/channeld/pkg/common" 16 "github.com/stretchr/testify/assert" 17 18 "google.golang.org/protobuf/proto" 19 "google.golang.org/protobuf/types/known/anypb" 20 ) 21 22 type testQueuedMessageSender struct { 23 MessageSender 24 msgQueue []common.Message 25 msgProcessor func(common.Message) (common.Message, error) 26 } 27 28 func (s *testQueuedMessageSender) Send(c *Connection, ctx MessageContext) { 29 if s.msgProcessor != nil { 30 var err error 31 ctx.Msg, err = s.msgProcessor(ctx.Msg) 32 if err != nil { 33 panic(err) 34 } 35 } 36 s.msgQueue = append(s.msgQueue, ctx.Msg) 37 } 38 39 func addTestConnection(t channeldpb.ConnectionType) *Connection { 40 return addTestConnectionWithProcessor(t, nil) 41 } 42 43 func addTestConnectionWithProcessor(t channeldpb.ConnectionType, p func(common.Message) (common.Message, error)) *Connection { 44 conn1, _ := net.Pipe() 45 c := AddConnection(conn1, t) 46 c.sender = &testQueuedMessageSender{msgQueue: make([]common.Message, 0), msgProcessor: p} 47 return c 48 } 49 50 func (c *Connection) testQueue() []common.Message { 51 return c.sender.(*testQueuedMessageSender).msgQueue 52 } 53 54 func (c *Connection) latestMsg() common.Message { 55 queue := c.testQueue() 56 if len(queue) > 0 { 57 return queue[len(queue)-1] 58 } else { 59 return nil 60 } 61 } 62 63 func testChannelDataMessageProcessor(msg common.Message) (common.Message, error) { 64 // Extract the payload from the ChannelDataUpdatMessage 65 payload := msg.(*channeldpb.ChannelDataUpdateMessage).Data 66 updateMsg, err := payload.UnmarshalNew() 67 return updateMsg, err 68 } 69 70 // Reproduce the issue that the message dequeued is not in the same order as the message is queued 71 func TestMessageReceiveOrder(t *testing.T) { 72 NUM := 100 73 inMsgQueue := make(chan struct { 74 order int 75 time ChannelTime 76 }, NUM) 77 startTime := time.Now() 78 79 for i := 0; i < NUM; i++ { 80 go func(order int) { 81 inMsgQueue <- struct { 82 order int 83 time ChannelTime 84 }{order: order, time: ChannelTime(time.Since(startTime))} 85 }(i) 86 } 87 88 time.Sleep(10 * time.Millisecond) 89 90 for len(inMsgQueue) > 0 { 91 msg := <-inMsgQueue 92 fmt.Printf("%d: %v\n", msg.order, msg.time) 93 } 94 } 95 96 // See the test case in [the design doc](doc/design.md#fan-out) 97 // TODO: add test cases with FieldMasks (no fan-out if no property is updated) 98 func TestFanOutChannelData(t *testing.T) { 99 InitLogs() 100 InitChannels() 101 InitConnections("../../config/server_conn_fsm_test.json", "../../config/client_non_authoratative_fsm.json") 102 103 c0 := addTestConnectionWithProcessor(channeldpb.ConnectionType_SERVER, testChannelDataMessageProcessor) 104 c1 := addTestConnectionWithProcessor(channeldpb.ConnectionType_CLIENT, testChannelDataMessageProcessor) 105 c2 := addTestConnectionWithProcessor(channeldpb.ConnectionType_CLIENT, testChannelDataMessageProcessor) 106 107 testChannel, _ := CreateChannel(channeldpb.ChannelType_TEST, c0) 108 // Stop the channel.Tick() goroutine 109 testChannel.removing = 1 110 dataMsg := &testpb.TestChannelDataMessage{ 111 Text: "a", 112 Num: 1, 113 } 114 testChannel.InitData(dataMsg, nil) 115 // We need to manually tick the channel. Set the interval to a very large value. 116 testChannel.tickInterval = time.Hour 117 118 cs, _ := c0.SubscribeToChannel(testChannel, nil) 119 assert.NotNil(t, cs) 120 subOptions1 := &channeldpb.ChannelSubscriptionOptions{ 121 FanOutIntervalMs: proto.Uint32(50), 122 } 123 cs, _ = c1.SubscribeToChannel(testChannel, subOptions1) 124 assert.NotNil(t, cs) 125 126 channelStartTime := ChannelTime(100 * int64(time.Millisecond)) 127 // F0 = the whole data 128 testChannel.tickData(channelStartTime) 129 assert.Equal(t, 1, len(c1.testQueue())) 130 assert.Equal(t, 0, len(c2.testQueue())) 131 assert.EqualValues(t, dataMsg.Num, c1.latestMsg().(*testpb.TestChannelDataMessage).Num) 132 133 subOptions2 := &channeldpb.ChannelSubscriptionOptions{ 134 FanOutIntervalMs: proto.Uint32(100), 135 } 136 cs, _ = c2.SubscribeToChannel(testChannel, subOptions2) 137 assert.NotNil(t, cs) 138 // F1 = no data, F7 = the whole data 139 testChannel.tickData(channelStartTime.AddMs(50)) 140 assert.Equal(t, 1, len(c1.testQueue())) 141 assert.Equal(t, 1, len(c2.testQueue())) 142 assert.EqualValues(t, dataMsg.Num, c2.latestMsg().(*testpb.TestChannelDataMessage).Num) 143 144 // U1 arrives 145 u1 := &testpb.TestChannelDataMessage{Text: "b"} 146 testChannel.Data().OnUpdate(u1, channelStartTime.AddMs(60), c0.Id(), nil) 147 148 // F2 = U1 149 testChannel.tickData(channelStartTime.AddMs(100)) 150 assert.Equal(t, 2, len(c1.testQueue())) 151 assert.Equal(t, 1, len(c2.testQueue())) 152 // U1 doesn't have "Num" property 153 assert.NotEqualValues(t, dataMsg.Num, c1.latestMsg().(*testpb.TestChannelDataMessage).Num) 154 assert.EqualValues(t, "b", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 155 assert.EqualValues(t, "a", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 156 157 // U2 arrives 158 u2 := &testpb.TestChannelDataMessage{Text: "c"} 159 testChannel.Data().OnUpdate(u2, channelStartTime.AddMs(120), c0.Id(), nil) 160 161 // F8=U1+U2; F3 = U2 162 testChannel.tickData(channelStartTime.AddMs(150)) 163 assert.Equal(t, 3, len(c1.testQueue())) 164 assert.Equal(t, 2, len(c2.testQueue())) 165 assert.EqualValues(t, "c", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 166 assert.EqualValues(t, "c", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 167 168 u3 := &testpb.TestChannelDataMessage{Text: "d"} 169 testChannel.Data().OnUpdate(u3, channelStartTime.AddMs(205), c2.Id(), nil) 170 testChannel.tickData(channelStartTime.AddMs(210)) 171 assert.Equal(t, 3, len(c1.testQueue())) 172 assert.Equal(t, 2, len(c2.testQueue())) 173 assert.EqualValues(t, "c", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 174 assert.EqualValues(t, "c", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 175 176 testChannel.tickData(channelStartTime.AddMs(250)) 177 178 assert.Equal(t, 4, len(c1.testQueue())) 179 assert.Equal(t, 3, len(c2.testQueue())) 180 assert.EqualValues(t, "d", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 181 assert.EqualValues(t, "d", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 182 183 u4 := &testpb.TestChannelDataMessage{Text: "e"} 184 testChannel.Data().OnUpdate(u4, channelStartTime.AddMs(206), c1.Id(), nil) 185 testChannel.tickData(channelStartTime.AddMs(300)) 186 187 assert.Equal(t, 5, len(c1.testQueue())) 188 assert.Equal(t, 3, len(c2.testQueue())) 189 assert.EqualValues(t, "e", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 190 assert.EqualValues(t, "d", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 191 192 testChannel.tickData(channelStartTime.AddMs(350)) 193 194 assert.Equal(t, 5, len(c1.testQueue())) 195 assert.Equal(t, 4, len(c2.testQueue())) 196 assert.EqualValues(t, "e", c1.latestMsg().(*testpb.TestChannelDataMessage).Text) 197 assert.EqualValues(t, "e", c2.latestMsg().(*testpb.TestChannelDataMessage).Text) 198 } 199 200 func BenchmarkCustomMergeMap(b *testing.B) { 201 dst := &testpb.TestMergeMessage{ 202 Kv: map[int64]*testpb.TestMergeMessage_StringWrapper{}, 203 } 204 src := &testpb.TestMergeMessage{ 205 Kv: map[int64]*testpb.TestMergeMessage_StringWrapper{}, 206 } 207 for i := 0; i < 100; i++ { 208 dst.Kv[int64(i)] = &testpb.TestMergeMessage_StringWrapper{Removed: false, Content: strconv.Itoa(rand.Int())} 209 if rand.Intn(100) < 10 { 210 src.Kv[int64(i)] = &testpb.TestMergeMessage_StringWrapper{Removed: true} 211 } else { 212 src.Kv[int64(i)] = &testpb.TestMergeMessage_StringWrapper{Removed: false, Content: strconv.Itoa(rand.Int())} 213 } 214 } 215 216 mergeOptions := &channeldpb.ChannelDataMergeOptions{ShouldCheckRemovableMapField: true} 217 b.ResetTimer() 218 for i := 0; i < b.N; i++ { 219 mergeWithOptions(dst, src, mergeOptions, nil) 220 } 221 222 // Protoreflect merge: 223 // BenchmarkCustomMergeMap-12 26959 43900 ns/op 8464 B/op 316 allocs/op 224 // BenchmarkCustomMergeMap-12 27038 46457 ns/op 8464 B/op 316 allocs/op 225 // BenchmarkCustomMergeMap-12 24746 49732 ns/op 8464 B/op 316 allocs/op 226 227 // Custom merge: (15x faster!!!) 228 // BenchmarkCustomMergeMap-12 353163 3172 ns/op 0 B/op 0 allocs/op 229 // BenchmarkCustomMergeMap-12 457196 2871 ns/op 0 B/op 0 allocs/op 230 // BenchmarkCustomMergeMap-12 419090 3004 ns/op 0 B/op 0 allocs/op 231 } 232 233 func TestMergeSubOptions(t *testing.T) { 234 subOptions := &channeldpb.ChannelSubscriptionOptions{ 235 DataAccess: Pointer(channeldpb.ChannelDataAccess_WRITE_ACCESS), 236 FanOutIntervalMs: proto.Uint32(100), 237 FanOutDelayMs: proto.Int32(200), 238 } 239 240 updateOptions := &channeldpb.ChannelSubscriptionOptions{ 241 DataAccess: Pointer(channeldpb.ChannelDataAccess_READ_ACCESS), 242 FanOutIntervalMs: proto.Uint32(50), 243 } 244 245 proto.Merge(subOptions, updateOptions) 246 247 assert.EqualValues(t, 50, *subOptions.FanOutIntervalMs) 248 //assert.False(t, subOptions.CanUpdateData) 249 assert.EqualValues(t, channeldpb.ChannelDataAccess_READ_ACCESS, *subOptions.DataAccess) 250 } 251 252 func TestListRemoveElement(t *testing.T) { 253 list := list.New() 254 list.PushBack("a") 255 list.PushBack("b") 256 list.PushBack("b") 257 list.PushBack("c") 258 list.PushBack("b") 259 list.PushBack("d") 260 p := list.Front() 261 var n int = list.Len() 262 for i := 0; i < n; i++ { 263 fmt.Println(p.Value) 264 if p.Value == "b" { 265 tmp := p.Next() 266 list.Remove(p) 267 p = tmp 268 continue 269 } 270 p = p.Next() 271 } 272 assert.Equal(t, 3, list.Len()) 273 } 274 275 func TestListMoveElement(t *testing.T) { 276 list := list.New() 277 list.PushBack("a") 278 list.PushBack("b") 279 list.PushBack("c") 280 assert.Equal(t, "a", list.Front().Value) 281 282 e := list.Front().Next() 283 assert.Equal(t, "b", e.Value) 284 temp := e.Prev() 285 list.MoveToBack(e) 286 e = temp.Next() 287 assert.Equal(t, "c", e.Value) 288 } 289 290 func TestDataMergeOptions(t *testing.T) { 291 InitLogs() 292 dstMsg := &testpb.TestMergeMessage{ 293 List: []string{"a", "b", "c"}, 294 Kv: map[int64]*testpb.TestMergeMessage_StringWrapper{ 295 1: {Content: "aa"}, 296 2: {Content: "bb"}, 297 }, 298 } 299 300 srcMsg := &testpb.TestMergeMessage{ 301 List: []string{"d", "e"}, 302 Kv: map[int64]*testpb.TestMergeMessage_StringWrapper{ 303 1: {Removed: true}, 304 2: {Content: "bbb"}, 305 }, 306 } 307 308 mergedMsg1 := proto.Clone(dstMsg).(*testpb.TestMergeMessage) 309 mergeOptions1 := &channeldpb.ChannelDataMergeOptions{ 310 ShouldReplaceList: true, 311 } 312 mergeWithOptions(mergedMsg1, srcMsg, mergeOptions1, nil) 313 assert.Equal(t, 2, len(mergedMsg1.List)) 314 assert.Equal(t, "e", mergedMsg1.List[1]) 315 316 mergedMsg2 := proto.Clone(dstMsg).(*testpb.TestMergeMessage) 317 mergeOptions2 := &channeldpb.ChannelDataMergeOptions{ 318 ListSizeLimit: 4, 319 } 320 mergeWithOptions(mergedMsg2, srcMsg, mergeOptions2, nil) // [a,b,c,d] 321 assert.Equal(t, 4, len(mergedMsg2.List)) 322 assert.Equal(t, "d", mergedMsg2.List[3]) 323 mergeOptions2.TruncateTop = true 324 mergeWithOptions(mergedMsg2, srcMsg, mergeOptions2, nil) // [c,d,d,e] 325 assert.Equal(t, "c", mergedMsg2.List[0]) 326 assert.Equal(t, "e", mergedMsg2.List[3]) 327 328 mergedMsg3 := proto.Clone(dstMsg).(*testpb.TestMergeMessage) 329 mergeOptions3 := &channeldpb.ChannelDataMergeOptions{ 330 ShouldCheckRemovableMapField: true, 331 } 332 srcBytes, _ := proto.Marshal(srcMsg) 333 proto.Unmarshal(srcBytes, srcMsg) 334 mergeWithOptions(mergedMsg3, srcMsg, mergeOptions3, nil) 335 assert.Equal(t, 1, len(mergedMsg3.Kv)) 336 _, exists := mergedMsg3.Kv[1] 337 assert.False(t, exists) 338 assert.Equal(t, "bbb", mergedMsg3.Kv[2].Content) 339 } 340 341 func TestReflectChannelData(t *testing.T) { 342 RegisterChannelDataType(channeldpb.ChannelType_TEST, &testpb.TestChannelDataMessage{}) 343 globalDataMsg, err := ReflectChannelDataMessage(channeldpb.ChannelType_TEST) 344 assert.NoError(t, err) 345 assert.NotNil(t, globalDataMsg) 346 assert.IsType(t, &testpb.TestChannelDataMessage{}, globalDataMsg) 347 } 348 349 func TestDataFieldMasks(t *testing.T) { 350 nestedMsg := &testpb.TestFieldMaskMessage_NestedMessage{ 351 P1: 1, 352 P2: 2, 353 } 354 testMsg := &testpb.TestFieldMaskMessage{ 355 Name: "test", 356 Msg: nestedMsg, 357 List: []*testpb.TestFieldMaskMessage_NestedMessage{nestedMsg}, 358 Kv1: map[int64]*testpb.TestFieldMaskMessage_NestedMessage{ 359 10: nestedMsg, 360 }, 361 Kv2: map[int64]string{ 362 100: "hello", 363 }, 364 } 365 366 filteredMsg1 := proto.Clone(testMsg) 367 fmutils.Filter(filteredMsg1, []string{"name"}) 368 t.Log(filteredMsg1.(*testpb.TestFieldMaskMessage).String()) 369 370 filteredMsg2 := proto.Clone(testMsg) 371 fmutils.Filter(filteredMsg2, []string{"msg.p1"}) 372 t.Log(filteredMsg2.(*testpb.TestFieldMaskMessage).String()) 373 374 filteredMsg3 := proto.Clone(testMsg) 375 fmutils.Filter(filteredMsg3, []string{"list.p2"}) 376 t.Log(filteredMsg3.(*testpb.TestFieldMaskMessage).String()) 377 378 filteredMsg4 := proto.Clone(testMsg) 379 fmutils.Filter(filteredMsg4, []string{"kv1.p1", "kv1.p2", "kv1.p3"}) 380 t.Log(filteredMsg4.(*testpb.TestFieldMaskMessage).String()) 381 fmutils.Prune(filteredMsg4, []string{"kv1.p1"}) 382 t.Log(filteredMsg4.(*testpb.TestFieldMaskMessage).String()) 383 384 filteredMsg5 := proto.Clone(testMsg) 385 fmutils.Filter(filteredMsg5, []string{"kv2.a"}) 386 t.Log(filteredMsg5.(*testpb.TestFieldMaskMessage).String()) 387 } 388 389 func TestProtobufAny(t *testing.T) { 390 any1, err := anypb.New(&testpb.TestAnyMessage_Type1{Value: "a"}) 391 assert.NoError(t, err) 392 393 any2, err := anypb.New(&testpb.TestAnyMessage_Type2{Value: 1}) 394 assert.NoError(t, err) 395 396 msg1 := &testpb.TestAnyMessage{Msg: any1} 397 msg2 := &testpb.TestAnyMessage{Msg: any2} 398 // Can merge the any property from different type 399 proto.Merge(msg1, msg2) 400 assert.EqualValues(t, any2, msg1.Msg) 401 // Can be converted to a message of a unknown type 402 um, err := msg1.Msg.UnmarshalNew() 403 assert.NoError(t, err) 404 assert.EqualValues(t, 1, um.(*testpb.TestAnyMessage_Type2).Value) 405 406 msg1.List = append(msg1.List, any1) 407 msg2.List = append(msg2.List, any2) 408 // Can merge the any list of different types 409 proto.Merge(msg1, msg2) 410 assert.Equal(t, 2, len(msg1.List)) 411 } 412 413 func TestProtobufMapMerge(t *testing.T) { 414 testMsg := &testpb.TestMapMessage{ 415 Kv: make(map[uint32]string), 416 Kv2: make(map[uint32]*testpb.TestMapMessage_StringWrapper), 417 } 418 testMsg.Kv[1] = "a" 419 testMsg.Kv[2] = "b" 420 testMsg.Kv[3] = "c" 421 testMsg.Kv[4] = "d" 422 423 testMsg.Kv2[1] = &testpb.TestMapMessage_StringWrapper{Content: "a"} 424 testMsg.Kv2[2] = &testpb.TestMapMessage_StringWrapper{Content: "b", Num: 2} 425 426 updateMsg := &testpb.TestMapMessage{ 427 Kv: make(map[uint32]string), 428 Kv2: make(map[uint32]*testpb.TestMapMessage_StringWrapper), 429 } 430 updateMsg.Kv[2] = "bb" 431 updateMsg.Kv[3] = "" 432 updateMsg.Kv[4] = "dd" 433 434 updateMsg.Kv2[1] = nil 435 updateMsg.Kv2[2] = &testpb.TestMapMessage_StringWrapper{Num: 3} 436 437 proto.Merge(testMsg, updateMsg) 438 439 assert.Equal(t, "a", testMsg.Kv[1]) 440 assert.Equal(t, "bb", testMsg.Kv[2]) 441 assert.Equal(t, "", testMsg.Kv[3]) 442 assert.Equal(t, "dd", testMsg.Kv[4]) 443 444 /* By default, protobuf ignores the nil value 445 assert.Equal(t, nil, testMsg.Kv2[1]) 446 */ 447 assert.NotEqual(t, nil, testMsg.Kv2[1]) 448 449 assert.Equal(t, int64(3), testMsg.Kv2[2].Num) 450 /* 451 // The other properties should remain the same 452 assert.Equal(t, "b", testMsg.Kv2[2].Content) 453 */ 454 assert.Equal(t, "", testMsg.Kv2[2].Content) 455 }