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') | ||
