LEFT | RIGHT |
1 // © 2017 and later: Unicode, Inc. and others. | 1 // © 2017 and later: Unicode, Inc. and others. |
2 // License & terms of use: http://www.unicode.org/copyright.html#License | 2 // License & terms of use: http://www.unicode.org/copyright.html#License |
3 package com.ibm.icu.dev.test.numbers; | 3 package com.ibm.icu.dev.test.numbers; |
4 | 4 |
5 import static org.junit.Assert.assertEquals; | 5 import static org.junit.Assert.assertEquals; |
6 import static org.junit.Assert.assertNotEquals; | 6 import static org.junit.Assert.assertNotEquals; |
7 import static org.junit.Assert.assertTrue; | 7 import static org.junit.Assert.assertTrue; |
8 import static org.junit.Assert.fail; | 8 import static org.junit.Assert.fail; |
9 | 9 |
| 10 import java.io.ByteArrayInputStream; |
| 11 import java.io.ByteArrayOutputStream; |
| 12 import java.io.IOException; |
| 13 import java.io.ObjectInputStream; |
| 14 import java.io.ObjectOutputStream; |
10 import java.lang.reflect.Field; | 15 import java.lang.reflect.Field; |
11 import java.lang.reflect.InvocationTargetException; | 16 import java.lang.reflect.InvocationTargetException; |
12 import java.lang.reflect.Method; | 17 import java.lang.reflect.Method; |
13 import java.lang.reflect.Modifier; | 18 import java.lang.reflect.Modifier; |
| 19 import java.lang.reflect.Parameter; |
14 import java.math.BigDecimal; | 20 import java.math.BigDecimal; |
15 import java.math.BigInteger; | 21 import java.math.BigInteger; |
| 22 import java.math.MathContext; |
16 import java.math.RoundingMode; | 23 import java.math.RoundingMode; |
17 import java.util.HashSet; | 24 import java.util.HashSet; |
18 import java.util.Set; | 25 import java.util.Set; |
19 | 26 |
20 import org.junit.Test; | 27 import org.junit.Test; |
21 | 28 |
| 29 import com.ibm.icu.dev.test.serializable.SerializableTestUtility; |
22 import com.ibm.icu.impl.number.Parse.ParseMode; | 30 import com.ibm.icu.impl.number.Parse.ParseMode; |
| 31 import com.ibm.icu.impl.number.PatternString; |
23 import com.ibm.icu.impl.number.Properties; | 32 import com.ibm.icu.impl.number.Properties; |
24 import com.ibm.icu.impl.number.formatters.CurrencyFormat.CurrencyStyle; | 33 import com.ibm.icu.impl.number.formatters.CurrencyFormat.CurrencyStyle; |
25 import com.ibm.icu.impl.number.formatters.PaddingFormat.PaddingLocation; | 34 import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; |
| 35 import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder.SignificantDigi
tsMode; |
26 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; | 36 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; |
27 import com.ibm.icu.text.CurrencyPluralInfo; | 37 import com.ibm.icu.text.CurrencyPluralInfo; |
28 import com.ibm.icu.text.MeasureFormat.FormatWidth; | 38 import com.ibm.icu.text.MeasureFormat.FormatWidth; |
29 import com.ibm.icu.util.Currency; | 39 import com.ibm.icu.util.Currency; |
30 import com.ibm.icu.util.Currency.CurrencyUsage; | 40 import com.ibm.icu.util.Currency.CurrencyUsage; |
31 import com.ibm.icu.util.MeasureUnit; | 41 import com.ibm.icu.util.MeasureUnit; |
32 import com.ibm.icu.util.ULocale; | 42 import com.ibm.icu.util.ULocale; |
33 | 43 |
34 public class PropertiesTest { | 44 public class PropertiesTest { |
35 | 45 |
36 @Test | 46 @Test |
37 public void testBasicEquals() { | 47 public void testBasicEquals() { |
38 Properties p1 = new Properties(); | 48 Properties p1 = new Properties(); |
39 Properties p2 = new Properties(); | 49 Properties p2 = new Properties(); |
40 assertEquals(p1, p2); | 50 assertEquals(p1, p2); |
41 | 51 |
42 p1.setPositivePrefix("abc"); | 52 p1.setPositivePrefix("abc"); |
43 assertNotEquals(p1, p2); | 53 assertNotEquals(p1, p2); |
44 p2.setPositivePrefix("xyz"); | 54 p2.setPositivePrefix("xyz"); |
45 assertNotEquals(p1, p2); | 55 assertNotEquals(p1, p2); |
46 p1.setPositivePrefix("xyz"); | 56 p1.setPositivePrefix("xyz"); |
47 assertEquals(p1, p2); | 57 assertEquals(p1, p2); |
48 } | 58 } |
49 | 59 |
50 @Test | 60 @Test |
51 public void testFieldCoverage() { | 61 public void testFieldCoverage() { |
| 62 Properties p0 = new Properties(); |
52 Properties p1 = new Properties(); | 63 Properties p1 = new Properties(); |
53 Properties p2 = new Properties(); | 64 Properties p2 = new Properties(); |
54 Properties p3 = new Properties(); | 65 Properties p3 = new Properties(); |
55 Properties p4 = new Properties(); | 66 Properties p4 = new Properties(); |
56 | 67 |
57 Set<Integer> hashCodes = new HashSet<Integer>(); | 68 Set<Integer> hashCodes = new HashSet<Integer>(); |
58 Field[] fields = Properties.class.getDeclaredFields(); | 69 Field[] fields = Properties.class.getDeclaredFields(); |
59 for (Field field : fields) { | 70 for (Field field : fields) { |
60 if (Modifier.isStatic(field.getModifiers())) { | 71 if (Modifier.isStatic(field.getModifiers())) { |
61 continue; | 72 continue; |
(...skipping 23 matching lines...) Expand all Loading... |
85 Properties.class, | 96 Properties.class, |
86 setter.getReturnType()); | 97 setter.getReturnType()); |
87 } catch (NoSuchMethodException e) { | 98 } catch (NoSuchMethodException e) { |
88 fail("Could not find method " + setterName + " for field " + field); | 99 fail("Could not find method " + setterName + " for field " + field); |
89 continue; | 100 continue; |
90 } catch (SecurityException e) { | 101 } catch (SecurityException e) { |
91 fail("Could not access method " + setterName + " for field " + field); | 102 fail("Could not access method " + setterName + " for field " + field); |
92 continue; | 103 continue; |
93 } | 104 } |
94 | 105 |
| 106 // Check for parameter name equality. |
| 107 // The parameter name is not always available, depending on compiler setti
ngs. |
| 108 Parameter param = setter.getParameters()[0]; |
| 109 if (!param.getName().subSequence(0, 3).equals("arg")) { |
| 110 assertEquals("Parameter name should equal field name", field.getName(),
param.getName()); |
| 111 } |
| 112 |
95 try { | 113 try { |
| 114 // Check for default value (should be null for objects) |
| 115 if (field.getType() != Integer.TYPE && field.getType() != Boolean.TYPE)
{ |
| 116 Object default0 = getter.invoke(p0); |
| 117 assertEquals("Field " + field + " has non-null default value:", null,
default0); |
| 118 } |
| 119 |
96 // Check for getter, equals, and hash code behavior | 120 // Check for getter, equals, and hash code behavior |
97 Object val0 = getSampleValueForType(field.getType(), 0); | 121 Object val0 = getSampleValueForType(field.getType(), 0); |
98 Object val1 = getSampleValueForType(field.getType(), 1); | 122 Object val1 = getSampleValueForType(field.getType(), 1); |
| 123 Object val2 = getSampleValueForType(field.getType(), 2); |
| 124 assertNotEquals(val0, val1); |
99 setter.invoke(p1, val0); | 125 setter.invoke(p1, val0); |
100 setter.invoke(p2, val0); | 126 setter.invoke(p2, val0); |
101 assertEquals(p1, p2); | 127 assertEquals(p1, p2); |
102 assertEquals(p1.hashCode(), p2.hashCode()); | 128 assertEquals(p1.hashCode(), p2.hashCode()); |
103 assertEquals(getter.invoke(p1), getter.invoke(p2)); | 129 assertEquals(getter.invoke(p1), getter.invoke(p2)); |
104 assertEquals(getter.invoke(p1), val0); | 130 assertEquals(getter.invoke(p1), val0); |
105 assertNotEquals(getter.invoke(p1), val1); | 131 assertNotEquals(getter.invoke(p1), val1); |
106 hashCodes.add(p1.hashCode()); | 132 hashCodes.add(p1.hashCode()); |
107 setter.invoke(p1, val1); | 133 setter.invoke(p1, val1); |
108 assertNotEquals("Field " + field + " is missing from equals()", p1, p2); | 134 assertNotEquals("Field " + field + " is missing from equals()", p1, p2); |
109 assertNotEquals(getter.invoke(p1), getter.invoke(p2)); | 135 assertNotEquals(getter.invoke(p1), getter.invoke(p2)); |
110 assertNotEquals(getter.invoke(p1), val0); | 136 assertNotEquals(getter.invoke(p1), val0); |
111 assertEquals(getter.invoke(p1), val1); | 137 assertEquals(getter.invoke(p1), val1); |
| 138 setter.invoke(p1, val0); |
| 139 assertEquals("Field " + field + " setter might have side effects", p1, p
2); |
| 140 assertEquals(p1.hashCode(), p2.hashCode()); |
| 141 assertEquals(getter.invoke(p1), getter.invoke(p2)); |
| 142 setter.invoke(p1, val1); |
112 setter.invoke(p2, val1); | 143 setter.invoke(p2, val1); |
113 assertEquals(p1, p2); | 144 assertEquals(p1, p2); |
114 assertEquals(p1.hashCode(), p2.hashCode()); | 145 assertEquals(p1.hashCode(), p2.hashCode()); |
115 assertEquals(getter.invoke(p1), getter.invoke(p2)); | 146 assertEquals(getter.invoke(p1), getter.invoke(p2)); |
| 147 setter.invoke(p1, val2); |
| 148 setter.invoke(p1, val1); |
| 149 assertEquals("Field " + field + " setter might have side effects", p1, p
2); |
| 150 assertEquals(p1.hashCode(), p2.hashCode()); |
| 151 assertEquals(getter.invoke(p1), getter.invoke(p2)); |
116 hashCodes.add(p1.hashCode()); | 152 hashCodes.add(p1.hashCode()); |
| 153 |
| 154 // Check for clone behavior |
| 155 Properties copy = p1.clone(); |
| 156 assertEquals("Field " + field + " did not get copied in clone", p1, copy
); |
| 157 assertEquals(p1.hashCode(), copy.hashCode()); |
| 158 assertEquals(getter.invoke(p1), getter.invoke(copy)); |
| 159 |
| 160 // Check for copyFrom behavior |
| 161 setter.invoke(p1, val0); |
| 162 assertNotEquals(p1, p2); |
| 163 assertNotEquals(getter.invoke(p1), getter.invoke(p2)); |
| 164 p2.copyFrom(p1); |
| 165 assertEquals("Field " + field + " is missing from copyFrom()", p1, p2); |
| 166 assertEquals(p1.hashCode(), p2.hashCode()); |
| 167 assertEquals(getter.invoke(p1), getter.invoke(p2)); |
117 | 168 |
118 // Load values into p3 and p4 for clear() behavior test | 169 // Load values into p3 and p4 for clear() behavior test |
119 setter.invoke(p3, getSampleValueForType(field.getType(), 3)); | 170 setter.invoke(p3, getSampleValueForType(field.getType(), 3)); |
120 hashCodes.add(p3.hashCode()); | 171 hashCodes.add(p3.hashCode()); |
121 setter.invoke(p4, getSampleValueForType(field.getType(), 4)); | 172 setter.invoke(p4, getSampleValueForType(field.getType(), 4)); |
122 hashCodes.add(p4.hashCode()); | 173 hashCodes.add(p4.hashCode()); |
123 } catch (IllegalAccessException e) { | 174 } catch (IllegalAccessException e) { |
124 fail("Could not access method for field " + field); | 175 fail("Could not access method for field " + field); |
125 } catch (IllegalArgumentException e) { | 176 } catch (IllegalArgumentException e) { |
126 fail("Could call method for field " + field); | 177 fail("Could call method for field " + field); |
(...skipping 10 matching lines...) Expand all Loading... |
137 | 188 |
138 // A good hashCode() implementation should produce very few collisions. We
added at most | 189 // A good hashCode() implementation should produce very few collisions. We
added at most |
139 // 4*fields.length codes to the set. We'll say the implementation is good i
f we had at least | 190 // 4*fields.length codes to the set. We'll say the implementation is good i
f we had at least |
140 // fields.length unique values. | 191 // fields.length unique values. |
141 // TODO: Should the requirement be stronger than this? | 192 // TODO: Should the requirement be stronger than this? |
142 assertTrue( | 193 assertTrue( |
143 "Too many hash code collisions: " + hashCodes.size() + " out of " + (fie
lds.length * 4), | 194 "Too many hash code collisions: " + hashCodes.size() + " out of " + (fie
lds.length * 4), |
144 hashCodes.size() >= fields.length); | 195 hashCodes.size() >= fields.length); |
145 } | 196 } |
146 | 197 |
| 198 /** |
| 199 * Creates a valid sample instance of the given type. Used to simulate getters
and setters. |
| 200 * |
| 201 * @param type The type to generate. |
| 202 * @param seed An integer seed, guaranteed to be positive. The same seed shoul
d generate two |
| 203 * instances that are equal. A different seed should in general generate t
wo instances that |
| 204 * are not equal; this might not always be possible, such as with booleans
or enums where |
| 205 * there are limited possible values. |
| 206 * @return An instance of the specified type. |
| 207 */ |
147 Object getSampleValueForType(Class<?> type, int seed) { | 208 Object getSampleValueForType(Class<?> type, int seed) { |
148 if (type == Integer.TYPE) { | 209 if (type == Integer.TYPE) { |
149 return seed * 1000001; | 210 return seed * 1000001; |
150 | 211 |
151 } else if (type == Boolean.TYPE) { | 212 } else if (type == Boolean.TYPE) { |
152 return (seed % 2) == 0; | 213 return (seed % 2) == 0; |
153 | 214 |
154 } else if (type == BigDecimal.class) { | 215 } else if (type == BigDecimal.class) { |
155 if (seed == 0) return null; | 216 if (seed == 0) return null; |
156 return new BigDecimal(seed * 1000002); | 217 return new BigDecimal(seed * 1000002); |
157 | 218 |
158 } else if (type == CharSequence.class) { | 219 } else if (type == String.class) { |
159 if (seed == 0) return null; | 220 if (seed == 0) return null; |
160 return BigInteger.valueOf(seed * 1000003).toString(32); | 221 return BigInteger.valueOf(seed * 1000003).toString(32); |
161 | 222 |
162 } else if (type == CompactStyle.class) { | 223 } else if (type == CompactStyle.class) { |
163 if (seed == 0) return null; | 224 if (seed == 0) return null; |
164 CompactStyle[] values = CompactStyle.values(); | 225 CompactStyle[] values = CompactStyle.values(); |
165 return values[seed % values.length]; | 226 return values[seed % values.length]; |
166 | 227 |
167 } else if (type == Currency.class) { | 228 } else if (type == Currency.class) { |
168 if (seed == 0) return null; | 229 if (seed == 0) return null; |
(...skipping 13 matching lines...) Expand all Loading... |
182 } else if (type == CurrencyUsage.class) { | 243 } else if (type == CurrencyUsage.class) { |
183 if (seed == 0) return null; | 244 if (seed == 0) return null; |
184 CurrencyUsage[] values = CurrencyUsage.values(); | 245 CurrencyUsage[] values = CurrencyUsage.values(); |
185 return values[seed % values.length]; | 246 return values[seed % values.length]; |
186 | 247 |
187 } else if (type == FormatWidth.class) { | 248 } else if (type == FormatWidth.class) { |
188 if (seed == 0) return null; | 249 if (seed == 0) return null; |
189 FormatWidth[] values = FormatWidth.values(); | 250 FormatWidth[] values = FormatWidth.values(); |
190 return values[seed % values.length]; | 251 return values[seed % values.length]; |
191 | 252 |
| 253 } else if (type == MathContext.class) { |
| 254 if (seed == 0) return null; |
| 255 RoundingMode[] modes = RoundingMode.values(); |
| 256 return new MathContext(seed, modes[seed % modes.length]); |
| 257 |
192 } else if (type == MeasureUnit.class) { | 258 } else if (type == MeasureUnit.class) { |
193 if (seed == 0) return null; | 259 if (seed == 0) return null; |
194 Object[] units = MeasureUnit.getAvailable().toArray(); | 260 Object[] units = MeasureUnit.getAvailable().toArray(); |
195 return units[seed % units.length]; | 261 return units[seed % units.length]; |
196 | 262 |
197 } else if (type == PaddingLocation.class) { | 263 } else if (type == PadPosition.class) { |
198 if (seed == 0) return null; | 264 if (seed == 0) return null; |
199 PaddingLocation[] values = PaddingLocation.values(); | 265 PadPosition[] values = PadPosition.values(); |
200 return values[seed % values.length]; | 266 return values[seed % values.length]; |
201 | 267 |
202 } else if (type == ParseMode.class) { | 268 } else if (type == ParseMode.class) { |
203 if (seed == 0) return null; | 269 if (seed == 0) return null; |
204 ParseMode[] values = ParseMode.values(); | 270 ParseMode[] values = ParseMode.values(); |
205 return values[seed % values.length]; | 271 return values[seed % values.length]; |
206 | 272 |
207 } else if (type == RoundingMode.class) { | 273 } else if (type == RoundingMode.class) { |
208 if (seed == 0) return null; | 274 if (seed == 0) return null; |
209 RoundingMode[] values = RoundingMode.values(); | 275 RoundingMode[] values = RoundingMode.values(); |
210 return values[seed % values.length]; | 276 return values[seed % values.length]; |
211 | 277 |
| 278 } else if (type == SignificantDigitsMode.class) { |
| 279 if (seed == 0) return null; |
| 280 SignificantDigitsMode[] values = SignificantDigitsMode.values(); |
| 281 return values[seed % values.length]; |
| 282 |
212 } else { | 283 } else { |
213 fail("Don't know how to handle type " + type); | 284 fail("Don't know how to handle type " + type + ". Please add it to getSamp
leValueForType()."); |
214 return null; | 285 return null; |
215 } | 286 } |
216 } | 287 } |
| 288 |
| 289 @Test |
| 290 public void TestBasicSerializationRoundTrip() throws IOException, ClassNotFoun
dException { |
| 291 Properties props0 = new Properties(); |
| 292 |
| 293 // Write values to some of the fields |
| 294 PatternString.parseToExistingProperties("A-**####,#00.00#b¤", props0); |
| 295 |
| 296 // Write to byte stream |
| 297 ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| 298 ObjectOutputStream oos = new ObjectOutputStream(baos); |
| 299 oos.writeObject(props0); |
| 300 oos.flush(); |
| 301 baos.close(); |
| 302 byte[] bytes = baos.toByteArray(); |
| 303 |
| 304 // Read from byte stream |
| 305 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes
)); |
| 306 Object obj = ois.readObject(); |
| 307 ois.close(); |
| 308 Properties props1 = (Properties) obj; |
| 309 |
| 310 // Test equality |
| 311 assertEquals("Did not round-trip through serialization", props0, props1); |
| 312 } |
| 313 |
| 314 /** Handler for serialization compatibility test suite. */ |
| 315 public static class PropertiesHandler implements SerializableTestUtility.Handl
er { |
| 316 |
| 317 @Override |
| 318 public Object[] getTestObjects() { |
| 319 return new Object[] { |
| 320 new Properties(), |
| 321 PatternString.parseToProperties("x#,##0.00%"), |
| 322 new Properties().setCompactStyle(CompactStyle.LONG).setMinimumExponentDi
gits(2) |
| 323 }; |
| 324 } |
| 325 |
| 326 @Override |
| 327 public boolean hasSameBehavior(Object a, Object b) { |
| 328 return a.equals(b); |
| 329 } |
| 330 } |
217 } | 331 } |
LEFT | RIGHT |