diff options
-rw-r--r-- | manifest_xml.py | 125 | ||||
-rw-r--r-- | tests/test_manifest_xml.py | 57 |
2 files changed, 127 insertions, 55 deletions
diff --git a/manifest_xml.py b/manifest_xml.py index fe09f498..edcbadae 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -57,6 +57,60 @@ urllib.parse.uses_netloc.extend([ | |||
57 | 'rpc']) | 57 | 'rpc']) |
58 | 58 | ||
59 | 59 | ||
60 | def XmlBool(node, attr, default=None): | ||
61 | """Determine boolean value of |node|'s |attr|. | ||
62 | |||
63 | Invalid values will issue a non-fatal warning. | ||
64 | |||
65 | Args: | ||
66 | node: XML node whose attributes we access. | ||
67 | attr: The attribute to access. | ||
68 | default: If the attribute is not set (value is empty), then use this. | ||
69 | |||
70 | Returns: | ||
71 | True if the attribute is a valid string representing true. | ||
72 | False if the attribute is a valid string representing false. | ||
73 | |default| otherwise. | ||
74 | """ | ||
75 | value = node.getAttribute(attr) | ||
76 | s = value.lower() | ||
77 | if s == '': | ||
78 | return default | ||
79 | elif s in {'yes', 'true', '1'}: | ||
80 | return True | ||
81 | elif s in {'no', 'false', '0'}: | ||
82 | return False | ||
83 | else: | ||
84 | print('warning: manifest: %s="%s": ignoring invalid XML boolean' % | ||
85 | (attr, value), file=sys.stderr) | ||
86 | return default | ||
87 | |||
88 | |||
89 | def XmlInt(node, attr, default=None): | ||
90 | """Determine integer value of |node|'s |attr|. | ||
91 | |||
92 | Args: | ||
93 | node: XML node whose attributes we access. | ||
94 | attr: The attribute to access. | ||
95 | default: If the attribute is not set (value is empty), then use this. | ||
96 | |||
97 | Returns: | ||
98 | The number if the attribute is a valid number. | ||
99 | |||
100 | Raises: | ||
101 | ManifestParseError: The number is invalid. | ||
102 | """ | ||
103 | value = node.getAttribute(attr) | ||
104 | if not value: | ||
105 | return default | ||
106 | |||
107 | try: | ||
108 | return int(value) | ||
109 | except ValueError: | ||
110 | raise ManifestParseError('manifest: invalid %s="%s" integer' % | ||
111 | (attr, value)) | ||
112 | |||
113 | |||
60 | class _Default(object): | 114 | class _Default(object): |
61 | """Project defaults within the manifest.""" | 115 | """Project defaults within the manifest.""" |
62 | 116 | ||
@@ -757,29 +811,14 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
757 | d.destBranchExpr = node.getAttribute('dest-branch') or None | 811 | d.destBranchExpr = node.getAttribute('dest-branch') or None |
758 | d.upstreamExpr = node.getAttribute('upstream') or None | 812 | d.upstreamExpr = node.getAttribute('upstream') or None |
759 | 813 | ||
760 | sync_j = node.getAttribute('sync-j') | 814 | d.sync_j = XmlInt(node, 'sync-j', 1) |
761 | if sync_j == '' or sync_j is None: | 815 | if d.sync_j <= 0: |
762 | d.sync_j = 1 | 816 | raise ManifestParseError('%s: sync-j must be greater than 0, not "%s"' % |
763 | else: | 817 | (self.manifestFile, d.sync_j)) |
764 | d.sync_j = int(sync_j) | ||
765 | |||
766 | sync_c = node.getAttribute('sync-c') | ||
767 | if not sync_c: | ||
768 | d.sync_c = False | ||
769 | else: | ||
770 | d.sync_c = sync_c.lower() in ("yes", "true", "1") | ||
771 | |||
772 | sync_s = node.getAttribute('sync-s') | ||
773 | if not sync_s: | ||
774 | d.sync_s = False | ||
775 | else: | ||
776 | d.sync_s = sync_s.lower() in ("yes", "true", "1") | ||
777 | 818 | ||
778 | sync_tags = node.getAttribute('sync-tags') | 819 | d.sync_c = XmlBool(node, 'sync-c', False) |
779 | if not sync_tags: | 820 | d.sync_s = XmlBool(node, 'sync-s', False) |
780 | d.sync_tags = True | 821 | d.sync_tags = XmlBool(node, 'sync-tags', True) |
781 | else: | ||
782 | d.sync_tags = sync_tags.lower() in ("yes", "true", "1") | ||
783 | return d | 822 | return d |
784 | 823 | ||
785 | def _ParseNotice(self, node): | 824 | def _ParseNotice(self, node): |
@@ -856,39 +895,15 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
856 | raise ManifestParseError("project %s path cannot be absolute in %s" % | 895 | raise ManifestParseError("project %s path cannot be absolute in %s" % |
857 | (name, self.manifestFile)) | 896 | (name, self.manifestFile)) |
858 | 897 | ||
859 | rebase = node.getAttribute('rebase') | 898 | rebase = XmlBool(node, 'rebase', True) |
860 | if not rebase: | 899 | sync_c = XmlBool(node, 'sync-c', False) |
861 | rebase = True | 900 | sync_s = XmlBool(node, 'sync-s', self._default.sync_s) |
862 | else: | 901 | sync_tags = XmlBool(node, 'sync-tags', self._default.sync_tags) |
863 | rebase = rebase.lower() in ("yes", "true", "1") | ||
864 | |||
865 | sync_c = node.getAttribute('sync-c') | ||
866 | if not sync_c: | ||
867 | sync_c = False | ||
868 | else: | ||
869 | sync_c = sync_c.lower() in ("yes", "true", "1") | ||
870 | |||
871 | sync_s = node.getAttribute('sync-s') | ||
872 | if not sync_s: | ||
873 | sync_s = self._default.sync_s | ||
874 | else: | ||
875 | sync_s = sync_s.lower() in ("yes", "true", "1") | ||
876 | |||
877 | sync_tags = node.getAttribute('sync-tags') | ||
878 | if not sync_tags: | ||
879 | sync_tags = self._default.sync_tags | ||
880 | else: | ||
881 | sync_tags = sync_tags.lower() in ("yes", "true", "1") | ||
882 | 902 | ||
883 | clone_depth = node.getAttribute('clone-depth') | 903 | clone_depth = XmlInt(node, 'clone-depth') |
884 | if clone_depth: | 904 | if clone_depth is not None and clone_depth <= 0: |
885 | try: | 905 | raise ManifestParseError('%s: clone-depth must be greater than 0, not "%s"' % |
886 | clone_depth = int(clone_depth) | 906 | (self.manifestFile, clone_depth)) |
887 | if clone_depth <= 0: | ||
888 | raise ValueError() | ||
889 | except ValueError: | ||
890 | raise ManifestParseError('invalid clone-depth %s in %s' % | ||
891 | (clone_depth, self.manifestFile)) | ||
892 | 907 | ||
893 | dest_branch = node.getAttribute('dest-branch') or self._default.destBranchExpr | 908 | dest_branch = node.getAttribute('dest-branch') or self._default.destBranchExpr |
894 | 909 | ||
@@ -911,7 +926,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
911 | groups.extend(set(default_groups).difference(groups)) | 926 | groups.extend(set(default_groups).difference(groups)) |
912 | 927 | ||
913 | if self.IsMirror and node.hasAttribute('force-path'): | 928 | if self.IsMirror and node.hasAttribute('force-path'): |
914 | if node.getAttribute('force-path').lower() in ("yes", "true", "1"): | 929 | if XmlBool(node, 'force-path', False): |
915 | gitdir = os.path.join(self.topdir, '%s.git' % path) | 930 | gitdir = os.path.join(self.topdir, '%s.git' % path) |
916 | 931 | ||
917 | project = Project(manifest=self, | 932 | project = Project(manifest=self, |
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 1cb72971..aa6cb7df 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py | |||
@@ -20,6 +20,7 @@ from __future__ import print_function | |||
20 | 20 | ||
21 | import os | 21 | import os |
22 | import unittest | 22 | import unittest |
23 | import xml.dom.minidom | ||
23 | 24 | ||
24 | import error | 25 | import error |
25 | import manifest_xml | 26 | import manifest_xml |
@@ -89,3 +90,59 @@ class ManifestValidateFilePaths(unittest.TestCase): | |||
89 | error.ManifestInvalidPathError, self.check_both, path, 'a') | 90 | error.ManifestInvalidPathError, self.check_both, path, 'a') |
90 | self.assertRaises( | 91 | self.assertRaises( |
91 | error.ManifestInvalidPathError, self.check_both, 'a', path) | 92 | error.ManifestInvalidPathError, self.check_both, 'a', path) |
93 | |||
94 | |||
95 | class ValueTests(unittest.TestCase): | ||
96 | """Check utility parsing code.""" | ||
97 | |||
98 | def _get_node(self, text): | ||
99 | return xml.dom.minidom.parseString(text).firstChild | ||
100 | |||
101 | def test_bool_default(self): | ||
102 | """Check XmlBool default handling.""" | ||
103 | node = self._get_node('<node/>') | ||
104 | self.assertIsNone(manifest_xml.XmlBool(node, 'a')) | ||
105 | self.assertIsNone(manifest_xml.XmlBool(node, 'a', None)) | ||
106 | self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123)) | ||
107 | |||
108 | node = self._get_node('<node a=""/>') | ||
109 | self.assertIsNone(manifest_xml.XmlBool(node, 'a')) | ||
110 | |||
111 | def test_bool_invalid(self): | ||
112 | """Check XmlBool invalid handling.""" | ||
113 | node = self._get_node('<node a="moo"/>') | ||
114 | self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123)) | ||
115 | |||
116 | def test_bool_true(self): | ||
117 | """Check XmlBool true values.""" | ||
118 | for value in ('yes', 'true', '1'): | ||
119 | node = self._get_node('<node a="%s"/>' % (value,)) | ||
120 | self.assertTrue(manifest_xml.XmlBool(node, 'a')) | ||
121 | |||
122 | def test_bool_false(self): | ||
123 | """Check XmlBool false values.""" | ||
124 | for value in ('no', 'false', '0'): | ||
125 | node = self._get_node('<node a="%s"/>' % (value,)) | ||
126 | self.assertFalse(manifest_xml.XmlBool(node, 'a')) | ||
127 | |||
128 | def test_int_default(self): | ||
129 | """Check XmlInt default handling.""" | ||
130 | node = self._get_node('<node/>') | ||
131 | self.assertIsNone(manifest_xml.XmlInt(node, 'a')) | ||
132 | self.assertIsNone(manifest_xml.XmlInt(node, 'a', None)) | ||
133 | self.assertEqual(123, manifest_xml.XmlInt(node, 'a', 123)) | ||
134 | |||
135 | node = self._get_node('<node a=""/>') | ||
136 | self.assertIsNone(manifest_xml.XmlInt(node, 'a')) | ||
137 | |||
138 | def test_int_good(self): | ||
139 | """Check XmlInt numeric handling.""" | ||
140 | for value in (-1, 0, 1, 50000): | ||
141 | node = self._get_node('<node a="%s"/>' % (value,)) | ||
142 | self.assertEqual(value, manifest_xml.XmlInt(node, 'a')) | ||
143 | |||
144 | def test_int_invalid(self): | ||
145 | """Check XmlInt invalid handling.""" | ||
146 | with self.assertRaises(error.ManifestParseError): | ||
147 | node = self._get_node('<node a="xx"/>') | ||
148 | manifest_xml.XmlInt(node, 'a') | ||