Skip to content

Commit 3c17fcb

Browse files
committed
Fix scala-js#6: Throw a structured exception when a command fails to start
1 parent 1debceb commit 3c17fcb

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

js-envs/src/main/scala/org/scalajs/jsenv/ExternalJSRun.scala

+10-1
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,18 @@ object ExternalJSRun {
140140

141141
config.logger.debug("Starting process: " + command.mkString(" "))
142142

143-
builder.start()
143+
try {
144+
builder.start()
145+
} catch {
146+
case t: Throwable =>
147+
throw new FailedToStartException(command, t)
148+
}
144149
}
145150

151+
final case class FailedToStartException(
152+
command: List[String], cause: Throwable)
153+
extends Exception(s"failed to start command $command", cause)
154+
146155
final case class NonZeroExitException(retVal: Int)
147156
extends Exception(s"exited with code $retVal")
148157

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Scala.js JS Envs (https://github.com/scala-js/scala-js-js-envs)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.jsenv
14+
15+
import org.junit.Test
16+
import org.junit.Assert._
17+
18+
import scala.concurrent.{Await, Future}
19+
import scala.concurrent.duration._
20+
21+
import scala.util.Failure
22+
23+
/** Tests of failure behavior for ExternalJSRun.
24+
*
25+
* Success behavior is tested via NodeJSSuite.
26+
*/
27+
class ExternalJSRunFailureTest {
28+
29+
def assertFails(future: Future[Unit])(
30+
pf: PartialFunction[Throwable, Unit]) = {
31+
Await.ready(future, 1.seconds).value.get match {
32+
case Failure(t) if pf.isDefinedAt(t) => // OK
33+
34+
case result =>
35+
result.get
36+
fail("run succeeded unexpectedly")
37+
}
38+
}
39+
40+
@Test
41+
def nonExistentCommand: Unit = {
42+
val cmd = List("nonexistent-cmd")
43+
val run = ExternalJSRun.start(cmd, ExternalJSRun.Config()) { _ =>
44+
fail("unexpected call to input")
45+
}
46+
47+
assertFails(run.future) {
48+
case ExternalJSRun.FailedToStartException(`cmd`, _) => // OK
49+
}
50+
}
51+
52+
@Test
53+
def failingCommand: Unit = {
54+
val run = ExternalJSRun.start(List("false"), ExternalJSRun.Config())(_.close())
55+
56+
assertFails(run.future) {
57+
case ExternalJSRun.NonZeroExitException(_) => // OK
58+
}
59+
}
60+
61+
@Test
62+
def abortedCommand: Unit = {
63+
val run = ExternalJSRun.start(List("cat"), ExternalJSRun.Config()) { _ =>
64+
// Do not close stdin so cat keeps running
65+
}
66+
67+
run.close()
68+
69+
assertFails(run.future) {
70+
case ExternalJSRun.ClosedException() => // OK
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)