blob: bc28813d7d4282586a177e76b37938bf21c4610c [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2007-2012, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.dev.test.format;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.impl.Utility;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.PluralRules.PluralType;
import com.ibm.icu.util.ULocale;
/**
* @author dougfelt (Doug Felt)
*/
public class PluralRulesTest extends TestFmwk {
public static void main(String[] args) throws Exception {
new PluralRulesTest().run(args);
}
private static final String[] parseTestData = {
"a: n is 1", "a:1",
"a: n mod 10 is 2", "a:2,12,22",
"a: n is not 1", "a:0,2,3,4,5",
"a: n mod 3 is not 1", "a:0,2,3,5,6,8,9",
"a: n in 2..5", "a:2,3,4,5",
"a: n within 2..5", "a:2,3,4,5",
"a: n not in 2..5", "a:0,1,6,7,8",
"a: n not within 2..5", "a:0,1,6,7,8",
"a: n mod 10 in 2..5", "a:2,3,4,5,12,13,14,15,22,23,24,25",
"a: n mod 10 within 2..5", "a:2,3,4,5,12,13,14,15,22,23,24,25",
"a: n mod 10 is 2 and n is not 12", "a:2,22,32,42",
"a: n mod 10 in 2..3 or n mod 10 is 5", "a:2,3,5,12,13,15,22,23,25",
"a: n mod 10 within 2..3 or n mod 10 is 5", "a:2,3,5,12,13,15,22,23,25",
"a: n is 1 or n is 4 or n is 23", "a:1,4,23",
"a: n mod 2 is 1 and n is not 3 and n in 1..11", "a:1,5,7,9,11",
"a: n mod 2 is 1 and n is not 3 and n within 1..11", "a:1,5,7,9,11",
"a: n mod 2 is 1 or n mod 5 is 1 and n is not 6", "a:1,3,5,7,9,11,13,15,16",
"a: n in 2..5; b: n in 5..8; c: n mod 2 is 1", "a:2,3,4,5;b:6,7,8;c:1,9,11",
"a: n within 2..5; b: n within 5..8; c: n mod 2 is 1", "a:2,3,4,5;b:6,7,8;c:1,9,11",
"a: n in 2,4..6; b: n within 7..9,11..12,20", "a:2,4,5,6;b:7,8,9,11,12,20",
"a: n in 2..8,12 and n not in 4..6", "a:2,3,7,8,12",
"a: n mod 10 in 2,3,5..7 and n is not 12", "a:2,3,5,6,7,13,15,16,17",
"a: n in 2..6,3..7", "a:2,3,4,5,6,7",
};
private String[] getTargetStrings(String targets) {
List list = new ArrayList(50);
String[] valSets = Utility.split(targets, ';');
for (int i = 0; i < valSets.length; ++i) {
String[] temp = Utility.split(valSets[i], ':');
String key = temp[0].trim();
String[] vals = Utility.split(temp[1], ',');
for (int j = 0; j < vals.length; ++j) {
String valString = vals[j].trim();
int val = Integer.parseInt(valString);
while (list.size() <= val) {
list.add(null);
}
if (list.get(val) != null) {
fail("test data error, key: " + list.get(val) + " already set for: " + val);
}
list.set(val, key);
}
}
String[] result = (String[]) list.toArray(new String[list.size()]);
for (int i = 0; i < result.length; ++i) {
if (result[i] == null) {
result[i] = "other";
}
}
return result;
}
private void checkTargets(PluralRules rules, String[] targets) {
for (int i = 0; i < targets.length; ++i) {
assertEquals("value " + i, targets[i], rules.select(i));
}
}
public void testParseEmpty() throws ParseException {
PluralRules rules = PluralRules.parseDescription("a:n");
assertEquals("empty", "a", rules.select(0));
}
public void testParsing() {
for (int i = 0; i < parseTestData.length; i += 2) {
String pattern = parseTestData[i];
String expected = parseTestData[i + 1];
logln("pattern[" + i + "] " + pattern);
try {
PluralRules rules = PluralRules.createRules(pattern);
String[] targets = getTargetStrings(expected);
checkTargets(rules, targets);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
}
private static String[][] equalityTestData = {
{ "a: n is 5",
"a: n in 2..6 and n not in 2..4 and n is not 6" },
{ "a: n in 2..3",
"a: n is 2 or n is 3",
"a: n is 3 and n in 2..5 or n is 2" },
{ "a: n is 12; b:n mod 10 in 2..3",
"b: n mod 10 in 2..3 and n is not 12; a: n in 12..12",
"b: n is 13; a: n is 12; b: n mod 10 is 2 or n mod 10 is 3" },
};
private static String[][] inequalityTestData = {
{ "a: n mod 8 is 3",
"a: n mod 7 is 3"
},
{ "a: n mod 3 is 2 and n is not 5",
"a: n mod 6 is 2 or n is 8 or n is 11"
}
};
private void compareEquality(String id, Object[] objects, boolean shouldBeEqual) {
for (int i = 0; i < objects.length; ++i) {
Object lhs = objects[i];
int start = shouldBeEqual ? i : i + 1;
for (int j = start; j < objects.length; ++j) {
Object rhs = objects[j];
if (shouldBeEqual != lhs.equals(rhs)) {
String msg = shouldBeEqual ? "should be equal" : "should not be equal";
fail(id + " " + msg + " (" + i + ", " + j + "):\n " + lhs + "\n " + rhs);
}
// assertEquals("obj " + i + " and " + j, lhs, rhs);
}
}
}
private void compareEqualityTestSets(String[][] sets, boolean shouldBeEqual) {
for (int i = 0; i < sets.length; ++i) {
String[] patterns = sets[i];
PluralRules[] rules = new PluralRules[patterns.length];
for (int j = 0; j < patterns.length; ++j) {
rules[j] = PluralRules.createRules(patterns[j]);
}
compareEquality("test " + i, rules, shouldBeEqual);
}
}
public void testEquality() {
compareEqualityTestSets(equalityTestData, true);
}
public void testInequality() {
compareEqualityTestSets(inequalityTestData, false);
}
public void testBuiltInRules() {
// spot check
PluralRules rules = PluralRules.forLocale(ULocale.US);
assertEquals("us 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("us 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("us 2", PluralRules.KEYWORD_OTHER, rules.select(2));
rules = PluralRules.forLocale(ULocale.JAPAN);
assertEquals("ja 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("ja 1", PluralRules.KEYWORD_OTHER, rules.select(1));
assertEquals("ja 2", PluralRules.KEYWORD_OTHER, rules.select(2));
rules = PluralRules.forLocale(ULocale.createCanonical("ru"));
assertEquals("ru 0", PluralRules.KEYWORD_MANY, rules.select(0));
assertEquals("ru 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("ru 2", PluralRules.KEYWORD_FEW, rules.select(2));
}
public void testFunctionalEquivalent() {
// spot check
ULocale unknown = ULocale.createCanonical("zz_ZZ");
ULocale un_equiv = PluralRules.getFunctionalEquivalent(unknown, null);
assertEquals("unknown locales have root", ULocale.ROOT, un_equiv);
ULocale jp_equiv = PluralRules.getFunctionalEquivalent(ULocale.JAPAN, null);
ULocale cn_equiv = PluralRules.getFunctionalEquivalent(ULocale.CHINA, null);
assertEquals("japan and china equivalent locales", jp_equiv, cn_equiv);
boolean[] available = new boolean[1];
ULocale russia = ULocale.createCanonical("ru_RU");
ULocale ru_ru_equiv = PluralRules.getFunctionalEquivalent(russia, available);
assertFalse("ru_RU not listed", available[0]);
ULocale russian = ULocale.createCanonical("ru");
ULocale ru_equiv = PluralRules.getFunctionalEquivalent(russian, available);
assertTrue("ru listed", available[0]);
assertEquals("ru and ru_RU equivalent locales", ru_ru_equiv, ru_equiv);
}
public void testAvailableULocales() {
ULocale[] locales = PluralRules.getAvailableULocales();
Set localeSet = new HashSet();
localeSet.addAll(Arrays.asList(locales));
assertEquals("locales are unique in list", locales.length, localeSet.size());
}
/*
* Test the method public static PluralRules parseDescription(String description)
*/
public void TestParseDescription() {
try {
if (PluralRules.DEFAULT != PluralRules.parseDescription("")) {
errln("PluralRules.parseDescription(String) was suppose "
+ "to return PluralRules.DEFAULT when String is of " + "length 0.");
}
} catch (ParseException e) {
errln("PluralRules.parseDescription(String) was not suppose " + "to return an exception.");
}
}
/*
* Tests the method public static PluralRules createRules(String description)
*/
public void TestCreateRules() {
try {
if (PluralRules.createRules(null) != null) {
errln("PluralRules.createRules(String) was suppose to "
+ "return null for an invalid String descrtiption.");
}
} catch (Exception e) {
}
}
/*
* Tests the method public int hashCode()
*/
public void TestHashCode() {
// Bad test, breaks whenever PluralRules implementation changes.
// PluralRules pr = PluralRules.DEFAULT;
// if (106069776 != pr.hashCode()) {
// errln("PluralRules.hashCode() was suppose to return 106069776 " + "when PluralRules.DEFAULT.");
// }
}
/*
* Tests the method public boolean equals(PluralRules rhs)
*/
public void TestEquals() {
PluralRules pr = PluralRules.DEFAULT;
if (pr.equals((PluralRules) null)) {
errln("PluralRules.equals(PluralRules) was supposed to return false " + "when passing null.");
}
}
private void assertRuleValue(String rule, double value) {
assertRuleKeyValue("a:" + rule, "a", value);
}
private void assertRuleKeyValue(String rule, String key, double value) {
PluralRules pr = PluralRules.createRules(rule);
assertEquals(rule, value, pr.getUniqueKeywordValue(key));
}
/*
* Tests getUniqueKeywordValue()
*/
public void TestGetUniqueKeywordValue() {
assertRuleValue("n is 1", 1);
assertRuleValue("n in 2..2", 2);
assertRuleValue("n within 2..2", 2);
assertRuleValue("n in 3..4", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n within 3..4", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n is 2 or n is 2", 2);
assertRuleValue("n is 2 and n is 2", 2);
assertRuleValue("n is 2 or n is 3", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n is 2 and n is 3", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n is 2 or n in 2..3", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n is 2 and n in 2..3", 2);
assertRuleKeyValue("a: n is 1", "not_defined", PluralRules.NO_UNIQUE_VALUE); // key not defined
assertRuleKeyValue("a: n is 1", "other", PluralRules.NO_UNIQUE_VALUE); // key matches default rule
assertRuleValue("n in 2,3", PluralRules.NO_UNIQUE_VALUE);
assertRuleValue("n in 2,3..6 and n not in 2..3,5..6", 4);
}
/**
* The version in PluralFormatUnitTest is not really a test, and it's in the wrong place
* anyway, so I'm putting a variant of it here.
*/
public void TestGetSamples() {
Set<ULocale> uniqueRuleSet = new HashSet<ULocale>();
for (ULocale locale : PluralRules.getAvailableULocales()) {
uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
}
for (ULocale locale : uniqueRuleSet) {
PluralRules rules = PluralRules.forLocale(locale);
logln("\nlocale: " + (locale == ULocale.ROOT ? "root" : locale.toString()) + ", rules: " + rules);
Set<String> keywords = rules.getKeywords();
for (String keyword : keywords) {
Collection<Double> list = rules.getSamples(keyword);
logln("keyword: " + keyword + ", samples: " + list);
assertNotNull("list is not null", list);
if (list != null) {
assertTrue("list is not empty", !list.isEmpty());
for (double value : list) {
assertEquals("value " + value + " matches keyword", keyword, rules.select(value));
}
}
}
assertNull("list is null", rules.getSamples("@#$%^&*"));
}
}
/**
* Returns the empty set if the keyword is not defined, null if there are an unlimited
* number of values for the keyword, or the set of values that trigger the keyword.
*/
public void TestGetAllKeywordValues() {
// data is pairs of strings, the rule, and the expected values as arguments
String[] data = {
"a: n in 2..5", "a: 2,3,4,5; other: null; b:",
"a: n not in 2..5", "a: null; other: null",
"a: n within 2..5", "a: null; other: null",
"a: n not within 2..5", "a: null; other: null",
"a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on out, always null
"a: n in 2..5 and n within 6..8", "a:",
"a: n in 2..5 and n within 5..8", "a: 5",
"a: n within 2..5 and n within 6..8", "a:", // our sampling catches these
"a: n within 2..5 and n within 5..8", "a: 5", // ''
"a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4",
"a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 " +
"or n within 5..6 and n within 6..7", "a: null", // but not this...
"a: n mod 3 is 0", "a: null",
"a: n mod 3 is 0 and n within 1..2", "a:",
"a: n mod 3 is 0 and n within 0..5", "a: 0,3",
"a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, we don't catch...
"a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12",
"a: n in 2,4..6 and n is not 5", "a: 2,4,6",
};
for (int i = 0; i < data.length; i += 2) {
String ruleDescription = data[i];
String result = data[i+1];
PluralRules p = PluralRules.createRules(ruleDescription);
for (String ruleResult : result.split(";")) {
String[] ruleAndValues = ruleResult.split(":");
String keyword = ruleAndValues[0].trim();
String valueList = ruleAndValues.length < 2 ? null : ruleAndValues[1];
if (valueList != null) {
valueList = valueList.trim();
}
Collection<Double> values;
if (valueList == null || valueList.length() == 0) {
values = Collections.<Double>emptyList();
} else if ("null".equals(valueList)) {
values = null;
} else {
values = new ArrayList<Double>();
for (String value : valueList.split(",")) {
values.add(Double.parseDouble(value));
}
}
Collection<Double> results = p.getAllKeywordValues(keyword);
assertEquals("keyword '" + keyword + "'", values, results);
if (results != null) {
try {
results.add(PluralRules.NO_UNIQUE_VALUE);
fail("returned set is modifiable");
} catch (UnsupportedOperationException e) {
// pass
}
}
}
}
}
public void TestOrdinal() {
PluralRules pr = PluralRules.forLocale(ULocale.ENGLISH, PluralType.ORDINAL);
assertEquals("PluralRules(en-ordinal).select(2)", "two", pr.select(2));
}
}