summaryrefslogtreecommitdiff
path: root/tests/test_subcmds_sync.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_subcmds_sync.py')
-rw-r--r--tests/test_subcmds_sync.py167
1 files changed, 151 insertions, 16 deletions
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py
index e7e8013e0..32ff3cdd1 100644
--- a/tests/test_subcmds_sync.py
+++ b/tests/test_subcmds_sync.py
@@ -501,27 +501,116 @@ def test_cli_smart_sync(
@pytest.mark.parametrize(
- "argv, sync_smartsync_manifest, expected_exception",
+ "argv, user_pref, sync_smartsync_manifest, expected_smart_sync, "
+ "expected_implicit",
[
- (["-u", "user", "-p", "pass"], False, sync.SmartSyncError),
- (["-u", "user", "-p", "pass"], True, None),
- (["-s", "-u", "user", "-p", "pass"], False, None),
- (["-t", "tag", "-u", "user", "-p", "pass"], False, None),
+ # sync.smartsync=false: never smart sync unless -s/-t is given.
+ ([], False, True, False, False),
+ ([], False, False, False, False),
+ (["-s"], False, True, True, False),
+ (["--smart-sync"], False, False, True, False),
+ (["-t", "tag123"], False, True, False, False),
+ (["--nmu"], False, True, False, False),
+ # sync.smartsync=true: like -s, counted as explicit, not implicit.
+ ([], True, False, True, False),
+ ([], True, True, True, False),
+ (["--nmu"], True, False, True, False),
+ (["--no-smart-sync"], True, True, False, False),
+ (["-t", "tag123"], True, False, False, False),
+ (["-m", "other.xml"], True, False, False, False),
+ (["-l"], True, False, False, False),
+ (["--superproject-revision=abc"], True, False, False, False),
+ # sync.smartsync unset: manifest default applies as before.
+ ([], None, True, True, True),
+ ([], None, False, False, False),
+ ],
+)
+def test_user_config_smart_sync(
+ argv: List[str],
+ user_pref: Optional[bool],
+ sync_smartsync_manifest: bool,
+ expected_smart_sync: bool,
+ expected_implicit: bool,
+) -> None:
+ """Tests the sync.smartsync user setting against CLI and manifest."""
+ manifest = mock.MagicMock()
+ manifest.default.sync_smartsync = sync_smartsync_manifest
+
+ user_cfg = mock.MagicMock()
+ user_cfg.GetBoolean.return_value = user_pref
+
+ cmd = sync.Sync(manifest=manifest)
+ opts, args = cmd.OptionParser.parse_args(argv)
+ cmd.ValidateOptions(opts, args)
+ with mock.patch.object(sync.RepoConfig, "ForUser", return_value=user_cfg):
+ implicit = cmd._ResolveSmartSyncOption(opts, manifest)
+ user_cfg.GetBoolean.assert_called_once_with("sync.smartsync")
+ assert opts.smart_sync == expected_smart_sync
+ assert implicit == expected_implicit
+
+
+@pytest.mark.parametrize(
+ "value, expected_smart_sync",
+ [("true", True), ("false", False), ("yes", True), ("no", False)],
+)
+def test_user_config_smart_sync_from_file(
+ monkeypatch: pytest.MonkeyPatch,
+ tmp_home_dir: Path,
+ value: str,
+ expected_smart_sync: bool,
+) -> None:
+ """Tests that sync.smartsync is read from ~/.repoconfig/config."""
+ # ForUser() caches its instance process-wide; make it pick up this HOME.
+ monkeypatch.setattr(sync.RepoConfig, "_ForUser", None)
+ monkeypatch.delenv("REPO_CONFIG_DIR", raising=False)
+ cfg_dir = tmp_home_dir / ".repoconfig"
+ cfg_dir.mkdir()
+ (cfg_dir / "config").write_text(f"[sync]\n\tsmartsync = {value}\n")
+
+ manifest = mock.MagicMock()
+ # Set the manifest default to the opposite so we know the user config won.
+ manifest.default.sync_smartsync = not expected_smart_sync
+
+ cmd = sync.Sync(manifest=manifest)
+ opts, args = cmd.OptionParser.parse_args([])
+ cmd.ValidateOptions(opts, args)
+ assert cmd._ResolveSmartSyncOption(opts, manifest) is False
+ assert opts.smart_sync == expected_smart_sync
+
+
+@pytest.mark.parametrize(
+ "argv, user_pref, sync_smartsync_manifest, expected_exception",
+ [
+ (["-u", "user", "-p", "pass"], None, False, sync.SmartSyncError),
+ (["-u", "user", "-p", "pass"], None, True, None),
+ (["-s", "-u", "user", "-p", "pass"], None, False, None),
+ (["-t", "tag", "-u", "user", "-p", "pass"], None, False, None),
(
["--no-smart-sync", "-u", "user", "-p", "pass"],
+ None,
True,
sync.SmartSyncError,
),
- (["-l", "-u", "user", "-p", "pass"], True, sync.SmartSyncError),
- (["--nmu", "-u", "user", "-p", "pass"], True, None),
- (["--nmu", "-u", "user", "-p", "pass"], False, sync.SmartSyncError),
- (["-s", "-l", "-u", "user", "-p", "pass"], True, None),
- (["-u", "user"], False, SystemExit),
- (["-p", "pass"], False, SystemExit),
+ (["-l", "-u", "user", "-p", "pass"], None, True, sync.SmartSyncError),
+ (["--nmu", "-u", "user", "-p", "pass"], None, True, None),
+ (
+ ["--nmu", "-u", "user", "-p", "pass"],
+ None,
+ False,
+ sync.SmartSyncError,
+ ),
+ (["-s", "-l", "-u", "user", "-p", "pass"], None, True, None),
+ (["-u", "user"], None, False, SystemExit),
+ (["-p", "pass"], None, False, SystemExit),
+ # -u/-p are accepted when smart sync comes from sync.smartsync=true...
+ (["-u", "user", "-p", "pass"], True, False, None),
+ # ...and rejected when sync.smartsync=false drops the manifest default.
+ (["-u", "user", "-p", "pass"], False, True, sync.SmartSyncError),
],
)
def test_cli_manifest_server_credentials(
argv: List[str],
+ user_pref: Optional[bool],
sync_smartsync_manifest: bool,
expected_exception: Optional[Type[BaseException]],
) -> None:
@@ -529,15 +618,19 @@ def test_cli_manifest_server_credentials(
manifest = mock.MagicMock()
manifest.default.sync_smartsync = sync_smartsync_manifest
+ user_cfg = mock.MagicMock()
+ user_cfg.GetBoolean.return_value = user_pref
+
cmd = sync.Sync(manifest=manifest)
opts, args = cmd.OptionParser.parse_args(argv)
- if expected_exception:
- with pytest.raises(expected_exception):
+ with mock.patch.object(sync.RepoConfig, "ForUser", return_value=user_cfg):
+ if expected_exception:
+ with pytest.raises(expected_exception):
+ cmd.ValidateOptions(opts, args)
+ cmd._ResolveSmartSyncOption(opts, manifest)
+ else:
cmd.ValidateOptions(opts, args)
cmd._ResolveSmartSyncOption(opts, manifest)
- else:
- cmd.ValidateOptions(opts, args)
- cmd._ResolveSmartSyncOption(opts, manifest)
class LocalSyncState(unittest.TestCase):
@@ -1699,6 +1792,48 @@ class SyncCommand(unittest.TestCase):
mock_setup.assert_called_once()
self.assertFalse(mock_setup.call_args.kwargs.get("best_effort"))
+ def test_user_config_smart_sync_disabled(self) -> None:
+ """Ensure sync.smartsync=false skips the manifest's default."""
+ self.manifest.default.sync_smartsync = True
+ self.opt.smart_sync = None
+ mock.patch.object(self.cmd, "_UpdateAllManifestProjects").start()
+ user_cfg = mock.MagicMock()
+ user_cfg.GetBoolean.return_value = False
+ with mock.patch.object(
+ sync.RepoConfig, "ForUser", return_value=user_cfg
+ ):
+ with mock.patch.object(self.cmd, "_SmartSyncSetup") as mock_setup:
+ with mock.patch.object(self.cmd, "_UpdateRepoProject"):
+ with mock.patch.object(
+ self.cmd, "_ValidateOptionsWithManifest"
+ ):
+ with mock.patch.object(self.cmd, "_SyncInterleaved"):
+ with mock.patch.object(
+ self.cmd, "_RunPostSyncHook"
+ ):
+ self.cmd.Execute(self.opt, [])
+ mock_setup.assert_not_called()
+ self.assertFalse(self.opt.smart_sync)
+
+ def test_user_config_smart_sync_enabled_error(self) -> None:
+ """Ensure sync.smartsync=true fails hard instead of falling back."""
+ self.manifest.default.sync_smartsync = False
+ self.opt.smart_sync = None
+ self.opt.mp_update = False
+ user_cfg = mock.MagicMock()
+ user_cfg.GetBoolean.return_value = True
+ with mock.patch.object(
+ sync.RepoConfig, "ForUser", return_value=user_cfg
+ ):
+ with mock.patch.object(
+ self.cmd,
+ "_SmartSyncSetup",
+ side_effect=sync.SmartSyncError("unreachable"),
+ ) as mock_setup:
+ with self.assertRaises(sync.SmartSyncError):
+ self.cmd.Execute(self.opt, [])
+ mock_setup.assert_called_once()
+
def test_implicit_smart_sync_unparsable_manifest(self) -> None:
"""Ensure an unparsable smart sync manifest falls back to ToT."""
self.manifest.default.sync_smartsync = True