mirror of
https://github.com/Belphemur/CBZOptimizer.git
synced 2025-10-13 20:18:52 +02:00

Still need to figure out the memory issues Consolidates image conversion logic into a dedicated method. This change streamlines the conversion process by centralizing the setting of converted image data, extension, and size. It also introduces a flag to track whether an image has been converted. The old resource cleanup has been removed since it is not needed anymore.
328 lines
8.0 KiB
Go
328 lines
8.0 KiB
Go
package webp
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"image/color"
|
|
"image/jpeg"
|
|
"image/png"
|
|
"sync"
|
|
"testing"
|
|
|
|
_ "image/jpeg"
|
|
|
|
_ "golang.org/x/image/webp"
|
|
|
|
"github.com/belphemur/CBZOptimizer/v2/internal/manga"
|
|
"github.com/belphemur/CBZOptimizer/v2/pkg/converter/constant"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func createTestImage(width, height int, format string) (image.Image, error) {
|
|
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
|
|
|
// Create a gradient pattern to ensure we have actual image data
|
|
for y := 0; y < height; y++ {
|
|
for x := 0; x < width; x++ {
|
|
img.Set(x, y, color.RGBA{
|
|
R: uint8((x * 255) / width),
|
|
G: uint8((y * 255) / height),
|
|
B: 100,
|
|
A: 255,
|
|
})
|
|
}
|
|
}
|
|
return img, nil
|
|
}
|
|
|
|
func encodeImage(img image.Image, format string) (*bytes.Buffer, string, error) {
|
|
buf := new(bytes.Buffer)
|
|
|
|
switch format {
|
|
case "jpeg", "jpg":
|
|
if err := jpeg.Encode(buf, img, &jpeg.Options{Quality: 85}); err != nil {
|
|
return nil, "", err
|
|
}
|
|
return buf, ".jpg", nil
|
|
case "webp":
|
|
PrepareEncoder()
|
|
if err := Encode(buf, img, 80); err != nil {
|
|
return nil, "", err
|
|
}
|
|
return buf, ".webp", nil
|
|
case "png":
|
|
fallthrough
|
|
default:
|
|
if err := png.Encode(buf, img); err != nil {
|
|
return nil, "", err
|
|
}
|
|
return buf, ".png", nil
|
|
}
|
|
}
|
|
|
|
func createTestPage(t *testing.T, index int, width, height int, format string) *manga.Page {
|
|
img, err := createTestImage(width, height, format)
|
|
require.NoError(t, err)
|
|
|
|
buf, ext, err := encodeImage(img, format)
|
|
require.NoError(t, err)
|
|
|
|
return &manga.Page{
|
|
Index: uint16(index),
|
|
Contents: buf,
|
|
Extension: ext,
|
|
Size: uint64(buf.Len()),
|
|
}
|
|
}
|
|
|
|
func validateConvertedImage(t *testing.T, page *manga.Page) {
|
|
require.NotNil(t, page.Contents)
|
|
require.Greater(t, page.Contents.Len(), 0)
|
|
|
|
// Try to decode the image
|
|
img, format, err := image.Decode(bytes.NewReader(page.Contents.Bytes()))
|
|
require.NoError(t, err, "Failed to decode converted image")
|
|
|
|
if page.Extension == ".webp" {
|
|
assert.Equal(t, "webp", format, "Expected WebP format")
|
|
}
|
|
|
|
require.NotNil(t, img)
|
|
bounds := img.Bounds()
|
|
assert.Greater(t, bounds.Dx(), 0, "Image width should be positive")
|
|
assert.Greater(t, bounds.Dy(), 0, "Image height should be positive")
|
|
}
|
|
|
|
// TestConverter_ConvertChapter tests the ConvertChapter method of the WebP converter.
|
|
// It verifies various scenarios including:
|
|
// - Converting single normal images
|
|
// - Converting multiple normal images
|
|
// - Converting tall images with split enabled
|
|
// - Handling tall images that exceed maximum height
|
|
//
|
|
// For each test case it validates:
|
|
// - Proper error handling
|
|
// - Expected number of output pages
|
|
// - Correct page ordering
|
|
// - Split page handling and indexing
|
|
// - Progress callback behavior
|
|
//
|
|
// The test uses different image dimensions and split settings to ensure
|
|
// the converter handles all cases correctly while maintaining proper
|
|
// progress reporting and page ordering.
|
|
func TestConverter_ConvertChapter(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pages []*manga.Page
|
|
split bool
|
|
expectSplit bool
|
|
expectError bool
|
|
numExpected int
|
|
}{
|
|
{
|
|
name: "Single normal image",
|
|
pages: []*manga.Page{createTestPage(t, 1, 800, 1200, "jpeg")},
|
|
split: false,
|
|
expectSplit: false,
|
|
numExpected: 1,
|
|
},
|
|
{
|
|
name: "Multiple normal images",
|
|
pages: []*manga.Page{
|
|
createTestPage(t, 1, 800, 1200, "png"),
|
|
createTestPage(t, 2, 800, 1200, "jpeg"),
|
|
},
|
|
split: false,
|
|
expectSplit: false,
|
|
numExpected: 2,
|
|
},
|
|
{
|
|
name: "Tall image with split enabled",
|
|
pages: []*manga.Page{createTestPage(t, 1, 800, 5000, "jpeg")},
|
|
split: true,
|
|
expectSplit: true,
|
|
numExpected: 3, // Based on cropHeight of 2000
|
|
},
|
|
{
|
|
name: "Tall image without split",
|
|
pages: []*manga.Page{createTestPage(t, 1, 800, webpMaxHeight+100, "png")},
|
|
split: false,
|
|
expectError: true,
|
|
numExpected: 1,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
converter := New()
|
|
err := converter.PrepareConverter()
|
|
require.NoError(t, err)
|
|
|
|
chapter := &manga.Chapter{
|
|
Pages: tt.pages,
|
|
}
|
|
|
|
var progressMutex sync.Mutex
|
|
var lastProgress uint32
|
|
progress := func(message string, current uint32, total uint32) {
|
|
progressMutex.Lock()
|
|
defer progressMutex.Unlock()
|
|
assert.GreaterOrEqual(t, current, lastProgress, "Progress should never decrease")
|
|
lastProgress = current
|
|
assert.LessOrEqual(t, current, total, "Current progress should not exceed total")
|
|
}
|
|
|
|
convertedChapter, err := converter.ConvertChapter(chapter, 80, tt.split, progress)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
if convertedChapter != nil {
|
|
assert.LessOrEqual(t, len(convertedChapter.Pages), tt.numExpected)
|
|
}
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, convertedChapter)
|
|
assert.Len(t, convertedChapter.Pages, tt.numExpected)
|
|
|
|
// Validate all converted images
|
|
for _, page := range convertedChapter.Pages {
|
|
validateConvertedImage(t, page)
|
|
}
|
|
|
|
// Verify page order
|
|
for i := 1; i < len(convertedChapter.Pages); i++ {
|
|
prevPage := convertedChapter.Pages[i-1]
|
|
currPage := convertedChapter.Pages[i]
|
|
|
|
if prevPage.Index == currPage.Index {
|
|
assert.Less(t, prevPage.SplitPartIndex, currPage.SplitPartIndex,
|
|
"Split parts should be in ascending order for page %d", prevPage.Index)
|
|
} else {
|
|
assert.Less(t, prevPage.Index, currPage.Index,
|
|
"Pages should be in ascending order")
|
|
}
|
|
}
|
|
|
|
if tt.expectSplit {
|
|
splitFound := false
|
|
for _, page := range convertedChapter.Pages {
|
|
if page.IsSplitted {
|
|
splitFound = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, splitFound, "Expected to find at least one split page")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConverter_convertPage(t *testing.T) {
|
|
converter := New()
|
|
err := converter.PrepareConverter()
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
format string
|
|
isToBeConverted bool
|
|
expectWebP bool
|
|
}{
|
|
{
|
|
name: "Convert PNG to WebP",
|
|
format: "png",
|
|
isToBeConverted: true,
|
|
expectWebP: true,
|
|
},
|
|
{
|
|
name: "Already WebP",
|
|
format: "webp",
|
|
isToBeConverted: true,
|
|
expectWebP: true,
|
|
},
|
|
{
|
|
name: "Skip conversion",
|
|
format: "png",
|
|
isToBeConverted: false,
|
|
expectWebP: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
page := createTestPage(t, 1, 100, 100, tt.format)
|
|
img, err := createTestImage(100, 100, tt.format)
|
|
require.NoError(t, err)
|
|
container := manga.NewContainer(page, img, tt.format, tt.isToBeConverted)
|
|
|
|
converted, err := converter.convertPage(container, 80)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, converted)
|
|
|
|
if tt.expectWebP {
|
|
assert.Equal(t, ".webp", converted.Page.Extension)
|
|
validateConvertedImage(t, converted.Page)
|
|
} else {
|
|
assert.NotEqual(t, ".webp", converted.Page.Extension)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConverter_checkPageNeedsSplit(t *testing.T) {
|
|
converter := New()
|
|
|
|
tests := []struct {
|
|
name string
|
|
imageHeight int
|
|
split bool
|
|
expectSplit bool
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Normal height",
|
|
imageHeight: 1000,
|
|
split: true,
|
|
expectSplit: false,
|
|
},
|
|
{
|
|
name: "Height exceeds max with split enabled",
|
|
imageHeight: 5000,
|
|
split: true,
|
|
expectSplit: true,
|
|
},
|
|
{
|
|
name: "Height exceeds webp max without split",
|
|
imageHeight: webpMaxHeight + 100,
|
|
split: false,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
page := createTestPage(t, 1, 800, tt.imageHeight, "jpeg")
|
|
|
|
needsSplit, img, format, err := converter.checkPageNeedsSplit(page, tt.split)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, img)
|
|
assert.NotEmpty(t, format)
|
|
assert.Equal(t, tt.expectSplit, needsSplit)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConverter_Format(t *testing.T) {
|
|
converter := New()
|
|
assert.Equal(t, constant.WebP, converter.Format())
|
|
}
|