github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/sync/pipe_test.go (about)

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"sync/atomic"
     7  	"testing"
     8  
     9  	"github.com/ncw/rclone/fs"
    10  	"github.com/ncw/rclone/fstest/mockobject"
    11  	"github.com/stretchr/testify/assert"
    12  )
    13  
    14  func TestPipe(t *testing.T) {
    15  	var queueLength int
    16  	var queueSize int64
    17  	stats := func(n int, size int64) {
    18  		queueLength, queueSize = n, size
    19  	}
    20  
    21  	// Make a new pipe
    22  	p := newPipe(stats, 10)
    23  
    24  	checkStats := func(expectedN int, expectedSize int64) {
    25  		n, size := p.Stats()
    26  		assert.Equal(t, expectedN, n)
    27  		assert.Equal(t, expectedSize, size)
    28  		assert.Equal(t, expectedN, queueLength)
    29  		assert.Equal(t, expectedSize, queueSize)
    30  	}
    31  
    32  	checkStats(0, 0)
    33  
    34  	ctx := context.Background()
    35  
    36  	obj1 := mockobject.New("potato").WithContent([]byte("hello"), mockobject.SeekModeNone)
    37  
    38  	pair1 := fs.ObjectPair{Src: obj1, Dst: nil}
    39  
    40  	// Put an object
    41  	ok := p.Put(ctx, pair1)
    42  	assert.Equal(t, true, ok)
    43  	checkStats(1, 5)
    44  
    45  	// Close the pipe showing reading on closed pipe is OK
    46  	p.Close()
    47  
    48  	// Read from pipe
    49  	pair2, ok := p.Get(ctx)
    50  	assert.Equal(t, pair1, pair2)
    51  	assert.Equal(t, true, ok)
    52  	checkStats(0, 0)
    53  
    54  	// Check read on closed pipe
    55  	pair2, ok = p.Get(ctx)
    56  	assert.Equal(t, fs.ObjectPair{}, pair2)
    57  	assert.Equal(t, false, ok)
    58  
    59  	// Check panic on write to closed pipe
    60  	assert.Panics(t, func() { p.Put(ctx, pair1) })
    61  
    62  	// Make a new pipe
    63  	p = newPipe(stats, 10)
    64  	ctx2, cancel := context.WithCancel(ctx)
    65  
    66  	// cancel it in the background - check read ceases
    67  	go cancel()
    68  	pair2, ok = p.Get(ctx2)
    69  	assert.Equal(t, fs.ObjectPair{}, pair2)
    70  	assert.Equal(t, false, ok)
    71  
    72  	// check we can't write
    73  	ok = p.Put(ctx2, pair1)
    74  	assert.Equal(t, false, ok)
    75  
    76  }
    77  
    78  // TestPipeConcurrent runs concurrent Get and Put to flush out any
    79  // race conditions and concurrency problems.
    80  func TestPipeConcurrent(t *testing.T) {
    81  	const (
    82  		N           = 1000
    83  		readWriters = 10
    84  	)
    85  
    86  	stats := func(n int, size int64) {}
    87  
    88  	// Make a new pipe
    89  	p := newPipe(stats, 10)
    90  
    91  	var wg sync.WaitGroup
    92  	obj1 := mockobject.New("potato").WithContent([]byte("hello"), mockobject.SeekModeNone)
    93  	pair1 := fs.ObjectPair{Src: obj1, Dst: nil}
    94  	ctx := context.Background()
    95  	var count int64
    96  
    97  	for j := 0; j < readWriters; j++ {
    98  		wg.Add(2)
    99  		go func() {
   100  			defer wg.Done()
   101  			for i := 0; i < N; i++ {
   102  				// Read from pipe
   103  				pair2, ok := p.Get(ctx)
   104  				assert.Equal(t, pair1, pair2)
   105  				assert.Equal(t, true, ok)
   106  				atomic.AddInt64(&count, -1)
   107  			}
   108  		}()
   109  		go func() {
   110  			defer wg.Done()
   111  			for i := 0; i < N; i++ {
   112  				// Put an object
   113  				ok := p.Put(ctx, pair1)
   114  				assert.Equal(t, true, ok)
   115  				atomic.AddInt64(&count, 1)
   116  			}
   117  		}()
   118  	}
   119  	wg.Wait()
   120  
   121  	assert.Equal(t, int64(0), count)
   122  }