You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

225 lines
5.8 KiB

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