Commit 615ba2ba authored by Ben Joyce's avatar Ben Joyce Committed by Commit Bot

Suppress warnings from robolectric.

The files are identical to the original, except it uses robolectric's
Logger instead of javax.annotation.processing.Messager so that the
messages are only seen when the logger is turned on.

Removes weapons such as:
warning: Couldn't find shadowed type for org.robolectric.shadows.ShadowPathParser.ShadowPathData
warning: Couldn't find shadowed type for org.robolectric.shadows.ShadowSharedPreferences.ShadowSharedPreferencesEditorImpl

...
[12/179] ACTION //third_party/robolectric:shadows_multidex_java__compile_java(//build/toolchain/android:android_clang_arm64)
../../third_party/robolectric/robolectric/shadows/multidex/src/main/java/org/robolectric/shadows/multidex/ShadowMultiDex.java:12: warning: No such class android.support.multidex.MultiDex for SDKs 19-19/21-21/25-29
  protected static void install(Context context)
...


Bug: 967664
Change-Id: I5bcddef368a8e3f527bdb03f445f7174118c7ed0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2209632
Commit-Queue: benjamin joyce <bjoyce@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarPeter Wen <wnwen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#770686}
parent 6ff61ddc
......@@ -95,6 +95,7 @@ _BANNED_JAVA_IMPORTS = (
'net/android/javatests/src/org/chromium/net/'
'AndroidProxySelectorTest.java',
'components/cronet/',
'third_party/robolectric/local/',
),
),
)
......
......@@ -198,10 +198,14 @@ java_library("robolectric_annotations_java") {
}
java_annotation_processor("robolectric_processor") {
bypass_platform_checks = true
testonly = true
enable_bytecode_checks = false
deps = [
":robolectric_annotations_java",
# Added to use logger with local file.
":robolectric_utils_java",
":sdk_list_txt",
"//third_party/android_deps:com_google_code_gson_gson_java",
"//third_party/android_deps:com_google_guava_guava_java",
......@@ -218,6 +222,8 @@ java_annotation_processor("robolectric_processor") {
provider_configurations = [ "local/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor" ]
main_class = "org.robolectric.annotation.processing.RobolectricProcessor"
sources = [
"local/processor/src/main/java/org/robolectric/annotation/processing/generator/JavadocJsonGenerator.java",
"local/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/DocumentedElement.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/DocumentedMethod.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/DocumentedPackage.java",
......@@ -226,7 +232,9 @@ java_annotation_processor("robolectric_processor") {
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/RobolectricModel.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/RobolectricProcessor.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/Generator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/JavadocJsonGenerator.java",
# Provided locally to supress output warnings.
# "robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/JavadocJsonGenerator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/ServiceLoaderGenerator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/ShadowProviderGenerator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/generator/package-info.java",
......@@ -236,7 +244,9 @@ java_annotation_processor("robolectric_processor") {
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/ImplementsValidator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/RealObjectValidator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/ResetterValidator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java",
# Provided locally to supress output warnings.
# "robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/Validator.java",
"robolectric/processor/src/main/java/org/robolectric/annotation/processing/validator/package-info.java",
]
......
......@@ -17,6 +17,8 @@ Local Modifications:
for our own implementation of AsyncTask
- Added DefaultSdkProvider as a local file with Jellybean references removed.
- Removed non-compiling JDK9 class from UnsafeAccess.java.
- Changed SdkStore.java and JavadocJsonGenerator.java to put warnings in logger
rather than stout.
How To Update:
- Visit the migration guide to see which APIs changed and need updating.
http://robolectric.org/migrating/
......
package org.robolectric.annotation.processing.generator;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.robolectric.annotation.processing.DocumentedPackage;
import org.robolectric.annotation.processing.DocumentedType;
import org.robolectric.annotation.processing.RobolectricModel;
import org.robolectric.annotation.processing.RobolectricModel.ShadowInfo;
import org.robolectric.util.Logger;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
public class JavadocJsonGenerator extends Generator {
private final RobolectricModel model;
private final Messager messager;
private final Gson gson;
private final File jsonDocsDir;
public JavadocJsonGenerator(
RobolectricModel model, ProcessingEnvironment environment, File jsonDocsDir) {
super();
this.model = model;
this.messager = environment.getMessager();
gson = new GsonBuilder().setPrettyPrinting().create();
this.jsonDocsDir = jsonDocsDir;
}
@Override
public void generate() {
Map<String, String> shadowedTypes = new HashMap<>();
for (ShadowInfo entry : model.getVisibleShadowTypes()) {
shadowedTypes.put(entry.getShadowName().replace('$', '.'), entry.getActualName());
}
for (Map.Entry<String, String> entry : model.getExtraShadowTypes().entrySet()) {
String shadowType = entry.getKey().replace('$', '.');
String shadowedType = entry.getValue();
shadowedTypes.put(shadowType, shadowedType);
}
for (DocumentedPackage documentedPackage : model.getDocumentedPackages()) {
for (DocumentedType documentedType : documentedPackage.getDocumentedTypes()) {
String shadowedType = shadowedTypes.get(documentedType.getName());
if (shadowedType == null) {
Logger.strict("Couldn't find shadowed type for " + documentedType.getName());
} else {
writeJson(documentedType, new File(jsonDocsDir, shadowedType + ".json"));
}
}
}
}
private void writeJson(Object object, File file) {
try {
file.getParentFile().mkdirs();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
gson.toJson(object, writer);
}
} catch (IOException e) {
messager.printMessage(Diagnostic.Kind.ERROR, "Failed to write javadoc JSON file: " + e);
throw new RuntimeException(e);
}
}
}
package org.robolectric.annotation.processing.validator;
import static org.robolectric.annotation.Implementation.DEFAULT_SDK;
import static org.robolectric.annotation.processing.validator.ImplementsValidator.CONSTRUCTOR_METHOD_NAME;
import static org.robolectric.annotation.processing.validator.ImplementsValidator.STATIC_INITIALIZER_METHOD_NAME;
import static org.robolectric.annotation.processing.validator.ImplementsValidator.getClassFQName;
import com.sun.tools.javac.code.Type.ArrayType;
import com.sun.tools.javac.code.Type.TypeVar;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.robolectric.annotation.Implementation;
import org.robolectric.util.Logger;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
public class SdkStore {
private final Set<Sdk> sdks = new TreeSet<>();
private boolean loaded = false;
private final String sdksFile;
public SdkStore(String sdksFile) {
this.sdksFile = sdksFile;
}
List<Sdk> sdksMatching(Implementation implementation, int classMinSdk, int classMaxSdk) {
loadSdksOnce();
int minSdk = implementation == null ? DEFAULT_SDK : implementation.minSdk();
if (minSdk == DEFAULT_SDK) {
minSdk = 0;
}
if (classMinSdk > minSdk) {
minSdk = classMinSdk;
}
int maxSdk = implementation == null ? -1 : implementation.maxSdk();
if (maxSdk == -1) {
maxSdk = Integer.MAX_VALUE;
}
if (classMaxSdk != -1 && classMaxSdk < maxSdk) {
maxSdk = classMaxSdk;
}
List<Sdk> matchingSdks = new ArrayList<>();
for (Sdk sdk : sdks) {
Integer sdkInt = sdk.sdkInt;
if (sdkInt >= minSdk && sdkInt <= maxSdk) {
matchingSdks.add(sdk);
}
}
return matchingSdks;
}
private synchronized void loadSdksOnce() {
if (!loaded) {
sdks.addAll(loadFromSdksFile(sdksFile));
loaded = true;
}
}
private static List<Sdk> loadFromSdksFile(String resourceFileName) {
try (InputStream resIn = SdkStore.class.getResourceAsStream(resourceFileName)) {
if (resIn == null) {
throw new RuntimeException("no such resource " + resourceFileName);
}
BufferedReader in =
new BufferedReader(new InputStreamReader(resIn, Charset.defaultCharset()));
List<Sdk> sdks = new ArrayList<>();
String line;
while ((line = in.readLine()) != null) {
if (!line.startsWith("#")) {
sdks.add(new Sdk(line));
}
}
return sdks;
} catch (IOException e) {
throw new RuntimeException("failed reading " + resourceFileName, e);
}
}
private static String canonicalize(TypeMirror typeMirror) {
if (typeMirror instanceof TypeVar) {
return ((TypeVar) typeMirror).getUpperBound().toString();
} else if (typeMirror instanceof ArrayType) {
return canonicalize(((ArrayType) typeMirror).elemtype) + "[]";
} else {
return typeMirror.toString();
}
}
private static String typeWithoutGenerics(String paramType) {
return paramType.replaceAll("<.*", "");
}
static class Sdk implements Comparable<Sdk> {
private static final ClassInfo NULL_CLASS_INFO = new ClassInfo();
private final String path;
private final JarFile jarFile;
final int sdkInt;
private final Map<String, ClassInfo> classInfos = new HashMap<>();
private static File tempDir;
Sdk(String path) {
this.path = path;
this.jarFile = ensureJar();
this.sdkInt = readSdkInt();
}
/**
* Matches an `@Implementation` method against the framework method for this SDK.
*
* @param sdkClassElem the framework class being shadowed
* @param methodElement the `@Implementation` method declaration to check
* @param looseSignatures if `true`, also match any framework method with the same class,
* name, return type, and arity of parameters.
* @return a string describing any problems with this method, or `null` if it checks out.
*/
public String verifyMethod(TypeElement sdkClassElem, ExecutableElement methodElement,
boolean looseSignatures) {
String className = getClassFQName(sdkClassElem);
ClassInfo classInfo = getClassInfo(className);
if (classInfo == null) {
// Supresses a lot of output.
Logger.strict("No such class " + className);
return null;
}
MethodExtraInfo sdkMethod = classInfo.findMethod(methodElement, looseSignatures);
if (sdkMethod == null) {
return "No such method in " + className;
}
MethodExtraInfo implMethod = new MethodExtraInfo(methodElement);
if (!sdkMethod.equals(implMethod)
&& !suppressWarnings(methodElement, "robolectric.ShadowReturnTypeMismatch")) {
if (implMethod.isStatic != sdkMethod.isStatic) {
return "@Implementation for " + methodElement.getSimpleName() + " is "
+ (implMethod.isStatic ? "static" : "not static")
+ " unlike the SDK method";
}
if (!implMethod.returnType.equals(sdkMethod.returnType)) {
if ((looseSignatures && typeIsOkForLooseSignatures(implMethod, sdkMethod))
|| (looseSignatures
&& implMethod.returnType.equals("java.lang.Object[]"))
// Number is allowed for int or long return types
|| typeIsNumeric(sdkMethod, implMethod)) {
return null;
} else {
return "@Implementation for " + methodElement.getSimpleName()
+ " has a return type of " + implMethod.returnType + ", not "
+ sdkMethod.returnType + " as in the SDK method";
}
}
}
return null;
}
private boolean suppressWarnings(ExecutableElement methodElement, String warningName) {
SuppressWarnings[] suppressWarnings =
methodElement.getAnnotationsByType(SuppressWarnings.class);
for (SuppressWarnings suppression : suppressWarnings) {
for (String name : suppression.value()) {
if (warningName.equals(name)) {
return true;
}
}
}
return false;
}
private boolean typeIsNumeric(MethodExtraInfo sdkMethod, MethodExtraInfo implMethod) {
return implMethod.returnType.equals("java.lang.Number")
&& isNumericType(sdkMethod.returnType);
}
private boolean typeIsOkForLooseSignatures(
MethodExtraInfo implMethod, MethodExtraInfo sdkMethod) {
return
// loose signatures allow a return type of Object...
implMethod.returnType.equals("java.lang.Object")
// or Object[] for arrays...
|| (implMethod.returnType.equals("java.lang.Object[]")
&& sdkMethod.returnType.endsWith("[]"));
}
private boolean isNumericType(String type) {
return type.equals("int") || type.equals("long");
}
/**
* Load and analyze bytecode for the specified class, with caching.
*
* @param name the name of the class to analyze
* @return information about the methods in the specified class
*/
private synchronized ClassInfo getClassInfo(String name) {
ClassInfo classInfo = classInfos.get(name);
if (classInfo == null) {
ClassNode classNode = loadClassNode(name);
if (classNode == null) {
classInfos.put(name, NULL_CLASS_INFO);
} else {
classInfo = new ClassInfo(classNode);
classInfos.put(name, classInfo);
}
}
return classInfo == NULL_CLASS_INFO ? null : classInfo;
}
/**
* Determine the API level for this SDK jar by inspecting its `build.prop` file.
*
* If the `ro.build.version.codename` value isn't `REL`, this is an unreleased SDK, which
* is represented as `10000` (see {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}.
*
* @return the API level, or `10000`
*/
private int readSdkInt() {
Properties properties = new Properties();
try (InputStream inputStream =
jarFile.getInputStream(jarFile.getJarEntry("build.prop"))) {
properties.load(inputStream);
} catch (IOException e) {
throw new RuntimeException("failed to read build.prop from " + path);
}
int sdkInt = Integer.parseInt(properties.getProperty("ro.build.version.sdk"));
String codename = properties.getProperty("ro.build.version.codename");
if (!"REL".equals(codename)) {
sdkInt = 10000;
}
return sdkInt;
}
private JarFile ensureJar() {
try {
if (path.startsWith("classpath:")) {
return new JarFile(
copyResourceToFile(URI.create(path).getSchemeSpecificPart()));
} else {
return new JarFile(path);
}
} catch (IOException e) {
throw new RuntimeException("failed to open SDK " + sdkInt + " at " + path, e);
}
}
private static File copyResourceToFile(String resourcePath) throws IOException {
if (tempDir == null) {
File tempFile = File.createTempFile("prefix", "suffix");
tempFile.deleteOnExit();
tempDir = tempFile.getParentFile();
}
InputStream jarIn = SdkStore.class.getClassLoader().getResourceAsStream(resourcePath);
if (jarIn == null) {
throw new RuntimeException("SDK " + resourcePath + " not found");
}
File outFile = new File(tempDir, new File(resourcePath).getName());
outFile.deleteOnExit();
try (FileOutputStream jarOut = new FileOutputStream(outFile)) {
byte[] buffer = new byte[4096];
int len;
while ((len = jarIn.read(buffer)) != -1) {
jarOut.write(buffer, 0, len);
}
}
return outFile;
}
private ClassNode loadClassNode(String name) {
String classFileName = name.replace('.', '/') + ".class";
ZipEntry entry = jarFile.getEntry(classFileName);
if (entry == null) {
return null;
}
try (InputStream inputStream = jarFile.getInputStream(entry)) {
ClassReader classReader = new ClassReader(inputStream);
ClassNode classNode = new ClassNode();
classReader.accept(classNode,
ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return classNode;
} catch (IOException e) {
throw new RuntimeException("failed to analyze " + classFileName + " in " + path, e);
}
}
@Override
public int compareTo(Sdk sdk) {
return sdk.sdkInt - sdkInt;
}
}
static class ClassInfo {
private final Map<MethodInfo, MethodExtraInfo> methods = new HashMap<>();
private final Map<MethodInfo, MethodExtraInfo> erasedParamTypesMethods = new HashMap<>();
private ClassInfo() {}
public ClassInfo(ClassNode classNode) {
for (Object aMethod : classNode.methods) {
MethodNode method = ((MethodNode) aMethod);
MethodInfo methodInfo = new MethodInfo(method);
MethodExtraInfo methodExtraInfo = new MethodExtraInfo(method);
methods.put(methodInfo, methodExtraInfo);
erasedParamTypesMethods.put(methodInfo.erase(), methodExtraInfo);
}
}
MethodExtraInfo findMethod(ExecutableElement methodElement, boolean looseSignatures) {
MethodInfo methodInfo = new MethodInfo(methodElement);
MethodExtraInfo methodExtraInfo = methods.get(methodInfo);
if (looseSignatures && methodExtraInfo == null) {
methodExtraInfo = erasedParamTypesMethods.get(methodInfo.erase());
}
return methodExtraInfo;
}
}
static class MethodInfo {
private final String name;
private final List<String> paramTypes = new ArrayList<>();
/** Create a MethodInfo from ASM in-memory representation (an Android framework method). */
public MethodInfo(MethodNode method) {
this.name = method.name;
for (Type type : Type.getArgumentTypes(method.desc)) {
paramTypes.add(normalize(type));
}
}
/** Create a MethodInfo with all Object params (for looseSignatures=true). */
public MethodInfo(String name, int size) {
this.name = name;
for (int i = 0; i < size; i++) {
paramTypes.add("java.lang.Object");
}
}
/** Create a MethodInfo from AST (an @Implementation method in a shadow class). */
public MethodInfo(ExecutableElement methodElement) {
this.name = cleanMethodName(methodElement);
for (VariableElement variableElement : methodElement.getParameters()) {
TypeMirror varTypeMirror = variableElement.asType();
String paramType = canonicalize(varTypeMirror);
String paramTypeWithoutGenerics = typeWithoutGenerics(paramType);
paramTypes.add(paramTypeWithoutGenerics);
}
}
private String cleanMethodName(ExecutableElement methodElement) {
String name = methodElement.getSimpleName().toString();
if (CONSTRUCTOR_METHOD_NAME.equals(name)) {
return "<init>";
} else if (STATIC_INITIALIZER_METHOD_NAME.equals(name)) {
return "<clinit>";
} else {
return name;
}
}
public MethodInfo erase() {
return new MethodInfo(name, paramTypes.size());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MethodInfo that = (MethodInfo) o;
return Objects.equals(name, that.name) && Objects.equals(paramTypes, that.paramTypes);
}
@Override
public int hashCode() {
return Objects.hash(name, paramTypes);
}
@Override
public String toString() {
return "MethodInfo{"
+ "name='" + name + '\'' + ", paramTypes=" + paramTypes + '}';
}
}
private static String normalize(Type type) {
return type.getClassName().replace('$', '.');
}
static class MethodExtraInfo {
private final boolean isStatic;
private final String returnType;
public MethodExtraInfo(MethodNode method) {
this.isStatic = (method.access & Opcodes.ACC_STATIC) != 0;
this.returnType = typeWithoutGenerics(normalize(Type.getReturnType(method.desc)));
}
public MethodExtraInfo(ExecutableElement methodElement) {
this.isStatic = methodElement.getModifiers().contains(Modifier.STATIC);
this.returnType = typeWithoutGenerics(canonicalize(methodElement.getReturnType()));
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MethodExtraInfo that = (MethodExtraInfo) o;
return isStatic == that.isStatic && Objects.equals(returnType, that.returnType);
}
@Override
public int hashCode() {
return Objects.hash(isStatic, returnType);
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment