From 8630a7e8fe12985d71c00212f9362fd38fb0cb9e Mon Sep 17 00:00:00 2001
From: Krishnan Mahadevan <krishnan.mahadevan1978@gmail.com>
Date: Sat, 3 Dec 2022 22:23:15 +0530
Subject: [PATCH] Ensure ITestContext available for JUnit4 tests

Closes #2792
---
 CHANGES.txt                                   |  1 +
 .../org/testng/junit/JUnit4TestRunner.java    |  6 +++-
 .../org/testng/junit/JUnitTestRunner.java     | 11 +++++--
 .../src/test/java/test/JUnit4Test.java        | 17 ++++++++++
 .../junit4/issue2792/TestClassSample.java     |  9 ++++++
 .../TestContextGatheringListener.java         | 31 +++++++++++++++++++
 6 files changed, 71 insertions(+), 4 deletions(-)
 create mode 100644 testng-core/src/test/java/test/junit4/issue2792/TestClassSample.java
 create mode 100644 testng-core/src/test/java/test/junit4/issue2792/TestContextGatheringListener.java

diff --git a/CHANGES.txt b/CHANGES.txt
index 55e8d0347d..18fa6fbbb0 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 Current
+Fixed: GITHUB-2792: JUnitTestClass sets XmlTest as null when running JUnit 4 Tests using TestNG (Krishnan Mahadevan)
 Fixed: GITHUB-2844: Deprecate support for running Spock Tests (Krishnan Mahadevan)
 Fixed: GITHUB-550: Weird @BeforeMethod and @AfterMethod behaviour with dependsOnMethods (Krishnan Mahadevan)
 Fixed: GITHUB-893: TestNG should provide an Api which allow to find all dependent of a specific test (Krishnan Mahadevan)
diff --git a/testng-core/src/main/java/org/testng/junit/JUnit4TestRunner.java b/testng-core/src/main/java/org/testng/junit/JUnit4TestRunner.java
index 32688bbf2d..86ca94331b 100644
--- a/testng-core/src/main/java/org/testng/junit/JUnit4TestRunner.java
+++ b/testng-core/src/main/java/org/testng/junit/JUnit4TestRunner.java
@@ -231,7 +231,11 @@ private ITestResult createTestResult(ITestObjectFactory objectFactory, Descripti
     JUnit4TestClass tc = new JUnit4TestClass(test);
     JUnitTestMethod tm = new JUnit4TestMethod(objectFactory, tc, test);
 
-    TestResult tr = TestResult.newTestResultFor(tm);
+    ITestContext ctx = null;
+    if (m_parentRunner instanceof ITestContext) {
+      ctx = (ITestContext) m_parentRunner;
+    }
+    TestResult tr = TestResult.newContextAwareTestResult(tm, ctx);
 
     InvokedMethod im = new InvokedMethod(tr.getStartMillis(), tr);
     if (tr.getMethod() instanceof IInvocationStatus) {
diff --git a/testng-core/src/main/java/org/testng/junit/JUnitTestRunner.java b/testng-core/src/main/java/org/testng/junit/JUnitTestRunner.java
index 7ec4d2d5a4..42002a2309 100644
--- a/testng-core/src/main/java/org/testng/junit/JUnitTestRunner.java
+++ b/testng-core/src/main/java/org/testng/junit/JUnitTestRunner.java
@@ -27,8 +27,8 @@ public class JUnitTestRunner implements TestListener, IJUnitTestRunner {
   private final ITestObjectFactory m_objectFactory;
   private final ITestResultNotifier m_parentRunner;
 
-  private Map<Test, TestRunInfo> m_tests = new WeakHashMap<>();
-  private List<ITestNGMethod> m_methods = Lists.newArrayList();
+  private final Map<Test, TestRunInfo> m_tests = new WeakHashMap<>();
+  private final List<ITestNGMethod> m_methods = Lists.newArrayList();
   private Collection<IInvokedMethodListener> m_invokedMethodListeners = Lists.newArrayList();
 
   public JUnitTestRunner(ITestObjectFactory objectFactory, ITestResultNotifier tr) {
@@ -102,9 +102,14 @@ private org.testng.internal.TestResult recordResults(Test test, TestRunInfo tri)
     JUnitTestClass tc = new JUnit3TestClass(test);
     JUnitTestMethod tm = new JUnit3TestMethod(m_objectFactory, tc, test);
 
+    ITestContext ctx = null;
+    if (m_parentRunner instanceof ITestContext) {
+      ctx = (ITestContext) m_parentRunner;
+    }
+
     org.testng.internal.TestResult tr =
         org.testng.internal.TestResult.newEndTimeAwareTestResult(
-            tm, null, tri.m_failure, tri.m_start);
+            tm, ctx, tri.m_failure, tri.m_start);
 
     if (tri.isFailure()) {
       tr.setStatus(ITestResult.FAILURE);
diff --git a/testng-core/src/test/java/test/JUnit4Test.java b/testng-core/src/test/java/test/JUnit4Test.java
index 9145a76006..04724c6d69 100644
--- a/testng-core/src/test/java/test/JUnit4Test.java
+++ b/testng-core/src/test/java/test/JUnit4Test.java
@@ -1,9 +1,14 @@
 package test;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.testng.TestNG;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import test.junit4.*;
+import test.junit4.issue2792.TestClassSample;
+import test.junit4.issue2792.TestContextGatheringListener;
 
 public class JUnit4Test extends BaseTest {
 
@@ -72,4 +77,16 @@ public void testTests(
     verifyFailedTests(expectedFailedTests);
     verifySkippedTests(expectedSkippedTests);
   }
+
+  @Test(description = "GITHUB-2792")
+  public void ensureTestContextAvailableForListeners() {
+    TestNG testng = new TestNG();
+    testng.setTestClasses(new Class[] {TestClassSample.class});
+    TestContextGatheringListener listener = new TestContextGatheringListener();
+    testng.addListener(listener);
+    testng.setJUnit(true);
+    testng.run();
+    assertThat(listener.isTestContextFoundOnTestStart()).isTrue();
+    assertThat(listener.isTestContextFoundOnAfterInvocation()).isTrue();
+  }
 }
diff --git a/testng-core/src/test/java/test/junit4/issue2792/TestClassSample.java b/testng-core/src/test/java/test/junit4/issue2792/TestClassSample.java
new file mode 100644
index 0000000000..d7af869d3b
--- /dev/null
+++ b/testng-core/src/test/java/test/junit4/issue2792/TestClassSample.java
@@ -0,0 +1,9 @@
+package test.junit4.issue2792;
+
+import org.junit.Test;
+
+public class TestClassSample {
+
+  @Test
+  public void testMethod() {}
+}
diff --git a/testng-core/src/test/java/test/junit4/issue2792/TestContextGatheringListener.java b/testng-core/src/test/java/test/junit4/issue2792/TestContextGatheringListener.java
new file mode 100644
index 0000000000..0ced6a78e8
--- /dev/null
+++ b/testng-core/src/test/java/test/junit4/issue2792/TestContextGatheringListener.java
@@ -0,0 +1,31 @@
+package test.junit4.issue2792;
+
+import java.util.Objects;
+import org.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.ITestListener;
+import org.testng.ITestResult;
+
+public class TestContextGatheringListener implements IInvokedMethodListener, ITestListener {
+
+  private boolean testContextFoundOnTestStart = false;
+  private boolean testContextFoundOnAfterInvocation = false;
+
+  public boolean isTestContextFoundOnAfterInvocation() {
+    return testContextFoundOnAfterInvocation;
+  }
+
+  public boolean isTestContextFoundOnTestStart() {
+    return testContextFoundOnTestStart;
+  }
+
+  @Override
+  public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
+    testContextFoundOnAfterInvocation = Objects.nonNull(testResult.getTestContext());
+  }
+
+  @Override
+  public void onTestStart(ITestResult result) {
+    testContextFoundOnTestStart = Objects.nonNull(result.getTestContext());
+  }
+}