15 "github.com/anacrolix/dht"
16 _ "github.com/anacrolix/envpprof"
17 "github.com/anacrolix/missinggo"
18 "github.com/anacrolix/missinggo/filecache"
19 "github.com/anacrolix/torrent/bencode"
20 "github.com/anacrolix/torrent/internal/testutil"
21 "github.com/anacrolix/torrent/iplist"
22 "github.com/anacrolix/torrent/metainfo"
23 "github.com/anacrolix/torrent/storage"
24 "github.com/bradfitz/iter"
25 "github.com/stretchr/testify/assert"
26 "github.com/stretchr/testify/require"
27 "golang.org/x/time/rate"
30 func TestingConfig() *ClientConfig {
31 cfg := NewDefaultClientConfig()
32 cfg.ListenHost = LoopbackListenHost
34 cfg.DataDir = tempDir()
35 cfg.DisableTrackers = true
36 cfg.NoDefaultPortForwarding = true
37 cfg.DisableAcceptRateLimiting = true
41 func TestClientDefault(t *testing.T) {
42 cl, err := NewClient(TestingConfig())
43 require.NoError(t, err)
47 func TestClientNilConfig(t *testing.T) {
48 cl, err := NewClient(nil)
49 require.NoError(t, err)
53 func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
54 cfg := TestingConfig()
55 pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
56 require.NoError(t, err)
57 ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
59 cfg.DefaultStorage = ci
60 cl, err := NewClient(cfg)
61 require.NoError(t, err)
63 // And again, https://github.com/anacrolix/torrent/issues/158
64 cl, err = NewClient(cfg)
65 require.NoError(t, err)
69 func TestAddDropTorrent(t *testing.T) {
70 cl, err := NewClient(TestingConfig())
71 require.NoError(t, err)
73 dir, mi := testutil.GreetingTestTorrent()
74 defer os.RemoveAll(dir)
75 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
76 require.NoError(t, err)
78 tt.SetMaxEstablishedConns(0)
79 tt.SetMaxEstablishedConns(1)
83 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
88 func TestAddTorrentNoUsableURLs(t *testing.T) {
93 func TestAddPeersToUnknownTorrent(t *testing.T) {
98 func TestPieceHashSize(t *testing.T) {
99 assert.Equal(t, 20, pieceHash.Size())
102 func TestTorrentInitialState(t *testing.T) {
103 dir, mi := testutil.GreetingTestTorrent()
104 defer os.RemoveAll(dir)
106 config: &ClientConfig{},
109 tor := cl.newTorrent(
111 storage.NewFileWithCompletion(tempDir(), storage.NewMapPieceCompletion()),
115 err := tor.setInfoBytes(mi.InfoBytes)
117 require.NoError(t, err)
118 require.Len(t, tor.pieces, 3)
119 tor.pendAllChunkSpecs(0)
121 assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
123 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
126 func TestReducedDialTimeout(t *testing.T) {
127 cfg := NewDefaultClientConfig()
128 for _, _case := range []struct {
132 ExpectedReduced time.Duration
134 {cfg.NominalDialTimeout, 40, 0, cfg.NominalDialTimeout},
135 {cfg.NominalDialTimeout, 40, 1, cfg.NominalDialTimeout},
136 {cfg.NominalDialTimeout, 40, 39, cfg.NominalDialTimeout},
137 {cfg.NominalDialTimeout, 40, 40, cfg.NominalDialTimeout / 2},
138 {cfg.NominalDialTimeout, 40, 80, cfg.NominalDialTimeout / 3},
139 {cfg.NominalDialTimeout, 40, 4000, cfg.NominalDialTimeout / 101},
141 reduced := reducedDialTimeout(cfg.MinDialTimeout, _case.Max, _case.HalfOpenLimit, _case.PendingPeers)
142 expected := _case.ExpectedReduced
143 if expected < cfg.MinDialTimeout {
144 expected = cfg.MinDialTimeout
146 if reduced != expected {
147 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
152 func TestAddDropManyTorrents(t *testing.T) {
153 cl, err := NewClient(TestingConfig())
154 require.NoError(t, err)
156 for i := range iter.N(1000) {
158 binary.PutVarint(spec.InfoHash[:], int64(i))
159 tt, new, err := cl.AddTorrentSpec(&spec)
160 assert.NoError(t, err)
166 type FileCacheClientStorageFactoryParams struct {
169 Wrapper func(*filecache.Cache) storage.ClientImpl
172 func NewFileCacheClientStorageFactory(ps FileCacheClientStorageFactoryParams) storageFactory {
173 return func(dataDir string) storage.ClientImpl {
174 fc, err := filecache.NewCache(dataDir)
179 fc.SetCapacity(ps.Capacity)
181 return ps.Wrapper(fc)
185 type storageFactory func(string) storage.ClientImpl
187 func TestClientTransferDefault(t *testing.T) {
188 testClientTransfer(t, testClientTransferParams{
189 ExportClientStatus: true,
190 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
191 Wrapper: fileCachePieceResourceStorage,
196 func TestClientTransferRateLimitedUpload(t *testing.T) {
197 started := time.Now()
198 testClientTransfer(t, testClientTransferParams{
199 // We are uploading 13 bytes (the length of the greeting torrent). The
200 // chunks are 2 bytes in length. Then the smallest burst we can run
201 // with is 2. Time taken is (13-burst)/rate.
202 SeederUploadRateLimiter: rate.NewLimiter(11, 2),
203 ExportClientStatus: true,
205 require.True(t, time.Since(started) > time.Second)
208 func TestClientTransferRateLimitedDownload(t *testing.T) {
209 testClientTransfer(t, testClientTransferParams{
210 LeecherDownloadRateLimiter: rate.NewLimiter(512, 512),
214 func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
215 return storage.NewResourcePieces(fc.AsResourceProvider())
218 func TestClientTransferSmallCache(t *testing.T) {
219 testClientTransfer(t, testClientTransferParams{
220 LeecherStorage: NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
222 // Going below the piece length means it can't complete a piece so
223 // that it can be hashed.
225 Wrapper: fileCachePieceResourceStorage,
228 // Can't readahead too far or the cache will thrash and drop data we
231 ExportClientStatus: true,
235 func TestClientTransferVarious(t *testing.T) {
237 for _, ls := range []storageFactory{
238 NewFileCacheClientStorageFactory(FileCacheClientStorageFactoryParams{
239 Wrapper: fileCachePieceResourceStorage,
244 for _, ss := range []func(string) storage.ClientImpl{
248 for _, responsive := range []bool{false, true} {
249 testClientTransfer(t, testClientTransferParams{
250 Responsive: responsive,
254 for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
255 testClientTransfer(t, testClientTransferParams{
257 Responsive: responsive,
259 Readahead: readahead,
268 type testClientTransferParams struct {
272 ExportClientStatus bool
273 LeecherStorage func(string) storage.ClientImpl
274 SeederStorage func(string) storage.ClientImpl
275 SeederUploadRateLimiter *rate.Limiter
276 LeecherDownloadRateLimiter *rate.Limiter
279 // Creates a seeder and a leecher, and ensures the data transfers when a read
280 // is attempted on the leecher.
281 func testClientTransfer(t *testing.T, ps testClientTransferParams) {
282 greetingTempDir, mi := testutil.GreetingTestTorrent()
283 defer os.RemoveAll(greetingTempDir)
284 // Create seeder and a Torrent.
285 cfg := TestingConfig()
287 if ps.SeederUploadRateLimiter != nil {
288 cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
290 // cfg.ListenAddr = "localhost:4000"
291 if ps.SeederStorage != nil {
292 cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
293 defer cfg.DefaultStorage.Close()
295 cfg.DataDir = greetingTempDir
297 seeder, err := NewClient(cfg)
298 require.NoError(t, err)
299 if ps.ExportClientStatus {
300 defer testutil.ExportStatusWriter(seeder, "s")()
302 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
303 // Run a Stats right after Closing the Client. This will trigger the Stats
304 // panic in #214 caused by RemoteAddr on Closed uTP sockets.
305 defer seederTorrent.Stats()
307 seederTorrent.VerifyData()
308 // Create leecher and a Torrent.
309 leecherDataDir, err := ioutil.TempDir("", "")
310 require.NoError(t, err)
311 defer os.RemoveAll(leecherDataDir)
312 cfg = TestingConfig()
313 if ps.LeecherStorage == nil {
314 cfg.DataDir = leecherDataDir
316 cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
318 if ps.LeecherDownloadRateLimiter != nil {
319 cfg.DownloadRateLimiter = ps.LeecherDownloadRateLimiter
322 leecher, err := NewClient(cfg)
323 require.NoError(t, err)
324 defer leecher.Close()
325 if ps.ExportClientStatus {
326 defer testutil.ExportStatusWriter(leecher, "l")()
328 leecherTorrent, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
329 ret = TorrentSpecFromMetaInfo(mi)
333 require.NoError(t, err)
335 // Now do some things with leecher and seeder.
336 leecherTorrent.AddClientPeer(seeder)
337 // The Torrent should not be interested in obtaining peers, so the one we
338 // just added should be the only one.
339 assert.False(t, leecherTorrent.Seeding())
340 assert.EqualValues(t, 1, leecherTorrent.Stats().PendingPeers)
341 r := leecherTorrent.NewReader()
347 r.SetReadahead(ps.Readahead)
349 assertReadAllGreeting(t, r)
351 seederStats := seederTorrent.Stats()
352 assert.True(t, 13 <= seederStats.BytesWrittenData.Int64())
353 assert.True(t, 8 <= seederStats.ChunksWritten.Int64())
355 leecherStats := leecherTorrent.Stats()
356 assert.True(t, 13 <= leecherStats.BytesReadData.Int64())
357 assert.True(t, 8 <= leecherStats.ChunksRead.Int64())
359 // Try reading through again for the cases where the torrent data size
360 // exceeds the size of the cache.
361 assertReadAllGreeting(t, r)
364 func assertReadAllGreeting(t *testing.T, r io.ReadSeeker) {
365 pos, err := r.Seek(0, io.SeekStart)
366 assert.NoError(t, err)
367 assert.EqualValues(t, 0, pos)
368 _greeting, err := ioutil.ReadAll(r)
369 assert.NoError(t, err)
370 assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
373 // Check that after completing leeching, a leecher transitions to a seeding
374 // correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
375 func TestSeedAfterDownloading(t *testing.T) {
376 greetingTempDir, mi := testutil.GreetingTestTorrent()
377 defer os.RemoveAll(greetingTempDir)
379 cfg := TestingConfig()
381 cfg.DataDir = greetingTempDir
382 seeder, err := NewClient(cfg)
383 require.NoError(t, err)
385 defer testutil.ExportStatusWriter(seeder, "s")()
386 seederTorrent, ok, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
387 require.NoError(t, err)
389 seederTorrent.VerifyData()
391 cfg = TestingConfig()
393 cfg.DataDir, err = ioutil.TempDir("", "")
394 require.NoError(t, err)
395 defer os.RemoveAll(cfg.DataDir)
396 leecher, err := NewClient(cfg)
397 require.NoError(t, err)
398 defer leecher.Close()
399 defer testutil.ExportStatusWriter(leecher, "l")()
401 cfg = TestingConfig()
403 cfg.DataDir, err = ioutil.TempDir("", "")
404 require.NoError(t, err)
405 defer os.RemoveAll(cfg.DataDir)
406 leecherLeecher, _ := NewClient(cfg)
407 require.NoError(t, err)
408 defer leecherLeecher.Close()
409 defer testutil.ExportStatusWriter(leecherLeecher, "ll")()
410 leecherGreeting, ok, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
411 ret = TorrentSpecFromMetaInfo(mi)
415 require.NoError(t, err)
417 llg, ok, err := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
418 ret = TorrentSpecFromMetaInfo(mi)
422 require.NoError(t, err)
424 // Simultaneously DownloadAll in Leecher, and read the contents
425 // consecutively in LeecherLeecher. This non-deterministically triggered a
426 // case where the leecher wouldn't unchoke the LeecherLeecher.
427 var wg sync.WaitGroup
433 b, err := ioutil.ReadAll(r)
434 require.NoError(t, err)
435 assert.EqualValues(t, testutil.GreetingFileContents, b)
437 done := make(chan struct{})
439 go leecherGreeting.AddClientPeer(seeder)
440 go leecherGreeting.AddClientPeer(leecherLeecher)
444 leecherGreeting.DownloadAll()
450 func TestMergingTrackersByAddingSpecs(t *testing.T) {
451 cl, err := NewClient(TestingConfig())
452 require.NoError(t, err)
454 spec := TorrentSpec{}
455 T, new, _ := cl.AddTorrentSpec(&spec)
459 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
460 _, new, _ = cl.AddTorrentSpec(&spec)
462 assert.EqualValues(t, [][]string{{"http://a"}, {"udp://b"}}, T.metainfo.AnnounceList)
463 // Because trackers are disabled in TestingConfig.
464 assert.EqualValues(t, 0, len(T.trackerAnnouncers))
467 // We read from a piece which is marked completed, but is missing data.
468 func TestCompletedPieceWrongSize(t *testing.T) {
469 cfg := TestingConfig()
470 cfg.DefaultStorage = badStorage{}
471 cl, err := NewClient(cfg)
472 require.NoError(t, err)
474 info := metainfo.Info{
476 Pieces: make([]byte, 20),
477 Files: []metainfo.FileInfo{
478 {Path: []string{"greeting"}, Length: 13},
481 b, err := bencode.Marshal(info)
482 require.NoError(t, err)
483 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
485 InfoHash: metainfo.HashBytes(b),
487 require.NoError(t, err)
492 b, err = ioutil.ReadAll(r)
494 assert.NoError(t, err)
497 func BenchmarkAddLargeTorrent(b *testing.B) {
498 cfg := TestingConfig()
499 cfg.DisableTCP = true
500 cfg.DisableUTP = true
501 cfg.ListenHost = func(string) string { return "redonk" }
502 cl, err := NewClient(cfg)
503 require.NoError(b, err)
505 for range iter.N(b.N) {
506 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
514 func TestResponsive(t *testing.T) {
515 seederDataDir, mi := testutil.GreetingTestTorrent()
516 defer os.RemoveAll(seederDataDir)
517 cfg := TestingConfig()
519 cfg.DataDir = seederDataDir
520 seeder, err := NewClient(cfg)
523 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
524 seederTorrent.VerifyData()
525 leecherDataDir, err := ioutil.TempDir("", "")
527 defer os.RemoveAll(leecherDataDir)
528 cfg = TestingConfig()
529 cfg.DataDir = leecherDataDir
530 leecher, err := NewClient(cfg)
532 defer leecher.Close()
533 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
534 ret = TorrentSpecFromMetaInfo(mi)
538 leecherTorrent.AddClientPeer(seeder)
539 reader := leecherTorrent.NewReader()
541 reader.SetReadahead(0)
542 reader.SetResponsive()
544 _, err = reader.Seek(3, io.SeekStart)
545 require.NoError(t, err)
546 _, err = io.ReadFull(reader, b)
548 assert.EqualValues(t, "lo", string(b))
549 _, err = reader.Seek(11, io.SeekStart)
550 require.NoError(t, err)
551 n, err := io.ReadFull(reader, b)
553 assert.EqualValues(t, 2, n)
554 assert.EqualValues(t, "d\n", string(b))
557 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
558 seederDataDir, mi := testutil.GreetingTestTorrent()
559 defer os.RemoveAll(seederDataDir)
560 cfg := TestingConfig()
562 cfg.DataDir = seederDataDir
563 seeder, err := NewClient(cfg)
566 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
567 seederTorrent.VerifyData()
568 leecherDataDir, err := ioutil.TempDir("", "")
570 defer os.RemoveAll(leecherDataDir)
571 cfg = TestingConfig()
572 cfg.DataDir = leecherDataDir
573 leecher, err := NewClient(cfg)
575 defer leecher.Close()
576 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
577 ret = TorrentSpecFromMetaInfo(mi)
581 leecherTorrent.AddClientPeer(seeder)
582 reader := leecherTorrent.NewReader()
584 reader.SetReadahead(0)
585 reader.SetResponsive()
587 _, err = reader.Seek(3, io.SeekStart)
588 require.NoError(t, err)
589 _, err = io.ReadFull(reader, b)
591 assert.EqualValues(t, "lo", string(b))
592 go leecherTorrent.Drop()
593 _, err = reader.Seek(11, io.SeekStart)
594 require.NoError(t, err)
595 n, err := reader.Read(b)
596 assert.EqualError(t, err, "torrent closed")
597 assert.EqualValues(t, 0, n)
600 func TestDHTInheritBlocklist(t *testing.T) {
601 ipl := iplist.New(nil)
602 require.NotNil(t, ipl)
603 cfg := TestingConfig()
604 cfg.IPBlocklist = ipl
606 cl, err := NewClient(cfg)
607 require.NoError(t, err)
610 cl.eachDhtServer(func(s *dht.Server) {
611 assert.Equal(t, ipl, s.IPBlocklist())
614 assert.EqualValues(t, 2, numServers)
617 // Check that stuff is merged in subsequent AddTorrentSpec for the same
619 func TestAddTorrentSpecMerging(t *testing.T) {
620 cl, err := NewClient(TestingConfig())
621 require.NoError(t, err)
623 dir, mi := testutil.GreetingTestTorrent()
624 defer os.RemoveAll(dir)
625 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
626 InfoHash: mi.HashInfoBytes(),
628 require.NoError(t, err)
630 require.Nil(t, tt.Info())
631 _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
632 require.NoError(t, err)
633 require.False(t, new)
634 require.NotNil(t, tt.Info())
637 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
638 dir, mi := testutil.GreetingTestTorrent()
640 cl, _ := NewClient(TestingConfig())
642 tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
643 InfoHash: mi.HashInfoBytes(),
646 assert.EqualValues(t, 0, len(cl.Torrents()))
654 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
655 for i := range iter.N(info.NumPieces()) {
657 ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
661 func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.ClientImpl) {
662 fileCacheDir, err := ioutil.TempDir("", "")
663 require.NoError(t, err)
664 defer os.RemoveAll(fileCacheDir)
665 fileCache, err := filecache.NewCache(fileCacheDir)
666 require.NoError(t, err)
667 greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
668 defer os.RemoveAll(greetingDataTempDir)
669 filePieceStore := csf(fileCache)
670 defer filePieceStore.Close()
671 info, err := greetingMetainfo.UnmarshalInfo()
672 require.NoError(t, err)
673 ih := greetingMetainfo.HashInfoBytes()
674 greetingData, err := storage.NewClient(filePieceStore).OpenTorrent(&info, ih)
675 require.NoError(t, err)
676 writeTorrentData(greetingData, info, []byte(testutil.GreetingFileContents))
677 // require.Equal(t, len(testutil.GreetingFileContents), written)
678 // require.NoError(t, err)
679 for i := 0; i < info.NumPieces(); i++ {
681 if alreadyCompleted {
682 require.NoError(t, greetingData.Piece(p).MarkComplete())
685 cfg := TestingConfig()
686 // TODO: Disable network option?
687 cfg.DisableTCP = true
688 cfg.DisableUTP = true
689 cfg.DefaultStorage = filePieceStore
690 cl, err := NewClient(cfg)
691 require.NoError(t, err)
693 tt, err := cl.AddTorrent(greetingMetainfo)
694 require.NoError(t, err)
695 psrs := tt.PieceStateRuns()
696 assert.Len(t, psrs, 1)
697 assert.EqualValues(t, 3, psrs[0].Length)
698 assert.Equal(t, alreadyCompleted, psrs[0].Complete)
699 if alreadyCompleted {
701 b, err := ioutil.ReadAll(r)
702 assert.NoError(t, err)
703 assert.EqualValues(t, testutil.GreetingFileContents, b)
707 func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
708 testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
711 func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
712 testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
715 func TestAddMetainfoWithNodes(t *testing.T) {
716 cfg := TestingConfig()
717 cfg.ListenHost = func(string) string { return "" }
719 cfg.DhtStartingNodes = func() ([]dht.Addr, error) { return nil, nil }
720 // For now, we want to just jam the nodes into the table, without
721 // verifying them first. Also the DHT code doesn't support mixing secure
722 // and insecure nodes if security is enabled (yet).
723 // cfg.DHTConfig.NoSecurity = true
724 cl, err := NewClient(cfg)
725 require.NoError(t, err)
727 sum := func() (ret int64) {
728 cl.eachDhtServer(func(s *dht.Server) {
729 ret += s.Stats().OutboundQueriesAttempted
733 assert.EqualValues(t, 0, sum())
734 tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
735 require.NoError(t, err)
736 // Nodes are not added or exposed in Torrent's metainfo. We just randomly
737 // check if the announce-list is here instead. TODO: Add nodes.
738 assert.Len(t, tt.metainfo.AnnounceList, 5)
739 // There are 6 nodes in the torrent file.
740 assert.EqualValues(t, 6*len(cl.dhtServers), sum())
743 type testDownloadCancelParams struct {
744 SetLeecherStorageCapacity bool
745 LeecherStorageCapacity int64
749 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
750 greetingTempDir, mi := testutil.GreetingTestTorrent()
751 defer os.RemoveAll(greetingTempDir)
752 cfg := TestingConfig()
754 cfg.DataDir = greetingTempDir
755 seeder, err := NewClient(cfg)
756 require.NoError(t, err)
758 defer testutil.ExportStatusWriter(seeder, "s")()
759 seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
760 seederTorrent.VerifyData()
761 leecherDataDir, err := ioutil.TempDir("", "")
762 require.NoError(t, err)
763 defer os.RemoveAll(leecherDataDir)
764 fc, err := filecache.NewCache(leecherDataDir)
765 require.NoError(t, err)
766 if ps.SetLeecherStorageCapacity {
767 fc.SetCapacity(ps.LeecherStorageCapacity)
769 cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider())
770 cfg.DataDir = leecherDataDir
771 leecher, _ := NewClient(cfg)
772 defer leecher.Close()
773 defer testutil.ExportStatusWriter(leecher, "l")()
774 leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
775 ret = TorrentSpecFromMetaInfo(mi)
779 require.NoError(t, err)
781 psc := leecherGreeting.SubscribePieceStateChanges()
784 leecherGreeting.cl.lock()
785 leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
787 leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
789 leecherGreeting.cl.unlock()
790 done := make(chan struct{})
792 go leecherGreeting.AddClientPeer(seeder)
793 completes := make(map[int]bool, 3)
794 expected := func() map[int]bool {
796 return map[int]bool{0: false, 1: false, 2: false}
798 return map[int]bool{0: true, 1: true, 2: true}
801 for !reflect.DeepEqual(completes, expected) {
803 case _v := <-psc.Values:
804 v := _v.(PieceStateChange)
805 completes[v.Index] = v.Complete
810 func TestTorrentDownloadAll(t *testing.T) {
811 testDownloadCancel(t, testDownloadCancelParams{})
814 func TestTorrentDownloadAllThenCancel(t *testing.T) {
815 testDownloadCancel(t, testDownloadCancelParams{
820 // Ensure that it's an error for a peer to send an invalid have message.
821 func TestPeerInvalidHave(t *testing.T) {
822 cl, err := NewClient(TestingConfig())
823 require.NoError(t, err)
825 info := metainfo.Info{
827 Pieces: make([]byte, 20),
828 Files: []metainfo.FileInfo{{Length: 1}},
830 infoBytes, err := bencode.Marshal(info)
831 require.NoError(t, err)
832 tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
833 InfoBytes: infoBytes,
834 InfoHash: metainfo.HashBytes(infoBytes),
835 Storage: badStorage{},
837 require.NoError(t, err)
843 assert.NoError(t, cn.peerSentHave(0))
844 assert.Error(t, cn.peerSentHave(1))
847 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
848 greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
849 defer os.RemoveAll(greetingTempDir)
850 cfg := TestingConfig()
851 cfg.DataDir = greetingTempDir
852 seeder, err := NewClient(TestingConfig())
853 require.NoError(t, err)
854 seeder.AddTorrentSpec(&TorrentSpec{
855 InfoBytes: greetingMetainfo.InfoBytes,
859 // Check that when the listen port is 0, all the protocols listened on have
860 // the same port, and it isn't zero.
861 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
862 cl, err := NewClient(TestingConfig())
863 require.NoError(t, err)
865 port := cl.LocalPort()
866 assert.NotEqual(t, 0, port)
867 cl.eachListener(func(s socket) bool {
868 assert.Equal(t, port, missinggo.AddrPort(s.Addr()))
873 func TestClientDynamicListenTCPOnly(t *testing.T) {
874 cfg := TestingConfig()
875 cfg.DisableUTP = true
876 cfg.DisableTCP = false
877 cl, err := NewClient(cfg)
878 require.NoError(t, err)
880 assert.NotEqual(t, 0, cl.LocalPort())
883 func TestClientDynamicListenUTPOnly(t *testing.T) {
884 cfg := TestingConfig()
885 cfg.DisableTCP = true
886 cfg.DisableUTP = false
887 cl, err := NewClient(cfg)
888 require.NoError(t, err)
890 assert.NotEqual(t, 0, cl.LocalPort())
893 func totalConns(tts []*Torrent) (ret int) {
894 for _, tt := range tts {
902 func TestSetMaxEstablishedConn(t *testing.T) {
904 ih := testutil.GreetingMetaInfo().HashInfoBytes()
905 cfg := TestingConfig()
906 cfg.DisableAcceptRateLimiting = true
907 cfg.dropDuplicatePeerIds = true
908 for i := range iter.N(3) {
909 cl, err := NewClient(cfg)
910 require.NoError(t, err)
912 tt, _ := cl.AddTorrentInfoHash(ih)
913 tt.SetMaxEstablishedConns(2)
914 defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))()
915 tts = append(tts, tt)
918 for _, tt := range tts {
919 for _, _tt := range tts {
921 tt.AddClientPeer(_tt.cl)
926 waitTotalConns := func(num int) {
927 for totalConns(tts) != num {
929 time.Sleep(time.Millisecond)
934 tts[0].SetMaxEstablishedConns(1)
936 tts[0].SetMaxEstablishedConns(0)
938 tts[0].SetMaxEstablishedConns(1)
941 tts[0].SetMaxEstablishedConns(2)
946 func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
947 os.MkdirAll(dir, 0770)
948 file, err := os.Create(filepath.Join(dir, name))
949 require.NoError(t, err)
950 file.Write([]byte(name))
952 mi := metainfo.MetaInfo{}
954 info := metainfo.Info{PieceLength: 256 * 1024}
955 err = info.BuildFromFilePath(filepath.Join(dir, name))
956 require.NoError(t, err)
957 mi.InfoBytes, err = bencode.Marshal(info)
958 require.NoError(t, err)
959 magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
960 tr, err := cl.AddTorrent(&mi)
961 require.NoError(t, err)
962 require.True(t, tr.Seeding())
967 // https://github.com/anacrolix/torrent/issues/114
968 func TestMultipleTorrentsWithEncryption(t *testing.T) {
969 cfg := TestingConfig()
970 cfg.DisableUTP = true
972 cfg.DataDir = filepath.Join(cfg.DataDir, "server")
973 cfg.ForceEncryption = true
974 os.Mkdir(cfg.DataDir, 0755)
975 server, err := NewClient(cfg)
976 require.NoError(t, err)
978 defer testutil.ExportStatusWriter(server, "s")()
979 magnet1 := makeMagnet(t, server, cfg.DataDir, "test1")
980 makeMagnet(t, server, cfg.DataDir, "test2")
981 cfg = TestingConfig()
982 cfg.DisableUTP = true
983 cfg.DataDir = filepath.Join(cfg.DataDir, "client")
984 cfg.ForceEncryption = true
985 client, err := NewClient(cfg)
986 require.NoError(t, err)
988 defer testutil.ExportStatusWriter(client, "c")()
989 tr, err := client.AddMagnet(magnet1)
990 require.NoError(t, err)
991 tr.AddClientPeer(server)
997 func TestClientAddressInUse(t *testing.T) {
998 s, _ := NewUtpSocket("udp", ":50007", nil)
1002 cfg := TestingConfig().SetListenAddr(":50007")
1003 cl, err := NewClient(cfg)
1004 require.Error(t, err)
1008 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
1009 cc := TestingConfig()
1010 cc.DisableUTP = true
1012 cl, err := NewClient(cc)
1013 require.NoError(t, err)
1015 assert.NotEmpty(t, cl.DhtServers())