diff options
author | Mike Frysinger <vapier@google.com> | 2020-09-06 14:53:18 -0400 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2020-11-18 19:10:57 +0000 |
commit | 8c1e9cbef161f2ff12dadbacf26affd23876fde9 (patch) | |
tree | fcfdc404568a2d8dbc9edd9ec99ecdd758f8c424 | |
parent | a488af5ea5c53dd7cf2c90a751e77cc4ba87b7c3 (diff) | |
download | git-repo-8c1e9cbef161f2ff12dadbacf26affd23876fde9.tar.gz |
manifest_xml: refactor manifest parsing from client management
We conflate the manifest & parsing logic with the management of the
repo client checkout in a single class. This makes testing just one
part (the manifest parsing) hard as it requires a full checkout too.
Start splitting the two apart into separate classes to make it easy
to reason about & test.
Change-Id: Iaf897c93db9c724baba6044bfe7a589c024523b2
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/288682
Reviewed-by: Michael Mortensen <mmortensen@google.com>
Tested-by: Mike Frysinger <vapier@google.com>
-rwxr-xr-x | main.py | 13 | ||||
-rw-r--r-- | manifest_xml.py | 80 | ||||
-rw-r--r-- | project.py | 6 | ||||
-rw-r--r-- | subcmds/diffmanifests.py | 8 | ||||
-rw-r--r-- | subcmds/help.py | 4 | ||||
-rw-r--r-- | subcmds/info.py | 2 | ||||
-rw-r--r-- | subcmds/init.py | 4 | ||||
-rw-r--r-- | subcmds/status.py | 2 | ||||
-rw-r--r-- | tests/test_manifest_xml.py | 89 |
9 files changed, 164 insertions, 44 deletions
@@ -63,7 +63,7 @@ from error import NoManifestException | |||
63 | from error import NoSuchProjectError | 63 | from error import NoSuchProjectError |
64 | from error import RepoChangedException | 64 | from error import RepoChangedException |
65 | import gitc_utils | 65 | import gitc_utils |
66 | from manifest_xml import GitcManifest, XmlManifest | 66 | from manifest_xml import GitcClient, RepoClient |
67 | from pager import RunPager, TerminatePager | 67 | from pager import RunPager, TerminatePager |
68 | from wrapper import WrapperPath, Wrapper | 68 | from wrapper import WrapperPath, Wrapper |
69 | 69 | ||
@@ -212,14 +212,15 @@ class _Repo(object): | |||
212 | return 1 | 212 | return 1 |
213 | 213 | ||
214 | cmd.repodir = self.repodir | 214 | cmd.repodir = self.repodir |
215 | cmd.manifest = XmlManifest(cmd.repodir) | 215 | cmd.client = RepoClient(cmd.repodir) |
216 | cmd.manifest = cmd.client.manifest | ||
216 | cmd.gitc_manifest = None | 217 | cmd.gitc_manifest = None |
217 | gitc_client_name = gitc_utils.parse_clientdir(os.getcwd()) | 218 | gitc_client_name = gitc_utils.parse_clientdir(os.getcwd()) |
218 | if gitc_client_name: | 219 | if gitc_client_name: |
219 | cmd.gitc_manifest = GitcManifest(cmd.repodir, gitc_client_name) | 220 | cmd.gitc_manifest = GitcClient(cmd.repodir, gitc_client_name) |
220 | cmd.manifest.isGitcClient = True | 221 | cmd.client.isGitcClient = True |
221 | 222 | ||
222 | Editor.globalConfig = cmd.manifest.globalConfig | 223 | Editor.globalConfig = cmd.client.globalConfig |
223 | 224 | ||
224 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: | 225 | if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: |
225 | print("fatal: '%s' requires a working directory" % name, | 226 | print("fatal: '%s' requires a working directory" % name, |
@@ -247,7 +248,7 @@ class _Repo(object): | |||
247 | return 1 | 248 | return 1 |
248 | 249 | ||
249 | if gopts.pager is not False and not isinstance(cmd, InteractiveCommand): | 250 | if gopts.pager is not False and not isinstance(cmd, InteractiveCommand): |
250 | config = cmd.manifest.globalConfig | 251 | config = cmd.client.globalConfig |
251 | if gopts.pager: | 252 | if gopts.pager: |
252 | use_pager = True | 253 | use_pager = True |
253 | else: | 254 | else: |
diff --git a/manifest_xml.py b/manifest_xml.py index e1ef330f..95c67d73 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -187,12 +187,24 @@ class _XmlRemote(object): | |||
187 | class XmlManifest(object): | 187 | class XmlManifest(object): |
188 | """manages the repo configuration file""" | 188 | """manages the repo configuration file""" |
189 | 189 | ||
190 | def __init__(self, repodir): | 190 | def __init__(self, repodir, manifest_file, local_manifests=None): |
191 | """Initialize. | ||
192 | |||
193 | Args: | ||
194 | repodir: Path to the .repo/ dir for holding all internal checkout state. | ||
195 | It must be in the top directory of the repo client checkout. | ||
196 | manifest_file: Full path to the manifest file to parse. This will usually | ||
197 | be |repodir|/|MANIFEST_FILE_NAME|. | ||
198 | local_manifests: Full path to the directory of local override manifests. | ||
199 | This will usually be |repodir|/|LOCAL_MANIFESTS_DIR_NAME|. | ||
200 | """ | ||
201 | # TODO(vapier): Move this out of this class. | ||
202 | self.globalConfig = GitConfig.ForUser() | ||
203 | |||
191 | self.repodir = os.path.abspath(repodir) | 204 | self.repodir = os.path.abspath(repodir) |
192 | self.topdir = os.path.dirname(self.repodir) | 205 | self.topdir = os.path.dirname(self.repodir) |
193 | self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) | 206 | self.manifestFile = manifest_file |
194 | self.globalConfig = GitConfig.ForUser() | 207 | self.local_manifests = local_manifests |
195 | self.isGitcClient = False | ||
196 | self._load_local_manifests = True | 208 | self._load_local_manifests = True |
197 | 209 | ||
198 | self.repoProject = MetaProject(self, 'repo', | 210 | self.repoProject = MetaProject(self, 'repo', |
@@ -602,20 +614,11 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
602 | nodes.append(self._ParseManifestXml(self.manifestFile, | 614 | nodes.append(self._ParseManifestXml(self.manifestFile, |
603 | self.manifestProject.worktree)) | 615 | self.manifestProject.worktree)) |
604 | 616 | ||
605 | if self._load_local_manifests: | 617 | if self._load_local_manifests and self.local_manifests: |
606 | if os.path.exists(os.path.join(self.repodir, LOCAL_MANIFEST_NAME)): | ||
607 | print('error: %s is not supported; put local manifests in `%s`' | ||
608 | 'instead' % (LOCAL_MANIFEST_NAME, | ||
609 | os.path.join(self.repodir, LOCAL_MANIFESTS_DIR_NAME)), | ||
610 | file=sys.stderr) | ||
611 | sys.exit(1) | ||
612 | |||
613 | local_dir = os.path.abspath(os.path.join(self.repodir, | ||
614 | LOCAL_MANIFESTS_DIR_NAME)) | ||
615 | try: | 618 | try: |
616 | for local_file in sorted(platform_utils.listdir(local_dir)): | 619 | for local_file in sorted(platform_utils.listdir(self.local_manifests)): |
617 | if local_file.endswith('.xml'): | 620 | if local_file.endswith('.xml'): |
618 | local = os.path.join(local_dir, local_file) | 621 | local = os.path.join(self.local_manifests, local_file) |
619 | nodes.append(self._ParseManifestXml(local, self.repodir)) | 622 | nodes.append(self._ParseManifestXml(local, self.repodir)) |
620 | except OSError: | 623 | except OSError: |
621 | pass | 624 | pass |
@@ -1253,15 +1256,7 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md | |||
1253 | 1256 | ||
1254 | 1257 | ||
1255 | class GitcManifest(XmlManifest): | 1258 | class GitcManifest(XmlManifest): |
1256 | 1259 | """Parser for GitC (git-in-the-cloud) manifests.""" | |
1257 | def __init__(self, repodir, gitc_client_name): | ||
1258 | """Initialize the GitcManifest object.""" | ||
1259 | super(GitcManifest, self).__init__(repodir) | ||
1260 | self.isGitcClient = True | ||
1261 | self.gitc_client_name = gitc_client_name | ||
1262 | self.gitc_client_dir = os.path.join(gitc_utils.get_gitc_manifest_dir(), | ||
1263 | gitc_client_name) | ||
1264 | self.manifestFile = os.path.join(self.gitc_client_dir, '.manifest') | ||
1265 | 1260 | ||
1266 | def _ParseProject(self, node, parent=None): | 1261 | def _ParseProject(self, node, parent=None): |
1267 | """Override _ParseProject and add support for GITC specific attributes.""" | 1262 | """Override _ParseProject and add support for GITC specific attributes.""" |
@@ -1272,3 +1267,38 @@ class GitcManifest(XmlManifest): | |||
1272 | """Output GITC Specific Project attributes""" | 1267 | """Output GITC Specific Project attributes""" |
1273 | if p.old_revision: | 1268 | if p.old_revision: |
1274 | e.setAttribute('old-revision', str(p.old_revision)) | 1269 | e.setAttribute('old-revision', str(p.old_revision)) |
1270 | |||
1271 | |||
1272 | class RepoClient(XmlManifest): | ||
1273 | """Manages a repo client checkout.""" | ||
1274 | |||
1275 | def __init__(self, repodir, manifest_file=None): | ||
1276 | self.isGitcClient = False | ||
1277 | |||
1278 | if os.path.exists(os.path.join(repodir, LOCAL_MANIFEST_NAME)): | ||
1279 | print('error: %s is not supported; put local manifests in `%s` instead' % | ||
1280 | (LOCAL_MANIFEST_NAME, os.path.join(repodir, LOCAL_MANIFESTS_DIR_NAME)), | ||
1281 | file=sys.stderr) | ||
1282 | sys.exit(1) | ||
1283 | |||
1284 | if manifest_file is None: | ||
1285 | manifest_file = os.path.join(repodir, MANIFEST_FILE_NAME) | ||
1286 | local_manifests = os.path.abspath(os.path.join(repodir, LOCAL_MANIFESTS_DIR_NAME)) | ||
1287 | super(RepoClient, self).__init__(repodir, manifest_file, local_manifests) | ||
1288 | |||
1289 | # TODO: Completely separate manifest logic out of the client. | ||
1290 | self.manifest = self | ||
1291 | |||
1292 | |||
1293 | class GitcClient(RepoClient, GitcManifest): | ||
1294 | """Manages a GitC client checkout.""" | ||
1295 | |||
1296 | def __init__(self, repodir, gitc_client_name): | ||
1297 | """Initialize the GitcManifest object.""" | ||
1298 | self.gitc_client_name = gitc_client_name | ||
1299 | self.gitc_client_dir = os.path.join(gitc_utils.get_gitc_manifest_dir(), | ||
1300 | gitc_client_name) | ||
1301 | |||
1302 | super(GitcManifest, self).__init__( | ||
1303 | repodir, os.path.join(self.gitc_client_dir, '.manifest')) | ||
1304 | self.isGitcClient = True | ||
@@ -510,7 +510,7 @@ class Project(object): | |||
510 | with exponential backoff and jitter. | 510 | with exponential backoff and jitter. |
511 | old_revision: saved git commit id for open GITC projects. | 511 | old_revision: saved git commit id for open GITC projects. |
512 | """ | 512 | """ |
513 | self.manifest = manifest | 513 | self.client = self.manifest = manifest |
514 | self.name = name | 514 | self.name = name |
515 | self.remote = remote | 515 | self.remote = remote |
516 | self.gitdir = gitdir.replace('\\', '/') | 516 | self.gitdir = gitdir.replace('\\', '/') |
@@ -551,7 +551,7 @@ class Project(object): | |||
551 | self.linkfiles = [] | 551 | self.linkfiles = [] |
552 | self.annotations = [] | 552 | self.annotations = [] |
553 | self.config = GitConfig.ForRepository(gitdir=self.gitdir, | 553 | self.config = GitConfig.ForRepository(gitdir=self.gitdir, |
554 | defaults=self.manifest.globalConfig) | 554 | defaults=self.client.globalConfig) |
555 | 555 | ||
556 | if self.worktree: | 556 | if self.worktree: |
557 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) | 557 | self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir) |
@@ -1168,7 +1168,7 @@ class Project(object): | |||
1168 | self._InitHooks() | 1168 | self._InitHooks() |
1169 | 1169 | ||
1170 | def _CopyAndLinkFiles(self): | 1170 | def _CopyAndLinkFiles(self): |
1171 | if self.manifest.isGitcClient: | 1171 | if self.client.isGitcClient: |
1172 | return | 1172 | return |
1173 | for copyfile in self.copyfiles: | 1173 | for copyfile in self.copyfiles: |
1174 | copyfile._Copy() | 1174 | copyfile._Copy() |
diff --git a/subcmds/diffmanifests.py b/subcmds/diffmanifests.py index 72c441ac..409bbdac 100644 --- a/subcmds/diffmanifests.py +++ b/subcmds/diffmanifests.py | |||
@@ -16,7 +16,7 @@ | |||
16 | 16 | ||
17 | from color import Coloring | 17 | from color import Coloring |
18 | from command import PagedCommand | 18 | from command import PagedCommand |
19 | from manifest_xml import XmlManifest | 19 | from manifest_xml import RepoClient |
20 | 20 | ||
21 | 21 | ||
22 | class _Coloring(Coloring): | 22 | class _Coloring(Coloring): |
@@ -183,7 +183,7 @@ synced and their revisions won't be found. | |||
183 | self.OptionParser.error('missing manifests to diff') | 183 | self.OptionParser.error('missing manifests to diff') |
184 | 184 | ||
185 | def Execute(self, opt, args): | 185 | def Execute(self, opt, args): |
186 | self.out = _Coloring(self.manifest.globalConfig) | 186 | self.out = _Coloring(self.client.globalConfig) |
187 | self.printText = self.out.nofmt_printer('text') | 187 | self.printText = self.out.nofmt_printer('text') |
188 | if opt.color: | 188 | if opt.color: |
189 | self.printProject = self.out.nofmt_printer('project', attr='bold') | 189 | self.printProject = self.out.nofmt_printer('project', attr='bold') |
@@ -193,12 +193,12 @@ synced and their revisions won't be found. | |||
193 | else: | 193 | else: |
194 | self.printProject = self.printAdded = self.printRemoved = self.printRevision = self.printText | 194 | self.printProject = self.printAdded = self.printRemoved = self.printRevision = self.printText |
195 | 195 | ||
196 | manifest1 = XmlManifest(self.manifest.repodir) | 196 | manifest1 = RepoClient(self.manifest.repodir) |
197 | manifest1.Override(args[0], load_local_manifests=False) | 197 | manifest1.Override(args[0], load_local_manifests=False) |
198 | if len(args) == 1: | 198 | if len(args) == 1: |
199 | manifest2 = self.manifest | 199 | manifest2 = self.manifest |
200 | else: | 200 | else: |
201 | manifest2 = XmlManifest(self.manifest.repodir) | 201 | manifest2 = RepoClient(self.manifest.repodir) |
202 | manifest2.Override(args[1], load_local_manifests=False) | 202 | manifest2.Override(args[1], load_local_manifests=False) |
203 | 203 | ||
204 | diff = manifest1.projectsDiff(manifest2) | 204 | diff = manifest1.projectsDiff(manifest2) |
diff --git a/subcmds/help.py b/subcmds/help.py index 1e16019a..c219a763 100644 --- a/subcmds/help.py +++ b/subcmds/help.py | |||
@@ -65,7 +65,7 @@ Displays detailed usage information about a command. | |||
65 | def gitc_supported(cmd): | 65 | def gitc_supported(cmd): |
66 | if not isinstance(cmd, GitcAvailableCommand) and not isinstance(cmd, GitcClientCommand): | 66 | if not isinstance(cmd, GitcAvailableCommand) and not isinstance(cmd, GitcClientCommand): |
67 | return True | 67 | return True |
68 | if self.manifest.isGitcClient: | 68 | if self.client.isGitcClient: |
69 | return True | 69 | return True |
70 | if isinstance(cmd, GitcClientCommand): | 70 | if isinstance(cmd, GitcClientCommand): |
71 | return False | 71 | return False |
@@ -127,7 +127,7 @@ Displays detailed usage information about a command. | |||
127 | self.wrap.end_paragraph(1) | 127 | self.wrap.end_paragraph(1) |
128 | self.wrap.end_paragraph(0) | 128 | self.wrap.end_paragraph(0) |
129 | 129 | ||
130 | out = _Out(self.manifest.globalConfig) | 130 | out = _Out(self.client.globalConfig) |
131 | out._PrintSection('Summary', 'helpSummary') | 131 | out._PrintSection('Summary', 'helpSummary') |
132 | cmd.OptionParser.print_help() | 132 | cmd.OptionParser.print_help() |
133 | out._PrintSection('Description', 'helpDescription') | 133 | out._PrintSection('Description', 'helpDescription') |
diff --git a/subcmds/info.py b/subcmds/info.py index f4d6f98c..60149975 100644 --- a/subcmds/info.py +++ b/subcmds/info.py | |||
@@ -44,7 +44,7 @@ class Info(PagedCommand): | |||
44 | help="Disable all remote operations") | 44 | help="Disable all remote operations") |
45 | 45 | ||
46 | def Execute(self, opt, args): | 46 | def Execute(self, opt, args): |
47 | self.out = _Coloring(self.manifest.globalConfig) | 47 | self.out = _Coloring(self.client.globalConfig) |
48 | self.heading = self.out.printer('heading', attr='bold') | 48 | self.heading = self.out.printer('heading', attr='bold') |
49 | self.headtext = self.out.nofmt_printer('headtext', fg='yellow') | 49 | self.headtext = self.out.nofmt_printer('headtext', fg='yellow') |
50 | self.redtext = self.out.printer('redtext', fg='red') | 50 | self.redtext = self.out.printer('redtext', fg='red') |
diff --git a/subcmds/init.py b/subcmds/init.py index 5ba0d074..f46babfe 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -365,7 +365,7 @@ to update the working directory files. | |||
365 | return a | 365 | return a |
366 | 366 | ||
367 | def _ShouldConfigureUser(self, opt): | 367 | def _ShouldConfigureUser(self, opt): |
368 | gc = self.manifest.globalConfig | 368 | gc = self.client.globalConfig |
369 | mp = self.manifest.manifestProject | 369 | mp = self.manifest.manifestProject |
370 | 370 | ||
371 | # If we don't have local settings, get from global. | 371 | # If we don't have local settings, get from global. |
@@ -414,7 +414,7 @@ to update the working directory files. | |||
414 | return False | 414 | return False |
415 | 415 | ||
416 | def _ConfigureColor(self): | 416 | def _ConfigureColor(self): |
417 | gc = self.manifest.globalConfig | 417 | gc = self.client.globalConfig |
418 | if self._HasColorSet(gc): | 418 | if self._HasColorSet(gc): |
419 | return | 419 | return |
420 | 420 | ||
diff --git a/subcmds/status.py b/subcmds/status.py index c380dba3..dfa974e9 100644 --- a/subcmds/status.py +++ b/subcmds/status.py | |||
@@ -165,7 +165,7 @@ the following meanings: | |||
165 | proj_dirs, proj_dirs_parents, outstring) | 165 | proj_dirs, proj_dirs_parents, outstring) |
166 | 166 | ||
167 | if outstring: | 167 | if outstring: |
168 | output = StatusColoring(self.manifest.globalConfig) | 168 | output = StatusColoring(self.client.globalConfig) |
169 | output.project('Objects not within a project (orphans)') | 169 | output.project('Objects not within a project (orphans)') |
170 | output.nl() | 170 | output.nl() |
171 | for entry in outstring: | 171 | for entry in outstring: |
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index aa6cb7df..40385cce 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py | |||
@@ -19,6 +19,8 @@ | |||
19 | from __future__ import print_function | 19 | from __future__ import print_function |
20 | 20 | ||
21 | import os | 21 | import os |
22 | import shutil | ||
23 | import tempfile | ||
22 | import unittest | 24 | import unittest |
23 | import xml.dom.minidom | 25 | import xml.dom.minidom |
24 | 26 | ||
@@ -146,3 +148,90 @@ class ValueTests(unittest.TestCase): | |||
146 | with self.assertRaises(error.ManifestParseError): | 148 | with self.assertRaises(error.ManifestParseError): |
147 | node = self._get_node('<node a="xx"/>') | 149 | node = self._get_node('<node a="xx"/>') |
148 | manifest_xml.XmlInt(node, 'a') | 150 | manifest_xml.XmlInt(node, 'a') |
151 | |||
152 | |||
153 | class 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']) | ||