blob: 40385ccebe2d009c275d1016ee3ea6df28078788 [file] [log] [blame]
Mike Frysinger04122b72019-07-31 23:32:58 -04001# -*- coding:utf-8 -*-
2#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Unittests for the manifest_xml.py module."""
18
19from __future__ import print_function
20
Mike Frysingerd9254592020-02-19 22:36:26 -050021import os
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -040022import shutil
23import tempfile
Mike Frysinger04122b72019-07-31 23:32:58 -040024import unittest
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -050025import xml.dom.minidom
Mike Frysinger04122b72019-07-31 23:32:58 -040026
27import error
28import manifest_xml
29
30
31class ManifestValidateFilePaths(unittest.TestCase):
32 """Check _ValidateFilePaths helper.
33
34 This doesn't access a real filesystem.
35 """
36
37 def check_both(self, *args):
38 manifest_xml.XmlManifest._ValidateFilePaths('copyfile', *args)
39 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
40
41 def test_normal_path(self):
42 """Make sure good paths are accepted."""
43 self.check_both('foo', 'bar')
44 self.check_both('foo/bar', 'bar')
45 self.check_both('foo', 'bar/bar')
46 self.check_both('foo/bar', 'bar/bar')
47
48 def test_symlink_targets(self):
49 """Some extra checks for symlinks."""
50 def check(*args):
51 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
52
53 # We allow symlinks to end in a slash since we allow them to point to dirs
54 # in general. Technically the slash isn't necessary.
55 check('foo/', 'bar')
Mike Frysingerae625412020-02-10 17:10:03 -050056 # We allow a single '.' to get a reference to the project itself.
57 check('.', 'bar')
Mike Frysinger04122b72019-07-31 23:32:58 -040058
59 def test_bad_paths(self):
60 """Make sure bad paths (src & dest) are rejected."""
61 PATHS = (
62 '..',
63 '../',
64 './',
65 'foo/',
66 './foo',
67 '../foo',
68 'foo/./bar',
69 'foo/../../bar',
70 '/foo',
71 './../foo',
72 '.git/foo',
73 # Check case folding.
74 '.GIT/foo',
75 'blah/.git/foo',
76 '.repo/foo',
77 '.repoconfig',
78 # Block ~ due to 8.3 filenames on Windows filesystems.
79 '~',
80 'foo~',
81 'blah/foo~',
82 # Block Unicode characters that get normalized out by filesystems.
83 u'foo\u200Cbar',
84 )
Mike Frysingerd9254592020-02-19 22:36:26 -050085 # Make sure platforms that use path separators (e.g. Windows) are also
86 # rejected properly.
87 if os.path.sep != '/':
88 PATHS += tuple(x.replace('/', os.path.sep) for x in PATHS)
89
Mike Frysinger04122b72019-07-31 23:32:58 -040090 for path in PATHS:
91 self.assertRaises(
92 error.ManifestInvalidPathError, self.check_both, path, 'a')
93 self.assertRaises(
94 error.ManifestInvalidPathError, self.check_both, 'a', path)
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -050095
96
97class ValueTests(unittest.TestCase):
98 """Check utility parsing code."""
99
100 def _get_node(self, text):
101 return xml.dom.minidom.parseString(text).firstChild
102
103 def test_bool_default(self):
104 """Check XmlBool default handling."""
105 node = self._get_node('<node/>')
106 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
107 self.assertIsNone(manifest_xml.XmlBool(node, 'a', None))
108 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
109
110 node = self._get_node('<node a=""/>')
111 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
112
113 def test_bool_invalid(self):
114 """Check XmlBool invalid handling."""
115 node = self._get_node('<node a="moo"/>')
116 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
117
118 def test_bool_true(self):
119 """Check XmlBool true values."""
120 for value in ('yes', 'true', '1'):
121 node = self._get_node('<node a="%s"/>' % (value,))
122 self.assertTrue(manifest_xml.XmlBool(node, 'a'))
123
124 def test_bool_false(self):
125 """Check XmlBool false values."""
126 for value in ('no', 'false', '0'):
127 node = self._get_node('<node a="%s"/>' % (value,))
128 self.assertFalse(manifest_xml.XmlBool(node, 'a'))
129
130 def test_int_default(self):
131 """Check XmlInt default handling."""
132 node = self._get_node('<node/>')
133 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
134 self.assertIsNone(manifest_xml.XmlInt(node, 'a', None))
135 self.assertEqual(123, manifest_xml.XmlInt(node, 'a', 123))
136
137 node = self._get_node('<node a=""/>')
138 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
139
140 def test_int_good(self):
141 """Check XmlInt numeric handling."""
142 for value in (-1, 0, 1, 50000):
143 node = self._get_node('<node a="%s"/>' % (value,))
144 self.assertEqual(value, manifest_xml.XmlInt(node, 'a'))
145
146 def test_int_invalid(self):
147 """Check XmlInt invalid handling."""
148 with self.assertRaises(error.ManifestParseError):
149 node = self._get_node('<node a="xx"/>')
150 manifest_xml.XmlInt(node, 'a')
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400151
152
153class XmlManifestTests(unittest.TestCase):
154 """Check manifest processing."""
155
156 def setUp(self):
157 self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
158 self.repodir = os.path.join(self.tempdir, '.repo')
159 self.manifest_dir = os.path.join(self.repodir, 'manifests')
160 self.manifest_file = os.path.join(
161 self.repodir, manifest_xml.MANIFEST_FILE_NAME)
162 self.local_manifest_dir = os.path.join(
163 self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME)
164 os.mkdir(self.repodir)
165 os.mkdir(self.manifest_dir)
166
167 # The manifest parsing really wants a git repo currently.
168 gitdir = os.path.join(self.repodir, 'manifests.git')
169 os.mkdir(gitdir)
170 with open(os.path.join(gitdir, 'config'), 'w') as fp:
171 fp.write("""[remote "origin"]
172 url = https://localhost:0/manifest
173""")
174
175 def tearDown(self):
176 shutil.rmtree(self.tempdir, ignore_errors=True)
177
178 def getXmlManifest(self, data):
179 """Helper to initialize a manifest for testing."""
180 with open(self.manifest_file, 'w') as fp:
181 fp.write(data)
182 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
183
184 def test_empty(self):
185 """Parse an 'empty' manifest file."""
186 manifest = self.getXmlManifest(
187 '<?xml version="1.0" encoding="UTF-8"?>'
188 '<manifest></manifest>')
189 self.assertEqual(manifest.remotes, {})
190 self.assertEqual(manifest.projects, [])
191
192 def test_link(self):
193 """Verify Link handling with new names."""
194 manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file)
195 with open(os.path.join(self.manifest_dir, 'foo.xml'), 'w') as fp:
196 fp.write('<manifest></manifest>')
197 manifest.Link('foo.xml')
198 with open(self.manifest_file) as fp:
199 self.assertIn('<include name="foo.xml" />', fp.read())
200
201 def test_toxml_empty(self):
202 """Verify the ToXml() helper."""
203 manifest = self.getXmlManifest(
204 '<?xml version="1.0" encoding="UTF-8"?>'
205 '<manifest></manifest>')
206 self.assertEqual(manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>')
207
208 def test_todict_empty(self):
209 """Verify the ToDict() helper."""
210 manifest = self.getXmlManifest(
211 '<?xml version="1.0" encoding="UTF-8"?>'
212 '<manifest></manifest>')
213 self.assertEqual(manifest.ToDict(), {})
214
215 def test_project_group(self):
216 """Check project group settings."""
217 manifest = self.getXmlManifest("""
218<manifest>
219 <remote name="test-remote" fetch="http://localhost" />
220 <default remote="test-remote" revision="refs/heads/main" />
221 <project name="test-name" path="test-path"/>
222 <project name="extras" path="path" groups="g1,g2,g1"/>
223</manifest>
224""")
225 self.assertEqual(len(manifest.projects), 2)
226 # Ordering isn't guaranteed.
227 result = {
228 manifest.projects[0].name: manifest.projects[0].groups,
229 manifest.projects[1].name: manifest.projects[1].groups,
230 }
231 project = manifest.projects[0]
232 self.assertCountEqual(
233 result['test-name'],
234 ['name:test-name', 'all', 'path:test-path'])
235 self.assertCountEqual(
236 result['extras'],
237 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path'])