@@ -59,16 +59,19 @@ trait RunnerOrchestration {
5959 def runMain (classPath : String , toolArgs : ToolArgs )(implicit summaryReport : SummaryReporting ): Status =
6060 monitor.runMain(classPath)
6161
62+ /** Each method of Debuggee can be called only once, in the order of definition.*/
6263 trait Debuggee :
63- /** the jdi port to connect the debugger */
64- def jdiPort : Int
64+ /** read the jdi port to connect the debugger */
65+ def readJdiPort () : Int
6566 /** start the main method in the background */
6667 def launch (): Unit
68+ /** wait until the end of the main method */
69+ def exit (): Status
6770
6871 /** Provide a Debuggee for debugging the Test class's main method
69- * @param f the debugging flow: set breakpoints, launch main class, pause, step, evaluate etc
72+ * @param f the debugging flow: set breakpoints, launch main class, pause, step, evaluate, exit etc
7073 */
71- def debugMain (classPath : String )(f : Debuggee => Unit )(implicit summaryReport : SummaryReporting ): Status =
74+ def debugMain (classPath : String )(f : Debuggee => Unit )(implicit summaryReport : SummaryReporting ): Unit =
7275 assert(debugMode, " debugMode is disabled" )
7376 monitor.debugMain(classPath)(f)
7477
@@ -92,14 +95,31 @@ trait RunnerOrchestration {
9295 def runMain (classPath : String )(implicit summaryReport : SummaryReporting ): Status =
9396 withRunner(_.runMain(classPath))
9497
95- def debugMain (classPath : String )(f : Debuggee => Unit )(implicit summaryReport : SummaryReporting ): Status =
98+ def debugMain (classPath : String )(f : Debuggee => Unit )(implicit summaryReport : SummaryReporting ): Unit =
9699 withRunner(_.debugMain(classPath)(f))
97100
98- // A JVM process and its JDI port for debugging, if debugMode is enabled.
99- private class RunnerProcess (p : Process , val jdiPort : Option [Int ]):
100- val stdout = new BufferedReader (new InputStreamReader (p.getInputStream(), StandardCharsets .UTF_8 ))
101- val stdin = new PrintStream (p.getOutputStream(), /* autoFlush = */ true )
101+ private class RunnerProcess (p : Process ):
102+ private val stdout = new BufferedReader (new InputStreamReader (p.getInputStream(), StandardCharsets .UTF_8 ))
103+ private val stdin = new PrintStream (p.getOutputStream(), /* autoFlush = */ true )
104+
105+ def readLine (): String =
106+ stdout.readLine() match
107+ case s " Listening for transport dt_socket at address: $port" =>
108+ throw new IOException (
109+ s " Unexpected transport dt_socket message. " +
110+ " The port is going to be lost and no debugger will be able to connect."
111+ )
112+ case line => line
113+
114+ def printLine (line : String ): Unit = stdin.println(line)
115+
116+ def getJdiPort (): Int =
117+ stdout.readLine() match
118+ case s " Listening for transport dt_socket at address: $port" => port.toInt
119+ case line => throw new IOException (s " Failed getting JDI port of child JVM: got $line" )
120+
102121 export p .{exitValue , isAlive , destroy }
122+ end RunnerProcess
103123
104124 private class Runner (private var process : RunnerProcess ):
105125
@@ -111,7 +131,7 @@ trait RunnerOrchestration {
111131 */
112132 def isAlive : Boolean =
113133 try { process.exitValue(); false }
114- catch { case _ : IllegalThreadStateException => true }
134+ catch case _ : IllegalThreadStateException => true
115135
116136 /** Destroys the underlying process and kills IO streams */
117137 def kill (): Unit =
@@ -123,51 +143,50 @@ trait RunnerOrchestration {
123143 assert(process ne null , " Runner was killed and then reused without setting a new process" )
124144 awaitStatusOrRespawn(startMain(classPath))
125145
126- def debugMain (classPath : String )(f : Debuggee => Unit ): Status =
146+ def debugMain (classPath : String )(f : Debuggee => Unit ): Unit =
127147 assert(process ne null , " Runner was killed and then reused without setting a new process" )
128- assert(process.jdiPort.isDefined, " Runner has not been started in debug mode" )
129148
130- var mainFuture : Future [Status ] = null
131149 val debuggee = new Debuggee :
132- def jdiPort : Int = process.jdiPort.get
133- def launch (): Unit =
134- mainFuture = startMain(classPath)
150+ var mainFuture : Future [Status ] = null
151+ def readJdiPort (): Int = process.getJdiPort()
152+ def launch (): Unit = mainFuture = startMain(classPath)
153+ def exit (): Status =
154+ awaitStatusOrRespawn(mainFuture)
135155
136156 try f(debuggee)
137- catch case debugFailure : Throwable =>
138- if mainFuture != null then awaitStatusOrRespawn(mainFuture)
139- throw debugFailure
140-
141- assert(mainFuture ne null , " main method not started by debugger" )
142- awaitStatusOrRespawn(mainFuture)
157+ catch case e : Throwable =>
158+ // if debugging failed it is safer to respawn a new process
159+ respawn()
160+ throw e
143161 end debugMain
144162
145163 private def startMain (classPath : String ): Future [Status ] =
146164 // pass classpath to running process
147- process.stdin.println (classPath)
165+ process.printLine (classPath)
148166
149167 // Create a future reading the object:
150168 Future :
151169 val sb = new StringBuilder
152170
153- var childOutput : String = process.stdout. readLine()
171+ var childOutput : String = process.readLine()
154172
155173 // Discard all messages until the test starts
156174 while (childOutput != ChildJVMMain .MessageStart && childOutput != null )
157- childOutput = process.stdout. readLine()
158- childOutput = process.stdout. readLine()
175+ childOutput = process.readLine()
176+ childOutput = process.readLine()
159177
160178 while childOutput != ChildJVMMain .MessageEnd && childOutput != null do
161179 sb.append(childOutput).append(System .lineSeparator)
162- childOutput = process.stdout. readLine()
180+ childOutput = process.readLine()
163181
164- if ( process.isAlive() && childOutput != null ) Success (sb.toString)
182+ if process.isAlive() && childOutput != null then Success (sb.toString)
165183 else Failure (sb.toString)
166184 end startMain
167185
168186 // wait status of the main class execution, respawn if failure or timeout
169187 private def awaitStatusOrRespawn (future : Future [Status ]): Status =
170- val status = try Await .result(future, maxDuration)
188+ val status =
189+ try Await .result(future, maxDuration)
171190 catch case _ : TimeoutException => Timeout
172191 // handle failures
173192 status match
@@ -178,13 +197,13 @@ trait RunnerOrchestration {
178197 // Makes the encapsulating RunnerMonitor spawn a new runner
179198 private def respawn (): Unit =
180199 process.destroy()
181- process = createProcess
200+ process = createProcess()
182201 end Runner
183202
184203 /** Create a process which has the classpath of the `ChildJVMMain` and the
185204 * scala library.
186205 */
187- private def createProcess : RunnerProcess = {
206+ private def createProcess () : RunnerProcess =
188207 val url = classOf [ChildJVMMain ].getProtectionDomain.getCodeSource.getLocation
189208 val cp = Paths .get(url.toURI).toString + JFile .pathSeparator + Properties .scalaLibrary
190209 val javaBin = Paths .get(sys.props(" java.home" ), " bin" , " java" ).toString
@@ -196,15 +215,7 @@ trait RunnerOrchestration {
196215 .redirectInput(ProcessBuilder .Redirect .PIPE )
197216 .redirectOutput(ProcessBuilder .Redirect .PIPE )
198217 .start()
199-
200- val jdiPort = Option .when(debugMode):
201- val reader = new BufferedReader (new InputStreamReader (process.getInputStream, StandardCharsets .UTF_8 ))
202- reader.readLine() match
203- case s " Listening for transport dt_socket at address: $port" => port.toInt
204- case line => throw new IOException (s " Failed getting JDI port of child JVM: got $line" )
205-
206- RunnerProcess (process, jdiPort)
207- }
218+ RunnerProcess (process)
208219
209220 private val freeRunners = mutable.Queue .empty[Runner ]
210221 private val busyRunners = mutable.Set .empty[Runner ]
@@ -213,7 +224,7 @@ trait RunnerOrchestration {
213224 while (freeRunners.isEmpty && busyRunners.size >= numberOfSlaves) wait()
214225
215226 val runner =
216- if (freeRunners.isEmpty) new Runner (createProcess)
227+ if (freeRunners.isEmpty) new Runner (createProcess() )
217228 else freeRunners.dequeue()
218229 busyRunners += runner
219230
0 commit comments