1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# -*- coding: utf-8 -*- 3*760c253cSXin Li# 4*760c253cSXin Li# Copyright 2016 The ChromiumOS Authors 5*760c253cSXin Li# Use of this source code is governed by a BSD-style license that can be 6*760c253cSXin Li# found in the LICENSE file. 7*760c253cSXin Li 8*760c253cSXin Li"""Unittest for the results reporter.""" 9*760c253cSXin Li 10*760c253cSXin Li 11*760c253cSXin Liimport collections 12*760c253cSXin Liimport io 13*760c253cSXin Liimport os 14*760c253cSXin Liimport unittest 15*760c253cSXin Liimport unittest.mock as mock 16*760c253cSXin Li 17*760c253cSXin Lifrom benchmark_run import MockBenchmarkRun 18*760c253cSXin Lifrom cros_utils import logger 19*760c253cSXin Lifrom experiment_factory import ExperimentFactory 20*760c253cSXin Lifrom experiment_file import ExperimentFile 21*760c253cSXin Lifrom machine_manager import MockCrosMachine 22*760c253cSXin Lifrom machine_manager import MockMachineManager 23*760c253cSXin Lifrom results_cache import MockResult 24*760c253cSXin Lifrom results_report import BenchmarkResults 25*760c253cSXin Lifrom results_report import HTMLResultsReport 26*760c253cSXin Lifrom results_report import JSONResultsReport 27*760c253cSXin Lifrom results_report import ParseChromeosImage 28*760c253cSXin Lifrom results_report import ParseStandardPerfReport 29*760c253cSXin Lifrom results_report import TextResultsReport 30*760c253cSXin Liimport test_flag 31*760c253cSXin Li 32*760c253cSXin Li 33*760c253cSXin Liclass FreeFunctionsTest(unittest.TestCase): 34*760c253cSXin Li """Tests for any free functions in results_report.""" 35*760c253cSXin Li 36*760c253cSXin Li def testParseChromeosImage(self): 37*760c253cSXin Li # N.B. the cases with blank versions aren't explicitly supported by 38*760c253cSXin Li # ParseChromeosImage. I'm not sure if they need to be supported, but the 39*760c253cSXin Li # goal of this was to capture existing functionality as much as possible. 40*760c253cSXin Li base_case = ( 41*760c253cSXin Li "/my/chroot/src/build/images/x86-generic/R01-1.0.date-time" 42*760c253cSXin Li "/chromiumos_test_image.bin" 43*760c253cSXin Li ) 44*760c253cSXin Li self.assertEqual(ParseChromeosImage(base_case), ("R01-1.0", base_case)) 45*760c253cSXin Li 46*760c253cSXin Li dir_base_case = os.path.dirname(base_case) 47*760c253cSXin Li self.assertEqual(ParseChromeosImage(dir_base_case), ("", dir_base_case)) 48*760c253cSXin Li 49*760c253cSXin Li buildbot_case = ( 50*760c253cSXin Li "/my/chroot/chroot/tmp/buildbot-build/R02-1.0.date-time" 51*760c253cSXin Li "/chromiumos_test_image.bin" 52*760c253cSXin Li ) 53*760c253cSXin Li buildbot_img = buildbot_case.split("/chroot/tmp")[1] 54*760c253cSXin Li 55*760c253cSXin Li self.assertEqual( 56*760c253cSXin Li ParseChromeosImage(buildbot_case), ("R02-1.0", buildbot_img) 57*760c253cSXin Li ) 58*760c253cSXin Li self.assertEqual( 59*760c253cSXin Li ParseChromeosImage(os.path.dirname(buildbot_case)), 60*760c253cSXin Li ("", os.path.dirname(buildbot_img)), 61*760c253cSXin Li ) 62*760c253cSXin Li 63*760c253cSXin Li # Ensure we do something reasonable when giving paths that don't quite 64*760c253cSXin Li # match the expected pattern. 65*760c253cSXin Li fun_case = "/chromiumos_test_image.bin" 66*760c253cSXin Li self.assertEqual(ParseChromeosImage(fun_case), ("", fun_case)) 67*760c253cSXin Li 68*760c253cSXin Li fun_case2 = "chromiumos_test_image.bin" 69*760c253cSXin Li self.assertEqual(ParseChromeosImage(fun_case2), ("", fun_case2)) 70*760c253cSXin Li 71*760c253cSXin Li 72*760c253cSXin Li# There are many ways for this to be done better, but the linter complains 73*760c253cSXin Li# about all of them (that I can think of, at least). 74*760c253cSXin Li_fake_path_number = [0] 75*760c253cSXin Li 76*760c253cSXin Li 77*760c253cSXin Lidef FakePath(ext): 78*760c253cSXin Li """Makes a unique path that shouldn't exist on the host system. 79*760c253cSXin Li 80*760c253cSXin Li Each call returns a different path, so if said path finds its way into an 81*760c253cSXin Li error message, it may be easier to track it to its source. 82*760c253cSXin Li """ 83*760c253cSXin Li _fake_path_number[0] += 1 84*760c253cSXin Li prefix = "/tmp/should/not/exist/%d/" % (_fake_path_number[0],) 85*760c253cSXin Li return os.path.join(prefix, ext) 86*760c253cSXin Li 87*760c253cSXin Li 88*760c253cSXin Lidef MakeMockExperiment(compiler="gcc"): 89*760c253cSXin Li """Mocks an experiment using the given compiler.""" 90*760c253cSXin Li mock_experiment_file = io.StringIO( 91*760c253cSXin Li """ 92*760c253cSXin Li board: x86-alex 93*760c253cSXin Li remote: 127.0.0.1 94*760c253cSXin Li locks_dir: /tmp 95*760c253cSXin Li perf_args: record -a -e cycles 96*760c253cSXin Li benchmark: PageCycler { 97*760c253cSXin Li iterations: 3 98*760c253cSXin Li } 99*760c253cSXin Li 100*760c253cSXin Li image1 { 101*760c253cSXin Li chromeos_image: %s 102*760c253cSXin Li } 103*760c253cSXin Li 104*760c253cSXin Li image2 { 105*760c253cSXin Li remote: 127.0.0.2 106*760c253cSXin Li chromeos_image: %s 107*760c253cSXin Li } 108*760c253cSXin Li """ 109*760c253cSXin Li % (FakePath("cros_image1.bin"), FakePath("cros_image2.bin")) 110*760c253cSXin Li ) 111*760c253cSXin Li efile = ExperimentFile(mock_experiment_file) 112*760c253cSXin Li experiment = ExperimentFactory().GetExperiment( 113*760c253cSXin Li efile, FakePath("working_directory"), FakePath("log_dir") 114*760c253cSXin Li ) 115*760c253cSXin Li for label in experiment.labels: 116*760c253cSXin Li label.compiler = compiler 117*760c253cSXin Li return experiment 118*760c253cSXin Li 119*760c253cSXin Li 120*760c253cSXin Lidef _InjectSuccesses(experiment, how_many, keyvals, for_benchmark=0): 121*760c253cSXin Li """Injects successful experiment runs (for each label) into the experiment.""" 122*760c253cSXin Li # Defensive copy of keyvals, so if it's modified, we'll know. 123*760c253cSXin Li keyvals = dict(keyvals) 124*760c253cSXin Li num_configs = len(experiment.benchmarks) * len(experiment.labels) 125*760c253cSXin Li num_runs = len(experiment.benchmark_runs) // num_configs 126*760c253cSXin Li 127*760c253cSXin Li # TODO(gbiv): Centralize the mocking of these, maybe? (It's also done in 128*760c253cSXin Li # benchmark_run_unittest) 129*760c253cSXin Li bench = experiment.benchmarks[for_benchmark] 130*760c253cSXin Li cache_conditions = [] 131*760c253cSXin Li log_level = "average" 132*760c253cSXin Li share_cache = "" 133*760c253cSXin Li locks_dir = "" 134*760c253cSXin Li log = logger.GetLogger() 135*760c253cSXin Li machine_manager = MockMachineManager( 136*760c253cSXin Li FakePath("chromeos_root"), 0, log_level, locks_dir 137*760c253cSXin Li ) 138*760c253cSXin Li machine_manager.AddMachine("testing_machine") 139*760c253cSXin Li machine = next( 140*760c253cSXin Li m for m in machine_manager.GetMachines() if m.name == "testing_machine" 141*760c253cSXin Li ) 142*760c253cSXin Li 143*760c253cSXin Li def MakeSuccessfulRun(n, label): 144*760c253cSXin Li run = MockBenchmarkRun( 145*760c253cSXin Li "mock_success%d" % (n,), 146*760c253cSXin Li bench, 147*760c253cSXin Li label, 148*760c253cSXin Li 1 + n + num_runs, 149*760c253cSXin Li cache_conditions, 150*760c253cSXin Li machine_manager, 151*760c253cSXin Li log, 152*760c253cSXin Li log_level, 153*760c253cSXin Li share_cache, 154*760c253cSXin Li {}, 155*760c253cSXin Li ) 156*760c253cSXin Li mock_result = MockResult(log, label, log_level, machine) 157*760c253cSXin Li mock_result.keyvals = keyvals 158*760c253cSXin Li run.result = mock_result 159*760c253cSXin Li return run 160*760c253cSXin Li 161*760c253cSXin Li for label in experiment.labels: 162*760c253cSXin Li experiment.benchmark_runs.extend( 163*760c253cSXin Li MakeSuccessfulRun(n, label) for n in range(how_many) 164*760c253cSXin Li ) 165*760c253cSXin Li return experiment 166*760c253cSXin Li 167*760c253cSXin Li 168*760c253cSXin Liclass TextResultsReportTest(unittest.TestCase): 169*760c253cSXin Li """Tests that the output of a text report contains the things we pass in. 170*760c253cSXin Li 171*760c253cSXin Li At the moment, this doesn't care deeply about the format in which said 172*760c253cSXin Li things are displayed. It just cares that they're present. 173*760c253cSXin Li """ 174*760c253cSXin Li 175*760c253cSXin Li def _checkReport(self, mock_getcooldown, email): 176*760c253cSXin Li num_success = 2 177*760c253cSXin Li success_keyvals = {"retval": 0, "machine": "some bot", "a_float": 3.96} 178*760c253cSXin Li experiment = _InjectSuccesses( 179*760c253cSXin Li MakeMockExperiment(), num_success, success_keyvals 180*760c253cSXin Li ) 181*760c253cSXin Li SECONDS_IN_MIN = 60 182*760c253cSXin Li mock_getcooldown.return_value = { 183*760c253cSXin Li experiment.remote[0]: 12 * SECONDS_IN_MIN, 184*760c253cSXin Li experiment.remote[1]: 8 * SECONDS_IN_MIN, 185*760c253cSXin Li } 186*760c253cSXin Li 187*760c253cSXin Li text_report = TextResultsReport.FromExperiment( 188*760c253cSXin Li experiment, email=email 189*760c253cSXin Li ).GetReport() 190*760c253cSXin Li self.assertIn(str(success_keyvals["a_float"]), text_report) 191*760c253cSXin Li self.assertIn(success_keyvals["machine"], text_report) 192*760c253cSXin Li self.assertIn(MockCrosMachine.CPUINFO_STRING, text_report) 193*760c253cSXin Li self.assertIn("\nDuration\n", text_report) 194*760c253cSXin Li self.assertIn("Total experiment time:\n", text_report) 195*760c253cSXin Li self.assertIn("Cooldown wait time:\n", text_report) 196*760c253cSXin Li self.assertIn( 197*760c253cSXin Li "DUT %s: %d min" % (experiment.remote[0], 12), text_report 198*760c253cSXin Li ) 199*760c253cSXin Li self.assertIn("DUT %s: %d min" % (experiment.remote[1], 8), text_report) 200*760c253cSXin Li return text_report 201*760c253cSXin Li 202*760c253cSXin Li @mock.patch.object(TextResultsReport, "GetTotalWaitCooldownTime") 203*760c253cSXin Li def testOutput(self, mock_getcooldown): 204*760c253cSXin Li email_report = self._checkReport(mock_getcooldown, email=True) 205*760c253cSXin Li text_report = self._checkReport(mock_getcooldown, email=False) 206*760c253cSXin Li 207*760c253cSXin Li # Ensure that the reports somehow different. Otherwise, having the 208*760c253cSXin Li # distinction is useless. 209*760c253cSXin Li self.assertNotEqual(email_report, text_report) 210*760c253cSXin Li 211*760c253cSXin Li def test_get_totalwait_cooldowntime(self): 212*760c253cSXin Li experiment = MakeMockExperiment() 213*760c253cSXin Li cros_machines = experiment.machine_manager.GetMachines() 214*760c253cSXin Li cros_machines[0].AddCooldownWaitTime(120) 215*760c253cSXin Li cros_machines[1].AddCooldownWaitTime(240) 216*760c253cSXin Li text_results = TextResultsReport.FromExperiment(experiment, email=False) 217*760c253cSXin Li total = text_results.GetTotalWaitCooldownTime() 218*760c253cSXin Li self.assertEqual(total[experiment.remote[0]], 120) 219*760c253cSXin Li self.assertEqual(total[experiment.remote[1]], 240) 220*760c253cSXin Li 221*760c253cSXin Li 222*760c253cSXin Liclass HTMLResultsReportTest(unittest.TestCase): 223*760c253cSXin Li """Tests that the output of a HTML report contains the things we pass in. 224*760c253cSXin Li 225*760c253cSXin Li At the moment, this doesn't care deeply about the format in which said 226*760c253cSXin Li things are displayed. It just cares that they're present. 227*760c253cSXin Li """ 228*760c253cSXin Li 229*760c253cSXin Li _TestOutput = collections.namedtuple( 230*760c253cSXin Li "TestOutput", 231*760c253cSXin Li [ 232*760c253cSXin Li "summary_table", 233*760c253cSXin Li "perf_html", 234*760c253cSXin Li "chart_js", 235*760c253cSXin Li "charts", 236*760c253cSXin Li "full_table", 237*760c253cSXin Li "experiment_file", 238*760c253cSXin Li ], 239*760c253cSXin Li ) 240*760c253cSXin Li 241*760c253cSXin Li @staticmethod 242*760c253cSXin Li def _GetTestOutput( 243*760c253cSXin Li perf_table, 244*760c253cSXin Li chart_js, 245*760c253cSXin Li summary_table, 246*760c253cSXin Li print_table, 247*760c253cSXin Li chart_divs, 248*760c253cSXin Li full_table, 249*760c253cSXin Li experiment_file, 250*760c253cSXin Li ): 251*760c253cSXin Li # N.B. Currently we don't check chart_js; it's just passed through because 252*760c253cSXin Li # cros lint complains otherwise. 253*760c253cSXin Li summary_table = print_table(summary_table, "HTML") 254*760c253cSXin Li perf_html = print_table(perf_table, "HTML") 255*760c253cSXin Li full_table = print_table(full_table, "HTML") 256*760c253cSXin Li return HTMLResultsReportTest._TestOutput( 257*760c253cSXin Li summary_table=summary_table, 258*760c253cSXin Li perf_html=perf_html, 259*760c253cSXin Li chart_js=chart_js, 260*760c253cSXin Li charts=chart_divs, 261*760c253cSXin Li full_table=full_table, 262*760c253cSXin Li experiment_file=experiment_file, 263*760c253cSXin Li ) 264*760c253cSXin Li 265*760c253cSXin Li def _GetOutput(self, experiment=None, benchmark_results=None): 266*760c253cSXin Li with mock.patch("results_report_templates.GenerateHTMLPage") as standin: 267*760c253cSXin Li if experiment is not None: 268*760c253cSXin Li HTMLResultsReport.FromExperiment(experiment).GetReport() 269*760c253cSXin Li else: 270*760c253cSXin Li HTMLResultsReport(benchmark_results).GetReport() 271*760c253cSXin Li mod_mock = standin 272*760c253cSXin Li self.assertEqual(mod_mock.call_count, 1) 273*760c253cSXin Li # call_args[0] is positional args, call_args[1] is kwargs. 274*760c253cSXin Li self.assertEqual(mod_mock.call_args[0], tuple()) 275*760c253cSXin Li fmt_args = mod_mock.call_args[1] 276*760c253cSXin Li return self._GetTestOutput(**fmt_args) 277*760c253cSXin Li 278*760c253cSXin Li def testNoSuccessOutput(self): 279*760c253cSXin Li output = self._GetOutput(MakeMockExperiment()) 280*760c253cSXin Li self.assertIn("no result", output.summary_table) 281*760c253cSXin Li self.assertIn("no result", output.full_table) 282*760c253cSXin Li self.assertEqual(output.charts, "") 283*760c253cSXin Li self.assertNotEqual(output.experiment_file, "") 284*760c253cSXin Li 285*760c253cSXin Li def testSuccessfulOutput(self): 286*760c253cSXin Li num_success = 2 287*760c253cSXin Li success_keyvals = {"retval": 0, "a_float": 3.96} 288*760c253cSXin Li output = self._GetOutput( 289*760c253cSXin Li _InjectSuccesses(MakeMockExperiment(), num_success, success_keyvals) 290*760c253cSXin Li ) 291*760c253cSXin Li 292*760c253cSXin Li self.assertNotIn("no result", output.summary_table) 293*760c253cSXin Li # self.assertIn(success_keyvals['machine'], output.summary_table) 294*760c253cSXin Li self.assertIn("a_float", output.summary_table) 295*760c253cSXin Li self.assertIn(str(success_keyvals["a_float"]), output.summary_table) 296*760c253cSXin Li self.assertIn("a_float", output.full_table) 297*760c253cSXin Li # The _ in a_float is filtered out when we're generating HTML. 298*760c253cSXin Li self.assertIn("afloat", output.charts) 299*760c253cSXin Li # And make sure we have our experiment file... 300*760c253cSXin Li self.assertNotEqual(output.experiment_file, "") 301*760c253cSXin Li 302*760c253cSXin Li def testBenchmarkResultFailure(self): 303*760c253cSXin Li labels = ["label1"] 304*760c253cSXin Li benchmark_names_and_iterations = [("bench1", 1)] 305*760c253cSXin Li benchmark_keyvals = {"bench1": [[]]} 306*760c253cSXin Li results = BenchmarkResults( 307*760c253cSXin Li labels, benchmark_names_and_iterations, benchmark_keyvals 308*760c253cSXin Li ) 309*760c253cSXin Li output = self._GetOutput(benchmark_results=results) 310*760c253cSXin Li self.assertIn("no result", output.summary_table) 311*760c253cSXin Li self.assertEqual(output.charts, "") 312*760c253cSXin Li self.assertEqual(output.experiment_file, "") 313*760c253cSXin Li 314*760c253cSXin Li def testBenchmarkResultSuccess(self): 315*760c253cSXin Li labels = ["label1"] 316*760c253cSXin Li benchmark_names_and_iterations = [("bench1", 1)] 317*760c253cSXin Li benchmark_keyvals = {"bench1": [[{"retval": 1, "foo": 2.0}]]} 318*760c253cSXin Li results = BenchmarkResults( 319*760c253cSXin Li labels, benchmark_names_and_iterations, benchmark_keyvals 320*760c253cSXin Li ) 321*760c253cSXin Li output = self._GetOutput(benchmark_results=results) 322*760c253cSXin Li self.assertNotIn("no result", output.summary_table) 323*760c253cSXin Li self.assertIn("bench1", output.summary_table) 324*760c253cSXin Li self.assertIn("bench1", output.full_table) 325*760c253cSXin Li self.assertNotEqual(output.charts, "") 326*760c253cSXin Li self.assertEqual(output.experiment_file, "") 327*760c253cSXin Li 328*760c253cSXin Li 329*760c253cSXin Liclass JSONResultsReportTest(unittest.TestCase): 330*760c253cSXin Li """Tests JSONResultsReport.""" 331*760c253cSXin Li 332*760c253cSXin Li REQUIRED_REPORT_KEYS = ("date", "time", "label", "test_name", "pass") 333*760c253cSXin Li EXPERIMENT_REPORT_KEYS = ( 334*760c253cSXin Li "board", 335*760c253cSXin Li "chromeos_image", 336*760c253cSXin Li "chromeos_version", 337*760c253cSXin Li "chrome_version", 338*760c253cSXin Li "compiler", 339*760c253cSXin Li ) 340*760c253cSXin Li 341*760c253cSXin Li @staticmethod 342*760c253cSXin Li def _GetRequiredKeys(is_experiment): 343*760c253cSXin Li required_keys = JSONResultsReportTest.REQUIRED_REPORT_KEYS 344*760c253cSXin Li if is_experiment: 345*760c253cSXin Li required_keys += JSONResultsReportTest.EXPERIMENT_REPORT_KEYS 346*760c253cSXin Li return required_keys 347*760c253cSXin Li 348*760c253cSXin Li def _CheckRequiredKeys(self, test_output, is_experiment): 349*760c253cSXin Li required_keys = self._GetRequiredKeys(is_experiment) 350*760c253cSXin Li for output in test_output: 351*760c253cSXin Li for key in required_keys: 352*760c253cSXin Li self.assertIn(key, output) 353*760c253cSXin Li 354*760c253cSXin Li def testAllFailedJSONReportOutput(self): 355*760c253cSXin Li experiment = MakeMockExperiment() 356*760c253cSXin Li results = JSONResultsReport.FromExperiment(experiment).GetReportObject() 357*760c253cSXin Li self._CheckRequiredKeys(results, is_experiment=True) 358*760c253cSXin Li # Nothing succeeded; we don't send anything more than what's required. 359*760c253cSXin Li required_keys = self._GetRequiredKeys(is_experiment=True) 360*760c253cSXin Li for result in results: 361*760c253cSXin Li self.assertCountEqual(result.keys(), required_keys) 362*760c253cSXin Li 363*760c253cSXin Li def testJSONReportOutputWithSuccesses(self): 364*760c253cSXin Li success_keyvals = { 365*760c253cSXin Li "retval": 0, 366*760c253cSXin Li "a_float": "2.3", 367*760c253cSXin Li "many_floats": [["1.0", "2.0"], ["3.0"]], 368*760c253cSXin Li "machine": "i'm a pirate", 369*760c253cSXin Li } 370*760c253cSXin Li 371*760c253cSXin Li # 2 is arbitrary. 372*760c253cSXin Li num_success = 2 373*760c253cSXin Li experiment = _InjectSuccesses( 374*760c253cSXin Li MakeMockExperiment(), num_success, success_keyvals 375*760c253cSXin Li ) 376*760c253cSXin Li results = JSONResultsReport.FromExperiment(experiment).GetReportObject() 377*760c253cSXin Li self._CheckRequiredKeys(results, is_experiment=True) 378*760c253cSXin Li 379*760c253cSXin Li num_passes = num_success * len(experiment.labels) 380*760c253cSXin Li non_failures = [r for r in results if r["pass"]] 381*760c253cSXin Li self.assertEqual(num_passes, len(non_failures)) 382*760c253cSXin Li 383*760c253cSXin Li # TODO(gbiv): ...Is the 3.0 *actually* meant to be dropped? 384*760c253cSXin Li expected_detailed = {"a_float": 2.3, "many_floats": [1.0, 2.0]} 385*760c253cSXin Li for pass_ in non_failures: 386*760c253cSXin Li self.assertIn("detailed_results", pass_) 387*760c253cSXin Li self.assertDictEqual(expected_detailed, pass_["detailed_results"]) 388*760c253cSXin Li self.assertIn("machine", pass_) 389*760c253cSXin Li self.assertEqual(success_keyvals["machine"], pass_["machine"]) 390*760c253cSXin Li 391*760c253cSXin Li def testFailedJSONReportOutputWithoutExperiment(self): 392*760c253cSXin Li labels = ["label1"] 393*760c253cSXin Li # yapf:disable 394*760c253cSXin Li benchmark_names_and_iterations = [ 395*760c253cSXin Li ("bench1", 1), 396*760c253cSXin Li ("bench2", 2), 397*760c253cSXin Li ("bench3", 1), 398*760c253cSXin Li ("bench4", 0), 399*760c253cSXin Li ] 400*760c253cSXin Li # yapf:enable 401*760c253cSXin Li 402*760c253cSXin Li benchmark_keyvals = { 403*760c253cSXin Li "bench1": [[{"retval": 1, "foo": 2.0}]], 404*760c253cSXin Li "bench2": [[{"retval": 1, "foo": 4.0}, {"retval": -1, "bar": 999}]], 405*760c253cSXin Li # lack of retval is considered a failure. 406*760c253cSXin Li "bench3": [[{}]], 407*760c253cSXin Li "bench4": [[]], 408*760c253cSXin Li } 409*760c253cSXin Li bench_results = BenchmarkResults( 410*760c253cSXin Li labels, benchmark_names_and_iterations, benchmark_keyvals 411*760c253cSXin Li ) 412*760c253cSXin Li results = JSONResultsReport(bench_results).GetReportObject() 413*760c253cSXin Li self._CheckRequiredKeys(results, is_experiment=False) 414*760c253cSXin Li self.assertFalse(any(r["pass"] for r in results)) 415*760c253cSXin Li 416*760c253cSXin Li def testJSONGetReportObeysJSONSettings(self): 417*760c253cSXin Li labels = ["label1"] 418*760c253cSXin Li benchmark_names_and_iterations = [("bench1", 1)] 419*760c253cSXin Li # These can be anything, really. So long as they're distinctive. 420*760c253cSXin Li separators = (",\t\n\t", ":\t\n\t") 421*760c253cSXin Li benchmark_keyvals = {"bench1": [[{"retval": 0, "foo": 2.0}]]} 422*760c253cSXin Li bench_results = BenchmarkResults( 423*760c253cSXin Li labels, benchmark_names_and_iterations, benchmark_keyvals 424*760c253cSXin Li ) 425*760c253cSXin Li reporter = JSONResultsReport( 426*760c253cSXin Li bench_results, json_args={"separators": separators} 427*760c253cSXin Li ) 428*760c253cSXin Li result_str = reporter.GetReport() 429*760c253cSXin Li self.assertIn(separators[0], result_str) 430*760c253cSXin Li self.assertIn(separators[1], result_str) 431*760c253cSXin Li 432*760c253cSXin Li def testSuccessfulJSONReportOutputWithoutExperiment(self): 433*760c253cSXin Li labels = ["label1"] 434*760c253cSXin Li benchmark_names_and_iterations = [("bench1", 1), ("bench2", 2)] 435*760c253cSXin Li benchmark_keyvals = { 436*760c253cSXin Li "bench1": [[{"retval": 0, "foo": 2.0}]], 437*760c253cSXin Li "bench2": [[{"retval": 0, "foo": 4.0}, {"retval": 0, "bar": 999}]], 438*760c253cSXin Li } 439*760c253cSXin Li bench_results = BenchmarkResults( 440*760c253cSXin Li labels, benchmark_names_and_iterations, benchmark_keyvals 441*760c253cSXin Li ) 442*760c253cSXin Li results = JSONResultsReport(bench_results).GetReportObject() 443*760c253cSXin Li self._CheckRequiredKeys(results, is_experiment=False) 444*760c253cSXin Li self.assertTrue(all(r["pass"] for r in results)) 445*760c253cSXin Li # Enforce that the results have *some* deterministic order. 446*760c253cSXin Li keyfn = lambda r: ( 447*760c253cSXin Li r["test_name"], 448*760c253cSXin Li r["detailed_results"].get("foo", 5.0), 449*760c253cSXin Li ) 450*760c253cSXin Li sorted_results = sorted(results, key=keyfn) 451*760c253cSXin Li detailed_results = [r["detailed_results"] for r in sorted_results] 452*760c253cSXin Li bench1, bench2_foo, bench2_bar = detailed_results 453*760c253cSXin Li self.assertEqual(bench1["foo"], 2.0) 454*760c253cSXin Li self.assertEqual(bench2_foo["foo"], 4.0) 455*760c253cSXin Li self.assertEqual(bench2_bar["bar"], 999) 456*760c253cSXin Li self.assertNotIn("bar", bench1) 457*760c253cSXin Li self.assertNotIn("bar", bench2_foo) 458*760c253cSXin Li self.assertNotIn("foo", bench2_bar) 459*760c253cSXin Li 460*760c253cSXin Li 461*760c253cSXin Liclass PerfReportParserTest(unittest.TestCase): 462*760c253cSXin Li """Tests for the perf report parser in results_report.""" 463*760c253cSXin Li 464*760c253cSXin Li @staticmethod 465*760c253cSXin Li def _ReadRealPerfReport(): 466*760c253cSXin Li my_dir = os.path.dirname(os.path.realpath(__file__)) 467*760c253cSXin Li with open(os.path.join(my_dir, "perf_files/perf.data.report.0")) as f: 468*760c253cSXin Li return f.read() 469*760c253cSXin Li 470*760c253cSXin Li def testParserParsesRealWorldPerfReport(self): 471*760c253cSXin Li report = ParseStandardPerfReport(self._ReadRealPerfReport()) 472*760c253cSXin Li self.assertCountEqual(["cycles", "instructions"], list(report.keys())) 473*760c253cSXin Li 474*760c253cSXin Li # Arbitrarily selected known percentages from the perf report. 475*760c253cSXin Li known_cycles_percentages = { 476*760c253cSXin Li "0xffffffffa4a1f1c9": 0.66, 477*760c253cSXin Li "0x0000115bb7ba9b54": 0.47, 478*760c253cSXin Li "0x0000000000082e08": 0.00, 479*760c253cSXin Li "0xffffffffa4a13e63": 0.00, 480*760c253cSXin Li } 481*760c253cSXin Li report_cycles = report["cycles"] 482*760c253cSXin Li self.assertEqual(len(report_cycles), 214) 483*760c253cSXin Li for k, v in known_cycles_percentages.items(): 484*760c253cSXin Li self.assertIn(k, report_cycles) 485*760c253cSXin Li self.assertEqual(v, report_cycles[k]) 486*760c253cSXin Li 487*760c253cSXin Li known_instrunctions_percentages = { 488*760c253cSXin Li "0x0000115bb6c35d7a": 1.65, 489*760c253cSXin Li "0x0000115bb7ba9b54": 0.67, 490*760c253cSXin Li "0x0000000000024f56": 0.00, 491*760c253cSXin Li "0xffffffffa4a0ee03": 0.00, 492*760c253cSXin Li } 493*760c253cSXin Li report_instructions = report["instructions"] 494*760c253cSXin Li self.assertEqual(len(report_instructions), 492) 495*760c253cSXin Li for k, v in known_instrunctions_percentages.items(): 496*760c253cSXin Li self.assertIn(k, report_instructions) 497*760c253cSXin Li self.assertEqual(v, report_instructions[k]) 498*760c253cSXin Li 499*760c253cSXin Li 500*760c253cSXin Liif __name__ == "__main__": 501*760c253cSXin Li test_flag.SetTestMode(True) 502*760c253cSXin Li unittest.main() 503