okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
b2b0f8d
main
1package uv 2 3import ( 4"bytes" 5"strings" 6"testing" 7) 8 9func TestRendererOutput (t * testing.T ) { 10cases := []struct { 11name string 12input []string 13wrap []bool 14relative bool 15altscreen bool 16expected []string 17 }{ 18 { 19name :"scroll to bottom in inline mode" , 20input : []string {"ABC" ,"XXX" }, 21expected : []string {"\rABC" ,"\rXXX" }, 22relative :true , 23 }, 24 { 25name :"scroll one line" , 26input : []string { 27loremIpsum [0 ], 28loremIpsum [0 ][10 :], 29 }, 30wrap : []bool { 31true , 32true , 33 }, 34expected :func () []string { 35if isWindows { 36return []string { 37"\x1b[H\x1b[2JLorem ipsu\r\nm dolor si\r\nt amet, co\r\nnsectetur\r\nadipiscin\x1b[?7lg\x1b[?7h" , 38"\x1b[Hm dolor si\r\nt amet, co\r\nnsectetur\x1b[K\r\nadipiscing\r\n elit. Vi\x1b[?7lv\x1b[?7h" , 39 } 40 }else { 41return []string { 42"\x1b[H\x1b[2JLorem ipsu\r\nm dolor si\r\nt amet, co\r\nnsectetur\r\nadipiscin\x1b[?7lg\x1b[?7h" , 43"\r\n elit. Vi\x1b[?7lv\x1b[?7h" , 44 } 45 } 46 }(), 47altscreen :true , 48 }, 49 { 50name :"scroll two lines" , 51input : []string { 52loremIpsum [0 ], 53loremIpsum [0 ][20 :], 54 }, 55wrap : []bool { 56true , 57true , 58 }, 59expected :func () []string { 60if isWindows { 61return []string { 62"\x1b[H\x1b[2JLorem ipsu\r\nm dolor si\r\nt amet, co\r\nnsectetur\r\nadipiscin\x1b[?7lg\x1b[?7h" , 63"\x1b[Ht amet, co\r\nnsectetur\x1b[K\r\nadipiscing\r\n elit. Viv\r\namus at o\x1b[?7lr\x1b[?7h" , 64 } 65 }else { 66return []string { 67"\x1b[H\x1b[2JLorem ipsu\r\nm dolor si\r\nt amet, co\r\nnsectetur\r\nadipiscin\x1b[?7lg\x1b[?7h" , 68"\r\x1b[2S\x1bM elit. Viv\r\namus at o\x1b[?7lr\x1b[?7h" , 69 } 70 } 71 }(), 72altscreen :true , 73 }, 74 { 75name :"insert line in the middle" , 76input : []string { 77"ABC\nDEF\nGHI\n" , 78"ABC\n\nDEF\nGHI" , 79 }, 80wrap : []bool { 81true , 82true , 83 }, 84expected :func () []string { 85if isWindows { 86return []string { 87"\x1b[H\x1b[2JABC\r\nDEF\r\nGHI" , 88"\r\x1bM\x1b[K\nDEF\r\nGHI" , 89 } 90 }else { 91return []string { 92"\x1b[H\x1b[2JABC\r\nDEF\r\nGHI" , 93"\r\x1bM\x1b[L" , 94 } 95 } 96 }(), 97altscreen :true , 98 }, 99 { 100name :"erase until end of line" , 101input : []string { 102"\nABCEFGHIJK" , 103"\nABCE " , 104 }, 105expected : []string { 106"\x1b[2;1HABCEFGHIJK" , 107"\r\x1b[5G\x1b[K" , 108 }, 109 }, 110 } 111 112for _ ,c := range cases { 113t .Run (c .name ,func (t * testing.T ) { 114var buf bytes.Buffer 115s := NewTerminalRenderer (& buf , []string { 116"TERM=xterm-256color" ,// Enable 256 colors 117"COLORTERM=truecolor" ,// Enable true color support 118 }) 119 120s .SetScrollOptim (! isWindows )// Disable scroll optimization on Windows for consistent results 121s .SetFullscreen (c .altscreen ) 122s .SetRelativeCursor (c .relative ) 123if c .altscreen { 124s .SaveCursor () 125s .Erase () 126 } 127 128scr := NewScreenBuffer (10 ,5 ) 129for i := range c .input { 130buf .Reset () 131 132comp := NewStyledString (c .input [i ]) 133if i < len (c .wrap ) { 134comp .Wrap = c .wrap [i ] 135 } 136comp .Draw (scr ,scr .Bounds ()) 137s .Render (scr .RenderBuffer ) 138if err := s .Flush ();err != nil { 139t .Fatalf ("Flush failed: %v" ,err ) 140 } 141 142if buf .String ()!= c .expected [i ] { 143t .Errorf ("Expected output[%d]:\n%q\nGot:\n%q" ,i ,c .expected [i ],buf .String ()) 144 } 145 } 146 }) 147 } 148} 149 150func TestRendererWideCellReanchor (t * testing.T ) { 151render := func (grapheme bool )string { 152var buf bytes.Buffer 153s := NewTerminalRenderer (& buf , []string { 154"TERM=xterm-256color" , 155"COLORTERM=truecolor" , 156 }) 157s .SetFullscreen (true ) 158s .SetGraphemeWidth (grapheme ) 159s .SaveCursor () 160s .Erase () 161 162scr := NewScreenBuffer (10 ,1 ) 163buf .Reset () 164NewStyledString ("δΈη" ).Draw (scr ,scr .Bounds ()) 165s .Render (scr .RenderBuffer ) 166if err := s .Flush ();err != nil { 167t .Fatalf ("Flush failed: %v" ,err ) 168 } 169return buf .String () 170 } 171 172// The re-anchor is a full absolute move, not a column-only one: a terminal 173// that measured the clusters wider than the model may have wrapped the 174// line, which moves the row as well. 175out := render (false ) 176reanchors := strings .Count (out ,"\x1b[1;5H" ) 177if reanchors != 1 { 178t .Errorf ("non-grapheme line with wide cells: want 1 re-anchor, got %d in %q" ,reanchors ,out ) 179 } 180if perCell := strings .Count (out ,"\x1b[1;3H" );perCell != 0 { 181t .Errorf ("adjacent wide cells emitted a per-cell re-anchor: %q" ,out ) 182 } 183 184gout := render (true ) 185if n := strings .Count (gout ,"\x1b[1;5H" );n != 0 { 186t .Errorf ("grapheme-mode line should not re-anchor, got %d in %q" ,n ,gout ) 187 } 188} 189 190var loremIpsum = []string { 191"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at ornare risus, quis lacinia magna. Suspendisse egestas purus risus, id rutrum diam porta non. Duis luctus tempus dictum. Maecenas luctus metus vitae nulla consectetur egestas. Curabitur faucibus nunc vel eros semper scelerisque. Proin dictum aliquam lacus dignissim fringilla. Praesent ut quam id dui aliquam vehicula in vitae orci. Fusce imperdiet aliquam quam. Nullam euismod magna tincidunt nisl ullamcorper, dignissim rutrum arcu rutrum. Nulla ac fringilla velit. Duis non pellentesque erat." , 192"In egestas ex et sem vulputate, congue bibendum diam ultrices. Nam auctor dictum enim, in rutrum nulla vestibulum sit amet. Vestibulum vel velit ac sem pellentesque accumsan. Vivamus pharetra mi non arcu tristique gravida. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed molestie lectus nunc, sit amet rhoncus orci laoreet vel. Nulla eget mattis massa. Nunc porta eros sollicitudin lorem dapibus luctus. Vestibulum ut turpis ut nibh tincidunt feugiat. Integer eget augue nunc. Morbi vitae ultrices neque. Nulla et convallis libero. Cras nec faucibus odio. Maecenas lacinia sed odio sit amet ultrices." , 193"Nunc at molestie massa. Phasellus commodo dui odio, quis pulvinar orci eleifend a. In et erat nec nisl auctor facilisis at at orci. Curabitur ut ligula in ipsum consequat consectetur. Suspendisse pulvinar arcu metus, et faucibus risus interdum pharetra. Vestibulum vulputate, arcu at malesuada varius, nisl turpis molestie risus, ut lobortis dolor neque vitae diam. Donec lectus libero, iaculis non diam sit amet, sagittis mattis lectus. Vestibulum a magna molestie neque molestie faucibus sagittis et ante. Etiam porta tincidunt nisi sit amet blandit. Vivamus et tellus diam. Vivamus id dolor placerat, tristique magna non, congue est. Nulla a condimentum nulla. Fusce maximus semper nunc, at bibendum mi. Nam malesuada vitae mi molestie tincidunt. Pellentesque sed vestibulum lectus, eu ultrices ligula. Phasellus id nibh tristique, ultricies diam vel, cursus odio." , 194"Integer sed mi viverra, convallis urna congue, efficitur libero. Duis non eros commodo, ultricies quam hendrerit, molestie velit. Nunc non eros vitae lectus hendrerit gravida. Nunc lacinia neque sapien, et accumsan orci elementum vel. Praesent vel interdum nisl. Duis eget diam turpis. Nunc gravida, lacus dictum congue pharetra, dui est laoreet massa, ac convallis elit est sed dui. Morbi luctus convallis dui id tristique." , 195"Praesent vitae laoreet risus. Sed ac facilisis justo. Morbi fringilla in est vel volutpat. Aliquam erat tortor, posuere ac libero sit amet, vehicula blandit sapien. Nullam feugiat purus eget sapien bibendum, id posuere risus finibus. Aliquam erat volutpat. Pellentesque ac purus accumsan, accumsan mi vel, viverra lectus. Ut sed porta erat, vitae mollis nibh. Nunc dignissim quis tellus sed blandit. Mauris id velit in odio commodo aliquet." , 196}