Initial version that uses new framework API in test-interface 1.0:

-Changed usages and implementations of interfaces in org.scalatools.testing._ to use/implement interfaces/classes in sbt.testing._ instead.
-Added sbt.testing to interfaceFilter in TestFramework.createTestLoader method to enable loading of classes in sbt.testing package.
-Added FrameworkWrapper.java to wrap old framework implementations.
-Added code in ForkMain.java to serialize Selectors.
This commit is contained in:
cheeseng 2013-04-03 21:58:54 +08:00
parent 838b737caa
commit 18bc8423b5
19 changed files with 651 additions and 95 deletions

View File

@ -4,7 +4,7 @@
package sbt package sbt
import scala.collection.mutable import scala.collection.mutable
import org.scalatools.testing._ import testing._
import java.net.ServerSocket import java.net.ServerSocket
import java.io._ import java.io._
import Tests.{Output => TestOutput, _} import Tests.{Output => TestOutput, _}

View File

@ -11,7 +11,7 @@ package sbt
import xsbti.api.Definition import xsbti.api.Definition
import ConcurrentRestrictions.Tag import ConcurrentRestrictions.Tag
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint} import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint}
import java.io.File import java.io.File
@ -145,12 +145,12 @@ object Tests
def overall(results: Iterable[TestResult.Value]): TestResult.Value = def overall(results: Iterable[TestResult.Value]): TestResult.Value =
(TestResult.Passed /: results) { (acc, result) => if(acc.id < result.id) result else acc } (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]) = 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 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]) = 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) }; val annotations = fingerprints collect { case ann: AnnotatedFingerprint => (ann.annotationName, ann.isModule, ann) };
log.debug("Subclass fingerprints: " + subclasses) log.debug("Subclass fingerprints: " + subclasses)
log.debug("Annotation fingerprints: " + annotations) log.debug("Annotation fingerprints: " + annotations)

View File

@ -15,7 +15,7 @@ package sbt
import complete._ import complete._
import std.TaskExtra._ import std.TaskExtra._
import inc.{FileValueCache, Locate} import inc.{FileValueCache, Locate}
import org.scalatools.testing.{Framework, AnnotatedFingerprint, SubclassFingerprint} import testing.{Framework, AnnotatedFingerprint, SubclassFingerprint}
import sys.error import sys.error
import scala.xml.NodeSeq import scala.xml.NodeSeq

View File

@ -14,7 +14,7 @@ package sbt
import scala.xml.{Node => XNode, NodeSeq} import scala.xml.{Node => XNode, NodeSeq}
import org.apache.ivy.core.module.{descriptor, id} import org.apache.ivy.core.module.{descriptor, id}
import descriptor.ModuleDescriptor, id.ModuleRevisionId import descriptor.ModuleDescriptor, id.ModuleRevisionId
import org.scalatools.testing.Framework import testing.Framework
import Configurations.CompilerPlugin import Configurations.CompilerPlugin
import Types.Id import Types.Id
import KeyRanks._ import KeyRanks._

View File

@ -0,0 +1,5 @@
scalaVersion := "2.10.1"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")

View File

@ -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 _ =>
}
}
}

View File

@ -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 ` {}
}

View File

@ -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 ` {}
}

View File

@ -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

View File

@ -0,0 +1,5 @@
scalaVersion := "2.10.1"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")

View File

@ -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 _ =>
}
}
}

View File

@ -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 ` {}
}

View File

@ -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 ` {}
}

View File

@ -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

View File

@ -3,7 +3,7 @@
*/ */
package sbt; package sbt;
import org.scalatools.testing.*; import sbt.testing.*;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
@ -15,15 +15,18 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class ForkMain { public class ForkMain {
static class SubclassFingerscan implements TestFingerprint, Serializable { static class SubclassFingerscan implements SubclassFingerprint, Serializable {
private boolean isModule; private boolean isModule;
private String superClassName; private String superclassName;
private boolean requireNoArgConstructor;
SubclassFingerscan(SubclassFingerprint print) { SubclassFingerscan(SubclassFingerprint print) {
isModule = print.isModule(); isModule = print.isModule();
superClassName = print.superClassName(); superclassName = print.superclassName();
requireNoArgConstructor = print.requireNoArgConstructor();
} }
public boolean isModule() { return isModule; } 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 { static class AnnotatedFingerscan implements AnnotatedFingerprint, Serializable {
private boolean isModule; private boolean isModule;
@ -59,21 +62,69 @@ public class ForkMain {
public String getMessage() { return originalMessage; } public String getMessage() { return originalMessage; }
public Exception getCause() { return cause; } 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 testName;
private String description; ForkTestSelector(TestSelector testSelector) {
private Result result; this.testName = testSelector.getTestName();
private Throwable error; }
ForkEvent(Event e) { public String getTestName() {
testName = e.testName(); return testName;
description = e.description(); }
result = e.result(); }
if (e.error() != null) error = new ForkError(e.error()); 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 { public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getByName(null), Integer.valueOf(args[0])); 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) { if (f1 instanceof SubclassFingerprint && f2 instanceof SubclassFingerprint) {
final SubclassFingerprint sf1 = (SubclassFingerprint) f1; final SubclassFingerprint sf1 = (SubclassFingerprint) f1;
final SubclassFingerprint sf2 = (SubclassFingerprint) f2; 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) { } else if (f1 instanceof AnnotatedFingerprint && f2 instanceof AnnotatedFingerprint) {
AnnotatedFingerprint af1 = (AnnotatedFingerprint) f1; AnnotatedFingerprint af1 = (AnnotatedFingerprint) f1;
AnnotatedFingerprint af2 = (AnnotatedFingerprint) f2; AnnotatedFingerprint af2 = (AnnotatedFingerprint) f2;
@ -141,44 +192,43 @@ public class ForkMain {
final Framework framework; final Framework framework;
try { 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) { } catch (ClassNotFoundException e) {
logError(os, "Framework implementation '" + implClassName + "' not present."); logError(os, "Framework implementation '" + implClassName + "' not present.");
continue; continue;
} }
ArrayList<ForkTestDefinition> filteredTests = new ArrayList<ForkTestDefinition>(); ArrayList<ForkTestDefinition> filteredTests = new ArrayList<ForkTestDefinition>();
for (Fingerprint testFingerprint : framework.tests()) { for (Fingerprint testFingerprint : framework.fingerprints()) {
for (ForkTestDefinition test : tests) { for (ForkTestDefinition test : tests) {
if (matches(testFingerprint, test.fingerprint)) filteredTests.add(test); 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) for (ForkTestDefinition test : filteredTests)
runTestSafe(test, runner, framework, frameworkArgs, os); runTestSafe(test, runner, loggers, os);
} }
write(os, ForkTags.Done); write(os, ForkTags.Done);
is.readObject(); 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; ForkEvent[] events;
try { try {
events = runTest(test, runner, framework, frameworkArgs, os); events = runTest(test, runner, loggers, os);
} catch (Throwable t) { } catch (Throwable t) {
events = new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) }; events = new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) };
} }
writeEvents(os, test, events); 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<ForkEvent> events = new ArrayList<ForkEvent>(); final List<ForkEvent> events = new ArrayList<ForkEvent>();
EventHandler handler = new EventHandler() { public void handle(Event e){ events.add(new ForkEvent(e)); } }; EventHandler handler = new EventHandler() { public void handle(Event e){ events.add(new ForkEvent(e)); } };
if (runner instanceof Runner2) { // TODO: To pass in correct explicitlySpecified and selectors
((Runner2) runner).run(test.name, test.fingerprint, handler, frameworkArgs); runner.task(test.name, test.fingerprint, false, new Selector[] { new SuiteSelector() }).execute(handler, loggers);
} 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 + "'"));
}
return events.toArray(new ForkEvent[events.size()]); return events.toArray(new ForkEvent[events.size()]);
} }
void run(ObjectInputStream is, ObjectOutputStream os) throws Exception { void run(ObjectInputStream is, ObjectOutputStream os) throws Exception {
@ -198,22 +248,23 @@ public class ForkMain {
void internalError(Throwable t) { void internalError(Throwable t) {
System.err.println("Internal error when running tests: " + t.toString()); 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() { return new ForkEvent(new Event() {
public String testName() { return name; } public String fullyQualifiedName() { return fullyQualifiedName; }
public String description() { return desc; } public boolean isModule() { return fingerprint instanceof SubclassFingerprint ? ((SubclassFingerprint) fingerprint).isModule() : ((AnnotatedFingerprint) fingerprint).isModule(); }
public Result result() { return r; } public Selector selector() { return selector; }
public Throwable error() { return err; } public Status status() { return r; }
public Throwable throwable() { return err; }
}); });
} }
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) { ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) {
logError(os, 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) { ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message, Throwable t) {
logError(os, message); logError(os, message);
write(os, t); write(os, t);
return testEvent(test.name, message, Result.Error, t); return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, t);
} }
} }
} }

View File

@ -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; i<length; i++) {
oldLoggers[i] = createOldLogger(loggers[i]);
}
org.scalatools.testing.Runner runner = oldFramework.testRunner(testClassLoader, oldLoggers);
if (runner instanceof org.scalatools.testing.Runner2) {
runRunner2((org.scalatools.testing.Runner2) runner, fingerprint, eventHandler);
}
else {
runRunner(runner, fingerprint, eventHandler);
}
return new Task[0];
}
};
}
public String done() {
return "";
}
public String[] args() {
return args;
}
public String[] remoteArgs() {
return new String[0]; // Old framework does not support remoteArgs
}
}

View File

@ -18,7 +18,7 @@ package sbt
*/ */
import scala.util.parsing.combinator._ import scala.util.parsing.combinator._
import org.scalatools.testing.{Fingerprint, AnnotatedFingerprint, TestFingerprint} import testing.{Fingerprint, AnnotatedFingerprint, TestFingerprint}
import DiscoveredParser._ import DiscoveredParser._
sealed abstract class Discovered extends Fingerprint with NotNull sealed abstract class Discovered extends Fingerprint with NotNull

View File

@ -5,8 +5,8 @@ package sbt
import java.io.File import java.io.File
import java.net.URLClassLoader import java.net.URLClassLoader
import org.scalatools.testing.{AnnotatedFingerprint, Fingerprint, SubclassFingerprint, TestFingerprint} import testing.{Logger=>TLogger, _}
import org.scalatools.testing.{Event, EventHandler, Framework, Runner, Runner2, Logger=>TLogger} import org.scalatools.testing.{Framework => OldFramework}
import classpath.{ClasspathUtilities, DualLoader, FilteredLoader} import classpath.{ClasspathUtilities, DualLoader, FilteredLoader}
object TestResult extends Enumeration object TestResult extends Enumeration
@ -17,7 +17,7 @@ object TestResult extends Enumeration
object TestFrameworks object TestFrameworks
{ {
val ScalaCheck = new TestFramework("org.scalacheck.ScalaCheckFramework") 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 Specs = new TestFramework("org.specs.runner.SpecsFramework")
val Specs2 = new TestFramework("org.specs2.runner.SpecsFramework") val Specs2 = new TestFramework("org.specs2.runner.SpecsFramework")
val JUnit = new TestFramework("com.novocode.junit.JUnitFramework") 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] = def create(loader: ClassLoader, log: Logger): Option[Framework] =
{ {
try { Some(Class.forName(implClassName, true, loader).newInstance.asInstanceOf[Framework]) } try
catch { case e: ClassNotFoundException => log.debug("Framework implementation '" + implClassName + "' not present."); None } {
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) 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 override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
} }
final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq[TestReportListener], log: Logger) final class TestRunner(framework: Framework, loader: ClassLoader, args: Array[String], listeners: Seq[TestReportListener], log: Logger) {
{ val delegate = framework.runner(args, Array.empty, loader)
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 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 val name = testDefinition.name
def runTest() = def runTest() =
{ {
// here we get the results! here is where we'd pass in the event listener // here we get the results! here is where we'd pass in the event listener
val results = new scala.collection.mutable.ListBuffer[Event] val results = new scala.collection.mutable.ListBuffer[Event]
val handler = new EventHandler { def handle(e:Event){ results += e } } 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) val event = TestEvent(results)
safeListenersCall(_.testEvent( event )) safeListenersCall(_.testEvent( event ))
event.result event.result
@ -96,12 +98,11 @@ final class TestRunner(framework: Framework, loader: ClassLoader, listeners: Seq
object TestFramework object TestFramework
{ {
def getTests(framework: Framework): Seq[Fingerprint] = def getFingerprints(framework: Framework): Seq[Fingerprint] =
framework.getClass.getMethod("tests").invoke(framework) match framework.getClass.getMethod("fingerprints").invoke(framework) match
{ {
case newStyle: Array[Fingerprint] => newStyle.toList case fingerprints: Array[Fingerprint] => fingerprints.toList
case oldStyle: Array[TestFingerprint] => oldStyle.toList case _ => sys.error("Could not call 'fingerprints' on framework " + framework)
case _ => sys.error("Could not call 'tests' on framework " + framework)
} }
private val ScalaCompilerJarPackages = "scala.tools." :: "jline." :: "ch.epfl.lamp." :: Nil 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) }) 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 { 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 a: AnnotatedFingerprint => (a.isModule, a.annotationName).hashCode
case _ => 0 case _ => 0
} }
def matches(a: Fingerprint, b: Fingerprint) = def matches(a: Fingerprint, b: Fingerprint) =
(a, b) match (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 (a: AnnotatedFingerprint, b: AnnotatedFingerprint) => a.isModule == b.isModule && a.annotationName == b.annotationName
case _ => false case _ => false
} }
def toString(f: Fingerprint): String = def toString(f: Fingerprint): String =
f match 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 af: AnnotatedFingerprint => "annotation(" + af.isModule + ", " + af.annotationName + ")"
case _ => f.toString case _ => f.toString
} }
@ -160,7 +161,7 @@ object TestFramework
{ {
for(test <- tests if !map.values.exists(_.contains(test))) 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)) for(framework <- frameworks.find(isTestForFramework))
map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test
} }
@ -171,7 +172,7 @@ object TestFramework
} }
private[this] def mergeDuplicates(framework: Framework, tests: Seq[TestDefinition]): Set[TestDefinition] = 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 = def pickOne(prints: Seq[Fingerprint]): Fingerprint =
frameworkPrints.find(prints.toSet) getOrElse prints.head frameworkPrints.find(prints.toSet) getOrElse prints.head
val uniqueDefs = val uniqueDefs =
@ -191,10 +192,10 @@ object TestFramework
val testTasks = val testTasks =
tests flatMap { case (framework, (testDefinitions, testArgs)) => 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 for(testDefinition <- testDefinitions) yield
{ {
val runTest = () => withContextLoader(loader) { runner.run(testDefinition, testArgs) } val runTest = () => withContextLoader(loader) { runner.run(testDefinition) }
(testDefinition.name, runTest) (testDefinition.name, runTest)
} }
} }
@ -210,8 +211,8 @@ object TestFramework
} }
def createTestLoader(classpath: Seq[File], scalaInstance: ScalaInstance, tempDir: File): ClassLoader = def createTestLoader(classpath: Seq[File], scalaInstance: ScalaInstance, tempDir: File): ClassLoader =
{ {
val interfaceJar = IO.classLocationFile(classOf[org.scalatools.testing.Framework]) val interfaceJar = IO.classLocationFile(classOf[testing.Framework])
val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") val interfaceFilter = (name: String) => name.startsWith("org.scalatools.testing.") || name.startsWith("sbt.testing.")
val notInterfaceFilter = (name: String) => !interfaceFilter(name) val notInterfaceFilter = (name: String) => !interfaceFilter(name)
val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false) val dual = new DualLoader(scalaInstance.loader, notInterfaceFilter, x => true, getClass.getClassLoader, interfaceFilter, x => false)
val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir) val main = ClasspathUtilities.makeLoader(classpath, dual, scalaInstance, tempDir)

View File

@ -4,7 +4,7 @@
package sbt package sbt
import org.scalatools.testing.{Logger => TLogger, Event => TEvent, Result => TResult} import testing.{Logger => TLogger, Event => TEvent, Status => TStatus}
trait TestReportListener trait TestReportListener
{ {
@ -38,9 +38,9 @@ object TestEvent
def apply(events: Seq[TEvent]): TestEvent = def apply(events: Seq[TEvent]): TestEvent =
{ {
val overallResult = (TestResult.Passed /: events) { (sum, event) => val overallResult = (TestResult.Passed /: events) { (sum, event) =>
val result = event.result val status = event.status
if(sum == TestResult.Error || result == TResult.Error) TestResult.Error if(sum == TestResult.Error || status == TStatus.Error) TestResult.Error
else if(sum == TestResult.Failed || result == TResult.Failure) TestResult.Failed else if(sum == TestResult.Failed || status == TStatus.Failure) TestResult.Failed
else TestResult.Passed else TestResult.Passed
} }
new TestEvent { new TestEvent {
@ -91,11 +91,11 @@ class TestLogger(val logging: TestLogging) extends TestsListener
def endGroup(name: String, result: TestResult.Value) {} def endGroup(name: String, result: TestResult.Value) {}
protected def count(event: TEvent): Unit = protected def count(event: TEvent): Unit =
{ {
val count = event.result match { val count = event.status match {
case TResult.Error => errorsCount case TStatus.Error => errorsCount
case TResult.Success => passedCount case TStatus.Success => passedCount
case TResult.Failure => failuresCount case TStatus.Failure => failuresCount
case TResult.Skipped => skippedCount case TStatus.Skipped => skippedCount
} }
count.incrementAndGet() count.incrementAndGet()
} }