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 }