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 
     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