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

com.aventstack.extentreports.reporter.ExtentXReporter Maven / Gradle / Ivy

package com.aventstack.extentreports.reporter;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.bson.Document;
import org.bson.types.ObjectId;

import com.aventstack.extentreports.configuration.Config;
import com.aventstack.extentreports.configuration.ConfigMap;
import com.aventstack.extentreports.mediastorage.MediaStorage;
import com.aventstack.extentreports.mediastorage.MediaStorageManagerFactory;
import com.aventstack.extentreports.model.Author;
import com.aventstack.extentreports.model.Category;
import com.aventstack.extentreports.model.ExceptionInfo;
import com.aventstack.extentreports.model.Log;
import com.aventstack.extentreports.model.Media;
import com.aventstack.extentreports.model.ScreenCapture;
import com.aventstack.extentreports.model.Screencast;
import com.aventstack.extentreports.model.Test;
import com.aventstack.extentreports.reporter.configuration.ExtentXReporterConfiguration;
import com.aventstack.extentreports.utils.MongoUtil;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;

/**
 * ExtentXReporter is a NoSQL database reporter (MongoDB by default), which updates information in
 * the database which is then used by the ExtentX server to display in-depth analysis. 
 */
public class ExtentXReporter extends AbstractReporter implements ReportAppendable {

    private static final String DEFAULT_CONFIG_FILE = "extentx-config.properties";
    private static final String DEFAULT_PROJECT_NAME = "Default";
    
    private Boolean appendExisting;
    private String url;
    
    private Map categoryNameObjectIdCollection;
    private Map exceptionNameObjectIdCollection;
    
    private ObjectId reportId;
    private ObjectId projectId;
    
    private MongoClient mongoClient;
    
    private MongoDatabase db;
    
    private MongoCollection projectCollection;
    private MongoCollection reportCollection;
    private MongoCollection testCollection;
    private MongoCollection logCollection;
    private MongoCollection exceptionCollection;
    private MongoCollection mediaCollection;
    private MongoCollection categoryCollection;
    private MongoCollection authorCollection;
    private MongoCollection categoryTestsTestCategories;
    private MongoCollection authorTestsTestAuthors;
    
    private MediaStorage media;
    private ExtentXReporterConfiguration userConfig;
    
    static {
        /* use mongodb reporting for only critical/severe events */
        Logger mongoLogger = Logger.getLogger("org.mongodb.driver");
        mongoLogger.setLevel(Level.SEVERE);
    }

    ExtentXReporter() {
        loadDefaultConfig();
    }
    
    public ExtentXReporter(String host) {
        this();
        mongoClient = new MongoClient(host, 27017);
    }
    
    public ExtentXReporter(String host,  MongoClientOptions options) {
        this();
        mongoClient = new MongoClient(host, options);
    }
    
    public ExtentXReporter(String host, int port) {
        this();
        mongoClient = new MongoClient(host, port);
    }
    
    public ExtentXReporter(MongoClientURI uri) {
        this();
        mongoClient = new MongoClient(uri);        
    }
    
    public ExtentXReporter(ServerAddress addr) {
        this();
        mongoClient = new MongoClient(addr);
    }
    
    public ExtentXReporter(List seeds) {
        this();
        mongoClient = new MongoClient(seeds);
    }
    
    public ExtentXReporter(List seeds, List credentialsList) {
        this();
        mongoClient = new MongoClient(seeds, credentialsList);
    }
    
    public ExtentXReporter(List seeds, List credentialsList, MongoClientOptions options) {
        this();
        mongoClient = new MongoClient(seeds, credentialsList, options);
    }
    
    public ExtentXReporter(List seeds, MongoClientOptions options) {
        this();
        mongoClient = new MongoClient(seeds, options);
    }
    
    public ExtentXReporter(ServerAddress addr, List credentialsList) {
        this();
        mongoClient = new MongoClient(addr, credentialsList);
    }
    
    public ExtentXReporter(ServerAddress addr, List credentialsList, MongoClientOptions options) {
        this();
        mongoClient = new MongoClient(addr, credentialsList, options);
    }
    
    public ExtentXReporter(ServerAddress addr, MongoClientOptions options) {
        this();
        mongoClient = new MongoClient(addr, options);
    }
    
    public ExtentXReporterConfiguration config() {       
        return userConfig;
    }
    
    @Override
    public void start() {
        loadUserConfig();
        
        db = mongoClient.getDatabase("extent");
        
        // collections
        projectCollection = db.getCollection("project");
        reportCollection = db.getCollection("report");
        testCollection = db.getCollection("test");
        logCollection = db.getCollection("log");
        exceptionCollection = db.getCollection("exception");
        mediaCollection = db.getCollection("media");
        categoryCollection = db.getCollection("category");
        authorCollection = db.getCollection("author");

        // many-to-many
        categoryTestsTestCategories = db.getCollection("category_tests__test_categories");
        authorTestsTestAuthors = db.getCollection("author_tests__test_authors");
        
        setupProject();
    }
    
    private void loadDefaultConfig() {
        configContext = new ConfigMap();
        userConfig = new ExtentXReporterConfiguration();
        
        ClassLoader loader = getClass().getClassLoader();
        InputStream is = loader.getResourceAsStream(DEFAULT_CONFIG_FILE);
        loadConfig(is);
    }
    
    private void loadUserConfig() {
        userConfig.getConfigMap().forEach(
            (k, v) -> {
                if (v != null) {
                    Config c = new Config();
                    c.setKey(k);
                    c.setValue(v);
                    
                    configContext.setConfig(c); 
                }
            }
        );
    }

    private void setupProject() {
        String projectName = configContext.getValue("projectName").toString().trim();
        if (projectName == null || projectName.isEmpty())
            projectName = DEFAULT_PROJECT_NAME;

        Document doc = new Document("name", projectName);
        Document project = projectCollection.find(doc).first();
        
        if (project != null) {
            projectId = project.getObjectId("_id");
        }
        else {
            projectCollection.insertOne(doc);
            projectId = MongoUtil.getId(doc);
        }
        
        setupReport(projectName);
    }
    
    private void setupReport(String projectName) {
        String reportName = configContext.getValue("reportName").toString().trim();
        if (reportName == null || reportName.isEmpty())
            reportName = projectName + " - " + Calendar.getInstance().getTimeInMillis();
        
        Object id = configContext.getValue("reportId");
        if (id != null && !id.toString().isEmpty() && appendExisting) {
            FindIterable iterable = reportCollection.find(new Document("_id", new ObjectId(id.toString())));
            Document report = iterable.first();
            
            if (report != null) {
                reportId = report.getObjectId("_id");
                return;
            }
        }
        
        Document doc = new Document("name", reportName)
                .append("startTime", getStartTime())
                .append("project", projectId);

        reportCollection.insertOne(doc);
        reportId = MongoUtil.getId(doc);
    }
    
    @Override
    public void stop() {
        mongoClient.close();
    }

    @Override
    public synchronized void flush() {
        setEndTime(Calendar.getInstance().getTime());
        
        if (testList == null || testList.size() == 0)
            return;
        
        Document doc = new Document("endTime", getEndTime())
                        .append("duration", getRunDuration())
                        .append("parentLength", sc.getParentCount())
                        .append("passParentLength", sc.getParentCountPass())
                        .append("failParentLength", sc.getParentCountFail())
                        .append("fatalParentLength", sc.getParentCountFatal())
                        .append("errorParentLength", sc.getParentCountError())
                        .append("warningParentLength", sc.getParentCountWarning())
                        .append("skipParentLength", sc.getParentCountSkip())
                        .append("exceptionsParentLength", sc.getChildCountExceptions())
                        
                        .append("childLength", sc.getChildCount())
                        .append("passChildLength", sc.getChildCountPass())
                        .append("failChildLength", sc.getChildCountFail())
                        .append("fatalChildLength", sc.getChildCountFatal())
                        .append("errorChildLength", sc.getChildCountError())
                        .append("warningChildLength", sc.getChildCountWarning())
                        .append("skipChildLength", sc.getChildCountSkip())
                        .append("infoChildLength", sc.getChildCountInfo())
                        .append("exceptionsChildLength", sc.getChildCountExceptions())
                        
                        .append("grandChildLength", sc.getGrandChildCount())
                        .append("passGrandChildLength", sc.getGrandChildCountPass())
                        .append("failGrandChildLength", sc.getGrandChildCountFail())
                        .append("fatalGrandChildLength", sc.getGrandChildCountFatal())
                        .append("errorGrandChildLength", sc.getGrandChildCountError())
                        .append("warningGrandChildLength", sc.getGrandChildCountWarning())
                        .append("skipGrandChildLength", sc.getGrandChildCountSkip())
                        .append("exceptionsGrandChildLength", sc.getGrandChildCountExceptions());
        
        reportCollection.updateOne(
                new Document("_id", reportId), 
                new Document("$set", doc));
    }
    
    @Override
    public void onTestStarted(Test test) {
        Document doc = new Document("project", projectId)
                .append("report", reportId)
                .append("level", test.getLevel())
                .append("name", test.getName())
                .append("status", test.getStatus().toString())
                .append("description", test.getDescription())
                .append("startTime", test.getStartTime())
                .append("endTime", test.getEndTime())
                .append("bdd", test.isBehaviorDrivenType())
                .append("childNodesLength", test.getNodeContext().size());
        
        if (test.isBehaviorDrivenType())
            doc.append("bddType", test.getBehaviorDrivenType().getSimpleName());
        
        testCollection.insertOne(doc);
    
        ObjectId testId = MongoUtil.getId(doc);
        test.setObjectId(testId);
    }
    
    @Override
    public synchronized void onNodeStarted(Test node) {
        Document doc = new Document("parent", node.getParent().getObjectId())
                .append("parentName", node.getParent().getName())
                .append("project", projectId)
                .append("report", reportId)
                .append("level", node.getLevel())
                .append("name", node.getName())
                .append("status", node.getStatus().toString())
                .append("description", node.getDescription())
                .append("startTime", node.getStartTime())
                .append("endTime", node.getEndTime())
                .append("childNodesCount", node.getNodeContext().getAll().size())
                .append("bdd", node.isBehaviorDrivenType())
                .append("childNodesLength", node.getNodeContext().size());

        if (node.isBehaviorDrivenType())
            doc.append("bddType", node.getBehaviorDrivenType().getSimpleName());
        
        testCollection.insertOne(doc);
        
        ObjectId nodeId = MongoUtil.getId(doc);
        node.setObjectId(nodeId);
        
        // update parent test stats
        updateTestBasedOnNode(node.getParent());
    }
    
    private void updateTestBasedOnNode(Test test) {
        Document doc = new Document("childNodesLength", test.getNodeContext().size());
        
        testCollection.updateOne(
                new Document("_id", test.getObjectId()),
                new Document("$set", doc));
    }

    @Override
    public synchronized void onLogAdded(Test test, Log log) {
        Document doc = new Document("test", test.getObjectId())
                .append("project", projectId)
                .append("report", reportId)
                .append("testName", test.getName())
                .append("sequence", log.getSequence())
                .append("status", log.getStatus().toString())
                .append("timestamp", log.getTimestamp())
                .append("details", log.getDetails());

        logCollection.insertOne(doc);
        
        if (test.hasException()) {
            if (exceptionNameObjectIdCollection == null)
                exceptionNameObjectIdCollection = new HashMap<>();
            
            ExceptionInfo ex = test.getExceptionInfoList().get(0);
            
            ObjectId exceptionId;
            doc = new Document("report", reportId)
                    .append("project", projectId)
                    .append("name", ex.getExceptionName());
                    
            FindIterable iterable = exceptionCollection.find(doc);
            Document docException = iterable.first();
            
            if (!exceptionNameObjectIdCollection.containsKey(ex.getExceptionName())) {               
                if (docException != null) {
                    exceptionNameObjectIdCollection.put(ex.getExceptionName(), docException.getObjectId("_id"));
                } else {
                    doc = new Document("project", projectId)
                            .append("report", reportId)
                            .append("name", ex.getExceptionName())
                            .append("stacktrace", ex.getStackTrace())
                            .append("testCount", 0);
                    
                    exceptionCollection.insertOne(doc);
                    
                    exceptionId = MongoUtil.getId(doc);
                    docException = exceptionCollection.find(new Document("_id", exceptionId)).first();
                    
                    exceptionNameObjectIdCollection.put(ex.getExceptionName(), exceptionId);
                }
            }

            Integer testCount = ((Integer) (docException.get("testCount"))) + 1;
            doc = new Document("testCount", testCount);
            
            exceptionCollection.updateOne(
                    new Document("_id", docException.getObjectId("_id")),
                    new Document("$set", doc));
            
            doc = new Document("exception", exceptionNameObjectIdCollection.get(ex.getExceptionName()));
            
            testCollection.updateOne(
                    new Document("_id", test.getObjectId()),
                    new Document("$set", doc));
        }
        
        endTestRecursive(test);
    }
    
    private void endTestRecursive(Test test) {
        Document doc = new Document("status", test.getStatus().toString())
                .append("endTime", test.getEndTime())
                .append("duration", test.getRunDurationMillis())
                .append("categorized", test.hasCategory());
        
        testCollection.updateOne(
                new Document("_id", test.getObjectId()),
                new Document("$set", doc));

        if (test.getLevel() > 0)
            endTestRecursive(test.getParent());
    }

    @Override
    public void onCategoryAssigned(Test test, Category category) {
        if (categoryNameObjectIdCollection == null)
            categoryNameObjectIdCollection = new HashMap<>();
        
        Document doc;
        
        if (!categoryNameObjectIdCollection.containsKey(category.getName())) {
            doc = new Document("report", reportId)
                    .append("project", projectId)
                    .append("name", category.getName());
                    
            FindIterable iterable = categoryCollection.find(doc);
            Document docCategory = iterable.first();
            
            if (docCategory != null) {
                categoryNameObjectIdCollection.put(category.getName(), docCategory.getObjectId("_id"));
            } else {
                doc = new Document("tests", test.getObjectId())
                        .append("project", projectId)
                        .append("report", reportId)
                        .append("name", category.getName())
                        .append("status", test.getStatus().toString())
                        .append("testName", test.getName());
                
                categoryCollection.insertOne(doc);
                
                ObjectId categoryId = MongoUtil.getId(doc);
                
                categoryNameObjectIdCollection.put(category.getName(), categoryId);
            }
        }
        
        /* create association with category
         * tests (many) <-> categories (many)
         * tests and categories have a many to many relationship
         *   - a test can be assigned with one or more categories
         *   - a category can have one or more tests
         */
        doc = new Document("test_categories", test.getObjectId())
                .append("category_tests", categoryNameObjectIdCollection.get(category.getName()))
                .append("category", category.getName())
                .append("test", test.getName());
        
        categoryTestsTestCategories.insertOne(doc);
    }

    @Override
    public void onAuthorAssigned(Test test, Author author) { 
        Document doc = new Document("tests", test.getObjectId())
                .append("project", projectId)
                .append("report", reportId)
                .append("name", author.getName())
                .append("status", test.getStatus().toString())
                .append("testName", test.getName());

        authorCollection.insertOne(doc);
        
        ObjectId authorId = MongoUtil.getId(doc);
        
        doc = new Document("test_authors", test.getObjectId())
                .append("author_tests", authorId)
                .append("author", author.getName())
                .append("test", test.getName());
        
        authorTestsTestAuthors.insertOne(doc);
    }
    
    @Override
    public void onScreenCaptureAdded(Test test, ScreenCapture screenCapture) throws IOException {
        storeUrl();
        screenCapture.setReportObjectId(reportId);
        
        createMedia(test, screenCapture);
        
        initMedia();
        
        media.storeMedia(screenCapture);
    }
    
    private void storeUrl() throws IOException {
        if (this.url == null) {
            Object url = configContext.getValue("serverUrl");
            
            if (url == null) {
                throw new IOException("server url cannot be null, use extentx.config().setServerUrl(url)");
            }
            
            this.url = url.toString().trim();
        }
    }
    
    private void createMedia(Test test, Media media) {
        Document doc = new Document("test", test.getObjectId())
                .append("project", projectId)
                .append("report", reportId)
                .append("testName", test.getName())
                .append("sequence", media.getSequence())
                .append("mediaType", media.getMediaType().toString().toLowerCase());

        mediaCollection.insertOne(doc);
        
        ObjectId mediaId = MongoUtil.getId(doc);
        media.setObjectId(mediaId);
    }
    
    private void initMedia() throws IOException {
        if (media == null) {
            media = new MediaStorageManagerFactory().getManager("http");
            media.init(url);
        }
    }
    
    @Override
    public void onScreencastAdded(Test test, Screencast screencast) throws IOException {
        storeUrl();
        screencast.setReportObjectId(reportId);
        
        createMedia(test, screencast);
        
        initMedia();
        
        media.storeMedia(screencast);
    }
    
    @Override
    public void setTestList(List reportTestList) {
        testList = reportTestList;
    }
    
    public List getTestList() {
        if (testList == null)
            testList = new ArrayList<>();
        
        return testList;
    }

	@Override
	public void setAppendExisting(Boolean b) {
		this.appendExisting = b;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy