blob: 9b2a2ccacd82ca341879e369a12a035688157e34 [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
Craig Bergstrom58263d32018-04-26 14:11:35 -060017import task_executor
Xixuan Wu40998892017-08-29 14:32:26 -070018import time_converter
19import trigger_receiver
20
Craig Bergstrom58263d32018-04-26 14:11:35 -060021from google.appengine.api import taskqueue
Xixuan Wu40998892017-08-29 14:32:26 -070022from google.appengine.ext import ndb
23from google.appengine.ext import testbed
24
25# Ensure that SUITE_SCHEDULER_CONFIG_FILE is read only once.
26_SUITE_CONFIG_READER = config_reader.ConfigReader(
Xixuan Wu26d06e02017-09-20 14:50:28 -070027 file_getter.TEST_SUITE_SCHEDULER_CONFIG_FILE)
Xixuan Wu40998892017-08-29 14:32:26 -070028
29
30def now_generator(start_time, interval_min=30, last_days=7):
31 """A datetime.datetime.now generator.
32
33 The generator will generate 'now' from start_time till start_time+last_days
34 for every interval_min.
35
36 Args:
37 start_time: A datetime.datetime object representing the initial value of
38 the 'now'.
39 interval_min: The interval minutes between current 'now' and next 'now.
40 last_days: Representing how many days this generator will last.
41
42 Yields:
43 a datetime.datetime object to mock the current time.
44 """
45 cur_time = start_time
46 end_time = start_time + datetime.timedelta(days=last_days)
47 while cur_time < end_time:
48 yield cur_time
49 cur_time += datetime.timedelta(minutes=interval_min)
50
51
Xixuan Wu09962902018-12-11 10:49:27 -080052def _should_schedule_timed_task(last_now, now):
53 """Check whether timed (weekly/nightly) task should be scheduled.
Xixuan Wu40998892017-08-29 14:32:26 -070054
Xixuan Wu09962902018-12-11 10:49:27 -080055 A timed task should be schduled when next hour is coming.
Xixuan Wu40998892017-08-29 14:32:26 -070056
57 Args:
Xixuan Wu09962902018-12-11 10:49:27 -080058 last_now: the last time to check if timed task should be scheduled
Xixuan Wu40998892017-08-29 14:32:26 -070059 or not.
Xixuan Wu09962902018-12-11 10:49:27 -080060 now: the current time to check if timed task should be scheduled.
Xixuan Wu40998892017-08-29 14:32:26 -070061
62 Returns:
63 a boolean indicating whether there will be nightly tasks scheduled.
64 """
65 if last_now is not None and last_now.hour != now.hour:
66 return True
67
68 return False
69
70
Xixuan Wu40998892017-08-29 14:32:26 -070071def _should_schedule_new_build_task(last_now, now):
72 """Check whether weekly task should be scheduled.
73
74 A new_build task should be schduled when there're new builds between last
75 check and this check.
76
77 Args:
78 last_now: the last time to check if new_build task should be scheduled.
79 now: the current time to check if new_build task should be scheduled.
80
81 Returns:
82 a boolean indicating whether there will be new_build tasks scheduled.
83 """
84 if last_now is not None and last_now != now:
85 return True
86
87 return False
88
89
90class FakeCIDBClient(object):
91 """Mock cloud_sql_client.CIDBClient."""
92
93 def get_passed_builds_since_date(self, since_date):
94 """Mock cloud_sql_client.CIDBClient.get_passed_builds_since_date."""
95 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -070096 return [cloud_sql_client.BuildInfo('link', None, '62', '9868.0.0',
Xixuan Wu244e0ec2018-05-23 14:49:55 -070097 'link-release'),
Xixuan Wu8d2f2862018-08-28 16:48:04 -070098 cloud_sql_client.BuildInfo('zako', None, '62', '9868.0.0',
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +000099 'zako-release')]
Xixuan Wu40998892017-08-29 14:32:26 -0700100
101 def get_latest_passed_builds(self, build_config):
102 """Mock cloud_sql_client.CIDBClient.get_latest_passed_builds."""
103 del build_config # unused
104 return cloud_sql_client.BuildInfo('link', '62', '9868.0.0', build_config)
105
Craig Bergstrom58263d32018-04-26 14:11:35 -0600106 def get_relaxed_pased_builds_since_date(self, since_date):
107 """Mock cloud_sql_client.CIDBClient.get_relaxed_pased_builds_since_date."""
108 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700109 return [cloud_sql_client.BuildInfo('grunt', None, '63', '9968.0.0',
Craig Bergstrom58263d32018-04-26 14:11:35 -0600110 'grunt-release')]
111
Xixuan Wu40998892017-08-29 14:32:26 -0700112
113class FakeAndroidBuildRestClient(object):
114 """Mock rest_client.AndroidBuildRestClient."""
115
116 def get_latest_build_id(self, branch, target):
117 """Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
118 del branch, target # unused
119 return '100'
120
121
122class FakeLabConfig(object):
123 """Mock rest_client.AndroidBuildRestClient."""
124
125 def get_android_board_list(self):
126 """Mock config_reader.LabConfig.get_android_board_list."""
127 return ('android-angler', 'android-bullhead')
128
Xixuan Wu6fb16272017-10-19 13:16:00 -0700129 def get_cros_board_list(self):
130 """Mock config_reader.LabConfig.get_android_board_list."""
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000131 return ('grunt', 'link', 'peppy', 'daisy', 'zako')
Xixuan Wu6fb16272017-10-19 13:16:00 -0700132
Xixuan Wubb74a372018-08-21 17:37:08 -0700133 def get_skylab_board_list(self):
134 """Mock config_reader.LabConfig.get_skylab_board_list."""
135 return set(['nyan_blaze'])
136
Xixuan Wu446b8ad2018-08-23 11:25:43 -0700137 def get_skylab_suite_list(self):
138 """Mock config_reader.LabConfig.get_skylab_suite_list."""
139 return set(['ent-nightly'])
140
Xixuan Wu5ff0fac2019-01-07 10:06:35 -0800141 def get_skylab_model_map(self):
142 """Mock config_reader.LabConfig.get_skylab_model_map."""
143 return {'coral': ['santa']}
144
Xixuan Wu40998892017-08-29 14:32:26 -0700145 def get_firmware_ro_build_list(self, release_board):
146 """Mock config_reader.LabConfig.get_firmware_ro_build_list."""
147 del release_board # unused
148 return 'firmware1,firmware2'
149
C Shapiro7f24a002017-12-05 14:25:09 -0700150 def get_cros_model_map(self):
151 """Mock config_reader.LabConfig.get_cros_model_map."""
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000152 return {}
C Shapiro7f24a002017-12-05 14:25:09 -0700153
Xixuan Wu40998892017-08-29 14:32:26 -0700154
Xixuan Wu008ee832017-10-12 16:59:34 -0700155class TriggerReceiverBaseTestCase(unittest.TestCase):
Xixuan Wu40998892017-08-29 14:32:26 -0700156
157 def setUp(self):
158 self.testbed = testbed.Testbed()
159 self.testbed.activate()
160 self.addCleanup(self.testbed.deactivate)
161
162 self.testbed.init_datastore_v3_stub()
163 self.testbed.init_memcache_stub()
164 ndb.get_context().clear_cache()
165 self.testbed.init_taskqueue_stub(
166 root_path=os.path.join(os.path.dirname(__file__)))
167 self.taskqueue_stub = self.testbed.get_stub(
168 testbed.TASKQUEUE_SERVICE_NAME)
169
170 mock_cidb_client = mock.patch('cloud_sql_client.CIDBClient')
171 self._mock_cidb_client = mock_cidb_client.start()
172 self.addCleanup(mock_cidb_client.stop)
173
174 mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
175 self._mock_android_client = mock_android_client.start()
176 self.addCleanup(mock_android_client.stop)
177
Xixuan Wu40998892017-08-29 14:32:26 -0700178 mock_lab_config = mock.patch('config_reader.LabConfig')
179 self._mock_lab_config = mock_lab_config.start()
180 self.addCleanup(mock_lab_config.stop)
181
182 mock_utc_now = mock.patch('time_converter.utc_now')
183 self._mock_utc_now = mock_utc_now.start()
184 self.addCleanup(mock_utc_now.stop)
185
186 self._mock_cidb_client.return_value = FakeCIDBClient()
187 self._mock_android_client.return_value = FakeAndroidBuildRestClient()
Xixuan Wu40998892017-08-29 14:32:26 -0700188 self._mock_lab_config.return_value = FakeLabConfig()
189
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700190 board_family_patcher = mock.patch(
191 'build_lib.get_board_family_mapping_from_gs')
192 board_family_getter = board_family_patcher.start()
193 board_family_getter.return_value = {
194 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
195 'ivybridge': ['link', 'link_freon']}
196 self.addCleanup(board_family_patcher.stop)
197
Xixuan Wu008ee832017-10-12 16:59:34 -0700198
199class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
200
201 _TEST_PST_HOUR = 20
202 _TEST_PST_DAY = 4 # Friday
203 _TEST_EVENT_PST_HOUR = 13
204
205 _FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
206 _FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
207
208 def setUp(self):
209 super(TriggerReceiverFakeConfigTestCase, self).setUp()
210
211 self.fake_config = config_reader.ConfigReader(None)
212 self._add_nightly_tasks(self.fake_config)
213 self._add_weekly_tasks(self.fake_config)
214
215 self.fake_config_with_settings = config_reader.ConfigReader(None)
216 self._add_weekly_tasks(self.fake_config_with_settings)
217 self._add_weekly_params(self.fake_config_with_settings)
218
219 mock_config_reader = mock.patch('config_reader.ConfigReader')
220 self._mock_config_reader = mock_config_reader.start()
221 self.addCleanup(mock_config_reader.stop)
222
223 def _add_nightly_tasks(self, fake_config):
224 fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
225 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
226 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
227 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
228 str(self._TEST_PST_HOUR))
229
230 def _add_weekly_tasks(self, fake_config):
231 fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
232 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
233 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
234 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
235
236 def _add_weekly_params(self, fake_config):
237 weekly_section_name = config_reader.EVENT_CLASSES['weekly'].section_name()
238 fake_config.add_section(weekly_section_name)
239 fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
240
241 def testInitializeTriggerReceiverWithNightlyEvent(self):
242 """Test nightly event can be handled on right hour."""
243 # A task with hour=20 should be scheduled at 20:00 in PST everyday, which
244 # is 3:00/4:00 in UTC everyday.
245 self._mock_config_reader.return_value = self.fake_config
246 given_utc_hour = time_converter.convert_time_info(
247 time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
248 utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
249 tzinfo=time_converter.UTC_TZ)
250 last_exec_client = datastore_client.LastExecutionRecordStore()
251 last_exec_client.set_last_execute_time(
252 'nightly', utc_now - datetime.timedelta(hours=1))
253 self._mock_utc_now.return_value = utc_now
254
255 suite_trigger = trigger_receiver.TriggerReceiver()
256 suite_trigger.cron()
257 self.assertTrue(suite_trigger.events['nightly'].should_handle)
258 self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
259 self.assertEqual(suite_trigger.event_results['nightly'][0],
260 self._FAKE_NIGHTLY_TASK_NAME)
261
262 def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
263 """Test weekly event without event settings can be handled on right day."""
264 # A task with day=4 (Friday) and default event_hour (23) should be
265 # scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
266 self._mock_config_reader.return_value = self.fake_config
267 given_utc_hour = time_converter.convert_time_info(
268 time_converter.TimeInfo(
269 self._TEST_PST_DAY,
270 config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
271 utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
272 tzinfo=time_converter.UTC_TZ)
273 last_exec_client = datastore_client.LastExecutionRecordStore()
274 last_exec_client.set_last_execute_time(
275 'weekly', utc_now - datetime.timedelta(days=1))
276 self._mock_utc_now.return_value = utc_now
277
278 suite_trigger = trigger_receiver.TriggerReceiver()
279 suite_trigger.cron()
280 self.assertTrue(suite_trigger.events['weekly'].should_handle)
281 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
282 self.assertEqual(suite_trigger.event_results['weekly'][0],
283 self._FAKE_WEEKLY_TASK_NAME)
284
285 def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
286 """Test weekly event with event settings can be handled on right day."""
287 # A task with day=4 (Friday) and event_hour=13 should be scheduled at
288 # Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
289 self._mock_config_reader.return_value = self.fake_config_with_settings
290 given_utc_time_info = time_converter.convert_time_info(
291 time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
292
293 # Set the current time as a Friday 20:00 or 21:00 in UTC.
294 utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
295 tzinfo=time_converter.UTC_TZ)
296 last_exec_client = datastore_client.LastExecutionRecordStore()
297 last_exec_client.set_last_execute_time(
298 'weekly', utc_now - datetime.timedelta(days=1))
299 self._mock_utc_now.return_value = utc_now
300
301 suite_trigger = trigger_receiver.TriggerReceiver()
302 suite_trigger.cron()
303 self.assertTrue(suite_trigger.events['weekly'].should_handle)
304 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
305 self.assertEqual(suite_trigger.event_results['weekly'][0],
306 self._FAKE_WEEKLY_TASK_NAME)
307
308
Craig Bergstrom58263d32018-04-26 14:11:35 -0600309class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
310 """Test the new_build functionality."""
311
Craig Bergstrom58263d32018-04-26 14:11:35 -0600312 def setUp(self):
313 """Set up for a test."""
314 super(TriggerReceiverFakeBuildTestCase, self).setUp()
315
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700316 self._prepare()
317
318 def _prepare(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -0600319 self.fake_config = config_reader.ConfigReader(None)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600320 mock_config_reader = mock.patch('config_reader.ConfigReader')
321 self._mock_config_reader = mock_config_reader.start()
322 self.addCleanup(mock_config_reader.stop)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600323
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700324 # Set last execution time for new_build events.
325 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600326 last_exec_client = datastore_client.LastExecutionRecordStore()
327 last_exec_client.set_last_execute_time(
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700328 'new_build', utc_now - datetime.timedelta(hours=1))
Craig Bergstrom58263d32018-04-26 14:11:35 -0600329 self._mock_utc_now.return_value = utc_now
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700330
331 def testNewBuildForHWTestSanityRequired(self):
332 """Test the new_build functionality."""
333 # Construct a fake config with only_hwtest_sanity_required.
334 fsnbt_name = 'FakeStrictNewBuildTask'
335 self.fake_config.add_section(fsnbt_name)
336 self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
337 self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
338 frnbt_name = 'FakeRelaxedNewBuildTask'
339 self.fake_config.add_section(frnbt_name)
340 self.fake_config.set(frnbt_name, 'run_on', 'new_build')
341 self.fake_config.set(frnbt_name, 'suite', 'fake_suite_relaxed')
342 self.fake_config.set(frnbt_name, 'only_hwtest_sanity_required', 'True')
343
344 self._mock_config_reader.return_value = self.fake_config
Craig Bergstrom58263d32018-04-26 14:11:35 -0600345
346 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600347 suite_trigger = trigger_receiver.TriggerReceiver()
348 suite_trigger.cron()
349
350 # Validate that the expected tests got kicked off.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600351 self.assertEqual(len(suite_trigger.event_results), 1)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600352 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000353 # 2 for strict passed builds, 3 for relaxed builds.
354 self.assertEqual(len(tasks), 5)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600355
356 # The number of builds that matched the base (success) Task.
357 count_base = 0
358 # The number of builds that matched the relaxed Task.
359 count_relaxed = 0
360 for task in tasks:
361 if 'fake_suite_base' in task.payload:
362 # Make sure it matched the expected build only.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600363 self.assertNotIn('grunt-release', task.payload)
364 count_base += 1
365
366 if 'fake_suite_relaxed' in task.payload:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600367 count_relaxed += 1
368
369 # Make each case matched precisely one event.
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000370 self.assertEqual(2, count_base)
371 self.assertEqual(3, count_relaxed)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600372
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700373 def testNewBuildWithExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700374 """Test the new_build suite with an existing board family."""
375 link_suite = 'FakeLinkNewBuildTask'
376 self.fake_config.add_section(link_suite)
377 self.fake_config.set(link_suite, 'run_on', 'new_build')
378 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700379 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700380 self._mock_config_reader.return_value = self.fake_config
381
382 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
383 suite_trigger = trigger_receiver.TriggerReceiver()
384 suite_trigger.cron()
385
386 self.assertEqual(len(suite_trigger.event_results), 1)
387 self.assertEqual(suite_trigger.event_results['new_build'],
388 [link_suite])
389 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
390 self.assertEqual(len(tasks), 1)
391 self.assertIn('link-release', tasks[0].payload)
392
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700393 def testNewBuildWithExistingBoardFamiliesAndBoards(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700394 """Test the new_build suite with an existing board family."""
395 link_suite = 'FakeLinkNewBuildTask'
396 self.fake_config.add_section(link_suite)
397 self.fake_config.set(link_suite, 'run_on', 'new_build')
398 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700399 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700400 self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
401 self._mock_config_reader.return_value = self.fake_config
402
403 suite_trigger = trigger_receiver.TriggerReceiver()
404 suite_trigger.cron()
405
406 self.assertEqual(len(suite_trigger.event_results), 1)
407 self.assertEqual(suite_trigger.event_results['new_build'],
408 [link_suite])
409 boards = suite_trigger.events['new_build'].task_list[0].boards
410 self.assertIn('asuka', boards)
411 self.assertIn('link', boards)
412
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700413 def testNewBuildWithNonExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700414 """Test the new_build suite with an non-existing board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700415 nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700416 self.fake_config.add_section(nyan_suite)
417 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
418 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700419 self.fake_config.set(nyan_suite, 'board_families', 'nyan')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700420 self._mock_config_reader.return_value = self.fake_config
421
422 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
423 suite_trigger = trigger_receiver.TriggerReceiver()
424 suite_trigger.cron()
425
426 self.assertEqual(len(suite_trigger.event_results), 1)
427 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
428 self.assertEqual(len(tasks), 0)
429
430 def testNewBuildWithNonSpecifiedBoardFamily(self):
431 """Test the new_build suite with an non-specified board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700432 normal_suite = 'FakeBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700433 self.fake_config.add_section(normal_suite)
434 self.fake_config.set(normal_suite, 'run_on', 'new_build')
435 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
436 self._mock_config_reader.return_value = self.fake_config
437
438 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
439 suite_trigger = trigger_receiver.TriggerReceiver()
440 suite_trigger.cron()
441
442 self.assertEqual(len(suite_trigger.event_results), 1)
443 self.assertEqual(suite_trigger.event_results['new_build'],
444 [normal_suite])
445 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000446 self.assertEqual(len(tasks), 2)
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700447
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700448 def testNewBuildExcludingExistingBoardFamilies(self):
449 """Test the new_build suite excluding an existing board family."""
450 link_suite = 'FakeLinkNewBuildTask'
451 self.fake_config.add_section(link_suite)
452 self.fake_config.set(link_suite, 'run_on', 'new_build')
453 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
454 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
455 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 [link_suite])
464 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
465 self.assertEqual(len(tasks), 1)
466 self.assertNotIn('link-release', tasks[0].payload)
467 self.assertIn('zako-release', tasks[0].payload)
468
469 def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
470 """Test the new_build suite with an existing board family."""
471 link_suite = 'FakeLinkNewBuildTask'
472 self.fake_config.add_section(link_suite)
473 self.fake_config.set(link_suite, 'run_on', 'new_build')
474 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
475 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000476 self.fake_config.set(link_suite, 'exclude_boards', 'asuka, paine, banon')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700477 self._mock_config_reader.return_value = self.fake_config
478
479 suite_trigger = trigger_receiver.TriggerReceiver()
480 suite_trigger.cron()
481
482 self.assertEqual(len(suite_trigger.event_results), 1)
483 self.assertEqual(suite_trigger.event_results['new_build'],
484 [link_suite])
485 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
486 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
487 self.assertNotIn('link-release', tasks[0].payload)
488 self.assertNotIn('asuka-release', tasks[0].payload)
489 self.assertIn('zako-release', tasks[0].payload)
490
491 def testNewBuildExcludingNonExistingBoardFamilies(self):
492 """Test the new_build suite excluding an non-existing board family."""
493 nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
494 self.fake_config.add_section(nyan_suite)
495 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
496 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
497 self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
498 self._mock_config_reader.return_value = self.fake_config
499
500 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
501 suite_trigger = trigger_receiver.TriggerReceiver()
502 suite_trigger.cron()
503
504 self.assertEqual(len(suite_trigger.event_results), 1)
505 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
506 self.assertEqual(len(tasks), 2)
507
508 def testNewBuildWithBoardExcludeBoardCollision(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700509 """Test the case that the same board in boards and exclude_boards."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700510 normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
511 self.fake_config.add_section(normal_suite)
512 self.fake_config.set(normal_suite, 'run_on', 'new_build')
513 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
514 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
515 self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
516 self._mock_config_reader.return_value = self.fake_config
517
518 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
519 suite_trigger = trigger_receiver.TriggerReceiver()
520 suite_trigger.cron()
521
522 self.assertEqual(len(suite_trigger.event_results), 1)
523 self.assertEqual(suite_trigger.event_results['new_build'],
524 [normal_suite])
525 # self.assertIn('new_build', suite_trigger.event_results)
526 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
527 self.assertEqual(len(tasks), 1)
528 self.assertNotIn('asuka-release', tasks[0].payload)
529 self.assertIn('zako-release', tasks[0].payload)
530
Xixuan Wu5ff0fac2019-01-07 10:06:35 -0800531 def testNewBuildWithModelsInSkylab(self):
532 """Test the new_build suite in skylab."""
533 normal_suite = 'FakeModelsInSkylab'
534 self.fake_config.add_section(normal_suite)
535 self.fake_config.set(normal_suite, 'run_on', 'new_build')
536 self.fake_config.set(normal_suite, 'suite', 'ent-nightly')
537 self.fake_config.set(normal_suite, 'exclude_boards', 'link, zako')
538 self._mock_config_reader.return_value = self.fake_config
539
540 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
541 suite_trigger = trigger_receiver.TriggerReceiver()
542 suite_trigger.cron()
543
544 self.assertEqual(len(suite_trigger.event_results), 1)
545 self.assertEqual(suite_trigger.event_results['new_build'],
546 [normal_suite])
547 self.assertIn('new_build', suite_trigger.event_results)
548 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
549 self.assertEqual(len(tasks), 2)
550 for t in tasks:
551 if 'model=santa' in t.payload:
552 self.assertIn('is_skylab=True', t.payload)
553
554 if 'model=astronaut' in t.payload:
555 self.assertIn('is_skylab=False', t.payload)
556
Craig Bergstrom58263d32018-04-26 14:11:35 -0600557
Xixuan Wu008ee832017-10-12 16:59:34 -0700558class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
559
560 def setUp(self):
561 super(TriggerReceiverRealConfigTestCase, self).setUp()
562 mock_config_reader = mock.patch('config_reader.ConfigReader')
563 self._mock_config_reader = mock_config_reader.start()
564 self.addCleanup(mock_config_reader.stop)
565 self._mock_config_reader.return_value = _SUITE_CONFIG_READER
566
567 def _get_ground_truth_task_list_from_config(self):
568 """Get the ground truth of to-be-scheduled task list from config file."""
569 self._mock_utc_now.return_value = datetime.datetime.now(
570 time_converter.UTC_TZ)
571 task_config = config_reader.TaskConfig(_SUITE_CONFIG_READER)
572 tasks = {}
573 for keyword, klass in config_reader.EVENT_CLASSES.iteritems():
574 new_event = klass(
575 task_config.get_event_setting(klass.section_name()), None)
576 new_event.set_task_list(
577 task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
578 tasks[keyword] = new_event.task_list
579
580 return tasks
581
Xixuan Wu40998892017-08-29 14:32:26 -0700582 def testCronWithoutLastExec(self):
583 """Test the first round of cron can be successfully executed."""
584 self._mock_utc_now.return_value = datetime.datetime.now(
585 time_converter.UTC_TZ)
586 suite_trigger = trigger_receiver.TriggerReceiver()
587 suite_trigger.cron()
588 self.assertFalse(suite_trigger.events['nightly'].should_handle)
589 self.assertFalse(suite_trigger.events['weekly'].should_handle)
590 self.assertFalse(suite_trigger.events['new_build'].should_handle)
591
592 self.assertEqual(suite_trigger.event_results, {})
593
594 def testCronTriggerNightly(self):
595 """Test nightly event is read with available nightly last_exec_time."""
596 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
597 last_exec_client = datastore_client.LastExecutionRecordStore()
598 last_exec_client.set_last_execute_time(
599 'nightly', utc_now - datetime.timedelta(hours=1))
600 self._mock_utc_now.return_value = utc_now
601 suite_trigger = trigger_receiver.TriggerReceiver()
602 self.assertTrue(suite_trigger.events['nightly'].should_handle)
603 self.assertFalse(suite_trigger.events['weekly'].should_handle)
604 self.assertFalse(suite_trigger.events['new_build'].should_handle)
605
Xixuan Wu33179672017-09-12 11:44:04 -0700606 def testCronTriggerNightlyOutdated(self):
607 """Test nightly event is read with available nightly last_exec_time."""
608 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
609 last_exec_client = datastore_client.LastExecutionRecordStore()
610 last_exec_client.set_last_execute_time(
611 'nightly', utc_now - datetime.timedelta(days=3))
612 self._mock_utc_now.return_value = utc_now
613 suite_trigger = trigger_receiver.TriggerReceiver()
614 self.assertFalse(suite_trigger.events['nightly'].should_handle)
615
616 def testCronTriggerWeeklyOutdated(self):
617 """Test weekly event is read with available weekly 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 'weekly', utc_now - datetime.timedelta(days=8))
622 self._mock_utc_now.return_value = utc_now
623 suite_trigger = trigger_receiver.TriggerReceiver()
624 self.assertFalse(suite_trigger.events['weekly'].should_handle)
625
Xixuan Wu40998892017-08-29 14:32:26 -0700626 def testCronForWeeks(self):
627 """Ensure cron job can be successfully scheduled for several weeks."""
Xixuan Wu008ee832017-10-12 16:59:34 -0700628 all_tasks = self._get_ground_truth_task_list_from_config()
Xixuan Wu40998892017-08-29 14:32:26 -0700629 last_now = None
630
631 for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ)):
632 self._mock_utc_now.return_value = now
633 suite_trigger = trigger_receiver.TriggerReceiver()
Xixuan Wu5451a662017-10-17 10:57:40 -0700634 with mock.patch('task.Task.schedule', return_value=True):
635 suite_trigger.cron()
Xixuan Wu40998892017-08-29 14:32:26 -0700636
Xixuan Wu40998892017-08-29 14:32:26 -0700637 should_scheduled_nightly_tasks = [
Xixuan Wu09962902018-12-11 10:49:27 -0800638 t.name for t in all_tasks['nightly'] if t.hour == now.hour]
Xixuan Wu008ee832017-10-12 16:59:34 -0700639
Xixuan Wu09962902018-12-11 10:49:27 -0800640 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700641 should_scheduled_nightly_tasks):
642 self.assertEqual(suite_trigger.event_results['nightly'],
643 should_scheduled_nightly_tasks)
644 else:
645 self.assertNotIn('nightly', suite_trigger.event_results.keys())
646
647 # Verify weekly tasks
648 should_scheduled_weekly_tasks = [
649 t.name for t in all_tasks['weekly']
Xixuan Wu09962902018-12-11 10:49:27 -0800650 if now.weekday() == t.day and now.hour == t.hour]
651 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700652 should_scheduled_weekly_tasks):
653 self.assertEqual(suite_trigger.event_results['weekly'],
654 should_scheduled_weekly_tasks)
655 else:
656 self.assertNotIn('weekly', suite_trigger.event_results.keys())
657
658 # Verify new_build tasks
659 should_scheduled_new_build_tasks = [
660 t.name for t in all_tasks['new_build']]
661 if (_should_schedule_new_build_task(last_now, now) and
662 should_scheduled_new_build_tasks):
663 self.assertEqual(suite_trigger.event_results['new_build'],
664 should_scheduled_new_build_tasks)
665 else:
666 self.assertNotIn('new_build', suite_trigger.event_results.keys())
667
668 last_now = now
669
670
671if __name__ == '__main__':
672 unittest.main()