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

com.solutions.it.exemplar.KarmaJunitReporterJsTestDriverSensor Maven / Gradle / Ivy

/*
 * 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 .
 */
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 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 getTestSuiteToFileNameMapping() {

    Map keyToFile = new HashMap();
    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 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 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();
  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy