From 33ee6012a633228d9adfd51c413b65386a0638f4 Mon Sep 17 00:00:00 2001
From: Krishnan Mahadevan <krishnan.mahadevan1978@gmail.com>
Date: Tue, 14 Mar 2023 12:32:14 +0530
Subject: [PATCH] Include listeners reference in testng-failed.xml

Closes #2879
---
 CHANGES.txt                                   |  1 +
 .../org/testng/reporters/FailedReporter.java  |  6 +++
 .../java/test/reports/FailedReporterTest.java | 50 +++++++++++++++++++
 .../issue2879/AnotherPrintingListener.java    |  9 ++++
 .../reports/issue2879/PrintingListener.java   | 11 ++++
 .../reports/issue2879/TestClassSample.java    | 23 +++++++++
 6 files changed, 100 insertions(+)
 create mode 100644 testng-core/src/test/java/test/reports/issue2879/AnotherPrintingListener.java
 create mode 100644 testng-core/src/test/java/test/reports/issue2879/PrintingListener.java
 create mode 100644 testng-core/src/test/java/test/reports/issue2879/TestClassSample.java

diff --git a/CHANGES.txt b/CHANGES.txt
index 288d93dcc5..97548b3288 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 Current
+Fixed: GITHUB-2879: Test listeners specified in parent testng.xml file are not included in testng-failed.xml file (Krishnan Mahadevan)
 Fixed: GITHUB-2866: TestNG.xml doesn't honour Parallel value of a clone (Krishnan Mahadevan)
 Fixed: GITHUB-2875: JUnitReportReporter should capture the test case output at the test case level
 Fixed: GITHUB-2771: After upgrading to TestNG 7.5.0, setting ITestResult.status to FAILURE doesn't fail the test anymore (Julien Herr & Krishnan Mahadevan)
diff --git a/testng-core/src/main/java/org/testng/reporters/FailedReporter.java b/testng-core/src/main/java/org/testng/reporters/FailedReporter.java
index 246d591ed8..155bf60caf 100644
--- a/testng-core/src/main/java/org/testng/reporters/FailedReporter.java
+++ b/testng-core/src/main/java/org/testng/reporters/FailedReporter.java
@@ -77,6 +77,12 @@ protected void generateFailureSuite(XmlSuite xmlSuite, ISuite suite, String outp
     }
 
     if (null != failedSuite.getTests() && failedSuite.getTests().size() > 0) {
+      if (xmlSuite.getParentSuite() != null
+          && !xmlSuite.getParentSuite().getLocalListeners().isEmpty()) {
+        List<String> merged =
+            Lists.merge(failedSuite.getListeners(), xmlSuite.getParentSuite().getListeners());
+        failedSuite.setListeners(merged);
+      }
       Utils.writeUtf8File(outputDir, TESTNG_FAILED_XML, failedSuite.toXml());
       Utils.writeUtf8File(suite.getOutputDirectory(), TESTNG_FAILED_XML, failedSuite.toXml());
     }
diff --git a/testng-core/src/test/java/test/reports/FailedReporterTest.java b/testng-core/src/test/java/test/reports/FailedReporterTest.java
index 4cea05792f..32fc29b410 100644
--- a/testng-core/src/test/java/test/reports/FailedReporterTest.java
+++ b/testng-core/src/test/java/test/reports/FailedReporterTest.java
@@ -8,6 +8,7 @@
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.testng.Assert;
@@ -32,9 +33,58 @@
 import test.reports.issue2611.TestClassWithBeforeSuiteSample;
 import test.reports.issue2611.TestClassWithBeforeTestSample;
 import test.reports.issue2611.TestClassWithJustTestMethodsSample;
+import test.reports.issue2879.AnotherPrintingListener;
+import test.reports.issue2879.PrintingListener;
+import test.reports.issue2879.TestClassSample;
 
 public class FailedReporterTest extends SimpleBaseTest {
 
+  @Test(description = "GITHUB-2879")
+  public void ensureParentListenersArePresentInFailedChildSuites() throws IOException {
+    XmlSuite xmlParentSuite = createXmlSuite("parent_suite");
+    xmlParentSuite.setListeners(Collections.singletonList(PrintingListener.class.getName()));
+    XmlSuite xmlSuite = createXmlSuite("2879_suite");
+    xmlSuite.setParentSuite(xmlParentSuite);
+    XmlTest xmlChildTest = createXmlTest(xmlSuite, "2879_test");
+    xmlChildTest.setClasses(Collections.singletonList(new XmlClass(TestClassSample.class)));
+    xmlParentSuite.getChildSuites().add(xmlSuite);
+    TestNG tng = create(xmlParentSuite);
+    Path temp = Files.createTempDirectory("tmp");
+    tng.setOutputDirectory(temp.toAbsolutePath().toString());
+    tng.addListener(new FailedReporter());
+    tng.run();
+
+    Collection<XmlSuite> failedSuites =
+        new Parser(temp.resolve(FailedReporter.TESTNG_FAILED_XML).toAbsolutePath().toString())
+            .parse();
+    XmlSuite failedSuite = failedSuites.iterator().next();
+    assertThat(failedSuite.getListeners()).containsExactly(PrintingListener.class.getName());
+  }
+
+  @Test(description = "GITHUB-2879")
+  public void ensureParentListenersAreAppendedInFailedChildSuites() throws IOException {
+    XmlSuite xmlParentSuite = createXmlSuite("parent_suite");
+    xmlParentSuite.setListeners(Collections.singletonList(PrintingListener.class.getName()));
+    XmlSuite xmlSuite = createXmlSuite("2879_suite");
+    xmlSuite.setParentSuite(xmlParentSuite);
+    xmlSuite.setListeners(Collections.singletonList(AnotherPrintingListener.class.getName()));
+    XmlTest xmlChildTest = createXmlTest(xmlSuite, "2879_test");
+    xmlChildTest.setClasses(Collections.singletonList(new XmlClass(TestClassSample.class)));
+    xmlParentSuite.getChildSuites().add(xmlSuite);
+    TestNG tng = create(xmlParentSuite);
+    Path temp = Files.createTempDirectory("tmp");
+    tng.setOutputDirectory(temp.toAbsolutePath().toString());
+    tng.addListener(new FailedReporter());
+    tng.run();
+
+    Collection<XmlSuite> failedSuites =
+        new Parser(temp.resolve(FailedReporter.TESTNG_FAILED_XML).toAbsolutePath().toString())
+            .parse();
+    XmlSuite failedSuite = failedSuites.iterator().next();
+    assertThat(failedSuite.getListeners())
+        .contains(PrintingListener.class.getName(), AnotherPrintingListener.class.getName());
+  }
+
   @Test
   public void failedFile() throws IOException {
     XmlSuite xmlSuite = createXmlSuite("Suite");
diff --git a/testng-core/src/test/java/test/reports/issue2879/AnotherPrintingListener.java b/testng-core/src/test/java/test/reports/issue2879/AnotherPrintingListener.java
new file mode 100644
index 0000000000..7458eca49f
--- /dev/null
+++ b/testng-core/src/test/java/test/reports/issue2879/AnotherPrintingListener.java
@@ -0,0 +1,9 @@
+package test.reports.issue2879;
+
+import org.testng.IExecutionListener;
+
+public class AnotherPrintingListener implements IExecutionListener {
+
+  @Override
+  public void onExecutionStart() {}
+}
diff --git a/testng-core/src/test/java/test/reports/issue2879/PrintingListener.java b/testng-core/src/test/java/test/reports/issue2879/PrintingListener.java
new file mode 100644
index 0000000000..e4c426a419
--- /dev/null
+++ b/testng-core/src/test/java/test/reports/issue2879/PrintingListener.java
@@ -0,0 +1,11 @@
+package test.reports.issue2879;
+
+import org.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.ITestResult;
+
+public class PrintingListener implements IInvokedMethodListener {
+
+  @Override
+  public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {}
+}
diff --git a/testng-core/src/test/java/test/reports/issue2879/TestClassSample.java b/testng-core/src/test/java/test/reports/issue2879/TestClassSample.java
new file mode 100644
index 0000000000..d30405a4fd
--- /dev/null
+++ b/testng-core/src/test/java/test/reports/issue2879/TestClassSample.java
@@ -0,0 +1,23 @@
+package test.reports.issue2879;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestClassSample {
+
+  @Test
+  public void passingOne() {}
+
+  @Test
+  public void passingTwo() {}
+
+  @Test
+  public void failingOne() {
+    Assert.fail();
+  }
+
+  @Test
+  public void failingTwo() {
+    Assert.fail();
+  }
+}