view src/org/thermostat/qa/reporter/ResultsGraphGenerator.java @ 173:6b6ac47a811f

Added new reporter (generating report from test logs) slightly modified existing templates and stylesheet (foldable details)
author Zdenek Zambersky <zzambers@redhat.com>
date Thu, 19 Mar 2015 12:44:59 +0100
parents a7bd25187fa0
children
line wrap: on
line source

/*

    ThermostatQA - test framework for Thermostat Monitoring Tool

    Copyright 2013 Red Hat, Inc.

This file is part of ThermostatQA

ThermostatQA is distributed under the GNU General Public License,
version 2 or any later version (with a special exception described
below, commonly known as the "Classpath Exception").

A copy of GNU General Public License (GPL) is included in this
distribution, in the file COPYING.

Linking ThermostatQA code with other modules is making a combined work
based on ThermostatQA.  Thus, the terms and conditions of the GPL
cover the whole combination.

As a special exception, the copyright holders of ThermostatQA give you
permission to link this code with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on ThermostatQA code.  If you modify ThermostatQA, you may
extend this exception to your version of the software, but you are
not obligated to do so.  If you do not wish to do so, delete this
exception statement from your version.
*/

package org.thermostat.qa.reporter;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;



/**
 * Simple generator for a graph (pie chart) containing test results which is
 * used on the index.html page.
 * 
 * @author Pavel Tisnovsky <ptisnovs@redhat.com>
 */
public class ResultsGraphGenerator implements CommonGenerator
{
    /**
     * Border between graph limits and the arc shape.
     */
    private static final int ARC_BORDER = 25;

    /**
     * Offset between the arc shape and text labels.
     */
    private static final int TEXT_OFFSET = 15;

    private int width;
    private int height;
    private int arcX;
    private int arcY;
    private int arcWidth;
    private int arcHeight;
    private Graphics2D graphics;
    private FontMetrics fontMetrics;

    /**
     * Generate graph and store it as a PNG file to the specified directory.
     *
     * @param testResult
     *            data structure containing one test result.
     * @param graphSize
     * @param string
     * @throws IOException 
     */
    public void generateGraph(TestResult testResult, Dimension graphSize, String fileName) throws IOException {
        setupGeometry(graphSize);
        BufferedImage bitmap = createBitmap();
        setGraphics(bitmap);
        clearCanvas();

        int totalTests = testResult.getPassed() + testResult.getFailed() + testResult.getError() + testResult.getIgnored();
        int[] results = new int[4];
        results[0] = testResult.getPassed();
        results[1] = testResult.getFailed();
        results[2] = testResult.getError();
        results[3] = testResult.getIgnored();
        Color[] arcColors = {Color.green, Color.red, Color.blue, Color.gray};

        drawFilledArcs(totalTests, results, arcColors);
        drawArcsAndLabels(totalTests, results);

        ImageIO.write(bitmap, "png", new File(fileName));
    }

    /**
     * @param totalTests
     * @param results
     * @param arcColors
     */
    private void drawFilledArcs(int totalTests, int[] results, Color[] arcColors) {
        float startAngle = 0.0f;
        float arcAngle;

        for (int i = 0; i < results.length; i++) {
            arcAngle = calcArcAngle(results[i], totalTests);
            fillArc(arcColors[i], startAngle, arcAngle);
            startAngle += arcAngle;
        }
    }

    /**
     * @param totalTests
     * @param results
     */
    private void drawArcsAndLabels(int totalTests, int[] results) {
        float startAngle = 0.0f;
        float arcAngle;
        for (int i = 0; i < results.length; i++) {
            arcAngle = calcArcAngle(results[i], totalTests);
            drawArc(startAngle, arcAngle);
            drawLabels(calcResultsPercentage(results[i], totalTests), startAngle, arcAngle);
            startAngle += arcAngle;
        }
    }

    /**
     * @param testCount
     * @param totalTests
     * @return
     */
    private float calcArcAngle(int testCount, int totalTests) {
        return (360.0f * testCount) / totalTests;
    }

    /**
     * @param testCount
     * @param totalTests
     * @return
     */
    private int calcResultsPercentage(int testCount, int totalTests) {
        return (int)((100.0f * testCount) / totalTests);
    }

    /**
     * @param graphSize
     */
    private void setupGeometry(Dimension graphSize) {
        this.width = graphSize.width;
        this.height = graphSize.height;
        this.arcX = ARC_BORDER;
        this.arcY = ARC_BORDER;
        this.arcWidth = this.width - (ARC_BORDER << 1);
        this.arcHeight = this.height - (ARC_BORDER << 1);
    }

    /**
     * @return
     */
    private BufferedImage createBitmap() {
        return new BufferedImage(this.width, this.height, BufferedImage.TYPE_4BYTE_ABGR);
    }

    /**
     * @param bitmap
     */
    private void setGraphics(BufferedImage bitmap) {
        this.graphics = (Graphics2D) bitmap.getGraphics();
        this.fontMetrics = this.graphics.getFontMetrics(this.graphics.getFont());
        setRenderingHints();
    }

    /**
     * Set the required rendering hints.
     */
    private void setRenderingHints() {
        this.graphics.addRenderingHints(new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY));
        this.graphics.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
    }

    /**
     * 
     */
    private void clearCanvas() {
        final int rectWidth = this.width - 2;
        final int rectHeight = this.height - 2;
        this.graphics.setColor(Color.black);
        this.graphics.setBackground(Color.white);
        this.graphics.clearRect(1, 1, rectWidth, rectHeight);
    }

    /**
     * @param startAngle
     * @param arcAngle
     */
    private void drawArc(float startAngle, float arcAngle) {
        final int centerX = this.width >> 1;
        final int centerY = this.height >> 1;
        final int radius = this.arcHeight >> 1;
        double x1 = centerX + radius * Math.cos(Math.PI * startAngle / 180.0);
        double y1 = centerY - radius * Math.sin(Math.PI * startAngle / 180.0);
        //double x2 = centerX + radius * Math.cos(startAngle + arcAngle);
        //double y2 = centerY + radius * Math.sin(startAngle + arcAngle);
        this.graphics.setColor(Color.black);
        this.graphics.drawArc(this.arcX, this.arcY, this.arcWidth, this.arcHeight, (int)startAngle, (int)arcAngle + 1);
        this.graphics.drawLine(centerX, centerY, (int)x1, (int)y1);
    }

    /**
     * @param percentage
     * @param startAngle
     * @param arcAngle
     */
    private void drawLabels(int percentage, float startAngle, float arcAngle) {
        final int centerX = this.width >> 1;
        final int centerY = this.height >> 1;
        final int radius = (this.arcHeight >> 1) + TEXT_OFFSET;
        final float angle = startAngle + arcAngle / 2.0f;
        double x1 = centerX + radius * Math.cos(Math.PI * angle / 180.0);
        double y1 = centerY - radius * Math.sin(Math.PI * angle / 180.0);
        x1 += 2;
        String label = percentage + "%";
        Rectangle2D rect = this.fontMetrics.getStringBounds(label, this.graphics);
        x1 -= rect.getWidth()/2;
        y1 += rect.getHeight()/2;
        this.graphics.setColor(Color.white);
        this.graphics.drawString(label, (int)x1, (int)y1);
        this.graphics.setColor(Color.black);
        this.graphics.drawString(label, (int)x1-1, (int)y1-1);
    }

    /**
     * @param arcColor
     * @param startAngle
     * @param arcAngle
     */
    private void fillArc(Color arcColor, float startAngle, float arcAngle) {
        this.graphics.setColor(arcColor);
        this.graphics.fillArc(this.arcX, this.arcY, this.arcWidth, this.arcHeight, (int)startAngle, (int)arcAngle + 1);
    }

    /**
     * Generate a graph containing daily test results.
     *
     * @param testResults
     *            data structure containing the test results.
     * @param params
     *            parameters passed to the generator.
     * @throws IOException
     */
    public static void generate(Map<String, Map<String, List<String>>> testResults, CommandLineParameters params) throws IOException {
        String date = params.getDate();
        // try to find results for the given date
        if (!testResults.containsKey(params.getDate()))
        {
            throw new RuntimeException("Results for " + date + " don't exist");
        }
        // get the results for the given date
        Map<String, List<String>> testResultForDate = testResults.get(date);
        TestResult testResult = TestResult.readSummary(testResultForDate);
        testResult.setDate(date);

        Dimension graphSize = new Dimension(256, 256);
        new ResultsGraphGenerator().generateGraph(testResult, graphSize, params.getReportDir() + "/graph.png");
    }

    /**
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        Dimension graphSize = new Dimension(256, 256);
        TestResult testResult = new TestResult(100, 10, 5, 20);
        new ResultsGraphGenerator().generateGraph(testResult, graphSize, "graph1.png");
        testResult = new TestResult(25, 25, 25, 25);
        new ResultsGraphGenerator().generateGraph(testResult, graphSize, "graph2.png");
        testResult = new TestResult(10, 20, 30, 40);
        new ResultsGraphGenerator().generateGraph(testResult, graphSize, "graph3.png");
        testResult = new TestResult(100, 10, 5, 20);
        new ResultsGraphGenerator().generateGraph(testResult, graphSize, "graph4.png");
    }

}