15 "github.com/anacrolix/dht"
16 _ "github.com/anacrolix/envpprof"
17 "github.com/anacrolix/missinggo"
18 "github.com/anacrolix/missinggo/filecache"
19 "github.com/bradfitz/iter"
20 "github.com/stretchr/testify/assert"
21 "github.com/stretchr/testify/require"
22 "golang.org/x/time/rate"
24 "github.com/anacrolix/torrent/bencode"
25 "github.com/anacrolix/torrent/internal/testutil"
26 "github.com/anacrolix/torrent/iplist"
27 "github.com/anacrolix/torrent/metainfo"
28 "github.com/anacrolix/torrent/storage"
31 func TestingConfig() *ClientConfig {
32 cfg := NewDefaultClientConfig()
33 cfg.ListenHost = LoopbackListenHost
35 cfg.DataDir = tempDir()
36 cfg.DisableTrackers = true
37 cfg.NoDefaultPortForwarding = true
38 cfg.DisableAcceptRateLimiting = true
42 func TestClientDefault(t *testing.T) {
43 cl, err := NewClient(TestingConfig())
44 require.NoError(t, err)
48 func TestClientNilConfig(t *testing.T) {
49 cl, err := NewClient(nil)
50 require.NoError(t, err)
54 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
55 cfg := TestingConfig()
56 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
57 require.NoError(t, err)
58 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
60 cfg.DefaultStorage = ci
61 cl, err := NewClient(cfg)
62 require.NoError(t, err)
64 // And again, https://github.com/anacrolix/torrent/issues/158
65 cl, err = NewClient(cfg)
66 require.NoError(t, err)
70 func TestAddDropTorrent(t *testing.T) {
71 cl, err := NewClient(TestingConfig())
72 require.NoError(t, err)
74 dir, mi := testutil.GreetingTestTorrent()
75 defer os.RemoveAll(dir)
76 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
77 require.NoError(t, err)
79 tt.SetMaxEstablishedConns(0)
80 tt.SetMaxEstablishedConns(1)
84 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
89 func TestAddTorrentNoUsableURLs(t *testing.T) {
94 func TestAddPeersToUnknownTorrent(t *testing.T) {
99 func TestPieceHashSize(t *testing.T) {
100 assert.Equal(t, 20, pieceHash.Size())
103 func TestTorrentInitialState(t *testing.T) {
104 dir, mi := testutil.GreetingTestTorrent()
105 defer os.RemoveAll(dir)
107 config: &ClientConfig{},
110 tor := cl.newTorrent(
112 storage.NewFileWithCompletion(tempDir(), storage.NewMapPieceCompletion()),
116 err := tor.setInfoBytes(mi.InfoBytes)
118 require.NoError(t, err)
119 require.Len(t, tor.pieces, 3)
120 tor.pendAllChunkSpecs(0)
122 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
124 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
127 func TestUnmarshalPEXMsg(t *testing.T) {
128 var m peerExchangeMessage
129 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
132 if len(m.Added) != 2 {
135 if m.Added[0].Port != 0x506 {
140 func TestReducedDialTimeout(t *testing.T) {
141 cfg := NewDefaultClientConfig()
142 for _, _case := range []struct {
146 ExpectedReduced time.Duration
148 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
149 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
150 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
151 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
152 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
153 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
155 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
156 expected := _case.ExpectedReduced
157 if expected < cfg.MinDialTimeout {
158 expected = cfg.MinDialTimeout
160 if reduced != expected {
161 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
166 func TestAddDropManyTorrents(t *testing.T) {
167 cl, err := NewClient(TestingConfig())
168 require.NoError(t, err)
170 for i := range iter.N(1000) {
172 binary.PutVarint(spec.InfoHash[:], int64(i))
173 tt, new, err := cl.AddTorrentSpec(&spec)
174 assert.NoError(t, err)
180 type FileCacheClientStorageFactoryParams struct {
183 Wrapper func(*filecache.Cache) storage.ClientImpl
186 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
187 return func(dataDir string) storage.ClientImpl {
188 fc, err := filecache.NewCache(dataDir)
193 fc.SetCapacity(ps.Capacity)
195 return ps.Wrapper(fc)
199 type storageFactory func(string) storage.ClientImpl
201 func TestClientTransferDefault(t *testing.T) {
202 testClientTransfer(t, testClientTransferParams{
203 ExportClientStatus: true,
204 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
205 Wrapper: fileCachePieceResourceStorage,
210 func TestClientTransferRateLimitedUpload(t *testing.T) {
211 started := time.Now()
212 testClientTransfer(t, testClientTransferParams{
213 // We are uploading 13 bytes (the length of the greeting torrent). The
214 // chunks are 2 bytes in length. Then the smallest burst we can run
215 // with is 2. Time taken is (13-burst)/rate.
216 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
217 ExportClientStatus: true,
219 require.True(t, time.Since(started) > time.Second)
222 func TestClientTransferRateLimitedDownload(t *testing.T) {
223 testClientTransfer(t, testClientTransferParams{
224 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
228 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
229 return storage.NewResourcePieces(fc.AsResourceProvider())
232 func TestClientTransferSmallCache(t *testing.T) {
233 testClientTransfer(t, testClientTransferParams{
234 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
236 // Going below the piece length means it can't complete a piece so
237 // that it can be hashed.
239 Wrapper: fileCachePieceResourceStorage,
242 // Can't readahead too far or the cache will thrash and drop data we
245 ExportClientStatus: true,
249 func TestClientTransferVarious(t *testing.T) {
251 for _, ls := range []storageFactory{
252 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
253 Wrapper: fileCachePieceResourceStorage,
258 for _, ss := range []func(string) storage.ClientImpl{
262 for _, responsive := range []bool{false, true} {
263 testClientTransfer(t, testClientTransferParams{
264 Responsive: responsive,
268 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
269 testClientTransfer(t, testClientTransferParams{
271 Responsive: responsive,
273 Readahead: readahead,
282 type testClientTransferParams struct {
286 ExportClientStatus bool
287 LeecherStorage func(string) storage.ClientImpl
288 SeederStorage func(string) storage.ClientImpl
289 SeederUploadRateLimiter *rate.Limiter
290 LeecherDownloadRateLimiter *rate.Limiter
293 // Creates a seeder and a leecher, and ensures the data transfers when a read
294 // is attempted on the leecher.
295 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
296 greetingTempDir, mi := testutil.GreetingTestTorrent()
297 defer os.RemoveAll(greetingTempDir)
298 // Create seeder and a Torrent.
299 cfg := TestingConfig()
301 if ps.SeederUploadRateLimiter != nil {
302 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
304 // cfg.ListenAddr = "localhost:4000"
305 if ps.SeederStorage != nil {
306 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
307 defer cfg.DefaultStorage.Close()
309 cfg.DataDir = greetingTempDir
311 seeder, err := NewClient(cfg)
312 require.NoError(t, err)
313 if ps.ExportClientStatus {
314 defer testutil.ExportStatusWriter(seeder, "s")()
316 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
317 // Run a Stats right after Closing the Client. This will trigger the Stats
318 // panic in #214 caused by RemoteAddr on Closed uTP sockets.
319 defer seederTorrent.Stats()
321 seederTorrent.VerifyData()
322 // Create leecher and a Torrent.
323 leecherDataDir, err := ioutil.TempDir("", "")
324 require.NoError(t, err)
325 defer os.RemoveAll(leecherDataDir)
326 cfg = TestingConfig()
327 if ps.LeecherStorage == nil {
328 cfg.DataDir = leecherDataDir
330 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
332 if ps.LeecherDownloadRateLimiter != nil {
333 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
336 leecher, err := NewClient(cfg)
337 require.NoError(t, err)
338 defer leecher.Close()
339 if ps.ExportClientStatus {
340 defer testutil.ExportStatusWriter(leecher, "l")()
342 leecherTorrent, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
343 ret = TorrentSpecFromMetaInfo(mi)
347 require.NoError(t, err)
349 // Now do some things with leecher and seeder.
350 leecherTorrent.AddClientPeer(seeder)
351 // The Torrent should not be interested in obtaining peers, so the one we
352 // just added should be the only one.
353 assert.False(t, leecherTorrent.Seeding())
354 assert.EqualValues(t, 1, leecherTorrent.Stats().PendingPeers)
355 r := leecherTorrent.NewReader()
361 r.SetReadahead(ps.Readahead)
363 assertReadAllGreeting(t, r)
365 seederStats := seederTorrent.Stats()
366 assert.True(t, 13 <= seederStats.BytesWrittenData.Int64())
367 assert.True(t, 8 <= seederStats.ChunksWritten.Int64())
369 leecherStats := leecherTorrent.Stats()
370 assert.True(t, 13 <= leecherStats.BytesReadData.Int64())
371 assert.True(t, 8 <= leecherStats.ChunksRead.Int64())
373 // Try reading through again for the cases where the torrent data size
374 // exceeds the size of the cache.
375 assertReadAllGreeting(t, r)
378 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
379 pos, err := r.Seek(0, io.SeekStart)
380 assert.NoError(t, err)
381 assert.EqualValues(t, 0, pos)
382 _greeting, err := ioutil.ReadAll(r)
383 assert.NoError(t, err)
384 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
387 // Check that after completing leeching, a leecher transitions to a seeding
388 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
389 func TestSeedAfterDownloading(t *testing.T) {
390 greetingTempDir, mi := testutil.GreetingTestTorrent()
391 defer os.RemoveAll(greetingTempDir)
393 cfg := TestingConfig()
395 cfg.DataDir = greetingTempDir
396 seeder, err := NewClient(cfg)
397 require.NoError(t, err)
399 defer testutil.ExportStatusWriter(seeder, "s")()
400 seederTorrent, ok, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
401 require.NoError(t, err)
403 seederTorrent.VerifyData()
405 cfg = TestingConfig()
407 cfg.DataDir, err = ioutil.TempDir("", "")
408 require.NoError(t, err)
409 defer os.RemoveAll(cfg.DataDir)
410 leecher, err := NewClient(cfg)
411 require.NoError(t, err)
412 defer leecher.Close()
413 defer testutil.ExportStatusWriter(leecher, "l")()
415 cfg = TestingConfig()
417 cfg.DataDir, err = ioutil.TempDir("", "")
418 require.NoError(t, err)
419 defer os.RemoveAll(cfg.DataDir)
420 leecherLeecher, _ := NewClient(cfg)
421 require.NoError(t, err)
422 defer leecherLeecher.Close()
423 defer testutil.ExportStatusWriter(leecherLeecher, "ll")()
424 leecherGreeting, ok, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
425 ret = TorrentSpecFromMetaInfo(mi)
429 require.NoError(t, err)
431 llg, ok, err := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
432 ret = TorrentSpecFromMetaInfo(mi)
436 require.NoError(t, err)
438 // Simultaneously DownloadAll in Leecher, and read the contents
439 // consecutively in LeecherLeecher. This non-deterministically triggered a
440 // case where the leecher wouldn't unchoke the LeecherLeecher.
441 var wg sync.WaitGroup
447 b, err := ioutil.ReadAll(r)
448 require.NoError(t, err)
449 assert.EqualValues(t, testutil.GreetingFileContents, b)
451 done := make(chan struct{})
453 go leecherGreeting.AddClientPeer(seeder)
454 go leecherGreeting.AddClientPeer(leecherLeecher)
458 leecherGreeting.DownloadAll()
464 func TestMergingTrackersByAddingSpecs(t *testing.T) {
465 cl, err := NewClient(TestingConfig())
466 require.NoError(t, err)
468 spec := TorrentSpec{}
469 T, new, _ := cl.AddTorrentSpec(&spec)
473 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
474 _, new, _ = cl.AddTorrentSpec(&spec)
476 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
477 // Because trackers are disabled in TestingConfig.
478 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
481 // We read from a piece which is marked completed, but is missing data.
482 func TestCompletedPieceWrongSize(t *testing.T) {
483 cfg := TestingConfig()
484 cfg.DefaultStorage = badStorage{}
485 cl, err := NewClient(cfg)
486 require.NoError(t, err)
488 info := metainfo.Info{
490 Pieces: make([]byte, 20),
491 Files: []metainfo.FileInfo{
492 {Path: []string{"greeting"}, Length: 13},
495 b, err := bencode.Marshal(info)
496 require.NoError(t, err)
497 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
499 InfoHash: metainfo.HashBytes(b),
501 require.NoError(t, err)
506 b, err = ioutil.ReadAll(r)
508 assert.NoError(t, err)
511 func BenchmarkAddLargeTorrent(b *testing.B) {
512 cfg := TestingConfig()
513 cfg.DisableTCP = true
514 cfg.DisableUTP = true
515 cfg.ListenHost = func(string) string { return "redonk" }
516 cl, err := NewClient(cfg)
517 require.NoError(b, err)
519 for range iter.N(b.N) {
520 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
528 func TestResponsive(t *testing.T) {
529 seederDataDir, mi := testutil.GreetingTestTorrent()
530 defer os.RemoveAll(seederDataDir)
531 cfg := TestingConfig()
533 cfg.DataDir = seederDataDir
534 seeder, err := NewClient(cfg)
537 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
538 seederTorrent.VerifyData()
539 leecherDataDir, err := ioutil.TempDir("", "")
541 defer os.RemoveAll(leecherDataDir)
542 cfg = TestingConfig()
543 cfg.DataDir = leecherDataDir
544 leecher, err := NewClient(cfg)
546 defer leecher.Close()
547 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
548 ret = TorrentSpecFromMetaInfo(mi)
552 leecherTorrent.AddClientPeer(seeder)
553 reader := leecherTorrent.NewReader()
555 reader.SetReadahead(0)
556 reader.SetResponsive()
558 _, err = reader.Seek(3, io.SeekStart)
559 require.NoError(t, err)
560 _, err = io.ReadFull(reader, b)
562 assert.EqualValues(t, "lo", string(b))
563 _, err = reader.Seek(11, io.SeekStart)
564 require.NoError(t, err)
565 n, err := io.ReadFull(reader, b)
567 assert.EqualValues(t, 2, n)
568 assert.EqualValues(t, "d\n", string(b))
571 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
572 seederDataDir, mi := testutil.GreetingTestTorrent()
573 defer os.RemoveAll(seederDataDir)
574 cfg := TestingConfig()
576 cfg.DataDir = seederDataDir
577 seeder, err := NewClient(cfg)
580 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
581 seederTorrent.VerifyData()
582 leecherDataDir, err := ioutil.TempDir("", "")
584 defer os.RemoveAll(leecherDataDir)
585 cfg = TestingConfig()
586 cfg.DataDir = leecherDataDir
587 leecher, err := NewClient(cfg)
589 defer leecher.Close()
590 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
591 ret = TorrentSpecFromMetaInfo(mi)
595 leecherTorrent.AddClientPeer(seeder)
596 reader := leecherTorrent.NewReader()
598 reader.SetReadahead(0)
599 reader.SetResponsive()
601 _, err = reader.Seek(3, io.SeekStart)
602 require.NoError(t, err)
603 _, err = io.ReadFull(reader, b)
605 assert.EqualValues(t, "lo", string(b))
606 go leecherTorrent.Drop()
607 _, err = reader.Seek(11, io.SeekStart)
608 require.NoError(t, err)
609 n, err := reader.Read(b)
610 assert.EqualError(t, err, "torrent closed")
611 assert.EqualValues(t, 0, n)
614 func TestDHTInheritBlocklist(t *testing.T) {
615 ipl := iplist.New(nil)
616 require.NotNil(t, ipl)
617 cfg := TestingConfig()
618 cfg.IPBlocklist = ipl
620 cl, err := NewClient(cfg)
621 require.NoError(t, err)
624 cl.eachDhtServer(func(s *dht.Server) {
625 assert.Equal(t, ipl, s.IPBlocklist())
628 assert.EqualValues(t, 2, numServers)
631 // Check that stuff is merged in subsequent AddTorrentSpec for the same
633 func TestAddTorrentSpecMerging(t *testing.T) {
634 cl, err := NewClient(TestingConfig())
635 require.NoError(t, err)
637 dir, mi := testutil.GreetingTestTorrent()
638 defer os.RemoveAll(dir)
639 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
640 InfoHash: mi.HashInfoBytes(),
642 require.NoError(t, err)
644 require.Nil(t, tt.Info())
645 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
646 require.NoError(t, err)
647 require.False(t, new)
648 require.NotNil(t, tt.Info())
651 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
652 dir, mi := testutil.GreetingTestTorrent()
654 cl, _ := NewClient(TestingConfig())
656 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
657 InfoHash: mi.HashInfoBytes(),
660 assert.EqualValues(t, 0, len(cl.Torrents()))
668 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
669 for i := range iter.N(info.NumPieces()) {
671 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
675 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
676 fileCacheDir, err := ioutil.TempDir("", "")
677 require.NoError(t, err)
678 defer os.RemoveAll(fileCacheDir)
679 fileCache, err := filecache.NewCache(fileCacheDir)
680 require.NoError(t, err)
681 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
682 defer os.RemoveAll(greetingDataTempDir)
683 filePieceStore := csf(fileCache)
684 defer filePieceStore.Close()
685 info, err := greetingMetainfo.UnmarshalInfo()
686 require.NoError(t, err)
687 ih := greetingMetainfo.HashInfoBytes()
688 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
689 require.NoError(t, err)
690 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
691 // require.Equal(t, len(testutil.GreetingFileContents), written)
692 // require.NoError(t, err)
693 for i := 0; i < info.NumPieces(); i++ {
695 if alreadyCompleted {
696 require.NoError(t, greetingData.Piece(p).MarkComplete())
699 cfg := TestingConfig()
700 // TODO: Disable network option?
701 cfg.DisableTCP = true
702 cfg.DisableUTP = true
703 cfg.DefaultStorage = filePieceStore
704 cl, err := NewClient(cfg)
705 require.NoError(t, err)
707 tt, err := cl.AddTorrent(greetingMetainfo)
708 require.NoError(t, err)
709 psrs := tt.PieceStateRuns()
710 assert.Len(t, psrs, 1)
711 assert.EqualValues(t, 3, psrs[0].Length)
712 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
713 if alreadyCompleted {
715 b, err := ioutil.ReadAll(r)
716 assert.NoError(t, err)
717 assert.EqualValues(t, testutil.GreetingFileContents, b)
721 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
722 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
725 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
726 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
729 func TestAddMetainfoWithNodes(t *testing.T) {
730 cfg := TestingConfig()
731 cfg.ListenHost = func(string) string { return "" }
733 cfg.DhtStartingNodes = func() ([]dht.Addr, error) { return nil, nil }
734 // For now, we want to just jam the nodes into the table, without
735 // verifying them first. Also the DHT code doesn't support mixing secure
736 // and insecure nodes if security is enabled (yet).
737 // cfg.DHTConfig.NoSecurity = true
738 cl, err := NewClient(cfg)
739 require.NoError(t, err)
741 sum := func() (ret int64) {
742 cl.eachDhtServer(func(s *dht.Server) {
743 ret += s.Stats().OutboundQueriesAttempted
747 assert.EqualValues(t, 0, sum())
748 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
749 require.NoError(t, err)
750 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
751 // check if the announce-list is here instead. TODO: Add nodes.
752 assert.Len(t, tt.metainfo.AnnounceList, 5)
753 // There are 6 nodes in the torrent file.
754 assert.EqualValues(t, 6*len(cl.dhtServers), sum())
757 type testDownloadCancelParams struct {
758 SetLeecherStorageCapacity bool
759 LeecherStorageCapacity int64
763 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
764 greetingTempDir, mi := testutil.GreetingTestTorrent()
765 defer os.RemoveAll(greetingTempDir)
766 cfg := TestingConfig()
768 cfg.DataDir = greetingTempDir
769 seeder, err := NewClient(cfg)
770 require.NoError(t, err)
772 defer testutil.ExportStatusWriter(seeder, "s")()
773 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
774 seederTorrent.VerifyData()
775 leecherDataDir, err := ioutil.TempDir("", "")
776 require.NoError(t, err)
777 defer os.RemoveAll(leecherDataDir)
778 fc, err := filecache.NewCache(leecherDataDir)
779 require.NoError(t, err)
780 if ps.SetLeecherStorageCapacity {
781 fc.SetCapacity(ps.LeecherStorageCapacity)
783 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
784 cfg.DataDir = leecherDataDir
785 leecher, _ := NewClient(cfg)
786 defer leecher.Close()
787 defer testutil.ExportStatusWriter(leecher, "l")()
788 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
789 ret = TorrentSpecFromMetaInfo(mi)
793 require.NoError(t, err)
795 psc := leecherGreeting.SubscribePieceStateChanges()
798 leecherGreeting.cl.mu.Lock()
799 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
801 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
803 leecherGreeting.cl.mu.Unlock()
804 done := make(chan struct{})
806 go leecherGreeting.AddClientPeer(seeder)
807 completes := make(map[int]bool, 3)
808 expected := func() map[int]bool {
810 return map[int]bool{0: false, 1: false, 2: false}
812 return map[int]bool{0: true, 1: true, 2: true}
815 for !reflect.DeepEqual(completes, expected) {
817 case _v := <-psc.Values:
818 v := _v.(PieceStateChange)
819 completes[v.Index] = v.Complete
824 func TestTorrentDownloadAll(t *testing.T) {
825 testDownloadCancel(t, testDownloadCancelParams{})
828 func TestTorrentDownloadAllThenCancel(t *testing.T) {
829 testDownloadCancel(t, testDownloadCancelParams{
834 // Ensure that it's an error for a peer to send an invalid have message.
835 func TestPeerInvalidHave(t *testing.T) {
836 cl, err := NewClient(TestingConfig())
837 require.NoError(t, err)
839 info := metainfo.Info{
841 Pieces: make([]byte, 20),
842 Files: []metainfo.FileInfo{{Length: 1}},
844 infoBytes, err := bencode.Marshal(info)
845 require.NoError(t, err)
846 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
847 InfoBytes: infoBytes,
848 InfoHash: metainfo.HashBytes(infoBytes),
849 Storage: badStorage{},
851 require.NoError(t, err)
857 assert.NoError(t, cn.peerSentHave(0))
858 assert.Error(t, cn.peerSentHave(1))
861 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
862 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
863 defer os.RemoveAll(greetingTempDir)
864 cfg := TestingConfig()
865 cfg.DataDir = greetingTempDir
866 seeder, err := NewClient(TestingConfig())
867 require.NoError(t, err)
868 seeder.AddTorrentSpec(&TorrentSpec{
869 InfoBytes: greetingMetainfo.InfoBytes,
873 // Check that when the listen port is 0, all the protocols listened on have
874 // the same port, and it isn't zero.
875 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
876 cl, err := NewClient(TestingConfig())
877 require.NoError(t, err)
879 port := cl.LocalPort()
880 assert.NotEqual(t, 0, port)
881 cl.eachListener(func(s socket) bool {
882 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
887 func TestClientDynamicListenTCPOnly(t *testing.T) {
888 cfg := TestingConfig()
889 cfg.DisableUTP = true
890 cl, err := NewClient(cfg)
891 require.NoError(t, err)
893 assert.NotEqual(t, 0, cl.LocalPort())
894 cl.eachListener(func(s socket) bool {
895 assert.True(t, isTcpNetwork(s.Addr().Network()))
900 func TestClientDynamicListenUTPOnly(t *testing.T) {
901 cfg := TestingConfig()
902 cfg.DisableTCP = true
903 cl, err := NewClient(cfg)
904 require.NoError(t, err)
906 assert.NotEqual(t, 0, cl.LocalPort())
907 cl.eachListener(func(s socket) bool {
908 assert.True(t, isUtpNetwork(s.Addr().Network()))
913 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
914 cfg := TestingConfig()
915 cfg.DisableTCP = true
916 cfg.DisableUTP = true
917 cl, err := NewClient(cfg)
918 require.NoError(t, err)
920 assert.Equal(t, 0, cl.LocalPort())
923 func totalConns(tts []*Torrent) (ret int) {
924 for _, tt := range tts {
932 func TestSetMaxEstablishedConn(t *testing.T) {
934 ih := testutil.GreetingMetaInfo().HashInfoBytes()
935 cfg := TestingConfig()
936 cfg.DisableAcceptRateLimiting = true
937 cfg.dropDuplicatePeerIds = true
938 for i := range iter.N(3) {
939 cl, err := NewClient(cfg)
940 require.NoError(t, err)
942 tt, _ := cl.AddTorrentInfoHash(ih)
943 tt.SetMaxEstablishedConns(2)
944 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))()
945 tts = append(tts, tt)
948 for _, tt := range tts {
949 for _, _tt := range tts {
951 tt.AddClientPeer(_tt.cl)
956 waitTotalConns := func(num int) {
957 for totalConns(tts) != num {
959 time.Sleep(time.Millisecond)
964 tts[0].SetMaxEstablishedConns(1)
966 tts[0].SetMaxEstablishedConns(0)
968 tts[0].SetMaxEstablishedConns(1)
971 tts[0].SetMaxEstablishedConns(2)
976 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
977 os.MkdirAll(dir, 0770)
978 file, err := os.Create(filepath.Join(dir, name))
979 require.NoError(t, err)
980 file.Write([]byte(name))
982 mi := metainfo.MetaInfo{}
984 info := metainfo.Info{PieceLength: 256 * 1024}
985 err = info.BuildFromFilePath(filepath.Join(dir, name))
986 require.NoError(t, err)
987 mi.InfoBytes, err = bencode.Marshal(info)
988 require.NoError(t, err)
989 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
990 tr, err := cl.AddTorrent(&mi)
991 require.NoError(t, err)
992 require.True(t, tr.Seeding())
997 // https://github.com/anacrolix/torrent/issues/114
998 func TestMultipleTorrentsWithEncryption(t *testing.T) {
999 cfg := TestingConfig()
1000 cfg.DisableUTP = true
1002 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1003 cfg.ForceEncryption = true
1004 os.Mkdir(cfg.DataDir, 0755)
1005 server, err := NewClient(cfg)
1006 require.NoError(t, err)
1007 defer server.Close()
1008 defer testutil.ExportStatusWriter(server, "s")()
1009 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1010 makeMagnet(t, server, cfg.DataDir, "test2")
1011 cfg = TestingConfig()
1012 cfg.DisableUTP = true
1013 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1014 cfg.ForceEncryption = true
1015 client, err := NewClient(cfg)
1016 require.NoError(t, err)
1017 defer client.Close()
1018 defer testutil.ExportStatusWriter(client, "c")()
1019 tr, err := client.AddMagnet(magnet1)
1020 require.NoError(t, err)
1021 tr.AddClientPeer(server)
1027 func TestClientAddressInUse(t *testing.T) {
1028 s, _ := NewUtpSocket("udp", ":50007")
1032 cfg := TestingConfig().SetListenAddr(":50007")
1033 cl, err := NewClient(cfg)
1034 require.Error(t, err)