only read one line of extra heartbeats from stdin
This commit is contained in:
parent
208477cc0c
commit
937350220e
7 changed files with 166 additions and 143 deletions
|
@ -157,3 +157,44 @@ class LanguagesTestCase(utils.TestCase):
|
|||
|
||||
language = u('Java')
|
||||
self.assertEqual(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0].get('language'), language)
|
||||
|
||||
def test_alternate_language_not_used_when_invalid(self):
|
||||
response = Response()
|
||||
response.status_code = 500
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
with utils.mock.patch('wakatime.stats.smart_guess_lexer') as mock_guess_lexer:
|
||||
mock_guess_lexer.return_value = None
|
||||
|
||||
now = u(int(time.time()))
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
entity = 'tests/samples/codefiles/python.py'
|
||||
args = ['--file', entity, '--config', config, '--time', now, '--alternate-language', 'foo', '--plugin', 'NeoVim/703 vim-wakatime/4.0.9']
|
||||
|
||||
retval = execute(args)
|
||||
self.assertEquals(retval, 102)
|
||||
|
||||
language = None
|
||||
self.assertEqual(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0].get('language'), language)
|
||||
|
||||
def test_error_reading_alternate_language_json_map_file(self):
|
||||
response = Response()
|
||||
response.status_code = 500
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
with utils.mock.patch('wakatime.stats.smart_guess_lexer') as mock_guess_lexer:
|
||||
mock_guess_lexer.return_value = None
|
||||
|
||||
with utils.mock.patch('wakatime.stats.open') as mock_open:
|
||||
mock_open.side_effect = IOError('')
|
||||
|
||||
now = u(int(time.time()))
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
entity = 'tests/samples/codefiles/python.py'
|
||||
args = ['--file', entity, '--config', config, '--time', now, '--alternate-language', 'foo', '--plugin', 'NeoVim/703 vim-wakatime/4.0.9']
|
||||
|
||||
retval = execute(args)
|
||||
self.assertEquals(retval, 102)
|
||||
|
||||
language = None
|
||||
self.assertEqual(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0].get('language'), language)
|
||||
|
|
|
@ -368,88 +368,6 @@ class BaseTestCase(utils.TestCase):
|
|||
self.assertEquals(stats, json.loads(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][1]))
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called()
|
||||
|
||||
def test_alternate_project(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
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'
|
||||
|
||||
args = ['--file', entity, '--alternate-project', 'xyz', '--config', config, '--time', now]
|
||||
|
||||
retval = execute(args)
|
||||
self.assertEquals(retval, API_ERROR)
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
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()
|
||||
|
||||
heartbeat = {
|
||||
'language': 'Text only',
|
||||
'lines': 2,
|
||||
'entity': os.path.abspath(entity),
|
||||
'project': os.path.basename(os.path.abspath('.')),
|
||||
'branch': os.environ.get('TRAVIS_COMMIT', ANY),
|
||||
'time': float(now),
|
||||
'type': 'file',
|
||||
}
|
||||
stats = {
|
||||
u('cursorpos'): None,
|
||||
u('dependencies'): [],
|
||||
u('language'): u('Text only'),
|
||||
u('lineno'): None,
|
||||
u('lines'): 2,
|
||||
}
|
||||
|
||||
self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, ANY, None)
|
||||
self.assertEquals(stats, json.loads(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][1]))
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called()
|
||||
|
||||
def test_set_project_from_command_line(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
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'
|
||||
|
||||
args = ['--file', entity, '--project', 'xyz', '--config', config, '--time', now]
|
||||
|
||||
retval = execute(args)
|
||||
self.assertEquals(retval, API_ERROR)
|
||||
self.assertEquals(sys.stdout.getvalue(), '')
|
||||
self.assertEquals(sys.stderr.getvalue(), '')
|
||||
|
||||
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()
|
||||
|
||||
heartbeat = {
|
||||
'language': 'Text only',
|
||||
'lines': 2,
|
||||
'entity': os.path.abspath(entity),
|
||||
'project': 'xyz',
|
||||
'branch': os.environ.get('TRAVIS_COMMIT', ANY),
|
||||
'time': float(now),
|
||||
'type': 'file',
|
||||
}
|
||||
stats = {
|
||||
u('cursorpos'): None,
|
||||
u('dependencies'): [],
|
||||
u('language'): u('Text only'),
|
||||
u('lineno'): None,
|
||||
u('lines'): 2,
|
||||
}
|
||||
|
||||
self.patched['wakatime.offlinequeue.Queue.push'].assert_called_once_with(heartbeat, ANY, None)
|
||||
self.assertEquals(stats, json.loads(self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][1]))
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called()
|
||||
|
||||
def test_missing_entity_file(self):
|
||||
response = Response()
|
||||
response.status_code = 201
|
||||
|
@ -468,7 +386,7 @@ class BaseTestCase(utils.TestCase):
|
|||
self.patched['wakatime.session_cache.SessionCache.save'].assert_not_called()
|
||||
|
||||
self.patched['wakatime.offlinequeue.Queue.push'].assert_not_called()
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_not_called()
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_called_once_with()
|
||||
|
||||
def test_proxy_argument(self):
|
||||
response = Response()
|
||||
|
@ -653,19 +571,23 @@ class BaseTestCase(utils.TestCase):
|
|||
response.status_code = 201
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
entity = 'tests/samples/codefiles/emptyfile.txt'
|
||||
project1 = os.path.basename(os.path.abspath('.'))
|
||||
project2 = 'xyz'
|
||||
entity1 = os.path.abspath('tests/samples/codefiles/emptyfile.txt')
|
||||
entity2 = os.path.abspath('tests/samples/codefiles/twolinefile.txt')
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
args = ['--file', entity, '--config', config, '--extra-heartbeats']
|
||||
args = ['--file', entity1, '--config', config, '--extra-heartbeats']
|
||||
|
||||
with utils.mock.patch('wakatime.main.sys.stdin') as mock_stdin:
|
||||
now = int(time.time())
|
||||
heartbeats = json.dumps([{
|
||||
'timestamp': now,
|
||||
'entity': entity,
|
||||
'entity': entity2,
|
||||
'entity_type': 'file',
|
||||
'project': project2,
|
||||
'is_write': True,
|
||||
}])
|
||||
mock_stdin.read.return_value = heartbeats
|
||||
mock_stdin.readline.return_value = heartbeats
|
||||
|
||||
retval = execute(args)
|
||||
|
||||
|
@ -678,4 +600,16 @@ class BaseTestCase(utils.TestCase):
|
|||
self.patched['wakatime.session_cache.SessionCache.save'].assert_has_calls([call(ANY), call(ANY)])
|
||||
|
||||
self.patched['wakatime.offlinequeue.Queue.push'].assert_not_called()
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_has_calls([call(), call()])
|
||||
self.patched['wakatime.offlinequeue.Queue.pop'].assert_called_once_with()
|
||||
|
||||
calls = self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].call_args_list
|
||||
|
||||
body = calls[0][0][0].body
|
||||
data = json.loads(body)
|
||||
self.assertEquals(data.get('entity'), entity1)
|
||||
self.assertEquals(data.get('project'), project1)
|
||||
|
||||
body = calls[1][0][0].body
|
||||
data = json.loads(body)
|
||||
self.assertEquals(data.get('entity'), entity2)
|
||||
self.assertEquals(data.get('project'), project2)
|
||||
|
|
|
@ -55,6 +55,60 @@ class LanguagesTestCase(utils.TestCase):
|
|||
|
||||
self.assertEquals('forced-project', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
def test_alternate_project_argument_does_not_override_detected_project(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
now = u(int(time.time()))
|
||||
entity = 'tests/samples/projects/git/emptyfile.txt'
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
project = os.path.basename(os.path.abspath('.'))
|
||||
|
||||
args = ['--alternate-project', 'alt-project', '--file', entity, '--config', config, '--time', now]
|
||||
|
||||
execute(args)
|
||||
|
||||
self.assertEquals(project, self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
def test_alternate_project_argument_does_not_override_project_argument(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
now = u(int(time.time()))
|
||||
entity = 'tests/samples/projects/git/emptyfile.txt'
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
|
||||
args = ['--project', 'forced-project', '--alternate-project', 'alt-project', '--file', entity, '--config', config, '--time', now]
|
||||
|
||||
execute(args)
|
||||
|
||||
self.assertEquals('forced-project', self.patched['wakatime.offlinequeue.Queue.push'].call_args[0][0]['project'])
|
||||
|
||||
def test_alternate_project_argument_used_when_project_not_detected(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
self.patched['wakatime.packages.requests.adapters.HTTPAdapter.send'].return_value = response
|
||||
|
||||
tempdir = tempfile.mkdtemp()
|
||||
entity = 'tests/samples/projects/git/emptyfile.txt'
|
||||
shutil.copy(entity, os.path.join(tempdir, 'emptyfile.txt'))
|
||||
|
||||
now = u(int(time.time()))
|
||||
entity = os.path.join(tempdir, 'emptyfile.txt')
|
||||
config = 'tests/samples/configs/good_config.cfg'
|
||||
|
||||
args = ['--file', entity, '--config', config, '--time', now]
|
||||
execute(args)
|
||||
|
||||
args = ['--file', entity, '--config', config, '--time', now, '--alternate-project', 'alt-project']
|
||||
execute(args)
|
||||
|
||||
calls = self.patched['wakatime.offlinequeue.Queue.push'].call_args_list
|
||||
self.assertEquals(None, calls[0][0][0].get('project'))
|
||||
self.assertEquals('alt-project', calls[1][0][0]['project'])
|
||||
|
||||
def test_wakatime_project_file(self):
|
||||
response = Response()
|
||||
response.status_code = 0
|
||||
|
|
|
@ -15,3 +15,4 @@ API_ERROR = 102
|
|||
CONFIG_FILE_PARSE_ERROR = 103
|
||||
AUTH_ERROR = 104
|
||||
UNKNOWN_ERROR = 105
|
||||
MALFORMED_HEARTBEAT_ERROR = 106
|
||||
|
|
|
@ -41,10 +41,10 @@ class CustomEncoder(json.JSONEncoder):
|
|||
|
||||
class JsonFormatter(logging.Formatter):
|
||||
|
||||
def setup(self, timestamp, isWrite, entity, version, plugin, verbose,
|
||||
def setup(self, timestamp, is_write, entity, version, plugin, verbose,
|
||||
warnings=False):
|
||||
self.timestamp = timestamp
|
||||
self.isWrite = isWrite
|
||||
self.is_write = is_write
|
||||
self.entity = entity
|
||||
self.version = version
|
||||
self.plugin = plugin
|
||||
|
@ -61,10 +61,10 @@ class JsonFormatter(logging.Formatter):
|
|||
if self.verbose:
|
||||
data['caller'] = record.pathname
|
||||
data['lineno'] = record.lineno
|
||||
data['isWrite'] = self.isWrite
|
||||
data['is_write'] = self.is_write
|
||||
data['file'] = self.entity
|
||||
if not self.isWrite:
|
||||
del data['isWrite']
|
||||
if not self.is_write:
|
||||
del data['is_write']
|
||||
data['level'] = record.levelname
|
||||
data['message'] = record.getMessage() if self.warnings else record.msg
|
||||
if not self.plugin:
|
||||
|
@ -103,7 +103,7 @@ def setup_logging(args, version):
|
|||
formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
||||
formatter.setup(
|
||||
timestamp=args.timestamp,
|
||||
isWrite=args.isWrite,
|
||||
is_write=args.is_write,
|
||||
entity=args.entity,
|
||||
version=version,
|
||||
plugin=args.plugin,
|
||||
|
@ -118,7 +118,7 @@ def setup_logging(args, version):
|
|||
warnings_formatter = JsonFormatter(datefmt='%Y/%m/%d %H:%M:%S %z')
|
||||
warnings_formatter.setup(
|
||||
timestamp=args.timestamp,
|
||||
isWrite=args.isWrite,
|
||||
is_write=args.is_write,
|
||||
entity=args.entity,
|
||||
version=version,
|
||||
plugin=args.plugin,
|
||||
|
|
|
@ -37,6 +37,7 @@ from .constants import (
|
|||
CONFIG_FILE_PARSE_ERROR,
|
||||
SUCCESS,
|
||||
UNKNOWN_ERROR,
|
||||
MALFORMED_HEARTBEAT_ERROR,
|
||||
)
|
||||
from .logger import setup_logging
|
||||
from .offlinequeue import Queue
|
||||
|
@ -107,7 +108,7 @@ def parseArguments():
|
|||
parser.add_argument('--key', dest='key',
|
||||
help='your wakatime api key; uses api_key from '+
|
||||
'~/.wakatime.cfg by default')
|
||||
parser.add_argument('--write', dest='isWrite',
|
||||
parser.add_argument('--write', dest='is_write',
|
||||
action='store_true',
|
||||
help='when set, tells api this heartbeat was triggered from '+
|
||||
'writing to a file')
|
||||
|
@ -299,7 +300,7 @@ def get_user_agent(plugin):
|
|||
|
||||
|
||||
def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
||||
entity=None, timestamp=None, isWrite=None, plugin=None,
|
||||
entity=None, timestamp=None, is_write=None, plugin=None,
|
||||
offline=None, entity_type='file', hidefilenames=None,
|
||||
proxy=None, api_url=None, timeout=None, **kwargs):
|
||||
"""Sends heartbeat as POST request to WakaTime api server.
|
||||
|
@ -331,8 +332,8 @@ def send_heartbeat(project=None, branch=None, hostname=None, stats={}, key=None,
|
|||
data['lineno'] = stats['lineno']
|
||||
if stats.get('cursorpos'):
|
||||
data['cursorpos'] = stats['cursorpos']
|
||||
if isWrite:
|
||||
data['is_write'] = isWrite
|
||||
if is_write:
|
||||
data['is_write'] = is_write
|
||||
if project:
|
||||
data['project'] = project
|
||||
if branch:
|
||||
|
@ -439,7 +440,7 @@ def sync_offline_heartbeats(args, hostname):
|
|||
hostname=hostname,
|
||||
stats=json.loads(heartbeat['stats']),
|
||||
key=args.key,
|
||||
isWrite=heartbeat['is_write'],
|
||||
is_write=heartbeat['is_write'],
|
||||
plugin=heartbeat['plugin'],
|
||||
offline=args.offline,
|
||||
hidefilenames=args.hidefilenames,
|
||||
|
@ -455,41 +456,36 @@ def sync_offline_heartbeats(args, hostname):
|
|||
return SUCCESS
|
||||
|
||||
|
||||
def process_heartbeat(args, configs, heartbeat):
|
||||
exclude = should_exclude(args.entity, args.include, args.exclude)
|
||||
def process_heartbeat(args, configs, hostname, heartbeat):
|
||||
exclude = should_exclude(heartbeat['entity'], args.include, args.exclude)
|
||||
if exclude is not False:
|
||||
log.debug(u('Skipping because matches exclude pattern: {pattern}').format(
|
||||
pattern=u(exclude),
|
||||
))
|
||||
return SUCCESS
|
||||
|
||||
if args.entity_type != 'file' or os.path.isfile(args.entity):
|
||||
if heartbeat['entity_type'] != 'file' or os.path.isfile(heartbeat['entity']):
|
||||
|
||||
stats = get_file_stats(args.entity,
|
||||
entity_type=args.entity_type,
|
||||
lineno=args.lineno,
|
||||
cursorpos=args.cursorpos,
|
||||
stats = get_file_stats(heartbeat['entity'],
|
||||
entity_type=heartbeat['entity_type'],
|
||||
lineno=heartbeat.get('lineno'),
|
||||
cursorpos=heartbeat.get('cursorpos'),
|
||||
plugin=args.plugin,
|
||||
alternate_language=args.alternate_language)
|
||||
alternate_language=heartbeat.get('alternate_language'))
|
||||
|
||||
project = args.project or args.alternate_project
|
||||
project = heartbeat.get('project') or heartbeat.get('alternate_project')
|
||||
branch = None
|
||||
if args.entity_type == 'file':
|
||||
project, branch = get_project_info(configs, args)
|
||||
if heartbeat['entity_type'] == 'file':
|
||||
project, branch = get_project_info(configs, heartbeat)
|
||||
|
||||
kwargs = vars(args)
|
||||
kwargs['project'] = project
|
||||
kwargs['branch'] = branch
|
||||
kwargs['stats'] = stats
|
||||
hostname = args.hostname or socket.gethostname()
|
||||
kwargs['hostname'] = hostname
|
||||
kwargs['timeout'] = args.timeout
|
||||
heartbeat['project'] = project
|
||||
heartbeat['branch'] = branch
|
||||
heartbeat['stats'] = stats
|
||||
heartbeat['hostname'] = hostname
|
||||
heartbeat['timeout'] = args.timeout
|
||||
heartbeat['key'] = args.key
|
||||
|
||||
status = send_heartbeat(**kwargs)
|
||||
if status == SUCCESS:
|
||||
return sync_offline_heartbeats(args, hostname)
|
||||
else:
|
||||
return status
|
||||
return send_heartbeat(**heartbeat)
|
||||
|
||||
else:
|
||||
log.debug('File does not exist; ignoring this heartbeat.')
|
||||
|
@ -508,23 +504,20 @@ def execute(argv=None):
|
|||
|
||||
try:
|
||||
|
||||
heartbeat = {
|
||||
'timestamp': args.timestamp,
|
||||
'entity': args.entity,
|
||||
'entity_type': args.entity_type,
|
||||
'project': args.project,
|
||||
'is_write': args.isWrite,
|
||||
'lineno': args.lineno,
|
||||
'cursorpos': args.cursorpos,
|
||||
'alternate_project': args.alternate_project,
|
||||
'alternate_language': args.alternate_language,
|
||||
}
|
||||
retval = process_heartbeat(args, configs, heartbeat)
|
||||
hostname = args.hostname or socket.gethostname()
|
||||
|
||||
heartbeat = vars(args)
|
||||
retval = process_heartbeat(args, configs, hostname, heartbeat)
|
||||
|
||||
if args.extra_heartbeats:
|
||||
heartbeats = json.loads(sys.stdin.read())
|
||||
for heartbeat in heartbeats:
|
||||
retval = process_heartbeat(args, configs, heartbeat)
|
||||
try:
|
||||
for heartbeat in json.loads(sys.stdin.readline()):
|
||||
retval = process_heartbeat(args, configs, hostname, heartbeat)
|
||||
except json.JSONDecodeError:
|
||||
retval = MALFORMED_HEARTBEAT_ERROR
|
||||
|
||||
if retval == SUCCESS:
|
||||
retval = sync_offline_heartbeats(args, hostname)
|
||||
|
||||
return retval
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ REV_CONTROL_PLUGINS = [
|
|||
]
|
||||
|
||||
|
||||
def get_project_info(configs, args):
|
||||
def get_project_info(configs, heartbeat):
|
||||
"""Find the current project and branch.
|
||||
|
||||
First looks for a .wakatime-project file. Second, uses the --project arg.
|
||||
|
@ -50,14 +50,14 @@ def get_project_info(configs, args):
|
|||
plugin_name = plugin_cls.__name__.lower()
|
||||
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
||||
|
||||
project = plugin_cls(args.entity, configs=plugin_configs)
|
||||
project = plugin_cls(heartbeat['entity'], configs=plugin_configs)
|
||||
if project.process():
|
||||
project_name = project_name or project.name()
|
||||
branch_name = project.branch()
|
||||
break
|
||||
|
||||
if project_name is None:
|
||||
project_name = args.project
|
||||
project_name = heartbeat.get('project')
|
||||
|
||||
if project_name is None or branch_name is None:
|
||||
|
||||
|
@ -66,14 +66,14 @@ def get_project_info(configs, args):
|
|||
plugin_name = plugin_cls.__name__.lower()
|
||||
plugin_configs = get_configs_for_plugin(plugin_name, configs)
|
||||
|
||||
project = plugin_cls(args.entity, configs=plugin_configs)
|
||||
project = plugin_cls(heartbeat['entity'], configs=plugin_configs)
|
||||
if project.process():
|
||||
project_name = project_name or project.name()
|
||||
branch_name = branch_name or project.branch()
|
||||
break
|
||||
|
||||
if project_name is None:
|
||||
project_name = args.alternate_project
|
||||
project_name = heartbeat.get('alternate_project')
|
||||
|
||||
return project_name, branch_name
|
||||
|
||||
|
|
Loading…
Reference in a new issue