Skip to content
New issue

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

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

Already on GitHub? # to your account

Allow listeners to be disabled at runtime #3088

Merged
merged 1 commit into from
Mar 14, 2024

Conversation

krmahadevan
Copy link
Member

@krmahadevan krmahadevan commented Mar 9, 2024

Closes #2381

Fixes #2381 .

Did you remember to?

  • Add test case(s)
  • Update CHANGES.txt
  • Auto applied styling via ./gradlew autostyleApply

We encourage pull requests that:

  • Add new features to TestNG (or)
  • Fix bugs in TestNG

If your pull request involves fixing SonarQube issues then we would suggest that you please discuss this with the
TestNG-dev before you spend time working on it.

Note: For more information on contribution guidelines please make sure you refer our Contributing section for detailed set of steps.

Summary by CodeRabbit

  • New Features
    • Introduced runtime control for listener inclusion to enhance flexibility.
    • Added documentation for the project's PGP artifact signing keys.
  • Refactor
    • Updated to use ReentrantLock for better concurrency control.
    • Enhanced code readability and conciseness by using streams and lambda expressions for listener invocation.
  • Bug Fixes
    • Aligned TestNG 7.x DataProvider behavior with TestNG 6.x standards.
  • Tests
    • Added tests to ensure listeners can be disabled as expected.
  • Documentation
    • Documented changes and new features for better user understanding.

@krmahadevan krmahadevan requested a review from juherr as a code owner March 9, 2024 17:51
Copy link

coderabbitai bot commented Mar 9, 2024

Walkthrough

The update introduces runtime control for listener inclusion by adding an isEnabled() method, ensuring only enabled listeners are processed. Test classes now have unique ids instantiated by TestNG. New test methods and classes validate disabling listeners at runtime.

Changes

File(s) Change Summary
CHANGES.txt, .../ITestNGListener.java, .../TestNG.java Added runtime control for listener inclusion, isEnabled() method, and listener enablement checks. Refactored for improved processing of enabled listeners.
.../JDK15AnnotationFinder.java, .../FactoryTestClassSample.java Implemented conditional checks for listener enablement before annotation transformation. Introduced a test class with factory methods for TestNG testing.
.../ListenersTest.java, .../SampleGlobalListener.java Added a test method to ensure listeners can be disabled and implemented a sample global listener for event logging during test execution.
.../SampleTransformer.java, .../TestClassSample.java Introduced a class for transforming TestNG annotations based on listener enablement. Contains a test class with annotated test methods and setup.

Assessment against linked issues

Objective Addressed Explanation
Enable runtime control of listener inclusion [#2381]
Allow defining order of listener invocation [#2874] The changes do not address defining the order of listener invocation explicitly.
Utilize IInvokedMethodListener for correct instance retrieval during parallel testing [#3082] The changes do not directly resolve the instance retrieval issue during parallel testing as per the linked issue.

Poem

In the realm of code where changes take flight,
TestNG listens, with runtime control so bright.
With each method and listener in sight,
The rabbit dances through the code's delight.
🐇 "Listeners enabled, or disabled with care,
TestNG's world now more aware."

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.
    • Open a follow-up GitHub issue for this discussion.
  • 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 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.

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: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 438674c and c0175cc.
Files selected for processing (14)
  • CHANGES.txt (1 hunks)
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java (1 hunks)
  • testng-core/src/main/java/org/testng/SuiteRunner.java (1 hunks)
  • testng-core/src/main/java/org/testng/TestNG.java (2 hunks)
  • testng-core/src/main/java/org/testng/TestRunner.java (3 hunks)
  • testng-core/src/main/java/org/testng/internal/DynamicGraph.java (2 hunks)
  • testng-core/src/main/java/org/testng/internal/Parameters.java (5 hunks)
  • testng-core/src/main/java/org/testng/internal/TestListenerHelper.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/InvokedMethodListenerInvoker.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (3 hunks)
  • testng-core/src/test/java/test/listeners/ListenersTest.java (2 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java (1 hunks)
Files not reviewed due to errors (1)
  • (no review received)
Files skipped from review due to trivial changes (1)
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java
Additional comments: 22
testng-core-api/src/main/java/org/testng/ITestNGListener.java (1)
  • 8-13: The addition of the isEnabled() method with a default implementation returning true is a thoughtful approach to introduce dynamic enabling/disabling of listeners without breaking existing implementations. This change enhances flexibility and control for TestNG users.
testng-core/src/main/java/org/testng/internal/invokers/InvokedMethodListenerInvoker.java (1)
  • 54-56: The implementation of the enabled check in the invokeListener method correctly ensures that only enabled listeners are invoked. This is a key part of the new feature allowing dynamic control over listener execution.
testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java (1)
  • 21-136: The SampleGlobalListener class effectively demonstrates the new feature by implementing various listener interfaces and overriding the isEnabled() method to return false. This serves as a good test case for validating the dynamic disabling of listeners at runtime.
testng-core/src/main/java/org/testng/internal/TestListenerHelper.java (1)
  • 102-104: The implementation of the enabled check in the runTestListeners method ensures that test listeners are only invoked if they are enabled. This aligns with the PR's objective and enhances the flexibility of listener execution.
testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java (1)
  • 171-173: The refactoring to use Java streams for filtering enabled listeners and iterating over them in the invocation of before and after class methods enhances code readability and conciseness. This change aligns with modern Java practices and supports the new feature's functionality.

Also applies to: 237-239

testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java (1)
  • 169-172: The modifications to include conditional checks for the transformer's enabled status before invoking the transform method for different types of annotations ensure that transformations occur only when appropriate. This enhancement aligns with the PR's objective and the dynamic control introduced by the isEnabled() method.

Also applies to: 174-176, 180-182, 184-186, 188-190, 192-194

testng-core/src/main/java/org/testng/internal/DynamicGraph.java (2)
  • 12-12: The addition of the org.testng.ITestNGListener import is necessary for the new functionality introduced in the setStatus method. This change is consistent with the PR's objectives.
  • 146-148: The modifications to filter and invoke visualisers based on their enabled status align with the PR's goal of allowing listeners to be disabled at runtime. This implementation uses modern Java practices and is efficient. However, ensure that all visualisers indeed implement ITestNGListener to avoid potential ClassCastException.
testng-core/src/main/java/org/testng/SuiteRunner.java (1)
  • 240-243: The modifications to use streams for filtering and invoking listeners based on their enabled status are well-implemented and align with the PR's objectives. This approach enhances code readability and conciseness. Ensure that the configuration.getListenerComparator() used for ordering listeners provides a stable and expected order for listener invocation, especially when listeners have dependencies on each other's execution order.

Also applies to: 245-249

testng-core/src/test/java/test/listeners/ListenersTest.java (1)
  • 525-532: The new test method ensureListenersCanBeDisabled correctly implements the functionality to test the dynamic disabling of listeners at runtime. It's crucial to ensure that the SampleGlobalListener correctly overrides the isEnabled() method to return false when it should be disabled. Additionally, the effectiveness of this test relies on the correct implementation of the isEnabled() check within the TestNG core.

Ensure that the SampleGlobalListener correctly implements the isEnabled() method and that its integration within the TestNG core is functioning as expected.

testng-core/src/main/java/org/testng/internal/Parameters.java (4)
  • 802-807: The implementation of filtering listeners based on their enabled status before data provider execution is a good practice. It ensures that only the necessary listeners are invoked, which can improve performance and maintainability. However, it's important to ensure that the isEnabled() method's default implementation returns true to maintain backward compatibility with existing listeners that do not override this method.
  • 822-824: Similar to the previous comment, filtering listeners based on their enabled status in the event of a data provider failure is a good practice. This approach ensures that only relevant listeners are notified about the failure, which can help in debugging and error handling. Again, ensure that the isEnabled() method's default implementation returns true for backward compatibility.
  • 845-850: The use of the isEnabled() method to filter listeners after data provider execution is consistent with the previous implementations. This consistency in handling listener invocation based on their enabled status across different stages of data provider execution is commendable. It enhances the flexibility and control over listener execution, aligning with the PR's objectives.
  • 862-866: Filtering IDataProviderInterceptor instances based on their enabled status before intercepting the parameters is a thoughtful addition. It allows for more granular control over which interceptors are applied, potentially reducing overhead and improving execution efficiency. As with the listeners, ensure that any default or existing interceptors are considered enabled unless explicitly overridden.
testng-core/src/main/java/org/testng/TestRunner.java (4)
  • 748-750: The addition of the isEnabled() check before applying the IMethodInterceptor is in line with the PR's objectives and correctly implemented. Please ensure that the default implementation of isEnabled() returns true to maintain backward compatibility with existing listeners that do not override this method.
  • 858-861: The addition of the isEnabled() check before invoking ITestListener.onStart() is correctly implemented and aligns with the PR's objectives. Ensure the default implementation of isEnabled() returns true for backward compatibility.
  • 865-869: The addition of the isEnabled() check before invoking ITestListener.onFinish() is correctly implemented. As with the onStart() method, please ensure the default implementation of isEnabled() returns true.
  • 1106-1108: The addition of the isEnabled() check before invoking IExecutionListener.onExecutionStart() is correctly implemented. Ensure the default implementation of isEnabled() returns true to maintain backward compatibility with existing execution listeners that do not override this method.
testng-core/src/main/java/org/testng/TestNG.java (4)
  • 1115-1115: The use of .stream().filter(ITestNGListener::isEnabled) to filter enabled listeners before invoking alter on them is a good application of Java 8 streams and lambda expressions. This ensures that only enabled listeners are processed, enhancing performance and maintainability.
  • 1123-1125: The refactoring to use streams and lambda expressions in runExecutionListeners for the start phase is consistent with modern Java practices. Filtering enabled listeners before invoking onExecutionStart is efficient and clean.
  • 1132-1134: Similarly, the use of streams and lambda expressions in the finish phase of runExecutionListeners maintains consistency and ensures that only enabled listeners are notified of the execution finish. This approach is both efficient and readable.
  • 1153-1155: In generateReports, filtering out disabled reporters before generating reports is a prudent choice. This prevents unnecessary processing and potential errors from disabled reporters. The use of streams here aligns with the overall approach of leveraging modern Java features for clarity and efficiency.

Copy link
Member

@juherr juherr left a comment

Choose a reason for hiding this comment

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

I don't get why we need to filter on enabled listeners when we can not add them in the context.

@krmahadevan
Copy link
Member Author

I don't get why we need to filter on enabled listeners when we can not add them in the context.

By providing this capability, we now allow users to enable/disable user level listeners at runtime. This provides a much more easier way of doing it, instead of adding if conditions around the methods as wrappers that provide for conditional execution.

You can imagine this to be something like a cheap version of @Conditional annotation of spring

@juherr
Copy link
Member

juherr commented Mar 14, 2024

@Conditional is a good comparison, and as far I know, it is only evaluated at the bootstrap phase and not every time at runtime.

@krmahadevan
Copy link
Member Author

@Conditional is a good comparison, and as far I know, it is only evaluated at the bootstrap phase and not every time at runtime.

I was only drawing parity in terms of the functional equivalency and not necessarily hinting at the implementation aspect.

For e.g., if one would like to have a listener conditionally executed (specific OS, or IDE vs CI or any other condition), I feel that this capability will be a lot more simpler instead of adding if conditions to all the method implementations which makes the code verbose.

@juherr
Copy link
Member

juherr commented Mar 14, 2024

OK. Let me time to think about it.

@krmahadevan
Copy link
Member Author

OK. Let me time to think about it.

@juherr - Please check this now. I have refactored the code wherein the check is being used to determine if the listener is to be wired in or not (in previous implementation, it was used to determine if listener is to be called or not) and thus aligning a bit more closer in terms of being the control flag at bootstrap time.

@krmahadevan krmahadevan requested a review from juherr March 14, 2024 09:42
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: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 19cd580 and bf7c195.
Files ignored due to path filters (1)
  • testng-core/src/test/resources/2381.xml is excluded by: !**/*.xml
Files selected for processing (9)
  • CHANGES.txt (1 hunks)
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java (1 hunks)
  • testng-core/src/main/java/org/testng/TestNG.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java (1 hunks)
  • testng-core/src/test/java/test/listeners/ListenersTest.java (2 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java (1 hunks)
Files skipped from review as they are similar to previous changes (7)
  • CHANGES.txt
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java
  • testng-core/src/main/java/org/testng/TestNG.java
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
  • testng-core/src/test/java/test/listeners/ListenersTest.java
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java
Additional comments: 5
testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java (3)
  • 11-12: The usage of the @Factory annotation with a data provider is correctly implemented. The parameter name ignored is acceptable in this context, as it indicates the parameter is necessary for the method signature but not used within the method body.
  • 14-17: The @DataProvider method dp is correctly implemented, returning the expected data format for TestNG. This setup is suitable for demonstration or testing purposes.
  • 19-26: The usage of lifecycle annotations @BeforeSuite, @Test, and @AfterSuite is correctly implemented. The methods are empty, which is acceptable in this context, as the focus is likely on the structure rather than the implementation.
testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java (2)
  • 27-30: The implementation of the isEnabled method, returning false, aligns with the PR's objectives of testing the dynamic disabling of listeners. This approach is crucial for verifying the new functionality.
  • 32-60: The transformation methods (transform) are consistently implemented with a logging mechanism that effectively aids in verifying the transformation process. This approach is practical and aligns with the objectives of providing insights into the transformation actions.

Copy link
Member

@juherr juherr left a comment

Choose a reason for hiding this comment

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

Thanks for the change. That is a good first step. We will be able to see for improvements later.

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: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 23bb95f and 374a6c7.
Files ignored due to path filters (1)
  • testng-core/src/test/resources/2381.xml is excluded by: !**/*.xml
Files selected for processing (9)
  • CHANGES.txt (1 hunks)
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java (1 hunks)
  • testng-core/src/main/java/org/testng/TestNG.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java (1 hunks)
  • testng-core/src/test/java/test/listeners/ListenersTest.java (2 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java (1 hunks)
Files skipped from review as they are similar to previous changes (9)
  • CHANGES.txt
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java
  • testng-core/src/main/java/org/testng/TestNG.java
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
  • testng-core/src/test/java/test/listeners/ListenersTest.java
  • testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java
  • testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java

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: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 23bb95f and a781326.
Files ignored due to path filters (1)
  • testng-core/src/test/resources/2381.xml is excluded by: !**/*.xml
Files selected for processing (9)
  • CHANGES.txt (1 hunks)
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java (1 hunks)
  • testng-core/src/main/java/org/testng/TestNG.java (1 hunks)
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java (1 hunks)
  • testng-core/src/test/java/test/listeners/ListenersTest.java (2 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java (1 hunks)
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java (1 hunks)
Files skipped from review as they are similar to previous changes (9)
  • CHANGES.txt
  • testng-core-api/src/main/java/org/testng/ITestNGListener.java
  • testng-core/src/main/java/org/testng/TestNG.java
  • testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
  • testng-core/src/test/java/test/listeners/ListenersTest.java
  • testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java
  • testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java
  • testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java
  • testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java

@krmahadevan krmahadevan merged commit 93ec005 into testng-team:master Mar 14, 2024
6 of 9 checks passed
@krmahadevan krmahadevan deleted the fix_2381 branch March 14, 2024 11:43
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FR] Controlling the inclusion of the listener at runtime
2 participants