#!/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()