251 lines
8.2 KiB
Python
251 lines
8.2 KiB
Python
"""
|
|
Container-optimized test suite for gallery generator.
|
|
Designed to run inside Apptainer/Singularity containers.
|
|
"""
|
|
|
|
import unittest
|
|
import tempfile
|
|
import shutil
|
|
import time
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
class TestContainerEnvironment(unittest.TestCase):
|
|
"""Test that the container environment is properly configured."""
|
|
|
|
def test_python_version(self):
|
|
"""Test that Python 3.11+ is available."""
|
|
version = sys.version_info
|
|
self.assertGreaterEqual(version.major, 3)
|
|
self.assertGreaterEqual(version.minor, 11)
|
|
|
|
def test_required_modules(self):
|
|
"""Test that required Python modules are installed."""
|
|
try:
|
|
import jinja2
|
|
import yaml
|
|
self.assertTrue(True) # Success if no ImportError
|
|
except ImportError as e:
|
|
self.fail(f"Required module not found: {e}")
|
|
|
|
def test_imagemagick_available(self):
|
|
"""Test that ImageMagick is installed and accessible."""
|
|
try:
|
|
result = subprocess.run(['convert', '-version'],
|
|
capture_output=True, text=True, timeout=10)
|
|
self.assertEqual(result.returncode, 0)
|
|
self.assertIn('ImageMagick', result.stdout)
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
self.fail("ImageMagick not available or not working")
|
|
|
|
def test_working_directory(self):
|
|
"""Test that the source code is available."""
|
|
expected_files = ['generate_gallery.py', 'config.yaml', 'orchestration/']
|
|
for file_path in expected_files:
|
|
path = Path('/src') / file_path
|
|
self.assertTrue(path.exists(), f"Missing: {file_path}")
|
|
|
|
|
|
class TestUtilityFunctions(unittest.TestCase):
|
|
"""Test core utility functions."""
|
|
|
|
def test_format_file_size(self):
|
|
"""Test file size formatting utility."""
|
|
# Import the function from the container's source
|
|
sys.path.insert(0, '/src')
|
|
from generate_gallery import format_file_size
|
|
|
|
self.assertEqual(format_file_size(0), "0 B")
|
|
self.assertEqual(format_file_size(1024), "1.0 KB")
|
|
self.assertEqual(format_file_size(1048576), "1.0 MB")
|
|
self.assertEqual(format_file_size(1073741824), "1.0 GB")
|
|
|
|
def test_needs_update(self):
|
|
"""Test file update checking."""
|
|
sys.path.insert(0, '/src')
|
|
from generate_gallery import needs_update
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
|
|
# Test missing target
|
|
source = temp_path / "source.txt"
|
|
target = temp_path / "target.txt"
|
|
source.write_text("test")
|
|
|
|
self.assertTrue(needs_update(source, target))
|
|
|
|
# Test up-to-date target
|
|
target.write_text("test")
|
|
time.sleep(0.1) # Ensure different timestamp
|
|
os.utime(target, (time.time(), time.time()))
|
|
|
|
self.assertFalse(needs_update(source, target))
|
|
|
|
|
|
class TestMetadataSystem(unittest.TestCase):
|
|
"""Test metadata loading and processing."""
|
|
|
|
def setUp(self):
|
|
sys.path.insert(0, '/src')
|
|
|
|
def test_metadata_loading(self):
|
|
"""Test loading metadata files."""
|
|
from orchestration.metadata import load_metadata_file
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
|
|
# Test YAML metadata
|
|
yaml_file = temp_path / "test.yaml"
|
|
yaml_content = "title: Test\nauthor: Container Test\n"
|
|
yaml_file.write_text(yaml_content)
|
|
|
|
metadata = load_metadata_file(yaml_file)
|
|
self.assertEqual(metadata['title'], 'Test')
|
|
self.assertEqual(metadata['author'], 'Container Test')
|
|
|
|
def test_metadata_inheritance(self):
|
|
"""Test metadata inheritance through directories."""
|
|
from orchestration.metadata import merge_metadata
|
|
|
|
parent = {'project': 'Test', 'version': '1.0'}
|
|
child = {'experiment': 'A', 'version': '1.1'}
|
|
|
|
merged = merge_metadata(parent, child)
|
|
|
|
self.assertEqual(merged['project'], 'Test')
|
|
self.assertEqual(merged['experiment'], 'A')
|
|
self.assertEqual(merged['version'], '1.1') # Child overrides parent
|
|
|
|
|
|
class TestPDFProcessing(unittest.TestCase):
|
|
"""Test PDF processing functionality."""
|
|
|
|
def setUp(self):
|
|
sys.path.insert(0, '/src')
|
|
self.temp_dir = Path(tempfile.mkdtemp())
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def create_mock_pdf(self, path: Path):
|
|
"""Create a minimal mock PDF."""
|
|
path.write_text("%PDF-1.4\nMock PDF for testing")
|
|
|
|
def test_pdf_conversion(self):
|
|
"""Test PDF to PNG conversion."""
|
|
from generate_gallery import convert_pdf_to_png
|
|
|
|
# Create a mock PDF
|
|
pdf_path = self.temp_dir / "test.pdf"
|
|
self.create_mock_pdf(pdf_path)
|
|
|
|
# This should work in the container with ImageMagick
|
|
try:
|
|
convert_pdf_to_png(pdf_path)
|
|
png_path = pdf_path.with_suffix('.png')
|
|
self.assertTrue(png_path.exists())
|
|
except subprocess.CalledProcessError:
|
|
# Allow test to pass if ImageMagick can't process our mock PDF
|
|
# (Real PDFs would work, but our mock might not)
|
|
self.skipTest("Mock PDF not processable by ImageMagick")
|
|
|
|
|
|
class TestGalleryGeneration(unittest.TestCase):
|
|
"""Test end-to-end gallery generation."""
|
|
|
|
def setUp(self):
|
|
sys.path.insert(0, '/src')
|
|
self.temp_dir = Path(tempfile.mkdtemp())
|
|
self.source_dir = self.temp_dir / "source"
|
|
self.web_dir = self.temp_dir / "web"
|
|
self.source_dir.mkdir()
|
|
self.web_dir.mkdir()
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.temp_dir)
|
|
|
|
def create_test_structure(self):
|
|
"""Create a minimal test directory structure."""
|
|
# Create mock PDF
|
|
pdf_path = self.source_dir / "test_plot.pdf"
|
|
pdf_path.write_text("%PDF-1.4\nTest plot content")
|
|
|
|
# Create metadata
|
|
metadata_path = self.source_dir / "metadata.yaml"
|
|
metadata_path.write_text("title: Container Test\nauthor: CI Pipeline\n")
|
|
|
|
def test_build_gallery(self):
|
|
"""Test building a simple gallery."""
|
|
from generate_gallery import build_gallery
|
|
|
|
self.create_test_structure()
|
|
|
|
# This should complete without errors
|
|
try:
|
|
build_gallery(self.source_dir, self.web_dir)
|
|
|
|
# Check that HTML was generated
|
|
html_file = self.web_dir / "index.html"
|
|
self.assertTrue(html_file.exists())
|
|
|
|
# Check that files were copied
|
|
pdf_file = self.web_dir / "test_plot.pdf"
|
|
self.assertTrue(pdf_file.exists())
|
|
|
|
except Exception as e:
|
|
self.fail(f"Gallery generation failed: {e}")
|
|
|
|
|
|
def run_container_tests():
|
|
"""Run all tests suitable for container execution."""
|
|
print("=" * 60)
|
|
print("GALLERY GENERATOR CONTAINER TESTS")
|
|
print("=" * 60)
|
|
print(f"Python version: {sys.version}")
|
|
print(f"Working directory: {os.getcwd()}")
|
|
print(f"Python path: {sys.path[:3]}...")
|
|
print("=" * 60)
|
|
|
|
# Create test suite
|
|
loader = unittest.TestLoader()
|
|
suite = unittest.TestSuite()
|
|
|
|
# Add test classes
|
|
test_classes = [
|
|
TestContainerEnvironment,
|
|
TestUtilityFunctions,
|
|
TestMetadataSystem,
|
|
TestPDFProcessing,
|
|
TestGalleryGeneration
|
|
]
|
|
|
|
for test_class in test_classes:
|
|
tests = loader.loadTestsFromTestCase(test_class)
|
|
suite.addTests(tests)
|
|
|
|
# Run tests
|
|
runner = unittest.TextTestRunner(verbosity=2)
|
|
result = runner.run(suite)
|
|
|
|
# Print summary
|
|
print("=" * 60)
|
|
if result.wasSuccessful():
|
|
print("✅ ALL TESTS PASSED")
|
|
else:
|
|
print("❌ SOME TESTS FAILED")
|
|
print(f"Failures: {len(result.failures)}")
|
|
print(f"Errors: {len(result.errors)}")
|
|
print("=" * 60)
|
|
|
|
return 0 if result.wasSuccessful() else 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit_code = run_container_tests()
|
|
sys.exit(exit_code)
|