Coverage for tests/test_simple_dataset_docs.py: 0%
94 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-16 16:14 -0700
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-16 16:14 -0700
1#!/usr/bin/env python
2"""
3Test script to ensure that class names match filenames in the simple_dataset_examples.
4"""
6import os
7import random
8import re
9import shutil
10import sys
11import tempfile
12import unittest
13from pathlib import Path
14from unittest.mock import MagicMock, patch
16import numpy as np
17import zarr
19# Add the script directory to the path to import generate_simple_dataset_docs
20sys.path.append(os.path.join(os.path.dirname(__file__), "..", "scripts"))
23class TestSimpleDatasetDocs(unittest.TestCase):
24 """
25 Test class to verify the correct relationship between class names and file names
26 in the simple_dataset_examples documentation.
27 """
29 @classmethod
30 def setUpClass(cls):
31 """Set up a temporary directory for testing."""
32 cls.temp_dir = tempfile.mkdtemp()
33 cls.docs_dir = os.path.join(cls.temp_dir, "docs")
34 os.makedirs(cls.docs_dir, exist_ok=True)
36 # Copy the original script output path to a backup
37 cls.original_docs_dir = Path("docs/simple_dataset_examples")
38 if cls.original_docs_dir.exists():
39 cls.backup_dir = Path(tempfile.mkdtemp())
40 shutil.copytree(cls.original_docs_dir, cls.backup_dir / "simple_dataset_examples")
41 print(f"Backed up original docs to {cls.backup_dir}")
42 else:
43 cls.backup_dir = None
45 @classmethod
46 def tearDownClass(cls):
47 """Clean up the temporary directory."""
48 shutil.rmtree(cls.temp_dir)
50 # Restore the original docs if they were backed up
51 if cls.backup_dir and cls.original_docs_dir.exists():
52 shutil.rmtree(cls.original_docs_dir)
53 shutil.copytree(cls.backup_dir / "simple_dataset_examples", cls.original_docs_dir)
54 shutil.rmtree(cls.backup_dir)
55 print("Restored original docs")
57 @patch("zarr.open")
58 @patch("copick.from_czcdp_datasets")
59 def test_class_name_to_filename_consistency(self, mock_from_czcdp, mock_zarr_open):
60 """
61 Test that each class name in the markdown file correctly corresponds to a matching
62 filename in the directory.
64 The test patches the output directory and then run the generate_docs function
65 with mocked dependencies to avoid actual copick operations.
66 """
67 # Set up mock for copick.from_czcdp_datasets
68 mock_copick_root = MagicMock()
69 mock_run = MagicMock()
70 mock_vs = MagicMock()
71 mock_tomogram = MagicMock()
73 # Configure mocks
74 mock_from_czcdp.return_value = mock_copick_root
75 mock_copick_root.runs = [mock_run]
76 mock_copick_root.pickable_objects = [
77 MagicMock(name="ferritin-complex"),
78 MagicMock(name="virus-like-capsid"),
79 MagicMock(name="cytosolic-ribosome"),
80 MagicMock(name="membrane"),
81 MagicMock(name="beta-galactosidase"),
82 MagicMock(name="beta-amylase"),
83 MagicMock(name="thyroglobulin"),
84 ]
85 for po in mock_copick_root.pickable_objects:
86 po.name = po.name
88 mock_run.name = "test_run"
89 mock_run.get_voxel_spacing.return_value = mock_vs
90 mock_vs.tomograms = [mock_tomogram]
91 mock_tomogram.tomo_type = "wbp-denoised"
93 # Mock picks for each object type
94 mock_picks_list = []
95 for po in mock_copick_root.pickable_objects:
96 mock_pick = MagicMock()
97 mock_pick.from_tool = True
98 mock_pick.pickable_object_name = po.name
99 mock_pick.numpy.return_value = (np.array([[100, 100, 100]]), None)
100 mock_picks_list.append(mock_pick)
102 mock_run.get_picks.return_value = mock_picks_list
104 # Mock zarr.open to return a dummy array
105 mock_zarr_root = MagicMock()
106 mock_zarr_open.return_value = mock_zarr_root
107 mock_zarr_root.__getitem__.return_value = np.random.randn(100, 100, 100)
108 mock_tomogram.zarr.return_value = "dummy_zarr_path"
110 # We'll patch the output directory and then run the generate_docs function
111 original_output_dir = Path("docs/simple_dataset_examples")
112 temp_output_dir = Path(self.docs_dir) / "simple_dataset_examples"
114 # Monkey patch the Path class to redirect the output
115 original_init = Path.__init__
117 def patched_init(self_path, *args, **kwargs):
118 # Replace the output directory path with our temp directory
119 arg_str = str(args[0]) if args else ""
120 if arg_str == str(original_output_dir):
121 original_init(self_path, temp_output_dir, **kwargs)
122 else:
123 original_init(self_path, *args, **kwargs)
125 # Apply monkey patch
126 Path.__init__ = patched_init
128 try:
129 # Run the document generation script
130 from generate_simple_dataset_docs import main as generate_docs
132 generate_docs()
134 # Verify the markdown file exists
135 md_file = temp_output_dir / "README.md"
136 self.assertTrue(md_file.exists(), f"README.md file not found at {md_file}")
138 # Read the markdown file
139 with open(md_file, "r") as f:
140 content = f.read()
142 # Extract all class names and filenames
143 class_pattern = re.compile(r"## Class: ([^\n]+)")
144 image_pattern = re.compile(r"!\[(.*?)\]\(\./([^)]+)\)")
146 class_names = class_pattern.findall(content)
147 image_refs = image_pattern.findall(content)
149 # Check that all class sections have a corresponding image
150 self.assertEqual(
151 len(class_names),
152 len(image_refs),
153 f"Number of class sections ({len(class_names)}) does not match number of images ({len(image_refs)})",
154 )
156 # Check the directory for actual image files
157 image_files = [f for f in os.listdir(temp_output_dir) if f.endswith(".png")]
159 # Check that all referenced images exist in the directory
160 for _, filename in image_refs:
161 self.assertIn(filename, image_files, f"Referenced image file {filename} not found in the directory")
163 # Verify that all class names match their corresponding images
164 for i, class_name in enumerate(class_names):
165 # Get the image reference for this class
166 image_alt, image_file = image_refs[i]
168 # The alt text should match the class name
169 self.assertEqual(
170 class_name,
171 image_alt,
172 f"Alt text '{image_alt}' does not match class name '{class_name}'",
173 )
175 # Generate the expected filename from the class name
176 expected_filename = f"{class_name.lower().replace(' ', '_').replace('-', '_')}.png"
178 # Check that the actual filename matches the expected filename
179 self.assertEqual(
180 expected_filename,
181 image_file,
182 f"Image filename '{image_file}' does not match expected filename '{expected_filename}' for class '{class_name}'",
183 )
185 # Verify the file exists with the expected name
186 self.assertTrue((temp_output_dir / image_file).exists(), f"Image file {image_file} does not exist")
188 # Check that we have examples for all the expected pickable objects
189 expected_class_names = [po.name for po in mock_copick_root.pickable_objects] + ["background"]
190 for expected_class in expected_class_names:
191 self.assertIn(expected_class, class_names, f"Missing example for expected class '{expected_class}'")
193 finally:
194 # Restore the original Path.__init__
195 Path.__init__ = original_init
198if __name__ == "__main__":
199 unittest.main()