2014-03-17 14:44:22 +00:00
|
|
|
package torrent
|
|
|
|
|
|
|
|
import (
|
2015-02-26 14:46:02 +00:00
|
|
|
"encoding/binary"
|
2016-02-17 07:26:10 +00:00
|
|
|
"errors"
|
2014-12-04 01:57:43 +00:00
|
|
|
"fmt"
|
2015-06-02 14:16:38 +00:00
|
|
|
"io"
|
2015-02-26 14:46:02 +00:00
|
|
|
"io/ioutil"
|
2014-12-04 01:57:43 +00:00
|
|
|
"log"
|
2016-02-17 07:26:10 +00:00
|
|
|
"math/rand"
|
2014-12-04 01:57:43 +00:00
|
|
|
"net"
|
2014-03-20 05:58:09 +00:00
|
|
|
"os"
|
2016-02-17 07:26:10 +00:00
|
|
|
"strings"
|
2015-08-12 06:51:12 +00:00
|
|
|
"sync"
|
2014-03-17 14:44:22 +00:00
|
|
|
"testing"
|
2014-11-18 00:04:09 +00:00
|
|
|
"time"
|
2014-08-21 08:24:19 +00:00
|
|
|
|
2015-05-20 12:20:11 +00:00
|
|
|
_ "github.com/anacrolix/envpprof"
|
2015-08-03 14:29:01 +00:00
|
|
|
"github.com/anacrolix/missinggo"
|
2016-03-29 00:14:34 +00:00
|
|
|
"github.com/anacrolix/missinggo/filecache"
|
2016-05-09 04:37:29 +00:00
|
|
|
"github.com/anacrolix/missinggo/pubsub"
|
2015-03-26 06:18:08 +00:00
|
|
|
"github.com/anacrolix/utp"
|
2015-02-26 14:46:02 +00:00
|
|
|
"github.com/bradfitz/iter"
|
2015-06-16 07:14:15 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2015-07-15 05:51:42 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2015-02-26 14:46:02 +00:00
|
|
|
|
2015-04-29 14:31:34 +00:00
|
|
|
"github.com/anacrolix/torrent/bencode"
|
2015-08-03 15:07:22 +00:00
|
|
|
"github.com/anacrolix/torrent/dht"
|
2015-03-20 05:37:44 +00:00
|
|
|
"github.com/anacrolix/torrent/internal/testutil"
|
2015-08-03 15:07:22 +00:00
|
|
|
"github.com/anacrolix/torrent/iplist"
|
2015-06-02 14:16:38 +00:00
|
|
|
"github.com/anacrolix/torrent/metainfo"
|
2016-03-28 09:38:30 +00:00
|
|
|
"github.com/anacrolix/torrent/storage"
|
2014-03-17 14:44:22 +00:00
|
|
|
)
|
|
|
|
|
2015-03-18 07:32:31 +00:00
|
|
|
func init() {
|
2015-10-03 14:22:46 +00:00
|
|
|
log.SetFlags(log.LstdFlags | log.Llongfile)
|
2015-03-18 07:32:31 +00:00
|
|
|
}
|
|
|
|
|
2015-02-26 14:46:02 +00:00
|
|
|
var TestingConfig = Config{
|
2016-05-05 13:05:28 +00:00
|
|
|
ListenAddr: "localhost:0",
|
|
|
|
NoDHT: true,
|
|
|
|
DisableTrackers: true,
|
|
|
|
DataDir: "/dev/null",
|
2016-01-16 13:12:53 +00:00
|
|
|
DHTConfig: dht.ServerConfig{
|
|
|
|
NoDefaultBootstrap: true,
|
|
|
|
},
|
2015-02-26 14:46:02 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func TestClientDefault(t *testing.T) {
|
2015-03-18 07:32:31 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2016-03-28 10:57:04 +00:00
|
|
|
require.NoError(t, err)
|
2015-03-08 06:28:14 +00:00
|
|
|
cl.Close()
|
2014-08-21 08:07:06 +00:00
|
|
|
}
|
|
|
|
|
2015-02-06 03:54:59 +00:00
|
|
|
func TestAddDropTorrent(t *testing.T) {
|
2015-03-10 17:22:56 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2016-03-28 10:57:04 +00:00
|
|
|
require.NoError(t, err)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-02-06 03:54:59 +00:00
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(dir)
|
2015-03-18 07:32:31 +00:00
|
|
|
tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
2016-03-28 10:57:04 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, new)
|
2015-02-06 03:54:59 +00:00
|
|
|
tt.Drop()
|
|
|
|
}
|
|
|
|
|
2014-03-17 14:44:22 +00:00
|
|
|
func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddTorrentNoUsableURLs(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddPeersToUnknownTorrent(t *testing.T) {
|
|
|
|
t.SkipNow()
|
|
|
|
}
|
2014-03-20 05:58:09 +00:00
|
|
|
|
|
|
|
func TestPieceHashSize(t *testing.T) {
|
2014-04-08 16:36:05 +00:00
|
|
|
if pieceHash.Size() != 20 {
|
2014-03-20 05:58:09 +00:00
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTorrentInitialState(t *testing.T) {
|
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(dir)
|
2016-05-09 04:37:29 +00:00
|
|
|
tor := &Torrent{
|
|
|
|
infoHash: mi.Info.Hash(),
|
|
|
|
pieceStateChanges: pubsub.NewPubSub(),
|
|
|
|
}
|
2015-07-15 05:31:18 +00:00
|
|
|
tor.chunkSize = 2
|
2016-05-12 02:44:51 +00:00
|
|
|
tor.storageOpener = storage.NewFile("/dev/null")
|
2016-03-28 09:38:30 +00:00
|
|
|
// Needed to lock for asynchronous piece verification.
|
|
|
|
tor.cl = new(Client)
|
2016-05-09 05:47:39 +00:00
|
|
|
err := tor.setInfoBytes(mi.Info.Bytes)
|
2016-03-28 09:38:30 +00:00
|
|
|
require.NoError(t, err)
|
2016-04-03 06:50:53 +00:00
|
|
|
require.Len(t, tor.pieces, 3)
|
2014-12-09 03:59:01 +00:00
|
|
|
tor.pendAllChunkSpecs(0)
|
2016-05-07 08:56:44 +00:00
|
|
|
tor.cl.mu.Lock()
|
2016-01-13 06:11:59 +00:00
|
|
|
assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
|
2016-05-07 08:56:44 +00:00
|
|
|
tor.cl.mu.Unlock()
|
2015-07-15 05:31:18 +00:00
|
|
|
assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
|
2014-03-20 05:58:09 +00:00
|
|
|
}
|
2014-06-29 09:07:43 +00:00
|
|
|
|
|
|
|
func TestUnmarshalPEXMsg(t *testing.T) {
|
|
|
|
var m peerExchangeMessage
|
|
|
|
if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(m.Added) != 2 {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
if m.Added[0].Port != 0x506 {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
}
|
2014-11-18 00:04:09 +00:00
|
|
|
|
|
|
|
func TestReducedDialTimeout(t *testing.T) {
|
|
|
|
for _, _case := range []struct {
|
|
|
|
Max time.Duration
|
|
|
|
HalfOpenLimit int
|
|
|
|
PendingPeers int
|
|
|
|
ExpectedReduced time.Duration
|
|
|
|
}{
|
2014-11-19 03:53:00 +00:00
|
|
|
{nominalDialTimeout, 40, 0, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 1, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 39, nominalDialTimeout},
|
|
|
|
{nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
|
|
|
|
{nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
|
|
|
|
{nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
|
2014-11-18 00:04:09 +00:00
|
|
|
} {
|
|
|
|
reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
|
2014-11-19 03:53:00 +00:00
|
|
|
expected := _case.ExpectedReduced
|
|
|
|
if expected < minDialTimeout {
|
|
|
|
expected = minDialTimeout
|
|
|
|
}
|
|
|
|
if reduced != expected {
|
2014-11-18 00:04:09 +00:00
|
|
|
t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-04 01:57:43 +00:00
|
|
|
|
|
|
|
func TestUTPRawConn(t *testing.T) {
|
2015-08-04 16:51:40 +00:00
|
|
|
l, err := utp.NewSocket("udp", "")
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer l.Close()
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
_, err := l.Accept()
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
// Connect a UTP peer to see if the RawConn will still work.
|
2015-08-04 16:51:40 +00:00
|
|
|
s, _ := utp.NewSocket("udp", "")
|
2015-08-03 14:48:17 +00:00
|
|
|
defer s.Close()
|
|
|
|
utpPeer, err := s.Dial(fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error dialing utp listener: %s", err)
|
|
|
|
}
|
|
|
|
defer utpPeer.Close()
|
|
|
|
peer, err := net.ListenPacket("udp", ":0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer peer.Close()
|
|
|
|
|
|
|
|
msgsReceived := 0
|
2015-05-24 11:37:14 +00:00
|
|
|
// How many messages to send. I've set this to double the channel buffer
|
|
|
|
// size in the raw packetConn.
|
|
|
|
const N = 200
|
2014-12-04 01:57:43 +00:00
|
|
|
readerStopped := make(chan struct{})
|
|
|
|
// The reader goroutine.
|
|
|
|
go func() {
|
|
|
|
defer close(readerStopped)
|
|
|
|
b := make([]byte, 500)
|
|
|
|
for i := 0; i < N; i++ {
|
2015-10-03 14:02:14 +00:00
|
|
|
n, _, err := l.ReadFrom(b)
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("error reading from raw conn: %s", err)
|
|
|
|
}
|
|
|
|
msgsReceived++
|
|
|
|
var d int
|
|
|
|
fmt.Sscan(string(b[:n]), &d)
|
|
|
|
if d != i {
|
|
|
|
log.Printf("got wrong number: expected %d, got %d", i, d)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2015-08-03 14:29:01 +00:00
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", missinggo.AddrPort(l.Addr())))
|
2014-12-26 06:19:01 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-12-04 01:57:43 +00:00
|
|
|
for i := 0; i < N; i++ {
|
2014-12-26 06:19:01 +00:00
|
|
|
_, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
|
2014-12-04 01:57:43 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
time.Sleep(time.Microsecond)
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-readerStopped:
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
t.Fatal("reader timed out")
|
|
|
|
}
|
|
|
|
if msgsReceived != N {
|
|
|
|
t.Fatalf("messages received: %d", msgsReceived)
|
|
|
|
}
|
|
|
|
}
|
2015-01-11 10:42:57 +00:00
|
|
|
|
|
|
|
func TestTwoClientsArbitraryPorts(t *testing.T) {
|
|
|
|
for i := 0; i < 2; i++ {
|
2015-03-18 07:32:31 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
2015-01-11 10:42:57 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-01-11 10:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
|
|
|
|
func TestAddDropManyTorrents(t *testing.T) {
|
2016-03-24 12:52:38 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
|
|
|
require.NoError(t, err)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer cl.Close()
|
2015-02-26 14:46:02 +00:00
|
|
|
for i := range iter.N(1000) {
|
2015-03-18 07:32:31 +00:00
|
|
|
var spec TorrentSpec
|
|
|
|
binary.PutVarint(spec.InfoHash[:], int64(i))
|
|
|
|
tt, new, err := cl.AddTorrentSpec(&spec)
|
2016-03-24 12:52:38 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.True(t, new)
|
2015-03-18 07:32:31 +00:00
|
|
|
defer tt.Drop()
|
2015-02-26 14:46:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-21 13:30:42 +00:00
|
|
|
func TestClientTransferDefault(t *testing.T) {
|
2016-03-28 09:38:30 +00:00
|
|
|
testClientTransfer(t, testClientTransferParams{
|
2016-05-16 10:11:00 +00:00
|
|
|
ExportClientStatus: true,
|
|
|
|
LeecherFileCachePieceStorageFactory: fileCachePieceResourceStorage,
|
2016-03-28 09:38:30 +00:00
|
|
|
})
|
2016-02-21 13:30:42 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 11:50:43 +00:00
|
|
|
func fileCachePieceResourceStorage(fc *filecache.Cache) storage.Client {
|
2016-05-16 12:02:03 +00:00
|
|
|
return storage.NewResourcePieces(fc.AsResourceProvider())
|
2016-05-16 10:11:00 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 11:50:43 +00:00
|
|
|
func fileCachePieceFileStorage(fc *filecache.Cache) storage.Client {
|
2016-05-16 12:02:03 +00:00
|
|
|
return storage.NewFileStorePieces(fc.AsFileStore())
|
2016-05-16 10:11:00 +00:00
|
|
|
}
|
|
|
|
|
2016-02-21 15:41:07 +00:00
|
|
|
func TestClientTransferSmallCache(t *testing.T) {
|
|
|
|
testClientTransfer(t, testClientTransferParams{
|
|
|
|
SetLeecherStorageCapacity: true,
|
|
|
|
// Going below the piece length means it can't complete a piece so
|
|
|
|
// that it can be hashed.
|
|
|
|
LeecherStorageCapacity: 5,
|
|
|
|
SetReadahead: true,
|
|
|
|
// Can't readahead too far or the cache will thrash and drop data we
|
|
|
|
// thought we had.
|
2016-05-16 10:11:00 +00:00
|
|
|
Readahead: 0,
|
|
|
|
ExportClientStatus: true,
|
|
|
|
LeecherFileCachePieceStorageFactory: fileCachePieceResourceStorage,
|
2016-02-21 15:41:07 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-02-21 13:30:42 +00:00
|
|
|
func TestClientTransferVarious(t *testing.T) {
|
2016-05-16 11:50:43 +00:00
|
|
|
for _, lsf := range []func(*filecache.Cache) storage.Client{
|
2016-05-16 10:11:00 +00:00
|
|
|
fileCachePieceFileStorage,
|
|
|
|
fileCachePieceResourceStorage,
|
2016-03-28 10:57:04 +00:00
|
|
|
} {
|
2016-05-16 11:50:43 +00:00
|
|
|
for _, ss := range []func(string) storage.Client{
|
2016-05-16 10:11:00 +00:00
|
|
|
storage.NewFile,
|
|
|
|
storage.NewMMap,
|
|
|
|
} {
|
|
|
|
for _, responsive := range []bool{false, true} {
|
2016-03-28 10:57:04 +00:00
|
|
|
testClientTransfer(t, testClientTransferParams{
|
2016-05-16 10:11:00 +00:00
|
|
|
Responsive: responsive,
|
|
|
|
SeederStorage: ss,
|
|
|
|
LeecherFileCachePieceStorageFactory: lsf,
|
2016-03-28 10:57:04 +00:00
|
|
|
})
|
2016-05-16 10:11:00 +00:00
|
|
|
for _, readahead := range []int64{-1, 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20} {
|
|
|
|
testClientTransfer(t, testClientTransferParams{
|
|
|
|
SeederStorage: ss,
|
|
|
|
Responsive: responsive,
|
|
|
|
SetReadahead: true,
|
|
|
|
Readahead: readahead,
|
|
|
|
LeecherFileCachePieceStorageFactory: lsf,
|
|
|
|
})
|
|
|
|
}
|
2016-03-28 10:57:04 +00:00
|
|
|
}
|
2016-02-21 13:30:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type testClientTransferParams struct {
|
2016-05-16 10:11:00 +00:00
|
|
|
Responsive bool
|
|
|
|
Readahead int64
|
|
|
|
SetReadahead bool
|
|
|
|
ExportClientStatus bool
|
|
|
|
SetLeecherStorageCapacity bool
|
|
|
|
LeecherStorageCapacity int64
|
2016-05-16 11:50:43 +00:00
|
|
|
LeecherFileCachePieceStorageFactory func(*filecache.Cache) storage.Client
|
|
|
|
SeederStorage func(string) storage.Client
|
2016-02-21 13:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func testClientTransfer(t *testing.T, ps testClientTransferParams) {
|
2015-02-26 14:46:02 +00:00
|
|
|
greetingTempDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingTempDir)
|
|
|
|
cfg := TestingConfig
|
2015-06-16 06:57:47 +00:00
|
|
|
cfg.Seed = true
|
2016-03-28 10:57:04 +00:00
|
|
|
if ps.SeederStorage != nil {
|
|
|
|
cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
|
|
|
|
} else {
|
|
|
|
cfg.DataDir = greetingTempDir
|
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
seeder, err := NewClient(&cfg)
|
2016-02-21 11:08:01 +00:00
|
|
|
require.NoError(t, err)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer seeder.Close()
|
2016-02-21 13:30:42 +00:00
|
|
|
if ps.ExportClientStatus {
|
2016-02-21 15:36:41 +00:00
|
|
|
testutil.ExportStatusWriter(seeder, "s")
|
2016-02-21 13:30:42 +00:00
|
|
|
}
|
2016-03-28 09:38:30 +00:00
|
|
|
_, new, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, new)
|
2015-02-26 14:46:02 +00:00
|
|
|
leecherDataDir, err := ioutil.TempDir("", "")
|
2016-02-21 11:08:01 +00:00
|
|
|
require.NoError(t, err)
|
2015-02-26 14:46:02 +00:00
|
|
|
defer os.RemoveAll(leecherDataDir)
|
2016-03-29 00:14:34 +00:00
|
|
|
fc, err := filecache.NewCache(leecherDataDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
if ps.SetLeecherStorageCapacity {
|
|
|
|
fc.SetCapacity(ps.LeecherStorageCapacity)
|
|
|
|
}
|
2016-05-16 10:11:00 +00:00
|
|
|
cfg.DefaultStorage = ps.LeecherFileCachePieceStorageFactory(fc)
|
2016-02-26 11:10:09 +00:00
|
|
|
leecher, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
2015-03-08 06:28:14 +00:00
|
|
|
defer leecher.Close()
|
2016-02-21 13:30:42 +00:00
|
|
|
if ps.ExportClientStatus {
|
2016-02-21 15:36:41 +00:00
|
|
|
testutil.ExportStatusWriter(leecher, "l")
|
2016-02-21 13:30:42 +00:00
|
|
|
}
|
2016-02-21 11:08:01 +00:00
|
|
|
leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
2015-07-15 05:31:18 +00:00
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 2
|
2016-03-28 09:38:30 +00:00
|
|
|
ret.Storage = storage.NewFile(leecherDataDir)
|
2015-07-15 05:31:18 +00:00
|
|
|
return
|
|
|
|
}())
|
2016-02-21 11:08:01 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, new)
|
2015-02-26 14:46:02 +00:00
|
|
|
leecherGreeting.AddPeers([]Peer{
|
|
|
|
Peer{
|
2015-08-03 14:29:01 +00:00
|
|
|
IP: missinggo.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(seeder.ListenAddr()),
|
2015-02-26 14:46:02 +00:00
|
|
|
},
|
|
|
|
})
|
2015-04-14 13:59:41 +00:00
|
|
|
r := leecherGreeting.NewReader()
|
|
|
|
defer r.Close()
|
2016-02-21 13:30:42 +00:00
|
|
|
if ps.Responsive {
|
|
|
|
r.SetResponsive()
|
|
|
|
}
|
|
|
|
if ps.SetReadahead {
|
|
|
|
r.SetReadahead(ps.Readahead)
|
|
|
|
}
|
2016-02-21 15:41:07 +00:00
|
|
|
for range iter.N(2) {
|
|
|
|
pos, err := r.Seek(0, os.SEEK_SET)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, 0, pos)
|
|
|
|
_greeting, err := ioutil.ReadAll(r)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, testutil.GreetingFileContents, _greeting)
|
|
|
|
}
|
2015-02-26 14:46:02 +00:00
|
|
|
}
|
2015-03-04 02:07:11 +00:00
|
|
|
|
2015-08-12 06:51:12 +00:00
|
|
|
// Check that after completing leeching, a leecher transitions to a seeding
|
|
|
|
// correctly. Connected in a chain like so: Seeder <-> Leecher <-> LeecherLeecher.
|
|
|
|
func TestSeedAfterDownloading(t *testing.T) {
|
|
|
|
greetingTempDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingTempDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.Seed = true
|
|
|
|
cfg.DataDir = greetingTempDir
|
|
|
|
seeder, err := NewClient(&cfg)
|
2016-03-30 08:16:40 +00:00
|
|
|
require.NoError(t, err)
|
2015-08-12 06:51:12 +00:00
|
|
|
defer seeder.Close()
|
2016-02-21 15:36:41 +00:00
|
|
|
testutil.ExportStatusWriter(seeder, "s")
|
2015-08-12 06:51:12 +00:00
|
|
|
seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
cfg.DataDir, err = ioutil.TempDir("", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(cfg.DataDir)
|
2016-03-30 08:16:40 +00:00
|
|
|
leecher, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
2015-08-12 06:51:12 +00:00
|
|
|
defer leecher.Close()
|
2016-02-21 15:36:41 +00:00
|
|
|
testutil.ExportStatusWriter(leecher, "l")
|
2015-08-12 06:51:12 +00:00
|
|
|
cfg.Seed = false
|
2016-03-28 09:38:30 +00:00
|
|
|
// cfg.TorrentDataOpener = nil
|
2015-08-12 06:51:12 +00:00
|
|
|
cfg.DataDir, err = ioutil.TempDir("", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(cfg.DataDir)
|
|
|
|
leecherLeecher, _ := NewClient(&cfg)
|
|
|
|
defer leecherLeecher.Close()
|
2016-02-21 15:36:41 +00:00
|
|
|
testutil.ExportStatusWriter(leecherLeecher, "ll")
|
2015-08-12 06:51:12 +00:00
|
|
|
leecherGreeting, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 2
|
|
|
|
return
|
|
|
|
}())
|
|
|
|
llg, _, _ := leecherLeecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 3
|
|
|
|
return
|
|
|
|
}())
|
|
|
|
// Simultaneously DownloadAll in Leecher, and read the contents
|
|
|
|
// consecutively in LeecherLeecher. This non-deterministically triggered a
|
|
|
|
// case where the leecher wouldn't unchoke the LeecherLeecher.
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
r := llg.NewReader()
|
|
|
|
defer r.Close()
|
|
|
|
b, err := ioutil.ReadAll(r)
|
|
|
|
require.NoError(t, err)
|
2016-01-18 14:41:33 +00:00
|
|
|
assert.EqualValues(t, testutil.GreetingFileContents, b)
|
2015-08-12 06:51:12 +00:00
|
|
|
}()
|
|
|
|
leecherGreeting.AddPeers([]Peer{
|
|
|
|
Peer{
|
|
|
|
IP: missinggo.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(seeder.ListenAddr()),
|
|
|
|
},
|
|
|
|
Peer{
|
|
|
|
IP: missinggo.AddrIP(leecherLeecher.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(leecherLeecher.ListenAddr()),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
leecherGreeting.DownloadAll()
|
|
|
|
leecher.WaitAll()
|
|
|
|
}()
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2015-08-03 06:23:05 +00:00
|
|
|
func TestMergingTrackersByAddingSpecs(t *testing.T) {
|
2016-02-22 03:30:02 +00:00
|
|
|
cl, err := NewClient(&TestingConfig)
|
|
|
|
require.NoError(t, err)
|
2015-03-27 15:50:55 +00:00
|
|
|
defer cl.Close()
|
|
|
|
spec := TorrentSpec{}
|
|
|
|
T, new, _ := cl.AddTorrentSpec(&spec)
|
|
|
|
if !new {
|
2015-08-03 06:23:05 +00:00
|
|
|
t.FailNow()
|
2015-03-27 15:50:55 +00:00
|
|
|
}
|
|
|
|
spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
|
|
|
|
_, new, _ = cl.AddTorrentSpec(&spec)
|
|
|
|
if new {
|
2015-08-03 06:23:05 +00:00
|
|
|
t.FailNow()
|
2015-03-27 15:50:55 +00:00
|
|
|
}
|
2016-04-03 08:40:43 +00:00
|
|
|
assert.EqualValues(t, T.trackers[0][0], "http://a")
|
|
|
|
assert.EqualValues(t, T.trackers[1][0], "udp://b")
|
2015-03-27 15:50:55 +00:00
|
|
|
}
|
2015-06-02 14:16:38 +00:00
|
|
|
|
2016-03-28 09:38:30 +00:00
|
|
|
type badStorage struct{}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (bs badStorage) OpenTorrent(*metainfo.InfoEx) (storage.Torrent, error) {
|
|
|
|
return bs, nil
|
2016-03-28 11:40:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (bs badStorage) Close() error {
|
2016-03-28 11:40:29 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (bs badStorage) Piece(p metainfo.Piece) storage.Piece {
|
2016-03-28 09:38:30 +00:00
|
|
|
return badStoragePiece{p}
|
|
|
|
}
|
2015-10-01 14:09:04 +00:00
|
|
|
|
2016-03-28 09:38:30 +00:00
|
|
|
type badStoragePiece struct {
|
|
|
|
p metainfo.Piece
|
|
|
|
}
|
2015-06-02 14:16:38 +00:00
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (p badStoragePiece) WriteAt(b []byte, off int64) (int, error) {
|
2015-06-02 14:16:38 +00:00
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (p badStoragePiece) GetIsComplete() bool {
|
2015-06-02 14:16:38 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (p badStoragePiece) MarkComplete() error {
|
2016-02-17 07:26:10 +00:00
|
|
|
return errors.New("psyyyyyyyche")
|
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (p badStoragePiece) randomlyTruncatedDataString() string {
|
2016-02-17 07:26:10 +00:00
|
|
|
return "hello, world\n"[:rand.Intn(14)]
|
2015-06-02 14:16:38 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 04:11:11 +00:00
|
|
|
func (p badStoragePiece) ReadAt(b []byte, off int64) (n int, err error) {
|
|
|
|
r := strings.NewReader(p.randomlyTruncatedDataString())
|
|
|
|
return r.ReadAt(b, off+p.p.Offset())
|
2015-06-02 14:16:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We read from a piece which is marked completed, but is missing data.
|
|
|
|
func TestCompletedPieceWrongSize(t *testing.T) {
|
|
|
|
cfg := TestingConfig
|
2016-03-28 09:38:30 +00:00
|
|
|
cfg.DefaultStorage = badStorage{}
|
2016-05-11 11:11:52 +00:00
|
|
|
cl, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
2015-06-02 14:16:38 +00:00
|
|
|
defer cl.Close()
|
2016-05-09 05:47:39 +00:00
|
|
|
ie := metainfo.InfoEx{
|
|
|
|
Info: metainfo.Info{
|
|
|
|
PieceLength: 15,
|
|
|
|
Pieces: make([]byte, 20),
|
|
|
|
Files: []metainfo.FileInfo{
|
|
|
|
metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
|
2015-06-02 14:16:38 +00:00
|
|
|
},
|
|
|
|
},
|
2016-05-09 05:47:39 +00:00
|
|
|
}
|
|
|
|
ie.UpdateBytes()
|
|
|
|
tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
|
|
|
|
Info: &ie,
|
|
|
|
InfoHash: ie.Hash(),
|
2015-06-02 14:16:38 +00:00
|
|
|
})
|
2016-02-17 07:26:10 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer tt.Drop()
|
|
|
|
assert.True(t, new)
|
2015-06-02 14:16:38 +00:00
|
|
|
r := tt.NewReader()
|
|
|
|
defer r.Close()
|
2016-02-17 07:26:10 +00:00
|
|
|
b, err := ioutil.ReadAll(r)
|
|
|
|
assert.Len(t, b, 13)
|
|
|
|
assert.NoError(t, err)
|
2015-06-02 14:16:38 +00:00
|
|
|
}
|
2015-06-22 09:43:22 +00:00
|
|
|
|
|
|
|
func BenchmarkAddLargeTorrent(b *testing.B) {
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.DisableTCP = true
|
|
|
|
cfg.DisableUTP = true
|
|
|
|
cfg.ListenAddr = "redonk"
|
|
|
|
cl, _ := NewClient(&cfg)
|
|
|
|
defer cl.Close()
|
|
|
|
for range iter.N(b.N) {
|
|
|
|
t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
t.Drop()
|
|
|
|
}
|
|
|
|
}
|
2015-07-15 05:51:42 +00:00
|
|
|
|
|
|
|
func TestResponsive(t *testing.T) {
|
|
|
|
seederDataDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(seederDataDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.Seed = true
|
|
|
|
cfg.DataDir = seederDataDir
|
|
|
|
seeder, err := NewClient(&cfg)
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer seeder.Close()
|
|
|
|
seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
leecherDataDir, err := ioutil.TempDir("", "")
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer os.RemoveAll(leecherDataDir)
|
|
|
|
cfg = TestingConfig
|
|
|
|
cfg.DataDir = leecherDataDir
|
|
|
|
leecher, err := NewClient(&cfg)
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer leecher.Close()
|
|
|
|
leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 2
|
|
|
|
return
|
|
|
|
}())
|
|
|
|
leecherTorrent.AddPeers([]Peer{
|
|
|
|
Peer{
|
2015-08-03 14:29:01 +00:00
|
|
|
IP: missinggo.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(seeder.ListenAddr()),
|
2015-07-15 05:51:42 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
reader := leecherTorrent.NewReader()
|
2016-02-04 14:17:26 +00:00
|
|
|
defer reader.Close()
|
2015-07-15 05:51:42 +00:00
|
|
|
reader.SetReadahead(0)
|
|
|
|
reader.SetResponsive()
|
|
|
|
b := make([]byte, 2)
|
2016-01-18 07:35:14 +00:00
|
|
|
_, err = reader.Seek(3, os.SEEK_SET)
|
|
|
|
require.NoError(t, err)
|
|
|
|
_, err = io.ReadFull(reader, b)
|
2015-07-15 05:51:42 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.EqualValues(t, "lo", string(b))
|
2016-01-18 07:35:14 +00:00
|
|
|
_, err = reader.Seek(11, os.SEEK_SET)
|
|
|
|
require.NoError(t, err)
|
|
|
|
n, err := io.ReadFull(reader, b)
|
2015-07-15 05:51:42 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.EqualValues(t, 2, n)
|
|
|
|
assert.EqualValues(t, "d\n", string(b))
|
|
|
|
}
|
2015-08-03 15:07:22 +00:00
|
|
|
|
2015-11-05 13:40:47 +00:00
|
|
|
func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
|
|
|
|
seederDataDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(seederDataDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.Seed = true
|
|
|
|
cfg.DataDir = seederDataDir
|
|
|
|
seeder, err := NewClient(&cfg)
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer seeder.Close()
|
|
|
|
seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
leecherDataDir, err := ioutil.TempDir("", "")
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer os.RemoveAll(leecherDataDir)
|
|
|
|
cfg = TestingConfig
|
|
|
|
cfg.DataDir = leecherDataDir
|
|
|
|
leecher, err := NewClient(&cfg)
|
|
|
|
require.Nil(t, err)
|
|
|
|
defer leecher.Close()
|
|
|
|
leecherTorrent, _, _ := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 2
|
|
|
|
return
|
|
|
|
}())
|
|
|
|
leecherTorrent.AddPeers([]Peer{
|
|
|
|
Peer{
|
|
|
|
IP: missinggo.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(seeder.ListenAddr()),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
reader := leecherTorrent.NewReader()
|
2016-02-04 14:17:26 +00:00
|
|
|
defer reader.Close()
|
2015-11-05 13:40:47 +00:00
|
|
|
reader.SetReadahead(0)
|
|
|
|
reader.SetResponsive()
|
|
|
|
b := make([]byte, 2)
|
2016-01-18 07:35:14 +00:00
|
|
|
_, err = reader.Seek(3, os.SEEK_SET)
|
|
|
|
require.NoError(t, err)
|
|
|
|
_, err = io.ReadFull(reader, b)
|
2015-11-05 13:40:47 +00:00
|
|
|
assert.Nil(t, err)
|
|
|
|
assert.EqualValues(t, "lo", string(b))
|
|
|
|
go leecherTorrent.Drop()
|
2016-01-18 07:35:14 +00:00
|
|
|
_, err = reader.Seek(11, os.SEEK_SET)
|
|
|
|
require.NoError(t, err)
|
|
|
|
n, err := reader.Read(b)
|
2015-11-05 13:40:47 +00:00
|
|
|
assert.EqualError(t, err, "torrent closed")
|
|
|
|
assert.EqualValues(t, 0, n)
|
|
|
|
}
|
|
|
|
|
2015-08-03 15:07:22 +00:00
|
|
|
func TestDHTInheritBlocklist(t *testing.T) {
|
|
|
|
ipl := iplist.New(nil)
|
|
|
|
require.NotNil(t, ipl)
|
2016-01-16 13:12:53 +00:00
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.IPBlocklist = ipl
|
|
|
|
cfg.NoDHT = false
|
|
|
|
cl, err := NewClient(&cfg)
|
2015-08-03 15:07:22 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer cl.Close()
|
|
|
|
require.Equal(t, ipl, cl.DHT().IPBlocklist())
|
|
|
|
}
|
2015-08-23 02:50:32 +00:00
|
|
|
|
|
|
|
// Check that stuff is merged in subsequent AddTorrentSpec for the same
|
|
|
|
// infohash.
|
|
|
|
func TestAddTorrentSpecMerging(t *testing.T) {
|
|
|
|
cl, err := NewClient(&TestingConfig)
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer cl.Close()
|
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(dir)
|
2016-05-05 12:40:38 +00:00
|
|
|
tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
|
|
|
|
InfoHash: mi.Info.Hash(),
|
|
|
|
})
|
2015-08-23 02:50:32 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, new)
|
|
|
|
require.Nil(t, tt.Info())
|
|
|
|
_, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, new)
|
|
|
|
require.NotNil(t, tt.Info())
|
|
|
|
}
|
|
|
|
|
2015-09-17 02:53:52 +00:00
|
|
|
func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
|
|
|
|
dir, mi := testutil.GreetingTestTorrent()
|
|
|
|
os.RemoveAll(dir)
|
|
|
|
cl, _ := NewClient(&TestingConfig)
|
|
|
|
defer cl.Close()
|
2016-05-05 12:40:38 +00:00
|
|
|
tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
|
|
|
|
InfoHash: mi.Info.Hash(),
|
|
|
|
})
|
2015-09-17 02:53:52 +00:00
|
|
|
tt.Drop()
|
|
|
|
assert.EqualValues(t, 0, len(cl.Torrents()))
|
|
|
|
select {
|
|
|
|
case <-tt.GotInfo():
|
|
|
|
t.FailNow()
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 06:09:05 +00:00
|
|
|
|
2016-03-29 00:14:34 +00:00
|
|
|
func writeTorrentData(ts storage.Torrent, info *metainfo.InfoEx, b []byte) {
|
|
|
|
for i := range iter.N(info.NumPieces()) {
|
2016-03-30 08:12:57 +00:00
|
|
|
n, _ := ts.Piece(info.Piece(i)).WriteAt(b, 0)
|
2016-03-29 00:14:34 +00:00
|
|
|
b = b[n:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-16 11:50:43 +00:00
|
|
|
func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf func(*filecache.Cache) storage.Client) {
|
2016-03-29 00:14:34 +00:00
|
|
|
fileCacheDir, err := ioutil.TempDir("", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(fileCacheDir)
|
|
|
|
fileCache, err := filecache.NewCache(fileCacheDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
greetingDataTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingDataTempDir)
|
2016-05-16 10:11:00 +00:00
|
|
|
filePieceStore := csf(fileCache)
|
2016-03-29 00:14:34 +00:00
|
|
|
greetingData, err := filePieceStore.OpenTorrent(&greetingMetainfo.Info)
|
|
|
|
require.NoError(t, err)
|
|
|
|
writeTorrentData(greetingData, &greetingMetainfo.Info, []byte(testutil.GreetingFileContents))
|
|
|
|
// require.Equal(t, len(testutil.GreetingFileContents), written)
|
|
|
|
// require.NoError(t, err)
|
|
|
|
for i := 0; i < greetingMetainfo.Info.NumPieces(); i++ {
|
|
|
|
p := greetingMetainfo.Info.Piece(i)
|
|
|
|
if alreadyCompleted {
|
|
|
|
err := greetingData.Piece(p).MarkComplete()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cfg := TestingConfig
|
|
|
|
// TODO: Disable network option?
|
|
|
|
cfg.DisableTCP = true
|
|
|
|
cfg.DisableUTP = true
|
|
|
|
cfg.DefaultStorage = filePieceStore
|
|
|
|
cl, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer cl.Close()
|
|
|
|
tt, err := cl.AddTorrent(greetingMetainfo)
|
|
|
|
require.NoError(t, err)
|
|
|
|
psrs := tt.PieceStateRuns()
|
|
|
|
assert.Len(t, psrs, 1)
|
|
|
|
assert.EqualValues(t, 3, psrs[0].Length)
|
|
|
|
assert.Equal(t, alreadyCompleted, psrs[0].Complete)
|
|
|
|
if alreadyCompleted {
|
|
|
|
r := tt.NewReader()
|
|
|
|
b, err := ioutil.ReadAll(r)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.EqualValues(t, testutil.GreetingFileContents, b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddTorrentPiecesAlreadyCompleted(t *testing.T) {
|
2016-05-16 10:11:00 +00:00
|
|
|
testAddTorrentPriorPieceCompletion(t, true, fileCachePieceFileStorage)
|
|
|
|
testAddTorrentPriorPieceCompletion(t, true, fileCachePieceResourceStorage)
|
2016-03-29 00:14:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
|
2016-05-16 10:11:00 +00:00
|
|
|
testAddTorrentPriorPieceCompletion(t, false, fileCachePieceFileStorage)
|
|
|
|
testAddTorrentPriorPieceCompletion(t, false, fileCachePieceResourceStorage)
|
2016-03-29 00:14:34 +00:00
|
|
|
}
|
2016-02-23 14:48:44 +00:00
|
|
|
|
2016-02-24 10:56:50 +00:00
|
|
|
func TestAddMetainfoWithNodes(t *testing.T) {
|
2016-02-23 14:48:44 +00:00
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.NoDHT = false
|
2016-02-24 10:56:50 +00:00
|
|
|
// For now, we want to just jam the nodes into the table, without
|
|
|
|
// verifying them first. Also the DHT code doesn't support mixing secure
|
|
|
|
// and insecure nodes if security is enabled (yet).
|
|
|
|
cfg.DHTConfig.NoSecurity = true
|
2016-02-23 14:48:44 +00:00
|
|
|
cl, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer cl.Close()
|
|
|
|
assert.EqualValues(t, cl.DHT().NumNodes(), 0)
|
|
|
|
tt, err := cl.AddTorrentFromFile("metainfo/testdata/issue_65a.torrent")
|
|
|
|
require.NoError(t, err)
|
2016-04-03 08:40:43 +00:00
|
|
|
assert.Len(t, tt.trackers, 5)
|
2016-02-23 14:48:44 +00:00
|
|
|
assert.EqualValues(t, 6, cl.DHT().NumNodes())
|
|
|
|
}
|
2016-02-26 11:10:29 +00:00
|
|
|
|
|
|
|
type testDownloadCancelParams struct {
|
|
|
|
ExportClientStatus bool
|
|
|
|
SetLeecherStorageCapacity bool
|
|
|
|
LeecherStorageCapacity int64
|
|
|
|
Cancel bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
|
|
|
|
greetingTempDir, mi := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingTempDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.Seed = true
|
|
|
|
cfg.DataDir = greetingTempDir
|
|
|
|
seeder, err := NewClient(&cfg)
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer seeder.Close()
|
|
|
|
if ps.ExportClientStatus {
|
|
|
|
testutil.ExportStatusWriter(seeder, "s")
|
|
|
|
}
|
|
|
|
seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
|
|
|
|
leecherDataDir, err := ioutil.TempDir("", "")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer os.RemoveAll(leecherDataDir)
|
2016-03-29 00:14:34 +00:00
|
|
|
fc, err := filecache.NewCache(leecherDataDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
if ps.SetLeecherStorageCapacity {
|
|
|
|
fc.SetCapacity(ps.LeecherStorageCapacity)
|
|
|
|
}
|
2016-05-16 12:02:03 +00:00
|
|
|
cfg.DefaultStorage = storage.NewFileStorePieces(fc.AsFileStore())
|
2016-03-28 09:38:30 +00:00
|
|
|
cfg.DataDir = leecherDataDir
|
2016-02-26 11:10:29 +00:00
|
|
|
leecher, _ := NewClient(&cfg)
|
|
|
|
defer leecher.Close()
|
|
|
|
if ps.ExportClientStatus {
|
|
|
|
testutil.ExportStatusWriter(leecher, "l")
|
|
|
|
}
|
|
|
|
leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) {
|
|
|
|
ret = TorrentSpecFromMetaInfo(mi)
|
|
|
|
ret.ChunkSize = 2
|
|
|
|
return
|
|
|
|
}())
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, new)
|
|
|
|
psc := leecherGreeting.SubscribePieceStateChanges()
|
|
|
|
defer psc.Close()
|
|
|
|
leecherGreeting.DownloadAll()
|
|
|
|
if ps.Cancel {
|
|
|
|
leecherGreeting.CancelPieces(0, leecherGreeting.NumPieces())
|
|
|
|
}
|
|
|
|
leecherGreeting.AddPeers([]Peer{
|
|
|
|
Peer{
|
|
|
|
IP: missinggo.AddrIP(seeder.ListenAddr()),
|
|
|
|
Port: missinggo.AddrPort(seeder.ListenAddr()),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
completes := make(map[int]bool, 3)
|
|
|
|
values:
|
|
|
|
for {
|
2016-03-28 09:38:30 +00:00
|
|
|
// started := time.Now()
|
2016-02-26 11:10:29 +00:00
|
|
|
select {
|
|
|
|
case _v := <-psc.Values:
|
2016-03-28 09:38:30 +00:00
|
|
|
// log.Print(time.Since(started))
|
2016-02-26 11:10:29 +00:00
|
|
|
v := _v.(PieceStateChange)
|
|
|
|
completes[v.Index] = v.Complete
|
2016-02-26 11:18:08 +00:00
|
|
|
case <-time.After(100 * time.Millisecond):
|
2016-02-26 11:10:29 +00:00
|
|
|
break values
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ps.Cancel {
|
|
|
|
assert.EqualValues(t, map[int]bool{0: false, 1: false, 2: false}, completes)
|
|
|
|
} else {
|
|
|
|
assert.EqualValues(t, map[int]bool{0: true, 1: true, 2: true}, completes)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTorrentDownloadAll(t *testing.T) {
|
|
|
|
testDownloadCancel(t, testDownloadCancelParams{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTorrentDownloadAllThenCancel(t *testing.T) {
|
|
|
|
testDownloadCancel(t, testDownloadCancelParams{
|
|
|
|
Cancel: true,
|
|
|
|
})
|
|
|
|
}
|
2016-03-22 02:09:02 +00:00
|
|
|
|
|
|
|
// Ensure that it's an error for a peer to send an invalid have message.
|
|
|
|
func TestPeerInvalidHave(t *testing.T) {
|
|
|
|
cl, err := NewClient(&TestingConfig)
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer cl.Close()
|
2016-05-09 05:47:39 +00:00
|
|
|
ie := metainfo.InfoEx{
|
|
|
|
Info: metainfo.Info{
|
|
|
|
PieceLength: 1,
|
|
|
|
Pieces: make([]byte, 20),
|
|
|
|
Files: []metainfo.FileInfo{{Length: 1}},
|
2016-03-22 02:09:02 +00:00
|
|
|
},
|
2016-05-09 05:47:39 +00:00
|
|
|
}
|
|
|
|
ie.UpdateBytes()
|
|
|
|
tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
|
|
|
|
Info: &ie,
|
|
|
|
InfoHash: ie.Hash(),
|
2016-03-22 02:09:02 +00:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, _new)
|
|
|
|
defer tt.Drop()
|
|
|
|
cn := &connection{
|
2016-04-03 08:40:43 +00:00
|
|
|
t: tt,
|
2016-03-22 02:09:02 +00:00
|
|
|
}
|
|
|
|
assert.NoError(t, cn.peerSentHave(0))
|
|
|
|
assert.Error(t, cn.peerSentHave(1))
|
|
|
|
}
|
2016-03-28 09:38:30 +00:00
|
|
|
|
|
|
|
func TestPieceCompletedInStorageButNotClient(t *testing.T) {
|
|
|
|
greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
|
|
|
|
defer os.RemoveAll(greetingTempDir)
|
|
|
|
cfg := TestingConfig
|
|
|
|
cfg.DataDir = greetingTempDir
|
|
|
|
seeder, err := NewClient(&TestingConfig)
|
|
|
|
require.NoError(t, err)
|
|
|
|
seeder.AddTorrentSpec(&TorrentSpec{
|
|
|
|
Info: &greetingMetainfo.Info,
|
|
|
|
})
|
|
|
|
}
|