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  }