@@ -21,90 +21,116 @@ def capture(msg, endpoint="events"):
2121 return sent
2222
2323 def test_y_1d_no_x (self ):
24+ """Basic 1D Y without X auto-generates x-axis."""
2425 sent = self ._line (np .array ([1.0 , 2.0 , 3.0 ]))
2526 self .assertIn ("data" , sent ["payload" ])
2627
2728 def test_y_0d_raises (self ):
29+ """Scalar Y raises before reaching scatter."""
2830 with self .assertRaises (AssertionError ):
2931 self .viz .line (np .float64 (1.0 ))
3032
3133 def test_y_3d_raises (self ):
34+ """3D Y raises on the ndim check."""
3235 with self .assertRaises (AssertionError ):
3336 self .viz .line (np .ones ((2 , 3 , 4 )))
3437
3538 def test_y_empty_last_dim_raises (self ):
39+ """Zero-column Y raises on the empty check."""
3640 with self .assertRaises (AssertionError ):
3741 self .viz .line (np .empty ((5 , 0 )))
3842
3943 def test_x_3d_raises (self ):
44+ """3D X raises on the ndim check."""
4045 with self .assertRaises (AssertionError ):
4146 self .viz .line (np .array ([1.0 , 2.0 ]), X = np .ones ((2 , 1 , 1 )))
4247
4348 def test_x_shape_mismatch_raises (self ):
49+ """X and Y with different lengths raise on the shape check."""
4450 with self .assertRaises (AssertionError ):
4551 self .viz .line (np .array ([1.0 , 2.0 , 3.0 ]), X = np .array ([0.0 , 1.0 ]))
4652
4753 def test_single_line_one_trace (self ):
54+ """1D Y produces exactly one trace."""
4855 sent = self ._line (np .array ([1.0 , 2.0 , 3.0 ]))
4956 self .assertEqual (len (sent ["payload" ]["data" ]), 1 )
5057
5158 def test_multi_line_2d_y (self ):
59+ """2D Y with M columns produces M traces."""
5260 Y = np .array ([[1.0 , 4.0 ], [2.0 , 5.0 ], [3.0 , 6.0 ]])
5361 sent = self ._line (Y )
5462 self .assertEqual (len (sent ["payload" ]["data" ]), 2 )
5563
5664 def test_y_2d_single_col_one_trace (self ):
65+ """(N,1) Y is squeezed to 1D before building linedata."""
5766 sent = self ._line (np .array ([[1.0 ], [2.0 ], [3.0 ]]))
5867 self .assertEqual (len (sent ["payload" ]["data" ]), 1 )
5968
6069 def test_y_2d_x_1d_broadcasts (self ):
70+ """1D X is tiled to match the shape of 2D Y."""
6171 Y = np .array ([[1.0 , 4.0 ], [2.0 , 5.0 ], [3.0 , 6.0 ]])
6272 X = np .array ([0.0 , 1.0 , 2.0 ])
6373 sent = self ._line (Y , X = X )
6474 self .assertIn ("data" , sent ["payload" ])
6575
6676 def test_update_append_requires_x (self ):
77+ """update='append' without X raises before sending."""
6778 with self .assertRaises (AssertionError ):
6879 self .viz .line (np .array ([1.0 , 2.0 ]), win = "w" , update = "append" )
6980
7081 def test_update_append_sets_append_true (self ):
82+ """Existing window with update='append' sets append=True in payload."""
7183 Y = np .array ([1.0 , 2.0 ])
7284 X = np .array ([0.0 , 1.0 ])
7385 with patch .object (self .viz , "win_exists" , return_value = True ):
7486 sent = self ._line (Y , X = X , win = "w" , update = "append" )
7587 self .assertTrue (sent ["payload" ]["append" ])
7688
7789 def test_update_replace_sets_append_false (self ):
90+ """update='replace' sets append=False in payload."""
7891 Y = np .array ([1.0 , 2.0 ])
7992 X = np .array ([0.0 , 1.0 ])
8093 sent = self ._line (Y , X = X , win = "w" , update = "replace" )
8194 self .assertFalse (sent ["payload" ]["append" ])
8295
8396 def test_update_replace_uses_update_endpoint (self ):
97+ """update='replace' routes to the update endpoint."""
8498 Y = np .array ([1.0 , 2.0 ])
8599 X = np .array ([0.0 , 1.0 ])
86100 sent = self ._line (Y , X = X , win = "w" , update = "replace" )
87101 self .assertEqual (sent ["endpoint" ], "update" )
88102
89103 def test_update_append_new_window_no_append_key (self ):
104+ """New window with update='append' falls back to creation, no append key."""
90105 Y = np .array ([1.0 , 2.0 ])
91106 X = np .array ([0.0 , 1.0 ])
92107 with patch .object (self .viz , "win_exists" , return_value = False ):
93108 sent = self ._line (Y , X = X , win = "w" , update = "append" )
94109 self .assertNotIn ("append" , sent ["payload" ])
95110
96111 def test_update_remove_sends_delete (self ):
112+ """update='remove' sends delete=True without touching Y."""
97113 sent = self ._line (None , win = "w" , name = "trace1" , update = "remove" )
98114 self .assertTrue (sent ["payload" ]["delete" ])
99115
100116 def test_nan_y_passes_through (self ):
117+ """All-NaN Y values survive into the payload for use as update mask."""
101118 Y = np .array ([np .nan , np .nan , np .nan ])
102119 X = np .array ([0.0 , 1.0 , 2.0 ])
103120 with patch .object (self .viz , "win_exists" , return_value = True ):
104121 sent = self ._line (Y , X = X , win = "w" , update = "append" )
105122 y_vals = sent ["payload" ]["data" ][0 ]["y" ]
106123 self .assertTrue (all (np .isnan (v ) for v in y_vals ))
107124
125+ def test_inf_y_passes_through (self ):
126+ """Inf Y values pass through without raising."""
127+ Y = np .array ([np .inf , 1.0 , - np .inf ])
128+ X = np .array ([0.0 , 1.0 , 2.0 ])
129+ sent = self ._line (Y , X = X )
130+ y_vals = sent ["payload" ]["data" ][0 ]["y" ]
131+ self .assertTrue (np .isinf (y_vals [0 ]))
132+ self .assertTrue (np .isinf (y_vals [2 ]))
133+
108134
109135class TestScatter (unittest .TestCase ):
110136 def setUp (self ):
@@ -123,77 +149,99 @@ def capture(msg, endpoint="events"):
123149 return sent
124150
125151 def test_nx2_input (self ):
152+ """Nx2 X produces a scatter trace."""
126153 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ], [5.0 , 6.0 ]])
127154 sent = self ._scatter (X )
128155 self .assertEqual (sent ["payload" ]["data" ][0 ]["type" ], "scatter" )
129156
130157 def test_nx3_input_produces_scatter3d (self ):
158+ """Nx3 X produces a scatter3d trace."""
131159 X = np .array ([[1.0 , 2.0 , 3.0 ], [4.0 , 5.0 , 6.0 ]])
132160 sent = self ._scatter (X )
133161 self .assertEqual (sent ["payload" ]["data" ][0 ]["type" ], "scatter3d" )
134162
135163 def test_x_1d_raises (self ):
164+ """1D X raises on the ndim check."""
136165 with self .assertRaises (AssertionError ):
137166 self .viz .scatter (np .array ([1.0 , 2.0 , 3.0 ]))
138167
139168 def test_x_wrong_cols_raises (self ):
169+ """X with column count other than 2 or 3 raises."""
140170 with self .assertRaises (AssertionError ):
141171 self .viz .scatter (np .ones ((3 , 4 )))
142172
143173 def test_y_size_mismatch_raises (self ):
174+ """Y length not matching X row count raises."""
144175 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
145176 Y = np .array ([1 , 2 , 3 ])
146177 with self .assertRaises (AssertionError ):
147178 self .viz .scatter (X , Y = Y )
148179
149180 def test_nan_label_raises (self ):
181+ """NaN in Y labels raises via the isfinite check in _normalize_labels."""
150182 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
151183 Y = np .array ([1.0 , np .nan ])
152184 with self .assertRaises (AssertionError ):
153185 self .viz .scatter (X , Y = Y )
154186
187+ def test_inf_label_raises (self ):
188+ """Inf in Y labels raises via the same isfinite check as NaN."""
189+ X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
190+ Y = np .array ([1.0 , np .inf ])
191+ with self .assertRaises (AssertionError ):
192+ self .viz .scatter (X , Y = Y )
193+
155194 def test_multiple_labels_produce_multiple_traces (self ):
195+ """Distinct Y label values produce one trace each."""
156196 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ], [5.0 , 6.0 ]])
157197 Y = np .array ([1 , 1 , 2 ])
158198 sent = self ._scatter (X , Y = Y )
159199 self .assertEqual (len (sent ["payload" ]["data" ]), 2 )
160200
161201 def test_name_with_multiple_labels_raises (self ):
202+ """name= combined with multiple label groups raises."""
162203 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
163204 Y = np .array ([1 , 2 ])
164205 with self .assertRaises (AssertionError ):
165206 self .viz .scatter (X , Y = Y , name = "trace1" )
166207
167208 def test_store_history_with_update_raises (self ):
209+ """store_history=True combined with update raises ValueError."""
168210 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
169211 with self .assertRaises (ValueError ):
170212 self .viz .scatter (X , opts = {"store_history" : True }, update = "append" , win = "w" )
171213
172214 def test_update_without_win_raises (self ):
215+ """update without a win raises ValueError."""
173216 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
174217 with self .assertRaises (ValueError ):
175218 self .viz .scatter (X , update = "replace" )
176219
177220 def test_update_remove_requires_name (self ):
221+ """update='remove' without name raises."""
178222 with self .assertRaises (AssertionError ):
179223 self .viz .scatter (None , win = "w" , update = "remove" )
180224
181225 def test_update_remove_sends_delete (self ):
226+ """update='remove' sends delete=True in payload."""
182227 sent = self ._scatter (None , win = "w" , name = "trace1" , update = "remove" )
183228 self .assertTrue (sent ["payload" ]["delete" ])
184229
185230 def test_update_append_sets_append_true (self ):
231+ """Existing window with update='append' sets append=True."""
186232 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
187233 with patch .object (self .viz , "win_exists" , return_value = True ):
188234 sent = self ._scatter (X , win = "w" , update = "append" )
189235 self .assertTrue (sent ["payload" ]["append" ])
190236
191237 def test_update_replace_sets_append_false (self ):
238+ """update='replace' sets append=False in payload."""
192239 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ]])
193240 sent = self ._scatter (X , win = "w" , update = "replace" )
194241 self .assertFalse (sent ["payload" ]["append" ])
195242
196243 def test_name_based_update_1d_x_1d_y (self ):
244+ """Name-based update stacks 1D X and Y into Nx2 before sending."""
197245 X = np .array ([1.0 , 2.0 , 3.0 ])
198246 Y = np .array ([4.0 , 5.0 , 6.0 ])
199247 with patch .object (self .viz , "win_exists" , return_value = True ):
@@ -220,42 +268,57 @@ def capture(msg, endpoint="events"):
220268 return sent
221269
222270 def test_nx_m_input (self ):
271+ """NxM X produces a heatmap trace."""
223272 X = np .array ([[1.0 , 2.0 ], [3.0 , 4.0 ], [5.0 , 6.0 ]])
224273 sent = self ._heatmap (X )
225274 self .assertEqual (sent ["payload" ]["data" ][0 ]["type" ], "heatmap" )
226275
227276 def test_x_not_2d_raises (self ):
277+ """1D X raises on the 2D check."""
228278 with self .assertRaises (AssertionError ):
229279 self .viz .heatmap (np .array ([1.0 , 2.0 , 3.0 ]))
230280
231281 def test_invalid_update_raises (self ):
282+ """Unknown update value raises before building data."""
232283 X = np .ones ((3 , 3 ))
233284 with self .assertRaises (AssertionError ):
234285 self .viz .heatmap (X , update = "badvalue" )
235286
236287 def test_colormap_defaults_to_viridis (self ):
288+ """colormap defaults to Viridis when not specified."""
237289 X = np .ones ((2 , 2 ))
238290 sent = self ._heatmap (X )
239291 self .assertEqual (sent ["payload" ]["opts" ]["colormap" ], "Viridis" )
240292
241293 def test_append_row_sets_update_dir (self ):
294+ """appendRow sets updateDir and append=True on the update endpoint."""
242295 X = np .ones ((2 , 2 ))
243296 sent = self ._heatmap (X , update = "appendRow" , win = "w" )
244297 self .assertEqual (sent ["payload" ]["updateDir" ], "appendRow" )
245298 self .assertTrue (sent ["payload" ]["append" ])
246299 self .assertEqual (sent ["endpoint" ], "update" )
247300
248301 def test_append_column_sets_update_dir (self ):
302+ """appendColumn sets updateDir and append=True."""
249303 X = np .ones ((2 , 2 ))
250304 sent = self ._heatmap (X , update = "appendColumn" , win = "w" )
251305 self .assertEqual (sent ["payload" ]["updateDir" ], "appendColumn" )
252306 self .assertTrue (sent ["payload" ]["append" ])
253307
254308 def test_replace_sets_append_false (self ):
309+ """replace sets append=False on the update endpoint."""
255310 X = np .ones ((2 , 2 ))
256311 sent = self ._heatmap (X , update = "replace" , win = "w" )
257312 self .assertFalse (sent ["payload" ]["append" ])
258313
314+ def test_nan_values_pass_through (self ):
315+ """NaN values in X pass through to the payload without raising."""
316+ X = np .array ([[1.0 , np .nan ], [np .nan , 4.0 ]])
317+ sent = self ._heatmap (X )
318+ z = sent ["payload" ]["data" ][0 ]["z" ]
319+ self .assertTrue (np .isnan (z [0 ][1 ]))
320+ self .assertTrue (np .isnan (z [1 ][0 ]))
321+
259322
260323if __name__ == "__main__" :
261324 unittest .main ()
0 commit comments