18 _ "github.com/anacrolix/envpprof"
19 "github.com/anacrolix/missinggo"
20 "github.com/anacrolix/missinggo/filecache"
21 "github.com/anacrolix/missinggo/pubsub"
22 "github.com/bradfitz/iter"
23 "github.com/stretchr/testify/assert"
24 "github.com/stretchr/testify/require"
25 "golang.org/x/time/rate"
27 "github.com/anacrolix/torrent/bencode"
28 "github.com/anacrolix/torrent/internal/testutil"
29 "github.com/anacrolix/torrent/iplist"
30 "github.com/anacrolix/torrent/metainfo"
31 "github.com/anacrolix/torrent/storage"
34 func TestingConfig() *Config {
36 ListenAddr: "localhost:0",
38 DataDir: func() string {
39 ret, err := ioutil.TempDir(tempDir, "")
45 DisableTrackers: true,
50 func TestClientDefault(t *testing.T) {
51 cl, err := NewClient(TestingConfig())
52 require.NoError(t, err)
56 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
57 cfg := TestingConfig()
58 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
59 require.NoError(t, err)
60 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
62 cfg.DefaultStorage = ci
63 cl, err := NewClient(cfg)
64 require.NoError(t, err)
66 // And again, https://github.com/anacrolix/torrent/issues/158
67 cl, err = NewClient(cfg)
68 require.NoError(t, err)
72 func TestAddDropTorrent(t *testing.T) {
73 cl, err := NewClient(TestingConfig())
74 require.NoError(t, err)
76 dir, mi := testutil.GreetingTestTorrent()
77 defer os.RemoveAll(dir)
78 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
79 require.NoError(t, err)
81 tt.SetMaxEstablishedConns(0)
82 tt.SetMaxEstablishedConns(1)
86 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
90 func TestAddTorrentNoUsableURLs(t *testing.T) {
94 func TestAddPeersToUnknownTorrent(t *testing.T) {
98 func TestPieceHashSize(t *testing.T) {
99 if pieceHash.Size() != 20 {
104 func TestTorrentInitialState(t *testing.T) {
105 dir, mi := testutil.GreetingTestTorrent()
106 defer os.RemoveAll(dir)
108 infoHash: mi.HashInfoBytes(),
109 pieceStateChanges: pubsub.NewPubSub(),
112 tor.storageOpener = storage.NewClient(storage.NewFileWithCompletion("/dev/null", storage.NewMapPieceCompletion()))
113 // Needed to lock for asynchronous piece verification.
115 err := tor.setInfoBytes(mi.InfoBytes)
116 require.NoError(t, err)
117 require.Len(t, tor.pieces, 3)
118 tor.pendAllChunkSpecs(0)
120 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
122 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
125 func TestUnmarshalPEXMsg(t *testing.T) {
126 var m peerExchangeMessage
127 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
130 if len(m.Added) != 2 {
133 if m.Added[0].Port != 0x506 {
138 func TestReducedDialTimeout(t *testing.T) {
139 for _, _case := range []struct {
143 ExpectedReduced time.Duration
145 {nominalDialTimeout, 40, 0, nominalDialTimeout},
146 {nominalDialTimeout, 40, 1, nominalDialTimeout},
147 {nominalDialTimeout, 40, 39, nominalDialTimeout},
148 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
149 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
150 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
152 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
153 expected := _case.ExpectedReduced
154 if expected < minDialTimeout {
155 expected = minDialTimeout
157 if reduced != expected {
158 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
163 func TestUTPRawConn(t *testing.T) {
164 l, err := NewUtpSocket("udp", "")
165 require.NoError(t, err)
175 // Connect a UTP peer to see if the RawConn will still work.
176 s, err := NewUtpSocket("udp", "")
177 require.NoError(t, err)
179 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
180 require.NoError(t, err)
181 defer utpPeer.Close()
182 peer, err := net.ListenPacket("udp", ":0")
183 require.NoError(t, err)
187 // How many messages to send. I've set this to double the channel buffer
188 // size in the raw packetConn.
190 readerStopped := make(chan struct{})
191 // The reader goroutine.
193 defer close(readerStopped)
194 b := make([]byte, 500)
195 for i := 0; i < N; i++ {
196 n, _, err := l.ReadFrom(b)
197 require.NoError(t, err)
200 fmt.Sscan(string(b[:n]), &d)
201 assert.Equal(t, i, d)
204 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
205 require.NoError(t, err)
206 for i := 0; i < N; i++ {
207 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
208 require.NoError(t, err)
209 time.Sleep(time.Millisecond)
212 case <-readerStopped:
213 case <-time.After(time.Second):
214 t.Fatal("reader timed out")
216 if msgsReceived != N {
217 t.Fatalf("messages received: %d", msgsReceived)
221 func TestTwoClientsArbitraryPorts(t *testing.T) {
222 for i := 0; i < 2; i++ {
223 cl, err := NewClient(TestingConfig())
231 func TestAddDropManyTorrents(t *testing.T) {
232 cl, err := NewClient(TestingConfig())
233 require.NoError(t, err)
235 for i := range iter.N(1000) {
237 binary.PutVarint(spec.InfoHash[:], int64(i))
238 tt, new, err := cl.AddTorrentSpec(&spec)
239 assert.NoError(t, err)
245 type FileCacheClientStorageFactoryParams struct {
248 Wrapper func(*filecache.Cache) storage.ClientImpl
251 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
252 return func(dataDir string) storage.ClientImpl {
253 fc, err := filecache.NewCache(dataDir)
258 fc.SetCapacity(ps.Capacity)
260 return ps.Wrapper(fc)
264 type storageFactory func(string) storage.ClientImpl
266 func TestClientTransferDefault(t *testing.T) {
267 testClientTransfer(t, testClientTransferParams{
268 ExportClientStatus: true,
269 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
270 Wrapper: fileCachePieceResourceStorage,
275 func TestClientTransferRateLimitedUpload(t *testing.T) {
276 started := time.Now()
277 testClientTransfer(t, testClientTransferParams{
278 // We are uploading 13 bytes (the length of the greeting torrent). The
279 // chunks are 2 bytes in length. Then the smallest burst we can run
280 // with is 2. Time taken is (13-burst)/rate.
281 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
283 require.True(t, time.Since(started) > time.Second)
286 func TestClientTransferRateLimitedDownload(t *testing.T) {
287 testClientTransfer(t, testClientTransferParams{
288 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
292 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
293 return storage.NewResourcePieces(fc.AsResourceProvider())
296 func TestClientTransferSmallCache(t *testing.T) {
297 testClientTransfer(t, testClientTransferParams{
298 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
300 // Going below the piece length means it can't complete a piece so
301 // that it can be hashed.
303 Wrapper: fileCachePieceResourceStorage,
306 // Can't readahead too far or the cache will thrash and drop data we
309 ExportClientStatus: true,
313 func TestClientTransferVarious(t *testing.T) {
315 for _, ls := range []storageFactory{
316 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
317 Wrapper: fileCachePieceResourceStorage,
322 for _, ss := range []func(string) storage.ClientImpl{
326 for _, responsive := range []bool{false, true} {
327 testClientTransfer(t, testClientTransferParams{
328 Responsive: responsive,
332 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
333 testClientTransfer(t, testClientTransferParams{
335 Responsive: responsive,
337 Readahead: readahead,
346 type testClientTransferParams struct {
350 ExportClientStatus bool
351 LeecherStorage func(string) storage.ClientImpl
352 SeederStorage func(string) storage.ClientImpl
353 SeederUploadRateLimiter *rate.Limiter
354 LeecherDownloadRateLimiter *rate.Limiter
357 // Creates a seeder and a leecher, and ensures the data transfers when a read
358 // is attempted on the leecher.
359 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
360 greetingTempDir, mi := testutil.GreetingTestTorrent()
361 defer os.RemoveAll(greetingTempDir)
362 // Create seeder and a Torrent.
363 cfg := TestingConfig()
365 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
366 // cfg.ListenAddr = "localhost:4000"
367 if ps.SeederStorage != nil {
368 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
369 defer cfg.DefaultStorage.Close()
371 cfg.DataDir = greetingTempDir
373 seeder, err := NewClient(cfg)
374 require.NoError(t, err)
376 if ps.ExportClientStatus {
377 testutil.ExportStatusWriter(seeder, "s")
379 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
380 seederTorrent.VerifyData()
381 // Create leecher and a Torrent.
382 leecherDataDir, err := ioutil.TempDir("", "")
383 require.NoError(t, err)
384 defer os.RemoveAll(leecherDataDir)
385 if ps.LeecherStorage == nil {
386 cfg.DataDir = leecherDataDir
388 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
390 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
391 // cfg.ListenAddr = "localhost:4001"
392 leecher, err := NewClient(cfg)
393 require.NoError(t, err)
394 defer leecher.Close()
395 if ps.ExportClientStatus {
396 testutil.ExportStatusWriter(leecher, "l")
398 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
399 ret = TorrentSpecFromMetaInfo(mi)
403 require.NoError(t, err)
405 // Now do some things with leecher and seeder.
406 addClientPeer(leecherGreeting, seeder)
407 r := leecherGreeting.NewReader()
413 r.SetReadahead(ps.Readahead)
415 assertReadAllGreeting(t, r)
416 // After one read through, we can assume certain torrent statistics.
417 // These are not a strict requirement. It is however interesting to
419 // t.Logf("%#v", seederTorrent.Stats())
420 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
421 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
422 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
423 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
424 // Read through again for the cases where the torrent data size exceeds
425 // the size of the cache.
426 assertReadAllGreeting(t, r)
429 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
430 pos, err := r.Seek(0, os.SEEK_SET)
431 assert.NoError(t, err)
432 assert.EqualValues(t, 0, pos)
433 _greeting, err := ioutil.ReadAll(r)
434 assert.NoError(t, err)
435 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
438 // Check that after completing leeching, a leecher transitions to a seeding
439 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
440 func TestSeedAfterDownloading(t *testing.T) {
441 greetingTempDir, mi := testutil.GreetingTestTorrent()
442 defer os.RemoveAll(greetingTempDir)
443 cfg := TestingConfig()
445 cfg.DataDir = greetingTempDir
446 seeder, err := NewClient(cfg)
447 require.NoError(t, err)
449 testutil.ExportStatusWriter(seeder, "s")
450 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
451 seederTorrent.VerifyData()
452 cfg.DataDir, err = ioutil.TempDir("", "")
453 require.NoError(t, err)
454 defer os.RemoveAll(cfg.DataDir)
455 leecher, err := NewClient(cfg)
456 require.NoError(t, err)
457 defer leecher.Close()
458 testutil.ExportStatusWriter(leecher, "l")
460 // cfg.TorrentDataOpener = nil
461 cfg.DataDir, err = ioutil.TempDir("", "")
462 require.NoError(t, err)
463 defer os.RemoveAll(cfg.DataDir)
464 leecherLeecher, _ := NewClient(cfg)
465 defer leecherLeecher.Close()
466 testutil.ExportStatusWriter(leecherLeecher, "ll")
467 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
468 ret = TorrentSpecFromMetaInfo(mi)
472 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
473 ret = TorrentSpecFromMetaInfo(mi)
477 // Simultaneously DownloadAll in Leecher, and read the contents
478 // consecutively in LeecherLeecher. This non-deterministically triggered a
479 // case where the leecher wouldn't unchoke the LeecherLeecher.
480 var wg sync.WaitGroup
486 b, err := ioutil.ReadAll(r)
487 require.NoError(t, err)
488 assert.EqualValues(t, testutil.GreetingFileContents, b)
490 addClientPeer(leecherGreeting, seeder)
491 addClientPeer(leecherGreeting, leecherLeecher)
495 leecherGreeting.DownloadAll()
501 func TestMergingTrackersByAddingSpecs(t *testing.T) {
502 cl, err := NewClient(TestingConfig())
503 require.NoError(t, err)
505 spec := TorrentSpec{}
506 T, new, _ := cl.AddTorrentSpec(&spec)
510 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
511 _, new, _ = cl.AddTorrentSpec(&spec)
513 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
514 // Because trackers are disabled in TestingConfig.
515 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
518 type badStorage struct{}
520 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
524 func (bs badStorage) Close() error {
528 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
529 return badStoragePiece{p}
532 type badStoragePiece struct {
536 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
540 func (p badStoragePiece) GetIsComplete() bool {
544 func (p badStoragePiece) MarkComplete() error {
545 return errors.New("psyyyyyyyche")
548 func (p badStoragePiece) MarkNotComplete() error {
549 return errors.New("psyyyyyyyche")
552 func (p badStoragePiece) randomlyTruncatedDataString() string {
553 return "hello, world\n"[:rand.Intn(14)]
556 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
557 r := strings.NewReader(p.randomlyTruncatedDataString())
558 return r.ReadAt(b, off+p.p.Offset())
561 // We read from a piece which is marked completed, but is missing data.
562 func TestCompletedPieceWrongSize(t *testing.T) {
563 cfg := TestingConfig()
564 cfg.DefaultStorage = badStorage{}
565 cl, err := NewClient(cfg)
566 require.NoError(t, err)
568 info := metainfo.Info{
570 Pieces: make([]byte, 20),
571 Files: []metainfo.FileInfo{
572 {Path: []string{"greeting"}, Length: 13},
575 b, err := bencode.Marshal(info)
576 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
578 InfoHash: metainfo.HashBytes(b),
580 require.NoError(t, err)
585 b, err = ioutil.ReadAll(r)
587 assert.NoError(t, err)
590 func BenchmarkAddLargeTorrent(b *testing.B) {
591 cfg := TestingConfig()
592 cfg.DisableTCP = true
593 cfg.DisableUTP = true
594 cfg.ListenAddr = "redonk"
595 cl, err := NewClient(cfg)
596 require.NoError(b, err)
598 for range iter.N(b.N) {
599 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
607 func TestResponsive(t *testing.T) {
608 seederDataDir, mi := testutil.GreetingTestTorrent()
609 defer os.RemoveAll(seederDataDir)
610 cfg := TestingConfig()
612 cfg.DataDir = seederDataDir
613 seeder, err := NewClient(cfg)
616 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
617 seederTorrent.VerifyData()
618 leecherDataDir, err := ioutil.TempDir("", "")
620 defer os.RemoveAll(leecherDataDir)
621 cfg = TestingConfig()
622 cfg.DataDir = leecherDataDir
623 leecher, err := NewClient(cfg)
625 defer leecher.Close()
626 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
627 ret = TorrentSpecFromMetaInfo(mi)
631 addClientPeer(leecherTorrent, seeder)
632 reader := leecherTorrent.NewReader()
634 reader.SetReadahead(0)
635 reader.SetResponsive()
637 _, err = reader.Seek(3, os.SEEK_SET)
638 require.NoError(t, err)
639 _, err = io.ReadFull(reader, b)
641 assert.EqualValues(t, "lo", string(b))
642 _, err = reader.Seek(11, os.SEEK_SET)
643 require.NoError(t, err)
644 n, err := io.ReadFull(reader, b)
646 assert.EqualValues(t, 2, n)
647 assert.EqualValues(t, "d\n", string(b))
650 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
651 seederDataDir, mi := testutil.GreetingTestTorrent()
652 defer os.RemoveAll(seederDataDir)
653 cfg := TestingConfig()
655 cfg.DataDir = seederDataDir
656 seeder, err := NewClient(cfg)
659 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
660 seederTorrent.VerifyData()
661 leecherDataDir, err := ioutil.TempDir("", "")
663 defer os.RemoveAll(leecherDataDir)
664 cfg = TestingConfig()
665 cfg.DataDir = leecherDataDir
666 leecher, err := NewClient(cfg)
668 defer leecher.Close()
669 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
670 ret = TorrentSpecFromMetaInfo(mi)
674 addClientPeer(leecherTorrent, seeder)
675 reader := leecherTorrent.NewReader()
677 reader.SetReadahead(0)
678 reader.SetResponsive()
680 _, err = reader.Seek(3, os.SEEK_SET)
681 require.NoError(t, err)
682 _, err = io.ReadFull(reader, b)
684 assert.EqualValues(t, "lo", string(b))
685 go leecherTorrent.Drop()
686 _, err = reader.Seek(11, os.SEEK_SET)
687 require.NoError(t, err)
688 n, err := reader.Read(b)
689 assert.EqualError(t, err, "torrent closed")
690 assert.EqualValues(t, 0, n)
693 func TestDHTInheritBlocklist(t *testing.T) {
694 ipl := iplist.New(nil)
695 require.NotNil(t, ipl)
696 cfg := TestingConfig()
697 cfg.IPBlocklist = ipl
699 cl, err := NewClient(cfg)
700 require.NoError(t, err)
702 require.Equal(t, ipl, cl.DHT().IPBlocklist())
705 // Check that stuff is merged in subsequent AddTorrentSpec for the same
707 func TestAddTorrentSpecMerging(t *testing.T) {
708 cl, err := NewClient(TestingConfig())
709 require.NoError(t, err)
711 dir, mi := testutil.GreetingTestTorrent()
712 defer os.RemoveAll(dir)
713 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
714 InfoHash: mi.HashInfoBytes(),
716 require.NoError(t, err)
718 require.Nil(t, tt.Info())
719 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
720 require.NoError(t, err)
721 require.False(t, new)
722 require.NotNil(t, tt.Info())
725 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
726 dir, mi := testutil.GreetingTestTorrent()
728 cl, _ := NewClient(TestingConfig())
730 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
731 InfoHash: mi.HashInfoBytes(),
734 assert.EqualValues(t, 0, len(cl.Torrents()))
742 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
743 for i := range iter.N(info.NumPieces()) {
745 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
749 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
750 fileCacheDir, err := ioutil.TempDir("", "")
751 require.NoError(t, err)
752 defer os.RemoveAll(fileCacheDir)
753 fileCache, err := filecache.NewCache(fileCacheDir)
754 require.NoError(t, err)
755 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
756 defer os.RemoveAll(greetingDataTempDir)
757 filePieceStore := csf(fileCache)
758 defer filePieceStore.Close()
759 info, err := greetingMetainfo.UnmarshalInfo()
760 require.NoError(t, err)
761 ih := greetingMetainfo.HashInfoBytes()
762 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
763 require.NoError(t, err)
764 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
765 // require.Equal(t, len(testutil.GreetingFileContents), written)
766 // require.NoError(t, err)
767 for i := 0; i < info.NumPieces(); i++ {
769 if alreadyCompleted {
770 require.NoError(t, greetingData.Piece(p).MarkComplete())
773 cfg := TestingConfig()
774 // TODO: Disable network option?
775 cfg.DisableTCP = true
776 cfg.DisableUTP = true
777 cfg.DefaultStorage = filePieceStore
778 cl, err := NewClient(cfg)
779 require.NoError(t, err)
781 tt, err := cl.AddTorrent(greetingMetainfo)
782 require.NoError(t, err)
783 psrs := tt.PieceStateRuns()
784 assert.Len(t, psrs, 1)
785 assert.EqualValues(t, 3, psrs[0].Length)
786 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
787 if alreadyCompleted {
789 b, err := ioutil.ReadAll(r)
790 assert.NoError(t, err)
791 assert.EqualValues(t, testutil.GreetingFileContents, b)
795 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
796 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
799 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
800 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
803 func TestAddMetainfoWithNodes(t *testing.T) {
804 cfg := TestingConfig()
805 cfg.ListenAddr = ":0"
807 // For now, we want to just jam the nodes into the table, without
808 // verifying them first. Also the DHT code doesn't support mixing secure
809 // and insecure nodes if security is enabled (yet).
810 cfg.DHTConfig.NoSecurity = true
811 cl, err := NewClient(cfg)
812 require.NoError(t, err)
814 assert.EqualValues(t, 0, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
815 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
816 require.NoError(t, err)
817 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
818 // check if the announce-list is here instead. TODO: Add nodes.
819 assert.Len(t, tt.metainfo.AnnounceList, 5)
820 // There are 6 nodes in the torrent file.
821 assert.EqualValues(t, 6, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
824 type testDownloadCancelParams struct {
825 ExportClientStatus bool
826 SetLeecherStorageCapacity bool
827 LeecherStorageCapacity int64
831 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
832 greetingTempDir, mi := testutil.GreetingTestTorrent()
833 defer os.RemoveAll(greetingTempDir)
834 cfg := TestingConfig()
836 cfg.DataDir = greetingTempDir
837 seeder, err := NewClient(cfg)
838 require.NoError(t, err)
840 if ps.ExportClientStatus {
841 testutil.ExportStatusWriter(seeder, "s")
843 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
844 seederTorrent.VerifyData()
845 leecherDataDir, err := ioutil.TempDir("", "")
846 require.NoError(t, err)
847 defer os.RemoveAll(leecherDataDir)
848 fc, err := filecache.NewCache(leecherDataDir)
849 require.NoError(t, err)
850 if ps.SetLeecherStorageCapacity {
851 fc.SetCapacity(ps.LeecherStorageCapacity)
853 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
854 cfg.DataDir = leecherDataDir
855 leecher, _ := NewClient(cfg)
856 defer leecher.Close()
857 if ps.ExportClientStatus {
858 testutil.ExportStatusWriter(leecher, "l")
860 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
861 ret = TorrentSpecFromMetaInfo(mi)
865 require.NoError(t, err)
867 psc := leecherGreeting.SubscribePieceStateChanges()
869 leecherGreeting.DownloadAll()
871 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
873 addClientPeer(leecherGreeting, seeder)
874 completes := make(map[int]bool, 3)
877 // started := time.Now()
879 case _v := <-psc.Values:
880 // log.Print(time.Since(started))
881 v := _v.(PieceStateChange)
882 completes[v.Index] = v.Complete
883 case <-time.After(100 * time.Millisecond):
888 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
890 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
895 func TestTorrentDownloadAll(t *testing.T) {
896 testDownloadCancel(t, testDownloadCancelParams{})
899 func TestTorrentDownloadAllThenCancel(t *testing.T) {
900 testDownloadCancel(t, testDownloadCancelParams{
905 // Ensure that it's an error for a peer to send an invalid have message.
906 func TestPeerInvalidHave(t *testing.T) {
907 cl, err := NewClient(TestingConfig())
908 require.NoError(t, err)
910 info := metainfo.Info{
912 Pieces: make([]byte, 20),
913 Files: []metainfo.FileInfo{{Length: 1}},
915 infoBytes, err := bencode.Marshal(info)
916 require.NoError(t, err)
917 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
918 InfoBytes: infoBytes,
919 InfoHash: metainfo.HashBytes(infoBytes),
921 require.NoError(t, err)
927 assert.NoError(t, cn.peerSentHave(0))
928 assert.Error(t, cn.peerSentHave(1))
931 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
932 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
933 defer os.RemoveAll(greetingTempDir)
934 cfg := TestingConfig()
935 cfg.DataDir = greetingTempDir
936 seeder, err := NewClient(TestingConfig())
937 require.NoError(t, err)
938 seeder.AddTorrentSpec(&TorrentSpec{
939 InfoBytes: greetingMetainfo.InfoBytes,
943 func TestPrepareTrackerAnnounce(t *testing.T) {
945 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
946 require.NoError(t, err)
947 assert.False(t, blocked)
948 assert.EqualValues(t, "localhost:1234", host)
949 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
952 // Check that when the listen port is 0, all the protocols listened on have
953 // the same port, and it isn't zero.
954 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
955 cl, err := NewClient(TestingConfig())
956 require.NoError(t, err)
958 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
959 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
962 func TestClientDynamicListenTCPOnly(t *testing.T) {
963 cfg := TestingConfig()
964 cfg.DisableUTP = true
965 cl, err := NewClient(cfg)
966 require.NoError(t, err)
968 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
969 assert.Nil(t, cl.utpSock)
972 func TestClientDynamicListenUTPOnly(t *testing.T) {
973 cfg := TestingConfig()
974 cfg.DisableTCP = true
975 cl, err := NewClient(cfg)
976 require.NoError(t, err)
978 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
979 assert.Nil(t, cl.tcpListener)
982 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
983 cfg := TestingConfig()
984 cfg.DisableTCP = true
985 cfg.DisableUTP = true
986 cl, err := NewClient(cfg)
987 require.NoError(t, err)
989 assert.Nil(t, cl.ListenAddr())
992 func addClientPeer(t *Torrent, cl *Client) {
995 IP: missinggo.AddrIP(cl.ListenAddr()),
996 Port: missinggo.AddrPort(cl.ListenAddr()),
1001 func totalConns(tts []*Torrent) (ret int) {
1002 for _, tt := range tts {
1004 ret += len(tt.conns)
1010 func TestSetMaxEstablishedConn(t *testing.T) {
1012 ih := testutil.GreetingMetaInfo().HashInfoBytes()
1013 for i := range iter.N(3) {
1014 cl, err := NewClient(TestingConfig())
1015 require.NoError(t, err)
1017 tt, _ := cl.AddTorrentInfoHash(ih)
1018 tt.SetMaxEstablishedConns(2)
1019 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
1020 tts = append(tts, tt)
1022 addPeers := func() {
1023 for i, tt := range tts {
1024 for _, _tt := range tts[:i] {
1025 addClientPeer(tt, _tt.cl)
1029 waitTotalConns := func(num int) {
1030 for totalConns(tts) != num {
1031 time.Sleep(time.Millisecond)
1036 tts[0].SetMaxEstablishedConns(1)
1038 tts[0].SetMaxEstablishedConns(0)
1040 tts[0].SetMaxEstablishedConns(1)
1043 tts[0].SetMaxEstablishedConns(2)
1048 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1049 os.MkdirAll(dir, 0770)
1050 file, err := os.Create(filepath.Join(dir, name))
1051 require.NoError(t, err)
1052 file.Write([]byte(name))
1054 mi := metainfo.MetaInfo{}
1056 info := metainfo.Info{PieceLength: 256 * 1024}
1057 err = info.BuildFromFilePath(filepath.Join(dir, name))
1058 require.NoError(t, err)
1059 mi.InfoBytes, err = bencode.Marshal(info)
1060 require.NoError(t, err)
1061 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1062 tr, err := cl.AddTorrent(&mi)
1063 require.NoError(t, err)
1064 require.True(t, tr.Seeding())
1069 // https://github.com/anacrolix/torrent/issues/114
1070 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1071 cfg := TestingConfig()
1072 cfg.DisableUTP = true
1074 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1076 cfg.ForceEncryption = true
1077 os.Mkdir(cfg.DataDir, 0755)
1078 server, err := NewClient(cfg)
1079 require.NoError(t, err)
1080 defer server.Close()
1081 testutil.ExportStatusWriter(server, "s")
1082 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1083 makeMagnet(t, server, cfg.DataDir, "test2")
1084 cfg = TestingConfig()
1085 cfg.DisableUTP = true
1086 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1088 cfg.ForceEncryption = true
1089 client, err := NewClient(cfg)
1090 require.NoError(t, err)
1091 defer client.Close()
1092 testutil.ExportStatusWriter(client, "c")
1093 tr, err := client.AddMagnet(magnet1)
1094 require.NoError(t, err)
1095 tr.AddPeers([]Peer{{
1096 IP: missinggo.AddrIP(server.ListenAddr()),
1097 Port: missinggo.AddrPort(server.ListenAddr()),
1104 func TestClientAddressInUse(t *testing.T) {
1105 s, _ := NewUtpSocket("udp", ":50007")
1109 cfg := TestingConfig()
1110 cfg.ListenAddr = ":50007"
1111 cl, err := NewClient(cfg)
1112 require.Error(t, err)