Skip to content

Commit

Permalink
Merge pull request #17 from kohsuke/master
Browse files Browse the repository at this point in the history
JENKINS-15539 serve jacoco.exec over HTTP
  • Loading branch information
ognjenb committed Feb 23, 2013
2 parents 009c766 + 45e4e6e commit 1c8657a
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 132 deletions.
79 changes: 12 additions & 67 deletions src/main/java/hudson/plugins/jacoco/JacocoBuildAction.java
Expand Up @@ -10,7 +10,6 @@
import hudson.plugins.jacoco.model.CoverageElement;
import hudson.plugins.jacoco.model.CoverageElement.Type;
import hudson.plugins.jacoco.model.CoverageObject;
import hudson.plugins.jacoco.report.ClassReport;
import hudson.plugins.jacoco.report.CoverageReport;

import java.io.File;
Expand All @@ -22,10 +21,10 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.data.ExecFileLoader;
import org.jvnet.localizer.Localizable;
import org.kohsuke.stapler.StaplerProxy;

Expand Down Expand Up @@ -198,62 +197,9 @@ public AbstractBuild<?,?> getBuild() {
return owner;
}


protected ExecutionFileLoader getJacocoReports(File file, String[] includes, String[] excludes) throws IOException {
ExecutionFileLoader efl = null;
try {
FilePath path = new FilePath(file);
FilePath pathToExecFiles = new FilePath(path, "execFiles");

efl = new ExecutionFileLoader();

int i=0;
FilePath checkPath=null;
while((checkPath=new FilePath(pathToExecFiles ,"exec"+i)).exists()) {
efl.addExecFile(new FilePath(checkPath, "jacoco.exec"));

i++;
}
efl.setIncludes(includes);
efl.setExcludes(excludes);
efl.setClassDir(new FilePath(path, "classes"));
efl.setSrcDir(new FilePath(path, "sources"));
efl.loadBundleCoverage();

} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return efl;
}

protected static ExecutionFileLoader getJacocoReports(FilePath fp, String[] includes, String[] excludes) throws IOException {
ExecutionFileLoader efl = null;
try {
FilePath path = fp;
FilePath pathToExecFiles = new FilePath(path, "execFiles");

efl = new ExecutionFileLoader();

int i=0;
FilePath checkPath=null;
while((checkPath=new FilePath(pathToExecFiles ,"exec"+i)).exists()) {
efl.addExecFile(new FilePath(checkPath, "jacoco.exec"));

i++;
}
efl.setIncludes(includes);
efl.setExcludes(excludes);
efl.setClassDir(new FilePath(path, "classes"));
efl.setSrcDir(new FilePath(path, "sources"));
efl.loadBundleCoverage();

} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return efl;
}
public JacocoReportDir getJacocoReport() {
return new JacocoReportDir(owner);
}

/**
* Obtains the detailed {@link CoverageReport} instance.
Expand All @@ -265,10 +211,10 @@ public synchronized CoverageReport getResult() {
if(r!=null) return r;
}

final File reportFolder = JacocoPublisher.getJacocoReport(owner);
final JacocoReportDir reportFolder = getJacocoReport();

try {
CoverageReport r = new CoverageReport(this, getJacocoReports(reportFolder, inclusions, exclusions));
CoverageReport r = new CoverageReport(this, reportFolder.parse(inclusions, exclusions));
report = new WeakReference<CoverageReport>(r);
r.setThresholds(thresholds);
return r;
Expand Down Expand Up @@ -307,31 +253,30 @@ public JacocoBuildAction getPreviousResult() {
* @throws IOException
* if failed to parse the file.
*/
public static JacocoBuildAction load(AbstractBuild<?,?> owner, Rule rule, JacocoHealthReportThresholds thresholds, BuildListener listener, FilePath actualBuildDirRoot, String[] includes, String[] excludes) throws IOException {
public static JacocoBuildAction load(AbstractBuild<?,?> owner, Rule rule, JacocoHealthReportThresholds thresholds, BuildListener listener, JacocoReportDir layout, String[] includes, String[] excludes) throws IOException {
PrintStream logger = listener.getLogger();
Map<CoverageElement.Type,Coverage> ratios = null;

ratios = loadRatios(actualBuildDirRoot, ratios, includes, excludes);
ratios = loadRatios(layout, ratios, includes, excludes);
return new JacocoBuildAction(owner, rule, ratios, thresholds, listener, includes, excludes);
}


/**
* Extracts top-level coverage information from the JaCoCo report document.
*
* @param actualBuildDirRoot
* @param layout
* @param ratios
* @return
* @throws IOException
*/
private static Map<Type, Coverage> loadRatios(FilePath actualBuildDirRoot, Map<Type, Coverage> ratios, String[] includes, String[] excludes) throws IOException {
private static Map<Type, Coverage> loadRatios(JacocoReportDir layout, Map<Type, Coverage> ratios, String[] includes, String[] excludes) throws IOException {

if (ratios == null) {
ratios = new LinkedHashMap<CoverageElement.Type, Coverage>();
}
IBundleCoverage bundleCoverage = null;
ExecutionFileLoader efl = getJacocoReports(actualBuildDirRoot, includes, excludes);
bundleCoverage = efl.getBundleCoverage();
ExecutionFileLoader efl = layout.parse(includes, excludes);
IBundleCoverage bundleCoverage = efl.getBundleCoverage();
Coverage ratio = new Coverage();
ratio.accumulatePP(bundleCoverage.getClassCounter().getMissedCount(), bundleCoverage.getClassCounter().getCoveredCount());
ratios.put(CoverageElement.Type.CLASS, ratio);
Expand Down
52 changes: 16 additions & 36 deletions src/main/java/hudson/plugins/jacoco/JacocoPublisher.java
Expand Up @@ -4,6 +4,7 @@
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.Result;
Expand All @@ -21,6 +22,7 @@
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import org.apache.tools.ant.DirectoryScanner;
Expand Down Expand Up @@ -252,6 +254,7 @@ protected static FilePath[] resolveDirPaths(AbstractBuild<?, ?> build, BuildList
try {
directoryPaths = build.getWorkspace().act(new FilePath.FileCallable<FilePath[]>() {
public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
FilePath base = new FilePath(f);
ArrayList<FilePath> localDirectoryPaths= new ArrayList<FilePath>();
String[] includes = input.split(",");
DirectoryScanner ds = new DirectoryScanner();
Expand All @@ -263,7 +266,7 @@ public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
String[] dirs = ds.getIncludedDirectories();

for (String dir : dirs) {
localDirectoryPaths.add(new FilePath(new File(dir)));
localDirectoryPaths.add(base.child(dir));
}
FilePath[] lfp = {};//trick to have an empty array as a parameter, so the returned array will contain the elements
return localDirectoryPaths.toArray(lfp);
Expand All @@ -287,14 +290,9 @@ public FilePath[] invoke(File f, VirtualChannel channel) throws IOException {
public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {

final PrintStream logger = listener.getLogger();
FilePath[] matchedExecFiles = null;
FilePath[] matchedClassDirs = null;
FilePath[] matchedSrcDirs = null;
FilePath actualBuildDirRoot = null;
FilePath actualBuildClassDir = null;
FilePath actualBuildSrcDir = null;
FilePath actualBuildExecDir = null;



logger.println("[JaCoCo plugin] Collecting JaCoCo coverage data...");

Expand All @@ -318,35 +316,24 @@ public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListene
} else {
logger.println("[JaCoCo plugin] " + execPattern + ";" + classPattern + ";" + sourcePattern + ";" + " locations are configured");
}
actualBuildDirRoot = new FilePath(getJacocoReport(build));
actualBuildClassDir = new FilePath(actualBuildDirRoot, "classes");
actualBuildSrcDir = new FilePath(actualBuildDirRoot, "sources");
actualBuildExecDir = new FilePath(actualBuildDirRoot, "execFiles");

matchedExecFiles = build.getWorkspace().list(resolveFilePaths(build, listener, execPattern));
logger.println("[JaCoCo plugin] Number of found exec files: " + matchedExecFiles.length);
JacocoReportDir dir = new JacocoReportDir(build);

List<FilePath> matchedExecFiles = Arrays.asList(build.getWorkspace().list(resolveFilePaths(build, listener, execPattern)));
logger.println("[JaCoCo plugin] Number of found exec files: " + matchedExecFiles.size());
logger.print("[JaCoCo plugin] Saving matched execfiles: ");
int i=0;
for (FilePath file : matchedExecFiles) {
FilePath separateExecDir = new FilePath(actualBuildExecDir, "exec"+i);
FilePath fullExecName = separateExecDir.child("jacoco.exec");
file.copyTo(fullExecName);
logger.print(" " + file.getRemote());
++i;
}
dir.addExecFiles(matchedExecFiles);
logger.print(" " + Util.join(matchedExecFiles," "));
matchedClassDirs = resolveDirPaths(build, listener, classPattern);
logger.print("\n[JaCoCo plugin] Saving matched class directories: ");
for (FilePath file : matchedClassDirs) {
file= new FilePath(build.getWorkspace(), file.getRemote()+"\\");
saveCoverageReports(actualBuildClassDir, file);
logger.print(" " + file.getRemote());
dir.saveClassesFrom(file);
logger.print(" " + file);
}
matchedSrcDirs = resolveDirPaths(build, listener, sourcePattern);
logger.print("\n[JaCoCo plugin] Saving matched source directories: ");
for (FilePath file : matchedSrcDirs) {
file= new FilePath(build.getWorkspace(), file.getRemote());
saveCoverageReports(actualBuildSrcDir, file);
logger.print(" " + file.getRemote());
dir.saveSourcesFrom(file);
logger.print(" " + file);
}

//logger.println("[JaCoCo plugin] BuildENV: " +build.getEnvironment(listener).toString());
Expand All @@ -372,7 +359,7 @@ public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListene
logger.println("[JaCoCo plugin] exclusions: " + Arrays.toString(excludes));
}

final JacocoBuildAction action = JacocoBuildAction.load(build, rule, healthReports, listener, actualBuildDirRoot, includes, excludes);
final JacocoBuildAction action = JacocoBuildAction.load(build, rule, healthReports, listener, dir, includes, excludes);
action.getThresholds().ensureValid();
logger.println("[JaCoCo plugin] Thresholds: " + action.getThresholds());
build.getActions().add(action);
Expand Down Expand Up @@ -409,13 +396,6 @@ public BuildStepMonitor getRequiredMonitorService() {
return BuildStepMonitor.BUILD;
}

/**
* Gets the directory to store report files
*/
static File getJacocoReport(AbstractBuild<?,?> build) {
return new File(build.getRootDir(), "jacoco");
}

@Override
public BuildStepDescriptor<Publisher> getDescriptor() {
return DESCRIPTOR;
Expand Down
111 changes: 111 additions & 0 deletions src/main/java/hudson/plugins/jacoco/JacocoReportDir.java
@@ -0,0 +1,111 @@
package hudson.plugins.jacoco;

import hudson.FilePath;
import hudson.model.AbstractBuild;

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

/**
* Encapsulates the directory structure in $JENKINS_HOME where we store jacoco related files.
*
* @author Kohsuke Kawaguchi
*/
public class JacocoReportDir {
private final File root;

public JacocoReportDir(AbstractBuild<?,?> build) {
root = new File(build.getRootDir(), "jacoco");
}

/**
* Where we store *.class files, honoring package names as directories.
*/
public File getClassesDir() {
return new File(root,"classes");
}

public void saveClassesFrom(FilePath dir) throws IOException, InterruptedException {
FilePath d = new FilePath(getClassesDir());
d.mkdirs();
dir.copyRecursiveTo(d);
}

/**
* Where we store *.java files, honoring package names as directories.
*/
public File getSourcesDir() {
return new File(root,"sources");
}

public void saveSourcesFrom(FilePath dir) throws IOException, InterruptedException {
FilePath d = new FilePath(getSourcesDir());
d.mkdirs();
dir.copyRecursiveTo(d);
}

/**
* Root directory that stores jacoco.exec files.
* Each exec file is stored in its own directory.
*
* @see #getExecFiles()
*/
public File getExecFilesDir() {
return new File(root,"execFiles");
}

/**
* Lists up existing jacoco.exec files.
*/
public List<File> getExecFiles() {
List<File> r = new ArrayList<File>();
int i = 0;
File root = getExecFilesDir();
File checkPath;
while ((checkPath = new File(root, "exec" + i)).exists()) {
r.add(new File(checkPath,"jacoco.exec"));
i++;
}

return r;
}

public void addExecFiles(Iterable<FilePath> execFiles) throws IOException, InterruptedException {
FilePath root = new FilePath(getExecFilesDir());
int i=0;
for (FilePath file : execFiles) {
FilePath separateExecDir;
do {
separateExecDir = new FilePath(root, "exec"+(i++));
} while (separateExecDir.exists());

FilePath fullExecName = separateExecDir.child("jacoco.exec");
file.copyTo(fullExecName);
}
}

/**
* Parses the saved "jacoco.exec" files into an {@link ExecutionFileLoader}.
*/
public ExecutionFileLoader parse(String[] includes, String[] excludes) throws IOException {
ExecutionFileLoader efl = new ExecutionFileLoader();
for (File exec : getExecFiles()) {
efl.addExecFile(new FilePath(exec));
}

efl.setIncludes(includes);
efl.setExcludes(excludes);
efl.setClassDir(new FilePath(getClassesDir()));
efl.setSrcDir(new FilePath(getSourcesDir()));
efl.loadBundleCoverage();

return efl;
}

@Override
public String toString() {
return root.toString();
}
}
5 changes: 3 additions & 2 deletions src/main/java/hudson/plugins/jacoco/report/ClassReport.java
Expand Up @@ -3,6 +3,7 @@
import org.jacoco.core.analysis.IClassCoverage;

import java.io.File;
import java.io.Writer;
import java.util.logging.Logger;

/**
Expand Down Expand Up @@ -37,8 +38,8 @@ public File getSourceFilePath() {
return new File(sourceFilePath);
}

public String printHighlightedSrcFile() {
return new SourceAnnotator(getSourceFilePath()).printHighlightedSrcFile(classCov);
public void printHighlightedSrcFile(Writer output) {
new SourceAnnotator(getSourceFilePath()).printHighlightedSrcFile(classCov,output);
}

@Override
Expand Down

0 comments on commit 1c8657a

Please sign in to comment.