Skip to content

Commit

Permalink
Change to use test-interface-1.0-SNAP7, and use ScalaTest 2.0.M6-SNAP…
Browse files Browse the repository at this point in the history
…26 which implemented test-interface-1.0-SNAP7.
  • Loading branch information
cheeseng committed Jun 28, 2013
1 parent 220886b commit 5bb4635
Show file tree
Hide file tree
Showing 15 changed files with 148 additions and 125 deletions.
19 changes: 17 additions & 2 deletions main/actions/src/main/scala/sbt/ForkTests.scala
Expand Up @@ -11,7 +11,7 @@ import Tests.{Output => TestOutput, _}
import ForkMain._

private[sbt] object ForkTests {
def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = {
def apply(runners: Map[TestFramework, Runner], tests: List[TestDefinition], config: Execution, loader: ClassLoader, classpath: Seq[File], fork: ForkOptions, log: Logger): Task[TestOutput] = {
val opts = config.options.toList
val listeners = opts flatMap {
case Listeners(ls) => ls
Expand Down Expand Up @@ -40,7 +40,22 @@ private[sbt] object ForkTests {
case _: java.net.SocketException => return
}
val os = new ObjectOutputStream(socket.getOutputStream)
val is = new ObjectInputStream(socket.getInputStream)
// Make sure that ObjectInputStream use the passed in class loader
// ObjectInputStream class loading seems to be confusing, some old but useful links for reference:
// https://forums.oracle.com/thread/1151865
// http://sourceforge.net/p/jpype/bugs/52/
// http://tech-tauk.blogspot.com/2010/05/thread-context-classlaoder-in.html
val is = new ObjectInputStream(socket.getInputStream) {
override protected def resolveClass(desc: ObjectStreamClass): Class[_] = {
try {
val name = desc.getName
Class.forName(name, false, loader)
}
catch {
case e: ClassNotFoundException => super.resolveClass(desc)
}
}
}

try {
os.writeBoolean(log.ansiCodesSupported)
Expand Down
14 changes: 8 additions & 6 deletions main/actions/src/main/scala/sbt/Tests.scala
Expand Up @@ -11,7 +11,7 @@ package sbt
import xsbti.api.Definition
import ConcurrentRestrictions.Tag

import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, Task => TestTask}
import testing.{AnnotatedFingerprint, Fingerprint, Framework, SubclassFingerprint, Runner, TaskDef, SuiteSelector, Task => TestTask}
import scala.annotation.tailrec

import java.io.File
Expand Down Expand Up @@ -123,9 +123,10 @@ object Tests
}
type TestRunnable = (String, TestFunction)

private def createNestedRunnables(name: String, loader: ClassLoader, testFun: TestFunction, nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] =
private def createNestedRunnables(loader: ClassLoader, testFun: TestFunction, nestedTasks: Seq[TestTask]): Seq[(String, TestFunction)] =
nestedTasks.view.zipWithIndex map { case (nt, idx) =>
(name, TestFramework.createTestFunction(loader, new TestDefinition(testFun.testDefinition.name + "-" + idx, testFun.testDefinition.fingerprint), testFun.runner, nt))
val testFunDef = testFun.taskDef
(testFunDef.fullyQualifiedName, TestFramework.createTestFunction(loader, new TaskDef(testFunDef.fullyQualifiedName + "-" + idx, testFunDef.fingerprint, testFunDef.explicitlySpecified, testFunDef.selectors), testFun.runner, nt))
}

def makeParallel(loader: ClassLoader, runnables: Iterable[TestRunnable], setupTasks: Task[Unit], tags: Seq[(Tag,Int)]): Task[Map[String,SuiteResult]] =
Expand All @@ -142,7 +143,7 @@ object Tests
val base = task { (name, fun.apply()) }
val taggedBase = base.tagw(tags : _*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_)) : _*)
taggedBase flatMap { case (name, (result, nested)) =>
val nestedRunnables = createNestedRunnables(fun.testDefinition.name, loader, fun, nested)
val nestedRunnables = createNestedRunnables(loader, fun, nested)
toTasks(loader, nestedRunnables, tags).map( _.updated(name, result) )
}
}
Expand All @@ -155,7 +156,7 @@ object Tests
case hd :: rst =>
val testFun = hd._2
val (result, nestedTasks) = testFun.apply()
val nestedRunnables = createNestedRunnables(testFun.testDefinition.name, loader, testFun, nestedTasks)
val nestedRunnables = createNestedRunnables(loader, testFun, nestedTasks)
processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc)
case Nil => acc
}
Expand Down Expand Up @@ -202,7 +203,8 @@ object Tests
defined(annotations, d.annotations, d.isModule)

val discovered = Discovery(firsts(subclasses), firsts(annotations))(definitions)
val tests = for( (df, di) <- discovered; fingerprint <- toFingerprints(di) ) yield new TestDefinition(df.name, fingerprint)
// TODO: To pass in correct explicitlySpecified and selectors
val tests = for( (df, di) <- discovered; fingerprint <- toFingerprints(di) ) yield new TestDefinition(df.name, fingerprint, false, Array(new SuiteSelector))
val mains = discovered collect { case (df, di) if di.hasMain => df.name }
(tests, mains.toSet)
}
Expand Down
2 changes: 1 addition & 1 deletion main/src/main/scala/sbt/Defaults.scala
Expand Up @@ -484,7 +484,7 @@ object Defaults extends BuildCommon
case Tests.Group(name, tests, runPolicy) =>
runPolicy match {
case Tests.SubProcess(opts) =>
ForkTests(runners, tests.toList, config, cp.files, opts, s.log) tag Tags.ForkedTestGroup
ForkTests(runners, tests.toList, config, loader, cp.files, opts, s.log) tag Tags.ForkedTestGroup
case Tests.InProcess =>
Tests(frameworks, loader, runners, tests, config, s.log)
}
Expand Down
4 changes: 2 additions & 2 deletions project/Sbt.scala
Expand Up @@ -79,10 +79,10 @@ object Sbt extends Build
// Apache Ivy integration
lazy val ivySub = baseProject(file("ivy"), "Ivy") dependsOn(interfaceSub, launchInterfaceSub, crossSub, logSub % "compile;test->test", ioSub % "compile;test->test", launchSub % "test->test") settings(ivy, jsch, testExclusive)
// Runner for uniform test interface
lazy val testingSub = baseProject(file("testing"), "Testing") dependsOn(ioSub, classpathSub, logSub, launchInterfaceSub, testAgentSub) settings(libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP3")
lazy val testingSub = baseProject(file("testing"), "Testing") dependsOn(ioSub, classpathSub, logSub, launchInterfaceSub, testAgentSub) settings(libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP7")
// Testing agent for running tests in a separate process.
lazy val testAgentSub = project(file("testing/agent"), "Test Agent") settings(
libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP3"
libraryDependencies += "org.scalatest" % "test-interface" % "1.0-SNAP7"
)

// Basic task engine
Expand Down
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/do-not-discover/build.sbt
@@ -1,5 +1,5 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/done/build.sbt
@@ -1,5 +1,5 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/nested-inproc-par/build.sbt
@@ -1,6 +1,6 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")

Expand Down
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/nested-inproc-seq/build.sbt
@@ -1,6 +1,6 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")

Expand Down
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/nested-subproc/build.sbt
@@ -1,6 +1,6 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")

Expand Down
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/single-runner/build.sbt
@@ -1,5 +1,5 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
4 changes: 2 additions & 2 deletions sbt/src/sbt-test/tests/t543/project/Ticket543Test.scala
Expand Up @@ -15,9 +15,9 @@ object Ticket543Test extends Build {
testListeners += new TestReportListener {
def testEvent(event: TestEvent) {
for (e <- event.detail.filter(_.status == sbt.testing.Status.Failure)) {
if (e.throwable ne null) {
if (e.throwable != null && e.throwable.isDefined) {
val caw = new CharArrayWriter
e.throwable.printStackTrace(new PrintWriter(caw))
e.throwable.get.printStackTrace(new PrintWriter(caw))
if (caw.toString.contains("Test.scala:"))
marker.createNewFile()
}
Expand Down
2 changes: 1 addition & 1 deletion sbt/src/sbt-test/tests/task/build.sbt
@@ -1,5 +1,5 @@
scalaVersion := "2.10.1"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP15"
libraryDependencies += "org.scalatest" %% "scalatest" % "2.0.M6-SNAP26"

testOptions in Test += Tests.Argument("-r", "custom.CustomReporter")
130 changes: 57 additions & 73 deletions testing/agent/src/main/java/sbt/ForkMain.java
Expand Up @@ -63,68 +63,41 @@ static class ForkError extends Exception {
public String getMessage() { return originalMessage; }
public Exception getCause() { return cause; }
}
static class ForkSelector extends Selector implements Serializable {}
static class ForkSuiteSelector extends ForkSelector {}
static class ForkTestSelector extends ForkSelector {
private String testName;
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 Fingerprint fingerprint;
private Selector selector;
private Status status;
private Throwable throwable;
private OptionalThrowable throwable;
private long duration;
ForkEvent(Event e) {
fullyQualifiedName = e.fullyQualifiedName();
isModule = e.isModule();
Fingerprint rawFingerprint = e.fingerprint();
if (rawFingerprint instanceof SubclassFingerprint)
this.fingerprint = new SubclassFingerscan((SubclassFingerprint) rawFingerprint);
else
this.fingerprint = new AnnotatedFingerscan((AnnotatedFingerprint) rawFingerprint);
selector = forkSelector(e.selector());
status = e.status();
if (e.throwable() != null) throwable = new ForkError(e.throwable());
OptionalThrowable originalThrowable = e.throwable();
if (originalThrowable.isDefined())
this.throwable = new OptionalThrowable(new ForkError(originalThrowable.get()));
else
this.throwable = originalThrowable;
this.duration = e.duration();
}
public String fullyQualifiedName() { return fullyQualifiedName; }
public boolean isModule() { return isModule; }
public Fingerprint fingerprint() { return fingerprint; }
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);
public OptionalThrowable throwable() { return throwable; }
public long duration() { return duration; }
protected Selector forkSelector(Selector selector) {
if (selector instanceof Serializable)
return selector;
else
return new ForkNestedTestSelector((NestedTestSelector) selector);
throw new UnsupportedOperationException("Selector implementation must be Serializable.");
}
}
public static void main(String[] args) throws Exception {
Expand Down Expand Up @@ -172,8 +145,8 @@ void logError(ObjectOutputStream os, String message) {
void logDebug(ObjectOutputStream os, String message) {
write(os, new Object[]{ForkTags.Debug, message});
}
void writeEvents(ObjectOutputStream os, ForkTestDefinition test, ForkEvent[] events) {
write(os, new Object[]{test.name, events});
void writeEvents(ObjectOutputStream os, TaskDef taskDef, ForkEvent[] events) {
write(os, new Object[]{taskDef.fullyQualifiedName(), events});
}
void runTests(ObjectInputStream is, final ObjectOutputStream os) throws Exception {
final boolean ansiCodesSupported = is.readBoolean();
Expand Down Expand Up @@ -212,15 +185,18 @@ void runTests(ObjectInputStream is, final ObjectOutputStream os) throws Exceptio
if (framework == null)
continue;

ArrayList<ForkTestDefinition> filteredTests = new ArrayList<ForkTestDefinition>();
ArrayList<TaskDef> filteredTests = new ArrayList<TaskDef>();
for (Fingerprint testFingerprint : framework.fingerprints()) {
for (ForkTestDefinition test : tests) {
if (matches(testFingerprint, test.fingerprint)) filteredTests.add(test);
// TODO: To pass in correct explicitlySpecified and selectors
if (matches(testFingerprint, test.fingerprint))
filteredTests.add(new TaskDef(test.name, test.fingerprint, false, new Selector[] { new SuiteSelector() }));
}
}
final Runner runner = framework.runner(frameworkArgs, remoteFrameworkArgs, getClass().getClassLoader());
for (ForkTestDefinition test : filteredTests)
runTestSafe(test, runner, loggers, os);
Task[] tasks = runner.tasks(filteredTests.toArray(new TaskDef[filteredTests.size()]));
for (Task task : tasks)
runTestSafe(task, runner, loggers, os);
runner.done();
}
write(os, ForkTags.Done);
Expand All @@ -240,21 +216,19 @@ public Task getTask() {
return task;
}
}
void runTestSafe(ForkTestDefinition test, Runner runner, Logger[] loggers, ObjectOutputStream os) {
void runTestSafe(Task task, Runner runner, Logger[] loggers, ObjectOutputStream os) {
TaskDef taskDef = task.taskDef();
try {
// TODO: To pass in correct explicitlySpecified and selectors
Task task = runner.task(test.name, test.fingerprint, false, new Selector[] { new SuiteSelector() });

List<NestedTask> nestedTasks = new ArrayList<NestedTask>();
for (Task nt : runTest(test, task, loggers, os))
nestedTasks.add(new NestedTask(test.name, nt));
for (Task nt : runTest(taskDef, task, loggers, os))
nestedTasks.add(new NestedTask(taskDef.fullyQualifiedName(), nt));
while (true) {
List<NestedTask> newNestedTasks = new ArrayList<NestedTask>();
int nestedTasksLength = nestedTasks.size();
for (int i = 0; i < nestedTasksLength; i++) {
NestedTask nestedTask = nestedTasks.get(i);
String nestedParentName = nestedTask.getParentName() + "-" + i;
for (Task nt : runTest(new ForkTestDefinition(nestedParentName, test.fingerprint), nestedTask.getTask(), loggers, os)) {
for (Task nt : runTest(nestedTask.getTask().taskDef(), nestedTask.getTask(), loggers, os)) {
newNestedTasks.add(new NestedTask(nestedParentName, nt));
}
}
Expand All @@ -265,10 +239,10 @@ void runTestSafe(ForkTestDefinition test, Runner runner, Logger[] loggers, Objec
}
}
} catch (Throwable t) {
writeEvents(os, test, new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) });
writeEvents(os, taskDef, new ForkEvent[] { testError(os, taskDef, "Uncaught exception when running " + taskDef.fullyQualifiedName() + ": " + t.toString(), t) });
}
}
Task[] runTest(ForkTestDefinition test, Task task, Logger[] loggers, ObjectOutputStream os) {
Task[] runTest(TaskDef taskDef, Task task, Logger[] loggers, ObjectOutputStream os) {
ForkEvent[] events;
Task[] nestedTasks;
try {
Expand All @@ -279,9 +253,9 @@ Task[] runTest(ForkTestDefinition test, Task task, Logger[] loggers, ObjectOutpu
}
catch (Throwable t) {
nestedTasks = new Task[0];
events = new ForkEvent[] { testError(os, test, "Uncaught exception when running " + test.name + ": " + t.toString(), t) };
events = new ForkEvent[] { testError(os, taskDef, "Uncaught exception when running " + taskDef.fullyQualifiedName() + ": " + t.toString(), t) };
}
writeEvents(os, test, events);
writeEvents(os, taskDef, events);
return nestedTasks;
}
void run(ObjectInputStream is, ObjectOutputStream os) throws Exception {
Expand All @@ -301,23 +275,33 @@ void run(ObjectInputStream is, ObjectOutputStream os) throws Exception {
void internalError(Throwable t) {
System.err.println("Internal error when running tests: " + t.toString());
}
ForkEvent testEvent(final String fullyQualifiedName, final Fingerprint fingerprint, final Selector selector, final Status r, final Throwable err) {
ForkEvent testEvent(final String fullyQualifiedName, final Fingerprint fingerprint, final Selector selector, final Status r, final Throwable err, final long duration) {
final OptionalThrowable throwable;
if (err == null)
throwable = new OptionalThrowable();
else
throwable = new OptionalThrowable(err);
return new ForkEvent(new Event() {
public String fullyQualifiedName() { return fullyQualifiedName; }
public boolean isModule() { return fingerprint instanceof SubclassFingerprint ? ((SubclassFingerprint) fingerprint).isModule() : ((AnnotatedFingerprint) fingerprint).isModule(); }
public Fingerprint fingerprint() { return fingerprint; }
public Selector selector() { return selector; }
public Status status() { return r; }
public Throwable throwable() { return err; }
public OptionalThrowable throwable() {
return throwable;
}
public long duration() {
return duration;
}
});
}
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) {
ForkEvent testError(ObjectOutputStream os, TaskDef taskDef, String message) {
logError(os, message);
return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, null);
return testEvent(taskDef.fullyQualifiedName(), taskDef.fingerprint(), new SuiteSelector(), Status.Error, null, 0);
}
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message, Throwable t) {
ForkEvent testError(ObjectOutputStream os, TaskDef taskDef, String message, Throwable t) {
logError(os, message);
write(os, t);
return testEvent(test.name, test.fingerprint, new SuiteSelector(), Status.Error, t);
return testEvent(taskDef.fullyQualifiedName(), taskDef.fingerprint(), new SuiteSelector(), Status.Error, t, 0);
}
}
}

0 comments on commit 5bb4635

Please sign in to comment.