blob: 671917c1a5d8f0fcf40679ca9b0428bce1b027b4 [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',
Xixuan Wu3b9dfb42019-01-03 16:48:55 -080099 'zako-release'),
100 cloud_sql_client.BuildInfo('coral', 'santa', '62', '9868.0.0',
101 'coral-release'),
102 cloud_sql_client.BuildInfo('coral', 'astronaut', '62', '9868.0.0',
103 'coral-release'),
104 cloud_sql_client.BuildInfo('coral', 'lava', '62', '9868.0.0',
105 'coral-release')]
Xixuan Wu40998892017-08-29 14:32:26 -0700106
107 def get_latest_passed_builds(self, build_config):
108 """Mock cloud_sql_client.CIDBClient.get_latest_passed_builds."""
109 del build_config # unused
110 return cloud_sql_client.BuildInfo('link', '62', '9868.0.0', build_config)
111
Craig Bergstrom58263d32018-04-26 14:11:35 -0600112 def get_relaxed_pased_builds_since_date(self, since_date):
113 """Mock cloud_sql_client.CIDBClient.get_relaxed_pased_builds_since_date."""
114 del since_date # unused
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700115 return [cloud_sql_client.BuildInfo('grunt', None, '63', '9968.0.0',
Craig Bergstrom58263d32018-04-26 14:11:35 -0600116 'grunt-release')]
117
Xixuan Wu40998892017-08-29 14:32:26 -0700118
119class FakeAndroidBuildRestClient(object):
120 """Mock rest_client.AndroidBuildRestClient."""
121
122 def get_latest_build_id(self, branch, target):
123 """Mock rest_client.AndroidBuildRestClient.get_latest_build_id."""
124 del branch, target # unused
125 return '100'
126
127
128class FakeLabConfig(object):
129 """Mock rest_client.AndroidBuildRestClient."""
130
131 def get_android_board_list(self):
132 """Mock config_reader.LabConfig.get_android_board_list."""
133 return ('android-angler', 'android-bullhead')
134
Xixuan Wu6fb16272017-10-19 13:16:00 -0700135 def get_cros_board_list(self):
136 """Mock config_reader.LabConfig.get_android_board_list."""
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800137 return ('grunt', 'link', 'peppy', 'daisy', 'zako', 'coral', 'reef')
Xixuan Wu6fb16272017-10-19 13:16:00 -0700138
Xixuan Wubb74a372018-08-21 17:37:08 -0700139 def get_skylab_board_list(self):
140 """Mock config_reader.LabConfig.get_skylab_board_list."""
141 return set(['nyan_blaze'])
142
Xixuan Wu446b8ad2018-08-23 11:25:43 -0700143 def get_skylab_suite_list(self):
144 """Mock config_reader.LabConfig.get_skylab_suite_list."""
145 return set(['ent-nightly'])
146
Xixuan Wu40998892017-08-29 14:32:26 -0700147 def get_firmware_ro_build_list(self, release_board):
148 """Mock config_reader.LabConfig.get_firmware_ro_build_list."""
149 del release_board # unused
150 return 'firmware1,firmware2'
151
C Shapiro7f24a002017-12-05 14:25:09 -0700152 def get_cros_model_map(self):
153 """Mock config_reader.LabConfig.get_cros_model_map."""
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800154 return {'coral': ['santa', 'astronaut']}
C Shapiro7f24a002017-12-05 14:25:09 -0700155
Xixuan Wu40998892017-08-29 14:32:26 -0700156
Xixuan Wu008ee832017-10-12 16:59:34 -0700157class TriggerReceiverBaseTestCase(unittest.TestCase):
Xixuan Wu40998892017-08-29 14:32:26 -0700158
159 def setUp(self):
160 self.testbed = testbed.Testbed()
161 self.testbed.activate()
162 self.addCleanup(self.testbed.deactivate)
163
164 self.testbed.init_datastore_v3_stub()
165 self.testbed.init_memcache_stub()
166 ndb.get_context().clear_cache()
167 self.testbed.init_taskqueue_stub(
168 root_path=os.path.join(os.path.dirname(__file__)))
169 self.taskqueue_stub = self.testbed.get_stub(
170 testbed.TASKQUEUE_SERVICE_NAME)
171
172 mock_cidb_client = mock.patch('cloud_sql_client.CIDBClient')
173 self._mock_cidb_client = mock_cidb_client.start()
174 self.addCleanup(mock_cidb_client.stop)
175
176 mock_android_client = mock.patch('rest_client.AndroidBuildRestClient')
177 self._mock_android_client = mock_android_client.start()
178 self.addCleanup(mock_android_client.stop)
179
Xixuan Wu40998892017-08-29 14:32:26 -0700180 mock_lab_config = mock.patch('config_reader.LabConfig')
181 self._mock_lab_config = mock_lab_config.start()
182 self.addCleanup(mock_lab_config.stop)
183
184 mock_utc_now = mock.patch('time_converter.utc_now')
185 self._mock_utc_now = mock_utc_now.start()
186 self.addCleanup(mock_utc_now.stop)
187
188 self._mock_cidb_client.return_value = FakeCIDBClient()
189 self._mock_android_client.return_value = FakeAndroidBuildRestClient()
Xixuan Wu40998892017-08-29 14:32:26 -0700190 self._mock_lab_config.return_value = FakeLabConfig()
191
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700192 board_family_patcher = mock.patch(
193 'build_lib.get_board_family_mapping_from_gs')
194 board_family_getter = board_family_patcher.start()
195 board_family_getter.return_value = {
196 'nyan': ['nyan', 'nyan_blaze', 'nyan_big'],
197 'ivybridge': ['link', 'link_freon']}
198 self.addCleanup(board_family_patcher.stop)
199
Xixuan Wu008ee832017-10-12 16:59:34 -0700200
201class TriggerReceiverFakeConfigTestCase(TriggerReceiverBaseTestCase):
202
203 _TEST_PST_HOUR = 20
204 _TEST_PST_DAY = 4 # Friday
205 _TEST_EVENT_PST_HOUR = 13
206
207 _FAKE_NIGHTLY_TASK_NAME = 'fake_nightly_task'
208 _FAKE_WEEKLY_TASK_NAME = 'fake_weekly_task'
209
210 def setUp(self):
211 super(TriggerReceiverFakeConfigTestCase, self).setUp()
212
213 self.fake_config = config_reader.ConfigReader(None)
214 self._add_nightly_tasks(self.fake_config)
215 self._add_weekly_tasks(self.fake_config)
216
217 self.fake_config_with_settings = config_reader.ConfigReader(None)
218 self._add_weekly_tasks(self.fake_config_with_settings)
219 self._add_weekly_params(self.fake_config_with_settings)
220
221 mock_config_reader = mock.patch('config_reader.ConfigReader')
222 self._mock_config_reader = mock_config_reader.start()
223 self.addCleanup(mock_config_reader.stop)
224
225 def _add_nightly_tasks(self, fake_config):
226 fake_config.add_section(self._FAKE_NIGHTLY_TASK_NAME)
227 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'suite', 'fake_suite')
228 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'run_on', 'nightly')
229 fake_config.set(self._FAKE_NIGHTLY_TASK_NAME, 'hour',
230 str(self._TEST_PST_HOUR))
231
232 def _add_weekly_tasks(self, fake_config):
233 fake_config.add_section(self._FAKE_WEEKLY_TASK_NAME)
234 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'suite', 'fake_suite')
235 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'run_on', 'weekly')
236 fake_config.set(self._FAKE_WEEKLY_TASK_NAME, 'day', str(self._TEST_PST_DAY))
237
238 def _add_weekly_params(self, fake_config):
239 weekly_section_name = config_reader.EVENT_CLASSES['weekly'].section_name()
240 fake_config.add_section(weekly_section_name)
241 fake_config.set(weekly_section_name, 'hour', str(self._TEST_EVENT_PST_HOUR))
242
243 def testInitializeTriggerReceiverWithNightlyEvent(self):
244 """Test nightly event can be handled on right hour."""
245 # A task with hour=20 should be scheduled at 20:00 in PST everyday, which
246 # is 3:00/4:00 in UTC everyday.
247 self._mock_config_reader.return_value = self.fake_config
248 given_utc_hour = time_converter.convert_time_info(
249 time_converter.TimeInfo(None, self._TEST_PST_HOUR)).hour
250 utc_now = datetime.datetime(2017, 8, 6, given_utc_hour,
251 tzinfo=time_converter.UTC_TZ)
252 last_exec_client = datastore_client.LastExecutionRecordStore()
253 last_exec_client.set_last_execute_time(
254 'nightly', utc_now - datetime.timedelta(hours=1))
255 self._mock_utc_now.return_value = utc_now
256
257 suite_trigger = trigger_receiver.TriggerReceiver()
258 suite_trigger.cron()
259 self.assertTrue(suite_trigger.events['nightly'].should_handle)
260 self.assertEqual(len(suite_trigger.event_results['nightly']), 1)
261 self.assertEqual(suite_trigger.event_results['nightly'][0],
262 self._FAKE_NIGHTLY_TASK_NAME)
263
264 def testInitializeTriggerReceiverWithWeeklyEventWithoutEventHour(self):
265 """Test weekly event without event settings can be handled on right day."""
266 # A task with day=4 (Friday) and default event_hour (23) should be
267 # scheduled at Friday 23:00 in PST, which is Saturday 6:00 or 7:00 in UTC.
268 self._mock_config_reader.return_value = self.fake_config
269 given_utc_hour = time_converter.convert_time_info(
270 time_converter.TimeInfo(
271 self._TEST_PST_DAY,
272 config_reader.EVENT_CLASSES['weekly'].DEFAULT_PST_HOUR)).hour
273 utc_now = datetime.datetime(2017, 10, 14, given_utc_hour,
274 tzinfo=time_converter.UTC_TZ)
275 last_exec_client = datastore_client.LastExecutionRecordStore()
276 last_exec_client.set_last_execute_time(
277 'weekly', utc_now - datetime.timedelta(days=1))
278 self._mock_utc_now.return_value = utc_now
279
280 suite_trigger = trigger_receiver.TriggerReceiver()
281 suite_trigger.cron()
282 self.assertTrue(suite_trigger.events['weekly'].should_handle)
283 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
284 self.assertEqual(suite_trigger.event_results['weekly'][0],
285 self._FAKE_WEEKLY_TASK_NAME)
286
287 def testInitializeTriggerReceiverWithWeeklyEventWithEventHour(self):
288 """Test weekly event with event settings can be handled on right day."""
289 # A task with day=4 (Friday) and event_hour=13 should be scheduled at
290 # Friday 13:00 in PST, which is Friday 20:00 or 21:00 in UTC.
291 self._mock_config_reader.return_value = self.fake_config_with_settings
292 given_utc_time_info = time_converter.convert_time_info(
293 time_converter.TimeInfo(self._TEST_PST_DAY, self._TEST_EVENT_PST_HOUR))
294
295 # Set the current time as a Friday 20:00 or 21:00 in UTC.
296 utc_now = datetime.datetime(2017, 10, 13, given_utc_time_info.hour,
297 tzinfo=time_converter.UTC_TZ)
298 last_exec_client = datastore_client.LastExecutionRecordStore()
299 last_exec_client.set_last_execute_time(
300 'weekly', utc_now - datetime.timedelta(days=1))
301 self._mock_utc_now.return_value = utc_now
302
303 suite_trigger = trigger_receiver.TriggerReceiver()
304 suite_trigger.cron()
305 self.assertTrue(suite_trigger.events['weekly'].should_handle)
306 self.assertEqual(len(suite_trigger.event_results['weekly']), 1)
307 self.assertEqual(suite_trigger.event_results['weekly'][0],
308 self._FAKE_WEEKLY_TASK_NAME)
309
310
Craig Bergstrom58263d32018-04-26 14:11:35 -0600311class TriggerReceiverFakeBuildTestCase(TriggerReceiverBaseTestCase):
312 """Test the new_build functionality."""
313
Craig Bergstrom58263d32018-04-26 14:11:35 -0600314 def setUp(self):
315 """Set up for a test."""
316 super(TriggerReceiverFakeBuildTestCase, self).setUp()
317
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700318 self._prepare()
319
320 def _prepare(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -0600321 self.fake_config = config_reader.ConfigReader(None)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600322 mock_config_reader = mock.patch('config_reader.ConfigReader')
323 self._mock_config_reader = mock_config_reader.start()
324 self.addCleanup(mock_config_reader.stop)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600325
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700326 # Set last execution time for new_build events.
327 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600328 last_exec_client = datastore_client.LastExecutionRecordStore()
329 last_exec_client.set_last_execute_time(
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700330 'new_build', utc_now - datetime.timedelta(hours=1))
Craig Bergstrom58263d32018-04-26 14:11:35 -0600331 self._mock_utc_now.return_value = utc_now
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700332
333 def testNewBuildForHWTestSanityRequired(self):
334 """Test the new_build functionality."""
335 # Construct a fake config with only_hwtest_sanity_required.
336 fsnbt_name = 'FakeStrictNewBuildTask'
337 self.fake_config.add_section(fsnbt_name)
338 self.fake_config.set(fsnbt_name, 'run_on', 'new_build')
339 self.fake_config.set(fsnbt_name, 'suite', 'fake_suite_base')
340 frnbt_name = 'FakeRelaxedNewBuildTask'
341 self.fake_config.add_section(frnbt_name)
342 self.fake_config.set(frnbt_name, 'run_on', 'new_build')
343 self.fake_config.set(frnbt_name, 'suite', 'fake_suite_relaxed')
344 self.fake_config.set(frnbt_name, 'only_hwtest_sanity_required', 'True')
345
346 self._mock_config_reader.return_value = self.fake_config
Craig Bergstrom58263d32018-04-26 14:11:35 -0600347
348 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600349 suite_trigger = trigger_receiver.TriggerReceiver()
350 suite_trigger.cron()
351
352 # Validate that the expected tests got kicked off.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600353 self.assertEqual(len(suite_trigger.event_results), 1)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600354 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800355 # 4 for strict passed builds (See get_passed_builds_since_date()
356 # in FakeCIDBClient).
357 # 5 for relaxed builds (Plus one build in
358 # get_relaxed_pased_builds_since_date() in FakeCIDBClient).
359 self.assertEqual(len(tasks), 9)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600360
361 # The number of builds that matched the base (success) Task.
362 count_base = 0
363 # The number of builds that matched the relaxed Task.
364 count_relaxed = 0
365 for task in tasks:
366 if 'fake_suite_base' in task.payload:
367 # Make sure it matched the expected build only.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600368 self.assertNotIn('grunt-release', task.payload)
369 count_base += 1
370
371 if 'fake_suite_relaxed' in task.payload:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600372 count_relaxed += 1
373
374 # Make each case matched precisely one event.
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800375 self.assertEqual(4, count_base)
376 self.assertEqual(5, count_relaxed)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600377
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700378 def testNewBuildWithExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700379 """Test the new_build suite with an existing board family."""
380 link_suite = 'FakeLinkNewBuildTask'
381 self.fake_config.add_section(link_suite)
382 self.fake_config.set(link_suite, 'run_on', 'new_build')
383 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700384 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700385 self._mock_config_reader.return_value = self.fake_config
386
387 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
388 suite_trigger = trigger_receiver.TriggerReceiver()
389 suite_trigger.cron()
390
391 self.assertEqual(len(suite_trigger.event_results), 1)
392 self.assertEqual(suite_trigger.event_results['new_build'],
393 [link_suite])
394 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
395 self.assertEqual(len(tasks), 1)
396 self.assertIn('link-release', tasks[0].payload)
397
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700398 def testNewBuildWithExistingBoardFamiliesAndBoards(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700399 """Test the new_build suite with an existing board family."""
400 link_suite = 'FakeLinkNewBuildTask'
401 self.fake_config.add_section(link_suite)
402 self.fake_config.set(link_suite, 'run_on', 'new_build')
403 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700404 self.fake_config.set(link_suite, 'board_families', 'ivybridge')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700405 self.fake_config.set(link_suite, 'boards', 'asuka, paine, banon')
406 self._mock_config_reader.return_value = self.fake_config
407
408 suite_trigger = trigger_receiver.TriggerReceiver()
409 suite_trigger.cron()
410
411 self.assertEqual(len(suite_trigger.event_results), 1)
412 self.assertEqual(suite_trigger.event_results['new_build'],
413 [link_suite])
414 boards = suite_trigger.events['new_build'].task_list[0].boards
415 self.assertIn('asuka', boards)
416 self.assertIn('link', boards)
417
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700418 def testNewBuildWithNonExistingBoardFamilies(self):
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700419 """Test the new_build suite with an non-existing board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700420 nyan_suite = 'FakeNonExistBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700421 self.fake_config.add_section(nyan_suite)
422 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
423 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700424 self.fake_config.set(nyan_suite, 'board_families', 'nyan')
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700425 self._mock_config_reader.return_value = self.fake_config
426
427 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
428 suite_trigger = trigger_receiver.TriggerReceiver()
429 suite_trigger.cron()
430
431 self.assertEqual(len(suite_trigger.event_results), 1)
432 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
433 self.assertEqual(len(tasks), 0)
434
435 def testNewBuildWithNonSpecifiedBoardFamily(self):
436 """Test the new_build suite with an non-specified board family."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700437 normal_suite = 'FakeBoardFamiliesNewBuildTask'
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700438 self.fake_config.add_section(normal_suite)
439 self.fake_config.set(normal_suite, 'run_on', 'new_build')
440 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
441 self._mock_config_reader.return_value = self.fake_config
442
443 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
444 suite_trigger = trigger_receiver.TriggerReceiver()
445 suite_trigger.cron()
446
447 self.assertEqual(len(suite_trigger.event_results), 1)
448 self.assertEqual(suite_trigger.event_results['new_build'],
449 [normal_suite])
450 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800451 self.assertEqual(len(tasks), 4)
Xixuan Wu244e0ec2018-05-23 14:49:55 -0700452
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700453 def testNewBuildExcludingExistingBoardFamilies(self):
454 """Test the new_build suite excluding an existing board family."""
455 link_suite = 'FakeLinkNewBuildTask'
456 self.fake_config.add_section(link_suite)
457 self.fake_config.set(link_suite, 'run_on', 'new_build')
458 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
459 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800460 self.fake_config.set(link_suite, 'exclude_boards', 'coral')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700461 self._mock_config_reader.return_value = self.fake_config
462
463 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
464 suite_trigger = trigger_receiver.TriggerReceiver()
465 suite_trigger.cron()
466
467 self.assertEqual(len(suite_trigger.event_results), 1)
468 self.assertEqual(suite_trigger.event_results['new_build'],
469 [link_suite])
470 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
471 self.assertEqual(len(tasks), 1)
472 self.assertNotIn('link-release', tasks[0].payload)
473 self.assertIn('zako-release', tasks[0].payload)
474
475 def testNewBuildExcludingExistingBoardFamiliesAndBoards(self):
476 """Test the new_build suite with an existing board family."""
477 link_suite = 'FakeLinkNewBuildTask'
478 self.fake_config.add_section(link_suite)
479 self.fake_config.set(link_suite, 'run_on', 'new_build')
480 self.fake_config.set(link_suite, 'suite', 'fake_suite_base')
481 self.fake_config.set(link_suite, 'exclude_board_families', 'ivybridge')
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800482 self.fake_config.set(link_suite, 'exclude_boards',
483 'asuka, paine, banon, coral')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700484 self._mock_config_reader.return_value = self.fake_config
485
486 suite_trigger = trigger_receiver.TriggerReceiver()
487 suite_trigger.cron()
488
489 self.assertEqual(len(suite_trigger.event_results), 1)
490 self.assertEqual(suite_trigger.event_results['new_build'],
491 [link_suite])
492 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
493 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
494 self.assertNotIn('link-release', tasks[0].payload)
495 self.assertNotIn('asuka-release', tasks[0].payload)
496 self.assertIn('zako-release', tasks[0].payload)
497
498 def testNewBuildExcludingNonExistingBoardFamilies(self):
499 """Test the new_build suite excluding an non-existing board family."""
500 nyan_suite = 'FakeNonExistExcludeBoardFamiliesNewBuildTask'
501 self.fake_config.add_section(nyan_suite)
502 self.fake_config.set(nyan_suite, 'run_on', 'new_build')
503 self.fake_config.set(nyan_suite, 'suite', 'fake_suite_base')
504 self.fake_config.set(nyan_suite, 'exclude_board_families', 'nyan')
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800505 self.fake_config.set(nyan_suite, 'exclude_boards', 'coral')
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700506 self._mock_config_reader.return_value = self.fake_config
507
508 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
509 suite_trigger = trigger_receiver.TriggerReceiver()
510 suite_trigger.cron()
511
512 self.assertEqual(len(suite_trigger.event_results), 1)
513 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
514 self.assertEqual(len(tasks), 2)
515
516 def testNewBuildWithBoardExcludeBoardCollision(self):
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700517 """Test the case that the same board in boards and exclude_boards."""
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700518 normal_suite = 'FakeBoardExludingBoardCollisionNewBuildTask'
519 self.fake_config.add_section(normal_suite)
520 self.fake_config.set(normal_suite, 'run_on', 'new_build')
521 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
522 self.fake_config.set(normal_suite, 'boards', 'zako, asuka')
523 self.fake_config.set(normal_suite, 'exclude_boards', 'asuka')
524 self._mock_config_reader.return_value = self.fake_config
525
526 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
527 suite_trigger = trigger_receiver.TriggerReceiver()
528 suite_trigger.cron()
529
530 self.assertEqual(len(suite_trigger.event_results), 1)
531 self.assertEqual(suite_trigger.event_results['new_build'],
532 [normal_suite])
533 # self.assertIn('new_build', suite_trigger.event_results)
534 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
535 self.assertEqual(len(tasks), 1)
536 self.assertNotIn('asuka-release', tasks[0].payload)
537 self.assertIn('zako-release', tasks[0].payload)
538
Xixuan Wu3b9dfb42019-01-03 16:48:55 -0800539 def testNewBuildWithModels(self):
540 """Test the new_build suite with an existing models entry."""
541 normal_suite = 'FakeModelsNewBuildTask'
542 self.fake_config.add_section(normal_suite)
543 self.fake_config.set(normal_suite, 'run_on', 'new_build')
544 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
545 self.fake_config.set(normal_suite, 'models', 'coral_lava, coral_santa')
546 self.fake_config.set(normal_suite, 'exclude_boards', 'link, zako')
547 self._mock_config_reader.return_value = self.fake_config
548
549 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
550 suite_trigger = trigger_receiver.TriggerReceiver()
551 suite_trigger.cron()
552
553 self.assertEqual(len(suite_trigger.event_results), 1)
554 self.assertEqual(suite_trigger.event_results['new_build'],
555 [normal_suite])
556 self.assertIn('new_build', suite_trigger.event_results)
557 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
558 self.assertEqual(len(tasks), 1)
559 self.assertNotIn('model=astronaut', tasks[0].payload)
560 self.assertIn('model=santa', tasks[0].payload)
561
562 def testNewBuildWithExcludeModels(self):
563 """Test the new_build suite with an existing exclude_models entry."""
564 normal_suite = 'FakeExludingModelsNewBuildTask'
565 self.fake_config.add_section(normal_suite)
566 self.fake_config.set(normal_suite, 'run_on', 'new_build')
567 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
568 self.fake_config.set(normal_suite, 'exclude_models',
569 'coral_lava, coral_santa')
570 self.fake_config.set(normal_suite, 'exclude_boards', 'link, zako')
571 self._mock_config_reader.return_value = self.fake_config
572
573 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
574 suite_trigger = trigger_receiver.TriggerReceiver()
575 suite_trigger.cron()
576
577 self.assertEqual(len(suite_trigger.event_results), 1)
578 self.assertEqual(suite_trigger.event_results['new_build'],
579 [normal_suite])
580 self.assertIn('new_build', suite_trigger.event_results)
581 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
582 self.assertEqual(len(tasks), 1)
583 self.assertNotIn('model=santa', tasks[0].payload)
584 self.assertIn('model=astronaut', tasks[0].payload)
585
586 def testNewBuildWithModelsExcludeModels(self):
587 """Test the new_build suite with models and exclude_models entry."""
588 normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
589 self.fake_config.add_section(normal_suite)
590 self.fake_config.set(normal_suite, 'run_on', 'new_build')
591 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
592 self.fake_config.set(normal_suite, 'models', 'coral_santa, coral_astronaut')
593 self.fake_config.set(normal_suite, 'exclude_models',
594 'coral_lava, coral_santa')
595 self.fake_config.set(normal_suite, 'exclude_boards', 'link, zako')
596 self._mock_config_reader.return_value = self.fake_config
597
598 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
599 suite_trigger = trigger_receiver.TriggerReceiver()
600 suite_trigger.cron()
601
602 self.assertEqual(len(suite_trigger.event_results), 1)
603 self.assertEqual(suite_trigger.event_results['new_build'],
604 [normal_suite])
605 self.assertIn('new_build', suite_trigger.event_results)
606 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
607 self.assertEqual(len(tasks), 1)
608 self.assertNotIn('model=santa', tasks[0].payload)
609 self.assertIn('model=astronaut', tasks[0].payload)
610
611 def testNewBuildWithNoModelList(self):
612 """Test the new_build suite with models and empty board_model mapping."""
613 normal_suite = 'FakeModelsExcludeModelsNewBuildTask'
614 self.fake_config.add_section(normal_suite)
615 self.fake_config.set(normal_suite, 'run_on', 'new_build')
616 self.fake_config.set(normal_suite, 'suite', 'fake_suite_base')
617 self.fake_config.set(normal_suite, 'exclude_boards', 'link, zako')
618 self._mock_config_reader.return_value = self.fake_config
619
620 queue = taskqueue.Queue(task_executor.SUITES_QUEUE)
621 suite_trigger = trigger_receiver.TriggerReceiver()
622 suite_trigger.cron()
623
624 self.assertIn('new_build', suite_trigger.event_results)
625 tasks = queue.lease_tasks(3600, 10, deadline=0.5)
626 # Verify that coral-release is not kicked off on model lava as lava is not
627 # listed in cros_model_map.
628 self.assertEqual(len(tasks), 2)
629 self.assertNotIn('lava', tasks[0].payload)
630 self.assertNotIn('lava', tasks[1].payload)
631
Craig Bergstrom58263d32018-04-26 14:11:35 -0600632
Xixuan Wu008ee832017-10-12 16:59:34 -0700633class TriggerReceiverRealConfigTestCase(TriggerReceiverBaseTestCase):
634
635 def setUp(self):
636 super(TriggerReceiverRealConfigTestCase, self).setUp()
637 mock_config_reader = mock.patch('config_reader.ConfigReader')
638 self._mock_config_reader = mock_config_reader.start()
639 self.addCleanup(mock_config_reader.stop)
640 self._mock_config_reader.return_value = _SUITE_CONFIG_READER
641
642 def _get_ground_truth_task_list_from_config(self):
643 """Get the ground truth of to-be-scheduled task list from config file."""
644 self._mock_utc_now.return_value = datetime.datetime.now(
645 time_converter.UTC_TZ)
646 task_config = config_reader.TaskConfig(_SUITE_CONFIG_READER)
647 tasks = {}
648 for keyword, klass in config_reader.EVENT_CLASSES.iteritems():
649 new_event = klass(
650 task_config.get_event_setting(klass.section_name()), None)
651 new_event.set_task_list(
652 task_config.get_tasks_by_keyword(klass.KEYWORD)['tasks'])
653 tasks[keyword] = new_event.task_list
654
655 return tasks
656
Xixuan Wu40998892017-08-29 14:32:26 -0700657 def testCronWithoutLastExec(self):
658 """Test the first round of cron can be successfully executed."""
659 self._mock_utc_now.return_value = datetime.datetime.now(
660 time_converter.UTC_TZ)
661 suite_trigger = trigger_receiver.TriggerReceiver()
662 suite_trigger.cron()
663 self.assertFalse(suite_trigger.events['nightly'].should_handle)
664 self.assertFalse(suite_trigger.events['weekly'].should_handle)
665 self.assertFalse(suite_trigger.events['new_build'].should_handle)
666
667 self.assertEqual(suite_trigger.event_results, {})
668
669 def testCronTriggerNightly(self):
670 """Test nightly event is read with available nightly last_exec_time."""
671 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
672 last_exec_client = datastore_client.LastExecutionRecordStore()
673 last_exec_client.set_last_execute_time(
674 'nightly', utc_now - datetime.timedelta(hours=1))
675 self._mock_utc_now.return_value = utc_now
676 suite_trigger = trigger_receiver.TriggerReceiver()
677 self.assertTrue(suite_trigger.events['nightly'].should_handle)
678 self.assertFalse(suite_trigger.events['weekly'].should_handle)
679 self.assertFalse(suite_trigger.events['new_build'].should_handle)
680
Xixuan Wu33179672017-09-12 11:44:04 -0700681 def testCronTriggerNightlyOutdated(self):
682 """Test nightly event is read with available nightly last_exec_time."""
683 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
684 last_exec_client = datastore_client.LastExecutionRecordStore()
685 last_exec_client.set_last_execute_time(
686 'nightly', utc_now - datetime.timedelta(days=3))
687 self._mock_utc_now.return_value = utc_now
688 suite_trigger = trigger_receiver.TriggerReceiver()
689 self.assertFalse(suite_trigger.events['nightly'].should_handle)
690
691 def testCronTriggerWeeklyOutdated(self):
692 """Test weekly event is read with available weekly last_exec_time."""
693 utc_now = datetime.datetime.now(time_converter.UTC_TZ)
694 last_exec_client = datastore_client.LastExecutionRecordStore()
695 last_exec_client.set_last_execute_time(
696 'weekly', utc_now - datetime.timedelta(days=8))
697 self._mock_utc_now.return_value = utc_now
698 suite_trigger = trigger_receiver.TriggerReceiver()
699 self.assertFalse(suite_trigger.events['weekly'].should_handle)
700
Xixuan Wu40998892017-08-29 14:32:26 -0700701 def testCronForWeeks(self):
702 """Ensure cron job can be successfully scheduled for several weeks."""
Xixuan Wu008ee832017-10-12 16:59:34 -0700703 all_tasks = self._get_ground_truth_task_list_from_config()
Xixuan Wu40998892017-08-29 14:32:26 -0700704 last_now = None
705
706 for now in now_generator(datetime.datetime.now(time_converter.UTC_TZ)):
707 self._mock_utc_now.return_value = now
708 suite_trigger = trigger_receiver.TriggerReceiver()
Xixuan Wu5451a662017-10-17 10:57:40 -0700709 with mock.patch('task.Task.schedule', return_value=True):
710 suite_trigger.cron()
Xixuan Wu40998892017-08-29 14:32:26 -0700711
Xixuan Wu40998892017-08-29 14:32:26 -0700712 should_scheduled_nightly_tasks = [
Xixuan Wu09962902018-12-11 10:49:27 -0800713 t.name for t in all_tasks['nightly'] if t.hour == now.hour]
Xixuan Wu008ee832017-10-12 16:59:34 -0700714
Xixuan Wu09962902018-12-11 10:49:27 -0800715 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700716 should_scheduled_nightly_tasks):
717 self.assertEqual(suite_trigger.event_results['nightly'],
718 should_scheduled_nightly_tasks)
719 else:
720 self.assertNotIn('nightly', suite_trigger.event_results.keys())
721
722 # Verify weekly tasks
723 should_scheduled_weekly_tasks = [
724 t.name for t in all_tasks['weekly']
Xixuan Wu09962902018-12-11 10:49:27 -0800725 if now.weekday() == t.day and now.hour == t.hour]
726 if (_should_schedule_timed_task(last_now, now) and
Xixuan Wu40998892017-08-29 14:32:26 -0700727 should_scheduled_weekly_tasks):
728 self.assertEqual(suite_trigger.event_results['weekly'],
729 should_scheduled_weekly_tasks)
730 else:
731 self.assertNotIn('weekly', suite_trigger.event_results.keys())
732
733 # Verify new_build tasks
734 should_scheduled_new_build_tasks = [
735 t.name for t in all_tasks['new_build']]
736 if (_should_schedule_new_build_task(last_now, now) and
737 should_scheduled_new_build_tasks):
738 self.assertEqual(suite_trigger.event_results['new_build'],
739 should_scheduled_new_build_tasks)
740 else:
741 self.assertNotIn('new_build', suite_trigger.event_results.keys())
742
743 last_now = now
744
745
746if __name__ == '__main__':
747 unittest.main()