17 "github.com/anacrolix/dht"
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() *ClientConfig {
34 cfg := NewDefaultClientConfig()
35 cfg.ListenHost = LoopbackListenHost
37 cfg.DataDir = tempDir()
38 cfg.DisableTrackers = true
39 cfg.NoDefaultPortForwarding = true
43 func TestClientDefault(t *testing.T) {
44 cl, err := NewClient(TestingConfig())
45 require.NoError(t, err)
49 func TestClientNilConfig(t *testing.T) {
50 cl, err := NewClient(nil)
51 require.NoError(t, err)
55 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
56 cfg := TestingConfig()
57 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
58 require.NoError(t, err)
59 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
61 cfg.DefaultStorage = ci
62 cl, err := NewClient(cfg)
63 require.NoError(t, err)
65 // And again, https://github.com/anacrolix/torrent/issues/158
66 cl, err = NewClient(cfg)
67 require.NoError(t, err)
71 func TestAddDropTorrent(t *testing.T) {
72 cl, err := NewClient(TestingConfig())
73 require.NoError(t, err)
75 dir, mi := testutil.GreetingTestTorrent()
76 defer os.RemoveAll(dir)
77 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
78 require.NoError(t, err)
80 tt.SetMaxEstablishedConns(0)
81 tt.SetMaxEstablishedConns(1)
85 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
90 func TestAddTorrentNoUsableURLs(t *testing.T) {
95 func TestAddPeersToUnknownTorrent(t *testing.T) {
100 func TestPieceHashSize(t *testing.T) {
101 assert.Equal(t, 20, pieceHash.Size())
104 func TestTorrentInitialState(t *testing.T) {
105 dir, mi := testutil.GreetingTestTorrent()
106 defer os.RemoveAll(dir)
108 config: &ClientConfig{},
111 tor := cl.newTorrent(
113 storage.NewFileWithCompletion(tempDir(), storage.NewMapPieceCompletion()),
117 err := tor.setInfoBytes(mi.InfoBytes)
119 require.NoError(t, err)
120 require.Len(t, tor.pieces, 3)
121 tor.pendAllChunkSpecs(0)
123 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
125 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
128 func TestUnmarshalPEXMsg(t *testing.T) {
129 var m peerExchangeMessage
130 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
133 if len(m.Added) != 2 {
136 if m.Added[0].Port != 0x506 {
141 func TestReducedDialTimeout(t *testing.T) {
142 cfg := NewDefaultClientConfig()
143 for _, _case := range []struct {
147 ExpectedReduced time.Duration
149 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
150 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
151 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
152 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
153 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
154 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
156 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
157 expected := _case.ExpectedReduced
158 if expected < cfg.MinDialTimeout {
159 expected = cfg.MinDialTimeout
161 if reduced != expected {
162 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
167 func TestUTPRawConn(t *testing.T) {
168 l, err := NewUtpSocket("udp", "")
169 require.NoError(t, err)
179 // Connect a UTP peer to see if the RawConn will still work.
180 s, err := NewUtpSocket("udp", "")
181 require.NoError(t, err)
183 utpPeer, err := s.DialContext(context.Background(), "", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
184 require.NoError(t, err)
185 defer utpPeer.Close()
186 peer, err := net.ListenPacket("udp", ":0")
187 require.NoError(t, err)
191 // How many messages to send. I've set this to double the channel buffer
192 // size in the raw packetConn.
194 readerStopped := make(chan struct{})
195 // The reader goroutine.
197 defer close(readerStopped)
198 b := make([]byte, 500)
199 for i := 0; i < N; i++ {
200 n, _, err := l.ReadFrom(b)
201 require.NoError(t, err)
204 fmt.Sscan(string(b[:n]), &d)
205 assert.Equal(t, i, d)
208 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
209 require.NoError(t, err)
210 for i := 0; i < N; i++ {
211 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
212 require.NoError(t, err)
213 time.Sleep(time.Millisecond)
216 case <-readerStopped:
217 case <-time.After(time.Second):
218 t.Fatal("reader timed out")
220 if msgsReceived != N {
221 t.Fatalf("messages received: %d", msgsReceived)
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 type FileCacheClientStorageFactoryParams struct {
242 Wrapper func(*filecache.Cache) storage.ClientImpl
245 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
246 return func(dataDir string) storage.ClientImpl {
247 fc, err := filecache.NewCache(dataDir)
252 fc.SetCapacity(ps.Capacity)
254 return ps.Wrapper(fc)
258 type storageFactory func(string) storage.ClientImpl
260 func TestClientTransferDefault(t *testing.T) {
261 testClientTransfer(t, testClientTransferParams{
262 ExportClientStatus: true,
263 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
264 Wrapper: fileCachePieceResourceStorage,
269 func TestClientTransferRateLimitedUpload(t *testing.T) {
270 started := time.Now()
271 testClientTransfer(t, testClientTransferParams{
272 // We are uploading 13 bytes (the length of the greeting torrent). The
273 // chunks are 2 bytes in length. Then the smallest burst we can run
274 // with is 2. Time taken is (13-burst)/rate.
275 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
276 ExportClientStatus: true,
278 require.True(t, time.Since(started) > time.Second)
281 func TestClientTransferRateLimitedDownload(t *testing.T) {
282 testClientTransfer(t, testClientTransferParams{
283 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
287 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
288 return storage.NewResourcePieces(fc.AsResourceProvider())
291 func TestClientTransferSmallCache(t *testing.T) {
292 testClientTransfer(t, testClientTransferParams{
293 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
295 // Going below the piece length means it can't complete a piece so
296 // that it can be hashed.
298 Wrapper: fileCachePieceResourceStorage,
301 // Can't readahead too far or the cache will thrash and drop data we
304 ExportClientStatus: true,
308 func TestClientTransferVarious(t *testing.T) {
310 for _, ls := range []storageFactory{
311 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
312 Wrapper: fileCachePieceResourceStorage,
317 for _, ss := range []func(string) storage.ClientImpl{
321 for _, responsive := range []bool{false, true} {
322 testClientTransfer(t, testClientTransferParams{
323 Responsive: responsive,
327 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
328 testClientTransfer(t, testClientTransferParams{
330 Responsive: responsive,
332 Readahead: readahead,
341 type testClientTransferParams struct {
345 ExportClientStatus bool
346 LeecherStorage func(string) storage.ClientImpl
347 SeederStorage func(string) storage.ClientImpl
348 SeederUploadRateLimiter *rate.Limiter
349 LeecherDownloadRateLimiter *rate.Limiter
352 // Creates a seeder and a leecher, and ensures the data transfers when a read
353 // is attempted on the leecher.
354 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
355 greetingTempDir, mi := testutil.GreetingTestTorrent()
356 defer os.RemoveAll(greetingTempDir)
357 // Create seeder and a Torrent.
358 cfg := TestingConfig()
360 if ps.SeederUploadRateLimiter != nil {
361 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
363 // cfg.ListenAddr = "localhost:4000"
364 if ps.SeederStorage != nil {
365 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
366 defer cfg.DefaultStorage.Close()
368 cfg.DataDir = greetingTempDir
370 seeder, err := NewClient(cfg)
371 require.NoError(t, err)
372 if ps.ExportClientStatus {
373 testutil.ExportStatusWriter(seeder, "s")
375 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
376 // Run a Stats right after Closing the Client. This will trigger the Stats
377 // panic in #214 caused by RemoteAddr on Closed uTP sockets.
378 defer seederTorrent.Stats()
380 seederTorrent.VerifyData()
381 // Create leecher and a Torrent.
382 leecherDataDir, err := ioutil.TempDir("", "")
383 require.NoError(t, err)
384 defer os.RemoveAll(leecherDataDir)
385 cfg = TestingConfig()
386 if ps.LeecherStorage == nil {
387 cfg.DataDir = leecherDataDir
389 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
391 if ps.LeecherDownloadRateLimiter != nil {
392 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
395 leecher, err := NewClient(cfg)
396 require.NoError(t, err)
397 defer leecher.Close()
398 if ps.ExportClientStatus {
399 testutil.ExportStatusWriter(leecher, "l")
401 leecherTorrent, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
402 ret = TorrentSpecFromMetaInfo(mi)
406 require.NoError(t, err)
408 // Now do some things with leecher and seeder.
409 leecherTorrent.AddClientPeer(seeder)
410 // The Torrent should not be interested in obtaining peers, so the one we
411 // just added should be the only one.
412 assert.False(t, leecherTorrent.Seeding())
413 assert.EqualValues(t, 1, leecherTorrent.Stats().PendingPeers)
414 r := leecherTorrent.NewReader()
420 r.SetReadahead(ps.Readahead)
422 assertReadAllGreeting(t, r)
424 seederStats := seederTorrent.Stats()
425 assert.True(t, 13 <= seederStats.BytesWrittenData.Int64())
426 assert.True(t, 8 <= seederStats.ChunksWritten.Int64())
428 leecherStats := leecherTorrent.Stats()
429 assert.True(t, 13 <= leecherStats.BytesReadData.Int64())
430 assert.True(t, 8 <= leecherStats.ChunksRead.Int64())
432 // Try reading through again for the cases where the torrent data size
433 // exceeds the size of the cache.
434 assertReadAllGreeting(t, r)
437 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
438 pos, err := r.Seek(0, io.SeekStart)
439 assert.NoError(t, err)
440 assert.EqualValues(t, 0, pos)
441 _greeting, err := ioutil.ReadAll(r)
442 assert.NoError(t, err)
443 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
446 // Check that after completing leeching, a leecher transitions to a seeding
447 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
448 func TestSeedAfterDownloading(t *testing.T) {
449 greetingTempDir, mi := testutil.GreetingTestTorrent()
450 defer os.RemoveAll(greetingTempDir)
452 cfg := TestingConfig()
454 cfg.DataDir = greetingTempDir
455 seeder, err := NewClient(cfg)
456 require.NoError(t, err)
458 testutil.ExportStatusWriter(seeder, "s")
459 seederTorrent, ok, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
460 require.NoError(t, err)
462 seederTorrent.VerifyData()
464 cfg = TestingConfig()
466 cfg.DataDir, err = ioutil.TempDir("", "")
467 require.NoError(t, err)
468 defer os.RemoveAll(cfg.DataDir)
469 leecher, err := NewClient(cfg)
470 require.NoError(t, err)
471 defer leecher.Close()
472 testutil.ExportStatusWriter(leecher, "l")
474 cfg = TestingConfig()
476 cfg.DataDir, err = ioutil.TempDir("", "")
477 require.NoError(t, err)
478 defer os.RemoveAll(cfg.DataDir)
479 leecherLeecher, _ := NewClient(cfg)
480 require.NoError(t, err)
481 defer leecherLeecher.Close()
482 testutil.ExportStatusWriter(leecherLeecher, "ll")
483 leecherGreeting, ok, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
484 ret = TorrentSpecFromMetaInfo(mi)
488 require.NoError(t, err)
490 llg, ok, err := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
491 ret = TorrentSpecFromMetaInfo(mi)
495 require.NoError(t, err)
497 // Simultaneously DownloadAll in Leecher, and read the contents
498 // consecutively in LeecherLeecher. This non-deterministically triggered a
499 // case where the leecher wouldn't unchoke the LeecherLeecher.
500 var wg sync.WaitGroup
506 b, err := ioutil.ReadAll(r)
507 require.NoError(t, err)
508 assert.EqualValues(t, testutil.GreetingFileContents, b)
510 done := make(chan struct{})
512 go leecherGreeting.AddClientPeer(seeder)
513 go leecherGreeting.AddClientPeer(leecherLeecher)
517 leecherGreeting.DownloadAll()
523 func TestMergingTrackersByAddingSpecs(t *testing.T) {
524 cl, err := NewClient(TestingConfig())
525 require.NoError(t, err)
527 spec := TorrentSpec{}
528 T, new, _ := cl.AddTorrentSpec(&spec)
532 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
533 _, new, _ = cl.AddTorrentSpec(&spec)
535 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
536 // Because trackers are disabled in TestingConfig.
537 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
540 // We read from a piece which is marked completed, but is missing data.
541 func TestCompletedPieceWrongSize(t *testing.T) {
542 cfg := TestingConfig()
543 cfg.DefaultStorage = badStorage{}
544 cl, err := NewClient(cfg)
545 require.NoError(t, err)
547 info := metainfo.Info{
549 Pieces: make([]byte, 20),
550 Files: []metainfo.FileInfo{
551 {Path: []string{"greeting"}, Length: 13},
554 b, err := bencode.Marshal(info)
555 require.NoError(t, err)
556 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
558 InfoHash: metainfo.HashBytes(b),
560 require.NoError(t, err)
565 b, err = ioutil.ReadAll(r)
567 assert.NoError(t, err)
570 func BenchmarkAddLargeTorrent(b *testing.B) {
571 cfg := TestingConfig()
572 cfg.DisableTCP = true
573 cfg.DisableUTP = true
574 cfg.ListenHost = func(string) string { return "redonk" }
575 cl, err := NewClient(cfg)
576 require.NoError(b, err)
578 for range iter.N(b.N) {
579 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
587 func TestResponsive(t *testing.T) {
588 seederDataDir, mi := testutil.GreetingTestTorrent()
589 defer os.RemoveAll(seederDataDir)
590 cfg := TestingConfig()
592 cfg.DataDir = seederDataDir
593 seeder, err := NewClient(cfg)
596 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
597 seederTorrent.VerifyData()
598 leecherDataDir, err := ioutil.TempDir("", "")
600 defer os.RemoveAll(leecherDataDir)
601 cfg = TestingConfig()
602 cfg.DataDir = leecherDataDir
603 leecher, err := NewClient(cfg)
605 defer leecher.Close()
606 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
607 ret = TorrentSpecFromMetaInfo(mi)
611 leecherTorrent.AddClientPeer(seeder)
612 reader := leecherTorrent.NewReader()
614 reader.SetReadahead(0)
615 reader.SetResponsive()
617 _, err = reader.Seek(3, io.SeekStart)
618 require.NoError(t, err)
619 _, err = io.ReadFull(reader, b)
621 assert.EqualValues(t, "lo", string(b))
622 _, err = reader.Seek(11, io.SeekStart)
623 require.NoError(t, err)
624 n, err := io.ReadFull(reader, b)
626 assert.EqualValues(t, 2, n)
627 assert.EqualValues(t, "d\n", string(b))
630 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
631 seederDataDir, mi := testutil.GreetingTestTorrent()
632 defer os.RemoveAll(seederDataDir)
633 cfg := TestingConfig()
635 cfg.DataDir = seederDataDir
636 seeder, err := NewClient(cfg)
639 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
640 seederTorrent.VerifyData()
641 leecherDataDir, err := ioutil.TempDir("", "")
643 defer os.RemoveAll(leecherDataDir)
644 cfg = TestingConfig()
645 cfg.DataDir = leecherDataDir
646 leecher, err := NewClient(cfg)
648 defer leecher.Close()
649 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
650 ret = TorrentSpecFromMetaInfo(mi)
654 leecherTorrent.AddClientPeer(seeder)
655 reader := leecherTorrent.NewReader()
657 reader.SetReadahead(0)
658 reader.SetResponsive()
660 _, err = reader.Seek(3, io.SeekStart)
661 require.NoError(t, err)
662 _, err = io.ReadFull(reader, b)
664 assert.EqualValues(t, "lo", string(b))
665 go leecherTorrent.Drop()
666 _, err = reader.Seek(11, io.SeekStart)
667 require.NoError(t, err)
668 n, err := reader.Read(b)
669 assert.EqualError(t, err, "torrent closed")
670 assert.EqualValues(t, 0, n)
673 func TestDHTInheritBlocklist(t *testing.T) {
674 ipl := iplist.New(nil)
675 require.NotNil(t, ipl)
676 cfg := TestingConfig()
677 cfg.IPBlocklist = ipl
679 cl, err := NewClient(cfg)
680 require.NoError(t, err)
683 cl.eachDhtServer(func(s *dht.Server) {
684 assert.Equal(t, ipl, s.IPBlocklist())
687 assert.EqualValues(t, 2, numServers)
690 // Check that stuff is merged in subsequent AddTorrentSpec for the same
692 func TestAddTorrentSpecMerging(t *testing.T) {
693 cl, err := NewClient(TestingConfig())
694 require.NoError(t, err)
696 dir, mi := testutil.GreetingTestTorrent()
697 defer os.RemoveAll(dir)
698 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
699 InfoHash: mi.HashInfoBytes(),
701 require.NoError(t, err)
703 require.Nil(t, tt.Info())
704 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
705 require.NoError(t, err)
706 require.False(t, new)
707 require.NotNil(t, tt.Info())
710 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
711 dir, mi := testutil.GreetingTestTorrent()
713 cl, _ := NewClient(TestingConfig())
715 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
716 InfoHash: mi.HashInfoBytes(),
719 assert.EqualValues(t, 0, len(cl.Torrents()))
727 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
728 for i := range iter.N(info.NumPieces()) {
730 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
734 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
735 fileCacheDir, err := ioutil.TempDir("", "")
736 require.NoError(t, err)
737 defer os.RemoveAll(fileCacheDir)
738 fileCache, err := filecache.NewCache(fileCacheDir)
739 require.NoError(t, err)
740 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
741 defer os.RemoveAll(greetingDataTempDir)
742 filePieceStore := csf(fileCache)
743 defer filePieceStore.Close()
744 info, err := greetingMetainfo.UnmarshalInfo()
745 require.NoError(t, err)
746 ih := greetingMetainfo.HashInfoBytes()
747 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
748 require.NoError(t, err)
749 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
750 // require.Equal(t, len(testutil.GreetingFileContents), written)
751 // require.NoError(t, err)
752 for i := 0; i < info.NumPieces(); i++ {
754 if alreadyCompleted {
755 require.NoError(t, greetingData.Piece(p).MarkComplete())
758 cfg := TestingConfig()
759 // TODO: Disable network option?
760 cfg.DisableTCP = true
761 cfg.DisableUTP = true
762 cfg.DefaultStorage = filePieceStore
763 cl, err := NewClient(cfg)
764 require.NoError(t, err)
766 tt, err := cl.AddTorrent(greetingMetainfo)
767 require.NoError(t, err)
768 psrs := tt.PieceStateRuns()
769 assert.Len(t, psrs, 1)
770 assert.EqualValues(t, 3, psrs[0].Length)
771 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
772 if alreadyCompleted {
774 b, err := ioutil.ReadAll(r)
775 assert.NoError(t, err)
776 assert.EqualValues(t, testutil.GreetingFileContents, b)
780 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
781 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
784 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
785 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
788 func TestAddMetainfoWithNodes(t *testing.T) {
789 cfg := TestingConfig()
790 cfg.ListenHost = func(string) string { return "" }
792 cfg.DhtStartingNodes = func() ([]dht.Addr, error) { return nil, nil }
793 // For now, we want to just jam the nodes into the table, without
794 // verifying them first. Also the DHT code doesn't support mixing secure
795 // and insecure nodes if security is enabled (yet).
796 // cfg.DHTConfig.NoSecurity = true
797 cl, err := NewClient(cfg)
798 require.NoError(t, err)
800 sum := func() (ret int64) {
801 cl.eachDhtServer(func(s *dht.Server) {
802 ret += s.Stats().OutboundQueriesAttempted
806 assert.EqualValues(t, 0, sum())
807 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
808 require.NoError(t, err)
809 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
810 // check if the announce-list is here instead. TODO: Add nodes.
811 assert.Len(t, tt.metainfo.AnnounceList, 5)
812 // There are 6 nodes in the torrent file.
813 assert.EqualValues(t, 6*len(cl.dhtServers), sum())
816 type testDownloadCancelParams struct {
817 SetLeecherStorageCapacity bool
818 LeecherStorageCapacity int64
822 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
823 greetingTempDir, mi := testutil.GreetingTestTorrent()
824 defer os.RemoveAll(greetingTempDir)
825 cfg := TestingConfig()
827 cfg.DataDir = greetingTempDir
828 seeder, err := NewClient(cfg)
829 require.NoError(t, err)
831 testutil.ExportStatusWriter(seeder, "s")
832 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
833 seederTorrent.VerifyData()
834 leecherDataDir, err := ioutil.TempDir("", "")
835 require.NoError(t, err)
836 defer os.RemoveAll(leecherDataDir)
837 fc, err := filecache.NewCache(leecherDataDir)
838 require.NoError(t, err)
839 if ps.SetLeecherStorageCapacity {
840 fc.SetCapacity(ps.LeecherStorageCapacity)
842 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
843 cfg.DataDir = leecherDataDir
844 leecher, _ := NewClient(cfg)
845 defer leecher.Close()
846 testutil.ExportStatusWriter(leecher, "l")
847 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
848 ret = TorrentSpecFromMetaInfo(mi)
852 require.NoError(t, err)
854 psc := leecherGreeting.SubscribePieceStateChanges()
857 leecherGreeting.cl.mu.Lock()
858 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
860 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
862 leecherGreeting.cl.mu.Unlock()
863 done := make(chan struct{})
865 go leecherGreeting.AddClientPeer(seeder)
866 completes := make(map[int]bool, 3)
867 expected := func() map[int]bool {
869 return map[int]bool{0: false, 1: false, 2: false}
871 return map[int]bool{0: true, 1: true, 2: true}
874 for !reflect.DeepEqual(completes, expected) {
876 case _v := <-psc.Values:
877 v := _v.(PieceStateChange)
878 completes[v.Index] = v.Complete
883 func TestTorrentDownloadAll(t *testing.T) {
884 testDownloadCancel(t, testDownloadCancelParams{})
887 func TestTorrentDownloadAllThenCancel(t *testing.T) {
888 testDownloadCancel(t, testDownloadCancelParams{
893 // Ensure that it's an error for a peer to send an invalid have message.
894 func TestPeerInvalidHave(t *testing.T) {
895 cl, err := NewClient(TestingConfig())
896 require.NoError(t, err)
898 info := metainfo.Info{
900 Pieces: make([]byte, 20),
901 Files: []metainfo.FileInfo{{Length: 1}},
903 infoBytes, err := bencode.Marshal(info)
904 require.NoError(t, err)
905 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
906 InfoBytes: infoBytes,
907 InfoHash: metainfo.HashBytes(infoBytes),
908 Storage: badStorage{},
910 require.NoError(t, err)
916 assert.NoError(t, cn.peerSentHave(0))
917 assert.Error(t, cn.peerSentHave(1))
920 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
921 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
922 defer os.RemoveAll(greetingTempDir)
923 cfg := TestingConfig()
924 cfg.DataDir = greetingTempDir
925 seeder, err := NewClient(TestingConfig())
926 require.NoError(t, err)
927 seeder.AddTorrentSpec(&TorrentSpec{
928 InfoBytes: greetingMetainfo.InfoBytes,
932 // Check that when the listen port is 0, all the protocols listened on have
933 // the same port, and it isn't zero.
934 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
935 cl, err := NewClient(TestingConfig())
936 require.NoError(t, err)
938 port := cl.LocalPort()
939 assert.NotEqual(t, 0, port)
940 cl.eachListener(func(s socket) bool {
941 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
946 func TestClientDynamicListenTCPOnly(t *testing.T) {
947 cfg := TestingConfig()
948 cfg.DisableUTP = true
949 cl, err := NewClient(cfg)
950 require.NoError(t, err)
952 assert.NotEqual(t, 0, cl.LocalPort())
953 cl.eachListener(func(s socket) bool {
954 assert.True(t, isTcpNetwork(s.Addr().Network()))
959 func TestClientDynamicListenUTPOnly(t *testing.T) {
960 cfg := TestingConfig()
961 cfg.DisableTCP = true
962 cl, err := NewClient(cfg)
963 require.NoError(t, err)
965 assert.NotEqual(t, 0, cl.LocalPort())
966 cl.eachListener(func(s socket) bool {
967 assert.True(t, isUtpNetwork(s.Addr().Network()))
972 func TestClientDynamicListenPortNoProtocols(t *testing.T) {
973 cfg := TestingConfig()
974 cfg.DisableTCP = true
975 cfg.DisableUTP = true
976 cl, err := NewClient(cfg)
977 require.NoError(t, err)
979 assert.Equal(t, 0, cl.LocalPort())
982 func totalConns(tts []*Torrent) (ret int) {
983 for _, tt := range tts {
991 func TestSetMaxEstablishedConn(t *testing.T) {
992 ss := testutil.NewStatusServer(t)
995 ih := testutil.GreetingMetaInfo().HashInfoBytes()
996 cfg := TestingConfig()
997 cfg.DisableAcceptRateLimiting = true
998 cfg.dropDuplicatePeerIds = true
999 for i := range iter.N(3) {
1000 cl, err := NewClient(cfg)
1001 require.NoError(t, err)
1003 tt, _ := cl.AddTorrentInfoHash(ih)
1004 tt.SetMaxEstablishedConns(2)
1005 ss.HandleStatusWriter(cl, fmt.Sprintf("/%d", i))
1006 tts = append(tts, tt)
1008 addPeers := func() {
1009 for _, tt := range tts {
1010 for _, _tt := range tts {
1012 tt.AddClientPeer(_tt.cl)
1017 waitTotalConns := func(num int) {
1018 for totalConns(tts) != num {
1020 time.Sleep(time.Millisecond)
1025 tts[0].SetMaxEstablishedConns(1)
1027 tts[0].SetMaxEstablishedConns(0)
1029 tts[0].SetMaxEstablishedConns(1)
1032 tts[0].SetMaxEstablishedConns(2)
1037 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
1038 os.MkdirAll(dir, 0770)
1039 file, err := os.Create(filepath.Join(dir, name))
1040 require.NoError(t, err)
1041 file.Write([]byte(name))
1043 mi := metainfo.MetaInfo{}
1045 info := metainfo.Info{PieceLength: 256 * 1024}
1046 err = info.BuildFromFilePath(filepath.Join(dir, name))
1047 require.NoError(t, err)
1048 mi.InfoBytes, err = bencode.Marshal(info)
1049 require.NoError(t, err)
1050 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
1051 tr, err := cl.AddTorrent(&mi)
1052 require.NoError(t, err)
1053 require.True(t, tr.Seeding())
1058 // https://github.com/anacrolix/torrent/issues/114
1059 func TestMultipleTorrentsWithEncryption(t *testing.T) {
1060 cfg := TestingConfig()
1061 cfg.DisableUTP = true
1063 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
1064 cfg.ForceEncryption = true
1065 os.Mkdir(cfg.DataDir, 0755)
1066 server, err := NewClient(cfg)
1067 require.NoError(t, err)
1068 defer server.Close()
1069 testutil.ExportStatusWriter(server, "s")
1070 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
1071 makeMagnet(t, server, cfg.DataDir, "test2")
1072 cfg = TestingConfig()
1073 cfg.DisableUTP = true
1074 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
1075 cfg.ForceEncryption = true
1076 client, err := NewClient(cfg)
1077 require.NoError(t, err)
1078 defer client.Close()
1079 testutil.ExportStatusWriter(client, "c")
1080 tr, err := client.AddMagnet(magnet1)
1081 require.NoError(t, err)
1082 tr.AddClientPeer(server)
1088 func TestClientAddressInUse(t *testing.T) {
1089 s, _ := NewUtpSocket("udp", ":50007")
1093 cfg := TestingConfig().SetListenAddr(":50007")
1094 cl, err := NewClient(cfg)
1095 require.Error(t, err)