From a3f1b9c22fe2e95bb9d376d7ccf1dde8b0258120 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 6 Oct 2010 08:24:13 -0400 Subject: [PATCH] fix tests, discovery updated compile tests for new minimal AnalysisCallback moved discovery to discovery/ subproject and updated for new approach fixed discovery to only find public methods when searching for annotated definitions extracting inherited definitions unimplemented in api/, so some discovery tests fail moved discovery classes from sbt.inc package to sbt.compile --- compile/discover/Discovered.scala | 2 +- compile/discover/Discovery.scala | 12 +-- .../src/test/scala/ApplicationsTest.scala | 23 +++-- .../src/test/scala/DetectAnnotations.scala | 39 ++++++--- .../src/test/scala/DetectSubclasses.scala | 42 +++++++++ compile/src/test/scala/CheckBasic.scala | 13 +-- compile/src/test/scala/CompileTest.scala | 87 ++++++------------- compile/src/test/scala/DetectSubclasses.scala | 34 -------- compile/src/test/scala/TestCompile.scala | 31 +++---- compile/src/test/scala/WithCompiler.scala | 39 +++++++++ interface/src/test/scala/TestCallback.scala | 29 ++----- ivy/src/test/scala/ComponentManagerTest.scala | 2 +- ivy/src/test/scala/TestIvyLogger.scala | 12 --- main/DefaultProject.scala | 3 +- main/build/Build.scala | 8 +- main/build/LoadCommand.scala | 5 +- main/build/Parse.scala | 5 +- project/build/XSbt.scala | 9 +- util/log/src/test/scala/TestLogger.scala | 11 +++ 19 files changed, 219 insertions(+), 187 deletions(-) rename compile/{ => discover}/src/test/scala/ApplicationsTest.scala (82%) rename compile/{ => discover}/src/test/scala/DetectAnnotations.scala (60%) create mode 100644 compile/discover/src/test/scala/DetectSubclasses.scala delete mode 100644 compile/src/test/scala/DetectSubclasses.scala create mode 100644 compile/src/test/scala/WithCompiler.scala delete mode 100644 ivy/src/test/scala/TestIvyLogger.scala create mode 100644 util/log/src/test/scala/TestLogger.scala diff --git a/compile/discover/Discovered.scala b/compile/discover/Discovered.scala index 068be9e83..2776a41c9 100644 --- a/compile/discover/Discovered.scala +++ b/compile/discover/Discovered.scala @@ -2,7 +2,7 @@ * Copyright 2010 Mark Harrah */ package sbt -package inc +package compile final case class Discovered(baseClasses: Set[String], annotations: Set[String], hasMain: Boolean, isModule: Boolean) { diff --git a/compile/discover/Discovery.scala b/compile/discover/Discovery.scala index e2ac36a89..f7f8842a2 100644 --- a/compile/discover/Discovery.scala +++ b/compile/discover/Discovery.scala @@ -2,7 +2,7 @@ * Copyright 2010 Mark Harrah */ package sbt -package inc +package compile import xsbti.api.{Path => APath, _} @@ -23,14 +23,14 @@ class Discovery(baseClasses: Set[String], annotations: Set[String]) val onClass = findAnnotations(c.annotations) val onDefs = defAnnotations(c.structure.declared) ++ defAnnotations(c.structure.inherited) val module = isModule(c) - new Discovered( bases(c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module ) + new Discovered( bases(c.name, c.structure.parents), onClass ++ onDefs, module && hasMainMethod(c), module ) } - def bases(c: Seq[Type]): Set[String] = - c.flatMap(simpleName).filter(baseClasses).toSet + def bases(own: String, c: Seq[Type]): Set[String] = + (own +: c.flatMap(simpleName)).filter(baseClasses).toSet def findAnnotations(as: Seq[Annotation]): Set[String] = as.flatMap { a => simpleName(a.base).filter(annotations) }.toSet def defAnnotations(defs: Seq[Definition]): Set[String] = - findAnnotations( defs.flatMap { case d: Def => d.annotations.toSeq; case _ => Nil } ) + findAnnotations( defs.flatMap { case d: Def if isPublic(d) => d.annotations.toSeq; case _ => Nil } ) } object Discovery { @@ -39,6 +39,8 @@ object Discovery val d = new Discovery(subclasses, annotations) d(definitions) } + def applications(definitions: Seq[Definition]): Seq[(Definition, Discovered)] = + apply(Set.empty, Set.empty)( definitions ) def isConcrete(a: Definition): Boolean = isConcrete(a.modifiers) def isConcrete(m: Modifiers) = !m.isAbstract && !m.isDeferred diff --git a/compile/src/test/scala/ApplicationsTest.scala b/compile/discover/src/test/scala/ApplicationsTest.scala similarity index 82% rename from compile/src/test/scala/ApplicationsTest.scala rename to compile/discover/src/test/scala/ApplicationsTest.scala index 0dea907e4..d8be01580 100644 --- a/compile/src/test/scala/ApplicationsTest.scala +++ b/compile/discover/src/test/scala/ApplicationsTest.scala @@ -1,8 +1,9 @@ -package xsbt +package sbt +package compile -import java.io.File -import java.net.URLClassLoader -import org.specs.Specification + import java.io.File + import java.net.URLClassLoader + import org.specs.Specification /** Verifies that the analyzer plugin properly detects main methods. The main method must be * public with the right signature and be defined on a public, top-level module.*/ @@ -104,16 +105,24 @@ object ApplicationsTest extends Specification "Analysis plugin should detect applications" in { WithFiles(sources : _*) { case files @ Seq(main, main2, main3, main4, main5, main6, main7, main8, main9, mainA, mainB, mainC, mainD, mainE, mainF) => for(scalaVersion <- TestCompile.allVersions) - CallbackTest(scalaVersion, files, Nil, Nil) { (callback, file, scalaInstance, log) => - val expected = Seq( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB", + CallbackTest.full(scalaVersion, files) { (callback, file, scalaInstance, log) => + val expected = Set( main -> "Main", main4 -> "Main4", main8 -> "Main8", main9 -> "Main9", mainB -> "MainB", mainE -> "MainE1", mainE -> "MainE2", mainE -> "MainE3", mainE -> "MainE4", mainE -> "MainE5", mainF -> "MainF1", mainF -> "MainF2", mainF -> "MainF4") - (callback.applications) must haveTheSameElementsAs(expected) + val actual = applications(callback).toSet + (actual -- expected) must beEmpty + (expected -- actual) must beEmpty val loader = new URLClassLoader(Array(file.toURI.toURL), scalaInstance.loader) for( (_, className) <- expected) testRun(loader, className) } } } + def applications(callback: xsbti.TestCallback): Seq[(File, String)] = + for( (file, api) <- callback.apis.toSeq; application <- applications(api)) + yield (file, application) + def applications(src: xsbti.api.Source): Seq[String] = + Discovery.applications(src.definitions) collect { case (definition, Discovered(_, _, true, _)) => definition.name } + private def testRun(loader: ClassLoader, className: String) { val obj = Class.forName(className, true, loader) diff --git a/compile/src/test/scala/DetectAnnotations.scala b/compile/discover/src/test/scala/DetectAnnotations.scala similarity index 60% rename from compile/src/test/scala/DetectAnnotations.scala rename to compile/discover/src/test/scala/DetectAnnotations.scala index d5bd5fbff..56993bb30 100644 --- a/compile/src/test/scala/DetectAnnotations.scala +++ b/compile/discover/src/test/scala/DetectAnnotations.scala @@ -1,7 +1,8 @@ -package xsbt +package sbt +package compile -import java.io.File -import org.specs.Specification + import java.io.File + import org.specs.Specification object DetectAnnotations extends Specification { @@ -32,20 +33,30 @@ object DetectAnnotations extends Specification { case files @ Seq(a, b, c, sup1File, sup2File, sup3File, sup4File, midFile, sub1File, sub2File, sub3File, sub4File, sub5File, sub6File, sub7File, sub8File, sub9File, subAFile, subBFile) => for(scalaVersion <- TestCompile.allVersions) - CallbackTest(scalaVersion, files, Nil, Seq("c.A", "B", "d.C") ) { (callback, _, _, _) => + CallbackTest.simple(scalaVersion, files) { callback => val expected = - (sup3File, "Super3", "B", false) :: - (sub3File, "Sub3", "B", false) :: - (sub3File, "Sub3", "c.A", false) :: - (sub7File, "Sub7", "d.C", false) :: - (sub8File, "Sub8", "c.A", false) :: - (sub9File, "Sub9", "B", true) :: - (subAFile, "SubA", "c.A", true) :: - (subBFile, "SubB", "c.A", true) :: - (subBFile, "SubB", "d.C", false) :: + (sup3File, "Super3", Set("B"), false) :: + (sub3File, "Sub3", Set("B", "c.A"), false) :: + (sub7File, "Sub7", Set("d.C"), false) :: + (sub8File, "Sub8", Set("c.A"), false) :: + (sub9File, "Sub9", Set("B"), true) :: + (subAFile, "SubA", Set("c.A"), true) :: + (subBFile, "SubB", Set("c.A"), true) :: + (subBFile, "SubB", Set("d.C"), false) :: Nil - (callback.foundAnnotated) must haveTheSameElementsAs(expected) + val actual = subclasses(callback).toSet + val actualOnly = (actual -- expected) + println("Actual: " + actualOnly) + val expectedOnly = (expected.toSet -- actual) + println("Expected: " + expectedOnly) + expectedOnly must beEmpty + actualOnly must beEmpty } } } + def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] = + for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(Set.empty, annotationNames)(src.definitions) if !discovered.isEmpty ) yield + (file, definition.name, discovered.annotations, discovered.isModule) + + def annotationNames = Set("c.A", "B", "d.C") } \ No newline at end of file diff --git a/compile/discover/src/test/scala/DetectSubclasses.scala b/compile/discover/src/test/scala/DetectSubclasses.scala new file mode 100644 index 000000000..7985d2014 --- /dev/null +++ b/compile/discover/src/test/scala/DetectSubclasses.scala @@ -0,0 +1,42 @@ +package sbt +package compile + + import java.io.File + import org.specs.Specification + +object DetectSubclasses extends Specification +{ + val sources = + ("a/Super.scala" -> "package a; trait Super") :: + ("a/Super2.scala" -> "class Super2") :: + ("b/Middle.scala" -> "package y.w; trait Mid extends a.Super") :: + ("b/Sub1.scala" -> "package a; class Sub1 extends y.w.Mid") :: + ("b/Sub2.scala" -> "final class Sub2 extends a.Super") :: + ("Sub3.scala" -> "private class F extends a.Super; package c { object Sub3 extends Super2 }") :: + Nil + + "Analysis plugin should detect subclasses" in { + WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*) + { + case files @ Seq(supFile, sup2File, midFile, sub1File, sub2File, sub3File) => + for(scalaVersion <- TestCompile.allVersions) + CallbackTest.simple(scalaVersion, files) { callback => + val expected = + (sub1File, "a.Sub1", Set("a.Super"), false) :: + (sub2File, "Sub2", Set("a.Super"), false) :: + (sup2File, "Super2", Set("Super2"), false) :: + (sub3File, "c.Sub3", Set("Super2"), true) :: + Nil + val actual = subclasses(callback).toSet + val actualOnly = actual -- expected + val expectedOnly = expected.toSet -- actual + assert(actualOnly.isEmpty, "Actual only: " + actualOnly) + assert(expectedOnly.isEmpty , "Expected only: " + expectedOnly) + } + } + } + def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] = + for( (file, src) <- callback.apis.toSeq; (definition, discovered) <- Discovery(subclassNames, Set.empty)(src.definitions) if !discovered.isEmpty ) yield + (file, definition.name, discovered.baseClasses, discovered.isModule) + def subclassNames = Set( "a.Super", "Super2", "x.Super3", "Super4") +} \ No newline at end of file diff --git a/compile/src/test/scala/CheckBasic.scala b/compile/src/test/scala/CheckBasic.scala index e69a9fd0c..4fd4ce0dd 100644 --- a/compile/src/test/scala/CheckBasic.scala +++ b/compile/src/test/scala/CheckBasic.scala @@ -1,7 +1,8 @@ -package xsbt +package sbt +package compile -import java.io.File -import org.specs.Specification + import java.io.File + import org.specs.Specification object CheckBasic extends Specification { @@ -21,9 +22,9 @@ object CheckBasic extends Specification WithFiles(basicName -> basicSource){ files => for(scalaVersion <- TestCompile.allVersions) { - FileUtilities.withTemporaryDirectory { outputDirectory => + IO.withTemporaryDirectory { outputDirectory => WithCompiler(scalaVersion) { (compiler, log) => - compiler.doc(Set() ++ files, Set.empty, outputDirectory, Nil, 5, log) + compiler.doc(files.toSeq, Nil, outputDirectory, Nil, 5, log) } } true must beTrue // don't know how to just check that previous line completes without exception @@ -33,7 +34,7 @@ object CheckBasic extends Specification "Analyzer plugin should send source begin and end" in { WithFiles(basicName -> basicSource) { files => for(scalaVersion <- TestCompile.allVersions) - CallbackTest(scalaVersion, files) { callback => + CallbackTest.simple(scalaVersion, files) { callback => (callback.beganSources) must haveTheSameElementsAs(files) (callback.endedSources) must haveTheSameElementsAs(files) } diff --git a/compile/src/test/scala/CompileTest.scala b/compile/src/test/scala/CompileTest.scala index fe17614fd..1549c57a3 100644 --- a/compile/src/test/scala/CompileTest.scala +++ b/compile/src/test/scala/CompileTest.scala @@ -1,10 +1,9 @@ -package xsbt +package sbt +package compile -import sbt.{ComponentManager, TestIvyLogger} - -import java.io.File -import FileUtilities.withTemporaryDirectory -import org.specs._ + import java.io.File + import IO.withTemporaryDirectory + import org.specs._ object CompileTest extends Specification { @@ -15,8 +14,8 @@ object CompileTest extends Specification WithCompiler( "2.7.4" )(testCompileAnalysis) WithCompiler( "2.7.5" )(testCompileAnalysis) WithCompiler( "2.7.7" )(testCompileAnalysis) - WithCompiler( "2.8.0.Beta1" )(testCompileAnalysis) - WithCompiler( "2.8.0-SNAPSHOT" )(testCompileAnalysis) + WithCompiler( "2.8.0" )(testCompileAnalysis) + WithCompiler( "2.8.1.RC2" )(testCompileAnalysis) } } @@ -24,16 +23,17 @@ object CompileTest extends Specification "Properly handle classpaths" in { testClasspath("2.7.2") testClasspath("2.7.7") - testClasspath("2.8.0.Beta1") + testClasspath("2.8.0") + testClasspath("2.8.1.RC2") } } - private def testCompileAnalysis(compiler: AnalyzingCompiler, log: CompileLogger) + private def testCompileAnalysis(compiler: AnalyzingCompiler, log: Logger) { WithFiles( new File("Test.scala") -> "object Test" ) { sources => withTemporaryDirectory { temp => - val callback = new xsbti.TestCallback(Array(), Array()) - compiler(Set() ++ sources, Set.empty, temp, Nil, callback, 10, log) + val callback = new xsbti.TestCallback + compiler(sources, Nil, temp, Nil, callback, 10, log) (callback.beganSources) must haveTheSameElementsAs(sources) } } @@ -52,66 +52,31 @@ object CompileTest extends Specification def compiler(autoBoot: Boolean, compilerOnClasspath: Boolean): RawCompiler = new RawCompiler(ScalaInstance(scalaVersion, launch), new ClasspathOptions(autoBoot, compilerOnClasspath, true), log) - val callback = new xsbti.TestCallback(Array(), Array()) + val callback = new xsbti.TestCallback val standard = compiler(true, true) val noCompiler = compiler(true, false) val fullExplicit = compiler(false, false) val fullBoot = "-bootclasspath" :: fullExplicit.compilerArguments.createBootClasspath :: Nil - val withCompiler = Set() + noCompiler.scalaInstance.compilerJar + val withCompiler = noCompiler.scalaInstance.compilerJar :: Nil - WithFiles( new File("Test.scala") -> "object Test", new File("Test2.scala") -> UsingCompiler ) { case Array(plain, useCompiler) => - val plainSrcs = Set[File](plain) - val compSrcs = Set[File](useCompiler) - FileUtilities.withTemporaryDirectory { out => - standard(plainSrcs, Set.empty, out, Nil) //success - standard(compSrcs, Set.empty, out, Nil) //success + WithFiles( new File("Test.scala") -> "object Test", new File("Test2.scala") -> UsingCompiler ) { case Seq(plain, useCompiler) => + val plainSrcs = Seq[File](plain) + val compSrcs = Seq[File](useCompiler) + withTemporaryDirectory { out => + standard(plainSrcs, Nil, out, Nil) //success + standard(compSrcs, Nil, out, Nil) //success - noCompiler(plainSrcs, Set.empty, out, Nil) //success - shouldFail( noCompiler(compSrcs, Set.empty, out, Nil) ) - noCompiler(compSrcs, withCompiler, out, Nil) //success + noCompiler(plainSrcs, Nil, out, Nil) //success + shouldFail( noCompiler(compSrcs, Nil, out, Nil) ) + noCompiler(compSrcs, withCompiler, out, Nil) //success - shouldFail( fullExplicit(plainSrcs, Set.empty, out, Nil) )// failure - shouldFail( fullExplicit(compSrcs, Set.empty, out, Nil) )// failure - fullExplicit(plainSrcs, Set.empty, out, fullBoot) // success + shouldFail( fullExplicit(plainSrcs, Nil, out, Nil) )// failure + shouldFail( fullExplicit(compSrcs, Nil, out, Nil) )// failure + fullExplicit(plainSrcs, Nil, out, fullBoot) // success fullExplicit(compSrcs, withCompiler, out, fullBoot) // success } } } } -object WithCompiler -{ - def apply[T](scalaVersion: String)(f: (AnalyzingCompiler, CompileLogger) => T): T = - { - launcher { (launch, log) => - FileUtilities.withTemporaryDirectory { componentDirectory => - val manager = new ComponentManager(xsbt.boot.Locks, new boot.ComponentProvider(componentDirectory), log) - val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager, log) - compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID) - define(manager, ComponentCompiler.compilerInterfaceSrcID, getResource("CompilerInterface.scala"), getClassResource(classOf[jline.Completor])) - define(manager, ComponentCompiler.xsbtiID, getClassResource(classOf[xsbti.AnalysisCallback])) - f(compiler, log) - } - } - } - def launcher[T](f: (xsbti.Launcher, TestIvyLogger with CompileLogger) => T): T = - { - val log = new TestIvyLogger with CompileLogger - log.setLevel(Level.Debug) - log.bufferQuietly { - boot.LaunchTest.withLauncher { launch => f(launch, log) } - } - } - def getClassResource(resource: Class[_]): File = FileUtilities.classLocationFile(resource) - def getResource(resource: String): File = - { - val src = getClass.getClassLoader.getResource(resource) - if(src ne null) FileUtilities.asFile(src) else error("Resource not found: " + resource) - } - def define(manager: ComponentManager, id: String, files: File*) - { - manager.clearCache(id) - manager.define(id, files) - } -} \ No newline at end of file diff --git a/compile/src/test/scala/DetectSubclasses.scala b/compile/src/test/scala/DetectSubclasses.scala deleted file mode 100644 index 58e77f4b3..000000000 --- a/compile/src/test/scala/DetectSubclasses.scala +++ /dev/null @@ -1,34 +0,0 @@ -package xsbt - -import java.io.File -import org.specs.Specification - -object DetectSubclasses extends Specification -{ - val sources = - ("a/Super.scala" -> "package a; trait Super") :: - ("a/Super2.scala" -> "class Super2") :: - ("b/Middle.scala" -> "package y.w; trait Mid extends a.Super") :: - ("b/Sub1.scala" -> "package a; class Sub1 extends y.w.Mid") :: - ("b/Sub2.scala" -> "final class Sub2 extends a.Super") :: - ("Sub3.scala" -> "private class F extends a.Super; package c { object Sub3 extends Super2 }") :: - Nil - - "Analysis plugin should detect subclasses" in { - WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*) - { - case files @ Seq(supFile, sup2File, midFile, sub1File, sub2File, sub3File) => - for(scalaVersion <- TestCompile.allVersions) - CallbackTest(scalaVersion, files, Seq( "a.Super", "Super2", "x.Super3", "Super4"), Nil ) { (callback, _, _, _) => - val expected = - (sub1File, "a.Sub1", "a.Super", false) :: - (sub2File, "Sub2", "a.Super", false) :: - (sup2File, "Super2", "Super2", false) :: - (sub3File, "c.Sub3", "Super2", true) :: - Nil - (callback.foundSubclasses) must haveTheSameElementsAs(expected) - (callback.invalidSuperclasses) must haveTheSameElementsAs(Seq("x.Super3", "Super4")) - } - } - } -} \ No newline at end of file diff --git a/compile/src/test/scala/TestCompile.scala b/compile/src/test/scala/TestCompile.scala index 76ef4537a..9ca5c7211 100644 --- a/compile/src/test/scala/TestCompile.scala +++ b/compile/src/test/scala/TestCompile.scala @@ -1,36 +1,37 @@ -package xsbt +package sbt +package compile -import java.io.File -import java.net.URLClassLoader -import xsbti.TestCallback -import FileUtilities.withTemporaryDirectory + import java.io.File + import java.net.URLClassLoader + import xsbti.TestCallback + import IO.withTemporaryDirectory object TestCompile { // skip 2.7.3 and 2.7.4 for speed - def allVersions = List("2.7.2", "2.7.5", "2.7.7", "2.8.0.Beta1", "2.8.0-SNAPSHOT")//List("2.7.2", "2.7.3", "2.7.4", "2.7.5", "2.8.0-SNAPSHOT") + def allVersions = List("2.8.0")//List("2.7.2", "2.7.5", "2.7.7", "2.8.0", "2.8.1.RC1") /** Tests running the compiler interface with the analyzer plugin with a test callback. The test callback saves all information * that the plugin sends it for post-compile analysis by the provided function.*/ - def apply[T](scalaVersion: String, sources: Set[File], outputDirectory: File, options: Seq[String], superclassNames: Seq[String], annotationNames: Seq[String]) - (f: (TestCallback, ScalaInstance, CompileLogger) => T): T = + def apply[T](scalaVersion: String, sources: Seq[File], outputDirectory: File, options: Seq[String]) + (f: (TestCallback, ScalaInstance, Logger) => T): T = { - val testCallback = new TestCallback(superclassNames.toArray, annotationNames.toArray) + val testCallback = new TestCallback WithCompiler(scalaVersion) { (compiler, log) => - compiler(sources, Set.empty, outputDirectory, options, testCallback, 5, log) + compiler(sources, Nil, outputDirectory, options, testCallback, 5, log) f(testCallback, compiler.scalaInstance, log) } } /** Tests running the compiler interface with the analyzer plugin. The provided function is given a ClassLoader that can * load the compiled classes..*/ def apply[T](scalaVersion: String, sources: Seq[File])(f: ClassLoader => T): T = - CallbackTest.apply(scalaVersion, sources, Nil, Nil){ case (_, outputDir, _, _) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) } + CallbackTest.full(scalaVersion, sources){ case (_, outputDir, _, _) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) } } object CallbackTest { - def apply[T](scalaVersion: String, sources: Iterable[File])(f: TestCallback => T): T = - apply(scalaVersion, sources.toSeq, Nil, Nil){ case (callback, _, _, _) => f(callback) } - def apply[T](scalaVersion: String, sources: Seq[File], superclassNames: Seq[String], annotationNames: Seq[String])(f: (TestCallback, File, ScalaInstance, CompileLogger) => T): T = + def simple[T](scalaVersion: String, sources: Seq[File])(f: TestCallback => T): T = + full(scalaVersion, sources){ case (callback, _, _, _) => f(callback) } + def full[T](scalaVersion: String, sources: Seq[File])(f: (TestCallback, File, ScalaInstance, Logger) => T): T = withTemporaryDirectory { outputDir => - TestCompile(scalaVersion, Set() ++ sources, outputDir, Nil, superclassNames, annotationNames) { case (callback, instance, log) => f(callback, outputDir, instance, log) } + TestCompile(scalaVersion, sources, outputDir, Nil) { case (callback, instance, log) => f(callback, outputDir, instance, log) } } } \ No newline at end of file diff --git a/compile/src/test/scala/WithCompiler.scala b/compile/src/test/scala/WithCompiler.scala new file mode 100644 index 000000000..3e5a5bfe4 --- /dev/null +++ b/compile/src/test/scala/WithCompiler.scala @@ -0,0 +1,39 @@ +package sbt +package compile + + import xsbt.boot + import java.io.File + import IO.withTemporaryDirectory + +object WithCompiler +{ + def apply[T](scalaVersion: String)(f: (AnalyzingCompiler, Logger) => T): T = + { + launcher { (launch, log) => + withTemporaryDirectory { componentDirectory => + val manager = new ComponentManager(xsbt.boot.Locks, new boot.ComponentProvider(componentDirectory), log) + val compiler = new AnalyzingCompiler(ScalaInstance(scalaVersion, launch), manager, log) + compiler.newComponentCompiler(log).clearCache(ComponentCompiler.compilerInterfaceID) + define(manager, ComponentCompiler.compilerInterfaceSrcID, getResource("CompilerInterface.scala"), getClassResource(classOf[jline.Completor])) + define(manager, ComponentCompiler.xsbtiID, getClassResource(classOf[xsbti.AnalysisCallback])) + f(compiler, log) + } + } + } + def launcher[T](f: (xsbti.Launcher, Logger) => T): T = + TestLogger { log => + boot.LaunchTest.withLauncher { launch => f(launch, log) } + } + + def getClassResource(resource: Class[_]): File = IO.classLocationFile(resource) + def getResource(resource: String): File = + { + val src = getClass.getClassLoader.getResource(resource) + if(src ne null) IO.asFile(src) else error("Resource not found: " + resource) + } + def define(manager: ComponentManager, id: String, files: File*) + { + manager.clearCache(id) + manager.define(id, files) + } +} \ No newline at end of file diff --git a/interface/src/test/scala/TestCallback.scala b/interface/src/test/scala/TestCallback.scala index 75e8d77af..d554a9a08 100644 --- a/interface/src/test/scala/TestCallback.scala +++ b/interface/src/test/scala/TestCallback.scala @@ -1,34 +1,23 @@ package xsbti -import java.io.File -import scala.collection.mutable.ArrayBuffer + import java.io.File + import scala.collection.mutable.ArrayBuffer -class TestCallback(val superclassNames: Array[String], val annotationNames: Array[String]) extends AnalysisCallback +class TestCallback extends AnalysisCallback { - val invalidSuperclasses = new ArrayBuffer[String] val beganSources = new ArrayBuffer[File] val endedSources = new ArrayBuffer[File] - val foundSubclasses = new ArrayBuffer[(File, String, String, Boolean)] - val foundAnnotated = new ArrayBuffer[(File, String, String, Boolean)] val sourceDependencies = new ArrayBuffer[(File, File)] - val jarDependencies = new ArrayBuffer[(File, File)] - val classDependencies = new ArrayBuffer[(File, File)] - val productDependencies = new ArrayBuffer[(File, File)] + val binaryDependencies = new ArrayBuffer[(File, String, File)] val products = new ArrayBuffer[(File, File)] - val applications = new ArrayBuffer[(File, String)] + val apis = new ArrayBuffer[(File, xsbti.api.Source)] - def superclassNotFound(superclassName: String) { invalidSuperclasses += superclassName } def beginSource(source: File) { beganSources += source } - def foundSubclass(source: File, subclassName: String, superclassName: String, isModule: Boolean): Unit = - foundSubclasses += ((source, subclassName, superclassName, isModule)) - def foundAnnotated(source: File, className: String, annotationName: String, isModule: Boolean): Unit = - foundAnnotated += ((source, className, annotationName, isModule)) + def sourceDependency(dependsOn: File, source: File) { sourceDependencies += ((dependsOn, source)) } - def jarDependency(jar: File, source: File) { jarDependencies += ((jar, source)) } - def classDependency(clazz: File, source: File) { classDependencies += ((clazz, source)) } - def productDependency(clazz: File, source: File) { productDependencies += ((clazz, source)) } + def binaryDependency(binary: File, name: String, source: File) { binaryDependencies += ((binary, name, source)) } def generatedClass(source: File, module: File) { products += ((source, module)) } def endSource(source: File) { endedSources += source } - def foundApplication(source: File, className: String) { applications += ((source, className)) } - def api(source: File, sourceAPI: xsbti.api.Source) = () + + def api(source: File, sourceAPI: xsbti.api.Source) { apis += ((source, sourceAPI)) } } \ No newline at end of file diff --git a/ivy/src/test/scala/ComponentManagerTest.scala b/ivy/src/test/scala/ComponentManagerTest.scala index 8ad4c0999..537361fcb 100644 --- a/ivy/src/test/scala/ComponentManagerTest.scala +++ b/ivy/src/test/scala/ComponentManagerTest.scala @@ -77,5 +77,5 @@ object ComponentManagerTest extends Specification private def writeRandomContent(file: File) = IO.write(file, randomString) private def randomString = "asdf" private def withManager[T](f: ComponentManager => T): T = - TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp), logger)) } ) + TestLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(xsbt.boot.Locks, new xsbt.boot.ComponentProvider(temp), logger)) } ) } \ No newline at end of file diff --git a/ivy/src/test/scala/TestIvyLogger.scala b/ivy/src/test/scala/TestIvyLogger.scala deleted file mode 100644 index c173b43fd..000000000 --- a/ivy/src/test/scala/TestIvyLogger.scala +++ /dev/null @@ -1,12 +0,0 @@ -package sbt - -class TestIvyLogger extends BufferedLogger(new ConsoleLogger) with IvyLogger -object TestIvyLogger -{ - def apply[T](f: IvyLogger => T): T = - { - val log = new TestIvyLogger - log.setLevel(Level.Debug) - log.bufferQuietly(f(log)) - } -} \ No newline at end of file diff --git a/main/DefaultProject.scala b/main/DefaultProject.scala index c7d12102b..5a9a8e432 100644 --- a/main/DefaultProject.scala +++ b/main/DefaultProject.scala @@ -4,7 +4,8 @@ package sbt import std._ - import inc.{Analysis,Discovered,Discovery} + import compile.{Discovered,Discovery} + import inc.Analysis import TaskExtra._ import Configurations.{Compile => CompileConfig, Test => TestConfig, Runtime => RunConfig} import ClasspathProject._ diff --git a/main/build/Build.scala b/main/build/Build.scala index 4cc065c0e..9384eb44c 100644 --- a/main/build/Build.scala +++ b/main/build/Build.scala @@ -7,7 +7,7 @@ package build import java.io.File import classpath.ClasspathUtilities.toLoader import ModuleUtilities.getObject -import compile.{AnalyzingCompiler, JavaCompiler} +import compile.{AnalyzingCompiler, Discovery, JavaCompiler} import inc.Analysis import Path._ import GlobFilter._ @@ -91,14 +91,14 @@ object Build import Auto.{Annotation, Explicit, Subclass} auto match { case Explicit => if(name.isEmpty) error("No name specified to load explicitly.") else Seq(new ToLoad(name)) - case Subclass => discover(analysis, module, new inc.Discovery(Set(name), Set.empty)) - case Annotation => discover(analysis, module, new inc.Discovery(Set.empty, Set(name))) + case Subclass => discover(analysis, module, new Discovery(Set(name), Set.empty)) + case Annotation => discover(analysis, module, new Discovery(Set.empty, Set(name))) } } def discover(analysis: inc.Analysis, command: DiscoverCommand): Seq[ToLoad] = discover(analysis, command.module, command.discovery) - def discover(analysis: inc.Analysis, module: Option[Boolean], discovery: inc.Discovery): Seq[ToLoad] = + def discover(analysis: inc.Analysis, module: Option[Boolean], discovery: Discovery): Seq[ToLoad] = { for(src <- analysis.apis.internal.values.toSeq; (df, found) <- discovery(src.definitions) if !found.isEmpty && moduleMatches(found.isModule, module)) diff --git a/main/build/LoadCommand.scala b/main/build/LoadCommand.scala index 6caee4c65..d8eac29d3 100644 --- a/main/build/LoadCommand.scala +++ b/main/build/LoadCommand.scala @@ -4,7 +4,8 @@ package sbt package build -import java.io.File + import java.io.File + import compile.Discovery sealed trait LoadCommand final case class BinaryLoad(classpath: Seq[File], module: Boolean, name: String) extends LoadCommand @@ -17,7 +18,7 @@ object Auto extends Enumeration } final case class CompileCommand(classpath: Seq[File], sources: Seq[File], output: Option[File], options: Seq[String]) -final case class DiscoverCommand(module: Option[Boolean], discovery: inc.Discovery) +final case class DiscoverCommand(module: Option[Boolean], discovery: Discovery) final case class ToLoad(name: String, isModule: Boolean = false) { diff --git a/main/build/Parse.scala b/main/build/Parse.scala index cc380f82d..f38cadb3b 100644 --- a/main/build/Parse.scala +++ b/main/build/Parse.scala @@ -4,7 +4,8 @@ package sbt package build -import java.io.File + import compile.Discovery + import java.io.File final class ParseException(msg: String) extends RuntimeException(msg) @@ -72,7 +73,7 @@ The command has the following syntax: val args = arguments(commandString) val subs = names("sub", args) val annots = names("annot", args) - DiscoverCommand(module(args), new inc.Discovery(subs, annots)) + DiscoverCommand(module(args), new Discovery(subs, annots)) } def auto(args: Seq[String]): Auto.Value = diff --git a/project/build/XSbt.scala b/project/build/XSbt.scala index ccfb7c3fb..df744267d 100644 --- a/project/build/XSbt.scala +++ b/project/build/XSbt.scala @@ -66,13 +66,14 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths // Implements the core functionality of detecting and propagating changes incrementally. // Defines the data structures for representing file fingerprints and relationships and the overall source analysis val compileIncrementalSub = testedBase(compilePath / "inc", "Incremental Compiler", collectionSub, apiSub, ioSub) - // Searches the source API data structures, currently looks for subclasses and annotations - val discoverySub = testedBase(compilePath / "discover", "Discovery", compileIncrementalSub, apiSub) // Persists the incremental data structures using SBinary val compilePersistSub = project(compilePath / "persist", "Persist", new PersistProject(_), compileIncrementalSub, apiSub) // sbt-side interface to compiler. Calls compiler-side interface reflectively val compilerSub = project(compilePath, "Compile", new CompileProject(_), launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub, logSub) + // Searches the source API data structures, currently looks for subclasses and annotations + val discoverySub = project(compilePath / "discover", "Discovery", new DiscoveryProject(_), compileIncrementalSub, apiSub) + // mostly for implementing 'load' command, could perhaps be trimmed and merged into 'main' val buildSub = baseProject("main" / "build", "Project Builder", @@ -216,6 +217,7 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths { val testInterface = "org.scala-tools.testing" % "test-interface" % "0.5" } + class DiscoveryProject(info: ProjectInfo) extends TestedBase(info) with TestWithCompile class CompileProject(info: ProjectInfo) extends Base(info) with TestWithLog with TestWithLaunch { override def testCompileAction = super.testCompileAction dependsOn(compileInterfaceSub.`package`, interfaceSub.`package`) @@ -322,6 +324,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths override def projectClasspath(config: Configuration) = Path.emptyPathFinder } } + trait TestWithCompile extends TestWith { + override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(compilerSub) + } trait TestWithIO extends TestWith { override def testWithTestClasspath = super.testWithTestClasspath ++ Seq(ioSub) } diff --git a/util/log/src/test/scala/TestLogger.scala b/util/log/src/test/scala/TestLogger.scala new file mode 100644 index 000000000..edf2b00dd --- /dev/null +++ b/util/log/src/test/scala/TestLogger.scala @@ -0,0 +1,11 @@ +package sbt + +object TestLogger +{ + def apply[T](f: Logger => T): T = + { + val log = new BufferedLogger(ConsoleLogger()) + log.setLevel(Level.Debug) + log.bufferQuietly(f(log)) + } +} \ No newline at end of file