1package bidi
2
3import (
4 "bufio"
5 "fmt"
6 "os"
7 "reflect"
8 "strconv"
9 "strings"
10 "testing"
11)
12
13// nefergui: verify pinned Unicode 17 BidiCharacterTest paragraph level, L1
14// character levels and L2 visual order against the full upstream corpus.
15func TestBidiCharacterConformance(t *testing.T) {
16 f, err := os.Open("testdata/BidiCharacterTest.txt")
17 if err != nil {
18 t.Fatal(err)
19 }
20 defer f.Close()
21 scan := bufio.NewScanner(f)
22 scan.Buffer(make([]byte, 4096), 2<<20)
23 lineNo, failures, cases := 0, 0, 0
24 for scan.Scan() {
25 lineNo++
26 s := strings.TrimSpace(scan.Text())
27 if s == "" || s[0] == '#' {
28 continue
29 }
30 parts := strings.Split(s, ";")
31 if len(parts) != 5 {
32 t.Fatalf("line %d: fields %d", lineNo, len(parts))
33 }
34 text := make([]rune, 0)
35 for _, hex := range strings.Fields(parts[0]) {
36 n, e := strconv.ParseUint(hex, 16, 32)
37 if e != nil {
38 t.Fatal(e)
39 }
40 text = append(text, rune(n))
41 }
42 mode, e := strconv.Atoi(parts[1])
43 if e != nil || mode < 0 || mode > 2 {
44 t.Fatalf("line %d: base %q", lineNo, parts[1])
45 }
46 base := Auto
47 if mode == 0 {
48 base = LTR
49 } else if mode == 1 {
50 base = RTL
51 }
52 breaks := []int{len(text)}
53 levels, para, e := Resolve(text, base, breaks)
54 if e != nil {
55 t.Fatalf("line %d: %v", lineNo, e)
56 }
57 expectedPara, e := strconv.Atoi(parts[2])
58 if e != nil {
59 t.Fatal(e)
60 }
61 var expectedLevels []uint8
62 for _, token := range strings.Fields(parts[3]) {
63 if token == "x" {
64 expectedLevels = append(expectedLevels, 255)
65 continue
66 }
67 n, e := strconv.Atoi(token)
68 if e != nil {
69 t.Fatal(e)
70 }
71 expectedLevels = append(expectedLevels, uint8(n))
72 }
73 order, e := Reorder(text, levels, breaks)
74 if e != nil {
75 t.Fatal(e)
76 }
77 var expectedOrder []int
78 for _, token := range strings.Fields(parts[4]) {
79 n, e := strconv.Atoi(token)
80 if e != nil {
81 t.Fatal(e)
82 }
83 expectedOrder = append(expectedOrder, n)
84 }
85 if len(expectedOrder) == 0 {
86 expectedOrder = []int{}
87 }
88 if len(order) != 1 {
89 t.Fatal("missing line")
90 }
91 if int(para) != expectedPara || len(levels) != len(expectedLevels) || !reflect.DeepEqual(order[0], expectedOrder) || !matchLevels(text, levels, expectedLevels) {
92 failures++
93 if failures <= 10 {
94 t.Errorf("line %d: para %d/%d levels %v/%v order %v/%v", lineNo, para, expectedPara, levels, expectedLevels, order[0], expectedOrder)
95 }
96 }
97 cases++
98 }
99 if err := scan.Err(); err != nil {
100 t.Fatal(err)
101 }
102 if failures > 0 {
103 t.Fatalf("%d/%d conformance failures", failures, cases)
104 }
105 t.Logf("%d Unicode 17 BidiCharacterTest cases", cases)
106}
107func matchLevels(text []rune, got, want []uint8) bool {
108 if len(got) != len(want) {
109 return false
110 }
111 for i, n := range want {
112 if n == 255 {
113 p, _ := LookupRune(text[i])
114 if !isRemovedByX9(p.Class()) {
115 panic(fmt.Sprintf("non-X9 x at %d", i))
116 }
117 continue
118 }
119 if got[i] != n {
120 return false
121 }
122 }
123 return true
124}