implement a resolution handler
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package resolution
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse parses a resolution string in the form <width>x<height> and returns a resolution instance.
|
||||
func Parse(s string) (Resolution, error) {
|
||||
parts := strings.Split(s, "x")
|
||||
if len(parts) != 2 {
|
||||
return Resolution{}, fmt.Errorf("failed to parse resolution: %s. Expected format <width>x<height>", s)
|
||||
}
|
||||
width, err := strconv.Atoi(parts[0])
|
||||
if err != nil {
|
||||
return Resolution{}, fmt.Errorf("width: %s has an invalid value. Expected an integer", parts[0])
|
||||
}
|
||||
height, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return Resolution{}, fmt.Errorf("height: %s has an invalid value. Expected an integer", parts[1])
|
||||
}
|
||||
return Resolution{Width: width, Height: height}, nil
|
||||
}
|
||||
|
||||
// Resolution defines represents the width and height of a thumbnail.
|
||||
type Resolution struct {
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
// String returns the resolution in the format:
|
||||
//
|
||||
// <width>x<height>
|
||||
func (r Resolution) String() string {
|
||||
return strconv.Itoa(r.Width) + "x" + strconv.Itoa(r.Height)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package resolution
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseWithEmptyString(t *testing.T) {
|
||||
_, err := Parse("")
|
||||
if err == nil {
|
||||
t.Error("Parse with empty string should return an error.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
rStr := "42x23"
|
||||
r, _ := Parse(rStr)
|
||||
if r.Width != 42 || r.Height != 23 {
|
||||
t.Errorf("Expected resolution %s got %s", rStr, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
r := Resolution{Width: 42, Height: 23}
|
||||
expected := "42x23"
|
||||
if r.String() != expected {
|
||||
t.Errorf("Expected string %s got %s", expected, r.String())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package resolution
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Init creates an instance of Resolutions from resolution strings.
|
||||
func Init(rStrs []string) (Resolutions, error) {
|
||||
var rs Resolutions
|
||||
for _, rStr := range rStrs {
|
||||
r, err := Parse(rStr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize resolutions: %s", err.Error())
|
||||
}
|
||||
rs = append(rs, r)
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// Resolutions represents the available thumbnail resolutions.
|
||||
type Resolutions []Resolution
|
||||
|
||||
// ClosestMatch returns the resolution which is closest to the provided resolution.
|
||||
func (r Resolutions) ClosestMatch(width, height int) Resolution {
|
||||
if len(r) == 0 {
|
||||
return Resolution{Width: width, Height: height}
|
||||
}
|
||||
|
||||
isLandscape := width > height
|
||||
givenLen := math.Max(float64(width), float64(height))
|
||||
|
||||
// Initialize with the first resolution
|
||||
match := r[0]
|
||||
matchLen := dimensionLength(match, isLandscape)
|
||||
minDiff := math.Abs(givenLen - float64(matchLen))
|
||||
|
||||
for i := 1; i < len(r); i++ {
|
||||
r := r[i]
|
||||
rLen := dimensionLength(r, isLandscape)
|
||||
diff := math.Abs(givenLen - float64(rLen))
|
||||
|
||||
if diff <= minDiff {
|
||||
minDiff = diff
|
||||
match = r
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
func dimensionLength(r Resolution, landscape bool) int {
|
||||
if landscape {
|
||||
return r.Width
|
||||
}
|
||||
return r.Height
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package resolution
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInitWithEmptyArray(t *testing.T) {
|
||||
rs, err := Init([]string{})
|
||||
if err != nil {
|
||||
t.Errorf("Init with an empty array should not fail. Error: %s.\n", err.Error())
|
||||
}
|
||||
if len(rs) != 0 {
|
||||
t.Error("Init with an empty array should return an empty Resolutions instance.\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitWithNil(t *testing.T) {
|
||||
rs, err := Init(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Init with nil parameter should not fail. Error: %s.\n", err.Error())
|
||||
}
|
||||
if len(rs) != 0 {
|
||||
t.Error("Init with nil parameter should return an empty Resolutions instance.\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitWithInvalidValuesInArray(t *testing.T) {
|
||||
_, err := Init([]string{"invalid"})
|
||||
if err == nil {
|
||||
t.Error("Init with invalid parameter should fail.\n")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
rs, err := Init([]string{"16x16"})
|
||||
if err != nil {
|
||||
t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error())
|
||||
}
|
||||
if len(rs) != 1 {
|
||||
t.Errorf("resolutions has size %d, expected size %d.\n", len(rs), 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitWithMultipleResolutions(t *testing.T) {
|
||||
rStrs := []string{"16x16", "32x32", "64x64", "128x128"}
|
||||
rs, err := Init(rStrs)
|
||||
if err != nil {
|
||||
t.Errorf("Init with valid parameter should not fail. Error: %s.\n", err.Error())
|
||||
}
|
||||
if len(rs) != len(rStrs) {
|
||||
t.Errorf("resolutions has size %d, expected size %d.\n", len(rs), len(rStrs))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClosestMatchWithEmptyResolutions(t *testing.T) {
|
||||
rs, _ := Init(nil)
|
||||
width := 24
|
||||
height := 24
|
||||
|
||||
r := rs.ClosestMatch(width, height)
|
||||
if r.Width != width || r.Height != height {
|
||||
t.Errorf("ClosestMatch from empty resolutions should return the given resolution")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClosestMatch(t *testing.T) {
|
||||
rs, _ := Init([]string{"16x16", "24x24", "32x32", "64x64", "128x128"})
|
||||
table := [][]int{
|
||||
[]int{17, 17, 16, 16},
|
||||
[]int{12, 17, 16, 16},
|
||||
[]int{24, 24, 24, 24},
|
||||
[]int{20, 20, 24, 24},
|
||||
[]int{20, 80, 64, 64},
|
||||
[]int{80, 20, 64, 64},
|
||||
[]int{48, 48, 64, 64},
|
||||
[]int{1024, 1024, 128, 128},
|
||||
}
|
||||
|
||||
for _, row := range table {
|
||||
width := row[0]
|
||||
height := row[1]
|
||||
expectedWidth := row[2]
|
||||
expectedHeight := row[3]
|
||||
|
||||
match := rs.ClosestMatch(width, height)
|
||||
|
||||
if match.Width != expectedWidth || match.Height != expectedHeight {
|
||||
t.Errorf("Expected resolution %dx%d got %s", expectedWidth, expectedHeight, match.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user