This PR changes how we structure path resolution around all the utilities under `tools/cr` to be truly relative, with all paths ultimately being derived from the path for the brave repository. This should make testing in sandboxing more reliable, as we can just rely on the CWD to override behaviour in a consistent way across multiple tools, even if they are being launched as a subprocess, which is not possible when using mock patching for global constants. This change eleminates the need for `FakeChromiumSrc` as its main use was to patch `Repository` to use the path of the `tempdir` provided by `FakeChromiumRepo`. After this change, all should stay the same, however this change also does away with `BRAVE_CORE_PATH`, and replaces it with `repository.brave.root` as a more idiomatic way to indicate a `brave-core`'s root path. The main side effect of this change is that now both `brockt`, and `plaster` should just work by being called anywhere under `brave/`. Resolves https://github.com/brave/brave-browser/issues/55404
811 lines
36 KiB
Python
Executable File
811 lines
36 KiB
Python
Executable File
#!/usr/bin/env vpython3
|
|
# Copyright (c) 2025 The Brave Authors. All rights reserved.
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
import unittest
|
|
from pathlib import Path
|
|
from patchfile import Patchfile
|
|
import repository
|
|
from repository import Repository
|
|
|
|
from test.fake_chromium_repo import FakeChromiumRepo
|
|
|
|
|
|
class PatchfileTest(unittest.TestCase):
|
|
"""Test the patchfile generation and application."""
|
|
|
|
def setUp(self):
|
|
"""Set up a fake Chromium repository for testing."""
|
|
self.fake_chromium_src = FakeChromiumRepo()
|
|
self.fake_chromium_src.setup()
|
|
self.fake_chromium_src.add_dep('v8')
|
|
self.fake_chromium_src.add_dep('third_party/test1')
|
|
self.fake_chromium_src.add_dep('third_party/devtools-frontend/src')
|
|
self.addCleanup(self.fake_chromium_src.cleanup)
|
|
|
|
def test_get_repository_from_patch_name(self):
|
|
"""Test the result of get_repository_from_patch_name"""
|
|
dex_py = Path('build/android/gyp/dex.py')
|
|
for repo in [
|
|
self.fake_chromium_src.chromium,
|
|
self.fake_chromium_src.chromium / 'v8',
|
|
self.fake_chromium_src.chromium / 'third_party/test1'
|
|
]:
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
dex_py, 'original\n', repo)
|
|
self.fake_chromium_src.commit('Add dex.py', repo)
|
|
(repo / dex_py).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
patchfile = Patchfile(
|
|
path=Path("patches/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(patchfile.get_repository_from_patch_name(),
|
|
repository.chromium)
|
|
|
|
patchfile = Patchfile(
|
|
path=Path("patches/v8/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(patchfile.get_repository_from_patch_name(),
|
|
Repository(repository.chromium.root / 'v8'))
|
|
|
|
patchfile = Patchfile(path=Path(
|
|
"patches/third_party/test1/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(
|
|
patchfile.get_repository_from_patch_name(),
|
|
Repository(repository.chromium.root / 'third_party/test1'))
|
|
|
|
def test_source_name_from_patch_naming(self):
|
|
"""Test patched file name name heuristics."""
|
|
dex_py = Path('build/android/gyp/dex.py')
|
|
for repo in [
|
|
self.fake_chromium_src.chromium,
|
|
self.fake_chromium_src.chromium / 'v8',
|
|
self.fake_chromium_src.chromium / 'third_party/test1'
|
|
]:
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
dex_py, 'original\n', repo)
|
|
self.fake_chromium_src.commit('Add dex.py', repo)
|
|
(repo / dex_py).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
patchfile = Patchfile(
|
|
path=Path("patches/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(patchfile.source_name_from_patch_naming(),
|
|
"build/android/gyp/dex.py")
|
|
|
|
patchfile = Patchfile(
|
|
path=Path("patches/v8/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(patchfile.source_name_from_patch_naming(),
|
|
"build/android/gyp/dex.py")
|
|
|
|
patchfile = Patchfile(path=Path(
|
|
"patches/third_party/test1/build-android-gyp-dex.py.patch"))
|
|
self.assertEqual(patchfile.source_name_from_patch_naming(),
|
|
"build/android/gyp/dex.py")
|
|
|
|
def test_apply_conflict(self):
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
|
|
enum Location {
|
|
FROM_STORE,
|
|
UNPACKED,
|
|
THIRD_PARTY,
|
|
INSTALLED_BY_DEFAULT,
|
|
UNKNOWN
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Let's create a patch for it
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'FROM_STORE', 'FROM_STORE,\n FROM_BRAVE_STORE'))
|
|
self.fake_chromium_src.run_update_patches()
|
|
# clearing out our custom change so we can have upstream changes
|
|
# piling to this file
|
|
self.fake_chromium_src._run_git_command(
|
|
["checkout", "."], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
|
|
# Adding an upstream chromium change that should conflict with our
|
|
# patch
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl,
|
|
target_file.read_text().replace('FROM_STORE',
|
|
'FROM_STORE,\n DELETED'),
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit(
|
|
'Added DELETED to developer_private.idl Location',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
self.assertNotIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CONFLICT)
|
|
|
|
def test_apply_conflict_with_whitespace_error(self):
|
|
"""Tests the behavior when applying a patch with whitespace errors."""
|
|
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
|
|
enum Location {
|
|
FROM_STORE,
|
|
UNPACKED,
|
|
THIRD_PARTY,
|
|
INSTALLED_BY_DEFAULT,
|
|
UNKNOWN
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Let's create a patch for it
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'FROM_STORE', 'FROM_STORE,\n FROM_BRAVE_STORE'))
|
|
|
|
# Let's create a patch with trailing spaces
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'UNKNOWN', 'UNKNOWN,\n ANOTHER '))
|
|
|
|
self.fake_chromium_src.run_update_patches()
|
|
# clearing out our custom change so we can have upstream changes
|
|
# piling to this file
|
|
self.fake_chromium_src._run_git_command(
|
|
["checkout", "."], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
|
|
# Adding an upstream chromium change that should conflict with our
|
|
# patch
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl,
|
|
target_file.read_text().replace('FROM_STORE',
|
|
'FROM_STORE,\n DELETED'),
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit(
|
|
'Added DELETED to developer_private.idl Location',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
self.assertNotIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CONFLICT)
|
|
|
|
def test_apply_clean(self):
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
|
|
enum Location {
|
|
FROM_STORE,
|
|
UNPACKED,
|
|
THIRD_PARTY,
|
|
INSTALLED_BY_DEFAULT,
|
|
UNKNOWN
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Let's create a patch for it
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'FROM_STORE', 'FROM_STORE,\n FROM_BRAVE_STORE'))
|
|
self.fake_chromium_src.run_update_patches()
|
|
# clearing out our custom change so we can have upstream changes piling
|
|
# to this file
|
|
self.fake_chromium_src._run_git_command(
|
|
["checkout", "."], self.fake_chromium_src.chromium)
|
|
|
|
# Adding an upstream chromium change to the file.
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl,
|
|
target_file.read_text() + """
|
|
enum ViewType {
|
|
APP_WINDOW,
|
|
BACKGROUND_CONTENTS,
|
|
COMPONENT,
|
|
EXTENSION_BACKGROUND_PAGE,
|
|
EXTENSION_GUEST,
|
|
EXTENSION_POPUP,
|
|
EXTENSION_SERVICE_WORKER_BACKGROUND,
|
|
TAB_CONTENTS,
|
|
OFFSCREEN_DOCUMENT,
|
|
EXTENSION_SIDE_PANEL,
|
|
DEVELOPER_TOOLS
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit(
|
|
'Added ViewType to developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, """
|
|
enum ErrorType {
|
|
MANIFEST,
|
|
RUNTIME
|
|
};
|
|
""" + target_file.read_text(), self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit(
|
|
'Added ErrorType to developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
self.assertNotIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CLEAN)
|
|
self.assertIn('FROM_BRAVE_STORE', target_file.read_text())
|
|
|
|
def test_apply_broken(self):
|
|
'''Tests the behavior when applying a broken patchfile.'''
|
|
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, 'Just a test\nline1\line2\line3',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
target_file.write_text(target_file.read_text() + 'last line\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '.'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CLEAN)
|
|
self.assertIn('last line', target_file.read_text())
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '--force', 'HEAD'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
# A simple strip over the contents of the patch should break it
|
|
target_patch = (self.fake_chromium_src.brave /
|
|
self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
target_patch.write_text(target_patch.read_text().strip())
|
|
|
|
# apply() reads the patch from disk at call time, so the same
|
|
# Patchfile instance applies the now-broken patch.
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.BROKEN)
|
|
|
|
def test_apply_on_deleted(self):
|
|
'''Tests the behavior when applying a patch to a deleted file.'''
|
|
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, 'Just a test\nline1\line2\line3',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
target_file.write_text(target_file.read_text() + 'last line\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '.'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CLEAN)
|
|
self.assertIn('last line', target_file.read_text())
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '--force', 'HEAD'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
# deleting file.
|
|
self.fake_chromium_src.delete_file(test_idl,
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Delete developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.DELETED)
|
|
|
|
def test_apply_on_deleted_with_whitespace_error(self):
|
|
'''Tests DELETED status when stderr has whitespace warnings before the
|
|
error: line (the bug that was fixed).'''
|
|
|
|
test_idl = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_idl, 'Just a test\nline1\nline2\nline3\n',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
target_file = self.fake_chromium_src.chromium / test_idl
|
|
# Trailing whitespace causes git to emit a warning line before the
|
|
# error: line when applying against a deleted file.
|
|
target_file.write_text(target_file.read_text() + 'last line \n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '.'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
# Sanity-check: patch applies cleanly before we delete the file.
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.CLEAN)
|
|
self.assertIn('last line', target_file.read_text())
|
|
|
|
self.fake_chromium_src._run_git_command(
|
|
['checkout', '--force', 'HEAD'], self.fake_chromium_src.chromium)
|
|
self.assertNotIn('last line', target_file.read_text())
|
|
|
|
# Simulate upstream deletion of the file.
|
|
self.fake_chromium_src.delete_file(test_idl,
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Delete developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_idl))
|
|
status = patchfile.apply()
|
|
self.assertEqual(status, Patchfile.ApplyStatus.DELETED)
|
|
|
|
def test_source_from_brave(self):
|
|
"""Tests the source_from_brave method of Patchfile."""
|
|
dex_py = Path('build/android/gyp/dex.py')
|
|
devtools_ts = Path(
|
|
'front_end/panels/timeline/components/LiveMetricsView.ts')
|
|
devtools_repo = (self.fake_chromium_src.chromium /
|
|
'third_party/devtools-frontend/src')
|
|
for repo, source in [
|
|
(self.fake_chromium_src.chromium, dex_py),
|
|
(self.fake_chromium_src.chromium / 'v8', dex_py),
|
|
(devtools_repo, devtools_ts),
|
|
]:
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
source, 'original\n', repo)
|
|
self.fake_chromium_src.commit(f'Add {source.name}', repo)
|
|
(repo / source).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
self.assertEqual(
|
|
Patchfile(path=Path('patches/v8/build-android-gyp-dex.py.patch')
|
|
).source_from_brave(),
|
|
repository.chromium.root / 'v8' / 'build/android/gyp/dex.py')
|
|
self.assertEqual(
|
|
Patchfile(path=Path(
|
|
'patches/build-android-gyp-dex.py.patch')).source_from_brave(),
|
|
repository.chromium.root / 'build/android/gyp/dex.py')
|
|
|
|
_devtools_patch = ('patches/third_party/devtools-frontend/src/'
|
|
'front_end-panels-timeline-components-'
|
|
'LiveMetricsView.ts.patch')
|
|
_devtools_source = (
|
|
'front_end/panels/timeline/components/LiveMetricsView.ts')
|
|
self.assertEqual(
|
|
Patchfile(path=Path(_devtools_patch)).source_from_brave(),
|
|
repository.chromium.root / 'third_party/devtools-frontend/src' /
|
|
_devtools_source)
|
|
|
|
def test_path_from_repo(self):
|
|
"""Tests the path_from_repo method of Patchfile."""
|
|
dex_py = Path('build/android/gyp/dex.py')
|
|
for repo in [
|
|
self.fake_chromium_src.chromium,
|
|
self.fake_chromium_src.chromium / 'v8'
|
|
]:
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
dex_py, 'original\n', repo)
|
|
self.fake_chromium_src.commit('Add dex.py', repo)
|
|
(repo / dex_py).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
self.assertEqual(
|
|
Patchfile(path=Path(
|
|
'patches/v8/build-android-gyp-dex.py.patch')).path_from_repo(),
|
|
Repository(repository.chromium.root / 'v8').to_brave() /
|
|
'patches/v8/build-android-gyp-dex.py.patch')
|
|
self.assertEqual(
|
|
Patchfile(path=Path(
|
|
'patches/build-android-gyp-dex.py.patch')).path_from_repo(),
|
|
repository.chromium.to_brave() /
|
|
'patches/build-android-gyp-dex.py.patch')
|
|
|
|
def test_get_last_commit_for_source(self):
|
|
"""Test get_last_commit_for_source method."""
|
|
test_file = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_file, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
|
|
# Commit the file and get the short commit hash
|
|
initial_commit_hash = self.fake_chromium_src.commit(
|
|
'Add developer_private.idl', self.fake_chromium_src.chromium)[:7]
|
|
|
|
# Create a patch for the file
|
|
target_file = self.fake_chromium_src.chromium / test_file
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'HOSTED_APP', 'HOSTED_APP,\n NEW_TYPE'))
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
# Add a few empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 1',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 2',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Verify the last commit for the source matches the initial commit hash
|
|
patchfile_path = self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_file)
|
|
patchfile = Patchfile(path=patchfile_path)
|
|
self.assertEqual(patchfile.get_last_commit_for_source(),
|
|
initial_commit_hash)
|
|
|
|
# Delete the file and commit
|
|
self.fake_chromium_src.delete_file(test_file,
|
|
self.fake_chromium_src.chromium)
|
|
delete_commit_hash = self.fake_chromium_src.commit(
|
|
'Delete developer_private.idl',
|
|
self.fake_chromium_src.chromium)[:7]
|
|
|
|
# Add a few more empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 3',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 4',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Get the patch file path
|
|
patchfile = Patchfile(path=patchfile_path)
|
|
|
|
# Verify the last commit for the source matches the delete commit hash
|
|
self.assertEqual(patchfile.get_last_commit_for_source(),
|
|
delete_commit_hash)
|
|
|
|
def test_get_source_removal_status(self):
|
|
"""Test get_source_removal_status for a deleted source file."""
|
|
test_file = Path('chrome/common/extensions/api/developer_private.idl')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_file, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
|
|
# Commit the file
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Add a few empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 1',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 2',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Create a patch for the file
|
|
target_file = self.fake_chromium_src.chromium / test_file
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'HOSTED_APP', 'HOSTED_APP,\n NEW_TYPE'))
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
# Delete the file and commit
|
|
self.fake_chromium_src.delete_file(test_file,
|
|
self.fake_chromium_src.chromium)
|
|
delete_commit_hash = self.fake_chromium_src.commit(
|
|
'Delete developer_private.idl', self.fake_chromium_src.chromium)
|
|
|
|
# Get the patch file path
|
|
patchfile_path = self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_file)
|
|
patchfile = Patchfile(path=patchfile_path)
|
|
|
|
# Add a few more empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 3',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 4',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Verify the source removal status
|
|
removal_status = patchfile.get_source_removal_status(
|
|
delete_commit_hash)
|
|
self.assertEqual(removal_status.status, 'D') # 'D' indicates deletion
|
|
self.assertIn('Delete developer_private.idl',
|
|
removal_status.commit_details)
|
|
self.assertIsNone(removal_status.renamed_to)
|
|
|
|
def test_get_source_rename_status(self):
|
|
"""Test get_source_removal_status for a renamed source file."""
|
|
test_file = Path('chrome/common/extensions/api/developer_private.idl')
|
|
renamed_file = Path('chrome/common/extensions/api/renamed_private.idl')
|
|
|
|
# Write and commit the original file
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_file, """
|
|
enum ExtensionType {
|
|
HOSTED_APP,
|
|
PLATFORM_APP,
|
|
LEGACY_PACKAGED_APP,
|
|
EXTENSION,
|
|
THEME,
|
|
SHARED_MODULE
|
|
};
|
|
""", self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add developer_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Add a few empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 1',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 2',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Create a patch for the file
|
|
target_file = self.fake_chromium_src.chromium / test_file
|
|
target_file.write_text(target_file.read_text().replace(
|
|
'HOSTED_APP', 'HOSTED_APP,\n NEW_TYPE'))
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
# Rename the file and commit
|
|
(self.fake_chromium_src.chromium / test_file).rename(
|
|
self.fake_chromium_src.chromium / renamed_file)
|
|
self.fake_chromium_src._run_git_command(
|
|
['add', '-A'], self.fake_chromium_src.chromium)
|
|
rename_commit_hash = self.fake_chromium_src.commit(
|
|
'Rename developer_private.idl to renamed_private.idl',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Get the patch file path
|
|
patchfile_path = self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_file)
|
|
patchfile = Patchfile(path=patchfile_path)
|
|
|
|
# Add a few more empty commits
|
|
self.fake_chromium_src.commit_empty('Empty commit 3',
|
|
self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit_empty('Empty commit 4',
|
|
self.fake_chromium_src.chromium)
|
|
|
|
# Verify the source rename status
|
|
rename_status = patchfile.get_source_removal_status(rename_commit_hash)
|
|
self.assertEqual(rename_status.status, 'R') # 'R' indicates rename
|
|
self.assertIn('Rename developer_private.idl to renamed_private.idl',
|
|
rename_status.commit_details)
|
|
self.assertEqual(
|
|
(self.fake_chromium_src.chromium /
|
|
rename_status.renamed_to).resolve(),
|
|
(self.fake_chromium_src.chromium / renamed_file).resolve())
|
|
|
|
def test_plaster_set_when_file_exists(self):
|
|
"""Plaster path is set for a Chromium patch when the toml exists."""
|
|
test_file = Path('chrome/browser/foo.cc')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_file, 'original\n', self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add foo.cc',
|
|
self.fake_chromium_src.chromium)
|
|
(self.fake_chromium_src.chromium /
|
|
test_file).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
plaster_path = (self.fake_chromium_src.brave /
|
|
'rewrite/chrome/browser/foo.cc.toml')
|
|
plaster_path.parent.mkdir(parents=True, exist_ok=True)
|
|
plaster_path.write_text('')
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_file))
|
|
self.assertEqual(patchfile.plaster.resolve(), plaster_path.resolve())
|
|
|
|
def test_plaster_none_when_file_missing(self):
|
|
"""Plaster is None for a Chromium patch when no toml file exists."""
|
|
test_file = Path('chrome/browser/foo.cc')
|
|
self.fake_chromium_src.write_and_stage_file(
|
|
test_file, 'original\n', self.fake_chromium_src.chromium)
|
|
self.fake_chromium_src.commit('Add foo.cc',
|
|
self.fake_chromium_src.chromium)
|
|
(self.fake_chromium_src.chromium /
|
|
test_file).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
self.fake_chromium_src.chromium, test_file))
|
|
self.assertIsNone(patchfile.plaster)
|
|
|
|
def test_plaster_none_for_non_chromium_repo(self):
|
|
"""Plaster is None for patches targeting a sub-repository."""
|
|
test_file = Path('src/foo.cc')
|
|
v8 = self.fake_chromium_src.chromium / 'v8'
|
|
self.fake_chromium_src.write_and_stage_file(test_file, 'original\n',
|
|
v8)
|
|
self.fake_chromium_src.commit('Add foo.cc', v8)
|
|
(v8 / test_file).write_text('original\nbrave_change\n')
|
|
self.fake_chromium_src.run_update_patches()
|
|
|
|
# Create a toml file that would match if the repo check were absent.
|
|
plaster_path = self.fake_chromium_src.brave / 'rewrite/src/foo.cc.toml'
|
|
plaster_path.parent.mkdir(parents=True, exist_ok=True)
|
|
plaster_path.write_text('')
|
|
|
|
patchfile = Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
v8, test_file))
|
|
self.assertIsNone(patchfile.plaster)
|
|
|
|
|
|
class PatchfilePlasterApplyTest(unittest.TestCase):
|
|
"""Tests for Patchfile.apply() when a plaster file is associated."""
|
|
|
|
_SOURCE_FILE = Path('chrome/browser/foo.cc')
|
|
_BASE_CONTENT = 'void old_func() {}\nstatic int x = 0;\n'
|
|
_BRAVE_CONTENT = 'void old_func() {}\nstatic int x = 1;\n'
|
|
_UPSTREAM_CONTENT = 'void old_func() {}\nstatic int x = 2;\n'
|
|
|
|
# count = 0 means "replace all matches, bypass count validation".
|
|
_WORKING_TOML = ('[[substitution]]\n'
|
|
'description = "Replace old_func"\n'
|
|
'pattern = "old_func"\n'
|
|
'replace = "new_func"\n'
|
|
'count = 0\n')
|
|
|
|
# No substitution key → TypeError iterating None → PLASTER_BROKEN.
|
|
_BROKEN_TOML = '# no substitution\n'
|
|
|
|
def setUp(self):
|
|
self.fake_chromium_src = FakeChromiumRepo()
|
|
self.fake_chromium_src.setup()
|
|
self.addCleanup(self.fake_chromium_src.cleanup)
|
|
|
|
def _write_plaster_toml(self, content: str) -> None:
|
|
toml_path = (self.fake_chromium_src.brave / 'rewrite' /
|
|
self._SOURCE_FILE.parent /
|
|
(self._SOURCE_FILE.name + '.toml'))
|
|
toml_path.parent.mkdir(parents=True, exist_ok=True)
|
|
toml_path.write_text(content)
|
|
|
|
def _setup_conflict_and_patchfile(self) -> Patchfile:
|
|
"""Commits BASE_CONTENT, generates a brave patch (x=0→1), then commits
|
|
UPSTREAM_CONTENT (x=0→2) so that applying the patch hits the conflict
|
|
path in apply().
|
|
|
|
Must be called after _write_plaster_toml so the Patchfile constructor
|
|
finds the TOML and sets the plaster field.
|
|
"""
|
|
chromium = self.fake_chromium_src.chromium
|
|
self.fake_chromium_src.write_and_stage_file(self._SOURCE_FILE,
|
|
self._BASE_CONTENT,
|
|
chromium)
|
|
self.fake_chromium_src.commit('Add foo.cc', chromium)
|
|
|
|
(chromium / self._SOURCE_FILE).write_text(self._BRAVE_CONTENT)
|
|
self.fake_chromium_src.run_update_patches()
|
|
self.fake_chromium_src._run_git_command(['checkout', '.'], chromium)
|
|
|
|
self.fake_chromium_src.write_and_stage_file(self._SOURCE_FILE,
|
|
self._UPSTREAM_CONTENT,
|
|
chromium)
|
|
self.fake_chromium_src.commit('Upstream change', chromium)
|
|
|
|
return Patchfile(
|
|
path=self.fake_chromium_src.get_patchfile_path_for_source(
|
|
chromium, self._SOURCE_FILE))
|
|
|
|
def _setup_broken_and_patchfile(self) -> tuple[Patchfile, Path]:
|
|
"""Commits BASE_CONTENT and generates a valid brave patch. Returns the
|
|
Patchfile and the absolute path to the patch file on disk.
|
|
|
|
Strip the patch file after this call to trigger the broken-patch path
|
|
in apply(). Must be called after _write_plaster_toml.
|
|
"""
|
|
chromium = self.fake_chromium_src.chromium
|
|
self.fake_chromium_src.write_and_stage_file(self._SOURCE_FILE,
|
|
self._BASE_CONTENT,
|
|
chromium)
|
|
self.fake_chromium_src.commit('Add foo.cc', chromium)
|
|
|
|
(chromium / self._SOURCE_FILE).write_text(self._BRAVE_CONTENT)
|
|
self.fake_chromium_src.run_update_patches()
|
|
self.fake_chromium_src._run_git_command(['checkout', '.'], chromium)
|
|
|
|
patch_rel = self.fake_chromium_src.get_patchfile_path_for_source(
|
|
chromium, self._SOURCE_FILE)
|
|
return (Patchfile(path=patch_rel),
|
|
self.fake_chromium_src.brave / patch_rel)
|
|
|
|
def test_apply_conflict_plaster_fixed(self):
|
|
"""Conflict path + working plaster → PLASTER_FIXED."""
|
|
self._write_plaster_toml(self._WORKING_TOML)
|
|
patchfile = self._setup_conflict_and_patchfile()
|
|
self.assertEqual(patchfile.apply(),
|
|
Patchfile.ApplyStatus.PLASTER_FIXED)
|
|
|
|
def test_apply_conflict_plaster_broken(self):
|
|
"""Conflict path + broken plaster → PLASTER_BROKEN."""
|
|
self._write_plaster_toml(self._BROKEN_TOML)
|
|
patchfile = self._setup_conflict_and_patchfile()
|
|
self.assertEqual(patchfile.apply(),
|
|
Patchfile.ApplyStatus.PLASTER_BROKEN)
|
|
|
|
def test_apply_broken_patch_plaster_fixed(self):
|
|
"""Broken patch path + working plaster → PLASTER_FIXED."""
|
|
self._write_plaster_toml(self._WORKING_TOML)
|
|
patchfile, patch_path = self._setup_broken_and_patchfile()
|
|
patch_path.write_text(patch_path.read_text().strip())
|
|
self.assertEqual(patchfile.apply(),
|
|
Patchfile.ApplyStatus.PLASTER_FIXED)
|
|
|
|
def test_apply_broken_patch_plaster_broken(self):
|
|
"""Broken patch path + broken plaster → PLASTER_BROKEN."""
|
|
self._write_plaster_toml(self._BROKEN_TOML)
|
|
patchfile, patch_path = self._setup_broken_and_patchfile()
|
|
patch_path.write_text(patch_path.read_text().strip())
|
|
self.assertEqual(patchfile.apply(),
|
|
Patchfile.ApplyStatus.PLASTER_BROKEN)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|