From f7a3f99dc9e92556f3a0c588633b651439b5f7db Mon Sep 17 00:00:00 2001 From: Gavin Mak Date: Mon, 23 Jun 2025 09:04:26 -0700 Subject: sync: Share self-update logic between sync modes The logic for checking for repo self-updates lives in _FetchMain, which is part of the "phased" sync path. Extract this logic into a new _UpdateRepoProject helper method. Call this common helper from _ExecuteHelper before either sync mode begins, so the repo self-update check is always performed. Bug: 421935613 Change-Id: I9a804f43fbf6239c4146be446040be531f12fc8a Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/484041 Reviewed-by: Scott Lee Commit-Queue: Gavin Mak Tested-by: Gavin Mak --- tests/test_subcmds_sync.py | 102 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'tests') diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py index e7213ed9..9cd19f10 100644 --- a/tests/test_subcmds_sync.py +++ b/tests/test_subcmds_sync.py @@ -527,6 +527,108 @@ class SyncCommand(unittest.TestCase): self.assertIn(self.sync_network_half_error, e.aggregate_errors) +class SyncUpdateRepoProject(unittest.TestCase): + """Tests for Sync._UpdateRepoProject.""" + + def setUp(self): + """Common setup.""" + self.repodir = tempfile.mkdtemp(".repo") + self.manifest = manifest = mock.MagicMock(repodir=self.repodir) + # Create a repoProject with a mock Sync_NetworkHalf. + repoProject = mock.MagicMock(name="repo") + repoProject.Sync_NetworkHalf = mock.Mock( + return_value=SyncNetworkHalfResult(True, None) + ) + manifest.repoProject = repoProject + manifest.IsArchive = False + manifest.CloneFilter = None + manifest.PartialCloneExclude = None + manifest.CloneFilterForDepth = None + + git_event_log = mock.MagicMock(ErrorEvent=mock.Mock(return_value=None)) + self.cmd = sync.Sync(manifest=manifest, git_event_log=git_event_log) + + opt, _ = self.cmd.OptionParser.parse_args([]) + opt.local_only = False + opt.repo_verify = False + opt.verbose = False + opt.quiet = True + opt.force_sync = False + opt.clone_bundle = False + opt.tags = False + opt.optimized_fetch = False + opt.retry_fetches = 0 + opt.prune = False + self.opt = opt + self.errors = [] + + mock.patch.object(sync.Sync, "_GetCurrentBranchOnly").start() + + def tearDown(self): + shutil.rmtree(self.repodir) + mock.patch.stopall() + + def test_fetches_when_stale(self): + """Test it fetches when the repo project is stale.""" + self.manifest.repoProject.LastFetch = time.time() - ( + sync._ONE_DAY_S + 1 + ) + + with mock.patch.object(sync, "_PostRepoFetch") as mock_post_fetch: + self.cmd._UpdateRepoProject(self.opt, self.manifest, self.errors) + self.manifest.repoProject.Sync_NetworkHalf.assert_called_once() + mock_post_fetch.assert_called_once() + self.assertEqual(self.errors, []) + + def test_skips_when_fresh(self): + """Test it skips fetch when repo project is fresh.""" + self.manifest.repoProject.LastFetch = time.time() + + with mock.patch.object(sync, "_PostRepoFetch") as mock_post_fetch: + self.cmd._UpdateRepoProject(self.opt, self.manifest, self.errors) + self.manifest.repoProject.Sync_NetworkHalf.assert_not_called() + mock_post_fetch.assert_not_called() + + def test_skips_local_only(self): + """Test it does nothing with --local-only.""" + self.opt.local_only = True + self.manifest.repoProject.LastFetch = time.time() - ( + sync._ONE_DAY_S + 1 + ) + + with mock.patch.object(sync, "_PostRepoFetch") as mock_post_fetch: + self.cmd._UpdateRepoProject(self.opt, self.manifest, self.errors) + self.manifest.repoProject.Sync_NetworkHalf.assert_not_called() + mock_post_fetch.assert_not_called() + + def test_post_repo_fetch_skipped_on_env_var(self): + """Test _PostRepoFetch is skipped when REPO_SKIP_SELF_UPDATE is set.""" + self.manifest.repoProject.LastFetch = time.time() + + with mock.patch.dict(os.environ, {"REPO_SKIP_SELF_UPDATE": "1"}): + with mock.patch.object(sync, "_PostRepoFetch") as mock_post_fetch: + self.cmd._UpdateRepoProject( + self.opt, self.manifest, self.errors + ) + mock_post_fetch.assert_not_called() + + def test_fetch_failure_is_handled(self): + """Test that a fetch failure is recorded and doesn't crash.""" + self.manifest.repoProject.LastFetch = time.time() - ( + sync._ONE_DAY_S + 1 + ) + fetch_error = GitError("Fetch failed") + self.manifest.repoProject.Sync_NetworkHalf.return_value = ( + SyncNetworkHalfResult(False, fetch_error) + ) + + with mock.patch.object(sync, "_PostRepoFetch") as mock_post_fetch: + self.cmd._UpdateRepoProject(self.opt, self.manifest, self.errors) + self.manifest.repoProject.Sync_NetworkHalf.assert_called_once() + mock_post_fetch.assert_not_called() + self.assertEqual(self.errors, [fetch_error]) + + class InterleavedSyncTest(unittest.TestCase): """Tests for interleaved sync.""" -- cgit v1.2.3-54-g00ecf