001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2014 Oliver Burn 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019package com.puppycrawl.tools.checkstyle.checks.coding; 020 021import com.puppycrawl.tools.checkstyle.api.Check; 022import com.puppycrawl.tools.checkstyle.api.DetailAST; 023import com.puppycrawl.tools.checkstyle.api.TokenTypes; 024 025import com.puppycrawl.tools.checkstyle.checks.CheckUtils; 026 027/** 028 * Ensures that the setUp(), tearDown()methods are named correctly, 029 * have no arguments, return void and are either public or protected. 030 * Also ensures that suite() is named correctly, has no arguments, returns 031 * junit.framework.Test, and is public and static. 032 * 033 * Rationale: Developers will often misname one or more of these 034 * methods and not realise that the method is not being called. 035 * 036 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> 037 */ 038public final class JUnitTestCaseCheck extends Check 039{ 040 /** <code>setUp()</code> method name. */ 041 private static final String SET_UP_METHOD_NAME = "setUp"; 042 /** <code>tearDown()</code> method name. */ 043 private static final String TEAR_DOWN_METHOD_NAME = "tearDown"; 044 /** <code>suite()</code> method name. */ 045 private static final String SUITE_METHOD_NAME = "suite"; 046 047 @Override 048 public int[] getDefaultTokens() 049 { 050 return new int[] {TokenTypes.METHOD_DEF}; 051 } 052 053 @Override 054 public int[] getRequiredTokens() 055 { 056 return getDefaultTokens(); 057 } 058 059 @Override 060 public void visitToken(DetailAST aAST) 061 { 062 switch (aAST.getType()) { 063 case TokenTypes.METHOD_DEF: 064 visitMethodDef(aAST); 065 break; 066 default: 067 throw new IllegalStateException(aAST.toString()); 068 } 069 } 070 071 /** 072 * Checks given method definition. 073 * @param aAST a method def node for check 074 */ 075 private void visitMethodDef(DetailAST aAST) 076 { 077 final String name = aAST.findFirstToken(TokenTypes.IDENT).getText(); 078 079 if (name.equalsIgnoreCase(SET_UP_METHOD_NAME)) { 080 checkSetUpTearDownMethod(aAST, name, SET_UP_METHOD_NAME); 081 } 082 else if (name.equalsIgnoreCase(TEAR_DOWN_METHOD_NAME)) { 083 checkSetUpTearDownMethod(aAST, name, TEAR_DOWN_METHOD_NAME); 084 } 085 else if (name.equalsIgnoreCase(SUITE_METHOD_NAME)) { 086 checkSuiteMethod(aAST, name); 087 } 088 } 089 090 /** 091 * Checks signature/name of <code>suite()</code>. 092 * @param aAST method definition node 093 * @param aActualName method name 094 */ 095 private void checkSuiteMethod(DetailAST aAST, String aActualName) 096 { 097 if (!aActualName.equals(SUITE_METHOD_NAME)) { 098 log(aAST, "junit.method.name", SUITE_METHOD_NAME); 099 } 100 101 if (!isPublicAndStatic(aAST)) { 102 log(aAST, "junit.method.public.and.static", SUITE_METHOD_NAME); 103 } 104 105 // let's check return type 106 final DetailAST typeAST = aAST.findFirstToken(TokenTypes.TYPE); 107 final boolean isArray = 108 (typeAST.findFirstToken(TokenTypes.ARRAY_DECLARATOR) != null); 109 final String type = CheckUtils.createFullType(typeAST).getText(); 110 if (isArray 111 || (!"Test".equals(type) 112 && !"junit.framework.Test".equals(type))) 113 { 114 log(aAST, "junit.method.return.type", 115 SUITE_METHOD_NAME, "junit.framework.Test"); 116 } 117 checkParameters(aAST, SUITE_METHOD_NAME); 118 } 119 120 /** 121 * Checks signature/name of <code>setUp()</code>/<code>tearDown</code>. 122 * @param aAST method definition node 123 * @param aActualName actual method name 124 * @param aExpectedName expected method name 125 */ 126 private void checkSetUpTearDownMethod(DetailAST aAST, String aActualName, 127 String aExpectedName) 128 { 129 if (!aActualName.equals(aExpectedName)) { 130 log(aAST, "junit.method.name", aActualName, aExpectedName); 131 } 132 133 if (!isPublicOrProtected(aAST)) { 134 log(aAST, "junit.method.protected.or.public", aExpectedName); 135 } 136 137 if (isStatic(aAST)) { 138 log(aAST, "junit.method.static", aExpectedName); 139 } 140 141 checkReturnValue(aAST, aActualName); 142 checkParameters(aAST, aActualName); 143 } 144 145 /** 146 * Checks that given method returns <code>void</code>. 147 * @param aAST method definition node 148 * @param aName method name 149 */ 150 private void checkReturnValue(DetailAST aAST, String aName) 151 { 152 final DetailAST returnValueAST = aAST.findFirstToken(TokenTypes.TYPE); 153 154 if (returnValueAST.findFirstToken(TokenTypes.LITERAL_VOID) == null) { 155 log(aAST, "junit.method.return.type", aName, "void"); 156 } 157 } 158 159 /** 160 * Checks return value of given method. 161 * @param aAST method definition node 162 * @param aName method name 163 */ 164 private void checkParameters(DetailAST aAST, String aName) 165 { 166 final DetailAST parametersAST = 167 aAST.findFirstToken(TokenTypes.PARAMETERS); 168 169 if (parametersAST.getChildCount() != 0) { 170 log(aAST, "junit.method.parameters", aName); 171 } 172 } 173 174 /** 175 * Checks if given method declared as public or 176 * protected and non-static. 177 * @param aAST method definition node 178 * @return true if given method is declared as public or protected 179 */ 180 private boolean isPublicOrProtected(DetailAST aAST) 181 { 182 final DetailAST modifiersAST = 183 aAST.findFirstToken(TokenTypes.MODIFIERS); 184 final DetailAST publicAST = 185 modifiersAST.findFirstToken(TokenTypes.LITERAL_PUBLIC); 186 final DetailAST protectedAST = 187 modifiersAST.findFirstToken(TokenTypes.LITERAL_PROTECTED); 188 189 return (publicAST != null) || (protectedAST != null); 190 } 191 192 /** 193 * Checks if given method declared as <code>public</code> and 194 * <code>static</code>. 195 * @param aAST method definition node 196 * @return true if given method is declared as public and static 197 */ 198 private boolean isPublicAndStatic(DetailAST aAST) 199 { 200 final DetailAST modifiersAST = 201 aAST.findFirstToken(TokenTypes.MODIFIERS); 202 final DetailAST publicAST = 203 modifiersAST.findFirstToken(TokenTypes.LITERAL_PUBLIC); 204 final DetailAST staticAST = 205 modifiersAST.findFirstToken(TokenTypes.LITERAL_STATIC); 206 207 return (publicAST != null) && (staticAST != null); 208 } 209 210 /** 211 * Checks if given method declared as static. 212 * @param aAST method definition node 213 * @return true if given method is declared as static 214 */ 215 private boolean isStatic(DetailAST aAST) 216 { 217 final DetailAST modifiersAST = 218 aAST.findFirstToken(TokenTypes.MODIFIERS); 219 final DetailAST staticAST = 220 modifiersAST.findFirstToken(TokenTypes.LITERAL_STATIC); 221 222 return (staticAST != null); 223 } 224}