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"""Tests for modifying a tryjob.""" 8 9from __future__ import print_function 10 11import json 12import unittest 13import unittest.mock as mock 14 15import get_llvm_hash 16import modify_a_tryjob 17import test_helpers 18import update_packages_and_run_tests 19import update_tryjob_status 20 21 22class ModifyATryjobTest(unittest.TestCase): 23 """Unittests for modifying a tryjob.""" 24 25 def testNoTryjobsInStatusFile(self): 26 bisect_test_contents = {'start': 369410, 'end': 369420, 'jobs': []} 27 28 # Create a temporary .JSON file to simulate a .JSON file that has bisection 29 # contents. 30 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 31 with open(temp_json_file, 'w') as f: 32 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 33 34 revision_to_modify = 369411 35 36 args_output = test_helpers.ArgsOutputTest() 37 args_output.builders = None 38 args_output.options = None 39 40 # Verify the exception is raised there are no tryjobs in the status file 41 # and the mode is not to 'add' a tryjob. 42 with self.assertRaises(SystemExit) as err: 43 modify_a_tryjob.PerformTryjobModification( 44 revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, 45 temp_json_file, args_output.extra_change_lists, args_output.options, 46 args_output.builders, args_output.chroot_path, args_output.verbose) 47 48 self.assertEqual(str(err.exception), 'No tryjobs in %s' % temp_json_file) 49 50 # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob 51 # was not found. 52 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) 53 def testNoTryjobIndexFound(self, mock_find_tryjob_index): 54 bisect_test_contents = { 55 'start': 369410, 56 'end': 369420, 57 'jobs': [{ 58 'rev': 369411, 59 'status': 'pending', 60 'buildbucket_id': 1200 61 }] 62 } 63 64 # Create a temporary .JSON file to simulate a .JSON file that has bisection 65 # contents. 66 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 67 with open(temp_json_file, 'w') as f: 68 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 69 70 revision_to_modify = 369412 71 72 args_output = test_helpers.ArgsOutputTest() 73 args_output.builders = None 74 args_output.options = None 75 76 # Verify the exception is raised when the index of the tryjob was not 77 # found in the status file and the mode is not to 'add' a tryjob. 78 with self.assertRaises(ValueError) as err: 79 modify_a_tryjob.PerformTryjobModification( 80 revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, 81 temp_json_file, args_output.extra_change_lists, args_output.options, 82 args_output.builders, args_output.chroot_path, args_output.verbose) 83 84 self.assertEqual( 85 str(err.exception), 'Unable to find tryjob for %d in %s' % 86 (revision_to_modify, temp_json_file)) 87 88 mock_find_tryjob_index.assert_called_once() 89 90 # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob 91 # was found. 92 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) 93 def testSuccessfullyRemovedTryjobInStatusFile(self, mock_find_tryjob_index): 94 bisect_test_contents = { 95 'start': 369410, 96 'end': 369420, 97 'jobs': [{ 98 'rev': 369414, 99 'status': 'pending', 100 'buildbucket_id': 1200 101 }] 102 } 103 104 # Create a temporary .JSON file to simulate a .JSON file that has bisection 105 # contents. 106 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 107 with open(temp_json_file, 'w') as f: 108 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 109 110 revision_to_modify = 369414 111 112 args_output = test_helpers.ArgsOutputTest() 113 args_output.builders = None 114 args_output.options = None 115 116 modify_a_tryjob.PerformTryjobModification( 117 revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, 118 temp_json_file, args_output.extra_change_lists, args_output.options, 119 args_output.builders, args_output.chroot_path, args_output.verbose) 120 121 # Verify that the tryjob was removed from the status file. 122 with open(temp_json_file) as status_file: 123 bisect_contents = json.load(status_file) 124 125 expected_file_contents = {'start': 369410, 'end': 369420, 'jobs': []} 126 127 self.assertDictEqual(bisect_contents, expected_file_contents) 128 129 mock_find_tryjob_index.assert_called_once() 130 131 # Simulate the behavior of `RunTryJobs()` when successfully submitted a 132 # tryjob. 133 @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs') 134 # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob 135 # was found. 136 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) 137 def testSuccessfullyRelaunchedTryjob(self, mock_find_tryjob_index, 138 mock_run_tryjob): 139 140 bisect_test_contents = { 141 'start': 142 369410, 143 'end': 144 369420, 145 'jobs': [{ 146 'rev': 369411, 147 'status': 'bad', 148 'link': 'https://some_tryjob_link.com', 149 'buildbucket_id': 1200, 150 'cl': 123, 151 'extra_cls': None, 152 'options': None, 153 'builder': ['some-builder-tryjob'] 154 }] 155 } 156 157 tryjob_result = [{ 158 'link': 'https://some_new_tryjob_link.com', 159 'buildbucket_id': 20 160 }] 161 162 mock_run_tryjob.return_value = tryjob_result 163 164 # Create a temporary .JSON file to simulate a .JSON file that has bisection 165 # contents. 166 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 167 with open(temp_json_file, 'w') as f: 168 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 169 170 revision_to_modify = 369411 171 172 args_output = test_helpers.ArgsOutputTest() 173 args_output.builders = None 174 args_output.options = None 175 176 modify_a_tryjob.PerformTryjobModification( 177 revision_to_modify, modify_a_tryjob.ModifyTryjob.RELAUNCH, 178 temp_json_file, args_output.extra_change_lists, args_output.options, 179 args_output.builders, args_output.chroot_path, args_output.verbose) 180 181 # Verify that the tryjob's information was updated after submtting the 182 # tryjob. 183 with open(temp_json_file) as status_file: 184 bisect_contents = json.load(status_file) 185 186 expected_file_contents = { 187 'start': 188 369410, 189 'end': 190 369420, 191 'jobs': [{ 192 'rev': 369411, 193 'status': 'pending', 194 'link': 'https://some_new_tryjob_link.com', 195 'buildbucket_id': 20, 196 'cl': 123, 197 'extra_cls': None, 198 'options': None, 199 'builder': ['some-builder-tryjob'] 200 }] 201 } 202 203 self.assertDictEqual(bisect_contents, expected_file_contents) 204 205 mock_find_tryjob_index.assert_called_once() 206 207 mock_run_tryjob.assert_called_once() 208 209 # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob 210 # was found. 211 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) 212 def testAddingTryjobThatAlreadyExists(self, mock_find_tryjob_index): 213 bisect_test_contents = { 214 'start': 369410, 215 'end': 369420, 216 'jobs': [{ 217 'rev': 369411, 218 'status': 'bad', 219 'builder': ['some-builder'] 220 }] 221 } 222 223 # Create a temporary .JSON file to simulate a .JSON file that has bisection 224 # contents. 225 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 226 with open(temp_json_file, 'w') as f: 227 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 228 229 revision_to_add = 369411 230 231 # Index of the tryjob in 'jobs' list. 232 tryjob_index = 0 233 234 args_output = test_helpers.ArgsOutputTest() 235 args_output.options = None 236 237 # Verify the exception is raised when the tryjob that is going to added 238 # already exists in the status file (found its index). 239 with self.assertRaises(ValueError) as err: 240 modify_a_tryjob.PerformTryjobModification( 241 revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, 242 args_output.extra_change_lists, args_output.options, 243 args_output.builders, args_output.chroot_path, args_output.verbose) 244 245 self.assertEqual( 246 str(err.exception), 'Tryjob already exists (index is %d) in %s.' % 247 (tryjob_index, temp_json_file)) 248 249 mock_find_tryjob_index.assert_called_once() 250 251 # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found. 252 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) 253 def testSuccessfullyDidNotAddTryjobOutsideOfBisectionBounds( 254 self, mock_find_tryjob_index): 255 256 bisect_test_contents = { 257 'start': 369410, 258 'end': 369420, 259 'jobs': [{ 260 'rev': 369411, 261 'status': 'bad' 262 }] 263 } 264 265 # Create a temporary .JSON file to simulate a .JSON file that has bisection 266 # contents. 267 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 268 with open(temp_json_file, 'w') as f: 269 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 270 271 # Add a revision that is outside of 'start' and 'end'. 272 revision_to_add = 369450 273 274 args_output = test_helpers.ArgsOutputTest() 275 args_output.options = None 276 277 # Verify the exception is raised when adding a tryjob that does not exist 278 # and is not within 'start' and 'end'. 279 with self.assertRaises(ValueError) as err: 280 modify_a_tryjob.PerformTryjobModification( 281 revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, 282 args_output.extra_change_lists, args_output.options, 283 args_output.builders, args_output.chroot_path, args_output.verbose) 284 285 self.assertEqual( 286 str(err.exception), 'Failed to add tryjob to %s' % temp_json_file) 287 288 mock_find_tryjob_index.assert_called_once() 289 290 # Simulate the behavior of `AddTryjob()` when successfully submitted the 291 # tryjob and constructed the tryjob information (a dictionary). 292 @mock.patch.object(modify_a_tryjob, 'AddTryjob') 293 # Simulate the behavior of `GetLLVMHashAndVersionFromSVNOption()` when 294 # successfully retrieved the git hash of the revision to launch a tryjob for. 295 @mock.patch.object( 296 get_llvm_hash, 297 'GetLLVMHashAndVersionFromSVNOption', 298 return_value=('a123testhash1', 369418)) 299 # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found. 300 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) 301 def testSuccessfullyAddedTryjob(self, mock_find_tryjob_index, 302 mock_get_llvm_hash, mock_add_tryjob): 303 304 bisect_test_contents = { 305 'start': 369410, 306 'end': 369420, 307 'jobs': [{ 308 'rev': 369411, 309 'status': 'bad' 310 }] 311 } 312 313 # Create a temporary .JSON file to simulate a .JSON file that has bisection 314 # contents. 315 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 316 with open(temp_json_file, 'w') as f: 317 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 318 319 # Add a revision that is outside of 'start' and 'end'. 320 revision_to_add = 369418 321 322 args_output = test_helpers.ArgsOutputTest() 323 args_output.options = None 324 325 new_tryjob_info = { 326 'rev': revision_to_add, 327 'status': 'pending', 328 'options': args_output.options, 329 'extra_cls': args_output.extra_change_lists, 330 'builder': args_output.builders 331 } 332 333 mock_add_tryjob.return_value = new_tryjob_info 334 335 modify_a_tryjob.PerformTryjobModification( 336 revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, 337 args_output.extra_change_lists, args_output.options, 338 args_output.builders, args_output.chroot_path, args_output.verbose) 339 340 # Verify that the tryjob was added to the status file. 341 with open(temp_json_file) as status_file: 342 bisect_contents = json.load(status_file) 343 344 expected_file_contents = { 345 'start': 369410, 346 'end': 369420, 347 'jobs': [{ 348 'rev': 369411, 349 'status': 'bad' 350 }, new_tryjob_info] 351 } 352 353 self.assertDictEqual(bisect_contents, expected_file_contents) 354 355 mock_find_tryjob_index.assert_called_once() 356 357 mock_get_llvm_hash.assert_called_once_with(revision_to_add) 358 359 mock_add_tryjob.assert_called_once() 360 361 # Simulate the behavior of `FindTryjobIndex()` when the tryjob was found. 362 @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) 363 def testModifyATryjobOptionDoesNotExist(self, mock_find_tryjob_index): 364 bisect_test_contents = { 365 'start': 369410, 366 'end': 369420, 367 'jobs': [{ 368 'rev': 369414, 369 'status': 'bad' 370 }] 371 } 372 373 # Create a temporary .JSON file to simulate a .JSON file that has bisection 374 # contents. 375 with test_helpers.CreateTemporaryJsonFile() as temp_json_file: 376 with open(temp_json_file, 'w') as f: 377 test_helpers.WritePrettyJsonFile(bisect_test_contents, f) 378 379 # Add a revision that is outside of 'start' and 'end'. 380 revision_to_modify = 369414 381 382 args_output = test_helpers.ArgsOutputTest() 383 args_output.builders = None 384 args_output.options = None 385 386 # Verify the exception is raised when the modify a tryjob option does not 387 # exist. 388 with self.assertRaises(ValueError) as err: 389 modify_a_tryjob.PerformTryjobModification( 390 revision_to_modify, 'remove_link', temp_json_file, 391 args_output.extra_change_lists, args_output.options, 392 args_output.builders, args_output.chroot_path, args_output.verbose) 393 394 self.assertEqual( 395 str(err.exception), 396 'Invalid "modify_tryjob" option provided: remove_link') 397 398 mock_find_tryjob_index.assert_called_once() 399 400 401if __name__ == '__main__': 402 unittest.main() 403