.circleci/config.yml | 2 +- client_test.go | 22 ++++++++++------------ fs/torrentfs_test.go | 2 +- internal/testutil/status_writer.go | 63 ++++++++++++++++++++++++++--------------------------- diff --git a/.circleci/config.yml b/.circleci/config.yml index d01aafdc68763cb556e933f691ae86442e7c1d6e..464d417e9aa94404796946a3f8a0d9558547af86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,7 +22,7 @@ - checkout - run: sudo apt-get update - run: sudo apt install fuse pv - run: go get -t -d -v -race $PROJECT_GO_PACKAGE/... - - run: go test -v -race $PROJECT_GO_PACKAGE/... + - run: go test -v -race $PROJECT_GO_PACKAGE/... -count 2 - run: go test -bench . $PROJECT_GO_PACKAGE/... - run: CGO_ENABLED=0 go get -t -d -v $PROJECT_GO_PACKAGE/... - run: set +e; CGO_ENABLED=0 go test -v $PROJECT_GO_PACKAGE/...; true diff --git a/client_test.go b/client_test.go index 7921f90cd6500aebe020547e6539c6745e4eef59..76893d7303402f78fdd3d376a086f3c477606388 100644 --- a/client_test.go +++ b/client_test.go @@ -311,7 +311,7 @@ } seeder, err := NewClient(cfg) require.NoError(t, err) if ps.ExportClientStatus { - testutil.ExportStatusWriter(seeder, "s") + defer testutil.ExportStatusWriter(seeder, "s")() } seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) // Run a Stats right after Closing the Client. This will trigger the Stats @@ -337,7 +337,7 @@ leecher, err := NewClient(cfg) require.NoError(t, err) defer leecher.Close() if ps.ExportClientStatus { - testutil.ExportStatusWriter(leecher, "l") + defer testutil.ExportStatusWriter(leecher, "l")() } leecherTorrent, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) @@ -396,7 +396,7 @@ cfg.DataDir = greetingTempDir seeder, err := NewClient(cfg) require.NoError(t, err) defer seeder.Close() - testutil.ExportStatusWriter(seeder, "s") + defer testutil.ExportStatusWriter(seeder, "s")() seederTorrent, ok, err := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) require.NoError(t, err) assert.True(t, ok) @@ -410,7 +410,7 @@ defer os.RemoveAll(cfg.DataDir) leecher, err := NewClient(cfg) require.NoError(t, err) defer leecher.Close() - testutil.ExportStatusWriter(leecher, "l") + defer testutil.ExportStatusWriter(leecher, "l")() cfg = TestingConfig() cfg.Seed = false @@ -420,7 +420,7 @@ defer os.RemoveAll(cfg.DataDir) leecherLeecher, _ := NewClient(cfg) require.NoError(t, err) defer leecherLeecher.Close() - testutil.ExportStatusWriter(leecherLeecher, "ll") + defer testutil.ExportStatusWriter(leecherLeecher, "ll")() leecherGreeting, ok, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 @@ -769,7 +769,7 @@ cfg.DataDir = greetingTempDir seeder, err := NewClient(cfg) require.NoError(t, err) defer seeder.Close() - testutil.ExportStatusWriter(seeder, "s") + defer testutil.ExportStatusWriter(seeder, "s")() seederTorrent, _, _ := seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi)) seederTorrent.VerifyData() leecherDataDir, err := ioutil.TempDir("", "") @@ -784,7 +784,7 @@ cfg.DefaultStorage = storage.NewResourcePieces(fc.AsResourceProvider()) cfg.DataDir = leecherDataDir leecher, _ := NewClient(cfg) defer leecher.Close() - testutil.ExportStatusWriter(leecher, "l") + defer testutil.ExportStatusWriter(leecher, "l")() leecherGreeting, new, err := leecher.AddTorrentSpec(func() (ret *TorrentSpec) { ret = TorrentSpecFromMetaInfo(mi) ret.ChunkSize = 2 @@ -930,8 +930,6 @@ return } func TestSetMaxEstablishedConn(t *testing.T) { - ss := testutil.NewStatusServer(t) - defer ss.Close() var tts []*Torrent ih := testutil.GreetingMetaInfo().HashInfoBytes() cfg := TestingConfig() @@ -943,7 +941,7 @@ require.NoError(t, err) defer cl.Close() tt, _ := cl.AddTorrentInfoHash(ih) tt.SetMaxEstablishedConns(2) - ss.HandleStatusWriter(cl, fmt.Sprintf("/%d", i)) + defer testutil.ExportStatusWriter(cl, fmt.Sprintf("%d", i))() tts = append(tts, tt) } addPeers := func() { @@ -1007,7 +1005,7 @@ os.Mkdir(cfg.DataDir, 0755) server, err := NewClient(cfg) require.NoError(t, err) defer server.Close() - testutil.ExportStatusWriter(server, "s") + defer testutil.ExportStatusWriter(server, "s")() magnet1 := makeMagnet(t, server, cfg.DataDir, "test1") makeMagnet(t, server, cfg.DataDir, "test2") cfg = TestingConfig() @@ -1017,7 +1015,7 @@ cfg.ForceEncryption = true client, err := NewClient(cfg) require.NoError(t, err) defer client.Close() - testutil.ExportStatusWriter(client, "c") + defer testutil.ExportStatusWriter(client, "c")() tr, err := client.AddMagnet(magnet1) require.NoError(t, err) tr.AddClientPeer(server) diff --git a/fs/torrentfs_test.go b/fs/torrentfs_test.go index 8ac69bb70731e7285a6fa7a85c7f995e8ca9e106..fb326cc52ecba1f6d68b068b99e008c7c3957ac5 100644 --- a/fs/torrentfs_test.go +++ b/fs/torrentfs_test.go @@ -174,7 +174,7 @@ cfg.ListenHost = torrent.LoopbackListenHost seeder, err := torrent.NewClient(cfg) require.NoError(t, err) defer seeder.Close() - testutil.ExportStatusWriter(seeder, "s") + defer testutil.ExportStatusWriter(seeder, "s")() // Just to mix things up, the seeder starts with the data, but the leecher // starts with the metainfo. seederTorrent, err := seeder.AddMagnet(fmt.Sprintf("magnet:?xt=urn:btih:%s", layout.Metainfo.HashInfoBytes().HexString())) diff --git a/internal/testutil/status_writer.go b/internal/testutil/status_writer.go index 0266e3eba856efee21e4765c3a358f94ddde79f2..5ce66c612865b98d5f12c302973aa3bc93d1ad8f 100644 --- a/internal/testutil/status_writer.go +++ b/internal/testutil/status_writer.go @@ -3,51 +3,50 @@ import ( "fmt" "io" - "log" - "net" "net/http" - "testing" + "sync" "github.com/anacrolix/missinggo" - "github.com/stretchr/testify/require" ) type StatusWriter interface { WriteStatus(io.Writer) } -// Use StatusServer instead to allow -count > 1 when testing. -func ExportStatusWriter(sw StatusWriter, path string) { +// The key is the route pattern. The value is nil when the resource is +// released. +var ( + mu sync.Mutex + sws = map[string]StatusWriter{} +) + +func ExportStatusWriter(sw StatusWriter, path string) (release func()) { + pattern := fmt.Sprintf("/%s/%s", missinggo.GetTestName(), path) + release = func() { + mu.Lock() + defer mu.Unlock() + sws[pattern] = nil + } + mu.Lock() + defer mu.Unlock() + if curSw, ok := sws[pattern]; ok { + if curSw != nil { + panic(fmt.Sprintf("%q still in use", pattern)) + } + sws[pattern] = sw + return + } http.HandleFunc( - fmt.Sprintf("/%s/%s", missinggo.GetTestName(), path), + pattern, func(w http.ResponseWriter, r *http.Request) { + sw := sws[pattern] + if sw == nil { + http.NotFound(w, r) + return + } sw.WriteStatus(w) }, ) -} - -type StatusServer struct { - sm http.ServeMux - l net.Listener -} - -func NewStatusServer(t *testing.T) (ret *StatusServer) { - l, err := net.Listen("tcp", "localhost:0") - require.NoError(t, err) - ret = &StatusServer{ - l: l, - } - log.Printf("serving status at %q", l.Addr()) - go http.Serve(l, &ret.sm) + sws[pattern] = sw return } - -func (me *StatusServer) HandleStatusWriter(sw StatusWriter, path string) { - me.sm.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - sw.WriteStatus(w) - }) -} - -func (me StatusServer) Close() { - me.l.Close() -}