Skip to content

Commit

Permalink
#85 more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Jan 14, 2018
1 parent d2f7afb commit 15ded43
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 31 deletions.
10 changes: 2 additions & 8 deletions src/main/java/org/jpeek/Report.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,15 @@ public void save(final Path target) throws IOException {
new TeeInput(
xml.toString(),
target.resolve(
String.format(
"%s.xml",
this.metric.getClass().getSimpleName()
)
String.format("%s.xml", this.metric)
)
)
).intValue();
new LengthOf(
new TeeInput(
Report.STYLESHEET.transform(xml).toString(),
target.resolve(
String.format(
"%s.html",
this.metric.getClass().getSimpleName()
)
String.format("%s.html", this.metric)
)
)
).intValue();
Expand Down
58 changes: 41 additions & 17 deletions src/main/java/org/jpeek/Skeleton.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.cactoos.map.MapEntry;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.signature.SignatureReader;
Expand All @@ -62,7 +63,7 @@
*
* @author Yegor Bugayenko (yegor256@gmail.com)
* @version $Id$
* @see <a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A metrics suite for object oriented design</a>
* @see <a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A packages suite for object oriented design</a>
* @since 0.23
* @checkstyle AbbreviationAsWordInNameCheck (5 lines)
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
Expand Down Expand Up @@ -117,7 +118,7 @@ public XML xml() throws IOException {
.attr("id", ent.getKey())
.append(ent.getValue())
.up(),
this.metrics()
this.packages()
)
)
)
Expand All @@ -128,16 +129,16 @@ public XML xml() throws IOException {
}

/**
* Calculate metrics for all classes.
* @return Metrics
* Calculate Xembly for all packages.
* @return XML for all packages (one by one)
* @throws IOException If fails
*/
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
private Iterable<Map.Entry<String, Directives>> metrics()
private Iterable<Map.Entry<String, Directives>> packages()
throws IOException {
final Map<String, Directives> map = new HashMap<>(0);
final Iterable<Map.Entry<String, Directives>> all = new Mapped<>(
Skeleton::metric,
Skeleton::xembly,
new Filtered<>(
// @checkstyle BooleanExpressionComplexityCheck (10 lines)
ctClass -> !ctClass.isInterface()
Expand All @@ -148,7 +149,7 @@ private Iterable<Map.Entry<String, Directives>> metrics()
new Mapped<>(
path -> {
try (InputStream stream =
new FileInputStream(path.toFile())) {
new FileInputStream(path.toFile())) {
return this.pool.makeClassIfNew(stream);
}
},
Expand All @@ -168,11 +169,11 @@ private Iterable<Map.Entry<String, Directives>> metrics()
}

/**
* Calculate metrics for a single .class file.
* Calculate Xembly for a single .class file.
* @param ctc The class
* @return Metrics
*/
private static Map.Entry<String, Directives> metric(final CtClass ctc) {
private static Map.Entry<String, Directives> xembly(final CtClass ctc) {
ctc.defrost();
String pkg = ctc.getPackageName();
if (pkg == null) {
Expand All @@ -183,7 +184,7 @@ private static Map.Entry<String, Directives> metric(final CtClass ctc) {
new Directives()
.add("class")
.attr("id", ctc.getSimpleName())
.append(Skeleton.xembly(ctc))
.append(Skeleton.details(ctc))
.up()
);
}
Expand All @@ -195,21 +196,41 @@ private static Map.Entry<String, Directives> metric(final CtClass ctc) {
* @checkstyle ParameterNumberCheck (200 lines)
* @checkstyle AnonInnerLengthCheck (200 lines)
*/
private static Iterable<Directive> xembly(final CtClass ctc) {
private static Iterable<Directive> details(final CtClass ctc) {
final ClassReader reader;
try {
reader = new ClassReader(ctc.toBytecode());
} catch (final IOException | CannotCompileException ex) {
throw new IllegalStateException(ex);
}
final Directives dirs = new Directives().add("methods");
final Directives dirs = new Directives();
reader.accept(
new ClassVisitor(Opcodes.ASM6) {
@Override
public FieldVisitor visitField(final int access,
final String name, final String desc,
final String signature, final Object value) {
dirs.addIf("attributes")
.add("attribute")
.set(name)
.attr("type", desc.replaceAll(";$", ""))
.attr(
"public",
(access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC
)
.attr(
"static",
(access & Opcodes.ACC_STATIC) == Opcodes.ACC_STATIC
)
.up().up();
return super.visitField(access, name, desc, signature, value);
}
@Override
public MethodVisitor visitMethod(final int access,
final String mtd, final String desc,
final String signature, final String[] exceptions) {
dirs.add("method")
dirs.addIf("methods")
.add("method")
.attr("name", mtd)
.attr("desc", desc)
.attr(
Expand Down Expand Up @@ -247,7 +268,7 @@ public void visitBaseType(final char name) {
for (final String type : types) {
dirs.add("arg").set("?").attr("type", type).up();
}
dirs.up().up();
dirs.up().up().up();
return new MethodVisitor(
Opcodes.ASM6, super.visitMethod(
access, mtd, desc, signature, exceptions
Expand All @@ -259,8 +280,11 @@ public void visitFieldInsn(final int opcode,
final String dsc) {
super.visitFieldInsn(opcode, owner, attr, dsc);
dirs.xpath(
String.format("methods/method[@desc='%s']", desc)
).addIf("ops").add("op");
String.format(
"methods/method[@name='%s' and @desc='%s']",
mtd, desc
)
).strict(1).addIf("ops").add("op");
if (opcode == Opcodes.GETFIELD) {
dirs.attr("code", "get");
} else if (opcode == Opcodes.PUTFIELD) {
Expand All @@ -270,7 +294,7 @@ public void visitFieldInsn(final int opcode,
} else if (opcode == Opcodes.PUTSTATIC) {
dirs.attr("code", "put_static");
}
dirs.set(attr).up().up();
dirs.set(attr).up().up().up().up();
}
};
}
Expand Down
65 changes: 65 additions & 0 deletions src/main/resources/org/jpeek/metrics/LCOM.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?xml version="1.0"?>
<!--
The MIT License (MIT)
Copyright (c) 2017 Yegor Bugayenko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="skeleton">
<metric>
<xsl:apply-templates select="@*"/>
<title>LCOM</title>
<description>
<xsl:text>LCOM is calculated as the number of pairs of methods
operating on disjoint sets of instance variables,
educed by the number of method pairs acting on at
least one shared instance variable.
Say, there are 5 methods in a class. This means that there are 10
pairs of methods (`5 * 4 / 2`). Now, we need to see how many of these
pairs are using at least one and the same attribute (Nonempty) and how many
of them are not using any similar attributes (Empty). Then, we
just do `LCOM = Empty - Nonempty`. The metric can be really big,
starting from zero and up to any possible number. The bigger the
value the least cohesive is the class. A perfect design would have
`LCOM=0`.</xsl:text>
</description>
<xsl:apply-templates select="node()"/>
</metric>
</xsl:template>
<xsl:template match="class">
<xsl:copy>
<xsl:attribute name="value">
<xsl:text>0</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="@*"/>
<vars>
<var id="sum">0</var>
<var id="methods">0</var>
<var id="attrs">0</var>
</vars>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
1 change: 1 addition & 0 deletions src/main/resources/org/jpeek/xsd/metric.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ SOFTWARE.
<xs:element name="metric">
<xs:complexType>
<xs:all>
<xs:element name="description" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="app" type="app" minOccurs="0" maxOccurs="1">
<xs:unique name="packageUnique">
<xs:selector xpath="./package"/>
Expand Down
19 changes: 18 additions & 1 deletion src/main/resources/org/jpeek/xsd/skeleton.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ SOFTWARE.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="class">
<xs:all>
<xs:element name="attributes" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="attribute" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" use="required" type="xs:string"/>
<xs:attribute name="public" use="required" type="xs:boolean"/>
<xs:attribute name="static" use="required" type="xs:boolean"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="methods" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
Expand All @@ -49,7 +66,7 @@ SOFTWARE.
<xs:element name="ops" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:sequence>
<xs:element name="op" minOccurs="0" maxOccurs="1">
<xs:element name="op" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
Expand Down
9 changes: 5 additions & 4 deletions src/test/java/org/jpeek/ReportTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public final class ReportTest {
@Test
public void createsXmlReport() throws IOException {
final Path output = Files.createTempDirectory("");
new Report(new Skeleton(new FakeBase("Foo")).xml(), "LCOM").save(output);
new Report(new Skeleton(new FakeBase()).xml(), "LCOM").save(output);
MatcherAssert.assertThat(
Files.exists(output.resolve("LCOM.xml")),
Matchers.equalTo(true)
Expand Down Expand Up @@ -106,7 +106,8 @@ public void createsFullXmlReport() throws IOException {
new XMLDocument(
new Xembler(
new Directives()
.add("metric")
.add("skeleton")
.append(new Header())
.add("app").attr("id", ".")
.add("package").attr("id", ".")
.add("class").attr("id", "A").attr("value", "0.1").up()
Expand All @@ -116,11 +117,11 @@ public void createsFullXmlReport() throws IOException {
.add("class").attr("id", "E").attr("value", "NaN").up()
).xmlQuietly()
),
""
"LCOM"
).save(output);
MatcherAssert.assertThat(
XhtmlMatchers.xhtml(
new TextOf(output.resolve("Fixed.xml")).asString()
new TextOf(output.resolve("LCOM.xml")).asString()
),
XhtmlMatchers.hasXPaths(
"/metric[min='0.5' and max='0.6']",
Expand Down
8 changes: 7 additions & 1 deletion src/test/java/org/jpeek/SkeletonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ public void createsXml() throws IOException {
new FakeBase("OverloadMethods", "Bar")
).xml().toString()
),
XhtmlMatchers.hasXPaths("/skeleton")
XhtmlMatchers.hasXPaths(
"/skeleton/app/package[count(class)=2]",
"//class[@id='Bar']/methods[count(method)=4]",
"//class[@id='OverloadMethods']/methods[count(method)=5]",
"//method[@name='<init>' and @ctor='true']",
"//class[@id='Bar']//method[@name='<i nit>']/ops[count(op)=2]"
)
);
}

Expand Down

0 comments on commit 15ded43

Please sign in to comment.