1*760c253cSXin Li#!/usr/bin/env python3 2*760c253cSXin Li# -*- coding: utf-8 -*- 3*760c253cSXin Li 4*760c253cSXin Li# Copyright 2015 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"""This contains the unit tests for the new Crosperf task scheduler.""" 9*760c253cSXin Li 10*760c253cSXin Li 11*760c253cSXin Liimport functools 12*760c253cSXin Liimport io 13*760c253cSXin Liimport unittest 14*760c253cSXin Liimport unittest.mock as mock 15*760c253cSXin Li 16*760c253cSXin Liimport benchmark_run 17*760c253cSXin Lifrom cros_utils.command_executer import CommandExecuter 18*760c253cSXin Lifrom experiment_factory import ExperimentFactory 19*760c253cSXin Lifrom experiment_file import ExperimentFile 20*760c253cSXin Lifrom experiment_runner_unittest import FakeLogger 21*760c253cSXin Lifrom schedv2 import Schedv2 22*760c253cSXin Liimport test_flag 23*760c253cSXin Li 24*760c253cSXin Li 25*760c253cSXin LiEXPERIMENT_FILE_1 = """\ 26*760c253cSXin Liboard: daisy 27*760c253cSXin Liremote: chromeos-daisy1.cros chromeos-daisy2.cros 28*760c253cSXin Lilocks_dir: /tmp 29*760c253cSXin Li 30*760c253cSXin Libenchmark: kraken { 31*760c253cSXin Li suite: telemetry_Crosperf 32*760c253cSXin Li iterations: 3 33*760c253cSXin Li} 34*760c253cSXin Li 35*760c253cSXin Liimage1 { 36*760c253cSXin Li chromeos_image: /chromeos/src/build/images/daisy/latest/cros_image1.bin 37*760c253cSXin Li remote: chromeos-daisy3.cros 38*760c253cSXin Li} 39*760c253cSXin Li 40*760c253cSXin Liimage2 { 41*760c253cSXin Li chromeos_image: /chromeos/src/build/imaages/daisy/latest/cros_image2.bin 42*760c253cSXin Li remote: chromeos-daisy4.cros chromeos-daisy5.cros 43*760c253cSXin Li} 44*760c253cSXin Li""" 45*760c253cSXin Li 46*760c253cSXin LiEXPERIMENT_FILE_WITH_FORMAT = """\ 47*760c253cSXin Liboard: daisy 48*760c253cSXin Liremote: chromeos-daisy1.cros chromeos-daisy2.cros 49*760c253cSXin Lilocks_dir: /tmp 50*760c253cSXin Li 51*760c253cSXin Libenchmark: kraken {{ 52*760c253cSXin Li suite: telemetry_Crosperf 53*760c253cSXin Li iterations: {kraken_iterations} 54*760c253cSXin Li}} 55*760c253cSXin Li 56*760c253cSXin Liimage1 {{ 57*760c253cSXin Li chromeos_image: /chromeos/src/build/images/daisy/latest/cros_image1.bin 58*760c253cSXin Li remote: chromeos-daisy3.cros 59*760c253cSXin Li}} 60*760c253cSXin Li 61*760c253cSXin Liimage2 {{ 62*760c253cSXin Li chromeos_image: /chromeos/src/build/imaages/daisy/latest/cros_image2.bin 63*760c253cSXin Li remote: chromeos-daisy4.cros chromeos-daisy5.cros 64*760c253cSXin Li}} 65*760c253cSXin Li""" 66*760c253cSXin Li 67*760c253cSXin Li 68*760c253cSXin Liclass Schedv2Test(unittest.TestCase): 69*760c253cSXin Li """Class for setting up and running the unit tests.""" 70*760c253cSXin Li 71*760c253cSXin Li def setUp(self): 72*760c253cSXin Li self.exp = None 73*760c253cSXin Li 74*760c253cSXin Li mock_logger = FakeLogger() 75*760c253cSXin Li mock_cmd_exec = mock.Mock(spec=CommandExecuter) 76*760c253cSXin Li 77*760c253cSXin Li @mock.patch( 78*760c253cSXin Li "benchmark_run.BenchmarkRun", new=benchmark_run.MockBenchmarkRun 79*760c253cSXin Li ) 80*760c253cSXin Li def _make_fake_experiment(self, expstr): 81*760c253cSXin Li """Create fake experiment from string. 82*760c253cSXin Li 83*760c253cSXin Li Note - we mock out BenchmarkRun in this step. 84*760c253cSXin Li """ 85*760c253cSXin Li experiment_file = ExperimentFile(io.StringIO(expstr)) 86*760c253cSXin Li experiment = ExperimentFactory().GetExperiment( 87*760c253cSXin Li experiment_file, working_directory="", log_dir="" 88*760c253cSXin Li ) 89*760c253cSXin Li return experiment 90*760c253cSXin Li 91*760c253cSXin Li def test_remote(self): 92*760c253cSXin Li """Test that remotes in labels are aggregated into experiment.remote.""" 93*760c253cSXin Li 94*760c253cSXin Li self.exp = self._make_fake_experiment(EXPERIMENT_FILE_1) 95*760c253cSXin Li self.exp.log_level = "verbose" 96*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 97*760c253cSXin Li self.assertFalse(my_schedv2.is_complete()) 98*760c253cSXin Li self.assertIn("chromeos-daisy1.cros", self.exp.remote) 99*760c253cSXin Li self.assertIn("chromeos-daisy2.cros", self.exp.remote) 100*760c253cSXin Li self.assertIn("chromeos-daisy3.cros", self.exp.remote) 101*760c253cSXin Li self.assertIn("chromeos-daisy4.cros", self.exp.remote) 102*760c253cSXin Li self.assertIn("chromeos-daisy5.cros", self.exp.remote) 103*760c253cSXin Li 104*760c253cSXin Li def test_unreachable_remote(self): 105*760c253cSXin Li """Test unreachable remotes are removed from experiment and label.""" 106*760c253cSXin Li 107*760c253cSXin Li def MockIsReachable(cm): 108*760c253cSXin Li return ( 109*760c253cSXin Li cm.name != "chromeos-daisy3.cros" 110*760c253cSXin Li and cm.name != "chromeos-daisy5.cros" 111*760c253cSXin Li ) 112*760c253cSXin Li 113*760c253cSXin Li with mock.patch( 114*760c253cSXin Li "machine_manager.MockCrosMachine.IsReachable", new=MockIsReachable 115*760c253cSXin Li ): 116*760c253cSXin Li self.exp = self._make_fake_experiment(EXPERIMENT_FILE_1) 117*760c253cSXin Li self.assertIn("chromeos-daisy1.cros", self.exp.remote) 118*760c253cSXin Li self.assertIn("chromeos-daisy2.cros", self.exp.remote) 119*760c253cSXin Li self.assertNotIn("chromeos-daisy3.cros", self.exp.remote) 120*760c253cSXin Li self.assertIn("chromeos-daisy4.cros", self.exp.remote) 121*760c253cSXin Li self.assertNotIn("chromeos-daisy5.cros", self.exp.remote) 122*760c253cSXin Li 123*760c253cSXin Li for l in self.exp.labels: 124*760c253cSXin Li if l.name == "image2": 125*760c253cSXin Li self.assertNotIn("chromeos-daisy5.cros", l.remote) 126*760c253cSXin Li self.assertIn("chromeos-daisy4.cros", l.remote) 127*760c253cSXin Li elif l.name == "image1": 128*760c253cSXin Li self.assertNotIn("chromeos-daisy3.cros", l.remote) 129*760c253cSXin Li 130*760c253cSXin Li @mock.patch("schedv2.BenchmarkRunCacheReader") 131*760c253cSXin Li def test_BenchmarkRunCacheReader_1(self, reader): 132*760c253cSXin Li """Test benchmarkrun set is split into 5 segments.""" 133*760c253cSXin Li 134*760c253cSXin Li self.exp = self._make_fake_experiment( 135*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=9) 136*760c253cSXin Li ) 137*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 138*760c253cSXin Li self.assertFalse(my_schedv2.is_complete()) 139*760c253cSXin Li # We have 9 * 2 == 18 brs, we use 5 threads, each reading 4, 4, 4, 140*760c253cSXin Li # 4, 2 brs respectively. 141*760c253cSXin Li # Assert that BenchmarkRunCacheReader() is called 5 times. 142*760c253cSXin Li self.assertEqual(reader.call_count, 5) 143*760c253cSXin Li # reader.call_args_list[n] - nth call. 144*760c253cSXin Li # reader.call_args_list[n][0] - positioned args in nth call. 145*760c253cSXin Li # reader.call_args_list[n][0][1] - the 2nd arg in nth call, 146*760c253cSXin Li # that is 'br_list' in 'schedv2.BenchmarkRunCacheReader'. 147*760c253cSXin Li self.assertEqual(len(reader.call_args_list[0][0][1]), 4) 148*760c253cSXin Li self.assertEqual(len(reader.call_args_list[1][0][1]), 4) 149*760c253cSXin Li self.assertEqual(len(reader.call_args_list[2][0][1]), 4) 150*760c253cSXin Li self.assertEqual(len(reader.call_args_list[3][0][1]), 4) 151*760c253cSXin Li self.assertEqual(len(reader.call_args_list[4][0][1]), 2) 152*760c253cSXin Li 153*760c253cSXin Li @mock.patch("schedv2.BenchmarkRunCacheReader") 154*760c253cSXin Li def test_BenchmarkRunCacheReader_2(self, reader): 155*760c253cSXin Li """Test benchmarkrun set is split into 4 segments.""" 156*760c253cSXin Li 157*760c253cSXin Li self.exp = self._make_fake_experiment( 158*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=8) 159*760c253cSXin Li ) 160*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 161*760c253cSXin Li self.assertFalse(my_schedv2.is_complete()) 162*760c253cSXin Li # We have 8 * 2 == 16 brs, we use 4 threads, each reading 4 brs. 163*760c253cSXin Li self.assertEqual(reader.call_count, 4) 164*760c253cSXin Li self.assertEqual(len(reader.call_args_list[0][0][1]), 4) 165*760c253cSXin Li self.assertEqual(len(reader.call_args_list[1][0][1]), 4) 166*760c253cSXin Li self.assertEqual(len(reader.call_args_list[2][0][1]), 4) 167*760c253cSXin Li self.assertEqual(len(reader.call_args_list[3][0][1]), 4) 168*760c253cSXin Li 169*760c253cSXin Li @mock.patch("schedv2.BenchmarkRunCacheReader") 170*760c253cSXin Li def test_BenchmarkRunCacheReader_3(self, reader): 171*760c253cSXin Li """Test benchmarkrun set is split into 2 segments.""" 172*760c253cSXin Li 173*760c253cSXin Li self.exp = self._make_fake_experiment( 174*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=3) 175*760c253cSXin Li ) 176*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 177*760c253cSXin Li self.assertFalse(my_schedv2.is_complete()) 178*760c253cSXin Li # We have 3 * 2 == 6 brs, we use 2 threads. 179*760c253cSXin Li self.assertEqual(reader.call_count, 2) 180*760c253cSXin Li self.assertEqual(len(reader.call_args_list[0][0][1]), 3) 181*760c253cSXin Li self.assertEqual(len(reader.call_args_list[1][0][1]), 3) 182*760c253cSXin Li 183*760c253cSXin Li @mock.patch("schedv2.BenchmarkRunCacheReader") 184*760c253cSXin Li def test_BenchmarkRunCacheReader_4(self, reader): 185*760c253cSXin Li """Test benchmarkrun set is not splitted.""" 186*760c253cSXin Li 187*760c253cSXin Li self.exp = self._make_fake_experiment( 188*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=1) 189*760c253cSXin Li ) 190*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 191*760c253cSXin Li self.assertFalse(my_schedv2.is_complete()) 192*760c253cSXin Li # We have 1 * 2 == 2 br, so only 1 instance. 193*760c253cSXin Li self.assertEqual(reader.call_count, 1) 194*760c253cSXin Li self.assertEqual(len(reader.call_args_list[0][0][1]), 2) 195*760c253cSXin Li 196*760c253cSXin Li def test_cachehit(self): 197*760c253cSXin Li """Test cache-hit and none-cache-hit brs are properly organized.""" 198*760c253cSXin Li 199*760c253cSXin Li def MockReadCache(br): 200*760c253cSXin Li br.cache_hit = br.label.name == "image2" 201*760c253cSXin Li 202*760c253cSXin Li with mock.patch( 203*760c253cSXin Li "benchmark_run.MockBenchmarkRun.ReadCache", new=MockReadCache 204*760c253cSXin Li ): 205*760c253cSXin Li # We have 2 * 30 brs, half of which are put into _cached_br_list. 206*760c253cSXin Li self.exp = self._make_fake_experiment( 207*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=30) 208*760c253cSXin Li ) 209*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 210*760c253cSXin Li self.assertEqual(len(my_schedv2.get_cached_run_list()), 30) 211*760c253cSXin Li # The non-cache-hit brs are put into Schedv2._label_brl_map. 212*760c253cSXin Li self.assertEqual( 213*760c253cSXin Li functools.reduce( 214*760c253cSXin Li lambda a, x: a + len(x[1]), 215*760c253cSXin Li my_schedv2.get_label_map().items(), 216*760c253cSXin Li 0, 217*760c253cSXin Li ), 218*760c253cSXin Li 30, 219*760c253cSXin Li ) 220*760c253cSXin Li 221*760c253cSXin Li def test_nocachehit(self): 222*760c253cSXin Li """Test no cache-hit.""" 223*760c253cSXin Li 224*760c253cSXin Li def MockReadCache(br): 225*760c253cSXin Li br.cache_hit = False 226*760c253cSXin Li 227*760c253cSXin Li with mock.patch( 228*760c253cSXin Li "benchmark_run.MockBenchmarkRun.ReadCache", new=MockReadCache 229*760c253cSXin Li ): 230*760c253cSXin Li # We have 2 * 30 brs, none of which are put into _cached_br_list. 231*760c253cSXin Li self.exp = self._make_fake_experiment( 232*760c253cSXin Li EXPERIMENT_FILE_WITH_FORMAT.format(kraken_iterations=30) 233*760c253cSXin Li ) 234*760c253cSXin Li my_schedv2 = Schedv2(self.exp) 235*760c253cSXin Li self.assertEqual(len(my_schedv2.get_cached_run_list()), 0) 236*760c253cSXin Li # The non-cache-hit brs are put into Schedv2._label_brl_map. 237*760c253cSXin Li self.assertEqual( 238*760c253cSXin Li functools.reduce( 239*760c253cSXin Li lambda a, x: a + len(x[1]), 240*760c253cSXin Li my_schedv2.get_label_map().items(), 241*760c253cSXin Li 0, 242*760c253cSXin Li ), 243*760c253cSXin Li 60, 244*760c253cSXin Li ) 245*760c253cSXin Li 246*760c253cSXin Li 247*760c253cSXin Liif __name__ == "__main__": 248*760c253cSXin Li test_flag.SetTestMode(True) 249*760c253cSXin Li unittest.main() 250