View Javadoc
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 }