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

org.sonar.plugins.javascript.lcov.LCOVParser Maven / Gradle / Ivy

The newest version!
/*
 * SonarQube JavaScript Plugin
 * Copyright (C) 2011 SonarSource and Eriks Nukis
 * [email protected]
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.plugins.javascript.lcov;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.io.FileUtils;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.measures.CoverageMeasuresBuilder;

import javax.annotation.CheckForNull;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
 */
public final class LCOVParser {

  private static final String SF = "SF:";
  private static final String DA = "DA:";
  private static final String BRDA = "BRDA:";

  private final Map coverageByFile;
  private final FileSystem fs;
  private final List unresolvedPaths = Lists.newArrayList();

  private LCOVParser(FileSystem fs, List lines) {
    this.fs = fs;
    this.coverageByFile = parse(lines);
  }

  public static Map parse(FileSystem fs, List lines) {
    return new LCOVParser(fs, lines).coverageByFile();
  }

  public static LCOVParser create(FileSystem fs, File file) {
    final List lines;
    try {
      lines = FileUtils.readLines(file);
    } catch (IOException e) {
      throw new IllegalArgumentException("Could not read content from file: " + file, e);
    }
    return new LCOVParser(fs, lines);
  }

  public Map coverageByFile() {
    return coverageByFile;
  }

  public List unresolvedPaths() {
    return unresolvedPaths;
  }

  private Map parse(List lines) {
    final Map files = Maps.newHashMap();
    FileData fileData = null;

    for (String line : lines) {
      if (line.startsWith(SF)) {
        // SF:
        fileData = loadCurrentFileData(files, line);
      } else if (fileData != null) {
        if (line.startsWith(DA)) {
          // DA:,[,]
          String execution = line.substring(DA.length());
          String executionCount = execution.substring(execution.indexOf(',') + 1);
          String lineNumber = execution.substring(0, execution.indexOf(','));

          fileData.addLine(Integer.valueOf(lineNumber), Integer.valueOf(executionCount));
        } else if (line.startsWith(BRDA)) {
          // BRDA:,,,
          String[] tokens = line.substring(BRDA.length()).trim().split(",");
          String lineNumber = tokens[0];
          String branchNumber = tokens[1] + tokens[2];
          String taken = tokens[3];

          fileData.addBranch(Integer.valueOf(lineNumber), branchNumber, "-".equals(taken) ? 0 : Integer.valueOf(taken));
        }
      }
    }

    Map coveredFiles = Maps.newHashMap();
    for (Map.Entry e : files.entrySet()) {
      coveredFiles.put(e.getKey(), e.getValue().convert());
    }
    return coveredFiles;
  }

  @CheckForNull
  private FileData loadCurrentFileData(final Map files, String line) {
    String filePath = line.substring(SF.length());
    FileData fileData = null;
    // some tools (like Istanbul, Karma) provide relative paths, so let's consider them relative to project directory
    InputFile inputFile = fs.inputFile(fs.predicates().hasPath(filePath));
    if (inputFile != null) {
      fileData = files.get(inputFile);
      if (fileData == null) {
        fileData = new FileData();
        files.put(inputFile, fileData);
      }
    } else {
      unresolvedPaths.add(filePath);
    }
    return fileData;
  }

  private static class FileData {
    /**
     * line number -> branch number -> taken
     */
    private Map> branches = Maps.newHashMap();

    /**
     * line number -> execution count
     */
    private Map hits = Maps.newHashMap();

    public void addBranch(Integer lineNumber, String branchNumber, Integer taken) {
      Map branchesForLine = branches.get(lineNumber);
      if (branchesForLine == null) {
        branchesForLine = Maps.newHashMap();
        branches.put(lineNumber, branchesForLine);
      }
      Integer currentValue = branchesForLine.get(branchNumber);
      branchesForLine.put(branchNumber, Objects.firstNonNull(currentValue, 0) + taken);
    }

    public void addLine(Integer lineNumber, Integer executionCount) {
      Integer currentValue = hits.get(lineNumber);
      hits.put(lineNumber, Objects.firstNonNull(currentValue, 0) + executionCount);
    }

    public CoverageMeasuresBuilder convert() {
      CoverageMeasuresBuilder result = CoverageMeasuresBuilder.create();
      for (Map.Entry e : hits.entrySet()) {
        result.setHits(e.getKey(), e.getValue());
      }
      for (Map.Entry> e : branches.entrySet()) {
        int conditions = e.getValue().size();
        int covered = 0;
        for (Integer taken : e.getValue().values()) {
          if (taken > 0) {
            covered++;
          }
        }
        result.setConditions(e.getKey(), conditions, covered);
      }
      return result;
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy