diff options
| author | Mike Frysinger <vapier@google.com> | 2026-03-26 01:54:14 -0400 |
|---|---|---|
| committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2026-04-06 11:36:39 -0700 |
| commit | 654690e1b8695d72885533016482306e32528f48 (patch) | |
| tree | f5c0a5a0be5329d4eb16aa30fd5e58df35d38065 | |
| parent | ac2be4c089fda58b9ba2f7b2bdb1416f8d3a74b2 (diff) | |
| download | git-repo-654690e1b8695d72885533016482306e32528f48.tar.gz | |
tests: convert more tests to pytest
Change-Id: Id4d48b61dc435564c336385bbc4944eb475d1942
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/569443
Tested-by: Mike Frysinger <vapier@google.com>
Commit-Queue: Mike Frysinger <vapier@google.com>
Reviewed-by: Gavin Mak <gavinmak@google.com>
| -rw-r--r-- | tests/test_git_trace2_event_log.py | 755 | ||||
| -rw-r--r-- | tests/test_manifest_xml.py | 1036 | ||||
| -rw-r--r-- | tests/test_subcmds_upload.py | 81 | ||||
| -rw-r--r-- | tests/test_wrapper.py | 642 |
4 files changed, 1308 insertions, 1206 deletions
diff --git a/tests/test_git_trace2_event_log.py b/tests/test_git_trace2_event_log.py index be2d09b07..9a6ba2052 100644 --- a/tests/test_git_trace2_event_log.py +++ b/tests/test_git_trace2_event_log.py | |||
| @@ -18,17 +18,24 @@ import contextlib | |||
| 18 | import io | 18 | import io |
| 19 | import json | 19 | import json |
| 20 | import os | 20 | import os |
| 21 | import re | ||
| 21 | import socket | 22 | import socket |
| 22 | import tempfile | 23 | import tempfile |
| 23 | import threading | 24 | import threading |
| 24 | import unittest | 25 | from typing import Any, Dict, List, Optional |
| 25 | from unittest import mock | 26 | from unittest import mock |
| 26 | 27 | ||
| 28 | import pytest | ||
| 29 | |||
| 27 | import git_trace2_event_log | 30 | import git_trace2_event_log |
| 28 | import platform_utils | 31 | import platform_utils |
| 29 | 32 | ||
| 30 | 33 | ||
| 31 | def serverLoggingThread(socket_path, server_ready, received_traces): | 34 | def server_logging_thread( |
| 35 | socket_path: str, | ||
| 36 | server_ready: threading.Condition, | ||
| 37 | received_traces: List[str], | ||
| 38 | ) -> None: | ||
| 32 | """Helper function to receive logs over a Unix domain socket. | 39 | """Helper function to receive logs over a Unix domain socket. |
| 33 | 40 | ||
| 34 | Appends received messages on the provided socket and appends to | 41 | Appends received messages on the provided socket and appends to |
| @@ -57,405 +64,425 @@ def serverLoggingThread(socket_path, server_ready, received_traces): | |||
| 57 | received_traces.extend(data.decode("utf-8").splitlines()) | 64 | received_traces.extend(data.decode("utf-8").splitlines()) |
| 58 | 65 | ||
| 59 | 66 | ||
| 60 | class EventLogTestCase(unittest.TestCase): | 67 | PARENT_SID_KEY = "GIT_TRACE2_PARENT_SID" |
| 61 | """TestCase for the EventLog module.""" | 68 | PARENT_SID_VALUE = "parent_sid" |
| 69 | SELF_SID_REGEX = r"repo-\d+T\d+Z-.*" | ||
| 70 | FULL_SID_REGEX = rf"^{PARENT_SID_VALUE}/{SELF_SID_REGEX}" | ||
| 71 | |||
| 72 | |||
| 73 | @pytest.fixture | ||
| 74 | def event_log() -> git_trace2_event_log.EventLog: | ||
| 75 | """Fixture for the EventLog module.""" | ||
| 76 | # By default we initialize with the expected case where | ||
| 77 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). | ||
| 78 | env = {PARENT_SID_KEY: PARENT_SID_VALUE} | ||
| 79 | return git_trace2_event_log.EventLog(env=env) | ||
| 80 | |||
| 81 | |||
| 82 | def verify_common_keys( | ||
| 83 | log_entry: Dict[str, Any], | ||
| 84 | expected_event_name: Optional[str] = None, | ||
| 85 | full_sid: bool = True, | ||
| 86 | ) -> None: | ||
| 87 | """Helper function to verify common event log keys.""" | ||
| 88 | assert "event" in log_entry | ||
| 89 | assert "sid" in log_entry | ||
| 90 | assert "thread" in log_entry | ||
| 91 | assert "time" in log_entry | ||
| 92 | |||
| 93 | # Do basic data format validation. | ||
| 94 | if expected_event_name: | ||
| 95 | assert expected_event_name == log_entry["event"] | ||
| 96 | if full_sid: | ||
| 97 | assert re.match(FULL_SID_REGEX, log_entry["sid"]) | ||
| 98 | else: | ||
| 99 | assert re.match(SELF_SID_REGEX, log_entry["sid"]) | ||
| 100 | assert re.match(r"^\d+-\d+-\d+T\d+:\d+:\d+\.\d+\+00:00$", log_entry["time"]) | ||
| 101 | |||
| 102 | |||
| 103 | def read_log(log_path: str) -> List[Dict[str, Any]]: | ||
| 104 | """Helper function to read log data into a list.""" | ||
| 105 | log_data = [] | ||
| 106 | with open(log_path, mode="rb") as f: | ||
| 107 | for line in f: | ||
| 108 | log_data.append(json.loads(line)) | ||
| 109 | return log_data | ||
| 110 | |||
| 111 | |||
| 112 | def remove_prefix(s: str, prefix: str) -> str: | ||
| 113 | """Return a copy string after removing |prefix| from |s|, if present or | ||
| 114 | the original string.""" | ||
| 115 | if s.startswith(prefix): | ||
| 116 | return s[len(prefix) :] | ||
| 117 | else: | ||
| 118 | return s | ||
| 119 | |||
| 120 | |||
| 121 | def test_initial_state_with_parent_sid( | ||
| 122 | event_log: git_trace2_event_log.EventLog, | ||
| 123 | ) -> None: | ||
| 124 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent.""" | ||
| 125 | assert re.match(FULL_SID_REGEX, event_log.full_sid) | ||
| 126 | |||
| 127 | |||
| 128 | def test_initial_state_no_parent_sid() -> None: | ||
| 129 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set.""" | ||
| 130 | # Setup an empty environment dict (no parent sid). | ||
| 131 | event_log = git_trace2_event_log.EventLog(env={}) | ||
| 132 | assert re.match(SELF_SID_REGEX, event_log.full_sid) | ||
| 133 | |||
| 134 | |||
| 135 | def test_version_event(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 136 | """Test 'version' event data is valid. | ||
| 137 | |||
| 138 | Verify that the 'version' event is written even when no other | ||
| 139 | events are added. | ||
| 62 | 140 | ||
| 63 | PARENT_SID_KEY = "GIT_TRACE2_PARENT_SID" | 141 | Expected event log: |
| 64 | PARENT_SID_VALUE = "parent_sid" | 142 | <version event> |
| 65 | SELF_SID_REGEX = r"repo-\d+T\d+Z-.*" | 143 | """ |
| 66 | FULL_SID_REGEX = rf"^{PARENT_SID_VALUE}/{SELF_SID_REGEX}" | 144 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: |
| 145 | log_path = event_log.Write(path=tempdir) | ||
| 146 | log_data = read_log(log_path) | ||
| 147 | |||
| 148 | # A log with no added events should only have the version entry. | ||
| 149 | assert len(log_data) == 1 | ||
| 150 | version_event = log_data[0] | ||
| 151 | verify_common_keys(version_event, expected_event_name="version") | ||
| 152 | # Check for 'version' event specific fields. | ||
| 153 | assert "evt" in version_event | ||
| 154 | assert "exe" in version_event | ||
| 155 | # Verify "evt" version field is a string. | ||
| 156 | assert isinstance(version_event["evt"], str) | ||
| 157 | |||
| 158 | |||
| 159 | def test_start_event(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 160 | """Test and validate 'start' event data is valid. | ||
| 161 | |||
| 162 | Expected event log: | ||
| 163 | <version event> | ||
| 164 | <start event> | ||
| 165 | """ | ||
| 166 | event_log.StartEvent([]) | ||
| 167 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 168 | log_path = event_log.Write(path=tempdir) | ||
| 169 | log_data = read_log(log_path) | ||
| 170 | |||
| 171 | assert len(log_data) == 2 | ||
| 172 | start_event = log_data[1] | ||
| 173 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 174 | verify_common_keys(start_event, expected_event_name="start") | ||
| 175 | # Check for 'start' event specific fields. | ||
| 176 | assert "argv" in start_event | ||
| 177 | assert isinstance(start_event["argv"], list) | ||
| 178 | |||
| 179 | |||
| 180 | def test_exit_event_result_none( | ||
| 181 | event_log: git_trace2_event_log.EventLog, | ||
| 182 | ) -> None: | ||
| 183 | """Test 'exit' event data is valid when result is None. | ||
| 184 | |||
| 185 | We expect None result to be converted to 0 in the exit event data. | ||
| 186 | |||
| 187 | Expected event log: | ||
| 188 | <version event> | ||
| 189 | <exit event> | ||
| 190 | """ | ||
| 191 | event_log.ExitEvent(None) | ||
| 192 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 193 | log_path = event_log.Write(path=tempdir) | ||
| 194 | log_data = read_log(log_path) | ||
| 195 | |||
| 196 | assert len(log_data) == 2 | ||
| 197 | exit_event = log_data[1] | ||
| 198 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 199 | verify_common_keys(exit_event, expected_event_name="exit") | ||
| 200 | # Check for 'exit' event specific fields. | ||
| 201 | assert "code" in exit_event | ||
| 202 | # 'None' result should convert to 0 (successful) return code. | ||
| 203 | assert exit_event["code"] == 0 | ||
| 204 | |||
| 205 | |||
| 206 | def test_exit_event_result_integer( | ||
| 207 | event_log: git_trace2_event_log.EventLog, | ||
| 208 | ) -> None: | ||
| 209 | """Test 'exit' event data is valid when result is an integer. | ||
| 210 | |||
| 211 | Expected event log: | ||
| 212 | <version event> | ||
| 213 | <exit event> | ||
| 214 | """ | ||
| 215 | event_log.ExitEvent(2) | ||
| 216 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 217 | log_path = event_log.Write(path=tempdir) | ||
| 218 | log_data = read_log(log_path) | ||
| 219 | |||
| 220 | assert len(log_data) == 2 | ||
| 221 | exit_event = log_data[1] | ||
| 222 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 223 | verify_common_keys(exit_event, expected_event_name="exit") | ||
| 224 | # Check for 'exit' event specific fields. | ||
| 225 | assert "code" in exit_event | ||
| 226 | assert exit_event["code"] == 2 | ||
| 227 | |||
| 228 | |||
| 229 | def test_command_event(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 230 | """Test and validate 'command' event data is valid. | ||
| 231 | |||
| 232 | Expected event log: | ||
| 233 | <version event> | ||
| 234 | <command event> | ||
| 235 | """ | ||
| 236 | event_log.CommandEvent(name="repo", subcommands=["init", "this"]) | ||
| 237 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 238 | log_path = event_log.Write(path=tempdir) | ||
| 239 | log_data = read_log(log_path) | ||
| 240 | |||
| 241 | assert len(log_data) == 2 | ||
| 242 | command_event = log_data[1] | ||
| 243 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 244 | verify_common_keys(command_event, expected_event_name="cmd_name") | ||
| 245 | # Check for 'command' event specific fields. | ||
| 246 | assert "name" in command_event | ||
| 247 | assert command_event["name"] == "repo-init-this" | ||
| 248 | |||
| 249 | |||
| 250 | def test_def_params_event_repo_config( | ||
| 251 | event_log: git_trace2_event_log.EventLog, | ||
| 252 | ) -> None: | ||
| 253 | """Test 'def_params' event data outputs only repo config keys. | ||
| 254 | |||
| 255 | Expected event log: | ||
| 256 | <version event> | ||
| 257 | <def_param event> | ||
| 258 | <def_param event> | ||
| 259 | """ | ||
| 260 | config = { | ||
| 261 | "git.foo": "bar", | ||
| 262 | "repo.partialclone": "true", | ||
| 263 | "repo.partialclonefilter": "blob:none", | ||
| 264 | } | ||
| 265 | event_log.DefParamRepoEvents(config) | ||
| 266 | |||
| 267 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 268 | log_path = event_log.Write(path=tempdir) | ||
| 269 | log_data = read_log(log_path) | ||
| 270 | |||
| 271 | assert len(log_data) == 3 | ||
| 272 | def_param_events = log_data[1:] | ||
| 273 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 274 | |||
| 275 | for event in def_param_events: | ||
| 276 | verify_common_keys(event, expected_event_name="def_param") | ||
| 277 | # Check for 'def_param' event specific fields. | ||
| 278 | assert "param" in event | ||
| 279 | assert "value" in event | ||
| 280 | assert event["param"].startswith("repo.") | ||
| 281 | |||
| 282 | |||
| 283 | def test_def_params_event_no_repo_config( | ||
| 284 | event_log: git_trace2_event_log.EventLog, | ||
| 285 | ) -> None: | ||
| 286 | """Test 'def_params' event data won't output non-repo config keys. | ||
| 287 | |||
| 288 | Expected event log: | ||
| 289 | <version event> | ||
| 290 | """ | ||
| 291 | config = { | ||
| 292 | "git.foo": "bar", | ||
| 293 | "git.core.foo2": "baz", | ||
| 294 | } | ||
| 295 | event_log.DefParamRepoEvents(config) | ||
| 296 | |||
| 297 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 298 | log_path = event_log.Write(path=tempdir) | ||
| 299 | log_data = read_log(log_path) | ||
| 300 | |||
| 301 | assert len(log_data) == 1 | ||
| 302 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 67 | 303 | ||
| 68 | def setUp(self): | ||
| 69 | """Load the event_log module every time.""" | ||
| 70 | self._event_log = None | ||
| 71 | # By default we initialize with the expected case where | ||
| 72 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). | ||
| 73 | env = { | ||
| 74 | self.PARENT_SID_KEY: self.PARENT_SID_VALUE, | ||
| 75 | } | ||
| 76 | self._event_log = git_trace2_event_log.EventLog(env=env) | ||
| 77 | self._log_data = None | ||
| 78 | 304 | ||
| 79 | def verifyCommonKeys( | 305 | def test_data_event_config(event_log: git_trace2_event_log.EventLog) -> None: |
| 80 | self, log_entry, expected_event_name=None, full_sid=True | 306 | """Test 'data' event data outputs all config keys. |
| 307 | |||
| 308 | Expected event log: | ||
| 309 | <version event> | ||
| 310 | <data event> | ||
| 311 | <data event> | ||
| 312 | """ | ||
| 313 | config = { | ||
| 314 | "git.foo": "bar", | ||
| 315 | "repo.partialclone": "false", | ||
| 316 | "repo.syncstate.superproject.hassuperprojecttag": "true", | ||
| 317 | "repo.syncstate.superproject.sys.argv": ["--", "sync", "protobuf"], | ||
| 318 | } | ||
| 319 | prefix_value = "prefix" | ||
| 320 | event_log.LogDataConfigEvents(config, prefix_value) | ||
| 321 | |||
| 322 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 323 | log_path = event_log.Write(path=tempdir) | ||
| 324 | log_data = read_log(log_path) | ||
| 325 | |||
| 326 | assert len(log_data) == 5 | ||
| 327 | data_events = log_data[1:] | ||
| 328 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 329 | |||
| 330 | for event in data_events: | ||
| 331 | verify_common_keys(event) | ||
| 332 | # Check for 'data' event specific fields. | ||
| 333 | assert "key" in event | ||
| 334 | assert "value" in event | ||
| 335 | key = event["key"] | ||
| 336 | key = remove_prefix(key, f"{prefix_value}/") | ||
| 337 | value = event["value"] | ||
| 338 | assert event_log.GetDataEventName(value) == event["event"] | ||
| 339 | assert key in config | ||
| 340 | assert value == config[key] | ||
| 341 | |||
| 342 | |||
| 343 | def test_error_event(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 344 | """Test and validate 'error' event data is valid. | ||
| 345 | |||
| 346 | Expected event log: | ||
| 347 | <version event> | ||
| 348 | <error event> | ||
| 349 | """ | ||
| 350 | msg = "invalid option: --cahced" | ||
| 351 | fmt = "invalid option: %s" | ||
| 352 | event_log.ErrorEvent(msg, fmt) | ||
| 353 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 354 | log_path = event_log.Write(path=tempdir) | ||
| 355 | log_data = read_log(log_path) | ||
| 356 | |||
| 357 | assert len(log_data) == 2 | ||
| 358 | error_event = log_data[1] | ||
| 359 | verify_common_keys(log_data[0], expected_event_name="version") | ||
| 360 | verify_common_keys(error_event, expected_event_name="error") | ||
| 361 | # Check for 'error' event specific fields. | ||
| 362 | assert "msg" in error_event | ||
| 363 | assert "fmt" in error_event | ||
| 364 | assert error_event["msg"] == f"RepoErrorEvent:{msg}" | ||
| 365 | assert error_event["fmt"] == f"RepoErrorEvent:{fmt}" | ||
| 366 | |||
| 367 | |||
| 368 | def test_write_with_filename(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 369 | """Test Write() with a path to a file exits with None.""" | ||
| 370 | assert event_log.Write(path="path/to/file") is None | ||
| 371 | |||
| 372 | |||
| 373 | def test_write_with_git_config( | ||
| 374 | tmp_path, | ||
| 375 | event_log: git_trace2_event_log.EventLog, | ||
| 376 | ) -> None: | ||
| 377 | """Test Write() uses the git config path when 'git config' call succeeds.""" | ||
| 378 | with mock.patch.object( | ||
| 379 | event_log, | ||
| 380 | "_GetEventTargetPath", | ||
| 381 | return_value=str(tmp_path), | ||
| 81 | ): | 382 | ): |
| 82 | """Helper function to verify common event log keys.""" | 383 | assert os.path.dirname(event_log.Write()) == str(tmp_path) |
| 83 | self.assertIn("event", log_entry) | 384 | |
| 84 | self.assertIn("sid", log_entry) | 385 | |
| 85 | self.assertIn("thread", log_entry) | 386 | def test_write_no_git_config(event_log: git_trace2_event_log.EventLog) -> None: |
| 86 | self.assertIn("time", log_entry) | 387 | """Test Write() with no git config variable present exits with None.""" |
| 87 | 388 | with mock.patch.object(event_log, "_GetEventTargetPath", return_value=None): | |
| 88 | # Do basic data format validation. | 389 | assert event_log.Write() is None |
| 89 | if expected_event_name: | 390 | |
| 90 | self.assertEqual(expected_event_name, log_entry["event"]) | 391 | |
| 91 | if full_sid: | 392 | def test_write_non_string(event_log: git_trace2_event_log.EventLog) -> None: |
| 92 | self.assertRegex(log_entry["sid"], self.FULL_SID_REGEX) | 393 | """Test Write() with non-string type for |path| throws TypeError.""" |
| 93 | else: | 394 | with pytest.raises(TypeError): |
| 94 | self.assertRegex(log_entry["sid"], self.SELF_SID_REGEX) | 395 | event_log.Write(path=1234) |
| 95 | self.assertRegex( | 396 | |
| 96 | log_entry["time"], r"^\d+-\d+-\d+T\d+:\d+:\d+\.\d+\+00:00$" | 397 | |
| 398 | @pytest.mark.skipif( | ||
| 399 | not hasattr(socket, "AF_UNIX"), reason="Requires AF_UNIX sockets" | ||
| 400 | ) | ||
| 401 | def test_write_socket(event_log: git_trace2_event_log.EventLog) -> None: | ||
| 402 | """Test Write() with Unix domain socket and validate received traces.""" | ||
| 403 | received_traces: List[str] = [] | ||
| 404 | with tempfile.TemporaryDirectory(prefix="test_server_sockets") as tempdir: | ||
| 405 | socket_path = os.path.join(tempdir, "server.sock") | ||
| 406 | server_ready = threading.Condition() | ||
| 407 | # Start "server" listening on Unix domain socket at socket_path. | ||
| 408 | server_thread = threading.Thread( | ||
| 409 | target=server_logging_thread, | ||
| 410 | args=(socket_path, server_ready, received_traces), | ||
| 97 | ) | 411 | ) |
| 412 | try: | ||
| 413 | server_thread.start() | ||
| 98 | 414 | ||
| 99 | def readLog(self, log_path): | 415 | with server_ready: |
| 100 | """Helper function to read log data into a list.""" | 416 | server_ready.wait(timeout=120) |
| 101 | log_data = [] | ||
| 102 | with open(log_path, mode="rb") as f: | ||
| 103 | for line in f: | ||
| 104 | log_data.append(json.loads(line)) | ||
| 105 | return log_data | ||
| 106 | |||
| 107 | def remove_prefix(self, s, prefix): | ||
| 108 | """Return a copy string after removing |prefix| from |s|, if present or | ||
| 109 | the original string.""" | ||
| 110 | if s.startswith(prefix): | ||
| 111 | return s[len(prefix) :] | ||
| 112 | else: | ||
| 113 | return s | ||
| 114 | |||
| 115 | def test_initial_state_with_parent_sid(self): | ||
| 116 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent.""" | ||
| 117 | self.assertRegex(self._event_log.full_sid, self.FULL_SID_REGEX) | ||
| 118 | |||
| 119 | def test_initial_state_no_parent_sid(self): | ||
| 120 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set.""" | ||
| 121 | # Setup an empty environment dict (no parent sid). | ||
| 122 | self._event_log = git_trace2_event_log.EventLog(env={}) | ||
| 123 | self.assertRegex(self._event_log.full_sid, self.SELF_SID_REGEX) | ||
| 124 | |||
| 125 | def test_version_event(self): | ||
| 126 | """Test 'version' event data is valid. | ||
| 127 | |||
| 128 | Verify that the 'version' event is written even when no other | ||
| 129 | events are addded. | ||
| 130 | |||
| 131 | Expected event log: | ||
| 132 | <version event> | ||
| 133 | """ | ||
| 134 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 135 | log_path = self._event_log.Write(path=tempdir) | ||
| 136 | self._log_data = self.readLog(log_path) | ||
| 137 | |||
| 138 | # A log with no added events should only have the version entry. | ||
| 139 | self.assertEqual(len(self._log_data), 1) | ||
| 140 | version_event = self._log_data[0] | ||
| 141 | self.verifyCommonKeys(version_event, expected_event_name="version") | ||
| 142 | # Check for 'version' event specific fields. | ||
| 143 | self.assertIn("evt", version_event) | ||
| 144 | self.assertIn("exe", version_event) | ||
| 145 | # Verify "evt" version field is a string. | ||
| 146 | self.assertIsInstance(version_event["evt"], str) | ||
| 147 | |||
| 148 | def test_start_event(self): | ||
| 149 | """Test and validate 'start' event data is valid. | ||
| 150 | |||
| 151 | Expected event log: | ||
| 152 | <version event> | ||
| 153 | <start event> | ||
| 154 | """ | ||
| 155 | self._event_log.StartEvent([]) | ||
| 156 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 157 | log_path = self._event_log.Write(path=tempdir) | ||
| 158 | self._log_data = self.readLog(log_path) | ||
| 159 | |||
| 160 | self.assertEqual(len(self._log_data), 2) | ||
| 161 | start_event = self._log_data[1] | ||
| 162 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 163 | self.verifyCommonKeys(start_event, expected_event_name="start") | ||
| 164 | # Check for 'start' event specific fields. | ||
| 165 | self.assertIn("argv", start_event) | ||
| 166 | self.assertTrue(isinstance(start_event["argv"], list)) | ||
| 167 | |||
| 168 | def test_exit_event_result_none(self): | ||
| 169 | """Test 'exit' event data is valid when result is None. | ||
| 170 | |||
| 171 | We expect None result to be converted to 0 in the exit event data. | ||
| 172 | |||
| 173 | Expected event log: | ||
| 174 | <version event> | ||
| 175 | <exit event> | ||
| 176 | """ | ||
| 177 | self._event_log.ExitEvent(None) | ||
| 178 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 179 | log_path = self._event_log.Write(path=tempdir) | ||
| 180 | self._log_data = self.readLog(log_path) | ||
| 181 | |||
| 182 | self.assertEqual(len(self._log_data), 2) | ||
| 183 | exit_event = self._log_data[1] | ||
| 184 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 185 | self.verifyCommonKeys(exit_event, expected_event_name="exit") | ||
| 186 | # Check for 'exit' event specific fields. | ||
| 187 | self.assertIn("code", exit_event) | ||
| 188 | # 'None' result should convert to 0 (successful) return code. | ||
| 189 | self.assertEqual(exit_event["code"], 0) | ||
| 190 | |||
| 191 | def test_exit_event_result_integer(self): | ||
| 192 | """Test 'exit' event data is valid when result is an integer. | ||
| 193 | |||
| 194 | Expected event log: | ||
| 195 | <version event> | ||
| 196 | <exit event> | ||
| 197 | """ | ||
| 198 | self._event_log.ExitEvent(2) | ||
| 199 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 200 | log_path = self._event_log.Write(path=tempdir) | ||
| 201 | self._log_data = self.readLog(log_path) | ||
| 202 | |||
| 203 | self.assertEqual(len(self._log_data), 2) | ||
| 204 | exit_event = self._log_data[1] | ||
| 205 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 206 | self.verifyCommonKeys(exit_event, expected_event_name="exit") | ||
| 207 | # Check for 'exit' event specific fields. | ||
| 208 | self.assertIn("code", exit_event) | ||
| 209 | self.assertEqual(exit_event["code"], 2) | ||
| 210 | |||
| 211 | def test_command_event(self): | ||
| 212 | """Test and validate 'command' event data is valid. | ||
| 213 | |||
| 214 | Expected event log: | ||
| 215 | <version event> | ||
| 216 | <command event> | ||
| 217 | """ | ||
| 218 | self._event_log.CommandEvent(name="repo", subcommands=["init", "this"]) | ||
| 219 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 220 | log_path = self._event_log.Write(path=tempdir) | ||
| 221 | self._log_data = self.readLog(log_path) | ||
| 222 | |||
| 223 | self.assertEqual(len(self._log_data), 2) | ||
| 224 | command_event = self._log_data[1] | ||
| 225 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 226 | self.verifyCommonKeys(command_event, expected_event_name="cmd_name") | ||
| 227 | # Check for 'command' event specific fields. | ||
| 228 | self.assertIn("name", command_event) | ||
| 229 | self.assertEqual(command_event["name"], "repo-init-this") | ||
| 230 | |||
| 231 | def test_def_params_event_repo_config(self): | ||
| 232 | """Test 'def_params' event data outputs only repo config keys. | ||
| 233 | |||
| 234 | Expected event log: | ||
| 235 | <version event> | ||
| 236 | <def_param event> | ||
| 237 | <def_param event> | ||
| 238 | """ | ||
| 239 | config = { | ||
| 240 | "git.foo": "bar", | ||
| 241 | "repo.partialclone": "true", | ||
| 242 | "repo.partialclonefilter": "blob:none", | ||
| 243 | } | ||
| 244 | self._event_log.DefParamRepoEvents(config) | ||
| 245 | |||
| 246 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 247 | log_path = self._event_log.Write(path=tempdir) | ||
| 248 | self._log_data = self.readLog(log_path) | ||
| 249 | |||
| 250 | self.assertEqual(len(self._log_data), 3) | ||
| 251 | def_param_events = self._log_data[1:] | ||
| 252 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 253 | |||
| 254 | for event in def_param_events: | ||
| 255 | self.verifyCommonKeys(event, expected_event_name="def_param") | ||
| 256 | # Check for 'def_param' event specific fields. | ||
| 257 | self.assertIn("param", event) | ||
| 258 | self.assertIn("value", event) | ||
| 259 | self.assertTrue(event["param"].startswith("repo.")) | ||
| 260 | |||
| 261 | def test_def_params_event_no_repo_config(self): | ||
| 262 | """Test 'def_params' event data won't output non-repo config keys. | ||
| 263 | |||
| 264 | Expected event log: | ||
| 265 | <version event> | ||
| 266 | """ | ||
| 267 | config = { | ||
| 268 | "git.foo": "bar", | ||
| 269 | "git.core.foo2": "baz", | ||
| 270 | } | ||
| 271 | self._event_log.DefParamRepoEvents(config) | ||
| 272 | |||
| 273 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 274 | log_path = self._event_log.Write(path=tempdir) | ||
| 275 | self._log_data = self.readLog(log_path) | ||
| 276 | |||
| 277 | self.assertEqual(len(self._log_data), 1) | ||
| 278 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 279 | |||
| 280 | def test_data_event_config(self): | ||
| 281 | """Test 'data' event data outputs all config keys. | ||
| 282 | |||
| 283 | Expected event log: | ||
| 284 | <version event> | ||
| 285 | <data event> | ||
| 286 | <data event> | ||
| 287 | """ | ||
| 288 | config = { | ||
| 289 | "git.foo": "bar", | ||
| 290 | "repo.partialclone": "false", | ||
| 291 | "repo.syncstate.superproject.hassuperprojecttag": "true", | ||
| 292 | "repo.syncstate.superproject.sys.argv": ["--", "sync", "protobuf"], | ||
| 293 | } | ||
| 294 | prefix_value = "prefix" | ||
| 295 | self._event_log.LogDataConfigEvents(config, prefix_value) | ||
| 296 | |||
| 297 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 298 | log_path = self._event_log.Write(path=tempdir) | ||
| 299 | self._log_data = self.readLog(log_path) | ||
| 300 | |||
| 301 | self.assertEqual(len(self._log_data), 5) | ||
| 302 | data_events = self._log_data[1:] | ||
| 303 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 304 | |||
| 305 | for event in data_events: | ||
| 306 | self.verifyCommonKeys(event) | ||
| 307 | # Check for 'data' event specific fields. | ||
| 308 | self.assertIn("key", event) | ||
| 309 | self.assertIn("value", event) | ||
| 310 | key = event["key"] | ||
| 311 | key = self.remove_prefix(key, f"{prefix_value}/") | ||
| 312 | value = event["value"] | ||
| 313 | self.assertEqual( | ||
| 314 | self._event_log.GetDataEventName(value), event["event"] | ||
| 315 | ) | ||
| 316 | self.assertTrue(key in config and value == config[key]) | ||
| 317 | |||
| 318 | def test_error_event(self): | ||
| 319 | """Test and validate 'error' event data is valid. | ||
| 320 | |||
| 321 | Expected event log: | ||
| 322 | <version event> | ||
| 323 | <error event> | ||
| 324 | """ | ||
| 325 | msg = "invalid option: --cahced" | ||
| 326 | fmt = "invalid option: %s" | ||
| 327 | self._event_log.ErrorEvent(msg, fmt) | ||
| 328 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 329 | log_path = self._event_log.Write(path=tempdir) | ||
| 330 | self._log_data = self.readLog(log_path) | ||
| 331 | |||
| 332 | self.assertEqual(len(self._log_data), 2) | ||
| 333 | error_event = self._log_data[1] | ||
| 334 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 335 | self.verifyCommonKeys(error_event, expected_event_name="error") | ||
| 336 | # Check for 'error' event specific fields. | ||
| 337 | self.assertIn("msg", error_event) | ||
| 338 | self.assertIn("fmt", error_event) | ||
| 339 | self.assertEqual(error_event["msg"], f"RepoErrorEvent:{msg}") | ||
| 340 | self.assertEqual(error_event["fmt"], f"RepoErrorEvent:{fmt}") | ||
| 341 | |||
| 342 | def test_write_with_filename(self): | ||
| 343 | """Test Write() with a path to a file exits with None.""" | ||
| 344 | self.assertIsNone(self._event_log.Write(path="path/to/file")) | ||
| 345 | |||
| 346 | def test_write_with_git_config(self): | ||
| 347 | """Test Write() uses the git config path when 'git config' call | ||
| 348 | succeeds.""" | ||
| 349 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 350 | with mock.patch.object( | ||
| 351 | self._event_log, | ||
| 352 | "_GetEventTargetPath", | ||
| 353 | return_value=tempdir, | ||
| 354 | ): | ||
| 355 | self.assertEqual( | ||
| 356 | os.path.dirname(self._event_log.Write()), tempdir | ||
| 357 | ) | ||
| 358 | |||
| 359 | def test_write_no_git_config(self): | ||
| 360 | """Test Write() with no git config variable present exits with None.""" | ||
| 361 | with mock.patch.object( | ||
| 362 | self._event_log, "_GetEventTargetPath", return_value=None | ||
| 363 | ): | ||
| 364 | self.assertIsNone(self._event_log.Write()) | ||
| 365 | |||
| 366 | def test_write_non_string(self): | ||
| 367 | """Test Write() with non-string type for |path| throws TypeError.""" | ||
| 368 | with self.assertRaises(TypeError): | ||
| 369 | self._event_log.Write(path=1234) | ||
| 370 | |||
| 371 | @unittest.skipIf(not hasattr(socket, "AF_UNIX"), "Requires AF_UNIX sockets") | ||
| 372 | def test_write_socket(self): | ||
| 373 | """Test Write() with Unix domain socket for |path| and validate received | ||
| 374 | traces.""" | ||
| 375 | received_traces = [] | ||
| 376 | with tempfile.TemporaryDirectory( | ||
| 377 | prefix="test_server_sockets" | ||
| 378 | ) as tempdir: | ||
| 379 | socket_path = os.path.join(tempdir, "server.sock") | ||
| 380 | server_ready = threading.Condition() | ||
| 381 | # Start "server" listening on Unix domain socket at socket_path. | ||
| 382 | server_thread = threading.Thread( | ||
| 383 | target=serverLoggingThread, | ||
| 384 | args=(socket_path, server_ready, received_traces), | ||
| 385 | ) | ||
| 386 | try: | ||
| 387 | server_thread.start() | ||
| 388 | |||
| 389 | with server_ready: | ||
| 390 | server_ready.wait(timeout=120) | ||
| 391 | 417 | ||
| 392 | self._event_log.StartEvent([]) | 418 | event_log.StartEvent([]) |
| 393 | path = self._event_log.Write(path=f"af_unix:{socket_path}") | 419 | path = event_log.Write(path=f"af_unix:{socket_path}") |
| 394 | finally: | 420 | finally: |
| 395 | server_thread.join(timeout=5) | 421 | server_thread.join(timeout=5) |
| 396 | 422 | ||
| 397 | self.assertEqual(path, f"af_unix:stream:{socket_path}") | 423 | assert path == f"af_unix:stream:{socket_path}" |
| 398 | self.assertEqual(len(received_traces), 2) | 424 | assert len(received_traces) == 2 |
| 399 | version_event = json.loads(received_traces[0]) | 425 | version_event = json.loads(received_traces[0]) |
| 400 | start_event = json.loads(received_traces[1]) | 426 | start_event = json.loads(received_traces[1]) |
| 401 | self.verifyCommonKeys(version_event, expected_event_name="version") | 427 | verify_common_keys(version_event, expected_event_name="version") |
| 402 | self.verifyCommonKeys(start_event, expected_event_name="start") | 428 | verify_common_keys(start_event, expected_event_name="start") |
| 403 | # Check for 'start' event specific fields. | 429 | # Check for 'start' event specific fields. |
| 404 | self.assertIn("argv", start_event) | 430 | assert "argv" in start_event |
| 405 | self.assertIsInstance(start_event["argv"], list) | 431 | assert isinstance(start_event["argv"], list) |
| 406 | 432 | ||
| 407 | 433 | ||
| 408 | class EventLogVerboseTestCase(unittest.TestCase): | 434 | class TestEventLogVerbose: |
| 409 | """TestCase for the EventLog module verbose logging.""" | 435 | """TestCase for the EventLog module verbose logging.""" |
| 410 | 436 | ||
| 411 | def setUp(self): | 437 | def test_write_socket_error_no_verbose(self) -> None: |
| 412 | self._event_log = git_trace2_event_log.EventLog(env={}) | ||
| 413 | |||
| 414 | def test_write_socket_error_no_verbose(self): | ||
| 415 | """Test Write() suppression of socket errors when not verbose.""" | 438 | """Test Write() suppression of socket errors when not verbose.""" |
| 416 | self._event_log.verbose = False | 439 | event_log = git_trace2_event_log.EventLog(env={}) |
| 440 | event_log.verbose = False | ||
| 417 | with contextlib.redirect_stderr( | 441 | with contextlib.redirect_stderr( |
| 418 | io.StringIO() | 442 | io.StringIO() |
| 419 | ) as mock_stderr, mock.patch("socket.socket", side_effect=OSError): | 443 | ) as mock_stderr, mock.patch("socket.socket", side_effect=OSError): |
| 420 | self._event_log.Write(path="af_unix:stream:/tmp/test_sock") | 444 | event_log.Write(path="af_unix:stream:/tmp/test_sock") |
| 421 | self.assertEqual(mock_stderr.getvalue(), "") | 445 | assert mock_stderr.getvalue() == "" |
| 422 | 446 | ||
| 423 | def test_write_socket_error_verbose(self): | 447 | def test_write_socket_error_verbose(self) -> None: |
| 424 | """Test Write() printing of socket errors when verbose.""" | 448 | """Test Write() printing of socket errors when verbose.""" |
| 425 | self._event_log.verbose = True | 449 | event_log = git_trace2_event_log.EventLog(env={}) |
| 450 | event_log.verbose = True | ||
| 426 | with contextlib.redirect_stderr( | 451 | with contextlib.redirect_stderr( |
| 427 | io.StringIO() | 452 | io.StringIO() |
| 428 | ) as mock_stderr, mock.patch( | 453 | ) as mock_stderr, mock.patch( |
| 429 | "socket.socket", side_effect=OSError("Mock error") | 454 | "socket.socket", side_effect=OSError("Mock error") |
| 430 | ): | 455 | ): |
| 431 | self._event_log.Write(path="af_unix:stream:/tmp/test_sock") | 456 | event_log.Write(path="af_unix:stream:/tmp/test_sock") |
| 432 | self.assertIn( | 457 | assert ( |
| 433 | "git trace2 logging failed: Mock error", | 458 | "git trace2 logging failed: Mock error" |
| 434 | mock_stderr.getvalue(), | 459 | in mock_stderr.getvalue() |
| 435 | ) | 460 | ) |
| 436 | 461 | ||
| 437 | def test_write_file_error_no_verbose(self): | 462 | def test_write_file_error_no_verbose(self) -> None: |
| 438 | """Test Write() suppression of file errors when not verbose.""" | 463 | """Test Write() suppression of file errors when not verbose.""" |
| 439 | self._event_log.verbose = False | 464 | event_log = git_trace2_event_log.EventLog(env={}) |
| 465 | event_log.verbose = False | ||
| 440 | with contextlib.redirect_stderr( | 466 | with contextlib.redirect_stderr( |
| 441 | io.StringIO() | 467 | io.StringIO() |
| 442 | ) as mock_stderr, mock.patch( | 468 | ) as mock_stderr, mock.patch( |
| 443 | "tempfile.NamedTemporaryFile", side_effect=FileExistsError | 469 | "tempfile.NamedTemporaryFile", side_effect=FileExistsError |
| 444 | ): | 470 | ): |
| 445 | self._event_log.Write(path="/tmp") | 471 | event_log.Write(path="/tmp") |
| 446 | self.assertEqual(mock_stderr.getvalue(), "") | 472 | assert mock_stderr.getvalue() == "" |
| 447 | 473 | ||
| 448 | def test_write_file_error_verbose(self): | 474 | def test_write_file_error_verbose(self) -> None: |
| 449 | """Test Write() printing of file errors when verbose.""" | 475 | """Test Write() printing of file errors when verbose.""" |
| 450 | self._event_log.verbose = True | 476 | event_log = git_trace2_event_log.EventLog(env={}) |
| 477 | event_log.verbose = True | ||
| 451 | with contextlib.redirect_stderr( | 478 | with contextlib.redirect_stderr( |
| 452 | io.StringIO() | 479 | io.StringIO() |
| 453 | ) as mock_stderr, mock.patch( | 480 | ) as mock_stderr, mock.patch( |
| 454 | "tempfile.NamedTemporaryFile", | 481 | "tempfile.NamedTemporaryFile", |
| 455 | side_effect=FileExistsError("Mock error"), | 482 | side_effect=FileExistsError("Mock error"), |
| 456 | ): | 483 | ): |
| 457 | self._event_log.Write(path="/tmp") | 484 | event_log.Write(path="/tmp") |
| 458 | self.assertIn( | 485 | assert ( |
| 459 | "git trace2 logging failed: FileExistsError", | 486 | "git trace2 logging failed: FileExistsError" |
| 460 | mock_stderr.getvalue(), | 487 | in mock_stderr.getvalue() |
| 461 | ) | 488 | ) |
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 5e0c78334..473f781ba 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py | |||
| @@ -18,10 +18,10 @@ import os | |||
| 18 | from pathlib import Path | 18 | from pathlib import Path |
| 19 | import platform | 19 | import platform |
| 20 | import re | 20 | import re |
| 21 | import tempfile | ||
| 22 | import unittest | ||
| 23 | import xml.dom.minidom | 21 | import xml.dom.minidom |
| 24 | 22 | ||
| 23 | import pytest | ||
| 24 | |||
| 25 | import error | 25 | import error |
| 26 | import manifest_xml | 26 | import manifest_xml |
| 27 | 27 | ||
| @@ -66,7 +66,7 @@ if os.path.sep != "/": | |||
| 66 | ) | 66 | ) |
| 67 | 67 | ||
| 68 | 68 | ||
| 69 | def sort_attributes(manifest): | 69 | def sort_attributes(manifest: str) -> str: |
| 70 | """Sort the attributes of all elements alphabetically. | 70 | """Sort the attributes of all elements alphabetically. |
| 71 | 71 | ||
| 72 | This is needed because different versions of the toxml() function from | 72 | This is needed because different versions of the toxml() function from |
| @@ -93,13 +93,12 @@ def sort_attributes(manifest): | |||
| 93 | return new_manifest | 93 | return new_manifest |
| 94 | 94 | ||
| 95 | 95 | ||
| 96 | class ManifestParseTestCase(unittest.TestCase): | 96 | class RepoClient: |
| 97 | """TestCase for parsing manifests.""" | 97 | """Basic empty repo checkout.""" |
| 98 | 98 | ||
| 99 | def setUp(self): | 99 | def __init__(self, topdir: Path): |
| 100 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests") | 100 | self.topdir = topdir |
| 101 | self.tempdir = Path(self.tempdirobj.name) | 101 | self.repodir = self.topdir / ".repo" |
| 102 | self.repodir = self.tempdir / ".repo" | ||
| 103 | self.manifest_dir = self.repodir / "manifests" | 102 | self.manifest_dir = self.repodir / "manifests" |
| 104 | self.manifest_file = self.repodir / manifest_xml.MANIFEST_FILE_NAME | 103 | self.manifest_file = self.repodir / manifest_xml.MANIFEST_FILE_NAME |
| 105 | self.local_manifest_dir = ( | 104 | self.local_manifest_dir = ( |
| @@ -107,7 +106,6 @@ class ManifestParseTestCase(unittest.TestCase): | |||
| 107 | ) | 106 | ) |
| 108 | self.repodir.mkdir() | 107 | self.repodir.mkdir() |
| 109 | self.manifest_dir.mkdir() | 108 | self.manifest_dir.mkdir() |
| 110 | |||
| 111 | # The manifest parsing really wants a git repo currently. | 109 | # The manifest parsing really wants a git repo currently. |
| 112 | gitdir = self.repodir / "manifests.git" | 110 | gitdir = self.repodir / "manifests.git" |
| 113 | gitdir.mkdir() | 111 | gitdir.mkdir() |
| @@ -117,10 +115,7 @@ class ManifestParseTestCase(unittest.TestCase): | |||
| 117 | """ | 115 | """ |
| 118 | ) | 116 | ) |
| 119 | 117 | ||
| 120 | def tearDown(self): | 118 | def get_xml_manifest(self, data: str) -> manifest_xml.XmlManifest: |
| 121 | self.tempdirobj.cleanup() | ||
| 122 | |||
| 123 | def getXmlManifest(self, data): | ||
| 124 | """Helper to initialize a manifest for testing.""" | 119 | """Helper to initialize a manifest for testing.""" |
| 125 | self.manifest_file.write_text(data, encoding="utf-8") | 120 | self.manifest_file.write_text(data, encoding="utf-8") |
| 126 | return manifest_xml.XmlManifest( | 121 | return manifest_xml.XmlManifest( |
| @@ -128,33 +123,43 @@ class ManifestParseTestCase(unittest.TestCase): | |||
| 128 | ) | 123 | ) |
| 129 | 124 | ||
| 130 | @staticmethod | 125 | @staticmethod |
| 131 | def encodeXmlAttr(attr): | 126 | def encode_xml_attr(attr: str) -> str: |
| 132 | """Encode |attr| using XML escape rules.""" | 127 | """Encode |attr| using XML escape rules.""" |
| 133 | return attr.replace("\r", "
").replace("\n", "
") | 128 | return attr.replace("\r", "
").replace("\n", "
") |
| 134 | 129 | ||
| 135 | 130 | ||
| 136 | class ManifestValidateFilePaths(unittest.TestCase): | 131 | @pytest.fixture |
| 132 | def repo_client(tmp_path: Path) -> RepoClient: | ||
| 133 | """Generate a basic empty repo checkout. | ||
| 134 | |||
| 135 | The manifest is not generated. | ||
| 136 | """ | ||
| 137 | return RepoClient(tmp_path) | ||
| 138 | |||
| 139 | |||
| 140 | class TestManifestValidateFilePaths: | ||
| 137 | """Check _ValidateFilePaths helper. | 141 | """Check _ValidateFilePaths helper. |
| 138 | 142 | ||
| 139 | This doesn't access a real filesystem. | 143 | This doesn't access a real filesystem. |
| 140 | """ | 144 | """ |
| 141 | 145 | ||
| 142 | def check_both(self, *args): | 146 | def check_both(self, src: str, dest: str) -> None: |
| 143 | manifest_xml.XmlManifest._ValidateFilePaths("copyfile", *args) | 147 | """Check copyfile & linkfile.""" |
| 144 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args) | 148 | manifest_xml.XmlManifest._ValidateFilePaths("copyfile", src, dest) |
| 149 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", src, dest) | ||
| 145 | 150 | ||
| 146 | def test_normal_path(self): | 151 | def test_normal_path(self) -> None: |
| 147 | """Make sure good paths are accepted.""" | 152 | """Make sure good paths are accepted.""" |
| 148 | self.check_both("foo", "bar") | 153 | self.check_both("foo", "bar") |
| 149 | self.check_both("foo/bar", "bar") | 154 | self.check_both("foo/bar", "bar") |
| 150 | self.check_both("foo", "bar/bar") | 155 | self.check_both("foo", "bar/bar") |
| 151 | self.check_both("foo/bar", "bar/bar") | 156 | self.check_both("foo/bar", "bar/bar") |
| 152 | 157 | ||
| 153 | def test_symlink_targets(self): | 158 | def test_symlink_targets(self) -> None: |
| 154 | """Some extra checks for symlinks.""" | 159 | """Some extra checks for symlinks.""" |
| 155 | 160 | ||
| 156 | def check(*args): | 161 | def check(src: str, dest: str) -> None: |
| 157 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args) | 162 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", src, dest) |
| 158 | 163 | ||
| 159 | # We allow symlinks to end in a slash since we allow them to point to | 164 | # We allow symlinks to end in a slash since we allow them to point to |
| 160 | # dirs in general. Technically the slash isn't necessary. | 165 | # dirs in general. Technically the slash isn't necessary. |
| @@ -162,114 +167,111 @@ class ManifestValidateFilePaths(unittest.TestCase): | |||
| 162 | # We allow a single '.' to get a reference to the project itself. | 167 | # We allow a single '.' to get a reference to the project itself. |
| 163 | check(".", "bar") | 168 | check(".", "bar") |
| 164 | 169 | ||
| 165 | def test_bad_paths(self): | 170 | def test_bad_paths(self) -> None: |
| 166 | """Make sure bad paths (src & dest) are rejected.""" | 171 | """Make sure bad paths (src & dest) are rejected.""" |
| 167 | for path in INVALID_FS_PATHS: | 172 | for path in INVALID_FS_PATHS: |
| 168 | self.assertRaises( | 173 | with pytest.raises(error.ManifestInvalidPathError): |
| 169 | error.ManifestInvalidPathError, self.check_both, path, "a" | 174 | self.check_both(path, "a") |
| 170 | ) | 175 | with pytest.raises(error.ManifestInvalidPathError): |
| 171 | self.assertRaises( | 176 | self.check_both("a", path) |
| 172 | error.ManifestInvalidPathError, self.check_both, "a", path | ||
| 173 | ) | ||
| 174 | 177 | ||
| 175 | 178 | ||
| 176 | class ValueTests(unittest.TestCase): | 179 | class TestValue: |
| 177 | """Check utility parsing code.""" | 180 | """Check utility parsing code.""" |
| 178 | 181 | ||
| 179 | def _get_node(self, text): | 182 | def _get_node(self, text: str) -> xml.dom.minidom.Element: |
| 180 | return xml.dom.minidom.parseString(text).firstChild | 183 | return xml.dom.minidom.parseString(text).firstChild |
| 181 | 184 | ||
| 182 | def test_bool_default(self): | 185 | def test_bool_default(self) -> None: |
| 183 | """Check XmlBool default handling.""" | 186 | """Check XmlBool default handling.""" |
| 184 | node = self._get_node("<node/>") | 187 | node = self._get_node("<node/>") |
| 185 | self.assertIsNone(manifest_xml.XmlBool(node, "a")) | 188 | assert manifest_xml.XmlBool(node, "a") is None |
| 186 | self.assertIsNone(manifest_xml.XmlBool(node, "a", None)) | 189 | assert manifest_xml.XmlBool(node, "a", None) is None |
| 187 | self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123)) | 190 | assert manifest_xml.XmlBool(node, "a", 123) == 123 |
| 188 | 191 | ||
| 189 | node = self._get_node('<node a=""/>') | 192 | node = self._get_node('<node a=""/>') |
| 190 | self.assertIsNone(manifest_xml.XmlBool(node, "a")) | 193 | assert manifest_xml.XmlBool(node, "a") is None |
| 191 | 194 | ||
| 192 | def test_bool_invalid(self): | 195 | def test_bool_invalid(self) -> None: |
| 193 | """Check XmlBool invalid handling.""" | 196 | """Check XmlBool invalid handling.""" |
| 194 | node = self._get_node('<node a="moo"/>') | 197 | node = self._get_node('<node a="moo"/>') |
| 195 | self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123)) | 198 | assert manifest_xml.XmlBool(node, "a", 123) == 123 |
| 196 | 199 | ||
| 197 | def test_bool_true(self): | 200 | def test_bool_true(self) -> None: |
| 198 | """Check XmlBool true values.""" | 201 | """Check XmlBool true values.""" |
| 199 | for value in ("yes", "true", "1"): | 202 | for value in ("yes", "true", "1"): |
| 200 | node = self._get_node(f'<node a="{value}"/>') | 203 | node = self._get_node(f'<node a="{value}"/>') |
| 201 | self.assertTrue(manifest_xml.XmlBool(node, "a")) | 204 | assert manifest_xml.XmlBool(node, "a") is True |
| 202 | 205 | ||
| 203 | def test_bool_false(self): | 206 | def test_bool_false(self) -> None: |
| 204 | """Check XmlBool false values.""" | 207 | """Check XmlBool false values.""" |
| 205 | for value in ("no", "false", "0"): | 208 | for value in ("no", "false", "0"): |
| 206 | node = self._get_node(f'<node a="{value}"/>') | 209 | node = self._get_node(f'<node a="{value}"/>') |
| 207 | self.assertFalse(manifest_xml.XmlBool(node, "a")) | 210 | assert manifest_xml.XmlBool(node, "a") is False |
| 208 | 211 | ||
| 209 | def test_int_default(self): | 212 | def test_int_default(self) -> None: |
| 210 | """Check XmlInt default handling.""" | 213 | """Check XmlInt default handling.""" |
| 211 | node = self._get_node("<node/>") | 214 | node = self._get_node("<node/>") |
| 212 | self.assertIsNone(manifest_xml.XmlInt(node, "a")) | 215 | assert manifest_xml.XmlInt(node, "a") is None |
| 213 | self.assertIsNone(manifest_xml.XmlInt(node, "a", None)) | 216 | assert manifest_xml.XmlInt(node, "a", None) is None |
| 214 | self.assertEqual(123, manifest_xml.XmlInt(node, "a", 123)) | 217 | assert manifest_xml.XmlInt(node, "a", 123) == 123 |
| 215 | 218 | ||
| 216 | node = self._get_node('<node a=""/>') | 219 | node = self._get_node('<node a=""/>') |
| 217 | self.assertIsNone(manifest_xml.XmlInt(node, "a")) | 220 | assert manifest_xml.XmlInt(node, "a") is None |
| 218 | 221 | ||
| 219 | def test_int_good(self): | 222 | def test_int_good(self) -> None: |
| 220 | """Check XmlInt numeric handling.""" | 223 | """Check XmlInt numeric handling.""" |
| 221 | for value in (-1, 0, 1, 50000): | 224 | for value in (-1, 0, 1, 50000): |
| 222 | node = self._get_node(f'<node a="{value}"/>') | 225 | node = self._get_node(f'<node a="{value}"/>') |
| 223 | self.assertEqual(value, manifest_xml.XmlInt(node, "a")) | 226 | assert manifest_xml.XmlInt(node, "a") == value |
| 224 | 227 | ||
| 225 | def test_int_invalid(self): | 228 | def test_int_invalid(self) -> None: |
| 226 | """Check XmlInt invalid handling.""" | 229 | """Check XmlInt invalid handling.""" |
| 227 | with self.assertRaises(error.ManifestParseError): | 230 | with pytest.raises(error.ManifestParseError): |
| 228 | node = self._get_node('<node a="xx"/>') | 231 | node = self._get_node('<node a="xx"/>') |
| 229 | manifest_xml.XmlInt(node, "a") | 232 | manifest_xml.XmlInt(node, "a") |
| 230 | 233 | ||
| 231 | 234 | ||
| 232 | class XmlManifestTests(ManifestParseTestCase): | 235 | class TestXmlManifest: |
| 233 | """Check manifest processing.""" | 236 | """Check manifest processing.""" |
| 234 | 237 | ||
| 235 | def test_empty(self): | 238 | def test_empty(self, repo_client: RepoClient) -> None: |
| 236 | """Parse an 'empty' manifest file.""" | 239 | """Parse an 'empty' manifest file.""" |
| 237 | manifest = self.getXmlManifest( | 240 | manifest = repo_client.get_xml_manifest( |
| 238 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" | 241 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 239 | ) | 242 | ) |
| 240 | self.assertEqual(manifest.remotes, {}) | 243 | assert manifest.remotes == {} |
| 241 | self.assertEqual(manifest.projects, []) | 244 | assert manifest.projects == [] |
| 242 | 245 | ||
| 243 | def test_link(self): | 246 | def test_link(self, repo_client: RepoClient) -> None: |
| 244 | """Verify Link handling with new names.""" | 247 | """Verify Link handling with new names.""" |
| 245 | manifest = manifest_xml.XmlManifest( | 248 | manifest = repo_client.get_xml_manifest("<manifest></manifest>") |
| 246 | str(self.repodir), str(self.manifest_file) | 249 | (repo_client.manifest_dir / "foo.xml").write_text( |
| 250 | "<manifest></manifest>" | ||
| 247 | ) | 251 | ) |
| 248 | (self.manifest_dir / "foo.xml").write_text("<manifest></manifest>") | ||
| 249 | manifest.Link("foo.xml") | 252 | manifest.Link("foo.xml") |
| 250 | self.assertIn( | 253 | assert ( |
| 251 | '<include name="foo.xml" />', self.manifest_file.read_text() | 254 | '<include name="foo.xml" />' |
| 255 | in repo_client.manifest_file.read_text() | ||
| 252 | ) | 256 | ) |
| 253 | 257 | ||
| 254 | def test_toxml_empty(self): | 258 | def test_toxml_empty(self, repo_client: RepoClient) -> None: |
| 255 | """Verify the ToXml() helper.""" | 259 | """Verify the ToXml() helper.""" |
| 256 | manifest = self.getXmlManifest( | 260 | manifest = repo_client.get_xml_manifest( |
| 257 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" | 261 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 258 | ) | 262 | ) |
| 259 | self.assertEqual( | 263 | assert manifest.ToXml().toxml() == '<?xml version="1.0" ?><manifest/>' |
| 260 | manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>' | ||
| 261 | ) | ||
| 262 | 264 | ||
| 263 | def test_todict_empty(self): | 265 | def test_todict_empty(self, repo_client: RepoClient) -> None: |
| 264 | """Verify the ToDict() helper.""" | 266 | """Verify the ToDict() helper.""" |
| 265 | manifest = self.getXmlManifest( | 267 | manifest = repo_client.get_xml_manifest( |
| 266 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" | 268 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 267 | ) | 269 | ) |
| 268 | self.assertEqual(manifest.ToDict(), {}) | 270 | assert manifest.ToDict() == {} |
| 269 | 271 | ||
| 270 | def test_toxml_omit_local(self): | 272 | def test_toxml_omit_local(self, repo_client: RepoClient) -> None: |
| 271 | """Does not include local_manifests projects when omit_local=True.""" | 273 | """Does not include local_manifests projects when omit_local=True.""" |
| 272 | manifest = self.getXmlManifest( | 274 | manifest = repo_client.get_xml_manifest( |
| 273 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' | 275 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' |
| 274 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' | 276 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' |
| 275 | '<project name="p" groups="local::me"/>' | 277 | '<project name="p" groups="local::me"/>' |
| @@ -277,16 +279,16 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 277 | '<project name="r" groups="keep"/>' | 279 | '<project name="r" groups="keep"/>' |
| 278 | "</manifest>" | 280 | "</manifest>" |
| 279 | ) | 281 | ) |
| 280 | self.assertEqual( | 282 | assert ( |
| 281 | sort_attributes(manifest.ToXml(omit_local=True).toxml()), | 283 | sort_attributes(manifest.ToXml(omit_local=True).toxml()) |
| 282 | '<?xml version="1.0" ?><manifest>' | 284 | == '<?xml version="1.0" ?><manifest>' |
| 283 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' | 285 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' |
| 284 | '<project name="q"/><project groups="keep" name="r"/></manifest>', | 286 | '<project name="q"/><project groups="keep" name="r"/></manifest>' |
| 285 | ) | 287 | ) |
| 286 | 288 | ||
| 287 | def test_toxml_with_local(self): | 289 | def test_toxml_with_local(self, repo_client: RepoClient) -> None: |
| 288 | """Does include local_manifests projects when omit_local=False.""" | 290 | """Does include local_manifests projects when omit_local=False.""" |
| 289 | manifest = self.getXmlManifest( | 291 | manifest = repo_client.get_xml_manifest( |
| 290 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' | 292 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' |
| 291 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' | 293 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' |
| 292 | '<project name="p" groups="local::me"/>' | 294 | '<project name="p" groups="local::me"/>' |
| @@ -294,17 +296,17 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 294 | '<project name="r" groups="keep"/>' | 296 | '<project name="r" groups="keep"/>' |
| 295 | "</manifest>" | 297 | "</manifest>" |
| 296 | ) | 298 | ) |
| 297 | self.assertEqual( | 299 | assert ( |
| 298 | sort_attributes(manifest.ToXml(omit_local=False).toxml()), | 300 | sort_attributes(manifest.ToXml(omit_local=False).toxml()) |
| 299 | '<?xml version="1.0" ?><manifest>' | 301 | == '<?xml version="1.0" ?><manifest>' |
| 300 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' | 302 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' |
| 301 | '<project groups="local::me" name="p"/>' | 303 | '<project groups="local::me" name="p"/>' |
| 302 | '<project name="q"/><project groups="keep" name="r"/></manifest>', | 304 | '<project name="q"/><project groups="keep" name="r"/></manifest>' |
| 303 | ) | 305 | ) |
| 304 | 306 | ||
| 305 | def test_repo_hooks(self): | 307 | def test_repo_hooks(self, repo_client: RepoClient) -> None: |
| 306 | """Check repo-hooks settings.""" | 308 | """Check repo-hooks settings.""" |
| 307 | manifest = self.getXmlManifest( | 309 | manifest = repo_client.get_xml_manifest( |
| 308 | """ | 310 | """ |
| 309 | <manifest> | 311 | <manifest> |
| 310 | <remote name="test-remote" fetch="http://localhost" /> | 312 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -314,14 +316,12 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 314 | </manifest> | 316 | </manifest> |
| 315 | """ | 317 | """ |
| 316 | ) | 318 | ) |
| 317 | self.assertEqual(manifest.repo_hooks_project.name, "repohooks") | 319 | assert manifest.repo_hooks_project.name == "repohooks" |
| 318 | self.assertEqual( | 320 | assert manifest.repo_hooks_project.enabled_repo_hooks == ["a", "b"] |
| 319 | manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"] | ||
| 320 | ) | ||
| 321 | 321 | ||
| 322 | def test_repo_hooks_unordered(self): | 322 | def test_repo_hooks_unordered(self, repo_client: RepoClient) -> None: |
| 323 | """Check repo-hooks settings work even if the project def comes second.""" # noqa: E501 | 323 | """Check repo-hooks settings work when the project comes after.""" |
| 324 | manifest = self.getXmlManifest( | 324 | manifest = repo_client.get_xml_manifest( |
| 325 | """ | 325 | """ |
| 326 | <manifest> | 326 | <manifest> |
| 327 | <remote name="test-remote" fetch="http://localhost" /> | 327 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -331,14 +331,12 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 331 | </manifest> | 331 | </manifest> |
| 332 | """ | 332 | """ |
| 333 | ) | 333 | ) |
| 334 | self.assertEqual(manifest.repo_hooks_project.name, "repohooks") | 334 | assert manifest.repo_hooks_project.name == "repohooks" |
| 335 | self.assertEqual( | 335 | assert manifest.repo_hooks_project.enabled_repo_hooks == ["a", "b"] |
| 336 | manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"] | ||
| 337 | ) | ||
| 338 | 336 | ||
| 339 | def test_unknown_tags(self): | 337 | def test_unknown_tags(self, repo_client: RepoClient) -> None: |
| 340 | """Check superproject settings.""" | 338 | """Check superproject settings.""" |
| 341 | manifest = self.getXmlManifest( | 339 | manifest = repo_client.get_xml_manifest( |
| 342 | """ | 340 | """ |
| 343 | <manifest> | 341 | <manifest> |
| 344 | <remote name="test-remote" fetch="http://localhost" /> | 342 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -349,20 +347,20 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 349 | </manifest> | 347 | </manifest> |
| 350 | """ | 348 | """ |
| 351 | ) | 349 | ) |
| 352 | self.assertEqual(manifest.superproject.name, "superproject") | 350 | assert manifest.superproject.name == "superproject" |
| 353 | self.assertEqual(manifest.superproject.remote.name, "test-remote") | 351 | assert manifest.superproject.remote.name == "test-remote" |
| 354 | self.assertEqual( | 352 | assert ( |
| 355 | sort_attributes(manifest.ToXml().toxml()), | 353 | sort_attributes(manifest.ToXml().toxml()) |
| 356 | '<?xml version="1.0" ?><manifest>' | 354 | == '<?xml version="1.0" ?><manifest>' |
| 357 | '<remote fetch="http://localhost" name="test-remote"/>' | 355 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 358 | '<default remote="test-remote" revision="refs/heads/main"/>' | 356 | '<default remote="test-remote" revision="refs/heads/main"/>' |
| 359 | '<superproject name="superproject"/>' | 357 | '<superproject name="superproject"/>' |
| 360 | "</manifest>", | 358 | "</manifest>" |
| 361 | ) | 359 | ) |
| 362 | 360 | ||
| 363 | def test_remote_annotations(self): | 361 | def test_remote_annotations(self, repo_client: RepoClient) -> None: |
| 364 | """Check remote settings.""" | 362 | """Check remote settings.""" |
| 365 | manifest = self.getXmlManifest( | 363 | manifest = repo_client.get_xml_manifest( |
| 366 | """ | 364 | """ |
| 367 | <manifest> | 365 | <manifest> |
| 368 | <remote name="test-remote" fetch="http://localhost"> | 366 | <remote name="test-remote" fetch="http://localhost"> |
| @@ -371,24 +369,20 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 371 | </manifest> | 369 | </manifest> |
| 372 | """ | 370 | """ |
| 373 | ) | 371 | ) |
| 374 | self.assertEqual( | 372 | assert manifest.remotes["test-remote"].annotations[0].name == "foo" |
| 375 | manifest.remotes["test-remote"].annotations[0].name, "foo" | 373 | assert manifest.remotes["test-remote"].annotations[0].value == "bar" |
| 376 | ) | 374 | assert ( |
| 377 | self.assertEqual( | 375 | sort_attributes(manifest.ToXml().toxml()) |
| 378 | manifest.remotes["test-remote"].annotations[0].value, "bar" | 376 | == '<?xml version="1.0" ?><manifest>' |
| 379 | ) | ||
| 380 | self.assertEqual( | ||
| 381 | sort_attributes(manifest.ToXml().toxml()), | ||
| 382 | '<?xml version="1.0" ?><manifest>' | ||
| 383 | '<remote fetch="http://localhost" name="test-remote">' | 377 | '<remote fetch="http://localhost" name="test-remote">' |
| 384 | '<annotation name="foo" value="bar"/>' | 378 | '<annotation name="foo" value="bar"/>' |
| 385 | "</remote>" | 379 | "</remote>" |
| 386 | "</manifest>", | 380 | "</manifest>" |
| 387 | ) | 381 | ) |
| 388 | 382 | ||
| 389 | def test_parse_with_xml_doctype(self): | 383 | def test_parse_with_xml_doctype(self, repo_client: RepoClient) -> None: |
| 390 | """Check correct manifest parse with DOCTYPE node present.""" | 384 | """Check correct manifest parse with DOCTYPE node present.""" |
| 391 | manifest = self.getXmlManifest( | 385 | manifest = repo_client.get_xml_manifest( |
| 392 | """<?xml version="1.0" encoding="UTF-8"?> | 386 | """<?xml version="1.0" encoding="UTF-8"?> |
| 393 | <!DOCTYPE manifest []> | 387 | <!DOCTYPE manifest []> |
| 394 | <manifest> | 388 | <manifest> |
| @@ -398,42 +392,41 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 398 | </manifest> | 392 | </manifest> |
| 399 | """ | 393 | """ |
| 400 | ) | 394 | ) |
| 401 | self.assertEqual(len(manifest.projects), 1) | 395 | assert len(manifest.projects) == 1 |
| 402 | self.assertEqual(manifest.projects[0].name, "test-project") | 396 | assert manifest.projects[0].name == "test-project" |
| 403 | 397 | ||
| 404 | def test_sync_j_max(self): | 398 | def test_sync_j_max(self, repo_client: RepoClient) -> None: |
| 405 | """Check sync-j-max handling.""" | 399 | """Check sync-j-max handling.""" |
| 406 | # Check valid value. | 400 | # Check valid value. |
| 407 | manifest = self.getXmlManifest( | 401 | manifest = repo_client.get_xml_manifest( |
| 408 | '<manifest><default sync-j-max="5" /></manifest>' | 402 | '<manifest><default sync-j-max="5" /></manifest>' |
| 409 | ) | 403 | ) |
| 410 | self.assertEqual(manifest.default.sync_j_max, 5) | 404 | assert manifest.default.sync_j_max == 5 |
| 411 | self.assertEqual( | 405 | assert ( |
| 412 | manifest.ToXml().toxml(), | 406 | manifest.ToXml().toxml() == '<?xml version="1.0" ?>' |
| 413 | '<?xml version="1.0" ?>' | 407 | '<manifest><default sync-j-max="5"/></manifest>' |
| 414 | '<manifest><default sync-j-max="5"/></manifest>', | ||
| 415 | ) | 408 | ) |
| 416 | 409 | ||
| 417 | # Check invalid values. | 410 | # Check invalid values. |
| 418 | with self.assertRaises(error.ManifestParseError): | 411 | with pytest.raises(error.ManifestParseError): |
| 419 | manifest = self.getXmlManifest( | 412 | manifest = repo_client.get_xml_manifest( |
| 420 | '<manifest><default sync-j-max="0" /></manifest>' | 413 | '<manifest><default sync-j-max="0" /></manifest>' |
| 421 | ) | 414 | ) |
| 422 | manifest.ToXml() | 415 | manifest.ToXml() |
| 423 | 416 | ||
| 424 | with self.assertRaises(error.ManifestParseError): | 417 | with pytest.raises(error.ManifestParseError): |
| 425 | manifest = self.getXmlManifest( | 418 | manifest = repo_client.get_xml_manifest( |
| 426 | '<manifest><default sync-j-max="-1" /></manifest>' | 419 | '<manifest><default sync-j-max="-1" /></manifest>' |
| 427 | ) | 420 | ) |
| 428 | manifest.ToXml() | 421 | manifest.ToXml() |
| 429 | 422 | ||
| 430 | 423 | ||
| 431 | class IncludeElementTests(ManifestParseTestCase): | 424 | class TestIncludeElement: |
| 432 | """Tests for <include>.""" | 425 | """Tests for <include>.""" |
| 433 | 426 | ||
| 434 | def test_revision_default(self): | 427 | def test_revision_default(self, repo_client: RepoClient) -> None: |
| 435 | """Check handling of revision attribute.""" | 428 | """Check handling of revision attribute.""" |
| 436 | root_m = self.manifest_dir / "root.xml" | 429 | root_m = repo_client.manifest_dir / "root.xml" |
| 437 | root_m.write_text( | 430 | root_m.write_text( |
| 438 | """ | 431 | """ |
| 439 | <manifest> | 432 | <manifest> |
| @@ -445,7 +438,7 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 445 | </manifest> | 438 | </manifest> |
| 446 | """ | 439 | """ |
| 447 | ) | 440 | ) |
| 448 | (self.manifest_dir / "stable.xml").write_text( | 441 | (repo_client.manifest_dir / "stable.xml").write_text( |
| 449 | """ | 442 | """ |
| 450 | <manifest> | 443 | <manifest> |
| 451 | <include name="man1.xml" /> | 444 | <include name="man1.xml" /> |
| @@ -455,7 +448,7 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 455 | </manifest> | 448 | </manifest> |
| 456 | """ | 449 | """ |
| 457 | ) | 450 | ) |
| 458 | (self.manifest_dir / "man1.xml").write_text( | 451 | (repo_client.manifest_dir / "man1.xml").write_text( |
| 459 | """ | 452 | """ |
| 460 | <manifest> | 453 | <manifest> |
| 461 | <project name="man1-name1" /> | 454 | <project name="man1-name1" /> |
| @@ -463,7 +456,7 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 463 | </manifest> | 456 | </manifest> |
| 464 | """ | 457 | """ |
| 465 | ) | 458 | ) |
| 466 | (self.manifest_dir / "man2.xml").write_text( | 459 | (repo_client.manifest_dir / "man2.xml").write_text( |
| 467 | """ | 460 | """ |
| 468 | <manifest> | 461 | <manifest> |
| 469 | <project name="man2-name1" /> | 462 | <project name="man2-name1" /> |
| @@ -471,31 +464,34 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 471 | </manifest> | 464 | </manifest> |
| 472 | """ | 465 | """ |
| 473 | ) | 466 | ) |
| 474 | include_m = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 467 | include_m = manifest_xml.XmlManifest( |
| 468 | str(repo_client.repodir), str(root_m) | ||
| 469 | ) | ||
| 475 | for proj in include_m.projects: | 470 | for proj in include_m.projects: |
| 476 | if proj.name == "root-name1": | 471 | if proj.name == "root-name1": |
| 477 | # Check include revision not set on root level proj. | 472 | # Check include revision not set on root level proj. |
| 478 | self.assertNotEqual("stable-branch", proj.revisionExpr) | 473 | assert proj.revisionExpr != "stable-branch" |
| 479 | if proj.name == "root-name2": | 474 | if proj.name == "root-name2": |
| 480 | # Check root proj revision not removed. | 475 | # Check root proj revision not removed. |
| 481 | self.assertEqual("refs/heads/main", proj.revisionExpr) | 476 | assert proj.revisionExpr == "refs/heads/main" |
| 482 | if proj.name == "stable-name1": | 477 | if proj.name == "stable-name1": |
| 483 | # Check stable proj has inherited revision include node. | 478 | # Check stable proj has inherited revision include node. |
| 484 | self.assertEqual("stable-branch", proj.revisionExpr) | 479 | assert proj.revisionExpr == "stable-branch" |
| 485 | if proj.name == "stable-name2": | 480 | if proj.name == "stable-name2": |
| 486 | # Check stable proj revision can override include node. | 481 | # Check stable proj revision can override include node. |
| 487 | self.assertEqual("stable-branch2", proj.revisionExpr) | 482 | assert proj.revisionExpr == "stable-branch2" |
| 488 | if proj.name == "man1-name1": | 483 | if proj.name == "man1-name1": |
| 489 | self.assertEqual("stable-branch", proj.revisionExpr) | 484 | assert proj.revisionExpr == "stable-branch" |
| 490 | if proj.name == "man1-name2": | 485 | if proj.name == "man1-name2": |
| 491 | self.assertEqual("stable-branch3", proj.revisionExpr) | 486 | assert proj.revisionExpr == "stable-branch3" |
| 492 | if proj.name == "man2-name1": | 487 | if proj.name == "man2-name1": |
| 493 | self.assertEqual("stable-branch2", proj.revisionExpr) | 488 | assert proj.revisionExpr == "stable-branch2" |
| 494 | if proj.name == "man2-name2": | 489 | if proj.name == "man2-name2": |
| 495 | self.assertEqual("stable-branch3", proj.revisionExpr) | 490 | assert proj.revisionExpr == "stable-branch3" |
| 496 | 491 | ||
| 497 | def test_group_levels(self): | 492 | def test_group_levels(self, repo_client: RepoClient) -> None: |
| 498 | root_m = self.manifest_dir / "root.xml" | 493 | """Check handling of nested include groups.""" |
| 494 | root_m = repo_client.manifest_dir / "root.xml" | ||
| 499 | root_m.write_text( | 495 | root_m.write_text( |
| 500 | """ | 496 | """ |
| 501 | <manifest> | 497 | <manifest> |
| @@ -507,7 +503,7 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 507 | </manifest> | 503 | </manifest> |
| 508 | """ | 504 | """ |
| 509 | ) | 505 | ) |
| 510 | (self.manifest_dir / "level1.xml").write_text( | 506 | (repo_client.manifest_dir / "level1.xml").write_text( |
| 511 | """ | 507 | """ |
| 512 | <manifest> | 508 | <manifest> |
| 513 | <include name="level2.xml" groups="level2-group" /> | 509 | <include name="level2.xml" groups="level2-group" /> |
| @@ -515,33 +511,38 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 515 | </manifest> | 511 | </manifest> |
| 516 | """ | 512 | """ |
| 517 | ) | 513 | ) |
| 518 | (self.manifest_dir / "level2.xml").write_text( | 514 | (repo_client.manifest_dir / "level2.xml").write_text( |
| 519 | """ | 515 | """ |
| 520 | <manifest> | 516 | <manifest> |
| 521 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> | 517 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> |
| 522 | </manifest> | 518 | </manifest> |
| 523 | """ | 519 | """ |
| 524 | ) | 520 | ) |
| 525 | include_m = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 521 | include_m = manifest_xml.XmlManifest( |
| 522 | str(repo_client.repodir), str(root_m) | ||
| 523 | ) | ||
| 526 | for proj in include_m.projects: | 524 | for proj in include_m.projects: |
| 527 | if proj.name == "root-name1": | 525 | if proj.name == "root-name1": |
| 528 | # Check include group not set on root level proj. | 526 | # Check include group not set on root level proj. |
| 529 | self.assertNotIn("level1-group", proj.groups) | 527 | assert "level1-group" not in proj.groups |
| 530 | if proj.name == "root-name2": | 528 | if proj.name == "root-name2": |
| 531 | # Check root proj group not removed. | 529 | # Check root proj group not removed. |
| 532 | self.assertIn("r2g1", proj.groups) | 530 | assert "r2g1" in proj.groups |
| 533 | if proj.name == "level1-name1": | 531 | if proj.name == "level1-name1": |
| 534 | # Check level1 proj has inherited group level 1. | 532 | # Check level1 proj has inherited group level 1. |
| 535 | self.assertIn("level1-group", proj.groups) | 533 | assert "level1-group" in proj.groups |
| 536 | if proj.name == "level2-name1": | 534 | if proj.name == "level2-name1": |
| 537 | # Check level2 proj has inherited group levels 1 and 2. | 535 | # Check level2 proj has inherited group levels 1 and 2. |
| 538 | self.assertIn("level1-group", proj.groups) | 536 | assert "level1-group" in proj.groups |
| 539 | self.assertIn("level2-group", proj.groups) | 537 | assert "level2-group" in proj.groups |
| 540 | # Check level2 proj group not removed. | 538 | # Check level2 proj group not removed. |
| 541 | self.assertIn("l2g1", proj.groups) | 539 | assert "l2g1" in proj.groups |
| 542 | 540 | ||
| 543 | def test_group_levels_with_extend_project(self): | 541 | def test_group_levels_with_extend_project( |
| 544 | root_m = self.manifest_dir / "root.xml" | 542 | self, repo_client: RepoClient |
| 543 | ) -> None: | ||
| 544 | """Check inheritance of groups via extend-project.""" | ||
| 545 | root_m = repo_client.manifest_dir / "root.xml" | ||
| 545 | root_m.write_text( | 546 | root_m.write_text( |
| 546 | """ | 547 | """ |
| 547 | <manifest> | 548 | <manifest> |
| @@ -552,32 +553,36 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 552 | </manifest> | 553 | </manifest> |
| 553 | """ | 554 | """ |
| 554 | ) | 555 | ) |
| 555 | (self.manifest_dir / "man1.xml").write_text( | 556 | (repo_client.manifest_dir / "man1.xml").write_text( |
| 556 | """ | 557 | """ |
| 557 | <manifest> | 558 | <manifest> |
| 558 | <project name="project1" path="project1" /> | 559 | <project name="project1" path="project1" /> |
| 559 | </manifest> | 560 | </manifest> |
| 560 | """ | 561 | """ |
| 561 | ) | 562 | ) |
| 562 | (self.manifest_dir / "man2.xml").write_text( | 563 | (repo_client.manifest_dir / "man2.xml").write_text( |
| 563 | """ | 564 | """ |
| 564 | <manifest> | 565 | <manifest> |
| 565 | <extend-project name="project1" groups="eg1" /> | 566 | <extend-project name="project1" groups="eg1" /> |
| 566 | </manifest> | 567 | </manifest> |
| 567 | """ | 568 | """ |
| 568 | ) | 569 | ) |
| 569 | include_m = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 570 | include_m = manifest_xml.XmlManifest( |
| 571 | str(repo_client.repodir), str(root_m) | ||
| 572 | ) | ||
| 570 | proj = include_m.projects[0] | 573 | proj = include_m.projects[0] |
| 571 | # Check project has inherited group via project element. | 574 | # Check project has inherited group via project element. |
| 572 | self.assertIn("top-group1", proj.groups) | 575 | assert "top-group1" in proj.groups |
| 573 | # Check project has inherited group via extend-project element. | 576 | # Check project has inherited group via extend-project element. |
| 574 | self.assertIn("top-group2", proj.groups) | 577 | assert "top-group2" in proj.groups |
| 575 | # Check project has set group via extend-project element. | 578 | # Check project has set group via extend-project element. |
| 576 | self.assertIn("eg1", proj.groups) | 579 | assert "eg1" in proj.groups |
| 577 | 580 | ||
| 578 | def test_extend_project_does_not_inherit_local_groups(self): | 581 | def test_extend_project_does_not_inherit_local_groups( |
| 582 | self, repo_client: RepoClient | ||
| 583 | ) -> None: | ||
| 579 | """Check that extend-project does not inherit local groups.""" | 584 | """Check that extend-project does not inherit local groups.""" |
| 580 | root_m = self.manifest_dir / "root.xml" | 585 | root_m = repo_client.manifest_dir / "root.xml" |
| 581 | root_m.write_text( | 586 | root_m.write_text( |
| 582 | """ | 587 | """ |
| 583 | <manifest> | 588 | <manifest> |
| @@ -588,26 +593,28 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 588 | </manifest> | 593 | </manifest> |
| 589 | """ | 594 | """ |
| 590 | ) | 595 | ) |
| 591 | (self.manifest_dir / "man1.xml").write_text( | 596 | (repo_client.manifest_dir / "man1.xml").write_text( |
| 592 | """ | 597 | """ |
| 593 | <manifest> | 598 | <manifest> |
| 594 | <extend-project name="project1" groups="g3" /> | 599 | <extend-project name="project1" groups="g3" /> |
| 595 | </manifest> | 600 | </manifest> |
| 596 | """ | 601 | """ |
| 597 | ) | 602 | ) |
| 598 | include_m = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 603 | include_m = manifest_xml.XmlManifest( |
| 604 | str(repo_client.repodir), str(root_m) | ||
| 605 | ) | ||
| 599 | proj = include_m.projects[0] | 606 | proj = include_m.projects[0] |
| 600 | 607 | ||
| 601 | self.assertIn("g1", proj.groups) | 608 | assert "g1" in proj.groups |
| 602 | self.assertNotIn("local:g2", proj.groups) | 609 | assert "local:g2" not in proj.groups |
| 603 | self.assertIn("g3", proj.groups) | 610 | assert "g3" in proj.groups |
| 604 | 611 | ||
| 605 | def test_allow_bad_name_from_user(self): | 612 | def test_allow_bad_name_from_user(self, repo_client: RepoClient) -> None: |
| 606 | """Check handling of bad name attribute from the user's input.""" | 613 | """Check handling of bad name attribute from the user's input.""" |
| 607 | 614 | ||
| 608 | def parse(name): | 615 | def parse(name: str) -> None: |
| 609 | name = self.encodeXmlAttr(name) | 616 | name = repo_client.encode_xml_attr(name) |
| 610 | manifest = self.getXmlManifest( | 617 | manifest = repo_client.get_xml_manifest( |
| 611 | f""" | 618 | f""" |
| 612 | <manifest> | 619 | <manifest> |
| 613 | <remote name="default-remote" fetch="http://localhost" /> | 620 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -620,26 +627,26 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 620 | manifest.ToXml() | 627 | manifest.ToXml() |
| 621 | 628 | ||
| 622 | # Setup target of the include. | 629 | # Setup target of the include. |
| 623 | target = self.tempdir / "target.xml" | 630 | target = repo_client.topdir / "target.xml" |
| 624 | target.write_text("<manifest></manifest>") | 631 | target.write_text("<manifest></manifest>") |
| 625 | 632 | ||
| 626 | # Include with absolute path. | 633 | # Include with absolute path. |
| 627 | parse(os.path.abspath(target)) | 634 | parse(str(target.absolute())) |
| 628 | 635 | ||
| 629 | # Include with relative path. | 636 | # Include with relative path. |
| 630 | parse(os.path.relpath(target, self.manifest_dir)) | 637 | parse(os.path.relpath(str(target), str(repo_client.manifest_dir))) |
| 631 | 638 | ||
| 632 | def test_bad_name_checks(self): | 639 | def test_bad_name_checks(self, repo_client: RepoClient) -> None: |
| 633 | """Check handling of bad name attribute.""" | 640 | """Check handling of bad name attribute.""" |
| 634 | 641 | ||
| 635 | def parse(name): | 642 | def parse(name: str) -> None: |
| 636 | name = self.encodeXmlAttr(name) | 643 | name = repo_client.encode_xml_attr(name) |
| 637 | # Setup target of the include. | 644 | # Setup target of the include. |
| 638 | (self.manifest_dir / "target.xml").write_text( | 645 | (repo_client.manifest_dir / "target.xml").write_text( |
| 639 | f'<manifest><include name="{name}"/></manifest>' | 646 | f'<manifest><include name="{name}"/></manifest>' |
| 640 | ) | 647 | ) |
| 641 | 648 | ||
| 642 | manifest = self.getXmlManifest( | 649 | manifest = repo_client.get_xml_manifest( |
| 643 | """ | 650 | """ |
| 644 | <manifest> | 651 | <manifest> |
| 645 | <remote name="default-remote" fetch="http://localhost" /> | 652 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -652,23 +659,23 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 652 | manifest.ToXml() | 659 | manifest.ToXml() |
| 653 | 660 | ||
| 654 | # Handle empty name explicitly because a different codepath rejects it. | 661 | # Handle empty name explicitly because a different codepath rejects it. |
| 655 | with self.assertRaises(error.ManifestParseError): | 662 | with pytest.raises(error.ManifestParseError): |
| 656 | parse("") | 663 | parse("") |
| 657 | 664 | ||
| 658 | for path in INVALID_FS_PATHS: | 665 | for path in INVALID_FS_PATHS: |
| 659 | if not path: | 666 | if not path: |
| 660 | continue | 667 | continue |
| 661 | 668 | ||
| 662 | with self.assertRaises(error.ManifestInvalidPathError): | 669 | with pytest.raises(error.ManifestInvalidPathError): |
| 663 | parse(path) | 670 | parse(path) |
| 664 | 671 | ||
| 665 | 672 | ||
| 666 | class ProjectElementTests(ManifestParseTestCase): | 673 | class TestProjectElement: |
| 667 | """Tests for <project>.""" | 674 | """Tests for <project>.""" |
| 668 | 675 | ||
| 669 | def test_group(self): | 676 | def test_group(self, repo_client: RepoClient) -> None: |
| 670 | """Check project group settings.""" | 677 | """Check project group settings.""" |
| 671 | manifest = self.getXmlManifest( | 678 | manifest = repo_client.get_xml_manifest( |
| 672 | """ | 679 | """ |
| 673 | <manifest> | 680 | <manifest> |
| 674 | <remote name="test-remote" fetch="http://localhost" /> | 681 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -678,28 +685,33 @@ class ProjectElementTests(ManifestParseTestCase): | |||
| 678 | </manifest> | 685 | </manifest> |
| 679 | """ | 686 | """ |
| 680 | ) | 687 | ) |
| 681 | self.assertEqual(len(manifest.projects), 2) | 688 | assert len(manifest.projects) == 2 |
| 682 | # Ordering isn't guaranteed. | 689 | # Ordering isn't guaranteed. |
| 683 | result = { | 690 | result = { |
| 684 | manifest.projects[0].name: manifest.projects[0].groups, | 691 | manifest.projects[0].name: manifest.projects[0].groups, |
| 685 | manifest.projects[1].name: manifest.projects[1].groups, | 692 | manifest.projects[1].name: manifest.projects[1].groups, |
| 686 | } | 693 | } |
| 687 | self.assertEqual( | 694 | assert result["test-name"] == { |
| 688 | result["test-name"], {"name:test-name", "all", "path:test-path"} | 695 | "name:test-name", |
| 689 | ) | 696 | "all", |
| 690 | self.assertEqual( | 697 | "path:test-path", |
| 691 | result["extras"], | 698 | } |
| 692 | {"g1", "g2", "name:extras", "all", "path:path"}, | 699 | assert result["extras"] == { |
| 693 | ) | 700 | "g1", |
| 701 | "g2", | ||
| 702 | "name:extras", | ||
| 703 | "all", | ||
| 704 | "path:path", | ||
| 705 | } | ||
| 694 | groupstr = "default,platform-" + platform.system().lower() | 706 | groupstr = "default,platform-" + platform.system().lower() |
| 695 | self.assertEqual(groupstr, manifest.GetManifestGroupsStr()) | 707 | assert manifest.GetManifestGroupsStr() == groupstr |
| 696 | groupstr = "g1,g2,g1" | 708 | groupstr = "g1,g2,g1" |
| 697 | manifest.manifestProject.config.SetString("manifest.groups", groupstr) | 709 | manifest.manifestProject.config.SetString("manifest.groups", groupstr) |
| 698 | self.assertEqual(groupstr, manifest.GetManifestGroupsStr()) | 710 | assert manifest.GetManifestGroupsStr() == groupstr |
| 699 | 711 | ||
| 700 | def test_set_revision_id(self): | 712 | def test_set_revision_id(self, repo_client: RepoClient) -> None: |
| 701 | """Check setting of project's revisionId.""" | 713 | """Check setting of project's revisionId.""" |
| 702 | manifest = self.getXmlManifest( | 714 | manifest = repo_client.get_xml_manifest( |
| 703 | """ | 715 | """ |
| 704 | <manifest> | 716 | <manifest> |
| 705 | <remote name="default-remote" fetch="http://localhost" /> | 717 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -708,25 +720,25 @@ class ProjectElementTests(ManifestParseTestCase): | |||
| 708 | </manifest> | 720 | </manifest> |
| 709 | """ | 721 | """ |
| 710 | ) | 722 | ) |
| 711 | self.assertEqual(len(manifest.projects), 1) | 723 | assert len(manifest.projects) == 1 |
| 712 | project = manifest.projects[0] | 724 | project = manifest.projects[0] |
| 713 | project.SetRevisionId("ABCDEF") | 725 | project.SetRevisionId("ABCDEF") |
| 714 | self.assertEqual( | 726 | assert ( |
| 715 | sort_attributes(manifest.ToXml().toxml()), | 727 | sort_attributes(manifest.ToXml().toxml()) |
| 716 | '<?xml version="1.0" ?><manifest>' | 728 | == '<?xml version="1.0" ?><manifest>' |
| 717 | '<remote fetch="http://localhost" name="default-remote"/>' | 729 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 718 | '<default remote="default-remote" revision="refs/heads/main"/>' | 730 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 719 | '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>' # noqa: E501 | 731 | '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>' # noqa: E501 |
| 720 | "</manifest>", | 732 | "</manifest>" |
| 721 | ) | 733 | ) |
| 722 | 734 | ||
| 723 | def test_trailing_slash(self): | 735 | def test_trailing_slash(self, repo_client: RepoClient) -> None: |
| 724 | """Check handling of trailing slashes in attributes.""" | 736 | """Check handling of trailing slashes in attributes.""" |
| 725 | 737 | ||
| 726 | def parse(name, path): | 738 | def parse(name: str, path: str) -> manifest_xml.XmlManifest: |
| 727 | name = self.encodeXmlAttr(name) | 739 | name = repo_client.encode_xml_attr(name) |
| 728 | path = self.encodeXmlAttr(path) | 740 | path = repo_client.encode_xml_attr(path) |
| 729 | return self.getXmlManifest( | 741 | return repo_client.get_xml_manifest( |
| 730 | f""" | 742 | f""" |
| 731 | <manifest> | 743 | <manifest> |
| 732 | <remote name="default-remote" fetch="http://localhost" /> | 744 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -737,48 +749,36 @@ class ProjectElementTests(ManifestParseTestCase): | |||
| 737 | ) | 749 | ) |
| 738 | 750 | ||
| 739 | manifest = parse("a/path/", "foo") | 751 | manifest = parse("a/path/", "foo") |
| 740 | self.assertEqual( | 752 | assert os.path.normpath(manifest.projects[0].gitdir) == os.path.join( |
| 741 | os.path.normpath(manifest.projects[0].gitdir), | 753 | str(repo_client.topdir), ".repo", "projects", "foo.git" |
| 742 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), | ||
| 743 | ) | 754 | ) |
| 744 | self.assertEqual( | 755 | assert os.path.normpath(manifest.projects[0].objdir) == os.path.join( |
| 745 | os.path.normpath(manifest.projects[0].objdir), | 756 | str(repo_client.topdir), ".repo", "project-objects", "a", "path.git" |
| 746 | os.path.join( | ||
| 747 | self.tempdir, ".repo", "project-objects", "a", "path.git" | ||
| 748 | ), | ||
| 749 | ) | 757 | ) |
| 750 | 758 | ||
| 751 | manifest = parse("a/path", "foo/") | 759 | manifest = parse("a/path", "foo/") |
| 752 | self.assertEqual( | 760 | assert os.path.normpath(manifest.projects[0].gitdir) == os.path.join( |
| 753 | os.path.normpath(manifest.projects[0].gitdir), | 761 | str(repo_client.topdir), ".repo", "projects", "foo.git" |
| 754 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), | ||
| 755 | ) | 762 | ) |
| 756 | self.assertEqual( | 763 | assert os.path.normpath(manifest.projects[0].objdir) == os.path.join( |
| 757 | os.path.normpath(manifest.projects[0].objdir), | 764 | str(repo_client.topdir), ".repo", "project-objects", "a", "path.git" |
| 758 | os.path.join( | ||
| 759 | self.tempdir, ".repo", "project-objects", "a", "path.git" | ||
| 760 | ), | ||
| 761 | ) | 765 | ) |
| 762 | 766 | ||
| 763 | manifest = parse("a/path", "foo//////") | 767 | manifest = parse("a/path", "foo//////") |
| 764 | self.assertEqual( | 768 | assert os.path.normpath(manifest.projects[0].gitdir) == os.path.join( |
| 765 | os.path.normpath(manifest.projects[0].gitdir), | 769 | str(repo_client.topdir), ".repo", "projects", "foo.git" |
| 766 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), | ||
| 767 | ) | 770 | ) |
| 768 | self.assertEqual( | 771 | assert os.path.normpath(manifest.projects[0].objdir) == os.path.join( |
| 769 | os.path.normpath(manifest.projects[0].objdir), | 772 | str(repo_client.topdir), ".repo", "project-objects", "a", "path.git" |
| 770 | os.path.join( | ||
| 771 | self.tempdir, ".repo", "project-objects", "a", "path.git" | ||
| 772 | ), | ||
| 773 | ) | 773 | ) |
| 774 | 774 | ||
| 775 | def test_toplevel_path(self): | 775 | def test_toplevel_path(self, repo_client: RepoClient) -> None: |
| 776 | """Check handling of path=. specially.""" | 776 | """Check handling of path=. specially.""" |
| 777 | 777 | ||
| 778 | def parse(name, path): | 778 | def parse(name: str, path: str) -> manifest_xml.XmlManifest: |
| 779 | name = self.encodeXmlAttr(name) | 779 | name = repo_client.encode_xml_attr(name) |
| 780 | path = self.encodeXmlAttr(path) | 780 | path = repo_client.encode_xml_attr(path) |
| 781 | return self.getXmlManifest( | 781 | return repo_client.get_xml_manifest( |
| 782 | f""" | 782 | f""" |
| 783 | <manifest> | 783 | <manifest> |
| 784 | <remote name="default-remote" fetch="http://localhost" /> | 784 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -790,18 +790,19 @@ class ProjectElementTests(ManifestParseTestCase): | |||
| 790 | 790 | ||
| 791 | for path in (".", "./", ".//", ".///"): | 791 | for path in (".", "./", ".//", ".///"): |
| 792 | manifest = parse("server/path", path) | 792 | manifest = parse("server/path", path) |
| 793 | self.assertEqual( | 793 | assert os.path.normpath( |
| 794 | os.path.normpath(manifest.projects[0].gitdir), | 794 | manifest.projects[0].gitdir |
| 795 | os.path.join(self.tempdir, ".repo", "projects", "..git"), | 795 | ) == os.path.join( |
| 796 | str(repo_client.topdir), ".repo", "projects", "..git" | ||
| 796 | ) | 797 | ) |
| 797 | 798 | ||
| 798 | def test_bad_path_name_checks(self): | 799 | def test_bad_path_name_checks(self, repo_client: RepoClient) -> None: |
| 799 | """Check handling of bad path & name attributes.""" | 800 | """Check handling of bad path & name attributes.""" |
| 800 | 801 | ||
| 801 | def parse(name, path): | 802 | def parse(name: str, path: str) -> None: |
| 802 | name = self.encodeXmlAttr(name) | 803 | name = repo_client.encode_xml_attr(name) |
| 803 | path = self.encodeXmlAttr(path) | 804 | path = repo_client.encode_xml_attr(path) |
| 804 | manifest = self.getXmlManifest( | 805 | manifest = repo_client.get_xml_manifest( |
| 805 | f""" | 806 | f""" |
| 806 | <manifest> | 807 | <manifest> |
| 807 | <remote name="default-remote" fetch="http://localhost" /> | 808 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -818,28 +819,28 @@ class ProjectElementTests(ManifestParseTestCase): | |||
| 818 | 819 | ||
| 819 | # Handle empty name explicitly because a different codepath rejects it. | 820 | # Handle empty name explicitly because a different codepath rejects it. |
| 820 | # Empty path is OK because it defaults to the name field. | 821 | # Empty path is OK because it defaults to the name field. |
| 821 | with self.assertRaises(error.ManifestParseError): | 822 | with pytest.raises(error.ManifestParseError): |
| 822 | parse("", "ok") | 823 | parse("", "ok") |
| 823 | 824 | ||
| 824 | for path in INVALID_FS_PATHS: | 825 | for path in INVALID_FS_PATHS: |
| 825 | if not path or path.endswith("/") or path.endswith(os.path.sep): | 826 | if not path or path.endswith("/") or path.endswith(os.path.sep): |
| 826 | continue | 827 | continue |
| 827 | 828 | ||
| 828 | with self.assertRaises(error.ManifestInvalidPathError): | 829 | with pytest.raises(error.ManifestInvalidPathError): |
| 829 | parse(path, "ok") | 830 | parse(path, "ok") |
| 830 | 831 | ||
| 831 | # We have a dedicated test for path=".". | 832 | # We have a dedicated test for path=".". |
| 832 | if path not in {"."}: | 833 | if path not in {"."}: |
| 833 | with self.assertRaises(error.ManifestInvalidPathError): | 834 | with pytest.raises(error.ManifestInvalidPathError): |
| 834 | parse("ok", path) | 835 | parse("ok", path) |
| 835 | 836 | ||
| 836 | 837 | ||
| 837 | class SuperProjectElementTests(ManifestParseTestCase): | 838 | class TestSuperProjectElement: |
| 838 | """Tests for <superproject>.""" | 839 | """Tests for <superproject>.""" |
| 839 | 840 | ||
| 840 | def test_superproject(self): | 841 | def test_superproject(self, repo_client: RepoClient) -> None: |
| 841 | """Check superproject settings.""" | 842 | """Check superproject settings.""" |
| 842 | manifest = self.getXmlManifest( | 843 | manifest = repo_client.get_xml_manifest( |
| 843 | """ | 844 | """ |
| 844 | <manifest> | 845 | <manifest> |
| 845 | <remote name="test-remote" fetch="http://localhost" /> | 846 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -848,25 +849,24 @@ class SuperProjectElementTests(ManifestParseTestCase): | |||
| 848 | </manifest> | 849 | </manifest> |
| 849 | """ | 850 | """ |
| 850 | ) | 851 | ) |
| 851 | self.assertEqual(manifest.superproject.name, "superproject") | 852 | assert manifest.superproject.name == "superproject" |
| 852 | self.assertEqual(manifest.superproject.remote.name, "test-remote") | 853 | assert manifest.superproject.remote.name == "test-remote" |
| 853 | self.assertEqual( | 854 | assert ( |
| 854 | manifest.superproject.remote.url, "http://localhost/superproject" | 855 | manifest.superproject.remote.url == "http://localhost/superproject" |
| 855 | ) | 856 | ) |
| 856 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") | 857 | assert manifest.superproject.revision == "refs/heads/main" |
| 857 | self.assertEqual( | 858 | assert ( |
| 858 | sort_attributes(manifest.ToXml().toxml()), | 859 | sort_attributes(manifest.ToXml().toxml()) |
| 859 | '<?xml version="1.0" ?><manifest>' | 860 | == '<?xml version="1.0" ?><manifest>' |
| 860 | '<remote fetch="http://localhost" name="test-remote"/>' | 861 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 861 | '<default remote="test-remote" revision="refs/heads/main"/>' | 862 | '<default remote="test-remote" revision="refs/heads/main"/>' |
| 862 | '<superproject name="superproject"/>' | 863 | '<superproject name="superproject"/>' |
| 863 | "</manifest>", | 864 | "</manifest>" |
| 864 | ) | 865 | ) |
| 865 | 866 | ||
| 866 | def test_superproject_revision(self): | 867 | def test_superproject_revision(self, repo_client: RepoClient) -> None: |
| 867 | """Check superproject settings with a different revision attribute""" | 868 | """Check superproject settings with a different revision attribute""" |
| 868 | self.maxDiff = None | 869 | manifest = repo_client.get_xml_manifest( |
| 869 | manifest = self.getXmlManifest( | ||
| 870 | """ | 870 | """ |
| 871 | <manifest> | 871 | <manifest> |
| 872 | <remote name="test-remote" fetch="http://localhost" /> | 872 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -875,25 +875,26 @@ class SuperProjectElementTests(ManifestParseTestCase): | |||
| 875 | </manifest> | 875 | </manifest> |
| 876 | """ | 876 | """ |
| 877 | ) | 877 | ) |
| 878 | self.assertEqual(manifest.superproject.name, "superproject") | 878 | assert manifest.superproject.name == "superproject" |
| 879 | self.assertEqual(manifest.superproject.remote.name, "test-remote") | 879 | assert manifest.superproject.remote.name == "test-remote" |
| 880 | self.assertEqual( | 880 | assert ( |
| 881 | manifest.superproject.remote.url, "http://localhost/superproject" | 881 | manifest.superproject.remote.url == "http://localhost/superproject" |
| 882 | ) | 882 | ) |
| 883 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") | 883 | assert manifest.superproject.revision == "refs/heads/stable" |
| 884 | self.assertEqual( | 884 | assert ( |
| 885 | sort_attributes(manifest.ToXml().toxml()), | 885 | sort_attributes(manifest.ToXml().toxml()) |
| 886 | '<?xml version="1.0" ?><manifest>' | 886 | == '<?xml version="1.0" ?><manifest>' |
| 887 | '<remote fetch="http://localhost" name="test-remote"/>' | 887 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 888 | '<default remote="test-remote" revision="refs/heads/main"/>' | 888 | '<default remote="test-remote" revision="refs/heads/main"/>' |
| 889 | '<superproject name="superproject" revision="refs/heads/stable"/>' | 889 | '<superproject name="superproject" revision="refs/heads/stable"/>' |
| 890 | "</manifest>", | 890 | "</manifest>" |
| 891 | ) | 891 | ) |
| 892 | 892 | ||
| 893 | def test_superproject_revision_default_negative(self): | 893 | def test_superproject_revision_default_negative( |
| 894 | self, repo_client: RepoClient | ||
| 895 | ) -> None: | ||
| 894 | """Check superproject settings with a same revision attribute""" | 896 | """Check superproject settings with a same revision attribute""" |
| 895 | self.maxDiff = None | 897 | manifest = repo_client.get_xml_manifest( |
| 896 | manifest = self.getXmlManifest( | ||
| 897 | """ | 898 | """ |
| 898 | <manifest> | 899 | <manifest> |
| 899 | <remote name="test-remote" fetch="http://localhost" /> | 900 | <remote name="test-remote" fetch="http://localhost" /> |
| @@ -902,51 +903,53 @@ class SuperProjectElementTests(ManifestParseTestCase): | |||
| 902 | </manifest> | 903 | </manifest> |
| 903 | """ | 904 | """ |
| 904 | ) | 905 | ) |
| 905 | self.assertEqual(manifest.superproject.name, "superproject") | 906 | assert manifest.superproject.name == "superproject" |
| 906 | self.assertEqual(manifest.superproject.remote.name, "test-remote") | 907 | assert manifest.superproject.remote.name == "test-remote" |
| 907 | self.assertEqual( | 908 | assert ( |
| 908 | manifest.superproject.remote.url, "http://localhost/superproject" | 909 | manifest.superproject.remote.url == "http://localhost/superproject" |
| 909 | ) | 910 | ) |
| 910 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") | 911 | assert manifest.superproject.revision == "refs/heads/stable" |
| 911 | self.assertEqual( | 912 | assert ( |
| 912 | sort_attributes(manifest.ToXml().toxml()), | 913 | sort_attributes(manifest.ToXml().toxml()) |
| 913 | '<?xml version="1.0" ?><manifest>' | 914 | == '<?xml version="1.0" ?><manifest>' |
| 914 | '<remote fetch="http://localhost" name="test-remote"/>' | 915 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 915 | '<default remote="test-remote" revision="refs/heads/stable"/>' | 916 | '<default remote="test-remote" revision="refs/heads/stable"/>' |
| 916 | '<superproject name="superproject"/>' | 917 | '<superproject name="superproject"/>' |
| 917 | "</manifest>", | 918 | "</manifest>" |
| 918 | ) | 919 | ) |
| 919 | 920 | ||
| 920 | def test_superproject_revision_remote(self): | 921 | def test_superproject_revision_remote( |
| 922 | self, repo_client: RepoClient | ||
| 923 | ) -> None: | ||
| 921 | """Check superproject settings with a same revision attribute""" | 924 | """Check superproject settings with a same revision attribute""" |
| 922 | self.maxDiff = None | 925 | manifest = repo_client.get_xml_manifest( |
| 923 | manifest = self.getXmlManifest( | ||
| 924 | """ | 926 | """ |
| 925 | <manifest> | 927 | <manifest> |
| 926 | <remote name="test-remote" fetch="http://localhost" revision="refs/heads/main" /> | 928 | <remote name="test-remote" fetch="http://localhost" |
| 929 | revision="refs/heads/main" /> | ||
| 927 | <default remote="test-remote" /> | 930 | <default remote="test-remote" /> |
| 928 | <superproject name="superproject" revision="refs/heads/stable" /> | 931 | <superproject name="superproject" revision="refs/heads/stable" /> |
| 929 | </manifest> | 932 | </manifest> |
| 930 | """ # noqa: E501 | 933 | """ |
| 931 | ) | 934 | ) |
| 932 | self.assertEqual(manifest.superproject.name, "superproject") | 935 | assert manifest.superproject.name == "superproject" |
| 933 | self.assertEqual(manifest.superproject.remote.name, "test-remote") | 936 | assert manifest.superproject.remote.name == "test-remote" |
| 934 | self.assertEqual( | 937 | assert ( |
| 935 | manifest.superproject.remote.url, "http://localhost/superproject" | 938 | manifest.superproject.remote.url == "http://localhost/superproject" |
| 936 | ) | 939 | ) |
| 937 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") | 940 | assert manifest.superproject.revision == "refs/heads/stable" |
| 938 | self.assertEqual( | 941 | assert ( |
| 939 | sort_attributes(manifest.ToXml().toxml()), | 942 | sort_attributes(manifest.ToXml().toxml()) |
| 940 | '<?xml version="1.0" ?><manifest>' | 943 | == '<?xml version="1.0" ?><manifest>' |
| 941 | '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>' # noqa: E501 | 944 | '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>' # noqa: E501 |
| 942 | '<default remote="test-remote"/>' | 945 | '<default remote="test-remote"/>' |
| 943 | '<superproject name="superproject" revision="refs/heads/stable"/>' | 946 | '<superproject name="superproject" revision="refs/heads/stable"/>' |
| 944 | "</manifest>", | 947 | "</manifest>" |
| 945 | ) | 948 | ) |
| 946 | 949 | ||
| 947 | def test_remote(self): | 950 | def test_remote(self, repo_client: RepoClient) -> None: |
| 948 | """Check superproject settings with a remote.""" | 951 | """Check superproject settings with a remote.""" |
| 949 | manifest = self.getXmlManifest( | 952 | manifest = repo_client.get_xml_manifest( |
| 950 | """ | 953 | """ |
| 951 | <manifest> | 954 | <manifest> |
| 952 | <remote name="default-remote" fetch="http://localhost" /> | 955 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -956,28 +959,26 @@ class SuperProjectElementTests(ManifestParseTestCase): | |||
| 956 | </manifest> | 959 | </manifest> |
| 957 | """ | 960 | """ |
| 958 | ) | 961 | ) |
| 959 | self.assertEqual(manifest.superproject.name, "platform/superproject") | 962 | assert manifest.superproject.name == "platform/superproject" |
| 960 | self.assertEqual( | 963 | assert manifest.superproject.remote.name == "superproject-remote" |
| 961 | manifest.superproject.remote.name, "superproject-remote" | 964 | assert ( |
| 965 | manifest.superproject.remote.url | ||
| 966 | == "http://localhost/platform/superproject" | ||
| 962 | ) | 967 | ) |
| 963 | self.assertEqual( | 968 | assert manifest.superproject.revision == "refs/heads/main" |
| 964 | manifest.superproject.remote.url, | 969 | assert ( |
| 965 | "http://localhost/platform/superproject", | 970 | sort_attributes(manifest.ToXml().toxml()) |
| 966 | ) | 971 | == '<?xml version="1.0" ?><manifest>' |
| 967 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") | ||
| 968 | self.assertEqual( | ||
| 969 | sort_attributes(manifest.ToXml().toxml()), | ||
| 970 | '<?xml version="1.0" ?><manifest>' | ||
| 971 | '<remote fetch="http://localhost" name="default-remote"/>' | 972 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 972 | '<remote fetch="http://localhost" name="superproject-remote"/>' | 973 | '<remote fetch="http://localhost" name="superproject-remote"/>' |
| 973 | '<default remote="default-remote" revision="refs/heads/main"/>' | 974 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 974 | '<superproject name="platform/superproject" remote="superproject-remote"/>' # noqa: E501 | 975 | '<superproject name="platform/superproject" remote="superproject-remote"/>' # noqa: E501 |
| 975 | "</manifest>", | 976 | "</manifest>" |
| 976 | ) | 977 | ) |
| 977 | 978 | ||
| 978 | def test_defalut_remote(self): | 979 | def test_default_remote(self, repo_client: RepoClient) -> None: |
| 979 | """Check superproject settings with a default remote.""" | 980 | """Check superproject settings with a default remote.""" |
| 980 | manifest = self.getXmlManifest( | 981 | manifest = repo_client.get_xml_manifest( |
| 981 | """ | 982 | """ |
| 982 | <manifest> | 983 | <manifest> |
| 983 | <remote name="default-remote" fetch="http://localhost" /> | 984 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -986,62 +987,61 @@ class SuperProjectElementTests(ManifestParseTestCase): | |||
| 986 | </manifest> | 987 | </manifest> |
| 987 | """ | 988 | """ |
| 988 | ) | 989 | ) |
| 989 | self.assertEqual(manifest.superproject.name, "superproject") | 990 | assert manifest.superproject.name == "superproject" |
| 990 | self.assertEqual(manifest.superproject.remote.name, "default-remote") | 991 | assert manifest.superproject.remote.name == "default-remote" |
| 991 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") | 992 | assert manifest.superproject.revision == "refs/heads/main" |
| 992 | self.assertEqual( | 993 | assert ( |
| 993 | sort_attributes(manifest.ToXml().toxml()), | 994 | sort_attributes(manifest.ToXml().toxml()) |
| 994 | '<?xml version="1.0" ?><manifest>' | 995 | == '<?xml version="1.0" ?><manifest>' |
| 995 | '<remote fetch="http://localhost" name="default-remote"/>' | 996 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 996 | '<default remote="default-remote" revision="refs/heads/main"/>' | 997 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 997 | '<superproject name="superproject"/>' | 998 | '<superproject name="superproject"/>' |
| 998 | "</manifest>", | 999 | "</manifest>" |
| 999 | ) | 1000 | ) |
| 1000 | 1001 | ||
| 1001 | 1002 | ||
| 1002 | class ContactinfoElementTests(ManifestParseTestCase): | 1003 | class TestContactinfoElement: |
| 1003 | """Tests for <contactinfo>.""" | 1004 | """Tests for <contactinfo>.""" |
| 1004 | 1005 | ||
| 1005 | def test_contactinfo(self): | 1006 | def test_contactinfo(self, repo_client: RepoClient) -> None: |
| 1006 | """Check contactinfo settings.""" | 1007 | """Check contactinfo settings.""" |
| 1007 | bugurl = "http://localhost/contactinfo" | 1008 | bugurl = "http://localhost/contactinfo" |
| 1008 | manifest = self.getXmlManifest( | 1009 | manifest = repo_client.get_xml_manifest( |
| 1009 | f""" | 1010 | f""" |
| 1010 | <manifest> | 1011 | <manifest> |
| 1011 | <contactinfo bugurl="{bugurl}"/> | 1012 | <contactinfo bugurl="{bugurl}"/> |
| 1012 | </manifest> | 1013 | </manifest> |
| 1013 | """ | 1014 | """ |
| 1014 | ) | 1015 | ) |
| 1015 | self.assertEqual(manifest.contactinfo.bugurl, bugurl) | 1016 | assert manifest.contactinfo.bugurl == bugurl |
| 1016 | self.assertEqual( | 1017 | assert ( |
| 1017 | manifest.ToXml().toxml(), | 1018 | manifest.ToXml().toxml() == '<?xml version="1.0" ?><manifest>' |
| 1018 | '<?xml version="1.0" ?><manifest>' | ||
| 1019 | f'<contactinfo bugurl="{bugurl}"/>' | 1019 | f'<contactinfo bugurl="{bugurl}"/>' |
| 1020 | "</manifest>", | 1020 | "</manifest>" |
| 1021 | ) | 1021 | ) |
| 1022 | 1022 | ||
| 1023 | 1023 | ||
| 1024 | class DefaultElementTests(ManifestParseTestCase): | 1024 | class TestDefaultElement: |
| 1025 | """Tests for <default>.""" | 1025 | """Tests for <default>.""" |
| 1026 | 1026 | ||
| 1027 | def test_default(self): | 1027 | def test_default(self) -> None: |
| 1028 | """Check default settings.""" | 1028 | """Check default settings.""" |
| 1029 | a = manifest_xml._Default() | 1029 | a = manifest_xml._Default() |
| 1030 | a.revisionExpr = "foo" | 1030 | a.revisionExpr = "foo" |
| 1031 | a.remote = manifest_xml._XmlRemote(name="remote") | 1031 | a.remote = manifest_xml._XmlRemote(name="remote") |
| 1032 | b = manifest_xml._Default() | 1032 | b = manifest_xml._Default() |
| 1033 | b.revisionExpr = "bar" | 1033 | b.revisionExpr = "bar" |
| 1034 | self.assertEqual(a, a) | 1034 | assert a == a |
| 1035 | self.assertNotEqual(a, b) | 1035 | assert a != b |
| 1036 | self.assertNotEqual(b, a.remote) | 1036 | assert b != a.remote |
| 1037 | self.assertNotEqual(a, 123) | 1037 | assert a != 123 |
| 1038 | self.assertNotEqual(a, None) | 1038 | assert a is not None |
| 1039 | 1039 | ||
| 1040 | 1040 | ||
| 1041 | class RemoteElementTests(ManifestParseTestCase): | 1041 | class TestRemoteElement: |
| 1042 | """Tests for <remote>.""" | 1042 | """Tests for <remote>.""" |
| 1043 | 1043 | ||
| 1044 | def test_remote(self): | 1044 | def test_remote(self) -> None: |
| 1045 | """Check remote settings.""" | 1045 | """Check remote settings.""" |
| 1046 | a = manifest_xml._XmlRemote(name="foo") | 1046 | a = manifest_xml._XmlRemote(name="foo") |
| 1047 | a.AddAnnotation("key1", "value1", "true") | 1047 | a.AddAnnotation("key1", "value1", "true") |
| @@ -1051,20 +1051,21 @@ class RemoteElementTests(ManifestParseTestCase): | |||
| 1051 | c.AddAnnotation("key1", "value2", "true") | 1051 | c.AddAnnotation("key1", "value2", "true") |
| 1052 | d = manifest_xml._XmlRemote(name="foo") | 1052 | d = manifest_xml._XmlRemote(name="foo") |
| 1053 | d.AddAnnotation("key1", "value1", "false") | 1053 | d.AddAnnotation("key1", "value1", "false") |
| 1054 | self.assertEqual(a, a) | 1054 | assert a == a |
| 1055 | self.assertNotEqual(a, b) | 1055 | assert a != b |
| 1056 | self.assertNotEqual(a, c) | 1056 | assert a != c |
| 1057 | self.assertNotEqual(a, d) | 1057 | assert a != d |
| 1058 | self.assertNotEqual(a, manifest_xml._Default()) | 1058 | assert a != manifest_xml._Default() |
| 1059 | self.assertNotEqual(a, 123) | 1059 | assert a != 123 |
| 1060 | self.assertNotEqual(a, None) | 1060 | assert a is not None |
| 1061 | 1061 | ||
| 1062 | 1062 | ||
| 1063 | class RemoveProjectElementTests(ManifestParseTestCase): | 1063 | class TestRemoveProjectElement: |
| 1064 | """Tests for <remove-project>.""" | 1064 | """Tests for <remove-project>.""" |
| 1065 | 1065 | ||
| 1066 | def test_remove_one_project(self): | 1066 | def test_remove_one_project(self, repo_client: RepoClient) -> None: |
| 1067 | manifest = self.getXmlManifest( | 1067 | """Check removal of a single project.""" |
| 1068 | manifest = repo_client.get_xml_manifest( | ||
| 1068 | """ | 1069 | """ |
| 1069 | <manifest> | 1070 | <manifest> |
| 1070 | <remote name="default-remote" fetch="http://localhost" /> | 1071 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1074,10 +1075,13 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1074 | </manifest> | 1075 | </manifest> |
| 1075 | """ | 1076 | """ |
| 1076 | ) | 1077 | ) |
| 1077 | self.assertEqual(manifest.projects, []) | 1078 | assert manifest.projects == [] |
| 1078 | 1079 | ||
| 1079 | def test_remove_one_project_one_remains(self): | 1080 | def test_remove_one_project_one_remains( |
| 1080 | manifest = self.getXmlManifest( | 1081 | self, repo_client: RepoClient |
| 1082 | ) -> None: | ||
| 1083 | """Check removal of one project while another remains.""" | ||
| 1084 | manifest = repo_client.get_xml_manifest( | ||
| 1081 | """ | 1085 | """ |
| 1082 | <manifest> | 1086 | <manifest> |
| 1083 | <remote name="default-remote" fetch="http://localhost" /> | 1087 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1089,24 +1093,30 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1089 | """ | 1093 | """ |
| 1090 | ) | 1094 | ) |
| 1091 | 1095 | ||
| 1092 | self.assertEqual(len(manifest.projects), 1) | 1096 | assert len(manifest.projects) == 1 |
| 1093 | self.assertEqual(manifest.projects[0].name, "yourproject") | 1097 | assert manifest.projects[0].name == "yourproject" |
| 1094 | 1098 | ||
| 1095 | def test_remove_one_project_doesnt_exist(self): | 1099 | def test_remove_one_project_doesnt_exist( |
| 1096 | with self.assertRaises(manifest_xml.ManifestParseError): | 1100 | self, repo_client: RepoClient |
| 1097 | manifest = self.getXmlManifest( | 1101 | ) -> None: |
| 1098 | """ | 1102 | """Check removal of non-existent project fails.""" |
| 1103 | manifest = repo_client.get_xml_manifest( | ||
| 1104 | """ | ||
| 1099 | <manifest> | 1105 | <manifest> |
| 1100 | <remote name="default-remote" fetch="http://localhost" /> | 1106 | <remote name="default-remote" fetch="http://localhost" /> |
| 1101 | <default remote="default-remote" revision="refs/heads/main" /> | 1107 | <default remote="default-remote" revision="refs/heads/main" /> |
| 1102 | <remove-project name="myproject" /> | 1108 | <remove-project name="myproject" /> |
| 1103 | </manifest> | 1109 | </manifest> |
| 1104 | """ | 1110 | """ |
| 1105 | ) | 1111 | ) |
| 1112 | with pytest.raises(error.ManifestParseError): | ||
| 1106 | manifest.projects | 1113 | manifest.projects |
| 1107 | 1114 | ||
| 1108 | def test_remove_one_optional_project_doesnt_exist(self): | 1115 | def test_remove_one_optional_project_doesnt_exist( |
| 1109 | manifest = self.getXmlManifest( | 1116 | self, repo_client: RepoClient |
| 1117 | ) -> None: | ||
| 1118 | """Check optional removal of non-existent project passes.""" | ||
| 1119 | manifest = repo_client.get_xml_manifest( | ||
| 1110 | """ | 1120 | """ |
| 1111 | <manifest> | 1121 | <manifest> |
| 1112 | <remote name="default-remote" fetch="http://localhost" /> | 1122 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1115,10 +1125,11 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1115 | </manifest> | 1125 | </manifest> |
| 1116 | """ | 1126 | """ |
| 1117 | ) | 1127 | ) |
| 1118 | self.assertEqual(manifest.projects, []) | 1128 | assert manifest.projects == [] |
| 1119 | 1129 | ||
| 1120 | def test_remove_using_path_attrib(self): | 1130 | def test_remove_using_path_attrib(self, repo_client: RepoClient) -> None: |
| 1121 | manifest = self.getXmlManifest( | 1131 | """Check removal using name and path attributes.""" |
| 1132 | manifest = repo_client.get_xml_manifest( | ||
| 1122 | """ | 1133 | """ |
| 1123 | <manifest> | 1134 | <manifest> |
| 1124 | <remote name="default-remote" fetch="http://localhost" /> | 1135 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1145,18 +1156,21 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1145 | for proj in manifest.projects: | 1156 | for proj in manifest.projects: |
| 1146 | if proj.name == "project1": | 1157 | if proj.name == "project1": |
| 1147 | found_proj1_path1 = True | 1158 | found_proj1_path1 = True |
| 1148 | self.assertEqual(proj.relpath, "tests/path1") | 1159 | assert proj.relpath == "tests/path1" |
| 1149 | if proj.name == "project2": | 1160 | if proj.name == "project2": |
| 1150 | found_proj2 = True | 1161 | found_proj2 = True |
| 1151 | self.assertNotEqual(proj.name, "project3") | 1162 | assert proj.name != "project3" |
| 1152 | self.assertNotEqual(proj.name, "project4") | 1163 | assert proj.name != "project4" |
| 1153 | self.assertNotEqual(proj.name, "project5") | 1164 | assert proj.name != "project5" |
| 1154 | self.assertNotEqual(proj.name, "project6") | 1165 | assert proj.name != "project6" |
| 1155 | self.assertTrue(found_proj1_path1) | 1166 | assert found_proj1_path1 |
| 1156 | self.assertTrue(found_proj2) | 1167 | assert found_proj2 |
| 1157 | 1168 | ||
| 1158 | def test_base_revision_checks_on_patching(self): | 1169 | def test_base_revision_checks_on_patching( |
| 1159 | manifest_fail_wrong_tag = self.getXmlManifest( | 1170 | self, repo_client: RepoClient |
| 1171 | ) -> None: | ||
| 1172 | """Check base-rev validation during patching.""" | ||
| 1173 | manifest_fail_wrong_tag = repo_client.get_xml_manifest( | ||
| 1160 | """ | 1174 | """ |
| 1161 | <manifest> | 1175 | <manifest> |
| 1162 | <remote name="default-remote" fetch="http://localhost" /> | 1176 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1166,10 +1180,10 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1166 | </manifest> | 1180 | </manifest> |
| 1167 | """ | 1181 | """ |
| 1168 | ) | 1182 | ) |
| 1169 | with self.assertRaises(error.ManifestParseError): | 1183 | with pytest.raises(error.ManifestParseError): |
| 1170 | manifest_fail_wrong_tag.ToXml() | 1184 | manifest_fail_wrong_tag.ToXml() |
| 1171 | 1185 | ||
| 1172 | manifest_fail_remove = self.getXmlManifest( | 1186 | manifest_fail_remove = repo_client.get_xml_manifest( |
| 1173 | """ | 1187 | """ |
| 1174 | <manifest> | 1188 | <manifest> |
| 1175 | <remote name="default-remote" fetch="http://localhost" /> | 1189 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1179,10 +1193,10 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1179 | </manifest> | 1193 | </manifest> |
| 1180 | """ | 1194 | """ |
| 1181 | ) | 1195 | ) |
| 1182 | with self.assertRaises(error.ManifestParseError): | 1196 | with pytest.raises(error.ManifestParseError): |
| 1183 | manifest_fail_remove.ToXml() | 1197 | manifest_fail_remove.ToXml() |
| 1184 | 1198 | ||
| 1185 | manifest_fail_extend = self.getXmlManifest( | 1199 | manifest_fail_extend = repo_client.get_xml_manifest( |
| 1186 | """ | 1200 | """ |
| 1187 | <manifest> | 1201 | <manifest> |
| 1188 | <remote name="default-remote" fetch="http://localhost" /> | 1202 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1192,10 +1206,10 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1192 | </manifest> | 1206 | </manifest> |
| 1193 | """ | 1207 | """ |
| 1194 | ) | 1208 | ) |
| 1195 | with self.assertRaises(error.ManifestParseError): | 1209 | with pytest.raises(error.ManifestParseError): |
| 1196 | manifest_fail_extend.ToXml() | 1210 | manifest_fail_extend.ToXml() |
| 1197 | 1211 | ||
| 1198 | manifest_fail_unknown = self.getXmlManifest( | 1212 | manifest_fail_unknown = repo_client.get_xml_manifest( |
| 1199 | """ | 1213 | """ |
| 1200 | <manifest> | 1214 | <manifest> |
| 1201 | <remote name="default-remote" fetch="http://localhost" /> | 1215 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1205,10 +1219,10 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1205 | </manifest> | 1219 | </manifest> |
| 1206 | """ | 1220 | """ |
| 1207 | ) | 1221 | ) |
| 1208 | with self.assertRaises(error.ManifestParseError): | 1222 | with pytest.raises(error.ManifestParseError): |
| 1209 | manifest_fail_unknown.ToXml() | 1223 | manifest_fail_unknown.ToXml() |
| 1210 | 1224 | ||
| 1211 | manifest_ok = self.getXmlManifest( | 1225 | manifest_ok = repo_client.get_xml_manifest( |
| 1212 | """ | 1226 | """ |
| 1213 | <manifest> | 1227 | <manifest> |
| 1214 | <remote name="default-remote" fetch="http://localhost" /> | 1228 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1234,18 +1248,21 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 1234 | found_proj2 = True | 1248 | found_proj2 = True |
| 1235 | if proj.name == "project3": | 1249 | if proj.name == "project3": |
| 1236 | found_proj3 = True | 1250 | found_proj3 = True |
| 1237 | self.assertNotEqual(proj.name, "project1") | 1251 | assert proj.name != "project1" |
| 1238 | self.assertNotEqual(proj.name, "project4") | 1252 | assert proj.name != "project4" |
| 1239 | self.assertTrue(found_proj2) | 1253 | assert found_proj2 |
| 1240 | self.assertTrue(found_proj3) | 1254 | assert found_proj3 |
| 1241 | self.assertTrue(len(manifest_ok.projects) == 2) | 1255 | assert len(manifest_ok.projects) == 2 |
| 1242 | 1256 | ||
| 1243 | 1257 | ||
| 1244 | class ExtendProjectElementTests(ManifestParseTestCase): | 1258 | class TestExtendProjectElement: |
| 1245 | """Tests for <extend-project>.""" | 1259 | """Tests for <extend-project>.""" |
| 1246 | 1260 | ||
| 1247 | def test_extend_project_dest_path_single_match(self): | 1261 | def test_extend_project_dest_path_single_match( |
| 1248 | manifest = self.getXmlManifest( | 1262 | self, repo_client: RepoClient |
| 1263 | ) -> None: | ||
| 1264 | """Check dest-path when single match exists.""" | ||
| 1265 | manifest = repo_client.get_xml_manifest( | ||
| 1249 | """ | 1266 | """ |
| 1250 | <manifest> | 1267 | <manifest> |
| 1251 | <remote name="default-remote" fetch="http://localhost" /> | 1268 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1255,13 +1272,15 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1255 | </manifest> | 1272 | </manifest> |
| 1256 | """ | 1273 | """ |
| 1257 | ) | 1274 | ) |
| 1258 | self.assertEqual(len(manifest.projects), 1) | 1275 | assert len(manifest.projects) == 1 |
| 1259 | self.assertEqual(manifest.projects[0].relpath, "bar") | 1276 | assert manifest.projects[0].relpath == "bar" |
| 1260 | 1277 | ||
| 1261 | def test_extend_project_dest_path_multi_match(self): | 1278 | def test_extend_project_dest_path_multi_match( |
| 1262 | with self.assertRaises(manifest_xml.ManifestParseError): | 1279 | self, repo_client: RepoClient |
| 1263 | manifest = self.getXmlManifest( | 1280 | ) -> None: |
| 1264 | """ | 1281 | """Check dest-path when multiple matches exist fails.""" |
| 1282 | manifest = repo_client.get_xml_manifest( | ||
| 1283 | """ | ||
| 1265 | <manifest> | 1284 | <manifest> |
| 1266 | <remote name="default-remote" fetch="http://localhost" /> | 1285 | <remote name="default-remote" fetch="http://localhost" /> |
| 1267 | <default remote="default-remote" revision="refs/heads/main" /> | 1286 | <default remote="default-remote" revision="refs/heads/main" /> |
| @@ -1270,11 +1289,15 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1270 | <extend-project name="myproject" dest-path="bar" /> | 1289 | <extend-project name="myproject" dest-path="bar" /> |
| 1271 | </manifest> | 1290 | </manifest> |
| 1272 | """ | 1291 | """ |
| 1273 | ) | 1292 | ) |
| 1293 | with pytest.raises(error.ManifestParseError): | ||
| 1274 | manifest.projects | 1294 | manifest.projects |
| 1275 | 1295 | ||
| 1276 | def test_extend_project_dest_path_multi_match_path_specified(self): | 1296 | def test_extend_project_dest_path_multi_match_path_specified( |
| 1277 | manifest = self.getXmlManifest( | 1297 | self, repo_client: RepoClient |
| 1298 | ) -> None: | ||
| 1299 | """Check dest-path when path is specified for multi-match.""" | ||
| 1300 | manifest = repo_client.get_xml_manifest( | ||
| 1278 | """ | 1301 | """ |
| 1279 | <manifest> | 1302 | <manifest> |
| 1280 | <remote name="default-remote" fetch="http://localhost" /> | 1303 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1285,29 +1308,32 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1285 | </manifest> | 1308 | </manifest> |
| 1286 | """ | 1309 | """ |
| 1287 | ) | 1310 | ) |
| 1288 | self.assertEqual(len(manifest.projects), 2) | 1311 | assert len(manifest.projects) == 2 |
| 1289 | if manifest.projects[0].relpath == "y": | 1312 | if manifest.projects[0].relpath == "y": |
| 1290 | self.assertEqual(manifest.projects[1].relpath, "bar") | 1313 | assert manifest.projects[1].relpath == "bar" |
| 1291 | else: | 1314 | else: |
| 1292 | self.assertEqual(manifest.projects[0].relpath, "bar") | 1315 | assert manifest.projects[0].relpath == "bar" |
| 1293 | self.assertEqual(manifest.projects[1].relpath, "y") | 1316 | assert manifest.projects[1].relpath == "y" |
| 1294 | 1317 | ||
| 1295 | def test_extend_project_dest_branch(self): | 1318 | def test_extend_project_dest_branch(self, repo_client: RepoClient) -> None: |
| 1296 | manifest = self.getXmlManifest( | 1319 | """Check dest-branch update via extend-project.""" |
| 1320 | manifest = repo_client.get_xml_manifest( | ||
| 1297 | """ | 1321 | """ |
| 1298 | <manifest> | 1322 | <manifest> |
| 1299 | <remote name="default-remote" fetch="http://localhost" /> | 1323 | <remote name="default-remote" fetch="http://localhost" /> |
| 1300 | <default remote="default-remote" revision="refs/heads/main" dest-branch="foo" /> | 1324 | <default remote="default-remote" revision="refs/heads/main" |
| 1325 | dest-branch="foo" /> | ||
| 1301 | <project name="myproject" /> | 1326 | <project name="myproject" /> |
| 1302 | <extend-project name="myproject" dest-branch="bar" /> | 1327 | <extend-project name="myproject" dest-branch="bar" /> |
| 1303 | </manifest> | 1328 | </manifest> |
| 1304 | """ # noqa: E501 | 1329 | """ |
| 1305 | ) | 1330 | ) |
| 1306 | self.assertEqual(len(manifest.projects), 1) | 1331 | assert len(manifest.projects) == 1 |
| 1307 | self.assertEqual(manifest.projects[0].dest_branch, "bar") | 1332 | assert manifest.projects[0].dest_branch == "bar" |
| 1308 | 1333 | ||
| 1309 | def test_extend_project_upstream(self): | 1334 | def test_extend_project_upstream(self, repo_client: RepoClient) -> None: |
| 1310 | manifest = self.getXmlManifest( | 1335 | """Check upstream update via extend-project.""" |
| 1336 | manifest = repo_client.get_xml_manifest( | ||
| 1311 | """ | 1337 | """ |
| 1312 | <manifest> | 1338 | <manifest> |
| 1313 | <remote name="default-remote" fetch="http://localhost" /> | 1339 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1317,11 +1343,12 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1317 | </manifest> | 1343 | </manifest> |
| 1318 | """ | 1344 | """ |
| 1319 | ) | 1345 | ) |
| 1320 | self.assertEqual(len(manifest.projects), 1) | 1346 | assert len(manifest.projects) == 1 |
| 1321 | self.assertEqual(manifest.projects[0].upstream, "bar") | 1347 | assert manifest.projects[0].upstream == "bar" |
| 1322 | 1348 | ||
| 1323 | def test_extend_project_copyfiles(self): | 1349 | def test_extend_project_copyfiles(self, repo_client: RepoClient) -> None: |
| 1324 | manifest = self.getXmlManifest( | 1350 | """Check copyfile addition via extend-project.""" |
| 1351 | manifest = repo_client.get_xml_manifest( | ||
| 1325 | """ | 1352 | """ |
| 1326 | <manifest> | 1353 | <manifest> |
| 1327 | <remote name="default-remote" fetch="http://localhost" /> | 1354 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1333,21 +1360,24 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1333 | </manifest> | 1360 | </manifest> |
| 1334 | """ | 1361 | """ |
| 1335 | ) | 1362 | ) |
| 1336 | self.assertEqual(list(manifest.projects[0].copyfiles)[0].src, "foo") | 1363 | assert list(manifest.projects[0].copyfiles)[0].src == "foo" |
| 1337 | self.assertEqual(list(manifest.projects[0].copyfiles)[0].dest, "bar") | 1364 | assert list(manifest.projects[0].copyfiles)[0].dest == "bar" |
| 1338 | self.assertEqual( | 1365 | assert ( |
| 1339 | sort_attributes(manifest.ToXml().toxml()), | 1366 | sort_attributes(manifest.ToXml().toxml()) |
| 1340 | '<?xml version="1.0" ?><manifest>' | 1367 | == '<?xml version="1.0" ?><manifest>' |
| 1341 | '<remote fetch="http://localhost" name="default-remote"/>' | 1368 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 1342 | '<default remote="default-remote" revision="refs/heads/main"/>' | 1369 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 1343 | '<project name="myproject">' | 1370 | '<project name="myproject">' |
| 1344 | '<copyfile dest="bar" src="foo"/>' | 1371 | '<copyfile dest="bar" src="foo"/>' |
| 1345 | "</project>" | 1372 | "</project>" |
| 1346 | "</manifest>", | 1373 | "</manifest>" |
| 1347 | ) | 1374 | ) |
| 1348 | 1375 | ||
| 1349 | def test_extend_project_duplicate_copyfiles(self): | 1376 | def test_extend_project_duplicate_copyfiles( |
| 1350 | root_m = self.manifest_dir / "root.xml" | 1377 | self, repo_client: RepoClient |
| 1378 | ) -> None: | ||
| 1379 | """Check duplicate copyfile handling in includes.""" | ||
| 1380 | root_m = repo_client.manifest_dir / "root.xml" | ||
| 1351 | root_m.write_text( | 1381 | root_m.write_text( |
| 1352 | """ | 1382 | """ |
| 1353 | <manifest> | 1383 | <manifest> |
| @@ -1359,21 +1389,21 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1359 | </manifest> | 1389 | </manifest> |
| 1360 | """ | 1390 | """ |
| 1361 | ) | 1391 | ) |
| 1362 | (self.manifest_dir / "man1.xml").write_text( | 1392 | (repo_client.manifest_dir / "man1.xml").write_text( |
| 1363 | """ | 1393 | """ |
| 1364 | <manifest> | 1394 | <manifest> |
| 1365 | <include name="common.xml" /> | 1395 | <include name="common.xml" /> |
| 1366 | </manifest> | 1396 | </manifest> |
| 1367 | """ | 1397 | """ |
| 1368 | ) | 1398 | ) |
| 1369 | (self.manifest_dir / "man2.xml").write_text( | 1399 | (repo_client.manifest_dir / "man2.xml").write_text( |
| 1370 | """ | 1400 | """ |
| 1371 | <manifest> | 1401 | <manifest> |
| 1372 | <include name="common.xml" /> | 1402 | <include name="common.xml" /> |
| 1373 | </manifest> | 1403 | </manifest> |
| 1374 | """ | 1404 | """ |
| 1375 | ) | 1405 | ) |
| 1376 | (self.manifest_dir / "common.xml").write_text( | 1406 | (repo_client.manifest_dir / "common.xml").write_text( |
| 1377 | """ | 1407 | """ |
| 1378 | <manifest> | 1408 | <manifest> |
| 1379 | <extend-project name="myproject"> | 1409 | <extend-project name="myproject"> |
| @@ -1382,13 +1412,16 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1382 | </manifest> | 1412 | </manifest> |
| 1383 | """ | 1413 | """ |
| 1384 | ) | 1414 | ) |
| 1385 | manifest = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 1415 | manifest = manifest_xml.XmlManifest( |
| 1386 | self.assertEqual(len(manifest.projects[0].copyfiles), 1) | 1416 | str(repo_client.repodir), str(root_m) |
| 1387 | self.assertEqual(list(manifest.projects[0].copyfiles)[0].src, "foo") | 1417 | ) |
| 1388 | self.assertEqual(list(manifest.projects[0].copyfiles)[0].dest, "bar") | 1418 | assert len(manifest.projects[0].copyfiles) == 1 |
| 1419 | assert list(manifest.projects[0].copyfiles)[0].src == "foo" | ||
| 1420 | assert list(manifest.projects[0].copyfiles)[0].dest == "bar" | ||
| 1389 | 1421 | ||
| 1390 | def test_extend_project_linkfiles(self): | 1422 | def test_extend_project_linkfiles(self, repo_client: RepoClient) -> None: |
| 1391 | manifest = self.getXmlManifest( | 1423 | """Check linkfile addition via extend-project.""" |
| 1424 | manifest = repo_client.get_xml_manifest( | ||
| 1392 | """ | 1425 | """ |
| 1393 | <manifest> | 1426 | <manifest> |
| 1394 | <remote name="default-remote" fetch="http://localhost" /> | 1427 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1400,21 +1433,24 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1400 | </manifest> | 1433 | </manifest> |
| 1401 | """ | 1434 | """ |
| 1402 | ) | 1435 | ) |
| 1403 | self.assertEqual(list(manifest.projects[0].linkfiles)[0].src, "foo") | 1436 | assert list(manifest.projects[0].linkfiles)[0].src == "foo" |
| 1404 | self.assertEqual(list(manifest.projects[0].linkfiles)[0].dest, "bar") | 1437 | assert list(manifest.projects[0].linkfiles)[0].dest == "bar" |
| 1405 | self.assertEqual( | 1438 | assert ( |
| 1406 | sort_attributes(manifest.ToXml().toxml()), | 1439 | sort_attributes(manifest.ToXml().toxml()) |
| 1407 | '<?xml version="1.0" ?><manifest>' | 1440 | == '<?xml version="1.0" ?><manifest>' |
| 1408 | '<remote fetch="http://localhost" name="default-remote"/>' | 1441 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 1409 | '<default remote="default-remote" revision="refs/heads/main"/>' | 1442 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 1410 | '<project name="myproject">' | 1443 | '<project name="myproject">' |
| 1411 | '<linkfile dest="bar" src="foo"/>' | 1444 | '<linkfile dest="bar" src="foo"/>' |
| 1412 | "</project>" | 1445 | "</project>" |
| 1413 | "</manifest>", | 1446 | "</manifest>" |
| 1414 | ) | 1447 | ) |
| 1415 | 1448 | ||
| 1416 | def test_extend_project_duplicate_linkfiles(self): | 1449 | def test_extend_project_duplicate_linkfiles( |
| 1417 | root_m = self.manifest_dir / "root.xml" | 1450 | self, repo_client: RepoClient |
| 1451 | ) -> None: | ||
| 1452 | """Check duplicate linkfile handling in includes.""" | ||
| 1453 | root_m = repo_client.manifest_dir / "root.xml" | ||
| 1418 | root_m.write_text( | 1454 | root_m.write_text( |
| 1419 | """ | 1455 | """ |
| 1420 | <manifest> | 1456 | <manifest> |
| @@ -1426,21 +1462,21 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1426 | </manifest> | 1462 | </manifest> |
| 1427 | """ | 1463 | """ |
| 1428 | ) | 1464 | ) |
| 1429 | (self.manifest_dir / "man1.xml").write_text( | 1465 | (repo_client.manifest_dir / "man1.xml").write_text( |
| 1430 | """ | 1466 | """ |
| 1431 | <manifest> | 1467 | <manifest> |
| 1432 | <include name="common.xml" /> | 1468 | <include name="common.xml" /> |
| 1433 | </manifest> | 1469 | </manifest> |
| 1434 | """ | 1470 | """ |
| 1435 | ) | 1471 | ) |
| 1436 | (self.manifest_dir / "man2.xml").write_text( | 1472 | (repo_client.manifest_dir / "man2.xml").write_text( |
| 1437 | """ | 1473 | """ |
| 1438 | <manifest> | 1474 | <manifest> |
| 1439 | <include name="common.xml" /> | 1475 | <include name="common.xml" /> |
| 1440 | </manifest> | 1476 | </manifest> |
| 1441 | """ | 1477 | """ |
| 1442 | ) | 1478 | ) |
| 1443 | (self.manifest_dir / "common.xml").write_text( | 1479 | (repo_client.manifest_dir / "common.xml").write_text( |
| 1444 | """ | 1480 | """ |
| 1445 | <manifest> | 1481 | <manifest> |
| 1446 | <extend-project name="myproject"> | 1482 | <extend-project name="myproject"> |
| @@ -1449,13 +1485,16 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1449 | </manifest> | 1485 | </manifest> |
| 1450 | """ | 1486 | """ |
| 1451 | ) | 1487 | ) |
| 1452 | manifest = manifest_xml.XmlManifest(str(self.repodir), str(root_m)) | 1488 | manifest = manifest_xml.XmlManifest( |
| 1453 | self.assertEqual(len(manifest.projects[0].linkfiles), 1) | 1489 | str(repo_client.repodir), str(root_m) |
| 1454 | self.assertEqual(list(manifest.projects[0].linkfiles)[0].src, "foo") | 1490 | ) |
| 1455 | self.assertEqual(list(manifest.projects[0].linkfiles)[0].dest, "bar") | 1491 | assert len(manifest.projects[0].linkfiles) == 1 |
| 1492 | assert list(manifest.projects[0].linkfiles)[0].src == "foo" | ||
| 1493 | assert list(manifest.projects[0].linkfiles)[0].dest == "bar" | ||
| 1456 | 1494 | ||
| 1457 | def test_extend_project_annotations(self): | 1495 | def test_extend_project_annotations(self, repo_client: RepoClient) -> None: |
| 1458 | manifest = self.getXmlManifest( | 1496 | """Check annotation addition via extend-project.""" |
| 1497 | manifest = repo_client.get_xml_manifest( | ||
| 1459 | """ | 1498 | """ |
| 1460 | <manifest> | 1499 | <manifest> |
| 1461 | <remote name="default-remote" fetch="http://localhost" /> | 1500 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1467,21 +1506,24 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1467 | </manifest> | 1506 | </manifest> |
| 1468 | """ | 1507 | """ |
| 1469 | ) | 1508 | ) |
| 1470 | self.assertEqual(manifest.projects[0].annotations[0].name, "foo") | 1509 | assert manifest.projects[0].annotations[0].name == "foo" |
| 1471 | self.assertEqual(manifest.projects[0].annotations[0].value, "bar") | 1510 | assert manifest.projects[0].annotations[0].value == "bar" |
| 1472 | self.assertEqual( | 1511 | assert ( |
| 1473 | sort_attributes(manifest.ToXml().toxml()), | 1512 | sort_attributes(manifest.ToXml().toxml()) |
| 1474 | '<?xml version="1.0" ?><manifest>' | 1513 | == '<?xml version="1.0" ?><manifest>' |
| 1475 | '<remote fetch="http://localhost" name="default-remote"/>' | 1514 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 1476 | '<default remote="default-remote" revision="refs/heads/main"/>' | 1515 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 1477 | '<project name="myproject">' | 1516 | '<project name="myproject">' |
| 1478 | '<annotation name="foo" value="bar"/>' | 1517 | '<annotation name="foo" value="bar"/>' |
| 1479 | "</project>" | 1518 | "</project>" |
| 1480 | "</manifest>", | 1519 | "</manifest>" |
| 1481 | ) | 1520 | ) |
| 1482 | 1521 | ||
| 1483 | def test_extend_project_annotations_multiples(self): | 1522 | def test_extend_project_annotations_multiples( |
| 1484 | manifest = self.getXmlManifest( | 1523 | self, repo_client: RepoClient |
| 1524 | ) -> None: | ||
| 1525 | """Check multiple annotation additions via extend-project.""" | ||
| 1526 | manifest = repo_client.get_xml_manifest( | ||
| 1485 | """ | 1527 | """ |
| 1486 | <manifest> | 1528 | <manifest> |
| 1487 | <remote name="default-remote" fetch="http://localhost" /> | 1529 | <remote name="default-remote" fetch="http://localhost" /> |
| @@ -1497,18 +1539,17 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1497 | </manifest> | 1539 | </manifest> |
| 1498 | """ | 1540 | """ |
| 1499 | ) | 1541 | ) |
| 1500 | self.assertEqual( | 1542 | assert [ |
| 1501 | [(a.name, a.value) for a in manifest.projects[0].annotations], | 1543 | (a.name, a.value) for a in manifest.projects[0].annotations |
| 1502 | [ | 1544 | ] == [ |
| 1503 | ("foo", "bar"), | 1545 | ("foo", "bar"), |
| 1504 | ("few", "bar"), | 1546 | ("few", "bar"), |
| 1505 | ("foo", "new_bar"), | 1547 | ("foo", "new_bar"), |
| 1506 | ("new", "anno"), | 1548 | ("new", "anno"), |
| 1507 | ], | 1549 | ] |
| 1508 | ) | 1550 | assert ( |
| 1509 | self.assertEqual( | 1551 | sort_attributes(manifest.ToXml().toxml()) |
| 1510 | sort_attributes(manifest.ToXml().toxml()), | 1552 | == '<?xml version="1.0" ?><manifest>' |
| 1511 | '<?xml version="1.0" ?><manifest>' | ||
| 1512 | '<remote fetch="http://localhost" name="default-remote"/>' | 1553 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 1513 | '<default remote="default-remote" revision="refs/heads/main"/>' | 1554 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 1514 | '<project name="myproject">' | 1555 | '<project name="myproject">' |
| @@ -1517,81 +1558,78 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 1517 | '<annotation name="foo" value="new_bar"/>' | 1558 | '<annotation name="foo" value="new_bar"/>' |
| 1518 | '<annotation name="new" value="anno"/>' | 1559 | '<annotation name="new" value="anno"/>' |
| 1519 | "</project>" | 1560 | "</project>" |
| 1520 | "</manifest>", | 1561 | "</manifest>" |
| 1521 | ) | 1562 | ) |
| 1522 | 1563 | ||
| 1523 | 1564 | ||
| 1524 | class NormalizeUrlTests(ManifestParseTestCase): | 1565 | class TestNormalizeUrl: |
| 1525 | """Tests for normalize_url() in manifest_xml.py""" | 1566 | """Tests for normalize_url() in manifest_xml.py""" |
| 1526 | 1567 | ||
| 1527 | def test_has_trailing_slash(self): | 1568 | def test_has_trailing_slash(self) -> None: |
| 1569 | """Trailing slashes should be removed.""" | ||
| 1528 | url = "http://foo.com/bar/baz/" | 1570 | url = "http://foo.com/bar/baz/" |
| 1529 | self.assertEqual( | 1571 | assert manifest_xml.normalize_url(url) == "http://foo.com/bar/baz" |
| 1530 | "http://foo.com/bar/baz", manifest_xml.normalize_url(url) | ||
| 1531 | ) | ||
| 1532 | 1572 | ||
| 1533 | url = "http://foo.com/bar/" | 1573 | url = "http://foo.com/bar/" |
| 1534 | self.assertEqual("http://foo.com/bar", manifest_xml.normalize_url(url)) | 1574 | assert manifest_xml.normalize_url(url) == "http://foo.com/bar" |
| 1535 | 1575 | ||
| 1536 | def test_has_leading_slash(self): | 1576 | def test_has_leading_slash(self) -> None: |
| 1537 | """SCP-like syntax except a / comes before the : which git disallows.""" | 1577 | """SCP-like syntax except a / comes before the : which git disallows.""" |
| 1538 | url = "/git@foo.com:bar/baf" | 1578 | url = "/git@foo.com:bar/baf" |
| 1539 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1579 | assert manifest_xml.normalize_url(url) == url |
| 1540 | 1580 | ||
| 1541 | url = "gi/t@foo.com:bar/baf" | 1581 | url = "gi/t@foo.com:bar/baf" |
| 1542 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1582 | assert manifest_xml.normalize_url(url) == url |
| 1543 | 1583 | ||
| 1544 | url = "git@fo/o.com:bar/baf" | 1584 | url = "git@fo/o.com:bar/baf" |
| 1545 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1585 | assert manifest_xml.normalize_url(url) == url |
| 1546 | 1586 | ||
| 1547 | def test_has_no_scheme(self): | 1587 | def test_has_no_scheme(self) -> None: |
| 1548 | """Deal with cases where we have no scheme, but we also | 1588 | """Deal with cases where we have no scheme, but we also |
| 1549 | aren't dealing with the git SCP-like syntax | 1589 | aren't dealing with the git SCP-like syntax |
| 1550 | """ | 1590 | """ |
| 1551 | url = "foo.com/baf/bat" | 1591 | url = "foo.com/baf/bat" |
| 1552 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1592 | assert manifest_xml.normalize_url(url) == url |
| 1553 | 1593 | ||
| 1554 | url = "foo.com/baf" | 1594 | url = "foo.com/baf" |
| 1555 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1595 | assert manifest_xml.normalize_url(url) == url |
| 1556 | 1596 | ||
| 1557 | url = "git@foo.com/baf/bat" | 1597 | url = "git@foo.com/baf/bat" |
| 1558 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1598 | assert manifest_xml.normalize_url(url) == url |
| 1559 | 1599 | ||
| 1560 | url = "git@foo.com/baf" | 1600 | url = "git@foo.com/baf" |
| 1561 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1601 | assert manifest_xml.normalize_url(url) == url |
| 1562 | 1602 | ||
| 1563 | url = "/file/path/here" | 1603 | url = "/file/path/here" |
| 1564 | self.assertEqual(url, manifest_xml.normalize_url(url)) | 1604 | assert manifest_xml.normalize_url(url) == url |
| 1565 | 1605 | ||
| 1566 | def test_has_no_scheme_matches_scp_like_syntax(self): | 1606 | def test_has_no_scheme_matches_scp_like_syntax(self) -> None: |
| 1607 | """SCP-like syntax should be converted to ssh://.""" | ||
| 1567 | url = "git@foo.com:bar/baf" | 1608 | url = "git@foo.com:bar/baf" |
| 1568 | self.assertEqual( | 1609 | assert manifest_xml.normalize_url(url) == "ssh://git@foo.com/bar/baf" |
| 1569 | "ssh://git@foo.com/bar/baf", manifest_xml.normalize_url(url) | ||
| 1570 | ) | ||
| 1571 | 1610 | ||
| 1572 | url = "git@foo.com:bar/" | 1611 | url = "git@foo.com:bar/" |
| 1573 | self.assertEqual( | 1612 | assert manifest_xml.normalize_url(url) == "ssh://git@foo.com/bar" |
| 1574 | "ssh://git@foo.com/bar", manifest_xml.normalize_url(url) | ||
| 1575 | ) | ||
| 1576 | 1613 | ||
| 1577 | def test_remote_url_resolution(self): | 1614 | def test_remote_url_resolution(self) -> None: |
| 1615 | """Check resolvedFetchUrl calculation.""" | ||
| 1578 | remote = manifest_xml._XmlRemote( | 1616 | remote = manifest_xml._XmlRemote( |
| 1579 | name="foo", | 1617 | name="foo", |
| 1580 | fetch="git@github.com:org2/", | 1618 | fetch="git@github.com:org2/", |
| 1581 | manifestUrl="git@github.com:org2/custom_manifest.git", | 1619 | manifestUrl="git@github.com:org2/custom_manifest.git", |
| 1582 | ) | 1620 | ) |
| 1583 | self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl) | 1621 | assert remote.resolvedFetchUrl == "ssh://git@github.com/org2" |
| 1584 | 1622 | ||
| 1585 | remote = manifest_xml._XmlRemote( | 1623 | remote = manifest_xml._XmlRemote( |
| 1586 | name="foo", | 1624 | name="foo", |
| 1587 | fetch="ssh://git@github.com/org2/", | 1625 | fetch="ssh://git@github.com/org2/", |
| 1588 | manifestUrl="git@github.com:org2/custom_manifest.git", | 1626 | manifestUrl="git@github.com:org2/custom_manifest.git", |
| 1589 | ) | 1627 | ) |
| 1590 | self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl) | 1628 | assert remote.resolvedFetchUrl == "ssh://git@github.com/org2" |
| 1591 | 1629 | ||
| 1592 | remote = manifest_xml._XmlRemote( | 1630 | remote = manifest_xml._XmlRemote( |
| 1593 | name="foo", | 1631 | name="foo", |
| 1594 | fetch="git@github.com:org2/", | 1632 | fetch="git@github.com:org2/", |
| 1595 | manifestUrl="ssh://git@github.com/org2/custom_manifest.git", | 1633 | manifestUrl="ssh://git@github.com/org2/custom_manifest.git", |
| 1596 | ) | 1634 | ) |
| 1597 | self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl) | 1635 | assert remote.resolvedFetchUrl == "ssh://git@github.com/org2" |
diff --git a/tests/test_subcmds_upload.py b/tests/test_subcmds_upload.py index cd8889778..51c0a4cb7 100644 --- a/tests/test_subcmds_upload.py +++ b/tests/test_subcmds_upload.py | |||
| @@ -14,9 +14,10 @@ | |||
| 14 | 14 | ||
| 15 | """Unittests for the subcmds/upload.py module.""" | 15 | """Unittests for the subcmds/upload.py module.""" |
| 16 | 16 | ||
| 17 | import unittest | ||
| 18 | from unittest import mock | 17 | from unittest import mock |
| 19 | 18 | ||
| 19 | import pytest | ||
| 20 | |||
| 20 | from error import GitError | 21 | from error import GitError |
| 21 | from error import UploadError | 22 | from error import UploadError |
| 22 | from subcmds import upload | 23 | from subcmds import upload |
| @@ -26,45 +27,39 @@ class UnexpectedError(Exception): | |||
| 26 | """An exception not expected by upload command.""" | 27 | """An exception not expected by upload command.""" |
| 27 | 28 | ||
| 28 | 29 | ||
| 29 | class UploadCommand(unittest.TestCase): | 30 | # A stub people list (reviewers, cc). |
| 30 | """Check registered all_commands.""" | 31 | _STUB_PEOPLE = ([], []) |
| 31 | 32 | ||
| 32 | def setUp(self): | 33 | |
| 33 | self.cmd = upload.Upload() | 34 | @pytest.fixture |
| 34 | self.branch = mock.MagicMock() | 35 | def cmd() -> upload.Upload: |
| 35 | self.people = mock.MagicMock() | 36 | """Fixture to provide an Upload command instance with mocked methods.""" |
| 36 | self.opt, _ = self.cmd.OptionParser.parse_args([]) | 37 | cmd = upload.Upload() |
| 37 | mock.patch.object( | 38 | with mock.patch.object( |
| 38 | self.cmd, "_AppendAutoList", return_value=None | 39 | cmd, "_AppendAutoList", return_value=None |
| 39 | ).start() | 40 | ), mock.patch.object(cmd, "git_event_log"): |
| 40 | mock.patch.object(self.cmd, "git_event_log").start() | 41 | yield cmd |
| 41 | 42 | ||
| 42 | def tearDown(self): | 43 | |
| 43 | mock.patch.stopall() | 44 | def test_UploadAndReport_UploadError(cmd: upload.Upload) -> None: |
| 44 | 45 | """Check UploadExitError raised when UploadError encountered.""" | |
| 45 | def test_UploadAndReport_UploadError(self): | 46 | opt, _ = cmd.OptionParser.parse_args([]) |
| 46 | """Check UploadExitError raised when UploadError encountered.""" | 47 | with mock.patch.object(cmd, "_UploadBranch", side_effect=UploadError("")): |
| 47 | side_effect = UploadError("upload error") | 48 | with pytest.raises(upload.UploadExitError): |
| 48 | with mock.patch.object( | 49 | cmd._UploadAndReport(opt, [mock.MagicMock()], _STUB_PEOPLE) |
| 49 | self.cmd, "_UploadBranch", side_effect=side_effect | 50 | |
| 50 | ): | 51 | |
| 51 | with self.assertRaises(upload.UploadExitError): | 52 | def test_UploadAndReport_GitError(cmd: upload.Upload) -> None: |
| 52 | self.cmd._UploadAndReport(self.opt, [self.branch], self.people) | 53 | """Check UploadExitError raised when GitError encountered.""" |
| 53 | 54 | opt, _ = cmd.OptionParser.parse_args([]) | |
| 54 | def test_UploadAndReport_GitError(self): | 55 | with mock.patch.object(cmd, "_UploadBranch", side_effect=GitError("")): |
| 55 | """Check UploadExitError raised when GitError encountered.""" | 56 | with pytest.raises(upload.UploadExitError): |
| 56 | side_effect = GitError("some git error") | 57 | cmd._UploadAndReport(opt, [mock.MagicMock()], _STUB_PEOPLE) |
| 57 | with mock.patch.object( | 58 | |
| 58 | self.cmd, "_UploadBranch", side_effect=side_effect | 59 | |
| 59 | ): | 60 | def test_UploadAndReport_UnhandledError(cmd: upload.Upload) -> None: |
| 60 | with self.assertRaises(upload.UploadExitError): | 61 | """Check UnexpectedError passed through.""" |
| 61 | self.cmd._UploadAndReport(self.opt, [self.branch], self.people) | 62 | opt, _ = cmd.OptionParser.parse_args([]) |
| 62 | 63 | with mock.patch.object(cmd, "_UploadBranch", side_effect=UnexpectedError): | |
| 63 | def test_UploadAndReport_UnhandledError(self): | 64 | with pytest.raises(UnexpectedError): |
| 64 | """Check UnexpectedError passed through.""" | 65 | cmd._UploadAndReport(opt, [mock.MagicMock()], _STUB_PEOPLE) |
| 65 | side_effect = UnexpectedError("some os error") | ||
| 66 | with mock.patch.object( | ||
| 67 | self.cmd, "_UploadBranch", side_effect=side_effect | ||
| 68 | ): | ||
| 69 | with self.assertRaises(type(side_effect)): | ||
| 70 | self.cmd._UploadAndReport(self.opt, [self.branch], self.people) | ||
diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 7845ee163..7a1a87289 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py | |||
| @@ -19,261 +19,298 @@ import os | |||
| 19 | import re | 19 | import re |
| 20 | import subprocess | 20 | import subprocess |
| 21 | import sys | 21 | import sys |
| 22 | import tempfile | ||
| 23 | import unittest | ||
| 24 | from unittest import mock | 22 | from unittest import mock |
| 25 | 23 | ||
| 24 | import pytest | ||
| 26 | import utils_for_test | 25 | import utils_for_test |
| 27 | 26 | ||
| 28 | import main | 27 | import main |
| 29 | import wrapper | 28 | import wrapper |
| 30 | 29 | ||
| 31 | 30 | ||
| 32 | class RepoWrapperTestCase(unittest.TestCase): | 31 | @pytest.fixture(autouse=True) |
| 33 | """TestCase for the wrapper module.""" | 32 | def reset_wrapper() -> None: |
| 33 | """Reset the wrapper module every time.""" | ||
| 34 | wrapper.Wrapper.cache_clear() | ||
| 34 | 35 | ||
| 35 | def setUp(self): | ||
| 36 | """Load the wrapper module every time.""" | ||
| 37 | wrapper.Wrapper.cache_clear() | ||
| 38 | self.wrapper = wrapper.Wrapper() | ||
| 39 | 36 | ||
| 37 | @pytest.fixture | ||
| 38 | def repo_wrapper() -> wrapper.Wrapper: | ||
| 39 | """Fixture for the wrapper module.""" | ||
| 40 | return wrapper.Wrapper() | ||
| 40 | 41 | ||
| 41 | class RepoWrapperUnitTest(RepoWrapperTestCase): | 42 | |
| 43 | class GitCheckout: | ||
| 44 | """Class to hold git checkout info for tests.""" | ||
| 45 | |||
| 46 | def __init__(self, git_dir, rev_list): | ||
| 47 | self.git_dir = git_dir | ||
| 48 | self.rev_list = rev_list | ||
| 49 | |||
| 50 | |||
| 51 | @pytest.fixture(scope="module") | ||
| 52 | def git_checkout(tmp_path_factory) -> GitCheckout: | ||
| 53 | """Fixture for tests that use a real/small git checkout. | ||
| 54 | |||
| 55 | Create a repo to operate on, but do it once per-test-run. | ||
| 56 | """ | ||
| 57 | tempdir = tmp_path_factory.mktemp("repo-rev-tests") | ||
| 58 | run_git = wrapper.Wrapper().run_git | ||
| 59 | |||
| 60 | remote = os.path.join(tempdir, "remote") | ||
| 61 | os.mkdir(remote) | ||
| 62 | |||
| 63 | utils_for_test.init_git_tree(remote) | ||
| 64 | run_git("commit", "--allow-empty", "-minit", cwd=remote) | ||
| 65 | run_git("branch", "stable", cwd=remote) | ||
| 66 | run_git("tag", "v1.0", cwd=remote) | ||
| 67 | run_git("commit", "--allow-empty", "-m2nd commit", cwd=remote) | ||
| 68 | rev_list = run_git("rev-list", "HEAD", cwd=remote).stdout.splitlines() | ||
| 69 | |||
| 70 | run_git("init", cwd=tempdir) | ||
| 71 | run_git( | ||
| 72 | "fetch", | ||
| 73 | remote, | ||
| 74 | "+refs/heads/*:refs/remotes/origin/*", | ||
| 75 | cwd=tempdir, | ||
| 76 | ) | ||
| 77 | yield GitCheckout(tempdir, rev_list) | ||
| 78 | |||
| 79 | |||
| 80 | class TestRepoWrapper: | ||
| 42 | """Tests helper functions in the repo wrapper""" | 81 | """Tests helper functions in the repo wrapper""" |
| 43 | 82 | ||
| 44 | def test_version(self): | 83 | def test_version(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 45 | """Make sure _Version works.""" | 84 | """Make sure _Version works.""" |
| 46 | with self.assertRaises(SystemExit) as e: | 85 | with pytest.raises(SystemExit) as e: |
| 47 | with mock.patch("sys.stdout", new_callable=io.StringIO) as stdout: | 86 | with mock.patch("sys.stdout", new_callable=io.StringIO) as stdout: |
| 48 | with mock.patch( | 87 | with mock.patch( |
| 49 | "sys.stderr", new_callable=io.StringIO | 88 | "sys.stderr", new_callable=io.StringIO |
| 50 | ) as stderr: | 89 | ) as stderr: |
| 51 | self.wrapper._Version() | 90 | repo_wrapper._Version() |
| 52 | self.assertEqual(0, e.exception.code) | 91 | assert e.value.code == 0 |
| 53 | self.assertEqual("", stderr.getvalue()) | 92 | assert stderr.getvalue() == "" |
| 54 | self.assertIn("repo launcher version", stdout.getvalue()) | 93 | assert "repo launcher version" in stdout.getvalue() |
| 55 | 94 | ||
| 56 | def test_python_constraints(self): | 95 | def test_python_constraints(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 57 | """The launcher should never require newer than main.py.""" | 96 | """The launcher should never require newer than main.py.""" |
| 58 | self.assertGreaterEqual( | 97 | assert ( |
| 59 | main.MIN_PYTHON_VERSION_HARD, self.wrapper.MIN_PYTHON_VERSION_HARD | 98 | main.MIN_PYTHON_VERSION_HARD >= repo_wrapper.MIN_PYTHON_VERSION_HARD |
| 60 | ) | 99 | ) |
| 61 | self.assertGreaterEqual( | 100 | assert ( |
| 62 | main.MIN_PYTHON_VERSION_SOFT, self.wrapper.MIN_PYTHON_VERSION_SOFT | 101 | main.MIN_PYTHON_VERSION_SOFT >= repo_wrapper.MIN_PYTHON_VERSION_SOFT |
| 63 | ) | 102 | ) |
| 64 | # Make sure the versions are themselves in sync. | 103 | # Make sure the versions are themselves in sync. |
| 65 | self.assertGreaterEqual( | 104 | assert ( |
| 66 | self.wrapper.MIN_PYTHON_VERSION_SOFT, | 105 | repo_wrapper.MIN_PYTHON_VERSION_SOFT |
| 67 | self.wrapper.MIN_PYTHON_VERSION_HARD, | 106 | >= repo_wrapper.MIN_PYTHON_VERSION_HARD |
| 68 | ) | 107 | ) |
| 69 | 108 | ||
| 70 | def test_init_parser(self): | 109 | def test_init_parser(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 71 | """Make sure 'init' GetParser works.""" | 110 | """Make sure 'init' GetParser works.""" |
| 72 | parser = self.wrapper.GetParser() | 111 | parser = repo_wrapper.GetParser() |
| 73 | opts, args = parser.parse_args([]) | 112 | opts, args = parser.parse_args([]) |
| 74 | self.assertEqual([], args) | 113 | assert args == [] |
| 75 | self.assertIsNone(opts.manifest_url) | 114 | assert opts.manifest_url is None |
| 76 | 115 | ||
| 77 | 116 | ||
| 78 | class SetGitTrace2ParentSid(RepoWrapperTestCase): | 117 | class TestSetGitTrace2ParentSid: |
| 79 | """Check SetGitTrace2ParentSid behavior.""" | 118 | """Check SetGitTrace2ParentSid behavior.""" |
| 80 | 119 | ||
| 81 | KEY = "GIT_TRACE2_PARENT_SID" | 120 | KEY = "GIT_TRACE2_PARENT_SID" |
| 82 | VALID_FORMAT = re.compile(r"^repo-[0-9]{8}T[0-9]{6}Z-P[0-9a-f]{8}$") | 121 | VALID_FORMAT = re.compile(r"^repo-[0-9]{8}T[0-9]{6}Z-P[0-9a-f]{8}$") |
| 83 | 122 | ||
| 84 | def test_first_set(self): | 123 | def test_first_set(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 85 | """Test env var not yet set.""" | 124 | """Test env var not yet set.""" |
| 86 | env = {} | 125 | env = {} |
| 87 | self.wrapper.SetGitTrace2ParentSid(env) | 126 | repo_wrapper.SetGitTrace2ParentSid(env) |
| 88 | self.assertIn(self.KEY, env) | 127 | assert self.KEY in env |
| 89 | value = env[self.KEY] | 128 | value = env[self.KEY] |
| 90 | self.assertRegex(value, self.VALID_FORMAT) | 129 | assert self.VALID_FORMAT.match(value) |
| 91 | 130 | ||
| 92 | def test_append(self): | 131 | def test_append(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 93 | """Test env var is appended.""" | 132 | """Test env var is appended.""" |
| 94 | env = {self.KEY: "pfx"} | 133 | env = {self.KEY: "pfx"} |
| 95 | self.wrapper.SetGitTrace2ParentSid(env) | 134 | repo_wrapper.SetGitTrace2ParentSid(env) |
| 96 | self.assertIn(self.KEY, env) | 135 | assert self.KEY in env |
| 97 | value = env[self.KEY] | 136 | value = env[self.KEY] |
| 98 | self.assertTrue(value.startswith("pfx/")) | 137 | assert value.startswith("pfx/") |
| 99 | self.assertRegex(value[4:], self.VALID_FORMAT) | 138 | assert self.VALID_FORMAT.match(value[4:]) |
| 100 | 139 | ||
| 101 | def test_global_context(self): | 140 | def test_global_context(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 102 | """Check os.environ gets updated by default.""" | 141 | """Check os.environ gets updated by default.""" |
| 103 | os.environ.pop(self.KEY, None) | 142 | os.environ.pop(self.KEY, None) |
| 104 | self.wrapper.SetGitTrace2ParentSid() | 143 | repo_wrapper.SetGitTrace2ParentSid() |
| 105 | self.assertIn(self.KEY, os.environ) | 144 | assert self.KEY in os.environ |
| 106 | value = os.environ[self.KEY] | 145 | value = os.environ[self.KEY] |
| 107 | self.assertRegex(value, self.VALID_FORMAT) | 146 | assert self.VALID_FORMAT.match(value) |
| 108 | 147 | ||
| 109 | 148 | ||
| 110 | class RunCommand(RepoWrapperTestCase): | 149 | class TestRunCommand: |
| 111 | """Check run_command behavior.""" | 150 | """Check run_command behavior.""" |
| 112 | 151 | ||
| 113 | def test_capture(self): | 152 | def test_capture(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 114 | """Check capture_output handling.""" | 153 | """Check capture_output handling.""" |
| 115 | ret = self.wrapper.run_command(["echo", "hi"], capture_output=True) | 154 | ret = repo_wrapper.run_command(["echo", "hi"], capture_output=True) |
| 116 | # echo command appends OS specific linesep, but on Windows + Git Bash | 155 | # echo command appends OS specific linesep, but on Windows + Git Bash |
| 117 | # we get UNIX ending, so we allow both. | 156 | # we get UNIX ending, so we allow both. |
| 118 | self.assertIn(ret.stdout, ["hi" + os.linesep, "hi\n"]) | 157 | assert ret.stdout in ["hi" + os.linesep, "hi\n"] |
| 119 | 158 | ||
| 120 | def test_check(self): | 159 | def test_check(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 121 | """Check check handling.""" | 160 | """Check check handling.""" |
| 122 | self.wrapper.run_command(["true"], check=False) | 161 | repo_wrapper.run_command(["true"], check=False) |
| 123 | self.wrapper.run_command(["true"], check=True) | 162 | repo_wrapper.run_command(["true"], check=True) |
| 124 | self.wrapper.run_command(["false"], check=False) | 163 | repo_wrapper.run_command(["false"], check=False) |
| 125 | with self.assertRaises(subprocess.CalledProcessError): | 164 | with pytest.raises(subprocess.CalledProcessError): |
| 126 | self.wrapper.run_command(["false"], check=True) | 165 | repo_wrapper.run_command(["false"], check=True) |
| 127 | 166 | ||
| 128 | 167 | ||
| 129 | class RunGit(RepoWrapperTestCase): | 168 | class TestRunGit: |
| 130 | """Check run_git behavior.""" | 169 | """Check run_git behavior.""" |
| 131 | 170 | ||
| 132 | def test_capture(self): | 171 | def test_capture(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 133 | """Check capture_output handling.""" | 172 | """Check capture_output handling.""" |
| 134 | ret = self.wrapper.run_git("--version") | 173 | ret = repo_wrapper.run_git("--version") |
| 135 | self.assertIn("git", ret.stdout) | 174 | assert "git" in ret.stdout |
| 136 | 175 | ||
| 137 | def test_check(self): | 176 | def test_check(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 138 | """Check check handling.""" | 177 | """Check check handling.""" |
| 139 | with self.assertRaises(self.wrapper.CloneFailure): | 178 | with pytest.raises(repo_wrapper.CloneFailure): |
| 140 | self.wrapper.run_git("--version-asdfasdf") | 179 | repo_wrapper.run_git("--version-asdfasdf") |
| 141 | self.wrapper.run_git("--version-asdfasdf", check=False) | 180 | repo_wrapper.run_git("--version-asdfasdf", check=False) |
| 142 | 181 | ||
| 143 | 182 | ||
| 144 | class ParseGitVersion(RepoWrapperTestCase): | 183 | class TestParseGitVersion: |
| 145 | """Check ParseGitVersion behavior.""" | 184 | """Check ParseGitVersion behavior.""" |
| 146 | 185 | ||
| 147 | def test_autoload(self): | 186 | def test_autoload(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 148 | """Check we can load the version from the live git.""" | 187 | """Check we can load the version from the live git.""" |
| 149 | ret = self.wrapper.ParseGitVersion() | 188 | assert repo_wrapper.ParseGitVersion() is not None |
| 150 | self.assertIsNotNone(ret) | ||
| 151 | 189 | ||
| 152 | def test_bad_ver(self): | 190 | def test_bad_ver(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 153 | """Check handling of bad git versions.""" | 191 | """Check handling of bad git versions.""" |
| 154 | ret = self.wrapper.ParseGitVersion(ver_str="asdf") | 192 | assert repo_wrapper.ParseGitVersion(ver_str="asdf") is None |
| 155 | self.assertIsNone(ret) | ||
| 156 | 193 | ||
| 157 | def test_normal_ver(self): | 194 | def test_normal_ver(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 158 | """Check handling of normal git versions.""" | 195 | """Check handling of normal git versions.""" |
| 159 | ret = self.wrapper.ParseGitVersion(ver_str="git version 2.25.1") | 196 | ret = repo_wrapper.ParseGitVersion(ver_str="git version 2.25.1") |
| 160 | self.assertEqual(2, ret.major) | 197 | assert ret.major == 2 |
| 161 | self.assertEqual(25, ret.minor) | 198 | assert ret.minor == 25 |
| 162 | self.assertEqual(1, ret.micro) | 199 | assert ret.micro == 1 |
| 163 | self.assertEqual("2.25.1", ret.full) | 200 | assert ret.full == "2.25.1" |
| 164 | 201 | ||
| 165 | def test_extended_ver(self): | 202 | def test_extended_ver(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 166 | """Check handling of extended distro git versions.""" | 203 | """Check handling of extended distro git versions.""" |
| 167 | ret = self.wrapper.ParseGitVersion( | 204 | ret = repo_wrapper.ParseGitVersion( |
| 168 | ver_str="git version 1.30.50.696.g5e7596f4ac-goog" | 205 | ver_str="git version 1.30.50.696.g5e7596f4ac-goog" |
| 169 | ) | 206 | ) |
| 170 | self.assertEqual(1, ret.major) | 207 | assert ret.major == 1 |
| 171 | self.assertEqual(30, ret.minor) | 208 | assert ret.minor == 30 |
| 172 | self.assertEqual(50, ret.micro) | 209 | assert ret.micro == 50 |
| 173 | self.assertEqual("1.30.50.696.g5e7596f4ac-goog", ret.full) | 210 | assert ret.full == "1.30.50.696.g5e7596f4ac-goog" |
| 174 | 211 | ||
| 175 | 212 | ||
| 176 | class CheckGitVersion(RepoWrapperTestCase): | 213 | class TestCheckGitVersion: |
| 177 | """Check _CheckGitVersion behavior.""" | 214 | """Check _CheckGitVersion behavior.""" |
| 178 | 215 | ||
| 179 | def test_unknown(self): | 216 | def test_unknown(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 180 | """Unknown versions should abort.""" | 217 | """Unknown versions should abort.""" |
| 181 | with mock.patch.object( | 218 | with mock.patch.object( |
| 182 | self.wrapper, "ParseGitVersion", return_value=None | 219 | repo_wrapper, "ParseGitVersion", return_value=None |
| 183 | ): | 220 | ): |
| 184 | with self.assertRaises(self.wrapper.CloneFailure): | 221 | with pytest.raises(repo_wrapper.CloneFailure): |
| 185 | self.wrapper._CheckGitVersion() | 222 | repo_wrapper._CheckGitVersion() |
| 186 | 223 | ||
| 187 | def test_old(self): | 224 | def test_old(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 188 | """Old versions should abort.""" | 225 | """Old versions should abort.""" |
| 189 | with mock.patch.object( | 226 | with mock.patch.object( |
| 190 | self.wrapper, | 227 | repo_wrapper, |
| 191 | "ParseGitVersion", | 228 | "ParseGitVersion", |
| 192 | return_value=self.wrapper.GitVersion(1, 0, 0, "1.0.0"), | 229 | return_value=repo_wrapper.GitVersion(1, 0, 0, "1.0.0"), |
| 193 | ): | 230 | ): |
| 194 | with self.assertRaises(self.wrapper.CloneFailure): | 231 | with pytest.raises(repo_wrapper.CloneFailure): |
| 195 | self.wrapper._CheckGitVersion() | 232 | repo_wrapper._CheckGitVersion() |
| 196 | 233 | ||
| 197 | def test_new(self): | 234 | def test_new(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 198 | """Newer versions should run fine.""" | 235 | """Newer versions should run fine.""" |
| 199 | with mock.patch.object( | 236 | with mock.patch.object( |
| 200 | self.wrapper, | 237 | repo_wrapper, |
| 201 | "ParseGitVersion", | 238 | "ParseGitVersion", |
| 202 | return_value=self.wrapper.GitVersion(100, 0, 0, "100.0.0"), | 239 | return_value=repo_wrapper.GitVersion(100, 0, 0, "100.0.0"), |
| 203 | ): | 240 | ): |
| 204 | self.wrapper._CheckGitVersion() | 241 | repo_wrapper._CheckGitVersion() |
| 205 | 242 | ||
| 206 | 243 | ||
| 207 | class Requirements(RepoWrapperTestCase): | 244 | class TestRequirements: |
| 208 | """Check Requirements handling.""" | 245 | """Check Requirements handling.""" |
| 209 | 246 | ||
| 210 | def test_missing_file(self): | 247 | def test_missing_file(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 211 | """Don't crash if the file is missing (old version).""" | 248 | """Don't crash if the file is missing (old version).""" |
| 212 | self.assertIsNone( | 249 | assert ( |
| 213 | self.wrapper.Requirements.from_dir(utils_for_test.THIS_DIR) | 250 | repo_wrapper.Requirements.from_dir(utils_for_test.THIS_DIR) is None |
| 214 | ) | 251 | ) |
| 215 | self.assertIsNone( | 252 | assert ( |
| 216 | self.wrapper.Requirements.from_file( | 253 | repo_wrapper.Requirements.from_file( |
| 217 | utils_for_test.THIS_DIR / "xxxxxxxxxxxxxxxxxxxxxxxx" | 254 | utils_for_test.THIS_DIR / "xxxxxxxxxxxxxxxxxxxxxxxx" |
| 218 | ) | 255 | ) |
| 256 | is None | ||
| 219 | ) | 257 | ) |
| 220 | 258 | ||
| 221 | def test_corrupt_data(self): | 259 | def test_corrupt_data(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 222 | """If the file can't be parsed, don't blow up.""" | 260 | """If the file can't be parsed, don't blow up.""" |
| 223 | self.assertIsNone(self.wrapper.Requirements.from_file(__file__)) | 261 | assert repo_wrapper.Requirements.from_file(__file__) is None |
| 224 | self.assertIsNone(self.wrapper.Requirements.from_data(b"x")) | 262 | assert repo_wrapper.Requirements.from_data(b"x") is None |
| 225 | 263 | ||
| 226 | def test_valid_data(self): | 264 | def test_valid_data(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 227 | """Make sure we can parse the file we ship.""" | 265 | """Make sure we can parse the file we ship.""" |
| 228 | self.assertIsNotNone(self.wrapper.Requirements.from_data(b"{}")) | 266 | assert repo_wrapper.Requirements.from_data(b"{}") is not None |
| 229 | rootdir = utils_for_test.THIS_DIR.parent | 267 | rootdir = utils_for_test.THIS_DIR.parent |
| 230 | self.assertIsNotNone(self.wrapper.Requirements.from_dir(rootdir)) | 268 | assert repo_wrapper.Requirements.from_dir(rootdir) is not None |
| 231 | self.assertIsNotNone( | 269 | assert ( |
| 232 | self.wrapper.Requirements.from_file(rootdir / "requirements.json") | 270 | repo_wrapper.Requirements.from_file(rootdir / "requirements.json") |
| 271 | is not None | ||
| 233 | ) | 272 | ) |
| 234 | 273 | ||
| 235 | def test_format_ver(self): | 274 | def test_format_ver(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 236 | """Check format_ver can format.""" | 275 | """Check format_ver can format.""" |
| 237 | self.assertEqual( | 276 | assert repo_wrapper.Requirements._format_ver((1, 2, 3)) == "1.2.3" |
| 238 | "1.2.3", self.wrapper.Requirements._format_ver((1, 2, 3)) | 277 | assert repo_wrapper.Requirements._format_ver([1]) == "1" |
| 239 | ) | ||
| 240 | self.assertEqual("1", self.wrapper.Requirements._format_ver([1])) | ||
| 241 | 278 | ||
| 242 | def test_assert_all_unknown(self): | 279 | def test_assert_all_unknown(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 243 | """Check assert_all works with incompatible file.""" | 280 | """Check assert_all works with incompatible file.""" |
| 244 | reqs = self.wrapper.Requirements({}) | 281 | reqs = repo_wrapper.Requirements({}) |
| 245 | reqs.assert_all() | 282 | reqs.assert_all() |
| 246 | 283 | ||
| 247 | def test_assert_all_new_repo(self): | 284 | def test_assert_all_new_repo(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 248 | """Check assert_all accepts new enough repo.""" | 285 | """Check assert_all accepts new enough repo.""" |
| 249 | reqs = self.wrapper.Requirements({"repo": {"hard": [1, 0]}}) | 286 | reqs = repo_wrapper.Requirements({"repo": {"hard": [1, 0]}}) |
| 250 | reqs.assert_all() | 287 | reqs.assert_all() |
| 251 | 288 | ||
| 252 | def test_assert_all_old_repo(self): | 289 | def test_assert_all_old_repo(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 253 | """Check assert_all rejects old repo.""" | 290 | """Check assert_all rejects old repo.""" |
| 254 | reqs = self.wrapper.Requirements({"repo": {"hard": [99999, 0]}}) | 291 | reqs = repo_wrapper.Requirements({"repo": {"hard": [99999, 0]}}) |
| 255 | with self.assertRaises(SystemExit): | 292 | with pytest.raises(SystemExit): |
| 256 | reqs.assert_all() | 293 | reqs.assert_all() |
| 257 | 294 | ||
| 258 | def test_assert_all_new_python(self): | 295 | def test_assert_all_new_python(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 259 | """Check assert_all accepts new enough python.""" | 296 | """Check assert_all accepts new enough python.""" |
| 260 | reqs = self.wrapper.Requirements({"python": {"hard": sys.version_info}}) | 297 | reqs = repo_wrapper.Requirements({"python": {"hard": sys.version_info}}) |
| 261 | reqs.assert_all() | 298 | reqs.assert_all() |
| 262 | 299 | ||
| 263 | def test_assert_all_old_python(self): | 300 | def test_assert_all_old_python(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 264 | """Check assert_all rejects old python.""" | 301 | """Check assert_all rejects old python.""" |
| 265 | reqs = self.wrapper.Requirements({"python": {"hard": [99999, 0]}}) | 302 | reqs = repo_wrapper.Requirements({"python": {"hard": [99999, 0]}}) |
| 266 | with self.assertRaises(SystemExit): | 303 | with pytest.raises(SystemExit): |
| 267 | reqs.assert_all() | 304 | reqs.assert_all() |
| 268 | 305 | ||
| 269 | def test_assert_ver_unknown(self): | 306 | def test_assert_ver_unknown(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 270 | """Check assert_ver works with incompatible file.""" | 307 | """Check assert_ver works with incompatible file.""" |
| 271 | reqs = self.wrapper.Requirements({}) | 308 | reqs = repo_wrapper.Requirements({}) |
| 272 | reqs.assert_ver("xxx", (1, 0)) | 309 | reqs.assert_ver("xxx", (1, 0)) |
| 273 | 310 | ||
| 274 | def test_assert_ver_new(self): | 311 | def test_assert_ver_new(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 275 | """Check assert_ver allows new enough versions.""" | 312 | """Check assert_ver allows new enough versions.""" |
| 276 | reqs = self.wrapper.Requirements( | 313 | reqs = repo_wrapper.Requirements( |
| 277 | {"git": {"hard": [1, 0], "soft": [2, 0]}} | 314 | {"git": {"hard": [1, 0], "soft": [2, 0]}} |
| 278 | ) | 315 | ) |
| 279 | reqs.assert_ver("git", (1, 0)) | 316 | reqs.assert_ver("git", (1, 0)) |
| @@ -281,274 +318,279 @@ class Requirements(RepoWrapperTestCase): | |||
| 281 | reqs.assert_ver("git", (2, 0)) | 318 | reqs.assert_ver("git", (2, 0)) |
| 282 | reqs.assert_ver("git", (2, 5)) | 319 | reqs.assert_ver("git", (2, 5)) |
| 283 | 320 | ||
| 284 | def test_assert_ver_old(self): | 321 | def test_assert_ver_old(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 285 | """Check assert_ver rejects old versions.""" | 322 | """Check assert_ver rejects old versions.""" |
| 286 | reqs = self.wrapper.Requirements( | 323 | reqs = repo_wrapper.Requirements( |
| 287 | {"git": {"hard": [1, 0], "soft": [2, 0]}} | 324 | {"git": {"hard": [1, 0], "soft": [2, 0]}} |
| 288 | ) | 325 | ) |
| 289 | with self.assertRaises(SystemExit): | 326 | with pytest.raises(SystemExit): |
| 290 | reqs.assert_ver("git", (0, 5)) | 327 | reqs.assert_ver("git", (0, 5)) |
| 291 | 328 | ||
| 292 | 329 | ||
| 293 | class NeedSetupGnuPG(RepoWrapperTestCase): | 330 | class TestNeedSetupGnuPG: |
| 294 | """Check NeedSetupGnuPG behavior.""" | 331 | """Check NeedSetupGnuPG behavior.""" |
| 295 | 332 | ||
| 296 | def test_missing_dir(self): | 333 | def test_missing_dir(self, tmp_path, repo_wrapper: wrapper.Wrapper) -> None: |
| 297 | """The ~/.repoconfig tree doesn't exist yet.""" | 334 | """The ~/.repoconfig tree doesn't exist yet.""" |
| 298 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 335 | repo_wrapper.home_dot_repo = str(tmp_path / "foo") |
| 299 | self.wrapper.home_dot_repo = os.path.join(tempdir, "foo") | 336 | assert repo_wrapper.NeedSetupGnuPG() |
| 300 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
| 301 | 337 | ||
| 302 | def test_missing_keyring(self): | 338 | def test_missing_keyring( |
| 339 | self, tmp_path, repo_wrapper: wrapper.Wrapper | ||
| 340 | ) -> None: | ||
| 303 | """The keyring-version file doesn't exist yet.""" | 341 | """The keyring-version file doesn't exist yet.""" |
| 304 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 342 | repo_wrapper.home_dot_repo = str(tmp_path) |
| 305 | self.wrapper.home_dot_repo = tempdir | 343 | assert repo_wrapper.NeedSetupGnuPG() |
| 306 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
| 307 | 344 | ||
| 308 | def test_empty_keyring(self): | 345 | def test_empty_keyring( |
| 346 | self, tmp_path, repo_wrapper: wrapper.Wrapper | ||
| 347 | ) -> None: | ||
| 309 | """The keyring-version file exists, but is empty.""" | 348 | """The keyring-version file exists, but is empty.""" |
| 310 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 349 | repo_wrapper.home_dot_repo = str(tmp_path) |
| 311 | self.wrapper.home_dot_repo = tempdir | 350 | (tmp_path / "keyring-version").write_text("") |
| 312 | with open(os.path.join(tempdir, "keyring-version"), "w"): | 351 | assert repo_wrapper.NeedSetupGnuPG() |
| 313 | pass | ||
| 314 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
| 315 | 352 | ||
| 316 | def test_old_keyring(self): | 353 | def test_old_keyring(self, tmp_path, repo_wrapper: wrapper.Wrapper) -> None: |
| 317 | """The keyring-version file exists, but it's old.""" | 354 | """The keyring-version file exists, but it's old.""" |
| 318 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 355 | repo_wrapper.home_dot_repo = str(tmp_path) |
| 319 | self.wrapper.home_dot_repo = tempdir | 356 | (tmp_path / "keyring-version").write_text("1.0\n") |
| 320 | with open(os.path.join(tempdir, "keyring-version"), "w") as fp: | 357 | assert repo_wrapper.NeedSetupGnuPG() |
| 321 | fp.write("1.0\n") | ||
| 322 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | ||
| 323 | 358 | ||
| 324 | def test_new_keyring(self): | 359 | def test_new_keyring(self, tmp_path, repo_wrapper: wrapper.Wrapper) -> None: |
| 325 | """The keyring-version file exists, and is up-to-date.""" | 360 | """The keyring-version file exists, and is up-to-date.""" |
| 326 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 361 | repo_wrapper.home_dot_repo = str(tmp_path) |
| 327 | self.wrapper.home_dot_repo = tempdir | 362 | (tmp_path / "keyring-version").write_text("1000.0\n") |
| 328 | with open(os.path.join(tempdir, "keyring-version"), "w") as fp: | 363 | assert not repo_wrapper.NeedSetupGnuPG() |
| 329 | fp.write("1000.0\n") | ||
| 330 | self.assertFalse(self.wrapper.NeedSetupGnuPG()) | ||
| 331 | 364 | ||
| 332 | 365 | ||
| 333 | class SetupGnuPG(RepoWrapperTestCase): | 366 | class TestSetupGnuPG: |
| 334 | """Check SetupGnuPG behavior.""" | 367 | """Check SetupGnuPG behavior.""" |
| 335 | 368 | ||
| 336 | def test_full(self): | 369 | def test_full(self, tmp_path, repo_wrapper: wrapper.Wrapper) -> None: |
| 337 | """Make sure it works completely.""" | 370 | """Make sure it works completely.""" |
| 338 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: | 371 | repo_wrapper.home_dot_repo = str(tmp_path) |
| 339 | self.wrapper.home_dot_repo = tempdir | 372 | repo_wrapper.gpg_dir = str(tmp_path / "gnupg") |
| 340 | self.wrapper.gpg_dir = os.path.join( | 373 | assert repo_wrapper.SetupGnuPG(True) |
| 341 | self.wrapper.home_dot_repo, "gnupg" | 374 | data = (tmp_path / "keyring-version").read_text() |
| 342 | ) | 375 | assert ( |
| 343 | self.assertTrue(self.wrapper.SetupGnuPG(True)) | 376 | ".".join(str(x) for x in repo_wrapper.KEYRING_VERSION) |
| 344 | with open(os.path.join(tempdir, "keyring-version")) as fp: | 377 | == data.strip() |
| 345 | data = fp.read() | 378 | ) |
| 346 | self.assertEqual( | ||
| 347 | ".".join(str(x) for x in self.wrapper.KEYRING_VERSION), | ||
| 348 | data.strip(), | ||
| 349 | ) | ||
| 350 | 379 | ||
| 351 | 380 | ||
| 352 | class VerifyRev(RepoWrapperTestCase): | 381 | class TestVerifyRev: |
| 353 | """Check verify_rev behavior.""" | 382 | """Check verify_rev behavior.""" |
| 354 | 383 | ||
| 355 | def test_verify_passes(self): | 384 | def test_verify_passes(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 356 | """Check when we have a valid signed tag.""" | 385 | """Check when we have a valid signed tag.""" |
| 357 | desc_result = subprocess.CompletedProcess([], 0, "v1.0\n", "") | 386 | desc_result = subprocess.CompletedProcess([], 0, "v1.0\n", "") |
| 358 | gpg_result = subprocess.CompletedProcess([], 0, "", "") | 387 | gpg_result = subprocess.CompletedProcess([], 0, "", "") |
| 359 | with mock.patch.object( | 388 | with mock.patch.object( |
| 360 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) | 389 | repo_wrapper, "run_git", side_effect=(desc_result, gpg_result) |
| 361 | ): | 390 | ): |
| 362 | ret = self.wrapper.verify_rev( | 391 | ret = repo_wrapper.verify_rev( |
| 363 | "/", "refs/heads/stable", "1234", True | 392 | "/", "refs/heads/stable", "1234", True |
| 364 | ) | 393 | ) |
| 365 | self.assertEqual("v1.0^0", ret) | 394 | assert ret == "v1.0^0" |
| 366 | 395 | ||
| 367 | def test_unsigned_commit(self): | 396 | def test_unsigned_commit(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 368 | """Check we fall back to signed tag when we have an unsigned commit.""" | 397 | """Check we fall back to signed tag when we have an unsigned commit.""" |
| 369 | desc_result = subprocess.CompletedProcess([], 0, "v1.0-10-g1234\n", "") | 398 | desc_result = subprocess.CompletedProcess([], 0, "v1.0-10-g1234\n", "") |
| 370 | gpg_result = subprocess.CompletedProcess([], 0, "", "") | 399 | gpg_result = subprocess.CompletedProcess([], 0, "", "") |
| 371 | with mock.patch.object( | 400 | with mock.patch.object( |
| 372 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) | 401 | repo_wrapper, "run_git", side_effect=(desc_result, gpg_result) |
| 373 | ): | 402 | ): |
| 374 | ret = self.wrapper.verify_rev( | 403 | ret = repo_wrapper.verify_rev( |
| 375 | "/", "refs/heads/stable", "1234", True | 404 | "/", "refs/heads/stable", "1234", True |
| 376 | ) | 405 | ) |
| 377 | self.assertEqual("v1.0^0", ret) | 406 | assert ret == "v1.0^0" |
| 378 | 407 | ||
| 379 | def test_verify_fails(self): | 408 | def test_verify_fails(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 380 | """Check we fall back to signed tag when we have an unsigned commit.""" | 409 | """Check we fall back to signed tag when we have an unsigned commit.""" |
| 381 | desc_result = subprocess.CompletedProcess([], 0, "v1.0-10-g1234\n", "") | 410 | desc_result = subprocess.CompletedProcess([], 0, "v1.0-10-g1234\n", "") |
| 382 | gpg_result = RuntimeError | 411 | gpg_result = RuntimeError |
| 383 | with mock.patch.object( | 412 | with mock.patch.object( |
| 384 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) | 413 | repo_wrapper, "run_git", side_effect=(desc_result, gpg_result) |
| 385 | ): | 414 | ): |
| 386 | with self.assertRaises(RuntimeError): | 415 | with pytest.raises(RuntimeError): |
| 387 | self.wrapper.verify_rev("/", "refs/heads/stable", "1234", True) | 416 | repo_wrapper.verify_rev("/", "refs/heads/stable", "1234", True) |
| 388 | |||
| 389 | |||
| 390 | class GitCheckoutTestCase(RepoWrapperTestCase): | ||
| 391 | """Tests that use a real/small git checkout.""" | ||
| 392 | |||
| 393 | GIT_DIR = None | ||
| 394 | REV_LIST = None | ||
| 395 | |||
| 396 | @classmethod | ||
| 397 | def setUpClass(cls): | ||
| 398 | # Create a repo to operate on, but do it once per-class. | ||
| 399 | cls.tempdirobj = tempfile.TemporaryDirectory(prefix="repo-rev-tests") | ||
| 400 | cls.GIT_DIR = cls.tempdirobj.name | ||
| 401 | run_git = wrapper.Wrapper().run_git | ||
| 402 | |||
| 403 | remote = os.path.join(cls.GIT_DIR, "remote") | ||
| 404 | os.mkdir(remote) | ||
| 405 | |||
| 406 | utils_for_test.init_git_tree(remote) | ||
| 407 | run_git("commit", "--allow-empty", "-minit", cwd=remote) | ||
| 408 | run_git("branch", "stable", cwd=remote) | ||
| 409 | run_git("tag", "v1.0", cwd=remote) | ||
| 410 | run_git("commit", "--allow-empty", "-m2nd commit", cwd=remote) | ||
| 411 | cls.REV_LIST = run_git( | ||
| 412 | "rev-list", "HEAD", cwd=remote | ||
| 413 | ).stdout.splitlines() | ||
| 414 | |||
| 415 | run_git("init", cwd=cls.GIT_DIR) | ||
| 416 | run_git( | ||
| 417 | "fetch", | ||
| 418 | remote, | ||
| 419 | "+refs/heads/*:refs/remotes/origin/*", | ||
| 420 | cwd=cls.GIT_DIR, | ||
| 421 | ) | ||
| 422 | |||
| 423 | @classmethod | ||
| 424 | def tearDownClass(cls): | ||
| 425 | if not cls.tempdirobj: | ||
| 426 | return | ||
| 427 | 417 | ||
| 428 | cls.tempdirobj.cleanup() | ||
| 429 | 418 | ||
| 430 | 419 | class TestResolveRepoRev: | |
| 431 | class ResolveRepoRev(GitCheckoutTestCase): | ||
| 432 | """Check resolve_repo_rev behavior.""" | 420 | """Check resolve_repo_rev behavior.""" |
| 433 | 421 | ||
| 434 | def test_explicit_branch(self): | 422 | def test_explicit_branch( |
| 423 | self, | ||
| 424 | repo_wrapper: wrapper.Wrapper, | ||
| 425 | git_checkout: GitCheckout, | ||
| 426 | ) -> None: | ||
| 435 | """Check refs/heads/branch argument.""" | 427 | """Check refs/heads/branch argument.""" |
| 436 | rrev, lrev = self.wrapper.resolve_repo_rev( | 428 | rrev, lrev = repo_wrapper.resolve_repo_rev( |
| 437 | self.GIT_DIR, "refs/heads/stable" | 429 | git_checkout.git_dir, "refs/heads/stable" |
| 438 | ) | 430 | ) |
| 439 | self.assertEqual("refs/heads/stable", rrev) | 431 | assert rrev == "refs/heads/stable" |
| 440 | self.assertEqual(self.REV_LIST[1], lrev) | 432 | assert lrev == git_checkout.rev_list[1] |
| 441 | 433 | ||
| 442 | with self.assertRaises(self.wrapper.CloneFailure): | 434 | with pytest.raises(repo_wrapper.CloneFailure): |
| 443 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "refs/heads/unknown") | 435 | repo_wrapper.resolve_repo_rev( |
| 436 | git_checkout.git_dir, "refs/heads/unknown" | ||
| 437 | ) | ||
| 444 | 438 | ||
| 445 | def test_explicit_tag(self): | 439 | def test_explicit_tag( |
| 440 | self, | ||
| 441 | repo_wrapper: wrapper.Wrapper, | ||
| 442 | git_checkout: GitCheckout, | ||
| 443 | ) -> None: | ||
| 446 | """Check refs/tags/tag argument.""" | 444 | """Check refs/tags/tag argument.""" |
| 447 | rrev, lrev = self.wrapper.resolve_repo_rev( | 445 | rrev, lrev = repo_wrapper.resolve_repo_rev( |
| 448 | self.GIT_DIR, "refs/tags/v1.0" | 446 | git_checkout.git_dir, "refs/tags/v1.0" |
| 449 | ) | 447 | ) |
| 450 | self.assertEqual("refs/tags/v1.0", rrev) | 448 | assert rrev == "refs/tags/v1.0" |
| 451 | self.assertEqual(self.REV_LIST[1], lrev) | 449 | assert lrev == git_checkout.rev_list[1] |
| 452 | 450 | ||
| 453 | with self.assertRaises(self.wrapper.CloneFailure): | 451 | with pytest.raises(repo_wrapper.CloneFailure): |
| 454 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "refs/tags/unknown") | 452 | repo_wrapper.resolve_repo_rev( |
| 453 | git_checkout.git_dir, "refs/tags/unknown" | ||
| 454 | ) | ||
| 455 | 455 | ||
| 456 | def test_branch_name(self): | 456 | def test_branch_name( |
| 457 | self, | ||
| 458 | repo_wrapper: wrapper.Wrapper, | ||
| 459 | git_checkout: GitCheckout, | ||
| 460 | ) -> None: | ||
| 457 | """Check branch argument.""" | 461 | """Check branch argument.""" |
| 458 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "stable") | 462 | rrev, lrev = repo_wrapper.resolve_repo_rev( |
| 459 | self.assertEqual("refs/heads/stable", rrev) | 463 | git_checkout.git_dir, "stable" |
| 460 | self.assertEqual(self.REV_LIST[1], lrev) | 464 | ) |
| 461 | 465 | assert rrev == "refs/heads/stable" | |
| 462 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "main") | 466 | assert lrev == git_checkout.rev_list[1] |
| 463 | self.assertEqual("refs/heads/main", rrev) | 467 | |
| 464 | self.assertEqual(self.REV_LIST[0], lrev) | 468 | rrev, lrev = repo_wrapper.resolve_repo_rev(git_checkout.git_dir, "main") |
| 465 | 469 | assert rrev == "refs/heads/main" | |
| 466 | def test_tag_name(self): | 470 | assert lrev == git_checkout.rev_list[0] |
| 471 | |||
| 472 | def test_tag_name( | ||
| 473 | self, | ||
| 474 | repo_wrapper: wrapper.Wrapper, | ||
| 475 | git_checkout: GitCheckout, | ||
| 476 | ) -> None: | ||
| 467 | """Check tag argument.""" | 477 | """Check tag argument.""" |
| 468 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "v1.0") | 478 | rrev, lrev = repo_wrapper.resolve_repo_rev(git_checkout.git_dir, "v1.0") |
| 469 | self.assertEqual("refs/tags/v1.0", rrev) | 479 | assert rrev == "refs/tags/v1.0" |
| 470 | self.assertEqual(self.REV_LIST[1], lrev) | 480 | assert lrev == git_checkout.rev_list[1] |
| 471 | 481 | ||
| 472 | def test_full_commit(self): | 482 | def test_full_commit( |
| 483 | self, | ||
| 484 | repo_wrapper: wrapper.Wrapper, | ||
| 485 | git_checkout: GitCheckout, | ||
| 486 | ) -> None: | ||
| 473 | """Check specific commit argument.""" | 487 | """Check specific commit argument.""" |
| 474 | commit = self.REV_LIST[0] | 488 | commit = git_checkout.rev_list[0] |
| 475 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | 489 | rrev, lrev = repo_wrapper.resolve_repo_rev(git_checkout.git_dir, commit) |
| 476 | self.assertEqual(commit, rrev) | 490 | assert rrev == commit |
| 477 | self.assertEqual(commit, lrev) | 491 | assert lrev == commit |
| 478 | 492 | ||
| 479 | def test_partial_commit(self): | 493 | def test_partial_commit( |
| 494 | self, | ||
| 495 | repo_wrapper: wrapper.Wrapper, | ||
| 496 | git_checkout: GitCheckout, | ||
| 497 | ) -> None: | ||
| 480 | """Check specific (partial) commit argument.""" | 498 | """Check specific (partial) commit argument.""" |
| 481 | commit = self.REV_LIST[0][0:20] | 499 | commit = git_checkout.rev_list[0][0:20] |
| 482 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | 500 | rrev, lrev = repo_wrapper.resolve_repo_rev(git_checkout.git_dir, commit) |
| 483 | self.assertEqual(self.REV_LIST[0], rrev) | 501 | assert rrev == git_checkout.rev_list[0] |
| 484 | self.assertEqual(self.REV_LIST[0], lrev) | 502 | assert lrev == git_checkout.rev_list[0] |
| 485 | 503 | ||
| 486 | def test_unknown(self): | 504 | def test_unknown( |
| 505 | self, | ||
| 506 | repo_wrapper: wrapper.Wrapper, | ||
| 507 | git_checkout: GitCheckout, | ||
| 508 | ) -> None: | ||
| 487 | """Check unknown ref/commit argument.""" | 509 | """Check unknown ref/commit argument.""" |
| 488 | with self.assertRaises(self.wrapper.CloneFailure): | 510 | with pytest.raises(repo_wrapper.CloneFailure): |
| 489 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "boooooooya") | 511 | repo_wrapper.resolve_repo_rev(git_checkout.git_dir, "boooooooya") |
| 490 | 512 | ||
| 491 | 513 | ||
| 492 | class CheckRepoVerify(RepoWrapperTestCase): | 514 | class TestCheckRepoVerify: |
| 493 | """Check check_repo_verify behavior.""" | 515 | """Check check_repo_verify behavior.""" |
| 494 | 516 | ||
| 495 | def test_no_verify(self): | 517 | def test_no_verify(self, repo_wrapper: wrapper.Wrapper) -> None: |
| 496 | """Always fail with --no-repo-verify.""" | 518 | """Always fail with --no-repo-verify.""" |
| 497 | self.assertFalse(self.wrapper.check_repo_verify(False)) | 519 | assert not repo_wrapper.check_repo_verify(False) |
| 498 | 520 | ||
| 499 | def test_gpg_initialized(self): | 521 | def test_gpg_initialized( |
| 522 | self, | ||
| 523 | repo_wrapper: wrapper.Wrapper, | ||
| 524 | ) -> None: | ||
| 500 | """Should pass if gpg is setup already.""" | 525 | """Should pass if gpg is setup already.""" |
| 501 | with mock.patch.object( | 526 | with mock.patch.object( |
| 502 | self.wrapper, "NeedSetupGnuPG", return_value=False | 527 | repo_wrapper, "NeedSetupGnuPG", return_value=False |
| 503 | ): | 528 | ): |
| 504 | self.assertTrue(self.wrapper.check_repo_verify(True)) | 529 | assert repo_wrapper.check_repo_verify(True) |
| 505 | 530 | ||
| 506 | def test_need_gpg_setup(self): | 531 | def test_need_gpg_setup( |
| 532 | self, | ||
| 533 | repo_wrapper: wrapper.Wrapper, | ||
| 534 | ) -> None: | ||
| 507 | """Should pass/fail based on gpg setup.""" | 535 | """Should pass/fail based on gpg setup.""" |
| 508 | with mock.patch.object( | 536 | with mock.patch.object( |
| 509 | self.wrapper, "NeedSetupGnuPG", return_value=True | 537 | repo_wrapper, "NeedSetupGnuPG", return_value=True |
| 510 | ): | 538 | ): |
| 511 | with mock.patch.object(self.wrapper, "SetupGnuPG") as m: | 539 | with mock.patch.object(repo_wrapper, "SetupGnuPG") as m: |
| 512 | m.return_value = True | 540 | m.return_value = True |
| 513 | self.assertTrue(self.wrapper.check_repo_verify(True)) | 541 | assert repo_wrapper.check_repo_verify(True) |
| 514 | 542 | ||
| 515 | m.return_value = False | 543 | m.return_value = False |
| 516 | self.assertFalse(self.wrapper.check_repo_verify(True)) | 544 | assert not repo_wrapper.check_repo_verify(True) |
| 517 | 545 | ||
| 518 | 546 | ||
| 519 | class CheckRepoRev(GitCheckoutTestCase): | 547 | class TestCheckRepoRev: |
| 520 | """Check check_repo_rev behavior.""" | 548 | """Check check_repo_rev behavior.""" |
| 521 | 549 | ||
| 522 | def test_verify_works(self): | 550 | def test_verify_works( |
| 551 | self, | ||
| 552 | repo_wrapper: wrapper.Wrapper, | ||
| 553 | git_checkout: GitCheckout, | ||
| 554 | ) -> None: | ||
| 523 | """Should pass when verification passes.""" | 555 | """Should pass when verification passes.""" |
| 524 | with mock.patch.object( | 556 | with mock.patch.object( |
| 525 | self.wrapper, "check_repo_verify", return_value=True | 557 | repo_wrapper, "check_repo_verify", return_value=True |
| 526 | ): | 558 | ): |
| 527 | with mock.patch.object( | 559 | with mock.patch.object( |
| 528 | self.wrapper, "verify_rev", return_value="12345" | 560 | repo_wrapper, "verify_rev", return_value="12345" |
| 529 | ): | 561 | ): |
| 530 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, "stable") | 562 | rrev, lrev = repo_wrapper.check_repo_rev( |
| 531 | self.assertEqual("refs/heads/stable", rrev) | 563 | git_checkout.git_dir, "stable" |
| 532 | self.assertEqual("12345", lrev) | 564 | ) |
| 533 | 565 | assert rrev == "refs/heads/stable" | |
| 534 | def test_verify_fails(self): | 566 | assert lrev == "12345" |
| 567 | |||
| 568 | def test_verify_fails( | ||
| 569 | self, | ||
| 570 | repo_wrapper: wrapper.Wrapper, | ||
| 571 | git_checkout: GitCheckout, | ||
| 572 | ) -> None: | ||
| 535 | """Should fail when verification fails.""" | 573 | """Should fail when verification fails.""" |
| 536 | with mock.patch.object( | 574 | with mock.patch.object( |
| 537 | self.wrapper, "check_repo_verify", return_value=True | 575 | repo_wrapper, "check_repo_verify", return_value=True |
| 538 | ): | 576 | ): |
| 539 | with mock.patch.object( | 577 | with mock.patch.object( |
| 540 | self.wrapper, "verify_rev", side_effect=RuntimeError | 578 | repo_wrapper, "verify_rev", side_effect=RuntimeError |
| 541 | ): | 579 | ): |
| 542 | with self.assertRaises(RuntimeError): | 580 | with pytest.raises(RuntimeError): |
| 543 | self.wrapper.check_repo_rev(self.GIT_DIR, "stable") | 581 | repo_wrapper.check_repo_rev(git_checkout.git_dir, "stable") |
| 544 | 582 | ||
| 545 | def test_verify_ignore(self): | 583 | def test_verify_ignore( |
| 584 | self, | ||
| 585 | repo_wrapper: wrapper.Wrapper, | ||
| 586 | git_checkout: GitCheckout, | ||
| 587 | ) -> None: | ||
| 546 | """Should pass when verification is disabled.""" | 588 | """Should pass when verification is disabled.""" |
| 547 | with mock.patch.object( | 589 | with mock.patch.object( |
| 548 | self.wrapper, "verify_rev", side_effect=RuntimeError | 590 | repo_wrapper, "verify_rev", side_effect=RuntimeError |
| 549 | ): | 591 | ): |
| 550 | rrev, lrev = self.wrapper.check_repo_rev( | 592 | rrev, lrev = repo_wrapper.check_repo_rev( |
| 551 | self.GIT_DIR, "stable", repo_verify=False | 593 | git_checkout.git_dir, "stable", repo_verify=False |
| 552 | ) | 594 | ) |
| 553 | self.assertEqual("refs/heads/stable", rrev) | 595 | assert rrev == "refs/heads/stable" |
| 554 | self.assertEqual(self.REV_LIST[1], lrev) | 596 | assert lrev == git_checkout.rev_list[1] |
