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  }