| # Copyright 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Module for task unittests.""" |
| # pylint: disable=g-missing-super-call |
| |
| import json |
| import re |
| import unittest |
| |
| import build_lib |
| import config_reader |
| import mock |
| import task |
| import task_config_reader |
| import tot_manager |
| |
| from chromite.api.gen.test_platform.suite_scheduler import analytics_pb2 |
| from chromite.api.gen.chromiumos import branch_pb2 |
| |
| from google.protobuf import json_format |
| |
| FAKE_UUID = 'c78e0bf3-4142-11ea-bc66-88e9fe4c5349' |
| |
| |
| class FakeTotMilestoneManager(tot_manager.TotMilestoneManager): |
| """Mock class for tot_manager.TotMilestoneManager.""" |
| |
| def __init__(self, is_sanity): |
| self.is_sanity = is_sanity |
| self.storage_client = None |
| self.tot = self._tot_milestone() |
| |
| |
| class FakeLabConfig(object): |
| """Mock class for config_reader.LabConfig.""" |
| |
| def get_cros_model_map(self): |
| return {'coral': ['robo', 'nasher', 'lava'], 'hatch': ['akemi', 'kled']} |
| |
| def get_android_model_map(self): |
| return {'pixel3': ['pixel3xl'], 'pixel4': ['pixel4a']} |
| |
| |
| class FakeBigqueryRestClient(object): |
| |
| def __init__(self, rest_client, project=None, dataset=None, table=None): |
| """Initialize the mock class.""" |
| self.table = table |
| self.rows = [] |
| |
| def insert(self, rows): |
| self.rows = rows |
| return True |
| |
| |
| class TaskBaseTestCase(unittest.TestCase): |
| |
| BOARD = 'fake_board' |
| UNIBUILD_BOARD = 'coral' |
| EXCLUDE_BOARD = 'exclude_fake_board' |
| NAME = 'fake_name' |
| ANALYTICS_NAME='fake_name' |
| SUITE = 'fake_suite' |
| POOL = 'fake_pool' |
| PLATFORM = '6182.0.0-rc2' |
| MILESTONE = '30' |
| NUM = 1 |
| PRIORITY = 50 |
| TIMEOUT = 600 |
| |
| def setUp(self): |
| tot_patcher = mock.patch('tot_manager.TotMilestoneManager') |
| self._mock_tot = tot_patcher.start() |
| self.addCleanup(tot_patcher.stop) |
| # Can't pass in False since storage_client is not mocked. |
| self._mock_tot.return_value = FakeTotMilestoneManager(True) |
| |
| _mock_bq_client = mock.patch('rest_client.BigqueryRestClient') |
| mock_bq_client = _mock_bq_client.start() |
| self.addCleanup(_mock_bq_client.stop) |
| self.mock_bq_client = FakeBigqueryRestClient( |
| None, project='proj', dataset='dataset', table='foo') |
| mock_bq_client.return_value = self.mock_bq_client |
| |
| self.task = task.Task( |
| task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=[], |
| analytics_name=self.ANALYTICS_NAME, |
| pool=self.POOL, |
| num=self.NUM, |
| boards=','.join( |
| [self.BOARD, self.UNIBUILD_BOARD]), |
| exclude_boards=self.EXCLUDE_BOARD, |
| priority=self.PRIORITY, |
| timeout=self.TIMEOUT)) |
| |
| self._empty_configs = config_reader.Configs( |
| lab_config=config_reader.LabConfig(None)) |
| self._fake_configs = config_reader.Configs( |
| lab_config=FakeLabConfig()) |
| |
| self.firmware_dict = { |
| ('cros', 'fake_board'): ('fake_board-release/R30-6182.0.0-rc2'), |
| ('firmware', 'fake_board'): ('firmware-fake_board-12345.67.' |
| 'A-firmwarebranch/RFoo-1.0.0-b1234567' |
| '/fake_board') |
| } |
| |
| |
| class TaskTestCase(TaskBaseTestCase): |
| |
| def setUp(self): |
| super(TaskTestCase, self).setUp() |
| |
| mock_push = mock.patch('task.Task._push_suite') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def tearDown(self): |
| self.task.boards = ','.join([self.BOARD, self.UNIBUILD_BOARD]) |
| self.task.models = None |
| |
| def testSetSpecCompareInfoEqual(self): |
| """Test compare info setting for specs that equals to a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '==tot-2') |
| self.task._set_spec_compare_info() |
| self.assertTrue(self.task._version_equal_constraint) |
| self.assertFalse(self.task._version_gte_constraint) |
| self.assertFalse(self.task._version_lte_constraint) |
| self.assertEqual(self.task._bare_branches, []) |
| |
| def testSetSpecCompareInfoLess(self): |
| """Test compare info setting for specs that is less than a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '<=tot') |
| self.task._set_spec_compare_info() |
| self.assertFalse(self.task._version_equal_constraint) |
| self.assertFalse(self.task._version_gte_constraint) |
| self.assertTrue(self.task._version_lte_constraint) |
| self.assertEqual(self.task._bare_branches, []) |
| |
| def testSetSpecCompareInfoGreater(self): |
| """Test compare info setting for specs that is greater than a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '>=tot-2') |
| self.task._set_spec_compare_info() |
| self.assertFalse(self.task._version_equal_constraint) |
| self.assertTrue(self.task._version_gte_constraint) |
| self.assertFalse(self.task._version_lte_constraint) |
| self.assertEqual(self.task._bare_branches, []) |
| |
| def testFitsSpecEqual(self): |
| """Test milestone check for specs that equals to a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '==tot-1') |
| self.task._set_spec_compare_info() |
| self.assertFalse(self.task._fits_spec('40')) |
| self.assertTrue(self.task._fits_spec('39')) |
| self.assertFalse(self.task._fits_spec('38')) |
| |
| def testFitsSpecLess(self): |
| """Test milestone check for specs that is less than a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '<=tot-1') |
| self.task._set_spec_compare_info() |
| self.assertFalse(self.task._fits_spec('40')) |
| self.assertTrue(self.task._fits_spec('39')) |
| self.assertTrue(self.task._fits_spec('38')) |
| |
| def testFitsSpecGreater(self): |
| """Test milestone check for specs that is greater than a milestone.""" |
| self.task.branch_specs = re.split(r'\s*,\s*', '>=tot-2') |
| self.task._set_spec_compare_info() |
| self.assertTrue(self.task._fits_spec('39')) |
| self.assertTrue(self.task._fits_spec('38')) |
| self.assertFalse(self.task._fits_spec('37')) |
| |
| def testGetFirmwareSuccessfully(self): |
| """Test get firmware from firmware_dict successfully.""" |
| |
| firmware = self.task._get_firmware_build( |
| 'cros', self.BOARD, self.firmware_dict, None) |
| self.assertEqual( |
| firmware, |
| '{0}-release/R{1}-{2}'.format(self.BOARD, |
| self.MILESTONE, |
| self.PLATFORM)) |
| firmware = self.task._get_firmware_build( |
| 'firmware', self.BOARD, self.firmware_dict, None) |
| self.assertEqual( |
| firmware, |
| 'firmware-{0}-12345.67.A-firmwarebranch/' |
| 'RFoo-1.0.0-b1234567/{0}'.format(self.BOARD)) |
| |
| def testGetNonExistentBoard(self): |
| """Test get firmware for non-existent board from firmware_dict.""" |
| self.assertIsNone(self.task._get_firmware_build( |
| 'firmware', 'non-existent', self.firmware_dict, None)) |
| |
| def testScheduleCrosSuccessfully(self): |
| """test schedule cros builds successfully.""" |
| branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'} |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 1) |
| |
| def testScheduleCrosNonvalidBoard(self): |
| """Test schedule no cros builds due to non-allowed board.""" |
| branch_builds = {('%s_2' % self.BOARD, None, 'release', '56'): '0000.00.00'} |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._empty_configs) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleCrosExcludeBoard(self): |
| """Test schedule no cros builds due to board is excluded.""" |
| branch_builds = {(self.EXCLUDE_BOARD, None, 'release', '56'): '0000.00.00'} |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._empty_configs) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleCrosSuccessfullyWithSpecifiedModel(self): |
| """Test schedule unibuild with specific model.""" |
| self.task.board = self.UNIBUILD_BOARD |
| branch_builds = {(self.UNIBUILD_BOARD, 'robo', 'release', '56'): |
| '0000.00.00', |
| (self.UNIBUILD_BOARD, 'lava', 'release', '56'): |
| '0000.00.00'} |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 2) |
| |
| def testScheduleCrosSuccessfullyForBoardWithSuffix(self): |
| """Test schedule board with suffix and specific model.""" |
| branch_builds = { |
| ('hatch-kernelnext', None, 'release', '56'): '0000.00.00' |
| } |
| self.task.boards = 'hatch-kernelnext' |
| self.task.models = 'hatch_akemi' |
| self.task._schedule_cros_builds(branch_builds, self.firmware_dict, |
| self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 1) |
| |
| def testScheduleCrosNonValidSpec(self): |
| """Test schedule no cros builds due to non-allowed branch milestone.""" |
| branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'} |
| # Only run tasks whose milestone = tot (R40) |
| self.task.branch_specs = re.split(r'\s*,\s*', '==tot') |
| self.task._set_spec_compare_info() |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._empty_configs) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleCrosSuccessfullyWithValidFirmware(self): |
| """Test schedule cros builds successfully with valid firmware.""" |
| branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'} |
| self.task.firmware_ro_build_spec = 'firmware' |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 1) |
| |
| def testScheduleCrosWithNonValidFirmware(self): |
| """Test schedule no cros builds due to non-existent firmware.""" |
| branch_builds = {('foo-board', None, 'release', '56'): '0000.00.00'} |
| self.task.firmware_ro_build_spec = 'firmware' |
| self.task._schedule_cros_builds(branch_builds, |
| self.firmware_dict, |
| self._empty_configs) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleLaunchControlWithFullBranches(self): |
| """Test schedule all launch control builds successfully.""" |
| lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertEqual(self._mock_push.call_count, 2) |
| |
| def testScheduleLaunchControlWithPartlyBranches(self): |
| """Test schedule part of launch control builds due to branch check.""" |
| lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertEqual(self._mock_push.call_count, 1) |
| |
| def testScheduleLaunchControlWithNoBranches(self): |
| """Test schedule none of launch control builds due to branch check.""" |
| lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| self.task.launch_control_branches = [] |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleLaunchControlNonvalidBoard(self): |
| """Test schedule none of launch control builds due to board check.""" |
| lc_builds = {'%s_2' % self.BOARD: |
| ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| def testScheduleLaunchControlExcludeBoard(self): |
| """Test schedule none of launch control builds due to board check.""" |
| lc_builds = {self.EXCLUDE_BOARD: |
| ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertEqual(self._mock_push.call_count, 0) |
| |
| |
| class TaskPushTestCase(TaskBaseTestCase): |
| |
| def setUp(self): |
| super(TaskPushTestCase, self).setUp() |
| mock_push = mock.patch('task_executor.push') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testScheduleCrOSIsPushSuccessfully(self): |
| """Test IS_PUSHED is changed if some CrOS suites are scheduled.""" |
| branch_builds = {(self.BOARD, None, 'release', '56'): '0000.00.00'} |
| self.task.os_type = build_lib.OS_TYPE_CROS |
| self.assertTrue(self.task.schedule( |
| [], (branch_builds, []), self.firmware_dict, self._fake_configs)) |
| |
| def testScheduleLaunchControlIsPushSuccessfully(self): |
| """Test IS_PUSHED is changed if some launch control suites are scheduled.""" |
| lc_builds = {self.BOARD: ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task.os_type = build_lib.OS_TYPES_LAUNCH_CONTROL |
| self.assertTrue(self.task.schedule( |
| lc_builds, ([], []), self.firmware_dict, |
| self._empty_configs)) |
| |
| def testScheduleCrosIsPushInvalidBoard(self): |
| """Test schedule no cros builds due to non-allowed board.""" |
| branch_builds = ( |
| {('%s_2' % self.BOARD, None, 'release', '56'): '0000.00.00'}, {}, |
| ) |
| self.task.os_type = build_lib.OS_TYPE_CROS |
| self.assertFalse(self.task.schedule( |
| [], branch_builds, self.firmware_dict, self._empty_configs)) |
| |
| def testScheduleLaunchControlIsPushInvalidBoard(self): |
| """Test schedule none of launch control builds due to board check.""" |
| lc_builds = {'%s_2' % self.BOARD: |
| ['git_nyc-mr2-release/shamu-userdebug/3844975', |
| 'git_nyc-mr1-release/shamu-userdebug/3783920']} |
| lc_branches = 'git_nyc-mr1-release,git_nyc-mr2-release' |
| self.task.launch_control_branches = [ |
| t.lstrip() for t in lc_branches.split(',')] |
| self.task.os_type = build_lib.OS_TYPES_LAUNCH_CONTROL |
| self.task._schedule_launch_control_builds(lc_builds) |
| self.assertFalse(self.task.schedule( |
| lc_builds, ([], []), self.firmware_dict, |
| self._empty_configs)) |
| |
| |
| class TaskJobSectionTestCase(TaskBaseTestCase): |
| |
| def setUp(self): |
| super(TaskJobSectionTestCase, self).setUp() |
| mock_push = mock.patch('task_executor.push') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testJobSectionName(self): |
| """Test the job section name if recorded.""" |
| self.task.schedule( |
| [], ([], []), self.firmware_dict, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 0) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(job_sec.job_name, 'fake_name') |
| |
| def testAddBoard(self): |
| """Test the configured board is recorded.""" |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=[], |
| boards='coral')) |
| new_task.schedule( |
| [], ([], []), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.build_targets), 1) |
| self.assertEqual(job_sec.build_targets[0].name, 'coral') |
| |
| def testAddModel(self): |
| """Test the configured model is recorded.""" |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=[], |
| boards='coral', |
| exclude_models='coral_robo,coral_nasher')) |
| new_task.schedule( |
| [], ([], []), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| # lava is the only coral model that was not excluded. |
| self.assertEqual(len(job_sec.models), 1) |
| self.assertEqual(job_sec.models[0].value, 'lava') |
| |
| def testAddMatchedBuild(self): |
| """Test the matched build is recorded.""" |
| branch_builds = {('coral', '', 'release', '40'): |
| '0000.00.00', |
| ('hatch', '', 'release', '37'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral,hatch', |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.matched_builds), 1) |
| build = job_sec.matched_builds[0].release_build |
| # hatch should not be recorded as its build is too old. |
| self.assertEqual(build.build_target.name, 'coral') |
| self.assertEqual(build.milestone, 40) |
| self.assertEqual(build.chrome_os_version, '0000.00.00') |
| self.assertEqual(build.type, branch_pb2.Branch.RELEASE) |
| |
| def testAddMatchedRelaxBuild(self): |
| """Test the matched relax build is recorded.""" |
| relax_builds = {('coral', '', 'release', '40'): |
| '0000.00.00', |
| ('hatch', '', 'release', '37'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral,hatch', |
| only_successful_build_required=True, |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], ([], relax_builds), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.matched_builds), 1) |
| build = job_sec.matched_builds[0].relax_build |
| # hatch should not be recorded as its build is too old. |
| self.assertEqual(build.build_target.name, 'coral') |
| self.assertEqual(build.milestone, 40) |
| self.assertEqual(build.chrome_os_version, '0000.00.00') |
| self.assertEqual(build.type, branch_pb2.Branch.RELEASE) |
| |
| def testAddMatchedFirmwareBuild(self): |
| """Test the matched firmware build is recorded.""" |
| branch_builds = {('fake_board', '', 'release', '40'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='fake_board', |
| firmware_rw_build_spec='firmware', |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.matched_builds), 2) |
| build = job_sec.matched_builds[1].firmware_rw_build |
| self.assertEqual(build.build_target.name, 'fake_board') |
| self.assertEqual(build.type, branch_pb2.Branch.FIRMWARE) |
| self.assertEqual(build.artifact.path, |
| self.firmware_dict['firmware', 'fake_board']) |
| |
| def testScheduleJobSuccessfully(self): |
| """Test the scheduled job is recorded.""" |
| branch_builds = {('coral', 'lava', 'release', '40'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral', |
| os_type=build_lib.OS_TYPE_CROS)) |
| with mock.patch('uuid.uuid1', return_value=FAKE_UUID): |
| new_task.schedule( |
| [], (branch_builds, []), self.firmware_dict, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.schedule_jobs), 1) |
| job = job_sec.schedule_jobs[0] |
| self.assertEqual(job.build_target.name, 'coral') |
| self.assertEqual(job.model.value, 'lava') |
| self.assertEqual(job.queued_task_id, FAKE_UUID) |
| |
| def testScheduleJobFailedforFirmwareRO(self): |
| """Test the dropped job due to firmware absence is recorded.""" |
| branch_builds = {('coral', 'lava', 'release', '40'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral', |
| firmware_ro_build_spec='firmware', |
| firmware_ro_version=None, |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), {}, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.schedule_jobs), 1) |
| job = job_sec.schedule_jobs[0] |
| self.assertEqual(job.build_target.name, 'coral') |
| self.assertEqual(job.model.value, 'lava') |
| self.assertIn('No RO firmware build to run', job.justification) |
| |
| def testScheduleJobFailedforFirmwareRW(self): |
| """Test the dropped job due to firmware absence is recorded.""" |
| branch_builds = {('coral', 'lava', 'release', '40'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral', |
| firmware_rw_build_spec='firmware', |
| firmware_rw_version=None, |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), {}, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.schedule_jobs), 1) |
| job = job_sec.schedule_jobs[0] |
| self.assertEqual(job.build_target.name, 'coral') |
| self.assertEqual(job.model.value, 'lava') |
| self.assertIn('No RW firmware build to run', job.justification) |
| |
| def testScheduleJobFailedforBranchNotMaching(self): |
| """Test the dropped job due to branch spec is recorded.""" |
| branch_builds = {('coral', 'lava', 'release', '38'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-1'], |
| boards='coral', |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), {}, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| self.assertEqual(len(job_sec.schedule_jobs), 1) |
| job = job_sec.schedule_jobs[0] |
| self.assertEqual(job.build_target.name, 'coral') |
| self.assertEqual(job.model.value, 'lava') |
| self.assertIn('branch_build spec', job.justification) |
| |
| def testScheduleJobFailedforAnyModel(self): |
| """Test the dropped job due to any_model is recorded.""" |
| branch_builds = {('coral', None, 'release', '38'): |
| '0000.00.00'} |
| new_task = task.Task(task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='coral', |
| any_model=True, |
| exclude_models='coral_nasher', |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule( |
| [], (branch_builds, []), {}, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| jobs = job_sec.schedule_jobs |
| # 'nasher' is excluded, hence there should be two jobs for the rest models |
| # 'robo', 'lava'. |
| self.assertEqual(2, len(jobs)) |
| # The first job should be scheduled with empty model, as any_model is True. |
| self.assertEqual(jobs[0].build_target.name, 'coral') |
| self.assertEqual(jobs[0].model.value, '') |
| # The second job should be skipped and the justification is recorded. |
| self.assertEqual(jobs[1].build_target.name, 'coral') |
| self.assertEqual(jobs[1].model.value, 'lava') |
| self.assertIn('Skip model lava as any_model enabled', jobs[1].justification) |
| |
| def testScheduleJobForBoardWithSuffix(self): |
| """Test record a schedule job with board plus suffix and model specified.""" |
| branch_builds = {('hatch-kernelnext', None, 'release', '38'): '0000.00.00'} |
| new_task = task.Task( |
| task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=['>=tot-2'], |
| boards='hatch-kernelnext', |
| models='hatch_akemi', |
| os_type=build_lib.OS_TYPE_CROS)) |
| new_task.schedule([], (branch_builds, []), {}, self._fake_configs) |
| job_sec = json_format.Parse( |
| json.dumps(self.mock_bq_client.rows[0]['json']), |
| analytics_pb2.ScheduleJobSection()) |
| jobs = job_sec.schedule_jobs |
| # Only the specified model should appear in the scheduled jobs. |
| self.assertEqual(1, len(jobs)) |
| self.assertEqual(jobs[0].build_target.name, 'hatch-kernelnext') |
| self.assertEqual(jobs[0].model.value, 'akemi') |
| |
| |
| class TaskBaseMultiDUTsTestCase(unittest.TestCase): |
| |
| NAME = 'fake_name' |
| ANALYTICS_NAME='fake_name' |
| SUITE = 'fake_suite' |
| POOL = 'fake_pool' |
| PLATFORM = '6182.0.0-rc2' |
| MILESTONE = '30' |
| NUM = 1 |
| PRIORITY = 50 |
| TIMEOUT = 600 |
| BUILD_DICT = { |
| ('coral', 'robo', 'release', '55'): '0000.00.00', |
| ('coral', 'robo', 'release', '56'): '0000.00.00', |
| ('coral', 'nasher', 'release', '55'): '0000.00.00', |
| ('coral', 'nasher', 'release', '56'): '0000.00.00', |
| ('coral', 'lava', 'release', '55'): '0000.00.00', |
| ('coral', 'lava', 'release', '56'): '0000.00.00', |
| ('hatch', 'akemi', 'release', '55'): '0000.00.00', |
| ('hatch', 'akemi', 'release', '56'): '0000.00.00', |
| ('hatch', 'kled', 'release', '55'): '0000.00.00', |
| ('hatch', 'kled', 'release', '56'): '0000.00.00', |
| } |
| RECENT_BUILD_DICT = { |
| ('coral', None, 'release', '55'): '0000.00.00', |
| ('coral', None, 'release', '56'): '0000.00.00', |
| ('hatch', None, 'release', '55'): '0000.00.00', |
| ('hatch', None, 'release', '56'): '0000.00.00', |
| } |
| |
| def setUp(self, multi_dut_boards=None, multi_dut_models=None): |
| tot_patcher = mock.patch('tot_manager.TotMilestoneManager') |
| self._mock_tot = tot_patcher.start() |
| self.addCleanup(tot_patcher.stop) |
| # Can't pass in False since storage_client is not mocked. |
| self._mock_tot.return_value = FakeTotMilestoneManager(True) |
| |
| _mock_bq_client = mock.patch('rest_client.BigqueryRestClient') |
| mock_bq_client = _mock_bq_client.start() |
| self.addCleanup(_mock_bq_client.stop) |
| self.mock_bq_client = FakeBigqueryRestClient( |
| None, project='proj', dataset='dataset', table='foo') |
| mock_bq_client.return_value = self.mock_bq_client |
| |
| self.task = task.Task( |
| task_config_reader.TaskInfo( |
| name=self.NAME, |
| suite=self.SUITE, |
| branch_specs=[], |
| analytics_name=self.ANALYTICS_NAME, |
| pool=self.POOL, |
| num=self.NUM, |
| multi_dut_boards=multi_dut_boards, |
| multi_dut_models=multi_dut_models, |
| priority=self.PRIORITY, |
| timeout=self.TIMEOUT)) |
| |
| self._empty_configs = config_reader.Configs( |
| lab_config=config_reader.LabConfig(None)) |
| self._fake_configs = config_reader.Configs( |
| lab_config=FakeLabConfig()) |
| |
| |
| class TaskMultiDUTsByBoardTestCase(TaskBaseMultiDUTsTestCase): |
| |
| def setUp(self): |
| super(TaskMultiDUTsByBoardTestCase, self).setUp( |
| multi_dut_boards="coral;hatch, hatch;coral") |
| |
| mock_push = mock.patch('task.Task._push_suite') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testMultiDUTsTaskAttributes(self): |
| """Test correct multi-duts related attributes is set when |
| multi_dut_boards presents in configs. |
| """ |
| self.assertEqual(self.task.is_multi_dut_testing, True) |
| self.assertEqual(self.task.multi_dut_boards, |
| [['coral', 'hatch'], ['hatch', 'coral']]) |
| |
| def testGetMultiDUTsBuildTargetsDictWithAnyModel(self): |
| """Test the correct build targets dict can be generated with |
| any_model set to True. |
| """ |
| self.task.any_model = True |
| d = self.task._get_multi_duts_build_targets_dict(self._fake_configs) |
| expected_dict = { |
| 'coral': [ |
| [ |
| task.BuildTarget('coral', None, None), |
| task.BuildTarget('hatch', None, None), |
| ], |
| ], |
| 'hatch': [ |
| [ |
| task.BuildTarget('hatch', None, None), |
| task.BuildTarget('coral', None, None), |
| ], |
| ] |
| } |
| self.assertEqual(d, expected_dict) |
| |
| def testGetMultiDUTsBuildTargetsDictWithoutAnyModel(self): |
| """Test the correct build targets dict can be generated with |
| any_model set to False. |
| """ |
| self.task.any_model = False |
| d = self.task._get_multi_duts_build_targets_dict(self._fake_configs) |
| expected_dict = { |
| 'coral': [ |
| [ |
| task.BuildTarget('coral', 'robo', None), |
| task.BuildTarget('hatch', None, None), |
| ], |
| [ |
| task.BuildTarget('coral', 'nasher', None), |
| task.BuildTarget('hatch', None, None), |
| ], |
| [ |
| task.BuildTarget('coral', 'lava', None), |
| task.BuildTarget('hatch', None, None), |
| ], |
| ], |
| 'hatch': [ |
| [ |
| task.BuildTarget('hatch', 'akemi', None), |
| task.BuildTarget('coral', None, None), |
| ], |
| [ |
| task.BuildTarget('hatch', 'kled', None), |
| task.BuildTarget('coral', None, None), |
| ], |
| ] |
| } |
| self.assertEqual(d, expected_dict) |
| |
| def testScheduleMultiDUTsCrosWithAnyModel(self): |
| """Test schedule multi DUTs cros build with any_model set to True.""" |
| self.task.any_model = True |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 4) |
| |
| def testScheduleMultiDUTsCrosWithoutAnyModel(self): |
| """Test schedule multi DUTs cros build with any_model set to False.""" |
| self.task.any_model = False |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 10) |
| |
| def testScheduleMultiDUTsWithMissingSecondaryBuild(self): |
| """Test schedule multi DUTs cros build when missing secondary build |
| info in recent dict, the expectation is no suite will be pushed. |
| """ |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, {}, self._fake_configs) |
| assert not self._mock_push.called |
| |
| |
| class TaskMultiDUTsByModelTestCase(TaskBaseMultiDUTsTestCase): |
| |
| def setUp(self): |
| super(TaskMultiDUTsByModelTestCase, self).setUp( |
| multi_dut_models='robo;akemi, kled;lava') |
| |
| mock_push = mock.patch('task.Task._push_suite') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testMultiDUTsTaskAttributes(self): |
| """Test correct multi-duts related attributes is set when |
| multi_dut_models presents in configs. |
| """ |
| self.assertEqual(self.task.is_multi_dut_testing, True) |
| self.assertEqual(self.task.multi_dut_models, |
| [['robo', 'akemi'], ['kled', 'lava']]) |
| |
| def testGetMultiDUTsBuildTargetsDict(self): |
| """Test the correct build targets dict can be generated.""" |
| d = self.task._get_multi_duts_build_targets_dict(self._fake_configs) |
| expected_dict = { |
| 'coral': [ |
| [ |
| task.BuildTarget('coral', 'robo', None), |
| task.BuildTarget('hatch', 'akemi', None), |
| ], |
| ], |
| 'hatch': [ |
| [ |
| task.BuildTarget('hatch', 'kled', None), |
| task.BuildTarget('coral', 'lava', None), |
| ], |
| ] |
| } |
| self.assertEqual(d, expected_dict) |
| |
| def testScheduleMultiDUTsCrosWithoutAnyModel(self): |
| """Test schedule multi DUTs cros build.""" |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 4) |
| |
| |
| class TaskMultiDUTsWithAndroidByBoardTestCase(TaskBaseMultiDUTsTestCase): |
| |
| def setUp(self): |
| super(TaskMultiDUTsWithAndroidByBoardTestCase, self).setUp( |
| multi_dut_boards='coral;pixel3, hatch;pixel4') |
| |
| mock_push = mock.patch('task.Task._push_suite') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testGetMultiDUTsBuildTargetsDictWithAndroidByBoard(self): |
| """Test the correct build targets dict can be generated with |
| Android devices when specified by board. |
| """ |
| self.task.any_model = True |
| d = self.task._get_multi_duts_build_targets_dict(self._fake_configs) |
| expected_dict = { |
| 'coral': [ |
| [ |
| task.BuildTarget('coral', None, None), |
| task.BuildTarget('pixel3', None, None), |
| ], |
| ], |
| 'hatch': [ |
| [ |
| task.BuildTarget('hatch', None, None), |
| task.BuildTarget('pixel4', None, None), |
| ], |
| ] |
| } |
| self.assertEqual(d, expected_dict) |
| |
| def testScheduleMultiDUTsWithAndroidByBoard(self): |
| """Test schedule multi DUTs cros build with Android by board.""" |
| self.task.any_model = True |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 4) |
| |
| |
| class TaskMultiDUTsWithAndroidByModelTestCase(TaskBaseMultiDUTsTestCase): |
| |
| def setUp(self): |
| super(TaskMultiDUTsWithAndroidByModelTestCase, self).setUp( |
| multi_dut_models='robo;pixel3xl, kled;pixel4a') |
| |
| mock_push = mock.patch('task.Task._push_suite') |
| self._mock_push = mock_push.start() |
| self.addCleanup(mock_push.stop) |
| |
| def testGetMultiDUTsBuildTargetsDictWithAndroidByModel(self): |
| """Test the correct build targets dict can be generated with |
| Android devices when specified by model. |
| """ |
| d = self.task._get_multi_duts_build_targets_dict(self._fake_configs) |
| expected_dict = { |
| 'coral': [ |
| [ |
| task.BuildTarget('coral', 'robo', None), |
| task.BuildTarget('pixel3', 'pixel3xl', None), |
| ], |
| ], |
| 'hatch': [ |
| [ |
| task.BuildTarget('hatch', 'kled', None), |
| task.BuildTarget('pixel4', 'pixel4a', None), |
| ], |
| ] |
| } |
| self.assertEqual(d, expected_dict) |
| |
| def testScheduleMultiDUTsWithAndroidByModel(self): |
| """Test schedule multi DUTs cros build with Android by model.""" |
| self.task.any_model = True |
| self.task._schedule_multi_duts_cros_builds( |
| self.BUILD_DICT, self.RECENT_BUILD_DICT, self._fake_configs) |
| self.assertEqual(self._mock_push.call_count, 4) |