You've already forked imgproxy
mirror of
https://github.com/imgproxy/imgproxy.git
synced 2026-06-13 22:32:40 +02:00
169 lines
5.1 KiB
Go
169 lines
5.1 KiB
Go
package testutil
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/imgproxy/imgproxy/v4/imagetype"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
// hashPath is a path to hash data in testdata folder
|
|
hashPath = "test-hashes"
|
|
|
|
// If TEST_CREATE_MISSING_HASHES is set, matcher would create missing hash files
|
|
createMissingHashesEnv = "TEST_CREATE_MISSING_HASHES"
|
|
|
|
// If this is set, the images are saved to this folder before hash is calculated
|
|
saveTmpImagesPathEnv = "TEST_SAVE_TMP_IMAGES_PATH"
|
|
)
|
|
|
|
// ImageHashCacheMatcher is a helper struct for image hash comparison in tests
|
|
type ImageHashCacheMatcher struct {
|
|
hashesPath string
|
|
createMissingHashes bool
|
|
saveTmpImagesPath string
|
|
hashType ImageHashType
|
|
}
|
|
|
|
// NewImageHashCacheMatcher creates a new ImageHashRegressionMatcher instance
|
|
func NewImageHashCacheMatcher(testDataProvider *TestDataProvider, hashType ImageHashType) *ImageHashCacheMatcher {
|
|
hashesPath := testDataProvider.Path(hashPath)
|
|
createMissingHashes := len(os.Getenv(createMissingHashesEnv)) > 0
|
|
saveTmpImagesPath := os.Getenv(saveTmpImagesPathEnv)
|
|
|
|
return &ImageHashCacheMatcher{
|
|
hashesPath: hashesPath,
|
|
createMissingHashes: createMissingHashes,
|
|
saveTmpImagesPath: saveTmpImagesPath,
|
|
hashType: hashType,
|
|
}
|
|
}
|
|
|
|
// ImageMatches is a testing helper, which accepts image as reader, calculates
|
|
// hash and compares it with a hash saved to testdata/test-hashes folder.
|
|
func (m *ImageHashCacheMatcher) ImageMatches(t *testing.T, img io.Reader, key string, maxDistance float32) {
|
|
t.Helper()
|
|
|
|
// Read image in memory
|
|
buf, err := io.ReadAll(img)
|
|
require.NoError(t, err)
|
|
|
|
// Save tmp image if requested
|
|
m.saveTmpImage(t, key, buf)
|
|
|
|
// Calculate hash using shared helper
|
|
sourceHash := m.calculateHash(t, buf)
|
|
|
|
// Calculate image hash path (create folder if missing)
|
|
hashPath := m.makeTargetPath(t, m.hashesPath, t.Name(), key, "hash")
|
|
|
|
// Try to read or create the hash file
|
|
f, err := os.Open(hashPath)
|
|
if os.IsNotExist(err) {
|
|
// If the hash file does not exist, and we are not allowed to create it, fail
|
|
if !m.createMissingHashes {
|
|
require.NoError(
|
|
t, err,
|
|
"failed to read target hash from %s, use %s=true to create it, %s=/some/path to check resulting images",
|
|
hashPath,
|
|
createMissingHashesEnv,
|
|
saveTmpImagesPathEnv,
|
|
)
|
|
}
|
|
|
|
// Create missing hash file
|
|
h, hashErr := os.Create(hashPath)
|
|
require.NoError(t, hashErr, "failed to create target hash file %s", hashPath)
|
|
defer h.Close()
|
|
|
|
// Dump calculated source hash to this hash file
|
|
hashErr = sourceHash.Dump(h)
|
|
require.NoError(t, hashErr, "failed to write target hash to %s", hashPath)
|
|
|
|
t.Logf("Created missing hash in %s", hashPath)
|
|
return
|
|
}
|
|
|
|
// Otherwise, if there is no error or error is something else
|
|
require.NoError(t, err)
|
|
|
|
// Load image hash from hash file
|
|
targetHash, err := LoadImageHash(f)
|
|
require.NoError(t, err, "failed to load target hash from %s", hashPath)
|
|
|
|
// Ensure distance is OK
|
|
distance, err := sourceHash.Distance(targetHash)
|
|
require.NoError(t, err, "failed to calculate hash distance for %s", key)
|
|
|
|
require.LessOrEqual(t, distance, maxDistance, "image hashes are too different for %s: distance %f", key, distance)
|
|
}
|
|
|
|
// calculateHash converts image data to RGBA using VIPS and calculates hash
|
|
func (m *ImageHashCacheMatcher) calculateHash(t *testing.T, buf []byte) *ImageHash {
|
|
t.Helper()
|
|
|
|
// Calculate hash
|
|
hash, err := NewImageHashFromBytes(buf, m.hashType)
|
|
require.NoError(t, err)
|
|
|
|
return hash
|
|
}
|
|
|
|
// makeTargetPath creates the target directory and returns file path for saving
|
|
// the image or hash.
|
|
func (m *ImageHashCacheMatcher) makeTargetPath(
|
|
t *testing.T,
|
|
base, folder, filename, ext string,
|
|
) string {
|
|
t.Helper()
|
|
|
|
// Create the target directory if it doesn't exist
|
|
targetDir := path.Join(base, folder)
|
|
err := os.MkdirAll(targetDir, 0750)
|
|
require.NoError(t, err, "failed to create %s target directory", targetDir)
|
|
|
|
// Replace the extension with the detected one
|
|
filename = filename + "." + ext
|
|
|
|
// Create the target file
|
|
targetPath := path.Join(targetDir, filename)
|
|
|
|
return targetPath
|
|
}
|
|
|
|
// saveTmpImage saves the provided image data to a temporary file
|
|
func (m *ImageHashCacheMatcher) saveTmpImage(t *testing.T, key string, buf []byte) {
|
|
t.Helper()
|
|
|
|
if m.saveTmpImagesPath == "" {
|
|
return
|
|
}
|
|
|
|
// Detect the image type to get the correct extension
|
|
ext, err := imagetype.Detect(bytes.NewReader(buf), "", "")
|
|
require.NoError(t, err)
|
|
|
|
// Put all the test images into the same folder regardless of
|
|
// the test structure (hierarchy).
|
|
tmpImageFileName := strings.ReplaceAll(filepath.Join(t.Name(), key), "/", "_")
|
|
|
|
targetPath := m.makeTargetPath(t, m.saveTmpImagesPath, "", tmpImageFileName, ext.String())
|
|
|
|
targetFile, err := os.Create(targetPath)
|
|
require.NoError(t, err, "failed to create %s target file %s", saveTmpImagesPathEnv, targetPath)
|
|
defer targetFile.Close()
|
|
|
|
// Write the image data to the file
|
|
_, err = io.Copy(targetFile, bytes.NewReader(buf))
|
|
require.NoError(t, err, "failed to write to %s target file %s", saveTmpImagesPathEnv, targetPath)
|
|
|
|
t.Logf("Saved temporary image to %s", targetPath)
|
|
}
|