summaryrefslogtreecommitdiffstats
path: root/tests/test_manifest_xml.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_manifest_xml.py')
-rw-r--r--tests/test_manifest_xml.py845
1 files changed, 845 insertions, 0 deletions
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
new file mode 100644
index 00000000..cb3eb855
--- /dev/null
+++ b/tests/test_manifest_xml.py
@@ -0,0 +1,845 @@
1# 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
17import os
18import platform
19import re
20import shutil
21import tempfile
22import unittest
23import xml.dom.minidom
24
25import error
26import manifest_xml
27
28
29# Invalid paths that we don't want in the filesystem.
30INVALID_FS_PATHS = (
31 '',
32 '.',
33 '..',
34 '../',
35 './',
36 './/',
37 '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',
56 # Block newlines.
57 'f\n/bar',
58 'f\r/bar',
59)
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
67def 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
91class 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
122 @staticmethod
123 def encodeXmlAttr(attr):
124 """Encode |attr| using XML escape rules."""
125 return attr.replace('\r', '&#x000d;').replace('\n', '&#x000a;')
126
127
128class 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')
153 # We allow a single '.' to get a reference to the project itself.
154 check('.', 'bar')
155
156 def test_bad_paths(self):
157 """Make sure bad paths (src & dest) are rejected."""
158 for path in INVALID_FS_PATHS:
159 self.assertRaises(
160 error.ManifestInvalidPathError, self.check_both, path, 'a')
161 self.assertRaises(
162 error.ManifestInvalidPathError, self.check_both, 'a', path)
163
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')
219
220
221class XmlManifestTests(ManifestParseTestCase):
222 """Check manifest processing."""
223
224 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
255 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
268 def test_repo_hooks_unordered(self):
269 """Check repo-hooks settings work even if the project def comes second."""
270 manifest = self.getXmlManifest("""
271<manifest>
272 <remote name="test-remote" fetch="http://localhost" />
273 <default remote="test-remote" revision="refs/heads/main" />
274 <repo-hooks in-project="repohooks" enabled-list="a, b"/>
275 <project name="repohooks" path="src/repohooks"/>
276</manifest>
277""")
278 self.assertEqual(manifest.repo_hooks_project.name, 'repohooks')
279 self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b'])
280
281 def test_unknown_tags(self):
282 """Check superproject settings."""
283 manifest = self.getXmlManifest("""
284<manifest>
285 <remote name="test-remote" fetch="http://localhost" />
286 <default remote="test-remote" revision="refs/heads/main" />
287 <superproject name="superproject"/>
288 <iankaz value="unknown (possible) future tags are ignored"/>
289 <x-custom-tag>X tags are always ignored</x-custom-tag>
290</manifest>
291""")
292 self.assertEqual(manifest.superproject['name'], 'superproject')
293 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
294 self.assertEqual(
295 sort_attributes(manifest.ToXml().toxml()),
296 '<?xml version="1.0" ?><manifest>'
297 '<remote fetch="http://localhost" name="test-remote"/>'
298 '<default remote="test-remote" revision="refs/heads/main"/>'
299 '<superproject name="superproject"/>'
300 '</manifest>')
301
302 def test_remote_annotations(self):
303 """Check remote settings."""
304 manifest = self.getXmlManifest("""
305<manifest>
306 <remote name="test-remote" fetch="http://localhost">
307 <annotation name="foo" value="bar"/>
308 </remote>
309</manifest>
310""")
311 self.assertEqual(manifest.remotes['test-remote'].annotations[0].name, 'foo')
312 self.assertEqual(manifest.remotes['test-remote'].annotations[0].value, 'bar')
313 self.assertEqual(
314 sort_attributes(manifest.ToXml().toxml()),
315 '<?xml version="1.0" ?><manifest>'
316 '<remote fetch="http://localhost" name="test-remote">'
317 '<annotation name="foo" value="bar"/>'
318 '</remote>'
319 '</manifest>')
320
321
322class IncludeElementTests(ManifestParseTestCase):
323 """Tests for <include>."""
324
325 def test_group_levels(self):
326 root_m = os.path.join(self.manifest_dir, 'root.xml')
327 with open(root_m, 'w') as fp:
328 fp.write("""
329<manifest>
330 <remote name="test-remote" fetch="http://localhost" />
331 <default remote="test-remote" revision="refs/heads/main" />
332 <include name="level1.xml" groups="level1-group" />
333 <project name="root-name1" path="root-path1" />
334 <project name="root-name2" path="root-path2" groups="r2g1,r2g2" />
335</manifest>
336""")
337 with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp:
338 fp.write("""
339<manifest>
340 <include name="level2.xml" groups="level2-group" />
341 <project name="level1-name1" path="level1-path1" />
342</manifest>
343""")
344 with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp:
345 fp.write("""
346<manifest>
347 <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" />
348</manifest>
349""")
350 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
351 for proj in include_m.projects:
352 if proj.name == 'root-name1':
353 # Check include group not set on root level proj.
354 self.assertNotIn('level1-group', proj.groups)
355 if proj.name == 'root-name2':
356 # Check root proj group not removed.
357 self.assertIn('r2g1', proj.groups)
358 if proj.name == 'level1-name1':
359 # Check level1 proj has inherited group level 1.
360 self.assertIn('level1-group', proj.groups)
361 if proj.name == 'level2-name1':
362 # Check level2 proj has inherited group levels 1 and 2.
363 self.assertIn('level1-group', proj.groups)
364 self.assertIn('level2-group', proj.groups)
365 # Check level2 proj group not removed.
366 self.assertIn('l2g1', proj.groups)
367
368 def test_allow_bad_name_from_user(self):
369 """Check handling of bad name attribute from the user's input."""
370 def parse(name):
371 name = self.encodeXmlAttr(name)
372 manifest = self.getXmlManifest(f"""
373<manifest>
374 <remote name="default-remote" fetch="http://localhost" />
375 <default remote="default-remote" revision="refs/heads/main" />
376 <include name="{name}" />
377</manifest>
378""")
379 # Force the manifest to be parsed.
380 manifest.ToXml()
381
382 # Setup target of the include.
383 target = os.path.join(self.tempdir, 'target.xml')
384 with open(target, 'w') as fp:
385 fp.write('<manifest></manifest>')
386
387 # Include with absolute path.
388 parse(os.path.abspath(target))
389
390 # Include with relative path.
391 parse(os.path.relpath(target, self.manifest_dir))
392
393 def test_bad_name_checks(self):
394 """Check handling of bad name attribute."""
395 def parse(name):
396 name = self.encodeXmlAttr(name)
397 # Setup target of the include.
398 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
399 fp.write(f'<manifest><include name="{name}"/></manifest>')
400
401 manifest = self.getXmlManifest("""
402<manifest>
403 <remote name="default-remote" fetch="http://localhost" />
404 <default remote="default-remote" revision="refs/heads/main" />
405 <include name="target.xml" />
406</manifest>
407""")
408 # Force the manifest to be parsed.
409 manifest.ToXml()
410
411 # Handle empty name explicitly because a different codepath rejects it.
412 with self.assertRaises(error.ManifestParseError):
413 parse('')
414
415 for path in INVALID_FS_PATHS:
416 if not path:
417 continue
418
419 with self.assertRaises(error.ManifestInvalidPathError):
420 parse(path)
421
422
423class ProjectElementTests(ManifestParseTestCase):
424 """Tests for <project>."""
425
426 def test_group(self):
427 """Check project group settings."""
428 manifest = self.getXmlManifest("""
429<manifest>
430 <remote name="test-remote" fetch="http://localhost" />
431 <default remote="test-remote" revision="refs/heads/main" />
432 <project name="test-name" path="test-path"/>
433 <project name="extras" path="path" groups="g1,g2,g1"/>
434</manifest>
435""")
436 self.assertEqual(len(manifest.projects), 2)
437 # Ordering isn't guaranteed.
438 result = {
439 manifest.projects[0].name: manifest.projects[0].groups,
440 manifest.projects[1].name: manifest.projects[1].groups,
441 }
442 project = manifest.projects[0]
443 self.assertCountEqual(
444 result['test-name'],
445 ['name:test-name', 'all', 'path:test-path'])
446 self.assertCountEqual(
447 result['extras'],
448 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path'])
449 groupstr = 'default,platform-' + platform.system().lower()
450 self.assertEqual(groupstr, manifest.GetGroupsStr())
451 groupstr = 'g1,g2,g1'
452 manifest.manifestProject.config.SetString('manifest.groups', groupstr)
453 self.assertEqual(groupstr, manifest.GetGroupsStr())
454
455 def test_set_revision_id(self):
456 """Check setting of project's revisionId."""
457 manifest = self.getXmlManifest("""
458<manifest>
459 <remote name="default-remote" fetch="http://localhost" />
460 <default remote="default-remote" revision="refs/heads/main" />
461 <project name="test-name"/>
462</manifest>
463""")
464 self.assertEqual(len(manifest.projects), 1)
465 project = manifest.projects[0]
466 project.SetRevisionId('ABCDEF')
467 self.assertEqual(
468 sort_attributes(manifest.ToXml().toxml()),
469 '<?xml version="1.0" ?><manifest>'
470 '<remote fetch="http://localhost" name="default-remote"/>'
471 '<default remote="default-remote" revision="refs/heads/main"/>'
472 '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>'
473 '</manifest>')
474
475 def test_trailing_slash(self):
476 """Check handling of trailing slashes in attributes."""
477 def parse(name, path):
478 name = self.encodeXmlAttr(name)
479 path = self.encodeXmlAttr(path)
480 return self.getXmlManifest(f"""
481<manifest>
482 <remote name="default-remote" fetch="http://localhost" />
483 <default remote="default-remote" revision="refs/heads/main" />
484 <project name="{name}" path="{path}" />
485</manifest>
486""")
487
488 manifest = parse('a/path/', 'foo')
489 self.assertEqual(manifest.projects[0].gitdir,
490 os.path.join(self.tempdir, '.repo/projects/foo.git'))
491 self.assertEqual(manifest.projects[0].objdir,
492 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
493
494 manifest = parse('a/path', 'foo/')
495 self.assertEqual(manifest.projects[0].gitdir,
496 os.path.join(self.tempdir, '.repo/projects/foo.git'))
497 self.assertEqual(manifest.projects[0].objdir,
498 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
499
500 manifest = parse('a/path', 'foo//////')
501 self.assertEqual(manifest.projects[0].gitdir,
502 os.path.join(self.tempdir, '.repo/projects/foo.git'))
503 self.assertEqual(manifest.projects[0].objdir,
504 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
505
506 def test_toplevel_path(self):
507 """Check handling of path=. specially."""
508 def parse(name, path):
509 name = self.encodeXmlAttr(name)
510 path = self.encodeXmlAttr(path)
511 return self.getXmlManifest(f"""
512<manifest>
513 <remote name="default-remote" fetch="http://localhost" />
514 <default remote="default-remote" revision="refs/heads/main" />
515 <project name="{name}" path="{path}" />
516</manifest>
517""")
518
519 for path in ('.', './', './/', './//'):
520 manifest = parse('server/path', path)
521 self.assertEqual(manifest.projects[0].gitdir,
522 os.path.join(self.tempdir, '.repo/projects/..git'))
523
524 def test_bad_path_name_checks(self):
525 """Check handling of bad path & name attributes."""
526 def parse(name, path):
527 name = self.encodeXmlAttr(name)
528 path = self.encodeXmlAttr(path)
529 manifest = self.getXmlManifest(f"""
530<manifest>
531 <remote name="default-remote" fetch="http://localhost" />
532 <default remote="default-remote" revision="refs/heads/main" />
533 <project name="{name}" path="{path}" />
534</manifest>
535""")
536 # Force the manifest to be parsed.
537 manifest.ToXml()
538
539 # Verify the parser is valid by default to avoid buggy tests below.
540 parse('ok', 'ok')
541
542 # Handle empty name explicitly because a different codepath rejects it.
543 # Empty path is OK because it defaults to the name field.
544 with self.assertRaises(error.ManifestParseError):
545 parse('', 'ok')
546
547 for path in INVALID_FS_PATHS:
548 if not path or path.endswith('/'):
549 continue
550
551 with self.assertRaises(error.ManifestInvalidPathError):
552 parse(path, 'ok')
553
554 # We have a dedicated test for path=".".
555 if path not in {'.'}:
556 with self.assertRaises(error.ManifestInvalidPathError):
557 parse('ok', path)
558
559
560class SuperProjectElementTests(ManifestParseTestCase):
561 """Tests for <superproject>."""
562
563 def test_superproject(self):
564 """Check superproject settings."""
565 manifest = self.getXmlManifest("""
566<manifest>
567 <remote name="test-remote" fetch="http://localhost" />
568 <default remote="test-remote" revision="refs/heads/main" />
569 <superproject name="superproject"/>
570</manifest>
571""")
572 self.assertEqual(manifest.superproject['name'], 'superproject')
573 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
574 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
575 self.assertEqual(manifest.superproject['revision'], 'refs/heads/main')
576 self.assertEqual(
577 sort_attributes(manifest.ToXml().toxml()),
578 '<?xml version="1.0" ?><manifest>'
579 '<remote fetch="http://localhost" name="test-remote"/>'
580 '<default remote="test-remote" revision="refs/heads/main"/>'
581 '<superproject name="superproject"/>'
582 '</manifest>')
583
584 def test_superproject_revision(self):
585 """Check superproject settings with a different revision attribute"""
586 self.maxDiff = None
587 manifest = self.getXmlManifest("""
588<manifest>
589 <remote name="test-remote" fetch="http://localhost" />
590 <default remote="test-remote" revision="refs/heads/main" />
591 <superproject name="superproject" revision="refs/heads/stable" />
592</manifest>
593""")
594 self.assertEqual(manifest.superproject['name'], 'superproject')
595 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
596 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
597 self.assertEqual(manifest.superproject['revision'], 'refs/heads/stable')
598 self.assertEqual(
599 sort_attributes(manifest.ToXml().toxml()),
600 '<?xml version="1.0" ?><manifest>'
601 '<remote fetch="http://localhost" name="test-remote"/>'
602 '<default remote="test-remote" revision="refs/heads/main"/>'
603 '<superproject name="superproject" revision="refs/heads/stable"/>'
604 '</manifest>')
605
606 def test_superproject_revision_default_negative(self):
607 """Check superproject settings with a same revision attribute"""
608 self.maxDiff = None
609 manifest = self.getXmlManifest("""
610<manifest>
611 <remote name="test-remote" fetch="http://localhost" />
612 <default remote="test-remote" revision="refs/heads/stable" />
613 <superproject name="superproject" revision="refs/heads/stable" />
614</manifest>
615""")
616 self.assertEqual(manifest.superproject['name'], 'superproject')
617 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
618 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
619 self.assertEqual(manifest.superproject['revision'], 'refs/heads/stable')
620 self.assertEqual(
621 sort_attributes(manifest.ToXml().toxml()),
622 '<?xml version="1.0" ?><manifest>'
623 '<remote fetch="http://localhost" name="test-remote"/>'
624 '<default remote="test-remote" revision="refs/heads/stable"/>'
625 '<superproject name="superproject"/>'
626 '</manifest>')
627
628 def test_superproject_revision_remote(self):
629 """Check superproject settings with a same revision attribute"""
630 self.maxDiff = None
631 manifest = self.getXmlManifest("""
632<manifest>
633 <remote name="test-remote" fetch="http://localhost" revision="refs/heads/main" />
634 <default remote="test-remote" />
635 <superproject name="superproject" revision="refs/heads/stable" />
636</manifest>
637""")
638 self.assertEqual(manifest.superproject['name'], 'superproject')
639 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
640 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
641 self.assertEqual(manifest.superproject['revision'], 'refs/heads/stable')
642 self.assertEqual(
643 sort_attributes(manifest.ToXml().toxml()),
644 '<?xml version="1.0" ?><manifest>'
645 '<remote fetch="http://localhost" name="test-remote" revision="refs/heads/main"/>'
646 '<default remote="test-remote"/>'
647 '<superproject name="superproject" revision="refs/heads/stable"/>'
648 '</manifest>')
649
650 def test_remote(self):
651 """Check superproject settings with a remote."""
652 manifest = self.getXmlManifest("""
653<manifest>
654 <remote name="default-remote" fetch="http://localhost" />
655 <remote name="superproject-remote" fetch="http://localhost" />
656 <default remote="default-remote" revision="refs/heads/main" />
657 <superproject name="platform/superproject" remote="superproject-remote"/>
658</manifest>
659""")
660 self.assertEqual(manifest.superproject['name'], 'platform/superproject')
661 self.assertEqual(manifest.superproject['remote'].name, 'superproject-remote')
662 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/platform/superproject')
663 self.assertEqual(manifest.superproject['revision'], 'refs/heads/main')
664 self.assertEqual(
665 sort_attributes(manifest.ToXml().toxml()),
666 '<?xml version="1.0" ?><manifest>'
667 '<remote fetch="http://localhost" name="default-remote"/>'
668 '<remote fetch="http://localhost" name="superproject-remote"/>'
669 '<default remote="default-remote" revision="refs/heads/main"/>'
670 '<superproject name="platform/superproject" remote="superproject-remote"/>'
671 '</manifest>')
672
673 def test_defalut_remote(self):
674 """Check superproject settings with a default remote."""
675 manifest = self.getXmlManifest("""
676<manifest>
677 <remote name="default-remote" fetch="http://localhost" />
678 <default remote="default-remote" revision="refs/heads/main" />
679 <superproject name="superproject" remote="default-remote"/>
680</manifest>
681""")
682 self.assertEqual(manifest.superproject['name'], 'superproject')
683 self.assertEqual(manifest.superproject['remote'].name, 'default-remote')
684 self.assertEqual(manifest.superproject['revision'], 'refs/heads/main')
685 self.assertEqual(
686 sort_attributes(manifest.ToXml().toxml()),
687 '<?xml version="1.0" ?><manifest>'
688 '<remote fetch="http://localhost" name="default-remote"/>'
689 '<default remote="default-remote" revision="refs/heads/main"/>'
690 '<superproject name="superproject"/>'
691 '</manifest>')
692
693
694class ContactinfoElementTests(ManifestParseTestCase):
695 """Tests for <contactinfo>."""
696
697 def test_contactinfo(self):
698 """Check contactinfo settings."""
699 bugurl = 'http://localhost/contactinfo'
700 manifest = self.getXmlManifest(f"""
701<manifest>
702 <contactinfo bugurl="{bugurl}"/>
703</manifest>
704""")
705 self.assertEqual(manifest.contactinfo.bugurl, bugurl)
706 self.assertEqual(
707 manifest.ToXml().toxml(),
708 '<?xml version="1.0" ?><manifest>'
709 f'<contactinfo bugurl="{bugurl}"/>'
710 '</manifest>')
711
712
713class DefaultElementTests(ManifestParseTestCase):
714 """Tests for <default>."""
715
716 def test_default(self):
717 """Check default settings."""
718 a = manifest_xml._Default()
719 a.revisionExpr = 'foo'
720 a.remote = manifest_xml._XmlRemote(name='remote')
721 b = manifest_xml._Default()
722 b.revisionExpr = 'bar'
723 self.assertEqual(a, a)
724 self.assertNotEqual(a, b)
725 self.assertNotEqual(b, a.remote)
726 self.assertNotEqual(a, 123)
727 self.assertNotEqual(a, None)
728
729
730class RemoteElementTests(ManifestParseTestCase):
731 """Tests for <remote>."""
732
733 def test_remote(self):
734 """Check remote settings."""
735 a = manifest_xml._XmlRemote(name='foo')
736 a.AddAnnotation('key1', 'value1', 'true')
737 b = manifest_xml._XmlRemote(name='foo')
738 b.AddAnnotation('key2', 'value1', 'true')
739 c = manifest_xml._XmlRemote(name='foo')
740 c.AddAnnotation('key1', 'value2', 'true')
741 d = manifest_xml._XmlRemote(name='foo')
742 d.AddAnnotation('key1', 'value1', 'false')
743 self.assertEqual(a, a)
744 self.assertNotEqual(a, b)
745 self.assertNotEqual(a, c)
746 self.assertNotEqual(a, d)
747 self.assertNotEqual(a, manifest_xml._Default())
748 self.assertNotEqual(a, 123)
749 self.assertNotEqual(a, None)
750
751
752class RemoveProjectElementTests(ManifestParseTestCase):
753 """Tests for <remove-project>."""
754
755 def test_remove_one_project(self):
756 manifest = self.getXmlManifest("""
757<manifest>
758 <remote name="default-remote" fetch="http://localhost" />
759 <default remote="default-remote" revision="refs/heads/main" />
760 <project name="myproject" />
761 <remove-project name="myproject" />
762</manifest>
763""")
764 self.assertEqual(manifest.projects, [])
765
766 def test_remove_one_project_one_remains(self):
767 manifest = self.getXmlManifest("""
768<manifest>
769 <remote name="default-remote" fetch="http://localhost" />
770 <default remote="default-remote" revision="refs/heads/main" />
771 <project name="myproject" />
772 <project name="yourproject" />
773 <remove-project name="myproject" />
774</manifest>
775""")
776
777 self.assertEqual(len(manifest.projects), 1)
778 self.assertEqual(manifest.projects[0].name, 'yourproject')
779
780 def test_remove_one_project_doesnt_exist(self):
781 with self.assertRaises(manifest_xml.ManifestParseError):
782 manifest = self.getXmlManifest("""
783<manifest>
784 <remote name="default-remote" fetch="http://localhost" />
785 <default remote="default-remote" revision="refs/heads/main" />
786 <remove-project name="myproject" />
787</manifest>
788""")
789 manifest.projects
790
791 def test_remove_one_optional_project_doesnt_exist(self):
792 manifest = self.getXmlManifest("""
793<manifest>
794 <remote name="default-remote" fetch="http://localhost" />
795 <default remote="default-remote" revision="refs/heads/main" />
796 <remove-project name="myproject" optional="true" />
797</manifest>
798""")
799 self.assertEqual(manifest.projects, [])
800
801
802class ExtendProjectElementTests(ManifestParseTestCase):
803 """Tests for <extend-project>."""
804
805 def test_extend_project_dest_path_single_match(self):
806 manifest = self.getXmlManifest("""
807<manifest>
808 <remote name="default-remote" fetch="http://localhost" />
809 <default remote="default-remote" revision="refs/heads/main" />
810 <project name="myproject" />
811 <extend-project name="myproject" dest-path="bar" />
812</manifest>
813""")
814 self.assertEqual(len(manifest.projects), 1)
815 self.assertEqual(manifest.projects[0].relpath, 'bar')
816
817 def test_extend_project_dest_path_multi_match(self):
818 with self.assertRaises(manifest_xml.ManifestParseError):
819 manifest = self.getXmlManifest("""
820<manifest>
821 <remote name="default-remote" fetch="http://localhost" />
822 <default remote="default-remote" revision="refs/heads/main" />
823 <project name="myproject" path="x" />
824 <project name="myproject" path="y" />
825 <extend-project name="myproject" dest-path="bar" />
826</manifest>
827""")
828 manifest.projects
829
830 def test_extend_project_dest_path_multi_match_path_specified(self):
831 manifest = self.getXmlManifest("""
832<manifest>
833 <remote name="default-remote" fetch="http://localhost" />
834 <default remote="default-remote" revision="refs/heads/main" />
835 <project name="myproject" path="x" />
836 <project name="myproject" path="y" />
837 <extend-project name="myproject" path="x" dest-path="bar" />
838</manifest>
839""")
840 self.assertEqual(len(manifest.projects), 2)
841 if manifest.projects[0].relpath == 'y':
842 self.assertEqual(manifest.projects[1].relpath, 'bar')
843 else:
844 self.assertEqual(manifest.projects[0].relpath, 'bar')
845 self.assertEqual(manifest.projects[1].relpath, 'y')