diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95fa8444..06d8582f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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' }, @@ -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 diff --git a/.scalafmt.conf b/.scalafmt.conf index 4a5fc51c..1e9af78a 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.0.7" +version = "3.0.8" project.git = true runner.dialect = "scala213" assumeStandardLibraryStripMargin = true diff --git a/build.sbt b/build.sbt index 6d9b9ad7..868f3b4a 100644 --- a/build.sbt +++ b/build.sbt @@ -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", @@ -15,7 +17,7 @@ val bin212 = "2.12.9", "2.12.8" ) -val bin213 = +lazy val bin213 = Seq( defaultScala213, "2.13.5", @@ -80,8 +82,7 @@ lazy val sharedSettings = List( } else { scalacOptions.value } - }, - crossScalaVersions := bin212 ++ bin213 + } ) lazy val root = Project("scalac-scoverage", file(".")) @@ -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 @@ -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", diff --git a/domain/src/main/scala/scoverage/domain/Builders.scala b/domain/src/main/scala/scoverage/domain/Builders.scala new file mode 100644 index 00000000..040874a0 --- /dev/null +++ b/domain/src/main/scala/scoverage/domain/Builders.scala @@ -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) + } +} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CodeGrid.scala b/domain/src/main/scala/scoverage/domain/CodeGrid.scala similarity index 98% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/CodeGrid.scala rename to domain/src/main/scala/scoverage/domain/CodeGrid.scala index 60300245..70b33f96 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CodeGrid.scala +++ b/domain/src/main/scala/scoverage/domain/CodeGrid.scala @@ -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]) { diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/Constants.scala b/domain/src/main/scala/scoverage/domain/Constants.scala similarity index 87% rename from scalac-scoverage-plugin/src/main/scala/scoverage/Constants.scala rename to domain/src/main/scala/scoverage/domain/Constants.scala index e1b1e57e..c3633d9c 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/Constants.scala +++ b/domain/src/main/scala/scoverage/domain/Constants.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.domain object Constants { // the file that contains the statement mappings @@ -10,4 +10,6 @@ object Constants { val DataDir = "scoverage-data" // the prefix the measurement files have val MeasurementsPrefix = "scoverage.measurements." + + val CoverageDataFormatVersion = "3.0" } diff --git a/domain/src/main/scala/scoverage/domain/CoverageMetrics.scala b/domain/src/main/scala/scoverage/domain/CoverageMetrics.scala new file mode 100644 index 00000000..ad296976 --- /dev/null +++ b/domain/src/main/scala/scoverage/domain/CoverageMetrics.scala @@ -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 +} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/DoubleFormat.scala b/domain/src/main/scala/scoverage/domain/DoubleFormat.scala similarity index 95% rename from scalac-scoverage-plugin/src/main/scala/scoverage/DoubleFormat.scala rename to domain/src/main/scala/scoverage/domain/DoubleFormat.scala index e470672a..edcb8def 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/DoubleFormat.scala +++ b/domain/src/main/scala/scoverage/domain/DoubleFormat.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.domain import java.text.DecimalFormat import java.text.DecimalFormatSymbols diff --git a/domain/src/main/scala/scoverage/domain/Location.scala b/domain/src/main/scala/scoverage/domain/Location.scala new file mode 100644 index 00000000..89104cc7 --- /dev/null +++ b/domain/src/main/scala/scoverage/domain/Location.scala @@ -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 +) diff --git a/domain/src/main/scala/scoverage/domain/Statement.scala b/domain/src/main/scala/scoverage/domain/Statement.scala new file mode 100644 index 00000000..f92f4f46 --- /dev/null +++ b/domain/src/main/scala/scoverage/domain/Statement.scala @@ -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 +} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/status.scala b/domain/src/main/scala/scoverage/domain/StatementStatus.scala similarity index 88% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/status.scala rename to domain/src/main/scala/scoverage/domain/StatementStatus.scala index b8b82387..3aa04e7e 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/status.scala +++ b/domain/src/main/scala/scoverage/domain/StatementStatus.scala @@ -1,4 +1,4 @@ -package scoverage.report +package scoverage.domain /** @author Stephen Samuel */ sealed trait StatementStatus diff --git a/domain/src/main/scala/scoverage/domain/coverage.scala b/domain/src/main/scala/scoverage/domain/coverage.scala new file mode 100644 index 00000000..a6b8764c --- /dev/null +++ b/domain/src/main/scala/scoverage/domain/coverage.scala @@ -0,0 +1,90 @@ +package scoverage.domain + +import scala.collection.mutable + +/** @author Stephen Samuel + */ +case class Coverage() + extends CoverageMetrics + with MethodBuilders + with java.io.Serializable + with ClassBuilders + with PackageBuilders + with FileBuilders { + + private val statementsById = mutable.Map[Int, Statement]() + override def statements = statementsById.values + def add(stmt: Statement): Unit = statementsById.put(stmt.id, stmt) + + private val ignoredStatementsById = mutable.Map[Int, Statement]() + override def ignoredStatements = ignoredStatementsById.values + def addIgnoredStatement(stmt: Statement): Unit = + ignoredStatementsById.put(stmt.id, stmt) + + def avgClassesPerPackage = classCount / packageCount.toDouble + def avgClassesPerPackageFormatted: String = DoubleFormat.twoFractionDigits( + avgClassesPerPackage + ) + + def avgMethodsPerClass = methodCount / classCount.toDouble + def avgMethodsPerClassFormatted: String = DoubleFormat.twoFractionDigits( + avgMethodsPerClass + ) + + def loc = files.map(_.loc).sum + def linesPerFile = loc / fileCount.toDouble + def linesPerFileFormatted: String = + DoubleFormat.twoFractionDigits(linesPerFile) + + // returns the classes by least coverage + def risks(limit: Int) = classes.toSeq + .sortBy(_.statementCount) + .reverse + .sortBy(_.statementCoverage) + .take(limit) + + def apply(ids: Iterable[(Int, String)]): Unit = ids foreach invoked + def invoked(id: (Int, String)): Unit = + statementsById.get(id._1).foreach(_.invoked(id._2)) +} + +trait ClassBuilders { + def statements: Iterable[Statement] + def classes = statements + .groupBy(_.location.fullClassName) + .map(arg => MeasuredClass(arg._1, arg._2)) + def classCount: Int = classes.size +} + +trait FileBuilders { + def statements: Iterable[Statement] + def files: Iterable[MeasuredFile] = + statements.groupBy(_.source).map(arg => MeasuredFile(arg._1, arg._2)) + def fileCount: Int = files.size +} + +sealed trait ClassType +object ClassType { + case object Object extends ClassType + case object Class extends ClassType + case object Trait extends ClassType + def fromString(str: String): ClassType = { + str.toLowerCase match { + case "object" => Object + case "trait" => Trait + case _ => Class + } + } +} + +case class ClassRef(name: String) { + lazy val simpleName = name.split(".").last + lazy val getPackage = name.split(".").dropRight(1).mkString(".") +} + +object ClassRef { + def fromFilepath(path: String) = ClassRef(path.replace('/', '.')) + def apply(_package: String, className: String): ClassRef = ClassRef( + _package.replace('/', '.') + "." + className + ) +} diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageTest.scala b/domain/src/test/scala/scoverage/domain/CoverageTest.scala similarity index 98% rename from scalac-scoverage-plugin/src/test/scala/scoverage/CoverageTest.scala rename to domain/src/test/scala/scoverage/domain/CoverageTest.scala index b469fd25..35f87b13 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageTest.scala +++ b/domain/src/test/scala/scoverage/domain/CoverageTest.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.domain import munit.FunSuite diff --git a/scalac-scoverage-plugin/src/main/resources/scalac-plugin.xml b/plugin/src/main/resources/scalac-plugin.xml similarity index 100% rename from scalac-scoverage-plugin/src/main/resources/scalac-plugin.xml rename to plugin/src/main/resources/scalac-plugin.xml diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/CoverageFilter.scala b/plugin/src/main/scala/scoverage/CoverageFilter.scala similarity index 100% rename from scalac-scoverage-plugin/src/main/scala/scoverage/CoverageFilter.scala rename to plugin/src/main/scala/scoverage/CoverageFilter.scala diff --git a/plugin/src/main/scala/scoverage/Location.scala b/plugin/src/main/scala/scoverage/Location.scala new file mode 100644 index 00000000..bba2b773 --- /dev/null +++ b/plugin/src/main/scala/scoverage/Location.scala @@ -0,0 +1,57 @@ +package scoverage + +import scala.tools.nsc.Global + +import scoverage.domain.ClassType + +object Location { + + def fromGlobal(global: Global): global.Tree => Option[domain.Location] = { + tree => + def packageName(s: global.Symbol): String = { + s.enclosingPackage.fullName + } + + def className(s: global.Symbol): String = { + // anon functions are enclosed in proper classes. + if (s.enclClass.isAnonymousFunction || s.enclClass.isAnonymousClass) + className(s.owner) + else s.enclClass.nameString + } + + def classType(s: global.Symbol): ClassType = { + if (s.enclClass.isTrait) ClassType.Trait + else if (s.enclClass.isModuleOrModuleClass) ClassType.Object + else ClassType.Class + } + + def fullClassName(s: global.Symbol): String = { + // anon functions are enclosed in proper classes. + if (s.enclClass.isAnonymousFunction || s.enclClass.isAnonymousClass) + fullClassName(s.owner) + else s.enclClass.fullNameString + } + + def enclosingMethod(s: global.Symbol): String = { + // check if we are in a proper method and return that, otherwise traverse up + if (s.enclClass.isAnonymousFunction) enclosingMethod(s.owner) + else if (s.enclMethod.isPrimaryConstructor) "" + else Option(s.enclMethod.nameString).getOrElse("") + } + + def sourcePath(symbol: global.Symbol): String = { + Option(symbol.sourceFile).map(_.canonicalPath).getOrElse("") + } + + Option(tree.symbol) map { symbol => + domain.Location( + packageName(symbol), + className(symbol), + fullClassName(symbol), + classType(symbol), + enclosingMethod(symbol), + sourcePath(symbol) + ) + } + } +} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/ScoverageOptions.scala b/plugin/src/main/scala/scoverage/ScoverageOptions.scala similarity index 100% rename from scalac-scoverage-plugin/src/main/scala/scoverage/ScoverageOptions.scala rename to plugin/src/main/scala/scoverage/ScoverageOptions.scala diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/ScoveragePlugin.scala b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala similarity index 97% rename from scalac-scoverage-plugin/src/main/scala/scoverage/ScoveragePlugin.scala rename to plugin/src/main/scala/scoverage/ScoveragePlugin.scala index 996e2312..e11cbbe3 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/ScoveragePlugin.scala +++ b/plugin/src/main/scala/scoverage/ScoveragePlugin.scala @@ -11,17 +11,21 @@ import scala.tools.nsc.plugins.PluginComponent import scala.tools.nsc.transform.Transform import scala.tools.nsc.transform.TypingTransformers +import scoverage.domain.Coverage +import scoverage.domain.Statement +import scoverage.serialize.Serializer + /** @author Stephen Samuel */ class ScoveragePlugin(val global: Global) extends Plugin { override val name: String = ScoveragePlugin.name override val description: String = ScoveragePlugin.description - // TODO I'm not 100% sure why, but historically these have been parsed out - // first. One thing to play around with in the future would be to not do this - // here and rather do it later when we utilize setOpts and instead just - // initialize then in the instrumentationCompoent. This will save us - // iterating over these options twice. + // I'm not 100% sure why, but historically these have been parsed out first. + // One thing to play around with in the future would be to not do this here + // and rather do it later when we utilize setOpts and instead just initialize + // then in the instrumentationCompoent. This will save us iterating over + // these options twice. private val (extraAfterPhase, extraBeforePhase) = ScoverageOptions.processPhaseOptions( pluginOptions @@ -119,7 +123,7 @@ class ScoverageInstrumentationComponent( // we clean the data directory, because if the code has changed, then the number / order of // statements has changed by definition. So the old data would reference statements incorrectly // and thus skew the results. - IOUtils.clean(options.dataDir) + Serializer.clean(options.dataDir) reporter.echo("Beginning coverage instrumentation") super.run() @@ -148,7 +152,7 @@ class ScoverageInstrumentationComponent( import global._ // contains the location of the last node - var location: Location = _ + var location: domain.Location = _ /** The 'start' of the position, if it is available, else -1 * We cannot use 'isDefined' to test whether pos.start will work, as some @@ -353,7 +357,7 @@ class ScoverageInstrumentationComponent( coverageFilter.isSymbolIncluded(symbol.fullNameString) def updateLocation(t: Tree): Unit = { - Location(global)(t) match { + Location.fromGlobal(global)(t) match { case Some(loc) => this.location = loc case _ => reporter.warning(t.pos, s"[warn] Cannot update location for $t") @@ -479,7 +483,7 @@ class ScoverageInstrumentationComponent( /** Applications of methods with non trivial args means the args themselves * must also be instrumented */ - //todo remove once scala merges into Apply proper + // todo remove once scala merges into Apply proper case a: ApplyToImplicitArgs => instrument( treeCopy.Apply( diff --git a/scalac-scoverage-plugin/src/test/scala-2.11+/scoverage/macrosupport/TesterMacro.scala b/plugin/src/test/scala-2.11+/scoverage/macrosupport/TesterMacro.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala-2.11+/scoverage/macrosupport/TesterMacro.scala rename to plugin/src/test/scala-2.11+/scoverage/macrosupport/TesterMacro.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/LocationCompiler.scala b/plugin/src/test/scala/scoverage/LocationCompiler.scala similarity index 91% rename from scalac-scoverage-plugin/src/test/scala/scoverage/LocationCompiler.scala rename to plugin/src/test/scala/scoverage/LocationCompiler.scala index 45803a4f..7d8766a8 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/LocationCompiler.scala +++ b/plugin/src/test/scala/scoverage/LocationCompiler.scala @@ -7,12 +7,14 @@ import scala.tools.nsc.plugins.PluginComponent import scala.tools.nsc.transform.Transform import scala.tools.nsc.transform.TypingTransformers +import scoverage.reporter.IOUtils + class LocationCompiler( settings: scala.tools.nsc.Settings, reporter: scala.tools.nsc.reporters.Reporter ) extends scala.tools.nsc.Global(settings, reporter) { - val locations = List.newBuilder[(String, Location)] + val locations = List.newBuilder[(String, domain.Location)] private val locationSetter = new LocationSetter(this) def compile(code: String): Unit = { @@ -45,7 +47,7 @@ class LocationCompiler( extends TypingTransformer(unit) { override def transform(tree: global.Tree) = { - for (location <- Location(global)(tree)) { + for (location <- Location.fromGlobal(global)(tree)) { locations += (tree.getClass.getSimpleName -> location) } super.transform(tree) diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/LocationTest.scala b/plugin/src/test/scala/scoverage/LocationTest.scala similarity index 99% rename from scalac-scoverage-plugin/src/test/scala/scoverage/LocationTest.scala rename to plugin/src/test/scala/scoverage/LocationTest.scala index 409f44e7..0fdcbcec 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/LocationTest.scala +++ b/plugin/src/test/scala/scoverage/LocationTest.scala @@ -1,6 +1,7 @@ package scoverage import munit.FunSuite +import scoverage.domain.ClassType class LocationTest extends FunSuite { diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/MacroSupport.scala b/plugin/src/test/scala/scoverage/MacroSupport.scala similarity index 78% rename from scalac-scoverage-plugin/src/test/scala/scoverage/MacroSupport.scala rename to plugin/src/test/scala/scoverage/MacroSupport.scala index f79d7707..97f32a9c 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/MacroSupport.scala +++ b/plugin/src/test/scala/scoverage/MacroSupport.scala @@ -14,7 +14,7 @@ trait MacroSupport { val macroSupportDeps = Seq(testClasses) private def testClasses: File = new File( - s"./scalac-scoverage-plugin/target/scala-${ScoverageCompiler.ScalaVersion}/test-classes" + s"./plugin/target/scala-${ScoverageCompiler.ScalaVersion}/test-classes" ) } diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/PluginASTSupportTest.scala b/plugin/src/test/scala/scoverage/PluginASTSupportTest.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala/scoverage/PluginASTSupportTest.scala rename to plugin/src/test/scala/scoverage/PluginASTSupportTest.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/PluginCoverageScalaJsTest.scala b/plugin/src/test/scala/scoverage/PluginCoverageScalaJsTest.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala/scoverage/PluginCoverageScalaJsTest.scala rename to plugin/src/test/scala/scoverage/PluginCoverageScalaJsTest.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/PluginCoverageTest.scala b/plugin/src/test/scala/scoverage/PluginCoverageTest.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala/scoverage/PluginCoverageTest.scala rename to plugin/src/test/scala/scoverage/PluginCoverageTest.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/RegexCoverageFilterTest.scala b/plugin/src/test/scala/scoverage/RegexCoverageFilterTest.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala/scoverage/RegexCoverageFilterTest.scala rename to plugin/src/test/scala/scoverage/RegexCoverageFilterTest.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageCompiler.scala b/plugin/src/test/scala/scoverage/ScoverageCompiler.scala similarity index 96% rename from scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageCompiler.scala rename to plugin/src/test/scala/scoverage/ScoverageCompiler.scala index b83fcadb..e52e60c5 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageCompiler.scala +++ b/plugin/src/test/scala/scoverage/ScoverageCompiler.scala @@ -11,6 +11,8 @@ import scala.tools.nsc.plugins.PluginComponent import scala.tools.nsc.transform.Transform import scala.tools.nsc.transform.TypingTransformers +import scoverage.reporter.IOUtils + object ScoverageCompiler { val ScalaVersion: String = scala.util.Properties.versionNumberString @@ -33,7 +35,7 @@ object ScoverageCompiler { s.classpath.value = classPath.mkString(File.pathSeparator) val path = - s"./scalac-scoverage-plugin/target/scala-$ScalaVersion/test-generated-classes" + s"./plugin/target/scala-$ScalaVersion/test-generated-classes" new File(path).mkdirs() s.outdir.value = path s @@ -56,7 +58,7 @@ object ScoverageCompiler { private def sbtCompileDir: File = { val dir = new File( - s"./scalac-scoverage-plugin/target/scala-$ScalaVersion/classes" + s"./plugin/target/scala-$ScalaVersion/classes" ) if (!dir.exists) throw new FileNotFoundException( @@ -66,7 +68,7 @@ object ScoverageCompiler { } private def runtimeClasses: File = new File( - s"./scalac-scoverage-runtime/jvm/target/scala-$ScalaVersion/classes" + s"./runtime/jvm/target/scala-$ScalaVersion/classes" ) private def findScalaJar(artifactId: String): File = diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/macrosupport/Tester.scala b/plugin/src/test/scala/scoverage/macrosupport/Tester.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/scala/scoverage/macrosupport/Tester.scala rename to plugin/src/test/scala/scoverage/macrosupport/Tester.scala diff --git a/scalac-scoverage-plugin/src/main/resources/scoverage/index.html b/reporter/src/main/resources/scoverage/index.html similarity index 100% rename from scalac-scoverage-plugin/src/main/resources/scoverage/index.html rename to reporter/src/main/resources/scoverage/index.html diff --git a/scalac-scoverage-plugin/src/main/resources/scoverage/pure-min.css b/reporter/src/main/resources/scoverage/pure-min.css similarity index 100% rename from scalac-scoverage-plugin/src/main/resources/scoverage/pure-min.css rename to reporter/src/main/resources/scoverage/pure-min.css diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/BaseReportWriter.scala b/reporter/src/main/scala/scoverage/reporter/BaseReportWriter.scala similarity index 98% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/BaseReportWriter.scala rename to reporter/src/main/scala/scoverage/reporter/BaseReportWriter.scala index ebccb95f..6c89b2e4 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/BaseReportWriter.scala +++ b/reporter/src/main/scala/scoverage/reporter/BaseReportWriter.scala @@ -1,4 +1,4 @@ -package scoverage.report +package scoverage.reporter import java.io.File diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala b/reporter/src/main/scala/scoverage/reporter/CoberturaXmlWriter.scala similarity index 73% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala rename to reporter/src/main/scala/scoverage/reporter/CoberturaXmlWriter.scala index f6f7d2d2..327e4ddc 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala +++ b/reporter/src/main/scala/scoverage/reporter/CoberturaXmlWriter.scala @@ -1,12 +1,15 @@ -package scoverage.report +package scoverage.reporter import java.io.File import scala.xml.Node import scala.xml.PrettyPrinter -import scoverage.DoubleFormat.twoFractionDigits -import scoverage._ +import scoverage.domain.Coverage +import scoverage.domain.DoubleFormat +import scoverage.domain.MeasuredClass +import scoverage.domain.MeasuredMethod +import scoverage.domain.MeasuredPackage /** @author Stephen Samuel */ class CoberturaXmlWriter( @@ -31,8 +34,8 @@ class CoberturaXmlWriter( def method(method: MeasuredMethod): Node = { { @@ -48,8 +51,8 @@ class CoberturaXmlWriter( def klass(klass: MeasuredClass): Node = { {klass.methods.map(method)} @@ -67,8 +70,8 @@ class CoberturaXmlWriter( def pack(pack: MeasuredPackage): Node = { {pack.classes.map(klass)} @@ -81,12 +84,16 @@ class CoberturaXmlWriter( } def xml(coverage: Coverage): Node = { - diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoverageAggregator.scala b/reporter/src/main/scala/scoverage/reporter/CoverageAggregator.scala similarity index 92% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/CoverageAggregator.scala rename to reporter/src/main/scala/scoverage/reporter/CoverageAggregator.scala index 27f9c564..8f8d6608 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoverageAggregator.scala +++ b/reporter/src/main/scala/scoverage/reporter/CoverageAggregator.scala @@ -1,10 +1,9 @@ -package scoverage.report +package scoverage.reporter import java.io.File -import scoverage.Coverage -import scoverage.IOUtils -import scoverage.Serializer +import scoverage.domain.Coverage +import scoverage.serialize.Serializer object CoverageAggregator { diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/IOUtils.scala b/reporter/src/main/scala/scoverage/reporter/IOUtils.scala similarity index 98% rename from scalac-scoverage-plugin/src/main/scala/scoverage/IOUtils.scala rename to reporter/src/main/scala/scoverage/reporter/IOUtils.scala index 5ecfee29..aae159ab 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/IOUtils.scala +++ b/reporter/src/main/scala/scoverage/reporter/IOUtils.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.reporter import java.io._ @@ -7,6 +7,8 @@ import scala.collection.mutable import scala.io.Codec import scala.io.Source +import scoverage.domain.Constants + /** @author Stephen Samuel */ object IOUtils { diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageHtmlWriter.scala b/reporter/src/main/scala/scoverage/reporter/ScoverageHtmlWriter.scala similarity index 98% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageHtmlWriter.scala rename to reporter/src/main/scala/scoverage/reporter/ScoverageHtmlWriter.scala index b1960433..849e7663 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageHtmlWriter.scala +++ b/reporter/src/main/scala/scoverage/reporter/ScoverageHtmlWriter.scala @@ -1,11 +1,15 @@ -package scoverage.report +package scoverage.reporter import java.io.File import java.util.Date import scala.xml.Node -import scoverage._ +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( diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala b/reporter/src/main/scala/scoverage/reporter/ScoverageXmlWriter.scala similarity index 95% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala rename to reporter/src/main/scala/scoverage/reporter/ScoverageXmlWriter.scala index 9f4866c8..49b4eebf 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala +++ b/reporter/src/main/scala/scoverage/reporter/ScoverageXmlWriter.scala @@ -1,11 +1,15 @@ -package scoverage.report +package scoverage.reporter import java.io.File import scala.xml.Node import scala.xml.PrettyPrinter -import scoverage._ +import scoverage.domain.Coverage +import scoverage.domain.MeasuredClass +import scoverage.domain.MeasuredMethod +import scoverage.domain.MeasuredPackage +import scoverage.domain.Statement /** @author Stephen Samuel */ class ScoverageXmlWriter( diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/StatementWriter.scala b/reporter/src/main/scala/scoverage/reporter/StatementWriter.scala similarity index 94% rename from scalac-scoverage-plugin/src/main/scala/scoverage/report/StatementWriter.scala rename to reporter/src/main/scala/scoverage/reporter/StatementWriter.scala index 4c0f0ddd..13a4d775 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/StatementWriter.scala +++ b/reporter/src/main/scala/scoverage/reporter/StatementWriter.scala @@ -1,8 +1,8 @@ -package scoverage.report +package scoverage.reporter import scala.xml.Node -import _root_.scoverage.MeasuredFile +import scoverage.domain.MeasuredFile /** @author Stephen Samuel */ class StatementWriter(mFile: MeasuredFile) { diff --git a/scalac-scoverage-plugin/src/test/resources/scoverage/cobertura.sample.xml b/reporter/src/test/resources/scoverage/reporter/cobertura.sample.xml similarity index 100% rename from scalac-scoverage-plugin/src/test/resources/scoverage/cobertura.sample.xml rename to reporter/src/test/resources/scoverage/reporter/cobertura.sample.xml diff --git a/scalac-scoverage-plugin/src/test/resources/scoverage/coverage-04.dtd b/reporter/src/test/resources/scoverage/reporter/coverage-04.dtd similarity index 100% rename from scalac-scoverage-plugin/src/test/resources/scoverage/coverage-04.dtd rename to reporter/src/test/resources/scoverage/reporter/coverage-04.dtd diff --git a/scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/ClassContainingHtml.scala b/reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/ClassContainingHtml.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/ClassContainingHtml.scala rename to reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/ClassContainingHtml.scala diff --git a/scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/ClassInMainDir.scala b/reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/ClassInMainDir.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/ClassInMainDir.scala rename to reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/ClassInMainDir.scala diff --git a/scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/subdir/ClassInSubDir.scala b/reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/subdir/ClassInSubDir.scala similarity index 100% rename from scalac-scoverage-plugin/src/test/resources/scoverage/forHtmlWriter/src/main/scala/subdir/ClassInSubDir.scala rename to reporter/src/test/resources/scoverage/reporter/forHtmlWriter/src/main/scala/subdir/ClassInSubDir.scala diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/CoberturaXmlWriterTest.scala b/reporter/src/test/scala/scoverage/reporter/CoberturaXmlWriterTest.scala similarity index 97% rename from scalac-scoverage-plugin/src/test/scala/scoverage/CoberturaXmlWriterTest.scala rename to reporter/src/test/scala/scoverage/reporter/CoberturaXmlWriterTest.scala index 0f6cc460..b1c091f3 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/CoberturaXmlWriterTest.scala +++ b/reporter/src/test/scala/scoverage/reporter/CoberturaXmlWriterTest.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.reporter import java.io.File import java.util.UUID @@ -12,7 +12,10 @@ import scala.xml.factory.XMLLoader import munit.FunSuite import org.xml.sax.ErrorHandler import org.xml.sax.SAXParseException -import scoverage.report.CoberturaXmlWriter +import scoverage.domain.ClassType +import scoverage.domain.Coverage +import scoverage.domain.Location +import scoverage.domain.Statement /** @author Stephen Samuel */ class CoberturaXmlWriterTest extends FunSuite { @@ -35,7 +38,7 @@ class CoberturaXmlWriterTest extends FunSuite { val dir = tempDir() - val coverage = scoverage.Coverage() + val coverage = Coverage() coverage .add( Statement( diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageAggregatorTest.scala b/reporter/src/test/scala/scoverage/reporter/CoverageAggregatorTest.scala similarity index 91% rename from scalac-scoverage-plugin/src/test/scala/scoverage/CoverageAggregatorTest.scala rename to reporter/src/test/scala/scoverage/reporter/CoverageAggregatorTest.scala index c6db910e..3744c0aa 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageAggregatorTest.scala +++ b/reporter/src/test/scala/scoverage/reporter/CoverageAggregatorTest.scala @@ -1,11 +1,16 @@ -package scoverage +package scoverage.reporter import java.io.File import java.io.FileWriter import java.util.UUID import munit.FunSuite -import scoverage.report.CoverageAggregator +import scoverage.domain.ClassType +import scoverage.domain.Constants +import scoverage.domain.Coverage +import scoverage.domain.Location +import scoverage.domain.Statement +import scoverage.serialize.Serializer class CoverageAggregatorTest extends FunSuite { diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageMetricsTest.scala b/reporter/src/test/scala/scoverage/reporter/CoverageMetricsTest.scala similarity index 90% rename from scalac-scoverage-plugin/src/test/scala/scoverage/CoverageMetricsTest.scala rename to reporter/src/test/scala/scoverage/reporter/CoverageMetricsTest.scala index 801d9d53..bd8d58e5 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/CoverageMetricsTest.scala +++ b/reporter/src/test/scala/scoverage/reporter/CoverageMetricsTest.scala @@ -1,6 +1,8 @@ -package scoverage +package scoverage.reporter import munit.FunSuite +import scoverage.domain.CoverageMetrics +import scoverage.domain.Statement class CoverageMetricsTest extends FunSuite { diff --git a/reporter/src/test/scala/scoverage/reporter/CoverageTest.scala b/reporter/src/test/scala/scoverage/reporter/CoverageTest.scala new file mode 100644 index 00000000..35f87b13 --- /dev/null +++ b/reporter/src/test/scala/scoverage/reporter/CoverageTest.scala @@ -0,0 +1,92 @@ +package scoverage.domain + +import munit.FunSuite + +/** @author Stephen Samuel */ +class CoverageTest extends FunSuite { + + test("coverage for no statements is 1") { + val coverage = Coverage() + assertEquals(1.0, coverage.statementCoverage) + } + + test("coverage for no invoked statements is 0") { + val coverage = Coverage() + coverage.add( + Statement( + Location("", "", "", ClassType.Object, "", ""), + 1, + 2, + 3, + 4, + "", + "", + "", + false, + 0 + ) + ) + assertEquals(0.0, coverage.statementCoverage) + } + + test("coverage for invoked statements") { + val coverage = Coverage() + coverage.add( + Statement( + Location("", "", "", ClassType.Object, "", ""), + 1, + 2, + 3, + 4, + "", + "", + "", + false, + 3 + ) + ) + coverage.add( + Statement( + Location("", "", "", ClassType.Object, "", ""), + 2, + 2, + 3, + 4, + "", + "", + "", + false, + 0 + ) + ) + coverage.add( + Statement( + Location("", "", "", ClassType.Object, "", ""), + 3, + 2, + 3, + 4, + "", + "", + "", + false, + 0 + ) + ) + coverage.add( + Statement( + Location("", "", "", ClassType.Object, "", ""), + 4, + 2, + 3, + 4, + "", + "", + "", + false, + 0 + ) + ) + assertEquals(0.25, coverage.statementCoverage) + } +} diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/IOUtilsTest.scala b/reporter/src/test/scala/scoverage/reporter/IOUtilsTest.scala similarity index 95% rename from scalac-scoverage-plugin/src/test/scala/scoverage/IOUtilsTest.scala rename to reporter/src/test/scala/scoverage/reporter/IOUtilsTest.scala index 538d6d63..8f3bcfe2 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/IOUtilsTest.scala +++ b/reporter/src/test/scala/scoverage/reporter/IOUtilsTest.scala @@ -1,10 +1,12 @@ -package scoverage +package scoverage.reporter import java.io.File import java.io.FileWriter import java.util.UUID import munit.FunSuite +import scoverage.domain.Constants +import scoverage.reporter.IOUtils /** @author Stephen Samuel */ class IOUtilsTest extends FunSuite { diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala b/reporter/src/test/scala/scoverage/reporter/ScoverageHtmlWriterTest.scala similarity index 95% rename from scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala rename to reporter/src/test/scala/scoverage/reporter/ScoverageHtmlWriterTest.scala index 33240611..86e8dd84 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala +++ b/reporter/src/test/scala/scoverage/reporter/ScoverageHtmlWriterTest.scala @@ -1,4 +1,4 @@ -package scoverage +package scoverage.reporter import java.io._ import java.util.UUID @@ -7,7 +7,10 @@ import scala.io.Source import scala.xml.XML import munit.FunSuite -import scoverage.report.ScoverageHtmlWriter +import scoverage.domain.ClassType +import scoverage.domain.Coverage +import scoverage.domain.Location +import scoverage.domain.Statement class ScoverageHtmlWriterTest extends FunSuite { diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/File.scala b/runtime/js/src/main/scala/scalajssupport/File.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/File.scala rename to runtime/js/src/main/scala/scalajssupport/File.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/FileWriter.scala b/runtime/js/src/main/scala/scalajssupport/FileWriter.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/FileWriter.scala rename to runtime/js/src/main/scala/scalajssupport/FileWriter.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/JsFile.scala b/runtime/js/src/main/scala/scalajssupport/JsFile.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/JsFile.scala rename to runtime/js/src/main/scala/scalajssupport/JsFile.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/NodeFile.scala b/runtime/js/src/main/scala/scalajssupport/NodeFile.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/NodeFile.scala rename to runtime/js/src/main/scala/scalajssupport/NodeFile.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/PhantomFile.scala b/runtime/js/src/main/scala/scalajssupport/PhantomFile.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/PhantomFile.scala rename to runtime/js/src/main/scala/scalajssupport/PhantomFile.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/RhinoFile.scala b/runtime/js/src/main/scala/scalajssupport/RhinoFile.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/RhinoFile.scala rename to runtime/js/src/main/scala/scalajssupport/RhinoFile.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scalajssupport/Source.scala b/runtime/js/src/main/scala/scalajssupport/Source.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scalajssupport/Source.scala rename to runtime/js/src/main/scala/scalajssupport/Source.scala diff --git a/scalac-scoverage-runtime/js/src/main/scala/scoverage/Platform.scala b/runtime/js/src/main/scala/scoverage/Platform.scala similarity index 100% rename from scalac-scoverage-runtime/js/src/main/scala/scoverage/Platform.scala rename to runtime/js/src/main/scala/scoverage/Platform.scala diff --git a/scalac-scoverage-runtime/jvm/src/main/scala/scoverage/Platform.scala b/runtime/jvm/src/main/scala/scoverage/Platform.scala similarity index 100% rename from scalac-scoverage-runtime/jvm/src/main/scala/scoverage/Platform.scala rename to runtime/jvm/src/main/scala/scoverage/Platform.scala diff --git a/scalac-scoverage-runtime/jvm/src/test/scala/scoverage/InvokerConcurrencyTest.scala b/runtime/jvm/src/test/scala/scoverage/InvokerConcurrencyTest.scala similarity index 100% rename from scalac-scoverage-runtime/jvm/src/test/scala/scoverage/InvokerConcurrencyTest.scala rename to runtime/jvm/src/test/scala/scoverage/InvokerConcurrencyTest.scala diff --git a/scalac-scoverage-runtime/shared/src/main/scala/scoverage/Invoker.scala b/runtime/shared/src/main/scala/scoverage/Invoker.scala similarity index 100% rename from scalac-scoverage-runtime/shared/src/main/scala/scoverage/Invoker.scala rename to runtime/shared/src/main/scala/scoverage/Invoker.scala diff --git a/scalac-scoverage-runtime/shared/src/test/scala/scoverage/InvokerMultiModuleTest.scala b/runtime/shared/src/test/scala/scoverage/InvokerMultiModuleTest.scala similarity index 100% rename from scalac-scoverage-runtime/shared/src/test/scala/scoverage/InvokerMultiModuleTest.scala rename to runtime/shared/src/test/scala/scoverage/InvokerMultiModuleTest.scala diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala deleted file mode 100644 index 94a6fcef..00000000 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala +++ /dev/null @@ -1,67 +0,0 @@ -package scoverage - -import scala.tools.nsc.Global - -/** @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 -) extends java.io.Serializable - -object Location { - - def apply(global: Global): global.Tree => Option[Location] = { tree => - def packageName(s: global.Symbol): String = { - s.enclosingPackage.fullName - } - - def className(s: global.Symbol): String = { - // anon functions are enclosed in proper classes. - if (s.enclClass.isAnonymousFunction || s.enclClass.isAnonymousClass) - className(s.owner) - else s.enclClass.nameString - } - - def classType(s: global.Symbol): ClassType = { - if (s.enclClass.isTrait) ClassType.Trait - else if (s.enclClass.isModuleOrModuleClass) ClassType.Object - else ClassType.Class - } - - def fullClassName(s: global.Symbol): String = { - // anon functions are enclosed in proper classes. - if (s.enclClass.isAnonymousFunction || s.enclClass.isAnonymousClass) - fullClassName(s.owner) - else s.enclClass.fullNameString - } - - def enclosingMethod(s: global.Symbol): String = { - // check if we are in a proper method and return that, otherwise traverse up - if (s.enclClass.isAnonymousFunction) enclosingMethod(s.owner) - else if (s.enclMethod.isPrimaryConstructor) "" - else Option(s.enclMethod.nameString).getOrElse("") - } - - def sourcePath(symbol: global.Symbol): String = { - Option(symbol.sourceFile).map(_.canonicalPath).getOrElse("") - } - - Option(tree.symbol) map { symbol => - Location( - packageName(symbol), - className(symbol), - fullClassName(symbol), - classType(symbol), - enclosingMethod(symbol), - sourcePath(symbol) - ) - } - } -} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala deleted file mode 100644 index aaad4cb2..00000000 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala +++ /dev/null @@ -1,230 +0,0 @@ -package scoverage - -import java.io.File - -import scala.collection.mutable - -import scoverage.DoubleFormat.twoFractionDigits - -/** @author Stephen Samuel - */ -case class Coverage() - extends CoverageMetrics - with MethodBuilders - with java.io.Serializable - with ClassBuilders - with PackageBuilders - with FileBuilders { - - private val statementsById = mutable.Map[Int, Statement]() - override def statements = statementsById.values - def add(stmt: Statement): Unit = statementsById.put(stmt.id, stmt) - - private val ignoredStatementsById = mutable.Map[Int, Statement]() - override def ignoredStatements = ignoredStatementsById.values - def addIgnoredStatement(stmt: Statement): Unit = - ignoredStatementsById.put(stmt.id, stmt) - - def avgClassesPerPackage = classCount / packageCount.toDouble - def avgClassesPerPackageFormatted: String = twoFractionDigits( - avgClassesPerPackage - ) - - def avgMethodsPerClass = methodCount / classCount.toDouble - def avgMethodsPerClassFormatted: String = twoFractionDigits( - avgMethodsPerClass - ) - - def loc = files.map(_.loc).sum - def linesPerFile = loc / fileCount.toDouble - def linesPerFileFormatted: String = twoFractionDigits(linesPerFile) - - // returns the classes by least coverage - def risks(limit: Int) = classes.toSeq - .sortBy(_.statementCount) - .reverse - .sortBy(_.statementCoverage) - .take(limit) - - def apply(ids: Iterable[(Int, String)]): Unit = ids foreach invoked - def invoked(id: (Int, String)): Unit = - statementsById.get(id._1).foreach(_.invoked(id._2)) -} - -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) - } -} - -trait ClassBuilders { - def statements: Iterable[Statement] - def classes = statements - .groupBy(_.location.fullClassName) - .map(arg => MeasuredClass(arg._1, arg._2)) - def classCount: Int = classes.size -} - -trait FileBuilders { - def statements: Iterable[Statement] - def files: Iterable[MeasuredFile] = - statements.groupBy(_.source).map(arg => MeasuredFile(arg._1, arg._2)) - def fileCount: Int = files.size -} - -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() -} - -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 -} - -sealed trait ClassType -object ClassType { - case object Object extends ClassType - case object Class extends ClassType - case object Trait extends ClassType - def fromString(str: String): ClassType = { - str.toLowerCase match { - case "object" => Object - case "trait" => Trait - case _ => Class - } - } -} - -case class ClassRef(name: String) { - lazy val simpleName = name.split(".").last - lazy val getPackage = name.split(".").dropRight(1).mkString(".") -} - -object ClassRef { - def fromFilepath(path: String) = ClassRef(path.replace('/', '.')) - def apply(_package: String, className: String): ClassRef = ClassRef( - _package.replace('/', '.') + "." + className - ) -} - -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 = 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 = twoFractionDigits(branchCoveragePercent) -} - -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 -} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala b/serializer/src/main/scala/scoverage/serialize/Serializer.scala similarity index 88% rename from scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala rename to serializer/src/main/scala/scoverage/serialize/Serializer.scala index b292f694..de862ae3 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala +++ b/serializer/src/main/scala/scoverage/serialize/Serializer.scala @@ -1,7 +1,8 @@ -package scoverage +package scoverage.serialize import java.io.BufferedWriter import java.io.File +import java.io.FileFilter import java.io.FileOutputStream import java.io.OutputStreamWriter import java.io.Writer @@ -9,9 +10,17 @@ import java.io.Writer import scala.io.Codec import scala.io.Source +import scoverage.domain.ClassType +import scoverage.domain.Constants +import scoverage.domain.Coverage +import scoverage.domain.Location +import scoverage.domain.Statement + object Serializer { - val coverageDataFormatVersion = "3.0" + def coverageFile(dataDir: File): File = coverageFile(dataDir.getAbsolutePath) + def coverageFile(dataDir: String): File = + new File(dataDir, Constants.CoverageFileName) // Write out coverage data to the given data directory, using the default coverage filename def serialize(coverage: Coverage, dataDir: String, sourceRoot: String): Unit = @@ -52,7 +61,7 @@ object Serializer { def writeHeader(writer: Writer): Unit = { writer.write( - s"""# Coverage data, format version: $coverageDataFormatVersion + s"""# Coverage data, format version: ${Constants.CoverageDataFormatVersion} |# Statement data: |# - id |# - source path @@ -103,10 +112,6 @@ object Serializer { .foreach(stmt => writeStatement(stmt, writer)) } - def coverageFile(dataDir: File): File = coverageFile(dataDir.getAbsolutePath) - def coverageFile(dataDir: String): File = - new File(dataDir, Constants.CoverageFileName) - def deserialize(file: File, sourceRoot: File): Coverage = { val source = Source.fromFile(file)(Codec.UTF8) try deserialize(source.getLines(), sourceRoot) @@ -163,7 +168,7 @@ object Serializer { val headerFirstLine = lines.next() require( - headerFirstLine == s"# Coverage data, format version: $coverageDataFormatVersion", + headerFirstLine == s"# Coverage data, format version: ${Constants.CoverageDataFormatVersion}", "Wrong file format" ) @@ -180,4 +185,14 @@ object Serializer { coverage } + def clean(dataDir: File): Unit = + findMeasurementFiles(dataDir).foreach(_.delete) + def clean(dataDir: String): Unit = clean(new File(dataDir)) + + def findMeasurementFiles(dataDir: File): Array[File] = + dataDir.listFiles(new FileFilter { + override def accept(pathname: File): Boolean = + pathname.getName.startsWith(Constants.MeasurementsPrefix) + }) + } diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala b/serializer/src/test/scala/scoverage/serialize/SerializerTest.scala similarity index 90% rename from scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala rename to serializer/src/test/scala/scoverage/serialize/SerializerTest.scala index dea4f21e..a6e10578 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala +++ b/serializer/src/test/scala/scoverage/serialize/SerializerTest.scala @@ -1,9 +1,14 @@ -package scoverage +package scoverage.serialize import java.io.File import java.io.StringWriter import munit.FunSuite +import scoverage.domain.ClassType +import scoverage.domain.Constants +import scoverage.domain.Coverage +import scoverage.domain.Location +import scoverage.domain.Statement class SerializerTest extends FunSuite { private val sourceRoot = new File(".").getCanonicalFile() @@ -32,7 +37,7 @@ class SerializerTest extends FunSuite { ) ) val expected = - s"""# Coverage data, format version: ${Serializer.coverageDataFormatVersion} + s"""# Coverage data, format version: ${Constants.CoverageDataFormatVersion} |# Statement data: |# - id |# - source path @@ -70,14 +75,14 @@ class SerializerTest extends FunSuite { |def test : String |\f |""".stripMargin - val writer = new StringWriter() //TODO-use UTF-8 + val writer = new StringWriter() val actual = Serializer.serialize(coverage, writer, sourceRoot) assertEquals(expected, writer.toString) } - test("coverage should be deserializable from plain text") { + test("coverage should be eserializable from plain text") { val input = - s"""# Coverage data, format version: ${Serializer.coverageDataFormatVersion} + s"""# Coverage data, format version: ${Constants.CoverageDataFormatVersion} |# Statement data: |# - id |# - source path @@ -165,7 +170,7 @@ class SerializerTest extends FunSuite { ) ) val expected = - s"""# Coverage data, format version: ${Serializer.coverageDataFormatVersion} + s"""# Coverage data, format version: ${Constants.CoverageDataFormatVersion} |# Statement data: |# - id |# - source path @@ -203,14 +208,14 @@ class SerializerTest extends FunSuite { |def test : String |\f |""".stripMargin - val writer = new StringWriter() //TODO-use UTF-8 + val writer = new StringWriter() val actual = Serializer.serialize(coverage, writer, sourceRoot) assertEquals(expected, writer.toString) } test("coverage should deserialize sourcePath by prefixing cwd") { val input = - s"""# Coverage data, format version: ${Serializer.coverageDataFormatVersion} + s"""# Coverage data, format version: ${Constants.CoverageDataFormatVersion} |# Statement data: |# - id |# - source path