You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

458 lines
11 KiB

  1. // Copyright 2017 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. package cldrtree
  5. import (
  6. "bytes"
  7. "flag"
  8. "io/ioutil"
  9. "log"
  10. "math/rand"
  11. "path/filepath"
  12. "reflect"
  13. "regexp"
  14. "strconv"
  15. "strings"
  16. "testing"
  17. "golang.org/x/text/internal/gen"
  18. "golang.org/x/text/internal/language/compact"
  19. "golang.org/x/text/language"
  20. "golang.org/x/text/unicode/cldr"
  21. )
  22. var genOutput = flag.Bool("gen", false, "generate output files")
  23. func TestAliasRegexp(t *testing.T) {
  24. testCases := []struct {
  25. alias string
  26. want []string
  27. }{{
  28. alias: "miscPatterns[@numberSystem='latn']",
  29. want: []string{
  30. "miscPatterns[@numberSystem='latn']",
  31. "miscPatterns",
  32. "[@numberSystem='latn']",
  33. "numberSystem",
  34. "latn",
  35. },
  36. }, {
  37. alias: `calendar[@type='greg-foo']/days/`,
  38. want: []string{
  39. "calendar[@type='greg-foo']",
  40. "calendar",
  41. "[@type='greg-foo']",
  42. "type",
  43. "greg-foo",
  44. },
  45. }, {
  46. alias: "eraAbbr",
  47. want: []string{
  48. "eraAbbr",
  49. "eraAbbr",
  50. "",
  51. "",
  52. "",
  53. },
  54. }, {
  55. // match must be anchored at beginning.
  56. alias: `../calendar[@type='gregorian']/days/`,
  57. }}
  58. for _, tc := range testCases {
  59. t.Run(tc.alias, func(t *testing.T) {
  60. got := aliasRe.FindStringSubmatch(tc.alias)
  61. if !reflect.DeepEqual(got, tc.want) {
  62. t.Errorf("got %v; want %v", got, tc.want)
  63. }
  64. })
  65. }
  66. }
  67. func TestBuild(t *testing.T) {
  68. tree1, _ := loadTestdata(t, "test1")
  69. tree2, _ := loadTestdata(t, "test2")
  70. // Constants for second test
  71. const (
  72. calendar = iota
  73. field
  74. )
  75. const (
  76. month = iota
  77. era
  78. filler
  79. cyclicNameSet
  80. )
  81. const (
  82. abbreviated = iota
  83. narrow
  84. wide
  85. )
  86. testCases := []struct {
  87. desc string
  88. tree *Tree
  89. locale string
  90. path []uint16
  91. isFeature bool
  92. result string
  93. }{{
  94. desc: "und/chinese month format wide m1",
  95. tree: tree1,
  96. locale: "und",
  97. path: path(calendar, 0, month, 0, wide, 1),
  98. result: "cM01",
  99. }, {
  100. desc: "und/chinese month format wide m12",
  101. tree: tree1,
  102. locale: "und",
  103. path: path(calendar, 0, month, 0, wide, 12),
  104. result: "cM12",
  105. }, {
  106. desc: "und/non-existing value",
  107. tree: tree1,
  108. locale: "und",
  109. path: path(calendar, 0, month, 0, wide, 13),
  110. result: "",
  111. }, {
  112. desc: "und/dangi:chinese month format wide",
  113. tree: tree1,
  114. locale: "und",
  115. path: path(calendar, 1, month, 0, wide, 1),
  116. result: "cM01",
  117. }, {
  118. desc: "und/chinese month format abbreviated:wide",
  119. tree: tree1,
  120. locale: "und",
  121. path: path(calendar, 0, month, 0, abbreviated, 1),
  122. result: "cM01",
  123. }, {
  124. desc: "und/chinese month format narrow:wide",
  125. tree: tree1,
  126. locale: "und",
  127. path: path(calendar, 0, month, 0, narrow, 1),
  128. result: "cM01",
  129. }, {
  130. desc: "und/gregorian month format wide",
  131. tree: tree1,
  132. locale: "und",
  133. path: path(calendar, 2, month, 0, wide, 2),
  134. result: "gM02",
  135. }, {
  136. desc: "und/gregorian month format:stand-alone narrow",
  137. tree: tree1,
  138. locale: "und",
  139. path: path(calendar, 2, month, 0, narrow, 1),
  140. result: "1",
  141. }, {
  142. desc: "und/gregorian month stand-alone:format abbreviated",
  143. tree: tree1,
  144. locale: "und",
  145. path: path(calendar, 2, month, 1, abbreviated, 1),
  146. result: "gM01",
  147. }, {
  148. desc: "und/gregorian month stand-alone:format wide ",
  149. tree: tree1,
  150. locale: "und",
  151. path: path(calendar, 2, month, 1, abbreviated, 1),
  152. result: "gM01",
  153. }, {
  154. desc: "und/dangi:chinese month format narrow:wide ",
  155. tree: tree1,
  156. locale: "und",
  157. path: path(calendar, 1, month, 0, narrow, 4),
  158. result: "cM04",
  159. }, {
  160. desc: "und/field era displayname 0",
  161. tree: tree2,
  162. locale: "und",
  163. path: path(field, 0, 0, 0),
  164. result: "Era",
  165. }, {
  166. desc: "en/field era displayname 0",
  167. tree: tree2,
  168. locale: "en",
  169. path: path(field, 0, 0, 0),
  170. result: "era",
  171. }, {
  172. desc: "und/calendar hebrew format wide 7-leap",
  173. tree: tree2,
  174. locale: "und",
  175. path: path(calendar, 7, month, 0, wide, 0),
  176. result: "Adar II",
  177. }, {
  178. desc: "en-GB:en-001:en:und/calendar hebrew format wide 7-leap",
  179. tree: tree2,
  180. locale: "en-GB",
  181. path: path(calendar, 7, month, 0, wide, 0),
  182. result: "Adar II",
  183. }, {
  184. desc: "und/buddhist month format wide 11",
  185. tree: tree2,
  186. locale: "und",
  187. path: path(calendar, 0, month, 0, wide, 12),
  188. result: "genWideM12",
  189. }, {
  190. desc: "en-GB/gregorian month stand-alone narrow 2",
  191. tree: tree2,
  192. locale: "en-GB",
  193. path: path(calendar, 6, month, 1, narrow, 3),
  194. result: "gbNarrowM3",
  195. }, {
  196. desc: "en-GB/gregorian month format narrow 3/missing in en-GB",
  197. tree: tree2,
  198. locale: "en-GB",
  199. path: path(calendar, 6, month, 0, narrow, 4),
  200. result: "enNarrowM4",
  201. }, {
  202. desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB",
  203. tree: tree2,
  204. locale: "en-GB",
  205. path: path(calendar, 6, month, 0, narrow, 7),
  206. result: "gregNarrowM7",
  207. }, {
  208. desc: "en-GB/gregorian month format narrow 3/missing in en and en-GB",
  209. tree: tree2,
  210. locale: "en-GB",
  211. path: path(calendar, 6, month, 0, narrow, 7),
  212. result: "gregNarrowM7",
  213. }, {
  214. desc: "en-GB/gregorian era narrow",
  215. tree: tree2,
  216. locale: "en-GB",
  217. path: path(calendar, 6, era, abbreviated, 0, 1),
  218. isFeature: true,
  219. result: "AD",
  220. }, {
  221. desc: "en-GB/gregorian era narrow",
  222. tree: tree2,
  223. locale: "en-GB",
  224. path: path(calendar, 6, era, narrow, 0, 0),
  225. isFeature: true,
  226. result: "BC",
  227. }, {
  228. desc: "en-GB/gregorian era narrow",
  229. tree: tree2,
  230. locale: "en-GB",
  231. path: path(calendar, 6, era, wide, 1, 0),
  232. isFeature: true,
  233. result: "Before Common Era",
  234. }, {
  235. desc: "en-GB/dangi:chinese cyclicName, months, format, narrow:abbreviated 2",
  236. tree: tree2,
  237. locale: "en-GB",
  238. path: path(calendar, 1, cyclicNameSet, 3, 0, 1, 2),
  239. isFeature: true,
  240. result: "year2",
  241. }, {
  242. desc: "en-GB/field era-narrow ",
  243. tree: tree2,
  244. locale: "en-GB",
  245. path: path(field, 2, 0, 0),
  246. result: "era",
  247. }, {
  248. desc: "en-GB/field month-narrow relativeTime future one",
  249. tree: tree2,
  250. locale: "en-GB",
  251. path: path(field, 5, 2, 0, 1),
  252. isFeature: true,
  253. result: "001NarrowFutMOne",
  254. }, {
  255. // Don't fall back to the one of "en".
  256. desc: "en-GB/field month-short relativeTime past one:other",
  257. tree: tree2,
  258. locale: "en-GB",
  259. path: path(field, 4, 2, 1, 1),
  260. isFeature: true,
  261. result: "001ShortPastMOther",
  262. }, {
  263. desc: "en-GB/field month relativeTime future two:other",
  264. tree: tree2,
  265. locale: "en-GB",
  266. path: path(field, 3, 2, 0, 2),
  267. isFeature: true,
  268. result: "enFutMOther",
  269. }}
  270. for _, tc := range testCases {
  271. t.Run(tc.desc, func(t *testing.T) {
  272. tag, _ := compact.RegionalID(compact.Tag(language.MustParse(tc.locale)))
  273. s := tc.tree.lookup(tag, tc.isFeature, tc.path...)
  274. if s != tc.result {
  275. t.Errorf("got %q; want %q", s, tc.result)
  276. }
  277. })
  278. }
  279. }
  280. func path(e ...uint16) []uint16 { return e }
  281. func TestGen(t *testing.T) {
  282. testCases := []string{"test1", "test2"}
  283. for _, tc := range testCases {
  284. t.Run(tc, func(t *testing.T) {
  285. _, got := loadTestdata(t, tc)
  286. // Remove sizes that may vary per architecture.
  287. re := regexp.MustCompile("// Size: [0-9]*")
  288. got = re.ReplaceAllLiteral(got, []byte("// Size: xxxx"))
  289. re = regexp.MustCompile("// Total table size [0-9]*")
  290. got = re.ReplaceAllLiteral(got, []byte("// Total table size: xxxx"))
  291. file := filepath.Join("testdata", tc, "output.go")
  292. if *genOutput {
  293. ioutil.WriteFile(file, got, 0700)
  294. t.SkipNow()
  295. }
  296. b, err := ioutil.ReadFile(file)
  297. if err != nil {
  298. t.Fatalf("failed to open file: %v", err)
  299. }
  300. if want := string(b); string(got) != want {
  301. t.Log(string(got))
  302. t.Errorf("files differ")
  303. }
  304. })
  305. }
  306. }
  307. func loadTestdata(t *testing.T, test string) (tree *Tree, file []byte) {
  308. b := New("test")
  309. var d cldr.Decoder
  310. data, err := d.DecodePath(filepath.Join("testdata", test))
  311. if err != nil {
  312. t.Fatalf("error decoding testdata: %v", err)
  313. }
  314. context := Enum("context")
  315. widthMap := func(s string) string {
  316. // Align era with width values.
  317. if r, ok := map[string]string{
  318. "eraAbbr": "abbreviated",
  319. "eraNarrow": "narrow",
  320. "eraNames": "wide",
  321. }[s]; ok {
  322. s = r
  323. }
  324. return "w" + strings.Title(s)
  325. }
  326. width := EnumFunc("width", widthMap, "abbreviated", "narrow", "wide")
  327. month := Enum("month", "leap7")
  328. relative := EnumFunc("relative", func(s string) string {
  329. x, err := strconv.ParseInt(s, 10, 8)
  330. if err != nil {
  331. log.Fatal("Invalid number:", err)
  332. }
  333. return []string{
  334. "before1",
  335. "current",
  336. "after1",
  337. }[x+1]
  338. })
  339. cycleType := EnumFunc("cycleType", func(s string) string {
  340. return "cyc" + strings.Title(s)
  341. })
  342. r := rand.New(rand.NewSource(0))
  343. for _, loc := range data.Locales() {
  344. ldml := data.RawLDML(loc)
  345. x := b.Locale(language.Make(loc))
  346. if x := x.Index(ldml.Dates.Calendars); x != nil {
  347. for _, cal := range ldml.Dates.Calendars.Calendar {
  348. x := x.IndexFromType(cal)
  349. if x := x.Index(cal.Months); x != nil {
  350. for _, mc := range cal.Months.MonthContext {
  351. x := x.IndexFromType(mc, context)
  352. for _, mw := range mc.MonthWidth {
  353. x := x.IndexFromType(mw, width)
  354. for _, m := range mw.Month {
  355. x.SetValue(m.Yeartype+m.Type, m, month)
  356. }
  357. }
  358. }
  359. }
  360. if x := x.Index(cal.CyclicNameSets); x != nil {
  361. for _, cns := range cal.CyclicNameSets.CyclicNameSet {
  362. x := x.IndexFromType(cns, cycleType)
  363. for _, cc := range cns.CyclicNameContext {
  364. x := x.IndexFromType(cc, context)
  365. for _, cw := range cc.CyclicNameWidth {
  366. x := x.IndexFromType(cw, width)
  367. for _, c := range cw.CyclicName {
  368. x.SetValue(c.Type, c)
  369. }
  370. }
  371. }
  372. }
  373. }
  374. if x := x.Index(cal.Eras); x != nil {
  375. opts := []Option{width, SharedType()}
  376. if x := x.Index(cal.Eras.EraNames, opts...); x != nil {
  377. for _, e := range cal.Eras.EraNames.Era {
  378. x.IndexFromAlt(e).SetValue(e.Type, e)
  379. }
  380. }
  381. if x := x.Index(cal.Eras.EraAbbr, opts...); x != nil {
  382. for _, e := range cal.Eras.EraAbbr.Era {
  383. x.IndexFromAlt(e).SetValue(e.Type, e)
  384. }
  385. }
  386. if x := x.Index(cal.Eras.EraNarrow, opts...); x != nil {
  387. for _, e := range cal.Eras.EraNarrow.Era {
  388. x.IndexFromAlt(e).SetValue(e.Type, e)
  389. }
  390. }
  391. }
  392. {
  393. // Ensure having more than 2 buckets.
  394. f := x.IndexWithName("filler")
  395. b := make([]byte, maxStrlen)
  396. opt := &options{parent: x}
  397. r.Read(b)
  398. f.setValue("0", string(b), opt)
  399. }
  400. }
  401. }
  402. if x := x.Index(ldml.Dates.Fields); x != nil {
  403. for _, f := range ldml.Dates.Fields.Field {
  404. x := x.IndexFromType(f)
  405. for _, d := range f.DisplayName {
  406. x.Index(d).SetValue("", d)
  407. }
  408. for _, r := range f.Relative {
  409. x.Index(r).SetValue(r.Type, r, relative)
  410. }
  411. for _, rt := range f.RelativeTime {
  412. x := x.Index(rt).IndexFromType(rt)
  413. for _, p := range rt.RelativeTimePattern {
  414. x.SetValue(p.Count, p)
  415. }
  416. }
  417. for _, rp := range f.RelativePeriod {
  418. x.Index(rp).SetValue("", rp)
  419. }
  420. }
  421. }
  422. }
  423. tree, err = build(b)
  424. if err != nil {
  425. t.Fatal("error building tree:", err)
  426. }
  427. w := gen.NewCodeWriter()
  428. generate(b, tree, w)
  429. generateTestData(b, w)
  430. buf := &bytes.Buffer{}
  431. if _, err = w.WriteGo(buf, "test", ""); err != nil {
  432. t.Log(buf.String())
  433. t.Fatal("error generating code:", err)
  434. }
  435. return tree, buf.Bytes()
  436. }