diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index bb985917..98661f58 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -68,7 +68,7 @@ jobs: - name: Test continue-on-error: ${{contains(matrix.go_version, 'beta') || contains(matrix.go_version, 'rc')}} - run: go test -cover ./... -v + run: go test -race -cover ./... -v js: name: Build JS bundle diff --git a/Makefile b/Makefile index 24faefda..59773464 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ watch: ##@Development Start Go tests in watch mode (re-run when code changes) .PHONY: watch test: ##@Development Run Go tests - go test ./... + go test -race ./... .PHONY: test testall: test ##@Development Run Go and JS tests diff --git a/core/media_streamer_test.go b/core/media_streamer_test.go index 3fedd42c..a0560d2a 100644 --- a/core/media_streamer_test.go +++ b/core/media_streamer_test.go @@ -5,6 +5,8 @@ import ( "io" "os" "strings" + "sync" + "sync/atomic" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" @@ -18,7 +20,7 @@ import ( var _ = Describe("MediaStreamer", func() { var streamer MediaStreamer var ds model.DataStore - ffmpeg := &fakeFFmpeg{Data: "fake data"} + ffmpeg := newFakeFFmpeg("fake data") ctx := log.NewContext(context.TODO()) BeforeEach(func() { @@ -63,7 +65,7 @@ var _ = Describe("MediaStreamer", func() { Expect(err).To(BeNil()) _, _ = io.ReadAll(s) _ = s.Close() - Eventually(func() bool { return ffmpeg.closed }, "3s").Should(BeTrue()) + Eventually(func() bool { return ffmpeg.IsClosed() }, "3s").Should(BeTrue()) s, err = streamer.NewStream(ctx, "123", "mp3", 32) Expect(err).To(BeNil()) @@ -193,22 +195,31 @@ var _ = Describe("MediaStreamer", func() { }) }) +func newFakeFFmpeg(data string) *fakeFFmpeg { + return &fakeFFmpeg{Reader: strings.NewReader(data)} +} + type fakeFFmpeg struct { - Data string - r io.Reader - closed bool + io.Reader + lock sync.Mutex + closed atomic.Bool } func (ff *fakeFFmpeg) Start(ctx context.Context, cmd, path string, maxBitRate int) (f io.ReadCloser, err error) { - ff.r = strings.NewReader(ff.Data) return ff, nil } func (ff *fakeFFmpeg) Read(p []byte) (n int, err error) { - return ff.r.Read(p) + ff.lock.Lock() + defer ff.lock.Unlock() + return ff.Reader.Read(p) } func (ff *fakeFFmpeg) Close() error { - ff.closed = true + ff.closed.Store(true) return nil } + +func (ff *fakeFFmpeg) IsClosed() bool { + return ff.closed.Load() +} diff --git a/scanner/walk_dir_tree_test.go b/scanner/walk_dir_tree_test.go index 5b0079e7..4a226dc7 100644 --- a/scanner/walk_dir_tree_test.go +++ b/scanner/walk_dir_tree_test.go @@ -19,9 +19,9 @@ var _ = Describe("walk_dir_tree", func() { It("reads all info correctly", func() { var collected = dirMap{} results := make(walkResults, 5000) - var err error + var errC = make(chan error) go func() { - err = walkDirTree(context.TODO(), baseDir, results) + errC <- walkDirTree(context.Background(), baseDir, results) }() for { @@ -32,7 +32,7 @@ var _ = Describe("walk_dir_tree", func() { collected[stats.Path] = stats } - Expect(err).To(BeNil()) + Eventually(errC).Should(Receive(nil)) Expect(collected[baseDir]).To(MatchFields(IgnoreExtras, Fields{ "HasImages": BeTrue(), "HasPlaylist": BeFalse(),