golang.org/x/text@v0.14.0/unicode/bidi/core_test.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package bidi 6 7 import ( 8 "flag" 9 "fmt" 10 "log" 11 "strconv" 12 "strings" 13 "testing" 14 15 "golang.org/x/text/internal/gen" 16 "golang.org/x/text/internal/testtext" 17 "golang.org/x/text/internal/ucd" 18 "golang.org/x/text/unicode/norm" 19 ) 20 21 var testLevels = flag.Bool("levels", false, "enable testing of levels") 22 23 // TestBidiCore performs the tests in BidiTest.txt. 24 // See https://www.unicode.org/Public/UCD/latest/ucd/BidiTest.txt. 25 func TestBidiCore(t *testing.T) { 26 testtext.SkipIfNotLong(t) 27 28 r := gen.OpenUCDFile("BidiTest.txt") 29 defer r.Close() 30 31 var wantLevels, wantOrder []string 32 p := ucd.New(r, ucd.Part(func(p *ucd.Parser) { 33 s := strings.Split(p.String(0), ":") 34 switch s[0] { 35 case "Levels": 36 wantLevels = strings.Fields(s[1]) 37 case "Reorder": 38 wantOrder = strings.Fields(s[1]) 39 default: 40 log.Fatalf("Unknown part %q.", s[0]) 41 } 42 })) 43 44 for p.Next() { 45 types := []Class{} 46 for _, s := range p.Strings(0) { 47 types = append(types, bidiClass[s]) 48 } 49 // We ignore the bracketing part of the algorithm. 50 pairTypes := make([]bracketType, len(types)) 51 pairValues := make([]rune, len(types)) 52 53 for i := uint(0); i < 3; i++ { 54 if p.Uint(1)&(1<<i) == 0 { 55 continue 56 } 57 lev := level(int(i) - 1) 58 par, err := newParagraph(types, pairTypes, pairValues, lev) 59 if err != nil { 60 t.Error(err) 61 } 62 63 if *testLevels { 64 levels := par.getLevels([]int{len(types)}) 65 for i, s := range wantLevels { 66 if s == "x" { 67 continue 68 } 69 l, _ := strconv.ParseUint(s, 10, 8) 70 if level(l)&1 != levels[i]&1 { 71 t.Errorf("%s:%d:levels: got %v; want %v", p.String(0), lev, levels, wantLevels) 72 break 73 } 74 } 75 } 76 77 order := par.getReordering([]int{len(types)}) 78 gotOrder := filterOrder(types, order) 79 if got, want := fmt.Sprint(gotOrder), fmt.Sprint(wantOrder); got != want { 80 t.Errorf("%s:%d:order: got %v; want %v\noriginal %v", p.String(0), lev, got, want, order) 81 } 82 } 83 } 84 if err := p.Err(); err != nil { 85 log.Fatal(err) 86 } 87 } 88 89 var removeClasses = map[Class]bool{ 90 LRO: true, 91 RLO: true, 92 RLE: true, 93 LRE: true, 94 PDF: true, 95 BN: true, 96 } 97 98 // TestBidiCharacters performs the tests in BidiCharacterTest.txt. 99 // See https://www.unicode.org/Public/UCD/latest/ucd/BidiCharacterTest.txt 100 func TestBidiCharacters(t *testing.T) { 101 testtext.SkipIfNotLong(t) 102 103 ucd.Parse(gen.OpenUCDFile("BidiCharacterTest.txt"), func(p *ucd.Parser) { 104 var ( 105 types []Class 106 pairTypes []bracketType 107 pairValues []rune 108 parLevel level 109 110 wantLevel = level(p.Int(2)) 111 wantLevels = p.Strings(3) 112 wantVisualOrder = p.Strings(4) 113 ) 114 115 switch l := p.Int(1); l { 116 case 0, 1: 117 parLevel = level(l) 118 case 2: 119 parLevel = implicitLevel 120 default: 121 // Spec says to ignore unknown parts. 122 } 123 124 runes := p.Runes(0) 125 126 for _, r := range runes { 127 // Assign the bracket type. 128 if d := norm.NFKD.PropertiesString(string(r)).Decomposition(); d != nil { 129 r = []rune(string(d))[0] 130 } 131 p, _ := LookupRune(r) 132 133 // Assign the class for this rune. 134 types = append(types, p.Class()) 135 136 switch { 137 case !p.IsBracket(): 138 pairTypes = append(pairTypes, bpNone) 139 pairValues = append(pairValues, 0) 140 case p.IsOpeningBracket(): 141 pairTypes = append(pairTypes, bpOpen) 142 pairValues = append(pairValues, r) 143 default: 144 pairTypes = append(pairTypes, bpClose) 145 pairValues = append(pairValues, p.reverseBracket(r)) 146 } 147 } 148 par, err := newParagraph(types, pairTypes, pairValues, parLevel) 149 if err != nil { 150 t.Error(err) 151 } 152 153 // Test results: 154 if got := par.embeddingLevel; got != wantLevel { 155 t.Errorf("%v:level: got %d; want %d", string(runes), got, wantLevel) 156 } 157 158 if *testLevels { 159 gotLevels := getLevelStrings(types, par.getLevels([]int{len(types)})) 160 if got, want := fmt.Sprint(gotLevels), fmt.Sprint(wantLevels); got != want { 161 t.Errorf("%04X %q:%d: got %v; want %v\nval: %x\npair: %v", runes, string(runes), parLevel, got, want, pairValues, pairTypes) 162 } 163 } 164 165 order := par.getReordering([]int{len(types)}) 166 order = filterOrder(types, order) 167 if got, want := fmt.Sprint(order), fmt.Sprint(wantVisualOrder); got != want { 168 t.Errorf("%04X %q:%d: got %v; want %v\ngot order: %s", runes, string(runes), parLevel, got, want, reorder(runes, order)) 169 } 170 }) 171 } 172 173 func getLevelStrings(cl []Class, levels []level) []string { 174 var results []string 175 for i, l := range levels { 176 if !removeClasses[cl[i]] { 177 results = append(results, fmt.Sprint(l)) 178 } else { 179 results = append(results, "x") 180 } 181 } 182 return results 183 } 184 185 func filterOrder(cl []Class, order []int) []int { 186 no := []int{} 187 for _, o := range order { 188 if !removeClasses[cl[o]] { 189 no = append(no, o) 190 } 191 } 192 return no 193 } 194 195 func reorder(r []rune, order []int) string { 196 nr := make([]rune, len(order)) 197 for i, o := range order { 198 nr[i] = r[o] 199 } 200 return string(nr) 201 } 202 203 // bidiClass names and codes taken from class "bc" in 204 // https://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt 205 var bidiClass = map[string]Class{ 206 "AL": AL, // classArabicLetter, 207 "AN": AN, // classArabicNumber, 208 "B": B, // classParagraphSeparator, 209 "BN": BN, // classBoundaryNeutral, 210 "CS": CS, // classCommonSeparator, 211 "EN": EN, // classEuropeanNumber, 212 "ES": ES, // classEuropeanSeparator, 213 "ET": ET, // classEuropeanTerminator, 214 "L": L, // classLeftToRight, 215 "NSM": NSM, // classNonspacingMark, 216 "ON": ON, // classOtherNeutral, 217 "R": R, // classRightToLeft, 218 "S": S, // classSegmentSeparator, 219 "WS": WS, // classWhiteSpace, 220 221 "LRO": LRO, // classLeftToRightOverride, 222 "RLO": RLO, // classRightToLeftOverride, 223 "LRE": LRE, // classLeftToRightEmbedding, 224 "RLE": RLE, // classRightToLeftEmbedding, 225 "PDF": PDF, // classPopDirectionalFormat, 226 "LRI": LRI, // classLeftToRightIsolate, 227 "RLI": RLI, // classRightToLeftIsolate, 228 "FSI": FSI, // classFirstStrongIsolate, 229 "PDI": PDI, // classPopDirectionalIsolate, 230 }