github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/gossip/state/payloads_buffer_test.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package state
    18  
    19  import (
    20  	"crypto/rand"
    21  	"fmt"
    22  	"sync"
    23  	"sync/atomic"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/hyperledger/fabric/gossip/util"
    28  	proto "github.com/hyperledger/fabric/protos/gossip"
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  func init() {
    33  	util.SetupTestLogging()
    34  }
    35  
    36  func uuid() (string, error) {
    37  	uuid := make([]byte, 16)
    38  	_, err := rand.Read(uuid)
    39  	if err != nil {
    40  		return "", err
    41  	}
    42  	uuid[8] = uuid[8]&^0xc0 | 0x80
    43  
    44  	uuid[6] = uuid[6]&^0xf0 | 0x40
    45  	return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
    46  }
    47  
    48  func randomPayloadWithSeqNum(seqNum uint64) (*proto.Payload, error) {
    49  	data := make([]byte, 64)
    50  	_, err := rand.Read(data)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	uuid, err := uuid()
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return &proto.Payload{seqNum, uuid, data}, nil
    60  }
    61  
    62  func TestNewPayloadsBuffer(t *testing.T) {
    63  	payloadsBuffer := NewPayloadsBuffer(10)
    64  	assert.Equal(t, payloadsBuffer.Next(), uint64(10))
    65  }
    66  
    67  func TestPayloadsBufferImpl_Push(t *testing.T) {
    68  	buffer := NewPayloadsBuffer(5)
    69  
    70  	payload, err := randomPayloadWithSeqNum(4)
    71  
    72  	if err != nil {
    73  		t.Fatal("Wasn't able to generate random payload for test")
    74  	}
    75  
    76  	t.Log("Pushing new payload into buffer")
    77  	buffer.Push(payload)
    78  
    79  	// Payloads with sequence number less than buffer top
    80  	// index should not be accepted
    81  	t.Log("Getting next block sequence number")
    82  	assert.Equal(t, buffer.Next(), uint64(5))
    83  	t.Log("Check block buffer size")
    84  	assert.Equal(t, buffer.Size(), 0)
    85  
    86  	// Adding new payload with seq. number equal to top
    87  	// payload should not be added
    88  	payload, err = randomPayloadWithSeqNum(5)
    89  	if err != nil {
    90  		t.Fatal("Wasn't able to generate random payload for test")
    91  	}
    92  
    93  	t.Log("Pushing new payload into buffer")
    94  	buffer.Push(payload)
    95  	t.Log("Getting next block sequence number")
    96  	assert.Equal(t, buffer.Next(), uint64(5))
    97  	t.Log("Check block buffer size")
    98  	assert.Equal(t, buffer.Size(), 1)
    99  }
   100  
   101  func TestPayloadsBufferImpl_Ready(t *testing.T) {
   102  	fin := make(chan struct{})
   103  	buffer := NewPayloadsBuffer(1)
   104  	assert.Equal(t, buffer.Next(), uint64(1))
   105  
   106  	go func() {
   107  		<-buffer.Ready()
   108  		fin <- struct{}{}
   109  	}()
   110  
   111  	time.AfterFunc(100*time.Millisecond, func() {
   112  		payload, err := randomPayloadWithSeqNum(1)
   113  
   114  		if err != nil {
   115  			t.Fatal("Wasn't able to generate random payload for test")
   116  		}
   117  		buffer.Push(payload)
   118  	})
   119  
   120  	select {
   121  	case <-fin:
   122  		payload := buffer.Pop()
   123  		assert.Equal(t, payload.SeqNum, uint64(1))
   124  	case <-time.After(500 * time.Millisecond):
   125  		t.Fail()
   126  	}
   127  }
   128  
   129  // Test to push several concurrent blocks into the buffer
   130  // with same sequence number, only one expected to succeed
   131  func TestPayloadsBufferImpl_ConcurrentPush(t *testing.T) {
   132  
   133  	// Test setup, next block num to expect and
   134  	// how many concurrent pushes to simulate
   135  	nextSeqNum := uint64(7)
   136  	concurrency := 10
   137  
   138  	buffer := NewPayloadsBuffer(nextSeqNum)
   139  	assert.Equal(t, buffer.Next(), uint64(nextSeqNum))
   140  
   141  	startWG := sync.WaitGroup{}
   142  	startWG.Add(1)
   143  
   144  	finishWG := sync.WaitGroup{}
   145  	finishWG.Add(concurrency)
   146  
   147  	payload, err := randomPayloadWithSeqNum(nextSeqNum)
   148  	assert.NoError(t, err)
   149  
   150  	var errors []error
   151  
   152  	ready := int32(0)
   153  	readyWG := sync.WaitGroup{}
   154  	readyWG.Add(1)
   155  	go func() {
   156  		// Wait for next expected block to arrive
   157  		<-buffer.Ready()
   158  		atomic.AddInt32(&ready, 1)
   159  		readyWG.Done()
   160  	}()
   161  
   162  	for i := 0; i < concurrency; i++ {
   163  		go func() {
   164  			startWG.Wait()
   165  			errors = append(errors, buffer.Push(payload))
   166  			finishWG.Done()
   167  		}()
   168  	}
   169  	startWG.Done()
   170  	finishWG.Wait()
   171  
   172  	success := 0
   173  
   174  	// Only one push attempt expected to succeed
   175  	for _, err := range errors {
   176  		if err == nil {
   177  			success++
   178  		}
   179  	}
   180  
   181  	readyWG.Wait()
   182  	assert.Equal(t, int32(1), atomic.LoadInt32(&ready))
   183  	assert.Equal(t, 1, success)
   184  	// Buffer size has to be only one
   185  	assert.Equal(t, 1, buffer.Size())
   186  }