18 _ "github.com/anacrolix/envpprof"
19 "github.com/anacrolix/missinggo"
20 "github.com/anacrolix/missinggo/filecache"
21 "github.com/bradfitz/iter"
22 "github.com/stretchr/testify/assert"
23 "github.com/stretchr/testify/require"
24 "golang.org/x/time/rate"
26 "github.com/anacrolix/torrent/bencode"
27 "github.com/anacrolix/torrent/internal/testutil"
28 "github.com/anacrolix/torrent/iplist"
29 "github.com/anacrolix/torrent/metainfo"
30 "github.com/anacrolix/torrent/storage"
33 func TestingConfig() *Config {
35 ListenAddr: "localhost:0",
38 DisableTrackers: true,
39 NoDefaultPortForwarding: true,
44 func TestClientDefault(t *testing.T) {
45 cl, err := NewClient(TestingConfig())
46 require.NoError(t, err)
50 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
51 cfg := TestingConfig()
52 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
53 require.NoError(t, err)
54 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
56 cfg.DefaultStorage = ci
57 cl, err := NewClient(cfg)
58 require.NoError(t, err)
60 // And again, https://github.com/anacrolix/torrent/issues/158
61 cl, err = NewClient(cfg)
62 require.NoError(t, err)
66 func TestAddDropTorrent(t *testing.T) {
67 cl, err := NewClient(TestingConfig())
68 require.NoError(t, err)
70 dir, mi := testutil.GreetingTestTorrent()
71 defer os.RemoveAll(dir)
72 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
73 require.NoError(t, err)
75 tt.SetMaxEstablishedConns(0)
76 tt.SetMaxEstablishedConns(1)
80 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
85 func TestAddTorrentNoUsableURLs(t *testing.T) {
90 func TestAddPeersToUnknownTorrent(t *testing.T) {
95 func TestPieceHashSize(t *testing.T) {
96 assert.Equal(t, 20, pieceHash.Size())
99 func TestTorrentInitialState(t *testing.T) {
100 dir, mi := testutil.GreetingTestTorrent()
101 defer os.RemoveAll(dir)
104 tor := cl.newTorrent(
106 storage.NewFileWithCompletion(tempDir(), storage.NewMapPieceCompletion()),
110 err := tor.setInfoBytes(mi.InfoBytes)
112 require.NoError(t, err)
113 require.Len(t, tor.pieces, 3)
114 tor.pendAllChunkSpecs(0)
116 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
118 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
121 func TestUnmarshalPEXMsg(t *testing.T) {
122 var m peerExchangeMessage
123 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
126 if len(m.Added) != 2 {
129 if m.Added[0].Port != 0x506 {
134 func TestReducedDialTimeout(t *testing.T) {
137 for _, _case := range []struct {
141 ExpectedReduced time.Duration
143 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
144 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
145 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
146 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
147 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
148 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
150 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
151 expected := _case.ExpectedReduced
152 if expected < cfg.MinDialTimeout {
153 expected = cfg.MinDialTimeout
155 if reduced != expected {
156 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
161 func TestUTPRawConn(t *testing.T) {
162 l, err := NewUtpSocket("udp", "")
163 require.NoError(t, err)
173 // Connect a UTP peer to see if the RawConn will still work.
174 s, err := NewUtpSocket("udp", "")
175 require.NoError(t, err)
177 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
178 require.NoError(t, err)
179 defer utpPeer.Close()
180 peer, err := net.ListenPacket("udp", ":0")
181 require.NoError(t, err)
185 // How many messages to send. I've set this to double the channel buffer
186 // size in the raw packetConn.
188 readerStopped := make(chan struct{})
189 // The reader goroutine.
191 defer close(readerStopped)
192 b := make([]byte, 500)
193 for i := 0; i < N; i++ {
194 n, _, err := l.ReadFrom(b)
195 require.NoError(t, err)
198 fmt.Sscan(string(b[:n]), &d)
199 assert.Equal(t, i, d)
202 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
203 require.NoError(t, err)
204 for i := 0; i < N; i++ {
205 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
206 require.NoError(t, err)
207 time.Sleep(time.Millisecond)
210 case <-readerStopped:
211 case <-time.After(time.Second):
212 t.Fatal("reader timed out")
214 if msgsReceived != N {
215 t.Fatalf("messages received: %d", msgsReceived)
219 func TestTwoClientsArbitraryPorts(t *testing.T) {
220 for i := 0; i < 2; i++ {
221 cl, err := NewClient(TestingConfig())
229 func TestAddDropManyTorrents(t *testing.T) {
230 cl, err := NewClient(TestingConfig())
231 require.NoError(t, err)
233 for i := range iter.N(1000) {
235 binary.PutVarint(spec.InfoHash[:], int64(i))
236 tt, new, err := cl.AddTorrentSpec(&spec)
237 assert.NoError(t, err)
243 type FileCacheClientStorageFactoryParams struct {
246 Wrapper func(*filecache.Cache) storage.ClientImpl
249 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
250 return func(dataDir string) storage.ClientImpl {
251 fc, err := filecache.NewCache(dataDir)
256 fc.SetCapacity(ps.Capacity)
258 return ps.Wrapper(fc)
262 type storageFactory func(string) storage.ClientImpl
264 func TestClientTransferDefault(t *testing.T) {
265 testClientTransfer(t, testClientTransferParams{
266 ExportClientStatus: true,
267 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
268 Wrapper: fileCachePieceResourceStorage,
273 func TestClientTransferRateLimitedUpload(t *testing.T) {
274 started := time.Now()
275 testClientTransfer(t, testClientTransferParams{
276 // We are uploading 13 bytes (the length of the greeting torrent). The
277 // chunks are 2 bytes in length. Then the smallest burst we can run
278 // with is 2. Time taken is (13-burst)/rate.
279 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
281 require.True(t, time.Since(started) > time.Second)
284 func TestClientTransferRateLimitedDownload(t *testing.T) {
285 testClientTransfer(t, testClientTransferParams{
286 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
290 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
291 return storage.NewResourcePieces(fc.AsResourceProvider())
294 func TestClientTransferSmallCache(t *testing.T) {
295 testClientTransfer(t, testClientTransferParams{
296 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
298 // Going below the piece length means it can't complete a piece so
299 // that it can be hashed.
301 Wrapper: fileCachePieceResourceStorage,
304 // Can't readahead too far or the cache will thrash and drop data we
307 ExportClientStatus: true,
311 func TestClientTransferVarious(t *testing.T) {
313 for _, ls := range []storageFactory{
314 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
315 Wrapper: fileCachePieceResourceStorage,
320 for _, ss := range []func(string) storage.ClientImpl{
324 for _, responsive := range []bool{false, true} {
325 testClientTransfer(t, testClientTransferParams{
326 Responsive: responsive,
330 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
331 testClientTransfer(t, testClientTransferParams{
333 Responsive: responsive,
335 Readahead: readahead,
344 type testClientTransferParams struct {
348 ExportClientStatus bool
349 LeecherStorage func(string) storage.ClientImpl
350 SeederStorage func(string) storage.ClientImpl
351 SeederUploadRateLimiter *rate.Limiter
352 LeecherDownloadRateLimiter *rate.Limiter
355 // Creates a seeder and a leecher, and ensures the data transfers when a read
356 // is attempted on the leecher.
357 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
358 greetingTempDir, mi := testutil.GreetingTestTorrent()
359 defer os.RemoveAll(greetingTempDir)
360 // Create seeder and a Torrent.
361 cfg := TestingConfig()
363 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
364 // cfg.ListenAddr = "localhost:4000"
365 if ps.SeederStorage != nil {
366 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
367 defer cfg.DefaultStorage.Close()
369 cfg.DataDir = greetingTempDir
371 seeder, err := NewClient(cfg)
372 require.NoError(t, err)
373 if ps.ExportClientStatus {
374 testutil.ExportStatusWriter(seeder, "s")
376 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
377 // Run a Stats right after Closing the Client. This will trigger the Stats
378 // panic in #214 caused by RemoteAddr on Closed uTP sockets.
379 defer seederTorrent.Stats()
381 seederTorrent.VerifyData()
382 // Create leecher and a Torrent.
383 leecherDataDir, err := ioutil.TempDir("", "")
384 require.NoError(t, err)
385 defer os.RemoveAll(leecherDataDir)
386 if ps.LeecherStorage == nil {
387 cfg.DataDir = leecherDataDir
389 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
391 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
392 // cfg.ListenAddr = "localhost:4001"
393 leecher, err := NewClient(cfg)
394 require.NoError(t, err)
395 defer leecher.Close()
396 if ps.ExportClientStatus {
397 testutil.ExportStatusWriter(leecher, "l")
399 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
400 ret = TorrentSpecFromMetaInfo(mi)
404 require.NoError(t, err)
406 // Now do some things with leecher and seeder.
407 addClientPeer(leecherGreeting, seeder)
408 r := leecherGreeting.NewReader()
414 r.SetReadahead(ps.Readahead)
416 assertReadAllGreeting(t, r)
417 // After one read through, we can assume certain torrent statistics.
418 // These are not a strict requirement. It is however interesting to
420 // t.Logf("%#v", seederTorrent.Stats())
421 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
422 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
423 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
424 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
425 // Read through again for the cases where the torrent data size exceeds
426 // the size of the cache.
427 assertReadAllGreeting(t, r)
430 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
431 pos, err := r.Seek(0, io.SeekStart)
432 assert.NoError(t, err)
433 assert.EqualValues(t, 0, pos)
434 _greeting, err := ioutil.ReadAll(r)
435 assert.NoError(t, err)
436 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
439 // Check that after completing leeching, a leecher transitions to a seeding
440 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
441 func TestSeedAfterDownloading(t *testing.T) {
442 greetingTempDir, mi := testutil.GreetingTestTorrent()
443 defer os.RemoveAll(greetingTempDir)
444 cfg := TestingConfig()
446 cfg.DataDir = greetingTempDir
447 seeder, err := NewClient(cfg)
448 require.NoError(t, err)
450 testutil.ExportStatusWriter(seeder, "s")
451 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
452 seederTorrent.VerifyData()
453 cfg.DataDir, err = ioutil.TempDir("", "")
454 require.NoError(t, err)
455 defer os.RemoveAll(cfg.DataDir)
456 leecher, err := NewClient(cfg)
457 require.NoError(t, err)
458 defer leecher.Close()
459 testutil.ExportStatusWriter(leecher, "l")
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 var _ storage.ClientImpl = badStorage{}
522 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
526 func (bs badStorage) Close() error {
530 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
531 return badStoragePiece{p}
534 type badStoragePiece struct {
538 var _ storage.PieceImpl = badStoragePiece{}
540 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
544 func (p badStoragePiece) Completion() storage.Completion {
545 return storage.Completion{Complete: true, Ok: true}
548 func (p badStoragePiece) MarkComplete() error {
549 return errors.New("psyyyyyyyche")
552 func (p badStoragePiece) MarkNotComplete() error {
553 return errors.New("psyyyyyyyche")
556 func (p badStoragePiece) randomlyTruncatedDataString() string {
557 return "hello, world\n"[:rand.Intn(14)]
560 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
561 r := strings.NewReader(p.randomlyTruncatedDataString())
562 return r.ReadAt(b, off+p.p.Offset())
565 // We read from a piece which is marked completed, but is missing data.
566 func TestCompletedPieceWrongSize(t *testing.T) {
567 cfg := TestingConfig()
568 cfg.DefaultStorage = badStorage{}
569 cl, err := NewClient(cfg)
570 require.NoError(t, err)
572 info := metainfo.Info{
574 Pieces: make([]byte, 20),
575 Files: []metainfo.FileInfo{
576 {Path: []string{"greeting"}, Length: 13},
579 b, err := bencode.Marshal(info)
580 require.NoError(t, err)
581 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
583 InfoHash: metainfo.HashBytes(b),
585 require.NoError(t, err)
590 b, err = ioutil.ReadAll(r)
592 assert.NoError(t, err)
595 func BenchmarkAddLargeTorrent(b *testing.B) {
596 cfg := TestingConfig()
597 cfg.DisableTCP = true
598 cfg.DisableUTP = true
599 cfg.ListenAddr = "redonk"
600 cl, err := NewClient(cfg)
601 require.NoError(b, err)
603 for range iter.N(b.N) {
604 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
612 func TestResponsive(t *testing.T) {
613 seederDataDir, mi := testutil.GreetingTestTorrent()
614 defer os.RemoveAll(seederDataDir)
615 cfg := TestingConfig()
617 cfg.DataDir = seederDataDir
618 seeder, err := NewClient(cfg)
621 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
622 seederTorrent.VerifyData()
623 leecherDataDir, err := ioutil.TempDir("", "")
625 defer os.RemoveAll(leecherDataDir)
626 cfg = TestingConfig()
627 cfg.DataDir = leecherDataDir
628 leecher, err := NewClient(cfg)
630 defer leecher.Close()
631 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
632 ret = TorrentSpecFromMetaInfo(mi)
636 addClientPeer(leecherTorrent, seeder)
637 reader := leecherTorrent.NewReader()
639 reader.SetReadahead(0)
640 reader.SetResponsive()
642 _, err = reader.Seek(3, io.SeekStart)
643 require.NoError(t, err)
644 _, err = io.ReadFull(reader, b)
646 assert.EqualValues(t, "lo", string(b))
647 _, err = reader.Seek(11, io.SeekStart)
648 require.NoError(t, err)
649 n, err := io.ReadFull(reader, b)
651 assert.EqualValues(t, 2, n)
652 assert.EqualValues(t, "d\n", string(b))
655 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
656 seederDataDir, mi := testutil.GreetingTestTorrent()
657 defer os.RemoveAll(seederDataDir)
658 cfg := TestingConfig()
660 cfg.DataDir = seederDataDir
661 seeder, err := NewClient(cfg)
664 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
665 seederTorrent.VerifyData()
666 leecherDataDir, err := ioutil.TempDir("", "")
668 defer os.RemoveAll(leecherDataDir)
669 cfg = TestingConfig()
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, io.SeekStart)
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, io.SeekStart)
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)
701 cfg := TestingConfig()
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 defer filePieceStore.Close()
764 info, err := greetingMetainfo.UnmarshalInfo()
765 require.NoError(t, err)
766 ih := greetingMetainfo.HashInfoBytes()
767 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
768 require.NoError(t, err)
769 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
770 // require.Equal(t, len(testutil.GreetingFileContents), written)
771 // require.NoError(t, err)
772 for i := 0; i < info.NumPieces(); i++ {
774 if alreadyCompleted {
775 require.NoError(t, greetingData.Piece(p).MarkComplete())
778 cfg := TestingConfig()
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, fileCachePieceResourceStorage)
804 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
805 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
808 func TestAddMetainfoWithNodes(t *testing.T) {
809 cfg := TestingConfig()
810 cfg.ListenAddr = ":0"
812 // For now, we want to just jam the nodes into the table, without
813 // verifying them first. Also the DHT code doesn't support mixing secure
814 // and insecure nodes if security is enabled (yet).
815 cfg.DHTConfig.NoSecurity = true
816 cl, err := NewClient(cfg)
817 require.NoError(t, err)
819 assert.EqualValues(t, 0, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
820 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
821 require.NoError(t, err)
822 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
823 // check if the announce-list is here instead. TODO: Add nodes.
824 assert.Len(t, tt.metainfo.AnnounceList, 5)
825 // There are 6 nodes in the torrent file.
826 assert.EqualValues(t, 6, cl.DHT().NumNodes()+cl.DHT().Stats().OutstandingTransactions)
829 type testDownloadCancelParams struct {
830 ExportClientStatus bool
831 SetLeecherStorageCapacity bool
832 LeecherStorageCapacity int64
836 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
837 greetingTempDir, mi := testutil.GreetingTestTorrent()
838 defer os.RemoveAll(greetingTempDir)
839 cfg := TestingConfig()
841 cfg.DataDir = greetingTempDir
842 seeder, err := NewClient(cfg)
843 require.NoError(t, err)
845 if ps.ExportClientStatus {
846 testutil.ExportStatusWriter(seeder, "s")
848 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
849 seederTorrent.VerifyData()
850 leecherDataDir, err := ioutil.TempDir("", "")
851 require.NoError(t, err)
852 defer os.RemoveAll(leecherDataDir)
853 fc, err := filecache.NewCache(leecherDataDir)
854 require.NoError(t, err)
855 if ps.SetLeecherStorageCapacity {
856 fc.SetCapacity(ps.LeecherStorageCapacity)
858 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
859 cfg.DataDir = leecherDataDir
860 leecher, _ := NewClient(cfg)
861 defer leecher.Close()
862 if ps.ExportClientStatus {
863 testutil.ExportStatusWriter(leecher, "l")
865 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
866 ret = TorrentSpecFromMetaInfo(mi)
870 require.NoError(t, err)
872 psc := leecherGreeting.SubscribePieceStateChanges()
875 leecherGreeting.cl.mu.Lock()
876 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
878 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
880 leecherGreeting.cl.mu.Unlock()
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),
929 Storage: badStorage{},
931 require.NoError(t, err)
937 assert.NoError(t, cn.peerSentHave(0))
938 assert.Error(t, cn.peerSentHave(1))
941 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
942 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
943 defer os.RemoveAll(greetingTempDir)
944 cfg := TestingConfig()
945 cfg.DataDir = greetingTempDir
946 seeder, err := NewClient(TestingConfig())
947 require.NoError(t, err)
948 seeder.AddTorrentSpec(&TorrentSpec{
949 InfoBytes: greetingMetainfo.InfoBytes,
953 func TestPrepareTrackerAnnounce(t *testing.T) {
955 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
956 require.NoError(t, err)
957 assert.False(t, blocked)
958 assert.EqualValues(t, "localhost:1234", host)
959 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
962 // Check that when the listen port is 0, all the protocols listened on have
963 // the same port, and it isn't zero.
964 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
965 cl, err := NewClient(TestingConfig())
966 require.NoError(t, err)
968 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
969 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
972 func TestClientDynamicListenTCPOnly(t *testing.T) {
973 cfg := TestingConfig()
974 cfg.DisableUTP = 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.utpSock)
982 func TestClientDynamicListenUTPOnly(t *testing.T) {
983 cfg := TestingConfig()
984 cfg.DisableTCP = true
985 cl, err := NewClient(cfg)
986 require.NoError(t, err)
988 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
989 assert.Nil(t, cl.tcpListener)
992 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
993 cfg := TestingConfig()
994 cfg.DisableTCP = true
995 cfg.DisableUTP = true
996 cl, err := NewClient(cfg)
997 require.NoError(t, err)
999 assert.Nil(t, cl.ListenAddr())
1002 func addClientPeer(t *Torrent, cl *Client) {
1005 IP: missinggo.AddrIP(cl.ListenAddr()),
1006 Port: missinggo.AddrPort(cl.ListenAddr()),
1011 func totalConns(tts []*Torrent) (ret int) {
1012 for _, tt := range tts {
1014 ret += len(tt.conns)
1020 func TestSetMaxEstablishedConn(t *testing.T) {
1022 ih := testutil.GreetingMetaInfo().HashInfoBytes()
1023 for i := range iter.N(3) {
1024 cl, err := NewClient(TestingConfig())
1025 require.NoError(t, err)
1027 tt, _ := cl.AddTorrentInfoHash(ih)
1028 tt.SetMaxEstablishedConns(2)
1029 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
1030 tts = append(tts, tt)
1032 addPeers := func() {
1033 for i, tt := range tts {
1034 for _, _tt := range tts[:i] {
1035 addClientPeer(tt, _tt.cl)
1039 waitTotalConns := func(num int) {
1040 for totalConns(tts) != num {
1041 time.Sleep(time.Millisecond)
1046 tts[0].SetMaxEstablishedConns(1)
1048 tts[0].SetMaxEstablishedConns(0)
1050 tts[0].SetMaxEstablishedConns(1)
1053 tts[0].SetMaxEstablishedConns(2)
1058 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1059 os.MkdirAll(dir, 0770)
1060 file, err := os.Create(filepath.Join(dir, name))
1061 require.NoError(t, err)
1062 file.Write([]byte(name))
1064 mi := metainfo.MetaInfo{}
1066 info := metainfo.Info{PieceLength: 256 * 1024}
1067 err = info.BuildFromFilePath(filepath.Join(dir, name))
1068 require.NoError(t, err)
1069 mi.InfoBytes, err = bencode.Marshal(info)
1070 require.NoError(t, err)
1071 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1072 tr, err := cl.AddTorrent(&mi)
1073 require.NoError(t, err)
1074 require.True(t, tr.Seeding())
1079 // https://github.com/anacrolix/torrent/issues/114
1080 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1081 cfg := TestingConfig()
1082 cfg.DisableUTP = true
1084 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1085 cfg.ForceEncryption = true
1086 os.Mkdir(cfg.DataDir, 0755)
1087 server, err := NewClient(cfg)
1088 require.NoError(t, err)
1089 defer server.Close()
1090 testutil.ExportStatusWriter(server, "s")
1091 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1092 makeMagnet(t, server, cfg.DataDir, "test2")
1093 cfg = TestingConfig()
1094 cfg.DisableUTP = true
1095 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()),
1112 func TestClientAddressInUse(t *testing.T) {
1113 s, _ := NewUtpSocket("udp", ":50007")
1117 cfg := TestingConfig()
1118 cfg.ListenAddr = ":50007"
1119 cl, err := NewClient(cfg)
1120 require.Error(t, err)