我关注this tutorial和this Custom Detector Example以实施自定义Lint规则。基本上我所做的是:
我的问题是,在执行我的JUnits期间,我总是收到" No Warning"。当我对测试进行调试时,我可以看到我的自定义检测器没有被调用,我做错了什么?
Strings.java
public class Strings {
public static final String STR_ISSUE_001_ID = "VarsMustHaveMoreThanOneCharacter";
public static final String STR_ISSUE_001_DESCRIPTION = "Avoid naming variables with only one character";
public static final String STR_ISSUE_001_EXPLANATION = "Variables named with only one character do not pass any meaning to the reader. " +
"Variables name should clear indicate the meaning of the value it is holding";
}
Issues.java
public class Issues {
public static final
Issue ISSUE_001 = Issue.create(
STR_ISSUE_001_ID,
STR_ISSUE_001_DESCRIPTION,
STR_ISSUE_001_EXPLANATION,
SECURITY,
// Priority ranging from 0 to 10 in severeness
6,
WARNING,
new Implementation(VariableNameDetector.class, ALL_RESOURCES_SCOPE)
);
}
IssuesRegistry.java
public class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
@Override
public List<Issue> getIssues() {
List<Issue> issues = new ArrayList<>();
issues.add(ISSUE_001);
return issues;
}
}
VariableNameDetector.java
public class VariableNameDetector extends Detector implements Detector.JavaScanner {
public VariableNameDetector() {
}
@Override
public boolean appliesToResourceRefs() {
return false;
}
@Override
public boolean appliesTo(Context context, File file) {
return true;
}
@Override
@Nullable
public AstVisitor createJavaVisitor(JavaContext context) {
return new NamingConventionVisitor(context);
}
@Override
public List<String> getApplicableMethodNames() {
return null;
}
@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
List<Class<? extends Node>> types = new ArrayList<>(1);
types.add(lombok.ast.VariableDeclaration.class);
return types;
}
@Override
public void visitMethod(
JavaContext context,
AstVisitor visitor,
MethodInvocation methodInvocation
) {
}
@Override
public void visitResourceReference(
JavaContext context,
AstVisitor visitor,
Node node,
String type,
String name,
boolean isFramework
) {
}
private class NamingConventionVisitor extends ForwardingAstVisitor {
private final JavaContext context;
NamingConventionVisitor(JavaContext context) {
this.context = context;
}
@Override
public boolean visitVariableDeclaration(VariableDeclaration node) {
StrictListAccessor<VariableDefinitionEntry, VariableDeclaration> varDefinitions =
node.getVariableDefinitionEntries();
for (VariableDefinitionEntry varDefinition : varDefinitions) {
String name = varDefinition.astName().astValue();
if (name.length() == 1) {
context.report(
ISSUE_001,
context.getLocation(node),
STR_ISSUE_001_DESCRIPTION
);
return true;
}
}
return false;
}
}
}
的build.gradle
apply plugin: 'java'
configurations {
lintChecks
}
ext {
VERSION_LINT_API = '24.3.1'
VERSION_LINT_API_TESTS = '24.3.1'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.tools.lint:lint-api:$VERSION_LINT_API"
implementation "com.android.tools.lint:lint-checks:$VERSION_LINT_API"
testImplementation "com.android.tools.lint:lint-tests:$VERSION_LINT_API_TESTS"
}
jar {
manifest {
attributes('Lint-Registry': 'br.com.edsilfer.lint_rules.resources.IssueRegistry')
}
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
TestVariableNameDetector.java
private static final String ARG_DEFAULT_LINT_SUCCESS_LOG = "No warnings.";
@Override
protected Detector getDetector() {
return new VariableNameDetector();
}
@Override
protected List<Issue> getIssues() {
return Collections.singletonList(Issues.ISSUE_001);
}
public void test_file_with_no_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test.java", "public class Test {public String sampleVariable;}"))
);
}
public void test_file_with_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test3.java", "public class Test {public String a;bnvhgvhj}"))
);
}
}
PS:在Java模块上我无法访问assets
或res
文件夹,这就是我创建String.java
的原因我在我的单元测试中使用java(to, source)
- 我假设这个java
方法与我在此问题顶部引用的教程链接中的xml
相同的
答案 0 :(得分:3)
事实证明,在我的情况下问题出在JUnit本身。我认为我试图模拟文件的方式是错误的。以下文字是我创建的README.md of a sample project的一部分,用于记录我从此API中学到的内容并回答标题中的问题:
build.gradle
上:
Manifest-Version
和Lint-Registry
,将第一个设置为1.0,将第二个设置为类的完整路径,该类稍后将包含问题的目录; assemble
; ~/.android/lint
; createJavaVisitor()
方法中引用它;
build/lib
复制到~/.android/lint
- 如果您在build.gradle
上添加了执行此操作的任务,则可以跳过此步骤; < / LI>
~/.android/lint
后,下次程序启动时应由Lint读取自定义规则。为了在Android Studio中设置警报框,使缓存无效并重新启动 IDE就足够了,但是,./gradlew check
时,您的自定义规则会在Lint报告中被捕获,它可能是必须重新启动计算机;
测试自定义规则并非易事 - 主要是由于缺少官方API的文档。本节将介绍处理此问题的两种方法。该项目的主要目标是创建将针对实际文件运行的自定义规则,因此,测试文件将是测试它们所必需的。它们可以放在Lint Java Library Module的src/test/resources
文件夹中;
build.gradle
; EnhancedLintDetectorTest.java
和FileUtils.java
复制到项目的测试目录中;
src/test/resources
文件夹中的文件,这些文件是解决方法; EnhancedLintDetectorTest.java
应该返回所有将要进行测试的问题。一个很好的方法是从问题注册表中获取它们; EnhancedLintDetectorTest.java
; getDetector()
方法返回要测试的Detector实例; lintFiles("test file path taking resources dir as root")
执行自定义规则的检查并使用其结果对象来断言测试; 请注意,LintDetectorTest.java
来自TestCase.java
,因此,您仅限于JUnit 3.
您可能已经注意到,方法01可能有点过于复杂,尽管您仅限于JUnit 3功能。因为GitHub user a11n创建了一个Lint JUnit Rule,它允许以更简单的方式测试自定义Lint规则,并使用JUnit 4及更高版本的功能。请参阅他的项目README.md,了解有关如何使用此apprach创建测试的详细信息。
目前,Lint JUnit Rule没有更正测试文件的根目录,您可能无法看到从IDE传递的测试 - 但是当从命令行运行测试时它可以正常工作。我们创建了issue和PR来修复此错误。
答案 1 :(得分:1)
我不确定如何使用AST Api,但是我个人使用public final class RxJava2MethodCheckReturnValueDetector extends Detector implements Detector.JavaPsiScanner {
static final Issue ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE =
Issue.create("MethodMissingCheckReturnValue", "Method is missing the @CheckReturnValue annotation",
"Methods returning RxJava Reactive Types should be annotated with the @CheckReturnValue annotation.",
MESSAGES, 8, WARNING,
new Implementation(RxJava2MethodCheckReturnValueDetector.class, EnumSet.of(JAVA_FILE, TEST_SOURCES)));
@Override public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
}
@Override public JavaElementVisitor createPsiVisitor(@NonNull final JavaContext context) {
return new CheckReturnValueVisitor(context);
}
static class CheckReturnValueVisitor extends JavaElementVisitor {
private final JavaContext context;
CheckReturnValueVisitor(final JavaContext context) {
this.context = context;
}
@Override public void visitMethod(final PsiMethod method) {
final PsiType returnType = method.getReturnType();
if (returnType != null && Utils.isRxJava2TypeThatRequiresCheckReturnValueAnnotation(returnType)) {
final PsiAnnotation[] annotations = method.getModifierList().getAnnotations();
for (final PsiAnnotation annotation : annotations) {
if ("io.reactivex.annotations.CheckReturnValue".equals(annotation.getQualifiedName())) {
return;
}
}
final boolean isMethodMissingCheckReturnValueSuppressed = context.getDriver().isSuppressed(context, ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, method);
if (!isMethodMissingCheckReturnValueSuppressed) {
context.report(ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, context.getLocation(method.getNameIdentifier()), "Method should have @CheckReturnValue annotation");
}
}
}
}
}
,这是我正在为我工作的lint检查之一。
"SELECT LaTable.Num, LaTable.LaDate, Count(*) As C " & _
"FROM LaTable " & _
"GROUP BY LaTable.Num, LaTable.LaDate " & _
"HAVING Count(*) = 0;"
查看我写的更多here。