Skip to content

Commit fe669fa

Browse files
authored
refactor: create reporter, serializer, and domain modules (#405)
* feature: break reporter out into its own module * refactor: break domain out of reporter This allows for a bit more fine-grained control over what we are putting on the classpath. This should allow us to no longer need to include scala-xml on the users compiler claspath and only use it while doing the reporting. * refactor: Some more rearranging to ensure scala-xml isn't included in the compiler deps * Bump scalafmt to 3.0.8 * refactor: split out serializer module
1 parent 8a3f5cf commit fe669fa

File tree

64 files changed

+611
-388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+611
-388
lines changed

Diff for: .github/workflows/ci.yml

+26-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ on:
1212
pull_request:
1313

1414
jobs:
15-
test:
15+
test-plugin:
1616
runs-on: ${{ matrix.os }}
1717
strategy:
1818
fail-fast: false
1919
matrix:
2020
os: [ 'ubuntu-latest', 'windows-latest' ]
21-
java: [ '8', '11', '17' ]
21+
java: ['8', '17']
2222
scala: [
2323
{ version: '2.12.15' },
2424
{ version: '2.12.14' },
@@ -40,7 +40,30 @@ jobs:
4040
java-version: ${{ matrix.java }}
4141

4242
- name: run tests
43-
run: sbt ++${{ matrix.scala.version }} test
43+
run: sbt ++${{ matrix.scala.version }} plugin/test
44+
45+
test-the-rest:
46+
runs-on: ${{ matrix.os }}
47+
strategy:
48+
fail-fast: false
49+
matrix:
50+
os: [ 'ubuntu-latest', 'windows-latest' ]
51+
java: ['8', '17' ]
52+
module: ['runtime', 'runtimeJS', 'reporter', 'domain', 'serializer']
53+
steps:
54+
- name: checkout the repo
55+
uses: actions/checkout@v2
56+
with:
57+
fetch-depth: 0
58+
59+
- name: Set up JVM
60+
uses: actions/setup-java@v2
61+
with:
62+
distribution: 'temurin'
63+
java-version: ${{ matrix.java }}
64+
65+
- name: run tests
66+
run: sbt +${{ matrix.module }}/test
4467

4568
style-check:
4669
runs-on: ubuntu-latest

Diff for: .scalafmt.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = "3.0.7"
1+
version = "3.0.8"
22
project.git = true
33
runner.dialect = "scala213"
44
assumeStandardLibraryStripMargin = true

Diff for: build.sbt

+56-17
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import sbtcrossproject.CrossProject
22
import sbtcrossproject.CrossType
33

4-
val munitVersion = "0.7.29"
5-
val scalametaVersion = "4.4.28"
6-
val defaultScala213 = "2.13.6"
7-
val bin212 =
4+
lazy val munitVersion = "0.7.29"
5+
lazy val scalametaVersion = "4.4.28"
6+
lazy val defaultScala212 = "2.12.15"
7+
lazy val defaultScala213 = "2.13.6"
8+
lazy val defaultScala3 = "3.1.0"
9+
lazy val bin212 =
810
Seq(
9-
"2.12.15",
11+
defaultScala212,
1012
"2.12.14",
1113
"2.12.13",
1214
"2.12.12",
@@ -15,7 +17,7 @@ val bin212 =
1517
"2.12.9",
1618
"2.12.8"
1719
)
18-
val bin213 =
20+
lazy val bin213 =
1921
Seq(
2022
defaultScala213,
2123
"2.13.5",
@@ -80,8 +82,7 @@ lazy val sharedSettings = List(
8082
} else {
8183
scalacOptions.value
8284
}
83-
},
84-
crossScalaVersions := bin212 ++ bin213
85+
}
8586
)
8687

8788
lazy val root = Project("scalac-scoverage", file("."))
@@ -90,16 +91,17 @@ lazy val root = Project("scalac-scoverage", file("."))
9091
publishArtifact := false,
9192
publishLocal := {}
9293
)
93-
.aggregate(plugin, runtime.jvm, runtime.js)
94+
.aggregate(plugin, runtime.jvm, runtime.js, reporter, domain, serializer)
9495

9596
lazy val runtime = CrossProject(
96-
"scalac-scoverage-runtime",
97-
file("scalac-scoverage-runtime")
97+
"runtime",
98+
file("runtime")
9899
)(JVMPlatform, JSPlatform)
99100
.crossType(CrossType.Full)
100101
.withoutSuffixFor(JVMPlatform)
101102
.settings(
102103
name := "scalac-scoverage-runtime",
104+
crossScalaVersions := Seq(defaultScala212, defaultScala213),
103105
crossTarget := target.value / s"scala-${scalaVersion.value}",
104106
libraryDependencies ++= Seq(
105107
"org.scalameta" %% "munit" % munitVersion % Test
@@ -113,26 +115,63 @@ lazy val runtime = CrossProject(
113115
scalaJSStage := FastOptStage
114116
)
115117

116-
lazy val `scalac-scoverage-runtimeJVM` = runtime.jvm
117-
lazy val `scalac-scoverage-runtimeJS` = runtime.js
118+
lazy val `runtimeJVM` = runtime.jvm
119+
lazy val `runtimeJS` = runtime.js
118120

119121
lazy val plugin =
120-
Project("scalac-scoverage-plugin", file("scalac-scoverage-plugin"))
121-
.dependsOn(`scalac-scoverage-runtimeJVM` % Test)
122+
project
123+
.dependsOn(runtimeJVM % Test)
122124
.settings(
123125
name := "scalac-scoverage-plugin",
124126
crossTarget := target.value / s"scala-${scalaVersion.value}",
127+
crossScalaVersions := bin212 ++ bin213,
125128
crossVersion := CrossVersion.full,
126129
libraryDependencies ++= Seq(
127-
"org.scala-lang.modules" %% "scala-xml" % "2.0.0",
128130
"org.scalameta" %% "munit" % munitVersion % Test,
129131
"org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided
130132
),
131133
sharedSettings
132134
)
133135
.settings(
134-
(Test / unmanagedSourceDirectories) += (Test / sourceDirectory).value / "scala-2.12+"
136+
Test / unmanagedSourceDirectories += (Test / sourceDirectory).value / "scala-2.12+"
137+
)
138+
.dependsOn(domain, reporter % "test->compile", serializer)
139+
140+
lazy val reporter =
141+
project
142+
.settings(
143+
name := "scalac-scoverage-reporter",
144+
libraryDependencies ++= Seq(
145+
"org.scala-lang.modules" %% "scala-xml" % "2.0.0",
146+
"org.scalameta" %% "munit" % munitVersion % Test
147+
),
148+
sharedSettings,
149+
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
150+
)
151+
.dependsOn(domain, serializer)
152+
153+
lazy val domain =
154+
project
155+
.settings(
156+
name := "scalac-scoverage-domain",
157+
libraryDependencies ++= Seq(
158+
"org.scalameta" %% "munit" % munitVersion % Test
159+
),
160+
sharedSettings,
161+
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
162+
)
163+
164+
lazy val serializer =
165+
project
166+
.settings(
167+
name := "scalac-scoverage-serializer",
168+
libraryDependencies ++= Seq(
169+
"org.scalameta" %% "munit" % munitVersion % Test
170+
),
171+
sharedSettings,
172+
crossScalaVersions := Seq(defaultScala212, defaultScala213, defaultScala3)
135173
)
174+
.dependsOn(domain)
136175

137176
addCommandAlias(
138177
"styleFix",
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package scoverage.domain
2+
3+
trait MethodBuilders {
4+
def statements: Iterable[Statement]
5+
def methods: Seq[MeasuredMethod] = {
6+
statements
7+
.groupBy(stmt =>
8+
stmt.location.packageName + "/" + stmt.location.className + "/" + stmt.location.method
9+
)
10+
.map(arg => MeasuredMethod(arg._1, arg._2))
11+
.toSeq
12+
}
13+
def methodCount = methods.size
14+
}
15+
16+
trait PackageBuilders {
17+
def statements: Iterable[Statement]
18+
def packageCount = packages.size
19+
def packages: Seq[MeasuredPackage] = {
20+
statements
21+
.groupBy(_.location.packageName)
22+
.map(arg => MeasuredPackage(arg._1, arg._2))
23+
.toSeq
24+
.sortBy(_.name)
25+
}
26+
}

Diff for: scalac-scoverage-plugin/src/main/scala/scoverage/report/CodeGrid.scala renamed to domain/src/main/scala/scoverage/domain/CodeGrid.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
package scoverage.report
1+
package scoverage.domain
22

33
import scala.io.Codec
44
import scala.io.Source
55

6-
import _root_.scoverage.MeasuredFile
7-
86
/** @author Stephen Samuel */
97
class CodeGrid(mFile: MeasuredFile, sourceEncoding: Option[String]) {
108

Diff for: scalac-scoverage-plugin/src/main/scala/scoverage/Constants.scala renamed to domain/src/main/scala/scoverage/domain/Constants.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package scoverage
1+
package scoverage.domain
22

33
object Constants {
44
// the file that contains the statement mappings
@@ -10,4 +10,6 @@ object Constants {
1010
val DataDir = "scoverage-data"
1111
// the prefix the measurement files have
1212
val MeasurementsPrefix = "scoverage.measurements."
13+
14+
val CoverageDataFormatVersion = "3.0"
1315
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package scoverage.domain
2+
3+
import java.io.File
4+
5+
trait CoverageMetrics {
6+
def statements: Iterable[Statement]
7+
def statementCount: Int = statements.size
8+
9+
def ignoredStatements: Iterable[Statement]
10+
def ignoredStatementCount: Int = ignoredStatements.size
11+
12+
def invokedStatements: Iterable[Statement] = statements.filter(_.count > 0)
13+
def invokedStatementCount = invokedStatements.size
14+
def statementCoverage: Double = if (statementCount == 0) 1
15+
else invokedStatementCount / statementCount.toDouble
16+
def statementCoveragePercent = statementCoverage * 100
17+
def statementCoverageFormatted: String = DoubleFormat.twoFractionDigits(
18+
statementCoveragePercent
19+
)
20+
def branches: Iterable[Statement] = statements.filter(_.branch)
21+
def branchCount: Int = branches.size
22+
def branchCoveragePercent = branchCoverage * 100
23+
def invokedBranches: Iterable[Statement] = branches.filter(_.count > 0)
24+
def invokedBranchesCount = invokedBranches.size
25+
26+
/** @see http://stackoverflow.com/questions/25184716/scoverage-ambiguous-measurement-from-branch-coverage
27+
*/
28+
def branchCoverage: Double = {
29+
// if there are zero branches, then we have a single line of execution.
30+
// in that case, if there is at least some coverage, we have covered the branch.
31+
// if there is no coverage then we have not covered the branch
32+
if (branchCount == 0) {
33+
if (statementCoverage > 0) 1
34+
else 0
35+
} else {
36+
invokedBranchesCount / branchCount.toDouble
37+
}
38+
}
39+
def branchCoverageFormatted: String =
40+
DoubleFormat.twoFractionDigits(branchCoveragePercent)
41+
}
42+
43+
case class MeasuredMethod(name: String, statements: Iterable[Statement])
44+
extends CoverageMetrics {
45+
override def ignoredStatements: Iterable[Statement] = Seq()
46+
}
47+
48+
case class MeasuredClass(fullClassName: String, statements: Iterable[Statement])
49+
extends CoverageMetrics
50+
with MethodBuilders {
51+
52+
def source: String = statements.head.source
53+
def loc = statements.map(_.line).max
54+
55+
/** The class name for display is the FQN minus the package,
56+
* for example "com.a.Foo.Bar.Baz" should display as "Foo.Bar.Baz"
57+
* and "com.a.Foo" should display as "Foo".
58+
*
59+
* This is used in the class lists in the package and overview pages.
60+
*/
61+
def displayClassName = statements.headOption
62+
.map(_.location)
63+
.map { location =>
64+
location.fullClassName.stripPrefix(location.packageName + ".")
65+
}
66+
.getOrElse(fullClassName)
67+
68+
override def ignoredStatements: Iterable[Statement] = Seq()
69+
}
70+
71+
case class MeasuredPackage(name: String, statements: Iterable[Statement])
72+
extends CoverageMetrics
73+
with ClassCoverage
74+
with ClassBuilders
75+
with FileBuilders {
76+
override def ignoredStatements: Iterable[Statement] = Seq()
77+
}
78+
79+
case class MeasuredFile(source: String, statements: Iterable[Statement])
80+
extends CoverageMetrics
81+
with ClassCoverage
82+
with ClassBuilders {
83+
def filename = new File(source).getName
84+
def loc = statements.map(_.line).max
85+
86+
override def ignoredStatements: Iterable[Statement] = Seq()
87+
}
88+
89+
trait ClassCoverage {
90+
this: ClassBuilders =>
91+
val statements: Iterable[Statement]
92+
def invokedClasses: Int = classes.count(_.statements.count(_.count > 0) > 0)
93+
def classCoverage: Double = invokedClasses / classes.size.toDouble
94+
}

Diff for: scalac-scoverage-plugin/src/main/scala/scoverage/DoubleFormat.scala renamed to domain/src/main/scala/scoverage/domain/DoubleFormat.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package scoverage
1+
package scoverage.domain
22

33
import java.text.DecimalFormat
44
import java.text.DecimalFormatSymbols
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package scoverage.domain
2+
3+
/** @param packageName the name of the enclosing package
4+
* @param className the name of the closest enclosing class
5+
* @param fullClassName the fully qualified name of the closest enclosing class
6+
*/
7+
case class Location(
8+
packageName: String,
9+
className: String,
10+
fullClassName: String,
11+
classType: ClassType,
12+
method: String,
13+
sourcePath: String
14+
)
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package scoverage.domain
2+
3+
import scala.collection.mutable
4+
5+
case class Statement(
6+
location: Location,
7+
id: Int,
8+
start: Int,
9+
end: Int,
10+
line: Int,
11+
desc: String,
12+
symbolName: String,
13+
treeName: String,
14+
branch: Boolean,
15+
var count: Int = 0,
16+
ignored: Boolean = false,
17+
tests: mutable.Set[String] = mutable.Set[String]()
18+
) extends java.io.Serializable {
19+
def source = location.sourcePath
20+
def invoked(test: String): Unit = {
21+
count = count + 1
22+
if (test != "") tests += test
23+
}
24+
def isInvoked = count > 0
25+
}

Diff for: scalac-scoverage-plugin/src/main/scala/scoverage/report/status.scala renamed to domain/src/main/scala/scoverage/domain/StatementStatus.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package scoverage.report
1+
package scoverage.domain
22

33
/** @author Stephen Samuel */
44
sealed trait StatementStatus

0 commit comments

Comments
 (0)