19 _ "github.com/anacrolix/envpprof"
20 "github.com/anacrolix/missinggo"
21 "github.com/anacrolix/missinggo/filecache"
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 DisableMetainfoCache: true,
45 DHTConfig: dht.ServerConfig{
46 NoDefaultBootstrap: true,
50 func TestClientDefault(t *testing.T) {
51 cl, err := NewClient(&TestingConfig)
52 require.NoError(t, err)
56 func TestAddDropTorrent(t *testing.T) {
57 cl, err := NewClient(&TestingConfig)
58 require.NoError(t, err)
60 dir, mi := testutil.GreetingTestTorrent()
61 defer os.RemoveAll(dir)
62 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
63 require.NoError(t, err)
68 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
72 func TestAddTorrentNoUsableURLs(t *testing.T) {
76 func TestAddPeersToUnknownTorrent(t *testing.T) {
80 func TestPieceHashSize(t *testing.T) {
81 if pieceHash.Size() != 20 {
86 func TestTorrentInitialState(t *testing.T) {
87 dir, mi := testutil.GreetingTestTorrent()
88 defer os.RemoveAll(dir)
89 tor := newTorrent(func() (ih metainfo.Hash) {
90 missinggo.CopyExact(ih[:], mi.Info.Hash)
94 tor.storageOpener = storage.NewFile(dir)
95 // Needed to lock for asynchronous piece verification.
97 err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
98 require.NoError(t, err)
99 require.Len(t, tor.pieces, 3)
100 tor.pendAllChunkSpecs(0)
101 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
102 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
105 func TestUnmarshalPEXMsg(t *testing.T) {
106 var m peerExchangeMessage
107 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
110 if len(m.Added) != 2 {
113 if m.Added[0].Port != 0x506 {
118 func TestReducedDialTimeout(t *testing.T) {
119 for _, _case := range []struct {
123 ExpectedReduced time.Duration
125 {nominalDialTimeout, 40, 0, nominalDialTimeout},
126 {nominalDialTimeout, 40, 1, nominalDialTimeout},
127 {nominalDialTimeout, 40, 39, nominalDialTimeout},
128 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
129 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
130 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
132 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
133 expected := _case.ExpectedReduced
134 if expected < minDialTimeout {
135 expected = minDialTimeout
137 if reduced != expected {
138 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
143 func TestUTPRawConn(t *testing.T) {
144 l, err := utp.NewSocket("udp", "")
157 // Connect a UTP peer to see if the RawConn will still work.
158 s, _ := utp.NewSocket("udp", "")
160 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
162 t.Fatalf("error dialing utp listener: %s", err)
164 defer utpPeer.Close()
165 peer, err := net.ListenPacket("udp", ":0")
172 // How many messages to send. I've set this to double the channel buffer
173 // size in the raw packetConn.
175 readerStopped := make(chan struct{})
176 // The reader goroutine.
178 defer close(readerStopped)
179 b := make([]byte, 500)
180 for i := 0; i < N; i++ {
181 n, _, err := l.ReadFrom(b)
183 t.Fatalf("error reading from raw conn: %s", err)
187 fmt.Sscan(string(b[:n]), &d)
189 log.Printf("got wrong number: expected %d, got %d", i, d)
193 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
197 for i := 0; i < N; i++ {
198 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
202 time.Sleep(time.Microsecond)
205 case <-readerStopped:
206 case <-time.After(time.Second):
207 t.Fatal("reader timed out")
209 if msgsReceived != N {
210 t.Fatalf("messages received: %d", msgsReceived)
214 func TestTwoClientsArbitraryPorts(t *testing.T) {
215 for i := 0; i < 2; i++ {
216 cl, err := NewClient(&TestingConfig)
224 func TestAddDropManyTorrents(t *testing.T) {
225 cl, err := NewClient(&TestingConfig)
226 require.NoError(t, err)
228 for i := range iter.N(1000) {
230 binary.PutVarint(spec.InfoHash[:], int64(i))
231 tt, new, err := cl.AddTorrentSpec(&spec)
232 assert.NoError(t, err)
238 func TestClientTransferDefault(t *testing.T) {
239 testClientTransfer(t, testClientTransferParams{
240 ExportClientStatus: true,
244 func TestClientTransferSmallCache(t *testing.T) {
245 testClientTransfer(t, testClientTransferParams{
246 SetLeecherStorageCapacity: true,
247 // Going below the piece length means it can't complete a piece so
248 // that it can be hashed.
249 LeecherStorageCapacity: 5,
251 // Can't readahead too far or the cache will thrash and drop data we
254 ExportClientStatus: true,
258 func TestClientTransferVarious(t *testing.T) {
259 for _, ss := range []func(string) storage.I{
263 for _, responsive := range []bool{false, true} {
264 testClientTransfer(t, testClientTransferParams{
265 Responsive: responsive,
268 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
269 testClientTransfer(t, testClientTransferParams{
271 Responsive: responsive,
273 Readahead: readahead,
280 type testClientTransferParams struct {
284 ExportClientStatus bool
285 SetLeecherStorageCapacity bool
286 LeecherStorageCapacity int64
287 SeederStorage func(string) storage.I
290 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
291 greetingTempDir, mi := testutil.GreetingTestTorrent()
292 defer os.RemoveAll(greetingTempDir)
295 if ps.SeederStorage != nil {
296 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
298 cfg.DataDir = greetingTempDir
300 seeder, err := NewClient(&cfg)
301 require.NoError(t, err)
303 if ps.ExportClientStatus {
304 testutil.ExportStatusWriter(seeder, "s")
306 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
307 require.NoError(t, err)
309 leecherDataDir, err := ioutil.TempDir("", "")
310 require.NoError(t, err)
311 defer os.RemoveAll(leecherDataDir)
312 fc, err := filecache.NewCache(leecherDataDir)
313 require.NoError(t, err)
314 if ps.SetLeecherStorageCapacity {
315 fc.SetCapacity(ps.LeecherStorageCapacity)
317 cfg.DefaultStorage = storage.NewPieceFileStorage(fc.AsFileStore())
318 leecher, err := NewClient(&cfg)
319 require.NoError(t, err)
320 defer leecher.Close()
321 if ps.ExportClientStatus {
322 testutil.ExportStatusWriter(leecher, "l")
324 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
325 ret = TorrentSpecFromMetaInfo(mi)
327 ret.Storage = storage.NewFile(leecherDataDir)
330 require.NoError(t, err)
332 leecherGreeting.AddPeers([]Peer{
334 IP: missinggo.AddrIP(seeder.ListenAddr()),
335 Port: missinggo.AddrPort(seeder.ListenAddr()),
338 r := leecherGreeting.NewReader()
344 r.SetReadahead(ps.Readahead)
346 for range iter.N(2) {
347 pos, err := r.Seek(0, os.SEEK_SET)
348 assert.NoError(t, err)
349 assert.EqualValues(t, 0, pos)
350 _greeting, err := ioutil.ReadAll(r)
351 assert.NoError(t, err)
352 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
356 // Check that after completing leeching, a leecher transitions to a seeding
357 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
358 func TestSeedAfterDownloading(t *testing.T) {
359 greetingTempDir, mi := testutil.GreetingTestTorrent()
360 defer os.RemoveAll(greetingTempDir)
363 cfg.DataDir = greetingTempDir
364 seeder, err := NewClient(&cfg)
365 require.NoError(t, err)
367 testutil.ExportStatusWriter(seeder, "s")
368 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
369 cfg.DataDir, err = ioutil.TempDir("", "")
370 require.NoError(t, err)
371 defer os.RemoveAll(cfg.DataDir)
372 leecher, err := NewClient(&cfg)
373 require.NoError(t, err)
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.trackers[0][0], "http://a")
458 assert.EqualValues(t, T.trackers[1][0], "udp://b")
461 type badStorage struct{}
463 func (me badStorage) OpenTorrent(*metainfo.InfoEx) (storage.Torrent, error) {
467 func (me badStorage) Close() error {
471 func (me badStorage) Piece(p metainfo.Piece) storage.Piece {
472 return badStoragePiece{p}
475 type badStoragePiece struct {
479 func (me badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
483 func (me badStoragePiece) GetIsComplete() bool {
487 func (me badStoragePiece) MarkComplete() error {
488 return errors.New("psyyyyyyyche")
491 func (me badStoragePiece) randomlyTruncatedDataString() string {
492 return "hello, world\n"[:rand.Intn(14)]
495 func (me badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
496 r := strings.NewReader(me.randomlyTruncatedDataString())
497 return r.ReadAt(b, off+me.p.Offset())
500 // We read from a piece which is marked completed, but is missing data.
501 func TestCompletedPieceWrongSize(t *testing.T) {
503 cfg.DefaultStorage = badStorage{}
504 cl, _ := NewClient(&cfg)
506 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
507 Info: &metainfo.InfoEx{
510 Pieces: make([]byte, 20),
511 Files: []metainfo.FileInfo{
512 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
517 require.NoError(t, err)
522 b, err := ioutil.ReadAll(r)
524 assert.NoError(t, err)
527 func BenchmarkAddLargeTorrent(b *testing.B) {
529 cfg.DisableTCP = true
530 cfg.DisableUTP = true
531 cfg.ListenAddr = "redonk"
532 cl, _ := NewClient(&cfg)
534 for range iter.N(b.N) {
535 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
543 func TestResponsive(t *testing.T) {
544 seederDataDir, mi := testutil.GreetingTestTorrent()
545 defer os.RemoveAll(seederDataDir)
548 cfg.DataDir = seederDataDir
549 seeder, err := NewClient(&cfg)
552 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
553 leecherDataDir, err := ioutil.TempDir("", "")
555 defer os.RemoveAll(leecherDataDir)
557 cfg.DataDir = leecherDataDir
558 leecher, err := NewClient(&cfg)
560 defer leecher.Close()
561 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
562 ret = TorrentSpecFromMetaInfo(mi)
566 leecherTorrent.AddPeers([]Peer{
568 IP: missinggo.AddrIP(seeder.ListenAddr()),
569 Port: missinggo.AddrPort(seeder.ListenAddr()),
572 reader := leecherTorrent.NewReader()
574 reader.SetReadahead(0)
575 reader.SetResponsive()
577 _, err = reader.Seek(3, os.SEEK_SET)
578 require.NoError(t, err)
579 _, err = io.ReadFull(reader, b)
581 assert.EqualValues(t, "lo", string(b))
582 _, err = reader.Seek(11, os.SEEK_SET)
583 require.NoError(t, err)
584 n, err := io.ReadFull(reader, b)
586 assert.EqualValues(t, 2, n)
587 assert.EqualValues(t, "d\n", string(b))
590 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
591 seederDataDir, mi := testutil.GreetingTestTorrent()
592 defer os.RemoveAll(seederDataDir)
595 cfg.DataDir = seederDataDir
596 seeder, err := NewClient(&cfg)
599 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
600 leecherDataDir, err := ioutil.TempDir("", "")
602 defer os.RemoveAll(leecherDataDir)
604 cfg.DataDir = leecherDataDir
605 leecher, err := NewClient(&cfg)
607 defer leecher.Close()
608 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
609 ret = TorrentSpecFromMetaInfo(mi)
613 leecherTorrent.AddPeers([]Peer{
615 IP: missinggo.AddrIP(seeder.ListenAddr()),
616 Port: missinggo.AddrPort(seeder.ListenAddr()),
619 reader := leecherTorrent.NewReader()
621 reader.SetReadahead(0)
622 reader.SetResponsive()
624 _, err = reader.Seek(3, os.SEEK_SET)
625 require.NoError(t, err)
626 _, err = io.ReadFull(reader, b)
628 assert.EqualValues(t, "lo", string(b))
629 go leecherTorrent.Drop()
630 _, err = reader.Seek(11, os.SEEK_SET)
631 require.NoError(t, err)
632 n, err := reader.Read(b)
633 assert.EqualError(t, err, "torrent closed")
634 assert.EqualValues(t, 0, n)
637 func TestDHTInheritBlocklist(t *testing.T) {
638 ipl := iplist.New(nil)
639 require.NotNil(t, ipl)
641 cfg.IPBlocklist = ipl
643 cl, err := NewClient(&cfg)
644 require.NoError(t, err)
646 require.Equal(t, ipl, cl.DHT().IPBlocklist())
649 // Check that stuff is merged in subsequent AddTorrentSpec for the same
651 func TestAddTorrentSpecMerging(t *testing.T) {
652 cl, err := NewClient(&TestingConfig)
653 require.NoError(t, err)
655 dir, mi := testutil.GreetingTestTorrent()
656 defer os.RemoveAll(dir)
658 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
659 tt, new, err := cl.AddTorrentSpec(&ts)
660 require.NoError(t, err)
662 require.Nil(t, tt.Info())
663 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
664 require.NoError(t, err)
665 require.False(t, new)
666 require.NotNil(t, tt.Info())
669 // Check that torrent Info is obtained from the metainfo file cache.
670 func TestAddTorrentMetainfoInCache(t *testing.T) {
672 cfg.DisableMetainfoCache = false
673 cfg.ConfigDir, _ = ioutil.TempDir(os.TempDir(), "")
674 defer os.RemoveAll(cfg.ConfigDir)
675 cl, err := NewClient(&cfg)
676 require.NoError(t, err)
678 dir, mi := testutil.GreetingTestTorrent()
679 defer os.RemoveAll(dir)
680 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
681 require.NoError(t, err)
683 require.NotNil(t, tt.Info())
684 _, err = os.Stat(filepath.Join(cfg.ConfigDir, "torrents", fmt.Sprintf("%x.torrent", mi.Info.Hash.Bytes())))
685 require.NoError(t, err)
686 // Contains only the infohash.
688 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
689 _, ok := cl.Torrent(ts.InfoHash)
692 _, ok = cl.Torrent(ts.InfoHash)
694 tt, new, err = cl.AddTorrentSpec(&ts)
695 require.NoError(t, err)
697 // Obtained from the metainfo cache.
698 require.NotNil(t, tt.Info())
701 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
702 dir, mi := testutil.GreetingTestTorrent()
704 cl, _ := NewClient(&TestingConfig)
707 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
708 tt, _, _ := cl.AddTorrentSpec(&ts)
710 assert.EqualValues(t, 0, len(cl.Torrents()))
718 func writeTorrentData(ts storage.Torrent, info *metainfo.InfoEx, b []byte) {
719 for i := range iter.N(info.NumPieces()) {
720 n, _ := ts.Piece(info.Piece(i)).WriteAt(b, 0)
725 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool) {
726 fileCacheDir, err := ioutil.TempDir("", "")
727 require.NoError(t, err)
728 defer os.RemoveAll(fileCacheDir)
729 fileCache, err := filecache.NewCache(fileCacheDir)
730 require.NoError(t, err)
731 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
732 defer os.RemoveAll(greetingDataTempDir)
733 filePieceStore := storage.NewPieceFileStorage(fileCache.AsFileStore())
734 greetingData, err := filePieceStore.OpenTorrent(&greetingMetainfo.Info)
735 require.NoError(t, err)
736 writeTorrentData(greetingData, &greetingMetainfo.Info, []byte(testutil.GreetingFileContents))
737 // require.Equal(t, len(testutil.GreetingFileContents), written)
738 // require.NoError(t, err)
739 for i := 0; i < greetingMetainfo.Info.NumPieces(); i++ {
740 p := greetingMetainfo.Info.Piece(i)
741 if alreadyCompleted {
742 err := greetingData.Piece(p).MarkComplete()
743 assert.NoError(t, err)
747 // TODO: Disable network option?
748 cfg.DisableTCP = true
749 cfg.DisableUTP = true
750 cfg.DefaultStorage = filePieceStore
751 cl, err := NewClient(&cfg)
752 require.NoError(t, err)
754 tt, err := cl.AddTorrent(greetingMetainfo)
755 require.NoError(t, err)
756 psrs := tt.PieceStateRuns()
757 assert.Len(t, psrs, 1)
758 assert.EqualValues(t, 3, psrs[0].Length)
759 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
760 if alreadyCompleted {
762 b, err := ioutil.ReadAll(r)
763 assert.NoError(t, err)
764 assert.EqualValues(t, testutil.GreetingFileContents, b)
768 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
769 testAddTorrentPriorPieceCompletion(t, true)
772 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
773 testAddTorrentPriorPieceCompletion(t, false)
776 func TestAddMetainfoWithNodes(t *testing.T) {
779 // For now, we want to just jam the nodes into the table, without
780 // verifying them first. Also the DHT code doesn't support mixing secure
781 // and insecure nodes if security is enabled (yet).
782 cfg.DHTConfig.NoSecurity = true
783 cl, err := NewClient(&cfg)
784 require.NoError(t, err)
786 assert.EqualValues(t, cl.DHT().NumNodes(), 0)
787 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
788 require.NoError(t, err)
789 assert.Len(t, tt.trackers, 5)
790 assert.EqualValues(t, 6, cl.DHT().NumNodes())
793 type testDownloadCancelParams struct {
797 ExportClientStatus bool
798 SetLeecherStorageCapacity bool
799 LeecherStorageCapacity int64
803 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
804 greetingTempDir, mi := testutil.GreetingTestTorrent()
805 defer os.RemoveAll(greetingTempDir)
808 cfg.DataDir = greetingTempDir
809 seeder, err := NewClient(&cfg)
810 require.NoError(t, err)
812 if ps.ExportClientStatus {
813 testutil.ExportStatusWriter(seeder, "s")
815 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
816 leecherDataDir, err := ioutil.TempDir("", "")
817 require.NoError(t, err)
818 defer os.RemoveAll(leecherDataDir)
819 fc, err := filecache.NewCache(leecherDataDir)
820 require.NoError(t, err)
821 if ps.SetLeecherStorageCapacity {
822 fc.SetCapacity(ps.LeecherStorageCapacity)
824 cfg.DefaultStorage = storage.NewPieceFileStorage(fc.AsFileStore())
825 cfg.DataDir = leecherDataDir
826 leecher, _ := NewClient(&cfg)
827 defer leecher.Close()
828 if ps.ExportClientStatus {
829 testutil.ExportStatusWriter(leecher, "l")
831 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
832 ret = TorrentSpecFromMetaInfo(mi)
836 require.NoError(t, err)
838 psc := leecherGreeting.SubscribePieceStateChanges()
840 leecherGreeting.DownloadAll()
842 leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
844 leecherGreeting.AddPeers([]Peer{
846 IP: missinggo.AddrIP(seeder.ListenAddr()),
847 Port: missinggo.AddrPort(seeder.ListenAddr()),
850 completes := make(map[int]bool, 3)
853 // started := time.Now()
855 case _v := <-psc.Values:
856 // log.Print(time.Since(started))
857 v := _v.(PieceStateChange)
858 completes[v.Index] = v.Complete
859 case <-time.After(100 * time.Millisecond):
864 assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
866 assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
871 func TestTorrentDownloadAll(t *testing.T) {
872 testDownloadCancel(t, testDownloadCancelParams{})
875 func TestTorrentDownloadAllThenCancel(t *testing.T) {
876 testDownloadCancel(t, testDownloadCancelParams{
881 // Ensure that it's an error for a peer to send an invalid have message.
882 func TestPeerInvalidHave(t *testing.T) {
883 cl, err := NewClient(&TestingConfig)
884 require.NoError(t, err)
886 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
887 Info: &metainfo.InfoEx{
890 Pieces: make([]byte, 20),
891 Files: []metainfo.FileInfo{{Length: 1}},
895 require.NoError(t, err)
901 assert.NoError(t, cn.peerSentHave(0))
902 assert.Error(t, cn.peerSentHave(1))
905 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
906 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
907 defer os.RemoveAll(greetingTempDir)
909 cfg.DataDir = greetingTempDir
910 seeder, err := NewClient(&TestingConfig)
911 require.NoError(t, err)
912 seeder.AddTorrentSpec(&TorrentSpec{
913 Info: &greetingMetainfo.Info,