github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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  	proto "github.com/hyperledger/fabric/protos/gossip"
    28  	"github.com/stretchr/testify/assert"
    29  )
    30  
    31  func uuid() (string, error) {
    32  	uuid := make([]byte, 16)
    33  	_, err := rand.Read(uuid)
    34  	if err != nil {
    35  		return "", err
    36  	}
    37  	uuid[8] = uuid[8]&^0xc0 | 0x80
    38  
    39  	uuid[6] = uuid[6]&^0xf0 | 0x40
    40  	return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
    41  }
    42  
    43  func randomPayloadWithSeqNum(seqNum uint64) (*proto.Payload, error) {
    44  	data := make([]byte, 64)
    45  	_, err := rand.Read(data)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	uuid, err := uuid()
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return &proto.Payload{seqNum, uuid, data}, nil
    55  }
    56  
    57  func TestNewPayloadsBuffer(t *testing.T) {
    58  	payloadsBuffer := NewPayloadsBuffer(10)
    59  	assert.Equal(t, payloadsBuffer.Next(), uint64(10))
    60  }
    61  
    62  func TestPayloadsBufferImpl_Push(t *testing.T) {
    63  	buffer := NewPayloadsBuffer(5)
    64  
    65  	payload, err := randomPayloadWithSeqNum(4)
    66  
    67  	if err != nil {
    68  		t.Fatal("Wasn't able to generate random payload for test")
    69  	}
    70  
    71  	t.Log("Pushing new payload into buffer")
    72  	buffer.Push(payload)
    73  
    74  	// Payloads with sequence number less than buffer top
    75  	// index should not be accepted
    76  	t.Log("Getting next block sequence number")
    77  	assert.Equal(t, buffer.Next(), uint64(5))
    78  	t.Log("Check block buffer size")
    79  	assert.Equal(t, buffer.Size(), 0)
    80  
    81  	// Adding new payload with seq. number equal to top
    82  	// payload should not be added
    83  	payload, err = randomPayloadWithSeqNum(5)
    84  	if err != nil {
    85  		t.Fatal("Wasn't able to generate random payload for test")
    86  	}
    87  
    88  	t.Log("Pushing new payload into buffer")
    89  	buffer.Push(payload)
    90  	t.Log("Getting next block sequence number")
    91  	assert.Equal(t, buffer.Next(), uint64(5))
    92  	t.Log("Check block buffer size")
    93  	assert.Equal(t, buffer.Size(), 1)
    94  }
    95  
    96  func TestPayloadsBufferImpl_Ready(t *testing.T) {
    97  	fin := make(chan struct{})
    98  	buffer := NewPayloadsBuffer(1)
    99  	assert.Equal(t, buffer.Next(), uint64(1))
   100  
   101  	go func() {
   102  		<-buffer.Ready()
   103  		fin <- struct{}{}
   104  	}()
   105  
   106  	time.AfterFunc(100*time.Millisecond, func() {
   107  		payload, err := randomPayloadWithSeqNum(1)
   108  
   109  		if err != nil {
   110  			t.Fatal("Wasn't able to generate random payload for test")
   111  		}
   112  		buffer.Push(payload)
   113  	})
   114  
   115  	select {
   116  	case <-fin:
   117  		payload := buffer.Pop()
   118  		assert.Equal(t, payload.SeqNum, uint64(1))
   119  	case <-time.After(500 * time.Millisecond):
   120  		t.Fail()
   121  	}
   122  }
   123  
   124  // Test to push several concurrent blocks into the buffer
   125  // with same sequence number, only one expected to succeed
   126  func TestPayloadsBufferImpl_ConcurrentPush(t *testing.T) {
   127  
   128  	// Test setup, next block num to expect and
   129  	// how many concurrent pushes to simulate
   130  	nextSeqNum := uint64(7)
   131  	concurrency := 10
   132  
   133  	buffer := NewPayloadsBuffer(nextSeqNum)
   134  	assert.Equal(t, buffer.Next(), uint64(nextSeqNum))
   135  
   136  	startWG := sync.WaitGroup{}
   137  	startWG.Add(1)
   138  
   139  	finishWG := sync.WaitGroup{}
   140  	finishWG.Add(concurrency)
   141  
   142  	payload, err := randomPayloadWithSeqNum(nextSeqNum)
   143  	assert.NoError(t, err)
   144  
   145  	var errors []error
   146  
   147  	ready := int32(0)
   148  	readyWG := sync.WaitGroup{}
   149  	readyWG.Add(1)
   150  	go func() {
   151  		// Wait for next expected block to arrive
   152  		<-buffer.Ready()
   153  		atomic.AddInt32(&ready, 1)
   154  		readyWG.Done()
   155  	}()
   156  
   157  	for i := 0; i < concurrency; i++ {
   158  		go func() {
   159  			startWG.Wait()
   160  			errors = append(errors, buffer.Push(payload))
   161  			finishWG.Done()
   162  		}()
   163  	}
   164  	startWG.Done()
   165  	finishWG.Wait()
   166  
   167  	success := 0
   168  
   169  	// Only one push attempt expected to succeed
   170  	for _, err := range errors {
   171  		if err == nil {
   172  			success++
   173  		}
   174  	}
   175  
   176  	readyWG.Wait()
   177  	assert.Equal(t, int32(1), atomic.LoadInt32(&ready))
   178  	assert.Equal(t, 1, success)
   179  	// Buffer size has to be only one
   180  	assert.Equal(t, 1, buffer.Size())
   181  }