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