Skip to content

Commit

Permalink
Preserve empty class and sourcefile nodes in XML report (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin authored and marchof committed Jan 18, 2019
1 parent 0152b7a commit 13f29eb
Show file tree
Hide file tree
Showing 22 changed files with 284 additions and 53 deletions.
6 changes: 3 additions & 3 deletions jacoco-maven-plugin/src/org/jacoco/maven/ReportSupport.java
Expand Up @@ -222,9 +222,9 @@ private void logBundleInfo(final IBundleCoverage bundle,
c.getName()));
}
}
if (bundle.getClassCounter().getTotalCount() > 0
&& bundle.getLineCounter().getTotalCount() == 0) {
log.warn("To enable source code annotation class files have to be compiled with debug information.");
if (!bundle.isEmpty() && bundle.getLineCounter().getTotalCount() == 0) {
log.warn(
"To enable source code annotation class files have to be compiled with debug information.");
}
}

Expand Down
3 changes: 1 addition & 2 deletions org.jacoco.ant/src/org/jacoco/ant/ReportTask.java
Expand Up @@ -593,8 +593,7 @@ private void logBundleInfo(final IBundleCoverage bundle,
}

private void checkForMissingDebugInformation(final ICoverageNode node) {
if (node.getClassCounter().getTotalCount() > 0
&& node.getLineCounter().getTotalCount() == 0) {
if (!node.isEmpty() && node.getLineCounter().getTotalCount() == 0) {
log(format(
"To enable source code annotation class files for bundle '%s' have to be compiled with debug information.",
node.getName()), Project.MSG_WARN);
Expand Down
Expand Up @@ -13,7 +13,6 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -106,13 +105,10 @@ public void testCreateClassCovered() {
}

@Test
public void testIgnoreClassesWithoutCode() {
final MethodCoverageImpl method = new MethodCoverageImpl("doit", "()V",
null);
addClass(123L, false, "Sample", null, method);
public void should_not_ignore_empty_classes() {
addClass(123L, false, "Empty", null);

final Collection<IClassCoverage> classes = coverageBuilder.getClasses();
assertTrue(classes.isEmpty());
assertEquals(1, coverageBuilder.getClasses().size());
}

@Test(expected = IllegalStateException.class)
Expand Down
Expand Up @@ -93,7 +93,7 @@ public void testEmptyClass() {
assertEquals(CounterImpl.COUNTER_0_0, node.getInstructionCounter());
assertEquals(CounterImpl.COUNTER_0_0, node.getBranchCounter());
assertEquals(CounterImpl.COUNTER_0_0, node.getMethodCounter());
assertEquals(CounterImpl.COUNTER_1_0, node.getClassCounter());
assertEquals(CounterImpl.COUNTER_0_0, node.getClassCounter());
}

@Test
Expand Down
30 changes: 13 additions & 17 deletions org.jacoco.core/src/org/jacoco/core/analysis/CoverageBuilder.java
Expand Up @@ -98,23 +98,19 @@ public Collection<IClassCoverage> getNoMatchClasses() {
// === ICoverageVisitor ===

public void visitCoverage(final IClassCoverage coverage) {
// Only consider classes that actually contain code:
if (coverage.getInstructionCounter().getTotalCount() > 0) {
final String name = coverage.getName();
final IClassCoverage dup = classes.put(name, coverage);
if (dup != null) {
if (dup.getId() != coverage.getId()) {
throw new IllegalStateException(
"Can't add different class with same name: "
+ name);
}
} else {
final String source = coverage.getSourceFileName();
if (source != null) {
final SourceFileCoverageImpl sourceFile = getSourceFile(
source, coverage.getPackageName());
sourceFile.increment(coverage);
}
final String name = coverage.getName();
final IClassCoverage dup = classes.put(name, coverage);
if (dup != null) {
if (dup.getId() != coverage.getId()) {
throw new IllegalStateException(
"Can't add different class with same name: " + name);
}
} else {
final String source = coverage.getSourceFileName();
if (source != null) {
final SourceFileCoverageImpl sourceFile = getSourceFile(source,
coverage.getPackageName());
sourceFile.increment(coverage);
}
}
}
Expand Down
Expand Up @@ -143,6 +143,10 @@ public ICounter getCounter(final CounterEntity entity) {
throw new AssertionError(entity);
}

public boolean isEmpty() {
return getInstructionCounter().getTotalCount() == 0;
}

public ICoverageNode getPlainCopy() {
final CoverageNodeImpl copy = new CoverageNodeImpl(elementType, name);
copy.instructionCounter = CounterImpl.getInstance(instructionCounter);
Expand Down
Expand Up @@ -131,6 +131,13 @@ enum CounterEntity {
*/
ICounter getCounter(CounterEntity entity);

/**
* Checks whether this is an empty node.
*
* @return <code>true</code> if this node does not contain instructions
*/
boolean isEmpty();

/**
* Creates a plain copy of this node. While {@link ICoverageNode}
* implementations may contain heavy data structures, the copy returned by
Expand All @@ -141,4 +148,4 @@ enum CounterEntity {
*/
ICoverageNode getPlainCopy();

}
}
Expand Up @@ -113,7 +113,7 @@ private void addMethodCoverage(final String name, final String desc,
signature);
mcc.calculate(mc);

if (mc.getInstructionCounter().getTotalCount() > 0) {
if (!mc.isEmpty()) {
// Only consider methods that actually contain code
coverage.addMethod(mc);
}
Expand Down
Expand Up @@ -47,7 +47,6 @@ public ClassCoverageImpl(final String name, final long id,
this.id = id;
this.noMatch = noMatch;
this.methods = new ArrayList<IMethodCoverage>();
this.classCounter = CounterImpl.COUNTER_1_0;
}

/**
Expand All @@ -59,10 +58,11 @@ public ClassCoverageImpl(final String name, final long id,
public void addMethod(final IMethodCoverage method) {
this.methods.add(method);
increment(method);
// As class is considered as covered when at least one method is
// covered:
// Class is considered as covered when at least one method is covered:
if (methodCounter.getCoveredCount() > 0) {
this.classCounter = CounterImpl.COUNTER_0_1;
} else {
this.classCounter = CounterImpl.COUNTER_1_0;
}
}

Expand Down
2 changes: 2 additions & 0 deletions org.jacoco.doc/docroot/doc/changes.html
Expand Up @@ -51,6 +51,8 @@ <h3>New Features</h3>
(GitHub <a href="https://github.com/jacoco/jacoco/issues/818">#818</a>).</li>
<li>HTML report shows message when analyzed class does not match executed
(GitHub <a href="https://github.com/jacoco/jacoco/issues/819">#819</a>).</li>
<li>Empty class and sourcefile nodes are preserved and available in XML report
(GitHub <a href="https://github.com/jacoco/jacoco/issues/817">#817</a>).</li>
</ul>

<h3>Fixed Bugs</h3>
Expand Down
Expand Up @@ -13,6 +13,8 @@

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -45,7 +47,7 @@ public class ReportStructureTestDriver {

public Reader getSourceFile(String packageName, String fileName)
throws IOException {
return null;
return new StringReader("");
}

public int getTabWidth() {
Expand Down Expand Up @@ -84,11 +86,30 @@ public ReportStructureTestDriver() {
sourceFileCoverageImpl.increment(classCoverage);
sourceFileCoverage = sourceFileCoverageImpl;

final ClassCoverageImpl emptyClassInNonEmptyPackage = new ClassCoverageImpl(
"org/jacoco/example/Empty", 0, false);
emptyClassInNonEmptyPackage.setSourceFileName("Empty.java");
final SourceFileCoverageImpl emptySourceInNonEmptyPackage = new SourceFileCoverageImpl(
"Empty.java", "org/jacoco/example");

final ClassCoverageImpl emptyClassInEmptyPackage = new ClassCoverageImpl(
"empty/Empty", 0, false);
emptyClassInEmptyPackage.setSourceFileName("Empty.java");
final SourceFileCoverageImpl emptySourceInEmptyPackage = new SourceFileCoverageImpl(
"Empty.java", "empty");
final PackageCoverageImpl emptyPackage = new PackageCoverageImpl(
"empty",
Collections.<IClassCoverage> singletonList(
emptyClassInEmptyPackage),
Collections.<ISourceFileCoverage> singletonList(
emptySourceInEmptyPackage));

packageCoverage = new PackageCoverageImpl("org/jacoco/example",
Collections.singleton(classCoverage),
Collections.singleton(sourceFileCoverage));
Arrays.asList(classCoverage, emptyClassInNonEmptyPackage),
Arrays.asList(sourceFileCoverage,
emptySourceInNonEmptyPackage));
bundleCoverage = new BundleCoverageImpl("bundle",
Collections.singleton(packageCoverage));
Arrays.asList(packageCoverage, emptyPackage));
}

public void sendNestedGroups(IReportVisitor reportVisitor)
Expand Down
Expand Up @@ -64,6 +64,7 @@ public void testStructureWithGroup() throws IOException {
assertEquals(
"group/bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
lines.get(1));
assertEquals(2, lines.size());
}

@Test
Expand All @@ -77,16 +78,17 @@ public void testStructureWithNestedGroups() throws IOException {
assertEquals(
"report/bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
lines.get(2));
assertEquals(3, lines.size());
}

@Test
public void testStructureWithBundleOnly() throws IOException {
driver.sendBundle(visitor);
final List<String> lines = getLines();
assertEquals(HEADER, lines.get(0));
assertEquals(
"bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
assertEquals("bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
lines.get(1));
assertEquals(2, lines.size());
}

@Test
Expand Down
Expand Up @@ -51,6 +51,7 @@ public void testVisitBundle() throws Exception {
assertEquals(
"bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
reader.readLine());
assertEquals("no more lines expected", null, reader.readLine());
}

@Test
Expand All @@ -61,6 +62,7 @@ public void testVisitGroup() throws Exception {
assertEquals(
"group/bundle,org.jacoco.example,FooClass,10,15,1,2,0,3,1,2,0,1",
reader.readLine());
assertEquals("no more lines expected", null, reader.readLine());
}

private BufferedReader getResultReader() {
Expand Down
Expand Up @@ -65,16 +65,36 @@ public void testStructureWithGroup() throws IOException {
driver.sendGroup(formatter.createVisitor(output));
output.assertFile("index.html");
output.assertFile("bundle/index.html");

output.assertFile("bundle/org.jacoco.example/index.html");
output.assertFile("bundle/org.jacoco.example/index.source.html");
output.assertFile("bundle/org.jacoco.example/FooClass.html");
output.assertFile("bundle/org.jacoco.example/FooClass.java.html");
output.assertNoFile("bundle/org.jacoco.example/Empty.html");
output.assertNoFile("bundle/org.jacoco.example/Empty.java.html");

output.assertNoFile("bundle/empty/index.html");
output.assertNoFile("bundle/empty/index.source.html");
output.assertNoFile("bundle/empty/Empty.html");
output.assertNoFile("bundle/empty/Empty.java.html");
}

@Test
public void testStructureWithBundleOnly() throws IOException {
driver.sendBundle(formatter.createVisitor(output));
output.assertFile("index.html");

output.assertFile("org.jacoco.example/index.html");
output.assertFile("org.jacoco.example/index.source.html");
output.assertFile("org.jacoco.example/FooClass.html");
output.assertFile("org.jacoco.example/FooClass.java.html");
output.assertNoFile("org.jacoco.example/Empty.html");
output.assertNoFile("org.jacoco.example/Empty.java.html");

output.assertNoFile("empty/index.html");
output.assertNoFile("empty/index.source.html");
output.assertNoFile("empty/Empty.html");
output.assertNoFile("empty/Empty.java.html");
}

@Test
Expand Down
@@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Evgeny Mandrikov - initial API and implementation
*
*******************************************************************************/
package org.jacoco.report.internal.html.page;

import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.analysis.IPackageCoverage;
import org.jacoco.core.analysis.ISourceFileCoverage;
import org.jacoco.core.internal.analysis.BundleCoverageImpl;
import org.jacoco.core.internal.analysis.ClassCoverageImpl;
import org.jacoco.core.internal.analysis.CounterImpl;
import org.jacoco.core.internal.analysis.MethodCoverageImpl;
import org.jacoco.core.internal.analysis.PackageCoverageImpl;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;

import java.util.Arrays;
import java.util.Collections;

import static org.junit.Assert.assertEquals;

/**
* Unit tests for {@link BundlePage}.
*/
public class BundlePageTest extends PageTestBase {

@Before
@Override
public void setup() throws Exception {
super.setup();
}

@Test
public void should_render_non_empty_packages() throws Exception {
final ClassCoverageImpl classCoverage = new ClassCoverageImpl(
"example/Class", 0, false);
final MethodCoverageImpl methodCoverage = new MethodCoverageImpl("m",
"()V", null);
methodCoverage.increment(CounterImpl.COUNTER_1_0,
CounterImpl.COUNTER_0_0, 42);
classCoverage.addMethod(methodCoverage);
final IPackageCoverage nonEmptyPackage = new PackageCoverageImpl(
"example",
Collections.<IClassCoverage> singleton(classCoverage),
Collections.<ISourceFileCoverage> emptySet());

final IPackageCoverage emptyPackage = new PackageCoverageImpl("empty",
Collections.<IClassCoverage> emptySet(),
Collections.<ISourceFileCoverage> emptySet());

final IBundleCoverage node = new BundleCoverageImpl("bundle",
Arrays.asList(nonEmptyPackage, emptyPackage));

final BundlePage page = new BundlePage(node, null, null, rootFolder,
context);
page.render();

final Document doc = support.parse(output.getFile("index.html"));
assertEquals("el_package", support.findStr(doc,
"/html/body/table[1]/tbody/tr[1]/td[1]/a/@class"));
assertEquals("example", support.findStr(doc,
"/html/body/table[1]/tbody/tr[1]/td[1]/a"));
assertEquals("1",
support.findStr(doc, "count(/html/body/table[1]/tbody/tr)"));
}

}

0 comments on commit 13f29eb

Please sign in to comment.