OSDN Git Service

libgo: Update to weekly.2011-12-06.
[pf3gnuchains/gcc-fork.git] / libgo / go / exp / norm / normregtest.go
1 // Copyright 2011 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 main
6
7 import (
8         "bufio"
9         "bytes"
10         "exp/norm"
11         "flag"
12         "fmt"
13         "io"
14         "log"
15         "net/http"
16         "os"
17         "path"
18         "regexp"
19         "runtime"
20         "strconv"
21         "strings"
22         "time"
23         "unicode/utf8"
24 )
25
26 func main() {
27         flag.Parse()
28         loadTestData()
29         CharacterByCharacterTests()
30         StandardTests()
31         PerformanceTest()
32         if errorCount == 0 {
33                 fmt.Println("PASS")
34         }
35 }
36
37 const file = "NormalizationTest.txt"
38
39 var url = flag.String("url",
40         "http://www.unicode.org/Public/6.0.0/ucd/"+file,
41         "URL of Unicode database directory")
42 var localFiles = flag.Bool("local",
43         false,
44         "data files have been copied to the current directory; for debugging only")
45
46 var logger = log.New(os.Stderr, "", log.Lshortfile)
47
48 // This regression test runs the test set in NormalizationTest.txt
49 // (taken from http://www.unicode.org/Public/6.0.0/ucd/).
50 //
51 // NormalizationTest.txt has form:
52 // @Part0 # Specific cases
53 // #
54 // 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
55 // 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
56 //
57 // Each test has 5 columns (c1, c2, c3, c4, c5), where 
58 // (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1))
59 //
60 // CONFORMANCE:
61 // 1. The following invariants must be true for all conformant implementations
62 //
63 //    NFC
64 //      c2 ==  NFC(c1) ==  NFC(c2) ==  NFC(c3)
65 //      c4 ==  NFC(c4) ==  NFC(c5)
66 //
67 //    NFD
68 //      c3 ==  NFD(c1) ==  NFD(c2) ==  NFD(c3)
69 //      c5 ==  NFD(c4) ==  NFD(c5)
70 //
71 //    NFKC
72 //      c4 == NFKC(c1) == NFKC(c2) == NFKC(c3) == NFKC(c4) == NFKC(c5)
73 //
74 //    NFKD
75 //      c5 == NFKD(c1) == NFKD(c2) == NFKD(c3) == NFKD(c4) == NFKD(c5)
76 //
77 // 2. For every code point X assigned in this version of Unicode that is not
78 //    specifically listed in Part 1, the following invariants must be true
79 //    for all conformant implementations:
80 //
81 //      X == NFC(X) == NFD(X) == NFKC(X) == NFKD(X)
82 //
83
84 // Column types.
85 const (
86         cRaw = iota
87         cNFC
88         cNFD
89         cNFKC
90         cNFKD
91         cMaxColumns
92 )
93
94 // Holds data from NormalizationTest.txt
95 var part []Part
96
97 type Part struct {
98         name   string
99         number int
100         tests  []Test
101 }
102
103 type Test struct {
104         name   string
105         partnr int
106         number int
107         r      rune                // used for character by character test
108         cols   [cMaxColumns]string // Each has 5 entries, see below.
109 }
110
111 func (t Test) Name() string {
112         if t.number < 0 {
113                 return part[t.partnr].name
114         }
115         return fmt.Sprintf("%s:%d", part[t.partnr].name, t.number)
116 }
117
118 var partRe = regexp.MustCompile(`@Part(\d) # (.*)\n$`)
119 var testRe = regexp.MustCompile(`^` + strings.Repeat(`([\dA-F ]+);`, 5) + ` # (.*)\n?$`)
120
121 var counter int
122
123 // Load the data form NormalizationTest.txt
124 func loadTestData() {
125         if *localFiles {
126                 pwd, _ := os.Getwd()
127                 *url = "file://" + path.Join(pwd, file)
128         }
129         t := &http.Transport{}
130         t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
131         c := &http.Client{Transport: t}
132         resp, err := c.Get(*url)
133         if err != nil {
134                 logger.Fatal(err)
135         }
136         if resp.StatusCode != 200 {
137                 logger.Fatal("bad GET status for "+file, resp.Status)
138         }
139         f := resp.Body
140         defer f.Close()
141         input := bufio.NewReader(f)
142         for {
143                 line, err := input.ReadString('\n')
144                 if err != nil {
145                         if err == io.EOF {
146                                 break
147                         }
148                         logger.Fatal(err)
149                 }
150                 if len(line) == 0 || line[0] == '#' {
151                         continue
152                 }
153                 m := partRe.FindStringSubmatch(line)
154                 if m != nil {
155                         if len(m) < 3 {
156                                 logger.Fatal("Failed to parse Part: ", line)
157                         }
158                         i, err := strconv.Atoi(m[1])
159                         if err != nil {
160                                 logger.Fatal(err)
161                         }
162                         name := m[2]
163                         part = append(part, Part{name: name[:len(name)-1], number: i})
164                         continue
165                 }
166                 m = testRe.FindStringSubmatch(line)
167                 if m == nil || len(m) < 7 {
168                         logger.Fatalf(`Failed to parse: "%s" result: %#v`, line, m)
169                 }
170                 test := Test{name: m[6], partnr: len(part) - 1, number: counter}
171                 counter++
172                 for j := 1; j < len(m)-1; j++ {
173                         for _, split := range strings.Split(m[j], " ") {
174                                 r, err := strconv.ParseUint(split, 16, 64)
175                                 if err != nil {
176                                         logger.Fatal(err)
177                                 }
178                                 if test.r == 0 {
179                                         // save for CharacterByCharacterTests
180                                         test.r = int(r)
181                                 }
182                                 var buf [utf8.UTFMax]byte
183                                 sz := utf8.EncodeRune(buf[:], rune(r))
184                                 test.cols[j-1] += string(buf[:sz])
185                         }
186                 }
187                 part := &part[len(part)-1]
188                 part.tests = append(part.tests, test)
189         }
190 }
191
192 var fstr = []string{"NFC", "NFD", "NFKC", "NFKD"}
193
194 var errorCount int
195
196 func cmpResult(t *Test, name string, f norm.Form, gold, test, result string) {
197         if gold != result {
198                 errorCount++
199                 if errorCount > 20 {
200                         return
201                 }
202                 st, sr, sg := []rune(test), []rune(result), []rune(gold)
203                 logger.Printf("%s:%s: %s(%X)=%X; want:%X: %s",
204                         t.Name(), name, fstr[f], st, sr, sg, t.name)
205         }
206 }
207
208 func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bool) {
209         if result != want {
210                 errorCount++
211                 if errorCount > 20 {
212                         return
213                 }
214                 logger.Printf("%s:%s: %s(%X)=%v; want: %v", t.Name(), name, fstr[f], []rune(test), result, want)
215         }
216 }
217
218 func doTest(t *Test, f norm.Form, gold, test string) {
219         result := f.Bytes([]byte(test))
220         cmpResult(t, "Bytes", f, gold, test, string(result))
221         for i := range test {
222                 out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...)
223                 cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out))
224         }
225         cmpIsNormal(t, "IsNormal", f, test, f.IsNormal([]byte(test)), test == gold)
226 }
227
228 func doConformanceTests(t *Test, partn int) {
229         for i := 0; i <= 2; i++ {
230                 doTest(t, norm.NFC, t.cols[1], t.cols[i])
231                 doTest(t, norm.NFD, t.cols[2], t.cols[i])
232                 doTest(t, norm.NFKC, t.cols[3], t.cols[i])
233                 doTest(t, norm.NFKD, t.cols[4], t.cols[i])
234         }
235         for i := 3; i <= 4; i++ {
236                 doTest(t, norm.NFC, t.cols[3], t.cols[i])
237                 doTest(t, norm.NFD, t.cols[4], t.cols[i])
238                 doTest(t, norm.NFKC, t.cols[3], t.cols[i])
239                 doTest(t, norm.NFKD, t.cols[4], t.cols[i])
240         }
241 }
242
243 func CharacterByCharacterTests() {
244         tests := part[1].tests
245         last := 0
246         for i := 0; i <= len(tests); i++ { // last one is special case
247                 var r int
248                 if i == len(tests) {
249                         r = 0x2FA1E // Don't have to go to 0x10FFFF
250                 } else {
251                         r = tests[i].r
252                 }
253                 for last++; last < r; last++ {
254                         // Check all characters that were not explicitly listed in the test.
255                         t := &Test{partnr: 1, number: -1}
256                         char := string(last)
257                         doTest(t, norm.NFC, char, char)
258                         doTest(t, norm.NFD, char, char)
259                         doTest(t, norm.NFKC, char, char)
260                         doTest(t, norm.NFKD, char, char)
261                 }
262                 if i < len(tests) {
263                         doConformanceTests(&tests[i], 1)
264                 }
265         }
266 }
267
268 func StandardTests() {
269         for _, j := range []int{0, 2, 3} {
270                 for _, test := range part[j].tests {
271                         doConformanceTests(&test, j)
272                 }
273         }
274 }
275
276 // PerformanceTest verifies that normalization is O(n). If any of the
277 // code does not properly check for maxCombiningChars, normalization
278 // may exhibit O(n**2) behavior.
279 func PerformanceTest() {
280         runtime.GOMAXPROCS(2)
281         success := make(chan bool, 1)
282         go func() {
283                 buf := bytes.Repeat([]byte("\u035D"), 1024*1024)
284                 buf = append(buf, "\u035B"...)
285                 norm.NFC.Append(nil, buf...)
286                 success <- true
287         }()
288         timeout := time.After(1e9)
289         select {
290         case <-success:
291                 // test completed before the timeout
292         case <-timeout:
293                 errorCount++
294                 logger.Printf(`unexpectedly long time to complete PerformanceTest`)
295         }
296 }