| # Copyright 2012 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import base64 |
| import functools |
| import io |
| import json |
| import os |
| import re |
| import subprocess |
| import sys |
| import tempfile |
| import unittest |
| import urllib.request |
| from unittest.mockimport(ANY,Mock,MagicMock, mock_open, patch, call, |
| PropertyMock) |
| |
| bisect_builds= __import__('bisect-builds') |
| |
| if'NO_MOCK_SERVER'notin os.environ: |
| maybe_patch= patch |
| else: |
| # SetupEnvironment for gsutil to connect to real server. |
| options= bisect_builds.ParseCommandLine(['-a','linux64','-g','1']) |
| bisect_builds.SetupEnvironment(options) |
| bisect_builds.SetupAndroidEnvironment() |
| |
| # Mock object that always wraps for the spec. |
| # This will pass the call through and ignore the return_value and side_effect. |
| classWrappedMock(MagicMock): |
| |
| def __init__(self, |
| spec=None, |
| return_value=None, |
| side_effect=None, |
| *args, |
| **kwargs): |
| wraps= kwargs.pop('wraps', spec) |
| super().__init__(spec,*args,**kwargs, wraps=wraps) |
| |
| maybe_patch= functools.partial(patch, spec=True, new_callable=WrappedMock) |
| maybe_patch.object= functools.partial(patch.object, |
| spec=True, |
| new_callable=WrappedMock) |
| |
| |
| classBisectTestCase(unittest.TestCase): |
| |
| @classmethod |
| def setUpClass(cls): |
| # Patch the name pattern for pkgutil to accept "bisect-builds" as module |
| # name. |
| if sys.version_info[:2]>(3,8): |
| dotted_words= r'(?!\d)([\w-]+)(\.(?!\d)(\w+))*' |
| name_pattern= re.compile( |
| f'^(?P<pkg>{dotted_words})' |
| f'(?P<cln>:(?P<obj>{dotted_words})?)?$', re.UNICODE) |
| cls.name_pattern_patcher= patch('pkgutil._NAME_PATTERN', name_pattern) |
| cls.name_pattern_patcher.start() |
| |
| # patch cache filename to prevent pollute working dir. |
| fd, cls.tmp_cache_file= tempfile.mkstemp(suffix='.json') |
| os.close(fd) |
| cls.cache_filename_patcher= patch( |
| 'bisect-builds.ArchiveBuild._rev_list_cache_filename', |
| new=PropertyMock(return_value=cls.tmp_cache_file)) |
| cls.cache_filename_patcher.start() |
| |
| @classmethod |
| def tearDownClass(cls): |
| if sys.version_info[:2]>(3,8): |
| cls.name_pattern_patcher.stop() |
| cls.cache_filename_patcher.stop() |
| os.unlink(cls.tmp_cache_file) |
| |
| |
| classBisectTest(BisectTestCase): |
| |
| max_rev=10000 |
| |
| def setUp(self): |
| self.patchers=[] |
| self.patchers.append(patch('bisect-builds.DownloadJob._fetch')) |
| self.patchers.append( |
| patch('bisect-builds.ArchiveBuild.run_revision', |
| return_value=(0,'',''))) |
| self.patchers.append( |
| patch('bisect-builds.SnapshotBuild._get_rev_list', |
| return_value=range(self.max_rev))) |
| for eachin self.patchers: |
| each.start() |
| |
| def tearDown(self): |
| for eachin self.patchers: |
| each.stop() |
| |
| def bisect(self, good_rev, bad_rev, evaluate, num_runs=1): |
| options= bisect_builds.ParseCommandLine([ |
| '-a','linux64','-g', |
| str(good_rev),'-b', |
| str(bad_rev),'--times', |
| str(num_runs),'--no-local-cache' |
| ]) |
| archive_build= bisect_builds.create_archive_build(options) |
| (minrev, maxrev)= bisect_builds.Bisect(archive_build=archive_build, |
| evaluate=evaluate, |
| try_args=options.args) |
| return(minrev, maxrev) |
| |
| @patch('builtins.print') |
| def testBisectConsistentAnswer(self, mock_print): |
| |
| def get_steps(): |
| steps=[] |
| for callin mock_print.call_args_list: |
| if call.argsand call.args[0].startswith('You have'): |
| steps.append(int(re.search(r'(\d+) steps', call.args[0])[1])) |
| return steps |
| |
| self.assertEqual(self.bisect(1000,100,lambda*args:'g'),(100,101)) |
| self.assertSequenceEqual(get_steps(), range(10,1,-1)) |
| |
| mock_print.reset_mock() |
| self.assertEqual(self.bisect(100,1000,lambda*args:'b'),(100,101)) |
| self.assertSequenceEqual(get_steps(), range(10,0,-1)) |
| |
| mock_print.reset_mock() |
| self.assertEqual(self.bisect(2000,200,lambda*args:'b'),(1999,2000)) |
| self.assertSequenceEqual(get_steps(), range(11,0,-1)) |
| |
| mock_print.reset_mock() |
| self.assertEqual(self.bisect(200,2000,lambda*args:'g'),(1999,2000)) |
| self.assertSequenceEqual(get_steps(), range(11,1,-1)) |
| |
| @patch('bisect-builds.ArchiveBuild.run_revision', return_value=(0,'','')) |
| def test_bisect_should_retry(self, mock_run_revision): |
| evaluator=Mock(side_effect='rgrgrbr') |
| self.assertEqual(self.bisect(9,1, evaluator),(2,3)) |
| tested_revisions=[c.args[0]for cin evaluator.call_args_list] |
| self.assertEqual(tested_revisions,[5,5,3,3,2,2]) |
| self.assertEqual(mock_run_revision.call_count,6) |
| |
| evaluator=Mock(side_effect='rgrrrgrbr') |
| self.assertEqual(self.bisect(1,10, evaluator),(8,9)) |
| tested_revisions=[c.args[0]for cin evaluator.call_args_list] |
| self.assertEqual(tested_revisions,[6,6,8,8,8,8,9,9]) |
| |
| def test_bisect_should_unknown(self): |
| evaluator=Mock(side_effect='uuuggggg') |
| self.assertEqual(self.bisect(9,1, evaluator),(1,2)) |
| tested_revisions=[c.args[0]for cin evaluator.call_args_list] |
| self.assertEqual(tested_revisions,[5,3,6,7,2]) |
| |
| evaluator=Mock(side_effect='uuugggggg') |
| self.assertEqual(self.bisect(1,9, evaluator),(8,9)) |
| tested_revisions=[c.args[0]for cin evaluator.call_args_list] |
| self.assertEqual(tested_revisions,[5,7,4,3,8]) |
| |
| def test_bisect_should_quit(self): |
| evaluator=Mock(side_effect=SystemExit()) |
| with self.assertRaises(SystemExit): |
| self.assertEqual(self.bisect(9,1, evaluator),(None,None)) |
| |
| def test_edge_cases(self): |
| with self.assertRaises(bisect_builds.BisectException): |
| self.assertEqual(self.bisect(1,1,Mock()),(1,1)) |
| self.assertEqual(self.bisect(2,1,Mock()),(1,2)) |
| self.assertEqual(self.bisect(1,2,Mock()),(1,2)) |
| |
| |
| classDownloadJobTest(BisectTestCase): |
| |
| @patch('bisect-builds.gsutil_download') |
| def test_fetch_gsutil(self, mock_gsutil_download): |
| fetch= bisect_builds.DownloadJob('gs://some-file.zip',123) |
| fetch.start() |
| fetch.wait_for() |
| mock_gsutil_download.assert_called_once() |
| |
| @patch('urllib.request.urlretrieve') |
| def test_fetch_http(self, mock_urlretrieve): |
| fetch= bisect_builds.DownloadJob('http://some-file.zip',123) |
| fetch.start() |
| fetch.wait_for() |
| mock_urlretrieve.assert_called_once() |
| |
| @patch('tempfile.mkstemp', return_value=(321,'some-file.zip')) |
| @patch('urllib.request.urlretrieve') |
| @patch('os.close') |
| @patch('os.unlink') |
| def test_should_del(self, mock_unlink, mock_close, mock_urlretrieve, |
| mock_mkstemp): |
| fetch= bisect_builds.DownloadJob('http://some-file.zip',123) |
| fetch.start().wait_for() |
| fetch.stop() |
| mock_mkstemp.assert_called_once() |
| mock_close.assert_called_once() |
| mock_urlretrieve.assert_called_once() |
| mock_unlink.assert_called_with('some-file.zip') |
| |
| @patch('urllib.request.urlretrieve') |
| def test_stop_wait_for_should_be_able_to_reenter(self, mock_urlretrieve): |
| fetch= bisect_builds.DownloadJob('http://some-file.zip',123) |
| fetch.start() |
| fetch.wait_for() |
| fetch.wait_for() |
| fetch.stop() |
| fetch.stop() |
| |
| @patch('tempfile.mkstemp', |
| side_effect=[(321,'some-file.apks'),(123,'file2.apk')]) |
| @patch('bisect-builds.gsutil_download') |
| @patch('os.close') |
| @patch('os.unlink') |
| def test_should_support_multiple_files(self, mock_unlink, mock_close, |
| mock_gsutil, mock_mkstemp): |
| urls={ |
| 'trichrome': |
| ('gs://chrome-unsigned/android-B0urB0N/129.0.6626.0/high-arm_64/' |
| 'TrichromeChromeGoogle6432Stable.apks'), |
| 'trichrome_library': |
| ('gs://chrome-unsigned/android-B0urB0N/129.0.6626.0/high-arm_64/' |
| 'TrichromeLibraryGoogle6432Stable.apk'), |
| } |
| fetch= bisect_builds.DownloadJob(urls,123) |
| result= fetch.start().wait_for() |
| fetch.stop() |
| self.assertDictEqual(result,{ |
| 'trichrome':'some-file.apks', |
| 'trichrome_library':'file2.apk', |
| }) |
| self.assertEqual(mock_mkstemp.call_count,2) |
| self.assertEqual(mock_close.call_count,2) |
| mock_unlink.assert_has_calls( |
| [call('some-file.apks'), call('file2.apk')], any_order=True) |
| self.assertEqual(mock_gsutil.call_count,2) |
| |
| |
| @patch( |
| "urllib.request.urlopen", |
| side_effect=urllib.request.HTTPError('url',404,'Not Found',None,None), |
| ) |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| @patch('bisect-builds.GSUTILS_PATH', new='/some/path') |
| def test_download_failure_should_raised(self, mock_Popen, mock_urlopen): |
| fetch= bisect_builds.DownloadJob('http://some-file.zip',123) |
| with self.assertRaises(urllib.request.HTTPError): |
| fetch.start().wait_for() |
| |
| mock_Popen.return_value.communicate.return_value=(b'', b'status=403') |
| mock_Popen.return_value.returncode=1 |
| fetch= bisect_builds.DownloadJob('gs://some-file.zip',123) |
| with self.assertRaises(bisect_builds.BisectException): |
| fetch.start().wait_for() |
| |
| |
| classArchiveBuildTest(BisectTestCase): |
| |
| def setUp(self): |
| self.patcher= patch.multiple( |
| bisect_builds.ArchiveBuild, |
| __abstractmethods__=set(), |
| build_type='release', |
| _get_rev_list=Mock(return_value=list(map(str, range(10)))), |
| _rev_list_cache_key='abc') |
| self.patcher.start() |
| |
| def tearDown(self): |
| self.patcher.stop() |
| |
| def create_build(self,*args): |
| args=['-a','linux64','-g','0','-b','9',*args] |
| options= bisect_builds.ParseCommandLine(args) |
| return bisect_builds.ArchiveBuild(options) |
| |
| def test_cache_should_not_work_if_not_enabled(self): |
| build= self.create_build('--no-local-cache') |
| self.assertFalse(build.use_local_cache) |
| with patch('builtins.open')as m: |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| bisect_builds.ArchiveBuild._get_rev_list.assert_called_once() |
| m.assert_not_called() |
| |
| def test_cache_should_save_and_load(self): |
| build= self.create_build() |
| self.assertTrue(build.use_local_cache) |
| # Load the non-existent cache and write to it. |
| cached_data=[] |
| # The cache file would be opened 3 times: |
| # 1. read by _load_rev_list_cache |
| # 2. read by _save_rev_list_cache for existing cache |
| # 3. write by _save_rev_list_cache |
| write_mock=MagicMock() |
| write_mock.__enter__().write.side_effect=lambda d: cached_data.append(d) |
| with patch('builtins.open', |
| side_effect=[FileNotFoundError,FileNotFoundError, write_mock]): |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| bisect_builds.ArchiveBuild._get_rev_list.assert_called_once() |
| cached_json= json.loads(''.join(cached_data)) |
| self.assertDictEqual(cached_json,{'abc':[str(x)for xin range(10)]}) |
| # Load cache with cached data. |
| build= self.create_build('--use-local-cache') |
| bisect_builds.ArchiveBuild._get_rev_list.reset_mock() |
| with patch('builtins.open', mock_open(read_data=''.join(cached_data))): |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| bisect_builds.ArchiveBuild._get_rev_list.assert_not_called() |
| |
| @patch.object(bisect_builds.ArchiveBuild,'_load_rev_list_cache') |
| @patch.object(bisect_builds.ArchiveBuild,'_save_rev_list_cache') |
| @patch.object(bisect_builds.ArchiveBuild, |
| '_get_rev_list', |
| return_value=[str(x)for xin range(10)]) |
| def test_should_request_partial_rev_list(self, mock_get_rev_list, |
| mock_save_rev_list_cache, |
| mock_load_rev_list_cache): |
| build= self.create_build('--no-local-cache') |
| # missing latest |
| mock_load_rev_list_cache.return_value=[str(x)for xin range(5)] |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| mock_get_rev_list.assert_called_with('4','9') |
| # missing old and latest |
| mock_load_rev_list_cache.return_value=[str(x)for xin range(1,5)] |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| mock_get_rev_list.assert_called_with('0','9') |
| # missing old |
| mock_load_rev_list_cache.return_value=[str(x)for xin range(3,10)] |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| mock_get_rev_list.assert_called_with('0','3') |
| # no intersect |
| mock_load_rev_list_cache.return_value=['c','d','e'] |
| self.assertEqual(build.get_rev_list(),[str(x)for xin range(10)]) |
| mock_save_rev_list_cache.assert_called_with([str(x)for xin range(10)]+ |
| ['c','d','e']) |
| mock_get_rev_list.assert_called_with('0','c') |
| |
| @patch.object(bisect_builds.ArchiveBuild,'_get_rev_list', return_value=[]) |
| def test_should_raise_error_when_no_rev_list(self, mock_get_rev_list): |
| build= self.create_build('--no-local-cache') |
| with self.assertRaises(bisect_builds.BisectException): |
| build.get_rev_list() |
| mock_get_rev_list.assert_any_call('0','9') |
| mock_get_rev_list.assert_any_call() |
| |
| @unittest.skipIf('NO_MOCK_SERVER'notin os.environ, |
| 'The test is to ensure NO_MOCK_SERVER working correctly') |
| @maybe_patch('bisect-builds.GetRevisionFromVersion', return_value=123) |
| def test_no_mock(self, mock_GetRevisionFromVersion): |
| self.assertEqual(bisect_builds.GetRevisionFromVersion('127.0.6533.74'), |
| 1313161) |
| mock_GetRevisionFromVersion.assert_called() |
| |
| @patch('bisect-builds.ArchiveBuild._install_revision') |
| @patch('bisect-builds.ArchiveBuild._launch_revision', |
| return_value=(1,'','')) |
| def test_run_revision_should_return_early(self, mock_launch_revision, |
| mock_install_revision): |
| build= self.create_build() |
| build.run_revision('','',[]) |
| mock_launch_revision.assert_called_once() |
| |
| @patch('bisect-builds.ArchiveBuild._install_revision') |
| @patch('bisect-builds.ArchiveBuild._launch_revision', |
| return_value=(0,'','')) |
| def test_run_revision_should_do_all_runs(self, mock_launch_revision, |
| mock_install_revision): |
| build= self.create_build('--time','10') |
| build.run_revision('','',[]) |
| self.assertEqual(mock_launch_revision.call_count,10) |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('glob.glob', return_value=['temp-dir/linux64/chrome']) |
| @patch('os.path.abspath', return_value='/tmp/temp-dir/linux64/chrome') |
| def test_install_revision_should_unzip_and_search_executable( |
| self, mock_abspath, mock_glob, mock_UnzipFilenameToDir): |
| build= self.create_build() |
| self.assertEqual(build._install_revision('some-file.zip','temp-dir'), |
| {'chrome':'/tmp/temp-dir/linux64/chrome'}) |
| mock_UnzipFilenameToDir.assert_called_once_with('some-file.zip','temp-dir') |
| mock_glob.assert_called_once_with('temp-dir/*/chrome') |
| mock_abspath.assert_called_once_with('temp-dir/linux64/chrome') |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('glob.glob', |
| side_effect=[['temp-dir/chrome-linux64/chrome'], |
| ['temp-dir/chromedriver_linux64/chromedriver']]) |
| @patch('os.path.abspath', |
| side_effect=[ |
| '/tmp/temp-dir/chrome-linux64/chrome', |
| '/tmp/temp-dir/chromedriver_linux64/chromedriver' |
| ]) |
| def test_install_chromedriver(self, mock_abspath, mock_glob, |
| mock_UnzipFilenameToDir): |
| build= self.create_build('--chromedriver') |
| self.assertEqual( |
| build._install_revision( |
| { |
| 'chrome':'some-file.zip', |
| 'chromedriver':'some-other-file.zip', |
| },'temp-dir'), |
| { |
| 'chrome':'/tmp/temp-dir/chrome-linux64/chrome', |
| 'chromedriver':'/tmp/temp-dir/chromedriver_linux64/chromedriver', |
| }) |
| mock_UnzipFilenameToDir.assert_has_calls([ |
| call('some-file.zip','temp-dir'), |
| call('some-other-file.zip','temp-dir'), |
| ]) |
| mock_glob.assert_has_calls([ |
| call('temp-dir/*/chrome'), |
| call('temp-dir/*/chromedriver'), |
| ]) |
| mock_abspath.assert_has_calls([ |
| call('temp-dir/chrome-linux64/chrome'), |
| call('temp-dir/chromedriver_linux64/chromedriver') |
| ]) |
| |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_launch_revision_should_run_command(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| build= self.create_build() |
| build._launch_revision('temp-dir',{'chrome':'temp-dir/linux64/chrome'}, |
| []) |
| mock_Popen.assert_called_once_with( |
| 'temp-dir/linux64/chrome --user-data-dir=temp-dir/profile', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| |
| @unittest.skipIf(sys.platform.startswith('win'),'This test is not for win') |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_launch_revision_should_run_command_for_mac(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| build= self.create_build() |
| build._launch_revision( |
| 'temp-dir',{ |
| 'chrome': |
| 'temp-dir/full-build-mac/' |
| 'Google Chrome.app/Contents/MacOS/Google Chrome' |
| },[]) |
| mock_Popen.assert_called_once_with( |
| "'temp-dir/full-build-mac/" |
| "Google Chrome.app/Contents/MacOS/Google Chrome'" |
| ' --user-data-dir=temp-dir/profile', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| |
| @unittest.skipUnless(sys.platform.startswith('win'), |
| 'This test is for win only') |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_launch_revision_should_run_command_for_win(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| build= self.create_build() |
| build._launch_revision( |
| 'C:\\temp-dir',{ |
| 'chrome':'C:\\temp-dir\\full-build-win\\chrome.exe' |
| },[]) |
| mock_Popen.assert_called_once_with( |
| "C:\\temp-dir\\full-build-win\\chrome.exe " |
| '--user-data-dir=C:\\temp-dir/profile', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_command_replacement(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| build= self.create_build( |
| '--chromedriver','-c', |
| 'CHROMEDRIVER=%d BROWSER_EXECUTABLE_PATH=%p pytest %a') |
| build._launch_revision('/tmp',{ |
| 'chrome':'/tmp/chrome', |
| 'chromedriver':'/tmp/chromedriver' |
| },['--args','--args2="word 1"','word 2']) |
| if sys.platform.startswith('win'): |
| mock_Popen.assert_called_once_with( |
| 'CHROMEDRIVER=/tmp/chromedriver BROWSER_EXECUTABLE_PATH=/tmp/chrome ' |
| 'pytest --user-data-dir=/tmp/profile --args "--args2=\\"word 1\\"" ' |
| '"word 2"', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| else: |
| mock_Popen.assert_called_once_with( |
| 'CHROMEDRIVER=/tmp/chromedriver BROWSER_EXECUTABLE_PATH=/tmp/chrome ' |
| 'pytest --user-data-dir=/tmp/profile --args \'--args2="word 1"\' ' |
| '\'word 2\'', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| |
| |
| classReleaseBuildTest(BisectTestCase): |
| |
| def test_should_look_up_path_context(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88']) |
| self.assertEqual(options.archive,'linux64') |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| self.assertEqual(build.binary_name,'chrome') |
| self.assertEqual(build.listing_platform_dir,'linux64/') |
| self.assertEqual(build.archive_name,'chrome-linux64.zip') |
| self.assertEqual(build.archive_extract_dir,'chrome-linux64') |
| |
| @maybe_patch( |
| 'bisect-builds.GsutilList', |
| return_value=[ |
| 'gs://chrome-unsigned/desktop-5c0tCh/%s/linux64/chrome-linux64.zip'% |
| xfor xin['127.0.6533.74','127.0.6533.75','127.0.6533.76'] |
| ]) |
| def test_get_rev_list(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.76', |
| '--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| self.assertEqual(build.get_rev_list(), |
| ['127.0.6533.74','127.0.6533.75','127.0.6533.76']) |
| mock_GsutilList.assert_any_call('gs://chrome-unsigned/desktop-5c0tCh') |
| mock_GsutilList.assert_any_call(*[ |
| 'gs://chrome-unsigned/desktop-5c0tCh/%s/linux64/chrome-linux64.zip'% x |
| for xin['127.0.6533.74','127.0.6533.75','127.0.6533.76'] |
| ], |
| ignore_fail=True) |
| self.assertEqual(mock_GsutilList.call_count,2) |
| |
| @patch('bisect-builds.GsutilList', |
| return_value=['127.0.6533.74','127.0.6533.75','127.0.6533.76']) |
| def test_should_save_and_load_cache(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.77', |
| '--use-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| # Load the non-existent cache and write to it. |
| cached_data=[] |
| write_mock=MagicMock() |
| write_mock.__enter__().write.side_effect=lambda d: cached_data.append(d) |
| with patch('builtins.open', |
| side_effect=[FileNotFoundError,FileNotFoundError, write_mock]): |
| self.assertEqual(build.get_rev_list(), |
| ['127.0.6533.74','127.0.6533.75','127.0.6533.76']) |
| mock_GsutilList.assert_called() |
| cached_json= json.loads(''.join(cached_data)) |
| self.assertDictEqual( |
| cached_json,{ |
| build._rev_list_cache_key: |
| ['127.0.6533.74','127.0.6533.75','127.0.6533.76'] |
| }) |
| # Load cache with cached data and merge with new data |
| mock_GsutilList.return_value=['127.0.6533.76','127.0.6533.77'] |
| build= bisect_builds.create_archive_build(options) |
| with patch('builtins.open', mock_open(read_data=''.join(cached_data))): |
| self.assertEqual( |
| build.get_rev_list(), |
| ['127.0.6533.74','127.0.6533.75','127.0.6533.76','127.0.6533.77']) |
| print(mock_GsutilList.call_args) |
| mock_GsutilList.assert_any_call( |
| 'gs://chrome-unsigned/desktop-5c0tCh' |
| '/127.0.6533.76/linux64/chrome-linux64.zip', |
| 'gs://chrome-unsigned/desktop-5c0tCh' |
| '/127.0.6533.77/linux64/chrome-linux64.zip', |
| ignore_fail=True) |
| |
| def test_get_download_url(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.77']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| download_urls= build.get_download_url('127.0.6533.74') |
| self.assertEqual( |
| download_urls,'gs://chrome-unsigned/desktop-5c0tCh' |
| '/127.0.6533.74/linux64/chrome-linux64.zip') |
| |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.77', |
| '--chromedriver' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| download_urls= build.get_download_url('127.0.6533.74') |
| self.assertEqual( |
| download_urls,{ |
| 'chrome': |
| 'gs://chrome-unsigned/desktop-5c0tCh' |
| '/127.0.6533.74/linux64/chrome-linux64.zip', |
| 'chromedriver': |
| 'gs://chrome-unsigned/desktop-5c0tCh' |
| '/127.0.6533.74/linux64/chromedriver_linux64.zip', |
| }) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision_with_real_zipfile(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.77', |
| '--chromedriver','-c','driver=%d prog=%p' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ReleaseBuild) |
| download_job= build.get_download_job('127.0.6533.74') |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| self.assertRegex(mock_run.call_args.args[0], |
| r'driver=.+/chromedriver prog=.+/chrome') |
| |
| |
| classArchiveBuildWithCommitPositionTest(BisectTestCase): |
| |
| def setUp(self): |
| patch.multiple(bisect_builds.ArchiveBuildWithCommitPosition, |
| __abstractmethods__=set(), |
| build_type='release').start() |
| |
| @maybe_patch('bisect-builds.GetRevisionFromVersion', return_value=1313161) |
| @maybe_patch('bisect-builds.GetChromiumRevision', return_value=999999999) |
| def test_should_convert_revision_as_commit_position( |
| self, mock_GetChromiumRevision, mock_GetRevisionFromVersion): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','127.0.6533.74']) |
| build= bisect_builds.ArchiveBuildWithCommitPosition(options) |
| self.assertEqual(build.good_revision,1313161) |
| self.assertEqual(build.bad_revision,999999999) |
| mock_GetRevisionFromVersion.assert_called_once_with('127.0.6533.74') |
| mock_GetChromiumRevision.assert_called() |
| |
| |
| classOfficialBuildTest(BisectTestCase): |
| |
| def test_should_lookup_path_context(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-o','-a','linux64','-g','0','-b','10']) |
| self.assertEqual(options.archive,'linux64') |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.OfficialBuild) |
| self.assertEqual(build.binary_name,'chrome') |
| self.assertEqual(build.listing_platform_dir,'linux-builder-perf/') |
| self.assertEqual(build.archive_name,'chrome-perf-linux.zip') |
| self.assertEqual(build.archive_extract_dir,'full-build-linux') |
| |
| @maybe_patch('bisect-builds.GsutilList', |
| return_value=[ |
| 'full-build-linux_%d.zip'% x |
| for xin range(1313161,1313164) |
| ]) |
| def test_get_rev_list(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','linux64','-g','1313161','-b','1313163', |
| '--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.OfficialBuild) |
| self.assertEqual(build.get_rev_list(), list(range(1313161,1313164))) |
| mock_GsutilList.assert_called_once_with( |
| 'gs://chrome-test-builds/official-by-commit/linux-builder-perf/') |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision_with_real_zipfile(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','linux64','-g','1313161','-b','1313163', |
| '--chromedriver','-c','driver=%d prog=%p' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.OfficialBuild) |
| download_job= build.get_download_job(1334339) |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| self.assertRegex(mock_run.call_args.args[0], |
| r'driver=.+/chromedriver prog=.+/chrome') |
| |
| classSnapshotBuildTest(BisectTestCase): |
| |
| def test_should_lookup_path_context(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','0','-b','10']) |
| self.assertEqual(options.archive,'linux64') |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| self.assertEqual(build.binary_name,'chrome') |
| self.assertEqual(build.listing_platform_dir,'Linux_x64/') |
| self.assertEqual(build.archive_name,'chrome-linux.zip') |
| self.assertEqual(build.archive_extract_dir,'chrome-linux') |
| |
| CommonDataXMLContent='''<?xml version='1.0' encoding='UTF-8'?> |
| <ListBucketResult xmlns='http://doc.s3.amazonaws.com/2006-03-01'> |
| <Name>chromium-browser-snapshots</Name> |
| <Prefix>Linux_x64/</Prefix> |
| <Marker></Marker> |
| <NextMarker></NextMarker> |
| <Delimiter>/</Delimiter> |
| <IsTruncated>true</IsTruncated> |
| <CommonPrefixes> |
| <Prefix>Linux_x64/1313161/</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>Linux_x64/1313163/</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>Linux_x64/1313185/</Prefix> |
| </CommonPrefixes> |
| </ListBucketResult> |
| ''' |
| |
| @maybe_patch('urllib.request.urlopen', |
| return_value=io.BytesIO(CommonDataXMLContent.encode('utf8'))) |
| @patch('bisect-builds.GetChromiumRevision', return_value=1313185) |
| def test_get_rev_list(self, mock_GetChromiumRevision, mock_urlopen): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','1313161','-b','1313185','--no-local-cache']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| rev_list= build.get_rev_list() |
| mock_urlopen.assert_any_call( |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots/' |
| '?delimiter=/&prefix=Linux_x64/&marker=Linux_x64/1313161') |
| self.assertEqual(mock_urlopen.call_count,1) |
| self.assertEqual(rev_list,[1313161,1313163,1313185]) |
| |
| @patch('bisect-builds.SnapshotBuild._fetch_and_parse', |
| return_value=([int(s) |
| for sin sorted([str(x)for xin range(1,11)])],None)) |
| def test_get_rev_list_should_start_from_a_marker(self, mock_fetch_and_parse): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','0','-b','9','--no-local-cache']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| rev_list= build._get_rev_list(0,9) |
| self.assertEqual(rev_list, list(range(1,10))) |
| mock_fetch_and_parse.assert_called_once_with( |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots/' |
| '?delimiter=/&prefix=Linux_x64/&marker=Linux_x64/0') |
| mock_fetch_and_parse.reset_mock() |
| rev_list= build._get_rev_list(1,9) |
| self.assertEqual(rev_list, list(range(1,10))) |
| mock_fetch_and_parse.assert_called_once_with( |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots/' |
| '?delimiter=/&prefix=Linux_x64/&marker=Linux_x64/1') |
| |
| @patch('bisect-builds.SnapshotBuild._fetch_and_parse', |
| return_value=([int(s) |
| for sin sorted([str(x)for xin range(1,11)])],None)) |
| def test_get_rev_list_should_scan_all_pages(self, mock_fetch_and_parse): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','3','-b','11','--no-local-cache']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| rev_list= build._get_rev_list(0,11) |
| self.assertEqual(sorted(rev_list), list(range(1,11))) |
| mock_fetch_and_parse.assert_called_once_with( |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots/' |
| '?delimiter=/&prefix=Linux_x64/') |
| |
| def test_get_download_url(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','3','-b','11']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| download_urls= build.get_download_url(123) |
| self.assertEqual( |
| download_urls, |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots' |
| '/Linux_x64/123/chrome-linux.zip', |
| ) |
| |
| options= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','3','-b','11','--chromedriver']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| download_urls= build.get_download_url(123) |
| self.assertDictEqual( |
| download_urls,{ |
| 'chrome': |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots' |
| '/Linux_x64/123/chrome-linux.zip', |
| 'chromedriver': |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots' |
| '/Linux_x64/123/chromedriver_linux64.zip' |
| }) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision_with_real_zipfile(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-a','linux64','-g','1313161','-b','1313185','--chromedriver', |
| '-c','driver=%d prog=%p' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| download_job= build.get_download_job(1313161) |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| self.assertRegex(mock_run.call_args.args[0], |
| r'driver=.+/chromedriver prog=.+/chrome') |
| |
| @patch('bisect-builds.GetChromiumRevision', return_value=1313185) |
| def test_get_bad_revision(self, mock_GetChromiumRevision): |
| options= bisect_builds.ParseCommandLine(['-a','linux64','-g','1313161']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| mock_GetChromiumRevision.assert_called_once_with( |
| 'http://commondatastorage.googleapis.com/chromium-browser-snapshots' |
| '/Linux_x64/LAST_CHANGE') |
| self.assertEqual(build.bad_revision,1313185) |
| |
| |
| classChromeForTestingBuild(BisectTestCase): |
| |
| def test_should_lookup_path_context(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-cft','-a','linux64','-g','0','-b','10']) |
| self.assertEqual(options.archive,'linux64') |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| self.assertEqual(build.binary_name,'chrome') |
| self.assertEqual(build.listing_platform_dir,'linux64/') |
| self.assertEqual(build.archive_name,'chrome-linux64.zip') |
| self.assertEqual(build.chromedriver_binary_name,'chromedriver') |
| self.assertEqual(build.chromedriver_archive_name, |
| 'chromedriver-linux64.zip') |
| |
| CommonDataXMLContent='''<?xml version='1.0' encoding='UTF-8'?> |
| <ListBucketResult xmlns="http://doc.s3.amazonaws.com/2006-03-01"> |
| <Name>chrome-for-testing-per-commit-public</Name> |
| <Prefix>linux64/</Prefix> |
| <Marker/> |
| <Delimiter>/</Delimiter> |
| <IsTruncated>false</IsTruncated> |
| <Contents> |
| <Key>linux64/LAST_CHANGE</Key> |
| <Generation>1733959087133532</Generation> |
| <MetaGeneration>1</MetaGeneration> |
| <LastModified>2024-12-11T23:18:07.235Z</LastModified> |
| <ETag>"dd60cb93e225ab33d7254beca56b507a"</ETag> |
| <Size>7</Size> |
| </Contents> |
| <CommonPrefixes> |
| <Prefix>linux64/r1390729/</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>linux64/r1390746/</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>linux64/r1390757/</Prefix> |
| </CommonPrefixes> |
| </ListBucketResult> |
| ''' |
| |
| @maybe_patch('urllib.request.urlopen', |
| return_value=io.BytesIO(CommonDataXMLContent.encode('utf8'))) |
| @patch('bisect-builds.GetChromiumRevision', return_value=1390757) |
| def test_get_rev_list(self, mock_GetChromiumRevision, mock_urlopen): |
| options= bisect_builds.ParseCommandLine([ |
| '-cft','-a','linux64','-g','1390729','-b','1390757', |
| '--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| rev_list= build.get_rev_list() |
| mock_urlopen.assert_any_call( |
| 'https://storage.googleapis.com/chrome-for-testing-per-commit-public/' |
| '?delimiter=/&prefix=linux64/&marker=linux64/r1390729') |
| self.assertEqual(mock_urlopen.call_count,1) |
| self.assertEqual(rev_list,[1390729,1390746,1390757]) |
| |
| def test_get_marker(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-cft','-a','linux64','-g','1','-b','3']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| self.assertEqual('linux64/r1390729', |
| build._get_marker_for_revision(1390729)) |
| |
| def test_get_download_url(self): |
| options= bisect_builds.ParseCommandLine( |
| ['-cft','-a','linux64','-g','3','-b','11']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| download_urls= build.get_download_url(123) |
| self.assertEqual( |
| download_urls, |
| 'https://storage.googleapis.com/chrome-for-testing-per-commit-public' |
| '/linux64/r123/chrome-linux64.zip', |
| ) |
| |
| options= bisect_builds.ParseCommandLine( |
| ['-cft','-a','linux64','-g','3','-b','11','--chromedriver']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| download_urls= build.get_download_url(123) |
| download_urls= build.get_download_url(123) |
| self.assertEqual( |
| download_urls,{ |
| 'chrome': |
| 'https://storage.googleapis.com/chrome-for-testing-per-commit-public' |
| '/linux64/r123/chrome-linux64.zip', |
| 'chromedriver': |
| 'https://storage.googleapis.com/chrome-for-testing-per-commit-public' |
| '/linux64/r123/chromedriver-linux64.zip', |
| }) |
| |
| @patch('bisect-builds.GetChromiumRevision', return_value=1390757) |
| def test_get_bad_revision(self, mock_GetChromiumRevision): |
| options= bisect_builds.ParseCommandLine( |
| ['-cft','-a','linux64','-g','3']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ChromeForTestingBuild) |
| mock_GetChromiumRevision.assert_called_once_with( |
| 'https://storage.googleapis.com/chrome-for-testing-per-commit-public' |
| '/linux64/LAST_CHANGE') |
| self.assertEqual(build.bad_revision,1390757) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision_with_real_zipfile(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '--cft','-a','linux64','-g','1','--chromedriver','-c', |
| 'driver=%d prog=%p' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.SnapshotBuild) |
| download_job= build.get_download_job(build.bad_revision) |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| self.assertRegex(mock_run.call_args.args[0], |
| r'driver=.+/chromedriver prog=.+/chrome') |
| |
| |
| classASANBuildTest(BisectTestCase): |
| |
| CommonDataXMLContent='''<?xml version='1.0' encoding='UTF-8'?> |
| <ListBucketResult xmlns='http://doc.s3.amazonaws.com/2006-03-01'> |
| <Name>chromium-browser-asan</Name> |
| <Prefix>mac-release/asan-mac-release</Prefix> |
| <Marker></Marker> |
| <NextMarker></NextMarker> |
| <Delimiter>.zip</Delimiter> |
| <IsTruncated>true</IsTruncated> |
| <CommonPrefixes> |
| <Prefix>mac-release/asan-mac-release-1313186.zip</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>mac-release/asan-mac-release-1313195.zip</Prefix> |
| </CommonPrefixes> |
| <CommonPrefixes> |
| <Prefix>mac-release/asan-mac-release-1313210.zip</Prefix> |
| </CommonPrefixes> |
| </ListBucketResult> |
| ''' |
| |
| @maybe_patch('urllib.request.urlopen', |
| return_value=io.BytesIO(CommonDataXMLContent.encode('utf8'))) |
| def test_get_rev_list(self, mock_urlopen): |
| options= bisect_builds.ParseCommandLine([ |
| '--asan','-a','mac','-g','1313161','-b','1313210', |
| '--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.ASANBuild) |
| rev_list= build.get_rev_list() |
| # print(mock_urlopen.call_args_list) |
| mock_urlopen.assert_any_call( |
| 'http://commondatastorage.googleapis.com/chromium-browser-asan/' |
| '?delimiter=.zip&prefix=mac-release/asan-mac-release' |
| '&marker=mac-release/asan-mac-release-1313161.zip') |
| self.assertEqual(mock_urlopen.call_count,1) |
| self.assertEqual(rev_list,[1313186,1313195,1313210]) |
| |
| |
| classAndroidBuildTest(BisectTestCase): |
| |
| def setUp(self): |
| # patch for devil_imports |
| self.patchers=[] |
| flag_changer_patcher= maybe_patch('bisect-builds.flag_changer', |
| create=True) |
| self.patchers.append(flag_changer_patcher) |
| self.mock_flag_changer= flag_changer_patcher.start() |
| chrome_patcher= maybe_patch('bisect-builds.chrome', create=True) |
| self.patchers.append(chrome_patcher) |
| self.mock_chrome= chrome_patcher.start() |
| version_codes_patcher= maybe_patch('bisect-builds.version_codes', |
| create=True) |
| self.patchers.append(version_codes_patcher) |
| self.mock_version_codes= version_codes_patcher.start() |
| self.mock_version_codes.LOLLIPOP=21 |
| self.mock_version_codes.NOUGAT=24 |
| self.mock_version_codes.PIE=28 |
| self.mock_version_codes.Q=29 |
| initial_android_device_patcher= patch( |
| 'bisect-builds.InitializeAndroidDevice') |
| self.patchers.append(initial_android_device_patcher) |
| self.mock_initial_android_device= initial_android_device_patcher.start() |
| self.device= self.mock_initial_android_device.return_value |
| self.set_sdk_level(bisect_builds.version_codes.Q) |
| |
| def set_sdk_level(self, level): |
| self.device.build_version_sdk= level |
| |
| def tearDown(self): |
| for patcherin self.patchers: |
| patcher.stop() |
| |
| |
| classAndroidReleaseBuildTest(AndroidBuildTest): |
| |
| def setUp(self): |
| super().setUp() |
| self.set_sdk_level(bisect_builds.version_codes.PIE) |
| |
| @maybe_patch( |
| 'bisect-builds.GsutilList', |
| return_value=[ |
| 'gs://chrome-signed/android-B0urB0N/%s/arm_64/MonochromeStable.apk'% |
| xfor xin['127.0.6533.76','127.0.6533.78','127.0.6533.79'] |
| ]) |
| def test_get_android_rev_list(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64','--apk','chrome_stable','-g', |
| '127.0.6533.76','-b','127.0.6533.79','--signed','--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidReleaseBuild) |
| self.assertEqual(build.get_rev_list(), |
| ['127.0.6533.76','127.0.6533.78','127.0.6533.79']) |
| mock_GsutilList.assert_any_call('gs://chrome-signed/android-B0urB0N') |
| mock_GsutilList.assert_any_call(*[ |
| 'gs://chrome-signed/android-B0urB0N/%s/arm_64/MonochromeStable.apk'% x |
| for xin['127.0.6533.76','127.0.6533.78','127.0.6533.79'] |
| ], |
| ignore_fail=True) |
| self.assertEqual(mock_GsutilList.call_count,2) |
| |
| @patch('bisect-builds.InstallOnAndroid') |
| def test_install_revision(self, mock_InstallOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64','-g','127.0.6533.76','-b', |
| '127.0.6533.79','--apk','chrome' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidReleaseBuild) |
| build._install_revision('chrome.apk','temp-dir') |
| mock_InstallOnAndroid.assert_called_once_with(self.device,'chrome.apk') |
| |
| @patch('bisect-builds.LaunchOnAndroid') |
| def test_launch_revision(self, mock_LaunchOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64','-g','127.0.6533.76','-b', |
| '127.0.6533.79','--apk','chrome' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidReleaseBuild) |
| build._launch_revision('temp-dir',None) |
| mock_LaunchOnAndroid.assert_called_once_with(self.device,'chrome') |
| |
| @patch('bisect-builds.LaunchOnAndroid') |
| def test_webview_launch_revision(self, mock_LaunchOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64','-g','127.0.6533.76','-b', |
| '127.0.6533.79','--apk','system_webview' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidReleaseBuild) |
| build._launch_revision('temp-dir',None) |
| mock_LaunchOnAndroid.assert_called_once_with(self.device,'system_webview') |
| with self.assertRaises(bisect_builds.BisectException): |
| build._launch_revision('temp-dir',None,['args']) |
| |
| |
| classAndroidSnapshotBuildTest(AndroidBuildTest): |
| |
| def setUp(self): |
| super().setUp() |
| self.set_sdk_level(bisect_builds.version_codes.PIE) |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('bisect-builds.InstallOnAndroid') |
| @patch('glob.glob', return_value=['Monochrome.apk']) |
| def test_install_revision(self, mock_glob, mock_InstallOnAndroid, mock_unzip): |
| options= bisect_builds.ParseCommandLine([ |
| '-a','android-arm64','-g','1313161','-b','1313210','--apk', |
| 'chrome' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidSnapshotBuild) |
| build._install_revision('chrome.zip','temp-dir') |
| mock_glob.assert_called_once_with('temp-dir/*/apks/Monochrome.apk') |
| mock_InstallOnAndroid.assert_called_once_with(self.device,'Monochrome.apk') |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('sys.stdout', new_callable=io.StringIO) |
| @patch('glob.glob', |
| side_effect=[[], |
| [ |
| "temp-dir/full-build-linux/apks/MonochromeBeta.apk", |
| "temp-dir/full-build-linux/apks/ChromePublic.apk" |
| ]]) |
| def test_install_revision_with_show_available_apks(self, mock_glob, |
| mock_stdout, mock_unzip): |
| options= bisect_builds.ParseCommandLine([ |
| '-a','android-arm64','-g','1313161','-b','1313210','--apk', |
| 'chrome' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidSnapshotBuild) |
| with self.assertRaises(bisect_builds.BisectException): |
| build._install_revision('chrome.zip','temp-dir') |
| self.assertIn("The list of available --apk:", mock_stdout.getvalue()) |
| self.assertIn("chrome_beta", mock_stdout.getvalue()) |
| self.assertIn("chromium", mock_stdout.getvalue()) |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('sys.stdout', new_callable=io.StringIO) |
| @patch('glob.glob', |
| side_effect=[[],["temp-dir/full-build-linux/apks/unknown.apks"]]) |
| def test_install_revision_with_show_unknown_apks(self, mock_glob, mock_stdout, |
| mock_unzip): |
| options= bisect_builds.ParseCommandLine([ |
| '-a','android-arm64','-g','1313161','-b','1313210','--apk', |
| 'chrome' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidSnapshotBuild) |
| with self.assertRaises(bisect_builds.BisectException): |
| build._install_revision('chrome.zip','temp-dir') |
| self.assertIn("No supported apk found. But found following", |
| mock_stdout.getvalue()) |
| self.assertIn("unknown.apks", mock_stdout.getvalue()) |
| |
| classAndroidTrichromeReleaseBuildTest(AndroidBuildTest): |
| |
| def setUp(self): |
| super().setUp() |
| self.set_sdk_level(bisect_builds.version_codes.Q) |
| |
| @maybe_patch( |
| 'bisect-builds.GsutilList', |
| side_effect=[[ |
| 'gs://chrome-unsigned/android-B0urB0N/%s/'% xfor xin[ |
| '129.0.6626.0','129.0.6626.1','129.0.6627.0','129.0.6627.1', |
| '129.0.6628.0','129.0.6628.1' |
| ] |
| ], |
| [('gs://chrome-unsigned/android-B0urB0N/%s/' |
| 'high-arm_64/TrichromeChromeGoogle6432Stable.apks')% x |
| for xin['129.0.6626.0','129.0.6627.0','129.0.6628.0']]]) |
| def test_get_rev_list(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64-high','--apk','chrome_stable','-g', |
| '129.0.6626.0','-b','129.0.6628.0','--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeReleaseBuild) |
| self.assertEqual(build.get_rev_list(), |
| ['129.0.6626.0','129.0.6627.0','129.0.6628.0']) |
| print(mock_GsutilList.call_args_list) |
| mock_GsutilList.assert_any_call('gs://chrome-unsigned/android-B0urB0N') |
| mock_GsutilList.assert_any_call(*[ |
| ('gs://chrome-unsigned/android-B0urB0N/%s/' |
| 'high-arm_64/TrichromeChromeGoogle6432Stable.apks')% xfor xin[ |
| '129.0.6626.0','129.0.6626.1','129.0.6627.0','129.0.6627.1', |
| '129.0.6628.0' |
| ] |
| ], |
| ignore_fail=True) |
| self.assertEqual(mock_GsutilList.call_count,2) |
| |
| def test_should_raise_exception_for_PIE(self): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64-high','--apk','chrome_stable','-g', |
| '129.0.6626.0','-b','129.0.6667.0' |
| ]) |
| self.set_sdk_level(bisect_builds.version_codes.PIE) |
| with self.assertRaises(bisect_builds.BisectException): |
| bisect_builds.create_archive_build(options) |
| |
| def test_get_download_url(self): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64-high','--apk','chrome_stable','-g', |
| '129.0.6626.0','-b','129.0.6628.0' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeReleaseBuild) |
| download_urls= build.get_download_url('129.0.6626.0') |
| self.maxDiff=1000 |
| self.assertDictEqual( |
| download_urls,{ |
| 'trichrome': |
| ('gs://chrome-unsigned/android-B0urB0N/129.0.6626.0/high-arm_64/' |
| 'TrichromeChromeGoogle6432Stable.apks'), |
| 'trichrome_library': |
| ('gs://chrome-unsigned/android-B0urB0N/129.0.6626.0/high-arm_64/' |
| 'TrichromeLibraryGoogle6432Stable.apk'), |
| }) |
| |
| @patch('bisect-builds.InstallOnAndroid') |
| def test_install_revision(self, mock_InstallOnAndroid): |
| downloads={ |
| 'trichrome':'some-file.apks', |
| 'trichrome_library':'file2.apk', |
| } |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64-high','--apk','chrome_stable','-g', |
| '129.0.6626.0','-b','129.0.6628.0' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeReleaseBuild) |
| build._install_revision(downloads,'tmp-dir') |
| mock_InstallOnAndroid.assert_any_call(self.device,'some-file.apks') |
| mock_InstallOnAndroid.assert_any_call(self.device,'file2.apk') |
| |
| |
| classAndroidTrichromeOfficialBuildTest(AndroidBuildTest): |
| |
| @maybe_patch('bisect-builds.GsutilList', |
| return_value=[ |
| 'full-build-linux_%d.zip'% x |
| for xin[1334339,1334342,1334344,1334345,1334356] |
| ]) |
| def test_get_rev_list(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','chrome','-g','1334338', |
| '-b','1334380','--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| self.assertEqual(build.get_rev_list(), |
| [1334339,1334342,1334344,1334345,1334356]) |
| mock_GsutilList.assert_called_once_with( |
| 'gs://chrome-test-builds/official-by-commit/' |
| 'android_arm64_high_end-builder-perf/') |
| |
| def test_get_download_url(self): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','chrome','-g','1334338', |
| '-b','1334380' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| self.assertEqual( |
| build.get_download_url(1334338), |
| 'gs://chrome-test-builds/official-by-commit' |
| '/android_arm64_high_end-builder-perf/full-build-linux_1334338.zip') |
| |
| @patch('glob.glob', |
| side_effect=[[ |
| 'temp-dir/full-build-linux/apks/TrichromeChromeGoogle6432.apks' |
| ],['temp-dir/full-build-linux/apks/TrichromeLibraryGoogle6432.apk']]) |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('bisect-builds.InstallOnAndroid') |
| def test_install_revision(self, mock_InstallOnAndroid, |
| mock_UnzipFilenameToDir, mock_glob): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','chrome','-g','1334338', |
| '-b','1334380' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| build._install_revision('download.zip','tmp-dir') |
| mock_UnzipFilenameToDir.assert_called_once_with('download.zip','tmp-dir') |
| mock_InstallOnAndroid.assert_any_call( |
| self.device, |
| 'temp-dir/full-build-linux/apks/TrichromeLibraryGoogle6432.apk') |
| mock_InstallOnAndroid.assert_any_call( |
| self.device, |
| 'temp-dir/full-build-linux/apks/TrichromeChromeGoogle6432.apks') |
| |
| @patch('sys.stdout', new_callable=io.StringIO) |
| @patch('glob.glob', |
| side_effect=[[], |
| ['temp-dir/TrichromeChromeGoogle6432Canary.minimal.apks'] |
| ]) |
| @patch('bisect-builds.UnzipFilenameToDir') |
| def test_install_revision_with_show_available_apks(self, |
| mock_UnzipFilenameToDir, |
| mock_glob, mock_stdout): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','chrome','-g','1334338', |
| '-b','1334380' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| with self.assertRaises(bisect_builds.BisectException): |
| build._install_revision('download.zip','tmp-dir') |
| self.assertIn("The list of available --apk:", mock_stdout.getvalue()) |
| self.assertIn("chrome_canary", mock_stdout.getvalue()) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.InstallOnAndroid') |
| @patch('bisect-builds.LaunchOnAndroid') |
| def test_run_revision_with_real_zipfile(self, mock_LaunchOnAndroid, |
| mock_InstallOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','chrome','-g','1334338', |
| '-b','1334380' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| download_job= build.get_download_job(1334339) |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| print(mock_InstallOnAndroid.call_args_list) |
| self.assertRegex(mock_InstallOnAndroid.mock_calls[0].args[1], |
| 'full-build-linux/apks/TrichromeLibraryGoogle6432.apk$') |
| self.assertRegex( |
| mock_InstallOnAndroid.mock_calls[1].args[1], |
| 'full-build-linux/apks/TrichromeChromeGoogle6432.minimal.apks$') |
| mock_LaunchOnAndroid.assert_called_once_with(self.device,'chrome') |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.InstallOnAndroid') |
| @patch('bisect-builds.LaunchOnAndroid') |
| def test_run_revision_with_webview_apk_with_unsupported_versions( |
| self, mock_LaunchOnAndroid, mock_InstallOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','system_webview','-g', |
| '100000','-b','100010' |
| ]) |
| |
| with self.assertRaises(bisect_builds.BisectException): |
| _= bisect_builds.create_archive_build(options) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.InstallOnAndroid') |
| @patch('bisect-builds.LaunchOnAndroid') |
| def test_webview_launch_revision(self, mock_LaunchOnAndroid, |
| mock_InstallOnAndroid): |
| options= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','--apk','system_webview','-g', |
| '1350000','-b','1350010' |
| ]) |
| |
| build= bisect_builds.create_archive_build(options) |
| |
| self.assertIsInstance(build, bisect_builds.AndroidTrichromeOfficialBuild) |
| download_job= build.get_download_job(1334339) |
| zip_file= download_job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(zip_file, tempdir,[]) |
| print(mock_InstallOnAndroid.call_args_list) |
| self.assertRegex(mock_InstallOnAndroid.mock_calls[0].args[1], |
| 'full-build-linux/apks/TrichromeLibraryGoogle6432.apk$') |
| self.assertRegex( |
| mock_InstallOnAndroid.mock_calls[1].args[1], |
| 'full-build-linux/apks/TrichromeWebViewGoogle6432.minimal.apks$') |
| mock_LaunchOnAndroid.assert_called_once_with(self.device,'system_webview') |
| |
| |
| classLinuxReleaseBuildTest(BisectTestCase): |
| |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_launch_revision_should_has_no_sandbox(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| options= bisect_builds.ParseCommandLine( |
| ['-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88']) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.LinuxReleaseBuild) |
| build._launch_revision('temp-dir',{'chrome':'temp-dir/linux64/chrome'}, |
| []) |
| mock_Popen.assert_called_once_with( |
| 'temp-dir/linux64/chrome --user-data-dir=temp-dir/profile --no-sandbox', |
| cwd=None, |
| shell=True, |
| bufsize=-1, |
| stdout=ANY, |
| stderr=ANY) |
| |
| |
| classIOSReleaseBuildTest(BisectTestCase): |
| |
| @maybe_patch( |
| 'bisect-builds.GsutilList', |
| side_effect=[[ |
| 'gs://chrome-unsigned/ios-G1N/127.0.6533.76/', |
| 'gs://chrome-unsigned/ios-G1N/127.0.6533.77/', |
| 'gs://chrome-unsigned/ios-G1N/127.0.6533.78/' |
| ], |
| [ |
| 'gs://chrome-unsigned/ios-G1N' |
| '/127.0.6533.76/iphoneos17.5/ios/10863/canary.ipa', |
| 'gs://chrome-unsigned/ios-G1N' |
| '/127.0.6533.77/iphoneos17.5/ios/10866/canary.ipa', |
| 'gs://chrome-unsigned/ios-G1N' |
| '/127.0.6533.78/iphoneos17.5/ios/10868/canary.ipa' |
| ]]) |
| def test_list_rev(self, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios','--ipa=canary.ipa','--device-id','321','-g', |
| '127.0.6533.74','-b','127.0.6533.78','--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSReleaseBuild) |
| self.assertEqual(build.get_rev_list(), |
| ['127.0.6533.76','127.0.6533.77','127.0.6533.78']) |
| mock_GsutilList.assert_any_call('gs://chrome-unsigned/ios-G1N') |
| mock_GsutilList.assert_any_call(*[ |
| 'gs://chrome-unsigned/ios-G1N/%s/*/ios/*/canary.ipa'% x |
| for xin['127.0.6533.76','127.0.6533.77','127.0.6533.78'] |
| ], |
| ignore_fail=True) |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('glob.glob', return_value=['Payload/canary.app/Info.plist']) |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_install_revision(self, mock_Popen, mock_glob, |
| mock_UnzipFilenameToDir): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios','--ipa=canary.ipa','--device-id','321','-g', |
| '127.0.6533.74','-b','127.0.6533.78' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSReleaseBuild) |
| build._install_revision('canary.ipa','tempdir') |
| mock_glob.assert_called_once_with('tempdir/Payload/*/Info.plist') |
| mock_Popen.assert_has_calls([ |
| call([ |
| 'xcrun','devicectl','device','install','app','--device','321', |
| 'canary.ipa' |
| ], |
| cwd=None, |
| shell=False, |
| bufsize=-1, |
| stdout=-1, |
| stderr=-1), |
| call([ |
| 'plutil','-extract','CFBundleIdentifier','raw', |
| 'Payload/canary.app/Info.plist' |
| ], |
| cwd=None, |
| shell=False, |
| bufsize=-1, |
| stdout=-1, |
| stderr=-1) |
| ], |
| any_order=True) |
| |
| @patch('subprocess.Popen', spec=subprocess.Popen) |
| def test_launch_revision(self, mock_Popen): |
| mock_Popen.return_value.communicate.return_value=('','') |
| mock_Popen.return_value.returncode=0 |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios','--ipa=canary.ipa','--device-id','321','-g', |
| '127.0.6533.74','-b','127.0.6533.78','--','args1','args2' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSReleaseBuild) |
| build._launch_revision('tempdir','com.google.chrome.ios', options.args) |
| mock_Popen.assert_any_call([ |
| 'xcrun','devicectl','device','process','launch','--device','321', |
| 'com.google.chrome.ios','args1','args2' |
| ], |
| cwd=None, |
| shell=False, |
| bufsize=-1, |
| stdout=-1, |
| stderr=-1) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios','--ipa=canary.ipa','--device-id','321','-g', |
| '127.0.6533.74','-b','127.0.6533.78' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSReleaseBuild) |
| job= build.get_download_job('127.0.6533.76') |
| ipa= job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(ipa, tempdir, options.args) |
| mock_run.assert_has_calls([ |
| call([ |
| 'xcrun','devicectl','device','install','app','--device','321', |
| ANY |
| ]), |
| call(['plutil','-extract','CFBundleIdentifier','raw', ANY]), |
| call([ |
| 'xcrun','devicectl','device','process','launch','--device', |
| '321','stdout' |
| ]) |
| ]) |
| |
| |
| classIOSSimulatorReleaseBuildTest(BisectTestCase): |
| |
| @maybe_patch( |
| 'bisect-builds.GsutilList', |
| side_effect=[ |
| [ |
| 'gs://bling-archive/128.0.6534.0/', |
| 'gs://bling-archive/128.0.6534.1/', |
| 'gs://bling-archive/128.0.6535.0/', |
| 'gs://bling-archive/128.0.6535.1/', |
| 'gs://bling-archive/128.0.6536.0/', |
| ], |
| [ |
| 'gs://bling-archive/128.0.6534.0/20240612011643/Chromium.tar.gz', |
| 'gs://bling-archive/128.0.6536.0/20240613011356/Chromium.tar.gz', |
| ] |
| ]) |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'','')) |
| def test_list_rev(self, mock_run, mock_GsutilList): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios-simulator','--device-id','321','-g','128.0.6534.0', |
| '-b','128.0.6536.0','--no-local-cache' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSSimulatorReleaseBuild) |
| self.assertEqual(build.get_rev_list(),['128.0.6534.0','128.0.6536.0']) |
| mock_run.assert_called_once_with(['xcrun','simctl','boot','321']) |
| mock_GsutilList.assert_any_call('gs://bling-archive') |
| mock_GsutilList.assert_any_call(*[ |
| 'gs://bling-archive/%s/*/Chromium.tar.gz'% xfor xin[ |
| '128.0.6534.0','128.0.6534.1','128.0.6535.0','128.0.6535.1', |
| '128.0.6536.0' |
| ] |
| ], |
| ignore_fail=True) |
| |
| @patch('bisect-builds.UnzipFilenameToDir') |
| @patch('glob.glob', return_value=['Info.plist']) |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'','')) |
| def test_install_revision(self, mock_run, mock_glob, mock_UnzipFilenameToDir): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios-simulator','--device-id','321','-g','128.0.6534.0', |
| '-b','128.0.6539.0' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSSimulatorReleaseBuild) |
| build._install_revision('Chromium.tar.gz','tempdir') |
| mock_UnzipFilenameToDir.assert_called_once_with('Chromium.tar.gz', |
| 'tempdir') |
| self.assertEqual(mock_glob.call_count,2) |
| mock_run.assert_has_calls([ |
| call(['xcrun','simctl','boot','321']), |
| call(['xcrun','simctl','install','321', ANY]), |
| call(['plutil','-extract','CFBundleIdentifier','raw','Info.plist']), |
| ]) |
| |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'','')) |
| def test_launch_revision(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios-simulator','--device-id','321','-g','128.0.6534.0', |
| '-b','128.0.6539.0' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSSimulatorReleaseBuild) |
| build._launch_revision('tempdir','com.google.chrome.ios.dev', |
| ['args1','args2']) |
| mock_run.assert_any_call([ |
| 'xcrun','simctl','launch','321','com.google.chrome.ios.dev', |
| 'args1','args2' |
| ]) |
| |
| @unittest.skipUnless('NO_MOCK_SERVER'in os.environ, |
| 'The test only valid when NO_MOCK_SERVER') |
| @patch('bisect-builds.ArchiveBuild._run', return_value=(0,'stdout','')) |
| def test_run_revision(self, mock_run): |
| options= bisect_builds.ParseCommandLine([ |
| '-r','-a','ios-simulator','--device-id','321','-g','128.0.6534.0', |
| '-b','128.0.6539.0' |
| ]) |
| build= bisect_builds.create_archive_build(options) |
| self.assertIsInstance(build, bisect_builds.IOSSimulatorReleaseBuild) |
| job= build.get_download_job('128.0.6534.0') |
| download= job.start().wait_for() |
| with tempfile.TemporaryDirectory(prefix='bisect_tmp')as tempdir: |
| build.run_revision(download, tempdir, options.args) |
| mock_run.assert_has_calls([ |
| call(['xcrun','simctl','boot','321']), |
| call(['xcrun','simctl','install','321', ANY]), |
| call(['plutil','-extract','CFBundleIdentifier','raw', ANY]), |
| call(['xcrun','simctl','launch','321','stdout']) |
| ]) |
| |
| |
| classMaybeSwitchBuildTypeTest(BisectTestCase): |
| |
| def test_generate_new_command_without_cache(self): |
| command_line=[ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88', |
| '--no-local-cache' |
| ] |
| options= bisect_builds.ParseCommandLine(command_line) |
| with patch('sys.argv',['bisect-builds.py',*command_line]): |
| new_cmd= bisect_builds.MaybeSwitchBuildType( |
| options, bisect_builds.ChromiumVersion('127.0.6533.74'), |
| bisect_builds.ChromiumVersion('127.0.6533.88')) |
| self.assertEqual(new_cmd[1:],[ |
| '-o','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88', |
| '--verify-range','--no-local-cache' |
| ]) |
| |
| def test_android_signed_with_args(self): |
| command_line=[ |
| '-r','--archive=android-arm64-high','--good=127.0.6533.74','-b', |
| '127.0.6533.88','--apk=chrome','--signed','--no-local-cache','--', |
| 'args1','--args2' |
| ] |
| options= bisect_builds.ParseCommandLine(command_line) |
| with patch('sys.argv',['bisect-builds.py',*command_line]): |
| new_cmd= bisect_builds.MaybeSwitchBuildType(options,'127.0.6533.74', |
| '127.0.6533.88') |
| self.assertEqual(new_cmd[1:],[ |
| '-o','-a','android-arm64-high','-g','127.0.6533.74','-b', |
| '127.0.6533.88','--verify-range','--apk=chrome','--no-local-cache', |
| '--','args1','--args2' |
| ]) |
| |
| def test_no_official_build(self): |
| command_line=[ |
| '-r','-a','ios','--ipa=canary.ipa','--device-id','321','-g', |
| '127.0.6533.74','-b','127.0.6533.78','--no-local-cache' |
| ] |
| options= bisect_builds.ParseCommandLine(command_line) |
| with patch('sys.argv',['bisect-builds.py',*command_line]): |
| new_cmd= bisect_builds.MaybeSwitchBuildType(options,'127.0.6533.74', |
| '127.0.6533.88') |
| self.assertEqual(new_cmd,None) |
| |
| @patch('bisect-builds.ArchiveBuild.get_rev_list', return_value=list(range(3))) |
| def test_generate_suggestion_with_cache(self, mock_get_rev_list): |
| command_line=[ |
| '-r','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88', |
| '--use-local-cache' |
| ] |
| options= bisect_builds.ParseCommandLine(command_line) |
| with patch('sys.argv',['bisect-builds.py',*command_line]): |
| new_cmd= bisect_builds.MaybeSwitchBuildType(options,'127.0.6533.74', |
| '127.0.6533.88') |
| self.assertEqual(new_cmd[1:],[ |
| '-o','-a','linux64','-g','127.0.6533.74','-b','127.0.6533.88', |
| '--verify-range','--use-local-cache' |
| ]) |
| mock_get_rev_list.assert_called() |
| |
| |
| classMethodTest(BisectTestCase): |
| |
| @patch('sys.stderr', new_callable=io.StringIO) |
| def test_ParseCommandLine(self, mock_stderr): |
| opts= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','1','args1','args2 3','-b','2']) |
| self.assertEqual(opts.build_type,'snapshot') |
| self.assertEqual(opts.args,['args1','args2 3']) |
| |
| opts= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','1','args1','args2 3']) |
| self.assertEqual(opts.args,['args1','args2 3']) |
| |
| opts= bisect_builds.ParseCommandLine( |
| ['-a','linux64','-g','1','--','args1','args2 3','-b','2']) |
| self.assertEqual(opts.args,['args1','args2 3','-b','2']) |
| |
| with self.assertRaises(SystemExit): |
| bisect_builds.ParseCommandLine(['-a','mac64','-o','-g','1']) |
| self.assertRegexpMatches( |
| mock_stderr.getvalue(), r'To bisect for mac64, please choose from ' |
| r'release(-r), snapshot(-s)') |
| |
| @patch('bisect-builds._DetectArchive', return_value='linux64') |
| def test_ParseCommandLine_DetectArchive(self, mock_detect_archive): |
| opts= bisect_builds.ParseCommandLine(['-o','-g','1']) |
| self.assertEqual(opts.archive,'linux64') |
| |
| def test_ParseCommandLine_default_apk(self): |
| opts= bisect_builds.ParseCommandLine( |
| ['-o','-a','android-arm','-g','1']) |
| self.assertEqual(opts.apk,'chrome') |
| |
| opts= bisect_builds.ParseCommandLine(['-a','android-arm64','-g','1']) |
| self.assertEqual(opts.apk,'chromium') |
| |
| def test_ParseCommandLine_default_ipa(self): |
| opts= bisect_builds.ParseCommandLine( |
| ['-r','-a','ios','-g','127.0.6533.74','-b','127.0.6533.88']) |
| self.assertEqual(opts.ipa,'canary') |
| |
| def test_ParseCommandLine_DetectArchive_with_apk(self): |
| opts= bisect_builds.ParseCommandLine(['-o','--apk','chrome','-g','1']) |
| self.assertEqual(opts.archive,'android-arm64') |
| |
| def test_ParseCommandLine_DetectArchive_with_ipa(self): |
| opts= bisect_builds.ParseCommandLine( |
| ['-r','--ipa','stable','-g','127.0.6533.74','-b','127.0.6533.88']) |
| self.assertEqual(opts.archive,'ios-simulator') |
| |
| @patch('sys.stderr', new_callable=io.StringIO) |
| def test_ParseCommandLine_apk_error(self, mock_stderr): |
| with self.assertRaises(SystemExit): |
| bisect_builds.ParseCommandLine( |
| ['-a','linux64','--apk','chrome','-g','1']) |
| self.assertIn('--apk is only supported', mock_stderr.getvalue()) |
| |
| @patch('sys.stderr', new_callable=io.StringIO) |
| def test_ParseCommandLine_ipa_error(self, mock_stderr): |
| with self.assertRaises(SystemExit): |
| bisect_builds.ParseCommandLine( |
| ['-a','linux64','--ipa','stable','-g','1']) |
| self.assertIn('--ipa is only supported', mock_stderr.getvalue()) |
| |
| @patch('sys.stderr', new_callable=io.StringIO) |
| def test_ParseCommandLine_signed_error(self, mock_stderr): |
| with self.assertRaises(SystemExit): |
| bisect_builds.ParseCommandLine(['-a','linux64','--signed','-g','1']) |
| self.assertIn('--signed is only supported', mock_stderr.getvalue()) |
| |
| @patch('sys.stderr', new_callable=io.StringIO) |
| def test_ParseCommandLine_webview_incompatibility_error(self, mock_stderr): |
| with self.assertRaises(SystemExit): |
| _= bisect_builds.ParseCommandLine([ |
| '-r','-a','android-arm64-high','-g','127.0.6533.76','-b', |
| '127.0.6533.79','--apk','system_webview' |
| ]) |
| self.assertIn( |
| 'Bisecting WebView for android-arm64-high, please choose official ' |
| 'builds (-o)', mock_stderr.getvalue()) |
| |
| opts= bisect_builds.ParseCommandLine([ |
| '-o','-a','android-arm64-high','-g','1334017','-b','1335078', |
| '--apk','system_webview' |
| ]) |
| self.assertEqual(opts.apk,'system_webview') |
| self.assertEqual(opts.archive,'android-arm64-high') |
| self.assertEqual(opts.build_type,'official') |
| |
| |
| @patch('urllib.request.urlopen') |
| @patch('builtins.open') |
| @patch('sys.stdout', new_callable=io.StringIO) |
| def test_update_script(self, mock_stdout, mock_open, mock_urlopen): |
| mock_urlopen.return_value= io.BytesIO( |
| base64.b64encode('content'.encode('utf-8'))) |
| with self.assertRaises(SystemExit): |
| bisect_builds.ParseCommandLine(['--update-script']) |
| mock_urlopen.assert_called_once_with( |
| 'https://chromium.googlesource.com/chromium/src/+/HEAD/' |
| 'tools/bisect-builds.py?format=TEXT') |
| mock_open.assert_called_once() |
| mock_open.return_value.__enter__().write.assert_called_once_with('content') |
| self.assertEqual(mock_stdout.getvalue(),'Update successful!\n') |
| |
| @patch("urllib.request.urlopen", |
| side_effect=[ |
| urllib.request.HTTPError('url',404,'Not Found',None,None), |
| urllib.request.HTTPError('url',404,'Not Found',None,None), |
| io.BytesIO(b"NOT_A_JSON"), |
| io.BytesIO(b'{"chromium_main_branch_position": 123}'), |
| ]) |
| def test_GetRevisionFromVersion(self, mock_urlopen): |
| self.assertEqual(123, |
| bisect_builds.GetRevisionFromVersion('127.0.6533.134')) |
| mock_urlopen.assert_has_calls([ |
| call('https://chromiumdash.appspot.com/fetch_version' |
| '?version=127.0.6533.134'), |
| call('https://chromiumdash.appspot.com/fetch_version' |
| '?version=127.0.6533.0'), |
| ]) |
| |
| @maybe_patch("urllib.request.urlopen", |
| side_effect=[ |
| io.BytesIO(b'{"chromium_main_branch_position": null}'), |
| io.BytesIO(b'{"message": "DEP\\n"}'), |
| io.BytesIO(b')]}\'\n{"message": "Cr-Branched-From: ' |
| b'3d60439cfb36485e76a1c5bb7f513d3721b20da1-' |
| b'refs/heads/master@{#870763}\\n"}'), |
| ]) |
| def test_GetRevisionFromSourceTag(self, mock_urlopen): |
| self.assertEqual(870763, |
| bisect_builds.GetRevisionFromVersion('91.0.4472.38')) |
| mock_urlopen.assert_has_calls([ |
| call('https://chromiumdash.appspot.com/fetch_version' |
| '?version=91.0.4472.38'), |
| call('https://chromium.googlesource.com/chromium/src/' |
| '+/refs/tags/91.0.4472.38?format=JSON'), |
| call('https://chromium.googlesource.com/chromium/src/' |
| '+/refs/tags/91.0.4472.38^?format=JSON'), |
| ]) |
| |
| def test_join_args(self): |
| test_data=['a','b c','C:\\a b\\c','/a b/c','"a"',"'a'"] |
| quoted_command= bisect_builds.join_args( |
| [sys.executable,'-c','import sys, json; print(json.dumps(sys.argv))'] |
| + test_data) |
| |
| subproc= subprocess.Popen( |
| quoted_command, shell=True, stdout=subprocess.PIPE) |
| stdout, _= subproc.communicate() |
| dumped_argv= json.loads(stdout.decode('utf-8')) |
| |
| self.assertListEqual(dumped_argv,['-c']+ test_data) |
| |
| |
| classChromiumVersionTest(BisectTestCase): |
| def test_cmpare_version_numbers(self): |
| v127_0_6533_74= bisect_builds.ChromiumVersion('127.0.6533.74') |
| v127_0_6533_75= bisect_builds.ChromiumVersion('127.0.6533.75') |
| v127_0_6533_75_with_space= bisect_builds.ChromiumVersion('127.0.6533.75 ') |
| v127= bisect_builds.ChromiumVersion('127') |
| |
| self.assertLess(v127_0_6533_74, v127_0_6533_75) |
| self.assertLessEqual(v127_0_6533_74, v127_0_6533_75) |
| self.assertGreater(v127_0_6533_75, v127_0_6533_74) |
| self.assertGreaterEqual(v127_0_6533_75, v127_0_6533_74) |
| self.assertEqual(v127_0_6533_75, v127_0_6533_75_with_space) |
| self.assertLess(v127, v127_0_6533_74) |
| |
| if __name__=='__main__': |
| unittest.main() |