summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorGavin Mak <gavinmak@google.com>2026-04-22 00:39:42 +0000
committergerrit-scoped@luci-project-accounts.iam.gserviceaccount.com <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2026-05-20 12:28:55 -0700
commit2d54384a5e59fddd0626ec091cc0ad11511d834e (patch)
treec0a9d642bb4064a23df2044777b6f81f2c047473 /tests
parent1b4e7a04be7b49d8c0c5e161d36f209bca4b6498 (diff)
downloadgit-repo-stable.tar.gz
sync: Add --superproject-rev flag to sync to specific revisionv2.64stable
Allow syncing the outer manifest to a state defined by a specific superproject revision. It updates the superproject, reads the manifest commit from .supermanifest, and checks out the outer manifest project to that commit. Submanifests are then processed normally, allowing them to be updated to the revisions specified in the new outer manifest state. Bug: 416589884 Change-Id: I304c37a2b8794f9b74cb7e5e209a8a93762bdb52 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/576321 Commit-Queue: Gavin Mak <gavinmak@google.com> Tested-by: Gavin Mak <gavinmak@google.com> Reviewed-by: Mike Frysinger <vapier@google.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/test_subcmds_sync.py168
1 files changed, 168 insertions, 0 deletions
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py
index ef162392b..785c0e629 100644
--- a/tests/test_subcmds_sync.py
+++ b/tests/test_subcmds_sync.py
@@ -1286,3 +1286,171 @@ class UpdateCopyLinkfileListTest(unittest.TestCase):
1286 self.assertFalse(os.path.lexists(os.path.join(llms_dir, "rules"))) 1286 self.assertFalse(os.path.lexists(os.path.join(llms_dir, "rules")))
1287 self.assertTrue(os.path.exists(os.path.join(llms_dir, "my-notes.txt"))) 1287 self.assertTrue(os.path.exists(os.path.join(llms_dir, "my-notes.txt")))
1288 self.assertTrue(os.path.isdir(llms_dir)) 1288 self.assertTrue(os.path.isdir(llms_dir))
1289
1290
1291class SyncToSuperprojectRevTests(unittest.TestCase):
1292 """Tests for Sync._SyncToSuperprojectRev."""
1293
1294 def setUp(self):
1295 self.repodir = tempfile.mkdtemp(".repo")
1296 self.manifest = mock.MagicMock(repodir=self.repodir)
1297 self.manifest.superproject = mock.MagicMock()
1298 self.manifest.path_prefix = ""
1299
1300 self.mp = mock.MagicMock()
1301 self.cmd = sync.Sync(manifest=self.manifest)
1302 self.cmd.outer_manifest = self.manifest
1303
1304 self.opt = mock.Mock()
1305 self.opt.verbose = False
1306 self.opt.superproject_revision = "deadbeef"
1307 self.opt.mp_update = True
1308
1309 self.errors = []
1310
1311 def tearDown(self):
1312 shutil.rmtree(self.repodir)
1313
1314 @mock.patch("subcmds.sync.GitCommand")
1315 def test_successful_sync(self, mock_git_command):
1316 """Test successful sync to superproject rev."""
1317 mock_superproject = self.manifest.superproject
1318 mock_superproject.Sync.return_value = mock.Mock(success=True)
1319
1320 mock_git = mock.Mock()
1321 mock_git.Wait.return_value = 0
1322 mock_git.stdout = "proj branch manifest_commit_hash\n"
1323 mock_git_command.return_value = mock_git
1324
1325 with mock.patch.object(
1326 self.cmd, "_UpdateManifestProject"
1327 ) as mock_update:
1328 self.cmd._SyncToSuperprojectRev(
1329 self.opt, self.manifest, self.mp, "name", self.errors
1330 )
1331
1332 mock_superproject.SetRevisionId.assert_called_with("deadbeef")
1333 mock_superproject.Sync.assert_called_once()
1334 mock_git_command.assert_called_once()
1335 self.mp.SetRevision.assert_called_with("manifest_commit_hash")
1336 mock_update.assert_called_once()
1337 self.assertEqual(self.errors, [])
1338
1339 @mock.patch("subcmds.sync.GitCommand")
1340 def test_parse_error(self, mock_git_command):
1341 """Test error when .supermanifest cannot be parsed."""
1342 mock_superproject = self.manifest.superproject
1343 mock_superproject.Sync.return_value = mock.Mock(success=True)
1344
1345 mock_git = mock.Mock()
1346 mock_git.Wait.return_value = 0
1347 # Invalid format (not 3 parts)
1348 mock_git.stdout = "invalid_content\n"
1349 mock_git_command.return_value = mock_git
1350
1351 with self.assertRaises(sync.SyncError) as e:
1352 self.cmd._SyncToSuperprojectRev(
1353 self.opt, self.manifest, self.mp, "name", self.errors
1354 )
1355 self.assertIn("could not parse .supermanifest", str(e.exception))
1356
1357 @mock.patch("subcmds.sync.GitCommand")
1358 def test_read_error(self, mock_git_command):
1359 """Test error when reading .supermanifest fails."""
1360 mock_superproject = self.manifest.superproject
1361 mock_superproject.Sync.return_value = mock.Mock(success=True)
1362
1363 mock_git = mock.Mock()
1364 mock_git.Wait.return_value = 1
1365 mock_git.stderr = "git error"
1366 mock_git_command.return_value = mock_git
1367
1368 with self.assertRaises(sync.SyncError) as e:
1369 self.cmd._SyncToSuperprojectRev(
1370 self.opt, self.manifest, self.mp, "name", self.errors
1371 )
1372 self.assertIn("failed to read .supermanifest", str(e.exception))
1373
1374 def test_no_superproject(self):
1375 """Test error when superproject is not defined."""
1376 self.manifest.superproject = None
1377
1378 with self.assertRaises(sync.SyncError) as e:
1379 self.cmd._SyncToSuperprojectRev(
1380 self.opt, self.manifest, self.mp, "name", self.errors
1381 )
1382 self.assertIn("superproject not defined", str(e.exception))
1383
1384 @mock.patch("subcmds.sync.GitCommand")
1385 def test_sync_failure(self, mock_git_command):
1386 """Test error when superproject sync fails."""
1387 mock_superproject = self.manifest.superproject
1388 mock_superproject.Sync.return_value = mock.Mock(success=False)
1389
1390 with self.assertRaises(sync.SyncError) as e:
1391 self.cmd._SyncToSuperprojectRev(
1392 self.opt, self.manifest, self.mp, "name", self.errors
1393 )
1394 self.assertIn("failed to sync superproject", str(e.exception))
1395
1396
1397class UpdateAllManifestProjectsTests(unittest.TestCase):
1398 """Tests for Sync._UpdateAllManifestProjects."""
1399
1400 def setUp(self):
1401 self.repodir = tempfile.mkdtemp(".repo")
1402 self.manifest = mock.MagicMock(repodir=self.repodir)
1403 self.manifest.superproject = mock.MagicMock()
1404 self.manifest.path_prefix = ""
1405 self.manifest.standalone_manifest_url = None
1406 self.manifest.submanifests = {}
1407
1408 self.mp = mock.MagicMock()
1409 self.mp.manifest = self.manifest
1410 self.mp.standalone_manifest_url = None
1411 self.cmd = sync.Sync(manifest=self.manifest)
1412 self.cmd.outer_manifest = self.manifest
1413
1414 self.opt = mock.Mock()
1415 self.opt.verbose = False
1416 self.opt.superproject_revision = None
1417 self.opt.mp_update = True
1418
1419 self.errors = []
1420
1421 def tearDown(self):
1422 shutil.rmtree(self.repodir)
1423
1424 def test_superproject_revision_outer_manifest(self):
1425 """Test that _SyncToSuperprojectRev is called for outer manifest."""
1426 self.opt.superproject_revision = "deadbeef"
1427
1428 with mock.patch.object(
1429 self.cmd, "_SyncToSuperprojectRev"
1430 ) as mock_sync_to_rev:
1431 self.cmd._UpdateAllManifestProjects(
1432 self.opt, self.mp, "name", self.errors
1433 )
1434 mock_sync_to_rev.assert_called_once_with(
1435 self.opt, self.manifest, self.mp, "name", self.errors
1436 )
1437
1438 def test_superproject_revision_submanifest(self):
1439 """Test that _SyncToSuperprojectRev is NOT called for submanifest."""
1440 self.opt.superproject_revision = "deadbeef"
1441 submanifest = mock.MagicMock()
1442 submanifest.path_prefix = "sub/"
1443 submanifest.standalone_manifest_url = None
1444 self.mp.manifest = submanifest
1445
1446 with mock.patch.object(
1447 self.cmd, "_SyncToSuperprojectRev"
1448 ) as mock_sync_to_rev:
1449 with mock.patch.object(
1450 self.cmd, "_UpdateManifestProject"
1451 ) as mock_update_manifest:
1452 self.cmd._UpdateAllManifestProjects(
1453 self.opt, self.mp, "name", self.errors
1454 )
1455 mock_sync_to_rev.assert_not_called()
1456 mock_update_manifest.assert_called_once()