Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when real hashCode() is called in mocktionsession #170

Merged
merged 2 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.android.dx.mockito.inline.extended.tests;

import android.app.PendingIntent;
import android.content.ContentResolver;
import android.provider.Settings;

Expand All @@ -33,8 +34,9 @@
import static org.mockito.ArgumentMatchers.eq;

public class StaticMockitoSession {

@Test
public void strictUnnecessaryStubbing() throws Exception {
public void strictUnnecessaryStubbing() {
MockitoSession session = mockitoSession().spyStatic(Settings.Global.class).startMocking();

// Set up unnecessary stubbing
Expand All @@ -51,7 +53,7 @@ public void strictUnnecessaryStubbing() throws Exception {
}

@Test
public void lenientUnnecessaryStubbing() throws Exception {
public void lenientUnnecessaryStubbing() {
MockitoSession session = mockitoSession().strictness(Strictness.LENIENT)
.spyStatic(Settings.Global.class).startMocking();

Expand All @@ -61,4 +63,22 @@ public void lenientUnnecessaryStubbing() throws Exception {

session.finishMocking();
}

@Test
public void spyStatic() {
mockitoSession()
.initMocks(this)
.spyStatic(PendingIntent.class)
.startMocking()
.finishMocking();
}

@Test
public void mockStatic() {
mockitoSession()
.initMocks(this)
.mockStatic(PendingIntent.class)
.startMocking()
.finishMocking();
}
}
2 changes: 1 addition & 1 deletion dexmaker-mockito-inline-extended/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ android {
}

defaultConfig {
minSdkVersion 1
minSdkVersion 9
targetSdkVersion 28
versionName VERSION_NAME
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public final class InlineStaticMockMaker implements MockMaker {
* are modified, some are not. This list helps the {@link MockMethodAdvice} help figure out if a
* object's method calls should be intercepted.
*/
private final HashMap<Object, InvocationHandlerAdapter> markerToHandler = new HashMap<>();
private final Map<Object, InvocationHandlerAdapter> markerToHandler = new MarkerToHandlerMap();
private final Map<Class, Object> classToMarker = new HashMap<>();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.android.dx.mockito.inline;

import org.mockito.invocation.MockHandler;
import org.mockito.mock.MockCreationSettings;

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* A map for mock marker object -> {@link InvocationHandlerAdapter} but
* does not use the mock marker object as the key directly.
* The problem of not doing so is that the object's real hashCode() and equals() =
* methods will be invoked during
* {@link InlineStaticMockMaker#createMock(MockCreationSettings, MockHandler)}. This poses a
* potential test runtime error depending on the object's hashCode() implementation
*/
class MarkerToHandlerMap implements Map<Object, InvocationHandlerAdapter> {

private final Map<MockMarkerKey, InvocationHandlerAdapter> markerToHandler = new HashMap<>();

@Override
public int size() {
return markerToHandler.size();
}

@Override
public boolean isEmpty() {
return markerToHandler.isEmpty();
}

@Override
public boolean containsKey(Object key) {
return markerToHandler.containsKey(new MockMarkerKey(key));
}

@Override
public boolean containsValue(Object value) {
return markerToHandler.containsValue(value);
}

@Override
public InvocationHandlerAdapter get(Object key) {
return markerToHandler.get(new MockMarkerKey(key));
}

@Override
public InvocationHandlerAdapter put(Object key, InvocationHandlerAdapter value) {
return markerToHandler.put(new MockMarkerKey(key), value);
}

@Override
public InvocationHandlerAdapter remove(Object key) {
return markerToHandler.remove(new MockMarkerKey(key));
}

@Override
public void putAll(Map<?, ? extends InvocationHandlerAdapter> m) {
for (Entry<?, ? extends InvocationHandlerAdapter> entry : m.entrySet()) {
put(new MockMarkerKey(entry.getKey()), entry.getValue());
}
}

@Override
public void clear() {
markerToHandler.clear();
}

@Override
public Set<Object> keySet() {
Set<Object> set = new HashSet<>(entrySet().size());
for (MockMarkerKey key : markerToHandler.keySet()) {
set.add(key.mockMarker);
}
return set;
}

@Override
public Collection<InvocationHandlerAdapter> values() {
return markerToHandler.values();
}

@Override
public Set<Entry<Object, InvocationHandlerAdapter>> entrySet() {
Set<Entry<Object, InvocationHandlerAdapter>> set = new HashSet<>(entrySet().size());
for (Entry<MockMarkerKey, InvocationHandlerAdapter> entry : markerToHandler.entrySet()) {
set.add(new AbstractMap.SimpleImmutableEntry<>(entry.getKey().mockMarker, entry.getValue()));
}
return set;
}

private static class MockMarkerKey {

private Object mockMarker;
chao2zhang marked this conversation as resolved.
Show resolved Hide resolved

public MockMarkerKey(Object mockMarker) {
this.mockMarker = mockMarker;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MockMarkerKey mockMarkerKey = (MockMarkerKey) o;

return mockMarker == mockMarkerKey.mockMarker;
}

@Override
public int hashCode() {
return System.identityHashCode(mockMarker);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class MemoryLeaks {
private static final int ARRAY_LENGTH = 1 << 20; // 4 MB

@Test
public void callMethodWithMocksCycalically() {
public void callMethodWithMocksCyclically() {
for (int i = 0; i < 100; ++i) {
final A a = mock(A.class);
a.largeArray = new int[ARRAY_LENGTH];
Expand Down
2 changes: 1 addition & 1 deletion dexmaker-mockito-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ android {
}

defaultConfig {
minSdkVersion 8
minSdkVersion 14
targetSdkVersion 28
versionName VERSION_NAME

Expand Down
2 changes: 1 addition & 1 deletion dexmaker-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ android {

defaultConfig {
applicationId 'com.linkedin.dexmaker'
minSdkVersion 8
minSdkVersion 14
targetSdkVersion 28
versionCode 1
versionName VERSION_NAME
Expand Down