19 _ "github.com/anacrolix/envpprof"
20 "github.com/anacrolix/missinggo"
21 "github.com/anacrolix/missinggo/filecache"
22 "github.com/anacrolix/missinggo/pubsub"
23 "github.com/anacrolix/utp"
24 "github.com/bradfitz/iter"
25 "github.com/stretchr/testify/assert"
26 "github.com/stretchr/testify/require"
27 "golang.org/x/time/rate"
29 "github.com/anacrolix/torrent/bencode"
30 "github.com/anacrolix/torrent/dht"
31 "github.com/anacrolix/torrent/internal/testutil"
32 "github.com/anacrolix/torrent/iplist"
33 "github.com/anacrolix/torrent/metainfo"
34 "github.com/anacrolix/torrent/storage"
38 log.SetFlags(log.LstdFlags | log.Llongfile)
41 var TestingConfig = Config{
42 ListenAddr: "localhost:0",
44 DisableTrackers: true,
45 DataDir: "/tmp/anacrolix",
46 DHTConfig: dht.ServerConfig{
47 NoDefaultBootstrap: true,
52 func TestClientDefault(t *testing.T) {
53 cl, err := NewClient(&TestingConfig)
54 require.NoError(t, err)
58 func TestAddDropTorrent(t *testing.T) {
59 cl, err := NewClient(&TestingConfig)
60 require.NoError(t, err)
62 dir, mi := testutil.GreetingTestTorrent()
63 defer os.RemoveAll(dir)
64 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
65 require.NoError(t, err)
67 tt.SetMaxEstablishedConns(0)
68 tt.SetMaxEstablishedConns(1)
72 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
76 func TestAddTorrentNoUsableURLs(t *testing.T) {
80 func TestAddPeersToUnknownTorrent(t *testing.T) {
84 func TestPieceHashSize(t *testing.T) {
85 if pieceHash.Size() != 20 {
90 func TestTorrentInitialState(t *testing.T) {
91 dir, mi := testutil.GreetingTestTorrent()
92 defer os.RemoveAll(dir)
94 infoHash: mi.HashInfoBytes(),
95 pieceStateChanges: pubsub.NewPubSub(),
98 tor.storageOpener = storage.NewClient(storage.NewFile("/dev/null"))
99 // Needed to lock for asynchronous piece verification.
101 err := tor.setInfoBytes(mi.InfoBytes)
102 require.NoError(t, err)
103 require.Len(t, tor.pieces, 3)
104 tor.pendAllChunkSpecs(0)
106 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
108 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
111 func TestUnmarshalPEXMsg(t *testing.T) {
112 var m peerExchangeMessage
113 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
116 if len(m.Added) != 2 {
119 if m.Added[0].Port != 0x506 {
124 func TestReducedDialTimeout(t *testing.T) {
125 for _, _case := range []struct {
129 ExpectedReduced time.Duration
131 {nominalDialTimeout, 40, 0, nominalDialTimeout},
132 {nominalDialTimeout, 40, 1, nominalDialTimeout},
133 {nominalDialTimeout, 40, 39, nominalDialTimeout},
134 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
135 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
136 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
138 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
139 expected := _case.ExpectedReduced
140 if expected < minDialTimeout {
141 expected = minDialTimeout
143 if reduced != expected {
144 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
149 func TestUTPRawConn(t *testing.T) {
150 l, err := utp.NewSocket("udp", "")
163 // Connect a UTP peer to see if the RawConn will still work.
164 s, _ := utp.NewSocket("udp", "")
166 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
168 t.Fatalf("error dialing utp listener: %s", err)
170 defer utpPeer.Close()
171 peer, err := net.ListenPacket("udp", ":0")
178 // How many messages to send. I've set this to double the channel buffer
179 // size in the raw packetConn.
181 readerStopped := make(chan struct{})
182 // The reader goroutine.
184 defer close(readerStopped)
185 b := make([]byte, 500)
186 for i := 0; i < N; i++ {
187 n, _, err := l.ReadFrom(b)
189 t.Fatalf("error reading from raw conn: %s", err)
193 fmt.Sscan(string(b[:n]), &d)
195 log.Printf("got wrong number: expected %d, got %d", i, d)
199 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
203 for i := 0; i < N; i++ {
204 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
208 time.Sleep(time.Microsecond)
211 case <-readerStopped:
212 case <-time.After(time.Second):
213 t.Fatal("reader timed out")
215 if msgsReceived != N {
216 t.Fatalf("messages received: %d", msgsReceived)
220 func TestTwoClientsArbitraryPorts(t *testing.T) {
221 for i := 0; i < 2; i++ {
222 cl, err := NewClient(&TestingConfig)
230 func TestAddDropManyTorrents(t *testing.T) {
231 cl, err := NewClient(&TestingConfig)
232 require.NoError(t, err)
234 for i := range iter.N(1000) {
236 binary.PutVarint(spec.InfoHash[:], int64(i))
237 tt, new, err := cl.AddTorrentSpec(&spec)
238 assert.NoError(t, err)
244 type FileCacheClientStorageFactoryParams struct {
247 Wrapper func(*filecache.Cache) storage.ClientImpl
250 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
251 return func(dataDir string) storage.ClientImpl {
252 fc, err := filecache.NewCache(dataDir)
257 fc.SetCapacity(ps.Capacity)
259 return ps.Wrapper(fc)
263 type storageFactory func(string) storage.ClientImpl
265 func TestClientTransferDefault(t *testing.T) {
266 testClientTransfer(t, testClientTransferParams{
267 ExportClientStatus: true,
268 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
269 Wrapper: fileCachePieceResourceStorage,
274 func TestClientTransferRateLimitedUpload(t *testing.T) {
275 started := time.Now()
276 testClientTransfer(t, testClientTransferParams{
277 // We are uploading 13 bytes (the length of the greeting torrent). The
278 // chunks are 2 bytes in length. Then the smallest burst we can run
279 // with is 2. Time taken is (13-burst)/rate.
280 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
282 require.True(t, time.Since(started) > time.Second)
285 func TestClientTransferRateLimitedDownload(t *testing.T) {
286 testClientTransfer(t, testClientTransferParams{
287 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
291 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
292 return storage.NewResourcePieces(fc.AsResourceProvider())
295 func fileCachePieceFileStorage(fc *filecache.Cache) storage.ClientImpl {
296 return storage.NewFileStorePieces(fc.AsFileStore())
299 func TestClientTransferSmallCache(t *testing.T) {
300 testClientTransfer(t, testClientTransferParams{
301 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
303 // Going below the piece length means it can't complete a piece so
304 // that it can be hashed.
306 Wrapper: fileCachePieceResourceStorage,
309 // Can't readahead too far or the cache will thrash and drop data we
312 ExportClientStatus: true,
316 func TestClientTransferVarious(t *testing.T) {
318 for _, ls := range []storageFactory{
319 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
320 Wrapper: fileCachePieceFileStorage,
322 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
323 Wrapper: fileCachePieceResourceStorage,
328 for _, ss := range []func(string) storage.ClientImpl{
332 for _, responsive := range []bool{false, true} {
333 testClientTransfer(t, testClientTransferParams{
334 Responsive: responsive,
338 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
339 testClientTransfer(t, testClientTransferParams{
341 Responsive: responsive,
343 Readahead: readahead,
352 type testClientTransferParams struct {
356 ExportClientStatus bool
357 LeecherStorage func(string) storage.ClientImpl
358 SeederStorage func(string) storage.ClientImpl
359 SeederUploadRateLimiter *rate.Limiter
360 LeecherDownloadRateLimiter *rate.Limiter
363 // Creates a seeder and a leecher, and ensures the data transfers when a read
364 // is attempted on the leecher.
365 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
366 greetingTempDir, mi := testutil.GreetingTestTorrent()
367 defer os.RemoveAll(greetingTempDir)
368 // Create seeder and a Torrent.
371 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
372 // cfg.ListenAddr = "localhost:4000"
373 if ps.SeederStorage != nil {
374 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
376 cfg.DataDir = greetingTempDir
378 seeder, err := NewClient(&cfg)
379 require.NoError(t, err)
381 if ps.ExportClientStatus {
382 testutil.ExportStatusWriter(seeder, "s")
384 // seederTorrent, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
385 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
386 require.NoError(t, err)
388 // Create leecher and a Torrent.
389 leecherDataDir, err := ioutil.TempDir("", "")
390 require.NoError(t, err)
391 defer os.RemoveAll(leecherDataDir)
392 if ps.LeecherStorage == nil {
393 cfg.DataDir = leecherDataDir
395 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
397 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
398 // cfg.ListenAddr = "localhost:4001"
399 leecher, err := NewClient(&cfg)
400 require.NoError(t, err)
401 defer leecher.Close()
402 if ps.ExportClientStatus {
403 testutil.ExportStatusWriter(leecher, "l")
405 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
406 ret = TorrentSpecFromMetaInfo(mi)
408 ret.Storage = storage.NewFile(leecherDataDir)
411 require.NoError(t, err)
413 // Now do some things with leecher and seeder.
414 addClientPeer(leecherGreeting, seeder)
415 r := leecherGreeting.NewReader()
421 r.SetReadahead(ps.Readahead)
423 assertReadAllGreeting(t, r)
424 // After one read through, we can assume certain torrent statistics.
425 // These are not a strict requirement. It is however interesting to
427 // t.Logf("%#v", seederTorrent.Stats())
428 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
429 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
430 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
431 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
432 // Read through again for the cases where the torrent data size exceeds
433 // the size of the cache.
434 assertReadAllGreeting(t, r)
437 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
438 pos, err := r.Seek(0, os.SEEK_SET)
439 assert.NoError(t, err)
440 assert.EqualValues(t, 0, pos)
441 _greeting, err := ioutil.ReadAll(r)
442 assert.NoError(t, err)
443 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
446 // Check that after completing leeching, a leecher transitions to a seeding
447 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
448 func TestSeedAfterDownloading(t *testing.T) {
449 greetingTempDir, mi := testutil.GreetingTestTorrent()
450 defer os.RemoveAll(greetingTempDir)
453 cfg.DataDir = greetingTempDir
454 seeder, err := NewClient(&cfg)
455 require.NoError(t, err)
457 testutil.ExportStatusWriter(seeder, "s")
458 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
459 cfg.DataDir, err = ioutil.TempDir("", "")
460 require.NoError(t, err)
461 defer os.RemoveAll(cfg.DataDir)
462 leecher, err := NewClient(&cfg)
463 require.NoError(t, err)
464 defer leecher.Close()
465 testutil.ExportStatusWriter(leecher, "l")
467 // cfg.TorrentDataOpener = nil
468 cfg.DataDir, err = ioutil.TempDir("", "")
469 require.NoError(t, err)
470 defer os.RemoveAll(cfg.DataDir)
471 leecherLeecher, _ := NewClient(&cfg)
472 defer leecherLeecher.Close()
473 testutil.ExportStatusWriter(leecherLeecher, "ll")
474 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
475 ret = TorrentSpecFromMetaInfo(mi)
479 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
480 ret = TorrentSpecFromMetaInfo(mi)
484 // Simultaneously DownloadAll in Leecher, and read the contents
485 // consecutively in LeecherLeecher. This non-deterministically triggered a
486 // case where the leecher wouldn't unchoke the LeecherLeecher.
487 var wg sync.WaitGroup
493 b, err := ioutil.ReadAll(r)
494 require.NoError(t, err)
495 assert.EqualValues(t, testutil.GreetingFileContents, b)
497 addClientPeer(leecherGreeting, seeder)
498 addClientPeer(leecherGreeting, leecherLeecher)
502 leecherGreeting.DownloadAll()
508 func TestMergingTrackersByAddingSpecs(t *testing.T) {
509 cl, err := NewClient(&TestingConfig)
510 require.NoError(t, err)
512 spec := TorrentSpec{}
513 T, new, _ := cl.AddTorrentSpec(&spec)
517 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
518 _, new, _ = cl.AddTorrentSpec(&spec)
520 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
521 // Because trackers are disabled in TestingConfig.
522 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
525 type badStorage struct{}
527 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
531 func (bs badStorage) Close() error {
535 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
536 return badStoragePiece{p}
539 type badStoragePiece struct {
543 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
547 func (p badStoragePiece) GetIsComplete() bool {
551 func (p badStoragePiece) MarkComplete() error {
552 return errors.New("psyyyyyyyche")
555 func (p badStoragePiece) MarkNotComplete() error {
556 return errors.New("psyyyyyyyche")
559 func (p badStoragePiece) randomlyTruncatedDataString() string {
560 return "hello, world\n"[:rand.Intn(14)]
563 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
564 r := strings.NewReader(p.randomlyTruncatedDataString())
565 return r.ReadAt(b, off+p.p.Offset())
568 // We read from a piece which is marked completed, but is missing data.
569 func TestCompletedPieceWrongSize(t *testing.T) {
571 cfg.DefaultStorage = badStorage{}
572 cl, err := NewClient(&cfg)
573 require.NoError(t, err)
575 info := metainfo.Info{
577 Pieces: make([]byte, 20),
578 Files: []metainfo.FileInfo{
579 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
582 b, err := bencode.Marshal(info)
583 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
585 InfoHash: metainfo.HashBytes(b),
587 require.NoError(t, err)
592 b, err = ioutil.ReadAll(r)
594 assert.NoError(t, err)
597 func BenchmarkAddLargeTorrent(b *testing.B) {
599 cfg.DisableTCP = true
600 cfg.DisableUTP = true
601 cfg.ListenAddr = "redonk"
602 cl, err := NewClient(&cfg)
603 require.NoError(b, err)
605 for range iter.N(b.N) {
606 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
614 func TestResponsive(t *testing.T) {
615 seederDataDir, mi := testutil.GreetingTestTorrent()
616 defer os.RemoveAll(seederDataDir)
619 cfg.DataDir = seederDataDir
620 seeder, err := NewClient(&cfg)
623 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
624 leecherDataDir, err := ioutil.TempDir("", "")
626 defer os.RemoveAll(leecherDataDir)
628 cfg.DataDir = leecherDataDir
629 leecher, err := NewClient(&cfg)
631 defer leecher.Close()
632 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
633 ret = TorrentSpecFromMetaInfo(mi)
637 addClientPeer(leecherTorrent, seeder)
638 reader := leecherTorrent.NewReader()
640 reader.SetReadahead(0)
641 reader.SetResponsive()
643 _, err = reader.Seek(3, os.SEEK_SET)
644 require.NoError(t, err)
645 _, err = io.ReadFull(reader, b)
647 assert.EqualValues(t, "lo", string(b))
648 _, err = reader.Seek(11, os.SEEK_SET)
649 require.NoError(t, err)
650 n, err := io.ReadFull(reader, b)
652 assert.EqualValues(t, 2, n)
653 assert.EqualValues(t, "d\n", string(b))
656 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
657 seederDataDir, mi := testutil.GreetingTestTorrent()
658 defer os.RemoveAll(seederDataDir)
661 cfg.DataDir = seederDataDir
662 seeder, err := NewClient(&cfg)
665 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
666 leecherDataDir, err := ioutil.TempDir("", "")
668 defer os.RemoveAll(leecherDataDir)
670 cfg.DataDir = leecherDataDir
671 leecher, err := NewClient(&cfg)
673 defer leecher.Close()
674 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
675 ret = TorrentSpecFromMetaInfo(mi)
679 addClientPeer(leecherTorrent, seeder)
680 reader := leecherTorrent.NewReader()
682 reader.SetReadahead(0)
683 reader.SetResponsive()
685 _, err = reader.Seek(3, os.SEEK_SET)
686 require.NoError(t, err)
687 _, err = io.ReadFull(reader, b)
689 assert.EqualValues(t, "lo", string(b))
690 go leecherTorrent.Drop()
691 _, err = reader.Seek(11, os.SEEK_SET)
692 require.NoError(t, err)
693 n, err := reader.Read(b)
694 assert.EqualError(t, err, "torrent closed")
695 assert.EqualValues(t, 0, n)
698 func TestDHTInheritBlocklist(t *testing.T) {
699 ipl := iplist.New(nil)
700 require.NotNil(t, ipl)
702 cfg.IPBlocklist = ipl
704 cl, err := NewClient(&cfg)
705 require.NoError(t, err)
707 require.Equal(t, ipl, cl.DHT().IPBlocklist())
710 // Check that stuff is merged in subsequent AddTorrentSpec for the same
712 func TestAddTorrentSpecMerging(t *testing.T) {
713 cl, err := NewClient(&TestingConfig)
714 require.NoError(t, err)
716 dir, mi := testutil.GreetingTestTorrent()
717 defer os.RemoveAll(dir)
718 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
719 InfoHash: mi.HashInfoBytes(),
721 require.NoError(t, err)
723 require.Nil(t, tt.Info())
724 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
725 require.NoError(t, err)
726 require.False(t, new)
727 require.NotNil(t, tt.Info())
730 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
731 dir, mi := testutil.GreetingTestTorrent()
733 cl, _ := NewClient(&TestingConfig)
735 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
736 InfoHash: mi.HashInfoBytes(),
739 assert.EqualValues(t, 0, len(cl.Torrents()))
747 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
748 for i := range iter.N(info.NumPieces()) {
750 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
754 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
755 fileCacheDir, err := ioutil.TempDir("", "")
756 require.NoError(t, err)
757 defer os.RemoveAll(fileCacheDir)
758 fileCache, err := filecache.NewCache(fileCacheDir)
759 require.NoError(t, err)
760 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
761 defer os.RemoveAll(greetingDataTempDir)
762 filePieceStore := csf(fileCache)
763 info, err := greetingMetainfo.UnmarshalInfo()
764 require.NoError(t, err)
765 ih := greetingMetainfo.HashInfoBytes()
766 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
767 require.NoError(t, err)
768 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
769 // require.Equal(t, len(testutil.GreetingFileContents), written)
770 // require.NoError(t, err)
771 for i := 0; i < info.NumPieces(); i++ {
773 if alreadyCompleted {
774 err := greetingData.Piece(p).MarkComplete()
775 assert.NoError(t, err)
779 // TODO: Disable network option?
780 cfg.DisableTCP = true
781 cfg.DisableUTP = true
782 cfg.DefaultStorage = filePieceStore
783 cl, err := NewClient(&cfg)
784 require.NoError(t, err)
786 tt, err := cl.AddTorrent(greetingMetainfo)
787 require.NoError(t, err)
788 psrs := tt.PieceStateRuns()
789 assert.Len(t, psrs, 1)
790 assert.EqualValues(t, 3, psrs[0].Length)
791 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
792 if alreadyCompleted {
794 b, err := ioutil.ReadAll(r)
795 assert.NoError(t, err)
796 assert.EqualValues(t, testutil.GreetingFileContents, b)
800 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
801 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceFileStorage)
802 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
805 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
806 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceFileStorage)
807 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
810 func TestAddMetainfoWithNodes(t *testing.T) {
813 // For now, we want to just jam the nodes into the table, without
814 // verifying them first. Also the DHT code doesn't support mixing secure
815 // and insecure nodes if security is enabled (yet).
816 cfg.DHTConfig.NoSecurity = true
817 cl, err := NewClient(&cfg)
818 require.NoError(t, err)
820 assert.EqualValues(t, cl.DHT().NumNodes(), 0)
821 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
822 require.NoError(t, err)
823 assert.Len(t, tt.metainfo.AnnounceList, 5)
824 assert.EqualValues(t, 6, cl.DHT().NumNodes())
827 type testDownloadCancelParams struct {
828 ExportClientStatus bool
829 SetLeecherStorageCapacity bool
830 LeecherStorageCapacity int64
834 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
835 greetingTempDir, mi := testutil.GreetingTestTorrent()
836 defer os.RemoveAll(greetingTempDir)
839 cfg.DataDir = greetingTempDir
840 seeder, err := NewClient(&cfg)
841 require.NoError(t, err)
843 if ps.ExportClientStatus {
844 testutil.ExportStatusWriter(seeder, "s")
846 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
847 leecherDataDir, err := ioutil.TempDir("", "")
848 require.NoError(t, err)
849 defer os.RemoveAll(leecherDataDir)
850 fc, err := filecache.NewCache(leecherDataDir)
851 require.NoError(t, err)
852 if ps.SetLeecherStorageCapacity {
853 fc.SetCapacity(ps.LeecherStorageCapacity)
855 cfg.DefaultStorage = storage.NewFileStorePieces(fc.AsFileStore())
856 cfg.DataDir = leecherDataDir
857 leecher, _ := NewClient(&cfg)
858 defer leecher.Close()
859 if ps.ExportClientStatus {
860 testutil.ExportStatusWriter(leecher, "l")
862 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
863 ret = TorrentSpecFromMetaInfo(mi)
867 require.NoError(t, err)
869 psc := leecherGreeting.SubscribePieceStateChanges()
871 leecherGreeting.DownloadAll()
873 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
875 addClientPeer(leecherGreeting, seeder)
876 completes := make(map[int]bool, 3)
879 // started := time.Now()
881 case _v := <-psc.Values:
882 // log.Print(time.Since(started))
883 v := _v.(PieceStateChange)
884 completes[v.Index] = v.Complete
885 case <-time.After(100 * time.Millisecond):
890 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
892 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
897 func TestTorrentDownloadAll(t *testing.T) {
898 testDownloadCancel(t, testDownloadCancelParams{})
901 func TestTorrentDownloadAllThenCancel(t *testing.T) {
902 testDownloadCancel(t, testDownloadCancelParams{
907 // Ensure that it's an error for a peer to send an invalid have message.
908 func TestPeerInvalidHave(t *testing.T) {
909 cl, err := NewClient(&TestingConfig)
910 require.NoError(t, err)
912 info := metainfo.Info{
914 Pieces: make([]byte, 20),
915 Files: []metainfo.FileInfo{{Length: 1}},
917 infoBytes, err := bencode.Marshal(info)
918 require.NoError(t, err)
919 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
920 InfoBytes: infoBytes,
921 InfoHash: metainfo.HashBytes(infoBytes),
923 require.NoError(t, err)
929 assert.NoError(t, cn.peerSentHave(0))
930 assert.Error(t, cn.peerSentHave(1))
933 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
934 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
935 defer os.RemoveAll(greetingTempDir)
937 cfg.DataDir = greetingTempDir
938 seeder, err := NewClient(&TestingConfig)
939 require.NoError(t, err)
940 seeder.AddTorrentSpec(&TorrentSpec{
941 InfoBytes: greetingMetainfo.InfoBytes,
945 func TestPrepareTrackerAnnounce(t *testing.T) {
947 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
948 require.NoError(t, err)
949 assert.False(t, blocked)
950 assert.EqualValues(t, "localhost:1234", host)
951 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
954 // Check that when the listen port is 0, all the protocols listened on have
955 // the same port, and it isn't zero.
956 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
957 cl, err := NewClient(&TestingConfig)
958 require.NoError(t, err)
960 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
961 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
964 func TestClientDynamicListenTCPOnly(t *testing.T) {
966 cfg.DisableUTP = true
967 cl, err := NewClient(&cfg)
968 require.NoError(t, err)
970 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
971 assert.Nil(t, cl.utpSock)
974 func TestClientDynamicListenUTPOnly(t *testing.T) {
976 cfg.DisableTCP = true
977 cl, err := NewClient(&cfg)
978 require.NoError(t, err)
980 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
981 assert.Nil(t, cl.tcpListener)
984 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
986 cfg.DisableTCP = true
987 cfg.DisableUTP = true
988 cl, err := NewClient(&cfg)
989 require.NoError(t, err)
991 assert.Nil(t, cl.ListenAddr())
994 func addClientPeer(t *Torrent, cl *Client) {
997 IP: missinggo.AddrIP(cl.ListenAddr()),
998 Port: missinggo.AddrPort(cl.ListenAddr()),
1003 func printConnPeerCounts(t *Torrent) {
1005 log.Println(len(t.conns), len(t.peers))
1009 func totalConns(tts []*Torrent) (ret int) {
1010 for _, tt := range tts {
1012 ret += len(tt.conns)
1018 func TestSetMaxEstablishedConn(t *testing.T) {
1020 ih := testutil.GreetingMetaInfo().HashInfoBytes()
1021 cfg := TestingConfig
1022 for i := range iter.N(3) {
1023 cl, err := NewClient(&cfg)
1024 require.NoError(t, err)
1026 tt, _ := cl.AddTorrentInfoHash(ih)
1027 tt.SetMaxEstablishedConns(2)
1028 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
1029 tts = append(tts, tt)
1031 addPeers := func() {
1032 for i, tt := range tts {
1033 for _, _tt := range tts[:i] {
1034 addClientPeer(tt, _tt.cl)
1038 waitTotalConns := func(num int) {
1039 for totalConns(tts) != num {
1040 time.Sleep(time.Millisecond)
1045 tts[0].SetMaxEstablishedConns(1)
1047 tts[0].SetMaxEstablishedConns(0)
1049 tts[0].SetMaxEstablishedConns(1)
1052 tts[0].SetMaxEstablishedConns(2)
1057 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1058 os.MkdirAll(dir, 0770)
1059 file, err := os.Create(filepath.Join(dir, name))
1060 require.NoError(t, err)
1061 file.Write([]byte(name))
1063 mi := metainfo.MetaInfo{}
1065 info := metainfo.Info{PieceLength: 256 * 1024}
1066 err = info.BuildFromFilePath(filepath.Join(dir, name))
1067 require.NoError(t, err)
1068 mi.InfoBytes, err = bencode.Marshal(info)
1069 require.NoError(t, err)
1070 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1071 tr, err := cl.AddTorrent(&mi)
1072 require.NoError(t, err)
1073 assert.True(t, tr.Seeding())
1077 // https://github.com/anacrolix/torrent/issues/114
1078 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1079 cfg := TestingConfig
1080 cfg.DisableUTP = true
1082 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1084 cfg.ForceEncryption = true
1085 os.Mkdir(cfg.DataDir, 0755)
1086 server, err := NewClient(&cfg)
1087 require.NoError(t, err)
1088 defer server.Close()
1089 testutil.ExportStatusWriter(server, "s")
1090 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1091 makeMagnet(t, server, cfg.DataDir, "test2")
1093 cfg.DisableUTP = true
1094 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1096 cfg.ForceEncryption = true
1097 client, err := NewClient(&cfg)
1098 require.NoError(t, err)
1099 defer client.Close()
1100 testutil.ExportStatusWriter(client, "c")
1101 tr, err := client.AddMagnet(magnet1)
1102 require.NoError(t, err)
1103 tr.AddPeers([]Peer{Peer{
1104 IP: missinggo.AddrIP(server.ListenAddr()),
1105 Port: missinggo.AddrPort(server.ListenAddr()),