[Reports] Use timestamps on X axis in trends report
The main change is using timestamps on X axis in trends report instead of numeric value (which tells almost nothing). This patch also improves Trends.get_data() return value structure, which is passed to HTML template. Change-Id: I8b343bf9d5940311225fc247fd6a1aacdc5f683a
This commit is contained in:
parent
3ec2ec20e2
commit
97c9664044
|
@ -167,15 +167,15 @@ def trends(tasks_results):
|
|||
|
||||
|
||||
class Trends(object):
|
||||
"""Process tasks results and make trends data.
|
||||
"""Process workloads results and make trends data.
|
||||
|
||||
Group tasks results by their input configuration,
|
||||
Group workloads results by their input configuration,
|
||||
calculate statistics for these groups and prepare it
|
||||
for displaying in trends HTML report.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._tasks = {}
|
||||
self._data = {}
|
||||
|
||||
def _to_str(self, obj):
|
||||
"""Convert object into string."""
|
||||
|
@ -197,29 +197,39 @@ class Trends(object):
|
|||
|
||||
def add_result(self, result):
|
||||
key = self._make_hash(result["key"]["kw"])
|
||||
if key not in self._tasks:
|
||||
name = result["key"]["name"]
|
||||
self._tasks[key] = {"seq": 1,
|
||||
"name": name,
|
||||
"cls": name.split(".")[0],
|
||||
"met": name.split(".")[1],
|
||||
"data": {},
|
||||
"total": None,
|
||||
"atomic": [],
|
||||
"stat": {},
|
||||
"sla_failures": 0,
|
||||
"config": json.dumps(result["key"]["kw"],
|
||||
indent=2)}
|
||||
else:
|
||||
self._tasks[key]["seq"] += 1
|
||||
if key not in self._data:
|
||||
self._data[key] = {
|
||||
"actions": {},
|
||||
"sla_failures": 0,
|
||||
"name": result["key"]["name"],
|
||||
"config": json.dumps(result["key"]["kw"], indent=2)}
|
||||
|
||||
for sla in result["sla"]:
|
||||
self._tasks[key]["sla_failures"] += not sla["success"]
|
||||
self._data[key]["sla_failures"] += not sla["success"]
|
||||
|
||||
task = {row[0]: dict(zip(result["info"]["stat"]["cols"], row))
|
||||
stat = {row[0]: dict(zip(result["info"]["stat"]["cols"], row))
|
||||
for row in result["info"]["stat"]["rows"]}
|
||||
ts = int(result["info"]["tstamp_start"] * 1000)
|
||||
|
||||
for action in stat:
|
||||
# NOTE(amaretskiy): some atomic actions can be missed due to
|
||||
# failures. We can ignore that because we use NVD3 lineChart()
|
||||
# for displaying trends, which is safe for missed points
|
||||
if action not in self._data[key]["actions"]:
|
||||
self._data[key]["actions"][action] = {
|
||||
"durations": {"min": [], "median": [], "90%ile": [],
|
||||
"95%ile": [], "max": [], "avg": []},
|
||||
"success": []}
|
||||
|
||||
try:
|
||||
success = float(stat[action]["Success"].rstrip("%"))
|
||||
except ValueError:
|
||||
# Got "n/a" for some reason
|
||||
success = 0
|
||||
|
||||
self._data[key]["actions"][action]["success"].append(
|
||||
(ts, success))
|
||||
|
||||
for k in task:
|
||||
for tgt, src in (("min", "Min (sec)"),
|
||||
("median", "Median (sec)"),
|
||||
("90%ile", "90%ile (sec)"),
|
||||
|
@ -227,49 +237,44 @@ class Trends(object):
|
|||
("max", "Max (sec)"),
|
||||
("avg", "Avg (sec)")):
|
||||
|
||||
# NOTE(amaretskiy): some atomic actions can be
|
||||
# missed due to failures. We can ignore that
|
||||
# because we use NVD3 lineChart() for displaying
|
||||
# trends, which is safe for missed points
|
||||
if k not in self._tasks[key]["data"]:
|
||||
self._tasks[key]["data"][k] = {"min": [],
|
||||
"median": [],
|
||||
"90%ile": [],
|
||||
"95%ile": [],
|
||||
"max": [],
|
||||
"avg": [],
|
||||
"success": []}
|
||||
self._tasks[key]["data"][k][tgt].append(
|
||||
(self._tasks[key]["seq"], task[k][src]))
|
||||
|
||||
try:
|
||||
success = float(task[k]["Success"].rstrip("%"))
|
||||
except ValueError:
|
||||
# Got "n/a" for some reason
|
||||
success = 0
|
||||
self._tasks[key]["data"][k]["success"].append(
|
||||
(self._tasks[key]["seq"], success))
|
||||
self._data[key]["actions"][action]["durations"][tgt].append(
|
||||
(ts, stat[action][src]))
|
||||
|
||||
def get_data(self):
|
||||
for key, value in self._tasks.items():
|
||||
total = None
|
||||
for k, v in value["data"].items():
|
||||
success = [("success", v.pop("success"))]
|
||||
if k == "total":
|
||||
total = {"values": v, "success": success}
|
||||
trends = []
|
||||
|
||||
for wload in self._data.values():
|
||||
trend = {"stat": {},
|
||||
"name": wload["name"],
|
||||
"cls": wload["name"].split(".")[0],
|
||||
"met": wload["name"].split(".")[1],
|
||||
"sla_failures": wload["sla_failures"],
|
||||
"config": wload["config"],
|
||||
"actions": []}
|
||||
|
||||
for action, data in wload["actions"].items():
|
||||
action_durs = [(k, sorted(v))
|
||||
for k, v in data["durations"].items()]
|
||||
if action == "total":
|
||||
trend.update(
|
||||
{"length": len(data["success"]),
|
||||
"durations": action_durs,
|
||||
"success": [("success", sorted(data["success"]))]})
|
||||
else:
|
||||
self._tasks[key]["atomic"].append(
|
||||
{"name": k, "values": list(v.items()),
|
||||
"success": success})
|
||||
trend["actions"].append(
|
||||
{"name": action,
|
||||
"durations": action_durs,
|
||||
"success": [("success", sorted(data["success"]))]})
|
||||
|
||||
for stat, comp in (("min", charts.streaming.MinComputation()),
|
||||
("max", charts.streaming.MaxComputation()),
|
||||
("avg", charts.streaming.MeanComputation())):
|
||||
for k, v in total["values"][stat]:
|
||||
if isinstance(v, (float,) + six.integer_types):
|
||||
comp.add(v)
|
||||
self._tasks[key]["stat"][stat] = comp.result()
|
||||
del self._tasks[key]["data"]
|
||||
total["values"] = list(total["values"].items())
|
||||
self._tasks[key]["total"] = total
|
||||
self._tasks[key]["single"] = self._tasks[key]["seq"] < 2
|
||||
return sorted(self._tasks.values(), key=lambda s: s["name"])
|
||||
for k, v in trend["durations"]:
|
||||
for i in v:
|
||||
if isinstance(i[1], (float,) + six.integer_types):
|
||||
comp.add(i[1])
|
||||
trend["stat"][stat] = comp.result()
|
||||
|
||||
trends.append(trend)
|
||||
|
||||
return sorted(trends, key=lambda i: i["name"])
|
||||
|
|
|
@ -73,9 +73,9 @@ var widgetDirective = function($compile) {
|
|||
.showControls(opts.controls)
|
||||
.clipEdge(true);
|
||||
chart.xAxis
|
||||
.tickFormat(d3.format(opts.xformat || "d"))
|
||||
.axisLabel(opts.xname || "")
|
||||
.showMaxMin(false);
|
||||
.axisLabel(opts.xname)
|
||||
.tickFormat(opts.xformat)
|
||||
.showMaxMin(opts.showmaxmin);
|
||||
chart.yAxis
|
||||
.orient("left")
|
||||
.tickFormat(d3.format(opts.yformat || ",.3f"));
|
||||
|
@ -92,9 +92,10 @@ var widgetDirective = function($compile) {
|
|||
.useInteractiveGuideline(opts.guide)
|
||||
.clipEdge(true);
|
||||
chart.xAxis
|
||||
.tickFormat(d3.format(opts.xformat || "d"))
|
||||
.axisLabel(opts.xname || "")
|
||||
.showMaxMin(false);
|
||||
.axisLabel(opts.xname)
|
||||
.tickFormat(opts.xformat)
|
||||
.rotateLabels(opts.xrotate)
|
||||
.showMaxMin(opts.showmaxmin);
|
||||
chart.yAxis
|
||||
.orient("left")
|
||||
.tickFormat(d3.format(opts.yformat || ",.3f"));
|
||||
|
@ -184,14 +185,20 @@ var widgetDirective = function($compile) {
|
|||
}
|
||||
}
|
||||
|
||||
var options = {
|
||||
var opts = {
|
||||
xname: attrs.nameX || "",
|
||||
xformat: attrs.formatX || "d",
|
||||
xrotate: attrs.rotateX || 0,
|
||||
yformat: attrs.formatY || ",.3f",
|
||||
controls: attrs.controls === "true",
|
||||
guide: attrs.guide === "true"
|
||||
guide: attrs.guide === "true",
|
||||
showmaxmin: attrs.showmaxmin === "true"
|
||||
};
|
||||
Chart.get_chart(attrs.widget)(el, data, options, do_after);
|
||||
if (attrs.formatDateX) {
|
||||
opts.xformat = function(d) { return d3.time.format(attrs.formatDateX)(new Date(d)) }
|
||||
} else {
|
||||
opts.xformat = d3.format(attrs.formatX || "d")
|
||||
}
|
||||
Chart.get_chart(attrs.widget)(el, data, opts, do_after);
|
||||
}
|
||||
|
||||
if (attrs.nameY) {
|
||||
|
|
|
@ -97,9 +97,9 @@
|
|||
name: "Total",
|
||||
visible: function(){ return true }
|
||||
}, {
|
||||
id: "atomic",
|
||||
id: "actions",
|
||||
name: "Atomic actions",
|
||||
visible: function(){ return (! $scope.wload.single) && $scope.wload.atomic.length }
|
||||
visible: function(){ return ($scope.wload.length !== 1) && $scope.wload.actions.length }
|
||||
}, {
|
||||
id: "config",
|
||||
name: "Configuration",
|
||||
|
@ -134,6 +134,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Other helpers */
|
||||
|
||||
$scope.showError = function(message) {
|
||||
return (function (e) {
|
||||
e.style.display = "block";
|
||||
|
@ -173,8 +175,7 @@
|
|||
w.order_idx = itr > 1 ? " ["+itr+"]" : ""
|
||||
$scope.wload_map[w.ref] = w;
|
||||
$scope.nav_map[w.ref] = cls_idx;
|
||||
met.push({name:w.met, itr:itr, idx:idx, order_idx:w.order_idx,
|
||||
ref:w.ref, single:w.single});
|
||||
met.push({name:w.met, itr:itr, idx:idx, order_idx:w.order_idx, ref:w.ref});
|
||||
prev_met = w.met;
|
||||
itr += 1;
|
||||
}
|
||||
|
@ -271,7 +272,7 @@
|
|||
<div class="navmet"
|
||||
title="{{m.name}}{{m.order_idx}}"
|
||||
ng-show="n.idx==nav_idx"
|
||||
ng-class="{active:wload && m.ref==wload.ref, single:m.single}"
|
||||
ng-class="{active:wload && m.ref==wload.ref, single:m.length === 1}"
|
||||
ng-click="location.path(m.ref)"
|
||||
ng-repeat="m in n.met track by $index"
|
||||
ng-repeat-end>{{m.name}}{{m.order_idx}}</div>
|
||||
|
@ -336,18 +337,18 @@
|
|||
<tbody>
|
||||
<tr ng-repeat="w in data | orderBy:ov_srt:ov_dir"
|
||||
ng-click="location.path(w.ref)"
|
||||
ng-class="{single:w.single}">
|
||||
ng-class="{single:w.length === 1}">
|
||||
<td>{{w.ref}}
|
||||
<td>{{w.seq}}
|
||||
<td>{{w.length}}
|
||||
<td>
|
||||
<span ng-if="w.single">-</span>
|
||||
<span ng-if="!w.single">{{w.stat.min | number:4}}</span>
|
||||
<span ng-if="w.length === 1">-</span>
|
||||
<span ng-if="w.length !== 1">{{w.stat.min | number:4}}</span>
|
||||
<td>
|
||||
<span ng-if="w.single">-</span>
|
||||
<span ng-if="!w.single">{{w.stat.max | number:4}}</span>
|
||||
<span ng-if="w.length === 1">-</span>
|
||||
<span ng-if="w.length !== 1">{{w.stat.max | number:4}}</span>
|
||||
<td>
|
||||
<span ng-if="w.single">-</span>
|
||||
<span ng-if="!w.single">{{w.stat.avg | number:4}}</span>
|
||||
<span ng-if="w.length === 1">-</span>
|
||||
<span ng-if="w.length !== 1">{{w.stat.avg | number:4}}</span>
|
||||
<td title="{{w.sla_failures ? 'Failures: ' + w.sla_failures : 'No failures'}}">
|
||||
<span ng-hide="w.sla_failures" class="status-pass">✔</span>
|
||||
<span ng-show="w.sla_failures" class="status-fail">✖</span>
|
||||
|
@ -371,46 +372,58 @@
|
|||
<div ng-include="tab"></div>
|
||||
|
||||
<script type="text/ng-template" id="total">
|
||||
<div ng-if="wload.single">
|
||||
<div ng-if="wload.length === 1">
|
||||
<div style="margin:30px 0 10px; font-size:14px; color:#ff6622">
|
||||
This workload has single run so trends can not be displayed.<br>
|
||||
There should be at least two workload results with the same configuration
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!wload.single">
|
||||
<div ng-if="wload.length !== 1">
|
||||
<h2>Total durations</h2>
|
||||
<div widget="Lines"
|
||||
data="wload.total.values"
|
||||
name-x="Task run sequence number"
|
||||
data="wload.durations"
|
||||
controls="true"
|
||||
guide="true">
|
||||
guide="true"
|
||||
showmaxmin="true"
|
||||
rotate-x="-70"
|
||||
format-date-x="%Y-%m-%d %H:%M:%S"
|
||||
style="height:370px">
|
||||
</div>
|
||||
<h2>Total success rate</h2>
|
||||
<div widget="Lines"
|
||||
data="wload.total.success"
|
||||
name-x="Task run sequence number"
|
||||
data="wload.success"
|
||||
controls="true"
|
||||
guide="true">
|
||||
guide="true"
|
||||
showmaxmin="true"
|
||||
rotate-x="-70"
|
||||
format-date-x="%Y-%m-%d %H:%M:%S"
|
||||
style="height:370px">
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="atomic">
|
||||
<script type="text/ng-template" id="actions">
|
||||
<h2>Atomic actions durations / success rate</h2>
|
||||
<div ng-repeat="chart in wload.atomic track by $index">
|
||||
<div ng-repeat="chart in wload.actions track by $index">
|
||||
<span id="{{chart.name}}"></span>
|
||||
<div class="chart-title">{{chart.name}}</div>
|
||||
<div widget="Lines"
|
||||
data="chart.values"
|
||||
name-x="Task run sequence number"
|
||||
data="chart.durations"
|
||||
controls="true"
|
||||
guide="true">
|
||||
guide="true"
|
||||
showmaxmin="true"
|
||||
rotate-x="-70"
|
||||
format-date-x="%Y-%m-%d %H:%M:%S"
|
||||
style="height:370px">
|
||||
</div>
|
||||
<div widget="Lines"
|
||||
data="chart.success"
|
||||
name-x="Task run sequence number"
|
||||
controls="true"
|
||||
guide="true">
|
||||
guide="true"
|
||||
showmaxmin="true"
|
||||
rotate-x="-70"
|
||||
format-date-x="%Y-%m-%d %H:%M:%S"
|
||||
style="height:400px">
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
|
@ -178,7 +178,7 @@ class TrendsTestCase(test.TestCase):
|
|||
|
||||
def test___init__(self):
|
||||
trends = plot.Trends()
|
||||
self.assertEqual({}, trends._tasks)
|
||||
self.assertEqual({}, trends._data)
|
||||
self.assertRaises(TypeError, plot.Trends, 42)
|
||||
|
||||
@ddt.data({"args": [None], "result": "None"},
|
||||
|
@ -227,11 +227,12 @@ class TrendsTestCase(test.TestCase):
|
|||
atomic = {"a": 123, "b": 456}
|
||||
stat_rows = [["a", 0.7, 0.85, 0.9, 0.87, 1.25, 0.67, "100.0%", 4],
|
||||
["b", 0.5, 0.75, 0.85, 0.9, 1.1, 0.58, "100.0%", 4],
|
||||
["total", 1.2, 1.55, 1.7, 1.9, 1.5, 1.6, "100.0%", 4]]
|
||||
["total", 1.2, 1.55, 1.7, 1.8, 1.5, 0.8, "100.0%", 4]]
|
||||
return {
|
||||
"key": {"kw": salt + "_kw", "name": "Scenario.name_%s" % salt},
|
||||
"key": {"kw": "kw_%d" % salt, "name": "Scenario.name_%d" % salt},
|
||||
"sla": [{"success": sla_success}],
|
||||
"info": {"iterations_count": 4, "atomic": atomic,
|
||||
"tstamp_start": 123456.789 + salt,
|
||||
"stat": {"rows": stat_rows,
|
||||
"cols": ["Action", "Min (sec)", "Median (sec)",
|
||||
"90%ile (sec)", "95%ile (sec)",
|
||||
|
@ -240,120 +241,151 @@ class TrendsTestCase(test.TestCase):
|
|||
"iterations": ["<iter-0>", "<iter-1>", "<iter-2>", "<iter-3>"]}
|
||||
|
||||
def _sort_trends(self, trends_result):
|
||||
for r_idx, res in enumerate(trends_result):
|
||||
trends_result[r_idx]["total"]["values"].sort()
|
||||
for a_idx, dummy in enumerate(res["atomic"]):
|
||||
trends_result[r_idx]["atomic"][a_idx]["values"].sort()
|
||||
for idx in range(len(trends_result)):
|
||||
trends_result[idx]["durations"].sort()
|
||||
for a_idx in range(len(trends_result[idx]["actions"])):
|
||||
trends_result[idx]["actions"][a_idx]["durations"].sort()
|
||||
return trends_result
|
||||
|
||||
def test_add_result_and_get_data(self):
|
||||
trends = plot.Trends()
|
||||
for i in 0, 1:
|
||||
trends.add_result(self._make_result(str(i)))
|
||||
trends.add_result(self._make_result(i))
|
||||
expected = [
|
||||
{"atomic": [
|
||||
{"name": "a",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.9)]), ("95%ile", [(1, 0.87)]),
|
||||
("avg", [(1, 0.67)]), ("max", [(1, 1.25)]),
|
||||
("median", [(1, 0.85)]), ("min", [(1, 0.7)])]},
|
||||
{"name": "b",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.85)]), ("95%ile", [(1, 0.9)]),
|
||||
("avg", [(1, 0.58)]), ("max", [(1, 1.1)]),
|
||||
("median", [(1, 0.75)]), ("min", [(1, 0.5)])]}],
|
||||
"cls": "Scenario", "config": "\"0_kw\"", "met": "name_0",
|
||||
"name": "Scenario.name_0", "seq": 1, "single": True,
|
||||
"sla_failures": 0, "stat": {"avg": 1.6, "max": 1.5, "min": 1.2},
|
||||
"total": {"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 1.7)]),
|
||||
("95%ile", [(1, 1.9)]),
|
||||
("avg", [(1, 1.6)]),
|
||||
("max", [(1, 1.5)]),
|
||||
("median", [(1, 1.55)]),
|
||||
("min", [(1, 1.2)])]}},
|
||||
{"atomic": [
|
||||
{"name": "a",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.9)]), ("95%ile", [(1, 0.87)]),
|
||||
("avg", [(1, 0.67)]), ("max", [(1, 1.25)]),
|
||||
("median", [(1, 0.85)]), ("min", [(1, 0.7)])]},
|
||||
{"name": "b",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.85)]), ("95%ile", [(1, 0.9)]),
|
||||
("avg", [(1, 0.58)]), ("max", [(1, 1.1)]),
|
||||
("median", [(1, 0.75)]), ("min", [(1, 0.5)])]}],
|
||||
"cls": "Scenario", "config": "\"1_kw\"", "met": "name_1",
|
||||
"name": "Scenario.name_1", "seq": 1, "single": True,
|
||||
"sla_failures": 0, "stat": {"avg": 1.6, "max": 1.5, "min": 1.2},
|
||||
"total": {"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 1.7)]),
|
||||
("95%ile", [(1, 1.9)]),
|
||||
("avg", [(1, 1.6)]),
|
||||
("max", [(1, 1.5)]),
|
||||
("median", [(1, 1.55)]),
|
||||
("min", [(1, 1.2)])]}}]
|
||||
{"actions": [{"durations": [("90%ile", [(123456789, 0.9)]),
|
||||
("95%ile", [(123456789, 0.87)]),
|
||||
("avg", [(123456789, 0.67)]),
|
||||
("max", [(123456789, 1.25)]),
|
||||
("median", [(123456789, 0.85)]),
|
||||
("min", [(123456789, 0.7)])],
|
||||
"name": "a",
|
||||
"success": [("success", [(123456789, 100.0)])]},
|
||||
{"durations": [("90%ile", [(123456789, 0.85)]),
|
||||
("95%ile", [(123456789, 0.9)]),
|
||||
("avg", [(123456789, 0.58)]),
|
||||
("max", [(123456789, 1.1)]),
|
||||
("median", [(123456789, 0.75)]),
|
||||
("min", [(123456789, 0.5)])],
|
||||
"name": "b",
|
||||
"success": [("success", [(123456789, 100.0)])]}],
|
||||
"cls": "Scenario",
|
||||
"config": "\"kw_0\"",
|
||||
"durations": [("90%ile", [(123456789, 1.7)]),
|
||||
("95%ile", [(123456789, 1.8)]),
|
||||
("avg", [(123456789, 0.8)]),
|
||||
("max", [(123456789, 1.5)]),
|
||||
("median", [(123456789, 1.55)]),
|
||||
("min", [(123456789, 1.2)])],
|
||||
"length": 1,
|
||||
"met": "name_0",
|
||||
"name": "Scenario.name_0",
|
||||
"sla_failures": 0,
|
||||
"stat": {"avg": 1.425, "max": 1.8, "min": 0.8},
|
||||
"success": [("success", [(123456789, 100.0)])]},
|
||||
{"actions": [{"durations": [("90%ile", [(123457789, 0.9)]),
|
||||
("95%ile", [(123457789, 0.87)]),
|
||||
("avg", [(123457789, 0.67)]),
|
||||
("max", [(123457789, 1.25)]),
|
||||
("median", [(123457789, 0.85)]),
|
||||
("min", [(123457789, 0.7)])],
|
||||
"name": "a",
|
||||
"success": [("success", [(123457789, 100.0)])]},
|
||||
{"durations": [("90%ile", [(123457789, 0.85)]),
|
||||
("95%ile", [(123457789, 0.9)]),
|
||||
("avg", [(123457789, 0.58)]),
|
||||
("max", [(123457789, 1.1)]),
|
||||
("median", [(123457789, 0.75)]),
|
||||
("min", [(123457789, 0.5)])],
|
||||
"name": "b",
|
||||
"success": [("success", [(123457789, 100.0)])]}],
|
||||
"cls": "Scenario",
|
||||
"config": "\"kw_1\"",
|
||||
"durations": [("90%ile", [(123457789, 1.7)]),
|
||||
("95%ile", [(123457789, 1.8)]),
|
||||
("avg", [(123457789, 0.8)]),
|
||||
("max", [(123457789, 1.5)]),
|
||||
("median", [(123457789, 1.55)]),
|
||||
("min", [(123457789, 1.2)])],
|
||||
"length": 1,
|
||||
"met": "name_1",
|
||||
"name": "Scenario.name_1",
|
||||
"sla_failures": 0,
|
||||
"stat": {"avg": 1.425, "max": 1.8, "min": 0.8},
|
||||
"success": [("success", [(123457789, 100.0)])]}]
|
||||
self.assertEqual(expected, self._sort_trends(trends.get_data()))
|
||||
|
||||
def test_add_result_once_and_get_data(self):
|
||||
trends = plot.Trends()
|
||||
trends.add_result(self._make_result("foo", sla_success=False))
|
||||
trends.add_result(self._make_result(42, sla_success=False))
|
||||
expected = [
|
||||
{"atomic": [
|
||||
{"name": "a",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.9)]), ("95%ile", [(1, 0.87)]),
|
||||
("avg", [(1, 0.67)]), ("max", [(1, 1.25)]),
|
||||
("median", [(1, 0.85)]), ("min", [(1, 0.7)])]},
|
||||
{"name": "b",
|
||||
"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 0.85)]), ("95%ile", [(1, 0.9)]),
|
||||
("avg", [(1, 0.58)]), ("max", [(1, 1.1)]),
|
||||
("median", [(1, 0.75)]), ("min", [(1, 0.5)])]}],
|
||||
"cls": "Scenario", "config": "\"foo_kw\"", "met": "name_foo",
|
||||
"name": "Scenario.name_foo", "seq": 1, "single": True,
|
||||
"sla_failures": 1, "stat": {"avg": 1.6, "max": 1.5, "min": 1.2},
|
||||
"total": {"success": [("success", [(1, 100.0)])],
|
||||
"values": [("90%ile", [(1, 1.7)]),
|
||||
("95%ile", [(1, 1.9)]),
|
||||
("avg", [(1, 1.6)]),
|
||||
("max", [(1, 1.5)]),
|
||||
("median", [(1, 1.55)]),
|
||||
("min", [(1, 1.2)])]}}]
|
||||
{"actions": [{"durations": [("90%ile", [(123498789, 0.9)]),
|
||||
("95%ile", [(123498789, 0.87)]),
|
||||
("avg", [(123498789, 0.67)]),
|
||||
("max", [(123498789, 1.25)]),
|
||||
("median", [(123498789, 0.85)]),
|
||||
("min", [(123498789, 0.7)])],
|
||||
"name": "a",
|
||||
"success": [("success", [(123498789, 100.0)])]},
|
||||
{"durations": [("90%ile", [(123498789, 0.85)]),
|
||||
("95%ile", [(123498789, 0.9)]),
|
||||
("avg", [(123498789, 0.58)]),
|
||||
("max", [(123498789, 1.1)]),
|
||||
("median", [(123498789, 0.75)]),
|
||||
("min", [(123498789, 0.5)])],
|
||||
"name": "b",
|
||||
"success": [("success", [(123498789, 100.0)])]}],
|
||||
"cls": "Scenario",
|
||||
"config": "\"kw_42\"",
|
||||
"durations": [("90%ile", [(123498789, 1.7)]),
|
||||
("95%ile", [(123498789, 1.8)]),
|
||||
("avg", [(123498789, 0.8)]),
|
||||
("max", [(123498789, 1.5)]),
|
||||
("median", [(123498789, 1.55)]),
|
||||
("min", [(123498789, 1.2)])],
|
||||
"length": 1,
|
||||
"met": "name_42",
|
||||
"name": "Scenario.name_42",
|
||||
"sla_failures": 1,
|
||||
"stat": {"avg": 1.425, "max": 1.8, "min": 0.8},
|
||||
"success": [("success", [(123498789, 100.0)])]}]
|
||||
self.assertEqual(expected, self._sort_trends(trends.get_data()))
|
||||
|
||||
def test_add_result_with_na_and_get_data(self):
|
||||
trends = plot.Trends()
|
||||
trends.add_result(self._make_result("foo",
|
||||
sla_success=False, with_na=True))
|
||||
trends.add_result(
|
||||
self._make_result(42, sla_success=False, with_na=True))
|
||||
expected = [
|
||||
{"atomic": [{"name": "a",
|
||||
"success": [("success", [(1, 0)])],
|
||||
"values": [("90%ile", [(1, "n/a")]),
|
||||
("95%ile", [(1, "n/a")]),
|
||||
("avg", [(1, "n/a")]),
|
||||
("max", [(1, "n/a")]),
|
||||
("median", [(1, "n/a")]),
|
||||
("min", [(1, "n/a")])]},
|
||||
{"name": "b",
|
||||
"success": [("success", [(1, 0)])],
|
||||
"values": [("90%ile", [(1, "n/a")]),
|
||||
("95%ile", [(1, "n/a")]),
|
||||
("avg", [(1, "n/a")]),
|
||||
("max", [(1, "n/a")]),
|
||||
("median", [(1, "n/a")]),
|
||||
("min", [(1, "n/a")])]}],
|
||||
"cls": "Scenario", "config": "\"foo_kw\"", "met": "name_foo",
|
||||
"name": "Scenario.name_foo", "seq": 1, "single": True,
|
||||
"sla_failures": 1, "stat": {"avg": None, "max": None,
|
||||
"min": None},
|
||||
"total": {"success": [("success", [(1, 0)])],
|
||||
"values": [("90%ile", [(1, "n/a")]),
|
||||
("95%ile", [(1, "n/a")]),
|
||||
("avg", [(1, "n/a")]),
|
||||
("max", [(1, "n/a")]),
|
||||
("median", [(1, "n/a")]),
|
||||
("min", [(1, "n/a")])]}}]
|
||||
{"actions": [{"durations": [("90%ile", [(123498789, "n/a")]),
|
||||
("95%ile", [(123498789, "n/a")]),
|
||||
("avg", [(123498789, "n/a")]),
|
||||
("max", [(123498789, "n/a")]),
|
||||
("median", [(123498789, "n/a")]),
|
||||
("min", [(123498789, "n/a")])],
|
||||
"name": "a",
|
||||
"success": [("success", [(123498789, 0)])]},
|
||||
{"durations": [("90%ile", [(123498789, "n/a")]),
|
||||
("95%ile", [(123498789, "n/a")]),
|
||||
("avg", [(123498789, "n/a")]),
|
||||
("max", [(123498789, "n/a")]),
|
||||
("median", [(123498789, "n/a")]),
|
||||
("min", [(123498789, "n/a")])],
|
||||
"name": "b",
|
||||
"success": [("success", [(123498789, 0)])]}],
|
||||
"cls": "Scenario",
|
||||
"config": "\"kw_42\"",
|
||||
"durations": [("90%ile", [(123498789, "n/a")]),
|
||||
("95%ile", [(123498789, "n/a")]),
|
||||
("avg", [(123498789, "n/a")]),
|
||||
("max", [(123498789, "n/a")]),
|
||||
("median", [(123498789, "n/a")]),
|
||||
("min", [(123498789, "n/a")])],
|
||||
"length": 1,
|
||||
"met": "name_42",
|
||||
"name": "Scenario.name_42",
|
||||
"sla_failures": 1,
|
||||
"stat": {"avg": None, "max": None, "min": None},
|
||||
"success": [("success", [(123498789, 0)])]}]
|
||||
|
||||
self.assertEqual(expected, self._sort_trends(trends.get_data()))
|
||||
|
||||
def test_get_data_no_results_added(self):
|
||||
|
|
Loading…
Reference in New Issue