okami.mom/ultraviolet
git clone https://git.nkp.pet/okami.mom/ultraviolet
006e29f
upstream
1package uv 2 3import ( 4"testing" 5 6"github.com/charmbracelet/x/ansi" 7) 8 9// TestDrawWcWidthCells checks that drawing lays cells out over the same number 10// of columns the terminal advances over. A cell holds a whole grapheme 11// cluster, but under wcwidth its width is the sum of the cluster's codepoints, 12// which is what a terminal without Unicode core mode (DEC mode 2027) advances 13// by. Get that wrong and every cell after it lands in the wrong column. 14func TestDrawWcWidthCells (t * testing.T ) { 15tests := []struct { 16name string 17str string 18widths []int // width of each non-placeholder cell, in order 19 }{ 20 {"ascii" ,"abc" , []int {1 ,1 ,1 }}, 21 {"combining acute" ,"éx" , []int {1 ,1 }}, 22 {"devanagari" ,"नमस्ते" , []int {1 ,1 ,2 }}, 23 {"cjk" ,"世界" , []int {2 ,2 }}, 24 {"vs16" ,"⚠️" , []int {1 }}, 25 {"zwj pair" ,"👨💻" , []int {4 }}, 26 {"zwj flag" ,"🏳️🌈" , []int {3 }}, 27 {"skin tone" ,"👍🏽" , []int {4 }}, 28// Wider than any glyph, and wider than the four columns cells used to 29// be capped at. A capped cluster ends up zero width, which the 30// renderer skips as a wide-cell placeholder, so it never gets drawn. 31 {"zwj family" ,"👨👩👧👦" , []int {8 }}, 32 } 33 34for _ ,tc := range tests { 35t .Run (tc .name ,func (t * testing.T ) { 36buf := NewScreenBuffer (20 ,1 ) 37buf .Method = ansi .WcWidth 38NewStyledString (tc .str ).Draw (buf ,buf .Bounds ()) 39 40var x int 41for i ,want := range tc .widths { 42cell := buf .CellAt (x ,0 ) 43if cell == nil { 44t .Fatalf ("cell %d: no cell at column %d" ,i ,x ) 45 } 46if cell .Width != want { 47t .Errorf ("cell %d: width %d, want %d" ,i ,cell .Width ,want ) 48 } 49// Every column the cell covers past the first has to be a 50// placeholder, otherwise the buffer and the terminal disagree 51// about what occupies them. 52for j := 1 ;j < cell .Width ;j ++ { 53if p := buf .CellAt (x + j ,0 );p == nil || ! p .isWidePlaceholder () { 54t .Errorf ("column %d: got %+v, want a wide placeholder" ,x + j ,p ) 55 } 56 } 57x += max (cell .Width ,1 ) 58 } 59 60if got := ansi .StringWidthWc (tc .str );got != x { 61t .Errorf ("cells cover %d columns, but the string measures %d" ,x ,got ) 62 } 63 }) 64 } 65} 66 67// TestDrawGraphemeWidthCells covers the same strings under the width model of 68// a terminal in Unicode core mode, which measures a cluster as the one glyph 69// it draws. 70func TestDrawGraphemeWidthCells (t * testing.T ) { 71tests := []struct { 72str string 73width int // width of the first cell 74 }{ 75 {"नमस्ते" ,1 }, 76 {"⚠️" ,2 }, 77 {"👨👩👧👦" ,2 }, 78 {"👍🏽" ,2 }, 79 } 80 81for _ ,tc := range tests { 82buf := NewScreenBuffer (20 ,1 ) 83buf .Method = ansi .GraphemeWidth 84NewStyledString (tc .str ).Draw (buf ,buf .Bounds ()) 85 86cell := buf .CellAt (0 ,0 ) 87if cell == nil { 88t .Fatalf ("%q: no cell at column 0" ,tc .str ) 89 } 90if cell .Width != tc .width { 91t .Errorf ("%q: width %d, want %d" ,tc .str ,cell .Width ,tc .width ) 92 } 93 } 94} 95 96// TestNewCellWidths checks that cells built directly agree with the cells 97// [StyledString] lays out. 98func TestNewCellWidths (t * testing.T ) { 99for _ ,tc := range []struct { 100gr string 101want int 102 }{ 103 {"a" ,1 }, 104 {" " ,1 }, 105 {"स्ते" ,2 }, 106 {"👨💻" ,4 }, 107 } { 108if got := NewCell (ansi .WcWidth ,tc .gr ).Width ;got != tc .want { 109t .Errorf ("NewCell(WcWidth, %q).Width = %d, want %d" ,tc .gr ,got ,tc .want ) 110 } 111 } 112}