// // Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // A copy of the License is located at // // http://aws.amazon.com/apache2.0 // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // package org.jenkinsci.plugins.awsdevicefarm; import hudson.Functions; import hudson.model.AbstractBuild; import hudson.model.Run; import hudson.util.Area; import hudson.util.ChartUtil.NumberOnlyBuildLabel; import hudson.util.DataSetBuilder; import hudson.util.Graph; import hudson.util.ShiftedCategoryAxis; 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.CategoryItemRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.ui.RectangleEdge; import org.jfree.ui.RectangleInsets; import java.awt.*; import java.util.ArrayList; import java.util.List; /** * Generate stylized graphs for AWS Device Farm results. */ public class AWSDeviceFarmGraph extends Graph { public static final Color PassColor = new Color(0x0c9b49); public static final Color WarnColor = new Color(0xea562f); public static final Color FailColor = new Color(0xbe2326); public static final Color DurationColor = new Color(0x083250); public static final Color FrameColor = new Color(0xEBEBDC); private final String xLabel; private final String yLabel; private final CategoryDataset dataset; private Color[] colors; public AWSDeviceFarmGraph(AbstractBuild<?, ?> owner, Boolean isCompleted, Area size, CategoryDataset dataset, String xLabel, String yLabel, Color... colors) { // Toggle the graph timestamp so we don't cache the graph image if the run isn't completed. super(((isCompleted) ? owner.getTimestamp().getTimeInMillis() : -1), size.width, size.height); this.dataset = dataset; this.xLabel = xLabel; this.yLabel = yLabel; this.colors = colors; } /** * Create graph based on the given dataset and constraints. * * @return The JFreeChart graph. */ protected JFreeChart createGraph() { // Create chart. JFreeChart chart = ChartFactory.createStackedAreaChart(null, null, yLabel, dataset, PlotOrientation.VERTICAL, true, true, false); chart.setBackgroundPaint(Color.WHITE); // Create chart legend. LegendTitle legend = chart.getLegend(); legend.setPosition(RectangleEdge.RIGHT); // Create chart plot. CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setForegroundAlpha(0.7f); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.darkGray); // Create domain (x) axis. CategoryAxis domain = new ShiftedCategoryAxis(xLabel); domain.setCategoryLabelPositions(CategoryLabelPositions.UP_45); domain.setLowerMargin(0.0); domain.setUpperMargin(0.0); domain.setCategoryMargin(0.0); plot.setDomainAxis(domain); // Create range (y) axis. NumberAxis range = (NumberAxis) plot.getRangeAxis(); range.setAutoRange(true); range.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); // Create renderer and paint the chart. CategoryItemRenderer renderer = plot.getRenderer(); plot.setInsets(new RectangleInsets(5.0, 0, 0, 5.0)); // Set chart colors for sections. for (int i = 0; i < colors.length; i++) { renderer.setSeriesPaint(i, colors[i]); } return chart; } /** * Generate a results (pass/warn/fail) trend graph for recent results. * * @param owner The build which owns the latest result. * @param isCompleted The flag to denote if the result is completed which determines our caching. * @param results The list of previous to latest results which generate the trend. * @return The result trend graph. */ public static Graph createResultTrendGraph(AbstractBuild<?, ?> owner, Boolean isCompleted, List<AWSDeviceFarmTestResult> results) { List<String> rows = new ArrayList<String>(); List<Number> vals = new ArrayList<Number>(); List<NumberOnlyBuildLabel> cols = new ArrayList<NumberOnlyBuildLabel>(); for (AWSDeviceFarmTestResult result : results) { Run<?, ?> build = result.getOwner(); // Create label for this result using its Jenkins build number. NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(build); // Add 'pass' results rows.add("Pass"); cols.add(label); vals.add(result.getPassCount()); // Add 'warn' results rows.add("Warn"); cols.add(label); vals.add(result.getWarnCount()); // Add 'fail' results. rows.add("Fail"); cols.add(label); vals.add(result.getFailCount()); } CategoryDataset dataset = createDataset(vals, rows, cols); Color[] colors = new Color[]{AWSDeviceFarmGraph.PassColor, AWSDeviceFarmGraph.WarnColor, AWSDeviceFarmGraph.FailColor}; return new AWSDeviceFarmGraph(owner, isCompleted, getGraphSize(), dataset, "Build #", "# of tests", colors); } /** * Generate a duration trend graph for device minutes used for recent results. * * @param owner The build which owns the latest result. * @param isCompleted The flag to denote if the result is completed which determines our caching. * @param results The list of previous to latest results which generate the trend. * @return The duration trend graph. */ public static Graph createDurationTrendGraph(AbstractBuild<?, ?> owner, Boolean isCompleted, List<AWSDeviceFarmTestResult> results) { DataSetBuilder<String, NumberOnlyBuildLabel> builder = new DataSetBuilder<String, NumberOnlyBuildLabel>(); for (AWSDeviceFarmTestResult result : results) { // Create label for this result using its Jenkins build number. Run<?, ?> build = result.getOwner(); NumberOnlyBuildLabel label = new NumberOnlyBuildLabel(build); // Attach duration value for all results in our trend. builder.add(result.getDuration(), "Minutes", label); } CategoryDataset dataset = builder.build(); Color[] colors = new Color[]{AWSDeviceFarmGraph.DurationColor}; return new AWSDeviceFarmGraph(owner, isCompleted, getGraphSize(), dataset, "Build #", "Device Minutes Used", colors); } /** * Generate a category dataset. * * @param values The values in the dataset. * @param rows The names of the rows in the dataset. * @param columns The columns in the the dataset. * @return The category dataset. */ private static CategoryDataset createDataset(List<Number> values, List<String> rows, List<NumberOnlyBuildLabel> columns) { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); for (int i = 0; i < values.size(); i++) { dataset.addValue(values.get(i), rows.get(i), columns.get(i)); } return dataset; } /** * Get the size of the graph on the screen. * * @return The area of the graph. */ private static Area getGraphSize() { Area resolution = Functions.getScreenResolution(); if (resolution == null || resolution.width <= 800) { //too small or unknown return new Area(250, 100); } return new Area(500, 200); } }