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 }