diff --git a/jacoco-maven-plugin/src/org/jacoco/maven/ReportSupport.java b/jacoco-maven-plugin/src/org/jacoco/maven/ReportSupport.java index b66d68864..b582d8d98 100644 --- a/jacoco-maven-plugin/src/org/jacoco/maven/ReportSupport.java +++ b/jacoco-maven-plugin/src/org/jacoco/maven/ReportSupport.java @@ -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."); } } diff --git a/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java b/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java index 8ed7b8c60..e981b38a2 100644 --- a/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java +++ b/org.jacoco.ant/src/org/jacoco/ant/ReportTask.java @@ -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); diff --git a/org.jacoco.core.test/src/org/jacoco/core/analysis/CoverageBuilderTest.java b/org.jacoco.core.test/src/org/jacoco/core/analysis/CoverageBuilderTest.java index a4a389542..40c35e8fd 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/analysis/CoverageBuilderTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/analysis/CoverageBuilderTest.java @@ -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; @@ -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 classes = coverageBuilder.getClasses(); - assertTrue(classes.isEmpty()); + assertEquals(1, coverageBuilder.getClasses().size()); } @Test(expected = IllegalStateException.class) diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassCoverageImplTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassCoverageImplTest.java index b219a8784..57d281b3a 100644 --- a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassCoverageImplTest.java +++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassCoverageImplTest.java @@ -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 diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/CoverageBuilder.java b/org.jacoco.core/src/org/jacoco/core/analysis/CoverageBuilder.java index 2b240ecf5..3c21d244b 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/CoverageBuilder.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/CoverageBuilder.java @@ -98,23 +98,19 @@ public Collection 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); } } } diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/CoverageNodeImpl.java b/org.jacoco.core/src/org/jacoco/core/analysis/CoverageNodeImpl.java index c1bcb26ac..450a9a217 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/CoverageNodeImpl.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/CoverageNodeImpl.java @@ -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); diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/ICoverageNode.java b/org.jacoco.core/src/org/jacoco/core/analysis/ICoverageNode.java index f76052780..a43747c2e 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/ICoverageNode.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/ICoverageNode.java @@ -131,6 +131,13 @@ enum CounterEntity { */ ICounter getCounter(CounterEntity entity); + /** + * Checks whether this is an empty node. + * + * @return true 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 @@ -141,4 +148,4 @@ enum CounterEntity { */ ICoverageNode getPlainCopy(); -} \ No newline at end of file +} diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java index c584cacfb..f65bcf938 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java @@ -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); } diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java index cb45604c1..04bc81dc0 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassCoverageImpl.java @@ -47,7 +47,6 @@ public ClassCoverageImpl(final String name, final long id, this.id = id; this.noMatch = noMatch; this.methods = new ArrayList(); - this.classCounter = CounterImpl.COUNTER_1_0; } /** @@ -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; } } diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index b174728b5..e15e653f6 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -51,6 +51,8 @@

New Features

(GitHub #818).
  • HTML report shows message when analyzed class does not match executed (GitHub #819).
  • +
  • Empty class and sourcefile nodes are preserved and available in XML report + (GitHub #817).
  • Fixed Bugs

    diff --git a/org.jacoco.report.test/src/org/jacoco/report/ReportStructureTestDriver.java b/org.jacoco.report.test/src/org/jacoco/report/ReportStructureTestDriver.java index 203028fcd..9ac87f38f 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/ReportStructureTestDriver.java +++ b/org.jacoco.report.test/src/org/jacoco/report/ReportStructureTestDriver.java @@ -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; @@ -45,7 +47,7 @@ public class ReportStructureTestDriver { public Reader getSourceFile(String packageName, String fileName) throws IOException { - return null; + return new StringReader(""); } public int getTabWidth() { @@ -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. singletonList( + emptyClassInEmptyPackage), + Collections. 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) diff --git a/org.jacoco.report.test/src/org/jacoco/report/csv/CSVFormatterTest.java b/org.jacoco.report.test/src/org/jacoco/report/csv/CSVFormatterTest.java index ac0e61d9c..949ff0c10 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/csv/CSVFormatterTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/csv/CSVFormatterTest.java @@ -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 @@ -77,6 +78,7 @@ 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 @@ -84,9 +86,9 @@ public void testStructureWithBundleOnly() throws IOException { driver.sendBundle(visitor); final List 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 diff --git a/org.jacoco.report.test/src/org/jacoco/report/csv/CSVGroupHandlerTest.java b/org.jacoco.report.test/src/org/jacoco/report/csv/CSVGroupHandlerTest.java index 03174067b..b323f6dd5 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/csv/CSVGroupHandlerTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/csv/CSVGroupHandlerTest.java @@ -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 @@ -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() { diff --git a/org.jacoco.report.test/src/org/jacoco/report/html/HTMLFormatterTest.java b/org.jacoco.report.test/src/org/jacoco/report/html/HTMLFormatterTest.java index b1c03ff2d..e4fe16fd7 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/html/HTMLFormatterTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/html/HTMLFormatterTest.java @@ -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 diff --git a/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/BundlePageTest.java b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/BundlePageTest.java new file mode 100644 index 000000000..8de70f442 --- /dev/null +++ b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/BundlePageTest.java @@ -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. singleton(classCoverage), + Collections. emptySet()); + + final IPackageCoverage emptyPackage = new PackageCoverageImpl("empty", + Collections. emptySet(), + Collections. 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)")); + } + +} diff --git a/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackagePageTest.java b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackagePageTest.java index e7d1ed7c1..ded93ab08 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackagePageTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackagePageTest.java @@ -22,6 +22,8 @@ import org.jacoco.core.analysis.IPackageCoverage; import org.jacoco.core.analysis.ISourceFileCoverage; 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.jacoco.core.internal.analysis.SourceFileCoverageImpl; import org.jacoco.report.ISourceFileLocator; @@ -56,16 +58,49 @@ public Reader getSourceFile(String packageName, String fileName) }; } + @Test + public void should_render_non_empty_classes() throws Exception { + final ClassCoverageImpl nonEmptyClass = new ClassCoverageImpl( + "example/NonEmptyClass", 0, false); + final MethodCoverageImpl nonEmptyMethod = new MethodCoverageImpl("m", + "()V", null); + nonEmptyMethod.increment(CounterImpl.COUNTER_1_0, + CounterImpl.COUNTER_0_0, 42); + nonEmptyClass.addMethod(nonEmptyMethod); + final ClassCoverageImpl emptyClass = new ClassCoverageImpl( + "example/EmptyClass", 0, false); + + node = new PackageCoverageImpl("example", + Arrays. asList(emptyClass, nonEmptyClass), + Collections. emptySet()); + + page = new PackagePage(node, null, sourceLocator, rootFolder, context); + page.render(); + + final Document doc = support.parse(output.getFile("index.html")); + assertEquals("NonEmptyClass", 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)")); + } + @Test public void testContentsWithSource() throws Exception { - IClassCoverage class1 = new ClassCoverageImpl( + ClassCoverageImpl class1 = new ClassCoverageImpl( "org/jacoco/example/Foo1", 0x1000, false); - IClassCoverage class2 = new ClassCoverageImpl( + MethodCoverageImpl method1 = new MethodCoverageImpl("m", "()V", null); + method1.increment(CounterImpl.COUNTER_1_0, CounterImpl.COUNTER_0_0, 42); + class1.addMethod(method1); + ClassCoverageImpl class2 = new ClassCoverageImpl( "org/jacoco/example/Foo2", 0x2000, false); + MethodCoverageImpl method2 = new MethodCoverageImpl("m", "()V", null); + method2.increment(CounterImpl.COUNTER_1_0, CounterImpl.COUNTER_0_0, 42); + class2.addMethod(method2); ISourceFileCoverage src1 = new SourceFileCoverageImpl("Src1.java", "org/jacoco/example"); - node = new PackageCoverageImpl("org/jacoco/example", Arrays.asList( - class1, class2), Arrays.asList(src1)); + node = new PackageCoverageImpl("org/jacoco/example", + Arrays. asList(class1, class2), + Arrays.asList(src1)); page = new PackagePage(node, null, sourceLocator, rootFolder, context); page.render(); @@ -93,12 +128,19 @@ public void testContentsWithSource() throws Exception { @Test public void testContentsNoSource() throws Exception { - IClassCoverage class1 = new ClassCoverageImpl( + ClassCoverageImpl class1 = new ClassCoverageImpl( "org/jacoco/example/Foo1", 0x1000, false); - IClassCoverage class2 = new ClassCoverageImpl( + MethodCoverageImpl method1 = new MethodCoverageImpl("m", "()V", null); + method1.increment(CounterImpl.COUNTER_1_0, CounterImpl.COUNTER_0_0, 42); + class1.addMethod(method1); + ClassCoverageImpl class2 = new ClassCoverageImpl( "org/jacoco/example/Foo2", 0x2000, false); - node = new PackageCoverageImpl("org/jacoco/example", Arrays.asList( - class1, class2), Collections. emptyList()); + MethodCoverageImpl method2 = new MethodCoverageImpl("m", "()V", null); + method2.increment(CounterImpl.COUNTER_1_0, CounterImpl.COUNTER_0_0, 42); + class2.addMethod(method2); + node = new PackageCoverageImpl("org/jacoco/example", + Arrays. asList(class1, class2), + Collections. emptyList()); page = new PackagePage(node, null, sourceLocator, rootFolder, context); page.render(); diff --git a/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackageSourcePageTest.java b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackageSourcePageTest.java index c39a4982c..873da8224 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackageSourcePageTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackageSourcePageTest.java @@ -24,6 +24,7 @@ import org.jacoco.core.analysis.IClassCoverage; import org.jacoco.core.analysis.ISourceFileCoverage; +import org.jacoco.core.internal.analysis.CounterImpl; import org.jacoco.core.internal.analysis.PackageCoverageImpl; import org.jacoco.core.internal.analysis.SourceFileCoverageImpl; import org.jacoco.report.ISourceFileLocator; @@ -48,12 +49,16 @@ public class PackageSourcePageTest extends PageTestBase { @Override public void setup() throws Exception { super.setup(); - ISourceFileCoverage src1 = new SourceFileCoverageImpl("Src1.java", + SourceFileCoverageImpl src1 = new SourceFileCoverageImpl("Src1.java", "org/jacoco/example"); - ISourceFileCoverage src2 = new SourceFileCoverageImpl("Src2.java", + src1.increment(CounterImpl.COUNTER_1_0, + CounterImpl.COUNTER_0_0, 1); + SourceFileCoverageImpl src2 = new SourceFileCoverageImpl("Src2.java", "org/jacoco/example"); + src2.increment(CounterImpl.COUNTER_1_0, + CounterImpl.COUNTER_0_0, 1); node = new PackageCoverageImpl("org/jacoco/example", - Collections. emptyList(), Arrays.asList(src1, + Collections. emptyList(), Arrays.asList(src1, src2)); sourceLocator = new ISourceFileLocator() { @@ -108,6 +113,29 @@ public void testContents() throws Exception { "/html/body/table[1]/tbody/tr[2]/td[1]/span")); } + @Test + public void should_render_non_empty_sources() throws Exception { + final ISourceFileCoverage emptySource = new SourceFileCoverageImpl( + "Empty.java", "example"); + final SourceFileCoverageImpl nonEmptySource = new SourceFileCoverageImpl( + "NonEmpty.java", "example"); + nonEmptySource.increment(CounterImpl.COUNTER_1_0, + CounterImpl.COUNTER_0_0, 1); + node = new PackageCoverageImpl("example", + Collections. emptyList(), + Arrays.asList(emptySource, nonEmptySource)); + + page = new PackageSourcePage(node, null, sourceLocator, rootFolder, + context, packagePageLink); + page.render(); + + final Document doc = support.parse(output.getFile("index.source.html")); + assertEquals("NonEmpty.java", support.findStr(doc, + "/html/body/table[1]/tbody/tr[1]/td[1]/span")); + assertEquals("1", + support.findStr(doc, "count(/html/body/table[1]/tbody/tr)")); + } + @Test public void testGetSourceFilePages() throws Exception { page = new PackageSourcePage(node, null, sourceLocator, rootFolder, diff --git a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java index 57552ce85..3afb4e3c1 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java +++ b/org.jacoco.report.test/src/org/jacoco/report/xml/XMLFormatterTest.java @@ -134,9 +134,15 @@ public void testStructureWithBundleOnly() throws Exception { visitor.visitInfo(infos, data); driver.sendBundle(visitor); assertPathMatches("bundle", "/report/@name"); + + assertPathMatches("2", "count(/report/package)"); assertPathMatches("org/jacoco/example", "/report/package/@name"); + + assertPathMatches("3", "count(/report/package/class)"); assertPathMatches("org/jacoco/example/FooClass", "/report/package/class/@name"); + + assertPathMatches("1", "count(/report/package/class/method)"); assertPathMatches("fooMethod", "/report/package/class/method/@name"); assertPathMatches("1", "count(/report/counter[@type='INSTRUCTION'])"); @@ -163,6 +169,9 @@ public void testStructureWithBundleOnly() throws Exception { assertPathMatches("0", "report/counter[@type='CLASS']/@missed"); assertPathMatches("1", "report/counter[@type='CLASS']/@covered"); + assertPathMatches("2", + "count(report/package[@name='org/jacoco/example']/sourcefile)"); + assertPathMatches("3", "count(report/package/sourcefile/line)"); assertPathMatches("1", "report/package/sourcefile[@name='FooClass.java']/line[1]/@nr"); assertPathMatches("3", @@ -176,6 +185,21 @@ public void testStructureWithBundleOnly() throws Exception { "report/package/sourcefile[@name='FooClass.java']/line[3]/@nr"); assertPathMatches("4", "report/package/sourcefile[@name='FooClass.java']/line[3]/@mi"); + + assertPathMatches("0", "count(/report/package[@name='empty']/counter)"); + + assertPathMatches("1", "count(/report/package[@name='empty']/class)"); + assertPathMatches("empty/Empty", + "/report/package[@name='empty']/class/@name"); + assertPathMatches("0", + "count(report/package[@name='empty']/class/*)"); + + assertPathMatches("1", + "count(/report/package[@name='empty']/sourcefile)"); + assertPathMatches("Empty.java", + "report/package[@name='empty']/sourcefile/@name"); + assertPathMatches("0", + "count(report/package[@name='empty']/sourcefile/*)"); } @Test diff --git a/org.jacoco.report/src/org/jacoco/report/csv/CSVGroupHandler.java b/org.jacoco.report/src/org/jacoco/report/csv/CSVGroupHandler.java index 0f50ffe03..7b95af5c3 100644 --- a/org.jacoco.report/src/org/jacoco/report/csv/CSVGroupHandler.java +++ b/org.jacoco.report/src/org/jacoco/report/csv/CSVGroupHandler.java @@ -43,7 +43,9 @@ public void visitBundle(final IBundleCoverage bundle, for (final IPackageCoverage p : bundle.getPackages()) { final String packageName = p.getName(); for (final IClassCoverage c : p.getClasses()) { - writer.writeRow(name, packageName, c); + if (!c.isEmpty()) { + writer.writeRow(name, packageName, c); + } } } } diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/BundlePage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/BundlePage.java index 3189faa19..e12226c03 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/BundlePage.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/BundlePage.java @@ -62,6 +62,9 @@ public void render() throws IOException { private void renderPackages() throws IOException { for (final IPackageCoverage p : bundle.getPackages()) { + if (p.isEmpty()) { + continue; + } final String packagename = p.getName(); final String foldername = packagename.length() == 0 ? "default" : packagename.replace('/', '.'); diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackagePage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackagePage.java index f473850a2..10b2c461f 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackagePage.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackagePage.java @@ -65,6 +65,9 @@ public void render() throws IOException { private void renderClasses() throws IOException { for (final IClassCoverage c : getNode().getClasses()) { + if (c.isEmpty()) { + continue; + } final ILinkable sourceFilePage = packageSourcePage .getSourceFilePage(c.getSourceFileName()); final ClassPage page = new ClassPage(c, this, sourceFilePage, diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java index cfe7f792f..11d6dfbef 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java @@ -79,6 +79,9 @@ ILinkable getSourceFilePage(final String name) { private final void renderSourceFilePages() throws IOException { final String packagename = getNode().getName(); for (final ISourceFileCoverage s : getNode().getSourceFiles()) { + if (s.isEmpty()) { + continue; + } final String sourcename = s.getName(); final Reader reader = locator .getSourceFile(packagename, sourcename);