19 _ "github.com/anacrolix/envpprof"
20 "github.com/anacrolix/missinggo"
21 . "github.com/anacrolix/missinggo"
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 NoDefaultBlocklist: true,
44 DisableMetainfoCache: true,
46 DHTConfig: dht.ServerConfig{
47 NoDefaultBootstrap: true,
51 func TestClientDefault(t *testing.T) {
52 cl, err := NewClient(&TestingConfig)
59 func TestAddDropTorrent(t *testing.T) {
60 cl, err := NewClient(&TestingConfig)
65 dir, mi := testutil.GreetingTestTorrent()
66 defer os.RemoveAll(dir)
67 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
77 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
81 func TestAddTorrentNoUsableURLs(t *testing.T) {
85 func TestAddPeersToUnknownTorrent(t *testing.T) {
89 func TestPieceHashSize(t *testing.T) {
90 if pieceHash.Size() != 20 {
95 func TestTorrentInitialState(t *testing.T) {
96 dir, mi := testutil.GreetingTestTorrent()
97 defer os.RemoveAll(dir)
98 tor := newTorrent(func() (ih InfoHash) {
99 missinggo.CopyExact(ih[:], mi.Info.Hash)
103 tor.storage = storage.NewFile(dir)
104 // Needed to lock for asynchronous piece verification.
106 err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
107 require.NoError(t, err)
108 require.Len(t, tor.Pieces, 3)
109 tor.pendAllChunkSpecs(0)
110 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
111 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
114 func TestUnmarshalPEXMsg(t *testing.T) {
115 var m peerExchangeMessage
116 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
119 if len(m.Added) != 2 {
122 if m.Added[0].Port != 0x506 {
127 func TestReducedDialTimeout(t *testing.T) {
128 for _, _case := range []struct {
132 ExpectedReduced time.Duration
134 {nominalDialTimeout, 40, 0, nominalDialTimeout},
135 {nominalDialTimeout, 40, 1, nominalDialTimeout},
136 {nominalDialTimeout, 40, 39, nominalDialTimeout},
137 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
138 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
139 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
141 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
142 expected := _case.ExpectedReduced
143 if expected < minDialTimeout {
144 expected = minDialTimeout
146 if reduced != expected {
147 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
152 func TestUTPRawConn(t *testing.T) {
153 l, err := utp.NewSocket("udp", "")
166 // Connect a UTP peer to see if the RawConn will still work.
167 s, _ := utp.NewSocket("udp", "")
169 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
171 t.Fatalf("error dialing utp listener: %s", err)
173 defer utpPeer.Close()
174 peer, err := net.ListenPacket("udp", ":0")
181 // How many messages to send. I've set this to double the channel buffer
182 // size in the raw packetConn.
184 readerStopped := make(chan struct{})
185 // The reader goroutine.
187 defer close(readerStopped)
188 b := make([]byte, 500)
189 for i := 0; i < N; i++ {
190 n, _, err := l.ReadFrom(b)
192 t.Fatalf("error reading from raw conn: %s", err)
196 fmt.Sscan(string(b[:n]), &d)
198 log.Printf("got wrong number: expected %d, got %d", i, d)
202 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
206 for i := 0; i < N; i++ {
207 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
211 time.Sleep(time.Microsecond)
214 case <-readerStopped:
215 case <-time.After(time.Second):
216 t.Fatal("reader timed out")
218 if msgsReceived != N {
219 t.Fatalf("messages received: %d", msgsReceived)
223 func TestTwoClientsArbitraryPorts(t *testing.T) {
224 for i := 0; i < 2; i++ {
225 cl, err := NewClient(&TestingConfig)
233 func TestAddDropManyTorrents(t *testing.T) {
234 cl, err := NewClient(&TestingConfig)
235 require.NoError(t, err)
237 for i := range iter.N(1000) {
239 binary.PutVarint(spec.InfoHash[:], int64(i))
240 tt, new, err := cl.AddTorrentSpec(&spec)
241 assert.NoError(t, err)
247 func TestClientTransferDefault(t *testing.T) {
248 testClientTransfer(t, testClientTransferParams{
249 ExportClientStatus: true,
253 func TestClientTransferSmallCache(t *testing.T) {
254 testClientTransfer(t, testClientTransferParams{
255 SetLeecherStorageCapacity: true,
256 // Going below the piece length means it can't complete a piece so
257 // that it can be hashed.
258 LeecherStorageCapacity: 5,
260 // Can't readahead too far or the cache will thrash and drop data we
263 ExportClientStatus: true,
267 func TestClientTransferVarious(t *testing.T) {
268 for _, responsive := range []bool{false, true} {
269 testClientTransfer(t, testClientTransferParams{
270 Responsive: responsive,
272 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
273 testClientTransfer(t, testClientTransferParams{
274 Responsive: responsive,
276 Readahead: readahead,
282 type testClientTransferParams struct {
286 ExportClientStatus bool
287 SetLeecherStorageCapacity bool
288 LeecherStorageCapacity int64
291 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
292 greetingTempDir, mi := testutil.GreetingTestTorrent()
293 defer os.RemoveAll(greetingTempDir)
296 cfg.DataDir = greetingTempDir
297 seeder, err := NewClient(&cfg)
298 require.NoError(t, err)
300 if ps.ExportClientStatus {
301 testutil.ExportStatusWriter(seeder, "s")
303 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
304 require.NoError(t, err)
306 leecherDataDir, err := ioutil.TempDir("", "")
307 require.NoError(t, err)
308 defer os.RemoveAll(leecherDataDir)
309 // cfg.TorrentDataOpener = func() TorrentDataOpener {
310 // fc, err := filecache.NewCache(leecherDataDir)
311 // require.NoError(t, err)
312 // if ps.SetLeecherStorageCapacity {
313 // fc.SetCapacity(ps.LeecherStorageCapacity)
315 // store := pieceStore.New(fileCacheDataBackend.New(fc))
316 // return func(mi *metainfo.Info) storage.I {
317 // return store.OpenTorrentData(mi)
320 leecher, err := NewClient(&cfg)
321 require.NoError(t, err)
322 defer leecher.Close()
323 if ps.ExportClientStatus {
324 testutil.ExportStatusWriter(leecher, "l")
326 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
327 ret = TorrentSpecFromMetaInfo(mi)
329 ret.Storage = storage.NewFile(leecherDataDir)
332 require.NoError(t, err)
334 leecherGreeting.AddPeers([]Peer{
336 IP: missinggo.AddrIP(seeder.ListenAddr()),
337 Port: missinggo.AddrPort(seeder.ListenAddr()),
340 r := leecherGreeting.NewReader()
346 r.SetReadahead(ps.Readahead)
348 for range iter.N(2) {
349 pos, err := r.Seek(0, os.SEEK_SET)
350 assert.NoError(t, err)
351 assert.EqualValues(t, 0, pos)
352 _greeting, err := ioutil.ReadAll(r)
353 assert.NoError(t, err)
354 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
358 // Check that after completing leeching, a leecher transitions to a seeding
359 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
360 func TestSeedAfterDownloading(t *testing.T) {
361 greetingTempDir, mi := testutil.GreetingTestTorrent()
362 defer os.RemoveAll(greetingTempDir)
365 cfg.DataDir = greetingTempDir
366 seeder, err := NewClient(&cfg)
368 testutil.ExportStatusWriter(seeder, "s")
369 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
370 cfg.DataDir, err = ioutil.TempDir("", "")
371 require.NoError(t, err)
372 defer os.RemoveAll(cfg.DataDir)
373 leecher, _ := NewClient(&cfg)
374 defer leecher.Close()
375 testutil.ExportStatusWriter(leecher, "l")
377 // cfg.TorrentDataOpener = nil
378 cfg.DataDir, err = ioutil.TempDir("", "")
379 require.NoError(t, err)
380 defer os.RemoveAll(cfg.DataDir)
381 leecherLeecher, _ := NewClient(&cfg)
382 defer leecherLeecher.Close()
383 testutil.ExportStatusWriter(leecherLeecher, "ll")
384 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
385 ret = TorrentSpecFromMetaInfo(mi)
389 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
390 ret = TorrentSpecFromMetaInfo(mi)
394 // Simultaneously DownloadAll in Leecher, and read the contents
395 // consecutively in LeecherLeecher. This non-deterministically triggered a
396 // case where the leecher wouldn't unchoke the LeecherLeecher.
397 var wg sync.WaitGroup
403 b, err := ioutil.ReadAll(r)
404 require.NoError(t, err)
405 assert.EqualValues(t, testutil.GreetingFileContents, b)
407 leecherGreeting.AddPeers([]Peer{
409 IP: missinggo.AddrIP(seeder.ListenAddr()),
410 Port: missinggo.AddrPort(seeder.ListenAddr()),
413 IP: missinggo.AddrIP(leecherLeecher.ListenAddr()),
414 Port: missinggo.AddrPort(leecherLeecher.ListenAddr()),
420 leecherGreeting.DownloadAll()
426 func TestReadaheadPieces(t *testing.T) {
427 for _, case_ := range []struct {
428 readaheadBytes, pieceLength int64
431 {5 * 1024 * 1024, 256 * 1024, 19},
432 {5 * 1024 * 1024, 5 * 1024 * 1024, 1},
433 {5*1024*1024 - 1, 5 * 1024 * 1024, 1},
434 {5 * 1024 * 1024, 5*1024*1024 - 1, 2},
435 {0, 5 * 1024 * 1024, 0},
436 {5 * 1024 * 1024, 1048576, 4},
438 pieces := readaheadPieces(case_.readaheadBytes, case_.pieceLength)
439 assert.Equal(t, case_.readaheadPieces, pieces, "%v", case_)
443 func TestMergingTrackersByAddingSpecs(t *testing.T) {
444 cl, err := NewClient(&TestingConfig)
445 require.NoError(t, err)
447 spec := TorrentSpec{}
448 T, new, _ := cl.AddTorrentSpec(&spec)
452 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
453 _, new, _ = cl.AddTorrentSpec(&spec)
457 assert.EqualValues(t, T.torrent.Trackers[0][0], "http://a")
458 assert.EqualValues(t, T.torrent.Trackers[1][0], "udp://b")
461 type badStorage struct{}
463 func (me badStorage) Piece(p metainfo.Piece) storage.Piece {
464 return badStoragePiece{p}
467 type badStoragePiece struct {
471 func (me badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
475 func (me badStoragePiece) GetIsComplete() bool {
479 func (me badStoragePiece) MarkComplete() error {
480 return errors.New("psyyyyyyyche")
483 func (me badStoragePiece) randomlyTruncatedDataString() string {
484 return "hello, world\n"[:rand.Intn(14)]
487 func (me badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
488 r := strings.NewReader(me.randomlyTruncatedDataString())
489 return r.ReadAt(b, off+me.p.Offset())
492 // We read from a piece which is marked completed, but is missing data.
493 func TestCompletedPieceWrongSize(t *testing.T) {
495 cfg.DefaultStorage = badStorage{}
496 cl, _ := NewClient(&cfg)
498 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
499 Info: &metainfo.InfoEx{
502 Pieces: make([]byte, 20),
503 Files: []metainfo.FileInfo{
504 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
509 require.NoError(t, err)
514 b, err := ioutil.ReadAll(r)
516 assert.NoError(t, err)
519 func BenchmarkAddLargeTorrent(b *testing.B) {
521 cfg.DisableTCP = true
522 cfg.DisableUTP = true
523 cfg.ListenAddr = "redonk"
524 cl, _ := NewClient(&cfg)
526 for range iter.N(b.N) {
527 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
535 func TestResponsive(t *testing.T) {
536 seederDataDir, mi := testutil.GreetingTestTorrent()
537 defer os.RemoveAll(seederDataDir)
540 cfg.DataDir = seederDataDir
541 seeder, err := NewClient(&cfg)
544 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
545 leecherDataDir, err := ioutil.TempDir("", "")
547 defer os.RemoveAll(leecherDataDir)
549 cfg.DataDir = leecherDataDir
550 leecher, err := NewClient(&cfg)
552 defer leecher.Close()
553 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
554 ret = TorrentSpecFromMetaInfo(mi)
558 leecherTorrent.AddPeers([]Peer{
560 IP: missinggo.AddrIP(seeder.ListenAddr()),
561 Port: missinggo.AddrPort(seeder.ListenAddr()),
564 reader := leecherTorrent.NewReader()
566 reader.SetReadahead(0)
567 reader.SetResponsive()
569 _, err = reader.Seek(3, os.SEEK_SET)
570 require.NoError(t, err)
571 _, err = io.ReadFull(reader, b)
573 assert.EqualValues(t, "lo", string(b))
574 _, err = reader.Seek(11, os.SEEK_SET)
575 require.NoError(t, err)
576 n, err := io.ReadFull(reader, b)
578 assert.EqualValues(t, 2, n)
579 assert.EqualValues(t, "d\n", string(b))
582 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
583 seederDataDir, mi := testutil.GreetingTestTorrent()
584 defer os.RemoveAll(seederDataDir)
587 cfg.DataDir = seederDataDir
588 seeder, err := NewClient(&cfg)
591 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
592 leecherDataDir, err := ioutil.TempDir("", "")
594 defer os.RemoveAll(leecherDataDir)
596 cfg.DataDir = leecherDataDir
597 leecher, err := NewClient(&cfg)
599 defer leecher.Close()
600 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
601 ret = TorrentSpecFromMetaInfo(mi)
605 leecherTorrent.AddPeers([]Peer{
607 IP: missinggo.AddrIP(seeder.ListenAddr()),
608 Port: missinggo.AddrPort(seeder.ListenAddr()),
611 reader := leecherTorrent.NewReader()
613 reader.SetReadahead(0)
614 reader.SetResponsive()
616 _, err = reader.Seek(3, os.SEEK_SET)
617 require.NoError(t, err)
618 _, err = io.ReadFull(reader, b)
620 assert.EqualValues(t, "lo", string(b))
621 go leecherTorrent.Drop()
622 _, err = reader.Seek(11, os.SEEK_SET)
623 require.NoError(t, err)
624 n, err := reader.Read(b)
625 assert.EqualError(t, err, "torrent closed")
626 assert.EqualValues(t, 0, n)
629 func TestDHTInheritBlocklist(t *testing.T) {
630 ipl := iplist.New(nil)
631 require.NotNil(t, ipl)
633 cfg.IPBlocklist = ipl
635 cl, err := NewClient(&cfg)
636 require.NoError(t, err)
638 require.Equal(t, ipl, cl.DHT().IPBlocklist())
641 // Check that stuff is merged in subsequent AddTorrentSpec for the same
643 func TestAddTorrentSpecMerging(t *testing.T) {
644 cl, err := NewClient(&TestingConfig)
645 require.NoError(t, err)
647 dir, mi := testutil.GreetingTestTorrent()
648 defer os.RemoveAll(dir)
650 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
651 tt, new, err := cl.AddTorrentSpec(&ts)
652 require.NoError(t, err)
654 require.Nil(t, tt.Info())
655 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
656 require.NoError(t, err)
657 require.False(t, new)
658 require.NotNil(t, tt.Info())
661 // Check that torrent Info is obtained from the metainfo file cache.
662 func TestAddTorrentMetainfoInCache(t *testing.T) {
664 cfg.DisableMetainfoCache = false
665 cfg.ConfigDir, _ = ioutil.TempDir(os.TempDir(), "")
666 defer os.RemoveAll(cfg.ConfigDir)
667 cl, err := NewClient(&cfg)
668 require.NoError(t, err)
670 dir, mi := testutil.GreetingTestTorrent()
671 defer os.RemoveAll(dir)
672 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
673 require.NoError(t, err)
675 require.NotNil(t, tt.Info())
676 _, err = os.Stat(filepath.Join(cfg.ConfigDir, "torrents", fmt.Sprintf("%x.torrent", mi.Info.Hash)))
677 require.NoError(t, err)
678 // Contains only the infohash.
680 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
681 _, ok := cl.Torrent(ts.InfoHash)
684 _, ok = cl.Torrent(ts.InfoHash)
686 tt, new, err = cl.AddTorrentSpec(&ts)
687 require.NoError(t, err)
689 // Obtained from the metainfo cache.
690 require.NotNil(t, tt.Info())
693 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
694 dir, mi := testutil.GreetingTestTorrent()
696 cl, _ := NewClient(&TestingConfig)
699 CopyExact(&ts.InfoHash, mi.Info.Hash)
700 tt, _, _ := cl.AddTorrentSpec(&ts)
702 assert.EqualValues(t, 0, len(cl.Torrents()))
710 // func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool) {
711 // fileCacheDir, err := ioutil.TempDir("", "")
712 // require.NoError(t, err)
713 // defer os.RemoveAll(fileCacheDir)
714 // fileCache, err := filecache.NewCache(fileCacheDir)
715 // require.NoError(t, err)
716 // greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
717 // defer os.RemoveAll(greetingDataTempDir)
718 // filePieceStore := pieceStore.New(fileCacheDataBackend.New(fileCache))
719 // greetingData := filePieceStore.OpenTorrentData(&greetingMetainfo.Info.Info)
720 // written, err := greetingData.WriteAt([]byte(testutil.GreetingFileContents), 0)
721 // require.Equal(t, len(testutil.GreetingFileContents), written)
722 // require.NoError(t, err)
723 // for i := 0; i < greetingMetainfo.Info.NumPieces(); i++ {
724 // // p := greetingMetainfo.Info.Piece(i)
725 // if alreadyCompleted {
726 // err := greetingData.PieceCompleted(i)
727 // assert.NoError(t, err)
730 // cfg := TestingConfig
731 // // TODO: Disable network option?
732 // cfg.DisableTCP = true
733 // cfg.DisableUTP = true
734 // // cfg.DefaultStorage = filePieceStore
735 // cl, err := NewClient(&cfg)
736 // require.NoError(t, err)
738 // tt, err := cl.AddTorrent(greetingMetainfo)
739 // require.NoError(t, err)
740 // psrs := tt.PieceStateRuns()
741 // assert.Len(t, psrs, 1)
742 // assert.EqualValues(t, 3, psrs[0].Length)
743 // assert.Equal(t, alreadyCompleted, psrs[0].Complete)
744 // if alreadyCompleted {
745 // r := tt.NewReader()
746 // b, err := ioutil.ReadAll(r)
747 // assert.NoError(t, err)
748 // assert.EqualValues(t, testutil.GreetingFileContents, b)
752 // func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
753 // testAddTorrentPriorPieceCompletion(t, true)
756 // func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
757 // testAddTorrentPriorPieceCompletion(t, false)
760 func TestAddMetainfoWithNodes(t *testing.T) {
763 // For now, we want to just jam the nodes into the table, without
764 // verifying them first. Also the DHT code doesn't support mixing secure
765 // and insecure nodes if security is enabled (yet).
766 cfg.DHTConfig.NoSecurity = true
767 cl, err := NewClient(&cfg)
768 require.NoError(t, err)
770 assert.EqualValues(t, cl.DHT().NumNodes(), 0)
771 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
772 require.NoError(t, err)
773 assert.Len(t, tt.torrent.Trackers, 5)
774 assert.EqualValues(t, 6, cl.DHT().NumNodes())
777 type testDownloadCancelParams struct {
781 ExportClientStatus bool
782 SetLeecherStorageCapacity bool
783 LeecherStorageCapacity int64
787 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
788 greetingTempDir, mi := testutil.GreetingTestTorrent()
789 defer os.RemoveAll(greetingTempDir)
792 cfg.DataDir = greetingTempDir
793 seeder, err := NewClient(&cfg)
794 require.NoError(t, err)
796 if ps.ExportClientStatus {
797 testutil.ExportStatusWriter(seeder, "s")
799 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
800 leecherDataDir, err := ioutil.TempDir("", "")
801 require.NoError(t, err)
802 defer os.RemoveAll(leecherDataDir)
803 // cfg.TorrentDataOpener = func() TorrentDataOpener {
804 // fc, err := filecache.NewCache(leecherDataDir)
805 // require.NoError(t, err)
806 // if ps.SetLeecherStorageCapacity {
807 // fc.SetCapacity(ps.LeecherStorageCapacity)
809 // store := pieceStore.New(fileCacheDataBackend.New(fc))
810 // return func(mi *metainfo.Info) storage.I {
811 // return store.OpenTorrentData(mi)
814 cfg.DataDir = leecherDataDir
815 leecher, _ := NewClient(&cfg)
816 defer leecher.Close()
817 if ps.ExportClientStatus {
818 testutil.ExportStatusWriter(leecher, "l")
820 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
821 ret = TorrentSpecFromMetaInfo(mi)
825 require.NoError(t, err)
827 psc := leecherGreeting.SubscribePieceStateChanges()
829 leecherGreeting.DownloadAll()
831 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
833 leecherGreeting.AddPeers([]Peer{
835 IP: missinggo.AddrIP(seeder.ListenAddr()),
836 Port: missinggo.AddrPort(seeder.ListenAddr()),
839 completes := make(map[int]bool, 3)
842 // started := time.Now()
844 case _v := <-psc.Values:
845 // log.Print(time.Since(started))
846 v := _v.(PieceStateChange)
847 completes[v.Index] = v.Complete
848 case <-time.After(100 * time.Millisecond):
853 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
855 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
860 func TestTorrentDownloadAll(t *testing.T) {
861 testDownloadCancel(t, testDownloadCancelParams{})
864 func TestTorrentDownloadAllThenCancel(t *testing.T) {
865 testDownloadCancel(t, testDownloadCancelParams{
870 // Ensure that it's an error for a peer to send an invalid have message.
871 func TestPeerInvalidHave(t *testing.T) {
872 cl, err := NewClient(&TestingConfig)
873 require.NoError(t, err)
875 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
876 Info: &metainfo.InfoEx{
879 Pieces: make([]byte, 20),
880 Files: []metainfo.FileInfo{{Length: 1}},
884 require.NoError(t, err)
890 assert.NoError(t, cn.peerSentHave(0))
891 assert.Error(t, cn.peerSentHave(1))
894 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
895 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
896 defer os.RemoveAll(greetingTempDir)
898 cfg.DataDir = greetingTempDir
899 seeder, err := NewClient(&TestingConfig)
900 require.NoError(t, err)
901 seeder.AddTorrentSpec(&TorrentSpec{
902 Info: &greetingMetainfo.Info,