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 }