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