github.com/reds/docker@v1.11.2-rc1/distribution/xfer/transfer_test.go (about) 1 package xfer 2 3 import ( 4 "sync/atomic" 5 "testing" 6 "time" 7 8 "github.com/docker/docker/pkg/progress" 9 ) 10 11 func TestTransfer(t *testing.T) { 12 makeXferFunc := func(id string) DoFunc { 13 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 14 select { 15 case <-start: 16 default: 17 t.Fatalf("transfer function not started even though concurrency limit not reached") 18 } 19 20 xfer := NewTransfer() 21 go func() { 22 for i := 0; i <= 10; i++ { 23 progressChan <- progress.Progress{ID: id, Action: "testing", Current: int64(i), Total: 10} 24 time.Sleep(10 * time.Millisecond) 25 } 26 close(progressChan) 27 }() 28 return xfer 29 } 30 } 31 32 tm := NewTransferManager(5) 33 progressChan := make(chan progress.Progress) 34 progressDone := make(chan struct{}) 35 receivedProgress := make(map[string]int64) 36 37 go func() { 38 for p := range progressChan { 39 val, present := receivedProgress[p.ID] 40 if !present { 41 if p.Current != 0 { 42 t.Fatalf("got unexpected progress value: %d (expected 0)", p.Current) 43 } 44 } else if p.Current <= val { 45 t.Fatalf("got unexpected progress value: %d (expected %d)", p.Current, val+1) 46 } 47 receivedProgress[p.ID] = p.Current 48 } 49 close(progressDone) 50 }() 51 52 // Start a few transfers 53 ids := []string{"id1", "id2", "id3"} 54 xfers := make([]Transfer, len(ids)) 55 watchers := make([]*Watcher, len(ids)) 56 for i, id := range ids { 57 xfers[i], watchers[i] = tm.Transfer(id, makeXferFunc(id), progress.ChanOutput(progressChan)) 58 } 59 60 for i, xfer := range xfers { 61 <-xfer.Done() 62 xfer.Release(watchers[i]) 63 } 64 close(progressChan) 65 <-progressDone 66 67 for _, id := range ids { 68 if receivedProgress[id] != 10 { 69 t.Fatalf("final progress value %d instead of 10", receivedProgress[id]) 70 } 71 } 72 } 73 74 func TestConcurrencyLimit(t *testing.T) { 75 concurrencyLimit := 3 76 var runningJobs int32 77 78 makeXferFunc := func(id string) DoFunc { 79 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 80 xfer := NewTransfer() 81 go func() { 82 <-start 83 totalJobs := atomic.AddInt32(&runningJobs, 1) 84 if int(totalJobs) > concurrencyLimit { 85 t.Fatalf("too many jobs running") 86 } 87 for i := 0; i <= 10; i++ { 88 progressChan <- progress.Progress{ID: id, Action: "testing", Current: int64(i), Total: 10} 89 time.Sleep(10 * time.Millisecond) 90 } 91 atomic.AddInt32(&runningJobs, -1) 92 close(progressChan) 93 }() 94 return xfer 95 } 96 } 97 98 tm := NewTransferManager(concurrencyLimit) 99 progressChan := make(chan progress.Progress) 100 progressDone := make(chan struct{}) 101 receivedProgress := make(map[string]int64) 102 103 go func() { 104 for p := range progressChan { 105 receivedProgress[p.ID] = p.Current 106 } 107 close(progressDone) 108 }() 109 110 // Start more transfers than the concurrency limit 111 ids := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8"} 112 xfers := make([]Transfer, len(ids)) 113 watchers := make([]*Watcher, len(ids)) 114 for i, id := range ids { 115 xfers[i], watchers[i] = tm.Transfer(id, makeXferFunc(id), progress.ChanOutput(progressChan)) 116 } 117 118 for i, xfer := range xfers { 119 <-xfer.Done() 120 xfer.Release(watchers[i]) 121 } 122 close(progressChan) 123 <-progressDone 124 125 for _, id := range ids { 126 if receivedProgress[id] != 10 { 127 t.Fatalf("final progress value %d instead of 10", receivedProgress[id]) 128 } 129 } 130 } 131 132 func TestInactiveJobs(t *testing.T) { 133 concurrencyLimit := 3 134 var runningJobs int32 135 testDone := make(chan struct{}) 136 137 makeXferFunc := func(id string) DoFunc { 138 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 139 xfer := NewTransfer() 140 go func() { 141 <-start 142 totalJobs := atomic.AddInt32(&runningJobs, 1) 143 if int(totalJobs) > concurrencyLimit { 144 t.Fatalf("too many jobs running") 145 } 146 for i := 0; i <= 10; i++ { 147 progressChan <- progress.Progress{ID: id, Action: "testing", Current: int64(i), Total: 10} 148 time.Sleep(10 * time.Millisecond) 149 } 150 atomic.AddInt32(&runningJobs, -1) 151 close(inactive) 152 <-testDone 153 close(progressChan) 154 }() 155 return xfer 156 } 157 } 158 159 tm := NewTransferManager(concurrencyLimit) 160 progressChan := make(chan progress.Progress) 161 progressDone := make(chan struct{}) 162 receivedProgress := make(map[string]int64) 163 164 go func() { 165 for p := range progressChan { 166 receivedProgress[p.ID] = p.Current 167 } 168 close(progressDone) 169 }() 170 171 // Start more transfers than the concurrency limit 172 ids := []string{"id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8"} 173 xfers := make([]Transfer, len(ids)) 174 watchers := make([]*Watcher, len(ids)) 175 for i, id := range ids { 176 xfers[i], watchers[i] = tm.Transfer(id, makeXferFunc(id), progress.ChanOutput(progressChan)) 177 } 178 179 close(testDone) 180 for i, xfer := range xfers { 181 <-xfer.Done() 182 xfer.Release(watchers[i]) 183 } 184 close(progressChan) 185 <-progressDone 186 187 for _, id := range ids { 188 if receivedProgress[id] != 10 { 189 t.Fatalf("final progress value %d instead of 10", receivedProgress[id]) 190 } 191 } 192 } 193 194 func TestWatchRelease(t *testing.T) { 195 ready := make(chan struct{}) 196 197 makeXferFunc := func(id string) DoFunc { 198 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 199 xfer := NewTransfer() 200 go func() { 201 defer func() { 202 close(progressChan) 203 }() 204 <-ready 205 for i := int64(0); ; i++ { 206 select { 207 case <-time.After(10 * time.Millisecond): 208 case <-xfer.Context().Done(): 209 return 210 } 211 progressChan <- progress.Progress{ID: id, Action: "testing", Current: i, Total: 10} 212 } 213 }() 214 return xfer 215 } 216 } 217 218 tm := NewTransferManager(5) 219 220 type watcherInfo struct { 221 watcher *Watcher 222 progressChan chan progress.Progress 223 progressDone chan struct{} 224 receivedFirstProgress chan struct{} 225 } 226 227 progressConsumer := func(w watcherInfo) { 228 first := true 229 for range w.progressChan { 230 if first { 231 close(w.receivedFirstProgress) 232 } 233 first = false 234 } 235 close(w.progressDone) 236 } 237 238 // Start a transfer 239 watchers := make([]watcherInfo, 5) 240 var xfer Transfer 241 watchers[0].progressChan = make(chan progress.Progress) 242 watchers[0].progressDone = make(chan struct{}) 243 watchers[0].receivedFirstProgress = make(chan struct{}) 244 xfer, watchers[0].watcher = tm.Transfer("id1", makeXferFunc("id1"), progress.ChanOutput(watchers[0].progressChan)) 245 go progressConsumer(watchers[0]) 246 247 // Give it multiple watchers 248 for i := 1; i != len(watchers); i++ { 249 watchers[i].progressChan = make(chan progress.Progress) 250 watchers[i].progressDone = make(chan struct{}) 251 watchers[i].receivedFirstProgress = make(chan struct{}) 252 watchers[i].watcher = xfer.Watch(progress.ChanOutput(watchers[i].progressChan)) 253 go progressConsumer(watchers[i]) 254 } 255 256 // Now that the watchers are set up, allow the transfer goroutine to 257 // proceed. 258 close(ready) 259 260 // Confirm that each watcher gets progress output. 261 for _, w := range watchers { 262 <-w.receivedFirstProgress 263 } 264 265 // Release one watcher every 5ms 266 for _, w := range watchers { 267 xfer.Release(w.watcher) 268 <-time.After(5 * time.Millisecond) 269 } 270 271 // Now that all watchers have been released, Released() should 272 // return a closed channel. 273 <-xfer.Released() 274 275 // Done() should return a closed channel because the xfer func returned 276 // due to cancellation. 277 <-xfer.Done() 278 279 for _, w := range watchers { 280 close(w.progressChan) 281 <-w.progressDone 282 } 283 } 284 285 func TestWatchFinishedTransfer(t *testing.T) { 286 makeXferFunc := func(id string) DoFunc { 287 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 288 xfer := NewTransfer() 289 go func() { 290 // Finish immediately 291 close(progressChan) 292 }() 293 return xfer 294 } 295 } 296 297 tm := NewTransferManager(5) 298 299 // Start a transfer 300 watchers := make([]*Watcher, 3) 301 var xfer Transfer 302 xfer, watchers[0] = tm.Transfer("id1", makeXferFunc("id1"), progress.ChanOutput(make(chan progress.Progress))) 303 304 // Give it a watcher immediately 305 watchers[1] = xfer.Watch(progress.ChanOutput(make(chan progress.Progress))) 306 307 // Wait for the transfer to complete 308 <-xfer.Done() 309 310 // Set up another watcher 311 watchers[2] = xfer.Watch(progress.ChanOutput(make(chan progress.Progress))) 312 313 // Release the watchers 314 for _, w := range watchers { 315 xfer.Release(w) 316 } 317 318 // Now that all watchers have been released, Released() should 319 // return a closed channel. 320 <-xfer.Released() 321 } 322 323 func TestDuplicateTransfer(t *testing.T) { 324 ready := make(chan struct{}) 325 326 var xferFuncCalls int32 327 328 makeXferFunc := func(id string) DoFunc { 329 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 330 atomic.AddInt32(&xferFuncCalls, 1) 331 xfer := NewTransfer() 332 go func() { 333 defer func() { 334 close(progressChan) 335 }() 336 <-ready 337 for i := int64(0); ; i++ { 338 select { 339 case <-time.After(10 * time.Millisecond): 340 case <-xfer.Context().Done(): 341 return 342 } 343 progressChan <- progress.Progress{ID: id, Action: "testing", Current: i, Total: 10} 344 } 345 }() 346 return xfer 347 } 348 } 349 350 tm := NewTransferManager(5) 351 352 type transferInfo struct { 353 xfer Transfer 354 watcher *Watcher 355 progressChan chan progress.Progress 356 progressDone chan struct{} 357 receivedFirstProgress chan struct{} 358 } 359 360 progressConsumer := func(t transferInfo) { 361 first := true 362 for range t.progressChan { 363 if first { 364 close(t.receivedFirstProgress) 365 } 366 first = false 367 } 368 close(t.progressDone) 369 } 370 371 // Try to start multiple transfers with the same ID 372 transfers := make([]transferInfo, 5) 373 for i := range transfers { 374 t := &transfers[i] 375 t.progressChan = make(chan progress.Progress) 376 t.progressDone = make(chan struct{}) 377 t.receivedFirstProgress = make(chan struct{}) 378 t.xfer, t.watcher = tm.Transfer("id1", makeXferFunc("id1"), progress.ChanOutput(t.progressChan)) 379 go progressConsumer(*t) 380 } 381 382 // Allow the transfer goroutine to proceed. 383 close(ready) 384 385 // Confirm that each watcher gets progress output. 386 for _, t := range transfers { 387 <-t.receivedFirstProgress 388 } 389 390 // Confirm that the transfer function was called exactly once. 391 if xferFuncCalls != 1 { 392 t.Fatal("transfer function wasn't called exactly once") 393 } 394 395 // Release one watcher every 5ms 396 for _, t := range transfers { 397 t.xfer.Release(t.watcher) 398 <-time.After(5 * time.Millisecond) 399 } 400 401 for _, t := range transfers { 402 // Now that all watchers have been released, Released() should 403 // return a closed channel. 404 <-t.xfer.Released() 405 // Done() should return a closed channel because the xfer func returned 406 // due to cancellation. 407 <-t.xfer.Done() 408 } 409 410 for _, t := range transfers { 411 close(t.progressChan) 412 <-t.progressDone 413 } 414 }