diff --git a/main/actions/src/main/scala/sbt/ForkTests.scala b/main/actions/src/main/scala/sbt/ForkTests.scala index 3be3ad60d..c18b76283 100755 --- a/main/actions/src/main/scala/sbt/ForkTests.scala +++ b/main/actions/src/main/scala/sbt/ForkTests.scala @@ -4,7 +4,7 @@ package sbt import scala.collection.mutable -import org.scalatools.testing._ +import testing._ import java.net.ServerSocket import java.io._ import Tests.{Output => TestOutput, _} diff --git a/main/actions/src/main/scala/sbt/Tests.scala b/main/actions/src/main/scala/sbt/Tests.scala index c6549e4e2..3d1c6bcdc 100644 --- a/main/actions/src/main/scala/sbt/Tests.scala +++ b/main/actions/src/main/scala/sbt/Tests.scala @@ -11,7 +11,7 @@ package sbt import xsbti.api.Definition import ConcurrentRestrictions.Tag - import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint} + import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint} import java.io.File @@ -145,12 +145,12 @@ object Tests def overall(results: Iterable[TestResult.Value]): TestResult.Value = (TestResult.Passed /: results) { (acc, result) => if(acc.id < result.id) result else acc } def discover(frameworks: Seq[Framework], analysis: Analysis, log: Logger): (Seq[TestDefinition], Set[String]) = - discover(frameworks flatMap TestFramework.getTests, allDefs(analysis), log) + discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log) def allDefs(analysis: Analysis) = analysis.apis.internal.values.flatMap(_.api.definitions).toSeq def discover(fingerprints: Seq[Fingerprint], definitions: Seq[Definition], log: Logger): (Seq[TestDefinition], Set[String]) = { - val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superClassName, sub.isModule, sub) }; + val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub) }; val annotations = fingerprints collect { case ann: AnnotatedFingerprint => (ann.annotationName, ann.isModule, ann) }; log.debug("Subclass fingerprints: " + subclasses) log.debug("Annotation fingerprints: " + annotations) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 257016892..990adf54e 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -15,7 +15,7 @@ package sbt import complete._ import std.TaskExtra._ import inc.{FileValueCache, Locate} - import org.scalatools.testing.{Framework, AnnotatedFingerprint, SubclassFingerprint} + import testing.{Framework, AnnotatedFingerprint, SubclassFingerprint} import sys.error import scala.xml.NodeSeq diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 3cdbf4395..fa0e04ec9 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -14,7 +14,7 @@ package sbt import scala.xml.{Node => XNode, NodeSeq} import org.apache.ivy.core.module.{descriptor, id} import descriptor.ModuleDescriptor, id.ModuleRevisionId - import org.scalatools.testing.Framework + import testing.Framework import Configurations.CompilerPlugin import Types.Id import KeyRanks._ diff --git a/sbt/src/sbt-test/tests/do-not-discover/build.sbt b/sbt/src/sbt-test/tests/do-not-discover/build.sbt new file mode 100644 index 000000000..2e8f50f5b --- /dev/null +++ b/sbt/src/sbt-test/tests/do-not-discover/build.sbt @@ -0,0 +1,5 @@ +scalaVersion := "2.10.1" + +libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15" + +testOptions in Test += Tests.Argument("-r", "custom.CustomReporter") \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/do-not-discover/src/main/scala/custom/CustomReporter.scala b/sbt/src/sbt-test/tests/do-not-discover/src/main/scala/custom/CustomReporter.scala new file mode 100644 index 000000000..f21595450 --- /dev/null +++ b/sbt/src/sbt-test/tests/do-not-discover/src/main/scala/custom/CustomReporter.scala @@ -0,0 +1,30 @@ +package custom + +import java.io._ +import org.scalatest._ +import events._ + +class CustomReporter extends Reporter { + + private def writeFile(filePath: String, content: String) { + val file = new File(filePath) + val writer = + if (!file.exists) + new FileWriter(new File(filePath)) + else + new FileWriter(new File(filePath + "-2")) + writer.write(content) + writer.flush() + writer.close() + } + + def apply(event: Event) { + event match { + case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName) + case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName) + case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName) + case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName) + case _ => + } + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec.scala b/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec.scala new file mode 100644 index 000000000..57ec51bdf --- /dev/null +++ b/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec.scala @@ -0,0 +1,12 @@ +package com.test + +import org.scalatest.Spec + +class TestSpec extends Spec { + + def `TestSpec-test-1 ` {} + + def `TestSpec-test-2 ` {} + + def `TestSpec-test-3 ` {} +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec2.scala b/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec2.scala new file mode 100644 index 000000000..b3e2f6bd2 --- /dev/null +++ b/sbt/src/sbt-test/tests/do-not-discover/src/test/scala/com/test/TestSpec2.scala @@ -0,0 +1,13 @@ +package com.test + +import org.scalatest._ + +@DoNotDiscover +class TestSpec2 extends Spec { + + def `TestSpec2-test-1 ` {} + + def `TestSpec2-test-2 ` {} + + def `TestSpec2-test-3 ` {} +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/do-not-discover/test b/sbt/src/sbt-test/tests/do-not-discover/test new file mode 100644 index 000000000..82b725387 --- /dev/null +++ b/sbt/src/sbt-test/tests/do-not-discover/test @@ -0,0 +1,72 @@ +#Test the ScalaTest framework to exclude suite annotated with @DoNotDiscover (TestSpec2) properly. +#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/, +#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected), +#a xxxx-2 file will be written, thus here we also check for 'absent' of such file. + +> clean + +> test + +$ exists target/SuiteStarting-TestSpec + +$ absent target/SuiteStarting-TestSpec-2 + +$ exists target/SuiteCompleted-TestSpec + +$ absent target/SuiteCompleted-TestSpec-2 + +$ absent target/SuiteStarting-TestSpec2 + +$ absent target/SuiteStarting-TestSpec2-2 + +$ absent target/SuiteCompleted-TestSpec2 + +$ absent target/SuiteCompleted-TestSpec2-2 + +$ exists target/TestStarting-TestSpec-test-1 + +$ absent target/TestStarting-TestSpec-test-1-2 + +$ exists target/TestSucceeded-TestSpec-test-1 + +$ absent target/TestSucceeded-TestSpec-test-1-2 + +$ exists target/TestStarting-TestSpec-test-2 + +$ absent target/TestStarting-TestSpec-test-2-2 + +$ exists target/TestSucceeded-TestSpec-test-2 + +$ absent target/TestSucceeded-TestSpec-test-2-2 + +$ exists target/TestStarting-TestSpec-test-3 + +$ absent target/TestStarting-TestSpec-test-3-2 + +$ exists target/TestSucceeded-TestSpec-test-3 + +$ absent target/TestSucceeded-TestSpec-test-3-2 + +$ absent target/TestStarting-TestSpec2-test-1 + +$ absent target/TestStarting-TestSpec2-test-1-2 + +$ absent target/TestSucceeded-TestSpec2-test-1 + +$ absent target/TestSucceeded-TestSpec2-test-1-2 + +$ absent target/TestStarting-TestSpec2-test-2 + +$ absent target/TestStarting-TestSpec2-test-2-2 + +$ absent target/TestSucceeded-TestSpec2-test-2 + +$ absent target/TestSucceeded-TestSpec2-test-2-2 + +$ absent target/TestStarting-TestSpec2-test-3 + +$ absent target/TestStarting-TestSpec2-test-3-2 + +$ absent target/TestSucceeded-TestSpec2-test-3 + +$ absent target/TestSucceeded-TestSpec2-test-3-2 \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/task/build.sbt b/sbt/src/sbt-test/tests/task/build.sbt new file mode 100644 index 000000000..2e8f50f5b --- /dev/null +++ b/sbt/src/sbt-test/tests/task/build.sbt @@ -0,0 +1,5 @@ +scalaVersion := "2.10.1" + +libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15" + +testOptions in Test += Tests.Argument("-r", "custom.CustomReporter") \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/task/src/main/scala/custom/CustomReporter.scala b/sbt/src/sbt-test/tests/task/src/main/scala/custom/CustomReporter.scala new file mode 100644 index 000000000..f21595450 --- /dev/null +++ b/sbt/src/sbt-test/tests/task/src/main/scala/custom/CustomReporter.scala @@ -0,0 +1,30 @@ +package custom + +import java.io._ +import org.scalatest._ +import events._ + +class CustomReporter extends Reporter { + + private def writeFile(filePath: String, content: String) { + val file = new File(filePath) + val writer = + if (!file.exists) + new FileWriter(new File(filePath)) + else + new FileWriter(new File(filePath + "-2")) + writer.write(content) + writer.flush() + writer.close() + } + + def apply(event: Event) { + event match { + case SuiteStarting(_, suiteName, _, _, _, _, _, _, _, _) => writeFile("target/SuiteStarting-" + suiteName, suiteName) + case SuiteCompleted(_, suiteName, _, _, _, _, _, _, _, _, _) => writeFile("target/SuiteCompleted-" + suiteName, suiteName) + case TestStarting(_, _, _, _, testName, _, _, _, _, _, _, _) => writeFile("target/TestStarting-" + testName, testName) + case TestSucceeded(_, _, _, _, testName, _, _, _, _, _, _, _, _, _) => writeFile("target/TestSucceeded-" + testName, testName) + case _ => + } + } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec.scala b/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec.scala new file mode 100644 index 000000000..57ec51bdf --- /dev/null +++ b/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec.scala @@ -0,0 +1,12 @@ +package com.test + +import org.scalatest.Spec + +class TestSpec extends Spec { + + def `TestSpec-test-1 ` {} + + def `TestSpec-test-2 ` {} + + def `TestSpec-test-3 ` {} +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec2.scala b/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec2.scala new file mode 100644 index 000000000..ce747f260 --- /dev/null +++ b/sbt/src/sbt-test/tests/task/src/test/scala/com/test/TestSpec2.scala @@ -0,0 +1,12 @@ +package com.test + +import org.scalatest.Spec + +class TestSpec2 extends Spec { + + def `TestSpec2-test-1 ` {} + + def `TestSpec2-test-2 ` {} + + def `TestSpec2-test-3 ` {} +} \ No newline at end of file diff --git a/sbt/src/sbt-test/tests/task/test b/sbt/src/sbt-test/tests/task/test new file mode 100644 index 000000000..9c84af088 --- /dev/null +++ b/sbt/src/sbt-test/tests/task/test @@ -0,0 +1,72 @@ +#This test that the framework will execute ScalaTest suites as task properly. +#A CustomReporter is used to report expected ScalaTest's events by writing out to files in target/, +#it is then used to check for their existence, and if the expected event is fired > 1 (which is unexpected), +#a xxxx-2 file will be written, thus here we also check for 'absent' of such file. + +> clean + +> test + +$ exists target/SuiteStarting-TestSpec + +$ absent target/SuiteStarting-TestSpec-2 + +$ exists target/SuiteCompleted-TestSpec + +$ absent target/SuiteCompleted-TestSpec-2 + +$ exists target/SuiteStarting-TestSpec2 + +$ absent target/SuiteStarting-TestSpec2-2 + +$ exists target/SuiteCompleted-TestSpec2 + +$ absent target/SuiteCompleted-TestSpec2-2 + +$ exists target/TestStarting-TestSpec-test-1 + +$ absent target/TestStarting-TestSpec-test-1-2 + +$ exists target/TestSucceeded-TestSpec-test-1 + +$ absent target/TestSucceeded-TestSpec-test-1-2 + +$ exists target/TestStarting-TestSpec-test-2 + +$ absent target/TestStarting-TestSpec-test-2-2 + +$ exists target/TestSucceeded-TestSpec-test-2 + +$ absent target/TestSucceeded-TestSpec-test-2-2 + +$ exists target/TestStarting-TestSpec-test-3 + +$ absent target/TestStarting-TestSpec-test-3-2 + +$ exists target/TestSucceeded-TestSpec-test-3 + +$ absent target/TestSucceeded-TestSpec-test-3-2 + +$ exists target/TestStarting-TestSpec2-test-1 + +$ absent target/TestStarting-TestSpec2-test-1-2 + +$ exists target/TestSucceeded-TestSpec2-test-1 + +$ absent target/TestSucceeded-TestSpec2-test-1-2 + +$ exists target/TestStarting-TestSpec2-test-2 + +$ absent target/TestStarting-TestSpec2-test-2-2 + +$ exists target/TestSucceeded-TestSpec2-test-2 + +$ absent target/TestSucceeded-TestSpec2-test-2-2 + +$ exists target/TestStarting-TestSpec2-test-3 + +$ absent target/TestStarting-TestSpec2-test-3-2 + +$ exists target/TestSucceeded-TestSpec2-test-3 + +$ absent target/TestSucceeded-TestSpec2-test-3-2 \ No newline at end of file diff --git a/testing/agent/src/main/java/sbt/ForkMain.java b/testing/agent/src/main/java/sbt/ForkMain.java index 11a03927f..a04365e61 100755 --- a/testing/agent/src/main/java/sbt/ForkMain.java +++ b/testing/agent/src/main/java/sbt/ForkMain.java @@ -3,7 +3,7 @@ */ package sbt; -import org.scalatools.testing.*; +import sbt.testing.*; import java.io.IOException; import java.io.ObjectInputStream; @@ -15,15 +15,18 @@ import java.util.ArrayList; import java.util.List; public class ForkMain { - static class SubclassFingerscan implements TestFingerprint, Serializable { + static class SubclassFingerscan implements SubclassFingerprint, Serializable { private boolean isModule; - private String superClassName; + private String superclassName; + private boolean requireNoArgConstructor; SubclassFingerscan(SubclassFingerprint print) { isModule = print.isModule(); - superClassName = print.superClassName(); + superclassName = print.superclassName(); + requireNoArgConstructor = print.requireNoArgConstructor(); } public boolean isModule() { return isModule; } - public String superClassName() { return superClassName; } + public String superclassName() { return superclassName; } + public boolean requireNoArgConstructor() { return requireNoArgConstructor; } } static class AnnotatedFingerscan implements AnnotatedFingerprint, Serializable { private boolean isModule; @@ -59,21 +62,69 @@ public class ForkMain { public String getMessage() { return originalMessage; } public Exception getCause() { return cause; } } - static class ForkEvent implements Event, Serializable { + static class ForkSelector extends Selector implements Serializable {} + static class ForkSuiteSelector extends ForkSelector {} + static class ForkTestSelector extends ForkSelector { private String testName; - private String description; - private Result result; - private Throwable error; - ForkEvent(Event e) { - testName = e.testName(); - description = e.description(); - result = e.result(); - if (e.error() != null) error = new ForkError(e.error()); + ForkTestSelector(TestSelector testSelector) { + this.testName = testSelector.getTestName(); + } + public String getTestName() { + return testName; + } + } + static class ForkNestedSuiteSelector extends ForkSelector { + private String suiteId; + ForkNestedSuiteSelector(NestedSuiteSelector nestedSuiteSelector) { + this.suiteId = nestedSuiteSelector.getSuiteId(); + } + public String getSuiteId() { + return suiteId; + } + } + static class ForkNestedTestSelector extends ForkSelector { + private String suiteId; + private String testName; + ForkNestedTestSelector(NestedTestSelector nestedTestSelector) { + this.suiteId = nestedTestSelector.getSuiteId(); + this.testName = nestedTestSelector.getTestName(); + } + public String getSuiteId() { + return suiteId; + } + public String getTestName() { + return testName; + } + } + + static class ForkEvent implements Event, Serializable { + private String fullyQualifiedName; + private boolean isModule; + private ForkSelector selector; + private Status status; + private Throwable throwable; + ForkEvent(Event e) { + fullyQualifiedName = e.fullyQualifiedName(); + isModule = e.isModule(); + selector = forkSelector(e.selector()); + status = e.status(); + if (e.throwable() != null) throwable = new ForkError(e.throwable()); + } + public String fullyQualifiedName() { return fullyQualifiedName; } + public boolean isModule() { return isModule; } + public Selector selector() { return selector; } + public Status status() { return status; } + public Throwable throwable() { return throwable; } + protected ForkSelector forkSelector(Selector selector) { + if (selector instanceof SuiteSelector) + return new ForkSuiteSelector(); + else if (selector instanceof TestSelector) + return new ForkTestSelector((TestSelector) selector); + else if (selector instanceof NestedSuiteSelector) + return new ForkNestedSuiteSelector((NestedSuiteSelector) selector); + else + return new ForkNestedTestSelector((NestedTestSelector) selector); } - public String testName() { return testName; } - public String description() { return description; } - public Result result() { return result; } - public Throwable error() { return error; } } public static void main(String[] args) throws Exception { Socket socket = new Socket(InetAddress.getByName(null), Integer.valueOf(args[0])); @@ -95,7 +146,7 @@ public class ForkMain { if (f1 instanceof SubclassFingerprint && f2 instanceof SubclassFingerprint) { final SubclassFingerprint sf1 = (SubclassFingerprint) f1; final SubclassFingerprint sf2 = (SubclassFingerprint) f2; - return sf1.isModule() == sf2.isModule() && sf1.superClassName().equals(sf2.superClassName()); + return sf1.isModule() == sf2.isModule() && sf1.superclassName().equals(sf2.superclassName()); } else if (f1 instanceof AnnotatedFingerprint && f2 instanceof AnnotatedFingerprint) { AnnotatedFingerprint af1 = (AnnotatedFingerprint) f1; AnnotatedFingerprint af2 = (AnnotatedFingerprint) f2; @@ -141,44 +192,43 @@ public class ForkMain { final Framework framework; try { - framework = (Framework) Class.forName(implClassName).newInstance(); + Object rawFramework = Class.forName(implClassName).newInstance(); + if (rawFramework instanceof Framework) + framework = (Framework) rawFramework; + else + framework = new FrameworkWrapper((org.scalatools.testing.Framework) rawFramework); } catch (ClassNotFoundException e) { logError(os, "Framework implementation '" + implClassName + "' not present."); continue; } ArrayList filteredTests = new ArrayList(); - for (Fingerprint testFingerprint : framework.tests()) { + for (Fingerprint testFingerprint : framework.fingerprints()) { for (ForkTestDefinition test : tests) { if (matches(testFingerprint, test.fingerprint)) filteredTests.add(test); } } - final org.scalatools.testing.Runner runner = framework.testRunner(getClass().getClassLoader(), loggers); + final Runner runner = framework.runner(frameworkArgs, new String[0], getClass().getClassLoader()); for (ForkTestDefinition test : filteredTests) - runTestSafe(test, runner, framework, frameworkArgs, os); + runTestSafe(test, runner, loggers, os); } write(os, ForkTags.Done); is.readObject(); } - void runTestSafe(ForkTestDefinition test, org.scalatools.testing.Runner runner, Framework framework, String[] frameworkArgs, ObjectOutputStream os) { + void runTestSafe(ForkTestDefinition test, Runner runner, Logger[] loggers, ObjectOutputStream os) { ForkEvent[] events; try { - events = runTest(test, runner, framework, frameworkArgs, os); + events = runTest(test, runner, loggers, os); } catch (Throwable t) { events = new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) }; } writeEvents(os, test, events); } - ForkEvent[] runTest(ForkTestDefinition test, org.scalatools.testing.Runner runner, Framework framework, String[] frameworkArgs, ObjectOutputStream os) { + ForkEvent[] runTest(ForkTestDefinition test, Runner runner, Logger[] loggers, ObjectOutputStream os) { final List events = new ArrayList(); EventHandler handler = new EventHandler() { public void handle(Event e){ events.add(new ForkEvent(e)); } }; - if (runner instanceof Runner2) { - ((Runner2) runner).run(test.name, test.fingerprint, handler, frameworkArgs); - } else if (test.fingerprint instanceof TestFingerprint) { - runner.run(test.name, (TestFingerprint) test.fingerprint, handler, frameworkArgs); - } else { - events.add(testError(os, test, "Framework '" + framework + "' does not support test '" + test.name + "'")); - } + // TODO: To pass in correct explicitlySpecified and selectors + runner.task(test.name, test.fingerprint, false, new Selector[] { new SuiteSelector() }).execute(handler, loggers); return events.toArray(new ForkEvent[events.size()]); } void run(ObjectInputStream is, ObjectOutputStream os) throws Exception { @@ -198,22 +248,23 @@ public class ForkMain { void internalError(Throwable t) { System.err.println("Internal error when running tests: " + t.toString()); } - ForkEvent testEvent(final String name, final String desc, final Result r, final Throwable err) { + ForkEvent testEvent(final String fullyQualifiedName, final Fingerprint fingerprint, final Selector selector, final Status r, final Throwable err) { return new ForkEvent(new Event() { - public String testName() { return name; } - public String description() { return desc; } - public Result result() { return r; } - public Throwable error() { return err; } + public String fullyQualifiedName() { return fullyQualifiedName; } + public boolean isModule() { return fingerprint instanceof SubclassFingerprint ? ((SubclassFingerprint) fingerprint).isModule() : ((AnnotatedFingerprint) fingerprint).isModule(); } + public Selector selector() { return selector; } + public Status status() { return r; } + public Throwable throwable() { return err; } }); } ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) { logError(os, message); - return testEvent(test.name, message, Result.Error, null); + return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, null); } ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message, Throwable t) { logError(os, message); write(os, t); - return testEvent(test.name, message, Result.Error, t); + return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, t); } } } diff --git a/testing/agent/src/main/java/sbt/FrameworkWrapper.java b/testing/agent/src/main/java/sbt/FrameworkWrapper.java new file mode 100644 index 000000000..55d978025 --- /dev/null +++ b/testing/agent/src/main/java/sbt/FrameworkWrapper.java @@ -0,0 +1,241 @@ +package sbt; + +import sbt.testing.*; + +public class FrameworkWrapper implements Framework { + + private org.scalatools.testing.Framework oldFramework; + + public FrameworkWrapper(org.scalatools.testing.Framework oldFramework) { + this.oldFramework = oldFramework; + } + + public String name() { + return oldFramework.name(); + } + + public Fingerprint[] fingerprints() { + org.scalatools.testing.Fingerprint[] oldFingerprints = oldFramework.tests(); + int length = oldFingerprints.length; + Fingerprint[] fingerprints = new Fingerprint[length]; + for (int i=0; i < length; i++) { + org.scalatools.testing.Fingerprint oldFingerprint = oldFingerprints[i]; + if (oldFingerprint instanceof org.scalatools.testing.TestFingerprint) + fingerprints[i] = new TestFingerprintWrapper((org.scalatools.testing.TestFingerprint) oldFingerprint); + else if (oldFingerprint instanceof org.scalatools.testing.SubclassFingerprint) + fingerprints[i] = new SubclassFingerprintWrapper((org.scalatools.testing.SubclassFingerprint) oldFingerprint); + else + fingerprints[i] = new AnnotatedFingerprintWrapper((org.scalatools.testing.AnnotatedFingerprint) oldFingerprint); + } + return fingerprints; + } + + public Runner runner(String[] args, String[] remoteArgs, ClassLoader testClassLoader) { + return new RunnerWrapper(oldFramework, testClassLoader, args); + } +} + +class SubclassFingerprintWrapper implements SubclassFingerprint { + private String superclassName; + private boolean isModule; + private boolean requireNoArgConstructor; + + public SubclassFingerprintWrapper(org.scalatools.testing.SubclassFingerprint oldFingerprint) { + this.superclassName = oldFingerprint.superClassName(); + this.isModule = oldFingerprint.isModule(); + this.requireNoArgConstructor = false; // Old framework SubclassFingerprint does not require no arg constructor + } + + public boolean isModule() { + return isModule; + } + + public String superclassName() { + return superclassName; + } + + public boolean requireNoArgConstructor() { + return requireNoArgConstructor; + } +} + +class AnnotatedFingerprintWrapper implements AnnotatedFingerprint { + private String annotationName; + private boolean isModule; + + public AnnotatedFingerprintWrapper(org.scalatools.testing.AnnotatedFingerprint oldFingerprint) { + this.annotationName = oldFingerprint.annotationName(); + this.isModule = oldFingerprint.isModule(); + } + + public boolean isModule() { + return isModule; + } + + public String annotationName() { + return annotationName; + } +} + +class TestFingerprintWrapper extends SubclassFingerprintWrapper { + + public TestFingerprintWrapper(org.scalatools.testing.TestFingerprint oldFingerprint) { + super(oldFingerprint); + } +} + +class EventHandlerWrapper implements org.scalatools.testing.EventHandler { + + private EventHandler newEventHandler; + private String fullyQualifiedName; + private boolean isModule; + + public EventHandlerWrapper(EventHandler newEventHandler, String fullyQualifiedName, boolean isModule) { + this.newEventHandler = newEventHandler; + this.fullyQualifiedName = fullyQualifiedName; + this.isModule = isModule; + } + + public void handle(org.scalatools.testing.Event oldEvent) { + newEventHandler.handle(new EventWrapper(oldEvent, fullyQualifiedName, isModule)); + } +} + +class EventWrapper implements Event { + + private org.scalatools.testing.Event oldEvent; + private String className; + private boolean classIsModule; + + public EventWrapper(org.scalatools.testing.Event oldEvent, String className, boolean classIsModule) { + this.oldEvent = oldEvent; + this.className = className; + this.classIsModule = classIsModule; + } + + public String fullyQualifiedName() { + return className; + } + + public boolean isModule() { + return classIsModule; + } + + public Selector selector() { + return new TestSelector(oldEvent.testName()); + } + + public Status status() { + switch (oldEvent.result()) { + case Success: + return Status.Success; + case Error: + return Status.Error; + case Failure: + return Status.Failure; + case Skipped: + return Status.Skipped; + default: + throw new IllegalStateException("Invalid status."); + } + } + + public Throwable throwable() { + return oldEvent.error(); + } + +} + +class RunnerWrapper implements Runner { + + private org.scalatools.testing.Framework oldFramework; + private ClassLoader testClassLoader; + private String[] args; + + public RunnerWrapper(org.scalatools.testing.Framework oldFramework, ClassLoader testClassLoader, String[] args) { + this.oldFramework = oldFramework; + this.testClassLoader = testClassLoader; + this.args = args; + } + + public Task task(final String fullyQualifiedName, final Fingerprint fingerprint, boolean explicitlySpecified, Selector[] selectors) { + return new Task() { + public String[] tags() { + return new String[0]; // Old framework does not support tags + } + + private org.scalatools.testing.Logger createOldLogger(final Logger logger) { + return new org.scalatools.testing.Logger() { + public boolean ansiCodesSupported() { return logger.ansiCodesSupported(); } + public void error(String msg) { logger.error(msg); } + public void warn(String msg) { logger.warn(msg); } + public void info(String msg) { logger.info(msg); } + public void debug(String msg) { logger.debug(msg); } + public void trace(Throwable t) { logger.trace(t); } + }; + } + + private void runRunner(org.scalatools.testing.Runner runner, Fingerprint fingerprint, EventHandler eventHandler) { + // Old runner only support subclass fingerprint. + final SubclassFingerprint subclassFingerprint = (SubclassFingerprint) fingerprint; + org.scalatools.testing.TestFingerprint oldFingerprint = + new org.scalatools.testing.TestFingerprint() { + public boolean isModule() { return subclassFingerprint.isModule(); } + public String superClassName() { return subclassFingerprint.superclassName(); } + }; + runner.run(fullyQualifiedName, oldFingerprint, new EventHandlerWrapper(eventHandler, fullyQualifiedName, subclassFingerprint.isModule()), args); + } + + private void runRunner2(org.scalatools.testing.Runner2 runner, Fingerprint fingerprint, EventHandler eventHandler) { + org.scalatools.testing.Fingerprint oldFingerprint = null; + boolean isModule = false; + if (fingerprint instanceof SubclassFingerprint) { + final SubclassFingerprint subclassFingerprint = (SubclassFingerprint) fingerprint; + oldFingerprint = new org.scalatools.testing.SubclassFingerprint() { + public boolean isModule() { return subclassFingerprint.isModule(); } + public String superClassName() { return subclassFingerprint.superclassName(); } + }; + isModule = subclassFingerprint.isModule(); + } + else { + final AnnotatedFingerprint annotatedFingerprint = (AnnotatedFingerprint) fingerprint; + oldFingerprint = new org.scalatools.testing.AnnotatedFingerprint() { + public boolean isModule() { return annotatedFingerprint.isModule(); } + public String annotationName() { return annotatedFingerprint.annotationName(); } + }; + isModule = annotatedFingerprint.isModule(); + } + runner.run(fullyQualifiedName, oldFingerprint, new EventHandlerWrapper(eventHandler, fullyQualifiedName, isModule), args); + } + + public Task[] execute(EventHandler eventHandler, Logger[] loggers) { + int length = loggers.length; + org.scalatools.testing.Logger[] oldLoggers = new org.scalatools.testing.Logger[length]; + for (int i=0; iTLogger} + import testing.{Logger=>TLogger, _} + import org.scalatools.testing.{Framework => OldFramework} import classpath.{ClasspathUtilities, DualLoader, FilteredLoader} object TestResult extends Enumeration @@ -17,7 +17,7 @@ object TestResult extends Enumeration object TestFrameworks { val ScalaCheck = new TestFramework("org.scalacheck.ScalaCheckFramework") - val ScalaTest = new TestFramework("org.scalatest.tools.ScalaTestFramework") + val ScalaTest = new TestFramework("org.scalatest.tools.Framework") val Specs = new TestFramework("org.specs.runner.SpecsFramework") val Specs2 = new TestFramework("org.specs2.runner.SpecsFramework") val JUnit = new TestFramework("com.novocode.junit.JUnitFramework") @@ -27,8 +27,19 @@ case class TestFramework(val implClassName: String) { def create(loader: ClassLoader, log: Logger): Option[Framework] = { - try { Some(Class.forName(implClassName, true, loader).newInstance.asInstanceOf[Framework]) } - catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None } + try + { + Some( + Class.forName(implClassName, true, loader).newInstance match { + case newFramework: Framework => newFramework + case oldFramework: OldFramework => new FrameworkWrapper(oldFramework) + } + ) + } + catch + { + case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None + } } } final class TestDefinition(val name: String, val fingerprint: Fingerprint) @@ -43,33 +54,24 @@ final class TestDefinition(val name: String, val fingerprint: Fingerprint) override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode } -final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger) -{ - private[this] def run(testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit = - { - val loggers = listeners.flatMap(_.contentLogger(testDefinition)) - val delegate = framework.testRunner(loader, loggers.map(_.log).toArray) - try { delegateRun(delegate, testDefinition, handler, args) } - finally { loggers.foreach( _.flush() ) } - } - private[this] def delegateRun(delegate: Runner, testDefinition: TestDefinition, handler: EventHandler, args: Array[String]): Unit = - (testDefinition.fingerprint, delegate) match - { - case (simple: TestFingerprint, _) => delegate.run(testDefinition.name, simple, handler, args) - case (basic, runner2: Runner2) => runner2.run(testDefinition.name, basic, handler, args) - case _ => sys.error("Framework '" + framework + "' does not support test '" + testDefinition + "'") - } +final class TestRunner(framework: Framework, loader: ClassLoader, args: Array[String], listeners: Seq[TestReportListener], log: Logger) { + val delegate = framework.runner(args, Array.empty, loader) - final def run(testDefinition: TestDefinition, args: Seq[String]): TestResult.Value = + final def run(testDefinition: TestDefinition): TestResult.Value = { - log.debug("Running " + testDefinition + " with arguments " + args.mkString(", ")) + log.debug("Running " + testDefinition) val name = testDefinition.name def runTest() = { // here we get the results! here is where we'd pass in the event listener val results = new scala.collection.mutable.ListBuffer[Event] val handler = new EventHandler { def handle(e:Event){ results += e } } - run(testDefinition, handler, args.toArray) + val loggers = listeners.flatMap(_.contentLogger(testDefinition)) + try { + // TODO: To pass in correct explicitlySpecified and selectors + delegate.task(testDefinition.name, testDefinition.fingerprint, false, Array(new SuiteSelector)).execute(handler, loggers.map(_.log).toArray) + } + finally loggers.foreach( _.flush() ) val event = TestEvent(results) safeListenersCall(_.testEvent( event )) event.result @@ -95,13 +97,12 @@ final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq } object TestFramework -{ - def getTests(framework: Framework): Seq[Fingerprint] = - framework.getClass.getMethod("tests").invoke(framework) match +{ + def getFingerprints(framework: Framework): Seq[Fingerprint] = + framework.getClass.getMethod("fingerprints").invoke(framework) match { - case newStyle: Array[Fingerprint] => newStyle.toList - case oldStyle: Array[TestFingerprint] => oldStyle.toList - case _ => sys.error("Could not call 'tests' on framework " + framework) + case fingerprints: Array[Fingerprint] => fingerprints.toList + case _ => sys.error("Could not call 'fingerprints' on framework " + framework) } private val ScalaCompilerJarPackages = "scala.tools." :: "jline." :: "ch.epfl.lamp." :: Nil @@ -113,21 +114,21 @@ object TestFramework it.foreach(i => try f(i) catch { case e: Exception => log.trace(e); log.error(e.toString) }) private[sbt] def hashCode(f: Fingerprint): Int = f match { - case s: SubclassFingerprint => (s.isModule, s.superClassName).hashCode + case s: SubclassFingerprint => (s.isModule, s.superclassName).hashCode case a: AnnotatedFingerprint => (a.isModule, a.annotationName).hashCode case _ => 0 } def matches(a: Fingerprint, b: Fingerprint) = (a, b) match { - case (a: SubclassFingerprint, b: SubclassFingerprint) => a.isModule == b.isModule && a.superClassName == b.superClassName + case (a: SubclassFingerprint, b: SubclassFingerprint) => a.isModule == b.isModule && a.superclassName == b.superclassName case (a: AnnotatedFingerprint, b: AnnotatedFingerprint) => a.isModule == b.isModule && a.annotationName == b.annotationName case _ => false } def toString(f: Fingerprint): String = f match { - case sf: SubclassFingerprint => "subclass(" + sf.isModule + ", " + sf.superClassName + ")" + case sf: SubclassFingerprint => "subclass(" + sf.isModule + ", " + sf.superclassName + ")" case af: AnnotatedFingerprint => "annotation(" + af.isModule + ", " + af.annotationName + ")" case _ => f.toString } @@ -160,7 +161,7 @@ object TestFramework { for(test <- tests if !map.values.exists(_.contains(test))) { - def isTestForFramework(framework: Framework) = getTests(framework).exists {t => matches(t, test.fingerprint) } + def isTestForFramework(framework: Framework) = getFingerprints(framework).exists {t => matches(t, test.fingerprint) } for(framework <- frameworks.find(isTestForFramework)) map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test } @@ -171,7 +172,7 @@ object TestFramework } private[this] def mergeDuplicates(framework: Framework, tests: Seq[TestDefinition]): Set[TestDefinition] = { - val frameworkPrints = framework.tests.reverse + val frameworkPrints = framework.fingerprints.reverse def pickOne(prints: Seq[Fingerprint]): Fingerprint = frameworkPrints.find(prints.toSet) getOrElse prints.head val uniqueDefs = @@ -191,10 +192,10 @@ object TestFramework val testTasks = tests flatMap { case (framework, (testDefinitions, testArgs)) => - val runner = new TestRunner(framework, loader, listeners, log) + val runner = new TestRunner(framework, loader, testArgs.toArray, listeners, log) for(testDefinition <- testDefinitions) yield { - val runTest = () => withContextLoader(loader) { runner.run(testDefinition, testArgs) } + val runTest = () => withContextLoader(loader) { runner.run(testDefinition) } (testDefinition.name, runTest) } } @@ -210,8 +211,8 @@ object TestFramework } def createTestLoader(classpath: Seq[File], scalaInstance: ScalaInstance, tempDir: File): ClassLoader = { - val interfaceJar = IO.classLocationFile(classOf[org.scalatools.testing.Framework]) - val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") + val interfaceJar = IO.classLocationFile(classOf[testing.Framework]) + val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") || name.startsWith("sbt.testing.") val notInterfaceFilter = (name: String) => !interfaceFilter(name) val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false) val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir) diff --git a/testing/src/main/scala/sbt/TestReportListener.scala b/testing/src/main/scala/sbt/TestReportListener.scala index d20dbbb76..c1de312fd 100644 --- a/testing/src/main/scala/sbt/TestReportListener.scala +++ b/testing/src/main/scala/sbt/TestReportListener.scala @@ -4,7 +4,7 @@ package sbt - import org.scalatools.testing.{Logger => TLogger, Event => TEvent, Result => TResult} + import testing.{Logger => TLogger, Event => TEvent, Status => TStatus} trait TestReportListener { @@ -38,9 +38,9 @@ object TestEvent def apply(events: Seq[TEvent]): TestEvent = { val overallResult = (TestResult.Passed /: events) { (sum, event) => - val result = event.result - if(sum == TestResult.Error || result == TResult.Error) TestResult.Error - else if(sum == TestResult.Failed || result == TResult.Failure) TestResult.Failed + val status = event.status + if(sum == TestResult.Error || status == TStatus.Error) TestResult.Error + else if(sum == TestResult.Failed || status == TStatus.Failure) TestResult.Failed else TestResult.Passed } new TestEvent { @@ -91,11 +91,11 @@ class TestLogger(val logging: TestLogging) extends TestsListener def endGroup(name: String, result: TestResult.Value) {} protected def count(event: TEvent): Unit = { - val count = event.result match { - case TResult.Error => errorsCount - case TResult.Success => passedCount - case TResult.Failure => failuresCount - case TResult.Skipped => skippedCount + val count = event.status match { + case TStatus.Error => errorsCount + case TStatus.Success => passedCount + case TStatus.Failure => failuresCount + case TStatus.Skipped => skippedCount } count.incrementAndGet() }