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

Support customized implementation method name #8841

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -21,4 +21,18 @@

/** The annotated shadow method will be invoked only for the specified SDK or lesser. */
int maxSdk() default DEFAULT_SDK;

/**
* The implemented method name.
*
* <p>Sometimes internal methods return different types for different SDKs. It's safe because
* these methods are internal/private methods, not public methods. To support different return
* types of a method for different SDKs, we often use looseSignature method, although all return
* types are common types like bool and int. This field/property can be used to fix this issue by
* using different real methods for different SDKs.
*
* @return The expected implemented method name. If it is empty/null, the Robolectric will uses
* the method's name that marked by @Implementation as the implemented method name.
*/
String methodName() default "";
}
Expand Up @@ -534,7 +534,14 @@ private static String cleanMethodName(ExecutableElement methodElement) {
} else if (STATIC_INITIALIZER_METHOD_NAME.equals(name)) {
return "<clinit>";
} else {
return name;
Implementation implementation = methodElement.getAnnotation(Implementation.class);
String methodName = implementation == null ? "" : implementation.methodName();
methodName = methodName == null ? "" : methodName.trim();
if (methodName.isEmpty()) {
return name;
} else {
return methodName;
}
}
}

Expand Down
Expand Up @@ -22,6 +22,7 @@
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Priority;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.ReflectorObject;
import org.robolectric.sandbox.ShadowMatcher;
Expand Down Expand Up @@ -308,6 +309,7 @@ private Method findShadowMethod(
Class<?> shadowClass) {
Method method =
findShadowMethodDeclaredOnClass(shadowClass, name, types, shadowInfo.looseSignatures);

if (method != null) {
return method;
} else {
Expand All @@ -332,7 +334,9 @@ private Method findShadowMethod(
private Method findShadowMethodDeclaredOnClass(
Class<?> shadowClass, String methodName, Class<?>[] paramClasses, boolean looseSignatures) {
Method foundMethod = null;
for (Method method : shadowClass.getDeclaredMethods()) {
// Try to find shadow method with exact method name and looseSignature.
Method[] methods = shadowClass.getDeclaredMethods();
for (Method method : methods) {
if (!method.getName().equals(methodName)
|| method.getParameterCount() != paramClasses.length) {
continue;
Expand All @@ -349,6 +353,7 @@ private Method findShadowMethodDeclaredOnClass(
foundMethod = method;
break;
}

if (looseSignatures) {
boolean allParameterTypesAreObject = true;
for (Class<?> paramClass : method.getParameterTypes()) {
Expand All @@ -364,6 +369,24 @@ private Method findShadowMethodDeclaredOnClass(
}
}

if (foundMethod == null) {
// Try to find shadow method with Implementation#methodName's mapping name
for (Method method : methods) {
Implementation implementation = method.getAnnotation(Implementation.class);
if (implementation == null) {
continue;
}
String mappedMethodName = implementation.methodName().trim();
if (mappedMethodName.isEmpty() || !mappedMethodName.equals(methodName)) {
continue;
}
if (Arrays.equals(method.getParameterTypes(), paramClasses)) {
foundMethod = method;
break;
}
}
}

if (foundMethod != null) {
foundMethod.setAccessible(true);
return foundMethod;
Expand Down
Expand Up @@ -380,21 +380,21 @@ protected boolean setName(String name) {
* Needs looseSignatures because in Android T the return value of this method was changed from
* bool to int.
*/
@Implementation
protected Object setScanMode(int scanMode) {
boolean result = true;
if (scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE
&& scanMode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
&& scanMode != BluetoothAdapter.SCAN_MODE_NONE) {
result = false;
}

@Implementation(maxSdk = S_V2)
protected boolean setScanMode(int scanMode) {
boolean result =
scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE
|| scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE
|| scanMode == BluetoothAdapter.SCAN_MODE_NONE;
this.scanMode = scanMode;
if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.TIRAMISU) {
return result ? BluetoothStatusCodes.SUCCESS : BluetoothStatusCodes.ERROR_UNKNOWN;
} else {
return result;
}
return result;
}

@Implementation(minSdk = TIRAMISU, methodName = "setScanMode")
protected int setScanModeFromT(int scanMode) {
return setScanMode(scanMode)
? BluetoothStatusCodes.SUCCESS
: BluetoothStatusCodes.ERROR_UNKNOWN;
}

@Implementation(maxSdk = Q)
Expand Down