github.com/xhghs/rclone@v1.51.1-0.20200430155106-e186a28cced8/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/rclone/rclone/fs" 10 "github.com/rclone/rclone/fstest/mockobject" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestPipe(t *testing.T) { 16 var queueLength int 17 var queueSize int64 18 stats := func(n int, size int64) { 19 queueLength, queueSize = n, size 20 } 21 22 // Make a new pipe 23 p, err := newPipe("", stats, 10) 24 require.NoError(t, err) 25 26 checkStats := func(expectedN int, expectedSize int64) { 27 n, size := p.Stats() 28 assert.Equal(t, expectedN, n) 29 assert.Equal(t, expectedSize, size) 30 assert.Equal(t, expectedN, queueLength) 31 assert.Equal(t, expectedSize, queueSize) 32 } 33 34 checkStats(0, 0) 35 36 ctx := context.Background() 37 38 obj1 := mockobject.New("potato").WithContent([]byte("hello"), mockobject.SeekModeNone) 39 40 pair1 := fs.ObjectPair{Src: obj1, Dst: nil} 41 42 // Put an object 43 ok := p.Put(ctx, pair1) 44 assert.Equal(t, true, ok) 45 checkStats(1, 5) 46 47 // Close the pipe showing reading on closed pipe is OK 48 p.Close() 49 50 // Read from pipe 51 pair2, ok := p.Get(ctx) 52 assert.Equal(t, pair1, pair2) 53 assert.Equal(t, true, ok) 54 checkStats(0, 0) 55 56 // Check read on closed pipe 57 pair2, ok = p.Get(ctx) 58 assert.Equal(t, fs.ObjectPair{}, pair2) 59 assert.Equal(t, false, ok) 60 61 // Check panic on write to closed pipe 62 assert.Panics(t, func() { p.Put(ctx, pair1) }) 63 64 // Make a new pipe 65 p, err = newPipe("", stats, 10) 66 require.NoError(t, err) 67 ctx2, cancel := context.WithCancel(ctx) 68 69 // cancel it in the background - check read ceases 70 go cancel() 71 pair2, ok = p.Get(ctx2) 72 assert.Equal(t, fs.ObjectPair{}, pair2) 73 assert.Equal(t, false, ok) 74 75 // check we can't write 76 ok = p.Put(ctx2, pair1) 77 assert.Equal(t, false, ok) 78 79 } 80 81 // TestPipeConcurrent runs concurrent Get and Put to flush out any 82 // race conditions and concurrency problems. 83 func TestPipeConcurrent(t *testing.T) { 84 const ( 85 N = 1000 86 readWriters = 10 87 ) 88 89 stats := func(n int, size int64) {} 90 91 // Make a new pipe 92 p, err := newPipe("", stats, 10) 93 require.NoError(t, err) 94 95 var wg sync.WaitGroup 96 obj1 := mockobject.New("potato").WithContent([]byte("hello"), mockobject.SeekModeNone) 97 pair1 := fs.ObjectPair{Src: obj1, Dst: nil} 98 ctx := context.Background() 99 var count int64 100 101 for j := 0; j < readWriters; j++ { 102 wg.Add(2) 103 go func() { 104 defer wg.Done() 105 for i := 0; i < N; i++ { 106 // Read from pipe 107 pair2, ok := p.Get(ctx) 108 assert.Equal(t, pair1, pair2) 109 assert.Equal(t, true, ok) 110 atomic.AddInt64(&count, -1) 111 } 112 }() 113 go func() { 114 defer wg.Done() 115 for i := 0; i < N; i++ { 116 // Put an object 117 ok := p.Put(ctx, pair1) 118 assert.Equal(t, true, ok) 119 atomic.AddInt64(&count, 1) 120 } 121 }() 122 } 123 wg.Wait() 124 125 assert.Equal(t, int64(0), count) 126 } 127 128 func TestPipeOrderBy(t *testing.T) { 129 var ( 130 stats = func(n int, size int64) {} 131 ctx = context.Background() 132 obj1 = mockobject.New("b").WithContent([]byte("1"), mockobject.SeekModeNone) 133 obj2 = mockobject.New("a").WithContent([]byte("22"), mockobject.SeekModeNone) 134 pair1 = fs.ObjectPair{Src: obj1} 135 pair2 = fs.ObjectPair{Src: obj2} 136 ) 137 138 for _, test := range []struct { 139 orderBy string 140 swapped1 bool 141 swapped2 bool 142 }{ 143 {"", false, true}, 144 {"size", false, false}, 145 {"name", true, true}, 146 {"modtime", false, true}, 147 {"size,ascending", false, false}, 148 {"name,asc", true, true}, 149 {"modtime,ascending", false, true}, 150 {"size,descending", true, true}, 151 {"name,desc", false, false}, 152 {"modtime,descending", true, false}, 153 } { 154 t.Run(test.orderBy, func(t *testing.T) { 155 p, err := newPipe(test.orderBy, stats, 10) 156 require.NoError(t, err) 157 158 ok := p.Put(ctx, pair1) 159 assert.True(t, ok) 160 ok = p.Put(ctx, pair2) 161 assert.True(t, ok) 162 163 readAndCheck := func(swapped bool) { 164 readFirst, ok := p.Get(ctx) 165 assert.True(t, ok) 166 readSecond, ok := p.Get(ctx) 167 assert.True(t, ok) 168 169 if swapped { 170 assert.True(t, readFirst == pair2 && readSecond == pair1) 171 } else { 172 assert.True(t, readFirst == pair1 && readSecond == pair2) 173 } 174 } 175 176 readAndCheck(test.swapped1) 177 178 // insert other way round 179 180 ok = p.Put(ctx, pair2) 181 assert.True(t, ok) 182 ok = p.Put(ctx, pair1) 183 assert.True(t, ok) 184 185 readAndCheck(test.swapped2) 186 }) 187 } 188 } 189 190 func TestNewLess(t *testing.T) { 191 t.Run("blankOK", func(t *testing.T) { 192 less, err := newLess("") 193 require.NoError(t, err) 194 assert.Nil(t, less) 195 }) 196 197 t.Run("tooManyParts", func(t *testing.T) { 198 _, err := newLess("too,many,parts") 199 require.Error(t, err) 200 assert.Contains(t, err.Error(), "bad --order-by string") 201 }) 202 203 t.Run("unknownComparison", func(t *testing.T) { 204 _, err := newLess("potato") 205 require.Error(t, err) 206 assert.Contains(t, err.Error(), "unknown --order-by comparison") 207 }) 208 209 t.Run("unknownSortDirection", func(t *testing.T) { 210 _, err := newLess("name,sideways") 211 require.Error(t, err) 212 assert.Contains(t, err.Error(), "unknown --order-by sort direction") 213 }) 214 215 var ( 216 obj1 = mockobject.New("b").WithContent([]byte("1"), mockobject.SeekModeNone) 217 obj2 = mockobject.New("a").WithContent([]byte("22"), mockobject.SeekModeNone) 218 pair1 = fs.ObjectPair{Src: obj1} 219 pair2 = fs.ObjectPair{Src: obj2} 220 ) 221 222 for _, test := range []struct { 223 orderBy string 224 pair1LessPair2 bool 225 pair2LessPair1 bool 226 }{ 227 {"size", true, false}, 228 {"name", false, true}, 229 {"modtime", false, false}, 230 {"size,ascending", true, false}, 231 {"name,asc", false, true}, 232 {"modtime,ascending", false, false}, 233 {"size,descending", false, true}, 234 {"name,desc", true, false}, 235 {"modtime,descending", true, true}, 236 } { 237 t.Run(test.orderBy, func(t *testing.T) { 238 less, err := newLess(test.orderBy) 239 require.NoError(t, err) 240 require.NotNil(t, less) 241 pair1LessPair2 := less(pair1, pair2) 242 assert.Equal(t, test.pair1LessPair2, pair1LessPair2) 243 pair2LessPair1 := less(pair2, pair1) 244 assert.Equal(t, test.pair2LessPair1, pair2LessPair1) 245 }) 246 } 247 248 }