]> Sergey Matveev's repositories - btrtrc.git/blob - client_test.go
Allow chunk size to be specified per torrent
[btrtrc.git] / client_test.go
1 package torrent
2
3 import (
4         "encoding/binary"
5         "fmt"
6         "io"
7         "io/ioutil"
8         "log"
9         "net"
10         "os"
11         "path/filepath"
12         "testing"
13         "time"
14
15         _ "github.com/anacrolix/envpprof"
16         "github.com/anacrolix/utp"
17         "github.com/bradfitz/iter"
18         "github.com/stretchr/testify/assert"
19         "gopkg.in/check.v1"
20
21         "github.com/anacrolix/torrent/bencode"
22         "github.com/anacrolix/torrent/data"
23         "github.com/anacrolix/torrent/data/blob"
24         "github.com/anacrolix/torrent/internal/testutil"
25         "github.com/anacrolix/torrent/metainfo"
26         "github.com/anacrolix/torrent/util"
27 )
28
29 func init() {
30         log.SetFlags(log.LstdFlags | log.Lshortfile)
31 }
32
33 var TestingConfig = Config{
34         ListenAddr:           "localhost:0",
35         NoDHT:                true,
36         DisableTrackers:      true,
37         NoDefaultBlocklist:   true,
38         DisableMetainfoCache: true,
39         DataDir:              filepath.Join(os.TempDir(), "anacrolix"),
40 }
41
42 func TestClientDefault(t *testing.T) {
43         cl, err := NewClient(&TestingConfig)
44         if err != nil {
45                 t.Fatal(err)
46         }
47         cl.Close()
48 }
49
50 func TestAddDropTorrent(t *testing.T) {
51         cl, err := NewClient(&TestingConfig)
52         if err != nil {
53                 t.Fatal(err)
54         }
55         defer cl.Close()
56         dir, mi := testutil.GreetingTestTorrent()
57         defer os.RemoveAll(dir)
58         tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
59         if err != nil {
60                 t.Fatal(err)
61         }
62         if !new {
63                 t.FailNow()
64         }
65         tt.Drop()
66 }
67
68 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
69         t.SkipNow()
70 }
71
72 func TestAddTorrentNoUsableURLs(t *testing.T) {
73         t.SkipNow()
74 }
75
76 func TestAddPeersToUnknownTorrent(t *testing.T) {
77         t.SkipNow()
78 }
79
80 func TestPieceHashSize(t *testing.T) {
81         if pieceHash.Size() != 20 {
82                 t.FailNow()
83         }
84 }
85
86 func TestTorrentInitialState(t *testing.T) {
87         dir, mi := testutil.GreetingTestTorrent()
88         defer os.RemoveAll(dir)
89         tor, err := newTorrent(func() (ih InfoHash) {
90                 util.CopyExact(ih[:], mi.Info.Hash)
91                 return
92         }())
93         if err != nil {
94                 t.Fatal(err)
95         }
96         tor.chunkSize = 2
97         err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
98         if err != nil {
99                 t.Fatal(err)
100         }
101         if len(tor.Pieces) != 3 {
102                 t.Fatal("wrong number of pieces")
103         }
104         p := tor.Pieces[0]
105         tor.pendAllChunkSpecs(0)
106         assert.EqualValues(t, 3, p.numPendingChunks())
107         assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
108 }
109
110 func TestUnmarshalPEXMsg(t *testing.T) {
111         var m peerExchangeMessage
112         if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
113                 t.Fatal(err)
114         }
115         if len(m.Added) != 2 {
116                 t.FailNow()
117         }
118         if m.Added[0].Port != 0x506 {
119                 t.FailNow()
120         }
121 }
122
123 func TestReducedDialTimeout(t *testing.T) {
124         for _, _case := range []struct {
125                 Max             time.Duration
126                 HalfOpenLimit   int
127                 PendingPeers    int
128                 ExpectedReduced time.Duration
129         }{
130                 {nominalDialTimeout, 40, 0, nominalDialTimeout},
131                 {nominalDialTimeout, 40, 1, nominalDialTimeout},
132                 {nominalDialTimeout, 40, 39, nominalDialTimeout},
133                 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
134                 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
135                 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
136         } {
137                 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
138                 expected := _case.ExpectedReduced
139                 if expected < minDialTimeout {
140                         expected = minDialTimeout
141                 }
142                 if reduced != expected {
143                         t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
144                 }
145         }
146 }
147
148 func TestUTPRawConn(t *testing.T) {
149         l, err := utp.NewSocket("")
150         if err != nil {
151                 t.Fatal(err)
152         }
153         defer l.Close()
154         go func() {
155                 for {
156                         _, err := l.Accept()
157                         if err != nil {
158                                 break
159                         }
160                 }
161         }()
162         // Connect a UTP peer to see if the RawConn will still work.
163         utpPeer, err := func() *utp.Socket {
164                 s, _ := utp.NewSocket("")
165                 return s
166         }().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
167         if err != nil {
168                 t.Fatalf("error dialing utp listener: %s", err)
169         }
170         defer utpPeer.Close()
171         peer, err := net.ListenPacket("udp", ":0")
172         if err != nil {
173                 t.Fatal(err)
174         }
175         defer peer.Close()
176
177         msgsReceived := 0
178         // How many messages to send. I've set this to double the channel buffer
179         // size in the raw packetConn.
180         const N = 200
181         readerStopped := make(chan struct{})
182         // The reader goroutine.
183         go func() {
184                 defer close(readerStopped)
185                 b := make([]byte, 500)
186                 for i := 0; i < N; i++ {
187                         n, _, err := l.PacketConn().ReadFrom(b)
188                         if err != nil {
189                                 t.Fatalf("error reading from raw conn: %s", err)
190                         }
191                         msgsReceived++
192                         var d int
193                         fmt.Sscan(string(b[:n]), &d)
194                         if d != i {
195                                 log.Printf("got wrong number: expected %d, got %d", i, d)
196                         }
197                 }
198         }()
199         udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
200         if err != nil {
201                 t.Fatal(err)
202         }
203         for i := 0; i < N; i++ {
204                 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
205                 if err != nil {
206                         t.Fatal(err)
207                 }
208                 time.Sleep(time.Microsecond)
209         }
210         select {
211         case <-readerStopped:
212         case <-time.After(time.Second):
213                 t.Fatal("reader timed out")
214         }
215         if msgsReceived != N {
216                 t.Fatalf("messages received: %d", msgsReceived)
217         }
218 }
219
220 func TestTwoClientsArbitraryPorts(t *testing.T) {
221         for i := 0; i < 2; i++ {
222                 cl, err := NewClient(&TestingConfig)
223                 if err != nil {
224                         t.Fatal(err)
225                 }
226                 defer cl.Close()
227         }
228 }
229
230 func TestAddDropManyTorrents(t *testing.T) {
231         cl, _ := NewClient(&TestingConfig)
232         defer cl.Close()
233         for i := range iter.N(1000) {
234                 var spec TorrentSpec
235                 binary.PutVarint(spec.InfoHash[:], int64(i))
236                 tt, new, err := cl.AddTorrentSpec(&spec)
237                 if err != nil {
238                         t.Error(err)
239                 }
240                 if !new {
241                         t.FailNow()
242                 }
243                 defer tt.Drop()
244         }
245 }
246
247 func TestClientTransfer(t *testing.T) {
248         greetingTempDir, mi := testutil.GreetingTestTorrent()
249         defer os.RemoveAll(greetingTempDir)
250         cfg := TestingConfig
251         cfg.Seed = true
252         cfg.DataDir = greetingTempDir
253         seeder, err := NewClient(&cfg)
254         if err != nil {
255                 t.Fatal(err)
256         }
257         defer seeder.Close()
258         seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
259         leecherDataDir, err := ioutil.TempDir("", "")
260         if err != nil {
261                 t.Fatal(err)
262         }
263         defer os.RemoveAll(leecherDataDir)
264         // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
265         //      return blob.TorrentData(info, leecherDataDir), nil
266         // }
267         cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
268         leecher, _ := NewClient(&cfg)
269         defer leecher.Close()
270         leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
271                 ret = TorrentSpecFromMetaInfo(mi)
272                 ret.ChunkSize = 2
273                 return
274         }())
275         leecherGreeting.AddPeers([]Peer{
276                 Peer{
277                         IP:   util.AddrIP(seeder.ListenAddr()),
278                         Port: util.AddrPort(seeder.ListenAddr()),
279                 },
280         })
281         r := leecherGreeting.NewReader()
282         defer r.Close()
283         _greeting, err := ioutil.ReadAll(r)
284         if err != nil {
285                 t.Fatalf("%q %s", string(_greeting), err)
286         }
287         greeting := string(_greeting)
288         if greeting != testutil.GreetingFileContents {
289                 t.Fatal(":(")
290         }
291 }
292
293 func TestReadaheadPieces(t *testing.T) {
294         for _, case_ := range []struct {
295                 readaheadBytes, pieceLength int64
296                 readaheadPieces             int
297         }{
298                 {5 * 1024 * 1024, 256 * 1024, 19},
299                 {5 * 1024 * 1024, 5 * 1024 * 1024, 1},
300                 {5*1024*1024 - 1, 5 * 1024 * 1024, 1},
301                 {5 * 1024 * 1024, 5*1024*1024 - 1, 2},
302                 {0, 5 * 1024 * 1024, 0},
303                 {5 * 1024 * 1024, 1048576, 4},
304         } {
305                 pieces := readaheadPieces(case_.readaheadBytes, case_.pieceLength)
306                 assert.Equal(t, case_.readaheadPieces, pieces, "%v", case_)
307         }
308 }
309
310 func (suite) TestMergingTrackersByAddingSpecs(c *check.C) {
311         cl, _ := NewClient(&TestingConfig)
312         defer cl.Close()
313         spec := TorrentSpec{}
314         T, new, _ := cl.AddTorrentSpec(&spec)
315         if !new {
316                 c.FailNow()
317         }
318         spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
319         _, new, _ = cl.AddTorrentSpec(&spec)
320         if new {
321                 c.FailNow()
322         }
323         c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a")
324         c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b")
325 }
326
327 type badData struct {
328 }
329
330 func (me badData) WriteAt(b []byte, off int64) (int, error) {
331         return 0, nil
332 }
333
334 func (me badData) WriteSectionTo(w io.Writer, off, n int64) (int64, error) {
335         return 0, nil
336 }
337
338 func (me badData) PieceComplete(piece int) bool {
339         return true
340 }
341
342 func (me badData) PieceCompleted(piece int) error {
343         return nil
344 }
345
346 func (me badData) ReadAt(b []byte, off int64) (n int, err error) {
347         if off >= 5 {
348                 err = io.EOF
349                 return
350         }
351         n = copy(b, []byte("hello")[off:])
352         return
353 }
354
355 var _ StatefulData = badData{}
356
357 // We read from a piece which is marked completed, but is missing data.
358 func TestCompletedPieceWrongSize(t *testing.T) {
359         cfg := TestingConfig
360         cfg.TorrentDataOpener = func(*metainfo.Info) data.Data {
361                 return badData{}
362         }
363         cl, _ := NewClient(&cfg)
364         defer cl.Close()
365         tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
366                 Info: &metainfo.InfoEx{
367                         Info: metainfo.Info{
368                                 PieceLength: 15,
369                                 Pieces:      make([]byte, 20),
370                                 Files: []metainfo.FileInfo{
371                                         metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
372                                 },
373                         },
374                 },
375         })
376         if err != nil {
377                 t.Fatal(err)
378         }
379         if !new {
380                 t.Fatal("expected new")
381         }
382         r := tt.NewReader()
383         defer r.Close()
384         b := make([]byte, 20)
385         n, err := io.ReadFull(r, b)
386         if n != 5 || err != io.ErrUnexpectedEOF {
387                 t.Fatal(n, err)
388         }
389         defer tt.Drop()
390 }
391
392 func BenchmarkAddLargeTorrent(b *testing.B) {
393         cfg := TestingConfig
394         cfg.DisableTCP = true
395         cfg.DisableUTP = true
396         cfg.ListenAddr = "redonk"
397         cl, _ := NewClient(&cfg)
398         defer cl.Close()
399         for range iter.N(b.N) {
400                 t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
401                 if err != nil {
402                         b.Fatal(err)
403                 }
404                 t.Drop()
405         }
406 }