A:增加工具类

This commit is contained in:
jimmy committed 2025-07-26 14:33:24 +08:00
1 parent 1c97bfb181
commit 6decd94b1c
42 files changed
+1544 -4787

No files matched your search

+7 -7
View File
@@ -13,7 +13,7 @@ type ExifData map[string]interface{}
// GetExifData 获取 EXIF 数据
func GetExifData(ctx context.Context, input string) (ExifData, error) {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return nil, err
}
@@ -37,7 +37,7 @@ func GetExifData(ctx context.Context, input string) (ExifData, error) {
// GetExifField 获取特定的 EXIF 字段
func GetExifField(ctx context.Context, input string, fields ...string) (map[string]string, error) {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return nil, err
}
@@ -72,7 +72,7 @@ func GetExifField(ctx context.Context, input string, fields ...string) (map[stri
// RemoveExifData 移除 EXIF 数据
func RemoveExifData(ctx context.Context, input string, backup bool) error {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return err
}
@@ -92,7 +92,7 @@ func RemoveExifData(ctx context.Context, input string, backup bool) error {
// CopyExifData 复制 EXIF 数据从一个文件到另一个文件
func CopyExifData(ctx context.Context, source, target string) error {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return err
}
@@ -113,7 +113,7 @@ func CopyExifData(ctx context.Context, source, target string) error {
// SetExifField 设置 EXIF 字段
func SetExifField(ctx context.Context, input string, fields map[string]string) error {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return err
}
@@ -133,7 +133,7 @@ func SetExifField(ctx context.Context, input string, fields map[string]string) e
// IsExifToolAvailable 检查 ExifTool 是否可用
func IsExifToolAvailable() bool {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return false
}
return utils.ExifToolPath != ""
@@ -141,6 +141,6 @@ func IsExifToolAvailable() bool {
// GetToolPaths 获取工具路径(用于调试)
func GetToolPaths() (imageMagick, exifTool string) {
utils.EnsureInitialized()
utils.EnsureInitialized(nil, nil)
return utils.ImageMagickPath, utils.ExifToolPath
}
+623
View File
@@ -0,0 +1,623 @@
package tools
import (
"context"
"os"
"path/filepath"
"reflect"
"testing"
"time"
)
// 测试用的图片文件路径
var testImages = []string{
"../../img/cyberpunk2077_world_third@3x.webp",
"../../img/image-1.JPG",
"../../img/image-1-1.JPG",
"../../img/image-2.JPG",
"../../img/image-3.JPG",
"../../img/image-4.JPG",
"../../img/Snipaste_2022-12-17_20-13-15.png",
}
// TestIsExifToolAvailable 测试ExifTool是否可用
func TestIsExifToolAvailable(t *testing.T) {
available := IsExifToolAvailable()
if !available {
t.Skip("ExifTool not available, skipping EXIF tests")
}
t.Logf("ExifTool is available")
}
// TestGetToolPaths 测试获取工具路径
func TestGetToolPaths(t *testing.T) {
imageMagick, exifTool := GetToolPaths()
t.Logf("ImageMagick path: %s", imageMagick)
t.Logf("ExifTool path: %s", exifTool)
if exifTool == "" {
t.Skip("ExifTool path is empty, skipping remaining tests")
}
}
// TestGetExifData 测试获取EXIF数据
func TestGetExifData(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
for _, imgPath := range testImages {
t.Run(filepath.Base(imgPath), func(t *testing.T) {
// 检查文件是否存在
if _, err := os.Stat(imgPath); os.IsNotExist(err) {
t.Skipf("Test image %s does not exist", imgPath)
return
}
data, err := GetExifData(ctx, imgPath)
if err != nil {
t.Logf("Warning: Failed to get EXIF data for %s: %v", imgPath, err)
return
}
if len(data) == 0 {
t.Logf("No EXIF data found for %s", imgPath)
return
}
t.Logf("EXIF data for %s:", imgPath)
for key, value := range data {
t.Logf(" %s: %v", key, value)
}
// 验证常见的EXIF字段
commonFields := []string{"FileName", "FileSize", "ImageWidth", "ImageHeight"}
for _, field := range commonFields {
if _, exists := data[field]; exists {
t.Logf("Found expected field: %s", field)
}
}
})
}
}
// TestGetExifDataWithInvalidFile 测试无效文件的情况
func TestGetExifDataWithInvalidFile(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
// 测试不存在的文件
_, err := GetExifData(ctx, "nonexistent.jpg")
if err == nil {
t.Error("Expected error for nonexistent file, but got nil")
}
t.Logf("Expected error for nonexistent file: %v", err)
// 测试空路径
_, err = GetExifData(ctx, "")
if err == nil {
t.Error("Expected error for empty path, but got nil")
}
t.Logf("Expected error for empty path: %v", err)
}
// TestGetExifField 测试获取特定EXIF字段
func TestGetExifField(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
// 找一个存在的图片文件进行测试
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil {
testImg = imgPath
break
}
}
if testImg == "" {
t.Skip("No test images available")
}
t.Run("SingleField", func(t *testing.T) {
fields, err := GetExifField(ctx, testImg, "ImageWidth")
if err != nil {
t.Logf("Failed to get single field: %v", err)
return
}
t.Logf("Single field result: %+v", fields)
})
t.Run("MultipleFields", func(t *testing.T) {
fields, err := GetExifField(ctx, testImg, "ImageWidth", "ImageHeight", "FileSize")
if err != nil {
t.Logf("Failed to get multiple fields: %v", err)
return
}
t.Logf("Multiple fields result: %+v", fields)
})
t.Run("NonexistentField", func(t *testing.T) {
fields, err := GetExifField(ctx, testImg, "NonexistentField")
if err != nil {
t.Logf("Error getting nonexistent field (expected): %v", err)
} else {
t.Logf("Nonexistent field result: %+v", fields)
}
})
}
// TestRemoveExifData 测试移除EXIF数据
func TestRemoveExifData(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
// 找一个JPEG文件作为测试目标(因为JPEG通常有EXIF数据)
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil && filepath.Ext(imgPath) == ".JPG" {
testImg = imgPath
break
}
}
if testImg == "" {
t.Skip("No JPEG test images available")
}
// 创建测试文件的副本
tempFile := filepath.Join(os.TempDir(), "test_remove_exif.jpg")
defer os.Remove(tempFile)
// 复制原文件
if err := copyFile(testImg, tempFile); err != nil {
t.Fatalf("Failed to create test file copy: %v", err)
}
t.Run("RemoveWithBackup", func(t *testing.T) {
// 获取移除前的EXIF数据
beforeData, err := GetExifData(ctx, tempFile)
if err != nil {
t.Logf("No EXIF data before removal: %v", err)
} else {
t.Logf("EXIF data before removal: %d fields", len(beforeData))
}
// 移除EXIF数据(保留备份)
err = RemoveExifData(ctx, tempFile, true)
if err != nil {
t.Fatalf("Failed to remove EXIF data with backup: %v", err)
}
// 检查备份文件是否存在
backupFile := tempFile + "_original"
if _, err := os.Stat(backupFile); err == nil {
defer os.Remove(backupFile)
t.Logf("Backup file created successfully")
}
// 获取移除后的EXIF数据
afterData, err := GetExifData(ctx, tempFile)
if err != nil {
t.Logf("No EXIF data after removal (expected): %v", err)
} else {
t.Logf("EXIF data after removal: %d fields", len(afterData))
}
})
t.Run("RemoveWithoutBackup", func(t *testing.T) {
// 重新创建测试文件
if err := copyFile(testImg, tempFile); err != nil {
t.Fatalf("Failed to recreate test file: %v", err)
}
// 移除EXIF数据(不保留备份)
err := RemoveExifData(ctx, tempFile, false)
if err != nil {
t.Fatalf("Failed to remove EXIF data without backup: %v", err)
}
// 检查备份文件不存在
backupFile := tempFile + "_original"
if _, err := os.Stat(backupFile); err == nil {
t.Error("Backup file should not exist when backup=false")
os.Remove(backupFile)
}
})
t.Run("RemoveFromNonexistentFile", func(t *testing.T) {
err := RemoveExifData(ctx, "nonexistent.jpg", false)
if err == nil {
t.Error("Expected error for nonexistent file, but got nil")
}
t.Logf("Expected error for nonexistent file: %v", err)
})
}
// TestCopyExifData 测试复制EXIF数据
func TestCopyExifData(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
// 找两个不同的图片文件
var sourceImg, targetImg string
jpegCount := 0
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil {
if filepath.Ext(imgPath) == ".JPG" {
jpegCount++
if sourceImg == "" {
sourceImg = imgPath
} else if targetImg == "" && imgPath != sourceImg {
targetImg = imgPath
break
}
}
}
}
if sourceImg == "" || targetImg == "" {
t.Skip("Need at least 2 JPEG test images")
}
// 创建目标文件的副本
tempTarget := filepath.Join(os.TempDir(), "test_copy_target.jpg")
defer os.Remove(tempTarget)
if err := copyFile(targetImg, tempTarget); err != nil {
t.Fatalf("Failed to create target file copy: %v", err)
}
t.Run("CopyValidFiles", func(t *testing.T) {
// 获取源文件的EXIF数据
sourceData, err := GetExifData(ctx, sourceImg)
if err != nil {
t.Logf("No EXIF data in source file: %v", err)
}
// 复制EXIF数据
err = CopyExifData(ctx, sourceImg, tempTarget)
if err != nil {
t.Fatalf("Failed to copy EXIF data: %v", err)
}
// 获取目标文件的EXIF数据
targetData, err := GetExifData(ctx, tempTarget)
if err != nil {
t.Logf("No EXIF data in target file after copy: %v", err)
} else {
t.Logf("Successfully copied EXIF data, target now has %d fields", len(targetData))
// 比较一些关键字段
if sourceData != nil {
for _, field := range []string{"Make", "Model", "DateTime"} {
if sourceVal, exists := sourceData[field]; exists {
if targetVal, exists := targetData[field]; exists {
if reflect.DeepEqual(sourceVal, targetVal) {
t.Logf("Field %s copied correctly: %v", field, sourceVal)
} else {
t.Logf("Field %s differs: source=%v, target=%v", field, sourceVal, targetVal)
}
}
}
}
}
}
})
t.Run("CopyFromNonexistentSource", func(t *testing.T) {
err := CopyExifData(ctx, "nonexistent.jpg", tempTarget)
if err == nil {
t.Error("Expected error for nonexistent source file, but got nil")
}
t.Logf("Expected error for nonexistent source: %v", err)
})
t.Run("CopyToNonexistentTarget", func(t *testing.T) {
err := CopyExifData(ctx, sourceImg, "nonexistent.jpg")
if err == nil {
t.Error("Expected error for nonexistent target file, but got nil")
}
t.Logf("Expected error for nonexistent target: %v", err)
})
}
// TestSetExifField 测试设置EXIF字段
func TestSetExifField(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
// 找一个JPEG文件作为测试目标
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil && filepath.Ext(imgPath) == ".JPG" {
testImg = imgPath
break
}
}
if testImg == "" {
t.Skip("No JPEG test images available")
}
// 创建测试文件的副本
tempFile := filepath.Join(os.TempDir(), "test_set_exif.jpg")
defer os.Remove(tempFile)
if err := copyFile(testImg, tempFile); err != nil {
t.Fatalf("Failed to create test file copy: %v", err)
}
t.Run("SetSingleField", func(t *testing.T) {
fields := map[string]string{
"Artist": "Test Artist",
}
err := SetExifField(ctx, tempFile, fields)
if err != nil {
t.Fatalf("Failed to set single field: %v", err)
}
// 验证字段是否设置成功
data, err := GetExifData(ctx, tempFile)
if err != nil {
t.Fatalf("Failed to get EXIF data after setting: %v", err)
}
if artist, exists := data["Artist"]; exists {
t.Logf("Artist field set successfully: %v", artist)
} else {
t.Error("Artist field not found after setting")
}
})
t.Run("SetMultipleFields", func(t *testing.T) {
currentTime := time.Now().Format("2006:01:02 15:04:05")
fields := map[string]string{
"Artist": "Test Artist Updated",
"Comment": "Test Comment",
"DateTime": currentTime,
}
err := SetExifField(ctx, tempFile, fields)
if err != nil {
t.Fatalf("Failed to set multiple fields: %v", err)
}
// 验证字段是否设置成功
data, err := GetExifData(ctx, tempFile)
if err != nil {
t.Fatalf("Failed to get EXIF data after setting: %v", err)
}
for key, _ := range fields {
if actualValue, exists := data[key]; exists {
t.Logf("Field %s set successfully: %v", key, actualValue)
} else {
t.Errorf("Field %s not found after setting", key)
}
}
})
t.Run("SetFieldWithSpecialCharacters", func(t *testing.T) {
fields := map[string]string{
"Comment": "测试中文注释 & Special chars: !@#$%",
}
err := SetExifField(ctx, tempFile, fields)
if err != nil {
t.Logf("Failed to set field with special characters (may be expected): %v", err)
} else {
t.Logf("Successfully set field with special characters")
}
})
t.Run("SetFieldOnNonexistentFile", func(t *testing.T) {
fields := map[string]string{
"Artist": "Test Artist",
}
err := SetExifField(ctx, "nonexistent.jpg", fields)
if err == nil {
t.Error("Expected error for nonexistent file, but got nil")
}
t.Logf("Expected error for nonexistent file: %v", err)
})
}
// TestContextCancellation 测试上下文取消
func TestContextCancellation(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
// 找一个存在的图片文件
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil {
testImg = imgPath
break
}
}
if testImg == "" {
t.Skip("No test images available")
}
t.Run("CancelledContext", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel() // 立即取消上下文
_, err := GetExifData(ctx, testImg)
if err != nil {
t.Logf("Operation correctly failed with cancelled context: %v", err)
} else {
t.Log("Operation completed despite cancelled context")
}
})
t.Run("TimeoutContext", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
_, err := GetExifData(ctx, testImg)
if err != nil {
t.Logf("Operation correctly failed with timeout context: %v", err)
} else {
t.Log("Operation completed within timeout")
}
})
}
// TestDifferentImageFormats 测试不同图片格式
func TestDifferentImageFormats(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
ctx := context.Background()
formatTests := map[string][]string{
"JPEG": {".JPG", ".jpg", ".jpeg"},
"PNG": {".PNG", ".png"},
"WEBP": {".webp"},
}
for format, extensions := range formatTests {
t.Run(format, func(t *testing.T) {
found := false
for _, imgPath := range testImages {
ext := filepath.Ext(imgPath)
for _, validExt := range extensions {
if ext == validExt {
if _, err := os.Stat(imgPath); err == nil {
found = true
data, err := GetExifData(ctx, imgPath)
if err != nil {
t.Logf("No EXIF data for %s format (%s): %v", format, imgPath, err)
} else {
t.Logf("%s format (%s) has %d EXIF fields", format, imgPath, len(data))
}
break
}
}
}
if found {
break
}
}
if !found {
t.Logf("No %s format images found for testing", format)
}
})
}
}
// 辅助函数:复制文件
func copyFile(src, dst string) error {
input, err := os.ReadFile(src)
if err != nil {
return err
}
return os.WriteFile(dst, input, 0644)
}
// BenchmarkGetExifData 性能测试
func BenchmarkGetExifData(b *testing.B) {
if !IsExifToolAvailable() {
b.Skip("ExifTool not available")
}
// 找一个存在的图片文件
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil {
testImg = imgPath
break
}
}
if testImg == "" {
b.Skip("No test images available")
}
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := GetExifData(ctx, testImg)
if err != nil {
b.Fatalf("Benchmark failed: %v", err)
}
}
}
// TestConcurrentAccess 并发访问测试
func TestConcurrentAccess(t *testing.T) {
if !IsExifToolAvailable() {
t.Skip("ExifTool not available")
}
// 找一个存在的图片文件
var testImg string
for _, imgPath := range testImages {
if _, err := os.Stat(imgPath); err == nil {
testImg = imgPath
break
}
}
if testImg == "" {
t.Skip("No test images available")
}
ctx := context.Background()
const numGoroutines = 5
done := make(chan bool, numGoroutines)
errors := make(chan error, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(id int) {
defer func() { done <- true }()
_, err := GetExifData(ctx, testImg)
if err != nil {
errors <- err
return
}
t.Logf("Goroutine %d completed successfully", id)
}(i)
}
// 等待所有goroutine完成
for i := 0; i < numGoroutines; i++ {
select {
case <-done:
// 成功完成
case err := <-errors:
t.Errorf("Concurrent access error: %v", err)
case <-time.After(30 * time.Second):
t.Error("Concurrent access test timed out")
}
}
}
+10 -2
View File
@@ -18,7 +18,7 @@ type ImageInfo struct {
// ConvertImage 使用 ImageMagick 转换图片
// 示例: ConvertImage(ctx, "input.jpg", "output.png", "-quality", "90")
func ConvertImage(ctx context.Context, input, output string, options ...string) error {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return err
}
@@ -35,12 +35,20 @@ func ConvertImage(ctx context.Context, input, output string, options ...string)
// ResizeImage 调整图片大小
func ResizeImage(ctx context.Context, input, output string, width, height int) error {
// 添加参数验证
if width <= 0 || height <= 0 {
return fmt.Errorf("invalid dimensions: width and height must be positive, got width=%d, height=%d", width, height)
}
size := fmt.Sprintf("%dx%d", width, height)
return ConvertImage(ctx, input, output, "-resize", size)
}
// ResizeImageKeepAspect 按比例调整图片大小(保持宽高比)
func ResizeImageKeepAspect(ctx context.Context, input, output string, maxWidth, maxHeight int) error {
// 添加参数验证
if maxWidth <= 0 || maxHeight <= 0 {
return fmt.Errorf("invalid dimensions: maxWidth and maxHeight must be positive, got maxWidth=%d, maxHeight=%d", maxWidth, maxHeight)
}
size := fmt.Sprintf("%dx%d>", maxWidth, maxHeight)
return ConvertImage(ctx, input, output, "-resize", size)
}
@@ -58,7 +66,7 @@ func RotateImage(ctx context.Context, input, output string, degrees float64) err
// IsImageMagickAvailable 检查 ImageMagick 是否可用
func IsImageMagickAvailable() bool {
if err := utils.EnsureInitialized(); err != nil {
if err := utils.EnsureInitialized(nil, nil); err != nil {
return false
}
return utils.ImageMagickPath != ""
@@ -0,0 +1,612 @@
package tools
import (
"context"
"fmt"
"os"
"path/filepath"
"rear/internal/utils"
"testing"
"time"
)
const (
// 测试图片基础路径
testImageBasePath = "D:\\go-argus\\rear\\internal\\img"
// 测试输出目录
testOutputDir = "test_output"
)
// 测试用的图片文件
var testImages1 = []string{
"cyberpunk2077_world_third@3x.webp",
"image-1.JPG",
"image-1-1.JPG",
"image-2.JPG",
"image-3.JPG",
"image-4.JPG",
"img_1.jpg",
"img_2.jpg",
"Snipaste_2022-12-17_20-13-15.png",
}
// setupTest 设置测试环境
func setupTest(t *testing.T) (string, func()) {
// 创建测试输出目录
outputDir := filepath.Join(".", testOutputDir)
err := os.MkdirAll(outputDir, 0755)
if err != nil {
t.Fatalf("Failed to create test output directory: %v", err)
}
// 返回清理函数
cleanup := func() {
os.RemoveAll(outputDir)
}
return outputDir, cleanup
}
// getTestImagePath 获取测试图片的完整路径
func getTestImagePath(imageName string) string {
return filepath.Join(testImageBasePath, imageName)
}
// fileExists 检查文件是否存在
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// TestIsImageMagickAvailable 测试 ImageMagick 是否可用
func TestIsImageMagickAvailable(t *testing.T) {
available := IsImageMagickAvailable()
if !available {
t.Skip("ImageMagick is not available, skipping ImageMagick tests")
}
t.Log("ImageMagick is available")
}
// TestConvertImage 测试基础图片转换功能
func TestConvertImage(t *testing.T) {
dir := "D:\\go-argus\\rear\\output\\tools"
err := utils.Initialize(nil, &dir)
if err != nil {
return
}
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
tests := []struct {
name string
input string
output string
options []string
wantErr bool
}{
{
name: "Convert JPG to PNG",
input: getTestImagePath("image-1.JPG"),
output: filepath.Join(outputDir, "converted_jpg_to_png.png"),
options: []string{"-quality", "90"},
wantErr: false,
},
{
name: "Convert WebP to JPG",
input: getTestImagePath("cyberpunk2077_world_third@3x.webp"),
output: filepath.Join(outputDir, "converted_webp_to_jpg.jpg"),
options: []string{"-quality", "85"},
wantErr: false,
},
{
name: "Convert PNG to WebP",
input: getTestImagePath("Snipaste_2022-12-17_20-13-15.png"),
output: filepath.Join(outputDir, "converted_png_to_webp.webp"),
options: []string{"-quality", "80"},
wantErr: false,
},
{
name: "Invalid input file",
input: "nonexistent.jpg",
output: filepath.Join(outputDir, "should_not_exist.png"),
options: []string{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 检查输入文件是否存在(除了测试错误情况)
if !tt.wantErr && !fileExists(tt.input) {
t.Skipf("Input file %s does not exist", tt.input)
}
err := ConvertImage(ctx, tt.input, tt.output, tt.options...)
if tt.wantErr {
if err == nil {
t.Errorf("ConvertImage() expected error, got nil")
}
return
}
if err != nil {
t.Errorf("ConvertImage() error = %v", err)
return
}
// 检查输出文件是否存在
if !fileExists(tt.output) {
t.Errorf("Output file %s was not created", tt.output)
}
})
}
}
// TestResizeImage 测试调整图片大小功能
func TestResizeImage(t *testing.T) {
dir := "D:\\go-argus\\rear\\output\\tools"
err := utils.Initialize(nil, &dir)
if err != nil {
return
}
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
tests := []struct {
name string
input string
width int
height int
wantErr bool
}{
{
name: "Resize to 800x600",
input: getTestImagePath("image-1.JPG"),
width: 800,
height: 600,
wantErr: false,
},
{
name: "Resize to 1920x1080",
input: getTestImagePath("img_1.jpg"),
width: 1920,
height: 1080,
wantErr: false,
},
{
name: "Resize to small size 100x100",
input: getTestImagePath("Snipaste_2022-12-17_20-13-15.png"),
width: 100,
height: 100,
wantErr: false,
},
{
name: "Invalid dimensions",
input: getTestImagePath("image-1.JPG"),
width: -100,
height: -100,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !fileExists(tt.input) {
t.Skipf("Input file %s does not exist", tt.input)
}
output := filepath.Join(outputDir, fmt.Sprintf("resized_%dx%d_%s", tt.width, tt.height, filepath.Base(tt.input)))
err := ResizeImage(ctx, tt.input, output, tt.width, tt.height)
if tt.wantErr {
if err == nil {
t.Errorf("ResizeImage() expected error, got nil")
}
return
}
if err != nil {
t.Errorf("ResizeImage() error = %v", err)
return
}
if !fileExists(output) {
t.Errorf("Output file %s was not created", output)
}
})
}
}
// TestResizeImageKeepAspect 测试按比例调整图片大小功能
func TestResizeImageKeepAspect(t *testing.T) {
dir := "D:\\go-argus\\rear\\output\\tools"
err := utils.Initialize(nil, &dir)
if err != nil {
return
}
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
tests := []struct {
name string
input string
maxWidth int
maxHeight int
wantErr bool
}{
{
name: "Keep aspect ratio 1920x1080",
input: getTestImagePath("image-2.JPG"),
maxWidth: 1920,
maxHeight: 1080,
wantErr: false,
},
{
name: "Keep aspect ratio 800x600",
input: getTestImagePath("cyberpunk2077_world_third@3x.webp"),
maxWidth: 800,
maxHeight: 600,
wantErr: false,
},
{
name: "Keep aspect ratio thumbnail 200x200",
input: getTestImagePath("img_2.jpg"),
maxWidth: 200,
maxHeight: 200,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !fileExists(tt.input) {
t.Skipf("Input file %s does not exist", tt.input)
}
output := filepath.Join(outputDir, fmt.Sprintf("resized_aspect_%dx%d_%s", tt.maxWidth, tt.maxHeight, filepath.Base(tt.input)))
err := ResizeImageKeepAspect(ctx, tt.input, output, tt.maxWidth, tt.maxHeight)
if tt.wantErr {
if err == nil {
t.Errorf("ResizeImageKeepAspect() expected error, got nil")
}
return
}
if err != nil {
t.Errorf("ResizeImageKeepAspect() error = %v", err)
return
}
if !fileExists(output) {
t.Errorf("Output file %s was not created", output)
}
})
}
}
// TestCropImage 测试裁剪图片功能
func TestCropImage(t *testing.T) {
dir := "D:\\go-argus\\rear\\output\\tools"
err := utils.Initialize(nil, &dir)
if err != nil {
return
}
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
tests := []struct {
name string
input string
width int
height int
x int
y int
wantErr bool
}{
{
name: "Crop center 400x300",
input: getTestImagePath("image-3.JPG"),
width: 400,
height: 300,
x: 100,
y: 100,
wantErr: false,
},
{
name: "Crop square 500x500",
input: getTestImagePath("image-4.JPG"),
width: 500,
height: 500,
x: 50,
y: 50,
wantErr: false,
},
{
name: "Crop from origin",
input: getTestImagePath("Snipaste_2022-12-17_20-13-15.png"),
width: 200,
height: 200,
x: 0,
y: 0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !fileExists(tt.input) {
t.Skipf("Input file %s does not exist", tt.input)
}
output := filepath.Join(outputDir, fmt.Sprintf("cropped_%dx%d_%s", tt.width, tt.height, filepath.Base(tt.input)))
err := CropImage(ctx, tt.input, output, tt.width, tt.height, tt.x, tt.y)
if tt.wantErr {
if err == nil {
t.Errorf("CropImage() expected error, got nil")
}
return
}
if err != nil {
t.Errorf("CropImage() error = %v", err)
return
}
if !fileExists(output) {
t.Errorf("Output file %s was not created", output)
}
})
}
}
// TestRotateImage 测试旋转图片功能
func TestRotateImage(t *testing.T) {
dir := "D:\\go-argus\\rear\\output\\tools"
err := utils.Initialize(nil, &dir)
if err != nil {
return
}
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
tests := []struct {
name string
input string
degrees float64
wantErr bool
}{
{
name: "Rotate 90 degrees",
input: getTestImagePath("image-1-1.JPG"),
degrees: 90.0,
wantErr: false,
},
{
name: "Rotate 180 degrees",
input: getTestImagePath("img_1.jpg"),
degrees: 180.0,
wantErr: false,
},
{
name: "Rotate 270 degrees",
input: getTestImagePath("img_2.jpg"),
degrees: 270.0,
wantErr: false,
},
{
name: "Rotate 45 degrees",
input: getTestImagePath("Snipaste_2022-12-17_20-13-15.png"),
degrees: 45.0,
wantErr: false,
},
{
name: "Rotate negative degrees",
input: getTestImagePath("image-2.JPG"),
degrees: -90.0,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if !fileExists(tt.input) {
t.Skipf("Input file %s does not exist", tt.input)
}
output := filepath.Join(outputDir, fmt.Sprintf("rotated_%.0f_%s", tt.degrees, filepath.Base(tt.input)))
err := RotateImage(ctx, tt.input, output, tt.degrees)
if tt.wantErr {
if err == nil {
t.Errorf("RotateImage() expected error, got nil")
}
return
}
if err != nil {
t.Errorf("RotateImage() error = %v", err)
return
}
if !fileExists(output) {
t.Errorf("Output file %s was not created", output)
}
})
}
}
// TestContextCancellation1 测试上下文取消功能
func TestContextCancellation1(t *testing.T) {
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
// 创建一个会立即取消的上下文
ctx, cancel := context.WithCancel(context.Background())
cancel() // 立即取消
input := getTestImagePath("image-1.JPG")
if !fileExists(input) {
t.Skip("Test image does not exist")
}
output := filepath.Join(outputDir, "should_not_be_created.png")
err := ConvertImage(ctx, input, output, "-quality", "90")
if err == nil {
t.Error("Expected error due to context cancellation, got nil")
}
}
// TestBatchOperations 测试批量操作
func TestBatchOperations(t *testing.T) {
if !IsImageMagickAvailable() {
t.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(t)
defer cleanup()
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// 获取所有存在的测试图片
var existingImages []string
for _, imgName := range testImages1 {
imgPath := getTestImagePath(imgName)
if fileExists(imgPath) {
existingImages = append(existingImages, imgPath)
}
}
if len(existingImages) == 0 {
t.Skip("No test images found")
}
t.Logf("Processing %d images", len(existingImages))
// 批量转换为不同格式
for i, imgPath := range existingImages {
t.Run(fmt.Sprintf("BatchConvert_%d", i), func(t *testing.T) {
baseName := filepath.Base(imgPath)
ext := filepath.Ext(baseName)
nameWithoutExt := baseName[:len(baseName)-len(ext)]
// 转换为WebP
webpOutput := filepath.Join(outputDir, fmt.Sprintf("batch_%s.webp", nameWithoutExt))
err := ConvertImage(ctx, imgPath, webpOutput, "-quality", "80")
if err != nil {
t.Errorf("Failed to convert %s to WebP: %v", baseName, err)
}
// 创建缩略图
thumbnailOutput := filepath.Join(outputDir, fmt.Sprintf("thumbnail_%s.jpg", nameWithoutExt))
err = ResizeImageKeepAspect(ctx, imgPath, thumbnailOutput, 200, 200)
if err != nil {
t.Errorf("Failed to create thumbnail for %s: %v", baseName, err)
}
})
}
}
// BenchmarkConvertImage 性能测试
func BenchmarkConvertImage(b *testing.B) {
if !IsImageMagickAvailable() {
b.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(&testing.T{})
defer cleanup()
input := getTestImagePath("image-1.JPG")
if !fileExists(input) {
b.Skip("Test image does not exist")
}
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
output := filepath.Join(outputDir, fmt.Sprintf("bench_%d.png", i))
err := ConvertImage(ctx, input, output, "-quality", "90")
if err != nil {
b.Fatalf("ConvertImage failed: %v", err)
}
// 清理输出文件
os.Remove(output)
}
}
// BenchmarkResizeImage 性能测试
func BenchmarkResizeImage(b *testing.B) {
if !IsImageMagickAvailable() {
b.Skip("ImageMagick is not available")
}
outputDir, cleanup := setupTest(&testing.T{})
defer cleanup()
input := getTestImagePath("image-1.JPG")
if !fileExists(input) {
b.Skip("Test image does not exist")
}
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
output := filepath.Join(outputDir, fmt.Sprintf("bench_resize_%d.jpg", i))
err := ResizeImage(ctx, input, output, 800, 600)
if err != nil {
b.Fatalf("ResizeImage failed: %v", err)
}
// 清理输出文件
os.Remove(output)
}
}