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