func TestDuplicateTransfer(t *testing.T) { ready := make(chan struct{}) var xferFuncCalls int32 makeXferFunc := func(id string) DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { atomic.AddInt32(&xferFuncCalls, 1) xfer := NewTransfer() go func() { defer func() { close(progressChan) }() <-ready for i := int64(0); ; i++ { select { case <-time.After(10 * time.Millisecond): case <-xfer.Context().Done(): return } progressChan <- progress.Progress{ID: id, Action: "testing", Current: i, Total: 10} } }() return xfer } } tm := NewTransferManager(5) type transferInfo struct { xfer Transfer watcher *Watcher progressChan chan progress.Progress progressDone chan struct{} receivedFirstProgress chan struct{} } progressConsumer := func(t transferInfo) { first := true for range t.progressChan { if first { close(t.receivedFirstProgress) } first = false } close(t.progressDone) } // Try to start multiple transfers with the same ID transfers := make([]transferInfo, 5) for i := range transfers { t := &transfers[i] t.progressChan = make(chan progress.Progress) t.progressDone = make(chan struct{}) t.receivedFirstProgress = make(chan struct{}) t.xfer, t.watcher = tm.Transfer("id1", makeXferFunc("id1"), progress.ChanOutput(t.progressChan)) go progressConsumer(*t) } // Allow the transfer goroutine to proceed. close(ready) // Confirm that each watcher gets progress output. for _, t := range transfers { <-t.receivedFirstProgress } // Confirm that the transfer function was called exactly once. if xferFuncCalls != 1 { t.Fatal("transfer function wasn't called exactly once") } // Release one watcher every 5ms for _, t := range transfers { t.xfer.Release(t.watcher) <-time.After(5 * time.Millisecond) } for _, t := range transfers { // Now that all watchers have been released, Released() should // return a closed channel. <-t.xfer.Released() // Done() should return a closed channel because the xfer func returned // due to cancellation. <-t.xfer.Done() } for _, t := range transfers { close(t.progressChan) <-t.progressDone } }