Merge pull request #8271 from eed3si9n/wip/forked-test

[2.x] fix: Fixes forked test error handling on JDK 17
This commit is contained in:
eugene yokota 2025-09-08 00:17:58 -04:00 committed by GitHub
commit 649f071247
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 209 additions and 16 deletions

View File

@ -36,6 +36,10 @@ jobs:
# java: 8 # java: 8
# distribution: zulu # distribution: zulu
# jobtype: 6 # jobtype: 6
- os: ubuntu-latest
java: 21
distribution: zulu
jobtype: 7
- os: ubuntu-latest - os: ubuntu-latest
java: 11 java: 11
distribution: temurin distribution: temurin
@ -164,6 +168,11 @@ jobs:
# sbt -Dsbtzinc.path=$HOME/work/sbt/sbt/zinc -Dsbt.build.version=$BUILD_VERSION -Dsbt.build.fatal=false "+lowerUtils/publishLocal; {zinc}/publishLocal; upperModules/publishLocal" # sbt -Dsbtzinc.path=$HOME/work/sbt/sbt/zinc -Dsbt.build.version=$BUILD_VERSION -Dsbt.build.fatal=false "+lowerUtils/publishLocal; {zinc}/publishLocal; upperModules/publishLocal"
# rm -r $(find $HOME/.sbt/boot -name "*-SNAPSHOT") || true # rm -r $(find $HOME/.sbt/boot -name "*-SNAPSHOT") || true
# sbt -v -Dsbt.version=$BUILD_VERSION "++$SCALA_213; all $UTIL_TESTS; ++$SCALA_212; all $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*" # sbt -v -Dsbt.version=$BUILD_VERSION "++$SCALA_213; all $UTIL_TESTS; ++$SCALA_212; all $UTIL_TESTS; scripted actions/* source-dependencies/*1of3 dependency-management/*1of4 java/*"
- name: Scripted (tests)
if: ${{ matrix.jobtype == 7 }}
shell: bash
run: |
./sbt -v "scripted tests/*"
- name: Build and test lm-coursier (10) - name: Build and test lm-coursier (10)
if: ${{ matrix.jobtype == 10 }} if: ${{ matrix.jobtype == 10 }}
shell: bash shell: bash

View File

@ -1,5 +1,5 @@
val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val scalatest = "org.scalatest" %% "scalatest" % "3.2.19"
scalaVersion := "2.12.20" scalaVersion := "3.7.2"
fork := true fork := true
libraryDependencies += scalatest % Test libraryDependencies += scalatest % Test

View File

@ -0,0 +1,7 @@
import org.scalatest.funsuite.AnyFunSuite
class Test extends AnyFunSuite:
test("bad") {
sys.error("boom")
}
end Test

View File

@ -1,8 +1,8 @@
import org.scalatest.FlatSpec import org.scalatest.funsuite.AnyFunSuite
class Test extends FlatSpec { class Test extends AnyFunSuite:
val v = sys.env.getOrElse("tests.max.value", Int.MaxValue) val v = sys.env.getOrElse("tests.max.value", Int.MaxValue)
"A simple equation" should "hold" in { test("A simple equation should hold") {
assert(Int.MaxValue == v) assert(Int.MaxValue == v)
} }
} end Test

View File

@ -1,6 +1,10 @@
> testFull > testFull
$ copy-file changes/Test.scala src/test/scala/Test.scala $ copy-file changes/Bad.scala src/test/scala/Test.scala
-> testFull
$ copy-file changes/Test.scala src/test/scala/Test.scala
> testFull > testFull

View File

@ -4,14 +4,13 @@ import java.io.{ CharArrayWriter, PrintWriter }
val marker = new File("marker") val marker = new File("marker")
val check = TaskKey[Unit]("check", "Check correct error has been returned.") val check = TaskKey[Unit]("check", "Check correct error has been returned.")
val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" val scalatest = "org.scalatest" %% "scalatest" % "3.2.19"
val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1"
ThisBuild / scalaVersion := "2.12.20" ThisBuild / scalaVersion := "3.7.2"
lazy val root = (project in file(".")). lazy val root = (project in file(".")).
settings( settings(
libraryDependencies ++= List(scalaxml, scalatest), libraryDependencies ++= List(scalatest),
fork := true, fork := true,
testListeners += new TestReportListener { testListeners += new TestReportListener {
def testEvent(event: TestEvent): Unit = { def testEvent(event: TestEvent): Unit = {

View File

@ -1,6 +1,6 @@
import org.scalatest.FunSuite import org.scalatest.funsuite.AnyFunSuite
class SBT543 extends FunSuite { class SBT543 extends AnyFunSuite {
class MyCustomException(message: String) extends RuntimeException(message) class MyCustomException(message: String) extends RuntimeException(message)
test("throws a custom exception") { test("throws a custom exception") {
throw new MyCustomException("this is a custom exception") throw new MyCustomException("this is a custom exception")

View File

@ -1,3 +1,3 @@
-> test -> testFull
> check > check

View File

@ -167,8 +167,8 @@ public class ForkTestMain {
} }
public static class ForkErrorInfo implements Serializable { public static class ForkErrorInfo implements Serializable {
public final long id; public long id;
public final ForkError error; public ForkError error;
public ForkErrorInfo(long id, ForkError error) { public ForkErrorInfo(long id, ForkError error) {
this.id = id; this.id = id;

View File

@ -0,0 +1,18 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.worker1;
public class PersistedException extends Throwable {
private String className;
public PersistedException(String message, Throwable cause, String className) {
super(message, cause);
this.className = className;
}
}

View File

@ -0,0 +1,155 @@
/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt.internal.worker1;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.ArrayList;
class ThrowableAdapterFactory implements TypeAdapterFactory {
private ThrowableAdapterFactory() {}
public static final ThrowableAdapterFactory INSTANCE = new ThrowableAdapterFactory();
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
// Only handles Throwable and subclasses; let other factories handle any other type
if (!Throwable.class.isAssignableFrom(type.getRawType())) {
return null;
}
@SuppressWarnings("unchecked")
TypeAdapter<T> adapter =
(TypeAdapter<T>)
new TypeAdapter<Throwable>() {
@Override
public Throwable read(JsonReader in) throws IOException {
String message = null;
String type = null;
ArrayList<StackTraceElement> stackTraces = new ArrayList<>();
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
if (name.equals("message")) {
message = in.nextString();
} else if (name.equals("type")) {
type = in.nextString();
} else if (name.equals("staceTrace")) {
in.beginArray();
while (in.hasNext()) {
StackTraceElement item = readStaceTraceElement(in);
stackTraces.add(item);
}
in.endArray();
} else {
in.skipValue();
}
}
in.endObject();
Throwable ex = new PersistedException(message, null, type);
StackTraceElement array[] = new StackTraceElement[stackTraces.size()];
ex.setStackTrace(stackTraces.toArray(array));
return new ForkTestMain.ForkError(ex);
}
@Override
public void write(JsonWriter out, Throwable value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.beginObject();
// Include exception type name to give more context; for example
// NullPointerException might
// not have a message
out.name("type");
out.value(value.getClass().getSimpleName());
out.name("message");
out.value(value.getMessage());
Throwable cause = value.getCause();
if (cause != null) {
out.name("cause");
write(out, cause);
}
Throwable[] suppressedArray = value.getSuppressed();
if (suppressedArray.length > 0) {
out.name("suppressed");
out.beginArray();
for (Throwable suppressed : suppressedArray) {
write(out, suppressed);
}
out.endArray();
}
StackTraceElement[] staceTrace = value.getStackTrace();
if (staceTrace.length > 0) {
out.name("staceTrace");
out.beginArray();
for (StackTraceElement item : staceTrace) {
writeStaceTraceElement(out, item);
}
out.endArray();
}
out.endObject();
}
public StackTraceElement readStaceTraceElement(JsonReader in) throws IOException {
in.beginObject();
String className = null;
String methodName = null;
String fileName = null;
Integer lineNumber = null;
while (in.hasNext()) {
String name = in.nextName();
if (name.equals("className")) {
className = in.nextString();
} else if (name.equals("methodName")) {
methodName = in.nextString();
} else if (name.equals("fileName")) {
fileName = in.nextString();
} else if (name.equals("lineNumber")) {
lineNumber = in.nextInt();
} else {
in.skipValue();
}
}
in.endObject();
StackTraceElement retval =
new StackTraceElement(className, methodName, fileName, lineNumber);
return retval;
}
public void writeStaceTraceElement(JsonWriter out, StackTraceElement value)
throws IOException {
out.beginObject();
out.name("className");
out.value(value.getClassName());
out.name("methodName");
out.value(value.getMethodName());
out.name("fileName");
out.value(value.getFileName());
out.name("lineNumber");
out.value(value.getLineNumber());
out.endObject();
}
};
return adapter;
}
}

View File

@ -51,6 +51,7 @@ public final class WorkerMain {
return new GsonBuilder() return new GsonBuilder()
.registerTypeAdapterFactory(fingerprintFac) .registerTypeAdapterFactory(fingerprintFac)
.registerTypeAdapterFactory(selectorFac) .registerTypeAdapterFactory(selectorFac)
.registerTypeAdapterFactory(ThrowableAdapterFactory.INSTANCE)
.create(); .create();
} }