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