Skip to content

[Flaky test fix] - Issue 2579#3269

Merged
krmahadevan merged 3 commits into
testng-team:masterfrom
krmahadevan:bugfix/fix_2579
Apr 26, 2026
Merged

[Flaky test fix] - Issue 2579#3269
krmahadevan merged 3 commits into
testng-team:masterfrom
krmahadevan:bugfix/fix_2579

Conversation

@krmahadevan

@krmahadevan krmahadevan commented Apr 25, 2026

Copy link
Copy Markdown
Member

Closes #2579

What the new test measures

Instead of checking an arbitrary timestamp gap, each test method records when it is actively executing.

time ---->

active count
6 |                 ***************
5 |              ***               ***
4 |           ***                     ***
3 |        ***                           ***
2 |     ***                               ***
1 |  ***                                   ***
0 +------------------------------------------------
  m1  m2  m3  m4  m5  m6 start running

New assertion:
startedTestMethods == 6
maxActiveTestMethods == 6

Meaning:
all 6 methods started, and at one point all 6 were observed executing concurrently.

Behavioral difference

Old:
"Did every method start within 40 ms of the previous one?"

New:
"Did all six methods actually run at the same time?"

The new check matches the feature under test: suite-level parallel="methods" should execute methods concurrently. It removes the host-specific 40 ms timing assumption while still failing if execution becomes sequential.

Fixes # .

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

  • Tests
    • Replaced fragile timestamp-based tracking with reliable concurrency counters to measure parallel test execution.
    • Improved test setup/reset and made metrics collection deterministic for parallel runs.
    • Simplified assertions to directly validate expected counts of started and concurrently active test methods, strengthening parallel-execution verification.

@krmahadevan krmahadevan requested a review from juherr as a code owner April 25, 2026 10:23
@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9044ec0f-1dfb-4595-8bce-d73e434ac782

📥 Commits

Reviewing files that changed from the base of the PR and between d8d108c and 3ef61f3.

📒 Files selected for processing (2)
  • testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java
  • testng-core/src/test/java/test/thread/issue188/IssueTest.java

📝 Walkthrough

Walkthrough

Replaces flaky timestamp-based concurrency detection in the issue188 tests with atomic counters that track started test methods and peak concurrent active methods; tests are reset before execution and assert expected invocation counts and peak concurrency.

Changes

Cohort / File(s) Summary
Parallel execution test
testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java, testng-core/src/test/java/test/thread/issue188/IssueTest.java
Removed timestamp/Reporter-based timing and collections. Added EXPECTED_METHOD_INVOCATIONS, reset(), getters, and AtomicInteger counters (startedTestMethods, activeTestMethods, maxActiveTestMethods). Replaced @BeforeMethod logTime(ITestResult) with logStart() and added trackParallelExecution() to reliably measure and record peak concurrency; test asserts started count and max concurrency instead of timing deltas.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • juherr

Poem

🐰
I hopped through tests with careful paws,
Counting starts and peaks — no more flawed laws.
Atomic hops kept steady, sleep ensured the show,
Now flaky timings stop — let the green carrots grow! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly references Issue 2579 and describes a flaky test fix. It directly relates to the main change: replacing timing-based assertions with concurrency-based checks in the flaky test.
Linked Issues check ✅ Passed The code changes fully address the linked issue #2579: replacing the brittle 40ms timing assertion with concurrency-based checks (startedTestMethods == 6 and maxActiveTestMethods == 6) that verify parallel execution.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the flaky test. Issue188TestSample and IssueTest modifications exclusively implement the concurrency-tracking solution without introducing unrelated changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java (1)

9-9: Optional: document the coupling of EXPECTED_METHOD_INVOCATIONS to the suite layout.

The constant 6 is implicitly derived from IssueTest's setup (3 createXmlTest calls × 2 @Test methods). If a future change adds/removes an XmlTest or @Test method on either side without updating the other, the assertions silently drift. Consider either adding a brief comment here describing the derivation, or moving the constant to IssueTest and computing it inline (e.g., xmlTestCount * methodsPerClass) so the relationship is explicit at the call site.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java` at
line 9, The constant EXPECTED_METHOD_INVOCATIONS in Issue188TestSample is
tightly coupled to IssueTest's suite layout (3 XmlTest × 2 `@Test` methods);
update this by either adding a brief explanatory comment next to
EXPECTED_METHOD_INVOCATIONS describing its derivation ("3 XmlTest * 2 tests =
6") or, preferably, move the constant into IssueTest and compute it dynamically
(e.g., derive xmlTestCount and methodsPerClass and set
EXPECTED_METHOD_INVOCATIONS = xmlTestCount * methodsPerClass) so the
relationship is explicit and maintained at the source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java`:
- Line 9: The constant EXPECTED_METHOD_INVOCATIONS in Issue188TestSample is
tightly coupled to IssueTest's suite layout (3 XmlTest × 2 `@Test` methods);
update this by either adding a brief explanatory comment next to
EXPECTED_METHOD_INVOCATIONS describing its derivation ("3 XmlTest * 2 tests =
6") or, preferably, move the constant into IssueTest and compute it dynamically
(e.g., derive xmlTestCount and methodsPerClass and set
EXPECTED_METHOD_INVOCATIONS = xmlTestCount * methodsPerClass) so the
relationship is explicit and maintained at the source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 447b2ef6-5317-41bb-9d7d-6d9209eb54bb

📥 Commits

Reviewing files that changed from the base of the PR and between 88c32ff and 9d78c8b.

📒 Files selected for processing (2)
  • testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java
  • testng-core/src/test/java/test/thread/issue188/IssueTest.java

}

private void sleepSilently() {
private static void trackParallelExecution() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it thread safe?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We are trying to ascertain that at some point all the concurrently spawned test methods did overlap each other since its never going to be possible to truly ascertain if TestNG did start all the tests in parallel. Timestamped based guess work is also still only guestimate because we would need to define a tolerance value. And that tolerance value is what has been causing flakiness in CI.

Comment thread testng-core/src/test/java/test/thread/issue188/IssueTest.java Outdated

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java (2)

43-53: Sleep duration is now the implicit timing knob — worth a brief comment.

Reaching maxActiveTestMethods == 6 still relies on all six methods entering the sleep window before the first one exits. 1 s is far more forgiving than 40 ms and should be robust on CI, but a one-line comment explaining why the sleep exists (to keep methods concurrently active long enough to observe peak parallelism) would help future maintainers avoid “optimizing” it away.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java`
around lines 43 - 53, Add a one-line comment in the trackParallelExecution
method explaining that the TimeUnit.SECONDS.sleep(1) call is intentionally long
to keep multiple test methods concurrently active so maxActiveTestMethods can
reliably observe peak parallelism (e.g., to ensure all six methods enter the
sleep window before any exit), and reference the tracking variables
activeTestMethods and maxActiveTestMethods so future maintainers don’t remove or
shorten the sleep.

9-9: Consider documenting the coupling of EXPECTED_METHOD_INVOCATIONS to the suite layout.

The value 6 is implicitly derived from IssueTest.testSuiteLevelParallelMode registering 3 XmlTests × 2 @Test methods on this class. If the suite layout or the number of @Test methods here changes, the assertion semantics in IssueTest will silently shift (e.g., still pass with a smaller-than-expected concurrency). A short Javadoc comment pinning the derivation, or computing it as numXmlTests * <test-method-count> in IssueTest, would prevent that drift.

📝 Suggested doc-only tweak
-  public static final int EXPECTED_METHOD_INVOCATIONS = 6;
+  // 3 XmlTests in IssueTest#testSuiteLevelParallelMode x 2 `@Test` methods in this class.
+  public static final int EXPECTED_METHOD_INVOCATIONS = 6;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java` at
line 9, EXPECTED_METHOD_INVOCATIONS is tightly coupled to the suite layout
(derived from IssueTest.testSuiteLevelParallelMode registering 3 XmlTests × 2
`@Test` methods) and can silently drift; either add a short Javadoc on the
EXPECTED_METHOD_INVOCATIONS constant in Issue188TestSample explaining the
derivation (e.g., "3 XmlTests × 2 test methods in this class = 6"), or update
IssueTest.testSuiteLevelParallelMode to compute the expected count dynamically
(e.g., multiply the number of registered XmlTest instances by the number of
`@Test` methods on Issue188TestSample) so the assertion remains correct when
tests/layout change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java`:
- Around line 43-53: Add a one-line comment in the trackParallelExecution method
explaining that the TimeUnit.SECONDS.sleep(1) call is intentionally long to keep
multiple test methods concurrently active so maxActiveTestMethods can reliably
observe peak parallelism (e.g., to ensure all six methods enter the sleep window
before any exit), and reference the tracking variables activeTestMethods and
maxActiveTestMethods so future maintainers don’t remove or shorten the sleep.
- Line 9: EXPECTED_METHOD_INVOCATIONS is tightly coupled to the suite layout
(derived from IssueTest.testSuiteLevelParallelMode registering 3 XmlTests × 2
`@Test` methods) and can silently drift; either add a short Javadoc on the
EXPECTED_METHOD_INVOCATIONS constant in Issue188TestSample explaining the
derivation (e.g., "3 XmlTests × 2 test methods in this class = 6"), or update
IssueTest.testSuiteLevelParallelMode to compute the expected count dynamically
(e.g., multiply the number of registered XmlTest instances by the number of
`@Test` methods on Issue188TestSample) so the assertion remains correct when
tests/layout change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79bdd882-03c7-4558-a433-8bccc3cfea1f

📥 Commits

Reviewing files that changed from the base of the PR and between 9d78c8b and d8d108c.

📒 Files selected for processing (2)
  • testng-core/src/test/java/test/thread/issue188/Issue188TestSample.java
  • testng-core/src/test/java/test/thread/issue188/IssueTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • testng-core/src/test/java/test/thread/issue188/IssueTest.java

@krmahadevan krmahadevan requested a review from juherr April 26, 2026 04:36

@juherr juherr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

All tests are failing

Closes testng-team#2579

What the new test measures

Instead of checking an arbitrary timestamp gap, each test method records when
it is actively executing.

```
time ---->

active count
6 |                 ***************
5 |              ***               ***
4 |           ***                     ***
3 |        ***                           ***
2 |     ***                               ***
1 |  ***                                   ***
0 +------------------------------------------------
  m1  m2  m3  m4  m5  m6 start running
```

New assertion:
startedTestMethods == 6
maxActiveTestMethods == 6

Meaning:
all 6 methods started, and at one point all 6 were observed executing concurrently.

Behavioral difference

Old:
"Did every method start within 40 ms of the previous one?"

New:
"Did all six methods actually run at the same time?"

The new check matches the feature under test: suite-level
parallel="methods" should execute methods concurrently. It removes the
host-specific 40 ms timing assumption while still failing if execution becomes
sequential.
@krmahadevan

Copy link
Copy Markdown
Member Author

@juherr - That was due to formatting problems. Now its fixed.

@krmahadevan krmahadevan merged commit fa22dbe into testng-team:master Apr 26, 2026
9 of 10 checks passed
@krmahadevan krmahadevan deleted the bugfix/fix_2579 branch April 26, 2026 14:06
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.

Flaky test: test.thread.issue188.IssueTest.testSuiteLevelParallelMode

2 participants