1cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath/* GENERATED SOURCE. DO NOT MODIFY. */ 2cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath// © 2017 and later: Unicode, Inc. and others. 3cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath// License & terms of use: http://www.unicode.org/copyright.html#License 4cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathpackage android.icu.dev.test.number; 5cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 6cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport static org.junit.Assert.assertEquals; 7cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport static org.junit.Assert.assertNotEquals; 8cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport static org.junit.Assert.assertTrue; 9cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport static org.junit.Assert.fail; 10cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 11cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.io.ByteArrayInputStream; 12cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.io.ByteArrayOutputStream; 13cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.io.IOException; 14cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.io.ObjectInputStream; 15cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.io.ObjectOutputStream; 16cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.lang.reflect.Field; 17cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.lang.reflect.InvocationTargetException; 18ab762bb740405d0fefcccf4a0899a234f995be13Narayan Kamathimport java.lang.reflect.Method; 19cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.lang.reflect.Modifier; 20cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath// TODO: enable in Java 8: import java.lang.reflect.Parameter; 21cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.math.BigDecimal; 22cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.math.BigInteger; 23cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.math.MathContext; 24cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.math.RoundingMode; 25cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.util.HashMap; 26cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.util.HashSet; 27cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.util.Map; 28cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.util.Set; 29cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 30cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport org.junit.Test; 31cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 32cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.dev.test.serializable.SerializableTestUtility; 33cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.impl.number.DecimalFormatProperties; 34cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.impl.number.Padder.PadPosition; 35cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.impl.number.Parse.GroupingMode; 36cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.impl.number.Parse.ParseMode; 37cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.impl.number.PatternStringParser; 38cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.text.CompactDecimalFormat.CompactStyle; 39cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.text.CurrencyPluralInfo; 40cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.text.MeasureFormat.FormatWidth; 41cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.text.PluralRules; 42cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.util.Currency; 43cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.util.Currency.CurrencyUsage; 44cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.util.MeasureUnit; 45cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.util.ULocale; 46cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport android.icu.testsharding.MainTestShard; 47cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 48cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath@MainTestShard 49cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathpublic class PropertiesTest { 50cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 51cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath @Test 52cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath public void testBasicEquals() { 53cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p1 = new DecimalFormatProperties(); 54cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p2 = new DecimalFormatProperties(); 55cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath assertEquals(p1, p2); 56cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 57cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath p1.setPositivePrefix("abc"); 58cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath assertNotEquals(p1, p2); 59cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath p2.setPositivePrefix("xyz"); 60cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath assertNotEquals(p1, p2); 61cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath p1.setPositivePrefix("xyz"); 62cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath assertEquals(p1, p2); 63cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath } 64cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 65cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath @Test 66cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath public void testFieldCoverage() { 67cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p0 = new DecimalFormatProperties(); 68cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p1 = new DecimalFormatProperties(); 69cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p2 = new DecimalFormatProperties(); 70cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p3 = new DecimalFormatProperties(); 71cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath DecimalFormatProperties p4 = new DecimalFormatProperties(); 72cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 73cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath Set<Integer> hashCodes = new HashSet<Integer>(); 74cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath Field[] fields = DecimalFormatProperties.class.getDeclaredFields(); 75cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath for (Field field : fields) { 76cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath if (Modifier.isStatic(field.getModifiers())) { 77cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath continue; 78cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath } 79cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath 80cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath // Check for getters and setters 81cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath String fieldNamePascalCase = 82cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1); 83cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath String getterName = "get" + fieldNamePascalCase; 84 String setterName = "set" + fieldNamePascalCase; 85 Method getter, setter; 86 try { 87 getter = DecimalFormatProperties.class.getMethod(getterName); 88 assertEquals( 89 "Getter does not return correct type", field.getType(), getter.getReturnType()); 90 } catch (NoSuchMethodException e) { 91 fail("Could not find method " + getterName + " for field " + field); 92 continue; 93 } catch (SecurityException e) { 94 fail("Could not access method " + getterName + " for field " + field); 95 continue; 96 } 97 try { 98 setter = DecimalFormatProperties.class.getMethod(setterName, field.getType()); 99 assertEquals( 100 "Method " + setterName + " does not return correct type", 101 DecimalFormatProperties.class, 102 setter.getReturnType()); 103 } catch (NoSuchMethodException e) { 104 fail("Could not find method " + setterName + " for field " + field); 105 continue; 106 } catch (SecurityException e) { 107 fail("Could not access method " + setterName + " for field " + field); 108 continue; 109 } 110 111 // Check for parameter name equality. 112 // The parameter name is not always available, depending on compiler settings. 113 // TODO: Enable in Java 8 114 /* 115 Parameter param = setter.getParameters()[0]; 116 if (!param.getName().subSequence(0, 3).equals("arg")) { 117 assertEquals("Parameter name should equal field name", field.getName(), param.getName()); 118 } 119 */ 120 121 try { 122 // Check for default value (should be null for objects) 123 if (field.getType() != Integer.TYPE && field.getType() != Boolean.TYPE) { 124 Object default0 = getter.invoke(p0); 125 assertEquals("Field " + field + " has non-null default value:", null, default0); 126 } 127 128 // Check for getter, equals, and hash code behavior 129 Object val0 = getSampleValueForType(field.getType(), 0); 130 Object val1 = getSampleValueForType(field.getType(), 1); 131 Object val2 = getSampleValueForType(field.getType(), 2); 132 assertNotEquals(val0, val1); 133 setter.invoke(p1, val0); 134 setter.invoke(p2, val0); 135 assertEquals(p1, p2); 136 assertEquals(p1.hashCode(), p2.hashCode()); 137 assertEquals(getter.invoke(p1), getter.invoke(p2)); 138 assertEquals(getter.invoke(p1), val0); 139 assertNotEquals(getter.invoke(p1), val1); 140 hashCodes.add(p1.hashCode()); 141 setter.invoke(p1, val1); 142 assertNotEquals("Field " + field + " is missing from equals()", p1, p2); 143 assertNotEquals(getter.invoke(p1), getter.invoke(p2)); 144 assertNotEquals(getter.invoke(p1), val0); 145 assertEquals(getter.invoke(p1), val1); 146 setter.invoke(p1, val0); 147 assertEquals("Field " + field + " setter might have side effects", p1, p2); 148 assertEquals(p1.hashCode(), p2.hashCode()); 149 assertEquals(getter.invoke(p1), getter.invoke(p2)); 150 setter.invoke(p1, val1); 151 setter.invoke(p2, val1); 152 assertEquals(p1, p2); 153 assertEquals(p1.hashCode(), p2.hashCode()); 154 assertEquals(getter.invoke(p1), getter.invoke(p2)); 155 setter.invoke(p1, val2); 156 setter.invoke(p1, val1); 157 assertEquals("Field " + field + " setter might have side effects", p1, p2); 158 assertEquals(p1.hashCode(), p2.hashCode()); 159 assertEquals(getter.invoke(p1), getter.invoke(p2)); 160 hashCodes.add(p1.hashCode()); 161 162 // Check for clone behavior 163 DecimalFormatProperties copy = p1.clone(); 164 assertEquals("Field " + field + " did not get copied in clone", p1, copy); 165 assertEquals(p1.hashCode(), copy.hashCode()); 166 assertEquals(getter.invoke(p1), getter.invoke(copy)); 167 168 // Check for copyFrom behavior 169 setter.invoke(p1, val0); 170 assertNotEquals(p1, p2); 171 assertNotEquals(getter.invoke(p1), getter.invoke(p2)); 172 p2.copyFrom(p1); 173 assertEquals("Field " + field + " is missing from copyFrom()", p1, p2); 174 assertEquals(p1.hashCode(), p2.hashCode()); 175 assertEquals(getter.invoke(p1), getter.invoke(p2)); 176 177 // Load values into p3 and p4 for clear() behavior test 178 setter.invoke(p3, getSampleValueForType(field.getType(), 3)); 179 hashCodes.add(p3.hashCode()); 180 setter.invoke(p4, getSampleValueForType(field.getType(), 4)); 181 hashCodes.add(p4.hashCode()); 182 } catch (IllegalAccessException e) { 183 fail("Could not access method for field " + field); 184 } catch (IllegalArgumentException e) { 185 fail("Could call method for field " + field); 186 } catch (InvocationTargetException e) { 187 fail("Could invoke method on target for field " + field); 188 } 189 } 190 191 // Check for clear() behavior 192 assertNotEquals(p3, p4); 193 p3.clear(); 194 p4.clear(); 195 assertEquals("A field is missing from the clear() function", p3, p4); 196 197 // A good hashCode() implementation should produce very few collisions. We added at most 198 // 4*fields.length codes to the set. We'll say the implementation is good if we had at least 199 // fields.length unique values. 200 // TODO: Should the requirement be stronger than this? 201 assertTrue( 202 "Too many hash code collisions: " + hashCodes.size() + " out of " + (fields.length * 4), 203 hashCodes.size() >= fields.length); 204 } 205 206 /** 207 * Creates a valid sample instance of the given type. Used to simulate getters and setters. 208 * 209 * @param type The type to generate. 210 * @param seed An integer seed, guaranteed to be positive. The same seed should generate two 211 * instances that are equal. A different seed should in general generate two instances that 212 * are not equal; this might not always be possible, such as with booleans or enums where 213 * there are limited possible values. 214 * @return An instance of the specified type. 215 */ 216 Object getSampleValueForType(Class<?> type, int seed) { 217 if (type == Integer.TYPE) { 218 return seed * 1000001; 219 220 } else if (type == Boolean.TYPE) { 221 return (seed % 2) == 0; 222 223 } else if (type == BigDecimal.class) { 224 if (seed == 0) return null; 225 return new BigDecimal(seed * 1000002); 226 227 } else if (type == String.class) { 228 if (seed == 0) return null; 229 return BigInteger.valueOf(seed * 1000003).toString(32); 230 231 } else if (type == CompactStyle.class) { 232 if (seed == 0) return null; 233 CompactStyle[] values = CompactStyle.values(); 234 return values[seed % values.length]; 235 236 } else if (type == Currency.class) { 237 if (seed == 0) return null; 238 Object[] currencies = Currency.getAvailableCurrencies().toArray(); 239 return currencies[seed % currencies.length]; 240 241 } else if (type == CurrencyPluralInfo.class) { 242 if (seed == 0) return null; 243 ULocale[] locales = ULocale.getAvailableLocales(); 244 return CurrencyPluralInfo.getInstance(locales[seed % locales.length]); 245 246 } else if (type == CurrencyUsage.class) { 247 if (seed == 0) return null; 248 CurrencyUsage[] values = CurrencyUsage.values(); 249 return values[seed % values.length]; 250 251 } else if (type == GroupingMode.class) { 252 if (seed == 0) return null; 253 GroupingMode[] values = GroupingMode.values(); 254 return values[seed % values.length]; 255 256 } else if (type == FormatWidth.class) { 257 if (seed == 0) return null; 258 FormatWidth[] values = FormatWidth.values(); 259 return values[seed % values.length]; 260 261 } else if (type == Map.class) { 262 // Map<String,Map<String,String>> for compactCustomData property 263 if (seed == 0) return null; 264 Map<String, Map<String, String>> outer = new HashMap<String, Map<String, String>>(); 265 Map<String, String> inner = new HashMap<String, String>(); 266 inner.put("one", "0 thousand"); 267 StringBuilder magnitudeKey = new StringBuilder(); 268 magnitudeKey.append("1000"); 269 for (int i = 0; i < seed % 9; i++) { 270 magnitudeKey.append("0"); 271 } 272 outer.put(magnitudeKey.toString(), inner); 273 return outer; 274 275 } else if (type == MathContext.class) { 276 if (seed == 0) return null; 277 RoundingMode[] modes = RoundingMode.values(); 278 return new MathContext(seed, modes[seed % modes.length]); 279 280 } else if (type == MeasureUnit.class) { 281 if (seed == 0) return null; 282 Object[] units = MeasureUnit.getAvailable().toArray(); 283 return units[seed % units.length]; 284 285 } else if (type == PadPosition.class) { 286 if (seed == 0) return null; 287 PadPosition[] values = PadPosition.values(); 288 return values[seed % values.length]; 289 290 } else if (type == ParseMode.class) { 291 if (seed == 0) return null; 292 ParseMode[] values = ParseMode.values(); 293 return values[seed % values.length]; 294 295 } else if (type == PluralRules.class) { 296 if (seed == 0) return null; 297 ULocale[] locales = PluralRules.getAvailableULocales(); 298 return PluralRules.forLocale(locales[seed % locales.length]); 299 300 } else if (type == RoundingMode.class) { 301 if (seed == 0) return null; 302 RoundingMode[] values = RoundingMode.values(); 303 return values[seed % values.length]; 304 305 } else { 306 fail("Don't know how to handle type " + type + ". Please add it to getSampleValueForType()."); 307 return null; 308 } 309 } 310 311 @Test 312 public void TestBasicSerializationRoundTrip() throws IOException, ClassNotFoundException { 313 DecimalFormatProperties props0 = new DecimalFormatProperties(); 314 315 // Write values to some of the fields 316 PatternStringParser.parseToExistingProperties("A-**####,#00.00#b¤", props0); 317 318 // Write to byte stream 319 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 320 ObjectOutputStream oos = new ObjectOutputStream(baos); 321 oos.writeObject(props0); 322 oos.flush(); 323 baos.close(); 324 byte[] bytes = baos.toByteArray(); 325 326 // Read from byte stream 327 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); 328 Object obj = ois.readObject(); 329 ois.close(); 330 DecimalFormatProperties props1 = (DecimalFormatProperties) obj; 331 332 // Test equality 333 assertEquals("Did not round-trip through serialization", props0, props1); 334 } 335 336 /** Handler for serialization compatibility test suite. */ 337 public static class PropertiesHandler implements SerializableTestUtility.Handler { 338 339 @Override 340 public Object[] getTestObjects() { 341 return new Object[] { 342 new DecimalFormatProperties(), 343 PatternStringParser.parseToProperties("x#,##0.00%"), 344 new DecimalFormatProperties().setCompactStyle(CompactStyle.LONG).setMinimumExponentDigits(2) 345 }; 346 } 347 348 @Override 349 public boolean hasSameBehavior(Object a, Object b) { 350 return a.equals(b); 351 } 352 } 353 354 /** Handler for the ICU 59 class named "Properties" before it was renamed to "DecimalFormatProperties". */ 355 public static class ICU59PropertiesHandler implements SerializableTestUtility.Handler { 356 357 @Override 358 public Object[] getTestObjects() { 359 return new Object[] { 360 new android.icu.impl.number.Properties() 361 }; 362 } 363 364 @Override 365 public boolean hasSameBehavior(Object a, Object b) { 366 return true; 367 } 368 } 369} 370