title: How to correctly implement and test Custom Lint Rules in Android Studio?
body: <p>I'm following <a href="http://blog.xebia.com/how-to-create-your-own-lint-rule/" rel="nofollow noreferrer">this tutorial</a> and <a href="https://github.com/imobile3/cassowary/blob/master/libcassowary/support/lint/android/src/com/imobile3/cassowary/NamingConventionDetector.java" rel="nofollow noreferrer">this Custom Detector Example</a> in order to implement Custom Lint Rules. Basically what I've done is:</p>
  
  <ol>
  <li>Create a new Android Project in Android Studio;</li>
  <li>Create a java module for project created in step 1;</li>
  <li>On module's <code>build.gradle</code>, import Lint API dependencies;</li>
  <li>Create an <code>Issue</code> &amp; <code>IssueRegistry</code> &amp; <code>CustomDetector</code>;</li>
  <li>Reference the <code>IssueRegistry</code> on module's <code>build.gradle</code>;</li>
  <li>Create Unit tests;</li>
  </ol>
  
  <p>My problem is, during the execution of my JUnits, I always receive "No Warning". When I debug the test, I can see that my Custom Detector isn't called, what am I doing wrong?</p>
  
  <p><strong>Strings.java</strong></p>
  
  <pre><code>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";
  }
  </code></pre>
  
  <p><strong>Issues.java</strong></p>
  
  <pre><code>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)
      );
  }
  </code></pre>
  
  <p><strong>IssuesRegistry.java</strong></p>
  
  <pre><code>public class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
      @Override
      public List&lt;Issue&gt; getIssues() {
          List&lt;Issue&gt; issues = new ArrayList&lt;&gt;();
          issues.add(ISSUE_001);
          return issues;
      }
  }
  </code></pre>
  
  <p><strong>VariableNameDetector.java</strong></p>
  
  <pre><code>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&lt;String&gt; getApplicableMethodNames() {
          return null;
      }
  
      @Override
      public List&lt;Class&lt;? extends Node&gt;&gt; getApplicableNodeTypes() {
          List&lt;Class&lt;? extends Node&gt;&gt; types = new ArrayList&lt;&gt;(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&lt;VariableDefinitionEntry, VariableDeclaration&gt; 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;
          }
      }
  }
  </code></pre>
  
  <p><strong>build.gradle</strong></p>
  
  <pre><code>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"
  </code></pre>
  
  <p><strong>TestVariableNameDetector.java</strong></p>
  
  <pre><code>private static final String ARG_DEFAULT_LINT_SUCCESS_LOG = "No warnings.";
  
      @Override
      protected Detector getDetector() {
          return new VariableNameDetector();
      }
  
      @Override
      protected List&lt;Issue&gt; 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}"))
          );
      }
  }
  </code></pre>
  
  <p><em>P.S.: on Java's module I do not have access to <code>assets</code>or <code>res</code> folder, that is the reason why I've created a <code>String.java</code> and I'm using <code>java(to, source)</code> in my Unit test - I assumed that this <code>java</code> method does the same as the <code>xml</code> from the tutorial link I referenced at the top of this question.</em></p>