com.relevantcodes.extentreports.Report Maven / Gradle / Ivy
Show all versions of extentreports Show documentation
/*
* Copyright (c) 2015, Anshoo Arora (Relevant Codes). All rights reserved.
*
* Copyrights licensed under the New BSD License.
*
* See the accompanying LICENSE file for terms.
*/
package com.relevantcodes.extentreports;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.relevantcodes.extentreports.converters.TimeConverter;
import com.relevantcodes.extentreports.model.ExceptionInfo;
import com.relevantcodes.extentreports.model.Log;
import com.relevantcodes.extentreports.model.SuiteTimeInfo;
import com.relevantcodes.extentreports.model.Test;
import com.relevantcodes.extentreports.model.TestAttribute;
import com.relevantcodes.extentreports.utils.DateTimeUtil;
// Report abstract
abstract class Report extends LogSettings implements Serializable {
private static final long serialVersionUID = 888931414131302753L;
private static final Logger LOGGER = Logger.getLogger(Report.class.getName());
private static final String INTERNAL_WARNING = "Close was called before test could end safely using EndTest.";
/**
*
* Default protocol for style and script resources for the HTML report
*/
private static final String DEFAULT_PROTOCOL = "https";
/**
*
* Default configuration file for HTML report. This file is loaded by default
* when the {@link ExtentReports} initializes, and overridden when user provides
* their own configuration using loadConfig(args)
.
*/
private final String CONFIG_FILE = "extent-config.xml";
/**
*
* Path to the HTML report
*/
private String filePath;
/**
*
* Order in which tests will be displayed
*
*
* - DisplayOrder.OLDEST_FIRST - Displays the oldest (1st run) test at the top
* - DisplayOrder.NEWEST_FIRST - Displays the latest (last run) test at the top
*
*/
private DisplayOrder displayOrder;
/**
*
* NetworkMode setting for the HTML report
*
*
* - ONLINE - creates a single report file with all artifacts
* - OFFLINE - all report artifacts will be stored locally in
%reportFolder%/extentreports
* with the following structure:
*
* - extentreports/css
*
* - extentreports/js
*
*
*/
private NetworkMode networkMode;
/**
*
* Setting to overwrite (TRUE) the existing file or append (FALSE) to it
*
*
* - true - the file will be replaced with brand new markup, and all existing data
* will be lost. Use this option to create a brand new report
* - false - existing data will remain, new tests will be appended to the existing report.
* If the the supplied path does not exist, a new file will be created.
*
*/
private Boolean replaceExisting;
/**
*
* Default status of the report. This will be replace as logs are created. The final report status
* will be as per the top-most log status hierarchy as shown here:
* http://extentreports.relevantcodes.com/faqs.html#log-hierarchy
*/
private LogStatus reportStatus = LogStatus.UNKNOWN;
/**
*
* Report startime
*/
private Date startedTime;
/**
*
* Defines the total run duration in the past run, if the report is being appended
*
*
* Only applicable when replaceExisting = false
*
*
* Default value = 0
*/
private long totalDurationPastRun = 0; // millis
/**
*
* Run duration of the current suite
*/
private String currentSuiteRunDuration;
/**
*
* List of all logs created by the test runner
*
*
* Example: TestNG logs are created using Reporter.log()
and retrieved
* using Reporter.getOutput()
. It is possible to retrieve the logs and
* also update Extent using:
*
*
* for (String log : Reporter.getOutput()) {
* extent.setTestRunnerOutput(log)
* }
*/
private List testRunnerLogList;
/**
*
* List of all unique status logged
*/
private List logStatusList;
/**
*
* List of active reporters
*
*
* HTMLReporter is the default report and will be started automatically
*/
private List reporters;
/**
*
* Current executing test
*/
private Test test;
/**
*
* UUID of the report
*/
private UUID reportId;
/**
*
* Flag to indicate if the execution is ended using the close()
method.
* If true, prevent from creating/adding more tests.
*/
private Boolean terminated = false;
/**
*
* A map of categories and tests assigned
*/
private Map> categoryTestMap;
/**
*
* A map of exception headline and tests assigned
*/
private Map> exceptionTestMap;
/**
* Map of user defined configuration created from extent-config.xml
*/
private Map configurationMap;
/**
*
* Default configuration loaded from:
* com/relevantcodes/extentreports/resources/extent-config.xml
*
*
* Note: If user does not load a configuration file, the report uses the default
* configuration from the above path
*/
private Map defaultConfiguration;
/**
*
* Default locale of the HTML report
*/
private Locale locale = Locale.ENGLISH;
/**
*
* Report ID from MongoDB->Report collection, used by ExtentX
*/
private String mongoDBID = "";
/**
*
* Name of the project, "Default" if nothing set
*/
private String projectName = "Default";
protected SuiteTimeInfo suiteTimeInfo;
protected SystemInfo systemInfo;
protected List testList = new ArrayList();
protected File configFile = null;
protected List getTestList() {
return testList;
}
protected void updateTestQueue(ExtentTest extentTest) {
if (displayOrder == DisplayOrder.OLDEST_FIRST) {
testList.add(extentTest);
}
else {
testList.add(0, extentTest);
}
}
protected List getLogStatusList() {
return logStatusList;
}
protected Date getStartedTime() {
return startedTime;
}
protected String getRunDuration() {
currentSuiteRunDuration = DateTimeUtil.getDiff(Calendar.getInstance().getTime(), new Date(suiteTimeInfo.getSuiteStartTimestamp()));
return currentSuiteRunDuration;
}
protected String getRunDurationOverall() {
if (totalDurationPastRun == 0) {
if (currentSuiteRunDuration == null) {
getRunDuration();
}
return currentSuiteRunDuration;
}
long millis = (Calendar.getInstance().getTime().getTime() - startedTime.getTime());
millis += totalDurationPastRun;
long hours = TimeUnit.MILLISECONDS.toHours(millis);
long mins = TimeUnit.MILLISECONDS.toMinutes(millis);
long secs = TimeUnit.MILLISECONDS.toSeconds(millis);
millis -= secs * 1000;
return DateTimeUtil.getHMS(hours, mins, secs, millis);
}
protected void convertUpdateLastRunDuration() {
totalDurationPastRun = new TimeConverter(filePath).getLastRunDurationMillis();
}
protected List getTestRunnerLogList() {
return testRunnerLogList;
}
protected Map getConfigurationMap() {
return configurationMap;
}
protected Map> getCategoryTestMap() {
return categoryTestMap;
}
protected Map> getExceptionTestMap() {
return exceptionTestMap;
}
protected SystemInfo getSystemInfo() {
return systemInfo;
}
protected Map getSystemInfoMap() {
return getSystemInfo().getInfo();
}
protected void attach(IReporter reporter) {
if (reporters == null) {
reporters = new ArrayList();
}
reporters.add(reporter);
reporter.start(this);
}
protected void detach(IReporter reporter) {
reporter.stop();
reporters.remove(reporter);
}
protected void finalizeTest(Test test) {
if (test.getEndedTime() == null) {
test.setEndedTime(Calendar.getInstance().getTime());
}
Iterator catIter = test.categoryIterator();
// add each category and associated test
while (catIter.hasNext()) {
TestAttribute category = catIter.next();
if (!categoryTestMap.containsKey(category.getName())) {
List testList = new ArrayList();
testList.add(test);
categoryTestMap.put(category.getName(), testList);
}
else {
categoryTestMap.get(category.getName()).add(test);
}
}
List exceptionList = test.getExceptionList();
if (exceptionList != null) {
for (ExceptionInfo exceptionInfo : exceptionList) {
setCauseTest(exceptionInfo);
}
}
// #301 - for users using the TestNG listener, the log timestamps are all
// recorded as the time at which the report is generated. This causes the
// log timestamps to be greater than the test ended time. Below fix ensures
// the log timestamp is always equal to or between test start and end times
Iterator logIter = test.logIterator();
while (logIter.hasNext()) {
Log log = logIter.next();
if (log.getTimestamp().after(test.getEndedTime())) {
log.setTimestamp(test.getEndedTime());
}
}
test.prepareFinalize();
this.test = test;
for (IReporter reporter : reporters) {
reporter.addTest(test);
}
updateReportStatus(test.getStatus());
updateReportStartedTime(test);
}
private void setCauseTest(ExceptionInfo exceptionInfo) {
String ex = exceptionInfo.getExceptionName();
if (!exceptionTestMap.containsKey(ex)) {
exceptionTestMap.put(ex, new ArrayList());
}
exceptionTestMap.get(ex).add(exceptionInfo);
}
private void updateTestStatusList(Test test) {
Boolean toAdd = false;
toAdd = test.getStatus() == LogStatus.FATAL || test.getStatus() == LogStatus.ERROR || test.getStatus() == LogStatus.WARNING || test.getStatus() == LogStatus.UNKNOWN
? true
: false;
if (toAdd) {
if (!logStatusList.contains(test.getStatus())) {
logStatusList.add(test.getStatus());
}
}
if (test.hasChildNodes) {
List nodeList = test.getNodeList();
for (Test node : nodeList) {
updateTestStatusList(node);
}
}
}
private void updateReportStartedTime(Test test) {
long testStartedTime = test.getStartedTime().getTime();
if (suiteTimeInfo.getSuiteStartTimestamp() > testStartedTime) {
suiteTimeInfo.setSuiteStartTimestamp(testStartedTime);
}
}
protected void terminate() {
for (ExtentTest extentTest : testList) {
Test test = extentTest.getInternalTest();
if (!test.hasEnded) {
ExtentTestInterruptedException e = new ExtentTestInterruptedException(INTERNAL_WARNING);
test.setInternalWarning(INTERNAL_WARNING);
extentTest.log(LogStatus.FAIL, e);
test.hasEnded = true;
finalizeTest(test);
}
}
flush();
Iterator iter = reporters.iterator();
while (iter.hasNext()) {
iter.next().stop();
iter.remove();
}
terminated = true;
}
protected void flush() {
if (terminated) {
try {
throw new IOException("Unable to write source: Stream closed.");
}
catch (IOException e) {
LOGGER.log(Level.SEVERE, "Stream closed", e);
}
return;
}
suiteTimeInfo.setSuiteEndTimestamp(new Date().getTime());
// #320 - ensure atleast 1 test is added, otherwise skip the flush process
if (getTestList() != null) {
for (ExtentTest test : getTestList()) {
updateTestStatusList(test.getInternalTest());
}
for (IReporter reporter : reporters) {
reporter.flush();
}
}
}
protected Map loadConfig(Configuration config) {
configurationMap = config.getConfigurationMap();
// if user is using an out-dated version of the configuration file,
// use default configuration for keys that are not available
if (defaultConfiguration != null) {
for (Map.Entry entry : defaultConfiguration.entrySet()) {
if (!configurationMap.containsKey(entry.getKey())) {
configurationMap.put(entry.getKey(), entry.getValue());
}
}
}
updateBaseDefaultSettings(configurationMap);
return configurationMap;
}
/**
* Updates LogSettings with custom values
*
* @param configurationMap
*/
private void updateBaseDefaultSettings(Map configurationMap) {
if (configurationMap.get("dateFormat") != null && !configurationMap.get("dateFormat").isEmpty()) {
setLogDateFormat(configurationMap.get("dateFormat"));
}
else {
configurationMap.put("dateFormat", getLogDateFormat());
}
if (configurationMap.get("timeFormat") != null && !configurationMap.get("timeFormat").isEmpty()) {
setLogTimeFormat(configurationMap.get("timeFormat"));
}
else {
configurationMap.put("timeFormat", getLogTimeFormat());
}
if (configurationMap.get("protocol") == null || configurationMap.get("protocol").isEmpty()) {
configurationMap.put("protocol", DEFAULT_PROTOCOL);
}
}
protected void setTestRunnerLogs(String logs) {
testRunnerLogList.add(logs);
}
protected Test getCurrentTest() {
return test;
}
protected void setFilePath(String filePath) {
this.filePath = filePath;
File reportFile = new File(filePath);
if (reportFile.getParentFile() != null && !reportFile.getParentFile().exists()) {
reportFile.getParentFile().mkdirs();
}
}
protected String getFilePath() {
return filePath;
}
protected void setReplaceExisting(Boolean replaceExisting) {
this.replaceExisting = replaceExisting;
}
protected Boolean getReplaceExisting() {
return replaceExisting;
}
protected void setDisplayOrder(DisplayOrder displayOrder) {
this.displayOrder = displayOrder;
}
protected DisplayOrder getDisplayOrder() {
return displayOrder;
}
protected void setNetworkMode(NetworkMode networkMode) {
this.networkMode = networkMode;
}
protected NetworkMode getNetworkMode() {
return networkMode;
}
protected UUID getId() {
return reportId;
}
protected LogStatus getStatus() {
return reportStatus;
}
protected SuiteTimeInfo getSuiteTimeInfo() {
return suiteTimeInfo;
}
protected void setStartedTime(long startTime) {
suiteTimeInfo.setSuiteStartTimestamp(startTime);
}
protected void setDocumentLocale(Locale locale) {
this.locale = locale;
}
protected Locale getDocumentLocale() {
return locale;
}
protected void setProjectName(String name) {
projectName = name;
}
public String getProjectName() {
return projectName;
}
protected void setMongoDBObjectID(String id) {
this.mongoDBID = id;
}
public String getMongoDBObjectID() {
return mongoDBID;
}
protected Report() {
String resourceFile = Report.class
.getPackage()
.getName()
.replace(".", "/")
+ "/resources/"
+ CONFIG_FILE;
URL url = getClass().getClassLoader().getResource(resourceFile);
defaultConfiguration = loadConfig(new Configuration(url));
categoryTestMap = new TreeMap>();
exceptionTestMap = new TreeMap>();
systemInfo = new SystemInfo();
suiteTimeInfo = new SuiteTimeInfo();
testRunnerLogList = new ArrayList();
logStatusList = new ArrayList();
reportId = UUID.randomUUID();
startedTime = new Date(suiteTimeInfo.getSuiteStartTimestamp());
}
private void updateReportStatus(LogStatus logStatus) {
if (reportStatus == LogStatus.FATAL) return;
if (logStatus == LogStatus.FATAL) {
reportStatus = logStatus;
return;
}
if (reportStatus == LogStatus.FAIL) return;
if (logStatus == LogStatus.FAIL) {
reportStatus = logStatus;
return;
}
if (reportStatus == LogStatus.ERROR) return;
if (logStatus == LogStatus.ERROR) {
reportStatus = logStatus;
return;
}
if (reportStatus == LogStatus.WARNING) return;
if (logStatus == LogStatus.WARNING) {
reportStatus = logStatus;
return;
}
if (reportStatus == LogStatus.SKIP) return;
if (logStatus == LogStatus.SKIP) {
reportStatus = LogStatus.SKIP;
return;
}
if (reportStatus == LogStatus.PASS) return;
if (logStatus == LogStatus.PASS) {
reportStatus = LogStatus.PASS;
return;
}
if (reportStatus == LogStatus.INFO) return;
if (logStatus == LogStatus.INFO) {
reportStatus = LogStatus.INFO;
return;
}
reportStatus = LogStatus.UNKNOWN;
}
}