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/bradfitz/iter"
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
26 "golang.org/x/time/rate"
28 "github.com/anacrolix/torrent/bencode"
29 "github.com/anacrolix/torrent/internal/testutil"
30 "github.com/anacrolix/torrent/iplist"
31 "github.com/anacrolix/torrent/metainfo"
32 "github.com/anacrolix/torrent/storage"
36 log.SetFlags(log.LstdFlags | log.Llongfile)
39 func TestingConfig() *Config {
41 ListenAddr: "localhost:0",
43 DisableTrackers: true,
44 DataDir: func() string {
45 ret, err := ioutil.TempDir("", "")
55 func TestClientDefault(t *testing.T) {
56 cl, err := NewClient(TestingConfig())
57 require.NoError(t, err)
61 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
62 cfg := TestingConfig()
63 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
64 require.NoError(t, err)
65 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
67 cfg.DefaultStorage = ci
68 cl, err := NewClient(cfg)
69 require.NoError(t, err)
71 // And again, https://github.com/anacrolix/torrent/issues/158
72 cl, err = NewClient(cfg)
73 require.NoError(t, err)
77 func TestAddDropTorrent(t *testing.T) {
78 cl, err := NewClient(TestingConfig())
79 require.NoError(t, err)
81 dir, mi := testutil.GreetingTestTorrent()
82 defer os.RemoveAll(dir)
83 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
84 require.NoError(t, err)
86 tt.SetMaxEstablishedConns(0)
87 tt.SetMaxEstablishedConns(1)
91 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
95 func TestAddTorrentNoUsableURLs(t *testing.T) {
99 func TestAddPeersToUnknownTorrent(t *testing.T) {
103 func TestPieceHashSize(t *testing.T) {
104 if pieceHash.Size() != 20 {
109 func TestTorrentInitialState(t *testing.T) {
110 dir, mi := testutil.GreetingTestTorrent()
111 defer os.RemoveAll(dir)
113 infoHash: mi.HashInfoBytes(),
114 pieceStateChanges: pubsub.NewPubSub(),
117 tor.storageOpener = storage.NewClient(storage.NewFileWithCompletion("/dev/null", storage.NewMapPieceCompletion()))
118 // Needed to lock for asynchronous piece verification.
120 err := tor.setInfoBytes(mi.InfoBytes)
121 require.NoError(t, err)
122 require.Len(t, tor.pieces, 3)
123 tor.pendAllChunkSpecs(0)
125 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
127 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
130 func TestUnmarshalPEXMsg(t *testing.T) {
131 var m peerExchangeMessage
132 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
135 if len(m.Added) != 2 {
138 if m.Added[0].Port != 0x506 {
143 func TestReducedDialTimeout(t *testing.T) {
144 for _, _case := range []struct {
148 ExpectedReduced time.Duration
150 {nominalDialTimeout, 40, 0, nominalDialTimeout},
151 {nominalDialTimeout, 40, 1, nominalDialTimeout},
152 {nominalDialTimeout, 40, 39, nominalDialTimeout},
153 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
154 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
155 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
157 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
158 expected := _case.ExpectedReduced
159 if expected < minDialTimeout {
160 expected = minDialTimeout
162 if reduced != expected {
163 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
168 func TestUTPRawConn(t *testing.T) {
169 l, err := NewUtpSocket("udp", "")
170 require.NoError(t, err)
180 // Connect a UTP peer to see if the RawConn will still work.
181 s, err := NewUtpSocket("udp", "")
182 require.NoError(t, err)
184 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
185 require.NoError(t, err)
186 defer utpPeer.Close()
187 peer, err := net.ListenPacket("udp", ":0")
188 require.NoError(t, err)
192 // How many messages to send. I've set this to double the channel buffer
193 // size in the raw packetConn.
195 readerStopped := make(chan struct{})
196 // The reader goroutine.
198 defer close(readerStopped)
199 b := make([]byte, 500)
200 for i := 0; i < N; i++ {
201 n, _, err := l.ReadFrom(b)
203 t.Fatalf("error reading from raw conn: %s", err)
207 fmt.Sscan(string(b[:n]), &d)
209 log.Printf("got wrong number: expected %d, got %d", i, d)
213 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
214 require.NoError(t, err)
215 for i := 0; i < N; i++ {
216 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
220 time.Sleep(time.Microsecond)
223 case <-readerStopped:
224 case <-time.After(time.Second):
225 t.Fatal("reader timed out")
227 if msgsReceived != N {
228 t.Fatalf("messages received: %d", msgsReceived)
232 func TestTwoClientsArbitraryPorts(t *testing.T) {
233 for i := 0; i < 2; i++ {
234 cl, err := NewClient(TestingConfig())
242 func TestAddDropManyTorrents(t *testing.T) {
243 cl, err := NewClient(TestingConfig())
244 require.NoError(t, err)
246 for i := range iter.N(1000) {
248 binary.PutVarint(spec.InfoHash[:], int64(i))
249 tt, new, err := cl.AddTorrentSpec(&spec)
250 assert.NoError(t, err)
256 type FileCacheClientStorageFactoryParams struct {
259 Wrapper func(*filecache.Cache) storage.ClientImpl
262 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
263 return func(dataDir string) storage.ClientImpl {
264 fc, err := filecache.NewCache(dataDir)
269 fc.SetCapacity(ps.Capacity)
271 return ps.Wrapper(fc)
275 type storageFactory func(string) storage.ClientImpl
277 func TestClientTransferDefault(t *testing.T) {
278 testClientTransfer(t, testClientTransferParams{
279 ExportClientStatus: true,
280 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
281 Wrapper: fileCachePieceResourceStorage,
286 func TestClientTransferRateLimitedUpload(t *testing.T) {
287 started := time.Now()
288 testClientTransfer(t, testClientTransferParams{
289 // We are uploading 13 bytes (the length of the greeting torrent). The
290 // chunks are 2 bytes in length. Then the smallest burst we can run
291 // with is 2. Time taken is (13-burst)/rate.
292 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
294 require.True(t, time.Since(started) > time.Second)
297 func TestClientTransferRateLimitedDownload(t *testing.T) {
298 testClientTransfer(t, testClientTransferParams{
299 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
303 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
304 return storage.NewResourcePieces(fc.AsResourceProvider())
307 func TestClientTransferSmallCache(t *testing.T) {
308 testClientTransfer(t, testClientTransferParams{
309 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
311 // Going below the piece length means it can't complete a piece so
312 // that it can be hashed.
314 Wrapper: fileCachePieceResourceStorage,
317 // Can't readahead too far or the cache will thrash and drop data we
320 ExportClientStatus: true,
324 func TestClientTransferVarious(t *testing.T) {
326 for _, ls := range []storageFactory{
327 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
328 Wrapper: fileCachePieceResourceStorage,
333 for _, ss := range []func(string) storage.ClientImpl{
337 for _, responsive := range []bool{false, true} {
338 testClientTransfer(t, testClientTransferParams{
339 Responsive: responsive,
343 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
344 testClientTransfer(t, testClientTransferParams{
346 Responsive: responsive,
348 Readahead: readahead,
357 type testClientTransferParams struct {
361 ExportClientStatus bool
362 LeecherStorage func(string) storage.ClientImpl
363 SeederStorage func(string) storage.ClientImpl
364 SeederUploadRateLimiter *rate.Limiter
365 LeecherDownloadRateLimiter *rate.Limiter
368 // Creates a seeder and a leecher, and ensures the data transfers when a read
369 // is attempted on the leecher.
370 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
371 greetingTempDir, mi := testutil.GreetingTestTorrent()
372 defer os.RemoveAll(greetingTempDir)
373 // Create seeder and a Torrent.
374 cfg := TestingConfig()
376 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
377 // cfg.ListenAddr = "localhost:4000"
378 if ps.SeederStorage != nil {
379 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
380 defer cfg.DefaultStorage.Close()
382 cfg.DataDir = greetingTempDir
384 seeder, err := NewClient(cfg)
385 require.NoError(t, err)
387 if ps.ExportClientStatus {
388 testutil.ExportStatusWriter(seeder, "s")
390 // seederTorrent, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
391 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
392 require.NoError(t, err)
394 // Create leecher and a Torrent.
395 leecherDataDir, err := ioutil.TempDir("", "")
396 require.NoError(t, err)
397 defer os.RemoveAll(leecherDataDir)
398 if ps.LeecherStorage == nil {
399 cfg.DataDir = leecherDataDir
401 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
403 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
404 // cfg.ListenAddr = "localhost:4001"
405 leecher, err := NewClient(cfg)
406 require.NoError(t, err)
407 defer leecher.Close()
408 if ps.ExportClientStatus {
409 testutil.ExportStatusWriter(leecher, "l")
411 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
412 ret = TorrentSpecFromMetaInfo(mi)
416 require.NoError(t, err)
418 // Now do some things with leecher and seeder.
419 addClientPeer(leecherGreeting, seeder)
420 r := leecherGreeting.NewReader()
426 r.SetReadahead(ps.Readahead)
428 assertReadAllGreeting(t, r)
429 // After one read through, we can assume certain torrent statistics.
430 // These are not a strict requirement. It is however interesting to
432 // t.Logf("%#v", seederTorrent.Stats())
433 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
434 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
435 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
436 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
437 // Read through again for the cases where the torrent data size exceeds
438 // the size of the cache.
439 assertReadAllGreeting(t, r)
442 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
443 pos, err := r.Seek(0, os.SEEK_SET)
444 assert.NoError(t, err)
445 assert.EqualValues(t, 0, pos)
446 _greeting, err := ioutil.ReadAll(r)
447 assert.NoError(t, err)
448 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
451 // Check that after completing leeching, a leecher transitions to a seeding
452 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
453 func TestSeedAfterDownloading(t *testing.T) {
454 greetingTempDir, mi := testutil.GreetingTestTorrent()
455 defer os.RemoveAll(greetingTempDir)
456 cfg := TestingConfig()
458 cfg.DataDir = greetingTempDir
459 seeder, err := NewClient(cfg)
460 require.NoError(t, err)
462 testutil.ExportStatusWriter(seeder, "s")
463 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
464 cfg.DataDir, err = ioutil.TempDir("", "")
465 require.NoError(t, err)
466 defer os.RemoveAll(cfg.DataDir)
467 leecher, err := NewClient(cfg)
468 require.NoError(t, err)
469 defer leecher.Close()
470 testutil.ExportStatusWriter(leecher, "l")
472 // cfg.TorrentDataOpener = nil
473 cfg.DataDir, err = ioutil.TempDir("", "")
474 require.NoError(t, err)
475 defer os.RemoveAll(cfg.DataDir)
476 leecherLeecher, _ := NewClient(cfg)
477 defer leecherLeecher.Close()
478 testutil.ExportStatusWriter(leecherLeecher, "ll")
479 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
480 ret = TorrentSpecFromMetaInfo(mi)
484 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
485 ret = TorrentSpecFromMetaInfo(mi)
489 // Simultaneously DownloadAll in Leecher, and read the contents
490 // consecutively in LeecherLeecher. This non-deterministically triggered a
491 // case where the leecher wouldn't unchoke the LeecherLeecher.
492 var wg sync.WaitGroup
498 b, err := ioutil.ReadAll(r)
499 require.NoError(t, err)
500 assert.EqualValues(t, testutil.GreetingFileContents, b)
502 addClientPeer(leecherGreeting, seeder)
503 addClientPeer(leecherGreeting, leecherLeecher)
507 leecherGreeting.DownloadAll()
513 func TestMergingTrackersByAddingSpecs(t *testing.T) {
514 cl, err := NewClient(TestingConfig())
515 require.NoError(t, err)
517 spec := TorrentSpec{}
518 T, new, _ := cl.AddTorrentSpec(&spec)
522 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
523 _, new, _ = cl.AddTorrentSpec(&spec)
525 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
526 // Because trackers are disabled in TestingConfig.
527 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
530 type badStorage struct{}
532 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
536 func (bs badStorage) Close() error {
540 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
541 return badStoragePiece{p}
544 type badStoragePiece struct {
548 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
552 func (p badStoragePiece) GetIsComplete() bool {
556 func (p badStoragePiece) MarkComplete() error {
557 return errors.New("psyyyyyyyche")
560 func (p badStoragePiece) MarkNotComplete() error {
561 return errors.New("psyyyyyyyche")
564 func (p badStoragePiece) randomlyTruncatedDataString() string {
565 return "hello, world\n"[:rand.Intn(14)]
568 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
569 r := strings.NewReader(p.randomlyTruncatedDataString())
570 return r.ReadAt(b, off+p.p.Offset())
573 // We read from a piece which is marked completed, but is missing data.
574 func TestCompletedPieceWrongSize(t *testing.T) {
575 cfg := TestingConfig()
576 cfg.DefaultStorage = badStorage{}
577 cl, err := NewClient(cfg)
578 require.NoError(t, err)
580 info := metainfo.Info{
582 Pieces: make([]byte, 20),
583 Files: []metainfo.FileInfo{
584 {Path: []string{"greeting"}, Length: 13},
587 b, err := bencode.Marshal(info)
588 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
590 InfoHash: metainfo.HashBytes(b),
592 require.NoError(t, err)
597 b, err = ioutil.ReadAll(r)
599 assert.NoError(t, err)
602 func BenchmarkAddLargeTorrent(b *testing.B) {
603 cfg := TestingConfig()
604 cfg.DisableTCP = true
605 cfg.DisableUTP = true
606 cfg.ListenAddr = "redonk"
607 cl, err := NewClient(cfg)
608 require.NoError(b, err)
610 for range iter.N(b.N) {
611 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
619 func TestResponsive(t *testing.T) {
620 seederDataDir, mi := testutil.GreetingTestTorrent()
621 defer os.RemoveAll(seederDataDir)
622 cfg := TestingConfig()
624 cfg.DataDir = seederDataDir
625 seeder, err := NewClient(cfg)
628 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
629 leecherDataDir, err := ioutil.TempDir("", "")
631 defer os.RemoveAll(leecherDataDir)
632 cfg = TestingConfig()
633 cfg.DataDir = leecherDataDir
634 leecher, err := NewClient(cfg)
636 defer leecher.Close()
637 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
638 ret = TorrentSpecFromMetaInfo(mi)
642 addClientPeer(leecherTorrent, seeder)
643 reader := leecherTorrent.NewReader()
645 reader.SetReadahead(0)
646 reader.SetResponsive()
648 _, err = reader.Seek(3, os.SEEK_SET)
649 require.NoError(t, err)
650 _, err = io.ReadFull(reader, b)
652 assert.EqualValues(t, "lo", string(b))
653 _, err = reader.Seek(11, os.SEEK_SET)
654 require.NoError(t, err)
655 n, err := io.ReadFull(reader, b)
657 assert.EqualValues(t, 2, n)
658 assert.EqualValues(t, "d\n", string(b))
661 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
662 seederDataDir, mi := testutil.GreetingTestTorrent()
663 defer os.RemoveAll(seederDataDir)
664 cfg := TestingConfig()
666 cfg.DataDir = seederDataDir
667 seeder, err := NewClient(cfg)
670 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
671 leecherDataDir, err := ioutil.TempDir("", "")
673 defer os.RemoveAll(leecherDataDir)
674 cfg = TestingConfig()
675 cfg.DataDir = leecherDataDir
676 leecher, err := NewClient(cfg)
678 defer leecher.Close()
679 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
680 ret = TorrentSpecFromMetaInfo(mi)
684 addClientPeer(leecherTorrent, seeder)
685 reader := leecherTorrent.NewReader()
687 reader.SetReadahead(0)
688 reader.SetResponsive()
690 _, err = reader.Seek(3, os.SEEK_SET)
691 require.NoError(t, err)
692 _, err = io.ReadFull(reader, b)
694 assert.EqualValues(t, "lo", string(b))
695 go leecherTorrent.Drop()
696 _, err = reader.Seek(11, os.SEEK_SET)
697 require.NoError(t, err)
698 n, err := reader.Read(b)
699 assert.EqualError(t, err, "torrent closed")
700 assert.EqualValues(t, 0, n)
703 func TestDHTInheritBlocklist(t *testing.T) {
704 ipl := iplist.New(nil)
705 require.NotNil(t, ipl)
706 cfg := TestingConfig()
707 cfg.IPBlocklist = ipl
709 cl, err := NewClient(cfg)
710 require.NoError(t, err)
712 require.Equal(t, ipl, cl.DHT().IPBlocklist())
715 // Check that stuff is merged in subsequent AddTorrentSpec for the same
717 func TestAddTorrentSpecMerging(t *testing.T) {
718 cl, err := NewClient(TestingConfig())
719 require.NoError(t, err)
721 dir, mi := testutil.GreetingTestTorrent()
722 defer os.RemoveAll(dir)
723 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
724 InfoHash: mi.HashInfoBytes(),
726 require.NoError(t, err)
728 require.Nil(t, tt.Info())
729 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
730 require.NoError(t, err)
731 require.False(t, new)
732 require.NotNil(t, tt.Info())
735 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
736 dir, mi := testutil.GreetingTestTorrent()
738 cl, _ := NewClient(TestingConfig())
740 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
741 InfoHash: mi.HashInfoBytes(),
744 assert.EqualValues(t, 0, len(cl.Torrents()))
752 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
753 for i := range iter.N(info.NumPieces()) {
755 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
759 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
760 fileCacheDir, err := ioutil.TempDir("", "")
761 require.NoError(t, err)
762 defer os.RemoveAll(fileCacheDir)
763 fileCache, err := filecache.NewCache(fileCacheDir)
764 require.NoError(t, err)
765 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
766 defer os.RemoveAll(greetingDataTempDir)
767 filePieceStore := csf(fileCache)
768 info, err := greetingMetainfo.UnmarshalInfo()
769 require.NoError(t, err)
770 ih := greetingMetainfo.HashInfoBytes()
771 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
772 require.NoError(t, err)
773 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
774 // require.Equal(t, len(testutil.GreetingFileContents), written)
775 // require.NoError(t, err)
776 for i := 0; i < info.NumPieces(); i++ {
778 if alreadyCompleted {
779 err := greetingData.Piece(p).MarkComplete()
780 assert.NoError(t, err)
783 cfg := TestingConfig()
784 // TODO: Disable network option?
785 cfg.DisableTCP = true
786 cfg.DisableUTP = true
787 cfg.DefaultStorage = filePieceStore
788 cl, err := NewClient(cfg)
789 require.NoError(t, err)
791 tt, err := cl.AddTorrent(greetingMetainfo)
792 require.NoError(t, err)
793 psrs := tt.PieceStateRuns()
794 assert.Len(t, psrs, 1)
795 assert.EqualValues(t, 3, psrs[0].Length)
796 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
797 if alreadyCompleted {
799 b, err := ioutil.ReadAll(r)
800 assert.NoError(t, err)
801 assert.EqualValues(t, testutil.GreetingFileContents, b)
805 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
806 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
809 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
810 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
813 func TestAddMetainfoWithNodes(t *testing.T) {
814 cfg := TestingConfig()
815 cfg.ListenAddr = ":0"
817 // For now, we want to just jam the nodes into the table, without
818 // verifying them first. Also the DHT code doesn't support mixing secure
819 // and insecure nodes if security is enabled (yet).
820 cfg.DHTConfig.NoSecurity = true
821 cl, err := NewClient(cfg)
822 require.NoError(t, err)
824 assert.EqualValues(t, 0, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
825 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
826 require.NoError(t, err)
827 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
828 // check if the announce-list is here instead. TODO: Add nodes.
829 assert.Len(t, tt.metainfo.AnnounceList, 5)
830 // There are 6 nodes in the torrent file.
831 assert.EqualValues(t, 6, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
834 type testDownloadCancelParams struct {
835 ExportClientStatus bool
836 SetLeecherStorageCapacity bool
837 LeecherStorageCapacity int64
841 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
842 greetingTempDir, mi := testutil.GreetingTestTorrent()
843 defer os.RemoveAll(greetingTempDir)
844 cfg := TestingConfig()
846 cfg.DataDir = greetingTempDir
847 seeder, err := NewClient(cfg)
848 require.NoError(t, err)
850 if ps.ExportClientStatus {
851 testutil.ExportStatusWriter(seeder, "s")
853 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
854 leecherDataDir, err := ioutil.TempDir("", "")
855 require.NoError(t, err)
856 defer os.RemoveAll(leecherDataDir)
857 fc, err := filecache.NewCache(leecherDataDir)
858 require.NoError(t, err)
859 if ps.SetLeecherStorageCapacity {
860 fc.SetCapacity(ps.LeecherStorageCapacity)
862 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
863 cfg.DataDir = leecherDataDir
864 leecher, _ := NewClient(cfg)
865 defer leecher.Close()
866 if ps.ExportClientStatus {
867 testutil.ExportStatusWriter(leecher, "l")
869 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
870 ret = TorrentSpecFromMetaInfo(mi)
874 require.NoError(t, err)
876 psc := leecherGreeting.SubscribePieceStateChanges()
878 leecherGreeting.DownloadAll()
880 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
882 addClientPeer(leecherGreeting, seeder)
883 completes := make(map[int]bool, 3)
886 // started := time.Now()
888 case _v := <-psc.Values:
889 // log.Print(time.Since(started))
890 v := _v.(PieceStateChange)
891 completes[v.Index] = v.Complete
892 case <-time.After(100 * time.Millisecond):
897 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
899 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
904 func TestTorrentDownloadAll(t *testing.T) {
905 testDownloadCancel(t, testDownloadCancelParams{})
908 func TestTorrentDownloadAllThenCancel(t *testing.T) {
909 testDownloadCancel(t, testDownloadCancelParams{
914 // Ensure that it's an error for a peer to send an invalid have message.
915 func TestPeerInvalidHave(t *testing.T) {
916 cl, err := NewClient(TestingConfig())
917 require.NoError(t, err)
919 info := metainfo.Info{
921 Pieces: make([]byte, 20),
922 Files: []metainfo.FileInfo{{Length: 1}},
924 infoBytes, err := bencode.Marshal(info)
925 require.NoError(t, err)
926 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
927 InfoBytes: infoBytes,
928 InfoHash: metainfo.HashBytes(infoBytes),
930 require.NoError(t, err)
936 assert.NoError(t, cn.peerSentHave(0))
937 assert.Error(t, cn.peerSentHave(1))
940 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
941 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
942 defer os.RemoveAll(greetingTempDir)
943 cfg := TestingConfig()
944 cfg.DataDir = greetingTempDir
945 seeder, err := NewClient(TestingConfig())
946 require.NoError(t, err)
947 seeder.AddTorrentSpec(&TorrentSpec{
948 InfoBytes: greetingMetainfo.InfoBytes,
952 func TestPrepareTrackerAnnounce(t *testing.T) {
954 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
955 require.NoError(t, err)
956 assert.False(t, blocked)
957 assert.EqualValues(t, "localhost:1234", host)
958 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
961 // Check that when the listen port is 0, all the protocols listened on have
962 // the same port, and it isn't zero.
963 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
964 cl, err := NewClient(TestingConfig())
965 require.NoError(t, err)
967 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
968 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
971 func TestClientDynamicListenTCPOnly(t *testing.T) {
972 cfg := TestingConfig()
973 cfg.DisableUTP = true
974 cl, err := NewClient(cfg)
975 require.NoError(t, err)
977 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
978 assert.Nil(t, cl.utpSock)
981 func TestClientDynamicListenUTPOnly(t *testing.T) {
982 cfg := TestingConfig()
983 cfg.DisableTCP = true
984 cl, err := NewClient(cfg)
985 require.NoError(t, err)
987 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
988 assert.Nil(t, cl.tcpListener)
991 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
992 cfg := TestingConfig()
993 cfg.DisableTCP = true
994 cfg.DisableUTP = true
995 cl, err := NewClient(cfg)
996 require.NoError(t, err)
998 assert.Nil(t, cl.ListenAddr())
1001 func addClientPeer(t *Torrent, cl *Client) {
1004 IP: missinggo.AddrIP(cl.ListenAddr()),
1005 Port: missinggo.AddrPort(cl.ListenAddr()),
1010 func totalConns(tts []*Torrent) (ret int) {
1011 for _, tt := range tts {
1013 ret += len(tt.conns)
1019 func TestSetMaxEstablishedConn(t *testing.T) {
1021 ih := testutil.GreetingMetaInfo().HashInfoBytes()
1022 for i := range iter.N(3) {
1023 cl, err := NewClient(TestingConfig())
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")
1092 cfg = TestingConfig()
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{{
1104 IP: missinggo.AddrIP(server.ListenAddr()),
1105 Port: missinggo.AddrPort(server.ListenAddr()),