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) 2007-2016, International Business Machines Corporation and
      7  * others. All Rights Reserved.
      8  *******************************************************************************
      9  */
     10 
     11 package android.icu.dev.test.format;
     12 
     13 import java.text.FieldPosition;
     14 import java.text.ParsePosition;
     15 import java.util.Collection;
     16 import java.util.LinkedHashMap;
     17 import java.util.LinkedHashSet;
     18 import java.util.Locale;
     19 import java.util.Map;
     20 import java.util.Set;
     21 
     22 import org.junit.Test;
     23 import org.junit.runner.RunWith;
     24 import org.junit.runners.JUnit4;
     25 
     26 import android.icu.dev.test.TestFmwk;
     27 import android.icu.text.DecimalFormat;
     28 import android.icu.text.DecimalFormatSymbols;
     29 import android.icu.text.MessageFormat;
     30 import android.icu.text.NumberFormat;
     31 import android.icu.text.PluralFormat;
     32 import android.icu.text.PluralRules;
     33 import android.icu.text.PluralRules.PluralType;
     34 import android.icu.text.PluralRules.SampleType;
     35 import android.icu.util.ULocale;
     36 import android.icu.testsharding.MainTestShard;
     37 
     38 /**
     39  * @author tschumann (Tim Schumann)
     40  *
     41  */
     42 @MainTestShard
     43 @RunWith(JUnit4.class)
     44 public class PluralFormatUnitTest extends TestFmwk {
     45     @Test
     46     public void TestConstructor() {
     47         // Test correct formatting of numbers.
     48         PluralFormat plFmts[] = new PluralFormat[10];
     49         plFmts[0] = new PluralFormat();
     50         plFmts[0].applyPattern("other{#}");
     51         plFmts[1] = new PluralFormat(PluralRules.DEFAULT);
     52         plFmts[1].applyPattern("other{#}");
     53         plFmts[2] = new PluralFormat(PluralRules.DEFAULT, "other{#}");
     54         plFmts[3] = new PluralFormat("other{#}");
     55         plFmts[4] = new PluralFormat(ULocale.getDefault());
     56         plFmts[4].applyPattern("other{#}");
     57         plFmts[5] = new PluralFormat(ULocale.getDefault(), PluralRules.DEFAULT);
     58         plFmts[5].applyPattern("other{#}");
     59         plFmts[6] = new PluralFormat(ULocale.getDefault(),
     60                 PluralRules.DEFAULT,
     61                 "other{#}");
     62         plFmts[7] = new PluralFormat(ULocale.getDefault(), "other{#}");
     63 
     64         // Constructors with Java Locale
     65         plFmts[8] = new PluralFormat(Locale.getDefault());
     66         plFmts[8].applyPattern("other{#}");
     67         plFmts[9] = new PluralFormat(Locale.getDefault(), PluralRules.DEFAULT);
     68         plFmts[9].applyPattern("other{#}");
     69 
     70         // These plural formats should produce the same output as a
     71         // NumberFormat for the default locale.
     72         NumberFormat numberFmt = NumberFormat.getInstance(ULocale.getDefault());
     73         for (int n = 1; n < 13; n++) {
     74             String result = numberFmt.format(n);
     75             for (int k = 0; k < plFmts.length; ++k) {
     76                 TestFmwk.assertEquals("PluralFormat's output is not as expected",
     77                         result, plFmts[k].format(n));
     78             }
     79         }
     80         // Test some bigger numbers.
     81         // Coverage: Use the format(Object, ...) version.
     82         StringBuffer sb = new StringBuffer();
     83         FieldPosition ignore = new FieldPosition(-1);
     84         for (int n = 100; n < 113; n++) {
     85             String result = numberFmt.format(n*n);
     86             for (int k = 0; k < plFmts.length; ++k) {
     87                 sb.delete(0, sb.length());
     88                 String pfResult = plFmts[k].format(Long.valueOf(n*n), sb, ignore).toString();
     89                 TestFmwk.assertEquals("PluralFormat's output is not as expected", result, pfResult);
     90             }
     91         }
     92     }
     93 
     94     @Test
     95     public void TestEquals() {
     96         // There is neither clone() nor a copy constructor.
     97         PluralFormat de_fee_1 = new PluralFormat(ULocale.GERMAN, PluralType.CARDINAL, "other{fee}");
     98         PluralFormat de_fee_2 = new PluralFormat(ULocale.GERMAN, PluralType.CARDINAL, "other{fee}");
     99         PluralFormat de_fi = new PluralFormat(ULocale.GERMAN, PluralType.CARDINAL, "other{fi}");
    100         PluralFormat fr_fee = new PluralFormat(ULocale.FRENCH, PluralType.CARDINAL, "other{fee}");
    101         assertTrue("different de_fee objects", de_fee_1 != de_fee_2);
    102         assertTrue("equal de_fee objects", de_fee_1.equals(de_fee_2));
    103         assertFalse("different pattern strings", de_fee_1.equals(de_fi));
    104         assertFalse("different locales", de_fee_1.equals(fr_fee));
    105     }
    106 
    107     @Test
    108     public void TestApplyPatternAndFormat() {
    109         // Create rules for testing.
    110         PluralRules oddAndEven =  PluralRules.createRules("odd: n mod 2 is 1");
    111         {
    112             // Test full specified case for testing RuleSet
    113             PluralFormat plfOddAndEven = new PluralFormat(oddAndEven);
    114             plfOddAndEven.applyPattern("odd{# is odd.} other{# is even.}");
    115 
    116             // Test fall back to other.
    117             PluralFormat plfOddOrEven = new PluralFormat(oddAndEven);
    118             plfOddOrEven.applyPattern("other{# is odd or even.}");
    119 
    120             NumberFormat numberFormat =
    121                     NumberFormat.getInstance(ULocale.getDefault());
    122             for (int i = 0; i < 22; ++i) {
    123                 assertEquals("Fallback to other gave wrong results",
    124                         numberFormat.format(i) + " is odd or even.",
    125                         plfOddOrEven.format(i));
    126                 assertEquals("Fully specified PluralFormat gave wrong results",
    127                         numberFormat.format(i) + ((i%2 == 1) ?  " is odd."
    128                                 :  " is even."),
    129                                 plfOddAndEven.format(i));
    130             }
    131 
    132             // ICU 4.8 does not check for duplicate keywords any more.
    133             PluralFormat pf = new PluralFormat(ULocale.ENGLISH, oddAndEven,
    134                     "odd{foo} odd{bar} other{foobar}");
    135             assertEquals("should use first occurrence of the 'odd' keyword", "foo", pf.format(1));
    136             pf.applyPattern("odd{foo} other{bar} other{foobar}");
    137             assertEquals("should use first occurrence of the 'other' keyword", "bar", pf.format(2));
    138             // This sees the first "other" before calling the PluralSelector which then selects "other".
    139             pf.applyPattern("other{foo} odd{bar} other{foobar}");
    140             assertEquals("should use first occurrence of the 'other' keyword", "foo", pf.format(2));
    141         }
    142         // omit other keyword.
    143         try {
    144             PluralFormat plFmt = new PluralFormat(oddAndEven);
    145             plFmt.applyPattern("odd{foo}");
    146             errln("Not defining plural case other should result in an " +
    147                     "exception but did not.");
    148         }catch (IllegalArgumentException e){}
    149 
    150         // ICU 4.8 does not check for unknown keywords any more.
    151         {
    152             PluralFormat pf = new PluralFormat(ULocale.ENGLISH, oddAndEven, "otto{foo} other{bar}");
    153             assertEquals("should ignore unknown keywords", "bar", pf.format(1));
    154         }
    155 
    156         // Test invalid keyword.
    157         try {
    158             PluralFormat plFmt = new PluralFormat(oddAndEven);
    159             plFmt.applyPattern("*odd{foo} other{bar}");
    160             errln("Defining a message for an invalid keyword should result in " +
    161                     "an exception but did not.");
    162         }catch (IllegalArgumentException e){}
    163 
    164         // Test invalid syntax
    165         //   -- comma between keyword{message} clauses
    166         //   -- space in keywords
    167         //   -- keyword{message1}{message2}
    168         try {
    169             PluralFormat plFmt = new PluralFormat(oddAndEven);
    170             plFmt.applyPattern("odd{foo},other{bar}");
    171             errln("Separating keyword{message} items with other characters " +
    172                     "than space should provoke an exception but did not.");
    173         }catch (IllegalArgumentException e){}
    174         try {
    175             PluralFormat plFmt = new PluralFormat(oddAndEven);
    176             plFmt.applyPattern("od d{foo} other{bar}");
    177             errln("Spaces inside keywords should provoke an exception but " +
    178                     "did not.");
    179         }catch (IllegalArgumentException e){}
    180         try {
    181             PluralFormat plFmt = new PluralFormat(oddAndEven);
    182             plFmt.applyPattern("odd{foo}{foobar}other{foo}");
    183             errln("Defining multiple messages after a keyword should provoke " +
    184                     "an exception but did not.");
    185         }catch (IllegalArgumentException e){}
    186 
    187         // Check that nested format is preserved.
    188         {
    189             PluralFormat plFmt = new PluralFormat(oddAndEven);
    190             plFmt.applyPattern("odd{The number {0, number, #.#0} is odd.}" +
    191                     "other{The number {0, number, #.#0} is even.}");
    192             for (int i = 1; i < 3; ++i) {
    193                 assertEquals("format did not preserve a nested format string.",
    194                         ((i % 2 == 1) ?
    195                                 "The number {0, number, #.#0} is odd."
    196                                 : "The number {0, number, #.#0} is even."),
    197                                 plFmt.format(i));
    198             }
    199 
    200         }
    201         // Check that a pound sign in curly braces is preserved.
    202         {
    203             PluralFormat plFmt = new PluralFormat(oddAndEven);
    204             plFmt.applyPattern("odd{The number {1,number,#} is odd.}" +
    205                     "other{The number {2,number,#} is even.}");
    206             for (int i = 1; i < 3; ++i) {
    207                 assertEquals("format did not preserve # inside curly braces.",
    208                         ((i % 2 == 1) ? "The number {1,number,#} is odd."
    209                                 : "The number {2,number,#} is even."),
    210                                 plFmt.format(i));
    211             }
    212 
    213         }
    214     }
    215 
    216 
    217     @Test
    218     public void TestSamples() {
    219         Map<ULocale,Set<ULocale>> same = new LinkedHashMap();
    220         for (ULocale locale : PluralRules.getAvailableULocales()) {
    221             ULocale otherLocale = PluralRules.getFunctionalEquivalent(locale, null);
    222             Set<ULocale> others = same.get(otherLocale);
    223             if (others == null) same.put(otherLocale, others = new LinkedHashSet());
    224             others.add(locale);
    225             continue;
    226         }
    227         for (ULocale locale0 : same.keySet()) {
    228             PluralRules rules = PluralRules.forLocale(locale0);
    229             String localeName = locale0.toString().length() == 0 ? "root" : locale0.toString();
    230             logln(localeName + "\t=\t" + same.get(locale0));
    231             logln(localeName + "\ttoString\t" + rules.toString());
    232             Set<String> keywords = rules.getKeywords();
    233             for (String keyword : keywords) {
    234                 Collection<Double> list = rules.getSamples(keyword);
    235                 if (list.size() == 0) {
    236                     // if there aren't any integer samples, get the decimal ones.
    237                     list = rules.getSamples(keyword, SampleType.DECIMAL);
    238                 }
    239 
    240                 if (list == null || list.size() == 0) {
    241                     errln("Empty list for " + localeName + " : " + keyword);
    242                 } else {
    243                     logln("\t" + localeName + " : " + keyword + " ; " + list);
    244                 }
    245             }
    246         }
    247     }
    248 
    249     @Test
    250     public void TestSetLocale() {
    251         // Create rules for testing.
    252         PluralRules oddAndEven = PluralRules.createRules("odd__: n mod 2 is 1");
    253 
    254         PluralFormat plFmt = new PluralFormat(oddAndEven);
    255         plFmt.applyPattern("odd__{odd} other{even}");
    256         plFmt.setLocale(ULocale.ENGLISH);
    257 
    258         // Check that pattern gets deleted.
    259         NumberFormat nrFmt = NumberFormat.getInstance(ULocale.ENGLISH);
    260         assertEquals("pattern was not resetted by setLocale() call.",
    261                 nrFmt.format(5),
    262                 plFmt.format(5));
    263 
    264         // Check that rules got updated.
    265         plFmt.applyPattern("odd__{odd} other{even}");
    266         assertEquals("SetLocale should reset rules but did not.", "even", plFmt.format(1));
    267 
    268         plFmt.applyPattern("one{one} other{not one}");
    269         for (int i = 0; i < 20; ++i) {
    270             assertEquals("Wrong ruleset loaded by setLocale()",
    271                     ((i==1) ? "one" : "not one"),
    272                     plFmt.format(i));
    273         }
    274     }
    275 
    276     @Test
    277     public void TestParse() {
    278         PluralFormat plFmt = new PluralFormat("other{test}");
    279         try {
    280             plFmt.parse("test", new ParsePosition(0));
    281             errln("parse() should throw an UnsupportedOperationException but " +
    282                     "did not");
    283         } catch (UnsupportedOperationException e) {
    284         }
    285 
    286         plFmt = new PluralFormat("other{test}");
    287         try {
    288             plFmt.parseObject("test", new ParsePosition(0));
    289             errln("parse() should throw an UnsupportedOperationException but " +
    290                     "did not");
    291         } catch (UnsupportedOperationException e) {
    292         }
    293     }
    294 
    295     @Test
    296     public void TestPattern() {
    297         Object[] args = { "acme", null };
    298 
    299         {
    300             // ICU 4.8 PluralFormat does not trim() its pattern any more.
    301             // None of the other *Format classes do.
    302             String pat = "  one {one ''widget} other {# widgets}  ";
    303             PluralFormat pf = new PluralFormat(pat);
    304             assertEquals("should not trim() the pattern", pat, pf.toPattern());
    305         }
    306 
    307         MessageFormat pfmt = new MessageFormat("The disk ''{0}'' contains {1, plural,  one {one ''''{1, number, #.0}'''' widget} other {# widgets}}.");
    308         logln("");
    309         for (int i = 0; i < 3; ++i) {
    310             args[1] = new Integer(i);
    311             logln(pfmt.format(args));
    312         }
    313         /* ICU 4.8 returns null instead of a choice/plural/select Format object
    314          * (because it does not create an object for any "complex" argument).
    315         PluralFormat pf = (PluralFormat)pfmt.getFormatsByArgumentIndex()[1];
    316         logln(pf.toPattern());
    317          */
    318         logln(pfmt.toPattern());
    319         MessageFormat pfmt2 = new MessageFormat(pfmt.toPattern());
    320         assertEquals("message formats are equal", pfmt, pfmt2);
    321     }
    322 
    323     @Test
    324     public void TestExtendedPluralFormat() {
    325         String[] targets = {
    326                 "There are no widgets.",
    327                 "There is one widget.",
    328                 "There is a bling widget and one other widget.",
    329                 "There is a bling widget and 2 other widgets.",
    330                 "There is a bling widget and 3 other widgets.",
    331                 "Widgets, five (5-1=4) there be.",
    332                 "There is a bling widget and 5 other widgets.",
    333                 "There is a bling widget and 6 other widgets.",
    334         };
    335         String pluralStyle =
    336                 "offset:1.0 "
    337                         + "=0 {There are no widgets.} "
    338                         + "=1.0 {There is one widget.} "
    339                         + "=5 {Widgets, five (5-1=#) there be.} "
    340                         + "one {There is a bling widget and one other widget.} "
    341                         + "other {There is a bling widget and # other widgets.}";
    342         PluralFormat pf = new PluralFormat(ULocale.ENGLISH, pluralStyle);
    343         MessageFormat mf = new MessageFormat("{0,plural," + pluralStyle + "}", ULocale.ENGLISH);
    344         Integer args[] = new Integer[1];
    345         for (int i = 0; i <= 7; ++i) {
    346             String result = pf.format(i);
    347             assertEquals("PluralFormat.format(value " + i + ")", targets[i], result);
    348             args[0] = i;
    349             result = mf.format(args);
    350             assertEquals("MessageFormat.format(value " + i + ")", targets[i], result);
    351         }
    352 
    353         // Try explicit values after keywords.
    354         pf.applyPattern("other{zz}other{yy}one{xx}one{ww}=1{vv}=1{uu}");
    355         assertEquals("should find first matching *explicit* value", "vv", pf.format(1));
    356     }
    357 
    358     @Test
    359     public void TestExtendedPluralFormatParsing() {
    360         String[] failures = {
    361                 "offset:1..0 =0 {Foo}",
    362                 "offset:1.0 {Foo}",
    363                 "=0= {Foo}",
    364                 "=0 {Foo} =0.0 {Bar}",
    365                 " = {Foo}",
    366         };
    367         for (String fmt : failures) {
    368             try {
    369                 new PluralFormat(fmt);
    370                 fail("expected exception when parsing '" + fmt + "'");
    371             } catch (IllegalArgumentException e) {
    372                 // ok
    373             }
    374         }
    375     }
    376 
    377     @Test
    378     public void TestOrdinalFormat() {
    379         String pattern = "one{#st file}two{#nd file}few{#rd file}other{#th file}";
    380         PluralFormat pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL, pattern);
    381         assertEquals("PluralFormat.format(321)", "321st file", pf.format(321));
    382         assertEquals("PluralFormat.format(22)", "22nd file", pf.format(22));
    383         assertEquals("PluralFormat.format(3)", "3rd file", pf.format(3));
    384 
    385         // Code coverage: Use the other new-for-PluralType constructor as well.
    386         pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL);
    387         pf.applyPattern(pattern);
    388         assertEquals("PluralFormat.format(456)", "456th file", pf.format(456));
    389         assertEquals("PluralFormat.format(111)", "111th file", pf.format(111));
    390 
    391         // Code coverage: Use Locale not ULocale.
    392         pf = new PluralFormat(Locale.ENGLISH, PluralType.ORDINAL);
    393         pf.applyPattern(pattern);
    394         assertEquals("PluralFormat.format(456)", "456th file", pf.format(456));
    395         assertEquals("PluralFormat.format(111)", "111th file", pf.format(111));
    396     }
    397 
    398     @Test
    399     public void TestDecimals() {
    400         // Simple number replacement.
    401         PluralFormat pf = new PluralFormat(ULocale.ENGLISH, "one{one meter}other{# meters}");
    402         assertEquals("simple format(1)", "one meter", pf.format(1));
    403         assertEquals("simple format(1.5)", "1.5 meters", pf.format(1.5));
    404         PluralFormat pf2 = new PluralFormat(ULocale.ENGLISH,
    405                 "offset:1 one{another meter}other{another # meters}");
    406         pf2.setNumberFormat(new DecimalFormat("0.0", new DecimalFormatSymbols(ULocale.ENGLISH)));
    407         assertEquals("offset-decimals format(1)", "another 0.0 meters", pf2.format(1));
    408         assertEquals("offset-decimals format(2)", "another 1.0 meters", pf2.format(2));
    409         assertEquals("offset-decimals format(2.5)", "another 1.5 meters", pf2.format(2.5));
    410     }
    411 
    412     @Test
    413     public void TestNegative() {
    414         PluralFormat pluralFormat = new PluralFormat(ULocale.ENGLISH, "one{# foot}other{# feet}");
    415         String actual = pluralFormat.format(-3);
    416         assertEquals(pluralFormat.toString(), "-3 feet", actual);
    417     }
    418 }
    419