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.py346
1 files changed, 321 insertions, 25 deletions
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py
index eda06968..cb3eb855 100644
--- a/tests/test_manifest_xml.py
+++ b/tests/test_manifest_xml.py
@@ -16,6 +16,7 @@
16 16
17import os 17import os
18import platform 18import platform
19import re
19import shutil 20import shutil
20import tempfile 21import tempfile
21import unittest 22import unittest
@@ -52,6 +53,9 @@ INVALID_FS_PATHS = (
52 'blah/foo~', 53 'blah/foo~',
53 # Block Unicode characters that get normalized out by filesystems. 54 # Block Unicode characters that get normalized out by filesystems.
54 u'foo\u200Cbar', 55 u'foo\u200Cbar',
56 # Block newlines.
57 'f\n/bar',
58 'f\r/bar',
55) 59)
56 60
57# Make sure platforms that use path separators (e.g. Windows) are also 61# Make sure platforms that use path separators (e.g. Windows) are also
@@ -60,6 +64,30 @@ if os.path.sep != '/':
60 INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS) 64 INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS)
61 65
62 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
63class ManifestParseTestCase(unittest.TestCase): 91class ManifestParseTestCase(unittest.TestCase):
64 """TestCase for parsing manifests.""" 92 """TestCase for parsing manifests."""
65 93
@@ -91,6 +119,11 @@ class ManifestParseTestCase(unittest.TestCase):
91 fp.write(data) 119 fp.write(data)
92 return manifest_xml.XmlManifest(self.repodir, self.manifest_file) 120 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
93 121
122 @staticmethod
123 def encodeXmlAttr(attr):
124 """Encode |attr| using XML escape rules."""
125 return attr.replace('\r', '&#x000d;').replace('\n', '&#x000a;')
126
94 127
95class ManifestValidateFilePaths(unittest.TestCase): 128class ManifestValidateFilePaths(unittest.TestCase):
96 """Check _ValidateFilePaths helper. 129 """Check _ValidateFilePaths helper.
@@ -232,6 +265,19 @@ class XmlManifestTests(ManifestParseTestCase):
232 self.assertEqual(manifest.repo_hooks_project.name, 'repohooks') 265 self.assertEqual(manifest.repo_hooks_project.name, 'repohooks')
233 self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b']) 266 self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b'])
234 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
235 def test_unknown_tags(self): 281 def test_unknown_tags(self):
236 """Check superproject settings.""" 282 """Check superproject settings."""
237 manifest = self.getXmlManifest(""" 283 manifest = self.getXmlManifest("""
@@ -246,11 +292,30 @@ class XmlManifestTests(ManifestParseTestCase):
246 self.assertEqual(manifest.superproject['name'], 'superproject') 292 self.assertEqual(manifest.superproject['name'], 'superproject')
247 self.assertEqual(manifest.superproject['remote'].name, 'test-remote') 293 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
248 self.assertEqual( 294 self.assertEqual(
249 manifest.ToXml().toxml(), 295 sort_attributes(manifest.ToXml().toxml()),
250 '<?xml version="1.0" ?><manifest>' + 296 '<?xml version="1.0" ?><manifest>'
251 '<remote name="test-remote" fetch="http://localhost"/>' + 297 '<remote fetch="http://localhost" name="test-remote"/>'
252 '<default remote="test-remote" revision="refs/heads/main"/>' + 298 '<default remote="test-remote" revision="refs/heads/main"/>'
253 '<superproject name="superproject"/>' + 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>'
254 '</manifest>') 319 '</manifest>')
255 320
256 321
@@ -303,6 +368,7 @@ class IncludeElementTests(ManifestParseTestCase):
303 def test_allow_bad_name_from_user(self): 368 def test_allow_bad_name_from_user(self):
304 """Check handling of bad name attribute from the user's input.""" 369 """Check handling of bad name attribute from the user's input."""
305 def parse(name): 370 def parse(name):
371 name = self.encodeXmlAttr(name)
306 manifest = self.getXmlManifest(f""" 372 manifest = self.getXmlManifest(f"""
307<manifest> 373<manifest>
308 <remote name="default-remote" fetch="http://localhost" /> 374 <remote name="default-remote" fetch="http://localhost" />
@@ -327,6 +393,7 @@ class IncludeElementTests(ManifestParseTestCase):
327 def test_bad_name_checks(self): 393 def test_bad_name_checks(self):
328 """Check handling of bad name attribute.""" 394 """Check handling of bad name attribute."""
329 def parse(name): 395 def parse(name):
396 name = self.encodeXmlAttr(name)
330 # Setup target of the include. 397 # Setup target of the include.
331 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp: 398 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
332 fp.write(f'<manifest><include name="{name}"/></manifest>') 399 fp.write(f'<manifest><include name="{name}"/></manifest>')
@@ -398,16 +465,18 @@ class ProjectElementTests(ManifestParseTestCase):
398 project = manifest.projects[0] 465 project = manifest.projects[0]
399 project.SetRevisionId('ABCDEF') 466 project.SetRevisionId('ABCDEF')
400 self.assertEqual( 467 self.assertEqual(
401 manifest.ToXml().toxml(), 468 sort_attributes(manifest.ToXml().toxml()),
402 '<?xml version="1.0" ?><manifest>' + 469 '<?xml version="1.0" ?><manifest>'
403 '<remote name="default-remote" fetch="http://localhost"/>' + 470 '<remote fetch="http://localhost" name="default-remote"/>'
404 '<default remote="default-remote" revision="refs/heads/main"/>' + 471 '<default remote="default-remote" revision="refs/heads/main"/>'
405 '<project name="test-name" revision="ABCDEF"/>' + 472 '<project name="test-name" revision="ABCDEF" upstream="refs/heads/main"/>'
406 '</manifest>') 473 '</manifest>')
407 474
408 def test_trailing_slash(self): 475 def test_trailing_slash(self):
409 """Check handling of trailing slashes in attributes.""" 476 """Check handling of trailing slashes in attributes."""
410 def parse(name, path): 477 def parse(name, path):
478 name = self.encodeXmlAttr(name)
479 path = self.encodeXmlAttr(path)
411 return self.getXmlManifest(f""" 480 return self.getXmlManifest(f"""
412<manifest> 481<manifest>
413 <remote name="default-remote" fetch="http://localhost" /> 482 <remote name="default-remote" fetch="http://localhost" />
@@ -437,6 +506,8 @@ class ProjectElementTests(ManifestParseTestCase):
437 def test_toplevel_path(self): 506 def test_toplevel_path(self):
438 """Check handling of path=. specially.""" 507 """Check handling of path=. specially."""
439 def parse(name, path): 508 def parse(name, path):
509 name = self.encodeXmlAttr(name)
510 path = self.encodeXmlAttr(path)
440 return self.getXmlManifest(f""" 511 return self.getXmlManifest(f"""
441<manifest> 512<manifest>
442 <remote name="default-remote" fetch="http://localhost" /> 513 <remote name="default-remote" fetch="http://localhost" />
@@ -453,6 +524,8 @@ class ProjectElementTests(ManifestParseTestCase):
453 def test_bad_path_name_checks(self): 524 def test_bad_path_name_checks(self):
454 """Check handling of bad path & name attributes.""" 525 """Check handling of bad path & name attributes."""
455 def parse(name, path): 526 def parse(name, path):
527 name = self.encodeXmlAttr(name)
528 path = self.encodeXmlAttr(path)
456 manifest = self.getXmlManifest(f""" 529 manifest = self.getXmlManifest(f"""
457<manifest> 530<manifest>
458 <remote name="default-remote" fetch="http://localhost" /> 531 <remote name="default-remote" fetch="http://localhost" />
@@ -499,12 +572,79 @@ class SuperProjectElementTests(ManifestParseTestCase):
499 self.assertEqual(manifest.superproject['name'], 'superproject') 572 self.assertEqual(manifest.superproject['name'], 'superproject')
500 self.assertEqual(manifest.superproject['remote'].name, 'test-remote') 573 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
501 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject') 574 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
575 self.assertEqual(manifest.superproject['revision'], 'refs/heads/main')
502 self.assertEqual( 576 self.assertEqual(
503 manifest.ToXml().toxml(), 577 sort_attributes(manifest.ToXml().toxml()),
504 '<?xml version="1.0" ?><manifest>' + 578 '<?xml version="1.0" ?><manifest>'
505 '<remote name="test-remote" fetch="http://localhost"/>' + 579 '<remote fetch="http://localhost" name="test-remote"/>'
506 '<default remote="test-remote" revision="refs/heads/main"/>' + 580 '<default remote="test-remote" revision="refs/heads/main"/>'
507 '<superproject name="superproject"/>' + 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"/>'
508 '</manifest>') 648 '</manifest>')
509 649
510 def test_remote(self): 650 def test_remote(self):
@@ -520,13 +660,14 @@ class SuperProjectElementTests(ManifestParseTestCase):
520 self.assertEqual(manifest.superproject['name'], 'platform/superproject') 660 self.assertEqual(manifest.superproject['name'], 'platform/superproject')
521 self.assertEqual(manifest.superproject['remote'].name, 'superproject-remote') 661 self.assertEqual(manifest.superproject['remote'].name, 'superproject-remote')
522 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/platform/superproject') 662 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/platform/superproject')
663 self.assertEqual(manifest.superproject['revision'], 'refs/heads/main')
523 self.assertEqual( 664 self.assertEqual(
524 manifest.ToXml().toxml(), 665 sort_attributes(manifest.ToXml().toxml()),
525 '<?xml version="1.0" ?><manifest>' + 666 '<?xml version="1.0" ?><manifest>'
526 '<remote name="default-remote" fetch="http://localhost"/>' + 667 '<remote fetch="http://localhost" name="default-remote"/>'
527 '<remote name="superproject-remote" fetch="http://localhost"/>' + 668 '<remote fetch="http://localhost" name="superproject-remote"/>'
528 '<default remote="default-remote" revision="refs/heads/main"/>' + 669 '<default remote="default-remote" revision="refs/heads/main"/>'
529 '<superproject name="platform/superproject" remote="superproject-remote"/>' + 670 '<superproject name="platform/superproject" remote="superproject-remote"/>'
530 '</manifest>') 671 '</manifest>')
531 672
532 def test_defalut_remote(self): 673 def test_defalut_remote(self):
@@ -540,10 +681,165 @@ class SuperProjectElementTests(ManifestParseTestCase):
540""") 681""")
541 self.assertEqual(manifest.superproject['name'], 'superproject') 682 self.assertEqual(manifest.superproject['name'], 'superproject')
542 self.assertEqual(manifest.superproject['remote'].name, 'default-remote') 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)
543 self.assertEqual( 706 self.assertEqual(
544 manifest.ToXml().toxml(), 707 manifest.ToXml().toxml(),
545 '<?xml version="1.0" ?><manifest>' + 708 '<?xml version="1.0" ?><manifest>'
546 '<remote name="default-remote" fetch="http://localhost"/>' + 709 f'<contactinfo bugurl="{bugurl}"/>'
547 '<default remote="default-remote" revision="refs/heads/main"/>' +
548 '<superproject name="superproject"/>' +
549 '</manifest>') 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')