Skip to content

refactor: create reporter, serializer, and domain modules #405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ on:
pull_request:

jobs:
test:
test-plugin:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ 'ubuntu-latest', 'windows-latest' ]
java: [ '8', '11', '17' ]
java: ['8', '17']
scala: [
{ version: '2.12.15' },
{ version: '2.12.14' },
Expand All @@ -40,7 +40,30 @@ jobs:
java-version: ${{ matrix.java }}

- name: run tests
run: sbt ++${{ matrix.scala.version }} test
run: sbt ++${{ matrix.scala.version }} plugin/test

test-the-rest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ 'ubuntu-latest', 'windows-latest' ]
java: ['8', '17' ]
module: ['runtime', 'runtimeJS', 'reporter', 'domain', 'serializer']
steps:
- name: checkout the repo
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Set up JVM
uses: actions/setup-java@v2
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}

- name: run tests
run: sbt +${{ matrix.module }}/test

style-check:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "3.0.7"
version = "3.0.8"
project.git = true
runner.dialect = "scala213"
assumeStandardLibraryStripMargin = true
Expand Down
73 changes: 56 additions & 17 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import sbtcrossproject.CrossProject
import sbtcrossproject.CrossType

val munitVersion = "0.7.29"
val scalametaVersion = "4.4.28"
val defaultScala213 = "2.13.6"
val bin212 =
lazy val munitVersion = "0.7.29"
lazy val scalametaVersion = "4.4.28"
lazy val defaultScala212 = "2.12.15"
lazy val defaultScala213 = "2.13.6"
lazy val defaultScala3 = "3.1.0"
lazy val bin212 =
Seq(
"2.12.15",
defaultScala212,
"2.12.14",
"2.12.13",
"2.12.12",
Expand All @@ -15,7 +17,7 @@ val bin212 =
"2.12.9",
"2.12.8"
)
val bin213 =
lazy val bin213 =
Seq(
defaultScala213,
"2.13.5",
Expand Down Expand Up @@ -80,8 +82,7 @@ lazy val sharedSettings = List(
} else {
scalacOptions.value
}
},
crossScalaVersions := bin212 ++ bin213
}
)

lazy val root = Project("scalac-scoverage", file("."))
Expand All @@ -90,16 +91,17 @@ lazy val root = Project("scalac-scoverage", file("."))
publishArtifact := false,
publishLocal := {}
)
.aggregate(plugin, runtime.jvm, runtime.js)
.aggregate(plugin, runtime.jvm, runtime.js, reporter, domain, serializer)

lazy val runtime = CrossProject(
"scalac-scoverage-runtime",
file("scalac-scoverage-runtime")
"runtime",
file("runtime")
)(JVMPlatform, JSPlatform)
.crossType(CrossType.Full)
.withoutSuffixFor(JVMPlatform)
.settings(
name := "scalac-scoverage-runtime",
crossScalaVersions := Seq(defaultScala212, defaultScala213),
crossTarget := target.value / s"scala-${scalaVersion.value}",
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % munitVersion % Test
Expand All @@ -113,26 +115,63 @@ lazy val runtime = CrossProject(
scalaJSStage := FastOptStage
)

lazy val `scalac-scoverage-runtimeJVM` = runtime.jvm
lazy val `scalac-scoverage-runtimeJS` = runtime.js
lazy val `runtimeJVM` = runtime.jvm
lazy val `runtimeJS` = runtime.js

lazy val plugin =
Project("scalac-scoverage-plugin", file("scalac-scoverage-plugin"))
.dependsOn(`scalac-scoverage-runtimeJVM` % Test)
project
.dependsOn(runtimeJVM % Test)
.settings(
name := "scalac-scoverage-plugin",
crossTarget := target.value / s"scala-${scalaVersion.value}",
crossScalaVersions := bin212 ++ bin213,
crossVersion := CrossVersion.full,
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-xml" % "2.0.0",
"org.scalameta" %% "munit" % munitVersion % Test,
"org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided
),
sharedSettings
)
.settings(
(Test / unmanagedSourceDirectories) += (Test / sourceDirectory).value / "scala-2.12+"
Test / unmanagedSourceDirectories += (Test / sourceDirectory).value / "scala-2.12+"
)
.dependsOn(domain, reporter % "test->compile", serializer)

lazy val reporter =
project
.settings(
name := "scalac-scoverage-reporter",
libraryDependencies ++= Seq(
"org.scala-lang.modules" %% "scala-xml" % "2.0.0",
"org.scalameta" %% "munit" % munitVersion % Test
),
sharedSettings,
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
)
.dependsOn(domain, serializer)

lazy val domain =
project
.settings(
name := "scalac-scoverage-domain",
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % munitVersion % Test
),
sharedSettings,
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
)

lazy val serializer =
project
.settings(
name := "scalac-scoverage-serializer",
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % munitVersion % Test
),
sharedSettings,
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
)
.dependsOn(domain)

addCommandAlias(
"styleFix",
Expand Down
26 changes: 26 additions & 0 deletions domain/src/main/scala/scoverage/domain/Builders.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package scoverage.domain

trait MethodBuilders {
def statements: Iterable[Statement]
def methods: Seq[MeasuredMethod] = {
statements
.groupBy(stmt =>
stmt.location.packageName + "/" + stmt.location.className + "/" + stmt.location.method
)
.map(arg => MeasuredMethod(arg._1, arg._2))
.toSeq
}
def methodCount = methods.size
}

trait PackageBuilders {
def statements: Iterable[Statement]
def packageCount = packages.size
def packages: Seq[MeasuredPackage] = {
statements
.groupBy(_.location.packageName)
.map(arg => MeasuredPackage(arg._1, arg._2))
.toSeq
.sortBy(_.name)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package scoverage.report
package scoverage.domain

import scala.io.Codec
import scala.io.Source

import _root_.scoverage.MeasuredFile

/** @author Stephen Samuel */
class CodeGrid(mFile: MeasuredFile, sourceEncoding: Option[String]) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package scoverage
package scoverage.domain

object Constants {
// the file that contains the statement mappings
Expand All @@ -10,4 +10,6 @@ object Constants {
val DataDir = "scoverage-data"
// the prefix the measurement files have
val MeasurementsPrefix = "scoverage.measurements."

val CoverageDataFormatVersion = "3.0"
}
94 changes: 94 additions & 0 deletions domain/src/main/scala/scoverage/domain/CoverageMetrics.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package scoverage.domain

import java.io.File

trait CoverageMetrics {
def statements: Iterable[Statement]
def statementCount: Int = statements.size

def ignoredStatements: Iterable[Statement]
def ignoredStatementCount: Int = ignoredStatements.size

def invokedStatements: Iterable[Statement] = statements.filter(_.count > 0)
def invokedStatementCount = invokedStatements.size
def statementCoverage: Double = if (statementCount == 0) 1
else invokedStatementCount / statementCount.toDouble
def statementCoveragePercent = statementCoverage * 100
def statementCoverageFormatted: String = DoubleFormat.twoFractionDigits(
statementCoveragePercent
)
def branches: Iterable[Statement] = statements.filter(_.branch)
def branchCount: Int = branches.size
def branchCoveragePercent = branchCoverage * 100
def invokedBranches: Iterable[Statement] = branches.filter(_.count > 0)
def invokedBranchesCount = invokedBranches.size

/** @see http://stackoverflow.com/questions/25184716/scoverage-ambiguous-measurement-from-branch-coverage
*/
def branchCoverage: Double = {
// if there are zero branches, then we have a single line of execution.
// in that case, if there is at least some coverage, we have covered the branch.
// if there is no coverage then we have not covered the branch
if (branchCount == 0) {
if (statementCoverage > 0) 1
else 0
} else {
invokedBranchesCount / branchCount.toDouble
}
}
def branchCoverageFormatted: String =
DoubleFormat.twoFractionDigits(branchCoveragePercent)
}

case class MeasuredMethod(name: String, statements: Iterable[Statement])
extends CoverageMetrics {
override def ignoredStatements: Iterable[Statement] = Seq()
}

case class MeasuredClass(fullClassName: String, statements: Iterable[Statement])
extends CoverageMetrics
with MethodBuilders {

def source: String = statements.head.source
def loc = statements.map(_.line).max

/** The class name for display is the FQN minus the package,
* for example "com.a.Foo.Bar.Baz" should display as "Foo.Bar.Baz"
* and "com.a.Foo" should display as "Foo".
*
* This is used in the class lists in the package and overview pages.
*/
def displayClassName = statements.headOption
.map(_.location)
.map { location =>
location.fullClassName.stripPrefix(location.packageName + ".")
}
.getOrElse(fullClassName)

override def ignoredStatements: Iterable[Statement] = Seq()
}

case class MeasuredPackage(name: String, statements: Iterable[Statement])
extends CoverageMetrics
with ClassCoverage
with ClassBuilders
with FileBuilders {
override def ignoredStatements: Iterable[Statement] = Seq()
}

case class MeasuredFile(source: String, statements: Iterable[Statement])
extends CoverageMetrics
with ClassCoverage
with ClassBuilders {
def filename = new File(source).getName
def loc = statements.map(_.line).max

override def ignoredStatements: Iterable[Statement] = Seq()
}

trait ClassCoverage {
this: ClassBuilders =>
val statements: Iterable[Statement]
def invokedClasses: Int = classes.count(_.statements.count(_.count > 0) > 0)
def classCoverage: Double = invokedClasses / classes.size.toDouble
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package scoverage
package scoverage.domain

import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
Expand Down
14 changes: 14 additions & 0 deletions domain/src/main/scala/scoverage/domain/Location.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package scoverage.domain

/** @param packageName the name of the enclosing package
* @param className the name of the closest enclosing class
* @param fullClassName the fully qualified name of the closest enclosing class
*/
case class Location(
packageName: String,
className: String,
fullClassName: String,
classType: ClassType,
method: String,
sourcePath: String
)
25 changes: 25 additions & 0 deletions domain/src/main/scala/scoverage/domain/Statement.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package scoverage.domain

import scala.collection.mutable

case class Statement(
location: Location,
id: Int,
start: Int,
end: Int,
line: Int,
desc: String,
symbolName: String,
treeName: String,
branch: Boolean,
var count: Int = 0,
ignored: Boolean = false,
tests: mutable.Set[String] = mutable.Set[String]()
) extends java.io.Serializable {
def source = location.sourcePath
def invoked(test: String): Unit = {
count = count + 1
if (test != "") tests += test
}
def isInvoked = count > 0
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package scoverage.report
package scoverage.domain

/** @author Stephen Samuel */
sealed trait StatementStatus
Expand Down
Loading