/* * SonarQube Java * Copyright (C) 2010-2016 SonarSource SA * mailto:contact AT sonarsource DOT com * * 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 02110-1301, USA. */ package org.sonar.plugins.jacoco; import com.google.common.base.Preconditions; import org.jacoco.core.analysis.Analyzer; import org.jacoco.core.analysis.CoverageBuilder; import org.jacoco.core.data.ExecutionDataReader; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.data.ExecutionDataWriter; import org.jacoco.core.data.IExecutionDataVisitor; import org.jacoco.core.data.ISessionInfoVisitor; import org.sonar.squidbridge.api.AnalysisException; import javax.annotation.Nullable; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; public class JacocoReportReader { @Nullable private final File jacocoExecutionData; private final boolean useCurrentBinaryFormat; public JacocoReportReader(@Nullable File jacocoExecutionData) { this.jacocoExecutionData = jacocoExecutionData; this.useCurrentBinaryFormat = isCurrentReportFormat(jacocoExecutionData); } /** * Read JaCoCo report determining the format to be used. * @param executionDataVisitor visitor to store execution data. * @param sessionInfoStore visitor to store info session. * @return true if binary format is the latest one. * @throws IOException in case of error or binary format not supported. */ public JacocoReportReader readJacocoReport(IExecutionDataVisitor executionDataVisitor, ISessionInfoVisitor sessionInfoStore) { if (jacocoExecutionData == null) { return this; } JaCoCoExtensions.LOG.info("Analysing {}", jacocoExecutionData); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(jacocoExecutionData))) { if (useCurrentBinaryFormat) { ExecutionDataReader reader = new ExecutionDataReader(inputStream); reader.setSessionInfoVisitor(sessionInfoStore); reader.setExecutionDataVisitor(executionDataVisitor); reader.read(); } else { org.jacoco.previous.core.data.ExecutionDataReader reader = new org.jacoco.previous.core.data.ExecutionDataReader(inputStream); reader.setSessionInfoVisitor(sessionInfoStore); reader.setExecutionDataVisitor(executionDataVisitor); reader.read(); } } catch (IOException e) { throw new AnalysisException(String.format("Unable to read %s", jacocoExecutionData.getAbsolutePath()), e); } return this; } private static boolean isCurrentReportFormat(@Nullable File jacocoExecutionData) { if (jacocoExecutionData == null) { return true; } try (DataInputStream dis = new DataInputStream(new FileInputStream(jacocoExecutionData))) { byte firstByte = dis.readByte(); Preconditions.checkState(firstByte == ExecutionDataWriter.BLOCK_HEADER); Preconditions.checkState(dis.readChar() == ExecutionDataWriter.MAGIC_NUMBER); char version = dis.readChar(); boolean isCurrentFormat = version == ExecutionDataWriter.FORMAT_VERSION; if (!isCurrentFormat) { JaCoCoExtensions.LOG.warn("You are not using the latest JaCoCo binary format version, please consider upgrading to latest JaCoCo version."); } return isCurrentFormat; } catch (IOException | IllegalStateException e) { throw new AnalysisException(String.format("Unable to read %s to determine JaCoCo binary format.", jacocoExecutionData.getAbsolutePath()), e); } } public boolean useCurrentBinaryFormat() { return this.useCurrentBinaryFormat; } /** * Caller must guarantee that {@code classFiles} are actually class file. */ public CoverageBuilder analyzeFiles(ExecutionDataStore executionDataStore, Collection<File> classFiles) { CoverageBuilder coverageBuilder = new CoverageBuilder(); if (useCurrentBinaryFormat) { Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder); for (File classFile : classFiles) { analyzeClassFile(analyzer, classFile); } } else { org.jacoco.previous.core.analysis.Analyzer analyzer = new org.jacoco.previous.core.analysis.Analyzer(executionDataStore, coverageBuilder); for (File classFile : classFiles) { analyzeClassFile(analyzer, classFile); } } return coverageBuilder; } /** * Caller must guarantee that {@code classFile} is actually class file. */ private static void analyzeClassFile(org.jacoco.previous.core.analysis.Analyzer analyzer, File classFile) { try (InputStream inputStream = new FileInputStream(classFile)) { analyzer.analyzeClass(inputStream, classFile.getPath()); } catch (IOException e) { // (Godin): in fact JaCoCo includes name into exception JaCoCoExtensions.LOG.warn("Exception during analysis of file " + classFile.getAbsolutePath(), e); } } private static void analyzeClassFile(Analyzer analyzer, File classFile) { try (InputStream inputStream = new FileInputStream(classFile)) { analyzer.analyzeClass(inputStream, classFile.getPath()); } catch (IOException e) { // (Godin): in fact JaCoCo includes name into exception JaCoCoExtensions.LOG.warn("Exception during analysis of file " + classFile.getAbsolutePath(), e); } } }