127 lines
4.1 KiB
Go
127 lines
4.1 KiB
Go
package imagecheck
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"errors"
|
||
"image"
|
||
"image/color"
|
||
"image/png"
|
||
"testing"
|
||
)
|
||
|
||
// buildGIFHeader 构造仅含文件头与逻辑屏幕描述符的最小 GIF(无像素数据),
|
||
// 用于让 image.DecodeConfig 读取出宽高。GIF 无 CRC,构造简单可靠。
|
||
func buildGIFHeader(t *testing.T, width, height uint16) []byte {
|
||
t.Helper()
|
||
var buf bytes.Buffer
|
||
buf.WriteString("GIF89a")
|
||
// Logical Screen Descriptor: width(2 LE) + height(2 LE) + packed(1) + bgcolor(1) + aspect(1)
|
||
if err := binary.Write(&buf, binary.LittleEndian, width); err != nil {
|
||
t.Fatalf("write width: %v", err)
|
||
}
|
||
if err := binary.Write(&buf, binary.LittleEndian, height); err != nil {
|
||
t.Fatalf("write height: %v", err)
|
||
}
|
||
buf.Write([]byte{0x00, 0x00, 0x00}) // 无全局颜色表
|
||
return buf.Bytes()
|
||
}
|
||
|
||
func TestCheckPixelLimit_NormalPNG(t *testing.T) {
|
||
img := image.NewRGBA(image.Rect(0, 0, 100, 100))
|
||
for x := 0; x < 100; x++ {
|
||
for y := 0; y < 100; y++ {
|
||
img.Set(x, y, color.RGBA{R: 255, A: 255})
|
||
}
|
||
}
|
||
var buf bytes.Buffer
|
||
if err := png.Encode(&buf, img); err != nil {
|
||
t.Fatalf("encode png: %v", err)
|
||
}
|
||
if err := CheckPixelLimit(bytes.NewReader(buf.Bytes())); err != nil {
|
||
t.Fatalf("normal image should pass, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestCheckPixelLimit_AtLimit(t *testing.T) {
|
||
// 恰好等于上限 4096×4096,应通过(校验为严格大于才拒绝)。
|
||
data := buildGIFHeader(t, 4096, 4096)
|
||
if err := CheckPixelLimit(bytes.NewReader(data)); err != nil {
|
||
t.Fatalf("4096×4096 should pass, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestCheckPixelLimit_TooLarge(t *testing.T) {
|
||
// 4096×4097 超过上限,应拒绝并返回统一错误。
|
||
data := buildGIFHeader(t, 4096, 4097)
|
||
err := CheckPixelLimit(bytes.NewReader(data))
|
||
if err == nil {
|
||
t.Fatal("oversized image should be rejected, got nil")
|
||
}
|
||
if !errors.Is(err, ErrImageTooLarge) {
|
||
t.Fatalf("expected ErrImageTooLarge, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestCheckPixelLimit_InvalidData(t *testing.T) {
|
||
err := CheckPixelLimit(bytes.NewReader([]byte("this is not an image")))
|
||
if err == nil {
|
||
t.Fatal("invalid data should error, got nil")
|
||
}
|
||
if errors.Is(err, ErrImageTooLarge) {
|
||
t.Fatal("invalid data should not be classified as ErrImageTooLarge")
|
||
}
|
||
}
|
||
|
||
// encodePNG 生成指定宽高的纯色 PNG,供比例校验测试使用。
|
||
func encodePNG(t *testing.T, width, height int) []byte {
|
||
t.Helper()
|
||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||
var buf bytes.Buffer
|
||
if err := png.Encode(&buf, img); err != nil {
|
||
t.Fatalf("encode png: %v", err)
|
||
}
|
||
return buf.Bytes()
|
||
}
|
||
|
||
func TestCheckAspectRatio_Pass(t *testing.T) {
|
||
// 1920×1080 精确 16:9
|
||
if err := CheckAspectRatio(bytes.NewReader(encodePNG(t, 1920, 1080)), 16, 9, DefaultAspectTolerance); err != nil {
|
||
t.Fatalf("1920×1080 should pass 16:9, got %v", err)
|
||
}
|
||
// 1:1 正方形
|
||
if err := CheckAspectRatio(bytes.NewReader(encodePNG(t, 512, 512)), 1, 1, DefaultAspectTolerance); err != nil {
|
||
t.Fatalf("512×512 should pass 1:1, got %v", err)
|
||
}
|
||
// 容差内的微小偏差(1281×720 ≈ 1.779,仍属 16:9)
|
||
if err := CheckAspectRatio(bytes.NewReader(encodePNG(t, 1281, 720)), 16, 9, DefaultAspectTolerance); err != nil {
|
||
t.Fatalf("1281×720 should pass within tolerance, got %v", err)
|
||
}
|
||
}
|
||
|
||
func TestCheckAspectRatio_Reject(t *testing.T) {
|
||
// 4:3 直传背景图应被拒绝
|
||
err := CheckAspectRatio(bytes.NewReader(encodePNG(t, 800, 600)), 16, 9, DefaultAspectTolerance)
|
||
if err == nil {
|
||
t.Fatal("800×600 should be rejected as 16:9")
|
||
}
|
||
if !errors.Is(err, ErrBadAspectRatio) {
|
||
t.Fatalf("expected ErrBadAspectRatio, got %v", err)
|
||
}
|
||
// 竖长条不能当头像
|
||
if err := CheckAspectRatio(bytes.NewReader(encodePNG(t, 300, 500)), 1, 1, DefaultAspectTolerance); err == nil {
|
||
t.Fatal("300×500 should be rejected as 1:1")
|
||
}
|
||
}
|
||
|
||
func TestCheckAspectRatio_InvalidData(t *testing.T) {
|
||
// 无法解码的数据不应误判为比例不符
|
||
err := CheckAspectRatio(bytes.NewReader([]byte("this is not an image")), 1, 1, DefaultAspectTolerance)
|
||
if err == nil {
|
||
t.Fatal("invalid data should error, got nil")
|
||
}
|
||
if errors.Is(err, ErrBadAspectRatio) {
|
||
t.Fatal("invalid data should not be classified as ErrBadAspectRatio")
|
||
}
|
||
}
|