@@ -574,6 +574,176 @@ func TestStripANSI(t *testing.T) {
574574 expected := "2026-04-12 10:30:00 INFO application started"
575575 assertEqual (t , expected , StripANSI (input ))
576576 })
577+
578+ // =========================================================================
579+ // Advanced edge cases
580+ // =========================================================================
581+
582+ t .Run ("standalone ESC at end of string" , func (t * testing.T ) {
583+ t .Parallel ()
584+ // ESC alone without CSI sequence should be preserved (not a valid ANSI sequence)
585+ input := "text\033 "
586+ expected := "text\033 "
587+ assertEqual (t , expected , StripANSI (input ))
588+ })
589+
590+ t .Run ("ESC followed by non-bracket character" , func (t * testing.T ) {
591+ t .Parallel ()
592+ // Non-CSI escape sequence (e.g., \033) followed by something other than [
593+ input := "text\033 )text2"
594+ expected := "text\033 )text2"
595+ assertEqual (t , expected , StripANSI (input ))
596+ })
597+
598+ t .Run ("incomplete CSI sequence at end" , func (t * testing.T ) {
599+ t .Parallel ()
600+ // CSI sequence that starts but has no terminator
601+ input := "text\033 ["
602+ expected := "text"
603+ assertEqual (t , expected , StripANSI (input ))
604+ })
605+
606+ t .Run ("incomplete CSI with parameters at end" , func (t * testing.T ) {
607+ t .Parallel ()
608+ // CSI sequence with parameters but no terminator
609+ input := "text\033 [38;5;196"
610+ expected := "text"
611+ assertEqual (t , expected , StripANSI (input ))
612+ })
613+
614+ t .Run ("256-color SGR sequence" , func (t * testing.T ) {
615+ t .Parallel ()
616+ // 256-color foreground: ESC[38;5;<n>m
617+ input := "\033 [38;5;196mRED\033 [0m"
618+ expected := "RED"
619+ assertEqual (t , expected , StripANSI (input ))
620+ })
621+
622+ t .Run ("24-bit true color SGR sequence" , func (t * testing.T ) {
623+ t .Parallel ()
624+ // 24-bit RGB foreground: ESC[38;2;<r>;<g>;<b>m
625+ input := "\033 [38;2;255;0;0mTRUECOLOR\033 [0m"
626+ expected := "TRUECOLOR"
627+ assertEqual (t , expected , StripANSI (input ))
628+ })
629+
630+ t .Run ("cursor movement sequences" , func (t * testing.T ) {
631+ t .Parallel ()
632+ // Cursor up (A), down (B), forward (C), back (D)
633+ input := "\033 [2Aup\033 [3Bdown\033 [4Cforward\033 [5Dback"
634+ expected := "updownforwardback"
635+ assertEqual (t , expected , StripANSI (input ))
636+ })
637+
638+ t .Run ("cursor position sequence" , func (t * testing.T ) {
639+ t .Parallel ()
640+ // Cursor position: ESC[<row>;<col>H
641+ input := "start\033 [10;20Hmiddle\033 [1;1Hend"
642+ expected := "startmiddleend"
643+ assertEqual (t , expected , StripANSI (input ))
644+ })
645+
646+ t .Run ("erase sequences" , func (t * testing.T ) {
647+ t .Parallel ()
648+ // Erase in Display (J) and Erase in Line (K)
649+ input := "text\033 [2Jcleared\033 [Kline"
650+ expected := "textclearedline"
651+ assertEqual (t , expected , StripANSI (input ))
652+ })
653+
654+ t .Run ("scroll sequences" , func (t * testing.T ) {
655+ t .Parallel ()
656+ // Scroll up (S) and scroll down (T)
657+ input := "text\033 [2Sscrolled\033 [3T"
658+ expected := "textscrolled"
659+ assertEqual (t , expected , StripANSI (input ))
660+ })
661+
662+ t .Run ("multiple ESC in a row" , func (t * testing.T ) {
663+ t .Parallel ()
664+ // Multiple ESC characters where only the second forms a valid CSI
665+ input := "\033 \033 [31mred\033 [0m"
666+ expected := "\033 red"
667+ assertEqual (t , expected , StripANSI (input ))
668+ })
669+
670+ t .Run ("string with only ANSI codes" , func (t * testing.T ) {
671+ t .Parallel ()
672+ // String containing only escape sequences, no visible content
673+ input := "\033 [31m\033 [1m\033 [0m"
674+ expected := ""
675+ assertEqual (t , expected , StripANSI (input ))
676+ })
677+
678+ t .Run ("private mode sequences" , func (t * testing.T ) {
679+ t .Parallel ()
680+ // Private mode sequences use ? after CSI (e.g., show/hide cursor)
681+ input := "\033 [?25lhidden\033 [?25h"
682+ expected := "hidden"
683+ assertEqual (t , expected , StripANSI (input ))
684+ })
685+
686+ t .Run ("SGR with semicolons and multiple attributes" , func (t * testing.T ) {
687+ t .Parallel ()
688+ // Multiple SGR attributes: bold;red;underline
689+ input := "\033 [1;31;4mstyledtext\033 [0m"
690+ expected := "styledtext"
691+ assertEqual (t , expected , StripANSI (input ))
692+ })
693+
694+ t .Run ("CSI with @ terminator" , func (t * testing.T ) {
695+ t .Parallel ()
696+ // Insert character: ESC[@
697+ input := "ab\033 [2@cd"
698+ expected := "abcd"
699+ assertEqual (t , expected , StripANSI (input ))
700+ })
701+
702+ t .Run ("CSI with tilde terminator" , func (t * testing.T ) {
703+ t .Parallel ()
704+ // Function key sequences often end with ~
705+ input := "text\033 [15~more"
706+ expected := "textmore"
707+ assertEqual (t , expected , StripANSI (input ))
708+ })
709+
710+ t .Run ("mixed content with newlines and ANSI" , func (t * testing.T ) {
711+ t .Parallel ()
712+ input := "\033 [32mline1\033 [0m\n line2\n \033 [31mline3\033 [0m"
713+ expected := "line1\n line2\n line3"
714+ assertEqual (t , expected , StripANSI (input ))
715+ })
716+
717+ t .Run ("tab characters preserved with ANSI" , func (t * testing.T ) {
718+ t .Parallel ()
719+ input := "\033 [32mcol1\033 [0m\t col2"
720+ expected := "col1\t col2"
721+ assertEqual (t , expected , StripANSI (input ))
722+ })
723+
724+ t .Run ("very long ANSI sequence" , func (t * testing.T ) {
725+ t .Parallel ()
726+ // Long parameter list
727+ input := "\033 [0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15mtext\033 [0m"
728+ expected := "text"
729+ assertEqual (t , expected , StripANSI (input ))
730+ })
731+
732+ t .Run ("ESC at various positions" , func (t * testing.T ) {
733+ t .Parallel ()
734+ // ESC at start, middle, end positions without forming valid CSI
735+ input := "\033 text \033 "
736+ expected := "\033 text \033 "
737+ assertEqual (t , expected , StripANSI (input ))
738+ })
739+
740+ t .Run ("binary-like content with ESC" , func (t * testing.T ) {
741+ t .Parallel ()
742+ // Ensure non-printable characters are preserved
743+ input := "text\x00 \x01 \033 [31mred\033 [0m\x02 \x03 "
744+ expected := "text\x00 \x01 red\x02 \x03 "
745+ assertEqual (t , expected , StripANSI (input ))
746+ })
577747}
578748
579749// =============================================================================
@@ -695,3 +865,30 @@ func BenchmarkTextFormatter_FormatWithCaller(b *testing.B) {
695865 _ , _ = f .Format (entry )
696866 }
697867}
868+
869+ func BenchmarkStripANSI_NoEscape (b * testing.B ) {
870+ // Common case: no escape sequences
871+ input := "2026-04-12 10:30:00 INFO application started with key=value count=42"
872+ b .ResetTimer ()
873+ for i := 0 ; i < b .N ; i ++ {
874+ _ = StripANSI (input )
875+ }
876+ }
877+
878+ func BenchmarkStripANSI_WithColors (b * testing.B ) {
879+ // Typical colored log line
880+ input := "2026-04-12 10:30:00 \033 [32m\033 [1mINFO \033 [0m application started key=value"
881+ b .ResetTimer ()
882+ for i := 0 ; i < b .N ; i ++ {
883+ _ = StripANSI (input )
884+ }
885+ }
886+
887+ func BenchmarkStripANSI_Heavy (b * testing.B ) {
888+ // Many ANSI sequences
889+ input := "\033 [38;2;255;0;0m\033 [1mERROR\033 [0m \033 [38;5;196mfailed\033 [0m \033 [33mwarning\033 [0m \033 [34mdebug\033 [0m"
890+ b .ResetTimer ()
891+ for i := 0 ; i < b .N ; i ++ {
892+ _ = StripANSI (input )
893+ }
894+ }
0 commit comments