blob: 254984f533d64f6699504194399635b118667121 [file]
#!/usr/bin/env vpython3
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for test_suite_mapper.py."""
import contextlib
import io
import pathlib
import unittest
from unittest.mock import MagicMock, patch
from pyfakefs import fake_filesystem_unittest
import test_suite_mapper
MOCK_ISOLATE_MAP = {
'//chrome/test:browser_tests': 'browser_tests',
'//chrome/test:unit_tests': 'unit_tests',
'//media/midi:midi_unittests': 'midi_unittests',
'//media:media_unittests': 'media_unittests',
'//chrome/browser:foo_tests': 'foo_tests',
'//content/test:bar_tests': 'bar_tests',
'//foo/bar:baz_tests': 'baz_tests',
'//base:base_unittests': 'base_unittests',
'//:blink_wpt_tests': 'blink_wpt_tests',
}
class TestSuiteMapperUnit(fake_filesystem_unittest.TestCase):
def setUp(self):
self.setUpPyfakefs()
self.subprocess_patcher = patch('subprocess.run')
self.mock_run = self.subprocess_patcher.start()
self.addCleanup(self.subprocess_patcher.stop)
def test_common_dir_prefix_len(self):
self.assertEqual(
test_suite_mapper.common_dir_prefix_len('a/b/c', 'a/b/c'), 3
)
self.assertEqual(
test_suite_mapper.common_dir_prefix_len('a/b/c', 'a/b/d'), 2
)
self.assertEqual(
test_suite_mapper.common_dir_prefix_len('a/b/c', 'x/y/z'), 0
)
self.assertEqual(test_suite_mapper.common_dir_prefix_len('a/b/c', ''), 0)
self.assertEqual(test_suite_mapper.common_dir_prefix_len('a/b', 'a/b/c'), 2)
def test_parse_isolate_map_valid(self):
content = (
"{'browser_tests': {'label': '//chrome/test:browser_tests'},"
" 'unit_tests': {'label': '//chrome/test:unit_tests'}}"
)
expected = {
'//chrome/test:browser_tests': 'browser_tests',
'//chrome/test:unit_tests': 'unit_tests',
}
self.assertEqual(
test_suite_mapper.parse_isolate_map(content, 'test'), expected
)
def test_parse_isolate_map_missing_label(self):
content = "{'suite1': {'type': 'additional_compile_target'}}"
self.assertEqual(test_suite_mapper.parse_isolate_map(content, 'test'), {})
def test_parse_isolate_map_invalid(self):
content = 'invalid dict'
f = io.StringIO()
with contextlib.redirect_stderr(f):
self.assertIsNone(test_suite_mapper.parse_isolate_map(content, 'test'))
self.assertTrue(len(f.getvalue()) > 0)
def test_parse_isolate_map_not_dict(self):
content = '[1, 2, 3]'
f = io.StringIO()
with contextlib.redirect_stderr(f):
self.assertIsNone(test_suite_mapper.parse_isolate_map(content, 'test'))
self.assertIn('Expected a dictionary', f.getvalue())
def test_load_isolate_map_success(self):
self.fs.create_dir('/src')
pyl_path = pathlib.Path('/src') / test_suite_mapper.ISOLATE_MAP_PATH
self.fs.create_file(
pyl_path,
contents="{'browser_tests': {'label': '//chrome/test:browser_tests'}}",
)
isolate_map = test_suite_mapper.load_isolate_map('/src')
self.assertEqual(
isolate_map, {'//chrome/test:browser_tests': 'browser_tests'}
)
def test_load_isolate_map_missing_file(self):
self.fs.create_dir('/src')
f = io.StringIO()
with contextlib.redirect_stderr(f):
self.assertIsNone(test_suite_mapper.load_isolate_map('/src'))
self.assertIn('Isolate map not found', f.getvalue())
def test_load_isolate_map_read_error(self):
self.fs.create_dir('/src')
pyl_path = pathlib.Path('/src') / test_suite_mapper.ISOLATE_MAP_PATH
self.fs.create_dir(pyl_path)
f = io.StringIO()
with contextlib.redirect_stderr(f):
self.assertIsNone(test_suite_mapper.load_isolate_map('/src'))
self.assertIn('Error reading', f.getvalue())
def test_extract_test_suites_basic(self):
refs = [
'//chrome/browser:foo_tests',
'//content/test:bar_tests',
'//foo/bar:baz_tests',
]
suites = test_suite_mapper.extract_test_suites(
refs, pathlib.Path('chrome/browser/foo.cc'), MOCK_ISOLATE_MAP
)
self.assertEqual(suites, ['foo_tests'])
def test_extract_test_suites_proximity(self):
refs = [
'//chrome/test:browser_tests',
'//media/midi:midi_unittests',
]
suites = test_suite_mapper.extract_test_suites(
refs, pathlib.Path('media/midi/midi_manager.cc'), MOCK_ISOLATE_MAP
)
self.assertEqual(suites, ['midi_unittests'])
def test_extract_test_suites_filter_non_isolate(self):
refs = [
'//media/base:unit_tests',
'//media:media_unittests',
]
suites = test_suite_mapper.extract_test_suites(
refs,
pathlib.Path('media/base/audio_bus_unittest.cc'),
MOCK_ISOLATE_MAP,
)
self.assertEqual(suites, ['media_unittests'])
def test_extract_test_suites_empty_target(self):
refs = ['//chrome/browser:foo_tests', '', ' ']
suites = test_suite_mapper.extract_test_suites(
refs, pathlib.Path('chrome/browser/foo.cc'), MOCK_ISOLATE_MAP
)
self.assertEqual(suites, ['foo_tests'])
def test_extract_test_suites_no_match(self):
refs = ['//unrelated:target']
suites = test_suite_mapper.extract_test_suites(
refs, pathlib.Path('chrome/browser/foo.cc'), MOCK_ISOLATE_MAP
)
self.assertEqual(suites, [])
def test_extract_test_suites_all_zero_score(self):
refs = ['//media/midi:midi_unittests', '//foo/bar:baz_tests']
suites = test_suite_mapper.extract_test_suites(
refs, pathlib.Path('chrome/browser/foo.cc'), MOCK_ISOLATE_MAP
)
self.assertEqual(sorted(suites), ['baz_tests', 'midi_unittests'])
def test_verify_build_dir_valid(self):
self.fs.create_file(
'/src/out/Default/args.gn',
contents=(
'# This is a comment\ntarget_os = "linux"\n\nuse_goma = true\n'
),
)
success, err = test_suite_mapper.verify_build_dir('/src', 'out/Default')
self.assertTrue(success)
self.assertIsNone(err)
def test_verify_build_dir_missing_file(self):
self.fs.create_dir('/src')
success, err = test_suite_mapper.verify_build_dir('/src', 'out/Default')
self.assertFalse(success)
self.assertIn('args.gn not found', err)
def test_verify_build_dir_invalid_os(self):
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "android"\n'
)
success, err = test_suite_mapper.verify_build_dir('/src', 'out/Default')
self.assertFalse(success)
self.assertIn('Unsupported target_os: android', err)
@patch('sys.platform', 'linux')
def test_verify_build_dir_no_target_os_on_linux(self):
self.fs.create_file(
'/src/out/Default/args.gn', contents='is_debug = true\n'
)
success, err = test_suite_mapper.verify_build_dir('/src', 'out/Default')
self.assertTrue(success)
self.assertIsNone(err)
@patch('sys.platform', 'darwin')
def test_verify_build_dir_no_target_os_on_mac(self):
self.fs.create_file(
'/src/out/Default/args.gn', contents='is_debug = true\n'
)
success, err = test_suite_mapper.verify_build_dir('/src', 'out/Default')
self.assertFalse(success)
self.assertIn('Host platform darwin is not Linux', err)
def test_run_gn_refs_calls_gn(self):
self.mock_run.return_value = MagicMock(
returncode=0, stdout='//foo:foo_unittests\n', stderr=''
)
suites, err = test_suite_mapper.run_gn_refs(
'/src', 'out/Default', pathlib.Path('foo/bar.cc')
)
self.assertEqual(suites, ['//foo:foo_unittests'])
self.assertIsNone(err)
self.mock_run.assert_called_once_with(
[
'gn',
'refs',
'out/Default',
'--all',
'//foo/bar.cc',
],
capture_output=True,
text=True,
cwd='/src',
check=False,
encoding='utf-8',
)
def test_run_gn_refs_failure(self):
self.mock_run.return_value = MagicMock(
returncode=1, stdout='', stderr='GN error message\n'
)
suites, err = test_suite_mapper.run_gn_refs(
'/src', 'out/Default', pathlib.Path('foo/bar.cc')
)
self.assertIsNone(suites)
self.assertEqual(err, 'GN error message')
def test_run_gn_refs_exception(self):
self.mock_run.side_effect = OSError('Command not found')
suites, err = test_suite_mapper.run_gn_refs(
'/src', 'out/Default', pathlib.Path('foo/bar.cc')
)
self.assertIsNone(suites)
self.assertEqual(err, 'Command not found')
def test_get_build_gn_paths(self):
refs = [
'//chrome/browser:foo_tests',
'//content/test:bar_tests',
'//:blink_wpt_tests',
'invalid_target',
]
paths = test_suite_mapper.get_build_gn_paths(refs)
self.assertEqual(
paths,
['BUILD.gn', 'chrome/browser/BUILD.gn', 'content/test/BUILD.gn'],
)
def test_check_graph_stability_stable(self):
self.mock_run.return_value = MagicMock(returncode=0, stdout='')
refs = ['//chrome/browser:foo_tests']
stable, changed = test_suite_mapper.check_graph_stability(
'/src', 'abc123f', 'chrome/browser/foo.cc', refs
)
self.assertTrue(stable)
self.assertEqual(changed, [])
self.mock_run.assert_called_once_with(
[
'git',
'diff',
'--name-only',
'abc123f..HEAD',
'--',
'chrome/browser/foo.cc',
'chrome/browser/BUILD.gn',
],
capture_output=True,
text=True,
cwd='/src',
check=False,
encoding='utf-8',
)
def test_check_graph_stability_unstable(self):
self.mock_run.return_value = MagicMock(
returncode=0,
stdout='chrome/browser/BUILD.gn\n',
)
refs = ['//chrome/browser:foo_tests']
stable, changed = test_suite_mapper.check_graph_stability(
'/src', 'abc123f', 'chrome/browser/foo.cc', refs
)
self.assertFalse(stable)
self.assertEqual(changed, ['chrome/browser/BUILD.gn'])
def test_check_graph_stability_git_fail(self):
self.mock_run.return_value = MagicMock(
returncode=1, stderr='Git diff error'
)
f = io.StringIO()
with contextlib.redirect_stderr(f):
stable, changed = test_suite_mapper.check_graph_stability(
'/src', 'abc123f', 'chrome/browser/foo.cc', ['//chrome:foo']
)
self.assertFalse(stable)
self.assertEqual(changed, [])
self.assertIn('Stability check failed', f.getvalue())
def test_get_ignored_paths(self):
self.mock_run.return_value = MagicMock(
returncode=0,
stdout=('!! out/\n!! build/config/gclient_args.gni\n?? untracked_file\n'),
)
ignored = test_suite_mapper.get_ignored_paths('/src')
self.assertEqual(ignored, ['out', 'build/config/gclient_args.gni'])
def test_find_nested_repos_and_symlinks(self):
self.fs.create_dir('/src/out')
self.fs.create_dir('/src/.git')
self.fs.create_dir('/src/third_party/foo')
self.fs.create_file('/src/third_party/foo/.git')
self.fs.create_dir('/src/third_party/bar/.git')
self.fs.create_dir('/src/third_party/baz')
# Deep nested repo (depth > 5)
self.fs.create_file('/src/a/b/c/d/e/f/.git')
# Symlinks
self.fs.create_file('/src/third_party/baz/real_file')
self.fs.create_symlink(
'/src/third_party/link_to_file', '/src/third_party/baz/real_file'
)
self.fs.create_symlink(
'/src/third_party/link_to_dir', '/src/third_party/baz'
)
self.fs.create_symlink(
'/src/out/bad_link', '/src/third_party/baz/real_file'
)
# Symlink inside a nested repo should be skipped
self.fs.create_symlink(
'/src/third_party/foo/nested_link', '/src/third_party/baz/real_file'
)
targets = test_suite_mapper.find_nested_repos_and_symlinks('/src')
expected = [
'third_party/bar',
'third_party/foo',
'third_party/link_to_dir',
'third_party/link_to_file',
]
self.assertEqual(sorted(targets), expected)
def test_stub_missing_target(self):
wt_path = pathlib.Path('/wt')
self.fs.create_dir('/wt')
target_ref = '//third_party/foo:bar'
success = test_suite_mapper._stub_missing_target(wt_path, target_ref)
self.assertTrue(success)
build_gn_path = wt_path / 'third_party' / 'foo' / 'BUILD.gn'
self.assertTrue(build_gn_path.exists())
content = build_gn_path.read_text(encoding='utf-8')
self.assertIn('group("bar") {}', content)
def test_stub_missing_target_failure(self):
wt_path = pathlib.Path('/wt')
self.fs.create_file('/wt/third_party')
target_ref = '//third_party/foo:bar'
f = io.StringIO()
with contextlib.redirect_stderr(f):
success = test_suite_mapper._stub_missing_target(wt_path, target_ref)
self.assertFalse(success)
self.assertTrue(len(f.getvalue()) > 0)
def test_stub_missing_target_invalid_ref(self):
wt_path = pathlib.Path('/wt')
self.assertFalse(test_suite_mapper._stub_missing_target(wt_path, 'foo:bar'))
self.assertFalse(
test_suite_mapper._stub_missing_target(wt_path, '//foo/bar')
)
def test_spoof_rust_version_success(self):
wt_path = pathlib.Path('/wt')
self.fs.create_file(
'/wt/tools/rust/update_rust.py',
contents=(
"RUST_REVISION = 'aaaa05163abcbd08948b3efab796c543ba1ea687'\n"
"RUST_SUB_REVISION = 2\n"
),
)
src_dir = pathlib.Path('/src_rust')
self.fs.create_file(
'/src_rust/VERSION',
contents=(
'rustc 1.96.0 4c4205163abcbd08948b3efab796c543ba1ea687 '
'(4c4205163abcbd08948b3efab796c543ba1ea687-5-'
'llvmorg-23-init-10931-g20b6ec66 chromium)\n'
),
)
dest_dir = pathlib.Path('/dest_rust')
self.fs.create_dir('/dest_rust')
success = test_suite_mapper._spoof_rust_version(wt_path, src_dir, dest_dir)
self.assertTrue(success)
dest_version = dest_dir / 'VERSION'
self.assertTrue(dest_version.exists())
content = dest_version.read_text(encoding='utf-8')
self.assertEqual(
content,
'rustc 1.96.0 aaaa05163abcbd08948b3efab796c543ba1ea687 '
'(aaaa05163abcbd08948b3efab796c543ba1ea687-2-'
'llvmorg-23-init-10931-g20b6ec66 chromium)\n',
)
def test_spoof_rust_version_missing_update_rust(self):
wt_path = pathlib.Path('/wt')
self.fs.create_dir('/wt')
self.assertFalse(
test_suite_mapper._spoof_rust_version(
wt_path, pathlib.Path('/src'), pathlib.Path('/dest')
)
)
def test_spoof_rust_version_invalid_update_rust(self):
wt_path = pathlib.Path('/wt')
self.fs.create_file(
'/wt/tools/rust/update_rust.py', contents='invalid content\n'
)
self.assertFalse(
test_suite_mapper._spoof_rust_version(
wt_path, pathlib.Path('/src'), pathlib.Path('/dest')
)
)
def test_spoof_rust_version_missing_src_version(self):
wt_path = pathlib.Path('/wt')
self.fs.create_file(
'/wt/tools/rust/update_rust.py',
contents="RUST_REVISION = 'aaaa'\nRUST_SUB_REVISION = 1\n",
)
self.assertFalse(
test_suite_mapper._spoof_rust_version(
wt_path, pathlib.Path('/src'), pathlib.Path('/dest')
)
)
def test_spoof_rust_version_invalid_src_version_format(self):
wt_path = pathlib.Path('/wt')
self.fs.create_file(
'/wt/tools/rust/update_rust.py',
contents="RUST_REVISION = 'aaaa'\nRUST_SUB_REVISION = 1\n",
)
src_dir = pathlib.Path('/src')
self.fs.create_file(src_dir / 'VERSION', contents='invalid format\n')
self.assertFalse(
test_suite_mapper._spoof_rust_version(
wt_path, src_dir, pathlib.Path('/dest')
)
)
@patch('test_suite_mapper.get_ignored_paths')
@patch('test_suite_mapper.find_nested_repos_and_symlinks')
def test_setup_worktree_symlinks(self, mock_find, mock_get_ignored):
mock_get_ignored.return_value = ['third_party/llvm-build', 'out']
mock_find.return_value = ['third_party/foo', 'third_party/link_to_file']
self.fs.create_dir('/src')
self.fs.create_dir('/src/third_party/llvm-build')
self.fs.create_dir('/src/third_party/foo')
self.fs.create_file('/src/third_party/link_to_file')
self.fs.create_dir('/src/out/Default')
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "linux"\n'
)
self.fs.create_file('/src/out/Default/other_file', contents='ignored\n')
self.fs.create_dir('/wt')
test_suite_mapper._setup_worktree_symlinks(
pathlib.Path('/src'), pathlib.Path('/wt'), 'out/Default'
)
self.assertTrue(pathlib.Path('/wt/third_party/llvm-build').is_symlink())
self.assertTrue(pathlib.Path('/wt/third_party/foo').is_symlink())
self.assertTrue(pathlib.Path('/wt/third_party/link_to_file').is_symlink())
self.assertFalse(pathlib.Path('/wt/out').is_symlink())
self.assertFalse(pathlib.Path('/wt/out').exists())
@patch('test_suite_mapper.get_ignored_paths')
@patch('test_suite_mapper.find_nested_repos_and_symlinks')
@patch('test_suite_mapper._spoof_rust_version')
def test_setup_worktree_symlinks_rust_toolchain(
self, mock_spoof, mock_find, mock_get_ignored
):
mock_get_ignored.return_value = [
test_suite_mapper.RUST_TOOLCHAIN_PATH.as_posix()
]
mock_find.return_value = []
mock_spoof.return_value = True
self.fs.create_dir('/src')
rust_dir = pathlib.Path('/src') / test_suite_mapper.RUST_TOOLCHAIN_PATH
self.fs.create_file(rust_dir / 'bin/rustc')
self.fs.create_file(rust_dir / 'VERSION')
self.fs.create_dir('/wt')
self.fs.create_file(
pathlib.Path('/wt') / test_suite_mapper.UPDATE_RUST_PATH
)
test_suite_mapper._setup_worktree_symlinks(
pathlib.Path('/src'), pathlib.Path('/wt'), 'out/Default'
)
link_rust_dir = pathlib.Path('/wt') / test_suite_mapper.RUST_TOOLCHAIN_PATH
self.assertTrue((link_rust_dir / 'bin').is_symlink())
self.assertTrue((link_rust_dir / 'bin/rustc').exists())
self.assertFalse((link_rust_dir / 'VERSION').exists())
mock_spoof.assert_called_once()
def test_run_gn_gen_with_stubbing_success_immediate(self):
self.mock_run.return_value = MagicMock(returncode=0)
success = test_suite_mapper._run_gn_gen_with_stubbing(
pathlib.Path('/wt'), 'out/Default'
)
self.assertTrue(success)
self.mock_run.assert_called_once_with(
['gn', 'gen', 'out/Default'],
capture_output=True,
text=True,
cwd=pathlib.Path('/wt'),
check=False,
encoding='utf-8',
)
def test_run_gn_gen_with_stubbing_retry_success(self):
self.mock_run.side_effect = [
MagicMock(
returncode=1,
stdout=(
'ERROR Unresolved dependencies.\n //foo:bar needs'
' //third_party/baz:qux\n'
),
stderr='',
),
MagicMock(returncode=0),
]
self.fs.create_dir('/wt')
success = test_suite_mapper._run_gn_gen_with_stubbing(
pathlib.Path('/wt'), 'out/Default'
)
self.assertTrue(success)
self.assertEqual(self.mock_run.call_count, 2)
stub_path = pathlib.Path('/wt/third_party/baz/BUILD.gn')
self.assertTrue(stub_path.exists())
self.assertIn('group("qux") {}', stub_path.read_text(encoding='utf-8'))
def test_run_gn_gen_with_stubbing_failure_loop(self):
self.mock_run.return_value = MagicMock(
returncode=1,
stdout=(
'ERROR Unresolved dependencies.\n //foo:bar needs'
' //third_party/baz:qux\n'
),
stderr='',
)
self.fs.create_dir('/wt')
f = io.StringIO()
with contextlib.redirect_stderr(f):
success = test_suite_mapper._run_gn_gen_with_stubbing(
pathlib.Path('/wt'), 'out/Default'
)
self.assertFalse(success)
self.assertIn('already stubbed', f.getvalue())
def test_run_heavyweight_resolution_success(self):
isolate_map_content = (
"{'browser_tests': {'label': '//chrome/test:browser_tests'}}"
)
def mock_run_side_effect(cmd, *args, **kwargs):
if 'worktree' in cmd and 'add' in cmd:
wt_path = cmd[5]
self.fs.create_file(
pathlib.Path(wt_path) / test_suite_mapper.ISOLATE_MAP_PATH,
contents=isolate_map_content,
)
return MagicMock(returncode=0)
elif 'gen' in cmd:
return MagicMock(returncode=0)
elif 'refs' in cmd:
return MagicMock(
returncode=0,
stdout='//chrome/test:browser_tests\n',
stderr='',
)
return MagicMock(returncode=0)
self.mock_run.side_effect = mock_run_side_effect
self.fs.create_dir('/src')
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "linux"\n'
)
with (
patch('test_suite_mapper.get_ignored_paths', return_value=[]),
patch(
'test_suite_mapper.find_nested_repos_and_symlinks', return_value=[]
),
):
suites = test_suite_mapper.run_heavyweight_resolution(
pathlib.Path('/src'),
'abc123f',
'out/Default',
pathlib.Path('chrome/browser/foo.cc'),
)
self.assertEqual(suites, ['browser_tests'])
# Verify worktree add and remove were called with the same path
add_path = None
remove_path = None
for call in self.mock_run.call_args_list:
cmd = call[0][0]
if 'worktree' in cmd and 'add' in cmd:
add_path = cmd[5]
if 'worktree' in cmd and 'remove' in cmd:
remove_path = cmd[4]
self.assertIsNotNone(add_path)
self.assertIsNotNone(remove_path)
self.assertEqual(add_path, remove_path)
def test_run_heavyweight_resolution_worktree_fail(self):
self.mock_run.return_value = MagicMock(returncode=1, stderr='Git error')
f = io.StringIO()
with contextlib.redirect_stderr(f):
suites = test_suite_mapper.run_heavyweight_resolution(
pathlib.Path('/src'),
'abc123f',
'out/Default',
pathlib.Path('chrome/browser/foo.cc'),
)
self.assertIsNone(suites)
self.assertIn('Error creating git worktree', f.getvalue())
def test_run_heavyweight_resolution_gn_gen_fail(self):
self.mock_run.side_effect = [
MagicMock(returncode=0), # worktree add
MagicMock(returncode=1, stdout='GN gen error', stderr=''), # gn gen fail
MagicMock(returncode=0), # worktree remove
]
self.fs.create_dir('/src')
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "linux"\n'
)
f = io.StringIO()
with (
patch('test_suite_mapper.get_ignored_paths', return_value=[]),
patch(
'test_suite_mapper.find_nested_repos_and_symlinks', return_value=[]
),
contextlib.redirect_stderr(f),
):
suites = test_suite_mapper.run_heavyweight_resolution(
pathlib.Path('/src'),
'abc123f',
'out/Default',
pathlib.Path('chrome/browser/foo.cc'),
)
self.assertIsNone(suites)
def test_run_heavyweight_resolution_gn_refs_fail(self):
self.mock_run.side_effect = [
MagicMock(returncode=0), # worktree add
MagicMock(returncode=0), # gn gen
MagicMock(
returncode=1, stdout='', stderr='GN refs error'
), # gn refs fail
MagicMock(returncode=0), # worktree remove
]
self.fs.create_dir('/src')
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "linux"\n'
)
f = io.StringIO()
with (
patch('test_suite_mapper.get_ignored_paths', return_value=[]),
patch(
'test_suite_mapper.find_nested_repos_and_symlinks', return_value=[]
),
contextlib.redirect_stderr(f),
):
suites = test_suite_mapper.run_heavyweight_resolution(
pathlib.Path('/src'),
'abc123f',
'out/Default',
pathlib.Path('chrome/browser/foo.cc'),
)
self.assertIsNone(suites)
def test_run_heavyweight_resolution_missing_isolate_map(self):
self.mock_run.side_effect = [
MagicMock(returncode=0), # worktree add
MagicMock(returncode=0), # gn gen
MagicMock(returncode=0, stdout='//foo:bar\n', stderr=''), # gn refs
MagicMock(returncode=0), # worktree remove
]
self.fs.create_dir('/src')
self.fs.create_file(
'/src/out/Default/args.gn', contents='target_os = "linux"\n'
)
f = io.StringIO()
with (
patch('test_suite_mapper.get_ignored_paths', return_value=[]),
patch(
'test_suite_mapper.find_nested_repos_and_symlinks', return_value=[]
),
contextlib.redirect_stderr(f),
):
suites = test_suite_mapper.run_heavyweight_resolution(
pathlib.Path('/src'),
'abc123f',
'out/Default',
pathlib.Path('chrome/browser/foo.cc'),
)
self.assertIsNone(suites)
self.assertIn('Isolate map not found in worktree', f.getvalue())
def test_load_isolate_map_for_args_with_file(self):
content = "{'suite1': {'label': '//foo:bar'}}"
self.fs.create_file('/src/map.pyl', contents=content)
res = test_suite_mapper._load_isolate_map_for_args(
'/src', '/src/map.pyl', None, False
)
self.assertEqual(res, {'//foo:bar': 'suite1'})
with self.assertRaises(SystemExit):
test_suite_mapper._load_isolate_map_for_args(
'/src', '/src/missing.pyl', None, False
)
def test_load_isolate_map_for_args_revision(self):
with self.assertRaises(SystemExit):
test_suite_mapper._load_isolate_map_for_args(
'/src', None, 'abc123f', False
)
res = test_suite_mapper._load_isolate_map_for_args(
'/src', None, 'abc123f', True
)
self.assertIsNone(res)
def test_load_isolate_map_for_args_default(self):
with patch(
'test_suite_mapper.load_isolate_map', return_value={'a': 'b'}
) as mock_load:
res = test_suite_mapper._load_isolate_map_for_args(
'/src', None, None, False
)
self.assertEqual(res, {'a': 'b'})
mock_load.assert_called_once_with('/src')
def test_check_and_warn_stability_stable(self):
with patch(
'test_suite_mapper.check_graph_stability', return_value=(True, [])
):
stable = test_suite_mapper._check_and_warn_stability(
'/src', 'abc123f', pathlib.Path('foo.cc'), ['//foo']
)
self.assertTrue(stable)
def test_check_and_warn_stability_unstable(self):
with patch(
'test_suite_mapper.check_graph_stability',
return_value=(False, ['BUILD.gn']),
):
f = io.StringIO()
with contextlib.redirect_stderr(f):
stable = test_suite_mapper._check_and_warn_stability(
'/src', 'abc123f', pathlib.Path('foo.cc'), ['//foo']
)
self.assertFalse(stable)
self.assertIn(
'Warning: The GN graph or the file may have changed', f.getvalue()
)
self.assertIn('BUILD.gn', f.getvalue())
with patch(
'test_suite_mapper.check_graph_stability',
return_value=(False, ['f1', 'f2', 'f3', 'f4', 'f5', 'f6']),
):
f = io.StringIO()
with contextlib.redirect_stderr(f):
stable = test_suite_mapper._check_and_warn_stability(
'/src', 'abc123f', pathlib.Path('foo.cc'), ['//foo']
)
self.assertFalse(stable)
self.assertIn('f1, f2, f3, f4, f5 and 1 more', f.getvalue())
def test_should_run_heavyweight(self):
self.assertTrue(
test_suite_mapper._should_run_heavyweight(
'/src', 'abc123f', pathlib.Path('foo.cc'), ['//foo'], True, True, []
)
)
self.assertFalse(
test_suite_mapper._should_run_heavyweight(
'/src', 'abc123f', pathlib.Path('foo.cc'), ['//foo'], False, True, []
)
)
with patch(
'test_suite_mapper.load_isolate_map', return_value={'//foo': 'suite'}
):
self.assertFalse(
test_suite_mapper._should_run_heavyweight(
'/src',
'abc123f',
pathlib.Path('foo.cc'),
['//foo'],
False,
False,
['suite'],
)
)
with patch(
'test_suite_mapper.load_isolate_map', return_value={'//foo': 'suite2'}
):
f = io.StringIO()
with contextlib.redirect_stderr(f):
res = test_suite_mapper._should_run_heavyweight(
'/src',
'abc123f',
pathlib.Path('foo.cc'),
['//foo'],
False,
False,
['suite1'],
)
self.assertTrue(res)
self.assertIn('Difference detected', f.getvalue())
@patch('test_suite_mapper.run_heavyweight_resolution')
def test_execute_heavyweight_resolution_success(self, mock_resolve):
mock_resolve.return_value = ['suite1']
f = io.StringIO()
with contextlib.redirect_stderr(f):
res = test_suite_mapper._execute_heavyweight_resolution(
'/src', 'abc123f', 'out/Default', pathlib.Path('foo.cc'), ['suite1']
)
self.assertEqual(res, ['suite1'])
f = io.StringIO()
with contextlib.redirect_stderr(f):
res = test_suite_mapper._execute_heavyweight_resolution(
'/src', 'abc123f', 'out/Default', pathlib.Path('foo.cc'), ['suite2']
)
self.assertEqual(res, ['suite1'])
self.assertIn('Results corrected', f.getvalue())
@patch('test_suite_mapper.run_heavyweight_resolution')
def test_execute_heavyweight_resolution_fail(self, mock_resolve):
mock_resolve.return_value = None
f = io.StringIO()
with contextlib.redirect_stderr(f):
res = test_suite_mapper._execute_heavyweight_resolution(
'/src', 'abc123f', 'out/Default', pathlib.Path('foo.cc'), ['suite1']
)
self.assertEqual(res, ['suite1'])
self.assertIn('Falling back to lightweight results', f.getvalue())
def test_execute_heavyweight_resolution_no_rev(self):
with self.assertRaises(SystemExit):
test_suite_mapper._execute_heavyweight_resolution(
'/src', None, 'out/Default', pathlib.Path('foo.cc'), []
)
if __name__ == '__main__':
unittest.main()