15 _ "github.com/anacrolix/envpprof"
16 "github.com/anacrolix/missinggo"
17 "github.com/anacrolix/missinggo/filecache"
18 "github.com/bradfitz/iter"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
21 "golang.org/x/time/rate"
23 "github.com/anacrolix/torrent/bencode"
24 "github.com/anacrolix/torrent/internal/testutil"
25 "github.com/anacrolix/torrent/iplist"
26 "github.com/anacrolix/torrent/metainfo"
27 "github.com/anacrolix/torrent/storage"
30 func TestingConfig() *Config {
32 ListenAddr: "localhost:0",
35 DisableTrackers: true,
36 NoDefaultPortForwarding: true,
41 func TestClientDefault(t *testing.T) {
42 cl, err := NewClient(TestingConfig())
43 require.NoError(t, err)
47 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
48 cfg := TestingConfig()
49 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
50 require.NoError(t, err)
51 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
53 cfg.DefaultStorage = ci
54 cl, err := NewClient(cfg)
55 require.NoError(t, err)
57 // And again, https://github.com/anacrolix/torrent/issues/158
58 cl, err = NewClient(cfg)
59 require.NoError(t, err)
63 func TestAddDropTorrent(t *testing.T) {
64 cl, err := NewClient(TestingConfig())
65 require.NoError(t, err)
67 dir, mi := testutil.GreetingTestTorrent()
68 defer os.RemoveAll(dir)
69 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
70 require.NoError(t, err)
72 tt.SetMaxEstablishedConns(0)
73 tt.SetMaxEstablishedConns(1)
77 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
82 func TestAddTorrentNoUsableURLs(t *testing.T) {
87 func TestAddPeersToUnknownTorrent(t *testing.T) {
92 func TestPieceHashSize(t *testing.T) {
93 assert.Equal(t, 20, pieceHash.Size())
96 func TestTorrentInitialState(t *testing.T) {
97 dir, mi := testutil.GreetingTestTorrent()
98 defer os.RemoveAll(dir)
101 tor := cl.newTorrent(
103 storage.NewFileWithCompletion(tempDir(), storage.NewMapPieceCompletion()),
107 err := tor.setInfoBytes(mi.InfoBytes)
109 require.NoError(t, err)
110 require.Len(t, tor.pieces, 3)
111 tor.pendAllChunkSpecs(0)
113 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
115 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
118 func TestUnmarshalPEXMsg(t *testing.T) {
119 var m peerExchangeMessage
120 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
123 if len(m.Added) != 2 {
126 if m.Added[0].Port != 0x506 {
131 func TestReducedDialTimeout(t *testing.T) {
134 for _, _case := range []struct {
138 ExpectedReduced time.Duration
140 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
141 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
142 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
143 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
144 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
145 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
147 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
148 expected := _case.ExpectedReduced
149 if expected < cfg.MinDialTimeout {
150 expected = cfg.MinDialTimeout
152 if reduced != expected {
153 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
158 func TestUTPRawConn(t *testing.T) {
159 l, err := NewUtpSocket("udp", "")
160 require.NoError(t, err)
170 // Connect a UTP peer to see if the RawConn will still work.
171 s, err := NewUtpSocket("udp", "")
172 require.NoError(t, err)
174 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
175 require.NoError(t, err)
176 defer utpPeer.Close()
177 peer, err := net.ListenPacket("udp", ":0")
178 require.NoError(t, err)
182 // How many messages to send. I've set this to double the channel buffer
183 // size in the raw packetConn.
185 readerStopped := make(chan struct{})
186 // The reader goroutine.
188 defer close(readerStopped)
189 b := make([]byte, 500)
190 for i := 0; i < N; i++ {
191 n, _, err := l.ReadFrom(b)
192 require.NoError(t, err)
195 fmt.Sscan(string(b[:n]), &d)
196 assert.Equal(t, i, d)
199 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
200 require.NoError(t, err)
201 for i := 0; i < N; i++ {
202 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
203 require.NoError(t, err)
204 time.Sleep(time.Millisecond)
207 case <-readerStopped:
208 case <-time.After(time.Second):
209 t.Fatal("reader timed out")
211 if msgsReceived != N {
212 t.Fatalf("messages received: %d", msgsReceived)
216 func TestTwoClientsArbitraryPorts(t *testing.T) {
217 for i := 0; i < 2; i++ {
218 cl, err := NewClient(TestingConfig())
226 func TestAddDropManyTorrents(t *testing.T) {
227 cl, err := NewClient(TestingConfig())
228 require.NoError(t, err)
230 for i := range iter.N(1000) {
232 binary.PutVarint(spec.InfoHash[:], int64(i))
233 tt, new, err := cl.AddTorrentSpec(&spec)
234 assert.NoError(t, err)
240 type FileCacheClientStorageFactoryParams struct {
243 Wrapper func(*filecache.Cache) storage.ClientImpl
246 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
247 return func(dataDir string) storage.ClientImpl {
248 fc, err := filecache.NewCache(dataDir)
253 fc.SetCapacity(ps.Capacity)
255 return ps.Wrapper(fc)
259 type storageFactory func(string) storage.ClientImpl
261 func TestClientTransferDefault(t *testing.T) {
262 testClientTransfer(t, testClientTransferParams{
263 ExportClientStatus: true,
264 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
265 Wrapper: fileCachePieceResourceStorage,
270 func TestClientTransferRateLimitedUpload(t *testing.T) {
271 started := time.Now()
272 testClientTransfer(t, testClientTransferParams{
273 // We are uploading 13 bytes (the length of the greeting torrent). The
274 // chunks are 2 bytes in length. Then the smallest burst we can run
275 // with is 2. Time taken is (13-burst)/rate.
276 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
277 ExportClientStatus: true,
279 require.True(t, time.Since(started) > time.Second)
282 func TestClientTransferRateLimitedDownload(t *testing.T) {
283 testClientTransfer(t, testClientTransferParams{
284 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
288 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
289 return storage.NewResourcePieces(fc.AsResourceProvider())
292 func TestClientTransferSmallCache(t *testing.T) {
293 testClientTransfer(t, testClientTransferParams{
294 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
296 // Going below the piece length means it can't complete a piece so
297 // that it can be hashed.
299 Wrapper: fileCachePieceResourceStorage,
302 // Can't readahead too far or the cache will thrash and drop data we
305 ExportClientStatus: true,
309 func TestClientTransferVarious(t *testing.T) {
311 for _, ls := range []storageFactory{
312 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
313 Wrapper: fileCachePieceResourceStorage,
318 for _, ss := range []func(string) storage.ClientImpl{
322 for _, responsive := range []bool{false, true} {
323 testClientTransfer(t, testClientTransferParams{
324 Responsive: responsive,
328 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
329 testClientTransfer(t, testClientTransferParams{
331 Responsive: responsive,
333 Readahead: readahead,
342 type testClientTransferParams struct {
346 ExportClientStatus bool
347 LeecherStorage func(string) storage.ClientImpl
348 SeederStorage func(string) storage.ClientImpl
349 SeederUploadRateLimiter *rate.Limiter
350 LeecherDownloadRateLimiter *rate.Limiter
353 // Creates a seeder and a leecher, and ensures the data transfers when a read
354 // is attempted on the leecher.
355 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
356 greetingTempDir, mi := testutil.GreetingTestTorrent()
357 defer os.RemoveAll(greetingTempDir)
358 // Create seeder and a Torrent.
359 cfg := TestingConfig()
361 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
362 // cfg.ListenAddr = "localhost:4000"
363 if ps.SeederStorage != nil {
364 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
365 defer cfg.DefaultStorage.Close()
367 cfg.DataDir = greetingTempDir
369 seeder, err := NewClient(cfg)
370 require.NoError(t, err)
371 if ps.ExportClientStatus {
372 testutil.ExportStatusWriter(seeder, "s")
374 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
375 // Run a Stats right after Closing the Client. This will trigger the Stats
376 // panic in #214 caused by RemoteAddr on Closed uTP sockets.
377 defer seederTorrent.Stats()
379 seederTorrent.VerifyData()
380 // Create leecher and a Torrent.
381 leecherDataDir, err := ioutil.TempDir("", "")
382 require.NoError(t, err)
383 defer os.RemoveAll(leecherDataDir)
384 if ps.LeecherStorage == nil {
385 cfg.DataDir = leecherDataDir
387 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
389 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
391 leecher, err := NewClient(cfg)
392 require.NoError(t, err)
393 defer leecher.Close()
394 if ps.ExportClientStatus {
395 testutil.ExportStatusWriter(leecher, "l")
397 leecherTorrent, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
398 ret = TorrentSpecFromMetaInfo(mi)
402 require.NoError(t, err)
404 // Now do some things with leecher and seeder.
405 addClientPeer(leecherTorrent, seeder)
406 // The Torrent should not be interested in obtaining peers, so the one we
407 // just added should be the only one.
408 assert.False(t, leecherTorrent.Seeding())
409 assert.EqualValues(t, 1, leecherTorrent.Stats().PendingPeers)
410 r := leecherTorrent.NewReader()
416 r.SetReadahead(ps.Readahead)
418 assertReadAllGreeting(t, r)
419 assert.True(t, 13 <= seederTorrent.Stats().DataBytesWritten)
420 assert.True(t, 8 <= seederTorrent.Stats().ChunksWritten)
421 assert.True(t, 13 <= leecherTorrent.Stats().DataBytesRead)
422 assert.True(t, 8 <= leecherTorrent.Stats().ChunksRead)
423 // Try reading through again for the cases where the torrent data size
424 // exceeds the size of the cache.
425 assertReadAllGreeting(t, r)
428 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
429 pos, err := r.Seek(0, io.SeekStart)
430 assert.NoError(t, err)
431 assert.EqualValues(t, 0, pos)
432 _greeting, err := ioutil.ReadAll(r)
433 assert.NoError(t, err)
434 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
437 // Check that after completing leeching, a leecher transitions to a seeding
438 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
439 func TestSeedAfterDownloading(t *testing.T) {
440 greetingTempDir, mi := testutil.GreetingTestTorrent()
441 defer os.RemoveAll(greetingTempDir)
442 cfg := TestingConfig()
444 cfg.DataDir = greetingTempDir
445 seeder, err := NewClient(cfg)
446 require.NoError(t, err)
448 testutil.ExportStatusWriter(seeder, "s")
449 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
450 seederTorrent.VerifyData()
451 cfg.DataDir, err = ioutil.TempDir("", "")
452 require.NoError(t, err)
453 defer os.RemoveAll(cfg.DataDir)
454 leecher, err := NewClient(cfg)
455 require.NoError(t, err)
456 defer leecher.Close()
457 testutil.ExportStatusWriter(leecher, "l")
459 cfg.DataDir, err = ioutil.TempDir("", "")
460 require.NoError(t, err)
461 defer os.RemoveAll(cfg.DataDir)
462 leecherLeecher, _ := NewClient(cfg)
463 defer leecherLeecher.Close()
464 testutil.ExportStatusWriter(leecherLeecher, "ll")
465 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
466 ret = TorrentSpecFromMetaInfo(mi)
470 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
471 ret = TorrentSpecFromMetaInfo(mi)
475 // Simultaneously DownloadAll in Leecher, and read the contents
476 // consecutively in LeecherLeecher. This non-deterministically triggered a
477 // case where the leecher wouldn't unchoke the LeecherLeecher.
478 var wg sync.WaitGroup
484 b, err := ioutil.ReadAll(r)
485 require.NoError(t, err)
486 assert.EqualValues(t, testutil.GreetingFileContents, b)
488 addClientPeer(leecherGreeting, seeder)
489 addClientPeer(leecherGreeting, leecherLeecher)
493 leecherGreeting.DownloadAll()
499 func TestMergingTrackersByAddingSpecs(t *testing.T) {
500 cl, err := NewClient(TestingConfig())
501 require.NoError(t, err)
503 spec := TorrentSpec{}
504 T, new, _ := cl.AddTorrentSpec(&spec)
508 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
509 _, new, _ = cl.AddTorrentSpec(&spec)
511 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
512 // Because trackers are disabled in TestingConfig.
513 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
516 // We read from a piece which is marked completed, but is missing data.
517 func TestCompletedPieceWrongSize(t *testing.T) {
518 cfg := TestingConfig()
519 cfg.DefaultStorage = badStorage{}
520 cl, err := NewClient(cfg)
521 require.NoError(t, err)
523 info := metainfo.Info{
525 Pieces: make([]byte, 20),
526 Files: []metainfo.FileInfo{
527 {Path: []string{"greeting"}, Length: 13},
530 b, err := bencode.Marshal(info)
531 require.NoError(t, err)
532 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
534 InfoHash: metainfo.HashBytes(b),
536 require.NoError(t, err)
541 b, err = ioutil.ReadAll(r)
543 assert.NoError(t, err)
546 func BenchmarkAddLargeTorrent(b *testing.B) {
547 cfg := TestingConfig()
548 cfg.DisableTCP = true
549 cfg.DisableUTP = true
550 cfg.ListenAddr = "redonk"
551 cl, err := NewClient(cfg)
552 require.NoError(b, err)
554 for range iter.N(b.N) {
555 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
563 func TestResponsive(t *testing.T) {
564 seederDataDir, mi := testutil.GreetingTestTorrent()
565 defer os.RemoveAll(seederDataDir)
566 cfg := TestingConfig()
568 cfg.DataDir = seederDataDir
569 seeder, err := NewClient(cfg)
572 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
573 seederTorrent.VerifyData()
574 leecherDataDir, err := ioutil.TempDir("", "")
576 defer os.RemoveAll(leecherDataDir)
577 cfg = TestingConfig()
578 cfg.DataDir = leecherDataDir
579 leecher, err := NewClient(cfg)
581 defer leecher.Close()
582 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
583 ret = TorrentSpecFromMetaInfo(mi)
587 addClientPeer(leecherTorrent, seeder)
588 reader := leecherTorrent.NewReader()
590 reader.SetReadahead(0)
591 reader.SetResponsive()
593 _, err = reader.Seek(3, io.SeekStart)
594 require.NoError(t, err)
595 _, err = io.ReadFull(reader, b)
597 assert.EqualValues(t, "lo", string(b))
598 _, err = reader.Seek(11, io.SeekStart)
599 require.NoError(t, err)
600 n, err := io.ReadFull(reader, b)
602 assert.EqualValues(t, 2, n)
603 assert.EqualValues(t, "d\n", string(b))
606 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
607 seederDataDir, mi := testutil.GreetingTestTorrent()
608 defer os.RemoveAll(seederDataDir)
609 cfg := TestingConfig()
611 cfg.DataDir = seederDataDir
612 seeder, err := NewClient(cfg)
615 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
616 seederTorrent.VerifyData()
617 leecherDataDir, err := ioutil.TempDir("", "")
619 defer os.RemoveAll(leecherDataDir)
620 cfg = TestingConfig()
621 cfg.DataDir = leecherDataDir
622 leecher, err := NewClient(cfg)
624 defer leecher.Close()
625 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
626 ret = TorrentSpecFromMetaInfo(mi)
630 addClientPeer(leecherTorrent, seeder)
631 reader := leecherTorrent.NewReader()
633 reader.SetReadahead(0)
634 reader.SetResponsive()
636 _, err = reader.Seek(3, io.SeekStart)
637 require.NoError(t, err)
638 _, err = io.ReadFull(reader, b)
640 assert.EqualValues(t, "lo", string(b))
641 go leecherTorrent.Drop()
642 _, err = reader.Seek(11, io.SeekStart)
643 require.NoError(t, err)
644 n, err := reader.Read(b)
645 assert.EqualError(t, err, "torrent closed")
646 assert.EqualValues(t, 0, n)
649 func TestDHTInheritBlocklist(t *testing.T) {
650 ipl := iplist.New(nil)
651 require.NotNil(t, ipl)
652 cfg := TestingConfig()
653 cfg.IPBlocklist = ipl
655 cl, err := NewClient(cfg)
656 require.NoError(t, err)
658 require.Equal(t, ipl, cl.DHT().IPBlocklist())
661 // Check that stuff is merged in subsequent AddTorrentSpec for the same
663 func TestAddTorrentSpecMerging(t *testing.T) {
664 cl, err := NewClient(TestingConfig())
665 require.NoError(t, err)
667 dir, mi := testutil.GreetingTestTorrent()
668 defer os.RemoveAll(dir)
669 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
670 InfoHash: mi.HashInfoBytes(),
672 require.NoError(t, err)
674 require.Nil(t, tt.Info())
675 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
676 require.NoError(t, err)
677 require.False(t, new)
678 require.NotNil(t, tt.Info())
681 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
682 dir, mi := testutil.GreetingTestTorrent()
684 cl, _ := NewClient(TestingConfig())
686 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
687 InfoHash: mi.HashInfoBytes(),
690 assert.EqualValues(t, 0, len(cl.Torrents()))
698 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
699 for i := range iter.N(info.NumPieces()) {
701 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
705 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
706 fileCacheDir, err := ioutil.TempDir("", "")
707 require.NoError(t, err)
708 defer os.RemoveAll(fileCacheDir)
709 fileCache, err := filecache.NewCache(fileCacheDir)
710 require.NoError(t, err)
711 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
712 defer os.RemoveAll(greetingDataTempDir)
713 filePieceStore := csf(fileCache)
714 defer filePieceStore.Close()
715 info, err := greetingMetainfo.UnmarshalInfo()
716 require.NoError(t, err)
717 ih := greetingMetainfo.HashInfoBytes()
718 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
719 require.NoError(t, err)
720 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
721 // require.Equal(t, len(testutil.GreetingFileContents), written)
722 // require.NoError(t, err)
723 for i := 0; i < info.NumPieces(); i++ {
725 if alreadyCompleted {
726 require.NoError(t, greetingData.Piece(p).MarkComplete())
729 cfg := TestingConfig()
730 // TODO: Disable network option?
731 cfg.DisableTCP = true
732 cfg.DisableUTP = true
733 cfg.DefaultStorage = filePieceStore
734 cl, err := NewClient(cfg)
735 require.NoError(t, err)
737 tt, err := cl.AddTorrent(greetingMetainfo)
738 require.NoError(t, err)
739 psrs := tt.PieceStateRuns()
740 assert.Len(t, psrs, 1)
741 assert.EqualValues(t, 3, psrs[0].Length)
742 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
743 if alreadyCompleted {
745 b, err := ioutil.ReadAll(r)
746 assert.NoError(t, err)
747 assert.EqualValues(t, testutil.GreetingFileContents, b)
751 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
752 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
755 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
756 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
759 func TestAddMetainfoWithNodes(t *testing.T) {
760 cfg := TestingConfig()
761 cfg.ListenAddr = ":0"
763 // For now, we want to just jam the nodes into the table, without
764 // verifying them first. Also the DHT code doesn't support mixing secure
765 // and insecure nodes if security is enabled (yet).
766 cfg.DHTConfig.NoSecurity = true
767 cl, err := NewClient(cfg)
768 require.NoError(t, err)
770 assert.EqualValues(t, 0, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
771 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
772 require.NoError(t, err)
773 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
774 // check if the announce-list is here instead. TODO: Add nodes.
775 assert.Len(t, tt.metainfo.AnnounceList, 5)
776 // There are 6 nodes in the torrent file.
777 assert.EqualValues(t, 6, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
780 type testDownloadCancelParams struct {
781 ExportClientStatus bool
782 SetLeecherStorageCapacity bool
783 LeecherStorageCapacity int64
787 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
788 greetingTempDir, mi := testutil.GreetingTestTorrent()
789 defer os.RemoveAll(greetingTempDir)
790 cfg := TestingConfig()
792 cfg.DataDir = greetingTempDir
793 seeder, err := NewClient(cfg)
794 require.NoError(t, err)
796 if ps.ExportClientStatus {
797 testutil.ExportStatusWriter(seeder, "s")
799 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
800 seederTorrent.VerifyData()
801 leecherDataDir, err := ioutil.TempDir("", "")
802 require.NoError(t, err)
803 defer os.RemoveAll(leecherDataDir)
804 fc, err := filecache.NewCache(leecherDataDir)
805 require.NoError(t, err)
806 if ps.SetLeecherStorageCapacity {
807 fc.SetCapacity(ps.LeecherStorageCapacity)
809 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
810 cfg.DataDir = leecherDataDir
811 leecher, _ := NewClient(cfg)
812 defer leecher.Close()
813 if ps.ExportClientStatus {
814 testutil.ExportStatusWriter(leecher, "l")
816 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
817 ret = TorrentSpecFromMetaInfo(mi)
821 require.NoError(t, err)
823 psc := leecherGreeting.SubscribePieceStateChanges()
826 leecherGreeting.cl.mu.Lock()
827 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
829 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
831 leecherGreeting.cl.mu.Unlock()
833 addClientPeer(leecherGreeting, seeder)
834 completes := make(map[int]bool, 3)
837 // started := time.Now()
839 case _v := <-psc.Values:
840 // log.Print(time.Since(started))
841 v := _v.(PieceStateChange)
842 completes[v.Index] = v.Complete
843 case <-time.After(100 * time.Millisecond):
848 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
850 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
855 func TestTorrentDownloadAll(t *testing.T) {
856 testDownloadCancel(t, testDownloadCancelParams{})
859 func TestTorrentDownloadAllThenCancel(t *testing.T) {
860 testDownloadCancel(t, testDownloadCancelParams{
865 // Ensure that it's an error for a peer to send an invalid have message.
866 func TestPeerInvalidHave(t *testing.T) {
867 cl, err := NewClient(TestingConfig())
868 require.NoError(t, err)
870 info := metainfo.Info{
872 Pieces: make([]byte, 20),
873 Files: []metainfo.FileInfo{{Length: 1}},
875 infoBytes, err := bencode.Marshal(info)
876 require.NoError(t, err)
877 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
878 InfoBytes: infoBytes,
879 InfoHash: metainfo.HashBytes(infoBytes),
880 Storage: badStorage{},
882 require.NoError(t, err)
888 assert.NoError(t, cn.peerSentHave(0))
889 assert.Error(t, cn.peerSentHave(1))
892 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
893 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
894 defer os.RemoveAll(greetingTempDir)
895 cfg := TestingConfig()
896 cfg.DataDir = greetingTempDir
897 seeder, err := NewClient(TestingConfig())
898 require.NoError(t, err)
899 seeder.AddTorrentSpec(&TorrentSpec{
900 InfoBytes: greetingMetainfo.InfoBytes,
904 func TestPrepareTrackerAnnounce(t *testing.T) {
906 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
907 require.NoError(t, err)
908 assert.False(t, blocked)
909 assert.EqualValues(t, "localhost:1234", host)
910 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
913 // Check that when the listen port is 0, all the protocols listened on have
914 // the same port, and it isn't zero.
915 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
916 cl, err := NewClient(TestingConfig())
917 require.NoError(t, err)
919 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
920 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
923 func TestClientDynamicListenTCPOnly(t *testing.T) {
924 cfg := TestingConfig()
925 cfg.DisableUTP = true
926 cl, err := NewClient(cfg)
927 require.NoError(t, err)
929 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
930 assert.Nil(t, cl.utpSock)
933 func TestClientDynamicListenUTPOnly(t *testing.T) {
934 cfg := TestingConfig()
935 cfg.DisableTCP = true
936 cl, err := NewClient(cfg)
937 require.NoError(t, err)
939 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
940 assert.Nil(t, cl.tcpListener)
943 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
944 cfg := TestingConfig()
945 cfg.DisableTCP = true
946 cfg.DisableUTP = true
947 cl, err := NewClient(cfg)
948 require.NoError(t, err)
950 assert.Nil(t, cl.ListenAddr())
953 func addClientPeer(t *Torrent, cl *Client) {
956 IP: missinggo.AddrIP(cl.ListenAddr()),
957 Port: missinggo.AddrPort(cl.ListenAddr()),
962 func totalConns(tts []*Torrent) (ret int) {
963 for _, tt := range tts {
971 func TestSetMaxEstablishedConn(t *testing.T) {
973 ih := testutil.GreetingMetaInfo().HashInfoBytes()
974 for i := range iter.N(3) {
975 cl, err := NewClient(TestingConfig())
976 require.NoError(t, err)
978 tt, _ := cl.AddTorrentInfoHash(ih)
979 tt.SetMaxEstablishedConns(2)
980 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
981 tts = append(tts, tt)
984 for i, tt := range tts {
985 for _, _tt := range tts[:i] {
986 addClientPeer(tt, _tt.cl)
990 waitTotalConns := func(num int) {
991 for totalConns(tts) != num {
992 time.Sleep(time.Millisecond)
997 tts[0].SetMaxEstablishedConns(1)
999 tts[0].SetMaxEstablishedConns(0)
1001 tts[0].SetMaxEstablishedConns(1)
1004 tts[0].SetMaxEstablishedConns(2)
1009 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1010 os.MkdirAll(dir, 0770)
1011 file, err := os.Create(filepath.Join(dir, name))
1012 require.NoError(t, err)
1013 file.Write([]byte(name))
1015 mi := metainfo.MetaInfo{}
1017 info := metainfo.Info{PieceLength: 256 * 1024}
1018 err = info.BuildFromFilePath(filepath.Join(dir, name))
1019 require.NoError(t, err)
1020 mi.InfoBytes, err = bencode.Marshal(info)
1021 require.NoError(t, err)
1022 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1023 tr, err := cl.AddTorrent(&mi)
1024 require.NoError(t, err)
1025 require.True(t, tr.Seeding())
1030 // https://github.com/anacrolix/torrent/issues/114
1031 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1032 cfg := TestingConfig()
1033 cfg.DisableUTP = true
1035 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1036 cfg.ForceEncryption = true
1037 os.Mkdir(cfg.DataDir, 0755)
1038 server, err := NewClient(cfg)
1039 require.NoError(t, err)
1040 defer server.Close()
1041 testutil.ExportStatusWriter(server, "s")
1042 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1043 makeMagnet(t, server, cfg.DataDir, "test2")
1044 cfg = TestingConfig()
1045 cfg.DisableUTP = true
1046 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1047 cfg.ForceEncryption = true
1048 client, err := NewClient(cfg)
1049 require.NoError(t, err)
1050 defer client.Close()
1051 testutil.ExportStatusWriter(client, "c")
1052 tr, err := client.AddMagnet(magnet1)
1053 require.NoError(t, err)
1054 tr.AddPeers([]Peer{{
1055 IP: missinggo.AddrIP(server.ListenAddr()),
1056 Port: missinggo.AddrPort(server.ListenAddr()),
1063 func TestClientAddressInUse(t *testing.T) {
1064 s, _ := NewUtpSocket("udp", ":50007")
1068 cfg := TestingConfig()
1069 cfg.ListenAddr = ":50007"
1070 cl, err := NewClient(cfg)
1071 require.Error(t, err)