X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=torrent_test.go;h=808947e974a23fe9df13e8fb5714eac781445305;hb=HEAD;hp=5811b359516deb58631750f2e7c7ff43f3805840;hpb=9d96cd659f4f9beb2cfe72d43e40972a6d69af80;p=btrtrc.git diff --git a/torrent_test.go b/torrent_test.go index 5811b359..808947e9 100644 --- a/torrent_test.go +++ b/torrent_test.go @@ -1,40 +1,47 @@ package torrent import ( + "fmt" + "io" + "net" "os" "path/filepath" + "sync" "testing" - "github.com/anacrolix/missinggo" - "github.com/bradfitz/iter" + g "github.com/anacrolix/generics" + "github.com/anacrolix/log" + "github.com/anacrolix/missinggo/v2" + "github.com/anacrolix/missinggo/v2/bitmap" + qt "github.com/frankban/quicktest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/internal/testutil" "github.com/anacrolix/torrent/metainfo" - "github.com/anacrolix/torrent/peer_protocol" + pp "github.com/anacrolix/torrent/peer_protocol" "github.com/anacrolix/torrent/storage" ) -func r(i, b, l peer_protocol.Integer) request { - return request{i, chunkSpec{b, l}} +func r(i, b, l pp.Integer) Request { + return Request{i, ChunkSpec{b, l}} } -// Check the given Request is correct for various torrent offsets. +// Check the given request is correct for various torrent offsets. func TestTorrentRequest(t *testing.T) { const s = 472183431 // Length of torrent. for _, _case := range []struct { off int64 // An offset into the torrent. - req request // The expected Request. The zero value means !ok. + req Request // The expected request. The zero value means !ok. }{ // Invalid offset. - {-1, request{}}, + {-1, Request{}}, {0, r(0, 0, 16384)}, // One before the end of a piece. {1<<18 - 1, r(0, 1<<18-16384, 16384)}, // Offset beyond torrent length. - {472 * 1 << 20, request{}}, + {472 * 1 << 20, Request{}}, // One before the end of the torrent. Complicates the chunk length. {s - 1, r((s-1)/(1<<18), (s-1)%(1<<18)/(16384)*(16384), 12935)}, {1, r(0, 0, 16384)}, @@ -44,7 +51,7 @@ func TestTorrentRequest(t *testing.T) { {16384, r(0, 16384, 16384)}, } { req, ok := torrentOffsetRequest(472183431, 1<<18, 16384, _case.off) - if (_case.req == request{}) == ok { + if (_case.req == Request{}) == ok { t.Fatalf("expected %v, got %v", _case.req, req) } if req != _case.req { @@ -74,36 +81,40 @@ func TestTorrentString(t *testing.T) { // a large torrent with small pieces had a lot of overhead in recalculating // piece priorities everytime a reader (possibly in another Torrent) changed. func BenchmarkUpdatePiecePriorities(b *testing.B) { - cl := &Client{} + const ( + numPieces = 13410 + pieceLength = 256 << 10 + ) + cl := &Client{config: TestingConfig(b)} + cl.initLogger() t := cl.newTorrent(metainfo.Hash{}, nil) - t.info = &metainfo.Info{ - Pieces: make([]byte, 20*13410), - PieceLength: 256 << 10, - } - t.makePieces() + require.NoError(b, t.setInfo(&metainfo.Info{ + Pieces: make([]byte, metainfo.HashSize*numPieces), + PieceLength: pieceLength, + Length: pieceLength * numPieces, + })) + t.onSetInfo() assert.EqualValues(b, 13410, t.numPieces()) - for range iter.N(7) { + for i := 0; i < 7; i += 1 { r := t.NewReader() r.SetReadahead(32 << 20) - r.Seek(3500000, 0) + r.Seek(3500000, io.SeekStart) } assert.Len(b, t.readers, 7) - t.pendPieceRange(0, t.numPieces()) for i := 0; i < t.numPieces(); i += 3 { - t.completedPieces.Set(i, true) + t._completedPieces.Add(bitmap.BitIndex(i)) } - for range iter.N(b.N) { - t.updateAllPiecePriorities() + t.DownloadPieces(0, t.numPieces()) + for i := 0; i < b.N; i += 1 { + t.updateAllPiecePriorities("") } } // Check that a torrent containing zero-length file(s) will start, and that // they're created in the filesystem. The client storage is assumed to be // file-based on the native filesystem based. -func testEmptyFilesAndZeroPieceLength(t *testing.T, cs storage.ClientImpl) { - cfg := TestingConfig - cfg.DefaultStorage = cs - cl, err := NewClient(&TestingConfig) +func testEmptyFilesAndZeroPieceLength(t *testing.T, cfg *ClientConfig) { + cl, err := NewClient(cfg) require.NoError(t, err) defer cl.Close() ib, err := bencode.Marshal(metainfo.Info{ @@ -112,7 +123,7 @@ func testEmptyFilesAndZeroPieceLength(t *testing.T, cs storage.ClientImpl) { PieceLength: 0, }) require.NoError(t, err) - fp := filepath.Join(TestingConfig.DataDir, "empty") + fp := filepath.Join(cfg.DataDir, "empty") os.Remove(fp) assert.False(t, missinggo.FilePathExists(fp)) tt, err := cl.AddTorrent(&metainfo.MetaInfo{ @@ -122,31 +133,121 @@ func testEmptyFilesAndZeroPieceLength(t *testing.T, cs storage.ClientImpl) { defer tt.Drop() tt.DownloadAll() require.True(t, cl.WaitAll()) + assert.True(t, tt.Complete.Bool()) assert.True(t, missinggo.FilePathExists(fp)) } func TestEmptyFilesAndZeroPieceLengthWithFileStorage(t *testing.T) { - testEmptyFilesAndZeroPieceLength(t, storage.NewFile(TestingConfig.DataDir)) -} - -func TestEmptyFilesAndZeroPieceLengthWithMMapStorage(t *testing.T) { - testEmptyFilesAndZeroPieceLength(t, storage.NewMMap(TestingConfig.DataDir)) + cfg := TestingConfig(t) + ci := storage.NewFile(cfg.DataDir) + defer ci.Close() + cfg.DefaultStorage = ci + testEmptyFilesAndZeroPieceLength(t, cfg) } func TestPieceHashFailed(t *testing.T) { mi := testutil.GreetingMetaInfo() - tt := Torrent{ - cl: new(Client), - infoHash: mi.HashInfoBytes(), - storageOpener: storage.NewClient(badStorage{}), - chunkSize: 2, - } - require.NoError(t, tt.setInfoBytes(mi.InfoBytes)) - tt.cl.mu.Lock() - tt.pieces[1].DirtyChunks.AddRange(0, 3) + cl := newTestingClient(t) + tt := cl.newTorrent(mi.HashInfoBytes(), badStorage{}) + tt.setChunkSize(2) + require.NoError(t, tt.setInfoBytesLocked(mi.InfoBytes)) + tt.cl.lock() + tt.dirtyChunks.AddRange( + uint64(tt.pieceRequestIndexOffset(1)), + uint64(tt.pieceRequestIndexOffset(1)+3)) require.True(t, tt.pieceAllDirty(1)) - tt.pieceHashed(1, false) + tt.pieceHashed(1, false, nil) // Dirty chunks should be cleared so we can try again. require.False(t, tt.pieceAllDirty(1)) - tt.cl.mu.Unlock() + tt.cl.unlock() +} + +// Check the behaviour of Torrent.Metainfo when metadata is not completed. +func TestTorrentMetainfoIncompleteMetadata(t *testing.T) { + cfg := TestingConfig(t) + cfg.Debug = true + // Disable this just because we manually initiate a connection without it. + cfg.MinPeerExtensions.SetBit(pp.ExtensionBitFast, false) + cl, err := NewClient(cfg) + require.NoError(t, err) + defer cl.Close() + + mi := testutil.GreetingMetaInfo() + ih := mi.HashInfoBytes() + + tt, _ := cl.AddTorrentInfoHash(ih) + assert.Nil(t, tt.Metainfo().InfoBytes) + assert.False(t, tt.haveAllMetadataPieces()) + + nc, err := net.Dial("tcp", fmt.Sprintf(":%d", cl.LocalPort())) + require.NoError(t, err) + defer nc.Close() + + var pex PeerExtensionBits + pex.SetBit(pp.ExtensionBitLtep, true) + hr, err := pp.Handshake(nc, &ih, [20]byte{}, pex) + require.NoError(t, err) + assert.True(t, hr.PeerExtensionBits.GetBit(pp.ExtensionBitLtep)) + assert.EqualValues(t, cl.PeerID(), hr.PeerID) + assert.EqualValues(t, ih, hr.Hash) + + assert.EqualValues(t, 0, tt.metadataSize()) + + func() { + cl.lock() + defer cl.unlock() + go func() { + _, err = nc.Write(pp.Message{ + Type: pp.Extended, + ExtendedID: pp.HandshakeExtendedID, + ExtendedPayload: func() []byte { + d := map[string]interface{}{ + "metadata_size": len(mi.InfoBytes), + } + b, err := bencode.Marshal(d) + if err != nil { + panic(err) + } + return b + }(), + }.MustMarshalBinary()) + require.NoError(t, err) + }() + tt.metadataChanged.Wait() + }() + assert.Equal(t, make([]byte, len(mi.InfoBytes)), tt.metadataBytes) + assert.False(t, tt.haveAllMetadataPieces()) + assert.Nil(t, tt.Metainfo().InfoBytes) +} + +func TestRelativeAvailabilityHaveNone(t *testing.T) { + c := qt.New(t) + var err error + cl := Client{ + config: TestingConfig(t), + } + tt := Torrent{ + cl: &cl, + logger: log.Default, + gotMetainfoC: make(chan struct{}), + } + tt.setChunkSize(2) + g.MakeMapIfNil(&tt.conns) + pc := PeerConn{} + pc.t = &tt + pc.peerImpl = &pc + pc.initRequestState() + g.InitNew(&pc.callbacks) + tt.conns[&pc] = struct{}{} + err = pc.peerSentHave(0) + c.Assert(err, qt.IsNil) + info := testutil.Greeting.Info(5) + err = tt.setInfo(&info) + c.Assert(err, qt.IsNil) + tt.onSetInfo() + err = pc.peerSentHaveNone() + c.Assert(err, qt.IsNil) + var wg sync.WaitGroup + tt.close(&wg) + tt.assertAllPiecesRelativeAvailabilityZero() }