github.com/ergo-services/ergo@v1.999.224/proto/dist/proto_test.go (about)

     1  package dist
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  	"reflect"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ergo-services/ergo/etf"
    11  	"github.com/ergo-services/ergo/lib"
    12  )
    13  
    14  func TestDecodeDistHeaderAtomCache(t *testing.T) {
    15  	c := &distConnection{}
    16  	c.cache = etf.NewAtomCache()
    17  	a1 := etf.Atom("atom1")
    18  	a2 := etf.Atom("atom2")
    19  	c.cache.In.Atoms[1034] = &a1
    20  	c.cache.In.Atoms[5] = &a2
    21  	packet := []byte{
    22  		131, 68, // start dist header
    23  		5, 4, 137, 9, // 5 atoms and theirs flags
    24  		10, 5, // already cached atom ids
    25  		236, 3, 114, 101, 103, // atom 'reg'
    26  		9, 4, 99, 97, 108, 108, //atom 'call'
    27  		238, 13, 115, 101, 116, 95, 103, 101, 116, 95, 115, 116, 97, 116, 101, // atom 'set_get_state'
    28  		104, 4, 97, 6, 103, 82, 0, 0, 0, 0, 85, 0, 0, 0, 0, 2, 82, 1, 82, 2, // message...
    29  		104, 3, 82, 3, 103, 82, 0, 0, 0, 0, 245, 0, 0, 0, 2, 2,
    30  		104, 2, 82, 4, 109, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    31  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    32  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    33  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    34  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    35  	}
    36  
    37  	cacheExpected := []etf.Atom{"atom1", "atom2", "reg", "call", "set_get_state"}
    38  	cacheInExpected := c.cache.In.Atoms
    39  	a3 := etf.Atom("reg")
    40  	a4 := etf.Atom("call")
    41  	a5 := etf.Atom("set_get_state")
    42  	cacheInExpected[492] = &a3
    43  	cacheInExpected[9] = &a4
    44  	cacheInExpected[494] = &a5
    45  
    46  	packetExpected := packet[34:]
    47  	cache, packet1, _ := c.decodeDistHeaderAtomCache(packet[2:], nil)
    48  
    49  	if !bytes.Equal(packet1, packetExpected) {
    50  		t.Fatal("incorrect packet")
    51  	}
    52  
    53  	if !reflect.DeepEqual(c.cache.In.Atoms, cacheInExpected) {
    54  		t.Fatal("incorrect cacheIn")
    55  	}
    56  
    57  	if !reflect.DeepEqual(cache, cacheExpected) {
    58  		t.Fatal("incorrect cache", cache)
    59  	}
    60  
    61  }
    62  
    63  func TestEncodeDistHeaderAtomCache(t *testing.T) {
    64  
    65  	b := lib.TakeBuffer()
    66  	defer lib.ReleaseBuffer(b)
    67  
    68  	senderAtomCache := make(map[etf.Atom]etf.CacheItem)
    69  	encodingAtomCache := etf.TakeEncodingAtomCache()
    70  	defer etf.ReleaseEncodingAtomCache(encodingAtomCache)
    71  
    72  	senderAtomCache["reg"] = etf.CacheItem{ID: 1000, Encoded: false, Name: "reg"}
    73  	senderAtomCache["call"] = etf.CacheItem{ID: 499, Encoded: false, Name: "call"}
    74  	senderAtomCache["one_more_atom"] = etf.CacheItem{ID: 199, Encoded: true, Name: "one_more_atom"}
    75  	senderAtomCache["yet_another_atom"] = etf.CacheItem{ID: 2, Encoded: false, Name: "yet_another_atom"}
    76  	senderAtomCache["extra_atom"] = etf.CacheItem{ID: 10, Encoded: true, Name: "extra_atom"}
    77  	senderAtomCache["potato"] = etf.CacheItem{ID: 2017, Encoded: true, Name: "potato"}
    78  
    79  	// Encoded field is ignored here
    80  	encodingAtomCache.Append(etf.CacheItem{ID: 499, Name: "call"})
    81  	encodingAtomCache.Append(etf.CacheItem{ID: 1000, Name: "reg"})
    82  	encodingAtomCache.Append(etf.CacheItem{ID: 199, Name: "one_more_atom"})
    83  	encodingAtomCache.Append(etf.CacheItem{ID: 2017, Name: "potato"})
    84  
    85  	expected := []byte{
    86  		4, 185, 112, 0, // 4 atoms and theirs flags
    87  		243, 4, 99, 97, 108, 108, // atom call
    88  		232, 3, 114, 101, 103, // atom reg
    89  		199, // atom one_more_atom, already encoded
    90  		225, // atom potato, already encoded
    91  
    92  	}
    93  
    94  	l := &distConnection{}
    95  	l.encodeDistHeaderAtomCache(b, senderAtomCache, encodingAtomCache)
    96  
    97  	if !reflect.DeepEqual(b.B, expected) {
    98  		t.Fatal("incorrect value")
    99  	}
   100  
   101  	b.Reset()
   102  	encodingAtomCache.Append(etf.CacheItem{ID: 2, Name: "yet_another_atom"})
   103  
   104  	expected = []byte{
   105  		5, 49, 112, 8, // 5 atoms and theirs flags
   106  		243,                      // atom call. already encoded
   107  		232,                      // atom reg. already encoded
   108  		199,                      // atom one_more_atom. already encoded
   109  		225,                      // atom potato. already encoded
   110  		2, 16, 121, 101, 116, 95, // atom yet_another_atom
   111  		97, 110, 111, 116, 104, 101,
   112  		114, 95, 97, 116, 111, 109,
   113  	}
   114  	l.encodeDistHeaderAtomCache(b, senderAtomCache, encodingAtomCache)
   115  
   116  	if !reflect.DeepEqual(b.B, expected) {
   117  		t.Fatal("incorrect value", b.B)
   118  	}
   119  }
   120  
   121  func BenchmarkDecodeDistHeaderAtomCache(b *testing.B) {
   122  	link := &distConnection{}
   123  	packet := []byte{
   124  		131, 68, // start dist header
   125  		5, 4, 137, 9, // 5 atoms and theirs flags
   126  		10, 5, // already cached atom ids
   127  		236, 3, 114, 101, 103, // atom 'reg'
   128  		9, 4, 99, 97, 108, 108, //atom 'call'
   129  		238, 13, 115, 101, 116, 95, 103, 101, 116, 95, 115, 116, 97, 116, 101, // atom 'set_get_state'
   130  		104, 4, 97, 6, 103, 82, 0, 0, 0, 0, 85, 0, 0, 0, 0, 2, 82, 1, 82, 2, // message...
   131  		104, 3, 82, 3, 103, 82, 0, 0, 0, 0, 245, 0, 0, 0, 2, 2,
   132  		104, 2, 82, 4, 109, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   133  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   134  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   135  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   136  		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   137  	}
   138  	b.ResetTimer()
   139  	for i := 0; i < b.N; i++ {
   140  		link.decodeDistHeaderAtomCache(packet[2:], nil)
   141  	}
   142  }
   143  
   144  func BenchmarkEncodeDistHeaderAtomCache(b *testing.B) {
   145  	link := &distConnection{}
   146  	buf := lib.TakeBuffer()
   147  	defer lib.ReleaseBuffer(buf)
   148  
   149  	senderAtomCache := make(map[etf.Atom]etf.CacheItem)
   150  	encodingAtomCache := etf.TakeEncodingAtomCache()
   151  	defer etf.ReleaseEncodingAtomCache(encodingAtomCache)
   152  
   153  	senderAtomCache["reg"] = etf.CacheItem{ID: 1000, Encoded: false, Name: "reg"}
   154  	senderAtomCache["call"] = etf.CacheItem{ID: 499, Encoded: false, Name: "call"}
   155  	senderAtomCache["one_more_atom"] = etf.CacheItem{ID: 199, Encoded: true, Name: "one_more_atom"}
   156  	senderAtomCache["yet_another_atom"] = etf.CacheItem{ID: 2, Encoded: false, Name: "yet_another_atom"}
   157  	senderAtomCache["extra_atom"] = etf.CacheItem{ID: 10, Encoded: true, Name: "extra_atom"}
   158  	senderAtomCache["potato"] = etf.CacheItem{ID: 2017, Encoded: true, Name: "potato"}
   159  
   160  	// Encoded field is ignored here
   161  	encodingAtomCache.Append(etf.CacheItem{ID: 499, Name: "call"})
   162  	encodingAtomCache.Append(etf.CacheItem{ID: 1000, Name: "reg"})
   163  	encodingAtomCache.Append(etf.CacheItem{ID: 199, Name: "one_more_atom"})
   164  	encodingAtomCache.Append(etf.CacheItem{ID: 2017, Name: "potato"})
   165  	b.ResetTimer()
   166  	for i := 0; i < b.N; i++ {
   167  		link.encodeDistHeaderAtomCache(buf, senderAtomCache, encodingAtomCache)
   168  	}
   169  }
   170  
   171  func TestDecodeFragment(t *testing.T) {
   172  	link := &distConnection{}
   173  
   174  	link.checkCleanTimeout = 50 * time.Millisecond
   175  	link.checkCleanDeadline = 150 * time.Millisecond
   176  	link.fragments = make(map[uint64]*fragmentedPacket)
   177  
   178  	// decode fragment with fragmentID=0 should return error
   179  	fragment0 := []byte{protoDistFragment1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3}
   180  	if _, e := link.decodeFragment(fragment0, nil); e == nil {
   181  		t.Fatal("should be error here")
   182  	}
   183  
   184  	fragment1 := []byte{protoDistFragment1, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 2, 3}
   185  	fragment2 := []byte{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2, 4, 5, 6}
   186  	fragment3 := []byte{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 7, 8, 9}
   187  
   188  	expected := []byte{68, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   189  
   190  	// add first fragment
   191  	if x, e := link.decodeFragment(fragment1, nil); x != nil || e != nil {
   192  		t.Fatal("should be nil here", x, e)
   193  	}
   194  	// add second one
   195  	if x, e := link.decodeFragment(fragment2, nil); x != nil || e != nil {
   196  		t.Fatal("should be nil here", e)
   197  	}
   198  	// add the last one. should return *lib.Buffer with assembled packet
   199  	if x, e := link.decodeFragment(fragment3, nil); x == nil || e != nil {
   200  		t.Fatal("shouldn't be nil here", e)
   201  	} else {
   202  		// x should be *lib.Buffer
   203  		if !reflect.DeepEqual(expected, x.B) {
   204  			t.Fatal("exp:", expected, "got:", x.B)
   205  		}
   206  		lib.ReleaseBuffer(x)
   207  
   208  		// map of the fragments should be empty here
   209  		if len(link.fragments) > 0 {
   210  			t.Fatal("fragments should be empty")
   211  		}
   212  	}
   213  
   214  	link.checkCleanTimeout = 50 * time.Millisecond
   215  	link.checkCleanDeadline = 150 * time.Millisecond
   216  	// test lost fragment
   217  	// add the first fragment and wait 160ms
   218  	if x, e := link.decodeFragment(fragment1, nil); x != nil || e != nil {
   219  		t.Fatal("should be nil here", e)
   220  	}
   221  	if len(link.fragments) == 0 {
   222  		t.Fatal("fragments should have a record ")
   223  	}
   224  	// check and clean process should remove this record
   225  	time.Sleep(360 * time.Millisecond)
   226  
   227  	// map of the fragments should be empty here
   228  	if len(link.fragments) > 0 {
   229  		t.Fatal("fragments should be empty")
   230  	}
   231  
   232  	link.checkCleanTimeout = 0
   233  	link.checkCleanDeadline = 0
   234  	fragments := [][]byte{
   235  		{protoDistFragment1, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 9, 0, 1, 2, 3},
   236  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 8, 4, 5, 6},
   237  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, 8, 9},
   238  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6, 10, 11, 12},
   239  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 5, 13, 14, 15},
   240  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 4, 16, 17, 18},
   241  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 3, 19, 20, 21},
   242  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 2, 22, 23, 24},
   243  		{protoDistFragmentN, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 25, 26, 27},
   244  	}
   245  	expected = []byte{68, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27}
   246  
   247  	fragmentsReverse := make([][]byte, len(fragments))
   248  	l := len(fragments)
   249  	for i := 0; i < l; i++ {
   250  		fragmentsReverse[l-i-1] = fragments[i]
   251  	}
   252  
   253  	var result *lib.Buffer
   254  	var e error
   255  	for i := range fragmentsReverse {
   256  		if result, e = link.decodeFragment(fragmentsReverse[i], nil); e != nil {
   257  			t.Fatal(e)
   258  		}
   259  
   260  	}
   261  	if result == nil {
   262  		t.Fatal("got nil result")
   263  	}
   264  	if !reflect.DeepEqual(expected, result.B) {
   265  		t.Fatal("exp:", expected, "got:", result.B[:len(expected)])
   266  	}
   267  	// map of the fragments should be empty here
   268  	if len(link.fragments) > 0 {
   269  		t.Fatal("fragments should be empty")
   270  	}
   271  
   272  	// reshuffling 100 times
   273  	for k := 0; k < 100; k++ {
   274  		result = nil
   275  		fragmentsShuffle := make([][]byte, l)
   276  		rand.Seed(time.Now().UnixNano())
   277  		for i, v := range rand.Perm(l) {
   278  			fragmentsShuffle[v] = fragments[i]
   279  		}
   280  
   281  		for i := range fragmentsShuffle {
   282  			if result, e = link.decodeFragment(fragmentsShuffle[i], nil); e != nil {
   283  				t.Fatal(e)
   284  			}
   285  
   286  		}
   287  		if result == nil {
   288  			t.Fatal("got nil result")
   289  		}
   290  		if !reflect.DeepEqual(expected, result.B) {
   291  			t.Fatal("exp:", expected, "got:", result.B[:len(expected)])
   292  		}
   293  	}
   294  }