github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/text/cases/context_test.go (about) 1 // Copyright 2014 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 cases 6 7 import ( 8 "strings" 9 "testing" 10 "unicode" 11 12 "github.com/insionng/yougam/libraries/x/text/language" 13 "github.com/insionng/yougam/libraries/x/text/transform" 14 "github.com/insionng/yougam/libraries/x/text/unicode/norm" 15 "github.com/insionng/yougam/libraries/x/text/unicode/rangetable" 16 ) 17 18 // The following definitions are taken directly from Chapter 3 of The Unicode 19 // Standard. 20 21 func propCased(r rune) bool { 22 return propLower(r) || propUpper(r) || unicode.IsTitle(r) 23 } 24 25 func propLower(r rune) bool { 26 return unicode.IsLower(r) || unicode.Is(unicode.Other_Lowercase, r) 27 } 28 29 func propUpper(r rune) bool { 30 return unicode.IsUpper(r) || unicode.Is(unicode.Other_Uppercase, r) 31 } 32 33 func propIgnore(r rune) bool { 34 if unicode.In(r, unicode.Mn, unicode.Me, unicode.Cf, unicode.Lm, unicode.Sk) { 35 return true 36 } 37 return caseIgnorable[r] 38 } 39 40 func hasBreakProp(r rune) bool { 41 // binary search over ranges 42 lo := 0 43 hi := len(breakProp) 44 for lo < hi { 45 m := lo + (hi-lo)/2 46 bp := &breakProp[m] 47 if bp.lo <= r && r <= bp.hi { 48 return true 49 } 50 if r < bp.lo { 51 hi = m 52 } else { 53 lo = m + 1 54 } 55 } 56 return false 57 } 58 59 func contextFromRune(r rune) *context { 60 c := context{dst: make([]byte, 128), src: []byte(string(r)), atEOF: true} 61 c.next() 62 return &c 63 } 64 65 func TestCaseProperties(t *testing.T) { 66 assigned := rangetable.Assigned(UnicodeVersion) 67 coreVersion := rangetable.Assigned(unicode.Version) 68 for r := rune(0); r <= lastRuneForTesting; r++ { 69 if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) { 70 continue 71 } 72 c := contextFromRune(r) 73 if got, want := c.info.isCaseIgnorable(), propIgnore(r); got != want { 74 t.Errorf("caseIgnorable(%U): got %v; want %v (%x)", r, got, want, c.info) 75 } 76 // New letters may change case types, but existing case pairings should 77 // not change. See Case Pair Stability in 78 // http://unicode.org/policies/stability_policy.html. 79 if rf := unicode.SimpleFold(r); rf != r && unicode.In(rf, assigned) { 80 if got, want := c.info.isCased(), propCased(r); got != want { 81 t.Errorf("cased(%U): got %v; want %v (%x)", r, got, want, c.info) 82 } 83 if got, want := c.caseType() == cUpper, propUpper(r); got != want { 84 t.Errorf("upper(%U): got %v; want %v (%x)", r, got, want, c.info) 85 } 86 if got, want := c.caseType() == cLower, propLower(r); got != want { 87 t.Errorf("lower(%U): got %v; want %v (%x)", r, got, want, c.info) 88 } 89 } 90 if got, want := c.info.isBreak(), hasBreakProp(r); got != want { 91 t.Errorf("isBreak(%U): got %v; want %v (%x)", r, got, want, c.info) 92 } 93 } 94 // TODO: get title case from unicode file. 95 } 96 97 func TestMapping(t *testing.T) { 98 assigned := rangetable.Assigned(UnicodeVersion) 99 coreVersion := rangetable.Assigned(unicode.Version) 100 apply := func(r rune, f func(c *context) bool) string { 101 c := contextFromRune(r) 102 f(c) 103 return string(c.dst[:c.pDst]) 104 } 105 106 for r, tt := range special { 107 if got, want := apply(r, lower), tt.toLower; got != want { 108 t.Errorf("lowerSpecial:(%U): got %+q; want %+q", r, got, want) 109 } 110 if got, want := apply(r, title), tt.toTitle; got != want { 111 t.Errorf("titleSpecial:(%U): got %+q; want %+q", r, got, want) 112 } 113 if got, want := apply(r, upper), tt.toUpper; got != want { 114 t.Errorf("upperSpecial:(%U): got %+q; want %+q", r, got, want) 115 } 116 } 117 118 for r := rune(0); r <= lastRuneForTesting; r++ { 119 if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) { 120 continue 121 } 122 if rf := unicode.SimpleFold(r); rf == r || !unicode.In(rf, assigned) { 123 continue 124 } 125 if _, ok := special[r]; ok { 126 continue 127 } 128 want := string(unicode.ToLower(r)) 129 if got := apply(r, lower); got != want { 130 t.Errorf("lower:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want)) 131 } 132 133 want = string(unicode.ToUpper(r)) 134 if got := apply(r, upper); got != want { 135 t.Errorf("upper:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want)) 136 } 137 138 want = string(unicode.ToTitle(r)) 139 if got := apply(r, title); got != want { 140 t.Errorf("title:%q (%U): got %q %U; want %q %U", r, r, got, []rune(got), want, []rune(want)) 141 } 142 } 143 } 144 145 func runeFoldData(r rune) (x struct{ simple, full, special string }) { 146 x = foldMap[r] 147 if x.simple == "" { 148 x.simple = string(unicode.ToLower(r)) 149 } 150 if x.full == "" { 151 x.full = string(unicode.ToLower(r)) 152 } 153 if x.special == "" { 154 x.special = x.full 155 } 156 return 157 } 158 159 func TestFoldData(t *testing.T) { 160 assigned := rangetable.Assigned(UnicodeVersion) 161 coreVersion := rangetable.Assigned(unicode.Version) 162 apply := func(r rune, f func(c *context) bool) (string, info) { 163 c := contextFromRune(r) 164 f(c) 165 return string(c.dst[:c.pDst]), c.info.cccType() 166 } 167 for r := rune(0); r <= lastRuneForTesting; r++ { 168 if !unicode.In(r, assigned) || !unicode.In(r, coreVersion) { 169 continue 170 } 171 x := runeFoldData(r) 172 if got, info := apply(r, foldFull); got != x.full { 173 t.Errorf("full:%q (%U): got %q %U; want %q %U (ccc=%x)", r, r, got, []rune(got), x.full, []rune(x.full), info) 174 } 175 // TODO: special and simple. 176 } 177 } 178 179 func TestCCC(t *testing.T) { 180 assigned := rangetable.Assigned(UnicodeVersion) 181 normVersion := rangetable.Assigned(norm.Version) 182 for r := rune(0); r <= lastRuneForTesting; r++ { 183 if !unicode.In(r, assigned) || !unicode.In(r, normVersion) { 184 continue 185 } 186 c := contextFromRune(r) 187 188 p := norm.NFC.PropertiesString(string(r)) 189 want := cccOther 190 switch p.CCC() { 191 case 0: 192 want = cccZero 193 case above: 194 want = cccAbove 195 } 196 if got := c.info.cccType(); got != want { 197 t.Errorf("%U: got %x; want %x", r, got, want) 198 } 199 } 200 } 201 202 func TestWordBreaks(t *testing.T) { 203 for i, tt := range breakTest { 204 parts := strings.Split(tt, "|") 205 want := "" 206 for _, s := range parts { 207 want += Title(language.Und).String(s) 208 } 209 src := strings.Join(parts, "") 210 got := Title(language.Und).String(src) 211 if got != want { 212 t.Errorf("%d: title(%q) = %q; want %q", i, src, got, want) 213 } 214 } 215 } 216 217 func TestContext(t *testing.T) { 218 tests := []struct { 219 desc string 220 dstSize int 221 atEOF bool 222 src string 223 out string 224 nSrc int 225 err error 226 ops string 227 prefixArg string 228 prefixWant bool 229 }{{ 230 desc: "next: past end, atEOF, no checkpoint", 231 dstSize: 10, 232 atEOF: true, 233 src: "12", 234 out: "", 235 nSrc: 2, 236 ops: "next;next;next", 237 // Test that calling prefix with a non-empty argument when the buffer 238 // is depleted returns false. 239 prefixArg: "x", 240 prefixWant: false, 241 }, { 242 desc: "next: not at end, atEOF, no checkpoint", 243 dstSize: 10, 244 atEOF: false, 245 src: "12", 246 out: "", 247 nSrc: 0, 248 err: transform.ErrShortSrc, 249 ops: "next;next", 250 prefixArg: "", 251 prefixWant: true, 252 }, { 253 desc: "next: past end, !atEOF, no checkpoint", 254 dstSize: 10, 255 atEOF: false, 256 src: "12", 257 out: "", 258 nSrc: 0, 259 err: transform.ErrShortSrc, 260 ops: "next;next;next", 261 prefixArg: "", 262 prefixWant: true, 263 }, { 264 desc: "next: past end, !atEOF, checkpoint", 265 dstSize: 10, 266 atEOF: false, 267 src: "12", 268 out: "", 269 nSrc: 2, 270 ops: "next;next;checkpoint;next", 271 prefixArg: "", 272 prefixWant: true, 273 }, { 274 desc: "copy: exact count, atEOF, no checkpoint", 275 dstSize: 2, 276 atEOF: true, 277 src: "12", 278 out: "12", 279 nSrc: 2, 280 ops: "next;copy;next;copy;next", 281 prefixArg: "", 282 prefixWant: true, 283 }, { 284 desc: "copy: past end, !atEOF, no checkpoint", 285 dstSize: 2, 286 atEOF: false, 287 src: "12", 288 out: "", 289 nSrc: 0, 290 err: transform.ErrShortSrc, 291 ops: "next;copy;next;copy;next", 292 prefixArg: "", 293 prefixWant: true, 294 }, { 295 desc: "copy: past end, !atEOF, checkpoint", 296 dstSize: 2, 297 atEOF: false, 298 src: "12", 299 out: "12", 300 nSrc: 2, 301 ops: "next;copy;next;copy;checkpoint;next", 302 prefixArg: "", 303 prefixWant: true, 304 }, { 305 desc: "copy: short dst", 306 dstSize: 1, 307 atEOF: false, 308 src: "12", 309 out: "", 310 nSrc: 0, 311 err: transform.ErrShortDst, 312 ops: "next;copy;next;copy;checkpoint;next", 313 prefixArg: "12", 314 prefixWant: false, 315 }, { 316 desc: "copy: short dst, checkpointed", 317 dstSize: 1, 318 atEOF: false, 319 src: "12", 320 out: "1", 321 nSrc: 1, 322 err: transform.ErrShortDst, 323 ops: "next;copy;checkpoint;next;copy;next", 324 prefixArg: "", 325 prefixWant: true, 326 }, { 327 desc: "writeString: simple", 328 dstSize: 3, 329 atEOF: true, 330 src: "1", 331 out: "1ab", 332 nSrc: 1, 333 ops: "next;copy;writeab;next", 334 prefixArg: "", 335 prefixWant: true, 336 }, { 337 desc: "writeString: short dst", 338 dstSize: 2, 339 atEOF: true, 340 src: "12", 341 out: "", 342 nSrc: 0, 343 err: transform.ErrShortDst, 344 ops: "next;copy;writeab;next", 345 prefixArg: "2", 346 prefixWant: true, 347 }, { 348 desc: "writeString: simple", 349 dstSize: 3, 350 atEOF: true, 351 src: "12", 352 out: "1ab", 353 nSrc: 2, 354 ops: "next;copy;next;writeab;next", 355 prefixArg: "", 356 prefixWant: true, 357 }, { 358 desc: "writeString: short dst", 359 dstSize: 2, 360 atEOF: true, 361 src: "12", 362 out: "", 363 nSrc: 0, 364 err: transform.ErrShortDst, 365 ops: "next;copy;next;writeab;next", 366 prefixArg: "1", 367 prefixWant: false, 368 }, { 369 desc: "prefix", 370 dstSize: 2, 371 atEOF: true, 372 src: "12", 373 out: "", 374 nSrc: 0, 375 // Context will assign an ErrShortSrc if the input wasn't exhausted. 376 err: transform.ErrShortSrc, 377 prefixArg: "12", 378 prefixWant: true, 379 }} 380 for _, tt := range tests { 381 c := context{dst: make([]byte, tt.dstSize), src: []byte(tt.src), atEOF: tt.atEOF} 382 383 for _, op := range strings.Split(tt.ops, ";") { 384 switch op { 385 case "next": 386 c.next() 387 case "checkpoint": 388 c.checkpoint() 389 case "writeab": 390 c.writeString("ab") 391 case "copy": 392 c.copy() 393 case "": 394 default: 395 t.Fatalf("unknown op %q", op) 396 } 397 } 398 if got := c.hasPrefix(tt.prefixArg); got != tt.prefixWant { 399 t.Errorf("%s:\nprefix was %v; want %v", tt.desc, got, tt.prefixWant) 400 } 401 nDst, nSrc, err := c.ret() 402 if err != tt.err { 403 t.Errorf("%s:\nerror was %v; want %v", tt.desc, err, tt.err) 404 } 405 if out := string(c.dst[:nDst]); out != tt.out { 406 t.Errorf("%s:\nout was %q; want %q", tt.desc, out, tt.out) 407 } 408 if nSrc != tt.nSrc { 409 t.Errorf("%s:\nnSrc was %d; want %d", tt.desc, nSrc, tt.nSrc) 410 } 411 } 412 }