1 package org.pojomatic.internal;
2
3 import static org.testng.Assert.*;
4
5 import org.testng.annotations.Test;
6 import java.util.*;
7
8 import org.pojomatic.PropertyElement;
9 import org.pojomatic.TestUtils;
10 import org.pojomatic.annotations.*;
11 import org.pojomatic.internal.a.C1;
12 import org.pojomatic.internal.b.C2;
13 import org.pojomatic.internal.b.C4;
14 import org.pojomatic.internal.factory.PojoClassFactory;
15 import org.pojomatic.internal.factory.PojoDescriptor;
16 import org.pojomatic.internal.factory.PropertyDescriptor;
17
18 public class ClassPropertiesTest {
19 @Test public void testForClass() {
20 ClassProperties interfaceProperties = ClassProperties.forClass(Interface.class);
21 assertSame(ClassProperties.forClass(Interface.class), interfaceProperties);
22 }
23
24 @Test
25 public void testAnnotatedFields() throws Exception {
26 final PropertyElement privateStringField = TestUtils.field(FieldPojo.class, "privateString");
27 final PropertyElement publicIntField = TestUtils.field(FieldPojo.class, "publicInt");
28 final PropertyElement onlyForStringField = TestUtils.field(FieldPojo.class, "onlyForToString");
29 final PropertyElement forEqualsAndToString =
30 TestUtils.field(FieldPojo.class, "forEqualsAndToString");
31
32 ClassProperties classProperties = ClassProperties.forClass(FieldPojo.class);
33
34 assertEquals(asSet(classProperties.getEqualsProperties()), asSet(privateStringField, publicIntField, forEqualsAndToString));
35 assertEquals(asSet(classProperties.getHashCodeProperties()), asSet(privateStringField, publicIntField));
36 assertEquals(asSet(classProperties.getToStringProperties()), asSet(privateStringField, publicIntField, onlyForStringField, forEqualsAndToString));
37
38 assertEquals(classProperties.getAllProperties(), asSet(privateStringField, publicIntField, onlyForStringField, forEqualsAndToString));
39 }
40
41 @Test
42 public void testAutoFields() throws Exception {
43
44 final PropertyElement stringField = TestUtils.field(AutoFieldPojo.class, "string");
45 final PropertyElement allInDoubleField = TestUtils.field(AutoFieldPojo.class, "allInDouble");
46
47 ClassProperties classProperties = ClassProperties.forClass(AutoFieldPojo.class);
48
49 assertEquals(asSet(classProperties.getEqualsProperties()), asSet(allInDoubleField));
50 assertEquals(asSet(classProperties.getHashCodeProperties()), asSet(allInDoubleField));
51 assertEquals(asSet(classProperties.getToStringProperties()), asSet(stringField, allInDoubleField));
52 }
53
54 @Test
55 public void testAutoFieldWithSynthetic() throws Exception {
56 Set<PropertyElement> properties = asSet(TestUtils.field(AuthFieldPojoWithSyntheticEnclosingThis.class, "findMe"));
57 ClassProperties classProperties = ClassProperties.forClass(AuthFieldPojoWithSyntheticEnclosingThis.class);
58
59 assertEquals(classProperties.getEqualsProperties(), properties);
60 assertEquals(classProperties.getHashCodeProperties(), properties);
61 assertEquals(classProperties.getToStringProperties(), properties);
62 }
63
64 @Test
65 public void testAnnotatedMethods() throws Exception {
66 class MethodPojo {
67 @Property public int getInt() { return 0; }
68 @Property private String privateString() { return null; }
69 @Property(policy=PojomaticPolicy.EQUALS) public double onlyForEquals() { return 0.0; }
70 }
71
72 final PropertyElement getIntMethod = TestUtils.method(MethodPojo.class, "getInt");
73 final PropertyElement privateStringMethod = TestUtils.method(MethodPojo.class, "privateString");
74 final PropertyElement onlyForEqualsMethod = TestUtils.method(MethodPojo.class, "onlyForEquals");
75
76 ClassProperties classProperties = ClassProperties.forClass(MethodPojo.class);
77
78 assertEquals(asSet(classProperties.getEqualsProperties()), asSet(getIntMethod, privateStringMethod, onlyForEqualsMethod));
79 assertEquals(asSet(classProperties.getHashCodeProperties()), asSet(getIntMethod, privateStringMethod));
80 assertEquals(asSet(classProperties.getToStringProperties()), asSet(getIntMethod, privateStringMethod));
81 }
82
83 @Test
84 public void testAutoMethods() throws Exception {
85 final Set<PropertyElement> commonProperties = asSet(
86 TestUtils.method(AutoMethodPojo.class, "getInt"),
87 TestUtils.method(AutoMethodPojo.class, "isBoolean"),
88 TestUtils.method(AutoMethodPojo.class, "is_boolean"),
89 TestUtils.method(AutoMethodPojo.class, "get_int"));
90 final Set<PropertyElement> equalsHashCodeProperties = new HashSet<>(commonProperties);
91 equalsHashCodeProperties.add(TestUtils.method(AutoMethodPojo.class, "getHashCodeAndEquals"));
92
93 ClassProperties classProperties = ClassProperties.forClass(AutoMethodPojo.class);
94
95 assertEquals(asSet(classProperties.getEqualsProperties()), equalsHashCodeProperties);
96 assertEquals(asSet(classProperties.getHashCodeProperties()), equalsHashCodeProperties);
97 assertEquals(asSet(classProperties.getToStringProperties()), commonProperties);
98 }
99
100 @Test
101 public void testAutoMethodsWithSynthetic() throws Exception {
102 Class<?> pojoClass = new PojoClassFactory().generateClass(
103 new PojoDescriptor(
104 new PropertyDescriptor(String.class).withName("getX").asMethod(),
105 new PropertyDescriptor(Object.class).withName("getY").asMethod().asSynthetic())
106 .withAutoDetectPolicy(AutoDetectPolicy.METHOD));
107 final Set<PropertyElement> properties = asSet(TestUtils.method(pojoClass, "getX"));
108
109 ClassProperties classProperties = ClassProperties.forClass(pojoClass);
110
111 assertEquals(asSet(classProperties.getEqualsProperties()), properties);
112 assertEquals(asSet(classProperties.getHashCodeProperties()), properties);
113 assertEquals(asSet(classProperties.getToStringProperties()), properties);
114 }
115
116 @Test(expectedExceptions=IllegalArgumentException.class)
117 public void testAnnotatedMethodReturningVoid() {
118 class MethodReturnsVoidPojo { @Property public void noReturn() {} }
119 ClassProperties.forClass(MethodReturnsVoidPojo.class);
120 }
121
122 @Test(expectedExceptions=IllegalArgumentException.class)
123 public void testAnnotatedMethodTakingArgs() {
124 class MethodTakesArgsPojo {
125 @Property public int takesArgs(String death) {
126 return death.length(); }
127 }
128 ClassProperties.forClass(MethodTakesArgsPojo.class);
129 }
130
131 @Test
132 public void testAnnotatedInheritance() throws Exception {
133 Set<PropertyElement> expectedParent = asSet(TestUtils.method(ParentPojo.class, "getFoo"));
134 ClassProperties parentClassProperties = ClassProperties.forClass(ParentPojo.class);
135 assertEquals(asSet(parentClassProperties.getEqualsProperties()), expectedParent);
136 assertEquals(asSet(parentClassProperties.getHashCodeProperties()), expectedParent);
137 assertEquals(asSet(parentClassProperties.getToStringProperties()), expectedParent);
138
139 ClassProperties childClassProperties = ClassProperties.forClass(ChildPojo.class);
140 Set<PropertyElement> expectedChild = asSet(
141 TestUtils.method(ParentPojo.class, "getFoo"), TestUtils.field(ChildPojo.class, "other"));
142 assertEquals(asSet(childClassProperties.getEqualsProperties()), expectedChild);
143 assertEquals(asSet(childClassProperties.getHashCodeProperties()), expectedChild);
144 assertEquals(asSet(childClassProperties.getToStringProperties()), expectedChild);
145 }
146
147 @Test
148 public void testAutoInheritanceBothAuto() throws Exception {
149 Set<PropertyElement> expectedParent = asSet(TestUtils.method(ParentAutoPojo.class, "getFoo"));
150 ClassProperties parentClassProperties = ClassProperties.forClass(ParentAutoPojo.class);
151 assertEquals(asSet(parentClassProperties.getEqualsProperties()), expectedParent);
152 assertEquals(asSet(parentClassProperties.getHashCodeProperties()), Collections.EMPTY_SET);
153 assertEquals(asSet(parentClassProperties.getToStringProperties()), Collections.EMPTY_SET);
154
155 ClassProperties childClassProperties = ClassProperties.forClass(ChildAutoFieldPojo.class);
156 Set<PropertyElement> expectedChild = asSet(
157 TestUtils.field(ChildAutoFieldPojo.class, "other"));
158 assertEquals(asSet(childClassProperties.getEqualsProperties()), expectedParent);
159 assertEquals(asSet(childClassProperties.getHashCodeProperties()), Collections.EMPTY_SET);
160 assertEquals(asSet(childClassProperties.getToStringProperties()), expectedChild);
161 }
162
163 @Test
164 public void testAutoInheritanceWithOverride() throws Exception {
165 @AutoProperty(autoDetect=AutoDetectPolicy.METHOD)
166 class ChildAutoMethodPojo extends ParentPojo {
167 @Override public int getFoo() { return 2; }
168 @SuppressWarnings("unused") public int getBar() { return 2; }
169 }
170
171 ClassProperties childClassProperties = ClassProperties.forClass(ChildAutoMethodPojo.class);
172 Set<PropertyElement> expected = asSet(
173 TestUtils.method(ParentPojo.class, "getFoo"),
174 TestUtils.method(ChildAutoMethodPojo.class, "getBar"));
175 assertEquals(asSet(childClassProperties.getEqualsProperties()), expected);
176 assertEquals(asSet(childClassProperties.getHashCodeProperties()), expected);
177 assertEquals(asSet(childClassProperties.getToStringProperties()), expected);
178 }
179
180 @Test
181 public void testAutoInheritanceAnnotatedParent() throws Exception {
182 @AutoProperty(autoDetect=AutoDetectPolicy.METHOD)
183 class ChildExtendsAnnotatedPojo extends ParentPojo {
184 @Override public int getFoo() { return 0; }
185 @SuppressWarnings("unused") public String getMyString() { return "foo"; }
186 }
187
188 Set<PropertyElement> expectedParent = asSet(TestUtils.method(ParentPojo.class, "getFoo"));
189 ClassProperties parentClassProperties = ClassProperties.forClass(ParentPojo.class);
190 assertEquals(asSet(parentClassProperties.getEqualsProperties()), expectedParent);
191 assertEquals(asSet(parentClassProperties.getHashCodeProperties()), expectedParent);
192 assertEquals(asSet(parentClassProperties.getToStringProperties()), expectedParent);
193
194 ClassProperties childClassProperties = ClassProperties.forClass(ChildExtendsAnnotatedPojo.class);
195 Set<PropertyElement> expectedChild = asSet(
196 TestUtils.method(ParentPojo.class, "getFoo"),
197 TestUtils.method(ChildExtendsAnnotatedPojo.class, "getMyString"));
198 assertEquals(asSet(childClassProperties.getEqualsProperties()), expectedChild);
199 assertEquals(asSet(childClassProperties.getHashCodeProperties()), expectedChild);
200 assertEquals(asSet(childClassProperties.getToStringProperties()), expectedChild);
201 }
202
203 @Test
204 public void testAutoInheritanceAutoParentAnnotatedChild() throws Exception {
205 class ChildExtendsAutoPojo extends ParentAutoPojo {
206 @Property public String other;
207 @Override public int getFoo() { return 2; }
208 @SuppressWarnings("unused") public String getBar() { return ""; }
209 }
210
211 Set<PropertyElement> expectedParent = asSet(TestUtils.method(ParentAutoPojo.class, "getFoo"));
212 ClassProperties parentClassProperties = ClassProperties.forClass(ParentAutoPojo.class);
213 assertEquals(asSet(parentClassProperties.getEqualsProperties()), expectedParent);
214 assertEquals(asSet(parentClassProperties.getHashCodeProperties()), Collections.EMPTY_SET);
215 assertEquals(asSet(parentClassProperties.getToStringProperties()), Collections.EMPTY_SET);
216
217 ClassProperties childClassProperties = ClassProperties.forClass(ChildExtendsAutoPojo.class);
218 Set<PropertyElement> expectedChildEquals = asSet(
219 TestUtils.method(ParentAutoPojo.class, "getFoo"),
220 TestUtils.field(ChildExtendsAutoPojo.class, "other"));
221 assertEquals(asSet(childClassProperties.getEqualsProperties()), expectedChildEquals);
222 Set<PropertyElement> expectedChild = asSet(
223 TestUtils.field(ChildExtendsAutoPojo.class, "other"));
224 assertEquals(asSet(childClassProperties.getHashCodeProperties()), expectedChild);
225 assertEquals(asSet(childClassProperties.getToStringProperties()), expectedChild);
226 }
227
228 @Test
229 public void testOverriddenMethods() throws Exception {
230 ClassProperties classProperties = ClassProperties.forClass(C4.class);
231 assertEquals(asSet(classProperties.getEqualsProperties()), asSet(
232 TestUtils.method(C1.class, "packagePrivate"),
233 TestUtils.method(C1.class, "packagePrivateOverriddenProtected"),
234 TestUtils.method(C1.class, "packagePrivateOverriddenPublic"),
235 TestUtils.method(C1.class, "protectedMethod"),
236 TestUtils.method(C1.class, "publicMethod"),
237 TestUtils.method(C2.class, "packagePrivate"),
238 TestUtils.method(C2.class, "packagePrivateOverriddenProtected"),
239 TestUtils.method(C2.class, "packagePrivateOverriddenPublic")));
240 }
241
242 @Test
243 public void testAnnotatedStaticField() {
244 try {
245 ClassProperties.forClass(StaticField.class);
246 fail("Exception expected");
247 }
248 catch (IllegalArgumentException e) {
249 assertEquals(e.getMessage(), "Static field " + StaticField.class.getName() + ".a is annotated with @Property");
250 }
251 }
252
253 @Test
254 public void testSyntheticMethod() throws Exception {
255 assertEquals (
256 asSet(ClassProperties.forClass(Synthetic.class).getEqualsProperties()),
257 asSet(TestUtils.method(Synthetic.class, "getA")));
258 }
259
260 @Test
261 public void testAnnotatedStaticMethod() {
262 try {
263 ClassProperties.forClass(StaticMethod.class);
264 fail("Exception expected");
265 }
266 catch (IllegalArgumentException e) {
267 assertEquals(e.getMessage(), "Static method " + StaticMethod.class.getName() + ".a() is annotated with @Property");
268 }
269 }
270
271 @Test public void testInterface() throws Exception {
272 ClassProperties classProperties = ClassProperties.forClass(Interface.class);
273 PropertyElement getFoo = TestUtils.method(Interface.class, "getFoo");
274 PropertyElement baz = TestUtils.method(Interface.class, "baz");
275 assertEquals(asSet(classProperties.getHashCodeProperties()), asSet(getFoo));
276 assertEquals(asSet(classProperties.getToStringProperties()), asSet(getFoo));
277 assertEquals(asSet(classProperties.getEqualsProperties()), asSet(getFoo, baz));
278 }
279
280 @Test public void testIsCompatibleForEquals() {
281 class Parent { @Property int getX() { return 3; } }
282 class NonContributingChild extends Parent {}
283 @OverridesEquals class AnnotatedNonContributingChild extends Parent {}
284 class ContributingChild extends Parent{ @Property int getY() { return 3; } }
285 class ChildOfContributingChild extends ContributingChild{}
286
287 List<List<Class<?>>> partitions = Arrays.asList(
288 Arrays.<Class<?>>asList(Parent.class, NonContributingChild.class),
289 Arrays.<Class<?>>asList(ContributingChild.class, ChildOfContributingChild.class),
290 Arrays.<Class<?>>asList(AnnotatedNonContributingChild.class),
291 Arrays.<Class<?>>asList(Interface.class));
292 for (List<Class<?>> partition: partitions) {
293 for (Class<?> clazz: partition) {
294 for(List<Class<?>> otherPartition: partitions) {
295 for(Class<?> otherClazz: otherPartition) {
296 if (partition == otherPartition) {
297 assertTrue(ClassProperties.forClass(clazz).isCompatibleForEquals(otherClazz));
298 }
299 else {
300 assertFalse(ClassProperties.forClass(clazz).isCompatibleForEquals(otherClazz));
301 }
302 }
303 }
304 }
305 }
306 }
307
308 @Test public void testSubclassCannotOverrideEquals() {
309 class ChildOfInterface implements Interface {
310 @Override
311 public int getFoo() { return 0; }
312 @Override
313 public int bar() { return 0; }
314 @Override
315 public int baz() { return 0; }
316 }
317
318 assertTrue(ClassProperties.forClass(Interface.class).isCompatibleForEquals(ChildOfInterface.class));
319
320 @SubclassCannotOverrideEquals class A { @Property int x; }
321 class B extends A { @Property int y; }
322 assertTrue(ClassProperties.forClass(A.class).isCompatibleForEquals(B.class));
323 assertFalse(ClassProperties.forClass(B.class).isCompatibleForEquals(A.class));
324 }
325
326 @Test
327 public void testMissingClassBytes() throws Exception {
328 class Bean {
329 @Property int a, b, c;
330 }
331
332 ClassOnlyClassLoader classLoader = new ClassOnlyClassLoader(Bean.class.getClassLoader());
333 Class<?> beanClass = classLoader.loadClass(Bean.class.getName());
334 try {
335 ClassProperties.forClass(beanClass);
336 fail("Exception expected");
337 }
338 catch (RuntimeException e) {
339 assertEquals(e.getMessage(), "no class bytes for class " + beanClass.getName());
340 }
341 }
342
343
344
345
346 public static class FieldPojo {
347 @Property
348 private String privateString;
349
350 @Property
351 public int publicInt;
352
353 @Property(policy=PojomaticPolicy.TO_STRING)
354 private int onlyForToString;
355
356 @Property(policy=PojomaticPolicy.EQUALS_TO_STRING)
357 private int forEqualsAndToString;
358 }
359
360 @AutoProperty(policy=DefaultPojomaticPolicy.TO_STRING)
361 public static class AutoFieldPojo {
362 public String string;
363
364 @Property(policy=PojomaticPolicy.NONE)
365 public int ignoredInt;
366
367 @Property(policy=PojomaticPolicy.ALL)
368 public double allInDouble;
369
370
371 public float getNotDetected() { return 1f; }
372
373
374 public static String staticField;
375 }
376
377 @AutoProperty(autoDetect=AutoDetectPolicy.FIELD)
378 public class AuthFieldPojoWithSyntheticEnclosingThis {
379 @SuppressWarnings("unused")
380 private String findMe;
381 }
382
383 @AutoProperty(autoDetect=AutoDetectPolicy.METHOD, policy=DefaultPojomaticPolicy.ALL)
384 public static class AutoMethodPojo {
385
386 int notDetected;
387
388 @Property(policy=PojomaticPolicy.NONE)
389 public String getIgnored() { return null; }
390
391 @Property(policy=PojomaticPolicy.HASHCODE_EQUALS)
392 public double getHashCodeAndEquals() { return 0.0; }
393
394
395 public int getInt() { return 0; }
396 public boolean isBoolean() { return true; }
397 public boolean is_boolean() { return true; }
398 public int get_int() { return 0; }
399
400
401 public boolean isaEnabled() { return true; }
402 public int gettyIsAMuseum() { return 1; }
403 public String thisIsNotAGetter() { return "really, it's not"; }
404
405
406 public void getHello() {}
407 public int getTriple(int arg) { return arg * 3; }
408
409
410
411 public static String getStatic() { return null; }
412 }
413
414 private static abstract class ParentPojo {
415 @Property
416 public abstract int getFoo();
417 }
418
419 public static class ChildPojo extends ParentPojo {
420 @Property
421 public String other;
422
423 @Override public int getFoo() { return 2; }
424 }
425
426 @AutoProperty(autoDetect=AutoDetectPolicy.METHOD, policy=DefaultPojomaticPolicy.EQUALS)
427 private static abstract class ParentAutoPojo {
428 public abstract int getFoo();
429 }
430
431 @AutoProperty(autoDetect=AutoDetectPolicy.FIELD, policy=DefaultPojomaticPolicy.TO_STRING)
432 public static class ChildAutoFieldPojo extends ParentAutoPojo {
433 public String other;
434
435 @Override public int getFoo() { return 2; }
436 }
437
438 public static class ParentOfSynthetic {
439 public Number getA() { return null; }
440 }
441
442 public static class Synthetic extends ParentOfSynthetic {
443 @Override @Property public Integer getA() { return 3; }
444 }
445 @AutoProperty(autoDetect=AutoDetectPolicy.METHOD)
446 public static interface Interface {
447 int getFoo();
448 int bar();
449 @Property(policy=PojomaticPolicy.EQUALS) int baz();
450 }
451
452 public static class StaticField {
453 @Property public static int a;
454 }
455
456 public static class StaticMethod {
457 @Property public static int a() { return 1; }
458 }
459
460 private static Set<PropertyElement> asSet(PropertyElement... elements) {
461 return new HashSet<>(Arrays.asList(elements));
462 }
463
464 private static Set<PropertyElement> asSet(Collection<PropertyElement> elements) {
465 return new HashSet<>(elements);
466 }
467 }
468