1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# -*- coding: utf-8 -*- 3*760c253cSXin Li# Copyright 2016 The ChromiumOS Authors 4*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 5*760c253cSXin Li# found in the LICENSE file. 6*760c253cSXin Li 7*760c253cSXin Li"""Test for generate_report.py.""" 8*760c253cSXin Li 9*760c253cSXin Li 10*760c253cSXin Liimport copy 11*760c253cSXin Liimport json 12*760c253cSXin Liimport unittest 13*760c253cSXin Liimport unittest.mock as mock 14*760c253cSXin Li 15*760c253cSXin Liimport generate_report 16*760c253cSXin Liimport results_report 17*760c253cSXin Liimport test_flag 18*760c253cSXin Li 19*760c253cSXin Li 20*760c253cSXin Li# pylint: disable=deprecated-module 21*760c253cSXin Litry: 22*760c253cSXin Li from StringIO import StringIO # for Python 2 23*760c253cSXin Liexcept ImportError: 24*760c253cSXin Li from io import StringIO # for Python 3 25*760c253cSXin Li 26*760c253cSXin Li 27*760c253cSXin Liclass _ContextualStringIO(StringIO): 28*760c253cSXin Li """StringIO that can be used in `with` statements.""" 29*760c253cSXin Li 30*760c253cSXin Li def __init__(self, *args): 31*760c253cSXin Li StringIO.__init__(self, *args) 32*760c253cSXin Li 33*760c253cSXin Li def __enter__(self): 34*760c253cSXin Li return self 35*760c253cSXin Li 36*760c253cSXin Li def __exit__(self, _type, _value, _traceback): 37*760c253cSXin Li pass 38*760c253cSXin Li 39*760c253cSXin Li 40*760c253cSXin Liclass GenerateReportTests(unittest.TestCase): 41*760c253cSXin Li """Tests for generate_report.py.""" 42*760c253cSXin Li 43*760c253cSXin Li def testCountBenchmarks(self): 44*760c253cSXin Li runs = { 45*760c253cSXin Li "foo": [[{}, {}, {}], [{}, {}, {}, {}]], 46*760c253cSXin Li "bar": [], 47*760c253cSXin Li "baz": [[], [{}], [{}, {}, {}]], 48*760c253cSXin Li } 49*760c253cSXin Li results = generate_report.CountBenchmarks(runs) 50*760c253cSXin Li expected_results = [("foo", 4), ("bar", 0), ("baz", 3)] 51*760c253cSXin Li self.assertCountEqual(expected_results, results) 52*760c253cSXin Li 53*760c253cSXin Li def testCutResultsInPlace(self): 54*760c253cSXin Li bench_data = { 55*760c253cSXin Li "foo": [[{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 2.5, "c": 1}]], 56*760c253cSXin Li "bar": [[{"d": 11, "e": 12, "f": 13}]], 57*760c253cSXin Li "baz": [[{"g": 12, "h": 13}]], 58*760c253cSXin Li "qux": [[{"i": 11}]], 59*760c253cSXin Li } 60*760c253cSXin Li original_bench_data = copy.deepcopy(bench_data) 61*760c253cSXin Li 62*760c253cSXin Li max_keys = 2 63*760c253cSXin Li results = generate_report.CutResultsInPlace( 64*760c253cSXin Li bench_data, max_keys=max_keys, complain_on_update=False 65*760c253cSXin Li ) 66*760c253cSXin Li # Cuts should be in-place. 67*760c253cSXin Li self.assertIs(results, bench_data) 68*760c253cSXin Li self.assertCountEqual( 69*760c253cSXin Li list(original_bench_data.keys()), list(bench_data.keys()) 70*760c253cSXin Li ) 71*760c253cSXin Li for bench_name, original_runs in original_bench_data.items(): 72*760c253cSXin Li bench_runs = bench_data[bench_name] 73*760c253cSXin Li self.assertEqual(len(original_runs), len(bench_runs)) 74*760c253cSXin Li # Order of these sub-lists shouldn't have changed. 75*760c253cSXin Li for original_list, new_list in zip(original_runs, bench_runs): 76*760c253cSXin Li self.assertEqual(len(original_list), len(new_list)) 77*760c253cSXin Li for original_keyvals, sub_keyvals in zip( 78*760c253cSXin Li original_list, new_list 79*760c253cSXin Li ): 80*760c253cSXin Li # sub_keyvals must be a subset of original_keyvals 81*760c253cSXin Li self.assertDictContainsSubset(sub_keyvals, original_keyvals) 82*760c253cSXin Li 83*760c253cSXin Li def testCutResultsInPlaceLeavesRetval(self): 84*760c253cSXin Li bench_data = { 85*760c253cSXin Li "foo": [[{"retval": 0, "a": 1}]], 86*760c253cSXin Li "bar": [[{"retval": 1}]], 87*760c253cSXin Li "baz": [[{"RETVAL": 1}]], 88*760c253cSXin Li } 89*760c253cSXin Li results = generate_report.CutResultsInPlace( 90*760c253cSXin Li bench_data, max_keys=0, complain_on_update=False 91*760c253cSXin Li ) 92*760c253cSXin Li # Just reach into results assuming we know it otherwise outputs things in 93*760c253cSXin Li # the expected way. If it doesn't, testCutResultsInPlace should give an 94*760c253cSXin Li # indication as to what, exactly, is broken. 95*760c253cSXin Li self.assertEqual(list(results["foo"][0][0].items()), [("retval", 0)]) 96*760c253cSXin Li self.assertEqual(list(results["bar"][0][0].items()), [("retval", 1)]) 97*760c253cSXin Li self.assertEqual(list(results["baz"][0][0].items()), []) 98*760c253cSXin Li 99*760c253cSXin Li def _RunMainWithInput(self, args, input_obj): 100*760c253cSXin Li assert "-i" not in args 101*760c253cSXin Li args += ["-i", "-"] 102*760c253cSXin Li input_buf = _ContextualStringIO(json.dumps(input_obj)) 103*760c253cSXin Li with mock.patch( 104*760c253cSXin Li "generate_report.PickInputFile", return_value=input_buf 105*760c253cSXin Li ) as patched_pick: 106*760c253cSXin Li result = generate_report.Main(args) 107*760c253cSXin Li patched_pick.assert_called_once_with("-") 108*760c253cSXin Li return result 109*760c253cSXin Li 110*760c253cSXin Li @mock.patch("generate_report.RunActions") 111*760c253cSXin Li def testMain(self, mock_run_actions): 112*760c253cSXin Li # Email is left out because it's a bit more difficult to test, and it'll be 113*760c253cSXin Li # mildly obvious if it's failing. 114*760c253cSXin Li args = ["--json", "--html", "--text"] 115*760c253cSXin Li return_code = self._RunMainWithInput( 116*760c253cSXin Li args, {"platforms": [], "data": {}} 117*760c253cSXin Li ) 118*760c253cSXin Li self.assertEqual(0, return_code) 119*760c253cSXin Li self.assertEqual(mock_run_actions.call_count, 1) 120*760c253cSXin Li ctors = [ctor for ctor, _ in mock_run_actions.call_args[0][0]] 121*760c253cSXin Li self.assertEqual( 122*760c253cSXin Li ctors, 123*760c253cSXin Li [ 124*760c253cSXin Li results_report.JSONResultsReport, 125*760c253cSXin Li results_report.TextResultsReport, 126*760c253cSXin Li results_report.HTMLResultsReport, 127*760c253cSXin Li ], 128*760c253cSXin Li ) 129*760c253cSXin Li 130*760c253cSXin Li @mock.patch("generate_report.RunActions") 131*760c253cSXin Li def testMainSelectsHTMLIfNoReportsGiven(self, mock_run_actions): 132*760c253cSXin Li args = [] 133*760c253cSXin Li return_code = self._RunMainWithInput( 134*760c253cSXin Li args, {"platforms": [], "data": {}} 135*760c253cSXin Li ) 136*760c253cSXin Li self.assertEqual(0, return_code) 137*760c253cSXin Li self.assertEqual(mock_run_actions.call_count, 1) 138*760c253cSXin Li ctors = [ctor for ctor, _ in mock_run_actions.call_args[0][0]] 139*760c253cSXin Li self.assertEqual(ctors, [results_report.HTMLResultsReport]) 140*760c253cSXin Li 141*760c253cSXin Li # We only mock print_exc so we don't have exception info printed to stdout. 142*760c253cSXin Li @mock.patch("generate_report.WriteFile", side_effect=ValueError("Oh noo")) 143*760c253cSXin Li @mock.patch("traceback.print_exc") 144*760c253cSXin Li def testRunActionsRunsAllActionsRegardlessOfExceptions( 145*760c253cSXin Li self, mock_print_exc, mock_write_file 146*760c253cSXin Li ): 147*760c253cSXin Li actions = [ 148*760c253cSXin Li (None, "json"), 149*760c253cSXin Li (None, "html"), 150*760c253cSXin Li (None, "text"), 151*760c253cSXin Li (None, "email"), 152*760c253cSXin Li ] 153*760c253cSXin Li output_prefix = "-" 154*760c253cSXin Li ok = generate_report.RunActions( 155*760c253cSXin Li actions, {}, output_prefix, overwrite=False, verbose=False 156*760c253cSXin Li ) 157*760c253cSXin Li self.assertFalse(ok) 158*760c253cSXin Li self.assertEqual(mock_write_file.call_count, len(actions)) 159*760c253cSXin Li self.assertEqual(mock_print_exc.call_count, len(actions)) 160*760c253cSXin Li 161*760c253cSXin Li @mock.patch("generate_report.WriteFile") 162*760c253cSXin Li def testRunActionsReturnsTrueIfAllActionsSucceed(self, mock_write_file): 163*760c253cSXin Li actions = [(None, "json"), (None, "html"), (None, "text")] 164*760c253cSXin Li output_prefix = "-" 165*760c253cSXin Li ok = generate_report.RunActions( 166*760c253cSXin Li actions, {}, output_prefix, overwrite=False, verbose=False 167*760c253cSXin Li ) 168*760c253cSXin Li self.assertEqual(mock_write_file.call_count, len(actions)) 169*760c253cSXin Li self.assertTrue(ok) 170*760c253cSXin Li 171*760c253cSXin Li 172*760c253cSXin Liif __name__ == "__main__": 173*760c253cSXin Li test_flag.SetTestMode(True) 174*760c253cSXin Li unittest.main() 175