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 NoDefaultBlocklist: true,
44 DisableMetainfoCache: true,
46 DHTConfig: dht.ServerConfig{
47 NoDefaultBootstrap: true,
51 func TestClientDefault(t *testing.T) {
52 cl, err := NewClient(&TestingConfig)
53 require.NoError(t, err)
57 func TestAddDropTorrent(t *testing.T) {
58 cl, err := NewClient(&TestingConfig)
59 require.NoError(t, err)
61 dir, mi := testutil.GreetingTestTorrent()
62 defer os.RemoveAll(dir)
63 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
64 require.NoError(t, err)
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)
90 tor := newTorrent(func() (ih metainfo.InfoHash) {
91 missinggo.CopyExact(ih[:], mi.Info.Hash)
95 tor.storageOpener = storage.NewFile(dir)
96 // Needed to lock for asynchronous piece verification.
98 err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
99 require.NoError(t, err)
100 require.Len(t, tor.Pieces, 3)
101 tor.pendAllChunkSpecs(0)
102 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
103 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
106 func TestUnmarshalPEXMsg(t *testing.T) {
107 var m peerExchangeMessage
108 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
111 if len(m.Added) != 2 {
114 if m.Added[0].Port != 0x506 {
119 func TestReducedDialTimeout(t *testing.T) {
120 for _, _case := range []struct {
124 ExpectedReduced time.Duration
126 {nominalDialTimeout, 40, 0, nominalDialTimeout},
127 {nominalDialTimeout, 40, 1, nominalDialTimeout},
128 {nominalDialTimeout, 40, 39, nominalDialTimeout},
129 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
130 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
131 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
133 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
134 expected := _case.ExpectedReduced
135 if expected < minDialTimeout {
136 expected = minDialTimeout
138 if reduced != expected {
139 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
144 func TestUTPRawConn(t *testing.T) {
145 l, err := utp.NewSocket("udp", "")
158 // Connect a UTP peer to see if the RawConn will still work.
159 s, _ := utp.NewSocket("udp", "")
161 utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
163 t.Fatalf("error dialing utp listener: %s", err)
165 defer utpPeer.Close()
166 peer, err := net.ListenPacket("udp", ":0")
173 // How many messages to send. I've set this to double the channel buffer
174 // size in the raw packetConn.
176 readerStopped := make(chan struct{})
177 // The reader goroutine.
179 defer close(readerStopped)
180 b := make([]byte, 500)
181 for i := 0; i < N; i++ {
182 n, _, err := l.ReadFrom(b)
184 t.Fatalf("error reading from raw conn: %s", err)
188 fmt.Sscan(string(b[:n]), &d)
190 log.Printf("got wrong number: expected %d, got %d", i, d)
194 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
198 for i := 0; i < N; i++ {
199 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
203 time.Sleep(time.Microsecond)
206 case <-readerStopped:
207 case <-time.After(time.Second):
208 t.Fatal("reader timed out")
210 if msgsReceived != N {
211 t.Fatalf("messages received: %d", msgsReceived)
215 func TestTwoClientsArbitraryPorts(t *testing.T) {
216 for i := 0; i < 2; i++ {
217 cl, err := NewClient(&TestingConfig)
225 func TestAddDropManyTorrents(t *testing.T) {
226 cl, err := NewClient(&TestingConfig)
227 require.NoError(t, err)
229 for i := range iter.N(1000) {
231 binary.PutVarint(spec.InfoHash[:], int64(i))
232 tt, new, err := cl.AddTorrentSpec(&spec)
233 assert.NoError(t, err)
239 func TestClientTransferDefault(t *testing.T) {
240 testClientTransfer(t, testClientTransferParams{
241 ExportClientStatus: true,
245 func TestClientTransferSmallCache(t *testing.T) {
246 testClientTransfer(t, testClientTransferParams{
247 SetLeecherStorageCapacity: true,
248 // Going below the piece length means it can't complete a piece so
249 // that it can be hashed.
250 LeecherStorageCapacity: 5,
252 // Can't readahead too far or the cache will thrash and drop data we
255 ExportClientStatus: true,
259 func TestClientTransferVarious(t *testing.T) {
260 for _, ss := range []func(string) storage.I{
264 for _, responsive := range []bool{false, true} {
265 testClientTransfer(t, testClientTransferParams{
266 Responsive: responsive,
269 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
270 testClientTransfer(t, testClientTransferParams{
272 Responsive: responsive,
274 Readahead: readahead,
281 type testClientTransferParams struct {
285 ExportClientStatus bool
286 SetLeecherStorageCapacity bool
287 LeecherStorageCapacity int64
288 SeederStorage func(string) storage.I
291 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
292 greetingTempDir, mi := testutil.GreetingTestTorrent()
293 defer os.RemoveAll(greetingTempDir)
296 if ps.SeederStorage != nil {
297 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
299 cfg.DataDir = greetingTempDir
301 seeder, err := NewClient(&cfg)
302 require.NoError(t, err)
304 if ps.ExportClientStatus {
305 testutil.ExportStatusWriter(seeder, "s")
307 _, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
308 require.NoError(t, err)
310 leecherDataDir, err := ioutil.TempDir("", "")
311 require.NoError(t, err)
312 defer os.RemoveAll(leecherDataDir)
313 fc, err := filecache.NewCache(leecherDataDir)
314 require.NoError(t, err)
315 if ps.SetLeecherStorageCapacity {
316 fc.SetCapacity(ps.LeecherStorageCapacity)
318 cfg.DefaultStorage = storage.NewPieceFileStorage(storage.FileCacheFileStore{fc})
319 leecher, err := NewClient(&cfg)
320 require.NoError(t, err)
321 defer leecher.Close()
322 if ps.ExportClientStatus {
323 testutil.ExportStatusWriter(leecher, "l")
325 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
326 ret = TorrentSpecFromMetaInfo(mi)
328 ret.Storage = storage.NewFile(leecherDataDir)
331 require.NoError(t, err)
333 leecherGreeting.AddPeers([]Peer{
335 IP: missinggo.AddrIP(seeder.ListenAddr()),
336 Port: missinggo.AddrPort(seeder.ListenAddr()),
339 r := leecherGreeting.NewReader()
345 r.SetReadahead(ps.Readahead)
347 for range iter.N(2) {
348 pos, err := r.Seek(0, os.SEEK_SET)
349 assert.NoError(t, err)
350 assert.EqualValues(t, 0, pos)
351 _greeting, err := ioutil.ReadAll(r)
352 assert.NoError(t, err)
353 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
357 // Check that after completing leeching, a leecher transitions to a seeding
358 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
359 func TestSeedAfterDownloading(t *testing.T) {
360 greetingTempDir, mi := testutil.GreetingTestTorrent()
361 defer os.RemoveAll(greetingTempDir)
364 cfg.DataDir = greetingTempDir
365 seeder, err := NewClient(&cfg)
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, _ := NewClient(&cfg)
373 defer leecher.Close()
374 testutil.ExportStatusWriter(leecher, "l")
376 // cfg.TorrentDataOpener = nil
377 cfg.DataDir, err = ioutil.TempDir("", "")
378 require.NoError(t, err)
379 defer os.RemoveAll(cfg.DataDir)
380 leecherLeecher, _ := NewClient(&cfg)
381 defer leecherLeecher.Close()
382 testutil.ExportStatusWriter(leecherLeecher, "ll")
383 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
384 ret = TorrentSpecFromMetaInfo(mi)
388 llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
389 ret = TorrentSpecFromMetaInfo(mi)
393 // Simultaneously DownloadAll in Leecher, and read the contents
394 // consecutively in LeecherLeecher. This non-deterministically triggered a
395 // case where the leecher wouldn't unchoke the LeecherLeecher.
396 var wg sync.WaitGroup
402 b, err := ioutil.ReadAll(r)
403 require.NoError(t, err)
404 assert.EqualValues(t, testutil.GreetingFileContents, b)
406 leecherGreeting.AddPeers([]Peer{
408 IP: missinggo.AddrIP(seeder.ListenAddr()),
409 Port: missinggo.AddrPort(seeder.ListenAddr()),
412 IP: missinggo.AddrIP(leecherLeecher.ListenAddr()),
413 Port: missinggo.AddrPort(leecherLeecher.ListenAddr()),
419 leecherGreeting.DownloadAll()
425 func TestReadaheadPieces(t *testing.T) {
426 for _, case_ := range []struct {
427 readaheadBytes, pieceLength int64
430 {5 * 1024 * 1024, 256 * 1024, 19},
431 {5 * 1024 * 1024, 5 * 1024 * 1024, 1},
432 {5*1024*1024 - 1, 5 * 1024 * 1024, 1},
433 {5 * 1024 * 1024, 5*1024*1024 - 1, 2},
434 {0, 5 * 1024 * 1024, 0},
435 {5 * 1024 * 1024, 1048576, 4},
437 pieces := readaheadPieces(case_.readaheadBytes, case_.pieceLength)
438 assert.Equal(t, case_.readaheadPieces, pieces, "%v", case_)
442 func TestMergingTrackersByAddingSpecs(t *testing.T) {
443 cl, err := NewClient(&TestingConfig)
444 require.NoError(t, err)
446 spec := TorrentSpec{}
447 T, new, _ := cl.AddTorrentSpec(&spec)
451 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
452 _, new, _ = cl.AddTorrentSpec(&spec)
456 assert.EqualValues(t, T.torrent.Trackers[0][0], "http://a")
457 assert.EqualValues(t, T.torrent.Trackers[1][0], "udp://b")
460 type badStorage struct{}
462 func (me badStorage) OpenTorrent(*metainfo.InfoEx) (storage.Torrent, error) {
466 func (me badStorage) Close() error {
470 func (me badStorage) Piece(p metainfo.Piece) storage.Piece {
471 return badStoragePiece{p}
474 type badStoragePiece struct {
478 func (me badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
482 func (me badStoragePiece) GetIsComplete() bool {
486 func (me badStoragePiece) MarkComplete() error {
487 return errors.New("psyyyyyyyche")
490 func (me badStoragePiece) randomlyTruncatedDataString() string {
491 return "hello, world\n"[:rand.Intn(14)]
494 func (me badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
495 r := strings.NewReader(me.randomlyTruncatedDataString())
496 return r.ReadAt(b, off+me.p.Offset())
499 // We read from a piece which is marked completed, but is missing data.
500 func TestCompletedPieceWrongSize(t *testing.T) {
502 cfg.DefaultStorage = badStorage{}
503 cl, _ := NewClient(&cfg)
505 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
506 Info: &metainfo.InfoEx{
509 Pieces: make([]byte, 20),
510 Files: []metainfo.FileInfo{
511 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
516 require.NoError(t, err)
521 b, err := ioutil.ReadAll(r)
523 assert.NoError(t, err)
526 func BenchmarkAddLargeTorrent(b *testing.B) {
528 cfg.DisableTCP = true
529 cfg.DisableUTP = true
530 cfg.ListenAddr = "redonk"
531 cl, _ := NewClient(&cfg)
533 for range iter.N(b.N) {
534 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
542 func TestResponsive(t *testing.T) {
543 seederDataDir, mi := testutil.GreetingTestTorrent()
544 defer os.RemoveAll(seederDataDir)
547 cfg.DataDir = seederDataDir
548 seeder, err := NewClient(&cfg)
551 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
552 leecherDataDir, err := ioutil.TempDir("", "")
554 defer os.RemoveAll(leecherDataDir)
556 cfg.DataDir = leecherDataDir
557 leecher, err := NewClient(&cfg)
559 defer leecher.Close()
560 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
561 ret = TorrentSpecFromMetaInfo(mi)
565 leecherTorrent.AddPeers([]Peer{
567 IP: missinggo.AddrIP(seeder.ListenAddr()),
568 Port: missinggo.AddrPort(seeder.ListenAddr()),
571 reader := leecherTorrent.NewReader()
573 reader.SetReadahead(0)
574 reader.SetResponsive()
576 _, err = reader.Seek(3, os.SEEK_SET)
577 require.NoError(t, err)
578 _, err = io.ReadFull(reader, b)
580 assert.EqualValues(t, "lo", string(b))
581 _, err = reader.Seek(11, os.SEEK_SET)
582 require.NoError(t, err)
583 n, err := io.ReadFull(reader, b)
585 assert.EqualValues(t, 2, n)
586 assert.EqualValues(t, "d\n", string(b))
589 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
590 seederDataDir, mi := testutil.GreetingTestTorrent()
591 defer os.RemoveAll(seederDataDir)
594 cfg.DataDir = seederDataDir
595 seeder, err := NewClient(&cfg)
598 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
599 leecherDataDir, err := ioutil.TempDir("", "")
601 defer os.RemoveAll(leecherDataDir)
603 cfg.DataDir = leecherDataDir
604 leecher, err := NewClient(&cfg)
606 defer leecher.Close()
607 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
608 ret = TorrentSpecFromMetaInfo(mi)
612 leecherTorrent.AddPeers([]Peer{
614 IP: missinggo.AddrIP(seeder.ListenAddr()),
615 Port: missinggo.AddrPort(seeder.ListenAddr()),
618 reader := leecherTorrent.NewReader()
620 reader.SetReadahead(0)
621 reader.SetResponsive()
623 _, err = reader.Seek(3, os.SEEK_SET)
624 require.NoError(t, err)
625 _, err = io.ReadFull(reader, b)
627 assert.EqualValues(t, "lo", string(b))
628 go leecherTorrent.Drop()
629 _, err = reader.Seek(11, os.SEEK_SET)
630 require.NoError(t, err)
631 n, err := reader.Read(b)
632 assert.EqualError(t, err, "torrent closed")
633 assert.EqualValues(t, 0, n)
636 func TestDHTInheritBlocklist(t *testing.T) {
637 ipl := iplist.New(nil)
638 require.NotNil(t, ipl)
640 cfg.IPBlocklist = ipl
642 cl, err := NewClient(&cfg)
643 require.NoError(t, err)
645 require.Equal(t, ipl, cl.DHT().IPBlocklist())
648 // Check that stuff is merged in subsequent AddTorrentSpec for the same
650 func TestAddTorrentSpecMerging(t *testing.T) {
651 cl, err := NewClient(&TestingConfig)
652 require.NoError(t, err)
654 dir, mi := testutil.GreetingTestTorrent()
655 defer os.RemoveAll(dir)
657 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
658 tt, new, err := cl.AddTorrentSpec(&ts)
659 require.NoError(t, err)
661 require.Nil(t, tt.Info())
662 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
663 require.NoError(t, err)
664 require.False(t, new)
665 require.NotNil(t, tt.Info())
668 // Check that torrent Info is obtained from the metainfo file cache.
669 func TestAddTorrentMetainfoInCache(t *testing.T) {
671 cfg.DisableMetainfoCache = false
672 cfg.ConfigDir, _ = ioutil.TempDir(os.TempDir(), "")
673 defer os.RemoveAll(cfg.ConfigDir)
674 cl, err := NewClient(&cfg)
675 require.NoError(t, err)
677 dir, mi := testutil.GreetingTestTorrent()
678 defer os.RemoveAll(dir)
679 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
680 require.NoError(t, err)
682 require.NotNil(t, tt.Info())
683 _, err = os.Stat(filepath.Join(cfg.ConfigDir, "torrents", fmt.Sprintf("%x.torrent", mi.Info.Hash.Bytes())))
684 require.NoError(t, err)
685 // Contains only the infohash.
687 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
688 _, ok := cl.Torrent(ts.InfoHash)
691 _, ok = cl.Torrent(ts.InfoHash)
693 tt, new, err = cl.AddTorrentSpec(&ts)
694 require.NoError(t, err)
696 // Obtained from the metainfo cache.
697 require.NotNil(t, tt.Info())
700 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
701 dir, mi := testutil.GreetingTestTorrent()
703 cl, _ := NewClient(&TestingConfig)
706 missinggo.CopyExact(&ts.InfoHash, mi.Info.Hash)
707 tt, _, _ := cl.AddTorrentSpec(&ts)
709 assert.EqualValues(t, 0, len(cl.Torrents()))
717 func writeTorrentData(ts storage.Torrent, info *metainfo.InfoEx, b []byte) {
718 for i := range iter.N(info.NumPieces()) {
719 n, err := 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(storage.FileCacheFileStore{fileCache})
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.torrent.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(storage.FileCacheFileStore{fc})
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,