blob: 7bf69ec9fb86d73109287117f0106ddb26bf337c [file] [log] [blame]
Xixuan Wu40998892017-08-29 14:32:26 -07001# Copyright 2017 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 trigger_receiver unittests."""
Xixuan Wu09962902018-12-11 10:49:27 -08006# pylint: disable=g-missing-super-call
Xixuan Wu40998892017-08-29 14:32:26 -07007
8import datetime
9import os
10import unittest
11
12import cloud_sql_client
13import config_reader
14import datastore_client
15import file_getter
16import mock
Xixuan Wu51bb7102019-03-18 14:51:44 -070017import task_config_reader
Craig Bergstrom58263d32018-04-26 14:11:35 -060018import task_executor
Xixuan Wu40998892017-08-29 14:32:26 -070019import time_converter
20import trigger_receiver
21
Craig Bergstrom58263d32018-04-26 14:11:35 -060022from google.appengine.api import taskqueue
Xixuan Wu40998892017-08-29 14:32:26 -070023from google.appengine.ext import ndb
24from google.appengine.ext import testbed
25
26# Ensure that SUITE_SCHEDULER_CONFIG_FILE is read only once.
27_SUITE_CONFIG_READER = config_reader.ConfigReader(
Xixuan Wu26d06e02017-09-20 14:50:28 -070028 file_getter.TEST_SUITE_SCHEDULER_CONFIG_FILE)
Xixuan Wu40998892017-08-29 14:32:26 -070029
30
31def now_generator(start_time, interval_min=30, last_days=7):
32 """A datetime.datetime.now generator.
33
34 The generator will generate 'now' from start_time till start_time+last_days
35 for every interval_min.
36
37 Args:
38 start_time: A datetime.datetime object representing the initial value of
39 the 'now'.
40 interval_min: The interval minutes between current 'now' and next 'now.
41 last_days: Representing how many days this generator will last.
42
43 Yields:
44 a datetime.datetime object to mock the current time.
45 """
46 cur_time = start_time
47 end_time = start_time + datetime.timedelta(days=last_days)
48 while cur_time < end_time:
49 yield cur_time
50 cur_time += datetime.timedelta(minutes=interval_min)
51
52
Xixuan Wu09962902018-12-11 10:49:27 -080053def _should_schedule_timed_task(last_now, now):
54 """Check whether timed (weekly/nightly) task should be scheduled.
Xixuan Wu40998892017-08-29 14:32:26 -070055
Xixuan Wu09962902018-12-11 10:49:27 -080056 A timed task should be schduled when next hour is coming.
Xixuan Wu40998892017-08-29 14:32:26 -070057
58 Args:
Xixuan Wu09962902018-12-11 10:49:27 -080059 last_now: the last time to check if timed task should be scheduled
Xixuan Wu40998892017-08-29 14:32:26 -070060 or not.
Xixuan Wu09962902018-12-11 10:49:27 -080061 now: the current time to check if timed task should be scheduled.
Xixuan Wu40998892017-08-29 14:32:26 -070062
63 Returns:
64 a boolean indicating whether there will be nightly tasks scheduled.
65 """
66 if last_now is not None and last_now.hour != now.hour:
67 return True
68
69 return False
70
71
Xixuan Wu40998892017-08-29 14:32:26 -070072def _should_schedule_new_build_task(last_now, now):
73 """Check whether weekly task should be scheduled.
74
75 A new_build task should be schduled when there're new builds between last
76 check and this check.
77
78 Args:
79 last_now: the last time to check if new_build task should be scheduled.
80 now: the current time to check if new_build task should be scheduled.
81
82 Returns:
83 a boolean indicating whether there will be new_build tasks scheduled.
84 """
85 if last_now is not None and last_now != now:
86 return True
87
88 return False
89
90
91class FakeCIDBClient(object):
92 """Mock cloud_sql_client.CIDBClient."""
93
94 def get_passed_builds_since_date(self, since_date):
95 """Mock cloud_sql_client.CIDBClient.get_passed_builds_since_date."""
96 del since_date # unused
Xixuan Wua41efa22019-05-17 14:28:04 -070097 return [
98 cloud_sql_client.BuildInfo('link', None, '62', '9868.0.0',
99 'link-release'),
100 cloud_sql_client.BuildInfo('zako', None, '62', '9868.0.0',
101 'zako-release'),
102 cloud_sql_client.BuildInfo('samus-kernelnext', None, '62',
103 '9868.0.0', 'samus-kernelnext-release'),
104 cloud_sql_client.BuildInfo('reef', None, '62', '1234.0.0',
105 'reef-release'),
106 cloud_sql_client.BuildInfo('coral', 'santa', '62', '9868.0.0',
107 'coral-release'),
108 cloud_sql_client.BuildInfo('coral', 'astronaut', '62', '9868.0.0',
109 'coral-release'),
110 cloud_sql_client.BuildInfo('coral', 'lava', '62', '9868.0.0',
111 'coral-release'),
112 ]
Xixuan Wu40998892017-08-29 14:32:26 -0700113
114 def get_latest_passed_builds(self, build_config):
115 """Mock cloud_sql_client.CIDBClient.get_latest_passed_builds."""
116 del build_config # unused
117 return cloud_sql_client.BuildInfo('link', '62', '9868.0.0', build_config)
118
Craig Bergstrom58263d32018-04-26 14:11:35 -0600119 def get_relaxed_pased_builds_since_date(self, since_date):
120 """Mock cloud_sql_client.CIDBClient.get_relaxed_pased_builds_since_date."""
121 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700122 return [cloud_sql_client.BuildInfo('grunt', None, '63', '9968.0.0',
Craig Bergstrom58263d32018-04-26 14:11:35 -0600123 'grunt-release')]
124
Xixuan Wu40998892017-08-29 14:32:26 -0700125
126class FakeAndroidBuildRestClient(object):
127 """Mock rest_client.AndroidBuildRestClient."""
128
129 def get_latest_build_id(self, branch, target):
130 """Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
131 del branch, target # unused
132 return '100'
133
134
135class FakeLabConfig(object):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700136 """Mock config_reader.LabConfig."""
Xixuan Wu40998892017-08-29 14:32:26 -0700137
138 def get_android_board_list(self):
139 """Mock config_reader.LabConfig.get_android_board_list."""
140 return ('android-angler', 'android-bullhead')
141
Xixuan Wu6fb16272017-10-19 13:16:00 -0700142 def get_cros_board_list(self):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700143 """Mock config_reader.LabConfig.get_cros_board_list."""
Xixuan Wua41efa22019-05-17 14:28:04 -0700144 return ('grunt', 'link', 'peppy', 'daisy', 'zako', 'samus-kernelnext',
145 'coral', 'reef')
Xixuan Wu6fb16272017-10-19 13:16:00 -0700146
Xixuan Wu40998892017-08-29 14:32:26 -0700147 def get_firmware_ro_build_list(self, release_board):
148 """Mock config_reader.LabConfig.get_firmware_ro_build_list."""
149 del release_board # unused
150 return 'firmware1,firmware2'
151
C Shapiro7f24a002017-12-05 14:25:09 -0700152 def get_cros_model_map(self):
153 """Mock config_reader.LabConfig.get_cros_model_map."""
Xixuan Wua41efa22019-05-17 14:28:04 -0700154 return {
155 'coral': ['santa', 'astronaut'],
156 'reef': ['electro'],
157 }
C Shapiro7f24a002017-12-05 14:25:09 -0700158
Xixuan Wu40998892017-08-29 14:32:26 -0700159
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700160class FakeMigrationConfig(object):
161 """Mock config_reader.MigrationConfig."""
162
Xixuan Wu1e42c752019-03-21 13:41:49 -0700163 def get_skylab_suites_dump(self):
Xixuan Wuee6b6a82019-03-21 14:28:26 -0700164 """Mock config_reader.MigrationConfig.get_skylab_suite_list."""
Xixuan Wu1e42c752019-03-21 13:41:49 -0700165 return {'suite:ent-nightly':
166 {'pool:suites':
Xixuan Wu028f6732019-04-11 14:47:42 -0700167 {'model:santa': {
168 'skylab': True,
169 'override_pool': '',
170 'override_qs_account': '',
171 },
172 'model:zako': {
173 'skylab': True,
174 'override_pool': 'QUOTA_POOL',
175 'override_qs_account': 'QUOTA_ACCOUNT',
176 }}}}
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700177
178
Xixuan Wu008ee832017-10-12 16:59:34 -0700179class TriggerReceiverBaseTestCase(unittest.TestCase):
Xixuan Wu40998892017-08-29 14:32:26 -0700180
181 def setUp(self):
182 self.testbed = testbed.Testbed()
183 self.testbed.activate()
184 self.addCleanup(self.testbed.deactivate)
185
186 self.testbed.init_datastore_v3_stub()
187 self.testbed.init_memcache_stub()
188 ndb.get_context().clear_cache()
189 self.testbed.init_taskqueue_stub(
190 root_path=os.path.join(os.path.dirname(__file__)))
191 self.taskqueue_stub = self.testbed.get_stub(
192 testbed.TASKQUEUE_SERVICE_NAME)
193
194 mock_cidb_client = mock.patch('cloud_sql_client.CIDBClient')
195 self._mock_cidb_client = mock_cidb_client.start()
196 self.addCleanup(mock_cidb_client.stop)
197
198 mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
199 self._mock_android_client = mock_android_client.start()
200 self.addCleanup(mock_android_client.stop)
201
Xixuan Wu40998892017-08-29 14:32:26 -0700202 mock_lab_config = mock.patch('config_reader.LabConfig')
203 self._mock_lab_config = mock_lab_config.start()
204 self.addCleanup(mock_lab_config.stop)
205
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700206 mock_migration_config = mock.patch('config_reader.MigrationConfig')
207 self._mock_migration_config = mock_migration_config.start()
208 self.addCleanup(mock_migration_config.stop)
209
Xixuan Wu40998892017-08-29 14:32:26 -0700210 mock_utc_now = mock.patch('time_converter.utc_now')
211 self._mock_utc_now = mock_utc_now.start()
212 self.addCleanup(mock_utc_now.stop)
213
214 self._mock_cidb_client.return_value = FakeCIDBClient()
215 self._mock_android_client.return_value = FakeAndroidBuildRestClient()
Xixuan Wu40998892017-08-29 14:32:26 -0700216 self._mock_lab_config.return_value = FakeLabConfig()
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700217 self._mock_migration_config.return_value = FakeMigrationConfig()
Xixuan Wu40998892017-08-29 14:32:26 -0700218
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700219 board_family_patcher = mock.patch(
220 'build_lib.get_board_family_mapping_from_gs')
221 board_family_getter = board_family_patcher.start()
222 board_family_getter.return_value = {
223 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
224 'ivybridge': ['link', 'link_freon']}
225 self.addCleanup(board_family_patcher.stop)
226
Xixuan Wu008ee832017-10-12 16:59:34 -0700227
228class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
229
230 _TEST_PST_HOUR = 20
231 _TEST_PST_DAY = 4 # Friday
232 _TEST_EVENT_PST_HOUR = 13
233
234 _FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
235 _FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
236
237 def setUp(self):
238 super(TriggerReceiverFakeConfigTestCase, self).setUp()
239
240 self.fake_config = config_reader.ConfigReader(None)
241 self._add_nightly_tasks(self.fake_config)
242 self._add_weekly_tasks(self.fake_config)
243
244 self.fake_config_with_settings = config_reader.ConfigReader(None)
245 self._add_weekly_tasks(self.fake_config_with_settings)
246 self._add_weekly_params(self.fake_config_with_settings)
247
248 mock_config_reader = mock.patch('config_reader.ConfigReader')
249 self._mock_config_reader = mock_config_reader.start()
250 self.addCleanup(mock_config_reader.stop)
251
252 def _add_nightly_tasks(self, fake_config):
253 fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
254 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
255 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
256 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
257 str(self._TEST_PST_HOUR))
258
259 def _add_weekly_tasks(self, fake_config):
260 fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
261 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
262 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
263 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
264
265 def _add_weekly_params(self, fake_config):
Xixuan Wu51bb7102019-03-18 14:51:44 -0700266 weekly_section_name = task_config_reader.EVENT_CLASSES[
267 'weekly'].section_name()
Xixuan Wu008ee832017-10-12 16:59:34 -0700268 fake_config.add_section(weekly_section_name)
269 fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
270
271 def testInitializeTriggerReceiverWithNightlyEvent(self):
272 """Test nightly event can be handled on right hour."""
273 # A task with hour=20 should be scheduled at 20:00 in PST everyday, which
274 # is 3:00/4:00 in UTC everyday.
275 self._mock_config_reader.return_value = self.fake_config
276 given_utc_hour = time_converter.convert_time_info(
277 time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
278 utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
279 tzinfo=time_converter.UTC_TZ)
280 last_exec_client = datastore_client.LastExecutionRecordStore()
281 last_exec_client.set_last_execute_time(
282 'nightly', utc_now - datetime.timedelta(hours=1))
283 self._mock_utc_now.return_value = utc_now
284
285 suite_trigger = trigger_receiver.TriggerReceiver()
286 suite_trigger.cron()
287 self.assertTrue(suite_trigger.events['nightly'].should_handle)
288 self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
289 self.assertEqual(suite_trigger.event_results['nightly'][0],
290 self._FAKE_NIGHTLY_TASK_NAME)
291
292 def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
293 """Test weekly event without event settings can be handled on right day."""
294 # A task with day=4 (Friday) and default event_hour (23) should be
295 # scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
296 self._mock_config_reader.return_value = self.fake_config
297 given_utc_hour = time_converter.convert_time_info(
298 time_converter.TimeInfo(
299 self._TEST_PST_DAY,
Xixuan Wu51bb7102019-03-18 14:51:44 -0700300 task_config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
Xixuan Wu008ee832017-10-12 16:59:34 -0700301 utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
302 tzinfo=time_converter.UTC_TZ)
303 last_exec_client = datastore_client.LastExecutionRecordStore()
304 last_exec_client.set_last_execute_time(
305 'weekly', utc_now - datetime.timedelta(days=1))
306 self._mock_utc_now.return_value = utc_now
307
308 suite_trigger = trigger_receiver.TriggerReceiver()
309 suite_trigger.cron()
310 self.assertTrue(suite_trigger.events['weekly'].should_handle)
311 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
312 self.assertEqual(suite_trigger.event_results['weekly'][0],
313 self._FAKE_WEEKLY_TASK_NAME)
314
315 def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
316 """Test weekly event with event settings can be handled on right day."""
317 # A task with day=4 (Friday) and event_hour=13 should be scheduled at
318 # Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
319 self._mock_config_reader.return_value = self.fake_config_with_settings
320 given_utc_time_info = time_converter.convert_time_info(
321 time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
322
323 # Set the current time as a Friday 20:00 or 21:00 in UTC.
324 utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
325 tzinfo=time_converter.UTC_TZ)
326 last_exec_client = datastore_client.LastExecutionRecordStore()
327 last_exec_client.set_last_execute_time(
328 'weekly', utc_now - datetime.timedelta(days=1))
329 self._mock_utc_now.return_value = utc_now
330
331 suite_trigger = trigger_receiver.TriggerReceiver()
332 suite_trigger.cron()
333 self.assertTrue(suite_trigger.events['weekly'].should_handle)
334 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
335 self.assertEqual(suite_trigger.event_results['weekly'][0],
336 self._FAKE_WEEKLY_TASK_NAME)
337
338
Craig Bergstrom58263d32018-04-26 14:11:35 -0600339class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
340 """Test the new_build functionality."""
341
Craig Bergstrom58263d32018-04-26 14:11:35 -0600342 def setUp(self):
343 """Set up for a test."""
344 super(TriggerReceiverFakeBuildTestCase, self).setUp()
345
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700346 self._prepare()
347
348 def _prepare(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -0600349 self.fake_config = config_reader.ConfigReader(None)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600350 mock_config_reader = mock.patch('config_reader.ConfigReader')
351 self._mock_config_reader = mock_config_reader.start()
352 self.addCleanup(mock_config_reader.stop)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600353
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700354 # Set last execution time for new_build events.
355 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600356 last_exec_client = datastore_client.LastExecutionRecordStore()
357 last_exec_client.set_last_execute_time(
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700358 'new_build', utc_now - datetime.timedelta(hours=1))
Craig Bergstrom58263d32018-04-26 14:11:35 -0600359 self._mock_utc_now.return_value = utc_now
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700360
361 def testNewBuildForHWTestSanityRequired(self):
362 """Test the new_build functionality."""
363 # Construct a fake config with only_hwtest_sanity_required.
364 fsnbt_name = 'FakeStrictNewBuildTask'
365 self.fake_config.add_section(fsnbt_name)
366 self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
367 self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700368 self.fake_config.set(fsnbt_name, 'boards', 'link, zako, grunt')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700369 frnbt_name = 'FakeRelaxedNewBuildTask'
370 self.fake_config.add_section(frnbt_name)
371 self.fake_config.set(frnbt_name, 'run_on', 'new_build')
372 self.fake_config.set(frnbt_name, 'suite', 'fake_suite_relaxed')
373 self.fake_config.set(frnbt_name, 'only_hwtest_sanity_required', 'True')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700374 self.fake_config.set(frnbt_name, 'boards', 'link, zako, grunt')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700375
376 self._mock_config_reader.return_value = self.fake_config
Craig Bergstrom58263d32018-04-26 14:11:35 -0600377
378 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600379 suite_trigger = trigger_receiver.TriggerReceiver()
380 suite_trigger.cron()
381
382 # Validate that the expected tests got kicked off.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600383 self.assertEqual(len(suite_trigger.event_results), 1)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600384 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000385 # 2 for strict passed builds, 3 for relaxed builds.
386 self.assertEqual(len(tasks), 5)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600387
388 # The number of builds that matched the base (success) Task.
389 count_base = 0
390 # The number of builds that matched the relaxed Task.
391 count_relaxed = 0
392 for task in tasks:
393 if 'fake_suite_base' in task.payload:
394 # Make sure it matched the expected build only.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600395 self.assertNotIn('grunt-release', task.payload)
396 count_base += 1
397
398 if 'fake_suite_relaxed' in task.payload:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600399 count_relaxed += 1
400
401 # Make each case matched precisely one event.
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000402 self.assertEqual(2, count_base)
403 self.assertEqual(3, count_relaxed)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600404
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700405 def testNewBuildWithExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700406 """Test the new_build suite with an existing board family."""
407 link_suite = 'FakeLinkNewBuildTask'
408 self.fake_config.add_section(link_suite)
409 self.fake_config.set(link_suite, 'run_on', 'new_build')
410 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700411 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700412 self._mock_config_reader.return_value = self.fake_config
413
414 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
415 suite_trigger = trigger_receiver.TriggerReceiver()
416 suite_trigger.cron()
417
418 self.assertEqual(len(suite_trigger.event_results), 1)
419 self.assertEqual(suite_trigger.event_results['new_build'],
420 [link_suite])
421 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
422 self.assertEqual(len(tasks), 1)
423 self.assertIn('link-release', tasks[0].payload)
424
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700425 def testNewBuildWithExistingBoardFamiliesAndBoards(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700426 """Test the new_build suite with an existing board family."""
427 link_suite = 'FakeLinkNewBuildTask'
428 self.fake_config.add_section(link_suite)
429 self.fake_config.set(link_suite, 'run_on', 'new_build')
430 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700431 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700432 self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
433 self._mock_config_reader.return_value = self.fake_config
434
435 suite_trigger = trigger_receiver.TriggerReceiver()
436 suite_trigger.cron()
437
438 self.assertEqual(len(suite_trigger.event_results), 1)
439 self.assertEqual(suite_trigger.event_results['new_build'],
440 [link_suite])
441 boards = suite_trigger.events['new_build'].task_list[0].boards
442 self.assertIn('asuka', boards)
443 self.assertIn('link', boards)
444
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700445 def testNewBuildWithNonExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700446 """Test the new_build suite with an non-existing board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700447 nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700448 self.fake_config.add_section(nyan_suite)
449 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
450 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700451 self.fake_config.set(nyan_suite, 'board_families', 'nyan')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700452 self._mock_config_reader.return_value = self.fake_config
453
454 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
455 suite_trigger = trigger_receiver.TriggerReceiver()
456 suite_trigger.cron()
457
458 self.assertEqual(len(suite_trigger.event_results), 1)
459 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
460 self.assertEqual(len(tasks), 0)
461
462 def testNewBuildWithNonSpecifiedBoardFamily(self):
463 """Test the new_build suite with an non-specified board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700464 normal_suite = 'FakeBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700465 self.fake_config.add_section(normal_suite)
466 self.fake_config.set(normal_suite, 'run_on', 'new_build')
467 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700468 self.fake_config.set(normal_suite, 'boards', 'link, zako')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700469 self._mock_config_reader.return_value = self.fake_config
470
471 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
472 suite_trigger = trigger_receiver.TriggerReceiver()
473 suite_trigger.cron()
474
475 self.assertEqual(len(suite_trigger.event_results), 1)
476 self.assertEqual(suite_trigger.event_results['new_build'],
477 [normal_suite])
478 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000479 self.assertEqual(len(tasks), 2)
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700480
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700481 def testNewBuildExcludingExistingBoardFamilies(self):
482 """Test the new_build suite excluding an existing board family."""
483 link_suite = 'FakeLinkNewBuildTask'
484 self.fake_config.add_section(link_suite)
485 self.fake_config.set(link_suite, 'run_on', 'new_build')
486 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
487 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700488 self.fake_config.set(link_suite, 'boards', 'link, zako')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700489 self._mock_config_reader.return_value = self.fake_config
490
491 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
492 suite_trigger = trigger_receiver.TriggerReceiver()
493 suite_trigger.cron()
494
495 self.assertEqual(len(suite_trigger.event_results), 1)
496 self.assertEqual(suite_trigger.event_results['new_build'],
497 [link_suite])
498 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
499 self.assertEqual(len(tasks), 1)
500 self.assertNotIn('link-release', tasks[0].payload)
501 self.assertIn('zako-release', tasks[0].payload)
502
503 def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
504 """Test the new_build suite with an existing board family."""
505 link_suite = 'FakeLinkNewBuildTask'
506 self.fake_config.add_section(link_suite)
507 self.fake_config.set(link_suite, 'run_on', 'new_build')
508 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
509 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Xixuan Wua41efa22019-05-17 14:28:04 -0700510 self.fake_config.set(link_suite, 'exclude_boards',
511 'asuka, paine, banon, coral, reef')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700512 self._mock_config_reader.return_value = self.fake_config
513
514 suite_trigger = trigger_receiver.TriggerReceiver()
515 suite_trigger.cron()
516
517 self.assertEqual(len(suite_trigger.event_results), 1)
518 self.assertEqual(suite_trigger.event_results['new_build'],
519 [link_suite])
520 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
521 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
522 self.assertNotIn('link-release', tasks[0].payload)
523 self.assertNotIn('asuka-release', tasks[0].payload)
524 self.assertIn('zako-release', tasks[0].payload)
525
526 def testNewBuildExcludingNonExistingBoardFamilies(self):
527 """Test the new_build suite excluding an non-existing board family."""
528 nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
529 self.fake_config.add_section(nyan_suite)
530 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
531 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
532 self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700533 self.fake_config.set(nyan_suite, 'boards', 'link, zako')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700534 self._mock_config_reader.return_value = self.fake_config
535
536 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
537 suite_trigger = trigger_receiver.TriggerReceiver()
538 suite_trigger.cron()
539
540 self.assertEqual(len(suite_trigger.event_results), 1)
541 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
542 self.assertEqual(len(tasks), 2)
543
544 def testNewBuildWithBoardExcludeBoardCollision(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700545 """Test the case that the same board in boards and exclude_boards."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700546 normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
547 self.fake_config.add_section(normal_suite)
548 self.fake_config.set(normal_suite, 'run_on', 'new_build')
549 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
550 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
551 self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
552 self._mock_config_reader.return_value = self.fake_config
553
554 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
555 suite_trigger = trigger_receiver.TriggerReceiver()
556 suite_trigger.cron()
557
558 self.assertEqual(len(suite_trigger.event_results), 1)
559 self.assertEqual(suite_trigger.event_results['new_build'],
560 [normal_suite])
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700561 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
562 self.assertEqual(len(tasks), 1)
563 self.assertNotIn('asuka-release', tasks[0].payload)
564 self.assertIn('zako-release', tasks[0].payload)
565
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700566 def testNewBuildWithSkylab(self):
567 """Test the case that suites run in skylab."""
568 normal_suite = 'FakeSkylabSuite'
569 self.fake_config.add_section(normal_suite)
570 self.fake_config.set(normal_suite, 'run_on', 'new_build')
571 self.fake_config.set(normal_suite, 'suite', 'ent-nightly')
Xixuan Wu1e42c752019-03-21 13:41:49 -0700572 self.fake_config.set(normal_suite, 'pool', 'suites')
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700573 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
574 self._mock_config_reader.return_value = self.fake_config
575
576 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
577 suite_trigger = trigger_receiver.TriggerReceiver()
578 suite_trigger.cron()
579
580 self.assertEqual(len(suite_trigger.event_results), 1)
581 self.assertEqual(suite_trigger.event_results['new_build'],
582 [normal_suite])
583 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
584 self.assertEqual(len(tasks), 1)
585 self.assertIn('is_skylab=True', tasks[0].payload)
Xixuan Wu028f6732019-04-11 14:47:42 -0700586 self.assertIn('override_pool=QUOTA_POOL', tasks[0].payload)
587 self.assertIn('override_qs_account=QUOTA_ACCOUNT', tasks[0].payload)
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700588
Xixuan Wub4b2f412019-05-03 11:22:31 -0700589 def testNewBuildWithKernelnext(self):
590 """Test the case that suites run with board-kernelnext build."""
591 normal_suite = 'FakeKernelnextSuite'
592 self.fake_config.add_section(normal_suite)
593 self.fake_config.set(normal_suite, 'run_on', 'new_build')
594 self.fake_config.set(normal_suite, 'suite', 'fake-suite')
595 self.fake_config.set(normal_suite, 'pool', 'suites')
596 self.fake_config.set(normal_suite, 'boards', 'samus-kernelnext')
597 self._mock_config_reader.return_value = self.fake_config
598
599 self._mock_cidb_client.return_value = FakeCIDBClient()
600
601 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
602 suite_trigger = trigger_receiver.TriggerReceiver()
603 suite_trigger.cron()
604
605 self.assertEqual(len(suite_trigger.event_results), 1)
606 self.assertEqual(suite_trigger.event_results['new_build'],
607 [normal_suite])
608 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
609 self.assertEqual(len(tasks), 1)
610 # Board should be 'samus'.
611 self.assertIn('&board=samus&', tasks[0].payload)
612 # Build should be 'samus-kernelnext-release***'.
613 self.assertIn('cros_version=samus-kernelnext-release', tasks[0].payload)
614
Xixuan Wua41efa22019-05-17 14:28:04 -0700615 def testNewBuildWithModels(self):
616 """Test the new_build suite with an existing models entry."""
617 normal_suite = 'FakeModelsNewBuildTask'
618 self.fake_config.add_section(normal_suite)
619 self.fake_config.set(normal_suite, 'run_on', 'new_build')
620 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
621 self.fake_config.set(normal_suite, 'models', 'coral_lava, coral_santa')
622 self.fake_config.set(normal_suite, 'boards', 'coral')
623 self._mock_config_reader.return_value = self.fake_config
624
625 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
626 suite_trigger = trigger_receiver.TriggerReceiver()
627 suite_trigger.cron()
628
629 self.assertEqual(len(suite_trigger.event_results), 1)
630 self.assertEqual(suite_trigger.event_results['new_build'],
631 [normal_suite])
632 self.assertIn('new_build', suite_trigger.event_results)
633 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
634 self.assertEqual(len(tasks), 1)
635 self.assertNotIn('model=astronaut', tasks[0].payload)
636 self.assertIn('model=santa', tasks[0].payload)
637
638 def testNewBuildWithExcludeModels(self):
639 """Test the new_build suite with an existing exclude_models entry."""
640 normal_suite = 'FakeExludingModelsNewBuildTask'
641 self.fake_config.add_section(normal_suite)
642 self.fake_config.set(normal_suite, 'run_on', 'new_build')
643 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
644 self.fake_config.set(normal_suite, 'exclude_models',
645 'coral_lava, coral_santa')
646 self.fake_config.set(normal_suite, 'boards', 'coral')
647 self._mock_config_reader.return_value = self.fake_config
648
649 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
650 suite_trigger = trigger_receiver.TriggerReceiver()
651 suite_trigger.cron()
652
653 self.assertEqual(len(suite_trigger.event_results), 1)
654 self.assertEqual(suite_trigger.event_results['new_build'],
655 [normal_suite])
656 self.assertIn('new_build', suite_trigger.event_results)
657 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
658 self.assertEqual(len(tasks), 1)
659 self.assertNotIn('model=santa', tasks[0].payload)
660 self.assertIn('model=astronaut', tasks[0].payload)
661
662 def testNewBuildWithModelsExcludeModels(self):
663 """Test the new_build suite with models and exclude_models entry."""
664 normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
665 self.fake_config.add_section(normal_suite)
666 self.fake_config.set(normal_suite, 'run_on', 'new_build')
667 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
668 self.fake_config.set(normal_suite, 'models', 'coral_santa, coral_astronaut')
669 self.fake_config.set(normal_suite, 'exclude_models',
670 'coral_lava, coral_santa')
671 self.fake_config.set(normal_suite, 'boards', 'coral')
672 self._mock_config_reader.return_value = self.fake_config
673
674 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
675 suite_trigger = trigger_receiver.TriggerReceiver()
676 suite_trigger.cron()
677
678 self.assertEqual(len(suite_trigger.event_results), 1)
679 self.assertEqual(suite_trigger.event_results['new_build'],
680 [normal_suite])
681 self.assertIn('new_build', suite_trigger.event_results)
682 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
683 self.assertEqual(len(tasks), 1)
684 self.assertNotIn('model=santa', tasks[0].payload)
685 self.assertIn('model=astronaut', tasks[0].payload)
686
687 def testNewBuildWithNoModelListAndModelBuild(self):
688 """Test the new_build suite with models and empty board_model mapping."""
689 normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
690 self.fake_config.add_section(normal_suite)
691 self.fake_config.set(normal_suite, 'run_on', 'new_build')
692 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
693 self.fake_config.set(normal_suite, 'boards', 'coral')
694 self._mock_config_reader.return_value = self.fake_config
695
696 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
697 suite_trigger = trigger_receiver.TriggerReceiver()
698 suite_trigger.cron()
699
700 self.assertIn('new_build', suite_trigger.event_results)
701 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
702 # Verify that coral-release is not kicked off on model lava as lava is not
703 # listed in cros_model_map.
704 self.assertEqual(len(tasks), 2)
705 self.assertNotIn('lava', tasks[0].payload)
706 self.assertNotIn('lava', tasks[1].payload)
707
708 def testNewBuildWithNoModelListAndNoModelBuild(self):
709 """Test the new_build suite with models and empty board_model mapping."""
710 normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
711 self.fake_config.add_section(normal_suite)
712 self.fake_config.set(normal_suite, 'run_on', 'new_build')
713 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
714 self.fake_config.set(normal_suite, 'boards', 'reef')
715 self._mock_config_reader.return_value = self.fake_config
716
717 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
718 suite_trigger = trigger_receiver.TriggerReceiver()
719 suite_trigger.cron()
720
721 self.assertIn('new_build', suite_trigger.event_results)
722 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
723 # Verify that reef-release is kicked off on reef models.
724 self.assertEqual(len(tasks), 1)
725 self.assertIn('model=electro', tasks[0].payload)
726
Craig Bergstrom58263d32018-04-26 14:11:35 -0600727
Xixuan Wu008ee832017-10-12 16:59:34 -0700728class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
729
730 def setUp(self):
731 super(TriggerReceiverRealConfigTestCase, self).setUp()
732 mock_config_reader = mock.patch('config_reader.ConfigReader')
733 self._mock_config_reader = mock_config_reader.start()
734 self.addCleanup(mock_config_reader.stop)
735 self._mock_config_reader.return_value = _SUITE_CONFIG_READER
736
737 def _get_ground_truth_task_list_from_config(self):
738 """Get the ground truth of to-be-scheduled task list from config file."""
739 self._mock_utc_now.return_value = datetime.datetime.now(
740 time_converter.UTC_TZ)
Xixuan Wu51bb7102019-03-18 14:51:44 -0700741 task_config = task_config_reader.TaskConfig(_SUITE_CONFIG_READER)
Xixuan Wu008ee832017-10-12 16:59:34 -0700742 tasks = {}
Xixuan Wu51bb7102019-03-18 14:51:44 -0700743 for keyword, klass in task_config_reader.EVENT_CLASSES.iteritems():
Xixuan Wu008ee832017-10-12 16:59:34 -0700744 new_event = klass(
745 task_config.get_event_setting(klass.section_name()), None)
746 new_event.set_task_list(
747 task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
748 tasks[keyword] = new_event.task_list
749
750 return tasks
751
Xixuan Wu40998892017-08-29 14:32:26 -0700752 def testCronWithoutLastExec(self):
753 """Test the first round of cron can be successfully executed."""
754 self._mock_utc_now.return_value = datetime.datetime.now(
755 time_converter.UTC_TZ)
756 suite_trigger = trigger_receiver.TriggerReceiver()
757 suite_trigger.cron()
758 self.assertFalse(suite_trigger.events['nightly'].should_handle)
759 self.assertFalse(suite_trigger.events['weekly'].should_handle)
760 self.assertFalse(suite_trigger.events['new_build'].should_handle)
761
762 self.assertEqual(suite_trigger.event_results, {})
763
764 def testCronTriggerNightly(self):
765 """Test nightly event is read with available nightly last_exec_time."""
766 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
767 last_exec_client = datastore_client.LastExecutionRecordStore()
768 last_exec_client.set_last_execute_time(
769 'nightly', utc_now - datetime.timedelta(hours=1))
770 self._mock_utc_now.return_value = utc_now
771 suite_trigger = trigger_receiver.TriggerReceiver()
772 self.assertTrue(suite_trigger.events['nightly'].should_handle)
773 self.assertFalse(suite_trigger.events['weekly'].should_handle)
774 self.assertFalse(suite_trigger.events['new_build'].should_handle)
775
Xixuan Wu33179672017-09-12 11:44:04 -0700776 def testCronTriggerNightlyOutdated(self):
777 """Test nightly event is read with available nightly last_exec_time."""
778 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
779 last_exec_client = datastore_client.LastExecutionRecordStore()
780 last_exec_client.set_last_execute_time(
781 'nightly', utc_now - datetime.timedelta(days=3))
782 self._mock_utc_now.return_value = utc_now
783 suite_trigger = trigger_receiver.TriggerReceiver()
784 self.assertFalse(suite_trigger.events['nightly'].should_handle)
785
786 def testCronTriggerWeeklyOutdated(self):
787 """Test weekly event is read with available weekly last_exec_time."""
788 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
789 last_exec_client = datastore_client.LastExecutionRecordStore()
790 last_exec_client.set_last_execute_time(
791 'weekly', utc_now - datetime.timedelta(days=8))
792 self._mock_utc_now.return_value = utc_now
793 suite_trigger = trigger_receiver.TriggerReceiver()
794 self.assertFalse(suite_trigger.events['weekly'].should_handle)
795
Xixuan Wu40998892017-08-29 14:32:26 -0700796 def testCronForWeeks(self):
797 """Ensure cron job can be successfully scheduled for several weeks."""
Xixuan Wu008ee832017-10-12 16:59:34 -0700798 all_tasks = self._get_ground_truth_task_list_from_config()
Xixuan Wu40998892017-08-29 14:32:26 -0700799 last_now = None
800
801 for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ)):
802 self._mock_utc_now.return_value = now
803 suite_trigger = trigger_receiver.TriggerReceiver()
Xixuan Wu5451a662017-10-17 10:57:40 -0700804 with mock.patch('task.Task.schedule', return_value=True):
805 suite_trigger.cron()
Xixuan Wu40998892017-08-29 14:32:26 -0700806
Xixuan Wu40998892017-08-29 14:32:26 -0700807 should_scheduled_nightly_tasks = [
Xixuan Wu09962902018-12-11 10:49:27 -0800808 t.name for t in all_tasks['nightly'] if t.hour == now.hour]
Xixuan Wu008ee832017-10-12 16:59:34 -0700809
Xixuan Wu09962902018-12-11 10:49:27 -0800810 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700811 should_scheduled_nightly_tasks):
812 self.assertEqual(suite_trigger.event_results['nightly'],
813 should_scheduled_nightly_tasks)
814 else:
815 self.assertNotIn('nightly', suite_trigger.event_results.keys())
816
817 # Verify weekly tasks
818 should_scheduled_weekly_tasks = [
819 t.name for t in all_tasks['weekly']
Xixuan Wu09962902018-12-11 10:49:27 -0800820 if now.weekday() == t.day and now.hour == t.hour]
821 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700822 should_scheduled_weekly_tasks):
823 self.assertEqual(suite_trigger.event_results['weekly'],
824 should_scheduled_weekly_tasks)
825 else:
826 self.assertNotIn('weekly', suite_trigger.event_results.keys())
827
828 # Verify new_build tasks
829 should_scheduled_new_build_tasks = [
830 t.name for t in all_tasks['new_build']]
831 if (_should_schedule_new_build_task(last_now, now) and
832 should_scheduled_new_build_tasks):
833 self.assertEqual(suite_trigger.event_results['new_build'],
834 should_scheduled_new_build_tasks)
835 else:
836 self.assertNotIn('new_build', suite_trigger.event_results.keys())
837
838 last_now = now
839
840
841if __name__ == '__main__':
842 unittest.main()