/*
* 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 com.android.tools.lint.detector.api.Issue;
import java.util.List;
@SuppressWarnings("javadoc")
public class AnnotationDetectorTest extends AbstractCheckTest {
public void test() throws Exception {
assertEquals(
"src/test/pkg/WrongAnnotation.java:9: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" public static void foobar(View view, @SuppressLint(\"NewApi\") int foo) { // Invalid: class-file check\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~\n" +
"src/test/pkg/WrongAnnotation.java:10: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @SuppressLint(\"NewApi\") // Invalid\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~\n" +
"src/test/pkg/WrongAnnotation.java:12: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid: class-file based check on local variable\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
"src/test/pkg/WrongAnnotation.java:14: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @android.annotation.SuppressLint({\"SdCardPath\", \"NewApi\"}) // Invalid (FQN)\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
"src/test/pkg/WrongAnnotation.java:28: Error: The @SuppressLint annotation cannot be used on a local variable with the lint check 'NewApi': move out to the surrounding method [LocalSuppress]\n" +
" @SuppressLint(\"NewApi\")\n" +
" ~~~~~~~~~~~~~~~~~~~~~~~\n" +
"5 errors, 0 warnings\n",
lintProject(
"src/test/pkg/WrongAnnotation.java.txt=>src/test/pkg/WrongAnnotation.java"
));
}
@SuppressWarnings("ClassNameDiffersFromFileName")
public void testUniqueValues() throws Exception {
assertEquals(""
+ "src/test/pkg/IntDefTest.java:9: Error: Constants STYLE_NO_INPUT and STYLE_NO_FRAME specify the same exact value (2); this is usually a cut & paste or merge error [UniqueConstants]\n"
+ " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ " ~~~~~~~~~~~~~~\n"
+ " src/test/pkg/IntDefTest.java:9: Previous same value\n"
+ "src/test/pkg/IntDefTest.java:28: Error: Constants FLAG3 and FLAG2 specify the same exact value (562949953421312); this is usually a cut & paste or merge error [UniqueConstants]\n"
+ " @IntDef({FLAG2, FLAG3, FLAG1})\n"
+ " ~~~~~\n"
+ " src/test/pkg/IntDefTest.java:28: Previous same value\n"
+ "2 errors, 0 warnings\n",
lintProject(
java("src/test/pkg/IntDefTest.java", ""
+ "package test.pkg;\n"
+ "import android.support.annotation.IntDef;\n"
+ "\n"
+ "import java.lang.annotation.Retention;\n"
+ "import java.lang.annotation.RetentionPolicy;\n"
+ "\n"
+ "@SuppressLint(\"UnusedDeclaration\")\n"
+ "public class IntDefTest {\n"
+ " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface DialogStyle {}\n"
+ "\n"
+ " public static final int STYLE_NORMAL = 0;\n"
+ " public static final int STYLE_NO_TITLE = 1;\n"
+ " public static final int STYLE_NO_FRAME = 2;\n"
+ " public static final int STYLE_NO_INPUT = 2;\n"
+ "\n"
+ " @IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})\n"
+ " @SuppressWarnings(\"UniqueConstants\")\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface SuppressedDialogStyle {}\n"
+ "\n"
+ "\n"
+ " public static final long FLAG1 = 0x100000000000L;\n"
+ " public static final long FLAG2 = 0x0002000000000000L;\n"
+ " public static final long FLAG3 = 0x2000000000000L;\n"
+ "\n"
+ " @IntDef({FLAG2, FLAG3, FLAG1})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags {}\n"
+ "\n"
+ ""
+ "}"),
copy("src/android/support/annotation/IntDef.java.txt",
"src/android/support/annotation/IntDef.java")));
}
@SuppressWarnings("ClassNameDiffersFromFileName")
public void testFlagStyle() throws Exception {
assertEquals(""
+ "src/test/pkg/IntDefTest.java:13: Warning: Consider declaring this constant using 1 << 44 instead [ShiftFlags]\n"
+ " public static final long FLAG5 = 0x100000000000L;\n"
+ " ~~~~~~~~~~~~~~~\n"
+ "src/test/pkg/IntDefTest.java:14: Warning: Consider declaring this constant using 1 << 49 instead [ShiftFlags]\n"
+ " public static final long FLAG6 = 0x0002000000000000L;\n"
+ " ~~~~~~~~~~~~~~~~~~~\n"
+ "src/test/pkg/IntDefTest.java:15: Warning: Consider declaring this constant using 1 << 3 instead [ShiftFlags]\n"
+ " public static final long FLAG7 = 8L;\n"
+ " ~~\n"
+ "0 errors, 3 warnings\n",
lintProject(
java("src/test/pkg/IntDefTest.java", ""
+ "package test.pkg;\n"
+ "import android.support.annotation.IntDef;\n"
+ "\n"
+ "import java.lang.annotation.Retention;\n"
+ "import java.lang.annotation.RetentionPolicy;\n"
+ "\n"
+ "@SuppressWarnings(\"unused\")\n"
+ "public class IntDefTest {\n"
+ " public static final long FLAG1 = 1;\n"
+ " public static final long FLAG2 = 2;\n"
+ " public static final long FLAG3 = 1 << 2;\n"
+ " public static final long FLAG4 = 1 << 3;\n"
+ " public static final long FLAG5 = 0x100000000000L;\n"
+ " public static final long FLAG6 = 0x0002000000000000L;\n"
+ " public static final long FLAG7 = 8L;\n"
+ " public static final long FLAG8 = 9L;\n"
+ " public static final long FLAG9 = 0;\n"
+ " public static final long FLAG10 = 1;\n"
+ " public static final long FLAG11 = -1;\n"
+ "\n"
// Not a flag (missing flag=true)
+ " @IntDef({FLAG1, FLAG2, FLAG3})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags1 {}\n"
+ "\n"
// OK: Too few values
+ " @IntDef(flag = true, value={FLAG1, FLAG2})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags2 {}\n"
+ "\n"
// OK: Allow 0, 1, -1
+ " @IntDef(flag = true, value={FLAG9, FLAG10, FLAG11})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags3 {}\n"
+ "\n"
// OK: Already using shifts
+ " @IntDef(flag = true, value={FLAG1, FLAG3, FLAG4})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags4 {}\n"
+ "\n"
// Wrong: should be flagged
+ " @IntDef(flag = true, value={FLAG5, FLAG6, FLAG7, FLAG8})\n"
+ " @Retention(RetentionPolicy.SOURCE)\n"
+ " private @interface Flags5 {}\n"
+ "}"),
copy("src/android/support/annotation/IntDef.java.txt",
"src/android/support/annotation/IntDef.java")));
}
@Override
protected Detector getDetector() {
return new AnnotationDetector();
}
@Override
protected List<Issue> getIssues() {
List<Issue> issues = super.getIssues();
// Need these issues on to be found by the registry as well to look up scope
// in id references (these ids are referenced in the unit test java file below)
issues.add(ApiDetector.UNSUPPORTED);
issues.add(SdCardDetector.ISSUE);
return issues;
}
}