com.epam.ta.reportportal.util.analyzer.IssuesAnalyzerService.java Source code

Java tutorial

Introduction

Here is the source code for com.epam.ta.reportportal.util.analyzer.IssuesAnalyzerService.java

Source

/*
 * Copyright 2016 EPAM Systems
 * 
 * 
 * This file is part of EPAM Report Portal.
 * https://github.com/reportportal/service-api
 * 
 * Report Portal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Report Portal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
 * This file is part of Report Portal.
 *
 * Report Portal is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Report Portal is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Report Portal.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.epam.ta.reportportal.util.analyzer;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.epam.ta.reportportal.core.item.history.ITestItemsHistoryService;
import com.epam.ta.reportportal.core.statistics.StatisticsFacadeFactory;
import com.epam.ta.reportportal.database.dao.LaunchRepository;
import com.epam.ta.reportportal.database.dao.LogRepository;
import com.epam.ta.reportportal.database.dao.ProjectRepository;
import com.epam.ta.reportportal.database.dao.TestItemRepository;
import com.epam.ta.reportportal.database.entity.Launch;
import com.epam.ta.reportportal.database.entity.Log;
import com.epam.ta.reportportal.database.entity.Project;
import com.epam.ta.reportportal.database.entity.item.TestItem;
import com.epam.ta.reportportal.database.entity.item.issue.TestItemIssue;
import com.epam.ta.reportportal.database.entity.item.issue.TestItemIssueType;
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;

/**
 * Basic implementation of {@link IIssuesAnalyzer}
 *
 * @author Andrei_Ramanchuk
 *
 */
@Service("analyzerService")
public class IssuesAnalyzerService implements IIssuesAnalyzer {

    private static final Logger LOGGER = LoggerFactory.getLogger(IssuesAnalyzerService.class);
    private static final String MARKER = "AUTO-SYSTEM";
    private static final int MAXIMUM_SIZE = 10000;

    // Max time of cache item period
    private static final int CACHE_ITEM_LIVE = 1440;

    @Autowired
    private ProjectRepository projectRepository;

    @Autowired
    private LaunchRepository launchRepository;

    @Autowired
    private TestItemRepository testItemRepository;

    @Autowired
    private LogRepository logRepository;

    @Autowired
    private ITestItemsHistoryService historyServiceStrategy;

    @Autowired
    private StatisticsFacadeFactory statisticsFacadeFactory;

    @Value("${rp.issue.analyzer.rate}")
    private double acceptRate;

    /**
     * Cache for processing launches with Auto-Analyzer
     */
    private LoadingCache<String, String> processingIds;

    public IssuesAnalyzerService() {
        processingIds = CacheBuilder.newBuilder().maximumSize(MAXIMUM_SIZE)
                .expireAfterWrite(CACHE_ITEM_LIVE, TimeUnit.MINUTES).build(new CacheLoader<String, String>() {
                    @Override
                    public String load(String key) {
                        return "";
                    }
                });
    }

    @Override
    public List<TestItem> collectPreviousIssues(int depth, String launchId, String projectName) {
        processingIds.put(launchId, CacheElementEnum.STARTED.name());
        List<Launch> launchHistory = historyServiceStrategy.loadLaunches(depth, launchId, projectName, false);
        return launchHistory.stream()
                .flatMap(launch -> testItemRepository.findTestItemWithInvestigated(launch.getId()).stream())
                .collect(Collectors.toList());
    }

    @Override
    public void analyze(String launchId, List<TestItem> resources, List<TestItem> scope) {
        for (TestItem current : resources) {
            List<Double> curRate = Lists.newArrayList();
            TestItemIssue issue = null;
            boolean isInvestigated = false;
            List<Log> curItemErr = logRepository.findTestItemErrorLogs(current.getId());

            Launch launch = launchRepository.findOne(current.getLaunchRef());
            Project project = projectRepository.findOne(launch.getProjectRef());

            for (TestItem item : scope) {
                /*
                 * Avoid comparison with itself as investigated item during
                 * in_progress launch. Cause manually investigated item will be
                 * included in history of current one.
                 */
                if (item.getId().equalsIgnoreCase(current.getId()))
                    continue;

                List<Log> errors = logRepository.findTestItemErrorLogs(item.getId());
                if (errors.size() == curItemErr.size()) {
                    for (int i = 0; i < curItemErr.size(); i++) {
                        String curMsg = curItemErr.get(i).getLogMsg().replaceAll("\\d+", "")
                                .replaceAll("\\s(at)\\s", "");
                        String scopeMsg = errors.get(i).getLogMsg().replaceAll("\\d+", "").replaceAll("\\s(at)\\s",
                                "");
                        /*
                         * Get Levenshtein distance for two comparing log
                         * strings
                         */
                        int maxString = Math.max(curMsg.length(), scopeMsg.length());
                        int diff = StringUtils.getLevenshteinDistance(curMsg, scopeMsg);
                        /*
                         * Store percentage of equality
                         */
                        curRate.add(((double) (maxString - diff)) / maxString * 100);
                    }
                }
                if (!curRate.isEmpty() && (this.mathMiddle(curRate) >= acceptRate)) {
                    isInvestigated = true;
                    issue = item.getIssue();
                    /* Stop looping cause acceptable item found already. */
                    break;
                } else
                    curRate.clear();
            }

            if (isInvestigated) {
                TestItemIssue currentIssue = current.getIssue();
                /* If item was investigated till Launch finished. */
                if ((null != currentIssue.getExternalSystemIssues())
                        || (!currentIssue.getIssueType()
                                .equalsIgnoreCase(TestItemIssueType.TO_INVESTIGATE.getLocator()))
                        || (null != currentIssue.getIssueDescription())) {
                    currentIssue.setIssueDescription(
                            this.suggest(currentIssue.getIssueDescription(), issue, project.getConfiguration()));
                    current.setIssue(currentIssue);
                    testItemRepository.save(current);
                    /* If system investigate item from scratch */
                } else {
                    issue.setIssueDescription(this.mark(issue.getIssueDescription()));
                    current = statisticsFacadeFactory
                            .getStatisticsFacade(project.getConfiguration().getStatisticsCalculationStrategy())
                            .resetIssueStatistics(current);
                    current.setIssue(issue);
                    testItemRepository.save(current);
                    statisticsFacadeFactory
                            .getStatisticsFacade(project.getConfiguration().getStatisticsCalculationStrategy())
                            .updateIssueStatistics(current);
                }
            }
        }
        analyzeFinished(launchId);
    }

    @Override
    public boolean isPossible(String key) {
        String value = processingIds.getIfPresent(key);
        return Strings.isNullOrEmpty(value) || CacheElementEnum.FINISHED.name().equalsIgnoreCase(value);
    }

    @Override
    public void analyzeFinished(String key) {
        processingIds.put(key, CacheElementEnum.FINISHED.name());
        processingIds.invalidate(key);
    }

    @Override
    public boolean analyzeStarted(String key) {
        try {
            processingIds.put(key, CacheElementEnum.STARTED.name());
            return true;
        } catch (Exception ex) {
            LOGGER.error(ex.getMessage(), ex);
            return false;
        }
    }

    /*
     * Arithmetical Mean of list of double values
     */
    private double mathMiddle(List<Double> rates) {
        double sum = 0;
        for (double i : rates)
            sum += i;
        return sum / rates.size();
    }

    /*
     * Marker for issues which were auto-detected
     */
    private String mark(String description) {
        StringBuilder builder = new StringBuilder(MARKER);
        return null != description ? builder.append("\n\n").append(this.markerNullifier(description)).toString()
                : builder.toString();
    }

    /*
     * Add additional description for already investigated test-items during
     * launch running
     */
    private String suggest(String currentDescription, TestItemIssue previousIssue, Project.Configuration settings) {
        StringBuilder builder = new StringBuilder();
        if (null != currentDescription)
            builder.append(currentDescription);
        StringBuilder ticketIds = new StringBuilder();
        if (null != previousIssue.getExternalSystemIssues()) {
            for (TestItemIssue.ExternalSystemIssue externalSystemIssue : previousIssue.getExternalSystemIssues()) {
                ticketIds.append(externalSystemIssue.getTicketId()).append(",");
            }
            if (ticketIds.length() > 0)
                ticketIds.deleteCharAt(ticketIds.length() - 1);
            else
                ticketIds.append("");
        }
        builder.append("\n").append(MARKER).append("\n\n").append(" Similar issue has been found in history as:")
                .append("\n").append("*IssueType:* ")
                .append(settings.getByLocator(previousIssue.getIssueType()).getLongName()).append("\n")
                .append("*TicketIds:* ").append(ticketIds.toString()).append("\n");

        return null != previousIssue.getIssueDescription() ? builder.append("*IssueDescription:* ").append("\n")
                .append(previousIssue.getIssueDescription()).toString() : builder.toString();
    }

    /*
     * Remove all possible previous ${MARKER} fields from description even after
     * user manual changes
     */
    private String markerNullifier(String description) {
        // Dirty markers remover
        description = description.replaceAll(MARKER.concat("\n\n"), "");
        description = description.replaceAll(MARKER.concat("\n"), "");
        description = description.replaceAll(MARKER, "");
        return description;
    }

    public void setProcessingIds(LoadingCache<String, String> cache) {
        this.processingIds = cache;
    }

    public LoadingCache<String, String> getProcessIds() {
        return processingIds;
    }
}