View Javadoc
1   package org.pojomatic.internal;
2   
3   import static org.testng.Assert.*;
4   import static org.hamcrest.MatcherAssert.assertThat;
5   
6   import org.testng.annotations.Test;
7   
8   import java.io.IOException;
9   import java.lang.reflect.AnnotatedElement;
10  import java.util.ArrayList;
11  import java.util.regex.Pattern;
12  
13  import org.mockito.Mockito;
14  import org.pojomatic.Pojomatic;
15  import org.pojomatic.Pojomator;
16  import org.pojomatic.PropertyElement;
17  import org.pojomatic.annotations.PojoFormat;
18  import org.pojomatic.annotations.Property;
19  import org.pojomatic.annotations.PropertyFormat;
20  import org.pojomatic.formatter.DefaultEnhancedPropertyFormatter;
21  
22  import com.google.common.io.ByteStreams;
23  
24  public class PojomatorFactoryTest {
25  
26    public static class ToBeDuplicated {
27      @Property int x;
28    }
29  
30    @Test
31    public void testDuplciateClassNames() throws Exception {
32      final String simpleName = ToBeDuplicated.class.getName().replace(".", "/");
33      ClassLoader reloader = new ClassLoader(getClass().getClassLoader()) {
34        @Override
35        protected java.lang.Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
36          if (name.equals(simpleName)) {
37            byte[] bytes;
38            try {
39              bytes = ByteStreams.toByteArray(ToBeDuplicated.class.getClassLoader().getResourceAsStream(name + ".class"));
40            } catch (IOException e) {
41              throw new RuntimeException(e);
42            }
43            Class<?> clazz = defineClass(ToBeDuplicated.class.getName(), bytes, 0, bytes.length);
44            if (resolve) {
45              resolveClass(clazz);
46            }
47            return clazz;
48          }
49          else {
50            return super.loadClass(name, resolve);
51          }
52        }
53      };
54      Class<?> simple2 = reloader.loadClass(simpleName);
55      assertNotEquals(simple2, ToBeDuplicated.class);
56      Pojomator<ToBeDuplicated> pojomator1 = PojomatorFactory.makePojomator(ToBeDuplicated.class);
57      assertTrue(pojomator1.doEquals(ToBeDuplicated.class.newInstance(), ToBeDuplicated.class.newInstance()));
58      @SuppressWarnings("unchecked")
59      Pojomator<Object> pojomator2 = (Pojomator<Object>) PojomatorFactory.makePojomator(simple2);
60      assertTrue(pojomator2.doEquals(simple2.newInstance(), simple2.newInstance()));
61    }
62  
63    private static class Inaccessible {
64      @Override
65      public int hashCode() {
66        return 7;
67      }
68    }
69  
70    @Test
71    public void testFieldWithInaccessibleType() throws Exception {
72      class Simple {
73        @Property Inaccessible x = new Inaccessible();
74      }
75      assertEquals(PojomatorFactory.makePojomator(Simple.class).doHashCode(new Simple()), 31 + 7);
76    }
77  
78    @Test
79    public void testComplexObject() throws Exception {
80      class Complex {
81        @Property int i = 3;
82        @Property Object o = "hello";
83        @Property float f = 4.0f;
84      }
85      Complex complex = new Complex();
86      Pojomator<Complex> pojomator = PojomatorFactory.makePojomator(Complex.class);
87      assertEquals(pojomator.doHashCode(complex), 31 * (31 * (31  + complex.i) + complex.o.hashCode()) + Float.floatToIntBits(complex.f));
88    }
89  
90    @Test
91    public void testNullEquals() throws Exception {
92      class Simple {
93        @Property int x;
94      }
95      Simple instance = new Simple();
96      assertFalse(PojomatorFactory.makePojomator(Simple.class).doEquals(instance, null));
97    }
98  
99    @Test
100   public void testIncompatibleClassEquals() throws Exception {
101     class Simple1 {
102       @Property int x;
103     }
104     class Simple2 {
105       @Property int x;
106     }
107     assertFalse(PojomatorFactory.makePojomator(Simple1.class).doEquals(new Simple1(), new Simple2()));
108   }
109 
110   @Test
111   public void testSimpleToString() throws Exception {
112     class Simple {
113       @Property public String x() { return "foo"; }
114     }
115     assertEquals(PojomatorFactory.makePojomator(Simple.class).doToString(new Simple()), "Simple{x: {foo}}");
116   }
117 
118   @Test
119   public void testNonEnhancedPojoFormatter() throws Exception {
120     @SuppressWarnings("deprecation")
121     @PojoFormat(org.pojomatic.formatter.DefaultPojoFormatter.class)
122     class Simple {
123       @Property public String x() { return "foo"; }
124     }
125     assertEquals(PojomatorFactory.makePojomator(Simple.class).doToString(new Simple()), "Simple{x: {foo}}");
126   }
127 
128   @Test
129   public void testRepeatedFieldNames() {
130     class Parent {
131       protected Parent(int x) { this.x = x; }
132       @Property private int x;
133     }
134     class Child extends Parent {
135       public Child(int x1, int x2) { super(x1); this.x = x2; }
136       @Property private int x;
137     }
138     Pojomator<Child> pojomator = PojomatorFactory.makePojomator(Child.class);
139     assertTrue(pojomator.doEquals(new Child(1, 2), new Child(1, 2)));
140     assertFalse(pojomator.doEquals(new Child(1, 2), new Child(2, 1)));
141   }
142 
143   static class InitializablePropertyFormatter extends DefaultEnhancedPropertyFormatter {
144     private boolean initCalled;
145 
146     @Override
147     public void initialize(AnnotatedElement element) {
148       initCalled = true;
149     }
150 
151     @Override
152     public void appendFormatted(StringBuilder builder, int i) {
153       super.appendFormatted(builder, initCalled ? i * 2 : i);
154     }
155   }
156 
157   @Test
158   public void testPropertyFormatterRequringIntialization() {
159 
160     class Simple {
161       @Property
162       @PropertyFormat(InitializablePropertyFormatter.class)
163       int i = 3;
164     }
165 
166     Pojomator<Simple> pojomator = PojomatorFactory.makePojomator(Simple.class);
167     assertEquals(pojomator.doToString(new Simple()), "Simple{i: {6}}");
168   }
169 
170   @SuppressWarnings("deprecation")
171   public static class DummyPojoFormatter implements org.pojomatic.formatter.PojoFormatter {
172 
173     @Override
174     public String getToStringPrefix(Class<?> pojoClass) {
175       return "pojopre-" + pojoClass.getSimpleName() + ":";
176     }
177 
178     @Override
179     public String getToStringSuffix(Class<?> pojoClass) {
180       return "pojopost-" + pojoClass.getSimpleName() + ":";
181     }
182 
183     @Override
184     public String getPropertyPrefix(PropertyElement property) {
185       return "proppre-" + property.getName() + ":";
186     }
187 
188     @Override
189     public String getPropertySuffix(PropertyElement property) {
190       return "proppost-" + property.getName() + ":";
191     }
192 
193   }
194 
195   @Test
196   public void testPojoFormatterWrapping() {
197     @PojoFormat(DummyPojoFormatter.class)
198     class Pojo {
199       @Property int x;
200     }
201 
202     assertEquals(PojomatorFactory.makePojomator(Pojo.class).doToString(new Pojo()), "pojopre-Pojo:proppre-x:0proppost-x:pojopost-Pojo:");
203   }
204 
205   @SuppressWarnings("deprecation")
206   public static class DummyPropertyFormatter implements org.pojomatic.formatter.PropertyFormatter {
207     static AnnotatedElement initializedElement;
208 
209     @Override
210     public void initialize(AnnotatedElement element) {
211       initializedElement = element;
212     }
213 
214     @Override
215     public String format(Object value) {
216       return "-" + value + "-";
217     }
218   }
219 
220   @Test
221   public void testPropertyFormatterWrapping() throws Exception {
222     class Pojo {
223       @PropertyFormat(DummyPropertyFormatter.class)
224       @Property int x;
225     }
226     Pojomator<Pojo> pojomator = PojomatorFactory.makePojomator(Pojo.class);
227     assertEquals(DummyPropertyFormatter.initializedElement, Pojo.class.getDeclaredField("x"));
228     assertEquals(pojomator.doToString(new Pojo()), "Pojo{x: {-0-}}");
229   }
230 
231   @Test
232   public void testStacktrace() {
233     class Pojo {
234       @Property int getX() { return 3; }
235       @Property int getY() throws Exception { throw new Exception("testing"); }
236     }
237     Pojomator<Pojo> pojomator = PojomatorFactory.makePojomator(Pojo.class);
238     assertThat(
239       pojomator.getClass().getName(),
240       RegexMatcher.matches(Pattern.quote(PojomatorStub.class.getName()+ "$") + "\\d+"));
241 
242     Pojo pojo = new Pojo();
243     try {
244       pojomator.doHashCode(pojo);
245       fail("Exception expected");
246     }
247     catch (Exception e) {
248       assertEquals(e.getMessage(), "testing");
249       StackTraceElement[] stackTrace = e.getStackTrace();
250 
251       assertEquals(pojomator.getClass().getName(), stackTrace[1].getClassName());
252       assertStackTraceElementFromGeneratedByteCode(
253         stackTrace[1], pojomator, "get_method_" + Pojo.class.getName().replace('.', '$')+ "_getY", 204);
254       assertStackTraceElementFromGeneratedByteCode(stackTrace[2], pojomator, "doHashCode", 225);
255     }
256   }
257 
258   private void assertStackTraceElementFromGeneratedByteCode(
259     StackTraceElement element, Pojomator<?> pojomator, String methodName, int lineNumber) {
260     assertEquals(element.getClassName(), pojomator.getClass().getName());
261     assertEquals(element.getMethodName(), methodName);
262     assertEquals(element.getFileName(), "Look for visitLineNumber");
263     assertEquals(element.getLineNumber(), lineNumber);
264   }
265 
266   @Test
267   public void testProxiedClass() {
268     class Pojo {
269       @Property int getX() { return 3; }
270     }
271     Pojo spy = Mockito.spy(new Pojo());
272     assertEquals(Pojomatic.hashCode(spy), 31 + 3);
273   }
274 
275   @Test
276   public void testParentJarClass() {
277    class Pojo extends ArrayList<Integer> {
278     private static final long serialVersionUID = 1L;
279     @Property int age = 3;
280    }
281    assertEquals(PojomatorFactory.makePojomator(Pojo.class).doHashCode(new Pojo()), 31 + 3);
282   }
283 }