All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scoverage.reporter.ScoverageHtmlWriter.scala Maven / Gradle / Ivy

package scoverage.reporter

import java.io.File
import java.util.Date

import scala.xml.Node

import scoverage.domain.CodeGrid
import scoverage.domain.Coverage
import scoverage.domain.MeasuredClass
import scoverage.domain.MeasuredFile
import scoverage.domain.MeasuredPackage

/** @author Stephen Samuel */
class ScoverageHtmlWriter(
    sourceDirectories: Seq[File],
    outputDir: File,
    sourceEncoding: Option[String]
) extends BaseReportWriter(sourceDirectories, outputDir, sourceEncoding) {

  // to be used by gradle-scoverage plugin
  def this(
      sourceDirectories: Array[File],
      outputDir: File,
      sourceEncoding: Option[String]
  ) = {
    this(sourceDirectories.toSeq, outputDir, sourceEncoding)
  }

  // for backward compatibility only
  def this(sourceDirectories: Seq[File], outputDir: File) = {
    this(sourceDirectories, outputDir, None);
  }

  // for backward compatibility only
  def this(sourceDirectory: File, outputDir: File) = {
    this(Seq(sourceDirectory), outputDir)
  }

  def write(coverage: Coverage): Unit = {
    val indexFile = new File(outputDir.getAbsolutePath + "/index.html")
    val cssFile = new File(outputDir.getAbsolutePath + "/pure-min.css")
    val packageFile = new File(outputDir.getAbsolutePath + "/packages.html")
    val overviewFile = new File(outputDir.getAbsolutePath + "/overview.html")

    val index = {
      val in = getClass.getResourceAsStream("/scoverage/index.html")
      try IOUtils.readStreamAsString(in)
      finally in.close()
    }
    val css = {
      val in = getClass.getResourceAsStream("/scoverage/pure-min.css")
      try IOUtils.readStreamAsString(in)
      finally in.close()
    }
    IOUtils.writeToFile(indexFile, index, sourceEncoding)
    IOUtils.writeToFile(cssFile, css, sourceEncoding)
    IOUtils.writeToFile(
      packageFile,
      packageList(coverage).toString(),
      sourceEncoding
    )
    IOUtils.writeToFile(
      overviewFile,
      overview(coverage).toString(),
      sourceEncoding
    )

    coverage.packages.foreach(writePackage)
  }

  private def writePackage(pkg: MeasuredPackage): Unit = {
    // package overview files are written out using a filename that respects the package name
    // that means package com.example declared in a class at src/main/scala/mystuff/MyClass.scala will be written
    // to com.example.html
    val file = new File(outputDir, packageOverviewRelativePath(pkg))
    file.getParentFile.mkdirs()
    IOUtils.writeToFile(file, packageOverview(pkg).toString(), sourceEncoding)
    pkg.files.foreach(writeFile)
  }

  private def writeFile(mfile: MeasuredFile): Unit = {
    // each highlighted file is written out using the same structure as the original file.
    val file = new File(outputDir, relativeSource(mfile.source) + ".html")
    file.getParentFile.mkdirs()
    IOUtils.writeToFile(file, filePage(mfile).toString(), sourceEncoding)
  }

  private def packageOverviewRelativePath(pkg: MeasuredPackage) =
    pkg.name.replace("", "(empty)") + ".html"

  private def filePage(mfile: MeasuredFile): Node = {
    val filename = relativeSource(mfile.source) + ".html"
    val css =
      "table.codegrid { font-family: monospace; font-size: 12px; width: auto!important; }" +
        "table.statementlist { width: auto!important; font-size: 13px; } " +
        "table.codegrid td { padding: 0!important; border: 0!important } " +
        "table td.linenumber { width: 40px!important; } "
    
      
        
        
          {filename}
        
        {plugins}
        
      
      
        
        
{xml.Unparsed(new CodeGrid(mfile, sourceEncoding).highlighted)}
{new StatementWriter(mfile).output}
} def header = { val css = """.meter { | height: 14px; | position: relative; | background: #BB2020; |} | |.meter span { | display: block; | height: 100%; | background-color: rgb(43,194,83); | background-image: -webkit-gradient( | linear, | left bottom, | left top, | color-stop(0, rgb(43,194,83)), | color-stop(1, rgb(84,240,84)) | ); | background-image: -webkit-linear-gradient( | center bottom, | rgb(43,194,83) 37%, | rgb(84,240,84) 69% | ); | background-image: -moz-linear-gradient( | center bottom, | rgb(43,194,83) 37%, | rgb(84,240,84) 69% | ); | background-image: -ms-linear-gradient( | center bottom, | rgb(43,194,83) 37%, | rgb(84,240,84) 69% | ); | background-image: -o-linear-gradient( | center bottom, | rgb(43,194,83) 37%, | rgb(84,240,84) 69% | ); | -webkit-box-shadow: | inset 0 2px 9px rgba(255,255,255,0.3), | inset 0 -2px 6px rgba(0,0,0,0.4); | -moz-box-shadow: | inset 0 2px 9px rgba(255,255,255,0.3), | inset 0 -2px 6px rgba(0,0,0,0.4); | position: relative; | overflow: hidden; |}""".stripMargin Scoverage Code Coverage {plugins} } def packageOverview(pack: MeasuredPackage): Node = { {header} {classesTable(pack.classes, addPath = false)} } def classesTable(classes: Iterable[MeasuredClass], addPath: Boolean): Node = { {classes.toSeq.sortBy(_.fullClassName) map classRow}
Class Source file Lines Methods Statements Invoked Coverage Branches Invoked Coverage
} def classRow(klass: MeasuredClass): Node = { val filename: String = { val fileRelativeToSource = new File( relativeSource(klass.source) + ".html" ) val path = fileRelativeToSource.getParent val value = fileRelativeToSource.getName if (path.ne("")) { // (Normalise the pathSeparator to "/" in case we are running on Windows) fileRelativeToSource.toString.replace(File.separator, "/") } else { value } } val statement0f = Math.round(klass.statementCoveragePercent).toInt.toString val branch0f = Math.round(klass.branchCoveragePercent).toInt.toString {klass.displayClassName} { klass.statements.headOption .map(_.source.split(File.separatorChar).last) .getOrElse("") } {klass.loc.toString} {klass.methodCount.toString} {klass.statementCount.toString} {klass.invokedStatementCount.toString}
{klass.statementCoverageFormatted} % {klass.branchCount.toString} {klass.invokedBranchesCount.toString}
{klass.branchCoverageFormatted} % } def packageList(coverage: Coverage): Node = { Scoverage Code Coverage {plugins} { coverage.packages.map(arg => ) }
All packages {coverage.statementCoverageFormatted}%
{ arg.name } {arg.statementCoverageFormatted}%
} def risks(coverage: Coverage, limit: Int) = { { coverage .risks(limit) .map(klass => ) }
Class Lines Methods Statements Statement Rate Branches Branch Rate
{klass.displayClassName} {klass.loc.toString} {klass.methodCount.toString} {klass.statementCount.toString} {klass.statementCoverageFormatted} % {klass.branchCount.toString} {klass.branchCoverageFormatted} %
} def packages2(coverage: Coverage) = { val rows = coverage.packages.map(arg => { {arg.name} {arg.invokedClasses.toString} / {arg.classCount} ( {arg.classCoverage.toString} %) {arg.invokedStatements.toString()} / {arg.statementCount} ( {arg.statementCoverageFormatted} %) }) {rows}
} def overview(coverage: Coverage): Node = { {header}
SCoverage generated at {new Date().toString}
{stats(coverage)}
{classesTable(coverage.classes, addPath = true)}
} def stats(coverage: Coverage): Node = { val statement0f = Math.round(coverage.statementCoveragePercent).toInt.toString val branch0f = Math.round(coverage.branchCoveragePercent).toInt.toString
Lines of code: {coverage.loc.toString} Files: {coverage.fileCount.toString} Classes: {coverage.classCount.toString} Methods: {coverage.methodCount.toString}
Lines per file: {coverage.linesPerFileFormatted} Packages: {coverage.packageCount.toString} Classes per package: {coverage.avgClassesPerPackageFormatted} Methods per class: {coverage.avgMethodsPerClassFormatted}
Total statements: {coverage.statementCount.toString} Invoked statements: {coverage.invokedStatementCount.toString} Total branches: {coverage.branchCount.toString} Invoked branches: {coverage.invokedBranchesCount.toString}
Ignored statements: {coverage.ignoredStatementCount.toString}
Statement coverage: {coverage.statementCoverageFormatted} %
Branch coverage: {coverage.branchCoverageFormatted} %
} def plugins = { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy