001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.rules.migration;
004:
005: import net.sourceforge.pmd.ast.ASTAnnotation;
006: import net.sourceforge.pmd.ast.ASTBlock;
007: import net.sourceforge.pmd.ast.ASTBlockStatement;
008: import net.sourceforge.pmd.ast.ASTCatchStatement;
009: import net.sourceforge.pmd.ast.ASTClassOrInterfaceBodyDeclaration;
010: import net.sourceforge.pmd.ast.ASTMethodDeclaration;
011: import net.sourceforge.pmd.ast.ASTName;
012: import net.sourceforge.pmd.ast.ASTThrowStatement;
013: import net.sourceforge.pmd.ast.ASTTryStatement;
014: import net.sourceforge.pmd.ast.Node;
015: import net.sourceforge.pmd.ast.SimpleNode;
016: import net.sourceforge.pmd.rules.junit.AbstractJUnitRule;
017:
018: import java.util.ArrayList;
019: import java.util.List;
020:
021: /**
022: * This rule finds code like this:
023: *
024: * <pre>
025: * public void testFoo() {
026: * try {
027: * doSomething();
028: * fail("should have thrown an exception");
029: * } catch (Exception e) {
030: * }
031: * }
032: * </pre>
033: *
034: * In JUnit 4, use
035: *
036: * <pre>
037: * @Test(expected = Exception.class)
038: * </pre>
039: *
040: * @author acaplan
041: *
042: */
043: public class JUnitUseExpected extends AbstractJUnitRule {
044:
045: public Object visit(ASTClassOrInterfaceBodyDeclaration node,
046: Object data) {
047: boolean inAnnotation = false;
048: for (int i = 0; i < node.jjtGetNumChildren(); i++) {
049: Node child = node.jjtGetChild(i);
050: if (ASTAnnotation.class.equals(child.getClass())) {
051: ASTName annotationName = ((SimpleNode) child)
052: .getFirstChildOfType(ASTName.class);
053: if ("Test".equals(annotationName.getImage())) {
054: inAnnotation = true;
055: continue;
056: }
057: }
058: if (ASTMethodDeclaration.class.equals(child.getClass())) {
059: boolean isJUnitMethod = isJUnitMethod(
060: (ASTMethodDeclaration) child, data);
061: if (inAnnotation || isJUnitMethod) {
062: List<SimpleNode> found = new ArrayList<SimpleNode>();
063: found.addAll((List<SimpleNode>) visit(
064: (ASTMethodDeclaration) child, data));
065: for (SimpleNode name : found) {
066: addViolation(data, name);
067: }
068: }
069: }
070: inAnnotation = false;
071: }
072:
073: return super .visit(node, data);
074: }
075:
076: public Object visit(ASTMethodDeclaration node, Object data) {
077: List<ASTTryStatement> catches = node
078: .findChildrenOfType(ASTTryStatement.class);
079: List<SimpleNode> found = new ArrayList<SimpleNode>();
080: if (catches.isEmpty()) {
081: return found;
082: }
083: for (ASTTryStatement trySt : catches) {
084: ASTCatchStatement cStatement = getCatch(trySt);
085: if (cStatement != null) {
086: ASTBlock block = (ASTBlock) cStatement.jjtGetChild(1);
087: if (block.jjtGetNumChildren() != 0) {
088: continue;
089: }
090: List<ASTBlockStatement> blocks = ((SimpleNode) trySt
091: .jjtGetChild(0))
092: .findChildrenOfType(ASTBlockStatement.class);
093: if (blocks.isEmpty()) {
094: continue;
095: }
096: ASTBlockStatement st = blocks.get(blocks.size() - 1);
097: ASTName name = st.getFirstChildOfType(ASTName.class);
098: if (name != null && st.equals(name.getNthParent(5))
099: && "fail".equals(name.getImage())) {
100: found.add(name);
101: continue;
102: }
103: ASTThrowStatement th = st
104: .getFirstChildOfType(ASTThrowStatement.class);
105: if (th != null && st.equals(th.getNthParent(2))) {
106: found.add(th);
107: continue;
108: }
109: }
110: }
111: return found;
112: }
113:
114: private ASTCatchStatement getCatch(Node n) {
115: for (int i = 0; i < n.jjtGetNumChildren(); i++) {
116: if (n.jjtGetChild(i) instanceof ASTCatchStatement) {
117: return (ASTCatchStatement) n.jjtGetChild(i);
118: }
119: }
120: return null;
121: }
122: }
|