diff options
author | Tor Norbye <tnorbye@google.com> | 2012-09-10 07:38:04 -0700 |
---|---|---|
committer | android code review <noreply-gerritcodereview@google.com> | 2012-09-10 07:38:05 -0700 |
commit | 7ce6049cd1d368d7cd08ac5e22f855f23b2110a7 (patch) | |
tree | e95fc0fd269ff926c65efdde3bb47914a5e1134a | |
parent | 72000161fbccf626fe171e74489cc72923d670aa (diff) | |
parent | f963bdabb99f7ac305194ef3bbc7f52f4504cd96 (diff) | |
download | sdk-7ce6049cd1d368d7cd08ac5e22f855f23b2110a7.zip sdk-7ce6049cd1d368d7cd08ac5e22f855f23b2110a7.tar.gz sdk-7ce6049cd1d368d7cd08ac5e22f855f23b2110a7.tar.bz2 |
Merge "37001: Lint check: non existing Activity/Service/Receiver"
16 files changed, 661 insertions, 10 deletions
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java index ac09a1b..8915e81 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java @@ -54,7 +54,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { private static final List<Issue> sIssues; static { - final int initialCapacity = 106; + final int initialCapacity = 109; List<Issue> issues = new ArrayList<Issue>(initialCapacity); issues.add(AccessibilityDetector.ISSUE); @@ -89,6 +89,9 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(LocaleDetector.STRING_LOCALE); issues.add(LocaleDetector.DATE_FORMAT); issues.add(RegistrationDetector.ISSUE); + issues.add(MissingClassDetector.MISSING); + issues.add(MissingClassDetector.INSTANTIATABLE); + issues.add(MissingClassDetector.INNERCLASS); issues.add(HandlerDetector.ISSUE); issues.add(FragmentDetector.ISSUE); issues.add(TranslationDetector.EXTRA); diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java new file mode 100644 index 0000000..8c80b68 --- /dev/null +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/MissingClassDetector.java @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2012 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.tools.lint.checks; + +import static com.android.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX; +import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI; +import static com.android.tools.lint.detector.api.LintConstants.ATTR_NAME; +import static com.android.tools.lint.detector.api.LintConstants.ATTR_PACKAGE; +import static com.android.tools.lint.detector.api.LintConstants.CONSTRUCTOR_NAME; +import static com.android.tools.lint.detector.api.LintConstants.TAG_ACTIVITY; +import static com.android.tools.lint.detector.api.LintConstants.TAG_APPLICATION; +import static com.android.tools.lint.detector.api.LintConstants.TAG_PROVIDER; +import static com.android.tools.lint.detector.api.LintConstants.TAG_RECEIVER; +import static com.android.tools.lint.detector.api.LintConstants.TAG_SERVICE; + +import com.android.annotations.NonNull; +import com.android.tools.lint.detector.api.Category; +import com.android.tools.lint.detector.api.ClassContext; +import com.android.tools.lint.detector.api.Context; +import com.android.tools.lint.detector.api.Detector.ClassScanner; +import com.android.tools.lint.detector.api.Issue; +import com.android.tools.lint.detector.api.LayoutDetector; +import com.android.tools.lint.detector.api.LintUtils; +import com.android.tools.lint.detector.api.Location; +import com.android.tools.lint.detector.api.Location.Handle; +import com.android.tools.lint.detector.api.Scope; +import com.android.tools.lint.detector.api.Severity; +import com.android.tools.lint.detector.api.Speed; +import com.android.tools.lint.detector.api.XmlContext; +import com.google.common.collect.Maps; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +/** + * Checks to ensure that classes referenced in the manifest actually exist and are included + * + */ +public class MissingClassDetector extends LayoutDetector implements ClassScanner { + /** Manifest-referenced classes missing from the project or libraries */ + public static final Issue MISSING = Issue.create( + "MissingRegistered", //$NON-NLS-1$ + "Ensures that classes referenced in the manifest are present in the project or libraries", + + "If a class is referenced in the manifest, it must also exist in the project (or in one " + + "of the libraries included by the project. This check helps uncover typos in " + + "registration names, or attempts to rename or move classes without updating the " + + "manifest file properly.", + + Category.CORRECTNESS, + 8, + Severity.ERROR, + MissingClassDetector.class, + EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE, Scope.JAVA_LIBRARIES)).setMoreInfo( + "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ + + /** Are activity, service, receiver etc subclasses instantiatable? */ + public static final Issue INSTANTIATABLE = Issue.create( + "Instantiatable", //$NON-NLS-1$ + "Ensures that classes registered in the manifest file are instantiatable", + + "Activities, services, broadcast receivers etc. registered in the manifest file " + + "must be \"instiantable\" by the system, which means that the class must be " + + "public, it must have an empty public constructor, and if it's an inner class, " + + "it must be a static inner class.", + + Category.CORRECTNESS, + 6, + Severity.WARNING, + MissingClassDetector.class, + Scope.CLASS_FILE_SCOPE); + + /** Is the right character used for inner class separators? */ + public static final Issue INNERCLASS = Issue.create( + "InnerclassSeparator", //$NON-NLS-1$ + "Ensures that inner classes are referenced using '$' instead of '.' in class names", + + "When you reference an inner class in a manifest file, you must use '$' instead of '.' " + + "as the separator character, e.g. Outer$Inner instead of Outer.Inner.\n" + + "\n" + + "(If you get this warning for a class which is not actually an inner class, it's " + + "because you are using uppercase characters in your package name, which is not " + + "conventional.)", + + Category.CORRECTNESS, + 3, + Severity.WARNING, + MissingClassDetector.class, + Scope.MANIFEST_SCOPE); + + private Map<String, Location.Handle> mReferencedClasses; + + /** Constructs a new {@link MissingClassDetector} */ + public MissingClassDetector() { + } + + @Override + public @NonNull Speed getSpeed() { + return Speed.FAST; + } + + // ---- Implements XmlScanner ---- + + @Override + public Collection<String> getApplicableElements() { + return Arrays.asList( + TAG_APPLICATION, + TAG_ACTIVITY, + TAG_SERVICE, + TAG_RECEIVER, + TAG_PROVIDER + ); + } + + @Override + public void visitElement(@NonNull XmlContext context, @NonNull Element element) { + + Element root = element.getOwnerDocument().getDocumentElement(); + Attr classNameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME); + if (classNameNode == null) { + return; + } + String className = classNameNode.getValue(); + if (className.isEmpty()) { + return; + } + + String pkg = root.getAttribute(ATTR_PACKAGE); + String fqcn; + if (className.startsWith(".")) { //$NON-NLS-1$ + fqcn = pkg + className; + } else if (className.indexOf('.') == -1) { + // According to the <activity> manifest element documentation, this is not + // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html ) + // but it appears in manifest files and appears to be supported by the runtime + // so handle this in code as well: + fqcn = pkg + '.' + className; + } else { // else: the class name is already a fully qualified class name + fqcn = className; + } + + String signature = ClassContext.getInternalName(fqcn); + if (signature.isEmpty() || signature.startsWith(ANDROID_PKG_PREFIX)) { + return; + } + + if (mReferencedClasses == null) { + mReferencedClasses = Maps.newHashMapWithExpectedSize(16); + } + + Handle handle = context.parser.createLocationHandle(context, element); + mReferencedClasses.put(signature, handle); + + if (signature.indexOf('$') != -1) { + if (className.indexOf('$') == -1 && className.indexOf('.', 1) > 0) { + boolean haveUpperCase = false; + for (int i = 0, n = pkg.length(); i < n; i++) { + if (Character.isUpperCase(pkg.charAt(i))) { + haveUpperCase = true; + break; + } + } + if (!haveUpperCase) { + String message = String.format("Use '$' instead of '.' for inner classes " + + "(or use only lowercase letters in package names)", className); + Location location = context.getLocation(classNameNode); + context.report(INNERCLASS, element, location, message, null); + } + } + + // The internal name contains a $ which means it's an inner class. + // The conversion from fqcn to internal name is a bit ambiguous: + // "a.b.C.D" usually means "inner class D in class C in package a.b". + // However, it can (see issue 31592) also mean class D in package "a.b.C". + // To make sure we don't falsely complain that foo/Bar$Baz doesn't exist, + // in case the user has actually created a package named foo/Bar and a proper + // class named Baz, we register *both* into the reference map. + // When generating errors we'll look for these an rip them back out if + // it looks like one of the two variations have been seen. + signature = signature.replace('$', '/'); + mReferencedClasses.put(signature, handle); + } + } + + @Override + public void afterCheckProject(@NonNull Context context) { + if (!context.getProject().isLibrary() && mReferencedClasses != null && + !mReferencedClasses.isEmpty()) { + List<String> classes = new ArrayList<String>(mReferencedClasses.keySet()); + Collections.sort(classes); + for (String owner : classes) { + Location.Handle handle = mReferencedClasses.get(owner); + String fqcn = ClassContext.getFqcn(owner); + + String signature = ClassContext.getInternalName(fqcn); + if (!signature.equals(owner)) { + if (!mReferencedClasses.containsKey(signature)) { + continue; + } + } else { + signature = signature.replace('$', '/'); + if (!mReferencedClasses.containsKey(signature)) { + continue; + } + } + mReferencedClasses.remove(owner); + + String message = String.format( + "Class referenced in the manifest, %1$s, was not found in the " + + "project or the libraries", fqcn); + Location location = handle.resolve(); + context.report(MISSING, location, message, null); + } + } + } + + // ---- Implements ClassScanner ---- + + @Override + public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { + String curr = classNode.name; + if (mReferencedClasses != null && mReferencedClasses.containsKey(curr)) { + mReferencedClasses.remove(curr); + + // Ensure that the class is public, non static and has a null constructor! + + if ((classNode.access & Opcodes.ACC_PUBLIC) == 0) { + context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( + "This class should be public (%1$s)", + ClassContext.createSignature(classNode.name, null, null)), + null); + return; + } + + if (classNode.name.indexOf('$') != -1 && !LintUtils.isStaticInnerClass(classNode)) { + context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( + "This inner class should be static (%1$s)", + ClassContext.createSignature(classNode.name, null, null)), + null); + return; + } + + boolean hasDefaultConstructor = false; + @SuppressWarnings("rawtypes") // ASM API + List methodList = classNode.methods; + for (Object m : methodList) { + MethodNode method = (MethodNode) m; + if (method.name.equals(CONSTRUCTOR_NAME)) { + if (method.desc.equals("()V")) { //$NON-NLS-1$ + // The constructor must be public + if ((method.access & Opcodes.ACC_PUBLIC) != 0) { + hasDefaultConstructor = true; + } else { + context.report(INSTANTIATABLE, context.getLocation(method, classNode), + "The default constructor must be public", + null); + // Also mark that we have a constructor so we don't complain again + // below since we've already emitted a more specific error related + // to the default constructor + hasDefaultConstructor = true; + } + } + } + } + + if (!hasDefaultConstructor) { + context.report(INSTANTIATABLE, context.getLocation(classNode), String.format( + "This class should provide a default constructor (a public " + + "constructor with no arguments) (%1$s)", + ClassContext.createSignature(classNode.name, null, null)), + null); + } + } + } +} diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java index b645253..5f4aff4 100644 --- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java +++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/RegistrationDetector.java @@ -117,23 +117,29 @@ public class RegistrationDetector extends LayoutDetector implements ClassScanner } } - private static String getFqcn(Element element) { - StringBuilder sb = new StringBuilder(); + /** + * Returns the fully qualified class name for a manifest entry element that + * specifies a name attribute + * + * @param element the element + * @return the fully qualified class name + */ + @NonNull + private static String getFqcn(@NonNull Element element) { Element root = element.getOwnerDocument().getDocumentElement(); String pkg = root.getAttribute(ATTR_PACKAGE); String className = element.getAttributeNS(ANDROID_URI, ATTR_NAME); if (className.startsWith(".")) { //$NON-NLS-1$ - sb.append(pkg); + return pkg + className; } else if (className.indexOf('.') == -1) { // According to the <activity> manifest element documentation, this is not // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html ) // but it appears in manifest files and appears to be supported by the runtime // so handle this in code as well: - sb.append(pkg); - sb.append('.'); + return pkg + '.' + className; } // else: the class name is already a fully qualified class name - sb.append(className); - return sb.toString(); + + return className; } // ---- Implements ClassScanner ---- diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java index d644951..de5b65e 100644 --- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/AbstractCheckTest.java @@ -55,9 +55,19 @@ import junit.framework.TestCase; public abstract class AbstractCheckTest extends TestCase { protected abstract Detector getDetector(); + private Detector mDetector; + + private Detector getDetectorInstance() { + if (mDetector == null) { + mDetector = getDetector(); + } + + return mDetector; + } + protected List<Issue> getIssues() { List<Issue> issues = new ArrayList<Issue>(); - Class<? extends Detector> detectorClass = getDetector().getClass(); + Class<? extends Detector> detectorClass = getDetectorInstance().getClass(); // Get the list of issues from the registry and filter out others, to make sure // issues are properly registered List<Issue> candidates = new BuiltinIssueRegistry().getIssues(); @@ -264,7 +274,7 @@ public abstract class AbstractCheckTest extends TestCase { } protected boolean isEnabled(Issue issue) { - Class<? extends Detector> detectorClass = getDetector().getClass(); + Class<? extends Detector> detectorClass = getDetectorInstance().getClass(); if (issue.getDetectorClass() == detectorClass) { return true; } diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java new file mode 100644 index 0000000..d8f18b6 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/MissingClassDetectorTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2012 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.tools.lint.checks; + +import com.android.tools.lint.detector.api.Detector; + +import java.io.File; +import java.util.Arrays; + +@SuppressWarnings("javadoc") +public class MissingClassDetectorTest extends AbstractCheckTest { + @Override + protected Detector getDetector() { + return new MissingClassDetector(); + } + + public void test() throws Exception { + assertEquals( + "AndroidManifest.xml:13: Error: Class referenced in the manifest, test.pkg.TestProvider, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity android:name=\".TestProvider\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.TestProvider2, was not found in the project or the libraries [MissingRegistered]\n" + + " <service android:name=\"test.pkg.TestProvider2\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "AndroidManifest.xml:15: Error: Class referenced in the manifest, test.pkg.TestService, was not found in the project or the libraries [MissingRegistered]\n" + + " <provider android:name=\".TestService\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "AndroidManifest.xml:16: Error: Class referenced in the manifest, test.pkg.OnClickActivity, was not found in the project or the libraries [MissingRegistered]\n" + + " <receiver android:name=\"OnClickActivity\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "AndroidManifest.xml:17: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n" + + " <service android:name=\"TestReceiver\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "5 errors, 0 warnings\n", + + lintProject( + "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath" + )); + } + + public void testOkClasses() throws Exception { + assertEquals( + "No warnings.", + + lintProject( + "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java", + "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class", + "bytecode/TestService.java.txt=>src/test/pkg/TestService.java", + "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class", + "bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java", + "bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class", + "bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java", + "bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class", + "bytecode/TestReceiver.java.txt=>src/test/pkg/TestReceiver.java", + "bytecode/TestReceiver.class.data=>bin/classes/test/pkg/TestReceiver.class" + )); + } + + public void testOkLibraries() throws Exception { + assertEquals( + "No warnings.", + + lintProject( + "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "bytecode/classes.jar=>libs/classes.jar" + )); + } + + public void testLibraryProjects() throws Exception { + File master = getProjectDir("MasterProject", + // Master project + "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml", + "multiproject/main.properties=>project.properties", + "bytecode/.classpath=>.classpath" + ); + File library = getProjectDir("LibraryProject", + // Library project + "multiproject/library-manifest.xml=>AndroidManifest.xml", + "multiproject/library.properties=>project.properties", + "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java", + "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class", + "bytecode/TestService.java.txt=>src/test/pkg/TestService.java", + "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class", + "bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java", + "bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class", + "bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java", + "bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class" + // Missing TestReceiver: Test should complain about just that class + ); + assertEquals( + "MasterProject/AndroidManifest.xml:17: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n" + + " <service android:name=\"TestReceiver\" />\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + + "1 errors, 0 warnings\n", + + checkLint(Arrays.asList(master, library))); + } + + public void testInnerClassStatic() throws Exception { + assertEquals( + "src/test/pkg/Foo.java:8: Warning: This inner class should be static (test.pkg.Foo.Baz) [Instantiatable]\n" + + " public class Baz extends Activity {\n" + + " ^\n" + + "0 errors, 1 warnings\n", + + lintProject( + "registration/AndroidManifest.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Foo.java.txt=>src/test/pkg/Foo.java", + "registration/Foo.class.data=>bin/classes/test/pkg/Foo.class", + "registration/Foo$Bar.class.data=>bin/classes/test/pkg/Foo$Bar.class", + "registration/Foo$Baz.class.data=>bin/classes/test/pkg/Foo$Baz.class" + )); + } + + public void testInnerClassPublic() throws Exception { + assertEquals( + "src/test/pkg/Foo/Bar.java:6: Warning: The default constructor must be public [Instantiatable]\n" + + " private Bar() {\n" + + " ^\n" + + "0 errors, 1 warnings\n", + + lintProject( + "registration/AndroidManifestInner.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java", + "registration/Bar.class.data=>bin/classes/test/pkg/Foo/Bar.class" + )); + } + + public void testInnerClass() throws Exception { + assertEquals( + "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity\n" + + " ^\n" + + "AndroidManifest.xml:23: Error: Class referenced in the manifest, test.pkg.Foo.Baz, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity\n" + + " ^\n" + + "2 errors, 0 warnings\n", + + lintProject( + "registration/AndroidManifest.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Foo.java.txt=>src/test/pkg/Foo.java" + )); + } + + public void testInnerClass2() throws Exception { + assertEquals( + "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity\n" + + " ^\n" + + "1 errors, 0 warnings\n", + + lintProject( + "registration/AndroidManifestInner.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java" + )); + } + + public void testWrongSeparator1() throws Exception { + assertEquals( + "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity\n" + + " ^\n" + + "1 errors, 0 warnings\n", + + lintProject( + "registration/AndroidManifestWrong.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java" + )); + } + + public void testWrongSeparator2() throws Exception { + assertEquals( + "AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" + + " <activity\n" + + " ^\n" + + "AndroidManifest.xml:15: Warning: Use '$' instead of '.' for inner classes (or use only lowercase letters in package names) [InnerclassSeparator]\n" + + " android:name=\".Foo.Bar\"\n" + + " ~~~~~~~~~~~~~~~~~~~~~~~\n" + + "1 errors, 1 warnings\n", + + lintProject( + "registration/AndroidManifestWrong2.xml=>AndroidManifest.xml", + "bytecode/.classpath=>.classpath", + "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java" + )); + } +} diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar Binary files differnew file mode 100644 index 0000000..fa52dcf --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/bytecode/classes.jar diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml new file mode 100644 index 0000000..0b07cdf --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifest.xml @@ -0,0 +1,29 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="test.pkg" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="16" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".Foo$Bar" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity + android:name=".Foo$Baz" + android:label="@string/app_name" > + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml new file mode 100644 index 0000000..1b87d9d --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestInner.xml @@ -0,0 +1,25 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="test.pkg.Foo" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="16" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".Bar" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml new file mode 100644 index 0000000..bfdcde0 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong.xml @@ -0,0 +1,25 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="test.pkg.Foo" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="16" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name="test.pkg.Foo.Bar" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml new file mode 100644 index 0000000..8773e0f --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/AndroidManifestWrong2.xml @@ -0,0 +1,25 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="test.pkg" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="8" + android:targetSdkVersion="16" /> + + <application + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".Foo.Bar" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data Binary files differnew file mode 100644 index 0000000..e51a0e8 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt new file mode 100644 index 0000000..b514f11 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Bar.java.txt @@ -0,0 +1,8 @@ +package test.pkg.Foo; + +import android.app.Activity; + +public class Bar extends Activity { + private Bar() { + } +} diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data Binary files differnew file mode 100644 index 0000000..da45ebb --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Bar.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data Binary files differnew file mode 100644 index 0000000..aadd767 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo$Baz.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data Binary files differnew file mode 100644 index 0000000..aafcc73 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.class.data diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt new file mode 100644 index 0000000..a647030 --- /dev/null +++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/registration/Foo.java.txt @@ -0,0 +1,10 @@ +package test.pkg; + +import android.app.Activity; + +public class Foo { + public static class Bar extends Activity { + } + public class Baz extends Activity { + } +} |