This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/commons-secure-xml.git
The following commit(s) were added to refs/heads/main by this push:
new 61fe771 Fix the site test report and count Android in the coverage
(#82)
61fe771 is described below
commit 61fe7719f95e115c7b02030374b29bbf36515fa3
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Wed Sep 2 12:38:29 2026 +0200
Fix the site test report and count Android in the coverage (#82)
* Report on the site the tests the build already ran
The site's Surefire report counted zero tests: the eight surefire
executions write into target/surefire-reports/<flavor>, while the report
plugin scans target/surefire-reports itself. List the eight directories.
Take the non-forking goals while here. The `report` goal forks the test
phase, and in the forked lifecycle the first execution is configured with a
mixture of the other executions' settings (Xerces added, Saxon excluded,
xpath3 skipped, failures ignored), so `mvn test site` ran a ninth,
misconfigured test round that overwrote the real Saxon reports.
`report-only` reads what the build produced instead. The directory list is
scoped to that report set so `failsafe-report-only` keeps its own default,
target/failsafe-reports.
Assisted-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XVaEa2R2sHtBgJh8Mhv841
* Count the Android instrumented run in the coverage report
The device suite in android-tests exercises the platform-parser paths no JVM
run reaches, but its results never reached JaCoCo: the Gradle build
collected
no coverage, and nothing merged it into the Maven data.
Turn on the Android Gradle plugin's androidTest coverage, pinned to the
JaCoCo version the Maven build reports with so the on-device agent and the
report agree on the data format. The plugin instruments the whole test APK,
the library JAR under test included, and leaves one execution data file per
device under build/outputs/code_coverage.
Merge those files into target/jacoco.exec in the site lifecycle, so
jacoco:report shows the Android-only paths as covered. The merge stays out
of
the default lifecycle on purpose: jacoco:check keeps measuring the JVM run
alone, so the coverage minimums mean the same thing whether or not a device
run is lying around. With no Android run in the tree the merge is a no-op,
and JaCoCo drops execution data whose class no longer matches, so a stale
run
cannot inflate the numbers.
Document the round trip in the module README, along with the JDK 8 plus JDK
21 recipe that reaches the project's real coverage, and correct the
excluded-tag list while there: the suite runs the schema tests against the
Xerces on the androidTest classpath, and it is xpath that stays out, since
XPathFactory.newDefaultInstance has no Android fallback and the leak
controls
expect an unconfigured XPath to fetch.
Assisted-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XVaEa2R2sHtBgJh8Mhv841
* Set coverage minimums a single JDK run can meet
The minimums were unreachable: mvn -Pjacoco clean verify violated five of
the
six rules on every JDK, because much of the library only runs on Java 8 (the
MethodHandle fallbacks, the absent newDefaultInstance and newNSInstance, the
pre-18 XPathFactory) or only on a device.
Measure what one runtime reaches and set each minimum a point below the
weaker of a JDK 8 and a JDK 21 run: instructions 94.02% and 95.89%, branches
88.59% and 89.67%, lines 94.78% and 95.51%, complexity 93.63% and 95.75%,
methods 96.57% and 99.21%, classes 97.14% on both. The check now passes on
JDK 8, 11, 17, 21 and 25, while a run accumulated across JDK 8, JDK 21 and
Android clears every rule by three points or more.
Assisted-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XVaEa2R2sHtBgJh8Mhv841
---
android-tests/README.md | 84 +++++++++++++++++++++++++++++++++-----
android-tests/build.gradle.kts | 11 +++++
pom.xml | 92 ++++++++++++++++++++++++++++++++++++++----
3 files changed, 169 insertions(+), 18 deletions(-)
diff --git a/android-tests/README.md b/android-tests/README.md
index 729af19..af822c9 100644
--- a/android-tests/README.md
+++ b/android-tests/README.md
@@ -26,35 +26,97 @@ module; it is a standalone Gradle build kept separate so
the default `mvn` goal
- JDK 17 on `PATH` (AGP 8.x requires it).
- Android SDK with `platforms/android-34` and `build-tools/34.0.0` installed;
export `ANDROID_HOME` (or `ANDROID_SDK_ROOT`) to point at it.
-- Either an attached emulator/device (`adb devices` shows it) or the AGP
managed device
- bundled into this build (`api31`, AOSP system image).
-- The library JAR built by the parent Maven build:
+ ```shell
+ export ANDROID_HOME=/path/to/android
```
- cd .. && mvn -DskipTests package
+- Either an attached emulator/device (`adb devices` shows it) or the AGP
managed device bundled into this build (`api33`, AOSP system image).
+- The library JAR built by the parent Maven build:
+
+ ```shell
+ mvn -f.. -DskipTests package
```
## Running
Against an attached emulator/device:
-```
+```shell
./gradlew connectedAndroidTest
```
-Against the bundled AGP managed device (downloads the AOSP API 31 system image
on first
-run, then provisions and tears down a headless emulator for each invocation):
+Against the bundled AGP managed device
+(downloads the AOSP API 33 system image on first run,
+then provisions and tears down a headless emulator for each invocation):
+```shell
+./gradlew api33DebugAndroidTest
```
-./gradlew api31DebugAndroidTest
+
+## Coverage
+
+The run is instrumented with JaCoCo,
+pinned to the version the Maven build reports with,
+and leaves one execution data file per device under
`build/outputs/code_coverage`.
+The full round trip:
+
+1. Build the JAR the instrumented tests run against:
+
+ ```shell
+ mvn -f.. -DskipTests package
+ ```
+
+2. Run the tests on a device, either an attached one or the bundled managed
device:
+
+ ```shell
+ ./gradlew connectedDebugAndroidTest
+ ./gradlew api33DebugAndroidTest
+ ```
+
+3. Fold the device data into the project's coverage:
+
+ ```shell
+ mvn -f.. -Pjacoco verify site
+ ```
+
+ The JVM suite runs under the JaCoCo agent in this step,
+ so both halves end up in `target/jacoco.exec`:
+ the `jacoco` profile merges whatever this module produced into it in the
site lifecycle,
+ just before the report is written.
+ The coverage check runs earlier, on the JVM data alone,
+ so its minimums mean the same thing whether a device run is lying around;
+ the device data widens the report, not the bar.
+
+Repeat step 2 whenever the library changes.
+The execution data identifies each class by its bytecode,
+and JaCoCo drops data that no longer matches the compiled class,
+so a device run left over from an older JAR quietly lowers the numbers instead
of inflating them.
+
+### Maximum coverage
+
+This library is written to work from JDK 8 through 25 and on Android.
+Since new JAXP methods were introduced in JDK 9, 13, and 18,
+maximum coverage is obtained by the following recipe:
+
+```shell
+export JDK8=/path/to/jdk8
+export JDK21=/path/to/jdk21
+export ANDROID_HOME=/path/to/android
+JAVA_HOME=$JDK21 mvn -f.. -Pjacoco clean package
+./gradlew api33DebugAndroidTest
+JAVA_HOME=$JDK8 mvn -f.. -Pjacoco test
+JAVA_HOME=$JDK21 mvn -f.. -Pjacoco verify site
```
## Excluded test groups
-The build excludes JUnit 5 tags for JAXP types Android does not ship:
+The build runs the `dom`, `sax`, `schema`, and `trax` tags and excludes the
rest:
- `stax`: there is no `XMLInputFactory` on Android.
-- `schema`: there is no `SchemaFactory` on Android.
+- `xpath`: Android ships an XPath implementation,
+ but it is currently untested.
- `xpath3`: relies on Saxon, which is not on the Android classpath.
-DOM, SAX, TrAX and XPath 1.0 paths are exercised in full.
+DOM, SAX, TrAX, and schema paths are exercised in full;
+the schema tests run against the Apache Xerces the `androidTest` classpath
brings in,
+since Android ships `javax.xml.validation` without a `SchemaFactory`
implementation.
diff --git a/android-tests/build.gradle.kts b/android-tests/build.gradle.kts
index e8bfc34..845b1e1 100644
--- a/android-tests/build.gradle.kts
+++ b/android-tests/build.gradle.kts
@@ -42,6 +42,17 @@ android {
targetCompatibility = JavaVersion.VERSION_1_8
}
+ // Match the JaCoCo the Maven build reports with, so the on-device agent
understands the same execution data format.
+ testCoverage {
+ jacocoVersion = "0.8.15"
+ }
+
+ buildTypes {
+ getByName("debug") {
+ enableAndroidTestCoverage = true
+ }
+ }
+
sourceSets {
getByName("androidTest") {
java.srcDirs("../src/test/java")
diff --git a/pom.xml b/pom.xml
index 443148c..e63ea55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,14 +75,19 @@ limitations under the License.
<commons.xerces.version>2.12.2</commons.xerces.version>
<!-- Test-only: computes each secure class' shade closure, mirroring
maven-shade minimizeJar, for ShadingFootprintTest. -->
<commons.jdependency.version>2.16</commons.jdependency.version>
- <!-- jacoco-maven-plugin: Should only get better -->
+ <!--
+ jacoco-maven-plugin: Should only get better.
+
+ A single `mvn -Pjacoco clean verify` run covers less than the project as
a whole does.
+ These minima need to pass on all JDK versions, in particular JDK 8 and
JDK 21.
+ -->
<commons.jacoco.haltOnFailure>true</commons.jacoco.haltOnFailure>
- <commons.jacoco.classRatio>0.97</commons.jacoco.classRatio>
- <commons.jacoco.instructionRatio>0.97</commons.jacoco.instructionRatio>
- <commons.jacoco.methodRatio>0.99</commons.jacoco.methodRatio>
- <commons.jacoco.branchRatio>0.91</commons.jacoco.branchRatio>
- <commons.jacoco.lineRatio>0.96</commons.jacoco.lineRatio>
- <commons.jacoco.complexityRatio>0.96</commons.jacoco.complexityRatio>
+ <commons.jacoco.classRatio>0.96</commons.jacoco.classRatio>
+ <commons.jacoco.instructionRatio>0.93</commons.jacoco.instructionRatio>
+ <commons.jacoco.methodRatio>0.95</commons.jacoco.methodRatio>
+ <commons.jacoco.branchRatio>0.87</commons.jacoco.branchRatio>
+ <commons.jacoco.lineRatio>0.93</commons.jacoco.lineRatio>
+ <commons.jacoco.complexityRatio>0.92</commons.jacoco.complexityRatio>
</properties>
<dependencies>
<!--
@@ -453,9 +458,82 @@ limitations under the License.
</rulesets>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <reportSets>
+ <!-- The `report` and `failsafe-report` goals fork the test phase,
running the suite a second time; the `*-only` goals read what the build already
produced. -->
+ <reportSet>
+ <id>surefire</id>
+ <reports>
+ <report>report-only</report>
+ </reports>
+ <configuration>
+ <!-- The suite runs once per JAXP combination, each execution
into its own directory. -->
+ <reportsDirectories>
+
<reportsDirectory>${project.build.directory}/surefire-reports/jdk-xerces</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/saxon</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/saxon-xerces</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/stockjdk</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/woodstox</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/xalan</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/xalan-xerces</reportsDirectory>
+
<reportsDirectory>${project.build.directory}/surefire-reports/xerces</reportsDirectory>
+ </reportsDirectories>
+ </configuration>
+ </reportSet>
+ <reportSet>
+ <id>failsafe</id>
+ <!-- The integration tests keep the plugin's own default,
${project.build.directory}/failsafe-reports. -->
+ <reports>
+ <report>failsafe-report-only</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
</plugins>
</reporting>
<profiles>
+ <!--
+ Fold the Android instrumented run into the coverage data.
+ -->
+ <profile>
+ <id>jacoco</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>merge-android</id>
+ <phase>pre-site</phase>
+ <goals>
+ <goal>merge</goal>
+ </goals>
+ <configuration>
+ <fileSets>
+ <fileSet>
+ <directory>${project.build.directory}</directory>
+ <includes>
+ <include>jacoco.exec</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+
<directory>${project.basedir}/android-tests/build/outputs/code_coverage</directory>
+ <includes>
+ <include>**/*.ec</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+ <destFile>${project.build.directory}/jacoco.exec</destFile>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
<!--
Regression guard for the generated OSGi and JPMS descriptors.
The JPMS test requires JDK 9+