drop compilation tests

1. overlapped with integration tests
2. slower than integration tests
3. more fragile than integration tests
This commit is contained in:
Mark Harrah 2012-07-01 15:16:41 -04:00
parent e2b5ce374c
commit 3f12f2eb9f
8 changed files with 0 additions and 531 deletions

View File

@ -1,142 +0,0 @@
package xsbt.api
import java.io.File
import java.net.URLClassLoader
import org.specs.Specification
import sbt.WithFiles
import sbt.compiler.{CallbackTest,TestCompile}
/** 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.*/
object ApplicationsTest extends Specification
{
val sourceContent =
"""
object Main { def main(args: Array[String]) {} }
""" :: """
class Main2 { def main(args: Array[String]) {} }
""" :: """
object Main3 { private def main(args: Array[String]) {} }
private object Main3b extends Main2
object Main3c { private def main(args: Array[String]) {} }
protected object Main3d { def main(args: Array[String]) {} }
object Main3e {
protected def main(args: Array[String]) {}
}
package a {
object Main3f { private[a] def main(args: Array[String]) {} }
object Main3g { protected[a] def main(args: Array[String]) {} }
}
""" ::"""
object Main4 extends Main2
""" :: """
trait Main5 { def main(args: Array[String]) {} }; trait Main5b extends Main5; trait Main5c extends Main2; abstract class Main5d { def main(args: Array[String]) {} };
trait Main5e[T] { def main(args: Array[T]) {} }
trait Main5f[T <: String] { def main(args: Array[T]) {} }
""" :: """
object Main6a { var main = () }
object Main6b { var main = (args: Array[String]) => () }
""" :: """
object Main7 { object Main7b extends Main2 }
""" :: """
object Main8 extends Main2 { object Main7b extends Main2 }
""" :: """
object Main9 {
def main() {}
def main(i: Int) {}
def main(args: Array[String]) {}
}
""" :: """
object MainA {
def main() {}
def main(i: Int) {}
def main(args: Array[String], other: String) {}
def main(i: Array[Int]) {}
}
object MainA2 {
def main[T](args: Array[T]) {}
}
""" :: """
object MainB extends Main2 {
def main() {}
def main(i: Int) {}
}
""" :: """
object MainC1 {
def main(args: Array[String]) = 3
}
object MainC2 {
def main1(args: Array[String]) {}
}
""" :: """
object MainD1 {
val main = ()
}
object MainD2 {
val main = (args: Array[String]) => ()
}
""" :: """
object MainE1 {
type T = String
def main(args: Array[T]) {}
}
object MainE2 {
type AT = Array[String]
def main(args: AT) {}
}
object MainE3 {
type U = Unit
type T = String
def main(args: Array[T]): U = ()
}
object MainE4 {
def main[T](args: Array[String]) {}
}
object MainE5 {
type A[T] = Array[String]
def main[T](args: A[T]) {}
}
object MainE6 extends Main5e[String]
object MainE7 extends Main5e[Int]
object MainE8 extends Main5f[String]
""" :: """
object MainF1 extends Application { var x = 3; x = 5 }
object MainF2 { def main(args: Array[java.lang.String]) {} }
trait MainF3 { def main(args: Array[String]) {} }
object MainF4 extends MainF3 { }
""" ::
Nil
val sources = for((source, index) <- sourceContent.zipWithIndex) yield new File("Main" + (index+1) + ".scala") -> source
"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.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", mainE -> "MainE6", mainE -> "MainE8",
mainF -> "MainF1", mainF -> "MainF2", mainF -> "MainF4")
// the source signature is valid for the following, but the binary signature is not, so they can't actually be run.
// these are then known discrepancies between detected and actual entry points
val erased = Set( mainE -> "MainE6", mainE -> "MainE8" )
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 filterNot erased) testRun(loader, className)
}
}
}
def applications(callback: xsbti.TestCallback): Seq[(File, String)] =
for( (file, api) <- CallbackTest.apis(callback);
x = println("\n" + file + ":\n" + (api.definitions.flatMap { case c: xsbti.api.ClassLike => c.structure.inherited.filter(_.name == "main"); case _ => Nil }).map(xsbt.api.DefaultShowAPI.apply).mkString("\n"));
application <- applications(api))
yield (file, application)
def applications(src: xsbti.api.SourceAPI): 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)
obj.getMethod("main", classOf[Array[String]]).invoke(null, new Array[String](0))
}
}

View File

@ -1,68 +0,0 @@
package sbt
package compiler
import java.io.File
import org.specs.Specification
object CheckBasicAPI extends Specification
{
val basicName = new File("Basic.scala")
val basicSource =
"""
package org.example {
trait Parent[A] { def x: A }
abstract class Child extends Parent[String]
class S {
def x = new { def y = new S { def z = new Child { def x = "asdf" } } }
final val xconst = 3
private[this] def zz: Seq[_] = Nil
type L[M[_], P >: Int <: Int] = M[P]
type Q = L[Option, Int]
}
object Basic
trait Out {
trait In
def t: In
}
}
package org.example3 {
trait A extends Iterator[Int]
}
package org.example2 {
trait ZZ[S] {
val p: S
val q: ZZZ[S] { val what: S }
class A extends ZZZ[S] {
val p = error("")
val q = error("")
}
}
trait ZZZ[T] extends ZZ[List[T]]
trait ZZZZ extends ZZZ[Int]
trait Z extends ZZZZ
trait P[S] {
trait Q
def x(s: S): S
def q(v: Q): Q
val u: Q
val p: S
}
trait Out { self: P[String] =>
trait In
def z = p
def t(i: In): In
def y(q: Q): Q
}
}
"""
"Compiling should succeed" in {
WithFiles(basicName -> basicSource){ files =>
for(scalaVersion <- TestCompile.allVersions)
{
TestCompile(scalaVersion, files){ loader => Class.forName("org.example.Basic", false, loader) }
true must beTrue // don't know how to just check that previous line completes without exception
}
}
}
}

View File

@ -1,43 +0,0 @@
package sbt
package compiler
import java.io.File
import org.specs.Specification
object CheckBasic extends Specification
{
val basicName = new File("Basic.scala")
val basicSource = "package org.example { /** A comment */ object Basic }"
"Compiling basic file should succeed" in {
WithFiles(basicName -> basicSource){ files =>
for(scalaVersion <- TestCompile.allVersions)
{
TestCompile(scalaVersion, files){ loader => Class.forName("org.example.Basic", false, loader) }
true must beTrue // don't know how to just check that previous line completes without exception
}
}
}
"Scaladoc on basic file should succeed" in {
WithFiles(basicName -> basicSource){ files =>
for(scalaVersion <- TestCompile.allVersions)
{
IO.withTemporaryDirectory { outputDirectory =>
WithCompiler(scalaVersion) { (compiler, 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
}
}
}
"Analyzer plugin should send source begin and end" in {
WithFiles(basicName -> basicSource) { files =>
for(scalaVersion <- TestCompile.allVersions)
CallbackTest.simple(scalaVersion, files) { callback =>
(callback.beganSources) must haveTheSameElementsAs(files)
(callback.endedSources) must haveTheSameElementsAs(files)
}
}
}
}

View File

@ -1,97 +0,0 @@
package sbt
package compiler
import java.io.File
import IO.withTemporaryDirectory
import org.specs._
object CompileTest extends Specification
{
"Analysis compiler" should {
"compile basic sources" in {
WithCompiler( "2.9.1" )(testCompileAnalysis)
WithCompiler( "2.9.0-1" )(testCompileAnalysis)
WithCompiler( "2.8.0" )(testCompileAnalysis)
WithCompiler( "2.8.1" )(testCompileAnalysis)
}
}
"Raw compiler" should {
"Properly handle classpaths" in {
testClasspath("2.9.1")
testClasspath("2.9.0-1")
testClasspath("2.8.1")
testClasspath("2.8.0")
}
}
private def testCompileAnalysis(compiler: AnalyzingCompiler, log: Logger)
{
WithFiles( new File("Test.scala") -> "object Test" ) { sources =>
withTemporaryDirectory { temp =>
val callback = new xsbti.TestCallback
compiler(sources, noChanges, Nil, temp, Nil, callback, 10, CompilerCache.fresh, log)
(callback.beganSources) must haveTheSameElementsAs(sources)
}
}
}
val noChanges = new xsbti.compile.DependencyChanges {
def isEmpty = true
def modifiedBinaries = Array()
def modifiedClasses = Array()
}
val UsingCompiler = "object Test { classOf[scala.tools.nsc.Global] }"
private def shouldFail(act: => Unit) =
{
val success = try { act; true } catch { case t if expectedException(t) => false }
if(success) error("Expected exception not thrown")
}
private def expectedException(t: Throwable) =
t match
{
case e: Exception => true
case t if isMissingRequirementError(t) => true
case _ => false
}
private def shouldSucceed(act: => Unit) =
try { act } catch { case c: xsbti.CompileFailed => error(c.toString) }
private def isMissingRequirementError(t: Throwable) = t.getClass.getName == "scala.tools.nsc.MissingRequirementError"
private def testClasspath(scalaVersion: String) =
WithCompiler.launcher { (launch, log) =>
def compiler(bootLibrary: Boolean, compilerOnClasspath: Boolean): RawCompiler =
new RawCompiler(ScalaInstance(scalaVersion, launch), new ClasspathOptions(bootLibrary, compilerOnClasspath, true, true, bootLibrary), log)
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(true) :: Nil
val withCompiler = noCompiler.scalaInstance.compilerJar :: Nil
val withLibrary = noCompiler.scalaInstance.libraryJar :: Nil
val withLibraryCompiler = withLibrary ++ withCompiler
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 =>
shouldSucceed( standard(plainSrcs, Nil, out, Nil) )
shouldSucceed( standard(compSrcs, Nil, out, Nil) )
shouldSucceed( noCompiler(plainSrcs, Nil, out, Nil) )
shouldFail( noCompiler(compSrcs, Nil, out, Nil) )
shouldSucceed( noCompiler(compSrcs, withCompiler, out, Nil) )
shouldFail( fullExplicit(plainSrcs, Nil, out, Nil) )
shouldFail( fullExplicit(compSrcs, Nil, out, Nil) )
shouldSucceed( fullExplicit(plainSrcs, withLibrary, out, fullBoot) )
shouldSucceed( fullExplicit(compSrcs, withLibraryCompiler, out, fullBoot) )
true must beTrue
}
}
}
}

View File

@ -1,61 +0,0 @@
package xsbt.api
import sbt.WithFiles
import sbt.compiler.{CallbackTest,TestCompile}
import java.io.File
import org.specs.Specification
object DetectAnnotations extends Specification
{
val sources =
("c/A.scala" -> "package c; class A(x: Int, y: Int) extends Annotation") ::
("B.scala" -> "class B extends Annotation") ::
("d/C.scala" -> "package d; class C extends Annotation") ::
("a/Super1.scala" -> "package a; trait Super1") ::
("a/Super2.scala" -> "package a; @c.A(3,4) trait Super2") ::
("Super3.scala" -> "@B class Super3") ::
("a/Super4.scala" -> "package a; trait Super4 { @d.C def test = () }") ::
("b/Middle.scala" -> "package y.w; trait Mid extends a.Super2") ::
("b/Sub1.scala" -> "class Sub1 extends Super3 with y.w.Mid") ::
("b/Sub2.scala" -> "final class Sub2 extends a.Super1") ::
("b/Sub3.scala" -> "@B @c.A(3,4) final class Sub3 extends a.Super1") ::
("d/Sub4.scala" -> "@B private class Sub4 extends a.Super1") ::
("d/Sub5.scala" -> "@B protected class Sub5 extends a.Super1") ::
("d/Sub6.scala" -> "@B abstract class Sub6 extends a.Super1") ::
("d/Sub7.scala" -> "class Sub7 extends a.Super4") ::
("d/Sub8.scala" -> "class Sub8 { @c.A(5,6) def test(s: Int) = s }") ::
("d/Sub9.scala" -> "object Sub9 { @B def test(s: String) = s }") ::
("d/SubA.scala" -> "object SubA { @c.A(3,3) def test = () }\nclass SubA { @B private def test = 6 }") ::
("d/SubB.scala" -> "object SubB { @c.A(3,3) def test = 3 }\nclass SubB { @d.C def test = () }") ::
Nil
"Analysis plugin should detect annotations" in {
WithFiles(sources.map{case (file, content) => (new File(file), content)} : _*)
{
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.simple(scalaVersion, files) { callback =>
val expected =
(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
val actual = subclasses(callback).toSet
val actualOnly = (actual -- expected)
val expectedOnly = (expected.toSet -- actual)
expectedOnly must beEmpty
actualOnly must beEmpty
}
}
}
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
for( (file, src) <- CallbackTest.apis(callback); (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")
}

View File

@ -1,43 +0,0 @@
package xsbt.api
import sbt.WithFiles
import sbt.compiler.{CallbackTest,TestCompile}
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
actualOnly must beEmpty
expectedOnly must beEmpty
}
}
}
def subclasses(callback: xsbti.TestCallback): Seq[(File, String, Set[String], Boolean)] =
for( (file, src) <- CallbackTest.apis(callback); (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")
}

View File

@ -1,38 +0,0 @@
package sbt
package compiler
import java.io.File
import java.net.URLClassLoader
import xsbti.TestCallback
import IO.withTemporaryDirectory
object TestCompile
{
def allVersions = List("2.8.1", "2.9.0-1", "2.8.0", "2.9.1")
/** 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: Seq[File], outputDirectory: File, options: Seq[String])
(f: (TestCallback, xsbti.compile.ScalaInstance, Logger) => T): T =
{
val testCallback = new TestCallback
WithCompiler(scalaVersion) { (compiler, log) =>
compiler(sources, CompileTest.noChanges, Nil, outputDirectory, options, testCallback, 5, CompilerCache.fresh, 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.full(scalaVersion, sources){ case (_, outputDir, _, _) => f(new URLClassLoader(Array(outputDir.toURI.toURL))) }
}
object CallbackTest
{
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, xsbti.compile.ScalaInstance, Logger) => T): T =
withTemporaryDirectory { outputDir =>
TestCompile(scalaVersion, sources, outputDir, Nil) { case (callback, instance, log) => f(callback, outputDir, instance, log) }
}
def apis(callback: xsbti.TestCallback) =
callback.apis.toSeq map { case (src, api) => (src, xsbt.api.APIUtil.minimize(api)) }
}

View File

@ -1,39 +0,0 @@
package sbt
package compiler
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, true), None, 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)
}
}