Home | History | Annotate | Download | only in format
      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