blob: 3f03272ad1308b4166ef56af67420652b6991afb [file] [log] [blame]
Mike Frysinger04122b72019-07-31 23:32:58 -04001# Copyright (C) 2019 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Unittests for the manifest_xml.py module."""
16
Mike Frysingerd9254592020-02-19 22:36:26 -050017import os
Raman Tenneti080877e2021-03-09 15:19:06 -080018import platform
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020019import re
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -040020import tempfile
Mike Frysinger04122b72019-07-31 23:32:58 -040021import unittest
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -050022import xml.dom.minidom
Mike Frysinger04122b72019-07-31 23:32:58 -040023
24import error
25import manifest_xml
26
27
Mike Frysingera29424e2021-02-25 21:53:49 -050028# Invalid paths that we don't want in the filesystem.
29INVALID_FS_PATHS = (
Gavin Makea2e3302023-03-11 06:46:20 +000030 "",
31 ".",
32 "..",
33 "../",
34 "./",
35 ".//",
36 "foo/",
37 "./foo",
38 "../foo",
39 "foo/./bar",
40 "foo/../../bar",
41 "/foo",
42 "./../foo",
43 ".git/foo",
Mike Frysingera29424e2021-02-25 21:53:49 -050044 # Check case folding.
Gavin Makea2e3302023-03-11 06:46:20 +000045 ".GIT/foo",
46 "blah/.git/foo",
47 ".repo/foo",
48 ".repoconfig",
Mike Frysingera29424e2021-02-25 21:53:49 -050049 # Block ~ due to 8.3 filenames on Windows filesystems.
Gavin Makea2e3302023-03-11 06:46:20 +000050 "~",
51 "foo~",
52 "blah/foo~",
Mike Frysingera29424e2021-02-25 21:53:49 -050053 # Block Unicode characters that get normalized out by filesystems.
Gavin Makea2e3302023-03-11 06:46:20 +000054 "foo\u200Cbar",
Mike Frysingerf69c7ee2021-04-29 23:15:31 -040055 # Block newlines.
Gavin Makea2e3302023-03-11 06:46:20 +000056 "f\n/bar",
57 "f\r/bar",
Mike Frysingera29424e2021-02-25 21:53:49 -050058)
59
60# Make sure platforms that use path separators (e.g. Windows) are also
61# rejected properly.
Gavin Makea2e3302023-03-11 06:46:20 +000062if os.path.sep != "/":
63 INVALID_FS_PATHS += tuple(
64 x.replace("/", os.path.sep) for x in INVALID_FS_PATHS
65 )
Mike Frysingera29424e2021-02-25 21:53:49 -050066
67
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020068def sort_attributes(manifest):
Gavin Makea2e3302023-03-11 06:46:20 +000069 """Sort the attributes of all elements alphabetically.
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020070
Gavin Makea2e3302023-03-11 06:46:20 +000071 This is needed because different versions of the toxml() function from
72 xml.dom.minidom outputs the attributes of elements in different orders.
73 Before Python 3.8 they were output alphabetically, later versions preserve
74 the order specified by the user.
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020075
Gavin Makea2e3302023-03-11 06:46:20 +000076 Args:
77 manifest: String containing an XML manifest.
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020078
Gavin Makea2e3302023-03-11 06:46:20 +000079 Returns:
80 The XML manifest with the attributes of all elements sorted
81 alphabetically.
82 """
83 new_manifest = ""
84 # This will find every element in the XML manifest, whether they have
85 # attributes or not. This simplifies recreating the manifest below.
86 matches = re.findall(
87 r'(<[/?]?[a-z-]+\s*)((?:\S+?="[^"]+"\s*?)*)(\s*[/?]?>)', manifest
88 )
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
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020093
94
Mike Frysinger37ac3d62021-02-25 04:54:56 -050095class ManifestParseTestCase(unittest.TestCase):
Gavin Makea2e3302023-03-11 06:46:20 +000096 """TestCase for parsing manifests."""
Mike Frysinger37ac3d62021-02-25 04:54:56 -050097
Gavin Makea2e3302023-03-11 06:46:20 +000098 def setUp(self):
99 self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests")
100 self.tempdir = self.tempdirobj.name
101 self.repodir = os.path.join(self.tempdir, ".repo")
102 self.manifest_dir = os.path.join(self.repodir, "manifests")
103 self.manifest_file = os.path.join(
104 self.repodir, manifest_xml.MANIFEST_FILE_NAME
105 )
106 self.local_manifest_dir = os.path.join(
107 self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME
108 )
109 os.mkdir(self.repodir)
110 os.mkdir(self.manifest_dir)
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500111
Gavin Makea2e3302023-03-11 06:46:20 +0000112 # The manifest parsing really wants a git repo currently.
113 gitdir = os.path.join(self.repodir, "manifests.git")
114 os.mkdir(gitdir)
115 with open(os.path.join(gitdir, "config"), "w") as fp:
116 fp.write(
117 """[remote "origin"]
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500118 url = https://localhost:0/manifest
Gavin Makea2e3302023-03-11 06:46:20 +0000119"""
120 )
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500121
Gavin Makea2e3302023-03-11 06:46:20 +0000122 def tearDown(self):
123 self.tempdirobj.cleanup()
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500124
Gavin Makea2e3302023-03-11 06:46:20 +0000125 def getXmlManifest(self, data):
126 """Helper to initialize a manifest for testing."""
127 with open(self.manifest_file, "w", encoding="utf-8") as fp:
128 fp.write(data)
129 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500130
Gavin Makea2e3302023-03-11 06:46:20 +0000131 @staticmethod
132 def encodeXmlAttr(attr):
133 """Encode |attr| using XML escape rules."""
134 return attr.replace("\r", "&#x000d;").replace("\n", "&#x000a;")
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400135
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500136
Mike Frysinger04122b72019-07-31 23:32:58 -0400137class ManifestValidateFilePaths(unittest.TestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000138 """Check _ValidateFilePaths helper.
Mike Frysinger04122b72019-07-31 23:32:58 -0400139
Gavin Makea2e3302023-03-11 06:46:20 +0000140 This doesn't access a real filesystem.
141 """
Mike Frysinger04122b72019-07-31 23:32:58 -0400142
Gavin Makea2e3302023-03-11 06:46:20 +0000143 def check_both(self, *args):
144 manifest_xml.XmlManifest._ValidateFilePaths("copyfile", *args)
145 manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args)
Mike Frysinger04122b72019-07-31 23:32:58 -0400146
Gavin Makea2e3302023-03-11 06:46:20 +0000147 def test_normal_path(self):
148 """Make sure good paths are accepted."""
149 self.check_both("foo", "bar")
150 self.check_both("foo/bar", "bar")
151 self.check_both("foo", "bar/bar")
152 self.check_both("foo/bar", "bar/bar")
Mike Frysinger04122b72019-07-31 23:32:58 -0400153
Gavin Makea2e3302023-03-11 06:46:20 +0000154 def test_symlink_targets(self):
155 """Some extra checks for symlinks."""
Mike Frysinger04122b72019-07-31 23:32:58 -0400156
Gavin Makea2e3302023-03-11 06:46:20 +0000157 def check(*args):
158 manifest_xml.XmlManifest._ValidateFilePaths("linkfile", *args)
Mike Frysinger04122b72019-07-31 23:32:58 -0400159
Gavin Makea2e3302023-03-11 06:46:20 +0000160 # We allow symlinks to end in a slash since we allow them to point to
161 # dirs in general. Technically the slash isn't necessary.
162 check("foo/", "bar")
163 # We allow a single '.' to get a reference to the project itself.
164 check(".", "bar")
165
166 def test_bad_paths(self):
167 """Make sure bad paths (src & dest) are rejected."""
168 for path in INVALID_FS_PATHS:
169 self.assertRaises(
170 error.ManifestInvalidPathError, self.check_both, path, "a"
171 )
172 self.assertRaises(
173 error.ManifestInvalidPathError, self.check_both, "a", path
174 )
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500175
176
177class ValueTests(unittest.TestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000178 """Check utility parsing code."""
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500179
Gavin Makea2e3302023-03-11 06:46:20 +0000180 def _get_node(self, text):
181 return xml.dom.minidom.parseString(text).firstChild
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500182
Gavin Makea2e3302023-03-11 06:46:20 +0000183 def test_bool_default(self):
184 """Check XmlBool default handling."""
185 node = self._get_node("<node/>")
186 self.assertIsNone(manifest_xml.XmlBool(node, "a"))
187 self.assertIsNone(manifest_xml.XmlBool(node, "a", None))
188 self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500189
Gavin Makea2e3302023-03-11 06:46:20 +0000190 node = self._get_node('<node a=""/>')
191 self.assertIsNone(manifest_xml.XmlBool(node, "a"))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500192
Gavin Makea2e3302023-03-11 06:46:20 +0000193 def test_bool_invalid(self):
194 """Check XmlBool invalid handling."""
195 node = self._get_node('<node a="moo"/>')
196 self.assertEqual(123, manifest_xml.XmlBool(node, "a", 123))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500197
Gavin Makea2e3302023-03-11 06:46:20 +0000198 def test_bool_true(self):
199 """Check XmlBool true values."""
200 for value in ("yes", "true", "1"):
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400201 node = self._get_node(f'<node a="{value}"/>')
Gavin Makea2e3302023-03-11 06:46:20 +0000202 self.assertTrue(manifest_xml.XmlBool(node, "a"))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500203
Gavin Makea2e3302023-03-11 06:46:20 +0000204 def test_bool_false(self):
205 """Check XmlBool false values."""
206 for value in ("no", "false", "0"):
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400207 node = self._get_node(f'<node a="{value}"/>')
Gavin Makea2e3302023-03-11 06:46:20 +0000208 self.assertFalse(manifest_xml.XmlBool(node, "a"))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500209
Gavin Makea2e3302023-03-11 06:46:20 +0000210 def test_int_default(self):
211 """Check XmlInt default handling."""
212 node = self._get_node("<node/>")
213 self.assertIsNone(manifest_xml.XmlInt(node, "a"))
214 self.assertIsNone(manifest_xml.XmlInt(node, "a", None))
215 self.assertEqual(123, manifest_xml.XmlInt(node, "a", 123))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500216
Gavin Makea2e3302023-03-11 06:46:20 +0000217 node = self._get_node('<node a=""/>')
218 self.assertIsNone(manifest_xml.XmlInt(node, "a"))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500219
Gavin Makea2e3302023-03-11 06:46:20 +0000220 def test_int_good(self):
221 """Check XmlInt numeric handling."""
222 for value in (-1, 0, 1, 50000):
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400223 node = self._get_node(f'<node a="{value}"/>')
Gavin Makea2e3302023-03-11 06:46:20 +0000224 self.assertEqual(value, manifest_xml.XmlInt(node, "a"))
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500225
Gavin Makea2e3302023-03-11 06:46:20 +0000226 def test_int_invalid(self):
227 """Check XmlInt invalid handling."""
228 with self.assertRaises(error.ManifestParseError):
229 node = self._get_node('<node a="xx"/>')
230 manifest_xml.XmlInt(node, "a")
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400231
232
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500233class XmlManifestTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000234 """Check manifest processing."""
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400235
Gavin Makea2e3302023-03-11 06:46:20 +0000236 def test_empty(self):
237 """Parse an 'empty' manifest file."""
238 manifest = self.getXmlManifest(
239 '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>"
240 )
241 self.assertEqual(manifest.remotes, {})
242 self.assertEqual(manifest.projects, [])
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400243
Gavin Makea2e3302023-03-11 06:46:20 +0000244 def test_link(self):
245 """Verify Link handling with new names."""
246 manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file)
247 with open(os.path.join(self.manifest_dir, "foo.xml"), "w") as fp:
248 fp.write("<manifest></manifest>")
249 manifest.Link("foo.xml")
250 with open(self.manifest_file) as fp:
251 self.assertIn('<include name="foo.xml" />', fp.read())
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400252
Gavin Makea2e3302023-03-11 06:46:20 +0000253 def test_toxml_empty(self):
254 """Verify the ToXml() helper."""
255 manifest = self.getXmlManifest(
256 '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>"
257 )
258 self.assertEqual(
259 manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>'
260 )
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400261
Gavin Makea2e3302023-03-11 06:46:20 +0000262 def test_todict_empty(self):
263 """Verify the ToDict() helper."""
264 manifest = self.getXmlManifest(
265 '<?xml version="1.0" encoding="UTF-8"?>' "<manifest></manifest>"
266 )
267 self.assertEqual(manifest.ToDict(), {})
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400268
Gavin Makea2e3302023-03-11 06:46:20 +0000269 def test_toxml_omit_local(self):
270 """Does not include local_manifests projects when omit_local=True."""
271 manifest = self.getXmlManifest(
272 '<?xml version="1.0" encoding="UTF-8"?><manifest>'
273 '<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
274 '<project name="p" groups="local::me"/>'
275 '<project name="q"/>'
276 '<project name="r" groups="keep"/>'
277 "</manifest>"
278 )
279 self.assertEqual(
280 sort_attributes(manifest.ToXml(omit_local=True).toxml()),
281 '<?xml version="1.0" ?><manifest>'
282 '<remote fetch=".." name="a"/><default remote="a" revision="r"/>'
283 '<project name="q"/><project groups="keep" name="r"/></manifest>',
284 )
LaMont Jonesa8cf5752022-07-15 20:31:33 +0000285
Gavin Makea2e3302023-03-11 06:46:20 +0000286 def test_toxml_with_local(self):
287 """Does include local_manifests projects when omit_local=False."""
288 manifest = self.getXmlManifest(
289 '<?xml version="1.0" encoding="UTF-8"?><manifest>'
290 '<remote name="a" fetch=".."/><default remote="a" revision="r"/>'
291 '<project name="p" groups="local::me"/>'
292 '<project name="q"/>'
293 '<project name="r" groups="keep"/>'
294 "</manifest>"
295 )
296 self.assertEqual(
297 sort_attributes(manifest.ToXml(omit_local=False).toxml()),
298 '<?xml version="1.0" ?><manifest>'
299 '<remote fetch=".." name="a"/><default remote="a" revision="r"/>'
300 '<project groups="local::me" name="p"/>'
301 '<project name="q"/><project groups="keep" name="r"/></manifest>',
302 )
LaMont Jonesa8cf5752022-07-15 20:31:33 +0000303
Gavin Makea2e3302023-03-11 06:46:20 +0000304 def test_repo_hooks(self):
305 """Check repo-hooks settings."""
306 manifest = self.getXmlManifest(
307 """
Mike Frysinger51e39d52020-12-04 05:32:06 -0500308<manifest>
309 <remote name="test-remote" fetch="http://localhost" />
310 <default remote="test-remote" revision="refs/heads/main" />
311 <project name="repohooks" path="src/repohooks"/>
312 <repo-hooks in-project="repohooks" enabled-list="a, b"/>
313</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000314"""
315 )
316 self.assertEqual(manifest.repo_hooks_project.name, "repohooks")
317 self.assertEqual(
318 manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"]
319 )
Mike Frysinger51e39d52020-12-04 05:32:06 -0500320
Gavin Makea2e3302023-03-11 06:46:20 +0000321 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 """
Jack Neusa84f43a2021-09-21 22:23:55 +0000325<manifest>
326 <remote name="test-remote" fetch="http://localhost" />
327 <default remote="test-remote" revision="refs/heads/main" />
328 <repo-hooks in-project="repohooks" enabled-list="a, b"/>
329 <project name="repohooks" path="src/repohooks"/>
330</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000331"""
332 )
333 self.assertEqual(manifest.repo_hooks_project.name, "repohooks")
334 self.assertEqual(
335 manifest.repo_hooks_project.enabled_repo_hooks, ["a", "b"]
336 )
Jack Neusa84f43a2021-09-21 22:23:55 +0000337
Gavin Makea2e3302023-03-11 06:46:20 +0000338 def test_unknown_tags(self):
339 """Check superproject settings."""
340 manifest = self.getXmlManifest(
341 """
Raman Tenneti48b2d102021-01-11 12:18:47 -0800342<manifest>
343 <remote name="test-remote" fetch="http://localhost" />
344 <default remote="test-remote" revision="refs/heads/main" />
345 <superproject name="superproject"/>
346 <iankaz value="unknown (possible) future tags are ignored"/>
347 <x-custom-tag>X tags are always ignored</x-custom-tag>
348</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000349"""
350 )
351 self.assertEqual(manifest.superproject.name, "superproject")
352 self.assertEqual(manifest.superproject.remote.name, "test-remote")
353 self.assertEqual(
354 sort_attributes(manifest.ToXml().toxml()),
355 '<?xml version="1.0" ?><manifest>'
356 '<remote fetch="http://localhost" name="test-remote"/>'
357 '<default remote="test-remote" revision="refs/heads/main"/>'
358 '<superproject name="superproject"/>'
359 "</manifest>",
360 )
Raman Tenneti48b2d102021-01-11 12:18:47 -0800361
Gavin Makea2e3302023-03-11 06:46:20 +0000362 def test_remote_annotations(self):
363 """Check remote settings."""
364 manifest = self.getXmlManifest(
365 """
Jack Neus6ea0cae2021-07-20 20:52:33 +0000366<manifest>
367 <remote name="test-remote" fetch="http://localhost">
368 <annotation name="foo" value="bar"/>
369 </remote>
370</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000371"""
372 )
373 self.assertEqual(
374 manifest.remotes["test-remote"].annotations[0].name, "foo"
375 )
376 self.assertEqual(
377 manifest.remotes["test-remote"].annotations[0].value, "bar"
378 )
379 self.assertEqual(
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 )
Jack Neus6ea0cae2021-07-20 20:52:33 +0000387
Chris Allen7393f6b2023-10-20 16:35:39 +0100388 def test_parse_with_xml_doctype(self):
389 """Check correct manifest parse with DOCTYPE node present."""
390 manifest = self.getXmlManifest(
391 """<?xml version="1.0" encoding="UTF-8"?>
392<!DOCTYPE manifest []>
393<manifest>
394 <remote name="test-remote" fetch="http://localhost" />
395 <default remote="test-remote" revision="refs/heads/main" />
396 <project name="test-project" path="src/test-project"/>
397</manifest>
398"""
399 )
400 self.assertEqual(len(manifest.projects), 1)
401 self.assertEqual(manifest.projects[0].name, "test-project")
402
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200403
Mike Frysingera29424e2021-02-25 21:53:49 -0500404class IncludeElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000405 """Tests for <include>."""
Raman Tennetib5c5a5e2021-02-06 09:44:15 -0800406
Shuchuan Zeng3e3340d2023-04-18 10:36:50 +0800407 def test_revision_default(self):
408 """Check handling of revision attribute."""
409 root_m = os.path.join(self.manifest_dir, "root.xml")
410 with open(root_m, "w") as fp:
411 fp.write(
412 """
413<manifest>
414 <remote name="test-remote" fetch="http://localhost" />
415 <default remote="test-remote" revision="refs/heads/main" />
416 <include name="stable.xml" revision="stable-branch" />
417 <project name="root-name1" path="root-path1" />
418 <project name="root-name2" path="root-path2" />
419</manifest>
420"""
421 )
422 with open(os.path.join(self.manifest_dir, "stable.xml"), "w") as fp:
423 fp.write(
424 """
425<manifest>
426 <project name="stable-name1" path="stable-path1" />
427 <project name="stable-name2" path="stable-path2" revision="stable-branch2" />
428</manifest>
429"""
430 )
431 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
432 for proj in include_m.projects:
433 if proj.name == "root-name1":
434 # Check include revision not set on root level proj.
435 self.assertNotEqual("stable-branch", proj.revisionExpr)
436 if proj.name == "root-name2":
437 # Check root proj revision not removed.
438 self.assertEqual("refs/heads/main", proj.revisionExpr)
439 if proj.name == "stable-name1":
440 # Check stable proj has inherited revision include node.
441 self.assertEqual("stable-branch", proj.revisionExpr)
442 if proj.name == "stable-name2":
443 # Check stable proj revision can override include node.
444 self.assertEqual("stable-branch2", proj.revisionExpr)
445
Gavin Makea2e3302023-03-11 06:46:20 +0000446 def test_group_levels(self):
447 root_m = os.path.join(self.manifest_dir, "root.xml")
448 with open(root_m, "w") as fp:
449 fp.write(
450 """
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200451<manifest>
452 <remote name="test-remote" fetch="http://localhost" />
453 <default remote="test-remote" revision="refs/heads/main" />
454 <include name="level1.xml" groups="level1-group" />
455 <project name="root-name1" path="root-path1" />
456 <project name="root-name2" path="root-path2" groups="r2g1,r2g2" />
457</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000458"""
459 )
460 with open(os.path.join(self.manifest_dir, "level1.xml"), "w") as fp:
461 fp.write(
462 """
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200463<manifest>
464 <include name="level2.xml" groups="level2-group" />
465 <project name="level1-name1" path="level1-path1" />
466</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000467"""
468 )
469 with open(os.path.join(self.manifest_dir, "level2.xml"), "w") as fp:
470 fp.write(
471 """
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200472<manifest>
473 <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" />
474</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000475"""
476 )
477 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
478 for proj in include_m.projects:
479 if proj.name == "root-name1":
480 # Check include group not set on root level proj.
481 self.assertNotIn("level1-group", proj.groups)
482 if proj.name == "root-name2":
483 # Check root proj group not removed.
484 self.assertIn("r2g1", proj.groups)
485 if proj.name == "level1-name1":
486 # Check level1 proj has inherited group level 1.
487 self.assertIn("level1-group", proj.groups)
488 if proj.name == "level2-name1":
489 # Check level2 proj has inherited group levels 1 and 2.
490 self.assertIn("level1-group", proj.groups)
491 self.assertIn("level2-group", proj.groups)
492 # Check level2 proj group not removed.
493 self.assertIn("l2g1", proj.groups)
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500494
Gavin Makea2e3302023-03-11 06:46:20 +0000495 def test_allow_bad_name_from_user(self):
496 """Check handling of bad name attribute from the user's input."""
497
498 def parse(name):
499 name = self.encodeXmlAttr(name)
500 manifest = self.getXmlManifest(
501 f"""
Mike Frysingera29424e2021-02-25 21:53:49 -0500502<manifest>
503 <remote name="default-remote" fetch="http://localhost" />
504 <default remote="default-remote" revision="refs/heads/main" />
505 <include name="{name}" />
506</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000507"""
508 )
509 # Force the manifest to be parsed.
510 manifest.ToXml()
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500511
Gavin Makea2e3302023-03-11 06:46:20 +0000512 # Setup target of the include.
513 target = os.path.join(self.tempdir, "target.xml")
514 with open(target, "w") as fp:
515 fp.write("<manifest></manifest>")
Mike Frysinger54133972021-03-01 21:38:08 -0500516
Gavin Makea2e3302023-03-11 06:46:20 +0000517 # Include with absolute path.
518 parse(os.path.abspath(target))
Mike Frysinger54133972021-03-01 21:38:08 -0500519
Gavin Makea2e3302023-03-11 06:46:20 +0000520 # Include with relative path.
521 parse(os.path.relpath(target, self.manifest_dir))
Mike Frysinger54133972021-03-01 21:38:08 -0500522
Gavin Makea2e3302023-03-11 06:46:20 +0000523 def test_bad_name_checks(self):
524 """Check handling of bad name attribute."""
Mike Frysinger54133972021-03-01 21:38:08 -0500525
Gavin Makea2e3302023-03-11 06:46:20 +0000526 def parse(name):
527 name = self.encodeXmlAttr(name)
528 # Setup target of the include.
529 with open(
530 os.path.join(self.manifest_dir, "target.xml"),
531 "w",
532 encoding="utf-8",
533 ) as fp:
534 fp.write(f'<manifest><include name="{name}"/></manifest>')
535
536 manifest = self.getXmlManifest(
537 """
Mike Frysinger54133972021-03-01 21:38:08 -0500538<manifest>
539 <remote name="default-remote" fetch="http://localhost" />
540 <default remote="default-remote" revision="refs/heads/main" />
541 <include name="target.xml" />
542</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000543"""
544 )
545 # Force the manifest to be parsed.
546 manifest.ToXml()
Mike Frysinger54133972021-03-01 21:38:08 -0500547
Gavin Makea2e3302023-03-11 06:46:20 +0000548 # Handle empty name explicitly because a different codepath rejects it.
549 with self.assertRaises(error.ManifestParseError):
550 parse("")
Mike Frysingera29424e2021-02-25 21:53:49 -0500551
Gavin Makea2e3302023-03-11 06:46:20 +0000552 for path in INVALID_FS_PATHS:
553 if not path:
554 continue
Mike Frysingera29424e2021-02-25 21:53:49 -0500555
Gavin Makea2e3302023-03-11 06:46:20 +0000556 with self.assertRaises(error.ManifestInvalidPathError):
557 parse(path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500558
559
560class ProjectElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000561 """Tests for <project>."""
Mike Frysingera29424e2021-02-25 21:53:49 -0500562
Gavin Makea2e3302023-03-11 06:46:20 +0000563 def test_group(self):
564 """Check project group settings."""
565 manifest = self.getXmlManifest(
566 """
Mike Frysingera29424e2021-02-25 21:53:49 -0500567<manifest>
568 <remote name="test-remote" fetch="http://localhost" />
569 <default remote="test-remote" revision="refs/heads/main" />
570 <project name="test-name" path="test-path"/>
571 <project name="extras" path="path" groups="g1,g2,g1"/>
572</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000573"""
574 )
575 self.assertEqual(len(manifest.projects), 2)
576 # Ordering isn't guaranteed.
577 result = {
578 manifest.projects[0].name: manifest.projects[0].groups,
579 manifest.projects[1].name: manifest.projects[1].groups,
580 }
581 self.assertCountEqual(
582 result["test-name"], ["name:test-name", "all", "path:test-path"]
583 )
584 self.assertCountEqual(
585 result["extras"],
586 ["g1", "g2", "g1", "name:extras", "all", "path:path"],
587 )
588 groupstr = "default,platform-" + platform.system().lower()
589 self.assertEqual(groupstr, manifest.GetGroupsStr())
590 groupstr = "g1,g2,g1"
591 manifest.manifestProject.config.SetString("manifest.groups", groupstr)
592 self.assertEqual(groupstr, manifest.GetGroupsStr())
Mike Frysingera29424e2021-02-25 21:53:49 -0500593
Gavin Makea2e3302023-03-11 06:46:20 +0000594 def test_set_revision_id(self):
595 """Check setting of project's revisionId."""
596 manifest = self.getXmlManifest(
597 """
Mike Frysingera29424e2021-02-25 21:53:49 -0500598<manifest>
599 <remote name="default-remote" fetch="http://localhost" />
600 <default remote="default-remote" revision="refs/heads/main" />
601 <project name="test-name"/>
602</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000603"""
604 )
605 self.assertEqual(len(manifest.projects), 1)
606 project = manifest.projects[0]
607 project.SetRevisionId("ABCDEF")
608 self.assertEqual(
609 sort_attributes(manifest.ToXml().toxml()),
610 '<?xml version="1.0" ?><manifest>'
611 '<remote fetch="http://localhost" name="default-remote"/>'
612 '<default remote="default-remote" revision="refs/heads/main"/>'
613 '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>' # noqa: E501
614 "</manifest>",
615 )
Mike Frysingera29424e2021-02-25 21:53:49 -0500616
Gavin Makea2e3302023-03-11 06:46:20 +0000617 def test_trailing_slash(self):
618 """Check handling of trailing slashes in attributes."""
619
620 def parse(name, path):
621 name = self.encodeXmlAttr(name)
622 path = self.encodeXmlAttr(path)
623 return self.getXmlManifest(
624 f"""
Mike Frysingera29424e2021-02-25 21:53:49 -0500625<manifest>
626 <remote name="default-remote" fetch="http://localhost" />
627 <default remote="default-remote" revision="refs/heads/main" />
628 <project name="{name}" path="{path}" />
629</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000630"""
631 )
Mike Frysingera29424e2021-02-25 21:53:49 -0500632
Gavin Makea2e3302023-03-11 06:46:20 +0000633 manifest = parse("a/path/", "foo")
634 self.assertEqual(
635 os.path.normpath(manifest.projects[0].gitdir),
636 os.path.join(self.tempdir, ".repo", "projects", "foo.git"),
637 )
638 self.assertEqual(
639 os.path.normpath(manifest.projects[0].objdir),
640 os.path.join(
641 self.tempdir, ".repo", "project-objects", "a", "path.git"
642 ),
643 )
Mike Frysingera29424e2021-02-25 21:53:49 -0500644
Gavin Makea2e3302023-03-11 06:46:20 +0000645 manifest = parse("a/path", "foo/")
646 self.assertEqual(
647 os.path.normpath(manifest.projects[0].gitdir),
648 os.path.join(self.tempdir, ".repo", "projects", "foo.git"),
649 )
650 self.assertEqual(
651 os.path.normpath(manifest.projects[0].objdir),
652 os.path.join(
653 self.tempdir, ".repo", "project-objects", "a", "path.git"
654 ),
655 )
Mike Frysingera29424e2021-02-25 21:53:49 -0500656
Gavin Makea2e3302023-03-11 06:46:20 +0000657 manifest = parse("a/path", "foo//////")
658 self.assertEqual(
659 os.path.normpath(manifest.projects[0].gitdir),
660 os.path.join(self.tempdir, ".repo", "projects", "foo.git"),
661 )
662 self.assertEqual(
663 os.path.normpath(manifest.projects[0].objdir),
664 os.path.join(
665 self.tempdir, ".repo", "project-objects", "a", "path.git"
666 ),
667 )
Mike Frysinger0458faa2021-03-10 23:35:44 -0500668
Gavin Makea2e3302023-03-11 06:46:20 +0000669 def test_toplevel_path(self):
670 """Check handling of path=. specially."""
671
672 def parse(name, path):
673 name = self.encodeXmlAttr(name)
674 path = self.encodeXmlAttr(path)
675 return self.getXmlManifest(
676 f"""
Mike Frysinger0458faa2021-03-10 23:35:44 -0500677<manifest>
678 <remote name="default-remote" fetch="http://localhost" />
679 <default remote="default-remote" revision="refs/heads/main" />
680 <project name="{name}" path="{path}" />
681</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000682"""
683 )
Mike Frysinger0458faa2021-03-10 23:35:44 -0500684
Gavin Makea2e3302023-03-11 06:46:20 +0000685 for path in (".", "./", ".//", ".///"):
686 manifest = parse("server/path", path)
687 self.assertEqual(
688 os.path.normpath(manifest.projects[0].gitdir),
689 os.path.join(self.tempdir, ".repo", "projects", "..git"),
690 )
Mike Frysinger0458faa2021-03-10 23:35:44 -0500691
Gavin Makea2e3302023-03-11 06:46:20 +0000692 def test_bad_path_name_checks(self):
693 """Check handling of bad path & name attributes."""
694
695 def parse(name, path):
696 name = self.encodeXmlAttr(name)
697 path = self.encodeXmlAttr(path)
698 manifest = self.getXmlManifest(
699 f"""
Mike Frysingera29424e2021-02-25 21:53:49 -0500700<manifest>
701 <remote name="default-remote" fetch="http://localhost" />
702 <default remote="default-remote" revision="refs/heads/main" />
703 <project name="{name}" path="{path}" />
704</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000705"""
706 )
707 # Force the manifest to be parsed.
708 manifest.ToXml()
Mike Frysingera29424e2021-02-25 21:53:49 -0500709
Gavin Makea2e3302023-03-11 06:46:20 +0000710 # Verify the parser is valid by default to avoid buggy tests below.
711 parse("ok", "ok")
Mike Frysingera29424e2021-02-25 21:53:49 -0500712
Gavin Makea2e3302023-03-11 06:46:20 +0000713 # Handle empty name explicitly because a different codepath rejects it.
714 # Empty path is OK because it defaults to the name field.
715 with self.assertRaises(error.ManifestParseError):
716 parse("", "ok")
Mike Frysingera29424e2021-02-25 21:53:49 -0500717
Gavin Makea2e3302023-03-11 06:46:20 +0000718 for path in INVALID_FS_PATHS:
719 if not path or path.endswith("/") or path.endswith(os.path.sep):
720 continue
Mike Frysingera29424e2021-02-25 21:53:49 -0500721
Gavin Makea2e3302023-03-11 06:46:20 +0000722 with self.assertRaises(error.ManifestInvalidPathError):
723 parse(path, "ok")
Mike Frysinger0458faa2021-03-10 23:35:44 -0500724
Gavin Makea2e3302023-03-11 06:46:20 +0000725 # We have a dedicated test for path=".".
726 if path not in {"."}:
727 with self.assertRaises(error.ManifestInvalidPathError):
728 parse("ok", path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500729
730
731class SuperProjectElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000732 """Tests for <superproject>."""
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500733
Gavin Makea2e3302023-03-11 06:46:20 +0000734 def test_superproject(self):
735 """Check superproject settings."""
736 manifest = self.getXmlManifest(
737 """
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500738<manifest>
739 <remote name="test-remote" fetch="http://localhost" />
740 <default remote="test-remote" revision="refs/heads/main" />
741 <superproject name="superproject"/>
742</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000743"""
744 )
745 self.assertEqual(manifest.superproject.name, "superproject")
746 self.assertEqual(manifest.superproject.remote.name, "test-remote")
747 self.assertEqual(
748 manifest.superproject.remote.url, "http://localhost/superproject"
749 )
750 self.assertEqual(manifest.superproject.revision, "refs/heads/main")
751 self.assertEqual(
752 sort_attributes(manifest.ToXml().toxml()),
753 '<?xml version="1.0" ?><manifest>'
754 '<remote fetch="http://localhost" name="test-remote"/>'
755 '<default remote="test-remote" revision="refs/heads/main"/>'
756 '<superproject name="superproject"/>'
757 "</manifest>",
758 )
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500759
Gavin Makea2e3302023-03-11 06:46:20 +0000760 def test_superproject_revision(self):
761 """Check superproject settings with a different revision attribute"""
762 self.maxDiff = None
763 manifest = self.getXmlManifest(
764 """
Xin Lie0b16a22021-09-26 23:20:32 -0700765<manifest>
766 <remote name="test-remote" fetch="http://localhost" />
767 <default remote="test-remote" revision="refs/heads/main" />
768 <superproject name="superproject" revision="refs/heads/stable" />
769</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000770"""
771 )
772 self.assertEqual(manifest.superproject.name, "superproject")
773 self.assertEqual(manifest.superproject.remote.name, "test-remote")
774 self.assertEqual(
775 manifest.superproject.remote.url, "http://localhost/superproject"
776 )
777 self.assertEqual(manifest.superproject.revision, "refs/heads/stable")
778 self.assertEqual(
779 sort_attributes(manifest.ToXml().toxml()),
780 '<?xml version="1.0" ?><manifest>'
781 '<remote fetch="http://localhost" name="test-remote"/>'
782 '<default remote="test-remote" revision="refs/heads/main"/>'
783 '<superproject name="superproject" revision="refs/heads/stable"/>'
784 "</manifest>",
785 )
Xin Lie0b16a22021-09-26 23:20:32 -0700786
Gavin Makea2e3302023-03-11 06:46:20 +0000787 def test_superproject_revision_default_negative(self):
788 """Check superproject settings with a same revision attribute"""
789 self.maxDiff = None
790 manifest = self.getXmlManifest(
791 """
Xin Lie0b16a22021-09-26 23:20:32 -0700792<manifest>
793 <remote name="test-remote" fetch="http://localhost" />
794 <default remote="test-remote" revision="refs/heads/stable" />
795 <superproject name="superproject" revision="refs/heads/stable" />
796</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000797"""
798 )
799 self.assertEqual(manifest.superproject.name, "superproject")
800 self.assertEqual(manifest.superproject.remote.name, "test-remote")
801 self.assertEqual(
802 manifest.superproject.remote.url, "http://localhost/superproject"
803 )
804 self.assertEqual(manifest.superproject.revision, "refs/heads/stable")
805 self.assertEqual(
806 sort_attributes(manifest.ToXml().toxml()),
807 '<?xml version="1.0" ?><manifest>'
808 '<remote fetch="http://localhost" name="test-remote"/>'
809 '<default remote="test-remote" revision="refs/heads/stable"/>'
810 '<superproject name="superproject"/>'
811 "</manifest>",
812 )
Xin Lie0b16a22021-09-26 23:20:32 -0700813
Gavin Makea2e3302023-03-11 06:46:20 +0000814 def test_superproject_revision_remote(self):
815 """Check superproject settings with a same revision attribute"""
816 self.maxDiff = None
817 manifest = self.getXmlManifest(
818 """
Xin Lie0b16a22021-09-26 23:20:32 -0700819<manifest>
820 <remote name="test-remote" fetch="http://localhost" revision="refs/heads/main" />
821 <default remote="test-remote" />
822 <superproject name="superproject" revision="refs/heads/stable" />
823</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000824""" # noqa: E501
825 )
826 self.assertEqual(manifest.superproject.name, "superproject")
827 self.assertEqual(manifest.superproject.remote.name, "test-remote")
828 self.assertEqual(
829 manifest.superproject.remote.url, "http://localhost/superproject"
830 )
831 self.assertEqual(manifest.superproject.revision, "refs/heads/stable")
832 self.assertEqual(
833 sort_attributes(manifest.ToXml().toxml()),
834 '<?xml version="1.0" ?><manifest>'
835 '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>' # noqa: E501
836 '<default remote="test-remote"/>'
837 '<superproject name="superproject" revision="refs/heads/stable"/>'
838 "</manifest>",
839 )
Xin Lie0b16a22021-09-26 23:20:32 -0700840
Gavin Makea2e3302023-03-11 06:46:20 +0000841 def test_remote(self):
842 """Check superproject settings with a remote."""
843 manifest = self.getXmlManifest(
844 """
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500845<manifest>
846 <remote name="default-remote" fetch="http://localhost" />
847 <remote name="superproject-remote" fetch="http://localhost" />
848 <default remote="default-remote" revision="refs/heads/main" />
849 <superproject name="platform/superproject" remote="superproject-remote"/>
850</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000851"""
852 )
853 self.assertEqual(manifest.superproject.name, "platform/superproject")
854 self.assertEqual(
855 manifest.superproject.remote.name, "superproject-remote"
856 )
857 self.assertEqual(
858 manifest.superproject.remote.url,
859 "http://localhost/platform/superproject",
860 )
861 self.assertEqual(manifest.superproject.revision, "refs/heads/main")
862 self.assertEqual(
863 sort_attributes(manifest.ToXml().toxml()),
864 '<?xml version="1.0" ?><manifest>'
865 '<remote fetch="http://localhost" name="default-remote"/>'
866 '<remote fetch="http://localhost" name="superproject-remote"/>'
867 '<default remote="default-remote" revision="refs/heads/main"/>'
868 '<superproject name="platform/superproject" remote="superproject-remote"/>' # noqa: E501
869 "</manifest>",
870 )
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500871
Gavin Makea2e3302023-03-11 06:46:20 +0000872 def test_defalut_remote(self):
873 """Check superproject settings with a default remote."""
874 manifest = self.getXmlManifest(
875 """
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500876<manifest>
877 <remote name="default-remote" fetch="http://localhost" />
878 <default remote="default-remote" revision="refs/heads/main" />
879 <superproject name="superproject" remote="default-remote"/>
880</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000881"""
882 )
883 self.assertEqual(manifest.superproject.name, "superproject")
884 self.assertEqual(manifest.superproject.remote.name, "default-remote")
885 self.assertEqual(manifest.superproject.revision, "refs/heads/main")
886 self.assertEqual(
887 sort_attributes(manifest.ToXml().toxml()),
888 '<?xml version="1.0" ?><manifest>'
889 '<remote fetch="http://localhost" name="default-remote"/>'
890 '<default remote="default-remote" revision="refs/heads/main"/>'
891 '<superproject name="superproject"/>'
892 "</manifest>",
893 )
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700894
895
896class ContactinfoElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000897 """Tests for <contactinfo>."""
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700898
Gavin Makea2e3302023-03-11 06:46:20 +0000899 def test_contactinfo(self):
900 """Check contactinfo settings."""
901 bugurl = "http://localhost/contactinfo"
902 manifest = self.getXmlManifest(
903 f"""
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700904<manifest>
905 <contactinfo bugurl="{bugurl}"/>
906</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000907"""
908 )
909 self.assertEqual(manifest.contactinfo.bugurl, bugurl)
910 self.assertEqual(
911 manifest.ToXml().toxml(),
912 '<?xml version="1.0" ?><manifest>'
913 f'<contactinfo bugurl="{bugurl}"/>'
914 "</manifest>",
915 )
Jack Neus5ba21202021-06-09 15:21:25 +0000916
917
918class DefaultElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000919 """Tests for <default>."""
Jack Neus5ba21202021-06-09 15:21:25 +0000920
Gavin Makea2e3302023-03-11 06:46:20 +0000921 def test_default(self):
922 """Check default settings."""
923 a = manifest_xml._Default()
924 a.revisionExpr = "foo"
925 a.remote = manifest_xml._XmlRemote(name="remote")
926 b = manifest_xml._Default()
927 b.revisionExpr = "bar"
928 self.assertEqual(a, a)
929 self.assertNotEqual(a, b)
930 self.assertNotEqual(b, a.remote)
931 self.assertNotEqual(a, 123)
932 self.assertNotEqual(a, None)
Jack Neus5ba21202021-06-09 15:21:25 +0000933
934
935class RemoteElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000936 """Tests for <remote>."""
Jack Neus5ba21202021-06-09 15:21:25 +0000937
Gavin Makea2e3302023-03-11 06:46:20 +0000938 def test_remote(self):
939 """Check remote settings."""
940 a = manifest_xml._XmlRemote(name="foo")
941 a.AddAnnotation("key1", "value1", "true")
942 b = manifest_xml._XmlRemote(name="foo")
943 b.AddAnnotation("key2", "value1", "true")
944 c = manifest_xml._XmlRemote(name="foo")
945 c.AddAnnotation("key1", "value2", "true")
946 d = manifest_xml._XmlRemote(name="foo")
947 d.AddAnnotation("key1", "value1", "false")
948 self.assertEqual(a, a)
949 self.assertNotEqual(a, b)
950 self.assertNotEqual(a, c)
951 self.assertNotEqual(a, d)
952 self.assertNotEqual(a, manifest_xml._Default())
953 self.assertNotEqual(a, 123)
954 self.assertNotEqual(a, None)
Michael Kelly06da9982021-06-30 01:58:28 -0700955
956
957class RemoveProjectElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +0000958 """Tests for <remove-project>."""
Michael Kelly06da9982021-06-30 01:58:28 -0700959
Gavin Makea2e3302023-03-11 06:46:20 +0000960 def test_remove_one_project(self):
961 manifest = self.getXmlManifest(
962 """
Michael Kelly06da9982021-06-30 01:58:28 -0700963<manifest>
964 <remote name="default-remote" fetch="http://localhost" />
965 <default remote="default-remote" revision="refs/heads/main" />
966 <project name="myproject" />
967 <remove-project name="myproject" />
968</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000969"""
970 )
971 self.assertEqual(manifest.projects, [])
Michael Kelly06da9982021-06-30 01:58:28 -0700972
Gavin Makea2e3302023-03-11 06:46:20 +0000973 def test_remove_one_project_one_remains(self):
974 manifest = self.getXmlManifest(
975 """
Michael Kelly06da9982021-06-30 01:58:28 -0700976<manifest>
977 <remote name="default-remote" fetch="http://localhost" />
978 <default remote="default-remote" revision="refs/heads/main" />
979 <project name="myproject" />
980 <project name="yourproject" />
981 <remove-project name="myproject" />
982</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000983"""
984 )
Michael Kelly06da9982021-06-30 01:58:28 -0700985
Gavin Makea2e3302023-03-11 06:46:20 +0000986 self.assertEqual(len(manifest.projects), 1)
987 self.assertEqual(manifest.projects[0].name, "yourproject")
Michael Kelly06da9982021-06-30 01:58:28 -0700988
Gavin Makea2e3302023-03-11 06:46:20 +0000989 def test_remove_one_project_doesnt_exist(self):
990 with self.assertRaises(manifest_xml.ManifestParseError):
991 manifest = self.getXmlManifest(
992 """
Michael Kelly06da9982021-06-30 01:58:28 -0700993<manifest>
994 <remote name="default-remote" fetch="http://localhost" />
995 <default remote="default-remote" revision="refs/heads/main" />
996 <remove-project name="myproject" />
997</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +0000998"""
999 )
1000 manifest.projects
Michael Kelly06da9982021-06-30 01:58:28 -07001001
Gavin Makea2e3302023-03-11 06:46:20 +00001002 def test_remove_one_optional_project_doesnt_exist(self):
1003 manifest = self.getXmlManifest(
1004 """
Michael Kelly06da9982021-06-30 01:58:28 -07001005<manifest>
1006 <remote name="default-remote" fetch="http://localhost" />
1007 <default remote="default-remote" revision="refs/heads/main" />
1008 <remove-project name="myproject" optional="true" />
1009</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001010"""
1011 )
1012 self.assertEqual(manifest.projects, [])
Michael Kelly37c21c22020-06-13 02:10:40 -07001013
Fredrik de Grootbe71c2f2023-05-31 16:56:34 +02001014 def test_remove_using_path_attrib(self):
1015 manifest = self.getXmlManifest(
1016 """
1017<manifest>
1018 <remote name="default-remote" fetch="http://localhost" />
1019 <default remote="default-remote" revision="refs/heads/main" />
1020 <project name="project1" path="tests/path1" />
1021 <project name="project1" path="tests/path2" />
1022 <project name="project2" />
1023 <project name="project3" />
1024 <project name="project4" path="tests/path3" />
1025 <project name="project4" path="tests/path4" />
1026 <project name="project5" />
1027 <project name="project6" path="tests/path6" />
1028
1029 <remove-project name="project1" path="tests/path2" />
1030 <remove-project name="project3" />
1031 <remove-project name="project4" />
1032 <remove-project path="project5" />
1033 <remove-project path="tests/path6" />
1034</manifest>
1035"""
1036 )
1037 found_proj1_path1 = False
1038 found_proj2 = False
1039 for proj in manifest.projects:
1040 if proj.name == "project1":
1041 found_proj1_path1 = True
1042 self.assertEqual(proj.relpath, "tests/path1")
1043 if proj.name == "project2":
1044 found_proj2 = True
1045 self.assertNotEqual(proj.name, "project3")
1046 self.assertNotEqual(proj.name, "project4")
1047 self.assertNotEqual(proj.name, "project5")
1048 self.assertNotEqual(proj.name, "project6")
1049 self.assertTrue(found_proj1_path1)
1050 self.assertTrue(found_proj2)
1051
Michael Kelly37c21c22020-06-13 02:10:40 -07001052
1053class ExtendProjectElementTests(ManifestParseTestCase):
Gavin Makea2e3302023-03-11 06:46:20 +00001054 """Tests for <extend-project>."""
Michael Kelly37c21c22020-06-13 02:10:40 -07001055
Gavin Makea2e3302023-03-11 06:46:20 +00001056 def test_extend_project_dest_path_single_match(self):
1057 manifest = self.getXmlManifest(
1058 """
Michael Kelly37c21c22020-06-13 02:10:40 -07001059<manifest>
1060 <remote name="default-remote" fetch="http://localhost" />
1061 <default remote="default-remote" revision="refs/heads/main" />
1062 <project name="myproject" />
1063 <extend-project name="myproject" dest-path="bar" />
1064</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001065"""
1066 )
1067 self.assertEqual(len(manifest.projects), 1)
1068 self.assertEqual(manifest.projects[0].relpath, "bar")
Michael Kelly37c21c22020-06-13 02:10:40 -07001069
Gavin Makea2e3302023-03-11 06:46:20 +00001070 def test_extend_project_dest_path_multi_match(self):
1071 with self.assertRaises(manifest_xml.ManifestParseError):
1072 manifest = self.getXmlManifest(
1073 """
Michael Kelly37c21c22020-06-13 02:10:40 -07001074<manifest>
1075 <remote name="default-remote" fetch="http://localhost" />
1076 <default remote="default-remote" revision="refs/heads/main" />
1077 <project name="myproject" path="x" />
1078 <project name="myproject" path="y" />
1079 <extend-project name="myproject" dest-path="bar" />
1080</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001081"""
1082 )
1083 manifest.projects
Michael Kelly37c21c22020-06-13 02:10:40 -07001084
Gavin Makea2e3302023-03-11 06:46:20 +00001085 def test_extend_project_dest_path_multi_match_path_specified(self):
1086 manifest = self.getXmlManifest(
1087 """
Michael Kelly37c21c22020-06-13 02:10:40 -07001088<manifest>
1089 <remote name="default-remote" fetch="http://localhost" />
1090 <default remote="default-remote" revision="refs/heads/main" />
1091 <project name="myproject" path="x" />
1092 <project name="myproject" path="y" />
1093 <extend-project name="myproject" path="x" dest-path="bar" />
1094</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001095"""
1096 )
1097 self.assertEqual(len(manifest.projects), 2)
1098 if manifest.projects[0].relpath == "y":
1099 self.assertEqual(manifest.projects[1].relpath, "bar")
1100 else:
1101 self.assertEqual(manifest.projects[0].relpath, "bar")
1102 self.assertEqual(manifest.projects[1].relpath, "y")
Erik Elmeke4cdfdb72022-09-09 17:13:17 +02001103
Gavin Makea2e3302023-03-11 06:46:20 +00001104 def test_extend_project_dest_branch(self):
1105 manifest = self.getXmlManifest(
1106 """
Erik Elmeke4cdfdb72022-09-09 17:13:17 +02001107<manifest>
1108 <remote name="default-remote" fetch="http://localhost" />
1109 <default remote="default-remote" revision="refs/heads/main" dest-branch="foo" />
1110 <project name="myproject" />
1111 <extend-project name="myproject" dest-branch="bar" />
1112</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001113""" # noqa: E501
1114 )
1115 self.assertEqual(len(manifest.projects), 1)
1116 self.assertEqual(manifest.projects[0].dest_branch, "bar")
Erik Elmeke4cdfdb72022-09-09 17:13:17 +02001117
Gavin Makea2e3302023-03-11 06:46:20 +00001118 def test_extend_project_upstream(self):
1119 manifest = self.getXmlManifest(
1120 """
Erik Elmeke4cdfdb72022-09-09 17:13:17 +02001121<manifest>
1122 <remote name="default-remote" fetch="http://localhost" />
1123 <default remote="default-remote" revision="refs/heads/main" />
1124 <project name="myproject" />
1125 <extend-project name="myproject" upstream="bar" />
1126</manifest>
Gavin Makea2e3302023-03-11 06:46:20 +00001127"""
1128 )
1129 self.assertEqual(len(manifest.projects), 1)
1130 self.assertEqual(manifest.projects[0].upstream, "bar")
Michael Kelly3652b492023-09-19 09:51:03 -07001131
1132
1133class NormalizeUrlTests(ManifestParseTestCase):
1134 """Tests for normalize_url() in manifest_xml.py"""
1135
1136 def test_has_trailing_slash(self):
1137 url = "http://foo.com/bar/baz/"
1138 self.assertEqual(
1139 "http://foo.com/bar/baz", manifest_xml.normalize_url(url)
1140 )
1141
Vitalii Dmitriev449b23b2023-12-18 11:25:16 +02001142 url = "http://foo.com/bar/"
1143 self.assertEqual("http://foo.com/bar", manifest_xml.normalize_url(url))
1144
Mike Frysinger48e41372023-12-18 16:31:11 -05001145 def test_has_leading_slash(self):
1146 """SCP-like syntax except a / comes before the : which git disallows."""
1147 url = "/git@foo.com:bar/baf"
1148 self.assertEqual(url, manifest_xml.normalize_url(url))
1149
1150 url = "gi/t@foo.com:bar/baf"
1151 self.assertEqual(url, manifest_xml.normalize_url(url))
1152
1153 url = "git@fo/o.com:bar/baf"
1154 self.assertEqual(url, manifest_xml.normalize_url(url))
1155
Michael Kelly3652b492023-09-19 09:51:03 -07001156 def test_has_no_scheme(self):
1157 """Deal with cases where we have no scheme, but we also
1158 aren't dealing with the git SCP-like syntax
1159 """
1160 url = "foo.com/baf/bat"
1161 self.assertEqual(url, manifest_xml.normalize_url(url))
1162
Vitalii Dmitriev449b23b2023-12-18 11:25:16 +02001163 url = "foo.com/baf"
1164 self.assertEqual(url, manifest_xml.normalize_url(url))
1165
Michael Kelly3652b492023-09-19 09:51:03 -07001166 url = "git@foo.com/baf/bat"
1167 self.assertEqual(url, manifest_xml.normalize_url(url))
1168
Vitalii Dmitriev449b23b2023-12-18 11:25:16 +02001169 url = "git@foo.com/baf"
1170 self.assertEqual(url, manifest_xml.normalize_url(url))
1171
Michael Kelly3652b492023-09-19 09:51:03 -07001172 url = "/file/path/here"
1173 self.assertEqual(url, manifest_xml.normalize_url(url))
1174
1175 def test_has_no_scheme_matches_scp_like_syntax(self):
1176 url = "git@foo.com:bar/baf"
1177 self.assertEqual(
1178 "ssh://git@foo.com/bar/baf", manifest_xml.normalize_url(url)
1179 )
Vitalii Dmitriev449b23b2023-12-18 11:25:16 +02001180
1181 url = "git@foo.com:bar/"
1182 self.assertEqual(
1183 "ssh://git@foo.com/bar", manifest_xml.normalize_url(url)
1184 )
1185
1186 def test_remote_url_resolution(self):
1187 remote = manifest_xml._XmlRemote(
1188 name="foo",
1189 fetch="git@github.com:org2/",
1190 manifestUrl="git@github.com:org2/custom_manifest.git",
1191 )
1192 self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)
1193
1194 remote = manifest_xml._XmlRemote(
1195 name="foo",
1196 fetch="ssh://git@github.com/org2/",
1197 manifestUrl="git@github.com:org2/custom_manifest.git",
1198 )
1199 self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)
1200
1201 remote = manifest_xml._XmlRemote(
1202 name="foo",
1203 fetch="git@github.com:org2/",
1204 manifestUrl="ssh://git@github.com/org2/custom_manifest.git",
1205 )
1206 self.assertEqual("ssh://git@github.com/org2", remote.resolvedFetchUrl)