]> Sergey Matveev's repositories - btrtrc.git/blob - connection_test.go
3f6dfd93b17b282c023adbdaff18750b977a8fef
[btrtrc.git] / connection_test.go
1 package torrent
2
3 import (
4         "io"
5         "sync"
6         "testing"
7         "time"
8
9         "github.com/anacrolix/missinggo/pubsub"
10         "github.com/bradfitz/iter"
11         "github.com/stretchr/testify/assert"
12         "github.com/stretchr/testify/require"
13
14         "github.com/anacrolix/torrent/metainfo"
15         pp "github.com/anacrolix/torrent/peer_protocol"
16         "github.com/anacrolix/torrent/storage"
17 )
18
19 // Ensure that no race exists between sending a bitfield, and a subsequent
20 // Have that would potentially alter it.
21 func TestSendBitfieldThenHave(t *testing.T) {
22         r, w := io.Pipe()
23         c := &connection{
24                 t: &Torrent{
25                         cl: &Client{},
26                 },
27                 r: r,
28                 w: w,
29         }
30         c.writerCond.L = &c.t.cl.mu
31         go c.writer(time.Minute)
32         c.mu().Lock()
33         c.Bitfield([]bool{false, true, false})
34         c.mu().Unlock()
35         c.mu().Lock()
36         c.Have(2)
37         c.mu().Unlock()
38         b := make([]byte, 15)
39         n, err := io.ReadFull(r, b)
40         c.mu().Lock()
41         // This will cause connection.writer to terminate.
42         c.closed.Set()
43         c.mu().Unlock()
44         require.NoError(t, err)
45         require.EqualValues(t, 15, n)
46         // Here we see that the bitfield doesn't have piece 2 set, as that should
47         // arrive in the following Have message.
48         require.EqualValues(t, "\x00\x00\x00\x02\x05@\x00\x00\x00\x05\x04\x00\x00\x00\x02", string(b))
49 }
50
51 type torrentStorage struct {
52         writeSem sync.Mutex
53 }
54
55 func (me *torrentStorage) Close() error { return nil }
56
57 func (me *torrentStorage) Piece(mp metainfo.Piece) storage.PieceImpl {
58         return me
59 }
60
61 func (me *torrentStorage) Completion() storage.Completion {
62         return storage.Completion{}
63 }
64
65 func (me *torrentStorage) MarkComplete() error {
66         return nil
67 }
68
69 func (me *torrentStorage) MarkNotComplete() error {
70         return nil
71 }
72
73 func (me *torrentStorage) ReadAt([]byte, int64) (int, error) {
74         panic("shouldn't be called")
75 }
76
77 func (me *torrentStorage) WriteAt(b []byte, _ int64) (int, error) {
78         if len(b) != defaultChunkSize {
79                 panic(len(b))
80         }
81         me.writeSem.Unlock()
82         return len(b), nil
83 }
84
85 func BenchmarkConnectionMainReadLoop(b *testing.B) {
86         cl := &Client{}
87         ts := &torrentStorage{}
88         t := &Torrent{
89                 cl: cl,
90                 info: &metainfo.Info{
91                         Pieces:      make([]byte, 20),
92                         Length:      1 << 20,
93                         PieceLength: 1 << 20,
94                 },
95                 storage:           &storage.Torrent{ts},
96                 pieceStateChanges: pubsub.NewPubSub(),
97         }
98         t.setChunkSize(defaultChunkSize)
99         t.makePieces()
100         t.pendingPieces.Add(0)
101         r, w := io.Pipe()
102         cn := &connection{
103                 t: t,
104                 r: r,
105         }
106         mrlErr := make(chan error)
107         cl.mu.Lock()
108         go func() {
109                 err := cn.mainReadLoop()
110                 if err != nil {
111                         mrlErr <- err
112                 }
113                 close(mrlErr)
114         }()
115         msg := pp.Message{
116                 Type:  pp.Piece,
117                 Piece: make([]byte, defaultChunkSize),
118         }
119         wb, err := msg.MarshalBinary()
120         require.NoError(b, err)
121         b.SetBytes(int64(len(msg.Piece)))
122         ts.writeSem.Lock()
123         for range iter.N(b.N) {
124                 cl.mu.Lock()
125                 t.pieces[0].dirtyChunks.Clear()
126                 cl.mu.Unlock()
127                 n, err := w.Write(wb)
128                 require.NoError(b, err)
129                 require.EqualValues(b, len(wb), n)
130                 ts.writeSem.Lock()
131         }
132         w.Close()
133         require.NoError(b, <-mrlErr)
134         require.EqualValues(b, b.N, cn.UsefulChunksReceived)
135 }
136
137 func TestConnectionReceiveBadChunkIndex(t *testing.T) {
138         cn := connection{
139                 t: &Torrent{},
140         }
141         require.False(t, cn.t.haveInfo())
142         assert.NotPanics(t, func() { cn.receiveChunk(&pp.Message{}) })
143         cn.t.info = &metainfo.Info{}
144         require.True(t, cn.t.haveInfo())
145         assert.NotPanics(t, func() { cn.receiveChunk(&pp.Message{}) })
146 }