[🤖] Add Unit-Tests
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m34s

This commit is contained in:
2026-04-27 10:46:08 +02:00
parent dad0e3240d
commit 02d6894ec6
116 changed files with 18795 additions and 1 deletions
+122
View File
@@ -0,0 +1,122 @@
package imageext
import (
"testing"
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
)
func TestImageFit_Valid(t *testing.T) {
tst.AssertTrue(t, ImageFitStretch.Valid())
tst.AssertTrue(t, ImageFitCover.Valid())
tst.AssertTrue(t, ImageFitContainCenter.Valid())
tst.AssertTrue(t, ImageFitContainTopLeft.Valid())
tst.AssertTrue(t, ImageFitContainTopRight.Valid())
tst.AssertTrue(t, ImageFitContainBottomLeft.Valid())
tst.AssertTrue(t, ImageFitContainBottomRight.Valid())
tst.AssertFalse(t, ImageFit("UNKNOWN").Valid())
tst.AssertFalse(t, ImageFit("").Valid())
}
func TestImageFit_String(t *testing.T) {
tst.AssertEqual(t, ImageFitStretch.String(), "STRETCH")
tst.AssertEqual(t, ImageFitCover.String(), "COVER")
tst.AssertEqual(t, ImageFitContainCenter.String(), "CONTAIN_CENTER")
}
func TestImageFit_VarName(t *testing.T) {
tst.AssertEqual(t, ImageFitStretch.VarName(), "ImageFitStretch")
tst.AssertEqual(t, ImageFitContainBottomRight.VarName(), "ImageFitContainBottomRight")
tst.AssertEqual(t, ImageFit("UNKNOWN").VarName(), "")
}
func TestImageFit_TypeName(t *testing.T) {
tst.AssertEqual(t, ImageFitStretch.TypeName(), "ImageFit")
}
func TestImageFit_Values(t *testing.T) {
values := ImageFitValues()
tst.AssertEqual(t, len(values), 7)
tst.AssertEqual(t, values[0], ImageFitStretch)
}
func TestImageFit_ValuesAny(t *testing.T) {
tst.AssertEqual(t, len(ImageFitStretch.ValuesAny()), 7)
}
func TestImageFit_ValuesMeta(t *testing.T) {
meta := ImageFitValuesMeta()
tst.AssertEqual(t, len(meta), 7)
tst.AssertEqual(t, meta[0].VarName, "ImageFitStretch")
}
func TestParseImageFit(t *testing.T) {
v, ok := ParseImageFit("COVER")
tst.AssertTrue(t, ok)
tst.AssertEqual(t, v, ImageFitCover)
v, ok = ParseImageFit("CONTAIN_TOPLEFT")
tst.AssertTrue(t, ok)
tst.AssertEqual(t, v, ImageFitContainTopLeft)
_, ok = ParseImageFit("BOGUS")
tst.AssertFalse(t, ok)
}
func TestImageCompresson_Valid(t *testing.T) {
tst.AssertTrue(t, CompressionPNGNone.Valid())
tst.AssertTrue(t, CompressionPNGSpeed.Valid())
tst.AssertTrue(t, CompressionPNGBest.Valid())
tst.AssertTrue(t, CompressionJPEG100.Valid())
tst.AssertTrue(t, CompressionJPEG1.Valid())
tst.AssertFalse(t, ImageCompresson("UNKNOWN").Valid())
tst.AssertFalse(t, ImageCompresson("").Valid())
}
func TestImageCompresson_String(t *testing.T) {
tst.AssertEqual(t, CompressionPNGNone.String(), "PNG_NONE")
tst.AssertEqual(t, CompressionJPEG90.String(), "JPEG_090")
}
func TestImageCompresson_VarName(t *testing.T) {
tst.AssertEqual(t, CompressionJPEG50.VarName(), "CompressionJPEG50")
tst.AssertEqual(t, ImageCompresson("UNKNOWN").VarName(), "")
}
func TestImageCompresson_TypeName(t *testing.T) {
tst.AssertEqual(t, CompressionJPEG50.TypeName(), "ImageCompresson")
}
func TestImageCompresson_Values(t *testing.T) {
values := ImageCompressonValues()
tst.AssertEqual(t, len(values), 12)
}
func TestImageCompresson_ValuesAny(t *testing.T) {
tst.AssertEqual(t, len(CompressionPNGBest.ValuesAny()), 12)
}
func TestImageCompresson_ValuesMeta(t *testing.T) {
meta := ImageCompressonValuesMeta()
tst.AssertEqual(t, len(meta), 12)
}
func TestParseImageCompresson(t *testing.T) {
v, ok := ParseImageCompresson("PNG_BEST")
tst.AssertTrue(t, ok)
tst.AssertEqual(t, v, CompressionPNGBest)
v, ok = ParseImageCompresson("JPEG_080")
tst.AssertTrue(t, ok)
tst.AssertEqual(t, v, CompressionJPEG80)
_, ok = ParseImageCompresson("BOGUS")
tst.AssertFalse(t, ok)
}
func TestAllPackageEnums(t *testing.T) {
enums := AllPackageEnums()
tst.AssertEqual(t, len(enums), 2)
}
+302
View File
@@ -0,0 +1,302 @@
package imageext
import (
"bytes"
"image"
"image/color"
"image/jpeg"
"image/png"
"strings"
"testing"
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
)
// makeRGBA creates a solid-color RGBA image of the given size.
func makeRGBA(w, h int, c color.Color) *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.Set(x, y, c)
}
}
return img
}
// makeGradient creates an RGBA image with a gradient pattern.
// Useful for codecs that may behave oddly with uniform colors.
func makeGradient(w, h int) *image.RGBA {
img := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.Set(x, y, color.RGBA{
R: uint8((x * 255) / max1(w-1)),
G: uint8((y * 255) / max1(h-1)),
B: uint8(((x + y) * 255) / max1(w+h-2)),
A: 255,
})
}
}
return img
}
func max1(v int) int {
if v <= 0 {
return 1
}
return v
}
func TestCropImage_HalfRegion(t *testing.T) {
src := makeGradient(100, 80)
out, err := CropImage(src, 0.0, 0.0, 0.5, 0.5)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 50)
tst.AssertEqual(t, out.Bounds().Dy(), 40)
}
func TestCropImage_Offset(t *testing.T) {
src := makeGradient(200, 100)
out, err := CropImage(src, 0.25, 0.5, 0.5, 0.5)
tst.AssertNoErr(t, err)
// SubImage preserves coordinates of the parent image.
tst.AssertEqual(t, out.Bounds().Min.X, 50)
tst.AssertEqual(t, out.Bounds().Min.Y, 50)
tst.AssertEqual(t, out.Bounds().Dx(), 100)
tst.AssertEqual(t, out.Bounds().Dy(), 50)
}
func TestCropImage_Full(t *testing.T) {
src := makeGradient(40, 40)
out, err := CropImage(src, 0.0, 0.0, 1.0, 1.0)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 40)
tst.AssertEqual(t, out.Bounds().Dy(), 40)
}
func TestEncodeImage_AllCompressions(t *testing.T) {
src := makeGradient(20, 16)
cases := []struct {
comp ImageCompresson
mime string
signature []byte
}{
{CompressionPNGNone, "image/png", []byte{0x89, 'P', 'N', 'G'}},
{CompressionPNGSpeed, "image/png", []byte{0x89, 'P', 'N', 'G'}},
{CompressionPNGBest, "image/png", []byte{0x89, 'P', 'N', 'G'}},
{CompressionJPEG100, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG90, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG80, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG70, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG60, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG50, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG25, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG10, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
{CompressionJPEG1, "image/jpeg", []byte{0xFF, 0xD8, 0xFF}},
}
for _, c := range cases {
t.Run(string(c.comp), func(t *testing.T) {
buf, mime, err := EncodeImage(src, c.comp)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, mime, c.mime)
tst.AssertTrue(t, buf.Len() > 0)
data := buf.Bytes()
tst.AssertTrue(t, len(data) >= len(c.signature))
tst.AssertTrue(t, bytes.Equal(data[:len(c.signature)], c.signature))
// Round-trip decode to confirm the bytes form a valid image.
var dec image.Image
var derr error
if c.mime == "image/png" {
dec, derr = png.Decode(bytes.NewReader(data))
} else {
dec, derr = jpeg.Decode(bytes.NewReader(data))
}
tst.AssertNoErr(t, derr)
tst.AssertEqual(t, dec.Bounds().Dx(), 20)
tst.AssertEqual(t, dec.Bounds().Dy(), 16)
})
}
}
func TestEncodeImage_UnknownCompression(t *testing.T) {
src := makeRGBA(4, 4, color.White)
buf, mime, err := EncodeImage(src, ImageCompresson("UNKNOWN"))
tst.AssertTrue(t, err != nil)
tst.AssertEqual(t, mime, "")
tst.AssertEqual(t, buf.Len(), 0)
}
func TestObjectFitImage_Cover_SmallerThanBB(t *testing.T) {
// Image (100x100) is smaller than the BB (200x100), so the output is
// scaled down to the smaller-axis factor of 0.5: 100x50.
src := makeGradient(100, 100)
out, rect, err := ObjectFitImage(src, 200, 100, ImageFitCover, color.Transparent)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 100)
tst.AssertEqual(t, out.Bounds().Dy(), 50)
tst.AssertDeepEqual(t, rect, PercentageRectangle{0, 0, 1, 1})
}
func TestObjectFitImage_Cover_LargerThanBB(t *testing.T) {
// Image (400x200) is larger than the BB (200x100); fac is capped at 1, so
// the output is exactly the BB size.
src := makeGradient(400, 200)
out, _, err := ObjectFitImage(src, 200, 100, ImageFitCover, color.Transparent)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 200)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
}
func TestObjectFitImage_ContainCenter(t *testing.T) {
// Image 100x100 in a BB of 200x100 -> output 200x100, drawn rect 100x100 centered.
src := makeGradient(100, 100)
out, rect, err := ObjectFitImage(src, 200, 100, ImageFitContainCenter, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 200)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
// (200-100)/2 = 50 -> X=50/200 = 0.25, W=100/200=0.5, Y=0, H=1
tst.AssertDeepEqual(t, rect, PercentageRectangle{X: 0.25, Y: 0, W: 0.5, H: 1})
}
func TestObjectFitImage_ContainTopLeft(t *testing.T) {
src := makeGradient(100, 100)
out, rect, err := ObjectFitImage(src, 200, 100, ImageFitContainTopLeft, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 200)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
tst.AssertDeepEqual(t, rect, PercentageRectangle{X: 0, Y: 0, W: 0.5, H: 1})
}
func TestObjectFitImage_ContainTopRight(t *testing.T) {
src := makeGradient(100, 100)
out, rect, err := ObjectFitImage(src, 200, 100, ImageFitContainTopRight, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 200)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
tst.AssertDeepEqual(t, rect, PercentageRectangle{X: 0.5, Y: 0, W: 0.5, H: 1})
}
func TestObjectFitImage_ContainBottomLeft(t *testing.T) {
// Image 200x100 in a BB of 100x100 (image is bigger so facOut is capped at 1)
src := makeGradient(200, 100)
out, rect, err := ObjectFitImage(src, 100, 100, ImageFitContainBottomLeft, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 100)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
// dw=100, dh=50 -> bottom-left rect: (0, 50, 100, 100) -> Y=0.5, H=0.5
tst.AssertDeepEqual(t, rect, PercentageRectangle{X: 0, Y: 0.5, W: 1, H: 0.5})
}
func TestObjectFitImage_ContainBottomRight(t *testing.T) {
src := makeGradient(200, 100)
out, rect, err := ObjectFitImage(src, 100, 100, ImageFitContainBottomRight, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 100)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
tst.AssertDeepEqual(t, rect, PercentageRectangle{X: 0, Y: 0.5, W: 1, H: 0.5})
}
func TestObjectFitImage_Stretch(t *testing.T) {
// Image 100x100 in BB 200x100 -> uses max(facW=0.5, facH=1.0) capped at 1, so result is 200x100.
src := makeGradient(100, 100)
out, rect, err := ObjectFitImage(src, 200, 100, ImageFitStretch, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 200)
tst.AssertEqual(t, out.Bounds().Dy(), 100)
tst.AssertDeepEqual(t, rect, PercentageRectangle{0, 0, 1, 1})
}
func TestObjectFitImage_Stretch_SmallImage(t *testing.T) {
// Image 50x25 in BB 200x100 -> max(0.25, 0.25) = 0.25, output 50x25.
src := makeGradient(50, 25)
out, _, err := ObjectFitImage(src, 200, 100, ImageFitStretch, color.Black)
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 50)
tst.AssertEqual(t, out.Bounds().Dy(), 25)
}
func TestObjectFitImage_UnknownFit(t *testing.T) {
src := makeGradient(20, 20)
out, _, err := ObjectFitImage(src, 100, 100, ImageFit("BOGUS"), color.Black)
tst.AssertTrue(t, err != nil)
if out != nil {
t.Errorf("expected nil image on error, got %v", out)
}
}
func TestVerifyAndDecodeImage_PNG(t *testing.T) {
src := makeGradient(12, 8)
buf := bytes.Buffer{}
tst.AssertNoErr(t, png.Encode(&buf, src))
out, err := VerifyAndDecodeImage(&buf, "image/png")
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 12)
tst.AssertEqual(t, out.Bounds().Dy(), 8)
}
func TestVerifyAndDecodeImage_JPEG(t *testing.T) {
src := makeGradient(16, 16)
buf := bytes.Buffer{}
tst.AssertNoErr(t, jpeg.Encode(&buf, src, &jpeg.Options{Quality: 90}))
out, err := VerifyAndDecodeImage(&buf, "image/jpeg")
tst.AssertNoErr(t, err)
tst.AssertEqual(t, out.Bounds().Dx(), 16)
tst.AssertEqual(t, out.Bounds().Dy(), 16)
}
func TestVerifyAndDecodeImage_UnknownMime(t *testing.T) {
out, err := VerifyAndDecodeImage(strings.NewReader("whatever"), "image/gif")
tst.AssertTrue(t, err != nil)
if out != nil {
t.Errorf("expected nil image on error, got %v", out)
}
}
func TestVerifyAndDecodeImage_BadPNG(t *testing.T) {
out, err := VerifyAndDecodeImage(strings.NewReader("not a png"), "image/png")
tst.AssertTrue(t, err != nil)
if out != nil {
t.Errorf("expected nil image on error, got %v", out)
}
}
func TestVerifyAndDecodeImage_BadJPEG(t *testing.T) {
out, err := VerifyAndDecodeImage(strings.NewReader("not a jpeg"), "image/jpeg")
tst.AssertTrue(t, err != nil)
if out != nil {
t.Errorf("expected nil image on error, got %v", out)
}
}
+56
View File
@@ -0,0 +1,56 @@
package imageext
import (
"image"
"testing"
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
)
func TestPercentageRectangle_Of_FullRef(t *testing.T) {
r := PercentageRectangle{X: 0.25, Y: 0.5, W: 0.5, H: 0.25}
ref := Rectangle{X: 0, Y: 0, W: 100, H: 200}
got := r.Of(ref)
tst.AssertDeepEqual(t, got, Rectangle{X: 25, Y: 100, W: 50, H: 50})
}
func TestPercentageRectangle_Of_OffsetRef(t *testing.T) {
r := PercentageRectangle{X: 0.5, Y: 0.5, W: 0.5, H: 0.5}
ref := Rectangle{X: 10, Y: 20, W: 100, H: 100}
got := r.Of(ref)
tst.AssertDeepEqual(t, got, Rectangle{X: 60, Y: 70, W: 50, H: 50})
}
func TestPercentageRectangle_Of_Identity(t *testing.T) {
r := PercentageRectangle{X: 0, Y: 0, W: 1, H: 1}
ref := Rectangle{X: 5, Y: 6, W: 7, H: 8}
got := r.Of(ref)
tst.AssertDeepEqual(t, got, ref)
}
func TestCalcRelativeRect_FullInner(t *testing.T) {
inner := image.Rect(0, 0, 100, 100)
outer := image.Rect(0, 0, 100, 100)
got := calcRelativeRect(inner, outer)
tst.AssertDeepEqual(t, got, PercentageRectangle{X: 0, Y: 0, W: 1, H: 1})
}
func TestCalcRelativeRect_Centered(t *testing.T) {
inner := image.Rect(50, 25, 150, 75)
outer := image.Rect(0, 0, 200, 100)
got := calcRelativeRect(inner, outer)
tst.AssertDeepEqual(t, got, PercentageRectangle{X: 0.25, Y: 0.25, W: 0.5, H: 0.5})
}
func TestCalcRelativeRect_OffsetOuter(t *testing.T) {
inner := image.Rect(120, 60, 220, 110)
outer := image.Rect(100, 50, 300, 150)
got := calcRelativeRect(inner, outer)
tst.AssertDeepEqual(t, got, PercentageRectangle{X: 0.1, Y: 0.1, W: 0.5, H: 0.5})
}