blob: 7770e1e46abc08f8f0d67e2aceeff5e21a308e71 [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 Wu8d2f2862018-08-28 16:48:04 -070097 return [cloud_sql_client.BuildInfo('link', None, '62', '9868.0.0',
Xixuan Wu244e0ec2018-05-23 14:49:55 -070098 'link-release'),
Xixuan Wu8d2f2862018-08-28 16:48:04 -070099 cloud_sql_client.BuildInfo('zako', None, '62', '9868.0.0',
Xixuan Wub4b2f412019-05-03 11:22:31 -0700100 'zako-release'),
101 cloud_sql_client.BuildInfo('samus-kernelnext', None, '62',
102 '9868.0.0', 'samus-kernelnext-release')]
Xixuan Wu40998892017-08-29 14:32:26 -0700103
104 def get_latest_passed_builds(self, build_config):
105 """Mock cloud_sql_client.CIDBClient.get_latest_passed_builds."""
106 del build_config # unused
107 return cloud_sql_client.BuildInfo('link', '62', '9868.0.0', build_config)
108
Craig Bergstrom58263d32018-04-26 14:11:35 -0600109 def get_relaxed_pased_builds_since_date(self, since_date):
110 """Mock cloud_sql_client.CIDBClient.get_relaxed_pased_builds_since_date."""
111 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700112 return [cloud_sql_client.BuildInfo('grunt', None, '63', '9968.0.0',
Craig Bergstrom58263d32018-04-26 14:11:35 -0600113 'grunt-release')]
114
Xixuan Wu40998892017-08-29 14:32:26 -0700115
116class FakeAndroidBuildRestClient(object):
117 """Mock rest_client.AndroidBuildRestClient."""
118
119 def get_latest_build_id(self, branch, target):
120 """Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
121 del branch, target # unused
122 return '100'
123
124
125class FakeLabConfig(object):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700126 """Mock config_reader.LabConfig."""
Xixuan Wu40998892017-08-29 14:32:26 -0700127
128 def get_android_board_list(self):
129 """Mock config_reader.LabConfig.get_android_board_list."""
130 return ('android-angler', 'android-bullhead')
131
Xixuan Wu6fb16272017-10-19 13:16:00 -0700132 def get_cros_board_list(self):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700133 """Mock config_reader.LabConfig.get_cros_board_list."""
Xixuan Wub4b2f412019-05-03 11:22:31 -0700134 return ('grunt', 'link', 'peppy', 'daisy', 'zako', 'samus-kernelnext')
Xixuan Wu6fb16272017-10-19 13:16:00 -0700135
Xixuan Wu40998892017-08-29 14:32:26 -0700136 def get_firmware_ro_build_list(self, release_board):
137 """Mock config_reader.LabConfig.get_firmware_ro_build_list."""
138 del release_board # unused
139 return 'firmware1,firmware2'
140
C Shapiro7f24a002017-12-05 14:25:09 -0700141 def get_cros_model_map(self):
142 """Mock config_reader.LabConfig.get_cros_model_map."""
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000143 return {}
C Shapiro7f24a002017-12-05 14:25:09 -0700144
Xixuan Wu40998892017-08-29 14:32:26 -0700145
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700146class FakeMigrationConfig(object):
147 """Mock config_reader.MigrationConfig."""
148
Xixuan Wu1e42c752019-03-21 13:41:49 -0700149 def get_skylab_suites_dump(self):
Xixuan Wuee6b6a82019-03-21 14:28:26 -0700150 """Mock config_reader.MigrationConfig.get_skylab_suite_list."""
Xixuan Wu1e42c752019-03-21 13:41:49 -0700151 return {'suite:ent-nightly':
152 {'pool:suites':
Xixuan Wu028f6732019-04-11 14:47:42 -0700153 {'model:santa': {
154 'skylab': True,
155 'override_pool': '',
156 'override_qs_account': '',
157 },
158 'model:zako': {
159 'skylab': True,
160 'override_pool': 'QUOTA_POOL',
161 'override_qs_account': 'QUOTA_ACCOUNT',
162 }}}}
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700163
164
Xixuan Wu008ee832017-10-12 16:59:34 -0700165class TriggerReceiverBaseTestCase(unittest.TestCase):
Xixuan Wu40998892017-08-29 14:32:26 -0700166
167 def setUp(self):
168 self.testbed = testbed.Testbed()
169 self.testbed.activate()
170 self.addCleanup(self.testbed.deactivate)
171
172 self.testbed.init_datastore_v3_stub()
173 self.testbed.init_memcache_stub()
174 ndb.get_context().clear_cache()
175 self.testbed.init_taskqueue_stub(
176 root_path=os.path.join(os.path.dirname(__file__)))
177 self.taskqueue_stub = self.testbed.get_stub(
178 testbed.TASKQUEUE_SERVICE_NAME)
179
180 mock_cidb_client = mock.patch('cloud_sql_client.CIDBClient')
181 self._mock_cidb_client = mock_cidb_client.start()
182 self.addCleanup(mock_cidb_client.stop)
183
184 mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
185 self._mock_android_client = mock_android_client.start()
186 self.addCleanup(mock_android_client.stop)
187
Xixuan Wu40998892017-08-29 14:32:26 -0700188 mock_lab_config = mock.patch('config_reader.LabConfig')
189 self._mock_lab_config = mock_lab_config.start()
190 self.addCleanup(mock_lab_config.stop)
191
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700192 mock_migration_config = mock.patch('config_reader.MigrationConfig')
193 self._mock_migration_config = mock_migration_config.start()
194 self.addCleanup(mock_migration_config.stop)
195
Xixuan Wu40998892017-08-29 14:32:26 -0700196 mock_utc_now = mock.patch('time_converter.utc_now')
197 self._mock_utc_now = mock_utc_now.start()
198 self.addCleanup(mock_utc_now.stop)
199
200 self._mock_cidb_client.return_value = FakeCIDBClient()
201 self._mock_android_client.return_value = FakeAndroidBuildRestClient()
Xixuan Wu40998892017-08-29 14:32:26 -0700202 self._mock_lab_config.return_value = FakeLabConfig()
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700203 self._mock_migration_config.return_value = FakeMigrationConfig()
Xixuan Wu40998892017-08-29 14:32:26 -0700204
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700205 board_family_patcher = mock.patch(
206 'build_lib.get_board_family_mapping_from_gs')
207 board_family_getter = board_family_patcher.start()
208 board_family_getter.return_value = {
209 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
210 'ivybridge': ['link', 'link_freon']}
211 self.addCleanup(board_family_patcher.stop)
212
Xixuan Wu008ee832017-10-12 16:59:34 -0700213
214class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
215
216 _TEST_PST_HOUR = 20
217 _TEST_PST_DAY = 4 # Friday
218 _TEST_EVENT_PST_HOUR = 13
219
220 _FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
221 _FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
222
223 def setUp(self):
224 super(TriggerReceiverFakeConfigTestCase, self).setUp()
225
226 self.fake_config = config_reader.ConfigReader(None)
227 self._add_nightly_tasks(self.fake_config)
228 self._add_weekly_tasks(self.fake_config)
229
230 self.fake_config_with_settings = config_reader.ConfigReader(None)
231 self._add_weekly_tasks(self.fake_config_with_settings)
232 self._add_weekly_params(self.fake_config_with_settings)
233
234 mock_config_reader = mock.patch('config_reader.ConfigReader')
235 self._mock_config_reader = mock_config_reader.start()
236 self.addCleanup(mock_config_reader.stop)
237
238 def _add_nightly_tasks(self, fake_config):
239 fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
240 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
241 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
242 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
243 str(self._TEST_PST_HOUR))
244
245 def _add_weekly_tasks(self, fake_config):
246 fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
247 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
248 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
249 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
250
251 def _add_weekly_params(self, fake_config):
Xixuan Wu51bb7102019-03-18 14:51:44 -0700252 weekly_section_name = task_config_reader.EVENT_CLASSES[
253 'weekly'].section_name()
Xixuan Wu008ee832017-10-12 16:59:34 -0700254 fake_config.add_section(weekly_section_name)
255 fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
256
257 def testInitializeTriggerReceiverWithNightlyEvent(self):
258 """Test nightly event can be handled on right hour."""
259 # A task with hour=20 should be scheduled at 20:00 in PST everyday, which
260 # is 3:00/4:00 in UTC everyday.
261 self._mock_config_reader.return_value = self.fake_config
262 given_utc_hour = time_converter.convert_time_info(
263 time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
264 utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
265 tzinfo=time_converter.UTC_TZ)
266 last_exec_client = datastore_client.LastExecutionRecordStore()
267 last_exec_client.set_last_execute_time(
268 'nightly', utc_now - datetime.timedelta(hours=1))
269 self._mock_utc_now.return_value = utc_now
270
271 suite_trigger = trigger_receiver.TriggerReceiver()
272 suite_trigger.cron()
273 self.assertTrue(suite_trigger.events['nightly'].should_handle)
274 self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
275 self.assertEqual(suite_trigger.event_results['nightly'][0],
276 self._FAKE_NIGHTLY_TASK_NAME)
277
278 def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
279 """Test weekly event without event settings can be handled on right day."""
280 # A task with day=4 (Friday) and default event_hour (23) should be
281 # scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
282 self._mock_config_reader.return_value = self.fake_config
283 given_utc_hour = time_converter.convert_time_info(
284 time_converter.TimeInfo(
285 self._TEST_PST_DAY,
Xixuan Wu51bb7102019-03-18 14:51:44 -0700286 task_config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
Xixuan Wu008ee832017-10-12 16:59:34 -0700287 utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
288 tzinfo=time_converter.UTC_TZ)
289 last_exec_client = datastore_client.LastExecutionRecordStore()
290 last_exec_client.set_last_execute_time(
291 'weekly', utc_now - datetime.timedelta(days=1))
292 self._mock_utc_now.return_value = utc_now
293
294 suite_trigger = trigger_receiver.TriggerReceiver()
295 suite_trigger.cron()
296 self.assertTrue(suite_trigger.events['weekly'].should_handle)
297 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
298 self.assertEqual(suite_trigger.event_results['weekly'][0],
299 self._FAKE_WEEKLY_TASK_NAME)
300
301 def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
302 """Test weekly event with event settings can be handled on right day."""
303 # A task with day=4 (Friday) and event_hour=13 should be scheduled at
304 # Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
305 self._mock_config_reader.return_value = self.fake_config_with_settings
306 given_utc_time_info = time_converter.convert_time_info(
307 time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
308
309 # Set the current time as a Friday 20:00 or 21:00 in UTC.
310 utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
311 tzinfo=time_converter.UTC_TZ)
312 last_exec_client = datastore_client.LastExecutionRecordStore()
313 last_exec_client.set_last_execute_time(
314 'weekly', utc_now - datetime.timedelta(days=1))
315 self._mock_utc_now.return_value = utc_now
316
317 suite_trigger = trigger_receiver.TriggerReceiver()
318 suite_trigger.cron()
319 self.assertTrue(suite_trigger.events['weekly'].should_handle)
320 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
321 self.assertEqual(suite_trigger.event_results['weekly'][0],
322 self._FAKE_WEEKLY_TASK_NAME)
323
324
Craig Bergstrom58263d32018-04-26 14:11:35 -0600325class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
326 """Test the new_build functionality."""
327
Craig Bergstrom58263d32018-04-26 14:11:35 -0600328 def setUp(self):
329 """Set up for a test."""
330 super(TriggerReceiverFakeBuildTestCase, self).setUp()
331
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700332 self._prepare()
333
334 def _prepare(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -0600335 self.fake_config = config_reader.ConfigReader(None)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600336 mock_config_reader = mock.patch('config_reader.ConfigReader')
337 self._mock_config_reader = mock_config_reader.start()
338 self.addCleanup(mock_config_reader.stop)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600339
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700340 # Set last execution time for new_build events.
341 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600342 last_exec_client = datastore_client.LastExecutionRecordStore()
343 last_exec_client.set_last_execute_time(
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700344 'new_build', utc_now - datetime.timedelta(hours=1))
Craig Bergstrom58263d32018-04-26 14:11:35 -0600345 self._mock_utc_now.return_value = utc_now
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700346
347 def testNewBuildForHWTestSanityRequired(self):
348 """Test the new_build functionality."""
349 # Construct a fake config with only_hwtest_sanity_required.
350 fsnbt_name = 'FakeStrictNewBuildTask'
351 self.fake_config.add_section(fsnbt_name)
352 self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
353 self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700354 self.fake_config.set(fsnbt_name, 'boards', 'link, zako, grunt')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700355 frnbt_name = 'FakeRelaxedNewBuildTask'
356 self.fake_config.add_section(frnbt_name)
357 self.fake_config.set(frnbt_name, 'run_on', 'new_build')
358 self.fake_config.set(frnbt_name, 'suite', 'fake_suite_relaxed')
359 self.fake_config.set(frnbt_name, 'only_hwtest_sanity_required', 'True')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700360 self.fake_config.set(frnbt_name, 'boards', 'link, zako, grunt')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700361
362 self._mock_config_reader.return_value = self.fake_config
Craig Bergstrom58263d32018-04-26 14:11:35 -0600363
364 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600365 suite_trigger = trigger_receiver.TriggerReceiver()
366 suite_trigger.cron()
367
368 # Validate that the expected tests got kicked off.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600369 self.assertEqual(len(suite_trigger.event_results), 1)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600370 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000371 # 2 for strict passed builds, 3 for relaxed builds.
372 self.assertEqual(len(tasks), 5)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600373
374 # The number of builds that matched the base (success) Task.
375 count_base = 0
376 # The number of builds that matched the relaxed Task.
377 count_relaxed = 0
378 for task in tasks:
379 if 'fake_suite_base' in task.payload:
380 # Make sure it matched the expected build only.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600381 self.assertNotIn('grunt-release', task.payload)
382 count_base += 1
383
384 if 'fake_suite_relaxed' in task.payload:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600385 count_relaxed += 1
386
387 # Make each case matched precisely one event.
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000388 self.assertEqual(2, count_base)
389 self.assertEqual(3, count_relaxed)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600390
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700391 def testNewBuildWithExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700392 """Test the new_build suite with an existing board family."""
393 link_suite = 'FakeLinkNewBuildTask'
394 self.fake_config.add_section(link_suite)
395 self.fake_config.set(link_suite, 'run_on', 'new_build')
396 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700397 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700398 self._mock_config_reader.return_value = self.fake_config
399
400 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
401 suite_trigger = trigger_receiver.TriggerReceiver()
402 suite_trigger.cron()
403
404 self.assertEqual(len(suite_trigger.event_results), 1)
405 self.assertEqual(suite_trigger.event_results['new_build'],
406 [link_suite])
407 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
408 self.assertEqual(len(tasks), 1)
409 self.assertIn('link-release', tasks[0].payload)
410
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700411 def testNewBuildWithExistingBoardFamiliesAndBoards(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700412 """Test the new_build suite with an existing board family."""
413 link_suite = 'FakeLinkNewBuildTask'
414 self.fake_config.add_section(link_suite)
415 self.fake_config.set(link_suite, 'run_on', 'new_build')
416 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700417 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700418 self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
419 self._mock_config_reader.return_value = self.fake_config
420
421 suite_trigger = trigger_receiver.TriggerReceiver()
422 suite_trigger.cron()
423
424 self.assertEqual(len(suite_trigger.event_results), 1)
425 self.assertEqual(suite_trigger.event_results['new_build'],
426 [link_suite])
427 boards = suite_trigger.events['new_build'].task_list[0].boards
428 self.assertIn('asuka', boards)
429 self.assertIn('link', boards)
430
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700431 def testNewBuildWithNonExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700432 """Test the new_build suite with an non-existing board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700433 nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700434 self.fake_config.add_section(nyan_suite)
435 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
436 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700437 self.fake_config.set(nyan_suite, 'board_families', 'nyan')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700438 self._mock_config_reader.return_value = self.fake_config
439
440 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
441 suite_trigger = trigger_receiver.TriggerReceiver()
442 suite_trigger.cron()
443
444 self.assertEqual(len(suite_trigger.event_results), 1)
445 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
446 self.assertEqual(len(tasks), 0)
447
448 def testNewBuildWithNonSpecifiedBoardFamily(self):
449 """Test the new_build suite with an non-specified board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700450 normal_suite = 'FakeBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700451 self.fake_config.add_section(normal_suite)
452 self.fake_config.set(normal_suite, 'run_on', 'new_build')
453 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700454 self.fake_config.set(normal_suite, 'boards', 'link, zako')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700455 self._mock_config_reader.return_value = self.fake_config
456
457 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
458 suite_trigger = trigger_receiver.TriggerReceiver()
459 suite_trigger.cron()
460
461 self.assertEqual(len(suite_trigger.event_results), 1)
462 self.assertEqual(suite_trigger.event_results['new_build'],
463 [normal_suite])
464 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000465 self.assertEqual(len(tasks), 2)
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700466
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700467 def testNewBuildExcludingExistingBoardFamilies(self):
468 """Test the new_build suite excluding an existing board family."""
469 link_suite = 'FakeLinkNewBuildTask'
470 self.fake_config.add_section(link_suite)
471 self.fake_config.set(link_suite, 'run_on', 'new_build')
472 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
473 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700474 self.fake_config.set(link_suite, 'boards', 'link, zako')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700475 self._mock_config_reader.return_value = self.fake_config
476
477 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
478 suite_trigger = trigger_receiver.TriggerReceiver()
479 suite_trigger.cron()
480
481 self.assertEqual(len(suite_trigger.event_results), 1)
482 self.assertEqual(suite_trigger.event_results['new_build'],
483 [link_suite])
484 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
485 self.assertEqual(len(tasks), 1)
486 self.assertNotIn('link-release', tasks[0].payload)
487 self.assertIn('zako-release', tasks[0].payload)
488
489 def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
490 """Test the new_build suite with an existing board family."""
491 link_suite = 'FakeLinkNewBuildTask'
492 self.fake_config.add_section(link_suite)
493 self.fake_config.set(link_suite, 'run_on', 'new_build')
494 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
495 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000496 self.fake_config.set(link_suite, 'exclude_boards', 'asuka, paine, banon')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700497 self._mock_config_reader.return_value = self.fake_config
498
499 suite_trigger = trigger_receiver.TriggerReceiver()
500 suite_trigger.cron()
501
502 self.assertEqual(len(suite_trigger.event_results), 1)
503 self.assertEqual(suite_trigger.event_results['new_build'],
504 [link_suite])
505 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
506 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
507 self.assertNotIn('link-release', tasks[0].payload)
508 self.assertNotIn('asuka-release', tasks[0].payload)
509 self.assertIn('zako-release', tasks[0].payload)
510
511 def testNewBuildExcludingNonExistingBoardFamilies(self):
512 """Test the new_build suite excluding an non-existing board family."""
513 nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
514 self.fake_config.add_section(nyan_suite)
515 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
516 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
517 self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
Xixuan Wub4b2f412019-05-03 11:22:31 -0700518 self.fake_config.set(nyan_suite, 'boards', 'link, zako')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700519 self._mock_config_reader.return_value = self.fake_config
520
521 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
522 suite_trigger = trigger_receiver.TriggerReceiver()
523 suite_trigger.cron()
524
525 self.assertEqual(len(suite_trigger.event_results), 1)
526 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
527 self.assertEqual(len(tasks), 2)
528
529 def testNewBuildWithBoardExcludeBoardCollision(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700530 """Test the case that the same board in boards and exclude_boards."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700531 normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
532 self.fake_config.add_section(normal_suite)
533 self.fake_config.set(normal_suite, 'run_on', 'new_build')
534 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
535 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
536 self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
537 self._mock_config_reader.return_value = self.fake_config
538
539 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
540 suite_trigger = trigger_receiver.TriggerReceiver()
541 suite_trigger.cron()
542
543 self.assertEqual(len(suite_trigger.event_results), 1)
544 self.assertEqual(suite_trigger.event_results['new_build'],
545 [normal_suite])
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700546 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
547 self.assertEqual(len(tasks), 1)
548 self.assertNotIn('asuka-release', tasks[0].payload)
549 self.assertIn('zako-release', tasks[0].payload)
550
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700551 def testNewBuildWithSkylab(self):
552 """Test the case that suites run in skylab."""
553 normal_suite = 'FakeSkylabSuite'
554 self.fake_config.add_section(normal_suite)
555 self.fake_config.set(normal_suite, 'run_on', 'new_build')
556 self.fake_config.set(normal_suite, 'suite', 'ent-nightly')
Xixuan Wu1e42c752019-03-21 13:41:49 -0700557 self.fake_config.set(normal_suite, 'pool', 'suites')
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700558 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
559 self._mock_config_reader.return_value = self.fake_config
560
561 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
562 suite_trigger = trigger_receiver.TriggerReceiver()
563 suite_trigger.cron()
564
565 self.assertEqual(len(suite_trigger.event_results), 1)
566 self.assertEqual(suite_trigger.event_results['new_build'],
567 [normal_suite])
568 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
569 self.assertEqual(len(tasks), 1)
570 self.assertIn('is_skylab=True', tasks[0].payload)
Xixuan Wu028f6732019-04-11 14:47:42 -0700571 self.assertIn('override_pool=QUOTA_POOL', tasks[0].payload)
572 self.assertIn('override_qs_account=QUOTA_ACCOUNT', tasks[0].payload)
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700573
Xixuan Wub4b2f412019-05-03 11:22:31 -0700574 def testNewBuildWithKernelnext(self):
575 """Test the case that suites run with board-kernelnext build."""
576 normal_suite = 'FakeKernelnextSuite'
577 self.fake_config.add_section(normal_suite)
578 self.fake_config.set(normal_suite, 'run_on', 'new_build')
579 self.fake_config.set(normal_suite, 'suite', 'fake-suite')
580 self.fake_config.set(normal_suite, 'pool', 'suites')
581 self.fake_config.set(normal_suite, 'boards', 'samus-kernelnext')
582 self._mock_config_reader.return_value = self.fake_config
583
584 self._mock_cidb_client.return_value = FakeCIDBClient()
585
586 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
587 suite_trigger = trigger_receiver.TriggerReceiver()
588 suite_trigger.cron()
589
590 self.assertEqual(len(suite_trigger.event_results), 1)
591 self.assertEqual(suite_trigger.event_results['new_build'],
592 [normal_suite])
593 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
594 self.assertEqual(len(tasks), 1)
595 # Board should be 'samus'.
596 self.assertIn('&board=samus&', tasks[0].payload)
597 # Build should be 'samus-kernelnext-release***'.
598 self.assertIn('cros_version=samus-kernelnext-release', tasks[0].payload)
599
Craig Bergstrom58263d32018-04-26 14:11:35 -0600600
Xixuan Wu008ee832017-10-12 16:59:34 -0700601class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
602
603 def setUp(self):
604 super(TriggerReceiverRealConfigTestCase, self).setUp()
605 mock_config_reader = mock.patch('config_reader.ConfigReader')
606 self._mock_config_reader = mock_config_reader.start()
607 self.addCleanup(mock_config_reader.stop)
608 self._mock_config_reader.return_value = _SUITE_CONFIG_READER
609
610 def _get_ground_truth_task_list_from_config(self):
611 """Get the ground truth of to-be-scheduled task list from config file."""
612 self._mock_utc_now.return_value = datetime.datetime.now(
613 time_converter.UTC_TZ)
Xixuan Wu51bb7102019-03-18 14:51:44 -0700614 task_config = task_config_reader.TaskConfig(_SUITE_CONFIG_READER)
Xixuan Wu008ee832017-10-12 16:59:34 -0700615 tasks = {}
Xixuan Wu51bb7102019-03-18 14:51:44 -0700616 for keyword, klass in task_config_reader.EVENT_CLASSES.iteritems():
Xixuan Wu008ee832017-10-12 16:59:34 -0700617 new_event = klass(
618 task_config.get_event_setting(klass.section_name()), None)
619 new_event.set_task_list(
620 task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
621 tasks[keyword] = new_event.task_list
622
623 return tasks
624
Xixuan Wu40998892017-08-29 14:32:26 -0700625 def testCronWithoutLastExec(self):
626 """Test the first round of cron can be successfully executed."""
627 self._mock_utc_now.return_value = datetime.datetime.now(
628 time_converter.UTC_TZ)
629 suite_trigger = trigger_receiver.TriggerReceiver()
630 suite_trigger.cron()
631 self.assertFalse(suite_trigger.events['nightly'].should_handle)
632 self.assertFalse(suite_trigger.events['weekly'].should_handle)
633 self.assertFalse(suite_trigger.events['new_build'].should_handle)
634
635 self.assertEqual(suite_trigger.event_results, {})
636
637 def testCronTriggerNightly(self):
638 """Test nightly event is read with available nightly last_exec_time."""
639 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
640 last_exec_client = datastore_client.LastExecutionRecordStore()
641 last_exec_client.set_last_execute_time(
642 'nightly', utc_now - datetime.timedelta(hours=1))
643 self._mock_utc_now.return_value = utc_now
644 suite_trigger = trigger_receiver.TriggerReceiver()
645 self.assertTrue(suite_trigger.events['nightly'].should_handle)
646 self.assertFalse(suite_trigger.events['weekly'].should_handle)
647 self.assertFalse(suite_trigger.events['new_build'].should_handle)
648
Xixuan Wu33179672017-09-12 11:44:04 -0700649 def testCronTriggerNightlyOutdated(self):
650 """Test nightly event is read with available nightly last_exec_time."""
651 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
652 last_exec_client = datastore_client.LastExecutionRecordStore()
653 last_exec_client.set_last_execute_time(
654 'nightly', utc_now - datetime.timedelta(days=3))
655 self._mock_utc_now.return_value = utc_now
656 suite_trigger = trigger_receiver.TriggerReceiver()
657 self.assertFalse(suite_trigger.events['nightly'].should_handle)
658
659 def testCronTriggerWeeklyOutdated(self):
660 """Test weekly event is read with available weekly last_exec_time."""
661 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
662 last_exec_client = datastore_client.LastExecutionRecordStore()
663 last_exec_client.set_last_execute_time(
664 'weekly', utc_now - datetime.timedelta(days=8))
665 self._mock_utc_now.return_value = utc_now
666 suite_trigger = trigger_receiver.TriggerReceiver()
667 self.assertFalse(suite_trigger.events['weekly'].should_handle)
668
Xixuan Wu40998892017-08-29 14:32:26 -0700669 def testCronForWeeks(self):
670 """Ensure cron job can be successfully scheduled for several weeks."""
Xixuan Wu008ee832017-10-12 16:59:34 -0700671 all_tasks = self._get_ground_truth_task_list_from_config()
Xixuan Wu40998892017-08-29 14:32:26 -0700672 last_now = None
673
674 for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ)):
675 self._mock_utc_now.return_value = now
676 suite_trigger = trigger_receiver.TriggerReceiver()
Xixuan Wu5451a662017-10-17 10:57:40 -0700677 with mock.patch('task.Task.schedule', return_value=True):
678 suite_trigger.cron()
Xixuan Wu40998892017-08-29 14:32:26 -0700679
Xixuan Wu40998892017-08-29 14:32:26 -0700680 should_scheduled_nightly_tasks = [
Xixuan Wu09962902018-12-11 10:49:27 -0800681 t.name for t in all_tasks['nightly'] if t.hour == now.hour]
Xixuan Wu008ee832017-10-12 16:59:34 -0700682
Xixuan Wu09962902018-12-11 10:49:27 -0800683 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700684 should_scheduled_nightly_tasks):
685 self.assertEqual(suite_trigger.event_results['nightly'],
686 should_scheduled_nightly_tasks)
687 else:
688 self.assertNotIn('nightly', suite_trigger.event_results.keys())
689
690 # Verify weekly tasks
691 should_scheduled_weekly_tasks = [
692 t.name for t in all_tasks['weekly']
Xixuan Wu09962902018-12-11 10:49:27 -0800693 if now.weekday() == t.day and now.hour == t.hour]
694 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700695 should_scheduled_weekly_tasks):
696 self.assertEqual(suite_trigger.event_results['weekly'],
697 should_scheduled_weekly_tasks)
698 else:
699 self.assertNotIn('weekly', suite_trigger.event_results.keys())
700
701 # Verify new_build tasks
702 should_scheduled_new_build_tasks = [
703 t.name for t in all_tasks['new_build']]
704 if (_should_schedule_new_build_task(last_now, now) and
705 should_scheduled_new_build_tasks):
706 self.assertEqual(suite_trigger.event_results['new_build'],
707 should_scheduled_new_build_tasks)
708 else:
709 self.assertNotIn('new_build', suite_trigger.event_results.keys())
710
711 last_now = now
712
713
714if __name__ == '__main__':
715 unittest.main()