com.solutions.it.exemplar.KarmaJunitReporterJsTestDriverSensor.java Source code

Java tutorial

Introduction

Here is the source code for com.solutions.it.exemplar.KarmaJunitReporterJsTestDriverSensor.java

Source

/*
 * SonarQube Karma Test Reporting Plugin
 * Copyright (C) 2015 Exemplar IT Solutions LLC and Anthony Watson
 *
 * This file is part of SonarQube Karma Test Reporting Plugin.
 * SonarQube Karma Test Reporting Plugin 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.
 *
 * SonarQube Karma Test Reporting Plugin 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 SonarQube Karma Test Reporting Plugin.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.solutions.it.exemplar;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.Extension;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.config.Settings;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.plugins.javascript.JavaScriptLanguage;
import org.sonar.plugins.javascript.unittest.surefireparser.AbstractSurefireParser;

/**
 * SonarQube sensor that can register JavaScript unit test "spec" file names
 * based on the xml report generated by the karma-junit-reporter
 * so that the unit test report data can be shown in the dashboard.
 */
public class KarmaJunitReporterJsTestDriverSensor implements Sensor, Extension {

    private static final Logger LOG = LoggerFactory.getLogger(KarmaJunitReporterJsTestDriverSensor.class);
    private static final String JS_EXTENSION = ".js";

    protected FileSystem fileSystem;
    protected Settings settings;
    private final FilePredicate mainFilePredicate;
    private final FilePredicate testFilePredicate;
    private SuiteUtil suiteUtil;

    public KarmaJunitReporterJsTestDriverSensor(final FileSystem fileSystem, final Settings settings) {
        this.fileSystem = fileSystem;
        this.settings = settings;
        this.mainFilePredicate = fileSystem.predicates().and(fileSystem.predicates().hasType(InputFile.Type.MAIN),
                fileSystem.predicates().hasLanguage(JavaScriptLanguage.KEY));

        this.testFilePredicate = fileSystem.predicates().and(fileSystem.predicates().hasType(InputFile.Type.TEST),
                fileSystem.predicates().hasLanguage(JavaScriptLanguage.KEY));

        this.suiteUtil = new SuiteUtil();
    }

    @Override
    public boolean shouldExecuteOnProject(final Project project) {

        boolean isConfiguredToRun = StringUtils.isNotBlank(getReportsDirectoryPath());

        if (!isConfiguredToRun) {
            LOG.debug("{} will not execute since \"{}\" configuration was not found.",
                    KarmaJunitReporterJsTestDriverSensor.class.getSimpleName(),
                    KarmaJunitReportPlugin.REPORTS_PATH);
        }

        return isConfiguredToRun && fileSystem.hasFiles(mainFilePredicate);
    }

    @Override
    public void analyse(final Project project, final SensorContext context) {
        collect(context, getIOFile(getReportsDirectoryPath()));
    }

    protected void collect(final SensorContext context, final File reportsDir) {
        LOG.info("Parsing Unit Test run results in Surefire format from folder {}", reportsDir);

        final Map<String, String> suiteToFile = getTestSuiteToFileNameMapping();

        if (suiteToFile == null || suiteToFile.isEmpty()) {
            LOG.debug("No suite to file mappings could be determined.");
            return;
        }

        new AbstractSurefireParser() {

            @Override
            protected Resource getUnitTestResource(final String classKey) {

                String simpleClassName = null;

                if (classKey.endsWith(JS_EXTENSION)) {
                    simpleClassName = classKey;
                } else {
                    simpleClassName = getSimpleSureFireClassName(classKey);
                }

                if (simpleClassName == null || simpleClassName.length() == 0
                        || !suiteToFile.containsKey(simpleClassName)) {
                    LOG.warn(
                            "Test result will not be saved for test class \"{}\", because SonarQube associated resource has not been found in suite-to-file map using key \"{}\".",
                            classKey, simpleClassName);
                    return null;
                }

                InputFile inputFile = getTestFileRelativePathToBaseDir(suiteToFile.get(simpleClassName));

                if (inputFile != null) {
                    return context.getResource(inputFile);

                } else {
                    // Sonar resource not found from test file
                    LOG.warn(
                            "Test result will not be saved for test class \"{}\", because SonarQube associated resource has not been found using file name: \"{}\"",
                            classKey, suiteToFile.get(simpleClassName));
                    return null;
                }

            }
        }.collect(context, reportsDir);
    }

    private Map<String, String> getTestSuiteToFileNameMapping() {

        Map<String, String> keyToFile = new HashMap<String, String>();
        FilePredicate predicate = fileSystem.predicates().and(testFilePredicate);

        String testDirPrefix = null;
        if (StringUtils.isNotEmpty(settings.getString("sonar.tests"))) {
            testDirPrefix = new StringBuilder().append(settings.getString("sonar.tests")).append(File.separatorChar)
                    .toString();
        }
        LOG.debug("test directory prefix is " + testDirPrefix);

        Iterator<InputFile> fileIterator = fileSystem.inputFiles(predicate).iterator();
        while (fileIterator.hasNext()) {
            InputFile inputFile = fileIterator.next();
            LOG.debug("Attempting to determine suite name from \"{}\"", inputFile.relativePath());

            String relativePathKey = inputFile.relativePath();
            if (testDirPrefix != null && relativePathKey.startsWith(testDirPrefix)) {
                relativePathKey = relativePathKey.substring(testDirPrefix.length());
                LOG.debug("Removed \"{}\" test directory prefix from relativePathKey", testDirPrefix);
            }
            keyToFile.put(relativePathKey, inputFile.relativePath());
            LOG.debug("Found test file: \"{}\" with relativePathKey \"{}\"", inputFile.relativePath(),
                    relativePathKey);

            String suiteName = suiteUtil.getSuiteName(inputFile.file());
            if (suiteName != null && suiteName.length() > 0) {
                keyToFile.put(suiteName, inputFile.relativePath());
                LOG.debug("Found test file: \"{}\" with suite name \"{}\"", inputFile.relativePath(), suiteName);
            } else {
                LOG.warn("Suite name could not be determined for \"{}\"", inputFile.relativePath());
            }
        }

        return keyToFile;
    }

    /**
     * For a SureFire class name like "PhantomJS_1_9_8_(Mac_OS_X_0_0_0).myFile", this method will strip off
     * the first qualifier "PhantomJS_1_9_8_(Mac_OS_X_0_0_0)" if found and return "myFile".
     * @param className the SureFire class name
     * @return the SureFire class name
     */
    private String getSimpleSureFireClassName(final String className) {

        int dotIndex = className.indexOf(".");

        if (dotIndex < 0) {
            return className;
        }

        return className.substring(dotIndex + 1);
    }

    protected InputFile getTestFileRelativePathToBaseDir(final String fileName) {
        FilePredicate predicate = fileSystem.predicates().and(testFilePredicate,
                fileSystem.predicates().matchesPathPattern("**" + File.separatorChar + fileName));

        Iterator<InputFile> fileIterator = fileSystem.inputFiles(predicate).iterator();
        if (fileIterator.hasNext()) {
            InputFile inputFile = fileIterator.next();
            LOG.debug("Found potential test file corresponding to file name: {}", fileName);
            LOG.debug(
                    "Will fetch SonarQube associated resource with (logical) relative path to project base directory: {}",
                    inputFile.relativePath());
            return inputFile;
        }
        return null;
    }

    /**
     * Returns a java.io.File for the given path.
     * If path is not absolute, returns a File with project base directory as parent path.
     */
    protected File getIOFile(final String path) {
        File file = new File(path);
        if (!file.isAbsolute()) {
            file = new File(fileSystem.baseDir(), path);
        }

        return file;
    }

    protected String getReportsDirectoryPath() {
        return settings.getString(KarmaJunitReportPlugin.REPORTS_PATH);
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }
}