Files
ETPlot/tests/test_generate_gallery.py
T
2026-04-23 12:56:59 +02:00

291 lines
8.1 KiB
Python

import os
from pathlib import Path
from unittest.mock import patch, MagicMock
import pytest
from gallery import (
convert_pdf_to_png,
needs_update,
build_gallery,
calculate_directory_stats,
format_file_size,
datetime_from_timestamp,
strftime_filter
)
from datetime import datetime
def test_format_file_size():
assert format_file_size(0) == "0 B"
assert format_file_size(1) == "1.0 B"
assert format_file_size(1024) == "1.0 KB"
assert format_file_size(1048576) == "1.0 MB"
assert format_file_size(1073741824) == "1.0 GB"
assert format_file_size(1099511627776) == "1.0 TB"
def test_format_file_size_edge_cases():
assert format_file_size(1023) == "1023.0 B"
assert format_file_size(1536) == "1.5 KB"
assert format_file_size(2621440) == "2.5 MB"
def test_needs_update_missing_target(tmp_path):
source = tmp_path / "source.txt"
target = tmp_path / "target.txt"
source.write_text("test content")
assert needs_update(source, target) is True
def test_needs_update_target_newer(tmp_path):
source = tmp_path / "source.txt"
target = tmp_path / "target.txt"
source.write_text("test")
target.write_text("test")
# Make target newer by modifying its timestamp
import time
time.sleep(0.1)
target.touch()
assert needs_update(source, target) is False
def test_needs_update_source_newer(tmp_path):
source = tmp_path / "source.txt"
target = tmp_path / "target.txt"
target.write_text("test")
import time
time.sleep(0.1)
source.write_text("test")
# Force different modification times with 31+ second buffer
target_time = target.stat().st_mtime
source_time = target_time + 40 # 40 seconds newer (> 30 second buffer)
os.utime(source, (source_time, source_time))
assert needs_update(source, target) is True
@patch('subprocess.run')
def test_convert_pdf_to_png_success(mock_run, tmp_path):
from gallery import GalleryConfig
pdf_path = tmp_path / "test.pdf"
png_path = tmp_path / "test.png"
pdf_path.write_text("fake pdf")
mock_run.return_value = MagicMock(returncode=0)
convert_pdf_to_png(pdf_path, GalleryConfig(tmp_path))
mock_run.assert_called_once()
call_args = mock_run.call_args[0][0]
assert call_args[0] == "convert"
assert str(pdf_path) in call_args
assert str(png_path) in call_args
@patch('subprocess.run')
def test_convert_pdf_to_png_already_exists_newer(mock_run, tmp_path):
from gallery import GalleryConfig
pdf_path = tmp_path / "test.pdf"
png_path = tmp_path / "test.png"
pdf_path.write_text("fake pdf")
png_path.write_text("fake png")
# Make PNG much newer than PDF using explicit time
pdf_time = pdf_path.stat().st_mtime
png_time = pdf_time + 100 # PNG is 100 seconds newer
os.utime(png_path, (png_time, png_time))
convert_pdf_to_png(pdf_path, GalleryConfig(tmp_path))
# Should not call subprocess since PNG is newer
mock_run.assert_not_called()
@patch('subprocess.run')
def test_convert_pdf_to_png_pdf_newer(mock_run, tmp_path):
from gallery import GalleryConfig
pdf_path = tmp_path / "test.pdf"
png_path = tmp_path / "test.png"
png_path.write_text("fake png")
import time
time.sleep(0.1)
pdf_path.write_text("fake pdf")
mock_run.return_value = MagicMock(returncode=0)
convert_pdf_to_png(pdf_path, GalleryConfig(tmp_path))
# Should call subprocess since PDF is newer
mock_run.assert_called_once()
def test_calculate_directory_stats_empty(tmp_path):
stats = calculate_directory_stats(tmp_path)
assert stats["file_count"] == 0
assert stats["folder_count"] == 0
assert stats["total_size"] == 0
assert stats["pdf_size"] == 0
assert stats["png_size"] == 0
def test_calculate_directory_stats_with_files(tmp_path):
# Create test files
(tmp_path / "test.pdf").write_text("pdf content")
(tmp_path / "test.png").write_text("png content")
(tmp_path / "test.txt").write_text("txt content")
# Create subdirectory
subdir = tmp_path / "subdir"
subdir.mkdir()
(subdir / "nested.pdf").write_text("nested pdf")
stats = calculate_directory_stats(tmp_path)
assert stats["file_count"] == 4
assert stats["folder_count"] == 1
assert stats["total_size"] > 0
assert stats["pdf_size"] > 0
assert stats["png_size"] > 0
def test_calculate_directory_stats_nonexistent():
nonexistent = Path("/nonexistent/path")
stats = calculate_directory_stats(nonexistent)
assert stats["file_count"] == 0
assert stats["folder_count"] == 0
assert stats["total_size"] == 0
def test_datetime_from_timestamp():
timestamp = 1630000000 # Some Unix timestamp
dt = datetime_from_timestamp(timestamp)
assert isinstance(dt, datetime)
assert dt.timestamp() == timestamp
def test_strftime_filter():
dt = datetime(2023, 9, 8, 14, 30, 0)
formatted = strftime_filter(dt, "%Y-%m-%d %H:%M")
assert formatted == "2023-09-08 14:30"
@patch('gallery.builder.render_gallery_page')
@patch('gallery.utils.metadata.save_metadata_cache')
@patch('gallery.utils.metadata.resolve_metadata_for_plot')
@patch('gallery.utils.metadata.merge_metadata')
@patch('gallery.utils.metadata.load_folder_metadata')
@patch('gallery.utils.processing.convert_pdf_to_png')
@patch('shutil.copy2')
def test_build_gallery_basic(
mock_copy,
mock_convert,
mock_load_folder,
mock_merge,
mock_resolve,
mock_save_cache,
mock_template,
tmp_path
):
from gallery import GalleryConfig
source_dir = tmp_path / "source"
web_dir = tmp_path / "web"
source_dir.mkdir()
web_dir.mkdir()
# Create a test PDF
pdf_file = source_dir / "test.pdf"
pdf_file.write_text("fake pdf content")
# Don't create PNG - this will trigger convert_pdf_to_png call
# Mock returns
mock_load_folder.return_value = {"folder": "metadata"}
mock_merge.return_value = {"merged": "metadata"}
mock_resolve.return_value = {"plot": "metadata"}
mock_template.render.return_value = "<html>test</html>"
build_gallery(GalleryConfig(tmp_path), source_dir, web_dir)
# Verify mocks were called
mock_convert.assert_called_once_with(pdf_file)
mock_copy.assert_called() # Should be called for PDF
mock_save_cache.assert_called_once()
# Check HTML file was created
html_file = web_dir / "index.html"
assert html_file.exists()
@patch('gallery.builder.render_gallery_page')
@patch('gallery.utils.metadata.save_metadata_cache')
@patch('gallery.utils.metadata.load_folder_metadata')
def test_build_gallery_with_subdirs(
mock_load_folder,
mock_save_cache,
mock_template,
tmp_path
):
source_dir = tmp_path / "source"
web_dir = tmp_path / "web"
source_dir.mkdir()
web_dir.mkdir()
# Create subdirectory
subdir = source_dir / "subdir"
subdir.mkdir()
mock_load_folder.return_value = {}
mock_template.render.return_value = "<html>test</html>"
from gallery import GalleryConfig
build_gallery(config=GalleryConfig(tmp_path), source_dir=source_dir, web_dir=web_dir)
# Check subdirectory was created in web
web_subdir = web_dir / "subdir"
assert web_subdir.exists()
assert web_subdir.is_dir()
@patch('gallery.utils.processing.needs_update')
@patch('shutil.copy2')
def test_build_gallery_skip_up_to_date(mock_copy, mock_needs_update, tmp_path):
source_dir = tmp_path / "source"
web_dir = tmp_path / "web"
source_dir.mkdir()
web_dir.mkdir()
# Create test files
pdf_file = source_dir / "test.pdf"
png_file = source_dir / "test.png"
pdf_file.write_text("pdf")
png_file.write_text("png")
# Create target files
web_pdf = web_dir / "test.pdf"
web_png = web_dir / "test.png"
web_pdf.write_text("pdf")
web_png.write_text("png")
# Mock needs_update to return False (up to date)
mock_needs_update.return_value = False
from gallery import get_template
get_template()
from gallery import GalleryConfig
build_gallery(GalleryConfig(tmp_path), source_dir, web_dir)
# copy2 should not be called since files are up to date
mock_copy.assert_not_called()