github.com/anacrolix/torrent@v1.61.0/torrent_test.go (about) 1 package torrent 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net" 8 "os" 9 "path/filepath" 10 "testing" 11 12 g "github.com/anacrolix/generics" 13 "github.com/anacrolix/missinggo/v2" 14 "github.com/anacrolix/missinggo/v2/bitmap" 15 "github.com/go-quicktest/qt" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 "github.com/anacrolix/torrent/bencode" 20 "github.com/anacrolix/torrent/internal/testutil" 21 "github.com/anacrolix/torrent/metainfo" 22 pp "github.com/anacrolix/torrent/peer_protocol" 23 "github.com/anacrolix/torrent/storage" 24 ) 25 26 func r(i, b, l pp.Integer) Request { 27 return Request{i, ChunkSpec{b, l}} 28 } 29 30 // Check the given request is correct for various torrent offsets. 31 func TestTorrentRequest(t *testing.T) { 32 const s = 472183431 // Length of torrent. 33 for _, _case := range []struct { 34 off int64 // An offset into the torrent. 35 req Request // The expected request. The zero value means !ok. 36 }{ 37 // Invalid offset. 38 {-1, Request{}}, 39 {0, r(0, 0, 16384)}, 40 // One before the end of a piece. 41 {1<<18 - 1, r(0, 1<<18-16384, 16384)}, 42 // Offset beyond torrent length. 43 {472 * 1 << 20, Request{}}, 44 // One before the end of the torrent. Complicates the chunk length. 45 {s - 1, r((s-1)/(1<<18), (s-1)%(1<<18)/(16384)*(16384), 12935)}, 46 {1, r(0, 0, 16384)}, 47 // One before end of chunk. 48 {16383, r(0, 0, 16384)}, 49 // Second chunk. 50 {16384, r(0, 16384, 16384)}, 51 } { 52 req, ok := torrentOffsetRequest(472183431, 1<<18, 16384, _case.off) 53 if (_case.req == Request{}) == ok { 54 t.Fatalf("expected %v, got %v", _case.req, req) 55 } 56 if req != _case.req { 57 t.Fatalf("expected %v, got %v", _case.req, req) 58 } 59 } 60 } 61 62 func TestAppendToCopySlice(t *testing.T) { 63 orig := []int{1, 2, 3} 64 dupe := append([]int{}, orig...) 65 dupe[0] = 4 66 if orig[0] != 1 { 67 t.FailNow() 68 } 69 } 70 71 func TestTorrentString(t *testing.T) { 72 tor := &Torrent{} 73 tor.infoHash.Ok = true 74 tor.infoHash.Value[0] = 1 75 s := tor.InfoHash().HexString() 76 if s != "0100000000000000000000000000000000000000" { 77 t.FailNow() 78 } 79 } 80 81 // This benchmark is from the observation that a lot of overlapping Readers on 82 // a large torrent with small pieces had a lot of overhead in recalculating 83 // piece priorities everytime a reader (possibly in another Torrent) changed. 84 func BenchmarkUpdatePiecePriorities(b *testing.B) { 85 const ( 86 numPieces = 13410 87 pieceLength = 256 << 10 88 ) 89 cl := newTestingClient(b) 90 t := cl.newTorrentForTesting() 91 require.NoError(b, t.setInfoUnlocked(&metainfo.Info{ 92 Pieces: make([]byte, metainfo.HashSize*numPieces), 93 PieceLength: pieceLength, 94 Length: pieceLength * numPieces, 95 })) 96 assert.EqualValues(b, 13410, t.numPieces()) 97 for i := 0; i < 7; i += 1 { 98 r := t.NewReader() 99 r.SetReadahead(32 << 20) 100 r.Seek(3500000, io.SeekStart) 101 } 102 assert.Len(b, t.readers, 7) 103 for i := 0; i < t.numPieces(); i += 3 { 104 t._completedPieces.Add(bitmap.BitIndex(i)) 105 } 106 t.DownloadPieces(0, t.numPieces()) 107 for b.Loop() { 108 cl.lock() 109 t.updateAllPiecePriorities("") 110 cl.unlock() 111 } 112 } 113 114 // Check that a torrent containing zero-length file(s) will start, and that 115 // they're created in the filesystem. The client storage is assumed to be 116 // file-based on the native filesystem based. 117 func testEmptyFilesAndZeroPieceLength(t *testing.T, cfg *ClientConfig) { 118 cl, err := NewClient(cfg) 119 require.NoError(t, err) 120 defer cl.Close() 121 ib, err := bencode.Marshal(metainfo.Info{ 122 Name: "empty", 123 Length: 0, 124 PieceLength: 0, 125 }) 126 require.NoError(t, err) 127 fp := filepath.Join(cfg.DataDir, "empty") 128 os.Remove(fp) 129 assert.False(t, missinggo.FilePathExists(fp)) 130 tt, err := cl.AddTorrent(&metainfo.MetaInfo{ 131 InfoBytes: ib, 132 }) 133 require.NoError(t, err) 134 defer tt.Drop() 135 tt.DownloadAll() 136 require.True(t, cl.WaitAll()) 137 assert.True(t, tt.Complete().Bool()) 138 assert.True(t, missinggo.FilePathExists(fp)) 139 } 140 141 func TestEmptyFilesAndZeroPieceLengthWithFileStorage(t *testing.T) { 142 cfg := TestingConfig(t) 143 ci := storage.NewFile(cfg.DataDir) 144 defer ci.Close() 145 cfg.DefaultStorage = ci 146 testEmptyFilesAndZeroPieceLength(t, cfg) 147 } 148 149 func TestPieceHashFailed(t *testing.T) { 150 mi := testutil.GreetingMetaInfo() 151 cl := newTestingClient(t) 152 tt := cl.newTorrent(mi.HashInfoBytes(), badStorage{}) 153 tt.setChunkSize(2) 154 tt.cl.lock() 155 require.NoError(t, tt.setInfoBytesLocked(mi.InfoBytes)) 156 tt.cl.unlock() 157 tt.cl.lock() 158 tt.dirtyChunks.AddRange( 159 uint64(tt.pieceRequestIndexBegin(1)), 160 uint64(tt.pieceRequestIndexBegin(1)+3)) 161 require.True(t, tt.pieceAllDirty(1)) 162 tt.pieceHashed(1, false, nil) 163 // Dirty chunks should be cleared so we can try again. 164 require.False(t, tt.pieceAllDirty(1)) 165 tt.cl.unlock() 166 } 167 168 // Check the behaviour of Torrent.Metainfo when metadata is not completed. 169 func TestTorrentMetainfoIncompleteMetadata(t *testing.T) { 170 cfg := TestingConfig(t) 171 cfg.Debug = true 172 // Disable this just because we manually initiate a connection without it. 173 cfg.MinPeerExtensions.SetBit(pp.ExtensionBitFast, false) 174 cl, err := NewClient(cfg) 175 require.NoError(t, err) 176 defer cl.Close() 177 178 mi := testutil.GreetingMetaInfo() 179 ih := mi.HashInfoBytes() 180 181 tt, _ := cl.AddTorrentInfoHash(ih) 182 assert.Nil(t, tt.Metainfo().InfoBytes) 183 assert.False(t, tt.haveAllMetadataPieces()) 184 185 nc, err := net.Dial("tcp", fmt.Sprintf(":%d", cl.LocalPort())) 186 require.NoError(t, err) 187 defer nc.Close() 188 189 var pex PeerExtensionBits 190 pex.SetBit(pp.ExtensionBitLtep, true) 191 hr, err := pp.Handshake(context.Background(), nc, &ih, [20]byte{}, pex) 192 require.NoError(t, err) 193 assert.True(t, hr.PeerExtensionBits.GetBit(pp.ExtensionBitLtep)) 194 assert.EqualValues(t, cl.PeerID(), hr.PeerID) 195 assert.EqualValues(t, ih, hr.Hash) 196 197 assert.EqualValues(t, 0, tt.metadataSize()) 198 199 func() { 200 cl.lock() 201 defer cl.unlock() 202 go func() { 203 _, err = nc.Write(pp.Message{ 204 Type: pp.Extended, 205 ExtendedID: pp.HandshakeExtendedID, 206 ExtendedPayload: func() []byte { 207 d := map[string]interface{}{ 208 "metadata_size": len(mi.InfoBytes), 209 } 210 b, err := bencode.Marshal(d) 211 if err != nil { 212 panic(err) 213 } 214 return b 215 }(), 216 }.MustMarshalBinary()) 217 require.NoError(t, err) 218 }() 219 tt.metadataChanged.Wait() 220 }() 221 assert.Equal(t, make([]byte, len(mi.InfoBytes)), tt.metadataBytes) 222 assert.False(t, tt.haveAllMetadataPieces()) 223 assert.Nil(t, tt.Metainfo().InfoBytes) 224 } 225 226 func TestRelativeAvailabilityHaveNone(t *testing.T) { 227 var err error 228 cl := newTestingClient(t) 229 mi, _ := testutil.Greeting.Generate(5) 230 tt := cl.newTorrentOpt(AddTorrentOpts{InfoHash: mi.HashInfoBytes()}) 231 tt.setChunkSize(2) 232 g.MakeMapIfNil(&tt.conns) 233 pc := PeerConn{} 234 pc.t = tt 235 pc.legacyPeerImpl = &pc 236 pc.initRequestState() 237 g.InitNew(&pc.callbacks) 238 tt.cl.lock() 239 tt.conns[&pc] = struct{}{} 240 err = pc.peerSentHave(0) 241 tt.cl.unlock() 242 qt.Assert(t, qt.IsNil(err)) 243 err = tt.SetInfoBytes(mi.InfoBytes) 244 qt.Assert(t, qt.IsNil(err)) 245 tt.cl.lock() 246 err = pc.peerSentHaveNone() 247 tt.cl.unlock() 248 qt.Assert(t, qt.IsNil(err)) 249 tt.Drop() 250 tt.assertAllPiecesRelativeAvailabilityZero() 251 }