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