func (ds *readDelayedStorage) ReadBlock(b []byte, off int64) (n int, err error) {
	time.Sleep(ds.delay)
	n = len(b)
	return
}
/*func OpenReadDelayedStorage(mi *metainfo.MetaInfo, baseDir string, blocks []int,
logger *log.Logger) (s storage.Storage, seed bool) {
fs, seed := storage.OpenFileStorage(mi, baseDir, blocks, logger)
s = &readDelayedStorage{
fs: fs,
}
return
}
package torrent
import (
	"bytes"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
	"github.com/anacrolix/missinggo/bitmap"
	"github.com/anacrolix/torrent"
	"github.com/lkslts64/charo-torrent/bencode"
	"github.com/lkslts64/charo-torrent/metainfo"
	"github.com/lkslts64/charo-torrent/peer_wire"
	"github.com/lkslts64/charo-torrent/torrent/storage"
	"github.com/lkslts64/charo-torrent/tracker"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)
func TestTorrentNewConnection(t *testing.T) {
	_, tr := newClientWithTorrent(t, testingConfig(), helloWorldTorrentFile, nil)
	err := tr.StartDataTransfer()
	require.NoError(t, err)
	for i := 0; i < tr.maxEstablishedConnections; i++ {
		ci := &connInfo{
			t:        tr,
			recvC:    make(chan interface{}, sendCSize),
			sendC:    make(chan interface{}, recvCSize),
			droppedC: make(chan struct{}),
		}
		tr.newConnC <- ci
		switch (<-ci.sendC).(type) {
		case haveInfo:
		default:
			t.Fail()
		}
		switch (<-ci.sendC).(type) {
		case bitmap.Bitmap:
		default:
			t.Fail()
		}
	}
	assert.Equal(t, tr.maxEstablishedConnections, len(tr.conns))
}
func TestStatsUpdate(t *testing.T) {
	tr := &Torrent{
		mi: &metainfo.MetaInfo{},
		utmetadata: &utMetadatas{
			m: make(map[int64]*utMetadata),
		},
	}
	tr.downloadEnabled = true
	tr.uploadEnabled = true
	ci := &connInfo{
		t:        tr,
		recvC:    make(chan interface{}, sendCSize),
		sendC:    make(chan interface{}, recvCSize),
		droppedC: make(chan struct{}),
		state:    newConnState(),
		numWant:  1,  
	}
	tr.onConnMsg(msgWithConn{
		conn: ci,
		val: &peer_wire.Msg{
			Kind: peer_wire.Interested,
		},
	})
	ci.unchoke()
	sleepDur := time.Millisecond
	time.Sleep(sleepDur)
	assert.GreaterOrEqual(t, int64(ci.durationUploading()), int64(sleepDur))
	assert.Equal(t, int64(0), int64(ci.stats.sumUploading))
	ci.choke()
	assert.Greater(t, int64(ci.stats.sumUploading), int64(0))
	time.Sleep(sleepDur)
	assert.Less(t, int64(ci.durationUploading()), int64(2*sleepDur))
	assert.EqualValues(t, int64(0), int64(ci.durationDownloading()))
	ci.interested()
	tr.onConnMsg(msgWithConn{
		conn: ci,
		val: &peer_wire.Msg{
			Kind: peer_wire.Unchoke,
		},
	})
	time.Sleep(sleepDur)
	assert.GreaterOrEqual(t, int64(ci.durationDownloading()), int64(sleepDur))
	assert.Equal(t, float64(0), ci.rate())
	ci.stats.downloadUsefulBytes += 1 << 14
	r1 := ci.rate()
	assert.Greater(t, r1, float64(0))
	time.Sleep(sleepDur)
	r2 := ci.rate()
	assert.Less(t, r2, r1)
}
func testingConfig() *Config {
	return &Config{
		SelectorF:           NewDefaultPieceSelector,
		MaxOnFlightReqs:     250,
		MaxEstablishedConns: 55,
		BaseDir:             "./testdata/",
		DisableTrackers:     true,
		DisableDHT:          true,
		OpenStorage:         storage.OpenFileStorage,
		DialTimeout:         5 * time.Second,
		HandshakeTiemout:    4 * time.Second,
	}
}
var helloWorldTorrentFile = "./testdata/helloworld.torrent"
var helloWorldContents = "Hello World\n"
var blockchainTorrentFile = "./testdata/blockchain.torrent"
func TestLoadCompleteTorrent(t *testing.T) {
	_, tr := newClientWithTorrent(t, testingConfig(), helloWorldTorrentFile, nil)
	assert.Equal(t, true, tr.haveAll())
	data := make([]byte, tr.pieceLen(0))
	tr.readBlock(data, 0, 0)
	assert.EqualValues(t, 12, len(data))
	assert.EqualValues(t, helloWorldContents, string(data))
}
func TestSingleFileTorrentTransfer(t *testing.T) {
	testDataTransfer(t, dataTransferOpts{
		helloWorldTorrentFile,
		1,
	})
}
func TestMultiFileTorrentTransfer(t *testing.T) {
	testDataTransfer(t, dataTransferOpts{
		blockchainTorrentFile,
		7,
	})
}
func addrsToPeers(addrs []string) []Peer {
	peers := make([]Peer, len(addrs))
	for i, addr := range addrs {
		peers[i] = addrToPeer(addr, SourceUser)
	}
	return peers
}
type dataTransferOpts struct {
	filename    string
	numLeechers int
}
func testDataTransfer(t *testing.T, opts dataTransferOpts) {
	seeder, seederTr := newClientWithTorrent(t, testingConfig(), helloWorldTorrentFile, func(tr *Torrent) {
		assert.True(t, tr.haveAll())
		require.NoError(t, tr.StartDataTransfer())
	})
	defer seeder.Close()
	leechers := make([]*Client, opts.numLeechers)
	leechAddrs := make([]string, len(leechers))
	for i := range leechers {
		tcfg := testingConfig()
		tcfg.BaseDir += "/leecher" + strconv.Itoa(i)
		leechers[i], _ = newClientWithTorrent(t, tcfg, helloWorldTorrentFile, nil)
		leechAddrs[i] = leechers[i].addr()
		defer leechers[i].Close()
		defer os.RemoveAll(tcfg.BaseDir)
	}
	wg := sync.WaitGroup{}
	wg.Add(len(leechers))
	for i, leecher := range leechers {
		leecherTr := leecher.Torrents()[0]
		go func() {
			defer wg.Done()
			require.NoError(t, leecherTr.StartDataTransfer())
			<-leecherTr.DownloadedDataC
		}()
		leecherTr.AddPeers(addrsToPeers(append(leechAddrs[i+1:], seeder.addr()))...)
	}
	dataSeeder := make([]byte, seederTr.length)
	err := seederTr.readBlock(dataSeeder, 0, 0)
	require.NoError(t, err)
	wg.Wait()
	for _, leecher := range leechers {
		leecherTr := leecher.Torrents()[0]
		assert.True(t, leecherTr.haveAll())
		testContents(t, dataSeeder, leecherTr)
	}
}
func testContents(t *testing.T, dataSeeder []byte, leecherTr *Torrent) {
	assert.Equal(t, len(dataSeeder), leecherTr.length)
	dataLeecher := make([]byte, leecherTr.length)
	err := leecherTr.readBlock(dataLeecher, 0, 0)
	require.NoError(t, err)
	assert.Equal(t, dataSeeder, dataLeecher)
}
func testThirdPartyDataTransfer(t *testing.T, torrentFile string) {
	if testing.Short() {
		t.Skip("skiping test with third party torrent libriaries (anacrolix)")
	}
	cfg := torrent.NewDefaultClientConfig()
	cfg.DataDir = "./testdata"
	cfg.NoDHT = true
	cfg.Seed = true
	cfg.DisablePEX = true
	seeder, err := torrent.NewClient(cfg)
	require.NoError(t, err)
	defer seeder.Close()
	seederTr, err := seeder.AddTorrentFromFile(torrentFile)
	require.NoError(t, err)
	seederTr.VerifyData()
	assert.True(t, seederTr.Seeding())
	tcfg := testingConfig()
	tcfg.BaseDir += "/leecher"
	leecher, leecherTr := newClientWithTorrent(t, tcfg, torrentFile, nil)
	defer os.RemoveAll(leecher.config.BaseDir)
	leecherTr.AddPeers(addrToPeer(seeder.ListenAddrs()[0].String(), SourceUser))
	leecherTr.StartDataTransfer()
	<-leecherTr.DownloadedDataC
	assert.True(t, leecherTr.haveAll())
	testContentsThirdParty(t, seederTr, leecherTr)
}
func testContentsThirdParty(t *testing.T, seederTr *torrent.Torrent, leecherTr *Torrent) {
	assert.EqualValues(t, seederTr.Length(), leecherTr.length)
	dataSeeder := make([]byte, seederTr.Length())
	dataLeecher := make([]byte, leecherTr.length)
	r := seederTr.NewReader()
	n, err := r.Read(dataSeeder)
	require.EqualValues(t, err, io.EOF)
	if n != len(dataSeeder) {
		t.Log("third party reader:", err)
	}
	assert.EqualValues(t, n, len(dataSeeder))
	err = leecherTr.readBlock(dataLeecher, 0, 0)
	require.NoError(t, err)
	assert.Equal(t, dataSeeder, dataLeecher)
}
func TestThirdPartySingleFileDataTransfer(t *testing.T) {
	testThirdPartyDataTransfer(t, helloWorldTorrentFile)
}
func TestThirdPartyMultiFileDataTransfer(t *testing.T) {
	testThirdPartyDataTransfer(t, blockchainTorrentFile)
}
type dummyTracker struct {
	myAddr       string
	t            *testing.T
	peer         tracker.Peer
	numAnnounces int
}
func (dt *dummyTracker) addr() string {
	return "http://" + dt.myAddr + "/announce"
}
type httpAnnounceResponse struct {
	Interval int32          `bencode:"interval"`
	Peers    []tracker.Peer `bencode:"peers" empty:"omit"`
}
func (dt *dummyTracker) announceHandler(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	e, ok := q["event"]
	if ok {
		assert.Len(dt.t, e, 1)
		assert.EqualValues(dt.t, "started", e[0])
		assert.Equal(dt.t, 0, dt.numAnnounces)
	}
	bytes, _ := bencode.Encode(httpAnnounceResponse{
		Interval: 1,
		Peers: []tracker.Peer{
			dt.peer,
		},
	})
	w.Write(bytes)
	dt.numAnnounces++
}
func (dt *dummyTracker) serve() {
	port := strings.Split(dt.myAddr, ":")[1]
	http.HandleFunc("/announce", dt.announceHandler)
	go func() {
		log.Fatal(http.ListenAndServe(":"+port, nil))
	}()
}
var localhost = "127.0.0.1"
func TestTrackerAnnouncer(t *testing.T) {
	cfg := testingConfig()
	cfg.DisableTrackers = false
	cfg.BaseDir = ".testdata/utopia"
	cl, err := NewClient(cfg)
	require.NoError(t, err)
	dt := &dummyTracker{
		myAddr: localhost + ":8081",
		t:      t,
		peer: tracker.Peer{
			ID:   cl.ID(),
			IP:   []byte(getOutboundIP().String()),
			Port: uint16(cl.port),
		},
	}
	dt.serve()
	tr, err := cl.AddFromFile(helloWorldTorrentFile)
	require.NoError(t, err)
	tr.mi.Announce = dt.addr()
	require.NoError(t, tr.StartDataTransfer())
	time.Sleep(4 * time.Second)
	defer cl.Close()
	assert.Equal(t, 2, len(tr.conns))
}
type readDelayedStorage struct {
	delay time.Duration
}