From e2958d6d85529d48aa57cdf7fd6ca838a62e2535 Mon Sep 17 00:00:00 2001 From: Tejas Mandke Date: Tue, 25 Nov 2014 09:17:04 -0800 Subject: [PATCH] Add support for multi module project in runtime --- .../src/main/scala/scoverage/Invoker.scala | 21 +++++---- .../scoverage/InvokerMultiModuleTest.scala | 47 +++++++++++++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 scalac-scoverage-runtime/src/test/scala/scoverage/InvokerMultiModuleTest.scala diff --git a/scalac-scoverage-runtime/src/main/scala/scoverage/Invoker.scala b/scalac-scoverage-runtime/src/main/scala/scoverage/Invoker.scala index aafc7bc1..6660b6f9 100644 --- a/scalac-scoverage-runtime/src/main/scala/scoverage/Invoker.scala +++ b/scalac-scoverage-runtime/src/main/scala/scoverage/Invoker.scala @@ -10,8 +10,8 @@ import scala.io.Source object Invoker { private val MeasurementsPrefix = "scoverage.measurements." - private val threadFile = new ThreadLocal[FileWriter] - private val ids = TrieMap.empty[Int, Any] + private val threadFiles = new ThreadLocal[TrieMap[String, FileWriter]] + private val ids = TrieMap.empty[(String, Int), Any] /** * We record that the given id has been invoked by appending its id to the coverage @@ -34,17 +34,18 @@ object Invoker { // times since for coverage we only care about 1 or more, (it just slows things down to // do it more than once), anything we can do to help is good. This helps especially with code // that is executed many times quickly, eg tight loops. - if (!ids.contains(id)) { + if (!ids.contains(dataDir, id)) { // Each thread writes to a separate measurement file, to reduce contention // and because file appends via FileWriter are not atomic on Windows. - var writer = threadFile.get() - if (writer == null) { - val file = measurementFile(dataDir) - writer = new FileWriter(file, true) - threadFile.set(writer) - } + var files = threadFiles.get() + if (files == null) + files = TrieMap.empty[String, FileWriter] + threadFiles.set(files) + + val writer = files.getOrElseUpdate(dataDir, new FileWriter(measurementFile(dataDir), true)) writer.append(id.toString + '\n').flush() - ids.put(id, ()) + + ids.put((dataDir, id), ()) } } diff --git a/scalac-scoverage-runtime/src/test/scala/scoverage/InvokerMultiModuleTest.scala b/scalac-scoverage-runtime/src/test/scala/scoverage/InvokerMultiModuleTest.scala new file mode 100644 index 00000000..e034b57d --- /dev/null +++ b/scalac-scoverage-runtime/src/test/scala/scoverage/InvokerMultiModuleTest.scala @@ -0,0 +1,47 @@ +package scoverage + +import java.io.File + +import org.scalatest.{BeforeAndAfter, FunSuite} + +/** + * Verify that [[Invoker.invoked()]] can handle a multi-module project + */ +class InvokerMultiModuleTest extends FunSuite with BeforeAndAfter { + + val measurementDir = Array(new File("invoker-test.measurement0"), new File("invoker-test.measurement1")) + + before { + deleteMeasurementFiles() + measurementDir.foreach(_.mkdirs) + } + + test("calling Invoker.invoked on with different directories puts mesurments in different directories") { + + val testIds: Set[Int] = (1 to 10).toSet + + testIds.map { i: Int => Invoker.invoked(i, measurementDir(i % 2).toString) } + + // Verify measurements went to correct directory + val measurementFiles0 = Invoker.findMeasurementFiles(measurementDir(0)) + val idsFromFile0 = Invoker.invoked(measurementFiles0).toSet + + idsFromFile0 === testIds.filter { i: Int => i % 2 == 0 } + + val measurementFiles1 = Invoker.findMeasurementFiles(measurementDir(0)) + val idsFromFile1 = Invoker.invoked(measurementFiles1).toSet + idsFromFile1 === testIds.filter { i: Int => i % 2 == 1 } + } + + after { + deleteMeasurementFiles() + measurementDir.foreach(_.delete) + } + + private def deleteMeasurementFiles(): Unit = { + measurementDir.foreach((md) => { + if (md.isDirectory) + md.listFiles().foreach(_.delete()) + }) + } +}