View Javadoc
1   /*
2    * Copyright (c) 2011-2024 Qulice.com
3    *
4    * All rights reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions
8    * are met: 1) Redistributions of source code must retain the above
9    * copyright notice, this list of conditions and the following
10   * disclaimer. 2) Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following
12   * disclaimer in the documentation and/or other materials provided
13   * with the distribution. 3) Neither the name of the Qulice.com nor
14   * the names of its contributors may be used to endorse or promote
15   * products derived from this software without specific prior written
16   * permission.
17   *
18   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
20   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29   * OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  package com.qulice.pmd.rules;
32  
33  import net.sourceforge.pmd.lang.ast.Node;
34  import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
35  import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
36  import net.sourceforge.pmd.lang.java.ast.ASTName;
37  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
38  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
39  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
40  
41  /**
42   * Rule to check plain assertions in JUnit tests.
43   * @since 0.17
44   */
45  @SuppressWarnings("deprecation")
46  public final class ProhibitPlainJunitAssertionsRule
47      extends net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule {
48  
49      /**
50       * Mask of prohibited imports.
51       */
52      private static final String[] PROHIBITED = {
53          "org.junit.Assert.assert",
54          "junit.framework.Assert.assert",
55      };
56  
57      @Override
58      public Object visit(final ASTMethodDeclaration method, final Object data) {
59          if (this.isJUnitMethod(method, data)
60              && this.containsPlainJunitAssert(method.getBody())) {
61              this.asCtx(data).addViolation(method);
62          }
63          return data;
64      }
65  
66      @Override
67      public Object visit(final ASTImportDeclaration imp, final Object data) {
68          for (final String element : ProhibitPlainJunitAssertionsRule
69              .PROHIBITED) {
70              if (imp.getImportedName().contains(element)) {
71                  this.asCtx(data).addViolation(imp);
72                  break;
73              }
74          }
75          return super.visit(imp, data);
76      }
77  
78      /**
79       * Recursively verifies if node contains plain JUnit assert statements.
80       * @param node Root statement node to search
81       * @return True if statement contains plain JUnit assertions, false
82       *  otherwise
83       */
84      private boolean containsPlainJunitAssert(final Node node) {
85          boolean found = false;
86          if (node instanceof ASTStatementExpression
87              && ProhibitPlainJunitAssertionsRule.isPlainJunitAssert(node)) {
88              found = true;
89          }
90          if (!found) {
91              for (int iter = 0; iter < node.jjtGetNumChildren(); iter += 1) {
92                  final Node child = node.jjtGetChild(iter);
93                  if (this.containsPlainJunitAssert(child)) {
94                      found = true;
95                      break;
96                  }
97              }
98          }
99          return found;
100     }
101 
102     /**
103      * Tells if the statement is an assert statement or not.
104      * @param statement Root node to search assert statements
105      * @return True is statement is assert, false otherwise
106      */
107     private static boolean isPlainJunitAssert(final Node statement) {
108         final ASTPrimaryExpression expression =
109             ProhibitPlainJunitAssertionsRule.getChildNodeWithType(
110                 statement, ASTPrimaryExpression.class
111             );
112         final ASTPrimaryPrefix prefix =
113             ProhibitPlainJunitAssertionsRule.getChildNodeWithType(
114                 expression, ASTPrimaryPrefix.class
115             );
116         final ASTName name = ProhibitPlainJunitAssertionsRule
117             .getChildNodeWithType(prefix, ASTName.class);
118         boolean assrt = false;
119         if (name != null) {
120             final String img = name.getImage();
121             assrt = img != null && (img.startsWith("assert")
122                 || img.startsWith("Assert.assert"));
123         }
124         return assrt;
125     }
126 
127     /**
128      * Gets child node with specified type.
129      * @param node Parent node
130      * @param clazz Specified class
131      * @param <T> Node type
132      * @return Child node if exists, null otherwise
133      */
134     private static <T extends Node> T getChildNodeWithType(final Node node,
135         final Class<T> clazz) {
136         T expression = null;
137         if (node != null && node.jjtGetNumChildren() > 0
138             && clazz.isInstance(node.jjtGetChild(0))) {
139             expression = clazz.cast(node.jjtGetChild(0));
140         }
141         return expression;
142     }
143 
144 }