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