239 lines
10 KiB
Python
239 lines
10 KiB
Python
|
|
#!/usr/bin/env vpython3
|
||
|
|
# Copyright 2022 The Chromium Authors
|
||
|
|
# Use of this source code is governed by a BSD-style license that can be
|
||
|
|
# found in the LICENSE file.
|
||
|
|
"""File for testing compatible_utils.py."""
|
||
|
|
|
||
|
|
import io
|
||
|
|
import os
|
||
|
|
import stat
|
||
|
|
import tempfile
|
||
|
|
import unittest
|
||
|
|
import unittest.mock as mock
|
||
|
|
|
||
|
|
import compatible_utils
|
||
|
|
|
||
|
|
|
||
|
|
@unittest.skipIf(os.name == 'nt', 'Fuchsia tests not supported on Windows')
|
||
|
|
class CompatibleUtilsTest(unittest.TestCase):
|
||
|
|
"""Test compatible_utils.py methods."""
|
||
|
|
|
||
|
|
def test_running_unattended_returns_true_if_headless_set(self) -> None:
|
||
|
|
"""Test |running_unattended| returns True if CHROME_HEADLESS is set."""
|
||
|
|
with mock.patch('os.environ', {'SWARMING_SERVER': 0}):
|
||
|
|
self.assertTrue(compatible_utils.running_unattended())
|
||
|
|
|
||
|
|
with mock.patch('os.environ', {'FOO_HEADLESS': 0}):
|
||
|
|
self.assertFalse(compatible_utils.running_unattended())
|
||
|
|
|
||
|
|
def test_get_host_arch(self) -> None:
|
||
|
|
"""Test |get_host_arch| gets the host architecture and throws
|
||
|
|
exceptions on errors."""
|
||
|
|
supported_arches = ['x86_64', 'AMD64', 'aarch64']
|
||
|
|
with mock.patch('platform.machine', side_effect=supported_arches):
|
||
|
|
self.assertEqual(compatible_utils.get_host_arch(), 'x64')
|
||
|
|
self.assertEqual(compatible_utils.get_host_arch(), 'x64')
|
||
|
|
self.assertEqual(compatible_utils.get_host_arch(), 'arm64')
|
||
|
|
|
||
|
|
with mock.patch('platform.machine', return_value=['fake-arch']), \
|
||
|
|
self.assertRaises(NotImplementedError):
|
||
|
|
compatible_utils.get_host_arch()
|
||
|
|
|
||
|
|
def test_add_exec_to_file(self) -> None:
|
||
|
|
"""Test |add_exec_to_file| adds executable bit to file."""
|
||
|
|
with tempfile.NamedTemporaryFile() as f:
|
||
|
|
original_stat = os.stat(f.name).st_mode
|
||
|
|
self.assertFalse(original_stat & stat.S_IXUSR)
|
||
|
|
|
||
|
|
compatible_utils.add_exec_to_file(f.name)
|
||
|
|
|
||
|
|
new_stat = os.stat(f.name).st_mode
|
||
|
|
self.assertTrue(new_stat & stat.S_IXUSR)
|
||
|
|
|
||
|
|
# pylint: disable=no-self-use
|
||
|
|
def test_pave_adds_exec_to_binary_files(self) -> None:
|
||
|
|
"""Test |pave| calls |add_exec_to_file| on necessary files."""
|
||
|
|
with mock.patch('os.path.exists', return_value=True), \
|
||
|
|
mock.patch('compatible_utils.add_exec_to_file') as mock_exec, \
|
||
|
|
mock.patch('platform.machine', return_value='x86_64'), \
|
||
|
|
mock.patch('subprocess.run'):
|
||
|
|
compatible_utils.pave('some/path/to/dir', 'some-target')
|
||
|
|
|
||
|
|
mock_exec.assert_has_calls([
|
||
|
|
mock.call('some/path/to/dir/pave.sh'),
|
||
|
|
mock.call('some/path/to/dir/host_x64/bootserver')
|
||
|
|
],
|
||
|
|
any_order=True)
|
||
|
|
|
||
|
|
def test_pave_adds_exec_to_binary_files_if_pb_set_not_found(self) -> None:
|
||
|
|
"""Test |pave| calls |add_exec_to_file| on necessary files.
|
||
|
|
|
||
|
|
Checks if current product-bundle files exist. If not, defaults to
|
||
|
|
prebuilt-images set.
|
||
|
|
"""
|
||
|
|
with mock.patch('os.path.exists', return_value=False), \
|
||
|
|
mock.patch('compatible_utils.add_exec_to_file') as mock_exec, \
|
||
|
|
mock.patch('platform.machine', return_value='x86_64'), \
|
||
|
|
mock.patch('subprocess.run'):
|
||
|
|
compatible_utils.pave('some/path/to/dir', 'some-target')
|
||
|
|
|
||
|
|
mock_exec.assert_has_calls([
|
||
|
|
mock.call('some/path/to/dir/pave.sh'),
|
||
|
|
mock.call('some/path/to/dir/bootserver.exe.linux-x64')
|
||
|
|
],
|
||
|
|
any_order=True)
|
||
|
|
|
||
|
|
def test_pave_adds_target_id_if_given(self) -> None:
|
||
|
|
"""Test |pave| adds target-id to the arguments."""
|
||
|
|
with mock.patch('os.path.exists', return_value=False), \
|
||
|
|
mock.patch('compatible_utils.add_exec_to_file'), \
|
||
|
|
mock.patch('platform.machine', return_value='x86_64'), \
|
||
|
|
mock.patch('compatible_utils.get_ssh_keys',
|
||
|
|
return_value='authorized-keys-file'), \
|
||
|
|
mock.patch('subprocess.run') as mock_subproc:
|
||
|
|
mock_subproc.reset_mock()
|
||
|
|
compatible_utils.pave('some/path/to/dir', 'some-target')
|
||
|
|
|
||
|
|
mock_subproc.assert_called_once_with([
|
||
|
|
'some/path/to/dir/pave.sh', '--authorized-keys',
|
||
|
|
'authorized-keys-file', '-1', '-n', 'some-target'
|
||
|
|
],
|
||
|
|
check=True,
|
||
|
|
text=True,
|
||
|
|
timeout=300)
|
||
|
|
|
||
|
|
# pylint: disable=no-self-use
|
||
|
|
|
||
|
|
def test_parse_host_port_splits_address_and_strips_brackets(self) -> None:
|
||
|
|
"""Test |parse_host_port| splits ipv4 and ipv6 addresses correctly."""
|
||
|
|
self.assertEqual(compatible_utils.parse_host_port('hostname:55'),
|
||
|
|
('hostname', 55))
|
||
|
|
self.assertEqual(compatible_utils.parse_host_port('192.168.42.40:443'),
|
||
|
|
('192.168.42.40', 443))
|
||
|
|
self.assertEqual(
|
||
|
|
compatible_utils.parse_host_port('[2001:db8::1]:8080'),
|
||
|
|
('2001:db8::1', 8080))
|
||
|
|
|
||
|
|
def test_map_filter_filter_file_throws_value_error_if_wrong_path(self
|
||
|
|
) -> None:
|
||
|
|
"""Test |map_filter_file| throws ValueError if path is missing
|
||
|
|
FILTER_DIR."""
|
||
|
|
with self.assertRaises(ValueError):
|
||
|
|
compatible_utils.map_filter_file_to_package_file('foo')
|
||
|
|
|
||
|
|
with self.assertRaises(ValueError):
|
||
|
|
compatible_utils.map_filter_file_to_package_file('some/other/path')
|
||
|
|
|
||
|
|
with self.assertRaises(ValueError):
|
||
|
|
compatible_utils.map_filter_file_to_package_file('filters/file')
|
||
|
|
|
||
|
|
# No error.
|
||
|
|
compatible_utils.map_filter_file_to_package_file(
|
||
|
|
'testing/buildbot/filters/some.filter')
|
||
|
|
|
||
|
|
def test_map_filter_filter_replaces_filter_dir_with_pkg_path(self) -> None:
|
||
|
|
"""Test |map_filter_file| throws ValueError if path is missing
|
||
|
|
FILTER_DIR."""
|
||
|
|
self.assertEqual(
|
||
|
|
'/pkg/testing/buildbot/filters/some.filter',
|
||
|
|
compatible_utils.map_filter_file_to_package_file(
|
||
|
|
'foo/testing/buildbot/filters/some.filter'))
|
||
|
|
|
||
|
|
def test_get_sdk_hash_fallsback_to_args_file_if_buildargs_dne(self
|
||
|
|
) -> None:
|
||
|
|
"""Test |get_sdk_hash| checks if buildargs.gn exists.
|
||
|
|
|
||
|
|
If it does not, fallsback to args.gn. This should raise an exception
|
||
|
|
as it does not exist.
|
||
|
|
"""
|
||
|
|
with mock.patch('os.path.exists', return_value=False) as mock_exists, \
|
||
|
|
self.assertRaises(compatible_utils.VersionNotFoundError):
|
||
|
|
compatible_utils.get_sdk_hash('some/image/dir')
|
||
|
|
mock_exists.assert_has_calls([
|
||
|
|
mock.call('some/image/dir/buildargs.gn'),
|
||
|
|
mock.call('some/image/dir/args.gn')
|
||
|
|
])
|
||
|
|
|
||
|
|
def test_get_sdk_hash_parse_contents_of_args_file(self) -> None:
|
||
|
|
"""Test |get_sdk_hash| parses buildargs contents correctly."""
|
||
|
|
build_args_test_contents = """
|
||
|
|
build_info_board = "chromebook-x64"
|
||
|
|
build_info_product = "workstation_eng"
|
||
|
|
build_info_version = "10.20221114.2.1"
|
||
|
|
universe_package_labels += []
|
||
|
|
"""
|
||
|
|
with mock.patch('os.path.exists', return_value=True), \
|
||
|
|
mock.patch('builtins.open',
|
||
|
|
return_value=io.StringIO(build_args_test_contents)):
|
||
|
|
self.assertEqual(compatible_utils.get_sdk_hash('some/dir'),
|
||
|
|
('workstation_eng', '10.20221114.2.1'))
|
||
|
|
|
||
|
|
def test_get_sdk_hash_raises_error_if_keys_missing(self) -> None:
|
||
|
|
"""Test |get_sdk_hash| raises VersionNotFoundError if missing keys"""
|
||
|
|
build_args_test_contents = """
|
||
|
|
import("//boards/chromebook-x64.gni")
|
||
|
|
import("//products/workstation_eng.gni")
|
||
|
|
cxx_rbe_enable = true
|
||
|
|
host_labels += [ "//bundles/infra/build" ]
|
||
|
|
universe_package_labels += []
|
||
|
|
"""
|
||
|
|
with mock.patch('os.path.exists', return_value=True), \
|
||
|
|
mock.patch(
|
||
|
|
'builtins.open',
|
||
|
|
return_value=io.StringIO(build_args_test_contents)), \
|
||
|
|
self.assertRaises(compatible_utils.VersionNotFoundError):
|
||
|
|
compatible_utils.get_sdk_hash('some/dir')
|
||
|
|
|
||
|
|
def test_get_sdk_hash_raises_error_if_contents_empty(self) -> None:
|
||
|
|
"""Test |get_sdk_hash| raises VersionNotFoundError if no contents."""
|
||
|
|
with mock.patch('os.path.exists', return_value=True), \
|
||
|
|
mock.patch('builtins.open', return_value=io.StringIO("")), \
|
||
|
|
self.assertRaises(compatible_utils.VersionNotFoundError):
|
||
|
|
compatible_utils.get_sdk_hash('some/dir')
|
||
|
|
|
||
|
|
def trim_noop_prefixes(self, path):
|
||
|
|
"""Helper function to trim no-op path name prefixes that are
|
||
|
|
introduced by os.path.realpath on some platforms. These break
|
||
|
|
the unit tests, but have no actual effect on behavior."""
|
||
|
|
# These must all end in the path separator character for the
|
||
|
|
# string length computation to be correct on all platforms.
|
||
|
|
noop_prefixes = ['/private/']
|
||
|
|
for prefix in noop_prefixes:
|
||
|
|
if path.startswith(prefix):
|
||
|
|
return path[len(prefix) - 1:]
|
||
|
|
return path
|
||
|
|
|
||
|
|
def test_install_symbols(self):
|
||
|
|
|
||
|
|
"""Test |install_symbols|."""
|
||
|
|
|
||
|
|
with tempfile.TemporaryDirectory() as fuchsia_out_dir:
|
||
|
|
build_id = 'test_build_id'
|
||
|
|
symbol_file = os.path.join(fuchsia_out_dir, '.build-id',
|
||
|
|
build_id[:2], build_id[2:] + '.debug')
|
||
|
|
id_path = os.path.join(fuchsia_out_dir, 'ids.txt')
|
||
|
|
try:
|
||
|
|
binary_relpath = 'path/to/binary'
|
||
|
|
with open(id_path, 'w') as f:
|
||
|
|
f.write(f'{build_id} {binary_relpath}')
|
||
|
|
compatible_utils.install_symbols([id_path], fuchsia_out_dir)
|
||
|
|
self.assertTrue(os.path.islink(symbol_file))
|
||
|
|
self.assertEqual(
|
||
|
|
self.trim_noop_prefixes(os.path.realpath(symbol_file)),
|
||
|
|
os.path.join(fuchsia_out_dir, binary_relpath))
|
||
|
|
|
||
|
|
new_binary_relpath = 'path/to/new/binary'
|
||
|
|
with open(id_path, 'w') as f:
|
||
|
|
f.write(f'{build_id} {new_binary_relpath}')
|
||
|
|
compatible_utils.install_symbols([id_path], fuchsia_out_dir)
|
||
|
|
self.assertTrue(os.path.islink(symbol_file))
|
||
|
|
self.assertEqual(
|
||
|
|
self.trim_noop_prefixes(os.path.realpath(symbol_file)),
|
||
|
|
os.path.join(fuchsia_out_dir, new_binary_relpath))
|
||
|
|
finally:
|
||
|
|
os.remove(id_path)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
unittest.main()
|