xref: /aosp_15_r20/external/angle/build/util/version_test.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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