1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************* 6 * Copyright (C) 2011-2012, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 ******************************************************************************* 9 * created on: 2011aug12 10 * created by: Markus W. Scherer 11 */ 12 13 package android.icu.dev.test.format; 14 15 import java.util.ArrayList; 16 import java.util.Iterator; 17 import java.util.List; 18 import java.util.Locale; 19 20 import org.junit.Test; 21 import org.junit.runner.RunWith; 22 import org.junit.runners.JUnit4; 23 24 import android.icu.dev.test.TestFmwk; 25 import android.icu.text.MessagePattern; 26 import android.icu.text.MessagePatternUtil; 27 import android.icu.text.MessagePatternUtil.ArgNode; 28 import android.icu.text.MessagePatternUtil.ComplexArgStyleNode; 29 import android.icu.text.MessagePatternUtil.MessageContentsNode; 30 import android.icu.text.MessagePatternUtil.MessageNode; 31 import android.icu.text.MessagePatternUtil.TextNode; 32 import android.icu.text.MessagePatternUtil.VariantNode; 33 import android.icu.testsharding.MainTestShard; 34 35 /** 36 * Test MessagePatternUtil (MessagePattern-as-tree-of-nodes API) 37 * by building parallel trees of nodes and verifying that they match. 38 */ 39 @MainTestShard 40 @RunWith(JUnit4.class) 41 public final class MessagePatternUtilTest extends TestFmwk { 42 // The following nested "Expect..." classes are used to build 43 // a tree structure parallel to what the MessagePatternUtil class builds. 44 // These nested test classes are not static so that they have access to TestFmwk methods. 45 46 private class ExpectMessageNode { 47 private ExpectMessageNode expectTextThatContains(String s) { 48 contents.add(new ExpectTextNode(s)); 49 return this; 50 } 51 private ExpectMessageNode expectReplaceNumber() { 52 contents.add(new ExpectMessageContentsNode()); 53 return this; 54 } 55 private ExpectMessageNode expectNoneArg(Object name) { 56 contents.add(new ExpectArgNode(name)); 57 return this; 58 } 59 private ExpectMessageNode expectSimpleArg(Object name, String type) { 60 contents.add(new ExpectArgNode(name, type)); 61 return this; 62 } 63 private ExpectMessageNode expectSimpleArg(Object name, String type, String style) { 64 contents.add(new ExpectArgNode(name, type, style)); 65 return this; 66 } 67 private ExpectComplexArgNode expectChoiceArg(Object name) { 68 return expectComplexArg(name, MessagePattern.ArgType.CHOICE); 69 } 70 private ExpectComplexArgNode expectPluralArg(Object name) { 71 return expectComplexArg(name, MessagePattern.ArgType.PLURAL); 72 } 73 private ExpectComplexArgNode expectSelectArg(Object name) { 74 return expectComplexArg(name, MessagePattern.ArgType.SELECT); 75 } 76 private ExpectComplexArgNode expectSelectOrdinalArg(Object name) { 77 return expectComplexArg(name, MessagePattern.ArgType.SELECTORDINAL); 78 } 79 private ExpectComplexArgNode expectComplexArg(Object name, MessagePattern.ArgType argType) { 80 ExpectComplexArgNode complexArg = new ExpectComplexArgNode(this, name, argType); 81 contents.add(complexArg); 82 return complexArg; 83 } 84 private ExpectComplexArgNode finishVariant() { 85 return parent; 86 } 87 private void checkMatches(MessageNode msg) { 88 // matches() prints all errors. 89 matches(msg); 90 } 91 private boolean matches(MessageNode msg) { 92 List<MessageContentsNode> msgContents = msg.getContents(); 93 boolean ok = assertEquals("different numbers of MessageContentsNode", 94 contents.size(), msgContents.size()); 95 if (ok) { 96 Iterator<MessageContentsNode> msgIter = msgContents.iterator(); 97 for (ExpectMessageContentsNode ec : contents) { 98 ok &= ec.matches(msgIter.next()); 99 } 100 } 101 if (!ok) { 102 errln("error in message: " + msg.toString()); 103 } 104 return ok; 105 } 106 private ExpectComplexArgNode parent; // for finishVariant() 107 private List<ExpectMessageContentsNode> contents = 108 new ArrayList<ExpectMessageContentsNode>(); 109 } 110 111 /** 112 * Base class for message contents nodes. 113 * Used directly for REPLACE_NUMBER nodes, subclassed for others. 114 */ 115 private class ExpectMessageContentsNode { 116 protected boolean matches(MessageContentsNode c) { 117 return assertEquals("not a REPLACE_NUMBER node", 118 MessageContentsNode.Type.REPLACE_NUMBER, c.getType()); 119 } 120 } 121 122 private class ExpectTextNode extends ExpectMessageContentsNode { 123 private ExpectTextNode(String subString) { 124 this.subString = subString; 125 } 126 @Override 127 protected boolean matches(MessageContentsNode c) { 128 return 129 assertEquals("not a TextNode", 130 MessageContentsNode.Type.TEXT, c.getType()) && 131 assertTrue("TextNode does not contain \"" + subString + "\"", 132 ((TextNode)c).getText().contains(subString)); 133 } 134 private String subString; 135 } 136 137 private class ExpectArgNode extends ExpectMessageContentsNode { 138 private ExpectArgNode(Object name) { 139 this(name, null, null); 140 } 141 private ExpectArgNode(Object name, String type) { 142 this(name, type, null); 143 } 144 private ExpectArgNode(Object name, String type, String style) { 145 if (name instanceof String) { 146 this.name = (String)name; 147 this.number = -1; 148 } else { 149 this.number = (Integer)name; 150 this.name = Integer.toString(this.number); 151 } 152 if (type == null) { 153 argType = MessagePattern.ArgType.NONE; 154 } else { 155 argType = MessagePattern.ArgType.SIMPLE; 156 } 157 this.type = type; 158 this.style = style; 159 } 160 @Override 161 protected boolean matches(MessageContentsNode c) { 162 boolean ok = 163 assertEquals("not an ArgNode", 164 MessageContentsNode.Type.ARG, c.getType()); 165 if (!ok) { 166 return ok; 167 } 168 ArgNode arg = (ArgNode)c; 169 ok &= assertEquals("unexpected ArgNode argType", 170 argType, arg.getArgType()); 171 ok &= assertEquals("unexpected ArgNode arg name", 172 name, arg.getName()); 173 ok &= assertEquals("unexpected ArgNode arg number", 174 number, arg.getNumber()); 175 ok &= assertEquals("unexpected ArgNode arg type name", 176 type, arg.getTypeName()); 177 ok &= assertEquals("unexpected ArgNode arg style", 178 style, arg.getSimpleStyle()); 179 if (argType == MessagePattern.ArgType.NONE || argType == MessagePattern.ArgType.SIMPLE) { 180 ok &= assertNull("unexpected non-null complex style", arg.getComplexStyle()); 181 } 182 return ok; 183 } 184 private String name; 185 private int number; 186 protected MessagePattern.ArgType argType; 187 private String type; 188 private String style; 189 } 190 191 private class ExpectComplexArgNode extends ExpectArgNode { 192 private ExpectComplexArgNode(ExpectMessageNode parent, 193 Object name, MessagePattern.ArgType argType) { 194 super(name, argType.toString().toLowerCase(Locale.ENGLISH)); 195 this.argType = argType; 196 this.parent = parent; 197 } 198 private ExpectComplexArgNode expectOffset(double offset) { 199 this.offset = offset; 200 explicitOffset = true; 201 return this; 202 } 203 private ExpectMessageNode expectVariant(String selector) { 204 ExpectVariantNode variant = new ExpectVariantNode(this, selector); 205 variants.add(variant); 206 return variant.msg; 207 } 208 private ExpectMessageNode expectVariant(String selector, double value) { 209 ExpectVariantNode variant = new ExpectVariantNode(this, selector, value); 210 variants.add(variant); 211 return variant.msg; 212 } 213 private ExpectMessageNode finishComplexArg() { 214 return parent; 215 } 216 @Override 217 protected boolean matches(MessageContentsNode c) { 218 boolean ok = super.matches(c); 219 if (!ok) { 220 return ok; 221 } 222 ArgNode arg = (ArgNode)c; 223 ComplexArgStyleNode complexStyle = arg.getComplexStyle(); 224 ok &= assertNotNull("unexpected null complex style", complexStyle); 225 if (!ok) { 226 return ok; 227 } 228 ok &= assertEquals("unexpected complex-style argType", 229 argType, complexStyle.getArgType()); 230 ok &= assertEquals("unexpected complex-style hasExplicitOffset()", 231 explicitOffset, complexStyle.hasExplicitOffset()); 232 ok &= assertEquals("unexpected complex-style offset", 233 offset, complexStyle.getOffset()); 234 List<VariantNode> complexVariants = complexStyle.getVariants(); 235 ok &= assertEquals("different number of variants", 236 variants.size(), complexVariants.size()); 237 if (!ok) { 238 return ok; 239 } 240 Iterator<VariantNode> complexIter = complexVariants.iterator(); 241 for (ExpectVariantNode variant : variants) { 242 ok &= variant.matches(complexIter.next()); 243 } 244 return ok; 245 } 246 private ExpectMessageNode parent; // for finishComplexArg() 247 private boolean explicitOffset; 248 private double offset; 249 private List<ExpectVariantNode> variants = new ArrayList<ExpectVariantNode>(); 250 } 251 252 private class ExpectVariantNode { 253 private ExpectVariantNode(ExpectComplexArgNode parent, String selector) { 254 this(parent, selector, MessagePattern.NO_NUMERIC_VALUE); 255 } 256 private ExpectVariantNode(ExpectComplexArgNode parent, String selector, double value) { 257 this.selector = selector; 258 numericValue = value; 259 msg = new ExpectMessageNode(); 260 msg.parent = parent; 261 } 262 private boolean matches(VariantNode v) { 263 boolean ok = assertEquals("different selector strings", 264 selector, v.getSelector()); 265 ok &= assertEquals("different selector strings", 266 isSelectorNumeric(), v.isSelectorNumeric()); 267 ok &= assertEquals("different selector strings", 268 numericValue, v.getSelectorValue()); 269 return ok & msg.matches(v.getMessage()); 270 } 271 private boolean isSelectorNumeric() { 272 return numericValue != MessagePattern.NO_NUMERIC_VALUE; 273 } 274 private String selector; 275 private double numericValue; 276 private ExpectMessageNode msg; 277 } 278 279 // The actual tests start here. ---------------------------------------- *** 280 // Sample message strings are mostly from the MessagePatternUtilDemo. 281 282 @Test 283 public void TestHello() { 284 // No syntax. 285 MessageNode msg = MessagePatternUtil.buildMessageNode("Hello!"); 286 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hello"); 287 expect.checkMatches(msg); 288 } 289 290 @Test 291 public void TestHelloWithApos() { 292 // Literal ASCII apostrophe. 293 MessageNode msg = MessagePatternUtil.buildMessageNode("Hel'lo!"); 294 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hel'lo"); 295 expect.checkMatches(msg); 296 } 297 298 @Test 299 public void TestHelloWithQuote() { 300 // Apostrophe starts quoted literal text. 301 MessageNode msg = MessagePatternUtil.buildMessageNode("Hel'{o!"); 302 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hel{o"); 303 expect.checkMatches(msg); 304 // Terminating the quote should yield the same result. 305 msg = MessagePatternUtil.buildMessageNode("Hel'{'o!"); 306 expect.checkMatches(msg); 307 // Double apostrophe inside quoted literal text still encodes a single apostrophe. 308 msg = MessagePatternUtil.buildMessageNode("a'{bc''de'f"); 309 expect = new ExpectMessageNode().expectTextThatContains("a{bc'def"); 310 expect.checkMatches(msg); 311 } 312 313 @Test 314 public void TestNoneArg() { 315 // Numbered argument. 316 MessageNode msg = MessagePatternUtil.buildMessageNode("abc{0}def"); 317 ExpectMessageNode expect = new ExpectMessageNode(). 318 expectTextThatContains("abc").expectNoneArg(0).expectTextThatContains("def"); 319 expect.checkMatches(msg); 320 // Named argument. 321 msg = MessagePatternUtil.buildMessageNode("abc{ arg }def"); 322 expect = new ExpectMessageNode(). 323 expectTextThatContains("abc").expectNoneArg("arg").expectTextThatContains("def"); 324 expect.checkMatches(msg); 325 // Numbered and named arguments. 326 msg = MessagePatternUtil.buildMessageNode("abc{1}def{arg}ghi"); 327 expect = new ExpectMessageNode(). 328 expectTextThatContains("abc").expectNoneArg(1).expectTextThatContains("def"). 329 expectNoneArg("arg").expectTextThatContains("ghi"); 330 expect.checkMatches(msg); 331 } 332 333 @Test 334 public void TestSimpleArg() { 335 MessageNode msg = MessagePatternUtil.buildMessageNode("a'{bc''de'f{0,number,g'hi''jk'l#}"); 336 ExpectMessageNode expect = new ExpectMessageNode(). 337 expectTextThatContains("a{bc'def").expectSimpleArg(0, "number", "g'hi''jk'l#"); 338 expect.checkMatches(msg); 339 } 340 341 @Test 342 public void TestSelectArg() { 343 MessageNode msg = MessagePatternUtil.buildMessageNode( 344 "abc{2, number}ghi{3, select, xx {xxx} other {ooo}} xyz"); 345 ExpectMessageNode expect = new ExpectMessageNode(). 346 expectTextThatContains("abc").expectSimpleArg(2, "number"). 347 expectTextThatContains("ghi"). 348 expectSelectArg(3). 349 expectVariant("xx").expectTextThatContains("xxx").finishVariant(). 350 expectVariant("other").expectTextThatContains("ooo").finishVariant(). 351 finishComplexArg(). 352 expectTextThatContains(" xyz"); 353 expect.checkMatches(msg); 354 } 355 356 @Test 357 public void TestPluralArg() { 358 // Plural with only keywords. 359 MessageNode msg = MessagePatternUtil.buildMessageNode( 360 "abc{num_people, plural, offset:17 few{fff} other {oooo}}xyz"); 361 ExpectMessageNode expect = new ExpectMessageNode(). 362 expectTextThatContains("abc"). 363 expectPluralArg("num_people"). 364 expectOffset(17). 365 expectVariant("few").expectTextThatContains("fff").finishVariant(). 366 expectVariant("other").expectTextThatContains("oooo").finishVariant(). 367 finishComplexArg(). 368 expectTextThatContains("xyz"); 369 expect.checkMatches(msg); 370 // Plural with explicit-value selectors. 371 msg = MessagePatternUtil.buildMessageNode( 372 "abc{ num , plural , offset: 2 =1 {1} =-1 {-1} =3.14 {3.14} other {oo} }xyz"); 373 expect = new ExpectMessageNode(). 374 expectTextThatContains("abc"). 375 expectPluralArg("num"). 376 expectOffset(2). 377 expectVariant("=1", 1).expectTextThatContains("1").finishVariant(). 378 expectVariant("=-1", -1).expectTextThatContains("-1").finishVariant(). 379 expectVariant("=3.14", 3.14).expectTextThatContains("3.14").finishVariant(). 380 expectVariant("other").expectTextThatContains("oo").finishVariant(). 381 finishComplexArg(). 382 expectTextThatContains("xyz"); 383 expect.checkMatches(msg); 384 // Plural with number replacement. 385 msg = MessagePatternUtil.buildMessageNode( 386 "a_{0,plural,other{num=#'#'=#'#'={1,number,##}!}}_z"); 387 expect = new ExpectMessageNode(). 388 expectTextThatContains("a_"). 389 expectPluralArg(0). 390 expectVariant("other"). 391 expectTextThatContains("num=").expectReplaceNumber(). 392 expectTextThatContains("#=").expectReplaceNumber(). 393 expectTextThatContains("#=").expectSimpleArg(1, "number", "##"). 394 expectTextThatContains("!").finishVariant(). 395 finishComplexArg(). 396 expectTextThatContains("_z"); 397 expect.checkMatches(msg); 398 // Plural with explicit offset:0. 399 msg = MessagePatternUtil.buildMessageNode( 400 "a_{0,plural,offset:0 other{num=#!}}_z"); 401 expect = new ExpectMessageNode(). 402 expectTextThatContains("a_"). 403 expectPluralArg(0). 404 expectOffset(0). 405 expectVariant("other"). 406 expectTextThatContains("num=").expectReplaceNumber(). 407 expectTextThatContains("!").finishVariant(). 408 finishComplexArg(). 409 expectTextThatContains("_z"); 410 expect.checkMatches(msg); 411 } 412 413 414 @Test 415 public void TestSelectOrdinalArg() { 416 MessageNode msg = MessagePatternUtil.buildMessageNode( 417 "abc{num, selectordinal, offset:17 =0{null} few{fff} other {oooo}}xyz"); 418 ExpectMessageNode expect = new ExpectMessageNode(). 419 expectTextThatContains("abc"). 420 expectSelectOrdinalArg("num"). 421 expectOffset(17). 422 expectVariant("=0", 0).expectTextThatContains("null").finishVariant(). 423 expectVariant("few").expectTextThatContains("fff").finishVariant(). 424 expectVariant("other").expectTextThatContains("oooo").finishVariant(). 425 finishComplexArg(). 426 expectTextThatContains("xyz"); 427 expect.checkMatches(msg); 428 } 429 430 @Test 431 public void TestChoiceArg() { 432 MessageNode msg = MessagePatternUtil.buildMessageNode( 433 "a_{0,choice,- #-inf| 5 five | 99 # ninety'|'nine }_z"); 434 ExpectMessageNode expect = new ExpectMessageNode(). 435 expectTextThatContains("a_"). 436 expectChoiceArg(0). 437 expectVariant("#", Double.NEGATIVE_INFINITY). 438 expectTextThatContains("-inf").finishVariant(). 439 expectVariant("", 5).expectTextThatContains(" five ").finishVariant(). 440 expectVariant("#", 99).expectTextThatContains(" ninety|nine ").finishVariant(). 441 finishComplexArg(). 442 expectTextThatContains("_z"); 443 expect.checkMatches(msg); 444 } 445 446 @Test 447 public void TestComplexArgs() { 448 MessageNode msg = MessagePatternUtil.buildMessageNode( 449 "I don't {a,plural,other{w'{'on't #'#'}} and "+ 450 "{b,select,other{shan't'}'}} '{'''know'''}' and "+ 451 "{c,choice,0#can't'|'}"+ 452 "{z,number,#'#'###.00'}'}."); 453 ExpectMessageNode expect = new ExpectMessageNode(). 454 expectTextThatContains("I don't "). 455 expectPluralArg("a"). 456 expectVariant("other"). 457 expectTextThatContains("w{on't ").expectReplaceNumber(). 458 expectTextThatContains("#").finishVariant(). 459 finishComplexArg(). 460 expectTextThatContains(" and "). 461 expectSelectArg("b"). 462 expectVariant("other").expectTextThatContains("shan't}").finishVariant(). 463 finishComplexArg(). 464 expectTextThatContains(" {'know'} and "). 465 expectChoiceArg("c"). 466 expectVariant("#", 0).expectTextThatContains("can't|").finishVariant(). 467 finishComplexArg(). 468 expectSimpleArg("z", "number", "#'#'###.00'}'"). 469 expectTextThatContains("."); 470 expect.checkMatches(msg); 471 } 472 473 /** 474 * @return the text string of the VariantNode's message; 475 * assumes that its message consists of only text 476 */ 477 private String variantText(VariantNode v) { 478 return ((TextNode)v.getMessage().getContents().get(0)).getText(); 479 } 480 481 @Test 482 public void TestPluralVariantsByType() { 483 MessageNode msg = MessagePatternUtil.buildMessageNode( 484 "{p,plural,a{A}other{O}=4{iv}b{B}other{U}=2{ii}}"); 485 ExpectMessageNode expect = new ExpectMessageNode(). 486 expectPluralArg("p"). 487 expectVariant("a").expectTextThatContains("A").finishVariant(). 488 expectVariant("other").expectTextThatContains("O").finishVariant(). 489 expectVariant("=4", 4).expectTextThatContains("iv").finishVariant(). 490 expectVariant("b").expectTextThatContains("B").finishVariant(). 491 expectVariant("other").expectTextThatContains("U").finishVariant(). 492 expectVariant("=2", 2).expectTextThatContains("ii").finishVariant(). 493 finishComplexArg(); 494 if (!expect.matches(msg)) { 495 return; 496 } 497 List<VariantNode> numericVariants = new ArrayList<VariantNode>(); 498 List<VariantNode> keywordVariants = new ArrayList<VariantNode>(); 499 VariantNode other = 500 ((ArgNode)msg.getContents().get(0)).getComplexStyle(). 501 getVariantsByType(numericVariants, keywordVariants); 502 assertEquals("'other' selector", "other", other.getSelector()); 503 assertEquals("message string of first 'other'", "O", variantText(other)); 504 505 assertEquals("numericVariants.size()", 2, numericVariants.size()); 506 VariantNode v = numericVariants.get(0); 507 assertEquals("numericVariants[0] selector", "=4", v.getSelector()); 508 assertEquals("numericVariants[0] selector value", 4., v.getSelectorValue()); 509 assertEquals("numericVariants[0] text", "iv", variantText(v)); 510 v = numericVariants.get(1); 511 assertEquals("numericVariants[1] selector", "=2", v.getSelector()); 512 assertEquals("numericVariants[1] selector value", 2., v.getSelectorValue()); 513 assertEquals("numericVariants[1] text", "ii", variantText(v)); 514 515 assertEquals("keywordVariants.size()", 2, keywordVariants.size()); 516 v = keywordVariants.get(0); 517 assertEquals("keywordVariants[0] selector", "a", v.getSelector()); 518 assertFalse("keywordVariants[0].isSelectorNumeric()", v.isSelectorNumeric()); 519 assertEquals("keywordVariants[0] text", "A", variantText(v)); 520 v = keywordVariants.get(1); 521 assertEquals("keywordVariants[1] selector", "b", v.getSelector()); 522 assertFalse("keywordVariants[1].isSelectorNumeric()", v.isSelectorNumeric()); 523 assertEquals("keywordVariants[1] text", "B", variantText(v)); 524 } 525 526 @Test 527 public void TestSelectVariantsByType() { 528 MessageNode msg = MessagePatternUtil.buildMessageNode( 529 "{s,select,a{A}other{O}b{B}other{U}}"); 530 ExpectMessageNode expect = new ExpectMessageNode(). 531 expectSelectArg("s"). 532 expectVariant("a").expectTextThatContains("A").finishVariant(). 533 expectVariant("other").expectTextThatContains("O").finishVariant(). 534 expectVariant("b").expectTextThatContains("B").finishVariant(). 535 expectVariant("other").expectTextThatContains("U").finishVariant(). 536 finishComplexArg(); 537 if (!expect.matches(msg)) { 538 return; 539 } 540 // Check that we can use numericVariants = null. 541 List<VariantNode> keywordVariants = new ArrayList<VariantNode>(); 542 VariantNode other = 543 ((ArgNode)msg.getContents().get(0)).getComplexStyle(). 544 getVariantsByType(null, keywordVariants); 545 assertEquals("'other' selector", "other", other.getSelector()); 546 assertEquals("message string of first 'other'", "O", variantText(other)); 547 548 assertEquals("keywordVariants.size()", 2, keywordVariants.size()); 549 VariantNode v = keywordVariants.get(0); 550 assertEquals("keywordVariants[0] selector", "a", v.getSelector()); 551 assertFalse("keywordVariants[0].isSelectorNumeric()", v.isSelectorNumeric()); 552 assertEquals("keywordVariants[0] text", "A", variantText(v)); 553 v = keywordVariants.get(1); 554 assertEquals("keywordVariants[1] selector", "b", v.getSelector()); 555 assertFalse("keywordVariants[1].isSelectorNumeric()", v.isSelectorNumeric()); 556 assertEquals("keywordVariants[1] text", "B", variantText(v)); 557 } 558 } 559