diff --git a/README.md b/README.md index 432e95f..9ff884e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # imagorvideo [![Test Status](https://github.com/cshum/imagorvideo/workflows/test/badge.svg)](https://github.com/cshum/imagorvideo/actions/workflows/test.yml) +[![Coverage Status](https://img.shields.io/coveralls/github/cshum/imagorvideo)](https://coveralls.io/github/cshum/imagorvideo?branch=master) [![Docker Hub](https://img.shields.io/badge/docker-shumc/imagorvideo-blue.svg)](https://hub.docker.com/r/shumc/imagorvideo/) [![GitHub Container Registry](https://ghcr-badge.herokuapp.com/cshum/imagorvideo/latest_tag?trim=major&label=ghcr.io&ignore=next,master&color=%23007ec6)](https://github.com/cshum/imagorvideo/pkgs/container/imagorvideo) diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..c326014 --- /dev/null +++ b/config_test.go @@ -0,0 +1,17 @@ +package imagorvideo + +import ( + "github.com/cshum/imagor" + "github.com/cshum/imagor/config" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestConfig(t *testing.T) { + srv := config.CreateServer([]string{ + "-ffmpeg-fallback-image", "https://foo.com/bar.jpg", + }, Config) + app := srv.App.(*imagor.Imagor) + processor := app.Processors[0].(*Processor) + assert.Equal(t, "https://foo.com/bar.jpg", processor.FallbackImage) +} diff --git a/ffmpeg/errors_test.go b/ffmpeg/errors_test.go new file mode 100644 index 0000000..3b0d483 --- /dev/null +++ b/ffmpeg/errors_test.go @@ -0,0 +1,15 @@ +package ffmpeg + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestErrors(t *testing.T) { + assert.Equal(t, "ffmpeg: cannot allocate memory", ErrNoMem.Error()) + assert.Equal(t, "ffmpeg: end of file", ErrEOF.Error()) + assert.Equal(t, "ffmpeg: unknown error occurred", ErrUnknown.Error()) + assert.Equal(t, "ffmpeg: decoder not found", ErrDecoderNotFound.Error()) + assert.Equal(t, "ffmpeg: invalid data found when processing input", ErrInvalidData.Error()) + assert.Equal(t, "ffmpeg: video or cover art size exceeds maximum allowed dimensions", ErrTooBig.Error()) +} diff --git a/ffmpeg/ffmpeg_test.go b/ffmpeg/ffmpeg_test.go index 6e6a711..e3a52af 100644 --- a/ffmpeg/ffmpeg_test.go +++ b/ffmpeg/ffmpeg_test.go @@ -26,7 +26,7 @@ var files = []string{ var baseDir = "../testdata/" -func TestAVContextMeta(t *testing.T) { +func TestAVContext(t *testing.T) { vips.Startup(nil) SetFFmpegLogLevel(AVLogDebug) logger := zap.NewExample() @@ -43,7 +43,7 @@ func TestAVContextMeta(t *testing.T) { } }) require.NoError(t, os.MkdirAll(baseDir+"golden/meta", 0755)) - require.NoError(t, os.MkdirAll(baseDir+"golden/result", 0755)) + require.NoError(t, os.MkdirAll(baseDir+"golden/export", 0755)) t.Parallel() for _, filename := range files { t.Run(filename, func(t *testing.T) { @@ -87,12 +87,3 @@ func TestAVContextMeta(t *testing.T) { }) } } - -func TestErrors(t *testing.T) { - assert.Equal(t, "ffmpeg: cannot allocate memory", ErrNoMem.Error()) - assert.Equal(t, "ffmpeg: end of file", ErrEOF.Error()) - assert.Equal(t, "ffmpeg: unknown error occurred", ErrUnknown.Error()) - assert.Equal(t, "ffmpeg: decoder not found", ErrDecoderNotFound.Error()) - assert.Equal(t, "ffmpeg: invalid data found when processing input", ErrInvalidData.Error()) - assert.Equal(t, "ffmpeg: video or cover art size exceeds maximum allowed dimensions", ErrTooBig.Error()) -} diff --git a/processor.go b/processor.go index a6edbc0..465de19 100644 --- a/processor.go +++ b/processor.go @@ -57,9 +57,6 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp if err == nil || out != nil { return } - if _, ok := err.(imagor.ErrForward); ok { - return - } // fallback image on error out = imagor.NewBlobFromBytes(transPixel) if p.FallbackImage != "" { @@ -85,8 +82,15 @@ func (p *Processor) Process(ctx context.Context, in *imagor.Blob, params imagorp if reader, size, err = in.NewReader(); err != nil { return } + if size <= 0 { + // force read full file if size unknown + _ = reader.Close() + reader = nil + } default: - if reader, size, err = in.NewReadSeeker(); err != nil { + } + if reader == nil { + if reader, size, err = in.NewReadSeeker(); err != nil || size <= 0 { // write to temp file if read seeker not available if reader, _, err = in.NewReader(); err != nil { return diff --git a/processor_test.go b/processor_test.go new file mode 100644 index 0000000..f8a4316 --- /dev/null +++ b/processor_test.go @@ -0,0 +1,121 @@ +package imagorvideo + +import ( + "context" + "fmt" + "github.com/cshum/imagor" + "github.com/cshum/imagor/imagorpath" + "github.com/cshum/imagor/storage/filestorage" + "github.com/cshum/imagor/vips" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "reflect" + "runtime" + "testing" +) + +var testDataDir string + +func init() { + _, b, _, _ := runtime.Caller(0) + testDataDir = filepath.Join(filepath.Dir(b), "./testdata") +} + +type test struct { + name string + path string +} + +func TestProcessor(t *testing.T) { + v := vips.NewProcessor(vips.WithDebug(true)) + require.NoError(t, v.Startup(context.Background())) + t.Cleanup(func() { + require.NoError(t, v.Shutdown(context.Background())) + }) + var resultDir = filepath.Join(testDataDir, "golden/result") + doGoldenTests(t, resultDir, []test{ + {name: "mkv", path: "fit-in/100x100/everybody-betray-me.mkv"}, + {name: "mkv meta", path: "meta/everybody-betray-me.mkv"}, + {name: "mp4", path: "fit-in/100x100/schizo_0.mp4"}, + {name: "mp4 90", path: "fit-in/100x100/schizo_90.mp4"}, + {name: "mp4 180", path: "fit-in/100x100/schizo_180.mp4"}, + {name: "mp4 270", path: "fit-in/100x100/schizo_270.mp4"}, + {name: "image", path: "fit-in/100x100/demo.png"}, + }, WithDebug(true)) +} + +func doGoldenTests(t *testing.T, resultDir string, tests []test, opts ...Option) { + resStorage := filestorage.New(resultDir, + filestorage.WithSaveErrIfExists(true)) + loader := filestorage.New(testDataDir) + loaders := []imagor.Loader{ + loader, + loaderFunc(func(r *http.Request, image string) (blob *imagor.Blob, err error) { + image, _ = loader.Path(image) + return imagor.NewBlob(func() (reader io.ReadCloser, size int64, err error) { + // force read full file by 0 size + reader, err = os.Open(image) + return + }), nil + }), + } + for i, loader := range loaders { + app := imagor.New( + imagor.WithLoaders(loader), + imagor.WithUnsafe(true), + imagor.WithDebug(true), + imagor.WithLogger(zap.NewExample()), + imagor.WithProcessors(NewProcessor(opts...), vips.NewProcessor()), + ) + require.NoError(t, app.Startup(context.Background())) + t.Cleanup(func() { + assert.NoError(t, app.Shutdown(context.Background())) + }) + for _, tt := range tests { + t.Run(fmt.Sprintf("%s-%d", tt.name, i+1), func(t *testing.T) { + w := httptest.NewRecorder() + ctx, cancel := context.WithCancel(context.Background()) + req := httptest.NewRequest( + http.MethodGet, fmt.Sprintf("/unsafe/%s", tt.path), nil).WithContext(ctx) + app.ServeHTTP(w, req) + cancel() + assert.Equal(t, 200, w.Code) + b := imagor.NewBlobFromBytes(w.Body.Bytes()) + _ = resStorage.Put(context.Background(), tt.path, b) + path := filepath.Join(resultDir, imagorpath.Normalize(tt.path, nil)) + + bc := imagor.NewBlobFromFile(path) + buf, err := bc.ReadAll() + require.NoError(t, err) + if reflect.DeepEqual(buf, w.Body.Bytes()) { + return + } + img1, err := vips.LoadImageFromFile(path, nil) + require.NoError(t, err) + img2, err := vips.LoadImageFromBuffer(w.Body.Bytes(), nil) + require.NoError(t, err) + require.Equal(t, img1.Width(), img2.Width(), "width mismatch") + require.Equal(t, img1.Height(), img2.Height(), "height mismatch") + buf1, err := img1.ExportWebp(nil) + require.NoError(t, err) + buf2, err := img2.ExportWebp(nil) + require.NoError(t, err) + require.True(t, reflect.DeepEqual(buf1, buf2), "image mismatch") + }) + } + + } + +} + +type loaderFunc func(r *http.Request, image string) (blob *imagor.Blob, err error) + +func (f loaderFunc) Get(r *http.Request, image string) (*imagor.Blob, error) { + return f(r, image) +} diff --git a/testdata/golden/result/fit-in/100x100/demo.png b/testdata/golden/result/fit-in/100x100/demo.png new file mode 100644 index 0000000..a22b015 Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/demo.png differ diff --git a/testdata/golden/result/fit-in/100x100/everybody-betray-me.mkv b/testdata/golden/result/fit-in/100x100/everybody-betray-me.mkv new file mode 100644 index 0000000..d7eef4c Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/everybody-betray-me.mkv differ diff --git a/testdata/golden/result/fit-in/100x100/schizo_0.mp4 b/testdata/golden/result/fit-in/100x100/schizo_0.mp4 new file mode 100644 index 0000000..30a6269 Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/schizo_0.mp4 differ diff --git a/testdata/golden/result/fit-in/100x100/schizo_180.mp4 b/testdata/golden/result/fit-in/100x100/schizo_180.mp4 new file mode 100644 index 0000000..79613a9 Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/schizo_180.mp4 differ diff --git a/testdata/golden/result/fit-in/100x100/schizo_270.mp4 b/testdata/golden/result/fit-in/100x100/schizo_270.mp4 new file mode 100644 index 0000000..5595748 Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/schizo_270.mp4 differ diff --git a/testdata/golden/result/fit-in/100x100/schizo_90.mp4 b/testdata/golden/result/fit-in/100x100/schizo_90.mp4 new file mode 100644 index 0000000..09676d2 Binary files /dev/null and b/testdata/golden/result/fit-in/100x100/schizo_90.mp4 differ diff --git a/testdata/golden/result/meta/everybody-betray-me.mkv b/testdata/golden/result/meta/everybody-betray-me.mkv new file mode 100644 index 0000000..d64a578 --- /dev/null +++ b/testdata/golden/result/meta/everybody-betray-me.mkv @@ -0,0 +1 @@ +{"orientation":1,"duration":7407000000,"width":640,"height":480,"has_video":true,"has_audio":true,"has_alpha":false} \ No newline at end of file