package com.xtremelabs.robolectric.bytecode; import javassist.ClassPool; import javassist.CtClass; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; public class AndroidTranslatorUnitTest { private ClassPool classPool; private AndroidTranslator androidTranslator; @Before public void setUp() throws Exception { classPool = new ClassPool(true); androidTranslator = new AndroidTranslator(null, null); } @Test public void whenMethodReturnsObject_shouldGenerateMethodBody() throws Exception { CtClass ctClass = classPool.get("java.lang.String"); String methodBody = androidTranslator.generateMethodBody( ctClass, ctClass.getDeclaredMethod("substring", new CtClass[]{CtClass.intType}), ctClass, Type.OBJECT, false, false); assertEquals("if (!com.xtremelabs.robolectric.bytecode.RobolectricInternals.shouldCallDirectly(this)) {\n" + "Object x = com.xtremelabs.robolectric.bytecode.RobolectricInternals.methodInvoked(\n" + " java.lang.String.class, \"substring\", this, new String[] {\"int\"}, new Object[] {com.xtremelabs.robolectric.bytecode.RobolectricInternals.autobox($1)});\n" + "if (x != null) return ((java.lang.String) x);\n" + "return null;\n" + "}\n", methodBody); } @Test public void whenMethodReturnsPrimitive_shouldGenerateMethodBody() throws Exception { CtClass ctClass = classPool.get("java.lang.String"); String methodBody = androidTranslator.generateMethodBody( ctClass, ctClass.getDeclaredMethod("length"), ctClass, Type.OBJECT, false, false); assertEquals("if (!com.xtremelabs.robolectric.bytecode.RobolectricInternals.shouldCallDirectly(this)) {\n" + "Object x = com.xtremelabs.robolectric.bytecode.RobolectricInternals.methodInvoked(\n" + " java.lang.String.class, \"length\", this, new String[0], new Object[0]);\n" + "if (x != null) return ((java.lang.String) x);\n" + "return null;\n" + "}\n", methodBody); } @Test public void whenMethodReturnsVoid_shouldGenerateMethodBody() throws Exception { CtClass ctClass = classPool.get("java.lang.Object"); String methodBody = androidTranslator.generateMethodBody( ctClass, ctClass.getDeclaredMethod("wait"), ctClass, Type.VOID, false, false); assertEquals("if (!com.xtremelabs.robolectric.bytecode.RobolectricInternals.shouldCallDirectly(this)) {\n" + "com.xtremelabs.robolectric.bytecode.RobolectricInternals.methodInvoked(\n" + " java.lang.Object.class, \"wait\", this, new String[] {\"long\"}, new Object[] {com.xtremelabs.robolectric.bytecode.RobolectricInternals.autobox($1)});\n" + "return;\n" + "}\n", methodBody); } @Test public void whenMethodIsStatic_shouldGenerateMethodBody() throws Exception { CtClass ctClass = classPool.get("java.lang.String"); String methodBody = androidTranslator.generateMethodBody( ctClass, ctClass.getDeclaredMethod("valueOf", new CtClass[]{CtClass.intType}), ctClass, Type.OBJECT, true, false); assertEquals("if (!com.xtremelabs.robolectric.bytecode.RobolectricInternals.shouldCallDirectly(java.lang.String.class)) {\n" + "Object x = com.xtremelabs.robolectric.bytecode.RobolectricInternals.methodInvoked(\n" + " java.lang.String.class, \"valueOf\", null, new String[] {\"int\"}, new Object[] {com.xtremelabs.robolectric.bytecode.RobolectricInternals.autobox($1)});\n" + "if (x != null) return ((java.lang.String) x);\n" + "return null;\n" + "}\n", methodBody); } @Test public void shouldGenerateParameterList() throws Exception { assertEquals(androidTranslator.makeParameterReplacementList(0), ""); assertEquals(androidTranslator.makeParameterReplacementList(1), "$1"); assertEquals(androidTranslator.makeParameterReplacementList(2), "$1, $2"); } @Test public void shouldGenerateMethodBodyForEquals() throws Exception { CtClass ctClass = classPool.get("java.lang.Object"); String methodBody = androidTranslator.generateMethodBody( ctClass, ctClass.getDeclaredMethod("equals", new CtClass[]{ctClass}), ctClass, Type.BOOLEAN, false, true); assertEquals("if (!com.xtremelabs.robolectric.bytecode.RobolectricInternals.shouldCallDirectly(this)) {\n" + "Object x = com.xtremelabs.robolectric.bytecode.RobolectricInternals.methodInvoked(\n" + " java.lang.Object.class, \"equals\", this, new String[] {\"java.lang.Object\"}, new Object[] {com.xtremelabs.robolectric.bytecode.RobolectricInternals.autobox($1)});\n" + "if (x != null) return ((java.lang.Boolean) x).booleanValue();\n" + "return super.equals($1);}\n", methodBody); } @Test public void shouldInstrumentDefaultRequestDirector() throws Exception { assertTrue(androidTranslator.shouldInstrument(classPool.makeClass("org.apache.http.impl.client.DefaultRequestDirector"))); } @Test public void shouldInstrumentGoogleMapsClasses() throws Exception { assertTrue(androidTranslator.shouldInstrument(classPool.makeClass("com.google.android.maps.SomeMapsClass"))); } @Test public void shouldNotInstrumentCoreJdkClasses() throws Exception { assertFalse(androidTranslator.shouldInstrument(classPool.get("java.lang.Object"))); assertFalse(androidTranslator.shouldInstrument(classPool.get("java.lang.String"))); } @Test public void shouldInstumentAndroidCoreClasses() throws Exception { assertTrue(androidTranslator.shouldInstrument(classPool.makeClass("android.content.Intent"))); assertTrue(androidTranslator.shouldInstrument(classPool.makeClass("android.and.now.for.something.completely.different"))); } @Test public void shouldNotInstrumentLocalBroadcastManager() throws Exception { assertFalse(androidTranslator.shouldInstrument(classPool.makeClass("android.support.v4.content.LocalBroadcastManager"))); } @Test public void shouldAddCustomShadowClass() throws Exception { androidTranslator.addCustomShadowClass("my.custom.Klazz"); assertTrue(androidTranslator.shouldInstrument(classPool.makeClass("my.custom.Klazz"))); } @Test public void testOnLoadWithNonInstrumentedClass() throws Exception { ClassHandler handler = mock(ClassHandler.class); ClassCache cache = mock(ClassCache.class); AndroidTranslator translator = new AndroidTranslator(handler, cache); translator.onLoad(classPool, "java.lang.Object"); verify(cache).isWriting(); verifyNoMoreInteractions(cache); verifyZeroInteractions(handler); } @Test(expected = IllegalStateException.class) public void shouldThrowIllegalStateIfClassCacheIsWriting() throws Exception { ClassCache cache = mock(ClassCache.class); when(cache.isWriting()).thenReturn(true); new AndroidTranslator(null, cache).onLoad(classPool, "java.lang.Object"); } }