Package hudson.plugins.cobertura

Source Code of hudson.plugins.cobertura.CoberturaPublisher$CoberturaReportFilenameFilter

package hudson.plugins.cobertura;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractItem;
import hudson.model.AbstractProject;
import hudson.plugins.cobertura.renderers.SourceCodePainter;
import hudson.plugins.cobertura.renderers.SourceEncoding;
import hudson.plugins.cobertura.targets.CoverageMetric;
import hudson.plugins.cobertura.targets.CoverageTarget;
import hudson.plugins.cobertura.targets.CoverageResult;
import hudson.remoting.VirtualChannel;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;

import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import net.sf.json.JSONObject;

import org.apache.commons.beanutils.ConvertUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;

/**
* Cobertura {@link Publisher}.
*
* @author Stephen Connolly
*/
public class CoberturaPublisher extends Recorder {

    private final String coberturaReportFile;

    private final boolean onlyStable;

    private final boolean failUnhealthy;

    private final boolean failUnstable;

    private final boolean autoUpdateHealth;

    private final boolean autoUpdateStability;

    private final boolean zoomCoverageChart;
   
    private final int maxNumberOfBuilds;
   
    private boolean failNoReports = true;

    private CoverageTarget healthyTarget;

    private CoverageTarget unhealthyTarget;

    private CoverageTarget failingTarget;

    public static final CoberturaReportFilenameFilter COBERTURA_FILENAME_FILTER = new CoberturaReportFilenameFilter();

    private final SourceEncoding sourceEncoding;

    /**
     * @param coberturaReportFile the report directory
     * @stapler-constructor
     */
    @DataBoundConstructor
    public CoberturaPublisher(String coberturaReportFile, boolean onlyStable, boolean failUnhealthy, boolean failUnstable,
            boolean autoUpdateHealth, boolean autoUpdateStability, boolean zoomCoverageChart, boolean failNoReports, SourceEncoding sourceEncoding,
            int maxNumberOfBuilds) {
        this.coberturaReportFile = coberturaReportFile;
        this.onlyStable = onlyStable;
        this.failUnhealthy = failUnhealthy;
        this.failUnstable = failUnstable;
        this.autoUpdateHealth = autoUpdateHealth;
        this.autoUpdateStability = autoUpdateStability;
        this.zoomCoverageChart = zoomCoverageChart;
        this.failNoReports = failNoReports;
        this.sourceEncoding = sourceEncoding;
        this.maxNumberOfBuilds = maxNumberOfBuilds;
        this.healthyTarget = new CoverageTarget();
        this.unhealthyTarget = new CoverageTarget();
        this.failingTarget = new CoverageTarget();
    }

    /**
     * Getter for property 'targets'.
     *
     * @return Value for property 'targets'.
     */
    public List<CoberturaPublisherTarget> getTargets() {
        Map<CoverageMetric, CoberturaPublisherTarget> targets = new TreeMap<CoverageMetric, CoberturaPublisherTarget>();
        float checker;
        for (CoverageMetric metric : healthyTarget.getTargets()) {
            CoberturaPublisherTarget target = targets.get(metric);
            if (target == null) {
                target = new CoberturaPublisherTarget();
                target.setMetric(metric);
            }
            checker = (float) healthyTarget.getTarget(metric) / 100000f;
            if (checker <= 0.001f) {
                checker = (float) (Math.round(checker * 100000f));
            }
            target.setHealthy(checker);
            targets.put(metric, target);
        }
        for (CoverageMetric metric : unhealthyTarget.getTargets()) {
            CoberturaPublisherTarget target = targets.get(metric);
            if (target == null) {
                target = new CoberturaPublisherTarget();
                target.setMetric(metric);
            }
            checker = (float) unhealthyTarget.getTarget(metric) / 100000f;
            if (checker <= 0.001f) {
                checker = (float) (Math.round(checker * 100000f));
            }
            target.setUnhealthy(checker);
            targets.put(metric, target);
        }
        for (CoverageMetric metric : failingTarget.getTargets()) {
            CoberturaPublisherTarget target = targets.get(metric);
            if (target == null) {
                target = new CoberturaPublisherTarget();
                target.setMetric(metric);
            }
            checker = (float) failingTarget.getTarget(metric) / 100000f;
            if (checker <= 0.001f) {
                checker = (float) (Math.round(checker * 100000f));
            }
            target.setUnstable(checker);
            targets.put(metric, target);
        }
        List<CoberturaPublisherTarget> result = new ArrayList<CoberturaPublisherTarget>(targets.values());
        return result;
    }

    /**
     * Setter for property 'targets'.
     *
     * @param targets Value to set for property 'targets'.
     */
    private void setTargets(List<CoberturaPublisherTarget> targets) {
        healthyTarget.clear();
        unhealthyTarget.clear();
        failingTarget.clear();
        float rounded;
        for (CoberturaPublisherTarget target : targets) {
            if (target.getHealthy() != null) {
                rounded = (Math.round((float) 100f * target.getHealthy()));
                rounded = roundDecimalFloat(rounded);
                healthyTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
            }
            if (target.getUnhealthy() != null) {
                rounded = (Math.round((float) 100f * target.getUnhealthy()));
                rounded = roundDecimalFloat(rounded);
                unhealthyTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
            }
            if (target.getUnstable() != null) {
                rounded = (Math.round((float) 100f * target.getUnstable()));
                rounded = roundDecimalFloat(rounded);
                failingTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
            }
        }
    }

    /**
     * Getter for property 'coberturaReportFile'.
     *
     * @return Value for property 'coberturaReportFile'.
     */
    public String getCoberturaReportFile() {
        return coberturaReportFile;
    }

    /**
     * Which type of build should be considered.
     * @return the onlyStable
     */
    public boolean getOnlyStable() {
        return onlyStable;
    }
   
    public int getMaxNumberOfBuilds() {
    return maxNumberOfBuilds;
  }

    /**
     * Getter for property 'failUnhealthy'.
     *
     * @return Value for property 'failUnhealthy'.
     */
    public boolean getFailUnhealthy() {
        return failUnhealthy;
    }

    /**
     * Getter for property 'failUnstable'.
     *
     * @return Value for property 'failUnstable'.
     */
    public boolean getFailUnstable() {
        return failUnstable;
    }

    /**
     * Getter for property 'autoUpdateHealth'.
     *
     * @return Value for property 'autoUpdateHealth'.
     */
    public boolean getAutoUpdateHealth() {
        return autoUpdateHealth;
    }

    /**
     * Getter for property 'autoUpdateStability'.
     *
     * @return Value for property 'autoUpdateStability'.
     */
    public boolean getAutoUpdateStability() {
        return autoUpdateStability;
    }

    public boolean getZoomCoverageChart() {
        return zoomCoverageChart;
    }

    public boolean isFailNoReports() {
        return failNoReports;
    }
   
    /**
     * Getter for property 'healthyTarget'.
     *
     * @return Value for property 'healthyTarget'.
     */
    public CoverageTarget getHealthyTarget() {
        return healthyTarget;
    }

    /**
     * Setter for property 'healthyTarget'.
     *
     * @param healthyTarget Value to set for property 'healthyTarget'.
     */
    public void setHealthyTarget(CoverageTarget healthyTarget) {
        this.healthyTarget = healthyTarget;
    }

    /**
     * Getter for property 'unhealthyTarget'.
     *
     * @return Value for property 'unhealthyTarget'.
     */
    public CoverageTarget getUnhealthyTarget() {
        return unhealthyTarget;
    }

    /**
     * Setter for property 'unhealthyTarget'.
     *
     * @param unhealthyTarget Value to set for property 'unhealthyTarget'.
     */
    public void setUnhealthyTarget(CoverageTarget unhealthyTarget) {
        this.unhealthyTarget = unhealthyTarget;
    }

    /**
     * Getter for property 'failingTarget'.
     *
     * @return Value for property 'failingTarget'.
     */
    public CoverageTarget getFailingTarget() {
        return failingTarget;
    }

    /**
     * Setter for property 'failingTarget'.
     *
     * @param failingTarget Value to set for property 'failingTarget'.
     */
    public void setFailingTarget(CoverageTarget failingTarget) {
        this.failingTarget = failingTarget;
    }

    /**
     * Gets the directory where the Cobertura Report is stored for the given project.
     */
    /*package*/
    static File getCoberturaReportDir(AbstractItem project) {
        return new File(project.getRootDir(), "cobertura");
    }

    /**
     * Gets the directory where the Cobertura Report is stored for the given project.
     */
    /*package*/
    static File[] getCoberturaReports(AbstractBuild<?, ?> build) {
        return build.getRootDir().listFiles(COBERTURA_FILENAME_FILTER);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
            throws InterruptedException, IOException {
        Result threshold = onlyStable ? Result.SUCCESS : Result.UNSTABLE;
        if (build.getResult().isWorseThan(threshold)) {
            listener.getLogger().println("Skipping Cobertura coverage report as build was not " + threshold.toString() + " or better ...");
            return true;
        }

        listener.getLogger().println("[Cobertura] Publishing Cobertura coverage report...");
        final FilePath[] moduleRoots = build.getModuleRoots();
        final boolean multipleModuleRoots =
                moduleRoots != null && moduleRoots.length > 1;
        final FilePath moduleRoot = multipleModuleRoots ? build.getWorkspace() : build.getModuleRoot();
        final File buildCoberturaDir = build.getRootDir();
        FilePath buildTarget = new FilePath(buildCoberturaDir);

        FilePath[] reports = new FilePath[0];
        try {
            reports = moduleRoot.act(new ParseReportCallable(coberturaReportFile));

            // if the build has failed, then there's not
            // much point in reporting an error
            if (build.getResult().isWorseOrEqualTo(Result.FAILURE) && reports.length == 0) {
                return true;
            }

        } catch (IOException e) {
            Util.displayIOException(e, listener);
            e.printStackTrace(listener.fatalError("Unable to find coverage results"));
            build.setResult(Result.FAILURE);
        }

        if (reports.length == 0) {
            String msg = "[Cobertura] No coverage results were found using the pattern '"
                    + coberturaReportFile + "' relative to '"
                    + moduleRoot.getRemote() + "'."
                    + "  Did you enter a pattern relative to the correct directory?"
                    + "  Did you generate the XML report(s) for Cobertura?";
            listener.getLogger().println(msg);
            if (failNoReports) {
                build.setResult(Result.FAILURE);
            } else {
                listener.getLogger().println("[Cobertura] Skipped cobertura reports.");
            }
            return true;
        }

        for (int i = 0; i < reports.length; i++) {
            final FilePath targetPath = new FilePath(buildTarget, "coverage" + (i == 0 ? "" : i) + ".xml");
            try {
                reports[i].copyTo(targetPath);
            } catch (IOException e) {
                Util.displayIOException(e, listener);
                e.printStackTrace(listener.fatalError("Unable to copy coverage from " + reports[i] + " to " + buildTarget));
                build.setResult(Result.FAILURE);
            }
        }

        listener.getLogger().println("Publishing Cobertura coverage results...");
        Set<String> sourcePaths = new HashSet<String>();
        CoverageResult result = null;
        for (File coberturaXmlReport : getCoberturaReports(build)) {
            try {
                result = CoberturaCoverageParser.parse(coberturaXmlReport, result, sourcePaths);
            } catch (IOException e) {
                Util.displayIOException(e, listener);
                e.printStackTrace(listener.fatalError("Unable to parse " + coberturaXmlReport));
                build.setResult(Result.FAILURE);
            }
        }
        if (result != null) {
            listener.getLogger().println("Cobertura coverage report found.");
            result.setOwner(build);
            final FilePath paintedSourcesPath = new FilePath(new File(build.getProject().getRootDir(), "cobertura"));
            paintedSourcesPath.mkdirs();
            SourceCodePainter painter = new SourceCodePainter(paintedSourcesPath, sourcePaths,
                    result.getPaintedSources(), listener, getSourceEncoding());

            moduleRoot.act(painter);

            final CoberturaBuildAction action = CoberturaBuildAction.load(build, result, healthyTarget,
                    unhealthyTarget, getOnlyStable(), getFailUnhealthy(), getFailUnstable(), getAutoUpdateHealth(), getAutoUpdateStability());

            build.getActions().add(action);
            Set<CoverageMetric> failingMetrics = failingTarget.getFailingMetrics(result);
            if (!failingMetrics.isEmpty()) {
                listener.getLogger().println("Code coverage enforcement failed for the following metrics:");
                float oldStabilityPercent;
                float setStabilityPercent;
                for (CoverageMetric metric : failingMetrics) {
                    oldStabilityPercent = failingTarget.getObservedPercent(result, metric);
                    setStabilityPercent = failingTarget.getSetPercent(result, metric);
                    listener.getLogger().println("    " + metric.getName() + "'s stability is " + roundDecimalFloat(oldStabilityPercent * 100f) + " and set mininum stability is " + roundDecimalFloat(setStabilityPercent * 100f) + ".");
                }
                if (!getFailUnstable()) {
                    listener.getLogger().println("Setting Build to unstable.");
                    build.setResult(Result.UNSTABLE);
                } else {
                    listener.getLogger().println("Failing build due to unstability.");
                    build.setResult(Result.FAILURE);
                }
            }
            if (getFailUnhealthy()) {
                Set<CoverageMetric> unhealthyMetrics = unhealthyTarget.getFailingMetrics(result);
                if (!unhealthyMetrics.isEmpty()) {
                    listener.getLogger().println("Unhealthy for the following metrics:");
                    float oldHealthyPercent;
                    float setHealthyPercent;
                    for (CoverageMetric metric : unhealthyMetrics) {
                        oldHealthyPercent = unhealthyTarget.getObservedPercent(result, metric);
                        setHealthyPercent = unhealthyTarget.getSetPercent(result, metric);
                        listener.getLogger().println("    " + metric.getName() + "'s health is " + roundDecimalFloat(oldHealthyPercent * 100f) + " and set minimum health is " + roundDecimalFloat(setHealthyPercent * 100f) + ".");
                    }
                    listener.getLogger().println("Failing build because it is unhealthy.");
                    build.setResult(Result.FAILURE);
                }
            }
            if (build.getResult() == Result.SUCCESS) {
                if (getAutoUpdateHealth()) {
                    setNewPercentages(result, true, listener);
                }

                if (getAutoUpdateStability()) {
                    setNewPercentages(result, false, listener);
                }
            }
        } else {
            listener.getLogger().println("No coverage results were successfully parsed.  Did you generate "
                    + "the XML report(s) for Cobertura?");
            build.setResult(Result.FAILURE);
        }

        return true;
    }

    /**
     * Changes unhealthy or unstable percentage fields for ratcheting.
     */
    private void setNewPercentages(CoverageResult result, boolean select, BuildListener listener) {
        Set<CoverageMetric> healthyMetrics = healthyTarget.getAllMetrics(result);
        float newPercent;
        float oldPercent;
        if (!healthyMetrics.isEmpty()) {
            for (CoverageMetric metric : healthyMetrics) {
                newPercent = healthyTarget.getObservedPercent(result, metric);
                newPercent = (float) (Math.round(newPercent * 100f));
                if (select) {
                    oldPercent = unhealthyTarget.getSetPercent(result, metric);
                    oldPercent = (float) (Math.round(oldPercent * 100f));
                } else {
                    oldPercent = failingTarget.getSetPercent(result, metric);
                    oldPercent = (float) (Math.round(oldPercent * 100f));
                }
                if (newPercent > oldPercent) {
                    if (select) {
                        unhealthyTarget.setTarget(metric, (int) (newPercent * 1000f));
                        listener.getLogger().println("    " + metric.getName() + "'s new health minimum is: " + roundDecimalFloat(newPercent));
                    } else {
                        failingTarget.setTarget(metric, (int) (newPercent * 1000f));
                        listener.getLogger().println("    " + metric.getName() + "'s new stability minimum is: " + roundDecimalFloat(newPercent));
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Action getProjectAction(AbstractProject<?, ?> project) {
        return new CoberturaProjectAction(project, getOnlyStable());
    }

    /**
     * {@inheritDoc}
     */
    public BuildStepMonitor getRequiredMonitorService() {
        return BuildStepMonitor.BUILD;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public BuildStepDescriptor<Publisher> getDescriptor() {
        // see Descriptor javadoc for more about what a descriptor is.
        return DESCRIPTOR;
    }

    public SourceEncoding getSourceEncoding() {
        return sourceEncoding;
    }

    /**
     * Descriptor should be singleton.
     */
    @Extension
    public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

    public static class ParseReportCallable implements FilePath.FileCallable<FilePath[]> {

        private static final long serialVersionUID = 1L;

        private final String reportFilePath;

        public ParseReportCallable(String reportFilePath) {
            this.reportFilePath = reportFilePath;
        }

        public FilePath[] invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
            FilePath[] r = new FilePath(f).list(reportFilePath);

            XMLInputFactory factory = XMLInputFactory.newInstance();
            factory.setProperty("javax.xml.stream.supportDTD", false);

            for (FilePath filePath : r) {
                InputStream is = null;
                XMLEventReader reader = null;
                try {
                    is = filePath.read();
                    reader = factory.createXMLEventReader(is);
                    while (reader.hasNext()) {
                        XMLEvent event = reader.nextEvent();
                        if (event.isStartElement()) {
                            StartElement start = (StartElement) event;
                            if (start.getName().getLocalPart().equals("coverage")) {
                                // This is a cobertura coverage report file
                                break;
                            } else {
                                throw new IOException(filePath + " is not a cobertura coverage report, please check your report pattern");
                            }
                        }
                    }
                } catch (XMLStreamException e) {
                    throw new IOException(filePath + " is not an XML file, please check your report pattern");
                } finally {
                    try {
                        if (reader != null) {
                            try {
                                reader.close();
                            } catch (XMLStreamException ex) {
                                //
                            }                           
                        }
                    } finally {
                        IOUtils.closeQuietly(is);
                    }
                }

            }
            return r;
        }
    }

    /**
     * Descriptor for {@link CoberturaPublisher}. Used as a singleton. The class is marked as public so that it can be
     * accessed from views.
     * <p/>
     * <p/>
     * See <tt>views/hudson/plugins/cobertura/CoberturaPublisher/*.jelly</tt> for the actual HTML fragment for the
     * configuration screen.
     */
    public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {

        CoverageMetric[] metrics = {
            CoverageMetric.PACKAGES,
            CoverageMetric.FILES,
            CoverageMetric.CLASSES,
            CoverageMetric.METHOD,
            CoverageMetric.LINE,
            CoverageMetric.CONDITIONAL,};

        /**
         * Constructs a new DescriptorImpl.
         */
        DescriptorImpl() {
            super(CoberturaPublisher.class);
        }

        /**
         * This human readable name is used in the configuration screen.
         */
        public String getDisplayName() {
            return Messages.CoberturaPublisher_displayName();
        }

        /**
         * Getter for property 'metrics'.
         *
         * @return Value for property 'metrics'.
         */
        public List<CoverageMetric> getMetrics() {
            return Arrays.asList(metrics);
        }

        /**
         * Getter for property 'defaultTargets'.
         *
         * @return Value for property 'defaultTargets'.
         */
        public List<CoberturaPublisherTarget> getDefaultTargets() {
            List<CoberturaPublisherTarget> result = new ArrayList<CoberturaPublisherTarget>();
            result.add(new CoberturaPublisherTarget(CoverageMetric.METHOD, 80f, null, null));
            result.add(new CoberturaPublisherTarget(CoverageMetric.LINE, 80f, null, null));
            result.add(new CoberturaPublisherTarget(CoverageMetric.CONDITIONAL, 70f, null, null));
            return result;
        }

        public List<CoberturaPublisherTarget> getTargets(CoberturaPublisher instance) {
            if (instance == null) {
                return getDefaultTargets();
            }
            return instance.getTargets();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
            req.bindParameters(this, "cobertura.");
            save();
            return super.configure(req, formData);
        }

        /**
         * Creates a new instance of {@link CoberturaPublisher} from a submitted form.
         */
        @Override
        public CoberturaPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {
            CoberturaPublisher instance = req.bindJSON(CoberturaPublisher.class, formData);
            ConvertUtils.register(CoberturaPublisherTarget.CONVERTER, CoverageMetric.class);
            List<CoberturaPublisherTarget> targets = req
                    .bindParametersToList(CoberturaPublisherTarget.class, "cobertura.target.");
            instance.setTargets(targets);
            return instance;
        }

        @SuppressWarnings("unchecked")
        @Override
        public boolean isApplicable(Class<? extends AbstractProject> jobType) {
            return true;
        }
    }

    private static class CoberturaReportFilenameFilter implements FilenameFilter {

        /**
         * {@inheritDoc}
         */
        public boolean accept(File dir, String name) {
            // TODO take this out of an anonymous inner class, create a singleton and use a Regex to match the name
            return name.startsWith("coverage") && name.endsWith(".xml");
        }
    }

    public float roundDecimalFloat(Float input) {
        float rounded = (float) Math.round(input);
        rounded = rounded / 100f;
        return rounded;
    }
}
TOP

Related Classes of hudson.plugins.cobertura.CoberturaPublisher$CoberturaReportFilenameFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.