diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9299747a..438f4d05c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,10 @@ jobs: # java: 8 # distribution: zulu # jobtype: 6 + - os: ubuntu-latest + java: 21 + distribution: zulu + jobtype: 7 - os: ubuntu-latest java: 11 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" # 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/*" + - name: Scripted (tests) + if: ${{ matrix.jobtype == 7 }} + shell: bash + run: | + ./sbt -v "scripted tests/*" - name: Build and test lm-coursier (10) if: ${{ matrix.jobtype == 10 }} shell: bash diff --git a/sbt-app/src/sbt-test/tests/fork2/build.sbt b/sbt-app/src/sbt-test/tests/fork2/build.sbt index 8e239b5da..0fae8ec59 100644 --- a/sbt-app/src/sbt-test/tests/fork2/build.sbt +++ b/sbt-app/src/sbt-test/tests/fork2/build.sbt @@ -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 libraryDependencies += scalatest % Test diff --git a/sbt-app/src/sbt-test/tests/fork2/changes/Bad.scala b/sbt-app/src/sbt-test/tests/fork2/changes/Bad.scala new file mode 100644 index 000000000..15e65e7dc --- /dev/null +++ b/sbt-app/src/sbt-test/tests/fork2/changes/Bad.scala @@ -0,0 +1,7 @@ +import org.scalatest.funsuite.AnyFunSuite + +class Test extends AnyFunSuite: + test("bad") { + sys.error("boom") + } +end Test diff --git a/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala b/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala index f45303e11..5168a43af 100644 --- a/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala +++ b/sbt-app/src/sbt-test/tests/fork2/changes/Test.scala @@ -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) - "A simple equation" should "hold" in { + test("A simple equation should hold") { assert(Int.MaxValue == v) } -} +end Test diff --git a/sbt-app/src/sbt-test/tests/fork2/test b/sbt-app/src/sbt-test/tests/fork2/test index bf3b24774..47ff7d4b0 100644 --- a/sbt-app/src/sbt-test/tests/fork2/test +++ b/sbt-app/src/sbt-test/tests/fork2/test @@ -1,6 +1,10 @@ > 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 diff --git a/sbt-app/src/sbt-test/tests/t543/build.sbt b/sbt-app/src/sbt-test/tests/t543/build.sbt index 944e65e4e..fe5f694ba 100644 --- a/sbt-app/src/sbt-test/tests/t543/build.sbt +++ b/sbt-app/src/sbt-test/tests/t543/build.sbt @@ -4,14 +4,13 @@ import java.io.{ CharArrayWriter, PrintWriter } val marker = new File("marker") val check = TaskKey[Unit]("check", "Check correct error has been returned.") -val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" -val scalaxml = "org.scala-lang.modules" %% "scala-xml" % "1.1.1" +val scalatest = "org.scalatest" %% "scalatest" % "3.2.19" -ThisBuild / scalaVersion := "2.12.20" +ThisBuild / scalaVersion := "3.7.2" lazy val root = (project in file(".")). settings( - libraryDependencies ++= List(scalaxml, scalatest), + libraryDependencies ++= List(scalatest), fork := true, testListeners += new TestReportListener { def testEvent(event: TestEvent): Unit = { diff --git a/sbt-app/src/sbt-test/tests/t543/src/test/scala/Test.scala b/sbt-app/src/sbt-test/tests/t543/src/test/scala/Test.scala index 7cc7c481c..416cf0240 100644 --- a/sbt-app/src/sbt-test/tests/t543/src/test/scala/Test.scala +++ b/sbt-app/src/sbt-test/tests/t543/src/test/scala/Test.scala @@ -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) test("throws a custom exception") { throw new MyCustomException("this is a custom exception") diff --git a/sbt-app/src/sbt-test/tests/t543/test b/sbt-app/src/sbt-test/tests/t543/test index 79e059fc5..15d0c7143 100644 --- a/sbt-app/src/sbt-test/tests/t543/test +++ b/sbt-app/src/sbt-test/tests/t543/test @@ -1,3 +1,3 @@ --> test +-> testFull > check \ No newline at end of file diff --git a/worker/src/main/java/sbt/internal/worker1/ForkTestMain.java b/worker/src/main/java/sbt/internal/worker1/ForkTestMain.java index 607cf3e0d..1a63d1312 100644 --- a/worker/src/main/java/sbt/internal/worker1/ForkTestMain.java +++ b/worker/src/main/java/sbt/internal/worker1/ForkTestMain.java @@ -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; diff --git a/worker/src/main/java/sbt/internal/worker1/PersistedException.java b/worker/src/main/java/sbt/internal/worker1/PersistedException.java new file mode 100644 index 000000000..9fc44f068 --- /dev/null +++ b/worker/src/main/java/sbt/internal/worker1/PersistedException.java @@ -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; + } +} diff --git a/worker/src/main/java/sbt/internal/worker1/ThrowableAdapterFactory.java b/worker/src/main/java/sbt/internal/worker1/ThrowableAdapterFactory.java new file mode 100644 index 000000000..9616afe9e --- /dev/null +++ b/worker/src/main/java/sbt/internal/worker1/ThrowableAdapterFactory.java @@ -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 TypeAdapter create(Gson gson, TypeToken type) { + // Only handles Throwable and subclasses; let other factories handle any other type + if (!Throwable.class.isAssignableFrom(type.getRawType())) { + return null; + } + + @SuppressWarnings("unchecked") + TypeAdapter adapter = + (TypeAdapter) + new TypeAdapter() { + @Override + public Throwable read(JsonReader in) throws IOException { + String message = null; + String type = null; + ArrayList 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; + } +} diff --git a/worker/src/main/java/sbt/internal/worker1/WorkerMain.java b/worker/src/main/java/sbt/internal/worker1/WorkerMain.java index 1eea31df6..ec45e3889 100644 --- a/worker/src/main/java/sbt/internal/worker1/WorkerMain.java +++ b/worker/src/main/java/sbt/internal/worker1/WorkerMain.java @@ -51,6 +51,7 @@ public final class WorkerMain { return new GsonBuilder() .registerTypeAdapterFactory(fingerprintFac) .registerTypeAdapterFactory(selectorFac) + .registerTypeAdapterFactory(ThrowableAdapterFactory.INSTANCE) .create(); }