blob: 62d1f408f81260f148cda3628cca53eedbc90268 [file] [log] [blame]
# Copyright 2024 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module for trigger_receiver unittests."""
# pylint: disable=g-missing-super-call
import constants
import datetime
import safe_log
import json
import os
import unittest
import build_lib
import config_reader
import datastore_client
import file_getter
import mock
import task_config_reader
import task_executor
import time_converter
import trigger_receiver
import buildbucket
import ctpv2_requester
from chromite.third_party.infra_libs.buildbucket.proto import builder_common_pb2
from google.appengine.api import taskqueue
from google.appengine.ext import ndb
from google.appengine.ext import testbed
from google.protobuf import json_format
# Ensure that SUITE_SCHEDULER_CONFIG_FILE is read only once.
_SUITE_CONFIG_READER = config_reader.ConfigReader(
file_getter.TEST_SUITE_SCHEDULER_CONFIG_FILE)
def now_generator(start_time, interval_min=30, last_days=7):
"""A datetime.datetime.now generator.
The generator will generate 'now' from start_time till start_time+last_days
for every interval_min.
Args:
start_time: A datetime.datetime object representing the initial value of
the 'now'.
interval_min: The interval minutes between current 'now' and next 'now.
last_days: Representing how many days this generator will last.
Yields:
a datetime.datetime object to mock the current time.
"""
cur_time = start_time
end_time = start_time + datetime.timedelta(days=last_days)
while cur_time < end_time:
yield cur_time
cur_time += datetime.timedelta(minutes=interval_min)
def _should_schedule_timed_task(last_now, now):
"""Check whether timed (weekly/nightly/fortnightly) task should be scheduled.
A timed task should be schduled when next hour is coming.
Args:
last_now: the last time to check if timed task should be scheduled
or not.
now: the current time to check if timed task should be scheduled.
Returns:
a boolean indicating whether there will be nightly tasks scheduled.
"""
if last_now is not None and last_now.hour != now.hour:
return True
return False
def _should_schedule_new_build_task(last_now, now):
"""Check whether weekly task should be scheduled.
A new_build task should be schduled when there're new builds between last
check and this check.
Args:
last_now: the last time to check if new_build task should be scheduled.
now: the current time to check if new_build task should be scheduled.
Returns:
a boolean indicating whether there will be new_build tasks scheduled.
"""
if last_now is not None and last_now != now:
return True
return False
def _get_current_min(now):
"""A helper to truncate the given time to minute."""
return datetime.datetime(
now.year,
now.month,
now.day,
now.hour,
now.minute,
tzinfo=time_converter.UTC_TZ)
class FakeBuildClient(object):
"""Mock rest_client.BuildBucketBigqueryClient."""
def __init__(self):
self.firmware_builds_called = 0
def get_passed_builds(self, earliest, latest, event_type):
"""Mock get_passed_builds."""
del earliest # unused
del latest # unused
del event_type #unused
return [
build_lib.BuildInfo(
'link', None, '62', '9868.0.0', 'link-release'),
build_lib.BuildInfo(
'zako', None, '62', '9868.0.0', 'zako-release'),
build_lib.BuildInfo('samus-kernelnext', None, '62',
'9868.0.0', 'samus-kernelnext-release'),
build_lib.BuildInfo(
'reef', None, '62', '1234.0.0', 'reef-release'),
build_lib.BuildInfo(
'coral', 'santa', '62', '9868.0.0', 'coral-release'),
build_lib.BuildInfo(
'coral', 'astronaut', '62', '9868.0.0', 'coral-release'),
build_lib.BuildInfo(
'coral', 'lava', '62', '9868.0.0', 'coral-release'),
build_lib.BuildInfo(
'atlas-kernelnext', None, '60', '8500.0.0', 'atlas-kernelnext-release'),
build_lib.BuildInfo(
'atlas-kernelnext', None, '62', '9868.0.0', 'atlas-kernelnext-release'),
build_lib.BuildInfo('grunt', None, '63', '9968.0.0', 'grunt-release'),
build_lib.BuildInfo('nami', None, '62', '9867.0.0', 'nami-release'),
build_lib.BuildInfo('nami', 'akali', '62', '9868.0.0', 'nami-release'),
build_lib.BuildInfo('nami', 'bard', '62', '9866.0.0', 'nami-release'),
]
def get_latest_passed_firmware_builds(self):
"""Mock get_passed_firmware_builds."""
self.firmware_builds_called += 1
return [
['cros', 'fake_board', ('gs://chromeos-image-archive/'
'fake_board-release/R30-6182.0.0-rc2/'
'fake_board')],
['firmware', 'fake_board', ('gs://chromeos-image-archive/'
'firmware-fake_board-12345.67.'
'A-firmwarebranch/RFoo-1.0.0-b1e234567')],
['firmware', 'zako', ('gs://chromeos-image-archive/'
'firmware-zako-12345.67.'
'A-firmwarebranch/RFoo-1.0.0-b1e234567')],
['firmware', 'alt_board', ('gs://chromeos-image-archive/'
'firmware-alt_board-12345.67.'
'A-firmwarebranch/RFoo-1.0.0-b1e234567')],
]
class FakeAndroidBuildRestClient(object):
"""Mock rest_client.AndroidBuildRestClient."""
def get_latest_build_id(self, branch, target):
"""Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
del branch, target # unused
return '100'
class FakeLabConfig(object):
"""Mock config_reader.LabConfig."""
def get_android_board_list(self):
"""Mock config_reader.LabConfig.get_android_board_list."""
return ('android-angler', 'android-bullhead')
def get_cros_board_list(self):
"""Mock config_reader.LabConfig.get_cros_board_list."""
return ('grunt', 'link', 'peppy', 'daisy', 'zako', 'samus-kernelnext',
'coral', 'reef', 'nami', 'atlas-kernelnext', 'kevin-kernelnext')
def get_cros_model_map(self):
"""Mock config_reader.LabConfig.get_cros_model_map."""
return {
'coral': ['santa', 'astronaut'],
'reef': ['electro'],
'nami': ['akali', 'bard'],
}
def get_android_model_map(self):
""""Mock config_reader.LabConfig.get_android_model_map."""
return {
'pixel5': ['pixel5', 'pixel5a'],
'pixel6': ['pixel6', 'pixel6pro'],
'pixel7': ['pixel7'],
}
class TriggerReceiverBaseTestCase(unittest.TestCase):
_DEFAULT_BUILDER = builder_common_pb2.BuilderID(project='foo-proj',
bucket='foo-bucket',
builder='foo-builder')
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.addCleanup(self.testbed.deactivate)
self.testbed.init_datastore_v3_stub()
self.testbed.init_memcache_stub()
ndb.get_context().clear_cache()
self.testbed.init_taskqueue_stub(
root_path=os.path.join(os.path.dirname(__file__)))
self.taskqueue_stub = self.testbed.get_stub(
testbed.TASKQUEUE_SERVICE_NAME)
mock_build_client = mock.patch('rest_client.BuildBucketBigqueryClient')
self._mock_build_client = mock_build_client.start()
self.addCleanup(mock_build_client.stop)
mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
self._mock_android_client = mock_android_client.start()
self.addCleanup(mock_android_client.stop)
mock_lab_config = mock.patch('config_reader.LabConfig')
self._mock_lab_config = mock_lab_config.start()
self.addCleanup(mock_lab_config.stop)
mock_utc_now = mock.patch('time_converter.utc_now')
self._mock_utc_now = mock_utc_now.start()
self.addCleanup(mock_utc_now.stop)
self._mock_build_client.return_value = FakeBuildClient()
self._mock_android_client.return_value = FakeAndroidBuildRestClient()
self._mock_lab_config.return_value = FakeLabConfig()
board_family_patcher = mock.patch(
'build_lib.get_board_family_mapping_from_gs')
board_family_getter = board_family_patcher.start()
board_family_getter.return_value = {
'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
'ivybridge': ['link', 'link_freon']}
self.addCleanup(board_family_patcher.stop)
class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
_TEST_PST_HOUR = 20
_TEST_PST_DAY = 4 # Friday
_TEST_EVENT_PST_HOUR = 13
_FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
_FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
_FAKE_FORTNIGHTLY_TASK_NAME = 'fake_fortnightly_task'
def setUp(self):
super(TriggerReceiverFakeConfigTestCase, self).setUp()
self.fake_config = config_reader.ConfigReader(None)
self._add_nightly_tasks(self.fake_config)
self._add_weekly_tasks(self.fake_config)
self.fake_config_with_settings = config_reader.ConfigReader(None)
self._add_weekly_tasks(self.fake_config_with_settings)
self._add_weekly_params(self.fake_config_with_settings)
self._add_fortnightly_tasks(self.fake_config_with_settings)
self._add_fortnightly_params(self.fake_config_with_settings)
mock_config_reader = mock.patch('config_reader.ConfigReader')
self._mock_config_reader = mock_config_reader.start()
self.addCleanup(mock_config_reader.stop)
def _add_nightly_tasks(self, fake_config):
fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
str(self._TEST_PST_HOUR))
def _add_weekly_tasks(self, fake_config):
fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
def _add_weekly_params(self, fake_config):
weekly_section_name = task_config_reader.EVENT_CLASSES[
'weekly'].section_name()
fake_config.add_section(weekly_section_name)
fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
def _add_fortnightly_tasks(self, fake_config):
fake_config.add_section(self._FAKE_FORTNIGHTLY_TASK_NAME)
fake_config.set(self._FAKE_FORTNIGHTLY_TASK_NAME, 'suite', 'fake_suite')
fake_config.set(self._FAKE_FORTNIGHTLY_TASK_NAME, 'run_on', 'fortnightly')
fake_config.set(self._FAKE_FORTNIGHTLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
def _add_fortnightly_params(self, fake_config):
fortnightly_section_name = task_config_reader.EVENT_CLASSES[
'fortnightly'].section_name()
fake_config.add_section(fortnightly_section_name)
fake_config.set(fortnightly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
def testInitializeTriggerReceiverWithNightlyEvent(self):
"""Test nightly event can be handled on right hour."""
# A task with hour=20 should be scheduled at 20:00 in PST everyday, which
# is 3:00/4:00 in UTC everyday.
self._mock_config_reader.return_value = self.fake_config
given_utc_hour = time_converter.convert_time_info(
time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
tzinfo=time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'nightly', utc_now - datetime.timedelta(hours=1))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertTrue(suite_trigger.events['nightly'].should_handle)
self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
self.assertEqual(suite_trigger.event_results['nightly'][0],
self._FAKE_NIGHTLY_TASK_NAME)
def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
"""Test weekly event without event settings can be handled on right day."""
# A task with day=4 (Friday) and default event_hour (23) should be
# scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
self._mock_config_reader.return_value = self.fake_config
given_utc_hour = time_converter.convert_time_info(
time_converter.TimeInfo(
self._TEST_PST_DAY,
task_config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
tzinfo=time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'weekly', utc_now - datetime.timedelta(days=1))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertTrue(suite_trigger.events['weekly'].should_handle)
self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
self.assertEqual(suite_trigger.event_results['weekly'][0],
self._FAKE_WEEKLY_TASK_NAME)
def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
"""Test weekly event with event settings can be handled on right day."""
# A task with day=4 (Friday) and event_hour=13 should be scheduled at
# Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
self._mock_config_reader.return_value = self.fake_config_with_settings
given_utc_time_info = time_converter.convert_time_info(
time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
# Set the current time as a Friday 20:00 or 21:00 in UTC.
utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
tzinfo=time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'weekly', utc_now - datetime.timedelta(days=1))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertTrue(suite_trigger.events['weekly'].should_handle)
self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
self.assertEqual(suite_trigger.event_results['weekly'][0],
self._FAKE_WEEKLY_TASK_NAME)
class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
"""Test the new_build functionality."""
def setUp(self):
"""Set up for a test."""
super(TriggerReceiverFakeBuildTestCase, self).setUp()
self._prepare()
def _prepare(self):
self.reader = config_reader.ConfigReader(None)
self.fake_config = self.reader
mock_config_reader = mock.patch('config_reader.ConfigReader')
self._mock_config_reader = mock_config_reader.start()
self.addCleanup(mock_config_reader.stop)
# Set last execution time for new_build events.
utc_now = datetime.datetime.now(time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'new_build', utc_now - datetime.timedelta(hours=1))
self._mock_utc_now.return_value = utc_now
def testNewBuildWithExistingBoardFamilies(self):
"""Test the new_build suite with an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
self.fake_config.set(link_suite, 'board_families', 'ivybridge')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertIn('link-release', tasks[0].payload)
def testNewBuildWithBoardCtpv2(self):
"""Test the new_build suite with an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'run_via_ctpv2', 'true')
self.fake_config.set(link_suite, 'boards', 'coral, atlas-kernelnext')
self.fake_config.set(link_suite, 'suite', 'fake-nearby-share')
self.fake_config.set(link_suite, 'include_tags', 'group:cross-device, cross-device_nearbyshare')
self.fake_config.set(link_suite, 'exclude_tags', 'cross-device_floss')
self.fake_config.set(link_suite, 'koffee_filters', json.dumps([
{
'container': {
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_filter'
},
'dependentContainers': [
{
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_dependent_filter'
},
{
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_dependent_filter_with_digest_and_tags',
'digest': 'sha256:fakedigest',
'tags': [
'fake_tag_1',
'fake_tag_2'
]
}
]
}
]))
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
for task in tasks:
req = ctpv2_requester.CTPv2Request(task.extract_params())
safe_log.info('%s', json_format.MessageToJson(req.request))
def testNewBuildWithMultiBoardCtpv2(self):
"""Test the new_build suite with an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'run_via_ctpv2', 'true')
self.fake_config.set(link_suite, 'multi_dut_boards', 'coral;reef')
self.fake_config.set(link_suite, 'suite', 'fake-nearby-share')
self.fake_config.set(link_suite, 'include_tags', 'group:cross-device, cross-device_nearbyshare')
self.fake_config.set(link_suite, 'exclude_tags', 'cross-device_floss')
self.fake_config.set(link_suite, 'koffee_filters', json.dumps([
{
'container': {
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_filter'
},
'dependentContainers': [
{
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_dependent_filter'
},
{
'hostname': 'us-docker.pkg.dev',
'project': 'cros-registry/test-service',
'name': 'fake_dependent_filter_with_digest_and_tags',
'digest': 'sha256:fakedigest',
'tags': [
'fake_tag_1',
'fake_tag_2'
]
}
]
}
]))
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
for task in tasks:
req = ctpv2_requester.CTPv2Request(task.extract_params())
safe_log.info('%s', json_format.MessageToJson(req.request))
def testNewBuildWithExistingBoardFamiliesAndBoards(self):
"""Test the new_build suite with an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
self.fake_config.set(link_suite, 'board_families', 'ivybridge')
self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
self._mock_config_reader.return_value = self.fake_config
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
boards = suite_trigger.events['new_build'].task_list[0].boards
self.assertIn('asuka', boards)
self.assertIn('link', boards)
def testNewBuildWithNonExistingBoardFamilies(self):
"""Test the new_build suite with an non-existing board family."""
nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
self.fake_config.add_section(nyan_suite)
self.fake_config.set(nyan_suite, 'run_on', 'new_build')
self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
self.fake_config.set(nyan_suite, 'board_families', 'nyan')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 0)
def testNewBuildWithNonSpecifiedBoardFamily(self):
"""Test the new_build suite with an non-specified board family."""
normal_suite = 'FakeBoardFamiliesNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'boards', 'link, zako')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 2)
def testNewBuildExcludingExistingBoardFamilies(self):
"""Test the new_build suite excluding an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
self.fake_config.set(link_suite, 'boards', 'link, zako')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertNotIn('link-release', tasks[0].payload)
self.assertIn('zako-release', tasks[0].payload)
def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
"""Test the new_build suite with an existing board family."""
link_suite = 'FakeLinkNewBuildTask'
self.fake_config.add_section(link_suite)
self.fake_config.set(link_suite, 'run_on', 'new_build')
self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
self.fake_config.set(link_suite, 'exclude_boards',
'asuka, paine, banon, coral, reef, nami, samus-kernelnext')
self._mock_config_reader.return_value = self.fake_config
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[link_suite])
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 4)
tasks = sorted(tasks, key = lambda task: task.payload)
self.assertNotIn('link-release', tasks[1].payload)
self.assertNotIn('asuka-release', tasks[1].payload)
self.assertIn('zako-release', tasks[3].payload)
def testNewBuildExcludingNonExistingBoardFamilies(self):
"""Test the new_build suite excluding an non-existing board family."""
nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
self.fake_config.add_section(nyan_suite)
self.fake_config.set(nyan_suite, 'run_on', 'new_build')
self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
self.fake_config.set(nyan_suite, 'boards', 'link, zako')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 2)
def testNewBuildWithBoardExcludeBoardCollision(self):
"""Test the case that the same board in boards and exclude_boards."""
normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertNotIn('asuka-release', tasks[0].payload)
self.assertIn('zako-release', tasks[0].payload)
def testNewBuildWithKernelnext(self):
"""Test the case that suites run with board-kernelnext build."""
normal_suite = 'FakeKernelnextSuite'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake-suite')
self.fake_config.set(normal_suite, 'pool', 'suites')
self.fake_config.set(normal_suite, 'boards', 'samus-kernelnext')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
# Board should be 'samus'.
self.assertIn('&board=samus&', tasks[0].payload)
# Build should be 'samus-kernelnext-release***'.
self.assertIn('cros_version=samus-kernelnext-release', tasks[0].payload)
def testNewBuildWithModels(self):
"""Test the new_build suite with an existing models entry."""
normal_suite = 'FakeModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'models', 'coral_lava, coral_santa')
self.fake_config.set(normal_suite, 'boards', 'coral')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertNotIn('model=astronaut', tasks[0].payload)
self.assertIn('model=santa', tasks[0].payload)
def testNewBuildWithModelsForOnlySuccessfulBuildRequired(self):
"""Test the new_build suite with an existing models entry."""
normal_suite = 'FakeModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'models', 'nami_akali, nami_bard')
self.fake_config.set(normal_suite, 'boards', 'nami')
self.fake_config.set(normal_suite, 'only_successful_build_required', 'True')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 2)
tasks = sorted(tasks, key = lambda task: task.payload)
self.assertIn('model=bard', tasks[0].payload)
self.assertIn('9867.0.0', tasks[0].payload)
self.assertIn('model=akali', tasks[1].payload)
self.assertIn('9868.0.0', tasks[1].payload)
def testNewBuildWithExcludeModels(self):
"""Test the new_build suite with an existing exclude_models entry."""
normal_suite = 'FakeExludingModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'exclude_models',
'coral_lava, coral_santa')
self.fake_config.set(normal_suite, 'boards', 'coral')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertNotIn('model=santa', tasks[0].payload)
self.assertIn('model=astronaut', tasks[0].payload)
def testNewBuildWithModelsExcludeModels(self):
"""Test the new_build suite with models and exclude_models entry."""
normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'models', 'coral_santa, coral_astronaut')
self.fake_config.set(normal_suite, 'exclude_models',
'coral_lava, coral_santa')
self.fake_config.set(normal_suite, 'boards', 'coral')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertEqual(len(suite_trigger.event_results), 1)
self.assertEqual(suite_trigger.event_results['new_build'],
[normal_suite])
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertNotIn('model=santa', tasks[0].payload)
self.assertIn('model=astronaut', tasks[0].payload)
def testNewBuildWithNoModelListAndModelBuild(self):
"""Test the new_build suite with models and empty board_model mapping."""
normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'boards', 'coral')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
# Verify that coral-release is not kicked off on model lava as lava is not
# listed in cros_model_map.
self.assertEqual(len(tasks), 2)
self.assertNotIn('lava', tasks[0].payload)
self.assertNotIn('lava', tasks[1].payload)
def testNewBuildWithNoModelListAndNoModelBuild(self):
"""Test the new_build suite with models and empty board_model mapping."""
normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'boards', 'reef')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertIn('new_build', suite_trigger.event_results)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
# Verify that reef-release is kicked off on reef models.
self.assertEqual(len(tasks), 1)
self.assertIn('model=electro', tasks[0].payload)
def testNewBuildWithAnyModel(self):
"""Test the new_build suite with any_model option set."""
normal_suite = 'FakeAnyModelNewBuildTask'
self.fake_config.add_section(normal_suite)
self.fake_config.set(normal_suite, 'run_on', 'new_build')
self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
self.fake_config.set(normal_suite, 'any_model', 'True')
self.fake_config.set(normal_suite, 'boards', 'coral')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertIn('model=None', tasks[0].payload)
def testNewBuildWithFirmwareTask(self):
"""Test the new_build suite with firmware option set."""
# Construct a fake config with firmware build.
fsnbt_name = 'FakeStrictNewBuildTask'
self.fake_config.add_section(fsnbt_name)
self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
self.fake_config.set(fsnbt_name, 'boards', 'zako')
self.fake_config.set(fsnbt_name, 'firmware_ro_build_spec', 'firmware')
self.fake_config.set(fsnbt_name, 'test_source', 'cros')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
some_event = suite_trigger.events['new_build']
suite_trigger.cron()
# The firmware query has run for once.
self.assertEqual(suite_trigger._build_client.firmware_builds_called, 1)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertIn('firmware-zako-12345.67', tasks[0].payload)
def testNewBuildWithAltFirmwareTask(self):
"""Test the new_build suite with firmware option set."""
# Construct a fake config with firmware build.
fsnbt_name = 'FakeStrictNewBuildTask'
self.fake_config.add_section(fsnbt_name)
self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
self.fake_config.set(fsnbt_name, 'boards', 'zako')
self.fake_config.set(fsnbt_name, 'firmware_ro_build_spec', 'firmware')
self.fake_config.set(fsnbt_name, 'firmware_board_name', 'alt_board')
self.fake_config.set(fsnbt_name, 'test_source', 'cros')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
some_event = suite_trigger.events['new_build']
suite_trigger.cron()
# The firmware query has run for once.
self.assertEqual(suite_trigger._build_client.firmware_builds_called, 1)
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
self.assertIn('firmware-alt_board-12345.67', tasks[0].payload)
def testNewBuildWithoutFirmwareTask(self):
"""Test the event skips fetch the firmware builds."""
# Construct a fake config without firmware build.
fsnbt_name = 'FakeStrictNewBuildTask'
self.fake_config.add_section(fsnbt_name)
self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
self.fake_config.set(fsnbt_name, 'boards', 'zako')
self._mock_config_reader.return_value = self.fake_config
queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
# The firmware query was not called.
self.assertEqual(suite_trigger._build_client.firmware_builds_called, 0)
# Validate that the expected tests got kicked off.
tasks = queue.lease_tasks(3600, 10, deadline=0.5)
self.assertEqual(len(tasks), 1)
class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
def setUp(self):
super(TriggerReceiverRealConfigTestCase, self).setUp()
mock_config_reader = mock.patch('config_reader.ConfigReader')
self._mock_config_reader = mock_config_reader.start()
self.addCleanup(mock_config_reader.stop)
self._mock_config_reader.return_value = _SUITE_CONFIG_READER
def _get_ground_truth_task_list_from_config(self):
"""Get the ground truth of to-be-scheduled task list from config file."""
self._mock_utc_now.return_value = datetime.datetime.now(
time_converter.UTC_TZ)
task_config = task_config_reader.TaskConfig(_SUITE_CONFIG_READER, self._DEFAULT_BUILDER)
tasks = {}
for keyword, klass in task_config_reader.EVENT_CLASSES.iteritems():
new_event = klass(
task_config.get_event_setting(klass.section_name()), None)
new_event.set_task_list(
task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
tasks[keyword] = new_event.task_list
return tasks
def testEventShouldScanNewBuildFromSinceDateToTargetDate(self):
"""Test the event should scan new builds from last_exec to target_exec."""
_utc_now = datetime.datetime.now(time_converter.UTC_TZ)
# utc_now() should be called only 4 times for the creation of Weekly, Fortnightly
# Nightly or New_Build event.
self._mock_utc_now.side_effect = [_utc_now, _utc_now, _utc_now,_utc_now]
target_exec_utc = _get_current_min(_utc_now)
since_date = target_exec_utc - datetime.timedelta(hours=1)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time('new_build', since_date)
delay_minutes = datetime.timedelta(
minutes=constants.BaseEvent.DELAY_MINUTES)
with mock.patch(
'build_lib.get_cros_builds',
return_value=({}, {})) as mock_get_cros_builds:
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
# The new CrOS builds we fetched should be created within
# the specified time span.
mock_get_cros_builds.assert_any_call(
mock.ANY, mock.ANY, since_date - delay_minutes,
target_exec_utc - delay_minutes, 'new_build')
def testCronWithoutLastExec(self):
"""Test the first round of cron can be successfully executed."""
self._mock_utc_now.return_value = datetime.datetime.now(
time_converter.UTC_TZ)
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
suite_trigger.cron()
self.assertFalse(suite_trigger.events['nightly'].should_handle)
self.assertFalse(suite_trigger.events['weekly'].should_handle)
self.assertFalse(suite_trigger.events['new_build'].should_handle)
self.assertFalse(suite_trigger.events['fortnightly'].should_handle)
self.assertEqual(suite_trigger.event_results, {})
def testCronTriggerNightly(self):
"""Test nightly event is read with available nightly last_exec_time."""
utc_now = datetime.datetime.now(time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'nightly', utc_now - datetime.timedelta(hours=1))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
self.assertTrue(suite_trigger.events['nightly'].should_handle)
self.assertFalse(suite_trigger.events['weekly'].should_handle)
self.assertFalse(suite_trigger.events['new_build'].should_handle)
self.assertFalse(suite_trigger.events['fortnightly'].should_handle)
def testCronTriggerNightlyOutdated(self):
"""Test nightly event is read with available nightly last_exec_time."""
utc_now = datetime.datetime.now(time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'nightly', utc_now - datetime.timedelta(days=3))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
self.assertFalse(suite_trigger.events['nightly'].should_handle)
def testCronTriggerWeeklyOutdated(self):
"""Test weekly event is read with available weekly last_exec_time."""
utc_now = datetime.datetime.now(time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'weekly', utc_now - datetime.timedelta(days=8))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
self.assertFalse(suite_trigger.events['weekly'].should_handle)
def testCronTriggerFortnightlyOutdated(self):
"""Test fortnightly event is read with available fortnightly last_exec_time."""
utc_now = datetime.datetime.now(time_converter.UTC_TZ)
last_exec_client = datastore_client.LastExecutionRecordStore()
last_exec_client.set_last_execute_time(
'fortnightly', utc_now - datetime.timedelta(days=15))
self._mock_utc_now.return_value = utc_now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
self.assertFalse(suite_trigger.events['fortnightly'].should_handle)
def testCronForADay(self):
"""Ensure cron job can be successfully scheduled for a day."""
all_tasks = self._get_ground_truth_task_list_from_config()
last_now = None
for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ),
last_days=1):
self._mock_utc_now.return_value = now
suite_trigger = trigger_receiver.TriggerReceiver(fake=True)
with mock.patch('task.Task.schedule', return_value=True):
suite_trigger.cron()
should_scheduled_nightly_tasks = [
t.name for t in all_tasks['nightly'] if t.hour == now.hour]
if (_should_schedule_timed_task(last_now, now) and
should_scheduled_nightly_tasks):
self.assertEqual(suite_trigger.event_results['nightly'],
should_scheduled_nightly_tasks)
else:
self.assertNotIn('nightly', suite_trigger.event_results.keys())
# Verify fortnightly tasks
should_scheduled_fortnightly_tasks = [
t.name for t in all_tasks['fortnightly']
if now.weekday() == t.day and now.hour == t.hour]
if (_should_schedule_timed_task(last_now, now) and
should_scheduled_fortnightly_tasks):
self.assertEqual(suite_trigger.event_results['fortnightly'],
should_scheduled_fortnightly_tasks)
else:
self.assertNotIn('fortnightly', suite_trigger.event_results.keys())
# Verify weekly tasks
should_scheduled_weekly_tasks = [
t.name for t in all_tasks['weekly']
if now.weekday() == t.day and now.hour == t.hour]
if (_should_schedule_timed_task(last_now, now) and
should_scheduled_weekly_tasks):
self.assertEqual(suite_trigger.event_results['weekly'],
should_scheduled_weekly_tasks)
else:
self.assertNotIn('weekly', suite_trigger.event_results.keys())
# Verify new_build tasks
should_scheduled_new_build_tasks = [
t.name for t in all_tasks['new_build']]
if (_should_schedule_new_build_task(last_now, now) and
should_scheduled_new_build_tasks):
self.assertEqual(suite_trigger.event_results['new_build'],
should_scheduled_new_build_tasks)
else:
self.assertNotIn('new_build', suite_trigger.event_results.keys())
last_now = now
if __name__ == '__main__':
unittest.main()