Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline native injection w.r.t cloning #2001

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

krmahadevan
Copy link
Member

@krmahadevan krmahadevan commented Jan 6, 2019

Closes #1994

Currently as part of supporting native injection
TestNG resorts to invoking the “clone()” method
on the parameter being natively injected.

This becomes a problem because XmlTest is the only
internal TestNG object that implements the Cloneable
interface wherein it resorts to something like a deep
copy and adds the cloned instance to the XmlSuite.

This causes a lot of phony XmlTest objects to be
added up to the suite and causes issues as detailed
in the bug.

Fixed this by introducing a marker interface to
indicate to TestNG to skip cloning and use the object
as is. Annotated “XmlTest” with this annotation
so that we dont clone xmltest but instead use it
as is.

Yes this will expose the XmlTest object, but its
already exposed to the end user via various different
means. So I guess we are ok here.

Fixes #1994 .

Did you remember to?

  • Add test case(s)
  • Update CHANGES.txt

Summary by CodeRabbit

  • New Features
    • Introduced a method to allow overriding error messages in assertions.
    • Added @SkipCloning annotation to prevent cloning of objects for dependency injection.
    • Enhanced object copying functionality with a new copy constructor and deprecated the old cloning method in XmlTest.
    • Added a test to ensure native injection does not resort to cloning.
  • Bug Fixes
    • Fixed duplication issues with XmlTest objects used as parameters.
    • Resolved execution problems with @AfterGroups when group members fail or are skipped.
    • Ensured @BeforeGroups is invoked only when groups are explicitly specified.
  • Documentation
    • Deprecated methods and usage instructions updated across various components.

@krmahadevan
Copy link
Member Author

@juherr - As decided in #1996 I have added a copy constructor, deprecated the clone() method and also chose to retain the newly introduced annotation (I feel that if its evangelized properly it would help TestNG to skip costly clone methods in the user defined object that is being injected as a parameter).

@@ -0,0 +1,16 @@
package org.testng.annotations;
Copy link
Member

Choose a reason for hiding this comment

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

Move it in the internal/annotations package

Copy link
Member Author

Choose a reason for hiding this comment

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

@juherr - Here's my rationale behind leaving this exposed ( I also would like to evangelize about this lesser known behavior of TestNG )

Currently when it comes to parameter injection and associating that with the TestResult object, we resort to cloning() if the user's parameter object class implements the Cloneable interface. So there could be a chance wherein the parameters that the user is altering in their tests is perhaps not reflected in their listeners or in their reports because we cloned them.

I think we should enrich our documentation to first call out this behavior explicitly ( a most common use case is a data driven test that is run via a @DataProvider annotation) and via this newly introduced annotation, still let the user control whether they want or do not want their test objects to be cloned by TestNG.

Copy link
Member

Choose a reason for hiding this comment

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

I disagree because you want to make public something which wasn't asked.
Then, the clone solution was just a quick workaround for a design issue when the same object is used in reports. But the clone solution doesn't cover all the need.

That's why I have some doubts to publish a workaround of a workaround before be really sure how we need to fix the root issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

I disagree because you want to make public something which wasn't asked.

The changeset that introduced setting cloned variants of parameters was to fix #447 It was done by you via commit 78001fd

Via this commit we ended up implicitly cloning a parameter (if it was clone able) but never made this information public. IMO we should have done this, because an end-user should know if the real parameter that is being passed to the test method is being used or if its a cloned one.

We haven't heard of anyone mention about this problem only because usually our users dont implement the Cloneable interface to the parameter that they are passing to a data provider.

The moment someone does that, this implicit behavior will become evident.

So now it looks like the root cause was us introducing the Cloneable parameter.
What if the user provided parameter was clone able but the user didn't want it to be clone able ? How would they be able to support that. With collections it may not be possible because a user cannot add that annotation to a collection class (I think this also should be possible if we just enrich @SkipCloning annotation such that it can be added to a parameter just as how a user adds @Optional), but at least with a user's test project defined objects it should be something that they should be able to do.

This was my thinking.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I understand your point of view. But mine is the #447 was just a not complete workaround of the root issue.
I agree to release another workaround to fix the first workaround but I'm not feeling it good to make it public because we will have to manage deprecation.
Instead, I prefer to make it private and to find a good solution for the root issue.
Furthermore, I'm not sure my old commit will still work on with Java9 modules due to clone.setAccessible(true).

For the current PR:

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok.. how about this one.

For the current issue.

  1. Rename SkipCloning to Clone (This annotation will still be public)
  2. Revamp our logic to not look for Cloneable implementation alone, but also expect that the user annotate the parameter using @Clone annotation and only if both these are satisfied we resort to cloning. (This will break Issue with TextReporter output #447 but we are fixing it in the right way. All the user would need to do is add the newly created annotation as well to their Collection based parameters)
  3. Remove XmlTest from being annotated with any annotation.

For a permanent fix for #447 the following would be the recommendation (We can call this out in the documentation)

If TestNG should use a copy of the parameters then the following should be done

  1. Annotate the parameter using @Clone
  2. The parameter should implement Cloneable interface.

If for this also there's no agreement then I will leave this PR open and then start with the next issue (because I guess we would have come to consensus that Lets agree to disagree)

Copy link
Member Author

Choose a reason for hiding this comment

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

ping @juherr

Copy link
Member

Choose a reason for hiding this comment

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

SkipCloning or Clone, the problem is still the same: I don't see the use case of the feature.
The Cloneable fix was a quick fix which should be think again.

Copy link
Member Author

Choose a reason for hiding this comment

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

Without giving the control of deciding which parameter to be cloned and which not to, how do you propose the problem will be fixed ? Can you please help elaborate that ?

@cbeust what are you thoughts on this ?

Closes testng-team#1994

Currently as part of supporting native injection
TestNG resorts to invoking the “clone()” method 
on the parameter being natively injected.

This becomes a problem because XmlTest is the only
internal TestNG object that implements the Cloneable
interface wherein it resorts to something like a deep
copy and adds the cloned instance to the XmlSuite.

This causes a lot of phony XmlTest objects to be 
added up to the suite and causes issues as detailed
in the bug.

Fixed this by introducing a marker interface to 
indicate to TestNG to skip cloning and use the object
as is. Annotated “XmlTest” with this annotation 
so that we dont clone xmltest but instead use it 
as is.

Yes this will expose the XmlTest object, but its 
already exposed to the end user via various different
means. So I guess we are ok here.
@krmahadevan
Copy link
Member Author

Rebased off of master and resolved merge conflicts.

@juherr
Copy link
Member

juherr commented Feb 3, 2024

@krmahadevan Currently testing a new tool

Copy link

coderabbitai bot commented Feb 3, 2024

@juherr, based on the output from the GitHub CLI, the files changed in PR #2001 include:

  • CHANGES.txt
  • src/main/java/org/testng/annotations/SkipCloning.java
  • src/main/java/org/testng/internal/TestResult.java
  • src/main/java/org/testng/xml/XmlTest.java
  • src/test/java/test/parameters/ParameterTest.java
  • src/test/java/test/parameters/issue1994/TestclassSample.java

These changes seem to align with the description provided by @krmahadevan, including the addition of a copy constructor, deprecation of the clone() method, and the introduction of a new annotation. I will now proceed to review these changes focusing on the implementation details and adherence to the project's guidelines and the intentions mentioned in the previous PR discussion.

Analysis chain
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Fetch the list of files changed in PR #2001
gh pr view 2001 --repo testng-team/testng --json files

Length of output: 574

Action performed

Review triggered.

@testng-team testng-team deleted a comment from coderabbitai bot Feb 3, 2024
@juherr
Copy link
Member

juherr commented Feb 3, 2024

@coderabbitai review

@testng-team testng-team deleted a comment from coderabbitai bot Feb 3, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 4

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 275d1fa and fa4d026.
Files selected for processing (6)
  • CHANGES.txt (1 hunks)
  • src/main/java/org/testng/annotations/SkipCloning.java (1 hunks)
  • src/main/java/org/testng/internal/TestResult.java (3 hunks)
  • src/main/java/org/testng/xml/XmlTest.java (3 hunks)
  • src/test/java/test/parameters/ParameterTest.java (3 hunks)
  • src/test/java/test/parameters/issue1994/TestclassSample.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • src/main/java/org/testng/annotations/SkipCloning.java
Additional comments: 5
src/test/java/test/parameters/ParameterTest.java (2)
  • 17-17: Ensure that TestclassSample is used within this test class. If not directly used, consider removing the import statement to keep the code clean.
  • 134-139: The test testToEnsureNativeInjectionDoesnotResortToCloning correctly asserts that TestclassSample.count equals 1, aligning with the objective to verify that cloning does not occur. Ensure that the setup for this test guarantees only one instance of XmlTest is created to validate this assertion accurately.
src/main/java/org/testng/internal/TestResult.java (1)
  • 293-309: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [280-305]

The implementation of canAttemptCloning and its usage in setParameters correctly checks for the @SkipCloning annotation before attempting to clone parameters. This approach aligns with the objective to selectively bypass cloning for certain objects. Ensure that all relevant classes are annotated with @SkipCloning as needed.

src/main/java/org/testng/xml/XmlTest.java (2)
  • 20-20: The addition of the @SkipCloning annotation to XmlTest is appropriate and aligns with the objective to prevent unnecessary cloning of XmlTest objects.
  • 512-516: The deprecation of the clone method in favor of using the copy constructor is a good practice. However, ensure that all references to clone() within the codebase are updated to use the new copy constructor to avoid deprecation warnings.

@@ -1,4 +1,5 @@
Current
Fixed: GITHUB-1994: Prevent duplication of XmlTest objects when its used as a parameter for Native Injection (Krishnan Mahadevan)
Copy link

Choose a reason for hiding this comment

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

Fixed: Typo in the summary for the addition related to the Assertion class.

- Added a method in the Assertion class to enable downstream TestNG consumers to override the error message (Ryan Laseter)
+ Added a method in the Assertion class to allow downstream TestNG consumers to override the error message (Ryan Laseter)

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
Fixed: GITHUB-1994: Prevent duplication of XmlTest objects when its used as a parameter for Native Injection (Krishnan Mahadevan)
Fixed: GITHUB-1994: Prevent duplication of XmlTest objects when its used as a parameter for Native Injection (Krishnan Mahadevan)
Added a method in the Assertion class to allow downstream TestNG consumers to override the error message (Ryan Laseter)

Comment on lines +15 to +17
@BeforeClass
public void beforeClass(XmlTest xmlTest) {
}
Copy link

Choose a reason for hiding this comment

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

The beforeClass method is empty. If this is intentional, consider adding a comment explaining why; otherwise, implement the necessary logic or remove it if unused.

Comment on lines +19 to +21
@Test
public void testMethod() {
}
Copy link

Choose a reason for hiding this comment

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

The testMethod is empty. Similar to beforeClass, add a comment if this is intentional, implement required logic, or remove if unused.

Comment on lines +80 to +103
/**
* This constructor acts as a copy constructor. Please note that it does not automatically
* associate the copied {@link XmlTest} object with the current {@link XmlSuite} object and
* requires it to be done explicitly.
*
* @param xmlTest - The {@link XmlTest} object to copy from.
*/
public XmlTest(XmlTest xmlTest) {
XmlTest result = new XmlTest();
result.setName(getName());
result.setIncludedGroups(getIncludedGroups());
result.setExcludedGroups(getExcludedGroups());
result.setJUnit(isJUnit());
result.setParallel(getParallel());
result.setVerbose(getVerbose());
result.setParameters(getLocalParameters());
result.setXmlPackages(getXmlPackages());
result.setTimeOut(getTimeOut());

Map<String, List<String>> metagroups = getMetaGroups();
for (Map.Entry<String, List<String>> group : metagroups.entrySet()) {
result.addMetaGroup(group.getKey(), group.getValue());
}
}
Copy link

Choose a reason for hiding this comment

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

The copy constructor in XmlTest is implemented correctly but contains an unnecessary instantiation of XmlTest (XmlTest result = new XmlTest();) which is not used. Remove this line to clean up the constructor.

-    XmlTest result = new XmlTest();

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
/**
* This constructor acts as a copy constructor. Please note that it does not automatically
* associate the copied {@link XmlTest} object with the current {@link XmlSuite} object and
* requires it to be done explicitly.
*
* @param xmlTest - The {@link XmlTest} object to copy from.
*/
public XmlTest(XmlTest xmlTest) {
XmlTest result = new XmlTest();
result.setName(getName());
result.setIncludedGroups(getIncludedGroups());
result.setExcludedGroups(getExcludedGroups());
result.setJUnit(isJUnit());
result.setParallel(getParallel());
result.setVerbose(getVerbose());
result.setParameters(getLocalParameters());
result.setXmlPackages(getXmlPackages());
result.setTimeOut(getTimeOut());
Map<String, List<String>> metagroups = getMetaGroups();
for (Map.Entry<String, List<String>> group : metagroups.entrySet()) {
result.addMetaGroup(group.getKey(), group.getValue());
}
}
/**
* This constructor acts as a copy constructor. Please note that it does not automatically
* associate the copied {@link XmlTest} object with the current {@link XmlSuite} object and
* requires it to be done explicitly.
*
* @param xmlTest - The {@link XmlTest} object to copy from.
*/
public XmlTest(XmlTest xmlTest) {
result.setName(getName());
result.setIncludedGroups(getIncludedGroups());
result.setExcludedGroups(getExcludedGroups());
result.setJUnit(isJUnit());
result.setParallel(getParallel());
result.setVerbose(getVerbose());
result.setParameters(getLocalParameters());
result.setXmlPackages(getXmlPackages());
result.setTimeOut(getTimeOut());
Map<String, List<String>> metagroups = getMetaGroups();
for (Map.Entry<String, List<String>> group : metagroups.entrySet()) {
result.addMetaGroup(group.getKey(), group.getValue());
}
}

Copy link

coderabbitai bot commented Feb 3, 2024

Walkthrough

The recent updates to TestNG focus on enhancing the framework's reliability and usability. Key changes include the addition of a method to customize error messages in assertions, improvements to prevent the duplication of XmlTest objects, and adjustments ensuring group-related annotations work more intuitively. A significant introduction is the @SkipCloning annotation, aimed at refining the dependency injection mechanism by avoiding unnecessary cloning, promoting efficiency, and reducing errors.

Changes

File Path Change Summary
org.testng.annotations/SkipCloning.java Introduced @SkipCloning annotation to control object cloning during dependency injection.
org.testng.internal/TestResult.java Added method to check cloning eligibility, incorporating @SkipCloning logic.
org.testng.xml/XmlTest.java New copy constructor and @SkipCloning usage, deprecated clone() method.
test/parameters/ParameterTest.java
test/parameters/issue1994/TestclassSample.java
Testing enhancements for native injection and addressing issue #1994.

Assessment against linked issues

Objective Addressed Explanation
Identify and resolve duplication in XmlTest with parameters (#1994)
Prevent NullPointerExceptions related to duplicate/empty test cases (#1994)
Ensure consistent test behavior across different environments (#1994) The changes seem to address consistency but lack environment-specific validation details.
Verify solution with a sample suite and check for correct number of tests in m_tests (#1994) The summary does not mention explicit verification with a sample suite.

Poem

🐇✨

In the realm of code, where tests intertwine,
A rabbit hopped forth, making TestNG refine.
No more clones, no duplicates in sight,
Just clean, green tests, running right.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.

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