support unicode heartbeats in offline cache

This commit is contained in:
Alan Hamlett 2017-11-22 12:58:29 -08:00
parent a17000eee7
commit 7b09f8f0c6
4 changed files with 157 additions and 10 deletions

View file

@ -484,6 +484,47 @@ class MainTestCase(utils.TestCase):
self.assertOfflineHeartbeatsSynced() self.assertOfflineHeartbeatsSynced()
self.assertSessionCacheSaved() self.assertSessionCacheSaved()
@log_capture()
def test_nonascii_filename_saved_when_offline(self, logs):
logging.disable(logging.NOTSET)
response = Response()
response.status_code = 500
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
with utils.TemporaryDirectory() as tempdir:
filename = list(filter(lambda x: x.endswith('.txt'), os.listdir(u('tests/samples/codefiles/unicode'))))[0]
entity = os.path.join('tests/samples/codefiles/unicode', filename)
shutil.copy(entity, os.path.join(tempdir, filename))
entity = os.path.realpath(os.path.join(tempdir, filename))
now = u(int(time.time()))
config = 'tests/samples/configs/good_config.cfg'
key = str(uuid.uuid4())
heartbeat = {
'language': 'Text only',
'lines': 0,
'entity': os.path.realpath(entity),
'project': None,
'time': float(now),
'type': 'file',
'is_write': False,
'user_agent': ANY,
'dependencies': [],
}
args = ['--file', entity, '--key', key, '--config', config, '--time', now]
retval = execute(args)
self.assertEquals(retval, API_ERROR)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
self.assertHeartbeatSent(heartbeat)
self.assertHeartbeatSavedOffline()
self.assertOfflineHeartbeatsNotSynced()
self.assertSessionCacheDeleted()
@log_capture() @log_capture()
def test_unhandled_exception(self, logs): def test_unhandled_exception(self, logs):
logging.disable(logging.NOTSET) logging.disable(logging.NOTSET)

View file

@ -555,3 +555,94 @@ class OfflineQueueTestCase(utils.TestCase):
self.patched['wakatime.session_cache.SessionCache.get'].assert_called_once_with() self.patched['wakatime.session_cache.SessionCache.get'].assert_called_once_with()
self.patched['wakatime.session_cache.SessionCache.delete'].assert_called_once_with() self.patched['wakatime.session_cache.SessionCache.delete'].assert_called_once_with()
self.patched['wakatime.session_cache.SessionCache.save'].assert_not_called() self.patched['wakatime.session_cache.SessionCache.save'].assert_not_called()
"""@log_capture()
def non_ascii_heartbeat_saved(self, logs):
logging.disable(logging.NOTSET)
with utils.NamedTemporaryFile() as fh:
with utils.mock.patch('wakatime.offlinequeue.Queue._get_db_file') as mock_db_file:
mock_db_file.return_value = fh.name
response = Response()
response.status_code = 500
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
now = u(int(time.time()))
entity = 'tests/samples/codefiles/twolinefile.txt'
config = 'tests/samples/configs/good_config.cfg'
return 'tz汉语' if is_py3 else 'tz\xe6\xb1\x89\xe8\xaf\xad'
args = ['--file', entity, '--config', config, '--time', now]
execute(args)
queue = Queue(None, None)
saved_heartbeat = queue.pop()
self.assertEquals(os.path.realpath(entity), saved_heartbeat['entity'])
self.assertNothingPrinted()
self.assertNothingLogged()
self.patched['wakatime.session_cache.SessionCache.get'].assert_called_once_with()
self.patched['wakatime.session_cache.SessionCache.delete'].assert_called_once_with()
self.patched['wakatime.session_cache.SessionCache.save'].assert_not_called()
"""
@log_capture()
def test_nonascii_heartbeat_saved(self, logs):
logging.disable(logging.NOTSET)
with utils.NamedTemporaryFile() as fh:
with utils.mock.patch('wakatime.offlinequeue.Queue._get_db_file') as mock_db_file:
mock_db_file.return_value = fh.name
response = Response()
response.status_code = 500
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
with utils.TemporaryDirectory() as tempdir:
filename = list(filter(lambda x: x.endswith('.txt'), os.listdir(u('tests/samples/codefiles/unicode'))))[0]
entity = os.path.join('tests/samples/codefiles/unicode', filename)
shutil.copy(entity, os.path.join(tempdir, filename))
entity = os.path.realpath(os.path.join(tempdir, filename))
now = u(int(time.time()))
config = 'tests/samples/configs/good_config.cfg'
key = str(uuid.uuid4())
language = 'lang汉语' if self.isPy35OrNewer else 'lang\xe6\xb1\x89\xe8\xaf\xad'
project = 'proj汉语' if self.isPy35OrNewer else 'proj\xe6\xb1\x89\xe8\xaf\xad'
branch = 'branch汉语' if self.isPy35OrNewer else 'branch\xe6\xb1\x89\xe8\xaf\xad'
heartbeat = {
'language': u(language),
'lines': 0,
'entity': os.path.realpath(entity),
'project': u(project),
'branch': u(branch),
'time': float(now),
'type': 'file',
'is_write': False,
'user_agent': ANY,
'dependencies': [],
}
args = ['--file', entity, '--key', key, '--config', config, '--time', now]
with utils.mock.patch('wakatime.stats.standardize_language') as mock_language:
mock_language.return_value = (language, None)
with utils.mock.patch('wakatime.heartbeat.get_project_info') as mock_project:
mock_project.return_value = (project, branch)
execute(args)
self.assertNothingPrinted()
self.assertNothingLogged(logs)
self.assertHeartbeatSent(heartbeat)
queue = Queue(None, None)
saved_heartbeat = queue.pop()
self.assertEquals(os.path.realpath(entity), saved_heartbeat['entity'])
self.assertEquals(u(language), saved_heartbeat['language'])
self.assertEquals(u(project), saved_heartbeat['project'])
self.assertEquals(u(branch), saved_heartbeat['branch'])

View file

@ -91,7 +91,7 @@ class TestCase(unittest.TestCase):
if isinstance(heartbeat.get(key), list): if isinstance(heartbeat.get(key), list):
self.assertListsEqual(heartbeat.get(key), body[0].get(key), u('Expected heartbeat to be sent with {0}={1}, instead {0}={2}').format(u(key), u(heartbeat.get(key)), u(body[0].get(key)))) self.assertListsEqual(heartbeat.get(key), body[0].get(key), u('Expected heartbeat to be sent with {0}={1}, instead {0}={2}').format(u(key), u(heartbeat.get(key)), u(body[0].get(key))))
else: else:
self.assertEquals(heartbeat.get(key), body[0].get(key), u('Expected heartbeat to be sent with {1} {0}={2}, instead {3} {0}={4}').format(u(key), type(heartbeat.get(key)), u(heartbeat.get(key)), type(body[0].get(key)), u(body[0].get(key)))) self.assertEquals(heartbeat.get(key), body[0].get(key), u('Expected heartbeat to be sent with {1} {0}={2}, instead {3} {0}={4}').format(u(key), type(heartbeat.get(key)).__name__, u(heartbeat.get(key)), type(body[0].get(key)).__name__, u(body[0].get(key))))
if extra_heartbeats: if extra_heartbeats:
for i in range(len(extra_heartbeats)): for i in range(len(extra_heartbeats)):
@ -100,7 +100,7 @@ class TestCase(unittest.TestCase):
if isinstance(extra_heartbeats[i].get(key), list): if isinstance(extra_heartbeats[i].get(key), list):
self.assertListsEqual(extra_heartbeats[i].get(key), body[i + 1].get(key), u('Expected extra heartbeat {3} to be sent with {0}={1}, instead {0}={2}').format(u(key), u(extra_heartbeats[i].get(key)), u(body[i + 1].get(key)), i)) self.assertListsEqual(extra_heartbeats[i].get(key), body[i + 1].get(key), u('Expected extra heartbeat {3} to be sent with {0}={1}, instead {0}={2}').format(u(key), u(extra_heartbeats[i].get(key)), u(body[i + 1].get(key)), i))
else: else:
self.assertEquals(extra_heartbeats[i].get(key), body[i + 1].get(key), u('Expected extra heartbeat {5} to be sent with {1} {0}={2}, instead {3} {0}={4}').format(u(key), type(extra_heartbeats[i].get(key)), u(extra_heartbeats[i].get(key)), type(body[i + 1].get(key)), u(body[i + 1].get(key)), i)) self.assertEquals(extra_heartbeats[i].get(key), body[i + 1].get(key), u('Expected extra heartbeat {5} to be sent with {1} {0}={2}, instead {3} {0}={4}').format(u(key), type(extra_heartbeats[i].get(key)).__name__, u(extra_heartbeats[i].get(key)), type(body[i + 1].get(key)).__name__, u(body[i + 1].get(key)), i))
def assertSessionCacheUntouched(self): def assertSessionCacheUntouched(self):
self.patched['wakatime.session_cache.SessionCache.delete'].assert_not_called() self.patched['wakatime.session_cache.SessionCache.delete'].assert_not_called()

View file

@ -141,27 +141,42 @@ class Heartbeat(object):
def dict(self): def dict(self):
return { return {
'time': self.time, 'time': self.time,
'entity': self.entity, 'entity': self._unicode(self.entity),
'type': self.type, 'type': self.type,
'is_write': self.is_write, 'is_write': self.is_write,
'project': self.project, 'project': self._unicode(self.project),
'branch': self.branch, 'branch': self._unicode(self.branch),
'language': self.language, 'language': self._unicode(self.language),
'dependencies': self.dependencies, 'dependencies': self._unicode_list(self.dependencies),
'lines': self.lines, 'lines': self.lines,
'lineno': self.lineno, 'lineno': self.lineno,
'cursorpos': self.cursorpos, 'cursorpos': self.cursorpos,
'user_agent': self.user_agent, 'user_agent': self._unicode(self.user_agent),
} }
def items(self): def items(self):
return self.dict().items() return self.dict().items()
def get_id(self): def get_id(self):
return u('{h.time}-{h.type}-{h.project}-{h.branch}-{h.entity}-{h.is_write}').format( return u('{time}-{type}-{project}-{branch}-{entity}-{is_write}').format(
h=self, time=self.time,
type=self.type,
project=self._unicode(self.project),
branch=self._unicode(self.branch),
entity=self._unicode(self.entity),
is_write=self.is_write,
) )
def _unicode(self, value):
if value is None:
return None
return u(value)
def _unicode_list(self, values):
if values is None:
return None
return [self._unicode(value) for value in values]
def _excluded_by_pattern(self): def _excluded_by_pattern(self):
return should_exclude(self.entity, self.args.include, self.args.exclude) return should_exclude(self.entity, self.args.include, self.args.exclude)