1#!/usr/bin/env python 2"""Tests various operations using the cola.git module 3""" 4from __future__ import absolute_import, division, unicode_literals 5import unittest 6 7try: 8 from unittest.mock import patch 9except ImportError: 10 from mock import patch 11 12from cola import git 13from cola.git import STDOUT 14 15 16class GitModuleTestCase(unittest.TestCase): 17 @patch('cola.git.is_git_dir') 18 def test_find_git_dir_None(self, is_git_dir): 19 20 paths = git.find_git_directory(None) 21 22 self.assertFalse(is_git_dir.called) 23 self.assertEqual(None, paths.git_dir) 24 self.assertEqual(None, paths.git_file) 25 self.assertEqual(None, paths.worktree) 26 27 @patch('cola.git.is_git_dir') 28 def test_find_git_dir_empty_string(self, is_git_dir): 29 30 paths = git.find_git_directory('') 31 32 self.assertFalse(is_git_dir.called) 33 self.assertEqual(None, paths.git_dir) 34 self.assertEqual(None, paths.git_file) 35 self.assertEqual(None, paths.worktree) 36 37 @patch('cola.git.is_git_dir') 38 def test_find_git_dir_never_found(self, is_git_dir): 39 is_git_dir.return_value = False 40 41 paths = git.find_git_directory('/does/not/exist') 42 43 self.assertTrue(is_git_dir.called) 44 self.assertEqual(None, paths.git_dir) 45 self.assertEqual(None, paths.git_file) 46 self.assertEqual(None, paths.worktree) 47 48 self.assertEqual(8, is_git_dir.call_count) 49 kwargs = {} 50 is_git_dir.assert_has_calls( 51 [ 52 (('/does/not/exist',), kwargs), 53 (('/does/not/exist/.git',), kwargs), 54 (('/does/not',), kwargs), 55 (('/does/not/.git',), kwargs), 56 (('/does',), kwargs), 57 (('/does/.git',), kwargs), 58 (('/',), kwargs), 59 (('/.git',), kwargs), 60 ] 61 ) 62 63 @patch('cola.git.is_git_dir') 64 def test_find_git_dir_found_right_away(self, is_git_dir): 65 git_dir = '/seems/to/exist/.git' 66 worktree = '/seems/to/exist' 67 is_git_dir.return_value = True 68 69 paths = git.find_git_directory(git_dir) 70 71 self.assertTrue(is_git_dir.called) 72 self.assertEqual(git_dir, paths.git_dir) 73 self.assertEqual(None, paths.git_file) 74 self.assertEqual(worktree, paths.worktree) 75 76 @patch('cola.git.is_git_dir') 77 def test_find_git_does_discovery(self, is_git_dir): 78 git_dir = '/the/root/.git' 79 worktree = '/the/root' 80 is_git_dir.side_effect = lambda x: x == git_dir 81 82 paths = git.find_git_directory('/the/root/sub/dir') 83 84 self.assertEqual(git_dir, paths.git_dir) 85 self.assertEqual(None, paths.git_file) 86 self.assertEqual(worktree, paths.worktree) 87 88 @patch('cola.git.read_git_file') 89 @patch('cola.git.is_git_file') 90 @patch('cola.git.is_git_dir') 91 def test_find_git_honors_git_files(self, is_git_dir, is_git_file, read_git_file): 92 git_file = '/the/root/.git' 93 worktree = '/the/root' 94 git_dir = '/super/module/.git/modules/root' 95 96 is_git_dir.side_effect = lambda x: x == git_file 97 is_git_file.side_effect = lambda x: x == git_file 98 read_git_file.return_value = git_dir 99 100 paths = git.find_git_directory('/the/root/sub/dir') 101 102 self.assertEqual(git_dir, paths.git_dir) 103 self.assertEqual(git_file, paths.git_file) 104 self.assertEqual(worktree, paths.worktree) 105 106 kwargs = {} 107 self.assertEqual(6, is_git_dir.call_count) 108 is_git_dir.assert_has_calls( 109 [ 110 (('/the/root/sub/dir',), kwargs), 111 (('/the/root/sub/dir/.git',), kwargs), 112 (('/the/root/sub',), kwargs), 113 (('/the/root/sub/.git',), kwargs), 114 (('/the/root',), kwargs), 115 (('/the/root/.git',), kwargs), 116 ] 117 ) 118 read_git_file.assert_called_once_with('/the/root/.git') 119 120 @patch('cola.core.getenv') 121 @patch('cola.git.is_git_dir') 122 def test_find_git_honors_ceiling_dirs(self, is_git_dir, getenv): 123 124 git_dir = '/ceiling/.git' 125 ceiling = '/tmp:/ceiling:/other/ceiling' 126 is_git_dir.side_effect = lambda x: x == git_dir 127 128 def mock_getenv(k, v=None): 129 if k == 'GIT_CEILING_DIRECTORIES': 130 return ceiling 131 return v 132 133 getenv.side_effect = mock_getenv 134 135 paths = git.find_git_directory('/ceiling/sub/dir') 136 137 self.assertEqual(None, paths.git_dir) 138 self.assertEqual(None, paths.git_file) 139 self.assertEqual(None, paths.worktree) 140 141 self.assertEqual(4, is_git_dir.call_count) 142 kwargs = {} 143 is_git_dir.assert_has_calls( 144 [ 145 (('/ceiling/sub/dir',), kwargs), 146 (('/ceiling/sub/dir/.git',), kwargs), 147 (('/ceiling/sub',), kwargs), 148 (('/ceiling/sub/.git',), kwargs), 149 ] 150 ) 151 152 @patch('cola.core.islink') 153 @patch('cola.core.isdir') 154 @patch('cola.core.isfile') 155 def test_is_git_dir_finds_linked_repository(self, isfile, isdir, islink): 156 dirs = set( 157 [ 158 '/foo', 159 '/foo/.git', 160 '/foo/.git/refs', 161 '/foo/.git/objects', 162 '/foo/.git/worktrees', 163 '/foo/.git/worktrees/foo', 164 ] 165 ) 166 files = set( 167 [ 168 '/foo/.git/HEAD', 169 '/foo/.git/worktrees/foo/HEAD', 170 '/foo/.git/worktrees/foo/index', 171 '/foo/.git/worktrees/foo/commondir', 172 '/foo/.git/worktrees/foo/gitdir', 173 ] 174 ) 175 islink.return_value = False 176 isfile.side_effect = lambda x: x in files 177 isdir.side_effect = lambda x: x in dirs 178 179 self.assertTrue(git.is_git_dir('/foo/.git/worktrees/foo')) 180 self.assertTrue(git.is_git_dir('/foo/.git')) 181 182 @patch('cola.core.getenv') 183 @patch('cola.git.is_git_dir') 184 def test_find_git_worktree_from_GIT_DIR(self, is_git_dir, getenv): 185 git_dir = '/repo/.git' 186 worktree = '/repo' 187 is_git_dir.return_value = True 188 getenv.side_effect = lambda x: x == 'GIT_DIR' and '/repo/.git' or None 189 190 paths = git.find_git_directory(git_dir) 191 self.assertTrue(is_git_dir.called) 192 self.assertEqual(git_dir, paths.git_dir) 193 self.assertEqual(None, paths.git_file) 194 self.assertEqual(worktree, paths.worktree) 195 196 @patch('cola.git.is_git_dir') 197 def test_finds_no_worktree_from_bare_repo(self, is_git_dir): 198 git_dir = '/repos/bare.git' 199 worktree = None 200 is_git_dir.return_value = True 201 202 paths = git.find_git_directory(git_dir) 203 self.assertTrue(is_git_dir.called) 204 self.assertEqual(git_dir, paths.git_dir) 205 self.assertEqual(None, paths.git_file) 206 self.assertEqual(worktree, paths.worktree) 207 208 @patch('cola.core.getenv') 209 @patch('cola.git.is_git_dir') 210 def test_find_git_directory_uses_GIT_WORK_TREE(self, is_git_dir, getenv): 211 git_dir = '/repo/worktree/.git' 212 worktree = '/repo/worktree' 213 214 def is_git_dir_fn(path): 215 return path == git_dir 216 217 is_git_dir.side_effect = is_git_dir_fn 218 219 def getenv_fn(name): 220 if name == 'GIT_WORK_TREE': 221 return worktree 222 return None 223 224 getenv.side_effect = getenv_fn 225 226 paths = git.find_git_directory(worktree) 227 self.assertTrue(is_git_dir.called) 228 self.assertEqual(git_dir, paths.git_dir) 229 self.assertEqual(None, paths.git_file) 230 self.assertEqual(worktree, paths.worktree) 231 232 @patch('cola.core.getenv') 233 @patch('cola.git.is_git_dir') 234 def test_uses_cwd_for_worktree_with_GIT_DIR(self, is_git_dir, getenv): 235 git_dir = '/repo/.yadm/repo.git' 236 worktree = '/repo' 237 238 def getenv_fn(name): 239 if name == 'GIT_DIR': 240 return git_dir 241 return None 242 243 getenv.side_effect = getenv_fn 244 245 def is_git_dir_fn(path): 246 return path == git_dir 247 248 is_git_dir.side_effect = is_git_dir_fn 249 250 paths = git.find_git_directory(worktree) 251 self.assertTrue(is_git_dir.called) 252 self.assertTrue(getenv.called) 253 self.assertEqual(git_dir, paths.git_dir) 254 self.assertEqual(None, paths.git_file) 255 self.assertEqual(worktree, paths.worktree) 256 257 258class GitCommandTest(unittest.TestCase): 259 """Runs tests using a git.Git instance""" 260 261 def setUp(self): 262 """Creates a git.Git instance for later use""" 263 self.git = git.Git() 264 265 def test_transform_kwargs_empty(self): 266 expect = [] 267 actual = git.transform_kwargs(foo=None, bar=False) 268 self.assertEqual(expect, actual) 269 270 def test_transform_kwargs_single_dash_from_True(self): 271 """Single dash for one-character True""" 272 expect = ['-a'] 273 actual = git.transform_kwargs(a=True) 274 self.assertEqual(expect, actual) 275 276 def test_transform_kwargs_no_single_dash_from_False(self): 277 """No single-dash for False""" 278 expect = [] 279 actual = git.transform_kwargs(a=False) 280 self.assertEqual(expect, actual) 281 282 def test_transform_kwargs_double_dash_from_True(self): 283 """Double-dash for longer True""" 284 expect = ['--abc'] 285 actual = git.transform_kwargs(abc=True) 286 self.assertEqual(expect, actual) 287 288 def test_transform_kwargs_no_double_dash_from_True(self): 289 """No double-dash for False""" 290 expect = [] 291 actual = git.transform_kwargs(abc=False) 292 self.assertEqual(expect, actual) 293 294 def test_transform_kwargs_single_dash_int(self): 295 expect = ['-a1'] 296 actual = git.transform_kwargs(a=1) 297 self.assertEqual(expect, actual) 298 299 def test_transform_kwargs_double_dash_int(self): 300 expect = ['--abc=1'] 301 actual = git.transform_kwargs(abc=1) 302 self.assertEqual(expect, actual) 303 304 def test_transform_kwargs_single_dash_float(self): 305 expect = ['-a1.5'] 306 actual = git.transform_kwargs(a=1.5) 307 self.assertEqual(expect, actual) 308 309 def test_transform_kwargs_double_dash_float(self): 310 expect = ['--abc=1.5'] 311 actual = git.transform_kwargs(abc=1.5) 312 self.assertEqual(expect, actual) 313 314 def test_transform_kwargs_single_dash_string(self): 315 expect = ['-abc'] 316 actual = git.transform_kwargs(a='bc') 317 self.assertEqual(expect, actual) 318 319 def test_transform_double_single_dash_string(self): 320 expect = ['--abc=def'] 321 actual = git.transform_kwargs(abc='def') 322 self.assertEqual(expect, actual) 323 324 def test_version(self): 325 """Test running 'git version'""" 326 version = self.git.version()[STDOUT] 327 self.assertTrue(version.startswith('git version')) 328 329 def test_stdout(self): 330 """Test overflowing the stdout buffer""" 331 # Write to stdout only 332 code = r'import sys; s = "\0" * (1024 * 16 + 1); sys.stdout.write(s);' 333 status, out, err = git.Git.execute(['python', '-c', code], _raw=True) 334 self.assertEqual(status, 0) 335 self.assertEqual(len(out), 1024 * 16 + 1) 336 self.assertEqual(len(err), 0) 337 338 def test_stderr(self): 339 """Test that stderr is seen""" 340 # Write to stderr and capture it 341 code = r'import sys; s = "\0" * (1024 * 16 + 1); sys.stderr.write(s);' 342 status, out, err = git.Git.execute(['python', '-c', code], _raw=True) 343 self.assertEqual(status, 0) 344 self.assertEqual(len(out), 0) 345 self.assertEqual(len(err), 1024 * 16 + 1) 346 347 def test_stdout_and_stderr(self): 348 """Test ignoring stderr when stdout+stderr are provided (v2)""" 349 # Write to stdout and stderr but only capture stdout 350 code = ( 351 'import sys;' 352 's = "\\0" * (1024 * 16 + 1);' 353 'sys.stdout.write(s);' 354 'sys.stderr.write(s);' 355 ) 356 status, out, err = git.Git.execute(['python', '-c', code], _raw=True) 357 self.assertEqual(status, 0) 358 self.assertEqual(len(out), 1024 * 16 + 1) 359 self.assertEqual(len(err), 1024 * 16 + 1) 360 361 def test_it_doesnt_deadlock(self): 362 """Test that we don't deadlock with both stderr and stdout""" 363 # 16k+1 bytes to exhaust any output buffers 364 code = ( 365 'import sys;' 366 's = "\\0" * (1024 * 16 + 1);' 367 'sys.stderr.write(s);' 368 'sys.stdout.write(s);' 369 ) 370 status, out, err = git.Git.execute(['python', '-c', code], _raw=True) 371 self.assertEqual(status, 0) 372 self.assertEqual(out, '\0' * (1024 * 16 + 1)) 373 self.assertEqual(err, '\0' * (1024 * 16 + 1)) 374 375 376if __name__ == '__main__': 377 unittest.main() 378