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/anacrolix/utp"
23 "github.com/bradfitz/iter"
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
27 "github.com/anacrolix/torrent/bencode"
28 "github.com/anacrolix/torrent/dht"
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 var TestingConfig = Config{
40 ListenAddr: "localhost:0",
42 DisableTrackers: true,
43 DataDir: "/tmp/anacrolix",
44 DHTConfig: dht.ServerConfig{
45 NoDefaultBootstrap: true,
49 func TestClientDefault(t *testing.T) {
50 cl, err := NewClient(&TestingConfig)
51 require.NoError(t, err)
55 func TestAddDropTorrent(t *testing.T) {
56 cl, err := NewClient(&TestingConfig)
57 require.NoError(t, err)
59 dir, mi := testutil.GreetingTestTorrent()
60 defer os.RemoveAll(dir)
61 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
62 require.NoError(t, err)
64 tt.SetMaxEstablishedConns(0)
65 tt.SetMaxEstablishedConns(1)
69 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
73 func TestAddTorrentNoUsableURLs(t *testing.T) {
77 func TestAddPeersToUnknownTorrent(t *testing.T) {
81 func TestPieceHashSize(t *testing.T) {
82 if pieceHash.Size() != 20 {
87 func TestTorrentInitialState(t *testing.T) {
88 dir, mi := testutil.GreetingTestTorrent()
89 defer os.RemoveAll(dir)
91 infoHash: mi.HashInfoBytes(),
92 pieceStateChanges: pubsub.NewPubSub(),
95 tor.storageOpener = storage.NewClient(storage.NewFile("/dev/null"))
96 // Needed to lock for asynchronous piece verification.
98 err := tor.setInfoBytes(mi.InfoBytes)
99 require.NoError(t, err)
100 require.Len(t, tor.pieces, 3)
101 tor.pendAllChunkSpecs(0)
103 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
105 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
108 func TestUnmarshalPEXMsg(t *testing.T) {
109 var m peerExchangeMessage
110 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
113 if len(m.Added) != 2 {
116 if m.Added[0].Port != 0x506 {
121 func TestReducedDialTimeout(t *testing.T) {
122 for _, _case := range []struct {
126 ExpectedReduced time.Duration
128 {nominalDialTimeout, 40, 0, nominalDialTimeout},
129 {nominalDialTimeout, 40, 1, nominalDialTimeout},
130 {nominalDialTimeout, 40, 39, nominalDialTimeout},
131 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
132 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
133 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
135 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
136 expected := _case.ExpectedReduced
137 if expected < minDialTimeout {
138 expected = minDialTimeout
140 if reduced != expected {
141 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
146 func TestUTPRawConn(t *testing.T) {
147 l, err := utp.NewSocket("udp", "")
160 // Connect a UTP peer to see if the RawConn will still work.
161 s, _ := utp.NewSocket("udp", "")
163 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
165 t.Fatalf("error dialing utp listener: %s", err)
167 defer utpPeer.Close()
168 peer, err := net.ListenPacket("udp", ":0")
175 // How many messages to send. I've set this to double the channel buffer
176 // size in the raw packetConn.
178 readerStopped := make(chan struct{})
179 // The reader goroutine.
181 defer close(readerStopped)
182 b := make([]byte, 500)
183 for i := 0; i < N; i++ {
184 n, _, err := l.ReadFrom(b)
186 t.Fatalf("error reading from raw conn: %s", err)
190 fmt.Sscan(string(b[:n]), &d)
192 log.Printf("got wrong number: expected %d, got %d", i, d)
196 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
200 for i := 0; i < N; i++ {
201 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
205 time.Sleep(time.Microsecond)
208 case <-readerStopped:
209 case <-time.After(time.Second):
210 t.Fatal("reader timed out")
212 if msgsReceived != N {
213 t.Fatalf("messages received: %d", msgsReceived)
217 func TestTwoClientsArbitraryPorts(t *testing.T) {
218 for i := 0; i < 2; i++ {
219 cl, err := NewClient(&TestingConfig)
227 func TestAddDropManyTorrents(t *testing.T) {
228 cl, err := NewClient(&TestingConfig)
229 require.NoError(t, err)
231 for i := range iter.N(1000) {
233 binary.PutVarint(spec.InfoHash[:], int64(i))
234 tt, new, err := cl.AddTorrentSpec(&spec)
235 assert.NoError(t, err)
241 type FileCacheClientStorageFactoryParams struct {
244 Wrapper func(*filecache.Cache) storage.ClientImpl
247 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
248 return func(dataDir string) storage.ClientImpl {
249 fc, err := filecache.NewCache(dataDir)
254 fc.SetCapacity(ps.Capacity)
256 return ps.Wrapper(fc)
260 type storageFactory func(string) storage.ClientImpl
262 func TestClientTransferDefault(t *testing.T) {
263 testClientTransfer(t, testClientTransferParams{
264 ExportClientStatus: true,
265 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
266 Wrapper: fileCachePieceResourceStorage,
271 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
272 return storage.NewResourcePieces(fc.AsResourceProvider())
275 func fileCachePieceFileStorage(fc *filecache.Cache) storage.ClientImpl {
276 return storage.NewFileStorePieces(fc.AsFileStore())
279 func TestClientTransferSmallCache(t *testing.T) {
280 testClientTransfer(t, testClientTransferParams{
281 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
283 // Going below the piece length means it can't complete a piece so
284 // that it can be hashed.
286 Wrapper: fileCachePieceResourceStorage,
289 // Can't readahead too far or the cache will thrash and drop data we
292 ExportClientStatus: true,
296 func TestClientTransferVarious(t *testing.T) {
297 for _, ls := range []storageFactory{
298 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
299 Wrapper: fileCachePieceFileStorage,
301 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
302 Wrapper: fileCachePieceResourceStorage,
306 for _, ss := range []func(string) storage.ClientImpl{
310 for _, responsive := range []bool{false, true} {
311 testClientTransfer(t, testClientTransferParams{
312 Responsive: responsive,
316 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
317 testClientTransfer(t, testClientTransferParams{
319 Responsive: responsive,
321 Readahead: readahead,
330 type testClientTransferParams struct {
334 ExportClientStatus bool
335 LeecherStorage func(string) storage.ClientImpl
336 SeederStorage func(string) storage.ClientImpl
339 // Creates a seeder and a leecher, and ensures the data transfers when a read
340 // is attempted on the leecher.
341 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
342 greetingTempDir, mi := testutil.GreetingTestTorrent()
343 defer os.RemoveAll(greetingTempDir)
344 // Create seeder and a Torrent.
347 // cfg.ListenAddr = "localhost:4000"
348 if ps.SeederStorage != nil {
349 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
351 cfg.DataDir = greetingTempDir
353 seeder, err := NewClient(&cfg)
354 require.NoError(t, err)
356 if ps.ExportClientStatus {
357 testutil.ExportStatusWriter(seeder, "s")
359 // seederTorrent, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
360 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
361 require.NoError(t, err)
363 // Create leecher and a Torrent.
364 leecherDataDir, err := ioutil.TempDir("", "")
365 require.NoError(t, err)
366 defer os.RemoveAll(leecherDataDir)
367 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
368 // cfg.ListenAddr = "localhost:4001"
369 leecher, err := NewClient(&cfg)
370 require.NoError(t, err)
371 defer leecher.Close()
372 if ps.ExportClientStatus {
373 testutil.ExportStatusWriter(leecher, "l")
375 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
376 ret = TorrentSpecFromMetaInfo(mi)
378 ret.Storage = storage.NewFile(leecherDataDir)
381 require.NoError(t, err)
383 // Now do some things with leecher and seeder.
384 addClientPeer(leecherGreeting, seeder)
385 r := leecherGreeting.NewReader()
391 r.SetReadahead(ps.Readahead)
393 assertReadAllGreeting(t, r)
394 // After one read through, we can assume certain torrent statistics.
395 // These are not a strict requirement. It is however interesting to
397 // t.Logf("%#v", seederTorrent.Stats())
398 // assert.EqualValues(t, 13, seederTorrent.Stats().DataBytesWritten)
399 // assert.EqualValues(t, 8, seederTorrent.Stats().ChunksWritten)
400 // assert.EqualValues(t, 13, leecherGreeting.Stats().DataBytesRead)
401 // assert.EqualValues(t, 8, leecherGreeting.Stats().ChunksRead)
402 // Read through again for the cases where the torrent data size exceeds
403 // the size of the cache.
404 assertReadAllGreeting(t, r)
407 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
408 pos, err := r.Seek(0, os.SEEK_SET)
409 assert.NoError(t, err)
410 assert.EqualValues(t, 0, pos)
411 _greeting, err := ioutil.ReadAll(r)
412 assert.NoError(t, err)
413 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
416 // Check that after completing leeching, a leecher transitions to a seeding
417 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
418 func TestSeedAfterDownloading(t *testing.T) {
419 greetingTempDir, mi := testutil.GreetingTestTorrent()
420 defer os.RemoveAll(greetingTempDir)
423 cfg.DataDir = greetingTempDir
424 seeder, err := NewClient(&cfg)
425 require.NoError(t, err)
427 testutil.ExportStatusWriter(seeder, "s")
428 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
429 cfg.DataDir, err = ioutil.TempDir("", "")
430 require.NoError(t, err)
431 defer os.RemoveAll(cfg.DataDir)
432 leecher, err := NewClient(&cfg)
433 require.NoError(t, err)
434 defer leecher.Close()
435 testutil.ExportStatusWriter(leecher, "l")
437 // cfg.TorrentDataOpener = nil
438 cfg.DataDir, err = ioutil.TempDir("", "")
439 require.NoError(t, err)
440 defer os.RemoveAll(cfg.DataDir)
441 leecherLeecher, _ := NewClient(&cfg)
442 defer leecherLeecher.Close()
443 testutil.ExportStatusWriter(leecherLeecher, "ll")
444 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
445 ret = TorrentSpecFromMetaInfo(mi)
449 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
450 ret = TorrentSpecFromMetaInfo(mi)
454 // Simultaneously DownloadAll in Leecher, and read the contents
455 // consecutively in LeecherLeecher. This non-deterministically triggered a
456 // case where the leecher wouldn't unchoke the LeecherLeecher.
457 var wg sync.WaitGroup
463 b, err := ioutil.ReadAll(r)
464 require.NoError(t, err)
465 assert.EqualValues(t, testutil.GreetingFileContents, b)
467 addClientPeer(leecherGreeting, seeder)
468 addClientPeer(leecherGreeting, leecherLeecher)
472 leecherGreeting.DownloadAll()
478 func TestMergingTrackersByAddingSpecs(t *testing.T) {
479 cl, err := NewClient(&TestingConfig)
480 require.NoError(t, err)
482 spec := TorrentSpec{}
483 T, new, _ := cl.AddTorrentSpec(&spec)
487 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
488 _, new, _ = cl.AddTorrentSpec(&spec)
490 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
491 // Because trackers are disabled in TestingConfig.
492 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
495 type badStorage struct{}
497 func (bs badStorage) OpenTorrent(*metainfo.Info, metainfo.Hash) (storage.TorrentImpl, error) {
501 func (bs badStorage) Close() error {
505 func (bs badStorage) Piece(p metainfo.Piece) storage.PieceImpl {
506 return badStoragePiece{p}
509 type badStoragePiece struct {
513 func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
517 func (p badStoragePiece) GetIsComplete() bool {
521 func (p badStoragePiece) MarkComplete() error {
522 return errors.New("psyyyyyyyche")
525 func (p badStoragePiece) MarkNotComplete() error {
526 return errors.New("psyyyyyyyche")
529 func (p badStoragePiece) randomlyTruncatedDataString() string {
530 return "hello, world\n"[:rand.Intn(14)]
533 func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
534 r := strings.NewReader(p.randomlyTruncatedDataString())
535 return r.ReadAt(b, off+p.p.Offset())
538 // We read from a piece which is marked completed, but is missing data.
539 func TestCompletedPieceWrongSize(t *testing.T) {
541 cfg.DefaultStorage = badStorage{}
542 cl, err := NewClient(&cfg)
543 require.NoError(t, err)
545 info := metainfo.Info{
547 Pieces: make([]byte, 20),
548 Files: []metainfo.FileInfo{
549 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
552 b, err := bencode.Marshal(info)
553 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
555 InfoHash: metainfo.HashBytes(b),
557 require.NoError(t, err)
562 b, err = ioutil.ReadAll(r)
564 assert.NoError(t, err)
567 func BenchmarkAddLargeTorrent(b *testing.B) {
569 cfg.DisableTCP = true
570 cfg.DisableUTP = true
571 cfg.ListenAddr = "redonk"
572 cl, err := NewClient(&cfg)
573 require.NoError(b, err)
575 for range iter.N(b.N) {
576 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
584 func TestResponsive(t *testing.T) {
585 seederDataDir, mi := testutil.GreetingTestTorrent()
586 defer os.RemoveAll(seederDataDir)
589 cfg.DataDir = seederDataDir
590 seeder, err := NewClient(&cfg)
593 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
594 leecherDataDir, err := ioutil.TempDir("", "")
596 defer os.RemoveAll(leecherDataDir)
598 cfg.DataDir = leecherDataDir
599 leecher, err := NewClient(&cfg)
601 defer leecher.Close()
602 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
603 ret = TorrentSpecFromMetaInfo(mi)
607 addClientPeer(leecherTorrent, seeder)
608 reader := leecherTorrent.NewReader()
610 reader.SetReadahead(0)
611 reader.SetResponsive()
613 _, err = reader.Seek(3, os.SEEK_SET)
614 require.NoError(t, err)
615 _, err = io.ReadFull(reader, b)
617 assert.EqualValues(t, "lo", string(b))
618 _, err = reader.Seek(11, os.SEEK_SET)
619 require.NoError(t, err)
620 n, err := io.ReadFull(reader, b)
622 assert.EqualValues(t, 2, n)
623 assert.EqualValues(t, "d\n", string(b))
626 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
627 seederDataDir, mi := testutil.GreetingTestTorrent()
628 defer os.RemoveAll(seederDataDir)
631 cfg.DataDir = seederDataDir
632 seeder, err := NewClient(&cfg)
635 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
636 leecherDataDir, err := ioutil.TempDir("", "")
638 defer os.RemoveAll(leecherDataDir)
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 go leecherTorrent.Drop()
661 _, err = reader.Seek(11, os.SEEK_SET)
662 require.NoError(t, err)
663 n, err := reader.Read(b)
664 assert.EqualError(t, err, "torrent closed")
665 assert.EqualValues(t, 0, n)
668 func TestDHTInheritBlocklist(t *testing.T) {
669 ipl := iplist.New(nil)
670 require.NotNil(t, ipl)
672 cfg.IPBlocklist = ipl
674 cl, err := NewClient(&cfg)
675 require.NoError(t, err)
677 require.Equal(t, ipl, cl.DHT().IPBlocklist())
680 // Check that stuff is merged in subsequent AddTorrentSpec for the same
682 func TestAddTorrentSpecMerging(t *testing.T) {
683 cl, err := NewClient(&TestingConfig)
684 require.NoError(t, err)
686 dir, mi := testutil.GreetingTestTorrent()
687 defer os.RemoveAll(dir)
688 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
689 InfoHash: mi.HashInfoBytes(),
691 require.NoError(t, err)
693 require.Nil(t, tt.Info())
694 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
695 require.NoError(t, err)
696 require.False(t, new)
697 require.NotNil(t, tt.Info())
700 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
701 dir, mi := testutil.GreetingTestTorrent()
703 cl, _ := NewClient(&TestingConfig)
705 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
706 InfoHash: mi.HashInfoBytes(),
709 assert.EqualValues(t, 0, len(cl.Torrents()))
717 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
718 for i := range iter.N(info.NumPieces()) {
720 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
724 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
725 fileCacheDir, err := ioutil.TempDir("", "")
726 require.NoError(t, err)
727 defer os.RemoveAll(fileCacheDir)
728 fileCache, err := filecache.NewCache(fileCacheDir)
729 require.NoError(t, err)
730 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
731 defer os.RemoveAll(greetingDataTempDir)
732 filePieceStore := csf(fileCache)
733 info, err := greetingMetainfo.UnmarshalInfo()
734 require.NoError(t, err)
735 ih := greetingMetainfo.HashInfoBytes()
736 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
737 require.NoError(t, err)
738 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
739 // require.Equal(t, len(testutil.GreetingFileContents), written)
740 // require.NoError(t, err)
741 for i := 0; i < info.NumPieces(); i++ {
743 if alreadyCompleted {
744 err := greetingData.Piece(p).MarkComplete()
745 assert.NoError(t, err)
749 // TODO: Disable network option?
750 cfg.DisableTCP = true
751 cfg.DisableUTP = true
752 cfg.DefaultStorage = filePieceStore
753 cl, err := NewClient(&cfg)
754 require.NoError(t, err)
756 tt, err := cl.AddTorrent(greetingMetainfo)
757 require.NoError(t, err)
758 psrs := tt.PieceStateRuns()
759 assert.Len(t, psrs, 1)
760 assert.EqualValues(t, 3, psrs[0].Length)
761 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
762 if alreadyCompleted {
764 b, err := ioutil.ReadAll(r)
765 assert.NoError(t, err)
766 assert.EqualValues(t, testutil.GreetingFileContents, b)
770 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
771 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceFileStorage)
772 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
775 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
776 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceFileStorage)
777 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
780 func TestAddMetainfoWithNodes(t *testing.T) {
783 // For now, we want to just jam the nodes into the table, without
784 // verifying them first. Also the DHT code doesn't support mixing secure
785 // and insecure nodes if security is enabled (yet).
786 cfg.DHTConfig.NoSecurity = true
787 cl, err := NewClient(&cfg)
788 require.NoError(t, err)
790 assert.EqualValues(t, cl.DHT().NumNodes(), 0)
791 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
792 require.NoError(t, err)
793 assert.Len(t, tt.metainfo.AnnounceList, 5)
794 assert.EqualValues(t, 6, cl.DHT().NumNodes())
797 type testDownloadCancelParams struct {
798 ExportClientStatus bool
799 SetLeecherStorageCapacity bool
800 LeecherStorageCapacity int64
804 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
805 greetingTempDir, mi := testutil.GreetingTestTorrent()
806 defer os.RemoveAll(greetingTempDir)
809 cfg.DataDir = greetingTempDir
810 seeder, err := NewClient(&cfg)
811 require.NoError(t, err)
813 if ps.ExportClientStatus {
814 testutil.ExportStatusWriter(seeder, "s")
816 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
817 leecherDataDir, err := ioutil.TempDir("", "")
818 require.NoError(t, err)
819 defer os.RemoveAll(leecherDataDir)
820 fc, err := filecache.NewCache(leecherDataDir)
821 require.NoError(t, err)
822 if ps.SetLeecherStorageCapacity {
823 fc.SetCapacity(ps.LeecherStorageCapacity)
825 cfg.DefaultStorage = storage.NewFileStorePieces(fc.AsFileStore())
826 cfg.DataDir = leecherDataDir
827 leecher, _ := NewClient(&cfg)
828 defer leecher.Close()
829 if ps.ExportClientStatus {
830 testutil.ExportStatusWriter(leecher, "l")
832 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
833 ret = TorrentSpecFromMetaInfo(mi)
837 require.NoError(t, err)
839 psc := leecherGreeting.SubscribePieceStateChanges()
841 leecherGreeting.DownloadAll()
843 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
845 addClientPeer(leecherGreeting, seeder)
846 completes := make(map[int]bool, 3)
849 // started := time.Now()
851 case _v := <-psc.Values:
852 // log.Print(time.Since(started))
853 v := _v.(PieceStateChange)
854 completes[v.Index] = v.Complete
855 case <-time.After(100 * time.Millisecond):
860 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
862 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
867 func TestTorrentDownloadAll(t *testing.T) {
868 testDownloadCancel(t, testDownloadCancelParams{})
871 func TestTorrentDownloadAllThenCancel(t *testing.T) {
872 testDownloadCancel(t, testDownloadCancelParams{
877 // Ensure that it's an error for a peer to send an invalid have message.
878 func TestPeerInvalidHave(t *testing.T) {
879 cl, err := NewClient(&TestingConfig)
880 require.NoError(t, err)
882 info := metainfo.Info{
884 Pieces: make([]byte, 20),
885 Files: []metainfo.FileInfo{{Length: 1}},
887 infoBytes, err := bencode.Marshal(info)
888 require.NoError(t, err)
889 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
890 InfoBytes: infoBytes,
891 InfoHash: metainfo.HashBytes(infoBytes),
893 require.NoError(t, err)
899 assert.NoError(t, cn.peerSentHave(0))
900 assert.Error(t, cn.peerSentHave(1))
903 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
904 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
905 defer os.RemoveAll(greetingTempDir)
907 cfg.DataDir = greetingTempDir
908 seeder, err := NewClient(&TestingConfig)
909 require.NoError(t, err)
910 seeder.AddTorrentSpec(&TorrentSpec{
911 InfoBytes: greetingMetainfo.InfoBytes,
915 func TestPrepareTrackerAnnounce(t *testing.T) {
917 blocked, urlToUse, host, err := cl.prepareTrackerAnnounceUnlocked("http://localhost:1234/announce?herp")
918 require.NoError(t, err)
919 assert.False(t, blocked)
920 assert.EqualValues(t, "localhost:1234", host)
921 assert.EqualValues(t, "http://127.0.0.1:1234/announce?herp", urlToUse)
924 // Check that when the listen port is 0, all the protocols listened on have
925 // the same port, and it isn't zero.
926 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
927 cl, err := NewClient(&TestingConfig)
928 require.NoError(t, err)
930 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
931 assert.Equal(t, missinggo.AddrPort(cl.utpSock.Addr()), missinggo.AddrPort(cl.tcpListener.Addr()))
934 func TestClientDynamicListenTCPOnly(t *testing.T) {
936 cfg.DisableUTP = true
937 cl, err := NewClient(&cfg)
938 require.NoError(t, err)
940 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
941 assert.Nil(t, cl.utpSock)
944 func TestClientDynamicListenUTPOnly(t *testing.T) {
946 cfg.DisableTCP = true
947 cl, err := NewClient(&cfg)
948 require.NoError(t, err)
950 assert.NotEqual(t, 0, missinggo.AddrPort(cl.ListenAddr()))
951 assert.Nil(t, cl.tcpListener)
954 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
956 cfg.DisableTCP = true
957 cfg.DisableUTP = true
958 cl, err := NewClient(&cfg)
959 require.NoError(t, err)
961 assert.Nil(t, cl.ListenAddr())
964 func addClientPeer(t *Torrent, cl *Client) {
967 IP: missinggo.AddrIP(cl.ListenAddr()),
968 Port: missinggo.AddrPort(cl.ListenAddr()),
973 func printConnPeerCounts(t *Torrent) {
975 log.Println(len(t.conns), len(t.peers))
979 func totalConns(tts []*Torrent) (ret int) {
980 for _, tt := range tts {
988 func TestSetMaxEstablishedConn(t *testing.T) {
990 ih := testutil.GreetingMetaInfo().HashInfoBytes()
992 for i := range iter.N(3) {
993 cl, err := NewClient(&cfg)
994 require.NoError(t, err)
996 tt, _ := cl.AddTorrentInfoHash(ih)
997 tt.SetMaxEstablishedConns(2)
998 testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))
999 tts = append(tts, tt)
1001 addPeers := func() {
1002 for i, tt := range tts {
1003 for _, _tt := range tts[:i] {
1004 addClientPeer(tt, _tt.cl)
1008 waitTotalConns := func(num int) {
1009 for totalConns(tts) != num {
1010 time.Sleep(time.Millisecond)
1015 tts[0].SetMaxEstablishedConns(1)
1017 tts[0].SetMaxEstablishedConns(0)
1019 tts[0].SetMaxEstablishedConns(1)
1022 tts[0].SetMaxEstablishedConns(2)
1027 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1029 file, err := os.Create(dir + "/" + name)
1030 require.NoError(t, err)
1031 file.Write([]byte(name))
1033 mi := metainfo.MetaInfo{}
1035 info := metainfo.Info{ PieceLength: 256 * 1024 }
1036 err = info.BuildFromFilePath(dir + "/" + name)
1037 require.NoError(t, err)
1038 mi.InfoBytes, err = bencode.Marshal(info)
1039 require.NoError(t, err)
1040 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1041 tr, err := cl.AddTorrent(&mi)
1042 require.NoError(t, err)
1043 assert.True(t, tr.Seeding())
1047 // https://github.com/anacrolix/torrent/issues/114
1048 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1049 cfg := TestingConfig
1050 cfg.DisableUTP = true
1052 cfg.DataDir = cfg.DataDir + "/server"
1054 cfg.ForceEncryption = true
1055 os.Mkdir(cfg.DataDir, 0755)
1056 server, err := NewClient(&cfg)
1057 defer server.Close()
1058 require.NoError(t, err)
1059 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1060 makeMagnet(t, server, cfg.DataDir, "test2")
1062 cfg.DisableUTP = true
1063 cfg.DataDir = cfg.DataDir + "/client"
1065 cfg.ForceEncryption = true
1066 client, err := NewClient(&cfg)
1067 require.NoError(t, err)
1068 defer client.Close()
1069 tr, err := client.AddMagnet(magnet1)
1070 require.NoError(t, err)
1071 tr.AddPeers([]Peer{Peer{
1072 IP: missinggo.AddrIP(server.ListenAddr()),
1073 Port: missinggo.AddrPort(server.ListenAddr()),