90 lines
2.3 KiB
Go
90 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/jpeg"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// testPhoto returns a JPEG-decoded (so *image.YCbCr, 4:2:0) image with an odd
|
|
// size and a non-zero origin to exercise the plane offset arithmetic.
|
|
func testPhoto(t testing.TB, w, h int) *image.YCbCr {
|
|
t.Helper()
|
|
src := image.NewRGBA(image.Rect(0, 0, w, h))
|
|
for i := range src.Pix {
|
|
src.Pix[i] = uint8(i*7 + i/w)
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := jpeg.Encode(&buf, src, &jpeg.Options{Quality: 90}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
img, err := jpeg.Decode(&buf)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return img.(*image.YCbCr).SubImage(image.Rect(3, 5, w, h)).(*image.YCbCr)
|
|
}
|
|
|
|
func TestResizeBoxYCbCrMatchesGeneric(t *testing.T) {
|
|
src := testPhoto(t, 1001, 667)
|
|
fast := resizeBox(src, 120).(*image.RGBA)
|
|
// Hide the concrete type so resizeBox takes the generic At() path.
|
|
slow := resizeBox(struct{ image.Image }{src}, 120).(*image.RGBA)
|
|
if fast.Bounds() != slow.Bounds() {
|
|
t.Fatalf("bounds %v != %v", fast.Bounds(), slow.Bounds())
|
|
}
|
|
for i := range fast.Pix {
|
|
if fast.Pix[i] != slow.Pix[i] {
|
|
t.Fatalf("pixel byte %d: fast %d, generic %d", i, fast.Pix[i], slow.Pix[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkResizeBox(b *testing.B) {
|
|
src := testPhoto(b, 4000, 3000)
|
|
b.Run("ycbcr", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
resizeBox(src, 300)
|
|
}
|
|
})
|
|
b.Run("generic", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
resizeBox(struct{ image.Image }{src}, 300)
|
|
}
|
|
})
|
|
}
|
|
|
|
// After a restart (empty memory cache) the digest comes from the on-disk index
|
|
// without reading the source file.
|
|
func TestSourceDigestPersists(t *testing.T) {
|
|
old := thumbCacheDir
|
|
thumbCacheDir = t.TempDir()
|
|
t.Cleanup(func() { thumbCacheDir = old })
|
|
|
|
src := filepath.Join(t.TempDir(), "a.jpg")
|
|
if err := os.WriteFile(src, []byte("photo"), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
info, _ := os.Stat(src)
|
|
h1, data, err := sourceDigest(src, info)
|
|
if err != nil || data == nil {
|
|
t.Fatalf("first call: %v, data=%v", err, data)
|
|
}
|
|
|
|
digestCacheMu.Lock()
|
|
delete(digestCache, src)
|
|
digestCacheMu.Unlock()
|
|
// Removing the source proves the second call does not read it again.
|
|
if err := os.Remove(src); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
h2, data, err := sourceDigest(src, info)
|
|
if err != nil || data != nil || h2 != h1 {
|
|
t.Fatalf("after restart: h=%s data=%v err=%v, want %s from index", h2, data, err, h1)
|
|
}
|
|
}
|