package hudson.plugins.seleniumhq; import hudson.FilePath; import hudson.Functions; import hudson.model.Actionable; import hudson.model.Build; import hudson.model.DirectoryBrowserSupport; import hudson.model.Project; import hudson.model.ProminentProjectAction; import hudson.model.Result; import hudson.util.Area; import hudson.util.ChartUtil; import hudson.util.ColorPalette; import hudson.util.DataSetBuilder; import hudson.util.ShiftedCategoryAxis; import hudson.util.StackedAreaRenderer2; import hudson.util.ChartUtil.NumberOnlyBuildLabel; import java.awt.Color; import java.io.File; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletException; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.StackedAreaRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.ui.RectangleInsets; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; public class SeleniumhqProjectAction extends Actionable implements ProminentProjectAction { private final Project<?, ?> project; public SeleniumhqProjectAction(Project<?, ?> project) { this.project = project; } public String getDisplayName() { return "Selenium Report"; } public String getIconFileName() { return "/plugin/seleniumhq/icons/sla-48x48.png"; } public String getUrlName() { return "seleniumhq"; } public String getSearchUrl() { return getUrlName(); } public DirectoryBrowserSupport doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException,InterruptedException { File rootFile = SeleniumhqPublisher.getSeleniumReportDir(project); if (!rootFile.exists())rootFile.mkdir(); FilePath rootTarget = new FilePath(rootFile); if (rootTarget.list().size() == 0) { // Make index String index = "<html><head><title>Selenium result</title></head><body><center><br/><h2>No Selenium Test Result</h2></center></body></html>"; OutputStream output = rootTarget.child("index.html").write(); output.write(index.getBytes()); output.close(); } return new DirectoryBrowserSupport(this, rootTarget, "Seleniumhq", "graph.gif", false); } public SeleniumhqBuildAction getLastResult() { for (Build<?, ?> b = project.getLastBuild(); b != null; b = b.getPreviousBuild()) { if (b.getResult() == Result.FAILURE) continue; SeleniumhqBuildAction r = b.getAction(SeleniumhqBuildAction.class); if (r != null) return r; } return null; } public void doGraph(StaplerRequest req, StaplerResponse rsp) throws IOException { if (ChartUtil.awtProblemCause != null || getLastResult() == null) { // not available. send out error message rsp.sendRedirect2(req.getContextPath() + "/images/headless.png"); return; } if (req.checkIfModified(project.getLastBuild().getTimestamp(), rsp)) return; ChartUtil.generateGraph(req, rsp, createChart(req, buildDataSet()), calcDefaultSize()); } private CategoryDataset buildDataSet() { DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel> dsb = new DataSetBuilder<String, ChartUtil.NumberOnlyBuildLabel>(); for (SeleniumhqBuildAction a = getLastResult(); a != null; a = a.getPreviousResult()) { ChartUtil.NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(a.getOwner()); dsb.add(a.getResult().getNumTestPasses(), "passes", label); dsb.add(a.getResult().getNumTestFailures(), "failed", label); } return dsb.build(); } /** * Determines the default size of the trend graph. * * This is default because the query parameter can choose arbitrary size. * If the screen resolution is too low, use a smaller size. */ private Area calcDefaultSize() { Area res = Functions.getScreenResolution(); if(res!=null && res.width<=800) return new Area(250,100); else return new Area(500,200); } private JFreeChart createChart(StaplerRequest req, CategoryDataset dataset) { final String relPath = getRelPath(req); final JFreeChart chart = ChartFactory.createStackedAreaChart( null, // chart title null, // unused "count", // range axis label dataset, // data PlotOrientation.VERTICAL, // orientation false, // include legend true, // tooltips false // urls ); chart.setBackgroundPaint(Color.white); final CategoryPlot plot = chart.getCategoryPlot(); plot.setBackgroundPaint(Color.WHITE); plot.setOutlinePaint(null); plot.setForegroundAlpha(0.8f); plot.setRangeGridlinesVisible(true); plot.setRangeGridlinePaint(Color.black); CategoryAxis domainAxis = new ShiftedCategoryAxis(null); plot.setDomainAxis(domainAxis); domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90); domainAxis.setLowerMargin(0.0); domainAxis.setUpperMargin(0.0); domainAxis.setCategoryMargin(0.0); final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); StackedAreaRenderer ar = new StackedAreaRenderer2() { private static final long serialVersionUID = 1L; @Override public String generateURL(CategoryDataset dataset, int row, int column) { NumberOnlyBuildLabel label = (NumberOnlyBuildLabel) dataset.getColumnKey(column); return relPath+label.build.getNumber()+"/testReport/"; } }; plot.setRenderer(ar); ar.setSeriesPaint(0,ColorPalette.RED); // Failures. ar.setSeriesPaint(1,ColorPalette.BLUE); // Total. // crop extra space around the graph plot.setInsets(new RectangleInsets(0,0,0,5.0)); return chart; } private String getRelPath(StaplerRequest req) { String relPath = req.getParameter("rel"); if(relPath==null) return ""; return relPath; } }