blob: e78d85c38a30b5828f72648548e76d6d69686430 [file] [log] [blame]
Mike Frysinger04122b72019-07-31 23:32:58 -04001# 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
Mike Frysingerd9254592020-02-19 22:36:26 -050017import os
Raman Tenneti080877e2021-03-09 15:19:06 -080018import platform
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -040019import shutil
20import tempfile
Mike Frysinger04122b72019-07-31 23:32:58 -040021import unittest
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -050022import xml.dom.minidom
Mike Frysinger04122b72019-07-31 23:32:58 -040023
24import error
25import manifest_xml
26
27
Mike Frysingera29424e2021-02-25 21:53:49 -050028# Invalid paths that we don't want in the filesystem.
29INVALID_FS_PATHS = (
30 '',
31 '.',
32 '..',
33 '../',
34 './',
Mike Frysinger0458faa2021-03-10 23:35:44 -050035 './/',
Mike Frysingera29424e2021-02-25 21:53:49 -050036 'foo/',
37 './foo',
38 '../foo',
39 'foo/./bar',
40 'foo/../../bar',
41 '/foo',
42 './../foo',
43 '.git/foo',
44 # Check case folding.
45 '.GIT/foo',
46 'blah/.git/foo',
47 '.repo/foo',
48 '.repoconfig',
49 # Block ~ due to 8.3 filenames on Windows filesystems.
50 '~',
51 'foo~',
52 'blah/foo~',
53 # Block Unicode characters that get normalized out by filesystems.
54 u'foo\u200Cbar',
Mike Frysingerf69c7ee2021-04-29 23:15:31 -040055 # Block newlines.
56 'f\n/bar',
57 'f\r/bar',
Mike Frysingera29424e2021-02-25 21:53:49 -050058)
59
60# Make sure platforms that use path separators (e.g. Windows) are also
61# rejected properly.
62if os.path.sep != '/':
63 INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS)
64
65
Mike Frysinger37ac3d62021-02-25 04:54:56 -050066class ManifestParseTestCase(unittest.TestCase):
67 """TestCase for parsing manifests."""
68
69 def setUp(self):
70 self.tempdir = tempfile.mkdtemp(prefix='repo_tests')
71 self.repodir = os.path.join(self.tempdir, '.repo')
72 self.manifest_dir = os.path.join(self.repodir, 'manifests')
73 self.manifest_file = os.path.join(
74 self.repodir, manifest_xml.MANIFEST_FILE_NAME)
75 self.local_manifest_dir = os.path.join(
76 self.repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME)
77 os.mkdir(self.repodir)
78 os.mkdir(self.manifest_dir)
79
80 # The manifest parsing really wants a git repo currently.
81 gitdir = os.path.join(self.repodir, 'manifests.git')
82 os.mkdir(gitdir)
83 with open(os.path.join(gitdir, 'config'), 'w') as fp:
84 fp.write("""[remote "origin"]
85 url = https://localhost:0/manifest
86""")
87
88 def tearDown(self):
89 shutil.rmtree(self.tempdir, ignore_errors=True)
90
91 def getXmlManifest(self, data):
92 """Helper to initialize a manifest for testing."""
93 with open(self.manifest_file, 'w') as fp:
94 fp.write(data)
95 return manifest_xml.XmlManifest(self.repodir, self.manifest_file)
96
Mike Frysingerf69c7ee2021-04-29 23:15:31 -040097 @staticmethod
98 def encodeXmlAttr(attr):
99 """Encode |attr| using XML escape rules."""
100 return attr.replace('\r', '
').replace('\n', '
')
101
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500102
Mike Frysinger04122b72019-07-31 23:32:58 -0400103class ManifestValidateFilePaths(unittest.TestCase):
104 """Check _ValidateFilePaths helper.
105
106 This doesn't access a real filesystem.
107 """
108
109 def check_both(self, *args):
110 manifest_xml.XmlManifest._ValidateFilePaths('copyfile', *args)
111 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
112
113 def test_normal_path(self):
114 """Make sure good paths are accepted."""
115 self.check_both('foo', 'bar')
116 self.check_both('foo/bar', 'bar')
117 self.check_both('foo', 'bar/bar')
118 self.check_both('foo/bar', 'bar/bar')
119
120 def test_symlink_targets(self):
121 """Some extra checks for symlinks."""
122 def check(*args):
123 manifest_xml.XmlManifest._ValidateFilePaths('linkfile', *args)
124
125 # We allow symlinks to end in a slash since we allow them to point to dirs
126 # in general. Technically the slash isn't necessary.
127 check('foo/', 'bar')
Mike Frysingerae625412020-02-10 17:10:03 -0500128 # We allow a single '.' to get a reference to the project itself.
129 check('.', 'bar')
Mike Frysinger04122b72019-07-31 23:32:58 -0400130
131 def test_bad_paths(self):
132 """Make sure bad paths (src & dest) are rejected."""
Mike Frysingera29424e2021-02-25 21:53:49 -0500133 for path in INVALID_FS_PATHS:
Mike Frysinger04122b72019-07-31 23:32:58 -0400134 self.assertRaises(
135 error.ManifestInvalidPathError, self.check_both, path, 'a')
136 self.assertRaises(
137 error.ManifestInvalidPathError, self.check_both, 'a', path)
Mike Frysingerbb8ee7f2020-02-22 05:30:12 -0500138
139
140class ValueTests(unittest.TestCase):
141 """Check utility parsing code."""
142
143 def _get_node(self, text):
144 return xml.dom.minidom.parseString(text).firstChild
145
146 def test_bool_default(self):
147 """Check XmlBool default handling."""
148 node = self._get_node('<node/>')
149 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
150 self.assertIsNone(manifest_xml.XmlBool(node, 'a', None))
151 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
152
153 node = self._get_node('<node a=""/>')
154 self.assertIsNone(manifest_xml.XmlBool(node, 'a'))
155
156 def test_bool_invalid(self):
157 """Check XmlBool invalid handling."""
158 node = self._get_node('<node a="moo"/>')
159 self.assertEqual(123, manifest_xml.XmlBool(node, 'a', 123))
160
161 def test_bool_true(self):
162 """Check XmlBool true values."""
163 for value in ('yes', 'true', '1'):
164 node = self._get_node('<node a="%s"/>' % (value,))
165 self.assertTrue(manifest_xml.XmlBool(node, 'a'))
166
167 def test_bool_false(self):
168 """Check XmlBool false values."""
169 for value in ('no', 'false', '0'):
170 node = self._get_node('<node a="%s"/>' % (value,))
171 self.assertFalse(manifest_xml.XmlBool(node, 'a'))
172
173 def test_int_default(self):
174 """Check XmlInt default handling."""
175 node = self._get_node('<node/>')
176 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
177 self.assertIsNone(manifest_xml.XmlInt(node, 'a', None))
178 self.assertEqual(123, manifest_xml.XmlInt(node, 'a', 123))
179
180 node = self._get_node('<node a=""/>')
181 self.assertIsNone(manifest_xml.XmlInt(node, 'a'))
182
183 def test_int_good(self):
184 """Check XmlInt numeric handling."""
185 for value in (-1, 0, 1, 50000):
186 node = self._get_node('<node a="%s"/>' % (value,))
187 self.assertEqual(value, manifest_xml.XmlInt(node, 'a'))
188
189 def test_int_invalid(self):
190 """Check XmlInt invalid handling."""
191 with self.assertRaises(error.ManifestParseError):
192 node = self._get_node('<node a="xx"/>')
193 manifest_xml.XmlInt(node, 'a')
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400194
195
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500196class XmlManifestTests(ManifestParseTestCase):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400197 """Check manifest processing."""
198
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400199 def test_empty(self):
200 """Parse an 'empty' manifest file."""
201 manifest = self.getXmlManifest(
202 '<?xml version="1.0" encoding="UTF-8"?>'
203 '<manifest></manifest>')
204 self.assertEqual(manifest.remotes, {})
205 self.assertEqual(manifest.projects, [])
206
207 def test_link(self):
208 """Verify Link handling with new names."""
209 manifest = manifest_xml.XmlManifest(self.repodir, self.manifest_file)
210 with open(os.path.join(self.manifest_dir, 'foo.xml'), 'w') as fp:
211 fp.write('<manifest></manifest>')
212 manifest.Link('foo.xml')
213 with open(self.manifest_file) as fp:
214 self.assertIn('<include name="foo.xml" />', fp.read())
215
216 def test_toxml_empty(self):
217 """Verify the ToXml() helper."""
218 manifest = self.getXmlManifest(
219 '<?xml version="1.0" encoding="UTF-8"?>'
220 '<manifest></manifest>')
221 self.assertEqual(manifest.ToXml().toxml(), '<?xml version="1.0" ?><manifest/>')
222
223 def test_todict_empty(self):
224 """Verify the ToDict() helper."""
225 manifest = self.getXmlManifest(
226 '<?xml version="1.0" encoding="UTF-8"?>'
227 '<manifest></manifest>')
228 self.assertEqual(manifest.ToDict(), {})
229
Mike Frysinger51e39d52020-12-04 05:32:06 -0500230 def test_repo_hooks(self):
231 """Check repo-hooks settings."""
232 manifest = self.getXmlManifest("""
233<manifest>
234 <remote name="test-remote" fetch="http://localhost" />
235 <default remote="test-remote" revision="refs/heads/main" />
236 <project name="repohooks" path="src/repohooks"/>
237 <repo-hooks in-project="repohooks" enabled-list="a, b"/>
238</manifest>
239""")
240 self.assertEqual(manifest.repo_hooks_project.name, 'repohooks')
241 self.assertEqual(manifest.repo_hooks_project.enabled_repo_hooks, ['a', 'b'])
242
Raman Tenneti48b2d102021-01-11 12:18:47 -0800243 def test_unknown_tags(self):
244 """Check superproject settings."""
245 manifest = self.getXmlManifest("""
246<manifest>
247 <remote name="test-remote" fetch="http://localhost" />
248 <default remote="test-remote" revision="refs/heads/main" />
249 <superproject name="superproject"/>
250 <iankaz value="unknown (possible) future tags are ignored"/>
251 <x-custom-tag>X tags are always ignored</x-custom-tag>
252</manifest>
253""")
254 self.assertEqual(manifest.superproject['name'], 'superproject')
255 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
256 self.assertEqual(
257 manifest.ToXml().toxml(),
258 '<?xml version="1.0" ?><manifest>' +
259 '<remote name="test-remote" fetch="http://localhost"/>' +
260 '<default remote="test-remote" revision="refs/heads/main"/>' +
261 '<superproject name="superproject"/>' +
262 '</manifest>')
263
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200264
Mike Frysingera29424e2021-02-25 21:53:49 -0500265class IncludeElementTests(ManifestParseTestCase):
266 """Tests for <include>."""
Raman Tennetib5c5a5e2021-02-06 09:44:15 -0800267
Mike Frysingera29424e2021-02-25 21:53:49 -0500268 def test_group_levels(self):
Fredrik de Groot352c93b2020-10-06 12:55:14 +0200269 root_m = os.path.join(self.manifest_dir, 'root.xml')
270 with open(root_m, 'w') as fp:
271 fp.write("""
272<manifest>
273 <remote name="test-remote" fetch="http://localhost" />
274 <default remote="test-remote" revision="refs/heads/main" />
275 <include name="level1.xml" groups="level1-group" />
276 <project name="root-name1" path="root-path1" />
277 <project name="root-name2" path="root-path2" groups="r2g1,r2g2" />
278</manifest>
279""")
280 with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp:
281 fp.write("""
282<manifest>
283 <include name="level2.xml" groups="level2-group" />
284 <project name="level1-name1" path="level1-path1" />
285</manifest>
286""")
287 with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp:
288 fp.write("""
289<manifest>
290 <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" />
291</manifest>
292""")
293 include_m = manifest_xml.XmlManifest(self.repodir, root_m)
294 for proj in include_m.projects:
295 if proj.name == 'root-name1':
296 # Check include group not set on root level proj.
297 self.assertNotIn('level1-group', proj.groups)
298 if proj.name == 'root-name2':
299 # Check root proj group not removed.
300 self.assertIn('r2g1', proj.groups)
301 if proj.name == 'level1-name1':
302 # Check level1 proj has inherited group level 1.
303 self.assertIn('level1-group', proj.groups)
304 if proj.name == 'level2-name1':
305 # Check level2 proj has inherited group levels 1 and 2.
306 self.assertIn('level1-group', proj.groups)
307 self.assertIn('level2-group', proj.groups)
308 # Check level2 proj group not removed.
309 self.assertIn('l2g1', proj.groups)
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500310
Mike Frysinger54133972021-03-01 21:38:08 -0500311 def test_allow_bad_name_from_user(self):
312 """Check handling of bad name attribute from the user's input."""
Mike Frysingera29424e2021-02-25 21:53:49 -0500313 def parse(name):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400314 name = self.encodeXmlAttr(name)
Mike Frysingera29424e2021-02-25 21:53:49 -0500315 manifest = self.getXmlManifest(f"""
316<manifest>
317 <remote name="default-remote" fetch="http://localhost" />
318 <default remote="default-remote" revision="refs/heads/main" />
319 <include name="{name}" />
320</manifest>
321""")
322 # Force the manifest to be parsed.
323 manifest.ToXml()
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500324
Mike Frysinger54133972021-03-01 21:38:08 -0500325 # Setup target of the include.
326 target = os.path.join(self.tempdir, 'target.xml')
327 with open(target, 'w') as fp:
328 fp.write('<manifest></manifest>')
329
330 # Include with absolute path.
331 parse(os.path.abspath(target))
332
333 # Include with relative path.
334 parse(os.path.relpath(target, self.manifest_dir))
335
336 def test_bad_name_checks(self):
337 """Check handling of bad name attribute."""
338 def parse(name):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400339 name = self.encodeXmlAttr(name)
Mike Frysinger54133972021-03-01 21:38:08 -0500340 # Setup target of the include.
341 with open(os.path.join(self.manifest_dir, 'target.xml'), 'w') as fp:
342 fp.write(f'<manifest><include name="{name}"/></manifest>')
343
344 manifest = self.getXmlManifest("""
345<manifest>
346 <remote name="default-remote" fetch="http://localhost" />
347 <default remote="default-remote" revision="refs/heads/main" />
348 <include name="target.xml" />
349</manifest>
350""")
351 # Force the manifest to be parsed.
352 manifest.ToXml()
353
Mike Frysingera29424e2021-02-25 21:53:49 -0500354 # Handle empty name explicitly because a different codepath rejects it.
355 with self.assertRaises(error.ManifestParseError):
356 parse('')
357
358 for path in INVALID_FS_PATHS:
359 if not path:
360 continue
361
362 with self.assertRaises(error.ManifestInvalidPathError):
363 parse(path)
364
365
366class ProjectElementTests(ManifestParseTestCase):
367 """Tests for <project>."""
368
369 def test_group(self):
370 """Check project group settings."""
371 manifest = self.getXmlManifest("""
372<manifest>
373 <remote name="test-remote" fetch="http://localhost" />
374 <default remote="test-remote" revision="refs/heads/main" />
375 <project name="test-name" path="test-path"/>
376 <project name="extras" path="path" groups="g1,g2,g1"/>
377</manifest>
378""")
379 self.assertEqual(len(manifest.projects), 2)
380 # Ordering isn't guaranteed.
381 result = {
382 manifest.projects[0].name: manifest.projects[0].groups,
383 manifest.projects[1].name: manifest.projects[1].groups,
384 }
385 project = manifest.projects[0]
386 self.assertCountEqual(
387 result['test-name'],
388 ['name:test-name', 'all', 'path:test-path'])
389 self.assertCountEqual(
390 result['extras'],
391 ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path'])
Raman Tenneti080877e2021-03-09 15:19:06 -0800392 groupstr = 'default,platform-' + platform.system().lower()
393 self.assertEqual(groupstr, manifest.GetGroupsStr())
394 groupstr = 'g1,g2,g1'
395 manifest.manifestProject.config.SetString('manifest.groups', groupstr)
396 self.assertEqual(groupstr, manifest.GetGroupsStr())
Mike Frysingera29424e2021-02-25 21:53:49 -0500397
398 def test_set_revision_id(self):
399 """Check setting of project's revisionId."""
400 manifest = self.getXmlManifest("""
401<manifest>
402 <remote name="default-remote" fetch="http://localhost" />
403 <default remote="default-remote" revision="refs/heads/main" />
404 <project name="test-name"/>
405</manifest>
406""")
407 self.assertEqual(len(manifest.projects), 1)
408 project = manifest.projects[0]
409 project.SetRevisionId('ABCDEF')
410 self.assertEqual(
411 manifest.ToXml().toxml(),
412 '<?xml version="1.0" ?><manifest>' +
413 '<remote name="default-remote" fetch="http://localhost"/>' +
414 '<default remote="default-remote" revision="refs/heads/main"/>' +
415 '<project name="test-name" revision="ABCDEF"/>' +
416 '</manifest>')
417
418 def test_trailing_slash(self):
419 """Check handling of trailing slashes in attributes."""
420 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400421 name = self.encodeXmlAttr(name)
422 path = self.encodeXmlAttr(path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500423 return self.getXmlManifest(f"""
424<manifest>
425 <remote name="default-remote" fetch="http://localhost" />
426 <default remote="default-remote" revision="refs/heads/main" />
427 <project name="{name}" path="{path}" />
428</manifest>
429""")
430
431 manifest = parse('a/path/', 'foo')
432 self.assertEqual(manifest.projects[0].gitdir,
433 os.path.join(self.tempdir, '.repo/projects/foo.git'))
434 self.assertEqual(manifest.projects[0].objdir,
435 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
436
437 manifest = parse('a/path', 'foo/')
438 self.assertEqual(manifest.projects[0].gitdir,
439 os.path.join(self.tempdir, '.repo/projects/foo.git'))
440 self.assertEqual(manifest.projects[0].objdir,
441 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
442
Mike Frysinger0458faa2021-03-10 23:35:44 -0500443 manifest = parse('a/path', 'foo//////')
444 self.assertEqual(manifest.projects[0].gitdir,
445 os.path.join(self.tempdir, '.repo/projects/foo.git'))
446 self.assertEqual(manifest.projects[0].objdir,
447 os.path.join(self.tempdir, '.repo/project-objects/a/path.git'))
448
449 def test_toplevel_path(self):
450 """Check handling of path=. specially."""
451 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400452 name = self.encodeXmlAttr(name)
453 path = self.encodeXmlAttr(path)
Mike Frysinger0458faa2021-03-10 23:35:44 -0500454 return self.getXmlManifest(f"""
455<manifest>
456 <remote name="default-remote" fetch="http://localhost" />
457 <default remote="default-remote" revision="refs/heads/main" />
458 <project name="{name}" path="{path}" />
459</manifest>
460""")
461
462 for path in ('.', './', './/', './//'):
463 manifest = parse('server/path', path)
464 self.assertEqual(manifest.projects[0].gitdir,
465 os.path.join(self.tempdir, '.repo/projects/..git'))
466
Mike Frysingera29424e2021-02-25 21:53:49 -0500467 def test_bad_path_name_checks(self):
468 """Check handling of bad path & name attributes."""
469 def parse(name, path):
Mike Frysingerf69c7ee2021-04-29 23:15:31 -0400470 name = self.encodeXmlAttr(name)
471 path = self.encodeXmlAttr(path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500472 manifest = self.getXmlManifest(f"""
473<manifest>
474 <remote name="default-remote" fetch="http://localhost" />
475 <default remote="default-remote" revision="refs/heads/main" />
476 <project name="{name}" path="{path}" />
477</manifest>
478""")
479 # Force the manifest to be parsed.
480 manifest.ToXml()
481
482 # Verify the parser is valid by default to avoid buggy tests below.
483 parse('ok', 'ok')
484
485 # Handle empty name explicitly because a different codepath rejects it.
486 # Empty path is OK because it defaults to the name field.
487 with self.assertRaises(error.ManifestParseError):
488 parse('', 'ok')
489
490 for path in INVALID_FS_PATHS:
491 if not path or path.endswith('/'):
492 continue
493
494 with self.assertRaises(error.ManifestInvalidPathError):
495 parse(path, 'ok')
Mike Frysinger0458faa2021-03-10 23:35:44 -0500496
497 # We have a dedicated test for path=".".
498 if path not in {'.'}:
499 with self.assertRaises(error.ManifestInvalidPathError):
500 parse('ok', path)
Mike Frysingera29424e2021-02-25 21:53:49 -0500501
502
503class SuperProjectElementTests(ManifestParseTestCase):
Mike Frysinger37ac3d62021-02-25 04:54:56 -0500504 """Tests for <superproject>."""
505
506 def test_superproject(self):
507 """Check superproject settings."""
508 manifest = self.getXmlManifest("""
509<manifest>
510 <remote name="test-remote" fetch="http://localhost" />
511 <default remote="test-remote" revision="refs/heads/main" />
512 <superproject name="superproject"/>
513</manifest>
514""")
515 self.assertEqual(manifest.superproject['name'], 'superproject')
516 self.assertEqual(manifest.superproject['remote'].name, 'test-remote')
517 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/superproject')
518 self.assertEqual(
519 manifest.ToXml().toxml(),
520 '<?xml version="1.0" ?><manifest>' +
521 '<remote name="test-remote" fetch="http://localhost"/>' +
522 '<default remote="test-remote" revision="refs/heads/main"/>' +
523 '<superproject name="superproject"/>' +
524 '</manifest>')
525
526 def test_remote(self):
527 """Check superproject settings with a remote."""
528 manifest = self.getXmlManifest("""
529<manifest>
530 <remote name="default-remote" fetch="http://localhost" />
531 <remote name="superproject-remote" fetch="http://localhost" />
532 <default remote="default-remote" revision="refs/heads/main" />
533 <superproject name="platform/superproject" remote="superproject-remote"/>
534</manifest>
535""")
536 self.assertEqual(manifest.superproject['name'], 'platform/superproject')
537 self.assertEqual(manifest.superproject['remote'].name, 'superproject-remote')
538 self.assertEqual(manifest.superproject['remote'].url, 'http://localhost/platform/superproject')
539 self.assertEqual(
540 manifest.ToXml().toxml(),
541 '<?xml version="1.0" ?><manifest>' +
542 '<remote name="default-remote" fetch="http://localhost"/>' +
543 '<remote name="superproject-remote" fetch="http://localhost"/>' +
544 '<default remote="default-remote" revision="refs/heads/main"/>' +
545 '<superproject name="platform/superproject" remote="superproject-remote"/>' +
546 '</manifest>')
547
548 def test_defalut_remote(self):
549 """Check superproject settings with a default remote."""
550 manifest = self.getXmlManifest("""
551<manifest>
552 <remote name="default-remote" fetch="http://localhost" />
553 <default remote="default-remote" revision="refs/heads/main" />
554 <superproject name="superproject" remote="default-remote"/>
555</manifest>
556""")
557 self.assertEqual(manifest.superproject['name'], 'superproject')
558 self.assertEqual(manifest.superproject['remote'].name, 'default-remote')
559 self.assertEqual(
560 manifest.ToXml().toxml(),
561 '<?xml version="1.0" ?><manifest>' +
562 '<remote name="default-remote" fetch="http://localhost"/>' +
563 '<default remote="default-remote" revision="refs/heads/main"/>' +
564 '<superproject name="superproject"/>' +
565 '</manifest>')