blob: 6a759e482dd72fbe498b8e82f739643b633c9a4c [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',
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000100 'zako-release')]
Xixuan Wu40998892017-08-29 14:32:26 -0700101
102 def get_latest_passed_builds(self, build_config):
103 """Mock cloud_sql_client.CIDBClient.get_latest_passed_builds."""
104 del build_config # unused
105 return cloud_sql_client.BuildInfo('link', '62', '9868.0.0', build_config)
106
Craig Bergstrom58263d32018-04-26 14:11:35 -0600107 def get_relaxed_pased_builds_since_date(self, since_date):
108 """Mock cloud_sql_client.CIDBClient.get_relaxed_pased_builds_since_date."""
109 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700110 return [cloud_sql_client.BuildInfo('grunt', None, '63', '9968.0.0',
Craig Bergstrom58263d32018-04-26 14:11:35 -0600111 'grunt-release')]
112
Xixuan Wu40998892017-08-29 14:32:26 -0700113
114class FakeAndroidBuildRestClient(object):
115 """Mock rest_client.AndroidBuildRestClient."""
116
117 def get_latest_build_id(self, branch, target):
118 """Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
119 del branch, target # unused
120 return '100'
121
122
123class FakeLabConfig(object):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700124 """Mock config_reader.LabConfig."""
Xixuan Wu40998892017-08-29 14:32:26 -0700125
126 def get_android_board_list(self):
127 """Mock config_reader.LabConfig.get_android_board_list."""
128 return ('android-angler', 'android-bullhead')
129
Xixuan Wu6fb16272017-10-19 13:16:00 -0700130 def get_cros_board_list(self):
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700131 """Mock config_reader.LabConfig.get_cros_board_list."""
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000132 return ('grunt', 'link', 'peppy', 'daisy', 'zako')
Xixuan Wu6fb16272017-10-19 13:16:00 -0700133
Xixuan Wu40998892017-08-29 14:32:26 -0700134 def get_firmware_ro_build_list(self, release_board):
135 """Mock config_reader.LabConfig.get_firmware_ro_build_list."""
136 del release_board # unused
137 return 'firmware1,firmware2'
138
C Shapiro7f24a002017-12-05 14:25:09 -0700139 def get_cros_model_map(self):
140 """Mock config_reader.LabConfig.get_cros_model_map."""
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000141 return {}
C Shapiro7f24a002017-12-05 14:25:09 -0700142
Xixuan Wu40998892017-08-29 14:32:26 -0700143
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700144class FakeMigrationConfig(object):
145 """Mock config_reader.MigrationConfig."""
146
Xixuan Wu1e42c752019-03-21 13:41:49 -0700147 def get_skylab_suites_dump(self):
Xixuan Wuee6b6a82019-03-21 14:28:26 -0700148 """Mock config_reader.MigrationConfig.get_skylab_suite_list."""
Xixuan Wu1e42c752019-03-21 13:41:49 -0700149 return {'suite:ent-nightly':
150 {'pool:suites':
Xixuan Wu028f6732019-04-11 14:47:42 -0700151 {'model:santa': {
152 'skylab': True,
153 'override_pool': '',
154 'override_qs_account': '',
155 },
156 'model:zako': {
157 'skylab': True,
158 'override_pool': 'QUOTA_POOL',
159 'override_qs_account': 'QUOTA_ACCOUNT',
160 }}}}
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700161
162
Xixuan Wu008ee832017-10-12 16:59:34 -0700163class TriggerReceiverBaseTestCase(unittest.TestCase):
Xixuan Wu40998892017-08-29 14:32:26 -0700164
165 def setUp(self):
166 self.testbed = testbed.Testbed()
167 self.testbed.activate()
168 self.addCleanup(self.testbed.deactivate)
169
170 self.testbed.init_datastore_v3_stub()
171 self.testbed.init_memcache_stub()
172 ndb.get_context().clear_cache()
173 self.testbed.init_taskqueue_stub(
174 root_path=os.path.join(os.path.dirname(__file__)))
175 self.taskqueue_stub = self.testbed.get_stub(
176 testbed.TASKQUEUE_SERVICE_NAME)
177
178 mock_cidb_client = mock.patch('cloud_sql_client.CIDBClient')
179 self._mock_cidb_client = mock_cidb_client.start()
180 self.addCleanup(mock_cidb_client.stop)
181
182 mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
183 self._mock_android_client = mock_android_client.start()
184 self.addCleanup(mock_android_client.stop)
185
Xixuan Wu40998892017-08-29 14:32:26 -0700186 mock_lab_config = mock.patch('config_reader.LabConfig')
187 self._mock_lab_config = mock_lab_config.start()
188 self.addCleanup(mock_lab_config.stop)
189
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700190 mock_migration_config = mock.patch('config_reader.MigrationConfig')
191 self._mock_migration_config = mock_migration_config.start()
192 self.addCleanup(mock_migration_config.stop)
193
Xixuan Wu40998892017-08-29 14:32:26 -0700194 mock_utc_now = mock.patch('time_converter.utc_now')
195 self._mock_utc_now = mock_utc_now.start()
196 self.addCleanup(mock_utc_now.stop)
197
198 self._mock_cidb_client.return_value = FakeCIDBClient()
199 self._mock_android_client.return_value = FakeAndroidBuildRestClient()
Xixuan Wu40998892017-08-29 14:32:26 -0700200 self._mock_lab_config.return_value = FakeLabConfig()
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700201 self._mock_migration_config.return_value = FakeMigrationConfig()
Xixuan Wu40998892017-08-29 14:32:26 -0700202
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700203 board_family_patcher = mock.patch(
204 'build_lib.get_board_family_mapping_from_gs')
205 board_family_getter = board_family_patcher.start()
206 board_family_getter.return_value = {
207 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
208 'ivybridge': ['link', 'link_freon']}
209 self.addCleanup(board_family_patcher.stop)
210
Xixuan Wu008ee832017-10-12 16:59:34 -0700211
212class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
213
214 _TEST_PST_HOUR = 20
215 _TEST_PST_DAY = 4 # Friday
216 _TEST_EVENT_PST_HOUR = 13
217
218 _FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
219 _FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
220
221 def setUp(self):
222 super(TriggerReceiverFakeConfigTestCase, self).setUp()
223
224 self.fake_config = config_reader.ConfigReader(None)
225 self._add_nightly_tasks(self.fake_config)
226 self._add_weekly_tasks(self.fake_config)
227
228 self.fake_config_with_settings = config_reader.ConfigReader(None)
229 self._add_weekly_tasks(self.fake_config_with_settings)
230 self._add_weekly_params(self.fake_config_with_settings)
231
232 mock_config_reader = mock.patch('config_reader.ConfigReader')
233 self._mock_config_reader = mock_config_reader.start()
234 self.addCleanup(mock_config_reader.stop)
235
236 def _add_nightly_tasks(self, fake_config):
237 fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
238 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
239 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
240 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
241 str(self._TEST_PST_HOUR))
242
243 def _add_weekly_tasks(self, fake_config):
244 fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
245 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
246 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
247 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
248
249 def _add_weekly_params(self, fake_config):
Xixuan Wu51bb7102019-03-18 14:51:44 -0700250 weekly_section_name = task_config_reader.EVENT_CLASSES[
251 'weekly'].section_name()
Xixuan Wu008ee832017-10-12 16:59:34 -0700252 fake_config.add_section(weekly_section_name)
253 fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
254
255 def testInitializeTriggerReceiverWithNightlyEvent(self):
256 """Test nightly event can be handled on right hour."""
257 # A task with hour=20 should be scheduled at 20:00 in PST everyday, which
258 # is 3:00/4:00 in UTC everyday.
259 self._mock_config_reader.return_value = self.fake_config
260 given_utc_hour = time_converter.convert_time_info(
261 time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
262 utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
263 tzinfo=time_converter.UTC_TZ)
264 last_exec_client = datastore_client.LastExecutionRecordStore()
265 last_exec_client.set_last_execute_time(
266 'nightly', utc_now - datetime.timedelta(hours=1))
267 self._mock_utc_now.return_value = utc_now
268
269 suite_trigger = trigger_receiver.TriggerReceiver()
270 suite_trigger.cron()
271 self.assertTrue(suite_trigger.events['nightly'].should_handle)
272 self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
273 self.assertEqual(suite_trigger.event_results['nightly'][0],
274 self._FAKE_NIGHTLY_TASK_NAME)
275
276 def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
277 """Test weekly event without event settings can be handled on right day."""
278 # A task with day=4 (Friday) and default event_hour (23) should be
279 # scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
280 self._mock_config_reader.return_value = self.fake_config
281 given_utc_hour = time_converter.convert_time_info(
282 time_converter.TimeInfo(
283 self._TEST_PST_DAY,
Xixuan Wu51bb7102019-03-18 14:51:44 -0700284 task_config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
Xixuan Wu008ee832017-10-12 16:59:34 -0700285 utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
286 tzinfo=time_converter.UTC_TZ)
287 last_exec_client = datastore_client.LastExecutionRecordStore()
288 last_exec_client.set_last_execute_time(
289 'weekly', utc_now - datetime.timedelta(days=1))
290 self._mock_utc_now.return_value = utc_now
291
292 suite_trigger = trigger_receiver.TriggerReceiver()
293 suite_trigger.cron()
294 self.assertTrue(suite_trigger.events['weekly'].should_handle)
295 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
296 self.assertEqual(suite_trigger.event_results['weekly'][0],
297 self._FAKE_WEEKLY_TASK_NAME)
298
299 def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
300 """Test weekly event with event settings can be handled on right day."""
301 # A task with day=4 (Friday) and event_hour=13 should be scheduled at
302 # Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
303 self._mock_config_reader.return_value = self.fake_config_with_settings
304 given_utc_time_info = time_converter.convert_time_info(
305 time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
306
307 # Set the current time as a Friday 20:00 or 21:00 in UTC.
308 utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
309 tzinfo=time_converter.UTC_TZ)
310 last_exec_client = datastore_client.LastExecutionRecordStore()
311 last_exec_client.set_last_execute_time(
312 'weekly', utc_now - datetime.timedelta(days=1))
313 self._mock_utc_now.return_value = utc_now
314
315 suite_trigger = trigger_receiver.TriggerReceiver()
316 suite_trigger.cron()
317 self.assertTrue(suite_trigger.events['weekly'].should_handle)
318 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
319 self.assertEqual(suite_trigger.event_results['weekly'][0],
320 self._FAKE_WEEKLY_TASK_NAME)
321
322
Craig Bergstrom58263d32018-04-26 14:11:35 -0600323class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
324 """Test the new_build functionality."""
325
Craig Bergstrom58263d32018-04-26 14:11:35 -0600326 def setUp(self):
327 """Set up for a test."""
328 super(TriggerReceiverFakeBuildTestCase, self).setUp()
329
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700330 self._prepare()
331
332 def _prepare(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -0600333 self.fake_config = config_reader.ConfigReader(None)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600334 mock_config_reader = mock.patch('config_reader.ConfigReader')
335 self._mock_config_reader = mock_config_reader.start()
336 self.addCleanup(mock_config_reader.stop)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600337
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700338 # Set last execution time for new_build events.
339 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600340 last_exec_client = datastore_client.LastExecutionRecordStore()
341 last_exec_client.set_last_execute_time(
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700342 'new_build', utc_now - datetime.timedelta(hours=1))
Craig Bergstrom58263d32018-04-26 14:11:35 -0600343 self._mock_utc_now.return_value = utc_now
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700344
345 def testNewBuildForHWTestSanityRequired(self):
346 """Test the new_build functionality."""
347 # Construct a fake config with only_hwtest_sanity_required.
348 fsnbt_name = 'FakeStrictNewBuildTask'
349 self.fake_config.add_section(fsnbt_name)
350 self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
351 self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
352 frnbt_name = 'FakeRelaxedNewBuildTask'
353 self.fake_config.add_section(frnbt_name)
354 self.fake_config.set(frnbt_name, 'run_on', 'new_build')
355 self.fake_config.set(frnbt_name, 'suite', 'fake_suite_relaxed')
356 self.fake_config.set(frnbt_name, 'only_hwtest_sanity_required', 'True')
357
358 self._mock_config_reader.return_value = self.fake_config
Craig Bergstrom58263d32018-04-26 14:11:35 -0600359
360 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600361 suite_trigger = trigger_receiver.TriggerReceiver()
362 suite_trigger.cron()
363
364 # Validate that the expected tests got kicked off.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600365 self.assertEqual(len(suite_trigger.event_results), 1)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600366 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000367 # 2 for strict passed builds, 3 for relaxed builds.
368 self.assertEqual(len(tasks), 5)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600369
370 # The number of builds that matched the base (success) Task.
371 count_base = 0
372 # The number of builds that matched the relaxed Task.
373 count_relaxed = 0
374 for task in tasks:
375 if 'fake_suite_base' in task.payload:
376 # Make sure it matched the expected build only.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600377 self.assertNotIn('grunt-release', task.payload)
378 count_base += 1
379
380 if 'fake_suite_relaxed' in task.payload:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600381 count_relaxed += 1
382
383 # Make each case matched precisely one event.
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000384 self.assertEqual(2, count_base)
385 self.assertEqual(3, count_relaxed)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600386
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700387 def testNewBuildWithExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700388 """Test the new_build suite with an existing board family."""
389 link_suite = 'FakeLinkNewBuildTask'
390 self.fake_config.add_section(link_suite)
391 self.fake_config.set(link_suite, 'run_on', 'new_build')
392 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700393 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700394 self._mock_config_reader.return_value = self.fake_config
395
396 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
397 suite_trigger = trigger_receiver.TriggerReceiver()
398 suite_trigger.cron()
399
400 self.assertEqual(len(suite_trigger.event_results), 1)
401 self.assertEqual(suite_trigger.event_results['new_build'],
402 [link_suite])
403 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
404 self.assertEqual(len(tasks), 1)
405 self.assertIn('link-release', tasks[0].payload)
406
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700407 def testNewBuildWithExistingBoardFamiliesAndBoards(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700408 """Test the new_build suite with an existing board family."""
409 link_suite = 'FakeLinkNewBuildTask'
410 self.fake_config.add_section(link_suite)
411 self.fake_config.set(link_suite, 'run_on', 'new_build')
412 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700413 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700414 self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
415 self._mock_config_reader.return_value = self.fake_config
416
417 suite_trigger = trigger_receiver.TriggerReceiver()
418 suite_trigger.cron()
419
420 self.assertEqual(len(suite_trigger.event_results), 1)
421 self.assertEqual(suite_trigger.event_results['new_build'],
422 [link_suite])
423 boards = suite_trigger.events['new_build'].task_list[0].boards
424 self.assertIn('asuka', boards)
425 self.assertIn('link', boards)
426
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700427 def testNewBuildWithNonExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700428 """Test the new_build suite with an non-existing board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700429 nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700430 self.fake_config.add_section(nyan_suite)
431 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
432 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700433 self.fake_config.set(nyan_suite, 'board_families', 'nyan')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700434 self._mock_config_reader.return_value = self.fake_config
435
436 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
437 suite_trigger = trigger_receiver.TriggerReceiver()
438 suite_trigger.cron()
439
440 self.assertEqual(len(suite_trigger.event_results), 1)
441 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
442 self.assertEqual(len(tasks), 0)
443
444 def testNewBuildWithNonSpecifiedBoardFamily(self):
445 """Test the new_build suite with an non-specified board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700446 normal_suite = 'FakeBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700447 self.fake_config.add_section(normal_suite)
448 self.fake_config.set(normal_suite, 'run_on', 'new_build')
449 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
450 self._mock_config_reader.return_value = self.fake_config
451
452 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
453 suite_trigger = trigger_receiver.TriggerReceiver()
454 suite_trigger.cron()
455
456 self.assertEqual(len(suite_trigger.event_results), 1)
457 self.assertEqual(suite_trigger.event_results['new_build'],
458 [normal_suite])
459 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000460 self.assertEqual(len(tasks), 2)
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700461
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700462 def testNewBuildExcludingExistingBoardFamilies(self):
463 """Test the new_build suite excluding an existing board family."""
464 link_suite = 'FakeLinkNewBuildTask'
465 self.fake_config.add_section(link_suite)
466 self.fake_config.set(link_suite, 'run_on', 'new_build')
467 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
468 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
469 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 [link_suite])
478 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
479 self.assertEqual(len(tasks), 1)
480 self.assertNotIn('link-release', tasks[0].payload)
481 self.assertIn('zako-release', tasks[0].payload)
482
483 def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
484 """Test the new_build suite with an existing board family."""
485 link_suite = 'FakeLinkNewBuildTask'
486 self.fake_config.add_section(link_suite)
487 self.fake_config.set(link_suite, 'run_on', 'new_build')
488 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
489 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000490 self.fake_config.set(link_suite, 'exclude_boards', 'asuka, paine, banon')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700491 self._mock_config_reader.return_value = self.fake_config
492
493 suite_trigger = trigger_receiver.TriggerReceiver()
494 suite_trigger.cron()
495
496 self.assertEqual(len(suite_trigger.event_results), 1)
497 self.assertEqual(suite_trigger.event_results['new_build'],
498 [link_suite])
499 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
500 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
501 self.assertNotIn('link-release', tasks[0].payload)
502 self.assertNotIn('asuka-release', tasks[0].payload)
503 self.assertIn('zako-release', tasks[0].payload)
504
505 def testNewBuildExcludingNonExistingBoardFamilies(self):
506 """Test the new_build suite excluding an non-existing board family."""
507 nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
508 self.fake_config.add_section(nyan_suite)
509 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
510 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
511 self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
512 self._mock_config_reader.return_value = self.fake_config
513
514 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
515 suite_trigger = trigger_receiver.TriggerReceiver()
516 suite_trigger.cron()
517
518 self.assertEqual(len(suite_trigger.event_results), 1)
519 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
520 self.assertEqual(len(tasks), 2)
521
522 def testNewBuildWithBoardExcludeBoardCollision(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700523 """Test the case that the same board in boards and exclude_boards."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700524 normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
525 self.fake_config.add_section(normal_suite)
526 self.fake_config.set(normal_suite, 'run_on', 'new_build')
527 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
528 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
529 self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
530 self._mock_config_reader.return_value = self.fake_config
531
532 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
533 suite_trigger = trigger_receiver.TriggerReceiver()
534 suite_trigger.cron()
535
536 self.assertEqual(len(suite_trigger.event_results), 1)
537 self.assertEqual(suite_trigger.event_results['new_build'],
538 [normal_suite])
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700539 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
540 self.assertEqual(len(tasks), 1)
541 self.assertNotIn('asuka-release', tasks[0].payload)
542 self.assertIn('zako-release', tasks[0].payload)
543
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700544 def testNewBuildWithSkylab(self):
545 """Test the case that suites run in skylab."""
546 normal_suite = 'FakeSkylabSuite'
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', 'ent-nightly')
Xixuan Wu1e42c752019-03-21 13:41:49 -0700550 self.fake_config.set(normal_suite, 'pool', 'suites')
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700551 self.fake_config.set(normal_suite, 'boards', 'zako, 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])
561 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
562 self.assertEqual(len(tasks), 1)
563 self.assertIn('is_skylab=True', tasks[0].payload)
Xixuan Wu028f6732019-04-11 14:47:42 -0700564 self.assertIn('override_pool=QUOTA_POOL', tasks[0].payload)
565 self.assertIn('override_qs_account=QUOTA_ACCOUNT', tasks[0].payload)
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700566
Craig Bergstrom58263d32018-04-26 14:11:35 -0600567
Xixuan Wu008ee832017-10-12 16:59:34 -0700568class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
569
570 def setUp(self):
571 super(TriggerReceiverRealConfigTestCase, self).setUp()
572 mock_config_reader = mock.patch('config_reader.ConfigReader')
573 self._mock_config_reader = mock_config_reader.start()
574 self.addCleanup(mock_config_reader.stop)
575 self._mock_config_reader.return_value = _SUITE_CONFIG_READER
576
577 def _get_ground_truth_task_list_from_config(self):
578 """Get the ground truth of to-be-scheduled task list from config file."""
579 self._mock_utc_now.return_value = datetime.datetime.now(
580 time_converter.UTC_TZ)
Xixuan Wu51bb7102019-03-18 14:51:44 -0700581 task_config = task_config_reader.TaskConfig(_SUITE_CONFIG_READER)
Xixuan Wu008ee832017-10-12 16:59:34 -0700582 tasks = {}
Xixuan Wu51bb7102019-03-18 14:51:44 -0700583 for keyword, klass in task_config_reader.EVENT_CLASSES.iteritems():
Xixuan Wu008ee832017-10-12 16:59:34 -0700584 new_event = klass(
585 task_config.get_event_setting(klass.section_name()), None)
586 new_event.set_task_list(
587 task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
588 tasks[keyword] = new_event.task_list
589
590 return tasks
591
Xixuan Wu40998892017-08-29 14:32:26 -0700592 def testCronWithoutLastExec(self):
593 """Test the first round of cron can be successfully executed."""
594 self._mock_utc_now.return_value = datetime.datetime.now(
595 time_converter.UTC_TZ)
596 suite_trigger = trigger_receiver.TriggerReceiver()
597 suite_trigger.cron()
598 self.assertFalse(suite_trigger.events['nightly'].should_handle)
599 self.assertFalse(suite_trigger.events['weekly'].should_handle)
600 self.assertFalse(suite_trigger.events['new_build'].should_handle)
601
602 self.assertEqual(suite_trigger.event_results, {})
603
604 def testCronTriggerNightly(self):
605 """Test nightly event is read with available nightly last_exec_time."""
606 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
607 last_exec_client = datastore_client.LastExecutionRecordStore()
608 last_exec_client.set_last_execute_time(
609 'nightly', utc_now - datetime.timedelta(hours=1))
610 self._mock_utc_now.return_value = utc_now
611 suite_trigger = trigger_receiver.TriggerReceiver()
612 self.assertTrue(suite_trigger.events['nightly'].should_handle)
613 self.assertFalse(suite_trigger.events['weekly'].should_handle)
614 self.assertFalse(suite_trigger.events['new_build'].should_handle)
615
Xixuan Wu33179672017-09-12 11:44:04 -0700616 def testCronTriggerNightlyOutdated(self):
617 """Test nightly event is read with available nightly last_exec_time."""
618 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
619 last_exec_client = datastore_client.LastExecutionRecordStore()
620 last_exec_client.set_last_execute_time(
621 'nightly', utc_now - datetime.timedelta(days=3))
622 self._mock_utc_now.return_value = utc_now
623 suite_trigger = trigger_receiver.TriggerReceiver()
624 self.assertFalse(suite_trigger.events['nightly'].should_handle)
625
626 def testCronTriggerWeeklyOutdated(self):
627 """Test weekly event is read with available weekly last_exec_time."""
628 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
629 last_exec_client = datastore_client.LastExecutionRecordStore()
630 last_exec_client.set_last_execute_time(
631 'weekly', utc_now - datetime.timedelta(days=8))
632 self._mock_utc_now.return_value = utc_now
633 suite_trigger = trigger_receiver.TriggerReceiver()
634 self.assertFalse(suite_trigger.events['weekly'].should_handle)
635
Xixuan Wu40998892017-08-29 14:32:26 -0700636 def testCronForWeeks(self):
637 """Ensure cron job can be successfully scheduled for several weeks."""
Xixuan Wu008ee832017-10-12 16:59:34 -0700638 all_tasks = self._get_ground_truth_task_list_from_config()
Xixuan Wu40998892017-08-29 14:32:26 -0700639 last_now = None
640
641 for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ)):
642 self._mock_utc_now.return_value = now
643 suite_trigger = trigger_receiver.TriggerReceiver()
Xixuan Wu5451a662017-10-17 10:57:40 -0700644 with mock.patch('task.Task.schedule', return_value=True):
645 suite_trigger.cron()
Xixuan Wu40998892017-08-29 14:32:26 -0700646
Xixuan Wu40998892017-08-29 14:32:26 -0700647 should_scheduled_nightly_tasks = [
Xixuan Wu09962902018-12-11 10:49:27 -0800648 t.name for t in all_tasks['nightly'] if t.hour == now.hour]
Xixuan Wu008ee832017-10-12 16:59:34 -0700649
Xixuan Wu09962902018-12-11 10:49:27 -0800650 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700651 should_scheduled_nightly_tasks):
652 self.assertEqual(suite_trigger.event_results['nightly'],
653 should_scheduled_nightly_tasks)
654 else:
655 self.assertNotIn('nightly', suite_trigger.event_results.keys())
656
657 # Verify weekly tasks
658 should_scheduled_weekly_tasks = [
659 t.name for t in all_tasks['weekly']
Xixuan Wu09962902018-12-11 10:49:27 -0800660 if now.weekday() == t.day and now.hour == t.hour]
661 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700662 should_scheduled_weekly_tasks):
663 self.assertEqual(suite_trigger.event_results['weekly'],
664 should_scheduled_weekly_tasks)
665 else:
666 self.assertNotIn('weekly', suite_trigger.event_results.keys())
667
668 # Verify new_build tasks
669 should_scheduled_new_build_tasks = [
670 t.name for t in all_tasks['new_build']]
671 if (_should_schedule_new_build_task(last_now, now) and
672 should_scheduled_new_build_tasks):
673 self.assertEqual(suite_trigger.event_results['new_build'],
674 should_scheduled_new_build_tasks)
675 else:
676 self.assertNotIn('new_build', suite_trigger.event_results.keys())
677
678 last_now = now
679
680
681if __name__ == '__main__':
682 unittest.main()