sequenceeditor: use qtutils.SimpleTask instead of a raw Thread
[git-cola.git] / test / git_test.py
bloba7f8d5c61716e8a2b8a50c9205850cd8c06301b7
1 """Test the cola.git module"""
2 # pylint: disable=redefined-outer-name
4 import os
5 import pathlib
7 from cola import git
8 from cola.git import STDOUT
10 from .helper import patch
13 # 16k+1 bytes to exhaust any output buffers.
14 BUFFER_SIZE = (16 * 1024) + 1
17 @patch('cola.git.is_git_dir')
18 def test_find_git_dir_None(is_git_dir):
19 paths = git.find_git_directory(None)
21 assert not is_git_dir.called
22 assert paths.git_dir is None
23 assert paths.git_file is None
24 assert paths.worktree is None
27 @patch('cola.git.is_git_dir')
28 def test_find_git_dir_empty_string(is_git_dir):
29 paths = git.find_git_directory('')
31 assert not is_git_dir.called
32 assert paths.git_dir is None
33 assert paths.git_file is None
34 assert paths.worktree is None
37 @patch('cola.git.is_git_dir')
38 def test_find_git_dir_never_found(is_git_dir):
39 is_git_dir.return_value = False
41 paths = git.find_git_directory(str(pathlib.Path('/does/not/exist').resolve()))
43 assert is_git_dir.called
44 assert paths.git_dir is None
45 assert paths.git_file is None
46 assert paths.worktree is None
48 expect = 8
49 actual = is_git_dir.call_count
50 assert expect == actual
51 is_git_dir.assert_has_calls([
52 ((str(pathlib.Path('/does/not/exist').resolve()),), {}),
53 ((str(pathlib.Path('/does/not/exist/.git').resolve()),), {}),
54 ((str(pathlib.Path('/does/not').resolve()),), {}),
55 ((str(pathlib.Path('/does/not/.git').resolve()),), {}),
56 ((str(pathlib.Path('/does').resolve()),), {}),
57 ((str(pathlib.Path('/does/.git').resolve()),), {}),
58 ((str(pathlib.Path('/').resolve()),), {}),
59 ((str(pathlib.Path('/.git').resolve()),), {}),
63 @patch('cola.git.is_git_dir')
64 def test_find_git_dir_found_right_away(is_git_dir):
65 git_dir = str(pathlib.Path('/seems/to/exist/.git').resolve())
66 worktree = str(pathlib.Path('/seems/to/exist').resolve())
67 is_git_dir.return_value = True
69 paths = git.find_git_directory(git_dir)
71 assert is_git_dir.called
72 assert git_dir == paths.git_dir
73 assert paths.git_file is None
74 assert worktree == paths.worktree
77 @patch('cola.git.is_git_dir')
78 def test_find_git_does_discovery(is_git_dir):
79 git_dir = str(pathlib.Path('/the/root/.git').resolve())
80 worktree = str(pathlib.Path('/the/root').resolve())
81 is_git_dir.side_effect = lambda x: x == git_dir
83 paths = git.find_git_directory('/the/root/sub/dir')
85 assert git_dir == paths.git_dir
86 assert paths.git_file is None
87 assert worktree == paths.worktree
90 @patch('cola.git.read_git_file')
91 @patch('cola.git.is_git_file')
92 @patch('cola.git.is_git_dir')
93 def test_find_git_honors_git_files(is_git_dir, is_git_file, read_git_file):
94 git_file = str(pathlib.Path('/the/root/.git').resolve())
95 worktree = str(pathlib.Path('/the/root').resolve())
96 git_dir = str(pathlib.Path('/super/module/.git/modules/root').resolve())
98 is_git_dir.side_effect = lambda x: x == git_file
99 is_git_file.side_effect = lambda x: x == git_file
100 read_git_file.return_value = git_dir
102 paths = git.find_git_directory(str(pathlib.Path('/the/root/sub/dir').resolve()))
104 assert git_dir == paths.git_dir
105 assert git_file == paths.git_file
106 assert worktree == paths.worktree
108 expect = 6
109 actual = is_git_dir.call_count
110 assert expect == actual
111 is_git_dir.assert_has_calls([
112 ((str(pathlib.Path('/the/root/sub/dir').resolve()),), {}),
113 ((str(pathlib.Path('/the/root/sub/dir/.git').resolve()),), {}),
114 ((str(pathlib.Path('/the/root/sub').resolve()),), {}),
115 ((str(pathlib.Path('/the/root/sub/.git').resolve()),), {}),
116 ((str(pathlib.Path('/the/root').resolve()),), {}),
117 ((str(pathlib.Path('/the/root/.git').resolve()),), {}),
119 read_git_file.assert_called_once_with(git_file)
122 @patch('cola.core.getenv')
123 @patch('cola.git.is_git_dir')
124 def test_find_git_honors_ceiling_dirs(is_git_dir, getenv):
125 git_dir = str(pathlib.Path('/ceiling/.git').resolve())
126 ceiling = os.pathsep.join(
127 str(pathlib.Path(path).resolve())
128 for path in ('/tmp', '/ceiling', '/other/ceiling')
130 is_git_dir.side_effect = lambda x: x == git_dir
132 def mock_getenv(k, v=None):
133 if k == 'GIT_CEILING_DIRECTORIES':
134 return ceiling
135 return v
137 getenv.side_effect = mock_getenv
138 paths = git.find_git_directory(str(pathlib.Path('/ceiling/sub/dir').resolve()))
140 assert paths.git_dir is None
141 assert paths.git_file is None
142 assert paths.worktree is None
143 assert is_git_dir.call_count == 4
144 is_git_dir.assert_has_calls([
145 ((str(pathlib.Path('/ceiling/sub/dir').resolve()),), {}),
146 ((str(pathlib.Path('/ceiling/sub/dir/.git').resolve()),), {}),
147 ((str(pathlib.Path('/ceiling/sub').resolve()),), {}),
148 ((str(pathlib.Path('/ceiling/sub/.git').resolve()),), {}),
152 @patch('cola.core.islink')
153 @patch('cola.core.isdir')
154 @patch('cola.core.isfile')
155 def test_is_git_dir_finds_linked_repository(isfile, isdir, islink):
156 dirs = {
157 str(pathlib.Path(directory).resolve())
158 for directory in [
159 '/foo',
160 '/foo/.git',
161 '/foo/.git/refs',
162 '/foo/.git/objects',
163 '/foo/.git/worktrees',
164 '/foo/.git/worktrees/foo',
167 files = {
168 str(pathlib.Path(file).resolve())
169 for file in [
170 '/foo/.git/HEAD',
171 '/foo/.git/worktrees/foo/HEAD',
172 '/foo/.git/worktrees/foo/index',
173 '/foo/.git/worktrees/foo/commondir',
174 '/foo/.git/worktrees/foo/gitdir',
177 islink.return_value = False
178 isfile.side_effect = lambda x: x in files
179 isdir.side_effect = lambda x: x in dirs
181 assert git.is_git_dir(str(pathlib.Path('/foo/.git/worktrees/foo').resolve()))
182 assert git.is_git_dir(str(pathlib.Path('/foo/.git').resolve()))
185 @patch('cola.core.getenv')
186 @patch('cola.git.is_git_dir')
187 def test_find_git_worktree_from_GIT_DIR(is_git_dir, getenv):
188 git_dir = str(pathlib.Path('/repo/.git').resolve())
189 worktree = str(pathlib.Path('/repo').resolve())
190 is_git_dir.return_value = True
191 getenv.side_effect = lambda x: x == 'GIT_DIR' and git_dir or None
193 paths = git.find_git_directory(git_dir)
194 assert is_git_dir.called
195 assert git_dir == paths.git_dir
196 assert paths.git_file is None
197 assert worktree == paths.worktree
200 @patch('cola.git.is_git_dir')
201 def test_finds_no_worktree_from_bare_repo(is_git_dir):
202 git_dir = str(pathlib.Path('/repos/bare.git').resolve())
203 worktree = None
204 is_git_dir.return_value = True
206 paths = git.find_git_directory(git_dir)
207 assert is_git_dir.called
208 assert git_dir == paths.git_dir
209 assert paths.git_file is None
210 assert worktree == paths.worktree
213 @patch('cola.core.getenv')
214 @patch('cola.git.is_git_dir')
215 def test_find_git_directory_uses_GIT_WORK_TREE(is_git_dir, getenv):
216 git_dir = str(pathlib.Path('/repo/worktree/.git').resolve())
217 worktree = str(pathlib.Path('/repo/worktree').resolve())
219 def is_git_dir_func(path):
220 return path == git_dir
222 is_git_dir.side_effect = is_git_dir_func
224 def getenv_func(name):
225 if name == 'GIT_WORK_TREE':
226 return worktree
227 return None
229 getenv.side_effect = getenv_func
231 paths = git.find_git_directory(worktree)
232 assert is_git_dir.called
233 assert git_dir == paths.git_dir
234 assert paths.git_file is None
235 assert worktree == paths.worktree
238 @patch('cola.core.getenv')
239 @patch('cola.git.is_git_dir')
240 def test_uses_cwd_for_worktree_with_GIT_DIR(is_git_dir, getenv):
241 git_dir = str(pathlib.Path('/repo/.yadm/repo.git').resolve())
242 worktree = str(pathlib.Path('/repo').resolve())
244 def getenv_func(name):
245 if name == 'GIT_DIR':
246 return git_dir
247 return None
249 getenv.side_effect = getenv_func
251 def is_git_dir_func(path):
252 return path == git_dir
254 is_git_dir.side_effect = is_git_dir_func
256 paths = git.find_git_directory(worktree)
257 assert is_git_dir.called
258 assert getenv.called
259 assert git_dir == paths.git_dir
260 assert paths.git_file is None
261 assert worktree == paths.worktree
264 def test_transform_kwargs_empty():
265 expect = []
266 actual = git.transform_kwargs(foo=None, bar=False)
267 assert expect == actual
270 def test_transform_kwargs_single_dash_from_True():
271 """Single dash for one-character True"""
272 expect = ['-a']
273 actual = git.transform_kwargs(a=True)
274 assert expect == actual
277 def test_transform_kwargs_no_single_dash_from_False():
278 """No single-dash for False"""
279 expect = []
280 actual = git.transform_kwargs(a=False)
281 assert expect == actual
284 def test_transform_kwargs_double_dash_from_True():
285 """Double-dash for longer True"""
286 expect = ['--abc']
287 actual = git.transform_kwargs(abc=True)
288 assert expect == actual
291 def test_transform_kwargs_no_double_dash_from_True():
292 """No double-dash for False"""
293 expect = []
294 actual = git.transform_kwargs(abc=False)
295 assert expect == actual
298 def test_transform_kwargs_single_dash_int():
299 expect = ['-a1']
300 actual = git.transform_kwargs(a=1)
301 assert expect == actual
304 def test_transform_kwargs_double_dash_int():
305 expect = ['--abc=1']
306 actual = git.transform_kwargs(abc=1)
307 assert expect == actual
310 def test_transform_kwargs_single_dash_float():
311 expect = ['-a1.5']
312 actual = git.transform_kwargs(a=1.5)
313 assert expect == actual
316 def test_transform_kwargs_double_dash_float():
317 expect = ['--abc=1.5']
318 actual = git.transform_kwargs(abc=1.5)
319 assert expect == actual
322 def test_transform_kwargs_single_dash_string():
323 expect = ['-abc']
324 actual = git.transform_kwargs(a='bc')
325 assert expect == actual
328 def test_transform_double_single_dash_string():
329 expect = ['--abc=def']
330 actual = git.transform_kwargs(abc='def')
331 assert expect == actual
334 def test_version():
335 """Test running 'git version'"""
336 gitcmd = git.Git()
337 version = gitcmd.version()[STDOUT]
338 assert version.startswith('git version')
341 def test_stdout():
342 """Test overflowing the stdout buffer"""
343 # Write to stdout only
344 code = r'import sys; value = "\0" * %d; sys.stdout.write(value);' % BUFFER_SIZE
345 status, out, err = git.Git.execute(['python', '-c', code], _raw=True)
347 assert status == 0
348 expect = BUFFER_SIZE
349 actual = len(out)
350 assert expect == actual
352 expect = 0
353 actual = len(err)
354 assert expect == actual
357 def test_stderr():
358 """Test that stderr is seen"""
359 # Write to stderr and capture it
360 code = (
361 r'import sys;' r'value = "\0" * %d;' r'sys.stderr.write(value);'
362 ) % BUFFER_SIZE
364 status, out, err = git.Git.execute(['python', '-c', code], _raw=True)
366 expect = 0
367 actual = status
368 assert expect == actual
370 expect = 0
371 actual = len(out)
372 assert expect == actual
374 expect = BUFFER_SIZE
375 actual = len(err)
376 assert expect == actual
379 def test_stdout_and_stderr():
380 """Test ignoring stderr when stdout+stderr are provided (v2)"""
381 # Write to stdout and stderr but only capture stdout
382 code = (
383 r'import sys;'
384 r'value = "\0" * %d;'
385 r'sys.stdout.write(value);'
386 r'sys.stderr.write(value);'
387 ) % BUFFER_SIZE
389 status, out, err = git.Git.execute(['python', '-c', code], _raw=True)
391 expect = 0
392 actual = status
393 assert expect == actual
395 expect = BUFFER_SIZE
396 actual = len(out)
397 assert expect == actual
399 actual = len(err)
400 assert expect == actual
403 def test_it_doesnt_deadlock():
404 """Test that we don't deadlock with both stderr and stdout"""
405 code = (
406 r'import sys;'
407 r'value = "\0" * %d;'
408 r'sys.stderr.write(value);'
409 r'sys.stdout.write(value);'
410 ) % BUFFER_SIZE
412 status, out, err = git.Git.execute(['python', '-c', code], _raw=True)
414 expect = 0
415 actual = status
416 assert expect == actual
418 expect = '\0' * BUFFER_SIZE
419 actual = out
420 assert expect == actual
422 actual = err
423 assert expect == actual