Home | History | Annotate | Download | only in checkstyle
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.support.checkstyle;
     18 
     19 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
     20 import com.puppycrawl.tools.checkstyle.api.DetailAST;
     21 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
     22 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtility;
     23 
     24 /**
     25  * A check that verifies that all the methods marked with @Test have a matching test size
     26  * annotation such as @SmallTest, @MediumTest or @LargeTest. This is needed to make sure
     27  * that newly added tests get run in the automated test runner.
     28  */
     29 public class TestSizeAnnotationCheck extends AbstractCheck {
     30     private static final String SMALL_TEST = "SmallTest";
     31     private static final String MEDIUM_TEST = "MediumTest";
     32     private static final String LARGE_TEST = "LargeTest";
     33     private static final String TEST = "Test";
     34     private static final String RUN_WITH = "RunWith";
     35     private static final String JUNIT4 = "JUnit4";
     36 
     37     private static final String MESSAGE = "Method with @Test annotation must have a @SmallTest, "
     38             + "@MediumTest, or @LargeTest annotation. See https://goo.gl/c2I0WP for recommended "
     39             + "timeouts";
     40 
     41     @Override
     42     public int[] getDefaultTokens() {
     43         return new int[]{TokenTypes.CLASS_DEF, TokenTypes.INTERFACE_DEF};
     44     }
     45 
     46     @Override
     47     public int[] getAcceptableTokens() {
     48         return getDefaultTokens();
     49     }
     50 
     51     @Override
     52     public int[] getRequiredTokens() {
     53         return getDefaultTokens();
     54     }
     55 
     56     @Override
     57     public void visitToken(DetailAST ast) {
     58         if (isJUnit4Runner(ast)) {
     59             // Tests that run with JUnit4 do not require test size annotations.
     60             return;
     61         }
     62         final boolean classHasTestSizeAnnotation =
     63                 AnnotationUtility.containsAnnotation(ast, SMALL_TEST)
     64                         || AnnotationUtility.containsAnnotation(ast, MEDIUM_TEST)
     65                         || AnnotationUtility.containsAnnotation(ast, LARGE_TEST);
     66 
     67         DetailAST objBlock = ast.findFirstToken(TokenTypes.OBJBLOCK);
     68         for (DetailAST child = objBlock.getFirstChild();
     69                 child != null; child = child.getNextSibling()) {
     70             if (child.getType() == TokenTypes.METHOD_DEF
     71                     && AnnotationUtility.containsAnnotation(child, TEST)) {
     72                 final boolean methodHasTestSizeAnnotation =
     73                         AnnotationUtility.containsAnnotation(child, SMALL_TEST)
     74                                 || AnnotationUtility.containsAnnotation(child, MEDIUM_TEST)
     75                                 || AnnotationUtility.containsAnnotation(child, LARGE_TEST);
     76                 if (!classHasTestSizeAnnotation && !methodHasTestSizeAnnotation) {
     77                     log(child.getLineNo(), MESSAGE);
     78                 }
     79             }
     80         }
     81     }
     82 
     83     /**
     84      * Checks whether this test class is annotated with @RunWith(JUnit4.class).
     85      */
     86     private boolean isJUnit4Runner(DetailAST ast) {
     87         DetailAST runner = AnnotationUtility.getAnnotation(ast, RUN_WITH);
     88 
     89         // We make an assumption that @RunWith annotation will have this structure:
     90         //      @RunWith
     91         //         |
     92         //        EXPR
     93         //          |
     94         //    -----DOT-------
     95         //  /                \
     96         // Runner name     class
     97 
     98         if (runner == null // There is no @RunWith annotation
     99                 || runner.findFirstToken(TokenTypes.EXPR) == null // Annotation has no value.
    100                 || runner.findFirstToken(TokenTypes.EXPR).getFirstChild() == null
    101                 || runner.findFirstToken(TokenTypes.EXPR).getFirstChild().getFirstChild() == null) {
    102             return false;
    103         }
    104         return JUNIT4.equals(
    105                 runner.findFirstToken(TokenTypes.EXPR).getFirstChild().getFirstChild().getText());
    106     }
    107 }
    108