• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2019 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unittests for running tests after updating packages."""
8
9from __future__ import print_function
10
11import json
12import subprocess
13import unittest
14import unittest.mock as mock
15
16import chroot
17import get_llvm_hash
18import git
19import test_helpers
20import update_chromeos_llvm_hash
21import update_packages_and_run_tests
22
23
24# Testing with tryjobs.
25class UpdatePackagesAndRunTryjobsTest(unittest.TestCase):
26  """Unittests when running tryjobs after updating packages."""
27
28  def testNoLastTestedFile(self):
29    self.assertEqual(
30        update_packages_and_run_tests.UnchangedSinceLastRun(None, {}), False)
31
32  def testEmptyLastTestedFile(self):
33    with test_helpers.CreateTemporaryFile() as temp_file:
34      self.assertEqual(
35          update_packages_and_run_tests.UnchangedSinceLastRun(temp_file, {}),
36          False)
37
38  def testLastTestedFileDoesNotExist(self):
39    # Simulate 'open()' on a lasted tested file that does not exist.
40    mock.mock_open(read_data='')
41
42    self.assertEqual(
43        update_packages_and_run_tests.UnchangedSinceLastRun(
44            '/some/file/that/does/not/exist.txt', {}), False)
45
46  def testMatchedLastTestedFile(self):
47    with test_helpers.CreateTemporaryFile() as last_tested_file:
48      arg_dict = {
49          'svn_version': 1234,
50          'ebuilds': [
51              '/path/to/package1-r2.ebuild',
52              '/path/to/package2/package2-r3.ebuild'
53          ],
54          'builders': [
55              'kevin-llvm-next-toolchain-tryjob',
56              'eve-llvm-next-toolchain-tryjob'
57          ],
58          'extra_cls': [10, 1],
59          'tryjob_options': ['latest-toolchain', 'hwtest']
60      }
61
62      with open(last_tested_file, 'w') as f:
63        f.write(json.dumps(arg_dict, indent=2))
64
65      self.assertEqual(
66          update_packages_and_run_tests.UnchangedSinceLastRun(
67              last_tested_file, arg_dict), True)
68
69  def testGetTryJobCommandWithNoExtraInformation(self):
70    change_list = 1234
71
72    builder = 'nocturne'
73
74    expected_cmd = [
75        'cros', 'tryjob', '--yes', '--json', '-g',
76        '%d' % change_list, builder
77    ]
78
79    self.assertEqual(
80        update_packages_and_run_tests.GetTryJobCommand(change_list, None, None,
81                                                       builder), expected_cmd)
82
83  def testGetTryJobCommandWithExtraInformation(self):
84    change_list = 4321
85    extra_cls = [1000, 10]
86    options = ['option1', 'option2']
87    builder = 'kevin'
88
89    expected_cmd = [
90        'cros',
91        'tryjob',
92        '--yes',
93        '--json',
94        '-g',
95        '%d' % change_list,
96        '-g',
97        '%d' % extra_cls[0],
98        '-g',
99        '%d' % extra_cls[1],
100        '--%s' % options[0],
101        '--%s' % options[1],
102        builder,
103    ]
104
105    self.assertEqual(
106        update_packages_and_run_tests.GetTryJobCommand(change_list, extra_cls,
107                                                       options, builder),
108        expected_cmd)
109
110  @mock.patch.object(
111      update_packages_and_run_tests,
112      'GetCurrentTimeInUTC',
113      return_value='2019-09-09')
114  @mock.patch.object(update_packages_and_run_tests, 'AddLinksToCL')
115  @mock.patch.object(subprocess, 'check_output')
116  def testSuccessfullySubmittedTryJob(self, mock_cmd, mock_add_links_to_cl,
117                                      mock_launch_time):
118
119    expected_cmd = [
120        'cros', 'tryjob', '--yes', '--json', '-g',
121        '%d' % 900, '-g',
122        '%d' % 1200, '--some_option', 'builder1'
123    ]
124
125    bb_id = '1234'
126    url = 'http://ci.chromium.org/b/%s' % bb_id
127
128    mock_cmd.return_value = json.dumps([{'id': bb_id, 'url': url}])
129
130    chroot_path = '/some/path/to/chroot'
131    cl = 900
132    extra_cls = [1200]
133    options = ['some_option']
134    builders = ['builder1']
135
136    tests = update_packages_and_run_tests.RunTryJobs(cl, extra_cls, options,
137                                                     builders, chroot_path)
138
139    expected_tests = [{
140        'launch_time': mock_launch_time.return_value,
141        'link': url,
142        'buildbucket_id': int(bb_id),
143        'extra_cls': extra_cls,
144        'options': options,
145        'builder': builders
146    }]
147
148    self.assertEqual(tests, expected_tests)
149
150    mock_cmd.assert_called_once_with(
151        expected_cmd, cwd=chroot_path, encoding='utf-8')
152
153    mock_add_links_to_cl.assert_called_once()
154
155  @mock.patch.object(update_packages_and_run_tests, 'AddLinksToCL')
156  @mock.patch.object(subprocess, 'check_output')
157  def testSuccessfullySubmittedRecipeBuilders(self, mock_cmd,
158                                              mock_add_links_to_cl):
159
160    expected_cmd = [
161        'bb', 'add', '-json', '-cl',
162        'crrev.com/c/%s' % 900, '-cl',
163        'crrev.com/c/%s' % 1200, 'some_option', 'builder1'
164    ]
165
166    bb_id = '1234'
167    create_time = '2020-04-18T00:03:53.978767Z'
168
169    mock_cmd.return_value = json.dumps({'id': bb_id, 'createTime': create_time})
170
171    chroot_path = '/some/path/to/chroot'
172    cl = 900
173    extra_cls = [1200]
174    options = ['some_option']
175    builders = ['builder1']
176
177    tests = update_packages_and_run_tests.StartRecipeBuilders(
178        cl, extra_cls, options, builders, chroot_path)
179
180    expected_tests = [{
181        'launch_time': create_time,
182        'link': 'http://ci.chromium.org/b/%s' % bb_id,
183        'buildbucket_id': bb_id,
184        'extra_cls': extra_cls,
185        'options': options,
186        'builder': builders
187    }]
188
189    self.assertEqual(tests, expected_tests)
190
191    mock_cmd.assert_called_once_with(
192        expected_cmd, cwd=chroot_path, encoding='utf-8')
193
194    mock_add_links_to_cl.assert_called_once()
195
196  @mock.patch.object(subprocess, 'check_output', return_value=None)
197  def testSuccessfullyAddedTestLinkToCL(self, mock_exec_cmd):
198    chroot_path = '/abs/path/to/chroot'
199
200    test_cl_number = 1000
201
202    tests = [{'link': 'https://some_tryjob_link.com'}]
203
204    update_packages_and_run_tests.AddLinksToCL(tests, test_cl_number,
205                                               chroot_path)
206
207    expected_gerrit_message = [
208        '%s/chromite/bin/gerrit' % chroot_path, 'message',
209        str(test_cl_number),
210        'Started the following tests:\n%s' % tests[0]['link']
211    ]
212
213    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
214
215  @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs')
216  @mock.patch.object(update_chromeos_llvm_hash, 'UpdatePackages')
217  @mock.patch.object(update_packages_and_run_tests, 'GetCommandLineArgs')
218  @mock.patch.object(get_llvm_hash, 'GetLLVMHashAndVersionFromSVNOption')
219  @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
220  @mock.patch.object(chroot, 'GetChrootEbuildPaths')
221  def testUpdatedLastTestedFileWithNewTestedRevision(
222      self, mock_get_chroot_build_paths, mock_outside_chroot,
223      mock_get_hash_and_version, mock_get_commandline_args,
224      mock_update_packages, mock_run_tryjobs):
225
226    # Create a temporary file to simulate the last tested file that contains a
227    # revision.
228    with test_helpers.CreateTemporaryFile() as last_tested_file:
229      builders = [
230          'kevin-llvm-next-toolchain-tryjob', 'eve-llvm-next-toolchain-tryjob'
231      ]
232      extra_cls = [10, 1]
233      tryjob_options = ['latest-toolchain', 'hwtest']
234      ebuilds = [
235          '/path/to/package1/package1-r2.ebuild',
236          '/path/to/package2/package2-r3.ebuild'
237      ]
238
239      arg_dict = {
240          'svn_version': 100,
241          'ebuilds': ebuilds,
242          'builders': builders,
243          'extra_cls': extra_cls,
244          'tryjob_options': tryjob_options
245      }
246      # Parepared last tested file
247      with open(last_tested_file, 'w') as f:
248        json.dump(arg_dict, f, indent=2)
249
250      # Call with a changed LLVM svn version
251      args_output = test_helpers.ArgsOutputTest()
252      args_output.is_llvm_next = True
253      args_output.extra_change_lists = extra_cls
254      args_output.last_tested = last_tested_file
255      args_output.reviewers = []
256
257      args_output.subparser_name = 'tryjobs'
258      args_output.builders = builders
259      args_output.options = tryjob_options
260
261      mock_get_commandline_args.return_value = args_output
262
263      mock_get_chroot_build_paths.return_value = ebuilds
264
265      mock_get_hash_and_version.return_value = ('a123testhash2', 200)
266
267      mock_update_packages.return_value = git.CommitContents(
268          url='https://some_cl_url.com', cl_number=12345)
269
270      mock_run_tryjobs.return_value = [{
271          'link': 'https://some_tryjob_url.com',
272          'buildbucket_id': 1234
273      }]
274
275      update_packages_and_run_tests.main()
276
277      # Verify that the lasted tested file has been updated to the new LLVM
278      # revision.
279      with open(last_tested_file) as f:
280        arg_dict = json.load(f)
281
282        self.assertEqual(arg_dict['svn_version'], 200)
283
284    mock_outside_chroot.assert_called_once()
285
286    mock_get_commandline_args.assert_called_once()
287
288    mock_get_hash_and_version.assert_called_once()
289
290    mock_run_tryjobs.assert_called_once()
291
292    mock_update_packages.assert_called_once()
293
294
295class UpdatePackagesAndRunTestCQTest(unittest.TestCase):
296  """Unittests for CQ dry run after updating packages."""
297
298  def testGetCQDependString(self):
299    test_no_changelists = []
300    test_single_changelist = [1234]
301    test_multiple_changelists = [1234, 5678]
302
303    self.assertIsNone(
304        update_packages_and_run_tests.GetCQDependString(test_no_changelists))
305
306    self.assertEqual(
307        update_packages_and_run_tests.GetCQDependString(test_single_changelist),
308        '\nCq-Depend: chromium:1234')
309
310    self.assertEqual(
311        update_packages_and_run_tests.GetCQDependString(
312            test_multiple_changelists),
313        '\nCq-Depend: chromium:1234, chromium:5678')
314
315  def testGetCQIncludeTrybotsString(self):
316    test_no_trybot = None
317    test_valid_trybot = 'llvm-next'
318    test_invalid_trybot = 'invalid-name'
319
320    self.assertIsNone(
321        update_packages_and_run_tests.GetCQIncludeTrybotsString(test_no_trybot))
322
323    self.assertEqual(
324        update_packages_and_run_tests.GetCQIncludeTrybotsString(
325            test_valid_trybot),
326        '\nCq-Include-Trybots:chromeos/cq:cq-llvm-next-orchestrator')
327
328    with self.assertRaises(ValueError) as context:
329      update_packages_and_run_tests.GetCQIncludeTrybotsString(
330          test_invalid_trybot)
331
332    self.assertIn('is not a valid llvm trybot', str(context.exception))
333
334  @mock.patch.object(subprocess, 'check_output', return_value=None)
335  def testStartCQDryRunNoDeps(self, mock_exec_cmd):
336    chroot_path = '/abs/path/to/chroot'
337    test_cl_number = 1000
338
339    # test with no deps cls.
340    extra_cls = []
341    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
342                                                chroot_path)
343
344    expected_gerrit_message = [
345        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
346        str(test_cl_number), '1'
347    ]
348
349    mock_exec_cmd.assert_called_once_with(expected_gerrit_message)
350
351  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
352  @mock.patch.object(subprocess, 'check_output', return_value=None)
353  # test with a single deps cl.
354  def testStartCQDryRunSingleDep(self, mock_exec_cmd):
355    chroot_path = '/abs/path/to/chroot'
356    test_cl_number = 1000
357
358    extra_cls = [2000]
359    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
360                                                chroot_path)
361
362    expected_gerrit_cmd_1 = [
363        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
364        str(test_cl_number), '1'
365    ]
366    expected_gerrit_cmd_2 = [
367        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
368        str(2000), '1'
369    ]
370
371    self.assertEqual(mock_exec_cmd.call_count, 2)
372    self.assertEqual(mock_exec_cmd.call_args_list[0],
373                     mock.call(expected_gerrit_cmd_1))
374    self.assertEqual(mock_exec_cmd.call_args_list[1],
375                     mock.call(expected_gerrit_cmd_2))
376
377  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
378  @mock.patch.object(subprocess, 'check_output', return_value=None)
379  def testStartCQDryRunMultipleDep(self, mock_exec_cmd):
380    chroot_path = '/abs/path/to/chroot'
381    test_cl_number = 1000
382
383    # test with multiple deps cls.
384    extra_cls = [3000, 4000]
385    update_packages_and_run_tests.StartCQDryRun(test_cl_number, extra_cls,
386                                                chroot_path)
387
388    expected_gerrit_cmd_1 = [
389        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
390        str(test_cl_number), '1'
391    ]
392    expected_gerrit_cmd_2 = [
393        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
394        str(3000), '1'
395    ]
396    expected_gerrit_cmd_3 = [
397        '%s/chromite/bin/gerrit' % chroot_path, 'label-cq',
398        str(4000), '1'
399    ]
400
401    self.assertEqual(mock_exec_cmd.call_count, 3)
402    self.assertEqual(mock_exec_cmd.call_args_list[0],
403                     mock.call(expected_gerrit_cmd_1))
404    self.assertEqual(mock_exec_cmd.call_args_list[1],
405                     mock.call(expected_gerrit_cmd_2))
406    self.assertEqual(mock_exec_cmd.call_args_list[2],
407                     mock.call(expected_gerrit_cmd_3))
408
409  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
410  @mock.patch.object(subprocess, 'check_output', return_value=None)
411  # test with no reviewers.
412  def testAddReviewersNone(self, mock_exec_cmd):
413    chroot_path = '/abs/path/to/chroot'
414    reviewers = []
415    test_cl_number = 1000
416
417    update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers,
418                                               chroot_path)
419    self.assertTrue(mock_exec_cmd.not_called)
420
421  # Mock ExecCommandAndCaptureOutput for the gerrit command execution.
422  @mock.patch.object(subprocess, 'check_output', return_value=None)
423  # test with multiple reviewers.
424  def testAddReviewersMultiple(self, mock_exec_cmd):
425    chroot_path = '/abs/path/to/chroot'
426    reviewers = ['none1@chromium.org', 'none2@chromium.org']
427    test_cl_number = 1000
428
429    update_packages_and_run_tests.AddReviewers(test_cl_number, reviewers,
430                                               chroot_path)
431
432    expected_gerrit_cmd_1 = [
433        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
434        str(test_cl_number), 'none1@chromium.org'
435    ]
436    expected_gerrit_cmd_2 = [
437        '%s/chromite/bin/gerrit' % chroot_path, 'reviewers',
438        str(test_cl_number), 'none2@chromium.org'
439    ]
440
441    self.assertEqual(mock_exec_cmd.call_count, 2)
442    self.assertEqual(mock_exec_cmd.call_args_list[0],
443                     mock.call(expected_gerrit_cmd_1))
444    self.assertEqual(mock_exec_cmd.call_args_list[1],
445                     mock.call(expected_gerrit_cmd_2))
446
447
448if __name__ == '__main__':
449  unittest.main()
450