diff options
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) | ||