15 _ "github.com/anacrolix/envpprof"
16 "github.com/anacrolix/utp"
17 "github.com/bradfitz/iter"
18 "github.com/stretchr/testify/assert"
19 "github.com/stretchr/testify/require"
22 "github.com/anacrolix/torrent/bencode"
23 "github.com/anacrolix/torrent/data"
24 "github.com/anacrolix/torrent/data/blob"
25 "github.com/anacrolix/torrent/internal/testutil"
26 "github.com/anacrolix/torrent/metainfo"
27 "github.com/anacrolix/torrent/util"
31 log.SetFlags(log.LstdFlags | log.Lshortfile)
34 var TestingConfig = Config{
35 ListenAddr: "localhost:0",
37 DisableTrackers: true,
38 NoDefaultBlocklist: true,
39 DisableMetainfoCache: true,
40 DataDir: filepath.Join(os.TempDir(), "anacrolix"),
43 func TestClientDefault(t *testing.T) {
44 cl, err := NewClient(&TestingConfig)
51 func TestAddDropTorrent(t *testing.T) {
52 cl, err := NewClient(&TestingConfig)
57 dir, mi := testutil.GreetingTestTorrent()
58 defer os.RemoveAll(dir)
59 tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
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, err := newTorrent(func() (ih InfoHash) {
91 util.CopyExact(ih[:], mi.Info.Hash)
98 err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
102 if len(tor.Pieces) != 3 {
103 t.Fatal("wrong number of pieces")
106 tor.pendAllChunkSpecs(0)
107 assert.EqualValues(t, 3, p.numPendingChunks())
108 assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
111 func TestUnmarshalPEXMsg(t *testing.T) {
112 var m peerExchangeMessage
113 if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
116 if len(m.Added) != 2 {
119 if m.Added[0].Port != 0x506 {
124 func TestReducedDialTimeout(t *testing.T) {
125 for _, _case := range []struct {
129 ExpectedReduced time.Duration
131 {nominalDialTimeout, 40, 0, nominalDialTimeout},
132 {nominalDialTimeout, 40, 1, nominalDialTimeout},
133 {nominalDialTimeout, 40, 39, nominalDialTimeout},
134 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
135 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
136 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
138 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
139 expected := _case.ExpectedReduced
140 if expected < minDialTimeout {
141 expected = minDialTimeout
143 if reduced != expected {
144 t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
149 func TestUTPRawConn(t *testing.T) {
150 l, err := utp.NewSocket("")
163 // Connect a UTP peer to see if the RawConn will still work.
164 utpPeer, err := func() *utp.Socket {
165 s, _ := utp.NewSocket("")
167 }().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
169 t.Fatalf("error dialing utp listener: %s", err)
171 defer utpPeer.Close()
172 peer, err := net.ListenPacket("udp", ":0")
179 // How many messages to send. I've set this to double the channel buffer
180 // size in the raw packetConn.
182 readerStopped := make(chan struct{})
183 // The reader goroutine.
185 defer close(readerStopped)
186 b := make([]byte, 500)
187 for i := 0; i < N; i++ {
188 n, _, err := l.PacketConn().ReadFrom(b)
190 t.Fatalf("error reading from raw conn: %s", err)
194 fmt.Sscan(string(b[:n]), &d)
196 log.Printf("got wrong number: expected %d, got %d", i, d)
200 udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
204 for i := 0; i < N; i++ {
205 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
209 time.Sleep(time.Microsecond)
212 case <-readerStopped:
213 case <-time.After(time.Second):
214 t.Fatal("reader timed out")
216 if msgsReceived != N {
217 t.Fatalf("messages received: %d", msgsReceived)
221 func TestTwoClientsArbitraryPorts(t *testing.T) {
222 for i := 0; i < 2; i++ {
223 cl, err := NewClient(&TestingConfig)
231 func TestAddDropManyTorrents(t *testing.T) {
232 cl, _ := NewClient(&TestingConfig)
234 for i := range iter.N(1000) {
236 binary.PutVarint(spec.InfoHash[:], int64(i))
237 tt, new, err := cl.AddTorrentSpec(&spec)
248 func TestClientTransfer(t *testing.T) {
249 greetingTempDir, mi := testutil.GreetingTestTorrent()
250 defer os.RemoveAll(greetingTempDir)
253 cfg.DataDir = greetingTempDir
254 seeder, err := NewClient(&cfg)
259 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
260 leecherDataDir, err := ioutil.TempDir("", "")
264 defer os.RemoveAll(leecherDataDir)
265 // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
266 // return blob.TorrentData(info, leecherDataDir), nil
268 cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
269 leecher, _ := NewClient(&cfg)
270 defer leecher.Close()
271 leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
272 ret = TorrentSpecFromMetaInfo(mi)
276 leecherGreeting.AddPeers([]Peer{
278 IP: util.AddrIP(seeder.ListenAddr()),
279 Port: util.AddrPort(seeder.ListenAddr()),
282 r := leecherGreeting.NewReader()
284 _greeting, err := ioutil.ReadAll(r)
286 t.Fatalf("%q %s", string(_greeting), err)
288 greeting := string(_greeting)
289 if greeting != testutil.GreetingFileContents {
294 func TestReadaheadPieces(t *testing.T) {
295 for _, case_ := range []struct {
296 readaheadBytes, pieceLength int64
299 {5 * 1024 * 1024, 256 * 1024, 19},
300 {5 * 1024 * 1024, 5 * 1024 * 1024, 1},
301 {5*1024*1024 - 1, 5 * 1024 * 1024, 1},
302 {5 * 1024 * 1024, 5*1024*1024 - 1, 2},
303 {0, 5 * 1024 * 1024, 0},
304 {5 * 1024 * 1024, 1048576, 4},
306 pieces := readaheadPieces(case_.readaheadBytes, case_.pieceLength)
307 assert.Equal(t, case_.readaheadPieces, pieces, "%v", case_)
311 func (suite) TestMergingTrackersByAddingSpecs(c *check.C) {
312 cl, _ := NewClient(&TestingConfig)
314 spec := TorrentSpec{}
315 T, new, _ := cl.AddTorrentSpec(&spec)
319 spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
320 _, new, _ = cl.AddTorrentSpec(&spec)
324 c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a")
325 c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b")
328 type badData struct {
331 func (me badData) WriteAt(b []byte, off int64) (int, error) {
335 func (me badData) WriteSectionTo(w io.Writer, off, n int64) (int64, error) {
339 func (me badData) PieceComplete(piece int) bool {
343 func (me badData) PieceCompleted(piece int) error {
347 func (me badData) ReadAt(b []byte, off int64) (n int, err error) {
352 n = copy(b, []byte("hello")[off:])
356 var _ StatefulData = badData{}
358 // We read from a piece which is marked completed, but is missing data.
359 func TestCompletedPieceWrongSize(t *testing.T) {
361 cfg.TorrentDataOpener = func(*metainfo.Info) data.Data {
364 cl, _ := NewClient(&cfg)
366 tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
367 Info: &metainfo.InfoEx{
370 Pieces: make([]byte, 20),
371 Files: []metainfo.FileInfo{
372 metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
381 t.Fatal("expected new")
385 b := make([]byte, 20)
386 n, err := io.ReadFull(r, b)
387 if n != 5 || err != io.ErrUnexpectedEOF {
393 func BenchmarkAddLargeTorrent(b *testing.B) {
395 cfg.DisableTCP = true
396 cfg.DisableUTP = true
397 cfg.ListenAddr = "redonk"
398 cl, _ := NewClient(&cfg)
400 for range iter.N(b.N) {
401 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
409 func TestResponsive(t *testing.T) {
410 seederDataDir, mi := testutil.GreetingTestTorrent()
411 defer os.RemoveAll(seederDataDir)
414 cfg.DataDir = seederDataDir
415 seeder, err := NewClient(&cfg)
418 seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
419 leecherDataDir, err := ioutil.TempDir("", "")
421 defer os.RemoveAll(leecherDataDir)
423 cfg.DataDir = leecherDataDir
424 leecher, err := NewClient(&cfg)
426 defer leecher.Close()
427 leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
428 ret = TorrentSpecFromMetaInfo(mi)
432 leecherTorrent.AddPeers([]Peer{
434 IP: util.AddrIP(seeder.ListenAddr()),
435 Port: util.AddrPort(seeder.ListenAddr()),
438 reader := leecherTorrent.NewReader()
439 reader.SetReadahead(0)
440 reader.SetResponsive()
442 _, err = reader.ReadAt(b, 3)
444 assert.EqualValues(t, "lo", string(b))
445 n, err := reader.ReadAt(b, 11)
447 assert.EqualValues(t, 2, n)
448 assert.EqualValues(t, "d\n", string(b))