235 lines
6.6 KiB
Go
235 lines
6.6 KiB
Go
package media_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
|
|
"git.netflux.io/rob/clipper/config"
|
|
"git.netflux.io/rob/clipper/generated/mocks"
|
|
"git.netflux.io/rob/clipper/generated/store"
|
|
"git.netflux.io/rob/clipper/media"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestGetAudioSegment(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
fixturePath string
|
|
fixtureLen int64
|
|
startFrame, endFrame int64
|
|
channels int32
|
|
numBins int
|
|
wantPeaks []int16
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "entire fixture, stereo, 1 bin",
|
|
fixturePath: "testdata/tone-44100-stereo-int16.raw",
|
|
fixtureLen: 176400,
|
|
startFrame: 0,
|
|
endFrame: 44100,
|
|
channels: 2,
|
|
numBins: 1,
|
|
wantPeaks: []int16{32747, 32747},
|
|
},
|
|
{
|
|
name: "entire fixture, stereo, 4 bins",
|
|
fixturePath: "testdata/tone-44100-stereo-int16.raw",
|
|
fixtureLen: 176400,
|
|
startFrame: 0,
|
|
endFrame: 44100,
|
|
channels: 2,
|
|
numBins: 4,
|
|
wantPeaks: []int16{8173, 8177, 16366, 16370, 24557, 24555, 32747, 32747},
|
|
},
|
|
{
|
|
name: "entire fixture, stereo, 16 bins",
|
|
fixturePath: "testdata/tone-44100-stereo-int16.raw",
|
|
fixtureLen: 176400,
|
|
startFrame: 0,
|
|
endFrame: 44100,
|
|
channels: 2,
|
|
numBins: 16,
|
|
wantPeaks: []int16{2029, 2029, 4075, 4076, 6124, 6125, 8173, 8177, 10222, 10221, 12267, 12265, 14314, 14313, 16366, 16370, 18413, 18411, 20453, 20454, 22505, 22508, 24557, 24555, 26604, 26605, 28644, 28643, 30698, 30694, 32747, 32747},
|
|
},
|
|
{
|
|
name: "entire fixture, mono, 1 bin",
|
|
fixturePath: "testdata/tone-44100-mono-int16.raw",
|
|
fixtureLen: 88200,
|
|
startFrame: 0,
|
|
endFrame: 44100,
|
|
channels: 1,
|
|
numBins: 1,
|
|
wantPeaks: []int16{32748},
|
|
},
|
|
{
|
|
name: "entire fixture, mono, 32 bins",
|
|
fixturePath: "testdata/tone-44100-mono-int16.raw",
|
|
fixtureLen: 88200,
|
|
startFrame: 0,
|
|
endFrame: 44100,
|
|
channels: 1,
|
|
numBins: 32,
|
|
wantPeaks: []int16{1026, 2030, 3071, 4075, 5122, 6126, 7167, 8172, 9213, 10217, 11259, 12264, 13311, 14315, 15360, 16364, 17405, 18412, 19450, 20453, 21497, 22504, 23549, 24554, 25599, 26607, 27641, 28642, 29688, 30738, 31746, 32748},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
expectedBytes := (tc.endFrame - tc.startFrame) * int64(tc.channels) * media.SizeOfInt16
|
|
|
|
audioFile, err := os.Open(tc.fixturePath)
|
|
require.NoError(t, err)
|
|
defer audioFile.Close()
|
|
audioData := io.NopCloser(io.LimitReader(audioFile, int64(expectedBytes)))
|
|
|
|
mediaSetID := uuid.New()
|
|
mediaSet := store.MediaSet{ID: mediaSetID, AudioChannels: tc.channels}
|
|
|
|
// store is passed the mediaSetID and returns a mediaSet
|
|
store := &mocks.Store{}
|
|
store.On("GetMediaSet", mock.Anything, mediaSetID).Return(mediaSet, nil)
|
|
defer store.AssertExpectations(t)
|
|
|
|
// S3 is passed the expected byte range, and returns an io.Reader
|
|
s3Client := &mocks.S3Client{}
|
|
s3Client.On("GetObject", mock.Anything, mock.MatchedBy(func(input *s3.GetObjectInput) bool {
|
|
return *input.Range == fmt.Sprintf("bytes=0-%d", expectedBytes)
|
|
})).Return(&s3.GetObjectOutput{Body: audioData, ContentLength: tc.fixtureLen}, nil)
|
|
defer s3Client.AssertExpectations(t)
|
|
s3API := media.S3API{S3Client: s3Client, S3PresignClient: &mocks.S3PresignClient{}}
|
|
|
|
service := media.NewMediaSetService(store, nil, s3API, config.Config{}, zap.NewNop())
|
|
peaks, err := service.GetAudioSegment(context.Background(), mediaSetID, tc.startFrame, tc.endFrame, tc.numBins)
|
|
|
|
if tc.wantErr == "" {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, tc.wantPeaks, peaks)
|
|
} else {
|
|
assert.EqualError(t, err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkGetAudioSegment(b *testing.B) {
|
|
const (
|
|
startFrame = 0
|
|
endFrame = 1323000
|
|
channels = 2
|
|
fixturePath = "testdata/tone-44100-stereo-int16-30000ms.raw"
|
|
fixtureLen = 5292000
|
|
numBins = 2000
|
|
)
|
|
|
|
for n := 0; n < b.N; n++ {
|
|
b.StopTimer()
|
|
|
|
expectedBytes := (endFrame - startFrame) * int64(channels) * media.SizeOfInt16
|
|
|
|
audioFile, err := os.Open(fixturePath)
|
|
require.NoError(b, err)
|
|
audioData := io.NopCloser(io.LimitReader(audioFile, int64(expectedBytes)))
|
|
|
|
mediaSetID := uuid.New()
|
|
mediaSet := store.MediaSet{ID: mediaSetID, AudioChannels: channels}
|
|
|
|
store := &mocks.Store{}
|
|
store.On("GetMediaSet", mock.Anything, mediaSetID).Return(mediaSet, nil)
|
|
|
|
s3Client := &mocks.S3Client{}
|
|
s3Client.
|
|
On("GetObject", mock.Anything, mock.Anything).
|
|
Return(&s3.GetObjectOutput{Body: audioData, ContentLength: fixtureLen}, nil)
|
|
s3API := media.S3API{S3Client: s3Client, S3PresignClient: &mocks.S3PresignClient{}}
|
|
|
|
service := media.NewMediaSetService(store, nil, s3API, config.Config{}, zap.NewNop())
|
|
|
|
b.StartTimer()
|
|
_, err = service.GetAudioSegment(context.Background(), mediaSetID, startFrame, endFrame, numBins)
|
|
b.StopTimer()
|
|
|
|
require.NoError(b, err)
|
|
audioFile.Close()
|
|
}
|
|
}
|
|
|
|
type testReader struct {
|
|
count int
|
|
data [][]byte
|
|
}
|
|
|
|
func (r *testReader) Read(p []byte) (int, error) {
|
|
if r.count == len(r.data) {
|
|
return 0, io.EOF
|
|
}
|
|
n := copy(p, r.data[r.count])
|
|
r.count++
|
|
return n, nil
|
|
}
|
|
|
|
func TestModuloBufReader(t *testing.T) {
|
|
reader := testReader{
|
|
data: [][]byte{
|
|
{'a', 'b', 'c', 'd'},
|
|
{'e', 'f', 'g', 'h', 'i'},
|
|
{},
|
|
{'j', 'k', 'l', 'm'},
|
|
{'n', 'o', 'p'},
|
|
{'q', 'r', 's', 't', 'u'},
|
|
},
|
|
}
|
|
|
|
modReader := media.NewModuloBufReader(io.NopCloser(&reader), 4)
|
|
|
|
out := make([]byte, 5)
|
|
|
|
n, err := modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 4, n)
|
|
assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 4, n)
|
|
assert.Equal(t, []byte{'e', 'f', 'g', 'h'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.Zero(t, n)
|
|
assert.Empty(t, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 4, n)
|
|
assert.Equal(t, []byte{'i', 'j', 'k', 'l'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 4, n)
|
|
assert.Equal(t, []byte{'m', 'n', 'o', 'p'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 4, n)
|
|
assert.Equal(t, []byte{'q', 'r', 's', 't'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.Equal(t, io.EOF, err)
|
|
assert.Equal(t, 1, n)
|
|
assert.Equal(t, []byte{'u'}, out[:n])
|
|
|
|
n, err = modReader.Read(out)
|
|
assert.Zero(t, n)
|
|
assert.Equal(t, io.EOF, err)
|
|
}
|