1 package request_strategy
7 "github.com/bradfitz/iter"
8 qt "github.com/frankban/quicktest"
10 pp "github.com/anacrolix/torrent/peer_protocol"
13 func r(i pieceIndex, begin int) Request {
14 return Request{pp.Integer(i), ChunkSpec{pp.Integer(begin), 1}}
17 func chunkIterRange(end int) func(func(ChunkSpec)) {
18 return func(f func(ChunkSpec)) {
19 for offset := range iter.N(end) {
20 f(ChunkSpec{pp.Integer(offset), 1})
25 func chunkIter(offsets ...int) func(func(ChunkSpec)) {
26 return func(f func(ChunkSpec)) {
27 for _, offset := range offsets {
28 f(ChunkSpec{pp.Integer(offset), 1})
33 func requestSetFromSlice(rs ...Request) (ret map[Request]struct{}) {
34 ret = make(map[Request]struct{}, len(rs))
35 for _, r := range rs {
43 func (i intPeerId) Uintptr() uintptr {
47 func TestStealingFromSlowerPeer(t *testing.T) {
50 HasPiece: func(i pieceIndex) bool {
53 MaxRequests: math.MaxInt16,
56 // Slower than the stealers, but has all requests already.
58 stealee.DownloadRate = 1
59 stealee.HasExistingRequest = func(r Request) bool {
62 stealee.Id = intPeerId(1)
63 firstStealer := basePeer
64 firstStealer.Id = intPeerId(2)
65 secondStealer := basePeer
66 secondStealer.Id = intPeerId(3)
67 results := Run(Input{Torrents: []Torrent{{
71 IterPendingChunks: chunkIterRange(5),
80 c.Assert(results, qt.HasLen, 3)
81 check := func(p PeerId, l int) {
82 c.Check(results[p].Requests, qt.HasLen, l)
83 c.Check(results[p].Interested, qt.Equals, l > 0)
86 check(firstStealer.Id, 2)
87 check(secondStealer.Id, 2)
90 func checkNumRequestsAndInterest(c *qt.C, next PeerNextRequestState, num int, interest bool) {
91 c.Check(next.Requests, qt.HasLen, num)
92 c.Check(next.Interested, qt.Equals, interest)
95 func TestStealingFromSlowerPeersBasic(t *testing.T) {
98 HasPiece: func(i pieceIndex) bool {
101 MaxRequests: math.MaxInt16,
105 stealee.DownloadRate = 1
106 stealee.HasExistingRequest = func(r Request) bool {
109 stealee.Id = intPeerId(1)
110 firstStealer := basePeer
111 firstStealer.Id = intPeerId(2)
112 secondStealer := basePeer
113 secondStealer.Id = intPeerId(3)
114 results := Run(Input{Torrents: []Torrent{{
118 IterPendingChunks: chunkIter(0, 1),
127 checkNumRequestsAndInterest(c, results[firstStealer.Id], 1, true)
128 checkNumRequestsAndInterest(c, results[secondStealer.Id], 1, true)
129 checkNumRequestsAndInterest(c, results[stealee.Id], 0, false)
132 func TestPeerKeepsExistingIfReasonable(t *testing.T) {
135 HasPiece: func(i pieceIndex) bool {
138 MaxRequests: math.MaxInt16,
141 // Slower than the stealers, but has all requests already.
143 stealee.DownloadRate = 1
145 stealee.HasExistingRequest = func(r Request) bool {
148 stealee.Id = intPeerId(1)
149 firstStealer := basePeer
150 firstStealer.Id = intPeerId(2)
151 secondStealer := basePeer
152 secondStealer.Id = intPeerId(3)
153 results := Run(Input{Torrents: []Torrent{{
157 IterPendingChunks: chunkIter(0, 1, 3, 4),
166 c.Assert(results, qt.HasLen, 3)
167 check := func(p PeerId, l int) {
168 c.Check(results[p].Requests, qt.HasLen, l)
169 c.Check(results[p].Interested, qt.Equals, l > 0)
171 check(firstStealer.Id, 2)
172 check(secondStealer.Id, 1)
173 c.Check(results[stealee.Id], qt.ContentEquals, PeerNextRequestState{
175 Requests: requestSetFromSlice(keepReq),
179 func TestDontStealUnnecessarily(t *testing.T) {
182 HasPiece: func(i pieceIndex) bool {
185 MaxRequests: math.MaxInt16,
188 // Slower than the stealers, but has all requests already.
190 stealee.DownloadRate = 1
191 keepReqs := requestSetFromSlice(
192 r(3, 2), r(3, 4), r(3, 6), r(3, 8),
193 r(4, 0), r(4, 1), r(4, 7), r(4, 8))
194 stealee.HasExistingRequest = func(r Request) bool {
198 stealee.Id = intPeerId(1)
199 firstStealer := basePeer
200 firstStealer.Id = intPeerId(2)
201 secondStealer := basePeer
202 secondStealer.Id = intPeerId(3)
203 secondStealer.HasPiece = func(i pieceIndex) bool {
211 results := Run(Input{Torrents: []Torrent{{
216 IterPendingChunks: chunkIterRange(9),
221 IterPendingChunks: chunkIterRange(7),
226 IterPendingChunks: chunkIterRange(0),
231 IterPendingChunks: chunkIterRange(9),
236 IterPendingChunks: chunkIterRange(9),
245 c.Assert(results, qt.HasLen, 3)
246 check := func(p PeerId, l int) {
247 c.Check(results[p].Requests, qt.HasLen, l)
248 c.Check(results[p].Interested, qt.Equals, l > 0)
250 check(firstStealer.Id, 5)
251 check(secondStealer.Id, 7+9)
252 c.Check(results[stealee.Id], qt.ContentEquals, PeerNextRequestState{
254 Requests: requestSetFromSlice(r(4, 0), r(4, 1), r(4, 7), r(4, 8)),
258 // This tests a situation where multiple peers had the same existing request, due to "actual" and
259 // "next" request states being out of sync. This reasonable occurs when a peer hasn't fully updated
260 // its actual request state since the last request strategy run.
261 func TestDuplicatePreallocations(t *testing.T) {
262 peer := func(id int, downloadRate float64) Peer {
264 HasExistingRequest: func(r Request) bool {
268 HasPiece: func(i pieceIndex) bool {
272 DownloadRate: downloadRate,
275 results := Run(Input{
276 Torrents: []Torrent{{
280 IterPendingChunks: chunkIterRange(1),
284 IterPendingChunks: chunkIterRange(1),
287 // The second peer was be marked as the preallocation, clobbering the first. The
288 // first peer is preferred, and the piece isn't striped, so it gets preallocated a
289 // request, and then gets reallocated from the peer the same request.
296 c.Assert(2, qt.Equals, len(results[intPeerId(1)].Requests)+len(results[intPeerId(2)].Requests))