| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2016 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Test for generate_report.py.""" |
| |
| |
| import copy |
| import json |
| import unittest |
| import unittest.mock as mock |
| |
| import generate_report |
| import results_report |
| import test_flag |
| |
| |
| # pylint: disable=deprecated-module |
| try: |
| from StringIO import StringIO # for Python 2 |
| except ImportError: |
| from io import StringIO # for Python 3 |
| |
| |
| class _ContextualStringIO(StringIO): |
| """StringIO that can be used in `with` statements.""" |
| |
| def __init__(self, *args): |
| StringIO.__init__(self, *args) |
| |
| def __enter__(self): |
| return self |
| |
| def __exit__(self, _type, _value, _traceback): |
| pass |
| |
| |
| class GenerateReportTests(unittest.TestCase): |
| """Tests for generate_report.py.""" |
| |
| def testCountBenchmarks(self): |
| runs = { |
| "foo": [[{}, {}, {}], [{}, {}, {}, {}]], |
| "bar": [], |
| "baz": [[], [{}], [{}, {}, {}]], |
| } |
| results = generate_report.CountBenchmarks(runs) |
| expected_results = [("foo", 4), ("bar", 0), ("baz", 3)] |
| self.assertCountEqual(expected_results, results) |
| |
| def testCutResultsInPlace(self): |
| bench_data = { |
| "foo": [[{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 2.5, "c": 1}]], |
| "bar": [[{"d": 11, "e": 12, "f": 13}]], |
| "baz": [[{"g": 12, "h": 13}]], |
| "qux": [[{"i": 11}]], |
| } |
| original_bench_data = copy.deepcopy(bench_data) |
| |
| max_keys = 2 |
| results = generate_report.CutResultsInPlace( |
| bench_data, max_keys=max_keys, complain_on_update=False |
| ) |
| # Cuts should be in-place. |
| self.assertIs(results, bench_data) |
| self.assertCountEqual( |
| list(original_bench_data.keys()), list(bench_data.keys()) |
| ) |
| for bench_name, original_runs in original_bench_data.items(): |
| bench_runs = bench_data[bench_name] |
| self.assertEqual(len(original_runs), len(bench_runs)) |
| # Order of these sub-lists shouldn't have changed. |
| for original_list, new_list in zip(original_runs, bench_runs): |
| self.assertEqual(len(original_list), len(new_list)) |
| for original_keyvals, sub_keyvals in zip( |
| original_list, new_list |
| ): |
| # sub_keyvals must be a subset of original_keyvals |
| self.assertDictContainsSubset(sub_keyvals, original_keyvals) |
| |
| def testCutResultsInPlaceLeavesRetval(self): |
| bench_data = { |
| "foo": [[{"retval": 0, "a": 1}]], |
| "bar": [[{"retval": 1}]], |
| "baz": [[{"RETVAL": 1}]], |
| } |
| results = generate_report.CutResultsInPlace( |
| bench_data, max_keys=0, complain_on_update=False |
| ) |
| # Just reach into results assuming we know it otherwise outputs things in |
| # the expected way. If it doesn't, testCutResultsInPlace should give an |
| # indication as to what, exactly, is broken. |
| self.assertEqual(list(results["foo"][0][0].items()), [("retval", 0)]) |
| self.assertEqual(list(results["bar"][0][0].items()), [("retval", 1)]) |
| self.assertEqual(list(results["baz"][0][0].items()), []) |
| |
| def _RunMainWithInput(self, args, input_obj): |
| assert "-i" not in args |
| args += ["-i", "-"] |
| input_buf = _ContextualStringIO(json.dumps(input_obj)) |
| with mock.patch( |
| "generate_report.PickInputFile", return_value=input_buf |
| ) as patched_pick: |
| result = generate_report.Main(args) |
| patched_pick.assert_called_once_with("-") |
| return result |
| |
| @mock.patch("generate_report.RunActions") |
| def testMain(self, mock_run_actions): |
| # Email is left out because it's a bit more difficult to test, and it'll be |
| # mildly obvious if it's failing. |
| args = ["--json", "--html", "--text"] |
| return_code = self._RunMainWithInput( |
| args, {"platforms": [], "data": {}} |
| ) |
| self.assertEqual(0, return_code) |
| self.assertEqual(mock_run_actions.call_count, 1) |
| ctors = [ctor for ctor, _ in mock_run_actions.call_args[0][0]] |
| self.assertEqual( |
| ctors, |
| [ |
| results_report.JSONResultsReport, |
| results_report.TextResultsReport, |
| results_report.HTMLResultsReport, |
| ], |
| ) |
| |
| @mock.patch("generate_report.RunActions") |
| def testMainSelectsHTMLIfNoReportsGiven(self, mock_run_actions): |
| args = [] |
| return_code = self._RunMainWithInput( |
| args, {"platforms": [], "data": {}} |
| ) |
| self.assertEqual(0, return_code) |
| self.assertEqual(mock_run_actions.call_count, 1) |
| ctors = [ctor for ctor, _ in mock_run_actions.call_args[0][0]] |
| self.assertEqual(ctors, [results_report.HTMLResultsReport]) |
| |
| # We only mock print_exc so we don't have exception info printed to stdout. |
| @mock.patch("generate_report.WriteFile", side_effect=ValueError("Oh noo")) |
| @mock.patch("traceback.print_exc") |
| def testRunActionsRunsAllActionsRegardlessOfExceptions( |
| self, mock_print_exc, mock_write_file |
| ): |
| actions = [ |
| (None, "json"), |
| (None, "html"), |
| (None, "text"), |
| (None, "email"), |
| ] |
| output_prefix = "-" |
| ok = generate_report.RunActions( |
| actions, {}, output_prefix, overwrite=False, verbose=False |
| ) |
| self.assertFalse(ok) |
| self.assertEqual(mock_write_file.call_count, len(actions)) |
| self.assertEqual(mock_print_exc.call_count, len(actions)) |
| |
| @mock.patch("generate_report.WriteFile") |
| def testRunActionsReturnsTrueIfAllActionsSucceed(self, mock_write_file): |
| actions = [(None, "json"), (None, "html"), (None, "text")] |
| output_prefix = "-" |
| ok = generate_report.RunActions( |
| actions, {}, output_prefix, overwrite=False, verbose=False |
| ) |
| self.assertEqual(mock_write_file.call_count, len(actions)) |
| self.assertTrue(ok) |
| |
| |
| if __name__ == "__main__": |
| test_flag.SetTestMode(True) |
| unittest.main() |