-
Notifications
You must be signed in to change notification settings - Fork 1k
Flaky Test Fix - 2587 #3268
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
Flaky Test Fix - 2587 #3268
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,25 +2,24 @@ | |||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.ConcurrentHashMap; | ||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.ConcurrentMap; | ||||||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||||||||||||||||||||||||||||||||||||||||
| import org.testng.IInvokedMethod; | ||||||||||||||||||||||||||||||||||||||||||
| import org.testng.IInvokedMethodListener; | ||||||||||||||||||||||||||||||||||||||||||
| import org.testng.ITestResult; | ||||||||||||||||||||||||||||||||||||||||||
| import org.testng.collections.Lists; | ||||||||||||||||||||||||||||||||||||||||||
| import org.testng.collections.Maps; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public class LocalTrackingListener implements IInvokedMethodListener { | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private final Map<String, List<Statistics>> results = Maps.newConcurrentMap(); | ||||||||||||||||||||||||||||||||||||||||||
| private final Map<String, Long> threadIds = Maps.newConcurrentMap(); | ||||||||||||||||||||||||||||||||||||||||||
| private final ConcurrentMap<String, List<Statistics>> results = new ConcurrentHashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||
| private final ConcurrentMap<String, Long> threadIds = new ConcurrentHashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||
| public void afterInvocation(IInvokedMethod method, ITestResult testResult) { | ||||||||||||||||||||||||||||||||||||||||||
| String key = testResult.getInstance().toString(); | ||||||||||||||||||||||||||||||||||||||||||
| results.computeIfAbsent(key, k -> Lists.newArrayList()).add(new Statistics(testResult)); | ||||||||||||||||||||||||||||||||||||||||||
| if (!threadIds.containsKey(key)) { | ||||||||||||||||||||||||||||||||||||||||||
| Long threadId = Long.parseLong(testResult.getAttribute(SampleTestClass.THREAD_ID).toString()); | ||||||||||||||||||||||||||||||||||||||||||
| threadIds.put(key, threadId); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| results.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(new Statistics(testResult)); | ||||||||||||||||||||||||||||||||||||||||||
| Long threadId = asLong(testResult); | ||||||||||||||||||||||||||||||||||||||||||
| threadIds.putIfAbsent(key, threadId); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
18
to
23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm SampleTestClass currently has no configuration methods, so the NPE is latent rather than active.
fd -t f 'SampleTestClass.java' | xargs rg -nP '@(Before|After)(Method|Class|Suite|Test|Groups)\b'Repository: testng-team/testng Length of output: 458 🏁 Script executed: # List files in issue326 directory to understand the test structure
fd -t f . testng-core/src/test/java/test/factory/issue326/Repository: testng-team/testng Length of output: 269 🏁 Script executed: # Search for SampleTestClass specifically in issue326 directory
fd -t f 'SampleTestClass.java' testng-core/src/test/java/test/factory/issue326/Repository: testng-team/testng Length of output: 131 🏁 Script executed: # Check the full LocalTrackingListener.java file to see the asLong implementation
cat -n testng-core/src/test/java/test/factory/issue326/LocalTrackingListener.javaRepository: testng-team/testng Length of output: 2261 🏁 Script executed: cat -n testng-core/src/test/java/test/factory/issue326/SampleTestClass.javaRepository: testng-team/testng Length of output: 1763 🏁 Script executed: # Check IInvokedMethod to see if isTestMethod() method exists
rg -t java 'isTestMethod' --type-listRepository: testng-team/testng Length of output: 452 🏁 Script executed: # More specific search for IInvokedMethod interface
rg -A 10 'interface IInvokedMethod' testng-core/src/main/java/Repository: testng-team/testng Length of output: 44 🏁 Script executed: # Search for IInvokedMethod interface definition
fd -t f 'IInvokedMethod.java'Repository: testng-team/testng Length of output: 123 🏁 Script executed: # Check if isTestMethod exists in the codebase
rg -t java 'isTestMethod'Repository: testng-team/testng Length of output: 4353 🏁 Script executed: # View the IInvokedMethod interface to understand isTestMethod
cat -n testng-core-api/src/main/java/org/testng/IInvokedMethod.javaRepository: testng-team/testng Length of output: 771 Latent NPE risk in
Guard against this by returning early for non-test methods: Proposed fix `@Override`
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
+ if (!method.isTestMethod()) {
+ return;
+ }
String key = testResult.getInstance().toString();
results.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(new Statistics(testResult));
Long threadId = asLong(testResult);
threadIds.putIfAbsent(key, threadId);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this is a valid concern in the general context, its not relevant here because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
(╯°□°)╯ ✏️ Learnings added
🧠 Learnings used |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public Map<String, List<Statistics>> getResults() { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -31,17 +30,29 @@ public Map<String, Long> getThreadIds() { | |||||||||||||||||||||||||||||||||||||||||
| return threadIds; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| static class Statistics { | ||||||||||||||||||||||||||||||||||||||||||
| public static class Statistics { | ||||||||||||||||||||||||||||||||||||||||||
| String methodName; | ||||||||||||||||||||||||||||||||||||||||||
| long startTimeInMs; | ||||||||||||||||||||||||||||||||||||||||||
| long endTimeInMs; | ||||||||||||||||||||||||||||||||||||||||||
| long threadId; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public Statistics(ITestResult testResult) { | ||||||||||||||||||||||||||||||||||||||||||
| this(testResult.getMethod().getMethodName(), testResult.getStartMillis()); | ||||||||||||||||||||||||||||||||||||||||||
| this( | ||||||||||||||||||||||||||||||||||||||||||
| testResult.getMethod().getMethodName(), | ||||||||||||||||||||||||||||||||||||||||||
| testResult.getStartMillis(), | ||||||||||||||||||||||||||||||||||||||||||
| testResult.getEndMillis(), | ||||||||||||||||||||||||||||||||||||||||||
| asLong(testResult)); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public Statistics(String methodName, long startTimeInMs) { | ||||||||||||||||||||||||||||||||||||||||||
| public Statistics(String methodName, long startTimeInMs, long endTimeInMs, long threadId) { | ||||||||||||||||||||||||||||||||||||||||||
| this.methodName = methodName; | ||||||||||||||||||||||||||||||||||||||||||
| this.startTimeInMs = startTimeInMs; | ||||||||||||||||||||||||||||||||||||||||||
| this.endTimeInMs = endTimeInMs; | ||||||||||||||||||||||||||||||||||||||||||
| this.threadId = threadId; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private static long asLong(ITestResult itr) { | ||||||||||||||||||||||||||||||||||||||||||
| return ((Number) itr.getAttribute(SampleTestClass.THREAD_ID)).longValue(); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.