]> Sergey Matveev's repositories - btrtrc.git/blobdiff - client_test.go
Fix TestTorrentDroppedDuringResponsiveRead flakiness
[btrtrc.git] / client_test.go
index 24820ac49a07f41f520d507709332e33de577dac..dc53f8e863ee5a127fcc2bc2d8c0858570088f59 100644 (file)
@@ -9,14 +9,15 @@ import (
        "path/filepath"
        "reflect"
        "testing"
+       "testing/iotest"
        "time"
 
-       "github.com/bradfitz/iter"
+       "github.com/frankban/quicktest"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 
        "github.com/anacrolix/dht/v2"
-       "github.com/anacrolix/missinggo"
+       "github.com/anacrolix/missinggo/v2"
        "github.com/anacrolix/missinggo/v2/filecache"
 
        "github.com/anacrolix/torrent/bencode"
@@ -27,35 +28,23 @@ import (
 )
 
 func TestClientDefault(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
-       cl.Close()
+       require.Empty(t, cl.Close())
 }
 
 func TestClientNilConfig(t *testing.T) {
+       // The default config will put crap in the working directory.
+       origDir, _ := os.Getwd()
+       defer os.Chdir(origDir)
+       os.Chdir(t.TempDir())
        cl, err := NewClient(nil)
        require.NoError(t, err)
-       cl.Close()
-}
-
-func TestBoltPieceCompletionClosedWhenClientClosed(t *testing.T) {
-       cfg := TestingConfig()
-       pc, err := storage.NewBoltPieceCompletion(cfg.DataDir)
-       require.NoError(t, err)
-       ci := storage.NewFileWithCompletion(cfg.DataDir, pc)
-       defer ci.Close()
-       cfg.DefaultStorage = ci
-       cl, err := NewClient(cfg)
-       require.NoError(t, err)
-       cl.Close()
-       // And again, https://github.com/anacrolix/torrent/issues/158
-       cl, err = NewClient(cfg)
-       require.NoError(t, err)
-       cl.Close()
+       require.Empty(t, cl.Close())
 }
 
 func TestAddDropTorrent(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
        dir, mi := testutil.GreetingTestTorrent()
@@ -90,17 +79,16 @@ func TestPieceHashSize(t *testing.T) {
 func TestTorrentInitialState(t *testing.T) {
        dir, mi := testutil.GreetingTestTorrent()
        defer os.RemoveAll(dir)
-       cl := &Client{
-               config: TestingConfig(),
-       }
+       var cl Client
+       cl.init(TestingConfig(t))
        cl.initLogger()
        tor := cl.newTorrent(
                mi.HashInfoBytes(),
-               storage.NewFileWithCompletion(TestingTempDir.NewSub(), storage.NewMapPieceCompletion()),
+               storage.NewFileWithCompletion(t.TempDir(), storage.NewMapPieceCompletion()),
        )
        tor.setChunkSize(2)
        tor.cl.lock()
-       err := tor.setInfoBytes(mi.InfoBytes)
+       err := tor.setInfoBytesLocked(mi.InfoBytes)
        tor.cl.unlock()
        require.NoError(t, err)
        require.Len(t, tor.pieces, 3)
@@ -108,7 +96,7 @@ func TestTorrentInitialState(t *testing.T) {
        tor.cl.lock()
        assert.EqualValues(t, 3, tor.pieceNumPendingChunks(0))
        tor.cl.unlock()
-       assert.EqualValues(t, chunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
+       assert.EqualValues(t, ChunkSpec{4, 1}, chunkIndexSpec(2, tor.pieceLength(0), tor.chunkSize))
 }
 
 func TestReducedDialTimeout(t *testing.T) {
@@ -138,10 +126,10 @@ func TestReducedDialTimeout(t *testing.T) {
 }
 
 func TestAddDropManyTorrents(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
-       for i := range iter.N(1000) {
+       for i := 0; i < 1000; i += 1 {
                var spec TorrentSpec
                binary.PutVarint(spec.InfoHash[:], int64(i))
                tt, new, err := cl.AddTorrentSpec(&spec)
@@ -161,7 +149,7 @@ func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
 }
 
 func TestMergingTrackersByAddingSpecs(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
        spec := TorrentSpec{}
@@ -179,7 +167,7 @@ func TestMergingTrackersByAddingSpecs(t *testing.T) {
 
 // We read from a piece which is marked completed, but is missing data.
 func TestCompletedPieceWrongSize(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DefaultStorage = badStorage{}
        cl, err := NewClient(cfg)
        require.NoError(t, err)
@@ -202,20 +190,18 @@ func TestCompletedPieceWrongSize(t *testing.T) {
        assert.True(t, new)
        r := tt.NewReader()
        defer r.Close()
-       b, err = ioutil.ReadAll(r)
-       assert.Len(t, b, 13)
-       assert.NoError(t, err)
+       quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
 }
 
 func BenchmarkAddLargeTorrent(b *testing.B) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(b)
        cfg.DisableTCP = true
        cfg.DisableUTP = true
        cl, err := NewClient(cfg)
        require.NoError(b, err)
        defer cl.Close()
        b.ReportAllocs()
-       for range iter.N(b.N) {
+       for i := 0; i < b.N; i += 1 {
                t, err := cl.AddTorrentFromFile("testdata/bootstrap.dat.torrent")
                if err != nil {
                        b.Fatal(err)
@@ -227,7 +213,7 @@ func BenchmarkAddLargeTorrent(b *testing.B) {
 func TestResponsive(t *testing.T) {
        seederDataDir, mi := testutil.GreetingTestTorrent()
        defer os.RemoveAll(seederDataDir)
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.Seed = true
        cfg.DataDir = seederDataDir
        seeder, err := NewClient(cfg)
@@ -238,7 +224,7 @@ func TestResponsive(t *testing.T) {
        leecherDataDir, err := ioutil.TempDir("", "")
        require.Nil(t, err)
        defer os.RemoveAll(leecherDataDir)
-       cfg = TestingConfig()
+       cfg = TestingConfig(t)
        cfg.DataDir = leecherDataDir
        leecher, err := NewClient(cfg)
        require.Nil(t, err)
@@ -270,7 +256,7 @@ func TestResponsive(t *testing.T) {
 func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
        seederDataDir, mi := testutil.GreetingTestTorrent()
        defer os.RemoveAll(seederDataDir)
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.Seed = true
        cfg.DataDir = seederDataDir
        seeder, err := NewClient(cfg)
@@ -281,7 +267,7 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
        leecherDataDir, err := ioutil.TempDir("", "")
        require.Nil(t, err)
        defer os.RemoveAll(leecherDataDir)
-       cfg = TestingConfig()
+       cfg = TestingConfig(t)
        cfg.DataDir = leecherDataDir
        leecher, err := NewClient(cfg)
        require.Nil(t, err)
@@ -302,9 +288,9 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
        _, err = io.ReadFull(reader, b)
        assert.Nil(t, err)
        assert.EqualValues(t, "lo", string(b))
-       go leecherTorrent.Drop()
        _, err = reader.Seek(11, io.SeekStart)
        require.NoError(t, err)
+       leecherTorrent.Drop()
        n, err := reader.Read(b)
        assert.EqualError(t, err, "torrent closed")
        assert.EqualValues(t, 0, n)
@@ -313,7 +299,7 @@ func TestTorrentDroppedDuringResponsiveRead(t *testing.T) {
 func TestDhtInheritBlocklist(t *testing.T) {
        ipl := iplist.New(nil)
        require.NotNil(t, ipl)
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.IPBlocklist = ipl
        cfg.NoDHT = false
        cl, err := NewClient(cfg)
@@ -322,7 +308,7 @@ func TestDhtInheritBlocklist(t *testing.T) {
        numServers := 0
        cl.eachDhtServer(func(s DhtServer) {
                t.Log(s)
-               assert.Equal(t, ipl, s.(anacrolixDhtServerWrapper).Server.IPBlocklist())
+               assert.Equal(t, ipl, s.(AnacrolixDhtServerWrapper).Server.IPBlocklist())
                numServers++
        })
        assert.EqualValues(t, 2, numServers)
@@ -331,7 +317,7 @@ func TestDhtInheritBlocklist(t *testing.T) {
 // Check that stuff is merged in subsequent AddTorrentSpec for the same
 // infohash.
 func TestAddTorrentSpecMerging(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
        dir, mi := testutil.GreetingTestTorrent()
@@ -351,7 +337,7 @@ func TestAddTorrentSpecMerging(t *testing.T) {
 func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
        dir, mi := testutil.GreetingTestTorrent()
        os.RemoveAll(dir)
-       cl, _ := NewClient(TestingConfig())
+       cl, _ := NewClient(TestingConfig(t))
        defer cl.Close()
        tt, _, _ := cl.AddTorrentSpec(&TorrentSpec{
                InfoHash: mi.HashInfoBytes(),
@@ -366,7 +352,7 @@ func TestTorrentDroppedBeforeGotInfo(t *testing.T) {
 }
 
 func writeTorrentData(ts *storage.Torrent, info metainfo.Info, b []byte) {
-       for i := range iter.N(info.NumPieces()) {
+       for i := 0; i < info.NumPieces(); i += 1 {
                p := info.Piece(i)
                ts.Piece(p).WriteAt(b[p.Offset():p.Offset()+p.Length()], 0)
        }
@@ -395,7 +381,7 @@ func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf
                        require.NoError(t, greetingData.Piece(p).MarkComplete())
                }
        }
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        // TODO: Disable network option?
        cfg.DisableTCP = true
        cfg.DisableUTP = true
@@ -411,9 +397,7 @@ func testAddTorrentPriorPieceCompletion(t *testing.T, alreadyCompleted bool, csf
        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)
+               quicktest.Check(t, iotest.TestReader(r, []byte(testutil.GreetingFileContents)), quicktest.IsNil)
        }
 }
 
@@ -426,7 +410,7 @@ func TestAddTorrentPiecesNotAlreadyCompleted(t *testing.T) {
 }
 
 func TestAddMetainfoWithNodes(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.ListenHost = func(string) string { return "" }
        cfg.NoDHT = false
        cfg.DhtStartingNodes = func(string) dht.StartingNodesGetter { return func() ([]dht.Addr, error) { return nil, nil } }
@@ -463,7 +447,7 @@ type testDownloadCancelParams struct {
 func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
        greetingTempDir, mi := testutil.GreetingTestTorrent()
        defer os.RemoveAll(greetingTempDir)
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.Seed = true
        cfg.DataDir = greetingTempDir
        seeder, err := NewClient(cfg)
@@ -499,7 +483,7 @@ func testDownloadCancel(t *testing.T, ps testDownloadCancelParams) {
        leecherGreeting.cl.lock()
        leecherGreeting.downloadPiecesLocked(0, leecherGreeting.numPieces())
        if ps.Cancel {
-               leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces())
+               leecherGreeting.cancelPiecesLocked(0, leecherGreeting.NumPieces(), "")
        }
        leecherGreeting.cl.unlock()
        done := make(chan struct{})
@@ -532,7 +516,7 @@ func TestTorrentDownloadAllThenCancel(t *testing.T) {
 
 // Ensure that it's an error for a peer to send an invalid have message.
 func TestPeerInvalidHave(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DropMutuallyCompletePeers = false
        cl, err := NewClient(cfg)
        require.NoError(t, err)
@@ -553,9 +537,13 @@ func TestPeerInvalidHave(t *testing.T) {
        assert.True(t, _new)
        defer tt.Drop()
        cn := &PeerConn{Peer: Peer{
-               t: tt,
+               t:         tt,
+               callbacks: &cfg.Callbacks,
        }}
+       tt.conns[cn] = struct{}{}
        cn.peerImpl = cn
+       cl.lock()
+       defer cl.unlock()
        assert.NoError(t, cn.peerSentHave(0))
        assert.Error(t, cn.peerSentHave(1))
 }
@@ -563,9 +551,9 @@ func TestPeerInvalidHave(t *testing.T) {
 func TestPieceCompletedInStorageButNotClient(t *testing.T) {
        greetingTempDir, greetingMetainfo := testutil.GreetingTestTorrent()
        defer os.RemoveAll(greetingTempDir)
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DataDir = greetingTempDir
-       seeder, err := NewClient(TestingConfig())
+       seeder, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        seeder.AddTorrentSpec(&TorrentSpec{
                InfoBytes: greetingMetainfo.InfoBytes,
@@ -575,7 +563,7 @@ func TestPieceCompletedInStorageButNotClient(t *testing.T) {
 // Check that when the listen port is 0, all the protocols listened on have
 // the same port, and it isn't zero.
 func TestClientDynamicListenPortAllProtocols(t *testing.T) {
-       cl, err := NewClient(TestingConfig())
+       cl, err := NewClient(TestingConfig(t))
        require.NoError(t, err)
        defer cl.Close()
        port := cl.LocalPort()
@@ -587,7 +575,7 @@ func TestClientDynamicListenPortAllProtocols(t *testing.T) {
 }
 
 func TestClientDynamicListenTCPOnly(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DisableUTP = true
        cfg.DisableTCP = false
        cl, err := NewClient(cfg)
@@ -597,7 +585,7 @@ func TestClientDynamicListenTCPOnly(t *testing.T) {
 }
 
 func TestClientDynamicListenUTPOnly(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DisableTCP = true
        cfg.DisableUTP = false
        cl, err := NewClient(cfg)
@@ -618,10 +606,10 @@ func totalConns(tts []*Torrent) (ret int) {
 func TestSetMaxEstablishedConn(t *testing.T) {
        var tts []*Torrent
        ih := testutil.GreetingMetaInfo().HashInfoBytes()
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DisableAcceptRateLimiting = true
        cfg.DropDuplicatePeerIds = true
-       for i := range iter.N(3) {
+       for i := 0; i < 3; i += 1 {
                cl, err := NewClient(cfg)
                require.NoError(t, err)
                defer cl.Close()
@@ -661,8 +649,8 @@ func TestSetMaxEstablishedConn(t *testing.T) {
 
 // Creates a file containing its own name as data. Make a metainfo from that, adds it to the given
 // client, and returns a magnet link.
-func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
-       os.MkdirAll(dir, 0770)
+func makeMagnet(t *testing.T, cl *Client, dir, name string) string {
+       os.MkdirAll(dir, 0o770)
        file, err := os.Create(filepath.Join(dir, name))
        require.NoError(t, err)
        file.Write([]byte(name))
@@ -674,7 +662,7 @@ func makeMagnet(t *testing.T, cl *Client, dir string, name string) string {
        require.NoError(t, err)
        mi.InfoBytes, err = bencode.Marshal(info)
        require.NoError(t, err)
-       magnet := mi.Magnet(name, mi.HashInfoBytes()).String()
+       magnet := mi.Magnet(nil, &info).String()
        tr, err := cl.AddTorrent(&mi)
        require.NoError(t, err)
        require.True(t, tr.Seeding())
@@ -698,11 +686,11 @@ func TestMultipleTorrentsWithEncryption(t *testing.T) {
 
 // Test that the leecher can download a torrent in its entirety from the seeder. Note that the
 // seeder config is done first.
-func testSeederLeecherPair(t *testing.T, seeder func(*ClientConfig), leecher func(*ClientConfig)) {
-       cfg := TestingConfig()
+func testSeederLeecherPair(t *testing.T, seeder, leecher func(*ClientConfig)) {
+       cfg := TestingConfig(t)
        cfg.Seed = true
        cfg.DataDir = filepath.Join(cfg.DataDir, "server")
-       os.Mkdir(cfg.DataDir, 0755)
+       os.Mkdir(cfg.DataDir, 0o755)
        seeder(cfg)
        server, err := NewClient(cfg)
        require.NoError(t, err)
@@ -715,7 +703,7 @@ func testSeederLeecherPair(t *testing.T, seeder func(*ClientConfig), leecher fun
        for i := 0; i < 100; i++ {
                makeMagnet(t, server, cfg.DataDir, fmt.Sprintf("test%d", i+2))
        }
-       cfg = TestingConfig()
+       cfg = TestingConfig(t)
        cfg.DataDir = filepath.Join(cfg.DataDir, "client")
        leecher(cfg)
        client, err := NewClient(cfg)
@@ -766,14 +754,14 @@ func TestClientAddressInUse(t *testing.T) {
        if s != nil {
                defer s.Close()
        }
-       cfg := TestingConfig().SetListenAddr(":50007")
+       cfg := TestingConfig(t).SetListenAddr(":50007")
        cl, err := NewClient(cfg)
        require.Error(t, err)
        require.Nil(t, cl)
 }
 
 func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
-       cc := TestingConfig()
+       cc := TestingConfig(t)
        cc.DisableUTP = true
        cc.NoDHT = false
        cl, err := NewClient(cc)
@@ -782,33 +770,8 @@ func TestClientHasDhtServersWhenUtpDisabled(t *testing.T) {
        assert.NotEmpty(t, cl.DhtServers())
 }
 
-func TestIssue335(t *testing.T) {
-       dir, mi := testutil.GreetingTestTorrent()
-       defer os.RemoveAll(dir)
-       cfg := TestingConfig()
-       cfg.Seed = false
-       cfg.Debug = true
-       cfg.DataDir = dir
-       comp, err := storage.NewBoltPieceCompletion(dir)
-       require.NoError(t, err)
-       defer comp.Close()
-       cfg.DefaultStorage = storage.NewMMapWithCompletion(dir, comp)
-       cl, err := NewClient(cfg)
-       require.NoError(t, err)
-       defer cl.Close()
-       tor, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
-       require.NoError(t, err)
-       assert.True(t, new)
-       require.True(t, cl.WaitAll())
-       tor.Drop()
-       _, new, err = cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
-       require.NoError(t, err)
-       assert.True(t, new)
-       require.True(t, cl.WaitAll())
-}
-
 func TestClientDisabledImplicitNetworksButDhtEnabled(t *testing.T) {
-       cfg := TestingConfig()
+       cfg := TestingConfig(t)
        cfg.DisableTCP = true
        cfg.DisableUTP = true
        cfg.NoDHT = false