summaryrefslogtreecommitdiffstats
path: root/tests/test_project.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_project.py')
-rw-r--r--tests/test_project.py855
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
33def TempGitTree(): 33def 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
51class FakeProject(object): 51class 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
65class ReviewableBranchTests(unittest.TestCase): 67class 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
106class CopyLinkTestCase(unittest.TestCase): 110class 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
158class CopyFile(CopyLinkTestCase): 163class 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
261class LinkFile(CopyLinkTestCase): 267class 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
336class MigrateWorkTreeTests(unittest.TestCase): 348class 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
409class ManifestPropertiesFetchedCorrectly(unittest.TestCase): 442class 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")