Skip to content

fix(runner): keep the injected XmlTest out of the ITestResult parameter snapshot - #3401

Merged
krmahadevan merged 3 commits into
masterfrom
fix/xmltest-parameter-snapshot
Aug 21, 2026
Merged

fix(runner): keep the injected XmlTest out of the ITestResult parameter snapshot#3401
krmahadevan merged 3 commits into
masterfrom
fix/xmltest-parameter-snapshot

Conversation

@juherr

@juherr juherr commented Aug 20, 2026

Copy link
Copy Markdown
Member

Fix #1994

Problem

TestResult#setParameters stores a clone of every parameter that implements Cloneable. XmlTest is Cloneable, and TestNG injects it into configuration and test methods, so it went through that path too.

XmlTest#clone() is a suite-building helper rather than a copy: it builds its result with new XmlTest(suite), and that constructor does m_suite.getTests().add(this). Snapshotting an injected XmlTest therefore appended a phantom <test> to the suite that was running — with empty <classes>, exactly as reported — and handed reporters a clone instead of the test they asked about.

A sample injecting XmlTest into @BeforeTest, @Test and @AfterTest ends the run with 5 tests in a one-test suite. Four phantoms for three invocations, because the test-method path snapshots twice (TestInvokerTestResult.newTestResult, then TestResult.newTestResultFrom).

Change

XmlTest is now kept by reference — which is the issue's stated expectation, "Current XmlTest is returned". XmlTest#clone() itself is untouched, so an IAlterSuiteListener can still rely on its registering side effect — org.testng.xml.issue2866.ThreadCountingSuiteAlteringListener does exactly that and stays green.

The reflective clone-if-Cloneable rule moves out of TestResult into a new package-private LegacyParameterSnapshotter, whose stated responsibility is to preserve the historical ITestResult parameter representation for backward compatibility. That rule is type-blind — it applies to values TestNG injected as readily as to user data — so giving it a name and a single owner is the first step towards separating the arguments a method was invoked with from the representation reporting needs.

Behaviour is otherwise identical, quirks included: getDeclaredMethod rather than getMethod (a type that merely inherits clone() is still not snapshotted), nulls passed through, failures degrading to the original reference.

Scope is deliberately narrow: no public API, no new annotation, no opt-out mechanism, no reporter migration, no change to XmlTest#clone() semantics or its Cloneable-ness. LegacyParameterSnapshotter is package-private, so nothing is added to the OSGi surface.

XmlTest is the only injectable type this affects: Parameters fixes the natively-injectable set as ITestContext, XmlTest, Method, Object[], ITestResult, and of those only XmlTest is Cloneable with a reachable declared clone().

Tests

  • test.inject.issue1994.IssueTest — functional. Injects XmlTest into @BeforeTest, @Test, and into an @AfterTest that mixes the injection into an @Optional parameter list, which is the shape the issue was reported with. Asserts the suite still holds exactly one XmlTest, that it is the original instance, and that all three reported parameters are that same instance. Confirmed red without the fix: 5 tests instead of 1.
  • org.testng.internal.TestResultParametersTest — unit. Pins the four rules through setParameters/getParameters rather than through the helper: XmlTest by reference, Cloneable snapshotted, non-Cloneable by reference, null preserved.

Assertions use hasSize + isSameAs, never containsExactly: XmlTest overrides equals, so a clone compares equal to its original and equality-based assertions do not discriminate.

The GITHUB-447 mutable-data-provider coverage is unchanged and stays green; its test was labelled GITHUB-1090 (an unrelated preserve-order fix per CHANGES.txt), corrected in a separate commit.

Follow-ups for the "execution vs reporting snapshots" refactoring

  1. TestResult.newTestResultFrom and the two setParameters(source.getParameters()) calls in TestInvoker feed an already-snapshotted array back through the whole loop — this is why the repro produces four phantoms for three invocations.
  2. getParameters() returns the live internal array, so a listener can corrupt the snapshot the mechanism exists to protect. FactoryInstance#getParameters() already returns parameters.clone().
  3. The clone rule applies to injected objects as much as to user data. Only XmlTest misbehaves today by luck — Object[].class.getDeclaredMethod("clone") throws, and ITestContext/ITestResult/Method are not Cloneable. Building the reporting snapshot only over user-supplied slots would delete the instanceof XmlTest case.
  4. getDeclaredMethod("clone") only sees the exact runtime class, so Issue with TextReporter output #447's guarantee is silently conditional on a detail users cannot see.
  5. setParameters(null) throws NPE rather than clearing, on a public interface method.

Verification

./gradlew build — BUILD SUCCESSFUL, 410 test classes, 0 failures.

Summary by CodeRabbit

  • Bug Fixes
    • Improved parameter handling in test results and reporting.
    • Cloneable parameters are now safely snapshotted, while non-cloneable values and null parameters retain expected behavior.
    • Injected XmlTest instances remain linked to the original running test configuration.
  • Documentation
    • Added the 7.13.0 changelog entry covering parameter handling and other release updates.

juherr added 3 commits August 19, 2026 22:20
ReportTest#github1090 drives GitHub447Sample through GitHub447Listener and
asserts that each result kept the data-provider row as it was at invocation
time, which is exactly the GITHUB-447 guarantee. GITHUB-1090, per CHANGES.txt,
is an unrelated preserve-order fix, so the label sent readers to the wrong
issue and left the snapshot behaviour looking uncovered.

Name and description only; the fixtures and assertions are unchanged.
…er snapshot

TestResult#setParameters stored a clone of every Cloneable parameter. XmlTest is
Cloneable and TestNG injects it into configuration and test methods, so it went
through that path too -- and XmlTest#clone() is a suite-building helper rather
than a copy: it builds its result with new XmlTest(suite), whose constructor
registers the new test in that suite. Snapshotting the parameter therefore
appended a phantom <test> to the suite that was running, once per invocation
that received one, and handed reporters a clone instead of the real test.

XmlTest is now kept by reference. XmlTest#clone() itself is untouched, so an
IAlterSuiteListener can still rely on it to add a test.

The reflective clone-if-Cloneable rule moves out of TestResult into
LegacyParameterSnapshotter, whose stated responsibility is to preserve the
historical ITestResult parameter representation for backward compatibility.
Behaviour is otherwise identical, quirks included: getDeclaredMethod rather than
getMethod, nulls passed through, failures degrading to the original reference.
Separating the arguments a method was invoked with from the representation
reporting needs is the follow-up; naming the compatibility layer is the first
step towards it.

Fix #1994
Cover the shape GITHUB-1994 was reported with -- native injection mixed into an
@optional parameter list -- rather than a third plain injection point, and fold
the collector listener into the test that uses it, matching the nested-listener
style of Github1649Test and FactoryInstanceTest. The list it fills is no longer
wrapped in synchronizedList: the sample suite is single-threaded, and the getter
handed the live list to an unsynchronized iteration anyway, so the wrapper
advertised a guarantee it did not provide.

Trim the argumentative tail of the LegacyParameterSnapshotter javadoc down to
the fact it was resting on, rename the private overload to snapshotParameter so
resolution between Object[] and Object is not a question, and correct the unit
test's javadoc, which claimed to use only public API while calling an internal
factory.
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d673bf4-d067-48c0-8e30-c2184614e3a4

📥 Commits

Reviewing files that changed from the base of the PR and between e4480ce and 087988a.

📒 Files selected for processing (8)
  • CHANGES.txt
  • testng-core/src/test/java/org/testng/internal/TestResultParametersTest.java
  • testng-core/src/test/java/test/inject/issue1994/IssueTest.java
  • testng-core/src/test/java/test/inject/issue1994/XmlTestInjectionSample.java
  • testng-core/src/test/java/test/reports/ReportTest.java
  • testng-core/src/test/resources/testng.xml
  • testng-runner-api/src/main/java/org/testng/internal/LegacyParameterSnapshotter.java
  • testng-runner-api/src/main/java/org/testng/internal/TestResult.java

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

This change adds centralized parameter snapshotting for TestResult. Cloneable parameters are copied, while XmlTest, non-cloneable values, and null remain preserved. New regression tests validate injection identity and parameter behavior. The changelog records the 7.13.0 release changes.

Changes

XmlTest parameter snapshot handling

Layer / File(s) Summary
Parameter snapshot implementation
testng-runner-api/src/main/java/org/testng/internal/..., CHANGES.txt
LegacyParameterSnapshotter centralizes reflective cloning and fallback behavior. TestResult.setParameters delegates to it. The changelog adds the 7.13.0 release entry.
Regression coverage and test wiring
testng-core/src/test/java/..., testng-core/src/test/resources/testng.xml
Tests cover XmlTest identity, cloneable and non-cloneable parameters, null values, and reported snapshots. The new tests are added to the relevant suites, and an existing test is renamed for its current issue reference.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 08798

This change narrowly prevents injected XmlTest values from being cloned into phantom suite entries while preserving existing clone behavior elsewhere. No actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

Suggested reviewers: krmahadevan

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The core changes are in scope, but the unrelated ReportTest rename and broad changelog update are not tied to issue #1994. Remove the unrelated ReportTest rename or justify it separately, and split unrelated changelog content into a separate change.
Docstring Coverage ⚠️ Warning Docstring coverage is 5.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preserving injected XmlTest references in ITestResult parameter snapshots.
Linked Issues check ✅ Passed The implementation and tests satisfy issue #1994 by preventing XmlTest cloning and duplicate suite tests while preserving other snapshot behavior.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/xmltest-parameter-snapshot

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@krmahadevan
krmahadevan merged commit d34dc1f into master Aug 21, 2026
29 checks passed
@krmahadevan
krmahadevan deleted the fix/xmltest-parameter-snapshot branch August 21, 2026 12:04
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.

Duplicate Tests being added to XmlTests if XmlTest is used in the parameters of annotated methods

2 participants