1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2019 The Chromium Authors 2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file. 4*8975f5c5SAndroid Build Coastguard Worker 5*8975f5c5SAndroid Build Coastguard Workerimport os 6*8975f5c5SAndroid Build Coastguard Workerimport tempfile 7*8975f5c5SAndroid Build Coastguard Workerimport time 8*8975f5c5SAndroid Build Coastguard Workerimport unittest 9*8975f5c5SAndroid Build Coastguard Worker 10*8975f5c5SAndroid Build Coastguard Workerimport mock 11*8975f5c5SAndroid Build Coastguard Workerimport version 12*8975f5c5SAndroid Build Coastguard Worker 13*8975f5c5SAndroid Build Coastguard Worker 14*8975f5c5SAndroid Build Coastguard Workerdef _ReplaceArgs(args, *replacements): 15*8975f5c5SAndroid Build Coastguard Worker new_args = args[:] 16*8975f5c5SAndroid Build Coastguard Worker for flag, val in replacements: 17*8975f5c5SAndroid Build Coastguard Worker flag_index = args.index(flag) 18*8975f5c5SAndroid Build Coastguard Worker new_args[flag_index + 1] = val 19*8975f5c5SAndroid Build Coastguard Worker return new_args 20*8975f5c5SAndroid Build Coastguard Worker 21*8975f5c5SAndroid Build Coastguard Worker 22*8975f5c5SAndroid Build Coastguard Workerclass _VersionTest(unittest.TestCase): 23*8975f5c5SAndroid Build Coastguard Worker """Unittests for the version module. 24*8975f5c5SAndroid Build Coastguard Worker """ 25*8975f5c5SAndroid Build Coastguard Worker 26*8975f5c5SAndroid Build Coastguard Worker _CHROME_VERSION_FILE = os.path.join( 27*8975f5c5SAndroid Build Coastguard Worker os.path.dirname(__file__), os.pardir, os.pardir, 'chrome', 'VERSION') 28*8975f5c5SAndroid Build Coastguard Worker 29*8975f5c5SAndroid Build Coastguard Worker _SCRIPT = os.path.join(os.path.dirname(__file__), 'version.py') 30*8975f5c5SAndroid Build Coastguard Worker 31*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_VERSION = { 32*8975f5c5SAndroid Build Coastguard Worker 'MAJOR': '74', 33*8975f5c5SAndroid Build Coastguard Worker 'MINOR': '0', 34*8975f5c5SAndroid Build Coastguard Worker 'BUILD': '3720', 35*8975f5c5SAndroid Build Coastguard Worker 'PATCH': '0', 36*8975f5c5SAndroid Build Coastguard Worker } 37*8975f5c5SAndroid Build Coastguard Worker 38*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_TEMPLATE = ( 39*8975f5c5SAndroid Build Coastguard Worker 'full = "@MAJOR@.@MINOR@.@BUILD@.@PATCH@" ' 40*8975f5c5SAndroid Build Coastguard Worker 'major = "@MAJOR@" minor = "@MINOR@" ' 41*8975f5c5SAndroid Build Coastguard Worker 'build = "@BUILD@" patch = "@PATCH@" version_id = @VERSION_ID@ ') 42*8975f5c5SAndroid Build Coastguard Worker 43*8975f5c5SAndroid Build Coastguard Worker _ANDROID_CHROME_VARS = [ 44*8975f5c5SAndroid Build Coastguard Worker 'chrome_version_code', 45*8975f5c5SAndroid Build Coastguard Worker 'monochrome_version_code', 46*8975f5c5SAndroid Build Coastguard Worker 'trichrome_version_code', 47*8975f5c5SAndroid Build Coastguard Worker 'webview_stable_version_code', 48*8975f5c5SAndroid Build Coastguard Worker 'webview_beta_version_code', 49*8975f5c5SAndroid Build Coastguard Worker 'webview_dev_version_code', 50*8975f5c5SAndroid Build Coastguard Worker ] 51*8975f5c5SAndroid Build Coastguard Worker 52*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_ANDROID_TEMPLATE = ( 53*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_TEMPLATE + ''.join( 54*8975f5c5SAndroid Build Coastguard Worker ['%s = "@%s@" ' % (el, el.upper()) for el in _ANDROID_CHROME_VARS])) 55*8975f5c5SAndroid Build Coastguard Worker 56*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_ARGS = [ 57*8975f5c5SAndroid Build Coastguard Worker '-f', 58*8975f5c5SAndroid Build Coastguard Worker _CHROME_VERSION_FILE, 59*8975f5c5SAndroid Build Coastguard Worker '-t', 60*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_TEMPLATE, 61*8975f5c5SAndroid Build Coastguard Worker ] 62*8975f5c5SAndroid Build Coastguard Worker 63*8975f5c5SAndroid Build Coastguard Worker _EXAMPLE_ANDROID_ARGS = _ReplaceArgs(_EXAMPLE_ARGS, 64*8975f5c5SAndroid Build Coastguard Worker ['-t', _EXAMPLE_ANDROID_TEMPLATE]) + [ 65*8975f5c5SAndroid Build Coastguard Worker '-a', 66*8975f5c5SAndroid Build Coastguard Worker 'arm', 67*8975f5c5SAndroid Build Coastguard Worker '--os', 68*8975f5c5SAndroid Build Coastguard Worker 'android', 69*8975f5c5SAndroid Build Coastguard Worker ] 70*8975f5c5SAndroid Build Coastguard Worker 71*8975f5c5SAndroid Build Coastguard Worker @staticmethod 72*8975f5c5SAndroid Build Coastguard Worker def _RunBuildOutput(new_version_values={}, 73*8975f5c5SAndroid Build Coastguard Worker get_new_args=lambda old_args: old_args): 74*8975f5c5SAndroid Build Coastguard Worker """Parameterized helper method for running the main testable method in 75*8975f5c5SAndroid Build Coastguard Worker version.py. 76*8975f5c5SAndroid Build Coastguard Worker 77*8975f5c5SAndroid Build Coastguard Worker Keyword arguments: 78*8975f5c5SAndroid Build Coastguard Worker new_version_values -- dict used to update _EXAMPLE_VERSION 79*8975f5c5SAndroid Build Coastguard Worker get_new_args -- lambda for updating _EXAMPLE_ANDROID_ARGS 80*8975f5c5SAndroid Build Coastguard Worker """ 81*8975f5c5SAndroid Build Coastguard Worker 82*8975f5c5SAndroid Build Coastguard Worker with mock.patch('version.FetchValuesFromFile') as \ 83*8975f5c5SAndroid Build Coastguard Worker fetch_values_from_file_mock: 84*8975f5c5SAndroid Build Coastguard Worker 85*8975f5c5SAndroid Build Coastguard Worker fetch_values_from_file_mock.side_effect = (lambda values, file : 86*8975f5c5SAndroid Build Coastguard Worker values.update( 87*8975f5c5SAndroid Build Coastguard Worker dict(_VersionTest._EXAMPLE_VERSION, **new_version_values))) 88*8975f5c5SAndroid Build Coastguard Worker 89*8975f5c5SAndroid Build Coastguard Worker new_args = get_new_args(_VersionTest._EXAMPLE_ARGS) 90*8975f5c5SAndroid Build Coastguard Worker return version.BuildOutput(new_args) 91*8975f5c5SAndroid Build Coastguard Worker 92*8975f5c5SAndroid Build Coastguard Worker def testFetchValuesFromFile(self): 93*8975f5c5SAndroid Build Coastguard Worker """It returns a dict in correct format - { <str>: <str> }, to verify 94*8975f5c5SAndroid Build Coastguard Worker assumption of other tests that mock this function 95*8975f5c5SAndroid Build Coastguard Worker """ 96*8975f5c5SAndroid Build Coastguard Worker result = {} 97*8975f5c5SAndroid Build Coastguard Worker version.FetchValuesFromFile(result, self._CHROME_VERSION_FILE) 98*8975f5c5SAndroid Build Coastguard Worker 99*8975f5c5SAndroid Build Coastguard Worker for key, val in result.items(): 100*8975f5c5SAndroid Build Coastguard Worker self.assertIsInstance(key, str) 101*8975f5c5SAndroid Build Coastguard Worker self.assertIsInstance(val, str) 102*8975f5c5SAndroid Build Coastguard Worker 103*8975f5c5SAndroid Build Coastguard Worker def testBuildOutputAndroid(self): 104*8975f5c5SAndroid Build Coastguard Worker """Assert it gives includes assignments of expected variables""" 105*8975f5c5SAndroid Build Coastguard Worker output = self._RunBuildOutput( 106*8975f5c5SAndroid Build Coastguard Worker get_new_args=lambda args: self._EXAMPLE_ANDROID_ARGS) 107*8975f5c5SAndroid Build Coastguard Worker contents = output['contents'] 108*8975f5c5SAndroid Build Coastguard Worker 109*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bchrome_version_code = "\d+"\s') 110*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bmonochrome_version_code = "\d+"\s') 111*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\btrichrome_version_code = "\d+"\s') 112*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bwebview_stable_version_code = "\d+"\s') 113*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bwebview_beta_version_code = "\d+"\s') 114*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bwebview_dev_version_code = "\d+"\s') 115*8975f5c5SAndroid Build Coastguard Worker 116*8975f5c5SAndroid Build Coastguard Worker def testBuildOutputAndroidArchVariantsArm64(self): 117*8975f5c5SAndroid Build Coastguard Worker """Assert 64-bit-specific version codes""" 118*8975f5c5SAndroid Build Coastguard Worker new_template = ( 119*8975f5c5SAndroid Build Coastguard Worker self._EXAMPLE_ANDROID_TEMPLATE + 120*8975f5c5SAndroid Build Coastguard Worker "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" " 121*8975f5c5SAndroid Build Coastguard Worker "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" " 122*8975f5c5SAndroid Build Coastguard Worker "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" " 123*8975f5c5SAndroid Build Coastguard Worker "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ") 124*8975f5c5SAndroid Build Coastguard Worker args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS, 125*8975f5c5SAndroid Build Coastguard Worker ['-t', new_template]) 126*8975f5c5SAndroid Build Coastguard Worker new_args = _ReplaceArgs(args_with_template, ['-a', 'arm64']) 127*8975f5c5SAndroid Build Coastguard Worker output = self._RunBuildOutput(get_new_args=lambda args: new_args) 128*8975f5c5SAndroid Build Coastguard Worker contents = output['contents'] 129*8975f5c5SAndroid Build Coastguard Worker 130*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bmonochrome_64_32_version_code = "\d+"\s') 131*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bmonochrome_64_version_code = "\d+"\s') 132*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\btrichrome_64_32_version_code = "\d+"\s') 133*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\btrichrome_64_version_code = "\d+"\s') 134*8975f5c5SAndroid Build Coastguard Worker 135*8975f5c5SAndroid Build Coastguard Worker def testBuildOutputAndroidArchVariantsX64(self): 136*8975f5c5SAndroid Build Coastguard Worker """Assert 64-bit-specific version codes""" 137*8975f5c5SAndroid Build Coastguard Worker new_template = ( 138*8975f5c5SAndroid Build Coastguard Worker self._EXAMPLE_ANDROID_TEMPLATE + 139*8975f5c5SAndroid Build Coastguard Worker "monochrome_64_32_version_code = \"@MONOCHROME_64_32_VERSION_CODE@\" " 140*8975f5c5SAndroid Build Coastguard Worker "monochrome_64_version_code = \"@MONOCHROME_64_VERSION_CODE@\" " 141*8975f5c5SAndroid Build Coastguard Worker "trichrome_64_32_version_code = \"@TRICHROME_64_32_VERSION_CODE@\" " 142*8975f5c5SAndroid Build Coastguard Worker "trichrome_64_version_code = \"@TRICHROME_64_VERSION_CODE@\" ") 143*8975f5c5SAndroid Build Coastguard Worker args_with_template = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS, 144*8975f5c5SAndroid Build Coastguard Worker ['-t', new_template]) 145*8975f5c5SAndroid Build Coastguard Worker new_args = _ReplaceArgs(args_with_template, ['-a', 'x64']) 146*8975f5c5SAndroid Build Coastguard Worker output = self._RunBuildOutput(get_new_args=lambda args: new_args) 147*8975f5c5SAndroid Build Coastguard Worker contents = output['contents'] 148*8975f5c5SAndroid Build Coastguard Worker 149*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bmonochrome_64_32_version_code = "\d+"\s') 150*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\bmonochrome_64_version_code = "\d+"\s') 151*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\btrichrome_64_32_version_code = "\d+"\s') 152*8975f5c5SAndroid Build Coastguard Worker self.assertRegex(contents, r'\btrichrome_64_version_code = "\d+"\s') 153*8975f5c5SAndroid Build Coastguard Worker 154*8975f5c5SAndroid Build Coastguard Worker def testBuildOutputAndroidChromeArchInput(self): 155*8975f5c5SAndroid Build Coastguard Worker """Assert it raises an exception when using an invalid architecture input""" 156*8975f5c5SAndroid Build Coastguard Worker new_args = _ReplaceArgs(self._EXAMPLE_ANDROID_ARGS, ['-a', 'foobar']) 157*8975f5c5SAndroid Build Coastguard Worker # Mock sys.stderr because argparse will print to stderr when we pass 158*8975f5c5SAndroid Build Coastguard Worker # the invalid '-a' value. 159*8975f5c5SAndroid Build Coastguard Worker with self.assertRaises(SystemExit) as cm, mock.patch('sys.stderr'): 160*8975f5c5SAndroid Build Coastguard Worker self._RunBuildOutput(get_new_args=lambda args: new_args) 161*8975f5c5SAndroid Build Coastguard Worker 162*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(cm.exception.code, 2) 163*8975f5c5SAndroid Build Coastguard Worker 164*8975f5c5SAndroid Build Coastguard Worker def testSetExecutable(self): 165*8975f5c5SAndroid Build Coastguard Worker """Assert that -x sets executable on POSIX and is harmless on Windows.""" 166*8975f5c5SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmpdir: 167*8975f5c5SAndroid Build Coastguard Worker in_file = os.path.join(tmpdir, "in") 168*8975f5c5SAndroid Build Coastguard Worker out_file = os.path.join(tmpdir, "out") 169*8975f5c5SAndroid Build Coastguard Worker with open(in_file, "w") as f: 170*8975f5c5SAndroid Build Coastguard Worker f.write("") 171*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(version.main(['-i', in_file, '-o', out_file, '-x']), 0) 172*8975f5c5SAndroid Build Coastguard Worker 173*8975f5c5SAndroid Build Coastguard Worker # Whether lstat(out_file).st_mode has the executable bits set is 174*8975f5c5SAndroid Build Coastguard Worker # platform-specific. Therefore, test that out_file has the same 175*8975f5c5SAndroid Build Coastguard Worker # permissions that in_file would have after chmod(in_file, 0o755). 176*8975f5c5SAndroid Build Coastguard Worker # On Windows: both files will have 0o666. 177*8975f5c5SAndroid Build Coastguard Worker # On POSIX: both files will have 0o755. 178*8975f5c5SAndroid Build Coastguard Worker os.chmod(in_file, 0o755) # On Windows, this sets in_file to 0o666. 179*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(os.lstat(in_file).st_mode, os.lstat(out_file).st_mode) 180*8975f5c5SAndroid Build Coastguard Worker 181*8975f5c5SAndroid Build Coastguard Worker def testWriteIfChangedUpdateWhenContentChanged(self): 182*8975f5c5SAndroid Build Coastguard Worker """Assert it updates mtime of file when content is changed.""" 183*8975f5c5SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmpdir: 184*8975f5c5SAndroid Build Coastguard Worker file_name = os.path.join(tmpdir, "version.h") 185*8975f5c5SAndroid Build Coastguard Worker old_contents = "old contents" 186*8975f5c5SAndroid Build Coastguard Worker with open(file_name, "w") as f: 187*8975f5c5SAndroid Build Coastguard Worker f.write(old_contents) 188*8975f5c5SAndroid Build Coastguard Worker os.chmod(file_name, 0o644) 189*8975f5c5SAndroid Build Coastguard Worker mtime = os.lstat(file_name).st_mtime 190*8975f5c5SAndroid Build Coastguard Worker time.sleep(0.1) 191*8975f5c5SAndroid Build Coastguard Worker contents = "new contents" 192*8975f5c5SAndroid Build Coastguard Worker version.WriteIfChanged(file_name, contents, 0o644) 193*8975f5c5SAndroid Build Coastguard Worker with open(file_name) as f: 194*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(contents, f.read()) 195*8975f5c5SAndroid Build Coastguard Worker self.assertNotEqual(mtime, os.lstat(file_name).st_mtime) 196*8975f5c5SAndroid Build Coastguard Worker 197*8975f5c5SAndroid Build Coastguard Worker def testWriteIfChangedUpdateWhenModeChanged(self): 198*8975f5c5SAndroid Build Coastguard Worker """Assert it updates mtime of file when mode is changed.""" 199*8975f5c5SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmpdir: 200*8975f5c5SAndroid Build Coastguard Worker file_name = os.path.join(tmpdir, "version.h") 201*8975f5c5SAndroid Build Coastguard Worker contents = "old contents" 202*8975f5c5SAndroid Build Coastguard Worker with open(file_name, "w") as f: 203*8975f5c5SAndroid Build Coastguard Worker f.write(contents) 204*8975f5c5SAndroid Build Coastguard Worker os.chmod(file_name, 0o644) 205*8975f5c5SAndroid Build Coastguard Worker mtime = os.lstat(file_name).st_mtime 206*8975f5c5SAndroid Build Coastguard Worker time.sleep(0.1) 207*8975f5c5SAndroid Build Coastguard Worker version.WriteIfChanged(file_name, contents, 0o755) 208*8975f5c5SAndroid Build Coastguard Worker with open(file_name) as f: 209*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(contents, f.read()) 210*8975f5c5SAndroid Build Coastguard Worker self.assertNotEqual(mtime, os.lstat(file_name).st_mtime) 211*8975f5c5SAndroid Build Coastguard Worker 212*8975f5c5SAndroid Build Coastguard Worker def testWriteIfChangedNoUpdate(self): 213*8975f5c5SAndroid Build Coastguard Worker """Assert it does not update mtime of file when nothing is changed.""" 214*8975f5c5SAndroid Build Coastguard Worker with tempfile.TemporaryDirectory() as tmpdir: 215*8975f5c5SAndroid Build Coastguard Worker file_name = os.path.join(tmpdir, "version.h") 216*8975f5c5SAndroid Build Coastguard Worker contents = "old contents" 217*8975f5c5SAndroid Build Coastguard Worker with open(file_name, "w") as f: 218*8975f5c5SAndroid Build Coastguard Worker f.write(contents) 219*8975f5c5SAndroid Build Coastguard Worker os.chmod(file_name, 0o644) 220*8975f5c5SAndroid Build Coastguard Worker mtime = os.lstat(file_name).st_mtime 221*8975f5c5SAndroid Build Coastguard Worker time.sleep(0.1) 222*8975f5c5SAndroid Build Coastguard Worker version.WriteIfChanged(file_name, contents, 0o644) 223*8975f5c5SAndroid Build Coastguard Worker with open(file_name) as f: 224*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(contents, f.read()) 225*8975f5c5SAndroid Build Coastguard Worker self.assertEqual(mtime, os.lstat(file_name).st_mtime) 226*8975f5c5SAndroid Build Coastguard Worker 227*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__': 228*8975f5c5SAndroid Build Coastguard Worker unittest.main() 229