mirror of
https://github.com/sbt/sbt.git
synced 2026-09-07 02:55:23 +02:00
fix: Fixes forked test error handling on JDK 17
**Problem** Forked tests apparently incorrectly returns success if an exception is thrown on JDK 17 and up, due to exception failing to persist. **Solution** This adds custom codec for Throwable to workaround this issue.
This commit is contained in:
@@ -167,8 +167,8 @@ public class ForkTestMain {
|
||||
}
|
||||
|
||||
public static class ForkErrorInfo implements Serializable {
|
||||
public final long id;
|
||||
public final ForkError error;
|
||||
public long id;
|
||||
public ForkError error;
|
||||
|
||||
public ForkErrorInfo(long id, ForkError error) {
|
||||
this.id = id;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ public final class WorkerMain {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapterFactory(fingerprintFac)
|
||||
.registerTypeAdapterFactory(selectorFac)
|
||||
.registerTypeAdapterFactory(ThrowableAdapterFactory.INSTANCE)
|
||||
.create();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user