Skip to content

Commit

Permalink
Replace panics with errors in Loop2
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkKremer committed Sep 5, 2024
1 parent dc80e9a commit 8f9a3f1
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 17 deletions.
12 changes: 7 additions & 5 deletions compositors.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package beep
import (
"fmt"
"math"

"github.com/pkg/errors"
)

// Take returns a Streamer which streams at most num samples from s.
Expand Down Expand Up @@ -137,7 +139,7 @@ func LoopBetween(start, end int) LoopOption {
// are played once before and after the looping section, respectively.
//
// The returned Streamer propagates any errors from s.
func Loop2(s StreamSeeker, opts ...LoopOption) Streamer {
func Loop2(s StreamSeeker, opts ...LoopOption) (Streamer, error) {
l := &loop2{
s: s,
remains: -1, // indefinitely
Expand All @@ -150,14 +152,14 @@ func Loop2(s StreamSeeker, opts ...LoopOption) Streamer {

n := s.Len()
if l.start >= n {
panic(fmt.Sprintf("invalid argument to Loop; start position %d is bigger than the length %d of the source streamer", l.start, n))
return nil, errors.New(fmt.Sprintf("invalid argument to Loop2; start position %d must be smaller than the source streamer length %d", l.start, n))
}
if l.start > l.end {
panic(fmt.Sprintf("invalid argument to Loop; start position %d must be smaller than the end position %d", l.start, l.end))
if l.start >= l.end {
return nil, errors.New(fmt.Sprintf("invalid argument to Loop2; start position %d must be smaller than the end position %d", l.start, l.end))
}
l.end = min(l.end, n)

return l
return l, nil
}

type loop2 struct {
Expand Down
53 changes: 41 additions & 12 deletions compositors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,49 +46,76 @@ func TestLoop(t *testing.T) {
}

func TestLoop2(t *testing.T) {
// Loop indefinitely (no options).
// LoopStart is bigger than s.Len()
s, _ := testtools.NewSequentialDataStreamer(5)
got := testtools.CollectNum(16, beep.Loop2(s))
l, err := beep.Loop2(s, beep.LoopStart(5))
assert.EqualError(t, err, "invalid argument to Loop2; start position 5 must be smaller than the source streamer length 5")

// LoopStart is bigger than LoopEnd
s, _ = testtools.NewSequentialDataStreamer(5)
l, err = beep.Loop2(s, beep.LoopBetween(4, 4))
assert.EqualError(t, err, "invalid argument to Loop2; start position 4 must be smaller than the end position 4")

// Loop indefinitely (no options).
s, _ = testtools.NewSequentialDataStreamer(5)
l, err = beep.Loop2(s)
assert.NoError(t, err)
got := testtools.CollectNum(16, l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}}, got)

// Test no loop.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(0)))
l, err = beep.Loop2(s, beep.LoopTimes(0))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}, got)

// Test loop once.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(1)))
l, err = beep.Loop2(s, beep.LoopTimes(1))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}, got)

// Test loop twice.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(2)))
l, err = beep.Loop2(s, beep.LoopTimes(2))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}, got)

// Test loop from start position.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(2), beep.LoopStart(2)))
l, err = beep.Loop2(s, beep.LoopTimes(2), beep.LoopStart(2))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {2, 2}, {3, 3}, {4, 4}, {2, 2}, {3, 3}, {4, 4}}, got)

// Test loop with end position.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(2), beep.LoopEnd(4)))
l, err = beep.Loop2(s, beep.LoopTimes(2), beep.LoopEnd(4))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}}, got)

// Test loop with start and end position.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.Collect(beep.Loop2(s, beep.LoopTimes(2), beep.LoopBetween(2, 4)))
l, err = beep.Loop2(s, beep.LoopTimes(2), beep.LoopBetween(2, 4))
assert.NoError(t, err)
got = testtools.Collect(l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {2, 2}, {3, 3}, {2, 2}, {3, 3}, {4, 4}}, got)

// Loop indefinitely with both start and end position.
s, _ = testtools.NewSequentialDataStreamer(5)
got = testtools.CollectNum(10, beep.Loop2(s, beep.LoopBetween(2, 4)))
l, err = beep.Loop2(s, beep.LoopBetween(2, 4))
assert.NoError(t, err)
got = testtools.CollectNum(10, l)
assert.Equal(t, [][2]float64{{0, 0}, {1, 1}, {2, 2}, {3, 3}, {2, 2}, {3, 3}, {2, 2}, {3, 3}, {2, 2}, {3, 3}}, got)

//// Test streaming from the middle of the loops.
s, _ = testtools.NewSequentialDataStreamer(5)
l := beep.Loop2(s, beep.LoopTimes(2), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, 2, 3, 2, 3
l, err = beep.Loop2(s, beep.LoopTimes(2), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, 2, 3, 2, 3
assert.NoError(t, err)
// First stream to the middle of a loop.
buf := make([][2]float64, 3)
if n, ok := l.Stream(buf); n != 3 || !ok {
Expand All @@ -105,7 +132,8 @@ func TestLoop2(t *testing.T) {
expectedErr := errors.New("expected error")
s, _ = testtools.NewSequentialDataStreamer(5)
s = testtools.NewDelayedErrorStreamer(s, 5, expectedErr)
l = beep.Loop2(s, beep.LoopTimes(3), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, 2, 3, 2, 3
l, err = beep.Loop2(s, beep.LoopTimes(3), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, 2, 3, 2, 3
assert.NoError(t, err)
buf = make([][2]float64, 10)
if n, ok := l.Stream(buf); n != 5 || !ok {
t.Fatalf("want n %d got %d, want ok %t got %t", 5, n, true, ok)
Expand All @@ -120,7 +148,8 @@ func TestLoop2(t *testing.T) {
// Test error handling during call to Seek().
s, _ = testtools.NewSequentialDataStreamer(5)
s = testtools.NewSeekErrorStreamer(s, expectedErr)
l = beep.Loop2(s, beep.LoopTimes(3), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, [error]
l, err = beep.Loop2(s, beep.LoopTimes(3), beep.LoopBetween(2, 4)) // 0, 1, 2, 3, [error]
assert.NoError(t, err)
buf = make([][2]float64, 10)
if n, ok := l.Stream(buf); n != 4 || !ok {
t.Fatalf("want n %d got %d, want ok %t got %t", 4, n, true, ok)
Expand Down

0 comments on commit 8f9a3f1

Please sign in to comment.