blob: 96ee4c4a24486f131f9ebed88d8f5b412389242c [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 shutil
21import tempfile
Mike Frysinger04122b72019-07-31 23:32:58 -040022import unittest
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -050023import xml.dom.minidom
Mike Frysinger04122b72019-07-31 23:32:58 -040024
25import error
26import manifest_xml
27
28
Mike Frysingera29424e2021-02-25 21:53:49 -050029# Invalid paths that we don't want in the filesystem.
30INVALID_FS_PATHS = (
31 '',
32 '.',
33 '..',
34 '../',
35 './',
Mike Frysinger0458faa2021-03-10 23:35:44 -050036 './/',
Mike Frysingera29424e2021-02-25 21:53:49 -050037 'foo/',
38 './foo',
39 '../foo',
40 'foo/./bar',
41 'foo/../../bar',
42 '/foo',
43 './../foo',
44 '.git/foo',
45 # Check case folding.
46 '.GIT/foo',
47 'blah/.git/foo',
48 '.repo/foo',
49 '.repoconfig',
50 # Block ~ due to 8.3 filenames on Windows filesystems.
51 '~',
52 'foo~',
53 'blah/foo~',
54 # Block Unicode characters that get normalized out by filesystems.
55 u'foo\u200Cbar',
Mike Frysingerf69c7ee2021-04-29 23:15:31 -040056 # Block newlines.
57 'f\n/bar',
58 'f\r/bar',
Mike Frysingera29424e2021-02-25 21:53:49 -050059)
60
61# Make sure platforms that use path separators (e.g. Windows) are also
62# rejected properly.
63if os.path.sep != '/':
64 INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS)
65
66
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +020067def sort_attributes(manifest):
68 """Sort the attributes of all elements alphabetically.
69
70 This is needed because different versions of the toxml() function from
71 xml.dom.minidom outputs the attributes of elements in different orders.
72 Before Python 3.8 they were output alphabetically, later versions preserve
73 the order specified by the user.
74
75 Args:
76 manifest: String containing an XML manifest.
77
78 Returns:
79 The XML manifest with the attributes of all elements sorted alphabetically.
80 """
81 new_manifest = ''
82 # This will find every element in the XML manifest, whether they have
83 # attributes or not. This simplifies recreating the manifest below.
84 matches = re.findall(r'(<[/?]?[a-z-]+\s*)((?:\S+?="[^"]+"\s*?)*)(\s*[/?]?>)', manifest)
85 for head, attrs, tail in matches:
86 m = re.findall(r'\S+?="[^"]+"', attrs)
87 new_manifest += head + ' '.join(sorted(m)) + tail
88 return new_manifest
89
90
Mike Frysinger37ac3d62021-02-25 04:54:56 -050091class ManifestParseTestCase(unittest.TestCase):
92 """TestCase for parsing manifests."""
93
94 def setUp(self):
95 self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
96 self.repodir = os.path.join(self.tempdir, '.repo')
97 self.manifest_dir = os.path.join(self.repodir, 'manifests')
98 self.manifest_file = os.path.join(
99 self.repodir, manifest_xml.MANIFEST_FILE_NAME)
100 self.local_manifest_dir = os.path.join(
101 self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME)
102 os.mkdir(self.repodir)
103 os.mkdir(self.manifest_dir)
104
105 # The manifest parsing really wants a git repo currently.
106 gitdir = os.path.join(self.repodir, 'manifests.git')
107 os.mkdir(gitdir)
108 with open(os.path.join(gitdir, 'config'), 'w') as fp:
109 fp.write("""[remote "origin"]
110 url = https://localhost:0/manifest
111""")
112
113 def tearDown(self):
114 shutil.rmtree(self.tempdir, ignore_errors=True)
115
116 def getXmlManifest(self, data):
117 """Helper to initialize a manifest for testing."""
118 with open(self.manifest_file, 'w') as fp:
119 fp.write(data)
120 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
121
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400122 @staticmethod
123 def encodeXmlAttr(attr):
124 """Encode |attr| using XML escape rules."""
125 return attr.replace('\r', '&#x000d;').replace('\n', '&#x000a;')
126
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500127
Mike Frysinger04122b72019-07-31 23:32:58 -0400128class ManifestValidateFilePaths(unittest.TestCase):
129 """Check _ValidateFilePaths helper.
130
131 This doesn't access a real filesystem.
132 """
133
134 def check_both(self, *args):
135 manifest_xml.XmlManifest._ValidateFilePaths('copyfile', *args)
136 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
137
138 def test_normal_path(self):
139 """Make sure good paths are accepted."""
140 self.check_both('foo', 'bar')
141 self.check_both('foo/bar', 'bar')
142 self.check_both('foo', 'bar/bar')
143 self.check_both('foo/bar', 'bar/bar')
144
145 def test_symlink_targets(self):
146 """Some extra checks for symlinks."""
147 def check(*args):
148 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
149
150 # We allow symlinks to end in a slash since we allow them to point to dirs
151 # in general. Technically the slash isn't necessary.
152 check('foo/', 'bar')
Mike Frysingerae625412020-02-10 17:10:03 -0500153 # We allow a single '.' to get a reference to the project itself.
154 check('.', 'bar')
Mike Frysinger04122b72019-07-31 23:32:58 -0400155
156 def test_bad_paths(self):
157 """Make sure bad paths (src & dest) are rejected."""
Mike Frysingera29424e2021-02-25 21:53:49 -0500158 for path in INVALID_FS_PATHS:
Mike Frysinger04122b72019-07-31 23:32:58 -0400159 self.assertRaises(
160 error.ManifestInvalidPathError, self.check_both, path, 'a')
161 self.assertRaises(
162 error.ManifestInvalidPathError, self.check_both, 'a', path)
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500163
164
165class ValueTests(unittest.TestCase):
166 """Check utility parsing code."""
167
168 def _get_node(self, text):
169 return xml.dom.minidom.parseString(text).firstChild
170
171 def test_bool_default(self):
172 """Check XmlBool default handling."""
173 node = self._get_node('<node/>')
174 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
175 self.assertIsNone(manifest_xml.XmlBool(node, 'a', None))
176 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
177
178 node = self._get_node('<node a=""/>')
179 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
180
181 def test_bool_invalid(self):
182 """Check XmlBool invalid handling."""
183 node = self._get_node('<node a="moo"/>')
184 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
185
186 def test_bool_true(self):
187 """Check XmlBool true values."""
188 for value in ('yes', 'true', '1'):
189 node = self._get_node('<node a="%s"/>' % (value,))
190 self.assertTrue(manifest_xml.XmlBool(node, 'a'))
191
192 def test_bool_false(self):
193 """Check XmlBool false values."""
194 for value in ('no', 'false', '0'):
195 node = self._get_node('<node a="%s"/>' % (value,))
196 self.assertFalse(manifest_xml.XmlBool(node, 'a'))
197
198 def test_int_default(self):
199 """Check XmlInt default handling."""
200 node = self._get_node('<node/>')
201 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
202 self.assertIsNone(manifest_xml.XmlInt(node, 'a', None))
203 self.assertEqual(123, manifest_xml.XmlInt(node, 'a', 123))
204
205 node = self._get_node('<node a=""/>')
206 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
207
208 def test_int_good(self):
209 """Check XmlInt numeric handling."""
210 for value in (-1, 0, 1, 50000):
211 node = self._get_node('<node a="%s"/>' % (value,))
212 self.assertEqual(value, manifest_xml.XmlInt(node, 'a'))
213
214 def test_int_invalid(self):
215 """Check XmlInt invalid handling."""
216 with self.assertRaises(error.ManifestParseError):
217 node = self._get_node('<node a="xx"/>')
218 manifest_xml.XmlInt(node, 'a')
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400219
220
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500221class XmlManifestTests(ManifestParseTestCase):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400222 """Check manifest processing."""
223
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400224 def test_empty(self):
225 """Parse an 'empty' manifest file."""
226 manifest = self.getXmlManifest(
227 '<?xml version="1.0" encoding="UTF-8"?>'
228 '<manifest></manifest>')
229 self.assertEqual(manifest.remotes, {})
230 self.assertEqual(manifest.projects, [])
231
232 def test_link(self):
233 """Verify Link handling with new names."""
234 manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file)
235 with open(os.path.join(self.manifest_dir, 'foo.xml'), 'w') as fp:
236 fp.write('<manifest></manifest>')
237 manifest.Link('foo.xml')
238 with open(self.manifest_file) as fp:
239 self.assertIn('<include name="foo.xml" />', fp.read())
240
241 def test_toxml_empty(self):
242 """Verify the ToXml() helper."""
243 manifest = self.getXmlManifest(
244 '<?xml version="1.0" encoding="UTF-8"?>'
245 '<manifest></manifest>')
246 self.assertEqual(manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>')
247
248 def test_todict_empty(self):
249 """Verify the ToDict() helper."""
250 manifest = self.getXmlManifest(
251 '<?xml version="1.0" encoding="UTF-8"?>'
252 '<manifest></manifest>')
253 self.assertEqual(manifest.ToDict(), {})
254
Mike Frysinger51e39d52020-12-04 05:32:06 -0500255 def test_repo_hooks(self):
256 """Check repo-hooks settings."""
257 manifest = self.getXmlManifest("""
258<manifest>
259 <remote name="test-remote" fetch="http://localhost" />
260 <default remote="test-remote" revision="refs/heads/main" />
261 <project name="repohooks" path="src/repohooks"/>
262 <repo-hooks in-project="repohooks" enabled-list="a, b"/>
263</manifest>
264""")
265 self.assertEqual(manifest.repo_hooks_project.name, 'repohooks')
266 self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b'])
267
Raman Tenneti48b2d102021-01-11 12:18:47 -0800268 def test_unknown_tags(self):
269 """Check superproject settings."""
270 manifest = self.getXmlManifest("""
271<manifest>
272 <remote name="test-remote" fetch="http://localhost" />
273 <default remote="test-remote" revision="refs/heads/main" />
274 <superproject name="superproject"/>
275 <iankaz value="unknown (possible) future tags are ignored"/>
276 <x-custom-tag>X tags are always ignored</x-custom-tag>
277</manifest>
278""")
279 self.assertEqual(manifest.superproject['name'], 'superproject')
280 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
281 self.assertEqual(
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200282 sort_attributes(manifest.ToXml().toxml()),
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700283 '<?xml version="1.0" ?><manifest>'
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200284 '<remote fetch="http://localhost" name="test-remote"/>'
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700285 '<default remote="test-remote" revision="refs/heads/main"/>'
286 '<superproject name="superproject"/>'
Raman Tenneti48b2d102021-01-11 12:18:47 -0800287 '</manifest>')
288
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200289
Mike Frysingera29424e2021-02-25 21:53:49 -0500290class IncludeElementTests(ManifestParseTestCase):
291 """Tests for <include>."""
Raman Tennetib5c5a5e2021-02-06 09:44:15 -0800292
Mike Frysingera29424e2021-02-25 21:53:49 -0500293 def test_group_levels(self):
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200294 root_m = os.path.join(self.manifest_dir, 'root.xml')
295 with open(root_m, 'w') as fp:
296 fp.write("""
297<manifest>
298 <remote name="test-remote" fetch="http://localhost" />
299 <default remote="test-remote" revision="refs/heads/main" />
300 <include name="level1.xml" groups="level1-group" />
301 <project name="root-name1" path="root-path1" />
302 <project name="root-name2" path="root-path2" groups="r2g1,r2g2" />
303</manifest>
304""")
305 with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp:
306 fp.write("""
307<manifest>
308 <include name="level2.xml" groups="level2-group" />
309 <project name="level1-name1" path="level1-path1" />
310</manifest>
311""")
312 with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp:
313 fp.write("""
314<manifest>
315 <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" />
316</manifest>
317""")
318 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
319 for proj in include_m.projects:
320 if proj.name == 'root-name1':
321 # Check include group not set on root level proj.
322 self.assertNotIn('level1-group', proj.groups)
323 if proj.name == 'root-name2':
324 # Check root proj group not removed.
325 self.assertIn('r2g1', proj.groups)
326 if proj.name == 'level1-name1':
327 # Check level1 proj has inherited group level 1.
328 self.assertIn('level1-group', proj.groups)
329 if proj.name == 'level2-name1':
330 # Check level2 proj has inherited group levels 1 and 2.
331 self.assertIn('level1-group', proj.groups)
332 self.assertIn('level2-group', proj.groups)
333 # Check level2 proj group not removed.
334 self.assertIn('l2g1', proj.groups)
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500335
Mike Frysinger54133972021-03-01 21:38:08 -0500336 def test_allow_bad_name_from_user(self):
337 """Check handling of bad name attribute from the user's input."""
Mike Frysingera29424e2021-02-25 21:53:49 -0500338 def parse(name):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400339 name = self.encodeXmlAttr(name)
Mike Frysingera29424e2021-02-25 21:53:49 -0500340 manifest = self.getXmlManifest(f"""
341<manifest>
342 <remote name="default-remote" fetch="http://localhost" />
343 <default remote="default-remote" revision="refs/heads/main" />
344 <include name="{name}" />
345</manifest>
346""")
347 # Force the manifest to be parsed.
348 manifest.ToXml()
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500349
Mike Frysinger54133972021-03-01 21:38:08 -0500350 # Setup target of the include.
351 target = os.path.join(self.tempdir, 'target.xml')
352 with open(target, 'w') as fp:
353 fp.write('<manifest></manifest>')
354
355 # Include with absolute path.
356 parse(os.path.abspath(target))
357
358 # Include with relative path.
359 parse(os.path.relpath(target, self.manifest_dir))
360
361 def test_bad_name_checks(self):
362 """Check handling of bad name attribute."""
363 def parse(name):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400364 name = self.encodeXmlAttr(name)
Mike Frysinger54133972021-03-01 21:38:08 -0500365 # Setup target of the include.
366 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
367 fp.write(f'<manifest><include name="{name}"/></manifest>')
368
369 manifest = self.getXmlManifest("""
370<manifest>
371 <remote name="default-remote" fetch="http://localhost" />
372 <default remote="default-remote" revision="refs/heads/main" />
373 <include name="target.xml" />
374</manifest>
375""")
376 # Force the manifest to be parsed.
377 manifest.ToXml()
378
Mike Frysingera29424e2021-02-25 21:53:49 -0500379 # Handle empty name explicitly because a different codepath rejects it.
380 with self.assertRaises(error.ManifestParseError):
381 parse('')
382
383 for path in INVALID_FS_PATHS:
384 if not path:
385 continue
386
387 with self.assertRaises(error.ManifestInvalidPathError):
388 parse(path)
389
390
391class ProjectElementTests(ManifestParseTestCase):
392 """Tests for <project>."""
393
394 def test_group(self):
395 """Check project group settings."""
396 manifest = self.getXmlManifest("""
397<manifest>
398 <remote name="test-remote" fetch="http://localhost" />
399 <default remote="test-remote" revision="refs/heads/main" />
400 <project name="test-name" path="test-path"/>
401 <project name="extras" path="path" groups="g1,g2,g1"/>
402</manifest>
403""")
404 self.assertEqual(len(manifest.projects), 2)
405 # Ordering isn't guaranteed.
406 result = {
407 manifest.projects[0].name: manifest.projects[0].groups,
408 manifest.projects[1].name: manifest.projects[1].groups,
409 }
410 project = manifest.projects[0]
411 self.assertCountEqual(
412 result['test-name'],
413 ['name:test-name', 'all', 'path:test-path'])
414 self.assertCountEqual(
415 result['extras'],
416 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path'])
Raman Tenneti080877e2021-03-09 15:19:06 -0800417 groupstr = 'default,platform-' + platform.system().lower()
418 self.assertEqual(groupstr, manifest.GetGroupsStr())
419 groupstr = 'g1,g2,g1'
420 manifest.manifestProject.config.SetString('manifest.groups', groupstr)
421 self.assertEqual(groupstr, manifest.GetGroupsStr())
Mike Frysingera29424e2021-02-25 21:53:49 -0500422
423 def test_set_revision_id(self):
424 """Check setting of project's revisionId."""
425 manifest = self.getXmlManifest("""
426<manifest>
427 <remote name="default-remote" fetch="http://localhost" />
428 <default remote="default-remote" revision="refs/heads/main" />
429 <project name="test-name"/>
430</manifest>
431""")
432 self.assertEqual(len(manifest.projects), 1)
433 project = manifest.projects[0]
434 project.SetRevisionId('ABCDEF')
435 self.assertEqual(
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200436 sort_attributes(manifest.ToXml().toxml()),
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700437 '<?xml version="1.0" ?><manifest>'
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200438 '<remote fetch="http://localhost" name="default-remote"/>'
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700439 '<default remote="default-remote" revision="refs/heads/main"/>'
Xin Li0e776a52021-06-29 21:42:34 +0000440 '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>'
Mike Frysingera29424e2021-02-25 21:53:49 -0500441 '</manifest>')
442
443 def test_trailing_slash(self):
444 """Check handling of trailing slashes in attributes."""
445 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400446 name = self.encodeXmlAttr(name)
447 path = self.encodeXmlAttr(path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500448 return self.getXmlManifest(f"""
449<manifest>
450 <remote name="default-remote" fetch="http://localhost" />
451 <default remote="default-remote" revision="refs/heads/main" />
452 <project name="{name}" path="{path}" />
453</manifest>
454""")
455
456 manifest = parse('a/path/', 'foo')
457 self.assertEqual(manifest.projects[0].gitdir,
458 os.path.join(self.tempdir, '.repo/projects/foo.git'))
459 self.assertEqual(manifest.projects[0].objdir,
460 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
461
462 manifest = parse('a/path', 'foo/')
463 self.assertEqual(manifest.projects[0].gitdir,
464 os.path.join(self.tempdir, '.repo/projects/foo.git'))
465 self.assertEqual(manifest.projects[0].objdir,
466 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
467
Mike Frysinger0458faa2021-03-10 23:35:44 -0500468 manifest = parse('a/path', 'foo//////')
469 self.assertEqual(manifest.projects[0].gitdir,
470 os.path.join(self.tempdir, '.repo/projects/foo.git'))
471 self.assertEqual(manifest.projects[0].objdir,
472 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
473
474 def test_toplevel_path(self):
475 """Check handling of path=. specially."""
476 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400477 name = self.encodeXmlAttr(name)
478 path = self.encodeXmlAttr(path)
Mike Frysinger0458faa2021-03-10 23:35:44 -0500479 return self.getXmlManifest(f"""
480<manifest>
481 <remote name="default-remote" fetch="http://localhost" />
482 <default remote="default-remote" revision="refs/heads/main" />
483 <project name="{name}" path="{path}" />
484</manifest>
485""")
486
487 for path in ('.', './', './/', './//'):
488 manifest = parse('server/path', path)
489 self.assertEqual(manifest.projects[0].gitdir,
490 os.path.join(self.tempdir, '.repo/projects/..git'))
491
Mike Frysingera29424e2021-02-25 21:53:49 -0500492 def test_bad_path_name_checks(self):
493 """Check handling of bad path & name attributes."""
494 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400495 name = self.encodeXmlAttr(name)
496 path = self.encodeXmlAttr(path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500497 manifest = self.getXmlManifest(f"""
498<manifest>
499 <remote name="default-remote" fetch="http://localhost" />
500 <default remote="default-remote" revision="refs/heads/main" />
501 <project name="{name}" path="{path}" />
502</manifest>
503""")
504 # Force the manifest to be parsed.
505 manifest.ToXml()
506
507 # Verify the parser is valid by default to avoid buggy tests below.
508 parse('ok', 'ok')
509
510 # Handle empty name explicitly because a different codepath rejects it.
511 # Empty path is OK because it defaults to the name field.
512 with self.assertRaises(error.ManifestParseError):
513 parse('', 'ok')
514
515 for path in INVALID_FS_PATHS:
516 if not path or path.endswith('/'):
517 continue
518
519 with self.assertRaises(error.ManifestInvalidPathError):
520 parse(path, 'ok')
Mike Frysinger0458faa2021-03-10 23:35:44 -0500521
522 # We have a dedicated test for path=".".
523 if path not in {'.'}:
524 with self.assertRaises(error.ManifestInvalidPathError):
525 parse('ok', path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500526
527
528class SuperProjectElementTests(ManifestParseTestCase):
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500529 """Tests for <superproject>."""
530
531 def test_superproject(self):
532 """Check superproject settings."""
533 manifest = self.getXmlManifest("""
534<manifest>
535 <remote name="test-remote" fetch="http://localhost" />
536 <default remote="test-remote" revision="refs/heads/main" />
537 <superproject name="superproject"/>
538</manifest>
539""")
540 self.assertEqual(manifest.superproject['name'], 'superproject')
541 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
542 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
543 self.assertEqual(
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200544 sort_attributes(manifest.ToXml().toxml()),
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700545 '<?xml version="1.0" ?><manifest>'
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200546 '<remote fetch="http://localhost" name="test-remote"/>'
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700547 '<default remote="test-remote" revision="refs/heads/main"/>'
548 '<superproject name="superproject"/>'
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500549 '</manifest>')
550
551 def test_remote(self):
552 """Check superproject settings with a remote."""
553 manifest = self.getXmlManifest("""
554<manifest>
555 <remote name="default-remote" fetch="http://localhost" />
556 <remote name="superproject-remote" fetch="http://localhost" />
557 <default remote="default-remote" revision="refs/heads/main" />
558 <superproject name="platform/superproject" remote="superproject-remote"/>
559</manifest>
560""")
561 self.assertEqual(manifest.superproject['name'], 'platform/superproject')
562 self.assertEqual(manifest.superproject['remote'].name, 'superproject-remote')
563 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/platform/superproject')
564 self.assertEqual(
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200565 sort_attributes(manifest.ToXml().toxml()),
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700566 '<?xml version="1.0" ?><manifest>'
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200567 '<remote fetch="http://localhost" name="default-remote"/>'
568 '<remote fetch="http://localhost" name="superproject-remote"/>'
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700569 '<default remote="default-remote" revision="refs/heads/main"/>'
570 '<superproject name="platform/superproject" remote="superproject-remote"/>'
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500571 '</manifest>')
572
573 def test_defalut_remote(self):
574 """Check superproject settings with a default remote."""
575 manifest = self.getXmlManifest("""
576<manifest>
577 <remote name="default-remote" fetch="http://localhost" />
578 <default remote="default-remote" revision="refs/heads/main" />
579 <superproject name="superproject" remote="default-remote"/>
580</manifest>
581""")
582 self.assertEqual(manifest.superproject['name'], 'superproject')
583 self.assertEqual(manifest.superproject['remote'].name, 'default-remote')
584 self.assertEqual(
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200585 sort_attributes(manifest.ToXml().toxml()),
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700586 '<?xml version="1.0" ?><manifest>'
Peter Kjellerstedt5d58c182021-04-12 21:16:36 +0200587 '<remote fetch="http://localhost" name="default-remote"/>'
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700588 '<default remote="default-remote" revision="refs/heads/main"/>'
589 '<superproject name="superproject"/>'
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500590 '</manifest>')
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700591
592
593class ContactinfoElementTests(ManifestParseTestCase):
594 """Tests for <contactinfo>."""
595
596 def test_contactinfo(self):
597 """Check contactinfo settings."""
598 bugurl = 'http://localhost/contactinfo'
599 manifest = self.getXmlManifest(f"""
600<manifest>
601 <contactinfo bugurl="{bugurl}"/>
602</manifest>
603""")
Raman Tenneti993af5e2021-05-12 12:00:31 -0700604 self.assertEqual(manifest.contactinfo.bugurl, bugurl)
Raman Tenneti1c3f57e2021-05-04 12:32:13 -0700605 self.assertEqual(
606 manifest.ToXml().toxml(),
Raman Tenneti2f8fdbe2021-05-04 18:54:52 -0700607 '<?xml version="1.0" ?><manifest>'
608 f'<contactinfo bugurl="{bugurl}"/>'
609 '</manifest>')
Jack Neus5ba21202021-06-09 15:21:25 +0000610
611
612class DefaultElementTests(ManifestParseTestCase):
613 """Tests for <default>."""
614
615 def test_default(self):
616 """Check default settings."""
617 a = manifest_xml._Default()
618 a.revisionExpr = 'foo'
619 a.remote = manifest_xml._XmlRemote(name='remote')
620 b = manifest_xml._Default()
621 b.revisionExpr = 'bar'
622 self.assertEqual(a, a)
623 self.assertNotEqual(a, b)
624 self.assertNotEqual(b, a.remote)
625 self.assertNotEqual(a, 123)
626 self.assertNotEqual(a, None)
627
628
629class RemoteElementTests(ManifestParseTestCase):
630 """Tests for <remote>."""
631
632 def test_remote(self):
633 """Check remote settings."""
634 a = manifest_xml._XmlRemote(name='foo')
635 b = manifest_xml._XmlRemote(name='bar')
636 self.assertEqual(a, a)
637 self.assertNotEqual(a, b)
638 self.assertNotEqual(a, manifest_xml._Default())
639 self.assertNotEqual(a, 123)
640 self.assertNotEqual(a, None)
Michael Kelly06da9982021-06-30 01:58:28 -0700641
642
643class RemoveProjectElementTests(ManifestParseTestCase):
644 """Tests for <remove-project>."""
645
646 def test_remove_one_project(self):
647 manifest = self.getXmlManifest("""
648<manifest>
649 <remote name="default-remote" fetch="http://localhost" />
650 <default remote="default-remote" revision="refs/heads/main" />
651 <project name="myproject" />
652 <remove-project name="myproject" />
653</manifest>
654""")
655 self.assertEqual(manifest.projects, [])
656
657 def test_remove_one_project_one_remains(self):
658 manifest = self.getXmlManifest("""
659<manifest>
660 <remote name="default-remote" fetch="http://localhost" />
661 <default remote="default-remote" revision="refs/heads/main" />
662 <project name="myproject" />
663 <project name="yourproject" />
664 <remove-project name="myproject" />
665</manifest>
666""")
667
668 self.assertEqual(len(manifest.projects), 1)
669 self.assertEqual(manifest.projects[0].name, 'yourproject')
670
671 def test_remove_one_project_doesnt_exist(self):
672 with self.assertRaises(manifest_xml.ManifestParseError):
673 manifest = self.getXmlManifest("""
674<manifest>
675 <remote name="default-remote" fetch="http://localhost" />
676 <default remote="default-remote" revision="refs/heads/main" />
677 <remove-project name="myproject" />
678</manifest>
679""")
680 manifest.projects
681
682 def test_remove_one_optional_project_doesnt_exist(self):
683 manifest = self.getXmlManifest("""
684<manifest>
685 <remote name="default-remote" fetch="http://localhost" />
686 <default remote="default-remote" revision="refs/heads/main" />
687 <remove-project name="myproject" optional="true" />
688</manifest>
689""")
690 self.assertEqual(manifest.projects, [])