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