View Javadoc

1   /***
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.rules.junit;
5   
6   import net.sourceforge.pmd.AbstractRule;
7   import net.sourceforge.pmd.Rule;
8   import net.sourceforge.pmd.RuleContext;
9   import net.sourceforge.pmd.ast.ASTBlock;
10  import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
11  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
12  import net.sourceforge.pmd.ast.ASTMethodDeclarator;
13  import net.sourceforge.pmd.ast.ASTName;
14  import net.sourceforge.pmd.ast.ASTPrimaryExpression;
15  import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
16  import net.sourceforge.pmd.ast.ASTResultType;
17  import net.sourceforge.pmd.ast.ASTStatementExpression;
18  import net.sourceforge.pmd.ast.Node;
19  
20  public class JUnitTestsShouldContainAsserts extends AbstractRule implements Rule {
21  
22      public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
23          if (node.isInterface()) {
24              return data;
25          }
26          return super.visit(node, data);
27      }
28  
29  /*
30  FIXME
31  	public Object visit(ASTNestedInterfaceDeclaration node, Object data) {
32  		// skip also internal interfaces, bug [ 1146116 ] JUnitTestsShouldIncludeAssert crashes on inner Interface
33  		return data;
34  	}
35  */
36  
37      public Object visit(ASTMethodDeclaration declaration, Object data) {
38          if (!declaration.isPublic() || declaration.isAbstract() || declaration.isNative()) {
39              return data; // skip various inapplicable method variations
40          }
41  
42          if (declaration.jjtGetNumChildren()==3) {
43              ASTResultType resultType = (ASTResultType) declaration.jjtGetChild(0);
44              ASTMethodDeclarator declarator = (ASTMethodDeclarator) declaration.jjtGetChild(1);
45              ASTBlock block = (ASTBlock) declaration.jjtGetChild(2);
46              if (resultType.isVoid() && declarator.getImage().startsWith("test")) {
47                  if (!hasAssertStatement(block)) {
48                      RuleContext ctx = (RuleContext)data;
49                      ctx.getReport().addRuleViolation(createRuleViolation(ctx, declaration));
50                  }
51              }
52          }
53  		return data;
54  	}
55  	
56      private boolean hasAssertStatement(ASTBlock block) {
57          return containsAssert(block, false);
58  	}
59  	
60      private boolean containsAssert(Node n, boolean assertFound) {
61          if (!assertFound) {
62              if (n instanceof ASTStatementExpression) {
63                  if (isAssertOrFailStatement((ASTStatementExpression)n)) {
64                      return true;
65                  }
66              }
67              if (!assertFound) {
68                  for (int i=0;i<n.jjtGetNumChildren() && ! assertFound;i++) {
69                      Node c = n.jjtGetChild(i);
70                      if (containsAssert(c, assertFound)) 
71                          return true;
72                  }
73              }
74          }
75          return false;
76      }
77  
78      /***
79       * Tells if the expression is an assert statement or not.
80       */
81      private boolean isAssertOrFailStatement(ASTStatementExpression expression) {
82          if (expression!=null 
83                  && expression.jjtGetNumChildren()>0
84                  && expression.jjtGetChild(0) instanceof ASTPrimaryExpression
85                  ) {
86              ASTPrimaryExpression pe = (ASTPrimaryExpression) expression.jjtGetChild(0);
87              if (pe.jjtGetNumChildren()> 0
88                      && pe.jjtGetChild(0) instanceof ASTPrimaryPrefix) {
89                  ASTPrimaryPrefix pp = (ASTPrimaryPrefix) pe.jjtGetChild(0);
90                  if (pp.jjtGetNumChildren()>0 && pp.jjtGetChild(0) instanceof ASTName) {
91                      ASTName n = (ASTName) pp.jjtGetChild(0);
92                      if (n.getImage()!=null && (n.getImage().startsWith("assert") || n.getImage().startsWith("fail") )) {
93                          return true;
94                      }
95                  }
96              }
97          }
98          return false;
99      }
100 }