diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_manifest_xml.py | 230 |
1 files changed, 159 insertions, 71 deletions
diff --git a/tests/test_manifest_xml.py b/tests/test_manifest_xml.py index 4b545140..f69e9cf8 100644 --- a/tests/test_manifest_xml.py +++ b/tests/test_manifest_xml.py | |||
| @@ -24,6 +24,40 @@ import error | |||
| 24 | import manifest_xml | 24 | import manifest_xml |
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | # Invalid paths that we don't want in the filesystem. | ||
| 28 | INVALID_FS_PATHS = ( | ||
| 29 | '', | ||
| 30 | '.', | ||
| 31 | '..', | ||
| 32 | '../', | ||
| 33 | './', | ||
| 34 | 'foo/', | ||
| 35 | './foo', | ||
| 36 | '../foo', | ||
| 37 | 'foo/./bar', | ||
| 38 | 'foo/../../bar', | ||
| 39 | '/foo', | ||
| 40 | './../foo', | ||
| 41 | '.git/foo', | ||
| 42 | # Check case folding. | ||
| 43 | '.GIT/foo', | ||
| 44 | 'blah/.git/foo', | ||
| 45 | '.repo/foo', | ||
| 46 | '.repoconfig', | ||
| 47 | # Block ~ due to 8.3 filenames on Windows filesystems. | ||
| 48 | '~', | ||
| 49 | 'foo~', | ||
| 50 | 'blah/foo~', | ||
| 51 | # Block Unicode characters that get normalized out by filesystems. | ||
| 52 | u'foo\u200Cbar', | ||
| 53 | ) | ||
| 54 | |||
| 55 | # Make sure platforms that use path separators (e.g. Windows) are also | ||
| 56 | # rejected properly. | ||
| 57 | if os.path.sep != '/': | ||
| 58 | INVALID_FS_PATHS += tuple(x.replace('/', os.path.sep) for x in INVALID_FS_PATHS) | ||
| 59 | |||
| 60 | |||
| 27 | class ManifestParseTestCase(unittest.TestCase): | 61 | class ManifestParseTestCase(unittest.TestCase): |
| 28 | """TestCase for parsing manifests.""" | 62 | """TestCase for parsing manifests.""" |
| 29 | 63 | ||
| @@ -86,38 +120,7 @@ class ManifestValidateFilePaths(unittest.TestCase): | |||
| 86 | 120 | ||
| 87 | def test_bad_paths(self): | 121 | def test_bad_paths(self): |
| 88 | """Make sure bad paths (src & dest) are rejected.""" | 122 | """Make sure bad paths (src & dest) are rejected.""" |
| 89 | PATHS = ( | 123 | for path in INVALID_FS_PATHS: |
| 90 | '', | ||
| 91 | '.', | ||
| 92 | '..', | ||
| 93 | '../', | ||
| 94 | './', | ||
| 95 | 'foo/', | ||
| 96 | './foo', | ||
| 97 | '../foo', | ||
| 98 | 'foo/./bar', | ||
| 99 | 'foo/../../bar', | ||
| 100 | '/foo', | ||
| 101 | './../foo', | ||
| 102 | '.git/foo', | ||
| 103 | # Check case folding. | ||
| 104 | '.GIT/foo', | ||
| 105 | 'blah/.git/foo', | ||
| 106 | '.repo/foo', | ||
| 107 | '.repoconfig', | ||
| 108 | # Block ~ due to 8.3 filenames on Windows filesystems. | ||
| 109 | '~', | ||
| 110 | 'foo~', | ||
| 111 | 'blah/foo~', | ||
| 112 | # Block Unicode characters that get normalized out by filesystems. | ||
| 113 | u'foo\u200Cbar', | ||
| 114 | ) | ||
| 115 | # Make sure platforms that use path separators (e.g. Windows) are also | ||
| 116 | # rejected properly. | ||
| 117 | if os.path.sep != '/': | ||
| 118 | PATHS += tuple(x.replace('/', os.path.sep) for x in PATHS) | ||
| 119 | |||
| 120 | for path in PATHS: | ||
| 121 | self.assertRaises( | 124 | self.assertRaises( |
| 122 | error.ManifestInvalidPathError, self.check_both, path, 'a') | 125 | error.ManifestInvalidPathError, self.check_both, path, 'a') |
| 123 | self.assertRaises( | 126 | self.assertRaises( |
| @@ -248,7 +251,82 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 248 | '<superproject name="superproject"/>' + | 251 | '<superproject name="superproject"/>' + |
| 249 | '</manifest>') | 252 | '</manifest>') |
| 250 | 253 | ||
| 251 | def test_project_group(self): | 254 | |
| 255 | class IncludeElementTests(ManifestParseTestCase): | ||
| 256 | """Tests for <include>.""" | ||
| 257 | |||
| 258 | def test_group_levels(self): | ||
| 259 | root_m = os.path.join(self.manifest_dir, 'root.xml') | ||
| 260 | with open(root_m, 'w') as fp: | ||
| 261 | fp.write(""" | ||
| 262 | <manifest> | ||
| 263 | <remote name="test-remote" fetch="http://localhost" /> | ||
| 264 | <default remote="test-remote" revision="refs/heads/main" /> | ||
| 265 | <include name="level1.xml" groups="level1-group" /> | ||
| 266 | <project name="root-name1" path="root-path1" /> | ||
| 267 | <project name="root-name2" path="root-path2" groups="r2g1,r2g2" /> | ||
| 268 | </manifest> | ||
| 269 | """) | ||
| 270 | with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: | ||
| 271 | fp.write(""" | ||
| 272 | <manifest> | ||
| 273 | <include name="level2.xml" groups="level2-group" /> | ||
| 274 | <project name="level1-name1" path="level1-path1" /> | ||
| 275 | </manifest> | ||
| 276 | """) | ||
| 277 | with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: | ||
| 278 | fp.write(""" | ||
| 279 | <manifest> | ||
| 280 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> | ||
| 281 | </manifest> | ||
| 282 | """) | ||
| 283 | include_m = manifest_xml.XmlManifest(self.repodir, root_m) | ||
| 284 | for proj in include_m.projects: | ||
| 285 | if proj.name == 'root-name1': | ||
| 286 | # Check include group not set on root level proj. | ||
| 287 | self.assertNotIn('level1-group', proj.groups) | ||
| 288 | if proj.name == 'root-name2': | ||
| 289 | # Check root proj group not removed. | ||
| 290 | self.assertIn('r2g1', proj.groups) | ||
| 291 | if proj.name == 'level1-name1': | ||
| 292 | # Check level1 proj has inherited group level 1. | ||
| 293 | self.assertIn('level1-group', proj.groups) | ||
| 294 | if proj.name == 'level2-name1': | ||
| 295 | # Check level2 proj has inherited group levels 1 and 2. | ||
| 296 | self.assertIn('level1-group', proj.groups) | ||
| 297 | self.assertIn('level2-group', proj.groups) | ||
| 298 | # Check level2 proj group not removed. | ||
| 299 | self.assertIn('l2g1', proj.groups) | ||
| 300 | |||
| 301 | def test_bad_name_checks(self): | ||
| 302 | """Check handling of bad name attribute.""" | ||
| 303 | def parse(name): | ||
| 304 | manifest = self.getXmlManifest(f""" | ||
| 305 | <manifest> | ||
| 306 | <remote name="default-remote" fetch="http://localhost" /> | ||
| 307 | <default remote="default-remote" revision="refs/heads/main" /> | ||
| 308 | <include name="{name}" /> | ||
| 309 | </manifest> | ||
| 310 | """) | ||
| 311 | # Force the manifest to be parsed. | ||
| 312 | manifest.ToXml() | ||
| 313 | |||
| 314 | # Handle empty name explicitly because a different codepath rejects it. | ||
| 315 | with self.assertRaises(error.ManifestParseError): | ||
| 316 | parse('') | ||
| 317 | |||
| 318 | for path in INVALID_FS_PATHS: | ||
| 319 | if not path: | ||
| 320 | continue | ||
| 321 | |||
| 322 | with self.assertRaises(error.ManifestInvalidPathError): | ||
| 323 | parse(path) | ||
| 324 | |||
| 325 | |||
| 326 | class ProjectElementTests(ManifestParseTestCase): | ||
| 327 | """Tests for <project>.""" | ||
| 328 | |||
| 329 | def test_group(self): | ||
| 252 | """Check project group settings.""" | 330 | """Check project group settings.""" |
| 253 | manifest = self.getXmlManifest(""" | 331 | manifest = self.getXmlManifest(""" |
| 254 | <manifest> | 332 | <manifest> |
| @@ -272,7 +350,7 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 272 | result['extras'], | 350 | result['extras'], |
| 273 | ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) | 351 | ['g1', 'g2', 'g1', 'name:extras', 'all', 'path:path']) |
| 274 | 352 | ||
| 275 | def test_project_set_revision_id(self): | 353 | def test_set_revision_id(self): |
| 276 | """Check setting of project's revisionId.""" | 354 | """Check setting of project's revisionId.""" |
| 277 | manifest = self.getXmlManifest(""" | 355 | manifest = self.getXmlManifest(""" |
| 278 | <manifest> | 356 | <manifest> |
| @@ -292,51 +370,61 @@ class XmlManifestTests(ManifestParseTestCase): | |||
| 292 | '<project name="test-name" revision="ABCDEF"/>' + | 370 | '<project name="test-name" revision="ABCDEF"/>' + |
| 293 | '</manifest>') | 371 | '</manifest>') |
| 294 | 372 | ||
| 295 | def test_include_levels(self): | 373 | def test_trailing_slash(self): |
| 296 | root_m = os.path.join(self.manifest_dir, 'root.xml') | 374 | """Check handling of trailing slashes in attributes.""" |
| 297 | with open(root_m, 'w') as fp: | 375 | def parse(name, path): |
| 298 | fp.write(""" | 376 | return self.getXmlManifest(f""" |
| 299 | <manifest> | ||
| 300 | <remote name="test-remote" fetch="http://localhost" /> | ||
| 301 | <default remote="test-remote" revision="refs/heads/main" /> | ||
| 302 | <include name="level1.xml" groups="level1-group" /> | ||
| 303 | <project name="root-name1" path="root-path1" /> | ||
| 304 | <project name="root-name2" path="root-path2" groups="r2g1,r2g2" /> | ||
| 305 | </manifest> | ||
| 306 | """) | ||
| 307 | with open(os.path.join(self.manifest_dir, 'level1.xml'), 'w') as fp: | ||
| 308 | fp.write(""" | ||
| 309 | <manifest> | 377 | <manifest> |
| 310 | <include name="level2.xml" groups="level2-group" /> | 378 | <remote name="default-remote" fetch="http://localhost" /> |
| 311 | <project name="level1-name1" path="level1-path1" /> | 379 | <default remote="default-remote" revision="refs/heads/main" /> |
| 380 | <project name="{name}" path="{path}" /> | ||
| 312 | </manifest> | 381 | </manifest> |
| 313 | """) | 382 | """) |
| 314 | with open(os.path.join(self.manifest_dir, 'level2.xml'), 'w') as fp: | 383 | |
| 315 | fp.write(""" | 384 | manifest = parse('a/path/', 'foo') |
| 385 | self.assertEqual(manifest.projects[0].gitdir, | ||
| 386 | os.path.join(self.tempdir, '.repo/projects/foo.git')) | ||
| 387 | self.assertEqual(manifest.projects[0].objdir, | ||
| 388 | os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) | ||
| 389 | |||
| 390 | manifest = parse('a/path', 'foo/') | ||
| 391 | self.assertEqual(manifest.projects[0].gitdir, | ||
| 392 | os.path.join(self.tempdir, '.repo/projects/foo.git')) | ||
| 393 | self.assertEqual(manifest.projects[0].objdir, | ||
| 394 | os.path.join(self.tempdir, '.repo/project-objects/a/path.git')) | ||
| 395 | |||
| 396 | def test_bad_path_name_checks(self): | ||
| 397 | """Check handling of bad path & name attributes.""" | ||
| 398 | def parse(name, path): | ||
| 399 | manifest = self.getXmlManifest(f""" | ||
| 316 | <manifest> | 400 | <manifest> |
| 317 | <project name="level2-name1" path="level2-path1" groups="l2g1,l2g2" /> | 401 | <remote name="default-remote" fetch="http://localhost" /> |
| 402 | <default remote="default-remote" revision="refs/heads/main" /> | ||
| 403 | <project name="{name}" path="{path}" /> | ||
| 318 | </manifest> | 404 | </manifest> |
| 319 | """) | 405 | """) |
| 320 | include_m = manifest_xml.XmlManifest(self.repodir, root_m) | 406 | # Force the manifest to be parsed. |
| 321 | for proj in include_m.projects: | 407 | manifest.ToXml() |
| 322 | if proj.name == 'root-name1': | 408 | |
| 323 | # Check include group not set on root level proj. | 409 | # Verify the parser is valid by default to avoid buggy tests below. |
| 324 | self.assertNotIn('level1-group', proj.groups) | 410 | parse('ok', 'ok') |
| 325 | if proj.name == 'root-name2': | 411 | |
| 326 | # Check root proj group not removed. | 412 | # Handle empty name explicitly because a different codepath rejects it. |
| 327 | self.assertIn('r2g1', proj.groups) | 413 | # Empty path is OK because it defaults to the name field. |
| 328 | if proj.name == 'level1-name1': | 414 | with self.assertRaises(error.ManifestParseError): |
| 329 | # Check level1 proj has inherited group level 1. | 415 | parse('', 'ok') |
| 330 | self.assertIn('level1-group', proj.groups) | 416 | |
| 331 | if proj.name == 'level2-name1': | 417 | for path in INVALID_FS_PATHS: |
| 332 | # Check level2 proj has inherited group levels 1 and 2. | 418 | if not path or path.endswith('/'): |
| 333 | self.assertIn('level1-group', proj.groups) | 419 | continue |
| 334 | self.assertIn('level2-group', proj.groups) | 420 | |
| 335 | # Check level2 proj group not removed. | 421 | with self.assertRaises(error.ManifestInvalidPathError): |
| 336 | self.assertIn('l2g1', proj.groups) | 422 | parse(path, 'ok') |
| 423 | with self.assertRaises(error.ManifestInvalidPathError): | ||
| 424 | parse('ok', path) | ||
| 337 | 425 | ||
| 338 | 426 | ||
| 339 | class SuperProjectTests(ManifestParseTestCase): | 427 | class SuperProjectElementTests(ManifestParseTestCase): |
| 340 | """Tests for <superproject>.""" | 428 | """Tests for <superproject>.""" |
| 341 | 429 | ||
| 342 | def test_superproject(self): | 430 | def test_superproject(self): |
