# Copyright 2014: Mirantis Inc. # 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. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License 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. import json import sys import mock import testtools from rally.benchmark.processing import plot from tests.unit import test PLOT = "rally.benchmark.processing.plot." class PlotTestCase(test.TestCase): @mock.patch(PLOT + "ui_utils") @mock.patch(PLOT + "_process_results") def test_plot(self, mock_proc_results, mock_utils): mock_render = mock.Mock(return_value="plot_html") mock_utils.get_template = mock.Mock( return_value=mock.Mock(render=mock_render)) task_data = [{"name": "a"}, {"name": "b"}] task_source = "JSON" mock_proc_results.return_value = (task_source, task_data) result = plot.plot(["abc"]) self.assertEqual(result, "plot_html") mock_render.assert_called_once_with( data=json.dumps(task_data), source=json.dumps(task_source) ) mock_utils.get_template.assert_called_once_with("task/report.mako") @mock.patch(PLOT + "json.dumps") @mock.patch(PLOT + "_prepare_data") @mock.patch(PLOT + "_process_atomic") @mock.patch(PLOT + "_get_atomic_action_durations") @mock.patch(PLOT + "_process_main_duration") def test__process_results(self, mock_main_duration, mock_get_atomic, mock_atomic, mock_prepare, mock_dumps): sla = [{"success": True}] result = ["iter_1", "iter_2"] iterations = len(result) kw = {"runner": {"type": "foo_runner"}} result_ = lambda i: { "key": {"pos": i, "name": "Class.method", "kw": kw}, "result": result, "sla": sla} results = [result_(i) for i in (0, 1, 2)] table_cols = ["Action", "Min (sec)", "Median (sec)", "90%ile (sec)", "95%ile (sec)", "Max (sec)", "Avg (sec)", "Success", "Count"] atomic_durations = [["atomic_1"], ["atomic_2"]] mock_prepare.side_effect = lambda i: {"errors": "errors_list", "output": [], "output_errors": [], "sla": i["sla"], "load_duration": 1234.5, "full_duration": 6789.1} mock_main_duration.return_value = "main_duration" mock_get_atomic.return_value = atomic_durations mock_atomic.return_value = "main_atomic" mock_dumps.return_value = "JSON" source, scenarios = plot._process_results(results) source_dict = {"Class.method": [kw] * len(results)} mock_dumps.assert_called_with(source_dict, indent=2, sort_keys=True) self.assertEqual(source, "JSON") results = sorted(results, key=lambda r: "%s%s" % (r["key"]["name"], r["key"]["pos"])) for i, r in enumerate(results): config = json.dumps({r["key"]["name"]: [r["key"]["kw"]]}, indent=2) pos = int(r["key"]["pos"]) cls = r["key"]["name"].split(".")[0] met = r["key"]["name"].split(".")[1] name = "%s%s" % (met, (pos and " [%d]" % (pos + 1) or "")) self.assertEqual(scenarios[i], { "cls": cls, "pos": r["key"]["pos"], "met": met, "name": name, "config": config, "iterations": mock_main_duration.return_value, "atomic": mock_atomic.return_value, "table_cols": table_cols, "table_rows": atomic_durations, "errors": "errors_list", "output": [], "output_errors": [], "runner": "foo_runner", "sla": sla, "sla_success": True, "iterations_num": iterations, "load_duration": 1234.5, "full_duration": 6789.1 }) @testtools.skipIf(sys.version_info > (2, 9), "Problems with floating data") def test__process_main_time(self): result = { "result": [ { "error": [], "duration": 1, "idle_duration": 2, "atomic_actions": {}, "scenario_output": {"errors": [], "data": {}} }, { "error": ["some", "error", "occurred"], "duration": 1, "idle_duration": 1, "atomic_actions": {}, "scenario_output": {"errors": [], "data": {}} }, { "error": [], "duration": 2, "idle_duration": 3, "atomic_actions": {}, "scenario_output": {"errors": [], "data": {}} } ], "sla": "foo_sla", "load_duration": 1234.5, "full_duration": 6789.1 } output = plot._process_main_duration(result, plot._prepare_data(result)) self.assertEqual({ "pie": [ {"key": "success", "value": 2}, {"key": "errors", "value": 1} ], "iter": [ { "key": "duration", "values": [(1, 1.0), (2, 0), (3, 2.0)] }, { "key": "idle_duration", "values": [(1, 2.0), (2, 0), (3, 3.0)] } ], "histogram": [ { "key": "task", "method": "Square Root Choice", "values": [{"x": 1.0, "y": 1.0}, {"x": 1.0, "y": 0.0}] }, { "key": "task", "method": "Sturges Formula", "values": [{"x": 1.0, "y": 1.0}, {"x": 1.0, "y": 0.0}] }, { "key": "task", "method": "Rice Rule", "values": [{"x": 1.0, "y": 1.0}, {"x": 1.0, "y": 0.0}, {"x": 1.0, "y": 0.0}] }, { "key": "task", "method": "One Half", "values": [{"x": 2.0, "y": 2.0}] } ] }, output) @testtools.skipIf(sys.version_info > (2, 9), "Problems with floating data") def test__process_atomic_time(self): result = { "result": [ { "error": [], "atomic_actions": { "action1": 1, "action2": 2 }, "scenario_output": {"errors": [], "data": {}} }, { "error": ["some", "error", "occurred"], "atomic_actions": { "action1": 1, "action2": 2 }, "scenario_output": {"errors": [], "data": {}} }, { "error": [], "atomic_actions": { "action1": 3, "action2": 4 }, "scenario_output": {"errors": [], "data": {}} } ] } data = { "atomic_durations": { "action1": [(1, 1.0), (2, 0.0), (3, 3.0)], "action2": [(1, 2.0), (2, 0.0), (3, 4.0)]}} output = plot._process_atomic(result, data) self.assertEqual({ "histogram": [ [ { "key": "action1", "disabled": 0, "method": "Square Root Choice", "values": [{"x": 2, "y": 1}, {"x": 3, "y": 1}] }, { "key": "action1", "disabled": 0, "method": "Sturges Formula", "values": [{"x": 2, "y": 1}, {"x": 3, "y": 1}] }, { "key": "action1", "disabled": 0, "method": "Rice Rule", "values": [{"x": 1, "y": 1}, {"x": 1, "y": 0}, {"x": 1, "y": 0}] }, { "key": "action1", "disabled": 0, "method": "One Half", "values": [{"x": 3, "y": 2}] }, ], [ { "key": "action2", "disabled": 1, "method": "Square Root Choice", "values": [{"x": 3, "y": 1}, {"x": 4, "y": 1}] }, { "key": "action2", "disabled": 1, "method": "Sturges Formula", "values": [{"x": 3, "y": 1}, {"x": 4, "y": 1}] }, { "key": "action2", "disabled": 1, "method": "Rice Rule", "values": [{"x": 2, "y": 1}, {"x": 2, "y": 0}, {"x": 2, "y": 0}] }, { "key": "action2", "disabled": 1, "method": "One Half", "values": [{"x": 4, "y": 2}] } ] ], "pie": [ {"key": "action1", "value": 2.0}, {"key": "action2", "value": 3.0} ], "iter": [ { "key": "action1", "values": [(1, 1.), (2, 0.), (3, 3.)] }, { "key": "action2", "values": [(1, 2.), (2, 0.), (3, 4.)] } ] }, output) @mock.patch("rally.benchmark.processing.utils.compress") def test__prepare_data(self, mock_compress): mock_compress.side_effect = lambda i, **kv: i rows_num = 100 load_duration = 1234.5 full_duration = 6789.1 sla = [{"foo": "bar"}] data = [] for i in range(rows_num): atomic_actions = { "a1": i + 0.1, "a2": i + 0.8, } row = { "duration": i * 3.1, "idle_duration": i * 0.2, "error": [], "atomic_actions": atomic_actions, "scenario_output": {"errors": ["err"], "data": {"out_key": "out_value"}} } data.append(row) data[42]["error"] = ["foo", "bar", "spam"] data[52]["error"] = ["spam", "bar", "foo"] values_atomic_a1 = [i + 0.1 for i in range(rows_num)] values_atomic_a2 = [i + 0.8 for i in range(rows_num)] values_duration = [i * 3.1 for i in range(rows_num)] values_duration[42] = 0 values_duration[52] = 0 values_idle = [i * 0.2 for i in range(rows_num)] values_idle[42] = 0 values_idle[52] = 0 prepared_data = plot._prepare_data({"result": data, "load_duration": load_duration, "full_duration": full_duration, "sla": sla, "key": "foo_key"}) self.assertEqual(2, len(prepared_data["errors"])) calls = [mock.call(values_atomic_a1), mock.call(values_atomic_a2), mock.call(values_duration), mock.call(values_idle)] mock_compress.assert_has_calls(calls) expected_output = [{"key": "out_key", "values": ["out_value"] * rows_num}] expected_output_errors = [(i, [e]) for i, e in enumerate(["err"] * rows_num)] self.assertEqual({ "total_durations": {"duration": values_duration, "idle_duration": values_idle}, "atomic_durations": {"a1": values_atomic_a1, "a2": values_atomic_a2}, "errors": [{"iteration": 42, "message": "bar", "traceback": "spam", "type": "foo"}, {"iteration": 52, "message": "bar", "traceback": "foo", "type": "spam"}], "output": expected_output, "output_errors": expected_output_errors, "load_duration": load_duration, "full_duration": full_duration, "sla": sla, }, prepared_data)