Skip to content

Commit

Permalink
Local Compare mode HTML output files
Browse files Browse the repository at this point in the history
Local Compare mode should be able to generate HTML files with bug path.
  • Loading branch information
csordasmarton committed Oct 24, 2017
1 parent a4f2d01 commit dfdc7bf
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 9 deletions.
98 changes: 91 additions & 7 deletions libcodechecker/cmd/cmd_line_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
# -------------------------------------------------------------------------

import base64
from collections import defaultdict
from datetime import datetime
import json
import os
import re
import sys
import shutil

from plist_to_html import PlistToHtml

from codeCheckerDBAccess_v6 import constants, ttypes

from libcodechecker import generic_package_context
from libcodechecker import suppress_handler
from libcodechecker import suppress_file_handler
from libcodechecker.analyze import plist_parser
Expand Down Expand Up @@ -167,6 +172,7 @@ def handle_list_results(args):


def handle_diff_results(args):
context = generic_package_context.get_context()

def getDiffResults(client, baseids, cmp_data):

Expand Down Expand Up @@ -321,7 +327,66 @@ def getDiffReportDir(client, baseids, report_dir, diff_type):
filtered_reports.append(result)
return filtered_reports

def printReports(client, reports, output_format):
def cache_report_file(file_cache, file_id):
if file_id not in file_cache:
source = client.getSourceFileData(file_id, True,
ttypes.Encoding.BASE64)
file_content = base64.b64decode(source.fileContent)
file_cache[file_id] = {'id': file_id,
'path': source.filePath,
'content': file_content}

return file_cache[file_id]

def get_report_data(client, reports, file_cache):
file_sources = {}
report_data = []

for report in reports:
file_sources[report.fileId] = cache_report_file(file_cache,
report.fileId)
details = client.getReportDetails(report.reportId)
events = []
for index, event in enumerate(details.pathEvents):
file_sources[event.fileId] = cache_report_file(file_cache,
event.fileId)
events.append({'line': event.startLine,
'col': event.startCol,
'file': event.fileId,
'msg': event.msg,
'step': index + 1})
report_data.append(events)

return {'files': file_sources,
'reports': report_data}

def report_to_html(client, reports, output_dir):
html_builder = PlistToHtml.HtmlBuilder(context.path_plist_to_html_dist)

file_report_map = defaultdict(list)
for report in reports:
file_report_map[report.fileId].append(report)

file_cache = {}
for file_id, file_reports in file_report_map.iteritems():
checked_file = file_reports[0].checkedFile
filename = os.path.basename(checked_file)

report_data = get_report_data(client, file_reports, file_cache)

output_path = os.path.join(output_dir,
filename + '_' + str(file_id) + '.html')
html_builder.create(output_path, report_data)
print('Html file was generated for file {0}: {1}'.format(
checked_file, output_path))

def printReports(client, reports, output_format, diff_type):
output_dir = args.export_dir if 'export_dir' in args else None
if 'clean' in args and os.path.isdir(output_dir):
print("Previous analysis results in '{0}' have been removed, "
"overwriting with current results.".format(output_dir))
shutil.rmtree(output_dir)

if output_format == 'json':
output = []
for report in reports:
Expand All @@ -332,6 +397,24 @@ def printReports(client, reports, output_format):
print(CmdLineOutputEncoder().encode(output))
return

if output_format == 'html':
if len(reports) == 0:
print('No {0} reports was found!'.format(diff_type))
return

output_dir = args.export_dir
if not os.path.exists(output_dir):
os.makedirs(output_dir)

print("Generating HTML output files to {0} directory:\n".format(
output_dir))

report_to_html(client, reports, output_dir)

print('\nTo view the results in a browser run:\n'
'> firefox {0}'.format(args.export_dir))
return

header = ['File', 'Checker', 'Severity', 'Msg', 'Source']
rows = []
for report in reports:
Expand Down Expand Up @@ -392,13 +475,14 @@ def printReports(client, reports, output_format):
LOG.info("Matching against runs: " +
', '.join(map(lambda run: run.name, base_runs)))

diff_type = 'new'
if 'unresolved' in args:
diff_type = 'unresolved'
elif 'resolved' in args:
diff_type = 'resolved'

results = []
if report_dir_mode:
diff_type = 'new'
if 'unresolved' in args:
diff_type = 'unresolved'
elif 'resolved' in args:
diff_type = 'resolved'
results = getDiffReportDir(client, base_ids,
os.path.abspath(args.newname),
diff_type)
Expand All @@ -413,7 +497,7 @@ def printReports(client, reports, output_format):

results = getDiffResults(client, base_ids, cmp_data)

printReports(client, results, args.output_format)
printReports(client, results, args.output_format, diff_type)


def handle_list_result_types(args):
Expand Down
4 changes: 4 additions & 0 deletions libcodechecker/libclient/thrift_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def wrapper(self, *args, **kwargs):
def getRunData(self, run_name_filter):
pass

@ThriftClientCall
def getReportDetails(self, reportId):
pass

@ThriftClientCall
def getSourceFileData(self, fileId, fileContent, encoding):
pass
Expand Down
34 changes: 32 additions & 2 deletions libcodechecker/libhandlers/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import sys

from libcodechecker import output_formatters
from libcodechecker import util
from libcodechecker.cmd import cmd_line_client
from libcodechecker.cmd import product_client
from libcodechecker.logger import add_verbose_arguments
Expand Down Expand Up @@ -105,11 +106,27 @@ def __add_common_arguments(parser,
required=False,
# TODO: 'plaintext' only kept for legacy.
default="plaintext",
choices=["plaintext"] +
choices=["plaintext", "html"] +
output_formatters.USER_FORMATS,
help="The output format to use in showing "
"the data.")

common_group.add_argument('-e', '--export-dir',
dest="export_dir",
default=argparse.SUPPRESS,
help="Store the output in the given folder.")

common_group.add_argument('-c', '--clean',
dest="clean",
required=False,
action='store_true',
default=argparse.SUPPRESS,
help="Delete output results stored in the "
"output directory. (By default, it "
"would keep output files and "
"overwrites only those that contain "
"any reports).")

add_verbose_arguments(common_group)


Expand Down Expand Up @@ -211,6 +228,20 @@ def __register_diff(parser):
help="Show results that appear in both the 'base' and "
"the 'new' run.")

def __handle(args):
"""Custom handler for 'diff' so custom error messages can be
printed without having to capture 'parser' in main."""

output_dir = ['-e', '--export-dir']
if args.output_format == 'html' and \
not any(util.arg_match(output_dir, sys.argv[1:])):
parser.error("argument --output html: not allowed without "
"argument --export-dir")

cmd_line_client.handle_diff_results(args)

parser.set_defaults(func=__handle)


def __register_sum(parser):
"""
Expand Down Expand Up @@ -595,7 +626,6 @@ def add_arguments_to_parser(parser):
"differ between the two.",
help="Compare two analysis runs and show the difference.")
__register_diff(diff)
diff.set_defaults(func=cmd_line_client.handle_diff_results)
__add_common_arguments(diff, has_matrix_output=True)

sum_p = subcommands.add_parser(
Expand Down

0 comments on commit dfdc7bf

Please sign in to comment.