@@ -479,36 +479,20 @@ def test_heat_rows_reduce_flame_budget(self):
479479class TestBuildFireArtStyles :
480480 """Verify dim style on structural frame elements."""
481481
482- def test_top_edge_has_dim_style (self ):
483- """Top edge ▁ characters should have 'dim' style."""
484- text = _build_fire_art (50 , 20 )
485- plain = text .plain
486- idx = plain .index ("\u2581 " )
487- assert "dim" in _style_at (text , idx )
488-
489- def test_outer_frame_top_has_dim_style (self ):
490- """Outer frame ┌ should have 'dim' style."""
491- text = _build_fire_art (50 , 20 )
492- plain = text .plain
493- idx = plain .index ("\u250c " )
494- assert "dim" in _style_at (text , idx )
495-
496- def test_outer_frame_bottom_has_dim_style (self ):
497- """Outer frame └ should have 'dim' style."""
498- text = _build_fire_art (50 , 20 )
499- plain = text .plain
500- idx = plain .index ("\u2514 " )
501- assert "dim" in _style_at (text , idx )
502-
503- def test_inner_borders_have_dim_style (self ):
504- """│ border characters should have 'dim' style."""
505- text = _build_fire_art (50 , 20 )
482+ def test_all_frame_chars_have_dim_style (self ):
483+ """All structural frame characters must have 'dim' style exactly."""
484+ text = _build_fire_art (50 , 20 , fire_on = False )
506485 plain = text .plain
507- idx = plain .index ("\u2502 " )
508- assert "dim" in _style_at (text , idx )
486+ frame_chars = set ("\u2581 \u250c \u2510 \u2514 \u2518 \u2500 \u2502 " )
487+ for idx , ch in enumerate (plain ):
488+ if ch in frame_chars :
489+ style = _style_at (text , idx )
490+ assert style == "dim" , (
491+ f"Char { ch !r} at offset { idx } has style { style !r} , expected 'dim'"
492+ )
509493
510494 def test_outer_hearth_has_dim_style (self ):
511- """Outer hearth ▓ row should have 'dim' style."""
495+ """Outer hearth ▓ row should have 'dim' style exactly ."""
512496 text = _build_fire_art (50 , 20 )
513497 plain = text .plain
514498 lines = plain .split ("\n " )
@@ -518,13 +502,25 @@ def test_outer_hearth_has_dim_style(self):
518502 and not line .startswith ("\u2502 \u2502 " )
519503 and "\u2593 " in line
520504 ):
521- # Outer hearth line
522505 line_offset = plain .index (line )
523- hearth_idx = line .index ("\u2593 " )
524- assert "dim" in _style_at (text , line_offset + hearth_idx )
506+ for rel , ch in enumerate (line ):
507+ if ch == "\u2593 " :
508+ assert _style_at (text , line_offset + rel ) == "dim"
525509 return
526510 raise AssertionError ("No outer hearth row found" )
527511
512+ def test_frame_chars_with_fire_on (self ):
513+ """Frame chars have 'dim' style even with fire_on=True."""
514+ text = _build_fire_art (50 , 20 , fire_on = True )
515+ plain = text .plain
516+ frame_chars = set ("\u2581 \u250c \u2510 \u2514 \u2518 \u2500 \u2502 " )
517+ for idx , ch in enumerate (plain ):
518+ if ch in frame_chars :
519+ style = _style_at (text , idx )
520+ assert style == "dim" , (
521+ f"Char { ch !r} at offset { idx } has style { style !r} , expected 'dim'"
522+ )
523+
528524
529525# ---------------------------------------------------------------------------
530526# _build_fire_art – flame centering and geometry
@@ -613,24 +609,83 @@ def test_flame_row_min_width_respected(self):
613609 content = inner .strip ()
614610 assert len (content ) > 0
615611
616- def test_flame_row_trailing_pad_not_negative (self ):
617- """Trailing pad should never be negative (uses max(..., 0))."""
618- # Use a narrow width to stress the trailing pad calculation
619- w = 40
620- text = _build_fire_art (w , 20 , fire_on = True )
621- plain = text .plain
612+ def test_flame_row_inner_width_exact (self ):
613+ """Flame row inner content exactly matches iw (kills lead/trail mutations)."""
614+ w = 60
622615 iw = w - 4
623- lines = plain .split ("\n " )
616+ text = _build_fire_art (w , 20 , fire_on = True )
617+ lines = text .plain .split ("\n " )
618+ for i , line in enumerate (lines ):
619+ if not line .startswith ("\u2502 \u2502 " ):
620+ continue
621+ if not line .endswith ("\u2502 \u2502 " ):
622+ continue
623+ inner = line [2 :- 2 ]
624+ if "\u2591 " in inner or "\u2593 " in inner or inner .strip () == "" :
625+ continue
626+ # Flame inner width must equal iw exactly
627+ assert len (inner ) == iw , (
628+ f"Line { i } : inner width { len (inner )} != { iw } : { inner !r} "
629+ )
630+
631+ def test_flame_row_leading_spaces_only (self ):
632+ """Leading padding is only regular spaces (kills ' ' → 'XX XX')."""
633+ w = 60
634+ text = _build_fire_art (w , 20 , fire_on = True )
635+ lines = text .plain .split ("\n " )
624636 for line in lines :
625637 if not line .startswith ("\u2502 \u2502 " ):
626638 continue
627639 if not line .endswith ("\u2502 \u2502 " ):
628640 continue
629641 inner = line [2 :- 2 ]
630- if "\u2591 " in inner or "\u2593 " in inner :
642+ if "\u2591 " in inner or "\u2593 " in inner or inner .strip () == "" :
643+ continue
644+ # Leading padding: everything before first non-space
645+ lead_count = len (inner ) - len (inner .lstrip (" " ))
646+ leading = inner [:lead_count ]
647+ # Must be only spaces (kills " " → "XX XX" mutation)
648+ assert leading == " " * lead_count
649+
650+ def test_flame_row_trailing_spaces_only (self ):
651+ """Trailing padding is only regular spaces (kills ' ' → 'XX XX')."""
652+ w = 60
653+ text = _build_fire_art (w , 20 , fire_on = True )
654+ lines = text .plain .split ("\n " )
655+ for line in lines :
656+ if not line .startswith ("\u2502 \u2502 " ):
657+ continue
658+ if not line .endswith ("\u2502 \u2502 " ):
659+ continue
660+ inner = line [2 :- 2 ]
661+ if "\u2591 " in inner or "\u2593 " in inner or inner .strip () == "" :
662+ continue
663+ # Trailing padding: everything after last non-space
664+ trail_count = len (inner ) - len (inner .rstrip (" " ))
665+ trailing = inner [- trail_count :] if trail_count else ""
666+ # Must be only spaces
667+ assert trailing == " " * trail_count
668+
669+ def test_flame_centering_lead_roughly_half (self ):
670+ """Lead should be roughly (iw - body_w) // 2 (kills + and // 3)."""
671+ w = 60
672+ text = _build_fire_art (w , 20 , fire_on = True )
673+ lines = text .plain .split ("\n " )
674+ for line in lines :
675+ if not line .startswith ("\u2502 \u2502 " ):
676+ continue
677+ if not line .endswith ("\u2502 \u2502 " ):
678+ continue
679+ inner = line [2 :- 2 ]
680+ if "\u2591 " in inner or "\u2593 " in inner or inner .strip () == "" :
631681 continue
632- # Inner should be at least iw wide (may be wider for flame min_w)
633- assert len (inner ) >= iw
682+ lead = len (inner ) - len (inner .lstrip (" " ))
683+ trail = len (inner ) - len (inner .rstrip (" " ))
684+ total_pad = lead + trail
685+ if total_pad > 1 :
686+ # Lead should be <= trail (centering divides by 2)
687+ # With // 2 floor division, lead <= trail
688+ assert lead <= trail + 1 , f"Centering off: lead={ lead } , trail={ trail } "
634689
635690
636691# ---------------------------------------------------------------------------
0 commit comments