diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala index 343b6fbe..7845811a 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/Location.scala @@ -2,14 +2,17 @@ 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, - topLevelClass: String, + fullClassName: String, classType: ClassType, method: String, - sourcePath: String) extends java.io.Serializable { - val fqn = (packageName + ".").replace(".", "") + className -} + sourcePath: String) extends java.io.Serializable object Location { @@ -31,8 +34,10 @@ object Location { else ClassType.Class } - def topLevelClass(s: global.Symbol): String = { - s.enclosingTopLevelClass.nameString + 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 = { @@ -51,7 +56,7 @@ object Location { Location( packageName(symbol), className(symbol), - topLevelClass(symbol), + fullClassName(symbol), classType(symbol), enclosingMethod(symbol), sourcePath(symbol)) diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala index 846b7c59..4d0d4067 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala @@ -33,9 +33,9 @@ object Serializer { {stmt.location.classType.toString} - - {stmt.location.topLevelClass} - + + {stmt.location.fullClassName} + {stmt.location.method} @@ -93,7 +93,7 @@ object Serializer { val branch = (node \ "branch").text.toBoolean val _package = (node \ "package").text val _class = (node \ "class").text - val topLevelClass = (node \ "topLevelClass").text + val fullClassName = (node \ "fullClassName").text val method = (node \ "method").text val path = (node \ "path").text val treeName = (node \ "treeName").text @@ -109,7 +109,7 @@ object Serializer { case _ => ClassType.Class } Statement(source, - Location(_package, _class, topLevelClass, classType, method, path), + Location(_package, _class, fullClassName, classType, method, path), id, start, end, diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala index 242d99d8..ddfbd1e8 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/coverage.scala @@ -60,7 +60,7 @@ trait PackageBuilders { trait ClassBuilders { def statements: Iterable[Statement] - def classes = statements.groupBy(_.location.className).map(arg => MeasuredClass(arg._1, arg._2)) + def classes = statements.groupBy(_.location.fullClassName).map(arg => MeasuredClass(arg._1, arg._2)) def classCount: Int = classes.size } @@ -74,12 +74,23 @@ case class MeasuredMethod(name: String, statements: Iterable[Statement]) extends override def ignoredStatements: Iterable[Statement] = Seq() } -case class MeasuredClass(name: String, statements: Iterable[Statement]) +case class MeasuredClass(fullClassName: String, statements: Iterable[Statement]) extends CoverageMetrics with MethodBuilders { + def source: String = statements.head.source - def simpleName = name.split('.').last 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() } diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala index 8eab8826..4142da26 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/report/CoberturaXmlWriter.scala @@ -39,7 +39,7 @@ class CoberturaXmlWriter(sourceDirectories: Seq[File], outputDir: File) extends } def klass(klass: MeasuredClass): Node = { - - {classes.toSeq.sortBy(_.simpleName) map classRow} + {classes.toSeq.sortBy(_.fullClassName) map classRow} } @@ -217,11 +217,10 @@ class ScoverageHtmlWriter(sourceDirectories: Seq[File], outputDir: File) extends val statement0f = Math.round(klass.statementCoveragePercent).toInt.toString val branch0f = Math.round(klass.branchCoveragePercent).toInt.toString - val simpleClassName = klass.name.split('.').last - {simpleClassName} + {klass.displayClassName} @@ -331,7 +330,7 @@ class ScoverageHtmlWriter(sourceDirectories: Seq[File], outputDir: File) extends {coverage.risks(limit).map(klass => - {klass.simpleName} + {klass.displayClassName} {klass.loc.toString} diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlReader.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlReader.scala index cd238023..75dedaeb 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlReader.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlReader.scala @@ -22,7 +22,7 @@ object ScoverageXmlReader { val pkg = node \ "@package" val classname = node \ "@class" val classType = node \ "@class-type" - val topLevelClass = node \ "@top-level-class" + val fullClassName = node \ "@full-class-name" val method = node \ "@method" val start = node \ "@start" val end = node \ "@end" @@ -35,7 +35,7 @@ object ScoverageXmlReader { val location = Location(pkg.text, classname.text, - topLevelClass.text, + fullClassName.text, ClassType fromString classType.text, method.text, source.text) diff --git a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala index 3b995331..7d3b8f90 100644 --- a/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala +++ b/scalac-scoverage-plugin/src/main/scala/scoverage/report/ScoverageXmlWriter.scala @@ -10,7 +10,7 @@ import scala.xml.{Node, PrettyPrinter} class ScoverageXmlWriter(sourceDirectories: Seq[File], outputDir: File, debug: Boolean) extends BaseReportWriter(sourceDirectories, outputDir) { def this (sourceDir: File, outputDir: File, debug: Boolean) { - this(Seq(sourceDir), outputDir, debug); + this(Seq(sourceDir), outputDir, debug) } def write(coverage: Coverage): Unit = { @@ -37,7 +37,7 @@ class ScoverageXmlWriter(sourceDirectories: Seq[File], outputDir: File, debug: B " loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -23,7 +23,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Template").get._2 loc.packageName shouldBe "com.test" loc.className shouldBe "Bammy" - loc.topLevelClass shouldBe "Bammy" + loc.fullClassName shouldBe "com.test.Bammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Object loc.sourcePath should endWith(".scala") @@ -34,7 +34,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Template").get._2 loc.packageName shouldBe "com.test" loc.className shouldBe "Gammy" - loc.topLevelClass shouldBe "Gammy" + loc.fullClassName shouldBe "com.test.Gammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Trait loc.sourcePath should endWith(".scala") @@ -46,6 +46,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._2.method == "foo").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Hammy" + loc.fullClassName shouldBe "com.methodtest.Hammy" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") } @@ -55,10 +56,9 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._2.method == "goo").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Hammy" - loc.topLevelClass shouldBe "Hammy" + loc.fullClassName shouldBe "com.methodtest.Hammy" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") - } "should process anon functions as inside the enclosing method" in { val compiler = ScoverageCompiler.locationCompiler @@ -66,6 +66,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Function").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Jammy" + loc.fullClassName shouldBe "com.methodtest.Jammy" loc.method shouldBe "moo" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -77,7 +78,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._2.className == "Pammy").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Pammy" - loc.topLevelClass shouldBe "Jammy" + loc.fullClassName shouldBe "com.methodtest.Jammy.Pammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -88,7 +89,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._2.className == "Zammy").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Zammy" - loc.topLevelClass shouldBe "Jammy" + loc.fullClassName shouldBe "com.methodtest.Jammy.Zammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Object loc.sourcePath should endWith(".scala") @@ -99,7 +100,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._2.className == "Mammy").get._2 loc.packageName shouldBe "com.methodtest" loc.className shouldBe "Mammy" - loc.topLevelClass shouldBe "Jammy" + loc.fullClassName shouldBe "com.methodtest.Jammy.Mammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Trait loc.sourcePath should endWith(".scala") @@ -114,7 +115,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Template").get._2 loc.packageName shouldBe "com.a.b" loc.className shouldBe "Kammy" - loc.topLevelClass shouldBe "Kammy" + loc.fullClassName shouldBe "com.a.b.Kammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -127,6 +128,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Template").get._2 loc.packageName shouldBe "com.a.b" loc.className shouldBe "Kammy" + loc.fullClassName shouldBe "com.a.b.Kammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Object loc.sourcePath should endWith(".scala") @@ -139,7 +141,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "Template").get._2 loc.packageName shouldBe "com.a.b" loc.className shouldBe "Kammy" - loc.topLevelClass shouldBe "Kammy" + loc.fullClassName shouldBe "com.a.b.Kammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Trait loc.sourcePath should endWith(".scala") @@ -152,6 +154,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "ValDef").get._2 loc.packageName shouldBe "com.b" loc.className shouldBe "Tammy" + loc.fullClassName shouldBe "com.b.Tammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -162,7 +165,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "ValDef").get._2 loc.packageName shouldBe "com.b" loc.className shouldBe "Yammy" - loc.topLevelClass shouldBe "Yammy" + loc.fullClassName shouldBe "com.b.Yammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Object loc.sourcePath should endWith(".scala") @@ -173,7 +176,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().find(_._1 == "ValDef").get._2 loc.packageName shouldBe "com.b" loc.className shouldBe "Wammy" - loc.topLevelClass shouldBe "Wammy" + loc.fullClassName shouldBe "com.b.Wammy" loc.method shouldBe "" loc.classType shouldBe ClassType.Trait loc.sourcePath should endWith(".scala") @@ -189,7 +192,7 @@ class LocationTest extends FreeSpec with Matchers { val loc = compiler.locations.result().filter(_._1 == "Template").last._2 loc.packageName shouldBe "com.a" loc.className shouldBe "C" - loc.topLevelClass shouldBe "C" + loc.fullClassName shouldBe "com.a.C" loc.method shouldBe "" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") @@ -198,16 +201,24 @@ class LocationTest extends FreeSpec with Matchers { val compiler = ScoverageCompiler.locationCompiler compiler.compile( "package com.a; object A { def foo(b : B) : Unit = b.invoke }; trait B { def invoke : Unit }; class C { A.foo(new B { def invoke = () }) }") - println() - println(compiler.locations.result().mkString("\n")) val loc = compiler.locations.result().filter(_._1 == "DefDef").last._2 loc.packageName shouldBe "com.a" loc.className shouldBe "C" - loc.topLevelClass shouldBe "C" + loc.fullClassName shouldBe "com.a.C" loc.method shouldBe "invoke" loc.classType shouldBe ClassType.Class loc.sourcePath should endWith(".scala") } + "doubly nested classes should report correct fullClassName" in { + val compiler = ScoverageCompiler.locationCompiler + compiler.compile("package com.a \n object Foo { object Boo { object Moo { val name = 'sam } } }") + val loc = compiler.locations.result().find(_._1 == "ValDef").get._2 + loc.packageName shouldBe "com.a" + loc.className shouldBe "Moo" + loc.fullClassName shouldBe "com.a.Foo.Boo.Moo" + loc.method shouldBe "" + loc.classType shouldBe ClassType.Object + loc.sourcePath should endWith(".scala") + } } } - diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala b/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala index f565b906..8bad03c3 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala +++ b/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageHtmlWriterTest.scala @@ -28,14 +28,14 @@ class ScoverageHtmlWriterTest extends FunSuite { coverage.add( Statement(class2, - Location("coverage.sample", "Class2", "Class2", ClassType.Class, "msg_test", class2), + Location("coverage.sample", "Class2", "coverage.sample.Class2", ClassType.Class, "msg_test", class2), 2, 60, 80, 4, "scala.this.Predef.println(\"test code\")", "scala.Predef.println", "Apply", false, 0) ) coverage.add( Statement(class1, - Location("coverage.sample", "Class1", "Class1", ClassType.Class, "msg_coverage", class1), + Location("coverage.sample", "Class1", "coverage.sample.Class1", ClassType.Class, "msg_coverage", class1), 1, 64, 99, 4, "scala.this.Predef.println(\"measure coverage of code\")", "scala.Predef.println", "Apply", false, 0) ) diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageXmlReaderTest.scala b/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageXmlReaderTest.scala index ebda28c6..d2033e39 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageXmlReaderTest.scala +++ b/scalac-scoverage-plugin/src/test/scala/scoverage/ScoverageXmlReaderTest.scala @@ -20,7 +20,7 @@ class ScoverageXmlReaderTest extends FreeSpec with Matchers { coverage.add(Statement(canonicalPath("com/scoverage/class.scala"), Location("com.scoverage", "Test", - "TopLevel", + "com.scoverage.TopLevel.Test", ClassType.Object, "somemeth", canonicalPath("com/scoverage/class.scala")), @@ -37,7 +37,7 @@ class ScoverageXmlReaderTest extends FreeSpec with Matchers { coverage.add(Statement(canonicalPath("com/scoverage/foo/class.scala"), Location("com.scoverage.foo", "ServiceState", - "Service", + "com.scoverage.foo.Service.ServiceState", ClassType.Trait, "methlab", canonicalPath("com/scoverage/foo/class.scala")), diff --git a/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala b/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala index 297e24e8..9982945e 100644 --- a/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala +++ b/scalac-scoverage-plugin/src/test/scala/scoverage/SerializerTest.scala @@ -14,13 +14,13 @@ class SerializerTest extends FunSuite with MockitoSugar with OneInstancePerTest coverage.add( Statement( "mysource", - Location("org.scoverage", "test", "test", ClassType.Trait, "mymethod", "mypath"), + Location("org.scoverage", "test", "org.scoverage.test", ClassType.Trait, "mymethod", "mypath"), 14, 100, 200, 4, "def test : String", "test", "DefDef", true, 32 ) ) val expected = - mysource org.scoverage test Trait test mymethod mypath 14 100 200 4 def test : String test DefDef true 32 false + mysource org.scoverage test Trait org.scoverage.test mymethod mypath 14 100 200 4 def test : String test DefDef true 32 false val writer = new StringWriter() @@ -31,12 +31,12 @@ class SerializerTest extends FunSuite with MockitoSugar with OneInstancePerTest test("coverage should be deserializable from xml") { val input = - mysource org.scoverage test Trait test mymethod mypath 14 100 200 4 def test : String test DefDef true 32 false + mysource org.scoverage test Trait org.scoverage.test mymethod mypath 14 100 200 4 def test : String test DefDef true 32 false val statements = List(Statement( "mysource", - Location("org.scoverage", "test", "test", ClassType.Trait, "mymethod", "mypath"), + Location("org.scoverage", "test", "org.scoverage.test", ClassType.Trait, "mymethod", "mypath"), 14, 100, 200, 4, "def test : String", "test", "DefDef", true, 32 )) val coverage = Serializer.deserialize(input.toString()) diff --git a/version.sbt b/version.sbt index a1b7d187..eea4ff59 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "1.1.1" \ No newline at end of file +version in ThisBuild := "1.2.0"