View Javadoc
1   package org.pojomatic.internal;
2   
3   import static org.testng.Assert.*;
4   
5   import org.testng.annotations.Test;
6   import org.testng.annotations.BeforeMethod;
7   
8   import java.lang.reflect.AnnotatedElement;
9   import java.lang.reflect.Field;
10  import java.lang.reflect.Method;
11  import java.util.Arrays;
12  import java.util.Collections;
13  import java.util.EnumMap;
14  import java.util.List;
15  import java.util.Map;
16  
17  import org.pojomatic.PropertyElement;
18  
19  import com.google.common.base.Function;
20  import com.google.common.collect.Maps;
21  
22  
23  public class PropertyClassVisitorTest {
24    static class FieldsAndGetters {
25      int field1;
26      long getter1() { return 0L; }
27      String field2;
28      long field3;
29      boolean is2() { return false; }
30      void m3(@SuppressWarnings("unused") int n) {}
31      void m4(@SuppressWarnings("unused") int n) {}
32    }
33  
34    static class Other {
35      int n;
36    }
37  
38    private static enum NameExtractor implements Function<PropertyElement, String> {
39      INSTANCE;
40      @Override
41      public String apply(PropertyElement p) {
42        AnnotatedElement element = p.getElement();
43        if (element instanceof Method) {
44          return ((Method) element).getName();
45        } else {
46          return ((Field) element).getName();
47        }
48      }
49    }
50  
51    private PropertyElement f1;
52    private PropertyElement f2;
53    private PropertyElement f3;
54    private PropertyElement m1;
55    private PropertyElement m2;
56    private PropertyElement m3;
57    private PropertyElement m4;
58  
59    private static List<PropertyElement> NO_PROPERTIES = Collections.emptyList();
60  
61    @BeforeMethod
62    public void setup() throws Exception {
63      // Note that fields will be visited before methods, regardless of declaration order.
64      f1 = new PropertyField(FieldsAndGetters.class.getDeclaredField("field1"), "");
65      f2 = new PropertyField(FieldsAndGetters.class.getDeclaredField("field2"), "");
66      f3 = new PropertyField(Other.class.getDeclaredField("n"), "");
67      m1 = new PropertyAccessor(FieldsAndGetters.class.getDeclaredMethod("getter1"), "");
68      m2 = new PropertyAccessor(FieldsAndGetters.class.getDeclaredMethod("is2"), "");
69      m3 = new PropertyAccessor(FieldsAndGetters.class.getDeclaredMethod("m3", int.class), "");
70      m4 = new PropertyAccessor(FieldsAndGetters.class.getDeclaredMethod("m4", int.class), "");
71    }
72  
73    @Test
74    public void testReflectionOrdering() {
75      PropertyClassVisitor visitor = PropertyClassVisitor.visitClass(
76          FieldsAndGetters.class,
77          makeRoleMaps(Arrays.asList(f1, f2), Arrays.asList(f2, f1), NO_PROPERTIES),
78          makeRoleMaps(Arrays.asList(m2, m1), Arrays.asList(m1), Arrays.asList(m2)));
79      assertNotNull(visitor);
80      assertEquals(visitor.getSortedProperties(), makeRoleLists(Arrays.asList(f1, f2, m1, m2), Arrays.asList(f1, f2, m1), Arrays.asList(m2)));
81    }
82  
83    @Test
84    public void testMissingCodeSource() {
85      Map<PropertyRole, Map<String, PropertyElement>> roleMaps =
86          makeRoleMaps(NO_PROPERTIES, NO_PROPERTIES, NO_PROPERTIES);
87      PropertyClassVisitor visitor = PropertyClassVisitor.visitClass(String.class, roleMaps, roleMaps);
88      assertNull(visitor);
89    }
90  
91    @Test
92    public void testMissingClassBytes() throws Exception {
93      class Bean {}
94  
95      ClassOnlyClassLoader classLoader = new ClassOnlyClassLoader(Bean.class.getClassLoader());
96      Class<?> beanClass = classLoader.loadClass(Bean.class.getName());
97      assertNotSame(Bean.class, beanClass);
98      assertNotNull(beanClass.getProtectionDomain().getCodeSource().getLocation());
99      Map<PropertyRole, Map<String, PropertyElement>> roleMaps =
100         makeRoleMaps(NO_PROPERTIES, NO_PROPERTIES, NO_PROPERTIES);
101     PropertyClassVisitor visitor = PropertyClassVisitor.visitClass(String.class, roleMaps, roleMaps);
102     assertNull(visitor);
103   }
104 
105   @Test
106   public void testThrowReflectionMissmatch() throws Exception {
107     try {
108 
109       PropertyClassVisitor.visitClass(
110         FieldsAndGetters.class,
111         makeRoleMaps(Arrays.asList(f3), NO_PROPERTIES, NO_PROPERTIES),
112         makeRoleMaps(Arrays.asList(m3, m4), NO_PROPERTIES, NO_PROPERTIES));
113       fail("Exception expected");
114     }
115     catch (IllegalStateException e) {
116       assertEquals(e.getMessage(), "In class " + FieldsAndGetters.class.getName() + ", properties "
117       + f3.getElement().toString() + ", " + m3.getElement().toString() + ", " + m4.getElement().toString()
118       + " were found in reflection, but not when visiting the bytecode");
119     }
120   }
121 
122   @Test
123   public void testNestMates() throws Exception {
124     Field field = NestParent.class.getDeclaredField("i");
125     List<PropertyElement> fields = Arrays.<PropertyElement>asList(new PropertyField(field, "i"));
126     PropertyClassVisitor propertyClassVisitor = PropertyClassVisitor.visitClass(
127       NestParent.class,
128       makeRoleMaps(fields, fields, fields),
129       makeRoleMaps(NO_PROPERTIES, NO_PROPERTIES, NO_PROPERTIES));
130     assertEquals( // verify that visitClass was successful
131       propertyClassVisitor.getSortedProperties().get(PropertyRole.EQUALS),
132       fields);
133   }
134 
135   private static Map<PropertyRole, Map<String, PropertyElement>> makeRoleMaps(
136       List<PropertyElement> forEquals,
137       List<PropertyElement> forHashCode,
138       List<PropertyElement> forToString) {
139     EnumMap<PropertyRole, Map<String, PropertyElement>> enumMap = new EnumMap<>(PropertyRole.class);
140     enumMap.put(PropertyRole.EQUALS, Maps.uniqueIndex(forEquals, NameExtractor.INSTANCE));
141     enumMap.put(PropertyRole.HASH_CODE, Maps.uniqueIndex(forHashCode, NameExtractor.INSTANCE));
142     enumMap.put(PropertyRole.TO_STRING, Maps.uniqueIndex(forToString, NameExtractor.INSTANCE));
143     return enumMap;
144   }
145 
146   private static Map<PropertyRole, List<PropertyElement>> makeRoleLists(
147       List<PropertyElement> forEquals, List<PropertyElement> forHashCode, List<PropertyElement> forToString) {
148     EnumMap<PropertyRole, List<PropertyElement>> enumMap = new EnumMap<>(PropertyRole.class);
149     enumMap.put(PropertyRole.EQUALS, forEquals);
150     enumMap.put(PropertyRole.HASH_CODE, forHashCode);
151     enumMap.put(PropertyRole.TO_STRING, forToString);
152     return enumMap;
153   }
154 }