improve test coverage for git submodules

This commit is contained in:
Alan Hamlett 2017-11-23 12:26:57 -08:00
parent 1d57270daf
commit 0ca8a936db
3 changed files with 155 additions and 70 deletions

View file

@ -0,0 +1,9 @@
[settings]
debug = false
api_key = 1090a6ae-855f-4be7-b8fb-3edbaf1aa3ec
[git]
submodules_disabled =
this/path/does/not/exist
\(invalid regex)
git/asubmodule
that/other/path

View file

@ -9,7 +9,6 @@ import logging
import os
import platform
import shutil
import sys
import tempfile
import time
from testfixtures import log_capture
@ -17,11 +16,11 @@ from wakatime.compat import u
from wakatime.constants import API_ERROR, SUCCESS
from wakatime.exceptions import NotYetImplemented
from wakatime.projects.base import BaseProject
from . import utils
from .utils import ANY, CustomResponse, json
from wakatime.projects.git import Git
from .utils import ANY, DynamicIterable, TestCase, CustomResponse, mock, json
class ProjectTestCase(utils.TestCase):
class ProjectTestCase(TestCase):
patch_these = [
'wakatime.packages.requests.adapters.HTTPAdapter.send',
'wakatime.offlinequeue.Queue.push',
@ -170,14 +169,17 @@ class ProjectTestCase(utils.TestCase):
entity=os.path.join(tempdir, 'git', 'emptyfile.txt'),
)
def test_ioerror_when_reading_git_branch(self):
@log_capture()
def test_ioerror_when_reading_git_branch(self, logs):
logging.disable(logging.NOTSET)
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
entity = os.path.join(tempdir, 'git', 'emptyfile.txt')
with utils.mock.patch('wakatime.projects.git.open') as mock_open:
with mock.patch('wakatime.projects.git.open') as mock_open:
mock_open.side_effect = IOError('')
self.shared(
@ -186,6 +188,11 @@ class ProjectTestCase(utils.TestCase):
entity=entity,
)
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = 'OSError' if self.isPy35OrNewer else 'IOError'
self.assertIn(expected, actual)
def test_git_detached_head_not_used_as_branch(self):
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-detached-head', os.path.join(tempdir, 'git'))
@ -200,16 +207,16 @@ class ProjectTestCase(utils.TestCase):
)
def test_svn_project_detected(self):
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
with utils.mock.patch('wakatime.projects.subversion.Subversion._has_xcode_tools') as mock_has_xcode:
with mock.patch('wakatime.projects.subversion.Subversion._has_xcode_tools') as mock_has_xcode:
mock_has_xcode.return_value = True
with utils.mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_popen:
with mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_popen:
stdout = open('tests/samples/output/svn').read()
stderr = ''
mock_popen.return_value = utils.DynamicIterable((stdout, stderr), max_calls=1)
mock_popen.return_value = DynamicIterable((stdout, stderr), max_calls=1)
expected = None if platform.system() == 'Windows' else 'svn'
self.shared(
@ -217,17 +224,20 @@ class ProjectTestCase(utils.TestCase):
entity='projects/svn/afolder/emptyfile.txt',
)
def test_svn_exception_handled(self):
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
@log_capture()
def test_svn_exception_handled(self, logs):
logging.disable(logging.NOTSET)
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
with utils.mock.patch('wakatime.projects.subversion.Subversion._has_xcode_tools') as mock_has_xcode:
with mock.patch('wakatime.projects.subversion.Subversion._has_xcode_tools') as mock_has_xcode:
mock_has_xcode.return_value = True
with utils.mock.patch('wakatime.projects.subversion.Popen') as mock_popen:
with mock.patch('wakatime.projects.subversion.Popen') as mock_popen:
mock_popen.side_effect = OSError('')
with utils.mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_communicate:
with mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_communicate:
mock_communicate.side_effect = OSError('')
self.shared(
@ -235,17 +245,20 @@ class ProjectTestCase(utils.TestCase):
entity='projects/svn/afolder/emptyfile.txt',
)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
def test_svn_on_mac_without_xcode_tools_installed(self):
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
with utils.mock.patch('wakatime.projects.subversion.platform.system') as mock_system:
with mock.patch('wakatime.projects.subversion.platform.system') as mock_system:
mock_system.return_value = 'Darwin'
with utils.mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_popen:
with mock.patch('wakatime.projects.subversion.Popen.communicate') as mock_popen:
stdout = open('tests/samples/output/svn').read()
stderr = ''
mock_popen.return_value = utils.DynamicIterable((stdout, stderr), raise_on_calls=[OSError('')])
mock_popen.return_value = DynamicIterable((stdout, stderr), raise_on_calls=[OSError('')])
self.shared(
expected_project=None,
@ -263,13 +276,13 @@ class ProjectTestCase(utils.TestCase):
args = ['--file', entity, '--config', config, '--time', now]
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
with utils.mock.patch('wakatime.projects.subversion.platform.system') as mock_system:
with mock.patch('wakatime.projects.subversion.platform.system') as mock_system:
mock_system.return_value = 'Darwin'
with utils.mock.patch('wakatime.projects.subversion.Popen') as mock_popen:
with mock.patch('wakatime.projects.subversion.Popen') as mock_popen:
stdout = open('tests/samples/output/svn').read()
stderr = ''
@ -297,7 +310,7 @@ class ProjectTestCase(utils.TestCase):
response.status_code = 0
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
now = u(int(time.time()))
@ -311,12 +324,15 @@ class ProjectTestCase(utils.TestCase):
self.assertEquals('hg', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals('test-hg-branch', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['branch'])
def test_ioerror_when_reading_mercurial_branch(self):
@log_capture()
def test_ioerror_when_reading_mercurial_branch(self, logs):
logging.disable(logging.NOTSET)
response = Response()
response.status_code = 0
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
with utils.mock.patch('wakatime.projects.git.Git.process') as mock_git:
with mock.patch('wakatime.projects.git.Git.process') as mock_git:
mock_git.return_value = False
now = u(int(time.time()))
@ -325,13 +341,18 @@ class ProjectTestCase(utils.TestCase):
args = ['--file', entity, '--config', config, '--time', now]
with utils.mock.patch('wakatime.projects.mercurial.open') as mock_open:
with mock.patch('wakatime.projects.mercurial.open') as mock_open:
mock_open.side_effect = IOError('')
execute(args)
self.assertEquals('hg', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals('default', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['branch'])
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = 'OSError' if self.isPy35OrNewer else 'IOError'
self.assertIn(expected, actual)
def test_git_submodule_detected(self):
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
@ -376,7 +397,7 @@ class ProjectTestCase(utils.TestCase):
config='git-submodules-disabled.cfg',
)
def test_git_submodule_detected_but_disabled_using_regex(self):
def test_git_submodule_detected_and_disabled_using_regex(self):
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
@ -391,7 +412,7 @@ class ProjectTestCase(utils.TestCase):
config='git-submodules-disabled-using-regex.cfg',
)
def test_git_submodule_detected_but_enabled_using_regex(self):
def test_git_submodule_detected_and_enabled_using_regex(self):
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
@ -406,6 +427,85 @@ class ProjectTestCase(utils.TestCase):
config='git-submodules-enabled-using-regex.cfg',
)
@log_capture()
def test_git_submodule_detected_with_invalid_regex(self, logs):
logging.disable(logging.NOTSET)
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
shutil.move(os.path.join(tempdir, 'git', 'asubmodule', 'dot_git'), os.path.join(tempdir, 'git', 'asubmodule', '.git'))
entity = os.path.join(tempdir, 'git', 'asubmodule', 'emptyfile.txt')
self.shared(
expected_project='git',
expected_branch='master',
entity=entity,
config='git-submodules-invalid-regex.cfg',
)
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = u('WakaTime WARNING Regex error (unbalanced parenthesis) for disable git submodules pattern: \\(invalid regex)')
if self.isPy35OrNewer:
expected = 'WakaTime WARNING Regex error (unbalanced parenthesis at position 15) for disable git submodules pattern: \\(invalid regex)'
self.assertEquals(expected, actual)
@log_capture()
def test_git_find_path_from_submodule(self, logs):
logging.disable(logging.NOTSET)
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
shutil.move(os.path.join(tempdir, 'git', 'asubmodule', 'dot_git'), os.path.join(tempdir, 'git', 'asubmodule', '.git'))
path = os.path.join(tempdir, 'git', 'asubmodule')
git = Git(None)
result = git._find_path_from_submodule(path)
expected = os.path.realpath(os.path.join(tempdir, 'git', '.git', 'modules', 'asubmodule'))
self.assertEquals(expected, result)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
@log_capture()
def test_git_find_path_from_submodule_handles_exceptions(self, logs):
logging.disable(logging.NOTSET)
tempdir = tempfile.mkdtemp()
shutil.copytree('tests/samples/projects/git-with-submodule', os.path.join(tempdir, 'git'))
shutil.move(os.path.join(tempdir, 'git', 'dot_git'), os.path.join(tempdir, 'git', '.git'))
shutil.move(os.path.join(tempdir, 'git', 'asubmodule', 'dot_git'), os.path.join(tempdir, 'git', 'asubmodule', '.git'))
with mock.patch('wakatime.projects.git.open') as mock_open:
mock_open.side_effect = UnicodeDecodeError('utf8', ''.encode('utf8'), 0, 0, '')
git = Git(None)
path = os.path.join(tempdir, 'git', 'asubmodule')
result = git._find_path_from_submodule(path)
self.assertIsNone(result)
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = 'UnicodeDecodeError'
self.assertIn(expected, actual)
with mock.patch('wakatime.projects.git.open') as mock_open:
mock_open.side_effect = IOError('')
git = Git(None)
path = os.path.join(tempdir, 'git', 'asubmodule')
result = git._find_path_from_submodule(path)
self.assertIsNone(result)
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = 'OSError' if self.isPy35OrNewer else 'IOError'
self.assertIn(expected, actual)
@log_capture()
def test_project_map(self, logs):
logging.disable(logging.NOTSET)
@ -424,12 +524,8 @@ class ProjectTestCase(utils.TestCase):
self.assertEquals('proj-map', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
expected = u('')
self.assertEquals(log_output, expected)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
@log_capture()
def test_project_map_group_usage(self, logs):
@ -449,37 +545,28 @@ class ProjectTestCase(utils.TestCase):
self.assertEquals('proj-map42', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
expected = u('')
self.assertEquals(log_output, expected)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
@log_capture()
def test_project_map_with_invalid_regex(self, logs):
logging.disable(logging.NOTSET)
response = Response()
response.status_code = 0
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = CustomResponse()
now = u(int(time.time()))
entity = 'tests/samples/projects/project_map42/emptyfile.txt'
config = 'tests/samples/configs/project_map_invalid.cfg'
args = ['--file', entity, '--config', config, '--time', now]
retval = execute(args)
self.assertEquals(retval, SUCCESS)
execute(args)
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
output = [u(' ').join(x) for x in logs.actual()]
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = u('WakaTime WARNING Regex error (unexpected end of regular expression) for projectmap pattern: invalid[({regex')
if self.isPy35OrNewer:
expected = u('WakaTime WARNING Regex error (unterminated character set at position 7) for projectmap pattern: invalid[({regex')
self.assertEquals(output[0], expected)
self.assertEquals(expected, actual)
@log_capture()
def test_project_map_with_replacement_group_index_error(self, logs):
@ -494,16 +581,13 @@ class ProjectTestCase(utils.TestCase):
config = 'tests/samples/configs/project_map_malformed.cfg'
args = ['--file', entity, '--config', config, '--time', now]
retval = execute(args)
self.assertEquals(retval, API_ERROR)
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
self.assertNothingPrinted()
actual = self.getLogOutput(logs)
expected = u('WakaTime WARNING Regex error (tuple index out of range) for projectmap pattern: proj-map{3}')
self.assertEquals(log_output, expected)
self.assertEquals(expected, actual)
@log_capture()
def test_project_map_allows_duplicate_keys(self, logs):
@ -523,12 +607,8 @@ class ProjectTestCase(utils.TestCase):
self.assertEquals('proj-map-duplicate-5', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
expected = u('')
self.assertEquals(log_output, expected)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
@log_capture()
def test_project_map_allows_colon_in_key(self, logs):
@ -548,9 +628,5 @@ class ProjectTestCase(utils.TestCase):
self.assertEquals('proj-map-match', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
self.assertEquals(sys.stdout.getvalue(), '')
self.assertEquals(sys.stderr.getvalue(), '')
log_output = "\n".join([u(' ').join(x) for x in logs.actual()])
expected = u('')
self.assertEquals(log_output, expected)
self.assertNothingPrinted()
self.assertNothingLogged(logs)

View file

@ -123,6 +123,6 @@ class Git(BaseProject):
subpath = line[len('gitdir: '):].strip()
if os.path.isfile(os.path.join(path, subpath, 'config')) and \
os.path.isfile(os.path.join(path, subpath, 'HEAD')):
return os.path.join(path, subpath)
return os.path.realpath(os.path.join(path, subpath))
return None