#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2019 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Tests for modifying a tryjob.""" from __future__ import print_function import json import unittest import unittest.mock as mock import get_llvm_hash import modify_a_tryjob import test_helpers import update_packages_and_run_tests import update_tryjob_status class ModifyATryjobTest(unittest.TestCase): """Unittests for modifying a tryjob.""" def testNoTryjobsInStatusFile(self): bisect_test_contents = {'start': 369410, 'end': 369420, 'jobs': []} # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) revision_to_modify = 369411 args_output = test_helpers.ArgsOutputTest() args_output.builders = None args_output.options = None # Verify the exception is raised there are no tryjobs in the status file # and the mode is not to 'add' a tryjob. with self.assertRaises(SystemExit) as err: modify_a_tryjob.PerformTryjobModification( revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) self.assertEqual(str(err.exception), 'No tryjobs in %s' % temp_json_file) # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob # was not found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) def testNoTryjobIndexFound(self, mock_find_tryjob_index): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'pending', 'buildbucket_id': 1200 }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) revision_to_modify = 369412 args_output = test_helpers.ArgsOutputTest() args_output.builders = None args_output.options = None # Verify the exception is raised when the index of the tryjob was not # found in the status file and the mode is not to 'add' a tryjob. with self.assertRaises(ValueError) as err: modify_a_tryjob.PerformTryjobModification( revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) self.assertEqual( str(err.exception), 'Unable to find tryjob for %d in %s' % (revision_to_modify, temp_json_file)) mock_find_tryjob_index.assert_called_once() # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob # was found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) def testSuccessfullyRemovedTryjobInStatusFile(self, mock_find_tryjob_index): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369414, 'status': 'pending', 'buildbucket_id': 1200 }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) revision_to_modify = 369414 args_output = test_helpers.ArgsOutputTest() args_output.builders = None args_output.options = None modify_a_tryjob.PerformTryjobModification( revision_to_modify, modify_a_tryjob.ModifyTryjob.REMOVE, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) # Verify that the tryjob was removed from the status file. with open(temp_json_file) as status_file: bisect_contents = json.load(status_file) expected_file_contents = {'start': 369410, 'end': 369420, 'jobs': []} self.assertDictEqual(bisect_contents, expected_file_contents) mock_find_tryjob_index.assert_called_once() # Simulate the behavior of `RunTryJobs()` when successfully submitted a # tryjob. @mock.patch.object(update_packages_and_run_tests, 'RunTryJobs') # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob # was found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) def testSuccessfullyRelaunchedTryjob(self, mock_find_tryjob_index, mock_run_tryjob): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'bad', 'link': 'https://some_tryjob_link.com', 'buildbucket_id': 1200, 'cl': 123, 'extra_cls': None, 'options': None, 'builder': ['some-builder-tryjob'] }] } tryjob_result = [{ 'link': 'https://some_new_tryjob_link.com', 'buildbucket_id': 20 }] mock_run_tryjob.return_value = tryjob_result # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) revision_to_modify = 369411 args_output = test_helpers.ArgsOutputTest() args_output.builders = None args_output.options = None modify_a_tryjob.PerformTryjobModification( revision_to_modify, modify_a_tryjob.ModifyTryjob.RELAUNCH, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) # Verify that the tryjob's information was updated after submtting the # tryjob. with open(temp_json_file) as status_file: bisect_contents = json.load(status_file) expected_file_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'pending', 'link': 'https://some_new_tryjob_link.com', 'buildbucket_id': 20, 'cl': 123, 'extra_cls': None, 'options': None, 'builder': ['some-builder-tryjob'] }] } self.assertDictEqual(bisect_contents, expected_file_contents) mock_find_tryjob_index.assert_called_once() mock_run_tryjob.assert_called_once() # Simulate the behavior of `FindTryjobIndex()` when the index of the tryjob # was found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) def testAddingTryjobThatAlreadyExists(self, mock_find_tryjob_index): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'bad', 'builder': ['some-builder'] }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) revision_to_add = 369411 # Index of the tryjob in 'jobs' list. tryjob_index = 0 args_output = test_helpers.ArgsOutputTest() args_output.options = None # Verify the exception is raised when the tryjob that is going to added # already exists in the status file (found its index). with self.assertRaises(ValueError) as err: modify_a_tryjob.PerformTryjobModification( revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) self.assertEqual( str(err.exception), 'Tryjob already exists (index is %d) in %s.' % (tryjob_index, temp_json_file)) mock_find_tryjob_index.assert_called_once() # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) def testSuccessfullyDidNotAddTryjobOutsideOfBisectionBounds( self, mock_find_tryjob_index): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'bad' }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) # Add a revision that is outside of 'start' and 'end'. revision_to_add = 369450 args_output = test_helpers.ArgsOutputTest() args_output.options = None # Verify the exception is raised when adding a tryjob that does not exist # and is not within 'start' and 'end'. with self.assertRaises(ValueError) as err: modify_a_tryjob.PerformTryjobModification( revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) self.assertEqual( str(err.exception), 'Failed to add tryjob to %s' % temp_json_file) mock_find_tryjob_index.assert_called_once() # Simulate the behavior of `AddTryjob()` when successfully submitted the # tryjob and constructed the tryjob information (a dictionary). @mock.patch.object(modify_a_tryjob, 'AddTryjob') # Simulate the behavior of `GetLLVMHashAndVersionFromSVNOption()` when # successfully retrieved the git hash of the revision to launch a tryjob for. @mock.patch.object( get_llvm_hash, 'GetLLVMHashAndVersionFromSVNOption', return_value=('a123testhash1', 369418)) # Simulate the behavior of `FindTryjobIndex()` when the tryjob was not found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=None) def testSuccessfullyAddedTryjob(self, mock_find_tryjob_index, mock_get_llvm_hash, mock_add_tryjob): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'bad' }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) # Add a revision that is outside of 'start' and 'end'. revision_to_add = 369418 args_output = test_helpers.ArgsOutputTest() args_output.options = None new_tryjob_info = { 'rev': revision_to_add, 'status': 'pending', 'options': args_output.options, 'extra_cls': args_output.extra_change_lists, 'builder': args_output.builders } mock_add_tryjob.return_value = new_tryjob_info modify_a_tryjob.PerformTryjobModification( revision_to_add, modify_a_tryjob.ModifyTryjob.ADD, temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) # Verify that the tryjob was added to the status file. with open(temp_json_file) as status_file: bisect_contents = json.load(status_file) expected_file_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369411, 'status': 'bad' }, new_tryjob_info] } self.assertDictEqual(bisect_contents, expected_file_contents) mock_find_tryjob_index.assert_called_once() mock_get_llvm_hash.assert_called_once_with(revision_to_add) mock_add_tryjob.assert_called_once() # Simulate the behavior of `FindTryjobIndex()` when the tryjob was found. @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0) def testModifyATryjobOptionDoesNotExist(self, mock_find_tryjob_index): bisect_test_contents = { 'start': 369410, 'end': 369420, 'jobs': [{ 'rev': 369414, 'status': 'bad' }] } # Create a temporary .JSON file to simulate a .JSON file that has bisection # contents. with test_helpers.CreateTemporaryJsonFile() as temp_json_file: with open(temp_json_file, 'w') as f: test_helpers.WritePrettyJsonFile(bisect_test_contents, f) # Add a revision that is outside of 'start' and 'end'. revision_to_modify = 369414 args_output = test_helpers.ArgsOutputTest() args_output.builders = None args_output.options = None # Verify the exception is raised when the modify a tryjob option does not # exist. with self.assertRaises(ValueError) as err: modify_a_tryjob.PerformTryjobModification( revision_to_modify, 'remove_link', temp_json_file, args_output.extra_change_lists, args_output.options, args_output.builders, args_output.chroot_path, args_output.verbose) self.assertEqual( str(err.exception), 'Invalid "modify_tryjob" option provided: remove_link') mock_find_tryjob_index.assert_called_once() if __name__ == '__main__': unittest.main()