1 package request_strategy
7 qt "github.com/frankban/quicktest"
9 pp "github.com/anacrolix/torrent/peer_protocol"
12 func r(i pieceIndex, begin int) Request {
13 return Request{pp.Integer(i), ChunkSpec{pp.Integer(begin), 1}}
16 func chunkIterRange(end int) func(func(ChunkSpec)) {
17 return func(f func(ChunkSpec)) {
18 for offset := 0; offset < end; offset += 1 {
19 f(ChunkSpec{pp.Integer(offset), 1})
24 func chunkIter(offsets ...int) func(func(ChunkSpec)) {
25 return func(f func(ChunkSpec)) {
26 for _, offset := range offsets {
27 f(ChunkSpec{pp.Integer(offset), 1})
32 func requestSetFromSlice(rs ...Request) (ret map[Request]struct{}) {
33 ret = make(map[Request]struct{}, len(rs))
34 for _, r := range rs {
42 func (i intPeerId) Uintptr() uintptr {
46 func TestStealingFromSlowerPeer(t *testing.T) {
49 HasPiece: func(i pieceIndex) bool {
52 MaxRequests: math.MaxInt16,
55 // Slower than the stealers, but has all requests already.
57 stealee.DownloadRate = 1
58 stealee.HasExistingRequest = func(r Request) bool {
61 stealee.Id = intPeerId(1)
62 firstStealer := basePeer
63 firstStealer.Id = intPeerId(2)
64 secondStealer := basePeer
65 secondStealer.Id = intPeerId(3)
66 results := Run(Input{Torrents: []Torrent{{
70 IterPendingChunks: chunkIterRange(5),
79 c.Assert(results, qt.HasLen, 3)
80 check := func(p PeerId, l int) {
81 c.Check(results[p].Requests, qt.HasLen, l)
82 c.Check(results[p].Interested, qt.Equals, l > 0)
85 check(firstStealer.Id, 2)
86 check(secondStealer.Id, 2)
89 func checkNumRequestsAndInterest(c *qt.C, next PeerNextRequestState, num int, interest bool) {
90 c.Check(next.Requests, qt.HasLen, num)
91 c.Check(next.Interested, qt.Equals, interest)
94 func TestStealingFromSlowerPeersBasic(t *testing.T) {
97 HasPiece: func(i pieceIndex) bool {
100 MaxRequests: math.MaxInt16,
104 stealee.DownloadRate = 1
105 stealee.HasExistingRequest = func(r Request) bool {
108 stealee.Id = intPeerId(1)
109 firstStealer := basePeer
110 firstStealer.Id = intPeerId(2)
111 secondStealer := basePeer
112 secondStealer.Id = intPeerId(3)
113 results := Run(Input{Torrents: []Torrent{{
117 IterPendingChunks: chunkIter(0, 1),
126 checkNumRequestsAndInterest(c, results[firstStealer.Id], 1, true)
127 checkNumRequestsAndInterest(c, results[secondStealer.Id], 1, true)
128 checkNumRequestsAndInterest(c, results[stealee.Id], 0, false)
131 func TestPeerKeepsExistingIfReasonable(t *testing.T) {
134 HasPiece: func(i pieceIndex) bool {
137 MaxRequests: math.MaxInt16,
140 // Slower than the stealers, but has all requests already.
142 stealee.DownloadRate = 1
144 stealee.HasExistingRequest = func(r Request) bool {
147 stealee.Id = intPeerId(1)
148 firstStealer := basePeer
149 firstStealer.Id = intPeerId(2)
150 secondStealer := basePeer
151 secondStealer.Id = intPeerId(3)
152 results := Run(Input{Torrents: []Torrent{{
156 IterPendingChunks: chunkIter(0, 1, 3, 4),
165 c.Assert(results, qt.HasLen, 3)
166 check := func(p PeerId, l int) {
167 c.Check(results[p].Requests, qt.HasLen, l)
168 c.Check(results[p].Interested, qt.Equals, l > 0)
170 check(firstStealer.Id, 2)
171 check(secondStealer.Id, 1)
172 c.Check(results[stealee.Id], qt.ContentEquals, PeerNextRequestState{
174 Requests: requestSetFromSlice(keepReq),
178 func TestDontStealUnnecessarily(t *testing.T) {
181 HasPiece: func(i pieceIndex) bool {
184 MaxRequests: math.MaxInt16,
187 // Slower than the stealers, but has all requests already.
189 stealee.DownloadRate = 1
190 keepReqs := requestSetFromSlice(
191 r(3, 2), r(3, 4), r(3, 6), r(3, 8),
192 r(4, 0), r(4, 1), r(4, 7), r(4, 8))
193 stealee.HasExistingRequest = func(r Request) bool {
197 stealee.Id = intPeerId(1)
198 firstStealer := basePeer
199 firstStealer.Id = intPeerId(2)
200 secondStealer := basePeer
201 secondStealer.Id = intPeerId(3)
202 secondStealer.HasPiece = func(i pieceIndex) bool {
210 results := Run(Input{Torrents: []Torrent{{
215 IterPendingChunks: chunkIterRange(9),
220 IterPendingChunks: chunkIterRange(7),
225 IterPendingChunks: chunkIterRange(0),
230 IterPendingChunks: chunkIterRange(9),
235 IterPendingChunks: chunkIterRange(9),
244 c.Assert(results, qt.HasLen, 3)
245 check := func(p PeerId, l int) {
246 c.Check(results[p].Requests, qt.HasLen, l)
247 c.Check(results[p].Interested, qt.Equals, l > 0)
249 check(firstStealer.Id, 5)
250 check(secondStealer.Id, 7+9)
251 c.Check(results[stealee.Id], qt.ContentEquals, PeerNextRequestState{
253 Requests: requestSetFromSlice(r(4, 0), r(4, 1), r(4, 7), r(4, 8)),
257 // This tests a situation where multiple peers had the same existing request, due to "actual" and
258 // "next" request states being out of sync. This reasonable occurs when a peer hasn't fully updated
259 // its actual request state since the last request strategy run.
260 func TestDuplicatePreallocations(t *testing.T) {
261 peer := func(id int, downloadRate float64) Peer {
263 HasExistingRequest: func(r Request) bool {
267 HasPiece: func(i pieceIndex) bool {
271 DownloadRate: downloadRate,
274 results := Run(Input{
275 Torrents: []Torrent{{
279 IterPendingChunks: chunkIterRange(1),
283 IterPendingChunks: chunkIterRange(1),
286 // The second peer was be marked as the preallocation, clobbering the first. The
287 // first peer is preferred, and the piece isn't striped, so it gets preallocated a
288 // request, and then gets reallocated from the peer the same request.
295 c.Assert(2, qt.Equals, len(results[intPeerId(1)].Requests)+len(results[intPeerId(2)].Requests))