diff options
| author | Gavin Mak <gavinmak@google.com> | 2023-03-11 06:46:20 +0000 |
|---|---|---|
| committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-03-22 17:46:28 +0000 |
| commit | ea2e330e43c182dc16b0111ebc69ee5a71ee4ce1 (patch) | |
| tree | dc33ba0e56825b3e007d0589891756724725a465 /tests | |
| parent | 1604cf255f8c1786a23388db6d5277ac7949a24a (diff) | |
| download | git-repo-ea2e330e43c182dc16b0111ebc69ee5a71ee4ce1.tar.gz | |
Format codebase with black and check formatting in CQ
Apply rules set by https://gerrit-review.googlesource.com/c/git-repo/+/362954/ across the codebase and fix any lingering errors caught
by flake8. Also check black formatting in run_tests (and CQ).
Bug: b/267675342
Change-Id: I972d77649dac351150dcfeb1cd1ad0ea2efc1956
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/363474
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/conftest.py | 4 | ||||
| -rw-r--r-- | tests/test_editor.py | 44 | ||||
| -rw-r--r-- | tests/test_error.py | 60 | ||||
| -rw-r--r-- | tests/test_git_command.py | 222 | ||||
| -rw-r--r-- | tests/test_git_config.py | 318 | ||||
| -rw-r--r-- | tests/test_git_superproject.py | 782 | ||||
| -rw-r--r-- | tests/test_git_trace2_event_log.py | 725 | ||||
| -rw-r--r-- | tests/test_hooks.py | 63 | ||||
| -rw-r--r-- | tests/test_manifest_xml.py | 1414 | ||||
| -rw-r--r-- | tests/test_platform_utils.py | 54 | ||||
| -rw-r--r-- | tests/test_project.py | 855 | ||||
| -rw-r--r-- | tests/test_repo_trace.py | 68 | ||||
| -rw-r--r-- | tests/test_ssh.py | 90 | ||||
| -rw-r--r-- | tests/test_subcmds.py | 86 | ||||
| -rw-r--r-- | tests/test_subcmds_init.py | 51 | ||||
| -rw-r--r-- | tests/test_subcmds_sync.py | 215 | ||||
| -rw-r--r-- | tests/test_update_manpages.py | 10 | ||||
| -rw-r--r-- | tests/test_wrapper.py | 1029 |
18 files changed, 3275 insertions, 2815 deletions
diff --git a/tests/conftest.py b/tests/conftest.py index 3e43f6d3..e1a2292a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py | |||
| @@ -21,5 +21,5 @@ import repo_trace | |||
| 21 | 21 | ||
| 22 | @pytest.fixture(autouse=True) | 22 | @pytest.fixture(autouse=True) |
| 23 | def disable_repo_trace(tmp_path): | 23 | def disable_repo_trace(tmp_path): |
| 24 | """Set an environment marker to relax certain strict checks for test code.""" | 24 | """Set an environment marker to relax certain strict checks for test code.""" # noqa: E501 |
| 25 | repo_trace._TRACE_FILE = str(tmp_path / 'TRACE_FILE_from_test') | 25 | repo_trace._TRACE_FILE = str(tmp_path / "TRACE_FILE_from_test") |
diff --git a/tests/test_editor.py b/tests/test_editor.py index cfd4f5ed..8f5d160e 100644 --- a/tests/test_editor.py +++ b/tests/test_editor.py | |||
| @@ -20,37 +20,37 @@ from editor import Editor | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | class EditorTestCase(unittest.TestCase): | 22 | class EditorTestCase(unittest.TestCase): |
| 23 | """Take care of resetting Editor state across tests.""" | 23 | """Take care of resetting Editor state across tests.""" |
| 24 | 24 | ||
| 25 | def setUp(self): | 25 | def setUp(self): |
| 26 | self.setEditor(None) | 26 | self.setEditor(None) |
| 27 | 27 | ||
| 28 | def tearDown(self): | 28 | def tearDown(self): |
| 29 | self.setEditor(None) | 29 | self.setEditor(None) |
| 30 | 30 | ||
| 31 | @staticmethod | 31 | @staticmethod |
| 32 | def setEditor(editor): | 32 | def setEditor(editor): |
| 33 | Editor._editor = editor | 33 | Editor._editor = editor |
| 34 | 34 | ||
| 35 | 35 | ||
| 36 | class GetEditor(EditorTestCase): | 36 | class GetEditor(EditorTestCase): |
| 37 | """Check GetEditor behavior.""" | 37 | """Check GetEditor behavior.""" |
| 38 | 38 | ||
| 39 | def test_basic(self): | 39 | def test_basic(self): |
| 40 | """Basic checking of _GetEditor.""" | 40 | """Basic checking of _GetEditor.""" |
| 41 | self.setEditor(':') | 41 | self.setEditor(":") |
| 42 | self.assertEqual(':', Editor._GetEditor()) | 42 | self.assertEqual(":", Editor._GetEditor()) |
| 43 | 43 | ||
| 44 | 44 | ||
| 45 | class EditString(EditorTestCase): | 45 | class EditString(EditorTestCase): |
| 46 | """Check EditString behavior.""" | 46 | """Check EditString behavior.""" |
| 47 | 47 | ||
| 48 | def test_no_editor(self): | 48 | def test_no_editor(self): |
| 49 | """Check behavior when no editor is available.""" | 49 | """Check behavior when no editor is available.""" |
| 50 | self.setEditor(':') | 50 | self.setEditor(":") |
| 51 | self.assertEqual('foo', Editor.EditString('foo')) | 51 | self.assertEqual("foo", Editor.EditString("foo")) |
| 52 | 52 | ||
| 53 | def test_cat_editor(self): | 53 | def test_cat_editor(self): |
| 54 | """Check behavior when editor is `cat`.""" | 54 | """Check behavior when editor is `cat`.""" |
| 55 | self.setEditor('cat') | 55 | self.setEditor("cat") |
| 56 | self.assertEqual('foo', Editor.EditString('foo')) | 56 | self.assertEqual("foo", Editor.EditString("foo")) |
diff --git a/tests/test_error.py b/tests/test_error.py index 82b00c24..784e2d57 100644 --- a/tests/test_error.py +++ b/tests/test_error.py | |||
| @@ -22,32 +22,34 @@ import error | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class PickleTests(unittest.TestCase): | 24 | class PickleTests(unittest.TestCase): |
| 25 | """Make sure all our custom exceptions can be pickled.""" | 25 | """Make sure all our custom exceptions can be pickled.""" |
| 26 | 26 | ||
| 27 | def getExceptions(self): | 27 | def getExceptions(self): |
| 28 | """Return all our custom exceptions.""" | 28 | """Return all our custom exceptions.""" |
| 29 | for name in dir(error): | 29 | for name in dir(error): |
| 30 | cls = getattr(error, name) | 30 | cls = getattr(error, name) |
| 31 | if isinstance(cls, type) and issubclass(cls, Exception): | 31 | if isinstance(cls, type) and issubclass(cls, Exception): |
| 32 | yield cls | 32 | yield cls |
| 33 | 33 | ||
| 34 | def testExceptionLookup(self): | 34 | def testExceptionLookup(self): |
| 35 | """Make sure our introspection logic works.""" | 35 | """Make sure our introspection logic works.""" |
| 36 | classes = list(self.getExceptions()) | 36 | classes = list(self.getExceptions()) |
| 37 | self.assertIn(error.HookError, classes) | 37 | self.assertIn(error.HookError, classes) |
| 38 | # Don't assert the exact number to avoid being a change-detector test. | 38 | # Don't assert the exact number to avoid being a change-detector test. |
| 39 | self.assertGreater(len(classes), 10) | 39 | self.assertGreater(len(classes), 10) |
| 40 | 40 | ||
| 41 | def testPickle(self): | 41 | def testPickle(self): |
| 42 | """Try to pickle all the exceptions.""" | 42 | """Try to pickle all the exceptions.""" |
| 43 | for cls in self.getExceptions(): | 43 | for cls in self.getExceptions(): |
| 44 | args = inspect.getfullargspec(cls.__init__).args[1:] | 44 | args = inspect.getfullargspec(cls.__init__).args[1:] |
| 45 | obj = cls(*args) | 45 | obj = cls(*args) |
| 46 | p = pickle.dumps(obj) | 46 | p = pickle.dumps(obj) |
| 47 | try: | 47 | try: |
| 48 | newobj = pickle.loads(p) | 48 | newobj = pickle.loads(p) |
| 49 | except Exception as e: # pylint: disable=broad-except | 49 | except Exception as e: # pylint: disable=broad-except |
| 50 | self.fail('Class %s is unable to be pickled: %s\n' | 50 | self.fail( |
| 51 | 'Incomplete super().__init__(...) call?' % (cls, e)) | 51 | "Class %s is unable to be pickled: %s\n" |
| 52 | self.assertIsInstance(newobj, cls) | 52 | "Incomplete super().__init__(...) call?" % (cls, e) |
| 53 | self.assertEqual(str(obj), str(newobj)) | 53 | ) |
| 54 | self.assertIsInstance(newobj, cls) | ||
| 55 | self.assertEqual(str(obj), str(newobj)) | ||
diff --git a/tests/test_git_command.py b/tests/test_git_command.py index 96408a23..c4c3a4c5 100644 --- a/tests/test_git_command.py +++ b/tests/test_git_command.py | |||
| @@ -19,138 +19,146 @@ import os | |||
| 19 | import unittest | 19 | import unittest |
| 20 | 20 | ||
| 21 | try: | 21 | try: |
| 22 | from unittest import mock | 22 | from unittest import mock |
| 23 | except ImportError: | 23 | except ImportError: |
| 24 | import mock | 24 | import mock |
| 25 | 25 | ||
| 26 | import git_command | 26 | import git_command |
| 27 | import wrapper | 27 | import wrapper |
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | class GitCommandTest(unittest.TestCase): | 30 | class GitCommandTest(unittest.TestCase): |
| 31 | """Tests the GitCommand class (via git_command.git).""" | 31 | """Tests the GitCommand class (via git_command.git).""" |
| 32 | 32 | ||
| 33 | def setUp(self): | 33 | def setUp(self): |
| 34 | def realpath_mock(val): | ||
| 35 | return val | ||
| 34 | 36 | ||
| 35 | def realpath_mock(val): | 37 | mock.patch.object( |
| 36 | return val | 38 | os.path, "realpath", side_effect=realpath_mock |
| 39 | ).start() | ||
| 37 | 40 | ||
| 38 | mock.patch.object(os.path, 'realpath', side_effect=realpath_mock).start() | 41 | def tearDown(self): |
| 42 | mock.patch.stopall() | ||
| 39 | 43 | ||
| 40 | def tearDown(self): | 44 | def test_alternative_setting_when_matching(self): |
| 41 | mock.patch.stopall() | 45 | r = git_command._build_env( |
| 46 | objdir=os.path.join("zap", "objects"), gitdir="zap" | ||
| 47 | ) | ||
| 42 | 48 | ||
| 43 | def test_alternative_setting_when_matching(self): | 49 | self.assertIsNone(r.get("GIT_ALTERNATE_OBJECT_DIRECTORIES")) |
| 44 | r = git_command._build_env( | 50 | self.assertEqual( |
| 45 | objdir = os.path.join('zap', 'objects'), | 51 | r.get("GIT_OBJECT_DIRECTORY"), os.path.join("zap", "objects") |
| 46 | gitdir = 'zap' | 52 | ) |
| 47 | ) | ||
| 48 | 53 | ||
| 49 | self.assertIsNone(r.get('GIT_ALTERNATE_OBJECT_DIRECTORIES')) | 54 | def test_alternative_setting_when_different(self): |
| 50 | self.assertEqual(r.get('GIT_OBJECT_DIRECTORY'), os.path.join('zap', 'objects')) | 55 | r = git_command._build_env( |
| 56 | objdir=os.path.join("wow", "objects"), gitdir="zap" | ||
| 57 | ) | ||
| 51 | 58 | ||
| 52 | def test_alternative_setting_when_different(self): | 59 | self.assertEqual( |
| 53 | r = git_command._build_env( | 60 | r.get("GIT_ALTERNATE_OBJECT_DIRECTORIES"), |
| 54 | objdir = os.path.join('wow', 'objects'), | 61 | os.path.join("zap", "objects"), |
| 55 | gitdir = 'zap' | 62 | ) |
| 56 | ) | 63 | self.assertEqual( |
| 57 | 64 | r.get("GIT_OBJECT_DIRECTORY"), os.path.join("wow", "objects") | |
| 58 | self.assertEqual(r.get('GIT_ALTERNATE_OBJECT_DIRECTORIES'), os.path.join('zap', 'objects')) | 65 | ) |
| 59 | self.assertEqual(r.get('GIT_OBJECT_DIRECTORY'), os.path.join('wow', 'objects')) | ||
| 60 | 66 | ||
| 61 | 67 | ||
| 62 | class GitCallUnitTest(unittest.TestCase): | 68 | class GitCallUnitTest(unittest.TestCase): |
| 63 | """Tests the _GitCall class (via git_command.git).""" | 69 | """Tests the _GitCall class (via git_command.git).""" |
| 64 | 70 | ||
| 65 | def test_version_tuple(self): | 71 | def test_version_tuple(self): |
| 66 | """Check git.version_tuple() handling.""" | 72 | """Check git.version_tuple() handling.""" |
| 67 | ver = git_command.git.version_tuple() | 73 | ver = git_command.git.version_tuple() |
| 68 | self.assertIsNotNone(ver) | 74 | self.assertIsNotNone(ver) |
| 69 | 75 | ||
| 70 | # We don't dive too deep into the values here to avoid having to update | 76 | # We don't dive too deep into the values here to avoid having to update |
| 71 | # whenever git versions change. We do check relative to this min version | 77 | # whenever git versions change. We do check relative to this min |
| 72 | # as this is what `repo` itself requires via MIN_GIT_VERSION. | 78 | # version as this is what `repo` itself requires via MIN_GIT_VERSION. |
| 73 | MIN_GIT_VERSION = (2, 10, 2) | 79 | MIN_GIT_VERSION = (2, 10, 2) |
| 74 | self.assertTrue(isinstance(ver.major, int)) | 80 | self.assertTrue(isinstance(ver.major, int)) |
| 75 | self.assertTrue(isinstance(ver.minor, int)) | 81 | self.assertTrue(isinstance(ver.minor, int)) |
| 76 | self.assertTrue(isinstance(ver.micro, int)) | 82 | self.assertTrue(isinstance(ver.micro, int)) |
| 77 | 83 | ||
| 78 | self.assertGreater(ver.major, MIN_GIT_VERSION[0] - 1) | 84 | self.assertGreater(ver.major, MIN_GIT_VERSION[0] - 1) |
| 79 | self.assertGreaterEqual(ver.micro, 0) | 85 | self.assertGreaterEqual(ver.micro, 0) |
| 80 | self.assertGreaterEqual(ver.major, 0) | 86 | self.assertGreaterEqual(ver.major, 0) |
| 81 | 87 | ||
| 82 | self.assertGreaterEqual(ver, MIN_GIT_VERSION) | 88 | self.assertGreaterEqual(ver, MIN_GIT_VERSION) |
| 83 | self.assertLess(ver, (9999, 9999, 9999)) | 89 | self.assertLess(ver, (9999, 9999, 9999)) |
| 84 | 90 | ||
| 85 | self.assertNotEqual('', ver.full) | 91 | self.assertNotEqual("", ver.full) |
| 86 | 92 | ||
| 87 | 93 | ||
| 88 | class UserAgentUnitTest(unittest.TestCase): | 94 | class UserAgentUnitTest(unittest.TestCase): |
| 89 | """Tests the UserAgent function.""" | 95 | """Tests the UserAgent function.""" |
| 90 | 96 | ||
| 91 | def test_smoke_os(self): | 97 | def test_smoke_os(self): |
| 92 | """Make sure UA OS setting returns something useful.""" | 98 | """Make sure UA OS setting returns something useful.""" |
| 93 | os_name = git_command.user_agent.os | 99 | os_name = git_command.user_agent.os |
| 94 | # We can't dive too deep because of OS/tool differences, but we can check | 100 | # We can't dive too deep because of OS/tool differences, but we can |
| 95 | # the general form. | 101 | # check the general form. |
| 96 | m = re.match(r'^[^ ]+$', os_name) | 102 | m = re.match(r"^[^ ]+$", os_name) |
| 97 | self.assertIsNotNone(m) | 103 | self.assertIsNotNone(m) |
| 98 | 104 | ||
| 99 | def test_smoke_repo(self): | 105 | def test_smoke_repo(self): |
| 100 | """Make sure repo UA returns something useful.""" | 106 | """Make sure repo UA returns something useful.""" |
| 101 | ua = git_command.user_agent.repo | 107 | ua = git_command.user_agent.repo |
| 102 | # We can't dive too deep because of OS/tool differences, but we can check | 108 | # We can't dive too deep because of OS/tool differences, but we can |
| 103 | # the general form. | 109 | # check the general form. |
| 104 | m = re.match(r'^git-repo/[^ ]+ ([^ ]+) git/[^ ]+ Python/[0-9.]+', ua) | 110 | m = re.match(r"^git-repo/[^ ]+ ([^ ]+) git/[^ ]+ Python/[0-9.]+", ua) |
| 105 | self.assertIsNotNone(m) | 111 | self.assertIsNotNone(m) |
| 106 | 112 | ||
| 107 | def test_smoke_git(self): | 113 | def test_smoke_git(self): |
| 108 | """Make sure git UA returns something useful.""" | 114 | """Make sure git UA returns something useful.""" |
| 109 | ua = git_command.user_agent.git | 115 | ua = git_command.user_agent.git |
| 110 | # We can't dive too deep because of OS/tool differences, but we can check | 116 | # We can't dive too deep because of OS/tool differences, but we can |
| 111 | # the general form. | 117 | # check the general form. |
| 112 | m = re.match(r'^git/[^ ]+ ([^ ]+) git-repo/[^ ]+', ua) | 118 | m = re.match(r"^git/[^ ]+ ([^ ]+) git-repo/[^ ]+", ua) |
| 113 | self.assertIsNotNone(m) | 119 | self.assertIsNotNone(m) |
| 114 | 120 | ||
| 115 | 121 | ||
| 116 | class GitRequireTests(unittest.TestCase): | 122 | class GitRequireTests(unittest.TestCase): |
| 117 | """Test the git_require helper.""" | 123 | """Test the git_require helper.""" |
| 118 | 124 | ||
| 119 | def setUp(self): | 125 | def setUp(self): |
| 120 | self.wrapper = wrapper.Wrapper() | 126 | self.wrapper = wrapper.Wrapper() |
| 121 | ver = self.wrapper.GitVersion(1, 2, 3, 4) | 127 | ver = self.wrapper.GitVersion(1, 2, 3, 4) |
| 122 | mock.patch.object(git_command.git, 'version_tuple', return_value=ver).start() | 128 | mock.patch.object( |
| 123 | 129 | git_command.git, "version_tuple", return_value=ver | |
| 124 | def tearDown(self): | 130 | ).start() |
| 125 | mock.patch.stopall() | 131 | |
| 126 | 132 | def tearDown(self): | |
| 127 | def test_older_nonfatal(self): | 133 | mock.patch.stopall() |
| 128 | """Test non-fatal require calls with old versions.""" | 134 | |
| 129 | self.assertFalse(git_command.git_require((2,))) | 135 | def test_older_nonfatal(self): |
| 130 | self.assertFalse(git_command.git_require((1, 3))) | 136 | """Test non-fatal require calls with old versions.""" |
| 131 | self.assertFalse(git_command.git_require((1, 2, 4))) | 137 | self.assertFalse(git_command.git_require((2,))) |
| 132 | self.assertFalse(git_command.git_require((1, 2, 3, 5))) | 138 | self.assertFalse(git_command.git_require((1, 3))) |
| 133 | 139 | self.assertFalse(git_command.git_require((1, 2, 4))) | |
| 134 | def test_newer_nonfatal(self): | 140 | self.assertFalse(git_command.git_require((1, 2, 3, 5))) |
| 135 | """Test non-fatal require calls with newer versions.""" | 141 | |
| 136 | self.assertTrue(git_command.git_require((0,))) | 142 | def test_newer_nonfatal(self): |
| 137 | self.assertTrue(git_command.git_require((1, 0))) | 143 | """Test non-fatal require calls with newer versions.""" |
| 138 | self.assertTrue(git_command.git_require((1, 2, 0))) | 144 | self.assertTrue(git_command.git_require((0,))) |
| 139 | self.assertTrue(git_command.git_require((1, 2, 3, 0))) | 145 | self.assertTrue(git_command.git_require((1, 0))) |
| 140 | 146 | self.assertTrue(git_command.git_require((1, 2, 0))) | |
| 141 | def test_equal_nonfatal(self): | 147 | self.assertTrue(git_command.git_require((1, 2, 3, 0))) |
| 142 | """Test require calls with equal values.""" | 148 | |
| 143 | self.assertTrue(git_command.git_require((1, 2, 3, 4), fail=False)) | 149 | def test_equal_nonfatal(self): |
| 144 | self.assertTrue(git_command.git_require((1, 2, 3, 4), fail=True)) | 150 | """Test require calls with equal values.""" |
| 145 | 151 | self.assertTrue(git_command.git_require((1, 2, 3, 4), fail=False)) | |
| 146 | def test_older_fatal(self): | 152 | self.assertTrue(git_command.git_require((1, 2, 3, 4), fail=True)) |
| 147 | """Test fatal require calls with old versions.""" | 153 | |
| 148 | with self.assertRaises(SystemExit) as e: | 154 | def test_older_fatal(self): |
| 149 | git_command.git_require((2,), fail=True) | 155 | """Test fatal require calls with old versions.""" |
| 150 | self.assertNotEqual(0, e.code) | 156 | with self.assertRaises(SystemExit) as e: |
| 151 | 157 | git_command.git_require((2,), fail=True) | |
| 152 | def test_older_fatal_msg(self): | 158 | self.assertNotEqual(0, e.code) |
| 153 | """Test fatal require calls with old versions and message.""" | 159 | |
| 154 | with self.assertRaises(SystemExit) as e: | 160 | def test_older_fatal_msg(self): |
| 155 | git_command.git_require((2,), fail=True, msg='so sad') | 161 | """Test fatal require calls with old versions and message.""" |
| 156 | self.assertNotEqual(0, e.code) | 162 | with self.assertRaises(SystemExit) as e: |
| 163 | git_command.git_require((2,), fail=True, msg="so sad") | ||
| 164 | self.assertNotEqual(0, e.code) | ||
diff --git a/tests/test_git_config.py b/tests/test_git_config.py index 3b0aa8b4..a44dca0f 100644 --- a/tests/test_git_config.py +++ b/tests/test_git_config.py | |||
| @@ -22,167 +22,169 @@ import git_config | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | def fixture(*paths): | 24 | def fixture(*paths): |
| 25 | """Return a path relative to test/fixtures. | 25 | """Return a path relative to test/fixtures.""" |
| 26 | """ | 26 | return os.path.join(os.path.dirname(__file__), "fixtures", *paths) |
| 27 | return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) | ||
| 28 | 27 | ||
| 29 | 28 | ||
| 30 | class GitConfigReadOnlyTests(unittest.TestCase): | 29 | class GitConfigReadOnlyTests(unittest.TestCase): |
| 31 | """Read-only tests of the GitConfig class.""" | 30 | """Read-only tests of the GitConfig class.""" |
| 32 | 31 | ||
| 33 | def setUp(self): | 32 | def setUp(self): |
| 34 | """Create a GitConfig object using the test.gitconfig fixture. | 33 | """Create a GitConfig object using the test.gitconfig fixture.""" |
| 35 | """ | 34 | config_fixture = fixture("test.gitconfig") |
| 36 | config_fixture = fixture('test.gitconfig') | 35 | self.config = git_config.GitConfig(config_fixture) |
| 37 | self.config = git_config.GitConfig(config_fixture) | 36 | |
| 38 | 37 | def test_GetString_with_empty_config_values(self): | |
| 39 | def test_GetString_with_empty_config_values(self): | 38 | """ |
| 40 | """ | 39 | Test config entries with no value. |
| 41 | Test config entries with no value. | 40 | |
| 42 | 41 | [section] | |
| 43 | [section] | 42 | empty |
| 44 | empty | 43 | |
| 45 | 44 | """ | |
| 46 | """ | 45 | val = self.config.GetString("section.empty") |
| 47 | val = self.config.GetString('section.empty') | 46 | self.assertEqual(val, None) |
| 48 | self.assertEqual(val, None) | 47 | |
| 49 | 48 | def test_GetString_with_true_value(self): | |
| 50 | def test_GetString_with_true_value(self): | 49 | """ |
| 51 | """ | 50 | Test config entries with a string value. |
| 52 | Test config entries with a string value. | 51 | |
| 53 | 52 | [section] | |
| 54 | [section] | 53 | nonempty = true |
| 55 | nonempty = true | 54 | |
| 56 | 55 | """ | |
| 57 | """ | 56 | val = self.config.GetString("section.nonempty") |
| 58 | val = self.config.GetString('section.nonempty') | 57 | self.assertEqual(val, "true") |
| 59 | self.assertEqual(val, 'true') | 58 | |
| 60 | 59 | def test_GetString_from_missing_file(self): | |
| 61 | def test_GetString_from_missing_file(self): | 60 | """ |
| 62 | """ | 61 | Test missing config file |
| 63 | Test missing config file | 62 | """ |
| 64 | """ | 63 | config_fixture = fixture("not.present.gitconfig") |
| 65 | config_fixture = fixture('not.present.gitconfig') | 64 | config = git_config.GitConfig(config_fixture) |
| 66 | config = git_config.GitConfig(config_fixture) | 65 | val = config.GetString("empty") |
| 67 | val = config.GetString('empty') | 66 | self.assertEqual(val, None) |
| 68 | self.assertEqual(val, None) | 67 | |
| 69 | 68 | def test_GetBoolean_undefined(self): | |
| 70 | def test_GetBoolean_undefined(self): | 69 | """Test GetBoolean on key that doesn't exist.""" |
| 71 | """Test GetBoolean on key that doesn't exist.""" | 70 | self.assertIsNone(self.config.GetBoolean("section.missing")) |
| 72 | self.assertIsNone(self.config.GetBoolean('section.missing')) | 71 | |
| 73 | 72 | def test_GetBoolean_invalid(self): | |
| 74 | def test_GetBoolean_invalid(self): | 73 | """Test GetBoolean on invalid boolean value.""" |
| 75 | """Test GetBoolean on invalid boolean value.""" | 74 | self.assertIsNone(self.config.GetBoolean("section.boolinvalid")) |
| 76 | self.assertIsNone(self.config.GetBoolean('section.boolinvalid')) | 75 | |
| 77 | 76 | def test_GetBoolean_true(self): | |
| 78 | def test_GetBoolean_true(self): | 77 | """Test GetBoolean on valid true boolean.""" |
| 79 | """Test GetBoolean on valid true boolean.""" | 78 | self.assertTrue(self.config.GetBoolean("section.booltrue")) |
| 80 | self.assertTrue(self.config.GetBoolean('section.booltrue')) | 79 | |
| 81 | 80 | def test_GetBoolean_false(self): | |
| 82 | def test_GetBoolean_false(self): | 81 | """Test GetBoolean on valid false boolean.""" |
| 83 | """Test GetBoolean on valid false boolean.""" | 82 | self.assertFalse(self.config.GetBoolean("section.boolfalse")) |
| 84 | self.assertFalse(self.config.GetBoolean('section.boolfalse')) | 83 | |
| 85 | 84 | def test_GetInt_undefined(self): | |
| 86 | def test_GetInt_undefined(self): | 85 | """Test GetInt on key that doesn't exist.""" |
| 87 | """Test GetInt on key that doesn't exist.""" | 86 | self.assertIsNone(self.config.GetInt("section.missing")) |
| 88 | self.assertIsNone(self.config.GetInt('section.missing')) | 87 | |
| 89 | 88 | def test_GetInt_invalid(self): | |
| 90 | def test_GetInt_invalid(self): | 89 | """Test GetInt on invalid integer value.""" |
| 91 | """Test GetInt on invalid integer value.""" | 90 | self.assertIsNone(self.config.GetBoolean("section.intinvalid")) |
| 92 | self.assertIsNone(self.config.GetBoolean('section.intinvalid')) | 91 | |
| 93 | 92 | def test_GetInt_valid(self): | |
| 94 | def test_GetInt_valid(self): | 93 | """Test GetInt on valid integers.""" |
| 95 | """Test GetInt on valid integers.""" | 94 | TESTS = ( |
| 96 | TESTS = ( | 95 | ("inthex", 16), |
| 97 | ('inthex', 16), | 96 | ("inthexk", 16384), |
| 98 | ('inthexk', 16384), | 97 | ("int", 10), |
| 99 | ('int', 10), | 98 | ("intk", 10240), |
| 100 | ('intk', 10240), | 99 | ("intm", 10485760), |
| 101 | ('intm', 10485760), | 100 | ("intg", 10737418240), |
| 102 | ('intg', 10737418240), | 101 | ) |
| 103 | ) | 102 | for key, value in TESTS: |
| 104 | for key, value in TESTS: | 103 | self.assertEqual(value, self.config.GetInt("section.%s" % (key,))) |
| 105 | self.assertEqual(value, self.config.GetInt('section.%s' % (key,))) | ||
| 106 | 104 | ||
| 107 | 105 | ||
| 108 | class GitConfigReadWriteTests(unittest.TestCase): | 106 | class GitConfigReadWriteTests(unittest.TestCase): |
| 109 | """Read/write tests of the GitConfig class.""" | 107 | """Read/write tests of the GitConfig class.""" |
| 110 | 108 | ||
| 111 | def setUp(self): | 109 | def setUp(self): |
| 112 | self.tmpfile = tempfile.NamedTemporaryFile() | 110 | self.tmpfile = tempfile.NamedTemporaryFile() |
| 113 | self.config = self.get_config() | 111 | self.config = self.get_config() |
| 114 | 112 | ||
| 115 | def get_config(self): | 113 | def get_config(self): |
| 116 | """Get a new GitConfig instance.""" | 114 | """Get a new GitConfig instance.""" |
| 117 | return git_config.GitConfig(self.tmpfile.name) | 115 | return git_config.GitConfig(self.tmpfile.name) |
| 118 | 116 | ||
| 119 | def test_SetString(self): | 117 | def test_SetString(self): |
| 120 | """Test SetString behavior.""" | 118 | """Test SetString behavior.""" |
| 121 | # Set a value. | 119 | # Set a value. |
| 122 | self.assertIsNone(self.config.GetString('foo.bar')) | 120 | self.assertIsNone(self.config.GetString("foo.bar")) |
| 123 | self.config.SetString('foo.bar', 'val') | 121 | self.config.SetString("foo.bar", "val") |
| 124 | self.assertEqual('val', self.config.GetString('foo.bar')) | 122 | self.assertEqual("val", self.config.GetString("foo.bar")) |
| 125 | 123 | ||
| 126 | # Make sure the value was actually written out. | 124 | # Make sure the value was actually written out. |
| 127 | config = self.get_config() | 125 | config = self.get_config() |
| 128 | self.assertEqual('val', config.GetString('foo.bar')) | 126 | self.assertEqual("val", config.GetString("foo.bar")) |
| 129 | 127 | ||
| 130 | # Update the value. | 128 | # Update the value. |
| 131 | self.config.SetString('foo.bar', 'valll') | 129 | self.config.SetString("foo.bar", "valll") |
| 132 | self.assertEqual('valll', self.config.GetString('foo.bar')) | 130 | self.assertEqual("valll", self.config.GetString("foo.bar")) |
| 133 | config = self.get_config() | 131 | config = self.get_config() |
| 134 | self.assertEqual('valll', config.GetString('foo.bar')) | 132 | self.assertEqual("valll", config.GetString("foo.bar")) |
| 135 | 133 | ||
| 136 | # Delete the value. | 134 | # Delete the value. |
| 137 | self.config.SetString('foo.bar', None) | 135 | self.config.SetString("foo.bar", None) |
| 138 | self.assertIsNone(self.config.GetString('foo.bar')) | 136 | self.assertIsNone(self.config.GetString("foo.bar")) |
| 139 | config = self.get_config() | 137 | config = self.get_config() |
| 140 | self.assertIsNone(config.GetString('foo.bar')) | 138 | self.assertIsNone(config.GetString("foo.bar")) |
| 141 | 139 | ||
| 142 | def test_SetBoolean(self): | 140 | def test_SetBoolean(self): |
| 143 | """Test SetBoolean behavior.""" | 141 | """Test SetBoolean behavior.""" |
| 144 | # Set a true value. | 142 | # Set a true value. |
| 145 | self.assertIsNone(self.config.GetBoolean('foo.bar')) | 143 | self.assertIsNone(self.config.GetBoolean("foo.bar")) |
| 146 | for val in (True, 1): | 144 | for val in (True, 1): |
| 147 | self.config.SetBoolean('foo.bar', val) | 145 | self.config.SetBoolean("foo.bar", val) |
| 148 | self.assertTrue(self.config.GetBoolean('foo.bar')) | 146 | self.assertTrue(self.config.GetBoolean("foo.bar")) |
| 149 | 147 | ||
| 150 | # Make sure the value was actually written out. | 148 | # Make sure the value was actually written out. |
| 151 | config = self.get_config() | 149 | config = self.get_config() |
| 152 | self.assertTrue(config.GetBoolean('foo.bar')) | 150 | self.assertTrue(config.GetBoolean("foo.bar")) |
| 153 | self.assertEqual('true', config.GetString('foo.bar')) | 151 | self.assertEqual("true", config.GetString("foo.bar")) |
| 154 | 152 | ||
| 155 | # Set a false value. | 153 | # Set a false value. |
| 156 | for val in (False, 0): | 154 | for val in (False, 0): |
| 157 | self.config.SetBoolean('foo.bar', val) | 155 | self.config.SetBoolean("foo.bar", val) |
| 158 | self.assertFalse(self.config.GetBoolean('foo.bar')) | 156 | self.assertFalse(self.config.GetBoolean("foo.bar")) |
| 159 | 157 | ||
| 160 | # Make sure the value was actually written out. | 158 | # Make sure the value was actually written out. |
| 161 | config = self.get_config() | 159 | config = self.get_config() |
| 162 | self.assertFalse(config.GetBoolean('foo.bar')) | 160 | self.assertFalse(config.GetBoolean("foo.bar")) |
| 163 | self.assertEqual('false', config.GetString('foo.bar')) | 161 | self.assertEqual("false", config.GetString("foo.bar")) |
| 164 | 162 | ||
| 165 | # Delete the value. | 163 | # Delete the value. |
| 166 | self.config.SetBoolean('foo.bar', None) | 164 | self.config.SetBoolean("foo.bar", None) |
| 167 | self.assertIsNone(self.config.GetBoolean('foo.bar')) | 165 | self.assertIsNone(self.config.GetBoolean("foo.bar")) |
| 168 | config = self.get_config() | 166 | config = self.get_config() |
| 169 | self.assertIsNone(config.GetBoolean('foo.bar')) | 167 | self.assertIsNone(config.GetBoolean("foo.bar")) |
| 170 | 168 | ||
| 171 | def test_GetSyncAnalysisStateData(self): | 169 | def test_GetSyncAnalysisStateData(self): |
| 172 | """Test config entries with a sync state analysis data.""" | 170 | """Test config entries with a sync state analysis data.""" |
| 173 | superproject_logging_data = {} | 171 | superproject_logging_data = {} |
| 174 | superproject_logging_data['test'] = False | 172 | superproject_logging_data["test"] = False |
| 175 | options = type('options', (object,), {})() | 173 | options = type("options", (object,), {})() |
| 176 | options.verbose = 'true' | 174 | options.verbose = "true" |
| 177 | options.mp_update = 'false' | 175 | options.mp_update = "false" |
| 178 | TESTS = ( | 176 | TESTS = ( |
| 179 | ('superproject.test', 'false'), | 177 | ("superproject.test", "false"), |
| 180 | ('options.verbose', 'true'), | 178 | ("options.verbose", "true"), |
| 181 | ('options.mpupdate', 'false'), | 179 | ("options.mpupdate", "false"), |
| 182 | ('main.version', '1'), | 180 | ("main.version", "1"), |
| 183 | ) | 181 | ) |
| 184 | self.config.UpdateSyncAnalysisState(options, superproject_logging_data) | 182 | self.config.UpdateSyncAnalysisState(options, superproject_logging_data) |
| 185 | sync_data = self.config.GetSyncAnalysisStateData() | 183 | sync_data = self.config.GetSyncAnalysisStateData() |
| 186 | for key, value in TESTS: | 184 | for key, value in TESTS: |
| 187 | self.assertEqual(sync_data[f'{git_config.SYNC_STATE_PREFIX}{key}'], value) | 185 | self.assertEqual( |
| 188 | self.assertTrue(sync_data[f'{git_config.SYNC_STATE_PREFIX}main.synctime']) | 186 | sync_data[f"{git_config.SYNC_STATE_PREFIX}{key}"], value |
| 187 | ) | ||
| 188 | self.assertTrue( | ||
| 189 | sync_data[f"{git_config.SYNC_STATE_PREFIX}main.synctime"] | ||
| 190 | ) | ||
diff --git a/tests/test_git_superproject.py b/tests/test_git_superproject.py index b9b597a6..eb542c60 100644 --- a/tests/test_git_superproject.py +++ b/tests/test_git_superproject.py | |||
| @@ -28,297 +28,369 @@ from test_manifest_xml import sort_attributes | |||
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | class SuperprojectTestCase(unittest.TestCase): | 30 | class SuperprojectTestCase(unittest.TestCase): |
| 31 | """TestCase for the Superproject module.""" | 31 | """TestCase for the Superproject module.""" |
| 32 | 32 | ||
| 33 | PARENT_SID_KEY = 'GIT_TRACE2_PARENT_SID' | 33 | PARENT_SID_KEY = "GIT_TRACE2_PARENT_SID" |
| 34 | PARENT_SID_VALUE = 'parent_sid' | 34 | PARENT_SID_VALUE = "parent_sid" |
| 35 | SELF_SID_REGEX = r'repo-\d+T\d+Z-.*' | 35 | SELF_SID_REGEX = r"repo-\d+T\d+Z-.*" |
| 36 | FULL_SID_REGEX = r'^%s/%s' % (PARENT_SID_VALUE, SELF_SID_REGEX) | 36 | FULL_SID_REGEX = r"^%s/%s" % (PARENT_SID_VALUE, SELF_SID_REGEX) |
| 37 | 37 | ||
| 38 | def setUp(self): | 38 | def setUp(self): |
| 39 | """Set up superproject every time.""" | 39 | """Set up superproject every time.""" |
| 40 | self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests') | 40 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests") |
| 41 | self.tempdir = self.tempdirobj.name | 41 | self.tempdir = self.tempdirobj.name |
| 42 | self.repodir = os.path.join(self.tempdir, '.repo') | 42 | self.repodir = os.path.join(self.tempdir, ".repo") |
| 43 | self.manifest_file = os.path.join( | 43 | self.manifest_file = os.path.join( |
| 44 | self.repodir, manifest_xml.MANIFEST_FILE_NAME) | 44 | self.repodir, manifest_xml.MANIFEST_FILE_NAME |
| 45 | os.mkdir(self.repodir) | 45 | ) |
| 46 | self.platform = platform.system().lower() | 46 | os.mkdir(self.repodir) |
| 47 | 47 | self.platform = platform.system().lower() | |
| 48 | # By default we initialize with the expected case where | 48 | |
| 49 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). | 49 | # By default we initialize with the expected case where |
| 50 | env = { | 50 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). |
| 51 | self.PARENT_SID_KEY: self.PARENT_SID_VALUE, | 51 | env = { |
| 52 | } | 52 | self.PARENT_SID_KEY: self.PARENT_SID_VALUE, |
| 53 | self.git_event_log = git_trace2_event_log.EventLog(env=env) | 53 | } |
| 54 | 54 | self.git_event_log = git_trace2_event_log.EventLog(env=env) | |
| 55 | # The manifest parsing really wants a git repo currently. | 55 | |
| 56 | gitdir = os.path.join(self.repodir, 'manifests.git') | 56 | # The manifest parsing really wants a git repo currently. |
| 57 | os.mkdir(gitdir) | 57 | gitdir = os.path.join(self.repodir, "manifests.git") |
| 58 | with open(os.path.join(gitdir, 'config'), 'w') as fp: | 58 | os.mkdir(gitdir) |
| 59 | fp.write("""[remote "origin"] | 59 | with open(os.path.join(gitdir, "config"), "w") as fp: |
| 60 | fp.write( | ||
| 61 | """[remote "origin"] | ||
| 60 | url = https://localhost:0/manifest | 62 | url = https://localhost:0/manifest |
| 61 | """) | 63 | """ |
| 64 | ) | ||
| 62 | 65 | ||
| 63 | manifest = self.getXmlManifest(""" | 66 | manifest = self.getXmlManifest( |
| 67 | """ | ||
| 64 | <manifest> | 68 | <manifest> |
| 65 | <remote name="default-remote" fetch="http://localhost" /> | 69 | <remote name="default-remote" fetch="http://localhost" /> |
| 66 | <default remote="default-remote" revision="refs/heads/main" /> | 70 | <default remote="default-remote" revision="refs/heads/main" /> |
| 67 | <superproject name="superproject"/> | 71 | <superproject name="superproject"/> |
| 68 | <project path="art" name="platform/art" groups="notdefault,platform-""" + self.platform + """ | 72 | <project path="art" name="platform/art" groups="notdefault,platform-""" |
| 73 | + self.platform | ||
| 74 | + """ | ||
| 69 | " /></manifest> | 75 | " /></manifest> |
| 70 | """) | 76 | """ |
| 71 | self._superproject = git_superproject.Superproject( | 77 | ) |
| 72 | manifest, name='superproject', | 78 | self._superproject = git_superproject.Superproject( |
| 73 | remote=manifest.remotes.get('default-remote').ToRemoteSpec('superproject'), | 79 | manifest, |
| 74 | revision='refs/heads/main') | 80 | name="superproject", |
| 75 | 81 | remote=manifest.remotes.get("default-remote").ToRemoteSpec( | |
| 76 | def tearDown(self): | 82 | "superproject" |
| 77 | """Tear down superproject every time.""" | 83 | ), |
| 78 | self.tempdirobj.cleanup() | 84 | revision="refs/heads/main", |
| 79 | 85 | ) | |
| 80 | def getXmlManifest(self, data): | 86 | |
| 81 | """Helper to initialize a manifest for testing.""" | 87 | def tearDown(self): |
| 82 | with open(self.manifest_file, 'w') as fp: | 88 | """Tear down superproject every time.""" |
| 83 | fp.write(data) | 89 | self.tempdirobj.cleanup() |
| 84 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) | 90 | |
| 85 | 91 | def getXmlManifest(self, data): | |
| 86 | def verifyCommonKeys(self, log_entry, expected_event_name, full_sid=True): | 92 | """Helper to initialize a manifest for testing.""" |
| 87 | """Helper function to verify common event log keys.""" | 93 | with open(self.manifest_file, "w") as fp: |
| 88 | self.assertIn('event', log_entry) | 94 | fp.write(data) |
| 89 | self.assertIn('sid', log_entry) | 95 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) |
| 90 | self.assertIn('thread', log_entry) | 96 | |
| 91 | self.assertIn('time', log_entry) | 97 | def verifyCommonKeys(self, log_entry, expected_event_name, full_sid=True): |
| 92 | 98 | """Helper function to verify common event log keys.""" | |
| 93 | # Do basic data format validation. | 99 | self.assertIn("event", log_entry) |
| 94 | self.assertEqual(expected_event_name, log_entry['event']) | 100 | self.assertIn("sid", log_entry) |
| 95 | if full_sid: | 101 | self.assertIn("thread", log_entry) |
| 96 | self.assertRegex(log_entry['sid'], self.FULL_SID_REGEX) | 102 | self.assertIn("time", log_entry) |
| 97 | else: | 103 | |
| 98 | self.assertRegex(log_entry['sid'], self.SELF_SID_REGEX) | 104 | # Do basic data format validation. |
| 99 | self.assertRegex(log_entry['time'], r'^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$') | 105 | self.assertEqual(expected_event_name, log_entry["event"]) |
| 100 | 106 | if full_sid: | |
| 101 | def readLog(self, log_path): | 107 | self.assertRegex(log_entry["sid"], self.FULL_SID_REGEX) |
| 102 | """Helper function to read log data into a list.""" | 108 | else: |
| 103 | log_data = [] | 109 | self.assertRegex(log_entry["sid"], self.SELF_SID_REGEX) |
| 104 | with open(log_path, mode='rb') as f: | 110 | self.assertRegex(log_entry["time"], r"^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$") |
| 105 | for line in f: | 111 | |
| 106 | log_data.append(json.loads(line)) | 112 | def readLog(self, log_path): |
| 107 | return log_data | 113 | """Helper function to read log data into a list.""" |
| 108 | 114 | log_data = [] | |
| 109 | def verifyErrorEvent(self): | 115 | with open(log_path, mode="rb") as f: |
| 110 | """Helper to verify that error event is written.""" | 116 | for line in f: |
| 111 | 117 | log_data.append(json.loads(line)) | |
| 112 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | 118 | return log_data |
| 113 | log_path = self.git_event_log.Write(path=tempdir) | 119 | |
| 114 | self.log_data = self.readLog(log_path) | 120 | def verifyErrorEvent(self): |
| 115 | 121 | """Helper to verify that error event is written.""" | |
| 116 | self.assertEqual(len(self.log_data), 2) | 122 | |
| 117 | error_event = self.log_data[1] | 123 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: |
| 118 | self.verifyCommonKeys(self.log_data[0], expected_event_name='version') | 124 | log_path = self.git_event_log.Write(path=tempdir) |
| 119 | self.verifyCommonKeys(error_event, expected_event_name='error') | 125 | self.log_data = self.readLog(log_path) |
| 120 | # Check for 'error' event specific fields. | 126 | |
| 121 | self.assertIn('msg', error_event) | 127 | self.assertEqual(len(self.log_data), 2) |
| 122 | self.assertIn('fmt', error_event) | 128 | error_event = self.log_data[1] |
| 123 | 129 | self.verifyCommonKeys(self.log_data[0], expected_event_name="version") | |
| 124 | def test_superproject_get_superproject_no_superproject(self): | 130 | self.verifyCommonKeys(error_event, expected_event_name="error") |
| 125 | """Test with no url.""" | 131 | # Check for 'error' event specific fields. |
| 126 | manifest = self.getXmlManifest(""" | 132 | self.assertIn("msg", error_event) |
| 133 | self.assertIn("fmt", error_event) | ||
| 134 | |||
| 135 | def test_superproject_get_superproject_no_superproject(self): | ||
| 136 | """Test with no url.""" | ||
| 137 | manifest = self.getXmlManifest( | ||
| 138 | """ | ||
| 127 | <manifest> | 139 | <manifest> |
| 128 | </manifest> | 140 | </manifest> |
| 129 | """) | 141 | """ |
| 130 | self.assertIsNone(manifest.superproject) | 142 | ) |
| 131 | 143 | self.assertIsNone(manifest.superproject) | |
| 132 | def test_superproject_get_superproject_invalid_url(self): | 144 | |
| 133 | """Test with an invalid url.""" | 145 | def test_superproject_get_superproject_invalid_url(self): |
| 134 | manifest = self.getXmlManifest(""" | 146 | """Test with an invalid url.""" |
| 147 | manifest = self.getXmlManifest( | ||
| 148 | """ | ||
| 135 | <manifest> | 149 | <manifest> |
| 136 | <remote name="test-remote" fetch="localhost" /> | 150 | <remote name="test-remote" fetch="localhost" /> |
| 137 | <default remote="test-remote" revision="refs/heads/main" /> | 151 | <default remote="test-remote" revision="refs/heads/main" /> |
| 138 | <superproject name="superproject"/> | 152 | <superproject name="superproject"/> |
| 139 | </manifest> | 153 | </manifest> |
| 140 | """) | 154 | """ |
| 141 | superproject = git_superproject.Superproject( | 155 | ) |
| 142 | manifest, name='superproject', | 156 | superproject = git_superproject.Superproject( |
| 143 | remote=manifest.remotes.get('test-remote').ToRemoteSpec('superproject'), | 157 | manifest, |
| 144 | revision='refs/heads/main') | 158 | name="superproject", |
| 145 | sync_result = superproject.Sync(self.git_event_log) | 159 | remote=manifest.remotes.get("test-remote").ToRemoteSpec( |
| 146 | self.assertFalse(sync_result.success) | 160 | "superproject" |
| 147 | self.assertTrue(sync_result.fatal) | 161 | ), |
| 148 | 162 | revision="refs/heads/main", | |
| 149 | def test_superproject_get_superproject_invalid_branch(self): | 163 | ) |
| 150 | """Test with an invalid branch.""" | 164 | sync_result = superproject.Sync(self.git_event_log) |
| 151 | manifest = self.getXmlManifest(""" | 165 | self.assertFalse(sync_result.success) |
| 166 | self.assertTrue(sync_result.fatal) | ||
| 167 | |||
| 168 | def test_superproject_get_superproject_invalid_branch(self): | ||
| 169 | """Test with an invalid branch.""" | ||
| 170 | manifest = self.getXmlManifest( | ||
| 171 | """ | ||
| 152 | <manifest> | 172 | <manifest> |
| 153 | <remote name="test-remote" fetch="localhost" /> | 173 | <remote name="test-remote" fetch="localhost" /> |
| 154 | <default remote="test-remote" revision="refs/heads/main" /> | 174 | <default remote="test-remote" revision="refs/heads/main" /> |
| 155 | <superproject name="superproject"/> | 175 | <superproject name="superproject"/> |
| 156 | </manifest> | 176 | </manifest> |
| 157 | """) | 177 | """ |
| 158 | self._superproject = git_superproject.Superproject( | 178 | ) |
| 159 | manifest, name='superproject', | 179 | self._superproject = git_superproject.Superproject( |
| 160 | remote=manifest.remotes.get('test-remote').ToRemoteSpec('superproject'), | 180 | manifest, |
| 161 | revision='refs/heads/main') | 181 | name="superproject", |
| 162 | with mock.patch.object(self._superproject, '_branch', 'junk'): | 182 | remote=manifest.remotes.get("test-remote").ToRemoteSpec( |
| 163 | sync_result = self._superproject.Sync(self.git_event_log) | 183 | "superproject" |
| 164 | self.assertFalse(sync_result.success) | 184 | ), |
| 165 | self.assertTrue(sync_result.fatal) | 185 | revision="refs/heads/main", |
| 166 | self.verifyErrorEvent() | 186 | ) |
| 167 | 187 | with mock.patch.object(self._superproject, "_branch", "junk"): | |
| 168 | def test_superproject_get_superproject_mock_init(self): | 188 | sync_result = self._superproject.Sync(self.git_event_log) |
| 169 | """Test with _Init failing.""" | 189 | self.assertFalse(sync_result.success) |
| 170 | with mock.patch.object(self._superproject, '_Init', return_value=False): | 190 | self.assertTrue(sync_result.fatal) |
| 171 | sync_result = self._superproject.Sync(self.git_event_log) | 191 | self.verifyErrorEvent() |
| 172 | self.assertFalse(sync_result.success) | 192 | |
| 173 | self.assertTrue(sync_result.fatal) | 193 | def test_superproject_get_superproject_mock_init(self): |
| 174 | 194 | """Test with _Init failing.""" | |
| 175 | def test_superproject_get_superproject_mock_fetch(self): | 195 | with mock.patch.object(self._superproject, "_Init", return_value=False): |
| 176 | """Test with _Fetch failing.""" | 196 | sync_result = self._superproject.Sync(self.git_event_log) |
| 177 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 197 | self.assertFalse(sync_result.success) |
| 178 | os.mkdir(self._superproject._superproject_path) | 198 | self.assertTrue(sync_result.fatal) |
| 179 | with mock.patch.object(self._superproject, '_Fetch', return_value=False): | 199 | |
| 180 | sync_result = self._superproject.Sync(self.git_event_log) | 200 | def test_superproject_get_superproject_mock_fetch(self): |
| 181 | self.assertFalse(sync_result.success) | 201 | """Test with _Fetch failing.""" |
| 182 | self.assertTrue(sync_result.fatal) | 202 | with mock.patch.object(self._superproject, "_Init", return_value=True): |
| 183 | 203 | os.mkdir(self._superproject._superproject_path) | |
| 184 | def test_superproject_get_all_project_commit_ids_mock_ls_tree(self): | 204 | with mock.patch.object( |
| 185 | """Test with LsTree being a mock.""" | 205 | self._superproject, "_Fetch", return_value=False |
| 186 | data = ('120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00' | 206 | ): |
| 187 | '160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' | 207 | sync_result = self._superproject.Sync(self.git_event_log) |
| 188 | '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00' | 208 | self.assertFalse(sync_result.success) |
| 189 | '120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00' | 209 | self.assertTrue(sync_result.fatal) |
| 190 | '160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00') | 210 | |
| 191 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 211 | def test_superproject_get_all_project_commit_ids_mock_ls_tree(self): |
| 192 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): | 212 | """Test with LsTree being a mock.""" |
| 193 | with mock.patch.object(self._superproject, '_LsTree', return_value=data): | 213 | data = ( |
| 194 | commit_ids_result = self._superproject._GetAllProjectsCommitIds() | 214 | "120000 blob 158258bdf146f159218e2b90f8b699c4d85b5804\tAndroid.bp\x00" |
| 195 | self.assertEqual(commit_ids_result.commit_ids, { | 215 | "160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00" |
| 196 | 'art': '2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea', | 216 | "160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00" |
| 197 | 'bootable/recovery': 'e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06', | 217 | "120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00" |
| 198 | 'build/bazel': 'ade9b7a0d874e25fff4bf2552488825c6f111928' | 218 | "160000 commit ade9b7a0d874e25fff4bf2552488825c6f111928\tbuild/bazel\x00" |
| 199 | }) | 219 | ) |
| 200 | self.assertFalse(commit_ids_result.fatal) | 220 | with mock.patch.object(self._superproject, "_Init", return_value=True): |
| 201 | 221 | with mock.patch.object( | |
| 202 | def test_superproject_write_manifest_file(self): | 222 | self._superproject, "_Fetch", return_value=True |
| 203 | """Test with writing manifest to a file after setting revisionId.""" | 223 | ): |
| 204 | self.assertEqual(len(self._superproject._manifest.projects), 1) | 224 | with mock.patch.object( |
| 205 | project = self._superproject._manifest.projects[0] | 225 | self._superproject, "_LsTree", return_value=data |
| 206 | project.SetRevisionId('ABCDEF') | 226 | ): |
| 207 | # Create temporary directory so that it can write the file. | 227 | commit_ids_result = ( |
| 208 | os.mkdir(self._superproject._superproject_path) | 228 | self._superproject._GetAllProjectsCommitIds() |
| 209 | manifest_path = self._superproject._WriteManifestFile() | 229 | ) |
| 210 | self.assertIsNotNone(manifest_path) | 230 | self.assertEqual( |
| 211 | with open(manifest_path, 'r') as fp: | 231 | commit_ids_result.commit_ids, |
| 212 | manifest_xml_data = fp.read() | 232 | { |
| 213 | self.assertEqual( | 233 | "art": "2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea", |
| 214 | sort_attributes(manifest_xml_data), | 234 | "bootable/recovery": "e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06", |
| 215 | '<?xml version="1.0" ?><manifest>' | 235 | "build/bazel": "ade9b7a0d874e25fff4bf2552488825c6f111928", |
| 216 | '<remote fetch="http://localhost" name="default-remote"/>' | 236 | }, |
| 217 | '<default remote="default-remote" revision="refs/heads/main"/>' | 237 | ) |
| 218 | '<project groups="notdefault,platform-' + self.platform + '" ' | 238 | self.assertFalse(commit_ids_result.fatal) |
| 219 | 'name="platform/art" path="art" revision="ABCDEF" upstream="refs/heads/main"/>' | 239 | |
| 220 | '<superproject name="superproject"/>' | 240 | def test_superproject_write_manifest_file(self): |
| 221 | '</manifest>') | 241 | """Test with writing manifest to a file after setting revisionId.""" |
| 222 | 242 | self.assertEqual(len(self._superproject._manifest.projects), 1) | |
| 223 | def test_superproject_update_project_revision_id(self): | 243 | project = self._superproject._manifest.projects[0] |
| 224 | """Test with LsTree being a mock.""" | 244 | project.SetRevisionId("ABCDEF") |
| 225 | self.assertEqual(len(self._superproject._manifest.projects), 1) | 245 | # Create temporary directory so that it can write the file. |
| 226 | projects = self._superproject._manifest.projects | 246 | os.mkdir(self._superproject._superproject_path) |
| 227 | data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' | 247 | manifest_path = self._superproject._WriteManifestFile() |
| 228 | '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00') | 248 | self.assertIsNotNone(manifest_path) |
| 229 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 249 | with open(manifest_path, "r") as fp: |
| 230 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): | ||
| 231 | with mock.patch.object(self._superproject, | ||
| 232 | '_LsTree', | ||
| 233 | return_value=data): | ||
| 234 | # Create temporary directory so that it can write the file. | ||
| 235 | os.mkdir(self._superproject._superproject_path) | ||
| 236 | update_result = self._superproject.UpdateProjectsRevisionId(projects, self.git_event_log) | ||
| 237 | self.assertIsNotNone(update_result.manifest_path) | ||
| 238 | self.assertFalse(update_result.fatal) | ||
| 239 | with open(update_result.manifest_path, 'r') as fp: | ||
| 240 | manifest_xml_data = fp.read() | 250 | manifest_xml_data = fp.read() |
| 241 | self.assertEqual( | 251 | self.assertEqual( |
| 242 | sort_attributes(manifest_xml_data), | 252 | sort_attributes(manifest_xml_data), |
| 243 | '<?xml version="1.0" ?><manifest>' | 253 | '<?xml version="1.0" ?><manifest>' |
| 244 | '<remote fetch="http://localhost" name="default-remote"/>' | 254 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 245 | '<default remote="default-remote" revision="refs/heads/main"/>' | 255 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 246 | '<project groups="notdefault,platform-' + self.platform + '" ' | 256 | '<project groups="notdefault,platform-' + self.platform + '" ' |
| 247 | 'name="platform/art" path="art" ' | 257 | 'name="platform/art" path="art" revision="ABCDEF" upstream="refs/heads/main"/>' |
| 248 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | 258 | '<superproject name="superproject"/>' |
| 249 | '<superproject name="superproject"/>' | 259 | "</manifest>", |
| 250 | '</manifest>') | 260 | ) |
| 251 | 261 | ||
| 252 | def test_superproject_update_project_revision_id_no_superproject_tag(self): | 262 | def test_superproject_update_project_revision_id(self): |
| 253 | """Test update of commit ids of a manifest without superproject tag.""" | 263 | """Test with LsTree being a mock.""" |
| 254 | manifest = self.getXmlManifest(""" | 264 | self.assertEqual(len(self._superproject._manifest.projects), 1) |
| 265 | projects = self._superproject._manifest.projects | ||
| 266 | data = ( | ||
| 267 | "160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00" | ||
| 268 | "160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tbootable/recovery\x00" | ||
| 269 | ) | ||
| 270 | with mock.patch.object(self._superproject, "_Init", return_value=True): | ||
| 271 | with mock.patch.object( | ||
| 272 | self._superproject, "_Fetch", return_value=True | ||
| 273 | ): | ||
| 274 | with mock.patch.object( | ||
| 275 | self._superproject, "_LsTree", return_value=data | ||
| 276 | ): | ||
| 277 | # Create temporary directory so that it can write the file. | ||
| 278 | os.mkdir(self._superproject._superproject_path) | ||
| 279 | update_result = self._superproject.UpdateProjectsRevisionId( | ||
| 280 | projects, self.git_event_log | ||
| 281 | ) | ||
| 282 | self.assertIsNotNone(update_result.manifest_path) | ||
| 283 | self.assertFalse(update_result.fatal) | ||
| 284 | with open(update_result.manifest_path, "r") as fp: | ||
| 285 | manifest_xml_data = fp.read() | ||
| 286 | self.assertEqual( | ||
| 287 | sort_attributes(manifest_xml_data), | ||
| 288 | '<?xml version="1.0" ?><manifest>' | ||
| 289 | '<remote fetch="http://localhost" name="default-remote"/>' | ||
| 290 | '<default remote="default-remote" revision="refs/heads/main"/>' | ||
| 291 | '<project groups="notdefault,platform-' | ||
| 292 | + self.platform | ||
| 293 | + '" ' | ||
| 294 | 'name="platform/art" path="art" ' | ||
| 295 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | ||
| 296 | '<superproject name="superproject"/>' | ||
| 297 | "</manifest>", | ||
| 298 | ) | ||
| 299 | |||
| 300 | def test_superproject_update_project_revision_id_no_superproject_tag(self): | ||
| 301 | """Test update of commit ids of a manifest without superproject tag.""" | ||
| 302 | manifest = self.getXmlManifest( | ||
| 303 | """ | ||
| 255 | <manifest> | 304 | <manifest> |
| 256 | <remote name="default-remote" fetch="http://localhost" /> | 305 | <remote name="default-remote" fetch="http://localhost" /> |
| 257 | <default remote="default-remote" revision="refs/heads/main" /> | 306 | <default remote="default-remote" revision="refs/heads/main" /> |
| 258 | <project name="test-name"/> | 307 | <project name="test-name"/> |
| 259 | </manifest> | 308 | </manifest> |
| 260 | """) | 309 | """ |
| 261 | self.maxDiff = None | 310 | ) |
| 262 | self.assertIsNone(manifest.superproject) | 311 | self.maxDiff = None |
| 263 | self.assertEqual( | 312 | self.assertIsNone(manifest.superproject) |
| 264 | sort_attributes(manifest.ToXml().toxml()), | 313 | self.assertEqual( |
| 265 | '<?xml version="1.0" ?><manifest>' | 314 | sort_attributes(manifest.ToXml().toxml()), |
| 266 | '<remote fetch="http://localhost" name="default-remote"/>' | 315 | '<?xml version="1.0" ?><manifest>' |
| 267 | '<default remote="default-remote" revision="refs/heads/main"/>' | 316 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 268 | '<project name="test-name"/>' | 317 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 269 | '</manifest>') | 318 | '<project name="test-name"/>' |
| 270 | 319 | "</manifest>", | |
| 271 | def test_superproject_update_project_revision_id_from_local_manifest_group(self): | 320 | ) |
| 272 | """Test update of commit ids of a manifest that have local manifest no superproject group.""" | 321 | |
| 273 | local_group = manifest_xml.LOCAL_MANIFEST_GROUP_PREFIX + ':local' | 322 | def test_superproject_update_project_revision_id_from_local_manifest_group( |
| 274 | manifest = self.getXmlManifest(""" | 323 | self, |
| 324 | ): | ||
| 325 | """Test update of commit ids of a manifest that have local manifest no superproject group.""" | ||
| 326 | local_group = manifest_xml.LOCAL_MANIFEST_GROUP_PREFIX + ":local" | ||
| 327 | manifest = self.getXmlManifest( | ||
| 328 | """ | ||
| 275 | <manifest> | 329 | <manifest> |
| 276 | <remote name="default-remote" fetch="http://localhost" /> | 330 | <remote name="default-remote" fetch="http://localhost" /> |
| 277 | <remote name="goog" fetch="http://localhost2" /> | 331 | <remote name="goog" fetch="http://localhost2" /> |
| 278 | <default remote="default-remote" revision="refs/heads/main" /> | 332 | <default remote="default-remote" revision="refs/heads/main" /> |
| 279 | <superproject name="superproject"/> | 333 | <superproject name="superproject"/> |
| 280 | <project path="vendor/x" name="platform/vendor/x" remote="goog" | 334 | <project path="vendor/x" name="platform/vendor/x" remote="goog" |
| 281 | groups=\"""" + local_group + """ | 335 | groups=\"""" |
| 336 | + local_group | ||
| 337 | + """ | ||
| 282 | " revision="master-with-vendor" clone-depth="1" /> | 338 | " revision="master-with-vendor" clone-depth="1" /> |
| 283 | <project path="art" name="platform/art" groups="notdefault,platform-""" + self.platform + """ | 339 | <project path="art" name="platform/art" groups="notdefault,platform-""" |
| 340 | + self.platform | ||
| 341 | + """ | ||
| 284 | " /></manifest> | 342 | " /></manifest> |
| 285 | """) | 343 | """ |
| 286 | self.maxDiff = None | 344 | ) |
| 287 | self._superproject = git_superproject.Superproject( | 345 | self.maxDiff = None |
| 288 | manifest, name='superproject', | 346 | self._superproject = git_superproject.Superproject( |
| 289 | remote=manifest.remotes.get('default-remote').ToRemoteSpec('superproject'), | 347 | manifest, |
| 290 | revision='refs/heads/main') | 348 | name="superproject", |
| 291 | self.assertEqual(len(self._superproject._manifest.projects), 2) | 349 | remote=manifest.remotes.get("default-remote").ToRemoteSpec( |
| 292 | projects = self._superproject._manifest.projects | 350 | "superproject" |
| 293 | data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00') | 351 | ), |
| 294 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 352 | revision="refs/heads/main", |
| 295 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): | 353 | ) |
| 296 | with mock.patch.object(self._superproject, | 354 | self.assertEqual(len(self._superproject._manifest.projects), 2) |
| 297 | '_LsTree', | 355 | projects = self._superproject._manifest.projects |
| 298 | return_value=data): | 356 | data = "160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00" |
| 299 | # Create temporary directory so that it can write the file. | 357 | with mock.patch.object(self._superproject, "_Init", return_value=True): |
| 300 | os.mkdir(self._superproject._superproject_path) | 358 | with mock.patch.object( |
| 301 | update_result = self._superproject.UpdateProjectsRevisionId(projects, self.git_event_log) | 359 | self._superproject, "_Fetch", return_value=True |
| 302 | self.assertIsNotNone(update_result.manifest_path) | 360 | ): |
| 303 | self.assertFalse(update_result.fatal) | 361 | with mock.patch.object( |
| 304 | with open(update_result.manifest_path, 'r') as fp: | 362 | self._superproject, "_LsTree", return_value=data |
| 305 | manifest_xml_data = fp.read() | 363 | ): |
| 306 | # Verify platform/vendor/x's project revision hasn't changed. | 364 | # Create temporary directory so that it can write the file. |
| 307 | self.assertEqual( | 365 | os.mkdir(self._superproject._superproject_path) |
| 308 | sort_attributes(manifest_xml_data), | 366 | update_result = self._superproject.UpdateProjectsRevisionId( |
| 309 | '<?xml version="1.0" ?><manifest>' | 367 | projects, self.git_event_log |
| 310 | '<remote fetch="http://localhost" name="default-remote"/>' | 368 | ) |
| 311 | '<remote fetch="http://localhost2" name="goog"/>' | 369 | self.assertIsNotNone(update_result.manifest_path) |
| 312 | '<default remote="default-remote" revision="refs/heads/main"/>' | 370 | self.assertFalse(update_result.fatal) |
| 313 | '<project groups="notdefault,platform-' + self.platform + '" ' | 371 | with open(update_result.manifest_path, "r") as fp: |
| 314 | 'name="platform/art" path="art" ' | 372 | manifest_xml_data = fp.read() |
| 315 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | 373 | # Verify platform/vendor/x's project revision hasn't |
| 316 | '<superproject name="superproject"/>' | 374 | # changed. |
| 317 | '</manifest>') | 375 | self.assertEqual( |
| 318 | 376 | sort_attributes(manifest_xml_data), | |
| 319 | def test_superproject_update_project_revision_id_with_pinned_manifest(self): | 377 | '<?xml version="1.0" ?><manifest>' |
| 320 | """Test update of commit ids of a pinned manifest.""" | 378 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 321 | manifest = self.getXmlManifest(""" | 379 | '<remote fetch="http://localhost2" name="goog"/>' |
| 380 | '<default remote="default-remote" revision="refs/heads/main"/>' | ||
| 381 | '<project groups="notdefault,platform-' | ||
| 382 | + self.platform | ||
| 383 | + '" ' | ||
| 384 | 'name="platform/art" path="art" ' | ||
| 385 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | ||
| 386 | '<superproject name="superproject"/>' | ||
| 387 | "</manifest>", | ||
| 388 | ) | ||
| 389 | |||
| 390 | def test_superproject_update_project_revision_id_with_pinned_manifest(self): | ||
| 391 | """Test update of commit ids of a pinned manifest.""" | ||
| 392 | manifest = self.getXmlManifest( | ||
| 393 | """ | ||
| 322 | <manifest> | 394 | <manifest> |
| 323 | <remote name="default-remote" fetch="http://localhost" /> | 395 | <remote name="default-remote" fetch="http://localhost" /> |
| 324 | <default remote="default-remote" revision="refs/heads/main" /> | 396 | <default remote="default-remote" revision="refs/heads/main" /> |
| @@ -326,80 +398,132 @@ class SuperprojectTestCase(unittest.TestCase): | |||
| 326 | <project path="vendor/x" name="platform/vendor/x" revision="" /> | 398 | <project path="vendor/x" name="platform/vendor/x" revision="" /> |
| 327 | <project path="vendor/y" name="platform/vendor/y" | 399 | <project path="vendor/y" name="platform/vendor/y" |
| 328 | revision="52d3c9f7c107839ece2319d077de0cd922aa9d8f" /> | 400 | revision="52d3c9f7c107839ece2319d077de0cd922aa9d8f" /> |
| 329 | <project path="art" name="platform/art" groups="notdefault,platform-""" + self.platform + """ | 401 | <project path="art" name="platform/art" groups="notdefault,platform-""" |
| 402 | + self.platform | ||
| 403 | + """ | ||
| 330 | " /></manifest> | 404 | " /></manifest> |
| 331 | """) | 405 | """ |
| 332 | self.maxDiff = None | 406 | ) |
| 333 | self._superproject = git_superproject.Superproject( | 407 | self.maxDiff = None |
| 334 | manifest, name='superproject', | 408 | self._superproject = git_superproject.Superproject( |
| 335 | remote=manifest.remotes.get('default-remote').ToRemoteSpec('superproject'), | 409 | manifest, |
| 336 | revision='refs/heads/main') | 410 | name="superproject", |
| 337 | self.assertEqual(len(self._superproject._manifest.projects), 3) | 411 | remote=manifest.remotes.get("default-remote").ToRemoteSpec( |
| 338 | projects = self._superproject._manifest.projects | 412 | "superproject" |
| 339 | data = ('160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00' | 413 | ), |
| 340 | '160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tvendor/x\x00') | 414 | revision="refs/heads/main", |
| 341 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 415 | ) |
| 342 | with mock.patch.object(self._superproject, '_Fetch', return_value=True): | 416 | self.assertEqual(len(self._superproject._manifest.projects), 3) |
| 343 | with mock.patch.object(self._superproject, | 417 | projects = self._superproject._manifest.projects |
| 344 | '_LsTree', | 418 | data = ( |
| 345 | return_value=data): | 419 | "160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00" |
| 346 | # Create temporary directory so that it can write the file. | 420 | "160000 commit e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06\tvendor/x\x00" |
| 347 | os.mkdir(self._superproject._superproject_path) | 421 | ) |
| 348 | update_result = self._superproject.UpdateProjectsRevisionId(projects, self.git_event_log) | 422 | with mock.patch.object(self._superproject, "_Init", return_value=True): |
| 349 | self.assertIsNotNone(update_result.manifest_path) | 423 | with mock.patch.object( |
| 350 | self.assertFalse(update_result.fatal) | 424 | self._superproject, "_Fetch", return_value=True |
| 351 | with open(update_result.manifest_path, 'r') as fp: | 425 | ): |
| 352 | manifest_xml_data = fp.read() | 426 | with mock.patch.object( |
| 353 | # Verify platform/vendor/x's project revision hasn't changed. | 427 | self._superproject, "_LsTree", return_value=data |
| 354 | self.assertEqual( | 428 | ): |
| 355 | sort_attributes(manifest_xml_data), | 429 | # Create temporary directory so that it can write the file. |
| 356 | '<?xml version="1.0" ?><manifest>' | 430 | os.mkdir(self._superproject._superproject_path) |
| 357 | '<remote fetch="http://localhost" name="default-remote"/>' | 431 | update_result = self._superproject.UpdateProjectsRevisionId( |
| 358 | '<default remote="default-remote" revision="refs/heads/main"/>' | 432 | projects, self.git_event_log |
| 359 | '<project groups="notdefault,platform-' + self.platform + '" ' | 433 | ) |
| 360 | 'name="platform/art" path="art" ' | 434 | self.assertIsNotNone(update_result.manifest_path) |
| 361 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | 435 | self.assertFalse(update_result.fatal) |
| 362 | '<project name="platform/vendor/x" path="vendor/x" ' | 436 | with open(update_result.manifest_path, "r") as fp: |
| 363 | 'revision="e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06" upstream="refs/heads/main"/>' | 437 | manifest_xml_data = fp.read() |
| 364 | '<project name="platform/vendor/y" path="vendor/y" ' | 438 | # Verify platform/vendor/x's project revision hasn't |
| 365 | 'revision="52d3c9f7c107839ece2319d077de0cd922aa9d8f"/>' | 439 | # changed. |
| 366 | '<superproject name="superproject"/>' | 440 | self.assertEqual( |
| 367 | '</manifest>') | 441 | sort_attributes(manifest_xml_data), |
| 368 | 442 | '<?xml version="1.0" ?><manifest>' | |
| 369 | def test_Fetch(self): | 443 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 370 | manifest = self.getXmlManifest(""" | 444 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 445 | '<project groups="notdefault,platform-' | ||
| 446 | + self.platform | ||
| 447 | + '" ' | ||
| 448 | 'name="platform/art" path="art" ' | ||
| 449 | 'revision="2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea" upstream="refs/heads/main"/>' | ||
| 450 | '<project name="platform/vendor/x" path="vendor/x" ' | ||
| 451 | 'revision="e9d25da64d8d365dbba7c8ee00fe8c4473fe9a06" upstream="refs/heads/main"/>' | ||
| 452 | '<project name="platform/vendor/y" path="vendor/y" ' | ||
| 453 | 'revision="52d3c9f7c107839ece2319d077de0cd922aa9d8f"/>' | ||
| 454 | '<superproject name="superproject"/>' | ||
| 455 | "</manifest>", | ||
| 456 | ) | ||
| 457 | |||
| 458 | def test_Fetch(self): | ||
| 459 | manifest = self.getXmlManifest( | ||
| 460 | """ | ||
| 371 | <manifest> | 461 | <manifest> |
| 372 | <remote name="default-remote" fetch="http://localhost" /> | 462 | <remote name="default-remote" fetch="http://localhost" /> |
| 373 | <default remote="default-remote" revision="refs/heads/main" /> | 463 | <default remote="default-remote" revision="refs/heads/main" /> |
| 374 | <superproject name="superproject"/> | 464 | <superproject name="superproject"/> |
| 375 | " /></manifest> | 465 | " /></manifest> |
| 376 | """) | 466 | """ |
| 377 | self.maxDiff = None | 467 | ) |
| 378 | self._superproject = git_superproject.Superproject( | 468 | self.maxDiff = None |
| 379 | manifest, name='superproject', | 469 | self._superproject = git_superproject.Superproject( |
| 380 | remote=manifest.remotes.get('default-remote').ToRemoteSpec('superproject'), | 470 | manifest, |
| 381 | revision='refs/heads/main') | 471 | name="superproject", |
| 382 | os.mkdir(self._superproject._superproject_path) | 472 | remote=manifest.remotes.get("default-remote").ToRemoteSpec( |
| 383 | os.mkdir(self._superproject._work_git) | 473 | "superproject" |
| 384 | with mock.patch.object(self._superproject, '_Init', return_value=True): | 474 | ), |
| 385 | with mock.patch('git_superproject.GitCommand', autospec=True) as mock_git_command: | 475 | revision="refs/heads/main", |
| 386 | with mock.patch('git_superproject.GitRefs.get', autospec=True) as mock_git_refs: | 476 | ) |
| 387 | instance = mock_git_command.return_value | 477 | os.mkdir(self._superproject._superproject_path) |
| 388 | instance.Wait.return_value = 0 | 478 | os.mkdir(self._superproject._work_git) |
| 389 | mock_git_refs.side_effect = ['', '1234'] | 479 | with mock.patch.object(self._superproject, "_Init", return_value=True): |
| 390 | 480 | with mock.patch( | |
| 391 | self.assertTrue(self._superproject._Fetch()) | 481 | "git_superproject.GitCommand", autospec=True |
| 392 | self.assertEqual(mock_git_command.call_args.args,(None, [ | 482 | ) as mock_git_command: |
| 393 | 'fetch', 'http://localhost/superproject', '--depth', '1', | 483 | with mock.patch( |
| 394 | '--force', '--no-tags', '--filter', 'blob:none', | 484 | "git_superproject.GitRefs.get", autospec=True |
| 395 | 'refs/heads/main:refs/heads/main' | 485 | ) as mock_git_refs: |
| 396 | ])) | 486 | instance = mock_git_command.return_value |
| 397 | 487 | instance.Wait.return_value = 0 | |
| 398 | # If branch for revision exists, set as --negotiation-tip. | 488 | mock_git_refs.side_effect = ["", "1234"] |
| 399 | self.assertTrue(self._superproject._Fetch()) | 489 | |
| 400 | self.assertEqual(mock_git_command.call_args.args,(None, [ | 490 | self.assertTrue(self._superproject._Fetch()) |
| 401 | 'fetch', 'http://localhost/superproject', '--depth', '1', | 491 | self.assertEqual( |
| 402 | '--force', '--no-tags', '--filter', 'blob:none', | 492 | mock_git_command.call_args.args, |
| 403 | '--negotiation-tip', '1234', | 493 | ( |
| 404 | 'refs/heads/main:refs/heads/main' | 494 | None, |
| 405 | ])) | 495 | [ |
| 496 | "fetch", | ||
| 497 | "http://localhost/superproject", | ||
| 498 | "--depth", | ||
| 499 | "1", | ||
| 500 | "--force", | ||
| 501 | "--no-tags", | ||
| 502 | "--filter", | ||
| 503 | "blob:none", | ||
| 504 | "refs/heads/main:refs/heads/main", | ||
| 505 | ], | ||
| 506 | ), | ||
| 507 | ) | ||
| 508 | |||
| 509 | # If branch for revision exists, set as --negotiation-tip. | ||
| 510 | self.assertTrue(self._superproject._Fetch()) | ||
| 511 | self.assertEqual( | ||
| 512 | mock_git_command.call_args.args, | ||
| 513 | ( | ||
| 514 | None, | ||
| 515 | [ | ||
| 516 | "fetch", | ||
| 517 | "http://localhost/superproject", | ||
| 518 | "--depth", | ||
| 519 | "1", | ||
| 520 | "--force", | ||
| 521 | "--no-tags", | ||
| 522 | "--filter", | ||
| 523 | "blob:none", | ||
| 524 | "--negotiation-tip", | ||
| 525 | "1234", | ||
| 526 | "refs/heads/main:refs/heads/main", | ||
| 527 | ], | ||
| 528 | ), | ||
| 529 | ) | ||
diff --git a/tests/test_git_trace2_event_log.py b/tests/test_git_trace2_event_log.py index 7e7dfb7a..a6078d38 100644 --- a/tests/test_git_trace2_event_log.py +++ b/tests/test_git_trace2_event_log.py | |||
| @@ -27,361 +27,382 @@ import platform_utils | |||
| 27 | 27 | ||
| 28 | 28 | ||
| 29 | def serverLoggingThread(socket_path, server_ready, received_traces): | 29 | def serverLoggingThread(socket_path, server_ready, received_traces): |
| 30 | """Helper function to receive logs over a Unix domain socket. | 30 | """Helper function to receive logs over a Unix domain socket. |
| 31 | |||
| 32 | Appends received messages on the provided socket and appends to received_traces. | ||
| 33 | |||
| 34 | Args: | ||
| 35 | socket_path: path to a Unix domain socket on which to listen for traces | ||
| 36 | server_ready: a threading.Condition used to signal to the caller that this thread is ready to | ||
| 37 | accept connections | ||
| 38 | received_traces: a list to which received traces will be appended (after decoding to a utf-8 | ||
| 39 | string). | ||
| 40 | """ | ||
| 41 | platform_utils.remove(socket_path, missing_ok=True) | ||
| 42 | data = b'' | ||
| 43 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: | ||
| 44 | sock.bind(socket_path) | ||
| 45 | sock.listen(0) | ||
| 46 | with server_ready: | ||
| 47 | server_ready.notify() | ||
| 48 | with sock.accept()[0] as conn: | ||
| 49 | while True: | ||
| 50 | recved = conn.recv(4096) | ||
| 51 | if not recved: | ||
| 52 | break | ||
| 53 | data += recved | ||
| 54 | received_traces.extend(data.decode('utf-8').splitlines()) | ||
| 55 | 31 | ||
| 32 | Appends received messages on the provided socket and appends to | ||
| 33 | received_traces. | ||
| 56 | 34 | ||
| 57 | class EventLogTestCase(unittest.TestCase): | 35 | Args: |
| 58 | """TestCase for the EventLog module.""" | 36 | socket_path: path to a Unix domain socket on which to listen for traces |
| 59 | 37 | server_ready: a threading.Condition used to signal to the caller that | |
| 60 | PARENT_SID_KEY = 'GIT_TRACE2_PARENT_SID' | 38 | this thread is ready to accept connections |
| 61 | PARENT_SID_VALUE = 'parent_sid' | 39 | received_traces: a list to which received traces will be appended (after |
| 62 | SELF_SID_REGEX = r'repo-\d+T\d+Z-.*' | 40 | decoding to a utf-8 string). |
| 63 | FULL_SID_REGEX = r'^%s/%s' % (PARENT_SID_VALUE, SELF_SID_REGEX) | ||
| 64 | |||
| 65 | def setUp(self): | ||
| 66 | """Load the event_log module every time.""" | ||
| 67 | self._event_log_module = None | ||
| 68 | # By default we initialize with the expected case where | ||
| 69 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). | ||
| 70 | env = { | ||
| 71 | self.PARENT_SID_KEY: self.PARENT_SID_VALUE, | ||
| 72 | } | ||
| 73 | self._event_log_module = git_trace2_event_log.EventLog(env=env) | ||
| 74 | self._log_data = None | ||
| 75 | |||
| 76 | def verifyCommonKeys(self, log_entry, expected_event_name=None, full_sid=True): | ||
| 77 | """Helper function to verify common event log keys.""" | ||
| 78 | self.assertIn('event', log_entry) | ||
| 79 | self.assertIn('sid', log_entry) | ||
| 80 | self.assertIn('thread', log_entry) | ||
| 81 | self.assertIn('time', log_entry) | ||
| 82 | |||
| 83 | # Do basic data format validation. | ||
| 84 | if expected_event_name: | ||
| 85 | self.assertEqual(expected_event_name, log_entry['event']) | ||
| 86 | if full_sid: | ||
| 87 | self.assertRegex(log_entry['sid'], self.FULL_SID_REGEX) | ||
| 88 | else: | ||
| 89 | self.assertRegex(log_entry['sid'], self.SELF_SID_REGEX) | ||
| 90 | self.assertRegex(log_entry['time'], r'^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$') | ||
| 91 | |||
| 92 | def readLog(self, log_path): | ||
| 93 | """Helper function to read log data into a list.""" | ||
| 94 | log_data = [] | ||
| 95 | with open(log_path, mode='rb') as f: | ||
| 96 | for line in f: | ||
| 97 | log_data.append(json.loads(line)) | ||
| 98 | return log_data | ||
| 99 | |||
| 100 | def remove_prefix(self, s, prefix): | ||
| 101 | """Return a copy string after removing |prefix| from |s|, if present or the original string.""" | ||
| 102 | if s.startswith(prefix): | ||
| 103 | return s[len(prefix):] | ||
| 104 | else: | ||
| 105 | return s | ||
| 106 | |||
| 107 | def test_initial_state_with_parent_sid(self): | ||
| 108 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent.""" | ||
| 109 | self.assertRegex(self._event_log_module.full_sid, self.FULL_SID_REGEX) | ||
| 110 | |||
| 111 | def test_initial_state_no_parent_sid(self): | ||
| 112 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set.""" | ||
| 113 | # Setup an empty environment dict (no parent sid). | ||
| 114 | self._event_log_module = git_trace2_event_log.EventLog(env={}) | ||
| 115 | self.assertRegex(self._event_log_module.full_sid, self.SELF_SID_REGEX) | ||
| 116 | |||
| 117 | def test_version_event(self): | ||
| 118 | """Test 'version' event data is valid. | ||
| 119 | |||
| 120 | Verify that the 'version' event is written even when no other | ||
| 121 | events are addded. | ||
| 122 | |||
| 123 | Expected event log: | ||
| 124 | <version event> | ||
| 125 | """ | ||
| 126 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 127 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 128 | self._log_data = self.readLog(log_path) | ||
| 129 | |||
| 130 | # A log with no added events should only have the version entry. | ||
| 131 | self.assertEqual(len(self._log_data), 1) | ||
| 132 | version_event = self._log_data[0] | ||
| 133 | self.verifyCommonKeys(version_event, expected_event_name='version') | ||
| 134 | # Check for 'version' event specific fields. | ||
| 135 | self.assertIn('evt', version_event) | ||
| 136 | self.assertIn('exe', version_event) | ||
| 137 | # Verify "evt" version field is a string. | ||
| 138 | self.assertIsInstance(version_event['evt'], str) | ||
| 139 | |||
| 140 | def test_start_event(self): | ||
| 141 | """Test and validate 'start' event data is valid. | ||
| 142 | |||
| 143 | Expected event log: | ||
| 144 | <version event> | ||
| 145 | <start event> | ||
| 146 | """ | ||
| 147 | self._event_log_module.StartEvent() | ||
| 148 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 149 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 150 | self._log_data = self.readLog(log_path) | ||
| 151 | |||
| 152 | self.assertEqual(len(self._log_data), 2) | ||
| 153 | start_event = self._log_data[1] | ||
| 154 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 155 | self.verifyCommonKeys(start_event, expected_event_name='start') | ||
| 156 | # Check for 'start' event specific fields. | ||
| 157 | self.assertIn('argv', start_event) | ||
| 158 | self.assertTrue(isinstance(start_event['argv'], list)) | ||
| 159 | |||
| 160 | def test_exit_event_result_none(self): | ||
| 161 | """Test 'exit' event data is valid when result is None. | ||
| 162 | |||
| 163 | We expect None result to be converted to 0 in the exit event data. | ||
| 164 | |||
| 165 | Expected event log: | ||
| 166 | <version event> | ||
| 167 | <exit event> | ||
| 168 | """ | ||
| 169 | self._event_log_module.ExitEvent(None) | ||
| 170 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 171 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 172 | self._log_data = self.readLog(log_path) | ||
| 173 | |||
| 174 | self.assertEqual(len(self._log_data), 2) | ||
| 175 | exit_event = self._log_data[1] | ||
| 176 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 177 | self.verifyCommonKeys(exit_event, expected_event_name='exit') | ||
| 178 | # Check for 'exit' event specific fields. | ||
| 179 | self.assertIn('code', exit_event) | ||
| 180 | # 'None' result should convert to 0 (successful) return code. | ||
| 181 | self.assertEqual(exit_event['code'], 0) | ||
| 182 | |||
| 183 | def test_exit_event_result_integer(self): | ||
| 184 | """Test 'exit' event data is valid when result is an integer. | ||
| 185 | |||
| 186 | Expected event log: | ||
| 187 | <version event> | ||
| 188 | <exit event> | ||
| 189 | """ | ||
| 190 | self._event_log_module.ExitEvent(2) | ||
| 191 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 192 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 193 | self._log_data = self.readLog(log_path) | ||
| 194 | |||
| 195 | self.assertEqual(len(self._log_data), 2) | ||
| 196 | exit_event = self._log_data[1] | ||
| 197 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 198 | self.verifyCommonKeys(exit_event, expected_event_name='exit') | ||
| 199 | # Check for 'exit' event specific fields. | ||
| 200 | self.assertIn('code', exit_event) | ||
| 201 | self.assertEqual(exit_event['code'], 2) | ||
| 202 | |||
| 203 | def test_command_event(self): | ||
| 204 | """Test and validate 'command' event data is valid. | ||
| 205 | |||
| 206 | Expected event log: | ||
| 207 | <version event> | ||
| 208 | <command event> | ||
| 209 | """ | ||
| 210 | name = 'repo' | ||
| 211 | subcommands = ['init' 'this'] | ||
| 212 | self._event_log_module.CommandEvent(name='repo', subcommands=subcommands) | ||
| 213 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 214 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 215 | self._log_data = self.readLog(log_path) | ||
| 216 | |||
| 217 | self.assertEqual(len(self._log_data), 2) | ||
| 218 | command_event = self._log_data[1] | ||
| 219 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 220 | self.verifyCommonKeys(command_event, expected_event_name='command') | ||
| 221 | # Check for 'command' event specific fields. | ||
| 222 | self.assertIn('name', command_event) | ||
| 223 | self.assertIn('subcommands', command_event) | ||
| 224 | self.assertEqual(command_event['name'], name) | ||
| 225 | self.assertEqual(command_event['subcommands'], subcommands) | ||
| 226 | |||
| 227 | def test_def_params_event_repo_config(self): | ||
| 228 | """Test 'def_params' event data outputs only repo config keys. | ||
| 229 | |||
| 230 | Expected event log: | ||
| 231 | <version event> | ||
| 232 | <def_param event> | ||
| 233 | <def_param event> | ||
| 234 | """ | 41 | """ |
| 235 | config = { | 42 | platform_utils.remove(socket_path, missing_ok=True) |
| 236 | 'git.foo': 'bar', | 43 | data = b"" |
| 237 | 'repo.partialclone': 'true', | 44 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: |
| 238 | 'repo.partialclonefilter': 'blob:none', | 45 | sock.bind(socket_path) |
| 239 | } | 46 | sock.listen(0) |
| 240 | self._event_log_module.DefParamRepoEvents(config) | ||
| 241 | |||
| 242 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 243 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 244 | self._log_data = self.readLog(log_path) | ||
| 245 | |||
| 246 | self.assertEqual(len(self._log_data), 3) | ||
| 247 | def_param_events = self._log_data[1:] | ||
| 248 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 249 | |||
| 250 | for event in def_param_events: | ||
| 251 | self.verifyCommonKeys(event, expected_event_name='def_param') | ||
| 252 | # Check for 'def_param' event specific fields. | ||
| 253 | self.assertIn('param', event) | ||
| 254 | self.assertIn('value', event) | ||
| 255 | self.assertTrue(event['param'].startswith('repo.')) | ||
| 256 | |||
| 257 | def test_def_params_event_no_repo_config(self): | ||
| 258 | """Test 'def_params' event data won't output non-repo config keys. | ||
| 259 | |||
| 260 | Expected event log: | ||
| 261 | <version event> | ||
| 262 | """ | ||
| 263 | config = { | ||
| 264 | 'git.foo': 'bar', | ||
| 265 | 'git.core.foo2': 'baz', | ||
| 266 | } | ||
| 267 | self._event_log_module.DefParamRepoEvents(config) | ||
| 268 | |||
| 269 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 270 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 271 | self._log_data = self.readLog(log_path) | ||
| 272 | |||
| 273 | self.assertEqual(len(self._log_data), 1) | ||
| 274 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 275 | |||
| 276 | def test_data_event_config(self): | ||
| 277 | """Test 'data' event data outputs all config keys. | ||
| 278 | |||
| 279 | Expected event log: | ||
| 280 | <version event> | ||
| 281 | <data event> | ||
| 282 | <data event> | ||
| 283 | """ | ||
| 284 | config = { | ||
| 285 | 'git.foo': 'bar', | ||
| 286 | 'repo.partialclone': 'false', | ||
| 287 | 'repo.syncstate.superproject.hassuperprojecttag': 'true', | ||
| 288 | 'repo.syncstate.superproject.sys.argv': ['--', 'sync', 'protobuf'], | ||
| 289 | } | ||
| 290 | prefix_value = 'prefix' | ||
| 291 | self._event_log_module.LogDataConfigEvents(config, prefix_value) | ||
| 292 | |||
| 293 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 294 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 295 | self._log_data = self.readLog(log_path) | ||
| 296 | |||
| 297 | self.assertEqual(len(self._log_data), 5) | ||
| 298 | data_events = self._log_data[1:] | ||
| 299 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 300 | |||
| 301 | for event in data_events: | ||
| 302 | self.verifyCommonKeys(event) | ||
| 303 | # Check for 'data' event specific fields. | ||
| 304 | self.assertIn('key', event) | ||
| 305 | self.assertIn('value', event) | ||
| 306 | key = event['key'] | ||
| 307 | key = self.remove_prefix(key, f'{prefix_value}/') | ||
| 308 | value = event['value'] | ||
| 309 | self.assertEqual(self._event_log_module.GetDataEventName(value), event['event']) | ||
| 310 | self.assertTrue(key in config and value == config[key]) | ||
| 311 | |||
| 312 | def test_error_event(self): | ||
| 313 | """Test and validate 'error' event data is valid. | ||
| 314 | |||
| 315 | Expected event log: | ||
| 316 | <version event> | ||
| 317 | <error event> | ||
| 318 | """ | ||
| 319 | msg = 'invalid option: --cahced' | ||
| 320 | fmt = 'invalid option: %s' | ||
| 321 | self._event_log_module.ErrorEvent(msg, fmt) | ||
| 322 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 323 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 324 | self._log_data = self.readLog(log_path) | ||
| 325 | |||
| 326 | self.assertEqual(len(self._log_data), 2) | ||
| 327 | error_event = self._log_data[1] | ||
| 328 | self.verifyCommonKeys(self._log_data[0], expected_event_name='version') | ||
| 329 | self.verifyCommonKeys(error_event, expected_event_name='error') | ||
| 330 | # Check for 'error' event specific fields. | ||
| 331 | self.assertIn('msg', error_event) | ||
| 332 | self.assertIn('fmt', error_event) | ||
| 333 | self.assertEqual(error_event['msg'], msg) | ||
| 334 | self.assertEqual(error_event['fmt'], fmt) | ||
| 335 | |||
| 336 | def test_write_with_filename(self): | ||
| 337 | """Test Write() with a path to a file exits with None.""" | ||
| 338 | self.assertIsNone(self._event_log_module.Write(path='path/to/file')) | ||
| 339 | |||
| 340 | def test_write_with_git_config(self): | ||
| 341 | """Test Write() uses the git config path when 'git config' call succeeds.""" | ||
| 342 | with tempfile.TemporaryDirectory(prefix='event_log_tests') as tempdir: | ||
| 343 | with mock.patch.object(self._event_log_module, | ||
| 344 | '_GetEventTargetPath', return_value=tempdir): | ||
| 345 | self.assertEqual(os.path.dirname(self._event_log_module.Write()), tempdir) | ||
| 346 | |||
| 347 | def test_write_no_git_config(self): | ||
| 348 | """Test Write() with no git config variable present exits with None.""" | ||
| 349 | with mock.patch.object(self._event_log_module, | ||
| 350 | '_GetEventTargetPath', return_value=None): | ||
| 351 | self.assertIsNone(self._event_log_module.Write()) | ||
| 352 | |||
| 353 | def test_write_non_string(self): | ||
| 354 | """Test Write() with non-string type for |path| throws TypeError.""" | ||
| 355 | with self.assertRaises(TypeError): | ||
| 356 | self._event_log_module.Write(path=1234) | ||
| 357 | |||
| 358 | def test_write_socket(self): | ||
| 359 | """Test Write() with Unix domain socket for |path| and validate received traces.""" | ||
| 360 | received_traces = [] | ||
| 361 | with tempfile.TemporaryDirectory(prefix='test_server_sockets') as tempdir: | ||
| 362 | socket_path = os.path.join(tempdir, "server.sock") | ||
| 363 | server_ready = threading.Condition() | ||
| 364 | # Start "server" listening on Unix domain socket at socket_path. | ||
| 365 | try: | ||
| 366 | server_thread = threading.Thread( | ||
| 367 | target=serverLoggingThread, | ||
| 368 | args=(socket_path, server_ready, received_traces)) | ||
| 369 | server_thread.start() | ||
| 370 | |||
| 371 | with server_ready: | 47 | with server_ready: |
| 372 | server_ready.wait(timeout=120) | 48 | server_ready.notify() |
| 49 | with sock.accept()[0] as conn: | ||
| 50 | while True: | ||
| 51 | recved = conn.recv(4096) | ||
| 52 | if not recved: | ||
| 53 | break | ||
| 54 | data += recved | ||
| 55 | received_traces.extend(data.decode("utf-8").splitlines()) | ||
| 56 | |||
| 373 | 57 | ||
| 58 | class EventLogTestCase(unittest.TestCase): | ||
| 59 | """TestCase for the EventLog module.""" | ||
| 60 | |||
| 61 | PARENT_SID_KEY = "GIT_TRACE2_PARENT_SID" | ||
| 62 | PARENT_SID_VALUE = "parent_sid" | ||
| 63 | SELF_SID_REGEX = r"repo-\d+T\d+Z-.*" | ||
| 64 | FULL_SID_REGEX = r"^%s/%s" % (PARENT_SID_VALUE, SELF_SID_REGEX) | ||
| 65 | |||
| 66 | def setUp(self): | ||
| 67 | """Load the event_log module every time.""" | ||
| 68 | self._event_log_module = None | ||
| 69 | # By default we initialize with the expected case where | ||
| 70 | # repo launches us (so GIT_TRACE2_PARENT_SID is set). | ||
| 71 | env = { | ||
| 72 | self.PARENT_SID_KEY: self.PARENT_SID_VALUE, | ||
| 73 | } | ||
| 74 | self._event_log_module = git_trace2_event_log.EventLog(env=env) | ||
| 75 | self._log_data = None | ||
| 76 | |||
| 77 | def verifyCommonKeys( | ||
| 78 | self, log_entry, expected_event_name=None, full_sid=True | ||
| 79 | ): | ||
| 80 | """Helper function to verify common event log keys.""" | ||
| 81 | self.assertIn("event", log_entry) | ||
| 82 | self.assertIn("sid", log_entry) | ||
| 83 | self.assertIn("thread", log_entry) | ||
| 84 | self.assertIn("time", log_entry) | ||
| 85 | |||
| 86 | # Do basic data format validation. | ||
| 87 | if expected_event_name: | ||
| 88 | self.assertEqual(expected_event_name, log_entry["event"]) | ||
| 89 | if full_sid: | ||
| 90 | self.assertRegex(log_entry["sid"], self.FULL_SID_REGEX) | ||
| 91 | else: | ||
| 92 | self.assertRegex(log_entry["sid"], self.SELF_SID_REGEX) | ||
| 93 | self.assertRegex(log_entry["time"], r"^\d+-\d+-\d+T\d+:\d+:\d+\.\d+Z$") | ||
| 94 | |||
| 95 | def readLog(self, log_path): | ||
| 96 | """Helper function to read log data into a list.""" | ||
| 97 | log_data = [] | ||
| 98 | with open(log_path, mode="rb") as f: | ||
| 99 | for line in f: | ||
| 100 | log_data.append(json.loads(line)) | ||
| 101 | return log_data | ||
| 102 | |||
| 103 | def remove_prefix(self, s, prefix): | ||
| 104 | """Return a copy string after removing |prefix| from |s|, if present or | ||
| 105 | the original string.""" | ||
| 106 | if s.startswith(prefix): | ||
| 107 | return s[len(prefix) :] | ||
| 108 | else: | ||
| 109 | return s | ||
| 110 | |||
| 111 | def test_initial_state_with_parent_sid(self): | ||
| 112 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is set by parent.""" | ||
| 113 | self.assertRegex(self._event_log_module.full_sid, self.FULL_SID_REGEX) | ||
| 114 | |||
| 115 | def test_initial_state_no_parent_sid(self): | ||
| 116 | """Test initial state when 'GIT_TRACE2_PARENT_SID' is not set.""" | ||
| 117 | # Setup an empty environment dict (no parent sid). | ||
| 118 | self._event_log_module = git_trace2_event_log.EventLog(env={}) | ||
| 119 | self.assertRegex(self._event_log_module.full_sid, self.SELF_SID_REGEX) | ||
| 120 | |||
| 121 | def test_version_event(self): | ||
| 122 | """Test 'version' event data is valid. | ||
| 123 | |||
| 124 | Verify that the 'version' event is written even when no other | ||
| 125 | events are addded. | ||
| 126 | |||
| 127 | Expected event log: | ||
| 128 | <version event> | ||
| 129 | """ | ||
| 130 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 131 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 132 | self._log_data = self.readLog(log_path) | ||
| 133 | |||
| 134 | # A log with no added events should only have the version entry. | ||
| 135 | self.assertEqual(len(self._log_data), 1) | ||
| 136 | version_event = self._log_data[0] | ||
| 137 | self.verifyCommonKeys(version_event, expected_event_name="version") | ||
| 138 | # Check for 'version' event specific fields. | ||
| 139 | self.assertIn("evt", version_event) | ||
| 140 | self.assertIn("exe", version_event) | ||
| 141 | # Verify "evt" version field is a string. | ||
| 142 | self.assertIsInstance(version_event["evt"], str) | ||
| 143 | |||
| 144 | def test_start_event(self): | ||
| 145 | """Test and validate 'start' event data is valid. | ||
| 146 | |||
| 147 | Expected event log: | ||
| 148 | <version event> | ||
| 149 | <start event> | ||
| 150 | """ | ||
| 374 | self._event_log_module.StartEvent() | 151 | self._event_log_module.StartEvent() |
| 375 | path = self._event_log_module.Write(path=f'af_unix:{socket_path}') | 152 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: |
| 376 | finally: | 153 | log_path = self._event_log_module.Write(path=tempdir) |
| 377 | server_thread.join(timeout=5) | 154 | self._log_data = self.readLog(log_path) |
| 378 | 155 | ||
| 379 | self.assertEqual(path, f'af_unix:stream:{socket_path}') | 156 | self.assertEqual(len(self._log_data), 2) |
| 380 | self.assertEqual(len(received_traces), 2) | 157 | start_event = self._log_data[1] |
| 381 | version_event = json.loads(received_traces[0]) | 158 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") |
| 382 | start_event = json.loads(received_traces[1]) | 159 | self.verifyCommonKeys(start_event, expected_event_name="start") |
| 383 | self.verifyCommonKeys(version_event, expected_event_name='version') | 160 | # Check for 'start' event specific fields. |
| 384 | self.verifyCommonKeys(start_event, expected_event_name='start') | 161 | self.assertIn("argv", start_event) |
| 385 | # Check for 'start' event specific fields. | 162 | self.assertTrue(isinstance(start_event["argv"], list)) |
| 386 | self.assertIn('argv', start_event) | 163 | |
| 387 | self.assertIsInstance(start_event['argv'], list) | 164 | def test_exit_event_result_none(self): |
| 165 | """Test 'exit' event data is valid when result is None. | ||
| 166 | |||
| 167 | We expect None result to be converted to 0 in the exit event data. | ||
| 168 | |||
| 169 | Expected event log: | ||
| 170 | <version event> | ||
| 171 | <exit event> | ||
| 172 | """ | ||
| 173 | self._event_log_module.ExitEvent(None) | ||
| 174 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 175 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 176 | self._log_data = self.readLog(log_path) | ||
| 177 | |||
| 178 | self.assertEqual(len(self._log_data), 2) | ||
| 179 | exit_event = self._log_data[1] | ||
| 180 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 181 | self.verifyCommonKeys(exit_event, expected_event_name="exit") | ||
| 182 | # Check for 'exit' event specific fields. | ||
| 183 | self.assertIn("code", exit_event) | ||
| 184 | # 'None' result should convert to 0 (successful) return code. | ||
| 185 | self.assertEqual(exit_event["code"], 0) | ||
| 186 | |||
| 187 | def test_exit_event_result_integer(self): | ||
| 188 | """Test 'exit' event data is valid when result is an integer. | ||
| 189 | |||
| 190 | Expected event log: | ||
| 191 | <version event> | ||
| 192 | <exit event> | ||
| 193 | """ | ||
| 194 | self._event_log_module.ExitEvent(2) | ||
| 195 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 196 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 197 | self._log_data = self.readLog(log_path) | ||
| 198 | |||
| 199 | self.assertEqual(len(self._log_data), 2) | ||
| 200 | exit_event = self._log_data[1] | ||
| 201 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 202 | self.verifyCommonKeys(exit_event, expected_event_name="exit") | ||
| 203 | # Check for 'exit' event specific fields. | ||
| 204 | self.assertIn("code", exit_event) | ||
| 205 | self.assertEqual(exit_event["code"], 2) | ||
| 206 | |||
| 207 | def test_command_event(self): | ||
| 208 | """Test and validate 'command' event data is valid. | ||
| 209 | |||
| 210 | Expected event log: | ||
| 211 | <version event> | ||
| 212 | <command event> | ||
| 213 | """ | ||
| 214 | name = "repo" | ||
| 215 | subcommands = ["init" "this"] | ||
| 216 | self._event_log_module.CommandEvent( | ||
| 217 | name="repo", subcommands=subcommands | ||
| 218 | ) | ||
| 219 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 220 | log_path = self._event_log_module.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="command") | ||
| 227 | # Check for 'command' event specific fields. | ||
| 228 | self.assertIn("name", command_event) | ||
| 229 | self.assertIn("subcommands", command_event) | ||
| 230 | self.assertEqual(command_event["name"], name) | ||
| 231 | self.assertEqual(command_event["subcommands"], subcommands) | ||
| 232 | |||
| 233 | def test_def_params_event_repo_config(self): | ||
| 234 | """Test 'def_params' event data outputs only repo config keys. | ||
| 235 | |||
| 236 | Expected event log: | ||
| 237 | <version event> | ||
| 238 | <def_param event> | ||
| 239 | <def_param event> | ||
| 240 | """ | ||
| 241 | config = { | ||
| 242 | "git.foo": "bar", | ||
| 243 | "repo.partialclone": "true", | ||
| 244 | "repo.partialclonefilter": "blob:none", | ||
| 245 | } | ||
| 246 | self._event_log_module.DefParamRepoEvents(config) | ||
| 247 | |||
| 248 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 249 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 250 | self._log_data = self.readLog(log_path) | ||
| 251 | |||
| 252 | self.assertEqual(len(self._log_data), 3) | ||
| 253 | def_param_events = self._log_data[1:] | ||
| 254 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 255 | |||
| 256 | for event in def_param_events: | ||
| 257 | self.verifyCommonKeys(event, expected_event_name="def_param") | ||
| 258 | # Check for 'def_param' event specific fields. | ||
| 259 | self.assertIn("param", event) | ||
| 260 | self.assertIn("value", event) | ||
| 261 | self.assertTrue(event["param"].startswith("repo.")) | ||
| 262 | |||
| 263 | def test_def_params_event_no_repo_config(self): | ||
| 264 | """Test 'def_params' event data won't output non-repo config keys. | ||
| 265 | |||
| 266 | Expected event log: | ||
| 267 | <version event> | ||
| 268 | """ | ||
| 269 | config = { | ||
| 270 | "git.foo": "bar", | ||
| 271 | "git.core.foo2": "baz", | ||
| 272 | } | ||
| 273 | self._event_log_module.DefParamRepoEvents(config) | ||
| 274 | |||
| 275 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 276 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 277 | self._log_data = self.readLog(log_path) | ||
| 278 | |||
| 279 | self.assertEqual(len(self._log_data), 1) | ||
| 280 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 281 | |||
| 282 | def test_data_event_config(self): | ||
| 283 | """Test 'data' event data outputs all config keys. | ||
| 284 | |||
| 285 | Expected event log: | ||
| 286 | <version event> | ||
| 287 | <data event> | ||
| 288 | <data event> | ||
| 289 | """ | ||
| 290 | config = { | ||
| 291 | "git.foo": "bar", | ||
| 292 | "repo.partialclone": "false", | ||
| 293 | "repo.syncstate.superproject.hassuperprojecttag": "true", | ||
| 294 | "repo.syncstate.superproject.sys.argv": ["--", "sync", "protobuf"], | ||
| 295 | } | ||
| 296 | prefix_value = "prefix" | ||
| 297 | self._event_log_module.LogDataConfigEvents(config, prefix_value) | ||
| 298 | |||
| 299 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 300 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 301 | self._log_data = self.readLog(log_path) | ||
| 302 | |||
| 303 | self.assertEqual(len(self._log_data), 5) | ||
| 304 | data_events = self._log_data[1:] | ||
| 305 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 306 | |||
| 307 | for event in data_events: | ||
| 308 | self.verifyCommonKeys(event) | ||
| 309 | # Check for 'data' event specific fields. | ||
| 310 | self.assertIn("key", event) | ||
| 311 | self.assertIn("value", event) | ||
| 312 | key = event["key"] | ||
| 313 | key = self.remove_prefix(key, f"{prefix_value}/") | ||
| 314 | value = event["value"] | ||
| 315 | self.assertEqual( | ||
| 316 | self._event_log_module.GetDataEventName(value), event["event"] | ||
| 317 | ) | ||
| 318 | self.assertTrue(key in config and value == config[key]) | ||
| 319 | |||
| 320 | def test_error_event(self): | ||
| 321 | """Test and validate 'error' event data is valid. | ||
| 322 | |||
| 323 | Expected event log: | ||
| 324 | <version event> | ||
| 325 | <error event> | ||
| 326 | """ | ||
| 327 | msg = "invalid option: --cahced" | ||
| 328 | fmt = "invalid option: %s" | ||
| 329 | self._event_log_module.ErrorEvent(msg, fmt) | ||
| 330 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 331 | log_path = self._event_log_module.Write(path=tempdir) | ||
| 332 | self._log_data = self.readLog(log_path) | ||
| 333 | |||
| 334 | self.assertEqual(len(self._log_data), 2) | ||
| 335 | error_event = self._log_data[1] | ||
| 336 | self.verifyCommonKeys(self._log_data[0], expected_event_name="version") | ||
| 337 | self.verifyCommonKeys(error_event, expected_event_name="error") | ||
| 338 | # Check for 'error' event specific fields. | ||
| 339 | self.assertIn("msg", error_event) | ||
| 340 | self.assertIn("fmt", error_event) | ||
| 341 | self.assertEqual(error_event["msg"], msg) | ||
| 342 | self.assertEqual(error_event["fmt"], fmt) | ||
| 343 | |||
| 344 | def test_write_with_filename(self): | ||
| 345 | """Test Write() with a path to a file exits with None.""" | ||
| 346 | self.assertIsNone(self._event_log_module.Write(path="path/to/file")) | ||
| 347 | |||
| 348 | def test_write_with_git_config(self): | ||
| 349 | """Test Write() uses the git config path when 'git config' call | ||
| 350 | succeeds.""" | ||
| 351 | with tempfile.TemporaryDirectory(prefix="event_log_tests") as tempdir: | ||
| 352 | with mock.patch.object( | ||
| 353 | self._event_log_module, | ||
| 354 | "_GetEventTargetPath", | ||
| 355 | return_value=tempdir, | ||
| 356 | ): | ||
| 357 | self.assertEqual( | ||
| 358 | os.path.dirname(self._event_log_module.Write()), tempdir | ||
| 359 | ) | ||
| 360 | |||
| 361 | def test_write_no_git_config(self): | ||
| 362 | """Test Write() with no git config variable present exits with None.""" | ||
| 363 | with mock.patch.object( | ||
| 364 | self._event_log_module, "_GetEventTargetPath", return_value=None | ||
| 365 | ): | ||
| 366 | self.assertIsNone(self._event_log_module.Write()) | ||
| 367 | |||
| 368 | def test_write_non_string(self): | ||
| 369 | """Test Write() with non-string type for |path| throws TypeError.""" | ||
| 370 | with self.assertRaises(TypeError): | ||
| 371 | self._event_log_module.Write(path=1234) | ||
| 372 | |||
| 373 | def test_write_socket(self): | ||
| 374 | """Test Write() with Unix domain socket for |path| and validate received | ||
| 375 | traces.""" | ||
| 376 | received_traces = [] | ||
| 377 | with tempfile.TemporaryDirectory( | ||
| 378 | prefix="test_server_sockets" | ||
| 379 | ) as tempdir: | ||
| 380 | socket_path = os.path.join(tempdir, "server.sock") | ||
| 381 | server_ready = threading.Condition() | ||
| 382 | # Start "server" listening on Unix domain socket at socket_path. | ||
| 383 | try: | ||
| 384 | server_thread = threading.Thread( | ||
| 385 | target=serverLoggingThread, | ||
| 386 | args=(socket_path, server_ready, received_traces), | ||
| 387 | ) | ||
| 388 | server_thread.start() | ||
| 389 | |||
| 390 | with server_ready: | ||
| 391 | server_ready.wait(timeout=120) | ||
| 392 | |||
| 393 | self._event_log_module.StartEvent() | ||
| 394 | path = self._event_log_module.Write( | ||
| 395 | path=f"af_unix:{socket_path}" | ||
| 396 | ) | ||
| 397 | finally: | ||
| 398 | server_thread.join(timeout=5) | ||
| 399 | |||
| 400 | self.assertEqual(path, f"af_unix:stream:{socket_path}") | ||
| 401 | self.assertEqual(len(received_traces), 2) | ||
| 402 | version_event = json.loads(received_traces[0]) | ||
| 403 | start_event = json.loads(received_traces[1]) | ||
| 404 | self.verifyCommonKeys(version_event, expected_event_name="version") | ||
| 405 | self.verifyCommonKeys(start_event, expected_event_name="start") | ||
| 406 | # Check for 'start' event specific fields. | ||
| 407 | self.assertIn("argv", start_event) | ||
| 408 | self.assertIsInstance(start_event["argv"], list) | ||
diff --git a/tests/test_hooks.py b/tests/test_hooks.py index 6632b3e5..78277128 100644 --- a/tests/test_hooks.py +++ b/tests/test_hooks.py | |||
| @@ -17,39 +17,38 @@ | |||
| 17 | import hooks | 17 | import hooks |
| 18 | import unittest | 18 | import unittest |
| 19 | 19 | ||
| 20 | |||
| 20 | class RepoHookShebang(unittest.TestCase): | 21 | class RepoHookShebang(unittest.TestCase): |
| 21 | """Check shebang parsing in RepoHook.""" | 22 | """Check shebang parsing in RepoHook.""" |
| 22 | 23 | ||
| 23 | def test_no_shebang(self): | 24 | def test_no_shebang(self): |
| 24 | """Lines w/out shebangs should be rejected.""" | 25 | """Lines w/out shebangs should be rejected.""" |
| 25 | DATA = ( | 26 | DATA = ("", "#\n# foo\n", "# Bad shebang in script\n#!/foo\n") |
| 26 | '', | 27 | for data in DATA: |
| 27 | '#\n# foo\n', | 28 | self.assertIsNone(hooks.RepoHook._ExtractInterpFromShebang(data)) |
| 28 | '# Bad shebang in script\n#!/foo\n' | ||
| 29 | ) | ||
| 30 | for data in DATA: | ||
| 31 | self.assertIsNone(hooks.RepoHook._ExtractInterpFromShebang(data)) | ||
| 32 | 29 | ||
| 33 | def test_direct_interp(self): | 30 | def test_direct_interp(self): |
| 34 | """Lines whose shebang points directly to the interpreter.""" | 31 | """Lines whose shebang points directly to the interpreter.""" |
| 35 | DATA = ( | 32 | DATA = ( |
| 36 | ('#!/foo', '/foo'), | 33 | ("#!/foo", "/foo"), |
| 37 | ('#! /foo', '/foo'), | 34 | ("#! /foo", "/foo"), |
| 38 | ('#!/bin/foo ', '/bin/foo'), | 35 | ("#!/bin/foo ", "/bin/foo"), |
| 39 | ('#! /usr/foo ', '/usr/foo'), | 36 | ("#! /usr/foo ", "/usr/foo"), |
| 40 | ('#! /usr/foo -args', '/usr/foo'), | 37 | ("#! /usr/foo -args", "/usr/foo"), |
| 41 | ) | 38 | ) |
| 42 | for shebang, interp in DATA: | 39 | for shebang, interp in DATA: |
| 43 | self.assertEqual(hooks.RepoHook._ExtractInterpFromShebang(shebang), | 40 | self.assertEqual( |
| 44 | interp) | 41 | hooks.RepoHook._ExtractInterpFromShebang(shebang), interp |
| 42 | ) | ||
| 45 | 43 | ||
| 46 | def test_env_interp(self): | 44 | def test_env_interp(self): |
| 47 | """Lines whose shebang launches through `env`.""" | 45 | """Lines whose shebang launches through `env`.""" |
| 48 | DATA = ( | 46 | DATA = ( |
| 49 | ('#!/usr/bin/env foo', 'foo'), | 47 | ("#!/usr/bin/env foo", "foo"), |
| 50 | ('#!/bin/env foo', 'foo'), | 48 | ("#!/bin/env foo", "foo"), |
| 51 | ('#! /bin/env /bin/foo ', '/bin/foo'), | 49 | ("#! /bin/env /bin/foo ", "/bin/foo"), |
| 52 | ) | 50 | ) |
| 53 | for shebang, interp in DATA: | 51 | for shebang, interp in DATA: |
| 54 | self.assertEqual(hooks.RepoHook._ExtractInterpFromShebang(shebang), | 52 | self.assertEqual( |
| 55 | interp) | 53 | hooks.RepoHook._ExtractInterpFromShebang(shebang), interp |
| 54 | ) | ||
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 3634701f..648acde8 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py | |||
| @@ -27,291 +27,318 @@ import manifest_xml | |||
| 27 | 27 | ||
| 28 | # Invalid paths that we don't want in the filesystem. | 28 | # Invalid paths that we don't want in the filesystem. |
| 29 | INVALID_FS_PATHS = ( | 29 | INVALID_FS_PATHS = ( |
| 30 | '', | 30 | "", |
| 31 | '.', | 31 | ".", |
| 32 | '..', | 32 | "..", |
| 33 | '../', | 33 | "../", |
| 34 | './', | 34 | "./", |
| 35 | './/', | 35 | ".//", |
| 36 | 'foo/', | 36 | "foo/", |
| 37 | './foo', | 37 | "./foo", |
| 38 | '../foo', | 38 | "../foo", |
| 39 | 'foo/./bar', | 39 | "foo/./bar", |
| 40 | 'foo/../../bar', | 40 | "foo/../../bar", |
| 41 | '/foo', | 41 | "/foo", |
| 42 | './../foo', | 42 | "./../foo", |
| 43 | '.git/foo', | 43 | ".git/foo", |
| 44 | # Check case folding. | 44 | # Check case folding. |
| 45 | '.GIT/foo', | 45 | ".GIT/foo", |
| 46 | 'blah/.git/foo', | 46 | "blah/.git/foo", |
| 47 | '.repo/foo', | 47 | ".repo/foo", |
| 48 | '.repoconfig', | 48 | ".repoconfig", |
| 49 | # Block ~ due to 8.3 filenames on Windows filesystems. | 49 | # Block ~ due to 8.3 filenames on Windows filesystems. |
| 50 | '~', | 50 | "~", |
| 51 | 'foo~', | 51 | "foo~", |
| 52 | 'blah/foo~', | 52 | "blah/foo~", |
| 53 | # Block Unicode characters that get normalized out by filesystems. | 53 | # Block Unicode characters that get normalized out by filesystems. |
| 54 | u'foo\u200Cbar', | 54 | "foo\u200Cbar", |
| 55 | # Block newlines. | 55 | # Block newlines. |
| 56 | 'f\n/bar', | 56 | "f\n/bar", |
| 57 | 'f\r/bar', | 57 | "f\r/bar", |
| 58 | ) | 58 | ) |
| 59 | 59 | ||
| 60 | # Make sure platforms that use path separators (e.g. Windows) are also | 60 | # Make sure platforms that use path separators (e.g. Windows) are also |
| 61 | # rejected properly. | 61 | # rejected properly. |
| 62 | if os.path.sep != '/': | 62 | if os.path.sep != "/": |
| 63 | INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS) | 63 | INVALID_FS_PATHS += tuple( |
| 64 | x.replace("/", os.path.sep) for x in INVALID_FS_PATHS | ||
| 65 | ) | ||
| 64 | 66 | ||
| 65 | 67 | ||
| 66 | def sort_attributes(manifest): | 68 | def sort_attributes(manifest): |
| 67 | """Sort the attributes of all elements alphabetically. | 69 | """Sort the attributes of all elements alphabetically. |
| 68 | 70 | ||
| 69 | This is needed because different versions of the toxml() function from | 71 | This is needed because different versions of the toxml() function from |
| 70 | xml.dom.minidom outputs the attributes of elements in different orders. | 72 | xml.dom.minidom outputs the attributes of elements in different orders. |
| 71 | Before Python 3.8 they were output alphabetically, later versions preserve | 73 | Before Python 3.8 they were output alphabetically, later versions preserve |
| 72 | the order specified by the user. | 74 | the order specified by the user. |
| 73 | 75 | ||
| 74 | Args: | 76 | Args: |
| 75 | manifest: String containing an XML manifest. | 77 | manifest: String containing an XML manifest. |
| 76 | 78 | ||
| 77 | Returns: | 79 | Returns: |
| 78 | The XML manifest with the attributes of all elements sorted alphabetically. | 80 | The XML manifest with the attributes of all elements sorted |
| 79 | """ | 81 | alphabetically. |
| 80 | new_manifest = '' | 82 | """ |
| 81 | # This will find every element in the XML manifest, whether they have | 83 | new_manifest = "" |
| 82 | # attributes or not. This simplifies recreating the manifest below. | 84 | # This will find every element in the XML manifest, whether they have |
| 83 | matches = re.findall(r'(<[/?]?[a-z-]+\s*)((?:\S+?="[^"]+"\s*?)*)(\s*[/?]?>)', manifest) | 85 | # attributes or not. This simplifies recreating the manifest below. |
| 84 | for head, attrs, tail in matches: | 86 | matches = re.findall( |
| 85 | m = re.findall(r'\S+?="[^"]+"', attrs) | 87 | r'(<[/?]?[a-z-]+\s*)((?:\S+?="[^"]+"\s*?)*)(\s*[/?]?>)', manifest |
| 86 | new_manifest += head + ' '.join(sorted(m)) + tail | 88 | ) |
| 87 | return new_manifest | 89 | for head, attrs, tail in matches: |
| 90 | m = re.findall(r'\S+?="[^"]+"', attrs) | ||
| 91 | new_manifest += head + " ".join(sorted(m)) + tail | ||
| 92 | return new_manifest | ||
| 88 | 93 | ||
| 89 | 94 | ||
| 90 | class ManifestParseTestCase(unittest.TestCase): | 95 | class ManifestParseTestCase(unittest.TestCase): |
| 91 | """TestCase for parsing manifests.""" | 96 | """TestCase for parsing manifests.""" |
| 92 | 97 | ||
| 93 | def setUp(self): | 98 | def setUp(self): |
| 94 | self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests') | 99 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests") |
| 95 | self.tempdir = self.tempdirobj.name | 100 | self.tempdir = self.tempdirobj.name |
| 96 | self.repodir = os.path.join(self.tempdir, '.repo') | 101 | self.repodir = os.path.join(self.tempdir, ".repo") |
| 97 | self.manifest_dir = os.path.join(self.repodir, 'manifests') | 102 | self.manifest_dir = os.path.join(self.repodir, "manifests") |
| 98 | self.manifest_file = os.path.join( | 103 | self.manifest_file = os.path.join( |
| 99 | self.repodir, manifest_xml.MANIFEST_FILE_NAME) | 104 | self.repodir, manifest_xml.MANIFEST_FILE_NAME |
| 100 | self.local_manifest_dir = os.path.join( | 105 | ) |
| 101 | self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME) | 106 | self.local_manifest_dir = os.path.join( |
| 102 | os.mkdir(self.repodir) | 107 | self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME |
| 103 | os.mkdir(self.manifest_dir) | 108 | ) |
| 104 | 109 | os.mkdir(self.repodir) | |
| 105 | # The manifest parsing really wants a git repo currently. | 110 | os.mkdir(self.manifest_dir) |
| 106 | gitdir = os.path.join(self.repodir, 'manifests.git') | 111 | |
| 107 | os.mkdir(gitdir) | 112 | # The manifest parsing really wants a git repo currently. |
| 108 | with open(os.path.join(gitdir, 'config'), 'w') as fp: | 113 | gitdir = os.path.join(self.repodir, "manifests.git") |
| 109 | fp.write("""[remote "origin"] | 114 | os.mkdir(gitdir) |
| 115 | with open(os.path.join(gitdir, "config"), "w") as fp: | ||
| 116 | fp.write( | ||
| 117 | """[remote "origin"] | ||
| 110 | url = https://localhost:0/manifest | 118 | url = https://localhost:0/manifest |
| 111 | """) | 119 | """ |
| 120 | ) | ||
| 112 | 121 | ||
| 113 | def tearDown(self): | 122 | def tearDown(self): |
| 114 | self.tempdirobj.cleanup() | 123 | self.tempdirobj.cleanup() |
| 115 | 124 | ||
| 116 | def getXmlManifest(self, data): | 125 | def getXmlManifest(self, data): |
| 117 | """Helper to initialize a manifest for testing.""" | 126 | """Helper to initialize a manifest for testing.""" |
| 118 | with open(self.manifest_file, 'w', encoding="utf-8") as fp: | 127 | with open(self.manifest_file, "w", encoding="utf-8") as fp: |
| 119 | fp.write(data) | 128 | fp.write(data) |
| 120 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) | 129 | return manifest_xml.XmlManifest(self.repodir, self.manifest_file) |
| 121 | 130 | ||
| 122 | @staticmethod | 131 | @staticmethod |
| 123 | def encodeXmlAttr(attr): | 132 | def encodeXmlAttr(attr): |
| 124 | """Encode |attr| using XML escape rules.""" | 133 | """Encode |attr| using XML escape rules.""" |
| 125 | return attr.replace('\r', '
').replace('\n', '
') | 134 | return attr.replace("\r", "
").replace("\n", "
") |
| 126 | 135 | ||
| 127 | 136 | ||
| 128 | class ManifestValidateFilePaths(unittest.TestCase): | 137 | class ManifestValidateFilePaths(unittest.TestCase): |
| 129 | """Check _ValidateFilePaths helper. | 138 | """Check _ValidateFilePaths helper. |
| 130 | 139 | ||
| 131 | This doesn't access a real filesystem. | 140 | This doesn't access a real filesystem. |
| 132 | """ | 141 | """ |
| 133 | 142 | ||
| 134 | def check_both(self, *args): | 143 | def check_both(self, *args): |
| 135 | manifest_xml.XmlManifest._ValidateFilePaths('copyfile', *args) | 144 | manifest_xml.XmlManifest._ValidateFilePaths("copyfile", *args) |
| 136 | manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args) | 145 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args) |
| 137 | 146 | ||
| 138 | def test_normal_path(self): | 147 | def test_normal_path(self): |
| 139 | """Make sure good paths are accepted.""" | 148 | """Make sure good paths are accepted.""" |
| 140 | self.check_both('foo', 'bar') | 149 | self.check_both("foo", "bar") |
| 141 | self.check_both('foo/bar', 'bar') | 150 | self.check_both("foo/bar", "bar") |
| 142 | self.check_both('foo', 'bar/bar') | 151 | self.check_both("foo", "bar/bar") |
| 143 | self.check_both('foo/bar', 'bar/bar') | 152 | self.check_both("foo/bar", "bar/bar") |
| 144 | 153 | ||
| 145 | def test_symlink_targets(self): | 154 | def test_symlink_targets(self): |
| 146 | """Some extra checks for symlinks.""" | 155 | """Some extra checks for symlinks.""" |
| 147 | def check(*args): | 156 | |
| 148 | manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args) | 157 | def check(*args): |
| 149 | 158 | manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args) | |
| 150 | # We allow symlinks to end in a slash since we allow them to point to dirs | 159 | |
| 151 | # in general. Technically the slash isn't necessary. | 160 | # We allow symlinks to end in a slash since we allow them to point to |
| 152 | check('foo/', 'bar') | 161 | # dirs in general. Technically the slash isn't necessary. |
| 153 | # We allow a single '.' to get a reference to the project itself. | 162 | check("foo/", "bar") |
| 154 | check('.', 'bar') | 163 | # We allow a single '.' to get a reference to the project itself. |
| 155 | 164 | check(".", "bar") | |
| 156 | def test_bad_paths(self): | 165 | |
| 157 | """Make sure bad paths (src & dest) are rejected.""" | 166 | def test_bad_paths(self): |
| 158 | for path in INVALID_FS_PATHS: | 167 | """Make sure bad paths (src & dest) are rejected.""" |
| 159 | self.assertRaises( | 168 | for path in INVALID_FS_PATHS: |
| 160 | error.ManifestInvalidPathError, self.check_both, path, 'a') | 169 | self.assertRaises( |
| 161 | self.assertRaises( | 170 | error.ManifestInvalidPathError, self.check_both, path, "a" |
| 162 | error.ManifestInvalidPathError, self.check_both, 'a', path) | 171 | ) |
| 172 | self.assertRaises( | ||
| 173 | error.ManifestInvalidPathError, self.check_both, "a", path | ||
| 174 | ) | ||
| 163 | 175 | ||
| 164 | 176 | ||
| 165 | class ValueTests(unittest.TestCase): | 177 | class ValueTests(unittest.TestCase): |
| 166 | """Check utility parsing code.""" | 178 | """Check utility parsing code.""" |
| 167 | 179 | ||
| 168 | def _get_node(self, text): | 180 | def _get_node(self, text): |
| 169 | return xml.dom.minidom.parseString(text).firstChild | 181 | return xml.dom.minidom.parseString(text).firstChild |
| 170 | 182 | ||
| 171 | def test_bool_default(self): | 183 | def test_bool_default(self): |
| 172 | """Check XmlBool default handling.""" | 184 | """Check XmlBool default handling.""" |
| 173 | node = self._get_node('<node/>') | 185 | node = self._get_node("<node/>") |
| 174 | self.assertIsNone(manifest_xml.XmlBool(node, 'a')) | 186 | self.assertIsNone(manifest_xml.XmlBool(node, "a")) |
| 175 | self.assertIsNone(manifest_xml.XmlBool(node, 'a', None)) | 187 | self.assertIsNone(manifest_xml.XmlBool(node, "a", None)) |
| 176 | self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123)) | 188 | self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123)) |
| 177 | 189 | ||
| 178 | node = self._get_node('<node a=""/>') | 190 | node = self._get_node('<node a=""/>') |
| 179 | self.assertIsNone(manifest_xml.XmlBool(node, 'a')) | 191 | self.assertIsNone(manifest_xml.XmlBool(node, "a")) |
| 180 | 192 | ||
| 181 | def test_bool_invalid(self): | 193 | def test_bool_invalid(self): |
| 182 | """Check XmlBool invalid handling.""" | 194 | """Check XmlBool invalid handling.""" |
| 183 | node = self._get_node('<node a="moo"/>') | 195 | node = self._get_node('<node a="moo"/>') |
| 184 | self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123)) | 196 | self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123)) |
| 185 | 197 | ||
| 186 | def test_bool_true(self): | 198 | def test_bool_true(self): |
| 187 | """Check XmlBool true values.""" | 199 | """Check XmlBool true values.""" |
| 188 | for value in ('yes', 'true', '1'): | 200 | for value in ("yes", "true", "1"): |
| 189 | node = self._get_node('<node a="%s"/>' % (value,)) | 201 | node = self._get_node('<node a="%s"/>' % (value,)) |
| 190 | self.assertTrue(manifest_xml.XmlBool(node, 'a')) | 202 | self.assertTrue(manifest_xml.XmlBool(node, "a")) |
| 191 | 203 | ||
| 192 | def test_bool_false(self): | 204 | def test_bool_false(self): |
| 193 | """Check XmlBool false values.""" | 205 | """Check XmlBool false values.""" |
| 194 | for value in ('no', 'false', '0'): | 206 | for value in ("no", "false", "0"): |
| 195 | node = self._get_node('<node a="%s"/>' % (value,)) | 207 | node = self._get_node('<node a="%s"/>' % (value,)) |
| 196 | self.assertFalse(manifest_xml.XmlBool(node, 'a')) | 208 | self.assertFalse(manifest_xml.XmlBool(node, "a")) |
| 197 | 209 | ||
| 198 | def test_int_default(self): | 210 | def test_int_default(self): |
| 199 | """Check XmlInt default handling.""" | 211 | """Check XmlInt default handling.""" |
| 200 | node = self._get_node('<node/>') | 212 | node = self._get_node("<node/>") |
| 201 | self.assertIsNone(manifest_xml.XmlInt(node, 'a')) | 213 | self.assertIsNone(manifest_xml.XmlInt(node, "a")) |
| 202 | self.assertIsNone(manifest_xml.XmlInt(node, 'a', None)) | 214 | self.assertIsNone(manifest_xml.XmlInt(node, "a", None)) |
| 203 | self.assertEqual(123, manifest_xml.XmlInt(node, 'a', 123)) | 215 | self.assertEqual(123, manifest_xml.XmlInt(node, "a", 123)) |
| 204 | 216 | ||
| 205 | node = self._get_node('<node a=""/>') | 217 | node = self._get_node('<node a=""/>') |
| 206 | self.assertIsNone(manifest_xml.XmlInt(node, 'a')) | 218 | self.assertIsNone(manifest_xml.XmlInt(node, "a")) |
| 207 | 219 | ||
| 208 | def test_int_good(self): | 220 | def test_int_good(self): |
| 209 | """Check XmlInt numeric handling.""" | 221 | """Check XmlInt numeric handling.""" |
| 210 | for value in (-1, 0, 1, 50000): | 222 | for value in (-1, 0, 1, 50000): |
| 211 | node = self._get_node('<node a="%s"/>' % (value,)) | 223 | node = self._get_node('<node a="%s"/>' % (value,)) |
| 212 | self.assertEqual(value, manifest_xml.XmlInt(node, 'a')) | 224 | self.assertEqual(value, manifest_xml.XmlInt(node, "a")) |
| 213 | 225 | ||
| 214 | def test_int_invalid(self): | 226 | def test_int_invalid(self): |
| 215 | """Check XmlInt invalid handling.""" | 227 | """Check XmlInt invalid handling.""" |
| 216 | with self.assertRaises(error.ManifestParseError): | 228 | with self.assertRaises(error.ManifestParseError): |
| 217 | node = self._get_node('<node a="xx"/>') | 229 | node = self._get_node('<node a="xx"/>') |
| 218 | manifest_xml.XmlInt(node, 'a') | 230 | manifest_xml.XmlInt(node, "a") |
| 219 | 231 | ||
| 220 | 232 | ||
| 221 | class XmlManifestTests(ManifestParseTestCase): | 233 | class XmlManifestTests(ManifestParseTestCase): |
| 222 | """Check manifest processing.""" | 234 | """Check manifest processing.""" |
| 223 | 235 | ||
| 224 | def test_empty(self): | 236 | def test_empty(self): |
| 225 | """Parse an 'empty' manifest file.""" | 237 | """Parse an 'empty' manifest file.""" |
| 226 | manifest = self.getXmlManifest( | 238 | manifest = self.getXmlManifest( |
| 227 | '<?xml version="1.0" encoding="UTF-8"?>' | 239 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 228 | '<manifest></manifest>') | 240 | ) |
| 229 | self.assertEqual(manifest.remotes, {}) | 241 | self.assertEqual(manifest.remotes, {}) |
| 230 | self.assertEqual(manifest.projects, []) | 242 | self.assertEqual(manifest.projects, []) |
| 231 | 243 | ||
| 232 | def test_link(self): | 244 | def test_link(self): |
| 233 | """Verify Link handling with new names.""" | 245 | """Verify Link handling with new names.""" |
| 234 | manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file) | 246 | manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file) |
| 235 | with open(os.path.join(self.manifest_dir, 'foo.xml'), 'w') as fp: | 247 | with open(os.path.join(self.manifest_dir, "foo.xml"), "w") as fp: |
| 236 | fp.write('<manifest></manifest>') | 248 | fp.write("<manifest></manifest>") |
| 237 | manifest.Link('foo.xml') | 249 | manifest.Link("foo.xml") |
| 238 | with open(self.manifest_file) as fp: | 250 | with open(self.manifest_file) as fp: |
| 239 | self.assertIn('<include name="foo.xml" />', fp.read()) | 251 | self.assertIn('<include name="foo.xml" />', fp.read()) |
| 240 | 252 | ||
| 241 | def test_toxml_empty(self): | 253 | def test_toxml_empty(self): |
| 242 | """Verify the ToXml() helper.""" | 254 | """Verify the ToXml() helper.""" |
| 243 | manifest = self.getXmlManifest( | 255 | manifest = self.getXmlManifest( |
| 244 | '<?xml version="1.0" encoding="UTF-8"?>' | 256 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 245 | '<manifest></manifest>') | 257 | ) |
| 246 | self.assertEqual(manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>') | 258 | self.assertEqual( |
| 247 | 259 | manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>' | |
| 248 | def test_todict_empty(self): | 260 | ) |
| 249 | """Verify the ToDict() helper.""" | 261 | |
| 250 | manifest = self.getXmlManifest( | 262 | def test_todict_empty(self): |
| 251 | '<?xml version="1.0" encoding="UTF-8"?>' | 263 | """Verify the ToDict() helper.""" |
| 252 | '<manifest></manifest>') | 264 | manifest = self.getXmlManifest( |
| 253 | self.assertEqual(manifest.ToDict(), {}) | 265 | '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>" |
| 254 | 266 | ) | |
| 255 | def test_toxml_omit_local(self): | 267 | self.assertEqual(manifest.ToDict(), {}) |
| 256 | """Does not include local_manifests projects when omit_local=True.""" | 268 | |
| 257 | manifest = self.getXmlManifest( | 269 | def test_toxml_omit_local(self): |
| 258 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' | 270 | """Does not include local_manifests projects when omit_local=True.""" |
| 259 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' | 271 | manifest = self.getXmlManifest( |
| 260 | '<project name="p" groups="local::me"/>' | 272 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' |
| 261 | '<project name="q"/>' | 273 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' |
| 262 | '<project name="r" groups="keep"/>' | 274 | '<project name="p" groups="local::me"/>' |
| 263 | '</manifest>') | 275 | '<project name="q"/>' |
| 264 | self.assertEqual( | 276 | '<project name="r" groups="keep"/>' |
| 265 | sort_attributes(manifest.ToXml(omit_local=True).toxml()), | 277 | "</manifest>" |
| 266 | '<?xml version="1.0" ?><manifest>' | 278 | ) |
| 267 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' | 279 | self.assertEqual( |
| 268 | '<project name="q"/><project groups="keep" name="r"/></manifest>') | 280 | sort_attributes(manifest.ToXml(omit_local=True).toxml()), |
| 269 | 281 | '<?xml version="1.0" ?><manifest>' | |
| 270 | def test_toxml_with_local(self): | 282 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' |
| 271 | """Does include local_manifests projects when omit_local=False.""" | 283 | '<project name="q"/><project groups="keep" name="r"/></manifest>', |
| 272 | manifest = self.getXmlManifest( | 284 | ) |
| 273 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' | 285 | |
| 274 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' | 286 | def test_toxml_with_local(self): |
| 275 | '<project name="p" groups="local::me"/>' | 287 | """Does include local_manifests projects when omit_local=False.""" |
| 276 | '<project name="q"/>' | 288 | manifest = self.getXmlManifest( |
| 277 | '<project name="r" groups="keep"/>' | 289 | '<?xml version="1.0" encoding="UTF-8"?><manifest>' |
| 278 | '</manifest>') | 290 | '<remote name="a" fetch=".."/><default remote="a" revision="r"/>' |
| 279 | self.assertEqual( | 291 | '<project name="p" groups="local::me"/>' |
| 280 | sort_attributes(manifest.ToXml(omit_local=False).toxml()), | 292 | '<project name="q"/>' |
| 281 | '<?xml version="1.0" ?><manifest>' | 293 | '<project name="r" groups="keep"/>' |
| 282 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' | 294 | "</manifest>" |
| 283 | '<project groups="local::me" name="p"/>' | 295 | ) |
| 284 | '<project name="q"/><project groups="keep" name="r"/></manifest>') | 296 | self.assertEqual( |
| 285 | 297 | sort_attributes(manifest.ToXml(omit_local=False).toxml()), | |
| 286 | def test_repo_hooks(self): | 298 | '<?xml version="1.0" ?><manifest>' |
| 287 | """Check repo-hooks settings.""" | 299 | '<remote fetch=".." name="a"/><default remote="a" revision="r"/>' |
| 288 | manifest = self.getXmlManifest(""" | 300 | '<project groups="local::me" name="p"/>' |
| 301 | '<project name="q"/><project groups="keep" name="r"/></manifest>', | ||
| 302 | ) | ||
| 303 | |||
| 304 | def test_repo_hooks(self): | ||
| 305 | """Check repo-hooks settings.""" | ||
| 306 | manifest = self.getXmlManifest( | ||
| 307 | """ | ||
| 289 | <manifest> | 308 | <manifest> |
| 290 | <remote name="test-remote" fetch="http://localhost" /> | 309 | <remote name="test-remote" fetch="http://localhost" /> |
| 291 | <default remote="test-remote" revision="refs/heads/main" /> | 310 | <default remote="test-remote" revision="refs/heads/main" /> |
| 292 | <project name="repohooks" path="src/repohooks"/> | 311 | <project name="repohooks" path="src/repohooks"/> |
| 293 | <repo-hooks in-project="repohooks" enabled-list="a, b"/> | 312 | <repo-hooks in-project="repohooks" enabled-list="a, b"/> |
| 294 | </manifest> | 313 | </manifest> |
| 295 | """) | 314 | """ |
| 296 | self.assertEqual(manifest.repo_hooks_project.name, 'repohooks') | 315 | ) |
| 297 | self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b']) | 316 | self.assertEqual(manifest.repo_hooks_project.name, "repohooks") |
| 298 | 317 | self.assertEqual( | |
| 299 | def test_repo_hooks_unordered(self): | 318 | manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"] |
| 300 | """Check repo-hooks settings work even if the project def comes second.""" | 319 | ) |
| 301 | manifest = self.getXmlManifest(""" | 320 | |
| 321 | def test_repo_hooks_unordered(self): | ||
| 322 | """Check repo-hooks settings work even if the project def comes second.""" # noqa: E501 | ||
| 323 | manifest = self.getXmlManifest( | ||
| 324 | """ | ||
| 302 | <manifest> | 325 | <manifest> |
| 303 | <remote name="test-remote" fetch="http://localhost" /> | 326 | <remote name="test-remote" fetch="http://localhost" /> |
| 304 | <default remote="test-remote" revision="refs/heads/main" /> | 327 | <default remote="test-remote" revision="refs/heads/main" /> |
| 305 | <repo-hooks in-project="repohooks" enabled-list="a, b"/> | 328 | <repo-hooks in-project="repohooks" enabled-list="a, b"/> |
| 306 | <project name="repohooks" path="src/repohooks"/> | 329 | <project name="repohooks" path="src/repohooks"/> |
| 307 | </manifest> | 330 | </manifest> |
| 308 | """) | 331 | """ |
| 309 | self.assertEqual(manifest.repo_hooks_project.name, 'repohooks') | 332 | ) |
| 310 | self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b']) | 333 | self.assertEqual(manifest.repo_hooks_project.name, "repohooks") |
| 311 | 334 | self.assertEqual( | |
| 312 | def test_unknown_tags(self): | 335 | manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"] |
| 313 | """Check superproject settings.""" | 336 | ) |
| 314 | manifest = self.getXmlManifest(""" | 337 | |
| 338 | def test_unknown_tags(self): | ||
| 339 | """Check superproject settings.""" | ||
| 340 | manifest = self.getXmlManifest( | ||
| 341 | """ | ||
| 315 | <manifest> | 342 | <manifest> |
| 316 | <remote name="test-remote" fetch="http://localhost" /> | 343 | <remote name="test-remote" fetch="http://localhost" /> |
| 317 | <default remote="test-remote" revision="refs/heads/main" /> | 344 | <default remote="test-remote" revision="refs/heads/main" /> |
| @@ -319,44 +346,54 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 319 | <iankaz value="unknown (possible) future tags are ignored"/> | 346 | <iankaz value="unknown (possible) future tags are ignored"/> |
| 320 | <x-custom-tag>X tags are always ignored</x-custom-tag> | 347 | <x-custom-tag>X tags are always ignored</x-custom-tag> |
| 321 | </manifest> | 348 | </manifest> |
| 322 | """) | 349 | """ |
| 323 | self.assertEqual(manifest.superproject.name, 'superproject') | 350 | ) |
| 324 | self.assertEqual(manifest.superproject.remote.name, 'test-remote') | 351 | self.assertEqual(manifest.superproject.name, "superproject") |
| 325 | self.assertEqual( | 352 | self.assertEqual(manifest.superproject.remote.name, "test-remote") |
| 326 | sort_attributes(manifest.ToXml().toxml()), | 353 | self.assertEqual( |
| 327 | '<?xml version="1.0" ?><manifest>' | 354 | sort_attributes(manifest.ToXml().toxml()), |
| 328 | '<remote fetch="http://localhost" name="test-remote"/>' | 355 | '<?xml version="1.0" ?><manifest>' |
| 329 | '<default remote="test-remote" revision="refs/heads/main"/>' | 356 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 330 | '<superproject name="superproject"/>' | 357 | '<default remote="test-remote" revision="refs/heads/main"/>' |
| 331 | '</manifest>') | 358 | '<superproject name="superproject"/>' |
| 332 | 359 | "</manifest>", | |
| 333 | def test_remote_annotations(self): | 360 | ) |
| 334 | """Check remote settings.""" | 361 | |
| 335 | manifest = self.getXmlManifest(""" | 362 | def test_remote_annotations(self): |
| 363 | """Check remote settings.""" | ||
| 364 | manifest = self.getXmlManifest( | ||
| 365 | """ | ||
| 336 | <manifest> | 366 | <manifest> |
| 337 | <remote name="test-remote" fetch="http://localhost"> | 367 | <remote name="test-remote" fetch="http://localhost"> |
| 338 | <annotation name="foo" value="bar"/> | 368 | <annotation name="foo" value="bar"/> |
| 339 | </remote> | 369 | </remote> |
| 340 | </manifest> | 370 | </manifest> |
| 341 | """) | 371 | """ |
| 342 | self.assertEqual(manifest.remotes['test-remote'].annotations[0].name, 'foo') | 372 | ) |
| 343 | self.assertEqual(manifest.remotes['test-remote'].annotations[0].value, 'bar') | 373 | self.assertEqual( |
| 344 | self.assertEqual( | 374 | manifest.remotes["test-remote"].annotations[0].name, "foo" |
| 345 | sort_attributes(manifest.ToXml().toxml()), | 375 | ) |
| 346 | '<?xml version="1.0" ?><manifest>' | 376 | self.assertEqual( |
| 347 | '<remote fetch="http://localhost" name="test-remote">' | 377 | manifest.remotes["test-remote"].annotations[0].value, "bar" |
| 348 | '<annotation name="foo" value="bar"/>' | 378 | ) |
| 349 | '</remote>' | 379 | self.assertEqual( |
| 350 | '</manifest>') | 380 | sort_attributes(manifest.ToXml().toxml()), |
| 381 | '<?xml version="1.0" ?><manifest>' | ||
| 382 | '<remote fetch="http://localhost" name="test-remote">' | ||
| 383 | '<annotation name="foo" value="bar"/>' | ||
| 384 | "</remote>" | ||
| 385 | "</manifest>", | ||
| 386 | ) | ||
| 351 | 387 | ||
| 352 | 388 | ||
| 353 | class IncludeElementTests(ManifestParseTestCase): | 389 | class IncludeElementTests(ManifestParseTestCase): |
| 354 | """Tests for <include>.""" | 390 | """Tests for <include>.""" |
| 355 | 391 | ||
| 356 | def test_group_levels(self): | 392 | def test_group_levels(self): |
| 357 | root_m = os.path.join(self.manifest_dir, 'root.xml') | 393 | root_m = os.path.join(self.manifest_dir, "root.xml") |
| 358 | with open(root_m, 'w') as fp: | 394 | with open(root_m, "w") as fp: |
| 359 | fp.write(""" | 395 | fp.write( |
| 396 | """ | ||
| 360 | <manifest> | 397 | <manifest> |
| 361 | <remote name="test-remote" fetch="http://localhost" /> | 398 | <remote name="test-remote" fetch="http://localhost" /> |
| 362 | <default remote="test-remote" revision="refs/heads/main" /> | 399 | <default remote="test-remote" revision="refs/heads/main" /> |
| @@ -364,438 +401,524 @@ class IncludeElementTests(ManifestParseTestCase): | |||
| 364 | <project name="root-name1" path="root-path1" /> | 401 | <project name="root-name1" path="root-path1" /> |
| 365 | <project name="root-name2" path="root-path2" groups="r2g1,r2g2" /> | 402 | <project name="root-name2" path="root-path2" groups="r2g1,r2g2" /> |
| 366 | </manifest> | 403 | </manifest> |
| 367 | """) | 404 | """ |
| 368 | with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: | 405 | ) |
| 369 | fp.write(""" | 406 | with open(os.path.join(self.manifest_dir, "level1.xml"), "w") as fp: |
| 407 | fp.write( | ||
| 408 | """ | ||
| 370 | <manifest> | 409 | <manifest> |
| 371 | <include name="level2.xml" groups="level2-group" /> | 410 | <include name="level2.xml" groups="level2-group" /> |
| 372 | <project name="level1-name1" path="level1-path1" /> | 411 | <project name="level1-name1" path="level1-path1" /> |
| 373 | </manifest> | 412 | </manifest> |
| 374 | """) | 413 | """ |
| 375 | with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: | 414 | ) |
| 376 | fp.write(""" | 415 | with open(os.path.join(self.manifest_dir, "level2.xml"), "w") as fp: |
| 416 | fp.write( | ||
| 417 | """ | ||
| 377 | <manifest> | 418 | <manifest> |
| 378 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> | 419 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> |
| 379 | </manifest> | 420 | </manifest> |
| 380 | """) | 421 | """ |
| 381 | include_m = manifest_xml.XmlManifest(self.repodir, root_m) | 422 | ) |
| 382 | for proj in include_m.projects: | 423 | include_m = manifest_xml.XmlManifest(self.repodir, root_m) |
| 383 | if proj.name == 'root-name1': | 424 | for proj in include_m.projects: |
| 384 | # Check include group not set on root level proj. | 425 | if proj.name == "root-name1": |
| 385 | self.assertNotIn('level1-group', proj.groups) | 426 | # Check include group not set on root level proj. |
| 386 | if proj.name == 'root-name2': | 427 | self.assertNotIn("level1-group", proj.groups) |
| 387 | # Check root proj group not removed. | 428 | if proj.name == "root-name2": |
| 388 | self.assertIn('r2g1', proj.groups) | 429 | # Check root proj group not removed. |
| 389 | if proj.name == 'level1-name1': | 430 | self.assertIn("r2g1", proj.groups) |
| 390 | # Check level1 proj has inherited group level 1. | 431 | if proj.name == "level1-name1": |
| 391 | self.assertIn('level1-group', proj.groups) | 432 | # Check level1 proj has inherited group level 1. |
| 392 | if proj.name == 'level2-name1': | 433 | self.assertIn("level1-group", proj.groups) |
| 393 | # Check level2 proj has inherited group levels 1 and 2. | 434 | if proj.name == "level2-name1": |
| 394 | self.assertIn('level1-group', proj.groups) | 435 | # Check level2 proj has inherited group levels 1 and 2. |
| 395 | self.assertIn('level2-group', proj.groups) | 436 | self.assertIn("level1-group", proj.groups) |
| 396 | # Check level2 proj group not removed. | 437 | self.assertIn("level2-group", proj.groups) |
| 397 | self.assertIn('l2g1', proj.groups) | 438 | # Check level2 proj group not removed. |
| 398 | 439 | self.assertIn("l2g1", proj.groups) | |
| 399 | def test_allow_bad_name_from_user(self): | 440 | |
| 400 | """Check handling of bad name attribute from the user's input.""" | 441 | def test_allow_bad_name_from_user(self): |
| 401 | def parse(name): | 442 | """Check handling of bad name attribute from the user's input.""" |
| 402 | name = self.encodeXmlAttr(name) | 443 | |
| 403 | manifest = self.getXmlManifest(f""" | 444 | def parse(name): |
| 445 | name = self.encodeXmlAttr(name) | ||
| 446 | manifest = self.getXmlManifest( | ||
| 447 | f""" | ||
| 404 | <manifest> | 448 | <manifest> |
| 405 | <remote name="default-remote" fetch="http://localhost" /> | 449 | <remote name="default-remote" fetch="http://localhost" /> |
| 406 | <default remote="default-remote" revision="refs/heads/main" /> | 450 | <default remote="default-remote" revision="refs/heads/main" /> |
| 407 | <include name="{name}" /> | 451 | <include name="{name}" /> |
| 408 | </manifest> | 452 | </manifest> |
| 409 | """) | 453 | """ |
| 410 | # Force the manifest to be parsed. | 454 | ) |
| 411 | manifest.ToXml() | 455 | # Force the manifest to be parsed. |
| 412 | 456 | manifest.ToXml() | |
| 413 | # Setup target of the include. | 457 | |
| 414 | target = os.path.join(self.tempdir, 'target.xml') | 458 | # Setup target of the include. |
| 415 | with open(target, 'w') as fp: | 459 | target = os.path.join(self.tempdir, "target.xml") |
| 416 | fp.write('<manifest></manifest>') | 460 | with open(target, "w") as fp: |
| 417 | 461 | fp.write("<manifest></manifest>") | |
| 418 | # Include with absolute path. | 462 | |
| 419 | parse(os.path.abspath(target)) | 463 | # Include with absolute path. |
| 420 | 464 | parse(os.path.abspath(target)) | |
| 421 | # Include with relative path. | 465 | |
| 422 | parse(os.path.relpath(target, self.manifest_dir)) | 466 | # Include with relative path. |
| 423 | 467 | parse(os.path.relpath(target, self.manifest_dir)) | |
| 424 | def test_bad_name_checks(self): | 468 | |
| 425 | """Check handling of bad name attribute.""" | 469 | def test_bad_name_checks(self): |
| 426 | def parse(name): | 470 | """Check handling of bad name attribute.""" |
| 427 | name = self.encodeXmlAttr(name) | 471 | |
| 428 | # Setup target of the include. | 472 | def parse(name): |
| 429 | with open(os.path.join(self.manifest_dir, 'target.xml'), 'w', encoding="utf-8") as fp: | 473 | name = self.encodeXmlAttr(name) |
| 430 | fp.write(f'<manifest><include name="{name}"/></manifest>') | 474 | # Setup target of the include. |
| 431 | 475 | with open( | |
| 432 | manifest = self.getXmlManifest(""" | 476 | os.path.join(self.manifest_dir, "target.xml"), |
| 477 | "w", | ||
| 478 | encoding="utf-8", | ||
| 479 | ) as fp: | ||
| 480 | fp.write(f'<manifest><include name="{name}"/></manifest>') | ||
| 481 | |||
| 482 | manifest = self.getXmlManifest( | ||
| 483 | """ | ||
| 433 | <manifest> | 484 | <manifest> |
| 434 | <remote name="default-remote" fetch="http://localhost" /> | 485 | <remote name="default-remote" fetch="http://localhost" /> |
| 435 | <default remote="default-remote" revision="refs/heads/main" /> | 486 | <default remote="default-remote" revision="refs/heads/main" /> |
| 436 | <include name="target.xml" /> | 487 | <include name="target.xml" /> |
| 437 | </manifest> | 488 | </manifest> |
| 438 | """) | 489 | """ |
| 439 | # Force the manifest to be parsed. | 490 | ) |
| 440 | manifest.ToXml() | 491 | # Force the manifest to be parsed. |
| 492 | manifest.ToXml() | ||
| 441 | 493 | ||
| 442 | # Handle empty name explicitly because a different codepath rejects it. | 494 | # Handle empty name explicitly because a different codepath rejects it. |
| 443 | with self.assertRaises(error.ManifestParseError): | 495 | with self.assertRaises(error.ManifestParseError): |
| 444 | parse('') | 496 | parse("") |
| 445 | 497 | ||
| 446 | for path in INVALID_FS_PATHS: | 498 | for path in INVALID_FS_PATHS: |
| 447 | if not path: | 499 | if not path: |
| 448 | continue | 500 | continue |
| 449 | 501 | ||
| 450 | with self.assertRaises(error.ManifestInvalidPathError): | 502 | with self.assertRaises(error.ManifestInvalidPathError): |
| 451 | parse(path) | 503 | parse(path) |
| 452 | 504 | ||
| 453 | 505 | ||
| 454 | class ProjectElementTests(ManifestParseTestCase): | 506 | class ProjectElementTests(ManifestParseTestCase): |
| 455 | """Tests for <project>.""" | 507 | """Tests for <project>.""" |
| 456 | 508 | ||
| 457 | def test_group(self): | 509 | def test_group(self): |
| 458 | """Check project group settings.""" | 510 | """Check project group settings.""" |
| 459 | manifest = self.getXmlManifest(""" | 511 | manifest = self.getXmlManifest( |
| 512 | """ | ||
| 460 | <manifest> | 513 | <manifest> |
| 461 | <remote name="test-remote" fetch="http://localhost" /> | 514 | <remote name="test-remote" fetch="http://localhost" /> |
| 462 | <default remote="test-remote" revision="refs/heads/main" /> | 515 | <default remote="test-remote" revision="refs/heads/main" /> |
| 463 | <project name="test-name" path="test-path"/> | 516 | <project name="test-name" path="test-path"/> |
| 464 | <project name="extras" path="path" groups="g1,g2,g1"/> | 517 | <project name="extras" path="path" groups="g1,g2,g1"/> |
| 465 | </manifest> | 518 | </manifest> |
| 466 | """) | 519 | """ |
| 467 | self.assertEqual(len(manifest.projects), 2) | 520 | ) |
| 468 | # Ordering isn't guaranteed. | 521 | self.assertEqual(len(manifest.projects), 2) |
| 469 | result = { | 522 | # Ordering isn't guaranteed. |
| 470 | manifest.projects[0].name: manifest.projects[0].groups, | 523 | result = { |
| 471 | manifest.projects[1].name: manifest.projects[1].groups, | 524 | manifest.projects[0].name: manifest.projects[0].groups, |
| 472 | } | 525 | manifest.projects[1].name: manifest.projects[1].groups, |
| 473 | project = manifest.projects[0] | 526 | } |
| 474 | self.assertCountEqual( | 527 | self.assertCountEqual( |
| 475 | result['test-name'], | 528 | result["test-name"], ["name:test-name", "all", "path:test-path"] |
| 476 | ['name:test-name', 'all', 'path:test-path']) | 529 | ) |
| 477 | self.assertCountEqual( | 530 | self.assertCountEqual( |
| 478 | result['extras'], | 531 | result["extras"], |
| 479 | ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) | 532 | ["g1", "g2", "g1", "name:extras", "all", "path:path"], |
| 480 | groupstr = 'default,platform-' + platform.system().lower() | 533 | ) |
| 481 | self.assertEqual(groupstr, manifest.GetGroupsStr()) | 534 | groupstr = "default,platform-" + platform.system().lower() |
| 482 | groupstr = 'g1,g2,g1' | 535 | self.assertEqual(groupstr, manifest.GetGroupsStr()) |
| 483 | manifest.manifestProject.config.SetString('manifest.groups', groupstr) | 536 | groupstr = "g1,g2,g1" |
| 484 | self.assertEqual(groupstr, manifest.GetGroupsStr()) | 537 | manifest.manifestProject.config.SetString("manifest.groups", groupstr) |
| 485 | 538 | self.assertEqual(groupstr, manifest.GetGroupsStr()) | |
| 486 | def test_set_revision_id(self): | 539 | |
| 487 | """Check setting of project's revisionId.""" | 540 | def test_set_revision_id(self): |
| 488 | manifest = self.getXmlManifest(""" | 541 | """Check setting of project's revisionId.""" |
| 542 | manifest = self.getXmlManifest( | ||
| 543 | """ | ||
| 489 | <manifest> | 544 | <manifest> |
| 490 | <remote name="default-remote" fetch="http://localhost" /> | 545 | <remote name="default-remote" fetch="http://localhost" /> |
| 491 | <default remote="default-remote" revision="refs/heads/main" /> | 546 | <default remote="default-remote" revision="refs/heads/main" /> |
| 492 | <project name="test-name"/> | 547 | <project name="test-name"/> |
| 493 | </manifest> | 548 | </manifest> |
| 494 | """) | 549 | """ |
| 495 | self.assertEqual(len(manifest.projects), 1) | 550 | ) |
| 496 | project = manifest.projects[0] | 551 | self.assertEqual(len(manifest.projects), 1) |
| 497 | project.SetRevisionId('ABCDEF') | 552 | project = manifest.projects[0] |
| 498 | self.assertEqual( | 553 | project.SetRevisionId("ABCDEF") |
| 499 | sort_attributes(manifest.ToXml().toxml()), | 554 | self.assertEqual( |
| 500 | '<?xml version="1.0" ?><manifest>' | 555 | sort_attributes(manifest.ToXml().toxml()), |
| 501 | '<remote fetch="http://localhost" name="default-remote"/>' | 556 | '<?xml version="1.0" ?><manifest>' |
| 502 | '<default remote="default-remote" revision="refs/heads/main"/>' | 557 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 503 | '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>' | 558 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 504 | '</manifest>') | 559 | '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>' # noqa: E501 |
| 505 | 560 | "</manifest>", | |
| 506 | def test_trailing_slash(self): | 561 | ) |
| 507 | """Check handling of trailing slashes in attributes.""" | 562 | |
| 508 | def parse(name, path): | 563 | def test_trailing_slash(self): |
| 509 | name = self.encodeXmlAttr(name) | 564 | """Check handling of trailing slashes in attributes.""" |
| 510 | path = self.encodeXmlAttr(path) | 565 | |
| 511 | return self.getXmlManifest(f""" | 566 | def parse(name, path): |
| 567 | name = self.encodeXmlAttr(name) | ||
| 568 | path = self.encodeXmlAttr(path) | ||
| 569 | return self.getXmlManifest( | ||
| 570 | f""" | ||
| 512 | <manifest> | 571 | <manifest> |
| 513 | <remote name="default-remote" fetch="http://localhost" /> | 572 | <remote name="default-remote" fetch="http://localhost" /> |
| 514 | <default remote="default-remote" revision="refs/heads/main" /> | 573 | <default remote="default-remote" revision="refs/heads/main" /> |
| 515 | <project name="{name}" path="{path}" /> | 574 | <project name="{name}" path="{path}" /> |
| 516 | </manifest> | 575 | </manifest> |
| 517 | """) | 576 | """ |
| 518 | 577 | ) | |
| 519 | manifest = parse('a/path/', 'foo') | 578 | |
| 520 | self.assertEqual(os.path.normpath(manifest.projects[0].gitdir), | 579 | manifest = parse("a/path/", "foo") |
| 521 | os.path.join(self.tempdir, '.repo', 'projects', 'foo.git')) | 580 | self.assertEqual( |
| 522 | self.assertEqual(os.path.normpath(manifest.projects[0].objdir), | 581 | os.path.normpath(manifest.projects[0].gitdir), |
| 523 | os.path.join(self.tempdir, '.repo', 'project-objects', 'a', 'path.git')) | 582 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), |
| 524 | 583 | ) | |
| 525 | manifest = parse('a/path', 'foo/') | 584 | self.assertEqual( |
| 526 | self.assertEqual(os.path.normpath(manifest.projects[0].gitdir), | 585 | os.path.normpath(manifest.projects[0].objdir), |
| 527 | os.path.join(self.tempdir, '.repo', 'projects', 'foo.git')) | 586 | os.path.join( |
| 528 | self.assertEqual(os.path.normpath(manifest.projects[0].objdir), | 587 | self.tempdir, ".repo", "project-objects", "a", "path.git" |
| 529 | os.path.join(self.tempdir, '.repo', 'project-objects', 'a', 'path.git')) | 588 | ), |
| 530 | 589 | ) | |
| 531 | manifest = parse('a/path', 'foo//////') | 590 | |
| 532 | self.assertEqual(os.path.normpath(manifest.projects[0].gitdir), | 591 | manifest = parse("a/path", "foo/") |
| 533 | os.path.join(self.tempdir, '.repo', 'projects', 'foo.git')) | 592 | self.assertEqual( |
| 534 | self.assertEqual(os.path.normpath(manifest.projects[0].objdir), | 593 | os.path.normpath(manifest.projects[0].gitdir), |
| 535 | os.path.join(self.tempdir, '.repo', 'project-objects', 'a', 'path.git')) | 594 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), |
| 536 | 595 | ) | |
| 537 | def test_toplevel_path(self): | 596 | self.assertEqual( |
| 538 | """Check handling of path=. specially.""" | 597 | os.path.normpath(manifest.projects[0].objdir), |
| 539 | def parse(name, path): | 598 | os.path.join( |
| 540 | name = self.encodeXmlAttr(name) | 599 | self.tempdir, ".repo", "project-objects", "a", "path.git" |
| 541 | path = self.encodeXmlAttr(path) | 600 | ), |
| 542 | return self.getXmlManifest(f""" | 601 | ) |
| 602 | |||
| 603 | manifest = parse("a/path", "foo//////") | ||
| 604 | self.assertEqual( | ||
| 605 | os.path.normpath(manifest.projects[0].gitdir), | ||
| 606 | os.path.join(self.tempdir, ".repo", "projects", "foo.git"), | ||
| 607 | ) | ||
| 608 | self.assertEqual( | ||
| 609 | os.path.normpath(manifest.projects[0].objdir), | ||
| 610 | os.path.join( | ||
| 611 | self.tempdir, ".repo", "project-objects", "a", "path.git" | ||
| 612 | ), | ||
| 613 | ) | ||
| 614 | |||
| 615 | def test_toplevel_path(self): | ||
| 616 | """Check handling of path=. specially.""" | ||
| 617 | |||
| 618 | def parse(name, path): | ||
| 619 | name = self.encodeXmlAttr(name) | ||
| 620 | path = self.encodeXmlAttr(path) | ||
| 621 | return self.getXmlManifest( | ||
| 622 | f""" | ||
| 543 | <manifest> | 623 | <manifest> |
| 544 | <remote name="default-remote" fetch="http://localhost" /> | 624 | <remote name="default-remote" fetch="http://localhost" /> |
| 545 | <default remote="default-remote" revision="refs/heads/main" /> | 625 | <default remote="default-remote" revision="refs/heads/main" /> |
| 546 | <project name="{name}" path="{path}" /> | 626 | <project name="{name}" path="{path}" /> |
| 547 | </manifest> | 627 | </manifest> |
| 548 | """) | 628 | """ |
| 549 | 629 | ) | |
| 550 | for path in ('.', './', './/', './//'): | 630 | |
| 551 | manifest = parse('server/path', path) | 631 | for path in (".", "./", ".//", ".///"): |
| 552 | self.assertEqual(os.path.normpath(manifest.projects[0].gitdir), | 632 | manifest = parse("server/path", path) |
| 553 | os.path.join(self.tempdir, '.repo', 'projects', '..git')) | 633 | self.assertEqual( |
| 554 | 634 | os.path.normpath(manifest.projects[0].gitdir), | |
| 555 | def test_bad_path_name_checks(self): | 635 | os.path.join(self.tempdir, ".repo", "projects", "..git"), |
| 556 | """Check handling of bad path & name attributes.""" | 636 | ) |
| 557 | def parse(name, path): | 637 | |
| 558 | name = self.encodeXmlAttr(name) | 638 | def test_bad_path_name_checks(self): |
| 559 | path = self.encodeXmlAttr(path) | 639 | """Check handling of bad path & name attributes.""" |
| 560 | manifest = self.getXmlManifest(f""" | 640 | |
| 641 | def parse(name, path): | ||
| 642 | name = self.encodeXmlAttr(name) | ||
| 643 | path = self.encodeXmlAttr(path) | ||
| 644 | manifest = self.getXmlManifest( | ||
| 645 | f""" | ||
| 561 | <manifest> | 646 | <manifest> |
| 562 | <remote name="default-remote" fetch="http://localhost" /> | 647 | <remote name="default-remote" fetch="http://localhost" /> |
| 563 | <default remote="default-remote" revision="refs/heads/main" /> | 648 | <default remote="default-remote" revision="refs/heads/main" /> |
| 564 | <project name="{name}" path="{path}" /> | 649 | <project name="{name}" path="{path}" /> |
| 565 | </manifest> | 650 | </manifest> |
| 566 | """) | 651 | """ |
| 567 | # Force the manifest to be parsed. | 652 | ) |
| 568 | manifest.ToXml() | 653 | # Force the manifest to be parsed. |
| 654 | manifest.ToXml() | ||
| 569 | 655 | ||
| 570 | # Verify the parser is valid by default to avoid buggy tests below. | 656 | # Verify the parser is valid by default to avoid buggy tests below. |
| 571 | parse('ok', 'ok') | 657 | parse("ok", "ok") |
| 572 | 658 | ||
| 573 | # Handle empty name explicitly because a different codepath rejects it. | 659 | # Handle empty name explicitly because a different codepath rejects it. |
| 574 | # Empty path is OK because it defaults to the name field. | 660 | # Empty path is OK because it defaults to the name field. |
| 575 | with self.assertRaises(error.ManifestParseError): | 661 | with self.assertRaises(error.ManifestParseError): |
| 576 | parse('', 'ok') | 662 | parse("", "ok") |
| 577 | 663 | ||
| 578 | for path in INVALID_FS_PATHS: | 664 | for path in INVALID_FS_PATHS: |
| 579 | if not path or path.endswith('/') or path.endswith(os.path.sep): | 665 | if not path or path.endswith("/") or path.endswith(os.path.sep): |
| 580 | continue | 666 | continue |
| 581 | 667 | ||
| 582 | with self.assertRaises(error.ManifestInvalidPathError): | 668 | with self.assertRaises(error.ManifestInvalidPathError): |
| 583 | parse(path, 'ok') | 669 | parse(path, "ok") |
| 584 | 670 | ||
| 585 | # We have a dedicated test for path=".". | 671 | # We have a dedicated test for path=".". |
| 586 | if path not in {'.'}: | 672 | if path not in {"."}: |
| 587 | with self.assertRaises(error.ManifestInvalidPathError): | 673 | with self.assertRaises(error.ManifestInvalidPathError): |
| 588 | parse('ok', path) | 674 | parse("ok", path) |
| 589 | 675 | ||
| 590 | 676 | ||
| 591 | class SuperProjectElementTests(ManifestParseTestCase): | 677 | class SuperProjectElementTests(ManifestParseTestCase): |
| 592 | """Tests for <superproject>.""" | 678 | """Tests for <superproject>.""" |
| 593 | 679 | ||
| 594 | def test_superproject(self): | 680 | def test_superproject(self): |
| 595 | """Check superproject settings.""" | 681 | """Check superproject settings.""" |
| 596 | manifest = self.getXmlManifest(""" | 682 | manifest = self.getXmlManifest( |
| 683 | """ | ||
| 597 | <manifest> | 684 | <manifest> |
| 598 | <remote name="test-remote" fetch="http://localhost" /> | 685 | <remote name="test-remote" fetch="http://localhost" /> |
| 599 | <default remote="test-remote" revision="refs/heads/main" /> | 686 | <default remote="test-remote" revision="refs/heads/main" /> |
| 600 | <superproject name="superproject"/> | 687 | <superproject name="superproject"/> |
| 601 | </manifest> | 688 | </manifest> |
| 602 | """) | 689 | """ |
| 603 | self.assertEqual(manifest.superproject.name, 'superproject') | 690 | ) |
| 604 | self.assertEqual(manifest.superproject.remote.name, 'test-remote') | 691 | self.assertEqual(manifest.superproject.name, "superproject") |
| 605 | self.assertEqual(manifest.superproject.remote.url, 'http://localhost/superproject') | 692 | self.assertEqual(manifest.superproject.remote.name, "test-remote") |
| 606 | self.assertEqual(manifest.superproject.revision, 'refs/heads/main') | 693 | self.assertEqual( |
| 607 | self.assertEqual( | 694 | manifest.superproject.remote.url, "http://localhost/superproject" |
| 608 | sort_attributes(manifest.ToXml().toxml()), | 695 | ) |
| 609 | '<?xml version="1.0" ?><manifest>' | 696 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") |
| 610 | '<remote fetch="http://localhost" name="test-remote"/>' | 697 | self.assertEqual( |
| 611 | '<default remote="test-remote" revision="refs/heads/main"/>' | 698 | sort_attributes(manifest.ToXml().toxml()), |
| 612 | '<superproject name="superproject"/>' | 699 | '<?xml version="1.0" ?><manifest>' |
| 613 | '</manifest>') | 700 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 614 | 701 | '<default remote="test-remote" revision="refs/heads/main"/>' | |
| 615 | def test_superproject_revision(self): | 702 | '<superproject name="superproject"/>' |
| 616 | """Check superproject settings with a different revision attribute""" | 703 | "</manifest>", |
| 617 | self.maxDiff = None | 704 | ) |
| 618 | manifest = self.getXmlManifest(""" | 705 | |
| 706 | def test_superproject_revision(self): | ||
| 707 | """Check superproject settings with a different revision attribute""" | ||
| 708 | self.maxDiff = None | ||
| 709 | manifest = self.getXmlManifest( | ||
| 710 | """ | ||
| 619 | <manifest> | 711 | <manifest> |
| 620 | <remote name="test-remote" fetch="http://localhost" /> | 712 | <remote name="test-remote" fetch="http://localhost" /> |
| 621 | <default remote="test-remote" revision="refs/heads/main" /> | 713 | <default remote="test-remote" revision="refs/heads/main" /> |
| 622 | <superproject name="superproject" revision="refs/heads/stable" /> | 714 | <superproject name="superproject" revision="refs/heads/stable" /> |
| 623 | </manifest> | 715 | </manifest> |
| 624 | """) | 716 | """ |
| 625 | self.assertEqual(manifest.superproject.name, 'superproject') | 717 | ) |
| 626 | self.assertEqual(manifest.superproject.remote.name, 'test-remote') | 718 | self.assertEqual(manifest.superproject.name, "superproject") |
| 627 | self.assertEqual(manifest.superproject.remote.url, 'http://localhost/superproject') | 719 | self.assertEqual(manifest.superproject.remote.name, "test-remote") |
| 628 | self.assertEqual(manifest.superproject.revision, 'refs/heads/stable') | 720 | self.assertEqual( |
| 629 | self.assertEqual( | 721 | manifest.superproject.remote.url, "http://localhost/superproject" |
| 630 | sort_attributes(manifest.ToXml().toxml()), | 722 | ) |
| 631 | '<?xml version="1.0" ?><manifest>' | 723 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") |
| 632 | '<remote fetch="http://localhost" name="test-remote"/>' | 724 | self.assertEqual( |
| 633 | '<default remote="test-remote" revision="refs/heads/main"/>' | 725 | sort_attributes(manifest.ToXml().toxml()), |
| 634 | '<superproject name="superproject" revision="refs/heads/stable"/>' | 726 | '<?xml version="1.0" ?><manifest>' |
| 635 | '</manifest>') | 727 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 636 | 728 | '<default remote="test-remote" revision="refs/heads/main"/>' | |
| 637 | def test_superproject_revision_default_negative(self): | 729 | '<superproject name="superproject" revision="refs/heads/stable"/>' |
| 638 | """Check superproject settings with a same revision attribute""" | 730 | "</manifest>", |
| 639 | self.maxDiff = None | 731 | ) |
| 640 | manifest = self.getXmlManifest(""" | 732 | |
| 733 | def test_superproject_revision_default_negative(self): | ||
| 734 | """Check superproject settings with a same revision attribute""" | ||
| 735 | self.maxDiff = None | ||
| 736 | manifest = self.getXmlManifest( | ||
| 737 | """ | ||
| 641 | <manifest> | 738 | <manifest> |
| 642 | <remote name="test-remote" fetch="http://localhost" /> | 739 | <remote name="test-remote" fetch="http://localhost" /> |
| 643 | <default remote="test-remote" revision="refs/heads/stable" /> | 740 | <default remote="test-remote" revision="refs/heads/stable" /> |
| 644 | <superproject name="superproject" revision="refs/heads/stable" /> | 741 | <superproject name="superproject" revision="refs/heads/stable" /> |
| 645 | </manifest> | 742 | </manifest> |
| 646 | """) | 743 | """ |
| 647 | self.assertEqual(manifest.superproject.name, 'superproject') | 744 | ) |
| 648 | self.assertEqual(manifest.superproject.remote.name, 'test-remote') | 745 | self.assertEqual(manifest.superproject.name, "superproject") |
| 649 | self.assertEqual(manifest.superproject.remote.url, 'http://localhost/superproject') | 746 | self.assertEqual(manifest.superproject.remote.name, "test-remote") |
| 650 | self.assertEqual(manifest.superproject.revision, 'refs/heads/stable') | 747 | self.assertEqual( |
| 651 | self.assertEqual( | 748 | manifest.superproject.remote.url, "http://localhost/superproject" |
| 652 | sort_attributes(manifest.ToXml().toxml()), | 749 | ) |
| 653 | '<?xml version="1.0" ?><manifest>' | 750 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") |
| 654 | '<remote fetch="http://localhost" name="test-remote"/>' | 751 | self.assertEqual( |
| 655 | '<default remote="test-remote" revision="refs/heads/stable"/>' | 752 | sort_attributes(manifest.ToXml().toxml()), |
| 656 | '<superproject name="superproject"/>' | 753 | '<?xml version="1.0" ?><manifest>' |
| 657 | '</manifest>') | 754 | '<remote fetch="http://localhost" name="test-remote"/>' |
| 658 | 755 | '<default remote="test-remote" revision="refs/heads/stable"/>' | |
| 659 | def test_superproject_revision_remote(self): | 756 | '<superproject name="superproject"/>' |
| 660 | """Check superproject settings with a same revision attribute""" | 757 | "</manifest>", |
| 661 | self.maxDiff = None | 758 | ) |
| 662 | manifest = self.getXmlManifest(""" | 759 | |
| 760 | def test_superproject_revision_remote(self): | ||
| 761 | """Check superproject settings with a same revision attribute""" | ||
| 762 | self.maxDiff = None | ||
| 763 | manifest = self.getXmlManifest( | ||
| 764 | """ | ||
| 663 | <manifest> | 765 | <manifest> |
| 664 | <remote name="test-remote" fetch="http://localhost" revision="refs/heads/main" /> | 766 | <remote name="test-remote" fetch="http://localhost" revision="refs/heads/main" /> |
| 665 | <default remote="test-remote" /> | 767 | <default remote="test-remote" /> |
| 666 | <superproject name="superproject" revision="refs/heads/stable" /> | 768 | <superproject name="superproject" revision="refs/heads/stable" /> |
| 667 | </manifest> | 769 | </manifest> |
| 668 | """) | 770 | """ # noqa: E501 |
| 669 | self.assertEqual(manifest.superproject.name, 'superproject') | 771 | ) |
| 670 | self.assertEqual(manifest.superproject.remote.name, 'test-remote') | 772 | self.assertEqual(manifest.superproject.name, "superproject") |
| 671 | self.assertEqual(manifest.superproject.remote.url, 'http://localhost/superproject') | 773 | self.assertEqual(manifest.superproject.remote.name, "test-remote") |
| 672 | self.assertEqual(manifest.superproject.revision, 'refs/heads/stable') | 774 | self.assertEqual( |
| 673 | self.assertEqual( | 775 | manifest.superproject.remote.url, "http://localhost/superproject" |
| 674 | sort_attributes(manifest.ToXml().toxml()), | 776 | ) |
| 675 | '<?xml version="1.0" ?><manifest>' | 777 | self.assertEqual(manifest.superproject.revision, "refs/heads/stable") |
| 676 | '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>' | 778 | self.assertEqual( |
| 677 | '<default remote="test-remote"/>' | 779 | sort_attributes(manifest.ToXml().toxml()), |
| 678 | '<superproject name="superproject" revision="refs/heads/stable"/>' | 780 | '<?xml version="1.0" ?><manifest>' |
| 679 | '</manifest>') | 781 | '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>' # noqa: E501 |
| 680 | 782 | '<default remote="test-remote"/>' | |
| 681 | def test_remote(self): | 783 | '<superproject name="superproject" revision="refs/heads/stable"/>' |
| 682 | """Check superproject settings with a remote.""" | 784 | "</manifest>", |
| 683 | manifest = self.getXmlManifest(""" | 785 | ) |
| 786 | |||
| 787 | def test_remote(self): | ||
| 788 | """Check superproject settings with a remote.""" | ||
| 789 | manifest = self.getXmlManifest( | ||
| 790 | """ | ||
| 684 | <manifest> | 791 | <manifest> |
| 685 | <remote name="default-remote" fetch="http://localhost" /> | 792 | <remote name="default-remote" fetch="http://localhost" /> |
| 686 | <remote name="superproject-remote" fetch="http://localhost" /> | 793 | <remote name="superproject-remote" fetch="http://localhost" /> |
| 687 | <default remote="default-remote" revision="refs/heads/main" /> | 794 | <default remote="default-remote" revision="refs/heads/main" /> |
| 688 | <superproject name="platform/superproject" remote="superproject-remote"/> | 795 | <superproject name="platform/superproject" remote="superproject-remote"/> |
| 689 | </manifest> | 796 | </manifest> |
| 690 | """) | 797 | """ |
| 691 | self.assertEqual(manifest.superproject.name, 'platform/superproject') | 798 | ) |
| 692 | self.assertEqual(manifest.superproject.remote.name, 'superproject-remote') | 799 | self.assertEqual(manifest.superproject.name, "platform/superproject") |
| 693 | self.assertEqual(manifest.superproject.remote.url, 'http://localhost/platform/superproject') | 800 | self.assertEqual( |
| 694 | self.assertEqual(manifest.superproject.revision, 'refs/heads/main') | 801 | manifest.superproject.remote.name, "superproject-remote" |
| 695 | self.assertEqual( | 802 | ) |
| 696 | sort_attributes(manifest.ToXml().toxml()), | 803 | self.assertEqual( |
| 697 | '<?xml version="1.0" ?><manifest>' | 804 | manifest.superproject.remote.url, |
| 698 | '<remote fetch="http://localhost" name="default-remote"/>' | 805 | "http://localhost/platform/superproject", |
| 699 | '<remote fetch="http://localhost" name="superproject-remote"/>' | 806 | ) |
| 700 | '<default remote="default-remote" revision="refs/heads/main"/>' | 807 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") |
| 701 | '<superproject name="platform/superproject" remote="superproject-remote"/>' | 808 | self.assertEqual( |
| 702 | '</manifest>') | 809 | sort_attributes(manifest.ToXml().toxml()), |
| 703 | 810 | '<?xml version="1.0" ?><manifest>' | |
| 704 | def test_defalut_remote(self): | 811 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 705 | """Check superproject settings with a default remote.""" | 812 | '<remote fetch="http://localhost" name="superproject-remote"/>' |
| 706 | manifest = self.getXmlManifest(""" | 813 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 814 | '<superproject name="platform/superproject" remote="superproject-remote"/>' # noqa: E501 | ||
| 815 | "</manifest>", | ||
| 816 | ) | ||
| 817 | |||
| 818 | def test_defalut_remote(self): | ||
| 819 | """Check superproject settings with a default remote.""" | ||
| 820 | manifest = self.getXmlManifest( | ||
| 821 | """ | ||
| 707 | <manifest> | 822 | <manifest> |
| 708 | <remote name="default-remote" fetch="http://localhost" /> | 823 | <remote name="default-remote" fetch="http://localhost" /> |
| 709 | <default remote="default-remote" revision="refs/heads/main" /> | 824 | <default remote="default-remote" revision="refs/heads/main" /> |
| 710 | <superproject name="superproject" remote="default-remote"/> | 825 | <superproject name="superproject" remote="default-remote"/> |
| 711 | </manifest> | 826 | </manifest> |
| 712 | """) | 827 | """ |
| 713 | self.assertEqual(manifest.superproject.name, 'superproject') | 828 | ) |
| 714 | self.assertEqual(manifest.superproject.remote.name, 'default-remote') | 829 | self.assertEqual(manifest.superproject.name, "superproject") |
| 715 | self.assertEqual(manifest.superproject.revision, 'refs/heads/main') | 830 | self.assertEqual(manifest.superproject.remote.name, "default-remote") |
| 716 | self.assertEqual( | 831 | self.assertEqual(manifest.superproject.revision, "refs/heads/main") |
| 717 | sort_attributes(manifest.ToXml().toxml()), | 832 | self.assertEqual( |
| 718 | '<?xml version="1.0" ?><manifest>' | 833 | sort_attributes(manifest.ToXml().toxml()), |
| 719 | '<remote fetch="http://localhost" name="default-remote"/>' | 834 | '<?xml version="1.0" ?><manifest>' |
| 720 | '<default remote="default-remote" revision="refs/heads/main"/>' | 835 | '<remote fetch="http://localhost" name="default-remote"/>' |
| 721 | '<superproject name="superproject"/>' | 836 | '<default remote="default-remote" revision="refs/heads/main"/>' |
| 722 | '</manifest>') | 837 | '<superproject name="superproject"/>' |
| 838 | "</manifest>", | ||
| 839 | ) | ||
| 723 | 840 | ||
| 724 | 841 | ||
| 725 | class ContactinfoElementTests(ManifestParseTestCase): | 842 | class ContactinfoElementTests(ManifestParseTestCase): |
| 726 | """Tests for <contactinfo>.""" | 843 | """Tests for <contactinfo>.""" |
| 727 | 844 | ||
| 728 | def test_contactinfo(self): | 845 | def test_contactinfo(self): |
| 729 | """Check contactinfo settings.""" | 846 | """Check contactinfo settings.""" |
| 730 | bugurl = 'http://localhost/contactinfo' | 847 | bugurl = "http://localhost/contactinfo" |
| 731 | manifest = self.getXmlManifest(f""" | 848 | manifest = self.getXmlManifest( |
| 849 | f""" | ||
| 732 | <manifest> | 850 | <manifest> |
| 733 | <contactinfo bugurl="{bugurl}"/> | 851 | <contactinfo bugurl="{bugurl}"/> |
| 734 | </manifest> | 852 | </manifest> |
| 735 | """) | 853 | """ |
| 736 | self.assertEqual(manifest.contactinfo.bugurl, bugurl) | 854 | ) |
| 737 | self.assertEqual( | 855 | self.assertEqual(manifest.contactinfo.bugurl, bugurl) |
| 738 | manifest.ToXml().toxml(), | 856 | self.assertEqual( |
| 739 | '<?xml version="1.0" ?><manifest>' | 857 | manifest.ToXml().toxml(), |
| 740 | f'<contactinfo bugurl="{bugurl}"/>' | 858 | '<?xml version="1.0" ?><manifest>' |
| 741 | '</manifest>') | 859 | f'<contactinfo bugurl="{bugurl}"/>' |
| 860 | "</manifest>", | ||
| 861 | ) | ||
| 742 | 862 | ||
| 743 | 863 | ||
| 744 | class DefaultElementTests(ManifestParseTestCase): | 864 | class DefaultElementTests(ManifestParseTestCase): |
| 745 | """Tests for <default>.""" | 865 | """Tests for <default>.""" |
| 746 | 866 | ||
| 747 | def test_default(self): | 867 | def test_default(self): |
| 748 | """Check default settings.""" | 868 | """Check default settings.""" |
| 749 | a = manifest_xml._Default() | 869 | a = manifest_xml._Default() |
| 750 | a.revisionExpr = 'foo' | 870 | a.revisionExpr = "foo" |
| 751 | a.remote = manifest_xml._XmlRemote(name='remote') | 871 | a.remote = manifest_xml._XmlRemote(name="remote") |
| 752 | b = manifest_xml._Default() | 872 | b = manifest_xml._Default() |
| 753 | b.revisionExpr = 'bar' | 873 | b.revisionExpr = "bar" |
| 754 | self.assertEqual(a, a) | 874 | self.assertEqual(a, a) |
| 755 | self.assertNotEqual(a, b) | 875 | self.assertNotEqual(a, b) |
| 756 | self.assertNotEqual(b, a.remote) | 876 | self.assertNotEqual(b, a.remote) |
| 757 | self.assertNotEqual(a, 123) | 877 | self.assertNotEqual(a, 123) |
| 758 | self.assertNotEqual(a, None) | 878 | self.assertNotEqual(a, None) |
| 759 | 879 | ||
| 760 | 880 | ||
| 761 | class RemoteElementTests(ManifestParseTestCase): | 881 | class RemoteElementTests(ManifestParseTestCase): |
| 762 | """Tests for <remote>.""" | 882 | """Tests for <remote>.""" |
| 763 | 883 | ||
| 764 | def test_remote(self): | 884 | def test_remote(self): |
| 765 | """Check remote settings.""" | 885 | """Check remote settings.""" |
| 766 | a = manifest_xml._XmlRemote(name='foo') | 886 | a = manifest_xml._XmlRemote(name="foo") |
| 767 | a.AddAnnotation('key1', 'value1', 'true') | 887 | a.AddAnnotation("key1", "value1", "true") |
| 768 | b = manifest_xml._XmlRemote(name='foo') | 888 | b = manifest_xml._XmlRemote(name="foo") |
| 769 | b.AddAnnotation('key2', 'value1', 'true') | 889 | b.AddAnnotation("key2", "value1", "true") |
| 770 | c = manifest_xml._XmlRemote(name='foo') | 890 | c = manifest_xml._XmlRemote(name="foo") |
| 771 | c.AddAnnotation('key1', 'value2', 'true') | 891 | c.AddAnnotation("key1", "value2", "true") |
| 772 | d = manifest_xml._XmlRemote(name='foo') | 892 | d = manifest_xml._XmlRemote(name="foo") |
| 773 | d.AddAnnotation('key1', 'value1', 'false') | 893 | d.AddAnnotation("key1", "value1", "false") |
| 774 | self.assertEqual(a, a) | 894 | self.assertEqual(a, a) |
| 775 | self.assertNotEqual(a, b) | 895 | self.assertNotEqual(a, b) |
| 776 | self.assertNotEqual(a, c) | 896 | self.assertNotEqual(a, c) |
| 777 | self.assertNotEqual(a, d) | 897 | self.assertNotEqual(a, d) |
| 778 | self.assertNotEqual(a, manifest_xml._Default()) | 898 | self.assertNotEqual(a, manifest_xml._Default()) |
| 779 | self.assertNotEqual(a, 123) | 899 | self.assertNotEqual(a, 123) |
| 780 | self.assertNotEqual(a, None) | 900 | self.assertNotEqual(a, None) |
| 781 | 901 | ||
| 782 | 902 | ||
| 783 | class RemoveProjectElementTests(ManifestParseTestCase): | 903 | class RemoveProjectElementTests(ManifestParseTestCase): |
| 784 | """Tests for <remove-project>.""" | 904 | """Tests for <remove-project>.""" |
| 785 | 905 | ||
| 786 | def test_remove_one_project(self): | 906 | def test_remove_one_project(self): |
| 787 | manifest = self.getXmlManifest(""" | 907 | manifest = self.getXmlManifest( |
| 908 | """ | ||
| 788 | <manifest> | 909 | <manifest> |
| 789 | <remote name="default-remote" fetch="http://localhost" /> | 910 | <remote name="default-remote" fetch="http://localhost" /> |
| 790 | <default remote="default-remote" revision="refs/heads/main" /> | 911 | <default remote="default-remote" revision="refs/heads/main" /> |
| 791 | <project name="myproject" /> | 912 | <project name="myproject" /> |
| 792 | <remove-project name="myproject" /> | 913 | <remove-project name="myproject" /> |
| 793 | </manifest> | 914 | </manifest> |
| 794 | """) | 915 | """ |
| 795 | self.assertEqual(manifest.projects, []) | 916 | ) |
| 917 | self.assertEqual(manifest.projects, []) | ||
| 796 | 918 | ||
| 797 | def test_remove_one_project_one_remains(self): | 919 | def test_remove_one_project_one_remains(self): |
| 798 | manifest = self.getXmlManifest(""" | 920 | manifest = self.getXmlManifest( |
| 921 | """ | ||
| 799 | <manifest> | 922 | <manifest> |
| 800 | <remote name="default-remote" fetch="http://localhost" /> | 923 | <remote name="default-remote" fetch="http://localhost" /> |
| 801 | <default remote="default-remote" revision="refs/heads/main" /> | 924 | <default remote="default-remote" revision="refs/heads/main" /> |
| @@ -803,51 +926,59 @@ class RemoveProjectElementTests(ManifestParseTestCase): | |||
| 803 | <project name="yourproject" /> | 926 | <project name="yourproject" /> |
| 804 | <remove-project name="myproject" /> | 927 | <remove-project name="myproject" /> |
| 805 | </manifest> | 928 | </manifest> |
| 806 | """) | 929 | """ |
| 930 | ) | ||
| 807 | 931 | ||
| 808 | self.assertEqual(len(manifest.projects), 1) | 932 | self.assertEqual(len(manifest.projects), 1) |
| 809 | self.assertEqual(manifest.projects[0].name, 'yourproject') | 933 | self.assertEqual(manifest.projects[0].name, "yourproject") |
| 810 | 934 | ||
| 811 | def test_remove_one_project_doesnt_exist(self): | 935 | def test_remove_one_project_doesnt_exist(self): |
| 812 | with self.assertRaises(manifest_xml.ManifestParseError): | 936 | with self.assertRaises(manifest_xml.ManifestParseError): |
| 813 | manifest = self.getXmlManifest(""" | 937 | manifest = self.getXmlManifest( |
| 938 | """ | ||
| 814 | <manifest> | 939 | <manifest> |
| 815 | <remote name="default-remote" fetch="http://localhost" /> | 940 | <remote name="default-remote" fetch="http://localhost" /> |
| 816 | <default remote="default-remote" revision="refs/heads/main" /> | 941 | <default remote="default-remote" revision="refs/heads/main" /> |
| 817 | <remove-project name="myproject" /> | 942 | <remove-project name="myproject" /> |
| 818 | </manifest> | 943 | </manifest> |
| 819 | """) | 944 | """ |
| 820 | manifest.projects | 945 | ) |
| 946 | manifest.projects | ||
| 821 | 947 | ||
| 822 | def test_remove_one_optional_project_doesnt_exist(self): | 948 | def test_remove_one_optional_project_doesnt_exist(self): |
| 823 | manifest = self.getXmlManifest(""" | 949 | manifest = self.getXmlManifest( |
| 950 | """ | ||
| 824 | <manifest> | 951 | <manifest> |
| 825 | <remote name="default-remote" fetch="http://localhost" /> | 952 | <remote name="default-remote" fetch="http://localhost" /> |
| 826 | <default remote="default-remote" revision="refs/heads/main" /> | 953 | <default remote="default-remote" revision="refs/heads/main" /> |
| 827 | <remove-project name="myproject" optional="true" /> | 954 | <remove-project name="myproject" optional="true" /> |
| 828 | </manifest> | 955 | </manifest> |
| 829 | """) | 956 | """ |
| 830 | self.assertEqual(manifest.projects, []) | 957 | ) |
| 958 | self.assertEqual(manifest.projects, []) | ||
| 831 | 959 | ||
| 832 | 960 | ||
| 833 | class ExtendProjectElementTests(ManifestParseTestCase): | 961 | class ExtendProjectElementTests(ManifestParseTestCase): |
| 834 | """Tests for <extend-project>.""" | 962 | """Tests for <extend-project>.""" |
| 835 | 963 | ||
| 836 | def test_extend_project_dest_path_single_match(self): | 964 | def test_extend_project_dest_path_single_match(self): |
| 837 | manifest = self.getXmlManifest(""" | 965 | manifest = self.getXmlManifest( |
| 966 | """ | ||
| 838 | <manifest> | 967 | <manifest> |
| 839 | <remote name="default-remote" fetch="http://localhost" /> | 968 | <remote name="default-remote" fetch="http://localhost" /> |
| 840 | <default remote="default-remote" revision="refs/heads/main" /> | 969 | <default remote="default-remote" revision="refs/heads/main" /> |
| 841 | <project name="myproject" /> | 970 | <project name="myproject" /> |
| 842 | <extend-project name="myproject" dest-path="bar" /> | 971 | <extend-project name="myproject" dest-path="bar" /> |
| 843 | </manifest> | 972 | </manifest> |
| 844 | """) | 973 | """ |
| 845 | self.assertEqual(len(manifest.projects), 1) | 974 | ) |
| 846 | self.assertEqual(manifest.projects[0].relpath, 'bar') | 975 | self.assertEqual(len(manifest.projects), 1) |
| 847 | 976 | self.assertEqual(manifest.projects[0].relpath, "bar") | |
| 848 | def test_extend_project_dest_path_multi_match(self): | 977 | |
| 849 | with self.assertRaises(manifest_xml.ManifestParseError): | 978 | def test_extend_project_dest_path_multi_match(self): |
| 850 | manifest = self.getXmlManifest(""" | 979 | with self.assertRaises(manifest_xml.ManifestParseError): |
| 980 | manifest = self.getXmlManifest( | ||
| 981 | """ | ||
| 851 | <manifest> | 982 | <manifest> |
| 852 | <remote name="default-remote" fetch="http://localhost" /> | 983 | <remote name="default-remote" fetch="http://localhost" /> |
| 853 | <default remote="default-remote" revision="refs/heads/main" /> | 984 | <default remote="default-remote" revision="refs/heads/main" /> |
| @@ -855,11 +986,13 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 855 | <project name="myproject" path="y" /> | 986 | <project name="myproject" path="y" /> |
| 856 | <extend-project name="myproject" dest-path="bar" /> | 987 | <extend-project name="myproject" dest-path="bar" /> |
| 857 | </manifest> | 988 | </manifest> |
| 858 | """) | 989 | """ |
| 859 | manifest.projects | 990 | ) |
| 991 | manifest.projects | ||
| 860 | 992 | ||
| 861 | def test_extend_project_dest_path_multi_match_path_specified(self): | 993 | def test_extend_project_dest_path_multi_match_path_specified(self): |
| 862 | manifest = self.getXmlManifest(""" | 994 | manifest = self.getXmlManifest( |
| 995 | """ | ||
| 863 | <manifest> | 996 | <manifest> |
| 864 | <remote name="default-remote" fetch="http://localhost" /> | 997 | <remote name="default-remote" fetch="http://localhost" /> |
| 865 | <default remote="default-remote" revision="refs/heads/main" /> | 998 | <default remote="default-remote" revision="refs/heads/main" /> |
| @@ -867,34 +1000,39 @@ class ExtendProjectElementTests(ManifestParseTestCase): | |||
| 867 | <project name="myproject" path="y" /> | 1000 | <project name="myproject" path="y" /> |
| 868 | <extend-project name="myproject" path="x" dest-path="bar" /> | 1001 | <extend-project name="myproject" path="x" dest-path="bar" /> |
| 869 | </manifest> | 1002 | </manifest> |
| 870 | """) | 1003 | """ |
| 871 | self.assertEqual(len(manifest.projects), 2) | 1004 | ) |
| 872 | if manifest.projects[0].relpath == 'y': | 1005 | self.assertEqual(len(manifest.projects), 2) |
| 873 | self.assertEqual(manifest.projects[1].relpath, 'bar') | 1006 | if manifest.projects[0].relpath == "y": |
| 874 | else: | 1007 | self.assertEqual(manifest.projects[1].relpath, "bar") |
| 875 | self.assertEqual(manifest.projects[0].relpath, 'bar') | 1008 | else: |
| 876 | self.assertEqual(manifest.projects[1].relpath, 'y') | 1009 | self.assertEqual(manifest.projects[0].relpath, "bar") |
| 877 | 1010 | self.assertEqual(manifest.projects[1].relpath, "y") | |
| 878 | def test_extend_project_dest_branch(self): | 1011 | |
| 879 | manifest = self.getXmlManifest(""" | 1012 | def test_extend_project_dest_branch(self): |
| 1013 | manifest = self.getXmlManifest( | ||
| 1014 | """ | ||
| 880 | <manifest> | 1015 | <manifest> |
| 881 | <remote name="default-remote" fetch="http://localhost" /> | 1016 | <remote name="default-remote" fetch="http://localhost" /> |
| 882 | <default remote="default-remote" revision="refs/heads/main" dest-branch="foo" /> | 1017 | <default remote="default-remote" revision="refs/heads/main" dest-branch="foo" /> |
| 883 | <project name="myproject" /> | 1018 | <project name="myproject" /> |
| 884 | <extend-project name="myproject" dest-branch="bar" /> | 1019 | <extend-project name="myproject" dest-branch="bar" /> |
| 885 | </manifest> | 1020 | </manifest> |
| 886 | """) | 1021 | """ # noqa: E501 |
| 887 | self.assertEqual(len(manifest.projects), 1) | 1022 | ) |
| 888 | self.assertEqual(manifest.projects[0].dest_branch, 'bar') | 1023 | self.assertEqual(len(manifest.projects), 1) |
| 889 | 1024 | self.assertEqual(manifest.projects[0].dest_branch, "bar") | |
| 890 | def test_extend_project_upstream(self): | 1025 | |
| 891 | manifest = self.getXmlManifest(""" | 1026 | def test_extend_project_upstream(self): |
| 1027 | manifest = self.getXmlManifest( | ||
| 1028 | """ | ||
| 892 | <manifest> | 1029 | <manifest> |
| 893 | <remote name="default-remote" fetch="http://localhost" /> | 1030 | <remote name="default-remote" fetch="http://localhost" /> |
| 894 | <default remote="default-remote" revision="refs/heads/main" /> | 1031 | <default remote="default-remote" revision="refs/heads/main" /> |
| 895 | <project name="myproject" /> | 1032 | <project name="myproject" /> |
| 896 | <extend-project name="myproject" upstream="bar" /> | 1033 | <extend-project name="myproject" upstream="bar" /> |
| 897 | </manifest> | 1034 | </manifest> |
| 898 | """) | 1035 | """ |
| 899 | self.assertEqual(len(manifest.projects), 1) | 1036 | ) |
| 900 | self.assertEqual(manifest.projects[0].upstream, 'bar') | 1037 | self.assertEqual(len(manifest.projects), 1) |
| 1038 | self.assertEqual(manifest.projects[0].upstream, "bar") | ||
diff --git a/tests/test_platform_utils.py b/tests/test_platform_utils.py index 55b7805c..7a42de01 100644 --- a/tests/test_platform_utils.py +++ b/tests/test_platform_utils.py | |||
| @@ -22,29 +22,31 @@ import platform_utils | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class RemoveTests(unittest.TestCase): | 24 | class RemoveTests(unittest.TestCase): |
| 25 | """Check remove() helper.""" | 25 | """Check remove() helper.""" |
| 26 | 26 | ||
| 27 | def testMissingOk(self): | 27 | def testMissingOk(self): |
| 28 | """Check missing_ok handling.""" | 28 | """Check missing_ok handling.""" |
| 29 | with tempfile.TemporaryDirectory() as tmpdir: | 29 | with tempfile.TemporaryDirectory() as tmpdir: |
| 30 | path = os.path.join(tmpdir, 'test') | 30 | path = os.path.join(tmpdir, "test") |
| 31 | 31 | ||
| 32 | # Should not fail. | 32 | # Should not fail. |
| 33 | platform_utils.remove(path, missing_ok=True) | 33 | platform_utils.remove(path, missing_ok=True) |
| 34 | 34 | ||
| 35 | # Should fail. | 35 | # Should fail. |
| 36 | self.assertRaises(OSError, platform_utils.remove, path) | 36 | self.assertRaises(OSError, platform_utils.remove, path) |
| 37 | self.assertRaises(OSError, platform_utils.remove, path, missing_ok=False) | 37 | self.assertRaises( |
| 38 | 38 | OSError, platform_utils.remove, path, missing_ok=False | |
| 39 | # Should not fail if it exists. | 39 | ) |
| 40 | open(path, 'w').close() | 40 | |
| 41 | platform_utils.remove(path, missing_ok=True) | 41 | # Should not fail if it exists. |
| 42 | self.assertFalse(os.path.exists(path)) | 42 | open(path, "w").close() |
| 43 | 43 | platform_utils.remove(path, missing_ok=True) | |
| 44 | open(path, 'w').close() | 44 | self.assertFalse(os.path.exists(path)) |
| 45 | platform_utils.remove(path) | 45 | |
| 46 | self.assertFalse(os.path.exists(path)) | 46 | open(path, "w").close() |
| 47 | 47 | platform_utils.remove(path) | |
| 48 | open(path, 'w').close() | 48 | self.assertFalse(os.path.exists(path)) |
| 49 | platform_utils.remove(path, missing_ok=False) | 49 | |
| 50 | self.assertFalse(os.path.exists(path)) | 50 | open(path, "w").close() |
| 51 | platform_utils.remove(path, missing_ok=False) | ||
| 52 | self.assertFalse(os.path.exists(path)) | ||
diff --git a/tests/test_project.py b/tests/test_project.py index c50d9940..bc8330b2 100644 --- a/tests/test_project.py +++ b/tests/test_project.py | |||
| @@ -31,452 +31,493 @@ import project | |||
| 31 | 31 | ||
| 32 | @contextlib.contextmanager | 32 | @contextlib.contextmanager |
| 33 | def TempGitTree(): | 33 | def TempGitTree(): |
| 34 | """Create a new empty git checkout for testing.""" | 34 | """Create a new empty git checkout for testing.""" |
| 35 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 35 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 36 | # Tests need to assume, that main is default branch at init, | 36 | # Tests need to assume, that main is default branch at init, |
| 37 | # which is not supported in config until 2.28. | 37 | # which is not supported in config until 2.28. |
| 38 | cmd = ['git', 'init'] | 38 | cmd = ["git", "init"] |
| 39 | if git_command.git_require((2, 28, 0)): | 39 | if git_command.git_require((2, 28, 0)): |
| 40 | cmd += ['--initial-branch=main'] | 40 | cmd += ["--initial-branch=main"] |
| 41 | else: | 41 | else: |
| 42 | # Use template dir for init. | 42 | # Use template dir for init. |
| 43 | templatedir = tempfile.mkdtemp(prefix='.test-template') | 43 | templatedir = tempfile.mkdtemp(prefix=".test-template") |
| 44 | with open(os.path.join(templatedir, 'HEAD'), 'w') as fp: | 44 | with open(os.path.join(templatedir, "HEAD"), "w") as fp: |
| 45 | fp.write('ref: refs/heads/main\n') | 45 | fp.write("ref: refs/heads/main\n") |
| 46 | cmd += ['--template', templatedir] | 46 | cmd += ["--template", templatedir] |
| 47 | subprocess.check_call(cmd, cwd=tempdir) | 47 | subprocess.check_call(cmd, cwd=tempdir) |
| 48 | yield tempdir | 48 | yield tempdir |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | class FakeProject(object): | 51 | class FakeProject(object): |
| 52 | """A fake for Project for basic functionality.""" | 52 | """A fake for Project for basic functionality.""" |
| 53 | 53 | ||
| 54 | def __init__(self, worktree): | 54 | def __init__(self, worktree): |
| 55 | self.worktree = worktree | 55 | self.worktree = worktree |
| 56 | self.gitdir = os.path.join(worktree, '.git') | 56 | self.gitdir = os.path.join(worktree, ".git") |
| 57 | self.name = 'fakeproject' | 57 | self.name = "fakeproject" |
| 58 | self.work_git = project.Project._GitGetByExec( | 58 | self.work_git = project.Project._GitGetByExec( |
| 59 | self, bare=False, gitdir=self.gitdir) | 59 | self, bare=False, gitdir=self.gitdir |
| 60 | self.bare_git = project.Project._GitGetByExec( | 60 | ) |
| 61 | self, bare=True, gitdir=self.gitdir) | 61 | self.bare_git = project.Project._GitGetByExec( |
| 62 | self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) | 62 | self, bare=True, gitdir=self.gitdir |
| 63 | ) | ||
| 64 | self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) | ||
| 63 | 65 | ||
| 64 | 66 | ||
| 65 | class ReviewableBranchTests(unittest.TestCase): | 67 | class ReviewableBranchTests(unittest.TestCase): |
| 66 | """Check ReviewableBranch behavior.""" | 68 | """Check ReviewableBranch behavior.""" |
| 67 | 69 | ||
| 68 | def test_smoke(self): | 70 | def test_smoke(self): |
| 69 | """A quick run through everything.""" | 71 | """A quick run through everything.""" |
| 70 | with TempGitTree() as tempdir: | 72 | with TempGitTree() as tempdir: |
| 71 | fakeproj = FakeProject(tempdir) | 73 | fakeproj = FakeProject(tempdir) |
| 72 | 74 | ||
| 73 | # Generate some commits. | 75 | # Generate some commits. |
| 74 | with open(os.path.join(tempdir, 'readme'), 'w') as fp: | 76 | with open(os.path.join(tempdir, "readme"), "w") as fp: |
| 75 | fp.write('txt') | 77 | fp.write("txt") |
| 76 | fakeproj.work_git.add('readme') | 78 | fakeproj.work_git.add("readme") |
| 77 | fakeproj.work_git.commit('-mAdd file') | 79 | fakeproj.work_git.commit("-mAdd file") |
| 78 | fakeproj.work_git.checkout('-b', 'work') | 80 | fakeproj.work_git.checkout("-b", "work") |
| 79 | fakeproj.work_git.rm('-f', 'readme') | 81 | fakeproj.work_git.rm("-f", "readme") |
| 80 | fakeproj.work_git.commit('-mDel file') | 82 | fakeproj.work_git.commit("-mDel file") |
| 81 | 83 | ||
| 82 | # Start off with the normal details. | 84 | # Start off with the normal details. |
| 83 | rb = project.ReviewableBranch( | 85 | rb = project.ReviewableBranch( |
| 84 | fakeproj, fakeproj.config.GetBranch('work'), 'main') | 86 | fakeproj, fakeproj.config.GetBranch("work"), "main" |
| 85 | self.assertEqual('work', rb.name) | 87 | ) |
| 86 | self.assertEqual(1, len(rb.commits)) | 88 | self.assertEqual("work", rb.name) |
| 87 | self.assertIn('Del file', rb.commits[0]) | 89 | self.assertEqual(1, len(rb.commits)) |
| 88 | d = rb.unabbrev_commits | 90 | self.assertIn("Del file", rb.commits[0]) |
| 89 | self.assertEqual(1, len(d)) | 91 | d = rb.unabbrev_commits |
| 90 | short, long = next(iter(d.items())) | 92 | self.assertEqual(1, len(d)) |
| 91 | self.assertTrue(long.startswith(short)) | 93 | short, long = next(iter(d.items())) |
| 92 | self.assertTrue(rb.base_exists) | 94 | self.assertTrue(long.startswith(short)) |
| 93 | # Hard to assert anything useful about this. | 95 | self.assertTrue(rb.base_exists) |
| 94 | self.assertTrue(rb.date) | 96 | # Hard to assert anything useful about this. |
| 95 | 97 | self.assertTrue(rb.date) | |
| 96 | # Now delete the tracking branch! | 98 | |
| 97 | fakeproj.work_git.branch('-D', 'main') | 99 | # Now delete the tracking branch! |
| 98 | rb = project.ReviewableBranch( | 100 | fakeproj.work_git.branch("-D", "main") |
| 99 | fakeproj, fakeproj.config.GetBranch('work'), 'main') | 101 | rb = project.ReviewableBranch( |
| 100 | self.assertEqual(0, len(rb.commits)) | 102 | fakeproj, fakeproj.config.GetBranch("work"), "main" |
| 101 | self.assertFalse(rb.base_exists) | 103 | ) |
| 102 | # Hard to assert anything useful about this. | 104 | self.assertEqual(0, len(rb.commits)) |
| 103 | self.assertTrue(rb.date) | 105 | self.assertFalse(rb.base_exists) |
| 106 | # Hard to assert anything useful about this. | ||
| 107 | self.assertTrue(rb.date) | ||
| 104 | 108 | ||
| 105 | 109 | ||
| 106 | class CopyLinkTestCase(unittest.TestCase): | 110 | class CopyLinkTestCase(unittest.TestCase): |
| 107 | """TestCase for stub repo client checkouts. | 111 | """TestCase for stub repo client checkouts. |
| 108 | 112 | ||
| 109 | It'll have a layout like this: | 113 | It'll have a layout like this: |
| 110 | tempdir/ # self.tempdir | 114 | tempdir/ # self.tempdir |
| 111 | checkout/ # self.topdir | 115 | checkout/ # self.topdir |
| 112 | git-project/ # self.worktree | 116 | git-project/ # self.worktree |
| 113 | 117 | ||
| 114 | Attributes: | 118 | Attributes: |
| 115 | tempdir: A dedicated temporary directory. | 119 | tempdir: A dedicated temporary directory. |
| 116 | worktree: The top of the repo client checkout. | 120 | worktree: The top of the repo client checkout. |
| 117 | topdir: The top of a project checkout. | 121 | topdir: The top of a project checkout. |
| 118 | """ | 122 | """ |
| 119 | 123 | ||
| 120 | def setUp(self): | 124 | def setUp(self): |
| 121 | self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests') | 125 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests") |
| 122 | self.tempdir = self.tempdirobj.name | 126 | self.tempdir = self.tempdirobj.name |
| 123 | self.topdir = os.path.join(self.tempdir, 'checkout') | 127 | self.topdir = os.path.join(self.tempdir, "checkout") |
| 124 | self.worktree = os.path.join(self.topdir, 'git-project') | 128 | self.worktree = os.path.join(self.topdir, "git-project") |
| 125 | os.makedirs(self.topdir) | 129 | os.makedirs(self.topdir) |
| 126 | os.makedirs(self.worktree) | 130 | os.makedirs(self.worktree) |
| 127 | 131 | ||
| 128 | def tearDown(self): | 132 | def tearDown(self): |
| 129 | self.tempdirobj.cleanup() | 133 | self.tempdirobj.cleanup() |
| 130 | 134 | ||
| 131 | @staticmethod | 135 | @staticmethod |
| 132 | def touch(path): | 136 | def touch(path): |
| 133 | with open(path, 'w'): | 137 | with open(path, "w"): |
| 134 | pass | 138 | pass |
| 135 | 139 | ||
| 136 | def assertExists(self, path, msg=None): | 140 | def assertExists(self, path, msg=None): |
| 137 | """Make sure |path| exists.""" | 141 | """Make sure |path| exists.""" |
| 138 | if os.path.exists(path): | 142 | if os.path.exists(path): |
| 139 | return | 143 | return |
| 140 | 144 | ||
| 141 | if msg is None: | 145 | if msg is None: |
| 142 | msg = ['path is missing: %s' % path] | 146 | msg = ["path is missing: %s" % path] |
| 143 | while path != '/': | 147 | while path != "/": |
| 144 | path = os.path.dirname(path) | 148 | path = os.path.dirname(path) |
| 145 | if not path: | 149 | if not path: |
| 146 | # If we're given something like "foo", abort once we get to "". | 150 | # If we're given something like "foo", abort once we get to |
| 147 | break | 151 | # "". |
| 148 | result = os.path.exists(path) | 152 | break |
| 149 | msg.append('\tos.path.exists(%s): %s' % (path, result)) | 153 | result = os.path.exists(path) |
| 150 | if result: | 154 | msg.append("\tos.path.exists(%s): %s" % (path, result)) |
| 151 | msg.append('\tcontents: %r' % os.listdir(path)) | 155 | if result: |
| 152 | break | 156 | msg.append("\tcontents: %r" % os.listdir(path)) |
| 153 | msg = '\n'.join(msg) | 157 | break |
| 154 | 158 | msg = "\n".join(msg) | |
| 155 | raise self.failureException(msg) | 159 | |
| 160 | raise self.failureException(msg) | ||
| 156 | 161 | ||
| 157 | 162 | ||
| 158 | class CopyFile(CopyLinkTestCase): | 163 | class CopyFile(CopyLinkTestCase): |
| 159 | """Check _CopyFile handling.""" | 164 | """Check _CopyFile handling.""" |
| 160 | 165 | ||
| 161 | def CopyFile(self, src, dest): | 166 | def CopyFile(self, src, dest): |
| 162 | return project._CopyFile(self.worktree, src, self.topdir, dest) | 167 | return project._CopyFile(self.worktree, src, self.topdir, dest) |
| 163 | 168 | ||
| 164 | def test_basic(self): | 169 | def test_basic(self): |
| 165 | """Basic test of copying a file from a project to the toplevel.""" | 170 | """Basic test of copying a file from a project to the toplevel.""" |
| 166 | src = os.path.join(self.worktree, 'foo.txt') | 171 | src = os.path.join(self.worktree, "foo.txt") |
| 167 | self.touch(src) | 172 | self.touch(src) |
| 168 | cf = self.CopyFile('foo.txt', 'foo') | 173 | cf = self.CopyFile("foo.txt", "foo") |
| 169 | cf._Copy() | 174 | cf._Copy() |
| 170 | self.assertExists(os.path.join(self.topdir, 'foo')) | 175 | self.assertExists(os.path.join(self.topdir, "foo")) |
| 171 | 176 | ||
| 172 | def test_src_subdir(self): | 177 | def test_src_subdir(self): |
| 173 | """Copy a file from a subdir of a project.""" | 178 | """Copy a file from a subdir of a project.""" |
| 174 | src = os.path.join(self.worktree, 'bar', 'foo.txt') | 179 | src = os.path.join(self.worktree, "bar", "foo.txt") |
| 175 | os.makedirs(os.path.dirname(src)) | 180 | os.makedirs(os.path.dirname(src)) |
| 176 | self.touch(src) | 181 | self.touch(src) |
| 177 | cf = self.CopyFile('bar/foo.txt', 'new.txt') | 182 | cf = self.CopyFile("bar/foo.txt", "new.txt") |
| 178 | cf._Copy() | 183 | cf._Copy() |
| 179 | self.assertExists(os.path.join(self.topdir, 'new.txt')) | 184 | self.assertExists(os.path.join(self.topdir, "new.txt")) |
| 180 | 185 | ||
| 181 | def test_dest_subdir(self): | 186 | def test_dest_subdir(self): |
| 182 | """Copy a file to a subdir of a checkout.""" | 187 | """Copy a file to a subdir of a checkout.""" |
| 183 | src = os.path.join(self.worktree, 'foo.txt') | 188 | src = os.path.join(self.worktree, "foo.txt") |
| 184 | self.touch(src) | 189 | self.touch(src) |
| 185 | cf = self.CopyFile('foo.txt', 'sub/dir/new.txt') | 190 | cf = self.CopyFile("foo.txt", "sub/dir/new.txt") |
| 186 | self.assertFalse(os.path.exists(os.path.join(self.topdir, 'sub'))) | 191 | self.assertFalse(os.path.exists(os.path.join(self.topdir, "sub"))) |
| 187 | cf._Copy() | 192 | cf._Copy() |
| 188 | self.assertExists(os.path.join(self.topdir, 'sub', 'dir', 'new.txt')) | 193 | self.assertExists(os.path.join(self.topdir, "sub", "dir", "new.txt")) |
| 189 | 194 | ||
| 190 | def test_update(self): | 195 | def test_update(self): |
| 191 | """Make sure changed files get copied again.""" | 196 | """Make sure changed files get copied again.""" |
| 192 | src = os.path.join(self.worktree, 'foo.txt') | 197 | src = os.path.join(self.worktree, "foo.txt") |
| 193 | dest = os.path.join(self.topdir, 'bar') | 198 | dest = os.path.join(self.topdir, "bar") |
| 194 | with open(src, 'w') as f: | 199 | with open(src, "w") as f: |
| 195 | f.write('1st') | 200 | f.write("1st") |
| 196 | cf = self.CopyFile('foo.txt', 'bar') | 201 | cf = self.CopyFile("foo.txt", "bar") |
| 197 | cf._Copy() | 202 | cf._Copy() |
| 198 | self.assertExists(dest) | 203 | self.assertExists(dest) |
| 199 | with open(dest) as f: | 204 | with open(dest) as f: |
| 200 | self.assertEqual(f.read(), '1st') | 205 | self.assertEqual(f.read(), "1st") |
| 201 | 206 | ||
| 202 | with open(src, 'w') as f: | 207 | with open(src, "w") as f: |
| 203 | f.write('2nd!') | 208 | f.write("2nd!") |
| 204 | cf._Copy() | 209 | cf._Copy() |
| 205 | with open(dest) as f: | 210 | with open(dest) as f: |
| 206 | self.assertEqual(f.read(), '2nd!') | 211 | self.assertEqual(f.read(), "2nd!") |
| 207 | 212 | ||
| 208 | def test_src_block_symlink(self): | 213 | def test_src_block_symlink(self): |
| 209 | """Do not allow reading from a symlinked path.""" | 214 | """Do not allow reading from a symlinked path.""" |
| 210 | src = os.path.join(self.worktree, 'foo.txt') | 215 | src = os.path.join(self.worktree, "foo.txt") |
| 211 | sym = os.path.join(self.worktree, 'sym') | 216 | sym = os.path.join(self.worktree, "sym") |
| 212 | self.touch(src) | 217 | self.touch(src) |
| 213 | platform_utils.symlink('foo.txt', sym) | 218 | platform_utils.symlink("foo.txt", sym) |
| 214 | self.assertExists(sym) | 219 | self.assertExists(sym) |
| 215 | cf = self.CopyFile('sym', 'foo') | 220 | cf = self.CopyFile("sym", "foo") |
| 216 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 221 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 217 | 222 | ||
| 218 | def test_src_block_symlink_traversal(self): | 223 | def test_src_block_symlink_traversal(self): |
| 219 | """Do not allow reading through a symlink dir.""" | 224 | """Do not allow reading through a symlink dir.""" |
| 220 | realfile = os.path.join(self.tempdir, 'file.txt') | 225 | realfile = os.path.join(self.tempdir, "file.txt") |
| 221 | self.touch(realfile) | 226 | self.touch(realfile) |
| 222 | src = os.path.join(self.worktree, 'bar', 'file.txt') | 227 | src = os.path.join(self.worktree, "bar", "file.txt") |
| 223 | platform_utils.symlink(self.tempdir, os.path.join(self.worktree, 'bar')) | 228 | platform_utils.symlink(self.tempdir, os.path.join(self.worktree, "bar")) |
| 224 | self.assertExists(src) | 229 | self.assertExists(src) |
| 225 | cf = self.CopyFile('bar/file.txt', 'foo') | 230 | cf = self.CopyFile("bar/file.txt", "foo") |
| 226 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 231 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 227 | 232 | ||
| 228 | def test_src_block_copy_from_dir(self): | 233 | def test_src_block_copy_from_dir(self): |
| 229 | """Do not allow copying from a directory.""" | 234 | """Do not allow copying from a directory.""" |
| 230 | src = os.path.join(self.worktree, 'dir') | 235 | src = os.path.join(self.worktree, "dir") |
| 231 | os.makedirs(src) | 236 | os.makedirs(src) |
| 232 | cf = self.CopyFile('dir', 'foo') | 237 | cf = self.CopyFile("dir", "foo") |
| 233 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 238 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 234 | 239 | ||
| 235 | def test_dest_block_symlink(self): | 240 | def test_dest_block_symlink(self): |
| 236 | """Do not allow writing to a symlink.""" | 241 | """Do not allow writing to a symlink.""" |
| 237 | src = os.path.join(self.worktree, 'foo.txt') | 242 | src = os.path.join(self.worktree, "foo.txt") |
| 238 | self.touch(src) | 243 | self.touch(src) |
| 239 | platform_utils.symlink('dest', os.path.join(self.topdir, 'sym')) | 244 | platform_utils.symlink("dest", os.path.join(self.topdir, "sym")) |
| 240 | cf = self.CopyFile('foo.txt', 'sym') | 245 | cf = self.CopyFile("foo.txt", "sym") |
| 241 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 246 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 242 | 247 | ||
| 243 | def test_dest_block_symlink_traversal(self): | 248 | def test_dest_block_symlink_traversal(self): |
| 244 | """Do not allow writing through a symlink dir.""" | 249 | """Do not allow writing through a symlink dir.""" |
| 245 | src = os.path.join(self.worktree, 'foo.txt') | 250 | src = os.path.join(self.worktree, "foo.txt") |
| 246 | self.touch(src) | 251 | self.touch(src) |
| 247 | platform_utils.symlink(tempfile.gettempdir(), | 252 | platform_utils.symlink( |
| 248 | os.path.join(self.topdir, 'sym')) | 253 | tempfile.gettempdir(), os.path.join(self.topdir, "sym") |
| 249 | cf = self.CopyFile('foo.txt', 'sym/foo.txt') | 254 | ) |
| 250 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 255 | cf = self.CopyFile("foo.txt", "sym/foo.txt") |
| 251 | 256 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | |
| 252 | def test_src_block_copy_to_dir(self): | 257 | |
| 253 | """Do not allow copying to a directory.""" | 258 | def test_src_block_copy_to_dir(self): |
| 254 | src = os.path.join(self.worktree, 'foo.txt') | 259 | """Do not allow copying to a directory.""" |
| 255 | self.touch(src) | 260 | src = os.path.join(self.worktree, "foo.txt") |
| 256 | os.makedirs(os.path.join(self.topdir, 'dir')) | 261 | self.touch(src) |
| 257 | cf = self.CopyFile('foo.txt', 'dir') | 262 | os.makedirs(os.path.join(self.topdir, "dir")) |
| 258 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 263 | cf = self.CopyFile("foo.txt", "dir") |
| 264 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | ||
| 259 | 265 | ||
| 260 | 266 | ||
| 261 | class LinkFile(CopyLinkTestCase): | 267 | class LinkFile(CopyLinkTestCase): |
| 262 | """Check _LinkFile handling.""" | 268 | """Check _LinkFile handling.""" |
| 263 | 269 | ||
| 264 | def LinkFile(self, src, dest): | 270 | def LinkFile(self, src, dest): |
| 265 | return project._LinkFile(self.worktree, src, self.topdir, dest) | 271 | return project._LinkFile(self.worktree, src, self.topdir, dest) |
| 266 | 272 | ||
| 267 | def test_basic(self): | 273 | def test_basic(self): |
| 268 | """Basic test of linking a file from a project into the toplevel.""" | 274 | """Basic test of linking a file from a project into the toplevel.""" |
| 269 | src = os.path.join(self.worktree, 'foo.txt') | 275 | src = os.path.join(self.worktree, "foo.txt") |
| 270 | self.touch(src) | 276 | self.touch(src) |
| 271 | lf = self.LinkFile('foo.txt', 'foo') | 277 | lf = self.LinkFile("foo.txt", "foo") |
| 272 | lf._Link() | 278 | lf._Link() |
| 273 | dest = os.path.join(self.topdir, 'foo') | 279 | dest = os.path.join(self.topdir, "foo") |
| 274 | self.assertExists(dest) | 280 | self.assertExists(dest) |
| 275 | self.assertTrue(os.path.islink(dest)) | 281 | self.assertTrue(os.path.islink(dest)) |
| 276 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 282 | self.assertEqual( |
| 277 | 283 | os.path.join("git-project", "foo.txt"), os.readlink(dest) | |
| 278 | def test_src_subdir(self): | 284 | ) |
| 279 | """Link to a file in a subdir of a project.""" | 285 | |
| 280 | src = os.path.join(self.worktree, 'bar', 'foo.txt') | 286 | def test_src_subdir(self): |
| 281 | os.makedirs(os.path.dirname(src)) | 287 | """Link to a file in a subdir of a project.""" |
| 282 | self.touch(src) | 288 | src = os.path.join(self.worktree, "bar", "foo.txt") |
| 283 | lf = self.LinkFile('bar/foo.txt', 'foo') | 289 | os.makedirs(os.path.dirname(src)) |
| 284 | lf._Link() | 290 | self.touch(src) |
| 285 | self.assertExists(os.path.join(self.topdir, 'foo')) | 291 | lf = self.LinkFile("bar/foo.txt", "foo") |
| 286 | 292 | lf._Link() | |
| 287 | def test_src_self(self): | 293 | self.assertExists(os.path.join(self.topdir, "foo")) |
| 288 | """Link to the project itself.""" | 294 | |
| 289 | dest = os.path.join(self.topdir, 'foo', 'bar') | 295 | def test_src_self(self): |
| 290 | lf = self.LinkFile('.', 'foo/bar') | 296 | """Link to the project itself.""" |
| 291 | lf._Link() | 297 | dest = os.path.join(self.topdir, "foo", "bar") |
| 292 | self.assertExists(dest) | 298 | lf = self.LinkFile(".", "foo/bar") |
| 293 | self.assertEqual(os.path.join('..', 'git-project'), os.readlink(dest)) | 299 | lf._Link() |
| 294 | 300 | self.assertExists(dest) | |
| 295 | def test_dest_subdir(self): | 301 | self.assertEqual(os.path.join("..", "git-project"), os.readlink(dest)) |
| 296 | """Link a file to a subdir of a checkout.""" | 302 | |
| 297 | src = os.path.join(self.worktree, 'foo.txt') | 303 | def test_dest_subdir(self): |
| 298 | self.touch(src) | 304 | """Link a file to a subdir of a checkout.""" |
| 299 | lf = self.LinkFile('foo.txt', 'sub/dir/foo/bar') | 305 | src = os.path.join(self.worktree, "foo.txt") |
| 300 | self.assertFalse(os.path.exists(os.path.join(self.topdir, 'sub'))) | 306 | self.touch(src) |
| 301 | lf._Link() | 307 | lf = self.LinkFile("foo.txt", "sub/dir/foo/bar") |
| 302 | self.assertExists(os.path.join(self.topdir, 'sub', 'dir', 'foo', 'bar')) | 308 | self.assertFalse(os.path.exists(os.path.join(self.topdir, "sub"))) |
| 303 | 309 | lf._Link() | |
| 304 | def test_src_block_relative(self): | 310 | self.assertExists(os.path.join(self.topdir, "sub", "dir", "foo", "bar")) |
| 305 | """Do not allow relative symlinks.""" | 311 | |
| 306 | BAD_SOURCES = ( | 312 | def test_src_block_relative(self): |
| 307 | './', | 313 | """Do not allow relative symlinks.""" |
| 308 | '..', | 314 | BAD_SOURCES = ( |
| 309 | '../', | 315 | "./", |
| 310 | 'foo/.', | 316 | "..", |
| 311 | 'foo/./bar', | 317 | "../", |
| 312 | 'foo/..', | 318 | "foo/.", |
| 313 | 'foo/../foo', | 319 | "foo/./bar", |
| 314 | ) | 320 | "foo/..", |
| 315 | for src in BAD_SOURCES: | 321 | "foo/../foo", |
| 316 | lf = self.LinkFile(src, 'foo') | 322 | ) |
| 317 | self.assertRaises(error.ManifestInvalidPathError, lf._Link) | 323 | for src in BAD_SOURCES: |
| 318 | 324 | lf = self.LinkFile(src, "foo") | |
| 319 | def test_update(self): | 325 | self.assertRaises(error.ManifestInvalidPathError, lf._Link) |
| 320 | """Make sure changed targets get updated.""" | 326 | |
| 321 | dest = os.path.join(self.topdir, 'sym') | 327 | def test_update(self): |
| 322 | 328 | """Make sure changed targets get updated.""" | |
| 323 | src = os.path.join(self.worktree, 'foo.txt') | 329 | dest = os.path.join(self.topdir, "sym") |
| 324 | self.touch(src) | 330 | |
| 325 | lf = self.LinkFile('foo.txt', 'sym') | 331 | src = os.path.join(self.worktree, "foo.txt") |
| 326 | lf._Link() | 332 | self.touch(src) |
| 327 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 333 | lf = self.LinkFile("foo.txt", "sym") |
| 328 | 334 | lf._Link() | |
| 329 | # Point the symlink somewhere else. | 335 | self.assertEqual( |
| 330 | os.unlink(dest) | 336 | os.path.join("git-project", "foo.txt"), os.readlink(dest) |
| 331 | platform_utils.symlink(self.tempdir, dest) | 337 | ) |
| 332 | lf._Link() | 338 | |
| 333 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 339 | # Point the symlink somewhere else. |
| 340 | os.unlink(dest) | ||
| 341 | platform_utils.symlink(self.tempdir, dest) | ||
| 342 | lf._Link() | ||
| 343 | self.assertEqual( | ||
| 344 | os.path.join("git-project", "foo.txt"), os.readlink(dest) | ||
| 345 | ) | ||
| 334 | 346 | ||
| 335 | 347 | ||
| 336 | class MigrateWorkTreeTests(unittest.TestCase): | 348 | class MigrateWorkTreeTests(unittest.TestCase): |
| 337 | """Check _MigrateOldWorkTreeGitDir handling.""" | 349 | """Check _MigrateOldWorkTreeGitDir handling.""" |
| 338 | 350 | ||
| 339 | _SYMLINKS = { | 351 | _SYMLINKS = { |
| 340 | 'config', 'description', 'hooks', 'info', 'logs', 'objects', | 352 | "config", |
| 341 | 'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn', | 353 | "description", |
| 342 | } | 354 | "hooks", |
| 343 | _FILES = { | 355 | "info", |
| 344 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD', | 356 | "logs", |
| 345 | 'unknown-file-should-be-migrated', | 357 | "objects", |
| 346 | } | 358 | "packed-refs", |
| 347 | _CLEAN_FILES = { | 359 | "refs", |
| 348 | 'a-vim-temp-file~', '#an-emacs-temp-file#', | 360 | "rr-cache", |
| 349 | } | 361 | "shallow", |
| 350 | 362 | "svn", | |
| 351 | @classmethod | 363 | } |
| 352 | @contextlib.contextmanager | 364 | _FILES = { |
| 353 | def _simple_layout(cls): | 365 | "COMMIT_EDITMSG", |
| 354 | """Create a simple repo client checkout to test against.""" | 366 | "FETCH_HEAD", |
| 355 | with tempfile.TemporaryDirectory() as tempdir: | 367 | "HEAD", |
| 356 | tempdir = Path(tempdir) | 368 | "index", |
| 357 | 369 | "ORIG_HEAD", | |
| 358 | gitdir = tempdir / '.repo/projects/src/test.git' | 370 | "unknown-file-should-be-migrated", |
| 359 | gitdir.mkdir(parents=True) | 371 | } |
| 360 | cmd = ['git', 'init', '--bare', str(gitdir)] | 372 | _CLEAN_FILES = { |
| 361 | subprocess.check_call(cmd) | 373 | "a-vim-temp-file~", |
| 362 | 374 | "#an-emacs-temp-file#", | |
| 363 | dotgit = tempdir / 'src/test/.git' | 375 | } |
| 364 | dotgit.mkdir(parents=True) | 376 | |
| 365 | for name in cls._SYMLINKS: | 377 | @classmethod |
| 366 | (dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}') | 378 | @contextlib.contextmanager |
| 367 | for name in cls._FILES | cls._CLEAN_FILES: | 379 | def _simple_layout(cls): |
| 368 | (dotgit / name).write_text(name) | 380 | """Create a simple repo client checkout to test against.""" |
| 369 | 381 | with tempfile.TemporaryDirectory() as tempdir: | |
| 370 | yield tempdir | 382 | tempdir = Path(tempdir) |
| 371 | 383 | ||
| 372 | def test_standard(self): | 384 | gitdir = tempdir / ".repo/projects/src/test.git" |
| 373 | """Migrate a standard checkout that we expect.""" | 385 | gitdir.mkdir(parents=True) |
| 374 | with self._simple_layout() as tempdir: | 386 | cmd = ["git", "init", "--bare", str(gitdir)] |
| 375 | dotgit = tempdir / 'src/test/.git' | 387 | subprocess.check_call(cmd) |
| 376 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | 388 | |
| 377 | 389 | dotgit = tempdir / "src/test/.git" | |
| 378 | # Make sure the dir was transformed into a symlink. | 390 | dotgit.mkdir(parents=True) |
| 379 | self.assertTrue(dotgit.is_symlink()) | 391 | for name in cls._SYMLINKS: |
| 380 | self.assertEqual(os.readlink(dotgit), os.path.normpath('../../.repo/projects/src/test.git')) | 392 | (dotgit / name).symlink_to( |
| 381 | 393 | f"../../../.repo/projects/src/test.git/{name}" | |
| 382 | # Make sure files were moved over. | 394 | ) |
| 383 | gitdir = tempdir / '.repo/projects/src/test.git' | 395 | for name in cls._FILES | cls._CLEAN_FILES: |
| 384 | for name in self._FILES: | 396 | (dotgit / name).write_text(name) |
| 385 | self.assertEqual(name, (gitdir / name).read_text()) | 397 | |
| 386 | # Make sure files were removed. | 398 | yield tempdir |
| 387 | for name in self._CLEAN_FILES: | 399 | |
| 388 | self.assertFalse((gitdir / name).exists()) | 400 | def test_standard(self): |
| 389 | 401 | """Migrate a standard checkout that we expect.""" | |
| 390 | def test_unknown(self): | 402 | with self._simple_layout() as tempdir: |
| 391 | """A checkout with unknown files should abort.""" | 403 | dotgit = tempdir / "src/test/.git" |
| 392 | with self._simple_layout() as tempdir: | 404 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) |
| 393 | dotgit = tempdir / 'src/test/.git' | 405 | |
| 394 | (tempdir / '.repo/projects/src/test.git/random-file').write_text('one') | 406 | # Make sure the dir was transformed into a symlink. |
| 395 | (dotgit / 'random-file').write_text('two') | 407 | self.assertTrue(dotgit.is_symlink()) |
| 396 | with self.assertRaises(error.GitError): | 408 | self.assertEqual( |
| 397 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | 409 | os.readlink(dotgit), |
| 398 | 410 | os.path.normpath("../../.repo/projects/src/test.git"), | |
| 399 | # Make sure no content was actually changed. | 411 | ) |
| 400 | self.assertTrue(dotgit.is_dir()) | 412 | |
| 401 | for name in self._FILES: | 413 | # Make sure files were moved over. |
| 402 | self.assertTrue((dotgit / name).is_file()) | 414 | gitdir = tempdir / ".repo/projects/src/test.git" |
| 403 | for name in self._CLEAN_FILES: | 415 | for name in self._FILES: |
| 404 | self.assertTrue((dotgit / name).is_file()) | 416 | self.assertEqual(name, (gitdir / name).read_text()) |
| 405 | for name in self._SYMLINKS: | 417 | # Make sure files were removed. |
| 406 | self.assertTrue((dotgit / name).is_symlink()) | 418 | for name in self._CLEAN_FILES: |
| 419 | self.assertFalse((gitdir / name).exists()) | ||
| 420 | |||
| 421 | def test_unknown(self): | ||
| 422 | """A checkout with unknown files should abort.""" | ||
| 423 | with self._simple_layout() as tempdir: | ||
| 424 | dotgit = tempdir / "src/test/.git" | ||
| 425 | (tempdir / ".repo/projects/src/test.git/random-file").write_text( | ||
| 426 | "one" | ||
| 427 | ) | ||
| 428 | (dotgit / "random-file").write_text("two") | ||
| 429 | with self.assertRaises(error.GitError): | ||
| 430 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | ||
| 431 | |||
| 432 | # Make sure no content was actually changed. | ||
| 433 | self.assertTrue(dotgit.is_dir()) | ||
| 434 | for name in self._FILES: | ||
| 435 | self.assertTrue((dotgit / name).is_file()) | ||
| 436 | for name in self._CLEAN_FILES: | ||
| 437 | self.assertTrue((dotgit / name).is_file()) | ||
| 438 | for name in self._SYMLINKS: | ||
| 439 | self.assertTrue((dotgit / name).is_symlink()) | ||
| 407 | 440 | ||
| 408 | 441 | ||
| 409 | class ManifestPropertiesFetchedCorrectly(unittest.TestCase): | 442 | class ManifestPropertiesFetchedCorrectly(unittest.TestCase): |
| 410 | """Ensure properties are fetched properly.""" | 443 | """Ensure properties are fetched properly.""" |
| 411 | 444 | ||
| 412 | def setUpManifest(self, tempdir): | 445 | def setUpManifest(self, tempdir): |
| 413 | repodir = os.path.join(tempdir, '.repo') | 446 | repodir = os.path.join(tempdir, ".repo") |
| 414 | manifest_dir = os.path.join(repodir, 'manifests') | 447 | manifest_dir = os.path.join(repodir, "manifests") |
| 415 | manifest_file = os.path.join( | 448 | manifest_file = os.path.join(repodir, manifest_xml.MANIFEST_FILE_NAME) |
| 416 | repodir, manifest_xml.MANIFEST_FILE_NAME) | 449 | os.mkdir(repodir) |
| 417 | local_manifest_dir = os.path.join( | 450 | os.mkdir(manifest_dir) |
| 418 | repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME) | 451 | manifest = manifest_xml.XmlManifest(repodir, manifest_file) |
| 419 | os.mkdir(repodir) | ||
| 420 | os.mkdir(manifest_dir) | ||
| 421 | manifest = manifest_xml.XmlManifest(repodir, manifest_file) | ||
| 422 | 452 | ||
| 423 | return project.ManifestProject( | 453 | return project.ManifestProject( |
| 424 | manifest, 'test/manifest', os.path.join(tempdir, '.git'), tempdir) | 454 | manifest, "test/manifest", os.path.join(tempdir, ".git"), tempdir |
| 455 | ) | ||
| 425 | 456 | ||
| 426 | def test_manifest_config_properties(self): | 457 | def test_manifest_config_properties(self): |
| 427 | """Test we are fetching the manifest config properties correctly.""" | 458 | """Test we are fetching the manifest config properties correctly.""" |
| 428 | 459 | ||
| 429 | with TempGitTree() as tempdir: | 460 | with TempGitTree() as tempdir: |
| 430 | fakeproj = self.setUpManifest(tempdir) | 461 | fakeproj = self.setUpManifest(tempdir) |
| 431 | 462 | ||
| 432 | # Set property using the expected Set method, then ensure | 463 | # Set property using the expected Set method, then ensure |
| 433 | # the porperty functions are using the correct Get methods. | 464 | # the porperty functions are using the correct Get methods. |
| 434 | fakeproj.config.SetString( | 465 | fakeproj.config.SetString( |
| 435 | 'manifest.standalone', 'https://chicken/manifest.git') | 466 | "manifest.standalone", "https://chicken/manifest.git" |
| 436 | self.assertEqual( | 467 | ) |
| 437 | fakeproj.standalone_manifest_url, 'https://chicken/manifest.git') | 468 | self.assertEqual( |
| 469 | fakeproj.standalone_manifest_url, "https://chicken/manifest.git" | ||
| 470 | ) | ||
| 438 | 471 | ||
| 439 | fakeproj.config.SetString('manifest.groups', 'test-group, admin-group') | 472 | fakeproj.config.SetString( |
| 440 | self.assertEqual(fakeproj.manifest_groups, 'test-group, admin-group') | 473 | "manifest.groups", "test-group, admin-group" |
| 474 | ) | ||
| 475 | self.assertEqual( | ||
| 476 | fakeproj.manifest_groups, "test-group, admin-group" | ||
| 477 | ) | ||
| 441 | 478 | ||
| 442 | fakeproj.config.SetString('repo.reference', 'mirror/ref') | 479 | fakeproj.config.SetString("repo.reference", "mirror/ref") |
| 443 | self.assertEqual(fakeproj.reference, 'mirror/ref') | 480 | self.assertEqual(fakeproj.reference, "mirror/ref") |
| 444 | 481 | ||
| 445 | fakeproj.config.SetBoolean('repo.dissociate', False) | 482 | fakeproj.config.SetBoolean("repo.dissociate", False) |
| 446 | self.assertFalse(fakeproj.dissociate) | 483 | self.assertFalse(fakeproj.dissociate) |
| 447 | 484 | ||
| 448 | fakeproj.config.SetBoolean('repo.archive', False) | 485 | fakeproj.config.SetBoolean("repo.archive", False) |
| 449 | self.assertFalse(fakeproj.archive) | 486 | self.assertFalse(fakeproj.archive) |
| 450 | 487 | ||
| 451 | fakeproj.config.SetBoolean('repo.mirror', False) | 488 | fakeproj.config.SetBoolean("repo.mirror", False) |
| 452 | self.assertFalse(fakeproj.mirror) | 489 | self.assertFalse(fakeproj.mirror) |
| 453 | 490 | ||
| 454 | fakeproj.config.SetBoolean('repo.worktree', False) | 491 | fakeproj.config.SetBoolean("repo.worktree", False) |
| 455 | self.assertFalse(fakeproj.use_worktree) | 492 | self.assertFalse(fakeproj.use_worktree) |
| 456 | 493 | ||
| 457 | fakeproj.config.SetBoolean('repo.clonebundle', False) | 494 | fakeproj.config.SetBoolean("repo.clonebundle", False) |
| 458 | self.assertFalse(fakeproj.clone_bundle) | 495 | self.assertFalse(fakeproj.clone_bundle) |
| 459 | 496 | ||
| 460 | fakeproj.config.SetBoolean('repo.submodules', False) | 497 | fakeproj.config.SetBoolean("repo.submodules", False) |
| 461 | self.assertFalse(fakeproj.submodules) | 498 | self.assertFalse(fakeproj.submodules) |
| 462 | 499 | ||
| 463 | fakeproj.config.SetBoolean('repo.git-lfs', False) | 500 | fakeproj.config.SetBoolean("repo.git-lfs", False) |
| 464 | self.assertFalse(fakeproj.git_lfs) | 501 | self.assertFalse(fakeproj.git_lfs) |
| 465 | 502 | ||
| 466 | fakeproj.config.SetBoolean('repo.superproject', False) | 503 | fakeproj.config.SetBoolean("repo.superproject", False) |
| 467 | self.assertFalse(fakeproj.use_superproject) | 504 | self.assertFalse(fakeproj.use_superproject) |
| 468 | 505 | ||
| 469 | fakeproj.config.SetBoolean('repo.partialclone', False) | 506 | fakeproj.config.SetBoolean("repo.partialclone", False) |
| 470 | self.assertFalse(fakeproj.partial_clone) | 507 | self.assertFalse(fakeproj.partial_clone) |
| 471 | 508 | ||
| 472 | fakeproj.config.SetString('repo.depth', '48') | 509 | fakeproj.config.SetString("repo.depth", "48") |
| 473 | self.assertEqual(fakeproj.depth, '48') | 510 | self.assertEqual(fakeproj.depth, "48") |
| 474 | 511 | ||
| 475 | fakeproj.config.SetString('repo.clonefilter', 'blob:limit=10M') | 512 | fakeproj.config.SetString("repo.clonefilter", "blob:limit=10M") |
| 476 | self.assertEqual(fakeproj.clone_filter, 'blob:limit=10M') | 513 | self.assertEqual(fakeproj.clone_filter, "blob:limit=10M") |
| 477 | 514 | ||
| 478 | fakeproj.config.SetString('repo.partialcloneexclude', 'third_party/big_repo') | 515 | fakeproj.config.SetString( |
| 479 | self.assertEqual(fakeproj.partial_clone_exclude, 'third_party/big_repo') | 516 | "repo.partialcloneexclude", "third_party/big_repo" |
| 517 | ) | ||
| 518 | self.assertEqual( | ||
| 519 | fakeproj.partial_clone_exclude, "third_party/big_repo" | ||
| 520 | ) | ||
| 480 | 521 | ||
| 481 | fakeproj.config.SetString('manifest.platform', 'auto') | 522 | fakeproj.config.SetString("manifest.platform", "auto") |
| 482 | self.assertEqual(fakeproj.manifest_platform, 'auto') | 523 | self.assertEqual(fakeproj.manifest_platform, "auto") |
diff --git a/tests/test_repo_trace.py b/tests/test_repo_trace.py index 5faf2938..e4aeb5de 100644 --- a/tests/test_repo_trace.py +++ b/tests/test_repo_trace.py | |||
| @@ -22,35 +22,39 @@ import repo_trace | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | class TraceTests(unittest.TestCase): | 24 | class TraceTests(unittest.TestCase): |
| 25 | """Check Trace behavior.""" | 25 | """Check Trace behavior.""" |
| 26 | 26 | ||
| 27 | def testTrace_MaxSizeEnforced(self): | 27 | def testTrace_MaxSizeEnforced(self): |
| 28 | content = 'git chicken' | 28 | content = "git chicken" |
| 29 | 29 | ||
| 30 | with repo_trace.Trace(content, first_trace=True): | 30 | with repo_trace.Trace(content, first_trace=True): |
| 31 | pass | 31 | pass |
| 32 | first_trace_size = os.path.getsize(repo_trace._TRACE_FILE) | 32 | first_trace_size = os.path.getsize(repo_trace._TRACE_FILE) |
| 33 | 33 | ||
| 34 | with repo_trace.Trace(content): | 34 | with repo_trace.Trace(content): |
| 35 | pass | 35 | pass |
| 36 | self.assertGreater( | 36 | self.assertGreater( |
| 37 | os.path.getsize(repo_trace._TRACE_FILE), first_trace_size) | 37 | os.path.getsize(repo_trace._TRACE_FILE), first_trace_size |
| 38 | 38 | ) | |
| 39 | # Check we clear everything is the last chunk is larger than _MAX_SIZE. | 39 | |
| 40 | with mock.patch('repo_trace._MAX_SIZE', 0): | 40 | # Check we clear everything is the last chunk is larger than _MAX_SIZE. |
| 41 | with repo_trace.Trace(content, first_trace=True): | 41 | with mock.patch("repo_trace._MAX_SIZE", 0): |
| 42 | pass | 42 | with repo_trace.Trace(content, first_trace=True): |
| 43 | self.assertEqual(first_trace_size, | 43 | pass |
| 44 | os.path.getsize(repo_trace._TRACE_FILE)) | 44 | self.assertEqual( |
| 45 | 45 | first_trace_size, os.path.getsize(repo_trace._TRACE_FILE) | |
| 46 | # Check we only clear the chunks we need to. | 46 | ) |
| 47 | repo_trace._MAX_SIZE = (first_trace_size + 1) / (1024 * 1024) | 47 | |
| 48 | with repo_trace.Trace(content, first_trace=True): | 48 | # Check we only clear the chunks we need to. |
| 49 | pass | 49 | repo_trace._MAX_SIZE = (first_trace_size + 1) / (1024 * 1024) |
| 50 | self.assertEqual(first_trace_size * 2, | 50 | with repo_trace.Trace(content, first_trace=True): |
| 51 | os.path.getsize(repo_trace._TRACE_FILE)) | 51 | pass |
| 52 | 52 | self.assertEqual( | |
| 53 | with repo_trace.Trace(content, first_trace=True): | 53 | first_trace_size * 2, os.path.getsize(repo_trace._TRACE_FILE) |
| 54 | pass | 54 | ) |
| 55 | self.assertEqual(first_trace_size * 2, | 55 | |
| 56 | os.path.getsize(repo_trace._TRACE_FILE)) | 56 | with repo_trace.Trace(content, first_trace=True): |
| 57 | pass | ||
| 58 | self.assertEqual( | ||
| 59 | first_trace_size * 2, os.path.getsize(repo_trace._TRACE_FILE) | ||
| 60 | ) | ||
diff --git a/tests/test_ssh.py b/tests/test_ssh.py index ffb5cb94..a9c1be7f 100644 --- a/tests/test_ssh.py +++ b/tests/test_ssh.py | |||
| @@ -23,52 +23,56 @@ import ssh | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | class SshTests(unittest.TestCase): | 25 | class SshTests(unittest.TestCase): |
| 26 | """Tests the ssh functions.""" | 26 | """Tests the ssh functions.""" |
| 27 | 27 | ||
| 28 | def test_parse_ssh_version(self): | 28 | def test_parse_ssh_version(self): |
| 29 | """Check _parse_ssh_version() handling.""" | 29 | """Check _parse_ssh_version() handling.""" |
| 30 | ver = ssh._parse_ssh_version('Unknown\n') | 30 | ver = ssh._parse_ssh_version("Unknown\n") |
| 31 | self.assertEqual(ver, ()) | 31 | self.assertEqual(ver, ()) |
| 32 | ver = ssh._parse_ssh_version('OpenSSH_1.0\n') | 32 | ver = ssh._parse_ssh_version("OpenSSH_1.0\n") |
| 33 | self.assertEqual(ver, (1, 0)) | 33 | self.assertEqual(ver, (1, 0)) |
| 34 | ver = ssh._parse_ssh_version('OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13, OpenSSL 1.0.1f 6 Jan 2014\n') | 34 | ver = ssh._parse_ssh_version( |
| 35 | self.assertEqual(ver, (6, 6, 1)) | 35 | "OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.13, OpenSSL 1.0.1f 6 Jan 2014\n" |
| 36 | ver = ssh._parse_ssh_version('OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017\n') | 36 | ) |
| 37 | self.assertEqual(ver, (7, 6)) | 37 | self.assertEqual(ver, (6, 6, 1)) |
| 38 | ver = ssh._parse_ssh_version( | ||
| 39 | "OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017\n" | ||
| 40 | ) | ||
| 41 | self.assertEqual(ver, (7, 6)) | ||
| 38 | 42 | ||
| 39 | def test_version(self): | 43 | def test_version(self): |
| 40 | """Check version() handling.""" | 44 | """Check version() handling.""" |
| 41 | with mock.patch('ssh._run_ssh_version', return_value='OpenSSH_1.2\n'): | 45 | with mock.patch("ssh._run_ssh_version", return_value="OpenSSH_1.2\n"): |
| 42 | self.assertEqual(ssh.version(), (1, 2)) | 46 | self.assertEqual(ssh.version(), (1, 2)) |
| 43 | 47 | ||
| 44 | def test_context_manager_empty(self): | 48 | def test_context_manager_empty(self): |
| 45 | """Verify context manager with no clients works correctly.""" | 49 | """Verify context manager with no clients works correctly.""" |
| 46 | with multiprocessing.Manager() as manager: | 50 | with multiprocessing.Manager() as manager: |
| 47 | with ssh.ProxyManager(manager): | 51 | with ssh.ProxyManager(manager): |
| 48 | pass | 52 | pass |
| 49 | 53 | ||
| 50 | def test_context_manager_child_cleanup(self): | 54 | def test_context_manager_child_cleanup(self): |
| 51 | """Verify orphaned clients & masters get cleaned up.""" | 55 | """Verify orphaned clients & masters get cleaned up.""" |
| 52 | with multiprocessing.Manager() as manager: | 56 | with multiprocessing.Manager() as manager: |
| 53 | with ssh.ProxyManager(manager) as ssh_proxy: | 57 | with ssh.ProxyManager(manager) as ssh_proxy: |
| 54 | client = subprocess.Popen(['sleep', '964853320']) | 58 | client = subprocess.Popen(["sleep", "964853320"]) |
| 55 | ssh_proxy.add_client(client) | 59 | ssh_proxy.add_client(client) |
| 56 | master = subprocess.Popen(['sleep', '964853321']) | 60 | master = subprocess.Popen(["sleep", "964853321"]) |
| 57 | ssh_proxy.add_master(master) | 61 | ssh_proxy.add_master(master) |
| 58 | # If the process still exists, these will throw timeout errors. | 62 | # If the process still exists, these will throw timeout errors. |
| 59 | client.wait(0) | 63 | client.wait(0) |
| 60 | master.wait(0) | 64 | master.wait(0) |
| 61 | 65 | ||
| 62 | def test_ssh_sock(self): | 66 | def test_ssh_sock(self): |
| 63 | """Check sock() function.""" | 67 | """Check sock() function.""" |
| 64 | manager = multiprocessing.Manager() | 68 | manager = multiprocessing.Manager() |
| 65 | proxy = ssh.ProxyManager(manager) | 69 | proxy = ssh.ProxyManager(manager) |
| 66 | with mock.patch('tempfile.mkdtemp', return_value='/tmp/foo'): | 70 | with mock.patch("tempfile.mkdtemp", return_value="/tmp/foo"): |
| 67 | # old ssh version uses port | 71 | # Old ssh version uses port. |
| 68 | with mock.patch('ssh.version', return_value=(6, 6)): | 72 | with mock.patch("ssh.version", return_value=(6, 6)): |
| 69 | self.assertTrue(proxy.sock().endswith('%p')) | 73 | self.assertTrue(proxy.sock().endswith("%p")) |
| 70 | 74 | ||
| 71 | proxy._sock_path = None | 75 | proxy._sock_path = None |
| 72 | # new ssh version uses hash | 76 | # New ssh version uses hash. |
| 73 | with mock.patch('ssh.version', return_value=(6, 7)): | 77 | with mock.patch("ssh.version", return_value=(6, 7)): |
| 74 | self.assertTrue(proxy.sock().endswith('%C')) | 78 | self.assertTrue(proxy.sock().endswith("%C")) |
diff --git a/tests/test_subcmds.py b/tests/test_subcmds.py index bc53051a..73b66e3f 100644 --- a/tests/test_subcmds.py +++ b/tests/test_subcmds.py | |||
| @@ -21,53 +21,57 @@ import subcmds | |||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | class AllCommands(unittest.TestCase): | 23 | class AllCommands(unittest.TestCase): |
| 24 | """Check registered all_commands.""" | 24 | """Check registered all_commands.""" |
| 25 | 25 | ||
| 26 | def test_required_basic(self): | 26 | def test_required_basic(self): |
| 27 | """Basic checking of registered commands.""" | 27 | """Basic checking of registered commands.""" |
| 28 | # NB: We don't test all subcommands as we want to avoid "change detection" | 28 | # NB: We don't test all subcommands as we want to avoid "change |
| 29 | # tests, so we just look for the most common/important ones here that are | 29 | # detection" tests, so we just look for the most common/important ones |
| 30 | # unlikely to ever change. | 30 | # here that are unlikely to ever change. |
| 31 | for cmd in {'cherry-pick', 'help', 'init', 'start', 'sync', 'upload'}: | 31 | for cmd in {"cherry-pick", "help", "init", "start", "sync", "upload"}: |
| 32 | self.assertIn(cmd, subcmds.all_commands) | 32 | self.assertIn(cmd, subcmds.all_commands) |
| 33 | 33 | ||
| 34 | def test_naming(self): | 34 | def test_naming(self): |
| 35 | """Verify we don't add things that we shouldn't.""" | 35 | """Verify we don't add things that we shouldn't.""" |
| 36 | for cmd in subcmds.all_commands: | 36 | for cmd in subcmds.all_commands: |
| 37 | # Reject filename suffixes like "help.py". | 37 | # Reject filename suffixes like "help.py". |
| 38 | self.assertNotIn('.', cmd) | 38 | self.assertNotIn(".", cmd) |
| 39 | 39 | ||
| 40 | # Make sure all '_' were converted to '-'. | 40 | # Make sure all '_' were converted to '-'. |
| 41 | self.assertNotIn('_', cmd) | 41 | self.assertNotIn("_", cmd) |
| 42 | 42 | ||
| 43 | # Reject internal python paths like "__init__". | 43 | # Reject internal python paths like "__init__". |
| 44 | self.assertFalse(cmd.startswith('__')) | 44 | self.assertFalse(cmd.startswith("__")) |
| 45 | 45 | ||
| 46 | def test_help_desc_style(self): | 46 | def test_help_desc_style(self): |
| 47 | """Force some consistency in option descriptions. | 47 | """Force some consistency in option descriptions. |
| 48 | 48 | ||
| 49 | Python's optparse & argparse has a few default options like --help. Their | 49 | Python's optparse & argparse has a few default options like --help. |
| 50 | option description text uses lowercase sentence fragments, so enforce our | 50 | Their option description text uses lowercase sentence fragments, so |
| 51 | options follow the same style so UI is consistent. | 51 | enforce our options follow the same style so UI is consistent. |
| 52 | 52 | ||
| 53 | We enforce: | 53 | We enforce: |
| 54 | * Text starts with lowercase. | 54 | * Text starts with lowercase. |
| 55 | * Text doesn't end with period. | 55 | * Text doesn't end with period. |
| 56 | """ | 56 | """ |
| 57 | for name, cls in subcmds.all_commands.items(): | 57 | for name, cls in subcmds.all_commands.items(): |
| 58 | cmd = cls() | 58 | cmd = cls() |
| 59 | parser = cmd.OptionParser | 59 | parser = cmd.OptionParser |
| 60 | for option in parser.option_list: | 60 | for option in parser.option_list: |
| 61 | if option.help == optparse.SUPPRESS_HELP: | 61 | if option.help == optparse.SUPPRESS_HELP: |
| 62 | continue | 62 | continue |
| 63 | 63 | ||
| 64 | c = option.help[0] | 64 | c = option.help[0] |
| 65 | self.assertEqual( | 65 | self.assertEqual( |
| 66 | c.lower(), c, | 66 | c.lower(), |
| 67 | msg=f'subcmds/{name}.py: {option.get_opt_string()}: help text ' | 67 | c, |
| 68 | f'should start with lowercase: "{option.help}"') | 68 | msg=f"subcmds/{name}.py: {option.get_opt_string()}: " |
| 69 | f'help text should start with lowercase: "{option.help}"', | ||
| 70 | ) | ||
| 69 | 71 | ||
| 70 | self.assertNotEqual( | 72 | self.assertNotEqual( |
| 71 | option.help[-1], '.', | 73 | option.help[-1], |
| 72 | msg=f'subcmds/{name}.py: {option.get_opt_string()}: help text ' | 74 | ".", |
| 73 | f'should not end in a period: "{option.help}"') | 75 | msg=f"subcmds/{name}.py: {option.get_opt_string()}: " |
| 76 | f'help text should not end in a period: "{option.help}"', | ||
| 77 | ) | ||
diff --git a/tests/test_subcmds_init.py b/tests/test_subcmds_init.py index af4346de..25e5be56 100644 --- a/tests/test_subcmds_init.py +++ b/tests/test_subcmds_init.py | |||
| @@ -20,30 +20,27 @@ from subcmds import init | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | class InitCommand(unittest.TestCase): | 22 | class InitCommand(unittest.TestCase): |
| 23 | """Check registered all_commands.""" | 23 | """Check registered all_commands.""" |
| 24 | 24 | ||
| 25 | def setUp(self): | 25 | def setUp(self): |
| 26 | self.cmd = init.Init() | 26 | self.cmd = init.Init() |
| 27 | 27 | ||
| 28 | def test_cli_parser_good(self): | 28 | def test_cli_parser_good(self): |
| 29 | """Check valid command line options.""" | 29 | """Check valid command line options.""" |
| 30 | ARGV = ( | 30 | ARGV = ([],) |
| 31 | [], | 31 | for argv in ARGV: |
| 32 | ) | 32 | opts, args = self.cmd.OptionParser.parse_args(argv) |
| 33 | for argv in ARGV: | 33 | self.cmd.ValidateOptions(opts, args) |
| 34 | opts, args = self.cmd.OptionParser.parse_args(argv) | 34 | |
| 35 | self.cmd.ValidateOptions(opts, args) | 35 | def test_cli_parser_bad(self): |
| 36 | 36 | """Check invalid command line options.""" | |
| 37 | def test_cli_parser_bad(self): | 37 | ARGV = ( |
| 38 | """Check invalid command line options.""" | 38 | # Too many arguments. |
| 39 | ARGV = ( | 39 | ["url", "asdf"], |
| 40 | # Too many arguments. | 40 | # Conflicting options. |
| 41 | ['url', 'asdf'], | 41 | ["--mirror", "--archive"], |
| 42 | 42 | ) | |
| 43 | # Conflicting options. | 43 | for argv in ARGV: |
| 44 | ['--mirror', '--archive'], | 44 | opts, args = self.cmd.OptionParser.parse_args(argv) |
| 45 | ) | 45 | with self.assertRaises(SystemExit): |
| 46 | for argv in ARGV: | 46 | self.cmd.ValidateOptions(opts, args) |
| 47 | opts, args = self.cmd.OptionParser.parse_args(argv) | ||
| 48 | with self.assertRaises(SystemExit): | ||
| 49 | self.cmd.ValidateOptions(opts, args) | ||
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py index 236d54e5..5c8e606e 100644 --- a/tests/test_subcmds_sync.py +++ b/tests/test_subcmds_sync.py | |||
| @@ -23,111 +23,138 @@ import command | |||
| 23 | from subcmds import sync | 23 | from subcmds import sync |
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | @pytest.mark.parametrize('use_superproject, cli_args, result', [ | 26 | @pytest.mark.parametrize( |
| 27 | (True, ['--current-branch'], True), | 27 | "use_superproject, cli_args, result", |
| 28 | (True, ['--no-current-branch'], True), | 28 | [ |
| 29 | (True, [], True), | 29 | (True, ["--current-branch"], True), |
| 30 | (False, ['--current-branch'], True), | 30 | (True, ["--no-current-branch"], True), |
| 31 | (False, ['--no-current-branch'], False), | 31 | (True, [], True), |
| 32 | (False, [], None), | 32 | (False, ["--current-branch"], True), |
| 33 | ]) | 33 | (False, ["--no-current-branch"], False), |
| 34 | (False, [], None), | ||
| 35 | ], | ||
| 36 | ) | ||
| 34 | def test_get_current_branch_only(use_superproject, cli_args, result): | 37 | def test_get_current_branch_only(use_superproject, cli_args, result): |
| 35 | """Test Sync._GetCurrentBranchOnly logic. | 38 | """Test Sync._GetCurrentBranchOnly logic. |
| 36 | 39 | ||
| 37 | Sync._GetCurrentBranchOnly should return True if a superproject is requested, | 40 | Sync._GetCurrentBranchOnly should return True if a superproject is |
| 38 | and otherwise the value of the current_branch_only option. | 41 | requested, and otherwise the value of the current_branch_only option. |
| 39 | """ | 42 | """ |
| 40 | cmd = sync.Sync() | 43 | cmd = sync.Sync() |
| 41 | opts, _ = cmd.OptionParser.parse_args(cli_args) | 44 | opts, _ = cmd.OptionParser.parse_args(cli_args) |
| 42 | 45 | ||
| 43 | with mock.patch('git_superproject.UseSuperproject', | 46 | with mock.patch( |
| 44 | return_value=use_superproject): | 47 | "git_superproject.UseSuperproject", return_value=use_superproject |
| 45 | assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result | 48 | ): |
| 49 | assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result | ||
| 46 | 50 | ||
| 47 | 51 | ||
| 48 | # Used to patch os.cpu_count() for reliable results. | 52 | # Used to patch os.cpu_count() for reliable results. |
| 49 | OS_CPU_COUNT = 24 | 53 | OS_CPU_COUNT = 24 |
| 50 | 54 | ||
| 51 | @pytest.mark.parametrize('argv, jobs_manifest, jobs, jobs_net, jobs_check', [ | 55 | |
| 52 | # No user or manifest settings. | 56 | @pytest.mark.parametrize( |
| 53 | ([], None, OS_CPU_COUNT, 1, command.DEFAULT_LOCAL_JOBS), | 57 | "argv, jobs_manifest, jobs, jobs_net, jobs_check", |
| 54 | # No user settings, so manifest settings control. | 58 | [ |
| 55 | ([], 3, 3, 3, 3), | 59 | # No user or manifest settings. |
| 56 | # User settings, but no manifest. | 60 | ([], None, OS_CPU_COUNT, 1, command.DEFAULT_LOCAL_JOBS), |
| 57 | (['--jobs=4'], None, 4, 4, 4), | 61 | # No user settings, so manifest settings control. |
| 58 | (['--jobs=4', '--jobs-network=5'], None, 4, 5, 4), | 62 | ([], 3, 3, 3, 3), |
| 59 | (['--jobs=4', '--jobs-checkout=6'], None, 4, 4, 6), | 63 | # User settings, but no manifest. |
| 60 | (['--jobs=4', '--jobs-network=5', '--jobs-checkout=6'], None, 4, 5, 6), | 64 | (["--jobs=4"], None, 4, 4, 4), |
| 61 | (['--jobs-network=5'], None, OS_CPU_COUNT, 5, command.DEFAULT_LOCAL_JOBS), | 65 | (["--jobs=4", "--jobs-network=5"], None, 4, 5, 4), |
| 62 | (['--jobs-checkout=6'], None, OS_CPU_COUNT, 1, 6), | 66 | (["--jobs=4", "--jobs-checkout=6"], None, 4, 4, 6), |
| 63 | (['--jobs-network=5', '--jobs-checkout=6'], None, OS_CPU_COUNT, 5, 6), | 67 | (["--jobs=4", "--jobs-network=5", "--jobs-checkout=6"], None, 4, 5, 6), |
| 64 | # User settings with manifest settings. | 68 | ( |
| 65 | (['--jobs=4'], 3, 4, 4, 4), | 69 | ["--jobs-network=5"], |
| 66 | (['--jobs=4', '--jobs-network=5'], 3, 4, 5, 4), | 70 | None, |
| 67 | (['--jobs=4', '--jobs-checkout=6'], 3, 4, 4, 6), | 71 | OS_CPU_COUNT, |
| 68 | (['--jobs=4', '--jobs-network=5', '--jobs-checkout=6'], 3, 4, 5, 6), | 72 | 5, |
| 69 | (['--jobs-network=5'], 3, 3, 5, 3), | 73 | command.DEFAULT_LOCAL_JOBS, |
| 70 | (['--jobs-checkout=6'], 3, 3, 3, 6), | 74 | ), |
| 71 | (['--jobs-network=5', '--jobs-checkout=6'], 3, 3, 5, 6), | 75 | (["--jobs-checkout=6"], None, OS_CPU_COUNT, 1, 6), |
| 72 | # Settings that exceed rlimits get capped. | 76 | (["--jobs-network=5", "--jobs-checkout=6"], None, OS_CPU_COUNT, 5, 6), |
| 73 | (['--jobs=1000000'], None, 83, 83, 83), | 77 | # User settings with manifest settings. |
| 74 | ([], 1000000, 83, 83, 83), | 78 | (["--jobs=4"], 3, 4, 4, 4), |
| 75 | ]) | 79 | (["--jobs=4", "--jobs-network=5"], 3, 4, 5, 4), |
| 80 | (["--jobs=4", "--jobs-checkout=6"], 3, 4, 4, 6), | ||
| 81 | (["--jobs=4", "--jobs-network=5", "--jobs-checkout=6"], 3, 4, 5, 6), | ||
| 82 | (["--jobs-network=5"], 3, 3, 5, 3), | ||
| 83 | (["--jobs-checkout=6"], 3, 3, 3, 6), | ||
| 84 | (["--jobs-network=5", "--jobs-checkout=6"], 3, 3, 5, 6), | ||
| 85 | # Settings that exceed rlimits get capped. | ||
| 86 | (["--jobs=1000000"], None, 83, 83, 83), | ||
| 87 | ([], 1000000, 83, 83, 83), | ||
| 88 | ], | ||
| 89 | ) | ||
| 76 | def test_cli_jobs(argv, jobs_manifest, jobs, jobs_net, jobs_check): | 90 | def test_cli_jobs(argv, jobs_manifest, jobs, jobs_net, jobs_check): |
| 77 | """Tests --jobs option behavior.""" | 91 | """Tests --jobs option behavior.""" |
| 78 | mp = mock.MagicMock() | 92 | mp = mock.MagicMock() |
| 79 | mp.manifest.default.sync_j = jobs_manifest | 93 | mp.manifest.default.sync_j = jobs_manifest |
| 80 | 94 | ||
| 81 | cmd = sync.Sync() | 95 | cmd = sync.Sync() |
| 82 | opts, args = cmd.OptionParser.parse_args(argv) | 96 | opts, args = cmd.OptionParser.parse_args(argv) |
| 83 | cmd.ValidateOptions(opts, args) | 97 | cmd.ValidateOptions(opts, args) |
| 84 | 98 | ||
| 85 | with mock.patch.object(sync, '_rlimit_nofile', return_value=(256, 256)): | 99 | with mock.patch.object(sync, "_rlimit_nofile", return_value=(256, 256)): |
| 86 | with mock.patch.object(os, 'cpu_count', return_value=OS_CPU_COUNT): | 100 | with mock.patch.object(os, "cpu_count", return_value=OS_CPU_COUNT): |
| 87 | cmd._ValidateOptionsWithManifest(opts, mp) | 101 | cmd._ValidateOptionsWithManifest(opts, mp) |
| 88 | assert opts.jobs == jobs | 102 | assert opts.jobs == jobs |
| 89 | assert opts.jobs_network == jobs_net | 103 | assert opts.jobs_network == jobs_net |
| 90 | assert opts.jobs_checkout == jobs_check | 104 | assert opts.jobs_checkout == jobs_check |
| 91 | 105 | ||
| 92 | 106 | ||
| 93 | class GetPreciousObjectsState(unittest.TestCase): | 107 | class GetPreciousObjectsState(unittest.TestCase): |
| 94 | """Tests for _GetPreciousObjectsState.""" | 108 | """Tests for _GetPreciousObjectsState.""" |
| 95 | 109 | ||
| 96 | def setUp(self): | 110 | def setUp(self): |
| 97 | """Common setup.""" | 111 | """Common setup.""" |
| 98 | self.cmd = sync.Sync() | 112 | self.cmd = sync.Sync() |
| 99 | self.project = p = mock.MagicMock(use_git_worktrees=False, | 113 | self.project = p = mock.MagicMock( |
| 100 | UseAlternates=False) | 114 | use_git_worktrees=False, UseAlternates=False |
| 101 | p.manifest.GetProjectsWithName.return_value = [p] | 115 | ) |
| 102 | 116 | p.manifest.GetProjectsWithName.return_value = [p] | |
| 103 | self.opt = mock.Mock(spec_set=['this_manifest_only']) | 117 | |
| 104 | self.opt.this_manifest_only = False | 118 | self.opt = mock.Mock(spec_set=["this_manifest_only"]) |
| 105 | 119 | self.opt.this_manifest_only = False | |
| 106 | def test_worktrees(self): | 120 | |
| 107 | """False for worktrees.""" | 121 | def test_worktrees(self): |
| 108 | self.project.use_git_worktrees = True | 122 | """False for worktrees.""" |
| 109 | self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt)) | 123 | self.project.use_git_worktrees = True |
| 110 | 124 | self.assertFalse( | |
| 111 | def test_not_shared(self): | 125 | self.cmd._GetPreciousObjectsState(self.project, self.opt) |
| 112 | """Singleton project.""" | 126 | ) |
| 113 | self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt)) | 127 | |
| 114 | 128 | def test_not_shared(self): | |
| 115 | def test_shared(self): | 129 | """Singleton project.""" |
| 116 | """Shared project.""" | 130 | self.assertFalse( |
| 117 | self.project.manifest.GetProjectsWithName.return_value = [ | 131 | self.cmd._GetPreciousObjectsState(self.project, self.opt) |
| 118 | self.project, self.project | 132 | ) |
| 119 | ] | 133 | |
| 120 | self.assertTrue(self.cmd._GetPreciousObjectsState(self.project, self.opt)) | 134 | def test_shared(self): |
| 121 | 135 | """Shared project.""" | |
| 122 | def test_shared_with_alternates(self): | 136 | self.project.manifest.GetProjectsWithName.return_value = [ |
| 123 | """Shared project, with alternates.""" | 137 | self.project, |
| 124 | self.project.manifest.GetProjectsWithName.return_value = [ | 138 | self.project, |
| 125 | self.project, self.project | 139 | ] |
| 126 | ] | 140 | self.assertTrue( |
| 127 | self.project.UseAlternates = True | 141 | self.cmd._GetPreciousObjectsState(self.project, self.opt) |
| 128 | self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt)) | 142 | ) |
| 129 | 143 | ||
| 130 | def test_not_found(self): | 144 | def test_shared_with_alternates(self): |
| 131 | """Project not found in manifest.""" | 145 | """Shared project, with alternates.""" |
| 132 | self.project.manifest.GetProjectsWithName.return_value = [] | 146 | self.project.manifest.GetProjectsWithName.return_value = [ |
| 133 | self.assertFalse(self.cmd._GetPreciousObjectsState(self.project, self.opt)) | 147 | self.project, |
| 148 | self.project, | ||
| 149 | ] | ||
| 150 | self.project.UseAlternates = True | ||
| 151 | self.assertFalse( | ||
| 152 | self.cmd._GetPreciousObjectsState(self.project, self.opt) | ||
| 153 | ) | ||
| 154 | |||
| 155 | def test_not_found(self): | ||
| 156 | """Project not found in manifest.""" | ||
| 157 | self.project.manifest.GetProjectsWithName.return_value = [] | ||
| 158 | self.assertFalse( | ||
| 159 | self.cmd._GetPreciousObjectsState(self.project, self.opt) | ||
| 160 | ) | ||
diff --git a/tests/test_update_manpages.py b/tests/test_update_manpages.py index 0de85be9..12b19ec4 100644 --- a/tests/test_update_manpages.py +++ b/tests/test_update_manpages.py | |||
| @@ -20,9 +20,9 @@ from release import update_manpages | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | class UpdateManpagesTest(unittest.TestCase): | 22 | class UpdateManpagesTest(unittest.TestCase): |
| 23 | """Tests the update-manpages code.""" | 23 | """Tests the update-manpages code.""" |
| 24 | 24 | ||
| 25 | def test_replace_regex(self): | 25 | def test_replace_regex(self): |
| 26 | """Check that replace_regex works.""" | 26 | """Check that replace_regex works.""" |
| 27 | data = '\n\033[1mSummary\033[m\n' | 27 | data = "\n\033[1mSummary\033[m\n" |
| 28 | self.assertEqual(update_manpages.replace_regex(data),'\nSummary\n') | 28 | self.assertEqual(update_manpages.replace_regex(data), "\nSummary\n") |
diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index ef879a5d..21fa094d 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py | |||
| @@ -28,528 +28,615 @@ import wrapper | |||
| 28 | 28 | ||
| 29 | 29 | ||
| 30 | def fixture(*paths): | 30 | def fixture(*paths): |
| 31 | """Return a path relative to tests/fixtures. | 31 | """Return a path relative to tests/fixtures.""" |
| 32 | """ | 32 | return os.path.join(os.path.dirname(__file__), "fixtures", *paths) |
| 33 | return os.path.join(os.path.dirname(__file__), 'fixtures', *paths) | ||
| 34 | 33 | ||
| 35 | 34 | ||
| 36 | class RepoWrapperTestCase(unittest.TestCase): | 35 | class RepoWrapperTestCase(unittest.TestCase): |
| 37 | """TestCase for the wrapper module.""" | 36 | """TestCase for the wrapper module.""" |
| 38 | 37 | ||
| 39 | def setUp(self): | 38 | def setUp(self): |
| 40 | """Load the wrapper module every time.""" | 39 | """Load the wrapper module every time.""" |
| 41 | wrapper.Wrapper.cache_clear() | 40 | wrapper.Wrapper.cache_clear() |
| 42 | self.wrapper = wrapper.Wrapper() | 41 | self.wrapper = wrapper.Wrapper() |
| 43 | 42 | ||
| 44 | 43 | ||
| 45 | class RepoWrapperUnitTest(RepoWrapperTestCase): | 44 | class RepoWrapperUnitTest(RepoWrapperTestCase): |
| 46 | """Tests helper functions in the repo wrapper | 45 | """Tests helper functions in the repo wrapper""" |
| 47 | """ | 46 | |
| 48 | 47 | def test_version(self): | |
| 49 | def test_version(self): | 48 | """Make sure _Version works.""" |
| 50 | """Make sure _Version works.""" | 49 | with self.assertRaises(SystemExit) as e: |
| 51 | with self.assertRaises(SystemExit) as e: | 50 | with mock.patch("sys.stdout", new_callable=StringIO) as stdout: |
| 52 | with mock.patch('sys.stdout', new_callable=StringIO) as stdout: | 51 | with mock.patch("sys.stderr", new_callable=StringIO) as stderr: |
| 53 | with mock.patch('sys.stderr', new_callable=StringIO) as stderr: | 52 | self.wrapper._Version() |
| 54 | self.wrapper._Version() | 53 | self.assertEqual(0, e.exception.code) |
| 55 | self.assertEqual(0, e.exception.code) | 54 | self.assertEqual("", stderr.getvalue()) |
| 56 | self.assertEqual('', stderr.getvalue()) | 55 | self.assertIn("repo launcher version", stdout.getvalue()) |
| 57 | self.assertIn('repo launcher version', stdout.getvalue()) | 56 | |
| 58 | 57 | def test_python_constraints(self): | |
| 59 | def test_python_constraints(self): | 58 | """The launcher should never require newer than main.py.""" |
| 60 | """The launcher should never require newer than main.py.""" | 59 | self.assertGreaterEqual( |
| 61 | self.assertGreaterEqual(main.MIN_PYTHON_VERSION_HARD, | 60 | main.MIN_PYTHON_VERSION_HARD, self.wrapper.MIN_PYTHON_VERSION_HARD |
| 62 | self.wrapper.MIN_PYTHON_VERSION_HARD) | 61 | ) |
| 63 | self.assertGreaterEqual(main.MIN_PYTHON_VERSION_SOFT, | 62 | self.assertGreaterEqual( |
| 64 | self.wrapper.MIN_PYTHON_VERSION_SOFT) | 63 | main.MIN_PYTHON_VERSION_SOFT, self.wrapper.MIN_PYTHON_VERSION_SOFT |
| 65 | # Make sure the versions are themselves in sync. | 64 | ) |
| 66 | self.assertGreaterEqual(self.wrapper.MIN_PYTHON_VERSION_SOFT, | 65 | # Make sure the versions are themselves in sync. |
| 67 | self.wrapper.MIN_PYTHON_VERSION_HARD) | 66 | self.assertGreaterEqual( |
| 68 | 67 | self.wrapper.MIN_PYTHON_VERSION_SOFT, | |
| 69 | def test_init_parser(self): | 68 | self.wrapper.MIN_PYTHON_VERSION_HARD, |
| 70 | """Make sure 'init' GetParser works.""" | 69 | ) |
| 71 | parser = self.wrapper.GetParser(gitc_init=False) | 70 | |
| 72 | opts, args = parser.parse_args([]) | 71 | def test_init_parser(self): |
| 73 | self.assertEqual([], args) | 72 | """Make sure 'init' GetParser works.""" |
| 74 | self.assertIsNone(opts.manifest_url) | 73 | parser = self.wrapper.GetParser(gitc_init=False) |
| 75 | 74 | opts, args = parser.parse_args([]) | |
| 76 | def test_gitc_init_parser(self): | 75 | self.assertEqual([], args) |
| 77 | """Make sure 'gitc-init' GetParser works.""" | 76 | self.assertIsNone(opts.manifest_url) |
| 78 | parser = self.wrapper.GetParser(gitc_init=True) | 77 | |
| 79 | opts, args = parser.parse_args([]) | 78 | def test_gitc_init_parser(self): |
| 80 | self.assertEqual([], args) | 79 | """Make sure 'gitc-init' GetParser works.""" |
| 81 | self.assertIsNone(opts.manifest_file) | 80 | parser = self.wrapper.GetParser(gitc_init=True) |
| 82 | 81 | opts, args = parser.parse_args([]) | |
| 83 | def test_get_gitc_manifest_dir_no_gitc(self): | 82 | self.assertEqual([], args) |
| 84 | """ | 83 | self.assertIsNone(opts.manifest_file) |
| 85 | Test reading a missing gitc config file | 84 | |
| 86 | """ | 85 | def test_get_gitc_manifest_dir_no_gitc(self): |
| 87 | self.wrapper.GITC_CONFIG_FILE = fixture('missing_gitc_config') | 86 | """ |
| 88 | val = self.wrapper.get_gitc_manifest_dir() | 87 | Test reading a missing gitc config file |
| 89 | self.assertEqual(val, '') | 88 | """ |
| 90 | 89 | self.wrapper.GITC_CONFIG_FILE = fixture("missing_gitc_config") | |
| 91 | def test_get_gitc_manifest_dir(self): | 90 | val = self.wrapper.get_gitc_manifest_dir() |
| 92 | """ | 91 | self.assertEqual(val, "") |
| 93 | Test reading the gitc config file and parsing the directory | 92 | |
| 94 | """ | 93 | def test_get_gitc_manifest_dir(self): |
| 95 | self.wrapper.GITC_CONFIG_FILE = fixture('gitc_config') | 94 | """ |
| 96 | val = self.wrapper.get_gitc_manifest_dir() | 95 | Test reading the gitc config file and parsing the directory |
| 97 | self.assertEqual(val, '/test/usr/local/google/gitc') | 96 | """ |
| 98 | 97 | self.wrapper.GITC_CONFIG_FILE = fixture("gitc_config") | |
| 99 | def test_gitc_parse_clientdir_no_gitc(self): | 98 | val = self.wrapper.get_gitc_manifest_dir() |
| 100 | """ | 99 | self.assertEqual(val, "/test/usr/local/google/gitc") |
| 101 | Test parsing the gitc clientdir without gitc running | 100 | |
| 102 | """ | 101 | def test_gitc_parse_clientdir_no_gitc(self): |
| 103 | self.wrapper.GITC_CONFIG_FILE = fixture('missing_gitc_config') | 102 | """ |
| 104 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/something'), None) | 103 | Test parsing the gitc clientdir without gitc running |
| 105 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/gitc/manifest-rw/test'), 'test') | 104 | """ |
| 106 | 105 | self.wrapper.GITC_CONFIG_FILE = fixture("missing_gitc_config") | |
| 107 | def test_gitc_parse_clientdir(self): | 106 | self.assertEqual(self.wrapper.gitc_parse_clientdir("/something"), None) |
| 108 | """ | 107 | self.assertEqual( |
| 109 | Test parsing the gitc clientdir | 108 | self.wrapper.gitc_parse_clientdir("/gitc/manifest-rw/test"), "test" |
| 110 | """ | 109 | ) |
| 111 | self.wrapper.GITC_CONFIG_FILE = fixture('gitc_config') | 110 | |
| 112 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/something'), None) | 111 | def test_gitc_parse_clientdir(self): |
| 113 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/gitc/manifest-rw/test'), 'test') | 112 | """ |
| 114 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/gitc/manifest-rw/test/'), 'test') | 113 | Test parsing the gitc clientdir |
| 115 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/gitc/manifest-rw/test/extra'), 'test') | 114 | """ |
| 116 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/test/usr/local/google/gitc/test'), 'test') | 115 | self.wrapper.GITC_CONFIG_FILE = fixture("gitc_config") |
| 117 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/test/usr/local/google/gitc/test/'), 'test') | 116 | self.assertEqual(self.wrapper.gitc_parse_clientdir("/something"), None) |
| 118 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/test/usr/local/google/gitc/test/extra'), | 117 | self.assertEqual( |
| 119 | 'test') | 118 | self.wrapper.gitc_parse_clientdir("/gitc/manifest-rw/test"), "test" |
| 120 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/gitc/manifest-rw/'), None) | 119 | ) |
| 121 | self.assertEqual(self.wrapper.gitc_parse_clientdir('/test/usr/local/google/gitc/'), None) | 120 | self.assertEqual( |
| 121 | self.wrapper.gitc_parse_clientdir("/gitc/manifest-rw/test/"), "test" | ||
| 122 | ) | ||
| 123 | self.assertEqual( | ||
| 124 | self.wrapper.gitc_parse_clientdir("/gitc/manifest-rw/test/extra"), | ||
| 125 | "test", | ||
| 126 | ) | ||
| 127 | self.assertEqual( | ||
| 128 | self.wrapper.gitc_parse_clientdir( | ||
| 129 | "/test/usr/local/google/gitc/test" | ||
| 130 | ), | ||
| 131 | "test", | ||
| 132 | ) | ||
| 133 | self.assertEqual( | ||
| 134 | self.wrapper.gitc_parse_clientdir( | ||
| 135 | "/test/usr/local/google/gitc/test/" | ||
| 136 | ), | ||
| 137 | "test", | ||
| 138 | ) | ||
| 139 | self.assertEqual( | ||
| 140 | self.wrapper.gitc_parse_clientdir( | ||
| 141 | "/test/usr/local/google/gitc/test/extra" | ||
| 142 | ), | ||
| 143 | "test", | ||
| 144 | ) | ||
| 145 | self.assertEqual( | ||
| 146 | self.wrapper.gitc_parse_clientdir("/gitc/manifest-rw/"), None | ||
| 147 | ) | ||
| 148 | self.assertEqual( | ||
| 149 | self.wrapper.gitc_parse_clientdir("/test/usr/local/google/gitc/"), | ||
| 150 | None, | ||
| 151 | ) | ||
| 122 | 152 | ||
| 123 | 153 | ||
| 124 | class SetGitTrace2ParentSid(RepoWrapperTestCase): | 154 | class SetGitTrace2ParentSid(RepoWrapperTestCase): |
| 125 | """Check SetGitTrace2ParentSid behavior.""" | 155 | """Check SetGitTrace2ParentSid behavior.""" |
| 126 | 156 | ||
| 127 | KEY = 'GIT_TRACE2_PARENT_SID' | 157 | KEY = "GIT_TRACE2_PARENT_SID" |
| 128 | VALID_FORMAT = re.compile(r'^repo-[0-9]{8}T[0-9]{6}Z-P[0-9a-f]{8}$') | 158 | VALID_FORMAT = re.compile(r"^repo-[0-9]{8}T[0-9]{6}Z-P[0-9a-f]{8}$") |
| 129 | 159 | ||
| 130 | def test_first_set(self): | 160 | def test_first_set(self): |
| 131 | """Test env var not yet set.""" | 161 | """Test env var not yet set.""" |
| 132 | env = {} | 162 | env = {} |
| 133 | self.wrapper.SetGitTrace2ParentSid(env) | 163 | self.wrapper.SetGitTrace2ParentSid(env) |
| 134 | self.assertIn(self.KEY, env) | 164 | self.assertIn(self.KEY, env) |
| 135 | value = env[self.KEY] | 165 | value = env[self.KEY] |
| 136 | self.assertRegex(value, self.VALID_FORMAT) | 166 | self.assertRegex(value, self.VALID_FORMAT) |
| 137 | 167 | ||
| 138 | def test_append(self): | 168 | def test_append(self): |
| 139 | """Test env var is appended.""" | 169 | """Test env var is appended.""" |
| 140 | env = {self.KEY: 'pfx'} | 170 | env = {self.KEY: "pfx"} |
| 141 | self.wrapper.SetGitTrace2ParentSid(env) | 171 | self.wrapper.SetGitTrace2ParentSid(env) |
| 142 | self.assertIn(self.KEY, env) | 172 | self.assertIn(self.KEY, env) |
| 143 | value = env[self.KEY] | 173 | value = env[self.KEY] |
| 144 | self.assertTrue(value.startswith('pfx/')) | 174 | self.assertTrue(value.startswith("pfx/")) |
| 145 | self.assertRegex(value[4:], self.VALID_FORMAT) | 175 | self.assertRegex(value[4:], self.VALID_FORMAT) |
| 146 | 176 | ||
| 147 | def test_global_context(self): | 177 | def test_global_context(self): |
| 148 | """Check os.environ gets updated by default.""" | 178 | """Check os.environ gets updated by default.""" |
| 149 | os.environ.pop(self.KEY, None) | 179 | os.environ.pop(self.KEY, None) |
| 150 | self.wrapper.SetGitTrace2ParentSid() | 180 | self.wrapper.SetGitTrace2ParentSid() |
| 151 | self.assertIn(self.KEY, os.environ) | 181 | self.assertIn(self.KEY, os.environ) |
| 152 | value = os.environ[self.KEY] | 182 | value = os.environ[self.KEY] |
| 153 | self.assertRegex(value, self.VALID_FORMAT) | 183 | self.assertRegex(value, self.VALID_FORMAT) |
| 154 | 184 | ||
| 155 | 185 | ||
| 156 | class RunCommand(RepoWrapperTestCase): | 186 | class RunCommand(RepoWrapperTestCase): |
| 157 | """Check run_command behavior.""" | 187 | """Check run_command behavior.""" |
| 158 | 188 | ||
| 159 | def test_capture(self): | 189 | def test_capture(self): |
| 160 | """Check capture_output handling.""" | 190 | """Check capture_output handling.""" |
| 161 | ret = self.wrapper.run_command(['echo', 'hi'], capture_output=True) | 191 | ret = self.wrapper.run_command(["echo", "hi"], capture_output=True) |
| 162 | # echo command appends OS specific linesep, but on Windows + Git Bash | 192 | # echo command appends OS specific linesep, but on Windows + Git Bash |
| 163 | # we get UNIX ending, so we allow both. | 193 | # we get UNIX ending, so we allow both. |
| 164 | self.assertIn(ret.stdout, ['hi' + os.linesep, 'hi\n']) | 194 | self.assertIn(ret.stdout, ["hi" + os.linesep, "hi\n"]) |
| 165 | 195 | ||
| 166 | def test_check(self): | 196 | def test_check(self): |
| 167 | """Check check handling.""" | 197 | """Check check handling.""" |
| 168 | self.wrapper.run_command(['true'], check=False) | 198 | self.wrapper.run_command(["true"], check=False) |
| 169 | self.wrapper.run_command(['true'], check=True) | 199 | self.wrapper.run_command(["true"], check=True) |
| 170 | self.wrapper.run_command(['false'], check=False) | 200 | self.wrapper.run_command(["false"], check=False) |
| 171 | with self.assertRaises(self.wrapper.RunError): | 201 | with self.assertRaises(self.wrapper.RunError): |
| 172 | self.wrapper.run_command(['false'], check=True) | 202 | self.wrapper.run_command(["false"], check=True) |
| 173 | 203 | ||
| 174 | 204 | ||
| 175 | class RunGit(RepoWrapperTestCase): | 205 | class RunGit(RepoWrapperTestCase): |
| 176 | """Check run_git behavior.""" | 206 | """Check run_git behavior.""" |
| 177 | 207 | ||
| 178 | def test_capture(self): | 208 | def test_capture(self): |
| 179 | """Check capture_output handling.""" | 209 | """Check capture_output handling.""" |
| 180 | ret = self.wrapper.run_git('--version') | 210 | ret = self.wrapper.run_git("--version") |
| 181 | self.assertIn('git', ret.stdout) | 211 | self.assertIn("git", ret.stdout) |
| 182 | 212 | ||
| 183 | def test_check(self): | 213 | def test_check(self): |
| 184 | """Check check handling.""" | 214 | """Check check handling.""" |
| 185 | with self.assertRaises(self.wrapper.CloneFailure): | 215 | with self.assertRaises(self.wrapper.CloneFailure): |
| 186 | self.wrapper.run_git('--version-asdfasdf') | 216 | self.wrapper.run_git("--version-asdfasdf") |
| 187 | self.wrapper.run_git('--version-asdfasdf', check=False) | 217 | self.wrapper.run_git("--version-asdfasdf", check=False) |
| 188 | 218 | ||
| 189 | 219 | ||
| 190 | class ParseGitVersion(RepoWrapperTestCase): | 220 | class ParseGitVersion(RepoWrapperTestCase): |
| 191 | """Check ParseGitVersion behavior.""" | 221 | """Check ParseGitVersion behavior.""" |
| 192 | 222 | ||
| 193 | def test_autoload(self): | 223 | def test_autoload(self): |
| 194 | """Check we can load the version from the live git.""" | 224 | """Check we can load the version from the live git.""" |
| 195 | ret = self.wrapper.ParseGitVersion() | 225 | ret = self.wrapper.ParseGitVersion() |
| 196 | self.assertIsNotNone(ret) | 226 | self.assertIsNotNone(ret) |
| 197 | 227 | ||
| 198 | def test_bad_ver(self): | 228 | def test_bad_ver(self): |
| 199 | """Check handling of bad git versions.""" | 229 | """Check handling of bad git versions.""" |
| 200 | ret = self.wrapper.ParseGitVersion(ver_str='asdf') | 230 | ret = self.wrapper.ParseGitVersion(ver_str="asdf") |
| 201 | self.assertIsNone(ret) | 231 | self.assertIsNone(ret) |
| 202 | 232 | ||
| 203 | def test_normal_ver(self): | 233 | def test_normal_ver(self): |
| 204 | """Check handling of normal git versions.""" | 234 | """Check handling of normal git versions.""" |
| 205 | ret = self.wrapper.ParseGitVersion(ver_str='git version 2.25.1') | 235 | ret = self.wrapper.ParseGitVersion(ver_str="git version 2.25.1") |
| 206 | self.assertEqual(2, ret.major) | 236 | self.assertEqual(2, ret.major) |
| 207 | self.assertEqual(25, ret.minor) | 237 | self.assertEqual(25, ret.minor) |
| 208 | self.assertEqual(1, ret.micro) | 238 | self.assertEqual(1, ret.micro) |
| 209 | self.assertEqual('2.25.1', ret.full) | 239 | self.assertEqual("2.25.1", ret.full) |
| 210 | 240 | ||
| 211 | def test_extended_ver(self): | 241 | def test_extended_ver(self): |
| 212 | """Check handling of extended distro git versions.""" | 242 | """Check handling of extended distro git versions.""" |
| 213 | ret = self.wrapper.ParseGitVersion( | 243 | ret = self.wrapper.ParseGitVersion( |
| 214 | ver_str='git version 1.30.50.696.g5e7596f4ac-goog') | 244 | ver_str="git version 1.30.50.696.g5e7596f4ac-goog" |
| 215 | self.assertEqual(1, ret.major) | 245 | ) |
| 216 | self.assertEqual(30, ret.minor) | 246 | self.assertEqual(1, ret.major) |
| 217 | self.assertEqual(50, ret.micro) | 247 | self.assertEqual(30, ret.minor) |
| 218 | self.assertEqual('1.30.50.696.g5e7596f4ac-goog', ret.full) | 248 | self.assertEqual(50, ret.micro) |
| 249 | self.assertEqual("1.30.50.696.g5e7596f4ac-goog", ret.full) | ||
| 219 | 250 | ||
| 220 | 251 | ||
| 221 | class CheckGitVersion(RepoWrapperTestCase): | 252 | class CheckGitVersion(RepoWrapperTestCase): |
| 222 | """Check _CheckGitVersion behavior.""" | 253 | """Check _CheckGitVersion behavior.""" |
| 223 | 254 | ||
| 224 | def test_unknown(self): | 255 | def test_unknown(self): |
| 225 | """Unknown versions should abort.""" | 256 | """Unknown versions should abort.""" |
| 226 | with mock.patch.object(self.wrapper, 'ParseGitVersion', return_value=None): | 257 | with mock.patch.object( |
| 227 | with self.assertRaises(self.wrapper.CloneFailure): | 258 | self.wrapper, "ParseGitVersion", return_value=None |
| 228 | self.wrapper._CheckGitVersion() | 259 | ): |
| 229 | 260 | with self.assertRaises(self.wrapper.CloneFailure): | |
| 230 | def test_old(self): | 261 | self.wrapper._CheckGitVersion() |
| 231 | """Old versions should abort.""" | 262 | |
| 232 | with mock.patch.object( | 263 | def test_old(self): |
| 233 | self.wrapper, 'ParseGitVersion', | 264 | """Old versions should abort.""" |
| 234 | return_value=self.wrapper.GitVersion(1, 0, 0, '1.0.0')): | 265 | with mock.patch.object( |
| 235 | with self.assertRaises(self.wrapper.CloneFailure): | 266 | self.wrapper, |
| 236 | self.wrapper._CheckGitVersion() | 267 | "ParseGitVersion", |
| 237 | 268 | return_value=self.wrapper.GitVersion(1, 0, 0, "1.0.0"), | |
| 238 | def test_new(self): | 269 | ): |
| 239 | """Newer versions should run fine.""" | 270 | with self.assertRaises(self.wrapper.CloneFailure): |
| 240 | with mock.patch.object( | 271 | self.wrapper._CheckGitVersion() |
| 241 | self.wrapper, 'ParseGitVersion', | 272 | |
| 242 | return_value=self.wrapper.GitVersion(100, 0, 0, '100.0.0')): | 273 | def test_new(self): |
| 243 | self.wrapper._CheckGitVersion() | 274 | """Newer versions should run fine.""" |
| 275 | with mock.patch.object( | ||
| 276 | self.wrapper, | ||
| 277 | "ParseGitVersion", | ||
| 278 | return_value=self.wrapper.GitVersion(100, 0, 0, "100.0.0"), | ||
| 279 | ): | ||
| 280 | self.wrapper._CheckGitVersion() | ||
| 244 | 281 | ||
| 245 | 282 | ||
| 246 | class Requirements(RepoWrapperTestCase): | 283 | class Requirements(RepoWrapperTestCase): |
| 247 | """Check Requirements handling.""" | 284 | """Check Requirements handling.""" |
| 248 | 285 | ||
| 249 | def test_missing_file(self): | 286 | def test_missing_file(self): |
| 250 | """Don't crash if the file is missing (old version).""" | 287 | """Don't crash if the file is missing (old version).""" |
| 251 | testdir = os.path.dirname(os.path.realpath(__file__)) | 288 | testdir = os.path.dirname(os.path.realpath(__file__)) |
| 252 | self.assertIsNone(self.wrapper.Requirements.from_dir(testdir)) | 289 | self.assertIsNone(self.wrapper.Requirements.from_dir(testdir)) |
| 253 | self.assertIsNone(self.wrapper.Requirements.from_file( | 290 | self.assertIsNone( |
| 254 | os.path.join(testdir, 'xxxxxxxxxxxxxxxxxxxxxxxx'))) | 291 | self.wrapper.Requirements.from_file( |
| 255 | 292 | os.path.join(testdir, "xxxxxxxxxxxxxxxxxxxxxxxx") | |
| 256 | def test_corrupt_data(self): | 293 | ) |
| 257 | """If the file can't be parsed, don't blow up.""" | 294 | ) |
| 258 | self.assertIsNone(self.wrapper.Requirements.from_file(__file__)) | 295 | |
| 259 | self.assertIsNone(self.wrapper.Requirements.from_data(b'x')) | 296 | def test_corrupt_data(self): |
| 260 | 297 | """If the file can't be parsed, don't blow up.""" | |
| 261 | def test_valid_data(self): | 298 | self.assertIsNone(self.wrapper.Requirements.from_file(__file__)) |
| 262 | """Make sure we can parse the file we ship.""" | 299 | self.assertIsNone(self.wrapper.Requirements.from_data(b"x")) |
| 263 | self.assertIsNotNone(self.wrapper.Requirements.from_data(b'{}')) | 300 | |
| 264 | rootdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | 301 | def test_valid_data(self): |
| 265 | self.assertIsNotNone(self.wrapper.Requirements.from_dir(rootdir)) | 302 | """Make sure we can parse the file we ship.""" |
| 266 | self.assertIsNotNone(self.wrapper.Requirements.from_file(os.path.join( | 303 | self.assertIsNotNone(self.wrapper.Requirements.from_data(b"{}")) |
| 267 | rootdir, 'requirements.json'))) | 304 | rootdir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
| 268 | 305 | self.assertIsNotNone(self.wrapper.Requirements.from_dir(rootdir)) | |
| 269 | def test_format_ver(self): | 306 | self.assertIsNotNone( |
| 270 | """Check format_ver can format.""" | 307 | self.wrapper.Requirements.from_file( |
| 271 | self.assertEqual('1.2.3', self.wrapper.Requirements._format_ver((1, 2, 3))) | 308 | os.path.join(rootdir, "requirements.json") |
| 272 | self.assertEqual('1', self.wrapper.Requirements._format_ver([1])) | 309 | ) |
| 273 | 310 | ) | |
| 274 | def test_assert_all_unknown(self): | 311 | |
| 275 | """Check assert_all works with incompatible file.""" | 312 | def test_format_ver(self): |
| 276 | reqs = self.wrapper.Requirements({}) | 313 | """Check format_ver can format.""" |
| 277 | reqs.assert_all() | 314 | self.assertEqual( |
| 278 | 315 | "1.2.3", self.wrapper.Requirements._format_ver((1, 2, 3)) | |
| 279 | def test_assert_all_new_repo(self): | 316 | ) |
| 280 | """Check assert_all accepts new enough repo.""" | 317 | self.assertEqual("1", self.wrapper.Requirements._format_ver([1])) |
| 281 | reqs = self.wrapper.Requirements({'repo': {'hard': [1, 0]}}) | 318 | |
| 282 | reqs.assert_all() | 319 | def test_assert_all_unknown(self): |
| 283 | 320 | """Check assert_all works with incompatible file.""" | |
| 284 | def test_assert_all_old_repo(self): | 321 | reqs = self.wrapper.Requirements({}) |
| 285 | """Check assert_all rejects old repo.""" | 322 | reqs.assert_all() |
| 286 | reqs = self.wrapper.Requirements({'repo': {'hard': [99999, 0]}}) | 323 | |
| 287 | with self.assertRaises(SystemExit): | 324 | def test_assert_all_new_repo(self): |
| 288 | reqs.assert_all() | 325 | """Check assert_all accepts new enough repo.""" |
| 289 | 326 | reqs = self.wrapper.Requirements({"repo": {"hard": [1, 0]}}) | |
| 290 | def test_assert_all_new_python(self): | 327 | reqs.assert_all() |
| 291 | """Check assert_all accepts new enough python.""" | 328 | |
| 292 | reqs = self.wrapper.Requirements({'python': {'hard': sys.version_info}}) | 329 | def test_assert_all_old_repo(self): |
| 293 | reqs.assert_all() | 330 | """Check assert_all rejects old repo.""" |
| 294 | 331 | reqs = self.wrapper.Requirements({"repo": {"hard": [99999, 0]}}) | |
| 295 | def test_assert_all_old_python(self): | 332 | with self.assertRaises(SystemExit): |
| 296 | """Check assert_all rejects old python.""" | 333 | reqs.assert_all() |
| 297 | reqs = self.wrapper.Requirements({'python': {'hard': [99999, 0]}}) | 334 | |
| 298 | with self.assertRaises(SystemExit): | 335 | def test_assert_all_new_python(self): |
| 299 | reqs.assert_all() | 336 | """Check assert_all accepts new enough python.""" |
| 300 | 337 | reqs = self.wrapper.Requirements({"python": {"hard": sys.version_info}}) | |
| 301 | def test_assert_ver_unknown(self): | 338 | reqs.assert_all() |
| 302 | """Check assert_ver works with incompatible file.""" | 339 | |
| 303 | reqs = self.wrapper.Requirements({}) | 340 | def test_assert_all_old_python(self): |
| 304 | reqs.assert_ver('xxx', (1, 0)) | 341 | """Check assert_all rejects old python.""" |
| 305 | 342 | reqs = self.wrapper.Requirements({"python": {"hard": [99999, 0]}}) | |
| 306 | def test_assert_ver_new(self): | 343 | with self.assertRaises(SystemExit): |
| 307 | """Check assert_ver allows new enough versions.""" | 344 | reqs.assert_all() |
| 308 | reqs = self.wrapper.Requirements({'git': {'hard': [1, 0], 'soft': [2, 0]}}) | 345 | |
| 309 | reqs.assert_ver('git', (1, 0)) | 346 | def test_assert_ver_unknown(self): |
| 310 | reqs.assert_ver('git', (1, 5)) | 347 | """Check assert_ver works with incompatible file.""" |
| 311 | reqs.assert_ver('git', (2, 0)) | 348 | reqs = self.wrapper.Requirements({}) |
| 312 | reqs.assert_ver('git', (2, 5)) | 349 | reqs.assert_ver("xxx", (1, 0)) |
| 313 | 350 | ||
| 314 | def test_assert_ver_old(self): | 351 | def test_assert_ver_new(self): |
| 315 | """Check assert_ver rejects old versions.""" | 352 | """Check assert_ver allows new enough versions.""" |
| 316 | reqs = self.wrapper.Requirements({'git': {'hard': [1, 0], 'soft': [2, 0]}}) | 353 | reqs = self.wrapper.Requirements( |
| 317 | with self.assertRaises(SystemExit): | 354 | {"git": {"hard": [1, 0], "soft": [2, 0]}} |
| 318 | reqs.assert_ver('git', (0, 5)) | 355 | ) |
| 356 | reqs.assert_ver("git", (1, 0)) | ||
| 357 | reqs.assert_ver("git", (1, 5)) | ||
| 358 | reqs.assert_ver("git", (2, 0)) | ||
| 359 | reqs.assert_ver("git", (2, 5)) | ||
| 360 | |||
| 361 | def test_assert_ver_old(self): | ||
| 362 | """Check assert_ver rejects old versions.""" | ||
| 363 | reqs = self.wrapper.Requirements( | ||
| 364 | {"git": {"hard": [1, 0], "soft": [2, 0]}} | ||
| 365 | ) | ||
| 366 | with self.assertRaises(SystemExit): | ||
| 367 | reqs.assert_ver("git", (0, 5)) | ||
| 319 | 368 | ||
| 320 | 369 | ||
| 321 | class NeedSetupGnuPG(RepoWrapperTestCase): | 370 | class NeedSetupGnuPG(RepoWrapperTestCase): |
| 322 | """Check NeedSetupGnuPG behavior.""" | 371 | """Check NeedSetupGnuPG behavior.""" |
| 323 | 372 | ||
| 324 | def test_missing_dir(self): | 373 | def test_missing_dir(self): |
| 325 | """The ~/.repoconfig tree doesn't exist yet.""" | 374 | """The ~/.repoconfig tree doesn't exist yet.""" |
| 326 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 375 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 327 | self.wrapper.home_dot_repo = os.path.join(tempdir, 'foo') | 376 | self.wrapper.home_dot_repo = os.path.join(tempdir, "foo") |
| 328 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | 377 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) |
| 329 | 378 | ||
| 330 | def test_missing_keyring(self): | 379 | def test_missing_keyring(self): |
| 331 | """The keyring-version file doesn't exist yet.""" | 380 | """The keyring-version file doesn't exist yet.""" |
| 332 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 381 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 333 | self.wrapper.home_dot_repo = tempdir | 382 | self.wrapper.home_dot_repo = tempdir |
| 334 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | 383 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) |
| 335 | 384 | ||
| 336 | def test_empty_keyring(self): | 385 | def test_empty_keyring(self): |
| 337 | """The keyring-version file exists, but is empty.""" | 386 | """The keyring-version file exists, but is empty.""" |
| 338 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 387 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 339 | self.wrapper.home_dot_repo = tempdir | 388 | self.wrapper.home_dot_repo = tempdir |
| 340 | with open(os.path.join(tempdir, 'keyring-version'), 'w'): | 389 | with open(os.path.join(tempdir, "keyring-version"), "w"): |
| 341 | pass | 390 | pass |
| 342 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | 391 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) |
| 343 | 392 | ||
| 344 | def test_old_keyring(self): | 393 | def test_old_keyring(self): |
| 345 | """The keyring-version file exists, but it's old.""" | 394 | """The keyring-version file exists, but it's old.""" |
| 346 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 395 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 347 | self.wrapper.home_dot_repo = tempdir | 396 | self.wrapper.home_dot_repo = tempdir |
| 348 | with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp: | 397 | with open(os.path.join(tempdir, "keyring-version"), "w") as fp: |
| 349 | fp.write('1.0\n') | 398 | fp.write("1.0\n") |
| 350 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) | 399 | self.assertTrue(self.wrapper.NeedSetupGnuPG()) |
| 351 | 400 | ||
| 352 | def test_new_keyring(self): | 401 | def test_new_keyring(self): |
| 353 | """The keyring-version file exists, and is up-to-date.""" | 402 | """The keyring-version file exists, and is up-to-date.""" |
| 354 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 403 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 355 | self.wrapper.home_dot_repo = tempdir | 404 | self.wrapper.home_dot_repo = tempdir |
| 356 | with open(os.path.join(tempdir, 'keyring-version'), 'w') as fp: | 405 | with open(os.path.join(tempdir, "keyring-version"), "w") as fp: |
| 357 | fp.write('1000.0\n') | 406 | fp.write("1000.0\n") |
| 358 | self.assertFalse(self.wrapper.NeedSetupGnuPG()) | 407 | self.assertFalse(self.wrapper.NeedSetupGnuPG()) |
| 359 | 408 | ||
| 360 | 409 | ||
| 361 | class SetupGnuPG(RepoWrapperTestCase): | 410 | class SetupGnuPG(RepoWrapperTestCase): |
| 362 | """Check SetupGnuPG behavior.""" | 411 | """Check SetupGnuPG behavior.""" |
| 363 | 412 | ||
| 364 | def test_full(self): | 413 | def test_full(self): |
| 365 | """Make sure it works completely.""" | 414 | """Make sure it works completely.""" |
| 366 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 415 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 367 | self.wrapper.home_dot_repo = tempdir | 416 | self.wrapper.home_dot_repo = tempdir |
| 368 | self.wrapper.gpg_dir = os.path.join(self.wrapper.home_dot_repo, 'gnupg') | 417 | self.wrapper.gpg_dir = os.path.join( |
| 369 | self.assertTrue(self.wrapper.SetupGnuPG(True)) | 418 | self.wrapper.home_dot_repo, "gnupg" |
| 370 | with open(os.path.join(tempdir, 'keyring-version'), 'r') as fp: | 419 | ) |
| 371 | data = fp.read() | 420 | self.assertTrue(self.wrapper.SetupGnuPG(True)) |
| 372 | self.assertEqual('.'.join(str(x) for x in self.wrapper.KEYRING_VERSION), | 421 | with open(os.path.join(tempdir, "keyring-version"), "r") as fp: |
| 373 | data.strip()) | 422 | data = fp.read() |
| 423 | self.assertEqual( | ||
| 424 | ".".join(str(x) for x in self.wrapper.KEYRING_VERSION), | ||
| 425 | data.strip(), | ||
| 426 | ) | ||
| 374 | 427 | ||
| 375 | 428 | ||
| 376 | class VerifyRev(RepoWrapperTestCase): | 429 | class VerifyRev(RepoWrapperTestCase): |
| 377 | """Check verify_rev behavior.""" | 430 | """Check verify_rev behavior.""" |
| 378 | 431 | ||
| 379 | def test_verify_passes(self): | 432 | def test_verify_passes(self): |
| 380 | """Check when we have a valid signed tag.""" | 433 | """Check when we have a valid signed tag.""" |
| 381 | desc_result = self.wrapper.RunResult(0, 'v1.0\n', '') | 434 | desc_result = self.wrapper.RunResult(0, "v1.0\n", "") |
| 382 | gpg_result = self.wrapper.RunResult(0, '', '') | 435 | gpg_result = self.wrapper.RunResult(0, "", "") |
| 383 | with mock.patch.object(self.wrapper, 'run_git', | 436 | with mock.patch.object( |
| 384 | side_effect=(desc_result, gpg_result)): | 437 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) |
| 385 | ret = self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | 438 | ): |
| 386 | self.assertEqual('v1.0^0', ret) | 439 | ret = self.wrapper.verify_rev( |
| 387 | 440 | "/", "refs/heads/stable", "1234", True | |
| 388 | def test_unsigned_commit(self): | 441 | ) |
| 389 | """Check we fall back to signed tag when we have an unsigned commit.""" | 442 | self.assertEqual("v1.0^0", ret) |
| 390 | desc_result = self.wrapper.RunResult(0, 'v1.0-10-g1234\n', '') | 443 | |
| 391 | gpg_result = self.wrapper.RunResult(0, '', '') | 444 | def test_unsigned_commit(self): |
| 392 | with mock.patch.object(self.wrapper, 'run_git', | 445 | """Check we fall back to signed tag when we have an unsigned commit.""" |
| 393 | side_effect=(desc_result, gpg_result)): | 446 | desc_result = self.wrapper.RunResult(0, "v1.0-10-g1234\n", "") |
| 394 | ret = self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | 447 | gpg_result = self.wrapper.RunResult(0, "", "") |
| 395 | self.assertEqual('v1.0^0', ret) | 448 | with mock.patch.object( |
| 396 | 449 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) | |
| 397 | def test_verify_fails(self): | 450 | ): |
| 398 | """Check we fall back to signed tag when we have an unsigned commit.""" | 451 | ret = self.wrapper.verify_rev( |
| 399 | desc_result = self.wrapper.RunResult(0, 'v1.0-10-g1234\n', '') | 452 | "/", "refs/heads/stable", "1234", True |
| 400 | gpg_result = Exception | 453 | ) |
| 401 | with mock.patch.object(self.wrapper, 'run_git', | 454 | self.assertEqual("v1.0^0", ret) |
| 402 | side_effect=(desc_result, gpg_result)): | 455 | |
| 403 | with self.assertRaises(Exception): | 456 | def test_verify_fails(self): |
| 404 | self.wrapper.verify_rev('/', 'refs/heads/stable', '1234', True) | 457 | """Check we fall back to signed tag when we have an unsigned commit.""" |
| 458 | desc_result = self.wrapper.RunResult(0, "v1.0-10-g1234\n", "") | ||
| 459 | gpg_result = Exception | ||
| 460 | with mock.patch.object( | ||
| 461 | self.wrapper, "run_git", side_effect=(desc_result, gpg_result) | ||
| 462 | ): | ||
| 463 | with self.assertRaises(Exception): | ||
| 464 | self.wrapper.verify_rev("/", "refs/heads/stable", "1234", True) | ||
| 405 | 465 | ||
| 406 | 466 | ||
| 407 | class GitCheckoutTestCase(RepoWrapperTestCase): | 467 | class GitCheckoutTestCase(RepoWrapperTestCase): |
| 408 | """Tests that use a real/small git checkout.""" | 468 | """Tests that use a real/small git checkout.""" |
| 409 | 469 | ||
| 410 | GIT_DIR = None | 470 | GIT_DIR = None |
| 411 | REV_LIST = None | 471 | REV_LIST = None |
| 412 | 472 | ||
| 413 | @classmethod | 473 | @classmethod |
| 414 | def setUpClass(cls): | 474 | def setUpClass(cls): |
| 415 | # Create a repo to operate on, but do it once per-class. | 475 | # Create a repo to operate on, but do it once per-class. |
| 416 | cls.tempdirobj = tempfile.TemporaryDirectory(prefix='repo-rev-tests') | 476 | cls.tempdirobj = tempfile.TemporaryDirectory(prefix="repo-rev-tests") |
| 417 | cls.GIT_DIR = cls.tempdirobj.name | 477 | cls.GIT_DIR = cls.tempdirobj.name |
| 418 | run_git = wrapper.Wrapper().run_git | 478 | run_git = wrapper.Wrapper().run_git |
| 419 | 479 | ||
| 420 | remote = os.path.join(cls.GIT_DIR, 'remote') | 480 | remote = os.path.join(cls.GIT_DIR, "remote") |
| 421 | os.mkdir(remote) | 481 | os.mkdir(remote) |
| 422 | 482 | ||
| 423 | # Tests need to assume, that main is default branch at init, | 483 | # Tests need to assume, that main is default branch at init, |
| 424 | # which is not supported in config until 2.28. | 484 | # which is not supported in config until 2.28. |
| 425 | if git_command.git_require((2, 28, 0)): | 485 | if git_command.git_require((2, 28, 0)): |
| 426 | initstr = '--initial-branch=main' | 486 | initstr = "--initial-branch=main" |
| 427 | else: | 487 | else: |
| 428 | # Use template dir for init. | 488 | # Use template dir for init. |
| 429 | templatedir = tempfile.mkdtemp(prefix='.test-template') | 489 | templatedir = tempfile.mkdtemp(prefix=".test-template") |
| 430 | with open(os.path.join(templatedir, 'HEAD'), 'w') as fp: | 490 | with open(os.path.join(templatedir, "HEAD"), "w") as fp: |
| 431 | fp.write('ref: refs/heads/main\n') | 491 | fp.write("ref: refs/heads/main\n") |
| 432 | initstr = '--template=' + templatedir | 492 | initstr = "--template=" + templatedir |
| 433 | 493 | ||
| 434 | run_git('init', initstr, cwd=remote) | 494 | run_git("init", initstr, cwd=remote) |
| 435 | run_git('commit', '--allow-empty', '-minit', cwd=remote) | 495 | run_git("commit", "--allow-empty", "-minit", cwd=remote) |
| 436 | run_git('branch', 'stable', cwd=remote) | 496 | run_git("branch", "stable", cwd=remote) |
| 437 | run_git('tag', 'v1.0', cwd=remote) | 497 | run_git("tag", "v1.0", cwd=remote) |
| 438 | run_git('commit', '--allow-empty', '-m2nd commit', cwd=remote) | 498 | run_git("commit", "--allow-empty", "-m2nd commit", cwd=remote) |
| 439 | cls.REV_LIST = run_git('rev-list', 'HEAD', cwd=remote).stdout.splitlines() | 499 | cls.REV_LIST = run_git( |
| 440 | 500 | "rev-list", "HEAD", cwd=remote | |
| 441 | run_git('init', cwd=cls.GIT_DIR) | 501 | ).stdout.splitlines() |
| 442 | run_git('fetch', remote, '+refs/heads/*:refs/remotes/origin/*', cwd=cls.GIT_DIR) | 502 | |
| 443 | 503 | run_git("init", cwd=cls.GIT_DIR) | |
| 444 | @classmethod | 504 | run_git( |
| 445 | def tearDownClass(cls): | 505 | "fetch", |
| 446 | if not cls.tempdirobj: | 506 | remote, |
| 447 | return | 507 | "+refs/heads/*:refs/remotes/origin/*", |
| 448 | 508 | cwd=cls.GIT_DIR, | |
| 449 | cls.tempdirobj.cleanup() | 509 | ) |
| 510 | |||
| 511 | @classmethod | ||
| 512 | def tearDownClass(cls): | ||
| 513 | if not cls.tempdirobj: | ||
| 514 | return | ||
| 515 | |||
| 516 | cls.tempdirobj.cleanup() | ||
| 450 | 517 | ||
| 451 | 518 | ||
| 452 | class ResolveRepoRev(GitCheckoutTestCase): | 519 | class ResolveRepoRev(GitCheckoutTestCase): |
| 453 | """Check resolve_repo_rev behavior.""" | 520 | """Check resolve_repo_rev behavior.""" |
| 454 | 521 | ||
| 455 | def test_explicit_branch(self): | 522 | def test_explicit_branch(self): |
| 456 | """Check refs/heads/branch argument.""" | 523 | """Check refs/heads/branch argument.""" |
| 457 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/stable') | 524 | rrev, lrev = self.wrapper.resolve_repo_rev( |
| 458 | self.assertEqual('refs/heads/stable', rrev) | 525 | self.GIT_DIR, "refs/heads/stable" |
| 459 | self.assertEqual(self.REV_LIST[1], lrev) | 526 | ) |
| 460 | 527 | self.assertEqual("refs/heads/stable", rrev) | |
| 461 | with self.assertRaises(self.wrapper.CloneFailure): | 528 | self.assertEqual(self.REV_LIST[1], lrev) |
| 462 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/heads/unknown') | 529 | |
| 463 | 530 | with self.assertRaises(self.wrapper.CloneFailure): | |
| 464 | def test_explicit_tag(self): | 531 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "refs/heads/unknown") |
| 465 | """Check refs/tags/tag argument.""" | 532 | |
| 466 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/tags/v1.0') | 533 | def test_explicit_tag(self): |
| 467 | self.assertEqual('refs/tags/v1.0', rrev) | 534 | """Check refs/tags/tag argument.""" |
| 468 | self.assertEqual(self.REV_LIST[1], lrev) | 535 | rrev, lrev = self.wrapper.resolve_repo_rev( |
| 469 | 536 | self.GIT_DIR, "refs/tags/v1.0" | |
| 470 | with self.assertRaises(self.wrapper.CloneFailure): | 537 | ) |
| 471 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'refs/tags/unknown') | 538 | self.assertEqual("refs/tags/v1.0", rrev) |
| 472 | 539 | self.assertEqual(self.REV_LIST[1], lrev) | |
| 473 | def test_branch_name(self): | 540 | |
| 474 | """Check branch argument.""" | 541 | with self.assertRaises(self.wrapper.CloneFailure): |
| 475 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'stable') | 542 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "refs/tags/unknown") |
| 476 | self.assertEqual('refs/heads/stable', rrev) | 543 | |
| 477 | self.assertEqual(self.REV_LIST[1], lrev) | 544 | def test_branch_name(self): |
| 478 | 545 | """Check branch argument.""" | |
| 479 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'main') | 546 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "stable") |
| 480 | self.assertEqual('refs/heads/main', rrev) | 547 | self.assertEqual("refs/heads/stable", rrev) |
| 481 | self.assertEqual(self.REV_LIST[0], lrev) | 548 | self.assertEqual(self.REV_LIST[1], lrev) |
| 482 | 549 | ||
| 483 | def test_tag_name(self): | 550 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "main") |
| 484 | """Check tag argument.""" | 551 | self.assertEqual("refs/heads/main", rrev) |
| 485 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, 'v1.0') | 552 | self.assertEqual(self.REV_LIST[0], lrev) |
| 486 | self.assertEqual('refs/tags/v1.0', rrev) | 553 | |
| 487 | self.assertEqual(self.REV_LIST[1], lrev) | 554 | def test_tag_name(self): |
| 488 | 555 | """Check tag argument.""" | |
| 489 | def test_full_commit(self): | 556 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, "v1.0") |
| 490 | """Check specific commit argument.""" | 557 | self.assertEqual("refs/tags/v1.0", rrev) |
| 491 | commit = self.REV_LIST[0] | 558 | self.assertEqual(self.REV_LIST[1], lrev) |
| 492 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | 559 | |
| 493 | self.assertEqual(commit, rrev) | 560 | def test_full_commit(self): |
| 494 | self.assertEqual(commit, lrev) | 561 | """Check specific commit argument.""" |
| 495 | 562 | commit = self.REV_LIST[0] | |
| 496 | def test_partial_commit(self): | 563 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) |
| 497 | """Check specific (partial) commit argument.""" | 564 | self.assertEqual(commit, rrev) |
| 498 | commit = self.REV_LIST[0][0:20] | 565 | self.assertEqual(commit, lrev) |
| 499 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) | 566 | |
| 500 | self.assertEqual(self.REV_LIST[0], rrev) | 567 | def test_partial_commit(self): |
| 501 | self.assertEqual(self.REV_LIST[0], lrev) | 568 | """Check specific (partial) commit argument.""" |
| 502 | 569 | commit = self.REV_LIST[0][0:20] | |
| 503 | def test_unknown(self): | 570 | rrev, lrev = self.wrapper.resolve_repo_rev(self.GIT_DIR, commit) |
| 504 | """Check unknown ref/commit argument.""" | 571 | self.assertEqual(self.REV_LIST[0], rrev) |
| 505 | with self.assertRaises(self.wrapper.CloneFailure): | 572 | self.assertEqual(self.REV_LIST[0], lrev) |
| 506 | self.wrapper.resolve_repo_rev(self.GIT_DIR, 'boooooooya') | 573 | |
| 574 | def test_unknown(self): | ||
| 575 | """Check unknown ref/commit argument.""" | ||
| 576 | with self.assertRaises(self.wrapper.CloneFailure): | ||
| 577 | self.wrapper.resolve_repo_rev(self.GIT_DIR, "boooooooya") | ||
| 507 | 578 | ||
| 508 | 579 | ||
| 509 | class CheckRepoVerify(RepoWrapperTestCase): | 580 | class CheckRepoVerify(RepoWrapperTestCase): |
| 510 | """Check check_repo_verify behavior.""" | 581 | """Check check_repo_verify behavior.""" |
| 511 | 582 | ||
| 512 | def test_no_verify(self): | 583 | def test_no_verify(self): |
| 513 | """Always fail with --no-repo-verify.""" | 584 | """Always fail with --no-repo-verify.""" |
| 514 | self.assertFalse(self.wrapper.check_repo_verify(False)) | 585 | self.assertFalse(self.wrapper.check_repo_verify(False)) |
| 515 | 586 | ||
| 516 | def test_gpg_initialized(self): | 587 | def test_gpg_initialized(self): |
| 517 | """Should pass if gpg is setup already.""" | 588 | """Should pass if gpg is setup already.""" |
| 518 | with mock.patch.object(self.wrapper, 'NeedSetupGnuPG', return_value=False): | 589 | with mock.patch.object( |
| 519 | self.assertTrue(self.wrapper.check_repo_verify(True)) | 590 | self.wrapper, "NeedSetupGnuPG", return_value=False |
| 591 | ): | ||
| 592 | self.assertTrue(self.wrapper.check_repo_verify(True)) | ||
| 520 | 593 | ||
| 521 | def test_need_gpg_setup(self): | 594 | def test_need_gpg_setup(self): |
| 522 | """Should pass/fail based on gpg setup.""" | 595 | """Should pass/fail based on gpg setup.""" |
| 523 | with mock.patch.object(self.wrapper, 'NeedSetupGnuPG', return_value=True): | 596 | with mock.patch.object( |
| 524 | with mock.patch.object(self.wrapper, 'SetupGnuPG') as m: | 597 | self.wrapper, "NeedSetupGnuPG", return_value=True |
| 525 | m.return_value = True | 598 | ): |
| 526 | self.assertTrue(self.wrapper.check_repo_verify(True)) | 599 | with mock.patch.object(self.wrapper, "SetupGnuPG") as m: |
| 600 | m.return_value = True | ||
| 601 | self.assertTrue(self.wrapper.check_repo_verify(True)) | ||
| 527 | 602 | ||
| 528 | m.return_value = False | 603 | m.return_value = False |
| 529 | self.assertFalse(self.wrapper.check_repo_verify(True)) | 604 | self.assertFalse(self.wrapper.check_repo_verify(True)) |
| 530 | 605 | ||
| 531 | 606 | ||
| 532 | class CheckRepoRev(GitCheckoutTestCase): | 607 | class CheckRepoRev(GitCheckoutTestCase): |
| 533 | """Check check_repo_rev behavior.""" | 608 | """Check check_repo_rev behavior.""" |
| 534 | 609 | ||
| 535 | def test_verify_works(self): | 610 | def test_verify_works(self): |
| 536 | """Should pass when verification passes.""" | 611 | """Should pass when verification passes.""" |
| 537 | with mock.patch.object(self.wrapper, 'check_repo_verify', return_value=True): | 612 | with mock.patch.object( |
| 538 | with mock.patch.object(self.wrapper, 'verify_rev', return_value='12345'): | 613 | self.wrapper, "check_repo_verify", return_value=True |
| 539 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, 'stable') | 614 | ): |
| 540 | self.assertEqual('refs/heads/stable', rrev) | 615 | with mock.patch.object( |
| 541 | self.assertEqual('12345', lrev) | 616 | self.wrapper, "verify_rev", return_value="12345" |
| 542 | 617 | ): | |
| 543 | def test_verify_fails(self): | 618 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, "stable") |
| 544 | """Should fail when verification fails.""" | 619 | self.assertEqual("refs/heads/stable", rrev) |
| 545 | with mock.patch.object(self.wrapper, 'check_repo_verify', return_value=True): | 620 | self.assertEqual("12345", lrev) |
| 546 | with mock.patch.object(self.wrapper, 'verify_rev', side_effect=Exception): | 621 | |
| 547 | with self.assertRaises(Exception): | 622 | def test_verify_fails(self): |
| 548 | self.wrapper.check_repo_rev(self.GIT_DIR, 'stable') | 623 | """Should fail when verification fails.""" |
| 549 | 624 | with mock.patch.object( | |
| 550 | def test_verify_ignore(self): | 625 | self.wrapper, "check_repo_verify", return_value=True |
| 551 | """Should pass when verification is disabled.""" | 626 | ): |
| 552 | with mock.patch.object(self.wrapper, 'verify_rev', side_effect=Exception): | 627 | with mock.patch.object( |
| 553 | rrev, lrev = self.wrapper.check_repo_rev(self.GIT_DIR, 'stable', repo_verify=False) | 628 | self.wrapper, "verify_rev", side_effect=Exception |
| 554 | self.assertEqual('refs/heads/stable', rrev) | 629 | ): |
| 555 | self.assertEqual(self.REV_LIST[1], lrev) | 630 | with self.assertRaises(Exception): |
| 631 | self.wrapper.check_repo_rev(self.GIT_DIR, "stable") | ||
| 632 | |||
| 633 | def test_verify_ignore(self): | ||
| 634 | """Should pass when verification is disabled.""" | ||
| 635 | with mock.patch.object( | ||
| 636 | self.wrapper, "verify_rev", side_effect=Exception | ||
| 637 | ): | ||
| 638 | rrev, lrev = self.wrapper.check_repo_rev( | ||
| 639 | self.GIT_DIR, "stable", repo_verify=False | ||
| 640 | ) | ||
| 641 | self.assertEqual("refs/heads/stable", rrev) | ||
| 642 | self.assertEqual(self.REV_LIST[1], lrev) | ||
