| // 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, |
| } |