aboutsummaryrefslogtreecommitdiff
path: root/dexmaker-mockito-inline-tests
diff options
context:
space:
mode:
Diffstat (limited to 'dexmaker-mockito-inline-tests')
-rw-r--r--dexmaker-mockito-inline-tests/AndroidManifest.xml3
-rw-r--r--dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java87
-rw-r--r--dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java226
-rw-r--r--dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java391
-rw-r--r--dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java91
-rw-r--r--dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc151
6 files changed, 949 insertions, 0 deletions
diff --git a/dexmaker-mockito-inline-tests/AndroidManifest.xml b/dexmaker-mockito-inline-tests/AndroidManifest.xml
new file mode 100644
index 0000000..44afd30
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest package="com.android.dexmaker.mockito.inline.tests">
+ <application />
+</manifest>
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java
new file mode 100644
index 0000000..e90145f
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/CleanStackTrace.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.mockito.inline.tests;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class CleanStackTrace {
+ public abstract static class TestAbstractClass {
+ public abstract String returnA();
+ }
+
+ public static class TestClass {
+ public final String returnA() {
+ return "A";
+ }
+ }
+
+ public interface TestInterface {
+ String returnA();
+ }
+
+ @Test
+ public void cleanStackTraceProxy() {
+ TestAbstractClass t = mock(TestAbstractClass.class);
+
+ try {
+ verify(t).returnA();
+ } catch (Throwable verifyLocation) {
+ try {
+ throw new Exception();
+ } catch (Exception here) {
+ assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+ .getStackTrace()[0].getMethodName());
+ }
+ }
+ }
+
+ @Test
+ public void cleanStackTraceInline() {
+ TestClass t = mock(TestClass.class);
+
+ try {
+ verify(t).returnA();
+ } catch (Throwable verifyLocation) {
+ try {
+ throw new Exception();
+ } catch (Exception here) {
+ assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+ .getStackTrace()[1].getMethodName());
+ }
+ }
+ }
+
+ @Test
+ public void cleanStackTraceInterface() {
+ TestInterface t = mock(TestInterface.class);
+
+ try {
+ verify(t).returnA();
+ } catch (Throwable verifyLocation) {
+ try {
+ throw new Exception();
+ } catch (Exception here) {
+ assertEquals(here.getStackTrace()[0].getMethodName(), verifyLocation
+ .getStackTrace()[0].getMethodName());
+ }
+ }
+ }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java
new file mode 100644
index 0000000..fa02471
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockFinal.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.mockito.inline.tests;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.print.PrintAttributes;
+import android.printservice.PrintService;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class MockFinal {
+ @Test
+ public void mockFinalJavaMethod() throws Exception {
+ ClassLoader fakeParent = mock(ClassLoader.class);
+ ClassLoader mockClassLoader = mock(ClassLoader.class);
+
+ assertNull(mockClassLoader.getParent());
+
+ // ClassLoader#getParent is final
+ when(mockClassLoader.getParent()).thenReturn(fakeParent);
+
+ assertSame(fakeParent, mockClassLoader.getParent());
+ }
+
+ @Test
+ public void mockFinalAndroidFrameworkClass() throws Exception {
+ // PrintAttributes is final
+ PrintAttributes mockAttributes = mock(PrintAttributes.class);
+
+ assertEquals(0, mockAttributes.getColorMode());
+
+ when(mockAttributes.getColorMode()).thenReturn(42);
+
+ assertEquals(42, mockAttributes.getColorMode());
+ }
+
+ @Test
+ public void mockFinalMethodOfAndroidFrameworkClass() throws Exception {
+ IBinder fakeBinder = mock(IBinder.class);
+ PrintService mockService = mock(PrintService.class);
+
+ assertNull(mockService.onBind(new Intent()));
+
+ // PrintService#onBind is final
+ when(mockService.onBind(any(Intent.class))).thenReturn(fakeBinder);
+
+ assertSame(fakeBinder, mockService.onBind(new Intent()));
+ }
+
+ private final class FinalNonDefaultConstructorClass {
+ public FinalNonDefaultConstructorClass(int i) {
+ }
+
+ String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockNonDefaultConstructorClass() throws Exception {
+ FinalNonDefaultConstructorClass mock = mock(FinalNonDefaultConstructorClass.class);
+
+ assertNull(mock.returnA());
+ when(mock.returnA()).thenReturn("fakeA");
+
+ assertEquals("fakeA", mock.returnA());
+ }
+
+ private interface NonDefaultConstructorInterface {
+ String returnA();
+ }
+
+ @Test
+ public void mockNonDefaultConstructorInterface() throws Exception {
+ NonDefaultConstructorInterface mock = mock(NonDefaultConstructorInterface.class);
+
+ assertNull(mock.returnA());
+ when(mock.returnA()).thenReturn("fakeA");
+
+ assertEquals("fakeA", mock.returnA());
+ }
+
+ private static class SuperClass {
+ final String returnA() {
+ return "superA";
+ }
+
+ String returnB() {
+ return "superB";
+ }
+
+ String returnC() {
+ return "superC";
+ }
+ }
+
+ private static final class SubClass extends SuperClass {
+ String returnC() {
+ return "subC";
+ }
+ }
+
+ @Test
+ public void mockSubClass() throws Exception {
+ SubClass mocked = mock(SubClass.class);
+ SuperClass mockedSuper = mock(SuperClass.class);
+ SubClass nonMocked = new SubClass();
+ SuperClass nonMockedSuper = new SuperClass();
+
+ // Mock returns dummy value by default
+ assertNull(mocked.returnA());
+ assertNull(mocked.returnB());
+ assertNull(mocked.returnC());
+ assertNull(mockedSuper.returnA());
+ assertNull(mockedSuper.returnB());
+ assertNull(mockedSuper.returnC());
+
+ // Set fake values for mockedSuper
+ when(mockedSuper.returnA()).thenReturn("fakeA");
+ when(mockedSuper.returnB()).thenReturn("fakeB");
+ when(mockedSuper.returnC()).thenReturn("fakeC");
+
+ // mocked is unaffected
+ assertNull(mocked.returnA());
+ assertNull(mocked.returnB());
+ assertNull(mocked.returnC());
+
+ // Verify fake values of mockedSuper
+ assertEquals("fakeA", mockedSuper.returnA());
+ assertEquals("fakeB", mockedSuper.returnB());
+ assertEquals("fakeC", mockedSuper.returnC());
+
+ // Set fake values for mocked
+ when(mocked.returnA()).thenReturn("fake2A");
+ when(mocked.returnB()).thenReturn("fake2B");
+ when(mocked.returnC()).thenReturn("fake2C");
+
+ // Verify fake values of mocked
+ assertEquals("fake2A", mocked.returnA());
+ assertEquals("fake2B", mocked.returnB());
+ assertEquals("fake2C", mocked.returnC());
+
+ // non mocked instances are unaffected
+ assertEquals("superA", nonMocked.returnA());
+ assertEquals("superB", nonMocked.returnB());
+ assertEquals("subC", nonMocked.returnC());
+ assertEquals("superA", nonMockedSuper.returnA());
+ assertEquals("superB", nonMockedSuper.returnB());
+ assertEquals("superC", nonMockedSuper.returnC());
+ }
+
+ @Test
+ public void spySubClass() throws Exception {
+ SubClass spied = spy(SubClass.class);
+ SuperClass spiedSuper = spy(SuperClass.class);
+ SubClass nonSpied = new SubClass();
+ SuperClass nonSpiedSuper = new SuperClass();
+
+ // Spies call real method by default
+ assertEquals("superA", spied.returnA());
+ assertEquals("superB", spied.returnB());
+ assertEquals("subC", spied.returnC());
+ assertEquals("superA", spiedSuper.returnA());
+ assertEquals("superB", spiedSuper.returnB());
+ assertEquals("superC", spiedSuper.returnC());
+
+ // Set fake values for spiedSuper
+ when(spiedSuper.returnA()).thenReturn("fakeA");
+ when(spiedSuper.returnB()).thenReturn("fakeB");
+ when(spiedSuper.returnC()).thenReturn("fakeC");
+
+ // spied is unaffected
+ assertEquals("superA", spied.returnA());
+ assertEquals("superB", spied.returnB());
+ assertEquals("subC", spied.returnC());
+
+ // Verify fake values of spiedSuper
+ assertEquals("fakeA", spiedSuper.returnA());
+ assertEquals("fakeB", spiedSuper.returnB());
+ assertEquals("fakeC", spiedSuper.returnC());
+
+ // Set fake values for spied
+ when(spied.returnA()).thenReturn("fake2A");
+ when(spied.returnB()).thenReturn("fake2B");
+ when(spied.returnC()).thenReturn("fake2C");
+
+ // Verify fake values of spied
+ assertEquals("fake2A", spied.returnA());
+ assertEquals("fake2B", spied.returnB());
+ assertEquals("fake2C", spied.returnC());
+
+ // non spied instances are unaffected
+ assertEquals("superA", nonSpied.returnA());
+ assertEquals("superB", nonSpied.returnB());
+ assertEquals("subC", nonSpied.returnC());
+ assertEquals("superA", nonSpiedSuper.returnA());
+ assertEquals("superB", nonSpiedSuper.returnB());
+ assertEquals("superC", nonSpiedSuper.returnC());
+ }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java
new file mode 100644
index 0000000..aa828e5
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MockNonPublic.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.mockito.inline.tests;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+public class MockNonPublic {
+ private interface SingleMethodInterface {
+ String returnA();
+ }
+
+ private static <T extends Class> void mockSingleMethod(T clazz) {
+ SingleMethodInterface c = (SingleMethodInterface) mock(clazz);
+ assertNull(c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ }
+
+ private static <T extends Class> void spySingleMethod(T clazz) {
+ SingleMethodInterface c = (SingleMethodInterface) spy(clazz);
+ assertEquals("A", c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ }
+
+ private static <T extends SingleMethodInterface> void spyWrappedSingleMethod(T original) {
+ T c = spy(original);
+ assertEquals("A", c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+
+ // original is unaffected
+ assertEquals("A", original.returnA());
+ }
+
+ private interface DualMethodInterface {
+ String returnA();
+ String returnB();
+ }
+
+ private static <T extends Class> void mockDualMethod(T clazz) {
+ DualMethodInterface c = (DualMethodInterface) mock(clazz);
+ assertNull(c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+ }
+
+ private static <T extends Class> void spyDualMethod(T clazz) {
+ DualMethodInterface c = (DualMethodInterface) spy(clazz);
+ assertEquals("A", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+ }
+
+ private static <T extends DualMethodInterface> void spyWrappedDualMethod(T original) {
+ T c = spy(original);
+ assertEquals("A", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+
+ // original is unaffected
+ assertEquals("A", original.returnA());
+ assertEquals("B", original.returnB());
+ }
+
+ private static class PrivateClass implements SingleMethodInterface {
+ public String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockPrivateClass() {
+ mockSingleMethod(PrivateClass.class);
+ }
+
+ @Test
+ public void spyPrivateClass() {
+ spySingleMethod(PrivateClass.class);
+ }
+
+ @Test
+ public void spyWrappedPrivateClass() {
+ spyWrappedSingleMethod(new PrivateClass());
+ }
+
+ private interface PrivateInterface extends SingleMethodInterface {
+ String returnA();
+ }
+
+ @Test
+ public void mockPrivateInterface() {
+ mockSingleMethod(PrivateInterface.class);
+ }
+
+ private static class SubOfPrivateInterface implements PrivateInterface {
+ public String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockSubOfPrivateInterface() {
+ mockSingleMethod(SubOfPrivateInterface.class);
+ }
+
+ @Test
+ public void spySubOfPrivateInterface() {
+ spySingleMethod(SubOfPrivateInterface.class);
+ }
+
+ @Test
+ public void spyWrappedSubOfPrivateInterface() {
+ spyWrappedSingleMethod(new SubOfPrivateInterface());
+ }
+
+ private static abstract class PrivateAbstractClass implements DualMethodInterface {
+ public String returnA() {
+ return "A";
+ }
+
+ public abstract String returnB();
+ }
+
+ @Test
+ public void mockPrivateAbstractClass() {
+ mockDualMethod(PrivateAbstractClass.class);
+ }
+
+ private static class SubOfPrivateAbstractClass extends PrivateAbstractClass {
+ public String returnB() {
+ return "B";
+ }
+ }
+
+ @Test
+ public void mockSubOfPrivateAbstractClass() {
+ mockDualMethod(SubOfPrivateAbstractClass.class);
+ }
+
+ @Test
+ public void spySubOfPrivateAbstractClass() {
+ spyDualMethod(SubOfPrivateAbstractClass.class);
+ }
+
+ @Test
+ public void spyWrappedSubOfPrivateAbstractClass() {
+ spyWrappedDualMethod(new SubOfPrivateAbstractClass());
+ }
+
+ static class PackagePrivateClass implements SingleMethodInterface {
+ public String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockPackagePrivateClass() {
+ mockSingleMethod(PackagePrivateClass.class);
+ }
+
+ static abstract class PackagePrivateAbstractClass implements DualMethodInterface {
+ public String returnA() {
+ return "A";
+ }
+
+ public abstract String returnB();
+ }
+
+ @Test
+ public void mockPackagePrivateAbstractClass() {
+ mockDualMethod(PackagePrivateAbstractClass.class);
+ }
+
+ static class SubOfPackagePrivateAbstractClass extends PackagePrivateAbstractClass {
+ public String returnB() {
+ return "B";
+ }
+ }
+
+ @Test
+ public void mockSubOfPackagePrivateAbstractClass() {
+ mockDualMethod(SubOfPackagePrivateAbstractClass.class);
+ }
+
+ @Test
+ public void spySubOfPackagePrivateAbstractClass() {
+ spyDualMethod(SubOfPackagePrivateAbstractClass.class);
+ }
+
+ @Test
+ public void spyWrappedSubOfPackagePrivateAbstractClass() {
+ spyWrappedDualMethod(new SubOfPackagePrivateAbstractClass());
+ }
+
+ interface PackagePrivateInterface extends SingleMethodInterface {
+ String returnA();
+ }
+
+ @Test
+ public void mockPackagePrivateInterface() {
+ mockSingleMethod(PackagePrivateInterface.class);
+ }
+
+ static class SubOfPackagePrivateInterface implements PackagePrivateInterface {
+ public String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockSubOfPackagePrivateInterface() {
+ mockSingleMethod(SubOfPackagePrivateInterface.class);
+ }
+
+ @Test
+ public void spySubOfPackagePrivateInterface() {
+ spySingleMethod(SubOfPackagePrivateInterface.class);
+ }
+
+ @Test
+ public void spyWrappedSubOfPackagePrivateInterface() {
+ spyWrappedSingleMethod(new SubOfPackagePrivateInterface());
+ }
+
+ // Cannot implement SingleMethodInterface as returnA would have to be public
+ public static class ClassWithPackagePrivateMethod {
+ String returnA() {
+ return "A";
+ }
+ }
+
+ @Test
+ public void mockClassWithPackagePrivateMethod() {
+ ClassWithPackagePrivateMethod c = mock(ClassWithPackagePrivateMethod.class);
+ assertNull(c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ }
+
+ @Test
+ public void spyClassWithPackagePrivateMethod() {
+ ClassWithPackagePrivateMethod c = spy(ClassWithPackagePrivateMethod.class);
+ assertEquals("A", c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ }
+
+ @Test
+ public void spyWrappedClassWithPackagePrivateMethod() {
+ ClassWithPackagePrivateMethod original = new ClassWithPackagePrivateMethod();
+ ClassWithPackagePrivateMethod c = spy(original);
+ assertEquals("A", c.returnA());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+
+ // original is unaffected
+ assertEquals("A", original.returnA());
+ }
+
+ // Cannot implement DualMethodInterface as returnA/returnB would have to be public
+ public static abstract class AbstractClassWithPackagePrivateMethod {
+ String returnA() {
+ return "A";
+ }
+
+ abstract String returnB();
+ }
+
+ @Test
+ public void mockAbstractClassWithPackagePrivateMethod() {
+ AbstractClassWithPackagePrivateMethod c = mock(AbstractClassWithPackagePrivateMethod.class);
+ assertNull(c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+ }
+
+ public static class SubOfAbstractClassWithPackagePrivateMethod extends
+ AbstractClassWithPackagePrivateMethod {
+ String returnB() {
+ return "B";
+ }
+ }
+
+ @Test
+ public void mockSubOfAbstractClassWithPackagePrivateMethod() {
+ SubOfAbstractClassWithPackagePrivateMethod c = mock
+ (SubOfAbstractClassWithPackagePrivateMethod.class);
+ assertNull(c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertNull(c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+ }
+
+ @Test
+ public void spySubOfAbstractClassWithPackagePrivateMethod() {
+ SubOfAbstractClassWithPackagePrivateMethod c = spy
+ (SubOfAbstractClassWithPackagePrivateMethod.class);
+ assertEquals("A", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+ }
+
+ @Test
+ public void spyWrappedSubOfAbstractClassWithPackagePrivateMethod() {
+ SubOfAbstractClassWithPackagePrivateMethod original = new
+ SubOfAbstractClassWithPackagePrivateMethod();
+ SubOfAbstractClassWithPackagePrivateMethod c = spy(original);
+ assertEquals("A", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnA()).thenReturn("fakeA");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("B", c.returnB());
+
+ when(c.returnB()).thenReturn("fakeB");
+ assertEquals("fakeA", c.returnA());
+ assertEquals("fakeB", c.returnB());
+
+ // original is unaffected
+ assertEquals("A", original.returnA());
+ assertEquals("B", original.returnB());
+ }
+}
diff --git a/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java
new file mode 100644
index 0000000..d66b128
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/androidTest/java/com/android/dx/mockito/inline/tests/MultipleJvmtiAgentsInterference.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dx.mockito.inline.tests;
+
+import android.os.Build;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import dalvik.system.BaseDexClassLoader;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.mock;
+
+public class MultipleJvmtiAgentsInterference {
+ private static final String AGENT_LIB_NAME = "multiplejvmtiagentsinterferenceagent";
+
+ public class TestClass {
+ public String returnA() {
+ return "A";
+ }
+ }
+
+ @BeforeClass
+ public static void installTestAgent() throws Exception {
+ // TODO (moltmann@google.com): Replace with proper check for >= P
+ assumeTrue(Build.VERSION.CODENAME.equals("P"));
+
+ // Currently Debug.attachJvmtiAgent requires a file in the right directory
+ File copiedAgent = File.createTempFile("testagent", ".so");
+ copiedAgent.deleteOnExit();
+
+ try (InputStream is = new FileInputStream(((BaseDexClassLoader)
+ MultipleJvmtiAgentsInterference.class.getClassLoader()).findLibrary
+ (AGENT_LIB_NAME))) {
+ try (OutputStream os = new FileOutputStream(copiedAgent)) {
+ byte[] buffer = new byte[64 * 1024];
+
+ while (true) {
+ int numRead = is.read(buffer);
+ if (numRead == -1) {
+ break;
+ }
+ os.write(buffer, 0, numRead);
+ }
+ }
+ }
+
+ // TODO (moltmann@google.com): Replace with regular method call once the API becomes public
+ Class.forName("android.os.Debug").getMethod("attachJvmtiAgent", String.class, String
+ .class).invoke(null, copiedAgent.getAbsolutePath(), null);
+ }
+
+ @Test
+ public void otherAgentTransformsWhileMocking() {
+ TestClass t = mock(TestClass.class);
+
+ assertNull(t.returnA());
+
+ // Unrelated class re-transform does not affect mocking
+ nativeRetransformClasses(new Class<?>[]{MultipleJvmtiAgentsInterference.class});
+ assertNull(t.returnA());
+
+ // Re-transform of classes that are mocked does not affect mocking
+ nativeRetransformClasses(new Class<?>[]{TestClass.class});
+ assertNull(t.returnA());
+ }
+
+ private native int nativeRetransformClasses(Class<?>[] classes);
+}
diff --git a/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc b/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc
new file mode 100644
index 0000000..a293fe7
--- /dev/null
+++ b/dexmaker-mockito-inline-tests/src/main/jni/multiplejvmtiagentsinterferenceagent/agent.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#include <cstring>
+#include <cstdlib>
+#include <sstream>
+
+#include "jvmti.h"
+
+#include <dex_ir.h>
+#include <writer.h>
+#include <reader.h>
+
+using namespace dex;
+
+namespace com_android_dx_mockito_inline_tests {
+ static jvmtiEnv *localJvmtiEnv;
+
+ // Converts a class name to a type descriptor
+ // (ex. "java.lang.String" to "Ljava/lang/String;")
+ static std::string
+ ClassNameToDescriptor(const char* class_name) {
+ std::stringstream ss;
+ ss << "L";
+ for (auto p = class_name; *p != '\0'; ++p) {
+ ss << (*p == '.' ? '/' : *p);
+ }
+ ss << ";";
+ return ss.str();
+ }
+
+ static void
+ Transform(jvmtiEnv *jvmti_env,
+ JNIEnv *env,
+ jclass classBeingRedefined,
+ jobject loader,
+ const char *name,
+ jobject protectionDomain,
+ jint classDataLen,
+ const unsigned char *classData,
+ jint *newClassDataLen,
+ unsigned char **newClassData) {
+ // Isolate byte code of class class. This is needed as Android usually gives us more
+ // than the class we need.
+ // Then just return the isolated byte code without modification.
+ Reader reader(classData, (size_t) classDataLen);
+
+ u4 index = reader.FindClassIndex(ClassNameToDescriptor(name).c_str());
+ reader.CreateClassIr(index);
+ std::shared_ptr<ir::DexFile> ir = reader.GetIr();
+
+ class Allocator : public Writer::Allocator {
+ jvmtiEnv *jvmti_env;
+
+ public:
+ Allocator(jvmtiEnv *jvmti_env) : Writer::Allocator(), jvmti_env(jvmti_env) {
+ }
+
+ virtual void *Allocate(size_t size) {
+ unsigned char *mem;
+ jvmti_env->Allocate(size, &mem);
+ return mem;
+ }
+
+ virtual void Free(void *ptr) { ::free(ptr); }
+ };
+
+ Allocator allocator(jvmti_env);
+ Writer writer(ir);
+ size_t newClassLen;
+ *newClassData = writer.CreateImage(&allocator, &newClassLen);
+ *newClassDataLen = (jint) newClassLen;
+ }
+
+ // Initializes the agent
+ extern "C" jint Agent_OnAttach(JavaVM *vm,
+ char *options,
+ void *reserved) {
+ jint jvmError = vm->GetEnv(reinterpret_cast<void **>(&localJvmtiEnv), JVMTI_VERSION_1_2);
+ if (jvmError != JNI_OK) {
+ return jvmError;
+ }
+
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.can_retransform_classes = 1;
+
+ jvmtiError error = localJvmtiEnv->AddCapabilities(&caps);
+ if (error != JVMTI_ERROR_NONE) {
+ return error;
+ }
+
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = Transform;
+
+ error = localJvmtiEnv->SetEventCallbacks(&cb, sizeof(cb));
+ if (error != JVMTI_ERROR_NONE) {
+ return error;
+ }
+
+ error = localJvmtiEnv->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ return error;
+ }
+
+ return JVMTI_ERROR_NONE;
+ }
+
+
+ // Triggers retransformation of classes via this file's Transform method
+ extern "C" JNIEXPORT jint JNICALL
+ Java_com_android_dx_mockito_inline_tests_MultipleJvmtiAgentsInterference_nativeRetransformClasses(
+ JNIEnv *env,
+ jobject thiz,
+ jobjectArray classes) {
+ jsize numTransformedClasses = env->GetArrayLength(classes);
+ jclass *transformedClasses = (jclass *) malloc(numTransformedClasses * sizeof(jclass));
+ for (int i = 0; i < numTransformedClasses; i++) {
+ transformedClasses[i] = (jclass) env->NewGlobalRef(env->GetObjectArrayElement(classes,
+ i));
+ }
+
+ jvmtiError error = localJvmtiEnv->RetransformClasses(numTransformedClasses,
+ transformedClasses);
+
+ for (int i = 0; i < numTransformedClasses; i++) {
+ env->DeleteGlobalRef(transformedClasses[i]);
+ }
+ free(transformedClasses);
+
+ return error;
+ }
+} \ No newline at end of file