225 lines
5.8 KiB
Go
225 lines
5.8 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package bidi
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/text/internal/gen"
|
|
"golang.org/x/text/internal/testtext"
|
|
"golang.org/x/text/internal/ucd"
|
|
"golang.org/x/text/unicode/norm"
|
|
)
|
|
|
|
var testLevels = flag.Bool("levels", false, "enable testing of levels")
|
|
|
|
// TestBidiCore performs the tests in BidiTest.txt.
|
|
// See http://www.unicode.org/Public/UCD/latest/ucd/BidiTest.txt.
|
|
func TestBidiCore(t *testing.T) {
|
|
testtext.SkipIfNotLong(t)
|
|
|
|
r := gen.OpenUCDFile("BidiTest.txt")
|
|
defer r.Close()
|
|
|
|
var wantLevels, wantOrder []string
|
|
p := ucd.New(r, ucd.Part(func(p *ucd.Parser) {
|
|
s := strings.Split(p.String(0), ":")
|
|
switch s[0] {
|
|
case "Levels":
|
|
wantLevels = strings.Fields(s[1])
|
|
case "Reorder":
|
|
wantOrder = strings.Fields(s[1])
|
|
default:
|
|
log.Fatalf("Unknown part %q.", s[0])
|
|
}
|
|
}))
|
|
|
|
for p.Next() {
|
|
types := []Class{}
|
|
for _, s := range p.Strings(0) {
|
|
types = append(types, bidiClass[s])
|
|
}
|
|
// We ignore the bracketing part of the algorithm.
|
|
pairTypes := make([]bracketType, len(types))
|
|
pairValues := make([]rune, len(types))
|
|
|
|
for i := uint(0); i < 3; i++ {
|
|
if p.Uint(1)&(1<<i) == 0 {
|
|
continue
|
|
}
|
|
lev := level(int(i) - 1)
|
|
par := newParagraph(types, pairTypes, pairValues, lev)
|
|
|
|
if *testLevels {
|
|
levels := par.getLevels([]int{len(types)})
|
|
for i, s := range wantLevels {
|
|
if s == "x" {
|
|
continue
|
|
}
|
|
l, _ := strconv.ParseUint(s, 10, 8)
|
|
if level(l)&1 != levels[i]&1 {
|
|
t.Errorf("%s:%d:levels: got %v; want %v", p.String(0), lev, levels, wantLevels)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
order := par.getReordering([]int{len(types)})
|
|
gotOrder := filterOrder(types, order)
|
|
if got, want := fmt.Sprint(gotOrder), fmt.Sprint(wantOrder); got != want {
|
|
t.Errorf("%s:%d:order: got %v; want %v\noriginal %v", p.String(0), lev, got, want, order)
|
|
}
|
|
}
|
|
}
|
|
if err := p.Err(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
var removeClasses = map[Class]bool{
|
|
LRO: true,
|
|
RLO: true,
|
|
RLE: true,
|
|
LRE: true,
|
|
PDF: true,
|
|
BN: true,
|
|
}
|
|
|
|
// TestBidiCharacters performs the tests in BidiCharacterTest.txt.
|
|
// See http://www.unicode.org/Public/UCD/latest/ucd/BidiCharacterTest.txt
|
|
func TestBidiCharacters(t *testing.T) {
|
|
testtext.SkipIfNotLong(t)
|
|
|
|
ucd.Parse(gen.OpenUCDFile("BidiCharacterTest.txt"), func(p *ucd.Parser) {
|
|
var (
|
|
types []Class
|
|
pairTypes []bracketType
|
|
pairValues []rune
|
|
parLevel level
|
|
|
|
wantLevel = level(p.Int(2))
|
|
wantLevels = p.Strings(3)
|
|
wantVisualOrder = p.Strings(4)
|
|
)
|
|
|
|
switch l := p.Int(1); l {
|
|
case 0, 1:
|
|
parLevel = level(l)
|
|
case 2:
|
|
parLevel = implicitLevel
|
|
default:
|
|
// Spec says to ignore unknown parts.
|
|
}
|
|
|
|
runes := p.Runes(0)
|
|
|
|
for _, r := range runes {
|
|
// Assign the bracket type.
|
|
if d := norm.NFKD.PropertiesString(string(r)).Decomposition(); d != nil {
|
|
r = []rune(string(d))[0]
|
|
}
|
|
p, _ := LookupRune(r)
|
|
|
|
// Assign the class for this rune.
|
|
types = append(types, p.Class())
|
|
|
|
switch {
|
|
case !p.IsBracket():
|
|
pairTypes = append(pairTypes, bpNone)
|
|
pairValues = append(pairValues, 0)
|
|
case p.IsOpeningBracket():
|
|
pairTypes = append(pairTypes, bpOpen)
|
|
pairValues = append(pairValues, r)
|
|
default:
|
|
pairTypes = append(pairTypes, bpClose)
|
|
pairValues = append(pairValues, p.reverseBracket(r))
|
|
}
|
|
}
|
|
par := newParagraph(types, pairTypes, pairValues, parLevel)
|
|
|
|
// Test results:
|
|
if got := par.embeddingLevel; got != wantLevel {
|
|
t.Errorf("%v:level: got %d; want %d", string(runes), got, wantLevel)
|
|
}
|
|
|
|
if *testLevels {
|
|
gotLevels := getLevelStrings(types, par.getLevels([]int{len(types)}))
|
|
if got, want := fmt.Sprint(gotLevels), fmt.Sprint(wantLevels); got != want {
|
|
t.Errorf("%04X %q:%d: got %v; want %v\nval: %x\npair: %v", runes, string(runes), parLevel, got, want, pairValues, pairTypes)
|
|
}
|
|
}
|
|
|
|
order := par.getReordering([]int{len(types)})
|
|
order = filterOrder(types, order)
|
|
if got, want := fmt.Sprint(order), fmt.Sprint(wantVisualOrder); got != want {
|
|
t.Errorf("%04X %q:%d: got %v; want %v\ngot order: %s", runes, string(runes), parLevel, got, want, reorder(runes, order))
|
|
}
|
|
})
|
|
}
|
|
|
|
func getLevelStrings(cl []Class, levels []level) []string {
|
|
var results []string
|
|
for i, l := range levels {
|
|
if !removeClasses[cl[i]] {
|
|
results = append(results, fmt.Sprint(l))
|
|
} else {
|
|
results = append(results, "x")
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
func filterOrder(cl []Class, order []int) []int {
|
|
no := []int{}
|
|
for _, o := range order {
|
|
if !removeClasses[cl[o]] {
|
|
no = append(no, o)
|
|
}
|
|
}
|
|
return no
|
|
}
|
|
|
|
func reorder(r []rune, order []int) string {
|
|
nr := make([]rune, len(order))
|
|
for i, o := range order {
|
|
nr[i] = r[o]
|
|
}
|
|
return string(nr)
|
|
}
|
|
|
|
// bidiClass names and codes taken from class "bc" in
|
|
// http://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt
|
|
var bidiClass = map[string]Class{
|
|
"AL": AL, // classArabicLetter,
|
|
"AN": AN, // classArabicNumber,
|
|
"B": B, // classParagraphSeparator,
|
|
"BN": BN, // classBoundaryNeutral,
|
|
"CS": CS, // classCommonSeparator,
|
|
"EN": EN, // classEuropeanNumber,
|
|
"ES": ES, // classEuropeanSeparator,
|
|
"ET": ET, // classEuropeanTerminator,
|
|
"L": L, // classLeftToRight,
|
|
"NSM": NSM, // classNonspacingMark,
|
|
"ON": ON, // classOtherNeutral,
|
|
"R": R, // classRightToLeft,
|
|
"S": S, // classSegmentSeparator,
|
|
"WS": WS, // classWhiteSpace,
|
|
|
|
"LRO": LRO, // classLeftToRightOverride,
|
|
"RLO": RLO, // classRightToLeftOverride,
|
|
"LRE": LRE, // classLeftToRightEmbedding,
|
|
"RLE": RLE, // classRightToLeftEmbedding,
|
|
"PDF": PDF, // classPopDirectionalFormat,
|
|
"LRI": LRI, // classLeftToRightIsolate,
|
|
"RLI": RLI, // classRightToLeftIsolate,
|
|
"FSI": FSI, // classFirstStrongIsolate,
|
|
"PDI": PDI, // classPopDirectionalIsolate,
|
|
}
|