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

benweidig pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 96cf167a50994377cf2b905080f854fae7148687
Author: Ben Weidig <[email protected]>
AuthorDate: Sun Apr 19 11:59:23 2026 +0200

    TAP5-2809: run TestNG unit tests via Jupiter, integration tests native, 
more cleanup
    
    As using both JUnit and TestNG in the same project can be troublesome,
    we now created a deliberate split at integration testing level.
    
    Unit tests are run via the Jupiter testng-runner, whereas integration
    tests are still run with "native" TestNG, as SeleniumTestCase uses
    TestNG-only feature for its lifecycles.
    
    Furthermore, the Gradle files have been cleaned up and added some
    documentation to improve maintainability.
---
 .../tapestry.junit4-legacy-convention.gradle       |  16 ++-
 .../main/groovy/tapestry.junit5-convention.gradle  |  14 +-
 .../groovy/tapestry.junit5-spock-convention.gradle |   4 +
 .../groovy/tapestry.testing-base-convention.gradle |  39 +++--
 .../main/groovy/tapestry.testng-convention.gradle  |  35 +++--
 gradle/libs.versions.toml                          |   9 +-
 tapestry-beanvalidator/build.gradle                |  17 ++-
 tapestry-core/build.gradle                         | 158 ++++++++++++---------
 tapestry-core/src/test/resources/testng.xml        |  44 ------
 tapestry-hibernate-core/build.gradle               |   2 +-
 tapestry-hibernate/build.gradle                    |  17 ++-
 tapestry-hibernate/src/test/resources/testng.xml   |   5 -
 tapestry-http/build.gradle                         |  17 ++-
 tapestry-http/src/test/resources/testng.xml        |  11 ++
 tapestry-ioc/build.gradle                          |   2 +-
 tapestry-jmx/build.gradle                          |  15 ++
 tapestry-jpa/build.gradle                          |  15 ++
 .../jpa/JpaTransactionAdvisorImplTest.java         |  27 ++--
 .../org/apache/tapestry5/jpa/test/JpaTest.java     |  17 ++-
 tapestry-kaptcha/build.gradle                      |  15 ++
 .../kaptcha/components/KaptchaUnitTest.java        |   3 +-
 tapestry-spring/build.gradle                       |  17 ++-
 tapestry-spring/src/test/resources/testng.xml      |   6 -
 tapestry-test/build.gradle                         |  15 ++
 tapestry-upload/build.gradle                       |  15 ++
 tapestry-upload/src/test/resources/testng.xml      |   2 -
 tapestry-webresources/build.gradle                 |  18 ++-
 .../services/web/WebResourcesTest.java             |   5 +
 28 files changed, 363 insertions(+), 197 deletions(-)

diff --git a/buildSrc/src/main/groovy/tapestry.junit4-legacy-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.junit4-legacy-convention.gradle
index 54b2d6577..fc7e39272 100644
--- a/buildSrc/src/main/groovy/tapestry.junit4-legacy-convention.gradle
+++ b/buildSrc/src/main/groovy/tapestry.junit4-legacy-convention.gradle
@@ -1,11 +1,15 @@
+// Use ONLY for existing modules still using JUnit 4 test sources (@RunWith, 
@Rule, etc.).
+//
+// Adds the JUnit Vintage engine so those tests run on the JUnit Platform 
alongside
+// any Jupiter tests in the same module.
+// No test source changes required.
+//
+// Prefer migrating tests to JUnit 5 over adding this convention to new 
modules.
+
 plugins {
-    id 'tapestry.testing-base-convention'
+    id 'tapestry.junit5-convention'
 }
 
 dependencies {
-    testImplementation libs.junit4
-}
-
-tasks.withType(Test).configureEach {
-    useJUnit()
+    testRuntimeOnly libs.junit.vintage.engine
 }
diff --git a/buildSrc/src/main/groovy/tapestry.junit5-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.junit5-convention.gradle
index 59c163eb6..ee2ecdaea 100644
--- a/buildSrc/src/main/groovy/tapestry.junit5-convention.gradle
+++ b/buildSrc/src/main/groovy/tapestry.junit5-convention.gradle
@@ -1,14 +1,16 @@
+// Use for modules whose test sources use JUnit 5 (Jupiter) annotations 
(@Test, @ExtendWith, …).
+//
+// Convention hierarchy:
+//   tapestry.testing-base-convention           platform runner, BOM, logging
+//    └─ tapestry.junit5-convention               + Jupiter API + engine (this 
file)
+//        ├─ tapestry.junit5-spock-convention         + Spock
+//        └─ tapestry.junit4-legacy-convention        + Vintage engine for 
JUnit 4
+
 plugins {
     id 'tapestry.testing-base-convention'
 }
 
 dependencies {
-    testImplementation platform(libs.junit.bom)
     testImplementation libs.bundles.junit.jupiter.essentials
     testRuntimeOnly libs.junit.jupiter.engine
-    testRuntimeOnly libs.junit.platform.launcher
-}
-
-tasks.withType(Test).configureEach {
-    useJUnitPlatform()
 }
diff --git a/buildSrc/src/main/groovy/tapestry.junit5-spock-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.junit5-spock-convention.gradle
index 1570598ef..3c35d1a43 100644
--- a/buildSrc/src/main/groovy/tapestry.junit5-spock-convention.gradle
+++ b/buildSrc/src/main/groovy/tapestry.junit5-spock-convention.gradle
@@ -1,3 +1,7 @@
+// Use for modules that write tests in Spock (Groovy-based BDD framework).
+// Extends tapestry.junit5-convention with the Spock BOM and spock-core.
+// Spock runs on the JUnit Platform, so no additional runner config is needed.
+
 plugins {
     id 'tapestry.junit5-convention'
     id 'groovy'
diff --git a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle
index 35dd0f937..c4f1a1f06 100644
--- a/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle
+++ b/buildSrc/src/main/groovy/tapestry.testing-base-convention.gradle
@@ -1,12 +1,32 @@
+// Shared test infrastructure for all testing conventions.
+// Applied indirectly via tapestry.junit5-convention and 
tapestry.testng-convention.
+//
+// DO NOT USE DIRECTLY!
+//
+// Configures every Test task in the project (via tasks.withType(Test)):
+//   - JUnit Platform as the test runner (required by both Jupiter and 
testng-engine)
+//   - junit-platform-launcher + JUnit BOM on the runtime classpath
+//   - Standard system properties: encoding, locale, CI flag, Selenium timeout
+//   - Consistent test logging (full exception format, pass/skip/fail events, 
progress counter)
+//
+// The JUnit BOM ensures all JUnit Platform artifacts (launcher, engines) use 
the same version,
+// even in modules that only use TestNG and never write a single 
@Test(jupiter) method.
+
+import org.gradle.api.tasks.testing.logging.TestExceptionFormat
+import org.gradle.api.tasks.testing.TestResult.ResultType
+
 plugins {
     id 'java-library'
 }
 
 dependencies {
+    testImplementation platform(libs.junit.bom)
     testRuntimeOnly libs.slf4j.simple
+    testRuntimeOnly libs.junit.platform.launcher
 }
 
 tasks.withType(Test).configureEach { testTask ->
+    useJUnitPlatform()
     maxHeapSize = '600M'
 
     systemProperties(
@@ -21,22 +41,24 @@ tasks.withType(Test).configureEach { testTask ->
     environment.LANG = 'en_US.UTF-8'
 
     testLogging {
-        exceptionFormat 'full'
+        exceptionFormat = TestExceptionFormat.FULL
         events 'passed', 'skipped', 'failed'
         showStandardStreams = true
     }
 
+    // The code below is a helper to output test progress during testing
+
     def total = 0, passed = 0, failed = 0, skipped = 0
 
     afterTest { descriptor, result ->
         switch (result.resultType) {
-            case org.gradle.api.tasks.testing.TestResult.ResultType.SUCCESS:
+            case ResultType.SUCCESS:
                 passed++
                 break
-            case org.gradle.api.tasks.testing.TestResult.ResultType.FAILURE:
+            case ResultType.FAILURE:
                 failed++
                 break
-            case org.gradle.api.tasks.testing.TestResult.ResultType.SKIPPED:
+            case ResultType.SKIPPED:
                 skipped++
                 break
         }
@@ -50,17 +72,16 @@ tasks.withType(Test).configureEach { testTask ->
         // The root suite has a null parent. We only want to log the final 
summary.
         if (descriptor.parent == null) {
             total = passed + failed + skipped
-            // Using project.path gives a clear identifier like 
":tapestry-core"
-            def projectName = project.path
 
-            // Don't log if no tests were run
+            // Only log if we actually have tested something
             if (total > 0) {
+                // Using project.path plus task gives a clear identifier like 
":tapestry-core:test"
+                def taskIdentifier = "${project.path}:${name}"
                 logger.lifecycle 
"------------------------------------------------------------------------"
-                logger.lifecycle "Test Results for ${projectName}"
+                logger.lifecycle "Test Results for ${taskIdentifier}"
                 logger.lifecycle "  Tests run: ${total}, Passed: ${passed}, 
Failed: ${failed}, Skipped: ${skipped}"
                 logger.lifecycle 
"------------------------------------------------------------------------"
             }
         }
     }
 }
-
diff --git a/buildSrc/src/main/groovy/tapestry.testng-convention.gradle 
b/buildSrc/src/main/groovy/tapestry.testng-convention.gradle
index da6dc9d17..083069931 100644
--- a/buildSrc/src/main/groovy/tapestry.testng-convention.gradle
+++ b/buildSrc/src/main/groovy/tapestry.testng-convention.gradle
@@ -1,3 +1,19 @@
+// Use for modules whose test sources use TestNG annotations (@Test, 
@BeforeMethod, ...).
+//
+// Adds TestNG + EasyMock to the test compile classpath, and testng-engine at 
runtime
+// so the standard 'test' task (JUnit Platform) discovers and runs TestNG unit 
tests
+// without any configuration change.
+//
+// Modules that ALSO have Selenium integration tests must additionally declare:
+//   - A 'testNG' task using useTestNG { suiteXmlFiles << ... } for native 
TestNG execution
+//     (needed for correct @BeforeTest / ITestContext scoping across multiple 
test apps)
+//   - An exclusion on the 'test' task to skip integration classes
+//   - tasks.named('check') { dependsOn 'testNG' }
+//
+// Convention hierarchy:
+//   tapestry.testing-base-convention       platform runner, BOM, logging
+//     └─ tapestry.testng-convention          + TestNG + EasyMock + 
testng-engine (this file)
+
 plugins {
     id 'tapestry.testing-base-convention'
 }
@@ -5,22 +21,5 @@ plugins {
 dependencies {
     testImplementation libs.testng
     testImplementation libs.easymock
-}
-
-tasks.withType(Test).configureEach { testTask ->
-
-    def suiteFile = [
-        'src/test/resources/testng.xml',
-        'src/test/conf/testng.xml'
-    ].find { path ->
-        project.file(path).exists()
-    }
-
-    if (suiteFile) {
-        testTask.useTestNG {
-            suites suiteFile
-        }
-    } else {
-        testTask.useTestNG()
-    }
+    testRuntimeOnly libs.testng.engine
 }
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 98f100db6..cbaeb2a94 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -72,8 +72,7 @@ rhino                  = "1.7.7.2"
 
 # TESTING
 
-junit5 = "5.14.2"
-junit4 = "4.13.2"
+junit5 = "5.14.3"
 
 testng   = "7.5.1"
 easymock = "5.4.0"
@@ -197,11 +196,11 @@ junit-jupiter           = { module = 
"org.junit.jupiter:junit-jupiter"}
 junit-jupiter-api       = { module = "org.junit.jupiter:junit-jupiter-api" }
 junit-jupiter-params    = { module = "org.junit.jupiter:junit-jupiter-params" }
 junit-jupiter-engine    = { module = "org.junit.jupiter:junit-jupiter-engine" }
+junit-vintage-engine    = { module = "org.junit.vintage:junit-vintage-engine" }
 junit-platform-launcher = { module = 
"org.junit.platform:junit-platform-launcher" }
 
-junit4 = { module = "junit:junit", version.ref = "junit4" }
-
-testng = { module = "org.testng:testng", version.ref = "testng" }
+testng        = { module = "org.testng:testng", version.ref = "testng" }
+testng-engine = { module = "org.junit.support:testng-engine", version = 
"1.1.0" }
 
 easymock = { module = "org.easymock:easymock", version.ref = "easymock" }
 
diff --git a/tapestry-beanvalidator/build.gradle 
b/tapestry-beanvalidator/build.gradle
index b11ffabcd..611391011 100644
--- a/tapestry-beanvalidator/build.gradle
+++ b/tapestry-beanvalidator/build.gradle
@@ -2,8 +2,6 @@ plugins {
     id 'tapestry.testng-convention'
 }
 
-import t5build.*
-
 description = 'Support for JSR-303 Bean Validation via the Hibernate validator 
implementation'
 
 dependencies {
@@ -27,6 +25,21 @@ tasks.register('runTestApp303', JavaExec) {
     classpath += project.sourceSets.test.runtimeClasspath
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.beanvalidator.modules.BeanValidatorModule'
diff --git a/tapestry-core/build.gradle b/tapestry-core/build.gradle
index 9f4b55af0..133bef559 100644
--- a/tapestry-core/build.gradle
+++ b/tapestry-core/build.gradle
@@ -1,16 +1,12 @@
+import t5build.NpmTask
+
 plugins {
     id 'tapestry.junit5-convention'
     id 'tapestry.testng-convention'
 }
 
-import t5build.TapestryBuildLogic
-import org.apache.tools.ant.filters.ReplaceTokens
-
 description = 'Central module for Tapestry, containing all core services and 
components'
 
-def npmWorkingDir = 'src/main/typescript/'
-def npmExec = TapestryBuildLogic.isWindows() ? 'npm.cmd' : 'npm'
-
 dependencies {
     api project(':tapestry-ioc')
     api project(':tapestry-json')
@@ -29,41 +25,39 @@ dependencies {
     provided libs.javax.servlet.api
 
     testImplementation libs.commons.httpclient
-    testImplementation project(":tapestry-spock")
+    testImplementation project(':tapestry-spock')
 
-    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
+    testRuntimeOnly libs.hsqldb
 }
 
-tasks.register('npmInstall', Exec) {
-    group = 'TypeScript'
-    description = 'Runs npm install'
-    
-    workingDir = npmWorkingDir
-    commandLine npmExec, 'install'
+tasks.register('npmInstall', NpmTask) {
+    group = 'typescript'
+    description = 'Runs npm ci'
+
+    npmArgs = ['ci']
 }
 
-tasks.register('compileTypeScriptToAmd', Exec) {
-    group = 'TypeScript'
+tasks.register('compileTypeScriptToAmd', NpmTask) {
+    group = 'typescript'
     description = 'Compiles TypeScript to AMD modules'
-
     dependsOn npmInstall
 
-    workingDir = npmWorkingDir
-    commandLine npmExec, 'run', 'build-amd'
+    npmArgs = ['run', 'build-amd']
 }
 
-tasks.register('compileTypeScriptToEsModule', Exec) {
-    group = 'TypeScript'
+tasks.register('compileTypeScriptToEsModule', NpmTask) {
+    group = 'typescript'
     description = 'Compiles TypeScript to ES modules'
-
     dependsOn npmInstall
-    
-    workingDir = npmWorkingDir
-    commandLine npmExec, 'run', 'build-es-module'
+
+    npmArgs = ['run', 'build-es-module']
 }
 
+// Lifecycle task that triggers both TypeScript compilations.
+// Typed as Delete only because Gradle requires a concrete type
+// and this task performs no actions of its own.
 tasks.register('compileTypeScript', Delete) {
-    group = 'TypeScript'
+    group = 'typescript'
     description = 'Compiles all TypeScript variants'
 
     dependsOn compileTypeScriptToAmd, compileTypeScriptToEsModule
@@ -73,18 +67,16 @@ tasks.named('sourcesJar') {
     dependsOn compileTypeScript
 }
 
-tasks.register('generateTypeScriptDocs', Exec) {
-    group = 'TypeScript'
+tasks.register('generateTypeScriptDocs', NpmTask) {
+    group = 'typescript'
     description = 'Generates TypeScript documentation'
-
     dependsOn npmInstall
 
-    workingDir = npmWorkingDir
-    commandLine npmExec, 'run', 'docs'
+    npmArgs = ['run', 'docs']
 }
 
 tasks.register('cleanTypeScriptFiles', Delete) {
-    group = 'TypeScript'
+    group = 'typescript'
     description = 'Cleans generated TypeScript files'
 
     delete fileTree('src/main/resources/META-INF/assets/es-modules/t5/core') {
@@ -105,12 +97,18 @@ tasks.named('clean') {
 }
 
 
-// Not sure why this is necessary:
+// Groovy test classes extend Java test base classes, so Java must be compiled 
first.
+// Gradle does not infer this ordering automatically in mixed-language source 
sets.
 tasks.named('compileTestGroovy') {
     dependsOn compileTestJava
 }
 
 test {
+    useJUnitPlatform()
+
+    // Don't run integration tests via Jupiter testng-runner
+    exclude '**/integration/**'
+
     // Needed to have XMLTokenStreamTests.testStreamEncoding() passing on Java 
9+
     if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_1_9)) {
         jvmArgs('--add-opens=java.base/java.nio.charset=ALL-UNNAMED')
@@ -120,6 +118,23 @@ test {
     systemProperty 'user.language', 'en'
 }
 
+// This task runs the default integration tests that are defined in the 
testng.xml
+tasks.register('testNG', Test) {
+    group = 'verification'
+    classpath = sourceSets.test.runtimeClasspath
+
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+
+    // TAP5-2722
+    systemProperty 'user.language', 'en'
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 [
     'app1',
     'app2',
@@ -130,7 +145,7 @@ test {
     'appfolder'
 ].each { appName ->
     tasks.register("runTest${appName.capitalize()}", JavaExec) {
-        group = 'Application'
+        group = 'application'
         description = "Starts the ${appName} integration test app, useful when 
debugging."
 
         mainClass = 'org.apache.tapestry5.test.JettyRunner'
@@ -139,41 +154,48 @@ test {
     }
 }
 
-// Default is jQuery and Require.js enabled, so here are the tests for the
-// other combinations
-
-tasks.register('testWithJqueryAndRequireJsDisabled', Test) {
-    group = 'Verification'
-    description = 'Runs tests with jQuery and Require.js disabled'
+// The default testNG run uses jQuery + Require.js (the most common production 
config).
+// These three tasks exercise the other combinations. They are NOT wired into 
'check'
+// and run only via the 'continuousIntegration' task in the root build.
 
-    systemProperties(
-        'tapestry.javascript-infrastructure-provider': 'jquery',
-        'tapestry.require-js-enabled':                 'false',
-        'tapestry.port':                               '9091',
-        'tapestry.ssl-port':                           '8444'
-    )
-}
-
-tasks.register('testWithPrototypeAndRequireJsEnabled', Test) {
-    group = 'Verification'  
-    description = 'Runs tests with Prototype and Require.js enabled'
-
-    systemProperties(
-        'tapestry.javascript-infrastructure-provider': 'prototype',
-        'tapestry.require-js-enabled':                 'true',
-        'tapestry.port':                               '9092',
-        'tapestry.ssl-port':                           '8445'
-    )
-}
+[
+    [
+        name: 'testWithJqueryAndRequireJsDisabled',
+        provider: 'jquery',
+        requireJs: 'false',
+        port: '9091',
+        sslPort: '8444'
+    ],
+    [
+        name: 'testWithPrototypeAndRequireJsEnabled',
+        provider: 'prototype',
+        requireJs: 'true',
+        port: '9092',
+        sslPort:
+        '8445'
+    ],
+    [
+        name: 'testWithPrototypeAndRequireJsDisabled',
+        provider: 'prototype',
+        requireJs: 'false',
+        port: '9093',
+        sslPort: '8446'
+    ]
+].each { cfg ->
+    tasks.register(cfg.name, Test) {
+        group = 'verification'
+        description = "Runs integration tests with ${cfg.provider} and 
RequireJS ${cfg.requireJs == 'true' ? 'enabled' : 'disabled'}"
+        classpath = sourceSets.test.runtimeClasspath
 
-tasks.register('testWithPrototypeAndRequireJsDisabled', Test) {
-    group = 'Verification'
-    description = 'Runs tests with Prototype and Require.js disabled'
+        useTestNG {
+            suiteXmlFiles << project.file('src/test/resources/testng.xml')
+        }
 
-    systemProperties(
-        'tapestry.javascript-infrastructure-provider': 'prototype',
-        'tapestry.require-js-enabled':                 'false',
-        'tapestry.port':                               '9093',
-        'tapestry.ssl-port':                           '8446'
-    )
+        systemProperties(
+            'tapestry.javascript-infrastructure-provider': cfg.provider,
+            'tapestry.require-js-enabled': cfg.requireJs,
+            'tapestry.port': cfg.port,
+            'tapestry.ssl-port': cfg.sslPort
+        )
+    }
 }
diff --git a/tapestry-core/src/test/resources/testng.xml 
b/tapestry-core/src/test/resources/testng.xml
index e7ffac3f7..b46d6f256 100644
--- a/tapestry-core/src/test/resources/testng.xml
+++ b/tapestry-core/src/test/resources/testng.xml
@@ -11,50 +11,6 @@
     name="Tapestry Core"
     annotations="1.5"
     verbose="2">
-    <test
-        name="Component Unit Tests"
-        enabled="true">
-        <packages>
-            <package name="org.apache.tapestry5.pagetester" />
-            <package name="org.apache.tapestry5.integration.pagelevel" />
-            <package name="org.apache.tapestry5.corelib.base" />
-            <package name="org.apache.tapestry5.corelib.components" />
-            <package name="org.apache.tapestry5.corelib.mixins" />
-            <package name="org.apache.tapestry5.corelib.internal" />
-        </packages>
-    </test>
-
-    <test
-        name="Service Unit Tests"
-        enabled="true">
-        <packages>
-            <package name="org.apache.tapestry5.root" />
-            <package name="org.apache.tapestry5.dom" />
-            <package name="org.apache.tapestry5.runtime" />
-            <package name="org.apache.tapestry5.services" />
-            <package name="org.apache.tapestry5.services.javascript" />
-            <package name="org.apache.tapestry5.util" />
-            <package name="org.apache.tapestry5.validator" />
-            <package name="org.apache.tapestry5.internal" />
-            <package name="org.apache.tapestry5.internal.beaneditor" />
-            <package name="org.apache.tapestry5.internal.bindings" />
-            <package name="org.apache.tapestry5.internal.grid" />
-            <package name="org.apache.tapestry5.internal.model" />
-            <package name="org.apache.tapestry5.internal.pageload" />
-            <package name="org.apache.tapestry5.internal.services" />
-            <package name="org.apache.tapestry5.internal.services.assets" />
-            <package name="org.apache.tapestry5.internal.services.messages" />
-            <package name="org.apache.tapestry5.internal.services.meta" />
-            <package name="org.apache.tapestry5.internal.services.templates" />
-            <package name="org.apache.tapestry5.internal.structure" />
-            <package name="org.apache.tapestry5.internal.test" />
-            <package name="org.apache.tapestry5.internal.transform" />
-            <package name="org.apache.tapestry5.internal.translator" />
-            <package name="org.apache.tapestry5.internal.util" />
-            <package name="org.apache.tapestry5.internal.services.ajax" />
-        </packages>
-    </test>
-
     <!-- We break these out by which of the test applications they apply to. 
We have a bunch. -->
     <test
         name="Core Integration Tests"
diff --git a/tapestry-hibernate-core/build.gradle 
b/tapestry-hibernate-core/build.gradle
index 48aa01612..60ef293d3 100644
--- a/tapestry-hibernate-core/build.gradle
+++ b/tapestry-hibernate-core/build.gradle
@@ -12,7 +12,7 @@ dependencies {
     implementation libs.jaxb.runtime
 
     testImplementation project(':tapestry-test')
-    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
+    testRuntimeOnly libs.hsqldb
 }
 
 jar {
diff --git a/tapestry-hibernate/build.gradle b/tapestry-hibernate/build.gradle
index f6e08dd8b..a2a9d32d7 100644
--- a/tapestry-hibernate/build.gradle
+++ b/tapestry-hibernate/build.gradle
@@ -11,7 +11,7 @@ dependencies {
     implementation libs.jboss.logging
 
     testImplementation project(':tapestry-test')
-    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
+    testRuntimeOnly libs.hsqldb
 }
 
 tasks.register('runTestApp0', JavaExec) {
@@ -22,6 +22,21 @@ tasks.register('runTestApp0', JavaExec) {
     classpath += project.sourceSets.test.runtimeClasspath
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.hibernate.web.modules.HibernateModule'
diff --git a/tapestry-hibernate/src/test/resources/testng.xml 
b/tapestry-hibernate/src/test/resources/testng.xml
index ae459a0ca..764776869 100644
--- a/tapestry-hibernate/src/test/resources/testng.xml
+++ b/tapestry-hibernate/src/test/resources/testng.xml
@@ -17,11 +17,6 @@
 
 <suite name="Tapestry Hibernate" parallel="false" thread-count="10" 
annotations="1.5" verbose="2">
     <parameter name="tapestry.integration-webapp" value="src/test/webapp"/>
-    <test name="Tapestry Hibernate Internal APIs">
-        <packages>
-            <package name="org.apache.tapestry5.internal.hibernate"/>
-        </packages>
-    </test>
     <test name="Tapestry Hibernate Integration Tests">
         <packages>
             <package name="org.apache.tapestry5.hibernate.integration"/>
diff --git a/tapestry-http/build.gradle b/tapestry-http/build.gradle
index 49c4bea43..1cbc9ec3e 100644
--- a/tapestry-http/build.gradle
+++ b/tapestry-http/build.gradle
@@ -19,7 +19,7 @@ dependencies {
     testImplementation project(':tapestry-test')
     testImplementation project(':tapestry-test-constants')
 
-    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
+    testRuntimeOnly libs.hsqldb
     testRuntimeOnly libs.slf4j.simple
 }
 
@@ -33,3 +33,18 @@ tasks.named('jar', Jar) {
         filter(ReplaceTokens, tokens: [version: project.parent.version])
     }
 }
+
+tasks.named('test') {
+    exclude '**/TapestryHttpIntegrationTests*'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
diff --git a/tapestry-http/src/test/resources/testng.xml 
b/tapestry-http/src/test/resources/testng.xml
new file mode 100644
index 000000000..edd62c552
--- /dev/null
+++ b/tapestry-http/src/test/resources/testng.xml
@@ -0,0 +1,11 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd";>
+
+<suite name="Tapestry HTTP" annotations="1.5" verbose="2">
+
+    <test name="Tapestry HTTP Integration Tests" enabled="true">
+        <packages>
+            <package name="org.apache.tapestry5.http.test" />
+        </packages>
+    </test>
+
+</suite>
diff --git a/tapestry-ioc/build.gradle b/tapestry-ioc/build.gradle
index 75879fb7b..ce2e31c76 100644
--- a/tapestry-ioc/build.gradle
+++ b/tapestry-ioc/build.gradle
@@ -18,8 +18,8 @@ dependencies {
 
     testImplementation libs.commons.lang3
     testImplementation libs.hibernate.core
-    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
 
+    
testRuntimeOnly("${libs.hsqldb.get().module.group}:${libs.hsqldb.get().module.name}:${libs.hsqldb.get().version}:jdk8")
 }
 
 tasks.named('test', Test) {
diff --git a/tapestry-jmx/build.gradle b/tapestry-jmx/build.gradle
index 00694090c..b4d80f37e 100644
--- a/tapestry-jmx/build.gradle
+++ b/tapestry-jmx/build.gradle
@@ -10,6 +10,21 @@ dependencies {
     testImplementation project(':tapestry-test')
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 jar {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.jmx.modules.JmxModule'
diff --git a/tapestry-jpa/build.gradle b/tapestry-jpa/build.gradle
index 2bb87cb1d..da097ab4d 100644
--- a/tapestry-jpa/build.gradle
+++ b/tapestry-jpa/build.gradle
@@ -27,6 +27,21 @@ dependencies {
     }
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.jpa.modules.JpaModule'
diff --git 
a/tapestry-jpa/src/test/java/org/apache/tapestry5/internal/jpa/JpaTransactionAdvisorImplTest.java
 
b/tapestry-jpa/src/test/java/org/apache/tapestry5/internal/jpa/JpaTransactionAdvisorImplTest.java
index 5789ad684..e5591b68c 100644
--- 
a/tapestry-jpa/src/test/java/org/apache/tapestry5/internal/jpa/JpaTransactionAdvisorImplTest.java
+++ 
b/tapestry-jpa/src/test/java/org/apache/tapestry5/internal/jpa/JpaTransactionAdvisorImplTest.java
@@ -37,6 +37,9 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+// Disabled as JUnit would pick it up, but TestNG ignored it due to missing in 
testng.xml
+// 
https://github.com/apache/tapestry-5/commit/246e677097f249ad2352ddc595ef43cc257f8e27
+@Test(enabled = false)
 public class JpaTransactionAdvisorImplTest extends IOCTestCase
 {
     private static final String UNIT_NAME = "FooUnit";
@@ -62,7 +65,7 @@ public class JpaTransactionAdvisorImplTest extends IOCTestCase
         registry = null;
     }
 
-    @Test
+    @Test(enabled = false)
     public void undecorated()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -84,7 +87,7 @@ public class JpaTransactionAdvisorImplTest extends IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void persistence_unit_name_missing()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -118,7 +121,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void persistence_unit_name_missing_single_unit_configured()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -147,7 +150,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void persistence_unit_missing()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -181,7 +184,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void persistence_unit_missing_single_unit_configured()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -210,7 +213,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void transaction_inactive()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -238,7 +241,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void void_method()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -266,7 +269,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void void_method_with_param()
     {
         final VoidService delegate = newMock(VoidService.class);
@@ -292,7 +295,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void runtime_exception_will_abort_transaction() throws Exception
     {
         final Performer delegate = newMock(Performer.class);
@@ -328,7 +331,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void checked_exception_will_commit_transaction() throws Exception
     {
         final Performer delegate = newMock(Performer.class);
@@ -369,7 +372,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void return_type_method()
     {
         final ReturnTypeService delegate = newTestService();
@@ -395,7 +398,7 @@ public class JpaTransactionAdvisorImplTest extends 
IOCTestCase
         verify();
     }
 
-    @Test
+    @Test(enabled = false)
     public void return_type_method_with_param()
     {
         final ReturnTypeService delegate = newTestService();
diff --git 
a/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/test/JpaTest.java 
b/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/test/JpaTest.java
index 267b07617..2671c4fbf 100644
--- a/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/test/JpaTest.java
+++ b/tapestry-jpa/src/test/java/org/apache/tapestry5/jpa/test/JpaTest.java
@@ -47,6 +47,9 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+// Disabled as JUnit would pick it up, but TestNG ignored it due to missing in 
testng.xml
+// 
https://github.com/apache/tapestry-5/commit/246e677097f249ad2352ddc595ef43cc257f8e27
+@Test(enabled = false)
 public class JpaTest
 {
 
@@ -149,7 +152,7 @@ public class JpaTest
         return em.createQuery(query).getResultList();
     }
 
-    @Test
+    @Test(enabled = false)
     public void commitBothInNestedTransaction()
     {
         topLevelService.createThingOneAndTwo("one", "two");
@@ -158,19 +161,19 @@ public class JpaTest
         assertTrue(getEntityManager().find(VersionedThing.class, 
1).getVersion() > 0);
     }
 
-    @Test(expectedExceptions = RollbackException.class)
+    @Test(expectedExceptions = RollbackException.class, enabled = false)
     public void rollbackNestedFails()
     {
         topLevelService.createThingOneAndTwo("one", null);
     }
 
-    @Test(expectedExceptions = RollbackException.class)
+    @Test(expectedExceptions = RollbackException.class, enabled = false)
     public void rollbackTopFails()
     {
         topLevelService.createThingOneAndTwo(null, "two");
     }
 
-    @Test
+    @Test(enabled = false)
     public void sequentialCommitUsingInvokeAfterCommit()
     {
         topLevelService.createThingOneThenTwo("one", "two");
@@ -179,7 +182,7 @@ public class JpaTest
         assertTrue(getEntityManager().find(VersionedThing.class, 
1).getVersion() > 1);
     }
 
-    @Test
+    @Test(enabled = false)
     public void 
sequentialCommitUsingInvokeAfterCommitAndCommitAfterAnnotation()
     {
         topLevelService.createThingOneThenTwoWithNestedCommitAfter("one", 
"two");
@@ -188,7 +191,7 @@ public class JpaTest
         assertTrue(getEntityManager().find(VersionedThing.class, 
1).getVersion() > 1);
     }
 
-    @Test
+    @Test(enabled = false)
     public void sequentialRollbackAndAbortUsingInvokeAfterCommit()
     {
         try
@@ -203,7 +206,7 @@ public class JpaTest
         assertEquals(0, getInstances(ThingTwo.class).size());
     }
 
-    @Test
+    @Test(enabled = false)
     public void trySomething()
     {
         ThingOne thingOne = new ThingOne();
diff --git a/tapestry-kaptcha/build.gradle b/tapestry-kaptcha/build.gradle
index bb011c385..f08d8bcc1 100644
--- a/tapestry-kaptcha/build.gradle
+++ b/tapestry-kaptcha/build.gradle
@@ -14,6 +14,21 @@ dependencies {
     testImplementation project(':tapestry-test')
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.kaptcha.modules.KaptchaModule'
diff --git 
a/tapestry-kaptcha/src/test/java/org/apache/tapestry5/kaptcha/components/KaptchaUnitTest.java
 
b/tapestry-kaptcha/src/test/java/org/apache/tapestry5/kaptcha/components/KaptchaUnitTest.java
index 849bf0056..3ea5d713f 100644
--- 
a/tapestry-kaptcha/src/test/java/org/apache/tapestry5/kaptcha/components/KaptchaUnitTest.java
+++ 
b/tapestry-kaptcha/src/test/java/org/apache/tapestry5/kaptcha/components/KaptchaUnitTest.java
@@ -19,11 +19,12 @@ import org.apache.tapestry5.internal.test.TestableResponse;
 import org.apache.tapestry5.test.PageTester;
 import org.junit.Test;
 
+@Test(enabled = false)
 public class KaptchaUnitTest
 {
     
     @SuppressWarnings("unchecked")
-    @Test
+    @Test(enabled = false)
     public void cache_control_header()
     {
         PageTester tester = new PageTester("kaptcha.demo", "app");
diff --git a/tapestry-spring/build.gradle b/tapestry-spring/build.gradle
index 248e3d049..9782baf62 100644
--- a/tapestry-spring/build.gradle
+++ b/tapestry-spring/build.gradle
@@ -16,7 +16,7 @@ dependencies {
 
 ['', '1'].each { suffix ->
     tasks.register("runTestApp${suffix}", JavaExec) {
-        group = 'Application'
+        group = 'application'
         description = "Start tapestry-spring integration test app${suffix}, 
useful when debugging failing integration tests"
         mainClass = 'org.apache.tapestry5.test.JettyRunner'
         args '-d', "src/test/webapp${suffix}", '-p', '8080'
@@ -24,6 +24,21 @@ dependencies {
     }
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.spring.modules.SpringModule'
diff --git a/tapestry-spring/src/test/resources/testng.xml 
b/tapestry-spring/src/test/resources/testng.xml
index 076743557..d40d02bf2 100644
--- a/tapestry-spring/src/test/resources/testng.xml
+++ b/tapestry-spring/src/test/resources/testng.xml
@@ -16,12 +16,6 @@
 -->
 
 <suite name="Tapesty Spring Integration" parallel="false" thread-count="10" 
annotations="1.5" verbose="2">
-    <test name="Tapestry Spring Unit Tests">
-        <packages>
-            <package name="org.apache.tapestry5.spring"/>
-            <package name="org.apache.tapestry5.internal.spring"/>
-        </packages>
-    </test>
     <test name="Tapestry Spring Integration - Standard">
         <packages>
             <package name="org.apache.tapestry5.spring.integration.standard"/>
diff --git a/tapestry-test/build.gradle b/tapestry-test/build.gradle
index 70b34c377..281b3f9c0 100644
--- a/tapestry-test/build.gradle
+++ b/tapestry-test/build.gradle
@@ -25,3 +25,18 @@ dependencies {
     api libs.testng
     api libs.easymock
 }
+
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
diff --git a/tapestry-upload/build.gradle b/tapestry-upload/build.gradle
index 6873b19c6..d55fcbbb4 100644
--- a/tapestry-upload/build.gradle
+++ b/tapestry-upload/build.gradle
@@ -16,6 +16,21 @@ dependencies {
     testImplementation project(':tapestry-test')
 }
 
+tasks.named('test') {
+    exclude '**/integration/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    useTestNG {
+        suiteXmlFiles << project.file('src/test/resources/testng.xml')
+    }
+}
+
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.upload.modules.UploadModule'
diff --git a/tapestry-upload/src/test/resources/testng.xml 
b/tapestry-upload/src/test/resources/testng.xml
index a6ed19248..d6d82a9ec 100755
--- a/tapestry-upload/src/test/resources/testng.xml
+++ b/tapestry-upload/src/test/resources/testng.xml
@@ -19,8 +19,6 @@
     <test name="Tapesty Upload Component">
         <parameter name="tapestry.web-app-folder" value="src/test/webapp"/>
         <packages>
-            <package name="org.apache.tapestry5.upload.components"/>
-            <package name="org.apache.tapestry5.upload.internal.services"/>
             <package name="org.apache.tapestry5.upload.integration"/>
         </packages>
     </test>
diff --git a/tapestry-webresources/build.gradle 
b/tapestry-webresources/build.gradle
index fcca7534f..ff8d65a26 100644
--- a/tapestry-webresources/build.gradle
+++ b/tapestry-webresources/build.gradle
@@ -31,7 +31,19 @@ dependencies {
 }
 
 
-test {
+tasks.named('test') {
+    useJUnitPlatform()
+    exclude '**/services/web/**'
+}
+
+tasks.register('testNG', Test) {
+    group = 'verification'
+    description = 'Runs Selenium integration tests via native TestNG'
+
+    useTestNG()
+
+    include '**/services/web/**'
+
     systemProperties(
         'tapestry.page-load-timeout': '60',
         'tapestry.compiled-asset-cache-dir': "$buildDir/compiled-asset-cache",
@@ -41,6 +53,10 @@ test {
     )
 }
 
+tasks.named('check') {
+    dependsOn 'testNG'
+}
+
 tasks.named('jar', Jar) {
     manifest {
         attributes 'Tapestry-Module-Classes': 
'org.apache.tapestry5.webresources.modules.WebResourcesModule'
diff --git 
a/tapestry-webresources/src/test/java/t5/webresources/services/web/WebResourcesTest.java
 
b/tapestry-webresources/src/test/java/t5/webresources/services/web/WebResourcesTest.java
index c4b932c81..d4d5bb7f5 100644
--- 
a/tapestry-webresources/src/test/java/t5/webresources/services/web/WebResourcesTest.java
+++ 
b/tapestry-webresources/src/test/java/t5/webresources/services/web/WebResourcesTest.java
@@ -26,6 +26,11 @@ public class WebResourcesTest extends SeleniumTestCase {
     @Test
     public void test_CoffeeScript_compilation()
     {
+        // It appears that the initial CoffeeScriptCompiler/Rhino 
initialization might
+        // take too long on Apache CI, so we allow Selenium some more time to 
wait
+        // after initiating the request.
+        setTimeout("90000"); // 90s
+
         open("/");
         waitForInitializedPage();
         assertEquals(getText("banner"), "Index module loaded, bare!");


Reply via email to