From 774a2da58c09c7ce12c26ea0b04802404ef45d83 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 27 Nov 2025 09:49:53 -0800 Subject: [PATCH 1/5] Restore test i23245a --- tests/disabled/i23245a/test_2.scala | 6 ------ tests/{disabled => run}/i23245a/api.scala | 0 tests/run/i23245a/test_2.scala | 4 ++++ tests/run/i23245d/BaseTest_1.scala | 13 +++++++++++++ tests/run/i23245d/Test_2.scala | 4 ++++ 5 files changed, 21 insertions(+), 6 deletions(-) delete mode 100644 tests/disabled/i23245a/test_2.scala rename tests/{disabled => run}/i23245a/api.scala (100%) create mode 100644 tests/run/i23245a/test_2.scala create mode 100644 tests/run/i23245d/BaseTest_1.scala create mode 100644 tests/run/i23245d/Test_2.scala diff --git a/tests/disabled/i23245a/test_2.scala b/tests/disabled/i23245a/test_2.scala deleted file mode 100644 index faf0960c29cb..000000000000 --- a/tests/disabled/i23245a/test_2.scala +++ /dev/null @@ -1,6 +0,0 @@ - - -object Test extends logadapter.Api.SelfLogging: - def main(args: Array[String]): Unit = - summon[logadapter.LogAdapter].info("Hello") - diff --git a/tests/disabled/i23245a/api.scala b/tests/run/i23245a/api.scala similarity index 100% rename from tests/disabled/i23245a/api.scala rename to tests/run/i23245a/api.scala diff --git a/tests/run/i23245a/test_2.scala b/tests/run/i23245a/test_2.scala new file mode 100644 index 000000000000..c04ae43058b0 --- /dev/null +++ b/tests/run/i23245a/test_2.scala @@ -0,0 +1,4 @@ +object Test extends logadapter.Api.SelfLogging: + def main(args: Array[String]): Unit = + try summon[logadapter.LogAdapter].info("Hello") + catch t => t.printStackTrace(System.out) diff --git a/tests/run/i23245d/BaseTest_1.scala b/tests/run/i23245d/BaseTest_1.scala new file mode 100644 index 000000000000..4a23904cb4eb --- /dev/null +++ b/tests/run/i23245d/BaseTest_1.scala @@ -0,0 +1,13 @@ +abstract class BaseTest { + def genName(): String = "outerAccess" + trait Fixture { + lazy val service: Service = new Service { + val a = genName() + def doIt(a: String): Int = 0 + } + } +} + +trait Service { + def doIt(a: String): Int +} diff --git a/tests/run/i23245d/Test_2.scala b/tests/run/i23245d/Test_2.scala new file mode 100644 index 000000000000..9169e6668433 --- /dev/null +++ b/tests/run/i23245d/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends BaseTest { + def main(args: Array[String]): Unit = + new Fixture { service.doIt("test") } +} From 06b466db7c54f42f2ac9bae8b09007851a40b623 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 17 Dec 2025 10:50:51 -0800 Subject: [PATCH 2/5] Comment on weird test --- tests/run/2772.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run/2772.scala b/tests/run/2772.scala index fcde4c363b35..39c6b93489e6 100644 --- a/tests/run/2772.scala +++ b/tests/run/2772.scala @@ -1,5 +1,6 @@ import java.io.OutputStream +// System.err is ostensibly final but cannot be inlined object Test { def main(args: Array[String]): Unit = { val oldErr = System.err From d42d8f77fa3b6a9ed66f272009faeeb5165e6f63 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 17 Dec 2025 10:51:23 -0800 Subject: [PATCH 3/5] Rename weird test --- tests/run/{2772.scala => i2772.scala} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/run/{2772.scala => i2772.scala} (100%) diff --git a/tests/run/2772.scala b/tests/run/i2772.scala similarity index 100% rename from tests/run/2772.scala rename to tests/run/i2772.scala From 47abcd09c0a8e4377465bd649f22be4e91e6d377 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 17 Dec 2025 11:51:55 -0800 Subject: [PATCH 4/5] Protect stdio from malicious tests --- .../test/dotty/tools/vulpix/ChildJVMMain.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java index a6873ead1968..58bc483d2e60 100644 --- a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java +++ b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java @@ -41,11 +41,27 @@ private static void runMain(String dir) throws Exception { } public static void main(String[] args) throws Exception { - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - while (true) { - runMain(stdin.readLine()); - System.out.println(MessageEnd); - } + while (true) { + var err = System.err; + var out = System.out; + var in = System.in; + try { + runMain(stdin.readLine()); + } + finally { + if (err != System.err) { + System.setErr(err); + } + if (out != System.out) { + System.setOut(out); + } + if (in != System.in) { + System.setIn(in); + } + } + System.out.println(MessageEnd); + } } } From a3d3d4b134c34772127f658404c6d0f17f8810fa Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 18 Dec 2025 11:46:22 -0800 Subject: [PATCH 5/5] Leverage Scala and its idioms --- .../test/dotty/tools/vulpix/ChildJVMMain.java | 67 ------------------- .../dotty/tools/vulpix/ChildJVMMain.scala | 52 ++++++++++++++ .../dotty/tools/vulpix/ParallelTesting.scala | 28 ++++---- .../tools/vulpix/RunnerOrchestration.scala | 41 +++++------- tests/vulpix-tests/unit/timeout.scala | 6 +- 5 files changed, 84 insertions(+), 110 deletions(-) delete mode 100644 compiler/test/dotty/tools/vulpix/ChildJVMMain.java create mode 100644 compiler/test/dotty/tools/vulpix/ChildJVMMain.scala diff --git a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java b/compiler/test/dotty/tools/vulpix/ChildJVMMain.java deleted file mode 100644 index 58bc483d2e60..000000000000 --- a/compiler/test/dotty/tools/vulpix/ChildJVMMain.java +++ /dev/null @@ -1,67 +0,0 @@ -package dotty.tools.vulpix; - -import java.io.File; -import java.io.InputStreamReader; -import java.io.BufferedReader; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.lang.reflect.Method; - -public class ChildJVMMain { - static final String MessageStart = "##THIS IS THE START FOR ME, HELLO##"; - static final String MessageEnd = "##THIS IS THE END FOR ME, GOODBYE##"; - - private static void runMain(String dir) throws Exception { - Method meth = null; - Object[] args = new Object[]{ new String[]{ } }; - try { - String jcp = System.getProperty("java.class.path"); - String sep = File.pathSeparator; - System.setProperty("java.class.path", jcp == null ? dir : dir + sep + jcp); - - ArrayList cp = new ArrayList<>(); - for (String path : dir.split(sep)) - cp.add(new File(path).toURI().toURL()); - - URLClassLoader ucl = new URLClassLoader(cp.toArray(new URL[cp.size()])); - - Class cls = ucl.loadClass("Test"); - meth = cls.getMethod("main", String[].class); - } - catch (Throwable e) { - // Include the failure stack trace to the test output - System.out.println(MessageStart); - e.printStackTrace(); - throw e; - } - System.out.println(MessageStart); - - meth.invoke(null, args); - } - - public static void main(String[] args) throws Exception { - BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); - - while (true) { - var err = System.err; - var out = System.out; - var in = System.in; - try { - runMain(stdin.readLine()); - } - finally { - if (err != System.err) { - System.setErr(err); - } - if (out != System.out) { - System.setOut(out); - } - if (in != System.in) { - System.setIn(in); - } - } - System.out.println(MessageEnd); - } - } -} diff --git a/compiler/test/dotty/tools/vulpix/ChildJVMMain.scala b/compiler/test/dotty/tools/vulpix/ChildJVMMain.scala new file mode 100644 index 000000000000..c2816bd399d9 --- /dev/null +++ b/compiler/test/dotty/tools/vulpix/ChildJVMMain.scala @@ -0,0 +1,52 @@ +package dotty.tools.vulpix + +import java.io.{BufferedReader, File, InputStreamReader} +import java.net.{URL, URLClassLoader} +import java.util.ArrayList + +object ChildJVMMain: + val MessageStart = "##THIS IS THE START FOR ME, HELLO##" + val MessageEnd = "##THIS IS THE END FOR ME, GOODBYE##" + + def runMain(dir: String): Unit = + def meth = + val jcp = System.getProperty("java.class.path") + val sep = File.pathSeparator + System.setProperty("java.class.path", if jcp == null then dir else dir + sep + jcp) + + val loader = + val cp = ArrayList[URL]() + val paths = dir.split(sep) + for path <- paths do + cp.add(File(path).toURI().toURL()) + val urls = cp.toArray(Array.ofDim[URL](cp.size)) + URLClassLoader(urls) + + val cls = loader.loadClass("Test") + cls.getMethod("main", classOf[Array[String]]) + end meth + val m = + try meth + catch t => + // Include the failure stack trace to the test output + System.out.println(MessageStart) + t.printStackTrace() + throw t + System.out.println(MessageStart); + m.invoke(null, null) + + def main(args: Array[String]): Unit = + inline def savingSystem[T](inline body: => T): T = + val savedIn = System.in + val savedOut = System.out + val savedErr = System.err + try body + finally + System.setIn(savedIn) + System.setOut(savedOut) + System.setErr(savedErr) + val stdin = BufferedReader(InputStreamReader(System.in)) + while true do + savingSystem: + runMain(stdin.readLine()) + println(MessageEnd) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 3a2f261a8e16..a8f82c6cb61d 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -1292,21 +1292,19 @@ trait ParallelTesting extends RunnerOrchestration: this } - /** Extract `Failure` set and render from `Test` */ - private def reasonsForFailure(test: Test): String = { - val failureReport = - if test.failureCount == 0 then "" - else s"encountered ${test.failureCount} test failure(s):\n" - - failureReport + test.failureReasons.collect { - case test.TimeoutFailure(title) => - s" - test '$title' timed out" - case test.JavaCompilationFailure(msg) => - s" - java compilation failed with:\n${ msg.linesIterator.map(" " + _).mkString("\n") }" - case test.Generic => - " - generic failure (see test output)" - }.mkString("\n") - } + /** Extracts `Failure` set and renders from `Test`. */ + private def reasonsForFailure(test: Test): String = + if test.failureCount == 0 then "" + else + test.failureReasons.collect { + case test.TimeoutFailure(title) => + s" - test '$title' timed out" + case test.JavaCompilationFailure(msg) => + val header = " - java compilation failed with:\n" + msg.linesIterator.map(" " + _).mkString(header, "\n", "") + case test.Generic => + " - generic failure (see test output)" + }.mkString(s"encountered ${test.failureCount} test failure(s):\n", "\n", "") /** Copies `file` to `dir` - taking into account if `file` is a directory, * and if so copying recursively diff --git a/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala index df4d692175f9..599999737f6a 100644 --- a/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala +++ b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala @@ -98,40 +98,35 @@ trait RunnerOrchestration { def readLine(): String = stdout.readLine() match - case s"Listening for transport dt_socket at address: $port" => - throw new IOException( - s"Unexpected transport dt_socket message." + - " The port is going to be lost and no debugger will be able to connect." - ) - case line => line + case s"Listening for transport dt_socket at address: $port" => + throw IOException( + "Unexpected transport dt_socket message." + + " The port is going to be lost and no debugger will be able to connect." + ) + case line => line def printLine(line: String): Unit = stdin.println(line) def getJdiPort(): Int = stdout.readLine() match - case s"Listening for transport dt_socket at address: $port" => port.toInt - case line => throw new IOException(s"Failed getting JDI port of child JVM: got $line") + case s"Listening for transport dt_socket at address: $port" => port.toInt + case line => throw IOException(s"Failed getting JDI port of child JVM: got $line") - export p.{exitValue, isAlive, destroy} + def isAlive: Boolean = p.isAlive // export p.isAlive sans parens + + export p.{exitValue, destroy} end RunnerProcess private class Runner(private var process: RunnerProcess): - /** Checks if `process` is still alive - * - * When `process.exitValue()` is called on an active process the caught - * exception is thrown. As such we can know if the subprocess exited or - * not. - */ - def isAlive: Boolean = - try { process.exitValue(); false } - catch case _: IllegalThreadStateException => true - - /** Destroys the underlying process and kills IO streams */ + /** Checks whether the underlying process is still alive. */ + def isAlive: Boolean = process.isAlive + + /** Destroys the underlying process and kills IO streams. */ def kill(): Unit = if (process ne null) process.destroy() process = null - /** Blocks less than `maxDuration` while running `Test.main` from `dir` */ + /** Blocks less than `maxDuration` while running `Test.main` from `dir`. */ def runMain(classPath: String): Status = assert(process ne null, "Runner was killed and then reused without setting a new process") awaitStatusOrRespawn(startMain(classPath)) @@ -172,7 +167,7 @@ trait RunnerOrchestration { sb.append(childOutput).append(System.lineSeparator) childOutput = process.readLine() - if process.isAlive() && childOutput != null then Success(sb.toString) + if isAlive && childOutput != null then Success(sb.toString) else Failure(sb.toString) end startMain @@ -198,7 +193,7 @@ trait RunnerOrchestration { * scala library. */ private def createProcess(): RunnerProcess = - val url = classOf[ChildJVMMain].getProtectionDomain.getCodeSource.getLocation + val url = classOf[ChildJVMMain.type].getProtectionDomain.getCodeSource.getLocation val cp = Paths.get(url.toURI).toString + JFile.pathSeparator + Properties.scalaLibrary val javaBin = Paths.get(sys.props("java.home"), "bin", "java").toString val args = Seq("-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US", "-Xmx1g", "-cp", cp) ++ diff --git a/tests/vulpix-tests/unit/timeout.scala b/tests/vulpix-tests/unit/timeout.scala index ce9c9a6693d6..e1b850d814f1 100644 --- a/tests/vulpix-tests/unit/timeout.scala +++ b/tests/vulpix-tests/unit/timeout.scala @@ -1,5 +1 @@ -object Test { - def main(args: Array[String]): Unit = { - Thread.sleep(10 * 1000) - } -} +@main def Test = Thread.sleep(1000 * 1000)