View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.testng;
20  
21  import java.util.Arrays;
22  
23  import org.apache.maven.surefire.api.report.CategorizedReportEntry;
24  import org.apache.maven.surefire.api.report.OutputReportEntry;
25  import org.apache.maven.surefire.api.report.ReportEntry;
26  import org.apache.maven.surefire.api.report.RunListener;
27  import org.apache.maven.surefire.api.report.RunMode;
28  import org.apache.maven.surefire.api.report.SimpleReportEntry;
29  import org.apache.maven.surefire.api.report.StackTraceWriter;
30  import org.apache.maven.surefire.api.report.TestOutputReceiver;
31  import org.apache.maven.surefire.api.report.TestOutputReportEntry;
32  import org.apache.maven.surefire.api.report.TestReportListener;
33  import org.apache.maven.surefire.report.ClassMethodIndexer;
34  import org.apache.maven.surefire.report.PojoStackTraceWriter;
35  import org.apache.maven.surefire.report.RunModeSetter;
36  import org.testng.IClass;
37  import org.testng.ISuite;
38  import org.testng.ISuiteListener;
39  import org.testng.ITestContext;
40  import org.testng.ITestListener;
41  import org.testng.ITestResult;
42  
43  import static org.apache.maven.surefire.api.report.SimpleReportEntry.ignored;
44  import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
45  
46  /**
47   * Listens for and provides and adaptor layer so that
48   * TestNG tests can report their status to the current
49   * {@link RunListener}.
50   *
51   * @author jkuhnert
52   */
53  public class TestNGReporter
54          implements TestOutputReceiver<OutputReportEntry>, ITestListener, ISuiteListener, RunModeSetter {
55      private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
56      private final TestReportListener<TestOutputReportEntry> reporter;
57      private volatile RunMode runMode;
58  
59      /**
60       * Constructs a new instance that will listen to
61       * test updates from a {@link org.testng.TestNG} class instance.
62       * <br>
63       * <br>It is assumed that the requisite {@link org.testng.TestNG#addListener(ITestListener)}
64       * method call has already associated with this instance <i>before</i> the test
65       * suite is run.
66       *
67       * @param reportManager Instance to report suite status to
68       */
69      public TestNGReporter(TestReportListener<TestOutputReportEntry> reportManager) {
70          this.reporter = reportManager;
71      }
72  
73      protected final RunListener getRunListener() {
74          return reporter;
75      }
76  
77      @Override
78      public void onTestStart(ITestResult result) {
79          String className = result.getTestClass().getName();
80          String methodName = testName(result);
81          long testRunId = classMethodIndexer.indexClassMethod(className, methodName);
82          String group = groupString(result.getMethod().getGroups(), className);
83          reporter.testStarting(new CategorizedReportEntry(runMode, testRunId, className, methodName, group));
84      }
85  
86      @Override
87      public void onTestSuccess(ITestResult result) {
88          String className = result.getTestClass().getName();
89          String methodName = testName(result);
90          long testRunId = classMethodIndexer.indexClassMethod(className, methodName);
91          ReportEntry report = new SimpleReportEntry(runMode, testRunId, className, null, methodName, null);
92          reporter.testSucceeded(report);
93      }
94  
95      @Override
96      public void onTestFailure(ITestResult result) {
97          IClass clazz = result.getTestClass();
98          String className = clazz.getName();
99          String methodName = testName(result);
100         long testRunId = classMethodIndexer.indexClassMethod(className, methodName);
101         StackTraceWriter stackTraceWriter = new PojoStackTraceWriter(
102                 clazz.getRealClass().getName(), result.getMethod().getMethodName(), result.getThrowable());
103         ReportEntry report =
104                 withException(runMode, testRunId, clazz.getName(), null, methodName, null, stackTraceWriter);
105 
106         reporter.testFailed(report);
107     }
108 
109     @Override
110     public void onTestSkipped(ITestResult result) {
111         String className = result.getTestClass().getName();
112         String methodName = testName(result);
113         long testRunId = classMethodIndexer.indexClassMethod(className, methodName);
114         //noinspection ThrowableResultOfMethodCallIgnored
115         Throwable t = result.getThrowable();
116         String reason = t == null ? null : t.getMessage();
117         ReportEntry report = ignored(runMode, testRunId, className, null, methodName, null, reason);
118         reporter.testSkipped(report);
119     }
120 
121     @Override
122     public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
123         IClass clazz = result.getTestClass();
124         String className = clazz.getName();
125         String methodName = testName(result);
126         long testRunId = classMethodIndexer.indexClassMethod(className, methodName);
127         StackTraceWriter stackTraceWriter = new PojoStackTraceWriter(
128                 clazz.getRealClass().getName(), result.getMethod().getMethodName(), result.getThrowable());
129         ReportEntry report = withException(runMode, testRunId, className, null, methodName, null, stackTraceWriter);
130         reporter.testSucceeded(report);
131     }
132 
133     @Override
134     public void onStart(ITestContext context) {}
135 
136     @Override
137     public void onFinish(ITestContext context) {}
138 
139     @Override
140     public void onStart(ISuite suite) {}
141 
142     @Override
143     public void onFinish(ISuite suite) {}
144 
145     /**
146      * Creates a string out of the list of testng groups in the
147      * form of <pre>"group1,group2,group3"</pre>.
148      *
149      * @param groups       The groups being run
150      * @param defaultValue The default to use if no groups
151      * @return a string describing the groups
152      */
153     private static String groupString(String[] groups, String defaultValue) {
154         String retVal;
155         if (groups != null && groups.length > 0) {
156             StringBuilder str = new StringBuilder();
157             for (int i = 0; i < groups.length; i++) {
158                 str.append(groups[i]);
159                 if (i + 1 < groups.length) {
160                     str.append(",");
161                 }
162             }
163             retVal = str.toString();
164         } else {
165             retVal = defaultValue;
166         }
167         return retVal;
168     }
169 
170     public void onConfigurationFailure(ITestResult result) {
171         onTestFailure(result);
172     }
173 
174     public void onConfigurationSkip(ITestResult result) {
175         onTestSkipped(result);
176     }
177 
178     public void onConfigurationSuccess(ITestResult result) {
179         // DGF Don't record configuration successes as separate tests
180         // onTestSuccess( result );
181     }
182 
183     /**
184      * Acquire a better representation of the test name that includes parameters and the invocation count, if there are
185      * any parameters
186      *
187      * @param result the test result to extract from
188      * @return a descriptive name for the test
189      */
190     private static String testName(ITestResult result) {
191         Object[] parameters = result.getParameters();
192         String name = result.getName();
193         return parameters == null || parameters.length == 0
194                 ? name
195                 : name + Arrays.toString(parameters) + "(" + result.getMethod().getCurrentInvocationCount() + ")";
196     }
197 
198     @Override
199     public void setRunMode(RunMode runMode) {
200         this.runMode = runMode;
201     }
202 
203     @Override
204     public void writeTestOutput(OutputReportEntry reportEntry) {
205         Long testRunId = classMethodIndexer.getLocalIndex();
206         reporter.writeTestOutput(new TestOutputReportEntry(reportEntry, runMode, testRunId));
207     }
208 }