diff options
Diffstat (limited to 'tests/test_project.py')
| -rw-r--r-- | tests/test_project.py | 855 |
1 files changed, 448 insertions, 407 deletions
diff --git a/tests/test_project.py b/tests/test_project.py index c50d9940..bc8330b2 100644 --- a/tests/test_project.py +++ b/tests/test_project.py | |||
| @@ -31,452 +31,493 @@ import project | |||
| 31 | 31 | ||
| 32 | @contextlib.contextmanager | 32 | @contextlib.contextmanager |
| 33 | def TempGitTree(): | 33 | def TempGitTree(): |
| 34 | """Create a new empty git checkout for testing.""" | 34 | """Create a new empty git checkout for testing.""" |
| 35 | with tempfile.TemporaryDirectory(prefix='repo-tests') as tempdir: | 35 | with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: |
| 36 | # Tests need to assume, that main is default branch at init, | 36 | # Tests need to assume, that main is default branch at init, |
| 37 | # which is not supported in config until 2.28. | 37 | # which is not supported in config until 2.28. |
| 38 | cmd = ['git', 'init'] | 38 | cmd = ["git", "init"] |
| 39 | if git_command.git_require((2, 28, 0)): | 39 | if git_command.git_require((2, 28, 0)): |
| 40 | cmd += ['--initial-branch=main'] | 40 | cmd += ["--initial-branch=main"] |
| 41 | else: | 41 | else: |
| 42 | # Use template dir for init. | 42 | # Use template dir for init. |
| 43 | templatedir = tempfile.mkdtemp(prefix='.test-template') | 43 | templatedir = tempfile.mkdtemp(prefix=".test-template") |
| 44 | with open(os.path.join(templatedir, 'HEAD'), 'w') as fp: | 44 | with open(os.path.join(templatedir, "HEAD"), "w") as fp: |
| 45 | fp.write('ref: refs/heads/main\n') | 45 | fp.write("ref: refs/heads/main\n") |
| 46 | cmd += ['--template', templatedir] | 46 | cmd += ["--template", templatedir] |
| 47 | subprocess.check_call(cmd, cwd=tempdir) | 47 | subprocess.check_call(cmd, cwd=tempdir) |
| 48 | yield tempdir | 48 | yield tempdir |
| 49 | 49 | ||
| 50 | 50 | ||
| 51 | class FakeProject(object): | 51 | class FakeProject(object): |
| 52 | """A fake for Project for basic functionality.""" | 52 | """A fake for Project for basic functionality.""" |
| 53 | 53 | ||
| 54 | def __init__(self, worktree): | 54 | def __init__(self, worktree): |
| 55 | self.worktree = worktree | 55 | self.worktree = worktree |
| 56 | self.gitdir = os.path.join(worktree, '.git') | 56 | self.gitdir = os.path.join(worktree, ".git") |
| 57 | self.name = 'fakeproject' | 57 | self.name = "fakeproject" |
| 58 | self.work_git = project.Project._GitGetByExec( | 58 | self.work_git = project.Project._GitGetByExec( |
| 59 | self, bare=False, gitdir=self.gitdir) | 59 | self, bare=False, gitdir=self.gitdir |
| 60 | self.bare_git = project.Project._GitGetByExec( | 60 | ) |
| 61 | self, bare=True, gitdir=self.gitdir) | 61 | self.bare_git = project.Project._GitGetByExec( |
| 62 | self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) | 62 | self, bare=True, gitdir=self.gitdir |
| 63 | ) | ||
| 64 | self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) | ||
| 63 | 65 | ||
| 64 | 66 | ||
| 65 | class ReviewableBranchTests(unittest.TestCase): | 67 | class ReviewableBranchTests(unittest.TestCase): |
| 66 | """Check ReviewableBranch behavior.""" | 68 | """Check ReviewableBranch behavior.""" |
| 67 | 69 | ||
| 68 | def test_smoke(self): | 70 | def test_smoke(self): |
| 69 | """A quick run through everything.""" | 71 | """A quick run through everything.""" |
| 70 | with TempGitTree() as tempdir: | 72 | with TempGitTree() as tempdir: |
| 71 | fakeproj = FakeProject(tempdir) | 73 | fakeproj = FakeProject(tempdir) |
| 72 | 74 | ||
| 73 | # Generate some commits. | 75 | # Generate some commits. |
| 74 | with open(os.path.join(tempdir, 'readme'), 'w') as fp: | 76 | with open(os.path.join(tempdir, "readme"), "w") as fp: |
| 75 | fp.write('txt') | 77 | fp.write("txt") |
| 76 | fakeproj.work_git.add('readme') | 78 | fakeproj.work_git.add("readme") |
| 77 | fakeproj.work_git.commit('-mAdd file') | 79 | fakeproj.work_git.commit("-mAdd file") |
| 78 | fakeproj.work_git.checkout('-b', 'work') | 80 | fakeproj.work_git.checkout("-b", "work") |
| 79 | fakeproj.work_git.rm('-f', 'readme') | 81 | fakeproj.work_git.rm("-f", "readme") |
| 80 | fakeproj.work_git.commit('-mDel file') | 82 | fakeproj.work_git.commit("-mDel file") |
| 81 | 83 | ||
| 82 | # Start off with the normal details. | 84 | # Start off with the normal details. |
| 83 | rb = project.ReviewableBranch( | 85 | rb = project.ReviewableBranch( |
| 84 | fakeproj, fakeproj.config.GetBranch('work'), 'main') | 86 | fakeproj, fakeproj.config.GetBranch("work"), "main" |
| 85 | self.assertEqual('work', rb.name) | 87 | ) |
| 86 | self.assertEqual(1, len(rb.commits)) | 88 | self.assertEqual("work", rb.name) |
| 87 | self.assertIn('Del file', rb.commits[0]) | 89 | self.assertEqual(1, len(rb.commits)) |
| 88 | d = rb.unabbrev_commits | 90 | self.assertIn("Del file", rb.commits[0]) |
| 89 | self.assertEqual(1, len(d)) | 91 | d = rb.unabbrev_commits |
| 90 | short, long = next(iter(d.items())) | 92 | self.assertEqual(1, len(d)) |
| 91 | self.assertTrue(long.startswith(short)) | 93 | short, long = next(iter(d.items())) |
| 92 | self.assertTrue(rb.base_exists) | 94 | self.assertTrue(long.startswith(short)) |
| 93 | # Hard to assert anything useful about this. | 95 | self.assertTrue(rb.base_exists) |
| 94 | self.assertTrue(rb.date) | 96 | # Hard to assert anything useful about this. |
| 95 | 97 | self.assertTrue(rb.date) | |
| 96 | # Now delete the tracking branch! | 98 | |
| 97 | fakeproj.work_git.branch('-D', 'main') | 99 | # Now delete the tracking branch! |
| 98 | rb = project.ReviewableBranch( | 100 | fakeproj.work_git.branch("-D", "main") |
| 99 | fakeproj, fakeproj.config.GetBranch('work'), 'main') | 101 | rb = project.ReviewableBranch( |
| 100 | self.assertEqual(0, len(rb.commits)) | 102 | fakeproj, fakeproj.config.GetBranch("work"), "main" |
| 101 | self.assertFalse(rb.base_exists) | 103 | ) |
| 102 | # Hard to assert anything useful about this. | 104 | self.assertEqual(0, len(rb.commits)) |
| 103 | self.assertTrue(rb.date) | 105 | self.assertFalse(rb.base_exists) |
| 106 | # Hard to assert anything useful about this. | ||
| 107 | self.assertTrue(rb.date) | ||
| 104 | 108 | ||
| 105 | 109 | ||
| 106 | class CopyLinkTestCase(unittest.TestCase): | 110 | class CopyLinkTestCase(unittest.TestCase): |
| 107 | """TestCase for stub repo client checkouts. | 111 | """TestCase for stub repo client checkouts. |
| 108 | 112 | ||
| 109 | It'll have a layout like this: | 113 | It'll have a layout like this: |
| 110 | tempdir/ # self.tempdir | 114 | tempdir/ # self.tempdir |
| 111 | checkout/ # self.topdir | 115 | checkout/ # self.topdir |
| 112 | git-project/ # self.worktree | 116 | git-project/ # self.worktree |
| 113 | 117 | ||
| 114 | Attributes: | 118 | Attributes: |
| 115 | tempdir: A dedicated temporary directory. | 119 | tempdir: A dedicated temporary directory. |
| 116 | worktree: The top of the repo client checkout. | 120 | worktree: The top of the repo client checkout. |
| 117 | topdir: The top of a project checkout. | 121 | topdir: The top of a project checkout. |
| 118 | """ | 122 | """ |
| 119 | 123 | ||
| 120 | def setUp(self): | 124 | def setUp(self): |
| 121 | self.tempdirobj = tempfile.TemporaryDirectory(prefix='repo_tests') | 125 | self.tempdirobj = tempfile.TemporaryDirectory(prefix="repo_tests") |
| 122 | self.tempdir = self.tempdirobj.name | 126 | self.tempdir = self.tempdirobj.name |
| 123 | self.topdir = os.path.join(self.tempdir, 'checkout') | 127 | self.topdir = os.path.join(self.tempdir, "checkout") |
| 124 | self.worktree = os.path.join(self.topdir, 'git-project') | 128 | self.worktree = os.path.join(self.topdir, "git-project") |
| 125 | os.makedirs(self.topdir) | 129 | os.makedirs(self.topdir) |
| 126 | os.makedirs(self.worktree) | 130 | os.makedirs(self.worktree) |
| 127 | 131 | ||
| 128 | def tearDown(self): | 132 | def tearDown(self): |
| 129 | self.tempdirobj.cleanup() | 133 | self.tempdirobj.cleanup() |
| 130 | 134 | ||
| 131 | @staticmethod | 135 | @staticmethod |
| 132 | def touch(path): | 136 | def touch(path): |
| 133 | with open(path, 'w'): | 137 | with open(path, "w"): |
| 134 | pass | 138 | pass |
| 135 | 139 | ||
| 136 | def assertExists(self, path, msg=None): | 140 | def assertExists(self, path, msg=None): |
| 137 | """Make sure |path| exists.""" | 141 | """Make sure |path| exists.""" |
| 138 | if os.path.exists(path): | 142 | if os.path.exists(path): |
| 139 | return | 143 | return |
| 140 | 144 | ||
| 141 | if msg is None: | 145 | if msg is None: |
| 142 | msg = ['path is missing: %s' % path] | 146 | msg = ["path is missing: %s" % path] |
| 143 | while path != '/': | 147 | while path != "/": |
| 144 | path = os.path.dirname(path) | 148 | path = os.path.dirname(path) |
| 145 | if not path: | 149 | if not path: |
| 146 | # If we're given something like "foo", abort once we get to "". | 150 | # If we're given something like "foo", abort once we get to |
| 147 | break | 151 | # "". |
| 148 | result = os.path.exists(path) | 152 | break |
| 149 | msg.append('\tos.path.exists(%s): %s' % (path, result)) | 153 | result = os.path.exists(path) |
| 150 | if result: | 154 | msg.append("\tos.path.exists(%s): %s" % (path, result)) |
| 151 | msg.append('\tcontents: %r' % os.listdir(path)) | 155 | if result: |
| 152 | break | 156 | msg.append("\tcontents: %r" % os.listdir(path)) |
| 153 | msg = '\n'.join(msg) | 157 | break |
| 154 | 158 | msg = "\n".join(msg) | |
| 155 | raise self.failureException(msg) | 159 | |
| 160 | raise self.failureException(msg) | ||
| 156 | 161 | ||
| 157 | 162 | ||
| 158 | class CopyFile(CopyLinkTestCase): | 163 | class CopyFile(CopyLinkTestCase): |
| 159 | """Check _CopyFile handling.""" | 164 | """Check _CopyFile handling.""" |
| 160 | 165 | ||
| 161 | def CopyFile(self, src, dest): | 166 | def CopyFile(self, src, dest): |
| 162 | return project._CopyFile(self.worktree, src, self.topdir, dest) | 167 | return project._CopyFile(self.worktree, src, self.topdir, dest) |
| 163 | 168 | ||
| 164 | def test_basic(self): | 169 | def test_basic(self): |
| 165 | """Basic test of copying a file from a project to the toplevel.""" | 170 | """Basic test of copying a file from a project to the toplevel.""" |
| 166 | src = os.path.join(self.worktree, 'foo.txt') | 171 | src = os.path.join(self.worktree, "foo.txt") |
| 167 | self.touch(src) | 172 | self.touch(src) |
| 168 | cf = self.CopyFile('foo.txt', 'foo') | 173 | cf = self.CopyFile("foo.txt", "foo") |
| 169 | cf._Copy() | 174 | cf._Copy() |
| 170 | self.assertExists(os.path.join(self.topdir, 'foo')) | 175 | self.assertExists(os.path.join(self.topdir, "foo")) |
| 171 | 176 | ||
| 172 | def test_src_subdir(self): | 177 | def test_src_subdir(self): |
| 173 | """Copy a file from a subdir of a project.""" | 178 | """Copy a file from a subdir of a project.""" |
| 174 | src = os.path.join(self.worktree, 'bar', 'foo.txt') | 179 | src = os.path.join(self.worktree, "bar", "foo.txt") |
| 175 | os.makedirs(os.path.dirname(src)) | 180 | os.makedirs(os.path.dirname(src)) |
| 176 | self.touch(src) | 181 | self.touch(src) |
| 177 | cf = self.CopyFile('bar/foo.txt', 'new.txt') | 182 | cf = self.CopyFile("bar/foo.txt", "new.txt") |
| 178 | cf._Copy() | 183 | cf._Copy() |
| 179 | self.assertExists(os.path.join(self.topdir, 'new.txt')) | 184 | self.assertExists(os.path.join(self.topdir, "new.txt")) |
| 180 | 185 | ||
| 181 | def test_dest_subdir(self): | 186 | def test_dest_subdir(self): |
| 182 | """Copy a file to a subdir of a checkout.""" | 187 | """Copy a file to a subdir of a checkout.""" |
| 183 | src = os.path.join(self.worktree, 'foo.txt') | 188 | src = os.path.join(self.worktree, "foo.txt") |
| 184 | self.touch(src) | 189 | self.touch(src) |
| 185 | cf = self.CopyFile('foo.txt', 'sub/dir/new.txt') | 190 | cf = self.CopyFile("foo.txt", "sub/dir/new.txt") |
| 186 | self.assertFalse(os.path.exists(os.path.join(self.topdir, 'sub'))) | 191 | self.assertFalse(os.path.exists(os.path.join(self.topdir, "sub"))) |
| 187 | cf._Copy() | 192 | cf._Copy() |
| 188 | self.assertExists(os.path.join(self.topdir, 'sub', 'dir', 'new.txt')) | 193 | self.assertExists(os.path.join(self.topdir, "sub", "dir", "new.txt")) |
| 189 | 194 | ||
| 190 | def test_update(self): | 195 | def test_update(self): |
| 191 | """Make sure changed files get copied again.""" | 196 | """Make sure changed files get copied again.""" |
| 192 | src = os.path.join(self.worktree, 'foo.txt') | 197 | src = os.path.join(self.worktree, "foo.txt") |
| 193 | dest = os.path.join(self.topdir, 'bar') | 198 | dest = os.path.join(self.topdir, "bar") |
| 194 | with open(src, 'w') as f: | 199 | with open(src, "w") as f: |
| 195 | f.write('1st') | 200 | f.write("1st") |
| 196 | cf = self.CopyFile('foo.txt', 'bar') | 201 | cf = self.CopyFile("foo.txt", "bar") |
| 197 | cf._Copy() | 202 | cf._Copy() |
| 198 | self.assertExists(dest) | 203 | self.assertExists(dest) |
| 199 | with open(dest) as f: | 204 | with open(dest) as f: |
| 200 | self.assertEqual(f.read(), '1st') | 205 | self.assertEqual(f.read(), "1st") |
| 201 | 206 | ||
| 202 | with open(src, 'w') as f: | 207 | with open(src, "w") as f: |
| 203 | f.write('2nd!') | 208 | f.write("2nd!") |
| 204 | cf._Copy() | 209 | cf._Copy() |
| 205 | with open(dest) as f: | 210 | with open(dest) as f: |
| 206 | self.assertEqual(f.read(), '2nd!') | 211 | self.assertEqual(f.read(), "2nd!") |
| 207 | 212 | ||
| 208 | def test_src_block_symlink(self): | 213 | def test_src_block_symlink(self): |
| 209 | """Do not allow reading from a symlinked path.""" | 214 | """Do not allow reading from a symlinked path.""" |
| 210 | src = os.path.join(self.worktree, 'foo.txt') | 215 | src = os.path.join(self.worktree, "foo.txt") |
| 211 | sym = os.path.join(self.worktree, 'sym') | 216 | sym = os.path.join(self.worktree, "sym") |
| 212 | self.touch(src) | 217 | self.touch(src) |
| 213 | platform_utils.symlink('foo.txt', sym) | 218 | platform_utils.symlink("foo.txt", sym) |
| 214 | self.assertExists(sym) | 219 | self.assertExists(sym) |
| 215 | cf = self.CopyFile('sym', 'foo') | 220 | cf = self.CopyFile("sym", "foo") |
| 216 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 221 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 217 | 222 | ||
| 218 | def test_src_block_symlink_traversal(self): | 223 | def test_src_block_symlink_traversal(self): |
| 219 | """Do not allow reading through a symlink dir.""" | 224 | """Do not allow reading through a symlink dir.""" |
| 220 | realfile = os.path.join(self.tempdir, 'file.txt') | 225 | realfile = os.path.join(self.tempdir, "file.txt") |
| 221 | self.touch(realfile) | 226 | self.touch(realfile) |
| 222 | src = os.path.join(self.worktree, 'bar', 'file.txt') | 227 | src = os.path.join(self.worktree, "bar", "file.txt") |
| 223 | platform_utils.symlink(self.tempdir, os.path.join(self.worktree, 'bar')) | 228 | platform_utils.symlink(self.tempdir, os.path.join(self.worktree, "bar")) |
| 224 | self.assertExists(src) | 229 | self.assertExists(src) |
| 225 | cf = self.CopyFile('bar/file.txt', 'foo') | 230 | cf = self.CopyFile("bar/file.txt", "foo") |
| 226 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 231 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 227 | 232 | ||
| 228 | def test_src_block_copy_from_dir(self): | 233 | def test_src_block_copy_from_dir(self): |
| 229 | """Do not allow copying from a directory.""" | 234 | """Do not allow copying from a directory.""" |
| 230 | src = os.path.join(self.worktree, 'dir') | 235 | src = os.path.join(self.worktree, "dir") |
| 231 | os.makedirs(src) | 236 | os.makedirs(src) |
| 232 | cf = self.CopyFile('dir', 'foo') | 237 | cf = self.CopyFile("dir", "foo") |
| 233 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 238 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 234 | 239 | ||
| 235 | def test_dest_block_symlink(self): | 240 | def test_dest_block_symlink(self): |
| 236 | """Do not allow writing to a symlink.""" | 241 | """Do not allow writing to a symlink.""" |
| 237 | src = os.path.join(self.worktree, 'foo.txt') | 242 | src = os.path.join(self.worktree, "foo.txt") |
| 238 | self.touch(src) | 243 | self.touch(src) |
| 239 | platform_utils.symlink('dest', os.path.join(self.topdir, 'sym')) | 244 | platform_utils.symlink("dest", os.path.join(self.topdir, "sym")) |
| 240 | cf = self.CopyFile('foo.txt', 'sym') | 245 | cf = self.CopyFile("foo.txt", "sym") |
| 241 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 246 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) |
| 242 | 247 | ||
| 243 | def test_dest_block_symlink_traversal(self): | 248 | def test_dest_block_symlink_traversal(self): |
| 244 | """Do not allow writing through a symlink dir.""" | 249 | """Do not allow writing through a symlink dir.""" |
| 245 | src = os.path.join(self.worktree, 'foo.txt') | 250 | src = os.path.join(self.worktree, "foo.txt") |
| 246 | self.touch(src) | 251 | self.touch(src) |
| 247 | platform_utils.symlink(tempfile.gettempdir(), | 252 | platform_utils.symlink( |
| 248 | os.path.join(self.topdir, 'sym')) | 253 | tempfile.gettempdir(), os.path.join(self.topdir, "sym") |
| 249 | cf = self.CopyFile('foo.txt', 'sym/foo.txt') | 254 | ) |
| 250 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 255 | cf = self.CopyFile("foo.txt", "sym/foo.txt") |
| 251 | 256 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | |
| 252 | def test_src_block_copy_to_dir(self): | 257 | |
| 253 | """Do not allow copying to a directory.""" | 258 | def test_src_block_copy_to_dir(self): |
| 254 | src = os.path.join(self.worktree, 'foo.txt') | 259 | """Do not allow copying to a directory.""" |
| 255 | self.touch(src) | 260 | src = os.path.join(self.worktree, "foo.txt") |
| 256 | os.makedirs(os.path.join(self.topdir, 'dir')) | 261 | self.touch(src) |
| 257 | cf = self.CopyFile('foo.txt', 'dir') | 262 | os.makedirs(os.path.join(self.topdir, "dir")) |
| 258 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | 263 | cf = self.CopyFile("foo.txt", "dir") |
| 264 | self.assertRaises(error.ManifestInvalidPathError, cf._Copy) | ||
| 259 | 265 | ||
| 260 | 266 | ||
| 261 | class LinkFile(CopyLinkTestCase): | 267 | class LinkFile(CopyLinkTestCase): |
| 262 | """Check _LinkFile handling.""" | 268 | """Check _LinkFile handling.""" |
| 263 | 269 | ||
| 264 | def LinkFile(self, src, dest): | 270 | def LinkFile(self, src, dest): |
| 265 | return project._LinkFile(self.worktree, src, self.topdir, dest) | 271 | return project._LinkFile(self.worktree, src, self.topdir, dest) |
| 266 | 272 | ||
| 267 | def test_basic(self): | 273 | def test_basic(self): |
| 268 | """Basic test of linking a file from a project into the toplevel.""" | 274 | """Basic test of linking a file from a project into the toplevel.""" |
| 269 | src = os.path.join(self.worktree, 'foo.txt') | 275 | src = os.path.join(self.worktree, "foo.txt") |
| 270 | self.touch(src) | 276 | self.touch(src) |
| 271 | lf = self.LinkFile('foo.txt', 'foo') | 277 | lf = self.LinkFile("foo.txt", "foo") |
| 272 | lf._Link() | 278 | lf._Link() |
| 273 | dest = os.path.join(self.topdir, 'foo') | 279 | dest = os.path.join(self.topdir, "foo") |
| 274 | self.assertExists(dest) | 280 | self.assertExists(dest) |
| 275 | self.assertTrue(os.path.islink(dest)) | 281 | self.assertTrue(os.path.islink(dest)) |
| 276 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 282 | self.assertEqual( |
| 277 | 283 | os.path.join("git-project", "foo.txt"), os.readlink(dest) | |
| 278 | def test_src_subdir(self): | 284 | ) |
| 279 | """Link to a file in a subdir of a project.""" | 285 | |
| 280 | src = os.path.join(self.worktree, 'bar', 'foo.txt') | 286 | def test_src_subdir(self): |
| 281 | os.makedirs(os.path.dirname(src)) | 287 | """Link to a file in a subdir of a project.""" |
| 282 | self.touch(src) | 288 | src = os.path.join(self.worktree, "bar", "foo.txt") |
| 283 | lf = self.LinkFile('bar/foo.txt', 'foo') | 289 | os.makedirs(os.path.dirname(src)) |
| 284 | lf._Link() | 290 | self.touch(src) |
| 285 | self.assertExists(os.path.join(self.topdir, 'foo')) | 291 | lf = self.LinkFile("bar/foo.txt", "foo") |
| 286 | 292 | lf._Link() | |
| 287 | def test_src_self(self): | 293 | self.assertExists(os.path.join(self.topdir, "foo")) |
| 288 | """Link to the project itself.""" | 294 | |
| 289 | dest = os.path.join(self.topdir, 'foo', 'bar') | 295 | def test_src_self(self): |
| 290 | lf = self.LinkFile('.', 'foo/bar') | 296 | """Link to the project itself.""" |
| 291 | lf._Link() | 297 | dest = os.path.join(self.topdir, "foo", "bar") |
| 292 | self.assertExists(dest) | 298 | lf = self.LinkFile(".", "foo/bar") |
| 293 | self.assertEqual(os.path.join('..', 'git-project'), os.readlink(dest)) | 299 | lf._Link() |
| 294 | 300 | self.assertExists(dest) | |
| 295 | def test_dest_subdir(self): | 301 | self.assertEqual(os.path.join("..", "git-project"), os.readlink(dest)) |
| 296 | """Link a file to a subdir of a checkout.""" | 302 | |
| 297 | src = os.path.join(self.worktree, 'foo.txt') | 303 | def test_dest_subdir(self): |
| 298 | self.touch(src) | 304 | """Link a file to a subdir of a checkout.""" |
| 299 | lf = self.LinkFile('foo.txt', 'sub/dir/foo/bar') | 305 | src = os.path.join(self.worktree, "foo.txt") |
| 300 | self.assertFalse(os.path.exists(os.path.join(self.topdir, 'sub'))) | 306 | self.touch(src) |
| 301 | lf._Link() | 307 | lf = self.LinkFile("foo.txt", "sub/dir/foo/bar") |
| 302 | self.assertExists(os.path.join(self.topdir, 'sub', 'dir', 'foo', 'bar')) | 308 | self.assertFalse(os.path.exists(os.path.join(self.topdir, "sub"))) |
| 303 | 309 | lf._Link() | |
| 304 | def test_src_block_relative(self): | 310 | self.assertExists(os.path.join(self.topdir, "sub", "dir", "foo", "bar")) |
| 305 | """Do not allow relative symlinks.""" | 311 | |
| 306 | BAD_SOURCES = ( | 312 | def test_src_block_relative(self): |
| 307 | './', | 313 | """Do not allow relative symlinks.""" |
| 308 | '..', | 314 | BAD_SOURCES = ( |
| 309 | '../', | 315 | "./", |
| 310 | 'foo/.', | 316 | "..", |
| 311 | 'foo/./bar', | 317 | "../", |
| 312 | 'foo/..', | 318 | "foo/.", |
| 313 | 'foo/../foo', | 319 | "foo/./bar", |
| 314 | ) | 320 | "foo/..", |
| 315 | for src in BAD_SOURCES: | 321 | "foo/../foo", |
| 316 | lf = self.LinkFile(src, 'foo') | 322 | ) |
| 317 | self.assertRaises(error.ManifestInvalidPathError, lf._Link) | 323 | for src in BAD_SOURCES: |
| 318 | 324 | lf = self.LinkFile(src, "foo") | |
| 319 | def test_update(self): | 325 | self.assertRaises(error.ManifestInvalidPathError, lf._Link) |
| 320 | """Make sure changed targets get updated.""" | 326 | |
| 321 | dest = os.path.join(self.topdir, 'sym') | 327 | def test_update(self): |
| 322 | 328 | """Make sure changed targets get updated.""" | |
| 323 | src = os.path.join(self.worktree, 'foo.txt') | 329 | dest = os.path.join(self.topdir, "sym") |
| 324 | self.touch(src) | 330 | |
| 325 | lf = self.LinkFile('foo.txt', 'sym') | 331 | src = os.path.join(self.worktree, "foo.txt") |
| 326 | lf._Link() | 332 | self.touch(src) |
| 327 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 333 | lf = self.LinkFile("foo.txt", "sym") |
| 328 | 334 | lf._Link() | |
| 329 | # Point the symlink somewhere else. | 335 | self.assertEqual( |
| 330 | os.unlink(dest) | 336 | os.path.join("git-project", "foo.txt"), os.readlink(dest) |
| 331 | platform_utils.symlink(self.tempdir, dest) | 337 | ) |
| 332 | lf._Link() | 338 | |
| 333 | self.assertEqual(os.path.join('git-project', 'foo.txt'), os.readlink(dest)) | 339 | # Point the symlink somewhere else. |
| 340 | os.unlink(dest) | ||
| 341 | platform_utils.symlink(self.tempdir, dest) | ||
| 342 | lf._Link() | ||
| 343 | self.assertEqual( | ||
| 344 | os.path.join("git-project", "foo.txt"), os.readlink(dest) | ||
| 345 | ) | ||
| 334 | 346 | ||
| 335 | 347 | ||
| 336 | class MigrateWorkTreeTests(unittest.TestCase): | 348 | class MigrateWorkTreeTests(unittest.TestCase): |
| 337 | """Check _MigrateOldWorkTreeGitDir handling.""" | 349 | """Check _MigrateOldWorkTreeGitDir handling.""" |
| 338 | 350 | ||
| 339 | _SYMLINKS = { | 351 | _SYMLINKS = { |
| 340 | 'config', 'description', 'hooks', 'info', 'logs', 'objects', | 352 | "config", |
| 341 | 'packed-refs', 'refs', 'rr-cache', 'shallow', 'svn', | 353 | "description", |
| 342 | } | 354 | "hooks", |
| 343 | _FILES = { | 355 | "info", |
| 344 | 'COMMIT_EDITMSG', 'FETCH_HEAD', 'HEAD', 'index', 'ORIG_HEAD', | 356 | "logs", |
| 345 | 'unknown-file-should-be-migrated', | 357 | "objects", |
| 346 | } | 358 | "packed-refs", |
| 347 | _CLEAN_FILES = { | 359 | "refs", |
| 348 | 'a-vim-temp-file~', '#an-emacs-temp-file#', | 360 | "rr-cache", |
| 349 | } | 361 | "shallow", |
| 350 | 362 | "svn", | |
| 351 | @classmethod | 363 | } |
| 352 | @contextlib.contextmanager | 364 | _FILES = { |
| 353 | def _simple_layout(cls): | 365 | "COMMIT_EDITMSG", |
| 354 | """Create a simple repo client checkout to test against.""" | 366 | "FETCH_HEAD", |
| 355 | with tempfile.TemporaryDirectory() as tempdir: | 367 | "HEAD", |
| 356 | tempdir = Path(tempdir) | 368 | "index", |
| 357 | 369 | "ORIG_HEAD", | |
| 358 | gitdir = tempdir / '.repo/projects/src/test.git' | 370 | "unknown-file-should-be-migrated", |
| 359 | gitdir.mkdir(parents=True) | 371 | } |
| 360 | cmd = ['git', 'init', '--bare', str(gitdir)] | 372 | _CLEAN_FILES = { |
| 361 | subprocess.check_call(cmd) | 373 | "a-vim-temp-file~", |
| 362 | 374 | "#an-emacs-temp-file#", | |
| 363 | dotgit = tempdir / 'src/test/.git' | 375 | } |
| 364 | dotgit.mkdir(parents=True) | 376 | |
| 365 | for name in cls._SYMLINKS: | 377 | @classmethod |
| 366 | (dotgit / name).symlink_to(f'../../../.repo/projects/src/test.git/{name}') | 378 | @contextlib.contextmanager |
| 367 | for name in cls._FILES | cls._CLEAN_FILES: | 379 | def _simple_layout(cls): |
| 368 | (dotgit / name).write_text(name) | 380 | """Create a simple repo client checkout to test against.""" |
| 369 | 381 | with tempfile.TemporaryDirectory() as tempdir: | |
| 370 | yield tempdir | 382 | tempdir = Path(tempdir) |
| 371 | 383 | ||
| 372 | def test_standard(self): | 384 | gitdir = tempdir / ".repo/projects/src/test.git" |
| 373 | """Migrate a standard checkout that we expect.""" | 385 | gitdir.mkdir(parents=True) |
| 374 | with self._simple_layout() as tempdir: | 386 | cmd = ["git", "init", "--bare", str(gitdir)] |
| 375 | dotgit = tempdir / 'src/test/.git' | 387 | subprocess.check_call(cmd) |
| 376 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | 388 | |
| 377 | 389 | dotgit = tempdir / "src/test/.git" | |
| 378 | # Make sure the dir was transformed into a symlink. | 390 | dotgit.mkdir(parents=True) |
| 379 | self.assertTrue(dotgit.is_symlink()) | 391 | for name in cls._SYMLINKS: |
| 380 | self.assertEqual(os.readlink(dotgit), os.path.normpath('../../.repo/projects/src/test.git')) | 392 | (dotgit / name).symlink_to( |
| 381 | 393 | f"../../../.repo/projects/src/test.git/{name}" | |
| 382 | # Make sure files were moved over. | 394 | ) |
| 383 | gitdir = tempdir / '.repo/projects/src/test.git' | 395 | for name in cls._FILES | cls._CLEAN_FILES: |
| 384 | for name in self._FILES: | 396 | (dotgit / name).write_text(name) |
| 385 | self.assertEqual(name, (gitdir / name).read_text()) | 397 | |
| 386 | # Make sure files were removed. | 398 | yield tempdir |
| 387 | for name in self._CLEAN_FILES: | 399 | |
| 388 | self.assertFalse((gitdir / name).exists()) | 400 | def test_standard(self): |
| 389 | 401 | """Migrate a standard checkout that we expect.""" | |
| 390 | def test_unknown(self): | 402 | with self._simple_layout() as tempdir: |
| 391 | """A checkout with unknown files should abort.""" | 403 | dotgit = tempdir / "src/test/.git" |
| 392 | with self._simple_layout() as tempdir: | 404 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) |
| 393 | dotgit = tempdir / 'src/test/.git' | 405 | |
| 394 | (tempdir / '.repo/projects/src/test.git/random-file').write_text('one') | 406 | # Make sure the dir was transformed into a symlink. |
| 395 | (dotgit / 'random-file').write_text('two') | 407 | self.assertTrue(dotgit.is_symlink()) |
| 396 | with self.assertRaises(error.GitError): | 408 | self.assertEqual( |
| 397 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | 409 | os.readlink(dotgit), |
| 398 | 410 | os.path.normpath("../../.repo/projects/src/test.git"), | |
| 399 | # Make sure no content was actually changed. | 411 | ) |
| 400 | self.assertTrue(dotgit.is_dir()) | 412 | |
| 401 | for name in self._FILES: | 413 | # Make sure files were moved over. |
| 402 | self.assertTrue((dotgit / name).is_file()) | 414 | gitdir = tempdir / ".repo/projects/src/test.git" |
| 403 | for name in self._CLEAN_FILES: | 415 | for name in self._FILES: |
| 404 | self.assertTrue((dotgit / name).is_file()) | 416 | self.assertEqual(name, (gitdir / name).read_text()) |
| 405 | for name in self._SYMLINKS: | 417 | # Make sure files were removed. |
| 406 | self.assertTrue((dotgit / name).is_symlink()) | 418 | for name in self._CLEAN_FILES: |
| 419 | self.assertFalse((gitdir / name).exists()) | ||
| 420 | |||
| 421 | def test_unknown(self): | ||
| 422 | """A checkout with unknown files should abort.""" | ||
| 423 | with self._simple_layout() as tempdir: | ||
| 424 | dotgit = tempdir / "src/test/.git" | ||
| 425 | (tempdir / ".repo/projects/src/test.git/random-file").write_text( | ||
| 426 | "one" | ||
| 427 | ) | ||
| 428 | (dotgit / "random-file").write_text("two") | ||
| 429 | with self.assertRaises(error.GitError): | ||
| 430 | project.Project._MigrateOldWorkTreeGitDir(str(dotgit)) | ||
| 431 | |||
| 432 | # Make sure no content was actually changed. | ||
| 433 | self.assertTrue(dotgit.is_dir()) | ||
| 434 | for name in self._FILES: | ||
| 435 | self.assertTrue((dotgit / name).is_file()) | ||
| 436 | for name in self._CLEAN_FILES: | ||
| 437 | self.assertTrue((dotgit / name).is_file()) | ||
| 438 | for name in self._SYMLINKS: | ||
| 439 | self.assertTrue((dotgit / name).is_symlink()) | ||
| 407 | 440 | ||
| 408 | 441 | ||
| 409 | class ManifestPropertiesFetchedCorrectly(unittest.TestCase): | 442 | class ManifestPropertiesFetchedCorrectly(unittest.TestCase): |
| 410 | """Ensure properties are fetched properly.""" | 443 | """Ensure properties are fetched properly.""" |
| 411 | 444 | ||
| 412 | def setUpManifest(self, tempdir): | 445 | def setUpManifest(self, tempdir): |
| 413 | repodir = os.path.join(tempdir, '.repo') | 446 | repodir = os.path.join(tempdir, ".repo") |
| 414 | manifest_dir = os.path.join(repodir, 'manifests') | 447 | manifest_dir = os.path.join(repodir, "manifests") |
| 415 | manifest_file = os.path.join( | 448 | manifest_file = os.path.join(repodir, manifest_xml.MANIFEST_FILE_NAME) |
| 416 | repodir, manifest_xml.MANIFEST_FILE_NAME) | 449 | os.mkdir(repodir) |
| 417 | local_manifest_dir = os.path.join( | 450 | os.mkdir(manifest_dir) |
| 418 | repodir, manifest_xml.LOCAL_MANIFESTS_DIR_NAME) | 451 | manifest = manifest_xml.XmlManifest(repodir, manifest_file) |
| 419 | os.mkdir(repodir) | ||
| 420 | os.mkdir(manifest_dir) | ||
| 421 | manifest = manifest_xml.XmlManifest(repodir, manifest_file) | ||
| 422 | 452 | ||
| 423 | return project.ManifestProject( | 453 | return project.ManifestProject( |
| 424 | manifest, 'test/manifest', os.path.join(tempdir, '.git'), tempdir) | 454 | manifest, "test/manifest", os.path.join(tempdir, ".git"), tempdir |
| 455 | ) | ||
| 425 | 456 | ||
| 426 | def test_manifest_config_properties(self): | 457 | def test_manifest_config_properties(self): |
| 427 | """Test we are fetching the manifest config properties correctly.""" | 458 | """Test we are fetching the manifest config properties correctly.""" |
| 428 | 459 | ||
| 429 | with TempGitTree() as tempdir: | 460 | with TempGitTree() as tempdir: |
| 430 | fakeproj = self.setUpManifest(tempdir) | 461 | fakeproj = self.setUpManifest(tempdir) |
| 431 | 462 | ||
| 432 | # Set property using the expected Set method, then ensure | 463 | # Set property using the expected Set method, then ensure |
| 433 | # the porperty functions are using the correct Get methods. | 464 | # the porperty functions are using the correct Get methods. |
| 434 | fakeproj.config.SetString( | 465 | fakeproj.config.SetString( |
| 435 | 'manifest.standalone', 'https://chicken/manifest.git') | 466 | "manifest.standalone", "https://chicken/manifest.git" |
| 436 | self.assertEqual( | 467 | ) |
| 437 | fakeproj.standalone_manifest_url, 'https://chicken/manifest.git') | 468 | self.assertEqual( |
| 469 | fakeproj.standalone_manifest_url, "https://chicken/manifest.git" | ||
| 470 | ) | ||
| 438 | 471 | ||
| 439 | fakeproj.config.SetString('manifest.groups', 'test-group, admin-group') | 472 | fakeproj.config.SetString( |
| 440 | self.assertEqual(fakeproj.manifest_groups, 'test-group, admin-group') | 473 | "manifest.groups", "test-group, admin-group" |
| 474 | ) | ||
| 475 | self.assertEqual( | ||
| 476 | fakeproj.manifest_groups, "test-group, admin-group" | ||
| 477 | ) | ||
| 441 | 478 | ||
| 442 | fakeproj.config.SetString('repo.reference', 'mirror/ref') | 479 | fakeproj.config.SetString("repo.reference", "mirror/ref") |
| 443 | self.assertEqual(fakeproj.reference, 'mirror/ref') | 480 | self.assertEqual(fakeproj.reference, "mirror/ref") |
| 444 | 481 | ||
| 445 | fakeproj.config.SetBoolean('repo.dissociate', False) | 482 | fakeproj.config.SetBoolean("repo.dissociate", False) |
| 446 | self.assertFalse(fakeproj.dissociate) | 483 | self.assertFalse(fakeproj.dissociate) |
| 447 | 484 | ||
| 448 | fakeproj.config.SetBoolean('repo.archive', False) | 485 | fakeproj.config.SetBoolean("repo.archive", False) |
| 449 | self.assertFalse(fakeproj.archive) | 486 | self.assertFalse(fakeproj.archive) |
| 450 | 487 | ||
| 451 | fakeproj.config.SetBoolean('repo.mirror', False) | 488 | fakeproj.config.SetBoolean("repo.mirror", False) |
| 452 | self.assertFalse(fakeproj.mirror) | 489 | self.assertFalse(fakeproj.mirror) |
| 453 | 490 | ||
| 454 | fakeproj.config.SetBoolean('repo.worktree', False) | 491 | fakeproj.config.SetBoolean("repo.worktree", False) |
| 455 | self.assertFalse(fakeproj.use_worktree) | 492 | self.assertFalse(fakeproj.use_worktree) |
| 456 | 493 | ||
| 457 | fakeproj.config.SetBoolean('repo.clonebundle', False) | 494 | fakeproj.config.SetBoolean("repo.clonebundle", False) |
| 458 | self.assertFalse(fakeproj.clone_bundle) | 495 | self.assertFalse(fakeproj.clone_bundle) |
| 459 | 496 | ||
| 460 | fakeproj.config.SetBoolean('repo.submodules', False) | 497 | fakeproj.config.SetBoolean("repo.submodules", False) |
| 461 | self.assertFalse(fakeproj.submodules) | 498 | self.assertFalse(fakeproj.submodules) |
| 462 | 499 | ||
| 463 | fakeproj.config.SetBoolean('repo.git-lfs', False) | 500 | fakeproj.config.SetBoolean("repo.git-lfs", False) |
| 464 | self.assertFalse(fakeproj.git_lfs) | 501 | self.assertFalse(fakeproj.git_lfs) |
| 465 | 502 | ||
| 466 | fakeproj.config.SetBoolean('repo.superproject', False) | 503 | fakeproj.config.SetBoolean("repo.superproject", False) |
| 467 | self.assertFalse(fakeproj.use_superproject) | 504 | self.assertFalse(fakeproj.use_superproject) |
| 468 | 505 | ||
| 469 | fakeproj.config.SetBoolean('repo.partialclone', False) | 506 | fakeproj.config.SetBoolean("repo.partialclone", False) |
| 470 | self.assertFalse(fakeproj.partial_clone) | 507 | self.assertFalse(fakeproj.partial_clone) |
| 471 | 508 | ||
| 472 | fakeproj.config.SetString('repo.depth', '48') | 509 | fakeproj.config.SetString("repo.depth", "48") |
| 473 | self.assertEqual(fakeproj.depth, '48') | 510 | self.assertEqual(fakeproj.depth, "48") |
| 474 | 511 | ||
| 475 | fakeproj.config.SetString('repo.clonefilter', 'blob:limit=10M') | 512 | fakeproj.config.SetString("repo.clonefilter", "blob:limit=10M") |
| 476 | self.assertEqual(fakeproj.clone_filter, 'blob:limit=10M') | 513 | self.assertEqual(fakeproj.clone_filter, "blob:limit=10M") |
| 477 | 514 | ||
| 478 | fakeproj.config.SetString('repo.partialcloneexclude', 'third_party/big_repo') | 515 | fakeproj.config.SetString( |
| 479 | self.assertEqual(fakeproj.partial_clone_exclude, 'third_party/big_repo') | 516 | "repo.partialcloneexclude", "third_party/big_repo" |
| 517 | ) | ||
| 518 | self.assertEqual( | ||
| 519 | fakeproj.partial_clone_exclude, "third_party/big_repo" | ||
| 520 | ) | ||
| 480 | 521 | ||
| 481 | fakeproj.config.SetString('manifest.platform', 'auto') | 522 | fakeproj.config.SetString("manifest.platform", "auto") |
| 482 | self.assertEqual(fakeproj.manifest_platform, 'auto') | 523 | self.assertEqual(fakeproj.manifest_platform, "auto") |
