Coverage for tests/test_dataset_examples.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 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_dataset_examples
20sys.path.append(os.path.join(os.path.dirname(__file__), "..", "scripts"))
23class TestDatasetExamples(unittest.TestCase):
24 """
25 Test class to verify the correct relationship between class names and file names
26 in the 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/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 / "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 / "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/dataset_examples")
112 temp_output_dir = Path(self.docs_dir) / "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_dataset_examples 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()