Skip to content

Conversation

@lkalina-cera
Copy link

@lkalina-cera lkalina-cera commented Nov 21, 2025

Fixes #3243 .

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

  • Bug Fixes
    • Enhanced error handling during worker task execution with improved exception logging
    • Refined task status management to properly distinguish between successful and failed execution paths, improving reliability of subsequent task scheduling and shutdown operations

✏️ Tip: You can customize this high-level summary in your review settings.

the exception coming from the TestNGFutureTask.
@coderabbitai
Copy link

coderabbitai bot commented Nov 21, 2025

Walkthrough

The GraphOrchestrator class has been modified to differentiate error handling in the afterExecute method. When a worker task completes with an exception, the error is now logged and worker status updates are skipped. Successful task completions retain their previous behavior, altering the control flow between success and failure paths.

Changes

Cohort / File(s) Change Summary
Exception handling in task completion
testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java
Added conditional logic to log exceptions and skip worker status computation on task failure; preserves original status update behavior for successful completions

Sequence Diagram(s)

sequenceDiagram
    participant Worker
    participant GraphOrchestrator
    participant Logger
    participant NodeScheduler

    Worker->>GraphOrchestrator: afterExecute (task completes)
    
    alt Task completed with exception
        GraphOrchestrator->>Logger: Log error
        Logger-->>GraphOrchestrator: Error recorded
        Note over GraphOrchestrator: Skip status update
        GraphOrchestrator->>NodeScheduler: No status applied
    else Task completed successfully
        GraphOrchestrator->>GraphOrchestrator: Compute worker status
        Note over GraphOrchestrator: Apply status update
        GraphOrchestrator->>NodeScheduler: Schedule next nodes
    end
    
    GraphOrchestrator-->>Worker: Handler complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Focus areas:
    • Verify exception logging captures sufficient diagnostic information for debugging
    • Confirm that skipping status updates during exceptions doesn't cause orphaned nodes or deadlocks
    • Validate that the failure path properly triggers shutdown logic or error recovery mechanisms
    • Test scenarios where exceptions occur to ensure graceful degradation

Poem

🐰 A worker hops, then stumbles on the way,
Now logs its fall with care, no status to display,
When success arrives, the path proceeds with grace,
Scheduling the next nodes to find their place! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing exception handling in GraphOrchestrator#afterExecute to properly display exceptions from TestNGFutureTask, which aligns with the code changes shown in the summary.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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 and usage tips.

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java (1)

79-79: Consider using a fallback message when t.getMessage() returns null.

The getMessage() method can return null for some Throwable instances. While SLF4J handles null messages gracefully, providing a fallback improves log clarity.

Apply this diff:

-      Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
+      String message = t.getMessage() != null ? t.getMessage() : "Worker execution failed";
+      Logger.getLogger(GraphOrchestrator.class).error(message, t);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d50b2ad and a9c500e.

📒 Files selected for processing (1)
  • testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java (1)
testng-core-api/src/main/java/org/testng/log4testng/Logger.java (1)
  • Logger (13-192)

Comment on lines +78 to +82
if (t != null){
Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
} else {
setStatus(r, computeStatus(r));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Critical: Worker status not updated when exception occurs, potentially causing orchestrator to hang.

When an exception is thrown (t != null), the worker's status is never updated from RUNNING. This creates a critical issue:

  1. The shutdown check on line 83 counts only nodes with FINISHED status
  2. If a node fails and remains in RUNNING status, the condition graph.getNodeCount() == graph.getNodeCountWithStatus(IDynamicGraph.Status.FINISHED) may never be satisfied
  3. This prevents service.shutdown() from being called, potentially causing the orchestrator to hang indefinitely

The logging addition successfully addresses the PR objective to show exceptions from TestNGFutureTask. However, the status must still be updated even when exceptions occur.

Apply this diff to update the status even when an exception occurs:

-    if (t != null){
-      Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
-    } else {
-      setStatus(r, computeStatus(r));
-    }
+    if (t != null) {
+      Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
+    }
+    setStatus(r, computeStatus(r));

Alternatively, if IDynamicGraph.Status has a FAILED status, you could set nodes to FAILED on exception and update the shutdown logic to account for both FINISHED and FAILED nodes. Please verify the intended behavior when a worker throws an exception:


🏁 Script executed:

#!/bin/bash
# Description: Check if IDynamicGraph.Status has a FAILED status and how shutdown should behave

# Search for Status enum definition
ast-grep --pattern 'enum Status {
  $$$
}'

# Search for how getNodeCountWithStatus is used elsewhere
rg -n -C3 'getNodeCountWithStatus'

# Search for other exception handling patterns in the codebase
rg -n -C3 'afterExecute.*Throwable'

Length of output: 4415



🏁 Script executed:

#!/bin/bash
# Check computeStatus implementation and setStatus behavior

# Find computeStatus method
ast-grep --pattern 'private $_ computeStatus($_) {
  $$$
}'

# Find setStatus method
rg -n -C5 'private void setStatus'

# Check if there's any other shutdown mechanism or exception recovery
rg -n 'service.shutdown' 

# Check TestNGFutureTask to understand exception handling context
rg -n 'class TestNGFutureTask' -A 20

Length of output: 4811


🏁 Script executed:

#!/bin/bash
# Verify computeStatus behavior and status update mechanisms

# Find computeStatus implementation
ast-grep --pattern 'private $_ computeStatus($_) {
  $$$
}'

# Find setStatus implementation  
rg -n 'private void setStatus' -A 5

# Check if there's any other place that marks nodes as FINISHED besides afterExecute
rg -n 'setStatus.*FINISHED'

# Check the complete shutdown logic
rg -n 'service.shutdown' -B 5 -A 2

# Look for exception handling or finally blocks in afterExecute context
rg -n 'afterExecute' -A 20

Length of output: 12096


Critical: Worker status not updated when exception occurs, preventing orchestrator shutdown and causing potential hang.

When an exception is thrown (t != null), the setStatus() method is never called. This creates a critical reliability issue:

  1. The worker's tasks remain in RUNNING status (never transitioned to FINISHED or READY)
  2. The shutdown check on line 83 requires all nodes to have FINISHED status
  3. If any node fails with an exception and stays in RUNNING, the condition is never satisfied
  4. The service.shutdown() is never invoked, leaving the thread pool active and potentially causing the orchestrator to hang indefinitely

The logging addition successfully achieves the PR objective to expose exceptions from TestNGFutureTask. However, status management must be updated regardless of exception state to maintain the shutdown contract.

Fix: Move setStatus() outside the conditional so it executes for both exception and success paths:

-    if (t != null){
-      Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
-    } else {
-      setStatus(r, computeStatus(r));
-    }
+    if (t != null) {
+      Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
+    }
+    setStatus(r, computeStatus(r));

This ensures all nodes transition to FINISHED (or READY for thread affinity) even when exceptions occur, allowing the shutdown condition to be met.

📝 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. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (t != null){
Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
} else {
setStatus(r, computeStatus(r));
}
if (t != null) {
Logger.getLogger(GraphOrchestrator.class).error(t.getMessage(), t);
}
setStatus(r, computeStatus(r));
🤖 Prompt for AI Agents
In
testng-core/src/main/java/org/testng/internal/thread/graph/GraphOrchestrator.java
around lines 78 to 82, the code logs the exception but fails to update the
worker node status when t != null, leaving nodes in RUNNING and preventing
shutdown; move the call to setStatus(r, computeStatus(r)) out of the if/else so
it executes regardless of whether an exception occurred (keep the
Logger.error(t.getMessage(), t) in the t != null branch), ensuring nodes
transition to FINISHED (or READY for thread affinity) even on exceptions so the
shutdown condition can be met.

@lkalina-cera
Copy link
Author

Hi @krmahadevan can you look into this PR? What should I do to merge this?

@krmahadevan
Copy link
Member

@lkalina-cera - Can you please run ./gradlew autostyleApply to format the code and then update this PR?

Also it would be good if we could please include a test that demonstrates that the fix works fine.

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.

The GraphOrchestrator afterExecute method should handle null IWorker<T> r parameter

2 participants