@@ -252,3 +252,135 @@ def test_draw_preserves_original_df(self, adf_with_subframe):
252252 fig , ax , stats = adf .draw ("Sub.dz:x" , type = 'profile' , bins = 2 )
253253 cols_after = set (adf .df .columns )
254254 assert cols_before == cols_after , f"Columns leaked: { cols_after - cols_before } "
255+
256+
257+ # =========================================================================
258+ # draw_figures() tests
259+ # =========================================================================
260+
261+ class TestDrawFiguresSubframeResolution :
262+ """draw_figures() should resolve Subframe.column references."""
263+
264+ def test_draw_figures_subframe_basic (self , adf_with_subframe ):
265+ """draw_figures with Subframe.column in plot expr."""
266+ adf = adf_with_subframe
267+ specs = [{
268+ 'name' : 'test_fig' ,
269+ 'plots' : [
270+ {'expr' : 'Sub.dz:x' , 'type' : 'profile' , 'bins' : 3 },
271+ ]
272+ }]
273+ results = adf .draw_figures (specs )
274+ assert 'test_fig' in results
275+ assert results ['test_fig' ].get ('error' ) is None
276+
277+ def test_draw_figures_subframe_conflict (self , adf_with_subframe ):
278+ """draw_figures with conflicting column name."""
279+ adf = adf_with_subframe
280+ specs = [{
281+ 'name' : 'conflict_fig' ,
282+ 'plots' : [
283+ {'expr' : 'Sub.dy:x' , 'type' : 'profile' , 'bins' : 2 },
284+ ]
285+ }]
286+ results = adf .draw_figures (specs )
287+ assert results ['conflict_fig' ].get ('error' ) is None
288+
289+ def test_draw_figures_mixed_subframe_and_local (self , adf_with_subframe ):
290+ """draw_figures with both subframe and local columns in same figure."""
291+ adf = adf_with_subframe
292+ specs = [{
293+ 'name' : 'mixed_fig' ,
294+ 'ncols' : 2 ,
295+ 'plots' : [
296+ {'expr' : 'dy:x' , 'type' : 'profile' , 'bins' : 2 },
297+ {'expr' : 'Sub.dz:x' , 'type' : 'profile' , 'bins' : 2 },
298+ ]
299+ }]
300+ results = adf .draw_figures (specs )
301+ assert results ['mixed_fig' ].get ('error' ) is None
302+
303+ def test_draw_figures_multikey (self , adf_multikey_subframe ):
304+ """draw_figures with multi-key subframe."""
305+ adf = adf_multikey_subframe
306+ specs = [{
307+ 'name' : 'multikey_fig' ,
308+ 'plots' : [
309+ {'expr' : 'Side.dyS:x' , 'type' : 'profile' , 'bins' : 4 },
310+ {'expr' : 'Side.dzS:x' , 'type' : 'profile' , 'bins' : 4 },
311+ ]
312+ }]
313+ results = adf .draw_figures (specs )
314+ assert results ['multikey_fig' ].get ('error' ) is None
315+
316+ def test_draw_figures_defaults_cascade (self , adf_with_subframe ):
317+ """Per-figure defaults propagate to plots (linestyle, type, bins)."""
318+ adf = adf_with_subframe
319+ specs = [{
320+ 'name' : 'defaults_fig' ,
321+ 'defaults' : {'type' : 'profile' , 'bins' : 3 , 'linestyle' : 'none' },
322+ 'plots' : [
323+ {'expr' : 'Sub.dz:x' },
324+ {'expr' : 'dy:x' },
325+ ]
326+ }]
327+ results = adf .draw_figures (specs )
328+ assert results ['defaults_fig' ].get ('error' ) is None
329+
330+ def test_draw_figures_layout_tuple (self , adf_with_subframe ):
331+ """layout=(nrows, ncols) tuple works."""
332+ adf = adf_with_subframe
333+ specs = [{
334+ 'name' : 'layout_fig' ,
335+ 'layout' : (1 , 2 ),
336+ 'plots' : [
337+ {'expr' : 'dy:x' , 'type' : 'profile' , 'bins' : 2 },
338+ {'expr' : 'Sub.dz:x' , 'type' : 'profile' , 'bins' : 2 },
339+ ]
340+ }]
341+ results = adf .draw_figures (specs )
342+ assert results ['layout_fig' ].get ('error' ) is None
343+
344+ def test_draw_figures_subframe_in_selection (self , adf_with_subframe ):
345+ """Subframe.column works in selection within draw_figures."""
346+ adf = adf_with_subframe
347+ specs = [{
348+ 'name' : 'sel_fig' ,
349+ 'plots' : [
350+ {'expr' : 'dy:x' , 'type' : 'profile' , 'bins' : 2 ,
351+ 'selection' : 'Sub.dz<0.015' },
352+ ]
353+ }]
354+ results = adf .draw_figures (specs )
355+ assert results ['sel_fig' ].get ('error' ) is None
356+
357+ def test_draw_figures_multiple_figures (self , adf_with_subframe ):
358+ """Multiple figures in one draw_figures call."""
359+ adf = adf_with_subframe
360+ specs = [
361+ {
362+ 'name' : 'fig1' ,
363+ 'plots' : [{'expr' : 'Sub.dz:x' , 'type' : 'profile' , 'bins' : 2 }]
364+ },
365+ {
366+ 'name' : 'fig2' ,
367+ 'plots' : [{'expr' : 'Sub.dy:x' , 'type' : 'profile' , 'bins' : 2 }]
368+ },
369+ ]
370+ results = adf .draw_figures (specs )
371+ assert 'fig1' in results
372+ assert 'fig2' in results
373+ assert results ['fig1' ].get ('error' ) is None
374+ assert results ['fig2' ].get ('error' ) is None
375+
376+ def test_draw_figures_preserves_df (self , adf_with_subframe ):
377+ """draw_figures doesn't modify self.df."""
378+ adf = adf_with_subframe
379+ cols_before = set (adf .df .columns )
380+ specs = [{
381+ 'name' : 'preserve_fig' ,
382+ 'plots' : [{'expr' : 'Sub.dz:x' , 'type' : 'profile' , 'bins' : 2 }]
383+ }]
384+ adf .draw_figures (specs )
385+ cols_after = set (adf .df .columns )
386+ assert cols_before == cols_after
0 commit comments