📝 新增完整项目文档、测试套件与构建配置

This commit is contained in:
2026-02-11 22:00:05 +08:00
parent 121c165d62
commit 04dfc9e8e0
10 changed files with 1032 additions and 85 deletions

227
tests/test_optimization.py Normal file
View File

@@ -0,0 +1,227 @@
"""Tests for imageOptimization function."""
import pytest
from pathlib import Path
from unittest.mock import Mock, MagicMock, patch
from PIL import Image
import numpy as np
from pptopic.lib import imageOptimization
class TestImageOptimization:
"""Test imageOptimization function."""
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_with_file_path(self, mock_last_file, mock_subprocess,
mock_resize, mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with file path input."""
mock_img = MagicMock()
mock_img.size = (100, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.array([[[255, 0, 0]]])
mock_cvtcolor.return_value = mock_array
mock_resized = np.array([[[255, 0, 0]]])
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png")
assert result is not None
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_with_pil_image(self, mock_last_file, mock_subprocess,
mock_resize, mock_cvtcolor, tmp_path):
"""Test imageOptimization with PIL Image input."""
mock_img = MagicMock()
mock_img.size = (100, 100)
mock_img.mode = 'RGB'
mock_array = np.array([[[255, 0, 0]]])
mock_cvtcolor.return_value = mock_array
mock_resized = np.array([[[255, 0, 0]]])
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(mock_img)
assert result is not None
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.shutil.copyfile')
def test_image_optimization_with_save_file(self, mock_copyfile, mock_subprocess,
mock_resize, mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with saveFile parameter."""
mock_img = MagicMock()
mock_img.size = (100, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.array([[[255, 0, 0]]])
mock_cvtcolor.return_value = mock_array
mock_resized = np.array([[[255, 0, 0]]])
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.lastFile') as mock_last_file:
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png", saveFile=tmp_path / "output.png")
assert result is None
mock_copyfile.assert_called_once()
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_with_max_width(self, mock_last_file, mock_subprocess,
mock_resize, mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with max_width parameter."""
mock_img = MagicMock()
mock_img.size = (200, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.zeros((100, 200, 3), dtype=np.uint8)
mock_cvtcolor.return_value = mock_array
mock_resized = np.zeros((50, 100, 3), dtype=np.uint8)
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png", max_width=100)
mock_resize.assert_called_once()
assert result is not None
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_with_max_height(self, mock_last_file, mock_subprocess,
mock_resize, mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with max_height parameter."""
mock_img = MagicMock()
mock_img.size = (100, 200)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.zeros((200, 100, 3), dtype=np.uint8)
mock_cvtcolor.return_value = mock_array
mock_resized = np.zeros((100, 50, 3), dtype=np.uint8)
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png", max_height=100)
mock_resize.assert_called_once()
assert result is not None
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.cv2.resize')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_with_fractional_width(self, mock_last_file, mock_subprocess,
mock_resize, mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with fractional max_width (< 1)."""
mock_img = MagicMock()
mock_img.size = (200, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.zeros((100, 200, 3), dtype=np.uint8)
mock_cvtcolor.return_value = mock_array
mock_resized = np.zeros((50, 100, 3), dtype=np.uint8)
mock_resize.return_value = mock_resized
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png", max_width=0.5)
mock_resize.assert_called_once()
assert result is not None
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.subprocess.run')
@patch('pptopic.lib.lastFile')
def test_image_optimization_without_engine(self, mock_last_file, mock_subprocess,
mock_cvtcolor, mock_open, tmp_path):
"""Test imageOptimization with engine=None."""
mock_img = MagicMock()
mock_img.size = (100, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.array([[[255, 0, 0]]])
mock_cvtcolor.return_value = mock_array
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with patch('pptopic.lib.Image.open', return_value=mock_img):
mock_last_file.return_value = tmp_path / "optimized.png"
result = imageOptimization(tmp_path / "test.png", engine=None)
mock_subprocess.assert_not_called()
assert result is not None
@patch('pptopic.lib.Image.open')
@patch('pptopic.lib.cv2.cvtColor')
@patch('pptopic.lib.subprocess.run')
def test_image_optimization_engine_failure(self, mock_subprocess, mock_cvtcolor, mock_open):
"""Test imageOptimization when engine subprocess fails."""
mock_img = MagicMock()
mock_img.size = (100, 100)
mock_img.mode = 'RGB'
mock_open.return_value = mock_img
mock_array = np.array([[[255, 0, 0]]])
mock_cvtcolor.return_value = mock_array
mock_subprocess.side_effect = Exception("Engine not found")
mock_pil_img = MagicMock()
with patch('pptopic.lib.Image.fromarray', return_value=mock_pil_img):
with pytest.raises(Exception):
imageOptimization("test.png", engine="pngquant", engine_conf="--quality=85")