1 package org.pojomatic.internal; 2 3 import static org.testng.Assert.*; 4 5 import java.lang.reflect.Field; 6 7 import org.pojomatic.NoPojomaticPropertiesException; 8 import org.pojomatic.Pojomator; 9 import org.pojomatic.annotations.AutoDetectPolicy; 10 import org.pojomatic.annotations.AutoProperty; 11 import org.pojomatic.annotations.PojoFormat; 12 import org.pojomatic.annotations.PojomaticPolicy; 13 import org.pojomatic.annotations.Property; 14 import org.pojomatic.annotations.PropertyFormat; 15 import org.pojomatic.diff.Differences; 16 import org.pojomatic.diff.PropertyDifferences; 17 import org.pojomatic.diff.ValueDifference; 18 import org.pojomatic.formatter.DefaultEnhancedPojoFormatter; 19 import org.pojomatic.formatter.DefaultEnhancedPropertyFormatter; 20 import org.testng.annotations.Test; 21 22 import com.google.common.collect.Sets; 23 24 public class PojomatorImplTest { 25 private final static int HASH_CODE_MULTIPLIER = 31; 26 private final static int HASH_CODE_SEED = 1; 27 28 private static Pojomator<ObjectProperty> OBJECT_PROPERTY_POJOMATOR = 29 makePojomator(ObjectProperty.class); 30 31 private static Pojomator<ObjectPairProperty> OBJECT_PAIR_PROPERTY_POJOMATOR = 32 makePojomator(ObjectPairProperty.class); 33 34 private static final Pojomator<AccessCheckedProperties> ACCESS_CHECKED_PROPERTIES_POJOMATOR = 35 makePojomator(AccessCheckedProperties.class); 36 37 @Test(expectedExceptions=NullPointerException.class) public void testNullHashCode() { 38 OBJECT_PROPERTY_POJOMATOR.doHashCode(null); 39 } 40 41 @Test(expectedExceptions=NullPointerException.class) public void testToStringOnNull() { 42 OBJECT_PROPERTY_POJOMATOR.doToString(null); 43 } 44 45 @Test(expectedExceptions=NullPointerException.class) public void testNullInstanceEquals() { 46 OBJECT_PROPERTY_POJOMATOR.doEquals(null, new ObjectProperty("e")); 47 } 48 49 @Test public void testNullEquals() { 50 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(new ObjectProperty(null), null)); 51 } 52 53 @Test public void testReflexiveEquals() { 54 ExceptionThrowingProperty instance = new ExceptionThrowingProperty(); 55 assertTrue(makePojomator(ExceptionThrowingProperty.class).doEquals(instance, instance)); 56 } 57 58 @Test public void testCastCheckFailureForEquals() { 59 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(new ObjectProperty("test"), "differentClass")); 60 } 61 62 @Test public void testArrayVsNonArrayEquals() { 63 ObjectProperty arrayProperty = new ObjectProperty(new String[] {""}); 64 ObjectProperty stringProperty = new ObjectProperty(""); 65 ObjectProperty nullProperty = new ObjectProperty(null); 66 67 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(arrayProperty, stringProperty)); 68 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(stringProperty, arrayProperty)); 69 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(arrayProperty, nullProperty)); 70 assertFalse(OBJECT_PROPERTY_POJOMATOR.doEquals(nullProperty, arrayProperty)); 71 } 72 73 @Test public void testShortCircuitEquals() { 74 AccessCheckedProperties left = new AccessCheckedProperties(1,1); 75 AccessCheckedProperties right = new AccessCheckedProperties(2,2); 76 assertFalse(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doEquals(left, right)); 77 assertFalse(left.getBCalled); 78 assertFalse(right.getBCalled); 79 80 assertTrue(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doEquals(left, left)); 81 assertFalse(left.getBCalled); 82 83 assertFalse(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doEquals(left, null)); 84 assertFalse(left.getBCalled); 85 86 assertFalse(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doEquals(left, "hello")); 87 assertFalse(left.getBCalled); 88 89 assertTrue(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doEquals(left, new AccessCheckedProperties(1,1))); 90 assertTrue(left.getBCalled); 91 } 92 93 @Test public void testPropertyPairHashCode() { 94 assertEquals(OBJECT_PAIR_PROPERTY_POJOMATOR.doHashCode(new ObjectPairProperty("foo", "bar")), HASH_CODE_MULTIPLIER * (HASH_CODE_MULTIPLIER * HASH_CODE_SEED + "foo".hashCode()) 95 + "bar".hashCode()); 96 } 97 98 @Test public void testToStringNames() { 99 assertEquals(ACCESS_CHECKED_PROPERTIES_POJOMATOR.doToString(new AccessCheckedProperties(1, 2)), "AccessCheckedProperties{a: {1}, b: {2}}"); 100 } 101 102 @Test public void testCustomFormatters() { 103 assertEquals(makePojomator(FormattedObject.class).doToString(new FormattedObject("x")), "PREFIXFormattedObject{s: {BEFOREx}}"); 104 } 105 106 @Test(expectedExceptions=NullPointerException.class) 107 public void testDiffNullInstance() { 108 ObjectPairProperty other = new ObjectPairProperty("this", "that"); 109 OBJECT_PAIR_PROPERTY_POJOMATOR.doDiff(null, other); 110 } 111 112 @Test(expectedExceptions=NullPointerException.class) 113 public void testDiffNullOther() { 114 ObjectPairProperty instance = new ObjectPairProperty("this", "that"); 115 OBJECT_PAIR_PROPERTY_POJOMATOR.doDiff(instance, null); 116 } 117 118 @Test(expectedExceptions=NullPointerException.class) 119 public void testDiffNulls() { 120 OBJECT_PAIR_PROPERTY_POJOMATOR.doDiff(null, null); 121 } 122 123 @Test public void testDiffDifferentObjectsWithSinglePropertyDifferent() { 124 final Differences diffs = OBJECT_PAIR_PROPERTY_POJOMATOR.doDiff( 125 new ObjectPairProperty("this", "that"), new ObjectPairProperty("THIS", "that")); 126 assertTrue(diffs instanceof PropertyDifferences); 127 assertEquals(Sets.newHashSet(diffs.differences()), Sets.newHashSet(new ValueDifference("s", "this", "THIS"))); 128 } 129 130 @Test public void testDiffDifferentObjectsWithMultiplePropertiesDifferent() { 131 final Differences diffs = OBJECT_PAIR_PROPERTY_POJOMATOR.doDiff( 132 new ObjectPairProperty("this", "that"), new ObjectPairProperty("THIS", "THAT")); 133 assertEquals(diffs.getClass(), PropertyDifferences.class); 134 assertEquals(Sets.newHashSet(diffs.differences()), Sets.newHashSet(new ValueDifference("s", "this", "THIS"), new ValueDifference("t", "that", "THAT"))); 135 } 136 137 @Test public void testDiffAgainstWrongType() { 138 Pojomator<?> pojomator = OBJECT_PAIR_PROPERTY_POJOMATOR; 139 @SuppressWarnings("unchecked") Pojomator<Object> misCastPojomator = (Pojomator<Object>) pojomator; 140 try { 141 misCastPojomator.doDiff(new ObjectPairProperty(1,2), "wrong"); 142 fail("exception expected"); 143 } 144 catch (IllegalArgumentException e) { 145 assertEquals(e.getMessage(), "other has type java.lang.String which is not compatible for equality with org.pojomatic.internal.PojomatorImplTest$ObjectPairProperty"); 146 } 147 } 148 149 @Test public void testDiffWrongType() { 150 Pojomator<?> pojomator = OBJECT_PAIR_PROPERTY_POJOMATOR; 151 @SuppressWarnings("unchecked") Pojomator<Object> misCastPojomator = (Pojomator<Object>) pojomator; 152 try { 153 misCastPojomator.doDiff("wrong", new ObjectPairProperty(1,2)); 154 fail("exception expected"); 155 } 156 catch (IllegalArgumentException e) { 157 assertEquals(e.getMessage(), "instance has type java.lang.String which is not compatible for equality with org.pojomatic.internal.PojomatorImplTest$ObjectPairProperty"); 158 } 159 } 160 161 @Test(expectedExceptions= NoPojomaticPropertiesException.class) 162 public void testNonPojomatedClass() { 163 makePojomator(String.class); 164 } 165 166 @Test public void testInterface() { 167 Pojomator<Interface> pojomator = makePojomator(Interface.class); 168 class Impl1 implements Interface { 169 @Override 170 public int getInt() { return 2; } 171 @Override 172 public String getString() { return "hello"; } 173 } 174 175 class Impl2 implements Interface { 176 private final String string; 177 178 Impl2(String string) { this.string = string; } 179 @Override 180 public int getInt() { return 2; } 181 @Override 182 public String getString() { return string; } 183 } 184 185 assertEquals(pojomator.doToString(new Impl1()), "Interface{int: {2}, string: {hello}}"); 186 assertEquals(pojomator.doHashCode(new Impl1()), (HASH_CODE_MULTIPLIER + 2) * HASH_CODE_MULTIPLIER + "hello".hashCode()); 187 assertTrue(pojomator.doEquals(new Impl1(), new Impl2("hello"))); 188 assertFalse(pojomator.doEquals(new Impl1(), new Impl2("goodbye"))); 189 assertFalse(pojomator.doEquals(new Impl1(), "not even in the right hierarchy")); 190 } 191 192 @Test public void testIsCompatibleForEquals() { 193 assertTrue(OBJECT_PROPERTY_POJOMATOR.isCompatibleForEquality(ObjectProperty.class)); 194 assertFalse(OBJECT_PROPERTY_POJOMATOR.isCompatibleForEquality(ObjectPairProperty.class)); 195 assertTrue(makePojomator(Interface.class).isCompatibleForEquality(new Interface() { 196 @Override 197 public int getInt() { return 0; } 198 @Override 199 public String getString() { return null; } 200 }.getClass())); 201 } 202 203 @Test public void testEqualsScopedProperties() throws Exception { 204 assertFalse(PolicyProperties.policyUsedInEquals(PojomaticPolicy.NONE)); 205 assertFalse(PolicyProperties.policyUsedInEquals(PojomaticPolicy.TO_STRING)); 206 207 assertTrue(PolicyProperties.policyUsedInEquals(PojomaticPolicy.EQUALS)); 208 assertTrue(PolicyProperties.policyUsedInEquals(PojomaticPolicy.EQUALS_TO_STRING)); 209 assertTrue(PolicyProperties.policyUsedInEquals(PojomaticPolicy.HASHCODE_EQUALS)); 210 assertTrue(PolicyProperties.policyUsedInEquals(PojomaticPolicy.ALL)); 211 } 212 213 @Test public void testHashCodeScopedProperties() throws Exception { 214 assertFalse(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.NONE)); 215 assertFalse(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.TO_STRING)); 216 assertFalse(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.EQUALS)); 217 assertFalse(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.EQUALS_TO_STRING)); 218 219 assertTrue(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.HASHCODE_EQUALS)); 220 assertTrue(PolicyProperties.policyUsedInHashCode(PojomaticPolicy.ALL)); 221 } 222 223 @Test public void testToStringScopedProperties() throws Exception { 224 assertFalse(PolicyProperties.policyUsedInToString(PojomaticPolicy.NONE)); 225 assertFalse(PolicyProperties.policyUsedInToString(PojomaticPolicy.EQUALS)); 226 assertFalse(PolicyProperties.policyUsedInToString(PojomaticPolicy.HASHCODE_EQUALS)); 227 228 assertTrue(PolicyProperties.policyUsedInToString(PojomaticPolicy.TO_STRING)); 229 assertTrue(PolicyProperties.policyUsedInToString(PojomaticPolicy.EQUALS_TO_STRING)); 230 assertTrue(PolicyProperties.policyUsedInToString(PojomaticPolicy.ALL)); 231 } 232 233 @PojoFormat(SimplePojoFormatter.class) 234 private static class FormattedObject { 235 public FormattedObject(Object s) { 236 this.s = s; 237 } 238 @Property 239 @PropertyFormat(SimplePropertyFormatter.class) 240 public Object s; 241 } 242 243 public static class SimplePojoFormatter extends DefaultEnhancedPojoFormatter { 244 @Override 245 public void appendToStringPrefix(StringBuilder builder, Class<?> pojoClass) { 246 builder.append("PREFIX"); 247 super.appendToStringPrefix(builder, pojoClass); 248 } 249 } 250 251 public static class SimplePropertyFormatter extends DefaultEnhancedPropertyFormatter { 252 @Override 253 public void appendFormatted(StringBuilder builder, Object value) { 254 builder.append("BEFORE"); 255 super.appendFormatted(builder, value); 256 } 257 } 258 259 private static class ObjectProperty { 260 public ObjectProperty(Object s) { 261 this.s = s; 262 } 263 @Property public Object s; 264 } 265 266 private static class ObjectPairProperty { 267 public ObjectPairProperty(Object s, Object t) { 268 this.s = s; 269 this.t = t; 270 } 271 @Property public Object s; 272 @Property public Object t; 273 } 274 275 private static class ExceptionThrowingProperty { 276 @Property public int bomb() { 277 throw new RuntimeException(); 278 } 279 } 280 281 private static class AccessCheckedProperties { 282 public AccessCheckedProperties(int a, int b) { 283 this.a = a; 284 this.b = b; 285 } 286 287 @Property public int getA() { 288 return a; 289 } 290 291 @Property public int getB() { 292 getBCalled = true; 293 return b; 294 } 295 296 private int a, b; 297 private boolean getBCalled; 298 } 299 300 private static class PolicyProperties { 301 @Property(policy = PojomaticPolicy.ALL) 302 public int all; 303 304 @Property(policy = PojomaticPolicy.EQUALS) 305 public int equals; 306 307 @Property(policy = PojomaticPolicy.EQUALS_TO_STRING) 308 public int equalsToString; 309 310 @Property(policy = PojomaticPolicy.HASHCODE_EQUALS) 311 public int hashCodeEquals; 312 313 @Property(policy = PojomaticPolicy.TO_STRING) 314 public int toString; 315 316 @Property(policy = PojomaticPolicy.NONE) 317 public int none; 318 319 private static PolicyProperties withPolicyProperty(PojomaticPolicy policy, int value) throws Exception { 320 PolicyProperties instance = new PolicyProperties(); 321 for (Field field : instance.getClass().getDeclaredFields()) { 322 if (field.getAnnotation(Property.class).policy() == policy) { 323 field.setInt(instance, value); 324 return instance; 325 } 326 } 327 throw new IllegalArgumentException("No field with policy " + policy.name()); 328 } 329 330 public static boolean policyUsedInEquals(PojomaticPolicy policy) throws Exception { 331 return ! POJOMATOR.doEquals( 332 PolicyProperties.withPolicyProperty(policy, 1), 333 PolicyProperties.withPolicyProperty(policy, 2)); 334 } 335 336 public static boolean policyUsedInHashCode(PojomaticPolicy policy) throws Exception { 337 return POJOMATOR.doHashCode(PolicyProperties.withPolicyProperty(policy, 1)) 338 != POJOMATOR.doHashCode(PolicyProperties.withPolicyProperty(policy, 2)); 339 } 340 341 public static boolean policyUsedInToString(PojomaticPolicy policy) throws Exception { 342 return ! POJOMATOR.doToString(PolicyProperties.withPolicyProperty(policy, 1)).equals( 343 POJOMATOR.doToString(PolicyProperties.withPolicyProperty(policy, 2))); 344 } 345 346 private final static Pojomator<PolicyProperties> POJOMATOR = makePojomator(PolicyProperties.class); 347 } 348 349 @AutoProperty 350 private static class PrivateClass { 351 @SuppressWarnings("unused") 352 private int number; 353 } 354 355 @AutoProperty(autoDetect = AutoDetectPolicy.METHOD) 356 private static interface Interface { 357 public int getInt(); 358 public String getString(); 359 } 360 361 private static <T> Pojomator<T> makePojomator(Class<T> clazz) { 362 return PojomatorFactory.makePojomator(clazz); 363 } 364 }