From 8fc7f2f17f45ac160767cdc1a59010a49420c491 Mon Sep 17 00:00:00 2001 From: Navi Arimeka Date: Sun, 15 Mar 2020 11:00:48 +0300 Subject: [PATCH 1/4] Parse remote files --- benchmark_test.go | 10 ++--- ffprobe_test.go | 25 ++++++++---- http.go | 31 ++++++++++++++ http_test.go | 40 +++++++++++++++++++ image.go | 51 ++++++++++++++++++----- image_test.go | 100 +++++++++++++++++++++++++++++++++++++++++----- info.go | 53 +++++++++++++++++++++++- info_test.go | 57 +++++++++++++++++++++++++- mime.go | 9 ++++- mime_test.go | 42 ++++++++++++++++++- probe_test.go | 55 +++++++++++++++++++------ 11 files changed, 424 insertions(+), 49 deletions(-) create mode 100644 http.go create mode 100644 http_test.go diff --git a/benchmark_test.go b/benchmark_test.go index 598bcea..1524c54 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -13,20 +13,20 @@ const ( func BenchmarkParse(b *testing.B) { for i := 0; i < b.N; i++ { - mediaprobe.Parse(benchmarkValidVideo) + _, _ = mediaprobe.Parse(benchmarkValidVideo) } } func BenchmarkNew(b *testing.B) { for i := 0; i < b.N; i++ { - mediaprobe.New(benchmarkValidVideo) + _, _ = mediaprobe.New(benchmarkValidVideo) } } func BenchmarkInfo_CalculateMime(b *testing.B) { info, _ := mediaprobe.New(benchmarkValidVideo) for i := 0; i < b.N; i++ { - info.CalculateMime() + _ = info.CalculateMime() } } @@ -34,7 +34,7 @@ func BenchmarkInfo_FFProbe(b *testing.B) { info, _ := mediaprobe.New(benchmarkValidVideo) b.ResetTimer() for i := 0; i < b.N; i++ { - info.FFProbe() + _ = info.FFProbe() } } @@ -42,6 +42,6 @@ func BenchmarkInfo_ParseImage(b *testing.B) { info, _ := mediaprobe.New(benchmarkValidImage) b.ResetTimer() for i := 0; i < b.N; i++ { - info.FFProbe() + _ = info.FFProbe() } } diff --git a/ffprobe_test.go b/ffprobe_test.go index a48d9e8..3c40c45 100644 --- a/ffprobe_test.go +++ b/ffprobe_test.go @@ -7,18 +7,29 @@ import ( ) var bitrateFiles = map[string]int64{ - "./fixtures/video.mp4": 551193, - "./fixtures/with-meta.mov": 481140, - "./fixtures/audio.mp3": 128000, - "./fixtures/not-a-video.mp4": 0, - "./fixtures/corrupted.mp4": 0, + "http://localhost:9090/video.mp4": 551193, + "./fixtures/video.mp4": 551193, + "./fixtures/with-meta.mov": 481140, + "./fixtures/audio.mp3": 128000, + "./fixtures/not-a-video.mp4": 0, + "./fixtures/corrupted.mp4": 0, } func TestInfo_FFProbe(t *testing.T) { + handler := &Handler{ + Status: 0, + Filename: "./fixtures/video.mp4", + } + srv := ServeHttp(handler) + defer srv.Stop() + for filename, expectedBitrate := range bitrateFiles { - info, _ := mediaprobe.New(filename) + info, err := mediaprobe.New(filename) + if err != nil { + t.Fatalf("Filename: %s. Unexpected error %v", filename, err) + } - err := info.FFProbe() + err = info.FFProbe() if err != nil { if filename != "./fixtures/corrupted.mp4" { t.Errorf("Filename: %s. Unexpected error %v", filename, err) diff --git a/http.go b/http.go new file mode 100644 index 0000000..fb796aa --- /dev/null +++ b/http.go @@ -0,0 +1,31 @@ +package mediaprobe + +import ( + "fmt" + "io" + "net/http" + "net/url" + "time" +) + +var httpClient = &http.Client{ + Timeout: 5 * time.Second, +} + +func getRemoteFile(uri *url.URL) (io.ReadCloser, error) { + req := &http.Request{ + Method: http.MethodGet, + URL: uri, + } + resp, err := httpClient.Do(req) + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode) + } + + return resp.Body, nil +} diff --git a/http_test.go b/http_test.go new file mode 100644 index 0000000..8190963 --- /dev/null +++ b/http_test.go @@ -0,0 +1,40 @@ +package mediaprobe_test + +import ( + "context" + "net/http" +) + +type Server struct { + srv *http.Server +} + +func (s *Server) Stop() { + _ = s.srv.Shutdown(context.Background()) +} + +func ServeHttp(handler *Handler) *Server { + srv := &http.Server{ + Addr: ":9090", + Handler: handler, + } + go func(s *http.Server) { + _ = s.ListenAndServe() + }(srv) + + return &Server{srv: srv} +} + +type Handler struct { + Status int + Filename string +} + +func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if h.Status != 0 { + rw.WriteHeader(h.Status) + return + } + + http.ServeFile(rw, req, h.Filename) +} diff --git a/image.go b/image.go index 1938e3e..7c7e5e2 100644 --- a/image.go +++ b/image.go @@ -4,7 +4,6 @@ import ( "image" "io" "os" - // Expanding image package _ "golang.org/x/image/bmp" _ "golang.org/x/image/tiff" @@ -18,17 +17,46 @@ import ( // ParseImage used for retrieve image data func (info *Info) ParseImage() error { - file, err := os.Open(info.filename) + if info.data == nil { + file, err := os.Open(info.filename) + if err != nil { + return err + } + defer file.Close() + if err := info.decodeImage(file); err != nil { + return err + } + _, err = file.Seek(0, io.SeekStart) + if err != nil { + return err + } + info.decodeExif(file) + + return nil + } + + body, err := getRemoteFile(info.uri) + if err != nil { + return err + } + err = info.decodeImage(body) + body.Close() if err != nil { return err } - defer file.Close() - img, _, err := image.DecodeConfig(file) + body, err = getRemoteFile(info.uri) if err != nil { return err } - _, err = file.Seek(0, io.SeekStart) + info.decodeExif(body) + body.Close() + + return nil +} + +func (info *Info) decodeImage(reader io.Reader) error { + img, _, err := image.DecodeConfig(reader) if err != nil { return err } @@ -36,15 +64,18 @@ func (info *Info) ParseImage() error { info.Width = img.Width info.Height = img.Height - if x, err := exif.Decode(file); err == nil { + return nil +} + +func (info *Info) decodeExif(reader io.Reader) { + if x, err := exif.Decode(reader); err == nil { if orientationTag, err := x.Get(exif.Orientation); err == nil { switch orientationTag.String() { case "5", "6", "7", "8": - info.Width = img.Height - info.Height = img.Width + w, h := info.Width, info.Height + info.Width = h + info.Height = w } } } - - return nil } diff --git a/image_test.go b/image_test.go index da24b78..8eece01 100644 --- a/image_test.go +++ b/image_test.go @@ -1,12 +1,14 @@ package mediaprobe_test import ( + "net/http" "testing" "github.com/Arimeka/mediaprobe" ) const ( + testImageValidRemoteImage = "http://localhost:9090/image.jpeg" testImageValidImage = "./fixtures/image.jpeg" testImageWithExifOrientation = "./fixtures/left.jpg" testImageInvalidImage = "./fixtures/not-an-image.jpeg" @@ -14,7 +16,19 @@ const ( testImagePNGWithoutExifImage = "./fixtures/without-exif.png" ) -func TestInfo_ParseImageNotFound(t *testing.T) { +func TestInfo_ParseImage(t *testing.T) { + t.Run("not_found", parseImageNotFound) + t.Run("not_found_remote", parseImageNotFoundRemote) + t.Run("conn_error_remote", parseImageConnectionErrorRemote) + t.Run("valid", parseImageValid) + t.Run("valid_remote", parseImageValidRemote) + t.Run("invalid", parseImageInvalid) + t.Run("with_orientation", parseImageWithOrientation) + t.Run("jpeg_without_exif", parseImageJPEGWithoutExif) + t.Run("png_without_exif", parseImagePNGWithoutExif) +} + +func parseImageNotFound(t *testing.T) { info := &mediaprobe.Info{} err := info.ParseImage() if err == nil { @@ -22,14 +36,48 @@ func TestInfo_ParseImageNotFound(t *testing.T) { } } -func TestInfo_ParseImage(t *testing.T) { - info, _ := mediaprobe.New(testImageInvalidImage) - err := info.ParseImage() +func parseImageNotFoundRemote(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/image.jpeg", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New(testImageValidRemoteImage) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + handler.Status = http.StatusNotFound + err = info.ParseImage() if err == nil { - t.Errorf("Filename: %s. Expected to return error but return nil", testImageInvalidImage) + t.Errorf("Expected to return error found but return nil") } +} + +func parseImageConnectionErrorRemote(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/image.jpeg", + } + srv := ServeHttp(handler) - info, _ = mediaprobe.New(testImageValidImage) + info, err := mediaprobe.New(testImageValidRemoteImage) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + srv.Stop() + err = info.ParseImage() + if err == nil { + t.Errorf("Expected to return error found but return nil") + } +} + +func parseImageValid(t *testing.T) { + info, err := mediaprobe.New(testImageValidImage) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } err = info.ParseImage() if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testImageValidImage, err) @@ -41,7 +89,41 @@ func TestInfo_ParseImage(t *testing.T) { } } -func TestInfo_ParseImageWithOrientation(t *testing.T) { +func parseImageValidRemote(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/image.jpeg", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New(testImageValidRemoteImage) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + err = info.ParseImage() + if err != nil { + t.Errorf("Filename: %s. Unexpected error %v", testImageValidImage, err) + } + + width := info.Width + if width != 290 { + t.Errorf("Filename: %s. Not expected width. Expected %d; got %d", testImageValidImage, 290, width) + } +} + +func parseImageInvalid(t *testing.T) { + info, err := mediaprobe.New(testImageInvalidImage) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + err = info.ParseImage() + if err == nil { + t.Errorf("Filename: %s. Expected to return error but return nil", testImageInvalidImage) + } +} + +func parseImageWithOrientation(t *testing.T) { info, _ := mediaprobe.New(testImageWithExifOrientation) err := info.ParseImage() if err != nil { @@ -54,7 +136,7 @@ func TestInfo_ParseImageWithOrientation(t *testing.T) { } } -func TestInfo_ParseImageJPEGWithoutExif(t *testing.T) { +func parseImageJPEGWithoutExif(t *testing.T) { info, _ := mediaprobe.New(testImageJPEGWithoutExifImage) err := info.ParseImage() if err != nil { @@ -67,7 +149,7 @@ func TestInfo_ParseImageJPEGWithoutExif(t *testing.T) { } } -func TestInfo_ParseImagePNGWithoutExif(t *testing.T) { +func parseImagePNGWithoutExif(t *testing.T) { info, _ := mediaprobe.New(testImagePNGWithoutExifImage) err := info.ParseImage() if err != nil { diff --git a/info.go b/info.go index 9d00b4d..1f043e9 100644 --- a/info.go +++ b/info.go @@ -1,12 +1,24 @@ package mediaprobe import ( + "fmt" + "net/http" + "net/url" "os" "time" ) -// New initialized Info using magic bytes for calculating media type +// New initialized Info and calculate file size func New(filename string) (*Info, error) { + uri, err := url.Parse(filename) + if err != nil || !uri.IsAbs() { + return openFile(filename) + } + + return openURL(filename, uri) +} + +func openFile(filename string) (*Info, error) { file, err := os.Open(filename) if err != nil { return nil, err @@ -27,9 +39,48 @@ func New(filename string) (*Info, error) { return info, nil } +func openURL(filename string, uri *url.URL) (*Info, error) { + headReq := &http.Request{ + Method: http.MethodHead, + URL: uri, + } + head, err := httpClient.Do(headReq) + if err != nil { + return nil, err + } + if head.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code %d", head.StatusCode) + } + head.Body.Close() + + body, err := getRemoteFile(uri) + if err != nil { + return nil, err + } + defer body.Close() + + info := &Info{ + data: make([]byte, 1024), + filename: filename, + uri: uri, + + Name: filename, + Size: head.ContentLength, + } + + _, err = body.Read(info.data) + if err != nil { + return nil, err + } + + return info, nil +} + // Info contains parsed information type Info struct { filename string + uri *url.URL + data []byte Name string MediaType string diff --git a/info_test.go b/info_test.go index f3c1603..dd1d754 100644 --- a/info_test.go +++ b/info_test.go @@ -1,14 +1,69 @@ package mediaprobe_test import ( + "net/http" "testing" "github.com/Arimeka/mediaprobe" ) -func TestNewNotFound(t *testing.T) { +func TestNew(t *testing.T) { + t.Run("not_found", newNotFound) + t.Run("not_found_remote", newNotFoundRemote) + t.Run("conn_error_remote", newConnErrorRemote) + t.Run("local", newLocalFile) + t.Run("remote", newRemoteFile) +} + +func newNotFound(t *testing.T) { _, err := mediaprobe.New("") if err == nil { t.Errorf("Expected to return error found but return nil") } } + +func newNotFoundRemote(t *testing.T) { + handler := &Handler{ + Status: http.StatusNotFound, + } + srv := ServeHttp(handler) + defer srv.Stop() + + _, err := mediaprobe.New("http://localhost:9090/not-exist") + if err == nil { + t.Errorf("Expected to return error found but return nil") + } +} + +func newConnErrorRemote(t *testing.T) { + _, err := mediaprobe.New("http://localhost:9091/not-exist") + if err == nil { + t.Errorf("Expected to return error found but return nil") + } +} + +func newLocalFile(t *testing.T) { + info, err := mediaprobe.New("./fixtures/video.mp4") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if info.Size != 383631 { + t.Errorf("Unexpected size. Expected %d, got %d", 383631, info.Size) + } +} + +func newRemoteFile(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/video.mp4", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New("http://localhost:9090/video.mp4") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if info.Size != 383631 { + t.Errorf("Unexpected size. Expected %d, got %d", 383631, info.Size) + } +} diff --git a/mime.go b/mime.go index cf4d768..4cca09b 100644 --- a/mime.go +++ b/mime.go @@ -8,13 +8,18 @@ import ( // CalculateMime calculates mime type by magic numbers // Function uses libmagic bindings using github.com/rakyll/magicmime package. -func (info *Info) CalculateMime() error { +func (info *Info) CalculateMime() (err error) { if err := magicmime.Open(magicmime.MAGIC_MIME_TYPE | magicmime.MAGIC_SYMLINK | magicmime.MAGIC_ERROR); err != nil { return err } defer magicmime.Close() - media, err := magicmime.TypeByFile(info.filename) + var media string + if info.data != nil { + media, err = magicmime.TypeByBuffer(info.data) + } else { + media, err = magicmime.TypeByFile(info.filename) + } if err != nil { return err } diff --git a/mime_test.go b/mime_test.go index 4c329d8..e068f2a 100644 --- a/mime_test.go +++ b/mime_test.go @@ -6,10 +6,50 @@ import ( "github.com/Arimeka/mediaprobe" ) -func TestInfo_CalculateMimeNotFound(t *testing.T) { +func TestInfo_CalculateMime(t *testing.T) { + t.Run("not_found", calculateMimeNotFound) + t.Run("local", calculateMimeLocal) + t.Run("remote", calculateMimeRemote) +} + +func calculateMimeNotFound(t *testing.T) { info := &mediaprobe.Info{} err := info.CalculateMime() if err == nil { t.Errorf("Expected to return error found but return nil") } } + +func calculateMimeLocal(t *testing.T) { + info, err := mediaprobe.New("./fixtures/not-an-image.jpeg") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + err = info.CalculateMime() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if info.MediaType != "video" { + t.Errorf("Unexpected media type. Expected %s, got %s", "video", info.MediaType) + } +} + +func calculateMimeRemote(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/not-an-image.jpeg", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New("http://localhost:9090/not-an-image.jpeg") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + err = info.CalculateMime() + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + if info.MediaType != "video" { + t.Errorf("Unexpected media type. Expected %s, got %s", "video", info.MediaType) + } +} diff --git a/probe_test.go b/probe_test.go index f09f6a5..8fc3315 100644 --- a/probe_test.go +++ b/probe_test.go @@ -7,22 +7,34 @@ import ( ) const ( - testProbeValidImage = "./fixtures/image.jpeg" - testProbeInvalidImage = "./fixtures/not-an-image.jpeg" - testProbeValidVideo = "./fixtures/video.mp4" - testProbeInvalidVideo = "./fixtures/not-a-video.mp4" - testProbeValidAudio = "./fixtures/audio.mp3" - testProbeCorruptedFile = "./fixtures/corrupted.mp4" + testProbeValidRemoteImage = "http://localhost:9090/image.jpeg" + testProbeValidImage = "./fixtures/image.jpeg" + testProbeInvalidImage = "./fixtures/not-an-image.jpeg" + testProbeValidVideo = "./fixtures/video.mp4" + testProbeInvalidVideo = "./fixtures/not-a-video.mp4" + testProbeValidAudio = "./fixtures/audio.mp3" + testProbeCorruptedFile = "./fixtures/corrupted.mp4" ) -func TestParseNotFound(t *testing.T) { +func TestParse(t *testing.T) { + t.Run("not_found", parseNotFound) + t.Run("valid_remote_image", parseValidRemoteImage) + t.Run("valid_image", parseValidImage) + t.Run("invalid_image", parseInvalidImage) + t.Run("valid_video", parseValidVideo) + t.Run("invalid_video", parseInvalidVideo) + t.Run("valid_audio", parseValidAudio) + t.Run("corrupted_file", parseCorruptedFile) +} + +func parseNotFound(t *testing.T) { _, err := mediaprobe.Parse("") if err == nil { t.Errorf("Expected to return error found but return nil") } } -func TestParseValidImage(t *testing.T) { +func parseValidImage(t *testing.T) { info, err := mediaprobe.Parse(testProbeValidImage) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeValidImage, err) @@ -33,7 +45,24 @@ func TestParseValidImage(t *testing.T) { } } -func TestParseInvalidImage(t *testing.T) { +func parseValidRemoteImage(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/image.jpeg", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.Parse(testProbeValidRemoteImage) + if err != nil { + t.Errorf("Filename: %s. Unexpected error %v", testProbeValidRemoteImage, err) + } + width := info.Width + if width != 290 { + t.Errorf("Filename: %s. Not expected width. Expected %d; got %d", testProbeValidRemoteImage, 290, width) + } +} + +func parseInvalidImage(t *testing.T) { info, err := mediaprobe.Parse(testProbeInvalidImage) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeInvalidImage, err) @@ -44,7 +73,7 @@ func TestParseInvalidImage(t *testing.T) { } } -func TestParseValidVideo(t *testing.T) { +func parseValidVideo(t *testing.T) { info, err := mediaprobe.Parse(testProbeValidVideo) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeValidVideo, err) @@ -55,7 +84,7 @@ func TestParseValidVideo(t *testing.T) { } } -func TestParseInvalidVideo(t *testing.T) { +func parseInvalidVideo(t *testing.T) { info, err := mediaprobe.Parse(testProbeInvalidVideo) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeInvalidVideo, err) @@ -66,7 +95,7 @@ func TestParseInvalidVideo(t *testing.T) { } } -func TestParseValidAudio(t *testing.T) { +func parseValidAudio(t *testing.T) { info, err := mediaprobe.Parse(testProbeValidAudio) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeValidAudio, err) @@ -77,7 +106,7 @@ func TestParseValidAudio(t *testing.T) { } } -func TestParseCorruptedFile(t *testing.T) { +func parseCorruptedFile(t *testing.T) { info, err := mediaprobe.Parse(testProbeCorruptedFile) if err != nil { t.Errorf("Filename: %s. Unexpected error %v", testProbeCorruptedFile, err) From 475ed959a1ff59511344400ff54bbc1749957fc9 Mon Sep 17 00:00:00 2001 From: Navi Arimeka Date: Sun, 15 Mar 2020 11:07:16 +0300 Subject: [PATCH 2/4] Change doc host to pkg.go.dev --- README.md | 2 +- info.go | 4 +++- probe.go | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa7b826..9d8bc89 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Build Status](https://travis-ci.org/Arimeka/mediaprobe.svg?branch=master)](https://travis-ci.org/Arimeka/mediaprobe) [![Coverage Status](https://coveralls.io/repos/github/Arimeka/mediaprobe/badge.svg?branch=master)](https://coveralls.io/github/Arimeka/mediaprobe?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/Arimeka/mediaprobe)](https://goreportcard.com/report/github.com/Arimeka/mediaprobe) -[![GoDoc](https://godoc.org/github.com/Arimeka/mediaprobe?status.svg)](https://godoc.org/github.com/Arimeka/mediaprobe) +[![GoDoc](https://godoc.org/github.com/Arimeka/mediaprobe?status.svg)](https://pkg.go.dev/github.com/Arimeka/mediaprobe) # mediaprobe diff --git a/info.go b/info.go index 1f043e9..177719b 100644 --- a/info.go +++ b/info.go @@ -8,7 +8,9 @@ import ( "time" ) -// New initialized Info and calculate file size +// New initialized Info and calculate file size. +// +// Accepted filename as local path or absolute url. func New(filename string) (*Info, error) { uri, err := url.Parse(filename) if err != nil || !uri.IsAbs() { diff --git a/probe.go b/probe.go index 9935e02..d81ea67 100644 --- a/probe.go +++ b/probe.go @@ -9,6 +9,8 @@ import ( // Parse file media data // It determines the file type by magic bytes, // and parses the media data of the video or image. +// +// Accepted filename as local path or absolute url. func Parse(filename string) (Info, error) { info, err := New(filename) if err != nil { From 2e949ae3e9d3786e1be88bddc04e9b551330a1cd Mon Sep 17 00:00:00 2001 From: Navi Arimeka Date: Sun, 15 Mar 2020 12:32:22 +0300 Subject: [PATCH 3/4] Different ports for test servers --- ffprobe_test.go | 42 ++++++++++++++++++++++++++++++------------ http_test.go | 22 ++++++++++++---------- image_test.go | 7 +++---- info_test.go | 4 ++-- mime_test.go | 2 +- probe_test.go | 6 +++--- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/ffprobe_test.go b/ffprobe_test.go index 3c40c45..4deda0a 100644 --- a/ffprobe_test.go +++ b/ffprobe_test.go @@ -7,21 +7,14 @@ import ( ) var bitrateFiles = map[string]int64{ - "http://localhost:9090/video.mp4": 551193, - "./fixtures/video.mp4": 551193, - "./fixtures/with-meta.mov": 481140, - "./fixtures/audio.mp3": 128000, - "./fixtures/not-a-video.mp4": 0, - "./fixtures/corrupted.mp4": 0, + "./fixtures/video.mp4": 551193, + "./fixtures/with-meta.mov": 481140, + "./fixtures/audio.mp3": 128000, + "./fixtures/not-a-video.mp4": 0, + "./fixtures/corrupted.mp4": 0, } func TestInfo_FFProbe(t *testing.T) { - handler := &Handler{ - Status: 0, - Filename: "./fixtures/video.mp4", - } - srv := ServeHttp(handler) - defer srv.Stop() for filename, expectedBitrate := range bitrateFiles { info, err := mediaprobe.New(filename) @@ -45,4 +38,29 @@ func TestInfo_FFProbe(t *testing.T) { t.Errorf("Filename: %s. Not expected video bitrate. Expected %d; got %d", filename, expectedBitrate, bitrate) } } + + t.Run("remote_file", ffprobeRemoteFile) +} + +func ffprobeRemoteFile(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/video.mp4", + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New(srv.Endpoint()) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + err = info.FFProbe() + if err != nil { + t.Errorf("Unexpected error %v", err) + } + + bitrate := info.BitRate + if bitrate != 551193 { + t.Errorf("Not expected video bitrate. Expected %d; got %d", 551193, bitrate) + } } diff --git a/http_test.go b/http_test.go index 8190963..cbb923d 100644 --- a/http_test.go +++ b/http_test.go @@ -1,26 +1,28 @@ package mediaprobe_test import ( - "context" "net/http" + "net/http/httptest" + "time" ) type Server struct { - srv *http.Server + srv *httptest.Server } func (s *Server) Stop() { - _ = s.srv.Shutdown(context.Background()) + s.srv.Close() +} + +func (s *Server) Endpoint() string { + return s.srv.URL } func ServeHttp(handler *Handler) *Server { - srv := &http.Server{ - Addr: ":9090", - Handler: handler, - } - go func(s *http.Server) { - _ = s.ListenAndServe() - }(srv) + httptest.NewServer(handler) + + srv := httptest.NewServer(handler) + time.Sleep(100 * time.Millisecond) return &Server{srv: srv} } diff --git a/image_test.go b/image_test.go index 8eece01..355c364 100644 --- a/image_test.go +++ b/image_test.go @@ -8,7 +8,6 @@ import ( ) const ( - testImageValidRemoteImage = "http://localhost:9090/image.jpeg" testImageValidImage = "./fixtures/image.jpeg" testImageWithExifOrientation = "./fixtures/left.jpg" testImageInvalidImage = "./fixtures/not-an-image.jpeg" @@ -43,7 +42,7 @@ func parseImageNotFoundRemote(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - info, err := mediaprobe.New(testImageValidRemoteImage) + info, err := mediaprobe.New(srv.Endpoint()) if err != nil { t.Fatalf("Unexpected error %v", err) } @@ -61,7 +60,7 @@ func parseImageConnectionErrorRemote(t *testing.T) { } srv := ServeHttp(handler) - info, err := mediaprobe.New(testImageValidRemoteImage) + info, err := mediaprobe.New(srv.Endpoint()) if err != nil { t.Fatalf("Unexpected error %v", err) } @@ -96,7 +95,7 @@ func parseImageValidRemote(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - info, err := mediaprobe.New(testImageValidRemoteImage) + info, err := mediaprobe.New(srv.Endpoint()) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/info_test.go b/info_test.go index dd1d754..5d411ea 100644 --- a/info_test.go +++ b/info_test.go @@ -29,7 +29,7 @@ func newNotFoundRemote(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - _, err := mediaprobe.New("http://localhost:9090/not-exist") + _, err := mediaprobe.New(srv.Endpoint()) if err == nil { t.Errorf("Expected to return error found but return nil") } @@ -59,7 +59,7 @@ func newRemoteFile(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - info, err := mediaprobe.New("http://localhost:9090/video.mp4") + info, err := mediaprobe.New(srv.Endpoint()) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/mime_test.go b/mime_test.go index e068f2a..2a82aad 100644 --- a/mime_test.go +++ b/mime_test.go @@ -41,7 +41,7 @@ func calculateMimeRemote(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - info, err := mediaprobe.New("http://localhost:9090/not-an-image.jpeg") + info, err := mediaprobe.New(srv.Endpoint()) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/probe_test.go b/probe_test.go index 8fc3315..259dfb3 100644 --- a/probe_test.go +++ b/probe_test.go @@ -7,7 +7,7 @@ import ( ) const ( - testProbeValidRemoteImage = "http://localhost:9090/image.jpeg" + testProbeValidRemoteImage = "http://localhost:9097/image.jpeg" testProbeValidImage = "./fixtures/image.jpeg" testProbeInvalidImage = "./fixtures/not-an-image.jpeg" testProbeValidVideo = "./fixtures/video.mp4" @@ -52,9 +52,9 @@ func parseValidRemoteImage(t *testing.T) { srv := ServeHttp(handler) defer srv.Stop() - info, err := mediaprobe.Parse(testProbeValidRemoteImage) + info, err := mediaprobe.Parse(srv.Endpoint()) if err != nil { - t.Errorf("Filename: %s. Unexpected error %v", testProbeValidRemoteImage, err) + t.Errorf("Unexpected error %v", err) } width := info.Width if width != 290 { From 9d0458e72e3a4452b6588d16cd1d2edf50a33166 Mon Sep 17 00:00:00 2001 From: Navi Arimeka Date: Mon, 16 Mar 2020 13:55:44 +0300 Subject: [PATCH 4/4] More tests --- http_test.go | 17 ++++++++++++++ image_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++--- info_test.go | 15 +++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/http_test.go b/http_test.go index cbb923d..a5c312c 100644 --- a/http_test.go +++ b/http_test.go @@ -3,6 +3,8 @@ package mediaprobe_test import ( "net/http" "net/http/httptest" + "sync" + "sync/atomic" "time" ) @@ -30,6 +32,10 @@ func ServeHttp(handler *Handler) *Server { type Handler struct { Status int Filename string + + FailOnAttempt uint64 + Counter uint64 + mu sync.RWMutex } func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -37,6 +43,17 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(h.Status) return } + if h.FailOnAttempt > 0 { + h.mu.RLock() + counter := h.Counter + h.mu.RUnlock() + if counter >= h.FailOnAttempt { + rw.WriteHeader(http.StatusInternalServerError) + return + } + + atomic.AddUint64(&h.Counter, 1) + } http.ServeFile(rw, req, h.Filename) } diff --git a/image_test.go b/image_test.go index 355c364..24d330b 100644 --- a/image_test.go +++ b/image_test.go @@ -19,9 +19,11 @@ func TestInfo_ParseImage(t *testing.T) { t.Run("not_found", parseImageNotFound) t.Run("not_found_remote", parseImageNotFoundRemote) t.Run("conn_error_remote", parseImageConnectionErrorRemote) + t.Run("server_error_remote", parseImageServerErrorsRemote) t.Run("valid", parseImageValid) t.Run("valid_remote", parseImageValidRemote) t.Run("invalid", parseImageInvalid) + t.Run("invalid_remote", parseImageInvalidRemote) t.Run("with_orientation", parseImageWithOrientation) t.Run("jpeg_without_exif", parseImageJPEGWithoutExif) t.Run("png_without_exif", parseImagePNGWithoutExif) @@ -37,7 +39,7 @@ func parseImageNotFound(t *testing.T) { func parseImageNotFoundRemote(t *testing.T) { handler := &Handler{ - Filename: "./fixtures/image.jpeg", + Filename: testImageValidImage, } srv := ServeHttp(handler) defer srv.Stop() @@ -56,7 +58,7 @@ func parseImageNotFoundRemote(t *testing.T) { func parseImageConnectionErrorRemote(t *testing.T) { handler := &Handler{ - Filename: "./fixtures/image.jpeg", + Filename: testImageValidImage, } srv := ServeHttp(handler) @@ -72,6 +74,42 @@ func parseImageConnectionErrorRemote(t *testing.T) { } } +func parseImageServerErrorsRemote(t *testing.T) { + handler := &Handler{ + Filename: testImageValidImage, + FailOnAttempt: 2, + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New(srv.Endpoint()) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + err = info.ParseImage() + if err == nil { + t.Errorf("Expected to return error found but return nil") + } + + handler = &Handler{ + Filename: testImageValidImage, + FailOnAttempt: 3, + } + srv = ServeHttp(handler) + defer srv.Stop() + + info, err = mediaprobe.New(srv.Endpoint()) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + err = info.ParseImage() + if err == nil { + t.Errorf("Expected to return error found but return nil") + } +} + func parseImageValid(t *testing.T) { info, err := mediaprobe.New(testImageValidImage) if err != nil { @@ -90,7 +128,7 @@ func parseImageValid(t *testing.T) { func parseImageValidRemote(t *testing.T) { handler := &Handler{ - Filename: "./fixtures/image.jpeg", + Filename: testImageValidImage, } srv := ServeHttp(handler) defer srv.Stop() @@ -122,6 +160,23 @@ func parseImageInvalid(t *testing.T) { } } +func parseImageInvalidRemote(t *testing.T) { + handler := &Handler{ + Filename: testImageInvalidImage, + } + srv := ServeHttp(handler) + defer srv.Stop() + + info, err := mediaprobe.New(srv.Endpoint()) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + err = info.ParseImage() + if err == nil { + t.Errorf("Expected to return error but return nil") + } +} + func parseImageWithOrientation(t *testing.T) { info, _ := mediaprobe.New(testImageWithExifOrientation) err := info.ParseImage() diff --git a/info_test.go b/info_test.go index 5d411ea..57b8a2b 100644 --- a/info_test.go +++ b/info_test.go @@ -11,6 +11,7 @@ func TestNew(t *testing.T) { t.Run("not_found", newNotFound) t.Run("not_found_remote", newNotFoundRemote) t.Run("conn_error_remote", newConnErrorRemote) + t.Run("server_error_remote", newServerErrorRemote) t.Run("local", newLocalFile) t.Run("remote", newRemoteFile) } @@ -42,6 +43,20 @@ func newConnErrorRemote(t *testing.T) { } } +func newServerErrorRemote(t *testing.T) { + handler := &Handler{ + Filename: "./fixtures/video.mp4", + FailOnAttempt: 1, + } + srv := ServeHttp(handler) + defer srv.Stop() + + _, err := mediaprobe.New(srv.Endpoint()) + if err == nil { + t.Errorf("Expected to return error found but return nil") + } +} + func newLocalFile(t *testing.T) { info, err := mediaprobe.New("./fixtures/video.mp4") if err != nil {