Skip to content

Commit

Permalink
Handle exceptions not caught by the test framework when forking. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
harrah committed Jan 28, 2013
1 parent 8d11d10 commit 9702d3d
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 16 deletions.
10 changes: 10 additions & 0 deletions sbt/src/sbt-test/tests/fork-uncaught/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
scalaVersion := "2.9.2"

organization := "org.example"

name := "fork-uncaught"

fork := true

libraryDependencies += "org.scalatest" % "scalatest_2.9.2" % "2.0.M3" % "test" intransitive()

3 changes: 3 additions & 0 deletions sbt/src/sbt-test/tests/fork-uncaught/src/main/scala/Foo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package foo.test

object Foo { val foo = 5 }
15 changes: 15 additions & 0 deletions sbt/src/sbt-test/tests/fork-uncaught/src/test/scala/FooTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package foo.test

import org.scalatest.{FunSpec, ShouldMatchers}

class FooTest extends FunSpec with ShouldMatchers {

if(java.lang.Boolean.getBoolean("test.init.fail"))
sys.error("exception during construction")

describe("Foo.foo should") {
it("always return 5") {
Foo.foo should equal (5)
}
}
}
3 changes: 3 additions & 0 deletions sbt/src/sbt-test/tests/fork-uncaught/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
> test
> 'set javaOptions in Test += "-Dtest.init.fail=true"'
-> test
76 changes: 60 additions & 16 deletions testing/agent/src/main/java/sbt/ForkMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,20 @@ void write(ObjectOutputStream os, Object obj) {
throw new RunAborted(e);
}
}
void logError(ObjectOutputStream os, String message) {
write(os, new Object[]{ForkTags.Error, message});
}
void writeEvents(ObjectOutputStream os, ForkTestDefinition test, ForkEvent[] events) {
write(os, new Object[]{test.name, events});
}
void runTests(ObjectInputStream is, final ObjectOutputStream os) throws Exception {
final boolean ansiCodesSupported = is.readBoolean();
final ForkTestDefinition[] tests = (ForkTestDefinition[]) is.readObject();
int nFrameworks = is.readInt();
Logger[] loggers = {
new Logger() {
public boolean ansiCodesSupported() { return ansiCodesSupported; }
public void error(String s) { write(os, new Object[]{ForkTags.Error, s}); }
public void error(String s) { logError(os, s); }
public void warn(String s) { write(os, new Object[]{ForkTags.Warn, s}); }
public void info(String s) { write(os, new Object[]{ForkTags.Info, s}); }
public void debug(String s) { write(os, new Object[]{ForkTags.Debug, s}); }
Expand All @@ -137,7 +143,7 @@ void runTests(ObjectInputStream is, final ObjectOutputStream os) throws Exceptio
try {
framework = (Framework) Class.forName(implClassName).newInstance();
} catch (ClassNotFoundException e) {
write(os, new Object[]{ForkTags.Error, "Framework implementation '" + implClassName + "' not present."});
logError(os, "Framework implementation '" + implClassName + "' not present.");
continue;
}

Expand All @@ -148,28 +154,66 @@ void runTests(ObjectInputStream is, final ObjectOutputStream os) throws Exceptio
}
}
final org.scalatools.testing.Runner runner = framework.testRunner(getClass().getClassLoader(), loggers);
for (ForkTestDefinition test : filteredTests) {
final List<ForkEvent> events = new ArrayList<ForkEvent>();
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 {
write(os, new Object[]{ForkTags.Error, "Framework '" + framework + "' does not support test '" + test.name + "'"});
}
write(os, new Object[]{test.name, events.toArray(new ForkEvent[events.size()])});
}
for (ForkTestDefinition test : filteredTests)
runTestSafe(test, runner, framework, frameworkArgs, os);
}
write(os, ForkTags.Done);
is.readObject();
}
void run(ObjectInputStream is, final ObjectOutputStream os) throws Exception {
void runTestSafe(ForkTestDefinition test, org.scalatools.testing.Runner runner, Framework framework, String[] frameworkArgs, ObjectOutputStream os) {
ForkEvent[] events;
try {
events = runTest(test, runner, framework, frameworkArgs, 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) {
final List<ForkEvent> events = new ArrayList<ForkEvent>();
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 + "'"));
}
return events.toArray(new ForkEvent[events.size()]);
}
void run(ObjectInputStream is, ObjectOutputStream os) throws Exception {
try {
runTests(is, os);
} catch (RunAborted e) {
System.err.println("Internal error when running tests: " + e.getMessage());
internalError(e);
} catch (Throwable t) {
try {
logError(os, "Uncaught exception when running tests: " + t.toString());
write(os, t);
} catch (Throwable t2) {
internalError(t2);
}
}
}
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) {
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; }
});
}
ForkEvent testError(ObjectOutputStream os, ForkTestDefinition test, String message) {
logError(os, message);
return testEvent(test.name, message, Result.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);
}
}
}

0 comments on commit 9702d3d

Please sign in to comment.