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", "")
182 // Connect a UTP peer to see if the RawConn will still work.
183 s, _ := NewUtpSocket("udp", "")
185 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
187 t.Fatalf("error dialing utp listener: %s", err)
189 defer utpPeer.Close()
190 peer, err := net.ListenPacket("udp", ":0")
197 // How many messages to send. I've set this to double the channel buffer
198 // size in the raw packetConn.
200 readerStopped := make(chan struct{})
201 // The reader goroutine.
203 defer close(readerStopped)
204 b := make([]byte, 500)
205 for i := 0; i < N; i++ {
206 n, _, err := l.ReadFrom(b)
208 t.Fatalf("error reading from raw conn: %s", err)
212 fmt.Sscan(string(b[:n]), &d)
214 log.Printf("got wrong number: expected %d, got %d", i, d)
218 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
222 for i := 0; i < N; i++ {
223 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
227 time.Sleep(time.Microsecond)
230 case <-readerStopped:
231 case <-time.After(time.Second):
232 t.Fatal("reader timed out")
234 if msgsReceived != N {
235 t.Fatalf("messages received: %d", msgsReceived)
239 func TestTwoClientsArbitraryPorts(t *testing.T) {
240 for i := 0; i < 2; i++ {
241 cl, err := NewClient(TestingConfig())
249 func TestAddDropManyTorrents(t *testing.T) {
250 cl, err := NewClient(TestingConfig())
251 require.NoError(t, err)
253 for i := range iter.N(1000) {
255 binary.PutVarint(spec.InfoHash[:], int64(i))
256 tt, new, err := cl.AddTorrentSpec(&spec)
257 assert.NoError(t, err)
263 type FileCacheClientStorageFactoryParams struct {
266 Wrapper func(*filecache.Cache) storage.ClientImpl
269 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
270 return func(dataDir string) storage.ClientImpl {
271 fc, err := filecache.NewCache(dataDir)
276 fc.SetCapacity(ps.Capacity)
278 return ps.Wrapper(fc)
282 type storageFactory func(string) storage.ClientImpl
284 func TestClientTransferDefault(t *testing.T) {
285 testClientTransfer(t, testClientTransferParams{
286 ExportClientStatus: true,
287 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
288 Wrapper: fileCachePieceResourceStorage,
293 func TestClientTransferRateLimitedUpload(t *testing.T) {
294 started := time.Now()
295 testClientTransfer(t, testClientTransferParams{
296 // We are uploading 13 bytes (the length of the greeting torrent). The
297 // chunks are 2 bytes in length. Then the smallest burst we can run
298 // with is 2. Time taken is (13-burst)/rate.
299 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
301 require.True(t, time.Since(started) > time.Second)
304 func TestClientTransferRateLimitedDownload(t *testing.T) {
305 testClientTransfer(t, testClientTransferParams{
306 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
310 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
311 return storage.NewResourcePieces(fc.AsResourceProvider())
314 func TestClientTransferSmallCache(t *testing.T) {
315 testClientTransfer(t, testClientTransferParams{
316 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
318 // Going below the piece length means it can't complete a piece so
319 // that it can be hashed.
321 Wrapper: fileCachePieceResourceStorage,
324 // Can't readahead too far or the cache will thrash and drop data we
327 ExportClientStatus: true,
331 func TestClientTransferVarious(t *testing.T) {
333 for _, ls := range []storageFactory{
334 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
335 Wrapper: fileCachePieceResourceStorage,
340 for _, ss := range []func(string) storage.ClientImpl{
344 for _, responsive := range []bool{false, true} {
345 testClientTransfer(t, testClientTransferParams{
346 Responsive: responsive,
350 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
351 testClientTransfer(t, testClientTransferParams{
353 Responsive: responsive,
355 Readahead: readahead,
364 type testClientTransferParams struct {
368 ExportClientStatus bool
369 LeecherStorage func(string) storage.ClientImpl
370 SeederStorage func(string) storage.ClientImpl
371 SeederUploadRateLimiter *rate.Limiter
372 LeecherDownloadRateLimiter *rate.Limiter
375 // Creates a seeder and a leecher, and ensures the data transfers when a read
376 // is attempted on the leecher.
377 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
378 greetingTempDir, mi := testutil.GreetingTestTorrent()
379 defer os.RemoveAll(greetingTempDir)
380 // Create seeder and a Torrent.
381 cfg := TestingConfig()
383 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
384 // cfg.ListenAddr = "localhost:4000"
385 if ps.SeederStorage != nil {
386 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
387 defer cfg.DefaultStorage.Close()
389 cfg.DataDir = greetingTempDir
391 seeder, err := NewClient(cfg)
392 require.NoError(t, err)
394 if ps.ExportClientStatus {
395 testutil.ExportStatusWriter(seeder, "s")
397 // seederTorrent, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
398 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
399 require.NoError(t, err)
401 // Create leecher and a Torrent.
402 leecherDataDir, err := ioutil.TempDir("", "")
403 require.NoError(t, err)
404 defer os.RemoveAll(leecherDataDir)
405 if ps.LeecherStorage == nil {
406 cfg.DataDir = leecherDataDir
408 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
410 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
411 // cfg.ListenAddr = "localhost:4001"
412 leecher, err := NewClient(cfg)
413 require.NoError(t, err)
414 defer leecher.Close()
415 if ps.ExportClientStatus {
416 testutil.ExportStatusWriter(leecher, "l")
418 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
419 ret = TorrentSpecFromMetaInfo(mi)
423 require.NoError(t, err)
425 // Now do some things with leecher and seeder.
426 addClientPeer(leecherGreeting, seeder)
427 r := leecherGreeting.NewReader()
433 r.SetReadahead(ps.Readahead)
435 assertReadAllGreeting(t, r)
436 // After one read through, we can assume certain torrent statistics.
437 // These are not a strict requirement. It is however interesting to
439 // t.Logf("%#v", seederTorrent.Stats())
440 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
441 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
442 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
443 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
444 // Read through again for the cases where the torrent data size exceeds
445 // the size of the cache.
446 assertReadAllGreeting(t, r)
449 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
450 pos, err := r.Seek(0, os.SEEK_SET)
451 assert.NoError(t, err)
452 assert.EqualValues(t, 0, pos)
453 _greeting, err := ioutil.ReadAll(r)
454 assert.NoError(t, err)
455 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
458 // Check that after completing leeching, a leecher transitions to a seeding
459 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
460 func TestSeedAfterDownloading(t *testing.T) {
461 greetingTempDir, mi := testutil.GreetingTestTorrent()
462 defer os.RemoveAll(greetingTempDir)
463 cfg := TestingConfig()
465 cfg.DataDir = greetingTempDir
466 seeder, err := NewClient(cfg)
467 require.NoError(t, err)
469 testutil.ExportStatusWriter(seeder, "s")
470 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
471 cfg.DataDir, err = ioutil.TempDir("", "")
472 require.NoError(t, err)
473 defer os.RemoveAll(cfg.DataDir)
474 leecher, err := NewClient(cfg)
475 require.NoError(t, err)
476 defer leecher.Close()
477 testutil.ExportStatusWriter(leecher, "l")
479 // cfg.TorrentDataOpener = nil
480 cfg.DataDir, err = ioutil.TempDir("", "")
481 require.NoError(t, err)
482 defer os.RemoveAll(cfg.DataDir)
483 leecherLeecher, _ := NewClient(cfg)
484 defer leecherLeecher.Close()
485 testutil.ExportStatusWriter(leecherLeecher, "ll")
486 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
487 ret = TorrentSpecFromMetaInfo(mi)
491 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
492 ret = TorrentSpecFromMetaInfo(mi)
496 // Simultaneously DownloadAll in Leecher, and read the contents
497 // consecutively in LeecherLeecher. This non-deterministically triggered a
498 // case where the leecher wouldn't unchoke the LeecherLeecher.
499 var wg sync.WaitGroup
505 b, err := ioutil.ReadAll(r)
506 require.NoError(t, err)
507 assert.EqualValues(t, testutil.GreetingFileContents, b)
509 addClientPeer(leecherGreeting, seeder)
510 addClientPeer(leecherGreeting, leecherLeecher)
514 leecherGreeting.DownloadAll()
520 func TestMergingTrackersByAddingSpecs(t *testing.T) {
521 cl, err := NewClient(TestingConfig())
522 require.NoError(t, err)
524 spec := TorrentSpec{}
525 T, new, _ := cl.AddTorrentSpec(&spec)
529 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
530 _, new, _ = cl.AddTorrentSpec(&spec)
532 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
533 // Because trackers are disabled in TestingConfig.
534 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
537 type badStorage struct{}
539 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
543 func (bs badStorage) Close() error {
547 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
548 return badStoragePiece{p}
551 type badStoragePiece struct {
555 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
559 func (p badStoragePiece) GetIsComplete() bool {
563 func (p badStoragePiece) MarkComplete() error {
564 return errors.New("psyyyyyyyche")
567 func (p badStoragePiece) MarkNotComplete() error {
568 return errors.New("psyyyyyyyche")
571 func (p badStoragePiece) randomlyTruncatedDataString() string {
572 return "hello, world\n"[:rand.Intn(14)]
575 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
576 r := strings.NewReader(p.randomlyTruncatedDataString())
577 return r.ReadAt(b, off+p.p.Offset())
580 // We read from a piece which is marked completed, but is missing data.
581 func TestCompletedPieceWrongSize(t *testing.T) {
582 cfg := TestingConfig()
583 cfg.DefaultStorage = badStorage{}
584 cl, err := NewClient(cfg)
585 require.NoError(t, err)
587 info := metainfo.Info{
589 Pieces: make([]byte, 20),
590 Files: []metainfo.FileInfo{
591 {Path: []string{"greeting"}, Length: 13},
594 b, err := bencode.Marshal(info)
595 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
597 InfoHash: metainfo.HashBytes(b),
599 require.NoError(t, err)
604 b, err = ioutil.ReadAll(r)
606 assert.NoError(t, err)
609 func BenchmarkAddLargeTorrent(b *testing.B) {
610 cfg := TestingConfig()
611 cfg.DisableTCP = true
612 cfg.DisableUTP = true
613 cfg.ListenAddr = "redonk"
614 cl, err := NewClient(cfg)
615 require.NoError(b, err)
617 for range iter.N(b.N) {
618 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
626 func TestResponsive(t *testing.T) {
627 seederDataDir, mi := testutil.GreetingTestTorrent()
628 defer os.RemoveAll(seederDataDir)
629 cfg := TestingConfig()
631 cfg.DataDir = seederDataDir
632 seeder, err := NewClient(cfg)
635 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
636 leecherDataDir, err := ioutil.TempDir("", "")
638 defer os.RemoveAll(leecherDataDir)
639 cfg = TestingConfig()
640 cfg.DataDir = leecherDataDir
641 leecher, err := NewClient(cfg)
643 defer leecher.Close()
644 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
645 ret = TorrentSpecFromMetaInfo(mi)
649 addClientPeer(leecherTorrent, seeder)
650 reader := leecherTorrent.NewReader()
652 reader.SetReadahead(0)
653 reader.SetResponsive()
655 _, err = reader.Seek(3, os.SEEK_SET)
656 require.NoError(t, err)
657 _, err = io.ReadFull(reader, b)
659 assert.EqualValues(t, "lo", string(b))
660 _, err = reader.Seek(11, os.SEEK_SET)
661 require.NoError(t, err)
662 n, err := io.ReadFull(reader, b)
664 assert.EqualValues(t, 2, n)
665 assert.EqualValues(t, "d\n", string(b))
668 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
669 seederDataDir, mi := testutil.GreetingTestTorrent()
670 defer os.RemoveAll(seederDataDir)
671 cfg := TestingConfig()
673 cfg.DataDir = seederDataDir
674 seeder, err := NewClient(cfg)
677 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
678 leecherDataDir, err := ioutil.TempDir("", "")
680 defer os.RemoveAll(leecherDataDir)
681 cfg = TestingConfig()
682 cfg.DataDir = leecherDataDir
683 leecher, err := NewClient(cfg)
685 defer leecher.Close()
686 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
687 ret = TorrentSpecFromMetaInfo(mi)
691 addClientPeer(leecherTorrent, seeder)
692 reader := leecherTorrent.NewReader()
694 reader.SetReadahead(0)
695 reader.SetResponsive()
697 _, err = reader.Seek(3, os.SEEK_SET)
698 require.NoError(t, err)
699 _, err = io.ReadFull(reader, b)
701 assert.EqualValues(t, "lo", string(b))
702 go leecherTorrent.Drop()
703 _, err = reader.Seek(11, os.SEEK_SET)
704 require.NoError(t, err)
705 n, err := reader.Read(b)
706 assert.EqualError(t, err, "torrent closed")
707 assert.EqualValues(t, 0, n)
710 func TestDHTInheritBlocklist(t *testing.T) {
711 ipl := iplist.New(nil)
712 require.NotNil(t, ipl)
713 cfg := TestingConfig()
714 cfg.IPBlocklist = ipl
716 cl, err := NewClient(cfg)
717 require.NoError(t, err)
719 require.Equal(t, ipl, cl.DHT().IPBlocklist())
722 // Check that stuff is merged in subsequent AddTorrentSpec for the same
724 func TestAddTorrentSpecMerging(t *testing.T) {
725 cl, err := NewClient(TestingConfig())
726 require.NoError(t, err)
728 dir, mi := testutil.GreetingTestTorrent()
729 defer os.RemoveAll(dir)
730 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
731 InfoHash: mi.HashInfoBytes(),
733 require.NoError(t, err)
735 require.Nil(t, tt.Info())
736 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
737 require.NoError(t, err)
738 require.False(t, new)
739 require.NotNil(t, tt.Info())
742 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
743 dir, mi := testutil.GreetingTestTorrent()
745 cl, _ := NewClient(TestingConfig())
747 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
748 InfoHash: mi.HashInfoBytes(),
751 assert.EqualValues(t, 0, len(cl.Torrents()))
759 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
760 for i := range iter.N(info.NumPieces()) {
762 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
766 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
767 fileCacheDir, err := ioutil.TempDir("", "")
768 require.NoError(t, err)
769 defer os.RemoveAll(fileCacheDir)
770 fileCache, err := filecache.NewCache(fileCacheDir)
771 require.NoError(t, err)
772 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
773 defer os.RemoveAll(greetingDataTempDir)
774 filePieceStore := csf(fileCache)
775 info, err := greetingMetainfo.UnmarshalInfo()
776 require.NoError(t, err)
777 ih := greetingMetainfo.HashInfoBytes()
778 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
779 require.NoError(t, err)
780 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
781 // require.Equal(t, len(testutil.GreetingFileContents), written)
782 // require.NoError(t, err)
783 for i := 0; i < info.NumPieces(); i++ {
785 if alreadyCompleted {
786 err := greetingData.Piece(p).MarkComplete()
787 assert.NoError(t, err)
790 cfg := TestingConfig()
791 // TODO: Disable network option?
792 cfg.DisableTCP = true
793 cfg.DisableUTP = true
794 cfg.DefaultStorage = filePieceStore
795 cl, err := NewClient(cfg)
796 require.NoError(t, err)
798 tt, err := cl.AddTorrent(greetingMetainfo)
799 require.NoError(t, err)
800 psrs := tt.PieceStateRuns()
801 assert.Len(t, psrs, 1)
802 assert.EqualValues(t, 3, psrs[0].Length)
803 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
804 if alreadyCompleted {
806 b, err := ioutil.ReadAll(r)
807 assert.NoError(t, err)
808 assert.EqualValues(t, testutil.GreetingFileContents, b)
812 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
813 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
816 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
817 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
820 func TestAddMetainfoWithNodes(t *testing.T) {
821 cfg := TestingConfig()
822 cfg.ListenAddr = ":0"
824 // For now, we want to just jam the nodes into the table, without
825 // verifying them first. Also the DHT code doesn't support mixing secure
826 // and insecure nodes if security is enabled (yet).
827 cfg.DHTConfig.NoSecurity = true
828 cl, err := NewClient(cfg)
829 require.NoError(t, err)
831 assert.EqualValues(t, 0, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
832 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
833 require.NoError(t, err)
834 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
835 // check if the announce-list is here instead. TODO: Add nodes.
836 assert.Len(t, tt.metainfo.AnnounceList, 5)
837 // There are 6 nodes in the torrent file.
838 assert.EqualValues(t, 6, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
841 type testDownloadCancelParams struct {
842 ExportClientStatus bool
843 SetLeecherStorageCapacity bool
844 LeecherStorageCapacity int64
848 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
849 greetingTempDir, mi := testutil.GreetingTestTorrent()
850 defer os.RemoveAll(greetingTempDir)
851 cfg := TestingConfig()
853 cfg.DataDir = greetingTempDir
854 seeder, err := NewClient(cfg)
855 require.NoError(t, err)
857 if ps.ExportClientStatus {
858 testutil.ExportStatusWriter(seeder, "s")
860 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
861 leecherDataDir, err := ioutil.TempDir("", "")
862 require.NoError(t, err)
863 defer os.RemoveAll(leecherDataDir)
864 fc, err := filecache.NewCache(leecherDataDir)
865 require.NoError(t, err)
866 if ps.SetLeecherStorageCapacity {
867 fc.SetCapacity(ps.LeecherStorageCapacity)
869 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
870 cfg.DataDir = leecherDataDir
871 leecher, _ := NewClient(cfg)
872 defer leecher.Close()
873 if ps.ExportClientStatus {
874 testutil.ExportStatusWriter(leecher, "l")
876 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
877 ret = TorrentSpecFromMetaInfo(mi)
881 require.NoError(t, err)
883 psc := leecherGreeting.SubscribePieceStateChanges()
885 leecherGreeting.DownloadAll()
887 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
889 addClientPeer(leecherGreeting, seeder)
890 completes := make(map[int]bool, 3)
893 // started := time.Now()
895 case _v := <-psc.Values:
896 // log.Print(time.Since(started))
897 v := _v.(PieceStateChange)
898 completes[v.Index] = v.Complete
899 case <-time.After(100 * time.Millisecond):
904 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
906 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
911 func TestTorrentDownloadAll(t *testing.T) {
912 testDownloadCancel(t, testDownloadCancelParams{})
915 func TestTorrentDownloadAllThenCancel(t *testing.T) {
916 testDownloadCancel(t, testDownloadCancelParams{
921 // Ensure that it's an error for a peer to send an invalid have message.
922 func TestPeerInvalidHave(t *testing.T) {
923 cl, err := NewClient(TestingConfig())
924 require.NoError(t, err)
926 info := metainfo.Info{
928 Pieces: make([]byte, 20),
929 Files: []metainfo.FileInfo{{Length: 1}},
931 infoBytes, err := bencode.Marshal(info)
932 require.NoError(t, err)
933 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
934 InfoBytes: infoBytes,
935 InfoHash: metainfo.HashBytes(infoBytes),
937 require.NoError(t, err)
943 assert.NoError(t, cn.peerSentHave(0))
944 assert.Error(t, cn.peerSentHave(1))
947 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
948 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
949 defer os.RemoveAll(greetingTempDir)
950 cfg := TestingConfig()
951 cfg.DataDir = greetingTempDir
952 seeder, err := NewClient(TestingConfig())
953 require.NoError(t, err)
954 seeder.AddTorrentSpec(&TorrentSpec{
955 InfoBytes: greetingMetainfo.InfoBytes,
959 func TestPrepareTrackerAnnounce(t *testing.T) {
961 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
962 require.NoError(t, err)
963 assert.False(t, blocked)
964 assert.EqualValues(t, "localhost:1234", host)
965 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
968 // Check that when the listen port is 0, all the protocols listened on have
969 // the same port, and it isn't zero.
970 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
971 cl, err := NewClient(TestingConfig())
972 require.NoError(t, err)
974 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
975 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
978 func TestClientDynamicListenTCPOnly(t *testing.T) {
979 cfg := TestingConfig()
980 cfg.DisableUTP = true
981 cl, err := NewClient(cfg)
982 require.NoError(t, err)
984 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
985 assert.Nil(t, cl.utpSock)
988 func TestClientDynamicListenUTPOnly(t *testing.T) {
989 cfg := TestingConfig()
990 cfg.DisableTCP = true
991 cl, err := NewClient(cfg)
992 require.NoError(t, err)
994 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
995 assert.Nil(t, cl.tcpListener)
998 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
999 cfg := TestingConfig()
1000 cfg.DisableTCP = true
1001 cfg.DisableUTP = true
1002 cl, err := NewClient(cfg)
1003 require.NoError(t, err)
1005 assert.Nil(t, cl.ListenAddr())
1008 func addClientPeer(t *Torrent, cl *Client) {
1011 IP: missinggo.AddrIP(cl.ListenAddr()),
1012 Port: missinggo.AddrPort(cl.ListenAddr()),
1017 func totalConns(tts []*Torrent) (ret int) {
1018 for _, tt := range tts {
1020 ret += len(tt.conns)
1026 func TestSetMaxEstablishedConn(t *testing.T) {
1028 ih := testutil.GreetingMetaInfo().HashInfoBytes()
1029 for i := range iter.N(3) {
1030 cl, err := NewClient(TestingConfig())
1031 require.NoError(t, err)
1033 tt, _ := cl.AddTorrentInfoHash(ih)
1034 tt.SetMaxEstablishedConns(2)
1035 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
1036 tts = append(tts, tt)
1038 addPeers := func() {
1039 for i, tt := range tts {
1040 for _, _tt := range tts[:i] {
1041 addClientPeer(tt, _tt.cl)
1045 waitTotalConns := func(num int) {
1046 for totalConns(tts) != num {
1047 time.Sleep(time.Millisecond)
1052 tts[0].SetMaxEstablishedConns(1)
1054 tts[0].SetMaxEstablishedConns(0)
1056 tts[0].SetMaxEstablishedConns(1)
1059 tts[0].SetMaxEstablishedConns(2)
1064 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1065 os.MkdirAll(dir, 0770)
1066 file, err := os.Create(filepath.Join(dir, name))
1067 require.NoError(t, err)
1068 file.Write([]byte(name))
1070 mi := metainfo.MetaInfo{}
1072 info := metainfo.Info{PieceLength: 256 * 1024}
1073 err = info.BuildFromFilePath(filepath.Join(dir, name))
1074 require.NoError(t, err)
1075 mi.InfoBytes, err = bencode.Marshal(info)
1076 require.NoError(t, err)
1077 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1078 tr, err := cl.AddTorrent(&mi)
1079 require.NoError(t, err)
1080 assert.True(t, tr.Seeding())
1084 // https://github.com/anacrolix/torrent/issues/114
1085 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1086 cfg := TestingConfig()
1087 cfg.DisableUTP = true
1089 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1091 cfg.ForceEncryption = true
1092 os.Mkdir(cfg.DataDir, 0755)
1093 server, err := NewClient(cfg)
1094 require.NoError(t, err)
1095 defer server.Close()
1096 testutil.ExportStatusWriter(server, "s")
1097 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1098 makeMagnet(t, server, cfg.DataDir, "test2")
1099 cfg = TestingConfig()
1100 cfg.DisableUTP = true
1101 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1103 cfg.ForceEncryption = true
1104 client, err := NewClient(cfg)
1105 require.NoError(t, err)
1106 defer client.Close()
1107 testutil.ExportStatusWriter(client, "c")
1108 tr, err := client.AddMagnet(magnet1)
1109 require.NoError(t, err)
1110 tr.AddPeers([]Peer{{
1111 IP: missinggo.AddrIP(server.ListenAddr()),
1112 Port: missinggo.AddrPort(server.ListenAddr()),