diff options
author | Guillaume Martres <smarter@ubuntu.com> | 2016-05-31 15:23:04 +0200 |
---|---|---|
committer | Felix Mulder <felix.mulder@gmail.com> | 2016-06-07 17:10:36 +0200 |
commit | 09b2f39731386cd5b3688d5c3badf75d956d4f6d (patch) | |
tree | d122fc0e96d6c1cd22d7e0d0b5ee523563d534e7 /bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala | |
parent | 030ff82070197f0c126f5c0287e076b0f6b6dd8d (diff) | |
download | dotty-09b2f39731386cd5b3688d5c3badf75d956d4f6d.tar.gz dotty-09b2f39731386cd5b3688d5c3badf75d956d4f6d.tar.bz2 dotty-09b2f39731386cd5b3688d5c3badf75d956d4f6d.zip |
Make the dotty-bridge sbt project a subproject of dotty
Note that the dotty-bridge tests will not be run automatically by `test`
which is short for `dotty/test`, to run the dotty-bridge tests, do in sbt:
> dotty-bridge/test
> dotty-bridge/scripted
Original history: https://github.com/smarter/dotty-bridge/commits/master
Diffstat (limited to 'bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala')
-rw-r--r-- | bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala b/bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala new file mode 100644 index 000000000..db037effe --- /dev/null +++ b/bridge/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala @@ -0,0 +1,191 @@ +/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ScalaCompilerForUnitTesting.scala */ +package xsbt + +import xsbti.compile.SingleOutput +import java.io.File +import _root_.scala.tools.nsc.reporters.ConsoleReporter +import _root_.scala.tools.nsc.Settings +import xsbti._ +import xsbti.api.SourceAPI +import sbt.IO.withTemporaryDirectory +import xsbti.api.ClassLike +import xsbti.api.Definition +import xsbti.api.Def +import xsbt.api.SameAPI +import sbt.ConsoleLogger +import xsbti.DependencyContext._ + +import ScalaCompilerForUnitTesting.ExtractedSourceDependencies + +/** + * Provides common functionality needed for unit tests that require compiling + * source code using Scala compiler. + */ +class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) { + + /** + * Compiles given source code using Scala compiler and returns API representation + * extracted by ExtractAPI class. + */ + def extractApiFromSrc(src: String): SourceAPI = { + val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) + analysisCallback.apis(tempSrcFile) + } + + def extractUsedNamesFromSrc(src: String): Set[String] = { + val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) + analysisCallback.usedNames(tempSrcFile) + } + + /** + * Extract used names from src provided as the second argument. + * + * The purpose of the first argument is to define names that the second + * source is going to refer to. Both files are compiled in the same compiler + * Run but only names used in the second src file are returned. + */ + def extractUsedNamesFromSrc(definitionSrc: String, actualSrc: String): Set[String] = { + // we drop temp src file corresponding to the definition src file + val (Seq(_, tempSrcFile), analysisCallback) = compileSrcs(definitionSrc, actualSrc) + analysisCallback.usedNames(tempSrcFile) + } + + /** + * Compiles given source code snippets (passed as Strings) using Scala compiler and returns extracted + * dependencies between snippets. Source code snippets are identified by symbols. Each symbol should + * be associated with one snippet only. + * + * Snippets can be grouped to be compiled together in the same compiler run. This is + * useful to compile macros, which cannot be used in the same compilation run that + * defines them. + * + * Symbols are used to express extracted dependencies between source code snippets. This way we have + * file system-independent way of testing dependencies between source code "files". + */ + def extractDependenciesFromSrcs(srcs: List[Map[Symbol, String]]): ExtractedSourceDependencies = { + val rawGroupedSrcs = srcs.map(_.values.toList) + val symbols = srcs.flatMap(_.keys) + val (tempSrcFiles, testCallback) = compileSrcs(rawGroupedSrcs) + val fileToSymbol = (tempSrcFiles zip symbols).toMap + + val memberRefFileDeps = testCallback.sourceDependencies collect { + // false indicates that those dependencies are not introduced by inheritance + case (target, src, DependencyByMemberRef) => (src, target) + } + val inheritanceFileDeps = testCallback.sourceDependencies collect { + // true indicates that those dependencies are introduced by inheritance + case (target, src, DependencyByInheritance) => (src, target) + } + def toSymbols(src: File, target: File): (Symbol, Symbol) = (fileToSymbol(src), fileToSymbol(target)) + val memberRefDeps = memberRefFileDeps map { case (src, target) => toSymbols(src, target) } + val inheritanceDeps = inheritanceFileDeps map { case (src, target) => toSymbols(src, target) } + def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { + import scala.collection.mutable.{ HashMap, MultiMap } + val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] + val multiMap = pairs.foldLeft(emptyMultiMap) { + case (acc, (key, value)) => + acc.addBinding(key, value) + } + // convert all collections to immutable variants + multiMap.toMap.mapValues(_.toSet).withDefaultValue(Set.empty) + } + + ExtractedSourceDependencies(pairsToMultiMap(memberRefDeps), pairsToMultiMap(inheritanceDeps)) + } + + def extractDependenciesFromSrcs(srcs: (Symbol, String)*): ExtractedSourceDependencies = { + val symbols = srcs.map(_._1) + assert(symbols.distinct.size == symbols.size, + s"Duplicate symbols for srcs detected: $symbols") + extractDependenciesFromSrcs(List(srcs.toMap)) + } + + /** + * Compiles given source code snippets written to temporary files. Each snippet is + * written to a separate temporary file. + * + * Snippets can be grouped to be compiled together in the same compiler run. This is + * useful to compile macros, which cannot be used in the same compilation run that + * defines them. + * + * The sequence of temporary files corresponding to passed snippets and analysis + * callback is returned as a result. + */ + private def compileSrcs(groupedSrcs: List[List[String]]): (Seq[File], TestCallback) = { + withTemporaryDirectory { temp => + val analysisCallback = new TestCallback(nameHashing) + val classesDir = new File(temp, "classes") + classesDir.mkdir() + + // val (compiler, ctx) = prepareCompiler(classesDir, analysisCallback, classesDir.toString) + + val files = for ((compilationUnit, unitId) <- groupedSrcs.zipWithIndex) yield { + val (compiler, ctx) = prepareCompiler(classesDir, analysisCallback, classesDir.toString) + val run = compiler.newRun(ctx) + val srcFiles = compilationUnit.toSeq.zipWithIndex map { + case (src, i) => + val fileName = s"Test-$unitId-$i.scala" + prepareSrcFile(temp, fileName, src) + } + val srcFilePaths = srcFiles.map(srcFile => srcFile.getAbsolutePath).toList + + run.compile(srcFilePaths) + + srcFilePaths.foreach(f => new File(f).delete) + srcFiles + } + (files.flatten.toSeq, analysisCallback) + } + } + + private def compileSrcs(srcs: String*): (Seq[File], TestCallback) = { + compileSrcs(List(srcs.toList)) + } + + private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = { + val srcFile = new File(baseDir, fileName) + sbt.IO.write(srcFile, src) + srcFile + } + + private def prepareCompiler(outputDir: File, analysisCallback: AnalysisCallback, classpath: String = ".") = { + val args = Array.empty[String] + object output extends SingleOutput { + def outputDirectory: File = outputDir + override def toString = s"SingleOutput($outputDirectory)" + } + + import dotty.tools.dotc._ + import dotty.tools.dotc.core.Contexts._ + + val driver = new Driver { + + protected def newCompiler(implicit ctx: Context): Compiler = new Compiler + + override protected def sourcesRequired = false + + def getCompiler(args: Array[String], rootCtx: Context) = { + val (fileNames, ctx) = setup(args, rootCtx) + (newCompiler(ctx), ctx) + } + } + val ctx = (new ContextBase).initialCtx.fresh.setSbtCallback(analysisCallback) + driver.getCompiler(Array("-classpath", classpath, "-usejavacp"), ctx) + } + + private object ConsoleReporter extends Reporter { + def reset(): Unit = () + def hasErrors: Boolean = false + def hasWarnings: Boolean = false + def printWarnings(): Unit = () + def problems: Array[Problem] = Array.empty + def log(pos: Position, msg: String, sev: Severity): Unit = println(msg) + def comment(pos: Position, msg: String): Unit = () + def printSummary(): Unit = () + } + +} + +object ScalaCompilerForUnitTesting { + case class ExtractedSourceDependencies(memberRef: Map[Symbol, Set[Symbol]], inheritance: Map[Symbol, Set[Symbol]]) +} |