From b24f036bc4817ec3f8ee29593c93721f826a6264 Mon Sep 17 00:00:00 2001 From: Morgan Richomme Date: Thu, 29 Sep 2016 15:39:42 +0200 Subject: Add a trend line to Functest reporting And use d3js lib rather than static images JIRA: FUNCTEST-483 Change-Id: I3c372a8e18fc366dfe510ab551596640fb6d9242 Signed-off-by: Morgan Richomme --- utils/test/reporting/functest/default.css | 77 ---------- utils/test/reporting/functest/reporting-status.py | 7 +- utils/test/reporting/functest/reportingConf.py | 1 - .../functest/template/index-status-tmpl.html | 96 +++++++----- utils/test/reporting/js/gauge.js | 165 +++++++++++++++++++++ utils/test/reporting/js/trend.js | 68 +++++++++ 6 files changed, 299 insertions(+), 115 deletions(-) delete mode 100644 utils/test/reporting/functest/default.css create mode 100644 utils/test/reporting/js/gauge.js create mode 100644 utils/test/reporting/js/trend.js diff --git a/utils/test/reporting/functest/default.css b/utils/test/reporting/functest/default.css deleted file mode 100644 index 897c3b12b..000000000 --- a/utils/test/reporting/functest/default.css +++ /dev/null @@ -1,77 +0,0 @@ -.panel-header-item { - position: relative; - display: inline-block; - padding-left: 17px; - padding-right: 17px; -} - -.panel-pod-name { - margin-top: 10px; - margin-right: 27px; - float:right; - padding: 6px; -} - -.panel-default > .panel-heading .badge { - background-color: #007e88; - position: relative; - display: inline-block; -} - -.panel-default > .panel-heading .progress-bar { - height: 100%; - position: absolute; - left: 0; - top: 0; - width: 100%; - background-color: #0095a2 -} -.panel-default > .panel-heading h4 { - color: white; -} - -.panel-default > .panel-heading { - background-color: #00ADBB; - overflow: hidden; - position: relative; - width: 100%; -} - -th{ - text-align: center; -} - -td{ - text-align: center; -} - -.tr-danger { - background-color: #177870; - color: white; -} - -.btn-more { - color: white; - background-color: #0095a2; -} - -h1 { - display: block; - font-size: 2em; - margin-top: 0.67em; - margin-bottom: 0.67em; - margin-left: 0; - margin-right: 0; - font-weight: bold; -} - -h2 { - display: block; - font-size: 1.5em; - margin-top: 0.83em; - margin-bottom: 0.83em; - margin-left: 0; - margin-right: 0; - font-weight: bold; - color:rgb(128, 128, 128) -} diff --git a/utils/test/reporting/functest/reporting-status.py b/utils/test/reporting/functest/reporting-status.py index 90699bd61..9df699629 100755 --- a/utils/test/reporting/functest/reporting-status.py +++ b/utils/test/reporting/functest/reporting-status.py @@ -184,8 +184,13 @@ for version in conf.versions: scenario_criteria = conf.MAX_SCENARIO_CRITERIA s_score = str(scenario_score) + "/" + str(scenario_criteria) - s_score_percent = float( + s_score_percent = 0.0 + try: + s_score_percent = float( scenario_score) / float(scenario_criteria) * 100 + except: + logger.error("cannot calculate the score percent") + s_status = "KO" if scenario_score < scenario_criteria: logger.info(">>>> scenario not OK, score = %s/%s" % diff --git a/utils/test/reporting/functest/reportingConf.py b/utils/test/reporting/functest/reportingConf.py index e1c4b61a8..1c9a2ac9f 100644 --- a/utils/test/reporting/functest/reportingConf.py +++ b/utils/test/reporting/functest/reportingConf.py @@ -13,7 +13,6 @@ installers = ["apex", "compass", "fuel", "joid"] # list of test cases declared in testcases.yaml but that must not be # taken into account for the scoring blacklist = ["ovno", "security_scan"] -# versions = ["brahmaputra", "master"] versions = ["master", "colorado"] PERIOD = 10 MAX_SCENARIO_CRITERIA = 50 diff --git a/utils/test/reporting/functest/template/index-status-tmpl.html b/utils/test/reporting/functest/template/index-status-tmpl.html index 67c23491a..2beb9128e 100644 --- a/utils/test/reporting/functest/template/index-status-tmpl.html +++ b/utils/test/reporting/functest/template/index-status-tmpl.html @@ -3,17 +3,65 @@ - + - + + + + // Draw the trend line + var mytrend = trend("#trend_svg{{loop.index}}",trend{{loop.index}}) + // **************************************** + {%- endfor %} + }); + if ( !window.isLoaded ) { + window.addEventListener("load", function() { + onDocumentReady(); + }, false); + } else { + onDocumentReady(); + } + + +
@@ -40,41 +88,17 @@

List of last scenarios ({{version}}) run over the last {{period}} days

- + + {% for scenario,iteration in scenario_stats.iteritems() -%} - + + diff --git a/utils/test/reporting/js/gauge.js b/utils/test/reporting/js/gauge.js new file mode 100644 index 000000000..4cad16c61 --- /dev/null +++ b/utils/test/reporting/js/gauge.js @@ -0,0 +1,165 @@ +// ****************************************** +// Gauge for reporting +// Each scenario has a score +// We use a gauge to indicate the trust level +// ****************************************** +var gauge = function(container) { + var that = {}; + var config = { + size : 150, + clipWidth : 250, + clipHeight : 100, + ringInset : 20, + ringWidth : 40, + + pointerWidth : 7, + pointerTailLength : 5, + pointerHeadLengthPercent : 0.8, + + minValue : 0, + maxValue : 100, + + minAngle : -90, + maxAngle : 90, + + transitionMs : 4000, + + majorTicks : 7, + labelFormat : d3.format(',g'), + labelInset : 10, + + arcColorFn : d3.interpolateHsl(d3.rgb('#ff0000'), d3.rgb('#00ff00')) + }; + + +var range = undefined; +var r = undefined; +var pointerHeadLength = undefined; +var value = 0; + +var svg = undefined; +var arc = undefined; +var scale = undefined; +var ticks = undefined; +var tickData = undefined; +var pointer = undefined; + +var donut = d3.layout.pie(); + +function deg2rad(deg) { + return deg * Math.PI / 180; +} + +function newAngle(d) { + var ratio = scale(d); + var newAngle = config.minAngle + (ratio * range); + return newAngle; +} + +function configure() { + range = config.maxAngle - config.minAngle; + r = config.size / 2; + pointerHeadLength = Math.round(r * config.pointerHeadLengthPercent); + + // a linear scale that maps domain values to a percent from 0..1 + scale = d3.scale.linear() + .range([0,1]) + .domain([config.minValue, config.maxValue]); + + ticks = scale.ticks(config.majorTicks); + tickData = d3.range(config.majorTicks).map(function() {return 1/config.majorTicks;}); + + arc = d3.svg.arc() + .innerRadius(r - config.ringWidth - config.ringInset) + .outerRadius(r - config.ringInset) + .startAngle(function(d, i) { + var ratio = d * i; + return deg2rad(config.minAngle + (ratio * range)); + }) + .endAngle(function(d, i) { + var ratio = d * (i+1); + return deg2rad(config.minAngle + (ratio * range)); + }); +} +that.configure = configure; + +function centerTranslation() { + return 'translate('+r +','+ r +')'; +} + +function isRendered() { + return (svg !== undefined); +} +that.isRendered = isRendered; + +function render(newValue) { + svg = d3.select(container) + .append('svg:svg') + .attr('class', 'gauge') + .attr('width', config.clipWidth) + .attr('height', config.clipHeight); + + var centerTx = centerTranslation(); + + var arcs = svg.append('g') + .attr('class', 'arc') + .attr('transform', centerTx); + + arcs.selectAll('path') + .data(tickData) + .enter().append('path') + .attr('fill', function(d, i) { + return config.arcColorFn(d * i); + }) + .attr('d', arc); + + var lg = svg.append('g') + .attr('class', 'label') + .attr('transform', centerTx); + lg.selectAll('text') + .data(ticks) + .enter().append('text') + .attr('transform', function(d) { + var ratio = scale(d); + var newAngle = config.minAngle + (ratio * range); + return 'rotate(' +newAngle +') translate(0,' +(config.labelInset - r) +')'; + }) + .text(config.labelFormat); + + var lineData = [ [config.pointerWidth / 2, 0], + [0, -pointerHeadLength], + [-(config.pointerWidth / 2), 0], + [0, config.pointerTailLength], + [config.pointerWidth / 2, 0] ]; + var pointerLine = d3.svg.line().interpolate('monotone'); + var pg = svg.append('g').data([lineData]) + .attr('class', 'pointer') + .attr('transform', centerTx); + + pointer = pg.append('path') + .attr('d', pointerLine/*function(d) { return pointerLine(d) +'Z';}*/ ) + .attr('transform', 'rotate(' +config.minAngle +')'); + + update(newValue === undefined ? 0 : newValue); +} +that.render = render; + +function update(newValue, newConfiguration) { + if ( newConfiguration !== undefined) { + configure(newConfiguration); + } + var ratio = scale(newValue); + var newAngle = config.minAngle + (ratio * range); + pointer.transition() + .duration(config.transitionMs) + .ease('elastic') + .attr('transform', 'rotate(' +newAngle +')'); +} +that.update = update; + +configure(); + +render(); + +return that; +}; diff --git a/utils/test/reporting/js/trend.js b/utils/test/reporting/js/trend.js new file mode 100644 index 000000000..ec48e75ef --- /dev/null +++ b/utils/test/reporting/js/trend.js @@ -0,0 +1,68 @@ +// ****************************************** +// Trend line for reporting +// based on scenario_history.txt +// where data looks like +// date,scenario,installer,detail,score +// 2016-09-22 13:12,os-nosdn-fdio-noha,apex,4/12,33.0 +// 2016-09-22 13:13,os-odl_l2-fdio-noha,apex,12/15,80.0 +// 2016-09-22 13:13,os-odl_l2-sfc-noha,apex,18/24,75.0 +// ..... +// ****************************************** +// Set the dimensions of the canvas / graph +var trend_margin = {top: 20, right: 30, bottom: 50, left: 40}, + trend_width = 300 - trend_margin.left - trend_margin.right, + trend_height = 130 - trend_margin.top - trend_margin.bottom; + +// Parse the date / time +var parseDate = d3.time.format("%Y-%m-%d %H:%M").parse; + +// Set the ranges +var trend_x = d3.time.scale().range([0, trend_width]); +var trend_y = d3.scale.linear().range([trend_height, 0]); + +// Define the axes +var trend_xAxis = d3.svg.axis().scale(trend_x) + .orient("bottom").ticks(2).tickFormat(d3.time.format("%m-%d")); + +var trend_yAxis = d3.svg.axis().scale(trend_y) + .orient("left").ticks(2); + +// Define the line +var valueline = d3.svg.line() + .x(function(d) { return trend_x(d.date); }) + .y(function(d) { return trend_y(d.score); }); + +var trend = function(container, trend_data) { + + var trend_svg = d3.select(container) + .append("svg") + .attr("width", trend_width + trend_margin.left + trend_margin.right) + .attr("height", trend_height + trend_margin.top + trend_margin.bottom) + .append("g") + .attr("transform", + "translate(" + trend_margin.left + "," + trend_margin.top + ")"); + + // Scale the range of the data + trend_x.domain(d3.extent(trend_data, function(d) { return d.date; })); + trend_y.domain([0, d3.max(trend_data, function(d) { return d.score; })]); + + // Add the X Axis + trend_svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + trend_height + ")") + .call(trend_xAxis); + + // Add the Y Axis + trend_svg.append("g") + .attr("class", "y axis") + .call(trend_yAxis); + + // Add the valueline path. + trend_svg.append("path") + .attr("class", "line") + .attr("d", valueline(trend_data)) + .attr("stroke", "steelblue") + .attr("fill", "none"); + + return trend; +} -- cgit 1.2.3-korg
ScenarioScenario StatusTrend Score Iteration
{{scenario}}{%if scenario_results[scenario].getScorePercent() < 8.3 -%} - - {%elif scenario_results[scenario].getScorePercent() < 16.7 -%} - - {%elif scenario_results[scenario].getScorePercent() < 25 -%} - - {%elif scenario_results[scenario].getScorePercent() < 33.3 -%} - - {%elif scenario_results[scenario].getScorePercent() < 41.7 -%} - - {%elif scenario_results[scenario].getScorePercent() < 50 -%} - - {%elif scenario_results[scenario].getScorePercent() < 58.3 -%} - - {%elif scenario_results[scenario].getScorePercent() < 66.7 -%} - - {%elif scenario_results[scenario].getScorePercent() < 75 -%} - - {%elif scenario_results[scenario].getScorePercent() < 83.3 -%} - - {%elif scenario_results[scenario].getScorePercent() < 91.7 -%} - - {%elif scenario_results[scenario].getScorePercent() < 100 -%} - - {%- else -%} - - {%- endif %}
{{scenario_results[scenario].getScore()}} {{iteration}}