Visitar URL original
Fix failing integration tests (#442) by elharo · Pull Request #499 · apache/maven-resources-plugin · GitHub
Skip to content

Fix failing integration tests (#442) - #499

Closed
elharo wants to merge 13 commits into
masterfrom
fix/issue-442-failing-integration-tests-rc5
Closed

elharo wants to merge 13 commits into
masterfrom
fix/issue-442-failing-integration-tests-rc5

Conversation

@elharo

@elharo elharo commented Aug 1, 2026 •

Copy link
Copy Markdown
Contributor
  • Disables Jenkins because it doesn't provide maven 4.0.0-rc-4 and RC5 is buggy
  • GitHub Actions (which runs the ITs with 4.0.0-rc-4) passes across the whole OS/JDK matrix.

Maven 4.0.0-rc-5 introduced a bug in EnhancedCompositeBeanHelper where field
accessibility state was cached globally, which could cause plugin configuration
injection to fail when the same configuration field is accessed multiple times
or in different contexts during a build (e.g., when both 'resources' and
'testResources' goals run in the same build).

This caused 4 integration test failures:
- escapeInterpolation, filter-test-resources, MRESOURCES-77: build filters
  were not applied to test resources because the buildFilters field was not
  properly injected for TestResourcesMojo
- MRESOURCES-131: the skip configuration did not work for testResources
  because TestResourcesMojo.skip was not properly injected

Fixes:

1. TestResourcesMojo: Remove the private 'skip' field that shadows the parent
   class field (ResourcesMojo.skip), which triggered the rc-5 field accessibility
   caching bug. Replace with isTestSkip() method that checks:
   - The inherited isSkip() (handles <skip>true</skip> in plugin config)
   - The 'maven.test.skip' session property (handles -Dmaven.test.skip=true)

2. ResourcesMojo: Add fallback in getCombinedFiltersList() to read build
   filters directly from the project model when the buildFilters field was not
   properly injected. This makes filter loading robust against field injection
   failures in Maven 4 when processing class hierarchies.

3. Upgrade mavenVersion from 4.0.0-rc-4 to 4.0.0-rc-5 so the CI tests
   against the Maven version that exposed these issues.

The Maven-side fix is in 4.0.0-rc-6 (apache/maven#11433), but these plugin-side
changes ensure correct behavior with Maven 4.0.0-rc-5 as well.

Closes #442

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Maven 4.0.0-rc-5 integration test failures by making test-resources execution more resilient to Maven’s field-injection edge cases and by aligning the project’s target Maven version with the failing environment.

Changes:

  • Removes TestResourcesMojo’s shadowing skip field and replaces it with isTestSkip() that checks both the inherited plugin skip parameter and maven.test.skip from the session.
  • Adds a fallback in ResourcesMojo#getCombinedFiltersList() to read build filters directly from the project model when buildFilters injection is missing.
  • Bumps <mavenVersion> to 4.0.0-rc-5 in pom.xml.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/main/java/org/apache/maven/plugins/resources/TestResourcesMojo.java Avoids skip field shadowing and adds session-based maven.test.skip handling.
src/main/java/org/apache/maven/plugins/resources/ResourcesMojo.java Makes build filter resolution robust when buildFilters is not injected.
pom.xml Updates the Maven prerequisite/target version to 4.0.0-rc-5.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* and the {@code maven.test.skip} property from the session.
*
* @return {@code true} if test resources should not be copied
* @since 3.x
* Its use is NOT RECOMMENDED, but quite convenient on occasion.
* @since 2.6
*/
@Parameter(property = "maven.test.skip", defaultValue = "false")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For compatibility reasons, the parameter can't be removed here, even if it's use is a no-op

* @return {@code true} if test resources should not be copied
* @since 3.x
*/
protected boolean isTestSkip() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected --> private

* and the {@code maven.test.skip} property from the session.
*
* @return {@code true} if test resources should not be copied
* @since 3.x

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this line

Comment thread pom.xml Outdated

<properties>
<mavenVersion>4.0.0-rc-4</mavenVersion>
<mavenVersion>4.0.0-rc-5</mavenVersion>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we jump to 4.0.0-rc-6?

RC4 and RC5 compatibility are not required

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to jump to 4.0.0-rc-6, but it is not yet published to Maven Central (only 4.0.0-rc-5 is available there; rc-6 is still in the Apache staging repository). The CI workflow for this repo also runs the integration tests with 4.0.0-rc-4 and cannot be changed in this PR (no workflow scope on the token), so a newer mavenVersion fails the plugin prerequisite check during the ITs. To keep CI green I reverted mavenVersion to 4.0.0-rc-4 (matching CI) and fixed the failing integration tests at the code level instead. All 27 ITs pass locally with 4.0.0-rc-4. We can bump mavenVersion + the CI workflow to rc-6 as soon as it is published and a workflow-scoped push is available.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: the real root cause turned out to be apache/maven#11425, fixed in 4.0.0-rc-6: the rc-5 enhanced configurator silently fails to write private fields via direct field injection (field-accessibility cache leak), leaving parameters without setters at their JVM defaults. That is exactly why escapeString, skip, buildFilters and useBuildFilters were ignored in the failing ITs.

This PR now adds setters for those parameters (setSkip, setEscapeString, setBuildFilters, setUseBuildFilters). The configurator prefers setter methods, which do not need setAccessible and therefore work on rc-4, rc-5 and rc-6 alike. All 27 ITs now pass under both 4.0.0-rc-4 and 4.0.0-rc-5.

I kept mavenVersion at 4.0.0-rc-4 because rc-6 is not yet in Maven Central (it is still in the Apache staging repository) and the repo CI workflow runs the ITs with 4.0.0-rc-4 and cannot be changed from this PR (no workflow scope). Happy to bump to rc-6 (and the CI workflow) as soon as it is published.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: after further discussion I reverted the setter-based workaround. Following the same convention as maven-jar-plugin (which closed its 4.0.0-rc-5 bump waiting for 4.0.0-rc-6), this PR keeps mavenVersion on 4.0.0-rc-4 and does not try to paper over the rc-5 configurator bug (apache/maven#11425) in the plugin. All 27 ITs pass under rc-4 (verified locally and on the GitHub Actions matrix). The rc-5 failures are fixed by Maven itself in 4.0.0-rc-6; once that is published to Maven Central we can bump mavenVersion and the CI workflow (which currently pins rc-4).

Keep the maven.test.skip parameter for backward compatibility, make
isTestSkip() private, and drop the non-specific @SInCE 3.x tag.

Also revert mavenVersion to 4.0.0-rc-4 so the plugin prerequisite
matches the Maven version CI uses to run the integration tests.
@elharo elharo changed the title Fix failing integration tests with Maven 4.0.0-rc-5 (#442) Fix failing integration tests (#442) Aug 1, 2026
@elharo

elharo commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

retest this please

@elharo
elharo marked this pull request as draft August 1, 2026 13:24
@elharo

elharo commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

I think the problem at root is that maven-resources-plugin is tripping over a bug in RC5 and Jenkins does not have RC4 installed. TBH we should drop jenkins.

@elharo
elharo marked this pull request as ready for review August 2, 2026 11:00
@elharo
elharo requested a review from cstamas August 2, 2026 11:00

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI passes across the full OS/JDK matrix. The Jenkinsfile removal is warranted given the stated blocker. Two issues with the isTestSkip() approach worth addressing before merge.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

*/
public void execute() throws MojoException {
if (skip) {
if (skip || isTestSkip()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Redundant check — skip and isTestSkip() both cover maven.test.skip.

The local skip field is @Parameter(property = "maven.test.skip"). isTestSkip() also reads maven.test.skip from session properties. So this condition checks the same property twice: once via field injection, once via the session fallback. That's the intent (tolerate rc-5 injection failure), but it means isTestSkip() is NOT a clean abstraction — it's a partial duplicate of the outer check.

The cleaner fix is to move all skip logic into isTestSkip() and drop the skip || from the outer call:

Suggested change
if (skip || isTestSkip()) {
if (isTestSkip()) {

Then update isTestSkip() to be the single source of truth:

private boolean isTestSkip() {
    // Check injection-based field first (prefers setter over direct field — works on all rc versions)
    if (skip) {
        return true;
    }
    // Fallback: read maven.test.skip directly from session (tolerates field-injection failure)
    Map<String, String> userProps = session.getUserProperties();
    Map<String, String> sysProps = session.getSystemProperties();
    String testSkip = userProps.getOrDefault("maven.test.skip", sysProps.get("maven.test.skip"));
    if (Boolean.parseBoolean(testSkip)) {
        return true;
    }
    // Also honour the parent's maven.resources.skip (skips all resource processing)
    return isSkip();
}

This makes the fallback intent explicit and removes the confusing skip || at the call site.

Comment on lines +81 to +83
* Returns {@code true} if test resource copying should be skipped.
* Checks both the inherited {@code skip} parameter (bound to {@code maven.resources.skip})
* and the {@code maven.test.skip} property from the session.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Javadoc is inaccurate. The method does NOT check maven.test.skip via the injected skip parameter — that check is in execute(), outside this method. isTestSkip() checks (1) maven.resources.skip via isSkip() and (2) maven.test.skip via session properties. The current wording implies both checks live inside isTestSkip() and is misleading to readers.

Suggested change
* Returns {@code true} if test resource copying should be skipped.
* Checks both the inherited {@code skip} parameter (bound to {@code maven.resources.skip})
* and the {@code maven.test.skip} property from the session.
* Returns {@code true} if test resource copying should be skipped.
* Checks {@code maven.resources.skip} (inherited from the parent mojo) and reads
* {@code maven.test.skip} directly from the session properties as a fallback when
* field injection is unavailable.

Resolves conflicts:
- Jenkinsfile.disable: removed (Jenkins disabled, aligns with PR intent)
- TestResourcesMojo.java: keep 'skip || isTestSkip()' — apache/maven#12626
  (fix field cache precedence) landed in rc-7, so isTestSkip() now works
  correctly without the isSkip() workaround

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after merge from master — the new commit (52dc1da) is a merge of master into the branch. No author changes since the last review.

⚠️ This PR is largely superseded by #500 (merged Aug 4), which already fixed the same skip field-shadowing issue on master with the skip || isSkip() approach. Master is now on Maven 4.0.0-rc-7, where the underlying configurator bug (apache/maven#11425) is fixed. The session-property fallback in isTestSkip() was designed to work around that rc-5 bug and is no longer needed.

Previous findings status: Both findings from the prior review remain unaddressed — no code changes were made; the only new commit is the merge.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

// buildFieldCache() lets a parent field shadow the child's, and then this
// class's own field will be configured directly.
if (skip || isSkip()) {
if (skip || isTestSkip()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Still unaddressed from previous review. skip is @Parameter(property = "maven.test.skip") — field injection already handles maven.test.skip. isTestSkip() also reads maven.test.skip from the session. The outer skip || duplicates that check.

Moreover, with master now on rc-7 (where the field-injection bug is fixed), the session-property fallback in isTestSkip() is unnecessary. The simpler skip || isSkip() from master (PR #500) already handles both maven.test.skip (via skip field) and maven.resources.skip (via isSkip()).

Suggestion: consider whether this PR should be closed in favor of #500's approach already on master, or rebased to only delete Jenkinsfile.disable.

Comment on lines +80 to +95
/**
* Returns {@code true} if test resource copying should be skipped.
* Checks both the inherited {@code skip} parameter (bound to {@code maven.resources.skip})
* and the {@code maven.test.skip} property from the session.
*
* @return {@code true} if test resources should not be copied
*/
private boolean isTestSkip() {
if (isSkip()) {
return true;
}
Map<String, String> userProps = session.getUserProperties();
Map<String, String> sysProps = session.getSystemProperties();
String testSkip = userProps.getOrDefault("maven.test.skip", sysProps.get("maven.test.skip"));
return Boolean.parseBoolean(testSkip);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Unnecessary complexity given rc-7. This entire method is a workaround for the rc-5 configurator bug (apache/maven#11425) that silently failed to write private fields via direct field injection. That bug was fixed in rc-6 and master is now on rc-7.

The session-property fallback (userProps.getOrDefault("maven.test.skip", sysProps.get(...))) will never fire on rc-6+ because the skip field is correctly injected. This adds dead code and an unnecessary java.util.Map import.

Javadoc is also still inaccurate (previous finding): the doc says it checks "the inherited skip parameter (bound to maven.resources.skip)" but isSkip() is what checks maven.resources.skip — the local skip field is bound to maven.test.skip, not maven.resources.skip.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after merge with master — the merge itself is clean (picks up rc-7 bump, site doc conversions, etc.), but the two findings from the previous review remain unaddressed.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

// buildFieldCache() lets a parent field shadow the child's, and then this
// class's own field will be configured directly.
if (skip || isSkip()) {
if (skip || isTestSkip()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Still unaddressed from previous review. skip (field injected from maven.test.skip) and isTestSkip() (reads maven.test.skip from session properties) both check the same property — the outer skip || is redundant with the session-based fallback inside isTestSkip().

The cleaner approach remains: move skip into isTestSkip() so it is the single source of truth for all skip logic:

Suggested change
if (skip || isTestSkip()) {
if (isTestSkip()) {

Then update isTestSkip() to incorporate the field check:

private boolean isTestSkip() {
    if (skip || isSkip()) {
        return true;
    }
    Map<String, String> userProps = session.getUserProperties();
    Map<String, String> sysProps = session.getSystemProperties();
    String testSkip = userProps.getOrDefault("maven.test.skip", sysProps.get("maven.test.skip"));
    return Boolean.parseBoolean(testSkip);
}

This makes the intent clear: isTestSkip() checks everything, and execute() just calls it.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after new commit (3fa91d10 — restores Jenkinsfile.disable from master). No author code changes since the last review.

All 3 previous findings remain unaddressed:

  1. Redundant skip || at call site — skip (field-injected from maven.test.skip) and isTestSkip() (reads maven.test.skip from session) both check the same property. The outer skip || is redundant.
  2. Javadoc inaccuracy — doc says isTestSkip() checks "the inherited skip parameter (bound to maven.resources.skip)" but isSkip() is what checks maven.resources.skip; the local skip field is bound to maven.test.skip.
  3. Dead code on rc-6+ — the session-property fallback in isTestSkip() was a workaround for the rc-5 configurator bug (apache/maven#11425), fixed in rc-6. Master is on rc-7. The fallback will never fire.

This PR is largely superseded by #500 (merged Aug 4), which already fixed the skip field-shadowing issue on master with skip || isSkip(). The remaining diff introduces isTestSkip() with unnecessary complexity for a bug that no longer exists.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@@ -62,17 +64,7 @@ public class TestResourcesMojo extends ResourcesMojo {
* {@inheritDoc}
*/
public void execute() throws MojoException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Still unaddressed (4th review). skip is @Parameter(property = "maven.test.skip") — field injection handles maven.test.skip. isTestSkip() also reads maven.test.skip from the session. The skip || duplicates the check.

With master now on rc-7 (field-injection bug fixed), the session-property fallback is dead code. Master's approach from PR #500 (skip || isSkip()) is simpler and sufficient:

Suggested change
public void execute() throws MojoException {
if (skip || isSkip()) {

Or, if this PR's intent is to add value beyond #500, consider closing it — #500 already merged the equivalent fix.

Comment on lines 79 to +83

/**
* Returns {@code true} if test resource copying should be skipped.
* Checks both the inherited {@code skip} parameter (bound to {@code maven.resources.skip})
* and the {@code maven.test.skip} property from the session.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Javadoc still inaccurate (3rd review). isSkip() checks maven.resources.skip, not the "inherited skip parameter". The local skip field is bound to maven.test.skip. The doc conflates the two.

Suggested change
/**
* Returns {@code true} if test resource copying should be skipped.
* Checks both the inherited {@code skip} parameter (bound to {@code maven.resources.skip})
* and the {@code maven.test.skip} property from the session.
* Returns {@code true} if test resource copying should be skipped.
* Checks {@code maven.resources.skip} via the inherited {@link #isSkip()} and reads
* {@code maven.test.skip} directly from session properties as a fallback when
* field injection is unavailable.

Comment on lines +84 to +95
*
* @return {@code true} if test resources should not be copied
*/
private boolean isTestSkip() {
if (isSkip()) {
return true;
}
Map<String, String> userProps = session.getUserProperties();
Map<String, String> sysProps = session.getSystemProperties();
String testSkip = userProps.getOrDefault("maven.test.skip", sysProps.get("maven.test.skip"));
return Boolean.parseBoolean(testSkip);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Dead code on rc-6+. This entire method is a workaround for the rc-5 configurator bug (apache/maven#11425) that silently failed to write private fields. Fixed in rc-6; master is on rc-7. The session-property fallback will never fire because skip is correctly injected.

Master already uses skip || isSkip() (PR #500) which handles both maven.test.skip (via field) and maven.resources.skip (via isSkip()). This method adds an unused java.util.Map import and dead code paths.

Recommend either:

@gnodet gnodet closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants