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  }