Tests for NCSS + reduced visitor adapter

This commit is contained in:
oowekyala
2017-06-23 23:55:16 +02:00
parent 4ec86558a7
commit 6ab9088759
15 changed files with 700 additions and 69 deletions

View File

@ -0,0 +1,39 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
/**
* @author Clément Fournier
*/
public class JavaParserVisitorReducedAdapter extends JavaParserVisitorAdapter {
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
return visit((ASTAnyTypeDeclaration) node, data);
}
public Object visit(ASTEnumDeclaration node, Object data) {
return visit((ASTAnyTypeDeclaration) node, data);
}
public Object visit(ASTAnyTypeDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTMethodDeclaration node, Object data) {
return visit((ASTMethodOrConstructorDeclaration) node, data);
}
public Object visit(ASTConstructorDeclaration node, Object data) {
return visit((ASTMethodOrConstructorDeclaration) node, data);
}
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.List;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.AccessNode;
@ -128,9 +127,8 @@ public abstract class AbstractMetric implements Metric {
return node.findDescendantsOfType(ASTMethodOrConstructorDeclaration.class);
}
ASTClassOrInterfaceBody body = (ASTClassOrInterfaceBody) node.jjtGetChild(0);
List<ASTClassOrInterfaceBodyDeclaration> outerDecls
= body.findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class);
= node.jjtGetChild(0).findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class);
List<ASTMethodOrConstructorDeclaration> operations = new ArrayList<>();

View File

@ -12,7 +12,6 @@ import java.util.Map;
import java.util.Set;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.QualifiedName;
import net.sourceforge.pmd.lang.java.oom.api.ClassMetric;

View File

@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.oom;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.oom.api.ClassMetricKey;
import net.sourceforge.pmd.lang.java.oom.api.Metric.Version;

View File

@ -6,12 +6,10 @@ package net.sourceforge.pmd.lang.java.oom;
import java.util.Stack;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorReducedAdapter;
import net.sourceforge.pmd.lang.java.oom.signature.FieldSignature;
import net.sourceforge.pmd.lang.java.oom.signature.OperationSignature;
@ -21,12 +19,12 @@ import net.sourceforge.pmd.lang.java.oom.signature.OperationSignature;
*
* @author Clément Fournier
*/
class MetricsVisitor extends JavaParserVisitorAdapter {
class MetricsVisitor extends JavaParserVisitorReducedAdapter {
private Stack<ClassStats> stack = new Stack<>();
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
public Object visit(ASTAnyTypeDeclaration node, Object data) {
stack.push(((PackageStats) data).getClassStats(node.getQualifiedName(), true));
super.visit(node, data);
stack.pop();
@ -34,26 +32,13 @@ class MetricsVisitor extends JavaParserVisitorAdapter {
return data;
}
@Override
public Object visit(ASTEnumDeclaration node, Object data) {
stack.push(((PackageStats) data).getClassStats(node.getQualifiedName(), true));
super.visit(node, data);
stack.pop();
return data;
}
@Override
public Object visit(ASTConstructorDeclaration node, Object data) {
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
stack.peek().addOperation(node.getQualifiedName().getOperation(), OperationSignature.buildFor(node));
return super.visit(node, data);
}
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
stack.peek().addOperation(node.getQualifiedName().getOperation(), OperationSignature.buildFor(node));
return super.visit(node, data);
}
@Override
public Object visit(ASTFieldDeclaration node, Object data) {

View File

@ -8,7 +8,6 @@ import java.util.HashMap;
import java.util.Map;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.QualifiedName;
import net.sourceforge.pmd.lang.java.oom.api.ClassMetricKey;

View File

@ -4,7 +4,7 @@
package net.sourceforge.pmd.lang.java.oom.api;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.oom.metrics.AtfdMetric;
import net.sourceforge.pmd.lang.java.oom.metrics.CycloMetric;
import net.sourceforge.pmd.lang.java.oom.metrics.LocMetric;
@ -37,7 +37,7 @@ public enum ClassMetricKey implements MetricKey<ClassMetric> {
return calculator;
}
public boolean supports(ASTClassOrInterfaceDeclaration node) {
public boolean supports(ASTAnyTypeDeclaration node) {
return calculator.supports(node);
}

View File

@ -0,0 +1,47 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
/**
* Java Rule with convenient visit methods to e.g. treat contructors and methods the same.
*
* @author Clément Fournier
*/
public abstract class AbstractJavaMetricsRule extends AbstractJavaRule {
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
return visit((ASTAnyTypeDeclaration) node, data);
}
public Object visit(ASTEnumDeclaration node, Object data) {
return visit((ASTAnyTypeDeclaration) node, data);
}
public Object visit(ASTAnyTypeDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
public Object visit(ASTMethodDeclaration node, Object data) {
return visit((ASTMethodOrConstructorDeclaration) node, data);
}
public Object visit(ASTConstructorDeclaration node, Object data) {
return visit((ASTMethodOrConstructorDeclaration) node, data);
}
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
return visit((JavaNode) node, data);
}
}

View File

@ -124,7 +124,6 @@ public class SignatureTest extends ParserTst {
compilationUnit.jjtAccept(new JavaParserVisitorAdapter() {
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
System.err.println(node.getMethodName());
assertEquals(Role.GETTER_OR_SETTER, Role.get(node));
return data;
}

View File

@ -4,41 +4,37 @@
package net.sourceforge.pmd.lang.java.oom.metrics;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration;
import net.sourceforge.pmd.lang.java.oom.Metrics;
import net.sourceforge.pmd.lang.java.oom.api.ClassMetricKey;
import net.sourceforge.pmd.lang.java.oom.api.Metric;
import net.sourceforge.pmd.lang.java.oom.api.Metric.Version;
import net.sourceforge.pmd.lang.java.oom.api.MetricVersion;
import net.sourceforge.pmd.lang.java.oom.api.OperationMetricKey;
import net.sourceforge.pmd.lang.java.oom.api.ResultOption;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule;
import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
import net.sourceforge.pmd.lang.rule.properties.DoubleProperty;
/**
* Abstract test for a metric.
* Abstract test rule for a metric. Tests of metrics use the standard framework for rule testing, using one dummy rule
* per metric. Default parameters can be overriden by overriding the protected methods of this class.
*
* @author Clément Fournier
*/
public abstract class AbstractMetricTestRule extends AbstractJavaRule {
public abstract class AbstractMetricTestRule extends AbstractJavaMetricsRule {
public static final BooleanProperty REPORT_CLASSES_DESCRIPTOR = new BooleanProperty(
"reportClasses", "Add class violations to the report", true, 2.0f);
public static final BooleanProperty REPORT_METHODS_DESCRIPTOR = new BooleanProperty(
"reportMethods", "Add method violations to the report", true, 3.0f);
public static final DoubleProperty REPORT_LEVEL_DESCRIPTOR = new DoubleProperty(
"reportLevel", "Minimum value required to report", -1., Double.POSITIVE_INFINITY, 0., 3.0f);
protected boolean reportClasses = true;
protected boolean reportMethods = true;
protected double reportLevel = 0.;
protected MetricVersion version = Metric.Version.STANDARD;
private final BooleanProperty reportClassesDescriptor = new BooleanProperty(
"reportClasses", "Add class violations to the report", isReportClasses(), 2.0f);
private final BooleanProperty reportMethodsDescriptor = new BooleanProperty(
"reportMethods", "Add method violations to the report", isReportMethods(), 3.0f);
private final DoubleProperty reportLevelDescriptor = new DoubleProperty(
"reportLevel", "Minimum value required to report", -1., Double.POSITIVE_INFINITY, defaultReportLevel(), 3.0f);
protected MetricVersion version;
private boolean reportClasses;
private boolean reportMethods;
private double reportLevel;
private ClassMetricKey classKey;
private OperationMetricKey opKey;
@ -46,26 +42,71 @@ public abstract class AbstractMetricTestRule extends AbstractJavaRule {
classKey = getClassKey();
opKey = getOpKey();
definePropertyDescriptor(REPORT_CLASSES_DESCRIPTOR);
definePropertyDescriptor(REPORT_METHODS_DESCRIPTOR);
definePropertyDescriptor(REPORT_LEVEL_DESCRIPTOR);
definePropertyDescriptor(reportClassesDescriptor);
definePropertyDescriptor(reportMethodsDescriptor);
definePropertyDescriptor(reportLevelDescriptor);
}
public Object visit(ASTCompilationUnit node, Object data) {
reportClasses = getProperty(REPORT_CLASSES_DESCRIPTOR);
reportMethods = getProperty(REPORT_METHODS_DESCRIPTOR);
reportLevel = getProperty(REPORT_LEVEL_DESCRIPTOR);
reportClasses = getProperty(reportClassesDescriptor);
reportMethods = getProperty(reportMethodsDescriptor);
reportLevel = getProperty(reportLevelDescriptor);
return super.visit(node, data);
}
/**
* Sets the default for reportClasses descriptor.
*
* @return The default for reportClasses descriptor
*/
protected boolean isReportClasses() {
return true;
}
/**
* Sets the default for reportMethods descriptor.
*
* @return The default for reportMethods descriptor
*/
protected boolean isReportMethods() {
return true;
}
/**
* Sets the version to test
*
* @return The version to test
*/
protected MetricVersion version() {
return Version.STANDARD;
}
/**
* Returns the class metric key to test, or null if we shouldn't test classes.
*
* @return The class metric key to test.
*/
protected abstract ClassMetricKey getClassKey();
/**
* Returns the class metric key to test, or null if we shouldn't test classes.
*
* @return The class metric key to test.
*/
protected abstract OperationMetricKey getOpKey();
/**
* Default report level, which is 0.
*
* @return The default report level.
*/
protected double defaultReportLevel() {
return 0.;
}
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
public Object visit(ASTAnyTypeDeclaration node, Object data) {
if (classKey != null && reportClasses && classKey.supports(node)) {
int classValue = (int) Metrics.get(classKey, node, version);
@ -82,17 +123,9 @@ public abstract class AbstractMetricTestRule extends AbstractJavaRule {
return super.visit(node, data);
}
@Override
public Object visit(ASTMethodDeclaration node, Object data) {
return processMethodOrConstructorDeclaration(node, data);
}
@Override
public Object visit(ASTConstructorDeclaration node, Object data) {
return processMethodOrConstructorDeclaration(node, data);
}
protected Object processMethodOrConstructorDeclaration(ASTMethodOrConstructorDeclaration node, Object data) {
public Object visit(ASTMethodOrConstructorDeclaration node, Object data) {
if (opKey != null && reportMethods && opKey.supports(node)) {
int methodValue = (int) Metrics.get(opKey, node, version);
if (methodValue >= reportLevel) {

View File

@ -15,9 +15,9 @@ public class AllMetricsTest extends SimpleAggregatorTst {
@Override
public void setUp() {
addRule(RULESET, "CyclomaticComplexity");
addRule(RULESET, "CycloTest");
addRule(RULESET, "NcssTest");
}
}

View File

@ -0,0 +1,29 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.oom.metrics;
import net.sourceforge.pmd.lang.java.oom.api.ClassMetricKey;
import net.sourceforge.pmd.lang.java.oom.api.OperationMetricKey;
/**
* @author Clément Fournier
*/
public class NcssTestRule extends AbstractMetricTestRule {
@Override
protected boolean isReportClasses() {
return false;
}
@Override
protected ClassMetricKey getClassKey() {
return ClassMetricKey.NCSS;
}
@Override
protected OperationMetricKey getOpKey() {
return OperationMetricKey.NCSS;
}
}

View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data>
<code-fragment id="basic-violation">
<![CDATA[
public class Complicated {
public void example() {
int x = 0, y = 1, z = 2, t = 2;
boolean a = false, b = true, c = false, d = true;
if (a && b || b && d) {
if (y == z) {
x = 2;
} else if (y == t && !d) {
x = 2;
} else {
x = 2;
}
} else if (c && d) {
while (z < y) {
x = 2;
}
} else if (a && !b) {
for (int n = 0; n < t; n++) {
x = 2;
}
} else {
switch (x) {
case 1:
x = 2;
break;
case 2:
case 3:
x = 2;
break;
default:
x = 2;
break;
}
}
}
}
]]>
</code-fragment>
<test-code>
<description>Complicated method</description>
<expected-problems>2</expected-problems>
<expected-messages>
<message>'.Complicated' has value 18 highest 17.</message>
<message>'.Complicated#example()' has value 17.</message>
</expected-messages>
<code-ref id="basic-violation"/>
</test-code>
<test-code>
<description>Empty methods should count 1</description>
<rule-property name="reportClasses">false</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>'.Foo#foo()' has value 1.</message>
</expected-messages>
<code>
<![CDATA[
class Foo {
void foo() {}
}
]]>
</code>
</test-code>
<test-code>
<description>Empty class should count 1</description>
<expected-problems>1</expected-problems>
<expected-messages>
<message>'.Foo' has value 0 highest 0.</message>
</expected-messages>
<code>
<![CDATA[
interface Foo {
}
]]>
</code>
</test-code>
<code-fragment id="constructor-violation">
<![CDATA[
public class Test {
public Test() {
if (a == 1) {
if (b == 2) {
System.out.println("b");
} else if (b == 1) {
}
} else {
}
}
}
]]>
</code-fragment>
<test-code>
<description>#984 Cyclomatic complexity should treat constructors like methods
</description>
<rule-property name="reportClasses">false</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>'.Test#Test()' has value 4.</message>
</expected-messages>
<code-ref id="constructor-violation"/>
</test-code>
<code-fragment id="fallthroughSwitch">
<![CDATA[
class Foo {
void foo(){}
void bar(){}
void switchCase() {
int x=0;
switch (x) {
case 1:
case 2: foo(); break;
default: bar(); break;
}
}
}
]]>
</code-fragment>
<test-code>
<description>Standard Cyclo should count empty switch labels too</description>
<rule-property name="reportClasses">false</rule-property>
<rule-property name="reportLevel">2</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>
<message>'.Foo#switchCase()' has value 3.</message>
</expected-messages>
<code-ref id="fallthroughSwitch"/>
</test-code>
<code-fragment id="manyBooleanOps">
<![CDATA[
class Foo {
void foo(){
int x=0, y=1;
boolean a, b;
if (x > 2 || y < 4) {
while (x++ < 10 && !(y-- < 0));
} else if (a && b || x < 4) {
return;
}
}
}
]]>
</code-fragment>
<test-code>
<description>Standard Cyclo should count boolean paths</description>
<expected-problems>1</expected-problems>
<rule-property name="reportClasses">false</rule-property>
<expected-messages>
<message>'.Foo#foo()' has value 8.</message>
</expected-messages>
<code-ref id="manyBooleanOps"/>
</test-code>
<test-code>
<description>Avoid division by 0 when averaging a metric over no operations</description>
<rule-property name="reportLevel">-1</rule-property>
<expected-problems>1</expected-problems>
<expected-messages>'.Foo' has value NaN.</expected-messages>
<code>
<![CDATA[
public class Foo { }
]]>
</code>
</test-code>
</test-data>

View File

@ -9,9 +9,16 @@
Metrics testing ruleset.
</description>
<rule name="CyclomaticComplexity"
<rule name="CycloTest"
message = "''{0}'' has value {1}."
class="net.sourceforge.pmd.lang.java.oom.metrics.CycloTestRule"
metrics="true">
</rule>
<rule name="NcssTest"
message = "''{0}'' has value {1}."
class="net.sourceforge.pmd.lang.java.oom.metrics.NcssTestRule"
metrics="true">
</rule>
</ruleset>