This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-grpc.git


The following commit(s) were added to refs/heads/main by this push:
     new 51736716 refactor: migrate JUnit 4 to JUnit 6.1.0 (#758)
51736716 is described below

commit 51736716fa526902e358b308ef03334e2346feac
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Sat Jun 27 21:50:51 2026 +0800

    refactor: migrate JUnit 4 to JUnit 6.1.0 (#758)
    
    Motivation:
    JUnit 4 is end-of-life. JUnit 6 unifies version numbering across
    Platform, Jupiter, and Vintage components.
    
    Modification:
    - Add sbt-jupiter-interface plugin and JupiterKeys overrides for
      JUnit Jupiter and Platform 6.1.0
    - Migrate test files (Java, Scala, Groovy) from JUnit 4 to Jupiter:
      * Java: @Test, @Before/@After -> @BeforeEach/@AfterEach,
        @Ignore -> @Disabled, Assert.xxx -> Assertions.xxx
        (message param moves from first to last)
      * Java: Remove `extends JUnitSuite` and @RunWith(JUnit4.class)
      * Scala: Remove @RunWith(classOf[JUnitRunner]) from ScalaTest specs
        where discovery does not depend on the JUnit 4 bridge
      * Scala: org.junit.Assert._ -> org.junit.jupiter.api.Assertions._
      * Groovy: Spock specs converted to JUnit Jupiter Java tests
        (@Unroll+where -> @ParameterizedTest+@MethodSource,
        cleanup() -> @AfterEach, @TempDir replaced with manual
        Files.createTempDirectory management)
    - Replace scalatestplus-junit with Jupiter test dependencies in
      Dependencies.scala (keep scalaTestPlusJunit for plugin-tester-scala
      where Gradle relies on the JUnit 4 runner for ScalaTest discovery)
    - Update Gradle build files to use JUnit Jupiter and useJUnitPlatform()
    - Convert gradle-plugin Spock/Groovy tests to JUnit Jupiter Java tests
      to avoid Groovy version conflicts with Gradle's bundled Groovy
    - Update benchmark-java standalone build to use JUnit Jupiter 6.1.0
    - Update scripted test build and PekkoGrpcClientTester to use
      Jupiter assertions
    - Configure explicit ScalaFutures PatienceConfig in Scala 3 scripted
      greeter specs and use brace syntax for scalafmt compatibility
    - Pin Gradle plugin tests to JUnit Jupiter 5.14.1 and add explicit
      junit-platform-launcher:1.14.1 for Spock 2.4 compatibility
      (main project tests use JUnit 6)
    
    Result:
    All tests run on JUnit 6.1.0 (Jupiter). JUnit 4 is no longer a test
    dependency in the main project.
    
    Tests:
    Not run here - each constituent change was validated in its own
    commit; full validation to follow after rebase.
    
    References:
    Refs #759 (Gradle wrapper 8.14.5 upgrade, now on main)
    Refs #762 (scalafmt disable Scala 3 syntax)
---
 benchmark-java/build.sbt                           |   3 +-
 .../grpc/benchmarks/driver/LoadClientTest.java     |  12 +-
 .../grpc/benchmarks/driver/LoadWorkerTest.java     |  24 ++-
 build.sbt                                          |   3 +
 gradle-plugin/build.gradle                         |   5 +-
 .../groovy/func/GradleCompatibilitySpec.groovy     |  60 -------
 .../src/test/groovy/helper/BaseSpec.groovy         |  63 -------
 .../src/test/groovy/unit/ApplySpec.groovy          | 182 -------------------
 .../src/test/groovy/unit/DependenciesSpec.groovy   | 103 -----------
 .../java/func/GradleCompatibilitySpecTest.java     |  93 ++++++++++
 gradle-plugin/src/test/java/helper/BaseSpec.java   | 158 +++++++++++++++++
 .../helper/ScalaWrapperPlugin.java}                |  14 +-
 .../src/test/java/unit/ApplySpecTest.java          | 196 +++++++++++++++++++++
 .../src/test/java/unit/DependenciesSpecTest.java   | 108 ++++++++++++
 .../helloworld/grpc/RichErrorModelNativeTest.java  |  11 +-
 .../myapp/helloworld/grpc/RichErrorModelTest.java  |  11 +-
 .../grpc/interop/PekkoGrpcJavaClientTester.java    |  11 +-
 .../grpc/interop/PekkoGrpcScalaClientTester.scala  |   6 +-
 plugin-tester-java/build.gradle                    |   2 +-
 project/Dependencies.scala                         |  24 ++-
 project/plugins.sbt                                |   1 +
 .../pekko/grpc/javadsl/ConcatOrNotFoundTest.java   |   7 +-
 .../sbt-test/gen-scala-server/00-interop/build.sbt |   3 +-
 .../apache/pekko/grpc/PekkoGrpcClientTester.scala  |   6 +-
 24 files changed, 631 insertions(+), 475 deletions(-)

diff --git a/benchmark-java/build.sbt b/benchmark-java/build.sbt
index 9305262c..e3050868 100644
--- a/benchmark-java/build.sbt
+++ b/benchmark-java/build.sbt
@@ -37,7 +37,8 @@ val root = project
       "org.hdrhistogram" % "HdrHistogram" % "2.1.12",
       "org.apache.commons" % "commons-math3" % "3.6.1",
       "org.scalatest" %% "scalatest" % "3.2.20" % Test,
-      "org.scalatestplus" %% "junit-4-13" % "3.2.20.0" % Test),
+      "org.junit.jupiter" % "junit-jupiter" % "6.1.0" % Test,
+      "com.github.sbt.junit" % "jupiter-interface" % "0.19.0" % Test),
     PB.artifactResolver := PB.artifactResolver.dependsOn(codeGenProject / 
Compile / publishLocal).value)
 
 compile / javacOptions += "-Xlint:deprecation"
diff --git 
a/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadClientTest.java
 
b/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadClientTest.java
index 481f00f8..0eb266ad 100644
--- 
a/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadClientTest.java
+++ 
b/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadClientTest.java
@@ -17,21 +17,17 @@
 
 package org.apache.pekko.grpc.benchmarks.driver;
 
-import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.pekko.grpc.benchmarks.proto.Control;
 import org.apache.pekko.grpc.benchmarks.proto.Stats;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.scalatestplus.junit.JUnitSuite;
+import org.junit.jupiter.api.Test;
 
 /**
  * Tests for {@link LoadClient}.
  */
-@RunWith(JUnit4.class)
-public class LoadClientTest extends JUnitSuite {
+public class LoadClientTest {
 
   @Test
   public void testHistogramToStatsConversion() throws Exception {
diff --git 
a/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadWorkerTest.java
 
b/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadWorkerTest.java
index c1593ce0..43f8b436 100644
--- 
a/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadWorkerTest.java
+++ 
b/benchmark-java/src/test/java/org/apache/pekko/grpc/benchmarks/driver/LoadWorkerTest.java
@@ -32,13 +32,10 @@ import org.apache.pekko.stream.javadsl.Sink;
 import org.apache.pekko.stream.javadsl.Source;
 import com.typesafe.config.Config;
 import com.typesafe.config.ConfigFactory;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.scalatestplus.junit.JUnitSuite;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 
 import java.net.InetSocketAddress;
 import java.time.Duration;
@@ -46,13 +43,12 @@ import java.util.concurrent.CompletionStage;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Basic tests for {@link org.apache.pekko.grpc.benchmarks.driver.LoadWorker}
  */
-@RunWith(JUnit4.class)
-public class LoadWorkerTest extends JUnitSuite {
+public class LoadWorkerTest {
 
 
   private static final int TIMEOUT = 5;
@@ -66,7 +62,7 @@ public class LoadWorkerTest extends JUnitSuite {
   private ActorSystem system;
   private Materializer mat;
 
-  @Before
+  @BeforeEach
   public void setup() throws Exception {
     // important to enable HTTP/2 in ActorSystem's config
     Config conf = ConfigFactory.parseString("pekko.http.server.enable-http2 = 
on")
@@ -81,7 +77,7 @@ public class LoadWorkerTest extends JUnitSuite {
     workerServiceStub = WorkerServiceClient.create(settings, system);
   }
 
-  @After
+  @AfterEach
   public void tearDown() throws Exception {
     if (system != null) {
       workerServiceStub.close();
@@ -90,7 +86,7 @@ public class LoadWorkerTest extends JUnitSuite {
     }
   }
 
-  @Ignore // SYNC_CLIENT not implemented
+  @Disabled // SYNC_CLIENT not implemented
   public void runUnaryBlockingClosedLoop() throws Exception {
     Control.ServerArgs.Builder serverArgsBuilder = 
Control.ServerArgs.newBuilder();
     serverArgsBuilder.getSetupBuilder()
@@ -173,7 +169,7 @@ public class LoadWorkerTest extends JUnitSuite {
     assertWorkOccurred(clientArgsBuilder.build());
   }
 
-  @Ignore // ASYNC_GENERIC_SERVER not implemented
+  @Disabled // ASYNC_GENERIC_SERVER not implemented
   public void runGenericPingPongAsyncClosedLoop() throws Exception {
     Control.ServerArgs.Builder serverArgsBuilder = 
Control.ServerArgs.newBuilder();
     serverArgsBuilder.getSetupBuilder()
diff --git a/build.sbt b/build.sbt
index 5570d185..478f5287 100644
--- a/build.sbt
+++ b/build.sbt
@@ -9,6 +9,7 @@
 
 import com.github.pjfanning.pekkobuild._
 import com.github.sbt.JavaFormatterPlugin.autoImport.javafmtSortImports
+import com.github.sbt.junit.jupiter.sbt.Import.JupiterKeys
 import 
net.bzzt.reproduciblebuilds.ReproducibleBuildsPlugin.reproducibleBuildsCheckResolver
 import org.apache.pekko.grpc.{ Dependencies, NoPublish, PekkoCoreDependency, 
PekkoHttpDependency }
 import org.apache.pekko.grpc.Dependencies.Versions.{ scala212, scala213 }
@@ -20,6 +21,8 @@ import sbt.Keys.scalaVersion
 sourceDistName := "apache-pekko-grpc"
 sourceDistIncubating := false
 ThisBuild / versionScheme := Some(VersionScheme.SemVerSpec)
+ThisBuild / JupiterKeys.junitJupiterVersion := "6.1.0"
+ThisBuild / JupiterKeys.junitPlatformVersion := "6.1.0"
 
 commands := commands.value.filterNot { command =>
   command.nameOption.exists { name =>
diff --git a/gradle-plugin/build.gradle b/gradle-plugin/build.gradle
index 04a5d83d..1aabeda0 100644
--- a/gradle-plugin/build.gradle
+++ b/gradle-plugin/build.gradle
@@ -131,11 +131,12 @@ repositories {
 dependencies {
   implementation 'com.google.protobuf:protobuf-gradle-plugin:0.9.5'
   implementation 'org.apache.commons:commons-lang3:3.19.0'
-  testImplementation 'junit:junit:4.13.2'
-  testImplementation 'org.spockframework:spock-core:2.3-groovy-3.0'
+  testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1'
+  testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.14.1'
 }
 
 test {
+  useJUnitPlatform()
   systemProperty 'pekkoGrpcTest.pluginVersion', project.version
   testLogging {
     events "passed", "skipped", "failed"
diff --git a/gradle-plugin/src/test/groovy/func/GradleCompatibilitySpec.groovy 
b/gradle-plugin/src/test/groovy/func/GradleCompatibilitySpec.groovy
deleted file mode 100644
index 5c108a4e..00000000
--- a/gradle-plugin/src/test/groovy/func/GradleCompatibilitySpec.groovy
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * license agreements; and to You under the Apache License, version 2.0:
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * This file is part of the Apache Pekko project, which was derived from Akka.
- */
-
-package func
-
-import helper.BaseSpec
-import org.gradle.testkit.runner.BuildResult
-import org.gradle.testkit.runner.GradleRunner
-import org.gradle.testkit.runner.TaskOutcome
-import spock.lang.Unroll
-
-class GradleCompatibilitySpec extends BaseSpec {
-
-    BuildResult executeGradleTaskWithVersion(String task, String 
gradleVersion, boolean shouldFail) {
-        def runner = GradleRunner.create().forwardOutput()
-            .withProjectDir(projectDir.root)
-            .withArguments("--stacktrace", task)
-            .withPluginClasspath()
-            .withDebug(true)
-            .withGradleVersion(gradleVersion)
-
-        if (shouldFail) {
-            return runner.buildAndFail()
-        } else {
-            return runner.build()
-        }
-    }
-
-    @Unroll
-    void 'should succeed for version #gradleVersion greater than 5.6'() {
-        given:
-        createBuildFolder()
-        generateBuildScripts()
-        when:
-        BuildResult result = executeGradleTaskWithVersion('tasks', 
gradleVersion, false)
-        then:
-        result.task(":tasks").outcome == TaskOutcome.SUCCESS
-        where:
-        gradleVersion << ["5.6", "5.6.4", "7.6.4"]
-    }
-
-    @Unroll
-    void 'should fail for version #gradleVersion less than 5.6'() {
-        given:
-        createBuildFolder()
-        generateBuildScripts()
-        when:
-        BuildResult result = executeGradleTaskWithVersion('tasks', 
gradleVersion, true)
-        then:
-        result.output.contains("Gradle version is ${gradleVersion}. Minimum 
supported version is 5.6")
-        where:
-        gradleVersion << ["5.5", "4.0"]
-    }
-}
diff --git a/gradle-plugin/src/test/groovy/helper/BaseSpec.groovy 
b/gradle-plugin/src/test/groovy/helper/BaseSpec.groovy
deleted file mode 100644
index a10d329d..00000000
--- a/gradle-plugin/src/test/groovy/helper/BaseSpec.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * license agreements; and to You under the Apache License, version 2.0:
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * This file is part of the Apache Pekko project, which was derived from Akka.
- */
-
-package helper
-
-import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension
-import org.junit.Rule
-import org.junit.rules.TemporaryFolder
-import spock.lang.Specification
-
-import static 
org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension.getPLUGIN_CODE
-
-abstract class BaseSpec extends Specification {
-
-    @Rule
-    public final TemporaryFolder projectDir = new TemporaryFolder()
-
-    File srcDir
-
-    File buildDir
-
-    File buildFile
-
-    def createBuildFolder() {
-        srcDir = projectDir.newFolder("src", "main", "proto")
-        buildDir = projectDir.newFolder("build")
-    }
-
-    def generateBuildScripts() {
-        buildFile = projectDir.newFile("build.gradle")
-        buildFile.text = """
-plugins {
-  id '$PLUGIN_CODE'
-}
-project.dependencies {
-    implementation "com.typesafe.scala-logging:scala-logging_2.12:3.9.2"
-}
-"""
-    }
-
-    def findPekkoGrpcRuntime() {
-        this.project.configurations.pekkoGrpcRuntime.allDependencies.find { 
it.name.contains("pekko-grpc-runtime") }
-    }
-
-    PekkoGrpcPluginExtension sampleSetup(def plugin = "scala", String scala = 
"2.12") {
-        if (plugin == "scala" || plugin == ScalaWrapperPlugin) {
-            def scalaDir = projectDir.newFolder('src', 'main', 'scala')
-            new File(scalaDir, "test.scala").text = "object PekkoGrpc"
-        }
-
-        project.pluginManager.apply PLUGIN_CODE
-        project.dependencies {
-            implementation 
"com.typesafe.scala-logging:scala-logging_$scala:3.9.2"
-        }
-        project.extensions.getByType(PekkoGrpcPluginExtension)
-    }
-}
diff --git a/gradle-plugin/src/test/groovy/unit/ApplySpec.groovy 
b/gradle-plugin/src/test/groovy/unit/ApplySpec.groovy
deleted file mode 100644
index ab01b54c..00000000
--- a/gradle-plugin/src/test/groovy/unit/ApplySpec.groovy
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * license agreements; and to You under the Apache License, version 2.0:
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * This file is part of the Apache Pekko project, which was derived from Akka.
- */
-
-package unit
-
-import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension
-import helper.BaseSpec
-import helper.ScalaWrapperPlugin
-import org.gradle.api.Project
-import org.gradle.api.ProjectConfigurationException
-import org.gradle.testfixtures.ProjectBuilder
-import org.gradle.testkit.runner.BuildResult
-import org.gradle.testkit.runner.GradleRunner
-import spock.lang.Unroll
-
-import static org.gradle.testkit.runner.TaskOutcome.SKIPPED
-import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
-import static org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension.PLUGIN_CODE
-
-class ApplySpec extends BaseSpec {
-
-    Project project
-
-    File log
-
-    def setup() {
-        createBuildFolder()
-        project = 
ProjectBuilder.builder().withProjectDir(projectDir.root).build()
-    }
-
-    def createLog() {
-        log = projectDir.newFile("build/pekko-grpcgradle-plugin.log")
-    }
-
-    BuildResult executeGradleTask(String task) {
-        def runner = GradleRunner.create().forwardOutput()
-            .withProjectDir(projectDir.root)
-            .withArguments("--stacktrace", task)
-            .withPluginClasspath()
-            .withDebug(true)
-        runner.build()
-    }
-
-    @Unroll
-    def "should detect language for #plugin"() {
-        given:
-        def pekkoGrpcExt = sampleSetup(plugin, scala)
-        when:
-        project.evaluate()
-        then:
-        with(pekkoGrpcExt) {
-            it.pluginVersion == 
System.getProperty("pekkoGrpcTest.pluginVersion")
-            it.scala == isScala
-        }
-        where:
-        plugin            || scala || isScala
-        "java"             | "2.12" | false
-        "java"             | "2.13" | false
-        "scala"            | "2.12" | true
-        "scala"            | "2.13" | true
-        ScalaWrapperPlugin | "2.12" | true
-        ScalaWrapperPlugin | "2.13" | true
-    }
-
-    def "should fail if no scala-library declared"() {
-        given:
-        project.pluginManager.apply "scala"
-        project.pluginManager.apply PLUGIN_CODE
-        when:
-        project.evaluate()
-        then:
-        def ex = thrown(ProjectConfigurationException)
-        ex.cause.message.startsWith "$PLUGIN_CODE requires a single 
major.minor version of `org.scala-lang:scala-library` in compileClasspath."
-        ex.cause.message.endsWith "Found []"
-    }
-
-    def "should fail if multiple scala-library declared"() {
-        given:
-        project.pluginManager.apply PLUGIN_CODE
-        and:
-        project.dependencies {
-            implementation group: 'org.mockito', name: 'mockito-scala_2.11', 
version: '1.6.1'
-            implementation group: 'org.mockito', name: 'mockito-scala_2.13', 
version: '1.14.8'
-        }
-        when:
-        project.evaluate()
-        then:
-        def ex = thrown(ProjectConfigurationException)
-        ex.cause.message.startsWith "$PLUGIN_CODE requires a single 
major.minor version of `org.scala-lang:scala-library` in compileClasspath."
-        ex.cause.message.endsWith "Found [2.11, 2.13]"
-    }
-
-    def "should not fail scala autodetect if dependencies contain 
underscore"() {
-        given:
-        def pekkoGrpcExt = sampleSetup()
-        and:
-        project.dependencies {
-            implementation 
"com.google.errorprone:error_prone_annotations:2.3.4"
-        }
-        when:
-        project.evaluate()
-        then: 'plugin is applied'
-        pekkoGrpcExt.scala
-    }
-
-    def "should disable compileJava if no java source files found"() {
-        given:
-        sampleSetup()
-        generateBuildScripts()
-        createLog()
-        when:
-        project.evaluate()
-        BuildResult result = executeGradleTask("compileJava")
-        then: "compileJava is enabled, but task was skipped"
-        project.tasks.getByName("compileJava").enabled
-        result.task(":compileJava").outcome == SKIPPED
-    }
-
-    def "should enable compileJava if java source files found"() {
-        given:
-        sampleSetup()
-        generateBuildScripts()
-        createLog()
-        when:
-        def javaDir = projectDir.newFolder('src', 'main', 'java')
-        new File(javaDir, "Empty.java").text = "final class Empty {}"
-        and:
-        BuildResult result = executeGradleTask("compileJava")
-        then: "compileJava is success"
-        result.task(":compileJava").outcome == SUCCESS
-    }
-
-    def "should enable compileJava if project has only generated java source 
files"() {
-        given:
-        sampleSetup("kotlin")
-        when:
-        def kotlinDir = projectDir.newFolder('src', 'main', 'kotlin')
-        new File(kotlinDir, "Empty.kt").text = "object Empty {}"
-        and:
-        project.evaluate()
-        then: "compileJava is enabled"
-        project.tasks.getByName("compileJava").enabled
-    }
-
-    def "should allow implicit declarations of scala-library version"() {
-        given:
-        project.pluginManager.apply PLUGIN_CODE
-        project.dependencies {
-            constraints {
-                implementation "org.scala-lang:scala-library:2.13.1"
-            }
-            implementation "org.scala-lang:scala-library"
-        }
-        when:
-        project.evaluate()
-        then:
-        project.extensions.getByType(PekkoGrpcPluginExtension)
-    }
-
-    def "should fail if scala-version implicitly declared and mismatches with 
other scala-library version"() {
-        given:
-        project.pluginManager.apply PLUGIN_CODE
-        project.dependencies {
-            constraints {
-                implementation "org.scala-lang:scala-library:2.12.0"
-            }
-            implementation 
"com.typesafe.scala-logging:scala-logging_2.13:3.9.2"
-        }
-        when:
-        project.evaluate()
-        then:
-        def ex = thrown(ProjectConfigurationException)
-        ex.cause.message.startsWith "$PLUGIN_CODE requires a single 
major.minor version of `org.scala-lang:scala-library` in compileClasspath."
-        ex.cause.message.endsWith "Found [2.12, 2.13]"
-    }
-}
diff --git a/gradle-plugin/src/test/groovy/unit/DependenciesSpec.groovy 
b/gradle-plugin/src/test/groovy/unit/DependenciesSpec.groovy
deleted file mode 100644
index cad726d9..00000000
--- a/gradle-plugin/src/test/groovy/unit/DependenciesSpec.groovy
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * license agreements; and to You under the Apache License, version 2.0:
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * This file is part of the Apache Pekko project, which was derived from Akka.
- */
-
-package unit
-
-import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension
-import helper.BaseSpec
-import org.gradle.api.Project
-import org.gradle.api.artifacts.Dependency
-import org.gradle.testfixtures.ProjectBuilder
-import spock.lang.Unroll
-
-import static 
org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension.GRPC_VERSION
-import static 
org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension.PROTOC_PLUGIN_SCALA_VERSION
-
-class DependenciesSpec extends BaseSpec {
-
-    static final String PROTOC_PLUGIN_CODEGEN = "pekkoGrpc"
-
-    def checkCodegen(Dependency d, PekkoGrpcPluginExtension ext) {
-        assert d.group == "org.apache.pekko"
-        assert d.name == "pekko-grpc-codegen_${PROTOC_PLUGIN_SCALA_VERSION}"
-        assert d.version == ext.pluginVersion
-        true
-    }
-
-    static final String PROTOC_PLUGIN_SCALAPB = "scalapb"
-
-    def checkScalapb(Dependency d, PekkoGrpcPluginExtension ext) {
-        assert d.group == "org.apache.pekko"
-        assert d.name == 
"pekko-grpc-scalapb-protoc-plugin_${PROTOC_PLUGIN_SCALA_VERSION}"
-        assert d.version == ext.pluginVersion
-        true
-    }
-
-    Project project
-
-    def setup() {
-        createBuildFolder()
-        project = 
ProjectBuilder.builder().withProjectDir(projectDir.root).build()
-    }
-
-    def "should add only protoc codegen plugin for java"() {
-        given:
-        def pekkoGrpcExt = sampleSetup('java')
-        when:
-        project.evaluate()
-        then:
-        Map<String, Dependency> plugins = 
project.protobuf.tools.plugins.collectEntries { [(it.name): 
project.dependencies.create(it.artifact)] }
-        plugins.keySet().sort() == [PROTOC_PLUGIN_CODEGEN]
-        and:
-        checkCodegen(plugins[(PROTOC_PLUGIN_CODEGEN)], pekkoGrpcExt)
-        and:
-        def cfg = 
project.configurations.getByName("protobufToolsLocator_$PROTOC_PLUGIN_CODEGEN")
-        cfg.dependencies.size() == 1
-        checkCodegen(cfg.dependencies.first(), pekkoGrpcExt)
-    }
-
-    def "should add protoc codegen and scalapb plugins for scala"() {
-        given:
-        def pekkoGrpcExt = sampleSetup('scala')
-        when:
-        project.evaluate()
-        then:
-        Map<String, Dependency> plugins = 
project.protobuf.tools.plugins.collectEntries { [(it.name): 
project.dependencies.create(it.artifact)] }
-        plugins.keySet().sort() == [PROTOC_PLUGIN_CODEGEN, 
PROTOC_PLUGIN_SCALAPB]
-        and:
-        checkCodegen(plugins[(PROTOC_PLUGIN_CODEGEN)], pekkoGrpcExt)
-        checkScalapb(plugins[(PROTOC_PLUGIN_SCALAPB)], pekkoGrpcExt)
-        and:
-        def cfg = 
project.configurations.getByName("protobufToolsLocator_$PROTOC_PLUGIN_CODEGEN")
-        cfg.dependencies.size() == 1
-        checkCodegen(cfg.dependencies.first(), pekkoGrpcExt)
-        and:
-        def cfg2 = 
project.configurations.getByName("protobufToolsLocator_$PROTOC_PLUGIN_SCALAPB")
-        cfg2.dependencies.size() == 1
-        checkScalapb(cfg2.dependencies.first(), pekkoGrpcExt)
-    }
-
-    @Unroll
-    def "should autodetected scala version for pekko-grpc-runtime #plugin 
#scala"() {
-        given:
-        def pekkoGrpcExt = sampleSetup(plugin, scala)
-        when:
-        project.evaluate()
-        then: "added to configuration"
-        def deps = project.configurations.implementation.dependencies
-        deps.any { it.name == "pekko-grpc-runtime_$scala" && it.version == 
pekkoGrpcExt.pluginVersion }
-        deps.any { it.name == "grpc-stub" && it.version == GRPC_VERSION }
-        where:
-        plugin || scala
-        "java"  | "2.12"
-        "java"  | "2.13"
-        "scala" | "2.12"
-        "scala" | "2.13"
-    }
-}
diff --git a/gradle-plugin/src/test/java/func/GradleCompatibilitySpecTest.java 
b/gradle-plugin/src/test/java/func/GradleCompatibilitySpecTest.java
new file mode 100644
index 00000000..b7c79663
--- /dev/null
+++ b/gradle-plugin/src/test/java/func/GradleCompatibilitySpecTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package func;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+import helper.BaseSpec;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.gradle.testkit.runner.TaskOutcome;
+import org.gradle.util.GradleVersion;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GradleCompatibilitySpecTest extends BaseSpec {
+
+    @AfterEach
+    void tearDown() {
+        cleanup();
+    }
+
+    private BuildResult executeGradleTaskWithVersion(String task, String 
gradleVersion, boolean shouldFail) {
+        GradleRunner runner = GradleRunner.create().forwardOutput()
+                .withProjectDir(projectDir)
+                .withArguments(gradleArguments(task))
+                .withPluginClasspath()
+                .withDebug(true)
+                .withGradleVersion(gradleVersion);
+
+        return runGradle(runner, shouldFail);
+    }
+
+    static Stream<Arguments> successVersions() {
+        return Stream.of(
+                Arguments.of("5.6"),
+                Arguments.of("5.6.4"),
+                Arguments.of("7.6.4"));
+    }
+
+    @ParameterizedTest(name = "should succeed for version {0} greater than 
5.6")
+    @MethodSource("successVersions")
+    void shouldSucceedForVersionGreaterThanOrEqualTo56(String gradleVersion) 
throws IOException {
+        assumeGradleVersionCanRunOnCurrentJava(gradleVersion);
+
+        createBuildFolder();
+        generateBuildScripts();
+
+        BuildResult result = executeGradleTaskWithVersion("tasks", 
gradleVersion, false);
+
+        assertEquals(TaskOutcome.SUCCESS, result.task(":tasks").getOutcome());
+    }
+
+    static Stream<Arguments> failVersions() {
+        return Stream.of(
+                Arguments.of("5.5"),
+                Arguments.of("4.0"));
+    }
+
+    @ParameterizedTest(name = "should fail for version {0} less than 5.6")
+    @MethodSource("failVersions")
+    void shouldFailForVersionLessThan56(String gradleVersion) throws 
IOException {
+        assumeGradleVersionCanRunOnCurrentJava(gradleVersion);
+
+        createBuildFolder();
+        generateBuildScripts();
+
+        BuildResult result = executeGradleTaskWithVersion("tasks", 
gradleVersion, true);
+
+        assertTrue(result.getOutput().contains(
+                "Gradle version is " + gradleVersion + ". Minimum supported 
version is 5.6"));
+    }
+
+    private static void assumeGradleVersionCanRunOnCurrentJava(String 
gradleVersion) {
+        Assumptions.assumeFalse(
+                Runtime.version().feature() >= 17 &&
+                        
GradleVersion.version(gradleVersion).compareTo(GradleVersion.version("7.3")) < 
0 &&
+                        
!GradleVersion.version(gradleVersion).equals(GradleVersion.version("4.0")),
+                "Gradle " + gradleVersion + " cannot run on Java " + 
Runtime.version().feature());
+    }
+}
diff --git a/gradle-plugin/src/test/java/helper/BaseSpec.java 
b/gradle-plugin/src/test/java/helper/BaseSpec.java
new file mode 100644
index 00000000..248d4b42
--- /dev/null
+++ b/gradle-plugin/src/test/java/helper/BaseSpec.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package helper;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension;
+import org.gradle.api.Project;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+
+public abstract class BaseSpec {
+
+    public static final String PLUGIN_CODE = "org.apache.pekko.grpc.gradle";
+
+    protected File projectDir;
+    protected File srcDir;
+    protected File buildDir;
+    protected File buildFile;
+
+    protected File ensureProjectDir() throws IOException {
+        if (projectDir == null) {
+            projectDir = 
Files.createTempDirectory("pekko-grpc-test-").toFile();
+        }
+        return projectDir;
+    }
+
+    protected void cleanup() {
+        if (projectDir != null && projectDir.exists()) {
+            deleteRecursively(projectDir);
+        }
+    }
+
+    protected void createBuildFolder() throws IOException {
+        ensureProjectDir();
+        srcDir = 
Files.createDirectories(projectDir.toPath().resolve("src/main/proto")).toFile();
+        buildDir = 
Files.createDirectories(projectDir.toPath().resolve("build")).toFile();
+    }
+
+    protected void generateBuildScripts() throws IOException {
+        ensureProjectDir();
+        buildFile = new File(projectDir, "build.gradle");
+        Files.writeString(buildFile.toPath(),
+                "plugins {\n" +
+                "  id '" + PLUGIN_CODE + "'\n" +
+                "}\n" +
+                "repositories {\n" +
+                "    mavenLocal()\n" +
+                "}\n" +
+                "project.dependencies {\n" +
+                "    implementation 
\"com.typesafe.scala-logging:scala-logging_2.13:3.9.2\"\n" +
+                "}\n");
+    }
+
+    protected List<String> gradleArguments(String task) {
+        List<String> arguments = new ArrayList<>();
+        arguments.add("--stacktrace");
+        String baselineVersion = baselineVersion();
+        if (baselineVersion != null) {
+            arguments.add("-Dpekko.grpc.baseline.version=" + baselineVersion);
+        }
+        arguments.add(task);
+        return arguments;
+    }
+
+    protected BuildResult runGradle(GradleRunner runner) {
+        return runGradle(runner, false);
+    }
+
+    protected BuildResult runGradle(GradleRunner runner, boolean shouldFail) {
+        String previousBaselineVersion = 
System.getProperty("pekko.grpc.baseline.version");
+        try {
+            if (shouldFail) {
+                return runner.buildAndFail();
+            } else {
+                return runner.build();
+            }
+        } finally {
+            restoreSystemProperty("pekko.grpc.baseline.version", 
previousBaselineVersion);
+        }
+    }
+
+    private void restoreSystemProperty(String key, String value) {
+        if (value == null) {
+            System.clearProperty(key);
+        } else {
+            System.setProperty(key, value);
+        }
+    }
+
+    private String baselineVersion() {
+        String configuredBaselineVersion = 
System.getProperty("pekkoGrpcTest.baselineVersion");
+        if (configuredBaselineVersion != null) {
+            return configuredBaselineVersion;
+        }
+
+        String pluginVersion = 
System.getProperty("pekkoGrpcTest.pluginVersion");
+        if (pluginVersion == null) {
+            return null;
+        }
+
+        return pluginVersion.replaceFirst("-\\d{8}-\\d{4}-SNAPSHOT$", 
"-SNAPSHOT");
+    }
+
+    protected PekkoGrpcPluginExtension sampleSetup(Project project) throws 
IOException {
+        return sampleSetup(project, "scala", "2.12");
+    }
+
+    protected PekkoGrpcPluginExtension sampleSetup(Project project, String 
plugin) throws IOException {
+        return sampleSetup(project, plugin, "2.12");
+    }
+
+    protected PekkoGrpcPluginExtension sampleSetup(Project project, String 
plugin, String scala) throws IOException {
+        ensureProjectDir();
+        if ("scala".equals(plugin)) {
+            File scalaDir = 
Files.createDirectories(projectDir.toPath().resolve("src/main/scala")).toFile();
+            Files.writeString(new File(scalaDir, "test.scala").toPath(), 
"object PekkoGrpc");
+        }
+        project.getPluginManager().apply(PLUGIN_CODE);
+        project.getDependencies().add("implementation",
+                "com.typesafe.scala-logging:scala-logging_" + scala + 
":3.9.2");
+        return 
project.getExtensions().getByType(PekkoGrpcPluginExtension.class);
+    }
+
+    protected PekkoGrpcPluginExtension sampleSetupWithPlugin(Project project, 
Class<?> pluginClass, String scala)
+            throws IOException {
+        ensureProjectDir();
+        File scalaDir = 
Files.createDirectories(projectDir.toPath().resolve("src/main/scala")).toFile();
+        Files.writeString(new File(scalaDir, "test.scala").toPath(), "object 
PekkoGrpc");
+        project.getPluginManager().apply(PLUGIN_CODE);
+        project.getDependencies().add("implementation",
+                "com.typesafe.scala-logging:scala-logging_" + scala + 
":3.9.2");
+        return 
project.getExtensions().getByType(PekkoGrpcPluginExtension.class);
+    }
+
+    private static void deleteRecursively(File file) {
+        if (file.isDirectory()) {
+            File[] children = file.listFiles();
+            if (children != null) {
+                for (File child : children) {
+                    deleteRecursively(child);
+                }
+            }
+        }
+        file.delete();
+    }
+}
diff --git a/gradle-plugin/src/test/groovy/helper/ScalaWrapperPlugin.groovy 
b/gradle-plugin/src/test/java/helper/ScalaWrapperPlugin.java
similarity index 51%
rename from gradle-plugin/src/test/groovy/helper/ScalaWrapperPlugin.groovy
rename to gradle-plugin/src/test/java/helper/ScalaWrapperPlugin.java
index 58af1b40..22eed3e6 100644
--- a/gradle-plugin/src/test/groovy/helper/ScalaWrapperPlugin.groovy
+++ b/gradle-plugin/src/test/java/helper/ScalaWrapperPlugin.java
@@ -7,15 +7,15 @@
  * This file is part of the Apache Pekko project, which was derived from Akka.
  */
 
-package helper
+package helper;
 
-import org.gradle.api.Plugin
-import org.gradle.api.Project
-import org.gradle.api.plugins.scala.ScalaPlugin
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.plugins.scala.ScalaPlugin;
 
-class ScalaWrapperPlugin implements Plugin<Project> {
+public class ScalaWrapperPlugin implements Plugin<Project> {
     @Override
-    void apply(Project project) {
-        project.pluginManager.apply ScalaPlugin
+    public void apply(Project project) {
+        project.getPluginManager().apply(ScalaPlugin.class);
     }
 }
diff --git a/gradle-plugin/src/test/java/unit/ApplySpecTest.java 
b/gradle-plugin/src/test/java/unit/ApplySpecTest.java
new file mode 100644
index 00000000..e2669e71
--- /dev/null
+++ b/gradle-plugin/src/test/java/unit/ApplySpecTest.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package unit;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.stream.Stream;
+
+import helper.BaseSpec;
+import helper.ScalaWrapperPlugin;
+import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension;
+import org.gradle.api.Project;
+import org.gradle.api.ProjectConfigurationException;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.testfixtures.ProjectBuilder;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.gradle.testkit.runner.TaskOutcome.SKIPPED;
+import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
+import static org.junit.jupiter.api.Assertions.*;
+
+class ApplySpecTest extends BaseSpec {
+
+    private Project project;
+
+    private File log;
+
+    @BeforeEach
+    void setUp() throws IOException {
+        createBuildFolder();
+        project = ProjectBuilder.builder().withProjectDir(projectDir).build();
+    }
+
+    @AfterEach
+    void tearDown() {
+        cleanup();
+    }
+
+    private void createLog() throws IOException {
+        log = new File(buildDir, "pekko-grpcgradle-plugin.log");
+        log.createNewFile();
+    }
+
+    private BuildResult executeGradleTask(String task) {
+        GradleRunner runner = GradleRunner.create().forwardOutput()
+                .withProjectDir(projectDir)
+                .withArguments(gradleArguments(task))
+                .withPluginClasspath()
+                .withDebug(true);
+        return runGradle(runner);
+    }
+
+    static Stream<Arguments> detectLanguageParams() {
+        return Stream.of(
+                Arguments.of("java", "2.12", false),
+                Arguments.of("java", "2.13", false),
+                Arguments.of("scala", "2.12", true),
+                Arguments.of("scala", "2.13", true),
+                Arguments.of(ScalaWrapperPlugin.class, "2.12", true),
+                Arguments.of(ScalaWrapperPlugin.class, "2.13", true));
+    }
+
+    @ParameterizedTest(name = "should detect language for {0}")
+    @MethodSource("detectLanguageParams")
+    void shouldDetectLanguage(Object plugin, String scala, boolean isScala) 
throws IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt;
+        if (plugin instanceof String) {
+            pekkoGrpcExt = sampleSetup(project, (String) plugin, scala);
+        } else {
+            pekkoGrpcExt = sampleSetupWithPlugin(project, (Class<?>) plugin, 
scala);
+        }
+        ((ProjectInternal) project).evaluate();
+
+        assertEquals(System.getProperty("pekkoGrpcTest.pluginVersion"), 
pekkoGrpcExt.getPluginVersion());
+        assertEquals(isScala, pekkoGrpcExt.getScala());
+    }
+
+    @Test
+    void shouldFailIfNoScalaLibraryDeclared() {
+        project.getPluginManager().apply("scala");
+        project.getPluginManager().apply(PLUGIN_CODE);
+
+        ProjectConfigurationException ex = 
assertThrows(ProjectConfigurationException.class,
+                () -> ((ProjectInternal) project).evaluate());
+
+        assertTrue(ex.getCause().getMessage().startsWith(
+                PLUGIN_CODE + " requires a single major.minor version of 
`org.scala-lang:scala-library` in compileClasspath."));
+        assertTrue(ex.getCause().getMessage().endsWith("Found []"));
+    }
+
+    @Test
+    void shouldFailIfMultipleScalaLibraryDeclared() {
+        project.getPluginManager().apply(PLUGIN_CODE);
+        project.getDependencies().add("implementation", 
"org.mockito:mockito-scala_2.11:1.6.1");
+        project.getDependencies().add("implementation", 
"org.mockito:mockito-scala_2.13:1.14.8");
+
+        ProjectConfigurationException ex = 
assertThrows(ProjectConfigurationException.class,
+                () -> ((ProjectInternal) project).evaluate());
+
+        assertTrue(ex.getCause().getMessage().startsWith(
+                PLUGIN_CODE + " requires a single major.minor version of 
`org.scala-lang:scala-library` in compileClasspath."));
+        assertTrue(ex.getCause().getMessage().endsWith("Found [2.11, 2.13]"));
+    }
+
+    @Test
+    void shouldNotFailScalaAutodetectIfDependenciesContainUnderscore() throws 
IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project);
+        project.getDependencies().add("implementation", 
"com.google.errorprone:error_prone_annotations:2.3.4");
+
+        ((ProjectInternal) project).evaluate();
+
+        assertTrue(pekkoGrpcExt.getScala());
+    }
+
+    @Test
+    void shouldDisableCompileJavaIfNoJavaSourceFilesFound() throws IOException 
{
+        sampleSetup(project);
+        generateBuildScripts();
+        createLog();
+
+        ((ProjectInternal) project).evaluate();
+        BuildResult result = executeGradleTask("compileJava");
+
+        assertTrue(project.getTasks().getByName("compileJava").getEnabled());
+        assertEquals(SKIPPED, result.task(":compileJava").getOutcome());
+    }
+
+    @Test
+    void shouldEnableCompileJavaIfJavaSourceFilesFound() throws IOException {
+        sampleSetup(project);
+        generateBuildScripts();
+        createLog();
+
+        File javaDir = 
Files.createDirectories(projectDir.toPath().resolve("src/main/java")).toFile();
+        Files.writeString(new File(javaDir, "Empty.java").toPath(), "final 
class Empty {}");
+
+        BuildResult result = executeGradleTask("compileJava");
+
+        assertEquals(SUCCESS, result.task(":compileJava").getOutcome());
+    }
+
+    @Test
+    void shouldEnableCompileJavaIfProjectHasOnlyGeneratedJavaSourceFiles() 
throws IOException {
+        sampleSetup(project, "kotlin");
+
+        File kotlinDir = 
Files.createDirectories(projectDir.toPath().resolve("src/main/kotlin")).toFile();
+        Files.writeString(new File(kotlinDir, "Empty.kt").toPath(), "object 
Empty {}");
+
+        ((ProjectInternal) project).evaluate();
+
+        assertTrue(project.getTasks().getByName("compileJava").getEnabled());
+    }
+
+    @Test
+    void shouldAllowImplicitDeclarationsOfScalaLibraryVersion() {
+        project.getPluginManager().apply(PLUGIN_CODE);
+        project.getDependencies().getConstraints().add("implementation",
+                "org.scala-lang:scala-library:2.13.1");
+        project.getDependencies().add("implementation", 
"org.scala-lang:scala-library");
+
+        ((ProjectInternal) project).evaluate();
+
+        
assertNotNull(project.getExtensions().getByType(PekkoGrpcPluginExtension.class));
+    }
+
+    @Test
+    void shouldFailIfScalaVersionImplicitlyDeclaredAndMismatches() {
+        project.getPluginManager().apply(PLUGIN_CODE);
+        project.getDependencies().getConstraints().add("implementation",
+                "org.scala-lang:scala-library:2.12.0");
+        project.getDependencies().add("implementation",
+                "com.typesafe.scala-logging:scala-logging_2.13:3.9.2");
+
+        ProjectConfigurationException ex = 
assertThrows(ProjectConfigurationException.class,
+                () -> ((ProjectInternal) project).evaluate());
+
+        assertTrue(ex.getCause().getMessage().startsWith(
+                PLUGIN_CODE + " requires a single major.minor version of 
`org.scala-lang:scala-library` in compileClasspath."));
+        assertTrue(ex.getCause().getMessage().endsWith("Found [2.12, 2.13]"));
+    }
+}
diff --git a/gradle-plugin/src/test/java/unit/DependenciesSpecTest.java 
b/gradle-plugin/src/test/java/unit/DependenciesSpecTest.java
new file mode 100644
index 00000000..3a390cad
--- /dev/null
+++ b/gradle-plugin/src/test/java/unit/DependenciesSpecTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * license agreements; and to You under the Apache License, version 2.0:
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This file is part of the Apache Pekko project, which was derived from Akka.
+ */
+
+package unit;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+
+import helper.BaseSpec;
+import org.apache.pekko.grpc.gradle.PekkoGrpcPluginExtension;
+import org.gradle.api.Project;
+import org.gradle.api.artifacts.Dependency;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.testfixtures.ProjectBuilder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DependenciesSpecTest extends BaseSpec {
+
+    static final String PROTOC_PLUGIN_CODEGEN = "pekkoGrpc";
+    static final String PROTOC_PLUGIN_SCALAPB = "scalapb";
+    static final String PROTOC_PLUGIN_SCALA_VERSION = "2.12";
+    static final String GRPC_VERSION = "1.82.1";
+
+    private Project project;
+
+    @BeforeEach
+    void setUp() throws IOException {
+        createBuildFolder();
+        project = ProjectBuilder.builder().withProjectDir(projectDir).build();
+    }
+
+    @AfterEach
+    void tearDown() {
+        cleanup();
+    }
+
+    private void checkCodegen(Dependency d, PekkoGrpcPluginExtension ext) {
+        assertEquals("org.apache.pekko", d.getGroup());
+        assertEquals("pekko-grpc-codegen_" + PROTOC_PLUGIN_SCALA_VERSION, 
d.getName());
+        assertEquals(ext.getPluginVersion(), d.getVersion());
+    }
+
+    private void checkScalapb(Dependency d, PekkoGrpcPluginExtension ext) {
+        assertEquals("org.apache.pekko", d.getGroup());
+        assertEquals("pekko-grpc-scalapb-protoc-plugin_" + 
PROTOC_PLUGIN_SCALA_VERSION, d.getName());
+        assertEquals(ext.getPluginVersion(), d.getVersion());
+    }
+
+    @Test
+    void shouldAddOnlyProtocCodegenPluginForJava() throws IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project, "java");
+        ((ProjectInternal) project).evaluate();
+
+        var cfg = 
project.getConfigurations().getByName("protobufToolsLocator_" + 
PROTOC_PLUGIN_CODEGEN);
+        assertEquals(1, cfg.getDependencies().size());
+        checkCodegen(cfg.getDependencies().iterator().next(), pekkoGrpcExt);
+    }
+
+    @Test
+    void shouldAddProtocCodegenAndScalapbPluginsForScala() throws IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project, "scala");
+        ((ProjectInternal) project).evaluate();
+
+        var cfg = 
project.getConfigurations().getByName("protobufToolsLocator_" + 
PROTOC_PLUGIN_CODEGEN);
+        assertEquals(1, cfg.getDependencies().size());
+        checkCodegen(cfg.getDependencies().iterator().next(), pekkoGrpcExt);
+
+        var cfg2 = 
project.getConfigurations().getByName("protobufToolsLocator_" + 
PROTOC_PLUGIN_SCALAPB);
+        assertEquals(1, cfg2.getDependencies().size());
+        checkScalapb(cfg2.getDependencies().iterator().next(), pekkoGrpcExt);
+    }
+
+    static Stream<Arguments> autodetectScalaParams() {
+        return Stream.of(
+                Arguments.of("java", "2.12"),
+                Arguments.of("java", "2.13"),
+                Arguments.of("scala", "2.12"),
+                Arguments.of("scala", "2.13"));
+    }
+
+    @ParameterizedTest(name = "should autodetect scala version for 
pekko-grpc-runtime {0} {1}")
+    @MethodSource("autodetectScalaParams")
+    void shouldAutodetectScalaVersionForPekkoGrpcRuntime(String plugin, String 
scala) throws IOException {
+        PekkoGrpcPluginExtension pekkoGrpcExt = sampleSetup(project, plugin, 
scala);
+        ((ProjectInternal) project).evaluate();
+
+        var deps = 
project.getConfigurations().getByName("implementation").getDependencies();
+        assertTrue(deps.stream().anyMatch(d ->
+                ("pekko-grpc-runtime_" + scala).equals(d.getName())
+                        && 
pekkoGrpcExt.getPluginVersion().equals(d.getVersion())));
+        assertTrue(deps.stream().anyMatch(d ->
+                "grpc-stub".equals(d.getName())
+                        && GRPC_VERSION.equals(d.getVersion())));
+    }
+}
diff --git 
a/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelNativeTest.java
 
b/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelNativeTest.java
index 29cb4b59..4f02c5c4 100644
--- 
a/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelNativeTest.java
+++ 
b/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelNativeTest.java
@@ -13,7 +13,8 @@
 
 package example.myapp.helloworld.grpc;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import com.google.rpc.error_details.LocalizedMessage;
 import com.typesafe.config.Config;
@@ -29,11 +30,9 @@ import org.apache.pekko.http.javadsl.Http;
 import org.apache.pekko.http.javadsl.ServerBinding;
 import org.apache.pekko.http.javadsl.model.HttpRequest;
 import org.apache.pekko.http.javadsl.model.HttpResponse;
-import org.junit.Assert;
-import org.junit.Test;
-import org.scalatestplus.junit.JUnitSuite;
+import org.junit.jupiter.api.Test;
 
-public class RichErrorModelNativeTest extends JUnitSuite {
+public class RichErrorModelNativeTest {
 
   private ServerBinding run(ActorSystem sys) throws Exception {
 
@@ -94,7 +93,7 @@ public class RichErrorModelNativeTest extends JUnitSuite {
 
     } catch (Exception e) {
       e.printStackTrace();
-      Assert.fail("Got unexpected error " + e.getMessage());
+      fail("Got unexpected error " + e.getMessage());
     } finally {
       if (client != null) client.close();
       sys.terminate();
diff --git 
a/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelTest.java
 
b/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelTest.java
index c5a15279..43222f29 100644
--- 
a/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelTest.java
+++ 
b/interop-tests/src/test/java/example/myapp/helloworld/grpc/RichErrorModelTest.java
@@ -13,7 +13,8 @@
 
 package example.myapp.helloworld.grpc;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import com.typesafe.config.Config;
 import com.typesafe.config.ConfigFactory;
@@ -29,11 +30,9 @@ import org.apache.pekko.http.javadsl.Http;
 import org.apache.pekko.http.javadsl.ServerBinding;
 import org.apache.pekko.http.javadsl.model.HttpRequest;
 import org.apache.pekko.http.javadsl.model.HttpResponse;
-import org.junit.Assert;
-import org.junit.Test;
-import org.scalatestplus.junit.JUnitSuite;
+import org.junit.jupiter.api.Test;
 
-public class RichErrorModelTest extends JUnitSuite {
+public class RichErrorModelTest {
 
   private com.google.protobuf.any.Any fromJavaProto(com.google.protobuf.Any 
javaPbSource) {
     return com.google.protobuf.any.Any.of(javaPbSource.getTypeUrl(), 
javaPbSource.getValue());
@@ -84,7 +83,7 @@ public class RichErrorModelTest extends JUnitSuite {
       // #client_request
 
     } catch (Exception e) {
-      Assert.fail("Got unexpected error " + e.getMessage());
+      fail("Got unexpected error " + e.getMessage());
     } finally {
       if (client != null) client.close();
       sys.terminate();
diff --git 
a/interop-tests/src/test/java/org/apache/pekko/grpc/interop/PekkoGrpcJavaClientTester.java
 
b/interop-tests/src/test/java/org/apache/pekko/grpc/interop/PekkoGrpcJavaClientTester.java
index 3dc24f36..168e688d 100644
--- 
a/interop-tests/src/test/java/org/apache/pekko/grpc/interop/PekkoGrpcJavaClientTester.java
+++ 
b/interop-tests/src/test/java/org/apache/pekko/grpc/interop/PekkoGrpcJavaClientTester.java
@@ -13,8 +13,8 @@
 
 package org.apache.pekko.grpc.interop;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import com.google.protobuf.ByteString;
 import io.grpc.Status;
@@ -49,7 +49,6 @@ import org.apache.pekko.stream.SystemMaterializer;
 import org.apache.pekko.stream.javadsl.Keep;
 import org.apache.pekko.stream.javadsl.Sink;
 import org.apache.pekko.stream.javadsl.Source;
-import org.junit.Assert;
 import scala.concurrent.ExecutionContext;
 
 /**
@@ -119,7 +118,7 @@ public class PekkoGrpcJavaClientTester implements 
ClientTester {
 
   @Override
   public void emptyUnary() throws Exception {
-    Assert.assertEquals(
+    assertEquals(
         EmptyProtos.Empty.newBuilder().build(),
         
client.emptyCall(EmptyProtos.Empty.newBuilder().build()).toCompletableFuture().get());
   }
@@ -430,9 +429,9 @@ public class PekkoGrpcJavaClientTester implements 
ClientTester {
 
     Metadata fullDuplexTrailer = 
fullDuplexMetadata.getTrailers().toCompletableFuture().get();
     assertEquals(
-        "Trailer should contain binary header [" + fullDuplexTrailer + "]",
         Optional.of(binaryValue),
-        fullDuplexTrailer.getBinary("x-grpc-test-echo-trailing-bin"));
+        fullDuplexTrailer.getBinary("x-grpc-test-echo-trailing-bin"),
+        "Trailer should contain binary header [" + fullDuplexTrailer + "]");
   }
 
   @Override
diff --git 
a/interop-tests/src/test/scala/org/apache/pekko/grpc/interop/PekkoGrpcScalaClientTester.scala
 
b/interop-tests/src/test/scala/org/apache/pekko/grpc/interop/PekkoGrpcScalaClientTester.scala
index cf30d81b..eec3891d 100644
--- 
a/interop-tests/src/test/scala/org/apache/pekko/grpc/interop/PekkoGrpcScalaClientTester.scala
+++ 
b/interop-tests/src/test/scala/org/apache/pekko/grpc/interop/PekkoGrpcScalaClientTester.scala
@@ -24,7 +24,7 @@ import io.grpc.testing.integration.messages._
 import io.grpc.testing.integration.test.{ TestServiceClient, 
UnimplementedServiceClient }
 import io.grpc.testing.integration2.{ ClientTester, Settings }
 import io.grpc.{ Status, StatusRuntimeException }
-import org.junit.Assert._
+import org.junit.jupiter.api.Assertions._
 import org.scalatest.matchers.should.Matchers._
 
 import java.io.InputStream
@@ -241,9 +241,9 @@ class PekkoGrpcScalaClientTester(val settings: Settings, 
backend: String, testWi
 
     val trailers = Await.result(fullDuplexMetadata.trailers, awaitTimeout)
     assertEquals(
-      s"Trailer should contain binary header [$trailers]",
       Some(binaryHeaderValue),
-      trailers.getBinary("x-grpc-test-echo-trailing-bin"))
+      trailers.getBinary("x-grpc-test-echo-trailing-bin"),
+      s"Trailer should contain binary header [$trailers]")
   }
 
   def statusCodeAndMessage(): Unit = {
diff --git a/plugin-tester-java/build.gradle b/plugin-tester-java/build.gradle
index dee02e36..666b9cc9 100644
--- a/plugin-tester-java/build.gradle
+++ b/plugin-tester-java/build.gradle
@@ -36,7 +36,7 @@ dependencies {
   implementation 
"org.apache.pekko:pekko-actor-typed_${scalaBinaryVersion}:${pekkoVersion}"
   implementation "org.scala-lang:scala-library:${scalaFullVersion}"
   testImplementation "org.scalatest:scalatest_${scalaBinaryVersion}:3.2.20"
-  testImplementation 
"org.scalatestplus:junit-4-13_${scalaBinaryVersion}:3.2.20.0"
+  testImplementation "org.junit.jupiter:junit-jupiter:6.1.0"
 }
 
 tasks.withType(Copy).configureEach {
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 5364ae9b..b39fb43c 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -12,6 +12,7 @@ package org.apache.pekko.grpc
 import sbt._
 import sbt.Keys._
 import buildinfo.BuildInfo
+import com.github.sbt.junit.jupiter.sbt.Import.JupiterKeys
 
 object Dependencies {
   object Versions {
@@ -77,6 +78,15 @@ object Dependencies {
     val scalaTestPlusJunit = "org.scalatestplus" %% "junit-4-13" % 
(Versions.scalaTest + ".0") % Test
   }
 
+  // JUnit Jupiter dependencies - versions derived from the 
sbt-jupiter-interface plugin settings
+  lazy val junitJupiterTestDeps: Def.Initialize[Seq[ModuleID]] = Def.setting {
+    Seq(
+      "com.github.sbt.junit" % "jupiter-interface" % 
JupiterKeys.jupiterVersion.value % sbt.Test,
+      "org.junit.jupiter" % "junit-jupiter-api" % 
JupiterKeys.junitJupiterVersion.value % sbt.Test,
+      "org.junit.jupiter" % "junit-jupiter-engine" % 
JupiterKeys.junitJupiterVersion.value % sbt.Test,
+      "org.junit.platform" % "junit-platform-launcher" % 
JupiterKeys.junitPlatformVersion.value % sbt.Test)
+  }
+
   object Runtime {
     val logback = "ch.qos.logback" % "logback-classic" % "1.5.37" % "runtime"
   }
@@ -106,8 +116,7 @@ object Dependencies {
     Compile.grpcCore,
     Compile.grpcStub % Provided, // comes from the generators
     Compile.grpcNettyShaded,
-    Test.scalaTest,
-    Test.scalaTestPlusJunit)
+    Test.scalaTest) ++ junitJupiterTestDeps.value
 
   lazy val mavenPlugin = l ++= Seq(
     Compile.slf4jApi,
@@ -127,8 +136,13 @@ object Dependencies {
     Compile.grpcInteropTesting,
     Compile.grpcInteropTesting % "protobuf", // gets the proto files for 
interop tests
     Runtime.logback,
-    Test.scalaTest.withConfigurations(Some("compile")),
-    Test.scalaTestPlusJunit.withConfigurations(Some("compile")))
+    Test.scalaTest.withConfigurations(Some("compile"))) ++ Def.setting {
+    Seq(
+      "com.github.sbt.junit" % "jupiter-interface" % 
JupiterKeys.jupiterVersion.value % "compile",
+      "org.junit.jupiter" % "junit-jupiter-api" % 
JupiterKeys.junitJupiterVersion.value % "compile",
+      "org.junit.jupiter" % "junit-jupiter-engine" % 
JupiterKeys.junitJupiterVersion.value % "compile",
+      "org.junit.platform" % "junit-platform-launcher" % 
JupiterKeys.junitPlatformVersion.value % "compile")
+  }.value
 
   lazy val pluginTester = l ++= Seq(
     // usually automatically added by `suggestedDependencies`, which doesn't 
work with ReflectiveCodeGen
@@ -136,5 +150,5 @@ object Dependencies {
     Runtime.logback,
     Test.scalaTest,
     Test.scalaTestPlusJunit,
-    Protobuf.googleCommonProtos)
+    Protobuf.googleCommonProtos) ++ junitJupiterTestDeps.value
 }
diff --git a/project/plugins.sbt b/project/plugins.sbt
index df1e6264..1b5cb39b 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -26,6 +26,7 @@ addSbtPlugin("net.bzzt" % "sbt-reproducible-builds" % "0.32")
 addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8")
 addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.1")
 addSbtPlugin("com.github.pjfanning" % "sbt-pekko-build" % "0.4.7")
+addSbtPlugin("com.github.sbt.junit" % "sbt-jupiter-interface" % "0.19.0")
 addSbtPlugin("com.github.pjfanning" % "sbt-source-dist" % "0.1.13")
 addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.9.0")
 
diff --git 
a/runtime/src/test/java/org/apache/pekko/grpc/javadsl/ConcatOrNotFoundTest.java 
b/runtime/src/test/java/org/apache/pekko/grpc/javadsl/ConcatOrNotFoundTest.java
index e3598ba2..91dc96a8 100644
--- 
a/runtime/src/test/java/org/apache/pekko/grpc/javadsl/ConcatOrNotFoundTest.java
+++ 
b/runtime/src/test/java/org/apache/pekko/grpc/javadsl/ConcatOrNotFoundTest.java
@@ -13,7 +13,7 @@
 
 package org.apache.pekko.grpc.javadsl;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -21,10 +21,9 @@ import java.util.concurrent.TimeUnit;
 import org.apache.pekko.grpc.internal.GrpcProtocolNative;
 import org.apache.pekko.http.javadsl.model.*;
 import org.apache.pekko.util.ByteString;
-import org.junit.Test;
-import org.scalatestplus.junit.JUnitSuite;
+import org.junit.jupiter.api.Test;
 
-public class ConcatOrNotFoundTest extends JUnitSuite {
+public class ConcatOrNotFoundTest {
 
   private final HttpRequest emptyGrpcRequest =
       HttpRequest.create()
diff --git a/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/build.sbt 
b/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/build.sbt
index 69c842ab..54253913 100644
--- a/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/build.sbt
+++ b/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/build.sbt
@@ -16,7 +16,8 @@ val grpcVersion = "1.82.1" // checked synced by 
VersionSyncCheckPlugin
 libraryDependencies ++= Seq(
   "io.grpc" % "grpc-interop-testing" % grpcVersion % "protobuf-src",
   "org.apache.pekko" %% "pekko-grpc-interop-tests" % 
sys.props("project.version") % "test",
-  "org.scalatest" %% "scalatest" % "3.2.20" % Test)
+  "org.scalatest" %% "scalatest" % "3.2.20" % Test,
+  "org.junit.jupiter" % "junit-jupiter-api" % "6.1.0" % Test)
 
 scalacOptions ++= List("-unchecked", "-deprecation", "-language:_", 
"-encoding", "UTF-8")
 
diff --git 
a/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/src/test/scala/org/apache/pekko/grpc/PekkoGrpcClientTester.scala
 
b/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/src/test/scala/org/apache/pekko/grpc/PekkoGrpcClientTester.scala
index 5f459e6c..0f2552f2 100644
--- 
a/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/src/test/scala/org/apache/pekko/grpc/PekkoGrpcClientTester.scala
+++ 
b/sbt-plugin/src/sbt-test/gen-scala-server/00-interop/src/test/scala/org/apache/pekko/grpc/PekkoGrpcClientTester.scala
@@ -28,7 +28,7 @@ import io.grpc.testing.integration.messages.{
 import io.grpc.testing.integration.empty.Empty
 import io.grpc.testing.integration2.{ ChannelBuilder, ClientTester, Settings }
 import io.grpc.{ ManagedChannel, Status, StatusRuntimeException }
-import org.junit.Assert._
+import org.junit.jupiter.api.Assertions._
 
 import scala.concurrent.duration._
 import scala.concurrent.{ Await, ExecutionContext, Future }
@@ -272,9 +272,9 @@ class PekkoGrpcClientTester(val settings: 
Settings)(implicit system: ActorSystem
 
     val trailers = Await.result(fullDuplexMetadata.trailers, awaitTimeout)
     assertEquals(
-      s"Trailer should contain binary header [$trailers]",
       Some(binaryHeaderValue),
-      trailers.getBinary("x-grpc-test-echo-trailing-bin"))
+      trailers.getBinary("x-grpc-test-echo-trailing-bin"),
+      s"Trailer should contain binary header [$trailers]")
 
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to