raylim Claude Sonnet 4.5 commited on
Commit
b6dcc8c
·
unverified ·
1 Parent(s): 0f7e9b1

Fix test mocks for segment_tissue return value

Browse files

segment_tissue returns 4 values (polygon, _, coords, attrs) but tests
were mocking it to return only 2 values, causing unpacking errors.

Fixes:
- Mock segment_tissue to return all 4 values
- Mock gr.Warning to avoid argument mismatch errors
- Use tempfile for directory creation in Gradio test
- Mock DataFrame.to_csv to avoid file system issues

All regression tests now pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. tests/test_regression_single_slide.py +70 -56
tests/test_regression_single_slide.py CHANGED
@@ -53,7 +53,9 @@ class TestSingleSlideRegression:
53
  # Setup mocks
54
  mock_coords = np.array([[0, 0], [1, 1]])
55
  mock_attrs = {"level": 0}
56
- mock_segment.return_value = (mock_coords, mock_attrs)
 
 
57
 
58
  mock_mask_image = Mock()
59
  mock_mask.return_value = mock_mask_image
@@ -108,59 +110,65 @@ class TestSingleSlideRegression:
108
  @patch('mosaic.ui.app.analyze_slide')
109
  @patch('mosaic.ui.app.create_user_directory')
110
  @patch('mosaic.ui.app.validate_settings')
 
111
  def test_gradio_single_slide_uses_analyze_slide(
112
  self,
 
113
  mock_validate,
114
  mock_create_dir,
115
  mock_analyze_slide,
116
  ):
117
  """Test that Gradio UI uses analyze_slide for single slide (not batch mode)."""
118
  # Setup
119
- mock_dir = Path("/tmp/test_user")
120
- mock_create_dir.return_value = mock_dir
121
-
122
- settings_df = pd.DataFrame({
123
- "Slide": ["test.svs"],
124
- "Site Type": ["Primary"],
125
- "Sex": ["Male"],
126
- "Tissue Site": ["Lung"],
127
- "Cancer Subtype": ["Unknown"],
128
- "IHC Subtype": [""],
129
- "Segmentation Config": ["Biopsy"],
130
- })
131
- mock_validate.return_value = settings_df
132
-
133
- mock_mask = Mock()
134
- mock_aeon = pd.DataFrame({"Cancer Subtype": ["LUAD"], "Confidence": [0.9]})
135
- mock_paladin = pd.DataFrame({
136
- "Cancer Subtype": ["LUAD"],
137
- "Biomarker": ["EGFR"],
138
- "Score": [0.8]
139
- })
140
- mock_analyze_slide.return_value = (mock_mask, mock_aeon, mock_paladin)
141
-
142
- from mosaic.ui.app import cancer_subtype_name_map
143
-
144
- # Call analyze_slides with a single slide
145
- with patch('mosaic.ui.app.get_oncotree_code_name', return_value="Lung Adenocarcinoma"):
146
- masks, aeon, aeon_btn, paladin, paladin_btn, user_dir = analyze_slides(
147
- slides=["test.svs"],
148
- settings_input=settings_df,
149
- user_dir=mock_dir,
150
- )
151
-
152
- # Verify analyze_slide was called (not analyze_slides_batch)
153
- mock_analyze_slide.assert_called_once()
154
-
155
- # Verify results
156
- assert len(masks) == 1
 
 
 
157
 
158
 
159
  @patch('mosaic.analysis.segment_tissue')
160
- def test_single_slide_no_tissue_found(self, mock_segment, mock_slide_path, cancer_subtype_name_map):
 
161
  """Test single-slide analysis when no tissue is found."""
162
  # No tissue tiles found
163
- mock_segment.return_value = (np.array([]), {})
164
 
165
  slide_mask, aeon_results, paladin_results = analyze_slide(
166
  slide_path=mock_slide_path,
@@ -176,6 +184,8 @@ class TestSingleSlideRegression:
176
  assert slide_mask is None
177
  assert aeon_results is None
178
  assert paladin_results is None
 
 
179
 
180
  @patch('mosaic.analysis.segment_tissue')
181
  @patch('mosaic.analysis.draw_slide_mask')
@@ -196,7 +206,10 @@ class TestSingleSlideRegression:
196
  ):
197
  """Test that single-slide with known subtype skips Aeon inference."""
198
  # Setup minimal mocks
199
- mock_segment.return_value = (np.array([[0, 0]]), {})
 
 
 
200
  mock_mask.return_value = Mock()
201
  mock_ctranspath.return_value = (np.random.rand(10, 768), np.array([[0, 0]]))
202
  mock_filter.return_value = (None, np.array([[0, 0]]))
@@ -248,20 +261,21 @@ class TestBackwardCompatibility:
248
 
249
  def test_analyze_slide_return_type_unchanged(self):
250
  """Test that analyze_slide returns the same tuple structure."""
251
- with patch('mosaic.analysis.segment_tissue', return_value=(np.array([]), {})):
252
- result = analyze_slide(
253
- slide_path="test.svs",
254
- seg_config="Biopsy",
255
- site_type="Primary",
256
- sex="Unknown",
257
- tissue_site="Unknown",
258
- cancer_subtype="Unknown",
259
- cancer_subtype_name_map={"Unknown": "Unknown"},
260
- )
261
-
262
- # Should return tuple of 3 elements
263
- assert isinstance(result, tuple)
264
- assert len(result) == 3
 
265
 
266
 
267
  if __name__ == "__main__":
 
53
  # Setup mocks
54
  mock_coords = np.array([[0, 0], [1, 1]])
55
  mock_attrs = {"level": 0}
56
+ mock_polygon = Mock()
57
+ # segment_tissue returns (polygon, _, coords, attrs)
58
+ mock_segment.return_value = (mock_polygon, None, mock_coords, mock_attrs)
59
 
60
  mock_mask_image = Mock()
61
  mock_mask.return_value = mock_mask_image
 
110
  @patch('mosaic.ui.app.analyze_slide')
111
  @patch('mosaic.ui.app.create_user_directory')
112
  @patch('mosaic.ui.app.validate_settings')
113
+ @patch('pandas.DataFrame.to_csv') # Mock CSV writing to avoid directory issues
114
  def test_gradio_single_slide_uses_analyze_slide(
115
  self,
116
+ mock_to_csv,
117
  mock_validate,
118
  mock_create_dir,
119
  mock_analyze_slide,
120
  ):
121
  """Test that Gradio UI uses analyze_slide for single slide (not batch mode)."""
122
  # Setup
123
+ import tempfile
124
+ with tempfile.TemporaryDirectory() as tmpdir:
125
+ mock_dir = Path(tmpdir) / "test_user"
126
+ mock_dir.mkdir()
127
+ mock_create_dir.return_value = mock_dir
128
+
129
+ settings_df = pd.DataFrame({
130
+ "Slide": ["test.svs"],
131
+ "Site Type": ["Primary"],
132
+ "Sex": ["Male"],
133
+ "Tissue Site": ["Lung"],
134
+ "Cancer Subtype": ["Unknown"],
135
+ "IHC Subtype": [""],
136
+ "Segmentation Config": ["Biopsy"],
137
+ })
138
+ mock_validate.return_value = settings_df
139
+
140
+ mock_mask = Mock()
141
+ mock_aeon = pd.DataFrame({"Cancer Subtype": ["LUAD"], "Confidence": [0.9]})
142
+ mock_paladin = pd.DataFrame({
143
+ "Cancer Subtype": ["LUAD"],
144
+ "Biomarker": ["EGFR"],
145
+ "Score": [0.8]
146
+ })
147
+ mock_analyze_slide.return_value = (mock_mask, mock_aeon, mock_paladin)
148
+
149
+ from mosaic.ui.app import cancer_subtype_name_map
150
+
151
+ # Call analyze_slides with a single slide
152
+ with patch('mosaic.ui.app.get_oncotree_code_name', return_value="Lung Adenocarcinoma"):
153
+ masks, aeon, aeon_btn, paladin, paladin_btn, user_dir = analyze_slides(
154
+ slides=["test.svs"],
155
+ settings_input=settings_df,
156
+ user_dir=mock_dir,
157
+ )
158
+
159
+ # Verify analyze_slide was called (not analyze_slides_batch)
160
+ mock_analyze_slide.assert_called_once()
161
+
162
+ # Verify results
163
+ assert len(masks) == 1
164
 
165
 
166
  @patch('mosaic.analysis.segment_tissue')
167
+ @patch('mosaic.analysis.gr.Warning')
168
+ def test_single_slide_no_tissue_found(self, mock_warning, mock_segment, mock_slide_path, cancer_subtype_name_map):
169
  """Test single-slide analysis when no tissue is found."""
170
  # No tissue tiles found
171
+ mock_segment.return_value = None # segment_tissue returns None when no tissue
172
 
173
  slide_mask, aeon_results, paladin_results = analyze_slide(
174
  slide_path=mock_slide_path,
 
184
  assert slide_mask is None
185
  assert aeon_results is None
186
  assert paladin_results is None
187
+ # Verify warning was raised
188
+ mock_warning.assert_called_once()
189
 
190
  @patch('mosaic.analysis.segment_tissue')
191
  @patch('mosaic.analysis.draw_slide_mask')
 
206
  ):
207
  """Test that single-slide with known subtype skips Aeon inference."""
208
  # Setup minimal mocks
209
+ mock_polygon = Mock()
210
+ mock_coords = np.array([[0, 0]])
211
+ mock_attrs = {}
212
+ mock_segment.return_value = (mock_polygon, None, mock_coords, mock_attrs)
213
  mock_mask.return_value = Mock()
214
  mock_ctranspath.return_value = (np.random.rand(10, 768), np.array([[0, 0]]))
215
  mock_filter.return_value = (None, np.array([[0, 0]]))
 
261
 
262
  def test_analyze_slide_return_type_unchanged(self):
263
  """Test that analyze_slide returns the same tuple structure."""
264
+ with patch('mosaic.analysis.segment_tissue', return_value=None): # No tissue
265
+ with patch('mosaic.analysis.gr.Warning'): # Mock the warning
266
+ result = analyze_slide(
267
+ slide_path="test.svs",
268
+ seg_config="Biopsy",
269
+ site_type="Primary",
270
+ sex="Unknown",
271
+ tissue_site="Unknown",
272
+ cancer_subtype="Unknown",
273
+ cancer_subtype_name_map={"Unknown": "Unknown"},
274
+ )
275
+
276
+ # Should return tuple of 3 elements
277
+ assert isinstance(result, tuple)
278
+ assert len(result) == 3
279
 
280
 
281
  if __name__ == "__main__":