All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aventstack.extentreports.ExtentObservable Maven / Gradle / Ivy

There is a newer version: 5.1.2
Show newest version
package com.aventstack.extentreports;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.aventstack.extentreports.model.Author;
import com.aventstack.extentreports.model.Category;
import com.aventstack.extentreports.model.Device;
import com.aventstack.extentreports.model.Log;
import com.aventstack.extentreports.model.ScreenCapture;
import com.aventstack.extentreports.model.Screencast;
import com.aventstack.extentreports.model.SystemAttribute;
import com.aventstack.extentreports.model.Test;
import com.aventstack.extentreports.reporter.BasicFileReporter;

abstract class ExtentObservable 
	implements ReportService {

	/**
	 * The current AnalysisStrategy for the run session. This decides the technique used 
	 * to count the test status at differing levels. For example, for a BDD style report, 
	 * the levels to be calculated are Feature, Scenario and Step (3 levels). For a generic, 
	 * non-BDD style report, levels can be dynamic. For a non-BDD style report, levels typically 
	 * consist of:
	 * 
	 * 

* 1 level: Test
* Test
* - Event * *

* 2 levels: Test & node
* Test
- Node
-- Event * *

* 2 levels: Test, the leaf-node
* Test
- Node
-- Leaf Node
--- Event * */ private AnalysisStrategy strategy = AnalysisStrategy.TEST; /** * Use this setting when building post-execution reports, such as from TestNG IReporter. * This setting allows setting test with your own variables and prevent update by Extent. * As of today, with this enabled, Extent does not use time-stamps for tests at the time * they were created */ private boolean usesManualConfiguration = false; /** * The status of the entire report or build */ private Status reportStatus = Status.PASS; /** * Time the report or build was started */ private Date reportStartDate = Calendar.getInstance().getTime(); /** * Time the report or build ended. This value is updated everytime flush * is called */ private Date reportEndDate; /** * A collection of tests arranged by category */ private TestAttributeTestContextProvider categoryContext = new TestAttributeTestContextProvider<>(); /** * A collection of tests arranged by author */ private TestAttributeTestContextProvider authorContext = new TestAttributeTestContextProvider<>(); /** * A collection of tests arranged by author */ private TestAttributeTestContextProvider deviceContext = new TestAttributeTestContextProvider<>(); /** * A collection of tests arranged by exception */ private ExceptionTestContextImpl exceptionContextBuilder = new ExceptionTestContextImpl(); /** * A context of all system or environment variables */ private SystemAttributeContext systemAttributeContext = new SystemAttributeContext(); /** * A list of all {@link ExtentReporter} reporters started by the attachReporter * method */ private List reporterList = new ArrayList<>(); /** * Any logs added by to the test runner can be added to Extent * *

* TestNG Example: * *

* Setting logs with TestNG: * *

Reporter.log("hello world")
* *

* Informing Extent of any logs added: *

	 * for (String s : Reporter.getOutput()) {
     *       extent.setTestRunnerOutput(s);
     * }
     * 
*/ private List testRunnerLogs = new ArrayList<>();; /** * A list of all tests created */ private List testList = new ArrayList<>(); /** * Instance of {@link ReportStatusStats} */ private ReportStatusStats stats = new ReportStatusStats(strategy); /** * A unique list of status tests are marked with * *

* Consider a report having 10 tests: * *

    *
  1. Test1: PASS
  2. *
  3. Test2: PASS
  4. *
  5. Test3: PASS
  6. *
  7. Test4: SKIP
  8. *
  9. Test5: SKIP
  10. *
  11. Test6: FAIL
  12. *
  13. Test7: PASS
  14. *
  15. Test8: PASS
  16. *
  17. Test9: FAIL
  18. *
  19. Test10: PASS
  20. *
* *

* Distinct list of contained status: * *

    *
  1. PASS
  2. *
  3. SKIP
  4. *
  5. FAIL
  6. *
*/ private List statusList = new ArrayList<>(); /** * Contains status as keys, which are translated over to statusList */ private Map statusMap = new HashMap<>(); protected ExtentObservable() { } /** * Subscribe the reporter to receive updates when making calls to the API * * @param reporter {@link ExtentReporter} reporter */ protected void register(ExtentReporter reporter) { reporterList.add(reporter); reporter.start(); } /** * Unsubscribe the reporter. Calling unsubscribe will call the stop method * and also remove the reporter from the list of started reporters * * @param reporter {@link ExtentReporter} reporter to unsubscribe */ protected void deregister(ExtentReporter reporter) { reporter.stop(); reporterList.remove(reporter); } /** * Retrieve a list of all started reporters * * @return a list of {@link ExtentReporter} objects */ protected List getReporterCollection() { return reporterList; } /** * Saves the started test and notifies all started reporters * * @param test a {@link Test} object */ protected synchronized void saveTest(Test test) { reporterInitialized(); testList.add(test); reporterList.forEach(x -> x.onTestStarted(test)); } private void reporterInitialized() { /*if (reporterList.isEmpty()) throw new IllegalStateException("No reporters started"); */ } /** * Removes the test and notifies all started reporters * * @param test a {@link Test} object */ protected synchronized void removeTest(Test test) { removeTestTestList(test); removeTestTestAttributeContext(test); reporterList.forEach(x -> x.onTestRemoved(test)); } /** * Removes a test from test list * * @param test a {@link Test} object */ private void removeTestTestList(Test test) { TestRemover.remove(testList, test); refreshReportEntities(); } /** * Removes test from test list of each context * * @param test a {@link Test} object */ private void removeTestTestAttributeContext(Test test) { if (test.hasCategory()) { categoryContext.removeTest(test); } if (test.hasAuthor()) { authorContext.removeTest(test); } if (test.hasDevice()) { deviceContext.removeTest(test); } } /** * Refreshes report entities such as {@link ReportStatusStats} and list of distinct {@link Status} */ private void refreshReportEntities() { refreshReportStats(); refreshStatusList(); } /** * Refresh and notify all reports of {@link ReportStatusStats} */ private void refreshReportStats() { stats.refresh(testList); } /** * Refresh and notify all reports of distinct status assigned to tests */ private void refreshStatusList() { statusMap.clear(); statusList.clear(); refreshStatusList(testList); statusMap.forEach((k,v) -> statusList.add(k)); } /** * Refreshes distinct status list * * @param list a list of started {@link Test} */ private void refreshStatusList(List list) { if (list == null || list.isEmpty()) return; list.stream() .map(Test::getStatus) .distinct() .collect(Collectors.toList()) .forEach(x -> statusMap.put(x, false)); list.forEach(x -> refreshStatusList(x.getNodeContext().getAll())); } /** * Notify reporters of the added node * * @param node a {@link Test} node */ synchronized void addNode(Test node) { reporterList.forEach(x -> x.onNodeStarted(node)); } /** * Notifies reporters with information of added {@link Log} * * @param test {@link Test} to which the event is added * @param log {@link Log} */ synchronized void addLog(Test test, Log log) { collectRunInfo(); reporterList.forEach(x -> x.onLogAdded(test, log)); } /** * Notifies reporters with information of added {@link Category} * * @param test {@link Test} to which the Category is added * @param category {@link Category} */ synchronized void assignCategory(Test test, Category category) { reporterList.forEach(x -> x.onCategoryAssigned(test, category)); } /** * Notifies reporters with information of added {@link Author} * * @param test {@link Test} to which the Author is added * @param author {@link Author} */ synchronized void assignAuthor(Test test, Author author) { reporterList.forEach(x -> x.onAuthorAssigned(test, author)); } /** * Notifies reporters with information of added {@link Author} * * @param test {@link Test} to which the Device is added * @param device {@link Device} */ synchronized void assignDevice(Test test, Device device) { reporterList.forEach(x -> x.onDeviceAssigned(test, device)); } /** * Notifies reporters with information of added {@link ScreenCapture} * * @param test {@link Test} to which the ScreenCapture is added * @param screenCapture {@link ScreenCapture} * * @throws IOException thrown if the {@link ScreenCapture} is not found */ synchronized void addScreenCapture(Test test, ScreenCapture screenCapture) throws IOException { for (ExtentReporter r : reporterList) { r.onScreenCaptureAdded(test, screenCapture); } } /** * Notifies reporters with information of added {@link ScreenCapture} * * @param test {@link Log} to which the ScreenCapture is added * @param screenCapture {@link ScreenCapture} * * @throws IOException thrown if the {@link ScreenCapture} is not found */ synchronized void addScreenCapture(Log log, ScreenCapture screenCapture) throws IOException { for (ExtentReporter r : reporterList) { r.onScreenCaptureAdded(log, screenCapture); } } /** * Notifies reporters with information of added {@link Screencast} * * @param test {@link Test} to which the ScreenCast is added * @param sc {@link Screencast} * * @throws IOException thrown if the {@link Screencast} is not found */ synchronized void addScreencast(Test test, Screencast screencast) throws IOException { for (ExtentReporter r : reporterList) { r.onScreencastAdded(test, screencast); } } /** * Returns the context of author with the list of tests for each * * @return a {@link TestAttributeTestContextProvider} object */ protected TestAttributeTestContextProvider getAuthorContextInfo() { return authorContext; } /** * Updates the status of the report or build * * @param status a {@link Status} */ private void updateReportStatus(Status status) { int statusIndex = Status.getStatusHierarchy().indexOf(status); int reportStatusIndex = Status.getStatusHierarchy().indexOf(reportStatus); reportStatus = statusIndex < reportStatusIndex ? status : reportStatus; } /** * Ends the test * * @param test a {@link Test} object */ private void endTest(Test test) { test.end(); updateReportStatus(test.getStatus()); } /** * Collects and updates all run information and notifies all reporters. Depending upon the * type of reporter, additional events can occur such as: * *
    *
  • A file written to disk (eg. case of {@link BasicFileReporter}
  • *
  • A database being updated (eg. case of KlovReporter)
  • *
*/ protected synchronized void flush() { reporterInitialized(); collectRunInfo(); notifyReporters(); } /** * Collects run information from all tests for assigned {@link Category}, {@link Author}, * Exception, Nodes. This also ends and updates all internal test information and * refreshes {@link ReportStatusStats} and the distinct list of {@link Status} */ private synchronized void collectRunInfo() { if (testList == null || testList.isEmpty()) return; reportEndDate = Calendar.getInstance().getTime(); testList.forEach(this::endTest); refreshReportEntities(); for (Test test : testList) { if (test.hasCategory()) { test.getCategoryContext().getAll() .forEach(x -> categoryContext.setAttributeContext((Category)x, test)); } if (test.hasAuthor()) { test.getAuthorContext().getAll() .forEach(x -> authorContext.setAttributeContext((Author)x, test)); } if (test.hasDevice()) { test.getDeviceContext().getAll() .forEach(x -> deviceContext.setAttributeContext((Device)x, test)); } if (test.hasException()) { test.getExceptionInfoList() .forEach(x -> exceptionContextBuilder.setExceptionContext(x, test)); } if (test.hasChildren()) { test.getNodeContext().getAll() .forEach(this::copyNodeAttributeAndRunTimeInfoToAttributeContexts); } } updateReportStartTimeForManualConfigurationSetting(); } /** * In case where manual configuration is used, calculate the correct timestamps based upon * the timestamps assigned to tests */ private void updateReportStartTimeForManualConfigurationSetting() { if (usesManualConfiguration) { Date minDate = testList.stream() .map(t -> t.getStartTime()) .min(Date::compareTo) .get(); Date maxDate = testList.stream() .map(t -> t.getEndTime()) .max(Date::compareTo) .get(); reportStartDate = reportStartDate.getTime() > minDate.getTime() ? minDate : reportStartDate; reportEndDate = reportEndDate.getTime() < maxDate.getTime() ? maxDate : reportEndDate; } } /** * Traverse all nodes and refresh {@link Category}, {@link Author}, Exception and Node context * information * * @param node a {@link Test} node */ private void copyNodeAttributeAndRunTimeInfoToAttributeContexts(Test node) { if (node.hasCategory()) { node.getCategoryContext().getAll() .forEach(x -> categoryContext.setAttributeContext((Category)x, node)); } if (node.hasAuthor()) { node.getAuthorContext().getAll() .forEach(x -> authorContext.setAttributeContext((Author)x, node)); } if (node.hasDevice()) { node.getDeviceContext().getAll() .forEach(x -> deviceContext.setAttributeContext((Device)x, node)); } if (node.hasException()) { node.getExceptionInfoList() .forEach(x -> exceptionContextBuilder.setExceptionContext(x, node)); } if (node.hasChildren()) { node.getNodeContext().getAll() .forEach(this::copyNodeAttributeAndRunTimeInfoToAttributeContexts); } } /** * Notify all reporters with complete run information */ private synchronized void notifyReporters() { if (!testList.isEmpty() && testList.get(0).isBehaviorDrivenType()) { strategy = AnalysisStrategy.BDD; } ReportAggregates reportAggregates = new ReportAggregatesBuilder() .setAuthorContext(authorContext) .setCategoryContext(categoryContext) .setDeviceContext(deviceContext) .setExceptionContext(exceptionContextBuilder) .setReportStatusStats(stats) .setStatusList(statusList) .setSystemAttributeContext(systemAttributeContext) .setTestList(testList) .setTestRunnerLogs(testRunnerLogs) .build(); reporterList.forEach(x -> x.setAnalysisStrategy(strategy)); reporterList.forEach(x -> x.flush(reportAggregates)); } /** * Ends logging, stops and clears the list of reporters */ protected void end() { flush(); reporterList.forEach(ExtentReporter::stop); reporterList.clear(); } /** * Add a system attribute * * @param sa a {@link SystemAttribute} object */ protected void setSystemInfo(SystemAttribute sa) { systemAttributeContext.setSystemAttribute(sa); } /** * Add a test runner log * * @param log a log event */ protected void setTestRunnerLogs(String log) { testRunnerLogs.add(log); } /** * Updates the {@link AnalysisStrategy} * * @param strategy a {@link AnalysisStrategy} object */ protected void setAnalysisStrategy(AnalysisStrategy strategy) { this.strategy = strategy; stats = new ReportStatusStats(strategy); } /** * Setting to allow user driven configuration for test time-stamps * * @param useManualConfig setting for manual configuration */ protected void setAllowManualConfig(boolean useManualConfig) { this.usesManualConfiguration = useManualConfig; } /** * Returns the current value of using manual configuration for test time-stamps * * @return setting for manual configuration */ protected boolean getAllowManualConfig() { return usesManualConfiguration; } /** * Return the {@link ReportStatusStats} object * * @return a {@link ReportStatusStats} object */ protected ReportStatusStats getStats() { return stats; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy