]> Sergey Matveev's repositories - btrtrc.git/blob - client_test.go
Improve uploading/seeding
[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         "gopkg.in/check.v1"
19
20         "github.com/anacrolix/torrent/bencode"
21         "github.com/anacrolix/torrent/data"
22         "github.com/anacrolix/torrent/data/blob"
23         "github.com/anacrolix/torrent/internal/testutil"
24         "github.com/anacrolix/torrent/metainfo"
25         "github.com/anacrolix/torrent/util"
26 )
27
28 func init() {
29         log.SetFlags(log.LstdFlags | log.Lshortfile)
30 }
31
32 var TestingConfig = Config{
33         ListenAddr:           "localhost:0",
34         NoDHT:                true,
35         DisableTrackers:      true,
36         NoDefaultBlocklist:   true,
37         DisableMetainfoCache: true,
38         DataDir:              filepath.Join(os.TempDir(), "anacrolix"),
39 }
40
41 func TestClientDefault(t *testing.T) {
42         cl, err := NewClient(&TestingConfig)
43         if err != nil {
44                 t.Fatal(err)
45         }
46         cl.Close()
47 }
48
49 func TestAddDropTorrent(t *testing.T) {
50         cl, err := NewClient(&TestingConfig)
51         if err != nil {
52                 t.Fatal(err)
53         }
54         defer cl.Close()
55         dir, mi := testutil.GreetingTestTorrent()
56         defer os.RemoveAll(dir)
57         tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
58         if err != nil {
59                 t.Fatal(err)
60         }
61         if !new {
62                 t.FailNow()
63         }
64         tt.Drop()
65 }
66
67 func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
68         t.SkipNow()
69 }
70
71 func TestAddTorrentNoUsableURLs(t *testing.T) {
72         t.SkipNow()
73 }
74
75 func TestAddPeersToUnknownTorrent(t *testing.T) {
76         t.SkipNow()
77 }
78
79 func TestPieceHashSize(t *testing.T) {
80         if pieceHash.Size() != 20 {
81                 t.FailNow()
82         }
83 }
84
85 func TestTorrentInitialState(t *testing.T) {
86         dir, mi := testutil.GreetingTestTorrent()
87         defer os.RemoveAll(dir)
88         tor, err := newTorrent(func() (ih InfoHash) {
89                 util.CopyExact(ih[:], mi.Info.Hash)
90                 return
91         }())
92         if err != nil {
93                 t.Fatal(err)
94         }
95         err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
96         if err != nil {
97                 t.Fatal(err)
98         }
99         if len(tor.Pieces) != 3 {
100                 t.Fatal("wrong number of pieces")
101         }
102         p := tor.Pieces[0]
103         tor.pendAllChunkSpecs(0)
104         if p.numPendingChunks() != 1 {
105                 t.Fatalf("should only be 1 chunk: %v", p.PendingChunkSpecs)
106         }
107         // TODO: Set chunkSize to 2, to test odd/even silliness.
108         if chunkIndexSpec(0, tor.pieceLength(0)).Length != 5 {
109                 t.Fatal("pending chunk spec is incorrect")
110         }
111 }
112
113 func TestUnmarshalPEXMsg(t *testing.T) {
114         var m peerExchangeMessage
115         if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
116                 t.Fatal(err)
117         }
118         if len(m.Added) != 2 {
119                 t.FailNow()
120         }
121         if m.Added[0].Port != 0x506 {
122                 t.FailNow()
123         }
124 }
125
126 func TestReducedDialTimeout(t *testing.T) {
127         for _, _case := range []struct {
128                 Max             time.Duration
129                 HalfOpenLimit   int
130                 PendingPeers    int
131                 ExpectedReduced time.Duration
132         }{
133                 {nominalDialTimeout, 40, 0, nominalDialTimeout},
134                 {nominalDialTimeout, 40, 1, nominalDialTimeout},
135                 {nominalDialTimeout, 40, 39, nominalDialTimeout},
136                 {nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
137                 {nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
138                 {nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
139         } {
140                 reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
141                 expected := _case.ExpectedReduced
142                 if expected < minDialTimeout {
143                         expected = minDialTimeout
144                 }
145                 if reduced != expected {
146                         t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
147                 }
148         }
149 }
150
151 func TestUTPRawConn(t *testing.T) {
152         l, err := utp.NewSocket("")
153         if err != nil {
154                 t.Fatal(err)
155         }
156         defer l.Close()
157         go func() {
158                 for {
159                         _, err := l.Accept()
160                         if err != nil {
161                                 break
162                         }
163                 }
164         }()
165         // Connect a UTP peer to see if the RawConn will still work.
166         utpPeer, err := func() *utp.Socket {
167                 s, _ := utp.NewSocket("")
168                 return s
169         }().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
170         if err != nil {
171                 t.Fatalf("error dialing utp listener: %s", err)
172         }
173         defer utpPeer.Close()
174         peer, err := net.ListenPacket("udp", ":0")
175         if err != nil {
176                 t.Fatal(err)
177         }
178         defer peer.Close()
179
180         msgsReceived := 0
181         // How many messages to send. I've set this to double the channel buffer
182         // size in the raw packetConn.
183         const N = 200
184         readerStopped := make(chan struct{})
185         // The reader goroutine.
186         go func() {
187                 defer close(readerStopped)
188                 b := make([]byte, 500)
189                 for i := 0; i < N; i++ {
190                         n, _, err := l.PacketConn().ReadFrom(b)
191                         if err != nil {
192                                 t.Fatalf("error reading from raw conn: %s", err)
193                         }
194                         msgsReceived++
195                         var d int
196                         fmt.Sscan(string(b[:n]), &d)
197                         if d != i {
198                                 log.Printf("got wrong number: expected %d, got %d", i, d)
199                         }
200                 }
201         }()
202         udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
203         if err != nil {
204                 t.Fatal(err)
205         }
206         for i := 0; i < N; i++ {
207                 _, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
208                 if err != nil {
209                         t.Fatal(err)
210                 }
211                 time.Sleep(time.Microsecond)
212         }
213         select {
214         case <-readerStopped:
215         case <-time.After(time.Second):
216                 t.Fatal("reader timed out")
217         }
218         if msgsReceived != N {
219                 t.Fatalf("messages received: %d", msgsReceived)
220         }
221 }
222
223 func TestTwoClientsArbitraryPorts(t *testing.T) {
224         for i := 0; i < 2; i++ {
225                 cl, err := NewClient(&TestingConfig)
226                 if err != nil {
227                         t.Fatal(err)
228                 }
229                 defer cl.Close()
230         }
231 }
232
233 func TestAddDropManyTorrents(t *testing.T) {
234         cl, _ := NewClient(&TestingConfig)
235         defer cl.Close()
236         for i := range iter.N(1000) {
237                 var spec TorrentSpec
238                 binary.PutVarint(spec.InfoHash[:], int64(i))
239                 tt, new, err := cl.AddTorrentSpec(&spec)
240                 if err != nil {
241                         t.Error(err)
242                 }
243                 if !new {
244                         t.FailNow()
245                 }
246                 defer tt.Drop()
247         }
248 }
249
250 func TestClientTransfer(t *testing.T) {
251         greetingTempDir, mi := testutil.GreetingTestTorrent()
252         defer os.RemoveAll(greetingTempDir)
253         cfg := TestingConfig
254         cfg.Seed = true
255         cfg.DataDir = greetingTempDir
256         seeder, err := NewClient(&cfg)
257         if err != nil {
258                 t.Fatal(err)
259         }
260         defer seeder.Close()
261         seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
262         leecherDataDir, err := ioutil.TempDir("", "")
263         if err != nil {
264                 t.Fatal(err)
265         }
266         defer os.RemoveAll(leecherDataDir)
267         // cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
268         //      return blob.TorrentData(info, leecherDataDir), nil
269         // }
270         cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
271         leecher, _ := NewClient(&cfg)
272         defer leecher.Close()
273         leecherGreeting, _, _ := leecher.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
274         leecherGreeting.AddPeers([]Peer{
275                 Peer{
276                         IP:   util.AddrIP(seeder.ListenAddr()),
277                         Port: util.AddrPort(seeder.ListenAddr()),
278                 },
279         })
280         r := leecherGreeting.NewReader()
281         defer r.Close()
282         _greeting, err := ioutil.ReadAll(r)
283         if err != nil {
284                 t.Fatalf("%q %s", string(_greeting), err)
285         }
286         greeting := string(_greeting)
287         if greeting != testutil.GreetingFileContents {
288                 t.Fatal(":(")
289         }
290 }
291
292 func TestReadaheadPieces(t *testing.T) {
293         for _, case_ := range []struct {
294                 readaheadBytes, pieceLength int64
295                 readaheadPieces             int
296         }{
297                 {5 * 1024 * 1024, 256 * 1024, 19},
298                 {5 * 1024 * 1024, 5 * 1024 * 1024, 0},
299                 {5*1024*1024 - 1, 5 * 1024 * 1024, 0},
300                 {5 * 1024 * 1024, 5*1024*1024 - 1, 1},
301                 {0, 5 * 1024 * 1024, -1},
302                 {5 * 1024 * 1024, 1048576, 4},
303         } {
304                 if readaheadPieces(case_.readaheadBytes, case_.pieceLength) != case_.readaheadPieces {
305                         t.Fatalf("case failed: %v", case_)
306                 }
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 }