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