summaryrefslogtreecommitdiff
path: root/coverage/jsonreport.py
diff options
context:
space:
mode:
authorMatt Bachmann <bachmann.matt@gmail.com>2019-07-08 23:09:38 -0400
committerNed Batchelder <ned@nedbatchelder.com>2019-08-31 07:24:08 -0400
commit9a78a80aaf8f8161b857ebf3cf02dd511181dd07 (patch)
treed73603d17fc5269a3b8199085bf67c436d99aedf /coverage/jsonreport.py
parent790f0b30010a3a1f68f4fa7c172ce3b31c7c4b24 (diff)
downloadpython-coveragepy-git-9a78a80aaf8f8161b857ebf3cf02dd511181dd07.tar.gz
Create a JSON report
Diffstat (limited to 'coverage/jsonreport.py')
-rw-r--r--coverage/jsonreport.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py
new file mode 100644
index 00000000..e44cbf08
--- /dev/null
+++ b/coverage/jsonreport.py
@@ -0,0 +1,100 @@
+# coding: utf-8
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
+
+"""Json reporting for coverage.py"""
+import datetime
+import json
+import sys
+
+from coverage import __version__
+from coverage.report import get_analysis_to_report
+from coverage.results import Numbers
+
+
+class JsonReporter(object):
+ """A reporter for writing JSON coverage results."""
+
+ def __init__(self, coverage):
+ self.coverage = coverage
+ self.config = self.coverage.config
+ self.total = Numbers()
+ self.report_data = {}
+
+ def report(self, morfs, outfile=None):
+ """Generate a json report for `morfs`.
+
+ `morfs` is a list of modules or file names.
+
+ `outfile` is a file object to write the json to
+
+ """
+ outfile = outfile or sys.stdout
+ coverage_data = self.coverage.get_data()
+ coverage_data.set_query_contexts(self.config.report_contexts)
+ self.report_data["meta"] = {
+ "version": __version__,
+ "timestamp": datetime.datetime.now().isoformat(),
+ "branch_coverage": coverage_data.has_arcs(),
+ "show_contexts": self.config.json_show_contexts,
+ }
+
+ measured_files = {}
+ for file_reporter, analysis in get_analysis_to_report(self.coverage, morfs):
+ measured_files[file_reporter.relative_filename()] = self.report_one_file(
+ coverage_data,
+ file_reporter,
+ analysis
+ )
+
+ self.report_data["files"] = measured_files
+
+ self.report_data["totals"] = {
+ 'covered_lines': self.total.n_executed,
+ 'num_statements': self.total.n_statements,
+ 'percent_covered': self.total.pc_covered,
+ 'missing_lines': self.total.n_missing,
+ 'excluded_lines': self.total.n_excluded,
+ }
+
+ if coverage_data.has_arcs():
+ self.report_data["totals"].update({
+ 'num_branches': self.total.n_branches,
+ 'num_partial_branches': self.total.n_partial_branches,
+ })
+
+ json.dump(
+ self.report_data,
+ outfile,
+ indent=4 if self.config.json_pretty_print else None
+ )
+
+ return self.total.n_statements and self.total.pc_covered
+
+ def report_one_file(self, coverage_data, file_reporter, analysis):
+ """Extract the relevant report data for a single file"""
+ nums = analysis.numbers
+ self.total += nums
+ summary = {
+ 'covered_lines': nums.n_executed,
+ 'num_statements': nums.n_statements,
+ 'percent_covered': nums.pc_covered,
+ 'missing_lines': nums.n_missing,
+ 'excluded_lines': nums.n_excluded,
+ }
+ reported_file = {
+ 'executed_lines': sorted(analysis.executed),
+ 'summary': summary,
+ 'missing_lines': sorted(analysis.missing),
+ 'excluded_lines': sorted(analysis.excluded)
+ }
+ if self.config.json_show_contexts:
+ reported_file['contexts'] = analysis.data.contexts_by_lineno(
+ file_reporter.filename
+ )
+ if coverage_data.has_arcs():
+ reported_file['summary'].update({
+ 'num_branches': nums.n_branches,
+ 'num_partial_branches': nums.n_partial_branches,
+ })
+ return reported_file