diff --git a/.github/workflows/android-screenshot.yml b/.github/workflows/android-screenshot.yml index 89b28804ad..2fdafb6f93 100644 --- a/.github/workflows/android-screenshot.yml +++ b/.github/workflows/android-screenshot.yml @@ -72,7 +72,7 @@ jobs: EOF chmod +x android-test-script.sh - name: Run Android Screenshot Test - uses: reactivecircus/android-emulator-runner@v2.37.0 + uses: reactivecircus/android-emulator-runner@v2.38.0 with: api-level: 35 avd-name: test_avd diff --git a/common.gradle b/common.gradle index de22668597..d8a0be6fc1 100644 --- a/common.gradle +++ b/common.gradle @@ -33,6 +33,18 @@ tasks.withType(JavaCompile) { // compile-time options: options.release = 8 } +// JUnit 6 requires Java 17 bytecode, so every compilation unit that consumes it is +// compiled for Java 17: the test sources of all modules and the main sources of the +// screenshot-test framework modules (which use JUnit in their main code). The engine +// itself keeps targeting Java 8. +def isScreenshotTestFrameworkModule = project.path.startsWith(':jme3-screenshot-tests:') +tasks.withType(JavaCompile).configureEach { JavaCompile javaCompile -> + if (javaCompile.name.startsWith('compileTest') + || (isScreenshotTestFrameworkModule && javaCompile.name == 'compileJava')) { + javaCompile.options.release = 17 + } +} + repositories { mavenCentral() flatDir { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 986add583e..8d2894ca25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,7 +33,7 @@ androidx-fragment-testing = { module ="androidx.fragment:fragment-testing", vers androidx-fragment-testing-manifest = { module = "androidx.fragment:fragment-testing-manifest", version.ref = "androidx-fragment-testing"} gradle-git = "org.ajoberstar:gradle-git:1.2.0" androidx-test-runner = "androidx.test:runner:1.7.0" -groovy-test = "org.apache.groovy:groovy-test:4.0.31" +groovy-test = "org.apache.groovy:groovy-test:5.1.0" gson = "com.google.code.gson:gson:2.14.0" googleMaterial = { module = "com.google.android.material:material", version.ref = "googleMaterial" } j-ogg-vorbis = "com.github.stephengold:j-ogg-vorbis:1.0.6" @@ -42,7 +42,7 @@ jbullet = "com.github.stephengold:jbullet:1.0.3" jinput = "net.java.jinput:jinput:2.0.9" jna = "net.java.dev.jna:jna:5.18.1" jnaerator-runtime = "com.nativelibs4java:jnaerator-runtime:0.12" -junit-bom = "org.junit:junit-bom:5.13.4" +junit-bom = "org.junit:junit-bom:6.1.3" junit4 = "junit:junit:4.13.2" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher" } diff --git a/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy b/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy index aeca16d853..5a49889442 100644 --- a/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy +++ b/jme3-core/src/test/groovy/com/jme3/app/state/AppStateManagerTest.groovy @@ -1,99 +1,108 @@ package com.jme3.app.state; import com.jme3.app.LegacyApplication; -import groovy.test.GroovyTestCase; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * Tests the state and ID based lookups of {@link AppStateManager}. + */ class AppStateManagerTest { - static class AttachTest extends GroovyTestCase { - - void testDuplicateId() { - def state1 = new AbstractAppState("test1") {}; - def state2 = new AbstractAppState("test1") {}; - - def app = new LegacyApplication(); - - app.getStateManager().attach(state1); - - shouldFail(IllegalArgumentException) { - app.getStateManager().attach(state2); - } - } - - void testDuplicateNullId() { - // Make sure that two states without an ID can - // still be registered. - def state1 = new AbstractAppState() {}; - def state2 = new AbstractAppState() {}; - - def app = new LegacyApplication(); - - app.getStateManager().attach(state1); + @Test + void attachStateWithDuplicateIdThrows() { + def state1 = new AbstractAppState("test1") {}; + def state2 = new AbstractAppState("test1") {}; + + def app = new LegacyApplication(); + + app.getStateManager().attach(state1); + + assertThrows(IllegalArgumentException, { app.getStateManager().attach(state2); - } + } as Executable); } - static class GetStateWithIdTest extends GroovyTestCase { - void testIdHit() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - - assertNotNull app.stateManager.getState("test1", AppState.class); - } - - void testIdMiss() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - - assertNull app.stateManager.getState("test2", AppState.class); - } - - void testDetached() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - app.stateManager.detach(state); - - assertNull app.stateManager.getState("test2", AppState.class); - } + @Test + void attachStatesWithoutIdSucceeds() { + // Make sure that two states without an ID can + // still be registered. + def state1 = new AbstractAppState() {}; + def state2 = new AbstractAppState() {}; + + def app = new LegacyApplication(); + + app.getStateManager().attach(state1); + app.getStateManager().attach(state2); } - - static class StateForIdTest extends GroovyTestCase { - void testIdHit() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - - assertNotNull app.stateManager.stateForId("test1", AppState.class); - } - - void testIdMiss() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - - shouldFail(IllegalArgumentException) { - app.stateManager.stateForId("test2", AppState.class); - } - } - - void testDetached() { - def state = new AbstractAppState("test1") {}; - def app = new LegacyApplication(); - - app.stateManager.attach(state); - app.stateManager.detach(state); - - shouldFail(IllegalArgumentException) { - app.stateManager.stateForId("test2", AppState.class); - } - } + + @Test + void getStateByIdHit() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNotNull(app.stateManager.getState("test1", AppState.class)); + } + + @Test + void getStateByIdMiss() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNull(app.stateManager.getState("test2", AppState.class)); + } + + @Test + void getStateByIdDetached() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + app.stateManager.detach(state); + + assertNull(app.stateManager.getState("test2", AppState.class)); + } + + @Test + void stateForIdHit() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertNotNull(app.stateManager.stateForId("test1", AppState.class)); + } + + @Test + void stateForIdMiss() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + + assertThrows(IllegalArgumentException, { + app.stateManager.stateForId("test2", AppState.class); + } as Executable); + } + + @Test + void stateForIdDetached() { + def state = new AbstractAppState("test1") {}; + def app = new LegacyApplication(); + + app.stateManager.attach(state); + app.stateManager.detach(state); + + assertThrows(IllegalArgumentException, { + app.stateManager.stateForId("test2", AppState.class); + } as Executable); } } diff --git a/jme3-nativeimage-plugin/build.gradle b/jme3-nativeimage-plugin/build.gradle index 92c21c2b63..28d0fcf31b 100644 --- a/jme3-nativeimage-plugin/build.gradle +++ b/jme3-nativeimage-plugin/build.gradle @@ -53,7 +53,7 @@ java { dependencies { implementation gradleApi() implementation localGroovy() - compileOnly 'org.graalvm.sdk:nativeimage:25.0.3' + compileOnly 'org.graalvm.sdk:nativeimage:25.2.4' compileOnly 'org.graalvm.nativeimage:svm:25.0.3' } diff --git a/jme3-screenshot-tests/jme3-screenshot-tests-desktop/build.gradle b/jme3-screenshot-tests/jme3-screenshot-tests-desktop/build.gradle index 86085d5448..12244ee605 100644 --- a/jme3-screenshot-tests/jme3-screenshot-tests-desktop/build.gradle +++ b/jme3-screenshot-tests/jme3-screenshot-tests-desktop/build.gradle @@ -16,7 +16,7 @@ dependencies { implementation project(':jme3-screenshot-tests:jme3-screenshot-tests-shared') implementation libs.extent.reports - implementation platform('org.junit:junit-bom:5.9.1') + implementation platform('org.junit:junit-bom:6.1.3') implementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly project(':jme3-testdata') @@ -25,7 +25,7 @@ dependencies { testing { suites { test { - useJUnitJupiter('5.9.1') + useJUnitJupiter('6.1.3') } } } diff --git a/jme3-screenshot-tests/jme3-screenshot-tests-shared/build.gradle b/jme3-screenshot-tests/jme3-screenshot-tests-shared/build.gradle index 66996d2076..12f83c54eb 100644 --- a/jme3-screenshot-tests/jme3-screenshot-tests-shared/build.gradle +++ b/jme3-screenshot-tests/jme3-screenshot-tests-shared/build.gradle @@ -13,8 +13,21 @@ dependencies { implementation project(':jme3-plugins') implementation 'com.aventstack:extentreports:5.1.2' - implementation platform('org.junit:junit-bom:5.9.1') - implementation 'org.junit.jupiter:junit-jupiter' + + // The test framework's main sources only need JUnit to compile - they reference it in + // imports and never call into it at runtime. It must therefore stay compile-only and + // never leak onto this module's runtime classpath: the Android screenshot-test app + // inherits this module's runtime dependencies and cannot dex the Java 17 JUnit 6 jars + // (D8: "Attempt to create a global synthetic for 'Record desugaring' without a + // global-synthetics consumer"). The Android app and its instrumentation tests use + // JUnit 4 only. + compileOnly platform('org.junit:junit-bom:6.1.3') + compileOnly 'org.junit.jupiter:junit-jupiter' + + // This module's own unit tests are JUnit 5 tests, so they still need the full Jupiter + // dependency (and the platform launcher) on their compile and runtime classpaths. + testImplementation platform('org.junit:junit-bom:6.1.3') + testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' testRuntimeOnly project(':jme3-testdata') } @@ -22,7 +35,7 @@ dependencies { testing { suites { test { - useJUnitJupiter('5.9.1') + useJUnitJupiter('6.1.3') } } }