Move NPathComplexity from metrics to design

This commit is contained in:
Clément Fournier
2017-11-08 14:11:36 +01:00
parent aa3068fedd
commit f8bea7f580
9 changed files with 250 additions and 656 deletions

View File

@ -18,7 +18,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorDecorator;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
import net.sourceforge.pmd.lang.java.rule.design.NPathComplexityRule;
/**
* Decorator which counts the complexity of boolean expressions for Cyclo.
@ -94,7 +93,7 @@ public class CycloPathAwareDecorator extends JavaParserVisitorDecorator {
super.visit(node, data);
if (node.isTernary()) {
int boolCompTern = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompTern = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
((MutableInt) data).add(1 + boolCompTern);
}
return data;

View File

@ -1,53 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.metrics.rule;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
import net.sourceforge.pmd.properties.IntegerProperty;
/**
* Simple n-path complexity rule.
*
* @author Clément Fournier
*/
public class NPathComplexityRule extends AbstractJavaMetricsRule {
private static final IntegerProperty REPORT_LEVEL_DESCRIPTOR = new IntegerProperty(
"reportLevel", "N-Path Complexity reporting threshold", 1, 30, 200, 1.0f);
private static int reportLevel = 200;
public NPathComplexityRule() {
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
}
@Override
public Object visit(ASTCompilationUnit node, Object data) {
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
super.visit(node, data);
return data;
}
@Override
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, node);
if (npath >= reportLevel) {
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
node.getQualifiedName().getOperation(), "" + npath, });
}
return data;
}
}

View File

@ -11,6 +11,8 @@ import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
import net.sourceforge.pmd.lang.java.metrics.impl.CycloMetric;
/**
* @author Donald A. Leckie,
@ -25,7 +27,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
public Object visit(ASTIfStatement node, Object data) {
super.visit(node, data);
int boolCompIf = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompIf = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompIf);
return data;
}
@ -34,8 +36,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
public Object visit(ASTForStatement node, Object data) {
super.visit(node, data);
int boolCompFor = NPathComplexityRule
.sumExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
int boolCompFor = CycloMetric.booleanExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompFor);
return data;
}
@ -44,7 +45,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
public Object visit(ASTDoStatement node, Object data) {
super.visit(node, data);
int boolCompDo = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompDo = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompDo);
return data;
}
@ -53,7 +54,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
public Object visit(ASTSwitchStatement node, Object data) {
super.visit(node, data);
int boolCompSwitch = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompSwitch = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompSwitch);
return data;
}
@ -62,7 +63,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
public Object visit(ASTWhileStatement node, Object data) {
super.visit(node, data);
int boolCompWhile = NPathComplexityRule.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompWhile = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompWhile);
return data;
}
@ -72,8 +73,7 @@ public class CyclomaticComplexityRule extends StdCyclomaticComplexityRule {
super.visit(node, data);
if (node.isTernary()) {
int boolCompTern = NPathComplexityRule
.sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int boolCompTern = CycloMetric.booleanExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
entryStack.peek().bumpDecisionPoints(boolCompTern);
}
return data;

View File

@ -4,262 +4,52 @@
package net.sourceforge.pmd.lang.java.rule.design;
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
import net.sourceforge.pmd.lang.java.ast.ASTDoStatement;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTForStatement;
import net.sourceforge.pmd.lang.java.ast.ASTIfStatement;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.java.ast.ASTStatement;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchLabel;
import net.sourceforge.pmd.lang.java.ast.ASTSwitchStatement;
import net.sourceforge.pmd.lang.java.ast.ASTTryStatement;
import net.sourceforge.pmd.lang.java.ast.ASTWhileStatement;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.rule.AbstractStatisticalJavaRule;
import net.sourceforge.pmd.stat.DataPoint;
import net.sourceforge.pmd.util.NumericConstants;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.metrics.JavaMetrics;
import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
import net.sourceforge.pmd.properties.IntegerProperty;
/**
* NPath complexity is a measurement of the acyclic execution paths through a
* function. See Nejmeh, Communications of the ACM Feb 1988 pp 188-200.
*
* Simple n-path complexity rule.
*
* @author Clément Fournier
* @author Jason Bennett
*/
public class NPathComplexityRule extends AbstractStatisticalJavaRule {
public class NPathComplexityRule extends AbstractJavaMetricsRule {
private static final IntegerProperty REPORT_LEVEL_DESCRIPTOR = new IntegerProperty(
"reportLevel", "N-Path Complexity reporting threshold", 1, 30, 200, 1.0f);
private static int reportLevel = 200;
public NPathComplexityRule() {
super();
setProperty(MINIMUM_DESCRIPTOR, 200d);
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
}
private int complexityMultipleOf(JavaNode node, int npathStart, Object data) {
int npath = npathStart;
JavaNode n;
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
n = (JavaNode) node.jjtGetChild(i);
npath *= (Integer) n.jjtAccept(this, data);
}
return npath;
}
private int complexitySumOf(JavaNode node, int npathStart, Object data) {
int npath = npathStart;
JavaNode n;
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
n = (JavaNode) node.jjtGetChild(i);
npath += (Integer) n.jjtAccept(this, data);
}
return npath;
}
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
int npath = complexityMultipleOf(node, 1, data);
public Object visit(ASTCompilationUnit node, Object data) {
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
DataPoint point = new DataPoint();
point.setNode(node);
point.setScore(1.0 * npath);
point.setMessage(getMessage());
addDataPoint(point);
return Integer.valueOf(npath);
super.visit(node, data);
return data;
}
@Override
public Object visit(JavaNode node, Object data) {
int npath = complexityMultipleOf(node, 1, data);
return Integer.valueOf(npath);
}
@Override
public Object visit(ASTIfStatement node, Object data) {
// (npath of if + npath of else (or 1) + bool_comp of if) * npath of
// next
List<JavaNode> statementChildren = new ArrayList<>();
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
if (node.jjtGetChild(i).getClass() == ASTStatement.class) {
statementChildren.add((JavaNode) node.jjtGetChild(i));
}
public final Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
int npath = (int) JavaMetrics.get(JavaOperationMetricKey.NPATH, node);
if (npath >= reportLevel) {
addViolation(data, node, new String[] {node instanceof ASTMethodDeclaration ? "method" : "constructor",
node.getQualifiedName().getOperation(), "" + npath, });
}
if (statementChildren.isEmpty() || statementChildren.size() == 1 && node.hasElse()
|| statementChildren.size() != 1 && !node.hasElse()) {
throw new IllegalStateException("If node has wrong number of children");
}
// add path for not taking if
int complexity = 0;
if (!node.hasElse()) {
complexity++;
}
for (JavaNode element : statementChildren) {
complexity += (Integer) element.jjtAccept(this, data);
}
int boolCompIf = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
return Integer.valueOf(boolCompIf + complexity);
}
@Override
public Object visit(ASTWhileStatement node, Object data) {
// (npath of while + bool_comp of while + 1) * npath of next
int boolCompWhile = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
Integer nPathWhile = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
return Integer.valueOf(boolCompWhile + nPathWhile + 1);
}
@Override
public Object visit(ASTDoStatement node, Object data) {
// (npath of do + bool_comp of do + 1) * npath of next
int boolCompDo = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
Integer nPathDo = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
return Integer.valueOf(boolCompDo + nPathDo + 1);
}
@Override
public Object visit(ASTForStatement node, Object data) {
// (npath of for + bool_comp of for + 1) * npath of next
int boolCompFor = sumExpressionComplexity(node.getFirstDescendantOfType(ASTExpression.class));
Integer nPathFor = (Integer) ((JavaNode) node.getFirstChildOfType(ASTStatement.class)).jjtAccept(this, data);
return Integer.valueOf(boolCompFor + nPathFor + 1);
}
@Override
public Object visit(ASTReturnStatement node, Object data) {
// return statements are valued at 1, or the value of the boolean
// expression
ASTExpression expr = node.getFirstChildOfType(ASTExpression.class);
if (expr == null) {
return NumericConstants.ONE;
}
int boolCompReturn = sumExpressionComplexity(expr);
int conditionalExpressionComplexity = complexityMultipleOf(expr, 1, data);
if (conditionalExpressionComplexity > 1) {
boolCompReturn += conditionalExpressionComplexity;
}
if (boolCompReturn > 0) {
return Integer.valueOf(boolCompReturn);
}
return NumericConstants.ONE;
}
@Override
public Object visit(ASTSwitchStatement node, Object data) {
// bool_comp of switch + sum(npath(case_range))
int boolCompSwitch = sumExpressionComplexity(node.getFirstChildOfType(ASTExpression.class));
int npath = 0;
int caseRange = 0;
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
JavaNode n = (JavaNode) node.jjtGetChild(i);
// Fall-through labels count as 1 for complexity
if (n instanceof ASTSwitchLabel) {
npath += caseRange;
caseRange = 1;
} else {
Integer complexity = (Integer) n.jjtAccept(this, data);
caseRange *= complexity;
}
}
// add in npath of last label
npath += caseRange;
return Integer.valueOf(boolCompSwitch + npath);
}
@Override
public Object visit(ASTTryStatement node, Object data) {
/*
* This scenario was not addressed by the original paper. Based on the
* principles outlined in the paper, as well as the Checkstyle NPath
* implementation, this code will add the complexity of the try to the
* complexities of the catch and finally blocks.
*/
int npath = complexitySumOf(node, 0, data);
return Integer.valueOf(npath);
}
@Override
public Object visit(ASTConditionalExpression node, Object data) {
if (node.isTernary()) {
int npath = complexitySumOf(node, 0, data);
npath += 2;
return Integer.valueOf(npath);
}
return NumericConstants.ONE;
}
/**
* Calculate the boolean complexity of the given expression. NPath boolean
* complexity is the sum of &amp;&amp; and || tokens. This is calculated by summing
* the number of children of the &amp;&amp;'s (minus one) and the children of the
* ||'s (minus one).
*
* <p>Note that this calculation applies to Cyclomatic Complexity as well.</p>
*
* @param expr
* control structure expression
* @return complexity of the boolean expression
*/
public static int sumExpressionComplexity(ASTExpression expr) {
if (expr == null) {
return 0;
}
List<ASTConditionalAndExpression> andNodes = expr.findDescendantsOfType(ASTConditionalAndExpression.class);
List<ASTConditionalOrExpression> orNodes = expr.findDescendantsOfType(ASTConditionalOrExpression.class);
int children = 0;
for (ASTConditionalOrExpression element : orNodes) {
children += element.jjtGetNumChildren();
children--;
}
for (ASTConditionalAndExpression element : andNodes) {
children += element.jjtGetNumChildren();
children--;
}
return children;
}
@Override
public Object[] getViolationParameters(DataPoint point) {
return new String[] { ((ASTMethodDeclaration) point.getNode()).getMethodName(),
String.valueOf((int) point.getScore()), };
return data;
}
}

View File

@ -1025,45 +1025,57 @@ public class Foo extends Bar {
<rule name="NPathComplexity"
since="3.9"
message="The method {0}() has an NPath complexity of {1}"
message="The {0} ''{1}'' has an NPath complexity of {2}"
class="net.sourceforge.pmd.lang.java.rule.design.NPathComplexityRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_design.html#npathcomplexity">
<description>
The NPath complexity of a method is the number of acyclic execution paths through that method.
A threshold of 200 is generally considered the point where measures should be taken to reduce
complexity and increase readability.
The NPath complexity of a method is the number of acyclic execution paths through that method.
While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of
full paths from the beginning to the end of the block of the method. That metric grows exponentially, as
it multiplies the complexity of statements in the same block. For more details on the calculation, see the
documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath).
A threshold of 200 is generally considered the point where measures should be taken to reduce
complexity and increase readability.
</description>
<priority>3</priority>
<example>
<![CDATA[
void bar() { // this is something more complex than it needs to be,
if (y) { // it should be broken down into smaller methods or functions
for (j = 0; j < m; j++) {
if (j > r) {
doSomething();
while (f < 5 ) {
anotherThing();
f -= 27;
}
} else {
tryThis();
}
}
<![CDATA[
public class Foo {
public static void bar() { // Ncss = 252: reported!
boolean a, b = true;
try { // 2 * 2 + 2 = 6
if (true) { // 2
List buz = new ArrayList();
}
for(int i = 0; i < 19; i++) { // * 2
List buz = new ArrayList();
}
} catch(Exception e) {
if (true) { // 2
e.printStackTrace();
}
}
if ( r - n > 45) {
while (doMagic()) {
findRabbits();
}
while (j++ < 20) { // * 2
List buz = new ArrayList();
}
try {
doSomethingDangerous();
} catch (Exception ex) {
makeAmends();
} finally {
dontDoItAgain();
switch(j) { // * 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
do { // * 3
List buz = new ArrayList();
} while (a && j++ < 30);
}
}
]]>
]]>
</example>
</rule>

View File

@ -62,60 +62,5 @@ class Foo {
</example>
</rule>
<rule name="NPathComplexity"
since="3.9"
message="The {0} ''{1}'' has an NPath complexity of {2}"
class="net.sourceforge.pmd.lang.java.metrics.rule.NPathComplexityRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_metrics.html#NPathComplexity">
<description>
The NPath complexity of a method is the number of acyclic execution paths through that method.
While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of
full paths from the beginning to the end of the block of the method. That metric grows exponentially, as
it multiplies the complexity of statements in the same block. For more details on the calculation, see the
documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath).
A threshold of 200 is generally considered the point where measures should be taken to reduce
complexity and increase readability.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
public static void bar() { // Ncss = 252: reported!
boolean a, b = true;
try { // 2 * 2 + 2 = 6
if (true) { // 2
List buz = new ArrayList();
}
for(int i = 0; i < 19; i++) { // * 2
List buz = new ArrayList();
}
} catch(Exception e) {
if (true) { // 2
e.printStackTrace();
}
}
while (j++ < 20) { // * 2
List buz = new ArrayList();
}
switch(j) { // * 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
do { // * 3
List buz = new ArrayList();
} while (a && j++ < 30);
}
}
]]>
</example>
</rule>
</ruleset>

View File

@ -26,6 +26,5 @@ public class MetricsRulesTest extends SimpleAggregatorTst {
@Override
public void setUp() {
addRule(RULESET, "CyclomaticComplexity");
addRule(RULESET, "NPathComplexity");
}
}

View File

@ -3,96 +3,197 @@
xmlns="http://pmd.sourceforge.net/rule-tests"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
<test-code>
<description>ok</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
<code-fragment id="full-example"><![CDATA[
public class Foo {
public static void bar() {
if (true) {List buz = new ArrayList();}
}
public static void bar() {
boolean a, b = true;
try { // 2 * 2 + 2
if (true) { // 2
List buz = new ArrayList();
}
for(int i = 0; i < 19; i++) { // 2
List buz = new ArrayList();
}
} catch(Exception e) {
if (true) { // 2
e.printStackTrace();
}
}
int j = 0;
if (true || a && b) { // 4
j = 10;
return;
}
while (j++ < 20) { // 2
List buz = new ArrayList();
}
switch(j) { // 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
do { // 3
List buz = new ArrayList();
} while (a && j++ < 30);
if (b) { // 2
return;
}
}
}
]]></code>
</test-code>
]]></code-fragment>
<test-code>
<description>fail, with minimum</description>
<rule-property name="minimum">1.0</rule-property>
<description>Full example</description>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method bar() has an NPath complexity of 2</message>
<message>The method 'bar()' has an NPath complexity of 2016</message>
</expected-messages>
<code-ref id="full-example"/>
</test-code>
<test-code>
<description>Test default report level - report 200</description>
<rule-property name="reportLevel">0</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method 'bar()' has an NPath complexity of 200</message>
</expected-messages>
<code><![CDATA[
public class Foo {
public static void bar() {
if (true) {List buz = new ArrayList();}
}
}
]]></code>
public class Foo {
public static void bar() {
boolean a, b = true;
int j = 0;
switch (j) { // 5
case 0:
case 1:
case 3: if (a || b) {} break;
}
switch (j) { // * 5
case 0:
case 1:
case 3: if (a || b) {} break;
}
if (true || a && b); // * 4
while (j++ < 20); // * 2
}
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
failure case
]]></description>
<expected-problems>1</expected-problems>
<description>Test default report level - ignore 199</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class Foo {
public static int bar() {
try{
if (true) {List buz = new ArrayList();}
for(int i = 0; i < 19; i++) {List buz = new ArrayList();}
int j = 0;
if (true) {j = 10;}
while (j++ < 20) {List buz = new ArrayList();}
if (true) {j = 21;}
if(false) {j = 0;}
do {List buz = new ArrayList();} while (j++ < 30);
} catch(Exception e){
if (true) {e.printStackTrace();}
}
if (true) {return 1;}
else {return 2;}
}
}
]]></code>
class Foo {
void bar() {
boolean a, b;
try {
switch(j) { // 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
switch(j) { // * 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
if (true || a || b); // * 4
} catch (ScaryException e) {
if (true || a); // + 3
}
}
}
]]></code>
</test-code>
<test-code>
<description>Boolean operators</description>
<rule-property name="reportLevel">2</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method 'bar()' has an NPath complexity of 4</message>
</expected-messages>
<code><![CDATA[
class Foo {
void bar() {
if (a && b || c);
}
}
]]></code>
</test-code>
<code-fragment id="bug3484404"><![CDATA[
class Bar
{
public void x(boolean x, boolean y)
{
z(
(x ? 1 : 2),
(y ? 3 : 4)
);
}
class Bar {
public void x(boolean x, boolean y) {
z((x ? 1 : 2), (y ? 3 : 4));
}
public int y(boolean x, boolean y)
{
return z(
(x ? 1 : 2),
(y ? 3 : 4)
);
}
public int y(boolean x, boolean y) {
return z((x ? 1 : 2), (y ? 3 : 4));
}
public int z(int x, int y)
{
return x + y;
}
public int z(int x, int y) {
return x + y;
}
}
]]></code-fragment>
<test-code>
<description>test case for bug 3484404 (Invalid NPath calculation in return statement)</description>
<expected-problems>0</expected-problems>
<code-ref id="bug3484404"/>
</test-code>
<test-code>
<description>test case for bug 3484404 (Invalid NPath calculation in return statement) with minimum 25</description>
<rule-property name="minimum">25.0</rule-property>
<rule-property name="reportLevel">4</rule-property>
<expected-problems>2</expected-problems>
<expected-messages>
<message>The method x() has an NPath complexity of 25</message>
<message>The method y() has an NPath complexity of 25</message>
<message>The method 'x(boolean, boolean)' has an NPath complexity of 4</message>
<message>The method 'y(boolean, boolean)' has an NPath complexity of 4</message>
</expected-messages>
<code-ref id="bug3484404"/>
</test-code>
<test-code>
<description>NPath supports constructors</description>
<rule-property name="reportLevel">6</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The constructor 'Foo()' has an NPath complexity of 7</message>
</expected-messages>
<code><![CDATA[
class Foo {
Foo() {
boolean a, b;
int j = 23;
switch(j) {
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
}
}
]]></code>
</test-code>
</test-data>

View File

@ -1,199 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data
xmlns="http://pmd.sourceforge.net/rule-tests"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
<code-fragment id="full-example"><![CDATA[
public class Foo {
public static void bar() {
boolean a, b = true;
try { // 2 * 2 + 2
if (true) { // 2
List buz = new ArrayList();
}
for(int i = 0; i < 19; i++) { // 2
List buz = new ArrayList();
}
} catch(Exception e) {
if (true) { // 2
e.printStackTrace();
}
}
int j = 0;
if (true || a && b) { // 4
j = 10;
return;
}
while (j++ < 20) { // 2
List buz = new ArrayList();
}
switch(j) { // 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
do { // 3
List buz = new ArrayList();
} while (a && j++ < 30);
if (b) { // 2
return;
}
}
}
]]></code-fragment>
<test-code>
<description>Full example</description>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method 'bar()' has an NPath complexity of 2016</message>
</expected-messages>
<code-ref id="full-example"/>
</test-code>
<test-code>
<description>Test default report level - report 200</description>
<rule-property name="reportLevel">0</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method 'bar()' has an NPath complexity of 200</message>
</expected-messages>
<code><![CDATA[
public class Foo {
public static void bar() {
boolean a, b = true;
int j = 0;
switch (j) { // 5
case 0:
case 1:
case 3: if (a || b) {} break;
}
switch (j) { // * 5
case 0:
case 1:
case 3: if (a || b) {} break;
}
if (true || a && b); // * 4
while (j++ < 20); // * 2
}
}
]]></code>
</test-code>
<test-code>
<description>Test default report level - ignore 199</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class Foo {
void bar() {
boolean a, b;
try {
switch(j) { // 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
switch(j) { // * 7
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
if (true || a || b); // * 4
} catch (ScaryException e) {
if (true || a); // + 3
}
}
}
]]></code>
</test-code>
<test-code>
<description>Boolean operators</description>
<rule-property name="reportLevel">2</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The method 'bar()' has an NPath complexity of 4</message>
</expected-messages>
<code><![CDATA[
class Foo {
void bar() {
if (a && b || c);
}
}
]]></code>
</test-code>
<code-fragment id="bug3484404"><![CDATA[
class Bar {
public void x(boolean x, boolean y) {
z((x ? 1 : 2), (y ? 3 : 4));
}
public int y(boolean x, boolean y) {
return z((x ? 1 : 2), (y ? 3 : 4));
}
public int z(int x, int y) {
return x + y;
}
}
]]></code-fragment>
<test-code>
<description>test case for bug 3484404 (Invalid NPath calculation in return statement)</description>
<rule-property name="reportLevel">4</rule-property>
<expected-problems>2</expected-problems>
<expected-messages>
<message>The method 'x(boolean, boolean)' has an NPath complexity of 4</message>
<message>The method 'y(boolean, boolean)' has an NPath complexity of 4</message>
</expected-messages>
<code-ref id="bug3484404"/>
</test-code>
<test-code>
<description>NPath supports constructors</description>
<rule-property name="reportLevel">6</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>The constructor 'Foo()' has an NPath complexity of 7</message>
</expected-messages>
<code><![CDATA[
class Foo {
Foo() {
boolean a, b;
int j = 23;
switch(j) {
case 1:
case 2: break;
case 3: j = 5; break;
case 4: if (b && a) { bar(); } break;
default: break;
}
}
}
]]></code>
</test-code>
</test-data>