diff options
author | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-10-07 23:09:58 -0700 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2013-10-07 23:09:58 -0700 |
commit | 1bbf1debc32022aa1a680adc78487756fb6755fd (patch) | |
tree | c0760f78dc3b997d0ea87a6945f60826af3477a4 | |
parent | 3cc8a309f8f4036c2202568cd5e51e90866f3cdf (diff) | |
parent | cf84baafe71c7cd3ce0e7d4ea638a719dd82cc19 (diff) | |
download | jacoco-1bbf1debc32022aa1a680adc78487756fb6755fd.tar.gz |
Merge pull request #142 from jacoco/issue-142
HTML report: Also list source files of a package
10 files changed, 460 insertions, 32 deletions
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html index 02286ffa..29f0f128 100644 --- a/org.jacoco.doc/docroot/doc/changes.html +++ b/org.jacoco.doc/docroot/doc/changes.html @@ -35,6 +35,8 @@ <li>The coverage check has been reworked to allow checks on all counter values on all element types (GitHub #106).</li> <li>Coverage checks are now also available in Ant (GitHub #106).</li> + <li>Additional list of source files for every package in HTML report + (GitHub #142).</li> </ul> <h3>Fixed Bugs</h3> diff --git a/org.jacoco.report.test/src/org/jacoco/report/MemoryMultiReportOutput.java b/org.jacoco.report.test/src/org/jacoco/report/MemoryMultiReportOutput.java index ccebe878..31059c1a 100644 --- a/org.jacoco.report.test/src/org/jacoco/report/MemoryMultiReportOutput.java +++ b/org.jacoco.report.test/src/org/jacoco/report/MemoryMultiReportOutput.java @@ -14,6 +14,7 @@ package org.jacoco.report; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; @@ -65,6 +66,10 @@ public class MemoryMultiReportOutput implements IMultiReportOutput { path, files.keySet()), files.get(path)); } + public void assertNoFile(String path) { + assertNull(String.format("Unexpected file %s.", path), files.get(path)); + } + public void assertSingleFile(String path) { assertEquals(Collections.singleton(path), files.keySet()); } 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 new file mode 100644 index 00000000..dcaefdc8 --- /dev/null +++ b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackagePageTest.java @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.internal.html.page; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; +import java.util.Collections; + +import org.jacoco.core.analysis.IClassCoverage; +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.PackageCoverageImpl; +import org.jacoco.core.internal.analysis.SourceFileCoverageImpl; +import org.jacoco.report.ISourceFileLocator; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; + +/** + * Unit tests for {@link PackageSourcePage}. + */ +public class PackagePageTest extends PageTestBase { + + private IPackageCoverage node; + private ISourceFileLocator sourceLocator; + + private PackagePage page; + + @Before + @Override + public void setup() throws Exception { + super.setup(); + sourceLocator = new ISourceFileLocator() { + + public int getTabWidth() { + return 4; + } + + public Reader getSourceFile(String packageName, String fileName) + throws IOException { + return null; + } + }; + } + + @Test + public void testContentsWithSource() throws Exception { + IClassCoverage class1 = new ClassCoverageImpl( + "org/jacoco/example/Foo1", 0x1000, null, "java/lang/Object", + null); + IClassCoverage class2 = new ClassCoverageImpl( + "org/jacoco/example/Foo2", 0x2000, null, "java/lang/Object", + null); + ISourceFileCoverage src1 = new SourceFileCoverageImpl("Src1.java", + "org/jacoco/example"); + node = new PackageCoverageImpl("org/jacoco/example", Arrays.asList( + class1, class2), Arrays.asList(src1)); + + page = new PackagePage(node, null, sourceLocator, rootFolder, context); + page.render(); + + final Document doc = support.parse(output.getFile("index.html")); + + // Expect "Source Files" links + assertEquals("index.source.html", + support.findStr(doc, "/html/body/div[1]/span[1]/a/@href")); + assertEquals("el_source", + support.findStr(doc, "/html/body/div[1]/span[1]/a/@class")); + assertEquals("Source Files", + support.findStr(doc, "/html/body/div[1]/span[1]/a")); + assertEquals("el_class", support.findStr(doc, + "/html/body/table[1]/tbody/tr[1]/td[1]/a/@class")); + assertEquals("Foo1", + support.findStr(doc, "/html/body/table[1]/tbody/tr[1]/td[1]/a")); + assertEquals("el_class", support.findStr(doc, + "/html/body/table[1]/tbody/tr[2]/td[1]/a/@class")); + assertEquals("Foo2", + support.findStr(doc, "/html/body/table[1]/tbody/tr[2]/td[1]/a")); + + output.assertFile("index.source.html"); + } + + @Test + public void testContentsNoSource() throws Exception { + IClassCoverage class1 = new ClassCoverageImpl( + "org/jacoco/example/Foo1", 0x1000, null, "java/lang/Object", + null); + IClassCoverage class2 = new ClassCoverageImpl( + "org/jacoco/example/Foo2", 0x2000, null, "java/lang/Object", + null); + node = new PackageCoverageImpl("org/jacoco/example", Arrays.asList( + class1, class2), Collections.<ISourceFileCoverage> emptyList()); + + page = new PackagePage(node, null, sourceLocator, rootFolder, context); + page.render(); + + // Expect no "Source Files" link + final Document doc = support.parse(output.getFile("index.html")); + assertEquals("Sessions", + support.findStr(doc, "/html/body/div[1]/span[1]/a")); + + // Expect no source files page: + output.assertNoFile("index.source.html"); + } + +} 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 new file mode 100644 index 00000000..b1228f27 --- /dev/null +++ b/org.jacoco.report.test/src/org/jacoco/report/internal/html/page/PackageSourcePageTest.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.internal.html.page; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Arrays; +import java.util.Collections; + +import org.jacoco.core.analysis.IClassCoverage; +import org.jacoco.core.analysis.ISourceFileCoverage; +import org.jacoco.core.internal.analysis.PackageCoverageImpl; +import org.jacoco.core.internal.analysis.SourceFileCoverageImpl; +import org.jacoco.report.ISourceFileLocator; +import org.jacoco.report.internal.ReportOutputFolder; +import org.jacoco.report.internal.html.ILinkable; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; + +/** + * Unit tests for {@link PackageSourcePage}. + */ +public class PackageSourcePageTest extends PageTestBase { + + private PackageCoverageImpl node; + private ISourceFileLocator sourceLocator; + private ILinkable packagePageLink; + + private PackageSourcePage page; + + @Before + @Override + public void setup() throws Exception { + super.setup(); + ISourceFileCoverage src1 = new SourceFileCoverageImpl("Src1.java", + "org/jacoco/example"); + ISourceFileCoverage src2 = new SourceFileCoverageImpl("Src2.java", + "org/jacoco/example"); + node = new PackageCoverageImpl("org/jacoco/example", + Collections.<IClassCoverage> emptyList(), Arrays.asList(src1, + src2)); + sourceLocator = new ISourceFileLocator() { + + public int getTabWidth() { + return 4; + } + + public Reader getSourceFile(String packageName, String fileName) + throws IOException { + return fileName.equals("Src1.java") ? new StringReader("") + : null; + } + }; + packagePageLink = new ILinkable() { + + public String getLinkStyle() { + fail(); + return null; + } + + public String getLinkLabel() { + fail(); + return null; + } + + public String getLink(ReportOutputFolder base) { + return "index.html"; + } + }; + } + + @Test + public void testContents() throws Exception { + page = new PackageSourcePage(node, null, sourceLocator, rootFolder, + context, packagePageLink); + page.render(); + + final Document doc = support.parse(output.getFile("index.source.html")); + assertEquals("index.html", + support.findStr(doc, "/html/body/div[1]/span[1]/a/@href")); + assertEquals("el_class", + support.findStr(doc, "/html/body/div[1]/span[1]/a/@class")); + assertEquals("Classes", + support.findStr(doc, "/html/body/div[1]/span[1]/a")); + assertEquals("el_source", support.findStr(doc, + "/html/body/table[1]/tbody/tr[1]/td[1]/a/@class")); + assertEquals("Src1.java", + support.findStr(doc, "/html/body/table[1]/tbody/tr[1]/td[1]/a")); + assertEquals("el_source", support.findStr(doc, + "/html/body/table[1]/tbody/tr[2]/td[1]/span/@class")); + assertEquals("Src2.java", support.findStr(doc, + "/html/body/table[1]/tbody/tr[2]/td[1]/span")); + } + + @Test + public void testGetSourceFilePages() throws Exception { + page = new PackageSourcePage(node, null, sourceLocator, rootFolder, + context, packagePageLink); + page.render(); + + assertNotNull(page.getSourceFilePage("Src1.java")); + assertNull(page.getSourceFilePage("Src2.java")); + } + +} 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 e74244f5..5844ec91 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 @@ -12,17 +12,15 @@ package org.jacoco.report.internal.html.page; import java.io.IOException; -import java.io.Reader; -import java.util.HashMap; -import java.util.Map; import org.jacoco.core.analysis.IClassCoverage; import org.jacoco.core.analysis.IPackageCoverage; -import org.jacoco.core.analysis.ISourceFileCoverage; import org.jacoco.report.ISourceFileLocator; import org.jacoco.report.internal.ReportOutputFolder; +import org.jacoco.report.internal.html.HTMLElement; import org.jacoco.report.internal.html.IHTMLReportContext; import org.jacoco.report.internal.html.ILinkable; +import org.jacoco.report.internal.html.resources.Styles; /** * Page showing coverage information for a Java package. The page contains a @@ -30,7 +28,8 @@ import org.jacoco.report.internal.html.ILinkable; */ public class PackagePage extends TablePage<IPackageCoverage> { - private final ISourceFileLocator locator; + private final PackageSourcePage packageSourcePage; + private final boolean sourceCoverageExists; /** * Creates a new visitor in the given context. @@ -50,39 +49,26 @@ public class PackagePage extends TablePage<IPackageCoverage> { final ISourceFileLocator locator, final ReportOutputFolder folder, final IHTMLReportContext context) { super(node, parent, folder, context); - this.locator = locator; + packageSourcePage = new PackageSourcePage(node, parent, locator, + folder, context, this); + sourceCoverageExists = !node.getSourceFiles().isEmpty(); } @Override public void render() throws IOException { - final Map<String, ILinkable> sourceFiles = renderSourceFiles(); - renderClasses(sourceFiles); - super.render(); - } - - private final Map<String, ILinkable> renderSourceFiles() throws IOException { - final Map<String, ILinkable> sourceFiles = new HashMap<String, ILinkable>(); - final String packagename = getNode().getName(); - for (final ISourceFileCoverage s : getNode().getSourceFiles()) { - final String sourcename = s.getName(); - final Reader reader = locator - .getSourceFile(packagename, sourcename); - if (reader != null) { - final SourceFilePage sourcePage = new SourceFilePage(s, reader, - locator.getTabWidth(), this, folder, context); - sourcePage.render(); - sourceFiles.put(sourcename, sourcePage); - } - + if (sourceCoverageExists) { + packageSourcePage.render(); } - return sourceFiles; + renderClasses(); + super.render(); } - private void renderClasses(final Map<String, ILinkable> sourceFiles) - throws IOException { + private void renderClasses() throws IOException { for (final IClassCoverage c : getNode().getClasses()) { - final ClassPage page = new ClassPage(c, this, sourceFiles.get(c - .getSourceFileName()), folder, context); + final ILinkable sourceFilePage = packageSourcePage + .getSourceFilePage(c.getSourceFileName()); + final ClassPage page = new ClassPage(c, this, sourceFilePage, + folder, context); page.render(); addItem(page); } @@ -103,4 +89,13 @@ public class PackagePage extends TablePage<IPackageCoverage> { return context.getLanguageNames().getPackageName(getNode().getName()); } + @Override + protected void infoLinks(final HTMLElement span) throws IOException { + if (sourceCoverageExists) { + final String link = packageSourcePage.getLink(folder); + span.a(link, Styles.EL_SOURCE).text("Source Files"); + } + super.infoLinks(span); + } + } 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 new file mode 100644 index 00000000..c7f0bf0b --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/PackageSourcePage.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.internal.html.page; + +import java.io.IOException; +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; + +import org.jacoco.core.analysis.IPackageCoverage; +import org.jacoco.core.analysis.ISourceFileCoverage; +import org.jacoco.report.ISourceFileLocator; +import org.jacoco.report.internal.ReportOutputFolder; +import org.jacoco.report.internal.html.HTMLElement; +import org.jacoco.report.internal.html.IHTMLReportContext; +import org.jacoco.report.internal.html.ILinkable; +import org.jacoco.report.internal.html.resources.Styles; + +/** + * Page showing coverage information for a Java package. The page contains a + * table with all classes of the package. + */ +public class PackageSourcePage extends TablePage<IPackageCoverage> { + + private final ISourceFileLocator locator; + private final Map<String, ILinkable> sourceFilePages; + private final ILinkable packagePage; + + /** + * Creates a new visitor in the given context. + * + * @param node + * coverage data for this package + * @param parent + * optional hierarchical parent + * @param locator + * source locator + * @param folder + * base folder to create this page in + * @param context + * settings context + * @param packagePage + * page listing the classes of this package + */ + public PackageSourcePage(final IPackageCoverage node, + final ReportPage parent, final ISourceFileLocator locator, + final ReportOutputFolder folder, final IHTMLReportContext context, + final ILinkable packagePage) { + super(node, parent, folder, context); + this.locator = locator; + this.packagePage = packagePage; + this.sourceFilePages = new HashMap<String, ILinkable>(); + } + + @Override + public void render() throws IOException { + renderSourceFilePages(); + super.render(); + } + + /** + * Returns the link to the source file page of the source file with the + * given name. If no source file was located, <code>null</code> is + * returned.. + */ + ILinkable getSourceFilePage(final String name) { + return sourceFilePages.get(name); + } + + private final void renderSourceFilePages() throws IOException { + final String packagename = getNode().getName(); + for (final ISourceFileCoverage s : getNode().getSourceFiles()) { + final String sourcename = s.getName(); + final Reader reader = locator + .getSourceFile(packagename, sourcename); + if (reader == null) { + addItem(new SourceFileItem(s)); + } else { + final SourceFilePage sourcePage = new SourceFilePage(s, reader, + locator.getTabWidth(), this, folder, context); + sourcePage.render(); + sourceFilePages.put(sourcename, sourcePage); + addItem(sourcePage); + } + + } + } + + @Override + protected String getOnload() { + return "initialSort(['breadcrumb', 'coveragetable'])"; + } + + @Override + protected String getFileName() { + return "index.source.html"; + } + + @Override + public String getLinkLabel() { + return context.getLanguageNames().getPackageName(getNode().getName()); + } + + @Override + protected void infoLinks(final HTMLElement span) throws IOException { + final String link = packagePage.getLink(folder); + span.a(link, Styles.EL_CLASS).text("Classes"); + super.infoLinks(span); + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/ReportPage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/ReportPage.java index a993e1ce..ccdb39ed 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/ReportPage.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/ReportPage.java @@ -102,7 +102,7 @@ public abstract class ReportPage implements ILinkable { body.attr("onload", getOnload()); final HTMLElement navigation = body.div(Styles.BREADCRUMB); navigation.attr("id", "breadcrumb"); - infoLinks(navigation.span(Styles.RIGHT)); + infoLinks(navigation.span(Styles.INFO)); breadcrumb(navigation, folder); body.h1().text(getLinkLabel()); content(body); @@ -118,7 +118,15 @@ public abstract class ReportPage implements ILinkable { return null; } - private void infoLinks(final HTMLElement span) throws IOException { + /** + * Inserts additional links on the top right corner. + * + * @param span + * parent element + * @throws IOException + * in case of IO problems with the report writer + */ + protected void infoLinks(final HTMLElement span) throws IOException { span.a(context.getSessionsPage(), folder); } diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SourceFileItem.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SourceFileItem.java new file mode 100644 index 00000000..e706aa05 --- /dev/null +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SourceFileItem.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 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: + * Marc R. Hoffmann - initial API and implementation + * + *******************************************************************************/ +package org.jacoco.report.internal.html.page; + +import org.jacoco.core.analysis.ICoverageNode; +import org.jacoco.core.analysis.ISourceFileCoverage; +import org.jacoco.report.internal.ReportOutputFolder; +import org.jacoco.report.internal.html.resources.Styles; +import org.jacoco.report.internal.html.table.ITableItem; + +/** + * Table items representing a source file which cannot be linked. + * + */ +final class SourceFileItem implements ITableItem { + + private final ICoverageNode node; + + SourceFileItem(final ISourceFileCoverage node) { + this.node = node; + } + + public String getLinkLabel() { + return node.getName(); + } + + public String getLinkStyle() { + return Styles.EL_SOURCE; + } + + public String getLink(final ReportOutputFolder base) { + return null; + } + + public ICoverageNode getNode() { + return node; + } + +} diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/resources/Styles.java b/org.jacoco.report/src/org/jacoco/report/internal/html/resources/Styles.java index 8899e87e..f2a9a94c 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/resources/Styles.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/resources/Styles.java @@ -19,6 +19,9 @@ public final class Styles { /** Breadcrumb bar */ public static final String BREADCRUMB = "breadcrumb"; + /** Info links within the Breadcrumb bar */ + public static final String INFO = "info"; + /** Footer */ public static final String FOOTER = "footer"; diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/resources/report.css b/org.jacoco.report/src/org/jacoco/report/internal/html/resources/report.css index b726f252..08eba792 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/resources/report.css +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/resources/report.css @@ -13,6 +13,13 @@ h1 { padding:2px 4px 2px 4px; } +.breadcrumb .info { + float:right; +} + +.breadcrumb .info a { + margin-left:8px; +} .el_report { padding-left:18px; |