Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 1 | # Copyright 2018 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | """Module for task config reader unittests.""" |
| 6 | # pylint: disable=g-missing-super-call |
| 7 | |
| 8 | import unittest |
| 9 | |
| 10 | import config_reader |
| 11 | import constants |
| 12 | import mock |
| 13 | import task_config_reader |
| 14 | import tot_manager |
| 15 | |
| 16 | |
| 17 | class FakeTotMilestoneManager(tot_manager.TotMilestoneManager): |
| 18 | |
| 19 | def __init__(self, is_sanity): |
| 20 | self.is_sanity = is_sanity |
| 21 | self.storage_client = None |
| 22 | self.tot = self._tot_milestone() |
| 23 | |
| 24 | |
| 25 | class BaseTaskConfigReaderTestCase(unittest.TestCase): |
| 26 | |
| 27 | _EVENT_TYPE = 'nightly' |
| 28 | _PRIORITY = task_config_reader.EVENT_CLASSES[_EVENT_TYPE].PRIORITY |
| 29 | _TIMEOUT = task_config_reader.EVENT_CLASSES[_EVENT_TYPE].TIMEOUT |
| 30 | _TASK_NAME = 'fake_task' |
| 31 | _SUITE = 'fake_suite' |
| 32 | _BRANCH_SPEC = 'tot-1' |
| 33 | _POOL = 'bvt' |
| 34 | _NUM = 2 |
| 35 | _BOARD = 'link' |
Xinan Lin | c864711 | 2020-02-04 16:45:56 -0800 | [diff] [blame] | 36 | _DIMENSIONS = 'label-wifi:foo' |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 37 | |
| 38 | def setUp(self): |
| 39 | self.config = config_reader.ConfigReader(None) |
| 40 | self.config.add_section(self._TASK_NAME) |
| 41 | self.config.set(self._TASK_NAME, 'suite', self._SUITE) |
| 42 | self.config.set(self._TASK_NAME, 'branch_specs', self._BRANCH_SPEC) |
| 43 | self.config.set(self._TASK_NAME, 'run_on', self._EVENT_TYPE) |
| 44 | self.config.set(self._TASK_NAME, 'pool', self._POOL) |
| 45 | self.config.set(self._TASK_NAME, 'num', '%d' % self._NUM) |
| 46 | self.config.set(self._TASK_NAME, 'boards', self._BOARD) |
Xinan Lin | c864711 | 2020-02-04 16:45:56 -0800 | [diff] [blame] | 47 | self.config.set(self._TASK_NAME, 'dimensions', self._DIMENSIONS) |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 48 | |
| 49 | tot_patcher = mock.patch('tot_manager.TotMilestoneManager') |
| 50 | self._tot = tot_patcher.start() |
| 51 | self._tot.return_value = FakeTotMilestoneManager(True) |
| 52 | |
| 53 | board_family_patcher = mock.patch( |
| 54 | 'build_lib.get_board_family_mapping_from_gs') |
| 55 | board_family_getter = board_family_patcher.start() |
| 56 | board_family_getter.return_value = { |
| 57 | 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'], |
| 58 | 'ivybridge': ['link', 'link_freon']} |
| 59 | |
| 60 | self.addCleanup(tot_patcher.stop) |
| 61 | self.addCleanup(board_family_patcher.stop) |
| 62 | |
| 63 | |
| 64 | class TaskCreationTestCase(BaseTaskConfigReaderTestCase): |
| 65 | |
| 66 | def testCreateTaskFromConfigNonexistentSection(self): |
| 67 | """Ensure a Task CANNOT be built without suite, and raise error.""" |
| 68 | task_config = task_config_reader.TaskConfig(self.config) |
| 69 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 70 | task_config._create_task_from_config_section, |
| 71 | 'non-existent section') |
| 72 | |
| 73 | def testCreateTaskFromConfigNotAllowedHeader(self): |
Brigit Rossbach | f6e5a52 | 2021-01-12 13:38:01 -0700 | [diff] [blame] | 74 | """Ensure a Task can be built with unknown config headers.""" |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 75 | self.config.set(self._TASK_NAME, 'non-valid-header', 'test') |
| 76 | task_config = task_config_reader.TaskConfig(self.config) |
Brigit Rossbach | f6e5a52 | 2021-01-12 13:38:01 -0700 | [diff] [blame] | 77 | response = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 78 | self.assertEqual(len(response), 1) |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 79 | |
| 80 | def testGetTaskByKeyword(self): |
| 81 | """Ensure a Task can be built from a correct config.""" |
| 82 | task_config = task_config_reader.TaskConfig(self.config) |
| 83 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 84 | self.assertEqual(len(tasks), 1) |
| 85 | new_task = tasks[0] |
| 86 | self.assertEqual(new_task.name, self._TASK_NAME) |
| 87 | self.assertEqual(new_task.suite, self._SUITE) |
| 88 | self.assertEqual(new_task.branch_specs, [self._BRANCH_SPEC]) |
| 89 | self.assertEqual(new_task.pool, self._POOL) |
| 90 | self.assertEqual(new_task.num, self._NUM) |
| 91 | self.assertEqual(new_task.boards, |
| 92 | [t.lstrip() for t in self._BOARD.split(',')]) |
| 93 | self.assertEqual(new_task.priority, self._PRIORITY) |
| 94 | self.assertEqual(new_task.timeout, self._TIMEOUT) |
Xinan Lin | c864711 | 2020-02-04 16:45:56 -0800 | [diff] [blame] | 95 | self.assertEqual(new_task.dimensions, self._DIMENSIONS) |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 96 | |
| 97 | def testGetTaskByKeywordNoRunonNoRaise(self): |
| 98 | """Ensure a Task CANNOT be built without run_on, but not raise error.""" |
| 99 | self.config.remove_option(self._TASK_NAME, 'run_on') |
| 100 | task_config = task_config_reader.TaskConfig(self.config) |
| 101 | try: |
| 102 | response = task_config.get_tasks_by_keyword(self._EVENT_TYPE) |
| 103 | self.assertEqual(len(response['tasks']), 0) |
| 104 | self.assertEqual(len(response['exceptions']), 1) |
| 105 | except task_config_reader.MalformedConfigEntryError: |
| 106 | self.fail('task_config should not raise error without run_on!') |
| 107 | |
| 108 | def testGetTaskByKeywordNoSuiteNoRaise(self): |
| 109 | """Ensure a Task CANNOT be built without suite, but not raise error. |
| 110 | |
| 111 | This is to ensure a malformed task won't affect other tasks' reading. |
| 112 | """ |
| 113 | self.config.remove_option(self._TASK_NAME, 'suite') |
| 114 | task_config = task_config_reader.TaskConfig(self.config) |
| 115 | try: |
| 116 | response = task_config.get_tasks_by_keyword(self._EVENT_TYPE) |
| 117 | self.assertEqual(len(response['tasks']), 0) |
| 118 | self.assertEqual(len(response['exceptions']), 1) |
| 119 | except config_reader.MalformedConfigEntry: |
| 120 | self.fail('task_config should not raise error without suite!') |
| 121 | |
| 122 | def testGetTaskByKeywordNoBranch(self): |
| 123 | """Ensure a Task can be built without branch_specs.""" |
| 124 | self.config.remove_option(self._TASK_NAME, 'branch_specs') |
| 125 | task_config = task_config_reader.TaskConfig(self.config) |
| 126 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 127 | self.assertEqual(tasks[0].branch_specs, []) |
| 128 | |
| 129 | def testGetTaskByKeywordNoNum(self): |
| 130 | """Ensure a Task can be built without num.""" |
| 131 | self.config.remove_option(self._TASK_NAME, 'num') |
| 132 | task_config = task_config_reader.TaskConfig(self.config) |
| 133 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 134 | self.assertIsNone(tasks[0].num) |
| 135 | |
| 136 | def testSetPriority(self): |
| 137 | """Ensure priority can be set by user.""" |
| 138 | priority = constants.Priorities.DEFAULT |
| 139 | self.config.set(self._TASK_NAME, 'priority', str(priority)) |
| 140 | task_config = task_config_reader.TaskConfig(self.config) |
| 141 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 142 | self.assertEqual(tasks[0].priority, priority) |
| 143 | |
Xinan Lin | 292f38c | 2020-04-16 14:09:50 -0700 | [diff] [blame] | 144 | def testSuitePoolShouldNotSetPriority(self): |
| 145 | """Ensure priority is not set for pool suites.""" |
| 146 | self.config.set(self._TASK_NAME, 'pool', 'suites') |
| 147 | task_config = task_config_reader.TaskConfig(self.config) |
| 148 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 149 | self.assertIsNone(tasks[0].priority) |
| 150 | |
Xixuan Wu | 51bb710 | 2019-03-18 14:51:44 -0700 | [diff] [blame] | 151 | def testSetTimeout(self): |
| 152 | """Ensure timeout can be set by user.""" |
| 153 | timeout = 1000 |
| 154 | self.config.set(self._TASK_NAME, 'timeout', str(timeout)) |
| 155 | task_config = task_config_reader.TaskConfig(self.config) |
| 156 | tasks = task_config.get_tasks_by_keyword(self._EVENT_TYPE)['tasks'] |
| 157 | self.assertEqual(tasks[0].timeout, timeout) |
| 158 | |
| 159 | |
| 160 | class TaskInfoCheckTestCase(BaseTaskConfigReaderTestCase): |
| 161 | |
| 162 | def testNonIntNum(self): |
| 163 | """Ensure a Task's num is a Integer.""" |
| 164 | self.config.set(self._TASK_NAME, 'num', 'non_int') |
| 165 | task_config = task_config_reader.TaskConfig(self.config) |
| 166 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 167 | task_config._read_task_section, |
| 168 | self._TASK_NAME) |
| 169 | |
| 170 | def testNonIntHour(self): |
| 171 | """Ensure a Task's hour is a Integer.""" |
| 172 | self.config.set(self._TASK_NAME, 'hour', 'non_int') |
| 173 | task_config = task_config_reader.TaskConfig(self.config) |
| 174 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 175 | task_config._read_task_section, |
| 176 | self._TASK_NAME) |
| 177 | |
| 178 | def testNonIntDay(self): |
| 179 | """Ensure a Task's hour is a Integer.""" |
| 180 | self.config.set(self._TASK_NAME, 'day', 'non_int') |
| 181 | task_config = task_config_reader.TaskConfig(self.config) |
| 182 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 183 | task_config._read_task_section, |
| 184 | self._TASK_NAME) |
| 185 | |
| 186 | def testNoSuite(self): |
| 187 | """Ensure a Task's suite exists.""" |
| 188 | self.config.remove_option(self._TASK_NAME, 'suite') |
| 189 | task_config = task_config_reader.TaskConfig(self.config) |
| 190 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 191 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 192 | task_config._validate_task_section, |
| 193 | task_info) |
| 194 | |
| 195 | def testInvalidIntHour(self): |
| 196 | """Ensure a Task's hour is in 0~23.""" |
| 197 | self.config.set(self._TASK_NAME, 'hour', '24') |
| 198 | task_config = task_config_reader.TaskConfig(self.config) |
| 199 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 200 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 201 | task_config._validate_task_section, |
| 202 | task_info) |
| 203 | |
| 204 | def testNoHourForNonNightlyEvent(self): |
| 205 | """Ensure a non-nightly Task cannot specify hour.""" |
| 206 | self.config.set(self._TASK_NAME, 'hour', '10') |
| 207 | self.config.set(self._TASK_NAME, 'run_on', 'new_build') |
| 208 | task_config = task_config_reader.TaskConfig(self.config) |
| 209 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 210 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 211 | task_config._validate_task_section, |
| 212 | task_info) |
| 213 | |
| 214 | def testInvalidIntDay(self): |
| 215 | """Ensure a Task's day is in 0~6.""" |
| 216 | self.config.set(self._TASK_NAME, 'day', '10') |
| 217 | self.config.set(self._TASK_NAME, 'run_on', 'weekly') |
| 218 | task_config = task_config_reader.TaskConfig(self.config) |
| 219 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 220 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 221 | task_config._validate_task_section, |
| 222 | task_info) |
| 223 | |
| 224 | def testNoDayForNonWeeklyEvent(self): |
| 225 | """Ensure a non-weekly Task cannot specify day.""" |
| 226 | self.config.set(self._TASK_NAME, 'day', '1') |
| 227 | self.config.set(self._TASK_NAME, 'run_on', 'nightly') |
| 228 | task_config = task_config_reader.TaskConfig(self.config) |
| 229 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 230 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 231 | task_config._validate_task_section, |
| 232 | task_info) |
| 233 | |
| 234 | def testInValidOSType(self): |
| 235 | """Ensure a Task's os_type is valid.""" |
| 236 | self.config.set(self._TASK_NAME, 'os_type', 'invalid') |
| 237 | task_config = task_config_reader.TaskConfig(self.config) |
| 238 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 239 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 240 | task_config._check_os_type, |
| 241 | task_info) |
| 242 | |
| 243 | def testNoLaunchControlForCrOS(self): |
| 244 | """Ensure a CrOS Task doesn't have launch control settings.""" |
| 245 | self.config.set(self._TASK_NAME, 'os_type', 'CrOS') |
| 246 | self.config.set(self._TASK_NAME, 'branches', 'shamu-userdebug') |
| 247 | task_config = task_config_reader.TaskConfig(self.config) |
| 248 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 249 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 250 | task_config._check_os_type, |
| 251 | task_info) |
| 252 | |
| 253 | def testLaunchControlForNonCrOS(self): |
| 254 | """Ensure an android Task has launch control settings.""" |
| 255 | self.config.set(self._TASK_NAME, 'os_type', 'android') |
| 256 | task_config = task_config_reader.TaskConfig(self.config) |
| 257 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 258 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 259 | task_config._check_os_type, |
| 260 | task_info) |
| 261 | |
| 262 | def testGetTestbedCount(self): |
| 263 | """Ensure testbed count can be successfully parsed.""" |
| 264 | task_config = task_config_reader.TaskConfig(self.config) |
| 265 | self.assertEqual(task_config._get_testbed_dut_count('sharu-2'), 2) |
| 266 | |
| 267 | def testNoTestbedForCrOS(self): |
| 268 | """Ensure CrOS task won't specify testbed parameters.""" |
| 269 | self.config.set(self._TASK_NAME, 'boards', 'sharu-2') |
| 270 | task_config = task_config_reader.TaskConfig(self.config) |
| 271 | task_info = task_config._read_task_section(self._TASK_NAME) |
| 272 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 273 | task_config._check_testbed_parameter, |
| 274 | task_info) |
| 275 | |
| 276 | def testInvalidBoardFamilies(self): |
| 277 | """Ensure to raise exception when a board family is invalid.""" |
| 278 | self.config.set(self._TASK_NAME, 'board_families', 'fake_one') |
| 279 | task_config = task_config_reader.TaskConfig(self.config) |
| 280 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 281 | task_config._read_task_section, |
| 282 | self._TASK_NAME) |
| 283 | |
| 284 | def testInvalidExcludeBoardFamilies(self): |
| 285 | """Ensure to raise exception when an exclude board family is invalid.""" |
| 286 | self.config.set(self._TASK_NAME, 'exclude_board_families', 'fake_one') |
| 287 | task_config = task_config_reader.TaskConfig(self.config) |
| 288 | self.assertRaises(task_config_reader.MalformedConfigEntryError, |
| 289 | task_config._read_task_section, |
| 290 | self._TASK_NAME) |