Skip to content

Commit

Permalink
feat: add --basedir and --submit options (TheKevJames#287)
Browse files Browse the repository at this point in the history
Enables users to dynamically remove a given base path from the
generated config failes and to upload files generated in previous
runs of the tool.
  • Loading branch information
swryan authored and andy-maier committed Dec 23, 2022
1 parent 8dd1a83 commit 7045ee6
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 7 deletions.
5 changes: 4 additions & 1 deletion coveralls/api.py
Expand Up @@ -223,7 +223,9 @@ def wear(self, dry_run=False):
json_string = self.create_report()
if dry_run:
return {}
return self.submit_report(json_string)

def submit_report(self, json_string):
endpoint = '{}/api/v1/jobs'.format(self._coveralls_host.rstrip('/'))
verify = not bool(os.environ.get('COVERALLS_SKIP_SSL_VERIFY'))
response = requests.post(endpoint, files={'json_file': json_string},
Expand Down Expand Up @@ -368,7 +370,8 @@ def get_coverage(self):
workman.load()
workman.get_data()

return CoverallReporter(workman, workman.config).coverage
base_dir = self.config.get('base_dir') or ''
return CoverallReporter(workman, workman.config, base_dir).coverage

@staticmethod
def debug_bad_encoding(data):
Expand Down
15 changes: 12 additions & 3 deletions coveralls/cli.py
Expand Up @@ -19,7 +19,9 @@
Global options:
--service=<name> Provide an alternative service name to submit.
--rcfile=<file> Specify configuration file. [default: .coveragerc]
--basedir=<dir> Base directory that is removed from reported paths.
--output=<file> Write report to file. Doesn't send anything.
--submit=<file> Upload a previously generated file.
--merge=<file> Merge report from file when submitting.
--finish Finish parallel jobs.
-h --help Display this help.
Expand Down Expand Up @@ -60,7 +62,8 @@ def main(argv=None):
try:
coverallz = Coveralls(token_required,
config_file=options['--rcfile'],
service_name=options['--service'])
service_name=options['--service'],
base_dir=options.get('--basedir') or '')

if options['--merge']:
coverallz.merge(options['--merge'])
Expand All @@ -75,6 +78,11 @@ def main(argv=None):
coverallz.save_report(options['--output'])
return

if options['--submit']:
with open(options['--submit']) as report_file:
coverallz.submit_report(report_file.read())
return

if options['--finish']:
log.info('Finishing parallel jobs...')
coverallz.parallel_finish()
Expand All @@ -86,8 +94,9 @@ def main(argv=None):

log.info('Coverage submitted!')
log.debug(result)
log.info(result['message'])
log.info(result['url'])
if result:
log.info(result.get('message'))
log.info(result.get('url'))
except KeyboardInterrupt: # pragma: no cover
log.info('Aborted')
except CoverallsException as e:
Expand Down
11 changes: 10 additions & 1 deletion coveralls/reporter.py
Expand Up @@ -12,8 +12,13 @@
class CoverallReporter:
"""Custom coverage.py reporter for coveralls.io."""

def __init__(self, cov, conf):
def __init__(self, cov, conf, base_dir=''):
self.coverage = []
self.base_dir = base_dir
if self.base_dir:
self.base_dir = self.base_dir.replace(os.path.sep, '/')
if self.base_dir[-1] != '/':
self.base_dir += '/'
self.report(cov, conf)

def report5(self, cov):
Expand Down Expand Up @@ -183,9 +188,13 @@ def get_arcs(analysis):
def parse_file(self, cu, analysis):
"""Generate data for single file."""
filename = cu.relative_filename()

# ensure results are properly merged between platforms
posix_filename = filename.replace(os.path.sep, '/')

if self.base_dir and posix_filename.startswith(self.base_dir):
posix_filename = posix_filename[len(self.base_dir):]

source = analysis.file_reporter.source()

token_lines = analysis.file_reporter.source_token_lines()
Expand Down
1 change: 1 addition & 0 deletions example/example.json
@@ -0,0 +1 @@
{"source_files": [{"name": "coveralls-python/example/project.py", "source": "def hello():\n print('world')\n\n\nclass Foo:\n \"\"\" Bar \"\"\"\n\n\ndef baz():\n print('this is not tested')\n\ndef branch(cond1, cond2):\n if cond1:\n print('condition tested both ways')\n if cond2:\n print('condition not tested both ways')\n", "coverage": [1, 1, null, null, 1, null, null, null, 1, 0, null, 1, 1, 1, 1, 1]}, {"name": "coveralls-python/example/runtests.py", "source": "from project import branch\nfrom project import hello\n\nif __name__ == '__main__':\n hello()\n branch(False, True)\n branch(True, True)\n", "coverage": [1, 1, null, 1, 1, 1, 1]}], "service_name": "coveralls-python", "config_file": ".coveragerc", "base_dir": null}
107 changes: 107 additions & 0 deletions tests/api/reporter_test.py
Expand Up @@ -62,6 +62,113 @@ def test_reporter(self):
'name': 'runtests.py',
'coverage': [1, 1, None, 1, 1, 1, 1]})

def test_reporter_no_base_dir_arg(self):
subprocess.call(['coverage', 'run', '--omit=**/.tox/*',
'example/runtests.py'], cwd=BASE_DIR)

# without base_dir arg, file name is prefixed with 'example/'
os.chdir(BASE_DIR)
results = Coveralls(repo_token='xxx').get_coverage()
assert len(results) == 2

assert_coverage(results[0], {
'source': ('def hello():\n'
' print(\'world\')\n\n\n'
'class Foo:\n'
' """ Bar """\n\n\n'
'def baz():\n'
' print(\'this is not tested\')\n\n'
'def branch(cond1, cond2):\n'
' if cond1:\n'
' print(\'condition tested both ways\')\n'
' if cond2:\n'
' print(\'condition not tested both ways\')\n'),
'name': 'example/project.py',
'coverage': [1, 1, None, None, 1, None, None,
None, 1, 0, None, 1, 1, 1, 1, 1]})

assert_coverage(results[1], {
'source': ('from project import branch\n'
'from project import hello\n\n'
"if __name__ == '__main__':\n"
' hello()\n'
' branch(False, True)\n'
' branch(True, True)\n'),
'name': 'example/runtests.py',
'coverage': [1, 1, None, 1, 1, 1, 1]})

def test_reporter_with_base_dir_arg(self):
subprocess.call(['coverage', 'run', '--omit=**/.tox/*',
'example/runtests.py'], cwd=BASE_DIR)

# without base_dir arg, file name is prefixed with 'example/'
os.chdir(BASE_DIR)
results = Coveralls(repo_token='xxx',
base_dir='example').get_coverage()
assert len(results) == 2

assert_coverage(results[0], {
'source': ('def hello():\n'
' print(\'world\')\n\n\n'
'class Foo:\n'
' """ Bar """\n\n\n'
'def baz():\n'
' print(\'this is not tested\')\n\n'
'def branch(cond1, cond2):\n'
' if cond1:\n'
' print(\'condition tested both ways\')\n'
' if cond2:\n'
' print(\'condition not tested both ways\')\n'),
'name': 'project.py',
'coverage': [1, 1, None, None, 1, None, None,
None, 1, 0, None, 1, 1, 1, 1, 1]})

assert_coverage(results[1], {
'source': ('from project import branch\n'
'from project import hello\n\n'
"if __name__ == '__main__':\n"
' hello()\n'
' branch(False, True)\n'
' branch(True, True)\n'),
'name': 'runtests.py',
'coverage': [1, 1, None, 1, 1, 1, 1]})

def test_reporter_with_base_dir_trailing_sep(self):
subprocess.call(['coverage', 'run', '--omit=**/.tox/*',
'example/runtests.py'], cwd=BASE_DIR)

# without base_dir arg, file name is prefixed with 'example/'
os.chdir(BASE_DIR)
results = Coveralls(repo_token='xxx',
base_dir='example/').get_coverage()
assert len(results) == 2

assert_coverage(results[0], {
'source': ('def hello():\n'
' print(\'world\')\n\n\n'
'class Foo:\n'
' """ Bar """\n\n\n'
'def baz():\n'
' print(\'this is not tested\')\n\n'
'def branch(cond1, cond2):\n'
' if cond1:\n'
' print(\'condition tested both ways\')\n'
' if cond2:\n'
' print(\'condition not tested both ways\')\n'),
'name': 'project.py',
'coverage': [1, 1, None, None, 1, None, None,
None, 1, 0, None, 1, 1, 1, 1, 1]})

assert_coverage(results[1], {
'source': ('from project import branch\n'
'from project import hello\n\n'
"if __name__ == '__main__':\n"
' hello()\n'
' branch(False, True)\n'
' branch(True, True)\n'),
'name': 'runtests.py',
'coverage': [1, 1, None, 1, 1, 1, 1]})

def test_reporter_with_branches(self):
subprocess.call(['coverage', 'run', '--branch', '--omit=**/.tox/*',
'runtests.py'], cwd=EXAMPLE_DIR)
Expand Down
27 changes: 25 additions & 2 deletions tests/cli_test.py
Expand Up @@ -11,6 +11,10 @@
EXC = CoverallsException('bad stuff happened')


BASE_DIR = os.path.dirname(os.path.dirname(__file__))
EXAMPLE_DIR = os.path.join(BASE_DIR, 'example')


def req_json(request):
return json.loads(request.body.decode('utf-8'))

Expand Down Expand Up @@ -104,15 +108,17 @@ def test_real(mock_wear, mock_log):
def test_rcfile(mock_coveralls):
coveralls.cli.main(argv=['--rcfile=coveragerc'])
mock_coveralls.assert_called_with(True, config_file='coveragerc',
service_name=None)
service_name=None,
base_dir='')


@mock.patch.dict(os.environ, {}, clear=True)
@mock.patch('coveralls.cli.Coveralls')
def test_service_name(mock_coveralls):
coveralls.cli.main(argv=['--service=travis-pro'])
mock_coveralls.assert_called_with(True, config_file='.coveragerc',
service_name='travis-pro')
service_name='travis-pro',
base_dir='')


@mock.patch.object(coveralls.cli.log, 'exception')
Expand Down Expand Up @@ -142,3 +148,20 @@ def test_save_report_to_file_no_token(mock_coveralls):
"""Check save_report api usage when token is not set."""
coveralls.cli.main(argv=['--output=test.log'])
mock_coveralls.assert_called_with('test.log')


@mock.patch.object(coveralls.Coveralls, 'submit_report')
@mock.patch.dict(os.environ, {'TRAVIS': 'True'}, clear=True)
def test_submit(mock_submit):
json_file = os.path.join(EXAMPLE_DIR, 'example.json')
json_string = open(json_file).read()
coveralls.cli.main(argv=['--submit=' + json_file])
mock_submit.assert_called_with(json_string)


@mock.patch('coveralls.cli.Coveralls')
def test_base_dir_arg(mock_coveralls):
coveralls.cli.main(argv=['--basedir=foo'])
mock_coveralls.assert_called_with(True, config_file='.coveragerc',
service_name=None,
base_dir='foo')

0 comments on commit 7045ee6

Please sign in to comment.